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