IPlay.c revision 3d332ff421e7179c36fb652771cc8ded53383729
1/* 2 * Copyright (C) 2010 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/* Play implementation */ 18 19#include "sles_allinclusive.h" 20 21 22static SLresult IPlay_SetPlayState(SLPlayItf self, SLuint32 state) 23{ 24 SL_ENTER_INTERFACE 25 26 switch (state) { 27 case SL_PLAYSTATE_STOPPED: 28 case SL_PLAYSTATE_PAUSED: 29 case SL_PLAYSTATE_PLAYING: 30 { 31 IPlay *thiz = (IPlay *) self; 32 unsigned attr = ATTR_NONE; 33 result = SL_RESULT_SUCCESS; 34#ifdef USE_OUTPUTMIXEXT 35 CAudioPlayer *audioPlayer = (SL_OBJECTID_AUDIOPLAYER == InterfaceToObjectID(thiz)) ? 36 (CAudioPlayer *) thiz->mThis : NULL; 37#endif 38 interface_lock_exclusive(thiz); 39 SLuint32 oldState = thiz->mState; 40 if (state != oldState) { 41#ifdef USE_OUTPUTMIXEXT 42 for (;; interface_cond_wait(thiz)) { 43 44 // We are comparing the old state (left) vs. new state (right). 45 // Note that the old state is 3 bits wide, but new state is only 2 bits wide. 46 // That is why the old state is on the left and new state is on the right. 47 switch ((oldState << 2) | state) { 48 49 case (SL_PLAYSTATE_STOPPED << 2) | SL_PLAYSTATE_STOPPED: 50 case (SL_PLAYSTATE_PAUSED << 2) | SL_PLAYSTATE_PAUSED: 51 case (SL_PLAYSTATE_PLAYING << 2) | SL_PLAYSTATE_PLAYING: 52 // no-op, and unreachable due to earlier "if (state != oldState)" 53 break; 54 55 case (SL_PLAYSTATE_STOPPED << 2) | SL_PLAYSTATE_PLAYING: 56 case (SL_PLAYSTATE_PAUSED << 2) | SL_PLAYSTATE_PLAYING: 57 attr = ATTR_PLAY_STATE; 58 // set enqueue attribute if queue is non-empty and state becomes PLAYING 59 if ((NULL != audioPlayer) && (audioPlayer->mBufferQueue.mFront != 60 audioPlayer->mBufferQueue.mRear)) { 61 // note that USE_OUTPUTMIXEXT does not support ATTR_ABQ_ENQUEUE 62 attr |= ATTR_BQ_ENQUEUE; 63 } 64 // fall through 65 66 case (SL_PLAYSTATE_STOPPED << 2) | SL_PLAYSTATE_PAUSED: 67 case (SL_PLAYSTATE_PLAYING << 2) | SL_PLAYSTATE_PAUSED: 68 // easy 69 thiz->mState = state; 70 break; 71 72 case (SL_PLAYSTATE_STOPPING << 2) | SL_PLAYSTATE_STOPPED: 73 // either spurious wakeup, or someone else requested same transition 74 continue; 75 76 case (SL_PLAYSTATE_STOPPING << 2) | SL_PLAYSTATE_PAUSED: 77 case (SL_PLAYSTATE_STOPPING << 2) | SL_PLAYSTATE_PLAYING: 78 // wait for other guy to finish his transition, then retry ours 79 continue; 80 81 case (SL_PLAYSTATE_PAUSED << 2) | SL_PLAYSTATE_STOPPED: 82 case (SL_PLAYSTATE_PLAYING << 2) | SL_PLAYSTATE_STOPPED: 83 // tell mixer to stop, then wait for mixer to acknowledge the request to stop 84 thiz->mState = SL_PLAYSTATE_STOPPING; 85 continue; 86 87 default: 88 // unexpected state 89 assert(SL_BOOLEAN_FALSE); 90 result = SL_RESULT_INTERNAL_ERROR; 91 break; 92 93 } 94 95 break; 96 } 97#else 98 // Here life looks easy for an Android, but there are other troubles in play land 99 thiz->mState = state; 100 attr = ATTR_PLAY_STATE; 101 // no need to set ATTR_BQ_ENQUEUE or ATTR_ABQ_ENQUEUE 102#endif 103 } 104 // SL_LOGD("set play state %d", state); 105 interface_unlock_exclusive_attributes(thiz, attr); 106 } 107 break; 108 default: 109 result = SL_RESULT_PARAMETER_INVALID; 110 break; 111 } 112 113 SL_LEAVE_INTERFACE 114} 115 116 117static SLresult IPlay_GetPlayState(SLPlayItf self, SLuint32 *pState) 118{ 119 SL_ENTER_INTERFACE 120 121 if (NULL == pState) { 122 result = SL_RESULT_PARAMETER_INVALID; 123 } else { 124 IPlay *thiz = (IPlay *) self; 125 interface_lock_shared(thiz); 126 SLuint32 state = thiz->mState; 127 interface_unlock_shared(thiz); 128 result = SL_RESULT_SUCCESS; 129#ifdef USE_OUTPUTMIXEXT 130 switch (state) { 131 case SL_PLAYSTATE_STOPPED: // as is 132 case SL_PLAYSTATE_PAUSED: 133 case SL_PLAYSTATE_PLAYING: 134 break; 135 case SL_PLAYSTATE_STOPPING: // these states require re-mapping 136 state = SL_PLAYSTATE_STOPPED; 137 break; 138 default: // impossible 139 assert(SL_BOOLEAN_FALSE); 140 state = SL_PLAYSTATE_STOPPED; 141 result = SL_RESULT_INTERNAL_ERROR; 142 break; 143 } 144#endif 145 *pState = state; 146 } 147 148 SL_LEAVE_INTERFACE 149} 150 151 152static SLresult IPlay_GetDuration(SLPlayItf self, SLmillisecond *pMsec) 153{ 154 SL_ENTER_INTERFACE 155 156 if (NULL == pMsec) { 157 result = SL_RESULT_PARAMETER_INVALID; 158 } else { 159 result = SL_RESULT_SUCCESS; 160 IPlay *thiz = (IPlay *) self; 161 // even though this is a getter, it can modify state due to caching 162 interface_lock_exclusive(thiz); 163 SLmillisecond duration = thiz->mDuration; 164#ifdef ANDROID 165 if (SL_TIME_UNKNOWN == duration) { 166 SLmillisecond temp; 167 switch (InterfaceToObjectID(thiz)) { 168 case SL_OBJECTID_AUDIOPLAYER: 169 result = android_audioPlayer_getDuration(thiz, &temp); 170 break; 171 case XA_OBJECTID_MEDIAPLAYER: 172 result = android_Player_getDuration(thiz, &temp); 173 break; 174 default: 175 result = SL_RESULT_FEATURE_UNSUPPORTED; 176 break; 177 } 178 if (SL_RESULT_SUCCESS == result) { 179 duration = temp; 180 thiz->mDuration = duration; 181 } 182 } 183#else 184 // will be set by containing AudioPlayer or MidiPlayer object at Realize, if known, 185 // otherwise the duration will be updated each time a new maximum position is detected 186#endif 187 interface_unlock_exclusive(thiz); 188 *pMsec = duration; 189 } 190 191 SL_LEAVE_INTERFACE 192} 193 194 195static SLresult IPlay_GetPosition(SLPlayItf self, SLmillisecond *pMsec) 196{ 197 SL_ENTER_INTERFACE 198 199 if (NULL == pMsec) { 200 result = SL_RESULT_PARAMETER_INVALID; 201 } else { 202 IPlay *thiz = (IPlay *) self; 203 SLmillisecond position; 204 interface_lock_shared(thiz); 205#ifdef ANDROID 206 // Android does not use the mPosition field for audio and media players 207 // and doesn't cache the position 208 switch (IObjectToObjectID((thiz)->mThis)) { 209 case SL_OBJECTID_AUDIOPLAYER: 210 android_audioPlayer_getPosition(thiz, &position); 211 break; 212 case XA_OBJECTID_MEDIAPLAYER: 213 android_Player_getPosition(thiz, &position); 214 break; 215 default: 216 // we shouldn'be here 217 assert(SL_BOOLEAN_FALSE); 218 } 219#else 220 // on other platforms we depend on periodic updates to the current position 221 position = thiz->mPosition; 222 // if a seek is pending, then lie about current position so the seek appears synchronous 223 if (SL_OBJECTID_AUDIOPLAYER == InterfaceToObjectID(thiz)) { 224 CAudioPlayer *audioPlayer = (CAudioPlayer *) thiz->mThis; 225 SLmillisecond pos = audioPlayer->mSeek.mPos; 226 if (SL_TIME_UNKNOWN != pos) { 227 position = pos; 228 } 229 } 230#endif 231 interface_unlock_shared(thiz); 232 *pMsec = position; 233 result = SL_RESULT_SUCCESS; 234 } 235 236 SL_LEAVE_INTERFACE 237} 238 239 240static SLresult IPlay_RegisterCallback(SLPlayItf self, slPlayCallback callback, void *pContext) 241{ 242 SL_ENTER_INTERFACE 243 244 IPlay *thiz = (IPlay *) self; 245 interface_lock_exclusive(thiz); 246 thiz->mCallback = callback; 247 thiz->mContext = pContext; 248 // omits _attributes b/c noone cares deeply enough about these fields to need quick notification 249 interface_unlock_exclusive(thiz); 250 result = SL_RESULT_SUCCESS; 251 252 SL_LEAVE_INTERFACE 253} 254 255 256static SLresult IPlay_SetCallbackEventsMask(SLPlayItf self, SLuint32 eventFlags) 257{ 258 SL_ENTER_INTERFACE 259 260 if (eventFlags & ~(SL_PLAYEVENT_HEADATEND | SL_PLAYEVENT_HEADATMARKER | 261 SL_PLAYEVENT_HEADATNEWPOS | SL_PLAYEVENT_HEADMOVING | SL_PLAYEVENT_HEADSTALLED)) { 262 result = SL_RESULT_PARAMETER_INVALID; 263 } else { 264 IPlay *thiz = (IPlay *) self; 265 interface_lock_exclusive(thiz); 266 if (thiz->mEventFlags != eventFlags) { 267#ifdef USE_OUTPUTMIXEXT 268 // enabling the "head at new position" play event will postpone the next update event 269 if (!(thiz->mEventFlags & SL_PLAYEVENT_HEADATNEWPOS) && 270 (eventFlags & SL_PLAYEVENT_HEADATNEWPOS)) { 271 thiz->mFramesSincePositionUpdate = 0; 272 } 273#endif 274 thiz->mEventFlags = eventFlags; 275 interface_unlock_exclusive_attributes(thiz, ATTR_TRANSPORT); 276 } else 277 interface_unlock_exclusive(thiz); 278 result = SL_RESULT_SUCCESS; 279 } 280 281 SL_LEAVE_INTERFACE 282} 283 284 285static SLresult IPlay_GetCallbackEventsMask(SLPlayItf self, SLuint32 *pEventFlags) 286{ 287 SL_ENTER_INTERFACE 288 289 if (NULL == pEventFlags) { 290 result = SL_RESULT_PARAMETER_INVALID; 291 } else { 292 IPlay *thiz = (IPlay *) self; 293 interface_lock_shared(thiz); 294 SLuint32 eventFlags = thiz->mEventFlags; 295 interface_unlock_shared(thiz); 296 *pEventFlags = eventFlags; 297 result = SL_RESULT_SUCCESS; 298 } 299 300 SL_LEAVE_INTERFACE 301} 302 303 304static SLresult IPlay_SetMarkerPosition(SLPlayItf self, SLmillisecond mSec) 305{ 306 SL_ENTER_INTERFACE 307 308 IPlay *thiz = (IPlay *) self; 309 bool significant = false; 310 interface_lock_exclusive(thiz); 311 if (thiz->mMarkerPosition != mSec) { 312 thiz->mMarkerPosition = mSec; 313 if (thiz->mEventFlags & SL_PLAYEVENT_HEADATMARKER) { 314 significant = true; 315 } 316 } 317 if (significant) { 318 interface_unlock_exclusive_attributes(thiz, ATTR_TRANSPORT); 319 } else 320 interface_unlock_exclusive(thiz); 321 result = SL_RESULT_SUCCESS; 322 323 SL_LEAVE_INTERFACE 324} 325 326 327static SLresult IPlay_ClearMarkerPosition(SLPlayItf self) 328{ 329 SL_ENTER_INTERFACE 330 331 IPlay *thiz = (IPlay *) self; 332 bool significant = false; 333 interface_lock_exclusive(thiz); 334 // clearing the marker position is equivalent to setting the marker to SL_TIME_UNKNOWN 335 if (thiz->mMarkerPosition != SL_TIME_UNKNOWN) { 336 thiz->mMarkerPosition = SL_TIME_UNKNOWN; 337 if (thiz->mEventFlags & SL_PLAYEVENT_HEADATMARKER) { 338 significant = true; 339 } 340 } 341 if (significant) { 342 interface_unlock_exclusive_attributes(thiz, ATTR_TRANSPORT); 343 } else { 344 interface_unlock_exclusive(thiz); 345 } 346 result = SL_RESULT_SUCCESS; 347 348 SL_LEAVE_INTERFACE 349} 350 351 352static SLresult IPlay_GetMarkerPosition(SLPlayItf self, SLmillisecond *pMsec) 353{ 354 SL_ENTER_INTERFACE 355 356 if (NULL == pMsec) { 357 result = SL_RESULT_PARAMETER_INVALID; 358 } else { 359 IPlay *thiz = (IPlay *) self; 360 interface_lock_shared(thiz); 361 SLmillisecond markerPosition = thiz->mMarkerPosition; 362 interface_unlock_shared(thiz); 363 *pMsec = markerPosition; 364 result = SL_RESULT_SUCCESS; 365 } 366 367 SL_LEAVE_INTERFACE 368} 369 370 371static SLresult IPlay_SetPositionUpdatePeriod(SLPlayItf self, SLmillisecond mSec) 372{ 373 SL_ENTER_INTERFACE 374 375 if (0 == mSec) { 376 result = SL_RESULT_PARAMETER_INVALID; 377 } else { 378 IPlay *thiz = (IPlay *) self; 379 bool significant = false; 380 interface_lock_exclusive(thiz); 381 if (thiz->mPositionUpdatePeriod != mSec) { 382 thiz->mPositionUpdatePeriod = mSec; 383#ifdef USE_OUTPUTMIXEXT 384 if (SL_OBJECTID_AUDIOPLAYER == InterfaceToObjectID(thiz)) { 385 CAudioPlayer *audioPlayer = (CAudioPlayer *) thiz->mThis; 386 SLuint32 frameUpdatePeriod = ((long long) mSec * 387 (long long) audioPlayer->mSampleRateMilliHz) / 1000000LL; 388 if (0 == frameUpdatePeriod) 389 frameUpdatePeriod = ~0; 390 thiz->mFrameUpdatePeriod = frameUpdatePeriod; 391 // setting a new update period postpones the next callback 392 thiz->mFramesSincePositionUpdate = 0; 393 } 394#endif 395 if (thiz->mEventFlags & SL_PLAYEVENT_HEADATNEWPOS) { 396 significant = true; 397 } 398 } 399 if (significant) { 400 interface_unlock_exclusive_attributes(thiz, ATTR_TRANSPORT); 401 } else 402 interface_unlock_exclusive(thiz); 403 result = SL_RESULT_SUCCESS; 404 } 405 406 SL_LEAVE_INTERFACE 407} 408 409 410static SLresult IPlay_GetPositionUpdatePeriod(SLPlayItf self, SLmillisecond *pMsec) 411{ 412 SL_ENTER_INTERFACE 413 414 if (NULL == pMsec) { 415 result = SL_RESULT_PARAMETER_INVALID; 416 } else { 417 IPlay *thiz = (IPlay *) self; 418 interface_lock_shared(thiz); 419 SLmillisecond positionUpdatePeriod = thiz->mPositionUpdatePeriod; 420 interface_unlock_shared(thiz); 421 *pMsec = positionUpdatePeriod; 422 result = SL_RESULT_SUCCESS; 423 } 424 425 SL_LEAVE_INTERFACE 426} 427 428 429static const struct SLPlayItf_ IPlay_Itf = { 430 IPlay_SetPlayState, 431 IPlay_GetPlayState, 432 IPlay_GetDuration, 433 IPlay_GetPosition, 434 IPlay_RegisterCallback, 435 IPlay_SetCallbackEventsMask, 436 IPlay_GetCallbackEventsMask, 437 IPlay_SetMarkerPosition, 438 IPlay_ClearMarkerPosition, 439 IPlay_GetMarkerPosition, 440 IPlay_SetPositionUpdatePeriod, 441 IPlay_GetPositionUpdatePeriod 442}; 443 444void IPlay_init(void *self) 445{ 446 IPlay *thiz = (IPlay *) self; 447 thiz->mItf = &IPlay_Itf; 448 thiz->mState = SL_PLAYSTATE_STOPPED; 449 thiz->mDuration = SL_TIME_UNKNOWN; // will be set by containing player object 450 thiz->mPosition = (SLmillisecond) 0; 451 thiz->mCallback = NULL; 452 thiz->mContext = NULL; 453 thiz->mEventFlags = 0; 454 thiz->mMarkerPosition = SL_TIME_UNKNOWN; 455 thiz->mPositionUpdatePeriod = 1000; // per spec 456#ifdef USE_OUTPUTMIXEXT 457 thiz->mFrameUpdatePeriod = 0; // because we don't know the sample rate yet 458 thiz->mLastSeekPosition = 0; 459 thiz->mFramesSinceLastSeek = 0; 460 thiz->mFramesSincePositionUpdate = 0; 461#endif 462} 463