• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 *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 
IPlay_GetPlayState(SLPlayItf self,SLuint32 * pState)109 static 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 
IPlay_GetDuration(SLPlayItf self,SLmillisecond * pMsec)144 static 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 
IPlay_GetPosition(SLPlayItf self,SLmillisecond * pMsec)178 static 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 
IPlay_RegisterCallback(SLPlayItf self,slPlayCallback callback,void * pContext)217 static 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 
IPlay_SetCallbackEventsMask(SLPlayItf self,SLuint32 eventFlags)233 static 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 
IPlay_GetCallbackEventsMask(SLPlayItf self,SLuint32 * pEventFlags)262 static 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 
IPlay_SetMarkerPosition(SLPlayItf self,SLmillisecond mSec)281 static 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 
IPlay_ClearMarkerPosition(SLPlayItf self)298 static 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 
IPlay_GetMarkerPosition(SLPlayItf self,SLmillisecond * pMsec)317 static 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 
IPlay_SetPositionUpdatePeriod(SLPlayItf self,SLmillisecond mSec)336 static 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 
IPlay_GetPositionUpdatePeriod(SLPlayItf self,SLmillisecond * pMsec)374 static 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 
393 static 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 
IPlay_init(void * self)408 void 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