haiku/src/kits/media/MediaFormats.cpp

642 lines
14 KiB
C++

/*
* Copyright 2004-2009, The Haiku Project. All rights reserved.
* Distributed under the terms of the MIT license.
*
* Authors:
* Axel Dörfler
* Marcus Overhagen
*/
#include "AddOnManager.h"
#include "DataExchange.h"
#include "FormatManager.h"
#include "MetaFormat.h"
#include "MediaDebug.h"
#include <MediaFormats.h>
#include <ObjectList.h>
#include <Message.h>
#include <Autolock.h>
#include <string.h>
using namespace BPrivate::media;
static BLocker sLock;
static BObjectList<meta_format> sFormats;
static bigtime_t sLastFormatsUpdate;
status_t
get_next_encoder(int32* cookie, const media_file_format* fileFormat,
const media_format* inputFormat, media_format* _outputFormat,
media_codec_info* _codecInfo)
{
// TODO: If fileFormat is provided (existing apps also pass NULL),
// we could at least check fileFormat->capabilities against
// outputFormat->type without even contacting the server.
if (cookie == NULL || inputFormat == NULL || _codecInfo == NULL)
return B_BAD_VALUE;
while (true) {
media_codec_info candidateCodecInfo;
media_format_family candidateFormatFamily;
media_format candidateInputFormat;
media_format candidateOutputFormat;
status_t ret = AddOnManager::GetInstance()->GetCodecInfo(
&candidateCodecInfo, &candidateFormatFamily,
&candidateInputFormat, &candidateOutputFormat, *cookie);
if (ret != B_OK)
return ret;
*cookie = *cookie + 1;
if (fileFormat != NULL && candidateFormatFamily != B_ANY_FORMAT_FAMILY
&& fileFormat->family != B_ANY_FORMAT_FAMILY
&& fileFormat->family != candidateFormatFamily) {
continue;
}
if (!candidateInputFormat.Matches(inputFormat))
continue;
if (_outputFormat != NULL)
*_outputFormat = candidateOutputFormat;
*_codecInfo = candidateCodecInfo;
break;
}
return B_OK;
}
status_t
get_next_encoder(int32* cookie, const media_file_format* fileFormat,
const media_format* inputFormat, const media_format* outputFormat,
media_codec_info* _codecInfo, media_format* _acceptedInputFormat,
media_format* _acceptedOutputFormat)
{
// TODO: If fileFormat is provided (existing apps also pass NULL),
// we could at least check fileFormat->capabilities against
// outputFormat->type without even contacting the server.
if (cookie == NULL || inputFormat == NULL || outputFormat == NULL
|| _codecInfo == NULL) {
return B_BAD_VALUE;
}
while (true) {
media_codec_info candidateCodecInfo;
media_format_family candidateFormatFamily;
media_format candidateInputFormat;
media_format candidateOutputFormat;
status_t ret = AddOnManager::GetInstance()->GetCodecInfo(
&candidateCodecInfo, &candidateFormatFamily, &candidateInputFormat,
&candidateOutputFormat, *cookie);
if (ret != B_OK)
return ret;
*cookie = *cookie + 1;
if (fileFormat != NULL && candidateFormatFamily != B_ANY_FORMAT_FAMILY
&& fileFormat->family != B_ANY_FORMAT_FAMILY
&& fileFormat->family != candidateFormatFamily) {
continue;
}
if (!candidateInputFormat.Matches(inputFormat)
|| !candidateOutputFormat.Matches(outputFormat)) {
continue;
}
// TODO: These formats are currently way too generic. For example,
// an encoder may want to adjust video width to a multiple of 16,
// or overwrite the intput and or output color space. To make this
// possible, we actually have to instantiate an Encoder here and
// ask it to specifiy the format.
if (_acceptedInputFormat != NULL)
*_acceptedInputFormat = candidateInputFormat;
if (_acceptedOutputFormat != NULL)
*_acceptedOutputFormat = candidateOutputFormat;
*_codecInfo = candidateCodecInfo;
break;
}
return B_OK;
}
status_t
get_next_encoder(int32* cookie, media_codec_info* _codecInfo)
{
if (cookie == NULL || _codecInfo == NULL)
return B_BAD_VALUE;
media_format_family formatFamily;
media_format inputFormat;
media_format outputFormat;
status_t ret = AddOnManager::GetInstance()->GetCodecInfo(_codecInfo,
&formatFamily, &inputFormat, &outputFormat, *cookie);
if (ret != B_OK)
return ret;
*cookie = *cookie + 1;
return B_OK;
}
bool
does_file_accept_format(const media_file_format* _fileFormat,
media_format* format, uint32 flags)
{
UNIMPLEMENTED();
return false;
}
// #pragma mark -
_media_format_description::_media_format_description()
{
memset(this, 0, sizeof(*this));
}
_media_format_description::~_media_format_description()
{
}
_media_format_description::_media_format_description(
const _media_format_description& other)
{
memcpy(this, &other, sizeof(*this));
}
_media_format_description&
_media_format_description::operator=(const _media_format_description& other)
{
memcpy(this, &other, sizeof(*this));
return *this;
}
bool
operator==(const media_format_description& a,
const media_format_description& b)
{
if (a.family != b.family)
return false;
switch (a.family) {
case B_BEOS_FORMAT_FAMILY:
return a.u.beos.format == b.u.beos.format;
case B_QUICKTIME_FORMAT_FAMILY:
return a.u.quicktime.codec == b.u.quicktime.codec
&& a.u.quicktime.vendor == b.u.quicktime.vendor;
case B_AVI_FORMAT_FAMILY:
return a.u.avi.codec == b.u.avi.codec;
case B_ASF_FORMAT_FAMILY:
return a.u.asf.guid == b.u.asf.guid;
case B_MPEG_FORMAT_FAMILY:
return a.u.mpeg.id == b.u.mpeg.id;
case B_WAV_FORMAT_FAMILY:
return a.u.wav.codec == b.u.wav.codec;
case B_AIFF_FORMAT_FAMILY:
return a.u.aiff.codec == b.u.aiff.codec;
case B_AVR_FORMAT_FAMILY:
return a.u.avr.id == b.u.avr.id;
case B_MISC_FORMAT_FAMILY:
return a.u.misc.file_format == b.u.misc.file_format
&& a.u.misc.codec == b.u.misc.codec;
default:
return false;
}
}
bool
operator<(const media_format_description& a, const media_format_description& b)
{
if (a.family != b.family)
return a.family < b.family;
switch (a.family) {
case B_BEOS_FORMAT_FAMILY:
return a.u.beos.format < b.u.beos.format;
case B_QUICKTIME_FORMAT_FAMILY:
if (a.u.quicktime.vendor == b.u.quicktime.vendor)
return a.u.quicktime.codec < b.u.quicktime.codec;
return a.u.quicktime.vendor < b.u.quicktime.vendor;
case B_AVI_FORMAT_FAMILY:
return a.u.avi.codec < b.u.avi.codec;
case B_ASF_FORMAT_FAMILY:
return a.u.asf.guid < b.u.asf.guid;
case B_MPEG_FORMAT_FAMILY:
return a.u.mpeg.id < b.u.mpeg.id;
case B_WAV_FORMAT_FAMILY:
return a.u.wav.codec < b.u.wav.codec;
case B_AIFF_FORMAT_FAMILY:
return a.u.aiff.codec < b.u.aiff.codec;
case B_AVR_FORMAT_FAMILY:
return a.u.avr.id < b.u.avr.id;
case B_MISC_FORMAT_FAMILY:
if (a.u.misc.file_format == b.u.misc.file_format)
return a.u.misc.codec < b.u.misc.codec;
return a.u.misc.file_format < b.u.misc.file_format;
default:
return true;
}
}
bool
operator==(const GUID& a, const GUID& b)
{
return memcmp(&a, &b, sizeof(a)) == 0;
}
bool
operator<(const GUID& a, const GUID& b)
{
return memcmp(&a, &b, sizeof(a)) < 0;
}
// #pragma mark -
//
// Some (meta) formats supply functions
meta_format::meta_format()
:
id(0)
{
}
meta_format::meta_format(const media_format_description& description,
const media_format& format, int32 id)
:
description(description),
format(format),
id(id)
{
}
meta_format::meta_format(const media_format_description& description)
:
description(description),
id(0)
{
}
meta_format::meta_format(const meta_format& other)
:
description(other.description),
format(other.format)
{
}
bool
meta_format::Matches(const media_format& otherFormat,
media_format_family family)
{
if (family != description.family)
return false;
return format.Matches(&otherFormat);
}
int
meta_format::CompareDescriptions(const meta_format* a, const meta_format* b)
{
if (a->description == b->description)
return 0;
if (a->description < b->description)
return -1;
return 1;
}
int
meta_format::Compare(const meta_format* a, const meta_format* b)
{
int compare = CompareDescriptions(a, b);
if (compare != 0)
return compare;
return a->id - b->id;
}
/** We share one global list for all BMediaFormats in the team - since the
* format data can change at any time, we have to update the list to ensure
* that we are working on the latest data set. The list is always sorted by
* description. The formats lock has to be held when you call this function.
*/
static status_t
update_media_formats()
{
if (!sLock.IsLocked())
return B_NOT_ALLOWED;
// We want the add-ons to register themselves with the format manager, so
// the list is up to date.
AddOnManager::GetInstance()->RegisterAddOns();
BMessage reply;
FormatManager::GetInstance()->GetFormats(sLastFormatsUpdate, reply);
// do we need an update at all?
bool needUpdate;
if (reply.FindBool("need_update", &needUpdate) < B_OK)
return B_ERROR;
if (!needUpdate)
return B_OK;
// update timestamp and check if the message is okay
type_code code;
int32 count;
if (reply.FindInt64("timestamp", &sLastFormatsUpdate) < B_OK
|| reply.GetInfo("formats", &code, &count) < B_OK)
return B_ERROR;
// overwrite already existing formats
int32 index = 0;
for (; index < sFormats.CountItems() && index < count; index++) {
meta_format* item = sFormats.ItemAt(index);
const meta_format* newItem;
ssize_t size;
if (reply.FindData("formats", MEDIA_META_FORMAT_TYPE, index,
(const void**)&newItem, &size) == B_OK)
*item = *newItem;
}
// allocate additional formats
for (; index < count; index++) {
const meta_format* newItem;
ssize_t size;
if (reply.FindData("formats", MEDIA_META_FORMAT_TYPE, index,
(const void**)&newItem, &size) == B_OK)
sFormats.AddItem(new meta_format(*newItem));
}
// remove no longer used formats
while (count < sFormats.CountItems())
delete sFormats.RemoveItemAt(count);
return B_OK;
}
// #pragma mark -
BMediaFormats::BMediaFormats()
:
fIteratorIndex(0)
{
}
BMediaFormats::~BMediaFormats()
{
}
status_t
BMediaFormats::InitCheck()
{
return sLock.InitCheck();
}
status_t
BMediaFormats::GetCodeFor(const media_format& format,
media_format_family family,
media_format_description* _description)
{
BAutolock locker(sLock);
status_t status = update_media_formats();
if (status < B_OK)
return status;
// search for a matching format
for (int32 index = sFormats.CountItems(); index-- > 0;) {
meta_format* metaFormat = sFormats.ItemAt(index);
if (metaFormat->Matches(format, family)) {
*_description = metaFormat->description;
return B_OK;
}
}
return B_MEDIA_BAD_FORMAT;
}
status_t
BMediaFormats::GetFormatFor(const media_format_description& description,
media_format* _format)
{
BAutolock locker(sLock);
status_t status = update_media_formats();
if (status < B_OK) {
ERROR("BMediaFormats: updating formats from server failed: %s!\n",
strerror(status));
return status;
}
TRACE("search for description family = %d, a = 0x%"
B_PRId32 "x, b = 0x%" B_PRId32 "x\n",
description.family, description.u.misc.file_format,
description.u.misc.codec);
// search for a matching format description
meta_format other(description);
const meta_format* metaFormat = sFormats.BinarySearch(other,
meta_format::CompareDescriptions);
TRACE("meta format == %p\n", metaFormat);
if (metaFormat == NULL) {
_format->Clear(); // clear to widlcard
return B_MEDIA_BAD_FORMAT;
}
// found it!
*_format = metaFormat->format;
return B_OK;
}
status_t
BMediaFormats::GetBeOSFormatFor(uint32 format,
media_format* _format, media_type type)
{
BMediaFormats formats;
media_format_description description;
description.family = B_BEOS_FORMAT_FAMILY;
description.u.beos.format = format;
status_t status = formats.GetFormatFor(description, _format);
if (status < B_OK)
return status;
if (type != B_MEDIA_UNKNOWN_TYPE && type != _format->type)
return B_BAD_TYPE;
return B_OK;
}
status_t
BMediaFormats::GetAVIFormatFor(uint32 codec,
media_format* _format, media_type type)
{
UNIMPLEMENTED();
BMediaFormats formats;
media_format_description description;
description.family = B_AVI_FORMAT_FAMILY;
description.u.avi.codec = codec;
status_t status = formats.GetFormatFor(description, _format);
if (status < B_OK)
return status;
if (type != B_MEDIA_UNKNOWN_TYPE && type != _format->type)
return B_BAD_TYPE;
return B_OK;
}
status_t
BMediaFormats::GetQuicktimeFormatFor(uint32 vendor, uint32 codec,
media_format* _format, media_type type)
{
BMediaFormats formats;
media_format_description description;
description.family = B_QUICKTIME_FORMAT_FAMILY;
description.u.quicktime.vendor = vendor;
description.u.quicktime.codec = codec;
status_t status = formats.GetFormatFor(description, _format);
if (status < B_OK)
return status;
if (type != B_MEDIA_UNKNOWN_TYPE && type != _format->type)
return B_BAD_TYPE;
return B_OK;
}
status_t
BMediaFormats::RewindFormats()
{
if (!sLock.IsLocked() || sLock.LockingThread() != find_thread(NULL)) {
// TODO: Shouldn't we simply drop into the debugger in this case?
return B_NOT_ALLOWED;
}
fIteratorIndex = 0;
return B_OK;
}
status_t
BMediaFormats::GetNextFormat(media_format* _format,
media_format_description* _description)
{
if (!sLock.IsLocked() || sLock.LockingThread() != find_thread(NULL)) {
// TODO: Shouldn't we simply drop into the debugger in this case?
return B_NOT_ALLOWED;
}
if (fIteratorIndex == 0) {
// This is the first call, so let's make sure we have current data to
// operate on.
status_t status = update_media_formats();
if (status < B_OK)
return status;
}
meta_format* format = sFormats.ItemAt(fIteratorIndex++);
if (format == NULL)
return B_BAD_INDEX;
return B_OK;
}
bool
BMediaFormats::Lock()
{
return sLock.Lock();
}
void
BMediaFormats::Unlock()
{
sLock.Unlock();
}
status_t
BMediaFormats::MakeFormatFor(const media_format_description* descriptions,
int32 descriptionCount, media_format* format, uint32 flags,
void* _reserved)
{
status_t status = FormatManager::GetInstance()->MakeFormatFor(descriptions,
descriptionCount, *format, flags, _reserved);
return status;
}
// #pragma mark - deprecated API
status_t
BMediaFormats::MakeFormatFor(const media_format_description& description,
const media_format& inFormat, media_format* _outFormat)
{
*_outFormat = inFormat;
return MakeFormatFor(&description, 1, _outFormat);
}