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