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