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