Port gtk-doc comments to their equivalent markdown syntax
[platform/upstream/gstreamer.git] / plugins / elements / gstcapsfilter.c
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., 51 Franklin St, Fifth Floor,
20  * Boston, MA 02110-1301, USA.
21  */
22 /**
23  * SECTION:element-capsfilter
24  * @title: capsfilter
25  *
26  * The element does not modify data as such, but can enforce limitations on the
27  * data format.
28  *
29  * ## Example launch line
30  * |[
31  * gst-launch-1.0 videotestsrc ! capsfilter caps=video/x-raw,format=GRAY8 ! videoconvert ! autovideosink
32  * ]| Limits acceptable video from videotestsrc to be grayscale. Equivalent to
33  * |[
34  * gst-launch-1.0 videotestsrc ! video/x-raw,format=GRAY8 ! videoconvert ! autovideosink
35  * ]| which is a short notation for the capsfilter element.
36  *
37  */
38
39 #ifdef HAVE_CONFIG_H
40 #include "config.h"
41 #endif
42
43 #include "../../gst/gst-i18n-lib.h"
44 #include "gstcapsfilter.h"
45
46 enum
47 {
48   PROP_0,
49   PROP_FILTER_CAPS,
50   PROP_CAPS_CHANGE_MODE
51 };
52
53 #define DEFAULT_CAPS_CHANGE_MODE (GST_CAPS_FILTER_CAPS_CHANGE_MODE_IMMEDIATE)
54
55 static GstStaticPadTemplate sinktemplate = GST_STATIC_PAD_TEMPLATE ("sink",
56     GST_PAD_SINK,
57     GST_PAD_ALWAYS,
58     GST_STATIC_CAPS_ANY);
59
60 static GstStaticPadTemplate srctemplate = GST_STATIC_PAD_TEMPLATE ("src",
61     GST_PAD_SRC,
62     GST_PAD_ALWAYS,
63     GST_STATIC_CAPS_ANY);
64
65
66 GST_DEBUG_CATEGORY_STATIC (gst_capsfilter_debug);
67 #define GST_CAT_DEFAULT gst_capsfilter_debug
68
69 /* TODO: Add a drop-buffers mode */
70 #define GST_TYPE_CAPS_FILTER_CAPS_CHANGE_MODE (gst_caps_filter_caps_change_mode_get_type())
71 static GType
72 gst_caps_filter_caps_change_mode_get_type (void)
73 {
74   static GType type = 0;
75   static const GEnumValue data[] = {
76     {GST_CAPS_FILTER_CAPS_CHANGE_MODE_IMMEDIATE,
77         "Only accept the current filter caps", "immediate"},
78     {GST_CAPS_FILTER_CAPS_CHANGE_MODE_DELAYED,
79         "Temporarily accept previous filter caps", "delayed"},
80     {0, NULL, NULL},
81   };
82
83   if (!type) {
84     type = g_enum_register_static ("GstCapsFilterCapsChangeMode", data);
85   }
86   return type;
87 }
88
89 #define _do_init \
90     GST_DEBUG_CATEGORY_INIT (gst_capsfilter_debug, "capsfilter", 0, \
91     "capsfilter element");
92 #define gst_capsfilter_parent_class parent_class
93 G_DEFINE_TYPE_WITH_CODE (GstCapsFilter, gst_capsfilter, GST_TYPE_BASE_TRANSFORM,
94     _do_init);
95
96
97 static void gst_capsfilter_set_property (GObject * object, guint prop_id,
98     const GValue * value, GParamSpec * pspec);
99 static void gst_capsfilter_get_property (GObject * object, guint prop_id,
100     GValue * value, GParamSpec * pspec);
101 static void gst_capsfilter_dispose (GObject * object);
102
103 static GstCaps *gst_capsfilter_transform_caps (GstBaseTransform * base,
104     GstPadDirection direction, GstCaps * caps, GstCaps * filter);
105 static gboolean gst_capsfilter_accept_caps (GstBaseTransform * base,
106     GstPadDirection direction, GstCaps * caps);
107 static GstFlowReturn gst_capsfilter_transform_ip (GstBaseTransform * base,
108     GstBuffer * buf);
109 static GstFlowReturn gst_capsfilter_prepare_buf (GstBaseTransform * trans,
110     GstBuffer * input, GstBuffer ** buf);
111 static gboolean gst_capsfilter_sink_event (GstBaseTransform * trans,
112     GstEvent * event);
113 static gboolean gst_capsfilter_stop (GstBaseTransform * trans);
114
115 static void
116 gst_capsfilter_class_init (GstCapsFilterClass * klass)
117 {
118   GObjectClass *gobject_class;
119   GstElementClass *gstelement_class;
120   GstBaseTransformClass *trans_class;
121
122   gobject_class = G_OBJECT_CLASS (klass);
123   gobject_class->set_property = gst_capsfilter_set_property;
124   gobject_class->get_property = gst_capsfilter_get_property;
125   gobject_class->dispose = gst_capsfilter_dispose;
126
127   g_object_class_install_property (gobject_class, PROP_FILTER_CAPS,
128       g_param_spec_boxed ("caps", _("Filter caps"),
129           _("Restrict the possible allowed capabilities (NULL means ANY). "
130               "Setting this property takes a reference to the supplied GstCaps "
131               "object."), GST_TYPE_CAPS,
132           G_PARAM_READWRITE | GST_PARAM_MUTABLE_PLAYING |
133           G_PARAM_STATIC_STRINGS));
134
135   g_object_class_install_property (gobject_class, PROP_CAPS_CHANGE_MODE,
136       g_param_spec_enum ("caps-change-mode", _("Caps Change Mode"),
137           _("Filter caps change behaviour"),
138           GST_TYPE_CAPS_FILTER_CAPS_CHANGE_MODE, DEFAULT_CAPS_CHANGE_MODE,
139           G_PARAM_READWRITE | GST_PARAM_MUTABLE_PLAYING |
140           G_PARAM_STATIC_STRINGS));
141
142   gstelement_class = GST_ELEMENT_CLASS (klass);
143   gst_element_class_set_static_metadata (gstelement_class,
144       "CapsFilter",
145       "Generic",
146       "Pass data without modification, limiting formats",
147       "David Schleef <ds@schleef.org>");
148   gst_element_class_add_static_pad_template (gstelement_class, &srctemplate);
149   gst_element_class_add_static_pad_template (gstelement_class, &sinktemplate);
150
151   trans_class = GST_BASE_TRANSFORM_CLASS (klass);
152   trans_class->transform_caps =
153       GST_DEBUG_FUNCPTR (gst_capsfilter_transform_caps);
154   trans_class->transform_ip = GST_DEBUG_FUNCPTR (gst_capsfilter_transform_ip);
155   trans_class->accept_caps = GST_DEBUG_FUNCPTR (gst_capsfilter_accept_caps);
156   trans_class->prepare_output_buffer =
157       GST_DEBUG_FUNCPTR (gst_capsfilter_prepare_buf);
158   trans_class->sink_event = GST_DEBUG_FUNCPTR (gst_capsfilter_sink_event);
159   trans_class->stop = GST_DEBUG_FUNCPTR (gst_capsfilter_stop);
160 }
161
162 static void
163 gst_capsfilter_init (GstCapsFilter * filter)
164 {
165   GstBaseTransform *trans = GST_BASE_TRANSFORM (filter);
166   gst_base_transform_set_gap_aware (trans, TRUE);
167   gst_base_transform_set_prefer_passthrough (trans, FALSE);
168   filter->filter_caps = gst_caps_new_any ();
169   filter->filter_caps_used = FALSE;
170   filter->got_sink_caps = FALSE;
171   filter->caps_change_mode = DEFAULT_CAPS_CHANGE_MODE;
172 }
173
174 static void
175 gst_capsfilter_set_property (GObject * object, guint prop_id,
176     const GValue * value, GParamSpec * pspec)
177 {
178   GstCapsFilter *capsfilter = GST_CAPS_FILTER (object);
179
180   switch (prop_id) {
181     case PROP_FILTER_CAPS:{
182       GstCaps *new_caps;
183       GstCaps *old_caps;
184       const GstCaps *new_caps_val = gst_value_get_caps (value);
185
186       if (new_caps_val == NULL) {
187         new_caps = gst_caps_new_any ();
188       } else {
189         new_caps = (GstCaps *) new_caps_val;
190         gst_caps_ref (new_caps);
191       }
192
193       GST_OBJECT_LOCK (capsfilter);
194       old_caps = capsfilter->filter_caps;
195       capsfilter->filter_caps = new_caps;
196       if (old_caps && capsfilter->filter_caps_used &&
197           capsfilter->caps_change_mode ==
198           GST_CAPS_FILTER_CAPS_CHANGE_MODE_DELAYED) {
199         capsfilter->previous_caps =
200             g_list_prepend (capsfilter->previous_caps, gst_caps_ref (old_caps));
201       } else if (capsfilter->caps_change_mode !=
202           GST_CAPS_FILTER_CAPS_CHANGE_MODE_DELAYED) {
203         g_list_free_full (capsfilter->previous_caps,
204             (GDestroyNotify) gst_caps_unref);
205         capsfilter->previous_caps = NULL;
206       }
207       capsfilter->filter_caps_used = FALSE;
208       GST_OBJECT_UNLOCK (capsfilter);
209
210       gst_caps_unref (old_caps);
211
212       GST_DEBUG_OBJECT (capsfilter, "set new caps %" GST_PTR_FORMAT, new_caps);
213
214       gst_base_transform_reconfigure_sink (GST_BASE_TRANSFORM (object));
215       break;
216     }
217     case PROP_CAPS_CHANGE_MODE:{
218       GstCapsFilterCapsChangeMode old_change_mode;
219
220       GST_OBJECT_LOCK (capsfilter);
221       old_change_mode = capsfilter->caps_change_mode;
222       capsfilter->caps_change_mode = g_value_get_enum (value);
223
224       if (capsfilter->caps_change_mode != old_change_mode) {
225         g_list_free_full (capsfilter->previous_caps,
226             (GDestroyNotify) gst_caps_unref);
227         capsfilter->previous_caps = NULL;
228       }
229       GST_OBJECT_UNLOCK (capsfilter);
230       break;
231     }
232     default:
233       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
234       break;
235   }
236 }
237
238 static void
239 gst_capsfilter_get_property (GObject * object, guint prop_id, GValue * value,
240     GParamSpec * pspec)
241 {
242   GstCapsFilter *capsfilter = GST_CAPS_FILTER (object);
243
244   switch (prop_id) {
245     case PROP_FILTER_CAPS:
246       GST_OBJECT_LOCK (capsfilter);
247       gst_value_set_caps (value, capsfilter->filter_caps);
248       GST_OBJECT_UNLOCK (capsfilter);
249       break;
250     case PROP_CAPS_CHANGE_MODE:
251       g_value_set_enum (value, capsfilter->caps_change_mode);
252       break;
253     default:
254       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
255       break;
256   }
257 }
258
259 static void
260 gst_capsfilter_dispose (GObject * object)
261 {
262   GstCapsFilter *filter = GST_CAPS_FILTER (object);
263
264   gst_caps_replace (&filter->filter_caps, NULL);
265   g_list_free_full (filter->pending_events, (GDestroyNotify) gst_event_unref);
266   filter->pending_events = NULL;
267
268   G_OBJECT_CLASS (parent_class)->dispose (object);
269 }
270
271 static GstCaps *
272 gst_capsfilter_transform_caps (GstBaseTransform * base,
273     GstPadDirection direction, GstCaps * caps, GstCaps * filter)
274 {
275   GstCapsFilter *capsfilter = GST_CAPS_FILTER (base);
276   GstCaps *ret, *filter_caps, *tmp;
277   gboolean retried = FALSE;
278   GstCapsFilterCapsChangeMode caps_change_mode;
279
280   GST_OBJECT_LOCK (capsfilter);
281   filter_caps = gst_caps_ref (capsfilter->filter_caps);
282   capsfilter->filter_caps_used = TRUE;
283   caps_change_mode = capsfilter->caps_change_mode;
284   GST_OBJECT_UNLOCK (capsfilter);
285
286 retry:
287   if (filter) {
288     tmp =
289         gst_caps_intersect_full (filter, filter_caps, GST_CAPS_INTERSECT_FIRST);
290     gst_caps_unref (filter_caps);
291     filter_caps = tmp;
292   }
293
294   ret = gst_caps_intersect_full (filter_caps, caps, GST_CAPS_INTERSECT_FIRST);
295
296   GST_DEBUG_OBJECT (capsfilter, "input:     %" GST_PTR_FORMAT, caps);
297   GST_DEBUG_OBJECT (capsfilter, "filter:    %" GST_PTR_FORMAT, filter);
298   GST_DEBUG_OBJECT (capsfilter, "caps filter:    %" GST_PTR_FORMAT,
299       filter_caps);
300   GST_DEBUG_OBJECT (capsfilter, "intersect: %" GST_PTR_FORMAT, ret);
301
302   if (gst_caps_is_empty (ret)
303       && caps_change_mode ==
304       GST_CAPS_FILTER_CAPS_CHANGE_MODE_DELAYED && capsfilter->previous_caps
305       && !retried) {
306     GList *l;
307
308     GST_DEBUG_OBJECT (capsfilter,
309         "Current filter caps are not compatible, retry with previous");
310     GST_OBJECT_LOCK (capsfilter);
311     gst_caps_unref (filter_caps);
312     gst_caps_unref (ret);
313     filter_caps = gst_caps_new_empty ();
314     for (l = capsfilter->previous_caps; l; l = l->next) {
315       filter_caps = gst_caps_merge (filter_caps, gst_caps_ref (l->data));
316     }
317     GST_OBJECT_UNLOCK (capsfilter);
318     retried = TRUE;
319     goto retry;
320   }
321
322   gst_caps_unref (filter_caps);
323
324   return ret;
325 }
326
327 static gboolean
328 gst_capsfilter_accept_caps (GstBaseTransform * base,
329     GstPadDirection direction, GstCaps * caps)
330 {
331   GstCapsFilter *capsfilter = GST_CAPS_FILTER (base);
332   GstCaps *filter_caps;
333   gboolean ret;
334
335   GST_OBJECT_LOCK (capsfilter);
336   filter_caps = gst_caps_ref (capsfilter->filter_caps);
337   capsfilter->filter_caps_used = TRUE;
338   GST_OBJECT_UNLOCK (capsfilter);
339
340   ret = gst_caps_can_intersect (caps, filter_caps);
341   GST_DEBUG_OBJECT (capsfilter, "can intersect: %d", ret);
342   if (!ret
343       && capsfilter->caps_change_mode ==
344       GST_CAPS_FILTER_CAPS_CHANGE_MODE_DELAYED) {
345     GList *l;
346
347     GST_OBJECT_LOCK (capsfilter);
348     for (l = capsfilter->previous_caps; l; l = l->next) {
349       ret = gst_caps_can_intersect (caps, l->data);
350       if (ret)
351         break;
352     }
353     GST_OBJECT_UNLOCK (capsfilter);
354
355     /* Tell upstream to reconfigure, it's still
356      * looking at old caps */
357     if (ret)
358       gst_base_transform_reconfigure_sink (base);
359   }
360
361   gst_caps_unref (filter_caps);
362
363   return ret;
364 }
365
366 static GstFlowReturn
367 gst_capsfilter_transform_ip (GstBaseTransform * base, GstBuffer * buf)
368 {
369   /* No actual work here. It's all done in the prepare output buffer
370    * func. */
371   return GST_FLOW_OK;
372 }
373
374 static void
375 gst_capsfilter_push_pending_events (GstCapsFilter * filter, GList * events)
376 {
377   GList *l;
378
379   for (l = g_list_last (events); l; l = l->prev) {
380     GST_LOG_OBJECT (filter, "Forwarding %s event",
381         GST_EVENT_TYPE_NAME (l->data));
382     GST_BASE_TRANSFORM_CLASS (parent_class)->sink_event (GST_BASE_TRANSFORM_CAST
383         (filter), l->data);
384   }
385   g_list_free (events);
386 }
387
388 /* Ouput buffer preparation ... if the buffer has no caps, and our allowed
389  * output caps is fixed, then send the caps downstream, making sure caps are
390  * sent before segment event.
391  *
392  * This ensures that caps event is sent if we can, so that pipelines like:
393  *   gst-launch filesrc location=rawsamples.raw !
394  *       audio/x-raw,format=S16LE,rate=48000,channels=2 ! alsasink
395  * will work.
396  */
397 static GstFlowReturn
398 gst_capsfilter_prepare_buf (GstBaseTransform * trans, GstBuffer * input,
399     GstBuffer ** buf)
400 {
401   GstFlowReturn ret = GST_FLOW_OK;
402   GstCapsFilter *filter = GST_CAPS_FILTER (trans);
403
404   /* always return the input as output buffer */
405   *buf = input;
406
407   if (GST_PAD_MODE (trans->srcpad) == GST_PAD_MODE_PUSH
408       && !filter->got_sink_caps) {
409
410     /* No caps. See if the output pad only supports fixed caps */
411     GstCaps *out_caps;
412     GList *pending_events = filter->pending_events;
413
414     GST_LOG_OBJECT (trans, "Input pad does not have caps");
415
416     filter->pending_events = NULL;
417
418     out_caps = gst_pad_get_current_caps (trans->srcpad);
419     if (out_caps == NULL) {
420       out_caps = gst_pad_get_allowed_caps (trans->srcpad);
421       g_return_val_if_fail (out_caps != NULL, GST_FLOW_ERROR);
422     }
423
424     out_caps = gst_caps_simplify (out_caps);
425
426     if (gst_caps_is_fixed (out_caps) && !gst_caps_is_empty (out_caps)) {
427       GST_DEBUG_OBJECT (trans, "Have fixed output caps %"
428           GST_PTR_FORMAT " to apply to srcpad", out_caps);
429
430       if (!gst_pad_has_current_caps (trans->srcpad)) {
431         if (gst_pad_set_caps (trans->srcpad, out_caps)) {
432           if (pending_events) {
433             gst_capsfilter_push_pending_events (filter, pending_events);
434             pending_events = NULL;
435           }
436         } else {
437           ret = GST_FLOW_NOT_NEGOTIATED;
438         }
439       } else {
440         gst_capsfilter_push_pending_events (filter, pending_events);
441         pending_events = NULL;
442       }
443
444       g_list_free_full (pending_events, (GDestroyNotify) gst_event_unref);
445       gst_caps_unref (out_caps);
446     } else {
447       gchar *caps_str = gst_caps_to_string (out_caps);
448
449       GST_DEBUG_OBJECT (trans, "Cannot choose caps. Have unfixed output caps %"
450           GST_PTR_FORMAT, out_caps);
451       gst_caps_unref (out_caps);
452
453       GST_ELEMENT_ERROR (trans, STREAM, FORMAT,
454           ("Filter caps do not completely specify the output format"),
455           ("Output caps are unfixed: %s", caps_str));
456
457       g_free (caps_str);
458       g_list_free_full (pending_events, (GDestroyNotify) gst_event_unref);
459
460       ret = GST_FLOW_ERROR;
461     }
462   } else if (G_UNLIKELY (filter->pending_events)) {
463     GList *events = filter->pending_events;
464
465     filter->pending_events = NULL;
466
467     /* push pending events before a buffer */
468     gst_capsfilter_push_pending_events (filter, events);
469   }
470
471   return ret;
472 }
473
474 /* Queue the segment event if there was no caps event */
475 static gboolean
476 gst_capsfilter_sink_event (GstBaseTransform * trans, GstEvent * event)
477 {
478   GstCapsFilter *filter = GST_CAPS_FILTER (trans);
479   gboolean ret;
480
481   if (GST_EVENT_TYPE (event) == GST_EVENT_FLUSH_STOP) {
482     GList *l;
483
484     for (l = filter->pending_events; l; l = l->next) {
485       if (GST_EVENT_TYPE (l->data) == GST_EVENT_SEGMENT ||
486           GST_EVENT_TYPE (l->data) == GST_EVENT_EOS) {
487         gst_event_unref (l->data);
488         filter->pending_events = g_list_delete_link (filter->pending_events, l);
489         break;
490       }
491     }
492   }
493
494   if (!GST_EVENT_IS_STICKY (event)
495       || GST_EVENT_TYPE (event) <= GST_EVENT_CAPS)
496     goto done;
497
498   /* If we get EOS before any buffers, just push all pending events */
499   if (GST_EVENT_TYPE (event) == GST_EVENT_EOS) {
500     GList *l;
501
502     for (l = g_list_last (filter->pending_events); l; l = l->prev) {
503       GST_LOG_OBJECT (trans, "Forwarding %s event",
504           GST_EVENT_TYPE_NAME (l->data));
505       GST_BASE_TRANSFORM_CLASS (parent_class)->sink_event (trans, l->data);
506     }
507     g_list_free (filter->pending_events);
508     filter->pending_events = NULL;
509   } else if (!filter->got_sink_caps) {
510     GST_LOG_OBJECT (trans, "Got %s event before caps, queueing",
511         GST_EVENT_TYPE_NAME (event));
512
513     filter->pending_events = g_list_prepend (filter->pending_events, event);
514
515     return TRUE;
516   }
517
518 done:
519
520   GST_LOG_OBJECT (trans, "Forwarding %s event", GST_EVENT_TYPE_NAME (event));
521   ret =
522       GST_BASE_TRANSFORM_CLASS (parent_class)->sink_event (trans,
523       gst_event_ref (event));
524
525   if (GST_EVENT_TYPE (event) == GST_EVENT_CAPS) {
526     filter->got_sink_caps = TRUE;
527     if (filter->caps_change_mode == GST_CAPS_FILTER_CAPS_CHANGE_MODE_DELAYED) {
528       GList *l;
529       GstCaps *caps;
530
531       gst_event_parse_caps (event, &caps);
532
533       /* Remove all previous caps up to one that works.
534        * Note that this might keep some leftover caps if there
535        * are multiple compatible caps */
536       GST_OBJECT_LOCK (filter);
537       for (l = g_list_last (filter->previous_caps); l; l = l->prev) {
538         if (gst_caps_can_intersect (caps, l->data)) {
539           while (l->next) {
540             gst_caps_unref (l->next->data);
541             l = g_list_delete_link (l, l->next);
542           }
543           break;
544         }
545       }
546       if (!l && gst_caps_can_intersect (caps, filter->filter_caps)) {
547         g_list_free_full (filter->previous_caps,
548             (GDestroyNotify) gst_caps_unref);
549         filter->previous_caps = NULL;
550         filter->filter_caps_used = TRUE;
551       }
552       GST_OBJECT_UNLOCK (filter);
553     }
554   }
555   gst_event_unref (event);
556
557   return ret;
558 }
559
560 static gboolean
561 gst_capsfilter_stop (GstBaseTransform * trans)
562 {
563   GstCapsFilter *filter = GST_CAPS_FILTER (trans);
564
565   g_list_free_full (filter->pending_events, (GDestroyNotify) gst_event_unref);
566   filter->pending_events = NULL;
567
568   GST_OBJECT_LOCK (filter);
569   g_list_free_full (filter->previous_caps, (GDestroyNotify) gst_caps_unref);
570   filter->previous_caps = NULL;
571   GST_OBJECT_UNLOCK (filter);
572
573   filter->got_sink_caps = FALSE;
574
575   return TRUE;
576 }