encodebin: Add action signal to get pad for a given profile

This allows getting a pad for a specific encoding profile, which can
be useful when there are several stream profiles of the same type.

Also update the encodebin unit tests so that we check that the returned
pad has the right caps.

https://bugzilla.gnome.org/show_bug.cgi?id=689845
This commit is contained in:
Edward Hervey 2013-03-28 15:20:19 +01:00
parent bc4238f959
commit b3d94bd0e4
2 changed files with 167 additions and 7 deletions

View File

@ -199,6 +199,8 @@ struct _GstEncodeBinClass
/* Action Signals */ /* Action Signals */
GstPad *(*request_pad) (GstEncodeBin * encodebin, GstCaps * caps); GstPad *(*request_pad) (GstEncodeBin * encodebin, GstCaps * caps);
GstPad *(*request_profile_pad) (GstEncodeBin * encodebin,
const gchar * profilename);
}; };
typedef struct _StreamGroup StreamGroup; typedef struct _StreamGroup StreamGroup;
@ -254,6 +256,7 @@ enum
enum enum
{ {
SIGNAL_REQUEST_PAD, SIGNAL_REQUEST_PAD,
SIGNAL_REQUEST_PROFILE_PAD,
LAST_SIGNAL LAST_SIGNAL
}; };
@ -314,6 +317,8 @@ static StreamGroup *_create_stream_group (GstEncodeBin * ebin,
static void stream_group_remove (GstEncodeBin * ebin, StreamGroup * sgroup); static void stream_group_remove (GstEncodeBin * ebin, StreamGroup * sgroup);
static GstPad *gst_encode_bin_request_pad_signal (GstEncodeBin * encodebin, static GstPad *gst_encode_bin_request_pad_signal (GstEncodeBin * encodebin,
GstCaps * caps); GstCaps * caps);
static GstPad *gst_encode_bin_request_profile_pad_signal (GstEncodeBin *
encodebin, const gchar * profilename);
static inline GstElement *_get_formatter (GstEncodeBin * ebin, static inline GstElement *_get_formatter (GstEncodeBin * ebin,
GstEncodingProfile * sprof); GstEncodingProfile * sprof);
@ -402,7 +407,26 @@ gst_encode_bin_class_init (GstEncodeBinClass * klass)
request_pad), NULL, NULL, g_cclosure_marshal_generic, request_pad), NULL, NULL, g_cclosure_marshal_generic,
GST_TYPE_PAD, 1, GST_TYPE_CAPS); GST_TYPE_PAD, 1, GST_TYPE_CAPS);
/**
* GstEncodeBin::request-profile-pad
* @encodebin: a #GstEncodeBin instance
* @profilename: the name of a #GstEncodingProfile
*
* Use this method to request an unused sink request #GstPad from the profile
* @profilename. You must release the pad with
* gst_element_release_request_pad() when you are done with it.
*
* Returns: A compatible #GstPad, or %NULL if no compatible #GstPad could be
* created or is available.
*/
gst_encode_bin_signals[SIGNAL_REQUEST_PROFILE_PAD] =
g_signal_new ("request-profile-pad", G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, G_STRUCT_OFFSET (GstEncodeBinClass,
request_profile_pad), NULL, NULL, g_cclosure_marshal_generic,
GST_TYPE_PAD, 1, G_TYPE_STRING);
klass->request_pad = gst_encode_bin_request_pad_signal; klass->request_pad = gst_encode_bin_request_pad_signal;
klass->request_profile_pad = gst_encode_bin_request_profile_pad_signal;
gst_element_class_add_pad_template (gstelement_klass, gst_element_class_add_pad_template (gstelement_klass,
gst_static_pad_template_get (&muxer_src_template)); gst_static_pad_template_get (&muxer_src_template));
@ -599,7 +623,8 @@ stream_profile_used_count (GstEncodeBin * ebin, GstEncodingProfile * sprof)
} }
static inline GstEncodingProfile * static inline GstEncodingProfile *
next_unused_stream_profile (GstEncodeBin * ebin, GType ptype, GstCaps * caps) next_unused_stream_profile (GstEncodeBin * ebin, GType ptype,
const gchar * name, GstCaps * caps)
{ {
GST_DEBUG_OBJECT (ebin, "ptype:%s, caps:%" GST_PTR_FORMAT, GST_DEBUG_OBJECT (ebin, "ptype:%s, caps:%" GST_PTR_FORMAT,
g_type_name (ptype), caps); g_type_name (ptype), caps);
@ -619,6 +644,32 @@ next_unused_stream_profile (GstEncodeBin * ebin, GType ptype, GstCaps * caps)
if (GST_IS_ENCODING_CONTAINER_PROFILE (ebin->profile)) { if (GST_IS_ENCODING_CONTAINER_PROFILE (ebin->profile)) {
const GList *tmp; const GList *tmp;
if (name) {
/* If we have a name, try to find a profile with the same name */
tmp =
gst_encoding_container_profile_get_profiles
(GST_ENCODING_CONTAINER_PROFILE (ebin->profile));
for (; tmp; tmp = tmp->next) {
GstEncodingProfile *sprof = (GstEncodingProfile *) tmp->data;
const gchar *profilename = gst_encoding_profile_get_name (sprof);
if (profilename && !strcmp (name, profilename)) {
guint presence = gst_encoding_profile_get_presence (sprof);
GST_DEBUG ("Found profile matching the requested name");
if (presence == 0
|| presence > stream_profile_used_count (ebin, sprof))
return sprof;
GST_WARNING ("Matching stream already used");
return NULL;
}
}
GST_DEBUG
("No profiles matching requested pad name, carrying on with normal stream matching");
}
for (tmp = for (tmp =
gst_encoding_container_profile_get_profiles gst_encoding_container_profile_get_profiles
(GST_ENCODING_CONTAINER_PROFILE (ebin->profile)); tmp; (GST_ENCODING_CONTAINER_PROFILE (ebin->profile)); tmp;
@ -626,16 +677,16 @@ next_unused_stream_profile (GstEncodeBin * ebin, GType ptype, GstCaps * caps)
GstEncodingProfile *sprof = (GstEncodingProfile *) tmp->data; GstEncodingProfile *sprof = (GstEncodingProfile *) tmp->data;
/* Pick an available Stream profile for which: /* Pick an available Stream profile for which:
* * either it is of the compatibly raw type, * * either it is of the compatible raw type,
* * OR we can pass it through directly without encoding * * OR we can pass it through directly without encoding
*/ */
if (G_TYPE_FROM_INSTANCE (sprof) == ptype) { if (G_TYPE_FROM_INSTANCE (sprof) == ptype) {
guint presence = gst_encoding_profile_get_presence (sprof); guint presence = gst_encoding_profile_get_presence (sprof);
GST_DEBUG ("Found a stream profile with the same type"); GST_DEBUG ("Found a stream profile with the same type");
if ((presence == 0) if (presence == 0
|| (presence > stream_profile_used_count (ebin, sprof))) || (presence > stream_profile_used_count (ebin, sprof)))
return sprof; return sprof;
} else if ((caps != NULL) && (ptype == G_TYPE_NONE)) { } else if (caps && ptype == G_TYPE_NONE) {
GstCaps *outcaps; GstCaps *outcaps;
gboolean res; gboolean res;
@ -665,7 +716,7 @@ request_pad_for_stream (GstEncodeBin * encodebin, GType ptype,
/* Figure out if we have a unused GstEncodingProfile we can use for /* Figure out if we have a unused GstEncodingProfile we can use for
* these caps */ * these caps */
sprof = next_unused_stream_profile (encodebin, ptype, caps); sprof = next_unused_stream_profile (encodebin, ptype, name, caps);
if (G_UNLIKELY (sprof == NULL)) if (G_UNLIKELY (sprof == NULL))
goto no_stream_profile; goto no_stream_profile;
@ -698,8 +749,8 @@ gst_encode_bin_request_new_pad (GstElement * element,
GST_DEBUG_OBJECT (element, "templ:%s, name:%s", templ->name_template, name); GST_DEBUG_OBJECT (element, "templ:%s, name:%s", templ->name_template, name);
/* Identify the stream group */ /* Identify the stream group (if name or caps have been provided) */
if (caps != NULL) { if (caps != NULL || name != NULL) {
res = request_pad_for_stream (ebin, G_TYPE_NONE, name, (GstCaps *) caps); res = request_pad_for_stream (ebin, G_TYPE_NONE, name, (GstCaps *) caps);
} }
@ -732,6 +783,16 @@ gst_encode_bin_request_pad_signal (GstEncodeBin * encodebin, GstCaps * caps)
return pad ? GST_PAD_CAST (gst_object_ref (pad)) : NULL; return pad ? GST_PAD_CAST (gst_object_ref (pad)) : NULL;
} }
static GstPad *
gst_encode_bin_request_profile_pad_signal (GstEncodeBin * encodebin,
const gchar * profilename)
{
GstPad *pad =
request_pad_for_stream (encodebin, G_TYPE_NONE, profilename, NULL);
return pad ? GST_PAD_CAST (gst_object_ref (pad)) : NULL;
}
static inline StreamGroup * static inline StreamGroup *
find_stream_group_from_pad (GstEncodeBin * ebin, GstPad * pad) find_stream_group_from_pad (GstEncodeBin * ebin, GstPad * pad)
{ {

View File

@ -90,6 +90,23 @@ create_vorbis_only_profile (void)
return prof; return prof;
} }
static void
_caps_match (GstPad * sinkpad, const gchar * capsname)
{
GstCaps *caps, *sinkcaps;
gchar *name;
caps = gst_caps_from_string (capsname);
sinkcaps = gst_pad_query_caps (sinkpad, NULL);
fail_unless (sinkcaps != NULL);
name = gst_caps_to_string (sinkcaps);
fail_unless (gst_caps_is_subset (sinkcaps, caps),
"caps ('%s') are not a subset of ('%s')", name, capsname);
g_free (name);
gst_caps_unref (sinkcaps);
gst_caps_unref (caps);
}
GST_START_TEST (test_encodebin_states) GST_START_TEST (test_encodebin_states)
{ {
GstElement *ebin; GstElement *ebin;
@ -188,6 +205,8 @@ GST_START_TEST (test_encodebin_sink_pads_static)
/* Check if the audio sink pad was properly created */ /* Check if the audio sink pad was properly created */
sinkpad = gst_element_get_static_pad (ebin, "audio_0"); sinkpad = gst_element_get_static_pad (ebin, "audio_0");
fail_unless (sinkpad != NULL); fail_unless (sinkpad != NULL);
/* Check caps match */
_caps_match (sinkpad, "audio/x-raw;audio/x-vorbis");
gst_object_unref (sinkpad); gst_object_unref (sinkpad);
/* Set back to NULL */ /* Set back to NULL */
@ -304,6 +323,7 @@ GST_START_TEST (test_encodebin_sink_pads_dynamic)
/* Check if the audio sink pad can be requested */ /* Check if the audio sink pad can be requested */
sinkpad = gst_element_get_request_pad (ebin, "audio_0"); sinkpad = gst_element_get_request_pad (ebin, "audio_0");
fail_unless (sinkpad != NULL); fail_unless (sinkpad != NULL);
_caps_match (sinkpad, "audio/x-raw;audio/x-vorbis");
gst_element_release_request_pad (ebin, sinkpad); gst_element_release_request_pad (ebin, sinkpad);
gst_object_unref (sinkpad); gst_object_unref (sinkpad);
sinkpad = NULL; sinkpad = NULL;
@ -313,6 +333,7 @@ GST_START_TEST (test_encodebin_sink_pads_dynamic)
g_signal_emit_by_name (ebin, "request-pad", sinkcaps, &sinkpad); g_signal_emit_by_name (ebin, "request-pad", sinkcaps, &sinkpad);
gst_caps_unref (sinkcaps); gst_caps_unref (sinkcaps);
fail_unless (sinkpad != NULL); fail_unless (sinkpad != NULL);
_caps_match (sinkpad, "audio/x-raw;audio/x-vorbis");
gst_element_release_request_pad (ebin, sinkpad); gst_element_release_request_pad (ebin, sinkpad);
gst_object_unref (sinkpad); gst_object_unref (sinkpad);
sinkpad = NULL; sinkpad = NULL;
@ -357,11 +378,13 @@ GST_START_TEST (test_encodebin_sink_pads_multiple_static)
/* Check if the audio sink pad was properly created */ /* Check if the audio sink pad was properly created */
sinkpadvorbis = gst_element_get_static_pad (ebin, "audio_0"); sinkpadvorbis = gst_element_get_static_pad (ebin, "audio_0");
fail_unless (sinkpadvorbis != NULL); fail_unless (sinkpadvorbis != NULL);
_caps_match (sinkpadvorbis, "audio/x-raw;audio/x-vorbis");
gst_object_unref (sinkpadvorbis); gst_object_unref (sinkpadvorbis);
/* Check if the video sink pad was properly created */ /* Check if the video sink pad was properly created */
sinkpadtheora = gst_element_get_static_pad (ebin, "video_1"); sinkpadtheora = gst_element_get_static_pad (ebin, "video_1");
fail_unless (sinkpadtheora != NULL); fail_unless (sinkpadtheora != NULL);
_caps_match (sinkpadtheora, "video/x-raw;video/x-theora");
gst_object_unref (sinkpadtheora); gst_object_unref (sinkpadtheora);
/* Set back to NULL */ /* Set back to NULL */
@ -398,10 +421,12 @@ GST_START_TEST (test_encodebin_sink_pads_multiple_dynamic)
/* Check if the audio sink pad was properly created */ /* Check if the audio sink pad was properly created */
sinkpadvorbis = gst_element_get_request_pad (ebin, "audio_0"); sinkpadvorbis = gst_element_get_request_pad (ebin, "audio_0");
_caps_match (sinkpadvorbis, "audio/x-raw;audio/x-vorbis");
fail_unless (sinkpadvorbis != NULL); fail_unless (sinkpadvorbis != NULL);
/* Check if the video sink pad was properly created */ /* Check if the video sink pad was properly created */
sinkpadtheora = gst_element_get_request_pad (ebin, "video_1"); sinkpadtheora = gst_element_get_request_pad (ebin, "video_1");
_caps_match (sinkpadtheora, "video/x-raw;video/x-theora");
fail_unless (sinkpadtheora != NULL); fail_unless (sinkpadtheora != NULL);
fail_unless_equals_int (gst_element_set_state (ebin, GST_STATE_PAUSED), fail_unless_equals_int (gst_element_set_state (ebin, GST_STATE_PAUSED),
@ -450,6 +475,7 @@ GST_START_TEST (test_encodebin_sink_pads_dynamic_encoder)
g_signal_emit_by_name (ebin, "request-pad", vorbiscaps, &sinkpad); g_signal_emit_by_name (ebin, "request-pad", vorbiscaps, &sinkpad);
gst_caps_unref (vorbiscaps); gst_caps_unref (vorbiscaps);
fail_unless (sinkpad != NULL); fail_unless (sinkpad != NULL);
_caps_match (sinkpad, "audio/x-raw;audio/x-vorbis");
gst_element_release_request_pad (ebin, sinkpad); gst_element_release_request_pad (ebin, sinkpad);
gst_object_unref (sinkpad); gst_object_unref (sinkpad);
@ -622,6 +648,7 @@ GST_START_TEST (test_encodebin_render_audio_dynamic)
sinkpad = gst_element_get_request_pad (ebin, "audio_0"); sinkpad = gst_element_get_request_pad (ebin, "audio_0");
fail_unless (sinkpad != NULL); fail_unless (sinkpad != NULL);
_caps_match (sinkpad, "audio/x-raw;audio/x-vorbis");
fail_unless_equals_int (gst_pad_link (srcpad, sinkpad), GST_PAD_LINK_OK); fail_unless_equals_int (gst_pad_link (srcpad, sinkpad), GST_PAD_LINK_OK);
@ -762,11 +789,13 @@ GST_START_TEST (test_encodebin_render_audio_video_dynamic)
sinkpad1 = gst_element_get_request_pad (ebin, "audio_0"); sinkpad1 = gst_element_get_request_pad (ebin, "audio_0");
fail_unless (srcpad != NULL); fail_unless (srcpad != NULL);
fail_unless (sinkpad1 != NULL); fail_unless (sinkpad1 != NULL);
_caps_match (sinkpad1, "audio/x-raw;audio/x-vorbis");
fail_unless_equals_int (gst_pad_link (srcpad, sinkpad1), GST_PAD_LINK_OK); fail_unless_equals_int (gst_pad_link (srcpad, sinkpad1), GST_PAD_LINK_OK);
gst_object_unref (srcpad); gst_object_unref (srcpad);
srcpad = gst_element_get_static_pad (videotestsrc, "src"); srcpad = gst_element_get_static_pad (videotestsrc, "src");
sinkpad2 = gst_element_get_request_pad (ebin, "video_1"); sinkpad2 = gst_element_get_request_pad (ebin, "video_1");
_caps_match (sinkpad2, "video/x-raw;video/x-theora");
fail_unless_equals_int (gst_pad_link (srcpad, sinkpad2), GST_PAD_LINK_OK); fail_unless_equals_int (gst_pad_link (srcpad, sinkpad2), GST_PAD_LINK_OK);
gst_object_unref (srcpad); gst_object_unref (srcpad);
@ -921,6 +950,75 @@ GST_START_TEST (test_encodebin_reuse)
GST_END_TEST; GST_END_TEST;
GST_START_TEST (test_encodebin_named_requests)
{
GstElement *ebin;
GstEncodingContainerProfile *cprof;
GstCaps *ogg, *vorbis, *theora;
GstEncodingProfile *vorbisprof, *theoraprof;
GstPad *srcpad, *sinkpadvorbis, *sinkpadtheora;
/* Create a profile with vorbis/theora named profile */
ogg = gst_caps_new_empty_simple ("application/ogg");
cprof =
gst_encoding_container_profile_new ((gchar *) "myprofile", NULL, ogg,
NULL);
gst_caps_unref (ogg);
vorbis = gst_caps_new_empty_simple ("audio/x-vorbis");
vorbisprof =
(GstEncodingProfile *) gst_encoding_audio_profile_new (vorbis, NULL, NULL,
0);
gst_encoding_profile_set_name (vorbisprof, "vorbisprofile");
fail_unless (gst_encoding_container_profile_add_profile (cprof, vorbisprof));
gst_caps_unref (vorbis);
theora = gst_caps_new_empty_simple ("video/x-theora");
theoraprof =
(GstEncodingProfile *) gst_encoding_video_profile_new (theora, NULL, NULL,
0);
gst_encoding_profile_set_name (theoraprof, "theoraprofile");
fail_unless (gst_encoding_container_profile_add_profile (cprof, theoraprof));
gst_caps_unref (theora);
ebin = gst_element_factory_make ("encodebin", NULL);
/* First try is with a streamprofile that has a forced presence of 1 */
g_object_set (ebin, "profile", cprof, NULL);
gst_encoding_profile_unref (cprof);
/* Check if the source pad was properly created */
srcpad = gst_element_get_static_pad (ebin, "src");
fail_unless (srcpad != NULL);
gst_object_unref (srcpad);
/* Request a vorbis profile pad */
g_signal_emit_by_name (ebin, "request-profile-pad", "vorbisprofile",
&sinkpadvorbis);
fail_unless (sinkpadvorbis != NULL);
_caps_match (sinkpadvorbis, "audio/x-raw;audio/x-vorbis");
gst_object_unref (sinkpadvorbis);
/* Request a theora profile pad */
g_signal_emit_by_name (ebin, "request-profile-pad", "theoraprofile",
&sinkpadtheora);
fail_unless (sinkpadtheora != NULL);
_caps_match (sinkpadtheora, "video/x-raw;video/x-theora");
gst_object_unref (sinkpadtheora);
fail_unless_equals_int (gst_element_set_state (ebin, GST_STATE_PAUSED),
GST_STATE_CHANGE_SUCCESS);
/* Set back to NULL */
fail_unless_equals_int (gst_element_set_state (ebin, GST_STATE_NULL),
GST_STATE_CHANGE_SUCCESS);
gst_object_unref (ebin);
}
GST_END_TEST;
static Suite * static Suite *
encodebin_suite (void) encodebin_suite (void)
@ -944,6 +1042,7 @@ encodebin_suite (void)
tcase_add_test (tc_chain, test_encodebin_render_audio_video_dynamic); tcase_add_test (tc_chain, test_encodebin_render_audio_video_dynamic);
tcase_add_test (tc_chain, test_encodebin_impossible_element_combination); tcase_add_test (tc_chain, test_encodebin_impossible_element_combination);
tcase_add_test (tc_chain, test_encodebin_reuse); tcase_add_test (tc_chain, test_encodebin_reuse);
tcase_add_test (tc_chain, test_encodebin_named_requests);
return s; return s;
} }