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