• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* GStreamer
2  * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
3  *                    2005 Wim Taymans <wim@fluendo.com>
4  *                    2005 Andy Wingo <wingo@fluendo.com>
5  *                    2005 Thomas Vander Stichele <thomas at apestaart dot org>
6  *                    2008 Wim Taymans <wim.taymans@gmail.com>
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Library General Public
10  * License as published by the Free Software Foundation; either
11  * version 2 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Library General Public License for more details.
17  *
18  * You should have received a copy of the GNU Library General Public
19  * License along with this library; if not, write to the
20  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
21  * Boston, MA 02110-1301, USA.
22  */
23 
24 /**
25  * SECTION:gstbasetransform
26  * @title: GstBaseTransform
27  * @short_description: Base class for simple transform filters
28  * @see_also: #GstBaseSrc, #GstBaseSink
29  *
30  * This base class is for filter elements that process data. Elements
31  * that are suitable for implementation using #GstBaseTransform are ones
32  * where the size and caps of the output is known entirely from the input
33  * caps and buffer sizes. These include elements that directly transform
34  * one buffer into another, modify the contents of a buffer in-place, as
35  * well as elements that collate multiple input buffers into one output buffer,
36  * or that expand one input buffer into multiple output buffers. See below
37  * for more concrete use cases.
38  *
39  * It provides for:
40  *
41  * * one sinkpad and one srcpad
42  * * Possible formats on sink and source pad implemented
43  *   with custom transform_caps function. By default uses
44  *   same format on sink and source.
45  *
46  * * Handles state changes
47  * * Does flushing
48  * * Push mode
49  * * Pull mode if the sub-class transform can operate on arbitrary data
50  *
51  * # Use Cases
52  *
53  * ## Passthrough mode
54  *
55  *   * Element has no interest in modifying the buffer. It may want to inspect it,
56  *     in which case the element should have a transform_ip function. If there
57  *     is no transform_ip function in passthrough mode, the buffer is pushed
58  *     intact.
59  *
60  *   * The #GstBaseTransformClass.passthrough_on_same_caps variable
61  *     will automatically set/unset passthrough based on whether the
62  *     element negotiates the same caps on both pads.
63  *
64  *   * #GstBaseTransformClass.passthrough_on_same_caps on an element that
65  *     doesn't implement a transform_caps function is useful for elements that
66  *     only inspect data (such as level)
67  *
68  *   * Example elements
69  *
70  *     * Level
71  *     * Videoscale, audioconvert, videoconvert, audioresample in certain modes.
72  *
73  * ## Modifications in-place - input buffer and output buffer are the same thing.
74  *
75  * * The element must implement a transform_ip function.
76  * * Output buffer size must <= input buffer size
77  * * If the always_in_place flag is set, non-writable buffers will be copied
78  *   and passed to the transform_ip function, otherwise a new buffer will be
79  *   created and the transform function called.
80  *
81  * * Incoming writable buffers will be passed to the transform_ip function
82  *   immediately.
83  * * only implementing transform_ip and not transform implies always_in_place = %TRUE
84  *
85  *   * Example elements:
86  *     * Volume
87  *     * Audioconvert in certain modes (signed/unsigned conversion)
88  *     * videoconvert in certain modes (endianness swapping)
89  *
90  * ## Modifications only to the caps/metadata of a buffer
91  *
92  * * The element does not require writable data, but non-writable buffers
93  *   should be subbuffered so that the meta-information can be replaced.
94  *
95  * * Elements wishing to operate in this mode should replace the
96  *   prepare_output_buffer method to create subbuffers of the input buffer
97  *   and set always_in_place to %TRUE
98  *
99  * * Example elements
100  *   * Capsfilter when setting caps on outgoing buffers that have
101  *     none.
102  *   * identity when it is going to re-timestamp buffers by
103  *     datarate.
104  *
105  * ## Normal mode
106  *   * always_in_place flag is not set, or there is no transform_ip function
107  *   * Element will receive an input buffer and output buffer to operate on.
108  *   * Output buffer is allocated by calling the prepare_output_buffer function.
109  *   * Example elements:
110  *     * Videoscale, videoconvert, audioconvert when doing
111  *     scaling/conversions
112  *
113  * ## Special output buffer allocations
114  *   * Elements which need to do special allocation of their output buffers
115  *     beyond allocating output buffers via the negotiated allocator or
116  *     buffer pool should implement the prepare_output_buffer method.
117  *
118  *   * Example elements:
119  *     * efence
120  *
121  * # Sub-class settable flags on GstBaseTransform
122  *
123  * * passthrough
124  *
125  *   * Implies that in the current configuration, the sub-class is not interested in modifying the buffers.
126  *   * Elements which are always in passthrough mode whenever the same caps has been negotiated on both pads can set the class variable passthrough_on_same_caps to have this behaviour automatically.
127  *
128  * * always_in_place
129  *   * Determines whether a non-writable buffer will be copied before passing
130  *     to the transform_ip function.
131  *
132  *   * Implied %TRUE if no transform function is implemented.
133  *   * Implied %FALSE if ONLY transform function is implemented.
134  */
135 
136 #ifdef HAVE_CONFIG_H
137 #  include "config.h"
138 #endif
139 
140 #include <stdlib.h>
141 #include <string.h>
142 
143 #include "../../../gst/gst_private.h"
144 #include "../../../gst/gst-i18n-lib.h"
145 #include "../../../gst/glib-compat-private.h"
146 #include "gstbasetransform.h"
147 
148 GST_DEBUG_CATEGORY_STATIC (gst_base_transform_debug);
149 #define GST_CAT_DEFAULT gst_base_transform_debug
150 
151 /* BaseTransform signals and args */
152 enum
153 {
154   /* FILL ME */
155   LAST_SIGNAL
156 };
157 
158 #define DEFAULT_PROP_QOS	FALSE
159 
160 enum
161 {
162   PROP_0,
163   PROP_QOS
164 };
165 
166 struct _GstBaseTransformPrivate
167 {
168   /* Set by sub-class */
169   gboolean passthrough;
170   gboolean always_in_place;
171 
172   GstCaps *cache_caps1;
173   gsize cache_caps1_size;
174   GstCaps *cache_caps2;
175   gsize cache_caps2_size;
176   gboolean have_same_caps;
177 
178   gboolean negotiated;
179 
180   /* QoS *//* with LOCK */
181   gboolean qos_enabled;
182   gdouble proportion;
183   GstClockTime earliest_time;
184   /* previous buffer had a discont */
185   gboolean discont;
186 
187   GstPadMode pad_mode;
188 
189   gboolean gap_aware;
190   gboolean prefer_passthrough;
191 
192   /* QoS stats */
193   guint64 processed;
194   guint64 dropped;
195 
196   GstClockTime position_out;
197 
198   GstBufferPool *pool;
199   gboolean pool_active;
200   GstAllocator *allocator;
201   GstAllocationParams params;
202   GstQuery *query;
203 };
204 
205 
206 static GstElementClass *parent_class = NULL;
207 static gint private_offset = 0;
208 
209 static void gst_base_transform_class_init (GstBaseTransformClass * klass);
210 static void gst_base_transform_init (GstBaseTransform * trans,
211     GstBaseTransformClass * klass);
212 static GstFlowReturn default_submit_input_buffer (GstBaseTransform * trans,
213     gboolean is_discont, GstBuffer * input);
214 static GstFlowReturn default_generate_output (GstBaseTransform * trans,
215     GstBuffer ** outbuf);
216 
217 /* we can't use G_DEFINE_ABSTRACT_TYPE because we need the klass in the _init
218  * method to get to the padtemplates */
219 GType
gst_base_transform_get_type(void)220 gst_base_transform_get_type (void)
221 {
222   static gsize base_transform_type = 0;
223 
224   if (g_once_init_enter (&base_transform_type)) {
225     GType _type;
226     static const GTypeInfo base_transform_info = {
227       sizeof (GstBaseTransformClass),
228       NULL,
229       NULL,
230       (GClassInitFunc) gst_base_transform_class_init,
231       NULL,
232       NULL,
233       sizeof (GstBaseTransform),
234       0,
235       (GInstanceInitFunc) gst_base_transform_init,
236     };
237 
238     _type = g_type_register_static (GST_TYPE_ELEMENT,
239         "GstBaseTransform", &base_transform_info, G_TYPE_FLAG_ABSTRACT);
240 
241     private_offset =
242         g_type_add_instance_private (_type, sizeof (GstBaseTransformPrivate));
243 
244     g_once_init_leave (&base_transform_type, _type);
245   }
246   return base_transform_type;
247 }
248 
249 static inline GstBaseTransformPrivate *
gst_base_transform_get_instance_private(GstBaseTransform * self)250 gst_base_transform_get_instance_private (GstBaseTransform * self)
251 {
252   return (G_STRUCT_MEMBER_P (self, private_offset));
253 }
254 
255 static void gst_base_transform_finalize (GObject * object);
256 static void gst_base_transform_set_property (GObject * object, guint prop_id,
257     const GValue * value, GParamSpec * pspec);
258 static void gst_base_transform_get_property (GObject * object, guint prop_id,
259     GValue * value, GParamSpec * pspec);
260 static gboolean gst_base_transform_src_activate_mode (GstPad * pad,
261     GstObject * parent, GstPadMode mode, gboolean active);
262 static gboolean gst_base_transform_sink_activate_mode (GstPad * pad,
263     GstObject * parent, GstPadMode mode, gboolean active);
264 static gboolean gst_base_transform_activate (GstBaseTransform * trans,
265     gboolean active);
266 static gboolean gst_base_transform_get_unit_size (GstBaseTransform * trans,
267     GstCaps * caps, gsize * size);
268 
269 static gboolean gst_base_transform_src_event (GstPad * pad, GstObject * parent,
270     GstEvent * event);
271 static gboolean gst_base_transform_src_eventfunc (GstBaseTransform * trans,
272     GstEvent * event);
273 static gboolean gst_base_transform_sink_event (GstPad * pad, GstObject * parent,
274     GstEvent * event);
275 static gboolean gst_base_transform_sink_eventfunc (GstBaseTransform * trans,
276     GstEvent * event);
277 static GstFlowReturn gst_base_transform_getrange (GstPad * pad,
278     GstObject * parent, guint64 offset, guint length, GstBuffer ** buffer);
279 static GstFlowReturn gst_base_transform_chain (GstPad * pad, GstObject * parent,
280     GstBuffer * buffer);
281 static GstCaps *gst_base_transform_default_transform_caps (GstBaseTransform *
282     trans, GstPadDirection direction, GstCaps * caps, GstCaps * filter);
283 static GstCaps *gst_base_transform_default_fixate_caps (GstBaseTransform *
284     trans, GstPadDirection direction, GstCaps * caps, GstCaps * othercaps);
285 static GstCaps *gst_base_transform_query_caps (GstBaseTransform * trans,
286     GstPad * pad, GstCaps * filter);
287 static gboolean gst_base_transform_acceptcaps_default (GstBaseTransform * trans,
288     GstPadDirection direction, GstCaps * caps);
289 static gboolean gst_base_transform_setcaps (GstBaseTransform * trans,
290     GstPad * pad, GstCaps * caps);
291 static gboolean gst_base_transform_default_decide_allocation (GstBaseTransform
292     * trans, GstQuery * query);
293 static gboolean gst_base_transform_default_propose_allocation (GstBaseTransform
294     * trans, GstQuery * decide_query, GstQuery * query);
295 static gboolean gst_base_transform_query (GstPad * pad, GstObject * parent,
296     GstQuery * query);
297 static gboolean gst_base_transform_default_query (GstBaseTransform * trans,
298     GstPadDirection direction, GstQuery * query);
299 static gboolean gst_base_transform_default_transform_size (GstBaseTransform *
300     trans, GstPadDirection direction, GstCaps * caps, gsize size,
301     GstCaps * othercaps, gsize * othersize);
302 
303 static GstFlowReturn default_prepare_output_buffer (GstBaseTransform * trans,
304     GstBuffer * inbuf, GstBuffer ** outbuf);
305 static gboolean default_copy_metadata (GstBaseTransform * trans,
306     GstBuffer * inbuf, GstBuffer * outbuf);
307 static gboolean
308 gst_base_transform_default_transform_meta (GstBaseTransform * trans,
309     GstBuffer * outbuf, GstMeta * meta, GstBuffer * inbuf);
310 
311 /* static guint gst_base_transform_signals[LAST_SIGNAL] = { 0 }; */
312 
313 
314 static void
gst_base_transform_finalize(GObject * object)315 gst_base_transform_finalize (GObject * object)
316 {
317   G_OBJECT_CLASS (parent_class)->finalize (object);
318 }
319 
320 static void
gst_base_transform_class_init(GstBaseTransformClass * klass)321 gst_base_transform_class_init (GstBaseTransformClass * klass)
322 {
323   GObjectClass *gobject_class;
324 
325   gobject_class = G_OBJECT_CLASS (klass);
326 
327   if (private_offset != 0)
328     g_type_class_adjust_private_offset (klass, &private_offset);
329 
330   GST_DEBUG_CATEGORY_INIT (gst_base_transform_debug, "basetransform", 0,
331       "basetransform element");
332 
333   GST_DEBUG ("gst_base_transform_class_init");
334 
335   parent_class = g_type_class_peek_parent (klass);
336 
337   gobject_class->set_property = gst_base_transform_set_property;
338   gobject_class->get_property = gst_base_transform_get_property;
339 
340   g_object_class_install_property (gobject_class, PROP_QOS,
341       g_param_spec_boolean ("qos", "QoS", "Handle Quality-of-Service events",
342           DEFAULT_PROP_QOS, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
343 
344   gobject_class->finalize = gst_base_transform_finalize;
345 
346   klass->passthrough_on_same_caps = FALSE;
347   klass->transform_ip_on_passthrough = TRUE;
348 
349   klass->transform_caps =
350       GST_DEBUG_FUNCPTR (gst_base_transform_default_transform_caps);
351   klass->fixate_caps =
352       GST_DEBUG_FUNCPTR (gst_base_transform_default_fixate_caps);
353   klass->accept_caps =
354       GST_DEBUG_FUNCPTR (gst_base_transform_acceptcaps_default);
355   klass->query = GST_DEBUG_FUNCPTR (gst_base_transform_default_query);
356   klass->decide_allocation =
357       GST_DEBUG_FUNCPTR (gst_base_transform_default_decide_allocation);
358   klass->propose_allocation =
359       GST_DEBUG_FUNCPTR (gst_base_transform_default_propose_allocation);
360   klass->transform_size =
361       GST_DEBUG_FUNCPTR (gst_base_transform_default_transform_size);
362   klass->transform_meta =
363       GST_DEBUG_FUNCPTR (gst_base_transform_default_transform_meta);
364 
365   klass->sink_event = GST_DEBUG_FUNCPTR (gst_base_transform_sink_eventfunc);
366   klass->src_event = GST_DEBUG_FUNCPTR (gst_base_transform_src_eventfunc);
367   klass->prepare_output_buffer =
368       GST_DEBUG_FUNCPTR (default_prepare_output_buffer);
369   klass->copy_metadata = GST_DEBUG_FUNCPTR (default_copy_metadata);
370   klass->submit_input_buffer = GST_DEBUG_FUNCPTR (default_submit_input_buffer);
371   klass->generate_output = GST_DEBUG_FUNCPTR (default_generate_output);
372 }
373 
374 static void
gst_base_transform_init(GstBaseTransform * trans,GstBaseTransformClass * bclass)375 gst_base_transform_init (GstBaseTransform * trans,
376     GstBaseTransformClass * bclass)
377 {
378   GstPadTemplate *pad_template;
379   GstBaseTransformPrivate *priv;
380 
381   GST_DEBUG ("gst_base_transform_init");
382 
383   priv = trans->priv = gst_base_transform_get_instance_private (trans);
384 
385   pad_template =
386       gst_element_class_get_pad_template (GST_ELEMENT_CLASS (bclass), "sink");
387   g_return_if_fail (pad_template != NULL);
388   trans->sinkpad = gst_pad_new_from_template (pad_template, "sink");
389   gst_pad_set_event_function (trans->sinkpad,
390       GST_DEBUG_FUNCPTR (gst_base_transform_sink_event));
391   gst_pad_set_chain_function (trans->sinkpad,
392       GST_DEBUG_FUNCPTR (gst_base_transform_chain));
393   gst_pad_set_activatemode_function (trans->sinkpad,
394       GST_DEBUG_FUNCPTR (gst_base_transform_sink_activate_mode));
395   gst_pad_set_query_function (trans->sinkpad,
396       GST_DEBUG_FUNCPTR (gst_base_transform_query));
397   gst_element_add_pad (GST_ELEMENT (trans), trans->sinkpad);
398 
399   pad_template =
400       gst_element_class_get_pad_template (GST_ELEMENT_CLASS (bclass), "src");
401   g_return_if_fail (pad_template != NULL);
402   trans->srcpad = gst_pad_new_from_template (pad_template, "src");
403   gst_pad_set_event_function (trans->srcpad,
404       GST_DEBUG_FUNCPTR (gst_base_transform_src_event));
405   gst_pad_set_getrange_function (trans->srcpad,
406       GST_DEBUG_FUNCPTR (gst_base_transform_getrange));
407   gst_pad_set_activatemode_function (trans->srcpad,
408       GST_DEBUG_FUNCPTR (gst_base_transform_src_activate_mode));
409   gst_pad_set_query_function (trans->srcpad,
410       GST_DEBUG_FUNCPTR (gst_base_transform_query));
411   gst_element_add_pad (GST_ELEMENT (trans), trans->srcpad);
412 
413   priv->qos_enabled = DEFAULT_PROP_QOS;
414   priv->cache_caps1 = NULL;
415   priv->cache_caps2 = NULL;
416   priv->pad_mode = GST_PAD_MODE_NONE;
417   priv->gap_aware = FALSE;
418   priv->prefer_passthrough = TRUE;
419 
420   priv->passthrough = FALSE;
421   if (bclass->transform == NULL) {
422     /* If no transform function, always_in_place is TRUE */
423     GST_DEBUG_OBJECT (trans, "setting in_place TRUE");
424     priv->always_in_place = TRUE;
425 
426     if (bclass->transform_ip == NULL) {
427       GST_DEBUG_OBJECT (trans, "setting passthrough TRUE");
428       priv->passthrough = TRUE;
429     }
430   }
431 
432   priv->processed = 0;
433   priv->dropped = 0;
434 }
435 
436 static GstCaps *
gst_base_transform_default_transform_caps(GstBaseTransform * trans,GstPadDirection direction,GstCaps * caps,GstCaps * filter)437 gst_base_transform_default_transform_caps (GstBaseTransform * trans,
438     GstPadDirection direction, GstCaps * caps, GstCaps * filter)
439 {
440   GstCaps *ret;
441 
442   GST_DEBUG_OBJECT (trans, "identity from: %" GST_PTR_FORMAT, caps);
443   /* no transform function, use the identity transform */
444   if (filter) {
445     ret = gst_caps_intersect_full (filter, caps, GST_CAPS_INTERSECT_FIRST);
446   } else {
447     ret = gst_caps_ref (caps);
448   }
449   return ret;
450 }
451 
452 /* given @caps on the src or sink pad (given by @direction)
453  * calculate the possible caps on the other pad.
454  *
455  * Returns new caps, unref after usage.
456  */
457 static GstCaps *
gst_base_transform_transform_caps(GstBaseTransform * trans,GstPadDirection direction,GstCaps * caps,GstCaps * filter)458 gst_base_transform_transform_caps (GstBaseTransform * trans,
459     GstPadDirection direction, GstCaps * caps, GstCaps * filter)
460 {
461   GstCaps *ret = NULL;
462   GstBaseTransformClass *klass;
463 
464   if (caps == NULL)
465     return NULL;
466 
467   klass = GST_BASE_TRANSFORM_GET_CLASS (trans);
468 
469   /* if there is a custom transform function, use this */
470   if (klass->transform_caps) {
471     GST_DEBUG_OBJECT (trans, "transform caps (direction = %d)", direction);
472 
473     GST_LOG_OBJECT (trans, "from: %" GST_PTR_FORMAT, caps);
474     ret = klass->transform_caps (trans, direction, caps, filter);
475     GST_LOG_OBJECT (trans, "  to: %" GST_PTR_FORMAT, ret);
476 
477 #ifdef GST_ENABLE_EXTRA_CHECKS
478     if (ret && filter) {
479       if (!gst_caps_is_subset (ret, filter)) {
480         GstCaps *intersection;
481 
482         GST_ERROR_OBJECT (trans,
483             "transform_caps returned caps %" GST_PTR_FORMAT
484             " which are not a real subset of the filter caps %"
485             GST_PTR_FORMAT, ret, filter);
486         g_warning ("%s: transform_caps returned caps which are not a real "
487             "subset of the filter caps", GST_ELEMENT_NAME (trans));
488 
489         intersection =
490             gst_caps_intersect_full (filter, ret, GST_CAPS_INTERSECT_FIRST);
491         gst_caps_unref (ret);
492         ret = intersection;
493       }
494     }
495 #endif
496   }
497 
498   GST_DEBUG_OBJECT (trans, "to: %" GST_PTR_FORMAT, ret);
499 
500   return ret;
501 }
502 
503 static gboolean
gst_base_transform_default_transform_meta(GstBaseTransform * trans,GstBuffer * outbuf,GstMeta * meta,GstBuffer * inbuf)504 gst_base_transform_default_transform_meta (GstBaseTransform * trans,
505     GstBuffer * outbuf, GstMeta * meta, GstBuffer * inbuf)
506 {
507   const GstMetaInfo *info = meta->info;
508   const gchar *const *tags;
509 
510   tags = gst_meta_api_type_get_tags (info->api);
511 
512   if (!tags)
513     return TRUE;
514 
515   return FALSE;
516 }
517 
518 static gboolean
gst_base_transform_default_transform_size(GstBaseTransform * trans,GstPadDirection direction,GstCaps * caps,gsize size,GstCaps * othercaps,gsize * othersize)519 gst_base_transform_default_transform_size (GstBaseTransform * trans,
520     GstPadDirection direction, GstCaps * caps, gsize size,
521     GstCaps * othercaps, gsize * othersize)
522 {
523   gsize inunitsize, outunitsize, units;
524   GstBaseTransformClass *klass;
525 
526   klass = GST_BASE_TRANSFORM_GET_CLASS (trans);
527 
528   if (klass->get_unit_size == NULL) {
529     /* if there is no transform_size and no unit_size, it means the
530      * element does not modify the size of a buffer */
531     *othersize = size;
532   } else {
533     /* there is no transform_size function, we have to use the unit_size
534      * functions. This method assumes there is a fixed unit_size associated with
535      * each caps. We provide the same amount of units on both sides. */
536     if (!gst_base_transform_get_unit_size (trans, caps, &inunitsize))
537       goto no_in_size;
538 
539     GST_DEBUG_OBJECT (trans,
540         "input size %" G_GSIZE_FORMAT ", input unit size %" G_GSIZE_FORMAT,
541         size, inunitsize);
542 
543     /* input size must be a multiple of the unit_size of the input caps */
544     if (inunitsize == 0 || (size % inunitsize != 0))
545       goto no_multiple;
546 
547     /* get the amount of units */
548     units = size / inunitsize;
549 
550     /* now get the unit size of the output */
551     if (!gst_base_transform_get_unit_size (trans, othercaps, &outunitsize))
552       goto no_out_size;
553 
554     /* the output size is the unit_size times the amount of units on the
555      * input */
556     *othersize = units * outunitsize;
557     GST_DEBUG_OBJECT (trans, "transformed size to %" G_GSIZE_FORMAT,
558         *othersize);
559   }
560   return TRUE;
561 
562   /* ERRORS */
563 no_in_size:
564   {
565     GST_DEBUG_OBJECT (trans, "could not get in_size");
566     g_warning ("%s: could not get in_size", GST_ELEMENT_NAME (trans));
567     return FALSE;
568   }
569 no_multiple:
570   {
571     GST_DEBUG_OBJECT (trans, "Size %" G_GSIZE_FORMAT " is not a multiple of"
572         "unit size %" G_GSIZE_FORMAT, size, inunitsize);
573     g_warning ("%s: size %" G_GSIZE_FORMAT " is not a multiple of unit size %"
574         G_GSIZE_FORMAT, GST_ELEMENT_NAME (trans), size, inunitsize);
575     return FALSE;
576   }
577 no_out_size:
578   {
579     GST_DEBUG_OBJECT (trans, "could not get out_size");
580     g_warning ("%s: could not get out_size", GST_ELEMENT_NAME (trans));
581     return FALSE;
582   }
583 }
584 
585 /* transform a buffer of @size with @caps on the pad with @direction to
586  * the size of a buffer with @othercaps and store the result in @othersize
587  *
588  * We have two ways of doing this:
589  *  1) use a custom transform size function, this is for complicated custom
590  *     cases with no fixed unit_size.
591  *  2) use the unit_size functions where there is a relationship between the
592  *     caps and the size of a buffer.
593  */
594 static gboolean
gst_base_transform_transform_size(GstBaseTransform * trans,GstPadDirection direction,GstCaps * caps,gsize size,GstCaps * othercaps,gsize * othersize)595 gst_base_transform_transform_size (GstBaseTransform * trans,
596     GstPadDirection direction, GstCaps * caps,
597     gsize size, GstCaps * othercaps, gsize * othersize)
598 {
599   GstBaseTransformClass *klass;
600   gboolean ret = FALSE;
601 
602   klass = GST_BASE_TRANSFORM_GET_CLASS (trans);
603 
604   GST_DEBUG_OBJECT (trans,
605       "asked to transform size %" G_GSIZE_FORMAT " for caps %"
606       GST_PTR_FORMAT " to size for caps %" GST_PTR_FORMAT " in direction %s",
607       size, caps, othercaps, direction == GST_PAD_SRC ? "SRC" : "SINK");
608 
609   if (klass->transform_size) {
610     /* if there is a custom transform function, use this */
611     ret = klass->transform_size (trans, direction, caps, size, othercaps,
612         othersize);
613   }
614   return ret;
615 }
616 
617 /* get the caps that can be handled by @pad. We perform:
618  *
619  *  - take the caps of peer of otherpad,
620  *  - filter against the padtemplate of otherpad,
621  *  - calculate all transforms of remaining caps
622  *  - filter against template of @pad
623  *
624  * If there is no peer, we simply return the caps of the padtemplate of pad.
625  */
626 static GstCaps *
gst_base_transform_query_caps(GstBaseTransform * trans,GstPad * pad,GstCaps * filter)627 gst_base_transform_query_caps (GstBaseTransform * trans, GstPad * pad,
628     GstCaps * filter)
629 {
630   GstPad *otherpad;
631   GstCaps *peercaps = NULL, *caps, *temp, *peerfilter = NULL;
632   GstCaps *templ, *otempl;
633 
634   otherpad = (pad == trans->srcpad) ? trans->sinkpad : trans->srcpad;
635 
636   templ = gst_pad_get_pad_template_caps (pad);
637   otempl = gst_pad_get_pad_template_caps (otherpad);
638 
639   /* first prepare the filter to be sent onwards. We need to filter and
640    * transform it to valid caps for the otherpad. */
641   if (filter) {
642     GST_DEBUG_OBJECT (pad, "filter caps  %" GST_PTR_FORMAT, filter);
643 
644     /* filtered against our padtemplate of this pad */
645     GST_DEBUG_OBJECT (pad, "our template  %" GST_PTR_FORMAT, templ);
646     temp = gst_caps_intersect_full (filter, templ, GST_CAPS_INTERSECT_FIRST);
647     GST_DEBUG_OBJECT (pad, "intersected %" GST_PTR_FORMAT, temp);
648 
649     /* then see what we can transform this to */
650     peerfilter = gst_base_transform_transform_caps (trans,
651         GST_PAD_DIRECTION (pad), temp, NULL);
652     GST_DEBUG_OBJECT (pad, "transformed  %" GST_PTR_FORMAT, peerfilter);
653     gst_caps_unref (temp);
654 
655     if (peerfilter) {
656       if (!gst_caps_is_empty (peerfilter)) {
657         /* and filter against the template of the other pad */
658         GST_DEBUG_OBJECT (pad, "our template  %" GST_PTR_FORMAT, otempl);
659         /* We keep the caps sorted like the returned caps */
660         temp =
661             gst_caps_intersect_full (peerfilter, otempl,
662             GST_CAPS_INTERSECT_FIRST);
663         GST_DEBUG_OBJECT (pad, "intersected %" GST_PTR_FORMAT, temp);
664         gst_caps_unref (peerfilter);
665         peerfilter = temp;
666       }
667 
668       /* If we filter out everything, bail out */
669       if (peerfilter && gst_caps_is_empty (peerfilter)) {
670         GST_DEBUG_OBJECT (pad, "peer filter caps are empty");
671         caps = peerfilter;
672         peerfilter = NULL;
673         goto done;
674       }
675     }
676   }
677 
678   GST_DEBUG_OBJECT (pad, "peer filter caps %" GST_PTR_FORMAT, peerfilter);
679 
680   /* query the peer with the transformed filter */
681   peercaps = gst_pad_peer_query_caps (otherpad, peerfilter);
682 
683   if (peerfilter)
684     gst_caps_unref (peerfilter);
685 
686   if (peercaps) {
687     GST_DEBUG_OBJECT (pad, "peer caps  %" GST_PTR_FORMAT, peercaps);
688 
689     /* filtered against our padtemplate on the other side */
690     GST_DEBUG_OBJECT (pad, "our template  %" GST_PTR_FORMAT, otempl);
691     temp = gst_caps_intersect_full (peercaps, otempl, GST_CAPS_INTERSECT_FIRST);
692     GST_DEBUG_OBJECT (pad, "intersected %" GST_PTR_FORMAT, temp);
693   } else {
694     temp = gst_caps_ref (otempl);
695   }
696 
697   /* then see what we can transform this to */
698   caps = gst_base_transform_transform_caps (trans,
699       GST_PAD_DIRECTION (otherpad), temp, filter);
700   GST_DEBUG_OBJECT (pad, "transformed  %" GST_PTR_FORMAT, caps);
701   gst_caps_unref (temp);
702   if (caps == NULL || gst_caps_is_empty (caps))
703     goto done;
704 
705   if (peercaps) {
706     /* and filter against the template of this pad */
707     GST_DEBUG_OBJECT (pad, "our template  %" GST_PTR_FORMAT, templ);
708     /* We keep the caps sorted like the returned caps */
709     temp = gst_caps_intersect_full (caps, templ, GST_CAPS_INTERSECT_FIRST);
710     GST_DEBUG_OBJECT (pad, "intersected %" GST_PTR_FORMAT, temp);
711     gst_caps_unref (caps);
712     caps = temp;
713 
714     if (trans->priv->prefer_passthrough) {
715       /* Now try if we can put the untransformed downstream caps first */
716       temp = gst_caps_intersect_full (peercaps, caps, GST_CAPS_INTERSECT_FIRST);
717       if (!gst_caps_is_empty (temp)) {
718         caps = gst_caps_merge (temp, caps);
719       } else {
720         gst_caps_unref (temp);
721       }
722     }
723   } else {
724     gst_caps_unref (caps);
725     /* no peer or the peer can do anything, our padtemplate is enough then */
726     if (filter) {
727       caps = gst_caps_intersect_full (filter, templ, GST_CAPS_INTERSECT_FIRST);
728     } else {
729       caps = gst_caps_ref (templ);
730     }
731   }
732 
733 done:
734   GST_DEBUG_OBJECT (trans, "returning  %" GST_PTR_FORMAT, caps);
735 
736   if (peercaps)
737     gst_caps_unref (peercaps);
738 
739   gst_caps_unref (templ);
740   gst_caps_unref (otempl);
741 
742   return caps;
743 }
744 
745 /* takes ownership of the pool, allocator and query */
746 static gboolean
gst_base_transform_set_allocation(GstBaseTransform * trans,GstBufferPool * pool,GstAllocator * allocator,const GstAllocationParams * params,GstQuery * query)747 gst_base_transform_set_allocation (GstBaseTransform * trans,
748     GstBufferPool * pool, GstAllocator * allocator,
749     const GstAllocationParams * params, GstQuery * query)
750 {
751   GstAllocator *oldalloc;
752   GstBufferPool *oldpool;
753   GstQuery *oldquery;
754   GstBaseTransformPrivate *priv = trans->priv;
755 
756   GST_OBJECT_LOCK (trans);
757   oldpool = priv->pool;
758   priv->pool = pool;
759   priv->pool_active = FALSE;
760 
761   oldalloc = priv->allocator;
762   priv->allocator = allocator;
763 
764   oldquery = priv->query;
765   priv->query = query;
766 
767   if (params)
768     priv->params = *params;
769   else
770     gst_allocation_params_init (&priv->params);
771   GST_OBJECT_UNLOCK (trans);
772 
773   if (oldpool) {
774     GST_DEBUG_OBJECT (trans, "deactivating old pool %p", oldpool);
775     gst_buffer_pool_set_active (oldpool, FALSE);
776     gst_object_unref (oldpool);
777   }
778   if (oldalloc) {
779     gst_object_unref (oldalloc);
780   }
781   if (oldquery) {
782     gst_query_unref (oldquery);
783   }
784   return TRUE;
785 }
786 
787 static gboolean
gst_base_transform_default_decide_allocation(GstBaseTransform * trans,GstQuery * query)788 gst_base_transform_default_decide_allocation (GstBaseTransform * trans,
789     GstQuery * query)
790 {
791   guint i, n_metas;
792   GstBaseTransformClass *klass;
793   GstCaps *outcaps;
794   GstBufferPool *pool;
795   guint size, min, max;
796   GstAllocator *allocator;
797   GstAllocationParams params;
798   GstStructure *config;
799   gboolean update_allocator;
800 
801   klass = GST_BASE_TRANSFORM_GET_CLASS (trans);
802 
803   n_metas = gst_query_get_n_allocation_metas (query);
804   for (i = 0; i < n_metas; i++) {
805     GType api;
806     const GstStructure *params;
807     gboolean remove;
808 
809     api = gst_query_parse_nth_allocation_meta (query, i, &params);
810 
811     /* by default we remove all metadata, subclasses should implement a
812      * filter_meta function */
813     if (gst_meta_api_type_has_tag (api, _gst_meta_tag_memory)) {
814       /* remove all memory dependent metadata because we are going to have to
815        * allocate different memory for input and output. */
816       GST_LOG_OBJECT (trans, "removing memory specific metadata %s",
817           g_type_name (api));
818       remove = TRUE;
819     } else if (G_LIKELY (klass->filter_meta)) {
820       /* remove if the subclass said so */
821       remove = !klass->filter_meta (trans, query, api, params);
822       GST_LOG_OBJECT (trans, "filter_meta for api %s returned: %s",
823           g_type_name (api), (remove ? "remove" : "keep"));
824     } else {
825       GST_LOG_OBJECT (trans, "removing metadata %s", g_type_name (api));
826       remove = TRUE;
827     }
828 
829     if (remove) {
830       gst_query_remove_nth_allocation_meta (query, i);
831       i--;
832       n_metas--;
833     }
834   }
835 
836   gst_query_parse_allocation (query, &outcaps, NULL);
837 
838   /* we got configuration from our peer or the decide_allocation method,
839    * parse them */
840   if (gst_query_get_n_allocation_params (query) > 0) {
841     /* try the allocator */
842     gst_query_parse_nth_allocation_param (query, 0, &allocator, &params);
843     update_allocator = TRUE;
844   } else {
845     allocator = NULL;
846     gst_allocation_params_init (&params);
847     update_allocator = FALSE;
848   }
849 
850   if (gst_query_get_n_allocation_pools (query) > 0) {
851     gst_query_parse_nth_allocation_pool (query, 0, &pool, &size, &min, &max);
852 
853     if (pool == NULL) {
854       /* no pool, we can make our own */
855       GST_DEBUG_OBJECT (trans, "no pool, making new pool");
856       pool = gst_buffer_pool_new ();
857     }
858   } else {
859     pool = NULL;
860     size = min = max = 0;
861   }
862 
863   /* now configure */
864   if (pool) {
865     config = gst_buffer_pool_get_config (pool);
866     gst_buffer_pool_config_set_params (config, outcaps, size, min, max);
867     gst_buffer_pool_config_set_allocator (config, allocator, &params);
868 
869     /* buffer pool may have to do some changes */
870     if (!gst_buffer_pool_set_config (pool, config)) {
871       config = gst_buffer_pool_get_config (pool);
872 
873       /* If change are not acceptable, fallback to generic pool */
874       if (!gst_buffer_pool_config_validate_params (config, outcaps, size, min,
875               max)) {
876         GST_DEBUG_OBJECT (trans, "unsupported pool, making new pool");
877 
878         gst_object_unref (pool);
879         pool = gst_buffer_pool_new ();
880         gst_buffer_pool_config_set_params (config, outcaps, size, min, max);
881         gst_buffer_pool_config_set_allocator (config, allocator, &params);
882       }
883 
884       if (!gst_buffer_pool_set_config (pool, config))
885         goto config_failed;
886     }
887   }
888 
889   if (update_allocator)
890     gst_query_set_nth_allocation_param (query, 0, allocator, &params);
891   else
892     gst_query_add_allocation_param (query, allocator, &params);
893   if (allocator)
894     gst_object_unref (allocator);
895 
896   if (pool) {
897     gst_query_set_nth_allocation_pool (query, 0, pool, size, min, max);
898     gst_object_unref (pool);
899   }
900 
901   return TRUE;
902 
903 config_failed:
904   if (pool)
905     gst_object_unref (pool);
906 
907   GST_ELEMENT_ERROR (trans, RESOURCE, SETTINGS,
908       ("Failed to configure the buffer pool"),
909       ("Configuration is most likely invalid, please report this issue."));
910   return FALSE;
911 }
912 
913 static gboolean
gst_base_transform_do_bufferpool(GstBaseTransform * trans,GstCaps * outcaps)914 gst_base_transform_do_bufferpool (GstBaseTransform * trans, GstCaps * outcaps)
915 {
916   GstQuery *query;
917   gboolean result = TRUE;
918   GstBufferPool *pool = NULL;
919   GstBaseTransformClass *klass;
920   GstBaseTransformPrivate *priv = trans->priv;
921   GstAllocator *allocator;
922   GstAllocationParams params;
923 
924   /* there are these possibilities:
925    *
926    * 1) we negotiated passthrough, we can proxy the bufferpool directly and we
927    *    will do that whenever some upstream does an allocation query.
928    * 2) we need to do a transform, we need to get a bufferpool from downstream
929    *    and configure it. When upstream does the ALLOCATION query, the
930    *    propose_allocation vmethod will be called and we will configure the
931    *    upstream allocator with our proposed values then.
932    */
933   if (priv->passthrough || priv->always_in_place) {
934     /* we are in passthrough, the input buffer is never copied and always passed
935      * along. We never allocate an output buffer on the srcpad. What we do is
936      * let the upstream element decide if it wants to use a bufferpool and
937      * then we will proxy the downstream pool */
938     GST_DEBUG_OBJECT (trans, "we're passthough, delay bufferpool");
939     gst_base_transform_set_allocation (trans, NULL, NULL, NULL, NULL);
940     return TRUE;
941   }
942 
943   /* not passthrough, we need to allocate */
944   /* find a pool for the negotiated caps now */
945   GST_DEBUG_OBJECT (trans, "doing allocation query");
946   query = gst_query_new_allocation (outcaps, TRUE);
947   if (!gst_pad_peer_query (trans->srcpad, query)) {
948     /* not a problem, just debug a little */
949     GST_DEBUG_OBJECT (trans, "peer ALLOCATION query failed");
950   }
951 
952   klass = GST_BASE_TRANSFORM_GET_CLASS (trans);
953 
954   GST_DEBUG_OBJECT (trans, "calling decide_allocation");
955   g_assert (klass->decide_allocation != NULL);
956   result = klass->decide_allocation (trans, query);
957 
958   GST_DEBUG_OBJECT (trans, "ALLOCATION (%d) params: %" GST_PTR_FORMAT, result,
959       query);
960 
961   if (!result)
962     goto no_decide_allocation;
963 
964   /* check again in case the sub-class have switch to passthrough/in-place
965    * after looking at the meta APIs */
966   if (priv->passthrough || priv->always_in_place) {
967     GST_DEBUG_OBJECT (trans, "no doing passthrough, delay bufferpool");
968     gst_base_transform_set_allocation (trans, NULL, NULL, NULL, NULL);
969     gst_query_unref (query);
970     return TRUE;
971   }
972 
973   /* we got configuration from our peer or the decide_allocation method,
974    * parse them */
975   if (gst_query_get_n_allocation_params (query) > 0) {
976     gst_query_parse_nth_allocation_param (query, 0, &allocator, &params);
977   } else {
978     allocator = NULL;
979     gst_allocation_params_init (&params);
980   }
981 
982   if (gst_query_get_n_allocation_pools (query) > 0)
983     gst_query_parse_nth_allocation_pool (query, 0, &pool, NULL, NULL, NULL);
984 
985   /* now store */
986   result =
987       gst_base_transform_set_allocation (trans, pool, allocator, &params,
988       query);
989 
990   return result;
991 
992   /* Errors */
993 no_decide_allocation:
994   {
995     GST_WARNING_OBJECT (trans, "Subclass failed to decide allocation");
996     gst_query_unref (query);
997 
998     return result;
999   }
1000 }
1001 
1002 /* function triggered when the in and out caps are negotiated and need
1003  * to be configured in the subclass. */
1004 static gboolean
gst_base_transform_configure_caps(GstBaseTransform * trans,GstCaps * in,GstCaps * out)1005 gst_base_transform_configure_caps (GstBaseTransform * trans, GstCaps * in,
1006     GstCaps * out)
1007 {
1008   gboolean ret = TRUE;
1009   GstBaseTransformClass *klass;
1010   GstBaseTransformPrivate *priv = trans->priv;
1011 
1012   klass = GST_BASE_TRANSFORM_GET_CLASS (trans);
1013 
1014   GST_DEBUG_OBJECT (trans, "in caps:  %" GST_PTR_FORMAT, in);
1015   GST_DEBUG_OBJECT (trans, "out caps: %" GST_PTR_FORMAT, out);
1016 
1017   /* clear the cache */
1018   gst_caps_replace (&priv->cache_caps1, NULL);
1019   gst_caps_replace (&priv->cache_caps2, NULL);
1020 
1021   /* figure out same caps state */
1022   priv->have_same_caps = gst_caps_is_equal (in, out);
1023   GST_DEBUG_OBJECT (trans, "have_same_caps: %d", priv->have_same_caps);
1024 
1025   /* Set the passthrough if the class wants passthrough_on_same_caps
1026    * and we have the same caps on each pad */
1027   if (klass->passthrough_on_same_caps)
1028     gst_base_transform_set_passthrough (trans, priv->have_same_caps);
1029 
1030   /* now configure the element with the caps */
1031   if (klass->set_caps) {
1032     GST_DEBUG_OBJECT (trans, "Calling set_caps method to setup caps");
1033     ret = klass->set_caps (trans, in, out);
1034   }
1035 
1036   return ret;
1037 }
1038 
1039 static GstCaps *
gst_base_transform_default_fixate_caps(GstBaseTransform * trans,GstPadDirection direction,GstCaps * caps,GstCaps * othercaps)1040 gst_base_transform_default_fixate_caps (GstBaseTransform * trans,
1041     GstPadDirection direction, GstCaps * caps, GstCaps * othercaps)
1042 {
1043   othercaps = gst_caps_fixate (othercaps);
1044   GST_DEBUG_OBJECT (trans, "fixated to %" GST_PTR_FORMAT, othercaps);
1045 
1046   return othercaps;
1047 }
1048 
1049 /* given a fixed @caps on @pad, create the best possible caps for the
1050  * other pad.
1051  * @caps must be fixed when calling this function.
1052  *
1053  * This function calls the transform caps vmethod of the basetransform to figure
1054  * out the possible target formats. It then tries to select the best format from
1055  * this list by:
1056  *
1057  * - attempt passthrough if the target caps is a superset of the input caps
1058  * - fixating by using peer caps
1059  * - fixating with transform fixate function
1060  * - fixating with pad fixate functions.
1061  *
1062  * this function returns a caps that can be transformed into and is accepted by
1063  * the peer element.
1064  */
1065 static GstCaps *
gst_base_transform_find_transform(GstBaseTransform * trans,GstPad * pad,GstCaps * caps)1066 gst_base_transform_find_transform (GstBaseTransform * trans, GstPad * pad,
1067     GstCaps * caps)
1068 {
1069   GstBaseTransformClass *klass;
1070   GstPad *otherpad, *otherpeer;
1071   GstCaps *othercaps;
1072   gboolean is_fixed;
1073 
1074   /* caps must be fixed here, this is a programming error if it's not */
1075   g_return_val_if_fail (gst_caps_is_fixed (caps), NULL);
1076 
1077   klass = GST_BASE_TRANSFORM_GET_CLASS (trans);
1078 
1079   otherpad = (pad == trans->srcpad) ? trans->sinkpad : trans->srcpad;
1080   otherpeer = gst_pad_get_peer (otherpad);
1081 
1082   /* see how we can transform the input caps. We need to do this even for
1083    * passthrough because it might be possible that this element cannot support
1084    * passthrough at all. */
1085   othercaps = gst_base_transform_transform_caps (trans,
1086       GST_PAD_DIRECTION (pad), caps, NULL);
1087 
1088   /* The caps we can actually output is the intersection of the transformed
1089    * caps with the pad template for the pad */
1090   if (othercaps && !gst_caps_is_empty (othercaps)) {
1091     GstCaps *intersect, *templ_caps;
1092 
1093     templ_caps = gst_pad_get_pad_template_caps (otherpad);
1094     GST_DEBUG_OBJECT (trans,
1095         "intersecting against padtemplate %" GST_PTR_FORMAT, templ_caps);
1096 
1097     intersect =
1098         gst_caps_intersect_full (othercaps, templ_caps,
1099         GST_CAPS_INTERSECT_FIRST);
1100 
1101     gst_caps_unref (othercaps);
1102     gst_caps_unref (templ_caps);
1103     othercaps = intersect;
1104   }
1105 
1106   /* check if transform is empty */
1107   if (!othercaps || gst_caps_is_empty (othercaps))
1108     goto no_transform;
1109 
1110   /* if the othercaps are not fixed, we need to fixate them, first attempt
1111    * is by attempting passthrough if the othercaps are a superset of caps. */
1112   /* FIXME. maybe the caps is not fixed because it has multiple structures of
1113    * fixed caps */
1114   is_fixed = gst_caps_is_fixed (othercaps);
1115   if (!is_fixed) {
1116     GST_DEBUG_OBJECT (trans,
1117         "transform returned non fixed  %" GST_PTR_FORMAT, othercaps);
1118 
1119     /* Now let's see what the peer suggests based on our transformed caps */
1120     if (otherpeer) {
1121       GstCaps *peercaps, *intersection, *templ_caps;
1122 
1123       GST_DEBUG_OBJECT (trans,
1124           "Checking peer caps with filter %" GST_PTR_FORMAT, othercaps);
1125 
1126       peercaps = gst_pad_query_caps (otherpeer, othercaps);
1127       GST_DEBUG_OBJECT (trans, "Resulted in %" GST_PTR_FORMAT, peercaps);
1128       if (!gst_caps_is_empty (peercaps)) {
1129         templ_caps = gst_pad_get_pad_template_caps (otherpad);
1130 
1131         GST_DEBUG_OBJECT (trans,
1132             "Intersecting with template caps %" GST_PTR_FORMAT, templ_caps);
1133 
1134         intersection =
1135             gst_caps_intersect_full (peercaps, templ_caps,
1136             GST_CAPS_INTERSECT_FIRST);
1137         GST_DEBUG_OBJECT (trans, "Intersection: %" GST_PTR_FORMAT,
1138             intersection);
1139         gst_caps_unref (peercaps);
1140         gst_caps_unref (templ_caps);
1141         peercaps = intersection;
1142 
1143         GST_DEBUG_OBJECT (trans,
1144             "Intersecting with transformed caps %" GST_PTR_FORMAT, othercaps);
1145         intersection =
1146             gst_caps_intersect_full (peercaps, othercaps,
1147             GST_CAPS_INTERSECT_FIRST);
1148         GST_DEBUG_OBJECT (trans, "Intersection: %" GST_PTR_FORMAT,
1149             intersection);
1150         gst_caps_unref (peercaps);
1151         gst_caps_unref (othercaps);
1152         othercaps = intersection;
1153       } else {
1154         gst_caps_unref (othercaps);
1155         othercaps = peercaps;
1156       }
1157 
1158       is_fixed = gst_caps_is_fixed (othercaps);
1159     } else {
1160       GST_DEBUG_OBJECT (trans, "no peer, doing passthrough");
1161       gst_caps_unref (othercaps);
1162       othercaps = gst_caps_ref (caps);
1163       is_fixed = TRUE;
1164     }
1165   }
1166   if (gst_caps_is_empty (othercaps))
1167     goto no_transform_possible;
1168 
1169   GST_DEBUG ("have %sfixed caps %" GST_PTR_FORMAT, (is_fixed ? "" : "non-"),
1170       othercaps);
1171 
1172   /* second attempt at fixation, call the fixate vmethod */
1173   /* caps could be fixed but the subclass may want to add fields */
1174   if (klass->fixate_caps) {
1175     GST_DEBUG_OBJECT (trans, "calling fixate_caps for %" GST_PTR_FORMAT
1176         " using caps %" GST_PTR_FORMAT " on pad %s:%s", othercaps, caps,
1177         GST_DEBUG_PAD_NAME (otherpad));
1178     /* note that we pass the complete array of structures to the fixate
1179      * function, it needs to truncate itself */
1180     othercaps =
1181         klass->fixate_caps (trans, GST_PAD_DIRECTION (pad), caps, othercaps);
1182 
1183     if (!othercaps) {
1184       g_critical ("basetransform: second attempt to fixate caps returned "
1185           "invalid (NULL) caps on pad %s:%s", GST_DEBUG_PAD_NAME (pad));
1186     }
1187     is_fixed = othercaps && gst_caps_is_fixed (othercaps);
1188     GST_DEBUG_OBJECT (trans, "after fixating %" GST_PTR_FORMAT, othercaps);
1189   }
1190 
1191   /* caps should be fixed now, if not we have to fail. */
1192   if (!is_fixed)
1193     goto could_not_fixate;
1194 
1195   /* and peer should accept */
1196   if (otherpeer && !gst_pad_query_accept_caps (otherpeer, othercaps))
1197     goto peer_no_accept;
1198 
1199   GST_DEBUG_OBJECT (trans, "Input caps were %" GST_PTR_FORMAT
1200       ", and got final caps %" GST_PTR_FORMAT, caps, othercaps);
1201 
1202   if (otherpeer)
1203     gst_object_unref (otherpeer);
1204 
1205   return othercaps;
1206 
1207   /* ERRORS */
1208 no_transform:
1209   {
1210     GST_DEBUG_OBJECT (trans,
1211         "transform returned useless  %" GST_PTR_FORMAT, othercaps);
1212     goto error_cleanup;
1213   }
1214 no_transform_possible:
1215   {
1216     GST_DEBUG_OBJECT (trans,
1217         "transform could not transform %" GST_PTR_FORMAT
1218         " in anything we support", caps);
1219     goto error_cleanup;
1220   }
1221 could_not_fixate:
1222   {
1223     GST_DEBUG_OBJECT (trans, "FAILED to fixate %" GST_PTR_FORMAT, othercaps);
1224     goto error_cleanup;
1225   }
1226 peer_no_accept:
1227   {
1228     GST_DEBUG_OBJECT (trans, "FAILED to get peer of %" GST_PTR_FORMAT
1229         " to accept %" GST_PTR_FORMAT, otherpad, othercaps);
1230     goto error_cleanup;
1231   }
1232 error_cleanup:
1233   {
1234     if (otherpeer)
1235       gst_object_unref (otherpeer);
1236     if (othercaps)
1237       gst_caps_unref (othercaps);
1238     return NULL;
1239   }
1240 }
1241 
1242 static gboolean
gst_base_transform_acceptcaps_default(GstBaseTransform * trans,GstPadDirection direction,GstCaps * caps)1243 gst_base_transform_acceptcaps_default (GstBaseTransform * trans,
1244     GstPadDirection direction, GstCaps * caps)
1245 {
1246   GstPad *pad, *otherpad;
1247   GstCaps *templ, *otempl, *ocaps = NULL;
1248   gboolean ret = TRUE;
1249 
1250   pad =
1251       (direction ==
1252       GST_PAD_SINK) ? GST_BASE_TRANSFORM_SINK_PAD (trans) :
1253       GST_BASE_TRANSFORM_SRC_PAD (trans);
1254   otherpad =
1255       (direction ==
1256       GST_PAD_SINK) ? GST_BASE_TRANSFORM_SRC_PAD (trans) :
1257       GST_BASE_TRANSFORM_SINK_PAD (trans);
1258 
1259   GST_DEBUG_OBJECT (trans, "accept caps %" GST_PTR_FORMAT, caps);
1260 
1261   templ = gst_pad_get_pad_template_caps (pad);
1262   otempl = gst_pad_get_pad_template_caps (otherpad);
1263 
1264   /* get all the formats we can handle on this pad */
1265   GST_DEBUG_OBJECT (trans, "intersect with pad template: %" GST_PTR_FORMAT,
1266       templ);
1267   if (!gst_caps_can_intersect (caps, templ))
1268     goto reject_caps;
1269 
1270   GST_DEBUG_OBJECT (trans, "trying to transform with filter: %"
1271       GST_PTR_FORMAT " (the other pad template)", otempl);
1272   ocaps = gst_base_transform_transform_caps (trans, direction, caps, otempl);
1273   if (!ocaps || gst_caps_is_empty (ocaps))
1274     goto no_transform_possible;
1275 
1276 done:
1277   GST_DEBUG_OBJECT (trans, "accept-caps result: %d", ret);
1278   if (ocaps)
1279     gst_caps_unref (ocaps);
1280   gst_caps_unref (templ);
1281   gst_caps_unref (otempl);
1282   return ret;
1283 
1284   /* ERRORS */
1285 reject_caps:
1286   {
1287     GST_DEBUG_OBJECT (trans, "caps can't intersect with the template");
1288     ret = FALSE;
1289     goto done;
1290   }
1291 no_transform_possible:
1292   {
1293     GST_DEBUG_OBJECT (trans,
1294         "transform could not transform %" GST_PTR_FORMAT
1295         " in anything we support", caps);
1296     ret = FALSE;
1297     goto done;
1298   }
1299 }
1300 
1301 /* called when new caps arrive on the sink pad,
1302  * We try to find the best caps for the other side using our _find_transform()
1303  * function. If there are caps, we configure the transform for this new
1304  * transformation.
1305  */
1306 static gboolean
gst_base_transform_setcaps(GstBaseTransform * trans,GstPad * pad,GstCaps * incaps)1307 gst_base_transform_setcaps (GstBaseTransform * trans, GstPad * pad,
1308     GstCaps * incaps)
1309 {
1310   GstBaseTransformPrivate *priv = trans->priv;
1311   GstCaps *outcaps, *prev_incaps = NULL, *prev_outcaps = NULL;
1312   gboolean ret = TRUE;
1313 
1314   GST_DEBUG_OBJECT (pad, "have new caps %p %" GST_PTR_FORMAT, incaps, incaps);
1315 
1316   /* find best possible caps for the other pad */
1317   outcaps = gst_base_transform_find_transform (trans, pad, incaps);
1318   if (!outcaps || gst_caps_is_empty (outcaps))
1319     goto no_transform_possible;
1320 
1321   /* configure the element now */
1322 
1323   /* if we have the same caps, we can optimize and reuse the input caps */
1324   if (gst_caps_is_equal (incaps, outcaps)) {
1325     GST_INFO_OBJECT (trans, "reuse caps");
1326     gst_caps_unref (outcaps);
1327     outcaps = gst_caps_ref (incaps);
1328   }
1329 
1330   prev_incaps = gst_pad_get_current_caps (trans->sinkpad);
1331   prev_outcaps = gst_pad_get_current_caps (trans->srcpad);
1332   if (prev_incaps && prev_outcaps && gst_caps_is_equal (prev_incaps, incaps)
1333       && gst_caps_is_equal (prev_outcaps, outcaps)) {
1334     GST_DEBUG_OBJECT (trans,
1335         "New caps equal to old ones: %" GST_PTR_FORMAT " -> %" GST_PTR_FORMAT,
1336         incaps, outcaps);
1337     ret = TRUE;
1338   } else {
1339     /* call configure now */
1340     if (!(ret = gst_base_transform_configure_caps (trans, incaps, outcaps)))
1341       goto failed_configure;
1342 
1343     if (!prev_outcaps || !gst_caps_is_equal (outcaps, prev_outcaps))
1344       /* let downstream know about our caps */
1345       ret = gst_pad_set_caps (trans->srcpad, outcaps);
1346   }
1347 
1348   if (ret) {
1349     /* try to get a pool when needed */
1350     ret = gst_base_transform_do_bufferpool (trans, outcaps);
1351   }
1352 
1353 done:
1354   if (outcaps)
1355     gst_caps_unref (outcaps);
1356   if (prev_incaps)
1357     gst_caps_unref (prev_incaps);
1358   if (prev_outcaps)
1359     gst_caps_unref (prev_outcaps);
1360 
1361   GST_OBJECT_LOCK (trans);
1362   priv->negotiated = ret;
1363   GST_OBJECT_UNLOCK (trans);
1364 
1365   return ret;
1366 
1367   /* ERRORS */
1368 no_transform_possible:
1369   {
1370     GST_WARNING_OBJECT (trans,
1371         "transform could not transform %" GST_PTR_FORMAT
1372         " in anything we support", incaps);
1373     ret = FALSE;
1374     goto done;
1375   }
1376 failed_configure:
1377   {
1378     GST_WARNING_OBJECT (trans, "FAILED to configure incaps %" GST_PTR_FORMAT
1379         " and outcaps %" GST_PTR_FORMAT, incaps, outcaps);
1380     ret = FALSE;
1381     goto done;
1382   }
1383 }
1384 
1385 static gboolean
gst_base_transform_default_propose_allocation(GstBaseTransform * trans,GstQuery * decide_query,GstQuery * query)1386 gst_base_transform_default_propose_allocation (GstBaseTransform * trans,
1387     GstQuery * decide_query, GstQuery * query)
1388 {
1389   gboolean ret;
1390 
1391   if (decide_query == NULL) {
1392     GST_DEBUG_OBJECT (trans, "doing passthrough query");
1393     ret = gst_pad_peer_query (trans->srcpad, query);
1394   } else {
1395     guint i, n_metas;
1396     /* non-passthrough, copy all metadata, decide_query does not contain the
1397      * metadata anymore that depends on the buffer memory */
1398     n_metas = gst_query_get_n_allocation_metas (decide_query);
1399     for (i = 0; i < n_metas; i++) {
1400       GType api;
1401       const GstStructure *params;
1402 
1403       api = gst_query_parse_nth_allocation_meta (decide_query, i, &params);
1404       GST_DEBUG_OBJECT (trans, "proposing metadata %s", g_type_name (api));
1405       gst_query_add_allocation_meta (query, api, params);
1406     }
1407     ret = TRUE;
1408   }
1409   return ret;
1410 }
1411 
1412 static gboolean
gst_base_transform_reconfigure_unlocked(GstBaseTransform * trans)1413 gst_base_transform_reconfigure_unlocked (GstBaseTransform * trans)
1414 {
1415   gboolean reconfigure, ret = TRUE;
1416 
1417   reconfigure = gst_pad_check_reconfigure (trans->srcpad);
1418 
1419   if (G_UNLIKELY (reconfigure)) {
1420     GstCaps *incaps;
1421 
1422     GST_DEBUG_OBJECT (trans, "we had a pending reconfigure");
1423 
1424     incaps = gst_pad_get_current_caps (trans->sinkpad);
1425     if (incaps == NULL)
1426       goto done;
1427 
1428     /* if we need to reconfigure we pretend new caps arrived. This
1429      * will reconfigure the transform with the new output format. */
1430     if (!gst_base_transform_setcaps (trans, trans->sinkpad, incaps)) {
1431       GST_ELEMENT_WARNING (trans, STREAM, FORMAT,
1432           ("not negotiated"), ("not negotiated"));
1433       ret = FALSE;
1434     }
1435 
1436     gst_caps_unref (incaps);
1437   }
1438 
1439 done:
1440 
1441   if (!ret)
1442     gst_pad_mark_reconfigure (trans->srcpad);
1443 
1444   return ret;
1445 }
1446 
1447 /**
1448  * gst_base_transform_reconfigure:
1449  * @trans: the #GstBaseTransform to set
1450  *
1451  * Negotiates src pad caps with downstream elements if the source pad is
1452  * marked as needing reconfiguring. Unmarks GST_PAD_FLAG_NEED_RECONFIGURE in
1453  * any case. But marks it again if negotiation fails.
1454  *
1455  * Do not call this in the #GstBaseTransformClass::transform or
1456  * #GstBaseTransformClass::transform_ip vmethod. Call this in
1457  * #GstBaseTransformClass::submit_input_buffer,
1458  * #GstBaseTransformClass::prepare_output_buffer or in
1459  * #GstBaseTransformClass::generate_output _before_ any output buffer is
1460  * allocated.
1461  *
1462  * It will be default be called when handling an ALLOCATION query or at the
1463  * very beginning of the default #GstBaseTransformClass::submit_input_buffer
1464  * implementation.
1465  *
1466  * Returns: %TRUE if the negotiation succeeded, else %FALSE.
1467  *
1468  * Since: 1.18
1469  */
1470 gboolean
gst_base_transform_reconfigure(GstBaseTransform * trans)1471 gst_base_transform_reconfigure (GstBaseTransform * trans)
1472 {
1473   gboolean ret;
1474 
1475   g_return_val_if_fail (GST_IS_BASE_TRANSFORM (trans), FALSE);
1476 
1477   GST_PAD_STREAM_LOCK (trans->sinkpad);
1478   ret = gst_base_transform_reconfigure_unlocked (trans);
1479   if (!ret)
1480     gst_pad_mark_reconfigure (trans->srcpad);
1481   GST_PAD_STREAM_UNLOCK (trans->sinkpad);
1482 
1483   return ret;
1484 }
1485 
1486 static gboolean
gst_base_transform_default_query(GstBaseTransform * trans,GstPadDirection direction,GstQuery * query)1487 gst_base_transform_default_query (GstBaseTransform * trans,
1488     GstPadDirection direction, GstQuery * query)
1489 {
1490   gboolean ret = FALSE;
1491   GstPad *pad, *otherpad;
1492   GstBaseTransformClass *klass;
1493   GstBaseTransformPrivate *priv = trans->priv;
1494 
1495   if (direction == GST_PAD_SRC) {
1496     pad = trans->srcpad;
1497     otherpad = trans->sinkpad;
1498   } else {
1499     pad = trans->sinkpad;
1500     otherpad = trans->srcpad;
1501   }
1502 
1503   klass = GST_BASE_TRANSFORM_GET_CLASS (trans);
1504 
1505   switch (GST_QUERY_TYPE (query)) {
1506     case GST_QUERY_ALLOCATION:
1507     {
1508       GstQuery *decide_query = NULL;
1509 
1510       /* can only be done on the sinkpad */
1511       if (direction != GST_PAD_SINK)
1512         goto done;
1513 
1514       ret = gst_base_transform_reconfigure_unlocked (trans);
1515       if (G_UNLIKELY (!ret))
1516         goto done;
1517 
1518       GST_OBJECT_LOCK (trans);
1519       if (!priv->negotiated && !priv->passthrough && (klass->set_caps != NULL)) {
1520         GST_DEBUG_OBJECT (trans,
1521             "not negotiated yet but need negotiation, can't answer ALLOCATION query");
1522         GST_OBJECT_UNLOCK (trans);
1523         goto done;
1524       }
1525 
1526       decide_query = trans->priv->query;
1527       trans->priv->query = NULL;
1528       GST_OBJECT_UNLOCK (trans);
1529 
1530       GST_DEBUG_OBJECT (trans,
1531           "calling propose allocation with query %" GST_PTR_FORMAT,
1532           decide_query);
1533 
1534       /* pass the query to the propose_allocation vmethod if any */
1535       if (G_LIKELY (klass->propose_allocation))
1536         ret = klass->propose_allocation (trans, decide_query, query);
1537       else
1538         ret = FALSE;
1539 
1540       if (decide_query) {
1541         GST_OBJECT_LOCK (trans);
1542 
1543         if (trans->priv->query == NULL)
1544           trans->priv->query = decide_query;
1545         else
1546           gst_query_unref (decide_query);
1547 
1548         GST_OBJECT_UNLOCK (trans);
1549       }
1550 
1551       GST_DEBUG_OBJECT (trans, "ALLOCATION ret %d, %" GST_PTR_FORMAT, ret,
1552           query);
1553       break;
1554     }
1555     case GST_QUERY_POSITION:
1556     {
1557       GstFormat format;
1558 
1559       gst_query_parse_position (query, &format, NULL);
1560       if (format == GST_FORMAT_TIME && trans->segment.format == GST_FORMAT_TIME) {
1561         gint64 pos;
1562         ret = TRUE;
1563 
1564         if ((direction == GST_PAD_SINK)
1565             || (trans->priv->position_out == GST_CLOCK_TIME_NONE)) {
1566           pos =
1567               gst_segment_to_stream_time (&trans->segment, GST_FORMAT_TIME,
1568               trans->segment.position);
1569         } else {
1570           pos = gst_segment_to_stream_time (&trans->segment, GST_FORMAT_TIME,
1571               trans->priv->position_out);
1572         }
1573         gst_query_set_position (query, format, pos);
1574       } else {
1575         ret = gst_pad_peer_query (otherpad, query);
1576       }
1577       break;
1578     }
1579     case GST_QUERY_ACCEPT_CAPS:
1580     {
1581       GstCaps *caps;
1582 
1583       gst_query_parse_accept_caps (query, &caps);
1584       if (klass->accept_caps) {
1585         ret = klass->accept_caps (trans, direction, caps);
1586         gst_query_set_accept_caps_result (query, ret);
1587         /* return TRUE, we answered the query */
1588         ret = TRUE;
1589       }
1590       break;
1591     }
1592     case GST_QUERY_CAPS:
1593     {
1594       GstCaps *filter, *caps;
1595 
1596       gst_query_parse_caps (query, &filter);
1597       caps = gst_base_transform_query_caps (trans, pad, filter);
1598       if (!caps) {
1599         GST_WARNING_OBJECT (pad, "no caps can be handled by this pad");
1600         caps = gst_caps_new_empty ();
1601       }
1602       gst_query_set_caps_result (query, caps);
1603       gst_caps_unref (caps);
1604       ret = TRUE;
1605       break;
1606     }
1607     default:
1608       ret = gst_pad_peer_query (otherpad, query);
1609       break;
1610   }
1611 
1612 done:
1613   return ret;
1614 }
1615 
1616 static gboolean
gst_base_transform_query(GstPad * pad,GstObject * parent,GstQuery * query)1617 gst_base_transform_query (GstPad * pad, GstObject * parent, GstQuery * query)
1618 {
1619   GstBaseTransform *trans;
1620   GstBaseTransformClass *bclass;
1621   gboolean ret = FALSE;
1622 
1623   trans = GST_BASE_TRANSFORM_CAST (parent);
1624   bclass = GST_BASE_TRANSFORM_GET_CLASS (trans);
1625 
1626   if (bclass->query)
1627     ret = bclass->query (trans, GST_PAD_DIRECTION (pad), query);
1628 
1629   return ret;
1630 }
1631 
1632 /* this function either returns the input buffer without incrementing the
1633  * refcount or it allocates a new (writable) buffer */
1634 static GstFlowReturn
default_prepare_output_buffer(GstBaseTransform * trans,GstBuffer * inbuf,GstBuffer ** outbuf)1635 default_prepare_output_buffer (GstBaseTransform * trans,
1636     GstBuffer * inbuf, GstBuffer ** outbuf)
1637 {
1638   GstBaseTransformPrivate *priv;
1639   GstFlowReturn ret;
1640   GstBaseTransformClass *bclass;
1641   GstCaps *incaps, *outcaps;
1642   gsize insize, outsize;
1643   gboolean res;
1644 
1645   priv = trans->priv;
1646   bclass = GST_BASE_TRANSFORM_GET_CLASS (trans);
1647 
1648   /* figure out how to allocate an output buffer */
1649   if (priv->passthrough) {
1650     /* passthrough, we will not modify the incoming buffer so we can just
1651      * reuse it */
1652     GST_DEBUG_OBJECT (trans, "passthrough: reusing input buffer");
1653     *outbuf = inbuf;
1654     goto done;
1655   }
1656 
1657   /* we can't reuse the input buffer */
1658   if (priv->pool) {
1659     if (!priv->pool_active) {
1660       GST_DEBUG_OBJECT (trans, "setting pool %p active", priv->pool);
1661       if (!gst_buffer_pool_set_active (priv->pool, TRUE))
1662         goto activate_failed;
1663       priv->pool_active = TRUE;
1664     }
1665     GST_DEBUG_OBJECT (trans, "using pool alloc");
1666     ret = gst_buffer_pool_acquire_buffer (priv->pool, outbuf, NULL);
1667     if (ret != GST_FLOW_OK)
1668       goto alloc_failed;
1669 
1670     goto copy_meta;
1671   }
1672 
1673   /* no pool, we need to figure out the size of the output buffer first */
1674   if ((bclass->transform_ip != NULL) && priv->always_in_place) {
1675     /* we want to do an in-place alloc */
1676     if (gst_buffer_is_writable (inbuf)) {
1677       GST_DEBUG_OBJECT (trans, "inplace reuse writable input buffer");
1678       *outbuf = inbuf;
1679     } else {
1680       GST_DEBUG_OBJECT (trans, "making writable buffer copy");
1681       /* we make a copy of the input buffer */
1682       *outbuf = gst_buffer_copy (inbuf);
1683     }
1684     goto done;
1685   }
1686 
1687   /* else use the transform function to get the size */
1688   incaps = gst_pad_get_current_caps (trans->sinkpad);
1689   outcaps = gst_pad_get_current_caps (trans->srcpad);
1690 
1691   /* srcpad might be flushing already if we're being shut down */
1692   if (outcaps == NULL)
1693     goto no_outcaps;
1694 
1695   GST_DEBUG_OBJECT (trans, "getting output size for alloc");
1696   /* copy transform, figure out the output size */
1697   insize = gst_buffer_get_size (inbuf);
1698   res = gst_base_transform_transform_size (trans,
1699       GST_PAD_SINK, incaps, insize, outcaps, &outsize);
1700 
1701   gst_caps_unref (incaps);
1702   gst_caps_unref (outcaps);
1703 
1704   if (!res)
1705     goto unknown_size;
1706 
1707   GST_DEBUG_OBJECT (trans, "doing alloc of size %" G_GSIZE_FORMAT, outsize);
1708   *outbuf = gst_buffer_new_allocate (priv->allocator, outsize, &priv->params);
1709   if (!*outbuf) {
1710     ret = GST_FLOW_ERROR;
1711     goto alloc_failed;
1712   }
1713 
1714 copy_meta:
1715   /* copy the metadata */
1716   if (bclass->copy_metadata)
1717     if (!bclass->copy_metadata (trans, inbuf, *outbuf)) {
1718       /* something failed, post a warning */
1719       GST_ELEMENT_WARNING (trans, STREAM, NOT_IMPLEMENTED,
1720           ("could not copy metadata"), (NULL));
1721     }
1722 
1723 done:
1724   return GST_FLOW_OK;
1725 
1726   /* ERRORS */
1727 activate_failed:
1728   {
1729     GST_ELEMENT_ERROR (trans, RESOURCE, SETTINGS,
1730         ("failed to activate bufferpool"), ("failed to activate bufferpool"));
1731     return GST_FLOW_ERROR;
1732   }
1733 unknown_size:
1734   {
1735     GST_ERROR_OBJECT (trans, "unknown output size");
1736     return GST_FLOW_ERROR;
1737   }
1738 alloc_failed:
1739   {
1740     GST_DEBUG_OBJECT (trans, "could not allocate buffer from pool");
1741     return ret;
1742   }
1743 no_outcaps:
1744   {
1745     GST_DEBUG_OBJECT (trans, "no output caps, source pad has been deactivated");
1746     gst_caps_unref (incaps);
1747     return GST_FLOW_FLUSHING;
1748   }
1749 }
1750 
1751 typedef struct
1752 {
1753   GstBaseTransform *trans;
1754   GstBuffer *outbuf;
1755 } CopyMetaData;
1756 
1757 static gboolean
foreach_metadata(GstBuffer * inbuf,GstMeta ** meta,gpointer user_data)1758 foreach_metadata (GstBuffer * inbuf, GstMeta ** meta, gpointer user_data)
1759 {
1760   CopyMetaData *data = user_data;
1761   GstBaseTransform *trans = data->trans;
1762   GstBaseTransformClass *klass;
1763   const GstMetaInfo *info = (*meta)->info;
1764   GstBuffer *outbuf = data->outbuf;
1765   gboolean do_copy = FALSE;
1766 
1767   klass = GST_BASE_TRANSFORM_GET_CLASS (trans);
1768 
1769   if (gst_meta_api_type_has_tag (info->api, _gst_meta_tag_memory)) {
1770     /* never call the transform_meta with memory specific metadata */
1771     GST_DEBUG_OBJECT (trans, "not copying memory specific metadata %s",
1772         g_type_name (info->api));
1773     do_copy = FALSE;
1774   } else if (klass->transform_meta) {
1775     do_copy = klass->transform_meta (trans, outbuf, *meta, inbuf);
1776     GST_DEBUG_OBJECT (trans, "transformed metadata %s: copy: %d",
1777         g_type_name (info->api), do_copy);
1778   }
1779 
1780   /* we only copy metadata when the subclass implemented a transform_meta
1781    * function and when it returns %TRUE */
1782   if (do_copy) {
1783     GstMetaTransformCopy copy_data = { FALSE, 0, -1 };
1784     /* simply copy then */
1785     if (info->transform_func) {
1786       GST_DEBUG_OBJECT (trans, "copy metadata %s", g_type_name (info->api));
1787       info->transform_func (outbuf, *meta, inbuf,
1788           _gst_meta_transform_copy, &copy_data);
1789     } else {
1790       GST_DEBUG_OBJECT (trans, "couldn't copy metadata %s",
1791           g_type_name (info->api));
1792     }
1793   }
1794   return TRUE;
1795 }
1796 
1797 static gboolean
default_copy_metadata(GstBaseTransform * trans,GstBuffer * inbuf,GstBuffer * outbuf)1798 default_copy_metadata (GstBaseTransform * trans,
1799     GstBuffer * inbuf, GstBuffer * outbuf)
1800 {
1801   GstBaseTransformPrivate *priv = trans->priv;
1802   CopyMetaData data;
1803 
1804   /* now copy the metadata */
1805   GST_DEBUG_OBJECT (trans, "copying metadata");
1806 
1807   /* this should not happen, buffers allocated from a pool or with
1808    * new_allocate should always be writable. */
1809   if (!gst_buffer_is_writable (outbuf))
1810     goto not_writable;
1811 
1812   /* when we get here, the metadata should be writable */
1813   gst_buffer_copy_into (outbuf, inbuf,
1814       GST_BUFFER_COPY_FLAGS | GST_BUFFER_COPY_TIMESTAMPS, 0, -1);
1815 
1816   /* clear the GAP flag when the subclass does not understand it */
1817   if (!priv->gap_aware)
1818     GST_BUFFER_FLAG_UNSET (outbuf, GST_BUFFER_FLAG_GAP);
1819 
1820 
1821   data.trans = trans;
1822   data.outbuf = outbuf;
1823 
1824   gst_buffer_foreach_meta (inbuf, foreach_metadata, &data);
1825 
1826   return TRUE;
1827 
1828   /* ERRORS */
1829 not_writable:
1830   {
1831     GST_WARNING_OBJECT (trans, "buffer %p not writable", outbuf);
1832     return FALSE;
1833   }
1834 }
1835 
1836 /* Given @caps calculate the size of one unit.
1837  *
1838  * For video caps, this is the size of one frame (and thus one buffer).
1839  * For audio caps, this is the size of one sample.
1840  *
1841  * These values are cached since they do not change and the calculation
1842  * potentially involves parsing caps and other expensive stuff.
1843  *
1844  * We have two cache locations to store the size, one for the source caps
1845  * and one for the sink caps.
1846  *
1847  * this function returns %FALSE if no size could be calculated.
1848  */
1849 static gboolean
gst_base_transform_get_unit_size(GstBaseTransform * trans,GstCaps * caps,gsize * size)1850 gst_base_transform_get_unit_size (GstBaseTransform * trans, GstCaps * caps,
1851     gsize * size)
1852 {
1853   gboolean res = FALSE;
1854   GstBaseTransformClass *bclass;
1855   GstBaseTransformPrivate *priv = trans->priv;
1856 
1857   /* see if we have the result cached */
1858   if (priv->cache_caps1 == caps) {
1859     *size = priv->cache_caps1_size;
1860     GST_DEBUG_OBJECT (trans,
1861         "returned %" G_GSIZE_FORMAT " from first cache", *size);
1862     return TRUE;
1863   }
1864   if (priv->cache_caps2 == caps) {
1865     *size = priv->cache_caps2_size;
1866     GST_DEBUG_OBJECT (trans,
1867         "returned %" G_GSIZE_FORMAT " from second cached", *size);
1868     return TRUE;
1869   }
1870 
1871   bclass = GST_BASE_TRANSFORM_GET_CLASS (trans);
1872   res = bclass->get_unit_size (trans, caps, size);
1873   GST_DEBUG_OBJECT (trans,
1874       "caps %" GST_PTR_FORMAT " has unit size %" G_GSIZE_FORMAT ", res %s",
1875       caps, *size, res ? "TRUE" : "FALSE");
1876 
1877   if (res) {
1878     /* and cache the values */
1879     if (priv->cache_caps1 == NULL) {
1880       gst_caps_replace (&priv->cache_caps1, caps);
1881       priv->cache_caps1_size = *size;
1882       GST_DEBUG_OBJECT (trans,
1883           "caching %" G_GSIZE_FORMAT " in first cache", *size);
1884     } else if (priv->cache_caps2 == NULL) {
1885       gst_caps_replace (&priv->cache_caps2, caps);
1886       priv->cache_caps2_size = *size;
1887       GST_DEBUG_OBJECT (trans,
1888           "caching %" G_GSIZE_FORMAT " in second cache", *size);
1889     } else {
1890       GST_DEBUG_OBJECT (trans, "no free spot to cache unit_size");
1891     }
1892   }
1893   return res;
1894 }
1895 
1896 static gboolean
gst_base_transform_sink_event(GstPad * pad,GstObject * parent,GstEvent * event)1897 gst_base_transform_sink_event (GstPad * pad, GstObject * parent,
1898     GstEvent * event)
1899 {
1900   GstBaseTransform *trans;
1901   GstBaseTransformClass *bclass;
1902   gboolean ret = TRUE;
1903 
1904   trans = GST_BASE_TRANSFORM_CAST (parent);
1905   bclass = GST_BASE_TRANSFORM_GET_CLASS (trans);
1906 
1907   if (bclass->sink_event)
1908     ret = bclass->sink_event (trans, event);
1909   else
1910     gst_event_unref (event);
1911 
1912   return ret;
1913 }
1914 
1915 static gboolean
gst_base_transform_sink_eventfunc(GstBaseTransform * trans,GstEvent * event)1916 gst_base_transform_sink_eventfunc (GstBaseTransform * trans, GstEvent * event)
1917 {
1918   gboolean ret = TRUE, forward = TRUE;
1919   GstBaseTransformPrivate *priv = trans->priv;
1920 
1921   switch (GST_EVENT_TYPE (event)) {
1922     case GST_EVENT_FLUSH_START:
1923       break;
1924     case GST_EVENT_FLUSH_STOP:
1925       GST_OBJECT_LOCK (trans);
1926       /* reset QoS parameters */
1927       priv->proportion = 1.0;
1928       priv->earliest_time = -1;
1929       priv->discont = FALSE;
1930       priv->processed = 0;
1931       priv->dropped = 0;
1932       GST_OBJECT_UNLOCK (trans);
1933       /* we need new segment info after the flush. */
1934       trans->have_segment = FALSE;
1935       gst_segment_init (&trans->segment, GST_FORMAT_UNDEFINED);
1936       priv->position_out = GST_CLOCK_TIME_NONE;
1937       break;
1938     case GST_EVENT_EOS:
1939       break;
1940     case GST_EVENT_TAG:
1941       break;
1942     case GST_EVENT_CAPS:
1943     {
1944       GstCaps *caps;
1945 
1946       gst_event_parse_caps (event, &caps);
1947       /* clear any pending reconfigure flag */
1948       gst_pad_check_reconfigure (trans->srcpad);
1949       ret = gst_base_transform_setcaps (trans, trans->sinkpad, caps);
1950       if (!ret)
1951         gst_pad_mark_reconfigure (trans->srcpad);
1952 
1953       forward = FALSE;
1954       break;
1955     }
1956     case GST_EVENT_SEGMENT:
1957     {
1958       gst_event_copy_segment (event, &trans->segment);
1959       trans->have_segment = TRUE;
1960 
1961       GST_DEBUG_OBJECT (trans, "received SEGMENT %" GST_SEGMENT_FORMAT,
1962           &trans->segment);
1963       break;
1964     }
1965     default:
1966       break;
1967   }
1968 
1969   if (ret && forward)
1970     ret = gst_pad_push_event (trans->srcpad, event);
1971   else
1972     gst_event_unref (event);
1973 
1974   return ret;
1975 }
1976 
1977 static gboolean
gst_base_transform_src_event(GstPad * pad,GstObject * parent,GstEvent * event)1978 gst_base_transform_src_event (GstPad * pad, GstObject * parent,
1979     GstEvent * event)
1980 {
1981   GstBaseTransform *trans;
1982   GstBaseTransformClass *bclass;
1983   gboolean ret = TRUE;
1984 
1985   trans = GST_BASE_TRANSFORM_CAST (parent);
1986   bclass = GST_BASE_TRANSFORM_GET_CLASS (trans);
1987 
1988   if (bclass->src_event)
1989     ret = bclass->src_event (trans, event);
1990   else
1991     gst_event_unref (event);
1992 
1993   return ret;
1994 }
1995 
1996 static gboolean
gst_base_transform_src_eventfunc(GstBaseTransform * trans,GstEvent * event)1997 gst_base_transform_src_eventfunc (GstBaseTransform * trans, GstEvent * event)
1998 {
1999   gboolean ret;
2000 
2001   GST_DEBUG_OBJECT (trans, "handling event %p %" GST_PTR_FORMAT, event, event);
2002 
2003   switch (GST_EVENT_TYPE (event)) {
2004     case GST_EVENT_SEEK:
2005       break;
2006     case GST_EVENT_NAVIGATION:
2007       break;
2008     case GST_EVENT_QOS:
2009     {
2010       gdouble proportion;
2011       GstClockTimeDiff diff;
2012       GstClockTime timestamp;
2013 
2014       gst_event_parse_qos (event, NULL, &proportion, &diff, &timestamp);
2015       gst_base_transform_update_qos (trans, proportion, diff, timestamp);
2016       break;
2017     }
2018     default:
2019       break;
2020   }
2021 
2022   ret = gst_pad_push_event (trans->sinkpad, event);
2023 
2024   return ret;
2025 }
2026 
2027 /* Takes the input buffer */
2028 static GstFlowReturn
default_submit_input_buffer(GstBaseTransform * trans,gboolean is_discont,GstBuffer * inbuf)2029 default_submit_input_buffer (GstBaseTransform * trans, gboolean is_discont,
2030     GstBuffer * inbuf)
2031 {
2032   GstBaseTransformClass *bclass = GST_BASE_TRANSFORM_GET_CLASS (trans);
2033   GstBaseTransformPrivate *priv = trans->priv;
2034   GstFlowReturn ret = GST_FLOW_OK;
2035   GstClockTime running_time;
2036   GstClockTime timestamp;
2037 
2038   if (G_UNLIKELY (!gst_base_transform_reconfigure_unlocked (trans)))
2039     goto not_negotiated;
2040 
2041   if (GST_BUFFER_OFFSET_IS_VALID (inbuf))
2042     GST_DEBUG_OBJECT (trans,
2043         "handling buffer %p of size %" G_GSIZE_FORMAT ", PTS %" GST_TIME_FORMAT
2044         " and offset %" G_GUINT64_FORMAT, inbuf, gst_buffer_get_size (inbuf),
2045         GST_TIME_ARGS (GST_BUFFER_PTS (inbuf)), GST_BUFFER_OFFSET (inbuf));
2046   else
2047     GST_DEBUG_OBJECT (trans,
2048         "handling buffer %p of size %" G_GSIZE_FORMAT ", PTS %" GST_TIME_FORMAT
2049         " and offset NONE", inbuf, gst_buffer_get_size (inbuf),
2050         GST_TIME_ARGS (GST_BUFFER_PTS (inbuf)));
2051 
2052   /* Don't allow buffer handling before negotiation, except in passthrough mode
2053    * or if the class doesn't implement a set_caps function (in which case it doesn't
2054    * care about caps)
2055    */
2056   if (!priv->negotiated && !priv->passthrough && (bclass->set_caps != NULL))
2057     goto not_negotiated;
2058 
2059   /* can only do QoS if the segment is in TIME */
2060   if (trans->segment.format != GST_FORMAT_TIME)
2061     goto no_qos;
2062 
2063   /* QOS is done on the running time of the buffer, get it now */
2064   timestamp = GST_BUFFER_TIMESTAMP (inbuf);
2065   running_time = gst_segment_to_running_time (&trans->segment, GST_FORMAT_TIME,
2066       timestamp);
2067 
2068   if (running_time != -1) {
2069     gboolean need_skip;
2070     GstClockTime earliest_time;
2071     gdouble proportion;
2072 
2073     /* lock for getting the QoS parameters that are set (in a different thread)
2074      * with the QOS events */
2075     GST_OBJECT_LOCK (trans);
2076     earliest_time = priv->earliest_time;
2077     proportion = priv->proportion;
2078     /* check for QoS, don't perform conversion for buffers
2079      * that are known to be late. */
2080     need_skip = priv->qos_enabled &&
2081         earliest_time != -1 && running_time <= earliest_time;
2082     GST_OBJECT_UNLOCK (trans);
2083 
2084     if (need_skip) {
2085       GstMessage *qos_msg;
2086       GstClockTime duration;
2087       guint64 stream_time;
2088       gint64 jitter;
2089 
2090       GST_CAT_DEBUG_OBJECT (GST_CAT_QOS, trans, "skipping transform: qostime %"
2091           GST_TIME_FORMAT " <= %" GST_TIME_FORMAT,
2092           GST_TIME_ARGS (running_time), GST_TIME_ARGS (earliest_time));
2093 
2094       priv->dropped++;
2095 
2096       duration = GST_BUFFER_DURATION (inbuf);
2097       stream_time =
2098           gst_segment_to_stream_time (&trans->segment, GST_FORMAT_TIME,
2099           timestamp);
2100       jitter = GST_CLOCK_DIFF (running_time, earliest_time);
2101 
2102       qos_msg =
2103           gst_message_new_qos (GST_OBJECT_CAST (trans), FALSE, running_time,
2104           stream_time, timestamp, duration);
2105       gst_message_set_qos_values (qos_msg, jitter, proportion, 1000000);
2106       gst_message_set_qos_stats (qos_msg, GST_FORMAT_BUFFERS,
2107           priv->processed, priv->dropped);
2108       gst_element_post_message (GST_ELEMENT_CAST (trans), qos_msg);
2109 
2110       /* mark discont for next buffer */
2111       priv->discont = TRUE;
2112       ret = GST_BASE_TRANSFORM_FLOW_DROPPED;
2113       goto skip;
2114     }
2115   }
2116 
2117 no_qos:
2118   /* Stash input buffer where the default generate_output
2119    * function can find it */
2120   if (trans->queued_buf)
2121     gst_buffer_unref (trans->queued_buf);
2122   trans->queued_buf = inbuf;
2123   return ret;
2124 skip:
2125   gst_buffer_unref (inbuf);
2126   return ret;
2127 
2128 not_negotiated:
2129   {
2130     gst_buffer_unref (inbuf);
2131     if (GST_PAD_IS_FLUSHING (trans->srcpad))
2132       return GST_FLOW_FLUSHING;
2133     return GST_FLOW_NOT_NEGOTIATED;
2134   }
2135 }
2136 
2137 static GstFlowReturn
default_generate_output(GstBaseTransform * trans,GstBuffer ** outbuf)2138 default_generate_output (GstBaseTransform * trans, GstBuffer ** outbuf)
2139 {
2140   GstBaseTransformClass *bclass = GST_BASE_TRANSFORM_GET_CLASS (trans);
2141   GstBaseTransformPrivate *priv = trans->priv;
2142   GstFlowReturn ret = GST_FLOW_OK;
2143   GstBuffer *inbuf;
2144   gboolean want_in_place;
2145 
2146   /* Retrieve stashed input buffer, if the default submit_input_buffer
2147    * was run. Takes ownership back from there */
2148   inbuf = trans->queued_buf;
2149   trans->queued_buf = NULL;
2150 
2151   /* This default processing method needs one input buffer to feed to
2152    * the transform functions, we can't do anything without it */
2153   if (inbuf == NULL)
2154     return GST_FLOW_OK;
2155 
2156   /* first try to allocate an output buffer based on the currently negotiated
2157    * format. outbuf will contain a buffer suitable for doing the configured
2158    * transform after this function. */
2159   if (bclass->prepare_output_buffer == NULL)
2160     goto no_prepare;
2161 
2162   GST_DEBUG_OBJECT (trans, "calling prepare buffer");
2163   ret = bclass->prepare_output_buffer (trans, inbuf, outbuf);
2164 
2165   if (ret != GST_FLOW_OK || *outbuf == NULL)
2166     goto no_buffer;
2167 
2168   GST_DEBUG_OBJECT (trans, "using allocated buffer in %p, out %p", inbuf,
2169       *outbuf);
2170 
2171   /* now perform the needed transform */
2172   if (priv->passthrough) {
2173     /* In passthrough mode, give transform_ip a look at the
2174      * buffer, without making it writable, or just push the
2175      * data through */
2176     if (bclass->transform_ip_on_passthrough && bclass->transform_ip) {
2177       GST_DEBUG_OBJECT (trans, "doing passthrough transform_ip");
2178       ret = bclass->transform_ip (trans, *outbuf);
2179     } else {
2180       GST_DEBUG_OBJECT (trans, "element is in passthrough");
2181     }
2182   } else {
2183     want_in_place = (bclass->transform_ip != NULL) && priv->always_in_place;
2184 
2185     if (want_in_place) {
2186       GST_DEBUG_OBJECT (trans, "doing inplace transform");
2187       ret = bclass->transform_ip (trans, *outbuf);
2188     } else {
2189       GST_DEBUG_OBJECT (trans, "doing non-inplace transform");
2190 
2191       if (bclass->transform)
2192         ret = bclass->transform (trans, inbuf, *outbuf);
2193       else
2194         ret = GST_FLOW_NOT_SUPPORTED;
2195     }
2196   }
2197 
2198   /* only unref input buffer if we allocated a new outbuf buffer. If we reused
2199    * the input buffer, no refcount is changed to keep the input buffer writable
2200    * when needed. */
2201   if (*outbuf != inbuf)
2202     gst_buffer_unref (inbuf);
2203 
2204   return ret;
2205 
2206   /* ERRORS */
2207 no_prepare:
2208   {
2209     gst_buffer_unref (inbuf);
2210     GST_ELEMENT_ERROR (trans, STREAM, NOT_IMPLEMENTED,
2211         ("Sub-class has no prepare_output_buffer implementation"), (NULL));
2212     return GST_FLOW_NOT_SUPPORTED;
2213   }
2214 no_buffer:
2215   {
2216     gst_buffer_unref (inbuf);
2217     *outbuf = NULL;
2218     GST_WARNING_OBJECT (trans, "could not get buffer from pool: %s",
2219         gst_flow_get_name (ret));
2220     return ret;
2221   }
2222 }
2223 
2224 /* FIXME, getrange is broken, need to pull range from the other
2225  * end based on the transform_size result.
2226  */
2227 static GstFlowReturn
gst_base_transform_getrange(GstPad * pad,GstObject * parent,guint64 offset,guint length,GstBuffer ** buffer)2228 gst_base_transform_getrange (GstPad * pad, GstObject * parent, guint64 offset,
2229     guint length, GstBuffer ** buffer)
2230 {
2231   GstBaseTransformClass *klass = GST_BASE_TRANSFORM_GET_CLASS (parent);
2232   GstBaseTransform *trans = GST_BASE_TRANSFORM_CAST (parent);
2233   GstBaseTransformPrivate *priv = trans->priv;
2234   GstFlowReturn ret;
2235   GstBuffer *inbuf = NULL;
2236   GstBuffer *outbuf = NULL;
2237 
2238   /* Try and generate a buffer, if the sub-class wants more data,
2239    * pull some and repeat until a buffer (or error) is produced */
2240   do {
2241     ret = klass->generate_output (trans, &outbuf);
2242 
2243     /* Consume the DROPPED return value and go get more data */
2244     if (ret == GST_BASE_TRANSFORM_FLOW_DROPPED)
2245       ret = GST_FLOW_OK;
2246 
2247     if (ret != GST_FLOW_OK || outbuf != NULL)
2248       break;
2249 
2250     /* No buffer generated, try and pull data */
2251     ret = gst_pad_pull_range (trans->sinkpad, offset, length, &inbuf);
2252     if (G_UNLIKELY (ret != GST_FLOW_OK))
2253       goto pull_error;
2254 
2255     if (klass->before_transform)
2256       klass->before_transform (trans, inbuf);
2257 
2258     /* Set discont flag so we can mark the next outgoing buffer */
2259     if (GST_BUFFER_IS_DISCONT (inbuf)) {
2260       GST_DEBUG_OBJECT (trans, "got DISCONT buffer %p", inbuf);
2261       priv->discont = TRUE;
2262     }
2263 
2264     /* FIXME: Input offsets and lengths need to be translated, as per
2265      * the FIXME above. For now, just advance somewhat */
2266     offset += gst_buffer_get_size (inbuf);
2267 
2268     ret = klass->submit_input_buffer (trans, priv->discont, inbuf);
2269     if (ret != GST_FLOW_OK) {
2270       if (ret == GST_BASE_TRANSFORM_FLOW_DROPPED)
2271         ret = GST_FLOW_OK;
2272       goto done;
2273     }
2274   } while (ret == GST_FLOW_OK && outbuf == NULL);
2275 
2276   *buffer = outbuf;
2277   if (outbuf) {
2278     /* apply DISCONT flag if the buffer is not yet marked as such */
2279     if (priv->discont) {
2280       GST_DEBUG_OBJECT (trans, "we have a pending DISCONT");
2281       if (!GST_BUFFER_IS_DISCONT (outbuf)) {
2282         GST_DEBUG_OBJECT (trans, "marking DISCONT on output buffer");
2283         outbuf = gst_buffer_make_writable (outbuf);
2284         GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_DISCONT);
2285       }
2286       priv->discont = FALSE;
2287     }
2288     priv->processed++;
2289   }
2290 done:
2291   return ret;
2292 
2293   /* ERRORS */
2294 pull_error:
2295   {
2296     GST_DEBUG_OBJECT (trans, "failed to pull a buffer: %s",
2297         gst_flow_get_name (ret));
2298     goto done;
2299   }
2300 }
2301 
2302 /* The flow of the chain function is the reverse of the
2303  * getrange() function - we have data, feed it to the sub-class
2304  * and then iterate, pushing buffers it generates until it either
2305  * wants more data or returns an error */
2306 static GstFlowReturn
gst_base_transform_chain(GstPad * pad,GstObject * parent,GstBuffer * buffer)2307 gst_base_transform_chain (GstPad * pad, GstObject * parent, GstBuffer * buffer)
2308 {
2309   GstBaseTransform *trans = GST_BASE_TRANSFORM_CAST (parent);
2310   GstBaseTransformClass *klass = GST_BASE_TRANSFORM_GET_CLASS (trans);
2311   GstBaseTransformPrivate *priv = trans->priv;
2312   GstFlowReturn ret;
2313   GstClockTime position = GST_CLOCK_TIME_NONE;
2314   GstClockTime timestamp, duration;
2315   GstBuffer *outbuf = NULL;
2316 
2317   timestamp = GST_BUFFER_TIMESTAMP (buffer);
2318   duration = GST_BUFFER_DURATION (buffer);
2319 
2320   /* calculate end position of the incoming buffer */
2321   if (timestamp != GST_CLOCK_TIME_NONE) {
2322     if (duration != GST_CLOCK_TIME_NONE)
2323       position = timestamp + duration;
2324     else
2325       position = timestamp;
2326   }
2327 
2328   if (klass->before_transform)
2329     klass->before_transform (trans, buffer);
2330 
2331   /* Set discont flag so we can mark the outgoing buffer */
2332   if (GST_BUFFER_IS_DISCONT (buffer)) {
2333     GST_DEBUG_OBJECT (trans, "got DISCONT buffer %p", buffer);
2334     priv->discont = TRUE;
2335   }
2336 
2337   /* Takes ownership of input buffer */
2338   ret = klass->submit_input_buffer (trans, priv->discont, buffer);
2339   if (ret != GST_FLOW_OK)
2340     goto done;
2341 
2342   do {
2343     outbuf = NULL;
2344 
2345     ret = klass->generate_output (trans, &outbuf);
2346 
2347     /* outbuf can be NULL, this means a dropped buffer, if we have a buffer but
2348      * GST_BASE_TRANSFORM_FLOW_DROPPED we will not push either. */
2349     if (outbuf != NULL) {
2350       if (ret == GST_FLOW_OK) {
2351         GstClockTime position_out = GST_CLOCK_TIME_NONE;
2352 
2353         /* Remember last stop position */
2354         if (position != GST_CLOCK_TIME_NONE &&
2355             trans->segment.format == GST_FORMAT_TIME)
2356           trans->segment.position = position;
2357 
2358         if (GST_BUFFER_TIMESTAMP_IS_VALID (outbuf)) {
2359           position_out = GST_BUFFER_TIMESTAMP (outbuf);
2360           if (GST_BUFFER_DURATION_IS_VALID (outbuf))
2361             position_out += GST_BUFFER_DURATION (outbuf);
2362         } else if (position != GST_CLOCK_TIME_NONE) {
2363           position_out = position;
2364         }
2365         if (position_out != GST_CLOCK_TIME_NONE
2366             && trans->segment.format == GST_FORMAT_TIME)
2367           priv->position_out = position_out;
2368 
2369         /* apply DISCONT flag if the buffer is not yet marked as such */
2370         if (trans->priv->discont) {
2371           GST_DEBUG_OBJECT (trans, "we have a pending DISCONT");
2372           if (!GST_BUFFER_IS_DISCONT (outbuf)) {
2373             GST_DEBUG_OBJECT (trans, "marking DISCONT on output buffer");
2374             outbuf = gst_buffer_make_writable (outbuf);
2375             GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_DISCONT);
2376           }
2377           priv->discont = FALSE;
2378         }
2379         priv->processed++;
2380 
2381         ret = gst_pad_push (trans->srcpad, outbuf);
2382       } else {
2383         GST_DEBUG_OBJECT (trans, "we got return %s", gst_flow_get_name (ret));
2384         gst_buffer_unref (outbuf);
2385       }
2386     }
2387   } while (ret == GST_FLOW_OK && outbuf != NULL);
2388 
2389 done:
2390   /* convert internal flow to OK and mark discont for the next buffer. */
2391   if (ret == GST_BASE_TRANSFORM_FLOW_DROPPED) {
2392     GST_DEBUG_OBJECT (trans, "dropped a buffer, marking DISCONT");
2393     priv->discont = TRUE;
2394     ret = GST_FLOW_OK;
2395   }
2396 
2397   return ret;
2398 }
2399 
2400 static void
gst_base_transform_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)2401 gst_base_transform_set_property (GObject * object, guint prop_id,
2402     const GValue * value, GParamSpec * pspec)
2403 {
2404   GstBaseTransform *trans;
2405 
2406   trans = GST_BASE_TRANSFORM_CAST (object);
2407 
2408   switch (prop_id) {
2409     case PROP_QOS:
2410       gst_base_transform_set_qos_enabled (trans, g_value_get_boolean (value));
2411       break;
2412     default:
2413       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2414       break;
2415   }
2416 }
2417 
2418 static void
gst_base_transform_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)2419 gst_base_transform_get_property (GObject * object, guint prop_id,
2420     GValue * value, GParamSpec * pspec)
2421 {
2422   GstBaseTransform *trans;
2423 
2424   trans = GST_BASE_TRANSFORM_CAST (object);
2425 
2426   switch (prop_id) {
2427     case PROP_QOS:
2428       g_value_set_boolean (value, gst_base_transform_is_qos_enabled (trans));
2429       break;
2430     default:
2431       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2432       break;
2433   }
2434 }
2435 
2436 /* not a vmethod of anything, just an internal method */
2437 static gboolean
gst_base_transform_activate(GstBaseTransform * trans,gboolean active)2438 gst_base_transform_activate (GstBaseTransform * trans, gboolean active)
2439 {
2440   GstBaseTransformClass *bclass;
2441   GstBaseTransformPrivate *priv = trans->priv;
2442   gboolean result = TRUE;
2443 
2444   bclass = GST_BASE_TRANSFORM_GET_CLASS (trans);
2445 
2446   if (active) {
2447     GstCaps *incaps, *outcaps;
2448 
2449     if (priv->pad_mode == GST_PAD_MODE_NONE && bclass->start)
2450       result &= bclass->start (trans);
2451 
2452     incaps = gst_pad_get_current_caps (trans->sinkpad);
2453     outcaps = gst_pad_get_current_caps (trans->srcpad);
2454 
2455     GST_OBJECT_LOCK (trans);
2456     if (incaps && outcaps)
2457       priv->have_same_caps =
2458           gst_caps_is_equal (incaps, outcaps) || priv->passthrough;
2459     else
2460       priv->have_same_caps = priv->passthrough;
2461     GST_DEBUG_OBJECT (trans, "have_same_caps %d", priv->have_same_caps);
2462     priv->negotiated = FALSE;
2463     trans->have_segment = FALSE;
2464     gst_segment_init (&trans->segment, GST_FORMAT_UNDEFINED);
2465     priv->position_out = GST_CLOCK_TIME_NONE;
2466     priv->proportion = 1.0;
2467     priv->earliest_time = -1;
2468     priv->discont = FALSE;
2469     priv->processed = 0;
2470     priv->dropped = 0;
2471     GST_OBJECT_UNLOCK (trans);
2472 
2473     if (incaps)
2474       gst_caps_unref (incaps);
2475     if (outcaps)
2476       gst_caps_unref (outcaps);
2477   } else {
2478     /* We must make sure streaming has finished before resetting things
2479      * and calling the ::stop vfunc */
2480     GST_PAD_STREAM_LOCK (trans->sinkpad);
2481     GST_PAD_STREAM_UNLOCK (trans->sinkpad);
2482 
2483     priv->have_same_caps = FALSE;
2484     /* We can only reset the passthrough mode if the instance told us to
2485        handle it in configure_caps */
2486     if (bclass->passthrough_on_same_caps) {
2487       gst_base_transform_set_passthrough (trans, FALSE);
2488     }
2489     gst_caps_replace (&priv->cache_caps1, NULL);
2490     gst_caps_replace (&priv->cache_caps2, NULL);
2491 
2492     /* Make sure any left over buffer is freed */
2493     gst_buffer_replace (&trans->queued_buf, NULL);
2494 
2495     if (priv->pad_mode != GST_PAD_MODE_NONE && bclass->stop)
2496       result &= bclass->stop (trans);
2497 
2498     gst_base_transform_set_allocation (trans, NULL, NULL, NULL, NULL);
2499   }
2500 
2501   return result;
2502 }
2503 
2504 static gboolean
gst_base_transform_sink_activate_mode(GstPad * pad,GstObject * parent,GstPadMode mode,gboolean active)2505 gst_base_transform_sink_activate_mode (GstPad * pad, GstObject * parent,
2506     GstPadMode mode, gboolean active)
2507 {
2508   gboolean result = FALSE;
2509   GstBaseTransform *trans;
2510 
2511   trans = GST_BASE_TRANSFORM_CAST (parent);
2512 
2513   switch (mode) {
2514     case GST_PAD_MODE_PUSH:
2515     {
2516       result = gst_base_transform_activate (trans, active);
2517 
2518       if (result)
2519         trans->priv->pad_mode = active ? GST_PAD_MODE_PUSH : GST_PAD_MODE_NONE;
2520 
2521       break;
2522     }
2523     default:
2524       result = TRUE;
2525       break;
2526   }
2527   return result;
2528 }
2529 
2530 static gboolean
gst_base_transform_src_activate_mode(GstPad * pad,GstObject * parent,GstPadMode mode,gboolean active)2531 gst_base_transform_src_activate_mode (GstPad * pad, GstObject * parent,
2532     GstPadMode mode, gboolean active)
2533 {
2534   gboolean result = FALSE;
2535   GstBaseTransform *trans;
2536 
2537   trans = GST_BASE_TRANSFORM_CAST (parent);
2538 
2539   switch (mode) {
2540     case GST_PAD_MODE_PULL:
2541     {
2542       result =
2543           gst_pad_activate_mode (trans->sinkpad, GST_PAD_MODE_PULL, active);
2544 
2545       if (result)
2546         result &= gst_base_transform_activate (trans, active);
2547 
2548       if (result)
2549         trans->priv->pad_mode = active ? mode : GST_PAD_MODE_NONE;
2550       break;
2551     }
2552     default:
2553       result = TRUE;
2554       break;
2555   }
2556 
2557   return result;
2558 }
2559 
2560 /**
2561  * gst_base_transform_set_passthrough:
2562  * @trans: the #GstBaseTransform to set
2563  * @passthrough: boolean indicating passthrough mode.
2564  *
2565  * Set passthrough mode for this filter by default. This is mostly
2566  * useful for filters that do not care about negotiation.
2567  *
2568  * Always %TRUE for filters which don't implement either a transform
2569  * or transform_ip or generate_output method.
2570  *
2571  * MT safe.
2572  */
2573 void
gst_base_transform_set_passthrough(GstBaseTransform * trans,gboolean passthrough)2574 gst_base_transform_set_passthrough (GstBaseTransform * trans,
2575     gboolean passthrough)
2576 {
2577   GstBaseTransformClass *bclass;
2578 
2579   g_return_if_fail (GST_IS_BASE_TRANSFORM (trans));
2580 
2581   bclass = GST_BASE_TRANSFORM_GET_CLASS (trans);
2582 
2583   GST_OBJECT_LOCK (trans);
2584   if (!passthrough) {
2585     if (bclass->transform_ip || bclass->transform || (bclass->generate_output
2586             && bclass->generate_output != default_generate_output))
2587       trans->priv->passthrough = FALSE;
2588   } else {
2589     trans->priv->passthrough = TRUE;
2590   }
2591 
2592   GST_DEBUG_OBJECT (trans, "set passthrough %d", trans->priv->passthrough);
2593   GST_OBJECT_UNLOCK (trans);
2594 }
2595 
2596 /**
2597  * gst_base_transform_is_passthrough:
2598  * @trans: the #GstBaseTransform to query
2599  *
2600  * See if @trans is configured as a passthrough transform.
2601  *
2602  * Returns: %TRUE if the transform is configured in passthrough mode.
2603  *
2604  * MT safe.
2605  */
2606 gboolean
gst_base_transform_is_passthrough(GstBaseTransform * trans)2607 gst_base_transform_is_passthrough (GstBaseTransform * trans)
2608 {
2609   gboolean result;
2610 
2611   g_return_val_if_fail (GST_IS_BASE_TRANSFORM (trans), FALSE);
2612 
2613   GST_OBJECT_LOCK (trans);
2614   result = trans->priv->passthrough;
2615   GST_OBJECT_UNLOCK (trans);
2616 
2617   return result;
2618 }
2619 
2620 /**
2621  * gst_base_transform_set_in_place:
2622  * @trans: the #GstBaseTransform to modify
2623  * @in_place: Boolean value indicating that we would like to operate
2624  * on in_place buffers.
2625  *
2626  * Determines whether a non-writable buffer will be copied before passing
2627  * to the transform_ip function.
2628  *
2629  *   * Always %TRUE if no transform function is implemented.
2630  *   * Always %FALSE if ONLY transform function is implemented.
2631  *
2632  * MT safe.
2633  */
2634 void
gst_base_transform_set_in_place(GstBaseTransform * trans,gboolean in_place)2635 gst_base_transform_set_in_place (GstBaseTransform * trans, gboolean in_place)
2636 {
2637   GstBaseTransformClass *bclass;
2638 
2639   g_return_if_fail (GST_IS_BASE_TRANSFORM (trans));
2640 
2641   bclass = GST_BASE_TRANSFORM_GET_CLASS (trans);
2642 
2643   GST_OBJECT_LOCK (trans);
2644 
2645   if (in_place) {
2646     if (bclass->transform_ip) {
2647       GST_DEBUG_OBJECT (trans, "setting in_place TRUE");
2648       trans->priv->always_in_place = TRUE;
2649     }
2650   } else {
2651     if (bclass->transform) {
2652       GST_DEBUG_OBJECT (trans, "setting in_place FALSE");
2653       trans->priv->always_in_place = FALSE;
2654     }
2655   }
2656 
2657   GST_OBJECT_UNLOCK (trans);
2658 }
2659 
2660 /**
2661  * gst_base_transform_is_in_place:
2662  * @trans: the #GstBaseTransform to query
2663  *
2664  * See if @trans is configured as a in_place transform.
2665  *
2666  * Returns: %TRUE if the transform is configured in in_place mode.
2667  *
2668  * MT safe.
2669  */
2670 gboolean
gst_base_transform_is_in_place(GstBaseTransform * trans)2671 gst_base_transform_is_in_place (GstBaseTransform * trans)
2672 {
2673   gboolean result;
2674 
2675   g_return_val_if_fail (GST_IS_BASE_TRANSFORM (trans), FALSE);
2676 
2677   GST_OBJECT_LOCK (trans);
2678   result = trans->priv->always_in_place;
2679   GST_OBJECT_UNLOCK (trans);
2680 
2681   return result;
2682 }
2683 
2684 /**
2685  * gst_base_transform_update_qos:
2686  * @trans: a #GstBaseTransform
2687  * @proportion: the proportion
2688  * @diff: the diff against the clock
2689  * @timestamp: the timestamp of the buffer generating the QoS expressed in
2690  * running_time.
2691  *
2692  * Set the QoS parameters in the transform. This function is called internally
2693  * when a QOS event is received but subclasses can provide custom information
2694  * when needed.
2695  *
2696  * MT safe.
2697  */
2698 void
gst_base_transform_update_qos(GstBaseTransform * trans,gdouble proportion,GstClockTimeDiff diff,GstClockTime timestamp)2699 gst_base_transform_update_qos (GstBaseTransform * trans,
2700     gdouble proportion, GstClockTimeDiff diff, GstClockTime timestamp)
2701 {
2702   g_return_if_fail (GST_IS_BASE_TRANSFORM (trans));
2703   g_return_if_fail (GST_CLOCK_TIME_IS_VALID (timestamp));
2704 
2705   GST_CAT_DEBUG_OBJECT (GST_CAT_QOS, trans,
2706       "qos: proportion: %lf, diff %" G_GINT64_FORMAT ", timestamp %"
2707       GST_TIME_FORMAT, proportion, diff, GST_TIME_ARGS (timestamp));
2708 
2709   GST_OBJECT_LOCK (trans);
2710   trans->priv->proportion = proportion;
2711   trans->priv->earliest_time = timestamp + diff;
2712   GST_OBJECT_UNLOCK (trans);
2713 }
2714 
2715 /**
2716  * gst_base_transform_set_qos_enabled:
2717  * @trans: a #GstBaseTransform
2718  * @enabled: new state
2719  *
2720  * Enable or disable QoS handling in the transform.
2721  *
2722  * MT safe.
2723  */
2724 void
gst_base_transform_set_qos_enabled(GstBaseTransform * trans,gboolean enabled)2725 gst_base_transform_set_qos_enabled (GstBaseTransform * trans, gboolean enabled)
2726 {
2727   g_return_if_fail (GST_IS_BASE_TRANSFORM (trans));
2728 
2729   GST_CAT_DEBUG_OBJECT (GST_CAT_QOS, trans, "enabled: %d", enabled);
2730 
2731   GST_OBJECT_LOCK (trans);
2732   trans->priv->qos_enabled = enabled;
2733   GST_OBJECT_UNLOCK (trans);
2734 }
2735 
2736 /**
2737  * gst_base_transform_is_qos_enabled:
2738  * @trans: a #GstBaseTransform
2739  *
2740  * Queries if the transform will handle QoS.
2741  *
2742  * Returns: %TRUE if QoS is enabled.
2743  *
2744  * MT safe.
2745  */
2746 gboolean
gst_base_transform_is_qos_enabled(GstBaseTransform * trans)2747 gst_base_transform_is_qos_enabled (GstBaseTransform * trans)
2748 {
2749   gboolean result;
2750 
2751   g_return_val_if_fail (GST_IS_BASE_TRANSFORM (trans), FALSE);
2752 
2753   GST_OBJECT_LOCK (trans);
2754   result = trans->priv->qos_enabled;
2755   GST_OBJECT_UNLOCK (trans);
2756 
2757   return result;
2758 }
2759 
2760 /**
2761  * gst_base_transform_set_gap_aware:
2762  * @trans: a #GstBaseTransform
2763  * @gap_aware: New state
2764  *
2765  * If @gap_aware is %FALSE (the default), output buffers will have the
2766  * %GST_BUFFER_FLAG_GAP flag unset.
2767  *
2768  * If set to %TRUE, the element must handle output buffers with this flag set
2769  * correctly, i.e. it can assume that the buffer contains neutral data but must
2770  * unset the flag if the output is no neutral data.
2771  *
2772  * MT safe.
2773  */
2774 void
gst_base_transform_set_gap_aware(GstBaseTransform * trans,gboolean gap_aware)2775 gst_base_transform_set_gap_aware (GstBaseTransform * trans, gboolean gap_aware)
2776 {
2777   g_return_if_fail (GST_IS_BASE_TRANSFORM (trans));
2778 
2779   GST_OBJECT_LOCK (trans);
2780   trans->priv->gap_aware = gap_aware;
2781   GST_DEBUG_OBJECT (trans, "set gap aware %d", trans->priv->gap_aware);
2782   GST_OBJECT_UNLOCK (trans);
2783 }
2784 
2785 /**
2786  * gst_base_transform_set_prefer_passthrough:
2787  * @trans: a #GstBaseTransform
2788  * @prefer_passthrough: New state
2789  *
2790  * If @prefer_passthrough is %TRUE (the default), @trans will check and
2791  * prefer passthrough caps from the list of caps returned by the
2792  * transform_caps vmethod.
2793  *
2794  * If set to %FALSE, the element must order the caps returned from the
2795  * transform_caps function in such a way that the preferred format is
2796  * first in the list. This can be interesting for transforms that can do
2797  * passthrough transforms but prefer to do something else, like a
2798  * capsfilter.
2799  *
2800  * MT safe.
2801  *
2802  * Since: 1.0.1
2803  */
2804 void
gst_base_transform_set_prefer_passthrough(GstBaseTransform * trans,gboolean prefer_passthrough)2805 gst_base_transform_set_prefer_passthrough (GstBaseTransform * trans,
2806     gboolean prefer_passthrough)
2807 {
2808   g_return_if_fail (GST_IS_BASE_TRANSFORM (trans));
2809 
2810   GST_OBJECT_LOCK (trans);
2811   trans->priv->prefer_passthrough = prefer_passthrough;
2812   GST_DEBUG_OBJECT (trans, "prefer passthrough %d", prefer_passthrough);
2813   GST_OBJECT_UNLOCK (trans);
2814 }
2815 
2816 /**
2817  * gst_base_transform_reconfigure_sink:
2818  * @trans: a #GstBaseTransform
2819  *
2820  * Instructs @trans to request renegotiation upstream. This function is
2821  * typically called after properties on the transform were set that
2822  * influence the input format.
2823  */
2824 void
gst_base_transform_reconfigure_sink(GstBaseTransform * trans)2825 gst_base_transform_reconfigure_sink (GstBaseTransform * trans)
2826 {
2827   g_return_if_fail (GST_IS_BASE_TRANSFORM (trans));
2828 
2829   /* push the renegotiate event */
2830   if (!gst_pad_push_event (GST_BASE_TRANSFORM_SINK_PAD (trans),
2831           gst_event_new_reconfigure ()))
2832     GST_DEBUG_OBJECT (trans, "Renegotiate event wasn't handled");
2833 }
2834 
2835 /**
2836  * gst_base_transform_reconfigure_src:
2837  * @trans: a #GstBaseTransform
2838  *
2839  * Instructs @trans to renegotiate a new downstream transform on the next
2840  * buffer. This function is typically called after properties on the transform
2841  * were set that influence the output format.
2842  */
2843 void
gst_base_transform_reconfigure_src(GstBaseTransform * trans)2844 gst_base_transform_reconfigure_src (GstBaseTransform * trans)
2845 {
2846   g_return_if_fail (GST_IS_BASE_TRANSFORM (trans));
2847 
2848   gst_pad_mark_reconfigure (trans->srcpad);
2849 }
2850 
2851 /**
2852  * gst_base_transform_get_buffer_pool:
2853  * @trans: a #GstBaseTransform
2854  *
2855  * Returns: (nullable) (transfer full): the instance of the #GstBufferPool used
2856  * by @trans; free it after use
2857  */
2858 GstBufferPool *
gst_base_transform_get_buffer_pool(GstBaseTransform * trans)2859 gst_base_transform_get_buffer_pool (GstBaseTransform * trans)
2860 {
2861   g_return_val_if_fail (GST_IS_BASE_TRANSFORM (trans), NULL);
2862 
2863   if (trans->priv->pool)
2864     return gst_object_ref (trans->priv->pool);
2865 
2866   return NULL;
2867 }
2868 
2869 /**
2870  * gst_base_transform_get_allocator:
2871  * @trans: a #GstBaseTransform
2872  * @allocator: (out) (optional) (nullable) (transfer full): the #GstAllocator
2873  * used
2874  * @params: (out caller-allocates) (optional): the #GstAllocationParams of @allocator
2875  *
2876  * Lets #GstBaseTransform sub-classes know the memory @allocator
2877  * used by the base class and its @params.
2878  *
2879  * Unref the @allocator after use.
2880  */
2881 void
gst_base_transform_get_allocator(GstBaseTransform * trans,GstAllocator ** allocator,GstAllocationParams * params)2882 gst_base_transform_get_allocator (GstBaseTransform * trans,
2883     GstAllocator ** allocator, GstAllocationParams * params)
2884 {
2885   g_return_if_fail (GST_IS_BASE_TRANSFORM (trans));
2886 
2887   if (allocator)
2888     *allocator = trans->priv->allocator ?
2889         gst_object_ref (trans->priv->allocator) : NULL;
2890 
2891   if (params)
2892     *params = trans->priv->params;
2893 }
2894 
2895 /**
2896  * gst_base_transform_update_src_caps:
2897  * @trans: a #GstBaseTransform
2898  * @updated_caps: An updated version of the srcpad caps to be pushed
2899  * downstream
2900  *
2901  * Updates the srcpad caps and sends the caps downstream. This function
2902  * can be used by subclasses when they have already negotiated their caps
2903  * but found a change in them (or computed new information). This way,
2904  * they can notify downstream about that change without losing any
2905  * buffer.
2906  *
2907  * Returns: %TRUE if the caps could be sent downstream %FALSE otherwise
2908  *
2909  * Since: 1.6
2910  */
2911 gboolean
gst_base_transform_update_src_caps(GstBaseTransform * trans,GstCaps * updated_caps)2912 gst_base_transform_update_src_caps (GstBaseTransform * trans,
2913     GstCaps * updated_caps)
2914 {
2915   g_return_val_if_fail (GST_IS_BASE_TRANSFORM (trans), FALSE);
2916 
2917   if (gst_pad_push_event (GST_BASE_TRANSFORM_SRC_PAD (trans),
2918           gst_event_new_caps (updated_caps))) {
2919     gst_pad_mark_reconfigure (trans->srcpad);
2920 
2921     return TRUE;
2922   }
2923 
2924   return FALSE;
2925 }
2926