1 /* GStreamer Element
2 * Copyright (C) 2006-2009 Mark Nauwelaerts <mnauw@users.sourceforge.net>
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
13 *
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 * Boston, MA 02110-1307, USA.
18 */
19
20 /**
21 * SECTION:element-capssetter
22 *
23 * Sets or merges caps on a stream's buffers. That is, a buffer's caps are
24 * updated using (fields of) #GstCapsSetter:caps. Note that this may contain
25 * multiple structures (though not likely recommended), but each of these must
26 * be fixed (or will otherwise be rejected).
27 *
28 * If #GstCapsSetter:join is %TRUE, then the incoming caps' mime-type is
29 * compared to the mime-type(s) of provided caps and only matching structure(s)
30 * are considered for updating.
31 *
32 * If #GstCapsSetter:replace is %TRUE, then any caps update is preceded by
33 * clearing existing fields, making provided fields (as a whole) replace
34 * incoming ones. Otherwise, no clearing is performed, in which case provided
35 * fields are added/merged onto incoming caps
36 *
37 * Although this element might mainly serve as debug helper,
38 * it can also practically be used to correct a faulty pixel-aspect-ratio,
39 * or to modify a yuv fourcc value to effectively swap chroma components or such
40 * alike.
41 */
42
43
44 #ifdef HAVE_CONFIG_H
45 #include "config.h"
46 #endif
47
48 #include "gstcapssetter.h"
49
50 #include <string.h>
51
52
53 GST_DEBUG_CATEGORY_STATIC (caps_setter_debug);
54 #define GST_CAT_DEFAULT caps_setter_debug
55
56
57 /* signals and args */
58 enum
59 {
60 /* FILL ME */
61 LAST_SIGNAL
62 };
63
64 enum
65 {
66 PROP_0,
67 PROP_CAPS,
68 PROP_JOIN,
69 PROP_REPLACE
70 /* FILL ME */
71 };
72
73 #define DEFAULT_JOIN TRUE
74 #define DEFAULT_REPLACE FALSE
75
76 static GstStaticPadTemplate gst_caps_setter_src_template =
77 GST_STATIC_PAD_TEMPLATE (GST_BASE_TRANSFORM_SRC_NAME,
78 GST_PAD_SRC,
79 GST_PAD_ALWAYS,
80 GST_STATIC_CAPS_ANY);
81
82 static GstStaticPadTemplate gst_caps_setter_sink_template =
83 GST_STATIC_PAD_TEMPLATE (GST_BASE_TRANSFORM_SINK_NAME,
84 GST_PAD_SINK,
85 GST_PAD_ALWAYS,
86 GST_STATIC_CAPS_ANY);
87
88
89 static gboolean gst_caps_setter_transform_size (GstBaseTransform * trans,
90 GstPadDirection direction, GstCaps * caps, gsize size,
91 GstCaps * othercaps, gsize * othersize);
92 static GstCaps *gst_caps_setter_transform_caps (GstBaseTransform * trans,
93 GstPadDirection direction, GstCaps * caps, GstCaps * cfilter);
94 static GstFlowReturn gst_caps_setter_transform_ip (GstBaseTransform * btrans,
95 GstBuffer * in);
96
97 static void gst_caps_setter_finalize (GObject * object);
98
99 static void gst_caps_setter_set_property (GObject * object, guint prop_id,
100 const GValue * value, GParamSpec * pspec);
101 static void gst_caps_setter_get_property (GObject * object, guint prop_id,
102 GValue * value, GParamSpec * pspec);
103
104 #define gst_caps_setter_parent_class parent_class
105 G_DEFINE_TYPE (GstCapsSetter, gst_caps_setter, GST_TYPE_BASE_TRANSFORM);
106
107 static void
gst_caps_setter_class_init(GstCapsSetterClass * g_class)108 gst_caps_setter_class_init (GstCapsSetterClass * g_class)
109 {
110 GObjectClass *gobject_class = (GObjectClass *) g_class;
111 GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
112 GstBaseTransformClass *trans_class = (GstBaseTransformClass *) g_class;
113
114 GST_DEBUG_CATEGORY_INIT (caps_setter_debug, "capssetter", 0, "capssetter");
115
116 gobject_class->set_property = gst_caps_setter_set_property;
117 gobject_class->get_property = gst_caps_setter_get_property;
118
119 gobject_class->finalize = gst_caps_setter_finalize;
120
121 g_object_class_install_property (gobject_class, PROP_CAPS,
122 g_param_spec_boxed ("caps", "Merge caps",
123 "Merge these caps (thereby overwriting) in the stream",
124 GST_TYPE_CAPS, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
125 g_object_class_install_property (gobject_class, PROP_JOIN,
126 g_param_spec_boolean ("join", "Join",
127 "Match incoming caps' mime-type to mime-type of provided caps",
128 DEFAULT_JOIN, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
129 g_object_class_install_property (gobject_class, PROP_REPLACE,
130 g_param_spec_boolean ("replace", "Replace",
131 "Drop fields of incoming caps", DEFAULT_REPLACE,
132 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
133
134 gst_element_class_set_static_metadata (element_class, "CapsSetter",
135 "Generic",
136 "Set/merge caps on stream",
137 "Mark Nauwelaerts <mnauw@users.sourceforge.net>");
138
139 gst_element_class_add_static_pad_template (element_class,
140 &gst_caps_setter_sink_template);
141 gst_element_class_add_static_pad_template (element_class,
142 &gst_caps_setter_src_template);
143
144 trans_class->transform_size =
145 GST_DEBUG_FUNCPTR (gst_caps_setter_transform_size);
146 trans_class->transform_caps =
147 GST_DEBUG_FUNCPTR (gst_caps_setter_transform_caps);
148 /* dummy seems needed */
149 trans_class->transform_ip = GST_DEBUG_FUNCPTR (gst_caps_setter_transform_ip);
150 }
151
152 static void
gst_caps_setter_init(GstCapsSetter * filter)153 gst_caps_setter_init (GstCapsSetter * filter)
154 {
155 filter->caps = gst_caps_new_any ();
156 filter->join = DEFAULT_JOIN;
157 filter->replace = DEFAULT_REPLACE;
158 }
159
160 static void
gst_caps_setter_finalize(GObject * object)161 gst_caps_setter_finalize (GObject * object)
162 {
163 GstCapsSetter *filter = GST_CAPS_SETTER (object);
164
165 gst_caps_replace (&filter->caps, NULL);
166
167 G_OBJECT_CLASS (parent_class)->finalize (object);
168 }
169
170 static gboolean
gst_caps_setter_transform_size(GstBaseTransform * trans,GstPadDirection direction,GstCaps * caps,gsize size,GstCaps * othercaps,gsize * othersize)171 gst_caps_setter_transform_size (GstBaseTransform * trans,
172 GstPadDirection direction, GstCaps * caps, gsize size,
173 GstCaps * othercaps, gsize * othersize)
174 {
175 *othersize = size;
176
177 return TRUE;
178 }
179
180 static GstCaps *
gst_caps_setter_transform_caps(GstBaseTransform * trans,GstPadDirection direction,GstCaps * caps,GstCaps * cfilter)181 gst_caps_setter_transform_caps (GstBaseTransform * trans,
182 GstPadDirection direction, GstCaps * caps, GstCaps * cfilter)
183 {
184 GstCapsSetter *filter = GST_CAPS_SETTER (trans);
185 GstCaps *ret = NULL, *filter_caps = NULL;
186 GstStructure *structure, *merge;
187 const gchar *name;
188 gint i, j, k;
189
190 GST_DEBUG_OBJECT (trans,
191 "receiving caps: %" GST_PTR_FORMAT ", with filter: %" GST_PTR_FORMAT,
192 caps, cfilter);
193
194 /* pass filter caps upstream, or any if no filter */
195 if (direction != GST_PAD_SINK) {
196 if (!cfilter || gst_caps_is_empty (cfilter)) {
197 return gst_caps_ref (GST_CAPS_ANY);
198 } else {
199 return gst_caps_ref (cfilter);
200 }
201 }
202
203 ret = gst_caps_copy (caps);
204
205 GST_OBJECT_LOCK (filter);
206 filter_caps = gst_caps_ref (filter->caps);
207 GST_OBJECT_UNLOCK (filter);
208
209 for (k = 0; k < gst_caps_get_size (ret); k++) {
210 structure = gst_caps_get_structure (ret, k);
211 name = gst_structure_get_name (structure);
212
213 for (i = 0; i < gst_caps_get_size (filter_caps); ++i) {
214 merge = gst_caps_get_structure (filter_caps, i);
215 if (gst_structure_has_name (merge, name) || !filter->join) {
216
217 if (!filter->join)
218 gst_structure_set_name (structure, gst_structure_get_name (merge));
219
220 if (filter->replace)
221 gst_structure_remove_all_fields (structure);
222
223 for (j = 0; j < gst_structure_n_fields (merge); ++j) {
224 const gchar *fname;
225
226 fname = gst_structure_nth_field_name (merge, j);
227 gst_structure_set_value (structure, fname,
228 gst_structure_get_value (merge, fname));
229 }
230 }
231 }
232 }
233
234 GST_DEBUG_OBJECT (trans, "returning caps: %" GST_PTR_FORMAT, ret);
235
236 gst_caps_unref (filter_caps);
237
238 return ret;
239 }
240
241 static GstFlowReturn
gst_caps_setter_transform_ip(GstBaseTransform * btrans,GstBuffer * in)242 gst_caps_setter_transform_ip (GstBaseTransform * btrans, GstBuffer * in)
243 {
244 return GST_FLOW_OK;
245 }
246
247 static gboolean
gst_caps_is_fixed_foreach(GQuark field_id,const GValue * value,gpointer unused)248 gst_caps_is_fixed_foreach (GQuark field_id, const GValue * value,
249 gpointer unused)
250 {
251 return gst_value_is_fixed (value);
252 }
253
254 static void
gst_caps_setter_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)255 gst_caps_setter_set_property (GObject * object, guint prop_id,
256 const GValue * value, GParamSpec * pspec)
257 {
258 GstCapsSetter *filter = GST_CAPS_SETTER (object);
259
260 switch (prop_id) {
261 case PROP_CAPS:{
262 GstCaps *new_caps;
263 const GstCaps *new_caps_val = gst_value_get_caps (value);
264 gint i;
265
266 if (new_caps_val == NULL) {
267 new_caps = gst_caps_new_any ();
268 } else {
269 new_caps = gst_caps_copy (new_caps_val);
270 }
271
272 for (i = 0; new_caps && (i < gst_caps_get_size (new_caps)); ++i) {
273 GstStructure *s;
274
275 s = gst_caps_get_structure (new_caps, i);
276 if (!gst_structure_foreach (s, gst_caps_is_fixed_foreach, NULL)) {
277 GST_ERROR_OBJECT (filter, "rejected unfixed caps: %" GST_PTR_FORMAT,
278 new_caps);
279 gst_caps_unref (new_caps);
280 new_caps = NULL;
281 break;
282 }
283 }
284
285 if (new_caps) {
286 GST_OBJECT_LOCK (filter);
287 gst_caps_replace (&filter->caps, new_caps);
288 /* drop extra ref */
289 gst_caps_unref (new_caps);
290 GST_OBJECT_UNLOCK (filter);
291
292 GST_DEBUG_OBJECT (filter, "set new caps %" GST_PTR_FORMAT, new_caps);
293 }
294
295 /* try to activate these new caps next time around */
296 gst_base_transform_reconfigure_src (GST_BASE_TRANSFORM (filter));
297 break;
298 }
299 case PROP_JOIN:
300 filter->join = g_value_get_boolean (value);
301 break;
302 case PROP_REPLACE:
303 filter->replace = g_value_get_boolean (value);
304 break;
305 default:
306 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
307 break;
308 }
309 }
310
311 static void
gst_caps_setter_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)312 gst_caps_setter_get_property (GObject * object, guint prop_id, GValue * value,
313 GParamSpec * pspec)
314 {
315 GstCapsSetter *filter = GST_CAPS_SETTER (object);
316
317 switch (prop_id) {
318 case PROP_CAPS:
319 gst_value_set_caps (value, filter->caps);
320 break;
321 case PROP_JOIN:
322 g_value_set_boolean (value, filter->join);
323 break;
324 case PROP_REPLACE:
325 g_value_set_boolean (value, filter->replace);
326 break;
327 default:
328 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
329 break;
330 }
331 }
332