• Home
  • History
  • Annotate
  • 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  
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 someone else to finish their 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