1 /* GStreamer
2  * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
17  * Boston, MA 02110-1301, USA.
18  */
19 /**
20  * SECTION:gstaudio
21  * @title: GstAudio
22  * @short_description: Support library for audio elements
23  *
24  * This library contains some helper functions for audio elements.
25  */
26 
27 #ifdef HAVE_CONFIG_H
28 #  include "config.h"
29 #endif
30 
31 #include <string.h>
32 
33 #include "audio.h"
34 #include "audio-enumtypes.h"
35 
36 #ifndef GST_DISABLE_GST_DEBUG
37 #define GST_CAT_DEFAULT ensure_debug_category()
38 static GstDebugCategory *
ensure_debug_category(void)39 ensure_debug_category (void)
40 {
41   static gsize cat_gonce = 0;
42 
43   if (g_once_init_enter (&cat_gonce)) {
44     gsize cat_done;
45 
46     cat_done = (gsize) _gst_debug_category_new ("audio", 0, "audio library");
47 
48     g_once_init_leave (&cat_gonce, cat_done);
49   }
50 
51   return (GstDebugCategory *) cat_gonce;
52 }
53 #else
54 #define ensure_debug_category() /* NOOP */
55 #endif /* GST_DISABLE_GST_DEBUG */
56 
57 
58 /**
59  * gst_audio_buffer_clip:
60  * @buffer: (transfer full): The buffer to clip.
61  * @segment: Segment in %GST_FORMAT_TIME or %GST_FORMAT_DEFAULT to which
62  *           the buffer should be clipped.
63  * @rate: sample rate.
64  * @bpf: size of one audio frame in bytes. This is the size of one sample *
65  * number of channels.
66  *
67  * Clip the buffer to the given %GstSegment.
68  *
69  * After calling this function the caller does not own a reference to
70  * @buffer anymore.
71  *
72  * Returns: (transfer full): %NULL if the buffer is completely outside the configured segment,
73  * otherwise the clipped buffer is returned.
74  *
75  * If the buffer has no timestamp, it is assumed to be inside the segment and
76  * is not clipped
77  */
78 GstBuffer *
gst_audio_buffer_clip(GstBuffer * buffer,const GstSegment * segment,gint rate,gint bpf)79 gst_audio_buffer_clip (GstBuffer * buffer, const GstSegment * segment,
80     gint rate, gint bpf)
81 {
82   GstBuffer *ret;
83   GstAudioMeta *meta;
84   GstClockTime timestamp = GST_CLOCK_TIME_NONE, duration = GST_CLOCK_TIME_NONE;
85   guint64 offset = GST_BUFFER_OFFSET_NONE, offset_end = GST_BUFFER_OFFSET_NONE;
86   gsize trim, size, osize;
87   gboolean change_duration = TRUE, change_offset = TRUE, change_offset_end =
88       TRUE;
89 
90   g_return_val_if_fail (segment->format == GST_FORMAT_TIME ||
91       segment->format == GST_FORMAT_DEFAULT, buffer);
92   g_return_val_if_fail (GST_IS_BUFFER (buffer), NULL);
93 
94   if (!GST_BUFFER_TIMESTAMP_IS_VALID (buffer))
95     /* No timestamp - assume the buffer is completely in the segment */
96     return buffer;
97 
98   /* Get copies of the buffer metadata to change later.
99    * Calculate the missing values for the calculations,
100    * they won't be changed later though. */
101 
102   meta = gst_buffer_get_audio_meta (buffer);
103 
104   /* these variables measure samples */
105   trim = 0;
106   osize = size = meta ? meta->samples : (gst_buffer_get_size (buffer) / bpf);
107 
108   /* no data, nothing to clip */
109   if (!size)
110     return buffer;
111 
112   timestamp = GST_BUFFER_TIMESTAMP (buffer);
113   GST_DEBUG ("timestamp %" GST_TIME_FORMAT, GST_TIME_ARGS (timestamp));
114   if (GST_BUFFER_DURATION_IS_VALID (buffer)) {
115     duration = GST_BUFFER_DURATION (buffer);
116   } else {
117     change_duration = FALSE;
118     duration = gst_util_uint64_scale (size, GST_SECOND, rate);
119   }
120 
121   if (GST_BUFFER_OFFSET_IS_VALID (buffer)) {
122     offset = GST_BUFFER_OFFSET (buffer);
123   } else {
124     change_offset = FALSE;
125     offset = 0;
126   }
127 
128   if (GST_BUFFER_OFFSET_END_IS_VALID (buffer)) {
129     offset_end = GST_BUFFER_OFFSET_END (buffer);
130   } else {
131     change_offset_end = FALSE;
132     offset_end = offset + size;
133   }
134 
135   if (segment->format == GST_FORMAT_TIME) {
136     /* Handle clipping for GST_FORMAT_TIME */
137 
138     guint64 start, stop, cstart, cstop, diff;
139 
140     start = timestamp;
141     stop = timestamp + duration;
142 
143     if (gst_segment_clip (segment, GST_FORMAT_TIME,
144             start, stop, &cstart, &cstop)) {
145 
146       diff = cstart - start;
147       if (diff > 0) {
148         timestamp = cstart;
149 
150         if (change_duration)
151           duration -= diff;
152 
153         diff = gst_util_uint64_scale (diff, rate, GST_SECOND);
154         if (change_offset)
155           offset += diff;
156         trim += diff;
157         size -= diff;
158       }
159 
160       diff = stop - cstop;
161       if (diff > 0) {
162         /* duration is always valid if stop is valid */
163         duration -= diff;
164 
165         diff = gst_util_uint64_scale (diff, rate, GST_SECOND);
166         if (change_offset_end)
167           offset_end -= diff;
168         size -= diff;
169       }
170     } else {
171       gst_buffer_unref (buffer);
172       return NULL;
173     }
174   } else {
175     /* Handle clipping for GST_FORMAT_DEFAULT */
176     guint64 start, stop, cstart, cstop, diff;
177 
178     g_return_val_if_fail (GST_BUFFER_OFFSET_IS_VALID (buffer), buffer);
179 
180     start = offset;
181     stop = offset_end;
182 
183     if (gst_segment_clip (segment, GST_FORMAT_DEFAULT,
184             start, stop, &cstart, &cstop)) {
185 
186       diff = cstart - start;
187       if (diff > 0) {
188         offset = cstart;
189 
190         timestamp = gst_util_uint64_scale (cstart, GST_SECOND, rate);
191 
192         if (change_duration)
193           duration -= gst_util_uint64_scale (diff, GST_SECOND, rate);
194 
195         trim += diff;
196         size -= diff;
197       }
198 
199       diff = stop - cstop;
200       if (diff > 0) {
201         offset_end = cstop;
202 
203         if (change_duration)
204           duration -= gst_util_uint64_scale (diff, GST_SECOND, rate);
205 
206         size -= diff;
207       }
208     } else {
209       gst_buffer_unref (buffer);
210       return NULL;
211     }
212   }
213 
214   if (trim == 0 && size == osize) {
215     ret = buffer;
216 
217     if (GST_BUFFER_TIMESTAMP (ret) != timestamp) {
218       ret = gst_buffer_make_writable (ret);
219       GST_BUFFER_TIMESTAMP (ret) = timestamp;
220     }
221     if (GST_BUFFER_DURATION (ret) != duration) {
222       ret = gst_buffer_make_writable (ret);
223       GST_BUFFER_DURATION (ret) = duration;
224     }
225   } else {
226     /* cut out all the samples that are no longer relevant */
227     GST_DEBUG ("trim %" G_GSIZE_FORMAT " size %" G_GSIZE_FORMAT, trim, size);
228     ret = gst_audio_buffer_truncate (buffer, bpf, trim, size);
229 
230     GST_DEBUG ("timestamp %" GST_TIME_FORMAT, GST_TIME_ARGS (timestamp));
231     if (ret) {
232       GST_BUFFER_TIMESTAMP (ret) = timestamp;
233 
234       if (change_duration)
235         GST_BUFFER_DURATION (ret) = duration;
236       if (change_offset)
237         GST_BUFFER_OFFSET (ret) = offset;
238       if (change_offset_end)
239         GST_BUFFER_OFFSET_END (ret) = offset_end;
240     } else {
241       GST_ERROR ("gst_audio_buffer_truncate failed");
242     }
243   }
244   return ret;
245 }
246 
247 /**
248  * gst_audio_buffer_truncate:
249  * @buffer: (transfer full): The buffer to truncate.
250  * @bpf: size of one audio frame in bytes. This is the size of one sample *
251  * number of channels.
252  * @trim: the number of samples to remove from the beginning of the buffer
253  * @samples: the final number of samples that should exist in this buffer or -1
254  * to use all the remaining samples if you are only removing samples from the
255  * beginning.
256  *
257  * Truncate the buffer to finally have @samples number of samples, removing
258  * the necessary amount of samples from the end and @trim number of samples
259  * from the beginning.
260  *
261  * After calling this function the caller does not own a reference to
262  * @buffer anymore.
263  *
264  * Returns: (transfer full): the truncated buffer or %NULL if the arguments
265  *   were invalid
266  *
267  * Since: 1.16
268  */
269 GstBuffer *
gst_audio_buffer_truncate(GstBuffer * buffer,gint bpf,gsize trim,gsize samples)270 gst_audio_buffer_truncate (GstBuffer * buffer, gint bpf, gsize trim,
271     gsize samples)
272 {
273   GstAudioMeta *meta = NULL;
274   GstBuffer *ret = NULL;
275   gsize orig_samples;
276   gint i;
277 
278   g_return_val_if_fail (GST_IS_BUFFER (buffer), NULL);
279 
280   meta = gst_buffer_get_audio_meta (buffer);
281   orig_samples = meta ? meta->samples : gst_buffer_get_size (buffer) / bpf;
282 
283   g_return_val_if_fail (trim < orig_samples, NULL);
284   g_return_val_if_fail (samples == -1 || trim + samples <= orig_samples, NULL);
285 
286   if (samples == -1)
287     samples = orig_samples - trim;
288 
289   /* nothing to truncate */
290   if (samples == orig_samples)
291     return buffer;
292 
293   if (!meta || meta->info.layout == GST_AUDIO_LAYOUT_INTERLEAVED) {
294     /* interleaved */
295     ret = gst_buffer_copy_region (buffer, GST_BUFFER_COPY_ALL, trim * bpf,
296         samples * bpf);
297     gst_buffer_unref (buffer);
298 
299     if ((meta = gst_buffer_get_audio_meta (ret)))
300       meta->samples = samples;
301   } else {
302     /* non-interleaved */
303     ret = gst_buffer_make_writable (buffer);
304     meta = gst_buffer_get_audio_meta (buffer);
305     meta->samples = samples;
306     for (i = 0; i < meta->info.channels; i++) {
307       meta->offsets[i] += trim * bpf / meta->info.channels;
308     }
309   }
310 
311   return ret;
312 }
313