/src/gstreamer/subprojects/gst-plugins-base/gst-libs/gst/audio/gstaudiobasesink.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* GStreamer |
2 | | * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu> |
3 | | * 2005 Wim Taymans <wim@fluendo.com> |
4 | | * |
5 | | * gstaudiobasesink.c: |
6 | | * |
7 | | * This library is free software; you can redistribute it and/or |
8 | | * modify it under the terms of the GNU Library General Public |
9 | | * License as published by the Free Software Foundation; either |
10 | | * version 2 of the License, or (at your option) any later version. |
11 | | * |
12 | | * This library is distributed in the hope that it will be useful, |
13 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
14 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
15 | | * Library General Public License for more details. |
16 | | * |
17 | | * You should have received a copy of the GNU Library General Public |
18 | | * License along with this library; if not, write to the |
19 | | * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, |
20 | | * Boston, MA 02110-1301, USA. |
21 | | */ |
22 | | |
23 | | /** |
24 | | * SECTION:gstaudiobasesink |
25 | | * @title: GstAudioBaseSink |
26 | | * @short_description: Base class for audio sinks |
27 | | * @see_also: #GstAudioSink, #GstAudioRingBuffer. |
28 | | * |
29 | | * This is the base class for audio sinks. Subclasses need to implement the |
30 | | * ::create_ringbuffer vmethod. This base class will then take care of |
31 | | * writing samples to the ringbuffer, synchronisation, clipping and flushing. |
32 | | */ |
33 | | #ifdef HAVE_CONFIG_H |
34 | | #include "config.h" |
35 | | #endif |
36 | | |
37 | | #include <string.h> |
38 | | |
39 | | #include <gst/audio/audio.h> |
40 | | #include "gstaudiobasesink.h" |
41 | | |
42 | | GST_DEBUG_CATEGORY_STATIC (gst_audio_base_sink_debug); |
43 | | #define GST_CAT_DEFAULT gst_audio_base_sink_debug |
44 | | |
45 | | struct _GstAudioBaseSinkPrivate |
46 | | { |
47 | | /* upstream latency */ |
48 | | GstClockTime us_latency; |
49 | | /* the clock slaving algorithm in use */ |
50 | | GstAudioBaseSinkSlaveMethod slave_method; |
51 | | /* running average of clock skew */ |
52 | | GstClockTimeDiff avg_skew; |
53 | | /* the number of samples we aligned last time */ |
54 | | gint64 last_align; |
55 | | |
56 | | gboolean sync_latency; |
57 | | |
58 | | GstClockTime eos_time; |
59 | | |
60 | | /* number of microseconds we allow clock slaving to drift |
61 | | * before resyncing */ |
62 | | guint64 drift_tolerance; |
63 | | |
64 | | /* number of nanoseconds we allow timestamps to drift |
65 | | * before resyncing */ |
66 | | GstClockTime alignment_threshold; |
67 | | |
68 | | /* time of the previous detected discont candidate */ |
69 | | GstClockTime discont_time; |
70 | | |
71 | | /* number of nanoseconds to wait until creating a discontinuity */ |
72 | | GstClockTime discont_wait; |
73 | | |
74 | | /* custom slaving algorithm callback */ |
75 | | GstAudioBaseSinkCustomSlavingCallback custom_slaving_callback; |
76 | | gpointer custom_slaving_cb_data; |
77 | | GDestroyNotify custom_slaving_cb_notify; |
78 | | }; |
79 | | |
80 | | /* BaseAudioSink signals and args */ |
81 | | enum |
82 | | { |
83 | | /* FILL ME */ |
84 | | LAST_SIGNAL |
85 | | }; |
86 | | |
87 | | /* FIXME: 2.0, store the buffer_time and latency_time in nanoseconds */ |
88 | 0 | #define DEFAULT_BUFFER_TIME ((200 * GST_MSECOND) / GST_USECOND) |
89 | 0 | #define DEFAULT_LATENCY_TIME ((10 * GST_MSECOND) / GST_USECOND) |
90 | 0 | #define DEFAULT_PROVIDE_CLOCK TRUE |
91 | 0 | #define DEFAULT_SLAVE_METHOD GST_AUDIO_BASE_SINK_SLAVE_SKEW |
92 | | |
93 | | /* FIXME, enable pull mode when clock slaving and trick modes are figured out */ |
94 | 0 | #define DEFAULT_CAN_ACTIVATE_PULL FALSE |
95 | | |
96 | | /* when timestamps drift for more than 40ms we resync. This should |
97 | | * be enough to compensate for timestamp rounding errors. */ |
98 | 0 | #define DEFAULT_ALIGNMENT_THRESHOLD (40 * GST_MSECOND) |
99 | | |
100 | | /* when clock slaving drift for more than 40ms we resync. This is |
101 | | * a reasonable default */ |
102 | 0 | #define DEFAULT_DRIFT_TOLERANCE ((40 * GST_MSECOND) / GST_USECOND) |
103 | | |
104 | | /* allow for one second before resyncing to see if the timestamps drift will |
105 | | * fix itself, or is a permanent offset */ |
106 | 0 | #define DEFAULT_DISCONT_WAIT (1 * GST_SECOND) |
107 | | |
108 | | enum |
109 | | { |
110 | | PROP_0, |
111 | | |
112 | | PROP_BUFFER_TIME, |
113 | | PROP_LATENCY_TIME, |
114 | | PROP_PROVIDE_CLOCK, |
115 | | PROP_SLAVE_METHOD, |
116 | | PROP_CAN_ACTIVATE_PULL, |
117 | | PROP_ALIGNMENT_THRESHOLD, |
118 | | PROP_DRIFT_TOLERANCE, |
119 | | PROP_DISCONT_WAIT, |
120 | | |
121 | | PROP_LAST |
122 | | }; |
123 | | |
124 | | #define _do_init \ |
125 | | GST_DEBUG_CATEGORY_INIT (gst_audio_base_sink_debug, "audiobasesink", 0, "audiobasesink element"); |
126 | 0 | #define gst_audio_base_sink_parent_class parent_class |
127 | | G_DEFINE_TYPE_WITH_CODE (GstAudioBaseSink, gst_audio_base_sink, |
128 | | GST_TYPE_BASE_SINK, G_ADD_PRIVATE (GstAudioBaseSink) _do_init); |
129 | | |
130 | | static void gst_audio_base_sink_dispose (GObject * object); |
131 | | |
132 | | static void gst_audio_base_sink_set_property (GObject * object, guint prop_id, |
133 | | const GValue * value, GParamSpec * pspec); |
134 | | static void gst_audio_base_sink_get_property (GObject * object, guint prop_id, |
135 | | GValue * value, GParamSpec * pspec); |
136 | | |
137 | | static GstStateChangeReturn gst_audio_base_sink_change_state (GstElement * |
138 | | element, GstStateChange transition); |
139 | | static gboolean gst_audio_base_sink_activate_pull (GstBaseSink * basesink, |
140 | | gboolean active); |
141 | | static gboolean gst_audio_base_sink_query (GstElement * element, GstQuery * |
142 | | query); |
143 | | |
144 | | static GstClock *gst_audio_base_sink_provide_clock (GstElement * elem); |
145 | | static inline void gst_audio_base_sink_reset_sync (GstAudioBaseSink * sink); |
146 | | static GstClockTime gst_audio_base_sink_get_time (GstClock * clock, |
147 | | GstAudioBaseSink * sink); |
148 | | static void gst_audio_base_sink_callback (GstAudioRingBuffer * rbuf, |
149 | | guint8 * data, guint len, gpointer user_data); |
150 | | |
151 | | static GstFlowReturn gst_audio_base_sink_preroll (GstBaseSink * bsink, |
152 | | GstBuffer * buffer); |
153 | | static GstFlowReturn gst_audio_base_sink_render (GstBaseSink * bsink, |
154 | | GstBuffer * buffer); |
155 | | static gboolean gst_audio_base_sink_event (GstBaseSink * bsink, |
156 | | GstEvent * event); |
157 | | static GstFlowReturn gst_audio_base_sink_wait_event (GstBaseSink * bsink, |
158 | | GstEvent * event); |
159 | | static void gst_audio_base_sink_get_times (GstBaseSink * bsink, |
160 | | GstBuffer * buffer, GstClockTime * start, GstClockTime * end); |
161 | | static gboolean gst_audio_base_sink_setcaps (GstBaseSink * bsink, |
162 | | GstCaps * caps); |
163 | | static GstCaps *gst_audio_base_sink_fixate (GstBaseSink * bsink, |
164 | | GstCaps * caps); |
165 | | |
166 | | static gboolean gst_audio_base_sink_query_pad (GstBaseSink * bsink, |
167 | | GstQuery * query); |
168 | | |
169 | | |
170 | | /* static guint gst_audio_base_sink_signals[LAST_SIGNAL] = { 0 }; */ |
171 | | |
172 | | static void |
173 | | gst_audio_base_sink_class_init (GstAudioBaseSinkClass * klass) |
174 | 0 | { |
175 | 0 | GObjectClass *gobject_class; |
176 | 0 | GstElementClass *gstelement_class; |
177 | 0 | GstBaseSinkClass *gstbasesink_class; |
178 | |
|
179 | 0 | gobject_class = (GObjectClass *) klass; |
180 | 0 | gstelement_class = (GstElementClass *) klass; |
181 | 0 | gstbasesink_class = (GstBaseSinkClass *) klass; |
182 | |
|
183 | 0 | gobject_class->set_property = gst_audio_base_sink_set_property; |
184 | 0 | gobject_class->get_property = gst_audio_base_sink_get_property; |
185 | 0 | gobject_class->dispose = gst_audio_base_sink_dispose; |
186 | |
|
187 | 0 | g_object_class_install_property (gobject_class, PROP_BUFFER_TIME, |
188 | 0 | g_param_spec_int64 ("buffer-time", "Buffer Time", |
189 | 0 | "Size of audio buffer in microseconds, this is the minimum " |
190 | 0 | "latency that the sink reports", 1, G_MAXINT64, DEFAULT_BUFFER_TIME, |
191 | 0 | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); |
192 | |
|
193 | 0 | g_object_class_install_property (gobject_class, PROP_LATENCY_TIME, |
194 | 0 | g_param_spec_int64 ("latency-time", "Latency Time", |
195 | 0 | "The minimum amount of data to write in each iteration " |
196 | 0 | "in microseconds", 1, G_MAXINT64, DEFAULT_LATENCY_TIME, |
197 | 0 | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); |
198 | |
|
199 | 0 | g_object_class_install_property (gobject_class, PROP_PROVIDE_CLOCK, |
200 | 0 | g_param_spec_boolean ("provide-clock", "Provide Clock", |
201 | 0 | "Provide a clock to be used as the global pipeline clock", |
202 | 0 | DEFAULT_PROVIDE_CLOCK, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); |
203 | |
|
204 | 0 | g_object_class_install_property (gobject_class, PROP_SLAVE_METHOD, |
205 | 0 | g_param_spec_enum ("slave-method", "Slave Method", |
206 | 0 | "Algorithm used to match the rate of the masterclock", |
207 | 0 | GST_TYPE_AUDIO_BASE_SINK_SLAVE_METHOD, DEFAULT_SLAVE_METHOD, |
208 | 0 | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); |
209 | |
|
210 | 0 | g_object_class_install_property (gobject_class, PROP_CAN_ACTIVATE_PULL, |
211 | 0 | g_param_spec_boolean ("can-activate-pull", "Allow Pull Scheduling", |
212 | 0 | "Allow pull-based scheduling", DEFAULT_CAN_ACTIVATE_PULL, |
213 | 0 | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); |
214 | | /** |
215 | | * GstAudioBaseSink:drift-tolerance: |
216 | | * |
217 | | * Controls the amount of time in microseconds that clocks are allowed |
218 | | * to drift before resynchronisation happens. |
219 | | */ |
220 | 0 | g_object_class_install_property (gobject_class, PROP_DRIFT_TOLERANCE, |
221 | 0 | g_param_spec_int64 ("drift-tolerance", "Drift Tolerance", |
222 | 0 | "Tolerance for clock drift in microseconds", 1, |
223 | 0 | G_MAXINT64, DEFAULT_DRIFT_TOLERANCE, |
224 | 0 | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); |
225 | | /** |
226 | | * GstAudioBaseSink:alignment_threshold: |
227 | | * |
228 | | * Controls the amount of time in nanoseconds that timestamps are allowed |
229 | | * to drift from their ideal time before choosing not to align them. |
230 | | */ |
231 | 0 | g_object_class_install_property (gobject_class, PROP_ALIGNMENT_THRESHOLD, |
232 | 0 | g_param_spec_uint64 ("alignment-threshold", "Alignment Threshold", |
233 | 0 | "Timestamp alignment threshold in nanoseconds", 1, |
234 | 0 | G_MAXUINT64 - 1, DEFAULT_ALIGNMENT_THRESHOLD, |
235 | 0 | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); |
236 | | |
237 | | /** |
238 | | * GstAudioBaseSink:discont-wait: |
239 | | * |
240 | | * A window of time in nanoseconds to wait before creating a discontinuity as |
241 | | * a result of breaching the drift-tolerance. |
242 | | */ |
243 | 0 | g_object_class_install_property (gobject_class, PROP_DISCONT_WAIT, |
244 | 0 | g_param_spec_uint64 ("discont-wait", "Discont Wait", |
245 | 0 | "Window of time in nanoseconds to wait before " |
246 | 0 | "creating a discontinuity", 0, |
247 | 0 | G_MAXUINT64 - 1, DEFAULT_DISCONT_WAIT, |
248 | 0 | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); |
249 | |
|
250 | 0 | gstelement_class->change_state = |
251 | 0 | GST_DEBUG_FUNCPTR (gst_audio_base_sink_change_state); |
252 | 0 | gstelement_class->provide_clock = |
253 | 0 | GST_DEBUG_FUNCPTR (gst_audio_base_sink_provide_clock); |
254 | 0 | gstelement_class->query = GST_DEBUG_FUNCPTR (gst_audio_base_sink_query); |
255 | |
|
256 | 0 | gstbasesink_class->fixate = GST_DEBUG_FUNCPTR (gst_audio_base_sink_fixate); |
257 | 0 | gstbasesink_class->set_caps = GST_DEBUG_FUNCPTR (gst_audio_base_sink_setcaps); |
258 | 0 | gstbasesink_class->event = GST_DEBUG_FUNCPTR (gst_audio_base_sink_event); |
259 | 0 | gstbasesink_class->wait_event = |
260 | 0 | GST_DEBUG_FUNCPTR (gst_audio_base_sink_wait_event); |
261 | 0 | gstbasesink_class->get_times = |
262 | 0 | GST_DEBUG_FUNCPTR (gst_audio_base_sink_get_times); |
263 | 0 | gstbasesink_class->preroll = GST_DEBUG_FUNCPTR (gst_audio_base_sink_preroll); |
264 | 0 | gstbasesink_class->render = GST_DEBUG_FUNCPTR (gst_audio_base_sink_render); |
265 | 0 | gstbasesink_class->query = GST_DEBUG_FUNCPTR (gst_audio_base_sink_query_pad); |
266 | 0 | gstbasesink_class->activate_pull = |
267 | 0 | GST_DEBUG_FUNCPTR (gst_audio_base_sink_activate_pull); |
268 | | |
269 | | /* ref class from a thread-safe context to work around missing bit of |
270 | | * thread-safety in GObject */ |
271 | 0 | g_type_class_ref (GST_TYPE_AUDIO_CLOCK); |
272 | 0 | g_type_class_ref (GST_TYPE_AUDIO_RING_BUFFER); |
273 | |
|
274 | 0 | } |
275 | | |
276 | | static void |
277 | | gst_audio_base_sink_init (GstAudioBaseSink * audiobasesink) |
278 | 0 | { |
279 | 0 | GstBaseSink *basesink = GST_BASE_SINK_CAST (audiobasesink); |
280 | |
|
281 | 0 | audiobasesink->priv = |
282 | 0 | gst_audio_base_sink_get_instance_private (audiobasesink); |
283 | |
|
284 | 0 | audiobasesink->buffer_time = DEFAULT_BUFFER_TIME; |
285 | 0 | audiobasesink->latency_time = DEFAULT_LATENCY_TIME; |
286 | 0 | audiobasesink->priv->slave_method = DEFAULT_SLAVE_METHOD; |
287 | 0 | audiobasesink->priv->drift_tolerance = DEFAULT_DRIFT_TOLERANCE; |
288 | 0 | audiobasesink->priv->alignment_threshold = DEFAULT_ALIGNMENT_THRESHOLD; |
289 | 0 | audiobasesink->priv->discont_wait = DEFAULT_DISCONT_WAIT; |
290 | 0 | audiobasesink->priv->custom_slaving_callback = NULL; |
291 | 0 | audiobasesink->priv->custom_slaving_cb_data = NULL; |
292 | 0 | audiobasesink->priv->custom_slaving_cb_notify = NULL; |
293 | |
|
294 | 0 | audiobasesink->provided_clock = gst_audio_clock_new ("GstAudioSinkClock", |
295 | 0 | (GstAudioClockGetTimeFunc) gst_audio_base_sink_get_time, audiobasesink, |
296 | 0 | NULL); |
297 | |
|
298 | 0 | basesink->can_activate_push = TRUE; |
299 | 0 | basesink->can_activate_pull = DEFAULT_CAN_ACTIVATE_PULL; |
300 | |
|
301 | 0 | gst_base_sink_set_last_sample_enabled (basesink, FALSE); |
302 | 0 | if (DEFAULT_PROVIDE_CLOCK) |
303 | 0 | GST_OBJECT_FLAG_SET (basesink, GST_ELEMENT_FLAG_PROVIDE_CLOCK); |
304 | 0 | else |
305 | 0 | GST_OBJECT_FLAG_UNSET (basesink, GST_ELEMENT_FLAG_PROVIDE_CLOCK); |
306 | 0 | } |
307 | | |
308 | | static void |
309 | | gst_audio_base_sink_dispose (GObject * object) |
310 | 0 | { |
311 | 0 | GstAudioBaseSink *sink; |
312 | |
|
313 | 0 | sink = GST_AUDIO_BASE_SINK (object); |
314 | |
|
315 | 0 | if (sink->priv->custom_slaving_cb_notify) |
316 | 0 | sink->priv->custom_slaving_cb_notify (sink->priv->custom_slaving_cb_data); |
317 | |
|
318 | 0 | if (sink->provided_clock) { |
319 | 0 | gst_audio_clock_invalidate (GST_AUDIO_CLOCK (sink->provided_clock)); |
320 | 0 | gst_object_unref (sink->provided_clock); |
321 | 0 | sink->provided_clock = NULL; |
322 | 0 | } |
323 | |
|
324 | 0 | if (sink->ringbuffer) { |
325 | 0 | gst_object_unparent (GST_OBJECT_CAST (sink->ringbuffer)); |
326 | 0 | sink->ringbuffer = NULL; |
327 | 0 | } |
328 | |
|
329 | 0 | G_OBJECT_CLASS (parent_class)->dispose (object); |
330 | 0 | } |
331 | | |
332 | | |
333 | | static GstClock * |
334 | | gst_audio_base_sink_provide_clock (GstElement * elem) |
335 | 0 | { |
336 | 0 | GstAudioBaseSink *sink; |
337 | 0 | GstClock *clock; |
338 | |
|
339 | 0 | sink = GST_AUDIO_BASE_SINK (elem); |
340 | | |
341 | | /* we have no ringbuffer (must be NULL state) */ |
342 | 0 | if (sink->ringbuffer == NULL) |
343 | 0 | goto wrong_state; |
344 | | |
345 | 0 | if (!gst_audio_ring_buffer_is_acquired (sink->ringbuffer)) |
346 | 0 | goto wrong_state; |
347 | | |
348 | 0 | GST_OBJECT_LOCK (sink); |
349 | 0 | if (!GST_OBJECT_FLAG_IS_SET (sink, GST_ELEMENT_FLAG_PROVIDE_CLOCK)) |
350 | 0 | goto clock_disabled; |
351 | | |
352 | 0 | clock = GST_CLOCK_CAST (gst_object_ref (sink->provided_clock)); |
353 | 0 | GST_OBJECT_UNLOCK (sink); |
354 | |
|
355 | 0 | return clock; |
356 | | |
357 | | /* ERRORS */ |
358 | 0 | wrong_state: |
359 | 0 | { |
360 | 0 | GST_DEBUG_OBJECT (sink, "ringbuffer not acquired"); |
361 | 0 | return NULL; |
362 | 0 | } |
363 | 0 | clock_disabled: |
364 | 0 | { |
365 | 0 | GST_DEBUG_OBJECT (sink, "clock provide disabled"); |
366 | 0 | GST_OBJECT_UNLOCK (sink); |
367 | 0 | return NULL; |
368 | 0 | } |
369 | 0 | } |
370 | | |
371 | | static gboolean |
372 | | gst_audio_base_sink_is_self_provided_clock (GstAudioBaseSink * sink) |
373 | 0 | { |
374 | 0 | return (sink->provided_clock && GST_IS_AUDIO_CLOCK (sink->provided_clock) && |
375 | 0 | GST_AUDIO_CLOCK_CAST (sink->provided_clock)->func == |
376 | 0 | (GstAudioClockGetTimeFunc) gst_audio_base_sink_get_time); |
377 | 0 | } |
378 | | |
379 | | static gboolean |
380 | | gst_audio_base_sink_query_pad (GstBaseSink * bsink, GstQuery * query) |
381 | 0 | { |
382 | 0 | gboolean res = FALSE; |
383 | 0 | GstAudioBaseSink *basesink; |
384 | |
|
385 | 0 | basesink = GST_AUDIO_BASE_SINK (bsink); |
386 | |
|
387 | 0 | switch (GST_QUERY_TYPE (query)) { |
388 | 0 | case GST_QUERY_CONVERT: |
389 | 0 | { |
390 | 0 | GstFormat src_fmt, dest_fmt; |
391 | 0 | gint64 src_val, dest_val; |
392 | |
|
393 | 0 | GST_LOG_OBJECT (basesink, "query convert"); |
394 | |
|
395 | 0 | if (basesink->ringbuffer) { |
396 | 0 | gst_query_parse_convert (query, &src_fmt, &src_val, &dest_fmt, NULL); |
397 | 0 | res = |
398 | 0 | gst_audio_ring_buffer_convert (basesink->ringbuffer, src_fmt, |
399 | 0 | src_val, dest_fmt, &dest_val); |
400 | 0 | if (res) { |
401 | 0 | gst_query_set_convert (query, src_fmt, src_val, dest_fmt, dest_val); |
402 | 0 | } |
403 | 0 | } |
404 | 0 | break; |
405 | 0 | } |
406 | 0 | default: |
407 | 0 | res = GST_BASE_SINK_CLASS (parent_class)->query (bsink, query); |
408 | 0 | break; |
409 | 0 | } |
410 | 0 | return res; |
411 | 0 | } |
412 | | |
413 | | static gboolean |
414 | | gst_audio_base_sink_query (GstElement * element, GstQuery * query) |
415 | 0 | { |
416 | 0 | gboolean res = FALSE; |
417 | 0 | GstAudioBaseSink *basesink; |
418 | |
|
419 | 0 | basesink = GST_AUDIO_BASE_SINK (element); |
420 | |
|
421 | 0 | switch (GST_QUERY_TYPE (query)) { |
422 | 0 | case GST_QUERY_LATENCY: |
423 | 0 | { |
424 | 0 | gboolean live, us_live; |
425 | 0 | GstClockTime min_l, max_l; |
426 | |
|
427 | 0 | GST_DEBUG_OBJECT (basesink, "latency query"); |
428 | | |
429 | | /* ask parent first, it will do an upstream query for us. */ |
430 | 0 | if ((res = |
431 | 0 | gst_base_sink_query_latency (GST_BASE_SINK_CAST (basesink), &live, |
432 | 0 | &us_live, &min_l, &max_l))) { |
433 | 0 | GstClockTime base_latency, min_latency, max_latency; |
434 | | |
435 | | /* we and upstream are both live, adjust the min_latency */ |
436 | 0 | if (live && us_live) { |
437 | 0 | GstAudioRingBufferSpec *spec; |
438 | |
|
439 | 0 | GST_OBJECT_LOCK (basesink); |
440 | 0 | if (!basesink->ringbuffer || !basesink->ringbuffer->spec.info.rate) { |
441 | 0 | GST_OBJECT_UNLOCK (basesink); |
442 | |
|
443 | 0 | GST_DEBUG_OBJECT (basesink, |
444 | 0 | "we are not negotiated, can't report latency yet"); |
445 | 0 | res = FALSE; |
446 | 0 | goto done; |
447 | 0 | } |
448 | 0 | spec = &basesink->ringbuffer->spec; |
449 | |
|
450 | 0 | basesink->priv->us_latency = min_l; |
451 | |
|
452 | 0 | base_latency = |
453 | 0 | gst_util_uint64_scale_int (spec->seglatency * spec->segsize, |
454 | 0 | GST_SECOND, spec->info.rate * spec->info.bpf); |
455 | 0 | GST_OBJECT_UNLOCK (basesink); |
456 | | |
457 | | /* we cannot go lower than the buffer size and the min peer latency */ |
458 | 0 | min_latency = base_latency + min_l; |
459 | | /* the max latency is the max of the peer, we can delay an infinite |
460 | | * amount of time. */ |
461 | 0 | max_latency = (max_l == -1) ? -1 : (base_latency + max_l); |
462 | |
|
463 | 0 | GST_DEBUG_OBJECT (basesink, |
464 | 0 | "peer min %" GST_TIME_FORMAT ", our min latency: %" |
465 | 0 | GST_TIME_FORMAT, GST_TIME_ARGS (min_l), |
466 | 0 | GST_TIME_ARGS (min_latency)); |
467 | 0 | GST_DEBUG_OBJECT (basesink, |
468 | 0 | "peer max %" GST_TIME_FORMAT ", our max latency: %" |
469 | 0 | GST_TIME_FORMAT, GST_TIME_ARGS (max_l), |
470 | 0 | GST_TIME_ARGS (max_latency)); |
471 | 0 | } else { |
472 | 0 | GST_DEBUG_OBJECT (basesink, |
473 | 0 | "peer or we are not live, don't care about latency"); |
474 | 0 | min_latency = min_l; |
475 | 0 | max_latency = max_l; |
476 | 0 | } |
477 | 0 | gst_query_set_latency (query, live, min_latency, max_latency); |
478 | 0 | } |
479 | 0 | break; |
480 | 0 | } |
481 | 0 | case GST_QUERY_CONVERT: |
482 | 0 | { |
483 | 0 | GstFormat src_fmt, dest_fmt; |
484 | 0 | gint64 src_val, dest_val; |
485 | |
|
486 | 0 | GST_LOG_OBJECT (basesink, "query convert"); |
487 | |
|
488 | 0 | if (basesink->ringbuffer) { |
489 | 0 | gst_query_parse_convert (query, &src_fmt, &src_val, &dest_fmt, NULL); |
490 | 0 | res = |
491 | 0 | gst_audio_ring_buffer_convert (basesink->ringbuffer, src_fmt, |
492 | 0 | src_val, dest_fmt, &dest_val); |
493 | 0 | if (res) { |
494 | 0 | gst_query_set_convert (query, src_fmt, src_val, dest_fmt, dest_val); |
495 | 0 | } |
496 | 0 | } |
497 | 0 | break; |
498 | 0 | } |
499 | 0 | default: |
500 | 0 | res = GST_ELEMENT_CLASS (parent_class)->query (element, query); |
501 | 0 | break; |
502 | 0 | } |
503 | | |
504 | 0 | done: |
505 | 0 | return res; |
506 | 0 | } |
507 | | |
508 | | |
509 | | /* we call this function without holding the lock on sink for performance |
510 | | * reasons. Try hard to not deal with and invalid ringbuffer and rate. */ |
511 | | static GstClockTime |
512 | | gst_audio_base_sink_get_time (GstClock * clock, GstAudioBaseSink * sink) |
513 | 0 | { |
514 | 0 | guint64 raw, samples; |
515 | 0 | guint delay; |
516 | 0 | GstClockTime result; |
517 | 0 | GstAudioRingBuffer *ringbuffer; |
518 | 0 | gint rate; |
519 | |
|
520 | 0 | if ((ringbuffer = sink->ringbuffer) == NULL) |
521 | 0 | return GST_CLOCK_TIME_NONE; |
522 | | |
523 | 0 | if ((rate = ringbuffer->spec.info.rate) == 0) |
524 | 0 | return GST_CLOCK_TIME_NONE; |
525 | | |
526 | | /* our processed samples are always increasing */ |
527 | 0 | raw = samples = gst_audio_ring_buffer_samples_done (ringbuffer); |
528 | | |
529 | | /* the number of samples not yet processed, this is still queued in the |
530 | | * device (not played for playback). */ |
531 | 0 | delay = gst_audio_ring_buffer_delay (ringbuffer); |
532 | |
|
533 | 0 | if (G_LIKELY (samples >= delay)) |
534 | 0 | samples -= delay; |
535 | 0 | else |
536 | 0 | samples = 0; |
537 | |
|
538 | 0 | result = gst_util_uint64_scale_int (samples, GST_SECOND, rate); |
539 | |
|
540 | 0 | GST_DEBUG_OBJECT (sink, |
541 | 0 | "processed samples: raw %" G_GUINT64_FORMAT ", delay %u, real %" |
542 | 0 | G_GUINT64_FORMAT ", time %" GST_TIME_FORMAT, |
543 | 0 | raw, delay, samples, GST_TIME_ARGS (result)); |
544 | |
|
545 | 0 | return result; |
546 | 0 | } |
547 | | |
548 | | /** |
549 | | * gst_audio_base_sink_set_provide_clock: |
550 | | * @sink: a #GstAudioBaseSink |
551 | | * @provide: new state |
552 | | * |
553 | | * Controls whether @sink will provide a clock or not. If @provide is %TRUE, |
554 | | * gst_element_provide_clock() will return a clock that reflects the datarate |
555 | | * of @sink. If @provide is %FALSE, gst_element_provide_clock() will return |
556 | | * NULL. |
557 | | */ |
558 | | void |
559 | | gst_audio_base_sink_set_provide_clock (GstAudioBaseSink * sink, |
560 | | gboolean provide) |
561 | 0 | { |
562 | 0 | g_return_if_fail (GST_IS_AUDIO_BASE_SINK (sink)); |
563 | | |
564 | 0 | GST_OBJECT_LOCK (sink); |
565 | 0 | if (provide) |
566 | 0 | GST_OBJECT_FLAG_SET (sink, GST_ELEMENT_FLAG_PROVIDE_CLOCK); |
567 | 0 | else |
568 | 0 | GST_OBJECT_FLAG_UNSET (sink, GST_ELEMENT_FLAG_PROVIDE_CLOCK); |
569 | 0 | GST_OBJECT_UNLOCK (sink); |
570 | 0 | } |
571 | | |
572 | | /** |
573 | | * gst_audio_base_sink_get_provide_clock: |
574 | | * @sink: a #GstAudioBaseSink |
575 | | * |
576 | | * Queries whether @sink will provide a clock or not. See also |
577 | | * gst_audio_base_sink_set_provide_clock. |
578 | | * |
579 | | * Returns: %TRUE if @sink will provide a clock. |
580 | | */ |
581 | | gboolean |
582 | | gst_audio_base_sink_get_provide_clock (GstAudioBaseSink * sink) |
583 | 0 | { |
584 | 0 | gboolean result; |
585 | |
|
586 | 0 | g_return_val_if_fail (GST_IS_AUDIO_BASE_SINK (sink), FALSE); |
587 | | |
588 | 0 | GST_OBJECT_LOCK (sink); |
589 | 0 | result = GST_OBJECT_FLAG_IS_SET (sink, GST_ELEMENT_FLAG_PROVIDE_CLOCK); |
590 | 0 | GST_OBJECT_UNLOCK (sink); |
591 | |
|
592 | 0 | return result; |
593 | 0 | } |
594 | | |
595 | | /** |
596 | | * gst_audio_base_sink_set_slave_method: |
597 | | * @sink: a #GstAudioBaseSink |
598 | | * @method: the new slave method |
599 | | * |
600 | | * Controls how clock slaving will be performed in @sink. |
601 | | */ |
602 | | void |
603 | | gst_audio_base_sink_set_slave_method (GstAudioBaseSink * sink, |
604 | | GstAudioBaseSinkSlaveMethod method) |
605 | 0 | { |
606 | 0 | g_return_if_fail (GST_IS_AUDIO_BASE_SINK (sink)); |
607 | | |
608 | 0 | GST_OBJECT_LOCK (sink); |
609 | 0 | sink->priv->slave_method = method; |
610 | 0 | GST_OBJECT_UNLOCK (sink); |
611 | 0 | } |
612 | | |
613 | | /** |
614 | | * gst_audio_base_sink_get_slave_method: |
615 | | * @sink: a #GstAudioBaseSink |
616 | | * |
617 | | * Get the current slave method used by @sink. |
618 | | * |
619 | | * Returns: The current slave method used by @sink. |
620 | | */ |
621 | | GstAudioBaseSinkSlaveMethod |
622 | | gst_audio_base_sink_get_slave_method (GstAudioBaseSink * sink) |
623 | 0 | { |
624 | 0 | GstAudioBaseSinkSlaveMethod result; |
625 | |
|
626 | 0 | g_return_val_if_fail (GST_IS_AUDIO_BASE_SINK (sink), -1); |
627 | | |
628 | 0 | GST_OBJECT_LOCK (sink); |
629 | 0 | result = sink->priv->slave_method; |
630 | 0 | GST_OBJECT_UNLOCK (sink); |
631 | |
|
632 | 0 | return result; |
633 | 0 | } |
634 | | |
635 | | |
636 | | /** |
637 | | * gst_audio_base_sink_set_drift_tolerance: |
638 | | * @sink: a #GstAudioBaseSink |
639 | | * @drift_tolerance: the new drift tolerance in microseconds |
640 | | * |
641 | | * Controls the sink's drift tolerance. |
642 | | */ |
643 | | void |
644 | | gst_audio_base_sink_set_drift_tolerance (GstAudioBaseSink * sink, |
645 | | gint64 drift_tolerance) |
646 | 0 | { |
647 | 0 | g_return_if_fail (GST_IS_AUDIO_BASE_SINK (sink)); |
648 | | |
649 | 0 | GST_OBJECT_LOCK (sink); |
650 | 0 | sink->priv->drift_tolerance = drift_tolerance; |
651 | 0 | GST_OBJECT_UNLOCK (sink); |
652 | 0 | } |
653 | | |
654 | | /** |
655 | | * gst_audio_base_sink_get_drift_tolerance: |
656 | | * @sink: a #GstAudioBaseSink |
657 | | * |
658 | | * Get the current drift tolerance, in microseconds, used by @sink. |
659 | | * |
660 | | * Returns: The current drift tolerance used by @sink. |
661 | | */ |
662 | | gint64 |
663 | | gst_audio_base_sink_get_drift_tolerance (GstAudioBaseSink * sink) |
664 | 0 | { |
665 | 0 | gint64 result; |
666 | |
|
667 | 0 | g_return_val_if_fail (GST_IS_AUDIO_BASE_SINK (sink), -1); |
668 | | |
669 | 0 | GST_OBJECT_LOCK (sink); |
670 | 0 | result = sink->priv->drift_tolerance; |
671 | 0 | GST_OBJECT_UNLOCK (sink); |
672 | |
|
673 | 0 | return result; |
674 | 0 | } |
675 | | |
676 | | /** |
677 | | * gst_audio_base_sink_set_alignment_threshold: |
678 | | * @sink: a #GstAudioBaseSink |
679 | | * @alignment_threshold: the new alignment threshold in nanoseconds |
680 | | * |
681 | | * Controls the sink's alignment threshold. |
682 | | */ |
683 | | void |
684 | | gst_audio_base_sink_set_alignment_threshold (GstAudioBaseSink * sink, |
685 | | GstClockTime alignment_threshold) |
686 | 0 | { |
687 | 0 | g_return_if_fail (GST_IS_AUDIO_BASE_SINK (sink)); |
688 | 0 | g_return_if_fail (GST_CLOCK_TIME_IS_VALID (alignment_threshold)); |
689 | | |
690 | 0 | GST_OBJECT_LOCK (sink); |
691 | 0 | sink->priv->alignment_threshold = alignment_threshold; |
692 | 0 | GST_OBJECT_UNLOCK (sink); |
693 | 0 | } |
694 | | |
695 | | /** |
696 | | * gst_audio_base_sink_get_alignment_threshold: |
697 | | * @sink: a #GstAudioBaseSink |
698 | | * |
699 | | * Get the current alignment threshold, in nanoseconds, used by @sink. |
700 | | * |
701 | | * Returns: The current alignment threshold used by @sink. |
702 | | */ |
703 | | GstClockTime |
704 | | gst_audio_base_sink_get_alignment_threshold (GstAudioBaseSink * sink) |
705 | 0 | { |
706 | 0 | GstClockTime result; |
707 | |
|
708 | 0 | g_return_val_if_fail (GST_IS_AUDIO_BASE_SINK (sink), GST_CLOCK_TIME_NONE); |
709 | | |
710 | 0 | GST_OBJECT_LOCK (sink); |
711 | 0 | result = sink->priv->alignment_threshold; |
712 | 0 | GST_OBJECT_UNLOCK (sink); |
713 | |
|
714 | 0 | return result; |
715 | 0 | } |
716 | | |
717 | | /** |
718 | | * gst_audio_base_sink_set_discont_wait: |
719 | | * @sink: a #GstAudioBaseSink |
720 | | * @discont_wait: the new discont wait in nanoseconds |
721 | | * |
722 | | * Controls how long the sink will wait before creating a discontinuity. |
723 | | */ |
724 | | void |
725 | | gst_audio_base_sink_set_discont_wait (GstAudioBaseSink * sink, |
726 | | GstClockTime discont_wait) |
727 | 0 | { |
728 | 0 | g_return_if_fail (GST_IS_AUDIO_BASE_SINK (sink)); |
729 | 0 | g_return_if_fail (GST_CLOCK_TIME_IS_VALID (discont_wait)); |
730 | | |
731 | 0 | GST_OBJECT_LOCK (sink); |
732 | 0 | sink->priv->discont_wait = discont_wait; |
733 | 0 | GST_OBJECT_UNLOCK (sink); |
734 | 0 | } |
735 | | |
736 | | /** |
737 | | * gst_audio_base_sink_set_custom_slaving_callback: |
738 | | * @sink: a #GstAudioBaseSink |
739 | | * @callback: a #GstAudioBaseSinkCustomSlavingCallback |
740 | | * @user_data: user data passed to the callback |
741 | | * @notify : called when user_data becomes unused |
742 | | * |
743 | | * Sets the custom slaving callback. This callback will |
744 | | * be invoked if the slave-method property is set to |
745 | | * GST_AUDIO_BASE_SINK_SLAVE_CUSTOM and the audio sink |
746 | | * receives and plays samples. |
747 | | * |
748 | | * Setting the callback to NULL causes the sink to |
749 | | * behave as if the GST_AUDIO_BASE_SINK_SLAVE_NONE |
750 | | * method were used. |
751 | | * |
752 | | * Since: 1.6 |
753 | | */ |
754 | | void |
755 | | gst_audio_base_sink_set_custom_slaving_callback (GstAudioBaseSink * sink, |
756 | | GstAudioBaseSinkCustomSlavingCallback callback, |
757 | | gpointer user_data, GDestroyNotify notify) |
758 | 0 | { |
759 | 0 | g_return_if_fail (GST_IS_AUDIO_BASE_SINK (sink)); |
760 | | |
761 | 0 | GST_OBJECT_LOCK (sink); |
762 | 0 | sink->priv->custom_slaving_callback = callback; |
763 | 0 | sink->priv->custom_slaving_cb_data = user_data; |
764 | 0 | sink->priv->custom_slaving_cb_notify = notify; |
765 | 0 | GST_OBJECT_UNLOCK (sink); |
766 | 0 | } |
767 | | |
768 | | static void |
769 | | gst_audio_base_sink_custom_cb_report_discont (GstAudioBaseSink * sink, |
770 | | GstAudioBaseSinkDiscontReason discont_reason) |
771 | 0 | { |
772 | 0 | if ((sink->priv->custom_slaving_callback != NULL) && |
773 | 0 | (sink->priv->slave_method == GST_AUDIO_BASE_SINK_SLAVE_CUSTOM)) { |
774 | 0 | sink->priv->custom_slaving_callback (sink, GST_CLOCK_TIME_NONE, |
775 | 0 | GST_CLOCK_TIME_NONE, NULL, discont_reason, |
776 | 0 | sink->priv->custom_slaving_cb_data); |
777 | 0 | } |
778 | 0 | } |
779 | | |
780 | | /** |
781 | | * gst_audio_base_sink_report_device_failure: |
782 | | * @sink: a #GstAudioBaseSink |
783 | | * |
784 | | * Informs this base class that the audio output device has failed for |
785 | | * some reason, causing a discontinuity (for example, because the device |
786 | | * recovered from the error, but lost all contents of its ring buffer). |
787 | | * This function is typically called by derived classes, and is useful |
788 | | * for the custom slave method. |
789 | | * |
790 | | * Since: 1.6 |
791 | | */ |
792 | | void |
793 | | gst_audio_base_sink_report_device_failure (GstAudioBaseSink * sink) |
794 | 0 | { |
795 | 0 | g_return_if_fail (GST_IS_AUDIO_BASE_SINK (sink)); |
796 | | |
797 | 0 | GST_OBJECT_LOCK (sink); |
798 | 0 | gst_audio_base_sink_custom_cb_report_discont (sink, |
799 | 0 | GST_AUDIO_BASE_SINK_DISCONT_REASON_DEVICE_FAILURE); |
800 | 0 | GST_OBJECT_UNLOCK (sink); |
801 | 0 | } |
802 | | |
803 | | /** |
804 | | * gst_audio_base_sink_get_discont_wait: |
805 | | * @sink: a #GstAudioBaseSink |
806 | | * |
807 | | * Get the current discont wait, in nanoseconds, used by @sink. |
808 | | * |
809 | | * Returns: The current discont wait used by @sink. |
810 | | */ |
811 | | GstClockTime |
812 | | gst_audio_base_sink_get_discont_wait (GstAudioBaseSink * sink) |
813 | 0 | { |
814 | 0 | GstClockTime result; |
815 | |
|
816 | 0 | g_return_val_if_fail (GST_IS_AUDIO_BASE_SINK (sink), -1); |
817 | | |
818 | 0 | GST_OBJECT_LOCK (sink); |
819 | 0 | result = sink->priv->discont_wait; |
820 | 0 | GST_OBJECT_UNLOCK (sink); |
821 | |
|
822 | 0 | return result; |
823 | 0 | } |
824 | | |
825 | | static void |
826 | | gst_audio_base_sink_set_property (GObject * object, guint prop_id, |
827 | | const GValue * value, GParamSpec * pspec) |
828 | 0 | { |
829 | 0 | GstAudioBaseSink *sink; |
830 | |
|
831 | 0 | sink = GST_AUDIO_BASE_SINK (object); |
832 | |
|
833 | 0 | switch (prop_id) { |
834 | 0 | case PROP_BUFFER_TIME: |
835 | 0 | sink->buffer_time = g_value_get_int64 (value); |
836 | 0 | break; |
837 | 0 | case PROP_LATENCY_TIME: |
838 | 0 | sink->latency_time = g_value_get_int64 (value); |
839 | 0 | break; |
840 | 0 | case PROP_PROVIDE_CLOCK: |
841 | 0 | gst_audio_base_sink_set_provide_clock (sink, g_value_get_boolean (value)); |
842 | 0 | break; |
843 | 0 | case PROP_SLAVE_METHOD: |
844 | 0 | gst_audio_base_sink_set_slave_method (sink, g_value_get_enum (value)); |
845 | 0 | break; |
846 | 0 | case PROP_CAN_ACTIVATE_PULL: |
847 | 0 | GST_BASE_SINK (sink)->can_activate_pull = g_value_get_boolean (value); |
848 | 0 | break; |
849 | 0 | case PROP_DRIFT_TOLERANCE: |
850 | 0 | gst_audio_base_sink_set_drift_tolerance (sink, g_value_get_int64 (value)); |
851 | 0 | break; |
852 | 0 | case PROP_ALIGNMENT_THRESHOLD: |
853 | 0 | gst_audio_base_sink_set_alignment_threshold (sink, |
854 | 0 | g_value_get_uint64 (value)); |
855 | 0 | break; |
856 | 0 | case PROP_DISCONT_WAIT: |
857 | 0 | gst_audio_base_sink_set_discont_wait (sink, g_value_get_uint64 (value)); |
858 | 0 | break; |
859 | 0 | default: |
860 | 0 | G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); |
861 | 0 | break; |
862 | 0 | } |
863 | 0 | } |
864 | | |
865 | | static void |
866 | | gst_audio_base_sink_get_property (GObject * object, guint prop_id, |
867 | | GValue * value, GParamSpec * pspec) |
868 | 0 | { |
869 | 0 | GstAudioBaseSink *sink; |
870 | |
|
871 | 0 | sink = GST_AUDIO_BASE_SINK (object); |
872 | |
|
873 | 0 | switch (prop_id) { |
874 | 0 | case PROP_BUFFER_TIME: |
875 | 0 | g_value_set_int64 (value, sink->buffer_time); |
876 | 0 | break; |
877 | 0 | case PROP_LATENCY_TIME: |
878 | 0 | g_value_set_int64 (value, sink->latency_time); |
879 | 0 | break; |
880 | 0 | case PROP_PROVIDE_CLOCK: |
881 | 0 | g_value_set_boolean (value, gst_audio_base_sink_get_provide_clock (sink)); |
882 | 0 | break; |
883 | 0 | case PROP_SLAVE_METHOD: |
884 | 0 | g_value_set_enum (value, gst_audio_base_sink_get_slave_method (sink)); |
885 | 0 | break; |
886 | 0 | case PROP_CAN_ACTIVATE_PULL: |
887 | 0 | g_value_set_boolean (value, GST_BASE_SINK (sink)->can_activate_pull); |
888 | 0 | break; |
889 | 0 | case PROP_DRIFT_TOLERANCE: |
890 | 0 | g_value_set_int64 (value, gst_audio_base_sink_get_drift_tolerance (sink)); |
891 | 0 | break; |
892 | 0 | case PROP_ALIGNMENT_THRESHOLD: |
893 | 0 | g_value_set_uint64 (value, |
894 | 0 | gst_audio_base_sink_get_alignment_threshold (sink)); |
895 | 0 | break; |
896 | 0 | case PROP_DISCONT_WAIT: |
897 | 0 | g_value_set_uint64 (value, gst_audio_base_sink_get_discont_wait (sink)); |
898 | 0 | break; |
899 | 0 | default: |
900 | 0 | G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); |
901 | 0 | break; |
902 | 0 | } |
903 | 0 | } |
904 | | |
905 | | static gboolean |
906 | | gst_audio_base_sink_setcaps (GstBaseSink * bsink, GstCaps * caps) |
907 | 0 | { |
908 | 0 | GstAudioBaseSink *sink = GST_AUDIO_BASE_SINK (bsink); |
909 | 0 | GstAudioRingBufferSpec *spec; |
910 | 0 | GstClockTime now, internal_time; |
911 | 0 | GstClockTime crate_num, crate_denom; |
912 | |
|
913 | 0 | if (!sink->ringbuffer) |
914 | 0 | return FALSE; |
915 | | |
916 | 0 | spec = &sink->ringbuffer->spec; |
917 | |
|
918 | 0 | if (G_UNLIKELY (spec->caps && gst_caps_is_equal (spec->caps, caps))) { |
919 | 0 | GST_DEBUG_OBJECT (sink, |
920 | 0 | "Ringbuffer caps haven't changed, skipping reconfiguration"); |
921 | 0 | return TRUE; |
922 | 0 | } |
923 | | |
924 | 0 | GST_DEBUG_OBJECT (sink, "release old ringbuffer"); |
925 | | |
926 | | /* get current time, updates the last_time. When the subclass has a clock that |
927 | | * restarts from 0 when a new format is negotiated, it will call |
928 | | * gst_audio_clock_reset() which will use this last_time to create an offset |
929 | | * so that time from the clock keeps on increasing monotonically. */ |
930 | 0 | now = gst_clock_get_time (sink->provided_clock); |
931 | 0 | internal_time = gst_clock_get_internal_time (sink->provided_clock); |
932 | |
|
933 | 0 | GST_DEBUG_OBJECT (sink, "time was %" GST_TIME_FORMAT, GST_TIME_ARGS (now)); |
934 | | |
935 | | /* release old ringbuffer */ |
936 | 0 | gst_audio_ring_buffer_pause (sink->ringbuffer); |
937 | 0 | gst_audio_ring_buffer_activate (sink->ringbuffer, FALSE); |
938 | 0 | gst_audio_ring_buffer_release (sink->ringbuffer); |
939 | |
|
940 | 0 | GST_DEBUG_OBJECT (sink, "parse caps"); |
941 | |
|
942 | 0 | spec->buffer_time = sink->buffer_time; |
943 | 0 | spec->latency_time = sink->latency_time; |
944 | | |
945 | | /* parse new caps */ |
946 | 0 | if (!gst_audio_ring_buffer_parse_caps (spec, caps)) |
947 | 0 | goto parse_error; |
948 | | |
949 | 0 | gst_audio_ring_buffer_debug_spec_buff (spec); |
950 | |
|
951 | 0 | GST_DEBUG_OBJECT (sink, "acquire ringbuffer"); |
952 | 0 | if (!gst_audio_ring_buffer_acquire (sink->ringbuffer, spec)) |
953 | 0 | goto acquire_error; |
954 | | |
955 | | /* If we use our own clock, we need to adjust the offset since it will now |
956 | | * restart from zero */ |
957 | 0 | if (gst_audio_base_sink_is_self_provided_clock (sink)) |
958 | 0 | gst_audio_clock_reset (GST_AUDIO_CLOCK (sink->provided_clock), 0); |
959 | | |
960 | | /* We need to resync since the ringbuffer restarted */ |
961 | 0 | gst_audio_base_sink_reset_sync (sink); |
962 | |
|
963 | 0 | gst_audio_base_sink_custom_cb_report_discont (sink, |
964 | 0 | GST_AUDIO_BASE_SINK_DISCONT_REASON_NEW_CAPS); |
965 | |
|
966 | 0 | if (bsink->pad_mode == GST_PAD_MODE_PUSH) { |
967 | 0 | GST_DEBUG_OBJECT (sink, "activate ringbuffer"); |
968 | 0 | gst_audio_ring_buffer_activate (sink->ringbuffer, TRUE); |
969 | 0 | } |
970 | | |
971 | | /* due to possible changes in the spec file we should recalibrate the clock */ |
972 | 0 | gst_clock_get_calibration (sink->provided_clock, NULL, NULL, |
973 | 0 | &crate_num, &crate_denom); |
974 | 0 | gst_clock_set_calibration (sink->provided_clock, |
975 | 0 | internal_time, now, crate_num, crate_denom); |
976 | | |
977 | | /* calculate actual latency and buffer times. |
978 | | * FIXME: In 2.0, store the latency_time internally in ns */ |
979 | 0 | spec->latency_time = gst_util_uint64_scale (spec->segsize, |
980 | 0 | (GST_SECOND / GST_USECOND), spec->info.rate * spec->info.bpf); |
981 | |
|
982 | 0 | spec->buffer_time = spec->segtotal * spec->latency_time; |
983 | |
|
984 | 0 | gst_audio_ring_buffer_debug_spec_buff (spec); |
985 | |
|
986 | 0 | gst_element_post_message (GST_ELEMENT_CAST (bsink), |
987 | 0 | gst_message_new_latency (GST_OBJECT (bsink))); |
988 | |
|
989 | 0 | return TRUE; |
990 | | |
991 | | /* ERRORS */ |
992 | 0 | parse_error: |
993 | 0 | { |
994 | 0 | GST_DEBUG_OBJECT (sink, "could not parse caps"); |
995 | 0 | GST_ELEMENT_ERROR (sink, STREAM, FORMAT, |
996 | 0 | (NULL), ("cannot parse audio format.")); |
997 | 0 | return FALSE; |
998 | 0 | } |
999 | 0 | acquire_error: |
1000 | 0 | { |
1001 | 0 | GST_DEBUG_OBJECT (sink, "could not acquire ringbuffer"); |
1002 | 0 | return FALSE; |
1003 | 0 | } |
1004 | 0 | } |
1005 | | |
1006 | | static GstCaps * |
1007 | | gst_audio_base_sink_fixate (GstBaseSink * bsink, GstCaps * caps) |
1008 | 0 | { |
1009 | 0 | GstStructure *s; |
1010 | 0 | gint width, depth; |
1011 | |
|
1012 | 0 | caps = gst_caps_make_writable (caps); |
1013 | |
|
1014 | 0 | s = gst_caps_get_structure (caps, 0); |
1015 | | |
1016 | | /* fields for all formats */ |
1017 | 0 | gst_structure_fixate_field_nearest_int (s, "rate", 44100); |
1018 | 0 | gst_structure_fixate_field_nearest_int (s, "channels", 2); |
1019 | 0 | gst_structure_fixate_field_nearest_int (s, "width", 16); |
1020 | | |
1021 | | /* fields for int */ |
1022 | 0 | if (gst_structure_has_field (s, "depth")) { |
1023 | 0 | gst_structure_get_int (s, "width", &width); |
1024 | | /* round width to nearest multiple of 8 for the depth */ |
1025 | 0 | depth = GST_ROUND_UP_8 (width); |
1026 | 0 | gst_structure_fixate_field_nearest_int (s, "depth", depth); |
1027 | 0 | } |
1028 | 0 | if (gst_structure_has_field (s, "signed")) |
1029 | 0 | gst_structure_fixate_field_boolean (s, "signed", TRUE); |
1030 | 0 | if (gst_structure_has_field (s, "endianness")) |
1031 | 0 | gst_structure_fixate_field_nearest_int (s, "endianness", G_BYTE_ORDER); |
1032 | |
|
1033 | 0 | caps = GST_BASE_SINK_CLASS (parent_class)->fixate (bsink, caps); |
1034 | |
|
1035 | 0 | return caps; |
1036 | 0 | } |
1037 | | |
1038 | | static inline void |
1039 | | gst_audio_base_sink_reset_sync (GstAudioBaseSink * sink) |
1040 | 0 | { |
1041 | 0 | sink->next_sample = -1; |
1042 | 0 | sink->priv->eos_time = -1; |
1043 | 0 | sink->priv->discont_time = -1; |
1044 | 0 | sink->priv->avg_skew = -1; |
1045 | 0 | sink->priv->last_align = 0; |
1046 | 0 | } |
1047 | | |
1048 | | static void |
1049 | | gst_audio_base_sink_get_times (GstBaseSink * bsink, GstBuffer * buffer, |
1050 | | GstClockTime * start, GstClockTime * end) |
1051 | 0 | { |
1052 | | /* our clock sync is a bit too much for the base class to handle so |
1053 | | * we implement it ourselves. */ |
1054 | 0 | *start = GST_CLOCK_TIME_NONE; |
1055 | 0 | *end = GST_CLOCK_TIME_NONE; |
1056 | 0 | } |
1057 | | |
1058 | | static void |
1059 | | gst_audio_base_sink_force_start (GstAudioBaseSink * sink) |
1060 | 0 | { |
1061 | | /* Set the eos_rendering flag so sub-classes definitely start the clock. |
1062 | | * FIXME 2.0: Pass this as a flag to gst_audio_ring_buffer_start() */ |
1063 | 0 | g_atomic_int_set (&sink->eos_rendering, 1); |
1064 | 0 | gst_audio_ring_buffer_start (sink->ringbuffer); |
1065 | 0 | g_atomic_int_set (&sink->eos_rendering, 0); |
1066 | 0 | } |
1067 | | |
1068 | | /* This waits for the drain to happen and can be canceled */ |
1069 | | static GstFlowReturn |
1070 | | gst_audio_base_sink_drain (GstAudioBaseSink * sink) |
1071 | 0 | { |
1072 | 0 | GstFlowReturn ret = GST_FLOW_OK; |
1073 | 0 | if (!sink->ringbuffer) |
1074 | 0 | return ret; |
1075 | 0 | if (!sink->ringbuffer->spec.info.rate) |
1076 | 0 | return ret; |
1077 | | |
1078 | | /* if PLAYING is interrupted, |
1079 | | * arrange to have clock running when going to PLAYING again */ |
1080 | 0 | g_atomic_int_set (&sink->eos_rendering, 1); |
1081 | | |
1082 | | /* need to start playback before we can drain, but only when |
1083 | | * we have successfully negotiated a format and thus acquired the |
1084 | | * ringbuffer. */ |
1085 | 0 | if (gst_audio_ring_buffer_is_acquired (sink->ringbuffer)) |
1086 | 0 | gst_audio_ring_buffer_start (sink->ringbuffer); |
1087 | |
|
1088 | 0 | if (sink->priv->eos_time != -1) { |
1089 | 0 | GST_DEBUG_OBJECT (sink, |
1090 | 0 | "last sample time %" GST_TIME_FORMAT, |
1091 | 0 | GST_TIME_ARGS (sink->priv->eos_time)); |
1092 | | |
1093 | | /* wait for the EOS time to be reached, this is the time when the last |
1094 | | * sample is played. */ |
1095 | 0 | ret = gst_base_sink_wait (GST_BASE_SINK (sink), sink->priv->eos_time, NULL); |
1096 | |
|
1097 | 0 | GST_DEBUG_OBJECT (sink, "drained audio"); |
1098 | 0 | } |
1099 | 0 | g_atomic_int_set (&sink->eos_rendering, 0); |
1100 | 0 | return ret; |
1101 | 0 | } |
1102 | | |
1103 | | static GstFlowReturn |
1104 | | gst_audio_base_sink_wait_event (GstBaseSink * bsink, GstEvent * event) |
1105 | 0 | { |
1106 | 0 | GstAudioBaseSink *sink = GST_AUDIO_BASE_SINK (bsink); |
1107 | 0 | GstFlowReturn ret = GST_FLOW_OK; |
1108 | 0 | gboolean clear_force_start_flag = FALSE; |
1109 | | |
1110 | | /* For both gap and EOS events, make sure the ringbuffer is running |
1111 | | * before trying to wait on the event! */ |
1112 | 0 | switch (GST_EVENT_TYPE (event)) { |
1113 | 0 | case GST_EVENT_EOS: |
1114 | 0 | case GST_EVENT_GAP: |
1115 | | /* We must have a negotiated format before starting the ringbuffer */ |
1116 | 0 | if (G_UNLIKELY (!gst_audio_ring_buffer_is_acquired (sink->ringbuffer))) { |
1117 | 0 | GST_ELEMENT_ERROR (sink, STREAM, FORMAT, (NULL), |
1118 | 0 | ("Sink not negotiated before %s event.", |
1119 | 0 | GST_EVENT_TYPE_NAME (event))); |
1120 | 0 | return GST_FLOW_ERROR; |
1121 | 0 | } |
1122 | | |
1123 | 0 | gst_audio_base_sink_force_start (sink); |
1124 | | /* Make sure the ringbuffer will start again if interrupted during event_wait() */ |
1125 | 0 | g_atomic_int_set (&sink->eos_rendering, 1); |
1126 | 0 | clear_force_start_flag = TRUE; |
1127 | 0 | break; |
1128 | 0 | default: |
1129 | 0 | break; |
1130 | 0 | } |
1131 | | |
1132 | 0 | ret = GST_BASE_SINK_CLASS (parent_class)->wait_event (bsink, event); |
1133 | 0 | if (ret != GST_FLOW_OK) |
1134 | 0 | goto done; |
1135 | | |
1136 | 0 | switch (GST_EVENT_TYPE (event)) { |
1137 | 0 | case GST_EVENT_EOS: |
1138 | | /* now wait till we played everything */ |
1139 | 0 | ret = gst_audio_base_sink_drain (sink); |
1140 | 0 | break; |
1141 | 0 | default: |
1142 | 0 | break; |
1143 | 0 | } |
1144 | | |
1145 | 0 | done: |
1146 | 0 | if (clear_force_start_flag) |
1147 | 0 | g_atomic_int_set (&sink->eos_rendering, 0); |
1148 | 0 | return ret; |
1149 | 0 | } |
1150 | | |
1151 | | static gboolean |
1152 | | gst_audio_base_sink_event (GstBaseSink * bsink, GstEvent * event) |
1153 | 0 | { |
1154 | 0 | GstAudioBaseSink *sink = GST_AUDIO_BASE_SINK (bsink); |
1155 | |
|
1156 | 0 | switch (GST_EVENT_TYPE (event)) { |
1157 | 0 | case GST_EVENT_FLUSH_START: |
1158 | 0 | if (sink->ringbuffer) |
1159 | 0 | gst_audio_ring_buffer_set_flushing (sink->ringbuffer, TRUE); |
1160 | 0 | break; |
1161 | 0 | case GST_EVENT_FLUSH_STOP: |
1162 | | /* always resync on sample after a flush */ |
1163 | 0 | gst_audio_base_sink_reset_sync (sink); |
1164 | |
|
1165 | 0 | gst_audio_base_sink_custom_cb_report_discont (sink, |
1166 | 0 | GST_AUDIO_BASE_SINK_DISCONT_REASON_FLUSH); |
1167 | |
|
1168 | 0 | if (sink->ringbuffer) |
1169 | 0 | gst_audio_ring_buffer_set_flushing (sink->ringbuffer, FALSE); |
1170 | 0 | break; |
1171 | 0 | default: |
1172 | 0 | break; |
1173 | 0 | } |
1174 | 0 | return GST_BASE_SINK_CLASS (parent_class)->event (bsink, event); |
1175 | 0 | } |
1176 | | |
1177 | | static GstFlowReturn |
1178 | | gst_audio_base_sink_preroll (GstBaseSink * bsink, GstBuffer * buffer) |
1179 | 0 | { |
1180 | 0 | GstAudioBaseSink *sink = GST_AUDIO_BASE_SINK (bsink); |
1181 | |
|
1182 | 0 | if (!gst_audio_ring_buffer_is_acquired (sink->ringbuffer)) |
1183 | 0 | goto wrong_state; |
1184 | | |
1185 | | /* we don't really do anything when prerolling. We could make a |
1186 | | * property to play this buffer to have some sort of scrubbing |
1187 | | * support. */ |
1188 | 0 | return GST_FLOW_OK; |
1189 | | |
1190 | 0 | wrong_state: |
1191 | 0 | { |
1192 | 0 | GST_DEBUG_OBJECT (sink, "ringbuffer in wrong state"); |
1193 | 0 | GST_ELEMENT_ERROR (sink, STREAM, FORMAT, (NULL), ("sink not negotiated.")); |
1194 | 0 | return GST_FLOW_NOT_NEGOTIATED; |
1195 | 0 | } |
1196 | 0 | } |
1197 | | |
1198 | | static guint64 |
1199 | | gst_audio_base_sink_get_offset (GstAudioBaseSink * sink) |
1200 | 0 | { |
1201 | 0 | guint64 sample, sps; |
1202 | 0 | guint64 writeseg, segdone; |
1203 | 0 | gint64 diff; |
1204 | | |
1205 | | /* assume we can append to the previous sample */ |
1206 | 0 | sample = sink->next_sample; |
1207 | | /* no previous sample, try to insert at position 0 */ |
1208 | 0 | if (sample == -1) |
1209 | 0 | sample = 0; |
1210 | |
|
1211 | 0 | sps = sink->ringbuffer->samples_per_seg; |
1212 | | |
1213 | | /* figure out the segment and the offset inside the segment where |
1214 | | * the sample should be written. */ |
1215 | 0 | writeseg = sample / sps; |
1216 | | |
1217 | | /* get the currently processed segment */ |
1218 | 0 | segdone = gst_audio_ring_buffer_get_segdone (sink->ringbuffer) |
1219 | 0 | - gst_audio_ring_buffer_get_segbase (sink->ringbuffer); |
1220 | | |
1221 | | /* see how far away it is from the write segment */ |
1222 | 0 | diff = writeseg - segdone; |
1223 | 0 | if (diff < 0) { |
1224 | | /* sample would be dropped, position to next playable position */ |
1225 | 0 | sample = (segdone + 1) * sps; |
1226 | 0 | } |
1227 | |
|
1228 | 0 | return sample; |
1229 | 0 | } |
1230 | | |
1231 | | static GstClockTime |
1232 | | clock_convert_external (GstClockTime external, GstClockTime cinternal, |
1233 | | GstClockTime cexternal, GstClockTime crate_num, GstClockTime crate_denom) |
1234 | 0 | { |
1235 | | /* adjust for rate and speed */ |
1236 | 0 | if (external >= cexternal) { |
1237 | 0 | external = |
1238 | 0 | gst_util_uint64_scale (external - cexternal, crate_denom, crate_num); |
1239 | 0 | external += cinternal; |
1240 | 0 | } else { |
1241 | 0 | external = |
1242 | 0 | gst_util_uint64_scale (cexternal - external, crate_denom, crate_num); |
1243 | 0 | if (cinternal > external) |
1244 | 0 | external = cinternal - external; |
1245 | 0 | else |
1246 | 0 | external = 0; |
1247 | 0 | } |
1248 | 0 | return external; |
1249 | 0 | } |
1250 | | |
1251 | | |
1252 | | /* apply the clock offset and invoke a custom callback |
1253 | | * which might also request changes to the playout pointer |
1254 | | * |
1255 | | * this reuses code from the skewing algorithm, but leaves |
1256 | | * decision on whether or not to skew (and how much to skew) |
1257 | | * up to the callback */ |
1258 | | static void |
1259 | | gst_audio_base_sink_custom_slaving (GstAudioBaseSink * sink, |
1260 | | GstClockTime render_start, GstClockTime render_stop, |
1261 | | GstClockTime * srender_start, GstClockTime * srender_stop) |
1262 | 0 | { |
1263 | 0 | GstClockTime cinternal, cexternal, crate_num, crate_denom; |
1264 | 0 | GstClockTime etime, itime; |
1265 | 0 | GstClockTimeDiff requested_skew, drift; |
1266 | 0 | gint driftsamples; |
1267 | 0 | gint64 last_align; |
1268 | | |
1269 | | /* get calibration parameters to compensate for offsets */ |
1270 | 0 | gst_clock_get_calibration (sink->provided_clock, &cinternal, &cexternal, |
1271 | 0 | &crate_num, &crate_denom); |
1272 | | |
1273 | | /* sample clocks and figure out clock skew */ |
1274 | 0 | etime = gst_clock_get_time (GST_ELEMENT_CLOCK (sink)); |
1275 | 0 | itime = gst_audio_clock_get_time (GST_AUDIO_CLOCK (sink->provided_clock)); |
1276 | 0 | itime = |
1277 | 0 | gst_audio_clock_adjust (GST_AUDIO_CLOCK (sink->provided_clock), itime); |
1278 | |
|
1279 | 0 | GST_DEBUG_OBJECT (sink, |
1280 | 0 | "internal %" GST_TIME_FORMAT " external %" GST_TIME_FORMAT |
1281 | 0 | " cinternal %" GST_TIME_FORMAT " cexternal %" GST_TIME_FORMAT, |
1282 | 0 | GST_TIME_ARGS (itime), GST_TIME_ARGS (etime), |
1283 | 0 | GST_TIME_ARGS (cinternal), GST_TIME_ARGS (cexternal)); |
1284 | | |
1285 | | /* make sure we never go below 0 */ |
1286 | 0 | etime = etime > cexternal ? etime - cexternal : 0; |
1287 | 0 | itime = itime > cinternal ? itime - cinternal : 0; |
1288 | | |
1289 | | /* don't do any skewing unless the callback explicitly requests one */ |
1290 | 0 | requested_skew = 0; |
1291 | |
|
1292 | 0 | if (sink->priv->custom_slaving_callback != NULL) { |
1293 | 0 | sink->priv->custom_slaving_callback (sink, etime, itime, &requested_skew, |
1294 | 0 | FALSE, sink->priv->custom_slaving_cb_data); |
1295 | 0 | GST_DEBUG_OBJECT (sink, "custom slaving requested skew %" GST_STIME_FORMAT, |
1296 | 0 | GST_STIME_ARGS (requested_skew)); |
1297 | 0 | } else { |
1298 | 0 | GST_DEBUG_OBJECT (sink, |
1299 | 0 | "no custom slaving callback set - clock drift will not be compensated"); |
1300 | 0 | } |
1301 | |
|
1302 | 0 | if (requested_skew > 0) { |
1303 | | /* Move the external time backward by the requested skew, but don't ever |
1304 | | * go negative. Moving the requested skew by the same distance defines |
1305 | | * the new clock skew window center point. This allows the clock to |
1306 | | * drift equally into either direction after the correction. */ |
1307 | 0 | if (G_LIKELY (cexternal > requested_skew)) |
1308 | 0 | drift = requested_skew; |
1309 | 0 | else |
1310 | 0 | drift = cexternal; |
1311 | |
|
1312 | 0 | cexternal -= drift; |
1313 | |
|
1314 | 0 | driftsamples = (sink->ringbuffer->spec.info.rate * drift) / GST_SECOND; |
1315 | 0 | last_align = sink->priv->last_align; |
1316 | | |
1317 | | /* if we were aligning in the wrong direction or we aligned more than what we |
1318 | | * will correct, resync */ |
1319 | 0 | if ((last_align < 0) || (last_align > driftsamples)) |
1320 | 0 | sink->next_sample = -1; |
1321 | |
|
1322 | 0 | GST_DEBUG_OBJECT (sink, |
1323 | 0 | "last_align %" G_GINT64_FORMAT " driftsamples %u, next %" |
1324 | 0 | G_GUINT64_FORMAT, last_align, driftsamples, sink->next_sample); |
1325 | |
|
1326 | 0 | gst_clock_set_calibration (sink->provided_clock, cinternal, cexternal, |
1327 | 0 | crate_num, crate_denom); |
1328 | 0 | } else if (requested_skew < 0) { |
1329 | 0 | drift = -requested_skew; |
1330 | 0 | cexternal += drift; |
1331 | |
|
1332 | 0 | driftsamples = (sink->ringbuffer->spec.info.rate * drift) / GST_SECOND; |
1333 | 0 | last_align = sink->priv->last_align; |
1334 | | |
1335 | | /* if we were aligning in the wrong direction or we aligned more than what we |
1336 | | * will correct, resync */ |
1337 | 0 | if ((last_align > 0) || (-last_align > driftsamples)) |
1338 | 0 | sink->next_sample = -1; |
1339 | |
|
1340 | 0 | GST_DEBUG_OBJECT (sink, |
1341 | 0 | "last_align %" G_GINT64_FORMAT " driftsamples %u, next %" |
1342 | 0 | G_GUINT64_FORMAT, last_align, driftsamples, sink->next_sample); |
1343 | |
|
1344 | 0 | gst_clock_set_calibration (sink->provided_clock, cinternal, cexternal, |
1345 | 0 | crate_num, crate_denom); |
1346 | 0 | } |
1347 | | |
1348 | | /* convert, ignoring speed */ |
1349 | 0 | render_start = clock_convert_external (render_start, cinternal, cexternal, |
1350 | 0 | crate_num, crate_denom); |
1351 | 0 | render_stop = clock_convert_external (render_stop, cinternal, cexternal, |
1352 | 0 | crate_num, crate_denom); |
1353 | |
|
1354 | 0 | *srender_start = render_start; |
1355 | 0 | *srender_stop = render_stop; |
1356 | 0 | } |
1357 | | |
1358 | | /* algorithm to calculate sample positions that will result in resampling to |
1359 | | * match the clock rate of the master */ |
1360 | | static void |
1361 | | gst_audio_base_sink_resample_slaving (GstAudioBaseSink * sink, |
1362 | | GstClockTime render_start, GstClockTime render_stop, |
1363 | | GstClockTime * srender_start, GstClockTime * srender_stop) |
1364 | 0 | { |
1365 | 0 | GstClockTime cinternal, cexternal; |
1366 | 0 | GstClockTime crate_num, crate_denom; |
1367 | | |
1368 | | /* FIXME, we can sample and add observations here or use the timeouts on the |
1369 | | * clock. No idea which one is better or more stable. The timeout seems more |
1370 | | * arbitrary but this one seems more demanding and does not work when there is |
1371 | | * no data coming in to the sink. */ |
1372 | | #if 0 |
1373 | | GstClockTime etime, itime; |
1374 | | gdouble r_squared; |
1375 | | |
1376 | | /* sample clocks and figure out clock skew */ |
1377 | | etime = gst_clock_get_time (GST_ELEMENT_CLOCK (sink)); |
1378 | | itime = gst_audio_clock_get_time (sink->provided_clock); |
1379 | | |
1380 | | /* add new observation */ |
1381 | | gst_clock_add_observation (sink->provided_clock, itime, etime, &r_squared); |
1382 | | #endif |
1383 | | |
1384 | | /* get calibration parameters to compensate for speed and offset differences |
1385 | | * when we are slaved */ |
1386 | 0 | gst_clock_get_calibration (sink->provided_clock, &cinternal, &cexternal, |
1387 | 0 | &crate_num, &crate_denom); |
1388 | |
|
1389 | 0 | GST_DEBUG_OBJECT (sink, "internal %" GST_TIME_FORMAT " external %" |
1390 | 0 | GST_TIME_FORMAT " %" G_GUINT64_FORMAT "/%" G_GUINT64_FORMAT " = %f", |
1391 | 0 | GST_TIME_ARGS (cinternal), GST_TIME_ARGS (cexternal), crate_num, |
1392 | 0 | crate_denom, gst_guint64_to_gdouble (crate_num) / |
1393 | 0 | gst_guint64_to_gdouble (crate_denom)); |
1394 | |
|
1395 | 0 | if (crate_num == 0) |
1396 | 0 | crate_denom = crate_num = 1; |
1397 | | |
1398 | | /* bring external time to internal time */ |
1399 | 0 | render_start = clock_convert_external (render_start, cinternal, cexternal, |
1400 | 0 | crate_num, crate_denom); |
1401 | 0 | render_stop = clock_convert_external (render_stop, cinternal, cexternal, |
1402 | 0 | crate_num, crate_denom); |
1403 | |
|
1404 | 0 | GST_DEBUG_OBJECT (sink, |
1405 | 0 | "after slaving: start %" GST_TIME_FORMAT " - stop %" GST_TIME_FORMAT, |
1406 | 0 | GST_TIME_ARGS (render_start), GST_TIME_ARGS (render_stop)); |
1407 | |
|
1408 | 0 | *srender_start = render_start; |
1409 | 0 | *srender_stop = render_stop; |
1410 | 0 | } |
1411 | | |
1412 | | /* algorithm to calculate sample positions that will result in changing the |
1413 | | * playout pointer to match the clock rate of the master */ |
1414 | | static void |
1415 | | gst_audio_base_sink_skew_slaving (GstAudioBaseSink * sink, |
1416 | | GstClockTime render_start, GstClockTime render_stop, |
1417 | | GstClockTime * srender_start, GstClockTime * srender_stop) |
1418 | 0 | { |
1419 | 0 | GstClockTime cinternal, cexternal, crate_num, crate_denom; |
1420 | 0 | GstClockTime etime, itime; |
1421 | 0 | GstClockTimeDiff skew, drift, mdrift2; |
1422 | 0 | gint driftsamples; |
1423 | 0 | gint64 last_align; |
1424 | | |
1425 | | /* get calibration parameters to compensate for offsets */ |
1426 | 0 | gst_clock_get_calibration (sink->provided_clock, &cinternal, &cexternal, |
1427 | 0 | &crate_num, &crate_denom); |
1428 | | |
1429 | | /* sample clocks and figure out clock skew */ |
1430 | 0 | etime = gst_clock_get_time (GST_ELEMENT_CLOCK (sink)); |
1431 | 0 | itime = gst_audio_clock_get_time (GST_AUDIO_CLOCK (sink->provided_clock)); |
1432 | 0 | itime = |
1433 | 0 | gst_audio_clock_adjust (GST_AUDIO_CLOCK (sink->provided_clock), itime); |
1434 | |
|
1435 | 0 | GST_DEBUG_OBJECT (sink, |
1436 | 0 | "internal %" GST_TIME_FORMAT " external %" GST_TIME_FORMAT |
1437 | 0 | " cinternal %" GST_TIME_FORMAT " cexternal %" GST_TIME_FORMAT, |
1438 | 0 | GST_TIME_ARGS (itime), GST_TIME_ARGS (etime), |
1439 | 0 | GST_TIME_ARGS (cinternal), GST_TIME_ARGS (cexternal)); |
1440 | | |
1441 | | /* make sure we never go below 0 */ |
1442 | 0 | etime = etime > cexternal ? etime - cexternal : 0; |
1443 | 0 | itime = itime > cinternal ? itime - cinternal : 0; |
1444 | | |
1445 | | /* do itime - etime. |
1446 | | * positive value means external clock goes slower |
1447 | | * negative value means external clock goes faster */ |
1448 | 0 | skew = GST_CLOCK_DIFF (etime, itime); |
1449 | 0 | if (sink->priv->avg_skew == -1) { |
1450 | | /* first observation */ |
1451 | 0 | sink->priv->avg_skew = skew; |
1452 | 0 | } else { |
1453 | | /* next observations use a moving average */ |
1454 | 0 | sink->priv->avg_skew = (31 * sink->priv->avg_skew + skew) / 32; |
1455 | 0 | } |
1456 | |
|
1457 | 0 | GST_DEBUG_OBJECT (sink, "internal %" GST_TIME_FORMAT " external %" |
1458 | 0 | GST_TIME_FORMAT " skew %" GST_STIME_FORMAT " avg %" GST_STIME_FORMAT, |
1459 | 0 | GST_TIME_ARGS (itime), GST_TIME_ARGS (etime), GST_STIME_ARGS (skew), |
1460 | 0 | GST_STIME_ARGS (sink->priv->avg_skew)); |
1461 | | |
1462 | | /* the max drift we allow */ |
1463 | 0 | mdrift2 = (sink->priv->drift_tolerance * 1000) / 2; |
1464 | | |
1465 | | /* adjust playout pointer based on skew */ |
1466 | 0 | if (sink->priv->avg_skew > mdrift2) { |
1467 | | /* master is running slower, move external time backwards */ |
1468 | 0 | GST_WARNING_OBJECT (sink, |
1469 | 0 | "correct clock skew %" GST_STIME_FORMAT " > %" GST_STIME_FORMAT, |
1470 | 0 | GST_STIME_ARGS (sink->priv->avg_skew), GST_STIME_ARGS (mdrift2)); |
1471 | | |
1472 | | /* Move the external time backward by the average skew, but don't ever |
1473 | | * go negative. Moving the average skew by the same distance defines |
1474 | | * the new clock skew window center point. This allows the clock to |
1475 | | * drift equally into either direction after the correction. */ |
1476 | 0 | if (G_LIKELY (cexternal > sink->priv->avg_skew)) |
1477 | 0 | drift = sink->priv->avg_skew; |
1478 | 0 | else |
1479 | 0 | drift = cexternal; |
1480 | 0 | cexternal -= drift; |
1481 | 0 | sink->priv->avg_skew -= drift; |
1482 | |
|
1483 | 0 | driftsamples = (sink->ringbuffer->spec.info.rate * drift) / GST_SECOND; |
1484 | 0 | last_align = sink->priv->last_align; |
1485 | | |
1486 | | /* if we were aligning in the wrong direction or we aligned more than what |
1487 | | * we will correct, resync */ |
1488 | 0 | if (last_align < 0 || last_align > driftsamples) |
1489 | 0 | sink->next_sample = -1; |
1490 | |
|
1491 | 0 | GST_DEBUG_OBJECT (sink, |
1492 | 0 | "last_align %" G_GINT64_FORMAT " driftsamples %u, next %" |
1493 | 0 | G_GUINT64_FORMAT, last_align, driftsamples, sink->next_sample); |
1494 | |
|
1495 | 0 | gst_clock_set_calibration (sink->provided_clock, cinternal, cexternal, |
1496 | 0 | crate_num, crate_denom); |
1497 | 0 | } else if (sink->priv->avg_skew < -mdrift2) { |
1498 | | /* master is running faster, move external time forwards */ |
1499 | 0 | GST_WARNING_OBJECT (sink, |
1500 | 0 | "correct clock skew %" GST_STIME_FORMAT " < -%" GST_STIME_FORMAT, |
1501 | 0 | GST_STIME_ARGS (sink->priv->avg_skew), GST_STIME_ARGS (mdrift2)); |
1502 | | |
1503 | | /* Move the external time forward by the average skew, and move the |
1504 | | * average skew by the same distance (which equals a reset to 0). This |
1505 | | * defines the new clock skew window center point. This allows the |
1506 | | * clock to drift equally into either direction after the correction. */ |
1507 | 0 | drift = -sink->priv->avg_skew; |
1508 | 0 | cexternal += drift; |
1509 | 0 | sink->priv->avg_skew = 0; |
1510 | |
|
1511 | 0 | driftsamples = (sink->ringbuffer->spec.info.rate * drift) / GST_SECOND; |
1512 | 0 | last_align = sink->priv->last_align; |
1513 | | |
1514 | | /* if we were aligning in the wrong direction or we aligned more than what |
1515 | | * we will correct, resync */ |
1516 | 0 | if (last_align > 0 || -last_align > driftsamples) |
1517 | 0 | sink->next_sample = -1; |
1518 | |
|
1519 | 0 | GST_DEBUG_OBJECT (sink, |
1520 | 0 | "last_align %" G_GINT64_FORMAT " driftsamples %u, next %" |
1521 | 0 | G_GUINT64_FORMAT, last_align, driftsamples, sink->next_sample); |
1522 | |
|
1523 | 0 | gst_clock_set_calibration (sink->provided_clock, cinternal, cexternal, |
1524 | 0 | crate_num, crate_denom); |
1525 | 0 | } |
1526 | | |
1527 | | /* convert, ignoring speed */ |
1528 | 0 | render_start = clock_convert_external (render_start, cinternal, cexternal, |
1529 | 0 | crate_num, crate_denom); |
1530 | 0 | render_stop = clock_convert_external (render_stop, cinternal, cexternal, |
1531 | 0 | crate_num, crate_denom); |
1532 | |
|
1533 | 0 | *srender_start = render_start; |
1534 | 0 | *srender_stop = render_stop; |
1535 | 0 | } |
1536 | | |
1537 | | /* apply the clock offset but do no slaving otherwise */ |
1538 | | static void |
1539 | | gst_audio_base_sink_none_slaving (GstAudioBaseSink * sink, |
1540 | | GstClockTime render_start, GstClockTime render_stop, |
1541 | | GstClockTime * srender_start, GstClockTime * srender_stop) |
1542 | 0 | { |
1543 | 0 | GstClockTime cinternal, cexternal, crate_num, crate_denom; |
1544 | | |
1545 | | /* get calibration parameters to compensate for offsets */ |
1546 | 0 | gst_clock_get_calibration (sink->provided_clock, &cinternal, &cexternal, |
1547 | 0 | &crate_num, &crate_denom); |
1548 | | |
1549 | | /* convert, ignoring speed */ |
1550 | 0 | render_start = clock_convert_external (render_start, cinternal, cexternal, |
1551 | 0 | crate_num, crate_denom); |
1552 | 0 | render_stop = clock_convert_external (render_stop, cinternal, cexternal, |
1553 | 0 | crate_num, crate_denom); |
1554 | |
|
1555 | 0 | *srender_start = render_start; |
1556 | 0 | *srender_stop = render_stop; |
1557 | 0 | } |
1558 | | |
1559 | | /* converts render_start and render_stop to their slaved values */ |
1560 | | static void |
1561 | | gst_audio_base_sink_handle_slaving (GstAudioBaseSink * sink, |
1562 | | GstClockTime render_start, GstClockTime render_stop, |
1563 | | GstClockTime * srender_start, GstClockTime * srender_stop) |
1564 | 0 | { |
1565 | 0 | switch (sink->priv->slave_method) { |
1566 | 0 | case GST_AUDIO_BASE_SINK_SLAVE_RESAMPLE: |
1567 | 0 | gst_audio_base_sink_resample_slaving (sink, render_start, render_stop, |
1568 | 0 | srender_start, srender_stop); |
1569 | 0 | break; |
1570 | 0 | case GST_AUDIO_BASE_SINK_SLAVE_SKEW: |
1571 | 0 | gst_audio_base_sink_skew_slaving (sink, render_start, render_stop, |
1572 | 0 | srender_start, srender_stop); |
1573 | 0 | break; |
1574 | 0 | case GST_AUDIO_BASE_SINK_SLAVE_NONE: |
1575 | 0 | gst_audio_base_sink_none_slaving (sink, render_start, render_stop, |
1576 | 0 | srender_start, srender_stop); |
1577 | 0 | break; |
1578 | 0 | case GST_AUDIO_BASE_SINK_SLAVE_CUSTOM: |
1579 | 0 | gst_audio_base_sink_custom_slaving (sink, render_start, render_stop, |
1580 | 0 | srender_start, srender_stop); |
1581 | 0 | break; |
1582 | 0 | default: |
1583 | 0 | g_warning ("unknown slaving method %d", sink->priv->slave_method); |
1584 | 0 | break; |
1585 | 0 | } |
1586 | 0 | } |
1587 | | |
1588 | | /* must be called with LOCK */ |
1589 | | static GstFlowReturn |
1590 | | gst_audio_base_sink_sync_latency (GstBaseSink * bsink, GstMiniObject * obj) |
1591 | 0 | { |
1592 | 0 | GstClock *clock; |
1593 | 0 | GstClockReturn status; |
1594 | 0 | GstClockTime time, render_delay; |
1595 | 0 | GstFlowReturn ret; |
1596 | 0 | GstAudioBaseSink *sink; |
1597 | 0 | GstClockTime itime, etime; |
1598 | 0 | GstClockTime rate_num, rate_denom; |
1599 | 0 | GstClockTimeDiff jitter; |
1600 | |
|
1601 | 0 | sink = GST_AUDIO_BASE_SINK (bsink); |
1602 | |
|
1603 | 0 | clock = GST_ELEMENT_CLOCK (sink); |
1604 | 0 | if (G_UNLIKELY (clock == NULL)) |
1605 | 0 | goto no_clock; |
1606 | | |
1607 | | /* we provided the global clock, don't need to do anything special */ |
1608 | 0 | if (clock == sink->provided_clock) |
1609 | 0 | goto no_slaving; |
1610 | | |
1611 | 0 | GST_OBJECT_UNLOCK (sink); |
1612 | |
|
1613 | 0 | do { |
1614 | 0 | GST_DEBUG_OBJECT (sink, "checking preroll"); |
1615 | |
|
1616 | 0 | ret = gst_base_sink_do_preroll (bsink, obj); |
1617 | 0 | if (ret != GST_FLOW_OK) |
1618 | 0 | goto flushing; |
1619 | | |
1620 | 0 | GST_OBJECT_LOCK (sink); |
1621 | 0 | time = sink->priv->us_latency; |
1622 | 0 | GST_OBJECT_UNLOCK (sink); |
1623 | | |
1624 | | /* Renderdelay is added onto our own latency, and needs |
1625 | | * to be subtracted as well */ |
1626 | 0 | render_delay = gst_base_sink_get_render_delay (bsink); |
1627 | |
|
1628 | 0 | if (G_LIKELY (time > render_delay)) |
1629 | 0 | time -= render_delay; |
1630 | 0 | else |
1631 | 0 | time = 0; |
1632 | | |
1633 | | /* preroll done, we can sync since we are in PLAYING now. */ |
1634 | 0 | GST_DEBUG_OBJECT (sink, "possibly waiting for clock to reach %" |
1635 | 0 | GST_TIME_FORMAT, GST_TIME_ARGS (time)); |
1636 | | |
1637 | | /* wait for the clock, this can be interrupted because we got shut down or |
1638 | | * we PAUSED. */ |
1639 | 0 | status = gst_base_sink_wait_clock (bsink, time, &jitter); |
1640 | |
|
1641 | 0 | GST_DEBUG_OBJECT (sink, "clock returned %d %" GST_TIME_FORMAT, status, |
1642 | 0 | GST_TIME_ARGS (jitter)); |
1643 | | |
1644 | | /* invalid time, no clock or sync disabled, just continue then */ |
1645 | 0 | if (status == GST_CLOCK_BADTIME) |
1646 | 0 | break; |
1647 | | |
1648 | | /* waiting could have been interrupted and we can be flushing now */ |
1649 | 0 | if (G_UNLIKELY (bsink->flushing)) |
1650 | 0 | goto flushing; |
1651 | | |
1652 | | /* retry if we got unscheduled, which means we did not reach the timeout |
1653 | | * yet. if some other error occurs, we continue. */ |
1654 | 0 | } while (status == GST_CLOCK_UNSCHEDULED); |
1655 | | |
1656 | 0 | GST_DEBUG_OBJECT (sink, "latency synced"); |
1657 | | |
1658 | | /* We might need to take the object lock within gst_audio_clock_get_time(), |
1659 | | * so call that before we take it again */ |
1660 | 0 | itime = gst_audio_clock_get_time (GST_AUDIO_CLOCK (sink->provided_clock)); |
1661 | 0 | itime = |
1662 | 0 | gst_audio_clock_adjust (GST_AUDIO_CLOCK (sink->provided_clock), itime); |
1663 | |
|
1664 | 0 | GST_OBJECT_LOCK (sink); |
1665 | | |
1666 | | /* when we prerolled in time, we can accurately set the calibration, |
1667 | | * our internal clock should exactly have been the latency (== the running |
1668 | | * time of the external clock) */ |
1669 | 0 | etime = GST_ELEMENT_CAST (sink)->base_time + time; |
1670 | |
|
1671 | 0 | if (status == GST_CLOCK_EARLY) { |
1672 | | /* when we prerolled late, we have to take into account the lateness */ |
1673 | 0 | GST_DEBUG_OBJECT (sink, "late preroll, adding jitter"); |
1674 | 0 | etime += jitter; |
1675 | 0 | } |
1676 | | |
1677 | | /* start ringbuffer so we can start slaving right away when we need to */ |
1678 | 0 | gst_audio_base_sink_force_start (sink); |
1679 | |
|
1680 | 0 | GST_DEBUG_OBJECT (sink, |
1681 | 0 | "internal time: %" GST_TIME_FORMAT " external time: %" GST_TIME_FORMAT, |
1682 | 0 | GST_TIME_ARGS (itime), GST_TIME_ARGS (etime)); |
1683 | | |
1684 | | /* copy the original calibrated rate but update the internal and external |
1685 | | * times. */ |
1686 | 0 | gst_clock_get_calibration (sink->provided_clock, NULL, NULL, &rate_num, |
1687 | 0 | &rate_denom); |
1688 | 0 | gst_clock_set_calibration (sink->provided_clock, itime, etime, |
1689 | 0 | rate_num, rate_denom); |
1690 | |
|
1691 | 0 | switch (sink->priv->slave_method) { |
1692 | 0 | case GST_AUDIO_BASE_SINK_SLAVE_RESAMPLE: |
1693 | | /* only set as master when we are resampling */ |
1694 | 0 | GST_DEBUG_OBJECT (sink, "Setting clock as master"); |
1695 | 0 | gst_clock_set_master (sink->provided_clock, clock); |
1696 | 0 | break; |
1697 | 0 | case GST_AUDIO_BASE_SINK_SLAVE_SKEW: |
1698 | 0 | case GST_AUDIO_BASE_SINK_SLAVE_NONE: |
1699 | 0 | case GST_AUDIO_BASE_SINK_SLAVE_CUSTOM: |
1700 | 0 | default: |
1701 | 0 | break; |
1702 | 0 | } |
1703 | | |
1704 | 0 | gst_audio_base_sink_reset_sync (sink); |
1705 | |
|
1706 | 0 | gst_audio_base_sink_custom_cb_report_discont (sink, |
1707 | 0 | GST_AUDIO_BASE_SINK_DISCONT_REASON_SYNC_LATENCY); |
1708 | |
|
1709 | 0 | return GST_FLOW_OK; |
1710 | | |
1711 | | /* ERRORS */ |
1712 | 0 | no_clock: |
1713 | 0 | { |
1714 | 0 | GST_DEBUG_OBJECT (sink, "we have no clock"); |
1715 | 0 | return GST_FLOW_OK; |
1716 | 0 | } |
1717 | 0 | no_slaving: |
1718 | 0 | { |
1719 | 0 | GST_DEBUG_OBJECT (sink, "we are not slaved"); |
1720 | 0 | return GST_FLOW_OK; |
1721 | 0 | } |
1722 | 0 | flushing: |
1723 | 0 | { |
1724 | 0 | GST_DEBUG_OBJECT (sink, "we are flushing"); |
1725 | 0 | GST_OBJECT_LOCK (sink); |
1726 | 0 | return GST_FLOW_FLUSHING; |
1727 | 0 | } |
1728 | 0 | } |
1729 | | |
1730 | 0 | #define ABSDIFF(a, b) ((a) > (b) ? (a) - (b) : (b) - (a)) |
1731 | | static gint64 |
1732 | | gst_audio_base_sink_get_alignment (GstAudioBaseSink * sink, |
1733 | | GstClockTime sample_offset) |
1734 | 0 | { |
1735 | 0 | GstAudioRingBuffer *ringbuf = sink->ringbuffer; |
1736 | 0 | gint64 align; |
1737 | 0 | gint64 sample_diff; |
1738 | 0 | gint64 max_sample_diff; |
1739 | 0 | guint64 segdone = gst_audio_ring_buffer_get_segdone (sink->ringbuffer) |
1740 | 0 | - gst_audio_ring_buffer_get_segbase (sink->ringbuffer); |
1741 | 0 | gint64 samples_done = segdone * (gint64) ringbuf->samples_per_seg; |
1742 | 0 | gint64 headroom = sample_offset - samples_done; |
1743 | 0 | gboolean allow_align = TRUE; |
1744 | 0 | gboolean discont = FALSE; |
1745 | 0 | gint rate; |
1746 | | |
1747 | | /* now try to align the sample to the previous one. */ |
1748 | | |
1749 | | /* calc align with previous sample and determine how big the |
1750 | | * difference is. */ |
1751 | 0 | align = sink->next_sample - sample_offset; |
1752 | 0 | sample_diff = ABS (align); |
1753 | | |
1754 | | /* calculate the max allowed drift in units of samples. */ |
1755 | 0 | rate = GST_AUDIO_INFO_RATE (&ringbuf->spec.info); |
1756 | 0 | max_sample_diff = gst_util_uint64_scale_int (sink->priv->alignment_threshold, |
1757 | 0 | rate, GST_SECOND); |
1758 | | |
1759 | | /* don't align if it means writing behind the read-segment */ |
1760 | 0 | if (sample_diff > headroom && align < 0) |
1761 | 0 | allow_align = FALSE; |
1762 | |
|
1763 | 0 | if (G_UNLIKELY (sample_diff >= max_sample_diff)) { |
1764 | | /* wait before deciding to make a discontinuity */ |
1765 | 0 | if (sink->priv->discont_wait > 0) { |
1766 | 0 | GstClockTime time = gst_util_uint64_scale_int (sample_offset, |
1767 | 0 | GST_SECOND, rate); |
1768 | 0 | GstClockTime expected_time = gst_util_uint64_scale_int (sink->next_sample, |
1769 | 0 | GST_SECOND, rate); |
1770 | |
|
1771 | 0 | if (sink->priv->discont_time == -1) { |
1772 | 0 | if (ABSDIFF (expected_time, time) >= sink->priv->discont_wait) |
1773 | 0 | discont = TRUE; |
1774 | 0 | else |
1775 | 0 | sink->priv->discont_time = expected_time; |
1776 | 0 | } else if (ABSDIFF (time, |
1777 | 0 | sink->priv->discont_time) >= sink->priv->discont_wait) { |
1778 | | /* discont_wait expired, discontinuity detected */ |
1779 | 0 | discont = TRUE; |
1780 | 0 | sink->priv->discont_time = -1; |
1781 | 0 | } |
1782 | 0 | } else { |
1783 | 0 | discont = TRUE; |
1784 | 0 | } |
1785 | 0 | } else if (G_UNLIKELY (sink->priv->discont_time != -1)) { |
1786 | | /* we have had a discont, but are now back on track! */ |
1787 | 0 | sink->priv->discont_time = -1; |
1788 | 0 | } |
1789 | |
|
1790 | 0 | if (G_LIKELY (!discont && allow_align)) { |
1791 | 0 | GST_DEBUG_OBJECT (sink, |
1792 | 0 | "align with prev sample, ABS (%" G_GINT64_FORMAT ") < %" |
1793 | 0 | G_GINT64_FORMAT, align, max_sample_diff); |
1794 | 0 | } else { |
1795 | 0 | gint64 diff_s G_GNUC_UNUSED; |
1796 | | |
1797 | | /* calculate sample diff in seconds for error message */ |
1798 | 0 | diff_s = gst_util_uint64_scale_int (sample_diff, GST_SECOND, rate); |
1799 | | |
1800 | | /* timestamps drifted apart from previous samples too much, we need to |
1801 | | * resync. We log this as an element warning. */ |
1802 | 0 | GST_WARNING_OBJECT (sink, |
1803 | 0 | "Unexpected discontinuity in audio timestamps of " |
1804 | 0 | "%s%" GST_TIME_FORMAT ", resyncing", |
1805 | 0 | sample_offset > sink->next_sample ? "+" : "-", GST_TIME_ARGS (diff_s)); |
1806 | 0 | align = 0; |
1807 | |
|
1808 | 0 | gst_audio_base_sink_custom_cb_report_discont (sink, |
1809 | 0 | GST_AUDIO_BASE_SINK_DISCONT_REASON_ALIGNMENT); |
1810 | 0 | } |
1811 | |
|
1812 | 0 | return align; |
1813 | 0 | } |
1814 | | |
1815 | | #undef ABSDIFF |
1816 | | |
1817 | | static GstFlowReturn |
1818 | | gst_audio_base_sink_render (GstBaseSink * bsink, GstBuffer * buf) |
1819 | 0 | { |
1820 | 0 | GstClockTime time, stop, render_start, render_stop, sample_offset; |
1821 | 0 | GstClockTimeDiff sync_offset, ts_offset; |
1822 | 0 | GstAudioBaseSinkClass *bclass; |
1823 | 0 | GstAudioBaseSink *sink; |
1824 | 0 | GstAudioRingBuffer *ringbuf; |
1825 | 0 | gint64 diff, align; |
1826 | 0 | guint64 ctime, cstop; |
1827 | 0 | gsize offset; |
1828 | 0 | GstMapInfo info; |
1829 | 0 | gsize size; |
1830 | 0 | guint samples, written; |
1831 | 0 | gint bpf, rate; |
1832 | 0 | gint accum; |
1833 | 0 | gint out_samples; |
1834 | 0 | GstClockTime base_time, render_delay, latency; |
1835 | 0 | GstClock *clock; |
1836 | 0 | gboolean sync, slaved, align_next; |
1837 | 0 | GstFlowReturn ret; |
1838 | 0 | GstSegment clip_seg; |
1839 | 0 | gint64 time_offset; |
1840 | 0 | GstBuffer *out = NULL; |
1841 | |
|
1842 | 0 | sink = GST_AUDIO_BASE_SINK (bsink); |
1843 | 0 | bclass = GST_AUDIO_BASE_SINK_GET_CLASS (sink); |
1844 | |
|
1845 | 0 | ringbuf = sink->ringbuffer; |
1846 | | |
1847 | | /* can't do anything when we don't have the device */ |
1848 | 0 | if (G_UNLIKELY (!gst_audio_ring_buffer_is_acquired (ringbuf))) |
1849 | 0 | goto wrong_state; |
1850 | | |
1851 | | /* Wait for upstream latency before starting the ringbuffer, we do this so |
1852 | | * that we can align the first sample of the ringbuffer to the base_time + |
1853 | | * latency. */ |
1854 | 0 | GST_OBJECT_LOCK (sink); |
1855 | 0 | base_time = GST_ELEMENT_CAST (sink)->base_time; |
1856 | 0 | if (G_UNLIKELY (sink->priv->sync_latency)) { |
1857 | 0 | ret = gst_audio_base_sink_sync_latency (bsink, GST_MINI_OBJECT_CAST (buf)); |
1858 | 0 | GST_OBJECT_UNLOCK (sink); |
1859 | 0 | if (G_UNLIKELY (ret != GST_FLOW_OK)) |
1860 | 0 | goto sync_latency_failed; |
1861 | | /* only do this once until we are set back to PLAYING */ |
1862 | 0 | sink->priv->sync_latency = FALSE; |
1863 | 0 | } else { |
1864 | 0 | GST_OBJECT_UNLOCK (sink); |
1865 | 0 | } |
1866 | | |
1867 | | /* Before we go on, let's see if we need to payload the data. If yes, we also |
1868 | | * need to unref the output buffer before leaving. */ |
1869 | 0 | if (bclass->payload) { |
1870 | 0 | out = bclass->payload (sink, buf); |
1871 | |
|
1872 | 0 | if (!out) |
1873 | 0 | goto payload_failed; |
1874 | | |
1875 | 0 | buf = out; |
1876 | 0 | } |
1877 | | |
1878 | 0 | bpf = GST_AUDIO_INFO_BPF (&ringbuf->spec.info); |
1879 | 0 | rate = GST_AUDIO_INFO_RATE (&ringbuf->spec.info); |
1880 | |
|
1881 | 0 | size = gst_buffer_get_size (buf); |
1882 | 0 | if (G_UNLIKELY (size % bpf) != 0) |
1883 | 0 | goto wrong_size; |
1884 | | |
1885 | 0 | samples = size / bpf; |
1886 | |
|
1887 | 0 | time = GST_BUFFER_PTS (buf); |
1888 | | |
1889 | | /* Last ditch attempt to ensure that we only play silence if |
1890 | | * we are in trickmode no-audio mode (or if a buffer is marked as a GAP) |
1891 | | * by dropping the buffer contents and rendering as a gap event instead */ |
1892 | 0 | if (G_UNLIKELY ((bsink->segment.flags & GST_SEGMENT_FLAG_TRICKMODE_NO_AUDIO) |
1893 | 0 | || (buf && GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_GAP)))) { |
1894 | 0 | GstClockTime duration; |
1895 | 0 | GstEvent *event; |
1896 | 0 | GstBaseSinkClass *bclass; |
1897 | 0 | GST_DEBUG_OBJECT (bsink, |
1898 | 0 | "Received GAP or ignoring audio for trickplay. Dropping contents"); |
1899 | |
|
1900 | 0 | duration = gst_util_uint64_scale_int (samples, GST_SECOND, rate); |
1901 | 0 | event = gst_event_new_gap (time, duration); |
1902 | |
|
1903 | 0 | bclass = GST_BASE_SINK_GET_CLASS (bsink); |
1904 | 0 | ret = bclass->wait_event (bsink, event); |
1905 | 0 | gst_event_unref (event); |
1906 | | |
1907 | | /* Ensure we'll resync on the next buffer as if discont */ |
1908 | 0 | sink->next_sample = -1; |
1909 | 0 | goto done; |
1910 | 0 | } |
1911 | | |
1912 | 0 | GST_DEBUG_OBJECT (sink, |
1913 | 0 | "time %" GST_TIME_FORMAT ", start %" |
1914 | 0 | GST_TIME_FORMAT ", samples %u", GST_TIME_ARGS (time), |
1915 | 0 | GST_TIME_ARGS (bsink->segment.start), samples); |
1916 | |
|
1917 | 0 | offset = 0; |
1918 | | |
1919 | | /* if not valid timestamp or we can't clip or sync, try to play |
1920 | | * sample ASAP */ |
1921 | 0 | if (!GST_CLOCK_TIME_IS_VALID (time)) { |
1922 | 0 | render_start = gst_audio_base_sink_get_offset (sink); |
1923 | 0 | render_stop = render_start + samples; |
1924 | 0 | GST_DEBUG_OBJECT (sink, "Buffer of size %" G_GSIZE_FORMAT " has no time." |
1925 | 0 | " Using render_start=%" G_GUINT64_FORMAT, size, render_start); |
1926 | | /* we don't have a start so we don't know stop either */ |
1927 | 0 | stop = -1; |
1928 | 0 | goto no_align; |
1929 | 0 | } |
1930 | | |
1931 | | /* let's calc stop based on the number of samples in the buffer instead |
1932 | | * of trusting the DURATION */ |
1933 | 0 | stop = time + gst_util_uint64_scale_int (samples, GST_SECOND, rate); |
1934 | | |
1935 | | /* prepare the clipping segment. Since we will be subtracting ts-offset and |
1936 | | * device-delay later we scale the start and stop with those values so that we |
1937 | | * can correctly clip them */ |
1938 | 0 | clip_seg.format = GST_FORMAT_TIME; |
1939 | 0 | clip_seg.start = bsink->segment.start; |
1940 | 0 | clip_seg.stop = bsink->segment.stop; |
1941 | 0 | clip_seg.duration = -1; |
1942 | | |
1943 | | /* the sync offset is the combination of ts-offset and device-delay */ |
1944 | 0 | latency = gst_base_sink_get_latency (bsink); |
1945 | 0 | ts_offset = gst_base_sink_get_ts_offset (bsink); |
1946 | 0 | render_delay = gst_base_sink_get_render_delay (bsink); |
1947 | 0 | sync_offset = ts_offset - render_delay + latency; |
1948 | |
|
1949 | 0 | GST_DEBUG_OBJECT (sink, |
1950 | 0 | "sync-offset %" GST_STIME_FORMAT ", render-delay %" GST_TIME_FORMAT |
1951 | 0 | ", ts-offset %" GST_STIME_FORMAT, GST_STIME_ARGS (sync_offset), |
1952 | 0 | GST_TIME_ARGS (render_delay), GST_STIME_ARGS (ts_offset)); |
1953 | | |
1954 | | /* compensate for ts-offset and device-delay when negative we need to |
1955 | | * clip. */ |
1956 | 0 | if (G_UNLIKELY (sync_offset < 0)) { |
1957 | 0 | clip_seg.start += -sync_offset; |
1958 | 0 | if (clip_seg.stop != -1) |
1959 | 0 | clip_seg.stop += -sync_offset; |
1960 | 0 | } |
1961 | | |
1962 | | /* samples should be rendered based on their timestamp. All samples |
1963 | | * arriving before the segment.start or after segment.stop are to be |
1964 | | * thrown away. All samples should also be clipped to the segment |
1965 | | * boundaries */ |
1966 | 0 | if (G_UNLIKELY (!gst_segment_clip (&clip_seg, GST_FORMAT_TIME, time, stop, |
1967 | 0 | &ctime, &cstop))) |
1968 | 0 | goto out_of_segment; |
1969 | | |
1970 | | /* see if some clipping happened */ |
1971 | 0 | diff = ctime - time; |
1972 | 0 | if (G_UNLIKELY (diff > 0)) { |
1973 | | /* bring clipped time to samples */ |
1974 | 0 | diff = gst_util_uint64_scale_int (diff, rate, GST_SECOND); |
1975 | 0 | GST_DEBUG_OBJECT (sink, "clipping start to %" GST_TIME_FORMAT " %" |
1976 | 0 | G_GUINT64_FORMAT " samples", GST_TIME_ARGS (ctime), diff); |
1977 | 0 | samples -= diff; |
1978 | 0 | offset += diff * bpf; |
1979 | 0 | time = ctime; |
1980 | 0 | } |
1981 | 0 | diff = stop - cstop; |
1982 | 0 | if (G_UNLIKELY (diff > 0)) { |
1983 | | /* bring clipped time to samples */ |
1984 | 0 | diff = gst_util_uint64_scale_int (diff, rate, GST_SECOND); |
1985 | 0 | GST_DEBUG_OBJECT (sink, "clipping stop to %" GST_TIME_FORMAT " %" |
1986 | 0 | G_GUINT64_FORMAT " samples", GST_TIME_ARGS (cstop), diff); |
1987 | 0 | samples -= diff; |
1988 | 0 | stop = cstop; |
1989 | 0 | } |
1990 | | |
1991 | | /* figure out how to sync */ |
1992 | 0 | if (G_LIKELY ((clock = GST_ELEMENT_CLOCK (bsink)))) |
1993 | 0 | sync = bsink->sync; |
1994 | 0 | else |
1995 | 0 | sync = FALSE; |
1996 | |
|
1997 | 0 | if (G_UNLIKELY (!sync)) { |
1998 | | /* no sync needed, play sample ASAP */ |
1999 | 0 | render_start = gst_audio_base_sink_get_offset (sink); |
2000 | 0 | render_stop = render_start + samples; |
2001 | 0 | GST_DEBUG_OBJECT (sink, |
2002 | 0 | "no sync needed. Using render_start=%" G_GUINT64_FORMAT, render_start); |
2003 | 0 | goto no_align; |
2004 | 0 | } |
2005 | | |
2006 | | /* bring buffer start and stop times to running time */ |
2007 | 0 | render_start = |
2008 | 0 | gst_segment_to_running_time (&bsink->segment, GST_FORMAT_TIME, time); |
2009 | 0 | render_stop = |
2010 | 0 | gst_segment_to_running_time (&bsink->segment, GST_FORMAT_TIME, stop); |
2011 | |
|
2012 | 0 | if (render_start == GST_CLOCK_TIME_NONE || render_stop == GST_CLOCK_TIME_NONE) |
2013 | 0 | goto too_late; |
2014 | | |
2015 | 0 | GST_DEBUG_OBJECT (sink, |
2016 | 0 | "running: start %" GST_TIME_FORMAT " - stop %" GST_TIME_FORMAT, |
2017 | 0 | GST_TIME_ARGS (render_start), GST_TIME_ARGS (render_stop)); |
2018 | | |
2019 | | /* store the time of the last sample, we'll use this to perform sync on the |
2020 | | * last sample when draining the buffer */ |
2021 | 0 | if (G_LIKELY (bsink->segment.rate >= 0.0)) { |
2022 | 0 | sink->priv->eos_time = render_stop; |
2023 | 0 | } else { |
2024 | 0 | sink->priv->eos_time = render_start; |
2025 | 0 | } |
2026 | |
|
2027 | 0 | if (G_UNLIKELY (sync_offset != 0)) { |
2028 | | /* compensate for ts-offset and delay. We know this will not underflow |
2029 | | * because we clipped above. */ |
2030 | 0 | GST_DEBUG_OBJECT (sink, |
2031 | 0 | "compensating for sync-offset %" GST_TIME_FORMAT, |
2032 | 0 | GST_TIME_ARGS (sync_offset)); |
2033 | 0 | render_start += sync_offset; |
2034 | 0 | render_stop += sync_offset; |
2035 | 0 | } |
2036 | |
|
2037 | 0 | if (base_time != 0) { |
2038 | 0 | GST_DEBUG_OBJECT (sink, "adding base_time %" GST_TIME_FORMAT, |
2039 | 0 | GST_TIME_ARGS (base_time)); |
2040 | | |
2041 | | /* add base time to sync against the clock */ |
2042 | 0 | render_start += base_time; |
2043 | 0 | render_stop += base_time; |
2044 | 0 | } |
2045 | |
|
2046 | 0 | if (G_UNLIKELY ((slaved = (clock != sink->provided_clock)))) { |
2047 | | /* handle clock slaving */ |
2048 | 0 | gst_audio_base_sink_handle_slaving (sink, render_start, render_stop, |
2049 | 0 | &render_start, &render_stop); |
2050 | 0 | } else { |
2051 | | /* no slaving needed but we need to adapt to the clock calibration |
2052 | | * parameters */ |
2053 | 0 | gst_audio_base_sink_none_slaving (sink, render_start, render_stop, |
2054 | 0 | &render_start, &render_stop); |
2055 | 0 | } |
2056 | |
|
2057 | 0 | GST_DEBUG_OBJECT (sink, |
2058 | 0 | "final timestamps: start %" GST_TIME_FORMAT " - stop %" GST_TIME_FORMAT, |
2059 | 0 | GST_TIME_ARGS (render_start), GST_TIME_ARGS (render_stop)); |
2060 | | |
2061 | | /* bring to position in the ringbuffer */ |
2062 | 0 | time_offset = GST_AUDIO_CLOCK_CAST (sink->provided_clock)->time_offset; |
2063 | |
|
2064 | 0 | if (G_UNLIKELY (time_offset != 0)) { |
2065 | 0 | GST_DEBUG_OBJECT (sink, |
2066 | 0 | "apply time offset %" GST_STIME_FORMAT, GST_STIME_ARGS (time_offset)); |
2067 | |
|
2068 | 0 | if (render_start > time_offset) |
2069 | 0 | render_start -= time_offset; |
2070 | 0 | else |
2071 | 0 | render_start = 0; |
2072 | 0 | if (render_stop > time_offset) |
2073 | 0 | render_stop -= time_offset; |
2074 | 0 | else |
2075 | 0 | render_stop = 0; |
2076 | 0 | } |
2077 | | |
2078 | | /* in some clock slaving cases, all late samples end up at 0 first, |
2079 | | * and subsequent ones align with that until threshold exceeded, |
2080 | | * and then sync back to 0 and so on, so avoid that altogether */ |
2081 | 0 | if (G_UNLIKELY (render_start == 0 && render_stop == 0)) |
2082 | 0 | goto too_late; |
2083 | | |
2084 | | /* and bring the time to the rate corrected offset in the buffer */ |
2085 | 0 | render_start = gst_util_uint64_scale_int (render_start, rate, GST_SECOND); |
2086 | 0 | render_stop = gst_util_uint64_scale_int (render_stop, rate, GST_SECOND); |
2087 | | |
2088 | | /* If the slaving got us an interval spanning 0, render_start will |
2089 | | have been set to 0. So if render_start is 0, we check whether |
2090 | | render_stop is set to contain all samples. If not, we need to |
2091 | | drop samples to match. */ |
2092 | 0 | if (render_start == 0) { |
2093 | 0 | guint nsamples = render_stop - render_start; |
2094 | 0 | if (nsamples < samples) { |
2095 | 0 | guint diff; |
2096 | |
|
2097 | 0 | diff = samples - nsamples; |
2098 | 0 | GST_DEBUG_OBJECT (bsink, "Clipped start: %u/%u samples", nsamples, |
2099 | 0 | samples); |
2100 | 0 | samples -= diff; |
2101 | 0 | offset += diff * bpf; |
2102 | 0 | } |
2103 | 0 | } |
2104 | | |
2105 | | /* positive playback rate, first sample is render_start, negative rate, first |
2106 | | * sample is render_stop. When no rate conversion is active, render exactly |
2107 | | * the amount of input samples to avoid aligning to rounding errors. */ |
2108 | 0 | if (G_LIKELY (bsink->segment.rate >= 0.0)) { |
2109 | 0 | sample_offset = render_start; |
2110 | 0 | if (G_LIKELY (bsink->segment.rate == 1.0)) |
2111 | 0 | render_stop = sample_offset + samples; |
2112 | 0 | } else { |
2113 | 0 | sample_offset = render_stop; |
2114 | 0 | if (bsink->segment.rate == -1.0) |
2115 | 0 | render_start = sample_offset + samples; |
2116 | 0 | } |
2117 | | |
2118 | | /* always resync after a discont */ |
2119 | 0 | if (G_UNLIKELY (GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DISCONT) || |
2120 | 0 | GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_RESYNC))) { |
2121 | 0 | GST_DEBUG_OBJECT (sink, "resync after discont/resync"); |
2122 | 0 | goto no_align; |
2123 | 0 | } |
2124 | | |
2125 | | /* resync when we don't know what to align the sample with */ |
2126 | 0 | if (G_UNLIKELY (sink->next_sample == -1)) { |
2127 | 0 | GST_DEBUG_OBJECT (sink, |
2128 | 0 | "no align possible: no previous sample position known"); |
2129 | 0 | goto no_align; |
2130 | 0 | } |
2131 | | |
2132 | 0 | align = gst_audio_base_sink_get_alignment (sink, sample_offset); |
2133 | 0 | sink->priv->last_align = align; |
2134 | | |
2135 | | /* apply alignment */ |
2136 | 0 | render_start += align; |
2137 | | |
2138 | | /* only align stop if we are not slaved to resample */ |
2139 | 0 | if (G_UNLIKELY (slaved |
2140 | 0 | && sink->priv->slave_method == GST_AUDIO_BASE_SINK_SLAVE_RESAMPLE)) { |
2141 | 0 | GST_DEBUG_OBJECT (sink, "no stop time align needed: we are slaved"); |
2142 | 0 | goto no_align; |
2143 | 0 | } |
2144 | 0 | render_stop += align; |
2145 | |
|
2146 | 0 | no_align: |
2147 | | /* number of target samples is difference between start and stop */ |
2148 | 0 | out_samples = render_stop - render_start; |
2149 | | |
2150 | | /* we render the first or last sample first, depending on the rate */ |
2151 | 0 | if (G_LIKELY (bsink->segment.rate >= 0.0)) |
2152 | 0 | sample_offset = render_start; |
2153 | 0 | else |
2154 | 0 | sample_offset = render_stop; |
2155 | |
|
2156 | 0 | GST_DEBUG_OBJECT (sink, "rendering at %" G_GUINT64_FORMAT " %d/%d", |
2157 | 0 | sample_offset, samples, out_samples); |
2158 | | |
2159 | | /* we need to accumulate over different runs for when we get interrupted */ |
2160 | 0 | accum = 0; |
2161 | 0 | align_next = TRUE; |
2162 | 0 | gst_buffer_map (buf, &info, GST_MAP_READ); |
2163 | 0 | do { |
2164 | 0 | written = |
2165 | 0 | gst_audio_ring_buffer_commit (ringbuf, &sample_offset, |
2166 | 0 | info.data + offset, samples, out_samples, &accum); |
2167 | |
|
2168 | 0 | GST_DEBUG_OBJECT (sink, "wrote %u of %u", written, samples); |
2169 | | /* if we wrote all, we're done */ |
2170 | 0 | if (G_LIKELY (written == samples)) |
2171 | 0 | break; |
2172 | | |
2173 | | /* else something interrupted us and we wait for preroll. */ |
2174 | 0 | if ((ret = gst_base_sink_wait_preroll (bsink)) != GST_FLOW_OK) |
2175 | 0 | goto stopping; |
2176 | | |
2177 | | /* if we got interrupted, we cannot assume that the next sample should |
2178 | | * be aligned to this one */ |
2179 | 0 | align_next = FALSE; |
2180 | | |
2181 | | /* update the output samples. FIXME, this will just skip them when pausing |
2182 | | * during trick mode */ |
2183 | 0 | if (out_samples > written) { |
2184 | 0 | out_samples -= written; |
2185 | 0 | accum = 0; |
2186 | 0 | } else |
2187 | 0 | break; |
2188 | | |
2189 | 0 | samples -= written; |
2190 | 0 | offset += written * bpf; |
2191 | 0 | } while (TRUE); |
2192 | 0 | gst_buffer_unmap (buf, &info); |
2193 | |
|
2194 | 0 | if (G_LIKELY (align_next)) |
2195 | 0 | sink->next_sample = sample_offset; |
2196 | 0 | else |
2197 | 0 | sink->next_sample = -1; |
2198 | |
|
2199 | 0 | GST_DEBUG_OBJECT (sink, "next sample expected at %" G_GUINT64_FORMAT, |
2200 | 0 | sink->next_sample); |
2201 | |
|
2202 | 0 | if (G_UNLIKELY (GST_CLOCK_TIME_IS_VALID (stop) |
2203 | 0 | && stop >= bsink->segment.stop)) { |
2204 | 0 | GST_DEBUG_OBJECT (sink, |
2205 | 0 | "start playback because we are at the end of segment"); |
2206 | 0 | gst_audio_base_sink_force_start (sink); |
2207 | 0 | } |
2208 | |
|
2209 | 0 | ret = GST_FLOW_OK; |
2210 | |
|
2211 | 0 | done: |
2212 | 0 | if (out) |
2213 | 0 | gst_buffer_unref (out); |
2214 | |
|
2215 | 0 | return ret; |
2216 | | |
2217 | | /* SPECIAL cases */ |
2218 | 0 | out_of_segment: |
2219 | 0 | { |
2220 | 0 | GST_DEBUG_OBJECT (sink, |
2221 | 0 | "dropping sample out of segment time %" GST_TIME_FORMAT ", start %" |
2222 | 0 | GST_TIME_FORMAT, GST_TIME_ARGS (time), |
2223 | 0 | GST_TIME_ARGS (bsink->segment.start)); |
2224 | 0 | ret = GST_FLOW_OK; |
2225 | 0 | goto done; |
2226 | 0 | } |
2227 | 0 | too_late: |
2228 | 0 | { |
2229 | 0 | GST_DEBUG_OBJECT (sink, "dropping late sample"); |
2230 | 0 | ret = GST_FLOW_OK; |
2231 | 0 | goto done; |
2232 | 0 | } |
2233 | | /* ERRORS */ |
2234 | 0 | payload_failed: |
2235 | 0 | { |
2236 | 0 | GST_ELEMENT_ERROR (sink, STREAM, FORMAT, (NULL), ("failed to payload.")); |
2237 | 0 | ret = GST_FLOW_ERROR; |
2238 | 0 | goto done; |
2239 | 0 | } |
2240 | 0 | wrong_state: |
2241 | 0 | { |
2242 | 0 | GST_DEBUG_OBJECT (sink, "ringbuffer not negotiated"); |
2243 | 0 | GST_ELEMENT_ERROR (sink, STREAM, FORMAT, (NULL), ("sink not negotiated.")); |
2244 | 0 | ret = GST_FLOW_NOT_NEGOTIATED; |
2245 | 0 | goto done; |
2246 | 0 | } |
2247 | 0 | wrong_size: |
2248 | 0 | { |
2249 | 0 | GST_DEBUG_OBJECT (sink, "wrong size"); |
2250 | 0 | GST_ELEMENT_ERROR (sink, STREAM, WRONG_TYPE, |
2251 | 0 | (NULL), ("sink received buffer of wrong size.")); |
2252 | 0 | ret = GST_FLOW_ERROR; |
2253 | 0 | goto done; |
2254 | 0 | } |
2255 | 0 | stopping: |
2256 | 0 | { |
2257 | 0 | GST_DEBUG_OBJECT (sink, "preroll got interrupted: %d (%s)", ret, |
2258 | 0 | gst_flow_get_name (ret)); |
2259 | 0 | gst_buffer_unmap (buf, &info); |
2260 | 0 | goto done; |
2261 | 0 | } |
2262 | 0 | sync_latency_failed: |
2263 | 0 | { |
2264 | 0 | GST_DEBUG_OBJECT (sink, "failed waiting for latency"); |
2265 | 0 | goto done; |
2266 | 0 | } |
2267 | 0 | } |
2268 | | |
2269 | | /** |
2270 | | * gst_audio_base_sink_create_ringbuffer: |
2271 | | * @sink: a #GstAudioBaseSink. |
2272 | | * |
2273 | | * Create and return the #GstAudioRingBuffer for @sink. This function will |
2274 | | * call the ::create_ringbuffer vmethod and will set @sink as the parent of |
2275 | | * the returned buffer (see gst_object_set_parent()). |
2276 | | * |
2277 | | * Returns: (transfer none) (nullable): The new ringbuffer of @sink. |
2278 | | */ |
2279 | | GstAudioRingBuffer * |
2280 | | gst_audio_base_sink_create_ringbuffer (GstAudioBaseSink * sink) |
2281 | 0 | { |
2282 | 0 | GstAudioBaseSinkClass *bclass; |
2283 | 0 | GstAudioRingBuffer *buffer = NULL; |
2284 | |
|
2285 | 0 | bclass = GST_AUDIO_BASE_SINK_GET_CLASS (sink); |
2286 | 0 | if (bclass->create_ringbuffer) |
2287 | 0 | buffer = bclass->create_ringbuffer (sink); |
2288 | |
|
2289 | 0 | if (buffer) |
2290 | 0 | gst_object_set_parent (GST_OBJECT (buffer), GST_OBJECT (sink)); |
2291 | |
|
2292 | 0 | return buffer; |
2293 | 0 | } |
2294 | | |
2295 | | static void |
2296 | | gst_audio_base_sink_callback (GstAudioRingBuffer * rbuf, guint8 * data, |
2297 | | guint len, gpointer user_data) |
2298 | 0 | { |
2299 | 0 | GstBaseSink *basesink; |
2300 | 0 | GstAudioBaseSink *sink; |
2301 | 0 | GstBuffer *buf = NULL; |
2302 | 0 | GstFlowReturn ret; |
2303 | 0 | gsize size; |
2304 | |
|
2305 | 0 | basesink = GST_BASE_SINK (user_data); |
2306 | 0 | sink = GST_AUDIO_BASE_SINK (user_data); |
2307 | |
|
2308 | 0 | GST_PAD_STREAM_LOCK (basesink->sinkpad); |
2309 | | |
2310 | | /* would be nice to arrange for pad_alloc_buffer to return data -- as it is we |
2311 | | * will copy twice, once into data, once into DMA */ |
2312 | 0 | GST_LOG_OBJECT (basesink, "pulling %u bytes offset %" G_GUINT64_FORMAT |
2313 | 0 | " to fill audio buffer", len, basesink->offset); |
2314 | 0 | ret = |
2315 | 0 | gst_pad_pull_range (basesink->sinkpad, basesink->segment.position, len, |
2316 | 0 | &buf); |
2317 | |
|
2318 | 0 | if (ret != GST_FLOW_OK) { |
2319 | 0 | if (ret == GST_FLOW_EOS) |
2320 | 0 | goto eos; |
2321 | 0 | else |
2322 | 0 | goto error; |
2323 | 0 | } |
2324 | | |
2325 | 0 | GST_BASE_SINK_PREROLL_LOCK (basesink); |
2326 | 0 | if (basesink->flushing) |
2327 | 0 | goto flushing; |
2328 | | |
2329 | | /* complete preroll and wait for PLAYING */ |
2330 | 0 | ret = gst_base_sink_do_preroll (basesink, GST_MINI_OBJECT_CAST (buf)); |
2331 | 0 | if (ret != GST_FLOW_OK) |
2332 | 0 | goto preroll_error; |
2333 | | |
2334 | 0 | size = gst_buffer_get_size (buf); |
2335 | |
|
2336 | 0 | if (len != size) { |
2337 | 0 | GST_INFO_OBJECT (basesink, |
2338 | 0 | "got different size than requested from sink pad: %u" |
2339 | 0 | " != %" G_GSIZE_FORMAT, len, size); |
2340 | 0 | len = MIN (size, len); |
2341 | 0 | } |
2342 | |
|
2343 | 0 | basesink->segment.position += len; |
2344 | |
|
2345 | 0 | gst_buffer_extract (buf, 0, data, len); |
2346 | 0 | GST_BASE_SINK_PREROLL_UNLOCK (basesink); |
2347 | |
|
2348 | 0 | GST_PAD_STREAM_UNLOCK (basesink->sinkpad); |
2349 | |
|
2350 | 0 | return; |
2351 | | |
2352 | 0 | error: |
2353 | 0 | { |
2354 | 0 | GST_WARNING_OBJECT (basesink, "Got flow '%s' but can't return it: %d", |
2355 | 0 | gst_flow_get_name (ret), ret); |
2356 | 0 | gst_audio_ring_buffer_pause (rbuf); |
2357 | 0 | GST_PAD_STREAM_UNLOCK (basesink->sinkpad); |
2358 | 0 | return; |
2359 | 0 | } |
2360 | 0 | eos: |
2361 | 0 | { |
2362 | | /* FIXME: this is not quite correct; we'll be called endlessly until |
2363 | | * the sink gets shut down; maybe we should set a flag somewhere, or |
2364 | | * set segment.stop and segment.duration to the last sample or so */ |
2365 | 0 | GST_DEBUG_OBJECT (sink, "EOS"); |
2366 | 0 | gst_audio_base_sink_drain (sink); |
2367 | 0 | gst_audio_ring_buffer_pause (rbuf); |
2368 | 0 | gst_element_post_message (GST_ELEMENT_CAST (sink), |
2369 | 0 | gst_message_new_eos (GST_OBJECT_CAST (sink))); |
2370 | 0 | GST_PAD_STREAM_UNLOCK (basesink->sinkpad); |
2371 | 0 | return; |
2372 | 0 | } |
2373 | 0 | flushing: |
2374 | 0 | { |
2375 | 0 | GST_DEBUG_OBJECT (sink, "we are flushing"); |
2376 | 0 | gst_audio_ring_buffer_pause (rbuf); |
2377 | 0 | GST_BASE_SINK_PREROLL_UNLOCK (basesink); |
2378 | 0 | GST_PAD_STREAM_UNLOCK (basesink->sinkpad); |
2379 | 0 | return; |
2380 | 0 | } |
2381 | 0 | preroll_error: |
2382 | 0 | { |
2383 | 0 | GST_DEBUG_OBJECT (sink, "error %s", gst_flow_get_name (ret)); |
2384 | 0 | gst_audio_ring_buffer_pause (rbuf); |
2385 | 0 | GST_BASE_SINK_PREROLL_UNLOCK (basesink); |
2386 | 0 | GST_PAD_STREAM_UNLOCK (basesink->sinkpad); |
2387 | 0 | return; |
2388 | 0 | } |
2389 | 0 | } |
2390 | | |
2391 | | static gboolean |
2392 | | gst_audio_base_sink_activate_pull (GstBaseSink * basesink, gboolean active) |
2393 | 0 | { |
2394 | 0 | gboolean ret; |
2395 | 0 | GstAudioBaseSink *sink = GST_AUDIO_BASE_SINK (basesink); |
2396 | |
|
2397 | 0 | if (active) { |
2398 | 0 | GST_DEBUG_OBJECT (basesink, "activating pull"); |
2399 | |
|
2400 | 0 | gst_audio_ring_buffer_set_callback (sink->ringbuffer, |
2401 | 0 | gst_audio_base_sink_callback, sink); |
2402 | |
|
2403 | 0 | ret = gst_audio_ring_buffer_activate (sink->ringbuffer, TRUE); |
2404 | 0 | } else { |
2405 | 0 | GST_DEBUG_OBJECT (basesink, "deactivating pull"); |
2406 | 0 | gst_audio_ring_buffer_set_callback (sink->ringbuffer, NULL, NULL); |
2407 | 0 | ret = gst_audio_ring_buffer_activate (sink->ringbuffer, FALSE); |
2408 | 0 | } |
2409 | |
|
2410 | 0 | return ret; |
2411 | 0 | } |
2412 | | |
2413 | | static GstStateChangeReturn |
2414 | | gst_audio_base_sink_change_state (GstElement * element, |
2415 | | GstStateChange transition) |
2416 | 0 | { |
2417 | 0 | GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS; |
2418 | 0 | GstAudioBaseSink *sink = GST_AUDIO_BASE_SINK (element); |
2419 | |
|
2420 | 0 | switch (transition) { |
2421 | 0 | case GST_STATE_CHANGE_NULL_TO_READY:{ |
2422 | 0 | GstAudioRingBuffer *rb; |
2423 | |
|
2424 | 0 | gst_audio_clock_reset (GST_AUDIO_CLOCK (sink->provided_clock), 0); |
2425 | 0 | rb = gst_audio_base_sink_create_ringbuffer (sink); |
2426 | 0 | if (rb == NULL) |
2427 | 0 | goto create_failed; |
2428 | | |
2429 | 0 | GST_OBJECT_LOCK (sink); |
2430 | 0 | sink->ringbuffer = rb; |
2431 | 0 | GST_OBJECT_UNLOCK (sink); |
2432 | |
|
2433 | 0 | if (!gst_audio_ring_buffer_open_device (sink->ringbuffer)) { |
2434 | 0 | GST_OBJECT_LOCK (sink); |
2435 | 0 | gst_object_unparent (GST_OBJECT_CAST (sink->ringbuffer)); |
2436 | 0 | sink->ringbuffer = NULL; |
2437 | 0 | GST_OBJECT_UNLOCK (sink); |
2438 | 0 | goto open_failed; |
2439 | 0 | } |
2440 | 0 | break; |
2441 | 0 | } |
2442 | 0 | case GST_STATE_CHANGE_READY_TO_PAUSED: |
2443 | 0 | gst_audio_base_sink_reset_sync (sink); |
2444 | 0 | gst_audio_ring_buffer_set_flushing (sink->ringbuffer, FALSE); |
2445 | 0 | gst_audio_ring_buffer_may_start (sink->ringbuffer, FALSE); |
2446 | | |
2447 | | /* Only post clock-provide messages if this is the clock that |
2448 | | * we've created. If the subclass has overridden it the subclass |
2449 | | * should post this messages whenever necessary */ |
2450 | 0 | if (gst_audio_base_sink_is_self_provided_clock (sink)) |
2451 | 0 | gst_element_post_message (element, |
2452 | 0 | gst_message_new_clock_provide (GST_OBJECT_CAST (element), |
2453 | 0 | sink->provided_clock, TRUE)); |
2454 | 0 | break; |
2455 | 0 | case GST_STATE_CHANGE_PAUSED_TO_PLAYING: |
2456 | 0 | { |
2457 | 0 | gboolean eos; |
2458 | |
|
2459 | 0 | GST_OBJECT_LOCK (sink); |
2460 | 0 | GST_DEBUG_OBJECT (sink, "ringbuffer may start now"); |
2461 | 0 | sink->priv->sync_latency = TRUE; |
2462 | 0 | eos = GST_BASE_SINK (sink)->eos; |
2463 | 0 | GST_OBJECT_UNLOCK (sink); |
2464 | |
|
2465 | 0 | gst_audio_ring_buffer_may_start (sink->ringbuffer, TRUE); |
2466 | 0 | if (GST_BASE_SINK_CAST (sink)->pad_mode == GST_PAD_MODE_PULL || |
2467 | 0 | g_atomic_int_get (&sink->eos_rendering) || eos) { |
2468 | | /* we always start the ringbuffer in pull mode immediately */ |
2469 | | /* sync rendering on eos needs running clock, |
2470 | | * and others need running clock when finished rendering eos */ |
2471 | 0 | gst_audio_ring_buffer_start (sink->ringbuffer); |
2472 | 0 | } |
2473 | 0 | break; |
2474 | 0 | } |
2475 | 0 | case GST_STATE_CHANGE_PLAYING_TO_PAUSED: |
2476 | | /* ringbuffer cannot start anymore */ |
2477 | 0 | gst_audio_ring_buffer_may_start (sink->ringbuffer, FALSE); |
2478 | 0 | gst_audio_ring_buffer_pause (sink->ringbuffer); |
2479 | |
|
2480 | 0 | GST_OBJECT_LOCK (sink); |
2481 | 0 | sink->priv->sync_latency = FALSE; |
2482 | 0 | GST_OBJECT_UNLOCK (sink); |
2483 | 0 | break; |
2484 | 0 | case GST_STATE_CHANGE_PAUSED_TO_READY: |
2485 | | /* Only post clock-lost messages if this is the clock that |
2486 | | * we've created. If the subclass has overridden it the subclass |
2487 | | * should post this messages whenever necessary */ |
2488 | 0 | if (gst_audio_base_sink_is_self_provided_clock (sink)) |
2489 | 0 | gst_element_post_message (element, |
2490 | 0 | gst_message_new_clock_lost (GST_OBJECT_CAST (element), |
2491 | 0 | sink->provided_clock)); |
2492 | | |
2493 | | /* make sure we unblock before calling the parent state change |
2494 | | * so it can grab the STREAM_LOCK */ |
2495 | 0 | gst_audio_ring_buffer_set_flushing (sink->ringbuffer, TRUE); |
2496 | 0 | break; |
2497 | 0 | default: |
2498 | 0 | break; |
2499 | 0 | } |
2500 | | |
2501 | 0 | ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); |
2502 | |
|
2503 | 0 | switch (transition) { |
2504 | 0 | case GST_STATE_CHANGE_PLAYING_TO_PAUSED: |
2505 | | /* stop slaving ourselves to the master, if any */ |
2506 | 0 | gst_clock_set_master (sink->provided_clock, NULL); |
2507 | 0 | break; |
2508 | 0 | case GST_STATE_CHANGE_PAUSED_TO_READY: |
2509 | 0 | gst_audio_ring_buffer_activate (sink->ringbuffer, FALSE); |
2510 | 0 | gst_audio_ring_buffer_release (sink->ringbuffer); |
2511 | 0 | break; |
2512 | 0 | case GST_STATE_CHANGE_READY_TO_NULL: |
2513 | | /* we release again here because the acquire happens when setting the |
2514 | | * caps, which happens before we commit the state to PAUSED and thus the |
2515 | | * PAUSED->READY state change (see above, where we release the ringbuffer) |
2516 | | * might not be called when we get here. */ |
2517 | 0 | gst_audio_ring_buffer_activate (sink->ringbuffer, FALSE); |
2518 | 0 | gst_audio_ring_buffer_release (sink->ringbuffer); |
2519 | 0 | gst_audio_ring_buffer_close_device (sink->ringbuffer); |
2520 | 0 | GST_OBJECT_LOCK (sink); |
2521 | 0 | gst_object_unparent (GST_OBJECT_CAST (sink->ringbuffer)); |
2522 | 0 | sink->ringbuffer = NULL; |
2523 | 0 | GST_OBJECT_UNLOCK (sink); |
2524 | 0 | break; |
2525 | 0 | default: |
2526 | 0 | break; |
2527 | 0 | } |
2528 | | |
2529 | 0 | return ret; |
2530 | | |
2531 | | /* ERRORS */ |
2532 | 0 | create_failed: |
2533 | 0 | { |
2534 | | /* subclass must post a meaningful error message */ |
2535 | 0 | GST_DEBUG_OBJECT (sink, "create failed"); |
2536 | 0 | return GST_STATE_CHANGE_FAILURE; |
2537 | 0 | } |
2538 | 0 | open_failed: |
2539 | 0 | { |
2540 | | /* subclass must post a meaningful error message */ |
2541 | 0 | GST_DEBUG_OBJECT (sink, "open failed"); |
2542 | 0 | return GST_STATE_CHANGE_FAILURE; |
2543 | 0 | } |
2544 | 0 | } |