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 22 static 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 117 static 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 152 static 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 195 static 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 240 static 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 256 static 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 } 279 result = SL_RESULT_SUCCESS; 280 } 281 282 SL_LEAVE_INTERFACE 283 } 284 285 286 static SLresult IPlay_GetCallbackEventsMask(SLPlayItf self, SLuint32 *pEventFlags) 287 { 288 SL_ENTER_INTERFACE 289 290 if (NULL == pEventFlags) { 291 result = SL_RESULT_PARAMETER_INVALID; 292 } else { 293 IPlay *thiz = (IPlay *) self; 294 interface_lock_shared(thiz); 295 SLuint32 eventFlags = thiz->mEventFlags; 296 interface_unlock_shared(thiz); 297 *pEventFlags = eventFlags; 298 result = SL_RESULT_SUCCESS; 299 } 300 301 SL_LEAVE_INTERFACE 302 } 303 304 305 static SLresult IPlay_SetMarkerPosition(SLPlayItf self, SLmillisecond mSec) 306 { 307 SL_ENTER_INTERFACE 308 309 if (SL_TIME_UNKNOWN == mSec) { 310 result = SL_RESULT_PARAMETER_INVALID; 311 } else { 312 IPlay *thiz = (IPlay *) self; 313 bool significant = false; 314 interface_lock_exclusive(thiz); 315 if (thiz->mMarkerPosition != mSec) { 316 thiz->mMarkerPosition = mSec; 317 if (thiz->mEventFlags & SL_PLAYEVENT_HEADATMARKER) { 318 significant = true; 319 } 320 } 321 if (significant) { 322 interface_unlock_exclusive_attributes(thiz, ATTR_TRANSPORT); 323 } else { 324 interface_unlock_exclusive(thiz); 325 } 326 result = SL_RESULT_SUCCESS; 327 } 328 329 SL_LEAVE_INTERFACE 330 } 331 332 333 static SLresult IPlay_ClearMarkerPosition(SLPlayItf self) 334 { 335 SL_ENTER_INTERFACE 336 337 IPlay *thiz = (IPlay *) self; 338 bool significant = false; 339 interface_lock_exclusive(thiz); 340 // clearing the marker position is equivalent to setting the marker to SL_TIME_UNKNOWN 341 if (thiz->mMarkerPosition != SL_TIME_UNKNOWN) { 342 thiz->mMarkerPosition = SL_TIME_UNKNOWN; 343 if (thiz->mEventFlags & SL_PLAYEVENT_HEADATMARKER) { 344 significant = true; 345 } 346 } 347 if (significant) { 348 interface_unlock_exclusive_attributes(thiz, ATTR_TRANSPORT); 349 } else { 350 interface_unlock_exclusive(thiz); 351 } 352 result = SL_RESULT_SUCCESS; 353 354 SL_LEAVE_INTERFACE 355 } 356 357 358 static SLresult IPlay_GetMarkerPosition(SLPlayItf self, SLmillisecond *pMsec) 359 { 360 SL_ENTER_INTERFACE 361 362 if (NULL == pMsec) { 363 result = SL_RESULT_PARAMETER_INVALID; 364 } else { 365 IPlay *thiz = (IPlay *) self; 366 interface_lock_shared(thiz); 367 SLmillisecond markerPosition = thiz->mMarkerPosition; 368 interface_unlock_shared(thiz); 369 *pMsec = markerPosition; 370 if (SL_TIME_UNKNOWN == markerPosition) { 371 result = SL_RESULT_PRECONDITIONS_VIOLATED; 372 } else { 373 result = SL_RESULT_SUCCESS; 374 } 375 } 376 377 SL_LEAVE_INTERFACE 378 } 379 380 381 static SLresult IPlay_SetPositionUpdatePeriod(SLPlayItf self, SLmillisecond mSec) 382 { 383 SL_ENTER_INTERFACE 384 385 if (0 == mSec) { 386 result = SL_RESULT_PARAMETER_INVALID; 387 } else { 388 IPlay *thiz = (IPlay *) self; 389 bool significant = false; 390 interface_lock_exclusive(thiz); 391 if (thiz->mPositionUpdatePeriod != mSec) { 392 thiz->mPositionUpdatePeriod = mSec; 393 #ifdef USE_OUTPUTMIXEXT 394 if (SL_OBJECTID_AUDIOPLAYER == InterfaceToObjectID(thiz)) { 395 CAudioPlayer *audioPlayer = (CAudioPlayer *) thiz->mThis; 396 SLuint32 frameUpdatePeriod = ((long long) mSec * 397 (long long) audioPlayer->mSampleRateMilliHz) / 1000000LL; 398 if (0 == frameUpdatePeriod) { 399 frameUpdatePeriod = ~0; 400 } 401 thiz->mFrameUpdatePeriod = frameUpdatePeriod; 402 // setting a new update period postpones the next callback 403 thiz->mFramesSincePositionUpdate = 0; 404 } 405 #endif 406 if (thiz->mEventFlags & SL_PLAYEVENT_HEADATNEWPOS) { 407 significant = true; 408 } 409 } 410 if (significant) { 411 interface_unlock_exclusive_attributes(thiz, ATTR_TRANSPORT); 412 } else { 413 interface_unlock_exclusive(thiz); 414 } 415 result = SL_RESULT_SUCCESS; 416 } 417 418 SL_LEAVE_INTERFACE 419 } 420 421 422 static SLresult IPlay_GetPositionUpdatePeriod(SLPlayItf self, SLmillisecond *pMsec) 423 { 424 SL_ENTER_INTERFACE 425 426 if (NULL == pMsec) { 427 result = SL_RESULT_PARAMETER_INVALID; 428 } else { 429 IPlay *thiz = (IPlay *) self; 430 interface_lock_shared(thiz); 431 SLmillisecond positionUpdatePeriod = thiz->mPositionUpdatePeriod; 432 interface_unlock_shared(thiz); 433 *pMsec = positionUpdatePeriod; 434 result = SL_RESULT_SUCCESS; 435 } 436 437 SL_LEAVE_INTERFACE 438 } 439 440 441 static const struct SLPlayItf_ IPlay_Itf = { 442 IPlay_SetPlayState, 443 IPlay_GetPlayState, 444 IPlay_GetDuration, 445 IPlay_GetPosition, 446 IPlay_RegisterCallback, 447 IPlay_SetCallbackEventsMask, 448 IPlay_GetCallbackEventsMask, 449 IPlay_SetMarkerPosition, 450 IPlay_ClearMarkerPosition, 451 IPlay_GetMarkerPosition, 452 IPlay_SetPositionUpdatePeriod, 453 IPlay_GetPositionUpdatePeriod 454 }; 455 456 void IPlay_init(void *self) 457 { 458 IPlay *thiz = (IPlay *) self; 459 thiz->mItf = &IPlay_Itf; 460 thiz->mState = SL_PLAYSTATE_STOPPED; 461 thiz->mDuration = SL_TIME_UNKNOWN; // will be set by containing player object 462 thiz->mPosition = (SLmillisecond) 0; 463 thiz->mCallback = NULL; 464 thiz->mContext = NULL; 465 thiz->mEventFlags = 0; 466 thiz->mMarkerPosition = SL_TIME_UNKNOWN; 467 thiz->mPositionUpdatePeriod = 1000; // per spec 468 #ifdef USE_OUTPUTMIXEXT 469 thiz->mFrameUpdatePeriod = 0; // because we don't know the sample rate yet 470 thiz->mLastSeekPosition = 0; 471 thiz->mFramesSinceLastSeek = 0; 472 thiz->mFramesSincePositionUpdate = 0; 473 #endif 474 } 475