mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-12-24 09:10:36 +00:00
decklinkaudiosink: Implement resampling/buffer reversing for trick-modes
https://bugzilla.gnome.org/show_bug.cgi?id=790114
This commit is contained in:
parent
86888d9918
commit
118b2967e7
2 changed files with 144 additions and 10 deletions
|
@ -70,6 +70,8 @@ static void gst_decklink_audio_sink_get_times (GstBaseSink * bsink,
|
||||||
GstBuffer * buffer, GstClockTime * start, GstClockTime * end);
|
GstBuffer * buffer, GstClockTime * start, GstClockTime * end);
|
||||||
static gboolean gst_decklink_audio_sink_query (GstBaseSink * bsink,
|
static gboolean gst_decklink_audio_sink_query (GstBaseSink * bsink,
|
||||||
GstQuery * query);
|
GstQuery * query);
|
||||||
|
static gboolean gst_decklink_audio_sink_event (GstBaseSink * bsink,
|
||||||
|
GstEvent * event);
|
||||||
|
|
||||||
static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink",
|
static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink",
|
||||||
GST_PAD_SINK,
|
GST_PAD_SINK,
|
||||||
|
@ -112,6 +114,7 @@ gst_decklink_audio_sink_class_init (GstDecklinkAudioSinkClass * klass)
|
||||||
basesink_class->get_times =
|
basesink_class->get_times =
|
||||||
GST_DEBUG_FUNCPTR (gst_decklink_audio_sink_get_times);
|
GST_DEBUG_FUNCPTR (gst_decklink_audio_sink_get_times);
|
||||||
basesink_class->query = GST_DEBUG_FUNCPTR (gst_decklink_audio_sink_query);
|
basesink_class->query = GST_DEBUG_FUNCPTR (gst_decklink_audio_sink_query);
|
||||||
|
basesink_class->event = GST_DEBUG_FUNCPTR (gst_decklink_audio_sink_event);
|
||||||
|
|
||||||
g_object_class_install_property (gobject_class, PROP_DEVICE_NUMBER,
|
g_object_class_install_property (gobject_class, PROP_DEVICE_NUMBER,
|
||||||
g_param_spec_int ("device-number", "Device number",
|
g_param_spec_int ("device-number", "Device number",
|
||||||
|
@ -292,6 +295,11 @@ gst_decklink_audio_sink_set_caps (GstBaseSink * bsink, GstCaps * caps)
|
||||||
self->output->audio_enabled = TRUE;
|
self->output->audio_enabled = TRUE;
|
||||||
self->info = info;
|
self->info = info;
|
||||||
|
|
||||||
|
// Create a new resampler as needed
|
||||||
|
if (self->resampler)
|
||||||
|
gst_audio_resampler_free (self->resampler);
|
||||||
|
self->resampler = NULL;
|
||||||
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -425,6 +433,43 @@ done:
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
gst_decklink_audio_sink_event (GstBaseSink * bsink, GstEvent * event)
|
||||||
|
{
|
||||||
|
GstDecklinkAudioSink *self = GST_DECKLINK_AUDIO_SINK_CAST (bsink);
|
||||||
|
|
||||||
|
if (GST_EVENT_TYPE (event) == GST_EVENT_SEGMENT) {
|
||||||
|
const GstSegment *new_segment;
|
||||||
|
|
||||||
|
gst_event_parse_segment (event, &new_segment);
|
||||||
|
|
||||||
|
if (ABS (new_segment->rate) != 1.0) {
|
||||||
|
guint out_rate = self->info.rate / ABS (new_segment->rate);
|
||||||
|
|
||||||
|
if (self->resampler && (self->resampler_out_rate != out_rate
|
||||||
|
|| self->resampler_in_rate != (guint) self->info.rate))
|
||||||
|
gst_audio_resampler_update (self->resampler, self->info.rate, out_rate,
|
||||||
|
NULL);
|
||||||
|
else if (!self->resampler)
|
||||||
|
self->resampler =
|
||||||
|
gst_audio_resampler_new (GST_AUDIO_RESAMPLER_METHOD_LINEAR,
|
||||||
|
GST_AUDIO_RESAMPLER_FLAG_NONE, self->info.finfo->format,
|
||||||
|
self->info.channels, self->info.rate, out_rate, NULL);
|
||||||
|
|
||||||
|
self->resampler_in_rate = self->info.rate;
|
||||||
|
self->resampler_out_rate = out_rate;
|
||||||
|
} else if (self->resampler) {
|
||||||
|
gst_audio_resampler_free (self->resampler);
|
||||||
|
self->resampler = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (new_segment->rate < 0)
|
||||||
|
gst_audio_stream_align_set_rate (self->stream_align, -48000);
|
||||||
|
}
|
||||||
|
|
||||||
|
return GST_BASE_SINK_CLASS (parent_class)->event (bsink, event);
|
||||||
|
}
|
||||||
|
|
||||||
static GstFlowReturn
|
static GstFlowReturn
|
||||||
gst_decklink_audio_sink_render (GstBaseSink * bsink, GstBuffer * buffer)
|
gst_decklink_audio_sink_render (GstBaseSink * bsink, GstBuffer * buffer)
|
||||||
{
|
{
|
||||||
|
@ -440,6 +485,7 @@ gst_decklink_audio_sink_render (GstBaseSink * bsink, GstBuffer * buffer)
|
||||||
GstMapInfo map_info;
|
GstMapInfo map_info;
|
||||||
const guint8 *data;
|
const guint8 *data;
|
||||||
gsize len, written_all;
|
gsize len, written_all;
|
||||||
|
gboolean discont;
|
||||||
|
|
||||||
GST_DEBUG_OBJECT (self, "Rendering buffer %p", buffer);
|
GST_DEBUG_OBJECT (self, "Rendering buffer %p", buffer);
|
||||||
|
|
||||||
|
@ -457,11 +503,77 @@ gst_decklink_audio_sink_render (GstBaseSink * bsink, GstBuffer * buffer)
|
||||||
|
|
||||||
timestamp = GST_BUFFER_TIMESTAMP (buffer);
|
timestamp = GST_BUFFER_TIMESTAMP (buffer);
|
||||||
duration = GST_BUFFER_DURATION (buffer);
|
duration = GST_BUFFER_DURATION (buffer);
|
||||||
gst_audio_stream_align_process (self->stream_align,
|
discont = gst_audio_stream_align_process (self->stream_align,
|
||||||
GST_BUFFER_IS_DISCONT (buffer), timestamp,
|
GST_BUFFER_IS_DISCONT (buffer), timestamp,
|
||||||
gst_buffer_get_size (buffer) / self->info.bpf, ×tamp, &duration,
|
gst_buffer_get_size (buffer) / self->info.bpf, ×tamp, &duration,
|
||||||
NULL);
|
NULL);
|
||||||
|
|
||||||
|
if (discont && self->resampler)
|
||||||
|
gst_audio_resampler_reset (self->resampler);
|
||||||
|
|
||||||
|
if (GST_BASE_SINK_CAST (self)->segment.rate < 0.0) {
|
||||||
|
GstMapInfo out_map;
|
||||||
|
gint out_frames = gst_buffer_get_size (buffer) / self->info.bpf;
|
||||||
|
|
||||||
|
buffer = gst_buffer_make_writable (gst_buffer_ref (buffer));
|
||||||
|
|
||||||
|
gst_buffer_map (buffer, &out_map, GST_MAP_READWRITE);
|
||||||
|
if (self->info.finfo->format == GST_AUDIO_FORMAT_S16) {
|
||||||
|
gint16 *swap_data = (gint16 *) out_map.data;
|
||||||
|
gint16 *swap_data_end =
|
||||||
|
swap_data + (out_frames - 1) * self->info.channels;
|
||||||
|
gint16 swap_tmp[16];
|
||||||
|
|
||||||
|
while (out_frames > 0) {
|
||||||
|
memcpy (&swap_tmp, swap_data, self->info.bpf);
|
||||||
|
memcpy (swap_data, swap_data_end, self->info.bpf);
|
||||||
|
memcpy (swap_data_end, &swap_tmp, self->info.bpf);
|
||||||
|
|
||||||
|
swap_data += self->info.channels;
|
||||||
|
swap_data_end -= self->info.channels;
|
||||||
|
|
||||||
|
out_frames -= 2;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
gint32 *swap_data = (gint32 *) out_map.data;
|
||||||
|
gint32 *swap_data_end =
|
||||||
|
swap_data + (out_frames - 1) * self->info.channels;
|
||||||
|
gint32 swap_tmp[16];
|
||||||
|
|
||||||
|
while (out_frames > 0) {
|
||||||
|
memcpy (&swap_tmp, swap_data, self->info.bpf);
|
||||||
|
memcpy (swap_data, swap_data_end, self->info.bpf);
|
||||||
|
memcpy (swap_data_end, &swap_tmp, self->info.bpf);
|
||||||
|
|
||||||
|
swap_data += self->info.channels;
|
||||||
|
swap_data_end -= self->info.channels;
|
||||||
|
|
||||||
|
out_frames -= 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
gst_buffer_unmap (buffer, &out_map);
|
||||||
|
} else {
|
||||||
|
gst_buffer_ref (buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (self->resampler) {
|
||||||
|
gint in_frames = gst_buffer_get_size (buffer) / self->info.bpf;
|
||||||
|
gint out_frames =
|
||||||
|
gst_audio_resampler_get_out_frames (self->resampler, in_frames);
|
||||||
|
GstBuffer *out_buf = gst_buffer_new_and_alloc (out_frames * self->info.bpf);
|
||||||
|
GstMapInfo out_map;
|
||||||
|
|
||||||
|
gst_buffer_map (buffer, &map_info, GST_MAP_READ);
|
||||||
|
gst_buffer_map (out_buf, &out_map, GST_MAP_READWRITE);
|
||||||
|
|
||||||
|
gst_audio_resampler_resample (self->resampler, (gpointer *) & map_info.data,
|
||||||
|
in_frames, (gpointer *) & out_map.data, out_frames);
|
||||||
|
|
||||||
|
gst_buffer_unmap (out_buf, &out_map);
|
||||||
|
gst_buffer_unmap (buffer, &map_info);
|
||||||
|
buffer = out_buf;
|
||||||
|
}
|
||||||
|
|
||||||
gst_buffer_map (buffer, &map_info, GST_MAP_READ);
|
gst_buffer_map (buffer, &map_info, GST_MAP_READ);
|
||||||
data = map_info.data;
|
data = map_info.data;
|
||||||
len = map_info.size / self->info.bpf;
|
len = map_info.size / self->info.bpf;
|
||||||
|
@ -513,11 +625,10 @@ gst_decklink_audio_sink_render (GstBaseSink * bsink, GstBuffer * buffer)
|
||||||
|
|
||||||
buffered_time =
|
buffered_time =
|
||||||
gst_util_uint64_scale (buffered_samples, GST_SECOND, self->info.rate);
|
gst_util_uint64_scale (buffered_samples, GST_SECOND, self->info.rate);
|
||||||
|
buffered_time /= ABS (GST_BASE_SINK_CAST (self)->segment.rate);
|
||||||
GST_DEBUG_OBJECT (self,
|
GST_DEBUG_OBJECT (self,
|
||||||
"Buffered %" GST_TIME_FORMAT " in the driver (%u samples)",
|
"Buffered %" GST_TIME_FORMAT " in the driver (%u samples)",
|
||||||
GST_TIME_ARGS (buffered_time), buffered_samples);
|
GST_TIME_ARGS (buffered_time), buffered_samples);
|
||||||
|
|
||||||
buffered_time /= GST_BASE_SINK_CAST (self)->segment.rate;
|
|
||||||
// We start waiting once we have more than buffer-time buffered
|
// We start waiting once we have more than buffer-time buffered
|
||||||
if (buffered_time > self->buffer_time) {
|
if (buffered_time > self->buffer_time) {
|
||||||
GstClockReturn clock_ret;
|
GstClockReturn clock_ret;
|
||||||
|
@ -562,14 +673,26 @@ gst_decklink_audio_sink_render (GstBaseSink * bsink, GstBuffer * buffer)
|
||||||
self->output->output->WriteAudioSamplesSync ((void *) data, len,
|
self->output->output->WriteAudioSamplesSync ((void *) data, len,
|
||||||
&written);
|
&written);
|
||||||
if (ret != S_OK) {
|
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_WARNING (self, STREAM, FAILED,
|
GST_ELEMENT_WARNING (self, STREAM, FAILED,
|
||||||
(NULL), ("Failed to write audio frame synchronously: 0x%08lx",
|
(NULL), ("Failed to write audio frame synchronously: 0x%08lx",
|
||||||
(unsigned long) ret));
|
(unsigned long) ret));
|
||||||
ret = S_OK;
|
flow_ret = GST_FLOW_ERROR;
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
flow_ret = GST_FLOW_FLUSHING;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
len -= written;
|
len -= written;
|
||||||
data += written * self->info.bpf;
|
data += written * self->info.bpf;
|
||||||
|
if (self->resampler)
|
||||||
|
written_all += written * ABS (GST_BASE_SINK_CAST (self)->segment.rate);
|
||||||
|
else
|
||||||
written_all += written;
|
written_all += written;
|
||||||
} else {
|
} else {
|
||||||
guint32 written = 0;
|
guint32 written = 0;
|
||||||
|
@ -598,6 +721,9 @@ gst_decklink_audio_sink_render (GstBaseSink * bsink, GstBuffer * buffer)
|
||||||
|
|
||||||
len -= written;
|
len -= written;
|
||||||
data += written * self->info.bpf;
|
data += written * self->info.bpf;
|
||||||
|
if (self->resampler)
|
||||||
|
written_all += written * ABS (GST_BASE_SINK_CAST (self)->segment.rate);
|
||||||
|
else
|
||||||
written_all += written;
|
written_all += written;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -605,6 +731,7 @@ gst_decklink_audio_sink_render (GstBaseSink * bsink, GstBuffer * buffer)
|
||||||
} while (len > 0);
|
} while (len > 0);
|
||||||
|
|
||||||
gst_buffer_unmap (buffer, &map_info);
|
gst_buffer_unmap (buffer, &map_info);
|
||||||
|
gst_buffer_unref (buffer);
|
||||||
|
|
||||||
return flow_ret;
|
return flow_ret;
|
||||||
}
|
}
|
||||||
|
@ -666,6 +793,11 @@ gst_decklink_audio_sink_stop (GstDecklinkAudioSink * self)
|
||||||
self->output->output->DisableAudioOutput ();
|
self->output->output->DisableAudioOutput ();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (self->resampler) {
|
||||||
|
gst_audio_resampler_free (self->resampler);
|
||||||
|
self->resampler = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -56,6 +56,8 @@ struct _GstDecklinkAudioSink
|
||||||
GstDecklinkOutput *output;
|
GstDecklinkOutput *output;
|
||||||
GstAudioInfo info;
|
GstAudioInfo info;
|
||||||
GstAudioStreamAlign *stream_align;
|
GstAudioStreamAlign *stream_align;
|
||||||
|
GstAudioResampler *resampler;
|
||||||
|
guint resampler_in_rate, resampler_out_rate;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct _GstDecklinkAudioSinkClass
|
struct _GstDecklinkAudioSinkClass
|
||||||
|
|
Loading…
Reference in a new issue