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