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