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