static gboolean gst_audioringbuffer_acquire (GstRingBuffer * buf, GstRingBufferSpec * spec) { GstAudioSink *sink; GstAudioSinkClass *csink; gboolean result = FALSE; sink = GST_AUDIO_SINK (GST_OBJECT_PARENT (buf)); csink = GST_AUDIO_SINK_GET_CLASS (sink); if (csink->prepare) result = csink->prepare (sink, spec); if (!result) goto could_not_prepare; /* set latency to one more segment as we need some headroom */ spec->seglatency = spec->segtotal + 1; buf->data = gst_buffer_new_and_alloc (spec->segtotal * spec->segsize); memset (GST_BUFFER_DATA (buf->data), 0, GST_BUFFER_SIZE (buf->data)); return TRUE; /* ERRORS */ could_not_prepare: { GST_DEBUG_OBJECT (sink, "could not prepare device"); return FALSE; } }
static gboolean gst_audioringbuffer_stop (GstRingBuffer * buf) { GstAudioSink *sink; GstAudioSinkClass *csink; sink = GST_AUDIO_SINK (GST_OBJECT_PARENT (buf)); csink = GST_AUDIO_SINK_GET_CLASS (sink); /* unblock any pending writes to the audio device */ if (csink->reset) { GST_DEBUG_OBJECT (sink, "reset..."); csink->reset (sink); GST_DEBUG_OBJECT (sink, "reset done"); } #if 0 if (abuf->running) { GST_DEBUG_OBJECT (sink, "stop, waiting..."); GST_AUDIORING_BUFFER_WAIT (buf); GST_DEBUG_OBJECT (sink, "stopped"); } #endif return TRUE; }
/* function is called with LOCK */ static gboolean gst_audioringbuffer_release (GstRingBuffer * buf) { GstAudioSink *sink; GstAudioSinkClass *csink; gboolean result = FALSE; sink = GST_AUDIO_SINK (GST_OBJECT_PARENT (buf)); csink = GST_AUDIO_SINK_GET_CLASS (sink); /* free the buffer */ gst_buffer_unref (buf->data); buf->data = NULL; if (csink->unprepare) result = csink->unprepare (sink); if (!result) goto could_not_unprepare; GST_DEBUG_OBJECT (sink, "unprepared"); return result; could_not_unprepare: { GST_DEBUG_OBJECT (sink, "could not unprepare device"); return FALSE; } }
static guint gst_audioringbuffer_delay (GstRingBuffer * buf) { GstAudioSink *sink; GstAudioSinkClass *csink; guint res = 0; sink = GST_AUDIO_SINK (GST_OBJECT_PARENT (buf)); csink = GST_AUDIO_SINK_GET_CLASS (sink); if (csink->delay) res = csink->delay (sink); return res; }
static gboolean gst_audioringbuffer_pause (GstRingBuffer * buf) { GstAudioSink *sink; GstAudioSinkClass *csink; sink = GST_AUDIO_SINK (GST_OBJECT_PARENT (buf)); csink = GST_AUDIO_SINK_GET_CLASS (sink); /* unblock any pending writes to the audio device */ if (csink->reset) { GST_DEBUG_OBJECT (sink, "reset..."); csink->reset (sink); GST_DEBUG_OBJECT (sink, "reset done"); } return TRUE; }
static gboolean gst_audio_sink_ring_buffer_acquire (GstAudioRingBuffer * buf, GstAudioRingBufferSpec * spec) { GstAudioSink *sink; GstAudioSinkClass *csink; gboolean result = FALSE; sink = GST_AUDIO_SINK (GST_OBJECT_PARENT (buf)); csink = GST_AUDIO_SINK_GET_CLASS (sink); if (csink->prepare) result = csink->prepare (sink, spec); if (!result) goto could_not_prepare; /* set latency to one more segment as we need some headroom */ spec->seglatency = spec->segtotal + 1; buf->size = spec->segtotal * spec->segsize; buf->memory = g_malloc (buf->size); if (buf->spec.type == GST_AUDIO_RING_BUFFER_FORMAT_TYPE_RAW) { gst_audio_format_fill_silence (buf->spec.info.finfo, buf->memory, buf->size); } else { /* FIXME, non-raw formats get 0 as the empty sample */ memset (buf->memory, 0, buf->size); } return TRUE; /* ERRORS */ could_not_prepare: { GST_DEBUG_OBJECT (sink, "could not prepare device"); return FALSE; } }
static gboolean gst_audioringbuffer_close_device (GstRingBuffer * buf) { GstAudioSink *sink; GstAudioSinkClass *csink; gboolean result = TRUE; sink = GST_AUDIO_SINK (GST_OBJECT_PARENT (buf)); csink = GST_AUDIO_SINK_GET_CLASS (sink); if (csink->close) result = csink->close (sink); if (!result) goto could_not_close; return result; could_not_close: { GST_DEBUG_OBJECT (sink, "could not close device"); return FALSE; } }
static gboolean gst_audio_sink_ring_buffer_open_device (GstAudioRingBuffer * buf) { GstAudioSink *sink; GstAudioSinkClass *csink; gboolean result = TRUE; sink = GST_AUDIO_SINK (GST_OBJECT_PARENT (buf)); csink = GST_AUDIO_SINK_GET_CLASS (sink); if (csink->open) result = csink->open (sink); if (!result) goto could_not_open; return result; could_not_open: { GST_DEBUG_OBJECT (sink, "could not open device"); return FALSE; } }
/* this internal thread does nothing else but write samples to the audio device. * It will write each segment in the ringbuffer and will update the play * pointer. * The start/stop methods control the thread. */ static void audioringbuffer_thread_func (GstRingBuffer * buf) { GstAudioSink *sink; GstAudioSinkClass *csink; GstAudioRingBuffer *abuf = GST_AUDIORING_BUFFER_CAST (buf); WriteFunc writefunc; GstMessage *message; GValue val = { 0 }; sink = GST_AUDIO_SINK (GST_OBJECT_PARENT (buf)); csink = GST_AUDIO_SINK_GET_CLASS (sink); GST_DEBUG_OBJECT (sink, "enter thread"); GST_OBJECT_LOCK (abuf); GST_DEBUG_OBJECT (sink, "signal wait"); GST_AUDIORING_BUFFER_SIGNAL (buf); GST_OBJECT_UNLOCK (abuf); writefunc = csink->write; if (writefunc == NULL) goto no_function; g_value_init (&val, G_TYPE_POINTER); g_value_set_pointer (&val, sink->thread); message = gst_message_new_stream_status (GST_OBJECT_CAST (buf), GST_STREAM_STATUS_TYPE_ENTER, GST_ELEMENT_CAST (sink)); gst_message_set_stream_status_object (message, &val); GST_DEBUG_OBJECT (sink, "posting ENTER stream status"); gst_element_post_message (GST_ELEMENT_CAST (sink), message); while (TRUE) { gint left, len; guint8 *readptr; gint readseg; /* buffer must be started */ if (gst_ring_buffer_prepare_read (buf, &readseg, &readptr, &len)) { gint written; left = len; do { written = writefunc (sink, readptr, left); GST_LOG_OBJECT (sink, "transfered %d bytes of %d from segment %d", written, left, readseg); if (written < 0 || written > left) { /* might not be critical, it e.g. happens when aborting playback */ GST_WARNING_OBJECT (sink, "error writing data in %s (reason: %s), skipping segment (left: %d, written: %d)", GST_DEBUG_FUNCPTR_NAME (writefunc), (errno > 1 ? g_strerror (errno) : "unknown"), left, written); break; } left -= written; readptr += written; } while (left > 0); /* clear written samples */ gst_ring_buffer_clear (buf, readseg); /* we wrote one segment */ gst_ring_buffer_advance (buf, 1); } else { GST_OBJECT_LOCK (abuf); if (!abuf->running) goto stop_running; GST_DEBUG_OBJECT (sink, "signal wait"); GST_AUDIORING_BUFFER_SIGNAL (buf); GST_DEBUG_OBJECT (sink, "wait for action"); #ifndef GSTREAMER_LITE GST_AUDIORING_BUFFER_WAIT (buf); #else // GSTREAMER_LITE // In same cases we may have condition when we waiting here for ring buffer to start, // while ring buffer is started and data is available. So, lets use wait with timeout // and recheck if we good to go. wait_segment() will start ring buffer when data is available. { GTimeVal timeout; g_get_current_time(&timeout); g_time_val_add(&timeout, 100000); // 100 millisecond GST_AUDIORING_BUFFER_TIMED_WAIT (buf, &timeout); } #endif // GSTREAMER_LITE GST_DEBUG_OBJECT (sink, "got signal"); if (!abuf->running) goto stop_running; GST_DEBUG_OBJECT (sink, "continue running"); GST_OBJECT_UNLOCK (abuf); } } /* Will never be reached */ g_assert_not_reached (); return; /* ERROR */ no_function: { GST_DEBUG_OBJECT (sink, "no write function, exit thread"); return; } stop_running: { GST_OBJECT_UNLOCK (abuf); GST_DEBUG_OBJECT (sink, "stop running, exit thread"); message = gst_message_new_stream_status (GST_OBJECT_CAST (buf), GST_STREAM_STATUS_TYPE_LEAVE, GST_ELEMENT_CAST (sink)); gst_message_set_stream_status_object (message, &val); GST_DEBUG_OBJECT (sink, "posting LEAVE stream status"); gst_element_post_message (GST_ELEMENT_CAST (sink), message); return; } }
/* this internal thread does nothing else but write samples to the audio device. * It will write each segment in the ringbuffer and will update the play * pointer. * The start/stop methods control the thread. */ static void audioringbuffer_thread_func (GstAudioRingBuffer * buf) { GstAudioSink *sink; GstAudioSinkClass *csink; GstAudioSinkRingBuffer *abuf = GST_AUDIO_SINK_RING_BUFFER_CAST (buf); WriteFunc writefunc; GstMessage *message; GValue val = { 0 }; sink = GST_AUDIO_SINK (GST_OBJECT_PARENT (buf)); csink = GST_AUDIO_SINK_GET_CLASS (sink); GST_DEBUG_OBJECT (sink, "enter thread"); GST_OBJECT_LOCK (abuf); GST_DEBUG_OBJECT (sink, "signal wait"); GST_AUDIO_SINK_RING_BUFFER_SIGNAL (buf); GST_OBJECT_UNLOCK (abuf); writefunc = csink->write; if (writefunc == NULL) goto no_function; message = gst_message_new_stream_status (GST_OBJECT_CAST (buf), GST_STREAM_STATUS_TYPE_ENTER, GST_ELEMENT_CAST (sink)); g_value_init (&val, GST_TYPE_G_THREAD); g_value_set_boxed (&val, g_thread_self ()); gst_message_set_stream_status_object (message, &val); g_value_unset (&val); GST_DEBUG_OBJECT (sink, "posting ENTER stream status"); gst_element_post_message (GST_ELEMENT_CAST (sink), message); while (TRUE) { gint left, len; guint8 *readptr; gint readseg; /* buffer must be started */ if (gst_audio_ring_buffer_prepare_read (buf, &readseg, &readptr, &len)) { gint written; left = len; do { written = writefunc (sink, readptr, left); GST_LOG_OBJECT (sink, "transfered %d bytes of %d from segment %d", written, left, readseg); if (written < 0 || written > left) { /* might not be critical, it e.g. happens when aborting playback */ GST_WARNING_OBJECT (sink, "error writing data in %s (reason: %s), skipping segment (left: %d, written: %d)", GST_DEBUG_FUNCPTR_NAME (writefunc), (errno > 1 ? g_strerror (errno) : "unknown"), left, written); break; } left -= written; readptr += written; } while (left > 0); /* clear written samples */ gst_audio_ring_buffer_clear (buf, readseg); /* we wrote one segment */ gst_audio_ring_buffer_advance (buf, 1); } else { GST_OBJECT_LOCK (abuf); if (!abuf->running) goto stop_running; if (G_UNLIKELY (g_atomic_int_get (&buf->state) == GST_AUDIO_RING_BUFFER_STATE_STARTED)) { GST_OBJECT_UNLOCK (abuf); continue; } GST_DEBUG_OBJECT (sink, "signal wait"); GST_AUDIO_SINK_RING_BUFFER_SIGNAL (buf); GST_DEBUG_OBJECT (sink, "wait for action"); GST_AUDIO_SINK_RING_BUFFER_WAIT (buf); GST_DEBUG_OBJECT (sink, "got signal"); if (!abuf->running) goto stop_running; GST_DEBUG_OBJECT (sink, "continue running"); GST_OBJECT_UNLOCK (abuf); } } /* Will never be reached */ g_assert_not_reached (); return; /* ERROR */ no_function: { GST_DEBUG_OBJECT (sink, "no write function, exit thread"); return; } stop_running: { GST_OBJECT_UNLOCK (abuf); GST_DEBUG_OBJECT (sink, "stop running, exit thread"); message = gst_message_new_stream_status (GST_OBJECT_CAST (buf), GST_STREAM_STATUS_TYPE_LEAVE, GST_ELEMENT_CAST (sink)); g_value_init (&val, GST_TYPE_G_THREAD); g_value_set_boxed (&val, g_thread_self ()); gst_message_set_stream_status_object (message, &val); g_value_unset (&val); GST_DEBUG_OBJECT (sink, "posting LEAVE stream status"); gst_element_post_message (GST_ELEMENT_CAST (sink), message); return; } }