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