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