static GstCaps * _set_caps_features_with_passthrough (const GstCaps * caps, const gchar * feature_name, GstCapsFeatures * passthrough) { guint i, j, m, n; GstCaps *tmp; tmp = gst_caps_new_empty (); n = gst_caps_get_size (caps); for (i = 0; i < n; i++) { GstCapsFeatures *features, *orig_features; GstStructure *s = gst_caps_get_structure (caps, i); orig_features = gst_caps_get_features (caps, i); features = gst_caps_features_new (feature_name, NULL); if (gst_caps_features_is_any (orig_features)) { /* if we have any features, we add both the features with and without @passthrough */ gst_caps_append_structure_full (tmp, gst_structure_copy (s), gst_caps_features_copy (features)); m = gst_caps_features_get_size (passthrough); for (j = 0; j < m; j++) { const gchar *feature = gst_caps_features_get_nth (passthrough, j); /* if we already have the features */ if (gst_caps_features_contains (features, feature)) continue; gst_caps_features_add (features, feature); } } else { m = gst_caps_features_get_size (orig_features); for (j = 0; j < m; j++) { const gchar *feature = gst_caps_features_get_nth (orig_features, j); /* if we already have the features */ if (gst_caps_features_contains (features, feature)) continue; if (g_strcmp0 (feature, GST_CAPS_FEATURE_MEMORY_SYSTEM_MEMORY) == 0) continue; if (gst_caps_features_contains (passthrough, feature)) { gst_caps_features_add (features, feature); } } } gst_caps_append_structure_full (tmp, gst_structure_copy (s), features); } return tmp; }
static GstCaps * gst_gl_mixer_set_caps_features (const GstCaps * caps, const gchar * feature_name) { GstCaps *tmp = gst_caps_copy (caps); guint n = gst_caps_get_size (tmp); guint i = 0; for (i = 0; i < n; i++) { GstCapsFeatures *features = gst_caps_get_features (tmp, i); if (features) { guint n_f = gst_caps_features_get_size (features); guint j = 0; for (j = 0; j < n_f; j++) { gst_caps_features_remove_id (features, gst_caps_features_get_nth_id (features, j)); } } gst_caps_features_add (features, feature_name); gst_caps_set_simple (tmp, "format", G_TYPE_STRING, "RGBA", NULL); } return tmp; }
/** * gst_caps_features_new_valist: * @feature1: name of first feature to set * @varargs: variable argument list * * Creates a new #GstCapsFeatures with the given features. * * Free-function: gst_caps_features_free * * Returns: (transfer full): a new, empty #GstCapsFeatures * * Since: 1.2 */ GstCapsFeatures * gst_caps_features_new_valist (const gchar * feature1, va_list varargs) { GstCapsFeatures *features; g_return_val_if_fail (feature1 != NULL, NULL); features = gst_caps_features_new_empty (); while (feature1) { gst_caps_features_add (features, feature1); feature1 = va_arg (varargs, const gchar *); } return features; }
GstCaps * gst_gl_overlay_compositor_add_caps (GstCaps * caps) { GstCaps *composition_caps; int i; composition_caps = gst_caps_copy (caps); for (i = 0; i < gst_caps_get_size (composition_caps); i++) { GstCapsFeatures *f = gst_caps_get_features (composition_caps, i); gst_caps_features_add (f, GST_CAPS_FEATURE_META_GST_VIDEO_OVERLAY_COMPOSITION); } caps = gst_caps_merge (composition_caps, caps); return caps; }
/** * gst_dvbsub_overlay_add_feature_and_intersect: * * Creates a new #GstCaps containing the (given caps + * given caps feature) + (given caps intersected by the * given filter). * * Returns: the new #GstCaps */ static GstCaps * gst_dvbsub_overlay_add_feature_and_intersect (GstCaps * caps, const gchar * feature, GstCaps * filter) { int i, caps_size; GstCaps *new_caps; new_caps = gst_caps_copy (caps); caps_size = gst_caps_get_size (new_caps); for (i = 0; i < caps_size; i++) { GstCapsFeatures *features = gst_caps_get_features (new_caps, i); if (!gst_caps_features_is_any (features)) { gst_caps_features_add (features, feature); } } gst_caps_append (new_caps, gst_caps_intersect_full (caps, filter, GST_CAPS_INTERSECT_FIRST)); return new_caps; }
static GstCaps * _set_caps_features_with_passthrough (const GstCaps * caps, const gchar * feature_name, GstCapsFeatures * passthrough) { guint i, j, m, n; GstCaps *tmp; tmp = gst_caps_copy (caps); n = gst_caps_get_size (caps); for (i = 0; i < n; i++) { GstCapsFeatures *features, *orig_features; orig_features = gst_caps_get_features (caps, i); features = gst_caps_features_new (feature_name, NULL); m = gst_caps_features_get_size (orig_features); for (j = 0; j < m; j++) { const gchar *feature = gst_caps_features_get_nth (orig_features, j); /* if we already have the features */ if (gst_caps_features_contains (features, feature)) continue; if (g_strcmp0 (feature, GST_CAPS_FEATURE_MEMORY_SYSTEM_MEMORY) == 0) continue; if (passthrough && gst_caps_features_contains (passthrough, feature)) { gst_caps_features_add (features, feature); } } gst_caps_set_features (tmp, i, features); } return tmp; }
GstCaps * gst_gl_caps_replace_all_caps_features (const GstCaps * caps, const gchar * feature_name) { GstCaps *tmp = gst_caps_copy (caps); guint n = gst_caps_get_size (tmp); guint i = 0; for (i = 0; i < n; i++) { GstCapsFeatures *features = gst_caps_get_features (tmp, i); if (features) { guint n_f = gst_caps_features_get_size (features); guint j = 0; for (j = 0; j < n_f; j++) { gst_caps_features_remove_id (features, gst_caps_features_get_nth_id (features, j)); } } gst_caps_features_add (features, feature_name); } return tmp; }
/** * gst_caps_features_from_string: * @features: a string representation of a #GstCapsFeatures. * * Creates a #GstCapsFeatures from a string representation. * * Free-function: gst_caps_features_free * * Returns: (transfer full): a new #GstCapsFeatures or NULL when the string could * not be parsed. Free with gst_caps_features_free() after use. * * Since: 1.2 */ GstCapsFeatures * gst_caps_features_from_string (const gchar * features) { GstCapsFeatures *ret; gboolean escape = FALSE; const gchar *features_orig = features; const gchar *feature; ret = gst_caps_features_new_empty (); if (!features || *features == '\0') return ret; if (strcmp (features, "ANY") == 0) { ret->is_any = TRUE; return ret; } /* Skip trailing spaces */ while (*features == ' ') features++; feature = features; while (TRUE) { gchar c = *features; if (c == '\\') { escape = TRUE; features++; continue; } else if ((!escape && c == ',') || c == '\0') { guint len = features - feature + 1; gchar *tmp; gchar *p; if (len == 1) { g_warning ("Failed deserialize caps features '%s'", features_orig); gst_caps_features_free (ret); return NULL; } tmp = g_malloc (len); memcpy (tmp, feature, len - 1); tmp[len - 1] = '\0'; p = tmp + len - 1; while (*p == ' ') { *p = '\0'; p--; } if (strstr (tmp, " ") != NULL || *tmp == '\0') { g_free (tmp); g_warning ("Failed deserialize caps features '%s'", features_orig); gst_caps_features_free (ret); return NULL; } gst_caps_features_add (ret, tmp); g_free (tmp); if (c == '\0') break; /* Skip to the next value */ features++; while (*features == ' ') features++; feature = features; } else { escape = FALSE; features++; } } return ret; }
/* only negotiate/query video overlay composition support for now */ static gboolean gst_dvbsub_overlay_negotiate (GstDVBSubOverlay * overlay, GstCaps * caps) { gboolean ret; gboolean attach = FALSE; gboolean caps_has_meta = TRUE; GstCapsFeatures *f; GST_DEBUG_OBJECT (overlay, "performing negotiation"); if (!caps) { caps = gst_pad_get_current_caps (overlay->srcpad); } else { gst_caps_ref (caps); } if (!caps || gst_caps_is_empty (caps)) goto no_format; /* Try to use the overlay meta if possible */ f = gst_caps_get_features (caps, 0); /* if the caps doesn't have the overlay meta, we query if downstream * accepts it before trying the version without the meta * If upstream already is using the meta then we can only use it */ if (!f || !gst_caps_features_contains (f, GST_CAPS_FEATURE_META_GST_VIDEO_OVERLAY_COMPOSITION)) { GstCaps *overlay_caps; GstCaps *peercaps; /* In this case we added the meta, but we can work without it * so preserve the original caps so we can use it as a fallback */ overlay_caps = gst_caps_copy (caps); f = gst_caps_get_features (overlay_caps, 0); gst_caps_features_add (f, GST_CAPS_FEATURE_META_GST_VIDEO_OVERLAY_COMPOSITION); /* FIXME: We should probably check if downstream *prefers* the * overlay meta, and only enforce usage of it if we can't handle * the format ourselves and thus would have to drop the overlays. * Otherwise we should prefer what downstream wants here. */ peercaps = gst_pad_peer_query_caps (overlay->srcpad, NULL); caps_has_meta = gst_caps_can_intersect (peercaps, overlay_caps); gst_caps_unref (peercaps); GST_DEBUG_OBJECT (overlay, "Downstream accepts the overlay meta: %d", caps_has_meta); if (caps_has_meta) { gst_caps_unref (caps); caps = overlay_caps; } else { /* fallback to the original */ gst_caps_unref (overlay_caps); caps_has_meta = FALSE; } } GST_DEBUG_OBJECT (overlay, "Using caps %" GST_PTR_FORMAT, caps); ret = gst_pad_set_caps (overlay->srcpad, caps); if (ret) { GstQuery *query; /* find supported meta */ query = gst_query_new_allocation (caps, FALSE); if (!gst_pad_peer_query (overlay->srcpad, query)) { /* no problem, we use the query defaults */ GST_DEBUG_OBJECT (overlay, "ALLOCATION query failed"); } if (caps_has_meta && gst_query_find_allocation_meta (query, GST_VIDEO_OVERLAY_COMPOSITION_META_API_TYPE, NULL)) attach = TRUE; overlay->attach_compo_to_buffer = attach; gst_query_unref (query); } gst_caps_unref (caps); return ret; no_format: { if (caps) gst_caps_unref (caps); return FALSE; } }
/* Based on gstbasetextoverlay.c */ static gboolean gst_overlay_composition_negotiate (GstOverlayComposition * self, GstCaps * caps) { gboolean upstream_has_meta = FALSE; gboolean caps_has_meta = FALSE; gboolean alloc_has_meta = FALSE; gboolean attach = FALSE; gboolean ret = TRUE; guint width, height; GstCapsFeatures *f; GstCaps *overlay_caps; GstQuery *query; guint alloc_index; GST_DEBUG_OBJECT (self, "performing negotiation"); /* Clear any pending reconfigure to avoid negotiating twice */ gst_pad_check_reconfigure (self->srcpad); self->window_width = self->window_height = 0; if (!caps) caps = gst_pad_get_current_caps (self->sinkpad); else gst_caps_ref (caps); if (!caps || gst_caps_is_empty (caps)) goto no_format; /* Check if upstream caps have meta */ if ((f = gst_caps_get_features (caps, 0))) { upstream_has_meta = gst_caps_features_contains (f, GST_CAPS_FEATURE_META_GST_VIDEO_OVERLAY_COMPOSITION); } /* Initialize dimensions */ width = self->info.width; height = self->info.height; if (upstream_has_meta) { overlay_caps = gst_caps_ref (caps); } else { GstCaps *peercaps; /* BaseTransform requires caps for the allocation query to work */ overlay_caps = gst_caps_copy (caps); f = gst_caps_get_features (overlay_caps, 0); gst_caps_features_add (f, GST_CAPS_FEATURE_META_GST_VIDEO_OVERLAY_COMPOSITION); /* Then check if downstream accept overlay composition in caps */ /* FIXME: We should probably check if downstream *prefers* the * overlay meta, and only enforce usage of it if we can't handle * the format ourselves and thus would have to drop the overlays. * Otherwise we should prefer what downstream wants here. */ peercaps = gst_pad_peer_query_caps (self->srcpad, overlay_caps); caps_has_meta = !gst_caps_is_empty (peercaps); gst_caps_unref (peercaps); GST_DEBUG_OBJECT (self, "caps have overlay meta %d", caps_has_meta); } if (upstream_has_meta || caps_has_meta) { /* Send caps immediatly, it's needed by GstBaseTransform to get a reply * from allocation query */ ret = gst_pad_set_caps (self->srcpad, overlay_caps); /* First check if the allocation meta has compositon */ query = gst_query_new_allocation (overlay_caps, FALSE); if (!gst_pad_peer_query (self->srcpad, query)) { /* no problem, we use the query defaults */ GST_DEBUG_OBJECT (self, "ALLOCATION query failed"); /* In case we were flushing, mark reconfigure and fail this method, * will make it retry */ if (GST_PAD_IS_FLUSHING (self->srcpad)) ret = FALSE; } alloc_has_meta = gst_query_find_allocation_meta (query, GST_VIDEO_OVERLAY_COMPOSITION_META_API_TYPE, &alloc_index); GST_DEBUG_OBJECT (self, "sink alloc has overlay meta %d", alloc_has_meta); if (alloc_has_meta) { const GstStructure *params; gst_query_parse_nth_allocation_meta (query, alloc_index, ¶ms); if (params) { if (gst_structure_get (params, "width", G_TYPE_UINT, &width, "height", G_TYPE_UINT, &height, NULL)) { GST_DEBUG_OBJECT (self, "received window size: %dx%d", width, height); g_assert (width != 0 && height != 0); } } } gst_query_unref (query); } /* Update render size if needed */ self->window_width = width; self->window_height = height; /* For backward compatbility, we will prefer bliting if downstream * allocation does not support the meta. In other case we will prefer * attaching, and will fail the negotiation in the unlikely case we are * force to blit, but format isn't supported. */ if (upstream_has_meta) { attach = TRUE; } else if (caps_has_meta) { if (alloc_has_meta) { attach = TRUE; } else { /* Don't attach unless we cannot handle the format */ attach = !can_blend_caps (caps); } } else { ret = can_blend_caps (caps); } /* If we attach, then pick the overlay caps */ if (attach) { GST_DEBUG_OBJECT (self, "Using caps %" GST_PTR_FORMAT, overlay_caps); /* Caps where already sent */ } else if (ret) { GST_DEBUG_OBJECT (self, "Using caps %" GST_PTR_FORMAT, caps); ret = gst_pad_set_caps (self->srcpad, caps); } self->attach_compo_to_buffer = attach; if (!ret) { GST_DEBUG_OBJECT (self, "negotiation failed, schedule reconfigure"); gst_pad_mark_reconfigure (self->srcpad); } g_signal_emit (self, overlay_composition_signals[SIGNAL_CAPS_CHANGED], 0, caps, self->window_width, self->window_height, NULL); gst_caps_unref (overlay_caps); gst_caps_unref (caps); return ret; no_format: { if (caps) gst_caps_unref (caps); gst_pad_mark_reconfigure (self->srcpad); return FALSE; } }