• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * GStreamer
3  * Copyright (C) 2016 Sebastian Dröge <sebastian@centricular.com>
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Library General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Library General Public License for more details.
14  *
15  * You should have received a copy of the GNU Library General Public
16  * License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
18  * Boston, MA 02110-1301, USA.
19  */
20 
21 #ifdef HAVE_CONFIG_H
22 #include "config.h"
23 #endif
24 
25 #include "gstaudiobuffersplit.h"
26 
27 #define GST_CAT_DEFAULT gst_audio_buffer_split_debug
28 GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
29 
30 static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink",
31     GST_PAD_SINK,
32     GST_PAD_ALWAYS,
33     GST_STATIC_CAPS (GST_AUDIO_CAPS_MAKE (GST_AUDIO_FORMATS_ALL)
34         ", layout = (string) interleaved")
35     );
36 
37 static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src",
38     GST_PAD_SRC,
39     GST_PAD_ALWAYS,
40     GST_STATIC_CAPS (GST_AUDIO_CAPS_MAKE (GST_AUDIO_FORMATS_ALL)
41         ", layout = (string) interleaved")
42     );
43 
44 enum
45 {
46   PROP_0,
47   PROP_OUTPUT_BUFFER_DURATION,
48   PROP_OUTPUT_BUFFER_SIZE,
49   PROP_ALIGNMENT_THRESHOLD,
50   PROP_DISCONT_WAIT,
51   PROP_STRICT_BUFFER_SIZE,
52   PROP_GAPLESS,
53   PROP_MAX_SILENCE_TIME,
54   LAST_PROP
55 };
56 
57 #define DEFAULT_OUTPUT_BUFFER_DURATION_N (1)
58 #define DEFAULT_OUTPUT_BUFFER_DURATION_D (50)
59 #define DEFAULT_ALIGNMENT_THRESHOLD   (40 * GST_MSECOND)
60 #define DEFAULT_DISCONT_WAIT (1 * GST_SECOND)
61 #define DEFAULT_STRICT_BUFFER_SIZE (FALSE)
62 #define DEFAULT_GAPLESS (FALSE)
63 #define DEFAULT_MAX_SILENCE_TIME (0)
64 
65 #define parent_class gst_audio_buffer_split_parent_class
66 G_DEFINE_TYPE_WITH_CODE (GstAudioBufferSplit, gst_audio_buffer_split,
67     GST_TYPE_ELEMENT, GST_DEBUG_CATEGORY_INIT (gst_audio_buffer_split_debug,
68         "audiobuffersplit", 0, "Audio buffer splitter"););
69 GST_ELEMENT_REGISTER_DEFINE (audiobuffersplit, "audiobuffersplit",
70     GST_RANK_NONE, GST_TYPE_AUDIO_BUFFER_SPLIT);
71 
72 static GstFlowReturn gst_audio_buffer_split_sink_chain (GstPad * pad,
73     GstObject * parent, GstBuffer * buffer);
74 static gboolean gst_audio_buffer_split_sink_event (GstPad * pad,
75     GstObject * parent, GstEvent * event);
76 static gboolean gst_audio_buffer_split_src_query (GstPad * pad,
77     GstObject * parent, GstQuery * query);
78 
79 static void gst_audio_buffer_split_finalize (GObject * object);
80 static void gst_audio_buffer_split_get_property (GObject * object,
81     guint property_id, GValue * value, GParamSpec * pspec);
82 static void gst_audio_buffer_split_set_property (GObject * object,
83     guint property_id, const GValue * value, GParamSpec * pspec);
84 
85 static GstStateChangeReturn gst_audio_buffer_split_change_state (GstElement *
86     element, GstStateChange transition);
87 
88 static void
gst_audio_buffer_split_class_init(GstAudioBufferSplitClass * klass)89 gst_audio_buffer_split_class_init (GstAudioBufferSplitClass * klass)
90 {
91   GObjectClass *gobject_class = (GObjectClass *) klass;
92   GstElementClass *gstelement_class = (GstElementClass *) klass;
93 
94   gobject_class->set_property = gst_audio_buffer_split_set_property;
95   gobject_class->get_property = gst_audio_buffer_split_get_property;
96   gobject_class->finalize = gst_audio_buffer_split_finalize;
97 
98   g_object_class_install_property (gobject_class, PROP_OUTPUT_BUFFER_DURATION,
99       gst_param_spec_fraction ("output-buffer-duration",
100           "Output Buffer Duration", "Output block size in seconds", 1, G_MAXINT,
101           G_MAXINT, 1, DEFAULT_OUTPUT_BUFFER_DURATION_N,
102           DEFAULT_OUTPUT_BUFFER_DURATION_D,
103           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS |
104           GST_PARAM_MUTABLE_READY));
105 
106   /**
107    * GstAudioBufferSplit:output-buffer-size
108    *
109    * Allow specifying a buffer size for splitting. Zero by default.
110    * Takes precedence over output-buffer-duration when set to a
111    * non zero value else will not be in effect.
112    *
113    * Since: 1.20
114    */
115   g_object_class_install_property (gobject_class, PROP_OUTPUT_BUFFER_SIZE,
116       g_param_spec_uint ("output-buffer-size", "Output buffer size",
117           "Output block size in bytes, takes precedence over "
118           "buffer duration when set to non zero", 0, G_MAXINT, 0,
119           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS |
120           GST_PARAM_MUTABLE_READY));
121 
122   g_object_class_install_property (gobject_class, PROP_ALIGNMENT_THRESHOLD,
123       g_param_spec_uint64 ("alignment-threshold", "Alignment Threshold",
124           "Timestamp alignment threshold in nanoseconds", 0,
125           G_MAXUINT64 - 1, DEFAULT_ALIGNMENT_THRESHOLD,
126           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS |
127           GST_PARAM_MUTABLE_READY));
128 
129   g_object_class_install_property (gobject_class, PROP_DISCONT_WAIT,
130       g_param_spec_uint64 ("discont-wait", "Discont Wait",
131           "Window of time in nanoseconds to wait before "
132           "creating a discontinuity", 0,
133           G_MAXUINT64 - 1, DEFAULT_DISCONT_WAIT,
134           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS |
135           GST_PARAM_MUTABLE_READY));
136 
137   g_object_class_install_property (gobject_class, PROP_STRICT_BUFFER_SIZE,
138       g_param_spec_boolean ("strict-buffer-size", "Strict buffer size",
139           "Discard the last samples at EOS or discont if they are too "
140           "small to fill a buffer", DEFAULT_STRICT_BUFFER_SIZE,
141           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS |
142           GST_PARAM_MUTABLE_READY));
143 
144   g_object_class_install_property (gobject_class, PROP_GAPLESS,
145       g_param_spec_boolean ("gapless", "Gapless",
146           "Insert silence/drop samples instead of creating a discontinuity",
147           DEFAULT_GAPLESS,
148           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS |
149           GST_PARAM_MUTABLE_READY));
150 
151   g_object_class_install_property (gobject_class, PROP_MAX_SILENCE_TIME,
152       g_param_spec_uint64 ("max-silence-time",
153           "Maximum time of silence to insert",
154           "Do not insert silence in gapless mode if the gap exceeds this "
155           "period (in ns) (0 = disabled)",
156           0, G_MAXUINT64, DEFAULT_MAX_SILENCE_TIME,
157           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS |
158           GST_PARAM_MUTABLE_READY));
159 
160   gst_element_class_set_static_metadata (gstelement_class,
161       "Audio Buffer Split", "Audio/Filter",
162       "Splits raw audio buffers into equal sized chunks",
163       "Sebastian Dröge <sebastian@centricular.com>");
164 
165   gst_element_class_add_pad_template (gstelement_class,
166       gst_static_pad_template_get (&src_template));
167   gst_element_class_add_pad_template (gstelement_class,
168       gst_static_pad_template_get (&sink_template));
169 
170   gstelement_class->change_state = gst_audio_buffer_split_change_state;
171 }
172 
173 static void
gst_audio_buffer_split_init(GstAudioBufferSplit * self)174 gst_audio_buffer_split_init (GstAudioBufferSplit * self)
175 {
176   self->sinkpad = gst_pad_new_from_static_template (&sink_template, "sink");
177   gst_pad_set_chain_function (self->sinkpad,
178       GST_DEBUG_FUNCPTR (gst_audio_buffer_split_sink_chain));
179   gst_pad_set_event_function (self->sinkpad,
180       GST_DEBUG_FUNCPTR (gst_audio_buffer_split_sink_event));
181   GST_PAD_SET_PROXY_CAPS (self->sinkpad);
182   gst_element_add_pad (GST_ELEMENT (self), self->sinkpad);
183 
184   self->srcpad = gst_pad_new_from_static_template (&src_template, "src");
185   gst_pad_set_query_function (self->srcpad,
186       GST_DEBUG_FUNCPTR (gst_audio_buffer_split_src_query));
187   GST_PAD_SET_PROXY_CAPS (self->srcpad);
188   gst_pad_use_fixed_caps (self->srcpad);
189   gst_element_add_pad (GST_ELEMENT (self), self->srcpad);
190 
191   self->output_buffer_duration_n = DEFAULT_OUTPUT_BUFFER_DURATION_N;
192   self->output_buffer_duration_d = DEFAULT_OUTPUT_BUFFER_DURATION_D;
193   self->strict_buffer_size = DEFAULT_STRICT_BUFFER_SIZE;
194   self->gapless = DEFAULT_GAPLESS;
195   self->output_buffer_size = 0;
196 
197   self->adapter = gst_adapter_new ();
198 
199   self->stream_align =
200       gst_audio_stream_align_new (48000, DEFAULT_ALIGNMENT_THRESHOLD,
201       DEFAULT_DISCONT_WAIT);
202 }
203 
204 static void
gst_audio_buffer_split_finalize(GObject * object)205 gst_audio_buffer_split_finalize (GObject * object)
206 {
207   GstAudioBufferSplit *self = GST_AUDIO_BUFFER_SPLIT (object);
208 
209   if (self->adapter) {
210     gst_object_unref (self->adapter);
211     self->adapter = NULL;
212   }
213 
214   if (self->stream_align) {
215     gst_audio_stream_align_free (self->stream_align);
216     self->stream_align = NULL;
217   }
218 
219   G_OBJECT_CLASS (parent_class)->finalize (object);
220 }
221 
222 static gboolean
gst_audio_buffer_split_update_samples_per_buffer(GstAudioBufferSplit * self)223 gst_audio_buffer_split_update_samples_per_buffer (GstAudioBufferSplit * self)
224 {
225   gboolean ret = TRUE;
226 
227   GST_OBJECT_LOCK (self);
228 
229   /* For a later time */
230   if (!self->info.finfo
231       || GST_AUDIO_INFO_FORMAT (&self->info) == GST_AUDIO_FORMAT_UNKNOWN) {
232     self->samples_per_buffer = 0;
233     goto out;
234   }
235 
236   if (self->output_buffer_size) {
237     self->output_buffer_duration_n =
238         self->output_buffer_size / GST_AUDIO_INFO_BPF (&self->info);
239     self->output_buffer_duration_d = GST_AUDIO_INFO_RATE (&self->info);
240   }
241 
242   self->samples_per_buffer =
243       (((guint64) GST_AUDIO_INFO_RATE (&self->info)) *
244       self->output_buffer_duration_n) / self->output_buffer_duration_d;
245   if (self->samples_per_buffer == 0) {
246     ret = FALSE;
247     goto out;
248   }
249 
250   self->error_per_buffer =
251       (((guint64) GST_AUDIO_INFO_RATE (&self->info)) *
252       self->output_buffer_duration_n) % self->output_buffer_duration_d;
253   self->accumulated_error = 0;
254 
255   GST_DEBUG_OBJECT (self, "Buffer duration: %u/%u",
256       self->output_buffer_duration_n, self->output_buffer_duration_d);
257   GST_DEBUG_OBJECT (self, "Samples per buffer: %u (error: %u/%u)",
258       self->samples_per_buffer, self->error_per_buffer,
259       self->output_buffer_duration_d);
260 out:
261   GST_OBJECT_UNLOCK (self);
262 
263   return ret;
264 }
265 
266 static void
gst_audio_buffer_split_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)267 gst_audio_buffer_split_set_property (GObject * object, guint property_id,
268     const GValue * value, GParamSpec * pspec)
269 {
270   GstAudioBufferSplit *self = GST_AUDIO_BUFFER_SPLIT (object);
271 
272   switch (property_id) {
273     case PROP_OUTPUT_BUFFER_DURATION:
274       self->output_buffer_duration_n = gst_value_get_fraction_numerator (value);
275       self->output_buffer_duration_d =
276           gst_value_get_fraction_denominator (value);
277       gst_audio_buffer_split_update_samples_per_buffer (self);
278       break;
279     case PROP_OUTPUT_BUFFER_SIZE:
280       self->output_buffer_size = g_value_get_uint (value);
281       gst_audio_buffer_split_update_samples_per_buffer (self);
282       break;
283     case PROP_ALIGNMENT_THRESHOLD:
284       GST_OBJECT_LOCK (self);
285       gst_audio_stream_align_set_alignment_threshold (self->stream_align,
286           g_value_get_uint64 (value));
287       GST_OBJECT_UNLOCK (self);
288       break;
289     case PROP_DISCONT_WAIT:
290       GST_OBJECT_LOCK (self);
291       gst_audio_stream_align_set_discont_wait (self->stream_align,
292           g_value_get_uint64 (value));
293       GST_OBJECT_UNLOCK (self);
294       break;
295     case PROP_STRICT_BUFFER_SIZE:
296       self->strict_buffer_size = g_value_get_boolean (value);
297       break;
298     case PROP_GAPLESS:
299       self->gapless = g_value_get_boolean (value);
300       break;
301     case PROP_MAX_SILENCE_TIME:
302       self->max_silence_time = g_value_get_uint64 (value);
303       break;
304     default:
305       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
306       break;
307   }
308 }
309 
310 static void
gst_audio_buffer_split_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)311 gst_audio_buffer_split_get_property (GObject * object, guint property_id,
312     GValue * value, GParamSpec * pspec)
313 {
314   GstAudioBufferSplit *self = GST_AUDIO_BUFFER_SPLIT (object);
315 
316   switch (property_id) {
317     case PROP_OUTPUT_BUFFER_DURATION:
318       gst_value_set_fraction (value, self->output_buffer_duration_n,
319           self->output_buffer_duration_d);
320       break;
321     case PROP_OUTPUT_BUFFER_SIZE:
322       g_value_set_uint (value, self->output_buffer_size);
323       break;
324     case PROP_ALIGNMENT_THRESHOLD:
325       GST_OBJECT_LOCK (self);
326       g_value_set_uint64 (value,
327           gst_audio_stream_align_get_alignment_threshold (self->stream_align));
328       GST_OBJECT_UNLOCK (self);
329       break;
330     case PROP_DISCONT_WAIT:
331       GST_OBJECT_LOCK (self);
332       g_value_set_uint64 (value,
333           gst_audio_stream_align_get_discont_wait (self->stream_align));
334       GST_OBJECT_UNLOCK (self);
335       break;
336     case PROP_STRICT_BUFFER_SIZE:
337       g_value_set_boolean (value, self->strict_buffer_size);
338       break;
339     case PROP_GAPLESS:
340       g_value_set_boolean (value, self->gapless);
341       break;
342     case PROP_MAX_SILENCE_TIME:
343       g_value_set_uint64 (value, self->max_silence_time);
344       break;
345     default:
346       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
347       break;
348   }
349 }
350 
351 static GstStateChangeReturn
gst_audio_buffer_split_change_state(GstElement * element,GstStateChange transition)352 gst_audio_buffer_split_change_state (GstElement * element,
353     GstStateChange transition)
354 {
355   GstAudioBufferSplit *self = GST_AUDIO_BUFFER_SPLIT (element);
356   GstStateChangeReturn state_ret;
357 
358   switch (transition) {
359     case GST_STATE_CHANGE_READY_TO_PAUSED:
360       gst_audio_info_init (&self->info);
361       gst_segment_init (&self->in_segment, GST_FORMAT_TIME);
362       gst_segment_init (&self->out_segment, GST_FORMAT_UNDEFINED);
363       self->segment_pending = FALSE;
364       GST_OBJECT_LOCK (self);
365       gst_audio_stream_align_mark_discont (self->stream_align);
366       GST_OBJECT_UNLOCK (self);
367       self->current_offset = -1;
368       self->accumulated_error = 0;
369       self->samples_per_buffer = 0;
370       break;
371     default:
372       break;
373   }
374 
375   state_ret =
376       GST_ELEMENT_CLASS (gst_audio_buffer_split_parent_class)->change_state
377       (element, transition);
378   if (state_ret == GST_STATE_CHANGE_FAILURE)
379     return state_ret;
380 
381   switch (transition) {
382     case GST_STATE_CHANGE_PAUSED_TO_READY:
383       gst_adapter_clear (self->adapter);
384       GST_OBJECT_LOCK (self);
385       gst_audio_stream_align_mark_discont (self->stream_align);
386       GST_OBJECT_UNLOCK (self);
387       break;
388     default:
389       break;
390   }
391 
392   return state_ret;
393 }
394 
395 static GstFlowReturn
gst_audio_buffer_split_output(GstAudioBufferSplit * self,gboolean force,gint rate,gint bpf,guint samples_per_buffer)396 gst_audio_buffer_split_output (GstAudioBufferSplit * self, gboolean force,
397     gint rate, gint bpf, guint samples_per_buffer)
398 {
399   gint size, avail;
400   GstFlowReturn ret = GST_FLOW_OK;
401   GstClockTime resync_pts;
402 
403   resync_pts = self->resync_pts;
404   size = samples_per_buffer * bpf;
405 
406   /* If we accumulated enough error for one sample, include one
407    * more sample in this buffer. Accumulated error is updated below */
408   if (self->error_per_buffer + self->accumulated_error >=
409       self->output_buffer_duration_d)
410     size += bpf;
411 
412   while ((avail = gst_adapter_available (self->adapter)) >= size || (force
413           && avail > 0)) {
414     GstBuffer *buffer;
415     GstClockTime resync_time_diff;
416 
417     size = MIN (size, avail);
418     buffer = gst_adapter_take_buffer (self->adapter, size);
419     buffer = gst_buffer_make_writable (buffer);
420 
421     /* After a reset we have to set the discont flag */
422     if (self->current_offset == 0)
423       GST_BUFFER_FLAG_SET (buffer,
424           GST_BUFFER_FLAG_DISCONT | GST_BUFFER_FLAG_RESYNC);
425     else
426       GST_BUFFER_FLAG_UNSET (buffer,
427           GST_BUFFER_FLAG_DISCONT | GST_BUFFER_FLAG_RESYNC);
428 
429     resync_time_diff =
430         gst_util_uint64_scale (self->current_offset, GST_SECOND, rate);
431     if (self->out_segment.rate < 0.0) {
432       if (resync_pts > resync_time_diff)
433         GST_BUFFER_PTS (buffer) = resync_pts - resync_time_diff;
434       else
435         GST_BUFFER_PTS (buffer) = 0;
436       GST_BUFFER_DURATION (buffer) =
437           gst_util_uint64_scale (size / bpf, GST_SECOND, rate);
438 
439       self->current_offset += size / bpf;
440     } else {
441       GST_BUFFER_PTS (buffer) = resync_pts + resync_time_diff;
442       self->current_offset += size / bpf;
443       resync_time_diff =
444           gst_util_uint64_scale (self->current_offset, GST_SECOND, rate);
445       GST_BUFFER_DURATION (buffer) =
446           resync_time_diff - (GST_BUFFER_PTS (buffer) - resync_pts);
447     }
448 
449     GST_BUFFER_OFFSET (buffer) = GST_BUFFER_OFFSET_NONE;
450     GST_BUFFER_OFFSET_END (buffer) = GST_BUFFER_OFFSET_NONE;
451 
452     self->accumulated_error =
453         (self->accumulated_error +
454         self->error_per_buffer) % self->output_buffer_duration_d;
455 
456     GST_LOG_OBJECT (self,
457         "Outputting buffer at running time %" GST_TIME_FORMAT
458         " with timestamp %" GST_TIME_FORMAT " with duration %" GST_TIME_FORMAT
459         " (%u samples)",
460         GST_TIME_ARGS (gst_segment_to_running_time (&self->out_segment,
461                 GST_FORMAT_TIME, GST_BUFFER_PTS (buffer))),
462         GST_TIME_ARGS (GST_BUFFER_PTS (buffer)),
463         GST_TIME_ARGS (GST_BUFFER_DURATION (buffer)), size / bpf);
464 
465     ret = gst_pad_push (self->srcpad, buffer);
466     if (ret != GST_FLOW_OK)
467       break;
468 
469     /* Update the size based on the accumulated error we have now after
470      * taking out a buffer. Same code as above */
471     size = samples_per_buffer * bpf;
472     if (self->error_per_buffer + self->accumulated_error >=
473         self->output_buffer_duration_d)
474       size += bpf;
475   }
476 
477   return ret;
478 }
479 
480 static GstFlowReturn
gst_audio_buffer_split_handle_discont(GstAudioBufferSplit * self,GstBuffer * buffer,GstAudioFormat format,gint rate,gint bpf,guint samples_per_buffer)481 gst_audio_buffer_split_handle_discont (GstAudioBufferSplit * self,
482     GstBuffer * buffer, GstAudioFormat format, gint rate, gint bpf,
483     guint samples_per_buffer)
484 {
485   gboolean discont;
486   GstFlowReturn ret = GST_FLOW_OK;
487   guint avail = gst_adapter_available (self->adapter);
488   guint avail_samples = avail / bpf;
489   guint64 new_offset;
490   GstClockTime input_rt, current_rt;
491   GstClockTime input_duration;
492   GstClockTime current_rt_end;
493 
494   input_rt =
495       gst_segment_to_running_time (&self->in_segment, GST_FORMAT_TIME,
496       GST_BUFFER_PTS (buffer));
497   input_duration =
498       (gst_buffer_get_size (buffer) / bpf) / ABS (self->in_segment.rate);
499 
500   GST_OBJECT_LOCK (self);
501 
502   if (self->in_segment.rate < 0) {
503     discont = FALSE;
504   } else {
505     discont = GST_BUFFER_IS_DISCONT (buffer)
506         || GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_FLAG_RESYNC);
507   }
508 
509   /* If the segment rate is changing this is a discontinuity */
510   discont = discont || (self->out_segment.format != GST_FORMAT_UNDEFINED
511       && self->in_segment.rate != self->out_segment.rate);
512 
513   /* If this is the very first buffer we also have a discontinuity */
514   discont = discont || self->current_offset == -1;
515 
516   discont =
517       gst_audio_stream_align_process (self->stream_align,
518       discont, input_rt, input_duration, NULL, NULL, NULL);
519   GST_OBJECT_UNLOCK (self);
520 
521   if (!discont)
522     return ret;
523 
524   /* Reset */
525   self->drop_samples = 0;
526 
527   if (self->in_segment.rate < 0.0) {
528     current_rt =
529         self->resync_rt - gst_util_uint64_scale (self->current_offset +
530         avail_samples, GST_SECOND, rate * ABS (self->in_segment.rate));
531     current_rt_end =
532         self->resync_rt - gst_util_uint64_scale (self->current_offset,
533         GST_SECOND, rate * ABS (self->in_segment.rate));
534   } else {
535     current_rt =
536         self->resync_rt + gst_util_uint64_scale (self->current_offset,
537         GST_SECOND, rate * self->in_segment.rate);
538     current_rt_end =
539         self->resync_rt + gst_util_uint64_scale (self->current_offset +
540         avail_samples, GST_SECOND, rate * self->in_segment.rate);
541   }
542 
543   if (self->gapless) {
544     if (self->current_offset != -1) {
545       GST_DEBUG_OBJECT (self,
546           "Got discont in gapless mode: Current running time %" GST_TIME_FORMAT
547           ", current end running time %" GST_TIME_FORMAT
548           ", running time after discont %" GST_TIME_FORMAT,
549           GST_TIME_ARGS (current_rt),
550           GST_TIME_ARGS (current_rt_end), GST_TIME_ARGS (input_rt));
551 
552       new_offset =
553           gst_util_uint64_scale (current_rt - self->resync_rt,
554           rate * ABS (self->in_segment.rate), GST_SECOND);
555       if (current_rt < self->resync_rt) {
556         guint64 drop_samples;
557 
558         new_offset =
559             gst_util_uint64_scale (self->resync_rt -
560             current_rt, rate * ABS (self->in_segment.rate), GST_SECOND);
561         drop_samples = self->current_offset + avail_samples + new_offset;
562 
563         GST_DEBUG_OBJECT (self,
564             "Dropping %" G_GUINT64_FORMAT " samples (%" GST_TIME_FORMAT ")",
565             drop_samples, GST_TIME_ARGS (gst_util_uint64_scale (drop_samples,
566                     GST_SECOND, rate)));
567         discont = FALSE;
568       } else if (new_offset > self->current_offset + avail_samples) {
569         guint64 silence_samples =
570             new_offset - (self->current_offset + avail_samples);
571         const GstAudioFormatInfo *info = gst_audio_format_get_info (format);
572         GstClockTime silence_time =
573             gst_util_uint64_scale (silence_samples, GST_SECOND, rate);
574 
575         if (silence_time > self->max_silence_time) {
576           GST_DEBUG_OBJECT (self,
577               "Not inserting %" G_GUINT64_FORMAT " samples of silence (%"
578               GST_TIME_FORMAT " exceeds maximum %" GST_TIME_FORMAT ")",
579               silence_samples, GST_TIME_ARGS (silence_time),
580               GST_TIME_ARGS (self->max_silence_time));
581         } else {
582           GST_DEBUG_OBJECT (self,
583               "Inserting %" G_GUINT64_FORMAT " samples of silence (%"
584               GST_TIME_FORMAT ")", silence_samples,
585               GST_TIME_ARGS (silence_time));
586 
587           /* Insert silence buffers to fill the gap in 1s chunks */
588           while (silence_samples > 0) {
589             guint n_samples = MIN (silence_samples, rate);
590             GstBuffer *silence;
591             GstMapInfo map;
592 
593             silence = gst_buffer_new_and_alloc (n_samples * bpf);
594             GST_BUFFER_FLAG_SET (silence, GST_BUFFER_FLAG_GAP);
595             gst_buffer_map (silence, &map, GST_MAP_WRITE);
596             gst_audio_format_info_fill_silence (info, map.data, map.size);
597             gst_buffer_unmap (silence, &map);
598 
599             gst_adapter_push (self->adapter, silence);
600             ret =
601                 gst_audio_buffer_split_output (self, FALSE, rate, bpf,
602                 samples_per_buffer);
603             if (ret != GST_FLOW_OK)
604               return ret;
605 
606             silence_samples -= n_samples;
607           }
608           discont = FALSE;
609         }
610       } else if (new_offset < self->current_offset + avail_samples) {
611         guint64 drop_samples =
612             self->current_offset + avail_samples - new_offset;
613 
614         GST_DEBUG_OBJECT (self,
615             "Dropping %" G_GUINT64_FORMAT " samples (%" GST_TIME_FORMAT ")",
616             drop_samples, GST_TIME_ARGS (gst_util_uint64_scale (drop_samples,
617                     GST_SECOND, rate)));
618         self->drop_samples = drop_samples;
619         discont = FALSE;
620       }
621     }
622   }
623 
624   if (discont) {
625     /* We might end up in here also in gapless mode, if the above code decided
626      * that no silence is to be inserted, because e.g. the gap is too big */
627     GST_DEBUG_OBJECT (self,
628         "Got %s: Current running time %" GST_TIME_FORMAT
629         ", current end running time %" GST_TIME_FORMAT
630         ", running time after discont %" GST_TIME_FORMAT,
631         self->current_offset == -1 ? "first buffer" : "discont",
632         GST_TIME_ARGS (current_rt),
633         GST_TIME_ARGS (current_rt_end), GST_TIME_ARGS (input_rt));
634 
635     if (self->strict_buffer_size) {
636       gst_adapter_clear (self->adapter);
637       ret = GST_FLOW_OK;
638     } else {
639       ret =
640           gst_audio_buffer_split_output (self, TRUE, rate, bpf,
641           samples_per_buffer);
642     }
643 
644     self->current_offset = 0;
645     self->accumulated_error = 0;
646     self->resync_pts = GST_BUFFER_PTS (buffer);
647     self->resync_rt = input_rt;
648 
649     if (self->segment_pending) {
650       GstEvent *event;
651 
652       self->out_segment = self->in_segment;
653       GST_DEBUG_OBJECT (self, "Updating output segment %" GST_SEGMENT_FORMAT,
654           &self->out_segment);
655       event = gst_event_new_segment (&self->out_segment);
656       gst_event_set_seqnum (event, self->segment_seqnum);
657       gst_pad_push_event (self->srcpad, event);
658       self->segment_pending = FALSE;
659     }
660   }
661 
662   return ret;
663 }
664 
665 static GstBuffer *
gst_audio_buffer_split_clip_buffer_start_for_gapless(GstAudioBufferSplit * self,GstBuffer * buffer,gint rate,gint bpf)666 gst_audio_buffer_split_clip_buffer_start_for_gapless (GstAudioBufferSplit *
667     self, GstBuffer * buffer, gint rate, gint bpf)
668 {
669   guint nsamples;
670 
671   if (!self->gapless || self->drop_samples == 0)
672     return buffer;
673 
674   nsamples = gst_buffer_get_size (buffer) / bpf;
675 
676   GST_DEBUG_OBJECT (self, "Have to drop %" G_GUINT64_FORMAT
677       " samples, got %u samples", self->drop_samples, nsamples);
678 
679   if (nsamples <= self->drop_samples) {
680     gst_buffer_unref (buffer);
681     self->drop_samples -= nsamples;
682     return NULL;
683   }
684 
685   if (self->out_segment.rate < 0.0) {
686     buffer =
687         gst_audio_buffer_truncate (buffer, bpf, 0,
688         nsamples - self->drop_samples);
689     self->drop_samples = 0;
690     return buffer;
691   } else {
692     buffer = gst_audio_buffer_truncate (buffer, bpf, self->drop_samples, -1);
693     self->drop_samples = 0;
694     return buffer;
695   }
696 
697   return buffer;
698 }
699 
700 static GstFlowReturn
gst_audio_buffer_split_sink_chain(GstPad * pad,GstObject * parent,GstBuffer * buffer)701 gst_audio_buffer_split_sink_chain (GstPad * pad, GstObject * parent,
702     GstBuffer * buffer)
703 {
704   GstAudioBufferSplit *self = GST_AUDIO_BUFFER_SPLIT (parent);
705   GstFlowReturn ret;
706   GstAudioFormat format;
707   gint rate, bpf, samples_per_buffer;
708 
709   GST_OBJECT_LOCK (self);
710   format =
711       self->info.
712       finfo ? GST_AUDIO_INFO_FORMAT (&self->info) : GST_AUDIO_FORMAT_UNKNOWN;
713   rate = GST_AUDIO_INFO_RATE (&self->info);
714   bpf = GST_AUDIO_INFO_BPF (&self->info);
715   samples_per_buffer = self->samples_per_buffer;
716   GST_OBJECT_UNLOCK (self);
717 
718   GST_LOG_OBJECT (self,
719       "Processing buffer at running time %" GST_TIME_FORMAT
720       " with timestamp %" GST_TIME_FORMAT " with duration %" GST_TIME_FORMAT
721       " (%u samples)",
722       GST_TIME_ARGS (gst_segment_to_running_time (&self->in_segment,
723               GST_FORMAT_TIME, GST_BUFFER_PTS (buffer))),
724       GST_TIME_ARGS (GST_BUFFER_PTS (buffer)),
725       GST_TIME_ARGS (GST_BUFFER_DURATION (buffer)),
726       (guint) (gst_buffer_get_size (buffer) / bpf));
727 
728   if (format == GST_AUDIO_FORMAT_UNKNOWN || samples_per_buffer == 0) {
729     gst_buffer_unref (buffer);
730     return GST_FLOW_NOT_NEGOTIATED;
731   }
732 
733   buffer = gst_audio_buffer_clip (buffer, &self->in_segment, rate, bpf);
734   if (!buffer)
735     return GST_FLOW_OK;
736 
737   ret =
738       gst_audio_buffer_split_handle_discont (self, buffer, format, rate, bpf,
739       samples_per_buffer);
740   if (ret != GST_FLOW_OK) {
741     gst_buffer_unref (buffer);
742     return ret;
743   }
744 
745   buffer =
746       gst_audio_buffer_split_clip_buffer_start_for_gapless (self, buffer, rate,
747       bpf);
748   if (!buffer)
749     return GST_FLOW_OK;
750 
751   gst_adapter_push (self->adapter, buffer);
752 
753   return gst_audio_buffer_split_output (self, FALSE, rate, bpf,
754       samples_per_buffer);
755 }
756 
757 static gboolean
gst_audio_buffer_split_sink_event(GstPad * pad,GstObject * parent,GstEvent * event)758 gst_audio_buffer_split_sink_event (GstPad * pad, GstObject * parent,
759     GstEvent * event)
760 {
761   GstAudioBufferSplit *self = GST_AUDIO_BUFFER_SPLIT (parent);
762   gboolean ret = FALSE;
763 
764   switch (GST_EVENT_TYPE (event)) {
765     case GST_EVENT_CAPS:{
766       GstCaps *caps;
767       GstAudioInfo info;
768 
769       gst_event_parse_caps (event, &caps);
770 
771       ret = gst_audio_info_from_caps (&info, caps);
772       if (ret) {
773         GST_DEBUG_OBJECT (self, "Got caps %" GST_PTR_FORMAT, caps);
774 
775         if (!gst_audio_info_is_equal (&info, &self->info)) {
776           if (self->strict_buffer_size) {
777             gst_adapter_clear (self->adapter);
778           } else {
779             GstAudioFormat format;
780             gint rate, bpf, samples_per_buffer;
781 
782             GST_OBJECT_LOCK (self);
783             format =
784                 self->info.finfo ? GST_AUDIO_INFO_FORMAT (&self->info) :
785                 GST_AUDIO_FORMAT_UNKNOWN;
786             rate = GST_AUDIO_INFO_RATE (&self->info);
787             bpf = GST_AUDIO_INFO_BPF (&self->info);
788             samples_per_buffer = self->samples_per_buffer;
789             GST_OBJECT_UNLOCK (self);
790 
791             if (format != GST_AUDIO_FORMAT_UNKNOWN && samples_per_buffer != 0)
792               gst_audio_buffer_split_output (self, TRUE, rate, bpf,
793                   samples_per_buffer);
794           }
795         }
796         self->info = info;
797         GST_OBJECT_LOCK (self);
798         gst_audio_stream_align_set_rate (self->stream_align, self->info.rate);
799         GST_OBJECT_UNLOCK (self);
800         ret = gst_audio_buffer_split_update_samples_per_buffer (self);
801       } else {
802         ret = FALSE;
803       }
804 
805       if (ret)
806         ret = gst_pad_event_default (pad, parent, event);
807       else
808         gst_event_unref (event);
809 
810       break;
811     }
812     case GST_EVENT_FLUSH_STOP:
813       gst_segment_init (&self->in_segment, GST_FORMAT_TIME);
814       gst_segment_init (&self->out_segment, GST_FORMAT_UNDEFINED);
815       self->segment_pending = FALSE;
816       GST_OBJECT_LOCK (self);
817       gst_audio_stream_align_mark_discont (self->stream_align);
818       GST_OBJECT_UNLOCK (self);
819       self->current_offset = -1;
820       self->accumulated_error = 0;
821       gst_adapter_clear (self->adapter);
822       ret = gst_pad_event_default (pad, parent, event);
823       break;
824     case GST_EVENT_SEGMENT:
825       gst_event_copy_segment (event, &self->in_segment);
826       if (self->in_segment.format != GST_FORMAT_TIME) {
827         gst_event_unref (event);
828         ret = FALSE;
829       } else {
830         GST_DEBUG_OBJECT (self,
831             "Received new input segment %" GST_SEGMENT_FORMAT,
832             &self->in_segment);
833         self->segment_pending = TRUE;
834         self->segment_seqnum = gst_event_get_seqnum (event);
835         gst_event_unref (event);
836         ret = TRUE;
837       }
838       break;
839     case GST_EVENT_EOS:
840       if (self->strict_buffer_size) {
841         gst_adapter_clear (self->adapter);
842       } else {
843         GstAudioFormat format;
844         gint rate, bpf, samples_per_buffer;
845 
846         GST_OBJECT_LOCK (self);
847         format =
848             self->info.finfo ? GST_AUDIO_INFO_FORMAT (&self->info) :
849             GST_AUDIO_FORMAT_UNKNOWN;
850         rate = GST_AUDIO_INFO_RATE (&self->info);
851         bpf = GST_AUDIO_INFO_BPF (&self->info);
852         samples_per_buffer = self->samples_per_buffer;
853         GST_OBJECT_UNLOCK (self);
854 
855         if (format != GST_AUDIO_FORMAT_UNKNOWN && samples_per_buffer != 0)
856           gst_audio_buffer_split_output (self, TRUE, rate, bpf,
857               samples_per_buffer);
858       }
859       ret = gst_pad_event_default (pad, parent, event);
860       break;
861     default:
862       ret = gst_pad_event_default (pad, parent, event);
863       break;
864   }
865 
866   return ret;
867 }
868 
869 static gboolean
gst_audio_buffer_split_src_query(GstPad * pad,GstObject * parent,GstQuery * query)870 gst_audio_buffer_split_src_query (GstPad * pad,
871     GstObject * parent, GstQuery * query)
872 {
873   GstAudioBufferSplit *self = GST_AUDIO_BUFFER_SPLIT (parent);
874   gboolean ret = FALSE;
875 
876   switch (GST_QUERY_TYPE (query)) {
877     case GST_QUERY_LATENCY:{
878       if ((ret = gst_pad_peer_query (self->sinkpad, query))) {
879         GstClockTime latency;
880         GstClockTime min, max;
881         gboolean live;
882 
883         gst_query_parse_latency (query, &live, &min, &max);
884 
885         GST_DEBUG_OBJECT (self, "Peer latency: min %"
886             GST_TIME_FORMAT " max %" GST_TIME_FORMAT,
887             GST_TIME_ARGS (min), GST_TIME_ARGS (max));
888 
889         latency =
890             gst_util_uint64_scale (GST_SECOND, self->output_buffer_duration_n,
891             self->output_buffer_duration_d);
892 
893         GST_DEBUG_OBJECT (self, "Our latency: min %" GST_TIME_FORMAT
894             ", max %" GST_TIME_FORMAT,
895             GST_TIME_ARGS (latency), GST_TIME_ARGS (latency));
896 
897         min += latency;
898         if (max != GST_CLOCK_TIME_NONE)
899           max += latency;
900 
901         GST_DEBUG_OBJECT (self, "Calculated total latency : min %"
902             GST_TIME_FORMAT " max %" GST_TIME_FORMAT,
903             GST_TIME_ARGS (min), GST_TIME_ARGS (max));
904 
905         gst_query_set_latency (query, live, min, max);
906       }
907 
908       break;
909     }
910     default:
911       ret = gst_pad_query_default (pad, parent, query);
912       break;
913   }
914 
915   return ret;
916 }
917 
918 static gboolean
plugin_init(GstPlugin * plugin)919 plugin_init (GstPlugin * plugin)
920 {
921   return GST_ELEMENT_REGISTER (audiobuffersplit, plugin);
922 }
923 
924 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
925     GST_VERSION_MINOR,
926     audiobuffersplit,
927     "Audio buffer splitter",
928     plugin_init, VERSION, "LGPL", PACKAGE_NAME, GST_PACKAGE_ORIGIN)
929