opensles: sprinkle comments and cosmetic fixes

This commit is contained in:
Josep Torra 2012-09-29 19:00:13 +02:00 committed by Sebastian Dröge
parent 78e3b9f428
commit 920354eb0d
4 changed files with 112 additions and 43 deletions

View file

@ -40,7 +40,9 @@ GST_BOILERPLATE_FULL (GstOpenSLESRingBuffer, gst_opensles_ringbuffer,
#define RECORDER_QUEUE_SIZE 2
/* Some generic helper functions */
/*
* Some generic helper functions
*/
static inline SLuint32
_opensles_sample_rate (guint rate)
@ -80,7 +82,6 @@ _opensles_sample_rate (guint rate)
static inline SLuint32
_opensles_channel_mask (GstRingBufferSpec * spec)
{
/* FIXME: handle more than two channels */
switch (spec->channels) {
case 1:
return (SL_SPEAKER_FRONT_CENTER);
@ -104,7 +105,9 @@ _opensles_format (GstRingBufferSpec * spec, SLDataFormat_PCM * format)
(spec->bigend ? SL_BYTEORDER_BIGENDIAN : SL_BYTEORDER_LITTLEENDIAN);
}
/* Recorder related functions */
/*
* Recorder related functions
*/
static gboolean
_opensles_recorder_acquire (GstRingBuffer * rb, GstRingBufferSpec * spec)
@ -126,13 +129,14 @@ _opensles_recorder_acquire (GstRingBuffer * rb, GstRingBufferSpec * spec)
};
SLDataSink audioSink = { &loc_bq, &format };
/* Required optional interfaces */
const SLInterfaceID id[1] = { SL_IID_ANDROIDSIMPLEBUFFERQUEUE };
const SLboolean req[1] = { SL_BOOLEAN_TRUE };
/* Define the format in OpenSL ES terms */
/* Define the audio format in OpenSL ES terminology */
_opensles_format (spec, &format);
/* Create audio recorder (requires the RECORD_AUDIO permission) */
/* Create the audio recorder object (requires the RECORD_AUDIO permission) */
result = (*thiz->engineEngine)->CreateAudioRecorder (thiz->engineEngine,
&thiz->recorderObject, &audioSrc, &audioSink, 1, id, req);
if (result != SL_RESULT_SUCCESS) {
@ -141,7 +145,7 @@ _opensles_recorder_acquire (GstRingBuffer * rb, GstRingBufferSpec * spec)
goto failed;
}
/* Realize the audio recorder */
/* Realize the audio recorder object */
result =
(*thiz->recorderObject)->Realize (thiz->recorderObject, SL_BOOLEAN_FALSE);
if (result != SL_RESULT_SUCCESS) {
@ -179,6 +183,11 @@ failed:
return FALSE;
}
/* This callback function is executed when the ringbuffer is started to preroll
* the output buffer queue with empty buffers, from app thread, and each time
* there's a filled buffer, from audio device processing thread,
* the callback behaviour.
*/
static void
_opensles_recorder_cb (SLAndroidSimpleBufferQueueItf bufferQueue, void *context)
{
@ -189,21 +198,24 @@ _opensles_recorder_cb (SLAndroidSimpleBufferQueueItf bufferQueue, void *context)
gint seg;
gint len;
/* Get a segment form the GStreamer ringbuffer to write in */
if (!gst_ring_buffer_prepare_read (rb, &seg, &ptr, &len)) {
GST_WARNING_OBJECT (rb, "No segment available");
return;
}
/* Enqueue a buffer */
GST_LOG_OBJECT (thiz, "enqueue: %p size %d segment: %d", ptr, len, seg);
result = (*thiz->bufferQueue)->Enqueue (thiz->bufferQueue, ptr, len);
/* Enqueue the sefment as buffer to be written */
result = (*thiz->bufferQueue)->Enqueue (thiz->bufferQueue, ptr, len);
if (result != SL_RESULT_SUCCESS) {
GST_ERROR_OBJECT (thiz, "bufferQueue.Enqueue failed(0x%08x)",
(guint32) result);
return;
}
/* FIXME: we advance here and behaviour might be racy with the reading
* thread */
gst_ring_buffer_advance (rb, 1);
}
@ -226,7 +238,7 @@ _opensles_recorder_start (GstRingBuffer * rb)
thiz->is_queue_callback_registered = TRUE;
}
/* Fill the queue by enqueing buffers */
/* Preroll the buffer queue by enqueing segments */
for (i = 0; i < RECORDER_QUEUE_SIZE; i++) {
_opensles_recorder_cb (NULL, rb);
}
@ -240,6 +252,7 @@ _opensles_recorder_start (GstRingBuffer * rb)
(guint32) result);
return FALSE;
}
return TRUE;
}
@ -280,7 +293,9 @@ _opensles_recorder_stop (GstRingBuffer * rb)
return TRUE;
}
/* Player related functions */
/*
* Player related functions
*/
static gboolean
_opensles_player_change_volume (GstRingBuffer * rb)
@ -326,6 +341,8 @@ _opensles_player_change_mute (GstRingBuffer * rb)
return TRUE;
}
/* This is a callback function invoked by the playback device thread and
* it's used to monitor position changes */
static void
_opensles_player_event_cb (SLPlayItf caller, void *context, SLuint32 event)
{
@ -338,8 +355,6 @@ _opensles_player_event_cb (SLPlayItf caller, void *context, SLuint32 event)
(*caller)->GetPosition (caller, &position);
GST_LOG_OBJECT (thiz, "at position=%u ms", (guint) position);
} else if (event & SL_PLAYEVENT_HEADSTALLED) {
GST_WARNING_OBJECT (thiz, "head stalled");
}
}
@ -363,13 +378,14 @@ _opensles_player_acquire (GstRingBuffer * rb, GstRingBufferSpec * spec)
};
SLDataSink audioSink = { &loc_outmix, NULL };
/* Create an audio player */
/* Define the required interfaces */
const SLInterfaceID ids[2] = { SL_IID_BUFFERQUEUE, SL_IID_VOLUME };
const SLboolean req[2] = { SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE };
/* Define the format in OpenSL ES terms */
/* Define the format in OpenSL ES terminology */
_opensles_format (spec, &format);
/* Create the player object */
result = (*thiz->engineEngine)->CreateAudioPlayer (thiz->engineEngine,
&thiz->playerObject, &audioSrc, &audioSink, 2, ids, req);
if (result != SL_RESULT_SUCCESS) {
@ -378,7 +394,7 @@ _opensles_player_acquire (GstRingBuffer * rb, GstRingBufferSpec * spec)
goto failed;
}
/* Realize the player */
/* Realize the player object */
result =
(*thiz->playerObject)->Realize (thiz->playerObject, SL_BOOLEAN_FALSE);
if (result != SL_RESULT_SUCCESS) {
@ -413,8 +429,8 @@ _opensles_player_acquire (GstRingBuffer * rb, GstRingBufferSpec * spec)
goto failed;
}
/* Request position update events at each 10 ms */
result = (*thiz->playerPlay)->SetPositionUpdatePeriod (thiz->playerPlay, 10);
/* Request position update events at each 20 ms */
result = (*thiz->playerPlay)->SetPositionUpdatePeriod (thiz->playerPlay, 20);
if (result != SL_RESULT_SUCCESS) {
GST_ERROR_OBJECT (thiz, "player.SetPositionUpdatePeriod failed(0x%08x)",
(guint32) result);
@ -423,7 +439,7 @@ _opensles_player_acquire (GstRingBuffer * rb, GstRingBufferSpec * spec)
/* Define the event mask to be monitorized */
result = (*thiz->playerPlay)->SetCallbackEventsMask (thiz->playerPlay,
SL_PLAYEVENT_HEADATNEWPOS | SL_PLAYEVENT_HEADSTALLED);
SL_PLAYEVENT_HEADATNEWPOS);
if (result != SL_RESULT_SUCCESS) {
GST_ERROR_OBJECT (thiz, "player.SetCallbackEventsMask failed(0x%08x)",
(guint32) result);
@ -443,7 +459,7 @@ _opensles_player_acquire (GstRingBuffer * rb, GstRingBufferSpec * spec)
_opensles_player_change_volume (rb);
_opensles_player_change_mute (rb);
/* Define our queue data buffer */
/* Allocate the queue associated ringbuffer memory */
thiz->data_segtotal = loc_bufq.numBuffers;
thiz->data = g_malloc (spec->segsize * thiz->data_segtotal);
thiz->cursor = 0;
@ -454,6 +470,16 @@ failed:
return FALSE;
}
/* This callback function is executed when the ringbuffer is started to preroll
* the input buffer queue with few buffers, from app thread, and each time
* that rendering of one buffer finishes, from audio device processing thread,
* the callback behaviour.
*
* We wrap the queue behaviour with an appropriate chunk of memory (queue len *
* ringbuffer segment size) which is used to hold the audio data while it's
* being processed in the queue. The memory region is used whit a ringbuffer
* behaviour.
*/
static void
_opensles_player_cb (SLAndroidSimpleBufferQueueItf bufferQueue, void *context)
{
@ -464,29 +490,33 @@ _opensles_player_cb (SLAndroidSimpleBufferQueueItf bufferQueue, void *context)
gint seg;
gint len;
/* Get a segment form the GStreamer ringbuffer to read some samples */
if (!gst_ring_buffer_prepare_read (rb, &seg, &ptr, &len)) {
GST_WARNING_OBJECT (rb, "No segment available");
return;
}
/* copy data to our queue ringbuffer */
/* copy the segment data to our queue associated ringbuffer memory */
cur = thiz->data + (thiz->cursor * rb->spec.segsize);
memcpy (cur, ptr, len);
g_atomic_int_inc (&thiz->segqueued);
/* Enqueue a buffer */
GST_LOG_OBJECT (thiz, "enqueue: %p size %d segment: %d in queue[%d]",
cur, len, seg, thiz->cursor);
result = (*thiz->bufferQueue)->Enqueue (thiz->bufferQueue, cur, len);
/* advance the cursor in our queue associated ringbuffer */
thiz->cursor = (thiz->cursor + 1) % thiz->data_segtotal;
/* Enqueue the buffer to be rendered */
result = (*thiz->bufferQueue)->Enqueue (thiz->bufferQueue, cur, len);
if (result != SL_RESULT_SUCCESS) {
GST_ERROR_OBJECT (thiz, "bufferQueue.Enqueue failed(0x%08x)",
(guint32) result);
return;
}
/* Fill with silence samples the segment of the GStreamer ringbuffer */
gst_ring_buffer_clear (rb, seg);
/* Make the segment reusable */
gst_ring_buffer_advance (rb, 1);
}
@ -540,6 +570,7 @@ _opensles_player_pause (GstRingBuffer * rb)
(guint32) result);
return FALSE;
}
return TRUE;
}
@ -553,7 +584,6 @@ _opensles_player_stop (GstRingBuffer * rb)
result =
(*thiz->playerPlay)->SetPlayState (thiz->playerPlay,
SL_PLAYSTATE_STOPPED);
if (result != SL_RESULT_SUCCESS) {
GST_ERROR_OBJECT (thiz, "player.SetPlayState failed(0x%08x)",
(guint32) result);
@ -578,13 +608,17 @@ _opensles_player_stop (GstRingBuffer * rb)
return FALSE;
}
/* Reset our state */
g_atomic_int_set (&thiz->segqueued, 0);
thiz->cursor = 0;
return TRUE;
}
/* OpenSL ES ring buffer wrapper */
/*
* OpenSL ES ringbuffer wrapper
*/
GstRingBuffer *
gst_opensles_ringbuffer_new (RingBufferMode mode)
@ -613,6 +647,7 @@ gst_opensles_ringbuffer_new (RingBufferMode mode)
}
GST_DEBUG_OBJECT (thiz, "ringbuffer created");
return GST_RING_BUFFER (thiz);
}
@ -652,14 +687,14 @@ gst_opensles_ringbuffer_open_device (GstRingBuffer * rb)
thiz = GST_OPENSLES_RING_BUFFER_CAST (rb);
/* Create engine */
/* Create the engine object */
result = slCreateEngine (&thiz->engineObject, 0, NULL, 0, NULL, NULL);
if (result != SL_RESULT_SUCCESS) {
GST_ERROR_OBJECT (thiz, "slCreateEngine failed(0x%08x)", (guint32) result);
goto failed;
}
/* Realize the engine */
/* Realize the engine object */
result = (*thiz->engineObject)->Realize (thiz->engineObject,
SL_BOOLEAN_FALSE);
if (result != SL_RESULT_SUCCESS) {
@ -667,8 +702,7 @@ gst_opensles_ringbuffer_open_device (GstRingBuffer * rb)
goto failed;
}
/* Get the engine interface, which is needed in order to
* create other objects */
/* Get the engine interface, which is needed in order to create other objects */
result = (*thiz->engineObject)->GetInterface (thiz->engineObject,
SL_IID_ENGINE, &thiz->engineEngine);
if (result != SL_RESULT_SUCCESS) {
@ -680,7 +714,7 @@ gst_opensles_ringbuffer_open_device (GstRingBuffer * rb)
if (thiz->mode == RB_MODE_SINK_PCM) {
SLOutputMixItf outputMix;
/* Create an output mixer */
/* Create an output mixer object */
result = (*thiz->engineEngine)->CreateOutputMix (thiz->engineEngine,
&thiz->outputMixObject, 0, NULL, NULL);
if (result != SL_RESULT_SUCCESS) {
@ -689,7 +723,7 @@ gst_opensles_ringbuffer_open_device (GstRingBuffer * rb)
goto failed;
}
/* Realize the output mixer */
/* Realize the output mixer object */
result = (*thiz->outputMixObject)->Realize (thiz->outputMixObject,
SL_BOOLEAN_FALSE);
if (result != SL_RESULT_SUCCESS) {
@ -698,16 +732,18 @@ gst_opensles_ringbuffer_open_device (GstRingBuffer * rb)
goto failed;
}
/* Check for output device options */
/* Get the mixer interface */
result = (*thiz->outputMixObject)->GetInterface (thiz->outputMixObject,
SL_IID_OUTPUTMIX, &outputMix);
if (result != SL_RESULT_SUCCESS) {
GST_WARNING_OBJECT (thiz, "outputMix.GetInterface failed(0x%08x)",
(guint32) result);
} else {
SLint32 numDevices;
SLuint32 deviceIDs[16];
SLint32 numDevices = 0;
SLuint32 deviceIDs[MAX_NUMBER_OUTPUT_DEVICES];
gint i;
/* Query the list of output devices */
(*outputMix)->GetDestinationOutputDeviceIDs (outputMix, &numDevices,
deviceIDs);
GST_DEBUG_OBJECT (thiz, "Found %d output devices", (gint) numDevices);
@ -731,13 +767,13 @@ gst_opensles_ringbuffer_close_device (GstRingBuffer * rb)
thiz = GST_OPENSLES_RING_BUFFER_CAST (rb);
/* Destroy output mix object */
/* Destroy the output mix object */
if (thiz->outputMixObject) {
(*thiz->outputMixObject)->Destroy (thiz->outputMixObject);
thiz->outputMixObject = NULL;
}
/* Destroy engine object, and invalidate all associated interfaces */
/* Destroy the engine object and invalidate all associated interfaces */
if (thiz->engineObject) {
(*thiz->engineObject)->Destroy (thiz->engineObject);
thiz->engineObject = NULL;
@ -801,6 +837,7 @@ gst_opensles_ringbuffer_release (GstRingBuffer * rb)
gst_buffer_unref (rb->data);
rb->data = NULL;
}
GST_DEBUG_OBJECT (thiz, "ringbuffer released");
return TRUE;
}

View file

@ -28,6 +28,8 @@
G_BEGIN_DECLS
#define MAX_NUMBER_OUTPUT_DEVICES 16
#define GST_TYPE_OPENSLES_RING_BUFFER \
(gst_opensles_ringbuffer_get_type())
#define GST_OPENSLES_RING_BUFFER(obj) \

View file

@ -17,6 +17,21 @@
* Boston, MA 02111-1307, USA.
*/
/**
* SECTION:element-openslessink
* @see_also: openslessrc
*
* This element renders raw audio samples using the OpenSL ES API in Android OS.
*
* <refsect2>
* <title>Example pipelines</title>
* |[
* gst-launch -v filesrc location=music.ogg ! oggdemux ! vorbisdec ! audioconvert ! audioresample ! opeslessink
* ]| Play an Ogg/Vorbis file.
* </refsect2>
*
*/
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
@ -40,6 +55,9 @@ enum
/* According to Android's NDK doc the following are the supported rates */
#define RATES "8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100"
/* 48000 Hz is also claimed to be supported but the AudioFlinger downsampling
* doesn't seems to work properly so we relay GStreamer audioresample element
* to cope with this samplerate. */
static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink",
GST_PAD_SINK,
@ -106,11 +124,6 @@ gst_opensles_sink_create_ringbuffer (GstBaseAudioSink * base)
(gint) (aod)->maxSampleRate, (gint) (aod)->isFreqRangeContinuous, \
(gint) (aod)->maxChannels
/* Next it's not defined in Android */
#ifndef MAX_NUMBER_OUTPUT_DEVICES
#define MAX_NUMBER_OUTPUT_DEVICES 16
#endif
static gboolean
_opensles_query_capabilities (GstOpenSLESSink * sink)
{
@ -136,8 +149,7 @@ _opensles_query_capabilities (GstOpenSLESSink * sink)
goto beach;
}
/* Get the engine interface, which is needed in order to
* create other objects */
/* Get the engine interface, which is needed in order to create other objects */
result = (*engineObject)->GetInterface (engineObject,
SL_IID_AUDIOIODEVICECAPABILITIES, &audioIODeviceCapabilities);
if (result != SL_RESULT_SUCCESS) {
@ -147,6 +159,7 @@ _opensles_query_capabilities (GstOpenSLESSink * sink)
goto beach;
}
/* Query the list of available audio outputs */
result = (*audioIODeviceCapabilities)->GetAvailableAudioOutputs
(audioIODeviceCapabilities, &numOutputs, outputDeviceIDs);
if (result != SL_RESULT_SUCCESS) {
@ -182,7 +195,7 @@ _opensles_query_capabilities (GstOpenSLESSink * sink)
res = TRUE;
beach:
/* Destroy engine object */
/* Destroy the engine object */
if (engineObject) {
(*engineObject)->Destroy (engineObject);
}
@ -269,6 +282,8 @@ gst_opensles_sink_init (GstOpenSLESSink * sink, GstOpenSLESSinkClass * gclass)
_opensles_query_capabilities (sink);
gst_base_audio_sink_set_provide_clock (GST_BASE_AUDIO_SINK (sink), TRUE);
/* Override some default values to fit on the AudioFlinger behaviour of
* processing 20ms buffers as minimum buffer size. */
GST_BASE_AUDIO_SINK (sink)->buffer_time = 400000;
GST_BASE_AUDIO_SINK (sink)->latency_time = 20000;
}

View file

@ -17,6 +17,21 @@
* Boston, MA 02111-1307, USA.
*/
/**
* SECTION:element-openslessrc
* @see_also: openslessink
*
* This element reads data from default audio input using the OpenSL ES API in Android OS.
*
* <refsect2>
* <title>Example pipelines</title>
* |[
* gst-launch -v openslessrc ! audioconvert ! vorbisenc ! oggmux ! filesink location=recorded.ogg
* ]| Record from default audio input and encode to Ogg/Vorbis.
* </refsect2>
*
*/
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif