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_PTS_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_PTS (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_PTS (ret) != timestamp) {
218 ret = gst_buffer_make_writable (ret);
219 GST_BUFFER_PTS (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_PTS (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 * This function does not know the audio rate, therefore the caller is
262 * responsible for re-setting the correct timestamp and duration to the
263 * buffer. However, timestamp will be preserved if trim == 0, and duration
264 * will also be preserved if there is no trimming to be done. Offset and
265 * offset end will be preserved / updated.
266 *
267 * After calling this function the caller does not own a reference to
268 * @buffer anymore.
269 *
270 * Returns: (transfer full): the truncated buffer or %NULL if the arguments
271 * were invalid
272 *
273 * Since: 1.16
274 */
275 GstBuffer *
gst_audio_buffer_truncate(GstBuffer * buffer,gint bpf,gsize trim,gsize samples)276 gst_audio_buffer_truncate (GstBuffer * buffer, gint bpf, gsize trim,
277 gsize samples)
278 {
279 GstAudioMeta *meta = NULL;
280 GstBuffer *ret = NULL;
281 gsize orig_samples;
282 gint i;
283 GstClockTime orig_ts, orig_offset;
284
285 g_return_val_if_fail (GST_IS_BUFFER (buffer), NULL);
286
287 meta = gst_buffer_get_audio_meta (buffer);
288 orig_samples = meta ? meta->samples : gst_buffer_get_size (buffer) / bpf;
289 orig_ts = GST_BUFFER_PTS (buffer);
290 orig_offset = GST_BUFFER_OFFSET (buffer);
291
292 g_return_val_if_fail (trim < orig_samples, NULL);
293 g_return_val_if_fail (samples == -1 || trim + samples <= orig_samples, NULL);
294
295 if (samples == -1)
296 samples = orig_samples - trim;
297
298 /* nothing to truncate */
299 if (samples == orig_samples)
300 return buffer;
301
302 GST_DEBUG ("Truncating %" G_GSIZE_FORMAT " to %" G_GSIZE_FORMAT
303 " (trim start %" G_GSIZE_FORMAT ", end %" G_GSIZE_FORMAT ")",
304 orig_samples, samples, trim, orig_samples - trim - samples);
305
306 if (!meta || meta->info.layout == GST_AUDIO_LAYOUT_INTERLEAVED) {
307 /* interleaved */
308 ret = gst_buffer_copy_region (buffer, GST_BUFFER_COPY_ALL, trim * bpf,
309 samples * bpf);
310 gst_buffer_unref (buffer);
311
312 if ((meta = gst_buffer_get_audio_meta (ret)))
313 meta->samples = samples;
314 } else {
315 /* non-interleaved */
316 ret = gst_buffer_make_writable (buffer);
317 meta = gst_buffer_get_audio_meta (ret);
318 meta->samples = samples;
319 for (i = 0; i < meta->info.channels; i++) {
320 meta->offsets[i] += trim * bpf / meta->info.channels;
321 }
322 }
323
324 GST_BUFFER_DTS (ret) = GST_CLOCK_TIME_NONE;
325 if (GST_CLOCK_TIME_IS_VALID (orig_ts) && trim == 0) {
326 GST_BUFFER_PTS (ret) = orig_ts;
327 } else {
328 GST_BUFFER_PTS (ret) = GST_CLOCK_TIME_NONE;
329 }
330 /* If duration was the same, it would have meant there's no trimming to be
331 * done, so we have an early return further up */
332 GST_BUFFER_DURATION (ret) = GST_CLOCK_TIME_NONE;
333 if (orig_offset != GST_BUFFER_OFFSET_NONE) {
334 GST_BUFFER_OFFSET (ret) = orig_offset + trim;
335 GST_BUFFER_OFFSET_END (ret) = GST_BUFFER_OFFSET (ret) + samples;
336 } else {
337 GST_BUFFER_OFFSET (ret) = GST_BUFFER_OFFSET_NONE;
338 GST_BUFFER_OFFSET_END (ret) = GST_BUFFER_OFFSET_NONE;
339 }
340
341 return ret;
342 }
343