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