| Wei Jia | 53692fa | 2017-12-11 10:33:46 -0800 | [diff] [blame] | 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 |  | 
| Wei Jia | 53692fa | 2017-12-11 10:33:46 -0800 | [diff] [blame] | 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> | 
| Dongwon Kang | bc8f53b | 2018-01-25 17:01:44 -0800 | [diff] [blame] | 31 | #include <media/MediaBufferHolder.h> | 
| Wei Jia | 53692fa | 2017-12-11 10:33:46 -0800 | [diff] [blame] | 32 | #include <media/MediaCodecBuffer.h> | 
 | 33 | #include <media/NdkMediaCodec.h> | 
| Wei Jia | 28288fb | 2017-12-15 13:45:29 -0800 | [diff] [blame] | 34 | #include <media/NdkWrapper.h> | 
| Wei Jia | 53692fa | 2017-12-11 10:33:46 -0800 | [diff] [blame] | 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> | 
| Wei Jia | 53692fa | 2017-12-11 10:33:46 -0800 | [diff] [blame] | 43 |  | 
| Wei Jia | 4049f13 | 2018-01-22 10:37:31 -0800 | [diff] [blame] | 44 | #include <system/window.h> | 
| Wei Jia | 53692fa | 2017-12-11 10:33:46 -0800 | [diff] [blame] | 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 |  | 
 | 55 | static inline bool getAudioDeepBufferSetting() { | 
 | 56 |     return property_get_bool("media.stagefright.audio.deep", false /* default_value */); | 
 | 57 | } | 
 | 58 |  | 
 | 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, | 
| Wei Jia | 28288fb | 2017-12-15 13:45:29 -0800 | [diff] [blame] | 65 |         const sp<ANativeWindowWrapper> &nww, | 
| Wei Jia | 53692fa | 2017-12-11 10:33:46 -0800 | [diff] [blame] | 66 |         const sp<CCDecoder> &ccDecoder) | 
 | 67 |     : DecoderBase(notify), | 
| Wei Jia | 28288fb | 2017-12-15 13:45:29 -0800 | [diff] [blame] | 68 |       mNativeWindow(nww), | 
| Wei Jia | 53692fa | 2017-12-11 10:33:46 -0800 | [diff] [blame] | 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 |  | 
 | 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 |  | 
 | 106 | 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 |  | 
| Wei Jia | 28288fb | 2017-12-15 13:45:29 -0800 | [diff] [blame] | 113 | status_t NuPlayer2::Decoder::setVideoSurface(const sp<ANativeWindowWrapper> &nww) { | 
 | 114 |     if (nww == NULL || nww->getANativeWindow() == NULL | 
 | 115 |         || ADebug::isExperimentEnabled("legacy-setsurface")) { | 
| Wei Jia | 53692fa | 2017-12-11 10:33:46 -0800 | [diff] [blame] | 116 |         return BAD_VALUE; | 
 | 117 |     } | 
 | 118 |  | 
 | 119 |     sp<AMessage> msg = new AMessage(kWhatSetVideoSurface, this); | 
 | 120 |  | 
| Wei Jia | 28288fb | 2017-12-15 13:45:29 -0800 | [diff] [blame] | 121 |     msg->setObject("surface", nww); | 
| Wei Jia | 53692fa | 2017-12-11 10:33:46 -0800 | [diff] [blame] | 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 |  | 
 | 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)); | 
| Wei Jia | 28288fb | 2017-12-15 13:45:29 -0800 | [diff] [blame] | 231 |             sp<ANativeWindowWrapper> nww = | 
 | 232 |                 static_cast<ANativeWindowWrapper *>(obj.get()); // non-null | 
 | 233 |             if (nww == NULL || nww->getANativeWindow() == NULL) { | 
 | 234 |                 break; | 
 | 235 |             } | 
