Buffer queue and audio player maintenance.
Known problems on Android only (SDL is ok):
High latency on first enqueue, or first after buffer underrun
Play after stop does not resume the current buffer.
Improvements:
Implement BufferQueue::Clear for both Android and SDL.
Play::SetPlayState to Stopped now works for SDL (not yet for Android), and
sets play cursor to start of current buffer.
Play::Pause and Play::Stop while playing now block until done for SDL.
SDL now calls buffer queue callback after each buffer completion, instead of when mixer needs a buffer.
Move SF_INFO sfinfo to CAudioPlayer so we always have good stuff like the sample rate etc.
BufferQueue::Enqueue on a successful enqueue to a formerly empty queue
now kicks off an attribute change followed by a synchronous call to a
hook when first buffer enqueued. Currently unused but could be a way to reduce latency.
Minor comment changes.
Add interface_cond_broadcast.
More logging of errors.
Include source file and line number in logs.
sles_to_android.cpp: needs code for #if 0 sections.
Disable trace logging of BufferQueue::Enqueue and GetState.
Fix printf warnings.
Fix typo in number of interfaces requested at CreateAudioPlayer.
Remove spurious printf and while loop.
Line length 100.
Add intermediate play states which need to be re-mapped.
Fix printf format warnings.
Re-organize the buffer queue initialization.
Implement true seeking based on position for libsndfile.
SndFile is working again, but just barely.
Make it build on non-Android platforms.
BufferQueue::RegisterCallback now checks for STOPPED play state.
Include function name in trace logs.
Enhance test to check buffer queue callbacks, get buffer queue state, get play state.
Fix bug with Stop while Paused not resetting buffer pointer.
Remove obsolete comments and #if 0.
Set enqueue attribute is state is PLAYING and the first buffer is enqueued.
Added slutPrintIIDs.
Update comments.
Fix build warnings.
Fix compile error introduced by the merge.
Fix runtime problems introduced during the merge.
Address code review comments:
- BufferQueue changes apply to AudioRecord also.
- Rename to conform to new naming conventions.
- Add comments.
Fix build warning.
Add interactive buffer queue test application.
Change-Id: I5b00545a599fd9dba96efb834fca2369d149eaaa
diff --git a/libopensles/IBufferQueue.c b/libopensles/IBufferQueue.c
index e048529..3eae19c 100644
--- a/libopensles/IBufferQueue.c
+++ b/libopensles/IBufferQueue.c
@@ -19,11 +19,37 @@
#include "sles_allinclusive.h"
+/** Determine the state of the audio player or audio recorder associated with a buffer queue.
+ * Note that PLAYSTATE and RECORDSTATE values are equivalent (where PLAYING == RECORDING).
+ */
+
+static SLuint32 getAssociatedState(IBufferQueue *this)
+{
+ SLuint32 state;
+ switch (InterfaceToObjectID(this)) {
+ case SL_OBJECTID_AUDIOPLAYER:
+ state = ((CAudioPlayer *) this->mThis)->mPlay.mState;
+ break;
+ case SL_OBJECTID_AUDIORECORDER:
+ state = ((CAudioRecorder *) this->mThis)->mRecord.mState;
+ break;
+ default:
+ // unreachable, but just in case we will assume it is stopped
+ assert(SL_BOOLEAN_FALSE);
+ state = SL_PLAYSTATE_STOPPED;
+ break;
+ }
+ return state;
+}
+
+
static SLresult IBufferQueue_Enqueue(SLBufferQueueItf self, const void *pBuffer, SLuint32 size)
{
SL_ENTER_INTERFACE
//SL_LOGV("IBufferQueue_Enqueue(%p, %p, %lu)", self, pBuffer, size);
+ // Note that Enqueue while a Clear is pending is equivalent to Enqueue followed by Clear
+
if (NULL == pBuffer || 0 == size) {
result = SL_RESULT_PARAMETER_INVALID;
} else {
@@ -41,7 +67,10 @@
++this->mState.count;
result = SL_RESULT_SUCCESS;
}
- interface_unlock_exclusive(this);
+ // set enqueue attribute if state is PLAYING and the first buffer is enqueued
+ interface_unlock_exclusive_attributes(this, ((SL_RESULT_SUCCESS == result) &&
+ (1 == this->mState.count) && (SL_PLAYSTATE_PLAYING == getAssociatedState(this))) ?
+ ATTR_ENQUEUE : ATTR_NONE);
}
SL_LEAVE_INTERFACE
}
@@ -51,18 +80,34 @@
{
SL_ENTER_INTERFACE
+ result = SL_RESULT_SUCCESS;
IBufferQueue *this = (IBufferQueue *) self;
interface_lock_exclusive(this);
- this->mFront = &this->mArray[0];
- this->mRear = &this->mArray[0];
- this->mSizeConsumed = 0;
- this->mState.count = 0;
+
#ifdef ANDROID
- // FIXME must flush associated player
- SL_LOGE("FIXME: IBufferQueue_Clear must flush associated player, not implemented");
+ if (SL_OBJECTID_AUDIOPLAYER == InterfaceToObjectID(this)) {
+ CAudioPlayer *audioPlayer = (CAudioPlayer *) this->mThis;
+ // flush associated audio player
+ result = android_audioPlayerClear(audioPlayer);
+ if (SL_RESULT_SUCCESS == result) {
+ this->mFront = &this->mArray[0];
+ this->mRear = &this->mArray[0];
+ this->mState.count = 0;
+ this->mSizeConsumed = 0;
+ }
+ }
#endif
+
+#ifdef USE_OUTPUTMIXEXT
+ // mixer might be reading from the front buffer, so tread carefully here
+ // NTH asynchronous cancel instead of blocking until mixer acknowledges
+ this->mClearRequested = SL_BOOLEAN_TRUE;
+ do
+ interface_cond_wait(this);
+ while (this->mClearRequested);
+#endif
+
interface_unlock_exclusive(this);
- result = SL_RESULT_SUCCESS;
SL_LEAVE_INTERFACE
}
@@ -72,6 +117,8 @@
{
SL_ENTER_INTERFACE
+ // Note that GetState while a Clear is pending is equivalent to GetState before the Clear
+
if (NULL == pState) {
result = SL_RESULT_PARAMETER_INVALID;
} else {
@@ -98,15 +145,17 @@
{
SL_ENTER_INTERFACE
- // FIXME verify conditions media object is in the SL_PLAYSTATE_STOPPED state
- SL_LOGE("FIXME: verify RegisterCallback is called on a player in SL_PLAYSTATE_STOPPED \
-state, not implemented");
IBufferQueue *this = (IBufferQueue *) self;
interface_lock_exclusive(this);
- this->mCallback = callback;
- this->mContext = pContext;
+ // verify pre-condition that media object is in the SL_PLAYSTATE_STOPPED state
+ if (SL_PLAYSTATE_STOPPED == getAssociatedState(this)) {
+ this->mCallback = callback;
+ this->mContext = pContext;
+ result = SL_RESULT_SUCCESS;
+ } else {
+ result = SL_RESULT_PRECONDITIONS_VIOLATED;
+ }
interface_unlock_exclusive(this);
- result = SL_RESULT_SUCCESS;
SL_LEAVE_INTERFACE
}
@@ -129,10 +178,13 @@
this->mCallback = NULL;
this->mContext = NULL;
this->mNumBuffers = 0;
+ this->mClearRequested = SL_BOOLEAN_FALSE;
this->mArray = NULL;
this->mFront = NULL;
this->mRear = NULL;
+#ifdef ANDROID
this->mSizeConsumed = 0;
+#endif
BufferHeader *bufferHeader = this->mTypical;
unsigned i;
for (i = 0; i < BUFFER_HEADER_TYPICAL+1; ++i, ++bufferHeader) {