1 /*
2 * Copyright 2017 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 "NuPlayer2Decoder"
19 #include <utils/Log.h>
20 #include <inttypes.h>
21
22 #include <algorithm>
23
24 #include "NuPlayer2CCDecoder.h"
25 #include "NuPlayer2Decoder.h"
26 #include "NuPlayer2Drm.h"
27 #include "NuPlayer2Renderer.h"
28 #include "NuPlayer2Source.h"
29
30 #include <cutils/properties.h>
31 #include <media/MediaBufferHolder.h>
32 #include <media/MediaCodecBuffer.h>
33 #include <media/NdkMediaCodec.h>
34 #include <media/NdkWrapper.h>
35 #include <media/stagefright/foundation/ABuffer.h>
36 #include <media/stagefright/foundation/ADebug.h>
37 #include <media/stagefright/foundation/AMessage.h>
38 #include <media/stagefright/foundation/avc_utils.h>
39 #include <media/stagefright/MediaBuffer.h>
40 #include <media/stagefright/MediaDefs.h>
41 #include <media/stagefright/MediaErrors.h>
42 #include <media/stagefright/SurfaceUtils.h>
43
44 #include <system/window.h>
45 #include "ATSParser.h"
46
47 namespace android {
48
49 static float kDisplayRefreshingRate = 60.f; // TODO: get this from the display
50
51 // The default total video frame rate of a stream when that info is not available from
52 // the source.
53 static float kDefaultVideoFrameRateTotal = 30.f;
54
getAudioDeepBufferSetting()55 static inline bool getAudioDeepBufferSetting() {
56 return property_get_bool("media.stagefright.audio.deep", false /* default_value */);
57 }
58
Decoder(const sp<AMessage> & notify,const sp<Source> & source,pid_t pid,uid_t uid,const sp<Renderer> & renderer,const sp<ANativeWindowWrapper> & nww,const sp<CCDecoder> & ccDecoder)59 NuPlayer2::Decoder::Decoder(
60 const sp<AMessage> ¬ify,
61 const sp<Source> &source,
62 pid_t pid,
63 uid_t uid,
64 const sp<Renderer> &renderer,
65 const sp<ANativeWindowWrapper> &nww,
66 const sp<CCDecoder> &ccDecoder)
67 : DecoderBase(notify),
68 mNativeWindow(nww),
69 mSource(source),
70 mRenderer(renderer),
71 mCCDecoder(ccDecoder),
72 mPid(pid),
73 mUid(uid),
74 mSkipRenderingUntilMediaTimeUs(-1ll),
75 mNumFramesTotal(0ll),
76 mNumInputFramesDropped(0ll),
77 mNumOutputFramesDropped(0ll),
78 mVideoWidth(0),
79 mVideoHeight(0),
80 mIsAudio(true),
81 mIsVideoAVC(false),
82 mIsSecure(false),
83 mIsEncrypted(false),
84 mIsEncryptedObservedEarlier(false),
85 mFormatChangePending(false),
86 mTimeChangePending(false),
87 mFrameRateTotal(kDefaultVideoFrameRateTotal),
88 mPlaybackSpeed(1.0f),
89 mNumVideoTemporalLayerTotal(1), // decode all layers
90 mNumVideoTemporalLayerAllowed(1),
91 mCurrentMaxVideoTemporalLayerId(0),
92 mResumePending(false),
93 mComponentName("decoder") {
94 mVideoTemporalLayerAggregateFps[0] = mFrameRateTotal;
95 }
96
~Decoder()97 NuPlayer2::Decoder::~Decoder() {
98 // Need to stop looper first since mCodec could be accessed on the mDecoderLooper.
99 stopLooper();
100 if (mCodec != NULL) {
101 mCodec->release();
102 }
103 releaseAndResetMediaBuffers();
104 }
105
getStats() const106 sp<AMessage> NuPlayer2::Decoder::getStats() const {
107 mStats->setInt64("frames-total", mNumFramesTotal);
108 mStats->setInt64("frames-dropped-input", mNumInputFramesDropped);
109 mStats->setInt64("frames-dropped-output", mNumOutputFramesDropped);
110 return mStats;
111 }
112
setVideoSurface(const sp<ANativeWindowWrapper> & nww)113 status_t NuPlayer2::Decoder::setVideoSurface(const sp<ANativeWindowWrapper> &nww) {
114 if (nww == NULL || nww->getANativeWindow() == NULL
115 || ADebug::isExperimentEnabled("legacy-setsurface")) {
116 return BAD_VALUE;
117 }
118
119 sp<AMessage> msg = new AMessage(kWhatSetVideoSurface, this);
120
121 msg->setObject("surface", nww);
122 sp<AMessage> response;
123 status_t err = msg->postAndAwaitResponse(&response);
124 if (err == OK && response != NULL) {
125 CHECK(response->findInt32("err", &err));
126 }
127 return err;
128 }
129
onMessageReceived(const sp<AMessage> & msg)130 void NuPlayer2::Decoder::onMessageReceived(const sp<AMessage> &msg) {
131 ALOGV("[%s] onMessage: %s", mComponentName.c_str(), msg->debugString().c_str());
132
133 switch (msg->what()) {
134 case kWhatCodecNotify:
135 {
136 int32_t cbID;
137 CHECK(msg->findInt32("callbackID", &cbID));
138
139 ALOGV("[%s] kWhatCodecNotify: cbID = %d, paused = %d",
140 mIsAudio ? "audio" : "video", cbID, mPaused);
141
142 if (mPaused) {
143 break;
144 }
145
146 switch (cbID) {
147 case AMediaCodecWrapper::CB_INPUT_AVAILABLE:
148 {
149 int32_t index;
150 CHECK(msg->findInt32("index", &index));
151
152 handleAnInputBuffer(index);
153 break;
154 }
155
156 case AMediaCodecWrapper::CB_OUTPUT_AVAILABLE:
157 {
158 int32_t index;
159 size_t offset;
160 size_t size;
161 int64_t timeUs;
162 int32_t flags;
163
164 CHECK(msg->findInt32("index", &index));
165 CHECK(msg->findSize("offset", &offset));
166 CHECK(msg->findSize("size", &size));
167 CHECK(msg->findInt64("timeUs", &timeUs));
168 CHECK(msg->findInt32("flags", &flags));
169
170 handleAnOutputBuffer(index, offset, size, timeUs, flags);
171 break;
172 }
173
174 case AMediaCodecWrapper::CB_OUTPUT_FORMAT_CHANGED:
175 {
176 sp<AMessage> format;
177 CHECK(msg->findMessage("format", &format));
178
179 handleOutputFormatChange(format);
180 break;
181 }
182
183 case AMediaCodecWrapper::CB_ERROR:
184 {
185 status_t err;
186 CHECK(msg->findInt32("err", &err));
187 ALOGE("Decoder (%s) reported error : 0x%x",
188 mIsAudio ? "audio" : "video", err);
189
190 handleError(err);
191 break;
192 }
193
194 default:
195 {
196 TRESPASS();
197 break;
198 }
199 }
200
201 break;
202 }
203
204 case kWhatRenderBuffer:
205 {
206 if (!isStaleReply(msg)) {
207 onRenderBuffer(msg);
208 }
209 break;
210 }
211
212 case kWhatAudioOutputFormatChanged:
213 {
214 if (!isStaleReply(msg)) {
215 status_t err;
216 if (msg->findInt32("err", &err) && err != OK) {
217 ALOGE("Renderer reported 0x%x when changing audio output format", err);
218 handleError(err);
219 }
220 }
221 break;
222 }
223
224 case kWhatSetVideoSurface:
225 {
226 sp<AReplyToken> replyID;
227 CHECK(msg->senderAwaitsResponse(&replyID));
228
229 sp<RefBase> obj;
230 CHECK(msg->findObject("surface", &obj));
231 sp<ANativeWindowWrapper> nww =
232 static_cast<ANativeWindowWrapper *>(obj.get()); // non-null
233 if (nww == NULL || nww->getANativeWindow() == NULL) {
234 break;
235 }
236 int32_t err = INVALID_OPERATION;
237 // NOTE: in practice mNativeWindow is always non-null,
238 // but checking here for completeness
239 if (mCodec != NULL
240 && mNativeWindow != NULL && mNativeWindow->getANativeWindow() != NULL) {
241 // TODO: once AwesomePlayer is removed, remove this automatic connecting
242 // to the surface by MediaPlayerService.
243 //
244 // at this point MediaPlayer2Manager::client has already connected to the
245 // surface, which MediaCodec does not expect
246 err = native_window_api_disconnect(nww->getANativeWindow(),
247 NATIVE_WINDOW_API_MEDIA);
248 if (err == OK) {
249 err = mCodec->setOutputSurface(nww);
250 ALOGI_IF(err, "codec setOutputSurface returned: %d", err);
251 if (err == OK) {
252 // reconnect to the old surface as MPS::Client will expect to
253 // be able to disconnect from it.
254 (void)native_window_api_connect(mNativeWindow->getANativeWindow(),
255 NATIVE_WINDOW_API_MEDIA);
256
257 mNativeWindow = nww;
258 }
259 }
260 if (err != OK) {
261 // reconnect to the new surface on error as MPS::Client will expect to
262 // be able to disconnect from it.
263 (void)native_window_api_connect(nww->getANativeWindow(),
264 NATIVE_WINDOW_API_MEDIA);
265 }
266 }
267
268 sp<AMessage> response = new AMessage;
269 response->setInt32("err", err);
270 response->postReply(replyID);
271 break;
272 }
273
274 case kWhatDrmReleaseCrypto:
275 {
276 ALOGV("kWhatDrmReleaseCrypto");
277 onReleaseCrypto(msg);
278 break;
279 }
280
281 default:
282 DecoderBase::onMessageReceived(msg);
283 break;
284 }
285 }
286
onConfigure(const sp<AMessage> & format)287 void NuPlayer2::Decoder::onConfigure(const sp<AMessage> &format) {
288 ALOGV("[%s] onConfigure (format=%s)", mComponentName.c_str(), format->debugString().c_str());
289 CHECK(mCodec == NULL);
290
291 mFormatChangePending = false;
292 mTimeChangePending = false;
293
294 ++mBufferGeneration;
295
296 AString mime;
297 CHECK(format->findString("mime", &mime));
298
299 mIsAudio = !strncasecmp("audio/", mime.c_str(), 6);
300 mIsVideoAVC = !strcasecmp(MEDIA_MIMETYPE_VIDEO_AVC, mime.c_str());
301
302 mComponentName = mime;
303 mComponentName.append(" decoder");
304 ALOGV("[%s] onConfigure (nww=%p)", mComponentName.c_str(),
305 (mNativeWindow == NULL ? NULL : mNativeWindow->getANativeWindow()));
306
307 mCodec = AMediaCodecWrapper::CreateDecoderByType(mime);
308 int32_t secure = 0;
309 if (format->findInt32("secure", &secure) && secure != 0) {
310 if (mCodec != NULL) {
311 if (mCodec->getName(&mComponentName) == OK) {
312 mComponentName.append(".secure");
313 mCodec->release();
314 ALOGI("[%s] creating", mComponentName.c_str());
315 mCodec = AMediaCodecWrapper::CreateCodecByName(mComponentName);
316 } else {
317 mCodec = NULL;
318 }
319 }
320 }
321 if (mCodec == NULL) {
322 ALOGE("Failed to create %s%s decoder",
323 (secure ? "secure " : ""), mime.c_str());
324 handleError(NO_INIT);
325 return;
326 }
327 mIsSecure = secure;
328
329 mCodec->getName(&mComponentName);
330
331 status_t err;
332 if (mNativeWindow != NULL && mNativeWindow->getANativeWindow() != NULL) {
333 // disconnect from surface as MediaCodec will reconnect
334 err = native_window_api_disconnect(mNativeWindow->getANativeWindow(),
335 NATIVE_WINDOW_API_MEDIA);
336 // We treat this as a warning, as this is a preparatory step.
337 // Codec will try to connect to the surface, which is where
338 // any error signaling will occur.
339 ALOGW_IF(err != OK, "failed to disconnect from surface: %d", err);
340 }
341
342 // Modular DRM
343 sp<RefBase> objCrypto;
344 format->findObject("crypto", &objCrypto);
345 sp<AMediaCryptoWrapper> crypto = static_cast<AMediaCryptoWrapper *>(objCrypto.get());
346 // non-encrypted source won't have a crypto
347 mIsEncrypted = (crypto != NULL);
348 // configure is called once; still using OR in case the behavior changes.
349 mIsEncryptedObservedEarlier = mIsEncryptedObservedEarlier || mIsEncrypted;
350 ALOGV("onConfigure mCrypto: %p, mIsSecure: %d", crypto.get(), mIsSecure);
351
352 err = mCodec->configure(
353 AMediaFormatWrapper::Create(format),
354 mNativeWindow,
355 crypto,
356 0 /* flags */);
357
358 if (err != OK) {
359 ALOGE("Failed to configure [%s] decoder (err=%d)", mComponentName.c_str(), err);
360 mCodec->release();
361 mCodec.clear();
362 handleError(err);
363 return;
364 }
365 rememberCodecSpecificData(format);
366
367 // the following should work in configured state
368 sp<AMediaFormatWrapper> outputFormat = mCodec->getOutputFormat();
369 if (outputFormat == NULL) {
370 handleError(INVALID_OPERATION);
371 return;
372 }
373 mInputFormat = mCodec->getInputFormat();
374 if (mInputFormat == NULL) {
375 handleError(INVALID_OPERATION);
376 return;
377 }
378
379 mStats->setString("mime", mime.c_str());
380 mStats->setString("component-name", mComponentName.c_str());
381
382 if (!mIsAudio) {
383 int32_t width, height;
384 if (outputFormat->getInt32("width", &width)
385 && outputFormat->getInt32("height", &height)) {
386 mStats->setInt32("width", width);
387 mStats->setInt32("height", height);
388 }
389 }
390
391 sp<AMessage> reply = new AMessage(kWhatCodecNotify, this);
392 mCodec->setCallback(reply);
393
394 err = mCodec->start();
395 if (err != OK) {
396 ALOGE("Failed to start [%s] decoder (err=%d)", mComponentName.c_str(), err);
397 mCodec->release();
398 mCodec.clear();
399 handleError(err);
400 return;
401 }
402
403 releaseAndResetMediaBuffers();
404
405 mPaused = false;
406 mResumePending = false;
407 }
408
onSetParameters(const sp<AMessage> & params)409 void NuPlayer2::Decoder::onSetParameters(const sp<AMessage> ¶ms) {
410 bool needAdjustLayers = false;
411 float frameRateTotal;
412 if (params->findFloat("frame-rate-total", &frameRateTotal)
413 && mFrameRateTotal != frameRateTotal) {
414 needAdjustLayers = true;
415 mFrameRateTotal = frameRateTotal;
416 }
417
418 int32_t numVideoTemporalLayerTotal;
419 if (params->findInt32("temporal-layer-count", &numVideoTemporalLayerTotal)
420 && numVideoTemporalLayerTotal >= 0
421 && numVideoTemporalLayerTotal <= kMaxNumVideoTemporalLayers
422 && mNumVideoTemporalLayerTotal != numVideoTemporalLayerTotal) {
423 needAdjustLayers = true;
424 mNumVideoTemporalLayerTotal = std::max(numVideoTemporalLayerTotal, 1);
425 }
426
427 if (needAdjustLayers && mNumVideoTemporalLayerTotal > 1) {
428 // TODO: For now, layer fps is calculated for some specific architectures.
429 // But it really should be extracted from the stream.
430 mVideoTemporalLayerAggregateFps[0] =
431 mFrameRateTotal / (float)(1ll << (mNumVideoTemporalLayerTotal - 1));
432 for (int32_t i = 1; i < mNumVideoTemporalLayerTotal; ++i) {
433 mVideoTemporalLayerAggregateFps[i] =
434 mFrameRateTotal / (float)(1ll << (mNumVideoTemporalLayerTotal - i))
435 + mVideoTemporalLayerAggregateFps[i - 1];
436 }
437 }
438
439 float playbackSpeed;
440 if (params->findFloat("playback-speed", &playbackSpeed)
441 && mPlaybackSpeed != playbackSpeed) {
442 needAdjustLayers = true;
443 mPlaybackSpeed = playbackSpeed;
444 }
445
446 if (needAdjustLayers) {
447 float decodeFrameRate = mFrameRateTotal;
448 // enable temporal layering optimization only if we know the layering depth
449 if (mNumVideoTemporalLayerTotal > 1) {
450 int32_t layerId;
451 for (layerId = 0; layerId < mNumVideoTemporalLayerTotal - 1; ++layerId) {
452 if (mVideoTemporalLayerAggregateFps[layerId] * mPlaybackSpeed
453 >= kDisplayRefreshingRate * 0.9) {
454 break;
455 }
456 }
457 mNumVideoTemporalLayerAllowed = layerId + 1;
458 decodeFrameRate = mVideoTemporalLayerAggregateFps[layerId];
459 }
460 ALOGV("onSetParameters: allowed layers=%d, decodeFps=%g",
461 mNumVideoTemporalLayerAllowed, decodeFrameRate);
462
463 if (mCodec == NULL) {
464 ALOGW("onSetParameters called before codec is created.");
465 return;
466 }
467
468 sp<AMediaFormatWrapper> codecParams = new AMediaFormatWrapper();
469 codecParams->setFloat("operating-rate", decodeFrameRate * mPlaybackSpeed);
470 mCodec->setParameters(codecParams);
471 }
472 }
473
onSetRenderer(const sp<Renderer> & renderer)474 void NuPlayer2::Decoder::onSetRenderer(const sp<Renderer> &renderer) {
475 mRenderer = renderer;
476 }
477
onResume(bool notifyComplete)478 void NuPlayer2::Decoder::onResume(bool notifyComplete) {
479 mPaused = false;
480
481 if (notifyComplete) {
482 mResumePending = true;
483 }
484
485 if (mCodec == NULL) {
486 ALOGE("[%s] onResume without a valid codec", mComponentName.c_str());
487 handleError(NO_INIT);
488 return;
489 }
490 mCodec->start();
491 }
492
doFlush(bool notifyComplete)493 void NuPlayer2::Decoder::doFlush(bool notifyComplete) {
494 if (mCCDecoder != NULL) {
495 mCCDecoder->flush();
496 }
497
498 if (mRenderer != NULL) {
499 mRenderer->flush(mIsAudio, notifyComplete);
500 mRenderer->signalTimeDiscontinuity();
501 }
502
503 status_t err = OK;
504 if (mCodec != NULL) {
505 err = mCodec->flush();
506 mCSDsToSubmit = mCSDsForCurrentFormat; // copy operator
507 ++mBufferGeneration;
508 }
509
510 if (err != OK) {
511 ALOGE("failed to flush [%s] (err=%d)", mComponentName.c_str(), err);
512 handleError(err);
513 // finish with posting kWhatFlushCompleted.
514 // we attempt to release the buffers even if flush fails.
515 }
516 releaseAndResetMediaBuffers();
517 mPaused = true;
518 }
519
520
onFlush()521 void NuPlayer2::Decoder::onFlush() {
522 doFlush(true);
523
524 if (isDiscontinuityPending()) {
525 // This could happen if the client starts seeking/shutdown
526 // after we queued an EOS for discontinuities.
527 // We can consider discontinuity handled.
528 finishHandleDiscontinuity(false /* flushOnTimeChange */);
529 }
530
531 sp<AMessage> notify = mNotify->dup();
532 notify->setInt32("what", kWhatFlushCompleted);
533 notify->post();
534 }
535
onShutdown(bool notifyComplete)536 void NuPlayer2::Decoder::onShutdown(bool notifyComplete) {
537 status_t err = OK;
538
539 // if there is a pending resume request, notify complete now
540 notifyResumeCompleteIfNecessary();
541
542 if (mCodec != NULL) {
543 err = mCodec->release();
544 mCodec = NULL;
545 ++mBufferGeneration;
546
547 if (mNativeWindow != NULL && mNativeWindow->getANativeWindow() != NULL) {
548 // reconnect to surface as MediaCodec disconnected from it
549 status_t error = native_window_api_connect(mNativeWindow->getANativeWindow(),
550 NATIVE_WINDOW_API_MEDIA);
551 ALOGW_IF(error != NO_ERROR,
552 "[%s] failed to connect to native window, error=%d",
553 mComponentName.c_str(), error);
554 }
555 mComponentName = "decoder";
556 }
557
558 releaseAndResetMediaBuffers();
559
560 if (err != OK) {
561 ALOGE("failed to release [%s] (err=%d)", mComponentName.c_str(), err);
562 handleError(err);
563 // finish with posting kWhatShutdownCompleted.
564 }
565
566 if (notifyComplete) {
567 sp<AMessage> notify = mNotify->dup();
568 notify->setInt32("what", kWhatShutdownCompleted);
569 notify->post();
570 mPaused = true;
571 }
572 }
573
574 /*
575 * returns true if we should request more data
576 */
doRequestBuffers()577 bool NuPlayer2::Decoder::doRequestBuffers() {
578 if (isDiscontinuityPending()) {
579 return false;
580 }
581 status_t err = OK;
582 while (err == OK && !mDequeuedInputBuffers.empty()) {
583 size_t bufferIx = *mDequeuedInputBuffers.begin();
584 sp<AMessage> msg = new AMessage();
585 msg->setSize("buffer-ix", bufferIx);
586 err = fetchInputData(msg);
587 if (err != OK && err != ERROR_END_OF_STREAM) {
588 // if EOS, need to queue EOS buffer
589 break;
590 }
591 mDequeuedInputBuffers.erase(mDequeuedInputBuffers.begin());
592
593 if (!mPendingInputMessages.empty()
594 || !onInputBufferFetched(msg)) {
595 mPendingInputMessages.push_back(msg);
596 }
597 }
598
599 return err == -EWOULDBLOCK
600 && mSource->feedMoreTSData() == OK;
601 }
602
handleError(int32_t err)603 void NuPlayer2::Decoder::handleError(int32_t err)
604 {
605 // We cannot immediately release the codec due to buffers still outstanding
606 // in the renderer. We signal to the player the error so it can shutdown/release the
607 // decoder after flushing and increment the generation to discard unnecessary messages.
608
609 ++mBufferGeneration;
610
611 sp<AMessage> notify = mNotify->dup();
612 notify->setInt32("what", kWhatError);
613 notify->setInt32("err", err);
614 notify->post();
615 }
616
releaseCrypto()617 status_t NuPlayer2::Decoder::releaseCrypto()
618 {
619 ALOGV("releaseCrypto");
620
621 sp<AMessage> msg = new AMessage(kWhatDrmReleaseCrypto, this);
622
623 sp<AMessage> response;
624 status_t status = msg->postAndAwaitResponse(&response);
625 if (status == OK && response != NULL) {
626 CHECK(response->findInt32("status", &status));
627 ALOGV("releaseCrypto ret: %d ", status);
628 } else {
629 ALOGE("releaseCrypto err: %d", status);
630 }
631
632 return status;
633 }
634
onReleaseCrypto(const sp<AMessage> & msg)635 void NuPlayer2::Decoder::onReleaseCrypto(const sp<AMessage>& msg)
636 {
637 status_t status = INVALID_OPERATION;
638 if (mCodec != NULL) {
639 status = mCodec->releaseCrypto();
640 } else {
641 // returning OK if the codec has been already released
642 status = OK;
643 ALOGE("onReleaseCrypto No mCodec. err: %d", status);
644 }
645
646 sp<AMessage> response = new AMessage;
647 response->setInt32("status", status);
648 // Clearing the state as it's tied to crypto. mIsEncryptedObservedEarlier is sticky though
649 // and lasts for the lifetime of this codec. See its use in fetchInputData.
650 mIsEncrypted = false;
651
652 sp<AReplyToken> replyID;
653 CHECK(msg->senderAwaitsResponse(&replyID));
654 response->postReply(replyID);
655 }
656
handleAnInputBuffer(size_t index)657 bool NuPlayer2::Decoder::handleAnInputBuffer(size_t index) {
658 if (isDiscontinuityPending()) {
659 return false;
660 }
661
662 if (mCodec == NULL) {
663 ALOGE("[%s] handleAnInputBuffer without a valid codec", mComponentName.c_str());
664 handleError(NO_INIT);
665 return false;
666 }
667
668 size_t bufferSize = 0;
669 uint8_t *bufferBase = mCodec->getInputBuffer(index, &bufferSize);
670
671 if (bufferBase == NULL) {
672 ALOGE("[%s] handleAnInputBuffer, failed to get input buffer", mComponentName.c_str());
673 handleError(UNKNOWN_ERROR);
674 return false;
675 }
676
677 sp<MediaCodecBuffer> buffer =
678 new MediaCodecBuffer(NULL /* format */, new ABuffer(bufferBase, bufferSize));
679
680 if (index >= mInputBuffers.size()) {
681 for (size_t i = mInputBuffers.size(); i <= index; ++i) {
682 mInputBuffers.add();
683 mMediaBuffers.add();
684 mInputBufferIsDequeued.add();
685 mMediaBuffers.editItemAt(i) = NULL;
686 mInputBufferIsDequeued.editItemAt(i) = false;
687 }
688 }
689 mInputBuffers.editItemAt(index) = buffer;
690
691 //CHECK_LT(bufferIx, mInputBuffers.size());
692
693 if (mMediaBuffers[index] != NULL) {
694 mMediaBuffers[index]->release();
695 mMediaBuffers.editItemAt(index) = NULL;
696 }
697 mInputBufferIsDequeued.editItemAt(index) = true;
698
699 if (!mCSDsToSubmit.isEmpty()) {
700 sp<AMessage> msg = new AMessage();
701 msg->setSize("buffer-ix", index);
702
703 sp<ABuffer> buffer = mCSDsToSubmit.itemAt(0);
704 ALOGI("[%s] resubmitting CSD", mComponentName.c_str());
705 msg->setBuffer("buffer", buffer);
706 mCSDsToSubmit.removeAt(0);
707 if (!onInputBufferFetched(msg)) {
708 handleError(UNKNOWN_ERROR);
709 return false;
710 }
711 return true;
712 }
713
714 while (!mPendingInputMessages.empty()) {
715 sp<AMessage> msg = *mPendingInputMessages.begin();
716 if (!onInputBufferFetched(msg)) {
717 break;
718 }
719 mPendingInputMessages.erase(mPendingInputMessages.begin());
720 }
721
722 if (!mInputBufferIsDequeued.editItemAt(index)) {
723 return true;
724 }
725
726 mDequeuedInputBuffers.push_back(index);
727
728 onRequestInputBuffers();
729 return true;
730 }
731
handleAnOutputBuffer(size_t index,size_t offset,size_t size,int64_t timeUs,int32_t flags)732 bool NuPlayer2::Decoder::handleAnOutputBuffer(
733 size_t index,
734 size_t offset,
735 size_t size,
736 int64_t timeUs,
737 int32_t flags) {
738 if (mCodec == NULL) {
739 ALOGE("[%s] handleAnOutputBuffer without a valid codec", mComponentName.c_str());
740 handleError(NO_INIT);
741 return false;
742 }
743
744 // CHECK_LT(bufferIx, mOutputBuffers.size());
745
746 size_t bufferSize = 0;
747 uint8_t *bufferBase = mCodec->getOutputBuffer(index, &bufferSize);
748
749 if (bufferBase == NULL) {
750 ALOGE("[%s] handleAnOutputBuffer, failed to get output buffer", mComponentName.c_str());
751 handleError(UNKNOWN_ERROR);
752 return false;
753 }
754
755 sp<MediaCodecBuffer> buffer =
756 new MediaCodecBuffer(NULL /* format */, new ABuffer(bufferBase, bufferSize));
757
758 if (index >= mOutputBuffers.size()) {
759 for (size_t i = mOutputBuffers.size(); i <= index; ++i) {
760 mOutputBuffers.add();
761 }
762 }
763
764 mOutputBuffers.editItemAt(index) = buffer;
765
766 buffer->setRange(offset, size);
767 buffer->meta()->clear();
768 buffer->meta()->setInt64("timeUs", timeUs);
769
770 bool eos = flags & AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM;
771 // we do not expect CODECCONFIG or SYNCFRAME for decoder
772
773 sp<AMessage> reply = new AMessage(kWhatRenderBuffer, this);
774 reply->setSize("buffer-ix", index);
775 reply->setInt32("generation", mBufferGeneration);
776
777 if (eos) {
778 ALOGI("[%s] saw output EOS", mIsAudio ? "audio" : "video");
779
780 buffer->meta()->setInt32("eos", true);
781 reply->setInt32("eos", true);
782 }
783
784 mNumFramesTotal += !mIsAudio;
785
786 if (mSkipRenderingUntilMediaTimeUs >= 0) {
787 if (timeUs < mSkipRenderingUntilMediaTimeUs) {
788 ALOGV("[%s] dropping buffer at time %lld as requested.",
789 mComponentName.c_str(), (long long)timeUs);
790
791 reply->post();
792 if (eos) {
793 notifyResumeCompleteIfNecessary();
794 if (mRenderer != NULL && !isDiscontinuityPending()) {
795 mRenderer->queueEOS(mIsAudio, ERROR_END_OF_STREAM);
796 }
797 }
798 return true;
799 }
800
801 mSkipRenderingUntilMediaTimeUs = -1;
802 }
803
804 // wait until 1st frame comes out to signal resume complete
805 notifyResumeCompleteIfNecessary();
806
807 if (mRenderer != NULL) {
808 // send the buffer to renderer.
809 mRenderer->queueBuffer(mIsAudio, buffer, reply);
810 if (eos && !isDiscontinuityPending()) {
811 mRenderer->queueEOS(mIsAudio, ERROR_END_OF_STREAM);
812 }
813 }
814
815 return true;
816 }
817
handleOutputFormatChange(const sp<AMessage> & format)818 void NuPlayer2::Decoder::handleOutputFormatChange(const sp<AMessage> &format) {
819 if (!mIsAudio) {
820 int32_t width, height;
821 if (format->findInt32("width", &width)
822 && format->findInt32("height", &height)) {
823 mStats->setInt32("width", width);
824 mStats->setInt32("height", height);
825 }
826 sp<AMessage> notify = mNotify->dup();
827 notify->setInt32("what", kWhatVideoSizeChanged);
828 notify->setMessage("format", format);
829 notify->post();
830 } else if (mRenderer != NULL) {
831 uint32_t flags;
832 int64_t durationUs;
833 bool hasVideo = (mSource->getFormat(false /* audio */) != NULL);
834 if (getAudioDeepBufferSetting() // override regardless of source duration
835 || (mSource->getDuration(&durationUs) == OK
836 && durationUs > AUDIO_SINK_MIN_DEEP_BUFFER_DURATION_US)) {
837 flags = AUDIO_OUTPUT_FLAG_DEEP_BUFFER;
838 } else {
839 flags = AUDIO_OUTPUT_FLAG_NONE;
840 }
841
842 sp<AMessage> reply = new AMessage(kWhatAudioOutputFormatChanged, this);
843 reply->setInt32("generation", mBufferGeneration);
844 mRenderer->changeAudioFormat(
845 format, false /* offloadOnly */, hasVideo,
846 flags, mSource->isStreaming(), reply);
847 }
848 }
849
releaseAndResetMediaBuffers()850 void NuPlayer2::Decoder::releaseAndResetMediaBuffers() {
851 for (size_t i = 0; i < mMediaBuffers.size(); i++) {
852 if (mMediaBuffers[i] != NULL) {
853 mMediaBuffers[i]->release();
854 mMediaBuffers.editItemAt(i) = NULL;
855 }
856 }
857 mMediaBuffers.resize(mInputBuffers.size());
858 for (size_t i = 0; i < mMediaBuffers.size(); i++) {
859 mMediaBuffers.editItemAt(i) = NULL;
860 }
861 mInputBufferIsDequeued.clear();
862 mInputBufferIsDequeued.resize(mInputBuffers.size());
863 for (size_t i = 0; i < mInputBufferIsDequeued.size(); i++) {
864 mInputBufferIsDequeued.editItemAt(i) = false;
865 }
866
867 mPendingInputMessages.clear();
868 mDequeuedInputBuffers.clear();
869 mSkipRenderingUntilMediaTimeUs = -1;
870 }
871
isStaleReply(const sp<AMessage> & msg)872 bool NuPlayer2::Decoder::isStaleReply(const sp<AMessage> &msg) {
873 int32_t generation;
874 CHECK(msg->findInt32("generation", &generation));
875 return generation != mBufferGeneration;
876 }
877
fetchInputData(sp<AMessage> & reply)878 status_t NuPlayer2::Decoder::fetchInputData(sp<AMessage> &reply) {
879 sp<ABuffer> accessUnit;
880 bool dropAccessUnit = true;
881 do {
882 status_t err = mSource->dequeueAccessUnit(mIsAudio, &accessUnit);
883
884 if (err == -EWOULDBLOCK) {
885 return err;
886 } else if (err != OK) {
887 if (err == INFO_DISCONTINUITY) {
888 int32_t type;
889 CHECK(accessUnit->meta()->findInt32("discontinuity", &type));
890
891 bool formatChange =
892 (mIsAudio &&
893 (type & ATSParser::DISCONTINUITY_AUDIO_FORMAT))
894 || (!mIsAudio &&
895 (type & ATSParser::DISCONTINUITY_VIDEO_FORMAT));
896
897 bool timeChange = (type & ATSParser::DISCONTINUITY_TIME) != 0;
898
899 ALOGI("%s discontinuity (format=%d, time=%d)",
900 mIsAudio ? "audio" : "video", formatChange, timeChange);
901
902 bool seamlessFormatChange = false;
903 sp<AMessage> newFormat = mSource->getFormat(mIsAudio);
904 if (formatChange) {
905 seamlessFormatChange =
906 supportsSeamlessFormatChange(newFormat);
907 // treat seamless format change separately
908 formatChange = !seamlessFormatChange;
909 }
910
911 // For format or time change, return EOS to queue EOS input,
912 // then wait for EOS on output.
913 if (formatChange /* not seamless */) {
914 mFormatChangePending = true;
915 err = ERROR_END_OF_STREAM;
916 } else if (timeChange) {
917 rememberCodecSpecificData(newFormat);
918 mTimeChangePending = true;
919 err = ERROR_END_OF_STREAM;
920 } else if (seamlessFormatChange) {
921 // reuse existing decoder and don't flush
922 rememberCodecSpecificData(newFormat);
923 continue;
924 } else {
925 // This stream is unaffected by the discontinuity
926 return -EWOULDBLOCK;
927 }
928 }
929
930 // reply should only be returned without a buffer set
931 // when there is an error (including EOS)
932 CHECK(err != OK);
933
934 reply->setInt32("err", err);
935 return ERROR_END_OF_STREAM;
936 }
937
938 dropAccessUnit = false;
939 if (!mIsAudio && !mIsEncrypted) {
940 // Extra safeguard if higher-level behavior changes. Otherwise, not required now.
941 // Preventing the buffer from being processed (and sent to codec) if this is a later
942 // round of playback but this time without prepareDrm. Or if there is a race between
943 // stop (which is not blocking) and releaseDrm allowing buffers being processed after
944 // Crypto has been released (GenericSource currently prevents this race though).
945 // Particularly doing this check before IsAVCReferenceFrame call to prevent parsing
946 // of encrypted data.
947 if (mIsEncryptedObservedEarlier) {
948 ALOGE("fetchInputData: mismatched mIsEncrypted/mIsEncryptedObservedEarlier (0/1)");
949
950 return INVALID_OPERATION;
951 }
952
953 int32_t layerId = 0;
954 bool haveLayerId = accessUnit->meta()->findInt32("temporal-layer-id", &layerId);
955 if (mRenderer->getVideoLateByUs() > 100000ll
956 && mIsVideoAVC
957 && !IsAVCReferenceFrame(accessUnit)) {
958 dropAccessUnit = true;
959 } else if (haveLayerId && mNumVideoTemporalLayerTotal > 1) {
960 // Add only one layer each time.
961 if (layerId > mCurrentMaxVideoTemporalLayerId + 1
962 || layerId >= mNumVideoTemporalLayerAllowed) {
963 dropAccessUnit = true;
964 ALOGV("dropping layer(%d), speed=%g, allowed layer count=%d, max layerId=%d",
965 layerId, mPlaybackSpeed, mNumVideoTemporalLayerAllowed,
966 mCurrentMaxVideoTemporalLayerId);
967 } else if (layerId > mCurrentMaxVideoTemporalLayerId) {
968 mCurrentMaxVideoTemporalLayerId = layerId;
969 } else if (layerId == 0 && mNumVideoTemporalLayerTotal > 1
970 && IsIDR(accessUnit->data(), accessUnit->size())) {
971 mCurrentMaxVideoTemporalLayerId = mNumVideoTemporalLayerTotal - 1;
972 }
973 }
974 if (dropAccessUnit) {
975 if (layerId <= mCurrentMaxVideoTemporalLayerId && layerId > 0) {
976 mCurrentMaxVideoTemporalLayerId = layerId - 1;
977 }
978 ++mNumInputFramesDropped;
979 }
980 }
981 } while (dropAccessUnit);
982
983 // ALOGV("returned a valid buffer of %s data", mIsAudio ? "mIsAudio" : "video");
984 #if 0
985 int64_t mediaTimeUs;
986 CHECK(accessUnit->meta()->findInt64("timeUs", &mediaTimeUs));
987 ALOGV("[%s] feeding input buffer at media time %.3f",
988 mIsAudio ? "audio" : "video",
989 mediaTimeUs / 1E6);
990 #endif
991
992 if (mCCDecoder != NULL) {
993 mCCDecoder->decode(accessUnit);
994 }
995
996 reply->setBuffer("buffer", accessUnit);
997
998 return OK;
999 }
1000
onInputBufferFetched(const sp<AMessage> & msg)1001 bool NuPlayer2::Decoder::onInputBufferFetched(const sp<AMessage> &msg) {
1002 if (mCodec == NULL) {
1003 ALOGE("[%s] onInputBufferFetched without a valid codec", mComponentName.c_str());
1004 handleError(NO_INIT);
1005 return false;
1006 }
1007
1008 size_t bufferIx;
1009 CHECK(msg->findSize("buffer-ix", &bufferIx));
1010 CHECK_LT(bufferIx, mInputBuffers.size());
1011 sp<MediaCodecBuffer> codecBuffer = mInputBuffers[bufferIx];
1012
1013 sp<ABuffer> buffer;
1014 bool hasBuffer = msg->findBuffer("buffer", &buffer);
1015 bool needsCopy = true;
1016
1017 if (buffer == NULL /* includes !hasBuffer */) {
1018 int32_t streamErr = ERROR_END_OF_STREAM;
1019 CHECK(msg->findInt32("err", &streamErr) || !hasBuffer);
1020
1021 CHECK(streamErr != OK);
1022
1023 // attempt to queue EOS
1024 status_t err = mCodec->queueInputBuffer(
1025 bufferIx,
1026 0,
1027 0,
1028 0,
1029 AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM);
1030 if (err == OK) {
1031 mInputBufferIsDequeued.editItemAt(bufferIx) = false;
1032 } else if (streamErr == ERROR_END_OF_STREAM) {
1033 streamErr = err;
1034 // err will not be ERROR_END_OF_STREAM
1035 }
1036
1037 if (streamErr != ERROR_END_OF_STREAM) {
1038 ALOGE("Stream error for [%s] (err=%d), EOS %s queued",
1039 mComponentName.c_str(),
1040 streamErr,
1041 err == OK ? "successfully" : "unsuccessfully");
1042 handleError(streamErr);
1043 }
1044 } else {
1045 sp<AMessage> extra;
1046 if (buffer->meta()->findMessage("extra", &extra) && extra != NULL) {
1047 int64_t resumeAtMediaTimeUs;
1048 if (extra->findInt64(
1049 "resume-at-mediaTimeUs", &resumeAtMediaTimeUs)) {
1050 ALOGI("[%s] suppressing rendering until %lld us",
1051 mComponentName.c_str(), (long long)resumeAtMediaTimeUs);
1052 mSkipRenderingUntilMediaTimeUs = resumeAtMediaTimeUs;
1053 }
1054 }
1055
1056 int64_t timeUs = 0;
1057 uint32_t flags = 0;
1058 CHECK(buffer->meta()->findInt64("timeUs", &timeUs));
1059
1060 int32_t eos, csd;
1061 // we do not expect SYNCFRAME for decoder
1062 if (buffer->meta()->findInt32("eos", &eos) && eos) {
1063 flags |= AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM;
1064 } else if (buffer->meta()->findInt32("csd", &csd) && csd) {
1065 flags |= AMEDIACODEC_BUFFER_FLAG_CODEC_CONFIG;
1066 }
1067
1068 // Modular DRM
1069 MediaBufferBase *mediaBuf = NULL;
1070 sp<AMediaCodecCryptoInfoWrapper> cryptInfo;
1071
1072 // copy into codec buffer
1073 if (needsCopy) {
1074 if (buffer->size() > codecBuffer->capacity()) {
1075 handleError(ERROR_BUFFER_TOO_SMALL);
1076 mDequeuedInputBuffers.push_back(bufferIx);
1077 return false;
1078 }
1079
1080 if (buffer->data() != NULL) {
1081 codecBuffer->setRange(0, buffer->size());
1082 memcpy(codecBuffer->data(), buffer->data(), buffer->size());
1083 } else { // No buffer->data()
1084 //Modular DRM
1085 sp<RefBase> holder;
1086 if (buffer->meta()->findObject("mediaBufferHolder", &holder)) {
1087 mediaBuf = (holder != nullptr) ?
1088 static_cast<MediaBufferHolder*>(holder.get())->mediaBuffer() : nullptr;
1089 }
1090 if (mediaBuf != NULL) {
1091 codecBuffer->setRange(0, mediaBuf->size());
1092 memcpy(codecBuffer->data(), mediaBuf->data(), mediaBuf->size());
1093
1094 MetaDataBase &meta_data = mediaBuf->meta_data();
1095 cryptInfo = AMediaCodecCryptoInfoWrapper::Create(meta_data);
1096 } else { // No mediaBuf
1097 ALOGE("onInputBufferFetched: buffer->data()/mediaBuf are NULL for %p",
1098 buffer.get());
1099 handleError(UNKNOWN_ERROR);
1100 return false;
1101 }
1102 } // buffer->data()
1103 } // needsCopy
1104
1105 status_t err;
1106 if (cryptInfo != NULL) {
1107 err = mCodec->queueSecureInputBuffer(
1108 bufferIx,
1109 codecBuffer->offset(),
1110 cryptInfo,
1111 timeUs,
1112 flags);
1113 // synchronous call so done with cryptInfo here
1114 } else {
1115 err = mCodec->queueInputBuffer(
1116 bufferIx,
1117 codecBuffer->offset(),
1118 codecBuffer->size(),
1119 timeUs,
1120 flags);
1121 } // no cryptInfo
1122
1123 if (err != OK) {
1124 ALOGE("onInputBufferFetched: queue%sInputBuffer failed for [%s] (err=%d)",
1125 (cryptInfo != NULL ? "Secure" : ""),
1126 mComponentName.c_str(), err);
1127 handleError(err);
1128 } else {
1129 mInputBufferIsDequeued.editItemAt(bufferIx) = false;
1130 }
1131
1132 } // buffer != NULL
1133 return true;
1134 }
1135
onRenderBuffer(const sp<AMessage> & msg)1136 void NuPlayer2::Decoder::onRenderBuffer(const sp<AMessage> &msg) {
1137 status_t err;
1138 int32_t render;
1139 size_t bufferIx;
1140 int32_t eos;
1141 CHECK(msg->findSize("buffer-ix", &bufferIx));
1142
1143 if (!mIsAudio) {
1144 int64_t timeUs;
1145 sp<MediaCodecBuffer> buffer = mOutputBuffers[bufferIx];
1146 buffer->meta()->findInt64("timeUs", &timeUs);
1147
1148 if (mCCDecoder != NULL && mCCDecoder->isSelected()) {
1149 mCCDecoder->display(timeUs);
1150 }
1151 }
1152
1153 if (mCodec == NULL) {
1154 err = NO_INIT;
1155 } else if (msg->findInt32("render", &render) && render) {
1156 int64_t timestampNs;
1157 CHECK(msg->findInt64("timestampNs", ×tampNs));
1158 err = mCodec->releaseOutputBufferAtTime(bufferIx, timestampNs);
1159 } else {
1160 mNumOutputFramesDropped += !mIsAudio;
1161 err = mCodec->releaseOutputBuffer(bufferIx, false /* render */);
1162 }
1163 if (err != OK) {
1164 ALOGE("failed to release output buffer for [%s] (err=%d)",
1165 mComponentName.c_str(), err);
1166 handleError(err);
1167 }
1168 if (msg->findInt32("eos", &eos) && eos
1169 && isDiscontinuityPending()) {
1170 finishHandleDiscontinuity(true /* flushOnTimeChange */);
1171 }
1172 }
1173
isDiscontinuityPending() const1174 bool NuPlayer2::Decoder::isDiscontinuityPending() const {
1175 return mFormatChangePending || mTimeChangePending;
1176 }
1177
finishHandleDiscontinuity(bool flushOnTimeChange)1178 void NuPlayer2::Decoder::finishHandleDiscontinuity(bool flushOnTimeChange) {
1179 ALOGV("finishHandleDiscontinuity: format %d, time %d, flush %d",
1180 mFormatChangePending, mTimeChangePending, flushOnTimeChange);
1181
1182 // If we have format change, pause and wait to be killed;
1183 // If we have time change only, flush and restart fetching.
1184
1185 if (mFormatChangePending) {
1186 mPaused = true;
1187 } else if (mTimeChangePending) {
1188 if (flushOnTimeChange) {
1189 doFlush(false /* notifyComplete */);
1190 signalResume(false /* notifyComplete */);
1191 }
1192 }
1193
1194 // Notify NuPlayer2 to either shutdown decoder, or rescan sources
1195 sp<AMessage> msg = mNotify->dup();
1196 msg->setInt32("what", kWhatInputDiscontinuity);
1197 msg->setInt32("formatChange", mFormatChangePending);
1198 msg->post();
1199
1200 mFormatChangePending = false;
1201 mTimeChangePending = false;
1202 }
1203
supportsSeamlessAudioFormatChange(const sp<AMessage> & targetFormat) const1204 bool NuPlayer2::Decoder::supportsSeamlessAudioFormatChange(
1205 const sp<AMessage> &targetFormat) const {
1206 if (targetFormat == NULL) {
1207 return true;
1208 }
1209
1210 AString mime;
1211 if (!targetFormat->findString("mime", &mime)) {
1212 return false;
1213 }
1214
1215 if (!strcasecmp(mime.c_str(), MEDIA_MIMETYPE_AUDIO_AAC)) {
1216 // field-by-field comparison
1217 const char * keys[] = { "channel-count", "sample-rate", "is-adts" };
1218 for (unsigned int i = 0; i < sizeof(keys) / sizeof(keys[0]); i++) {
1219 int32_t oldVal, newVal;
1220 if (!mInputFormat->getInt32(keys[i], &oldVal) ||
1221 !targetFormat->findInt32(keys[i], &newVal) ||
1222 oldVal != newVal) {
1223 return false;
1224 }
1225 }
1226
1227 sp<ABuffer> newBuf;
1228 uint8_t *oldBufData = NULL;
1229 size_t oldBufSize = 0;
1230 if (mInputFormat->getBuffer("csd-0", (void**)&oldBufData, &oldBufSize) &&
1231 targetFormat->findBuffer("csd-0", &newBuf)) {
1232 if (oldBufSize != newBuf->size()) {
1233 return false;
1234 }
1235 return !memcmp(oldBufData, newBuf->data(), oldBufSize);
1236 }
1237 }
1238 return false;
1239 }
1240
supportsSeamlessFormatChange(const sp<AMessage> & targetFormat) const1241 bool NuPlayer2::Decoder::supportsSeamlessFormatChange(const sp<AMessage> &targetFormat) const {
1242 if (mInputFormat == NULL) {
1243 return false;
1244 }
1245
1246 if (targetFormat == NULL) {
1247 return true;
1248 }
1249
1250 AString oldMime, newMime;
1251 if (!mInputFormat->getString("mime", &oldMime)
1252 || !targetFormat->findString("mime", &newMime)
1253 || !(oldMime == newMime)) {
1254 return false;
1255 }
1256
1257 bool audio = !strncasecmp(oldMime.c_str(), "audio/", strlen("audio/"));
1258 bool seamless;
1259 if (audio) {
1260 seamless = supportsSeamlessAudioFormatChange(targetFormat);
1261 } else {
1262 int32_t isAdaptive;
1263 seamless = (mCodec != NULL &&
1264 mInputFormat->getInt32("adaptive-playback", &isAdaptive) &&
1265 isAdaptive);
1266 }
1267
1268 ALOGV("%s seamless support for %s", seamless ? "yes" : "no", oldMime.c_str());
1269 return seamless;
1270 }
1271
rememberCodecSpecificData(const sp<AMessage> & format)1272 void NuPlayer2::Decoder::rememberCodecSpecificData(const sp<AMessage> &format) {
1273 if (format == NULL) {
1274 return;
1275 }
1276 mCSDsForCurrentFormat.clear();
1277 for (int32_t i = 0; ; ++i) {
1278 AString tag = "csd-";
1279 tag.append(i);
1280 sp<ABuffer> buffer;
1281 if (!format->findBuffer(tag.c_str(), &buffer)) {
1282 break;
1283 }
1284 mCSDsForCurrentFormat.push(buffer);
1285 }
1286 }
1287
notifyResumeCompleteIfNecessary()1288 void NuPlayer2::Decoder::notifyResumeCompleteIfNecessary() {
1289 if (mResumePending) {
1290 mResumePending = false;
1291
1292 sp<AMessage> notify = mNotify->dup();
1293 notify->setInt32("what", kWhatResumeCompleted);
1294 notify->post();
1295 }
1296 }
1297
1298 } // namespace android
1299
1300