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 <datasource/DataSourceFactory.h>
35 #include <media/DataSource.h>
36 #include <media/MediaSource.h>
37 #include <media/IMediaHTTPService.h>
38 #include <media/IMediaPlayerService.h>
39 #include <media/stagefright/foundation/ABuffer.h>
40 #include <media/stagefright/foundation/ALooper.h>
41 #include <media/stagefright/foundation/AMessage.h>
42 #include <media/stagefright/foundation/AUtils.h>
43 #include <media/stagefright/JPEGSource.h>
44 #include <media/stagefright/InterfaceUtils.h>
45 #include <media/stagefright/MediaCodec.h>
46 #include <media/stagefright/MediaCodecConstants.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/MediaExtractorFactory.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 #include <android/hardware/media/omx/1.0/IOmx.h>
68
69 #include "AudioPlayer.h"
70
71 using namespace android;
72
73 static long gNumRepetitions;
74 static long gMaxNumFrames; // 0 means decode all available.
75 static long gReproduceBug; // if not -1.
76 static bool gPreferSoftwareCodec;
77 static bool gForceToUseHardwareCodec;
78 static bool gPlaybackAudio;
79 static bool gWriteMP4;
80 static bool gDisplayHistogram;
81 static bool gVerbose = false;
82 static bool showProgress = true;
83 static String8 gWriteMP4Filename;
84 static String8 gComponentNameOverride;
85
86 static sp<ANativeWindow> gSurface;
87
getNowUs()88 static int64_t getNowUs() {
89 struct timeval tv;
90 gettimeofday(&tv, NULL);
91
92 return (int64_t)tv.tv_usec + tv.tv_sec * 1000000ll;
93 }
94
CompareIncreasing(const int64_t * a,const int64_t * b)95 static int CompareIncreasing(const int64_t *a, const int64_t *b) {
96 return (*a) < (*b) ? -1 : (*a) > (*b) ? 1 : 0;
97 }
98
displayDecodeHistogram(Vector<int64_t> * decodeTimesUs)99 static void displayDecodeHistogram(Vector<int64_t> *decodeTimesUs) {
100 printf("decode times:\n");
101
102 decodeTimesUs->sort(CompareIncreasing);
103
104 size_t n = decodeTimesUs->size();
105 int64_t minUs = decodeTimesUs->itemAt(0);
106 int64_t maxUs = decodeTimesUs->itemAt(n - 1);
107
108 printf("min decode time %" PRId64 " us (%.2f secs)\n", minUs, minUs / 1E6);
109 printf("max decode time %" PRId64 " us (%.2f secs)\n", maxUs, maxUs / 1E6);
110
111 size_t counts[100];
112 for (size_t i = 0; i < 100; ++i) {
113 counts[i] = 0;
114 }
115
116 for (size_t i = 0; i < n; ++i) {
117 int64_t x = decodeTimesUs->itemAt(i);
118
119 size_t slot = ((x - minUs) * 100) / (maxUs - minUs);
120 if (slot == 100) { slot = 99; }
121
122 ++counts[slot];
123 }
124
125 for (size_t i = 0; i < 100; ++i) {
126 int64_t slotUs = minUs + (i * (maxUs - minUs) / 100);
127
128 double fps = 1E6 / slotUs;
129 printf("[%.2f fps]: %zu\n", fps, counts[i]);
130 }
131 }
132
displayAVCProfileLevelIfPossible(const sp<MetaData> & meta)133 static void displayAVCProfileLevelIfPossible(const sp<MetaData>& meta) {
134 uint32_t type;
135 const void *data;
136 size_t size;
137 if (meta->findData(kKeyAVCC, &type, &data, &size)) {
138 const uint8_t *ptr = (const uint8_t *)data;
139 CHECK(size >= 7);
140 CHECK(ptr[0] == 1); // configurationVersion == 1
141 uint8_t profile = ptr[1];
142 uint8_t level = ptr[3];
143 fprintf(stderr, "AVC video profile %d and level %d\n", profile, level);
144 }
145 }
146
dumpSource(const sp<MediaSource> & source,const String8 & filename)147 static void dumpSource(const sp<MediaSource> &source, const String8 &filename) {
148 FILE *out = fopen(filename.string(), "wb");
149
150 CHECK_EQ((status_t)OK, source->start());
151
152 status_t err;
153 for (;;) {
154 MediaBufferBase *mbuf;
155 err = source->read(&mbuf);
156
157 if (err == INFO_FORMAT_CHANGED) {
158 continue;
159 } else if (err != OK) {
160 break;
161 }
162
163 if (gVerbose) {
164 MetaDataBase &meta = mbuf->meta_data();
165 fprintf(stdout, "sample format: %s\n", meta.toString().c_str());
166 }
167
168 CHECK_EQ(
169 fwrite((const uint8_t *)mbuf->data() + mbuf->range_offset(),
170 1,
171 mbuf->range_length(),
172 out),
173 mbuf->range_length());
174
175 mbuf->release();
176 mbuf = NULL;
177 }
178
179 CHECK_EQ((status_t)OK, source->stop());
180
181 fclose(out);
182 out = NULL;
183 }
184
playSource(sp<MediaSource> & source)185 static void playSource(sp<MediaSource> &source) {
186 sp<MetaData> meta = source->getFormat();
187
188 const char *mime;
189 CHECK(meta->findCString(kKeyMIMEType, &mime));
190
191 sp<MediaSource> rawSource;
192 if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_RAW, mime)) {
193 rawSource = source;
194 } else {
195 int flags = 0;
196 if (gPreferSoftwareCodec) {
197 flags |= MediaCodecList::kPreferSoftwareCodecs;
198 }
199 if (gForceToUseHardwareCodec) {
200 CHECK(!gPreferSoftwareCodec);
201 flags |= MediaCodecList::kHardwareCodecsOnly;
202 }
203 rawSource = SimpleDecodingSource::Create(
204 source, flags, gSurface,
205 gComponentNameOverride.isEmpty() ? nullptr : gComponentNameOverride.c_str(),
206 !gComponentNameOverride.isEmpty());
207 if (rawSource == NULL) {
208 return;
209 }
210 displayAVCProfileLevelIfPossible(meta);
211 }
212
213 source.clear();
214
215 status_t err = rawSource->start();
216
217 if (err != OK) {
218 fprintf(stderr, "rawSource returned error %d (0x%08x)\n", err, err);
219 return;
220 }
221
222 if (gPlaybackAudio) {
223 AudioPlayer *player = new AudioPlayer(NULL);
224 player->setSource(rawSource);
225 rawSource.clear();
226
227 err = player->start(true /* sourceAlreadyStarted */);
228
229 if (err == OK) {
230 status_t finalStatus;
231 while (!player->reachedEOS(&finalStatus)) {
232 usleep(100000ll);
233 }
234 } else {
235 fprintf(stderr, "unable to start playback err=%d (0x%08x)\n", err, err);
236 }
237
238 delete player;
239 player = NULL;
240
241 return;
242 } else if (gReproduceBug >= 3 && gReproduceBug <= 5) {
243 int64_t durationUs;
244 CHECK(meta->findInt64(kKeyDuration, &durationUs));
245
246 status_t err;
247 MediaBufferBase *buffer;
248 MediaSource::ReadOptions options;
249 int64_t seekTimeUs = -1;
250 for (;;) {
251 err = rawSource->read(&buffer, &options);
252 options.clearSeekTo();
253
254 bool shouldSeek = false;
255 if (err == INFO_FORMAT_CHANGED) {
256 CHECK(buffer == NULL);
257
258 printf("format changed.\n");
259 continue;
260 } else if (err != OK) {
261 printf("reached EOF.\n");
262
263 shouldSeek = true;
264 } else {
265 int64_t timestampUs;
266 CHECK(buffer->meta_data().findInt64(kKeyTime, ×tampUs));
267
268 bool failed = false;
269
270 if (seekTimeUs >= 0) {
271 int64_t diff = timestampUs - seekTimeUs;
272
273 if (diff < 0) {
274 diff = -diff;
275 }
276
277 if ((gReproduceBug == 4 && diff > 500000)
278 || (gReproduceBug == 5 && timestampUs < 0)) {
279 printf("wanted: %.2f secs, got: %.2f secs\n",
280 seekTimeUs / 1E6, timestampUs / 1E6);
281
282 printf("ERROR: ");
283 failed = true;
284 }
285 }
286
287 printf("buffer has timestamp %" PRId64 " us (%.2f secs)\n",
288 timestampUs, timestampUs / 1E6);
289
290 buffer->release();
291 buffer = NULL;
292
293 if (failed) {
294 break;
295 }
296
297 shouldSeek = ((double)rand() / RAND_MAX) < 0.1;
298
299 if (gReproduceBug == 3) {
300 shouldSeek = false;
301 }
302 }
303
304 seekTimeUs = -1;
305
306 if (shouldSeek) {
307 seekTimeUs = (rand() * (float)durationUs) / (float)RAND_MAX;
308 options.setSeekTo(seekTimeUs);
309
310 printf("seeking to %" PRId64 " us (%.2f secs)\n",
311 seekTimeUs, seekTimeUs / 1E6);
312 }
313 }
314
315 rawSource->stop();
316
317 return;
318 }
319
320 int n = 0;
321 int64_t startTime = getNowUs();
322
323 long numIterationsLeft = gNumRepetitions;
324 MediaSource::ReadOptions options;
325
326 int64_t sumDecodeUs = 0;
327 int64_t totalBytes = 0;
328
329 Vector<int64_t> decodeTimesUs;
330
331 while (numIterationsLeft-- > 0) {
332 long numFrames = 0;
333
334 MediaBufferBase *buffer;
335
336 for (;;) {
337 int64_t startDecodeUs = getNowUs();
338 status_t err = rawSource->read(&buffer, &options);
339 int64_t delayDecodeUs = getNowUs() - startDecodeUs;
340
341 options.clearSeekTo();
342
343 if (err != OK) {
344 CHECK(buffer == NULL);
345
346 if (err == INFO_FORMAT_CHANGED) {
347 printf("format changed.\n");
348 continue;
349 }
350
351 break;
352 }
353
354 if (buffer->range_length() > 0) {
355 if (gDisplayHistogram && n > 0) {
356 // Ignore the first time since it includes some setup
357 // cost.
358 decodeTimesUs.push(delayDecodeUs);
359 }
360
361 if (gVerbose) {
362 MetaDataBase &meta = buffer->meta_data();
363 fprintf(stdout, "%ld sample format: %s\n", numFrames, meta.toString().c_str());
364 } else if (showProgress && (n++ % 16) == 0) {
365 printf(".");
366 fflush(stdout);
367 }
368 }
369
370 sumDecodeUs += delayDecodeUs;
371 totalBytes += buffer->range_length();
372
373 buffer->release();
374 buffer = NULL;
375
376 ++numFrames;
377 if (gMaxNumFrames > 0 && numFrames == gMaxNumFrames) {
378 break;
379 }
380
381 if (gReproduceBug == 1 && numFrames == 40) {
382 printf("seeking past the end now.");
383 options.setSeekTo(0x7fffffffL);
384 } else if (gReproduceBug == 2 && numFrames == 40) {
385 printf("seeking to 5 secs.");
386 options.setSeekTo(5000000);
387 }
388 }
389
390 if (showProgress) {
391 printf("$");
392 fflush(stdout);
393 }
394
395 options.setSeekTo(0);
396 }
397
398 rawSource->stop();
399 printf("\n");
400
401 int64_t delay = getNowUs() - startTime;
402 if (!strncasecmp("video/", mime, 6)) {
403 printf("avg. %.2f fps\n", n * 1E6 / delay);
404
405 printf("avg. time to decode one buffer %.2f usecs\n",
406 (double)sumDecodeUs / n);
407
408 printf("decoded a total of %d frame(s).\n", n);
409
410 if (gDisplayHistogram) {
411 displayDecodeHistogram(&decodeTimesUs);
412 }
413 } else if (!strncasecmp("audio/", mime, 6)) {
414 // Frame count makes less sense for audio, as the output buffer
415 // sizes may be different across decoders.
416 printf("avg. %.2f KB/sec\n", totalBytes / 1024 * 1E6 / delay);
417
418 printf("decoded a total of %" PRId64 " bytes\n", totalBytes);
419 }
420 }
421
422 ////////////////////////////////////////////////////////////////////////////////
423
424 struct DetectSyncSource : public MediaSource {
425 explicit DetectSyncSource(const sp<MediaSource> &source);
426
427 virtual status_t start(MetaData *params = NULL);
428 virtual status_t stop();
429 virtual sp<MetaData> getFormat();
430
431 virtual status_t read(
432 MediaBufferBase **buffer, const ReadOptions *options);
433
434 private:
435 enum StreamType {
436 AVC,
437 MPEG4,
438 H263,
439 OTHER,
440 };
441
442 sp<MediaSource> mSource;
443 StreamType mStreamType;
444 bool mSawFirstIDRFrame;
445
446 DISALLOW_EVIL_CONSTRUCTORS(DetectSyncSource);
447 };
448
DetectSyncSource(const sp<MediaSource> & source)449 DetectSyncSource::DetectSyncSource(const sp<MediaSource> &source)
450 : mSource(source),
451 mStreamType(OTHER),
452 mSawFirstIDRFrame(false) {
453 const char *mime;
454 CHECK(mSource->getFormat()->findCString(kKeyMIMEType, &mime));
455
456 if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AVC)) {
457 mStreamType = AVC;
458 } else if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_MPEG4)) {
459 mStreamType = MPEG4;
460 CHECK(!"sync frame detection not implemented yet for MPEG4");
461 } else if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_H263)) {
462 mStreamType = H263;
463 CHECK(!"sync frame detection not implemented yet for H.263");
464 }
465 }
466
start(MetaData * params)467 status_t DetectSyncSource::start(MetaData *params) {
468 mSawFirstIDRFrame = false;
469
470 return mSource->start(params);
471 }
472
stop()473 status_t DetectSyncSource::stop() {
474 return mSource->stop();
475 }
476
getFormat()477 sp<MetaData> DetectSyncSource::getFormat() {
478 return mSource->getFormat();
479 }
480
isIDRFrame(MediaBufferBase * buffer)481 static bool isIDRFrame(MediaBufferBase *buffer) {
482 const uint8_t *data =
483 (const uint8_t *)buffer->data() + buffer->range_offset();
484 size_t size = buffer->range_length();
485 for (size_t i = 0; i + 3 < size; ++i) {
486 if (!memcmp("\x00\x00\x01", &data[i], 3)) {
487 uint8_t nalType = data[i + 3] & 0x1f;
488 if (nalType == 5) {
489 return true;
490 }
491 }
492 }
493
494 return false;
495 }
496
read(MediaBufferBase ** buffer,const ReadOptions * options)497 status_t DetectSyncSource::read(
498 MediaBufferBase **buffer, const ReadOptions *options) {
499 for (;;) {
500 status_t err = mSource->read(buffer, options);
501
502 if (err != OK) {
503 return err;
504 }
505
506 if (mStreamType == AVC) {
507 bool isIDR = isIDRFrame(*buffer);
508 (*buffer)->meta_data().setInt32(kKeyIsSyncFrame, isIDR);
509 if (isIDR) {
510 mSawFirstIDRFrame = true;
511 }
512 } else {
513 (*buffer)->meta_data().setInt32(kKeyIsSyncFrame, true);
514 }
515
516 if (mStreamType != AVC || mSawFirstIDRFrame) {
517 break;
518 }
519
520 // Ignore everything up to the first IDR frame.
521 (*buffer)->release();
522 *buffer = NULL;
523 }
524
525 return OK;
526 }
527
528 ////////////////////////////////////////////////////////////////////////////////
529
writeSourcesToMP4(Vector<sp<MediaSource>> & sources,bool syncInfoPresent)530 static void writeSourcesToMP4(
531 Vector<sp<MediaSource> > &sources, bool syncInfoPresent) {
532 #if 0
533 sp<MPEG4Writer> writer =
534 new MPEG4Writer(gWriteMP4Filename.string());
535 #else
536 int fd = open(gWriteMP4Filename.string(), O_CREAT | O_LARGEFILE | O_TRUNC | O_RDWR, S_IRUSR | S_IWUSR);
537 if (fd < 0) {
538 fprintf(stderr, "couldn't open file");
539 return;
540 }
541 sp<MPEG2TSWriter> writer =
542 new MPEG2TSWriter(fd);
543 #endif
544
545 // at most one minute.
546 writer->setMaxFileDuration(60000000ll);
547
548 for (size_t i = 0; i < sources.size(); ++i) {
549 sp<MediaSource> source = sources.editItemAt(i);
550
551 CHECK_EQ(writer->addSource(
552 syncInfoPresent ? source : new DetectSyncSource(source)),
553 (status_t)OK);
554 }
555
556 sp<MetaData> params = new MetaData;
557 params->setInt32(kKeyRealTimeRecording, false);
558 CHECK_EQ(writer->start(params.get()), (status_t)OK);
559
560 while (!writer->reachedEOS()) {
561 usleep(100000);
562 }
563 writer->stop();
564 }
565
performSeekTest(const sp<MediaSource> & source)566 static void performSeekTest(const sp<MediaSource> &source) {
567 CHECK_EQ((status_t)OK, source->start());
568
569 int64_t durationUs;
570 CHECK(source->getFormat()->findInt64(kKeyDuration, &durationUs));
571
572 for (int64_t seekTimeUs = 0; seekTimeUs <= durationUs;
573 seekTimeUs += 60000ll) {
574 MediaSource::ReadOptions options;
575 options.setSeekTo(
576 seekTimeUs, MediaSource::ReadOptions::SEEK_PREVIOUS_SYNC);
577
578 MediaBufferBase *buffer;
579 status_t err;
580 for (;;) {
581 err = source->read(&buffer, &options);
582
583 options.clearSeekTo();
584
585 if (err == INFO_FORMAT_CHANGED) {
586 CHECK(buffer == NULL);
587 continue;
588 }
589
590 if (err != OK) {
591 CHECK(buffer == NULL);
592 break;
593 }
594
595 CHECK(buffer != NULL);
596
597 if (buffer->range_length() > 0) {
598 break;
599 }
600
601 buffer->release();
602 buffer = NULL;
603 }
604
605 if (err == OK) {
606 int64_t timeUs;
607 CHECK(buffer->meta_data().findInt64(kKeyTime, &timeUs));
608
609 printf("%" PRId64 "\t%" PRId64 "\t%" PRId64 "\n",
610 seekTimeUs, timeUs, seekTimeUs - timeUs);
611
612 buffer->release();
613 buffer = NULL;
614 } else {
615 printf("ERROR\n");
616 break;
617 }
618 }
619
620 CHECK_EQ((status_t)OK, source->stop());
621 }
622
usage(const char * me)623 static void usage(const char *me) {
624 fprintf(stderr, "usage: %s [options] [input_filename]\n", me);
625 fprintf(stderr, " -h(elp)\n");
626 fprintf(stderr, " -a(udio)\n");
627 fprintf(stderr, " -n repetitions\n");
628 fprintf(stderr, " -l(ist) components\n");
629 fprintf(stderr, " -m max-number-of-frames-to-decode in each pass\n");
630 fprintf(stderr, " -b bug to reproduce\n");
631 fprintf(stderr, " -i(nfo) dump codec info (profiles and color formats supported, details)\n");
632 fprintf(stderr, " -t(humbnail) extract video thumbnail or album art\n");
633 fprintf(stderr, " -s(oftware) prefer software codec\n");
634 fprintf(stderr, " -r(hardware) force to use hardware codec\n");
635 fprintf(stderr, " -o playback audio\n");
636 fprintf(stderr, " -w(rite) filename (write to .mp4 file)\n");
637 fprintf(stderr, " -k seek test\n");
638 fprintf(stderr, " -N(ame) of the component\n");
639 fprintf(stderr, " -x display a histogram of decoding times/fps "
640 "(video only)\n");
641 fprintf(stderr, " -q don't show progress indicator\n");
642 fprintf(stderr, " -S allocate buffers from a surface\n");
643 fprintf(stderr, " -T allocate buffers from a surface texture\n");
644 fprintf(stderr, " -d(ump) output_filename (raw stream data to a file)\n");
645 fprintf(stderr, " -D(ump) output_filename (decoded PCM data to a file)\n");
646 fprintf(stderr, " -v be more verbose\n");
647 }
648
dumpCodecDetails(bool queryDecoders)649 static void dumpCodecDetails(bool queryDecoders) {
650 const char *codecType = queryDecoders? "Decoder" : "Encoder";
651 printf("\n%s infos by media types:\n"
652 "=============================\n", codecType);
653
654 sp<IMediaCodecList> list = MediaCodecList::getInstance();
655 size_t numCodecs = list->countCodecs();
656
657 // gather all media types supported by codec class, and link to codecs that support them
658 KeyedVector<AString, Vector<sp<MediaCodecInfo>>> allMediaTypes;
659 for (size_t codec_ix = 0; codec_ix < numCodecs; ++codec_ix) {
660 sp<MediaCodecInfo> info = list->getCodecInfo(codec_ix);
661 if (info->isEncoder() == !queryDecoders) {
662 Vector<AString> supportedMediaTypes;
663 info->getSupportedMediaTypes(&supportedMediaTypes);
664 if (!supportedMediaTypes.size()) {
665 printf("warning: %s does not support any media types\n",
666 info->getCodecName());
667 } else {
668 for (const AString &mediaType : supportedMediaTypes) {
669 if (allMediaTypes.indexOfKey(mediaType) < 0) {
670 allMediaTypes.add(mediaType, Vector<sp<MediaCodecInfo>>());
671 }
672 allMediaTypes.editValueFor(mediaType).add(info);
673 }
674 }
675 }
676 }
677
678 KeyedVector<AString, bool> visitedCodecs;
679 for (size_t type_ix = 0; type_ix < allMediaTypes.size(); ++type_ix) {
680 const AString &mediaType = allMediaTypes.keyAt(type_ix);
681 printf("\nMedia type '%s':\n", mediaType.c_str());
682
683 for (const sp<MediaCodecInfo> &info : allMediaTypes.valueAt(type_ix)) {
684 sp<MediaCodecInfo::Capabilities> caps = info->getCapabilitiesFor(mediaType.c_str());
685 if (caps == NULL) {
686 printf("warning: %s does not have capabilities for type %s\n",
687 info->getCodecName(), mediaType.c_str());
688 continue;
689 }
690 printf(" %s \"%s\" supports\n",
691 codecType, info->getCodecName());
692
693 auto printList = [](const char *type, const Vector<AString> &values){
694 printf(" %s: [", type);
695 for (size_t j = 0; j < values.size(); ++j) {
696 printf("\n %s%s", values[j].c_str(),
697 j == values.size() - 1 ? " " : ",");
698 }
699 printf("]\n");
700 };
701
702 if (visitedCodecs.indexOfKey(info->getCodecName()) < 0) {
703 visitedCodecs.add(info->getCodecName(), true);
704 {
705 Vector<AString> aliases;
706 info->getAliases(&aliases);
707 // quote alias
708 for (AString &alias : aliases) {
709 alias.insert("\"", 1, 0);
710 alias.append('"');
711 }
712 printList("aliases", aliases);
713 }
714 {
715 uint32_t attrs = info->getAttributes();
716 Vector<AString> list;
717 list.add(AStringPrintf("encoder: %d", !!(attrs & MediaCodecInfo::kFlagIsEncoder)));
718 list.add(AStringPrintf("vendor: %d", !!(attrs & MediaCodecInfo::kFlagIsVendor)));
719 list.add(AStringPrintf("software-only: %d", !!(attrs & MediaCodecInfo::kFlagIsSoftwareOnly)));
720 list.add(AStringPrintf("hw-accelerated: %d", !!(attrs & MediaCodecInfo::kFlagIsHardwareAccelerated)));
721 printList(AStringPrintf("attributes: %#x", attrs).c_str(), list);
722 }
723
724 printf(" owner: \"%s\"\n", info->getOwnerName());
725 printf(" rank: %u\n", info->getRank());
726 } else {
727 printf(" aliases, attributes, owner, rank: see above\n");
728 }
729
730 {
731 Vector<AString> list;
732 Vector<MediaCodecInfo::ProfileLevel> profileLevels;
733 caps->getSupportedProfileLevels(&profileLevels);
734 for (const MediaCodecInfo::ProfileLevel &pl : profileLevels) {
735 const char *niceProfile =
736 mediaType.equalsIgnoreCase(MIMETYPE_AUDIO_AAC) ? asString_AACObject(pl.mProfile) :
737 mediaType.equalsIgnoreCase(MIMETYPE_VIDEO_MPEG2) ? asString_MPEG2Profile(pl.mProfile) :
738 mediaType.equalsIgnoreCase(MIMETYPE_VIDEO_H263) ? asString_H263Profile(pl.mProfile) :
739 mediaType.equalsIgnoreCase(MIMETYPE_VIDEO_MPEG4) ? asString_MPEG4Profile(pl.mProfile) :
740 mediaType.equalsIgnoreCase(MIMETYPE_VIDEO_AVC) ? asString_AVCProfile(pl.mProfile) :
741 mediaType.equalsIgnoreCase(MIMETYPE_VIDEO_VP8) ? asString_VP8Profile(pl.mProfile) :
742 mediaType.equalsIgnoreCase(MIMETYPE_VIDEO_HEVC) ? asString_HEVCProfile(pl.mProfile) :
743 mediaType.equalsIgnoreCase(MIMETYPE_VIDEO_VP9) ? asString_VP9Profile(pl.mProfile) :
744 mediaType.equalsIgnoreCase(MIMETYPE_VIDEO_AV1) ? asString_AV1Profile(pl.mProfile) :"??";
745 const char *niceLevel =
746 mediaType.equalsIgnoreCase(MIMETYPE_VIDEO_MPEG2) ? asString_MPEG2Level(pl.mLevel) :
747 mediaType.equalsIgnoreCase(MIMETYPE_VIDEO_H263) ? asString_H263Level(pl.mLevel) :
748 mediaType.equalsIgnoreCase(MIMETYPE_VIDEO_MPEG4) ? asString_MPEG4Level(pl.mLevel) :
749 mediaType.equalsIgnoreCase(MIMETYPE_VIDEO_AVC) ? asString_AVCLevel(pl.mLevel) :
750 mediaType.equalsIgnoreCase(MIMETYPE_VIDEO_VP8) ? asString_VP8Level(pl.mLevel) :
751 mediaType.equalsIgnoreCase(MIMETYPE_VIDEO_HEVC) ? asString_HEVCTierLevel(pl.mLevel) :
752 mediaType.equalsIgnoreCase(MIMETYPE_VIDEO_VP9) ? asString_VP9Level(pl.mLevel) :
753 mediaType.equalsIgnoreCase(MIMETYPE_VIDEO_AV1) ? asString_AV1Level(pl.mLevel) :
754 "??";
755
756 list.add(AStringPrintf("% 5u/% 5u (%s/%s)",
757 pl.mProfile, pl.mLevel, niceProfile, niceLevel));
758 }
759 printList("profile/levels", list);
760 }
761
762 {
763 Vector<AString> list;
764 Vector<uint32_t> colors;
765 caps->getSupportedColorFormats(&colors);
766 for (uint32_t color : colors) {
767 list.add(AStringPrintf("%#x (%s)", color,
768 asString_ColorFormat((int32_t)color)));
769 }
770 printList("colors", list);
771 }
772
773 printf(" details: %s\n", caps->getDetails()->debugString(6).c_str());
774 }
775 }
776 }
777
main(int argc,char ** argv)778 int main(int argc, char **argv) {
779 android::ProcessState::self()->startThreadPool();
780
781 bool audioOnly = false;
782 bool listComponents = false;
783 bool dumpCodecInfo = false;
784 bool extractThumbnail = false;
785 bool seekTest = false;
786 bool useSurfaceAlloc = false;
787 bool useSurfaceTexAlloc = false;
788 bool dumpStream = false;
789 bool dumpPCMStream = false;
790 String8 dumpStreamFilename;
791 gNumRepetitions = 1;
792 gMaxNumFrames = 0;
793 gReproduceBug = -1;
794 gPreferSoftwareCodec = false;
795 gForceToUseHardwareCodec = false;
796 gPlaybackAudio = false;
797 gWriteMP4 = false;
798 gDisplayHistogram = false;
799
800 sp<android::ALooper> looper;
801
802 int res;
803 while ((res = getopt(argc, argv, "vhaqn:lm:b:itsrow:kN:xSTd:D:")) >= 0) {
804 switch (res) {
805 case 'a':
806 {
807 audioOnly = true;
808 break;
809 }
810
811 case 'q':
812 {
813 showProgress = false;
814 break;
815 }
816
817 case 'd':
818 {
819 dumpStream = true;
820 dumpStreamFilename.setTo(optarg);
821 break;
822 }
823
824 case 'D':
825 {
826 dumpPCMStream = true;
827 audioOnly = true;
828 dumpStreamFilename.setTo(optarg);
829 break;
830 }
831
832 case 'N':
833 {
834 gComponentNameOverride.setTo(optarg);
835 break;
836 }
837
838 case 'l':
839 {
840 listComponents = true;
841 break;
842 }
843
844 case 'm':
845 case 'n':
846 case 'b':
847 {
848 char *end;
849 long x = strtol(optarg, &end, 10);
850
851 if (*end != '\0' || end == optarg || x <= 0) {
852 x = 1;
853 }
854
855 if (res == 'n') {
856 gNumRepetitions = x;
857 } else if (res == 'm') {
858 gMaxNumFrames = x;
859 } else {
860 CHECK_EQ(res, 'b');
861 gReproduceBug = x;
862 }
863 break;
864 }
865
866 case 'w':
867 {
868 gWriteMP4 = true;
869 gWriteMP4Filename.setTo(optarg);
870 break;
871 }
872
873 case 'i':
874 {
875 dumpCodecInfo = true;
876 break;
877 }
878
879 case 't':
880 {
881 extractThumbnail = true;
882 break;
883 }
884
885 case 's':
886 {
887 gPreferSoftwareCodec = true;
888 break;
889 }
890
891 case 'r':
892 {
893 gForceToUseHardwareCodec = true;
894 break;
895 }
896
897 case 'o':
898 {
899 gPlaybackAudio = true;
900 break;
901 }
902
903 case 'k':
904 {
905 seekTest = true;
906 break;
907 }
908
909 case 'x':
910 {
911 gDisplayHistogram = true;
912 break;
913 }
914
915 case 'S':
916 {
917 useSurfaceAlloc = true;
918 break;
919 }
920
921 case 'T':
922 {
923 useSurfaceTexAlloc = true;
924 break;
925 }
926
927 case 'v':
928 {
929 gVerbose = true;
930 break;
931 }
932
933 case '?':
934 case 'h':
935 default:
936 {
937 usage(argv[0]);
938 exit(1);
939 break;
940 }
941 }
942 }
943
944 if (gPlaybackAudio && !audioOnly) {
945 // This doesn't make any sense if we're decoding the video track.
946 gPlaybackAudio = false;
947 }
948
949 argc -= optind;
950 argv += optind;
951
952 if (extractThumbnail) {
953 sp<IServiceManager> sm = defaultServiceManager();
954 sp<IBinder> binder = sm->getService(String16("media.player"));
955 sp<IMediaPlayerService> service =
956 interface_cast<IMediaPlayerService>(binder);
957
958 CHECK(service.get() != NULL);
959
960 sp<IMediaMetadataRetriever> retriever =
961 service->createMetadataRetriever();
962
963 CHECK(retriever != NULL);
964
965 for (int k = 0; k < argc; ++k) {
966 const char *filename = argv[k];
967
968 bool failed = true;
969
970 int fd = open(filename, O_RDONLY | O_LARGEFILE);
971 CHECK_GE(fd, 0);
972
973 off64_t fileSize = lseek64(fd, 0, SEEK_END);
974 CHECK_GE(fileSize, 0ll);
975
976 CHECK_EQ(retriever->setDataSource(fd, 0, fileSize), (status_t)OK);
977
978 close(fd);
979 fd = -1;
980
981 sp<IMemory> mem =
982 retriever->getFrameAtTime(-1,
983 MediaSource::ReadOptions::SEEK_PREVIOUS_SYNC,
984 HAL_PIXEL_FORMAT_RGB_565,
985 false /*metaOnly*/);
986
987 if (mem != NULL) {
988 failed = false;
989 printf("getFrameAtTime(%s) => OK\n", filename);
990
991 VideoFrame *frame = (VideoFrame *)mem->pointer();
992
993 CHECK_EQ(writeJpegFile("/sdcard/out.jpg",
994 frame->getFlattenedData(),
995 frame->mWidth, frame->mHeight), 0);
996 }
997
998 {
999 mem = retriever->extractAlbumArt();
1000
1001 if (mem != NULL) {
1002 failed = false;
1003 printf("extractAlbumArt(%s) => OK\n", filename);
1004 }
1005 }
1006
1007 if (failed) {
1008 printf("both getFrameAtTime and extractAlbumArt "
1009 "failed on file '%s'.\n", filename);
1010 }
1011 }
1012
1013 return 0;
1014 }
1015
1016 if (dumpCodecInfo) {
1017 dumpCodecDetails(true /* queryDecoders */);
1018 dumpCodecDetails(false /* queryDecoders */);
1019 }
1020
1021 if (listComponents) {
1022 using ::android::hardware::hidl_vec;
1023 using ::android::hardware::hidl_string;
1024 using namespace ::android::hardware::media::omx::V1_0;
1025 sp<IOmx> omx = IOmx::getService();
1026 CHECK(omx.get() != nullptr);
1027
1028 hidl_vec<IOmx::ComponentInfo> nodeList;
1029 auto transStatus = omx->listNodes([](
1030 const auto& status, const auto& nodeList) {
1031 CHECK(status == Status::OK);
1032 for (const auto& info : nodeList) {
1033 printf("%s\t Roles: ", info.mName.c_str());
1034 for (const auto& role : info.mRoles) {
1035 printf("%s\t", role.c_str());
1036 }
1037 }
1038 });
1039 CHECK(transStatus.isOk());
1040 }
1041
1042 sp<SurfaceComposerClient> composerClient;
1043 sp<SurfaceControl> control;
1044
1045 if ((useSurfaceAlloc || useSurfaceTexAlloc) && !audioOnly) {
1046 if (useSurfaceAlloc) {
1047 composerClient = new SurfaceComposerClient;
1048 CHECK_EQ(composerClient->initCheck(), (status_t)OK);
1049
1050 control = composerClient->createSurface(
1051 String8("A Surface"),
1052 1280,
1053 800,
1054 PIXEL_FORMAT_RGB_565,
1055 0);
1056
1057 CHECK(control != NULL);
1058 CHECK(control->isValid());
1059
1060 SurfaceComposerClient::Transaction{}
1061 .setLayer(control, INT_MAX)
1062 .show(control)
1063 .apply();
1064
1065 gSurface = control->getSurface();
1066 CHECK(gSurface != NULL);
1067 } else {
1068 CHECK(useSurfaceTexAlloc);
1069
1070 sp<IGraphicBufferProducer> producer;
1071 sp<IGraphicBufferConsumer> consumer;
1072 BufferQueue::createBufferQueue(&producer, &consumer);
1073 sp<GLConsumer> texture = new GLConsumer(consumer, 0 /* tex */,
1074 GLConsumer::TEXTURE_EXTERNAL, true /* useFenceSync */,
1075 false /* isControlledByApp */);
1076 gSurface = new Surface(producer);
1077 }
1078 }
1079
1080 status_t err = OK;
1081
1082 for (int k = 0; k < argc && err == OK; ++k) {
1083 bool syncInfoPresent = true;
1084
1085 const char *filename = argv[k];
1086
1087 sp<DataSource> dataSource =
1088 DataSourceFactory::getInstance()->CreateFromURI(NULL /* httpService */, filename);
1089
1090 if (strncasecmp(filename, "sine:", 5) && dataSource == NULL) {
1091 fprintf(stderr, "Unable to create data source.\n");
1092 return 1;
1093 }
1094
1095 bool isJPEG = false;
1096
1097 size_t len = strlen(filename);
1098 if (len >= 4 && !strcasecmp(filename + len - 4, ".jpg")) {
1099 isJPEG = true;
1100 }
1101
1102 Vector<sp<MediaSource> > mediaSources;
1103 sp<MediaSource> mediaSource;
1104
1105 if (isJPEG) {
1106 mediaSource = new JPEGSource(dataSource);
1107 if (gWriteMP4) {
1108 mediaSources.push(mediaSource);
1109 }
1110 } else if (!strncasecmp("sine:", filename, 5)) {
1111 char *end;
1112 long sampleRate = strtol(filename + 5, &end, 10);
1113
1114 if (end == filename + 5) {
1115 sampleRate = 44100;
1116 }
1117 mediaSource = new SineSource(sampleRate, 1);
1118 if (gWriteMP4) {
1119 mediaSources.push(mediaSource);
1120 }
1121 } else {
1122 sp<IMediaExtractor> extractor = MediaExtractorFactory::Create(dataSource);
1123
1124 if (extractor == NULL) {
1125 fprintf(stderr, "could not create extractor.\n");
1126 return -1;
1127 }
1128
1129 sp<MetaData> meta = extractor->getMetaData();
1130
1131 if (meta != NULL) {
1132 const char *mime;
1133 if (!meta->findCString(kKeyMIMEType, &mime)) {
1134 fprintf(stderr, "extractor did not provide MIME type.\n");
1135 return -1;
1136 }
1137
1138 if (!strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_MPEG2TS)) {
1139 syncInfoPresent = false;
1140 }
1141 }
1142
1143 size_t numTracks = extractor->countTracks();
1144
1145 if (gWriteMP4) {
1146 bool haveAudio = false;
1147 bool haveVideo = false;
1148 for (size_t i = 0; i < numTracks; ++i) {
1149 sp<MediaSource> source = CreateMediaSourceFromIMediaSource(
1150 extractor->getTrack(i));
1151 if (source == nullptr) {
1152 fprintf(stderr, "skip NULL track %zu, track count %zu.\n", i, numTracks);
1153 continue;
1154 }
1155
1156 const char *mime;
1157 CHECK(source->getFormat()->findCString(
1158 kKeyMIMEType, &mime));
1159
1160 bool useTrack = false;
1161 if (!haveAudio && !strncasecmp("audio/", mime, 6)) {
1162 haveAudio = true;
1163 useTrack = true;
1164 } else if (!haveVideo && !strncasecmp("video/", mime, 6)) {
1165 haveVideo = true;
1166 useTrack = true;
1167 }
1168
1169 if (useTrack) {
1170 mediaSources.push(source);
1171
1172 if (haveAudio && haveVideo) {
1173 break;
1174 }
1175 }
1176 }
1177 } else {
1178 sp<MetaData> meta;
1179 size_t i;
1180 for (i = 0; i < numTracks; ++i) {
1181 meta = extractor->getTrackMetaData(
1182 i, MediaExtractor::kIncludeExtensiveMetaData);
1183
1184 if (meta == NULL) {
1185 continue;
1186 }
1187 const char *mime;
1188 meta->findCString(kKeyMIMEType, &mime);
1189
1190 if (audioOnly && !strncasecmp(mime, "audio/", 6)) {
1191 break;
1192 }
1193
1194 if (!audioOnly && !strncasecmp(mime, "video/", 6)) {
1195 break;
1196 }
1197
1198 meta = NULL;
1199 }
1200
1201 if (meta == NULL) {
1202 fprintf(stderr,
1203 "No suitable %s track found. The '-a' option will "
1204 "target audio tracks only, the default is to target "
1205 "video tracks only.\n",
1206 audioOnly ? "audio" : "video");
1207 return -1;
1208 }
1209
1210 int64_t thumbTimeUs;
1211 if (meta->findInt64(kKeyThumbnailTime, &thumbTimeUs)) {
1212 printf("thumbnailTime: %" PRId64 " us (%.2f secs)\n",
1213 thumbTimeUs, thumbTimeUs / 1E6);
1214 }
1215
1216 mediaSource = CreateMediaSourceFromIMediaSource(extractor->getTrack(i));
1217 if (mediaSource == nullptr) {
1218 fprintf(stderr, "skip NULL track %zu, total tracks %zu.\n", i, numTracks);
1219 return -1;
1220 }
1221 }
1222 }
1223
1224 if (gWriteMP4) {
1225 writeSourcesToMP4(mediaSources, syncInfoPresent);
1226 } else if (dumpStream) {
1227 dumpSource(mediaSource, dumpStreamFilename);
1228 } else if (dumpPCMStream) {
1229 sp<MediaSource> decSource = SimpleDecodingSource::Create(mediaSource);
1230 dumpSource(decSource, dumpStreamFilename);
1231 } else if (seekTest) {
1232 performSeekTest(mediaSource);
1233 } else {
1234 playSource(mediaSource);
1235 }
1236 }
1237
1238 if ((useSurfaceAlloc || useSurfaceTexAlloc) && !audioOnly) {
1239 gSurface.clear();
1240
1241 if (useSurfaceAlloc) {
1242 composerClient->dispose();
1243 }
1244 }
1245
1246 return 0;
1247 }
1248