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