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
IPlay_SetPlayState(SLPlayItf self,SLuint32 state)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
IPlay_GetPlayState(SLPlayItf self,SLuint32 * pState)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
IPlay_GetDuration(SLPlayItf self,SLmillisecond * pMsec)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
IPlay_GetPosition(SLPlayItf self,SLmillisecond * pMsec)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
IPlay_RegisterCallback(SLPlayItf self,slPlayCallback callback,void * pContext)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
IPlay_SetCallbackEventsMask(SLPlayItf self,SLuint32 eventFlags)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
IPlay_GetCallbackEventsMask(SLPlayItf self,SLuint32 * pEventFlags)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
IPlay_SetMarkerPosition(SLPlayItf self,SLmillisecond mSec)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
IPlay_ClearMarkerPosition(SLPlayItf self)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
IPlay_GetMarkerPosition(SLPlayItf self,SLmillisecond * pMsec)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
IPlay_SetPositionUpdatePeriod(SLPlayItf self,SLmillisecond mSec)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
IPlay_GetPositionUpdatePeriod(SLPlayItf self,SLmillisecond * pMsec)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
IPlay_init(void * self)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