Branch data Line data Source code
1 : : /* GStreamer
2 : : * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
3 : : * 2000 Wim Taymans <wtay@chello.be>
4 : : * 2005 Wim Taymans <wim@fluendo.com>
5 : : * 2005 David Schleef <ds@schleef.org>
6 : : *
7 : : * This library is free software; you can redistribute it and/or
8 : : * modify it under the terms of the GNU Library General Public
9 : : * License as published by the Free Software Foundation; either
10 : : * version 2 of the License, or (at your option) any later version.
11 : : *
12 : : * This library is distributed in the hope that it will be useful,
13 : : * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 : : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 : : * Library General Public License for more details.
16 : : *
17 : : * You should have received a copy of the GNU Library General Public
18 : : * License along with this library; if not, write to the
19 : : * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20 : : * Boston, MA 02111-1307, USA.
21 : : */
22 : : /**
23 : : * SECTION:element-capsfilter
24 : : *
25 : : * The element does not modify data as such, but can enforce limitations on the
26 : : * data format.
27 : : *
28 : : * <refsect2>
29 : : * <title>Example launch line</title>
30 : : * |[
31 : : * gst-launch videotestsrc ! video/x-raw-gray ! ffmpegcolorspace ! autovideosink
32 : : * ]| Limits acceptable video from videotestsrc to be grayscale.
33 : : * </refsect2>
34 : : */
35 : :
36 : : #ifdef HAVE_CONFIG_H
37 : : #include "config.h"
38 : : #endif
39 : :
40 : : #include "../../gst/gst-i18n-lib.h"
41 : : #include "gstcapsfilter.h"
42 : :
43 : : enum
44 : : {
45 : : PROP_0,
46 : : PROP_FILTER_CAPS
47 : : };
48 : :
49 : :
50 : : static GstStaticPadTemplate sinktemplate = GST_STATIC_PAD_TEMPLATE ("sink",
51 : : GST_PAD_SINK,
52 : : GST_PAD_ALWAYS,
53 : : GST_STATIC_CAPS_ANY);
54 : :
55 : : static GstStaticPadTemplate srctemplate = GST_STATIC_PAD_TEMPLATE ("src",
56 : : GST_PAD_SRC,
57 : : GST_PAD_ALWAYS,
58 : : GST_STATIC_CAPS_ANY);
59 : :
60 : :
61 : : GST_DEBUG_CATEGORY_STATIC (gst_capsfilter_debug);
62 : : #define GST_CAT_DEFAULT gst_capsfilter_debug
63 : :
64 : : #define _do_init(bla) \
65 : : GST_DEBUG_CATEGORY_INIT (gst_capsfilter_debug, "capsfilter", 0, \
66 : : "capsfilter element");
67 : :
68 [ + + ][ + - ]: 233 : GST_BOILERPLATE_FULL (GstCapsFilter, gst_capsfilter, GstBaseTransform,
69 : 233 : GST_TYPE_BASE_TRANSFORM, _do_init);
70 : :
71 : :
72 : : static void gst_capsfilter_set_property (GObject * object, guint prop_id,
73 : : const GValue * value, GParamSpec * pspec);
74 : : static void gst_capsfilter_get_property (GObject * object, guint prop_id,
75 : : GValue * value, GParamSpec * pspec);
76 : : static void gst_capsfilter_dispose (GObject * object);
77 : :
78 : : static GstCaps *gst_capsfilter_transform_caps (GstBaseTransform * base,
79 : : GstPadDirection direction, GstCaps * caps);
80 : : static gboolean gst_capsfilter_accept_caps (GstBaseTransform * base,
81 : : GstPadDirection direction, GstCaps * caps);
82 : : static GstFlowReturn gst_capsfilter_transform_ip (GstBaseTransform * base,
83 : : GstBuffer * buf);
84 : : static GstFlowReturn gst_capsfilter_prepare_buf (GstBaseTransform * trans,
85 : : GstBuffer * input, gint size, GstCaps * caps, GstBuffer ** buf);
86 : :
87 : : static void
88 : 14 : gst_capsfilter_base_init (gpointer g_class)
89 : : {
90 : 14 : GstElementClass *gstelement_class = GST_ELEMENT_CLASS (g_class);
91 : :
92 : 14 : gst_element_class_set_details_simple (gstelement_class,
93 : : "CapsFilter",
94 : : "Generic",
95 : : "Pass data without modification, limiting formats",
96 : : "David Schleef <ds@schleef.org>");
97 : 14 : gst_element_class_add_pad_template (gstelement_class,
98 : : gst_static_pad_template_get (&srctemplate));
99 : 14 : gst_element_class_add_pad_template (gstelement_class,
100 : : gst_static_pad_template_get (&sinktemplate));
101 : 14 : }
102 : :
103 : : static void
104 : 14 : gst_capsfilter_class_init (GstCapsFilterClass * klass)
105 : : {
106 : : GObjectClass *gobject_class;
107 : : GstBaseTransformClass *trans_class;
108 : :
109 : 14 : gobject_class = G_OBJECT_CLASS (klass);
110 : 14 : gobject_class->set_property = gst_capsfilter_set_property;
111 : 14 : gobject_class->get_property = gst_capsfilter_get_property;
112 : 14 : gobject_class->dispose = gst_capsfilter_dispose;
113 : :
114 : 14 : g_object_class_install_property (gobject_class, PROP_FILTER_CAPS,
115 : 14 : g_param_spec_boxed ("caps", _("Filter caps"),
116 : 14 : _("Restrict the possible allowed capabilities (NULL means ANY). "
117 : : "Setting this property takes a reference to the supplied GstCaps "
118 : : "object."), GST_TYPE_CAPS,
119 : : G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
120 : :
121 : 14 : trans_class = GST_BASE_TRANSFORM_CLASS (klass);
122 : 14 : trans_class->transform_caps =
123 : 14 : GST_DEBUG_FUNCPTR (gst_capsfilter_transform_caps);
124 : 14 : trans_class->transform_ip = GST_DEBUG_FUNCPTR (gst_capsfilter_transform_ip);
125 : 14 : trans_class->accept_caps = GST_DEBUG_FUNCPTR (gst_capsfilter_accept_caps);
126 : 14 : trans_class->prepare_output_buffer =
127 : 14 : GST_DEBUG_FUNCPTR (gst_capsfilter_prepare_buf);
128 : 14 : }
129 : :
130 : : static void
131 : 16 : gst_capsfilter_init (GstCapsFilter * filter, GstCapsFilterClass * g_class)
132 : : {
133 : 16 : GstBaseTransform *trans = GST_BASE_TRANSFORM (filter);
134 : 16 : gst_base_transform_set_gap_aware (trans, TRUE);
135 : 16 : filter->filter_caps = gst_caps_new_any ();
136 : 16 : }
137 : :
138 : : static gboolean
139 : 0 : copy_func (GQuark field_id, const GValue * value, GstStructure * dest)
140 : : {
141 : 0 : gst_structure_id_set_value (dest, field_id, value);
142 : :
143 : 0 : return TRUE;
144 : : }
145 : :
146 : : static void
147 : 13 : gst_capsfilter_set_property (GObject * object, guint prop_id,
148 : : const GValue * value, GParamSpec * pspec)
149 : : {
150 : 13 : GstCapsFilter *capsfilter = GST_CAPSFILTER (object);
151 : :
152 [ + - ]: 13 : switch (prop_id) {
153 : : case PROP_FILTER_CAPS:{
154 : : GstCaps *new_caps;
155 : : GstCaps *old_caps, *suggest, *nego;
156 : 13 : const GstCaps *new_caps_val = gst_value_get_caps (value);
157 : :
158 [ - + ]: 13 : if (new_caps_val == NULL) {
159 : 0 : new_caps = gst_caps_new_any ();
160 : : } else {
161 : 13 : new_caps = (GstCaps *) new_caps_val;
162 : 13 : gst_caps_ref (new_caps);
163 : : }
164 : :
165 : 13 : GST_OBJECT_LOCK (capsfilter);
166 : 13 : old_caps = capsfilter->filter_caps;
167 : 13 : capsfilter->filter_caps = new_caps;
168 : 13 : GST_OBJECT_UNLOCK (capsfilter);
169 : :
170 : 13 : gst_caps_unref (old_caps);
171 : :
172 [ - + ]: 13 : GST_DEBUG_OBJECT (capsfilter, "set new caps %" GST_PTR_FORMAT, new_caps);
173 : :
174 : : /* filter the currently negotiated format against the new caps */
175 : 13 : GST_OBJECT_LOCK (GST_BASE_TRANSFORM_SINK_PAD (object));
176 : 13 : nego = GST_PAD_CAPS (GST_BASE_TRANSFORM_SINK_PAD (object));
177 [ - + ]: 13 : if (nego) {
178 [ # # ]: 0 : GST_DEBUG_OBJECT (capsfilter, "we had negotiated caps %" GST_PTR_FORMAT,
179 : : nego);
180 : :
181 [ # # ]: 0 : if (G_UNLIKELY (gst_caps_is_any (new_caps))) {
182 [ # # ]: 0 : GST_DEBUG_OBJECT (capsfilter, "not settings any suggestion");
183 : :
184 : 0 : suggest = NULL;
185 : : } else {
186 : : GstStructure *s1, *s2;
187 : :
188 : : /* first check if the name is the same */
189 : 0 : s1 = gst_caps_get_structure (nego, 0);
190 : 0 : s2 = gst_caps_get_structure (new_caps, 0);
191 : :
192 [ # # ]: 0 : if (gst_structure_get_name_id (s1) == gst_structure_get_name_id (s2)) {
193 : : /* same name, copy all fields from the new caps into the previously
194 : : * negotiated caps */
195 : 0 : suggest = gst_caps_copy (nego);
196 : 0 : s1 = gst_caps_get_structure (suggest, 0);
197 : 0 : gst_structure_foreach (s2, (GstStructureForeachFunc) copy_func, s1);
198 [ # # ]: 0 : GST_DEBUG_OBJECT (capsfilter, "copied structure fields");
199 : : } else {
200 [ # # ]: 0 : GST_DEBUG_OBJECT (capsfilter, "different structure names");
201 : : /* different names, we can only suggest the complete caps */
202 : 0 : suggest = gst_caps_copy (new_caps);
203 : : }
204 : : }
205 : : } else {
206 [ - + ]: 13 : GST_DEBUG_OBJECT (capsfilter, "no negotiated caps");
207 : : /* no previous caps, the getcaps function will be used to find suitable
208 : : * caps */
209 : 13 : suggest = NULL;
210 : : }
211 : 13 : GST_OBJECT_UNLOCK (GST_BASE_TRANSFORM_SINK_PAD (object));
212 : :
213 [ - + ]: 13 : GST_DEBUG_OBJECT (capsfilter, "suggesting new caps %" GST_PTR_FORMAT,
214 : : suggest);
215 : 13 : gst_base_transform_suggest (GST_BASE_TRANSFORM (object), suggest, 0);
216 [ - + ]: 13 : if (suggest)
217 : 0 : gst_caps_unref (suggest);
218 : :
219 : 13 : break;
220 : : }
221 : : default:
222 : 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
223 : 0 : break;
224 : : }
225 : 13 : }
226 : :
227 : : static void
228 : 0 : gst_capsfilter_get_property (GObject * object, guint prop_id, GValue * value,
229 : : GParamSpec * pspec)
230 : : {
231 : 0 : GstCapsFilter *capsfilter = GST_CAPSFILTER (object);
232 : :
233 [ # # ]: 0 : switch (prop_id) {
234 : : case PROP_FILTER_CAPS:
235 : 0 : GST_OBJECT_LOCK (capsfilter);
236 : 0 : gst_value_set_caps (value, capsfilter->filter_caps);
237 : 0 : GST_OBJECT_UNLOCK (capsfilter);
238 : 0 : break;
239 : : default:
240 : 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
241 : 0 : break;
242 : : }
243 : 0 : }
244 : :
245 : : static void
246 : 16 : gst_capsfilter_dispose (GObject * object)
247 : : {
248 : 16 : GstCapsFilter *filter = GST_CAPSFILTER (object);
249 : :
250 : 16 : gst_caps_replace (&filter->filter_caps, NULL);
251 : :
252 : 16 : G_OBJECT_CLASS (parent_class)->dispose (object);
253 : 16 : }
254 : :
255 : : static GstCaps *
256 : 37 : gst_capsfilter_transform_caps (GstBaseTransform * base,
257 : : GstPadDirection direction, GstCaps * caps)
258 : : {
259 : 37 : GstCapsFilter *capsfilter = GST_CAPSFILTER (base);
260 : : GstCaps *ret, *filter_caps;
261 : :
262 : 37 : GST_OBJECT_LOCK (capsfilter);
263 : 37 : filter_caps = gst_caps_ref (capsfilter->filter_caps);
264 : 37 : GST_OBJECT_UNLOCK (capsfilter);
265 : :
266 : 37 : ret = gst_caps_intersect (caps, filter_caps);
267 [ - + ]: 37 : GST_DEBUG_OBJECT (capsfilter, "input: %" GST_PTR_FORMAT, caps);
268 [ - + ]: 37 : GST_DEBUG_OBJECT (capsfilter, "filter: %" GST_PTR_FORMAT, filter_caps);
269 [ - + ]: 37 : GST_DEBUG_OBJECT (capsfilter, "intersect: %" GST_PTR_FORMAT, ret);
270 : :
271 : 37 : gst_caps_unref (filter_caps);
272 : :
273 : 37 : return ret;
274 : : }
275 : :
276 : : static gboolean
277 : 0 : gst_capsfilter_accept_caps (GstBaseTransform * base,
278 : : GstPadDirection direction, GstCaps * caps)
279 : : {
280 : 0 : GstCapsFilter *capsfilter = GST_CAPSFILTER (base);
281 : : GstCaps *filter_caps;
282 : : gboolean ret;
283 : :
284 : 0 : GST_OBJECT_LOCK (capsfilter);
285 : 0 : filter_caps = gst_caps_ref (capsfilter->filter_caps);
286 : 0 : GST_OBJECT_UNLOCK (capsfilter);
287 : :
288 : 0 : ret = gst_caps_can_intersect (caps, filter_caps);
289 [ # # ]: 0 : GST_DEBUG_OBJECT (capsfilter, "can intersect: %d", ret);
290 [ # # ]: 0 : if (ret) {
291 : : /* if we can intersect, see if the other end also accepts */
292 [ # # ]: 0 : if (direction == GST_PAD_SRC)
293 : 0 : ret = gst_pad_peer_accept_caps (GST_BASE_TRANSFORM_SINK_PAD (base), caps);
294 : : else
295 : 0 : ret = gst_pad_peer_accept_caps (GST_BASE_TRANSFORM_SRC_PAD (base), caps);
296 [ # # ]: 0 : GST_DEBUG_OBJECT (capsfilter, "peer accept: %d", ret);
297 : : }
298 : :
299 : 0 : gst_caps_unref (filter_caps);
300 : :
301 : 0 : return ret;
302 : : }
303 : :
304 : : static GstFlowReturn
305 : 5002 : gst_capsfilter_transform_ip (GstBaseTransform * base, GstBuffer * buf)
306 : : {
307 : : /* No actual work here. It's all done in the prepare output buffer
308 : : * func. */
309 : 5002 : return GST_FLOW_OK;
310 : : }
311 : :
312 : : /* Output buffer preparation... if the buffer has no caps, and
313 : : * our allowed output caps is fixed, then give the caps to the
314 : : * buffer.
315 : : * This ensures that outgoing buffers have caps if we can, so
316 : : * that pipelines like:
317 : : * gst-launch filesrc location=rawsamples.raw !
318 : : * audio/x-raw-int,width=16,depth=16,rate=48000,channels=2,
319 : : * endianness=4321,signed='(boolean)'true ! alsasink
320 : : * will work.
321 : : */
322 : : static GstFlowReturn
323 : 5003 : gst_capsfilter_prepare_buf (GstBaseTransform * trans, GstBuffer * input,
324 : : gint size, GstCaps * caps, GstBuffer ** buf)
325 : : {
326 : 5003 : GstFlowReturn ret = GST_FLOW_OK;
327 : :
328 [ - + ]: 5003 : if (GST_BUFFER_CAPS (input) != NULL) {
329 : : /* Output buffer already has caps */
330 [ # # ]: 0 : GST_LOG_OBJECT (trans, "Input buffer already has caps (implicitely fixed)");
331 : : /* FIXME : Move this behaviour to basetransform. The given caps are the ones
332 : : * of the source pad, therefore our outgoing buffers should always have
333 : : * those caps. */
334 [ # # ]: 0 : if (GST_BUFFER_CAPS (input) != caps) {
335 : : /* caps are different, make a metadata writable output buffer to set
336 : : * caps */
337 [ # # ]: 0 : if (gst_buffer_is_metadata_writable (input)) {
338 : : /* input is writable, just set caps and use this as the output */
339 : 0 : *buf = input;
340 : 0 : gst_buffer_set_caps (*buf, caps);
341 : 0 : gst_buffer_ref (input);
342 : : } else {
343 [ # # ]: 0 : GST_DEBUG_OBJECT (trans, "Creating sub-buffer and setting caps");
344 : 0 : *buf = gst_buffer_create_sub (input, 0, GST_BUFFER_SIZE (input));
345 : 0 : gst_buffer_set_caps (*buf, caps);
346 : : }
347 : : } else {
348 : : /* caps are right, just use a ref of the input as the outbuf */
349 : 0 : *buf = input;
350 : 0 : gst_buffer_ref (input);
351 : : }
352 : : } else {
353 : : /* Buffer has no caps. See if the output pad only supports fixed caps */
354 : : GstCaps *out_caps;
355 : :
356 : 5003 : out_caps = GST_PAD_CAPS (trans->srcpad);
357 : :
358 [ + + ]: 5003 : if (out_caps != NULL) {
359 : 4999 : gst_caps_ref (out_caps);
360 : : } else {
361 : 4 : out_caps = gst_pad_get_allowed_caps (trans->srcpad);
362 [ - + ]: 4 : g_return_val_if_fail (out_caps != NULL, GST_FLOW_ERROR);
363 : : }
364 : :
365 : 5003 : out_caps = gst_caps_make_writable (out_caps);
366 : 5003 : gst_caps_do_simplify (out_caps);
367 : :
368 [ + + ][ + - ]: 5003 : if (gst_caps_is_fixed (out_caps) && !gst_caps_is_empty (out_caps)) {
369 [ - + ]: 5002 : GST_DEBUG_OBJECT (trans, "Have fixed output caps %"
370 : : GST_PTR_FORMAT " to apply to buffer with no caps", out_caps);
371 [ + - ]: 5002 : if (gst_buffer_is_metadata_writable (input)) {
372 : 5002 : gst_buffer_ref (input);
373 : 5002 : *buf = input;
374 : : } else {
375 [ # # ]: 0 : GST_DEBUG_OBJECT (trans, "Creating sub-buffer and setting caps");
376 : 0 : *buf = gst_buffer_create_sub (input, 0, GST_BUFFER_SIZE (input));
377 : : }
378 : 5002 : GST_BUFFER_CAPS (*buf) = out_caps;
379 : :
380 [ + + ]: 5005 : if (GST_PAD_CAPS (trans->srcpad) == NULL)
381 : 3 : gst_pad_set_caps (trans->srcpad, out_caps);
382 : : } else {
383 : 1 : gchar *caps_str = gst_caps_to_string (out_caps);
384 : :
385 [ - + ]: 1 : GST_DEBUG_OBJECT (trans, "Cannot choose caps. Have unfixed output caps %"
386 : : GST_PTR_FORMAT, out_caps);
387 : 1 : gst_caps_unref (out_caps);
388 : :
389 : 1 : ret = GST_FLOW_ERROR;
390 [ + - ][ - + ]: 1 : GST_ELEMENT_ERROR (trans, STREAM, FORMAT,
[ + - ][ - + ]
391 : : ("Filter caps do not completely specify the output format"),
392 : : ("Output caps are unfixed: %s", caps_str));
393 : 1 : g_free (caps_str);
394 : : }
395 : : }
396 : :
397 : 5003 : return ret;
398 : : }
|