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