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