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