mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-11 20:01:35 +00:00
decklink2sink: Add auto-restart property
Support automatic restart when frame dropping happens
This commit is contained in:
parent
ce71612e83
commit
5125f68b12
3 changed files with 85 additions and 17 deletions
|
@ -149,8 +149,7 @@ public:
|
||||||
|
|
||||||
buffer_.resize (cur_size + num_samples);
|
buffer_.resize (cur_size + num_samples);
|
||||||
if (cur_size > 0) {
|
if (cur_size > 0) {
|
||||||
memmove (&buffer_[0] + silence_length_in_bytes, &buffer_[0],
|
memmove (&buffer_[0] + silence_length_in_bytes, &buffer_[0], cur_size);
|
||||||
cur_size);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
gst_audio_format_info_fill_silence (info_.finfo, &buffer_[0],
|
gst_audio_format_info_fill_silence (info_.finfo, &buffer_[0],
|
||||||
|
@ -234,12 +233,16 @@ struct _GstDeckLink2Output
|
||||||
|
|
||||||
gboolean configured;
|
gboolean configured;
|
||||||
gboolean prerolled;
|
gboolean prerolled;
|
||||||
|
guint64 late_count;
|
||||||
|
guint64 drop_count;
|
||||||
|
guint64 underrun_count;
|
||||||
};
|
};
|
||||||
|
|
||||||
static void gst_decklink2_output_dispose (GObject * object);
|
static void gst_decklink2_output_dispose (GObject * object);
|
||||||
static void gst_decklink2_output_finalize (GObject * object);
|
static void gst_decklink2_output_finalize (GObject * object);
|
||||||
static void gst_decklink2_output_on_stopped (GstDeckLink2Output * self);
|
static void gst_decklink2_output_on_stopped (GstDeckLink2Output * self);
|
||||||
static void gst_decklink2_output_on_completed (GstDeckLink2Output * self);
|
static void gst_decklink2_output_on_completed (GstDeckLink2Output * self,
|
||||||
|
BMDOutputFrameCompletionResult result);
|
||||||
|
|
||||||
#define gst_decklink2_output_parent_class parent_class
|
#define gst_decklink2_output_parent_class parent_class
|
||||||
G_DEFINE_TYPE (GstDeckLink2Output, gst_decklink2_output, GST_TYPE_OBJECT);
|
G_DEFINE_TYPE (GstDeckLink2Output, gst_decklink2_output, GST_TYPE_OBJECT);
|
||||||
|
@ -308,8 +311,9 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
if (result == bmdOutputFrameCompleted ||
|
if (result == bmdOutputFrameCompleted ||
|
||||||
result == bmdOutputFrameDisplayedLate) {
|
result == bmdOutputFrameDisplayedLate ||
|
||||||
gst_decklink2_output_on_completed (output_);
|
result == bmdOutputFrameDropped) {
|
||||||
|
gst_decklink2_output_on_completed (output_, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
return S_OK;
|
return S_OK;
|
||||||
|
@ -1013,7 +1017,8 @@ gst_decklink2_output_schedule_video_internal (GstDeckLink2Output * self,
|
||||||
|
|
||||||
HRESULT
|
HRESULT
|
||||||
gst_decklink2_output_schedule_stream (GstDeckLink2Output * output,
|
gst_decklink2_output_schedule_stream (GstDeckLink2Output * output,
|
||||||
IDeckLinkVideoFrame * frame, guint8 * audio_buf, gsize audio_buf_size)
|
IDeckLinkVideoFrame * frame, guint8 * audio_buf, gsize audio_buf_size,
|
||||||
|
guint64 * drop_count, guint64 * late_count, guint64 * underrun_count)
|
||||||
{
|
{
|
||||||
GstDeckLink2OutputPrivate *priv = output->priv;
|
GstDeckLink2OutputPrivate *priv = output->priv;
|
||||||
HRESULT hr;
|
HRESULT hr;
|
||||||
|
@ -1022,6 +1027,10 @@ gst_decklink2_output_schedule_stream (GstDeckLink2Output * output,
|
||||||
|
|
||||||
g_assert (output->configured);
|
g_assert (output->configured);
|
||||||
|
|
||||||
|
*drop_count = 0;
|
||||||
|
*late_count = 0;
|
||||||
|
*underrun_count = 0;
|
||||||
|
|
||||||
hr = gst_decklink2_output_is_running (output, &active);
|
hr = gst_decklink2_output_is_running (output, &active);
|
||||||
if (!gst_decklink2_result (hr)) {
|
if (!gst_decklink2_result (hr)) {
|
||||||
GST_ERROR_OBJECT (output, "Couldn't query active state, hr: 0x%x",
|
GST_ERROR_OBJECT (output, "Couldn't query active state, hr: 0x%x",
|
||||||
|
@ -1056,7 +1065,12 @@ gst_decklink2_output_schedule_stream (GstDeckLink2Output * output,
|
||||||
std::lock_guard < std::mutex > slk (priv->schedule_lock);
|
std::lock_guard < std::mutex > slk (priv->schedule_lock);
|
||||||
priv->audio_buf.Append (audio_buf, audio_buf_size);
|
priv->audio_buf.Append (audio_buf, audio_buf_size);
|
||||||
|
|
||||||
return gst_decklink2_output_schedule_video_internal (output, frame);
|
hr = gst_decklink2_output_schedule_video_internal (output, frame);
|
||||||
|
*drop_count = output->drop_count;
|
||||||
|
*late_count = output->late_count;
|
||||||
|
*underrun_count = output->underrun_count;
|
||||||
|
|
||||||
|
return hr;
|
||||||
}
|
}
|
||||||
|
|
||||||
static HRESULT
|
static HRESULT
|
||||||
|
@ -1094,6 +1108,10 @@ gst_decklink2_output_stop_internal (GstDeckLink2Output * self)
|
||||||
gst_decklink2_output_disable_video (self);
|
gst_decklink2_output_disable_video (self);
|
||||||
gst_decklink2_output_set_video_callback (self, NULL);
|
gst_decklink2_output_set_video_callback (self, NULL);
|
||||||
self->configured = FALSE;
|
self->configured = FALSE;
|
||||||
|
self->prerolled = FALSE;
|
||||||
|
self->late_count = 0;
|
||||||
|
self->drop_count = 0;
|
||||||
|
self->underrun_count = 0;
|
||||||
|
|
||||||
return hr;
|
return hr;
|
||||||
}
|
}
|
||||||
|
@ -1934,6 +1952,9 @@ gst_decklink2_output_configure (GstDeckLink2Output * output,
|
||||||
output->cdp_hdr_sequence_cntr = 0;
|
output->cdp_hdr_sequence_cntr = 0;
|
||||||
output->configured = TRUE;
|
output->configured = TRUE;
|
||||||
output->pts = 0;
|
output->pts = 0;
|
||||||
|
output->drop_count = 0;
|
||||||
|
output->late_count = 0;
|
||||||
|
output->underrun_count = 0;
|
||||||
|
|
||||||
return S_OK;
|
return S_OK;
|
||||||
|
|
||||||
|
@ -1951,13 +1972,19 @@ gst_decklink2_output_on_stopped (GstDeckLink2Output * self)
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
gst_decklink2_output_on_completed (GstDeckLink2Output * self)
|
gst_decklink2_output_on_completed (GstDeckLink2Output * self,
|
||||||
|
BMDOutputFrameCompletionResult result)
|
||||||
{
|
{
|
||||||
GstDeckLink2OutputPrivate *priv = self->priv;
|
GstDeckLink2OutputPrivate *priv = self->priv;
|
||||||
dlbool_t active;
|
dlbool_t active;
|
||||||
guint32 count = 0;
|
guint32 count = 0;
|
||||||
|
|
||||||
std::lock_guard < std::mutex > lk (priv->schedule_lock);
|
std::lock_guard < std::mutex > lk (priv->schedule_lock);
|
||||||
|
if (result == bmdOutputFrameDisplayedLate)
|
||||||
|
self->late_count++;
|
||||||
|
else if (result == bmdOutputFrameDropped)
|
||||||
|
self->drop_count++;
|
||||||
|
|
||||||
if (!self->last_frame)
|
if (!self->last_frame)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -1966,6 +1993,7 @@ gst_decklink2_output_on_completed (GstDeckLink2Output * self)
|
||||||
hr = gst_decklink2_output_get_num_bufferred (self, &count);
|
hr = gst_decklink2_output_get_num_bufferred (self, &count);
|
||||||
if (gst_decklink2_result (hr) && count <= self->min_buffered) {
|
if (gst_decklink2_result (hr) && count <= self->min_buffered) {
|
||||||
GST_WARNING_OBJECT (self, "Underrun, buffered count %u", count);
|
GST_WARNING_OBJECT (self, "Underrun, buffered count %u", count);
|
||||||
|
self->underrun_count++;
|
||||||
priv->audio_buf.PrependSilence ();
|
priv->audio_buf.PrependSilence ();
|
||||||
gst_decklink2_output_schedule_video_internal (self, self->last_frame);
|
gst_decklink2_output_schedule_video_internal (self, self->last_frame);
|
||||||
}
|
}
|
||||||
|
|
|
@ -65,7 +65,10 @@ IDeckLinkVideoFrame * gst_decklink2_output_upload (GstDeckLink2Output *
|
||||||
HRESULT gst_decklink2_output_schedule_stream (GstDeckLink2Output * output,
|
HRESULT gst_decklink2_output_schedule_stream (GstDeckLink2Output * output,
|
||||||
IDeckLinkVideoFrame * frame,
|
IDeckLinkVideoFrame * frame,
|
||||||
guint8 *audio_buf,
|
guint8 *audio_buf,
|
||||||
gsize audio_buf_size);
|
gsize audio_buf_size,
|
||||||
|
guint64 * drop_count,
|
||||||
|
guint64 * late_count,
|
||||||
|
guint64 * underrun_count);
|
||||||
|
|
||||||
HRESULT gst_decklink2_output_stop (GstDeckLink2Output * output);
|
HRESULT gst_decklink2_output_stop (GstDeckLink2Output * output);
|
||||||
|
|
||||||
|
|
|
@ -49,6 +49,7 @@ enum
|
||||||
PROP_N_PREROLL_FRAMES,
|
PROP_N_PREROLL_FRAMES,
|
||||||
PROP_MIN_BUFFERED_FRAMES,
|
PROP_MIN_BUFFERED_FRAMES,
|
||||||
PROP_MAX_BUFFERED_FRAMES,
|
PROP_MAX_BUFFERED_FRAMES,
|
||||||
|
PROP_AUTO_RESTART
|
||||||
};
|
};
|
||||||
|
|
||||||
#define DEFAULT_MODE bmdModeUnknown
|
#define DEFAULT_MODE bmdModeUnknown
|
||||||
|
@ -65,6 +66,7 @@ enum
|
||||||
#define DEFAULT_N_PREROLL_FRAMES 7
|
#define DEFAULT_N_PREROLL_FRAMES 7
|
||||||
#define DEFAULT_MIN_BUFFERED_FRAMES 3
|
#define DEFAULT_MIN_BUFFERED_FRAMES 3
|
||||||
#define DEFAULT_MAX_BUFFERED_FRAMES 14
|
#define DEFAULT_MAX_BUFFERED_FRAMES 14
|
||||||
|
#define DEFAULT_AUTO_RESTART FALSE
|
||||||
|
|
||||||
struct GstDeckLink2SinkPrivate
|
struct GstDeckLink2SinkPrivate
|
||||||
{
|
{
|
||||||
|
@ -88,6 +90,7 @@ struct _GstDeckLink2Sink
|
||||||
|
|
||||||
GstBufferPool *fallback_pool;
|
GstBufferPool *fallback_pool;
|
||||||
IDeckLinkVideoFrame *prepared_frame;
|
IDeckLinkVideoFrame *prepared_frame;
|
||||||
|
BMDVideoOutputFlags output_flags;
|
||||||
|
|
||||||
/* properties */
|
/* properties */
|
||||||
BMDDisplayMode display_mode;
|
BMDDisplayMode display_mode;
|
||||||
|
@ -104,6 +107,7 @@ struct _GstDeckLink2Sink
|
||||||
guint n_preroll_frames;
|
guint n_preroll_frames;
|
||||||
guint min_buffered_frames;
|
guint min_buffered_frames;
|
||||||
guint max_buffered_frames;
|
guint max_buffered_frames;
|
||||||
|
gboolean auto_restart;
|
||||||
};
|
};
|
||||||
|
|
||||||
static void gst_decklink2_sink_set_property (GObject * object,
|
static void gst_decklink2_sink_set_property (GObject * object,
|
||||||
|
@ -225,6 +229,12 @@ gst_decklink2_sink_class_init (GstDeckLink2SinkClass * klass)
|
||||||
"Max number of frames to buffer before dropping",
|
"Max number of frames to buffer before dropping",
|
||||||
0, 16, DEFAULT_MAX_BUFFERED_FRAMES, param_flags));
|
0, 16, DEFAULT_MAX_BUFFERED_FRAMES, param_flags));
|
||||||
|
|
||||||
|
g_object_class_install_property (object_class, PROP_AUTO_RESTART,
|
||||||
|
g_param_spec_boolean ("auto-restart", "Auto Restart",
|
||||||
|
"Restart streaming when frame is dropped, late or underrun happens",
|
||||||
|
DEFAULT_AUTO_RESTART,
|
||||||
|
(GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
|
||||||
|
|
||||||
GstCaps *templ_caps = gst_decklink2_get_default_template_caps ();
|
GstCaps *templ_caps = gst_decklink2_get_default_template_caps ();
|
||||||
templ_caps = gst_caps_make_writable (templ_caps);
|
templ_caps = gst_caps_make_writable (templ_caps);
|
||||||
|
|
||||||
|
@ -296,6 +306,7 @@ gst_decklink2_sink_init (GstDeckLink2Sink * self)
|
||||||
self->n_preroll_frames = DEFAULT_N_PREROLL_FRAMES;
|
self->n_preroll_frames = DEFAULT_N_PREROLL_FRAMES;
|
||||||
self->min_buffered_frames = DEFAULT_MIN_BUFFERED_FRAMES;
|
self->min_buffered_frames = DEFAULT_MIN_BUFFERED_FRAMES;
|
||||||
self->max_buffered_frames = DEFAULT_MAX_BUFFERED_FRAMES;
|
self->max_buffered_frames = DEFAULT_MAX_BUFFERED_FRAMES;
|
||||||
|
self->auto_restart = DEFAULT_AUTO_RESTART;
|
||||||
|
|
||||||
self->priv = new GstDeckLink2SinkPrivate ();
|
self->priv = new GstDeckLink2SinkPrivate ();
|
||||||
}
|
}
|
||||||
|
@ -362,6 +373,9 @@ gst_decklink2_sink_set_property (GObject * object, guint prop_id,
|
||||||
case PROP_MAX_BUFFERED_FRAMES:
|
case PROP_MAX_BUFFERED_FRAMES:
|
||||||
self->max_buffered_frames = g_value_get_int (value);
|
self->max_buffered_frames = g_value_get_int (value);
|
||||||
break;
|
break;
|
||||||
|
case PROP_AUTO_RESTART:
|
||||||
|
self->auto_restart = g_value_get_boolean (value);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||||
break;
|
break;
|
||||||
|
@ -419,6 +433,9 @@ gst_decklink2_sink_get_property (GObject * object, guint prop_id,
|
||||||
case PROP_MAX_BUFFERED_FRAMES:
|
case PROP_MAX_BUFFERED_FRAMES:
|
||||||
g_value_set_int (value, self->max_buffered_frames);
|
g_value_set_int (value, self->max_buffered_frames);
|
||||||
break;
|
break;
|
||||||
|
case PROP_AUTO_RESTART:
|
||||||
|
g_value_set_boolean (value, self->auto_restart);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||||
break;
|
break;
|
||||||
|
@ -592,16 +609,17 @@ gst_decklink2_sink_set_caps (GstBaseSink * sink, GstCaps * caps)
|
||||||
* Note that this flag will have no effect in practice if the video stream
|
* Note that this flag will have no effect in practice if the video stream
|
||||||
* does not contain timecode metadata.
|
* does not contain timecode metadata.
|
||||||
*/
|
*/
|
||||||
BMDVideoOutputFlags output_flags;
|
|
||||||
if (self->timecode_format == bmdTimecodeVITC ||
|
if (self->timecode_format == bmdTimecodeVITC ||
|
||||||
self->timecode_format == bmdTimecodeVITCField2) {
|
self->timecode_format == bmdTimecodeVITCField2) {
|
||||||
output_flags = bmdVideoOutputVITC;
|
self->output_flags = bmdVideoOutputVITC;
|
||||||
} else {
|
} else {
|
||||||
output_flags = bmdVideoOutputRP188;
|
self->output_flags = bmdVideoOutputRP188;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (self->caption_line > 0 || self->afd_bar_line > 0)
|
if (self->caption_line > 0 || self->afd_bar_line > 0) {
|
||||||
output_flags = (BMDVideoOutputFlags) (output_flags | bmdVideoOutputVANC);
|
self->output_flags = (BMDVideoOutputFlags)
|
||||||
|
(self->output_flags | bmdVideoOutputVANC);
|
||||||
|
}
|
||||||
|
|
||||||
GST_DEBUG_OBJECT (self, "Configuring output, mode %" GST_FOURCC_FORMAT
|
GST_DEBUG_OBJECT (self, "Configuring output, mode %" GST_FOURCC_FORMAT
|
||||||
", audio-sample-type %d, audio-channles %d",
|
", audio-sample-type %d, audio-channles %d",
|
||||||
|
@ -610,8 +628,8 @@ gst_decklink2_sink_set_caps (GstBaseSink * sink, GstCaps * caps)
|
||||||
|
|
||||||
hr = gst_decklink2_output_configure (self->output, self->n_preroll_frames,
|
hr = gst_decklink2_output_configure (self->output, self->n_preroll_frames,
|
||||||
self->min_buffered_frames, self->max_buffered_frames,
|
self->min_buffered_frames, self->max_buffered_frames,
|
||||||
&self->selected_mode, output_flags, self->profile_id, self->keyer_mode,
|
&self->selected_mode, self->output_flags, self->profile_id,
|
||||||
(guint8) self->keyer_level, self->mapping_format,
|
self->keyer_mode, (guint8) self->keyer_level, self->mapping_format,
|
||||||
self->audio_sample_type, self->audio_channels);
|
self->audio_sample_type, self->audio_channels);
|
||||||
if (hr != S_OK) {
|
if (hr != S_OK) {
|
||||||
GST_ERROR_OBJECT (self, "Couldn't configure output");
|
GST_ERROR_OBJECT (self, "Couldn't configure output");
|
||||||
|
@ -858,6 +876,7 @@ gst_decklink2_sink_render (GstBaseSink * sink, GstBuffer * buffer)
|
||||||
GstMapInfo info;
|
GstMapInfo info;
|
||||||
guint8 *audio_data = NULL;
|
guint8 *audio_data = NULL;
|
||||||
gsize audio_data_size = 0;
|
gsize audio_data_size = 0;
|
||||||
|
guint64 drop_count, late_count, underrun_count;
|
||||||
|
|
||||||
if (!self->prepared_frame) {
|
if (!self->prepared_frame) {
|
||||||
GST_ERROR_OBJECT (self, "No prepared frame");
|
GST_ERROR_OBJECT (self, "No prepared frame");
|
||||||
|
@ -886,7 +905,8 @@ gst_decklink2_sink_render (GstBaseSink * sink, GstBuffer * buffer)
|
||||||
G_GSIZE_FORMAT, self->prepared_frame, audio_data_size);
|
G_GSIZE_FORMAT, self->prepared_frame, audio_data_size);
|
||||||
|
|
||||||
hr = gst_decklink2_output_schedule_stream (self->output,
|
hr = gst_decklink2_output_schedule_stream (self->output,
|
||||||
self->prepared_frame, audio_data, audio_data_size);
|
self->prepared_frame, audio_data, audio_data_size, &drop_count,
|
||||||
|
&late_count, &underrun_count);
|
||||||
|
|
||||||
if (audio_buf)
|
if (audio_buf)
|
||||||
gst_buffer_unmap (audio_buf, &info);
|
gst_buffer_unmap (audio_buf, &info);
|
||||||
|
@ -897,5 +917,22 @@ gst_decklink2_sink_render (GstBaseSink * sink, GstBuffer * buffer)
|
||||||
return GST_FLOW_ERROR;
|
return GST_FLOW_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (self->auto_restart && (drop_count || late_count || underrun_count)) {
|
||||||
|
GST_WARNING_OBJECT (self, "Restart output, drop count: %" G_GUINT64_FORMAT
|
||||||
|
", late cout: %" G_GUINT64_FORMAT ", %" G_GUINT64_FORMAT,
|
||||||
|
drop_count, late_count, underrun_count);
|
||||||
|
|
||||||
|
hr = gst_decklink2_output_configure (self->output, self->n_preroll_frames,
|
||||||
|
self->min_buffered_frames, self->max_buffered_frames,
|
||||||
|
&self->selected_mode, self->output_flags, self->profile_id,
|
||||||
|
self->keyer_mode, (guint8) self->keyer_level, self->mapping_format,
|
||||||
|
self->audio_sample_type, self->audio_channels);
|
||||||
|
|
||||||
|
if (hr != S_OK) {
|
||||||
|
GST_ERROR_OBJECT (self, "Couldn't configure output");
|
||||||
|
return GST_FLOW_OK;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return GST_FLOW_OK;
|
return GST_FLOW_OK;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue