encodebin: delay missing encoder error as passthrough is still possible

Set up a fakesink with a pad probe to replace the missing encoder to detect
if encoding was really required and only error out in this case. Otherwise
just let passthrough branch work.

This delays the error posting from the set_state function to when buffers
are really flowing. Unit test updated accordingly

https://bugzilla.gnome.org/show_bug.cgi?id=650652
This commit is contained in:
Thiago Santos 2014-07-30 16:59:15 -03:00
parent 59fb749ef6
commit c9904fb639
2 changed files with 111 additions and 36 deletions

View file

@ -215,6 +215,7 @@ struct _StreamGroup
GList *converters; /* List of conversion GstElement */ GList *converters; /* List of conversion GstElement */
GstElement *capsfilter; /* profile->restriction (if non-NULL/ANY) */ GstElement *capsfilter; /* profile->restriction (if non-NULL/ANY) */
GstElement *encoder; /* Encoder (can be NULL) */ GstElement *encoder; /* Encoder (can be NULL) */
GstElement *fakesink; /* Fakesink (can be NULL) */
GstElement *combiner; GstElement *combiner;
GstElement *parser; GstElement *parser;
GstElement *smartencoder; GstElement *smartencoder;
@ -1076,7 +1077,45 @@ _profile_restriction_caps_cb (GstEncodingProfile * profile,
g_object_set (group->capsfilter, "caps", restriction, NULL); g_object_set (group->capsfilter, "caps", restriction, NULL);
} }
/* FIXME : Add handling of streams that don't need encoding */ static void
_post_missing_plugin_message (GstEncodeBin * ebin, GstEncodingProfile * prof)
{
GstCaps *format;
format = gst_encoding_profile_get_format (prof);
GST_ERROR_OBJECT (ebin, "Couldn't create encoder for format %" GST_PTR_FORMAT,
format);
/* missing plugin support */
gst_element_post_message (GST_ELEMENT_CAST (ebin),
gst_missing_encoder_message_new (GST_ELEMENT_CAST (ebin), format));
GST_ELEMENT_ERROR (ebin, CORE, MISSING_PLUGIN, (NULL),
("Couldn't create encoder for format %" GST_PTR_FORMAT, format));
gst_caps_unref (format);
}
static GstPadProbeReturn
_missing_plugin_probe (GstPad * pad, GstPadProbeInfo * info, gpointer udata)
{
StreamGroup *sgroup = udata;
GstEncodeBin *ebin = sgroup->ebin;
_post_missing_plugin_message (ebin, sgroup->profile);
return GST_PAD_PROBE_OK;
}
static void
_set_up_fake_encoder_pad_probe (GstEncodeBin * ebin, StreamGroup * sgroup)
{
GstPad *pad = gst_element_get_static_pad (sgroup->fakesink, "sink");
gst_pad_add_probe (pad, GST_PAD_PROBE_TYPE_BUFFER, _missing_plugin_probe,
sgroup, NULL);
gst_object_unref (pad);
}
/* FIXME : Add handling of streams that don't require conversion elements */ /* FIXME : Add handling of streams that don't require conversion elements */
/* /*
* Create the elements, StreamGroup, add the sink pad, link it to the muxer * Create the elements, StreamGroup, add the sink pad, link it to the muxer
@ -1091,6 +1130,7 @@ _create_stream_group (GstEncodeBin * ebin, GstEncodingProfile * sprof,
GstPad *sinkpad, *srcpad, *muxerpad = NULL; GstPad *sinkpad, *srcpad, *muxerpad = NULL;
/* Element we will link to the encoder */ /* Element we will link to the encoder */
GstElement *last = NULL; GstElement *last = NULL;
GstElement *encoder = NULL;
GList *tmp, *tosync = NULL; GList *tmp, *tosync = NULL;
GstCaps *format, *restriction; GstCaps *format, *restriction;
const gchar *missing_element_name; const gchar *missing_element_name;
@ -1116,14 +1156,6 @@ _create_stream_group (GstEncodeBin * ebin, GstEncodingProfile * sprof,
* * One for already encoded data * * One for already encoded data
*/ */
/* Exception to the rule above:
* We check if we have an available encoder so we can abort early */
/* FIXME : What if we only want to do passthrough ??? */
GST_LOG ("Checking for encoder availability");
sgroup->encoder = _get_encoder (ebin, sprof);
if (G_UNLIKELY (sgroup->encoder == NULL))
goto no_encoder;
/* Muxer. /* Muxer.
* If we are handling a container profile, figure out if the muxer has a * If we are handling a container profile, figure out if the muxer has a
* sinkpad compatible with the selected profile */ * sinkpad compatible with the selected profile */
@ -1287,19 +1319,30 @@ _create_stream_group (GstEncodeBin * ebin, GstEncodingProfile * sprof,
/* 1. Create the encoder */ /* 1. Create the encoder */
GST_LOG ("Adding encoder"); GST_LOG ("Adding encoder");
last = sgroup->encoder; sgroup->encoder = _get_encoder (ebin, sprof);
gst_bin_add ((GstBin *) ebin, sgroup->encoder); if (sgroup->encoder != NULL) {
tosync = g_list_append (tosync, sgroup->encoder); last = sgroup->encoder;
gst_bin_add ((GstBin *) ebin, sgroup->encoder);
tosync = g_list_append (tosync, sgroup->encoder);
sinkpad = sinkpad =
local_element_request_pad (sgroup->combiner, NULL, "encodingsink", NULL); local_element_request_pad (sgroup->combiner, NULL, "encodingsink",
if (G_UNLIKELY (sinkpad == NULL)) NULL);
goto no_combiner_sinkpad; if (G_UNLIKELY (sinkpad == NULL))
srcpad = gst_element_get_static_pad (sgroup->encoder, "src"); goto no_combiner_sinkpad;
if (G_UNLIKELY (fast_pad_link (srcpad, sinkpad) != GST_PAD_LINK_OK)) srcpad = gst_element_get_static_pad (sgroup->encoder, "src");
goto encoder_link_failure; if (G_UNLIKELY (fast_pad_link (srcpad, sinkpad) != GST_PAD_LINK_OK))
g_object_unref (sinkpad); goto encoder_link_failure;
g_object_unref (srcpad); g_object_unref (sinkpad);
g_object_unref (srcpad);
} else if (gst_encoding_profile_get_preset (sgroup->profile)
|| gst_encoding_profile_get_preset_name (sgroup->profile)) {
_post_missing_plugin_message (ebin, sprof);
goto cleanup;
} else {
/* passthrough can still work, if we discover that *
* encoding is required we post a missing plugin message */
}
/* 3. Create the conversion/restriction elements */ /* 3. Create the conversion/restriction elements */
@ -1312,7 +1355,21 @@ _create_stream_group (GstEncodeBin * ebin, GstEncodingProfile * sprof,
g_object_set (sgroup->capsfilter, "caps", restriction, NULL); g_object_set (sgroup->capsfilter, "caps", restriction, NULL);
gst_bin_add ((GstBin *) ebin, sgroup->capsfilter); gst_bin_add ((GstBin *) ebin, sgroup->capsfilter);
tosync = g_list_append (tosync, sgroup->capsfilter); tosync = g_list_append (tosync, sgroup->capsfilter);
fast_element_link (sgroup->capsfilter, sgroup->encoder); if (sgroup->encoder == NULL) {
/* no encoder available but it might be possible to just do passthrough, so
* let's just set up a fake pad to detect that encoding was attempted and
* if so it posts the missing plugin message */
sgroup->fakesink = gst_element_factory_make ("fakesink", NULL);
g_object_set (sgroup->fakesink, "async", FALSE, NULL);
gst_bin_add (GST_BIN_CAST (ebin), sgroup->fakesink);
tosync = g_list_append (tosync, sgroup->fakesink);
encoder = sgroup->fakesink;
_set_up_fake_encoder_pad_probe (ebin, sgroup);
} else {
encoder = sgroup->encoder;
}
fast_element_link (sgroup->capsfilter, encoder);
sgroup->restriction_sid = g_signal_connect (sprof, "notify::restriction-caps", sgroup->restriction_sid = g_signal_connect (sprof, "notify::restriction-caps",
G_CALLBACK (_profile_restriction_caps_cb), sgroup); G_CALLBACK (_profile_restriction_caps_cb), sgroup);
@ -1464,16 +1521,6 @@ splitter_encoding_failure:
GST_ERROR_OBJECT (ebin, "Error linking splitter to encoding stream"); GST_ERROR_OBJECT (ebin, "Error linking splitter to encoding stream");
goto cleanup; goto cleanup;
no_encoder:
GST_ERROR_OBJECT (ebin, "Couldn't create encoder for format %" GST_PTR_FORMAT,
format);
/* missing plugin support */
gst_element_post_message (GST_ELEMENT_CAST (ebin),
gst_missing_encoder_message_new (GST_ELEMENT_CAST (ebin), format));
GST_ELEMENT_ERROR (ebin, CORE, MISSING_PLUGIN, (NULL),
("Couldn't create encoder for format %" GST_PTR_FORMAT, format));
goto cleanup;
no_muxer_pad: no_muxer_pad:
GST_ERROR_OBJECT (ebin, GST_ERROR_OBJECT (ebin,
"Couldn't find a compatible muxer pad to link encoder to"); "Couldn't find a compatible muxer pad to link encoder to");
@ -1934,6 +1981,8 @@ stream_group_free (GstEncodeBin * ebin, StreamGroup * sgroup)
if (sgroup->encoder) if (sgroup->encoder)
gst_element_set_state (sgroup->encoder, GST_STATE_NULL); gst_element_set_state (sgroup->encoder, GST_STATE_NULL);
if (sgroup->fakesink)
gst_element_set_state (sgroup->fakesink, GST_STATE_NULL);
if (sgroup->outfilter) if (sgroup->outfilter)
gst_element_set_state (sgroup->outfilter, GST_STATE_NULL); gst_element_set_state (sgroup->outfilter, GST_STATE_NULL);
if (sgroup->smartencoder) if (sgroup->smartencoder)
@ -1941,7 +1990,10 @@ stream_group_free (GstEncodeBin * ebin, StreamGroup * sgroup)
if (sgroup->capsfilter) { if (sgroup->capsfilter) {
gst_element_set_state (sgroup->capsfilter, GST_STATE_NULL); gst_element_set_state (sgroup->capsfilter, GST_STATE_NULL);
gst_element_unlink (sgroup->capsfilter, sgroup->encoder); if (sgroup->encoder)
gst_element_unlink (sgroup->capsfilter, sgroup->encoder);
else
gst_element_unlink (sgroup->capsfilter, sgroup->fakesink);
gst_bin_remove ((GstBin *) ebin, sgroup->capsfilter); gst_bin_remove ((GstBin *) ebin, sgroup->capsfilter);
} }
@ -1990,6 +2042,9 @@ stream_group_free (GstEncodeBin * ebin, StreamGroup * sgroup)
if (sgroup->encoder) if (sgroup->encoder)
gst_bin_remove ((GstBin *) ebin, sgroup->encoder); gst_bin_remove ((GstBin *) ebin, sgroup->encoder);
if (sgroup->fakesink)
gst_bin_remove ((GstBin *) ebin, sgroup->fakesink);
if (sgroup->smartencoder) if (sgroup->smartencoder)
gst_bin_remove ((GstBin *) ebin, sgroup->smartencoder); gst_bin_remove ((GstBin *) ebin, sgroup->smartencoder);

View file

@ -104,13 +104,19 @@ create_vorbis_only_profile (void)
return prof; return prof;
} }
static GstCaps *
create_unsupported_caps (void)
{
return gst_caps_new_empty_simple ("audio/x-bogus");
}
static GstEncodingProfile * static GstEncodingProfile *
create_unsupported_profile (void) create_unsupported_profile (void)
{ {
GstEncodingProfile *prof; GstEncodingProfile *prof;
GstCaps *caps; GstCaps *caps;
caps = gst_caps_new_empty_simple ("audio/x-bogus"); caps = create_unsupported_caps ();
prof = prof =
(GstEncodingProfile *) gst_encoding_audio_profile_new (caps, NULL, NULL, (GstEncodingProfile *) gst_encoding_audio_profile_new (caps, NULL, NULL,
0); 0);
@ -1042,14 +1048,28 @@ GST_START_TEST (test_encodebin_missing_plugin_messages)
GstBus *bus = gst_pipeline_get_bus ((GstPipeline *) pipeline); GstBus *bus = gst_pipeline_get_bus ((GstPipeline *) pipeline);
GstElement *ebin = gst_element_factory_make ("encodebin", NULL); GstElement *ebin = gst_element_factory_make ("encodebin", NULL);
GstMessage *message; GstMessage *message;
GstElement *audiotestsrc;
GstPad *sinkpad, *srcpad;
audiotestsrc = gst_element_factory_make ("audiotestsrc", NULL);
g_object_set (audiotestsrc, "num-buffers", 1, NULL);
gst_bin_add ((GstBin *) pipeline, audiotestsrc);
/* first add to bin, then set profile */ /* first add to bin, then set profile */
gst_bin_add ((GstBin *) pipeline, ebin); gst_bin_add ((GstBin *) pipeline, ebin);
set_profile (ebin, create_unsupported_profile ()); set_profile (ebin, create_unsupported_profile ());
gst_element_set_state (pipeline, GST_STATE_READY); srcpad = gst_element_get_static_pad (audiotestsrc, "src");
sinkpad = gst_element_get_static_pad (ebin, "audio_0");
fail_unless (gst_pad_link (srcpad, sinkpad) == GST_PAD_LINK_OK);
gst_object_unref (srcpad);
gst_object_unref (sinkpad);
message = gst_bus_pop_filtered (bus, GST_MESSAGE_ELEMENT); gst_element_set_state (pipeline, GST_STATE_PLAYING);
message =
gst_bus_timed_pop_filtered (bus, GST_CLOCK_TIME_NONE,
GST_MESSAGE_ELEMENT);
fail_if (message == NULL); fail_if (message == NULL);
fail_if (!gst_is_missing_plugin_message (message)); fail_if (!gst_is_missing_plugin_message (message));
gst_message_unref (message); gst_message_unref (message);