xref: /haiku/src/kits/media/MediaFormats.cpp (revision 1deede7388b04dbeec5af85cae7164735ea9e70d)
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 
24 using namespace BPrivate::media;
25 
26 
27 static BLocker sLock;
28 static BObjectList<meta_format> sFormats;
29 static bigtime_t sLastFormatsUpdate;
30 
31 
32 status_t
33 get_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 
79 status_t
80 get_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 
138 status_t
139 get_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 
159 bool
160 does_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 
197 bool
198 operator==(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 
232 bool
233 operator<(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 
268 bool
269 operator==(const GUID& a, const GUID& b)
270 {
271 	return memcmp(&a, &b, sizeof(a)) == 0;
272 }
273 
274 
275 bool
276 operator<(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 
287 meta_format::meta_format()
288 	:
289 	id(0)
290 {
291 }
292 
293 
294 
295 meta_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 
305 meta_format::meta_format(const media_format_description& description)
306 	:
307 	description(description),
308 	id(0)
309 {
310 }
311 
312 
313 meta_format::meta_format(const meta_format& other)
314 	:
315 	description(other.description),
316 	format(other.format)
317 {
318 }
319 
320 
321 bool
322 meta_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 
332 int
333 meta_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 
345 int
346 meta_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  */
361 static status_t
362 update_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 
423 BMediaFormats::BMediaFormats()
424 	:
425 	fIteratorIndex(0)
426 {
427 }
428 
429 
430 BMediaFormats::~BMediaFormats()
431 {
432 }
433 
434 
435 status_t
436 BMediaFormats::InitCheck()
437 {
438 	return sLock.InitCheck();
439 }
440 
441 
442 status_t
443 BMediaFormats::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 
468 status_t
469 BMediaFormats::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 
502 status_t
503 BMediaFormats::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 
523 status_t
524 BMediaFormats::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 
545 status_t
546 BMediaFormats::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 
567 status_t
568 BMediaFormats::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 
580 status_t
581 BMediaFormats::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 
605 bool
606 BMediaFormats::Lock()
607 {
608 	return sLock.Lock();
609 }
610 
611 
612 void
613 BMediaFormats::Unlock()
614 {
615 	sLock.Unlock();
616 }
617 
618 
619 status_t
620 BMediaFormats::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 
634 status_t
635 BMediaFormats::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