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