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