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