mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-27 20:21:24 +00:00
transcode: Port to encodebin2
This allows supporting muxing sinks like hlssink2 or splitmux
This commit is contained in:
parent
b3544e24ba
commit
142e571c28
3 changed files with 189 additions and 66 deletions
|
@ -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": {
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
@ -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");
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue