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