xref: /haiku/src/kits/media/MediaFormats.cpp (revision a30a4a41f948ebb03b95dab065a27a584ac0c97a)
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 "debug.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 	BMessage reply;
368 	FormatManager::GetInstance()->GetFormats(sLastFormatsUpdate, reply);
369 
370 	// do we need an update at all?
371 	bool needUpdate;
372 	if (reply.FindBool("need_update", &needUpdate) < B_OK)
373 		return B_ERROR;
374 	if (!needUpdate)
375 		return B_OK;
376 
377 	// update timestamp and check if the message is okay
378 	type_code code;
379 	int32 count;
380 	if (reply.FindInt64("timestamp", &sLastFormatsUpdate) < B_OK
381 		|| reply.GetInfo("formats", &code, &count) < B_OK)
382 		return B_ERROR;
383 
384 	// overwrite already existing formats
385 
386 	int32 index = 0;
387 	for (; index < sFormats.CountItems() && index < count; index++) {
388 		meta_format* item = sFormats.ItemAt(index);
389 
390 		const meta_format* newItem;
391 		ssize_t size;
392 		if (reply.FindData("formats", MEDIA_META_FORMAT_TYPE, index,
393 				(const void**)&newItem, &size) == B_OK)
394 			*item = *newItem;
395 	}
396 
397 	// allocate additional formats
398 
399 	for (; index < count; index++) {
400 		const meta_format* newItem;
401 		ssize_t size;
402 		if (reply.FindData("formats", MEDIA_META_FORMAT_TYPE, index,
403 				(const void**)&newItem, &size) == B_OK)
404 			sFormats.AddItem(new meta_format(*newItem));
405 	}
406 
407 	// remove no longer used formats
408 
409 	while (count < sFormats.CountItems())
410 		delete sFormats.RemoveItemAt(count);
411 
412 	return B_OK;
413 }
414 
415 
416 //	#pragma mark -
417 
418 
419 BMediaFormats::BMediaFormats()
420 	:
421 	fIteratorIndex(0)
422 {
423 }
424 
425 
426 BMediaFormats::~BMediaFormats()
427 {
428 }
429 
430 
431 status_t
432 BMediaFormats::InitCheck()
433 {
434 	return sLock.Sem() >= B_OK ? B_OK : sLock.Sem();
435 }
436 
437 
438 status_t
439 BMediaFormats::GetCodeFor(const media_format& format,
440 	media_format_family family,
441 	media_format_description* _description)
442 {
443 	BAutolock locker(sLock);
444 
445 	status_t status = update_media_formats();
446 	if (status < B_OK)
447 		return status;
448 
449 	// search for a matching format
450 
451 	for (int32 index = sFormats.CountItems(); index-- > 0;) {
452 		meta_format* metaFormat = sFormats.ItemAt(index);
453 
454 		if (metaFormat->Matches(format, family)) {
455 			*_description = metaFormat->description;
456 			return B_OK;
457 		}
458 	}
459 
460 	return B_MEDIA_BAD_FORMAT;
461 }
462 
463 
464 status_t
465 BMediaFormats::GetFormatFor(const media_format_description& description,
466 	media_format* _format)
467 {
468 	BAutolock locker(sLock);
469 
470 	status_t status = update_media_formats();
471 	if (status < B_OK) {
472 		ERROR("BMediaFormats: updating formats from server failed: %s!\n",
473 			strerror(status));
474 		return status;
475 	}
476 	TRACE("search for description family = %d, a = 0x%lx, b = 0x%lx\n",
477 		description.family, description.u.misc.file_format,
478 		description.u.misc.codec);
479 
480 	// search for a matching format description
481 
482 	meta_format other(description);
483 	const meta_format* metaFormat = sFormats.BinarySearch(other,
484 		meta_format::CompareDescriptions);
485 	TRACE("meta format == %p\n", metaFormat);
486 	if (metaFormat == NULL) {
487 		memset(_format, 0, sizeof(*_format)); // clear to widlcard
488 		return B_MEDIA_BAD_FORMAT;
489 	}
490 
491 	// found it!
492 	*_format = metaFormat->format;
493 	return B_OK;
494 }
495 
496 
497 status_t
498 BMediaFormats::GetBeOSFormatFor(uint32 format,
499 	media_format* _format, media_type type)
500 {
501 	BMediaFormats formats;
502 
503 	media_format_description description;
504 	description.family = B_BEOS_FORMAT_FAMILY;
505 	description.u.beos.format = format;
506 
507 	status_t status = formats.GetFormatFor(description, _format);
508 	if (status < B_OK)
509 		return status;
510 
511 	if (type != B_MEDIA_UNKNOWN_TYPE && type != _format->type)
512 		return B_BAD_TYPE;
513 
514 	return B_OK;
515 }
516 
517 
518 status_t
519 BMediaFormats::GetAVIFormatFor(uint32 codec,
520 	media_format* _format, media_type type)
521 {
522 	UNIMPLEMENTED();
523 	BMediaFormats formats;
524 
525 	media_format_description description;
526 	description.family = B_AVI_FORMAT_FAMILY;
527 	description.u.avi.codec = codec;
528 
529 	status_t status = formats.GetFormatFor(description, _format);
530 	if (status < B_OK)
531 		return status;
532 
533 	if (type != B_MEDIA_UNKNOWN_TYPE && type != _format->type)
534 		return B_BAD_TYPE;
535 
536 	return B_OK;
537 }
538 
539 
540 status_t
541 BMediaFormats::GetQuicktimeFormatFor(uint32 vendor, uint32 codec,
542 	media_format* _format, media_type type)
543 {
544 	BMediaFormats formats;
545 
546 	media_format_description description;
547 	description.family = B_QUICKTIME_FORMAT_FAMILY;
548 	description.u.quicktime.vendor = vendor;
549 	description.u.quicktime.codec = codec;
550 
551 	status_t status = formats.GetFormatFor(description, _format);
552 	if (status < B_OK)
553 		return status;
554 
555 	if (type != B_MEDIA_UNKNOWN_TYPE && type != _format->type)
556 		return B_BAD_TYPE;
557 
558 	return B_OK;
559 }
560 
561 
562 status_t
563 BMediaFormats::RewindFormats()
564 {
565 	if (!sLock.IsLocked() || sLock.LockingThread() != find_thread(NULL)) {
566 		// TODO: Shouldn't we simply drop into the debugger in this case?
567 		return B_NOT_ALLOWED;
568 	}
569 
570 	fIteratorIndex = 0;
571 	return B_OK;
572 }
573 
574 
575 status_t
576 BMediaFormats::GetNextFormat(media_format* _format,
577 	media_format_description* _description)
578 {
579 	if (!sLock.IsLocked() || sLock.LockingThread() != find_thread(NULL)) {
580 		// TODO: Shouldn't we simply drop into the debugger in this case?
581 		return B_NOT_ALLOWED;
582 	}
583 
584 	if (fIteratorIndex == 0) {
585 		// This is the first call, so let's make sure we have current data to
586 		// operate on.
587 		status_t status = update_media_formats();
588 		if (status < B_OK)
589 			return status;
590 	}
591 
592 	meta_format* format = sFormats.ItemAt(fIteratorIndex++);
593 	if (format == NULL)
594 		return B_BAD_INDEX;
595 
596 	return B_OK;
597 }
598 
599 
600 bool
601 BMediaFormats::Lock()
602 {
603 	return sLock.Lock();
604 }
605 
606 
607 void
608 BMediaFormats::Unlock()
609 {
610 	sLock.Unlock();
611 }
612 
613 
614 status_t
615 BMediaFormats::MakeFormatFor(const media_format_description* descriptions,
616 	int32 descriptionCount, media_format* format, uint32 flags,
617 	void* _reserved)
618 {
619 	status_t status = FormatManager::GetInstance()->MakeFormatFor(descriptions,
620 		descriptionCount, *format, flags, _reserved);
621 
622 	return status;
623 }
624 
625 
626 // #pragma mark - deprecated API
627 
628 
629 status_t
630 BMediaFormats::MakeFormatFor(const media_format_description& description,
631 	const media_format& inFormat, media_format* _outFormat)
632 {
633 	*_outFormat = inFormat;
634 	return MakeFormatFor(&description, 1, _outFormat);
635 }
636 
637