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