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