summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSreerenj Balachandran <sreerenj.balachandran@intel.com>2013-04-23 19:13:23 +0300
committerSreerenj Balachandran <sreerenj.balachandran@intel.com>2013-04-23 19:13:23 +0300
commitb78d8b4e94350f08531e5fe010a123fb030ae716 (patch)
treec239688675ac2771af8151f7e0f40a7a5057f789
parentc5840ebbd9911348675f0c197566db27409c2124 (diff)
playbin: autoplug video_decoer:video_sink based on GstCapsFeatures.HEADmaster
-rw-r--r--gst/playback/gstplaybin2.c370
1 files changed, 320 insertions, 50 deletions
diff --git a/gst/playback/gstplaybin2.c b/gst/playback/gstplaybin2.c
index 0d473879e..4eb150c2c 100644
--- a/gst/playback/gstplaybin2.c
+++ b/gst/playback/gstplaybin2.c
@@ -285,6 +285,18 @@ enum
PLAYBIN_STREAM_LAST
};
+static void velements_free (GList * list);
+
+/* structure to hold the video_decoder and video_sink factories
+ * together with the number of common caps features */
+typedef struct
+{
+ GstElementFactory *vdec; /* video decoder */
+ GstElementFactory *vsink; /* video sink */
+ guint n_comm_cf; /* number of common caps features */
+ gboolean is_dirty;
+} GstVideoElement;
+
/* a structure to hold the objects for decoding a uri and the subtitle uri
*/
struct _GstSourceGroup
@@ -309,6 +321,8 @@ struct _GstSourceGroup
GstElement *audio_sink; /* autoplugged audio and video sinks */
GstElement *video_sink;
+ GList *velements; /* a list of GstVideoElements */
+
/* uridecodebins for uri and subtitle uri */
GstElement *uridecodebin;
GstElement *suburidecodebin;
@@ -1255,6 +1269,10 @@ free_group (GstPlayBin * playbin, GstSourceGroup * group)
}
group->video_sink = NULL;
+ if (group->velements)
+ velements_free (group->velements);
+ group->velements = NULL;
+
g_list_free (group->stream_changed_pending);
group->stream_changed_pending = NULL;
@@ -3235,6 +3253,163 @@ _factory_can_sink_caps (GstElementFactory * factory, GstCaps * caps)
return FALSE;
}
+static void
+velements_free (GList * list)
+{
+ GList *l;
+ GstVideoElement *elm;
+
+ for (l = list; l; l = l->next) {
+ elm = (GstVideoElement *) l->data;
+ gst_object_unref (elm->vdec);
+ gst_object_unref (elm->vsink);
+ g_slice_free (GstVideoElement, elm);
+ }
+ g_list_free (list);
+}
+
+static gint
+velement_compare (gconstpointer p1, gconstpointer p2)
+{
+ GstVideoElement *v1, *v2;
+ GstPluginFeature *fd1, *fd2, *fs1, *fs2;
+ gint diff, v1_rank, v2_rank;
+
+ v1 = (GstVideoElement *) p1;
+ v2 = (GstVideoElement *) p2;
+
+ fs1 = (GstPluginFeature *) v1->vsink;
+ fs2 = (GstPluginFeature *) v2->vsink;
+ fd1 = (GstPluginFeature *) v1->vdec;
+ fd2 = (GstPluginFeature *) v2->vdec;
+
+ v1_rank =
+ gst_plugin_feature_get_rank (fd1) + gst_plugin_feature_get_rank (fs1);
+ v2_rank =
+ gst_plugin_feature_get_rank (fd2) + gst_plugin_feature_get_rank (fs2);
+
+ /* comparison based on the rank */
+ diff = v2_rank - v1_rank;
+ if (diff != 0)
+ return diff;
+
+ /* comparison based on number of common caps features */
+ diff = v2->n_comm_cf - v1->n_comm_cf;
+ if (diff != 0)
+ return diff;
+
+ /* comparison based on the name of sink elements */
+ return strcmp (GST_OBJECT_NAME (fs1), GST_OBJECT_NAME (fs2));
+}
+
+static GList *
+create_velements_list (GList * dec_list, GList * sink_list)
+{
+ GstElementFactory *d_factory, *s_factory;
+ GstVideoElement *ve;
+ GList *dl, *sl;
+ GList *ve_list = NULL, *tmp = NULL;
+
+ dl = dec_list;
+ sl = sink_list;
+
+ /* create a list of video elements. Each element in the list
+ * is holding a video_decoder and a video_sink */
+ for (; dl; dl = dl->next) {
+ d_factory = (GstElementFactory *) dl->data;
+ if (gst_element_factory_list_is_type (d_factory,
+ GST_ELEMENT_FACTORY_TYPE_DECODER)) {
+
+ for (; sl; sl = sl->next) {
+ s_factory = (GstElementFactory *) sl->data;
+
+ ve = g_slice_new (GstVideoElement);
+ ve->vdec = gst_object_ref (d_factory);
+ ve->vsink = gst_object_ref (s_factory);
+ ve_list = g_list_prepend (ve_list, ve);
+ }
+ sl = sink_list;
+ }
+ }
+
+ tmp = ve_list;
+
+ /* compute the number of common caps features */
+ for (; tmp; tmp = tmp->next) {
+ GstVideoElement *velm;
+ GstElementFactory *dec, *sink;
+ const GList *d_templates, *s_templates;
+ GstStaticPadTemplate *d_templ = NULL, *s_templ = NULL, *tmp_templ;
+ GstCaps *d_tmpl_caps, *s_tmpl_caps;
+ GstCapsFeatures *d_features, *s_features;
+ guint d_caps_size, s_caps_size;
+ guint i, j, n_common = 0;
+ GList *walk;
+
+ velm = (GstVideoElement *) tmp->data;
+ dec = (GstElementFactory *) velm->vdec;
+ sink = (GstElementFactory *) velm->vsink;
+
+ d_templates = gst_element_factory_get_static_pad_templates (dec);
+ for (walk = (GList *) d_templates; walk; walk = g_list_next (walk)) {
+ tmp_templ = walk->data;
+ if (tmp_templ->direction == GST_PAD_SRC)
+ d_templ = tmp_templ;
+ else
+ continue;
+ }
+ s_templates = gst_element_factory_get_static_pad_templates (sink);
+ s_templ = s_templates->data;
+
+ d_tmpl_caps = gst_static_caps_get (&d_templ->static_caps);
+ s_tmpl_caps = gst_static_caps_get (&s_templ->static_caps);
+ d_caps_size = gst_caps_get_size (d_tmpl_caps);
+ s_caps_size = gst_caps_get_size (s_tmpl_caps);
+
+ for (i = 0; i < d_caps_size; i++) {
+ d_features = gst_caps_get_features ((const GstCaps *) d_tmpl_caps, i);
+ for (j = 0; j < s_caps_size; j++) {
+ s_features = gst_caps_get_features ((const GstCaps *) s_tmpl_caps, j);
+ if (gst_caps_features_is_equal (d_features, s_features))
+ n_common++;
+ }
+ }
+ velm->n_comm_cf = n_common;
+
+ gst_caps_unref (d_tmpl_caps);
+ gst_caps_unref (s_tmpl_caps);
+ }
+
+ ve_list = g_list_sort (ve_list, (GCompareFunc) velement_compare);
+ return ve_list;
+}
+
+static GList *
+create_decoders_list (GList * mylist, GList * velements)
+{
+ GList *list = NULL;
+ GstElementFactory *factory;
+ GstVideoElement *ve;
+
+ for (; mylist; mylist = mylist->next) {
+ factory = (GstElementFactory *) mylist->data;
+ /* if there are parsers, add them first */
+ if (!gst_element_factory_list_is_type (factory,
+ GST_ELEMENT_FACTORY_TYPE_DECODER))
+ list = g_list_prepend (list, factory);
+ }
+
+ for (; velements; velements = velements->next) {
+ ve = (GstVideoElement *) velements->data;
+ if (!g_list_find (list, ve->vdec))
+ list = g_list_prepend (list, ve->vdec);
+ }
+
+ list = g_list_reverse (list);
+
+ return list;
+}
+
/* Called when we must provide a list of factories to plug to @pad with @caps.
* We first check if we have a sink that can handle the format and if we do, we
* return NULL, to expose the pad. If we have no sink (or the sink does not
@@ -3247,6 +3422,7 @@ autoplug_factories_cb (GstElement * decodebin, GstPad * pad,
GList *mylist, *tmp;
GValueArray *result;
gboolean unref_caps = FALSE;
+ gboolean need_video_dec_list = FALSE;
if (!caps) {
caps = gst_caps_new_any ();
@@ -3254,7 +3430,6 @@ autoplug_factories_cb (GstElement * decodebin, GstPad * pad,
}
playbin = group->playbin;
-
GST_DEBUG_OBJECT (playbin, "factories group %p for %s:%s, %" GST_PTR_FORMAT,
group, GST_DEBUG_PAD_NAME (pad), caps);
@@ -3269,6 +3444,48 @@ autoplug_factories_cb (GstElement * decodebin, GstPad * pad,
GST_DEBUG_OBJECT (playbin, "found factories %p", mylist);
GST_PLUGIN_FEATURE_LIST_DEBUG (mylist);
+ /* check whether the caps are asking for a list of video_decoders */
+ tmp = mylist;
+ if (!gst_caps_is_any (caps)) {
+ for (; tmp; tmp = tmp->next) {
+ GstElementFactory *factory = (GstElementFactory *) tmp->data;
+
+ if (gst_element_factory_list_is_type (factory,
+ GST_ELEMENT_FACTORY_TYPE_DECODER |
+ GST_ELEMENT_FACTORY_TYPE_MEDIA_VIDEO |
+ GST_ELEMENT_FACTORY_TYPE_MEDIA_IMAGE)) {
+ need_video_dec_list = TRUE;
+ break;
+ }
+ }
+ }
+
+ if (need_video_dec_list && !group->velements) {
+ GList *video_sinks;
+ /* get a list of available video sink elements. The video_sink ref
+ * will get released once the grop->velements get freed */
+ video_sinks = gst_element_factory_list_get_elements
+ (GST_ELEMENT_FACTORY_TYPE_SINK |
+ GST_ELEMENT_FACTORY_TYPE_MEDIA_VIDEO |
+ GST_ELEMENT_FACTORY_TYPE_MEDIA_IMAGE, GST_RANK_MARGINAL);
+ video_sinks =
+ g_list_sort (video_sinks, gst_plugin_feature_rank_compare_func);
+
+ GST_SOURCE_GROUP_LOCK (group);
+ /* create the video element list */
+ group->velements = create_velements_list (mylist, video_sinks);
+ GST_SOURCE_GROUP_UNLOCK (group);
+
+ gst_plugin_feature_list_free (video_sinks);
+ }
+
+ if (group->velements && need_video_dec_list) {
+ GST_SOURCE_GROUP_LOCK (group);
+ /* sort mylist based on the video element list priority */
+ mylist = create_decoders_list (mylist, group->velements);
+ GST_SOURCE_GROUP_UNLOCK (group);
+ }
+
/* 2 additional elements for the already set audio/video sinks */
result = g_value_array_new (g_list_length (mylist) + 2);
@@ -3454,6 +3671,7 @@ sink_accepts_caps (GstElement * sink, GstCaps * caps)
return TRUE;
}
+
static GstStaticCaps raw_audio_caps = GST_STATIC_CAPS ("audio/x-raw");
static GstStaticCaps raw_video_caps = GST_STATIC_CAPS ("video/x-raw");
@@ -3470,6 +3688,8 @@ autoplug_select_cb (GstElement * decodebin, GstPad * pad,
const gchar *klass;
GstPlaySinkType type;
GstElement **sinkp;
+ GstVideoElement *ve = NULL;
+ GList *ve_list = NULL, *tmp = NULL;
playbin = group->playbin;
@@ -3490,62 +3710,112 @@ autoplug_select_cb (GstElement * decodebin, GstPad * pad,
GST_ELEMENT_FACTORY_TYPE_DECODER |
GST_ELEMENT_FACTORY_TYPE_MEDIA_AUDIO);
- /* If it is a decoder and we have a fixed sink for the media
- * type it outputs, check that the decoder is compatible with this sink */
- if ((isvideodec && group->video_sink) || (isaudiodec && group->audio_sink)) {
- gboolean compatible = TRUE;
- GstPad *sinkpad;
- GstCaps *caps;
- GstElement *sink;
+ ve_list = group->velements;
- if (isaudiodec)
- sink = group->audio_sink;
- else
- sink = group->video_sink;
-
- if ((sinkpad = gst_element_get_static_pad (sink, "sink"))) {
- GstPlayFlags flags = gst_play_bin_get_flags (playbin);
- GstCaps *raw_caps =
- (isaudiodec) ? gst_static_caps_get (&raw_audio_caps) :
- gst_static_caps_get (&raw_video_caps);
-
- caps = gst_pad_query_caps (sinkpad, NULL);
-
- /* If the sink supports raw audio/video, we first check
- * if the decoder could output any raw audio/video format
- * and assume it is compatible with the sink then. We don't
- * do a complete compatibility check here if converters
- * are plugged between the decoder and the sink because
- * the converters will convert between raw formats and
- * even if the decoder format is not supported by the decoder
- * a converter will convert it.
- *
- * We assume here that the converters can convert between
- * any raw format.
- */
- if ((isaudiodec && !(flags & GST_PLAY_FLAG_NATIVE_AUDIO)
- && gst_caps_can_intersect (caps, raw_caps)) || (!isaudiodec
- && !(flags & GST_PLAY_FLAG_NATIVE_VIDEO)
- && gst_caps_can_intersect (caps, raw_caps))) {
- compatible = gst_element_factory_can_src_any_caps (factory, raw_caps)
- || gst_element_factory_can_src_any_caps (factory, caps);
- } else {
- compatible = gst_element_factory_can_src_any_caps (factory, caps);
+ do {
+
+ if (!isvideodec && !isaudiodec)
+ return GST_AUTOPLUG_SELECT_TRY;
+
+ /* if it is a decoder and we don't have a fixed sink, then find out
+ * the matching video_sink from GstVideoElements list */
+ if (isvideodec && !group->video_sink && ve_list) {
+ tmp = group->velements;
+ for (; tmp; tmp = tmp->next) {
+ ve = (GstVideoElement *) tmp->data;
+ if (!ve->is_dirty && factory == ve->vdec) {
+ ve->is_dirty = 1;
+ break;
+ }
}
+ GST_SOURCE_GROUP_LOCK (group);
+ if (ve && ve->vsink)
+ group->video_sink =
+ gst_element_factory_create (ve->vsink, "video-sink");
+ GST_SOURCE_GROUP_UNLOCK (group);
+ }
- gst_object_unref (sinkpad);
- gst_caps_unref (caps);
+ /* If it is a decoder and we have a fixed sink for the media
+ * type it outputs, check that the decoder is compatible with this sink */
+ if ((isvideodec && group->video_sink) || (isaudiodec
+ && group->audio_sink)) {
+ gboolean compatible = TRUE;
+ GstPad *sinkpad;
+ GstCaps *caps;
+ GstElement *sink;
+
+ if (isaudiodec)
+ sink = group->audio_sink;
+ else
+ sink = group->video_sink;
+
+ if ((sinkpad = gst_element_get_static_pad (sink, "sink"))) {
+ GstPlayFlags flags = gst_play_bin_get_flags (playbin);
+ GstCaps *raw_caps =
+ (isaudiodec) ? gst_static_caps_get (&raw_audio_caps) :
+ gst_static_caps_get (&raw_video_caps);
+
+ caps = gst_pad_query_caps (sinkpad, NULL);
+
+ /* If the sink supports raw audio/video, we first check
+ * if the decoder could output any raw audio/video format
+ * and assume it is compatible with the sink then. We don't
+ * do a complete compatibility check here if converters
+ * are plugged between the decoder and the sink because
+ * the converters will convert between raw formats and
+ * even if the decoder format is not supported by the decoder
+ * a converter will convert it.
+ *
+ * We assume here that the converters can convert between
+ * any raw format.
+ */
+ if ((isaudiodec && !(flags & GST_PLAY_FLAG_NATIVE_AUDIO)
+ && gst_caps_can_intersect (caps, raw_caps)) || (!isaudiodec
+ && !(flags & GST_PLAY_FLAG_NATIVE_VIDEO)
+ && gst_caps_can_intersect (caps, raw_caps))) {
+ compatible =
+ gst_element_factory_can_src_any_caps (factory, raw_caps)
+ || gst_element_factory_can_src_any_caps (factory, caps);
+ } else {
+ compatible = gst_element_factory_can_src_any_caps (factory, caps);
+ }
+
+ gst_object_unref (sinkpad);
+ gst_caps_unref (caps);
+ }
+
+ if (compatible)
+ break;
+
+ GST_DEBUG_OBJECT (playbin, "%s not compatible with the fixed sink",
+ GST_OBJECT_NAME (factory));
+
+ if (!isvideodec)
+ return GST_AUTOPLUG_SELECT_SKIP;
+
+ if (group->video_sink != playbin->video_sink) {
+ GST_SOURCE_GROUP_LOCK (group);
+ gst_element_set_state (group->video_sink, GST_STATE_NULL);
+ gst_object_unref (group->video_sink);
+ group->video_sink = NULL;
+ GST_SOURCE_GROUP_UNLOCK (group);
+ } else
+ return GST_AUTOPLUG_SELECT_SKIP;
}
- if (compatible)
- return GST_AUTOPLUG_SELECT_TRY;
+ ve_list = ve_list->next;
+ } while (ve_list);
- GST_DEBUG_OBJECT (playbin, "%s not compatible with the fixed sink",
- GST_OBJECT_NAME (factory));
+ /* unset all dirty flags */
+ if (isvideodec) {
+ tmp = group->velements;
+ for (; tmp; tmp = tmp->next) {
+ ve = (GstVideoElement *) tmp->data;
+ ve->is_dirty = 1;
+ }
+ }
- return GST_AUTOPLUG_SELECT_SKIP;
- } else
- return GST_AUTOPLUG_SELECT_TRY;
+ return GST_AUTOPLUG_SELECT_TRY;
}
/* it's a sink, see if an instance of it actually works */