transcode: Port to encodebin2

This allows supporting muxing sinks like hlssink2 or splitmux
This commit is contained in:
Thibault Saunier 2020-11-19 17:55:10 -03:00
parent b3544e24ba
commit 142e571c28
3 changed files with 189 additions and 66 deletions

View file

@ -220692,6 +220692,11 @@
"caps": "ANY", "caps": "ANY",
"direction": "src", "direction": "src",
"presence": "always" "presence": "always"
},
"src_%%u": {
"caps": "ANY",
"direction": "src",
"presence": "sometimes"
} }
}, },
"properties": { "properties": {

View file

@ -50,10 +50,22 @@ GST_STATIC_PAD_TEMPLATE ("sink",
GST_PAD_ALWAYS, GST_PAD_ALWAYS,
GST_STATIC_CAPS_ANY); 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 = static GstStaticPadTemplate transcode_bin_src_template =
GST_STATIC_PAD_TEMPLATE ("src", GST_STATIC_PAD_TEMPLATE ("src_%u",
GST_PAD_SRC, GST_PAD_SRC,
GST_PAD_ALWAYS, GST_PAD_SOMETIMES,
GST_STATIC_CAPS_ANY); GST_STATIC_CAPS_ANY);
typedef struct typedef struct
@ -92,7 +104,6 @@ typedef struct
GstEncodingProfile *profile; GstEncodingProfile *profile;
gboolean avoid_reencoding; gboolean avoid_reencoding;
GstPad *sinkpad; GstPad *sinkpad;
GstPad *srcpad;
GstElement *audio_filter; GstElement *audio_filter;
GstElement *video_filter; GstElement *video_filter;
@ -364,33 +375,47 @@ decodebin_pad_added_cb (GstElement * decodebin, GstPad * pad,
(GstPadProbeCallback) wait_stream_start_probe, self, NULL); (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 static gboolean
make_encodebin (GstTranscodeBin * self) make_encodebin (GstTranscodeBin * self)
{ {
GstPad *pad;
GST_INFO_OBJECT (self, "making new encodebin"); GST_INFO_OBJECT (self, "making new encodebin");
if (!self->profile) if (!self->profile)
goto no_profile; goto no_profile;
self->encodebin = gst_element_factory_make ("encodebin", NULL); self->encodebin = gst_element_factory_make ("encodebin2", NULL);
if (!self->encodebin) if (!self->encodebin)
goto no_encodebin; goto no_encodebin;
gst_bin_add (GST_BIN (self), self->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); 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); return gst_element_sync_state_with_parent (self->encodebin);
/* ERRORS */ /* ERRORS */
@ -945,14 +970,6 @@ gst_transcode_bin_init (GstTranscodeBin * self)
gst_object_unref (pad_tmpl); 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 = self->transcoding_streams =
g_ptr_array_new_with_free_func ((GDestroyNotify) transcoding_stream_free); g_ptr_array_new_with_free_func ((GDestroyNotify) transcoding_stream_free);

View file

@ -103,68 +103,59 @@ post_missing_plugin_error (GstElement * dec, const gchar * element_name)
} }
/* *INDENT-ON* */ /* *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 static gboolean
make_dest (GstUriTranscodeBin * self) make_dest (GstUriTranscodeBin * self)
{ {
GError *err = NULL; 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)) 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, self->sink = gst_element_make_from_uri (GST_URI_SINK, self->dest_uri,
"sink", &err); "sink", &err);
if (!self->sink) if (!self->sink)
goto no_sink; goto no_sink_unlock;
GST_OBJECT_UNLOCK (self);
gst_bin_add (GST_BIN (self), self->sink); gst_bin_add (GST_BIN (self), self->sink);
g_object_set (self->sink, "sync", TRUE, "max-lateness", GST_CLOCK_TIME_NONE, g_object_set (self->sink, "sync", TRUE, "max-lateness", GST_CLOCK_TIME_NONE,
NULL); NULL);
return TRUE; 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, GST_ELEMENT_ERROR (self, RESOURCE, NOT_FOUND,
("Invalid URI \"%s\".", self->dest_uri), (NULL)); ("Invalid URI \"%s\".", self->dest_uri), (NULL));
g_clear_error (&err); g_clear_error (&err);
return FALSE; 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 /* whoops, could not create the source element, dig a little deeper to
* figure out what might be wrong. */ * figure out what might be wrong. */
if (err != NULL && err->code == GST_URI_ERROR_UNSUPPORTED_PROTOCOL) { 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 static void
src_pad_added_cb (GstElement * src, GstPad * pad, GstUriTranscodeBin * self) 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 static GstStateChangeReturn
gst_uri_transcode_bin_change_state (GstElement * element, gst_uri_transcode_bin_change_state (GstElement * element,
GstStateChange transition) GstStateChange transition)
@ -307,16 +407,13 @@ gst_uri_transcode_bin_change_state (GstElement * element,
switch (transition) { switch (transition) {
case GST_STATE_CHANGE_READY_TO_PAUSED: case GST_STATE_CHANGE_READY_TO_PAUSED:
if (!make_dest (self))
goto setup_failed;
if (!make_transcodebin (self)) if (!make_transcodebin (self))
goto setup_failed; goto setup_failed;
if (!make_source (self)) if (!make_source (self))
goto setup_failed; 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_STATE_PAUSED) == GST_STATE_CHANGE_FAILURE) {
GST_ERROR_OBJECT (self, GST_ERROR_OBJECT (self,
"Could not set %" GST_PTR_FORMAT " state to PAUSED", self->sink); "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); GObjectClass *object_class = G_OBJECT_CLASS (klass);
GstElementClass *gstelement_klass; GstElementClass *gstelement_klass;
GstBinClass *gstbin_klass;
object_class->get_property = gst_uri_transcode_bin_get_property; object_class->get_property = gst_uri_transcode_bin_get_property;
object_class->set_property = gst_uri_transcode_bin_set_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 = gstelement_klass->change_state =
GST_DEBUG_FUNCPTR (gst_uri_transcode_bin_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, GST_DEBUG_CATEGORY_INIT (gst_uri_transcodebin_debug, "uritranscodebin", 0,
"UriTranscodebin element"); "UriTranscodebin element");