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