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