1/*
2 * Copyright (C) 2012 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17//#define LOG_NDEBUG 0
18#define LOG_TAG "codec"
19#include <inttypes.h>
20#include <utils/Log.h>
21
22#include "SimplePlayer.h"
23
24#include <binder/IServiceManager.h>
25#include <binder/ProcessState.h>
26#include <media/ICrypto.h>
27#include <media/IMediaHTTPService.h>
28#include <media/IMediaPlayerService.h>
29#include <media/stagefright/foundation/ABuffer.h>
30#include <media/stagefright/foundation/ADebug.h>
31#include <media/stagefright/foundation/ALooper.h>
32#include <media/stagefright/foundation/AMessage.h>
33#include <media/stagefright/foundation/AString.h>
34#include <media/stagefright/DataSource.h>
35#include <media/stagefright/MediaCodec.h>
36#include <media/stagefright/MediaCodecList.h>
37#include <media/stagefright/MediaDefs.h>
38#include <media/stagefright/NuMediaExtractor.h>
39#include <gui/ISurfaceComposer.h>
40#include <gui/SurfaceComposerClient.h>
41#include <gui/Surface.h>
42#include <ui/DisplayInfo.h>
43
44static void usage(const char *me) {
45    fprintf(stderr, "usage: %s [-a] use audio\n"
46                    "\t\t[-v] use video\n"
47                    "\t\t[-p] playback\n"
48                    "\t\t[-S] allocate buffers from a surface\n",
49                    me);
50
51    exit(1);
52}
53
54namespace android {
55
56struct CodecState {
57    sp<MediaCodec> mCodec;
58    Vector<sp<ABuffer> > mInBuffers;
59    Vector<sp<ABuffer> > mOutBuffers;
60    bool mSignalledInputEOS;
61    bool mSawOutputEOS;
62    int64_t mNumBuffersDecoded;
63    int64_t mNumBytesDecoded;
64    bool mIsAudio;
65};
66
67}  // namespace android
68
69static int decode(
70        const android::sp<android::ALooper> &looper,
71        const char *path,
72        bool useAudio,
73        bool useVideo,
74        const android::sp<android::Surface> &surface) {
75    using namespace android;
76
77    static int64_t kTimeout = 500ll;
78
79    sp<NuMediaExtractor> extractor = new NuMediaExtractor;
80    if (extractor->setDataSource(NULL /* httpService */, path) != OK) {
81        fprintf(stderr, "unable to instantiate extractor.\n");
82        return 1;
83    }
84
85    KeyedVector<size_t, CodecState> stateByTrack;
86
87    bool haveAudio = false;
88    bool haveVideo = false;
89    for (size_t i = 0; i < extractor->countTracks(); ++i) {
90        sp<AMessage> format;
91        status_t err = extractor->getTrackFormat(i, &format);
92        CHECK_EQ(err, (status_t)OK);
93
94        AString mime;
95        CHECK(format->findString("mime", &mime));
96
97        bool isAudio = !strncasecmp(mime.c_str(), "audio/", 6);
98        bool isVideo = !strncasecmp(mime.c_str(), "video/", 6);
99
100        if (useAudio && !haveAudio && isAudio) {
101            haveAudio = true;
102        } else if (useVideo && !haveVideo && isVideo) {
103            haveVideo = true;
104        } else {
105            continue;
106        }
107
108        ALOGV("selecting track %d", i);
109
110        err = extractor->selectTrack(i);
111        CHECK_EQ(err, (status_t)OK);
112
113        CodecState *state =
114            &stateByTrack.editValueAt(stateByTrack.add(i, CodecState()));
115
116        state->mNumBytesDecoded = 0;
117        state->mNumBuffersDecoded = 0;
118        state->mIsAudio = isAudio;
119
120        state->mCodec = MediaCodec::CreateByType(
121                looper, mime.c_str(), false /* encoder */);
122
123        CHECK(state->mCodec != NULL);
124
125        err = state->mCodec->configure(
126                format, isVideo ? surface : NULL,
127                NULL /* crypto */,
128                0 /* flags */);
129
130        CHECK_EQ(err, (status_t)OK);
131
132        state->mSignalledInputEOS = false;
133        state->mSawOutputEOS = false;
134    }
135
136    CHECK(!stateByTrack.isEmpty());
137
138    int64_t startTimeUs = ALooper::GetNowUs();
139
140    for (size_t i = 0; i < stateByTrack.size(); ++i) {
141        CodecState *state = &stateByTrack.editValueAt(i);
142
143        sp<MediaCodec> codec = state->mCodec;
144
145        CHECK_EQ((status_t)OK, codec->start());
146
147        CHECK_EQ((status_t)OK, codec->getInputBuffers(&state->mInBuffers));
148        CHECK_EQ((status_t)OK, codec->getOutputBuffers(&state->mOutBuffers));
149
150        ALOGV("got %d input and %d output buffers",
151              state->mInBuffers.size(), state->mOutBuffers.size());
152    }
153
154    bool sawInputEOS = false;
155
156    for (;;) {
157        if (!sawInputEOS) {
158            size_t trackIndex;
159            status_t err = extractor->getSampleTrackIndex(&trackIndex);
160
161            if (err != OK) {
162                ALOGV("saw input eos");
163                sawInputEOS = true;
164            } else {
165                CodecState *state = &stateByTrack.editValueFor(trackIndex);
166
167                size_t index;
168                err = state->mCodec->dequeueInputBuffer(&index, kTimeout);
169
170                if (err == OK) {
171                    ALOGV("filling input buffer %d", index);
172
173                    const sp<ABuffer> &buffer = state->mInBuffers.itemAt(index);
174
175                    err = extractor->readSampleData(buffer);
176                    CHECK_EQ(err, (status_t)OK);
177
178                    int64_t timeUs;
179                    err = extractor->getSampleTime(&timeUs);
180                    CHECK_EQ(err, (status_t)OK);
181
182                    uint32_t bufferFlags = 0;
183
184                    err = state->mCodec->queueInputBuffer(
185                            index,
186                            0 /* offset */,
187                            buffer->size(),
188                            timeUs,
189                            bufferFlags);
190
191                    CHECK_EQ(err, (status_t)OK);
192
193                    extractor->advance();
194                } else {
195                    CHECK_EQ(err, -EAGAIN);
196                }
197            }
198        } else {
199            for (size_t i = 0; i < stateByTrack.size(); ++i) {
200                CodecState *state = &stateByTrack.editValueAt(i);
201
202                if (!state->mSignalledInputEOS) {
203                    size_t index;
204                    status_t err =
205                        state->mCodec->dequeueInputBuffer(&index, kTimeout);
206
207                    if (err == OK) {
208                        ALOGV("signalling input EOS on track %d", i);
209
210                        err = state->mCodec->queueInputBuffer(
211                                index,
212                                0 /* offset */,
213                                0 /* size */,
214                                0ll /* timeUs */,
215                                MediaCodec::BUFFER_FLAG_EOS);
216
217                        CHECK_EQ(err, (status_t)OK);
218
219                        state->mSignalledInputEOS = true;
220                    } else {
221                        CHECK_EQ(err, -EAGAIN);
222                    }
223                }
224            }
225        }
226
227        bool sawOutputEOSOnAllTracks = true;
228        for (size_t i = 0; i < stateByTrack.size(); ++i) {
229            CodecState *state = &stateByTrack.editValueAt(i);
230            if (!state->mSawOutputEOS) {
231                sawOutputEOSOnAllTracks = false;
232                break;
233            }
234        }
235
236        if (sawOutputEOSOnAllTracks) {
237            break;
238        }
239
240        for (size_t i = 0; i < stateByTrack.size(); ++i) {
241            CodecState *state = &stateByTrack.editValueAt(i);
242
243            if (state->mSawOutputEOS) {
244                continue;
245            }
246
247            size_t index;
248            size_t offset;
249            size_t size;
250            int64_t presentationTimeUs;
251            uint32_t flags;
252            status_t err = state->mCodec->dequeueOutputBuffer(
253                    &index, &offset, &size, &presentationTimeUs, &flags,
254                    kTimeout);
255
256            if (err == OK) {
257                ALOGV("draining output buffer %d, time = %lld us",
258                      index, presentationTimeUs);
259
260                ++state->mNumBuffersDecoded;
261                state->mNumBytesDecoded += size;
262
263                err = state->mCodec->releaseOutputBuffer(index);
264                CHECK_EQ(err, (status_t)OK);
265
266                if (flags & MediaCodec::BUFFER_FLAG_EOS) {
267                    ALOGV("reached EOS on output.");
268
269                    state->mSawOutputEOS = true;
270                }
271            } else if (err == INFO_OUTPUT_BUFFERS_CHANGED) {
272                ALOGV("INFO_OUTPUT_BUFFERS_CHANGED");
273                CHECK_EQ((status_t)OK,
274                         state->mCodec->getOutputBuffers(&state->mOutBuffers));
275
276                ALOGV("got %d output buffers", state->mOutBuffers.size());
277            } else if (err == INFO_FORMAT_CHANGED) {
278                sp<AMessage> format;
279                CHECK_EQ((status_t)OK, state->mCodec->getOutputFormat(&format));
280
281                ALOGV("INFO_FORMAT_CHANGED: %s", format->debugString().c_str());
282            } else {
283                CHECK_EQ(err, -EAGAIN);
284            }
285        }
286    }
287
288    int64_t elapsedTimeUs = ALooper::GetNowUs() - startTimeUs;
289
290    for (size_t i = 0; i < stateByTrack.size(); ++i) {
291        CodecState *state = &stateByTrack.editValueAt(i);
292
293        CHECK_EQ((status_t)OK, state->mCodec->release());
294
295        if (state->mIsAudio) {
296            printf("track %zu: %" PRId64 " bytes received. %.2f KB/sec\n",
297                   i,
298                   state->mNumBytesDecoded,
299                   state->mNumBytesDecoded * 1E6 / 1024 / elapsedTimeUs);
300        } else {
301            printf("track %zu: %" PRId64 " frames decoded, %.2f fps. %" PRId64
302                    " bytes received. %.2f KB/sec\n",
303                   i,
304                   state->mNumBuffersDecoded,
305                   state->mNumBuffersDecoded * 1E6 / elapsedTimeUs,
306                   state->mNumBytesDecoded,
307                   state->mNumBytesDecoded * 1E6 / 1024 / elapsedTimeUs);
308        }
309    }
310
311    return 0;
312}
313
314int main(int argc, char **argv) {
315    using namespace android;
316
317    const char *me = argv[0];
318
319    bool useAudio = false;
320    bool useVideo = false;
321    bool playback = false;
322    bool useSurface = false;
323
324    int res;
325    while ((res = getopt(argc, argv, "havpSD")) >= 0) {
326        switch (res) {
327            case 'a':
328            {
329                useAudio = true;
330                break;
331            }
332
333            case 'v':
334            {
335                useVideo = true;
336                break;
337            }
338
339            case 'p':
340            {
341                playback = true;
342                break;
343            }
344
345            case 'S':
346            {
347                useSurface = true;
348                break;
349            }
350
351            case '?':
352            case 'h':
353            default:
354            {
355                usage(me);
356            }
357        }
358    }
359
360    argc -= optind;
361    argv += optind;
362
363    if (argc != 1) {
364        usage(me);
365    }
366
367    if (!useAudio && !useVideo) {
368        useAudio = useVideo = true;
369    }
370
371    ProcessState::self()->startThreadPool();
372
373    DataSource::RegisterDefaultSniffers();
374
375    sp<ALooper> looper = new ALooper;
376    looper->start();
377
378    sp<SurfaceComposerClient> composerClient;
379    sp<SurfaceControl> control;
380    sp<Surface> surface;
381
382    if (playback || (useSurface && useVideo)) {
383        composerClient = new SurfaceComposerClient;
384        CHECK_EQ(composerClient->initCheck(), (status_t)OK);
385
386        sp<IBinder> display(SurfaceComposerClient::getBuiltInDisplay(
387                ISurfaceComposer::eDisplayIdMain));
388        DisplayInfo info;
389        SurfaceComposerClient::getDisplayInfo(display, &info);
390        ssize_t displayWidth = info.w;
391        ssize_t displayHeight = info.h;
392
393        ALOGV("display is %ld x %ld\n", displayWidth, displayHeight);
394
395        control = composerClient->createSurface(
396                String8("A Surface"),
397                displayWidth,
398                displayHeight,
399                PIXEL_FORMAT_RGB_565,
400                0);
401
402        CHECK(control != NULL);
403        CHECK(control->isValid());
404
405        SurfaceComposerClient::openGlobalTransaction();
406        CHECK_EQ(control->setLayer(INT_MAX), (status_t)OK);
407        CHECK_EQ(control->show(), (status_t)OK);
408        SurfaceComposerClient::closeGlobalTransaction();
409
410        surface = control->getSurface();
411        CHECK(surface != NULL);
412    }
413
414    if (playback) {
415        sp<SimplePlayer> player = new SimplePlayer;
416        looper->registerHandler(player);
417
418        player->setDataSource(argv[0]);
419        player->setSurface(surface->getIGraphicBufferProducer());
420        player->start();
421        sleep(60);
422        player->stop();
423        player->reset();
424    } else {
425        decode(looper, argv[0], useAudio, useVideo, surface);
426    }
427
428    if (playback || (useSurface && useVideo)) {
429        composerClient->dispose();
430    }
431
432    looper->stop();
433
434    return 0;
435}
436