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                 FALLTHROUGH_INTENDED;
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