blob: fd3f8091f94171a565a4098d3ea2ef4160c81fbb [file] [log] [blame]
Sebastian Dröge0acb3d82016-11-18 21:00:03 +02001/*
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
28GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
29
30static 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
36static 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
42enum
43{
44 PROP_0,
45 PROP_OUTPUT_BUFFER_DURATION,
46 PROP_ALIGNMENT_THRESHOLD,
47 PROP_DISCONT_WAIT,
Vivia Nikolaidou668c4402017-02-20 18:58:11 +020048 PROP_STRICT_BUFFER_SIZE,
Sebastian Dröge0acb3d82016-11-18 21:00:03 +020049 LAST_PROP
50};
51
52#define DEFAULT_OUTPUT_BUFFER_DURATION_N (1)
53#define DEFAULT_OUTPUT_BUFFER_DURATION_D (50)
54#define DEFAULT_ALIGNMENT_THRESHOLD (40 * GST_MSECOND)
55#define DEFAULT_DISCONT_WAIT (1 * GST_SECOND)
Vivia Nikolaidou668c4402017-02-20 18:58:11 +020056#define DEFAULT_STRICT_BUFFER_SIZE (FALSE)
Sebastian Dröge0acb3d82016-11-18 21:00:03 +020057
58#define parent_class gst_audio_buffer_split_parent_class
59G_DEFINE_TYPE (GstAudioBufferSplit, gst_audio_buffer_split, GST_TYPE_ELEMENT);
60
61static GstFlowReturn gst_audio_buffer_split_sink_chain (GstPad * pad,
62 GstObject * parent, GstBuffer * buffer);
63static gboolean gst_audio_buffer_split_sink_event (GstPad * pad,
64 GstObject * parent, GstEvent * event);
65static gboolean gst_audio_buffer_split_src_query (GstPad * pad,
66 GstObject * parent, GstQuery * query);
67
68static void gst_audio_buffer_split_finalize (GObject * object);
69static void gst_audio_buffer_split_get_property (GObject * object,
70 guint property_id, GValue * value, GParamSpec * pspec);
71static void gst_audio_buffer_split_set_property (GObject * object,
72 guint property_id, const GValue * value, GParamSpec * pspec);
73
74static GstStateChangeReturn gst_audio_buffer_split_change_state (GstElement *
75 element, GstStateChange transition);
76
77static void
78gst_audio_buffer_split_class_init (GstAudioBufferSplitClass * klass)
79{
80 GObjectClass *gobject_class = (GObjectClass *) klass;
81 GstElementClass *gstelement_class = (GstElementClass *) klass;
82
83 gobject_class->set_property = gst_audio_buffer_split_set_property;
84 gobject_class->get_property = gst_audio_buffer_split_get_property;
85 gobject_class->finalize = gst_audio_buffer_split_finalize;
86
87 g_object_class_install_property (gobject_class, PROP_OUTPUT_BUFFER_DURATION,
88 gst_param_spec_fraction ("output-buffer-duration",
89 "Output Buffer Duration", "Output block size in seconds", 1, G_MAXINT,
90 G_MAXINT, 1, DEFAULT_OUTPUT_BUFFER_DURATION_N,
91 DEFAULT_OUTPUT_BUFFER_DURATION_D,
92 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS |
93 GST_PARAM_MUTABLE_READY));
94
95 g_object_class_install_property (gobject_class, PROP_ALIGNMENT_THRESHOLD,
96 g_param_spec_uint64 ("alignment-threshold", "Alignment Threshold",
97 "Timestamp alignment threshold in nanoseconds", 0,
98 G_MAXUINT64 - 1, DEFAULT_ALIGNMENT_THRESHOLD,
99 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS |
100 GST_PARAM_MUTABLE_READY));
101
102 g_object_class_install_property (gobject_class, PROP_DISCONT_WAIT,
103 g_param_spec_uint64 ("discont-wait", "Discont Wait",
104 "Window of time in nanoseconds to wait before "
105 "creating a discontinuity", 0,
106 G_MAXUINT64 - 1, DEFAULT_DISCONT_WAIT,
107 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS |
108 GST_PARAM_MUTABLE_READY));
109
Vivia Nikolaidou668c4402017-02-20 18:58:11 +0200110 g_object_class_install_property (gobject_class, PROP_STRICT_BUFFER_SIZE,
111 g_param_spec_boolean ("strict-buffer-size", "Strict buffer size",
112 "Discard the last samples at EOS or discont if they are too "
113 "small to fill a buffer", DEFAULT_STRICT_BUFFER_SIZE,
114 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS |
115 GST_PARAM_MUTABLE_READY));
116
Sebastian Dröge0acb3d82016-11-18 21:00:03 +0200117 gst_element_class_set_static_metadata (gstelement_class,
118 "Audio Buffer Split", "Audio/Filter",
119 "Splits raw audio buffers into equal sized chunks",
120 "Sebastian Dröge <sebastian@centricular.com>");
121
122 gst_element_class_add_pad_template (gstelement_class,
123 gst_static_pad_template_get (&src_template));
124 gst_element_class_add_pad_template (gstelement_class,
125 gst_static_pad_template_get (&sink_template));
126
127 gstelement_class->change_state = gst_audio_buffer_split_change_state;
128}
129
130static void
131gst_audio_buffer_split_init (GstAudioBufferSplit * self)
132{
133 self->sinkpad = gst_pad_new_from_static_template (&sink_template, "sink");
134 gst_pad_set_chain_function (self->sinkpad,
135 GST_DEBUG_FUNCPTR (gst_audio_buffer_split_sink_chain));
136 gst_pad_set_event_function (self->sinkpad,
137 GST_DEBUG_FUNCPTR (gst_audio_buffer_split_sink_event));
138 GST_PAD_SET_PROXY_CAPS (self->sinkpad);
139 gst_element_add_pad (GST_ELEMENT (self), self->sinkpad);
140
141 self->srcpad = gst_pad_new_from_static_template (&src_template, "src");
Sebastian Dröge971a4812017-03-22 13:22:40 +0200142 gst_pad_set_query_function (self->srcpad,
Sebastian Dröge0acb3d82016-11-18 21:00:03 +0200143 GST_DEBUG_FUNCPTR (gst_audio_buffer_split_src_query));
144 GST_PAD_SET_PROXY_CAPS (self->srcpad);
145 gst_pad_use_fixed_caps (self->srcpad);
146 gst_element_add_pad (GST_ELEMENT (self), self->srcpad);
147
148 self->output_buffer_duration_n = DEFAULT_OUTPUT_BUFFER_DURATION_N;
149 self->output_buffer_duration_d = DEFAULT_OUTPUT_BUFFER_DURATION_D;
Vivia Nikolaidou668c4402017-02-20 18:58:11 +0200150 self->strict_buffer_size = DEFAULT_STRICT_BUFFER_SIZE;
Sebastian Dröge0acb3d82016-11-18 21:00:03 +0200151
152 self->adapter = gst_adapter_new ();
Sebastian Drögedd490e12017-09-12 16:41:18 +0300153
154 self->stream_align =
155 gst_audio_stream_align_new (48000, DEFAULT_ALIGNMENT_THRESHOLD,
156 DEFAULT_DISCONT_WAIT);
Sebastian Dröge0acb3d82016-11-18 21:00:03 +0200157}
158
159static void
160gst_audio_buffer_split_finalize (GObject * object)
161{
162 GstAudioBufferSplit *self = GST_AUDIO_BUFFER_SPLIT (object);
163
164 if (self->adapter) {
165 gst_object_unref (self->adapter);
166 self->adapter = NULL;
167 }
168
Sebastian Drögedd490e12017-09-12 16:41:18 +0300169 if (self->stream_align) {
170 gst_audio_stream_align_free (self->stream_align);
171 self->stream_align = NULL;
172 }
173
Sebastian Dröge0acb3d82016-11-18 21:00:03 +0200174 G_OBJECT_CLASS (parent_class)->finalize (object);
175}
176
Sebastian Dröge6026d122017-05-11 18:39:46 +0200177static gboolean
178gst_audio_buffer_split_update_samples_per_buffer (GstAudioBufferSplit * self)
179{
180 gboolean ret = TRUE;
181
182 GST_OBJECT_LOCK (self);
183
184 /* For a later time */
185 if (!self->info.finfo
186 || GST_AUDIO_INFO_FORMAT (&self->info) == GST_AUDIO_FORMAT_UNKNOWN) {
187 self->samples_per_buffer = 0;
188 goto out;
189 }
190
191 self->samples_per_buffer =
192 (((guint64) GST_AUDIO_INFO_RATE (&self->info)) *
193 self->output_buffer_duration_n) / self->output_buffer_duration_d;
194 if (self->samples_per_buffer == 0) {
195 ret = FALSE;
196 goto out;
197 }
198
199 self->error_per_buffer =
200 (((guint64) GST_AUDIO_INFO_RATE (&self->info)) *
201 self->output_buffer_duration_n) % self->output_buffer_duration_d;
202 self->accumulated_error = 0;
203
204 GST_DEBUG_OBJECT (self, "Buffer duration: %u/%u",
205 self->output_buffer_duration_n, self->output_buffer_duration_d);
206 GST_DEBUG_OBJECT (self, "Samples per buffer: %u (error: %u/%u)",
207 self->samples_per_buffer, self->error_per_buffer,
208 self->output_buffer_duration_d);
209out:
210 GST_OBJECT_UNLOCK (self);
211
212 return ret;
213}
214
Sebastian Dröge0acb3d82016-11-18 21:00:03 +0200215static void
216gst_audio_buffer_split_set_property (GObject * object, guint property_id,
217 const GValue * value, GParamSpec * pspec)
218{
219 GstAudioBufferSplit *self = GST_AUDIO_BUFFER_SPLIT (object);
220
221 switch (property_id) {
222 case PROP_OUTPUT_BUFFER_DURATION:
223 self->output_buffer_duration_n = gst_value_get_fraction_numerator (value);
224 self->output_buffer_duration_d =
225 gst_value_get_fraction_denominator (value);
Sebastian Dröge6026d122017-05-11 18:39:46 +0200226 gst_audio_buffer_split_update_samples_per_buffer (self);
Sebastian Dröge0acb3d82016-11-18 21:00:03 +0200227 break;
228 case PROP_ALIGNMENT_THRESHOLD:
Sebastian Drögedd490e12017-09-12 16:41:18 +0300229 GST_OBJECT_LOCK (self);
230 gst_audio_stream_align_set_alignment_threshold (self->stream_align,
231 g_value_get_uint64 (value));
232 GST_OBJECT_UNLOCK (self);
Sebastian Dröge0acb3d82016-11-18 21:00:03 +0200233 break;
234 case PROP_DISCONT_WAIT:
Sebastian Drögedd490e12017-09-12 16:41:18 +0300235 GST_OBJECT_LOCK (self);
236 gst_audio_stream_align_set_discont_wait (self->stream_align,
237 g_value_get_uint64 (value));
238 GST_OBJECT_UNLOCK (self);
Sebastian Dröge0acb3d82016-11-18 21:00:03 +0200239 break;
Vivia Nikolaidou668c4402017-02-20 18:58:11 +0200240 case PROP_STRICT_BUFFER_SIZE:
241 self->strict_buffer_size = g_value_get_boolean (value);
242 break;
Sebastian Dröge0acb3d82016-11-18 21:00:03 +0200243 default:
244 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
245 break;
246 }
247}
248
249static void
250gst_audio_buffer_split_get_property (GObject * object, guint property_id,
251 GValue * value, GParamSpec * pspec)
252{
253 GstAudioBufferSplit *self = GST_AUDIO_BUFFER_SPLIT (object);
254
255 switch (property_id) {
256 case PROP_OUTPUT_BUFFER_DURATION:
257 gst_value_set_fraction (value, self->output_buffer_duration_n,
258 self->output_buffer_duration_d);
259 break;
260 case PROP_ALIGNMENT_THRESHOLD:
Sebastian Drögedd490e12017-09-12 16:41:18 +0300261 GST_OBJECT_LOCK (self);
262 g_value_set_uint64 (value,
263 gst_audio_stream_align_get_alignment_threshold (self->stream_align));
264 GST_OBJECT_UNLOCK (self);
Sebastian Dröge0acb3d82016-11-18 21:00:03 +0200265 break;
266 case PROP_DISCONT_WAIT:
Sebastian Drögedd490e12017-09-12 16:41:18 +0300267 GST_OBJECT_LOCK (self);
268 g_value_set_uint64 (value,
269 gst_audio_stream_align_get_discont_wait (self->stream_align));
270 GST_OBJECT_UNLOCK (self);
Sebastian Dröge0acb3d82016-11-18 21:00:03 +0200271 break;
Vivia Nikolaidou668c4402017-02-20 18:58:11 +0200272 case PROP_STRICT_BUFFER_SIZE:
273 g_value_set_boolean (value, self->strict_buffer_size);
274 break;
Sebastian Dröge0acb3d82016-11-18 21:00:03 +0200275 default:
276 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
277 break;
278 }
279}
280
281static GstStateChangeReturn
282gst_audio_buffer_split_change_state (GstElement * element,
283 GstStateChange transition)
284{
285 GstAudioBufferSplit *self = GST_AUDIO_BUFFER_SPLIT (element);
286 GstStateChangeReturn state_ret;
287
288 switch (transition) {
289 case GST_STATE_CHANGE_READY_TO_PAUSED:
290 gst_audio_info_init (&self->info);
291 gst_segment_init (&self->segment, GST_FORMAT_TIME);
Sebastian Drögedd490e12017-09-12 16:41:18 +0300292 GST_OBJECT_LOCK (self);
293 gst_audio_stream_align_mark_discont (self->stream_align);
294 GST_OBJECT_UNLOCK (self);
Sebastian Dröge0acb3d82016-11-18 21:00:03 +0200295 self->current_offset = -1;
296 self->accumulated_error = 0;
Sebastian Dröge5cff1562016-12-23 13:27:42 +0200297 self->samples_per_buffer = 0;
Sebastian Dröge0acb3d82016-11-18 21:00:03 +0200298 break;
299 default:
300 break;
301 }
302
303 state_ret =
304 GST_ELEMENT_CLASS (gst_audio_buffer_split_parent_class)->change_state
305 (element, transition);
306 if (state_ret == GST_STATE_CHANGE_FAILURE)
307 return state_ret;
308
309 switch (transition) {
310 case GST_STATE_CHANGE_PAUSED_TO_READY:
311 gst_adapter_clear (self->adapter);
Sebastian Drögedd490e12017-09-12 16:41:18 +0300312 GST_OBJECT_LOCK (self);
313 gst_audio_stream_align_mark_discont (self->stream_align);
314 GST_OBJECT_UNLOCK (self);
Sebastian Dröge0acb3d82016-11-18 21:00:03 +0200315 break;
316 default:
317 break;
318 }
319
320 return state_ret;
321}
322
323static GstFlowReturn
Sebastian Dröge6026d122017-05-11 18:39:46 +0200324gst_audio_buffer_split_output (GstAudioBufferSplit * self, gboolean force,
325 gint rate, gint bpf, guint samples_per_buffer)
Sebastian Dröge0acb3d82016-11-18 21:00:03 +0200326{
Sebastian Dröge0acb3d82016-11-18 21:00:03 +0200327 gint size, avail;
328 GstFlowReturn ret = GST_FLOW_OK;
Sebastian Drögedd490e12017-09-12 16:41:18 +0300329 GstClockTime resync_time;
330
331 GST_OBJECT_LOCK (self);
332 resync_time =
333 gst_audio_stream_align_get_timestamp_at_discont (self->stream_align);
334 GST_OBJECT_UNLOCK (self);
Sebastian Dröge0acb3d82016-11-18 21:00:03 +0200335
Sebastian Dröge6026d122017-05-11 18:39:46 +0200336 size = samples_per_buffer * bpf;
Sebastian Dröge0acb3d82016-11-18 21:00:03 +0200337
338 /* If we accumulated enough error for one sample, include one
339 * more sample in this buffer. Accumulated error is updated below */
340 if (self->error_per_buffer + self->accumulated_error >=
341 self->output_buffer_duration_d)
342 size += bpf;
343
344 while ((avail = gst_adapter_available (self->adapter)) >= size || (force
345 && avail > 0)) {
346 GstBuffer *buffer;
347 GstClockTime resync_time_diff;
348
349 size = MIN (size, avail);
350 buffer = gst_adapter_take_buffer (self->adapter, size);
351
352 resync_time_diff =
353 gst_util_uint64_scale (self->current_offset, GST_SECOND, rate);
354 if (self->segment.rate < 0.0) {
Sebastian Drögedd490e12017-09-12 16:41:18 +0300355 if (resync_time > resync_time_diff)
356 GST_BUFFER_TIMESTAMP (buffer) = resync_time - resync_time_diff;
Sebastian Dröge0acb3d82016-11-18 21:00:03 +0200357 else
358 GST_BUFFER_TIMESTAMP (buffer) = 0;
359 GST_BUFFER_DURATION (buffer) =
360 gst_util_uint64_scale (size / bpf, GST_SECOND, rate);
361
362 self->current_offset += size / bpf;
363 } else {
Sebastian Drögedd490e12017-09-12 16:41:18 +0300364 GST_BUFFER_TIMESTAMP (buffer) = resync_time + resync_time_diff;
Sebastian Dröge0acb3d82016-11-18 21:00:03 +0200365 self->current_offset += size / bpf;
366 resync_time_diff =
367 gst_util_uint64_scale (self->current_offset, GST_SECOND, rate);
368 GST_BUFFER_DURATION (buffer) =
Sebastian Drögedd490e12017-09-12 16:41:18 +0300369 resync_time_diff - (GST_BUFFER_TIMESTAMP (buffer) - resync_time);
Sebastian Dröge0acb3d82016-11-18 21:00:03 +0200370 }
371
372 GST_BUFFER_OFFSET (buffer) = GST_BUFFER_OFFSET_NONE;
373 GST_BUFFER_OFFSET_END (buffer) = GST_BUFFER_OFFSET_NONE;
374
375 self->accumulated_error =
376 (self->accumulated_error +
377 self->error_per_buffer) % self->output_buffer_duration_d;
378
379 GST_LOG_OBJECT (self,
380 "Outputting buffer at timestamp %" GST_TIME_FORMAT " with duration %"
381 GST_TIME_FORMAT " (%u samples)",
382 GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buffer)),
383 GST_TIME_ARGS (GST_BUFFER_DURATION (buffer)), size / bpf);
384
385 ret = gst_pad_push (self->srcpad, buffer);
386 if (ret != GST_FLOW_OK)
387 break;
388 }
389
390 return ret;
391}
392
393static GstFlowReturn
394gst_audio_buffer_split_handle_discont (GstAudioBufferSplit * self,
Sebastian Dröge6026d122017-05-11 18:39:46 +0200395 GstBuffer * buffer, gint rate, gint bpf, guint samples_per_buffer)
Sebastian Dröge0acb3d82016-11-18 21:00:03 +0200396{
Sebastian Drögedd490e12017-09-12 16:41:18 +0300397 gboolean discont;
Sebastian Dröge0acb3d82016-11-18 21:00:03 +0200398 GstFlowReturn ret = GST_FLOW_OK;
399
Sebastian Drögedd490e12017-09-12 16:41:18 +0300400 GST_OBJECT_LOCK (self);
401 discont =
402 gst_audio_stream_align_process (self->stream_align,
403 self->segment.rate < 0 ? FALSE : GST_BUFFER_IS_DISCONT (buffer)
404 || GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_FLAG_RESYNC),
405 GST_BUFFER_PTS (buffer), gst_buffer_get_size (buffer) / bpf, NULL, NULL,
406 NULL);
407 GST_OBJECT_UNLOCK (self);
Sebastian Dröge0acb3d82016-11-18 21:00:03 +0200408
409 if (discont) {
Sebastian Drögedd490e12017-09-12 16:41:18 +0300410 if (self->strict_buffer_size) {
411 gst_adapter_clear (self->adapter);
412 ret = GST_FLOW_OK;
413 } else {
414 ret =
415 gst_audio_buffer_split_output (self, TRUE, rate, bpf,
416 samples_per_buffer);
Sebastian Dröge0acb3d82016-11-18 21:00:03 +0200417 }
Sebastian Drögedd490e12017-09-12 16:41:18 +0300418
Sebastian Dröge0acb3d82016-11-18 21:00:03 +0200419 self->current_offset = 0;
420 self->accumulated_error = 0;
Sebastian Dröge0acb3d82016-11-18 21:00:03 +0200421 }
422
423 return ret;
424}
425
426static GstBuffer *
427gst_audio_buffer_split_clip_buffer (GstAudioBufferSplit * self,
Sebastian Dröge6026d122017-05-11 18:39:46 +0200428 GstBuffer * buffer, const GstSegment * segment, gint rate, gint bpf)
Sebastian Dröge0acb3d82016-11-18 21:00:03 +0200429{
Sebastian Dröge6026d122017-05-11 18:39:46 +0200430 return gst_audio_buffer_clip (buffer, segment, rate, bpf);
Sebastian Dröge0acb3d82016-11-18 21:00:03 +0200431}
432
433static GstFlowReturn
434gst_audio_buffer_split_sink_chain (GstPad * pad, GstObject * parent,
435 GstBuffer * buffer)
436{
437 GstAudioBufferSplit *self = GST_AUDIO_BUFFER_SPLIT (parent);
438 GstFlowReturn ret;
Sebastian Dröge6026d122017-05-11 18:39:46 +0200439 GstAudioFormat format;
440 gint rate, bpf, samples_per_buffer;
Sebastian Dröge0acb3d82016-11-18 21:00:03 +0200441
Sebastian Dröge6026d122017-05-11 18:39:46 +0200442 GST_OBJECT_LOCK (self);
443 format =
444 self->info.
445 finfo ? GST_AUDIO_INFO_FORMAT (&self->info) : GST_AUDIO_FORMAT_UNKNOWN;
446 rate = GST_AUDIO_INFO_RATE (&self->info);
447 bpf = GST_AUDIO_INFO_BPF (&self->info);
448 samples_per_buffer = self->samples_per_buffer;
449 GST_OBJECT_UNLOCK (self);
450
451 if (format == GST_AUDIO_FORMAT_UNKNOWN || samples_per_buffer == 0) {
Sebastian Dröge0acb3d82016-11-18 21:00:03 +0200452 gst_buffer_unref (buffer);
453 return GST_FLOW_NOT_NEGOTIATED;
454 }
455
Sebastian Dröge6026d122017-05-11 18:39:46 +0200456 buffer =
457 gst_audio_buffer_split_clip_buffer (self, buffer, &self->segment, rate,
458 bpf);
Sebastian Dröge0acb3d82016-11-18 21:00:03 +0200459 if (!buffer)
460 return GST_FLOW_OK;
461
Sebastian Dröge6026d122017-05-11 18:39:46 +0200462 ret =
463 gst_audio_buffer_split_handle_discont (self, buffer, rate, bpf,
464 samples_per_buffer);
Sebastian Dröge0acb3d82016-11-18 21:00:03 +0200465 if (ret != GST_FLOW_OK) {
466 gst_buffer_unref (buffer);
467 return ret;
468 }
469
470 gst_adapter_push (self->adapter, buffer);
471
Sebastian Dröge6026d122017-05-11 18:39:46 +0200472 return gst_audio_buffer_split_output (self, FALSE, rate, bpf,
473 samples_per_buffer);
Sebastian Dröge0acb3d82016-11-18 21:00:03 +0200474}
475
476static gboolean
477gst_audio_buffer_split_sink_event (GstPad * pad, GstObject * parent,
478 GstEvent * event)
479{
480 GstAudioBufferSplit *self = GST_AUDIO_BUFFER_SPLIT (parent);
481 gboolean ret = FALSE;
482
483 switch (GST_EVENT_TYPE (event)) {
484 case GST_EVENT_CAPS:{
485 GstCaps *caps;
Sebastian Dröged01724a2017-09-12 16:43:26 +0300486 GstAudioInfo info;
Sebastian Dröge0acb3d82016-11-18 21:00:03 +0200487
488 gst_event_parse_caps (event, &caps);
489
Sebastian Dröged01724a2017-09-12 16:43:26 +0300490 ret = gst_audio_info_from_caps (&info, caps);
Sebastian Dröge0acb3d82016-11-18 21:00:03 +0200491 if (ret) {
Sebastian Dröge0acb3d82016-11-18 21:00:03 +0200492 GST_DEBUG_OBJECT (self, "Got caps %" GST_PTR_FORMAT, caps);
Sebastian Dröged01724a2017-09-12 16:43:26 +0300493
494 if (!gst_audio_info_is_equal (&info, &self->info)) {
495 if (self->strict_buffer_size) {
496 gst_adapter_clear (self->adapter);
497 } else {
498 GstAudioFormat format;
499 gint rate, bpf, samples_per_buffer;
500
501 GST_OBJECT_LOCK (self);
502 format =
503 self->info.finfo ? GST_AUDIO_INFO_FORMAT (&self->info) :
504 GST_AUDIO_FORMAT_UNKNOWN;
505 rate = GST_AUDIO_INFO_RATE (&self->info);
506 bpf = GST_AUDIO_INFO_BPF (&self->info);
507 samples_per_buffer = self->samples_per_buffer;
508 GST_OBJECT_UNLOCK (self);
509
510 if (format != GST_AUDIO_FORMAT_UNKNOWN && samples_per_buffer != 0)
511 gst_audio_buffer_split_output (self, TRUE, rate, bpf,
512 samples_per_buffer);
513 }
514 }
515 self->info = info;
516 GST_OBJECT_LOCK (self);
517 gst_audio_stream_align_set_rate (self->stream_align, self->info.rate);
518 GST_OBJECT_UNLOCK (self);
Sebastian Dröge6026d122017-05-11 18:39:46 +0200519 ret = gst_audio_buffer_split_update_samples_per_buffer (self);
Sebastian Dröge0acb3d82016-11-18 21:00:03 +0200520 } else {
521 ret = FALSE;
522 }
523
524 if (ret)
525 ret = gst_pad_event_default (pad, parent, event);
526 else
527 gst_event_unref (event);
528
529 break;
530 }
531 case GST_EVENT_FLUSH_STOP:
532 gst_segment_init (&self->segment, GST_FORMAT_TIME);
Sebastian Drögedd490e12017-09-12 16:41:18 +0300533 GST_OBJECT_LOCK (self);
534 gst_audio_stream_align_mark_discont (self->stream_align);
535 GST_OBJECT_UNLOCK (self);
Sebastian Dröge0acb3d82016-11-18 21:00:03 +0200536 self->current_offset = -1;
537 self->accumulated_error = 0;
538 gst_adapter_clear (self->adapter);
539 ret = gst_pad_event_default (pad, parent, event);
540 break;
541 case GST_EVENT_SEGMENT:
542 gst_event_copy_segment (event, &self->segment);
543 if (self->segment.format != GST_FORMAT_TIME) {
544 gst_event_unref (event);
545 ret = FALSE;
546 } else {
547 ret = gst_pad_event_default (pad, parent, event);
548 }
549 break;
550 case GST_EVENT_EOS:
Sebastian Dröge6026d122017-05-11 18:39:46 +0200551 if (self->strict_buffer_size) {
Vivia Nikolaidou668c4402017-02-20 18:58:11 +0200552 gst_adapter_clear (self->adapter);
Sebastian Dröge6026d122017-05-11 18:39:46 +0200553 } else {
554 GstAudioFormat format;
555 gint rate, bpf, samples_per_buffer;
556
557 GST_OBJECT_LOCK (self);
558 format =
559 self->info.finfo ? GST_AUDIO_INFO_FORMAT (&self->info) :
560 GST_AUDIO_FORMAT_UNKNOWN;
561 rate = GST_AUDIO_INFO_RATE (&self->info);
562 bpf = GST_AUDIO_INFO_BPF (&self->info);
563 samples_per_buffer = self->samples_per_buffer;
564 GST_OBJECT_UNLOCK (self);
565
566 if (format != GST_AUDIO_FORMAT_UNKNOWN && samples_per_buffer != 0)
567 gst_audio_buffer_split_output (self, TRUE, rate, bpf,
568 samples_per_buffer);
569 }
Sebastian Dröge0acb3d82016-11-18 21:00:03 +0200570 ret = gst_pad_event_default (pad, parent, event);
571 break;
572 default:
573 ret = gst_pad_event_default (pad, parent, event);
574 break;
575 }
576
577 return ret;
578}
579
580static gboolean
581gst_audio_buffer_split_src_query (GstPad * pad,
582 GstObject * parent, GstQuery * query)
583{
584 GstAudioBufferSplit *self = GST_AUDIO_BUFFER_SPLIT (parent);
585 gboolean ret = FALSE;
586
587 switch (GST_QUERY_TYPE (query)) {
588 case GST_QUERY_LATENCY:{
589 if ((ret = gst_pad_peer_query (self->sinkpad, query))) {
590 GstClockTime latency;
591 GstClockTime min, max;
592 gboolean live;
593
594 gst_query_parse_latency (query, &live, &min, &max);
595
596 GST_DEBUG_OBJECT (self, "Peer latency: min %"
597 GST_TIME_FORMAT " max %" GST_TIME_FORMAT,
598 GST_TIME_ARGS (min), GST_TIME_ARGS (max));
599
600 latency =
601 gst_util_uint64_scale (GST_SECOND, self->output_buffer_duration_n,
602 self->output_buffer_duration_d);
603
604 GST_DEBUG_OBJECT (self, "Our latency: min %" GST_TIME_FORMAT
605 ", max %" GST_TIME_FORMAT,
606 GST_TIME_ARGS (latency), GST_TIME_ARGS (latency));
607
608 min += latency;
609 if (max != GST_CLOCK_TIME_NONE)
610 max += latency;
611
612 GST_DEBUG_OBJECT (self, "Calculated total latency : min %"
613 GST_TIME_FORMAT " max %" GST_TIME_FORMAT,
614 GST_TIME_ARGS (min), GST_TIME_ARGS (max));
615
616 gst_query_set_latency (query, live, min, max);
617 }
618
619 break;
620 }
621 default:
622 ret = gst_pad_query_default (pad, parent, query);
623 break;
624 }
625
626 return ret;
627}
628
629static gboolean
630plugin_init (GstPlugin * plugin)
631{
632 GST_DEBUG_CATEGORY_INIT (gst_audio_buffer_split_debug, "audiobuffersplit",
633 0, "Audio buffer splitter");
634
635 gst_element_register (plugin, "audiobuffersplit", GST_RANK_NONE,
636 GST_TYPE_AUDIO_BUFFER_SPLIT);
637
638 return TRUE;
639}
640
641GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
642 GST_VERSION_MINOR,
643 audiobuffersplit,
644 "Audio buffer splitter",
645 plugin_init, VERSION, "LGPL", PACKAGE_NAME, GST_PACKAGE_ORIGIN)