decklinkvideosrc: fix decklinkvideosrc becomes unrecoverable if it fails to start streaming
See #2611 When transitioning to PLAYING we call IDeckLinkInput::StartStreams() (via GstDecklinkInput::start_streams). This can fail for various reasons, including if the hardware device is already in use. Previously, we logged StartStreams() failure as an element error but otherwise continued to successfully transition to PLAYING. Now, if StartStreams() returns an error we fail the state change. If StartStreams() fails then a subsequent call to StopStreams(), when transitioning PAUSED to READY, will also fail. Previously, if StopStreams() failed then the state change also failed. Unfortunately this prevented the element from later being disposed, which in turn meant the associated hardware resources was never freed. Consequently, if a decklinkvideosrc failed to transition PAUSED to READY, then the associated hardware device could not subsequently be used by any other decklinkvideosrc. Now, we log an element error if StopStreams() fails but otherwise consider the state change to have succeeded. This means that the element can be disposed and the associated hardware resource released. Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/9578>
This commit is contained in:
parent
6d53576194
commit
aee742223c
@ -596,7 +596,7 @@ struct _GstDecklinkInput {
|
||||
gboolean audio_enabled;
|
||||
GstElement *videosrc;
|
||||
gboolean video_enabled;
|
||||
void (*start_streams) (GstElement *videosrc);
|
||||
gboolean (*start_streams) (GstElement *videosrc);
|
||||
};
|
||||
|
||||
GstDecklinkOutput * gst_decklink_acquire_nth_output (gint n, gint64 persistent_id, GstElement * sink, gboolean is_audio);
|
||||
|
@ -247,7 +247,7 @@ static gboolean gst_decklink_video_src_close (GstDecklinkVideoSrc * self);
|
||||
|
||||
static gboolean gst_decklink_video_src_stop (GstDecklinkVideoSrc * self);
|
||||
|
||||
static void gst_decklink_video_src_start_streams (GstElement * element);
|
||||
static gboolean gst_decklink_video_src_start_streams (GstElement * element);
|
||||
|
||||
#define parent_class gst_decklink_video_src_parent_class
|
||||
G_DEFINE_TYPE (GstDecklinkVideoSrc, gst_decklink_video_src, GST_TYPE_PUSH_SRC);
|
||||
@ -693,8 +693,12 @@ gst_decklink_video_src_start (GstDecklinkVideoSrc * self)
|
||||
g_mutex_lock (&self->input->lock);
|
||||
self->input->mode = mode;
|
||||
self->input->video_enabled = TRUE;
|
||||
if (self->input->start_streams)
|
||||
self->input->start_streams (self->input->videosrc);
|
||||
if (self->input->start_streams) {
|
||||
if (!self->input->start_streams (self->input->videosrc)) {
|
||||
g_mutex_unlock (&self->input->lock);
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
g_mutex_unlock (&self->input->lock);
|
||||
|
||||
self->skipped_last = 0;
|
||||
@ -1897,7 +1901,11 @@ gst_decklink_video_src_stop (GstDecklinkVideoSrc * self)
|
||||
self->input->video_enabled = FALSE;
|
||||
g_mutex_unlock (&self->input->lock);
|
||||
|
||||
self->input->input->DisableVideoInput ();
|
||||
const HRESULT res = self->input->input->DisableVideoInput ();
|
||||
if (FAILED(res)) {
|
||||
GST_ELEMENT_ERROR (self, STREAM, FAILED,
|
||||
(NULL), ("Failed to disable video input: 0x%08lx", (unsigned long) res));
|
||||
}
|
||||
}
|
||||
|
||||
if (self->vbiparser) {
|
||||
@ -1910,11 +1918,10 @@ gst_decklink_video_src_stop (GstDecklinkVideoSrc * self)
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
static gboolean
|
||||
gst_decklink_video_src_start_streams (GstElement * element)
|
||||
{
|
||||
GstDecklinkVideoSrc *self = GST_DECKLINK_VIDEO_SRC_CAST (element);
|
||||
HRESULT res;
|
||||
|
||||
if (self->input->video_enabled && (!self->input->audiosrc
|
||||
|| self->input->audio_enabled)
|
||||
@ -1937,15 +1944,18 @@ gst_decklink_video_src_start_streams (GstElement * element)
|
||||
self->next_time_mapping.num = 1;
|
||||
self->next_time_mapping.den = 1;
|
||||
g_mutex_unlock (&self->lock);
|
||||
res = self->input->input->StartStreams ();
|
||||
if (res != S_OK) {
|
||||
|
||||
const HRESULT res = self->input->input->StartStreams ();
|
||||
if (FAILED(res)) {
|
||||
GST_ELEMENT_ERROR (self, STREAM, FAILED,
|
||||
(NULL), ("Failed to start streams: 0x%08lx", (unsigned long) res));
|
||||
return;
|
||||
return FALSE;
|
||||
}
|
||||
} else {
|
||||
GST_DEBUG_OBJECT (self, "Not starting streams yet");
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static GstStateChangeReturn
|
||||
@ -1997,19 +2007,24 @@ gst_decklink_video_src_change_state (GstElement * element,
|
||||
HRESULT res;
|
||||
|
||||
GST_DEBUG_OBJECT (self, "Stopping streams");
|
||||
|
||||
// Even if StopStreams() fails we consider the element state change to
|
||||
// have succeeded. This can happen if the earlier call to StartStreams()
|
||||
// failed. Downward state changes should never fail since doing so would
|
||||
// prevent the element being disposed (and so prevent the associated
|
||||
// hardware resource being released).
|
||||
res = self->input->input->StopStreams ();
|
||||
if (res != S_OK) {
|
||||
GST_ELEMENT_ERROR (self, STREAM, FAILED,
|
||||
(NULL), ("Failed to stop streams: 0x%08lx", (unsigned long) res));
|
||||
ret = GST_STATE_CHANGE_FAILURE;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case GST_STATE_CHANGE_PAUSED_TO_PLAYING:{
|
||||
g_mutex_lock (&self->input->lock);
|
||||
if (self->input->start_streams)
|
||||
self->input->start_streams (self->input->videosrc);
|
||||
if (self->input->start_streams) {
|
||||
if (!self->input->start_streams (self->input->videosrc))
|
||||
ret = GST_STATE_CHANGE_FAILURE;
|
||||
}
|
||||
g_mutex_unlock (&self->input->lock);
|
||||
|
||||
break;
|
||||
|
Loading…
x
Reference in New Issue
Block a user