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