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 // 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.Sem() >= B_OK ? B_OK : sLock.Sem(); 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%lx, b = 0x%lx\n", 481 description.family, description.u.misc.file_format, 482 description.u.misc.codec); 483 484 // search for a matching format description 485 486 meta_format other(description); 487 const meta_format* metaFormat = sFormats.BinarySearch(other, 488 meta_format::CompareDescriptions); 489 TRACE("meta format == %p\n", metaFormat); 490 if (metaFormat == NULL) { 491 memset(_format, 0, sizeof(*_format)); // clear to widlcard 492 return B_MEDIA_BAD_FORMAT; 493 } 494 495 // found it! 496 *_format = metaFormat->format; 497 return B_OK; 498 } 499 500 501 status_t 502 BMediaFormats::GetBeOSFormatFor(uint32 format, 503 media_format* _format, media_type type) 504 { 505 BMediaFormats formats; 506 507 media_format_description description; 508 description.family = B_BEOS_FORMAT_FAMILY; 509 description.u.beos.format = format; 510 511 status_t status = formats.GetFormatFor(description, _format); 512 if (status < B_OK) 513 return status; 514 515 if (type != B_MEDIA_UNKNOWN_TYPE && type != _format->type) 516 return B_BAD_TYPE; 517 518 return B_OK; 519 } 520 521 522 status_t 523 BMediaFormats::GetAVIFormatFor(uint32 codec, 524 media_format* _format, media_type type) 525 { 526 UNIMPLEMENTED(); 527 BMediaFormats formats; 528 529 media_format_description description; 530 description.family = B_AVI_FORMAT_FAMILY; 531 description.u.avi.codec = codec; 532 533 status_t status = formats.GetFormatFor(description, _format); 534 if (status < B_OK) 535 return status; 536 537 if (type != B_MEDIA_UNKNOWN_TYPE && type != _format->type) 538 return B_BAD_TYPE; 539 540 return B_OK; 541 } 542 543 544 status_t 545 BMediaFormats::GetQuicktimeFormatFor(uint32 vendor, uint32 codec, 546 media_format* _format, media_type type) 547 { 548 BMediaFormats formats; 549 550 media_format_description description; 551 description.family = B_QUICKTIME_FORMAT_FAMILY; 552 description.u.quicktime.vendor = vendor; 553 description.u.quicktime.codec = codec; 554 555 status_t status = formats.GetFormatFor(description, _format); 556 if (status < B_OK) 557 return status; 558 559 if (type != B_MEDIA_UNKNOWN_TYPE && type != _format->type) 560 return B_BAD_TYPE; 561 562 return B_OK; 563 } 564 565 566 status_t 567 BMediaFormats::RewindFormats() 568 { 569 if (!sLock.IsLocked() || sLock.LockingThread() != find_thread(NULL)) { 570 // TODO: Shouldn't we simply drop into the debugger in this case? 571 return B_NOT_ALLOWED; 572 } 573 574 fIteratorIndex = 0; 575 return B_OK; 576 } 577 578 579 status_t 580 BMediaFormats::GetNextFormat(media_format* _format, 581 media_format_description* _description) 582 { 583 if (!sLock.IsLocked() || sLock.LockingThread() != find_thread(NULL)) { 584 // TODO: Shouldn't we simply drop into the debugger in this case? 585 return B_NOT_ALLOWED; 586 } 587 588 if (fIteratorIndex == 0) { 589 // This is the first call, so let's make sure we have current data to 590 // operate on. 591 status_t status = update_media_formats(); 592 if (status < B_OK) 593 return status; 594 } 595 596 meta_format* format = sFormats.ItemAt(fIteratorIndex++); 597 if (format == NULL) 598 return B_BAD_INDEX; 599 600 return B_OK; 601 } 602 603 604 bool 605 BMediaFormats::Lock() 606 { 607 return sLock.Lock(); 608 } 609 610 611 void 612 BMediaFormats::Unlock() 613 { 614 sLock.Unlock(); 615 } 616 617 618 status_t 619 BMediaFormats::MakeFormatFor(const media_format_description* descriptions, 620 int32 descriptionCount, media_format* format, uint32 flags, 621 void* _reserved) 622 { 623 status_t status = FormatManager::GetInstance()->MakeFormatFor(descriptions, 624 descriptionCount, *format, flags, _reserved); 625 626 return status; 627 } 628 629 630 // #pragma mark - deprecated API 631 632 633 status_t 634 BMediaFormats::MakeFormatFor(const media_format_description& description, 635 const media_format& inFormat, media_format* _outFormat) 636 { 637 *_outFormat = inFormat; 638 return MakeFormatFor(&description, 1, _outFormat); 639 } 640 641