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