• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* GStreamer
2  * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
3  *                    2000 Wim Taymans <wtay@chello.be>
4  *                    2005 Wim Taymans <wim@fluendo.com>
5  *                    2007 Andy Wingo <wingo at pobox.com>
6  *                    2008 Sebastian Dröge <slomo@circular-chaos.rg>
7  *
8  * interleave.c: interleave samples, mostly based on adder.
9  *
10  * This library is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU Library General Public
12  * License as published by the Free Software Foundation; either
13  * version 2 of the License, or (at your option) any later version.
14  *
15  * This library is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18  * Library General Public License for more details.
19  *
20  * You should have received a copy of the GNU Library General Public
21  * License along with this library; if not, write to the
22  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
23  * Boston, MA 02110-1301, USA.
24  */
25 
26 /* TODO:
27  *       - handle caps changes
28  *       - handle more queries/events
29  */
30 
31 /**
32  * SECTION:element-interleave
33  * @title: interleave
34  * @see_also: deinterleave
35  *
36  * Merges separate mono inputs into one interleaved stream.
37  *
38  * This element handles all raw floating point sample formats and all signed integer sample formats. The first
39  * caps on one of the sinkpads will set the caps of the output so usually an audioconvert element should be
40  * placed before every sinkpad of interleave.
41  *
42  * It's possible to change the number of channels while the pipeline is running by adding or removing
43  * some of the request pads but this will change the caps of the output buffers. Changing the input
44  * caps is _not_ supported yet.
45  *
46  * The channel number of every sinkpad in the out can be retrieved from the "channel" property of the pad.
47  *
48  * ## Example launch line
49  * |[
50  * gst-launch-1.0 filesrc location=file.mp3 ! decodebin ! audioconvert ! "audio/x-raw,channels=2" ! deinterleave name=d  interleave name=i ! audioconvert ! wavenc ! filesink location=test.wav    d.src_0 ! queue ! audioconvert ! i.sink_1    d.src_1 ! queue ! audioconvert ! i.sink_0
51  * ]| Decodes and deinterleaves a Stereo MP3 file into separate channels and
52  * then interleaves the channels again to a WAV file with the channels
53  * exchanged.
54  * |[
55  * gst-launch-1.0 interleave name=i ! audioconvert ! wavenc ! filesink location=file.wav  filesrc location=file1.wav ! decodebin ! audioconvert ! "audio/x-raw,channels=1,channel-mask=(bitmask)0x1" ! queue ! i.sink_0   filesrc location=file2.wav ! decodebin ! audioconvert ! "audio/x-raw,channels=1,channel-mask=(bitmask)0x2" ! queue ! i.sink_1
56  * ]| Interleaves two Mono WAV files to a single Stereo WAV file. Having
57  * channel-masks defined in the sink pads ensures a sane mapping of the mono
58  * streams into the stereo stream. NOTE: the proper way to map channels in
59  * code is by using the channel-positions property of the interleave element.
60  *
61  */
62 
63 /* FIXME 0.11: suppress warnings for deprecated API such as GValueArray
64  * with newer GLib versions (>= 2.31.0) */
65 #define GLIB_DISABLE_DEPRECATION_WARNINGS
66 
67 #ifdef HAVE_CONFIG_H
68 #  include "config.h"
69 #endif
70 
71 #include <gst/gst.h>
72 #include <string.h>
73 #include "gstinterleaveelements.h"
74 #include "interleave.h"
75 
76 #include <gst/audio/audio.h>
77 #include <gst/audio/audio-enumtypes.h>
78 
79 GST_DEBUG_CATEGORY_STATIC (gst_interleave_debug);
80 #define GST_CAT_DEFAULT gst_interleave_debug
81 
82 static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink_%u",
83     GST_PAD_SINK,
84     GST_PAD_REQUEST,
85     GST_STATIC_CAPS ("audio/x-raw, "
86         "rate = (int) [ 1, MAX ], "
87         "channels = (int) 1, "
88         "format = (string) " GST_AUDIO_FORMATS_ALL ", "
89         "layout = (string) {non-interleaved, interleaved}")
90     );
91 
92 static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src",
93     GST_PAD_SRC,
94     GST_PAD_ALWAYS,
95     GST_STATIC_CAPS ("audio/x-raw, "
96         "rate = (int) [ 1, MAX ], "
97         "channels = (int) [ 1, MAX ], "
98         "format = (string) " GST_AUDIO_FORMATS_ALL ", "
99         "layout = (string) interleaved")
100     );
101 
102 #define MAKE_FUNC(type) \
103 static void interleave_##type (guint##type *out, guint##type *in, \
104     guint stride, guint nframes) \
105 { \
106   gint i; \
107   \
108   for (i = 0; i < nframes; i++) { \
109     *out = in[i]; \
110     out += stride; \
111   } \
112 }
113 
114 MAKE_FUNC (8);
115 MAKE_FUNC (16);
116 MAKE_FUNC (32);
117 MAKE_FUNC (64);
118 
119 static void
interleave_24(guint8 * out,guint8 * in,guint stride,guint nframes)120 interleave_24 (guint8 * out, guint8 * in, guint stride, guint nframes)
121 {
122   gint i;
123 
124   for (i = 0; i < nframes; i++) {
125     memcpy (out, in, 3);
126     out += stride * 3;
127     in += 3;
128   }
129 }
130 
131 typedef struct
132 {
133   GstPad parent;
134   guint channel;
135 } GstInterleavePad;
136 
137 enum
138 {
139   PROP_PAD_0,
140   PROP_PAD_CHANNEL
141 };
142 
143 static void gst_interleave_pad_class_init (GstPadClass * klass);
144 
145 #define GST_TYPE_INTERLEAVE_PAD (gst_interleave_pad_get_type())
146 #define GST_INTERLEAVE_PAD(pad) (G_TYPE_CHECK_INSTANCE_CAST((pad),GST_TYPE_INTERLEAVE_PAD,GstInterleavePad))
147 #define GST_INTERLEAVE_PAD_CAST(pad) ((GstInterleavePad *) pad)
148 #define GST_IS_INTERLEAVE_PAD(pad) (G_TYPE_CHECK_INSTANCE_TYPE((pad),GST_TYPE_INTERLEAVE_PAD))
149 static GType
gst_interleave_pad_get_type(void)150 gst_interleave_pad_get_type (void)
151 {
152   static GType type = 0;
153 
154   if (G_UNLIKELY (type == 0)) {
155     type = g_type_register_static_simple (GST_TYPE_PAD,
156         g_intern_static_string ("GstInterleavePad"), sizeof (GstPadClass),
157         (GClassInitFunc) gst_interleave_pad_class_init,
158         sizeof (GstInterleavePad), NULL, 0);
159   }
160   return type;
161 }
162 
163 static void
gst_interleave_pad_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)164 gst_interleave_pad_get_property (GObject * object,
165     guint prop_id, GValue * value, GParamSpec * pspec)
166 {
167   GstInterleavePad *self = GST_INTERLEAVE_PAD (object);
168 
169   switch (prop_id) {
170     case PROP_PAD_CHANNEL:
171       g_value_set_uint (value, self->channel);
172       break;
173     default:
174       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
175       break;
176   }
177 }
178 
179 static void
gst_interleave_pad_class_init(GstPadClass * klass)180 gst_interleave_pad_class_init (GstPadClass * klass)
181 {
182   GObjectClass *gobject_class = (GObjectClass *) klass;
183 
184   gobject_class->get_property = gst_interleave_pad_get_property;
185 
186   g_object_class_install_property (gobject_class,
187       PROP_PAD_CHANNEL,
188       g_param_spec_uint ("channel",
189           "Channel number",
190           "Number of the channel of this pad in the output", 0, G_MAXUINT, 0,
191           G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
192 }
193 
194 #define gst_interleave_parent_class parent_class
195 G_DEFINE_TYPE (GstInterleave, gst_interleave, GST_TYPE_ELEMENT);
196 GST_ELEMENT_REGISTER_DEFINE (interleave, "interleave",
197     GST_RANK_NONE, gst_interleave_get_type ());
198 
199 enum
200 {
201   PROP_0,
202   PROP_CHANNEL_POSITIONS,
203   PROP_CHANNEL_POSITIONS_FROM_INPUT
204 };
205 
206 static void gst_interleave_set_property (GObject * object,
207     guint prop_id, const GValue * value, GParamSpec * pspec);
208 static void gst_interleave_get_property (GObject * object,
209     guint prop_id, GValue * value, GParamSpec * pspec);
210 
211 static GstPad *gst_interleave_request_new_pad (GstElement * element,
212     GstPadTemplate * templ, const gchar * name, const GstCaps * caps);
213 static void gst_interleave_release_pad (GstElement * element, GstPad * pad);
214 
215 static GstStateChangeReturn gst_interleave_change_state (GstElement * element,
216     GstStateChange transition);
217 
218 static gboolean gst_interleave_src_query (GstPad * pad, GstObject * parent,
219     GstQuery * query);
220 
221 static gboolean gst_interleave_src_event (GstPad * pad, GstObject * parent,
222     GstEvent * event);
223 
224 static gboolean gst_interleave_sink_event (GstCollectPads * pads,
225     GstCollectData * data, GstEvent * event, gpointer user_data);
226 static gboolean gst_interleave_sink_query (GstCollectPads * pads,
227     GstCollectData * data, GstQuery * query, gpointer user_data);
228 
229 static gboolean gst_interleave_sink_setcaps (GstInterleave * self,
230     GstPad * pad, const GstCaps * caps, const GstAudioInfo * info);
231 
232 static GstCaps *gst_interleave_sink_getcaps (GstPad * pad, GstInterleave * self,
233     GstCaps * filter);
234 
235 static GstFlowReturn gst_interleave_collected (GstCollectPads * pads,
236     GstInterleave * self);
237 
238 static void
gst_interleave_finalize(GObject * object)239 gst_interleave_finalize (GObject * object)
240 {
241   GstInterleave *self = GST_INTERLEAVE (object);
242 
243   if (self->collect) {
244     gst_object_unref (self->collect);
245     self->collect = NULL;
246   }
247 
248   if (self->channel_positions
249       && self->channel_positions != self->input_channel_positions) {
250     g_value_array_free (self->channel_positions);
251     self->channel_positions = NULL;
252   }
253 
254   if (self->input_channel_positions) {
255     g_value_array_free (self->input_channel_positions);
256     self->input_channel_positions = NULL;
257   }
258 
259   gst_caps_replace (&self->sinkcaps, NULL);
260 
261   G_OBJECT_CLASS (parent_class)->finalize (object);
262 }
263 
264 static gint
compare_positions(gconstpointer a,gconstpointer b,gpointer user_data)265 compare_positions (gconstpointer a, gconstpointer b, gpointer user_data)
266 {
267   const gint i = *(const gint *) a;
268   const gint j = *(const gint *) b;
269   const gint *pos = (const gint *) user_data;
270 
271   if (pos[i] < pos[j])
272     return -1;
273   else if (pos[i] > pos[j])
274     return 1;
275   else
276     return 0;
277 }
278 
279 static gboolean
gst_interleave_channel_positions_to_mask(GValueArray * positions,gint default_ordering_map[64],guint64 * mask)280 gst_interleave_channel_positions_to_mask (GValueArray * positions,
281     gint default_ordering_map[64], guint64 * mask)
282 {
283   gint i;
284   guint channels;
285   GstAudioChannelPosition *pos;
286   gboolean ret;
287 
288   channels = positions->n_values;
289   pos = g_new (GstAudioChannelPosition, channels);
290 
291   for (i = 0; i < channels; i++) {
292     GValue *val;
293 
294     val = g_value_array_get_nth (positions, i);
295     pos[i] = g_value_get_enum (val);
296   }
297 
298   /* sort the default ordering map according to the position order */
299   for (i = 0; i < channels; i++) {
300     default_ordering_map[i] = i;
301   }
302   g_qsort_with_data (default_ordering_map, channels,
303       sizeof (*default_ordering_map), compare_positions, pos);
304 
305   ret = gst_audio_channel_positions_to_mask (pos, channels, FALSE, mask);
306   g_free (pos);
307 
308   return ret;
309 }
310 
311 static void
gst_interleave_set_channel_positions(GstInterleave * self,GstStructure * s)312 gst_interleave_set_channel_positions (GstInterleave * self, GstStructure * s)
313 {
314   if (self->channels <= 64 &&
315       self->channel_positions != NULL &&
316       self->channels == self->channel_positions->n_values) {
317     if (!gst_interleave_channel_positions_to_mask (self->channel_positions,
318             self->default_channels_ordering_map, &self->channel_mask)) {
319       GST_WARNING_OBJECT (self, "Invalid channel positions, using NONE");
320       self->channel_mask = 0;
321     }
322   } else {
323     self->channel_mask = 0;
324     if (self->channels <= 64) {
325       GST_WARNING_OBJECT (self, "Using NONE channel positions");
326     }
327   }
328   gst_structure_set (s, "channel-mask", GST_TYPE_BITMASK, self->channel_mask,
329       NULL);
330 }
331 
332 static void
gst_interleave_send_stream_start(GstInterleave * self)333 gst_interleave_send_stream_start (GstInterleave * self)
334 {
335   GST_OBJECT_LOCK (self);
336   if (self->send_stream_start) {
337     gchar s_id[32];
338 
339     self->send_stream_start = FALSE;
340     GST_OBJECT_UNLOCK (self);
341 
342     /* stream-start (FIXME: create id based on input ids) */
343     g_snprintf (s_id, sizeof (s_id), "interleave-%08x", g_random_int ());
344     gst_pad_push_event (self->src, gst_event_new_stream_start (s_id));
345   } else {
346     GST_OBJECT_UNLOCK (self);
347   }
348 }
349 
350 static void
gst_interleave_class_init(GstInterleaveClass * klass)351 gst_interleave_class_init (GstInterleaveClass * klass)
352 {
353   GstElementClass *gstelement_class;
354   GObjectClass *gobject_class;
355 
356   gobject_class = G_OBJECT_CLASS (klass);
357   gstelement_class = GST_ELEMENT_CLASS (klass);
358 
359   GST_DEBUG_CATEGORY_INIT (gst_interleave_debug, "interleave", 0,
360       "interleave element");
361 
362   gst_element_class_set_static_metadata (gstelement_class, "Audio interleaver",
363       "Filter/Converter/Audio",
364       "Folds many mono channels into one interleaved audio stream",
365       "Andy Wingo <wingo at pobox.com>, "
366       "Sebastian Dröge <slomo@circular-chaos.org>");
367 
368   gst_element_class_add_static_pad_template (gstelement_class, &sink_template);
369   gst_element_class_add_static_pad_template (gstelement_class, &src_template);
370 
371   /* Reference GstInterleavePad class to have the type registered from
372    * a threadsafe context
373    */
374   g_type_class_ref (GST_TYPE_INTERLEAVE_PAD);
375 
376   gobject_class->finalize = gst_interleave_finalize;
377   gobject_class->set_property = gst_interleave_set_property;
378   gobject_class->get_property = gst_interleave_get_property;
379 
380   /**
381    * GstInterleave:channel-positions
382    *
383    * Channel positions: This property controls the channel positions
384    * that are used on the src caps. The number of elements should be
385    * the same as the number of sink pads and the array should contain
386    * a valid list of channel positions. The n-th element of the array
387    * is the position of the n-th sink pad.
388    *
389    * These channel positions will only be used if they're valid and the
390    * number of elements is the same as the number of channels. If this
391    * is not given a NONE layout will be used.
392    *
393    */
394   g_object_class_install_property (gobject_class, PROP_CHANNEL_POSITIONS,
395       g_param_spec_value_array ("channel-positions", "Channel positions",
396           "Channel positions used on the output",
397           g_param_spec_enum ("channel-position", "Channel position",
398               "Channel position of the n-th input",
399               GST_TYPE_AUDIO_CHANNEL_POSITION,
400               GST_AUDIO_CHANNEL_POSITION_NONE,
401               G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS),
402           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
403 
404   /**
405    * GstInterleave:channel-positions-from-input
406    *
407    * Channel positions from input: If this property is set to %TRUE the channel
408    * positions will be taken from the input caps if valid channel positions for
409    * the output can be constructed from them. If this is set to %TRUE setting the
410    * channel-positions property overwrites this property again.
411    *
412    */
413   g_object_class_install_property (gobject_class,
414       PROP_CHANNEL_POSITIONS_FROM_INPUT,
415       g_param_spec_boolean ("channel-positions-from-input",
416           "Channel positions from input",
417           "Take channel positions from the input", TRUE,
418           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
419 
420   gstelement_class->request_new_pad =
421       GST_DEBUG_FUNCPTR (gst_interleave_request_new_pad);
422   gstelement_class->release_pad =
423       GST_DEBUG_FUNCPTR (gst_interleave_release_pad);
424   gstelement_class->change_state =
425       GST_DEBUG_FUNCPTR (gst_interleave_change_state);
426 }
427 
428 static void
gst_interleave_init(GstInterleave * self)429 gst_interleave_init (GstInterleave * self)
430 {
431   self->src = gst_pad_new_from_static_template (&src_template, "src");
432 
433   gst_pad_set_query_function (self->src,
434       GST_DEBUG_FUNCPTR (gst_interleave_src_query));
435   gst_pad_set_event_function (self->src,
436       GST_DEBUG_FUNCPTR (gst_interleave_src_event));
437 
438   gst_element_add_pad (GST_ELEMENT (self), self->src);
439 
440   self->collect = gst_collect_pads_new ();
441   gst_collect_pads_set_function (self->collect,
442       (GstCollectPadsFunction) gst_interleave_collected, self);
443 
444   self->input_channel_positions = g_value_array_new (0);
445   self->channel_positions_from_input = TRUE;
446   self->channel_positions = self->input_channel_positions;
447 }
448 
449 static void
gst_interleave_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)450 gst_interleave_set_property (GObject * object, guint prop_id,
451     const GValue * value, GParamSpec * pspec)
452 {
453   GstInterleave *self = GST_INTERLEAVE (object);
454 
455   switch (prop_id) {
456     case PROP_CHANNEL_POSITIONS:
457       if (self->channel_positions &&
458           self->channel_positions != self->input_channel_positions)
459         g_value_array_free (self->channel_positions);
460 
461       self->channel_positions = g_value_dup_boxed (value);
462       self->channel_positions_from_input = FALSE;
463       break;
464     case PROP_CHANNEL_POSITIONS_FROM_INPUT:
465       self->channel_positions_from_input = g_value_get_boolean (value);
466 
467       if (self->channel_positions_from_input) {
468         if (self->channel_positions &&
469             self->channel_positions != self->input_channel_positions)
470           g_value_array_free (self->channel_positions);
471         self->channel_positions = self->input_channel_positions;
472       }
473       break;
474     default:
475       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
476       break;
477   }
478 }
479 
480 static void
gst_interleave_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)481 gst_interleave_get_property (GObject * object, guint prop_id,
482     GValue * value, GParamSpec * pspec)
483 {
484   GstInterleave *self = GST_INTERLEAVE (object);
485 
486   switch (prop_id) {
487     case PROP_CHANNEL_POSITIONS:
488       g_value_set_boxed (value, self->channel_positions);
489       break;
490     case PROP_CHANNEL_POSITIONS_FROM_INPUT:
491       g_value_set_boolean (value, self->channel_positions_from_input);
492       break;
493     default:
494       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
495       break;
496   }
497 }
498 
499 static GstPad *
gst_interleave_request_new_pad(GstElement * element,GstPadTemplate * templ,const gchar * req_name,const GstCaps * caps)500 gst_interleave_request_new_pad (GstElement * element, GstPadTemplate * templ,
501     const gchar * req_name, const GstCaps * caps)
502 {
503   GstInterleave *self = GST_INTERLEAVE (element);
504   GstPad *new_pad;
505   gchar *pad_name;
506   gint channel, padnumber;
507   GValue val = { 0, };
508 
509   if (templ->direction != GST_PAD_SINK)
510     goto not_sink_pad;
511 
512   padnumber = g_atomic_int_add (&self->padcounter, 1);
513 
514   channel = g_atomic_int_add (&self->channels, 1);
515   if (!self->channel_positions_from_input)
516     channel = padnumber;
517 
518   pad_name = g_strdup_printf ("sink_%u", padnumber);
519   new_pad = GST_PAD_CAST (g_object_new (GST_TYPE_INTERLEAVE_PAD,
520           "name", pad_name, "direction", templ->direction,
521           "template", templ, NULL));
522   GST_INTERLEAVE_PAD_CAST (new_pad)->channel = channel;
523   GST_DEBUG_OBJECT (self, "requested new pad %s", pad_name);
524   g_free (pad_name);
525 
526   gst_pad_use_fixed_caps (new_pad);
527 
528   gst_collect_pads_add_pad (self->collect, new_pad, sizeof (GstCollectData),
529       NULL, TRUE);
530 
531   gst_collect_pads_set_event_function (self->collect,
532       (GstCollectPadsEventFunction)
533       GST_DEBUG_FUNCPTR (gst_interleave_sink_event), self);
534 
535   gst_collect_pads_set_query_function (self->collect,
536       (GstCollectPadsQueryFunction)
537       GST_DEBUG_FUNCPTR (gst_interleave_sink_query), self);
538 
539   if (!gst_element_add_pad (element, new_pad))
540     goto could_not_add;
541 
542   g_value_init (&val, GST_TYPE_AUDIO_CHANNEL_POSITION);
543   g_value_set_enum (&val, GST_AUDIO_CHANNEL_POSITION_NONE);
544   self->input_channel_positions =
545       g_value_array_append (self->input_channel_positions, &val);
546   g_value_unset (&val);
547 
548   /* Update the src caps if we already have them */
549   if (self->sinkcaps) {
550     GstCaps *srccaps;
551     GstStructure *s;
552 
553     /* Take lock to make sure processing finishes first */
554     GST_OBJECT_LOCK (self->collect);
555 
556     srccaps = gst_caps_copy (self->sinkcaps);
557     s = gst_caps_get_structure (srccaps, 0);
558 
559     gst_structure_set (s, "channels", G_TYPE_INT, self->channels, NULL);
560     gst_interleave_set_channel_positions (self, s);
561 
562     gst_interleave_send_stream_start (self);
563     gst_pad_set_caps (self->src, srccaps);
564     gst_caps_unref (srccaps);
565 
566     GST_OBJECT_UNLOCK (self->collect);
567   }
568 
569   return new_pad;
570 
571   /* errors */
572 not_sink_pad:
573   {
574     g_warning ("interleave: requested new pad that is not a SINK pad\n");
575     return NULL;
576   }
577 could_not_add:
578   {
579     GST_DEBUG_OBJECT (self, "could not add pad %s", GST_PAD_NAME (new_pad));
580     gst_collect_pads_remove_pad (self->collect, new_pad);
581     gst_object_unref (new_pad);
582     return NULL;
583   }
584 }
585 
586 static void
gst_interleave_release_pad(GstElement * element,GstPad * pad)587 gst_interleave_release_pad (GstElement * element, GstPad * pad)
588 {
589   GstInterleave *self = GST_INTERLEAVE (element);
590   GList *l;
591   GstAudioChannelPosition position;
592 
593   g_return_if_fail (GST_IS_INTERLEAVE_PAD (pad));
594 
595   /* Take lock to make sure we're not changing this when processing buffers */
596   GST_OBJECT_LOCK (self->collect);
597 
598   g_atomic_int_add (&self->channels, -1);
599 
600   if (gst_pad_has_current_caps (pad))
601     g_atomic_int_add (&self->configured_sinkpads_counter, -1);
602 
603   position = GST_INTERLEAVE_PAD_CAST (pad)->channel;
604   g_value_array_remove (self->input_channel_positions, position);
605 
606   /* Update channel numbers */
607   GST_OBJECT_LOCK (self);
608   for (l = GST_ELEMENT_CAST (self)->sinkpads; l != NULL; l = l->next) {
609     GstInterleavePad *ipad = GST_INTERLEAVE_PAD (l->data);
610 
611     if (GST_INTERLEAVE_PAD_CAST (pad)->channel < ipad->channel)
612       ipad->channel--;
613   }
614   GST_OBJECT_UNLOCK (self);
615 
616   /* Update the src caps if we already have them */
617   if (self->sinkcaps) {
618     if (self->channels > 0) {
619       GstCaps *srccaps;
620       GstStructure *s;
621 
622       srccaps = gst_caps_copy (self->sinkcaps);
623       s = gst_caps_get_structure (srccaps, 0);
624 
625       gst_structure_set (s, "channels", G_TYPE_INT, self->channels, NULL);
626       gst_interleave_set_channel_positions (self, s);
627 
628       gst_interleave_send_stream_start (self);
629       gst_pad_set_caps (self->src, srccaps);
630       gst_caps_unref (srccaps);
631     } else {
632       gst_caps_replace (&self->sinkcaps, NULL);
633     }
634   }
635 
636   GST_OBJECT_UNLOCK (self->collect);
637 
638   gst_collect_pads_remove_pad (self->collect, pad);
639   gst_element_remove_pad (element, pad);
640 }
641 
642 static GstStateChangeReturn
gst_interleave_change_state(GstElement * element,GstStateChange transition)643 gst_interleave_change_state (GstElement * element, GstStateChange transition)
644 {
645   GstInterleave *self;
646   GstStateChangeReturn ret;
647 
648   self = GST_INTERLEAVE (element);
649 
650   switch (transition) {
651     case GST_STATE_CHANGE_NULL_TO_READY:
652       break;
653     case GST_STATE_CHANGE_READY_TO_PAUSED:
654       self->timestamp = 0;
655       self->offset = 0;
656       gst_event_replace (&self->pending_segment, NULL);
657       self->send_stream_start = TRUE;
658       gst_collect_pads_start (self->collect);
659       break;
660     case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
661       break;
662     default:
663       break;
664   }
665 
666   /* Stop before calling the parent's state change function as
667    * GstCollectPads might take locks and we would deadlock in that
668    * case
669    */
670   if (transition == GST_STATE_CHANGE_PAUSED_TO_READY)
671     gst_collect_pads_stop (self->collect);
672 
673   ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
674 
675   switch (transition) {
676     case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
677       break;
678     case GST_STATE_CHANGE_PAUSED_TO_READY:
679       gst_caps_replace (&self->sinkcaps, NULL);
680       gst_event_replace (&self->pending_segment, NULL);
681       break;
682     case GST_STATE_CHANGE_READY_TO_NULL:
683       break;
684     default:
685       break;
686   }
687 
688   return ret;
689 }
690 
691 static void
__remove_channels(GstCaps * caps)692 __remove_channels (GstCaps * caps)
693 {
694   GstStructure *s;
695   gint i, size;
696 
697   size = gst_caps_get_size (caps);
698   for (i = 0; i < size; i++) {
699     s = gst_caps_get_structure (caps, i);
700     gst_structure_remove_field (s, "channel-mask");
701     gst_structure_remove_field (s, "channels");
702   }
703 }
704 
705 static void
__set_channels(GstCaps * caps,gint channels)706 __set_channels (GstCaps * caps, gint channels)
707 {
708   GstStructure *s;
709   gint i, size;
710 
711   size = gst_caps_get_size (caps);
712   for (i = 0; i < size; i++) {
713     s = gst_caps_get_structure (caps, i);
714     if (channels > 0)
715       gst_structure_set (s, "channels", G_TYPE_INT, channels, NULL);
716     else
717       gst_structure_set (s, "channels", GST_TYPE_INT_RANGE, 1, G_MAXINT, NULL);
718   }
719 }
720 
721 /* we can only accept caps that we and downstream can handle. */
722 static GstCaps *
gst_interleave_sink_getcaps(GstPad * pad,GstInterleave * self,GstCaps * filter)723 gst_interleave_sink_getcaps (GstPad * pad, GstInterleave * self,
724     GstCaps * filter)
725 {
726   GstCaps *result, *peercaps, *sinkcaps;
727 
728   GST_OBJECT_LOCK (self);
729 
730   /* If we already have caps on one of the sink pads return them */
731   if (self->sinkcaps) {
732     result = gst_caps_copy (self->sinkcaps);
733   } else {
734     /* get the downstream possible caps */
735     peercaps = gst_pad_peer_query_caps (self->src, NULL);
736 
737     /* get the allowed caps on this sinkpad */
738     sinkcaps = gst_caps_copy (gst_pad_get_pad_template_caps (pad));
739     __remove_channels (sinkcaps);
740     if (peercaps) {
741       peercaps = gst_caps_make_writable (peercaps);
742       __remove_channels (peercaps);
743       /* if the peer has caps, intersect */
744       GST_DEBUG_OBJECT (pad, "intersecting peer and template caps");
745       result = gst_caps_intersect (peercaps, sinkcaps);
746       gst_caps_unref (peercaps);
747       gst_caps_unref (sinkcaps);
748     } else {
749       /* the peer has no caps (or there is no peer), just use the allowed caps
750        * of this sinkpad. */
751       GST_DEBUG_OBJECT (pad, "no peer caps, using sinkcaps");
752       result = sinkcaps;
753     }
754     __set_channels (result, 1);
755   }
756 
757   GST_OBJECT_UNLOCK (self);
758 
759   if (filter != NULL) {
760     GstCaps *caps = result;
761 
762     GST_LOG_OBJECT (pad, "intersecting filter caps %" GST_PTR_FORMAT " with "
763         "preliminary result %" GST_PTR_FORMAT, filter, caps);
764 
765     result = gst_caps_intersect_full (filter, caps, GST_CAPS_INTERSECT_FIRST);
766     gst_caps_unref (caps);
767   }
768 
769   GST_DEBUG_OBJECT (pad, "Returning caps %" GST_PTR_FORMAT, result);
770 
771   return result;
772 }
773 
774 static void
gst_interleave_set_process_function(GstInterleave * self)775 gst_interleave_set_process_function (GstInterleave * self)
776 {
777   switch (self->width) {
778     case 8:
779       self->func = (GstInterleaveFunc) interleave_8;
780       break;
781     case 16:
782       self->func = (GstInterleaveFunc) interleave_16;
783       break;
784     case 24:
785       self->func = (GstInterleaveFunc) interleave_24;
786       break;
787     case 32:
788       self->func = (GstInterleaveFunc) interleave_32;
789       break;
790     case 64:
791       self->func = (GstInterleaveFunc) interleave_64;
792       break;
793     default:
794       g_assert_not_reached ();
795       break;
796   }
797 }
798 
799 static gboolean
gst_interleave_sink_setcaps(GstInterleave * self,GstPad * pad,const GstCaps * caps,const GstAudioInfo * info)800 gst_interleave_sink_setcaps (GstInterleave * self, GstPad * pad,
801     const GstCaps * caps, const GstAudioInfo * info)
802 {
803   g_return_val_if_fail (GST_IS_INTERLEAVE_PAD (pad), FALSE);
804 
805   /* TODO: handle caps changes */
806   if (self->sinkcaps && !gst_caps_is_subset (caps, self->sinkcaps)) {
807     goto cannot_change_caps;
808   } else {
809     GstCaps *srccaps;
810     GstStructure *s;
811     gboolean res;
812 
813     self->width = GST_AUDIO_INFO_WIDTH (info);
814     self->rate = GST_AUDIO_INFO_RATE (info);
815 
816     gst_interleave_set_process_function (self);
817 
818     srccaps = gst_caps_copy (caps);
819     s = gst_caps_get_structure (srccaps, 0);
820 
821     gst_structure_remove_field (s, "channel-mask");
822 
823     gst_structure_set (s, "channels", G_TYPE_INT, self->channels, "layout",
824         G_TYPE_STRING, "interleaved", NULL);
825     gst_interleave_set_channel_positions (self, s);
826 
827     gst_interleave_send_stream_start (self);
828     res = gst_pad_set_caps (self->src, srccaps);
829     gst_caps_unref (srccaps);
830 
831     if (!res)
832       goto src_did_not_accept;
833   }
834 
835   if (!self->sinkcaps) {
836     GstCaps *sinkcaps = gst_caps_copy (caps);
837     GstStructure *s = gst_caps_get_structure (sinkcaps, 0);
838 
839     gst_structure_remove_field (s, "channel-mask");
840 
841     GST_DEBUG_OBJECT (self, "setting sinkcaps %" GST_PTR_FORMAT, sinkcaps);
842 
843     gst_caps_replace (&self->sinkcaps, sinkcaps);
844 
845     gst_caps_unref (sinkcaps);
846   }
847 
848   return TRUE;
849 
850 cannot_change_caps:
851   {
852     GST_WARNING_OBJECT (self, "caps of %" GST_PTR_FORMAT " already set, can't "
853         "change", self->sinkcaps);
854     return FALSE;
855   }
856 src_did_not_accept:
857   {
858     GST_WARNING_OBJECT (self, "src did not accept setcaps()");
859     return FALSE;
860   }
861 }
862 
863 static gboolean
gst_interleave_sink_event(GstCollectPads * pads,GstCollectData * data,GstEvent * event,gpointer user_data)864 gst_interleave_sink_event (GstCollectPads * pads, GstCollectData * data,
865     GstEvent * event, gpointer user_data)
866 {
867   GstInterleave *self = GST_INTERLEAVE (user_data);
868   gboolean ret = TRUE;
869 
870   GST_DEBUG ("Got %s event on pad %s:%s", GST_EVENT_TYPE_NAME (event),
871       GST_DEBUG_PAD_NAME (data->pad));
872 
873   switch (GST_EVENT_TYPE (event)) {
874     case GST_EVENT_FLUSH_STOP:
875       GST_OBJECT_LOCK (self);
876       gst_event_replace (&self->pending_segment, NULL);
877       GST_OBJECT_UNLOCK (self);
878       break;
879     case GST_EVENT_SEGMENT:
880     {
881       GST_OBJECT_LOCK (self);
882       gst_event_replace (&self->pending_segment, event);
883       GST_OBJECT_UNLOCK (self);
884       break;
885     }
886     case GST_EVENT_CAPS:
887     {
888       GstCaps *caps;
889       GstAudioInfo info;
890       GValue *val;
891       guint channel;
892 
893       gst_event_parse_caps (event, &caps);
894 
895       if (!gst_audio_info_from_caps (&info, caps)) {
896         GST_WARNING_OBJECT (self, "invalid sink caps");
897         gst_event_unref (event);
898         event = NULL;
899         ret = FALSE;
900         break;
901       }
902 
903       if (self->channel_positions_from_input
904           && GST_AUDIO_INFO_CHANNELS (&info) == 1) {
905         channel = GST_INTERLEAVE_PAD_CAST (data->pad)->channel;
906         val = g_value_array_get_nth (self->input_channel_positions, channel);
907         g_value_set_enum (val, GST_AUDIO_INFO_POSITION (&info, 0));
908       }
909 
910       if (!gst_pad_has_current_caps (data->pad))
911         g_atomic_int_add (&self->configured_sinkpads_counter, 1);
912 
913       /* Last caps that are set on a sink pad are used as output caps */
914       if (g_atomic_int_get (&self->configured_sinkpads_counter) ==
915           self->channels) {
916         ret = gst_interleave_sink_setcaps (self, data->pad, caps, &info);
917         gst_event_unref (event);
918         event = NULL;
919       }
920       break;
921     }
922     case GST_EVENT_TAG:
923       GST_FIXME_OBJECT (self, "FIXME: merge tags and send after stream-start");
924       break;
925     default:
926       break;
927   }
928 
929   /* now GstCollectPads can take care of the rest, e.g. EOS */
930   if (event != NULL)
931     return gst_collect_pads_event_default (pads, data, event, FALSE);
932 
933   return ret;
934 }
935 
936 static gboolean
gst_interleave_sink_query(GstCollectPads * pads,GstCollectData * data,GstQuery * query,gpointer user_data)937 gst_interleave_sink_query (GstCollectPads * pads,
938     GstCollectData * data, GstQuery * query, gpointer user_data)
939 {
940   GstInterleave *self = GST_INTERLEAVE (user_data);
941   gboolean ret = TRUE;
942 
943   GST_DEBUG ("Got %s query on pad %s:%s", GST_QUERY_TYPE_NAME (query),
944       GST_DEBUG_PAD_NAME (data->pad));
945 
946   switch (GST_QUERY_TYPE (query)) {
947     case GST_QUERY_CAPS:
948     {
949       GstCaps *filter, *caps;
950 
951       gst_query_parse_caps (query, &filter);
952       caps = gst_interleave_sink_getcaps (data->pad, self, filter);
953       gst_query_set_caps_result (query, caps);
954       gst_caps_unref (caps);
955       ret = TRUE;
956       break;
957     }
958     default:
959       ret = gst_collect_pads_query_default (pads, data, query, FALSE);
960       break;
961   }
962 
963   return ret;
964 }
965 
966 static gboolean
gst_interleave_src_query_duration(GstInterleave * self,GstQuery * query)967 gst_interleave_src_query_duration (GstInterleave * self, GstQuery * query)
968 {
969   gint64 max;
970   gboolean res;
971   GstFormat format;
972   GstIterator *it;
973   gboolean done;
974 
975   /* parse format */
976   gst_query_parse_duration (query, &format, NULL);
977 
978   max = -1;
979   res = TRUE;
980   done = FALSE;
981 
982   /* Take maximum of all durations */
983   it = gst_element_iterate_sink_pads (GST_ELEMENT_CAST (self));
984   while (!done) {
985     GstIteratorResult ires;
986 
987     GValue item = { 0, };
988 
989     ires = gst_iterator_next (it, &item);
990     switch (ires) {
991       case GST_ITERATOR_DONE:
992         done = TRUE;
993         break;
994       case GST_ITERATOR_OK:
995       {
996         GstPad *pad = GST_PAD_CAST (g_value_dup_object (&item));
997 
998         gint64 duration;
999 
1000         /* ask sink peer for duration */
1001         res &= gst_pad_peer_query_duration (pad, format, &duration);
1002         /* take max from all valid return values */
1003         if (res) {
1004           /* valid unknown length, stop searching */
1005           if (duration == -1) {
1006             max = duration;
1007             done = TRUE;
1008           }
1009           /* else see if bigger than current max */
1010           else if (duration > max)
1011             max = duration;
1012         }
1013         gst_object_unref (pad);
1014         g_value_unset (&item);
1015         break;
1016       }
1017       case GST_ITERATOR_RESYNC:
1018         max = -1;
1019         res = TRUE;
1020         gst_iterator_resync (it);
1021         break;
1022       default:
1023         res = FALSE;
1024         done = TRUE;
1025         break;
1026     }
1027   }
1028   gst_iterator_free (it);
1029 
1030   if (res) {
1031     /* If in bytes format we have to multiply with the number of channels
1032      * to get the correct results. All other formats should be fine */
1033     if (format == GST_FORMAT_BYTES && max != -1)
1034       max *= self->channels;
1035 
1036     /* and store the max */
1037     GST_DEBUG_OBJECT (self, "Total duration in format %s: %"
1038         GST_TIME_FORMAT, gst_format_get_name (format), GST_TIME_ARGS (max));
1039     gst_query_set_duration (query, format, max);
1040   }
1041 
1042   return res;
1043 }
1044 
1045 static gboolean
gst_interleave_src_query(GstPad * pad,GstObject * parent,GstQuery * query)1046 gst_interleave_src_query (GstPad * pad, GstObject * parent, GstQuery * query)
1047 {
1048   GstInterleave *self = GST_INTERLEAVE (parent);
1049   gboolean res = FALSE;
1050 
1051   switch (GST_QUERY_TYPE (query)) {
1052     case GST_QUERY_POSITION:
1053     {
1054       GstFormat format;
1055 
1056       gst_query_parse_position (query, &format, NULL);
1057 
1058       switch (format) {
1059         case GST_FORMAT_TIME:
1060           /* FIXME, bring to stream time, might be tricky */
1061           gst_query_set_position (query, format, self->timestamp);
1062           res = TRUE;
1063           break;
1064         case GST_FORMAT_BYTES:
1065           gst_query_set_position (query, format,
1066               self->offset * self->channels * self->width);
1067           res = TRUE;
1068           break;
1069         case GST_FORMAT_DEFAULT:
1070           gst_query_set_position (query, format, self->offset);
1071           res = TRUE;
1072           break;
1073         default:
1074           break;
1075       }
1076       break;
1077     }
1078     case GST_QUERY_DURATION:
1079       res = gst_interleave_src_query_duration (self, query);
1080       break;
1081     default:
1082       /* FIXME, needs a custom query handler because we have multiple
1083        * sinkpads */
1084       res = gst_pad_query_default (pad, parent, query);
1085       break;
1086   }
1087 
1088   return res;
1089 }
1090 
1091 static gboolean
forward_event_func(const GValue * item,GValue * ret,GstEvent * event)1092 forward_event_func (const GValue * item, GValue * ret, GstEvent * event)
1093 {
1094   GstPad *pad = GST_PAD_CAST (g_value_dup_object (item));
1095   gst_event_ref (event);
1096   GST_LOG_OBJECT (pad, "About to send event %s", GST_EVENT_TYPE_NAME (event));
1097   if (!gst_pad_push_event (pad, event)) {
1098     g_value_set_boolean (ret, FALSE);
1099     GST_WARNING_OBJECT (pad, "Sending event  %p (%s) failed.",
1100         event, GST_EVENT_TYPE_NAME (event));
1101   } else {
1102     GST_LOG_OBJECT (pad, "Sent event  %p (%s).",
1103         event, GST_EVENT_TYPE_NAME (event));
1104   }
1105   gst_object_unref (pad);
1106   return TRUE;
1107 }
1108 
1109 static gboolean
forward_event(GstInterleave * self,GstEvent * event)1110 forward_event (GstInterleave * self, GstEvent * event)
1111 {
1112   GstIterator *it;
1113   GValue vret = { 0 };
1114 
1115   GST_LOG_OBJECT (self, "Forwarding event %p (%s)", event,
1116       GST_EVENT_TYPE_NAME (event));
1117 
1118   g_value_init (&vret, G_TYPE_BOOLEAN);
1119   g_value_set_boolean (&vret, TRUE);
1120   it = gst_element_iterate_sink_pads (GST_ELEMENT_CAST (self));
1121   gst_iterator_fold (it, (GstIteratorFoldFunction) forward_event_func, &vret,
1122       event);
1123   gst_iterator_free (it);
1124   gst_event_unref (event);
1125 
1126   return g_value_get_boolean (&vret);
1127 }
1128 
1129 
1130 static gboolean
gst_interleave_src_event(GstPad * pad,GstObject * parent,GstEvent * event)1131 gst_interleave_src_event (GstPad * pad, GstObject * parent, GstEvent * event)
1132 {
1133   GstInterleave *self = GST_INTERLEAVE (parent);
1134   gboolean result;
1135 
1136   switch (GST_EVENT_TYPE (event)) {
1137     case GST_EVENT_QOS:
1138       /* QoS might be tricky */
1139       result = FALSE;
1140       break;
1141     case GST_EVENT_SEEK:
1142     {
1143       GstSeekFlags flags;
1144 
1145       gst_event_parse_seek (event, NULL, NULL, &flags, NULL, NULL, NULL, NULL);
1146 
1147       /* check if we are flushing */
1148       if (flags & GST_SEEK_FLAG_FLUSH) {
1149         /* make sure we accept nothing anymore and return WRONG_STATE */
1150         gst_collect_pads_set_flushing (self->collect, TRUE);
1151 
1152         /* flushing seek, start flush downstream, the flush will be done
1153          * when all pads received a FLUSH_STOP. */
1154         gst_pad_push_event (self->src, gst_event_new_flush_start ());
1155       }
1156       result = forward_event (self, event);
1157       break;
1158     }
1159     case GST_EVENT_NAVIGATION:
1160       /* navigation is rather pointless. */
1161       result = FALSE;
1162       break;
1163     default:
1164       /* just forward the rest for now */
1165       result = forward_event (self, event);
1166       break;
1167   }
1168 
1169   return result;
1170 }
1171 
1172 static GstFlowReturn
gst_interleave_collected(GstCollectPads * pads,GstInterleave * self)1173 gst_interleave_collected (GstCollectPads * pads, GstInterleave * self)
1174 {
1175   guint size;
1176   GstBuffer *outbuf = NULL;
1177   GstFlowReturn ret = GST_FLOW_OK;
1178   GSList *collected;
1179   guint nsamples;
1180   guint ncollected = 0;
1181   gboolean empty = TRUE;
1182   gint width = self->width / 8;
1183   GstMapInfo write_info;
1184   GstClockTime timestamp = -1;
1185 
1186   size = gst_collect_pads_available (pads);
1187   if (size == 0)
1188     goto eos;
1189 
1190   g_return_val_if_fail (self->func != NULL, GST_FLOW_NOT_NEGOTIATED);
1191   g_return_val_if_fail (self->width > 0, GST_FLOW_NOT_NEGOTIATED);
1192   g_return_val_if_fail (self->channels > 0, GST_FLOW_NOT_NEGOTIATED);
1193   g_return_val_if_fail (self->rate > 0, GST_FLOW_NOT_NEGOTIATED);
1194 
1195   g_return_val_if_fail (size % width == 0, GST_FLOW_ERROR);
1196 
1197   GST_DEBUG_OBJECT (self, "Starting to collect %u bytes from %d channels", size,
1198       self->channels);
1199 
1200   nsamples = size / width;
1201 
1202   outbuf = gst_buffer_new_allocate (NULL, size * self->channels, NULL);
1203 
1204   if (outbuf == NULL || gst_buffer_get_size (outbuf) < size * self->channels) {
1205     gst_buffer_unref (outbuf);
1206     return GST_FLOW_NOT_NEGOTIATED;
1207   }
1208 
1209   gst_buffer_map (outbuf, &write_info, GST_MAP_WRITE);
1210   memset (write_info.data, 0, size * self->channels);
1211 
1212   for (collected = pads->data; collected != NULL; collected = collected->next) {
1213     GstCollectData *cdata;
1214     GstBuffer *inbuf;
1215     guint8 *outdata;
1216     GstMapInfo input_info;
1217     gint channel;
1218 
1219     cdata = (GstCollectData *) collected->data;
1220 
1221     inbuf = gst_collect_pads_take_buffer (pads, cdata, size);
1222     if (inbuf == NULL) {
1223       GST_DEBUG_OBJECT (cdata->pad, "No buffer available");
1224       goto next;
1225     }
1226     ncollected++;
1227 
1228     if (timestamp == -1)
1229       timestamp = GST_BUFFER_TIMESTAMP (inbuf);
1230 
1231     if (GST_BUFFER_FLAG_IS_SET (inbuf, GST_BUFFER_FLAG_GAP))
1232       goto next;
1233 
1234     empty = FALSE;
1235     channel = GST_INTERLEAVE_PAD_CAST (cdata->pad)->channel;
1236     if (self->channels <= 64 && self->channel_mask) {
1237       channel = self->default_channels_ordering_map[channel];
1238     }
1239     outdata = write_info.data + width * channel;
1240 
1241     gst_buffer_map (inbuf, &input_info, GST_MAP_READ);
1242     self->func (outdata, input_info.data, self->channels, nsamples);
1243     gst_buffer_unmap (inbuf, &input_info);
1244 
1245   next:
1246     if (inbuf)
1247       gst_buffer_unref (inbuf);
1248   }
1249 
1250   if (ncollected == 0) {
1251     gst_buffer_unmap (outbuf, &write_info);
1252     goto eos;
1253   }
1254 
1255   GST_OBJECT_LOCK (self);
1256   if (self->pending_segment) {
1257     GstEvent *event;
1258     GstSegment segment;
1259 
1260     event = self->pending_segment;
1261     self->pending_segment = NULL;
1262     GST_OBJECT_UNLOCK (self);
1263 
1264     /* convert the input segment to time now */
1265     gst_event_copy_segment (event, &segment);
1266 
1267     if (segment.format != GST_FORMAT_TIME) {
1268       gst_event_unref (event);
1269 
1270       /* not time, convert */
1271       switch (segment.format) {
1272         case GST_FORMAT_BYTES:
1273           segment.start *= width;
1274           if (segment.stop != -1)
1275             segment.stop *= width;
1276           if (segment.position != -1)
1277             segment.position *= width;
1278           /* fallthrough for the samples case */
1279         case GST_FORMAT_DEFAULT:
1280           segment.start =
1281               gst_util_uint64_scale_int (segment.start, GST_SECOND, self->rate);
1282           if (segment.stop != -1)
1283             segment.stop =
1284                 gst_util_uint64_scale_int (segment.stop, GST_SECOND,
1285                 self->rate);
1286           if (segment.position != -1)
1287             segment.position =
1288                 gst_util_uint64_scale_int (segment.position, GST_SECOND,
1289                 self->rate);
1290           break;
1291         default:
1292           GST_WARNING ("can't convert segment values");
1293           segment.start = 0;
1294           segment.stop = -1;
1295           segment.position = 0;
1296           break;
1297       }
1298       event = gst_event_new_segment (&segment);
1299     }
1300     gst_pad_push_event (self->src, event);
1301 
1302     GST_OBJECT_LOCK (self);
1303   }
1304   GST_OBJECT_UNLOCK (self);
1305 
1306   if (timestamp != -1) {
1307     self->offset = gst_util_uint64_scale_int (timestamp, self->rate,
1308         GST_SECOND);
1309     self->timestamp = timestamp;
1310   }
1311 
1312   GST_BUFFER_TIMESTAMP (outbuf) = self->timestamp;
1313   GST_BUFFER_OFFSET (outbuf) = self->offset;
1314 
1315   self->offset += nsamples;
1316   self->timestamp = gst_util_uint64_scale_int (self->offset,
1317       GST_SECOND, self->rate);
1318 
1319   GST_BUFFER_DURATION (outbuf) =
1320       self->timestamp - GST_BUFFER_TIMESTAMP (outbuf);
1321 
1322   if (empty)
1323     GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_GAP);
1324 
1325   gst_buffer_unmap (outbuf, &write_info);
1326 
1327   GST_LOG_OBJECT (self, "pushing outbuf, timestamp %" GST_TIME_FORMAT,
1328       GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (outbuf)));
1329   ret = gst_pad_push (self->src, outbuf);
1330 
1331   return ret;
1332 
1333 eos:
1334   {
1335     GST_DEBUG_OBJECT (self, "no data available, must be EOS");
1336     if (outbuf)
1337       gst_buffer_unref (outbuf);
1338     gst_pad_push_event (self->src, gst_event_new_eos ());
1339     return GST_FLOW_EOS;
1340   }
1341 }
1342