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