| Wei Jia | 53692fa | 2017-12-11 10:33:46 -0800 | [diff] [blame] | 236 |             int32_t err = INVALID_OPERATION; | 
| Wei Jia | 28288fb | 2017-12-15 13:45:29 -0800 | [diff] [blame] | 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) { | 
| Wei Jia | 53692fa | 2017-12-11 10:33:46 -0800 | [diff] [blame] | 241 |                 // TODO: once AwesomePlayer is removed, remove this automatic connecting | 
 | 242 |                 // to the surface by MediaPlayerService. | 
 | 243 |                 // | 
| Wei Jia | 28288fb | 2017-12-15 13:45:29 -0800 | [diff] [blame] | 244 |                 // at this point MediaPlayer2Manager::client has already connected to the | 
| Wei Jia | 53692fa | 2017-12-11 10:33:46 -0800 | [diff] [blame] | 245 |                 // surface, which MediaCodec does not expect | 
| Wei Jia | 4049f13 | 2018-01-22 10:37:31 -0800 | [diff] [blame] | 246 |                 err = native_window_api_disconnect(nww->getANativeWindow(), | 
 | 247 |                                                    NATIVE_WINDOW_API_MEDIA); | 
| Wei Jia | 53692fa | 2017-12-11 10:33:46 -0800 | [diff] [blame] | 248 |                 if (err == OK) { | 
| Wei Jia | 28288fb | 2017-12-15 13:45:29 -0800 | [diff] [blame] | 249 |                     err = mCodec->setOutputSurface(nww); | 
| Wei Jia | 53692fa | 2017-12-11 10:33:46 -0800 | [diff] [blame] | 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. | 
| Wei Jia | 4049f13 | 2018-01-22 10:37:31 -0800 | [diff] [blame] | 254 |                         (void)native_window_api_connect(mNativeWindow->getANativeWindow(), | 
 | 255 |                                                         NATIVE_WINDOW_API_MEDIA); | 
 | 256 |  | 
| Wei Jia | 28288fb | 2017-12-15 13:45:29 -0800 | [diff] [blame] | 257 |                         mNativeWindow = nww; | 
| Wei Jia | 53692fa | 2017-12-11 10:33:46 -0800 | [diff] [blame] | 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. | 
| Wei Jia | 4049f13 | 2018-01-22 10:37:31 -0800 | [diff] [blame] | 263 |                     (void)native_window_api_connect(nww->getANativeWindow(), | 
 | 264 |                                                     NATIVE_WINDOW_API_MEDIA); | 
| Wei Jia | 53692fa | 2017-12-11 10:33:46 -0800 | [diff] [blame] | 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 |  | 
 | 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"); | 
| Wei Jia | 28288fb | 2017-12-15 13:45:29 -0800 | [diff] [blame] | 304 |     ALOGV("[%s] onConfigure (nww=%p)", mComponentName.c_str(), | 
 | 305 |           (mNativeWindow == NULL ? NULL : mNativeWindow->getANativeWindow())); | 
| Wei Jia | 53692fa | 2017-12-11 10:33:46 -0800 | [diff] [blame] | 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; | 
| Wei Jia | 28288fb | 2017-12-15 13:45:29 -0800 | [diff] [blame] | 332 |     if (mNativeWindow != NULL && mNativeWindow->getANativeWindow() != NULL) { | 
| Wei Jia | 53692fa | 2017-12-11 10:33:46 -0800 | [diff] [blame] | 333 |         // disconnect from surface as MediaCodec will reconnect | 
| Wei Jia | 4049f13 | 2018-01-22 10:37:31 -0800 | [diff] [blame] | 334 |         err = native_window_api_disconnect(mNativeWindow->getANativeWindow(), | 
 | 335 |                                            NATIVE_WINDOW_API_MEDIA); | 
| Wei Jia | 53692fa | 2017-12-11 10:33:46 -0800 | [diff] [blame] | 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), | 
| Wei Jia | 28288fb | 2017-12-15 13:45:29 -0800 | [diff] [blame] | 354 |             mNativeWindow, | 
| Wei Jia | 53692fa | 2017-12-11 10:33:46 -0800 | [diff] [blame] | 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 |  | 
 | 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 |  | 
 | 474 | void NuPlayer2::Decoder::onSetRenderer(const sp<Renderer> &renderer) { | 
 | 475 |     mRenderer = renderer; | 
 | 476 | } | 
 | 477 |  | 
 | 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 |  | 
 | 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 |  | 
 | 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 |  | 
 | 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 |  | 
| Wei Jia | 28288fb | 2017-12-15 13:45:29 -0800 | [diff] [blame] | 547 |         if (mNativeWindow != NULL && mNativeWindow->getANativeWindow() != NULL) { | 
| Wei Jia | 53692fa | 2017-12-11 10:33:46 -0800 | [diff] [blame] | 548 |             // reconnect to surface as MediaCodec disconnected from it | 
| Wei Jia | 4049f13 | 2018-01-22 10:37:31 -0800 | [diff] [blame] | 549 |             status_t error = native_window_api_connect(mNativeWindow->getANativeWindow(), | 
 | 550 |                                                        NATIVE_WINDOW_API_MEDIA); | 
| Wei Jia | 53692fa | 2017-12-11 10:33:46 -0800 | [diff] [blame] | 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 |  */ | 
 | 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 |  | 
 | 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 |  | 
 | 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 |  | 
 | 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 |  | 
 | 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 |  | 
 | 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 |  | 
| Wei Jia | 5ed2faf | 2017-12-14 16:07:21 -0800 | [diff] [blame] | 784 |     mNumFramesTotal += !mIsAudio; | 
 | 785 |  | 
| Wei Jia | 53692fa | 2017-12-11 10:33:46 -0800 | [diff] [blame] | 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 |  | 
| Wei Jia | 53692fa | 2017-12-11 10:33:46 -0800 | [diff] [blame] | 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 |  | 
 | 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 |  | 
 | 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 |  | 
 | 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 |  | 
 | 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 |  | 
 | 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 |         MediaBuffer *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 | 
| Dongwon Kang | bc8f53b | 2018-01-25 17:01:44 -0800 | [diff] [blame] | 1085 |                 sp<RefBase> holder; | 
 | 1086 |                 if (buffer->meta()->findObject("mediaBufferHolder", &holder)) { | 
 | 1087 |                     mediaBuf = (holder != nullptr) ? | 
 | 1088 |                         static_cast<MediaBufferHolder*>(holder.get())->mediaBuffer() : nullptr; | 
 | 1089 |                 } | 
| Wei Jia | 53692fa | 2017-12-11 10:33:46 -0800 | [diff] [blame] | 1090 |                 if (mediaBuf != NULL) { | 
 | 1091 |                     codecBuffer->setRange(0, mediaBuf->size()); | 
 | 1092 |                     memcpy(codecBuffer->data(), mediaBuf->data(), mediaBuf->size()); | 
 | 1093 |  | 
 | 1094 |                     sp<MetaData> meta_data = mediaBuf->meta_data(); | 
 | 1095 |                     cryptInfo = AMediaCodecCryptoInfoWrapper::Create(meta_data); | 
| Wei Jia | 53692fa | 2017-12-11 10:33:46 -0800 | [diff] [blame] | 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 |  | 
 | 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 |  | 
 | 1174 | bool NuPlayer2::Decoder::isDiscontinuityPending() const { | 
 | 1175 |     return mFormatChangePending || mTimeChangePending; | 
 | 1176 | } | 
 | 1177 |  | 
 | 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 |  | 
 | 1204 | 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 |  | 
 | 1241 | 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 |  | 
 | 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 |  | 
 | 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 |  |