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