1 /* GStreamer
2  * Copyright (C) 2018 Collabora Ltd
3  *   @author George Kiagiadakis <george.kiagiadakis@collabora.com>
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Library General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Library General Public License for more details.
14  *
15  * You should have received a copy of the GNU Library General Public
16  * License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
18  * Boston, MA 02110-1301, USA.
19  */
20 
21 /**
22  * SECTION:gstplanaraudioadapter
23  * @title: GstPlanarAudioAdapter
24  * @short_description: adapts incoming audio data on a sink pad into chunks of N samples
25  *
26  * This class is similar to GstAdapter, but it is made to work with
27  * non-interleaved (planar) audio buffers. Before using, an audio format
28  * must be configured with gst_planar_audio_adapter_configure()
29  */
30 #ifdef HAVE_CONFIG_H
31 #include "config.h"
32 #endif
33 
34 #include "gstplanaraudioadapter.h"
35 
36 GST_DEBUG_CATEGORY_STATIC (gst_planar_audio_adapter_debug);
37 #define GST_CAT_DEFAULT gst_planar_audio_adapter_debug
38 
39 struct _GstPlanarAudioAdapter
40 {
41   GObject object;
42 
43   GstAudioInfo info;
44   GSList *buflist;
45   GSList *buflist_end;
46   gsize samples;
47   gsize skip;
48   guint count;
49 
50   GstClockTime pts;
51   guint64 pts_distance;
52   GstClockTime dts;
53   guint64 dts_distance;
54   guint64 offset;
55   guint64 offset_distance;
56 
57   GstClockTime pts_at_discont;
58   GstClockTime dts_at_discont;
59   guint64 offset_at_discont;
60 
61   guint64 distance_from_discont;
62 };
63 
64 struct _GstPlanarAudioAdapterClass
65 {
66   GObjectClass parent_class;
67 };
68 
69 #define _do_init \
70   GST_DEBUG_CATEGORY_INIT (gst_planar_audio_adapter_debug, "planaraudioadapter", \
71       0, "object to splice and merge audio buffers to desired size")
72 #define gst_planar_audio_adapter_parent_class parent_class
73 G_DEFINE_TYPE_WITH_CODE (GstPlanarAudioAdapter, gst_planar_audio_adapter,
74     G_TYPE_OBJECT, _do_init);
75 
76 static void gst_planar_audio_adapter_dispose (GObject * object);
77 
78 static void
gst_planar_audio_adapter_class_init(GstPlanarAudioAdapterClass * klass)79 gst_planar_audio_adapter_class_init (GstPlanarAudioAdapterClass * klass)
80 {
81   GObjectClass *object = G_OBJECT_CLASS (klass);
82 
83   object->dispose = gst_planar_audio_adapter_dispose;
84 }
85 
86 static void
gst_planar_audio_adapter_init(GstPlanarAudioAdapter * adapter)87 gst_planar_audio_adapter_init (GstPlanarAudioAdapter * adapter)
88 {
89   adapter->pts = GST_CLOCK_TIME_NONE;
90   adapter->pts_distance = 0;
91   adapter->dts = GST_CLOCK_TIME_NONE;
92   adapter->dts_distance = 0;
93   adapter->offset = GST_BUFFER_OFFSET_NONE;
94   adapter->offset_distance = 0;
95   adapter->pts_at_discont = GST_CLOCK_TIME_NONE;
96   adapter->dts_at_discont = GST_CLOCK_TIME_NONE;
97   adapter->offset_at_discont = GST_BUFFER_OFFSET_NONE;
98   adapter->distance_from_discont = 0;
99 }
100 
101 static void
gst_planar_audio_adapter_dispose(GObject * object)102 gst_planar_audio_adapter_dispose (GObject * object)
103 {
104   GstPlanarAudioAdapter *adapter = GST_PLANAR_AUDIO_ADAPTER (object);
105 
106   gst_planar_audio_adapter_clear (adapter);
107 
108   GST_CALL_PARENT (G_OBJECT_CLASS, dispose, (object));
109 }
110 
111 /**
112  * gst_planar_audio_adapter_new:
113  *
114  * Creates a new #GstPlanarAudioAdapter. Free with g_object_unref().
115  *
116  * Returns: (transfer full): a new #GstPlanarAudioAdapter
117  */
118 GstPlanarAudioAdapter *
gst_planar_audio_adapter_new(void)119 gst_planar_audio_adapter_new (void)
120 {
121   return g_object_new (GST_TYPE_PLANAR_AUDIO_ADAPTER, NULL);
122 }
123 
124 /**
125  * gst_planar_audio_adapter_configure:
126  * @adapter: a #GstPlanarAudioAdapter
127  * @info: a #GstAudioInfo describing the format of the audio data
128  *
129  * Sets up the @adapter to handle audio data of the specified audio format.
130  * Note that this will internally clear the adapter and re-initialize it.
131  */
132 void
gst_planar_audio_adapter_configure(GstPlanarAudioAdapter * adapter,const GstAudioInfo * info)133 gst_planar_audio_adapter_configure (GstPlanarAudioAdapter * adapter,
134     const GstAudioInfo * info)
135 {
136   g_return_if_fail (GST_IS_PLANAR_AUDIO_ADAPTER (adapter));
137   g_return_if_fail (info != NULL);
138   g_return_if_fail (GST_AUDIO_INFO_IS_VALID (info));
139   g_return_if_fail (info->layout == GST_AUDIO_LAYOUT_NON_INTERLEAVED);
140 
141   gst_planar_audio_adapter_clear (adapter);
142   adapter->info = *info;
143 }
144 
145 /**
146  * gst_planar_audio_adapter_clear:
147  * @adapter: a #GstPlanarAudioAdapter
148  *
149  * Removes all buffers from @adapter.
150  */
151 void
gst_planar_audio_adapter_clear(GstPlanarAudioAdapter * adapter)152 gst_planar_audio_adapter_clear (GstPlanarAudioAdapter * adapter)
153 {
154   g_return_if_fail (GST_IS_PLANAR_AUDIO_ADAPTER (adapter));
155 
156   g_slist_foreach (adapter->buflist, (GFunc) gst_mini_object_unref, NULL);
157   g_slist_free (adapter->buflist);
158   adapter->buflist = NULL;
159   adapter->buflist_end = NULL;
160   adapter->count = 0;
161   adapter->samples = 0;
162   adapter->skip = 0;
163 
164   adapter->pts = GST_CLOCK_TIME_NONE;
165   adapter->pts_distance = 0;
166   adapter->dts = GST_CLOCK_TIME_NONE;
167   adapter->dts_distance = 0;
168   adapter->offset = GST_BUFFER_OFFSET_NONE;
169   adapter->offset_distance = 0;
170   adapter->pts_at_discont = GST_CLOCK_TIME_NONE;
171   adapter->dts_at_discont = GST_CLOCK_TIME_NONE;
172   adapter->offset_at_discont = GST_BUFFER_OFFSET_NONE;
173   adapter->distance_from_discont = 0;
174 }
175 
176 static inline void
update_timestamps_and_offset(GstPlanarAudioAdapter * adapter,GstBuffer * buf)177 update_timestamps_and_offset (GstPlanarAudioAdapter * adapter, GstBuffer * buf)
178 {
179   GstClockTime pts, dts;
180   guint64 offset;
181 
182   pts = GST_BUFFER_PTS (buf);
183   if (GST_CLOCK_TIME_IS_VALID (pts)) {
184     GST_LOG_OBJECT (adapter, "new pts %" GST_TIME_FORMAT, GST_TIME_ARGS (pts));
185     adapter->pts = pts;
186     adapter->pts_distance = 0;
187   }
188   dts = GST_BUFFER_DTS (buf);
189   if (GST_CLOCK_TIME_IS_VALID (dts)) {
190     GST_LOG_OBJECT (adapter, "new dts %" GST_TIME_FORMAT, GST_TIME_ARGS (dts));
191     adapter->dts = dts;
192     adapter->dts_distance = 0;
193   }
194   offset = GST_BUFFER_OFFSET (buf);
195   if (offset != GST_BUFFER_OFFSET_NONE) {
196     GST_LOG_OBJECT (adapter, "new offset %" G_GUINT64_FORMAT, offset);
197     adapter->offset = offset;
198     adapter->offset_distance = 0;
199   }
200 
201   if (GST_BUFFER_IS_DISCONT (buf)) {
202     /* Take values as-is (might be NONE) */
203     adapter->pts_at_discont = pts;
204     adapter->dts_at_discont = dts;
205     adapter->offset_at_discont = offset;
206     adapter->distance_from_discont = 0;
207   }
208 }
209 
210 /**
211  * gst_planar_audio_adapter_push:
212  * @adapter: a #GstPlanarAudioAdapter
213  * @buf: (transfer full): a #GstBuffer to queue in the adapter
214  *
215  * Adds the data from @buf to the data stored inside @adapter and takes
216  * ownership of the buffer.
217  */
218 void
gst_planar_audio_adapter_push(GstPlanarAudioAdapter * adapter,GstBuffer * buf)219 gst_planar_audio_adapter_push (GstPlanarAudioAdapter * adapter, GstBuffer * buf)
220 {
221   GstAudioMeta *meta;
222   gsize samples;
223 
224   g_return_if_fail (GST_IS_PLANAR_AUDIO_ADAPTER (adapter));
225   g_return_if_fail (GST_AUDIO_INFO_IS_VALID (&adapter->info));
226   g_return_if_fail (GST_IS_BUFFER (buf));
227 
228   meta = gst_buffer_get_audio_meta (buf);
229   g_return_if_fail (meta != NULL);
230   g_return_if_fail (gst_audio_info_is_equal (&meta->info, &adapter->info));
231 
232   samples = meta->samples;
233   adapter->samples += samples;
234 
235   if (G_UNLIKELY (adapter->buflist == NULL)) {
236     GST_LOG_OBJECT (adapter, "pushing %p first %" G_GSIZE_FORMAT " samples",
237         buf, samples);
238     adapter->buflist = adapter->buflist_end = g_slist_append (NULL, buf);
239     update_timestamps_and_offset (adapter, buf);
240   } else {
241     /* Otherwise append to the end, and advance our end pointer */
242     GST_LOG_OBJECT (adapter, "pushing %p %" G_GSIZE_FORMAT " samples at end, "
243         "samples now %" G_GSIZE_FORMAT, buf, samples, adapter->samples);
244     adapter->buflist_end = g_slist_append (adapter->buflist_end, buf);
245     adapter->buflist_end = g_slist_next (adapter->buflist_end);
246   }
247   ++adapter->count;
248 }
249 
250 static void
gst_planar_audio_adapter_flush_unchecked(GstPlanarAudioAdapter * adapter,gsize to_flush)251 gst_planar_audio_adapter_flush_unchecked (GstPlanarAudioAdapter * adapter,
252     gsize to_flush)
253 {
254   GSList *g = adapter->buflist;
255   gsize cur_samples;
256 
257   /* clear state */
258   adapter->samples -= to_flush;
259 
260   /* take skip into account */
261   to_flush += adapter->skip;
262   /* distance is always at least the amount of skipped samples */
263   adapter->pts_distance -= adapter->skip;
264   adapter->dts_distance -= adapter->skip;
265   adapter->offset_distance -= adapter->skip;
266   adapter->distance_from_discont -= adapter->skip;
267 
268   g = adapter->buflist;
269   cur_samples = gst_buffer_get_audio_meta (g->data)->samples;
270   while (to_flush >= cur_samples) {
271     /* can skip whole buffer */
272     GST_LOG_OBJECT (adapter, "flushing out head buffer");
273     adapter->pts_distance += cur_samples;
274     adapter->dts_distance += cur_samples;
275     adapter->offset_distance += cur_samples;
276     adapter->distance_from_discont += cur_samples;
277     to_flush -= cur_samples;
278 
279     gst_buffer_unref (g->data);
280     g = g_slist_delete_link (g, g);
281     --adapter->count;
282 
283     if (G_UNLIKELY (g == NULL)) {
284       GST_LOG_OBJECT (adapter, "adapter empty now");
285       adapter->buflist_end = NULL;
286       break;
287     }
288     /* there is a new head buffer, update the timestamps */
289     update_timestamps_and_offset (adapter, g->data);
290     cur_samples = gst_buffer_get_audio_meta (g->data)->samples;
291   }
292   adapter->buflist = g;
293   /* account for the remaining bytes */
294   adapter->skip = to_flush;
295   adapter->pts_distance += to_flush;
296   adapter->dts_distance += to_flush;
297   adapter->offset_distance += to_flush;
298   adapter->distance_from_discont += to_flush;
299 }
300 
301 /**
302  * gst_planar_audio_adapter_flush:
303  * @adapter: a #GstPlanarAudioAdapter
304  * @to_flush: the number of samples to flush
305  *
306  * Flushes the first @to_flush samples in the @adapter. The caller must ensure
307  * that at least this many samples are available.
308  */
309 void
gst_planar_audio_adapter_flush(GstPlanarAudioAdapter * adapter,gsize to_flush)310 gst_planar_audio_adapter_flush (GstPlanarAudioAdapter * adapter, gsize to_flush)
311 {
312   g_return_if_fail (GST_IS_PLANAR_AUDIO_ADAPTER (adapter));
313   g_return_if_fail (to_flush <= adapter->samples);
314 
315   /* flushing out 0 bytes will do nothing */
316   if (G_UNLIKELY (to_flush == 0))
317     return;
318 
319   gst_planar_audio_adapter_flush_unchecked (adapter, to_flush);
320 }
321 
322 /**
323  * gst_planar_audio_adapter_get_buffer:
324  * @adapter: a #GstPlanarAudioAdapter
325  * @nsamples: the number of samples to get
326  * @flags: hint the intended use of the returned buffer
327  *
328  * Returns a #GstBuffer containing the first @nsamples of the @adapter, but
329  * does not flush them from the adapter.
330  * Use gst_planar_audio_adapter_take_buffer() for flushing at the same time.
331  *
332  * The map @flags can be used to give an optimization hint to this function.
333  * When the requested buffer is meant to be mapped only for reading, it might
334  * be possible to avoid copying memory in some cases.
335  *
336  * Caller owns a reference to the returned buffer. gst_buffer_unref() after
337  * usage.
338  *
339  * Free-function: gst_buffer_unref
340  *
341  * Returns: (transfer full) (nullable): a #GstBuffer containing the first
342  *     @nsamples of the adapter, or %NULL if @nsamples samples are not
343  *     available. gst_buffer_unref() when no longer needed.
344  */
345 GstBuffer *
gst_planar_audio_adapter_get_buffer(GstPlanarAudioAdapter * adapter,gsize nsamples,GstMapFlags flags)346 gst_planar_audio_adapter_get_buffer (GstPlanarAudioAdapter * adapter,
347     gsize nsamples, GstMapFlags flags)
348 {
349   GstBuffer *buffer = NULL;
350   GstBuffer *cur;
351   gsize hsamples, skip;
352 
353   g_return_val_if_fail (GST_IS_PLANAR_AUDIO_ADAPTER (adapter), NULL);
354   g_return_val_if_fail (GST_AUDIO_INFO_IS_VALID (&adapter->info), NULL);
355   g_return_val_if_fail (nsamples > 0, NULL);
356 
357   GST_LOG_OBJECT (adapter, "getting buffer of %" G_GSIZE_FORMAT " samples",
358       nsamples);
359 
360   /* we don't have enough data, return NULL. This is unlikely
361    * as one usually does an _available() first instead of grabbing a
362    * random size. */
363   if (G_UNLIKELY (nsamples > adapter->samples))
364     return NULL;
365 
366   cur = adapter->buflist->data;
367   skip = adapter->skip;
368   hsamples = gst_buffer_get_audio_meta (cur)->samples;
369 
370 
371   if (skip == 0 && hsamples == nsamples) {
372     /* our head buffer fits exactly the requirements */
373     GST_LOG_OBJECT (adapter, "providing buffer of %" G_GSIZE_FORMAT " samples"
374         " as head buffer", nsamples);
375 
376     buffer = gst_buffer_ref (cur);
377 
378   } else if (hsamples >= nsamples + skip && !(flags & GST_MAP_WRITE)) {
379     /* return a buffer with the same data as our head buffer but with
380      * a modified GstAudioMeta that maps only the parts of the planes
381      * that should be made available to the caller. This is more efficient
382      * for reading (no mem copy), but will hit performance if the caller
383      * decides to map for writing or otherwise do a deep copy */
384     GST_LOG_OBJECT (adapter, "providing buffer of %" G_GSIZE_FORMAT " samples"
385         " via copy region", nsamples);
386 
387     buffer = gst_buffer_copy_region (cur, GST_BUFFER_COPY_ALL, 0, -1);
388     gst_audio_buffer_truncate (buffer, adapter->info.bpf, skip, nsamples);
389 
390   } else {
391     gint c, bps;
392     GstAudioMeta *meta;
393 
394     /* construct a buffer with concatenated memory chunks from the appropriate
395      * places. These memories will be copied into a single memory chunk
396      * as soon as the buffer is mapped */
397     GST_LOG_OBJECT (adapter, "providing buffer of %" G_GSIZE_FORMAT " samples"
398         " via memory concatenation", nsamples);
399 
400     bps = adapter->info.finfo->width / 8;
401 
402     for (c = 0; c < adapter->info.channels; c++) {
403       gsize need = nsamples;
404       gsize cur_skip = skip;
405       gsize take_from_cur;
406       GSList *cur_node = adapter->buflist;
407 
408       while (need > 0) {
409         cur = cur_node->data;
410         meta = gst_buffer_get_audio_meta (cur);
411         take_from_cur = need > (meta->samples - cur_skip) ?
412             meta->samples - cur_skip : need;
413 
414         cur = gst_buffer_copy_region (cur, GST_BUFFER_COPY_MEMORY,
415             meta->offsets[c] + cur_skip * bps, take_from_cur * bps);
416 
417         if (!buffer)
418           buffer = cur;
419         else
420           gst_buffer_append (buffer, cur);
421 
422         need -= take_from_cur;
423         cur_skip = 0;
424         cur_node = g_slist_next (cur_node);
425       }
426     }
427 
428     gst_buffer_add_audio_meta (buffer, &adapter->info, nsamples, NULL);
429   }
430 
431   return buffer;
432 }
433 
434 /**
435  * gst_planar_audio_adapter_take_buffer:
436  * @adapter: a #GstPlanarAudioAdapter
437  * @nsamples: the number of samples to take
438  * @flags: hint the intended use of the returned buffer
439  *
440  * Returns a #GstBuffer containing the first @nsamples bytes of the
441  * @adapter. The returned bytes will be flushed from the adapter.
442  *
443  * See gst_planar_audio_adapter_get_buffer() for more details.
444  *
445  * Caller owns a reference to the returned buffer. gst_buffer_unref() after
446  * usage.
447  *
448  * Free-function: gst_buffer_unref
449  *
450  * Returns: (transfer full) (nullable): a #GstBuffer containing the first
451  *     @nsamples of the adapter, or %NULL if @nsamples samples are not
452  *     available. gst_buffer_unref() when no longer needed.
453  */
454 GstBuffer *
gst_planar_audio_adapter_take_buffer(GstPlanarAudioAdapter * adapter,gsize nsamples,GstMapFlags flags)455 gst_planar_audio_adapter_take_buffer (GstPlanarAudioAdapter * adapter,
456     gsize nsamples, GstMapFlags flags)
457 {
458   GstBuffer *buffer;
459 
460   buffer = gst_planar_audio_adapter_get_buffer (adapter, nsamples, flags);
461   if (buffer)
462     gst_planar_audio_adapter_flush_unchecked (adapter, nsamples);
463 
464   return buffer;
465 }
466 
467 /**
468  * gst_planar_audio_adapter_available:
469  * @adapter: a #GstPlanarAudioAdapter
470  *
471  * Gets the maximum amount of samples available, that is it returns the maximum
472  * value that can be supplied to gst_planar_audio_adapter_get_buffer() without
473  * that function returning %NULL.
474  *
475  * Returns: number of samples available in @adapter
476  */
477 gsize
gst_planar_audio_adapter_available(GstPlanarAudioAdapter * adapter)478 gst_planar_audio_adapter_available (GstPlanarAudioAdapter * adapter)
479 {
480   g_return_val_if_fail (GST_IS_PLANAR_AUDIO_ADAPTER (adapter), 0);
481 
482   return adapter->samples;
483 }
484 
485 /**
486  * gst_planar_audio_adapter_get_distance_from_discont:
487  * @adapter: a #GstPlanarAudioAdapter
488  *
489  * Get the distance in samples since the last buffer with the
490  * %GST_BUFFER_FLAG_DISCONT flag.
491  *
492  * The distance will be reset to 0 for all buffers with
493  * %GST_BUFFER_FLAG_DISCONT on them, and then calculated for all other
494  * following buffers based on their size.
495  *
496  * Returns: The offset. Can be %GST_BUFFER_OFFSET_NONE.
497  */
498 guint64
gst_planar_audio_adapter_distance_from_discont(GstPlanarAudioAdapter * adapter)499 gst_planar_audio_adapter_distance_from_discont (GstPlanarAudioAdapter * adapter)
500 {
501   return adapter->distance_from_discont;
502 }
503 
504 /**
505  * gst_planar_audio_adapter_offset_at_discont:
506  * @adapter: a #GstPlanarAudioAdapter
507  *
508  * Get the offset that was on the last buffer with the GST_BUFFER_FLAG_DISCONT
509  * flag, or GST_BUFFER_OFFSET_NONE.
510  *
511  * Returns: The offset at the last discont or GST_BUFFER_OFFSET_NONE.
512  */
513 guint64
gst_planar_audio_adapter_offset_at_discont(GstPlanarAudioAdapter * adapter)514 gst_planar_audio_adapter_offset_at_discont (GstPlanarAudioAdapter * adapter)
515 {
516   g_return_val_if_fail (GST_IS_PLANAR_AUDIO_ADAPTER (adapter),
517       GST_BUFFER_OFFSET_NONE);
518 
519   return adapter->offset_at_discont;
520 }
521 
522 /**
523  * gst_planar_audio_adapter_pts_at_discont:
524  * @adapter: a #GstPlanarAudioAdapter
525  *
526  * Get the PTS that was on the last buffer with the GST_BUFFER_FLAG_DISCONT
527  * flag, or GST_CLOCK_TIME_NONE.
528  *
529  * Returns: The PTS at the last discont or GST_CLOCK_TIME_NONE.
530  */
531 GstClockTime
gst_planar_audio_adapter_pts_at_discont(GstPlanarAudioAdapter * adapter)532 gst_planar_audio_adapter_pts_at_discont (GstPlanarAudioAdapter * adapter)
533 {
534   g_return_val_if_fail (GST_IS_PLANAR_AUDIO_ADAPTER (adapter),
535       GST_CLOCK_TIME_NONE);
536 
537   return adapter->pts_at_discont;
538 }
539 
540 /**
541  * gst_planar_audio_adapter_dts_at_discont:
542  * @adapter: a #GstPlanarAudioAdapter
543  *
544  * Get the DTS that was on the last buffer with the GST_BUFFER_FLAG_DISCONT
545  * flag, or GST_CLOCK_TIME_NONE.
546  *
547  * Returns: The DTS at the last discont or GST_CLOCK_TIME_NONE.
548  */
549 GstClockTime
gst_planar_audio_adapter_dts_at_discont(GstPlanarAudioAdapter * adapter)550 gst_planar_audio_adapter_dts_at_discont (GstPlanarAudioAdapter * adapter)
551 {
552   g_return_val_if_fail (GST_IS_PLANAR_AUDIO_ADAPTER (adapter),
553       GST_CLOCK_TIME_NONE);
554 
555   return adapter->dts_at_discont;
556 }
557 
558 /**
559  * gst_planar_audio_adapter_prev_offset:
560  * @adapter: a #GstPlanarAudioAdapter
561  * @distance: (out) (allow-none): pointer to a location for distance, or %NULL
562  *
563  * Get the offset that was before the current sample in the adapter. When
564  * @distance is given, the amount of samples between the offset and the current
565  * position is returned.
566  *
567  * The offset is reset to GST_BUFFER_OFFSET_NONE and the distance is set to 0
568  * when the adapter is first created or when it is cleared. This also means that
569  * before the first sample with an offset is removed from the adapter, the
570  * offset and distance returned are GST_BUFFER_OFFSET_NONE and 0 respectively.
571  *
572  * Returns: The previous seen offset.
573  */
574 guint64
gst_planar_audio_adapter_prev_offset(GstPlanarAudioAdapter * adapter,guint64 * distance)575 gst_planar_audio_adapter_prev_offset (GstPlanarAudioAdapter * adapter,
576     guint64 * distance)
577 {
578   g_return_val_if_fail (GST_IS_PLANAR_AUDIO_ADAPTER (adapter),
579       GST_BUFFER_OFFSET_NONE);
580 
581   if (distance)
582     *distance = adapter->offset_distance;
583 
584   return adapter->offset;
585 }
586 
587 /**
588  * gst_planar_audio_adapter_prev_pts:
589  * @adapter: a #GstPlanarAudioAdapter
590  * @distance: (out) (allow-none): pointer to location for distance, or %NULL
591  *
592  * Get the pts that was before the current sample in the adapter. When
593  * @distance is given, the amount of samples between the pts and the current
594  * position is returned.
595  *
596  * The pts is reset to GST_CLOCK_TIME_NONE and the distance is set to 0 when
597  * the adapter is first created or when it is cleared. This also means that before
598  * the first sample with a pts is removed from the adapter, the pts
599  * and distance returned are GST_CLOCK_TIME_NONE and 0 respectively.
600  *
601  * Returns: The previously seen pts.
602  */
603 GstClockTime
gst_planar_audio_adapter_prev_pts(GstPlanarAudioAdapter * adapter,guint64 * distance)604 gst_planar_audio_adapter_prev_pts (GstPlanarAudioAdapter * adapter,
605     guint64 * distance)
606 {
607   g_return_val_if_fail (GST_IS_PLANAR_AUDIO_ADAPTER (adapter),
608       GST_CLOCK_TIME_NONE);
609 
610   if (distance)
611     *distance = adapter->pts_distance;
612 
613   return adapter->pts;
614 }
615 
616 /**
617  * gst_planar_audio_adapter_prev_dts:
618  * @adapter: a #GstPlanarAudioAdapter
619  * @distance: (out) (allow-none): pointer to location for distance, or %NULL
620  *
621  * Get the dts that was before the current sample in the adapter. When
622  * @distance is given, the amount of bytes between the dts and the current
623  * position is returned.
624  *
625  * The dts is reset to GST_CLOCK_TIME_NONE and the distance is set to 0 when
626  * the adapter is first created or when it is cleared. This also means that
627  * before the first sample with a dts is removed from the adapter, the dts
628  * and distance returned are GST_CLOCK_TIME_NONE and 0 respectively.
629  *
630  * Returns: The previously seen dts.
631  */
632 GstClockTime
gst_planar_audio_adapter_prev_dts(GstPlanarAudioAdapter * adapter,guint64 * distance)633 gst_planar_audio_adapter_prev_dts (GstPlanarAudioAdapter * adapter,
634     guint64 * distance)
635 {
636   g_return_val_if_fail (GST_IS_PLANAR_AUDIO_ADAPTER (adapter),
637       GST_CLOCK_TIME_NONE);
638 
639   if (distance)
640     *distance = adapter->dts_distance;
641 
642   return adapter->dts;
643 }
644