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
get_next_encoder(int32 * cookie,const media_file_format * fileFormat,const media_format * inputFormat,media_format * _outputFormat,media_codec_info * _codecInfo)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
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)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
get_next_encoder(int32 * cookie,media_codec_info * _codecInfo)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
does_file_accept_format(const media_file_format * _fileFormat,media_format * format,uint32 flags)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
_media_format_description()171 _media_format_description::_media_format_description()
172 {
173 memset(this, 0, sizeof(*this));
174 }
175
176
~_media_format_description()177 _media_format_description::~_media_format_description()
178 {
179 }
180
181
_media_format_description(const _media_format_description & other)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&
operator =(const _media_format_description & other)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
operator ==(const media_format_description & a,const media_format_description & b)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
operator <(const media_format_description & a,const media_format_description & b)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
operator ==(const GUID & a,const GUID & b)269 operator==(const GUID& a, const GUID& b)
270 {
271 return memcmp(&a, &b, sizeof(a)) == 0;
272 }
273
274
275 bool
operator <(const GUID & a,const GUID & b)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
meta_format()287 meta_format::meta_format()
288 :
289 id(0)
290 {
291 }
292
293
294
meta_format(const media_format_description & description,const media_format & format,int32 id)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
meta_format(const media_format_description & description)305 meta_format::meta_format(const media_format_description& description)
306 :
307 description(description),
308 id(0)
309 {
310 }
311
312
meta_format(const meta_format & other)313 meta_format::meta_format(const meta_format& other)
314 :
315 description(other.description),
316 format(other.format)
317 {
318 }
319
320
321 bool
Matches(const media_format & otherFormat,media_format_family family)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
CompareDescriptions(const meta_format * a,const meta_format * b)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
Compare(const meta_format * a,const meta_format * b)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
update_media_formats()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
BMediaFormats()423 BMediaFormats::BMediaFormats()
424 :
425 fIteratorIndex(0)
426 {
427 }
428
429
~BMediaFormats()430 BMediaFormats::~BMediaFormats()
431 {
432 }
433
434
435 status_t
InitCheck()436 BMediaFormats::InitCheck()
437 {
438 return sLock.InitCheck();
439 }
440
441
442 status_t
GetCodeFor(const media_format & format,media_format_family family,media_format_description * _description)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
GetFormatFor(const media_format_description & description,media_format * _format)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
GetBeOSFormatFor(uint32 format,media_format * _format,media_type type)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
GetAVIFormatFor(uint32 codec,media_format * _format,media_type type)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
GetQuicktimeFormatFor(uint32 vendor,uint32 codec,media_format * _format,media_type type)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
RewindFormats()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
GetNextFormat(media_format * _format,media_format_description * _description)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
Lock()606 BMediaFormats::Lock()
607 {
608 return sLock.Lock();
609 }
610
611
612 void
Unlock()613 BMediaFormats::Unlock()
614 {
615 sLock.Unlock();
616 }
617
618
619 status_t
MakeFormatFor(const media_format_description * descriptions,int32 descriptionCount,media_format * format,uint32 flags,void * _reserved)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
MakeFormatFor(const media_format_description & description,const media_format & inFormat,media_format * _outFormat)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