1 /*
2 * Copyright (C) 2009 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include <inttypes.h>
18 #include <fcntl.h>
19 #include <stdlib.h>
20 #include <string.h>
21 #include <sys/time.h>
22 #include <sys/types.h>
23 #include <sys/stat.h>
24
25 //#define LOG_NDEBUG 0
26 #define LOG_TAG "stagefright"
27 #include <media/stagefright/foundation/ADebug.h>
28
29 #include "jpeg.h"
30 #include "SineSource.h"
31
32 #include <binder/IServiceManager.h>
33 #include <binder/ProcessState.h>
34 #include <media/ICrypto.h>
35 #include <media/IMediaHTTPService.h>
36 #include <media/IMediaCodecService.h>
37 #include <media/IMediaPlayerService.h>
38 #include <media/stagefright/foundation/ABuffer.h>
39 #include <media/stagefright/foundation/ALooper.h>
40 #include <media/stagefright/foundation/AMessage.h>
41 #include <media/stagefright/foundation/AUtils.h>
42 #include "include/NuCachedSource2.h"
43 #include <media/stagefright/AudioPlayer.h>
44 #include <media/stagefright/DataSource.h>
45 #include <media/stagefright/JPEGSource.h>
46 #include <media/stagefright/MediaCodec.h>
47 #include <media/stagefright/MediaCodecList.h>
48 #include <media/stagefright/MediaDefs.h>
49 #include <media/stagefright/MediaErrors.h>
50 #include <media/stagefright/MediaExtractor.h>
51 #include <media/stagefright/MediaSource.h>
52 #include <media/stagefright/MetaData.h>
53 #include <media/stagefright/SimpleDecodingSource.h>
54 #include <media/stagefright/Utils.h>
55 #include <media/mediametadataretriever.h>
56
57 #include <media/stagefright/foundation/hexdump.h>
58 #include <media/stagefright/MPEG2TSWriter.h>
59 #include <media/stagefright/MPEG4Writer.h>
60
61 #include <private/media/VideoFrame.h>
62
63 #include <gui/GLConsumer.h>
64 #include <gui/Surface.h>
65 #include <gui/SurfaceComposerClient.h>
66
67 using namespace android;
68
69 static long gNumRepetitions;
70 static long gMaxNumFrames; // 0 means decode all available.
71 static long gReproduceBug; // if not -1.
72 static bool gPreferSoftwareCodec;
73 static bool gForceToUseHardwareCodec;
74 static bool gPlaybackAudio;
75 static bool gWriteMP4;
76 static bool gDisplayHistogram;
77 static bool showProgress = true;
78 static String8 gWriteMP4Filename;
79
80 static sp<ANativeWindow> gSurface;
81
getNowUs()82 static int64_t getNowUs() {
83 struct timeval tv;
84 gettimeofday(&tv, NULL);
85
86 return (int64_t)tv.tv_usec + tv.tv_sec * 1000000ll;
87 }
88
CompareIncreasing(const int64_t * a,const int64_t * b)89 static int CompareIncreasing(const int64_t *a, const int64_t *b) {
90 return (*a) < (*b) ? -1 : (*a) > (*b) ? 1 : 0;
91 }
92
displayDecodeHistogram(Vector<int64_t> * decodeTimesUs)93 static void displayDecodeHistogram(Vector<int64_t> *decodeTimesUs) {
94 printf("decode times:\n");
95
96 decodeTimesUs->sort(CompareIncreasing);
97
98 size_t n = decodeTimesUs->size();
99 int64_t minUs = decodeTimesUs->itemAt(0);
100 int64_t maxUs = decodeTimesUs->itemAt(n - 1);
101
102 printf("min decode time %" PRId64 " us (%.2f secs)\n", minUs, minUs / 1E6);
103 printf("max decode time %" PRId64 " us (%.2f secs)\n", maxUs, maxUs / 1E6);
104
105 size_t counts[100];
106 for (size_t i = 0; i < 100; ++i) {
107 counts[i] = 0;
108 }
109
110 for (size_t i = 0; i < n; ++i) {
111 int64_t x = decodeTimesUs->itemAt(i);
112
113 size_t slot = ((x - minUs) * 100) / (maxUs - minUs);
114 if (slot == 100) { slot = 99; }
115
116 ++counts[slot];
117 }
118
119 for (size_t i = 0; i < 100; ++i) {
120 int64_t slotUs = minUs + (i * (maxUs - minUs) / 100);
121
122 double fps = 1E6 / slotUs;
123 printf("[%.2f fps]: %zu\n", fps, counts[i]);
124 }
125 }
126
displayAVCProfileLevelIfPossible(const sp<MetaData> & meta)127 static void displayAVCProfileLevelIfPossible(const sp<MetaData>& meta) {
128 uint32_t type;
129 const void *data;
130 size_t size;
131 if (meta->findData(kKeyAVCC, &type, &data, &size)) {
132 const uint8_t *ptr = (const uint8_t *)data;
133 CHECK(size >= 7);
134 CHECK(ptr[0] == 1); // configurationVersion == 1
135 uint8_t profile = ptr[1];
136 uint8_t level = ptr[3];
137 fprintf(stderr, "AVC video profile %d and level %d\n", profile, level);
138 }
139 }
140
dumpSource(const sp<IMediaSource> & source,const String8 & filename)141 static void dumpSource(const sp<IMediaSource> &source, const String8 &filename) {
142 FILE *out = fopen(filename.string(), "wb");
143
144 CHECK_EQ((status_t)OK, source->start());
145
146 status_t err;
147 for (;;) {
148 MediaBuffer *mbuf;
149 err = source->read(&mbuf);
150
151 if (err == INFO_FORMAT_CHANGED) {
152 continue;
153 } else if (err != OK) {
154 break;
155 }
156
157 CHECK_EQ(
158 fwrite((const uint8_t *)mbuf->data() + mbuf->range_offset(),
159 1,
160 mbuf->range_length(),
161 out),
162 (ssize_t)mbuf->range_length());
163
164 mbuf->release();
165 mbuf = NULL;
166 }
167
168 CHECK_EQ((status_t)OK, source->stop());
169
170 fclose(out);
171 out = NULL;
172 }
173
playSource(sp<IMediaSource> & source)174 static void playSource(sp<IMediaSource> &source) {
175 sp<MetaData> meta = source->getFormat();
176
177 const char *mime;
178 CHECK(meta->findCString(kKeyMIMEType, &mime));
179
180 sp<IMediaSource> rawSource;
181 if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_RAW, mime)) {
182 rawSource = source;
183 } else {
184 int flags = 0;
185 if (gPreferSoftwareCodec) {
186 flags |= MediaCodecList::kPreferSoftwareCodecs;
187 }
188 if (gForceToUseHardwareCodec) {
189 CHECK(!gPreferSoftwareCodec);
190 flags |= MediaCodecList::kHardwareCodecsOnly;
191 }
192 rawSource = SimpleDecodingSource::Create(source, flags, gSurface);
193 if (rawSource == NULL) {
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 %" PRId64 " 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 %" PRId64 " 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 (showProgress && (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 if (showProgress) {
370 printf("$");
371 fflush(stdout);
372 }
373
374 options.setSeekTo(0);
375 }
376
377 rawSource->stop();
378 printf("\n");
379
380 int64_t delay = getNowUs() - startTime;
381 if (!strncasecmp("video/", mime, 6)) {
382 printf("avg. %.2f fps\n", n * 1E6 / delay);
383
384 printf("avg. time to decode one buffer %.2f usecs\n",
385 (double)sumDecodeUs / n);
386
387 printf("decoded a total of %d frame(s).\n", n);
388
389 if (gDisplayHistogram) {
390 displayDecodeHistogram(&decodeTimesUs);
391 }
392 } else if (!strncasecmp("audio/", mime, 6)) {
393 // Frame count makes less sense for audio, as the output buffer
394 // sizes may be different across decoders.
395 printf("avg. %.2f KB/sec\n", totalBytes / 1024 * 1E6 / delay);
396
397 printf("decoded a total of %" PRId64 " bytes\n", totalBytes);
398 }
399 }
400
401 ////////////////////////////////////////////////////////////////////////////////
402
403 struct DetectSyncSource : public MediaSource {
404 DetectSyncSource(const sp<IMediaSource> &source);
405
406 virtual status_t start(MetaData *params = NULL);
407 virtual status_t stop();
408 virtual sp<MetaData> getFormat();
409
410 virtual status_t read(
411 MediaBuffer **buffer, const ReadOptions *options);
412
413 private:
414 enum StreamType {
415 AVC,
416 MPEG4,
417 H263,
418 OTHER,
419 };
420
421 sp<IMediaSource> mSource;
422 StreamType mStreamType;
423 bool mSawFirstIDRFrame;
424
425 DISALLOW_EVIL_CONSTRUCTORS(DetectSyncSource);
426 };
427
DetectSyncSource(const sp<IMediaSource> & source)428 DetectSyncSource::DetectSyncSource(const sp<IMediaSource> &source)
429 : mSource(source),
430 mStreamType(OTHER),
431 mSawFirstIDRFrame(false) {
432 const char *mime;
433 CHECK(mSource->getFormat()->findCString(kKeyMIMEType, &mime));
434
435 if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AVC)) {
436 mStreamType = AVC;
437 } else if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_MPEG4)) {
438 mStreamType = MPEG4;
439 CHECK(!"sync frame detection not implemented yet for MPEG4");
440 } else if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_H263)) {
441 mStreamType = H263;
442 CHECK(!"sync frame detection not implemented yet for H.263");
443 }
444 }
445
start(MetaData * params)446 status_t DetectSyncSource::start(MetaData *params) {
447 mSawFirstIDRFrame = false;
448
449 return mSource->start(params);
450 }
451
stop()452 status_t DetectSyncSource::stop() {
453 return mSource->stop();
454 }
455
getFormat()456 sp<MetaData> DetectSyncSource::getFormat() {
457 return mSource->getFormat();
458 }
459
isIDRFrame(MediaBuffer * buffer)460 static bool isIDRFrame(MediaBuffer *buffer) {
461 const uint8_t *data =
462 (const uint8_t *)buffer->data() + buffer->range_offset();
463 size_t size = buffer->range_length();
464 for (size_t i = 0; i + 3 < size; ++i) {
465 if (!memcmp("\x00\x00\x01", &data[i], 3)) {
466 uint8_t nalType = data[i + 3] & 0x1f;
467 if (nalType == 5) {
468 return true;
469 }
470 }
471 }
472
473 return false;
474 }
475
read(MediaBuffer ** buffer,const ReadOptions * options)476 status_t DetectSyncSource::read(
477 MediaBuffer **buffer, const ReadOptions *options) {
478 for (;;) {
479 status_t err = mSource->read(buffer, options);
480
481 if (err != OK) {
482 return err;
483 }
484
485 if (mStreamType == AVC) {
486 bool isIDR = isIDRFrame(*buffer);
487 (*buffer)->meta_data()->setInt32(kKeyIsSyncFrame, isIDR);
488 if (isIDR) {
489 mSawFirstIDRFrame = true;
490 }
491 } else {
492 (*buffer)->meta_data()->setInt32(kKeyIsSyncFrame, true);
493 }
494
495 if (mStreamType != AVC || mSawFirstIDRFrame) {
496 break;
497 }
498
499 // Ignore everything up to the first IDR frame.
500 (*buffer)->release();
501 *buffer = NULL;
502 }
503
504 return OK;
505 }
506
507 ////////////////////////////////////////////////////////////////////////////////
508
writeSourcesToMP4(Vector<sp<IMediaSource>> & sources,bool syncInfoPresent)509 static void writeSourcesToMP4(
510 Vector<sp<IMediaSource> > &sources, bool syncInfoPresent) {
511 #if 0
512 sp<MPEG4Writer> writer =
513 new MPEG4Writer(gWriteMP4Filename.string());
514 #else
515 int fd = open(gWriteMP4Filename.string(), O_CREAT | O_LARGEFILE | O_TRUNC | O_RDWR, S_IRUSR | S_IWUSR);
516 if (fd < 0) {
517 fprintf(stderr, "couldn't open file");
518 return;
519 }
520 sp<MPEG2TSWriter> writer =
521 new MPEG2TSWriter(fd);
522 #endif
523
524 // at most one minute.
525 writer->setMaxFileDuration(60000000ll);
526
527 for (size_t i = 0; i < sources.size(); ++i) {
528 sp<IMediaSource> source = sources.editItemAt(i);
529
530 CHECK_EQ(writer->addSource(
531 syncInfoPresent ? source : new DetectSyncSource(source)),
532 (status_t)OK);
533 }
534
535 sp<MetaData> params = new MetaData;
536 params->setInt32(kKeyRealTimeRecording, false);
537 CHECK_EQ(writer->start(params.get()), (status_t)OK);
538
539 while (!writer->reachedEOS()) {
540 usleep(100000);
541 }
542 writer->stop();
543 }
544
performSeekTest(const sp<IMediaSource> & source)545 static void performSeekTest(const sp<IMediaSource> &source) {
546 CHECK_EQ((status_t)OK, source->start());
547
548 int64_t durationUs;
549 CHECK(source->getFormat()->findInt64(kKeyDuration, &durationUs));
550
551 for (int64_t seekTimeUs = 0; seekTimeUs <= durationUs;
552 seekTimeUs += 60000ll) {
553 MediaSource::ReadOptions options;
554 options.setSeekTo(
555 seekTimeUs, MediaSource::ReadOptions::SEEK_PREVIOUS_SYNC);
556
557 MediaBuffer *buffer;
558 status_t err;
559 for (;;) {
560 err = source->read(&buffer, &options);
561
562 options.clearSeekTo();
563
564 if (err == INFO_FORMAT_CHANGED) {
565 CHECK(buffer == NULL);
566 continue;
567 }
568
569 if (err != OK) {
570 CHECK(buffer == NULL);
571 break;
572 }
573
574 if (buffer->range_length() > 0) {
575 break;
576 }
577
578 CHECK(buffer != NULL);
579
580 buffer->release();
581 buffer = NULL;
582 }
583
584 if (err == OK) {
585 int64_t timeUs;
586 CHECK(buffer->meta_data()->findInt64(kKeyTime, &timeUs));
587
588 printf("%" PRId64 "\t%" PRId64 "\t%" PRId64 "\n",
589 seekTimeUs, timeUs, seekTimeUs - timeUs);
590
591 buffer->release();
592 buffer = NULL;
593 } else {
594 printf("ERROR\n");
595 break;
596 }
597 }
598
599 CHECK_EQ((status_t)OK, source->stop());
600 }
601
usage(const char * me)602 static void usage(const char *me) {
603 fprintf(stderr, "usage: %s [options] [input_filename]\n", me);
604 fprintf(stderr, " -h(elp)\n");
605 fprintf(stderr, " -a(udio)\n");
606 fprintf(stderr, " -n repetitions\n");
607 fprintf(stderr, " -l(ist) components\n");
608 fprintf(stderr, " -m max-number-of-frames-to-decode in each pass\n");
609 fprintf(stderr, " -b bug to reproduce\n");
610 fprintf(stderr, " -p(rofiles) dump decoder profiles supported\n");
611 fprintf(stderr, " -t(humbnail) extract video thumbnail or album art\n");
612 fprintf(stderr, " -s(oftware) prefer software codec\n");
613 fprintf(stderr, " -r(hardware) force to use hardware codec\n");
614 fprintf(stderr, " -o playback audio\n");
615 fprintf(stderr, " -w(rite) filename (write to .mp4 file)\n");
616 fprintf(stderr, " -k seek test\n");
617 fprintf(stderr, " -x display a histogram of decoding times/fps "
618 "(video only)\n");
619 fprintf(stderr, " -q don't show progress indicator\n");
620 fprintf(stderr, " -S allocate buffers from a surface\n");
621 fprintf(stderr, " -T allocate buffers from a surface texture\n");
622 fprintf(stderr, " -d(ump) output_filename (raw stream data to a file)\n");
623 fprintf(stderr, " -D(ump) output_filename (decoded PCM data to a file)\n");
624 }
625
dumpCodecProfiles(bool queryDecoders)626 static void dumpCodecProfiles(bool queryDecoders) {
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, MEDIA_MIMETYPE_AUDIO_G711_MLAW,
632 MEDIA_MIMETYPE_AUDIO_G711_ALAW, MEDIA_MIMETYPE_AUDIO_VORBIS,
633 MEDIA_MIMETYPE_VIDEO_VP8, MEDIA_MIMETYPE_VIDEO_VP9,
634 MEDIA_MIMETYPE_VIDEO_DOLBY_VISION
635 };
636
637 const char *codecType = queryDecoders? "decoder" : "encoder";
638 printf("%s profiles:\n", codecType);
639
640 sp<IMediaCodecList> list = MediaCodecList::getInstance();
641 size_t numCodecs = list->countCodecs();
642
643 for (size_t k = 0; k < sizeof(kMimeTypes) / sizeof(kMimeTypes[0]); ++k) {
644 printf("type '%s':\n", kMimeTypes[k]);
645
646 for (size_t index = 0; index < numCodecs; ++index) {
647 sp<MediaCodecInfo> info = list->getCodecInfo(index);
648 if (info == NULL || info->isEncoder() != !queryDecoders) {
649 continue;
650 }
651 sp<MediaCodecInfo::Capabilities> caps = info->getCapabilitiesFor(kMimeTypes[k]);
652 if (caps == NULL) {
653 continue;
654 }
655 printf(" %s '%s' supports ",
656 codecType, info->getCodecName());
657
658 Vector<MediaCodecInfo::ProfileLevel> profileLevels;
659 caps->getSupportedProfileLevels(&profileLevels);
660 if (profileLevels.size() == 0) {
661 printf("NOTHING.\n");
662 continue;
663 }
664
665 for (size_t j = 0; j < profileLevels.size(); ++j) {
666 const MediaCodecInfo::ProfileLevel &profileLevel = profileLevels[j];
667
668 printf("%s%u/%u", j > 0 ? ", " : "",
669 profileLevel.mProfile, profileLevel.mLevel);
670 }
671
672 printf("\n");
673 }
674 }
675 }
676
main(int argc,char ** argv)677 int main(int argc, char **argv) {
678 android::ProcessState::self()->startThreadPool();
679
680 bool audioOnly = false;
681 bool listComponents = false;
682 bool dumpProfiles = false;
683 bool extractThumbnail = false;
684 bool seekTest = false;
685 bool useSurfaceAlloc = false;
686 bool useSurfaceTexAlloc = false;
687 bool dumpStream = false;
688 bool dumpPCMStream = false;
689 String8 dumpStreamFilename;
690 gNumRepetitions = 1;
691 gMaxNumFrames = 0;
692 gReproduceBug = -1;
693 gPreferSoftwareCodec = false;
694 gForceToUseHardwareCodec = false;
695 gPlaybackAudio = false;
696 gWriteMP4 = false;
697 gDisplayHistogram = false;
698
699 sp<ALooper> looper;
700
701 int res;
702 while ((res = getopt(argc, argv, "haqn:lm:b:ptsrow:kxSTd:D:")) >= 0) {
703 switch (res) {
704 case 'a':
705 {
706 audioOnly = true;
707 break;
708 }
709
710 case 'q':
711 {
712 showProgress = false;
713 break;
714 }
715
716 case 'd':
717 {
718 dumpStream = true;
719 dumpStreamFilename.setTo(optarg);
720 break;
721 }
722
723 case 'D':
724 {
725 dumpPCMStream = true;
726 audioOnly = true;
727 dumpStreamFilename.setTo(optarg);
728 break;
729 }
730
731 case 'l':
732 {
733 listComponents = true;
734 break;
735 }
736
737 case 'm':
738 case 'n':
739 case 'b':
740 {
741 char *end;
742 long x = strtol(optarg, &end, 10);
743
744 if (*end != '\0' || end == optarg || x <= 0) {
745 x = 1;
746 }
747
748 if (res == 'n') {
749 gNumRepetitions = x;
750 } else if (res == 'm') {
751 gMaxNumFrames = x;
752 } else {
753 CHECK_EQ(res, 'b');
754 gReproduceBug = x;
755 }
756 break;
757 }
758
759 case 'w':
760 {
761 gWriteMP4 = true;
762 gWriteMP4Filename.setTo(optarg);
763 break;
764 }
765
766 case 'p':
767 {
768 dumpProfiles = true;
769 break;
770 }
771
772 case 't':
773 {
774 extractThumbnail = true;
775 break;
776 }
777
778 case 's':
779 {
780 gPreferSoftwareCodec = true;
781 break;
782 }
783
784 case 'r':
785 {
786 gForceToUseHardwareCodec = true;
787 break;
788 }
789
790 case 'o':
791 {
792 gPlaybackAudio = true;
793 break;
794 }
795
796 case 'k':
797 {
798 seekTest = true;
799 break;
800 }
801
802 case 'x':
803 {
804 gDisplayHistogram = true;
805 break;
806 }
807
808 case 'S':
809 {
810 useSurfaceAlloc = true;
811 break;
812 }
813
814 case 'T':
815 {
816 useSurfaceTexAlloc = true;
817 break;
818 }
819
820 case '?':
821 case 'h':
822 default:
823 {
824 usage(argv[0]);
825 exit(1);
826 break;
827 }
828 }
829 }
830
831 if (gPlaybackAudio && !audioOnly) {
832 // This doesn't make any sense if we're decoding the video track.
833 gPlaybackAudio = false;
834 }
835
836 argc -= optind;
837 argv += optind;
838
839 if (extractThumbnail) {
840 sp<IServiceManager> sm = defaultServiceManager();
841 sp<IBinder> binder = sm->getService(String16("media.player"));
842 sp<IMediaPlayerService> service =
843 interface_cast<IMediaPlayerService>(binder);
844
845 CHECK(service.get() != NULL);
846
847 sp<IMediaMetadataRetriever> retriever =
848 service->createMetadataRetriever();
849
850 CHECK(retriever != NULL);
851
852 for (int k = 0; k < argc; ++k) {
853 const char *filename = argv[k];
854
855 bool failed = true;
856
857 int fd = open(filename, O_RDONLY | O_LARGEFILE);
858 CHECK_GE(fd, 0);
859
860 off64_t fileSize = lseek64(fd, 0, SEEK_END);
861 CHECK_GE(fileSize, 0ll);
862
863 CHECK_EQ(retriever->setDataSource(fd, 0, fileSize), (status_t)OK);
864
865 close(fd);
866 fd = -1;
867
868 sp<IMemory> mem =
869 retriever->getFrameAtTime(-1,
870 MediaSource::ReadOptions::SEEK_PREVIOUS_SYNC);
871
872 if (mem != NULL) {
873 failed = false;
874 printf("getFrameAtTime(%s) => OK\n", filename);
875
876 VideoFrame *frame = (VideoFrame *)mem->pointer();
877
878 CHECK_EQ(writeJpegFile("/sdcard/out.jpg",
879 (uint8_t *)frame + sizeof(VideoFrame),
880 frame->mWidth, frame->mHeight), 0);
881 }
882
883 {
884 mem = retriever->extractAlbumArt();
885
886 if (mem != NULL) {
887 failed = false;
888 printf("extractAlbumArt(%s) => OK\n", filename);
889 }
890 }
891
892 if (failed) {
893 printf("both getFrameAtTime and extractAlbumArt "
894 "failed on file '%s'.\n", filename);
895 }
896 }
897
898 return 0;
899 }
900
901 if (dumpProfiles) {
902 dumpCodecProfiles(true /* queryDecoders */);
903 dumpCodecProfiles(false /* queryDecoders */);
904 }
905
906 if (listComponents) {
907 sp<IServiceManager> sm = defaultServiceManager();
908 sp<IBinder> binder = sm->getService(String16("media.codec"));
909 sp<IMediaCodecService> service = interface_cast<IMediaCodecService>(binder);
910
911 CHECK(service.get() != NULL);
912
913 sp<IOMX> omx = service->getOMX();
914 CHECK(omx.get() != NULL);
915
916 List<IOMX::ComponentInfo> list;
917 omx->listNodes(&list);
918
919 for (List<IOMX::ComponentInfo>::iterator it = list.begin();
920 it != list.end(); ++it) {
921 printf("%s\t Roles: ", (*it).mName.string());
922 for (List<String8>::iterator itRoles = (*it).mRoles.begin() ;
923 itRoles != (*it).mRoles.end() ; ++itRoles) {
924 printf("%s\t", (*itRoles).string());
925 }
926 printf("\n");
927 }
928 }
929
930 sp<SurfaceComposerClient> composerClient;
931 sp<SurfaceControl> control;
932
933 if ((useSurfaceAlloc || useSurfaceTexAlloc) && !audioOnly) {
934 if (useSurfaceAlloc) {
935 composerClient = new SurfaceComposerClient;
936 CHECK_EQ(composerClient->initCheck(), (status_t)OK);
937
938 control = composerClient->createSurface(
939 String8("A Surface"),
940 1280,
941 800,
942 PIXEL_FORMAT_RGB_565,
943 0);
944
945 CHECK(control != NULL);
946 CHECK(control->isValid());
947
948 SurfaceComposerClient::openGlobalTransaction();
949 CHECK_EQ(control->setLayer(INT_MAX), (status_t)OK);
950 CHECK_EQ(control->show(), (status_t)OK);
951 SurfaceComposerClient::closeGlobalTransaction();
952
953 gSurface = control->getSurface();
954 CHECK(gSurface != NULL);
955 } else {
956 CHECK(useSurfaceTexAlloc);
957
958 sp<IGraphicBufferProducer> producer;
959 sp<IGraphicBufferConsumer> consumer;
960 BufferQueue::createBufferQueue(&producer, &consumer);
961 sp<GLConsumer> texture = new GLConsumer(consumer, 0 /* tex */,
962 GLConsumer::TEXTURE_EXTERNAL, true /* useFenceSync */,
963 false /* isControlledByApp */);
964 gSurface = new Surface(producer);
965 }
966 }
967
968 DataSource::RegisterDefaultSniffers();
969
970 status_t err = OK;
971
972 for (int k = 0; k < argc && err == OK; ++k) {
973 bool syncInfoPresent = true;
974
975 const char *filename = argv[k];
976
977 sp<DataSource> dataSource =
978 DataSource::CreateFromURI(NULL /* httpService */, filename);
979
980 if (strncasecmp(filename, "sine:", 5) && dataSource == NULL) {
981 fprintf(stderr, "Unable to create data source.\n");
982 return 1;
983 }
984
985 bool isJPEG = false;
986
987 size_t len = strlen(filename);
988 if (len >= 4 && !strcasecmp(filename + len - 4, ".jpg")) {
989 isJPEG = true;
990 }
991
992 Vector<sp<IMediaSource> > mediaSources;
993 sp<IMediaSource> mediaSource;
994
995 if (isJPEG) {
996 mediaSource = new JPEGSource(dataSource);
997 if (gWriteMP4) {
998 mediaSources.push(mediaSource);
999 }
1000 } else if (!strncasecmp("sine:", filename, 5)) {
1001 char *end;
1002 long sampleRate = strtol(filename + 5, &end, 10);
1003
1004 if (end == filename + 5) {
1005 sampleRate = 44100;
1006 }
1007 mediaSource = new SineSource(sampleRate, 1);
1008 if (gWriteMP4) {
1009 mediaSources.push(mediaSource);
1010 }
1011 } else {
1012 sp<IMediaExtractor> extractor = MediaExtractor::Create(dataSource);
1013
1014 if (extractor == NULL) {
1015 fprintf(stderr, "could not create extractor.\n");
1016 return -1;
1017 }
1018
1019 sp<MetaData> meta = extractor->getMetaData();
1020
1021 if (meta != NULL) {
1022 const char *mime;
1023 if (!meta->findCString(kKeyMIMEType, &mime)) {
1024 fprintf(stderr, "extractor did not provide MIME type.\n");
1025 return -1;
1026 }
1027
1028 if (!strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_MPEG2TS)) {
1029 syncInfoPresent = false;
1030 }
1031 }
1032
1033 size_t numTracks = extractor->countTracks();
1034
1035 if (gWriteMP4) {
1036 bool haveAudio = false;
1037 bool haveVideo = false;
1038 for (size_t i = 0; i < numTracks; ++i) {
1039 sp<IMediaSource> source = extractor->getTrack(i);
1040
1041 const char *mime;
1042 CHECK(source->getFormat()->findCString(
1043 kKeyMIMEType, &mime));
1044
1045 bool useTrack = false;
1046 if (!haveAudio && !strncasecmp("audio/", mime, 6)) {
1047 haveAudio = true;
1048 useTrack = true;
1049 } else if (!haveVideo && !strncasecmp("video/", mime, 6)) {
1050 haveVideo = true;
1051 useTrack = true;
1052 }
1053
1054 if (useTrack) {
1055 mediaSources.push(source);
1056
1057 if (haveAudio && haveVideo) {
1058 break;
1059 }
1060 }
1061 }
1062 } else {
1063 sp<MetaData> meta;
1064 size_t i;
1065 for (i = 0; i < numTracks; ++i) {
1066 meta = extractor->getTrackMetaData(
1067 i, MediaExtractor::kIncludeExtensiveMetaData);
1068
1069 if (meta == NULL) {
1070 break;
1071 }
1072 const char *mime;
1073 meta->findCString(kKeyMIMEType, &mime);
1074
1075 if (audioOnly && !strncasecmp(mime, "audio/", 6)) {
1076 break;
1077 }
1078
1079 if (!audioOnly && !strncasecmp(mime, "video/", 6)) {
1080 break;
1081 }
1082
1083 meta = NULL;
1084 }
1085
1086 if (meta == NULL) {
1087 fprintf(stderr,
1088 "No suitable %s track found. The '-a' option will "
1089 "target audio tracks only, the default is to target "
1090 "video tracks only.\n",
1091 audioOnly ? "audio" : "video");
1092 return -1;
1093 }
1094
1095 int64_t thumbTimeUs;
1096 if (meta->findInt64(kKeyThumbnailTime, &thumbTimeUs)) {
1097 printf("thumbnailTime: %" PRId64 " us (%.2f secs)\n",
1098 thumbTimeUs, thumbTimeUs / 1E6);
1099 }
1100
1101 mediaSource = extractor->getTrack(i);
1102 }
1103 }
1104
1105 if (gWriteMP4) {
1106 writeSourcesToMP4(mediaSources, syncInfoPresent);
1107 } else if (dumpStream) {
1108 dumpSource(mediaSource, dumpStreamFilename);
1109 } else if (dumpPCMStream) {
1110 sp<IMediaSource> decSource = SimpleDecodingSource::Create(mediaSource);
1111 dumpSource(decSource, dumpStreamFilename);
1112 } else if (seekTest) {
1113 performSeekTest(mediaSource);
1114 } else {
1115 playSource(mediaSource);
1116 }
1117 }
1118
1119 if ((useSurfaceAlloc || useSurfaceTexAlloc) && !audioOnly) {
1120 gSurface.clear();
1121
1122 if (useSurfaceAlloc) {
1123 composerClient->dispose();
1124 }
1125 }
1126
1127 return 0;
1128 }
1129