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