summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSebastian Dröge <sebastian@centricular.com>2017-11-08 19:31:37 +0200
committerSebastian Dröge <sebastian@centricular.com>2017-12-14 10:37:20 +0200
commit86888d9918c29e2243f2b81b77acc9f646de1488 (patch)
tree2e35f977fea532628181a979af4dc8365c340298
parentef0497c1490fe5d17b099a92c181df0f0bb77dfe (diff)
decklinkaudiosink: Re-implement around GstBaseSink instead of GstAudioBaseSink
The Decklink and GstAudioBaseSink APIs don't fit very well together, which causes various problems due to inaccuracies in the clock calculations and the actual ringbuffer and GStreamer's copy getting of sync. Problems are audio drop-outs and A/V sync getting wrong after pausing/seeking. https://bugzilla.gnome.org/show_bug.cgi?id=790114
-rw-r--r--sys/decklink/gstdecklink.cpp27
-rw-r--r--sys/decklink/gstdecklink.h6
-rw-r--r--sys/decklink/gstdecklinkaudiosink.cpp1157
-rw-r--r--sys/decklink/gstdecklinkaudiosink.h10
-rw-r--r--sys/decklink/gstdecklinkvideosink.cpp22
-rw-r--r--sys/decklink/gstdecklinkvideosink.h3
6 files changed, 592 insertions, 633 deletions
diff --git a/sys/decklink/gstdecklink.cpp b/sys/decklink/gstdecklink.cpp
index d7d9e503e..0527913c7 100644
--- a/sys/decklink/gstdecklink.cpp
+++ b/sys/decklink/gstdecklink.cpp
@@ -1339,33 +1339,6 @@ gst_decklink_release_nth_output (gint n, GstElement * sink, gboolean is_audio)
g_mutex_unlock (&output->lock);
}
-void
-gst_decklink_output_set_audio_clock (GstDecklinkOutput * output,
- GstClock * clock)
-{
- g_mutex_lock (&output->lock);
- if (output->audio_clock)
- gst_object_unref (output->audio_clock);
- output->audio_clock = clock;
- if (clock)
- gst_object_ref (clock);
- g_mutex_unlock (&output->lock);
-}
-
-
-GstClock *
-gst_decklink_output_get_audio_clock (GstDecklinkOutput * output)
-{
- GstClock *ret = NULL;
-
- g_mutex_lock (&output->lock);
- if (output->audio_clock)
- ret = GST_CLOCK_CAST (gst_object_ref (output->audio_clock));
- g_mutex_unlock (&output->lock);
-
- return ret;
-}
-
GstDecklinkInput *
gst_decklink_acquire_nth_input (gint n, GstElement * src, gboolean is_audio)
{
diff --git a/sys/decklink/gstdecklink.h b/sys/decklink/gstdecklink.h
index af98a0324..0b65bb25a 100644
--- a/sys/decklink/gstdecklink.h
+++ b/sys/decklink/gstdecklink.h
@@ -226,9 +226,6 @@ struct _GstDecklinkOutput {
/* Configured mode or NULL */
const GstDecklinkMode *mode;
- /* Set by the audio sink */
- GstClock *audio_clock;
-
GstElement *audiosink;
gboolean audio_enabled;
GstElement *videosink;
@@ -267,9 +264,6 @@ struct _GstDecklinkInput {
GstDecklinkOutput * gst_decklink_acquire_nth_output (gint n, GstElement * sink, gboolean is_audio);
void gst_decklink_release_nth_output (gint n, GstElement * sink, gboolean is_audio);
-void gst_decklink_output_set_audio_clock (GstDecklinkOutput * output, GstClock * clock);
-GstClock * gst_decklink_output_get_audio_clock (GstDecklinkOutput * output);
-
GstDecklinkInput * gst_decklink_acquire_nth_input (gint n, GstElement * src, gboolean is_audio);
void gst_decklink_release_nth_input (gint n, GstElement * src, gboolean is_audio);
diff --git a/sys/decklink/gstdecklinkaudiosink.cpp b/sys/decklink/gstdecklinkaudiosink.cpp
index 84cd2eaa4..ce8ab9420 100644
--- a/sys/decklink/gstdecklinkaudiosink.cpp
+++ b/sys/decklink/gstdecklinkaudiosink.cpp
@@ -23,489 +23,629 @@
#endif
#include "gstdecklinkaudiosink.h"
+#include "gstdecklinkvideosink.h"
+#include <string.h>
GST_DEBUG_CATEGORY_STATIC (gst_decklink_audio_sink_debug);
#define GST_CAT_DEFAULT gst_decklink_audio_sink_debug
-// Ringbuffer implementation
-
-#define GST_TYPE_DECKLINK_AUDIO_SINK_RING_BUFFER \
- (gst_decklink_audio_sink_ringbuffer_get_type())
-#define GST_DECKLINK_AUDIO_SINK_RING_BUFFER(obj) \
- (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_DECKLINK_AUDIO_SINK_RING_BUFFER,GstDecklinkAudioSinkRingBuffer))
-#define GST_DECKLINK_AUDIO_SINK_RING_BUFFER_CAST(obj) \
- ((GstDecklinkAudioSinkRingBuffer*) obj)
-#define GST_DECKLINK_AUDIO_SINK_RING_BUFFER_CLASS(klass) \
- (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_DECKLINK_AUDIO_SINK_RING_BUFFER,GstDecklinkAudioSinkRingBufferClass))
-#define GST_DECKLINK_AUDIO_SINK_RING_BUFFER_CAST_GET_CLASS(obj) \
- (G_TYPE_INSTANCE_GET_CLASS((obj),GST_TYPE_DECKLINK_AUDIO_SINK_RING_BUFFER,GstDecklinkAudioSinkRingBufferClass))
-#define GST_IS_DECKLINK_AUDIO_SINK_RING_BUFFER(obj) \
- (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_DECKLINK_AUDIO_SINK_RING_BUFFER))
-#define GST_IS_DECKLINK_AUDIO_SINK_RING_BUFFER_CLASS(klass) \
- (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_DECKLINK_AUDIO_SINK_RING_BUFFER))
-
-typedef struct _GstDecklinkAudioSinkRingBuffer GstDecklinkAudioSinkRingBuffer;
-typedef struct _GstDecklinkAudioSinkRingBufferClass
- GstDecklinkAudioSinkRingBufferClass;
-
-struct _GstDecklinkAudioSinkRingBuffer
+#define DEFAULT_DEVICE_NUMBER (0)
+#define DEFAULT_ALIGNMENT_THRESHOLD (40 * GST_MSECOND)
+#define DEFAULT_DISCONT_WAIT (1 * GST_SECOND)
+// Microseconds for audiobasesink compatibility...
+#define DEFAULT_BUFFER_TIME (50 * GST_MSECOND / 1000)
+
+enum
{
- GstAudioRingBuffer object;
+ PROP_0,
+ PROP_DEVICE_NUMBER,
+ PROP_HW_SERIAL_NUMBER,
+ PROP_ALIGNMENT_THRESHOLD,
+ PROP_DISCONT_WAIT,
+ PROP_BUFFER_TIME,
+};
- GstDecklinkOutput *output;
- GstDecklinkAudioSink *sink;
+static void gst_decklink_audio_sink_set_property (GObject * object,
+ guint property_id, const GValue * value, GParamSpec * pspec);
+static void gst_decklink_audio_sink_get_property (GObject * object,
+ guint property_id, GValue * value, GParamSpec * pspec);
+static void gst_decklink_audio_sink_finalize (GObject * object);
- GMutex clock_id_lock;
- GstClockID clock_id;
-};
+static GstStateChangeReturn
+gst_decklink_audio_sink_change_state (GstElement * element,
+ GstStateChange transition);
+static GstClock *gst_decklink_audio_sink_provide_clock (GstElement * element);
-struct _GstDecklinkAudioSinkRingBufferClass
-{
- GstAudioRingBufferClass parent_class;
-};
+static GstCaps *gst_decklink_audio_sink_get_caps (GstBaseSink * bsink,
+ GstCaps * filter);
+static gboolean gst_decklink_audio_sink_set_caps (GstBaseSink * bsink,
+ GstCaps * caps);
+static GstFlowReturn gst_decklink_audio_sink_render (GstBaseSink * bsink,
+ GstBuffer * buffer);
+static gboolean gst_decklink_audio_sink_open (GstBaseSink * bsink);
+static gboolean gst_decklink_audio_sink_close (GstBaseSink * bsink);
+static gboolean gst_decklink_audio_sink_stop (GstDecklinkAudioSink * self);
+static gboolean gst_decklink_audio_sink_unlock_stop (GstBaseSink * bsink);
+static void gst_decklink_audio_sink_get_times (GstBaseSink * bsink,
+ GstBuffer * buffer, GstClockTime * start, GstClockTime * end);
+static gboolean gst_decklink_audio_sink_query (GstBaseSink * bsink,
+ GstQuery * query);
-GType gst_decklink_audio_sink_ringbuffer_get_type (void);
-
-static void gst_decklink_audio_sink_ringbuffer_finalize (GObject * object);
-
-static void gst_decklink_audio_sink_ringbuffer_clear_all (GstAudioRingBuffer *
- rb);
-static guint gst_decklink_audio_sink_ringbuffer_delay (GstAudioRingBuffer * rb);
-static gboolean gst_decklink_audio_sink_ringbuffer_start (GstAudioRingBuffer *
- rb);
-static gboolean gst_decklink_audio_sink_ringbuffer_pause (GstAudioRingBuffer *
- rb);
-static gboolean gst_decklink_audio_sink_ringbuffer_stop (GstAudioRingBuffer *
- rb);
-static gboolean gst_decklink_audio_sink_ringbuffer_acquire (GstAudioRingBuffer *
- rb, GstAudioRingBufferSpec * spec);
-static gboolean gst_decklink_audio_sink_ringbuffer_release (GstAudioRingBuffer *
- rb);
-static gboolean
-gst_decklink_audio_sink_ringbuffer_open_device (GstAudioRingBuffer * rb);
-static gboolean
-gst_decklink_audio_sink_ringbuffer_close_device (GstAudioRingBuffer * rb);
+static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink",
+ GST_PAD_SINK,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS
+ ("audio/x-raw, format={S16LE,S32LE}, channels={2, 8, 16}, rate=48000, "
+ "layout=interleaved")
+ );
-#define ringbuffer_parent_class gst_decklink_audio_sink_ringbuffer_parent_class
-G_DEFINE_TYPE (GstDecklinkAudioSinkRingBuffer,
- gst_decklink_audio_sink_ringbuffer, GST_TYPE_AUDIO_RING_BUFFER);
+#define parent_class gst_decklink_audio_sink_parent_class
+G_DEFINE_TYPE (GstDecklinkAudioSink, gst_decklink_audio_sink,
+ GST_TYPE_BASE_SINK);
static void
- gst_decklink_audio_sink_ringbuffer_class_init
- (GstDecklinkAudioSinkRingBufferClass * klass)
+gst_decklink_audio_sink_class_init (GstDecklinkAudioSinkClass * klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
- GstAudioRingBufferClass *gstringbuffer_class =
- GST_AUDIO_RING_BUFFER_CLASS (klass);
-
- gobject_class->finalize = gst_decklink_audio_sink_ringbuffer_finalize;
-
- gstringbuffer_class->open_device =
- GST_DEBUG_FUNCPTR (gst_decklink_audio_sink_ringbuffer_open_device);
- gstringbuffer_class->close_device =
- GST_DEBUG_FUNCPTR (gst_decklink_audio_sink_ringbuffer_close_device);
- gstringbuffer_class->acquire =
- GST_DEBUG_FUNCPTR (gst_decklink_audio_sink_ringbuffer_acquire);
- gstringbuffer_class->release =
- GST_DEBUG_FUNCPTR (gst_decklink_audio_sink_ringbuffer_release);
- gstringbuffer_class->start =
- GST_DEBUG_FUNCPTR (gst_decklink_audio_sink_ringbuffer_start);
- gstringbuffer_class->pause =
- GST_DEBUG_FUNCPTR (gst_decklink_audio_sink_ringbuffer_pause);
- gstringbuffer_class->resume =
- GST_DEBUG_FUNCPTR (gst_decklink_audio_sink_ringbuffer_start);
- gstringbuffer_class->stop =
- GST_DEBUG_FUNCPTR (gst_decklink_audio_sink_ringbuffer_stop);
- gstringbuffer_class->delay =
- GST_DEBUG_FUNCPTR (gst_decklink_audio_sink_ringbuffer_delay);
- gstringbuffer_class->clear_all =
- GST_DEBUG_FUNCPTR (gst_decklink_audio_sink_ringbuffer_clear_all);
-}
+ GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
+ GstBaseSinkClass *basesink_class = GST_BASE_SINK_CLASS (klass);
-static void
-gst_decklink_audio_sink_ringbuffer_init (GstDecklinkAudioSinkRingBuffer * self)
-{
- g_mutex_init (&self->clock_id_lock);
-}
+ gobject_class->set_property = gst_decklink_audio_sink_set_property;
+ gobject_class->get_property = gst_decklink_audio_sink_get_property;
+ gobject_class->finalize = gst_decklink_audio_sink_finalize;
-static void
-gst_decklink_audio_sink_ringbuffer_finalize (GObject * object)
-{
- GstDecklinkAudioSinkRingBuffer *self =
- GST_DECKLINK_AUDIO_SINK_RING_BUFFER_CAST (object);
+ element_class->change_state =
+ GST_DEBUG_FUNCPTR (gst_decklink_audio_sink_change_state);
+ element_class->provide_clock =
+ GST_DEBUG_FUNCPTR (gst_decklink_audio_sink_provide_clock);
- gst_object_unref (self->sink);
- self->sink = NULL;
- g_mutex_clear (&self->clock_id_lock);
+ basesink_class->get_caps =
+ GST_DEBUG_FUNCPTR (gst_decklink_audio_sink_get_caps);
+ basesink_class->set_caps =
+ GST_DEBUG_FUNCPTR (gst_decklink_audio_sink_set_caps);
+ basesink_class->render = GST_DEBUG_FUNCPTR (gst_decklink_audio_sink_render);
+ // FIXME: These are misnamed in basesink!
+ basesink_class->start = GST_DEBUG_FUNCPTR (gst_decklink_audio_sink_open);
+ basesink_class->stop = GST_DEBUG_FUNCPTR (gst_decklink_audio_sink_close);
+ basesink_class->unlock_stop =
+ GST_DEBUG_FUNCPTR (gst_decklink_audio_sink_unlock_stop);
+ basesink_class->get_times =
+ GST_DEBUG_FUNCPTR (gst_decklink_audio_sink_get_times);
+ basesink_class->query = GST_DEBUG_FUNCPTR (gst_decklink_audio_sink_query);
- G_OBJECT_CLASS (ringbuffer_parent_class)->finalize (object);
-}
+ g_object_class_install_property (gobject_class, PROP_DEVICE_NUMBER,
+ g_param_spec_int ("device-number", "Device number",
+ "Output device instance to use", 0, G_MAXINT, DEFAULT_DEVICE_NUMBER,
+ (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS |
+ G_PARAM_CONSTRUCT)));
-class GStreamerAudioOutputCallback:public IDeckLinkAudioOutputCallback
-{
-public:
- GStreamerAudioOutputCallback (GstDecklinkAudioSinkRingBuffer * ringbuffer)
- :IDeckLinkAudioOutputCallback (), m_refcount (1)
- {
- m_ringbuffer =
- GST_DECKLINK_AUDIO_SINK_RING_BUFFER_CAST (gst_object_ref (ringbuffer));
- g_mutex_init (&m_mutex);
- }
+ g_object_class_install_property (gobject_class, PROP_HW_SERIAL_NUMBER,
+ g_param_spec_string ("hw-serial-number", "Hardware serial number",
+ "The serial number (hardware ID) of the Decklink card",
+ NULL, (GParamFlags) (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)));
- virtual HRESULT WINAPI QueryInterface (REFIID, LPVOID *)
- {
- return E_NOINTERFACE;
- }
+ g_object_class_install_property (gobject_class, PROP_ALIGNMENT_THRESHOLD,
+ g_param_spec_uint64 ("alignment-threshold", "Alignment Threshold",
+ "Timestamp alignment threshold in nanoseconds", 0,
+ G_MAXUINT64 - 1, DEFAULT_ALIGNMENT_THRESHOLD,
+ (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS |
+ GST_PARAM_MUTABLE_READY)));
- virtual ULONG WINAPI AddRef (void)
- {
- ULONG ret;
+ g_object_class_install_property (gobject_class, PROP_DISCONT_WAIT,
+ g_param_spec_uint64 ("discont-wait", "Discont Wait",
+ "Window of time in nanoseconds to wait before "
+ "creating a discontinuity", 0,
+ G_MAXUINT64 - 1, DEFAULT_DISCONT_WAIT,
+ (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS |
+ GST_PARAM_MUTABLE_READY)));
- g_mutex_lock (&m_mutex);
- m_refcount++;
- ret = m_refcount;
- g_mutex_unlock (&m_mutex);
+ g_object_class_install_property (gobject_class, PROP_BUFFER_TIME,
+ g_param_spec_uint64 ("buffer-time", "Buffer Time",
+ "Size of audio buffer in microseconds, this is the minimum latency that the sink reports",
+ 0, G_MAXUINT64, DEFAULT_BUFFER_TIME,
+ (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS |
+ GST_PARAM_MUTABLE_READY)));
- return ret;
- }
+ gst_element_class_add_static_pad_template (element_class, &sink_template);
- virtual ULONG WINAPI Release (void)
- {
- ULONG ret;
+ gst_element_class_set_static_metadata (element_class, "Decklink Audio Sink",
+ "Audio/Sink", "Decklink Sink", "David Schleef <ds@entropywave.com>, "
+ "Sebastian Dröge <sebastian@centricular.com>");
- g_mutex_lock (&m_mutex);
- m_refcount--;
- ret = m_refcount;
- g_mutex_unlock (&m_mutex);
+ GST_DEBUG_CATEGORY_INIT (gst_decklink_audio_sink_debug, "decklinkaudiosink",
+ 0, "debug category for decklinkaudiosink element");
+}
- if (ret == 0) {
- delete this;
- }
+static void
+gst_decklink_audio_sink_init (GstDecklinkAudioSink * self)
+{
+ self->device_number = DEFAULT_DEVICE_NUMBER;
+ self->stream_align =
+ gst_audio_stream_align_new (48000, DEFAULT_ALIGNMENT_THRESHOLD,
+ DEFAULT_DISCONT_WAIT);
+ self->buffer_time = DEFAULT_BUFFER_TIME * 1000;
- return ret;
- }
+ gst_base_sink_set_max_lateness (GST_BASE_SINK_CAST (self), 20 * GST_MSECOND);
+}
- virtual ~ GStreamerAudioOutputCallback () {
- gst_object_unref (m_ringbuffer);
- g_mutex_clear (&m_mutex);
- }
+void
+gst_decklink_audio_sink_set_property (GObject * object, guint property_id,
+ const GValue * value, GParamSpec * pspec)
+{
+ GstDecklinkAudioSink *self = GST_DECKLINK_AUDIO_SINK_CAST (object);
- virtual HRESULT WINAPI RenderAudioSamples (bool preroll)
- {
- guint8 *ptr;
- gint seg;
- gint len;
- gint bpf;
- guint written, written_sum;
- HRESULT res;
- const GstAudioRingBufferSpec *spec =
- &GST_AUDIO_RING_BUFFER_CAST (m_ringbuffer)->spec;
- guint delay, max_delay;
-
- GST_LOG_OBJECT (m_ringbuffer->sink, "Writing audio samples (preroll: %d)",
- preroll);
-
- delay =
- gst_audio_ring_buffer_delay (GST_AUDIO_RING_BUFFER_CAST (m_ringbuffer));
- max_delay = MAX ((spec->segtotal * spec->segsize) / 2, spec->segsize);
- max_delay /= GST_AUDIO_INFO_BPF (&spec->info);
- if (delay > max_delay) {
- GstClock *clock =
- gst_element_get_clock (GST_ELEMENT_CAST (m_ringbuffer->sink));
- GstClockTime wait_time;
- GstClockID clock_id;
- GstClockReturn clock_ret;
+ switch (property_id) {
+ case PROP_DEVICE_NUMBER:
+ self->device_number = g_value_get_int (value);
+ break;
+ case PROP_ALIGNMENT_THRESHOLD:
+ GST_OBJECT_LOCK (self);
+ gst_audio_stream_align_set_alignment_threshold (self->stream_align,
+ g_value_get_uint64 (value));
+ GST_OBJECT_UNLOCK (self);
+ break;
+ case PROP_DISCONT_WAIT:
+ GST_OBJECT_LOCK (self);
+ gst_audio_stream_align_set_discont_wait (self->stream_align,
+ g_value_get_uint64 (value));
+ GST_OBJECT_UNLOCK (self);
+ break;
+ case PROP_BUFFER_TIME:
+ GST_OBJECT_LOCK (self);
+ self->buffer_time = g_value_get_uint64 (value) * 1000;
+ GST_OBJECT_UNLOCK (self);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
- GST_DEBUG_OBJECT (m_ringbuffer->sink, "Delay %u > max delay %u", delay,
- max_delay);
-
- wait_time =
- gst_util_uint64_scale (delay - max_delay, GST_SECOND,
- GST_AUDIO_INFO_RATE (&spec->info));
- GST_DEBUG_OBJECT (m_ringbuffer->sink, "Waiting for %" GST_TIME_FORMAT,
- GST_TIME_ARGS (wait_time));
- wait_time += gst_clock_get_time (clock);
-
- g_mutex_lock (&m_ringbuffer->clock_id_lock);
- if (!GST_AUDIO_RING_BUFFER_CAST (m_ringbuffer)->acquired) {
- GST_DEBUG_OBJECT (m_ringbuffer->sink,
- "Ringbuffer not acquired anymore");
- g_mutex_unlock (&m_ringbuffer->clock_id_lock);
- gst_object_unref (clock);
- return S_OK;
- }
- clock_id = gst_clock_new_single_shot_id (clock, wait_time);
- m_ringbuffer->clock_id = clock_id;
- g_mutex_unlock (&m_ringbuffer->clock_id_lock);
- gst_object_unref (clock);
+void
+gst_decklink_audio_sink_get_property (GObject * object, guint property_id,
+ GValue * value, GParamSpec * pspec)
+{
+ GstDecklinkAudioSink *self = GST_DECKLINK_AUDIO_SINK_CAST (object);
- clock_ret = gst_clock_id_wait (clock_id, NULL);
+ switch (property_id) {
+ case PROP_DEVICE_NUMBER:
+ g_value_set_int (value, self->device_number);
+ break;
+ case PROP_HW_SERIAL_NUMBER:
+ if (self->output)
+ g_value_set_string (value, self->output->hw_serial_number);
+ else
+ g_value_set_string (value, NULL);
+ break;
+ case PROP_ALIGNMENT_THRESHOLD:
+ GST_OBJECT_LOCK (self);
+ g_value_set_uint64 (value,
+ gst_audio_stream_align_get_alignment_threshold (self->stream_align));
+ GST_OBJECT_UNLOCK (self);
+ break;
+ case PROP_DISCONT_WAIT:
+ GST_OBJECT_LOCK (self);
+ g_value_set_uint64 (value,
+ gst_audio_stream_align_get_discont_wait (self->stream_align));
+ GST_OBJECT_UNLOCK (self);
+ break;
+ case PROP_BUFFER_TIME:
+ GST_OBJECT_LOCK (self);
+ g_value_set_uint64 (value, self->buffer_time / 1000);
+ GST_OBJECT_UNLOCK (self);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
- g_mutex_lock (&m_ringbuffer->clock_id_lock);
- gst_clock_id_unref (clock_id);
- m_ringbuffer->clock_id = NULL;
- g_mutex_unlock (&m_ringbuffer->clock_id_lock);
+void
+gst_decklink_audio_sink_finalize (GObject * object)
+{
+ GstDecklinkAudioSink *self = GST_DECKLINK_AUDIO_SINK_CAST (object);
- if (clock_ret == GST_CLOCK_UNSCHEDULED) {
- GST_DEBUG_OBJECT (m_ringbuffer->sink, "Flushing");
- return S_OK;
- }
- }
+ if (self->stream_align) {
+ gst_audio_stream_align_free (self->stream_align);
+ self->stream_align = NULL;
+ }
- if (!gst_audio_ring_buffer_prepare_read (GST_AUDIO_RING_BUFFER_CAST
- (m_ringbuffer), &seg, &ptr, &len)) {
- GST_WARNING_OBJECT (m_ringbuffer->sink, "No segment available");
- return E_FAIL;
- }
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
- bpf =
- GST_AUDIO_INFO_BPF (&GST_AUDIO_RING_BUFFER_CAST (m_ringbuffer)->
- spec.info);
- len /= bpf;
- GST_LOG_OBJECT (m_ringbuffer->sink,
- "Write audio samples: %p size %d segment: %d", ptr, len, seg);
-
- written_sum = 0;
- do {
- res =
- m_ringbuffer->output->output->ScheduleAudioSamples (ptr, len,
- 0, 0, &written);
- len -= written;
- ptr += written * bpf;
- written_sum += written;
- } while (len > 0 && res == S_OK);
+static gboolean
+gst_decklink_audio_sink_set_caps (GstBaseSink * bsink, GstCaps * caps)
+{
+ GstDecklinkAudioSink *self = GST_DECKLINK_AUDIO_SINK_CAST (bsink);
+ HRESULT ret;
+ BMDAudioSampleType sample_depth;
+ GstAudioInfo info;
- GST_LOG_OBJECT (m_ringbuffer->sink, "Wrote %u samples: 0x%08lx",
- written_sum, (unsigned long) res);
+ GST_DEBUG_OBJECT (self, "Setting caps %" GST_PTR_FORMAT, caps);
- gst_audio_ring_buffer_clear (GST_AUDIO_RING_BUFFER_CAST (m_ringbuffer),
- seg);
- gst_audio_ring_buffer_advance (GST_AUDIO_RING_BUFFER_CAST (m_ringbuffer),
- 1);
+ if (!gst_audio_info_from_caps (&info, caps))
+ return FALSE;
- return res;
+ if (self->output->audio_enabled
+ && (self->info.finfo->format != info.finfo->format
+ || self->info.channels != info.channels)) {
+ GST_ERROR_OBJECT (self, "Reconfiguration not supported");
+ return FALSE;
+ } else if (self->output->audio_enabled) {
+ return TRUE;
}
-private:
- GstDecklinkAudioSinkRingBuffer * m_ringbuffer;
- GMutex m_mutex;
- gint m_refcount;
-};
+ if (info.finfo->format == GST_AUDIO_FORMAT_S16LE) {
+ sample_depth = bmdAudioSampleType16bitInteger;
+ } else {
+ sample_depth = bmdAudioSampleType32bitInteger;
+ }
-static void
-gst_decklink_audio_sink_ringbuffer_clear_all (GstAudioRingBuffer * rb)
-{
- GstDecklinkAudioSinkRingBuffer *self =
- GST_DECKLINK_AUDIO_SINK_RING_BUFFER_CAST (rb);
+ ret = self->output->output->EnableAudioOutput (bmdAudioSampleRate48kHz,
+ sample_depth, info.channels, bmdAudioOutputStreamContinuous);
+ if (ret != S_OK) {
+ GST_WARNING_OBJECT (self, "Failed to enable audio output 0x%08lx",
+ (unsigned long) ret);
+ return FALSE;
+ }
- GST_DEBUG_OBJECT (self->sink, "Flushing");
+ self->output->audio_enabled = TRUE;
+ self->info = info;
- if (self->output)
- self->output->output->FlushBufferedAudioSamples ();
+ return TRUE;
}
-static guint
-gst_decklink_audio_sink_ringbuffer_delay (GstAudioRingBuffer * rb)
+static GstCaps *
+gst_decklink_audio_sink_get_caps (GstBaseSink * bsink, GstCaps * filter)
{
- GstDecklinkAudioSinkRingBuffer *self =
- GST_DECKLINK_AUDIO_SINK_RING_BUFFER_CAST (rb);
- guint ret = 0;
- HRESULT res = S_OK;
+ GstDecklinkAudioSink *self = GST_DECKLINK_AUDIO_SINK_CAST (bsink);
+ GstCaps *caps;
+
+ if ((caps = gst_pad_get_current_caps (GST_BASE_SINK_PAD (bsink))))
+ return caps;
+
+ caps = gst_pad_get_pad_template_caps (GST_BASE_SINK_PAD (bsink));
+
+ GST_OBJECT_LOCK (self);
+ if (self->output && self->output->attributes) {
+ int64_t max_channels = 0;
+ HRESULT ret;
+ GstStructure *s;
+ GValue arr = G_VALUE_INIT;
+ GValue v = G_VALUE_INIT;
+
+ ret =
+ self->output->attributes->GetInt (BMDDeckLinkMaximumAudioChannels,
+ &max_channels);
+ /* 2 should always be supported */
+ if (ret != S_OK) {
+ max_channels = 2;
+ }
- if (self->output) {
- if ((res =
- self->output->output->GetBufferedAudioSampleFrameCount (&ret)) !=
- S_OK)
- ret = 0;
+ caps = gst_caps_make_writable (caps);
+ s = gst_caps_get_structure (caps, 0);
+
+ g_value_init (&arr, GST_TYPE_LIST);
+ g_value_init (&v, G_TYPE_INT);
+ if (max_channels >= 16) {
+ g_value_set_int (&v, 16);
+ gst_value_list_append_value (&arr, &v);
+ }
+ if (max_channels >= 8) {
+ g_value_set_int (&v, 8);
+ gst_value_list_append_value (&arr, &v);
+ }
+ g_value_set_int (&v, 2);
+ gst_value_list_append_value (&arr, &v);
+
+ gst_structure_set_value (s, "channels", &arr);
+ g_value_unset (&v);
+ g_value_unset (&arr);
}
+ GST_OBJECT_UNLOCK (self);
- GST_DEBUG_OBJECT (self->sink, "Delay: %u (0x%08lx)", ret,
- (unsigned long) res);
+ if (filter) {
+ GstCaps *intersection =
+ gst_caps_intersect_full (filter, caps, GST_CAPS_INTERSECT_FIRST);
+ gst_caps_unref (caps);
+ caps = intersection;
+ }
- return ret;
+ return caps;
}
-#if 0
static gboolean
-in_same_pipeline (GstElement * a, GstElement * b)
+gst_decklink_audio_sink_query (GstBaseSink * bsink, GstQuery * query)
{
- GstObject *root = NULL, *tmp;
- gboolean ret = FALSE;
-
- tmp = gst_object_get_parent (GST_OBJECT_CAST (a));
- while (tmp != NULL) {
- if (root)
- gst_object_unref (root);
- root = tmp;
- tmp = gst_object_get_parent (root);
+ GstDecklinkAudioSink *self = GST_DECKLINK_AUDIO_SINK (bsink);
+ gboolean res = FALSE;
+
+ switch (GST_QUERY_TYPE (query)) {
+ case GST_QUERY_LATENCY:
+ {
+ gboolean live, us_live;
+ GstClockTime min_l, max_l;
+
+ GST_DEBUG_OBJECT (self, "latency query");
+
+ /* ask parent first, it will do an upstream query for us. */
+ if ((res =
+ gst_base_sink_query_latency (GST_BASE_SINK_CAST (self), &live,
+ &us_live, &min_l, &max_l))) {
+ GstClockTime base_latency, min_latency, max_latency;
+
+ /* we and upstream are both live, adjust the min_latency */
+ if (live && us_live) {
+ GST_OBJECT_LOCK (self);
+ if (!self->info.rate) {
+ GST_OBJECT_UNLOCK (self);
+
+ GST_DEBUG_OBJECT (self,
+ "we are not negotiated, can't report latency yet");
+ res = FALSE;
+ goto done;
+ }
+
+ base_latency = self->buffer_time * 1000;
+ GST_OBJECT_UNLOCK (self);
+
+ /* we cannot go lower than the buffer size and the min peer latency */
+ min_latency = base_latency + min_l;
+ /* the max latency is the max of the peer, we can delay an infinite
+ * amount of time. */
+ max_latency =
+ (max_l ==
+ GST_CLOCK_TIME_NONE) ? GST_CLOCK_TIME_NONE : (base_latency +
+ max_l);
+
+ GST_DEBUG_OBJECT (self,
+ "peer min %" GST_TIME_FORMAT ", our min latency: %"
+ GST_TIME_FORMAT, GST_TIME_ARGS (min_l),
+ GST_TIME_ARGS (min_latency));
+ GST_DEBUG_OBJECT (self,
+ "peer max %" GST_TIME_FORMAT ", our max latency: %"
+ GST_TIME_FORMAT, GST_TIME_ARGS (max_l),
+ GST_TIME_ARGS (max_latency));
+ } else {
+ GST_DEBUG_OBJECT (self,
+ "peer or we are not live, don't care about latency");
+ min_latency = min_l;
+ max_latency = max_l;
+ }
+ gst_query_set_latency (query, live, min_latency, max_latency);
+ }
+ break;
+ }
+ default:
+ res = GST_BASE_SINK_CLASS (parent_class)->query (bsink, query);
+ break;
}
- ret = root && gst_object_has_ancestor (GST_OBJECT_CAST (b), root);
-
- if (root)
- gst_object_unref (root);
-
- return ret;
+done:
+ return res;
}
-#endif
-static gboolean
-gst_decklink_audio_sink_ringbuffer_start (GstAudioRingBuffer * rb)
+static GstFlowReturn
+gst_decklink_audio_sink_render (GstBaseSink * bsink, GstBuffer * buffer)
{
- GstDecklinkAudioSinkRingBuffer *self =
- GST_DECKLINK_AUDIO_SINK_RING_BUFFER_CAST (rb);
- GstElement *videosink = NULL;
- gboolean ret = TRUE;
-
- // Check if there is a video sink for this output too and if it
- // is actually in the same pipeline
- g_mutex_lock (&self->output->lock);
- if (self->output->videosink)
- videosink = GST_ELEMENT_CAST (gst_object_ref (self->output->videosink));
- g_mutex_unlock (&self->output->lock);
-
- if (!videosink) {
- GST_ELEMENT_ERROR (self->sink, STREAM, FAILED,
- (NULL), ("Audio sink needs a video sink for its operation"));
- ret = FALSE;
+ GstDecklinkAudioSink *self = GST_DECKLINK_AUDIO_SINK_CAST (bsink);
+ GstDecklinkVideoSink *video_sink;
+ GstFlowReturn flow_ret;
+ HRESULT ret;
+ GstClockTime timestamp, duration;
+ GstClockTime running_time, running_time_duration;
+ GstClockTime schedule_time, schedule_time_duration;
+ GstClockTime latency, render_delay;
+ GstClockTimeDiff ts_offset;
+ GstMapInfo map_info;
+ const guint8 *data;
+ gsize len, written_all;
+
+ GST_DEBUG_OBJECT (self, "Rendering buffer %p", buffer);
+
+ // FIXME: Handle no timestamps
+ if (!GST_BUFFER_TIMESTAMP_IS_VALID (buffer)) {
+ return GST_FLOW_ERROR;
}
- // FIXME: This causes deadlocks sometimes
-#if 0
- else if (!in_same_pipeline (GST_ELEMENT_CAST (self->sink), videosink)) {
- GST_ELEMENT_ERROR (self->sink, STREAM, FAILED,
- (NULL), ("Audio sink and video sink need to be in the same pipeline"));
- ret = FALSE;
+
+ if (GST_BASE_SINK_CAST (self)->flushing) {
+ return GST_FLOW_FLUSHING;
}
-#endif
- if (videosink)
- gst_object_unref (videosink);
- return ret;
-}
+ video_sink =
+ GST_DECKLINK_VIDEO_SINK (gst_object_ref (self->output->videosink));
+
+ timestamp = GST_BUFFER_TIMESTAMP (buffer);
+ duration = GST_BUFFER_DURATION (buffer);
+ gst_audio_stream_align_process (self->stream_align,
+ GST_BUFFER_IS_DISCONT (buffer), timestamp,
+ gst_buffer_get_size (buffer) / self->info.bpf, &timestamp, &duration,
+ NULL);
+
+ gst_buffer_map (buffer, &map_info, GST_MAP_READ);
+ data = map_info.data;
+ len = map_info.size / self->info.bpf;
+ written_all = 0;
+
+ do {
+ GstClockTime timestamp_now =
+ timestamp + gst_util_uint64_scale (written_all, GST_SECOND,
+ self->info.rate);
+ guint32 buffered_samples;
+ GstClockTime buffered_time;
+
+ if (GST_BASE_SINK_CAST (self)->flushing) {
+ flow_ret = GST_FLOW_FLUSHING;
+ break;
+ }
-static gboolean
-gst_decklink_audio_sink_ringbuffer_pause (GstAudioRingBuffer * rb)
-{
- return TRUE;
-}
+ running_time =
+ gst_segment_to_running_time (&GST_BASE_SINK_CAST (self)->segment,
+ GST_FORMAT_TIME, timestamp_now);
+ running_time_duration =
+ gst_segment_to_running_time (&GST_BASE_SINK_CAST (self)->segment,
+ GST_FORMAT_TIME, timestamp_now + duration) - running_time;
+
+ /* See gst_base_sink_adjust_time() */
+ latency = gst_base_sink_get_latency (bsink);
+ render_delay = gst_base_sink_get_render_delay (bsink);
+ ts_offset = gst_base_sink_get_ts_offset (bsink);
+ running_time += latency;
+
+ if (ts_offset < 0) {
+ ts_offset = -ts_offset;
+ if ((GstClockTime) ts_offset < running_time)
+ running_time -= ts_offset;
+ else
+ running_time = 0;
+ } else {
+ running_time += ts_offset;
+ }
-static gboolean
-gst_decklink_audio_sink_ringbuffer_stop (GstAudioRingBuffer * rb)
-{
- return TRUE;
-}
+ if (running_time > render_delay)
+ running_time -= render_delay;
+ else
+ running_time = 0;
-static gboolean
-gst_decklink_audio_sink_ringbuffer_acquire (GstAudioRingBuffer * rb,
- GstAudioRingBufferSpec * spec)
-{
- GstDecklinkAudioSinkRingBuffer *self =
- GST_DECKLINK_AUDIO_SINK_RING_BUFFER_CAST (rb);
- HRESULT ret;
- BMDAudioSampleType sample_depth;
+ if (self->output->
+ output->GetBufferedAudioSampleFrameCount (&buffered_samples) != S_OK)
+ buffered_samples = 0;
- GST_DEBUG_OBJECT (self->sink, "Acquire");
+ buffered_time =
+ gst_util_uint64_scale (buffered_samples, GST_SECOND, self->info.rate);
+ GST_DEBUG_OBJECT (self,
+ "Buffered %" GST_TIME_FORMAT " in the driver (%u samples)",
+ GST_TIME_ARGS (buffered_time), buffered_samples);
- if (spec->info.finfo->format == GST_AUDIO_FORMAT_S16LE) {
- sample_depth = bmdAudioSampleType16bitInteger;
- } else {
- sample_depth = bmdAudioSampleType32bitInteger;
- }
+ buffered_time /= GST_BASE_SINK_CAST (self)->segment.rate;
+ // We start waiting once we have more than buffer-time buffered
+ if (buffered_time > self->buffer_time) {
+ GstClockReturn clock_ret;
+ GstClockTime wait_time = running_time;
- ret = self->output->output->EnableAudioOutput (bmdAudioSampleRate48kHz,
- sample_depth, spec->info.channels, bmdAudioOutputStreamContinuous);
- if (ret != S_OK) {
- GST_WARNING_OBJECT (self->sink, "Failed to enable audio output 0x%08lx",
- (unsigned long) ret);
- return FALSE;
- }
+ GST_DEBUG_OBJECT (self,
+ "Buffered enough, wait for preroll or the clock or flushing");
- ret =
- self->output->
- output->SetAudioCallback (new GStreamerAudioOutputCallback (self));
- if (ret != S_OK) {
- GST_WARNING_OBJECT (self->sink,
- "Failed to set audio output callback 0x%08lx", (unsigned long) ret);
- return FALSE;
- }
+ if (wait_time < self->buffer_time)
+ wait_time = 0;
+ else
+ wait_time -= self->buffer_time;
+
+ flow_ret =
+ gst_base_sink_do_preroll (GST_BASE_SINK_CAST (self),
+ GST_MINI_OBJECT_CAST (buffer));
+ if (flow_ret != GST_FLOW_OK)
+ break;
+
+ clock_ret =
+ gst_base_sink_wait_clock (GST_BASE_SINK_CAST (self), wait_time, NULL);
+ if (GST_BASE_SINK_CAST (self)->flushing) {
+ flow_ret = GST_FLOW_FLUSHING;
+ break;
+ }
+ // Rerun the whole loop again
+ if (clock_ret == GST_CLOCK_UNSCHEDULED)
+ continue;
+ }
- spec->segsize =
- (spec->latency_time * GST_AUDIO_INFO_RATE (&spec->info) /
- G_USEC_PER_SEC) * GST_AUDIO_INFO_BPF (&spec->info);
- spec->segtotal = spec->buffer_time / spec->latency_time;
- // set latency to one more segment as we need some headroom
- spec->seglatency = spec->segtotal + 1;
+ schedule_time = running_time;
+ schedule_time_duration = running_time_duration;
- rb->size = spec->segtotal * spec->segsize;
- rb->memory = (guint8 *) g_malloc0 (rb->size);
+ gst_decklink_video_sink_convert_to_internal_clock (video_sink,
+ &schedule_time, &schedule_time_duration);
- return TRUE;
-}
+ if (!self->output->started) {
+ guint32 written = 0;
+ GST_LOG_OBJECT (self, "Writing audio frame synchronously because PAUSED");
-static gboolean
-gst_decklink_audio_sink_ringbuffer_release (GstAudioRingBuffer * rb)
-{
- GstDecklinkAudioSinkRingBuffer *self =
- GST_DECKLINK_AUDIO_SINK_RING_BUFFER_CAST (rb);
+ ret =
+ self->output->output->WriteAudioSamplesSync ((void *) data, len,
+ &written);
+ if (ret != S_OK) {
+ GST_ELEMENT_WARNING (self, STREAM, FAILED,
+ (NULL), ("Failed to write audio frame synchronously: 0x%08lx",
+ (unsigned long) ret));
+ ret = S_OK;
+ break;
+ }
+ len -= written;
+ data += written * self->info.bpf;
+ written_all += written;
+ } else {
+ guint32 written = 0;
- GST_DEBUG_OBJECT (self->sink, "Release");
+ GST_LOG_OBJECT (self, "Scheduling audio samples at %" GST_TIME_FORMAT
+ " with duration %" GST_TIME_FORMAT, GST_TIME_ARGS (schedule_time),
+ GST_TIME_ARGS (schedule_time_duration));
- if (self->output) {
- g_mutex_lock (&self->clock_id_lock);
- if (self->clock_id)
- gst_clock_id_unschedule (self->clock_id);
- g_mutex_unlock (&self->clock_id_lock);
+ ret = self->output->output->ScheduleAudioSamples ((void *) data, len,
+ schedule_time, GST_SECOND, &written);
+ if (ret != S_OK) {
+ bool is_running = true;
+ self->output->output->IsScheduledPlaybackRunning (&is_running);
+
+ if (is_running && !GST_BASE_SINK_CAST (self)->flushing && self->output->started) {
+ GST_ELEMENT_ERROR (self, STREAM, FAILED,
+ (NULL), ("Failed to schedule frame: 0x%08lx",
+ (unsigned long) ret));
+ flow_ret = GST_FLOW_ERROR;
+ break;
+ } else {
+ flow_ret = GST_FLOW_FLUSHING;
+ break;
+ }
+ }
- g_mutex_lock (&self->output->lock);
- self->output->audio_enabled = FALSE;
- if (self->output->start_scheduled_playback && self->output->videosink)
- self->output->start_scheduled_playback (self->output->videosink);
- g_mutex_unlock (&self->output->lock);
+ len -= written;
+ data += written * self->info.bpf;
+ written_all += written;
+ }
- self->output->output->DisableAudioOutput ();
- }
- // free the buffer
- g_free (rb->memory);
- rb->memory = NULL;
+ flow_ret = GST_FLOW_OK;
+ } while (len > 0);
- return TRUE;
+ gst_buffer_unmap (buffer, &map_info);
+
+ return flow_ret;
}
static gboolean
-gst_decklink_audio_sink_ringbuffer_open_device (GstAudioRingBuffer * rb)
+gst_decklink_audio_sink_open (GstBaseSink * bsink)
{
- GstDecklinkAudioSinkRingBuffer *self =
- GST_DECKLINK_AUDIO_SINK_RING_BUFFER_CAST (rb);
+ GstDecklinkAudioSink *self = GST_DECKLINK_AUDIO_SINK_CAST (bsink);
- GST_DEBUG_OBJECT (self->sink, "Open device");
+ GST_DEBUG_OBJECT (self, "Stopping");
self->output =
- gst_decklink_acquire_nth_output (self->sink->device_number,
+ gst_decklink_acquire_nth_output (self->device_number,
GST_ELEMENT_CAST (self), TRUE);
if (!self->output) {
GST_ERROR_OBJECT (self, "Failed to acquire output");
return FALSE;
}
- g_object_notify (G_OBJECT (self->sink), "hw-serial-number");
-
- gst_decklink_output_set_audio_clock (self->output,
- GST_AUDIO_BASE_SINK_CAST (self->sink)->provided_clock);
+ g_object_notify (G_OBJECT (self), "hw-serial-number");
return TRUE;
}
static gboolean
-gst_decklink_audio_sink_ringbuffer_close_device (GstAudioRingBuffer * rb)
+gst_decklink_audio_sink_close (GstBaseSink * bsink)
{
- GstDecklinkAudioSinkRingBuffer *self =
- GST_DECKLINK_AUDIO_SINK_RING_BUFFER_CAST (rb);
+ GstDecklinkAudioSink *self = GST_DECKLINK_AUDIO_SINK_CAST (bsink);
- GST_DEBUG_OBJECT (self->sink, "Close device");
+ GST_DEBUG_OBJECT (self, "Closing");
if (self->output) {
- gst_decklink_output_set_audio_clock (self->output, NULL);
- gst_decklink_release_nth_output (self->sink->device_number,
+ g_mutex_lock (&self->output->lock);
+ self->output->mode = NULL;
+ self->output->audio_enabled = FALSE;
+ if (self->output->start_scheduled_playback && self->output->videosink)
+ self->output->start_scheduled_playback (self->output->videosink);
+ g_mutex_unlock (&self->output->lock);
+
+ self->output->output->DisableAudioOutput ();
+ gst_decklink_release_nth_output (self->device_number,
GST_ELEMENT_CAST (self), TRUE);
self->output = NULL;
}
@@ -513,140 +653,42 @@ gst_decklink_audio_sink_ringbuffer_close_device (GstAudioRingBuffer * rb)
return TRUE;
}
-enum
-{
- PROP_0,
- PROP_DEVICE_NUMBER,
- PROP_HW_SERIAL_NUMBER
-};
-
-static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink",
- GST_PAD_SINK,
- GST_PAD_ALWAYS,
- GST_STATIC_CAPS
- ("audio/x-raw, format={S16LE,S32LE}, channels={2, 8, 16}, rate=48000, "
- "layout=interleaved")
- );
-
-static void gst_decklink_audio_sink_set_property (GObject * object,
- guint property_id, const GValue * value, GParamSpec * pspec);
-static void gst_decklink_audio_sink_get_property (GObject * object,
- guint property_id, GValue * value, GParamSpec * pspec);
-static void gst_decklink_audio_sink_finalize (GObject * object);
-
-static GstStateChangeReturn gst_decklink_audio_sink_change_state (GstElement *
- element, GstStateChange transition);
-static GstCaps *gst_decklink_audio_sink_get_caps (GstBaseSink * bsink,
- GstCaps * filter);
-static GstAudioRingBuffer
- * gst_decklink_audio_sink_create_ringbuffer (GstAudioBaseSink * absink);
-
-#define parent_class gst_decklink_audio_sink_parent_class
-G_DEFINE_TYPE (GstDecklinkAudioSink, gst_decklink_audio_sink,
- GST_TYPE_AUDIO_BASE_SINK);
-
-static void
-gst_decklink_audio_sink_class_init (GstDecklinkAudioSinkClass * klass)
+static gboolean
+gst_decklink_audio_sink_stop (GstDecklinkAudioSink * self)
{
- GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
- GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
- GstBaseSinkClass *basesink_class = GST_BASE_SINK_CLASS (klass);
- GstAudioBaseSinkClass *audiobasesink_class =
- GST_AUDIO_BASE_SINK_CLASS (klass);
-
- gobject_class->set_property = gst_decklink_audio_sink_set_property;
- gobject_class->get_property = gst_decklink_audio_sink_get_property;
- gobject_class->finalize = gst_decklink_audio_sink_finalize;
-
- element_class->change_state =
- GST_DEBUG_FUNCPTR (gst_decklink_audio_sink_change_state);
-
- basesink_class->get_caps =
- GST_DEBUG_FUNCPTR (gst_decklink_audio_sink_get_caps);
-
- audiobasesink_class->create_ringbuffer =
- GST_DEBUG_FUNCPTR (gst_decklink_audio_sink_create_ringbuffer);
-
- g_object_class_install_property (gobject_class, PROP_DEVICE_NUMBER,
- g_param_spec_int ("device-number", "Device number",
- "Output device instance to use", 0, G_MAXINT, 0,
- (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS |
- G_PARAM_CONSTRUCT)));
-
- g_object_class_install_property (gobject_class, PROP_HW_SERIAL_NUMBER,
- g_param_spec_string ("hw-serial-number", "Hardware serial number",
- "The serial number (hardware ID) of the Decklink card",
- NULL, (GParamFlags) (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)));
+ GST_DEBUG_OBJECT (self, "Stopping");
- gst_element_class_add_static_pad_template (element_class, &sink_template);
+ if (self->output && self->output->audio_enabled) {
+ g_mutex_lock (&self->output->lock);
+ self->output->audio_enabled = FALSE;
+ g_mutex_unlock (&self->output->lock);
- gst_element_class_set_static_metadata (element_class, "Decklink Audio Sink",
- "Audio/Sink", "Decklink Sink", "David Schleef <ds@entropywave.com>, "
- "Sebastian Dröge <sebastian@centricular.com>");
+ self->output->output->DisableAudioOutput ();
+ }
- GST_DEBUG_CATEGORY_INIT (gst_decklink_audio_sink_debug, "decklinkaudiosink",
- 0, "debug category for decklinkaudiosink element");
+ return TRUE;
}
-static void
-gst_decklink_audio_sink_init (GstDecklinkAudioSink * self)
+static gboolean
+gst_decklink_audio_sink_unlock_stop (GstBaseSink * bsink)
{
- self->device_number = 0;
-
- // 25.000ms latency time seems to be needed at least,
- // everything below can cause drop-outs
- // TODO: This is probably related to the video mode that
- // is selected, but not directly it seems. Choosing the
- // duration of a frame does not work.
- GST_AUDIO_BASE_SINK_CAST (self)->latency_time = 25000;
-}
+ GstDecklinkAudioSink *self = GST_DECKLINK_AUDIO_SINK (bsink);
-void
-gst_decklink_audio_sink_set_property (GObject * object, guint property_id,
- const GValue * value, GParamSpec * pspec)
-{
- GstDecklinkAudioSink *self = GST_DECKLINK_AUDIO_SINK_CAST (object);
-
- switch (property_id) {
- case PROP_DEVICE_NUMBER:
- self->device_number = g_value_get_int (value);
- break;
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
- break;
+ if (self->output) {
+ self->output->output->FlushBufferedAudioSamples ();
}
-}
-
-void
-gst_decklink_audio_sink_get_property (GObject * object, guint property_id,
- GValue * value, GParamSpec * pspec)
-{
- GstDecklinkAudioSink *self = GST_DECKLINK_AUDIO_SINK_CAST (object);
- switch (property_id) {
- case PROP_DEVICE_NUMBER:
- g_value_set_int (value, self->device_number);
- break;
- case PROP_HW_SERIAL_NUMBER:{
- GstDecklinkAudioSinkRingBuffer *buf =
- GST_DECKLINK_AUDIO_SINK_RING_BUFFER_CAST (GST_AUDIO_BASE_SINK_CAST
- (self)->ringbuffer);
- if (buf && buf->output)
- g_value_set_string (value, buf->output->hw_serial_number);
- else
- g_value_set_string (value, NULL);
- break;
- }
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
- break;
- }
+ return TRUE;
}
-void
-gst_decklink_audio_sink_finalize (GObject * object)
+static void
+gst_decklink_audio_sink_get_times (GstBaseSink * bsink, GstBuffer * buffer,
+ GstClockTime * start, GstClockTime * end)
{
- G_OBJECT_CLASS (parent_class)->finalize (object);
+ /* our clock sync is a bit too much for the base class to handle so
+ * we implement it ourselves. */
+ *start = GST_CLOCK_TIME_NONE;
+ *end = GST_CLOCK_TIME_NONE;
}
static GstStateChangeReturn
@@ -654,23 +696,33 @@ gst_decklink_audio_sink_change_state (GstElement * element,
GstStateChange transition)
{
GstDecklinkAudioSink *self = GST_DECKLINK_AUDIO_SINK_CAST (element);
- GstDecklinkAudioSinkRingBuffer *buf =
- GST_DECKLINK_AUDIO_SINK_RING_BUFFER_CAST (GST_AUDIO_BASE_SINK_CAST
- (self)->ringbuffer);
- GstStateChangeReturn ret;
+ GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
+
+ switch (transition) {
+ case GST_STATE_CHANGE_READY_TO_PAUSED:
+ GST_OBJECT_LOCK (self);
+ gst_audio_stream_align_mark_discont (self->stream_align);
+ GST_OBJECT_UNLOCK (self);
+ break;
+ default:
+ break;
+ }
ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
if (ret == GST_STATE_CHANGE_FAILURE)
return ret;
switch (transition) {
- case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
- g_mutex_lock (&buf->output->lock);
- buf->output->audio_enabled = TRUE;
- if (buf->output->start_scheduled_playback && buf->output->videosink)
- buf->output->start_scheduled_playback (buf->output->videosink);
- g_mutex_unlock (&buf->output->lock);
+ case GST_STATE_CHANGE_PAUSED_TO_READY:
+ gst_decklink_audio_sink_stop (self);
break;
+ case GST_STATE_CHANGE_PAUSED_TO_PLAYING:{
+ g_mutex_lock (&self->output->lock);
+ if (self->output->start_scheduled_playback)
+ self->output->start_scheduled_playback (self->output->videosink);
+ g_mutex_unlock (&self->output->lock);
+ break;
+ }
default:
break;
}
@@ -678,78 +730,13 @@ gst_decklink_audio_sink_change_state (GstElement * element,
return ret;
}
-static GstCaps *
-gst_decklink_audio_sink_get_caps (GstBaseSink * bsink, GstCaps * filter)
+static GstClock *
+gst_decklink_audio_sink_provide_clock (GstElement * element)
{
- GstDecklinkAudioSink *self = GST_DECKLINK_AUDIO_SINK_CAST (bsink);
- GstDecklinkAudioSinkRingBuffer *buf =
- GST_DECKLINK_AUDIO_SINK_RING_BUFFER_CAST (GST_AUDIO_BASE_SINK_CAST
- (self)->ringbuffer);
- GstCaps *caps = gst_pad_get_pad_template_caps (GST_BASE_SINK_PAD (bsink));
-
- if (buf) {
- GST_OBJECT_LOCK (buf);
- if (buf->output && buf->output->attributes) {
- int64_t max_channels = 0;
- HRESULT ret;
- GstStructure *s;
- GValue arr = G_VALUE_INIT;
- GValue v = G_VALUE_INIT;
-
- ret =
- buf->output->attributes->GetInt (BMDDeckLinkMaximumAudioChannels,
- &max_channels);
- /* 2 should always be supported */
- if (ret != S_OK) {
- max_channels = 2;
- }
-
- caps = gst_caps_make_writable (caps);
- s = gst_caps_get_structure (caps, 0);
-
- g_value_init (&arr, GST_TYPE_LIST);
- g_value_init (&v, G_TYPE_INT);
- if (max_channels >= 16) {
- g_value_set_int (&v, 16);
- gst_value_list_append_value (&arr, &v);
- }
- if (max_channels >= 8) {
- g_value_set_int (&v, 8);
- gst_value_list_append_value (&arr, &v);
- }
- g_value_set_int (&v, 2);
- gst_value_list_append_value (&arr, &v);
-
- gst_structure_set_value (s, "channels", &arr);
- g_value_unset (&v);
- g_value_unset (&arr);
- }
- GST_OBJECT_UNLOCK (buf);
- }
-
- if (filter) {
- GstCaps *intersection =
- gst_caps_intersect_full (filter, caps, GST_CAPS_INTERSECT_FIRST);
- gst_caps_unref (caps);
- caps = intersection;
- }
-
- return caps;
-}
-
-static GstAudioRingBuffer *
-gst_decklink_audio_sink_create_ringbuffer (GstAudioBaseSink * absink)
-{
- GstAudioRingBuffer *ret;
-
- GST_DEBUG_OBJECT (absink, "Creating ringbuffer");
-
- ret =
- GST_AUDIO_RING_BUFFER_CAST (g_object_new
- (GST_TYPE_DECKLINK_AUDIO_SINK_RING_BUFFER, NULL));
+ GstDecklinkAudioSink *self = GST_DECKLINK_AUDIO_SINK_CAST (element);
- GST_DECKLINK_AUDIO_SINK_RING_BUFFER_CAST (ret)->sink =
- (GstDecklinkAudioSink *) gst_object_ref (absink);
+ if (!self->output)
+ return NULL;
- return ret;
+ return GST_CLOCK_CAST (gst_object_ref (self->output->clock));
}
diff --git a/sys/decklink/gstdecklinkaudiosink.h b/sys/decklink/gstdecklinkaudiosink.h
index 82d20c392..c04aee61b 100644
--- a/sys/decklink/gstdecklinkaudiosink.h
+++ b/sys/decklink/gstdecklinkaudiosink.h
@@ -23,6 +23,7 @@
#define __GST_DECKLINK_AUDIO_SINK_H__
#include <gst/gst.h>
+#include <gst/base/base.h>
#include <gst/audio/audio.h>
#include "gstdecklink.h"
@@ -46,15 +47,20 @@ typedef struct _GstDecklinkAudioSinkClass GstDecklinkAudioSinkClass;
struct _GstDecklinkAudioSink
{
- GstAudioBaseSink parent;
+ GstBaseSink parent;
GstDecklinkModeEnum mode;
gint device_number;
+ GstClockTime buffer_time;
+
+ GstDecklinkOutput *output;
+ GstAudioInfo info;
+ GstAudioStreamAlign *stream_align;
};
struct _GstDecklinkAudioSinkClass
{
- GstAudioBaseSinkClass parent_class;
+ GstBaseSinkClass parent_class;
};
GType gst_decklink_audio_sink_get_type (void);
diff --git a/sys/decklink/gstdecklinkvideosink.cpp b/sys/decklink/gstdecklinkvideosink.cpp
index ac61637ba..90bd43e4b 100644
--- a/sys/decklink/gstdecklinkvideosink.cpp
+++ b/sys/decklink/gstdecklinkvideosink.cpp
@@ -526,17 +526,16 @@ gst_decklink_video_sink_render (GstBaseSink * bsink, GstBuffer * buffer)
return GST_FLOW_OK;
}
-static void
-convert_to_internal_clock (GstDecklinkVideoSink * self,
+void
+gst_decklink_video_sink_convert_to_internal_clock (GstDecklinkVideoSink * self,
GstClockTime * timestamp, GstClockTime * duration)
{
- GstClock *clock, *audio_clock;
+ GstClock *clock;
g_assert (timestamp != NULL);
clock = gst_element_get_clock (GST_ELEMENT_CAST (self));
- audio_clock = gst_decklink_output_get_audio_clock (self->output);
- if (clock && clock != self->output->clock && clock != audio_clock) {
+ if (clock && clock != self->output->clock) {
GstClockTime internal, external, rate_n, rate_d;
gst_clock_get_calibration (self->output->clock, &internal, &external,
&rate_n, &rate_d);
@@ -729,7 +728,7 @@ gst_decklink_video_sink_prepare (GstBaseSink * bsink, GstBuffer * buffer)
g_free (tc_str);
}
- convert_to_internal_clock (self, &running_time, &running_time_duration);
+ gst_decklink_video_sink_convert_to_internal_clock (self, &running_time, &running_time_duration);
if (!self->output->started) {
GST_LOG_OBJECT (self, "Showing video frame synchronously because PAUSED");
@@ -882,7 +881,7 @@ gst_decklink_video_sink_start_scheduled_playback (GstElement * element)
gst_clock_get_internal_time (self->output->clock);
self->external_base_time = gst_clock_get_internal_time (clock);
- convert_to_internal_clock (self, &start_time, NULL);
+ gst_decklink_video_sink_convert_to_internal_clock (self, &start_time, NULL);
g_mutex_lock (&self->output->lock);
// Check if someone else started in the meantime
@@ -970,7 +969,7 @@ gst_decklink_video_sink_stop_scheduled_playback (GstDecklinkVideoSink * self)
if (start_time == GST_CLOCK_TIME_NONE)
start_time = 0;
- convert_to_internal_clock (self, &start_time, NULL);
+ gst_decklink_video_sink_convert_to_internal_clock (self, &start_time, NULL);
// The start time is now the running time when we stopped
// playback
@@ -1030,17 +1029,14 @@ gst_decklink_video_sink_change_state (GstElement * element,
self->output->clock, TRUE));
break;
case GST_STATE_CHANGE_PAUSED_TO_PLAYING:{
- GstClock *clock, *audio_clock;
+ GstClock *clock;
clock = gst_element_get_clock (GST_ELEMENT_CAST (self));
if (clock) {
- audio_clock = gst_decklink_output_get_audio_clock (self->output);
- if (clock && clock != self->output->clock && clock != audio_clock) {
+ if (clock && clock != self->output->clock) {
gst_clock_set_master (self->output->clock, clock);
}
gst_object_unref (clock);
- if (audio_clock)
- gst_object_unref (audio_clock);
} else {
GST_ELEMENT_ERROR (self, STREAM, FAILED,
(NULL), ("Need a clock to go to PLAYING"));
diff --git a/sys/decklink/gstdecklinkvideosink.h b/sys/decklink/gstdecklinkvideosink.h
index 1b60df196..08eb0a0ec 100644
--- a/sys/decklink/gstdecklinkvideosink.h
+++ b/sys/decklink/gstdecklinkvideosink.h
@@ -71,6 +71,9 @@ struct _GstDecklinkVideoSinkClass
GType gst_decklink_video_sink_get_type (void);
+void gst_decklink_video_sink_convert_to_internal_clock (GstDecklinkVideoSink * self,
+ GstClockTime * timestamp, GstClockTime * duration);
+
G_END_DECLS
#endif /* __GST_DECKLINK_VIDEO_SINK_H__ */