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