xref: /haiku/src/kits/media/MediaFormats.cpp (revision 0d452c8f34013b611a54c746a71c05e28796eae2)
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 "DataExchange.h"
12 #include "MetaFormat.h"
13 #include "debug.h"
14 
15 #include <MediaFormats.h>
16 #include <ObjectList.h>
17 #include <Message.h>
18 #include <Autolock.h>
19 
20 #include <string.h>
21 
22 using namespace BPrivate::media;
23 
24 
25 static BLocker sLock;
26 static BObjectList<meta_format> sFormats;
27 static bigtime_t sLastFormatsUpdate;
28 
29 
30 status_t
31 get_next_encoder(int32* cookie, const media_file_format* fileFormat,
32 	const media_format* inputFormat, media_format* _outputFormat,
33 	media_codec_info* _codecInfo)
34 {
35 	// TODO: If fileFormat is provided (existing apps also pass NULL),
36 	// we could at least check fileFormat->capabilities against
37 	// outputFormat->type without even contacting the server.
38 
39 	if (cookie == NULL || inputFormat == NULL || _codecInfo == NULL)
40 		return B_BAD_VALUE;
41 
42 	while (true) {
43 		server_get_codec_info_request request;
44 		request.cookie = *cookie;
45 		server_get_codec_info_reply reply;
46 		status_t ret = QueryServer(SERVER_GET_CODEC_INFO_FOR_COOKIE, &request,
47 			sizeof(request), &reply, sizeof(reply));
48 		if (ret != B_OK)
49 			return ret;
50 
51 		*cookie = *cookie + 1;
52 
53 		if (fileFormat != NULL && reply.format_family != B_ANY_FORMAT_FAMILY
54 			&& fileFormat->family != B_ANY_FORMAT_FAMILY
55 			&& fileFormat->family != reply.format_family) {
56 			continue;
57 		}
58 
59 		if (!reply.input_format.Matches(inputFormat))
60 			continue;
61 
62 		if (_outputFormat != NULL)
63 			*_outputFormat = reply.output_format;
64 
65 		*_codecInfo = reply.codec_info;
66 		break;
67 	}
68 
69 	return B_OK;
70 }
71 
72 
73 status_t
74 get_next_encoder(int32* cookie, const media_file_format* fileFormat,
75 	const media_format* inputFormat, const media_format* outputFormat,
76 	media_codec_info* _codecInfo, media_format* _acceptedInputFormat,
77 	media_format* _acceptedOutputFormat)
78 {
79 	// TODO: If fileFormat is provided (existing apps also pass NULL),
80 	// we could at least check fileFormat->capabilities against
81 	// outputFormat->type without even contacting the server.
82 
83 	if (cookie == NULL || inputFormat == NULL || outputFormat == NULL
84 		|| _codecInfo == NULL) {
85 		return B_BAD_VALUE;
86 	}
87 
88 	while (true) {
89 		server_get_codec_info_request request;
90 		request.cookie = *cookie;
91 		server_get_codec_info_reply reply;
92 		status_t ret = QueryServer(SERVER_GET_CODEC_INFO_FOR_COOKIE, &request,
93 			sizeof(request), &reply, sizeof(reply));
94 		if (ret != B_OK)
95 			return ret;
96 
97 		*cookie = *cookie + 1;
98 
99 		if (fileFormat != NULL && reply.format_family != B_ANY_FORMAT_FAMILY
100 			&& fileFormat->family != B_ANY_FORMAT_FAMILY
101 			&& fileFormat->family != reply.format_family) {
102 			continue;
103 		}
104 
105 		if (!reply.input_format.Matches(inputFormat)
106 			|| !reply.output_format.Matches(outputFormat)) {
107 			continue;
108 		}
109 
110 		// TODO: These formats are currently way too generic. For example,
111 		// an encoder may want to adjust video width to a multiple of 16,
112 		// or overwrite the intput and or output color space. To make this
113 		// possible, we actually have to instantiate an Encoder here and
114 		// ask it to specifiy the format.
115 		if (_acceptedInputFormat != NULL)
116 			*_acceptedInputFormat = reply.input_format;
117 		if (_acceptedOutputFormat != NULL)
118 			*_acceptedOutputFormat = reply.output_format;
119 
120 		*_codecInfo = reply.codec_info;
121 		break;
122 	}
123 
124 	return B_OK;
125 }
126 
127 
128 status_t
129 get_next_encoder(int32* cookie, media_codec_info* _codecInfo)
130 {
131 	if (cookie == NULL || _codecInfo == NULL)
132 		return B_BAD_VALUE;
133 
134 	server_get_codec_info_request request;
135 	request.cookie = *cookie;
136 	server_get_codec_info_reply reply;
137 	status_t ret = QueryServer(SERVER_GET_CODEC_INFO_FOR_COOKIE, &request,
138 		sizeof(request), &reply, sizeof(reply));
139 	if (ret != B_OK)
140 		return ret;
141 
142 	*cookie = *cookie + 1;
143 	*_codecInfo = reply.codec_info;
144 
145 	return B_OK;
146 }
147 
148 
149 bool
150 does_file_accept_format(const media_file_format* _fileFormat,
151 	media_format* format, uint32 flags)
152 {
153 	UNIMPLEMENTED();
154 	return false;
155 }
156 
157 
158 //	#pragma mark -
159 
160 
161 _media_format_description::_media_format_description()
162 {
163 	memset(this, 0, sizeof(*this));
164 }
165 
166 
167 _media_format_description::~_media_format_description()
168 {
169 }
170 
171 
172 _media_format_description::_media_format_description(
173 	const _media_format_description& other)
174 {
175 	memcpy(this, &other, sizeof(*this));
176 }
177 
178 
179 _media_format_description&
180 _media_format_description::operator=(const _media_format_description& other)
181 {
182 	memcpy(this, &other, sizeof(*this));
183 	return *this;
184 }
185 
186 
187 bool
188 operator==(const media_format_description& a,
189 	const media_format_description& b)
190 {
191 	if (a.family != b.family)
192 		return false;
193 
194 	switch (a.family) {
195 		case B_BEOS_FORMAT_FAMILY:
196 			return a.u.beos.format == b.u.beos.format;
197 		case B_QUICKTIME_FORMAT_FAMILY:
198 			return a.u.quicktime.codec == b.u.quicktime.codec
199 				&& a.u.quicktime.vendor == b.u.quicktime.vendor;
200 		case B_AVI_FORMAT_FAMILY:
201 			return a.u.avi.codec == b.u.avi.codec;
202 		case B_ASF_FORMAT_FAMILY:
203 			return a.u.asf.guid == b.u.asf.guid;
204 		case B_MPEG_FORMAT_FAMILY:
205 			return a.u.mpeg.id == b.u.mpeg.id;
206 		case B_WAV_FORMAT_FAMILY:
207 			return a.u.wav.codec == b.u.wav.codec;
208 		case B_AIFF_FORMAT_FAMILY:
209 			return a.u.aiff.codec == b.u.aiff.codec;
210 		case B_AVR_FORMAT_FAMILY:
211 			return a.u.avr.id == b.u.avr.id;
212 		case B_MISC_FORMAT_FAMILY:
213 			return a.u.misc.file_format == b.u.misc.file_format
214 				&& a.u.misc.codec == b.u.misc.codec;
215 
216 		default:
217 			return false;
218 	}
219 }
220 
221 
222 bool
223 operator<(const media_format_description& a, const media_format_description& b)
224 {
225 	if (a.family != b.family)
226 		return a.family < b.family;
227 
228 	switch (a.family) {
229 		case B_BEOS_FORMAT_FAMILY:
230 			return a.u.beos.format < b.u.beos.format;
231 		case B_QUICKTIME_FORMAT_FAMILY:
232 			if (a.u.quicktime.vendor == b.u.quicktime.vendor)
233 				return a.u.quicktime.codec < b.u.quicktime.codec;
234 			return a.u.quicktime.vendor < b.u.quicktime.vendor;
235 		case B_AVI_FORMAT_FAMILY:
236 			return a.u.avi.codec < b.u.avi.codec;
237 		case B_ASF_FORMAT_FAMILY:
238 			return a.u.asf.guid < b.u.asf.guid;
239 		case B_MPEG_FORMAT_FAMILY:
240 			return a.u.mpeg.id < b.u.mpeg.id;
241 		case B_WAV_FORMAT_FAMILY:
242 			return a.u.wav.codec < b.u.wav.codec;
243 		case B_AIFF_FORMAT_FAMILY:
244 			return a.u.aiff.codec < b.u.aiff.codec;
245 		case B_AVR_FORMAT_FAMILY:
246 			return a.u.avr.id < b.u.avr.id;
247 		case B_MISC_FORMAT_FAMILY:
248 			if (a.u.misc.file_format == b.u.misc.file_format)
249 				return a.u.misc.codec < b.u.misc.codec;
250 			return a.u.misc.file_format < b.u.misc.file_format;
251 
252 		default:
253 			return true;
254 	}
255 }
256 
257 
258 bool
259 operator==(const GUID& a, const GUID& b)
260 {
261 	return memcmp(&a, &b, sizeof(a)) == 0;
262 }
263 
264 
265 bool
266 operator<(const GUID& a, const GUID& b)
267 {
268 	return memcmp(&a, &b, sizeof(a)) < 0;
269 }
270 
271 
272 //	#pragma mark -
273 //
274 //	Some (meta) formats supply functions
275 
276 
277 meta_format::meta_format()
278 	:
279 	id(0)
280 {
281 }
282 
283 
284 
285 meta_format::meta_format(const media_format_description& description,
286 	const media_format& format, int32 id)
287 	:
288 	description(description),
289 	format(format),
290 	id(id)
291 {
292 }
293 
294 
295 meta_format::meta_format(const media_format_description& description)
296 	:
297 	description(description),
298 	id(0)
299 {
300 }
301 
302 
303 meta_format::meta_format(const meta_format& other)
304 	:
305 	description(other.description),
306 	format(other.format)
307 {
308 }
309 
310 
311 bool
312 meta_format::Matches(const media_format& otherFormat,
313 	media_format_family family)
314 {
315 	if (family != description.family)
316 		return false;
317 
318 	return format.Matches(&otherFormat);
319 }
320 
321 
322 int
323 meta_format::CompareDescriptions(const meta_format* a, const meta_format* b)
324 {
325 	if (a->description == b->description)
326 		return 0;
327 
328 	if (a->description < b->description)
329 		return -1;
330 
331 	return 1;
332 }
333 
334 
335 int
336 meta_format::Compare(const meta_format* a, const meta_format* b)
337 {
338 	int compare = CompareDescriptions(a, b);
339 	if (compare != 0)
340 		return compare;
341 
342 	return a->id - b->id;
343 }
344 
345 
346 /** We share one global list for all BMediaFormats in the team - since the
347  *	format data can change at any time, we have to ask the server to update
348  *	the list to ensure that we are working on the latest data set.
349  *	The list we get from the server is always sorted by description.
350  *	The formats lock has to be hold when you call this function.
351  */
352 
353 static status_t
354 update_media_formats()
355 {
356 	if (!sLock.IsLocked())
357 		return B_NOT_ALLOWED;
358 
359 	BMessage request(MEDIA_SERVER_GET_FORMATS);
360 	request.AddInt64("last_timestamp", sLastFormatsUpdate);
361 
362 	BMessage reply;
363 	status_t status = QueryServer(request, reply);
364 	if (status < B_OK) {
365 		ERROR("BMediaFormats: Could not update formats: %s\n",
366 			strerror(status));
367 		return status;
368 	}
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 	BMessage request(MEDIA_SERVER_MAKE_FORMAT_FOR);
620 	for (int32 i = 0 ; i < descriptionCount ; i++) {
621 		request.AddData("description", B_RAW_TYPE, &descriptions[i],
622 			sizeof(descriptions[i]));
623 	}
624 	request.AddData("format", B_RAW_TYPE, format, sizeof(*format));
625 	request.AddData("flags", B_UINT32_TYPE, &flags, sizeof(flags));
626 	request.AddPointer("_reserved", _reserved);
627 
628 	BMessage reply;
629 	status_t status = QueryServer(request, reply);
630 	if (status != B_OK) {
631 		ERROR("BMediaFormats: Could not make a format: %s\n", strerror(status));
632 		return status;
633 	}
634 
635 	// check the status
636 	if (reply.FindInt32("result", &status) < B_OK)
637 		return B_ERROR;
638 	if (status != B_OK)
639 		return status;
640 
641 	// get the format
642 	const void* data;
643 	ssize_t size;
644 	if (reply.FindData("format", B_RAW_TYPE, 0, &data, &size) != B_OK)
645 		return B_ERROR;
646 	if (size != sizeof(*format))
647 		return B_ERROR;
648 
649 	// copy the BMessage's data into our format
650 	*format = *(media_format*)data;
651 
652 	return B_OK;
653 }
654 
655 
656 // #pragma mark - deprecated API
657 
658 
659 status_t
660 BMediaFormats::MakeFormatFor(const media_format_description& description,
661 	const media_format& inFormat, media_format* _outFormat)
662 {
663 	*_outFormat = inFormat;
664 	return MakeFormatFor(&description, 1, _outFormat);
665 }
666 
667