blob: c817443c589496a64338faa1820fa4adc440631b [file] [log] [blame]
James Dong8eb69d62011-03-17 11:48:13 -07001/*
2 * Copyright (C) 2010 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
Andreas Huberf5e376f2012-03-16 13:19:20 -070017//#define LOG_NDEBUG 0
18#define LOG_TAG "sf2"
19#include <utils/Log.h>
20
Andreas Huberf9334412010-12-15 15:17:42 -080021#include <binder/ProcessState.h>
22
23#include <media/stagefright/foundation/hexdump.h>
24#include <media/stagefright/foundation/ABuffer.h>
25#include <media/stagefright/foundation/ADebug.h>
26#include <media/stagefright/foundation/ALooper.h>
27#include <media/stagefright/foundation/AMessage.h>
28
29#include <media/stagefright/ACodec.h>
30#include <media/stagefright/DataSource.h>
31#include <media/stagefright/MediaBuffer.h>
32#include <media/stagefright/MediaDefs.h>
33#include <media/stagefright/MediaExtractor.h>
34#include <media/stagefright/MediaSource.h>
35#include <media/stagefright/MetaData.h>
Andreas Huber1065b3f2011-06-28 10:51:41 -070036#include <media/stagefright/NativeWindowWrapper.h>
Andreas Huberf9334412010-12-15 15:17:42 -080037#include <media/stagefright/Utils.h>
38
Mathias Agopiandf712ea2012-02-25 18:48:35 -080039#include <gui/SurfaceComposerClient.h>
Andreas Huberf9334412010-12-15 15:17:42 -080040
41#include "include/ESDS.h"
42
43using namespace android;
44
45struct Controller : public AHandler {
Andreas Huber1065b3f2011-06-28 10:51:41 -070046 Controller(const char *uri, bool decodeAudio,
47 const sp<Surface> &surface, bool renderToSurface)
Andreas Huberf9334412010-12-15 15:17:42 -080048 : mURI(uri),
49 mDecodeAudio(decodeAudio),
50 mSurface(surface),
Andreas Huber1065b3f2011-06-28 10:51:41 -070051 mRenderToSurface(renderToSurface),
Andreas Huber729de182011-09-28 12:37:36 -070052 mCodec(new ACodec),
53 mIsVorbis(false) {
Andreas Huberf9334412010-12-15 15:17:42 -080054 CHECK(!mDecodeAudio || mSurface == NULL);
55 }
56
57 void startAsync() {
58 (new AMessage(kWhatStart, id()))->post();
59 }
60
61protected:
62 virtual ~Controller() {
63 }
64
65 virtual void onMessageReceived(const sp<AMessage> &msg) {
66 switch (msg->what()) {
67 case kWhatStart:
68 {
69#if 1
70 mDecodeLooper = looper();
71#else
72 mDecodeLooper = new ALooper;
73 mDecodeLooper->setName("sf2 decode looper");
74 mDecodeLooper->start();
75#endif
76
77 sp<DataSource> dataSource =
78 DataSource::CreateFromURI(mURI.c_str());
79
80 sp<MediaExtractor> extractor =
81 MediaExtractor::Create(dataSource);
82
83 for (size_t i = 0; i < extractor->countTracks(); ++i) {
84 sp<MetaData> meta = extractor->getTrackMetaData(i);
85
86 const char *mime;
87 CHECK(meta->findCString(kKeyMIMEType, &mime));
88
89 if (!strncasecmp(mDecodeAudio ? "audio/" : "video/",
90 mime, 6)) {
91 mSource = extractor->getTrack(i);
Andreas Huber729de182011-09-28 12:37:36 -070092
93 if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_VORBIS)) {
94 mIsVorbis = true;
95 } else {
96 mIsVorbis = false;
97 }
Andreas Huberf9334412010-12-15 15:17:42 -080098 break;
99 }
100 }
101 CHECK(mSource != NULL);
102
103 CHECK_EQ(mSource->start(), (status_t)OK);
104
105 mDecodeLooper->registerHandler(mCodec);
106
107 mCodec->setNotificationMessage(
108 new AMessage(kWhatCodecNotify, id()));
109
110 sp<AMessage> format = makeFormat(mSource->getFormat());
111
112 if (mSurface != NULL) {
Andreas Huber1065b3f2011-06-28 10:51:41 -0700113 format->setObject(
114 "native-window", new NativeWindowWrapper(mSurface));
Andreas Huberf9334412010-12-15 15:17:42 -0800115 }
116
117 mCodec->initiateSetup(format);
118
119 mCSDIndex = 0;
120 mStartTimeUs = ALooper::GetNowUs();
121 mNumOutputBuffersReceived = 0;
122 mTotalBytesReceived = 0;
123 mLeftOverBuffer = NULL;
124 mFinalResult = OK;
125 mSeekState = SEEK_NONE;
126
127 // (new AMessage(kWhatSeek, id()))->post(5000000ll);
128 break;
129 }
130
131 case kWhatSeek:
132 {
133 printf("+");
134 fflush(stdout);
135
136 CHECK(mSeekState == SEEK_NONE
137 || mSeekState == SEEK_FLUSH_COMPLETED);
138
139 if (mLeftOverBuffer != NULL) {
140 mLeftOverBuffer->release();
141 mLeftOverBuffer = NULL;
142 }
143
144 mSeekState = SEEK_FLUSHING;
145 mSeekTimeUs = 30000000ll;
146
147 mCodec->signalFlush();
148 break;
149 }
150
151 case kWhatStop:
152 {
153 if (mLeftOverBuffer != NULL) {
154 mLeftOverBuffer->release();
155 mLeftOverBuffer = NULL;
156 }
157
158 CHECK_EQ(mSource->stop(), (status_t)OK);
159 mSource.clear();
160
161 mCodec->initiateShutdown();
162 break;
163 }
164
165 case kWhatCodecNotify:
166 {
167 int32_t what;
168 CHECK(msg->findInt32("what", &what));
169
170 if (what == ACodec::kWhatFillThisBuffer) {
171 onFillThisBuffer(msg);
172 } else if (what == ACodec::kWhatDrainThisBuffer) {
173 if ((mNumOutputBuffersReceived++ % 16) == 0) {
174 printf(".");
175 fflush(stdout);
176 }
177
178 onDrainThisBuffer(msg);
Andreas Huberafed0e12011-09-20 15:39:58 -0700179 } else if (what == ACodec::kWhatEOS
180 || what == ACodec::kWhatError) {
181 printf((what == ACodec::kWhatEOS) ? "$\n" : "E\n");
Andreas Huberf9334412010-12-15 15:17:42 -0800182
183 int64_t delayUs = ALooper::GetNowUs() - mStartTimeUs;
184
185 if (mDecodeAudio) {
186 printf("%lld bytes received. %.2f KB/sec\n",
187 mTotalBytesReceived,
188 mTotalBytesReceived * 1E6 / 1024 / delayUs);
189 } else {
190 printf("%d frames decoded, %.2f fps. %lld bytes "
191 "received. %.2f KB/sec\n",
192 mNumOutputBuffersReceived,
193 mNumOutputBuffersReceived * 1E6 / delayUs,
194 mTotalBytesReceived,
195 mTotalBytesReceived * 1E6 / 1024 / delayUs);
196 }
197
198 (new AMessage(kWhatStop, id()))->post();
199 } else if (what == ACodec::kWhatFlushCompleted) {
200 mSeekState = SEEK_FLUSH_COMPLETED;
201 mCodec->signalResume();
202
203 (new AMessage(kWhatSeek, id()))->post(5000000ll);
Andreas Huber777833c2011-02-04 10:14:08 -0800204 } else if (what == ACodec::kWhatOutputFormatChanged) {
Andreas Huber57788222012-02-21 11:47:18 -0800205 } else if (what == ACodec::kWhatShutdownCompleted) {
Andreas Huberf9334412010-12-15 15:17:42 -0800206 mDecodeLooper->unregisterHandler(mCodec->id());
207
208 if (mDecodeLooper != looper()) {
209 mDecodeLooper->stop();
210 }
211
212 looper()->stop();
Andreas Huberf5e376f2012-03-16 13:19:20 -0700213 } else if (what == ACodec::kWhatError) {
214 ALOGE("something went wrong, codec reported an error.");
215
216 printf("E\n");
217
218 (new AMessage(kWhatStop, id()))->post();
Andreas Huberf9334412010-12-15 15:17:42 -0800219 }
220 break;
221 }
222
223 default:
224 TRESPASS();
225 break;
226 }
227 }
228
229private:
230 enum {
231 kWhatStart = 'strt',
232 kWhatStop = 'stop',
233 kWhatCodecNotify = 'noti',
234 kWhatSeek = 'seek',
235 };
236
237 sp<ALooper> mDecodeLooper;
238
239 AString mURI;
240 bool mDecodeAudio;
241 sp<Surface> mSurface;
Andreas Huber1065b3f2011-06-28 10:51:41 -0700242 bool mRenderToSurface;
Andreas Huberf9334412010-12-15 15:17:42 -0800243 sp<ACodec> mCodec;
244 sp<MediaSource> mSource;
Andreas Huber729de182011-09-28 12:37:36 -0700245 bool mIsVorbis;
Andreas Huberf9334412010-12-15 15:17:42 -0800246
247 Vector<sp<ABuffer> > mCSD;
248 size_t mCSDIndex;
249
250 MediaBuffer *mLeftOverBuffer;
251 status_t mFinalResult;
252
253 int64_t mStartTimeUs;
254 int32_t mNumOutputBuffersReceived;
255 int64_t mTotalBytesReceived;
256
257 enum SeekState {
258 SEEK_NONE,
259 SEEK_FLUSHING,
260 SEEK_FLUSH_COMPLETED,
261 };
262 SeekState mSeekState;
263 int64_t mSeekTimeUs;
264
265 sp<AMessage> makeFormat(const sp<MetaData> &meta) {
266 CHECK(mCSD.isEmpty());
267
268 const char *mime;
269 CHECK(meta->findCString(kKeyMIMEType, &mime));
270
271 sp<AMessage> msg = new AMessage;
272 msg->setString("mime", mime);
273
274 if (!strncasecmp("video/", mime, 6)) {
275 int32_t width, height;
276 CHECK(meta->findInt32(kKeyWidth, &width));
277 CHECK(meta->findInt32(kKeyHeight, &height));
278
279 msg->setInt32("width", width);
280 msg->setInt32("height", height);
281 } else {
282 CHECK(!strncasecmp("audio/", mime, 6));
283
284 int32_t numChannels, sampleRate;
285 CHECK(meta->findInt32(kKeyChannelCount, &numChannels));
286 CHECK(meta->findInt32(kKeySampleRate, &sampleRate));
287
288 msg->setInt32("channel-count", numChannels);
289 msg->setInt32("sample-rate", sampleRate);
Andreas Hubered3e3e02012-03-26 11:13:27 -0700290
291 int32_t isADTS;
292 if (meta->findInt32(kKeyIsADTS, &isADTS) && isADTS != 0) {
293 msg->setInt32("is-adts", true);
294 }
Andreas Huberf9334412010-12-15 15:17:42 -0800295 }
296
297 uint32_t type;
298 const void *data;
299 size_t size;
300 if (meta->findData(kKeyAVCC, &type, &data, &size)) {
301 // Parse the AVCDecoderConfigurationRecord
302
303 const uint8_t *ptr = (const uint8_t *)data;
304
305 CHECK(size >= 7);
306 CHECK_EQ((unsigned)ptr[0], 1u); // configurationVersion == 1
307 uint8_t profile = ptr[1];
308 uint8_t level = ptr[3];
309
310 // There is decodable content out there that fails the following
311 // assertion, let's be lenient for now...
312 // CHECK((ptr[4] >> 2) == 0x3f); // reserved
313
314 size_t lengthSize = 1 + (ptr[4] & 3);
315
316 // commented out check below as H264_QVGA_500_NO_AUDIO.3gp
317 // violates it...
318 // CHECK((ptr[5] >> 5) == 7); // reserved
319
320 size_t numSeqParameterSets = ptr[5] & 31;
321
322 ptr += 6;
323 size -= 6;
324
325 sp<ABuffer> buffer = new ABuffer(1024);
326 buffer->setRange(0, 0);
327
328 for (size_t i = 0; i < numSeqParameterSets; ++i) {
329 CHECK(size >= 2);
330 size_t length = U16_AT(ptr);
331
332 ptr += 2;
333 size -= 2;
334
335 CHECK(size >= length);
336
337 memcpy(buffer->data() + buffer->size(), "\x00\x00\x00\x01", 4);
338 memcpy(buffer->data() + buffer->size() + 4, ptr, length);
339 buffer->setRange(0, buffer->size() + 4 + length);
340
341 ptr += length;
342 size -= length;
343 }
344
345 buffer->meta()->setInt32("csd", true);
346 mCSD.push(buffer);
347
348 buffer = new ABuffer(1024);
349 buffer->setRange(0, 0);
350
351 CHECK(size >= 1);
352 size_t numPictureParameterSets = *ptr;
353 ++ptr;
354 --size;
355
356 for (size_t i = 0; i < numPictureParameterSets; ++i) {
357 CHECK(size >= 2);
358 size_t length = U16_AT(ptr);
359
360 ptr += 2;
361 size -= 2;
362
363 CHECK(size >= length);
364
365 memcpy(buffer->data() + buffer->size(), "\x00\x00\x00\x01", 4);
366 memcpy(buffer->data() + buffer->size() + 4, ptr, length);
367 buffer->setRange(0, buffer->size() + 4 + length);
368
369 ptr += length;
370 size -= length;
371 }
372
373 buffer->meta()->setInt32("csd", true);
374 mCSD.push(buffer);
375
Andreas Huberbf6c85a2012-02-24 13:42:47 -0800376 msg->setBuffer("csd", buffer);
Andreas Huberf9334412010-12-15 15:17:42 -0800377 } else if (meta->findData(kKeyESDS, &type, &data, &size)) {
378 ESDS esds((const char *)data, size);
379 CHECK_EQ(esds.InitCheck(), (status_t)OK);
380
381 const void *codec_specific_data;
382 size_t codec_specific_data_size;
383 esds.getCodecSpecificInfo(
384 &codec_specific_data, &codec_specific_data_size);
385
386 sp<ABuffer> buffer = new ABuffer(codec_specific_data_size);
387
388 memcpy(buffer->data(), codec_specific_data,
389 codec_specific_data_size);
390
391 buffer->meta()->setInt32("csd", true);
392 mCSD.push(buffer);
Andreas Huber729de182011-09-28 12:37:36 -0700393 } else if (meta->findData(kKeyVorbisInfo, &type, &data, &size)) {
394 sp<ABuffer> buffer = new ABuffer(size);
395 memcpy(buffer->data(), data, size);
396
397 buffer->meta()->setInt32("csd", true);
398 mCSD.push(buffer);
399
400 CHECK(meta->findData(kKeyVorbisBooks, &type, &data, &size));
401
402 buffer = new ABuffer(size);
403 memcpy(buffer->data(), data, size);
404
405 buffer->meta()->setInt32("csd", true);
406 mCSD.push(buffer);
Andreas Huberf9334412010-12-15 15:17:42 -0800407 }
408
409 int32_t maxInputSize;
410 if (meta->findInt32(kKeyMaxInputSize, &maxInputSize)) {
411 msg->setInt32("max-input-size", maxInputSize);
412 }
413
414 return msg;
415 }
416
417 void onFillThisBuffer(const sp<AMessage> &msg) {
418 sp<AMessage> reply;
419 CHECK(msg->findMessage("reply", &reply));
420
Andreas Huberafed0e12011-09-20 15:39:58 -0700421 if (mSource == NULL || mSeekState == SEEK_FLUSHING) {
422 reply->setInt32("err", ERROR_END_OF_STREAM);
Andreas Huberf9334412010-12-15 15:17:42 -0800423 reply->post();
424 return;
425 }
426
Andreas Huberbf6c85a2012-02-24 13:42:47 -0800427 sp<ABuffer> outBuffer;
428 CHECK(msg->findBuffer("buffer", &outBuffer));
Andreas Huberf9334412010-12-15 15:17:42 -0800429
430 if (mCSDIndex < mCSD.size()) {
431 outBuffer = mCSD.editItemAt(mCSDIndex++);
432 outBuffer->meta()->setInt64("timeUs", 0);
433 } else {
434 size_t sizeLeft = outBuffer->capacity();
435 outBuffer->setRange(0, 0);
436
437 int32_t n = 0;
438
439 for (;;) {
440 MediaBuffer *inBuffer;
441
442 if (mLeftOverBuffer != NULL) {
443 inBuffer = mLeftOverBuffer;
444 mLeftOverBuffer = NULL;
445 } else if (mFinalResult != OK) {
446 break;
447 } else {
448 MediaSource::ReadOptions options;
449 if (mSeekState == SEEK_FLUSH_COMPLETED) {
450 options.setSeekTo(mSeekTimeUs);
451 mSeekState = SEEK_NONE;
452 }
453 status_t err = mSource->read(&inBuffer, &options);
454
455 if (err != OK) {
456 mFinalResult = err;
457 break;
458 }
459 }
460
Andreas Huber729de182011-09-28 12:37:36 -0700461 size_t sizeNeeded = inBuffer->range_length();
462 if (mIsVorbis) {
463 // Vorbis data is suffixed with the number of
464 // valid samples on the page.
465 sizeNeeded += sizeof(int32_t);
466 }
467
468 if (sizeNeeded > sizeLeft) {
Andreas Huberf9334412010-12-15 15:17:42 -0800469 if (outBuffer->size() == 0) {
Steve Block29357bc2012-01-06 19:20:56 +0000470 ALOGE("Unable to fit even a single input buffer of size %d.",
Andreas Huber729de182011-09-28 12:37:36 -0700471 sizeNeeded);
Andreas Huberf9334412010-12-15 15:17:42 -0800472 }
473 CHECK_GT(outBuffer->size(), 0u);
474
475 mLeftOverBuffer = inBuffer;
476 break;
477 }
478
479 ++n;
480
481 if (outBuffer->size() == 0) {
482 int64_t timeUs;
483 CHECK(inBuffer->meta_data()->findInt64(kKeyTime, &timeUs));
484
485 outBuffer->meta()->setInt64("timeUs", timeUs);
486 }
487
488 memcpy(outBuffer->data() + outBuffer->size(),
489 (const uint8_t *)inBuffer->data()
490 + inBuffer->range_offset(),
491 inBuffer->range_length());
492
Andreas Huber729de182011-09-28 12:37:36 -0700493 if (mIsVorbis) {
494 int32_t numPageSamples;
495 if (!inBuffer->meta_data()->findInt32(
496 kKeyValidSamples, &numPageSamples)) {
497 numPageSamples = -1;
498 }
Andreas Huberf9334412010-12-15 15:17:42 -0800499
Andreas Huber729de182011-09-28 12:37:36 -0700500 memcpy(outBuffer->data()
501 + outBuffer->size() + inBuffer->range_length(),
502 &numPageSamples, sizeof(numPageSamples));
503 }
504
505 outBuffer->setRange(
506 0, outBuffer->size() + sizeNeeded);
507
508 sizeLeft -= sizeNeeded;
Andreas Huberf9334412010-12-15 15:17:42 -0800509
510 inBuffer->release();
511 inBuffer = NULL;
512
Andreas Huber1065b3f2011-06-28 10:51:41 -0700513 break; // Don't coalesce
Andreas Huberf9334412010-12-15 15:17:42 -0800514 }
515
Steve Block3856b092011-10-20 11:56:00 +0100516 ALOGV("coalesced %d input buffers", n);
Andreas Huberf9334412010-12-15 15:17:42 -0800517
518 if (outBuffer->size() == 0) {
519 CHECK_NE(mFinalResult, (status_t)OK);
520
521 reply->setInt32("err", mFinalResult);
522 reply->post();
523 return;
524 }
525 }
526
Andreas Huberbf6c85a2012-02-24 13:42:47 -0800527 reply->setBuffer("buffer", outBuffer);
Andreas Huberf9334412010-12-15 15:17:42 -0800528 reply->post();
529 }
530
531 void onDrainThisBuffer(const sp<AMessage> &msg) {
Andreas Huberbf6c85a2012-02-24 13:42:47 -0800532 sp<ABuffer> buffer;
533 CHECK(msg->findBuffer("buffer", &buffer));
Andreas Huberf9334412010-12-15 15:17:42 -0800534
Andreas Huberf9334412010-12-15 15:17:42 -0800535 mTotalBytesReceived += buffer->size();
536
537 sp<AMessage> reply;
538 CHECK(msg->findMessage("reply", &reply));
539
Andreas Huber1065b3f2011-06-28 10:51:41 -0700540 if (mRenderToSurface) {
541 reply->setInt32("render", 1);
542 }
543
Andreas Huberf9334412010-12-15 15:17:42 -0800544 reply->post();
545 }
546
547 DISALLOW_EVIL_CONSTRUCTORS(Controller);
548};
549
550static void usage(const char *me) {
551 fprintf(stderr, "usage: %s\n", me);
552 fprintf(stderr, " -h(elp)\n");
553 fprintf(stderr, " -a(udio)\n");
554
555 fprintf(stderr,
Andreas Huber1065b3f2011-06-28 10:51:41 -0700556 " -S(urface) Allocate output buffers on a surface.\n"
557 " -R(ender) Render surface-allocated buffers.\n");
Andreas Huberf9334412010-12-15 15:17:42 -0800558}
559
560int main(int argc, char **argv) {
561 android::ProcessState::self()->startThreadPool();
562
563 bool decodeAudio = false;
564 bool useSurface = false;
Andreas Huber1065b3f2011-06-28 10:51:41 -0700565 bool renderToSurface = false;
Andreas Huberf9334412010-12-15 15:17:42 -0800566
567 int res;
Andreas Huber1065b3f2011-06-28 10:51:41 -0700568 while ((res = getopt(argc, argv, "haSR")) >= 0) {
Andreas Huberf9334412010-12-15 15:17:42 -0800569 switch (res) {
570 case 'a':
571 decodeAudio = true;
572 break;
573
Andreas Huber1065b3f2011-06-28 10:51:41 -0700574 case 'S':
Andreas Huberf9334412010-12-15 15:17:42 -0800575 useSurface = true;
576 break;
577
Andreas Huber1065b3f2011-06-28 10:51:41 -0700578 case 'R':
579 renderToSurface = true;
580 break;
581
Andreas Huberf9334412010-12-15 15:17:42 -0800582 case '?':
583 case 'h':
584 default:
585 {
586 usage(argv[0]);
587 return 1;
588 }
589 }
590 }
591
592 argc -= optind;
593 argv += optind;
594
595 if (argc != 1) {
596 usage(argv[-optind]);
597 return 1;
598 }
599
600 DataSource::RegisterDefaultSniffers();
601
602 sp<ALooper> looper = new ALooper;
603 looper->setName("sf2");
604
605 sp<SurfaceComposerClient> composerClient;
606 sp<SurfaceControl> control;
607 sp<Surface> surface;
608
609 if (!decodeAudio && useSurface) {
610 composerClient = new SurfaceComposerClient;
611 CHECK_EQ(composerClient->initCheck(), (status_t)OK);
612
613 control = composerClient->createSurface(
Andreas Huberf9334412010-12-15 15:17:42 -0800614 String8("A Surface"),
Andreas Huberf9334412010-12-15 15:17:42 -0800615 1280,
616 800,
617 PIXEL_FORMAT_RGB_565,
618 0);
619
620 CHECK(control != NULL);
621 CHECK(control->isValid());
622
Mathias Agopian2df788f2011-06-28 19:09:31 -0700623 SurfaceComposerClient::openGlobalTransaction();
Andreas Huber93d5ab62011-09-15 12:42:32 -0700624 CHECK_EQ(control->setLayer(INT_MAX), (status_t)OK);
Andreas Huberf9334412010-12-15 15:17:42 -0800625 CHECK_EQ(control->show(), (status_t)OK);
Mathias Agopian2df788f2011-06-28 19:09:31 -0700626 SurfaceComposerClient::closeGlobalTransaction();
Andreas Huberf9334412010-12-15 15:17:42 -0800627
628 surface = control->getSurface();
629 CHECK(surface != NULL);
Andreas Huber93d5ab62011-09-15 12:42:32 -0700630
631 CHECK_EQ((status_t)OK,
632 native_window_api_connect(
633 surface.get(), NATIVE_WINDOW_API_MEDIA));
Andreas Huberf9334412010-12-15 15:17:42 -0800634 }
635
Andreas Huber1065b3f2011-06-28 10:51:41 -0700636 sp<Controller> controller =
637 new Controller(argv[0], decodeAudio, surface, renderToSurface);
638
Andreas Huberf9334412010-12-15 15:17:42 -0800639 looper->registerHandler(controller);
640
641 controller->startAsync();
642
643 CHECK_EQ(looper->start(true /* runOnCallingThread */), (status_t)OK);
644
645 looper->unregisterHandler(controller->id());
646
647 if (!decodeAudio && useSurface) {
Andreas Huber93d5ab62011-09-15 12:42:32 -0700648 CHECK_EQ((status_t)OK,
649 native_window_api_disconnect(
650 surface.get(), NATIVE_WINDOW_API_MEDIA));
651
Andreas Huberf9334412010-12-15 15:17:42 -0800652 composerClient->dispose();
653 }
654
655 return 0;
656}
657