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