mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-14 05:12:09 +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);
|
||||
if (cur_size > 0) {
|
||||
memmove (&buffer_[0] + silence_length_in_bytes, &buffer_[0],
|
||||
cur_size);
|
||||
memmove (&buffer_[0] + silence_length_in_bytes, &buffer_[0], cur_size);
|
||||
}
|
||||
|
||||
gst_audio_format_info_fill_silence (info_.finfo, &buffer_[0],
|
||||
|
@ -234,12 +233,16 @@ struct _GstDeckLink2Output
|
|||
|
||||
gboolean configured;
|
||||
gboolean prerolled;
|
||||
guint64 late_count;
|
||||
guint64 drop_count;
|
||||
guint64 underrun_count;
|
||||
};
|
||||
|
||||
static void gst_decklink2_output_dispose (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_completed (GstDeckLink2Output * self);
|
||||
static void gst_decklink2_output_on_completed (GstDeckLink2Output * self,
|
||||
BMDOutputFrameCompletionResult result);
|
||||
|
||||
#define gst_decklink2_output_parent_class parent_class
|
||||
G_DEFINE_TYPE (GstDeckLink2Output, gst_decklink2_output, GST_TYPE_OBJECT);
|
||||
|
@ -308,8 +311,9 @@ public:
|
|||
}
|
||||
|
||||
if (result == bmdOutputFrameCompleted ||
|
||||
result == bmdOutputFrameDisplayedLate) {
|
||||
gst_decklink2_output_on_completed (output_);
|
||||
result == bmdOutputFrameDisplayedLate ||
|
||||
result == bmdOutputFrameDropped) {
|
||||
gst_decklink2_output_on_completed (output_, result);
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
|
@ -1013,7 +1017,8 @@ gst_decklink2_output_schedule_video_internal (GstDeckLink2Output * self,
|
|||
|
||||
HRESULT
|
||||
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;
|
||||
HRESULT hr;
|
||||
|
@ -1022,6 +1027,10 @@ gst_decklink2_output_schedule_stream (GstDeckLink2Output * output,
|
|||
|
||||
g_assert (output->configured);
|
||||
|
||||
*drop_count = 0;
|
||||
*late_count = 0;
|
||||
*underrun_count = 0;
|
||||
|
||||
hr = gst_decklink2_output_is_running (output, &active);
|
||||
if (!gst_decklink2_result (hr)) {
|
||||
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);
|
||||
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
|
||||
|
@ -1094,6 +1108,10 @@ gst_decklink2_output_stop_internal (GstDeckLink2Output * self)
|
|||
gst_decklink2_output_disable_video (self);
|
||||
gst_decklink2_output_set_video_callback (self, NULL);
|
||||
self->configured = FALSE;
|
||||
self->prerolled = FALSE;
|
||||
self->late_count = 0;
|
||||
self->drop_count = 0;
|
||||
self->underrun_count = 0;
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
@ -1934,6 +1952,9 @@ gst_decklink2_output_configure (GstDeckLink2Output * output,
|
|||
output->cdp_hdr_sequence_cntr = 0;
|
||||
output->configured = TRUE;
|
||||
output->pts = 0;
|
||||
output->drop_count = 0;
|
||||
output->late_count = 0;
|
||||
output->underrun_count = 0;
|
||||
|
||||
return S_OK;
|
||||
|
||||
|
@ -1951,13 +1972,19 @@ gst_decklink2_output_on_stopped (GstDeckLink2Output * self)
|
|||
}
|
||||
|
||||
static void
|
||||
gst_decklink2_output_on_completed (GstDeckLink2Output * self)
|
||||
gst_decklink2_output_on_completed (GstDeckLink2Output * self,
|
||||
BMDOutputFrameCompletionResult result)
|
||||
{
|
||||
GstDeckLink2OutputPrivate *priv = self->priv;
|
||||
dlbool_t active;
|
||||
guint32 count = 0;
|
||||
|
||||
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)
|
||||
return;
|
||||
|
||||
|
@ -1966,6 +1993,7 @@ gst_decklink2_output_on_completed (GstDeckLink2Output * self)
|
|||
hr = gst_decklink2_output_get_num_bufferred (self, &count);
|
||||
if (gst_decklink2_result (hr) && count <= self->min_buffered) {
|
||||
GST_WARNING_OBJECT (self, "Underrun, buffered count %u", count);
|
||||
self->underrun_count++;
|
||||
priv->audio_buf.PrependSilence ();
|
||||
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,
|
||||
IDeckLinkVideoFrame * frame,
|
||||
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);
|
||||
|
||||
|
|
|
@ -49,6 +49,7 @@ enum
|
|||
PROP_N_PREROLL_FRAMES,
|
||||
PROP_MIN_BUFFERED_FRAMES,
|
||||
PROP_MAX_BUFFERED_FRAMES,
|
||||
PROP_AUTO_RESTART
|
||||
};
|
||||
|
||||
#define DEFAULT_MODE bmdModeUnknown
|
||||
|
@ -65,6 +66,7 @@ enum
|
|||
#define DEFAULT_N_PREROLL_FRAMES 7
|
||||
#define DEFAULT_MIN_BUFFERED_FRAMES 3
|
||||
#define DEFAULT_MAX_BUFFERED_FRAMES 14
|
||||
#define DEFAULT_AUTO_RESTART FALSE
|
||||
|
||||
struct GstDeckLink2SinkPrivate
|
||||
{
|
||||
|
@ -88,6 +90,7 @@ struct _GstDeckLink2Sink
|
|||
|
||||
GstBufferPool *fallback_pool;
|
||||
IDeckLinkVideoFrame *prepared_frame;
|
||||
BMDVideoOutputFlags output_flags;
|
||||
|
||||
/* properties */
|
||||
BMDDisplayMode display_mode;
|
||||
|
@ -104,6 +107,7 @@ struct _GstDeckLink2Sink
|
|||
guint n_preroll_frames;
|
||||
guint min_buffered_frames;
|
||||
guint max_buffered_frames;
|
||||
gboolean auto_restart;
|
||||
};
|
||||
|
||||
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",
|
||||
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 ();
|
||||
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->min_buffered_frames = DEFAULT_MIN_BUFFERED_FRAMES;
|
||||
self->max_buffered_frames = DEFAULT_MAX_BUFFERED_FRAMES;
|
||||
self->auto_restart = DEFAULT_AUTO_RESTART;
|
||||
|
||||
self->priv = new GstDeckLink2SinkPrivate ();
|
||||
}
|
||||
|
@ -362,6 +373,9 @@ gst_decklink2_sink_set_property (GObject * object, guint prop_id,
|
|||
case PROP_MAX_BUFFERED_FRAMES:
|
||||
self->max_buffered_frames = g_value_get_int (value);
|
||||
break;
|
||||
case PROP_AUTO_RESTART:
|
||||
self->auto_restart = g_value_get_boolean (value);
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
|
@ -419,6 +433,9 @@ gst_decklink2_sink_get_property (GObject * object, guint prop_id,
|
|||
case PROP_MAX_BUFFERED_FRAMES:
|
||||
g_value_set_int (value, self->max_buffered_frames);
|
||||
break;
|
||||
case PROP_AUTO_RESTART:
|
||||
g_value_set_boolean (value, self->auto_restart);
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
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
|
||||
* does not contain timecode metadata.
|
||||
*/
|
||||
BMDVideoOutputFlags output_flags;
|
||||
if (self->timecode_format == bmdTimecodeVITC ||
|
||||
self->timecode_format == bmdTimecodeVITCField2) {
|
||||
output_flags = bmdVideoOutputVITC;
|
||||
self->output_flags = bmdVideoOutputVITC;
|
||||
} else {
|
||||
output_flags = bmdVideoOutputRP188;
|
||||
self->output_flags = bmdVideoOutputRP188;
|
||||
}
|
||||
|
||||
if (self->caption_line > 0 || self->afd_bar_line > 0)
|
||||
output_flags = (BMDVideoOutputFlags) (output_flags | bmdVideoOutputVANC);
|
||||
if (self->caption_line > 0 || self->afd_bar_line > 0) {
|
||||
self->output_flags = (BMDVideoOutputFlags)
|
||||
(self->output_flags | bmdVideoOutputVANC);
|
||||
}
|
||||
|
||||
GST_DEBUG_OBJECT (self, "Configuring output, mode %" GST_FOURCC_FORMAT
|
||||
", 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,
|
||||
self->min_buffered_frames, self->max_buffered_frames,
|
||||
&self->selected_mode, output_flags, self->profile_id, self->keyer_mode,
|
||||
(guint8) self->keyer_level, self->mapping_format,
|
||||
&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");
|
||||
|
@ -858,6 +876,7 @@ gst_decklink2_sink_render (GstBaseSink * sink, GstBuffer * buffer)
|
|||
GstMapInfo info;
|
||||
guint8 *audio_data = NULL;
|
||||
gsize audio_data_size = 0;
|
||||
guint64 drop_count, late_count, underrun_count;
|
||||
|
||||
if (!self->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);
|
||||
|
||||
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)
|
||||
gst_buffer_unmap (audio_buf, &info);
|
||||
|
@ -897,5 +917,22 @@ gst_decklink2_sink_render (GstBaseSink * sink, GstBuffer * buffer)
|
|||
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;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue