encodebin: Add action signal to get pad for a given profile
authorEdward Hervey <edward@collabora.com>
Thu, 28 Mar 2013 14:20:19 +0000 (15:20 +0100)
committerEdward Hervey <edward@collabora.com>
Sun, 31 Mar 2013 10:02:05 +0000 (12:02 +0200)
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

gst/encoding/gstencodebin.c
tests/check/elements/encodebin.c

index dd49c43..b567fc2 100644 (file)
@@ -199,6 +199,8 @@ struct _GstEncodeBinClass
 
   /* Action Signals */
   GstPad *(*request_pad) (GstEncodeBin * encodebin, GstCaps * caps);
+  GstPad *(*request_profile_pad) (GstEncodeBin * encodebin,
+      const gchar * profilename);
 };
 
 typedef struct _StreamGroup StreamGroup;
@@ -254,6 +256,7 @@ enum
 enum
 {
   SIGNAL_REQUEST_PAD,
+  SIGNAL_REQUEST_PROFILE_PAD,
   LAST_SIGNAL
 };
 
@@ -314,6 +317,8 @@ static StreamGroup *_create_stream_group (GstEncodeBin * ebin,
 static void stream_group_remove (GstEncodeBin * ebin, StreamGroup * sgroup);
 static GstPad *gst_encode_bin_request_pad_signal (GstEncodeBin * encodebin,
     GstCaps * caps);
+static GstPad *gst_encode_bin_request_profile_pad_signal (GstEncodeBin *
+    encodebin, const gchar * profilename);
 
 static inline GstElement *_get_formatter (GstEncodeBin * ebin,
     GstEncodingProfile * sprof);
@@ -402,7 +407,26 @@ gst_encode_bin_class_init (GstEncodeBinClass * klass)
           request_pad), NULL, NULL, g_cclosure_marshal_generic,
       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_profile_pad = gst_encode_bin_request_profile_pad_signal;
 
   gst_element_class_add_pad_template (gstelement_klass,
       gst_static_pad_template_get (&muxer_src_template));
@@ -599,7 +623,8 @@ stream_profile_used_count (GstEncodeBin * ebin, GstEncodingProfile * sprof)
 }
 
 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,
       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)) {
     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 =
         gst_encoding_container_profile_get_profiles
         (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;
 
       /* 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
        */
       if (G_TYPE_FROM_INSTANCE (sprof) == ptype) {
         guint presence = gst_encoding_profile_get_presence (sprof);
         GST_DEBUG ("Found a stream profile with the same type");
-        if ((presence == 0)
+        if (presence == 0
             || (presence > stream_profile_used_count (ebin, sprof)))
           return sprof;
-      } else if ((caps != NULL) && (ptype == G_TYPE_NONE)) {
+      } else if (caps && ptype == G_TYPE_NONE) {
         GstCaps *outcaps;
         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
    * these caps */
-  sprof = next_unused_stream_profile (encodebin, ptype, caps);
+  sprof = next_unused_stream_profile (encodebin, ptype, name, caps);
 
   if (G_UNLIKELY (sprof == NULL))
     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);
 
-  /* Identify the stream group */
-  if (caps != NULL) {
+  /* Identify the stream group (if name or caps have been provided) */
+  if (caps != NULL || name != NULL) {
     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;
 }
 
+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 *
 find_stream_group_from_pad (GstEncodeBin * ebin, GstPad * pad)
 {
index 3ffa80d..b896462 100644 (file)
@@ -90,6 +90,23 @@ create_vorbis_only_profile (void)
   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)
 {
   GstElement *ebin;
@@ -188,6 +205,8 @@ GST_START_TEST (test_encodebin_sink_pads_static)
   /* Check if the audio sink pad was properly created */
   sinkpad = gst_element_get_static_pad (ebin, "audio_0");
   fail_unless (sinkpad != NULL);
+  /* Check caps match */
+  _caps_match (sinkpad, "audio/x-raw;audio/x-vorbis");
   gst_object_unref (sinkpad);
 
   /* 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 */
   sinkpad = gst_element_get_request_pad (ebin, "audio_0");
   fail_unless (sinkpad != NULL);
+  _caps_match (sinkpad, "audio/x-raw;audio/x-vorbis");
   gst_element_release_request_pad (ebin, sinkpad);
   gst_object_unref (sinkpad);
   sinkpad = NULL;
@@ -313,6 +333,7 @@ GST_START_TEST (test_encodebin_sink_pads_dynamic)
   g_signal_emit_by_name (ebin, "request-pad", sinkcaps, &sinkpad);
   gst_caps_unref (sinkcaps);
   fail_unless (sinkpad != NULL);
+  _caps_match (sinkpad, "audio/x-raw;audio/x-vorbis");
   gst_element_release_request_pad (ebin, sinkpad);
   gst_object_unref (sinkpad);
   sinkpad = NULL;
@@ -357,11 +378,13 @@ GST_START_TEST (test_encodebin_sink_pads_multiple_static)
   /* Check if the audio sink pad was properly created */
   sinkpadvorbis = gst_element_get_static_pad (ebin, "audio_0");
   fail_unless (sinkpadvorbis != NULL);
+  _caps_match (sinkpadvorbis, "audio/x-raw;audio/x-vorbis");
   gst_object_unref (sinkpadvorbis);
 
   /* Check if the video sink pad was properly created */
   sinkpadtheora = gst_element_get_static_pad (ebin, "video_1");
   fail_unless (sinkpadtheora != NULL);
+  _caps_match (sinkpadtheora, "video/x-raw;video/x-theora");
   gst_object_unref (sinkpadtheora);
 
   /* 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 */
   sinkpadvorbis = gst_element_get_request_pad (ebin, "audio_0");
+  _caps_match (sinkpadvorbis, "audio/x-raw;audio/x-vorbis");
   fail_unless (sinkpadvorbis != NULL);
 
   /* Check if the video sink pad was properly created */
   sinkpadtheora = gst_element_get_request_pad (ebin, "video_1");
+  _caps_match (sinkpadtheora, "video/x-raw;video/x-theora");
   fail_unless (sinkpadtheora != NULL);
 
   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);
   gst_caps_unref (vorbiscaps);
   fail_unless (sinkpad != NULL);
+  _caps_match (sinkpad, "audio/x-raw;audio/x-vorbis");
   gst_element_release_request_pad (ebin, 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");
   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);
 
@@ -762,11 +789,13 @@ GST_START_TEST (test_encodebin_render_audio_video_dynamic)
   sinkpad1 = gst_element_get_request_pad (ebin, "audio_0");
   fail_unless (srcpad != 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);
   gst_object_unref (srcpad);
 
   srcpad = gst_element_get_static_pad (videotestsrc, "src");
   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);
   gst_object_unref (srcpad);
 
@@ -921,6 +950,75 @@ GST_START_TEST (test_encodebin_reuse)
 
 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 *
 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_impossible_element_combination);
   tcase_add_test (tc_chain, test_encodebin_reuse);
+  tcase_add_test (tc_chain, test_encodebin_named_requests);
 
   return s;
 }