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