/* function is called with LOCK */ static gboolean gst_audioringbuffer_release (GstRingBuffer * buf) { GstAudioSrc *src; GstAudioSrcClass *csrc; GstAudioRingBuffer *abuf; gboolean result = FALSE; src = GST_AUDIO_SRC (GST_OBJECT_PARENT (buf)); csrc = GST_AUDIO_SRC_GET_CLASS (src); abuf = GST_AUDIORING_BUFFER (buf); abuf->running = FALSE; GST_AUDIORING_BUFFER_SIGNAL (buf); GST_OBJECT_UNLOCK (buf); /* join the thread */ g_thread_join (src->thread); GST_OBJECT_LOCK (buf); /* free the buffer */ gst_buffer_unref (buf->data); buf->data = NULL; if (csrc->unprepare) result = csrc->unprepare (src); return result; }
static gboolean gst_audioringbuffer_start (GstRingBuffer * buf) { GST_DEBUG ("start, sending signal"); GST_AUDIORING_BUFFER_SIGNAL (buf); return TRUE; }
static gboolean gst_audioringbuffer_start (GstRingBuffer * buf) { GstAudioSink *sink; sink = GST_AUDIO_SINK (GST_OBJECT_PARENT (buf)); GST_DEBUG_OBJECT (sink, "start, sending signal"); GST_AUDIORING_BUFFER_SIGNAL (buf); return TRUE; }
static gboolean gst_audioringbuffer_activate (GstRingBuffer * buf, gboolean active) { GstAudioSink *sink; GstAudioRingBuffer *abuf; GError *error = NULL; sink = GST_AUDIO_SINK (GST_OBJECT_PARENT (buf)); abuf = GST_AUDIORING_BUFFER_CAST (buf); if (active) { abuf->running = TRUE; GST_DEBUG_OBJECT (sink, "starting thread"); sink->thread = g_thread_create ((GThreadFunc) audioringbuffer_thread_func, buf, TRUE, &error); if (!sink->thread || error != NULL) goto thread_failed; GST_DEBUG_OBJECT (sink, "waiting for thread"); /* the object lock is taken */ GST_AUDIORING_BUFFER_WAIT (buf); GST_DEBUG_OBJECT (sink, "thread is started"); } else { abuf->running = FALSE; GST_DEBUG_OBJECT (sink, "signal wait"); GST_AUDIORING_BUFFER_SIGNAL (buf); GST_OBJECT_UNLOCK (buf); /* join the thread */ g_thread_join (sink->thread); GST_OBJECT_LOCK (buf); } return TRUE; /* ERRORS */ thread_failed: { if (error) GST_ERROR_OBJECT (sink, "could not create thread %s", error->message); else GST_ERROR_OBJECT (sink, "could not create thread for unknown reason"); return FALSE; } }
static gboolean gst_audioringbuffer_activate (GstRingBuffer * buf, gboolean active) { GstAudioSink *sink; GstAudioRingBuffer *abuf; GError *error = NULL; sink = GST_AUDIO_SINK (GST_OBJECT_PARENT (buf)); abuf = GST_AUDIORING_BUFFER_CAST (buf); if (active) { abuf->running = TRUE; GST_DEBUG_OBJECT (sink, "starting thread"); sink->thread = g_thread_create ((GThreadFunc) audioringbuffer_thread_func, buf, TRUE, &error); if (!sink->thread || error != NULL) goto thread_failed; GST_DEBUG_OBJECT (sink, "waiting for thread"); /* the object lock is taken */ GST_AUDIORING_BUFFER_WAIT (buf); GST_DEBUG_OBJECT (sink, "thread is started"); } else { #ifndef GSTREAMER_LITE abuf->running = FALSE; GST_DEBUG_OBJECT (sink, "signal wait"); GST_AUDIORING_BUFFER_SIGNAL (buf); GST_OBJECT_UNLOCK (buf); /* join the thread */ g_thread_join (sink->thread); GST_OBJECT_LOCK (buf); #else // GSTREAMER_LITE // We may get called with active set to FALSE several times. // See gst_base_audio_sink_change_state() if (abuf->running) { abuf->running = FALSE; GST_DEBUG_OBJECT (sink, "signal wait"); GST_AUDIORING_BUFFER_SIGNAL (buf); GST_OBJECT_UNLOCK (buf); /* join the thread */ if (sink->thread != NULL) { g_thread_join (sink->thread); sink->thread = NULL; } GST_OBJECT_LOCK (buf); } #endif // GSTREAMER_LITE } return TRUE; /* ERRORS */ thread_failed: { if (error) GST_ERROR_OBJECT (sink, "could not create thread %s", error->message); else GST_ERROR_OBJECT (sink, "could not create thread for unknown reason"); 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 read samples from the audio device. * It will read 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) { GstAudioSrc *src; GstAudioSrcClass *csrc; GstAudioRingBuffer *abuf = GST_AUDIORING_BUFFER (buf); ReadFunc readfunc; GstMessage *message; GValue val = { 0 }; src = GST_AUDIO_SRC (GST_OBJECT_PARENT (buf)); csrc = GST_AUDIO_SRC_GET_CLASS (src); GST_DEBUG_OBJECT (src, "enter thread"); readfunc = csrc->read; if (readfunc == NULL) goto no_function; /* FIXME: maybe we should at least use a custom pointer type here? */ g_value_init (&val, G_TYPE_POINTER); g_value_set_pointer (&val, src->thread); message = gst_message_new_stream_status (GST_OBJECT_CAST (buf), GST_STREAM_STATUS_TYPE_ENTER, GST_ELEMENT_CAST (src)); gst_message_set_stream_status_object (message, &val); GST_DEBUG_OBJECT (src, "posting ENTER stream status"); gst_element_post_message (GST_ELEMENT_CAST (src), message); while (TRUE) { gint left, len; guint8 *readptr; gint readseg; if (gst_ring_buffer_prepare_read (buf, &readseg, &readptr, &len)) { gint read; left = len; do { read = readfunc (src, readptr, left); GST_LOG_OBJECT (src, "transfered %d bytes of %d to segment %d", read, left, readseg); if (read < 0 || read > left) { GST_WARNING_OBJECT (src, "error reading data %d (reason: %s), skipping segment", read, g_strerror (errno)); break; } left -= read; readptr += read; } while (left > 0); /* we read one segment */ gst_ring_buffer_advance (buf, 1); } else { GST_OBJECT_LOCK (abuf); if (!abuf->running) goto stop_running; GST_DEBUG_OBJECT (src, "signal wait"); GST_AUDIORING_BUFFER_SIGNAL (buf); GST_DEBUG_OBJECT (src, "wait for action"); GST_AUDIORING_BUFFER_WAIT (buf); GST_DEBUG_OBJECT (src, "got signal"); if (!abuf->running) goto stop_running; GST_DEBUG_OBJECT (src, "continue running"); GST_OBJECT_UNLOCK (abuf); } } /* Will never be reached */ g_assert_not_reached (); return; /* ERROR */ no_function: { GST_DEBUG ("no write function, exit thread"); return; } stop_running: { GST_OBJECT_UNLOCK (abuf); GST_DEBUG ("stop running, exit thread"); message = gst_message_new_stream_status (GST_OBJECT_CAST (buf), GST_STREAM_STATUS_TYPE_LEAVE, GST_ELEMENT_CAST (src)); gst_message_set_stream_status_object (message, &val); GST_DEBUG_OBJECT (src, "posting LEAVE stream status"); gst_element_post_message (GST_ELEMENT_CAST (src), message); return; } }