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