1/*
2 * Copyright 2004-2009, The Haiku Project. All rights reserved.
3 * Distributed under the terms of the MIT license.
4 *
5 * Authors:
6 *		Axel D��rfler
7 *		Marcus Overhagen
8 */
9
10
11#include "AddOnManager.h"
12#include "DataExchange.h"
13#include "FormatManager.h"
14#include "MetaFormat.h"
15#include "MediaDebug.h"
16
17#include <MediaFormats.h>
18#include <ObjectList.h>
19#include <Message.h>
20#include <Autolock.h>
21
22#include <string.h>
23
24using namespace BPrivate::media;
25
26
27static BLocker sLock;
28static BObjectList<meta_format> sFormats;
29static bigtime_t sLastFormatsUpdate;
30
31
32status_t
33get_next_encoder(int32* cookie, const media_file_format* fileFormat,
34	const media_format* inputFormat, media_format* _outputFormat,
35	media_codec_info* _codecInfo)
36{
37	// TODO: If fileFormat is provided (existing apps also pass NULL),
38	// we could at least check fileFormat->capabilities against
39	// outputFormat->type without even contacting the server.
40
41	if (cookie == NULL || inputFormat == NULL || _codecInfo == NULL)
42		return B_BAD_VALUE;
43
44	while (true) {
45		media_codec_info candidateCodecInfo;
46		media_format_family candidateFormatFamily;
47		media_format candidateInputFormat;
48		media_format candidateOutputFormat;
49
50		status_t ret = AddOnManager::GetInstance()->GetCodecInfo(
51			&candidateCodecInfo, &candidateFormatFamily,
52			&candidateInputFormat, &candidateOutputFormat, *cookie);
53
54		if (ret != B_OK)
55			return ret;
56
57		*cookie = *cookie + 1;
58
59		if (fileFormat != NULL && candidateFormatFamily != B_ANY_FORMAT_FAMILY
60			&& fileFormat->family != B_ANY_FORMAT_FAMILY
61			&& fileFormat->family != candidateFormatFamily) {
62			continue;
63		}
64
65		if (!candidateInputFormat.Matches(inputFormat))
66			continue;
67
68		if (_outputFormat != NULL)
69			*_outputFormat = candidateOutputFormat;
70
71		*_codecInfo = candidateCodecInfo;
72		break;
73	}
74
75	return B_OK;
76}
77
78
79status_t
80get_next_encoder(int32* cookie, const media_file_format* fileFormat,
81	const media_format* inputFormat, const media_format* outputFormat,
82	media_codec_info* _codecInfo, media_format* _acceptedInputFormat,
83	media_format* _acceptedOutputFormat)
84{
85	// TODO: If fileFormat is provided (existing apps also pass NULL),
86	// we could at least check fileFormat->capabilities against
87	// outputFormat->type without even contacting the server.
88
89	if (cookie == NULL || inputFormat == NULL || outputFormat == NULL
90		|| _codecInfo == NULL) {
91		return B_BAD_VALUE;
92	}
93
94	while (true) {
95		media_codec_info candidateCodecInfo;
96		media_format_family candidateFormatFamily;
97		media_format candidateInputFormat;
98		media_format candidateOutputFormat;
99
100		status_t ret = AddOnManager::GetInstance()->GetCodecInfo(
101			&candidateCodecInfo, &candidateFormatFamily, &candidateInputFormat,
102			&candidateOutputFormat, *cookie);
103
104		if (ret != B_OK)
105			return ret;
106
107		*cookie = *cookie + 1;
108
109		if (fileFormat != NULL && candidateFormatFamily != B_ANY_FORMAT_FAMILY
110			&& fileFormat->family != B_ANY_FORMAT_FAMILY
111			&& fileFormat->family != candidateFormatFamily) {
112			continue;
113		}
114
115		if (!candidateInputFormat.Matches(inputFormat)
116			|| !candidateOutputFormat.Matches(outputFormat)) {
117			continue;
118		}
119
120		// TODO: These formats are currently way too generic. For example,
121		// an encoder may want to adjust video width to a multiple of 16,
122		// or overwrite the intput and or output color space. To make this
123		// possible, we actually have to instantiate an Encoder here and
124		// ask it to specifiy the format.
125		if (_acceptedInputFormat != NULL)
126			*_acceptedInputFormat = candidateInputFormat;
127		if (_acceptedOutputFormat != NULL)
128			*_acceptedOutputFormat = candidateOutputFormat;
129
130		*_codecInfo = candidateCodecInfo;
131		break;
132	}
133
134	return B_OK;
135}
136
137
138status_t
139get_next_encoder(int32* cookie, media_codec_info* _codecInfo)
140{
141	if (cookie == NULL || _codecInfo == NULL)
142		return B_BAD_VALUE;
143
144	media_format_family formatFamily;
145	media_format inputFormat;
146	media_format outputFormat;
147
148	status_t ret = AddOnManager::GetInstance()->GetCodecInfo(_codecInfo,
149		&formatFamily, &inputFormat, &outputFormat, *cookie);
150	if (ret != B_OK)
151		return ret;
152
153	*cookie = *cookie + 1;
154
155	return B_OK;
156}
157
158
159bool
160does_file_accept_format(const media_file_format* _fileFormat,
161	media_format* format, uint32 flags)
162{
163	UNIMPLEMENTED();
164	return false;
165}
166
167
168//	#pragma mark -
169
170
171_media_format_description::_media_format_description()
172{
173	memset(this, 0, sizeof(*this));
174}
175
176
177_media_format_description::~_media_format_description()
178{
179}
180
181
182_media_format_description::_media_format_description(
183	const _media_format_description& other)
184{
185	memcpy(this, &other, sizeof(*this));
186}
187
188
189_media_format_description&
190_media_format_description::operator=(const _media_format_description& other)
191{
192	memcpy(this, &other, sizeof(*this));
193	return *this;
194}
195
196
197bool
198operator==(const media_format_description& a,
199	const media_format_description& b)
200{
201	if (a.family != b.family)
202		return false;
203
204	switch (a.family) {
205		case B_BEOS_FORMAT_FAMILY:
206			return a.u.beos.format == b.u.beos.format;
207		case B_QUICKTIME_FORMAT_FAMILY:
208			return a.u.quicktime.codec == b.u.quicktime.codec
209				&& a.u.quicktime.vendor == b.u.quicktime.vendor;
210		case B_AVI_FORMAT_FAMILY:
211			return a.u.avi.codec == b.u.avi.codec;
212		case B_ASF_FORMAT_FAMILY:
213			return a.u.asf.guid == b.u.asf.guid;
214		case B_MPEG_FORMAT_FAMILY:
215			return a.u.mpeg.id == b.u.mpeg.id;
216		case B_WAV_FORMAT_FAMILY:
217			return a.u.wav.codec == b.u.wav.codec;
218		case B_AIFF_FORMAT_FAMILY:
219			return a.u.aiff.codec == b.u.aiff.codec;
220		case B_AVR_FORMAT_FAMILY:
221			return a.u.avr.id == b.u.avr.id;
222		case B_MISC_FORMAT_FAMILY:
223			return a.u.misc.file_format == b.u.misc.file_format
224				&& a.u.misc.codec == b.u.misc.codec;
225
226		default:
227			return false;
228	}
229}
230
231
232bool
233operator<(const media_format_description& a, const media_format_description& b)
234{
235	if (a.family != b.family)
236		return a.family < b.family;
237
238	switch (a.family) {
239		case B_BEOS_FORMAT_FAMILY:
240			return a.u.beos.format < b.u.beos.format;
241		case B_QUICKTIME_FORMAT_FAMILY:
242			if (a.u.quicktime.vendor == b.u.quicktime.vendor)
243				return a.u.quicktime.codec < b.u.quicktime.codec;
244			return a.u.quicktime.vendor < b.u.quicktime.vendor;
245		case B_AVI_FORMAT_FAMILY:
246			return a.u.avi.codec < b.u.avi.codec;
247		case B_ASF_FORMAT_FAMILY:
248			return a.u.asf.guid < b.u.asf.guid;
249		case B_MPEG_FORMAT_FAMILY:
250			return a.u.mpeg.id < b.u.mpeg.id;
251		case B_WAV_FORMAT_FAMILY:
252			return a.u.wav.codec < b.u.wav.codec;
253		case B_AIFF_FORMAT_FAMILY:
254			return a.u.aiff.codec < b.u.aiff.codec;
255		case B_AVR_FORMAT_FAMILY:
256			return a.u.avr.id < b.u.avr.id;
257		case B_MISC_FORMAT_FAMILY:
258			if (a.u.misc.file_format == b.u.misc.file_format)
259				return a.u.misc.codec < b.u.misc.codec;
260			return a.u.misc.file_format < b.u.misc.file_format;
261
262		default:
263			return true;
264	}
265}
266
267
268bool
269operator==(const GUID& a, const GUID& b)
270{
271	return memcmp(&a, &b, sizeof(a)) == 0;
272}
273
274
275bool
276operator<(const GUID& a, const GUID& b)
277{
278	return memcmp(&a, &b, sizeof(a)) < 0;
279}
280
281
282//	#pragma mark -
283//
284//	Some (meta) formats supply functions
285
286
287meta_format::meta_format()
288	:
289	id(0)
290{
291}
292
293
294
295meta_format::meta_format(const media_format_description& description,
296	const media_format& format, int32 id)
297	:
298	description(description),
299	format(format),
300	id(id)
301{
302}
303
304
305meta_format::meta_format(const media_format_description& description)
306	:
307	description(description),
308	id(0)
309{
310}
311
312
313meta_format::meta_format(const meta_format& other)
314	:
315	description(other.description),
316	format(other.format)
317{
318}
319
320
321bool
322meta_format::Matches(const media_format& otherFormat,
323	media_format_family family)
324{
325	if (family != description.family)
326		return false;
327
328	return format.Matches(&otherFormat);
329}
330
331
332int
333meta_format::CompareDescriptions(const meta_format* a, const meta_format* b)
334{
335	if (a->description == b->description)
336		return 0;
337
338	if (a->description < b->description)
339		return -1;
340
341	return 1;
342}
343
344
345int
346meta_format::Compare(const meta_format* a, const meta_format* b)
347{
348	int compare = CompareDescriptions(a, b);
349	if (compare != 0)
350		return compare;
351
352	return a->id - b->id;
353}
354
355
356/** We share one global list for all BMediaFormats in the team - since the
357 *	format data can change at any time, we have to update the list to ensure
358 *	that we are working on the latest data set. The list is always sorted by
359 *	description. The formats lock has to be held when you call this function.
360 */
361static status_t
362update_media_formats()
363{
364	if (!sLock.IsLocked())
365		return B_NOT_ALLOWED;
366
367	// We want the add-ons to register themselves with the format manager, so
368	// the list is up to date.
369	AddOnManager::GetInstance()->RegisterAddOns();
370
371	BMessage reply;
372	FormatManager::GetInstance()->GetFormats(sLastFormatsUpdate, reply);
373
374	// do we need an update at all?
375	bool needUpdate;
376	if (reply.FindBool("need_update", &needUpdate) < B_OK)
377		return B_ERROR;
378	if (!needUpdate)
379		return B_OK;
380
381	// update timestamp and check if the message is okay
382	type_code code;
383	int32 count;
384	if (reply.FindInt64("timestamp", &sLastFormatsUpdate) < B_OK
385		|| reply.GetInfo("formats", &code, &count) < B_OK)
386		return B_ERROR;
387
388	// overwrite already existing formats
389
390	int32 index = 0;
391	for (; index < sFormats.CountItems() && index < count; index++) {
392		meta_format* item = sFormats.ItemAt(index);
393
394		const meta_format* newItem;
395		ssize_t size;
396		if (reply.FindData("formats", MEDIA_META_FORMAT_TYPE, index,
397				(const void**)&newItem, &size) == B_OK)
398			*item = *newItem;
399	}
400
401	// allocate additional formats
402
403	for (; index < count; index++) {
404		const meta_format* newItem;
405		ssize_t size;
406		if (reply.FindData("formats", MEDIA_META_FORMAT_TYPE, index,
407				(const void**)&newItem, &size) == B_OK)
408			sFormats.AddItem(new meta_format(*newItem));
409	}
410
411	// remove no longer used formats
412
413	while (count < sFormats.CountItems())
414		delete sFormats.RemoveItemAt(count);
415
416	return B_OK;
417}
418
419
420//	#pragma mark -
421
422
423BMediaFormats::BMediaFormats()
424	:
425	fIteratorIndex(0)
426{
427}
428
429
430BMediaFormats::~BMediaFormats()
431{
432}
433
434
435status_t
436BMediaFormats::InitCheck()
437{
438	return sLock.InitCheck();
439}
440
441
442status_t
443BMediaFormats::GetCodeFor(const media_format& format,
444	media_format_family family,
445	media_format_description* _description)
446{
447	BAutolock locker(sLock);
448
449	status_t status = update_media_formats();
450	if (status < B_OK)
451		return status;
452
453	// search for a matching format
454
455	for (int32 index = sFormats.CountItems(); index-- > 0;) {
456		meta_format* metaFormat = sFormats.ItemAt(index);
457
458		if (metaFormat->Matches(format, family)) {
459			*_description = metaFormat->description;
460			return B_OK;
461		}
462	}
463
464	return B_MEDIA_BAD_FORMAT;
465}
466
467
468status_t
469BMediaFormats::GetFormatFor(const media_format_description& description,
470	media_format* _format)
471{
472	BAutolock locker(sLock);
473
474	status_t status = update_media_formats();
475	if (status < B_OK) {
476		ERROR("BMediaFormats: updating formats from server failed: %s!\n",
477			strerror(status));
478		return status;
479	}
480	TRACE("search for description family = %d, a = 0x%"
481		B_PRId32 "x, b = 0x%" B_PRId32 "x\n",
482		description.family, description.u.misc.file_format,
483		description.u.misc.codec);
484
485	// search for a matching format description
486
487	meta_format other(description);
488	const meta_format* metaFormat = sFormats.BinarySearch(other,
489		meta_format::CompareDescriptions);
490	TRACE("meta format == %p\n", metaFormat);
491	if (metaFormat == NULL) {
492		_format->Clear(); // clear to widlcard
493		return B_MEDIA_BAD_FORMAT;
494	}
495
496	// found it!
497	*_format = metaFormat->format;
498	return B_OK;
499}
500
501
502status_t
503BMediaFormats::GetBeOSFormatFor(uint32 format,
504	media_format* _format, media_type type)
505{
506	BMediaFormats formats;
507
508	media_format_description description;
509	description.family = B_BEOS_FORMAT_FAMILY;
510	description.u.beos.format = format;
511
512	status_t status = formats.GetFormatFor(description, _format);
513	if (status < B_OK)
514		return status;
515
516	if (type != B_MEDIA_UNKNOWN_TYPE && type != _format->type)
517		return B_BAD_TYPE;
518
519	return B_OK;
520}
521
522
523status_t
524BMediaFormats::GetAVIFormatFor(uint32 codec,
525	media_format* _format, media_type type)
526{
527	UNIMPLEMENTED();
528	BMediaFormats formats;
529
530	media_format_description description;
531	description.family = B_AVI_FORMAT_FAMILY;
532	description.u.avi.codec = codec;
533
534	status_t status = formats.GetFormatFor(description, _format);
535	if (status < B_OK)
536		return status;
537
538	if (type != B_MEDIA_UNKNOWN_TYPE && type != _format->type)
539		return B_BAD_TYPE;
540
541	return B_OK;
542}
543
544
545status_t
546BMediaFormats::GetQuicktimeFormatFor(uint32 vendor, uint32 codec,
547	media_format* _format, media_type type)
548{
549	BMediaFormats formats;
550
551	media_format_description description;
552	description.family = B_QUICKTIME_FORMAT_FAMILY;
553	description.u.quicktime.vendor = vendor;
554	description.u.quicktime.codec = codec;
555
556	status_t status = formats.GetFormatFor(description, _format);
557	if (status < B_OK)
558		return status;
559
560	if (type != B_MEDIA_UNKNOWN_TYPE && type != _format->type)
561		return B_BAD_TYPE;
562
563	return B_OK;
564}
565
566
567status_t
568BMediaFormats::RewindFormats()
569{
570	if (!sLock.IsLocked() || sLock.LockingThread() != find_thread(NULL)) {
571		// TODO: Shouldn't we simply drop into the debugger in this case?
572		return B_NOT_ALLOWED;
573	}
574
575	fIteratorIndex = 0;
576	return B_OK;
577}
578
579
580status_t
581BMediaFormats::GetNextFormat(media_format* _format,
582	media_format_description* _description)
583{
584	if (!sLock.IsLocked() || sLock.LockingThread() != find_thread(NULL)) {
585		// TODO: Shouldn't we simply drop into the debugger in this case?
586		return B_NOT_ALLOWED;
587	}
588
589	if (fIteratorIndex == 0) {
590		// This is the first call, so let's make sure we have current data to
591		// operate on.
592		status_t status = update_media_formats();
593		if (status < B_OK)
594			return status;
595	}
596
597	meta_format* format = sFormats.ItemAt(fIteratorIndex++);
598	if (format == NULL)
599		return B_BAD_INDEX;
600
601	return B_OK;
602}
603
604
605bool
606BMediaFormats::Lock()
607{
608	return sLock.Lock();
609}
610
611
612void
613BMediaFormats::Unlock()
614{
615	sLock.Unlock();
616}
617
618
619status_t
620BMediaFormats::MakeFormatFor(const media_format_description* descriptions,
621	int32 descriptionCount, media_format* format, uint32 flags,
622	void* _reserved)
623{
624	status_t status = FormatManager::GetInstance()->MakeFormatFor(descriptions,
625		descriptionCount, *format, flags, _reserved);
626
627	return status;
628}
629
630
631// #pragma mark - deprecated API
632
633
634status_t
635BMediaFormats::MakeFormatFor(const media_format_description& description,
636	const media_format& inFormat, media_format* _outFormat)
637{
638	*_outFormat = inFormat;
639	return MakeFormatFor(&description, 1, _outFormat);
640}
641
642