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