stagefright.cpp revision 92d57f926eb39d72e8fd8f3b3c9e40d25f77ad47
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 67using namespace android; 68 69static long gNumRepetitions; 70static long gMaxNumFrames; // 0 means decode all available. 71static long gReproduceBug; // if not -1. 72static bool gPreferSoftwareCodec; 73static bool gForceToUseHardwareCodec; 74static bool gPlaybackAudio; 75static bool gWriteMP4; 76static bool gDisplayHistogram; 77static bool showProgress = true; 78static String8 gWriteMP4Filename; 79 80static sp<ANativeWindow> gSurface; 81 82static int64_t getNowUs() { 83 struct timeval tv; 84 gettimeofday(&tv, NULL); 85 86 return (int64_t)tv.tv_usec + tv.tv_sec * 1000000ll; 87} 88 89static int CompareIncreasing(const int64_t *a, const int64_t *b) { 90 return (*a) < (*b) ? -1 : (*a) > (*b) ? 1 : 0; 91} 92 93static void displayDecodeHistogram(Vector<int64_t> *decodeTimesUs) { 94 printf("decode times:\n"); 95 96 decodeTimesUs->sort(CompareIncreasing); 97 98 size_t n = decodeTimesUs->size(); 99 int64_t minUs = decodeTimesUs->itemAt(0); 100 int64_t maxUs = decodeTimesUs->itemAt(n - 1); 101 102 printf("min decode time %" PRId64 " us (%.2f secs)\n", minUs, minUs / 1E6); 103 printf("max decode time %" PRId64 " us (%.2f secs)\n", maxUs, maxUs / 1E6); 104 105 size_t counts[100]; 106 for (size_t i = 0; i < 100; ++i) { 107 counts[i] = 0; 108 } 109 110 for (size_t i = 0; i < n; ++i) { 111 int64_t x = decodeTimesUs->itemAt(i); 112 113 size_t slot = ((x - minUs) * 100) / (maxUs - minUs); 114 if (slot == 100) { slot = 99; } 115 116 ++counts[slot]; 117 } 118 119 for (size_t i = 0; i < 100; ++i) { 120 int64_t slotUs = minUs + (i * (maxUs - minUs) / 100); 121 122 double fps = 1E6 / slotUs; 123 printf("[%.2f fps]: %zu\n", fps, counts[i]); 124 } 125} 126 127static void displayAVCProfileLevelIfPossible(const sp<MetaData>& meta) { 128 uint32_t type; 129 const void *data; 130 size_t size; 131 if (meta->findData(kKeyAVCC, &type, &data, &size)) { 132 const uint8_t *ptr = (const uint8_t *)data; 133 CHECK(size >= 7); 134 CHECK(ptr[0] == 1); // configurationVersion == 1 135 uint8_t profile = ptr[1]; 136 uint8_t level = ptr[3]; 137 fprintf(stderr, "AVC video profile %d and level %d\n", profile, level); 138 } 139} 140 141static void dumpSource(const sp<IMediaSource> &source, const String8 &filename) { 142 FILE *out = fopen(filename.string(), "wb"); 143 144 CHECK_EQ((status_t)OK, source->start()); 145 146 status_t err; 147 for (;;) { 148 MediaBuffer *mbuf; 149 err = source->read(&mbuf); 150 151 if (err == INFO_FORMAT_CHANGED) { 152 continue; 153 } else if (err != OK) { 154 break; 155 } 156 157 CHECK_EQ( 158 fwrite((const uint8_t *)mbuf->data() + mbuf->range_offset(), 159 1, 160 mbuf->range_length(), 161 out), 162 (ssize_t)mbuf->range_length()); 163 164 mbuf->release(); 165 mbuf = NULL; 166 } 167 168 CHECK_EQ((status_t)OK, source->stop()); 169 170 fclose(out); 171 out = NULL; 172} 173 174static void playSource(sp<IMediaSource> &source) { 175 sp<MetaData> meta = source->getFormat(); 176 177 const char *mime; 178 CHECK(meta->findCString(kKeyMIMEType, &mime)); 179 180 sp<IMediaSource> rawSource; 181 if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_RAW, mime)) { 182 rawSource = source; 183 } else { 184 int flags = 0; 185 if (gPreferSoftwareCodec) { 186 flags |= MediaCodecList::kPreferSoftwareCodecs; 187 } 188 if (gForceToUseHardwareCodec) { 189 CHECK(!gPreferSoftwareCodec); 190 flags |= MediaCodecList::kHardwareCodecsOnly; 191 } 192 rawSource = SimpleDecodingSource::Create(source, flags, gSurface); 193 if (rawSource == NULL) { 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 explicit 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(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 sp<IMediaCodecList> list = MediaCodecList::getInstance(); 641 size_t numCodecs = list->countCodecs(); 642 643 for (size_t k = 0; k < sizeof(kMimeTypes) / sizeof(kMimeTypes[0]); ++k) { 644 printf("type '%s':\n", kMimeTypes[k]); 645 646 for (size_t index = 0; index < numCodecs; ++index) { 647 sp<MediaCodecInfo> info = list->getCodecInfo(index); 648 if (info == NULL || info->isEncoder() != !queryDecoders) { 649 continue; 650 } 651 sp<MediaCodecInfo::Capabilities> caps = info->getCapabilitiesFor(kMimeTypes[k]); 652 if (caps == NULL) { 653 continue; 654 } 655 printf(" %s '%s' supports ", 656 codecType, info->getCodecName()); 657 658 Vector<MediaCodecInfo::ProfileLevel> profileLevels; 659 caps->getSupportedProfileLevels(&profileLevels); 660 if (profileLevels.size() == 0) { 661 printf("NOTHING.\n"); 662 continue; 663 } 664 665 for (size_t j = 0; j < profileLevels.size(); ++j) { 666 const MediaCodecInfo::ProfileLevel &profileLevel = profileLevels[j]; 667 668 printf("%s%u/%u", j > 0 ? ", " : "", 669 profileLevel.mProfile, profileLevel.mLevel); 670 } 671 672 printf("\n"); 673 } 674 } 675} 676 677int main(int argc, char **argv) { 678 android::ProcessState::self()->startThreadPool(); 679 680 bool audioOnly = false; 681 bool listComponents = false; 682 bool dumpProfiles = false; 683 bool extractThumbnail = false; 684 bool seekTest = false; 685 bool useSurfaceAlloc = false; 686 bool useSurfaceTexAlloc = false; 687 bool dumpStream = false; 688 bool dumpPCMStream = false; 689 String8 dumpStreamFilename; 690 gNumRepetitions = 1; 691 gMaxNumFrames = 0; 692 gReproduceBug = -1; 693 gPreferSoftwareCodec = false; 694 gForceToUseHardwareCodec = false; 695 gPlaybackAudio = false; 696 gWriteMP4 = false; 697 gDisplayHistogram = false; 698 699 sp<ALooper> looper; 700 701 int res; 702 while ((res = getopt(argc, argv, "haqn:lm:b:ptsrow:kxSTd:D:")) >= 0) { 703 switch (res) { 704 case 'a': 705 { 706 audioOnly = true; 707 break; 708 } 709 710 case 'q': 711 { 712 showProgress = false; 713 break; 714 } 715 716 case 'd': 717 { 718 dumpStream = true; 719 dumpStreamFilename.setTo(optarg); 720 break; 721 } 722 723 case 'D': 724 { 725 dumpPCMStream = true; 726 audioOnly = true; 727 dumpStreamFilename.setTo(optarg); 728 break; 729 } 730 731 case 'l': 732 { 733 listComponents = true; 734 break; 735 } 736 737 case 'm': 738 case 'n': 739 case 'b': 740 { 741 char *end; 742 long x = strtol(optarg, &end, 10); 743 744 if (*end != '\0' || end == optarg || x <= 0) { 745 x = 1; 746 } 747 748 if (res == 'n') { 749 gNumRepetitions = x; 750 } else if (res == 'm') { 751 gMaxNumFrames = x; 752 } else { 753 CHECK_EQ(res, 'b'); 754 gReproduceBug = x; 755 } 756 break; 757 } 758 759 case 'w': 760 { 761 gWriteMP4 = true; 762 gWriteMP4Filename.setTo(optarg); 763 break; 764 } 765 766 case 'p': 767 { 768 dumpProfiles = true; 769 break; 770 } 771 772 case 't': 773 { 774 extractThumbnail = true; 775 break; 776 } 777 778 case 's': 779 { 780 gPreferSoftwareCodec = true; 781 break; 782 } 783 784 case 'r': 785 { 786 gForceToUseHardwareCodec = true; 787 break; 788 } 789 790 case 'o': 791 { 792 gPlaybackAudio = true; 793 break; 794 } 795 796 case 'k': 797 { 798 seekTest = true; 799 break; 800 } 801 802 case 'x': 803 { 804 gDisplayHistogram = true; 805 break; 806 } 807 808 case 'S': 809 { 810 useSurfaceAlloc = true; 811 break; 812 } 813 814 case 'T': 815 { 816 useSurfaceTexAlloc = true; 817 break; 818 } 819 820 case '?': 821 case 'h': 822 default: 823 { 824 usage(argv[0]); 825 exit(1); 826 break; 827 } 828 } 829 } 830 831 if (gPlaybackAudio && !audioOnly) { 832 // This doesn't make any sense if we're decoding the video track. 833 gPlaybackAudio = false; 834 } 835 836 argc -= optind; 837 argv += optind; 838 839 if (extractThumbnail) { 840 sp<IServiceManager> sm = defaultServiceManager(); 841 sp<IBinder> binder = sm->getService(String16("media.player")); 842 sp<IMediaPlayerService> service = 843 interface_cast<IMediaPlayerService>(binder); 844 845 CHECK(service.get() != NULL); 846 847 sp<IMediaMetadataRetriever> retriever = 848 service->createMetadataRetriever(); 849 850 CHECK(retriever != NULL); 851 852 for (int k = 0; k < argc; ++k) { 853 const char *filename = argv[k]; 854 855 bool failed = true; 856 857 int fd = open(filename, O_RDONLY | O_LARGEFILE); 858 CHECK_GE(fd, 0); 859 860 off64_t fileSize = lseek64(fd, 0, SEEK_END); 861 CHECK_GE(fileSize, 0ll); 862 863 CHECK_EQ(retriever->setDataSource(fd, 0, fileSize), (status_t)OK); 864 865 close(fd); 866 fd = -1; 867 868 sp<IMemory> mem = 869 retriever->getFrameAtTime(-1, 870 MediaSource::ReadOptions::SEEK_PREVIOUS_SYNC); 871 872 if (mem != NULL) { 873 failed = false; 874 printf("getFrameAtTime(%s) => OK\n", filename); 875 876 VideoFrame *frame = (VideoFrame *)mem->pointer(); 877 878 CHECK_EQ(writeJpegFile("/sdcard/out.jpg", 879 (uint8_t *)frame + sizeof(VideoFrame), 880 frame->mWidth, frame->mHeight), 0); 881 } 882 883 { 884 mem = retriever->extractAlbumArt(); 885 886 if (mem != NULL) { 887 failed = false; 888 printf("extractAlbumArt(%s) => OK\n", filename); 889 } 890 } 891 892 if (failed) { 893 printf("both getFrameAtTime and extractAlbumArt " 894 "failed on file '%s'.\n", filename); 895 } 896 } 897 898 return 0; 899 } 900 901 if (dumpProfiles) { 902 dumpCodecProfiles(true /* queryDecoders */); 903 dumpCodecProfiles(false /* queryDecoders */); 904 } 905 906 if (listComponents) { 907 sp<IServiceManager> sm = defaultServiceManager(); 908 sp<IBinder> binder = sm->getService(String16("media.codec")); 909 sp<IMediaCodecService> service = interface_cast<IMediaCodecService>(binder); 910 911 CHECK(service.get() != NULL); 912 913 sp<IOMX> omx = service->getOMX(); 914 CHECK(omx.get() != NULL); 915 916 List<IOMX::ComponentInfo> list; 917 omx->listNodes(&list); 918 919 for (List<IOMX::ComponentInfo>::iterator it = list.begin(); 920 it != list.end(); ++it) { 921 printf("%s\t Roles: ", (*it).mName.string()); 922 for (List<String8>::iterator itRoles = (*it).mRoles.begin() ; 923 itRoles != (*it).mRoles.end() ; ++itRoles) { 924 printf("%s\t", (*itRoles).string()); 925 } 926 printf("\n"); 927 } 928 } 929 930 sp<SurfaceComposerClient> composerClient; 931 sp<SurfaceControl> control; 932 933 if ((useSurfaceAlloc || useSurfaceTexAlloc) && !audioOnly) { 934 if (useSurfaceAlloc) { 935 composerClient = new SurfaceComposerClient; 936 CHECK_EQ(composerClient->initCheck(), (status_t)OK); 937 938 control = composerClient->createSurface( 939 String8("A Surface"), 940 1280, 941 800, 942 PIXEL_FORMAT_RGB_565, 943 0); 944 945 CHECK(control != NULL); 946 CHECK(control->isValid()); 947 948 SurfaceComposerClient::openGlobalTransaction(); 949 CHECK_EQ(control->setLayer(INT_MAX), (status_t)OK); 950 CHECK_EQ(control->show(), (status_t)OK); 951 SurfaceComposerClient::closeGlobalTransaction(); 952 953 gSurface = control->getSurface(); 954 CHECK(gSurface != NULL); 955 } else { 956 CHECK(useSurfaceTexAlloc); 957 958 sp<IGraphicBufferProducer> producer; 959 sp<IGraphicBufferConsumer> consumer; 960 BufferQueue::createBufferQueue(&producer, &consumer); 961 sp<GLConsumer> texture = new GLConsumer(consumer, 0 /* tex */, 962 GLConsumer::TEXTURE_EXTERNAL, true /* useFenceSync */, 963 false /* isControlledByApp */); 964 gSurface = new Surface(producer); 965 } 966 } 967 968 status_t err = OK; 969 970 for (int k = 0; k < argc && err == OK; ++k) { 971 bool syncInfoPresent = true; 972 973 const char *filename = argv[k]; 974 975 sp<DataSource> dataSource = 976 DataSource::CreateFromURI(NULL /* httpService */, filename); 977 978 if (strncasecmp(filename, "sine:", 5) && dataSource == NULL) { 979 fprintf(stderr, "Unable to create data source.\n"); 980 return 1; 981 } 982 983 bool isJPEG = false; 984 985 size_t len = strlen(filename); 986 if (len >= 4 && !strcasecmp(filename + len - 4, ".jpg")) { 987 isJPEG = true; 988 } 989 990 Vector<sp<IMediaSource> > mediaSources; 991 sp<IMediaSource> mediaSource; 992 993 if (isJPEG) { 994 mediaSource = new JPEGSource(dataSource); 995 if (gWriteMP4) { 996 mediaSources.push(mediaSource); 997 } 998 } else if (!strncasecmp("sine:", filename, 5)) { 999 char *end; 1000 long sampleRate = strtol(filename + 5, &end, 10); 1001 1002 if (end == filename + 5) { 1003 sampleRate = 44100; 1004 } 1005 mediaSource = new SineSource(sampleRate, 1); 1006 if (gWriteMP4) { 1007 mediaSources.push(mediaSource); 1008 } 1009 } else { 1010 sp<IMediaExtractor> extractor = MediaExtractor::Create(dataSource); 1011 1012 if (extractor == NULL) { 1013 fprintf(stderr, "could not create extractor.\n"); 1014 return -1; 1015 } 1016 1017 sp<MetaData> meta = extractor->getMetaData(); 1018 1019 if (meta != NULL) { 1020 const char *mime; 1021 if (!meta->findCString(kKeyMIMEType, &mime)) { 1022 fprintf(stderr, "extractor did not provide MIME type.\n"); 1023 return -1; 1024 } 1025 1026 if (!strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_MPEG2TS)) { 1027 syncInfoPresent = false; 1028 } 1029 } 1030 1031 size_t numTracks = extractor->countTracks(); 1032 1033 if (gWriteMP4) { 1034 bool haveAudio = false; 1035 bool haveVideo = false; 1036 for (size_t i = 0; i < numTracks; ++i) { 1037 sp<IMediaSource> source = extractor->getTrack(i); 1038 1039 const char *mime; 1040 CHECK(source->getFormat()->findCString( 1041 kKeyMIMEType, &mime)); 1042 1043 bool useTrack = false; 1044 if (!haveAudio && !strncasecmp("audio/", mime, 6)) { 1045 haveAudio = true; 1046 useTrack = true; 1047 } else if (!haveVideo && !strncasecmp("video/", mime, 6)) { 1048 haveVideo = true; 1049 useTrack = true; 1050 } 1051 1052 if (useTrack) { 1053 mediaSources.push(source); 1054 1055 if (haveAudio && haveVideo) { 1056 break; 1057 } 1058 } 1059 } 1060 } else { 1061 sp<MetaData> meta; 1062 size_t i; 1063 for (i = 0; i < numTracks; ++i) { 1064 meta = extractor->getTrackMetaData( 1065 i, MediaExtractor::kIncludeExtensiveMetaData); 1066 1067 if (meta == NULL) { 1068 break; 1069 } 1070 const char *mime; 1071 meta->findCString(kKeyMIMEType, &mime); 1072 1073 if (audioOnly && !strncasecmp(mime, "audio/", 6)) { 1074 break; 1075 } 1076 1077 if (!audioOnly && !strncasecmp(mime, "video/", 6)) { 1078 break; 1079 } 1080 1081 meta = NULL; 1082 } 1083 1084 if (meta == NULL) { 1085 fprintf(stderr, 1086 "No suitable %s track found. The '-a' option will " 1087 "target audio tracks only, the default is to target " 1088 "video tracks only.\n", 1089 audioOnly ? "audio" : "video"); 1090 return -1; 1091 } 1092 1093 int64_t thumbTimeUs; 1094 if (meta->findInt64(kKeyThumbnailTime, &thumbTimeUs)) { 1095 printf("thumbnailTime: %" PRId64 " us (%.2f secs)\n", 1096 thumbTimeUs, thumbTimeUs / 1E6); 1097 } 1098 1099 mediaSource = extractor->getTrack(i); 1100 } 1101 } 1102 1103 if (gWriteMP4) { 1104 writeSourcesToMP4(mediaSources, syncInfoPresent); 1105 } else if (dumpStream) { 1106 dumpSource(mediaSource, dumpStreamFilename); 1107 } else if (dumpPCMStream) { 1108 sp<IMediaSource> decSource = SimpleDecodingSource::Create(mediaSource); 1109 dumpSource(decSource, dumpStreamFilename); 1110 } else if (seekTest) { 1111 performSeekTest(mediaSource); 1112 } else { 1113 playSource(mediaSource); 1114 } 1115 } 1116 1117 if ((useSurfaceAlloc || useSurfaceTexAlloc) && !audioOnly) { 1118 gSurface.clear(); 1119 1120 if (useSurfaceAlloc) { 1121 composerClient->dispose(); 1122 } 1123 } 1124 1125 return 0; 1126} 1127