From 142e571c28fe5c3806843bfdf9929cc267739aba Mon Sep 17 00:00:00 2001 From: Thibault Saunier Date: Thu, 19 Nov 2020 17:55:10 -0300 Subject: [PATCH] transcode: Port to encodebin2 This allows supporting muxing sinks like hlssink2 or splitmux --- docs/plugins/gst_plugins_cache.json | 5 + gst/transcode/gsttranscodebin.c | 65 ++++++---- gst/transcode/gsturitranscodebin.c | 185 +++++++++++++++++++++------- 3 files changed, 189 insertions(+), 66 deletions(-) diff --git a/docs/plugins/gst_plugins_cache.json b/docs/plugins/gst_plugins_cache.json index eac56a4ad7..ce17f55f1c 100644 --- a/docs/plugins/gst_plugins_cache.json +++ b/docs/plugins/gst_plugins_cache.json @@ -220692,6 +220692,11 @@ "caps": "ANY", "direction": "src", "presence": "always" + }, + "src_%%u": { + "caps": "ANY", + "direction": "src", + "presence": "sometimes" } }, "properties": { diff --git a/gst/transcode/gsttranscodebin.c b/gst/transcode/gsttranscodebin.c index 4ec8b21acc..b6df25d8ac 100644 --- a/gst/transcode/gsttranscodebin.c +++ b/gst/transcode/gsttranscodebin.c @@ -50,10 +50,22 @@ GST_STATIC_PAD_TEMPLATE ("sink", GST_PAD_ALWAYS, GST_STATIC_CAPS_ANY); +/** + * GstTranscodeBin!src_%u: + * + * The sometimes source pad, it will be exposed depending on the + * #transcodebin:profile in use. + * + * Note: in GStreamer 1.18 it was a static + * srcpad but in the the 1.20 cycle it was decided that we should make it a + * sometimes pad as part of the development of #encodebin2. + * + * Since: 1.20 + */ static GstStaticPadTemplate transcode_bin_src_template = -GST_STATIC_PAD_TEMPLATE ("src", +GST_STATIC_PAD_TEMPLATE ("src_%u", GST_PAD_SRC, - GST_PAD_ALWAYS, + GST_PAD_SOMETIMES, GST_STATIC_CAPS_ANY); typedef struct @@ -92,7 +104,6 @@ typedef struct GstEncodingProfile *profile; gboolean avoid_reencoding; GstPad *sinkpad; - GstPad *srcpad; GstElement *audio_filter; GstElement *video_filter; @@ -364,33 +375,47 @@ decodebin_pad_added_cb (GstElement * decodebin, GstPad * pad, (GstPadProbeCallback) wait_stream_start_probe, self, NULL); } +static void +encodebin_pad_added_cb (GstElement * encodebin, GstPad * pad, GstElement * self) +{ + GstPadTemplate *template; + GstPad *new_pad; + gchar *name; + + if (!GST_PAD_IS_SRC (pad)) + return; + + template = gst_element_get_pad_template (self, "src_%u"); + + GST_OBJECT_LOCK (self); + name = g_strdup_printf ("src_%u", GST_ELEMENT (self)->numsrcpads); + GST_OBJECT_UNLOCK (self); + new_pad = gst_ghost_pad_new_from_template (name, pad, template); + g_free (name); + GST_DEBUG_OBJECT (self, "Encodebin exposed srcpad: %" GST_PTR_FORMAT, pad); + + gst_element_add_pad (self, new_pad); +} + static gboolean make_encodebin (GstTranscodeBin * self) { - GstPad *pad; GST_INFO_OBJECT (self, "making new encodebin"); if (!self->profile) goto no_profile; - self->encodebin = gst_element_factory_make ("encodebin", NULL); + self->encodebin = gst_element_factory_make ("encodebin2", NULL); if (!self->encodebin) goto no_encodebin; gst_bin_add (GST_BIN (self), self->encodebin); + + g_signal_connect (self->encodebin, "pad-added", + G_CALLBACK (encodebin_pad_added_cb), self); + g_object_set (self->encodebin, "profile", self->profile, NULL); - pad = gst_element_get_static_pad (self->encodebin, "src"); - if (!gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (self->srcpad), pad)) { - - gst_object_unref (pad); - GST_ERROR_OBJECT (self, "Could not ghost %" GST_PTR_FORMAT " srcpad", - self->encodebin); - - return FALSE; - } - gst_object_unref (pad); - return gst_element_sync_state_with_parent (self->encodebin); /* ERRORS */ @@ -945,14 +970,6 @@ gst_transcode_bin_init (GstTranscodeBin * self) gst_object_unref (pad_tmpl); - pad_tmpl = gst_static_pad_template_get (&transcode_bin_src_template); - - self->srcpad = gst_ghost_pad_new_no_target_from_template ("src", pad_tmpl); - gst_pad_set_active (self->srcpad, TRUE); - gst_element_add_pad (GST_ELEMENT (self), self->srcpad); - - gst_object_unref (pad_tmpl); - self->transcoding_streams = g_ptr_array_new_with_free_func ((GDestroyNotify) transcoding_stream_free); diff --git a/gst/transcode/gsturitranscodebin.c b/gst/transcode/gsturitranscodebin.c index 55b3634c0b..fc21df48dc 100644 --- a/gst/transcode/gsturitranscodebin.c +++ b/gst/transcode/gsturitranscodebin.c @@ -103,68 +103,59 @@ post_missing_plugin_error (GstElement * dec, const gchar * element_name) } /* *INDENT-ON* */ -static gboolean -make_transcodebin (GstUriTranscodeBin * self) -{ - GST_INFO_OBJECT (self, "making new transcodebin"); - - self->transcodebin = gst_element_factory_make ("transcodebin", NULL); - if (!self->transcodebin) - goto no_transcodebin; - - g_object_set (self->transcodebin, "profile", self->profile, - "video-filter", self->video_filter, - "audio-filter", self->audio_filter, - "avoid-reencoding", self->avoid_reencoding, NULL); - - gst_bin_add (GST_BIN (self), self->transcodebin); - if (!gst_element_link (self->transcodebin, self->sink)) { - GST_ERROR ("Could not link transcodbin"); - return FALSE; - } - - return TRUE; - - /* ERRORS */ -no_transcodebin: - { - post_missing_plugin_error (GST_ELEMENT_CAST (self), "transcodebin"); - - GST_ELEMENT_ERROR (self, CORE, MISSING_PLUGIN, (NULL), - ("No transcodebin element, check your installation")); - - return FALSE; - } -} - static gboolean make_dest (GstUriTranscodeBin * self) { GError *err = NULL; + GST_OBJECT_LOCK (self); + if (!self->dest_uri) { + GST_INFO_OBJECT (self, "Sink already set: %" GST_PTR_FORMAT, self->sink); + goto ok_unlock; + } + + if (!self->dest_uri) + goto ok_unlock; + if (!gst_uri_is_valid (self->dest_uri)) - goto invalid_uri; + goto invalid_uri_unlock; self->sink = gst_element_make_from_uri (GST_URI_SINK, self->dest_uri, "sink", &err); if (!self->sink) - goto no_sink; + goto no_sink_unlock; + GST_OBJECT_UNLOCK (self); gst_bin_add (GST_BIN (self), self->sink); g_object_set (self->sink, "sync", TRUE, "max-lateness", GST_CLOCK_TIME_NONE, NULL); + return TRUE; -invalid_uri: +ok_unlock: + GST_OBJECT_UNLOCK (self); + return TRUE; + +invalid_uri_unlock: { + GST_OBJECT_UNLOCK (self); GST_ELEMENT_ERROR (self, RESOURCE, NOT_FOUND, ("Invalid URI \"%s\".", self->dest_uri), (NULL)); g_clear_error (&err); return FALSE; } -no_sink: +invalid_uri: { + GST_ELEMENT_ERROR (self, RESOURCE, NOT_FOUND, + ("Invalid URI \"%s\".", self->source_uri), (NULL)); + g_clear_error (&err); + return FALSE; + } + +no_sink_unlock: + { + GST_OBJECT_UNLOCK (self); /* whoops, could not create the source element, dig a little deeper to * figure out what might be wrong. */ if (err != NULL && err->code == GST_URI_ERROR_UNSUPPORTED_PROTOCOL) { @@ -193,6 +184,69 @@ no_sink: } } +static void +transcodebin_pad_added_cb (GstElement * transcodebin, GstPad * pad, + GstUriTranscodeBin * self) +{ + + GstPad *sinkpad; + + if (GST_PAD_IS_SINK (pad)) + return; + + make_dest (self); + if (!self->sink) { + GST_ELEMENT_ERROR (self, CORE, FAILED, (NULL), ("No sink configured.")); + return; + } + + sinkpad = gst_element_get_static_pad (self->sink, "sink"); + if (!sinkpad) { + GST_ELEMENT_ERROR (self, CORE, FAILED, (NULL), ("Sink has not sinkpad?!")); + return; + } + + if (gst_pad_link (pad, sinkpad) != GST_PAD_LINK_OK) { + GST_ERROR_OBJECT (self, + "Could not link %" GST_PTR_FORMAT " and %" GST_PTR_FORMAT, pad, + sinkpad); + /* Let `pad unlinked` error pop up later */ + } +} + +static gboolean +make_transcodebin (GstUriTranscodeBin * self) +{ + GST_INFO_OBJECT (self, "making new transcodebin"); + + self->transcodebin = gst_element_factory_make ("transcodebin", NULL); + if (!self->transcodebin) + goto no_transcodebin; + + g_signal_connect (self->transcodebin, "pad-added", + G_CALLBACK (transcodebin_pad_added_cb), self); + + g_object_set (self->transcodebin, "profile", self->profile, + "video-filter", self->video_filter, + "audio-filter", self->audio_filter, + "avoid-reencoding", self->avoid_reencoding, NULL); + + gst_bin_add (GST_BIN (self), self->transcodebin); + + return TRUE; + + /* ERRORS */ +no_transcodebin: + { + post_missing_plugin_error (GST_ELEMENT_CAST (self), "transcodebin"); + + GST_ELEMENT_ERROR (self, CORE, MISSING_PLUGIN, (NULL), + ("No transcodebin element, check your installation")); + + return FALSE; + } +} + static void src_pad_added_cb (GstElement * src, GstPad * pad, GstUriTranscodeBin * self) { @@ -297,6 +351,52 @@ remove_all_children (GstUriTranscodeBin * self) } } +static void +set_location_on_muxer_if_sink (GstUriTranscodeBin * self, GstElement * child) +{ + GstElementFactory *factory = gst_element_get_factory (child); + + if (!factory) + return; + + if (!self->dest_uri) + return; + + /* Set out dest URI as location for muxer sinks. */ + if (!gst_element_factory_list_is_type (factory, + GST_ELEMENT_FACTORY_TYPE_MUXER) || + !gst_element_factory_list_is_type (factory, + GST_ELEMENT_FACTORY_TYPE_SINK)) { + + return; + } + + if (!g_object_class_find_property (G_OBJECT_GET_CLASS (child), "location")) + return; + + if (!gst_uri_has_protocol (self->dest_uri, "file")) { + GST_ELEMENT_ERROR (self, RESOURCE, SETTINGS, + ("Trying to use a not local file with a muxing sink which is not" + " supported."), (NULL)); + return; + } + + GST_OBJECT_FLAG_SET (self->transcodebin, GST_ELEMENT_FLAG_SINK); + g_object_set (child, "location", &self->dest_uri[strlen ("file://")], NULL); + GST_DEBUG_OBJECT (self, "Setting location: %s", + &self->dest_uri[strlen ("file://")]); +} + +static void +deep_element_added (GstBin * bin, GstBin * sub_bin, GstElement * child) +{ + GstUriTranscodeBin *self = GST_URI_TRANSCODE_BIN (bin); + + set_location_on_muxer_if_sink (self, child); + + GST_BIN_CLASS (parent_class)->deep_element_added (bin, sub_bin, child); +} + static GstStateChangeReturn gst_uri_transcode_bin_change_state (GstElement * element, GstStateChange transition) @@ -307,16 +407,13 @@ gst_uri_transcode_bin_change_state (GstElement * element, switch (transition) { case GST_STATE_CHANGE_READY_TO_PAUSED: - if (!make_dest (self)) - goto setup_failed; - if (!make_transcodebin (self)) goto setup_failed; if (!make_source (self)) goto setup_failed; - if (gst_element_set_state (self->sink, + if (self->sink && gst_element_set_state (self->sink, GST_STATE_PAUSED) == GST_STATE_CHANGE_FAILURE) { GST_ERROR_OBJECT (self, "Could not set %" GST_PTR_FORMAT " state to PAUSED", self->sink); @@ -497,6 +594,7 @@ gst_uri_transcode_bin_class_init (GstUriTranscodeBinClass * klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); GstElementClass *gstelement_klass; + GstBinClass *gstbin_klass; object_class->get_property = gst_uri_transcode_bin_get_property; object_class->set_property = gst_uri_transcode_bin_set_property; @@ -507,6 +605,9 @@ gst_uri_transcode_bin_class_init (GstUriTranscodeBinClass * klass) gstelement_klass->change_state = GST_DEBUG_FUNCPTR (gst_uri_transcode_bin_change_state); + gstbin_klass = (GstBinClass *) klass; + gstbin_klass->deep_element_added = GST_DEBUG_FUNCPTR (deep_element_added); + GST_DEBUG_CATEGORY_INIT (gst_uri_transcodebin_debug, "uritranscodebin", 0, "UriTranscodebin element");