blob: 5abfb711556f05347c5c92c2339461062609dd47 [file] [log] [blame]
Andreas Huberf9334412010-12-15 15:17:42 -08001/*
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
17//#define LOG_NDEBUG 0
18#define LOG_TAG "NuPlayerDecoder"
19#include <utils/Log.h>
Lajos Molnar1cd13982014-01-17 15:12:51 -080020#include <inttypes.h>
Andreas Huberf9334412010-12-15 15:17:42 -080021
22#include "NuPlayerDecoder.h"
23
Lajos Molnar1cd13982014-01-17 15:12:51 -080024#include <media/ICrypto.h>
Chong Zhanga7fa1d92014-06-11 14:49:23 -070025#include <media/stagefright/foundation/ABitReader.h>
Andreas Huberf9334412010-12-15 15:17:42 -080026#include <media/stagefright/foundation/ABuffer.h>
27#include <media/stagefright/foundation/ADebug.h>
Andreas Huber5bc087c2010-12-23 10:27:40 -080028#include <media/stagefright/foundation/AMessage.h>
Lajos Molnar1cd13982014-01-17 15:12:51 -080029#include <media/stagefright/MediaCodec.h>
Andreas Huberf9334412010-12-15 15:17:42 -080030#include <media/stagefright/MediaDefs.h>
Lajos Molnar1cd13982014-01-17 15:12:51 -080031#include <media/stagefright/MediaErrors.h>
Andreas Huberf9334412010-12-15 15:17:42 -080032
33namespace android {
34
35NuPlayer::Decoder::Decoder(
Glenn Kasten11731182011-02-08 17:26:17 -080036 const sp<AMessage> &notify,
37 const sp<NativeWindowWrapper> &nativeWindow)
Andreas Huberf9334412010-12-15 15:17:42 -080038 : mNotify(notify),
Lajos Molnar1cd13982014-01-17 15:12:51 -080039 mNativeWindow(nativeWindow),
40 mBufferGeneration(0),
Wei Jia704e7262014-06-04 16:21:56 -070041 mPaused(true),
Lajos Molnar1cd13982014-01-17 15:12:51 -080042 mComponentName("decoder") {
43 // Every decoder has its own looper because MediaCodec operations
44 // are blocking, but NuPlayer needs asynchronous operations.
45 mDecoderLooper = new ALooper;
46 mDecoderLooper->setName("NuPlayerDecoder");
47 mDecoderLooper->start(false, false, ANDROID_PRIORITY_AUDIO);
48
49 mCodecLooper = new ALooper;
50 mCodecLooper->setName("NuPlayerDecoder-MC");
51 mCodecLooper->start(false, false, ANDROID_PRIORITY_AUDIO);
Andreas Huberf9334412010-12-15 15:17:42 -080052}
53
54NuPlayer::Decoder::~Decoder() {
55}
56
Lajos Molnar1cd13982014-01-17 15:12:51 -080057void NuPlayer::Decoder::onConfigure(const sp<AMessage> &format) {
Andreas Huberf9334412010-12-15 15:17:42 -080058 CHECK(mCodec == NULL);
Andreas Huberf9334412010-12-15 15:17:42 -080059
Lajos Molnar1cd13982014-01-17 15:12:51 -080060 ++mBufferGeneration;
61
Andreas Huber84066782011-08-16 09:34:26 -070062 AString mime;
63 CHECK(format->findString("mime", &mime));
Andreas Huberf9334412010-12-15 15:17:42 -080064
Lajos Molnar1cd13982014-01-17 15:12:51 -080065 sp<Surface> surface = NULL;
66 if (mNativeWindow != NULL) {
67 surface = mNativeWindow->getSurfaceTextureClient();
Andreas Huber84066782011-08-16 09:34:26 -070068 }
Andreas Huberf9334412010-12-15 15:17:42 -080069
Lajos Molnar1cd13982014-01-17 15:12:51 -080070 mComponentName = mime;
71 mComponentName.append(" decoder");
72 ALOGV("[%s] onConfigure (surface=%p)", mComponentName.c_str(), surface.get());
73
74 mCodec = MediaCodec::CreateByType(mCodecLooper, mime.c_str(), false /* encoder */);
75 if (mCodec == NULL) {
76 ALOGE("Failed to create %s decoder", mime.c_str());
77 handleError(UNKNOWN_ERROR);
78 return;
79 }
80
81 mCodec->getName(&mComponentName);
82
Glenn Kasten11731182011-02-08 17:26:17 -080083 if (mNativeWindow != NULL) {
Lajos Molnar1cd13982014-01-17 15:12:51 -080084 // disconnect from surface as MediaCodec will reconnect
85 CHECK_EQ((int)NO_ERROR,
86 native_window_api_disconnect(
87 surface.get(),
88 NATIVE_WINDOW_API_MEDIA));
89 }
90 status_t err = mCodec->configure(
91 format, surface, NULL /* crypto */, 0 /* flags */);
92 if (err != OK) {
93 ALOGE("Failed to configure %s decoder (err=%d)", mComponentName.c_str(), err);
94 handleError(err);
95 return;
96 }
97 // the following should work in configured state
98 CHECK_EQ((status_t)OK, mCodec->getOutputFormat(&mOutputFormat));
99 CHECK_EQ((status_t)OK, mCodec->getInputFormat(&mInputFormat));
100
101 err = mCodec->start();
102 if (err != OK) {
103 ALOGE("Failed to start %s decoder (err=%d)", mComponentName.c_str(), err);
104 handleError(err);
105 return;
Andreas Huberf9334412010-12-15 15:17:42 -0800106 }
107
Lajos Molnar1cd13982014-01-17 15:12:51 -0800108 // the following should work after start
109 CHECK_EQ((status_t)OK, mCodec->getInputBuffers(&mInputBuffers));
110 CHECK_EQ((status_t)OK, mCodec->getOutputBuffers(&mOutputBuffers));
111 ALOGV("[%s] got %zu input and %zu output buffers",
112 mComponentName.c_str(),
113 mInputBuffers.size(),
114 mOutputBuffers.size());
Andreas Huber078cfcf2011-09-15 12:25:04 -0700115
Lajos Molnar1cd13982014-01-17 15:12:51 -0800116 requestCodecNotification();
Wei Jia704e7262014-06-04 16:21:56 -0700117 mPaused = false;
Lajos Molnar1cd13982014-01-17 15:12:51 -0800118}
Andreas Huber078cfcf2011-09-15 12:25:04 -0700119
Lajos Molnar1cd13982014-01-17 15:12:51 -0800120void NuPlayer::Decoder::requestCodecNotification() {
121 if (mCodec != NULL) {
122 sp<AMessage> reply = new AMessage(kWhatCodecNotify, id());
123 reply->setInt32("generation", mBufferGeneration);
124 mCodec->requestActivityNotification(reply);
125 }
126}
127
128bool NuPlayer::Decoder::isStaleReply(const sp<AMessage> &msg) {
129 int32_t generation;
130 CHECK(msg->findInt32("generation", &generation));
131 return generation != mBufferGeneration;
132}
133
134void NuPlayer::Decoder::init() {
135 mDecoderLooper->registerHandler(this);
136}
137
138void NuPlayer::Decoder::configure(const sp<AMessage> &format) {
139 sp<AMessage> msg = new AMessage(kWhatConfigure, id());
140 msg->setMessage("format", format);
141 msg->post();
142}
143
144void NuPlayer::Decoder::handleError(int32_t err)
145{
146 sp<AMessage> notify = mNotify->dup();
147 notify->setInt32("what", kWhatError);
148 notify->setInt32("err", err);
149 notify->post();
150}
151
152bool NuPlayer::Decoder::handleAnInputBuffer() {
153 size_t bufferIx = -1;
154 status_t res = mCodec->dequeueInputBuffer(&bufferIx);
155 ALOGV("[%s] dequeued input: %d",
156 mComponentName.c_str(), res == OK ? (int)bufferIx : res);
157 if (res != OK) {
158 if (res != -EAGAIN) {
159 handleError(res);
160 }
161 return false;
Andreas Huber078cfcf2011-09-15 12:25:04 -0700162 }
163
Lajos Molnar1cd13982014-01-17 15:12:51 -0800164 CHECK_LT(bufferIx, mInputBuffers.size());
Andreas Huberf9334412010-12-15 15:17:42 -0800165
Lajos Molnar1cd13982014-01-17 15:12:51 -0800166 sp<AMessage> reply = new AMessage(kWhatInputBufferFilled, id());
167 reply->setSize("buffer-ix", bufferIx);
168 reply->setInt32("generation", mBufferGeneration);
169
170 sp<AMessage> notify = mNotify->dup();
171 notify->setInt32("what", kWhatFillThisBuffer);
172 notify->setBuffer("buffer", mInputBuffers[bufferIx]);
173 notify->setMessage("reply", reply);
174 notify->post();
175 return true;
176}
177
178void android::NuPlayer::Decoder::onInputBufferFilled(const sp<AMessage> &msg) {
179 size_t bufferIx;
180 CHECK(msg->findSize("buffer-ix", &bufferIx));
181 CHECK_LT(bufferIx, mInputBuffers.size());
182 sp<ABuffer> codecBuffer = mInputBuffers[bufferIx];
183
184 sp<ABuffer> buffer;
185 bool hasBuffer = msg->findBuffer("buffer", &buffer);
186 if (buffer == NULL /* includes !hasBuffer */) {
187 int32_t streamErr = ERROR_END_OF_STREAM;
188 CHECK(msg->findInt32("err", &streamErr) || !hasBuffer);
189
190 if (streamErr == OK) {
191 /* buffers are returned to hold on to */
192 return;
193 }
194
195 // attempt to queue EOS
196 status_t err = mCodec->queueInputBuffer(
197 bufferIx,
198 0,
199 0,
200 0,
201 MediaCodec::BUFFER_FLAG_EOS);
202 if (streamErr == ERROR_END_OF_STREAM && err != OK) {
203 streamErr = err;
204 // err will not be ERROR_END_OF_STREAM
205 }
206
207 if (streamErr != ERROR_END_OF_STREAM) {
208 handleError(streamErr);
209 }
210 } else {
211 int64_t timeUs = 0;
212 uint32_t flags = 0;
213 CHECK(buffer->meta()->findInt64("timeUs", &timeUs));
214
215 int32_t eos;
216 // we do not expect CODECCONFIG or SYNCFRAME for decoder
217 if (buffer->meta()->findInt32("eos", &eos) && eos) {
218 flags |= MediaCodec::BUFFER_FLAG_EOS;
219 }
220
221 // copy into codec buffer
222 if (buffer != codecBuffer) {
223 CHECK_LE(buffer->size(), codecBuffer->capacity());
224 codecBuffer->setRange(0, buffer->size());
225 memcpy(codecBuffer->data(), buffer->data(), buffer->size());
226 }
227
228 status_t err = mCodec->queueInputBuffer(
229 bufferIx,
230 codecBuffer->offset(),
231 codecBuffer->size(),
232 timeUs,
233 flags);
234 if (err != OK) {
235 ALOGE("Failed to queue input buffer for %s (err=%d)",
236 mComponentName.c_str(), err);
237 handleError(err);
238 }
239 }
240}
241
242bool NuPlayer::Decoder::handleAnOutputBuffer() {
243 size_t bufferIx = -1;
244 size_t offset;
245 size_t size;
246 int64_t timeUs;
247 uint32_t flags;
248 status_t res = mCodec->dequeueOutputBuffer(
249 &bufferIx, &offset, &size, &timeUs, &flags);
250
251 if (res != OK) {
252 ALOGV("[%s] dequeued output: %d", mComponentName.c_str(), res);
253 } else {
254 ALOGV("[%s] dequeued output: %d (time=%lld flags=%" PRIu32 ")",
255 mComponentName.c_str(), (int)bufferIx, timeUs, flags);
256 }
257
258 if (res == INFO_OUTPUT_BUFFERS_CHANGED) {
259 res = mCodec->getOutputBuffers(&mOutputBuffers);
260 if (res != OK) {
261 ALOGE("Failed to get output buffers for %s after INFO event (err=%d)",
262 mComponentName.c_str(), res);
263 handleError(res);
264 return false;
265 }
266 // NuPlayer ignores this
267 return true;
268 } else if (res == INFO_FORMAT_CHANGED) {
269 sp<AMessage> format = new AMessage();
270 res = mCodec->getOutputFormat(&format);
271 if (res != OK) {
272 ALOGE("Failed to get output format for %s after INFO event (err=%d)",
273 mComponentName.c_str(), res);
274 handleError(res);
275 return false;
276 }
277
278 sp<AMessage> notify = mNotify->dup();
279 notify->setInt32("what", kWhatOutputFormatChanged);
280 notify->setMessage("format", format);
281 notify->post();
282 return true;
283 } else if (res == INFO_DISCONTINUITY) {
284 // nothing to do
285 return true;
286 } else if (res != OK) {
287 if (res != -EAGAIN) {
288 handleError(res);
289 }
290 return false;
291 }
292
293 CHECK_LT(bufferIx, mOutputBuffers.size());
294 sp<ABuffer> buffer = mOutputBuffers[bufferIx];
295 buffer->setRange(offset, size);
296 buffer->meta()->clear();
297 buffer->meta()->setInt64("timeUs", timeUs);
298 if (flags & MediaCodec::BUFFER_FLAG_EOS) {
299 buffer->meta()->setInt32("eos", true);
300 }
301 // we do not expect CODECCONFIG or SYNCFRAME for decoder
302
303 sp<AMessage> reply = new AMessage(kWhatRenderBuffer, id());
304 reply->setSize("buffer-ix", bufferIx);
305 reply->setInt32("generation", mBufferGeneration);
306
307 sp<AMessage> notify = mNotify->dup();
308 notify->setInt32("what", kWhatDrainThisBuffer);
309 notify->setBuffer("buffer", buffer);
310 notify->setMessage("reply", reply);
311 notify->post();
312
313 // FIXME: This should be handled after rendering is complete,
314 // but Renderer needs it now
315 if (flags & MediaCodec::BUFFER_FLAG_EOS) {
316 ALOGV("queueing eos [%s]", mComponentName.c_str());
317 sp<AMessage> notify = mNotify->dup();
318 notify->setInt32("what", kWhatEOS);
319 notify->setInt32("err", ERROR_END_OF_STREAM);
320 notify->post();
321 }
322 return true;
323}
324
325void NuPlayer::Decoder::onRenderBuffer(const sp<AMessage> &msg) {
326 status_t err;
327 int32_t render;
328 size_t bufferIx;
329 CHECK(msg->findSize("buffer-ix", &bufferIx));
330 if (msg->findInt32("render", &render) && render) {
331 err = mCodec->renderOutputBufferAndRelease(bufferIx);
332 } else {
333 err = mCodec->releaseOutputBuffer(bufferIx);
334 }
335 if (err != OK) {
336 ALOGE("failed to release output buffer for %s (err=%d)",
337 mComponentName.c_str(), err);
338 handleError(err);
339 }
340}
341
342void NuPlayer::Decoder::onFlush() {
343 status_t err = OK;
344 if (mCodec != NULL) {
345 err = mCodec->flush();
346 ++mBufferGeneration;
347 }
348
349 if (err != OK) {
350 ALOGE("failed to flush %s (err=%d)", mComponentName.c_str(), err);
351 handleError(err);
352 return;
353 }
354
355 sp<AMessage> notify = mNotify->dup();
356 notify->setInt32("what", kWhatFlushCompleted);
357 notify->post();
Wei Jia704e7262014-06-04 16:21:56 -0700358 mPaused = true;
359}
360
361void NuPlayer::Decoder::onResume() {
362 mPaused = false;
Lajos Molnar1cd13982014-01-17 15:12:51 -0800363}
364
365void NuPlayer::Decoder::onShutdown() {
366 status_t err = OK;
367 if (mCodec != NULL) {
368 err = mCodec->release();
369 mCodec = NULL;
370 ++mBufferGeneration;
371
372 if (mNativeWindow != NULL) {
373 // reconnect to surface as MediaCodec disconnected from it
374 CHECK_EQ((int)NO_ERROR,
375 native_window_api_connect(
376 mNativeWindow->getNativeWindow().get(),
377 NATIVE_WINDOW_API_MEDIA));
378 }
379 mComponentName = "decoder";
380 }
381
382 if (err != OK) {
383 ALOGE("failed to release %s (err=%d)", mComponentName.c_str(), err);
384 handleError(err);
385 return;
386 }
387
388 sp<AMessage> notify = mNotify->dup();
389 notify->setInt32("what", kWhatShutdownCompleted);
390 notify->post();
Wei Jia704e7262014-06-04 16:21:56 -0700391 mPaused = true;
Andreas Huberf9334412010-12-15 15:17:42 -0800392}
393
394void NuPlayer::Decoder::onMessageReceived(const sp<AMessage> &msg) {
Lajos Molnar1cd13982014-01-17 15:12:51 -0800395 ALOGV("[%s] onMessage: %s", mComponentName.c_str(), msg->debugString().c_str());
396
Andreas Huberf9334412010-12-15 15:17:42 -0800397 switch (msg->what()) {
Lajos Molnar1cd13982014-01-17 15:12:51 -0800398 case kWhatConfigure:
399 {
400 sp<AMessage> format;
401 CHECK(msg->findMessage("format", &format));
402 onConfigure(format);
403 break;
404 }
405
Andreas Huberf9334412010-12-15 15:17:42 -0800406 case kWhatCodecNotify:
407 {
Lajos Molnar1cd13982014-01-17 15:12:51 -0800408 if (!isStaleReply(msg)) {
Wei Jia704e7262014-06-04 16:21:56 -0700409 if (!mPaused) {
410 while (handleAnInputBuffer()) {
411 }
Lajos Molnar1cd13982014-01-17 15:12:51 -0800412 }
Andreas Huberf9334412010-12-15 15:17:42 -0800413
Lajos Molnar1cd13982014-01-17 15:12:51 -0800414 while (handleAnOutputBuffer()) {
415 }
Andreas Huberf9334412010-12-15 15:17:42 -0800416 }
Lajos Molnar1cd13982014-01-17 15:12:51 -0800417
418 requestCodecNotification();
419 break;
420 }
421
422 case kWhatInputBufferFilled:
423 {
424 if (!isStaleReply(msg)) {
425 onInputBufferFilled(msg);
426 }
427 break;
428 }
429
430 case kWhatRenderBuffer:
431 {
432 if (!isStaleReply(msg)) {
433 onRenderBuffer(msg);
434 }
435 break;
436 }
437
438 case kWhatFlush:
439 {
440 onFlush();
441 break;
442 }
443
Wei Jia704e7262014-06-04 16:21:56 -0700444 case kWhatResume:
445 {
446 onResume();
447 break;
448 }
449
Lajos Molnar1cd13982014-01-17 15:12:51 -0800450 case kWhatShutdown:
451 {
452 onShutdown();
Andreas Huberf9334412010-12-15 15:17:42 -0800453 break;
454 }
455
456 default:
457 TRESPASS();
458 break;
459 }
460}
461
Andreas Huberf9334412010-12-15 15:17:42 -0800462void NuPlayer::Decoder::signalFlush() {
Lajos Molnar1cd13982014-01-17 15:12:51 -0800463 (new AMessage(kWhatFlush, id()))->post();
Andreas Huberf9334412010-12-15 15:17:42 -0800464}
465
466void NuPlayer::Decoder::signalResume() {
Wei Jia704e7262014-06-04 16:21:56 -0700467 (new AMessage(kWhatResume, id()))->post();
Andreas Huberf9334412010-12-15 15:17:42 -0800468}
469
Andreas Huber3831a062010-12-21 10:22:33 -0800470void NuPlayer::Decoder::initiateShutdown() {
Lajos Molnar1cd13982014-01-17 15:12:51 -0800471 (new AMessage(kWhatShutdown, id()))->post();
Andreas Huber3831a062010-12-21 10:22:33 -0800472}
473
Robert Shih6d0a94e2014-01-23 16:18:22 -0800474bool NuPlayer::Decoder::supportsSeamlessAudioFormatChange(const sp<AMessage> &targetFormat) const {
475 if (targetFormat == NULL) {
476 return true;
477 }
478
479 AString mime;
480 if (!targetFormat->findString("mime", &mime)) {
481 return false;
482 }
483
484 if (!strcasecmp(mime.c_str(), MEDIA_MIMETYPE_AUDIO_AAC)) {
485 // field-by-field comparison
486 const char * keys[] = { "channel-count", "sample-rate", "is-adts" };
487 for (unsigned int i = 0; i < sizeof(keys) / sizeof(keys[0]); i++) {
488 int32_t oldVal, newVal;
Lajos Molnar1cd13982014-01-17 15:12:51 -0800489 if (!mOutputFormat->findInt32(keys[i], &oldVal) ||
490 !targetFormat->findInt32(keys[i], &newVal) ||
491 oldVal != newVal) {
Robert Shih6d0a94e2014-01-23 16:18:22 -0800492 return false;
493 }
494 }
495
496 sp<ABuffer> oldBuf, newBuf;
Lajos Molnar1cd13982014-01-17 15:12:51 -0800497 if (mOutputFormat->findBuffer("csd-0", &oldBuf) &&
498 targetFormat->findBuffer("csd-0", &newBuf)) {
Robert Shih6d0a94e2014-01-23 16:18:22 -0800499 if (oldBuf->size() != newBuf->size()) {
500 return false;
501 }
502 return !memcmp(oldBuf->data(), newBuf->data(), oldBuf->size());
503 }
504 }
505 return false;
506}
507
508bool NuPlayer::Decoder::supportsSeamlessFormatChange(const sp<AMessage> &targetFormat) const {
Lajos Molnar1cd13982014-01-17 15:12:51 -0800509 if (mOutputFormat == NULL) {
Robert Shih6d0a94e2014-01-23 16:18:22 -0800510 return false;
511 }
512
513 if (targetFormat == NULL) {
514 return true;
515 }
516
517 AString oldMime, newMime;
Lajos Molnar1cd13982014-01-17 15:12:51 -0800518 if (!mOutputFormat->findString("mime", &oldMime)
Robert Shih6d0a94e2014-01-23 16:18:22 -0800519 || !targetFormat->findString("mime", &newMime)
520 || !(oldMime == newMime)) {
521 return false;
522 }
523
524 bool audio = !strncasecmp(oldMime.c_str(), "audio/", strlen("audio/"));
525 bool seamless;
526 if (audio) {
527 seamless = supportsSeamlessAudioFormatChange(targetFormat);
528 } else {
Lajos Molnar1cd13982014-01-17 15:12:51 -0800529 int32_t isAdaptive;
530 seamless = (mCodec != NULL &&
531 mInputFormat->findInt32("adaptive-playback", &isAdaptive) &&
532 isAdaptive);
Robert Shih6d0a94e2014-01-23 16:18:22 -0800533 }
534
535 ALOGV("%s seamless support for %s", seamless ? "yes" : "no", oldMime.c_str());
536 return seamless;
537}
538
Chong Zhanga7fa1d92014-06-11 14:49:23 -0700539struct NuPlayer::CCDecoder::CCData {
540 CCData(uint8_t type, uint8_t data1, uint8_t data2)
541 : mType(type), mData1(data1), mData2(data2) {
542 }
543
544 uint8_t mType;
545 uint8_t mData1;
546 uint8_t mData2;
547};
548
549NuPlayer::CCDecoder::CCDecoder(const sp<AMessage> &notify)
550 : mNotify(notify),
551 mTrackCount(0),
552 mSelectedTrack(-1) {
553}
554
555size_t NuPlayer::CCDecoder::getTrackCount() const {
556 return mTrackCount;
557}
558
559sp<AMessage> NuPlayer::CCDecoder::getTrackInfo(size_t index) const {
560 CHECK(index == 0);
561
562 sp<AMessage> format = new AMessage();
563
564 format->setInt32("type", MEDIA_TRACK_TYPE_SUBTITLE);
565 format->setString("language", "und");
566 format->setString("mime", MEDIA_MIMETYPE_TEXT_CEA_608);
567 format->setInt32("auto", 1);
568 format->setInt32("default", 1);
569 format->setInt32("forced", 0);
570
571 return format;
572}
573
574status_t NuPlayer::CCDecoder::selectTrack(size_t index, bool select) {
575 CHECK(index < mTrackCount);
576
577 if (select) {
578 if (mSelectedTrack == (ssize_t)index) {
579 ALOGE("track %zu already selected", index);
580 return BAD_VALUE;
581 }
582 ALOGV("selected track %zu", index);
583 mSelectedTrack = index;
584 } else {
585 if (mSelectedTrack != (ssize_t)index) {
586 ALOGE("track %zu is not selected", index);
587 return BAD_VALUE;
588 }
589 ALOGV("unselected track %zu", index);
590 mSelectedTrack = -1;
591 }
592
593 return OK;
594}
595
596bool NuPlayer::CCDecoder::isSelected() const {
597 return mSelectedTrack >= 0 && mSelectedTrack < (int32_t)mTrackCount;
598}
599
600bool NuPlayer::CCDecoder::isNullPad(CCData *cc) const {
601 return cc->mData1 < 0x10 && cc->mData2 < 0x10;
602}
603
604void NuPlayer::CCDecoder::dumpBytePair(const sp<ABuffer> &ccBuf) const {
605 size_t offset = 0;
606 AString out;
607
608 while (offset < ccBuf->size()) {
609 char tmp[128];
610
611 CCData *cc = (CCData *) (ccBuf->data() + offset);
612
613 if (isNullPad(cc)) {
614 // 1 null pad or XDS metadata, ignore
615 offset += sizeof(CCData);
616 continue;
617 }
618
619 if (cc->mData1 >= 0x20 && cc->mData1 <= 0x7f) {
620 // 2 basic chars
621 sprintf(tmp, "[%d]Basic: %c %c", cc->mType, cc->mData1, cc->mData2);
622 } else if ((cc->mData1 == 0x11 || cc->mData1 == 0x19)
623 && cc->mData2 >= 0x30 && cc->mData2 <= 0x3f) {
624 // 1 special char
625 sprintf(tmp, "[%d]Special: %02x %02x", cc->mType, cc->mData1, cc->mData2);
626 } else if ((cc->mData1 == 0x12 || cc->mData1 == 0x1A)
627 && cc->mData2 >= 0x20 && cc->mData2 <= 0x3f){
628 // 1 Spanish/French char
629 sprintf(tmp, "[%d]Spanish: %02x %02x", cc->mType, cc->mData1, cc->mData2);
630 } else if ((cc->mData1 == 0x13 || cc->mData1 == 0x1B)
631 && cc->mData2 >= 0x20 && cc->mData2 <= 0x3f){
632 // 1 Portuguese/German/Danish char
633 sprintf(tmp, "[%d]German: %02x %02x", cc->mType, cc->mData1, cc->mData2);
634 } else if ((cc->mData1 == 0x11 || cc->mData1 == 0x19)
635 && cc->mData2 >= 0x20 && cc->mData2 <= 0x2f){
636 // Mid-Row Codes (Table 69)
637 sprintf(tmp, "[%d]Mid-row: %02x %02x", cc->mType, cc->mData1, cc->mData2);
638 } else if (((cc->mData1 == 0x14 || cc->mData1 == 0x1c)
639 && cc->mData2 >= 0x20 && cc->mData2 <= 0x2f)
640 ||
641 ((cc->mData1 == 0x17 || cc->mData1 == 0x1f)
642 && cc->mData2 >= 0x21 && cc->mData2 <= 0x23)){
643 // Misc Control Codes (Table 70)
644 sprintf(tmp, "[%d]Ctrl: %02x %02x", cc->mType, cc->mData1, cc->mData2);
645 } else if ((cc->mData1 & 0x70) == 0x10
646 && (cc->mData2 & 0x40) == 0x40
647 && ((cc->mData1 & 0x07) || !(cc->mData2 & 0x20)) ) {
648 // Preamble Address Codes (Table 71)
649 sprintf(tmp, "[%d]PAC: %02x %02x", cc->mType, cc->mData1, cc->mData2);
650 } else {
651 sprintf(tmp, "[%d]Invalid: %02x %02x", cc->mType, cc->mData1, cc->mData2);
652 }
653
654 if (out.size() > 0) {
655 out.append(", ");
656 }
657
658 out.append(tmp);
659
660 offset += sizeof(CCData);
661 }
662
663 ALOGI("%s", out.c_str());
664}
665
666bool NuPlayer::CCDecoder::extractFromSEI(const sp<ABuffer> &accessUnit) {
667 int64_t timeUs;
668 CHECK(accessUnit->meta()->findInt64("timeUs", &timeUs));
669
670 sp<ABuffer> sei;
671 if (!accessUnit->meta()->findBuffer("sei", &sei) || sei == NULL) {
672 return false;
673 }
674
675 bool hasCC = false;
676
677 ABitReader br(sei->data() + 1, sei->size() - 1);
678 // sei_message()
679 while (br.numBitsLeft() >= 16) { // at least 16-bit for sei_message()
680 uint32_t payload_type = 0;
681 size_t payload_size = 0;
682 uint8_t last_byte;
683
684 do {
685 last_byte = br.getBits(8);
686 payload_type += last_byte;
687 } while (last_byte == 0xFF);
688
689 do {
690 last_byte = br.getBits(8);
691 payload_size += last_byte;
692 } while (last_byte == 0xFF);
693
694 // sei_payload()
695 if (payload_type == 4) {
696 // user_data_registered_itu_t_t35()
697
698 // ATSC A/72: 6.4.2
699 uint8_t itu_t_t35_country_code = br.getBits(8);
700 uint16_t itu_t_t35_provider_code = br.getBits(16);
701 uint32_t user_identifier = br.getBits(32);
702 uint8_t user_data_type_code = br.getBits(8);
703
704 payload_size -= 1 + 2 + 4 + 1;
705
706 if (itu_t_t35_country_code == 0xB5
707 && itu_t_t35_provider_code == 0x0031
708 && user_identifier == 'GA94'
709 && user_data_type_code == 0x3) {
710 hasCC = true;
711
712 // MPEG_cc_data()
713 // ATSC A/53 Part 4: 6.2.3.1
714 br.skipBits(1); //process_em_data_flag
715 bool process_cc_data_flag = br.getBits(1);
716 br.skipBits(1); //additional_data_flag
717 size_t cc_count = br.getBits(5);
718 br.skipBits(8); // em_data;
719 payload_size -= 2;
720
721 if (process_cc_data_flag) {
722 AString out;
723
724 sp<ABuffer> ccBuf = new ABuffer(cc_count * sizeof(CCData));
725 ccBuf->setRange(0, 0);
726
727 for (size_t i = 0; i < cc_count; i++) {
728 uint8_t marker = br.getBits(5);
729 CHECK_EQ(marker, 0x1f);
730
731 bool cc_valid = br.getBits(1);
732 uint8_t cc_type = br.getBits(2);
733 // remove odd parity bit
734 uint8_t cc_data_1 = br.getBits(8) & 0x7f;
735 uint8_t cc_data_2 = br.getBits(8) & 0x7f;
736
737 if (cc_valid
738 && (cc_type == 0 || cc_type == 1)) {
739 CCData cc(cc_type, cc_data_1, cc_data_2);
740 if (!isNullPad(&cc)) {
741 memcpy(ccBuf->data() + ccBuf->size(),
742 (void *)&cc, sizeof(cc));
743 ccBuf->setRange(0, ccBuf->size() + sizeof(CCData));
744 }
745 }
746 }
747 payload_size -= cc_count * 3;
748
749 mCCMap.add(timeUs, ccBuf);
750 break;
751 }
752 } else {
753 ALOGV("Malformed SEI payload type 4");
754 }
755 } else {
756 ALOGV("Unsupported SEI payload type %d", payload_type);
757 }
758
759 // skipping remaining bits of this payload
760 br.skipBits(payload_size * 8);
761 }
762
763 return hasCC;
764}
765
766void NuPlayer::CCDecoder::decode(const sp<ABuffer> &accessUnit) {
767 if (extractFromSEI(accessUnit) && mTrackCount == 0) {
768 mTrackCount++;
769
770 ALOGI("Found CEA-608 track");
771 sp<AMessage> msg = mNotify->dup();
772 msg->setInt32("what", kWhatTrackAdded);
773 msg->post();
774 }
775 // TODO: extract CC from other sources
776}
777
778void NuPlayer::CCDecoder::display(int64_t timeUs) {
779 ssize_t index = mCCMap.indexOfKey(timeUs);
780 if (index < 0) {
781 ALOGV("cc for timestamp %" PRId64 " not found", timeUs);
782 return;
783 }
784
785 sp<ABuffer> &ccBuf = mCCMap.editValueAt(index);
786
787 if (ccBuf->size() > 0) {
788#if 0
789 dumpBytePair(ccBuf);
790#endif
791
792 ccBuf->meta()->setInt32("trackIndex", mSelectedTrack);
793 ccBuf->meta()->setInt64("timeUs", timeUs);
794 ccBuf->meta()->setInt64("durationUs", 0ll);
795
796 sp<AMessage> msg = mNotify->dup();
797 msg->setInt32("what", kWhatClosedCaptionData);
798 msg->setBuffer("buffer", ccBuf);
799 msg->post();
800 }
801
802 // remove all entries before timeUs
803 mCCMap.removeItemsAt(0, index + 1);
804}
805
Andreas Huberf9334412010-12-15 15:17:42 -0800806} // namespace android
807