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