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 
usage(const char * me)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 
decode(const android::sp<android::ALooper> & looper,const char * path,bool useAudio,bool useVideo,const android::sp<android::Surface> & surface)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 
main(int argc,char ** argv)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