codec.cpp revision 1bd139a2a68690e80398b70b27ca59550fea0e65
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                    "\t\t[-D] decrypt input buffers\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
64static sp<ICrypto> makeCrypto(
65        const uint8_t uuid[16], const void *data, size_t size) {
66    sp<IServiceManager> sm = defaultServiceManager();
67
68    sp<IBinder> binder =
69        sm->getService(String16("media.player"));
70
71    sp<IMediaPlayerService> service =
72        interface_cast<IMediaPlayerService>(binder);
73
74    CHECK(service != NULL);
75
76    sp<ICrypto> crypto = service->makeCrypto();
77
78    if (crypto == NULL || crypto->initCheck() != OK) {
79        return NULL;
80    }
81
82    status_t err = crypto->createPlugin(uuid, data, size);
83
84    if (err != OK) {
85        return NULL;
86    }
87
88    return crypto;
89}
90
91}  // namespace android
92
93static int decode(
94        const android::sp<android::ALooper> &looper,
95        const char *path,
96        bool useAudio,
97        bool useVideo,
98        const android::sp<android::Surface> &surface,
99        bool decryptInputBuffers) {
100    using namespace android;
101
102    static int64_t kTimeout = 500ll;
103
104    sp<NuMediaExtractor> extractor = new NuMediaExtractor;
105    if (extractor->setDataSource(path) != OK) {
106        fprintf(stderr, "unable to instantiate extractor.\n");
107        return 1;
108    }
109
110    sp<ICrypto> crypto;
111
112    KeyedVector<size_t, CodecState> stateByTrack;
113
114    bool haveAudio = false;
115    bool haveVideo = false;
116    for (size_t i = 0; i < extractor->countTracks(); ++i) {
117        sp<AMessage> format;
118        status_t err = extractor->getTrackFormat(i, &format);
119        CHECK_EQ(err, (status_t)OK);
120
121        AString mime;
122        CHECK(format->findString("mime", &mime));
123
124        bool isAudio = !strncasecmp(mime.c_str(), "audio/", 6);
125        bool isVideo = !strncasecmp(mime.c_str(), "video/", 6);
126
127        if (useAudio && !haveAudio && isAudio) {
128            haveAudio = true;
129        } else if (useVideo && !haveVideo && isVideo) {
130            haveVideo = true;
131        } else {
132            continue;
133        }
134
135        ALOGV("selecting track %d", i);
136
137        err = extractor->selectTrack(i);
138        CHECK_EQ(err, (status_t)OK);
139
140        CodecState *state =
141            &stateByTrack.editValueAt(stateByTrack.add(i, CodecState()));
142
143        state->mNumBytesDecoded = 0;
144        state->mNumBuffersDecoded = 0;
145        state->mIsAudio = isAudio;
146
147        if (decryptInputBuffers && crypto == NULL) {
148            sp<ABuffer> emm;
149            CHECK(format->findBuffer("emm", &emm));
150
151            sp<ABuffer> ecm;
152            CHECK(format->findBuffer("ecm", &ecm));
153
154            struct WVOpaqueInitData {
155                uint8_t mEMM[16];
156                uint8_t mECM[32];
157
158            } opaque;
159
160            CHECK_EQ(emm->size(), sizeof(opaque.mEMM));
161            memcpy(opaque.mEMM, emm->data(), emm->size());
162
163            CHECK_EQ(ecm->size(), 80u);
164            // bytes 16..47 of the original ecm stream data.
165            memcpy(opaque.mECM, ecm->data() + 16, 32);
166
167            static const uint8_t kUUIDWidevine[16] = {
168                0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
169                0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
170            };
171
172            crypto = makeCrypto(kUUIDWidevine, &opaque, sizeof(opaque));
173            CHECK(crypto != NULL);
174            CHECK_EQ(crypto->initCheck(), (status_t)OK);
175        }
176
177        if (decryptInputBuffers
178                && crypto->requiresSecureDecoderComponent(mime.c_str())) {
179            static const MediaCodecList *list = MediaCodecList::getInstance();
180
181            ssize_t index =
182                list->findCodecByType(mime.c_str(), false /* encoder */);
183
184            CHECK_GE(index, 0);
185
186            const char *componentName = list->getCodecName(index);
187
188            AString fullName = componentName;
189            fullName.append(".secure");
190
191            state->mCodec = MediaCodec::CreateByComponentName(
192                    looper, fullName.c_str());
193        } else {
194            state->mCodec = MediaCodec::CreateByType(
195                    looper, mime.c_str(), false /* encoder */);
196        }
197
198        CHECK(state->mCodec != NULL);
199
200        err = state->mCodec->configure(
201                format, isVideo ? surface : NULL,
202                crypto,
203                0 /* flags */);
204
205        CHECK_EQ(err, (status_t)OK);
206
207        size_t j = 0;
208        sp<ABuffer> buffer;
209        while (format->findBuffer(StringPrintf("csd-%d", j).c_str(), &buffer)) {
210            state->mCSD.push_back(buffer);
211
212            ++j;
213        }
214
215        state->mCSDIndex = 0;
216        state->mSignalledInputEOS = false;
217        state->mSawOutputEOS = false;
218
219        ALOGV("got %d pieces of codec specific data.", state->mCSD.size());
220    }
221
222    CHECK(!stateByTrack.isEmpty());
223
224    int64_t startTimeUs = ALooper::GetNowUs();
225
226    for (size_t i = 0; i < stateByTrack.size(); ++i) {
227        CodecState *state = &stateByTrack.editValueAt(i);
228
229        sp<MediaCodec> codec = state->mCodec;
230
231        CHECK_EQ((status_t)OK, codec->start());
232
233        CHECK_EQ((status_t)OK, codec->getInputBuffers(&state->mInBuffers));
234        CHECK_EQ((status_t)OK, codec->getOutputBuffers(&state->mOutBuffers));
235
236        ALOGV("got %d input and %d output buffers",
237              state->mInBuffers.size(), state->mOutBuffers.size());
238
239        while (state->mCSDIndex < state->mCSD.size()) {
240            size_t index;
241            status_t err = codec->dequeueInputBuffer(&index, -1ll);
242            CHECK_EQ(err, (status_t)OK);
243
244            const sp<ABuffer> &srcBuffer =
245                state->mCSD.itemAt(state->mCSDIndex++);
246
247            const sp<ABuffer> &buffer = state->mInBuffers.itemAt(index);
248
249            memcpy(buffer->data(), srcBuffer->data(), srcBuffer->size());
250
251            err = codec->queueInputBuffer(
252                    index,
253                    0 /* offset */,
254                    srcBuffer->size(),
255                    0ll /* timeUs */,
256                    MediaCodec::BUFFER_FLAG_CODECCONFIG);
257
258            CHECK_EQ(err, (status_t)OK);
259        }
260    }
261
262    bool sawInputEOS = false;
263
264    for (;;) {
265        if (!sawInputEOS) {
266            size_t trackIndex;
267            status_t err = extractor->getSampleTrackIndex(&trackIndex);
268
269            if (err != OK) {
270                ALOGV("saw input eos");
271                sawInputEOS = true;
272            } else {
273                CodecState *state = &stateByTrack.editValueFor(trackIndex);
274
275                size_t index;
276                err = state->mCodec->dequeueInputBuffer(&index, kTimeout);
277
278                if (err == OK) {
279                    ALOGV("filling input buffer %d", index);
280
281                    const sp<ABuffer> &buffer = state->mInBuffers.itemAt(index);
282
283                    err = extractor->readSampleData(buffer);
284                    CHECK_EQ(err, (status_t)OK);
285
286                    int64_t timeUs;
287                    err = extractor->getSampleTime(&timeUs);
288                    CHECK_EQ(err, (status_t)OK);
289
290                    uint32_t bufferFlags = 0;
291
292                    uint32_t sampleFlags;
293                    err = extractor->getSampleFlags(&sampleFlags);
294                    CHECK_EQ(err, (status_t)OK);
295
296                    if (sampleFlags & NuMediaExtractor::SAMPLE_FLAG_ENCRYPTED) {
297                        CHECK(decryptInputBuffers);
298
299                        bufferFlags |= MediaCodec::BUFFER_FLAG_ENCRYPTED;
300                    }
301
302                    err = state->mCodec->queueInputBuffer(
303                            index,
304                            0 /* offset */,
305                            buffer->size(),
306                            timeUs,
307                            bufferFlags);
308
309                    CHECK_EQ(err, (status_t)OK);
310
311                    extractor->advance();
312                } else {
313                    CHECK_EQ(err, -EAGAIN);
314                }
315            }
316        } else {
317            for (size_t i = 0; i < stateByTrack.size(); ++i) {
318                CodecState *state = &stateByTrack.editValueAt(i);
319
320                if (!state->mSignalledInputEOS) {
321                    size_t index;
322                    status_t err =
323                        state->mCodec->dequeueInputBuffer(&index, kTimeout);
324
325                    if (err == OK) {
326                        ALOGV("signalling input EOS on track %d", i);
327
328                        err = state->mCodec->queueInputBuffer(
329                                index,
330                                0 /* offset */,
331                                0 /* size */,
332                                0ll /* timeUs */,
333                                MediaCodec::BUFFER_FLAG_EOS);
334
335                        CHECK_EQ(err, (status_t)OK);
336
337                        state->mSignalledInputEOS = true;
338                    } else {
339                        CHECK_EQ(err, -EAGAIN);
340                    }
341                }
342            }
343        }
344
345        bool sawOutputEOSOnAllTracks = true;
346        for (size_t i = 0; i < stateByTrack.size(); ++i) {
347            CodecState *state = &stateByTrack.editValueAt(i);
348            if (!state->mSawOutputEOS) {
349                sawOutputEOSOnAllTracks = false;
350                break;
351            }
352        }
353
354        if (sawOutputEOSOnAllTracks) {
355            break;
356        }
357
358        for (size_t i = 0; i < stateByTrack.size(); ++i) {
359            CodecState *state = &stateByTrack.editValueAt(i);
360
361            if (state->mSawOutputEOS) {
362                continue;
363            }
364
365            size_t index;
366            size_t offset;
367            size_t size;
368            int64_t presentationTimeUs;
369            uint32_t flags;
370            status_t err = state->mCodec->dequeueOutputBuffer(
371                    &index, &offset, &size, &presentationTimeUs, &flags,
372                    kTimeout);
373
374            if (err == OK) {
375                ALOGV("draining output buffer %d, time = %lld us",
376                      index, presentationTimeUs);
377
378                ++state->mNumBuffersDecoded;
379                state->mNumBytesDecoded += size;
380
381                err = state->mCodec->releaseOutputBuffer(index);
382                CHECK_EQ(err, (status_t)OK);
383
384                if (flags & MediaCodec::BUFFER_FLAG_EOS) {
385                    ALOGV("reached EOS on output.");
386
387                    state->mSawOutputEOS = true;
388                }
389            } else if (err == INFO_OUTPUT_BUFFERS_CHANGED) {
390                ALOGV("INFO_OUTPUT_BUFFERS_CHANGED");
391                CHECK_EQ((status_t)OK,
392                         state->mCodec->getOutputBuffers(&state->mOutBuffers));
393
394                ALOGV("got %d output buffers", state->mOutBuffers.size());
395            } else if (err == INFO_FORMAT_CHANGED) {
396                sp<AMessage> format;
397                CHECK_EQ((status_t)OK, state->mCodec->getOutputFormat(&format));
398
399                ALOGV("INFO_FORMAT_CHANGED: %s", format->debugString().c_str());
400            } else {
401                CHECK_EQ(err, -EAGAIN);
402            }
403        }
404    }
405
406    int64_t elapsedTimeUs = ALooper::GetNowUs() - startTimeUs;
407
408    for (size_t i = 0; i < stateByTrack.size(); ++i) {
409        CodecState *state = &stateByTrack.editValueAt(i);
410
411        CHECK_EQ((status_t)OK, state->mCodec->release());
412
413        if (state->mIsAudio) {
414            printf("track %d: %lld bytes received. %.2f KB/sec\n",
415                   i,
416                   state->mNumBytesDecoded,
417                   state->mNumBytesDecoded * 1E6 / 1024 / elapsedTimeUs);
418        } else {
419            printf("track %d: %lld frames decoded, %.2f fps. %lld bytes "
420                   "received. %.2f KB/sec\n",
421                   i,
422                   state->mNumBuffersDecoded,
423                   state->mNumBuffersDecoded * 1E6 / elapsedTimeUs,
424                   state->mNumBytesDecoded,
425                   state->mNumBytesDecoded * 1E6 / 1024 / elapsedTimeUs);
426        }
427    }
428
429    return 0;
430}
431
432int main(int argc, char **argv) {
433    using namespace android;
434
435    const char *me = argv[0];
436
437    bool useAudio = false;
438    bool useVideo = false;
439    bool playback = false;
440    bool useSurface = false;
441    bool decryptInputBuffers = false;
442
443    int res;
444    while ((res = getopt(argc, argv, "havpSD")) >= 0) {
445        switch (res) {
446            case 'a':
447            {
448                useAudio = true;
449                break;
450            }
451
452            case 'v':
453            {
454                useVideo = true;
455                break;
456            }
457
458            case 'p':
459            {
460                playback = true;
461                break;
462            }
463
464            case 'S':
465            {
466                useSurface = true;
467                break;
468            }
469
470            case 'D':
471            {
472                decryptInputBuffers = true;
473                break;
474            }
475
476            case '?':
477            case 'h':
478            default:
479            {
480                usage(me);
481            }
482        }
483    }
484
485    argc -= optind;
486    argv += optind;
487
488    if (argc != 1) {
489        usage(me);
490    }
491
492    if (!useAudio && !useVideo) {
493        useAudio = useVideo = true;
494    }
495
496    ProcessState::self()->startThreadPool();
497
498    DataSource::RegisterDefaultSniffers();
499
500    sp<ALooper> looper = new ALooper;
501    looper->start();
502
503    sp<SurfaceComposerClient> composerClient;
504    sp<SurfaceControl> control;
505    sp<Surface> surface;
506
507    if (playback || (useSurface && useVideo)) {
508        composerClient = new SurfaceComposerClient;
509        CHECK_EQ(composerClient->initCheck(), (status_t)OK);
510
511        ssize_t displayWidth = composerClient->getDisplayWidth(0);
512        ssize_t displayHeight = composerClient->getDisplayHeight(0);
513
514        ALOGV("display is %ld x %ld\n", displayWidth, displayHeight);
515
516        control = composerClient->createSurface(
517                String8("A Surface"),
518                0,
519                displayWidth,
520                displayHeight,
521                PIXEL_FORMAT_RGB_565,
522                0);
523
524        CHECK(control != NULL);
525        CHECK(control->isValid());
526
527        SurfaceComposerClient::openGlobalTransaction();
528        CHECK_EQ(control->setLayer(INT_MAX), (status_t)OK);
529        CHECK_EQ(control->show(), (status_t)OK);
530        SurfaceComposerClient::closeGlobalTransaction();
531
532        surface = control->getSurface();
533        CHECK(surface != NULL);
534    }
535
536    if (playback) {
537        sp<SimplePlayer> player = new SimplePlayer;
538        looper->registerHandler(player);
539
540        player->setDataSource(argv[0]);
541        player->setSurface(surface->getSurfaceTexture());
542        player->start();
543        sleep(60);
544        player->stop();
545        player->reset();
546    } else {
547        decode(looper, argv[0],
548               useAudio, useVideo, surface, decryptInputBuffers);
549    }
550
551    if (playback || (useSurface && useVideo)) {
552        composerClient->dispose();
553    }
554
555    looper->stop();
556
557    return 0;
558}
559