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