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