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