1 /* 2 * Copyright (C) 2011 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 USE_LOG SLAndroidLogLevel_Verbose 18 19 #include "sles_allinclusive.h" 20 #include "android_GenericMediaPlayer.h" 21 22 #include <system/window.h> 23 24 #include <media/IMediaPlayerService.h> 25 #include <media/stagefright/foundation/ADebug.h> 26 #include <media/mediaplayer.h> // media_event_type media_error_type media_info_type 27 28 // default delay in Us used when reposting an event when the player is not ready to accept 29 // the command yet. This is for instance used when seeking on a MediaPlayer that's still preparing 30 #define DEFAULT_COMMAND_DELAY_FOR_REPOST_US (100*1000) // 100ms 31 32 // table of prefixes for known distant protocols; these are immediately dispatched to mediaserver 33 static const char* const kDistantProtocolPrefix[] = { "http://", "https://", "rtsp://"}; 34 #define NB_DISTANT_PROTOCOLS (sizeof(kDistantProtocolPrefix)/sizeof(kDistantProtocolPrefix[0])) 35 36 // is the specified URI a known distant protocol? 37 bool isDistantProtocol(const char *uri) 38 { 39 for (unsigned int i = 0; i < NB_DISTANT_PROTOCOLS; i++) { 40 if (!strncasecmp(uri, kDistantProtocolPrefix[i], strlen(kDistantProtocolPrefix[i]))) { 41 return true; 42 } 43 } 44 return false; 45 } 46 47 namespace android { 48 49 //-------------------------------------------------------------------------------------------------- 50 MediaPlayerNotificationClient::MediaPlayerNotificationClient(GenericMediaPlayer* gmp) : 51 mGenericMediaPlayer(gmp), 52 mPlayerPrepared(PREPARE_NOT_STARTED) 53 { 54 SL_LOGV("MediaPlayerNotificationClient::MediaPlayerNotificationClient()"); 55 } 56 57 MediaPlayerNotificationClient::~MediaPlayerNotificationClient() { 58 SL_LOGV("MediaPlayerNotificationClient::~MediaPlayerNotificationClient()"); 59 } 60 61 // Map a media_event_type enum (the msg of an IMediaPlayerClient::notify) to a string or NULL 62 static const char *media_event_type_to_string(media_event_type msg) 63 { 64 switch (msg) { 65 #define _(code) case code: return #code; 66 _(MEDIA_NOP) 67 _(MEDIA_PREPARED) 68 _(MEDIA_PLAYBACK_COMPLETE) 69 _(MEDIA_BUFFERING_UPDATE) 70 _(MEDIA_SEEK_COMPLETE) 71 _(MEDIA_SET_VIDEO_SIZE) 72 _(MEDIA_TIMED_TEXT) 73 _(MEDIA_ERROR) 74 _(MEDIA_INFO) 75 #undef _ 76 default: 77 return NULL; 78 } 79 } 80 81 // Map a media_error_type enum (the ext1 of a MEDIA_ERROR event) to a string or NULL 82 static const char *media_error_type_to_string(media_error_type err) 83 { 84 switch (err) { 85 #define _(code, msg) case code: return msg; 86 _(MEDIA_ERROR_UNKNOWN, "Unknown media error") 87 _(MEDIA_ERROR_SERVER_DIED, "Server died") 88 _(MEDIA_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK, "Not valid for progressive playback") 89 #undef _ 90 default: 91 return NULL; 92 } 93 } 94 95 // Map a media_info_type enum (the ext1 of a MEDIA_INFO event) to a string or NULL 96 static const char *media_info_type_to_string(media_info_type info) 97 { 98 switch (info) { 99 #define _(code, msg) case code: return msg; 100 _(MEDIA_INFO_UNKNOWN, "Unknown info") 101 _(MEDIA_INFO_VIDEO_TRACK_LAGGING, "Video track lagging") 102 _(MEDIA_INFO_BUFFERING_START, "Buffering start") 103 _(MEDIA_INFO_BUFFERING_END, "Buffering end") 104 _(MEDIA_INFO_NETWORK_BANDWIDTH, "Network bandwidth") 105 _(MEDIA_INFO_BAD_INTERLEAVING, "Bad interleaving") 106 _(MEDIA_INFO_NOT_SEEKABLE, "Not seekable") 107 _(MEDIA_INFO_METADATA_UPDATE, "Metadata update") 108 #undef _ 109 default: 110 return NULL; 111 } 112 } 113 114 //-------------------------------------------------- 115 // IMediaPlayerClient implementation 116 void MediaPlayerNotificationClient::notify(int msg, int ext1, int ext2, const Parcel *obj) { 117 SL_LOGV("MediaPlayerNotificationClient::notify(msg=%s (%d), ext1=%d, ext2=%d)", 118 media_event_type_to_string((enum media_event_type) msg), msg, ext1, ext2); 119 120 sp<GenericMediaPlayer> genericMediaPlayer(mGenericMediaPlayer.promote()); 121 if (genericMediaPlayer == NULL) { 122 SL_LOGW("MediaPlayerNotificationClient::notify after GenericMediaPlayer destroyed"); 123 return; 124 } 125 126 switch ((media_event_type) msg) { 127 case MEDIA_PREPARED: 128 { 129 Mutex::Autolock _l(mLock); 130 if (PREPARE_IN_PROGRESS == mPlayerPrepared) { 131 mPlayerPrepared = PREPARE_COMPLETED_SUCCESSFULLY; 132 mPlayerPreparedCondition.signal(); 133 } else { 134 SL_LOGE("Unexpected MEDIA_PREPARED"); 135 } 136 } 137 break; 138 139 case MEDIA_SET_VIDEO_SIZE: 140 // only send video size updates if the player was flagged as having video, to avoid 141 // sending video size updates of (0,0) 142 // We're running on a different thread than genericMediaPlayer's ALooper thread, 143 // so it would normally be racy to access fields within genericMediaPlayer. 144 // But in this case mHasVideo is const, so it is safe to access. 145 // Or alternatively, we could notify unconditionally and let it decide whether to handle. 146 if (genericMediaPlayer->mHasVideo && (ext1 != 0 || ext2 != 0)) { 147 genericMediaPlayer->notify(PLAYEREVENT_VIDEO_SIZE_UPDATE, 148 (int32_t)ext1 /*width*/, (int32_t)ext2 /*height*/, true /*async*/); 149 } 150 break; 151 152 case MEDIA_SEEK_COMPLETE: 153 genericMediaPlayer->seekComplete(); 154 break; 155 156 case MEDIA_PLAYBACK_COMPLETE: 157 genericMediaPlayer->notify(PLAYEREVENT_ENDOFSTREAM, 1, true /*async*/); 158 break; 159 160 case MEDIA_BUFFERING_UPDATE: 161 // if we receive any out-of-range data, then clamp it to reduce further harm 162 if (ext1 < 0) { 163 SL_LOGE("MEDIA_BUFFERING_UPDATE %d%% < 0", ext1); 164 ext1 = 0; 165 } else if (ext1 > 100) { 166 SL_LOGE("MEDIA_BUFFERING_UPDATE %d%% > 100", ext1); 167 ext1 = 100; 168 } 169 // values received from Android framework for buffer fill level use percent, 170 // while SL/XA use permille, so does GenericPlayer 171 genericMediaPlayer->bufferingUpdate(ext1 * 10 /*fillLevelPerMille*/); 172 break; 173 174 case MEDIA_ERROR: 175 { 176 SL_LOGV("MediaPlayerNotificationClient::notify(msg=MEDIA_ERROR, ext1=%s (%d), ext2=%d)", 177 media_error_type_to_string((media_error_type) ext1), ext1, ext2); 178 Mutex::Autolock _l(mLock); 179 if (PREPARE_IN_PROGRESS == mPlayerPrepared) { 180 mPlayerPrepared = PREPARE_COMPLETED_UNSUCCESSFULLY; 181 mPlayerPreparedCondition.signal(); 182 } else { 183 // inform client of errors after preparation 184 genericMediaPlayer->notify(PLAYEREVENT_ERRORAFTERPREPARE, ext1, true /*async*/); 185 } 186 } 187 break; 188 189 case MEDIA_NOP: 190 case MEDIA_TIMED_TEXT: 191 break; 192 193 case MEDIA_INFO: 194 SL_LOGV("MediaPlayerNotificationClient::notify(msg=MEDIA_INFO, ext1=%s (%d), ext2=%d)", 195 media_info_type_to_string((media_info_type) ext1), ext1, ext2); 196 switch (ext1) { 197 case MEDIA_INFO_VIDEO_TRACK_LAGGING: 198 SL_LOGV("MEDIA_INFO_VIDEO_TRACK_LAGGING by %d ms", ext1); 199 break; 200 case MEDIA_INFO_NETWORK_BANDWIDTH: 201 SL_LOGV("MEDIA_INFO_NETWORK_BANDWIDTH %d kbps", ext2); 202 break; 203 case MEDIA_INFO_UNKNOWN: 204 case MEDIA_INFO_BUFFERING_START: 205 case MEDIA_INFO_BUFFERING_END: 206 case MEDIA_INFO_BAD_INTERLEAVING: 207 case MEDIA_INFO_NOT_SEEKABLE: 208 case MEDIA_INFO_METADATA_UPDATE: 209 default: 210 break; 211 } 212 break; 213 214 default: 215 break; 216 } 217 218 } 219 220 //-------------------------------------------------- 221 void MediaPlayerNotificationClient::beforePrepare() 222 { 223 Mutex::Autolock _l(mLock); 224 assert(mPlayerPrepared == PREPARE_NOT_STARTED); 225 mPlayerPrepared = PREPARE_IN_PROGRESS; 226 } 227 228 //-------------------------------------------------- 229 bool MediaPlayerNotificationClient::blockUntilPlayerPrepared() { 230 Mutex::Autolock _l(mLock); 231 assert(mPlayerPrepared != PREPARE_NOT_STARTED); 232 while (mPlayerPrepared == PREPARE_IN_PROGRESS) { 233 mPlayerPreparedCondition.wait(mLock); 234 } 235 assert(mPlayerPrepared == PREPARE_COMPLETED_SUCCESSFULLY || 236 mPlayerPrepared == PREPARE_COMPLETED_UNSUCCESSFULLY); 237 return mPlayerPrepared == PREPARE_COMPLETED_SUCCESSFULLY; 238 } 239 240 //-------------------------------------------------------------------------------------------------- 241 GenericMediaPlayer::GenericMediaPlayer(const AudioPlayback_Parameters* params, bool hasVideo) : 242 GenericPlayer(params), 243 mHasVideo(hasVideo), 244 mSeekTimeMsec(0), 245 mVideoSurfaceTexture(0), 246 mPlayer(0), 247 mPlayerClient(new MediaPlayerNotificationClient(this)), 248 mPlayerDeathNotifier(new MediaPlayerDeathNotifier(mPlayerClient)) 249 { 250 SL_LOGD("GenericMediaPlayer::GenericMediaPlayer()"); 251 252 } 253 254 GenericMediaPlayer::~GenericMediaPlayer() { 255 SL_LOGD("GenericMediaPlayer::~GenericMediaPlayer()"); 256 } 257 258 void GenericMediaPlayer::preDestroy() { 259 // FIXME can't access mPlayer from outside the looper (no mutex!) so using mPreparedPlayer 260 sp<IMediaPlayer> player; 261 getPreparedPlayer(player); 262 if (player != NULL) { 263 player->stop(); 264 // causes CHECK failure in Nuplayer, but commented out in the subclass preDestroy 265 // randomly causes a NPE in StagefrightPlayer, heap corruption, or app hang 266 //player->setDataSource(NULL); 267 player->setVideoSurfaceTexture(NULL); 268 player->disconnect(); 269 // release all references to the IMediaPlayer 270 // FIXME illegal if not on looper 271 //mPlayer.clear(); 272 { 273 Mutex::Autolock _l(mPreparedPlayerLock); 274 mPreparedPlayer.clear(); 275 } 276 } 277 GenericPlayer::preDestroy(); 278 } 279 280 //-------------------------------------------------- 281 // overridden from GenericPlayer 282 // pre-condition: 283 // msec != NULL 284 // post-condition 285 // *msec == 286 // ANDROID_UNKNOWN_TIME if position is unknown at time of query, 287 // or the current MediaPlayer position 288 void GenericMediaPlayer::getPositionMsec(int* msec) { 289 SL_LOGD("GenericMediaPlayer::getPositionMsec()"); 290 sp<IMediaPlayer> player; 291 getPreparedPlayer(player); 292 // To avoid deadlock, directly call the MediaPlayer object 293 if (player == 0 || player->getCurrentPosition(msec) != NO_ERROR) { 294 *msec = ANDROID_UNKNOWN_TIME; 295 } 296 } 297 298 //-------------------------------------------------- 299 void GenericMediaPlayer::setVideoSurfaceTexture(const sp<ISurfaceTexture> &surfaceTexture) { 300 SL_LOGV("GenericMediaPlayer::setVideoSurfaceTexture()"); 301 // FIXME bug - race condition, should do in looper 302 if (mVideoSurfaceTexture.get() == surfaceTexture.get()) { 303 return; 304 } 305 if ((mStateFlags & kFlagPrepared) && (mPlayer != 0)) { 306 mPlayer->setVideoSurfaceTexture(surfaceTexture); 307 } 308 mVideoSurfaceTexture = surfaceTexture; 309 } 310 311 //-------------------------------------------------- 312 void GenericMediaPlayer::setPlaybackRate(int32_t ratePermille) { 313 SL_LOGV("GenericMediaPlayer::setPlaybackRate(%d)", ratePermille); 314 GenericPlayer::setPlaybackRate(ratePermille); 315 sp<IMediaPlayer> player; 316 getPreparedPlayer(player); 317 if (player != 0) { 318 Parcel rateParcel; 319 if (rateParcel.writeInt32(ratePermille) == OK) { 320 player->setParameter(KEY_PARAMETER_PLAYBACK_RATE_PERMILLE, rateParcel); 321 } 322 } 323 } 324 325 326 //-------------------------------------------------- 327 // Event handlers 328 329 // blocks until mPlayer is prepared 330 void GenericMediaPlayer::onPrepare() { 331 SL_LOGD("GenericMediaPlayer::onPrepare()"); 332 // Attempt to prepare at most once, and only if there is a MediaPlayer 333 if (!(mStateFlags & (kFlagPrepared | kFlagPreparedUnsuccessfully)) && (mPlayer != 0)) { 334 if (mHasVideo) { 335 if (mVideoSurfaceTexture != 0) { 336 mPlayer->setVideoSurfaceTexture(mVideoSurfaceTexture); 337 } 338 } 339 mPlayer->setAudioStreamType(mPlaybackParams.streamType); 340 mPlayerClient->beforePrepare(); 341 mPlayer->prepareAsync(); 342 if (mPlayerClient->blockUntilPlayerPrepared()) { 343 mStateFlags |= kFlagPrepared; 344 afterMediaPlayerPreparedSuccessfully(); 345 } else { 346 mStateFlags |= kFlagPreparedUnsuccessfully; 347 } 348 } 349 GenericPlayer::onPrepare(); 350 SL_LOGD("GenericMediaPlayer::onPrepare() done, mStateFlags=0x%x", mStateFlags); 351 } 352 353 354 void GenericMediaPlayer::onPlay() { 355 SL_LOGD("GenericMediaPlayer::onPlay()"); 356 if (((mStateFlags & (kFlagPrepared | kFlagPlaying)) == kFlagPrepared) && (mPlayer != 0)) { 357 mPlayer->start(); 358 } 359 GenericPlayer::onPlay(); 360 } 361 362 363 void GenericMediaPlayer::onPause() { 364 SL_LOGD("GenericMediaPlayer::onPause()"); 365 if (!(~mStateFlags & (kFlagPrepared | kFlagPlaying)) && (mPlayer != 0)) { 366 mPlayer->pause(); 367 } 368 GenericPlayer::onPause(); 369 } 370 371 372 void GenericMediaPlayer::onSeekComplete() { 373 SL_LOGV("GenericMediaPlayer::onSeekComplete()"); 374 // did we initiate the seek? 375 if (!(mStateFlags & kFlagSeeking)) { 376 // no, are we looping? 377 if (mStateFlags & kFlagLooping) { 378 // yes, per OpenSL ES 1.0.1 and 1.1 do NOT report it to client 379 // notify(PLAYEREVENT_ENDOFSTREAM, 1, true /*async*/); 380 // no, well that's surprising, but it's probably just a benign race condition 381 } else { 382 SL_LOGW("Unexpected seek complete event ignored"); 383 } 384 } 385 GenericPlayer::onSeekComplete(); 386 } 387 388 389 /** 390 * pre-condition: WHATPARAM_SEEK_SEEKTIME_MS parameter value >= 0 391 */ 392 void GenericMediaPlayer::onSeek(const sp<AMessage> &msg) { 393 SL_LOGV("GenericMediaPlayer::onSeek"); 394 int64_t timeMsec = ANDROID_UNKNOWN_TIME; 395 if (!msg->findInt64(WHATPARAM_SEEK_SEEKTIME_MS, &timeMsec)) { 396 // invalid command, drop it 397 return; 398 } 399 if ((mStateFlags & kFlagSeeking) && (timeMsec == mSeekTimeMsec) && 400 (timeMsec != ANDROID_UNKNOWN_TIME)) { 401 // already seeking to the same non-unknown time, cancel this command 402 return; 403 } else if (mStateFlags & kFlagPreparedUnsuccessfully) { 404 // discard seeks after unsuccessful prepare 405 } else if (!(mStateFlags & kFlagPrepared)) { 406 // we are not ready to accept a seek command at this time, retry later 407 msg->post(DEFAULT_COMMAND_DELAY_FOR_REPOST_US); 408 } else { 409 if (mPlayer != 0) { 410 mStateFlags |= kFlagSeeking; 411 mSeekTimeMsec = (int32_t)timeMsec; 412 // seek to unknown time is used by StreamPlayer after discontinuity 413 if (timeMsec == ANDROID_UNKNOWN_TIME) { 414 // FIXME simulate a MEDIA_SEEK_COMPLETE event in 250 ms; 415 // this is a terrible hack to make up for mediaserver not sending one 416 (new AMessage(kWhatSeekComplete, id()))->post(250000); 417 } else if (OK != mPlayer->seekTo(timeMsec)) { 418 mStateFlags &= ~kFlagSeeking; 419 mSeekTimeMsec = ANDROID_UNKNOWN_TIME; 420 // don't call updateOneShot because seek not yet done 421 } 422 } 423 } 424 } 425 426 427 void GenericMediaPlayer::onLoop(const sp<AMessage> &msg) { 428 SL_LOGV("GenericMediaPlayer::onLoop"); 429 int32_t loop = 0; 430 if (msg->findInt32(WHATPARAM_LOOP_LOOPING, &loop)) { 431 if (loop) { 432 mStateFlags |= kFlagLooping; 433 } else { 434 mStateFlags &= ~kFlagLooping; 435 } 436 // if we have a MediaPlayer then tell it now, otherwise we'll tell it after it's created 437 if (mPlayer != 0) { 438 (void) mPlayer->setLooping(loop); 439 } 440 } 441 } 442 443 444 void GenericMediaPlayer::onVolumeUpdate() { 445 SL_LOGD("GenericMediaPlayer::onVolumeUpdate()"); 446 if (mPlayer != 0) { 447 // use settings lock to read the volume settings 448 Mutex::Autolock _l(mSettingsLock); 449 mPlayer->setVolume(mAndroidAudioLevels.mFinalVolume[0], 450 mAndroidAudioLevels.mFinalVolume[1]); 451 } 452 } 453 454 455 void GenericMediaPlayer::onAttachAuxEffect(const sp<AMessage> &msg) { 456 SL_LOGD("GenericMediaPlayer::onAttachAuxEffect()"); 457 int32_t effectId = 0; 458 if (msg->findInt32(WHATPARAM_ATTACHAUXEFFECT, &effectId)) { 459 if (mPlayer != 0) { 460 status_t status; 461 status = mPlayer->attachAuxEffect(effectId); 462 // attachAuxEffect returns a status but we have no way to report it back to app 463 (void) status; 464 } 465 } 466 } 467 468 469 void GenericMediaPlayer::onSetAuxEffectSendLevel(const sp<AMessage> &msg) { 470 SL_LOGD("GenericMediaPlayer::onSetAuxEffectSendLevel()"); 471 float level = 0.0f; 472 if (msg->findFloat(WHATPARAM_SETAUXEFFECTSENDLEVEL, &level)) { 473 if (mPlayer != 0) { 474 status_t status; 475 status = mPlayer->setAuxEffectSendLevel(level); 476 // setAuxEffectSendLevel returns a status but we have no way to report it back to app 477 (void) status; 478 } 479 } 480 } 481 482 483 void GenericMediaPlayer::onBufferingUpdate(const sp<AMessage> &msg) { 484 int32_t fillLevel = 0; 485 if (msg->findInt32(WHATPARAM_BUFFERING_UPDATE, &fillLevel)) { 486 SL_LOGD("GenericMediaPlayer::onBufferingUpdate(fillLevel=%d)", fillLevel); 487 488 Mutex::Autolock _l(mSettingsLock); 489 mCacheFill = fillLevel; 490 // handle cache fill update 491 if (mCacheFill - mLastNotifiedCacheFill >= mCacheFillNotifThreshold) { 492 notifyCacheFill(); 493 } 494 // handle prefetch status update 495 // compute how much time ahead of position is buffered 496 int durationMsec, positionMsec = -1; 497 if ((mStateFlags & kFlagPrepared) && (mPlayer != 0) 498 && (OK == mPlayer->getDuration(&durationMsec)) 499 && (OK == mPlayer->getCurrentPosition(&positionMsec))) { 500 if ((-1 != durationMsec) && (-1 != positionMsec)) { 501 // evaluate prefetch status based on buffer time thresholds 502 int64_t bufferedDurationMsec = (durationMsec * fillLevel / 100) - positionMsec; 503 CacheStatus_t newCacheStatus = mCacheStatus; 504 if (bufferedDurationMsec > DURATION_CACHED_HIGH_MS) { 505 newCacheStatus = kStatusHigh; 506 } else if (bufferedDurationMsec > DURATION_CACHED_MED_MS) { 507 newCacheStatus = kStatusEnough; 508 } else if (bufferedDurationMsec > DURATION_CACHED_LOW_MS) { 509 newCacheStatus = kStatusIntermediate; 510 } else if (bufferedDurationMsec == 0) { 511 newCacheStatus = kStatusEmpty; 512 } else { 513 newCacheStatus = kStatusLow; 514 } 515 516 if (newCacheStatus != mCacheStatus) { 517 mCacheStatus = newCacheStatus; 518 notifyStatus(); 519 } 520 } 521 } 522 } else { 523 SL_LOGV("GenericMediaPlayer::onBufferingUpdate(fillLevel=unknown)"); 524 } 525 } 526 527 528 //-------------------------------------------------- 529 /** 530 * called from GenericMediaPlayer::onPrepare after the MediaPlayer mPlayer is prepared successfully 531 * pre-conditions: 532 * mPlayer != 0 533 * mPlayer is prepared successfully 534 */ 535 void GenericMediaPlayer::afterMediaPlayerPreparedSuccessfully() { 536 SL_LOGV("GenericMediaPlayer::afterMediaPlayerPrepared()"); 537 assert(mPlayer != 0); 538 assert(mStateFlags & kFlagPrepared); 539 // Mark this player as prepared successfully, so safe to directly call getCurrentPosition 540 { 541 Mutex::Autolock _l(mPreparedPlayerLock); 542 assert(mPreparedPlayer == 0); 543 mPreparedPlayer = mPlayer; 544 } 545 // retrieve channel count 546 int32_t channelCount; 547 Parcel *reply = new Parcel(); 548 status_t status = mPlayer->getParameter(KEY_PARAMETER_AUDIO_CHANNEL_COUNT, reply); 549 if (status == NO_ERROR) { 550 channelCount = reply->readInt32(); 551 } else { 552 // FIXME MPEG-2 TS doesn't yet implement this key, so default to stereo 553 channelCount = 2; 554 } 555 if (UNKNOWN_NUMCHANNELS != channelCount) { 556 // now that we know the channel count, re-calculate the volumes 557 notify(PLAYEREVENT_CHANNEL_COUNT, channelCount, true /*async*/); 558 } else { 559 ALOGW("channel count is still unknown after prepare"); 560 } 561 delete reply; 562 // retrieve duration 563 { 564 int msec = 0; 565 if (OK == mPlayer->getDuration(&msec)) { 566 Mutex::Autolock _l(mSettingsLock); 567 mDurationMsec = msec; 568 } 569 } 570 // now that we have a MediaPlayer, set the looping flag 571 if (mStateFlags & kFlagLooping) { 572 (void) mPlayer->setLooping(1); 573 } 574 // when the MediaPlayer mPlayer is prepared, there is "sufficient data" in the playback buffers 575 // if the data source was local, and the buffers are considered full so we need to notify that 576 bool isLocalSource = true; 577 if (kDataLocatorUri == mDataLocatorType) { 578 isLocalSource = !isDistantProtocol(mDataLocator.uriRef); 579 } 580 if (isLocalSource) { 581 SL_LOGD("media player prepared on local source"); 582 { 583 Mutex::Autolock _l(mSettingsLock); 584 mCacheStatus = kStatusHigh; 585 mCacheFill = 1000; 586 notifyStatus(); 587 notifyCacheFill(); 588 } 589 } else { 590 SL_LOGD("media player prepared on non-local source"); 591 } 592 // when the MediaPlayer mPlayer is prepared, apply the playback rate 593 Parcel rateParcel; 594 if (rateParcel.writeInt32((int32_t)mPlaybackRatePermille) == OK) { 595 mPlayer->setParameter(KEY_PARAMETER_PLAYBACK_RATE_PERMILLE, rateParcel); 596 } 597 } 598 599 600 //-------------------------------------------------- 601 // If player is prepared successfully, set output parameter to that reference, otherwise NULL 602 void GenericMediaPlayer::getPreparedPlayer(sp<IMediaPlayer> &preparedPlayer) 603 { 604 Mutex::Autolock _l(mPreparedPlayerLock); 605 preparedPlayer = mPreparedPlayer; 606 } 607 608 } // namespace android 609