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