stagefright.cpp revision c6ac859f5a82ea8642bc6351a45508a15f224f32
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/IMediaHTTPService.h> 35#include <media/IMediaPlayerService.h> 36#include <media/stagefright/foundation/ALooper.h> 37#include "include/NuCachedSource2.h" 38#include <media/stagefright/AudioPlayer.h> 39#include <media/stagefright/DataSource.h> 40#include <media/stagefright/JPEGSource.h> 41#include <media/stagefright/MediaDefs.h> 42#include <media/stagefright/MediaErrors.h> 43#include <media/stagefright/MediaExtractor.h> 44#include <media/stagefright/MediaSource.h> 45#include <media/stagefright/MetaData.h> 46#include <media/stagefright/OMXClient.h> 47#include <media/stagefright/OMXCodec.h> 48#include <media/mediametadataretriever.h> 49 50#include <media/stagefright/foundation/hexdump.h> 51#include <media/stagefright/MPEG2TSWriter.h> 52#include <media/stagefright/MPEG4Writer.h> 53 54#include <private/media/VideoFrame.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 int fd = open(gWriteMP4Filename.string(), O_CREAT | O_LARGEFILE | O_TRUNC | O_RDWR, S_IRUSR | S_IWUSR); 512 if (fd < 0) { 513 fprintf(stderr, "couldn't open file"); 514 return; 515 } 516 sp<MPEG2TSWriter> writer = 517 new MPEG2TSWriter(fd); 518#endif 519 520 // at most one minute. 521 writer->setMaxFileDuration(60000000ll); 522 523 for (size_t i = 0; i < sources.size(); ++i) { 524 sp<MediaSource> source = sources.editItemAt(i); 525 526 CHECK_EQ(writer->addSource( 527 syncInfoPresent ? source : new DetectSyncSource(source)), 528 (status_t)OK); 529 } 530 531 sp<MetaData> params = new MetaData; 532 params->setInt32(kKeyRealTimeRecording, false); 533 CHECK_EQ(writer->start(params.get()), (status_t)OK); 534 535 while (!writer->reachedEOS()) { 536 usleep(100000); 537 } 538 writer->stop(); 539} 540 541static void performSeekTest(const sp<MediaSource> &source) { 542 CHECK_EQ((status_t)OK, source->start()); 543 544 int64_t durationUs; 545 CHECK(source->getFormat()->findInt64(kKeyDuration, &durationUs)); 546 547 for (int64_t seekTimeUs = 0; seekTimeUs <= durationUs; 548 seekTimeUs += 60000ll) { 549 MediaSource::ReadOptions options; 550 options.setSeekTo( 551 seekTimeUs, MediaSource::ReadOptions::SEEK_PREVIOUS_SYNC); 552 553 MediaBuffer *buffer; 554 status_t err; 555 for (;;) { 556 err = source->read(&buffer, &options); 557 558 options.clearSeekTo(); 559 560 if (err == INFO_FORMAT_CHANGED) { 561 CHECK(buffer == NULL); 562 continue; 563 } 564 565 if (err != OK) { 566 CHECK(buffer == NULL); 567 break; 568 } 569 570 if (buffer->range_length() > 0) { 571 break; 572 } 573 574 CHECK(buffer != NULL); 575 576 buffer->release(); 577 buffer = NULL; 578 } 579 580 if (err == OK) { 581 int64_t timeUs; 582 CHECK(buffer->meta_data()->findInt64(kKeyTime, &timeUs)); 583 584 printf("%" PRId64 "\t%" PRId64 "\t%" PRId64 "\n", 585 seekTimeUs, timeUs, seekTimeUs - timeUs); 586 587 buffer->release(); 588 buffer = NULL; 589 } else { 590 printf("ERROR\n"); 591 break; 592 } 593 } 594 595 CHECK_EQ((status_t)OK, source->stop()); 596} 597 598static void usage(const char *me) { 599 fprintf(stderr, "usage: %s [options] [input_filename]\n", me); 600 fprintf(stderr, " -h(elp)\n"); 601 fprintf(stderr, " -a(udio)\n"); 602 fprintf(stderr, " -n repetitions\n"); 603 fprintf(stderr, " -l(ist) components\n"); 604 fprintf(stderr, " -m max-number-of-frames-to-decode in each pass\n"); 605 fprintf(stderr, " -b bug to reproduce\n"); 606 fprintf(stderr, " -p(rofiles) dump decoder profiles supported\n"); 607 fprintf(stderr, " -t(humbnail) extract video thumbnail or album art\n"); 608 fprintf(stderr, " -s(oftware) prefer software codec\n"); 609 fprintf(stderr, " -r(hardware) force to use hardware codec\n"); 610 fprintf(stderr, " -o playback audio\n"); 611 fprintf(stderr, " -w(rite) filename (write to .mp4 file)\n"); 612 fprintf(stderr, " -k seek test\n"); 613 fprintf(stderr, " -x display a histogram of decoding times/fps " 614 "(video only)\n"); 615 fprintf(stderr, " -S allocate buffers from a surface\n"); 616 fprintf(stderr, " -T allocate buffers from a surface texture\n"); 617 fprintf(stderr, " -d(ump) output_filename (raw stream data to a file)\n"); 618 fprintf(stderr, " -D(ump) output_filename (decoded PCM data to a file)\n"); 619} 620 621static void dumpCodecProfiles(const sp<IOMX>& omx, bool queryDecoders) { 622 const char *kMimeTypes[] = { 623 MEDIA_MIMETYPE_VIDEO_AVC, MEDIA_MIMETYPE_VIDEO_MPEG4, 624 MEDIA_MIMETYPE_VIDEO_H263, MEDIA_MIMETYPE_AUDIO_AAC, 625 MEDIA_MIMETYPE_AUDIO_AMR_NB, MEDIA_MIMETYPE_AUDIO_AMR_WB, 626 MEDIA_MIMETYPE_AUDIO_MPEG, MEDIA_MIMETYPE_AUDIO_G711_MLAW, 627 MEDIA_MIMETYPE_AUDIO_G711_ALAW, MEDIA_MIMETYPE_AUDIO_VORBIS, 628 MEDIA_MIMETYPE_VIDEO_VP8, MEDIA_MIMETYPE_VIDEO_VP9 629 }; 630 631 const char *codecType = queryDecoders? "decoder" : "encoder"; 632 printf("%s profiles:\n", codecType); 633 634 for (size_t k = 0; k < sizeof(kMimeTypes) / sizeof(kMimeTypes[0]); ++k) { 635 printf("type '%s':\n", kMimeTypes[k]); 636 637 Vector<CodecCapabilities> results; 638 // will retrieve hardware and software codecs 639 CHECK_EQ(QueryCodecs(omx, kMimeTypes[k], 640 queryDecoders, 641 &results), (status_t)OK); 642 643 for (size_t i = 0; i < results.size(); ++i) { 644 printf(" %s '%s' supports ", 645 codecType, results[i].mComponentName.string()); 646 647 if (results[i].mProfileLevels.size() == 0) { 648 printf("NOTHING.\n"); 649 continue; 650 } 651 652 for (size_t j = 0; j < results[i].mProfileLevels.size(); ++j) { 653 const CodecProfileLevel &profileLevel = 654 results[i].mProfileLevels[j]; 655 656 printf("%s%" PRIu32 "/%" PRIu32, j > 0 ? ", " : "", 657 profileLevel.mProfile, profileLevel.mLevel); 658 } 659 660 printf("\n"); 661 } 662 } 663} 664 665int main(int argc, char **argv) { 666 android::ProcessState::self()->startThreadPool(); 667 668 bool audioOnly = false; 669 bool listComponents = false; 670 bool dumpProfiles = false; 671 bool extractThumbnail = false; 672 bool seekTest = false; 673 bool useSurfaceAlloc = false; 674 bool useSurfaceTexAlloc = false; 675 bool dumpStream = false; 676 bool dumpPCMStream = false; 677 String8 dumpStreamFilename; 678 gNumRepetitions = 1; 679 gMaxNumFrames = 0; 680 gReproduceBug = -1; 681 gPreferSoftwareCodec = false; 682 gForceToUseHardwareCodec = false; 683 gPlaybackAudio = false; 684 gWriteMP4 = false; 685 gDisplayHistogram = false; 686 687 sp<ALooper> looper; 688 689 int res; 690 while ((res = getopt(argc, argv, "han:lm:b:ptsrow:kxSTd:D:")) >= 0) { 691 switch (res) { 692 case 'a': 693 { 694 audioOnly = true; 695 break; 696 } 697 698 case 'd': 699 { 700 dumpStream = true; 701 dumpStreamFilename.setTo(optarg); 702 break; 703 } 704 705 case 'D': 706 { 707 dumpPCMStream = true; 708 audioOnly = true; 709 dumpStreamFilename.setTo(optarg); 710 break; 711 } 712 713 case 'l': 714 { 715 listComponents = true; 716 break; 717 } 718 719 case 'm': 720 case 'n': 721 case 'b': 722 { 723 char *end; 724 long x = strtol(optarg, &end, 10); 725 726 if (*end != '\0' || end == optarg || x <= 0) { 727 x = 1; 728 } 729 730 if (res == 'n') { 731 gNumRepetitions = x; 732 } else if (res == 'm') { 733 gMaxNumFrames = x; 734 } else { 735 CHECK_EQ(res, 'b'); 736 gReproduceBug = x; 737 } 738 break; 739 } 740 741 case 'w': 742 { 743 gWriteMP4 = true; 744 gWriteMP4Filename.setTo(optarg); 745 break; 746 } 747 748 case 'p': 749 { 750 dumpProfiles = true; 751 break; 752 } 753 754 case 't': 755 { 756 extractThumbnail = true; 757 break; 758 } 759 760 case 's': 761 { 762 gPreferSoftwareCodec = true; 763 break; 764 } 765 766 case 'r': 767 { 768 gForceToUseHardwareCodec = true; 769 break; 770 } 771 772 case 'o': 773 { 774 gPlaybackAudio = true; 775 break; 776 } 777 778 case 'k': 779 { 780 seekTest = true; 781 break; 782 } 783 784 case 'x': 785 { 786 gDisplayHistogram = true; 787 break; 788 } 789 790 case 'S': 791 { 792 useSurfaceAlloc = true; 793 break; 794 } 795 796 case 'T': 797 { 798 useSurfaceTexAlloc = true; 799 break; 800 } 801 802 case '?': 803 case 'h': 804 default: 805 { 806 usage(argv[0]); 807 exit(1); 808 break; 809 } 810 } 811 } 812 813 if (gPlaybackAudio && !audioOnly) { 814 // This doesn't make any sense if we're decoding the video track. 815 gPlaybackAudio = false; 816 } 817 818 argc -= optind; 819 argv += optind; 820 821 if (extractThumbnail) { 822 sp<IServiceManager> sm = defaultServiceManager(); 823 sp<IBinder> binder = sm->getService(String16("media.player")); 824 sp<IMediaPlayerService> service = 825 interface_cast<IMediaPlayerService>(binder); 826 827 CHECK(service.get() != NULL); 828 829 sp<IMediaMetadataRetriever> retriever = 830 service->createMetadataRetriever(); 831 832 CHECK(retriever != NULL); 833 834 for (int k = 0; k < argc; ++k) { 835 const char *filename = argv[k]; 836 837 bool failed = true; 838 839 int fd = open(filename, O_RDONLY | O_LARGEFILE); 840 CHECK_GE(fd, 0); 841 842 off64_t fileSize = lseek64(fd, 0, SEEK_END); 843 CHECK_GE(fileSize, 0ll); 844 845 CHECK_EQ(retriever->setDataSource(fd, 0, fileSize), (status_t)OK); 846 847 close(fd); 848 fd = -1; 849 850 sp<IMemory> mem = 851 retriever->getFrameAtTime(-1, 852 MediaSource::ReadOptions::SEEK_PREVIOUS_SYNC); 853 854 if (mem != NULL) { 855 failed = false; 856 printf("getFrameAtTime(%s) => OK\n", filename); 857 858 VideoFrame *frame = (VideoFrame *)mem->pointer(); 859 860 CHECK_EQ(writeJpegFile("/sdcard/out.jpg", 861 (uint8_t *)frame + sizeof(VideoFrame), 862 frame->mWidth, frame->mHeight), 0); 863 } 864 865 { 866 mem = retriever->extractAlbumArt(); 867 868 if (mem != NULL) { 869 failed = false; 870 printf("extractAlbumArt(%s) => OK\n", filename); 871 } 872 } 873 874 if (failed) { 875 printf("both getFrameAtTime and extractAlbumArt " 876 "failed on file '%s'.\n", filename); 877 } 878 } 879 880 return 0; 881 } 882 883 if (dumpProfiles) { 884 sp<IServiceManager> sm = defaultServiceManager(); 885 sp<IBinder> binder = sm->getService(String16("media.player")); 886 sp<IMediaPlayerService> service = 887 interface_cast<IMediaPlayerService>(binder); 888 889 CHECK(service.get() != NULL); 890 891 sp<IOMX> omx = service->getOMX(); 892 CHECK(omx.get() != NULL); 893 dumpCodecProfiles(omx, true /* queryDecoders */); 894 dumpCodecProfiles(omx, false /* queryDecoders */); 895 } 896 897 if (listComponents) { 898 sp<IServiceManager> sm = defaultServiceManager(); 899 sp<IBinder> binder = sm->getService(String16("media.player")); 900 sp<IMediaPlayerService> service = interface_cast<IMediaPlayerService>(binder); 901 902 CHECK(service.get() != NULL); 903 904 sp<IOMX> omx = service->getOMX(); 905 CHECK(omx.get() != NULL); 906 907 List<IOMX::ComponentInfo> list; 908 omx->listNodes(&list); 909 910 for (List<IOMX::ComponentInfo>::iterator it = list.begin(); 911 it != list.end(); ++it) { 912 printf("%s\t Roles: ", (*it).mName.string()); 913 for (List<String8>::iterator itRoles = (*it).mRoles.begin() ; 914 itRoles != (*it).mRoles.end() ; ++itRoles) { 915 printf("%s\t", (*itRoles).string()); 916 } 917 printf("\n"); 918 } 919 } 920 921 sp<SurfaceComposerClient> composerClient; 922 sp<SurfaceControl> control; 923 924 if ((useSurfaceAlloc || useSurfaceTexAlloc) && !audioOnly) { 925 if (useSurfaceAlloc) { 926 composerClient = new SurfaceComposerClient; 927 CHECK_EQ(composerClient->initCheck(), (status_t)OK); 928 929 control = composerClient->createSurface( 930 String8("A Surface"), 931 1280, 932 800, 933 PIXEL_FORMAT_RGB_565, 934 0); 935 936 CHECK(control != NULL); 937 CHECK(control->isValid()); 938 939 SurfaceComposerClient::openGlobalTransaction(); 940 CHECK_EQ(control->setLayer(INT_MAX), (status_t)OK); 941 CHECK_EQ(control->show(), (status_t)OK); 942 SurfaceComposerClient::closeGlobalTransaction(); 943 944 gSurface = control->getSurface(); 945 CHECK(gSurface != NULL); 946 } else { 947 CHECK(useSurfaceTexAlloc); 948 949 sp<IGraphicBufferProducer> producer; 950 sp<IGraphicBufferConsumer> consumer; 951 BufferQueue::createBufferQueue(&producer, &consumer); 952 sp<GLConsumer> texture = new GLConsumer(consumer, 0 /* tex */, 953 GLConsumer::TEXTURE_EXTERNAL, true /* useFenceSync */, 954 false /* isControlledByApp */); 955 gSurface = new Surface(producer); 956 } 957 958 CHECK_EQ((status_t)OK, 959 native_window_api_connect( 960 gSurface.get(), NATIVE_WINDOW_API_MEDIA)); 961 } 962 963 DataSource::RegisterDefaultSniffers(); 964 965 OMXClient client; 966 status_t err = client.connect(); 967 968 for (int k = 0; k < argc; ++k) { 969 bool syncInfoPresent = true; 970 971 const char *filename = argv[k]; 972 973 sp<DataSource> dataSource = 974 DataSource::CreateFromURI(NULL /* httpService */, filename); 975 976 if (strncasecmp(filename, "sine:", 5) && dataSource == NULL) { 977 fprintf(stderr, "Unable to create data source.\n"); 978 return 1; 979 } 980 981 bool isJPEG = false; 982 983 size_t len = strlen(filename); 984 if (len >= 4 && !strcasecmp(filename + len - 4, ".jpg")) { 985 isJPEG = true; 986 } 987 988 Vector<sp<MediaSource> > mediaSources; 989 sp<MediaSource> mediaSource; 990 991 if (isJPEG) { 992 mediaSource = new JPEGSource(dataSource); 993 if (gWriteMP4) { 994 mediaSources.push(mediaSource); 995 } 996 } else if (!strncasecmp("sine:", filename, 5)) { 997 char *end; 998 long sampleRate = strtol(filename + 5, &end, 10); 999 1000 if (end == filename + 5) { 1001 sampleRate = 44100; 1002 } 1003 mediaSource = new SineSource(sampleRate, 1); 1004 if (gWriteMP4) { 1005 mediaSources.push(mediaSource); 1006 } 1007 } else { 1008 sp<MediaExtractor> extractor = MediaExtractor::Create(dataSource); 1009 1010 if (extractor == NULL) { 1011 fprintf(stderr, "could not create extractor.\n"); 1012 return -1; 1013 } 1014 1015 sp<MetaData> meta = extractor->getMetaData(); 1016 1017 if (meta != NULL) { 1018 const char *mime; 1019 CHECK(meta->findCString(kKeyMIMEType, &mime)); 1020 1021 if (!strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_MPEG2TS)) { 1022 syncInfoPresent = false; 1023 } 1024 } 1025 1026 size_t numTracks = extractor->countTracks(); 1027 1028 if (gWriteMP4) { 1029 bool haveAudio = false; 1030 bool haveVideo = false; 1031 for (size_t i = 0; i < numTracks; ++i) { 1032 sp<MediaSource> source = extractor->getTrack(i); 1033 1034 const char *mime; 1035 CHECK(source->getFormat()->findCString( 1036 kKeyMIMEType, &mime)); 1037 1038 bool useTrack = false; 1039 if (!haveAudio && !strncasecmp("audio/", mime, 6)) { 1040 haveAudio = true; 1041 useTrack = true; 1042 } else if (!haveVideo && !strncasecmp("video/", mime, 6)) { 1043 haveVideo = true; 1044 useTrack = true; 1045 } 1046 1047 if (useTrack) { 1048 mediaSources.push(source); 1049 1050 if (haveAudio && haveVideo) { 1051 break; 1052 } 1053 } 1054 } 1055 } else { 1056 sp<MetaData> meta; 1057 size_t i; 1058 for (i = 0; i < numTracks; ++i) { 1059 meta = extractor->getTrackMetaData( 1060 i, MediaExtractor::kIncludeExtensiveMetaData); 1061 1062 const char *mime; 1063 meta->findCString(kKeyMIMEType, &mime); 1064 1065 if (audioOnly && !strncasecmp(mime, "audio/", 6)) { 1066 break; 1067 } 1068 1069 if (!audioOnly && !strncasecmp(mime, "video/", 6)) { 1070 break; 1071 } 1072 1073 meta = NULL; 1074 } 1075 1076 if (meta == NULL) { 1077 fprintf(stderr, 1078 "No suitable %s track found. The '-a' option will " 1079 "target audio tracks only, the default is to target " 1080 "video tracks only.\n", 1081 audioOnly ? "audio" : "video"); 1082 return -1; 1083 } 1084 1085 int64_t thumbTimeUs; 1086 if (meta->findInt64(kKeyThumbnailTime, &thumbTimeUs)) { 1087 printf("thumbnailTime: %" PRId64 " us (%.2f secs)\n", 1088 thumbTimeUs, thumbTimeUs / 1E6); 1089 } 1090 1091 mediaSource = extractor->getTrack(i); 1092 } 1093 } 1094 1095 if (gWriteMP4) { 1096 writeSourcesToMP4(mediaSources, syncInfoPresent); 1097 } else if (dumpStream) { 1098 dumpSource(mediaSource, dumpStreamFilename); 1099 } else if (dumpPCMStream) { 1100 OMXClient client; 1101 CHECK_EQ(client.connect(), (status_t)OK); 1102 1103 sp<MediaSource> decSource = 1104 OMXCodec::Create( 1105 client.interface(), 1106 mediaSource->getFormat(), 1107 false, 1108 mediaSource, 1109 0, 1110 0); 1111 1112 dumpSource(decSource, dumpStreamFilename); 1113 } else if (seekTest) { 1114 performSeekTest(mediaSource); 1115 } else { 1116 playSource(&client, mediaSource); 1117 } 1118 } 1119 1120 if ((useSurfaceAlloc || useSurfaceTexAlloc) && !audioOnly) { 1121 CHECK_EQ((status_t)OK, 1122 native_window_api_disconnect( 1123 gSurface.get(), NATIVE_WINDOW_API_MEDIA)); 1124 1125 gSurface.clear(); 1126 1127 if (useSurfaceAlloc) { 1128 composerClient->dispose(); 1129 } 1130 } 1131 1132 client.disconnect(); 1133 1134 return 0; 1135} 1136