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