IPlay.c revision 5933f3d5e532aaac31ce0e6551c59f0197c0ae3c
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 (SL_OBJECTID_AUDIOPLAYER == InterfaceToObjectID(thiz))) { 167 SLmillisecond temp; 168 result = android_audioPlayer_getDuration(thiz, &temp); 169 if (SL_RESULT_SUCCESS == result) { 170 duration = temp; 171 thiz->mDuration = duration; 172 } 173 } 174 //FIXME support GetDuration for XA_OBJECTID_MEDIAPLAYER 175#else 176 // will be set by containing AudioPlayer or MidiPlayer object at Realize, if known, 177 // otherwise the duration will be updated each time a new maximum position is detected 178#endif 179 interface_unlock_exclusive(thiz); 180 *pMsec = duration; 181 } 182 183 SL_LEAVE_INTERFACE 184} 185 186 187static SLresult IPlay_GetPosition(SLPlayItf self, SLmillisecond *pMsec) 188{ 189 SL_ENTER_INTERFACE 190 191 if (NULL == pMsec) { 192 result = SL_RESULT_PARAMETER_INVALID; 193 } else { 194 IPlay *thiz = (IPlay *) self; 195 SLmillisecond position; 196 interface_lock_shared(thiz); 197#ifdef ANDROID 198 // Android does not use the mPosition field for audio and media players 199 // and doesn't cache the position 200 switch (IObjectToObjectID((thiz)->mThis)) { 201 case SL_OBJECTID_AUDIOPLAYER: 202 android_audioPlayer_getPosition(thiz, &position); 203 break; 204 case XA_OBJECTID_MEDIAPLAYER: 205 android_Player_getPosition(thiz, &position); 206 break; 207 default: 208 // we shouldn'be here 209 assert(SL_BOOLEAN_FALSE); 210 } 211#else 212 // on other platforms we depend on periodic updates to the current position 213 position = thiz->mPosition; 214 // if a seek is pending, then lie about current position so the seek appears synchronous 215 if (SL_OBJECTID_AUDIOPLAYER == InterfaceToObjectID(thiz)) { 216 CAudioPlayer *audioPlayer = (CAudioPlayer *) thiz->mThis; 217 SLmillisecond pos = audioPlayer->mSeek.mPos; 218 if (SL_TIME_UNKNOWN != pos) { 219 position = pos; 220 } 221 } 222#endif 223 interface_unlock_shared(thiz); 224 *pMsec = position; 225 result = SL_RESULT_SUCCESS; 226 } 227 228 SL_LEAVE_INTERFACE 229} 230 231 232static SLresult IPlay_RegisterCallback(SLPlayItf self, slPlayCallback callback, void *pContext) 233{ 234 SL_ENTER_INTERFACE 235 236 IPlay *thiz = (IPlay *) self; 237 interface_lock_exclusive(thiz); 238 thiz->mCallback = callback; 239 thiz->mContext = pContext; 240 // omits _attributes b/c noone cares deeply enough about these fields to need quick notification 241 interface_unlock_exclusive(thiz); 242 result = SL_RESULT_SUCCESS; 243 244 SL_LEAVE_INTERFACE 245} 246 247 248static SLresult IPlay_SetCallbackEventsMask(SLPlayItf self, SLuint32 eventFlags) 249{ 250 SL_ENTER_INTERFACE 251 252 if (eventFlags & ~(SL_PLAYEVENT_HEADATEND | SL_PLAYEVENT_HEADATMARKER | 253 SL_PLAYEVENT_HEADATNEWPOS | SL_PLAYEVENT_HEADMOVING | SL_PLAYEVENT_HEADSTALLED)) { 254 result = SL_RESULT_PARAMETER_INVALID; 255 } else { 256 IPlay *thiz = (IPlay *) self; 257 interface_lock_exclusive(thiz); 258 if (thiz->mEventFlags != eventFlags) { 259#ifdef USE_OUTPUTMIXEXT 260 // enabling the "head at new position" play event will postpone the next update event 261 if (!(thiz->mEventFlags & SL_PLAYEVENT_HEADATNEWPOS) && 262 (eventFlags & SL_PLAYEVENT_HEADATNEWPOS)) { 263 thiz->mFramesSincePositionUpdate = 0; 264 } 265#endif 266 thiz->mEventFlags = eventFlags; 267 interface_unlock_exclusive_attributes(thiz, ATTR_TRANSPORT); 268 } else 269 interface_unlock_exclusive(thiz); 270 result = SL_RESULT_SUCCESS; 271 } 272 273 SL_LEAVE_INTERFACE 274} 275 276 277static SLresult IPlay_GetCallbackEventsMask(SLPlayItf self, SLuint32 *pEventFlags) 278{ 279 SL_ENTER_INTERFACE 280 281 if (NULL == pEventFlags) { 282 result = SL_RESULT_PARAMETER_INVALID; 283 } else { 284 IPlay *thiz = (IPlay *) self; 285 interface_lock_shared(thiz); 286 SLuint32 eventFlags = thiz->mEventFlags; 287 interface_unlock_shared(thiz); 288 *pEventFlags = eventFlags; 289 result = SL_RESULT_SUCCESS; 290 } 291 292 SL_LEAVE_INTERFACE 293} 294 295 296static SLresult IPlay_SetMarkerPosition(SLPlayItf self, SLmillisecond mSec) 297{ 298 SL_ENTER_INTERFACE 299 300 IPlay *thiz = (IPlay *) self; 301 bool significant = false; 302 interface_lock_exclusive(thiz); 303 if (thiz->mMarkerPosition != mSec) { 304 thiz->mMarkerPosition = mSec; 305 if (thiz->mEventFlags & SL_PLAYEVENT_HEADATMARKER) { 306 significant = true; 307 } 308 } 309 if (significant) { 310 interface_unlock_exclusive_attributes(thiz, ATTR_TRANSPORT); 311 } else 312 interface_unlock_exclusive(thiz); 313 result = SL_RESULT_SUCCESS; 314 315 SL_LEAVE_INTERFACE 316} 317 318 319static SLresult IPlay_ClearMarkerPosition(SLPlayItf self) 320{ 321 SL_ENTER_INTERFACE 322 323 IPlay *thiz = (IPlay *) self; 324 bool significant = false; 325 interface_lock_exclusive(thiz); 326 // clearing the marker position is equivalent to setting the marker to SL_TIME_UNKNOWN 327 if (thiz->mMarkerPosition != SL_TIME_UNKNOWN) { 328 thiz->mMarkerPosition = SL_TIME_UNKNOWN; 329 if (thiz->mEventFlags & SL_PLAYEVENT_HEADATMARKER) { 330 significant = true; 331 } 332 } 333 if (significant) { 334 interface_unlock_exclusive_attributes(thiz, ATTR_TRANSPORT); 335 } else { 336 interface_unlock_exclusive(thiz); 337 } 338 result = SL_RESULT_SUCCESS; 339 340 SL_LEAVE_INTERFACE 341} 342 343 344static SLresult IPlay_GetMarkerPosition(SLPlayItf self, SLmillisecond *pMsec) 345{ 346 SL_ENTER_INTERFACE 347 348 if (NULL == pMsec) { 349 result = SL_RESULT_PARAMETER_INVALID; 350 } else { 351 IPlay *thiz = (IPlay *) self; 352 interface_lock_shared(thiz); 353 SLmillisecond markerPosition = thiz->mMarkerPosition; 354 interface_unlock_shared(thiz); 355 *pMsec = markerPosition; 356 result = SL_RESULT_SUCCESS; 357 } 358 359 SL_LEAVE_INTERFACE 360} 361 362 363static SLresult IPlay_SetPositionUpdatePeriod(SLPlayItf self, SLmillisecond mSec) 364{ 365 SL_ENTER_INTERFACE 366 367 if (0 == mSec) { 368 result = SL_RESULT_PARAMETER_INVALID; 369 } else { 370 IPlay *thiz = (IPlay *) self; 371 bool significant = false; 372 interface_lock_exclusive(thiz); 373 if (thiz->mPositionUpdatePeriod != mSec) { 374 thiz->mPositionUpdatePeriod = mSec; 375#ifdef USE_OUTPUTMIXEXT 376 if (SL_OBJECTID_AUDIOPLAYER == InterfaceToObjectID(thiz)) { 377 CAudioPlayer *audioPlayer = (CAudioPlayer *) thiz->mThis; 378 SLuint32 frameUpdatePeriod = ((long long) mSec * 379 (long long) audioPlayer->mSampleRateMilliHz) / 1000000LL; 380 if (0 == frameUpdatePeriod) 381 frameUpdatePeriod = ~0; 382 thiz->mFrameUpdatePeriod = frameUpdatePeriod; 383 // setting a new update period postpones the next callback 384 thiz->mFramesSincePositionUpdate = 0; 385 } 386#endif 387 if (thiz->mEventFlags & SL_PLAYEVENT_HEADATNEWPOS) { 388 significant = true; 389 } 390 } 391 if (significant) { 392 interface_unlock_exclusive_attributes(thiz, ATTR_TRANSPORT); 393 } else 394 interface_unlock_exclusive(thiz); 395 result = SL_RESULT_SUCCESS; 396 } 397 398 SL_LEAVE_INTERFACE 399} 400 401 402static SLresult IPlay_GetPositionUpdatePeriod(SLPlayItf self, SLmillisecond *pMsec) 403{ 404 SL_ENTER_INTERFACE 405 406 if (NULL == pMsec) { 407 result = SL_RESULT_PARAMETER_INVALID; 408 } else { 409 IPlay *thiz = (IPlay *) self; 410 interface_lock_shared(thiz); 411 SLmillisecond positionUpdatePeriod = thiz->mPositionUpdatePeriod; 412 interface_unlock_shared(thiz); 413 *pMsec = positionUpdatePeriod; 414 result = SL_RESULT_SUCCESS; 415 } 416 417 SL_LEAVE_INTERFACE 418} 419 420 421static const struct SLPlayItf_ IPlay_Itf = { 422 IPlay_SetPlayState, 423 IPlay_GetPlayState, 424 IPlay_GetDuration, 425 IPlay_GetPosition, 426 IPlay_RegisterCallback, 427 IPlay_SetCallbackEventsMask, 428 IPlay_GetCallbackEventsMask, 429 IPlay_SetMarkerPosition, 430 IPlay_ClearMarkerPosition, 431 IPlay_GetMarkerPosition, 432 IPlay_SetPositionUpdatePeriod, 433 IPlay_GetPositionUpdatePeriod 434}; 435 436void IPlay_init(void *self) 437{ 438 IPlay *thiz = (IPlay *) self; 439 thiz->mItf = &IPlay_Itf; 440 thiz->mState = SL_PLAYSTATE_STOPPED; 441 thiz->mDuration = SL_TIME_UNKNOWN; // will be set by containing player object 442 thiz->mPosition = (SLmillisecond) 0; 443 thiz->mCallback = NULL; 444 thiz->mContext = NULL; 445 thiz->mEventFlags = 0; 446 thiz->mMarkerPosition = SL_TIME_UNKNOWN; 447 thiz->mPositionUpdatePeriod = 1000; // per spec 448#ifdef USE_OUTPUTMIXEXT 449 thiz->mFrameUpdatePeriod = 0; // because we don't know the sample rate yet 450 thiz->mLastSeekPosition = 0; 451 thiz->mFramesSinceLastSeek = 0; 452 thiz->mFramesSincePositionUpdate = 0; 453#endif 454} 455