static gboolean gst_audioringbuffer_open_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->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; } }
static gboolean gst_audio_sink_ring_buffer_close_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->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_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 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; } }
static gboolean NAS_createFlow (GstNasSink * sink, GstRingBufferSpec * spec) { AuElement elements[2]; AuUint32 buf_samples; unsigned char format; format = gst_nas_sink_sink_get_format (spec); if (format == 0) { GST_ELEMENT_ERROR (sink, RESOURCE, SETTINGS, (NULL), ("Unable to get format %d", spec->format)); return FALSE; } GST_DEBUG_OBJECT (sink, "Format: %d %d\n", spec->format, format); sink->flow = AuGetScratchFlow (sink->audio, NULL); if (sink->flow == 0) { GST_DEBUG_OBJECT (sink, "couldn't get flow"); return FALSE; } /* free old Elements and reconnet to server, needed to change samplerate */ { AuBool clocked; int num_elements; AuStatus status; AuElement *oldelems; oldelems = AuGetElements (sink->audio, sink->flow, &clocked, &num_elements, &status); if (num_elements > 0) { GST_DEBUG_OBJECT (sink, "GetElements status: %i", status); if (oldelems) AuFreeElements (sink->audio, num_elements, oldelems); gst_nas_sink_close (GST_AUDIO_SINK (sink)); gst_nas_sink_open (GST_AUDIO_SINK (sink)); sink->flow = AuGetScratchFlow (sink->audio, NULL); if (sink->flow == 0) { GST_DEBUG_OBJECT (sink, "couldn't get flow"); return FALSE; } } } /* free old Elements and reconnet to server, needed to change samplerate */ { AuBool clocked; int num_elements; AuStatus status; AuElement *oldelems; oldelems = AuGetElements (sink->audio, sink->flow, &clocked, &num_elements, &status); if (num_elements > 0) { GST_DEBUG_OBJECT (sink, "GetElements status: %i", status); if (oldelems) AuFreeElements (sink->audio, num_elements, oldelems); gst_nas_sink_close (GST_AUDIO_SINK (sink)); gst_nas_sink_open (GST_AUDIO_SINK (sink)); sink->flow = AuGetScratchFlow (sink->audio, NULL); if (sink->flow == 0) { GST_DEBUG_OBJECT (sink, "couldn't get flow"); return FALSE; } } } buf_samples = spec->rate * NAS_SOUND_PORT_DURATION; /* spec->segsize = gst_util_uint64_scale (buf_samples * spec->bytes_per_sample, spec->latency_time, GST_SECOND / GST_USECOND); spec->segsize -= spec->segsize % spec->bytes_per_sample; spec->segtotal = spec->buffer_time / spec->latency_time; */ spec->segsize = buf_samples * spec->bytes_per_sample; spec->segtotal = 1; GST_DEBUG_OBJECT (sink, "Rate %d Format %d tracks %d bufs %d %d/%d w %d", spec->rate, format, spec->channels, (gint) buf_samples, spec->segsize, spec->segtotal, spec->width); AuMakeElementImportClient (&elements[0], /* element */ spec->rate, /* rate */ format, /* format */ spec->channels, /* number of tracks */ AuTrue, /* discart */ buf_samples, /* max samples */ (AuUint32) (buf_samples / 100 * AuSoundPortLowWaterMark), /* low water mark */ 0, /* num actions */ NULL); sink->device = NAS_getDevice (sink->audio, spec->channels); if (sink->device == AuNone) { GST_DEBUG_OBJECT (sink, "no device with %i tracks found", spec->channels); return FALSE; } AuMakeElementExportDevice (&elements[1], /* element */ 0, /* input */ sink->device, /* device */ spec->rate, /* rate */ AuUnlimitedSamples, /* num samples */ 0, /* num actions */ NULL); /* actions */ AuSetElements (sink->audio, /* server */ sink->flow, /* flow ID */ AuTrue, /* clocked */ 2, /* num elements */ elements, /* elements */ NULL); AuRegisterEventHandler (sink->audio, /* server */ AuEventHandlerIDMask, /* value mask */ 0, /* type */ sink->flow, /* flow ID */ NAS_EventHandler, /* callback */ (AuPointer) sink); /* data */ AuStartFlow (sink->audio, sink->flow, NULL); return TRUE; }