blob: fdfefdfe2669a34899e06fff8c23332eee35ba44 [file] [log] [blame]
Andreas Huber57788222012-02-21 11:47:18 -08001/*
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
Andreas Huber1bd139a2012-04-03 14:19:20 -070023#include <binder/IServiceManager.h>
Andreas Huber57788222012-02-21 11:47:18 -080024#include <binder/ProcessState.h>
Andreas Huber1bd139a2012-04-03 14:19:20 -070025#include <media/ICrypto.h>
26#include <media/IMediaPlayerService.h>
Andreas Huber57788222012-02-21 11:47:18 -080027#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>
Andreas Huber5b8987e2012-04-19 12:52:20 -070031#include <media/stagefright/foundation/AString.h>
Andreas Huber57788222012-02-21 11:47:18 -080032#include <media/stagefright/DataSource.h>
33#include <media/stagefright/MediaCodec.h>
Andreas Hubered3e3e02012-03-26 11:13:27 -070034#include <media/stagefright/MediaCodecList.h>
Andreas Huber57788222012-02-21 11:47:18 -080035#include <media/stagefright/MediaDefs.h>
36#include <media/stagefright/NuMediaExtractor.h>
Jeff Browna6195de2012-08-24 22:34:11 -070037#include <gui/ISurfaceComposer.h>
Mathias Agopiandf712ea2012-02-25 18:48:35 -080038#include <gui/SurfaceComposerClient.h>
Mathias Agopian1a2952a2013-02-14 17:11:27 -080039#include <gui/Surface.h>
Mathias Agopian3dca4c72012-07-25 19:22:42 -070040#include <ui/DisplayInfo.h>
Andreas Huber57788222012-02-21 11:47:18 -080041
42static void usage(const char *me) {
43 fprintf(stderr, "usage: %s [-a] use audio\n"
44 "\t\t[-v] use video\n"
Andreas Huberbae6f722012-03-07 12:40:59 -080045 "\t\t[-p] playback\n"
Andreas Huberf69e5302012-04-18 12:22:58 -070046 "\t\t[-S] allocate buffers from a surface\n",
Andreas Hubered3e3e02012-03-26 11:13:27 -070047 me);
Andreas Huber57788222012-02-21 11:47:18 -080048
49 exit(1);
50}
51
52namespace android {
53
54struct CodecState {
55 sp<MediaCodec> mCodec;
Andreas Huber57788222012-02-21 11:47:18 -080056 Vector<sp<ABuffer> > mInBuffers;
57 Vector<sp<ABuffer> > mOutBuffers;
Andreas Huber5aaeb0d2012-03-09 09:46:33 -080058 bool mSignalledInputEOS;
Andreas Huber57788222012-02-21 11:47:18 -080059 bool mSawOutputEOS;
Andreas Huberbae6f722012-03-07 12:40:59 -080060 int64_t mNumBuffersDecoded;
61 int64_t mNumBytesDecoded;
62 bool mIsAudio;
Andreas Huber57788222012-02-21 11:47:18 -080063};
64
65} // namespace android
66
67static int decode(
68 const android::sp<android::ALooper> &looper,
69 const char *path,
70 bool useAudio,
Andreas Huberbae6f722012-03-07 12:40:59 -080071 bool useVideo,
Andreas Huberf69e5302012-04-18 12:22:58 -070072 const android::sp<android::Surface> &surface) {
Andreas Huber57788222012-02-21 11:47:18 -080073 using namespace android;
74
Andreas Huberbae6f722012-03-07 12:40:59 -080075 static int64_t kTimeout = 500ll;
76
Andreas Huber57788222012-02-21 11:47:18 -080077 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
Andreas Huberbae6f722012-03-07 12:40:59 -080095 bool isAudio = !strncasecmp(mime.c_str(), "audio/", 6);
96 bool isVideo = !strncasecmp(mime.c_str(), "video/", 6);
97
98 if (useAudio && !haveAudio && isAudio) {
Andreas Huber57788222012-02-21 11:47:18 -080099 haveAudio = true;
Andreas Huberbae6f722012-03-07 12:40:59 -0800100 } else if (useVideo && !haveVideo && isVideo) {
Andreas Huber57788222012-02-21 11:47:18 -0800101 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
Andreas Huberbae6f722012-03-07 12:40:59 -0800114 state->mNumBytesDecoded = 0;
115 state->mNumBuffersDecoded = 0;
116 state->mIsAudio = isAudio;
117
Andreas Huberf69e5302012-04-18 12:22:58 -0700118 state->mCodec = MediaCodec::CreateByType(
119 looper, mime.c_str(), false /* encoder */);
Andreas Huber57788222012-02-21 11:47:18 -0800120
121 CHECK(state->mCodec != NULL);
122
123 err = state->mCodec->configure(
Andreas Hubered3e3e02012-03-26 11:13:27 -0700124 format, isVideo ? surface : NULL,
Andreas Huberf69e5302012-04-18 12:22:58 -0700125 NULL /* crypto */,
Andreas Huber1bd139a2012-04-03 14:19:20 -0700126 0 /* flags */);
Andreas Huber57788222012-02-21 11:47:18 -0800127
128 CHECK_EQ(err, (status_t)OK);
129
Andreas Huber5aaeb0d2012-03-09 09:46:33 -0800130 state->mSignalledInputEOS = false;
Andreas Huber57788222012-02-21 11:47:18 -0800131 state->mSawOutputEOS = false;
Andreas Huber57788222012-02-21 11:47:18 -0800132 }
133
134 CHECK(!stateByTrack.isEmpty());
135
Andreas Huberbae6f722012-03-07 12:40:59 -0800136 int64_t startTimeUs = ALooper::GetNowUs();
137
Andreas Huber57788222012-02-21 11:47:18 -0800138 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());
Andreas Huber57788222012-02-21 11:47:18 -0800150 }
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) {
Andreas Huber5aaeb0d2012-03-09 09:46:33 -0800160 ALOGV("saw input eos");
Andreas Huber57788222012-02-21 11:47:18 -0800161 sawInputEOS = true;
162 } else {
163 CodecState *state = &stateByTrack.editValueFor(trackIndex);
164
165 size_t index;
Andreas Huberbae6f722012-03-07 12:40:59 -0800166 err = state->mCodec->dequeueInputBuffer(&index, kTimeout);
Andreas Huber57788222012-02-21 11:47:18 -0800167
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
Andreas Hubered3e3e02012-03-26 11:13:27 -0700180 uint32_t bufferFlags = 0;
181
Andreas Huberf69e5302012-04-18 12:22:58 -0700182 err = state->mCodec->queueInputBuffer(
183 index,
184 0 /* offset */,
185 buffer->size(),
186 timeUs,
187 bufferFlags);
Andreas Huber57788222012-02-21 11:47:18 -0800188
189 CHECK_EQ(err, (status_t)OK);
190
191 extractor->advance();
192 } else {
193 CHECK_EQ(err, -EAGAIN);
194 }
195 }
Andreas Huber5aaeb0d2012-03-09 09:46:33 -0800196 } 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 }
Andreas Huber57788222012-02-21 11:47:18 -0800223 }
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,
Andreas Huberbae6f722012-03-07 12:40:59 -0800252 kTimeout);
Andreas Huber57788222012-02-21 11:47:18 -0800253
254 if (err == OK) {
255 ALOGV("draining output buffer %d, time = %lld us",
256 index, presentationTimeUs);
257
Andreas Huberbae6f722012-03-07 12:40:59 -0800258 ++state->mNumBuffersDecoded;
259 state->mNumBytesDecoded += size;
260
Andreas Huber57788222012-02-21 11:47:18 -0800261 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
Andreas Huberbae6f722012-03-07 12:40:59 -0800286 int64_t elapsedTimeUs = ALooper::GetNowUs() - startTimeUs;
287
Andreas Huber57788222012-02-21 11:47:18 -0800288 for (size_t i = 0; i < stateByTrack.size(); ++i) {
289 CodecState *state = &stateByTrack.editValueAt(i);
290
Andreas Huberc95c2dd2012-02-28 15:54:51 -0800291 CHECK_EQ((status_t)OK, state->mCodec->release());
Andreas Huberbae6f722012-03-07 12:40:59 -0800292
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 }
Andreas Huber57788222012-02-21 11:47:18 -0800307 }
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;
Andreas Huberbae6f722012-03-07 12:40:59 -0800320 bool useSurface = false;
Andreas Huber57788222012-02-21 11:47:18 -0800321
322 int res;
Andreas Hubered3e3e02012-03-26 11:13:27 -0700323 while ((res = getopt(argc, argv, "havpSD")) >= 0) {
Andreas Huber57788222012-02-21 11:47:18 -0800324 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
Andreas Huberbae6f722012-03-07 12:40:59 -0800343 case 'S':
344 {
345 useSurface = true;
346 break;
347 }
348
Andreas Huber57788222012-02-21 11:47:18 -0800349 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
Andreas Huberbae6f722012-03-07 12:40:59 -0800376 sp<SurfaceComposerClient> composerClient;
377 sp<SurfaceControl> control;
378 sp<Surface> surface;
379
380 if (playback || (useSurface && useVideo)) {
381 composerClient = new SurfaceComposerClient;
Andreas Huber57788222012-02-21 11:47:18 -0800382 CHECK_EQ(composerClient->initCheck(), (status_t)OK);
383
Jeff Browna6195de2012-08-24 22:34:11 -0700384 sp<IBinder> display(SurfaceComposerClient::getBuiltInDisplay(
385 ISurfaceComposer::eDisplayIdMain));
Mathias Agopian3dca4c72012-07-25 19:22:42 -0700386 DisplayInfo info;
Jeff Browna6195de2012-08-24 22:34:11 -0700387 SurfaceComposerClient::getDisplayInfo(display, &info);
Mathias Agopian3dca4c72012-07-25 19:22:42 -0700388 ssize_t displayWidth = info.w;
389 ssize_t displayHeight = info.h;
Andreas Huber57788222012-02-21 11:47:18 -0800390
391 ALOGV("display is %ld x %ld\n", displayWidth, displayHeight);
392
Andreas Huberbae6f722012-03-07 12:40:59 -0800393 control = composerClient->createSurface(
394 String8("A Surface"),
Andreas Huberbae6f722012-03-07 12:40:59 -0800395 displayWidth,
396 displayHeight,
397 PIXEL_FORMAT_RGB_565,
398 0);
Andreas Huber57788222012-02-21 11:47:18 -0800399
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
Andreas Huberbae6f722012-03-07 12:40:59 -0800408 surface = control->getSurface();
Andreas Huber57788222012-02-21 11:47:18 -0800409 CHECK(surface != NULL);
Andreas Huberbae6f722012-03-07 12:40:59 -0800410 }
Andreas Huber57788222012-02-21 11:47:18 -0800411
Andreas Huberbae6f722012-03-07 12:40:59 -0800412 if (playback) {
Andreas Huber57788222012-02-21 11:47:18 -0800413 sp<SimplePlayer> player = new SimplePlayer;
414 looper->registerHandler(player);
415
416 player->setDataSource(argv[0]);
Mathias Agopian1a2952a2013-02-14 17:11:27 -0800417 player->setSurface(surface->getIGraphicBufferProducer());
Andreas Huber57788222012-02-21 11:47:18 -0800418 player->start();
Andreas Hubere98f8c02012-02-23 11:21:43 -0800419 sleep(60);
Andreas Huber57788222012-02-21 11:47:18 -0800420 player->stop();
421 player->reset();
Andreas Huber57788222012-02-21 11:47:18 -0800422 } else {
Andreas Huberf69e5302012-04-18 12:22:58 -0700423 decode(looper, argv[0], useAudio, useVideo, surface);
Andreas Huberbae6f722012-03-07 12:40:59 -0800424 }
425
426 if (playback || (useSurface && useVideo)) {
427 composerClient->dispose();
Andreas Huber57788222012-02-21 11:47:18 -0800428 }
429
430 looper->stop();
431
432 return 0;
433}