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