stagefright.cpp revision df712ea86e6350f7005a02ab0e1c60c28a343ed0
1/* 2 * Copyright (C) 2009 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17//#define LOG_NDEBUG 0 18#define LOG_TAG "stagefright" 19#include <media/stagefright/foundation/ADebug.h> 20 21#include <sys/time.h> 22 23#include <stdlib.h> 24#include <string.h> 25#include <unistd.h> 26 27#include "SineSource.h" 28 29#include <binder/IServiceManager.h> 30#include <binder/ProcessState.h> 31#include <media/IMediaPlayerService.h> 32#include <media/stagefright/foundation/ALooper.h> 33#include "include/LiveSession.h" 34#include "include/NuCachedSource2.h" 35#include <media/stagefright/AudioPlayer.h> 36#include <media/stagefright/DataSource.h> 37#include <media/stagefright/JPEGSource.h> 38#include <media/stagefright/MediaDefs.h> 39#include <media/stagefright/MediaErrors.h> 40#include <media/stagefright/MediaExtractor.h> 41#include <media/stagefright/MediaSource.h> 42#include <media/stagefright/MetaData.h> 43#include <media/stagefright/OMXClient.h> 44#include <media/stagefright/OMXCodec.h> 45#include <media/mediametadataretriever.h> 46 47#include <media/stagefright/foundation/hexdump.h> 48#include <media/stagefright/MPEG2TSWriter.h> 49#include <media/stagefright/MPEG4Writer.h> 50 51#include <private/media/VideoFrame.h> 52#include <SkBitmap.h> 53#include <SkImageEncoder.h> 54 55#include <fcntl.h> 56 57#include <gui/SurfaceTextureClient.h> 58#include <gui/SurfaceComposerClient.h> 59 60using namespace android; 61 62static long gNumRepetitions; 63static long gMaxNumFrames; // 0 means decode all available. 64static long gReproduceBug; // if not -1. 65static bool gPreferSoftwareCodec; 66static bool gForceToUseHardwareCodec; 67static bool gPlaybackAudio; 68static bool gWriteMP4; 69static bool gDisplayHistogram; 70static String8 gWriteMP4Filename; 71 72static sp<ANativeWindow> gSurface; 73 74static int64_t getNowUs() { 75 struct timeval tv; 76 gettimeofday(&tv, NULL); 77 78 return (int64_t)tv.tv_usec + tv.tv_sec * 1000000ll; 79} 80 81static int CompareIncreasing(const int64_t *a, const int64_t *b) { 82 return (*a) < (*b) ? -1 : (*a) > (*b) ? 1 : 0; 83} 84 85static void displayDecodeHistogram(Vector<int64_t> *decodeTimesUs) { 86 printf("decode times:\n"); 87 88 decodeTimesUs->sort(CompareIncreasing); 89 90 size_t n = decodeTimesUs->size(); 91 int64_t minUs = decodeTimesUs->itemAt(0); 92 int64_t maxUs = decodeTimesUs->itemAt(n - 1); 93 94 printf("min decode time %lld us (%.2f secs)\n", minUs, minUs / 1E6); 95 printf("max decode time %lld us (%.2f secs)\n", maxUs, maxUs / 1E6); 96 97 size_t counts[100]; 98 for (size_t i = 0; i < 100; ++i) { 99 counts[i] = 0; 100 } 101 102 for (size_t i = 0; i < n; ++i) { 103 int64_t x = decodeTimesUs->itemAt(i); 104 105 size_t slot = ((x - minUs) * 100) / (maxUs - minUs); 106 if (slot == 100) { slot = 99; } 107 108 ++counts[slot]; 109 } 110 111 for (size_t i = 0; i < 100; ++i) { 112 int64_t slotUs = minUs + (i * (maxUs - minUs) / 100); 113 114 double fps = 1E6 / slotUs; 115 printf("[%.2f fps]: %d\n", fps, counts[i]); 116 } 117} 118 119static void displayAVCProfileLevelIfPossible(const sp<MetaData>& meta) { 120 uint32_t type; 121 const void *data; 122 size_t size; 123 if (meta->findData(kKeyAVCC, &type, &data, &size)) { 124 const uint8_t *ptr = (const uint8_t *)data; 125 CHECK(size >= 7); 126 CHECK(ptr[0] == 1); // configurationVersion == 1 127 uint8_t profile = ptr[1]; 128 uint8_t level = ptr[3]; 129 fprintf(stderr, "AVC video profile %d and level %d\n", profile, level); 130 } 131} 132 133static void dumpSource(const sp<MediaSource> &source, const String8 &filename) { 134 FILE *out = fopen(filename.string(), "wb"); 135 136 CHECK_EQ((status_t)OK, source->start()); 137 138 status_t err; 139 for (;;) { 140 MediaBuffer *mbuf; 141 err = source->read(&mbuf); 142 143 if (err == INFO_FORMAT_CHANGED) { 144 continue; 145 } else if (err != OK) { 146 break; 147 } 148 149 CHECK_EQ( 150 fwrite((const uint8_t *)mbuf->data() + mbuf->range_offset(), 151 1, 152 mbuf->range_length(), 153 out), 154 (ssize_t)mbuf->range_length()); 155 156 mbuf->release(); 157 mbuf = NULL; 158 } 159 160 CHECK_EQ((status_t)OK, source->stop()); 161 162 fclose(out); 163 out = NULL; 164} 165 166static void playSource(OMXClient *client, sp<MediaSource> &source) { 167 sp<MetaData> meta = source->getFormat(); 168 169 const char *mime; 170 CHECK(meta->findCString(kKeyMIMEType, &mime)); 171 172 sp<MediaSource> rawSource; 173 if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_RAW, mime)) { 174 rawSource = source; 175 } else { 176 int flags = 0; 177 if (gPreferSoftwareCodec) { 178 flags |= OMXCodec::kPreferSoftwareCodecs; 179 } 180 if (gForceToUseHardwareCodec) { 181 CHECK(!gPreferSoftwareCodec); 182 flags |= OMXCodec::kHardwareCodecsOnly; 183 } 184 rawSource = OMXCodec::Create( 185 client->interface(), meta, false /* createEncoder */, source, 186 NULL /* matchComponentName */, 187 flags, 188 gSurface); 189 190 if (rawSource == NULL) { 191 fprintf(stderr, "Failed to instantiate decoder for '%s'.\n", mime); 192 return; 193 } 194 displayAVCProfileLevelIfPossible(meta); 195 } 196 197 source.clear(); 198 199 status_t err = rawSource->start(); 200 201 if (err != OK) { 202 fprintf(stderr, "rawSource returned error %d (0x%08x)\n", err, err); 203 return; 204 } 205 206 if (gPlaybackAudio) { 207 AudioPlayer *player = new AudioPlayer(NULL); 208 player->setSource(rawSource); 209 rawSource.clear(); 210 211 player->start(true /* sourceAlreadyStarted */); 212 213 status_t finalStatus; 214 while (!player->reachedEOS(&finalStatus)) { 215 usleep(100000ll); 216 } 217 218 delete player; 219 player = NULL; 220 221 return; 222 } else if (gReproduceBug >= 3 && gReproduceBug <= 5) { 223 int64_t durationUs; 224 CHECK(meta->findInt64(kKeyDuration, &durationUs)); 225 226 status_t err; 227 MediaBuffer *buffer; 228 MediaSource::ReadOptions options; 229 int64_t seekTimeUs = -1; 230 for (;;) { 231 err = rawSource->read(&buffer, &options); 232 options.clearSeekTo(); 233 234 bool shouldSeek = false; 235 if (err == INFO_FORMAT_CHANGED) { 236 CHECK(buffer == NULL); 237 238 printf("format changed.\n"); 239 continue; 240 } else if (err != OK) { 241 printf("reached EOF.\n"); 242 243 shouldSeek = true; 244 } else { 245 int64_t timestampUs; 246 CHECK(buffer->meta_data()->findInt64(kKeyTime, ×tampUs)); 247 248 bool failed = false; 249 250 if (seekTimeUs >= 0) { 251 int64_t diff = timestampUs - seekTimeUs; 252 253 if (diff < 0) { 254 diff = -diff; 255 } 256 257 if ((gReproduceBug == 4 && diff > 500000) 258 || (gReproduceBug == 5 && timestampUs < 0)) { 259 printf("wanted: %.2f secs, got: %.2f secs\n", 260 seekTimeUs / 1E6, timestampUs / 1E6); 261 262 printf("ERROR: "); 263 failed = true; 264 } 265 } 266 267 printf("buffer has timestamp %lld us (%.2f secs)\n", 268 timestampUs, timestampUs / 1E6); 269 270 buffer->release(); 271 buffer = NULL; 272 273 if (failed) { 274 break; 275 } 276 277 shouldSeek = ((double)rand() / RAND_MAX) < 0.1; 278 279 if (gReproduceBug == 3) { 280 shouldSeek = false; 281 } 282 } 283 284 seekTimeUs = -1; 285 286 if (shouldSeek) { 287 seekTimeUs = (rand() * (float)durationUs) / RAND_MAX; 288 options.setSeekTo(seekTimeUs); 289 290 printf("seeking to %lld us (%.2f secs)\n", 291 seekTimeUs, seekTimeUs / 1E6); 292 } 293 } 294 295 rawSource->stop(); 296 297 return; 298 } 299 300 int n = 0; 301 int64_t startTime = getNowUs(); 302 303 long numIterationsLeft = gNumRepetitions; 304 MediaSource::ReadOptions options; 305 306 int64_t sumDecodeUs = 0; 307 int64_t totalBytes = 0; 308 309 Vector<int64_t> decodeTimesUs; 310 311 while (numIterationsLeft-- > 0) { 312 long numFrames = 0; 313 314 MediaBuffer *buffer; 315 316 for (;;) { 317 int64_t startDecodeUs = getNowUs(); 318 status_t err = rawSource->read(&buffer, &options); 319 int64_t delayDecodeUs = getNowUs() - startDecodeUs; 320 321 options.clearSeekTo(); 322 323 if (err != OK) { 324 CHECK(buffer == NULL); 325 326 if (err == INFO_FORMAT_CHANGED) { 327 printf("format changed.\n"); 328 continue; 329 } 330 331 break; 332 } 333 334 if (buffer->range_length() > 0) { 335 if (gDisplayHistogram && n > 0) { 336 // Ignore the first time since it includes some setup 337 // cost. 338 decodeTimesUs.push(delayDecodeUs); 339 } 340 341 if ((n++ % 16) == 0) { 342 printf("."); 343 fflush(stdout); 344 } 345 } 346 347 sumDecodeUs += delayDecodeUs; 348 totalBytes += buffer->range_length(); 349 350 buffer->release(); 351 buffer = NULL; 352 353 ++numFrames; 354 if (gMaxNumFrames > 0 && numFrames == gMaxNumFrames) { 355 break; 356 } 357 358 if (gReproduceBug == 1 && numFrames == 40) { 359 printf("seeking past the end now."); 360 options.setSeekTo(0x7fffffffL); 361 } else if (gReproduceBug == 2 && numFrames == 40) { 362 printf("seeking to 5 secs."); 363 options.setSeekTo(5000000); 364 } 365 } 366 367 printf("$"); 368 fflush(stdout); 369 370 options.setSeekTo(0); 371 } 372 373 rawSource->stop(); 374 printf("\n"); 375 376 int64_t delay = getNowUs() - startTime; 377 if (!strncasecmp("video/", mime, 6)) { 378 printf("avg. %.2f fps\n", n * 1E6 / delay); 379 380 printf("avg. time to decode one buffer %.2f usecs\n", 381 (double)sumDecodeUs / n); 382 383 printf("decoded a total of %d frame(s).\n", n); 384 385 if (gDisplayHistogram) { 386 displayDecodeHistogram(&decodeTimesUs); 387 } 388 } else if (!strncasecmp("audio/", mime, 6)) { 389 // Frame count makes less sense for audio, as the output buffer 390 // sizes may be different across decoders. 391 printf("avg. %.2f KB/sec\n", totalBytes / 1024 * 1E6 / delay); 392 393 printf("decoded a total of %lld bytes\n", totalBytes); 394 } 395} 396 397//////////////////////////////////////////////////////////////////////////////// 398 399struct DetectSyncSource : public MediaSource { 400 DetectSyncSource(const sp<MediaSource> &source); 401 402 virtual status_t start(MetaData *params = NULL); 403 virtual status_t stop(); 404 virtual sp<MetaData> getFormat(); 405 406 virtual status_t read( 407 MediaBuffer **buffer, const ReadOptions *options); 408 409private: 410 enum StreamType { 411 AVC, 412 MPEG4, 413 H263, 414 OTHER, 415 }; 416 417 sp<MediaSource> mSource; 418 StreamType mStreamType; 419 bool mSawFirstIDRFrame; 420 421 DISALLOW_EVIL_CONSTRUCTORS(DetectSyncSource); 422}; 423 424DetectSyncSource::DetectSyncSource(const sp<MediaSource> &source) 425 : mSource(source), 426 mStreamType(OTHER), 427 mSawFirstIDRFrame(false) { 428 const char *mime; 429 CHECK(mSource->getFormat()->findCString(kKeyMIMEType, &mime)); 430 431 if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AVC)) { 432 mStreamType = AVC; 433 } else if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_MPEG4)) { 434 mStreamType = MPEG4; 435 CHECK(!"sync frame detection not implemented yet for MPEG4"); 436 } else if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_H263)) { 437 mStreamType = H263; 438 CHECK(!"sync frame detection not implemented yet for H.263"); 439 } 440} 441 442status_t DetectSyncSource::start(MetaData *params) { 443 mSawFirstIDRFrame = false; 444 445 return mSource->start(params); 446} 447 448status_t DetectSyncSource::stop() { 449 return mSource->stop(); 450} 451 452sp<MetaData> DetectSyncSource::getFormat() { 453 return mSource->getFormat(); 454} 455 456static bool isIDRFrame(MediaBuffer *buffer) { 457 const uint8_t *data = 458 (const uint8_t *)buffer->data() + buffer->range_offset(); 459 size_t size = buffer->range_length(); 460 for (size_t i = 0; i + 3 < size; ++i) { 461 if (!memcmp("\x00\x00\x01", &data[i], 3)) { 462 uint8_t nalType = data[i + 3] & 0x1f; 463 if (nalType == 5) { 464 return true; 465 } 466 } 467 } 468 469 return false; 470} 471 472status_t DetectSyncSource::read( 473 MediaBuffer **buffer, const ReadOptions *options) { 474 for (;;) { 475 status_t err = mSource->read(buffer, options); 476 477 if (err != OK) { 478 return err; 479 } 480 481 if (mStreamType == AVC) { 482 bool isIDR = isIDRFrame(*buffer); 483 (*buffer)->meta_data()->setInt32(kKeyIsSyncFrame, isIDR); 484 if (isIDR) { 485 mSawFirstIDRFrame = true; 486 } 487 } else { 488 (*buffer)->meta_data()->setInt32(kKeyIsSyncFrame, true); 489 } 490 491 if (mStreamType != AVC || mSawFirstIDRFrame) { 492 break; 493 } 494 495 // Ignore everything up to the first IDR frame. 496 (*buffer)->release(); 497 *buffer = NULL; 498 } 499 500 return OK; 501} 502 503//////////////////////////////////////////////////////////////////////////////// 504 505static void writeSourcesToMP4( 506 Vector<sp<MediaSource> > &sources, bool syncInfoPresent) { 507#if 0 508 sp<MPEG4Writer> writer = 509 new MPEG4Writer(gWriteMP4Filename.string()); 510#else 511 sp<MPEG2TSWriter> writer = 512 new MPEG2TSWriter(gWriteMP4Filename.string()); 513#endif 514 515 // at most one minute. 516 writer->setMaxFileDuration(60000000ll); 517 518 for (size_t i = 0; i < sources.size(); ++i) { 519 sp<MediaSource> source = sources.editItemAt(i); 520 521 CHECK_EQ(writer->addSource( 522 syncInfoPresent ? source : new DetectSyncSource(source)), 523 (status_t)OK); 524 } 525 526 sp<MetaData> params = new MetaData; 527 params->setInt32(kKeyNotRealTime, true); 528 CHECK_EQ(writer->start(params.get()), (status_t)OK); 529 530 while (!writer->reachedEOS()) { 531 usleep(100000); 532 } 533 writer->stop(); 534} 535 536static void performSeekTest(const sp<MediaSource> &source) { 537 CHECK_EQ((status_t)OK, source->start()); 538 539 int64_t durationUs; 540 CHECK(source->getFormat()->findInt64(kKeyDuration, &durationUs)); 541 542 for (int64_t seekTimeUs = 0; seekTimeUs <= durationUs; 543 seekTimeUs += 60000ll) { 544 MediaSource::ReadOptions options; 545 options.setSeekTo( 546 seekTimeUs, MediaSource::ReadOptions::SEEK_PREVIOUS_SYNC); 547 548 MediaBuffer *buffer; 549 status_t err; 550 for (;;) { 551 err = source->read(&buffer, &options); 552 553 options.clearSeekTo(); 554 555 if (err == INFO_FORMAT_CHANGED) { 556 CHECK(buffer == NULL); 557 continue; 558 } 559 560 if (err != OK) { 561 CHECK(buffer == NULL); 562 break; 563 } 564 565 if (buffer->range_length() > 0) { 566 break; 567 } 568 569 CHECK(buffer != NULL); 570 571 buffer->release(); 572 buffer = NULL; 573 } 574 575 if (err == OK) { 576 int64_t timeUs; 577 CHECK(buffer->meta_data()->findInt64(kKeyTime, &timeUs)); 578 579 printf("%lld\t%lld\t%lld\n", seekTimeUs, timeUs, seekTimeUs - timeUs); 580 581 buffer->release(); 582 buffer = NULL; 583 } else { 584 printf("ERROR\n"); 585 break; 586 } 587 } 588 589 CHECK_EQ((status_t)OK, source->stop()); 590} 591 592static void usage(const char *me) { 593 fprintf(stderr, "usage: %s\n", me); 594 fprintf(stderr, " -h(elp)\n"); 595 fprintf(stderr, " -a(udio)\n"); 596 fprintf(stderr, " -n repetitions\n"); 597 fprintf(stderr, " -l(ist) components\n"); 598 fprintf(stderr, " -m max-number-of-frames-to-decode in each pass\n"); 599 fprintf(stderr, " -b bug to reproduce\n"); 600 fprintf(stderr, " -p(rofiles) dump decoder profiles supported\n"); 601 fprintf(stderr, " -t(humbnail) extract video thumbnail or album art\n"); 602 fprintf(stderr, " -s(oftware) prefer software codec\n"); 603 fprintf(stderr, " -r(hardware) force to use hardware codec\n"); 604 fprintf(stderr, " -o playback audio\n"); 605 fprintf(stderr, " -w(rite) filename (write to .mp4 file)\n"); 606 fprintf(stderr, " -k seek test\n"); 607 fprintf(stderr, " -x display a histogram of decoding times/fps " 608 "(video only)\n"); 609 fprintf(stderr, " -S allocate buffers from a surface\n"); 610 fprintf(stderr, " -T allocate buffers from a surface texture\n"); 611 fprintf(stderr, " -d(ump) filename (raw stream data to a file)\n"); 612} 613 614int main(int argc, char **argv) { 615 android::ProcessState::self()->startThreadPool(); 616 617 bool audioOnly = false; 618 bool listComponents = false; 619 bool dumpProfiles = false; 620 bool extractThumbnail = false; 621 bool seekTest = false; 622 bool useSurfaceAlloc = false; 623 bool useSurfaceTexAlloc = false; 624 bool dumpStream = false; 625 String8 dumpStreamFilename; 626 gNumRepetitions = 1; 627 gMaxNumFrames = 0; 628 gReproduceBug = -1; 629 gPreferSoftwareCodec = false; 630 gForceToUseHardwareCodec = false; 631 gPlaybackAudio = false; 632 gWriteMP4 = false; 633 gDisplayHistogram = false; 634 635 sp<ALooper> looper; 636 sp<LiveSession> liveSession; 637 638 int res; 639 while ((res = getopt(argc, argv, "han:lm:b:ptsrow:kxSTd:")) >= 0) { 640 switch (res) { 641 case 'a': 642 { 643 audioOnly = true; 644 break; 645 } 646 647 case 'd': 648 { 649 dumpStream = true; 650 dumpStreamFilename.setTo(optarg); 651 break; 652 } 653 654 case 'l': 655 { 656 listComponents = true; 657 break; 658 } 659 660 case 'm': 661 case 'n': 662 case 'b': 663 { 664 char *end; 665 long x = strtol(optarg, &end, 10); 666 667 if (*end != '\0' || end == optarg || x <= 0) { 668 x = 1; 669 } 670 671 if (res == 'n') { 672 gNumRepetitions = x; 673 } else if (res == 'm') { 674 gMaxNumFrames = x; 675 } else { 676 CHECK_EQ(res, 'b'); 677 gReproduceBug = x; 678 } 679 break; 680 } 681 682 case 'w': 683 { 684 gWriteMP4 = true; 685 gWriteMP4Filename.setTo(optarg); 686 break; 687 } 688 689 case 'p': 690 { 691 dumpProfiles = true; 692 break; 693 } 694 695 case 't': 696 { 697 extractThumbnail = true; 698 break; 699 } 700 701 case 's': 702 { 703 gPreferSoftwareCodec = true; 704 break; 705 } 706 707 case 'r': 708 { 709 gForceToUseHardwareCodec = true; 710 break; 711 } 712 713 case 'o': 714 { 715 gPlaybackAudio = true; 716 break; 717 } 718 719 case 'k': 720 { 721 seekTest = true; 722 break; 723 } 724 725 case 'x': 726 { 727 gDisplayHistogram = true; 728 break; 729 } 730 731 case 'S': 732 { 733 useSurfaceAlloc = true; 734 break; 735 } 736 737 case 'T': 738 { 739 useSurfaceTexAlloc = true; 740 break; 741 } 742 743 case '?': 744 case 'h': 745 default: 746 { 747 usage(argv[0]); 748 exit(1); 749 break; 750 } 751 } 752 } 753 754 if (gPlaybackAudio && !audioOnly) { 755 // This doesn't make any sense if we're decoding the video track. 756 gPlaybackAudio = false; 757 } 758 759 argc -= optind; 760 argv += optind; 761 762 if (extractThumbnail) { 763 sp<IServiceManager> sm = defaultServiceManager(); 764 sp<IBinder> binder = sm->getService(String16("media.player")); 765 sp<IMediaPlayerService> service = 766 interface_cast<IMediaPlayerService>(binder); 767 768 CHECK(service.get() != NULL); 769 770 sp<IMediaMetadataRetriever> retriever = 771 service->createMetadataRetriever(getpid()); 772 773 CHECK(retriever != NULL); 774 775 for (int k = 0; k < argc; ++k) { 776 const char *filename = argv[k]; 777 778 bool failed = true; 779 CHECK_EQ(retriever->setDataSource(filename), (status_t)OK); 780 sp<IMemory> mem = 781 retriever->getFrameAtTime(-1, 782 MediaSource::ReadOptions::SEEK_PREVIOUS_SYNC); 783 784 if (mem != NULL) { 785 failed = false; 786 printf("getFrameAtTime(%s) => OK\n", filename); 787 788 VideoFrame *frame = (VideoFrame *)mem->pointer(); 789 790 SkBitmap bitmap; 791 bitmap.setConfig( 792 SkBitmap::kRGB_565_Config, frame->mWidth, frame->mHeight); 793 794 bitmap.setPixels((uint8_t *)frame + sizeof(VideoFrame)); 795 796 CHECK(SkImageEncoder::EncodeFile( 797 "/sdcard/out.jpg", bitmap, 798 SkImageEncoder::kJPEG_Type, 799 SkImageEncoder::kDefaultQuality)); 800 } 801 802 { 803 mem = retriever->extractAlbumArt(); 804 805 if (mem != NULL) { 806 failed = false; 807 printf("extractAlbumArt(%s) => OK\n", filename); 808 } 809 } 810 811 if (failed) { 812 printf("both getFrameAtTime and extractAlbumArt " 813 "failed on file '%s'.\n", filename); 814 } 815 } 816 817 return 0; 818 } 819 820 if (dumpProfiles) { 821 sp<IServiceManager> sm = defaultServiceManager(); 822 sp<IBinder> binder = sm->getService(String16("media.player")); 823 sp<IMediaPlayerService> service = 824 interface_cast<IMediaPlayerService>(binder); 825 826 CHECK(service.get() != NULL); 827 828 sp<IOMX> omx = service->getOMX(); 829 CHECK(omx.get() != NULL); 830 831 const char *kMimeTypes[] = { 832 MEDIA_MIMETYPE_VIDEO_AVC, MEDIA_MIMETYPE_VIDEO_MPEG4, 833 MEDIA_MIMETYPE_VIDEO_H263, MEDIA_MIMETYPE_AUDIO_AAC, 834 MEDIA_MIMETYPE_AUDIO_AMR_NB, MEDIA_MIMETYPE_AUDIO_AMR_WB, 835 MEDIA_MIMETYPE_AUDIO_MPEG, MEDIA_MIMETYPE_AUDIO_G711_MLAW, 836 MEDIA_MIMETYPE_AUDIO_G711_ALAW, MEDIA_MIMETYPE_AUDIO_VORBIS, 837 MEDIA_MIMETYPE_VIDEO_VPX 838 }; 839 840 for (size_t k = 0; k < sizeof(kMimeTypes) / sizeof(kMimeTypes[0]); 841 ++k) { 842 printf("type '%s':\n", kMimeTypes[k]); 843 844 Vector<CodecCapabilities> results; 845 // will retrieve hardware and software codecs 846 CHECK_EQ(QueryCodecs(omx, kMimeTypes[k], 847 true, // queryDecoders 848 &results), (status_t)OK); 849 850 for (size_t i = 0; i < results.size(); ++i) { 851 printf(" decoder '%s' supports ", 852 results[i].mComponentName.string()); 853 854 if (results[i].mProfileLevels.size() == 0) { 855 printf("NOTHING.\n"); 856 continue; 857 } 858 859 for (size_t j = 0; j < results[i].mProfileLevels.size(); ++j) { 860 const CodecProfileLevel &profileLevel = 861 results[i].mProfileLevels[j]; 862 863 printf("%s%ld/%ld", j > 0 ? ", " : "", 864 profileLevel.mProfile, profileLevel.mLevel); 865 } 866 867 printf("\n"); 868 } 869 } 870 } 871 872 if (listComponents) { 873 sp<IServiceManager> sm = defaultServiceManager(); 874 sp<IBinder> binder = sm->getService(String16("media.player")); 875 sp<IMediaPlayerService> service = interface_cast<IMediaPlayerService>(binder); 876 877 CHECK(service.get() != NULL); 878 879 sp<IOMX> omx = service->getOMX(); 880 CHECK(omx.get() != NULL); 881 882 List<IOMX::ComponentInfo> list; 883 omx->listNodes(&list); 884 885 for (List<IOMX::ComponentInfo>::iterator it = list.begin(); 886 it != list.end(); ++it) { 887 printf("%s\t Roles: ", (*it).mName.string()); 888 for (List<String8>::iterator itRoles = (*it).mRoles.begin() ; 889 itRoles != (*it).mRoles.end() ; ++itRoles) { 890 printf("%s\t", (*itRoles).string()); 891 } 892 printf("\n"); 893 } 894 } 895 896 sp<SurfaceComposerClient> composerClient; 897 sp<SurfaceControl> control; 898 899 if ((useSurfaceAlloc || useSurfaceTexAlloc) && !audioOnly) { 900 if (useSurfaceAlloc) { 901 composerClient = new SurfaceComposerClient; 902 CHECK_EQ(composerClient->initCheck(), (status_t)OK); 903 904 control = composerClient->createSurface( 905 String8("A Surface"), 906 0, 907 1280, 908 800, 909 PIXEL_FORMAT_RGB_565, 910 0); 911 912 CHECK(control != NULL); 913 CHECK(control->isValid()); 914 915 SurfaceComposerClient::openGlobalTransaction(); 916 CHECK_EQ(control->setLayer(INT_MAX), (status_t)OK); 917 CHECK_EQ(control->show(), (status_t)OK); 918 SurfaceComposerClient::closeGlobalTransaction(); 919 920 gSurface = control->getSurface(); 921 CHECK(gSurface != NULL); 922 } else { 923 CHECK(useSurfaceTexAlloc); 924 925 sp<SurfaceTexture> texture = new SurfaceTexture(0 /* tex */); 926 gSurface = new SurfaceTextureClient(texture); 927 } 928 929 CHECK_EQ((status_t)OK, 930 native_window_api_connect( 931 gSurface.get(), NATIVE_WINDOW_API_MEDIA)); 932 } 933 934 DataSource::RegisterDefaultSniffers(); 935 936 OMXClient client; 937 status_t err = client.connect(); 938 939 for (int k = 0; k < argc; ++k) { 940 bool syncInfoPresent = true; 941 942 const char *filename = argv[k]; 943 944 sp<DataSource> dataSource = DataSource::CreateFromURI(filename); 945 946 if (strncasecmp(filename, "sine:", 5) 947 && strncasecmp(filename, "httplive://", 11) 948 && dataSource == NULL) { 949 fprintf(stderr, "Unable to create data source.\n"); 950 return 1; 951 } 952 953 bool isJPEG = false; 954 955 size_t len = strlen(filename); 956 if (len >= 4 && !strcasecmp(filename + len - 4, ".jpg")) { 957 isJPEG = true; 958 } 959 960 Vector<sp<MediaSource> > mediaSources; 961 sp<MediaSource> mediaSource; 962 963 if (isJPEG) { 964 mediaSource = new JPEGSource(dataSource); 965 if (gWriteMP4) { 966 mediaSources.push(mediaSource); 967 } 968 } else if (!strncasecmp("sine:", filename, 5)) { 969 char *end; 970 long sampleRate = strtol(filename + 5, &end, 10); 971 972 if (end == filename + 5) { 973 sampleRate = 44100; 974 } 975 mediaSource = new SineSource(sampleRate, 1); 976 if (gWriteMP4) { 977 mediaSources.push(mediaSource); 978 } 979 } else { 980 sp<MediaExtractor> extractor; 981 982 if (!strncasecmp("httplive://", filename, 11)) { 983 String8 uri("http://"); 984 uri.append(filename + 11); 985 986 if (looper == NULL) { 987 looper = new ALooper; 988 looper->start(); 989 } 990 liveSession = new LiveSession; 991 looper->registerHandler(liveSession); 992 993 liveSession->connect(uri.string()); 994 dataSource = liveSession->getDataSource(); 995 996 extractor = 997 MediaExtractor::Create( 998 dataSource, MEDIA_MIMETYPE_CONTAINER_MPEG2TS); 999 1000 syncInfoPresent = false; 1001 } else { 1002 extractor = MediaExtractor::Create(dataSource); 1003 1004 if (extractor == NULL) { 1005 fprintf(stderr, "could not create extractor.\n"); 1006 return -1; 1007 } 1008 1009 sp<MetaData> meta = extractor->getMetaData(); 1010 1011 if (meta != NULL) { 1012 const char *mime; 1013 CHECK(meta->findCString(kKeyMIMEType, &mime)); 1014 1015 if (!strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_MPEG2TS)) { 1016 syncInfoPresent = false; 1017 } 1018 } 1019 } 1020 1021 size_t numTracks = extractor->countTracks(); 1022 1023 if (gWriteMP4) { 1024 bool haveAudio = false; 1025 bool haveVideo = false; 1026 for (size_t i = 0; i < numTracks; ++i) { 1027 sp<MediaSource> source = extractor->getTrack(i); 1028 1029 const char *mime; 1030 CHECK(source->getFormat()->findCString( 1031 kKeyMIMEType, &mime)); 1032 1033 bool useTrack = false; 1034 if (!haveAudio && !strncasecmp("audio/", mime, 6)) { 1035 haveAudio = true; 1036 useTrack = true; 1037 } else if (!haveVideo && !strncasecmp("video/", mime, 6)) { 1038 haveVideo = true; 1039 useTrack = true; 1040 } 1041 1042 if (useTrack) { 1043 mediaSources.push(source); 1044 1045 if (haveAudio && haveVideo) { 1046 break; 1047 } 1048 } 1049 } 1050 } else { 1051 sp<MetaData> meta; 1052 size_t i; 1053 for (i = 0; i < numTracks; ++i) { 1054 meta = extractor->getTrackMetaData( 1055 i, MediaExtractor::kIncludeExtensiveMetaData); 1056 1057 const char *mime; 1058 meta->findCString(kKeyMIMEType, &mime); 1059 1060 if (audioOnly && !strncasecmp(mime, "audio/", 6)) { 1061 break; 1062 } 1063 1064 if (!audioOnly && !strncasecmp(mime, "video/", 6)) { 1065 break; 1066 } 1067 1068 meta = NULL; 1069 } 1070 1071 if (meta == NULL) { 1072 fprintf(stderr, 1073 "No suitable %s track found. The '-a' option will " 1074 "target audio tracks only, the default is to target " 1075 "video tracks only.\n", 1076 audioOnly ? "audio" : "video"); 1077 return -1; 1078 } 1079 1080 int64_t thumbTimeUs; 1081 if (meta->findInt64(kKeyThumbnailTime, &thumbTimeUs)) { 1082 printf("thumbnailTime: %lld us (%.2f secs)\n", 1083 thumbTimeUs, thumbTimeUs / 1E6); 1084 } 1085 1086 mediaSource = extractor->getTrack(i); 1087 } 1088 } 1089 1090 if (gWriteMP4) { 1091 writeSourcesToMP4(mediaSources, syncInfoPresent); 1092 } else if (dumpStream) { 1093 dumpSource(mediaSource, dumpStreamFilename); 1094 } else if (seekTest) { 1095 performSeekTest(mediaSource); 1096 } else { 1097 playSource(&client, mediaSource); 1098 } 1099 } 1100 1101 if ((useSurfaceAlloc || useSurfaceTexAlloc) && !audioOnly) { 1102 CHECK_EQ((status_t)OK, 1103 native_window_api_disconnect( 1104 gSurface.get(), NATIVE_WINDOW_API_MEDIA)); 1105 1106 gSurface.clear(); 1107 1108 if (useSurfaceAlloc) { 1109 composerClient->dispose(); 1110 } 1111 } 1112 1113 client.disconnect(); 1114 1115 return 0; 1116} 1117