/src/gstreamer/subprojects/gst-plugins-base/gst-libs/gst/audio/audio.c
Line | Count | Source |
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 * |
39 | | ensure_debug_category (void) |
40 | 0 | { |
41 | 0 | static gsize cat_gonce = 0; |
42 | |
|
43 | 0 | if (g_once_init_enter (&cat_gonce)) { |
44 | 0 | gsize cat_done; |
45 | |
|
46 | 0 | cat_done = (gsize) _gst_debug_category_new ("audio", 0, "audio library"); |
47 | |
|
48 | 0 | g_once_init_leave (&cat_gonce, cat_done); |
49 | 0 | } |
50 | |
|
51 | 0 | return (GstDebugCategory *) cat_gonce; |
52 | 0 | } |
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) (nullable): %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 * |
79 | | gst_audio_buffer_clip (GstBuffer * buffer, const GstSegment * segment, |
80 | | gint rate, gint bpf) |
81 | 68 | { |
82 | 68 | GstBuffer *ret; |
83 | 68 | GstAudioMeta *meta; |
84 | 68 | GstClockTime timestamp = GST_CLOCK_TIME_NONE, duration = GST_CLOCK_TIME_NONE; |
85 | 68 | guint64 offset = GST_BUFFER_OFFSET_NONE, offset_end = GST_BUFFER_OFFSET_NONE; |
86 | 68 | gsize trim, size, osize; |
87 | 68 | gboolean change_duration = TRUE, change_offset = TRUE, change_offset_end = |
88 | 68 | TRUE; |
89 | | |
90 | 68 | g_return_val_if_fail (segment->format == GST_FORMAT_TIME || |
91 | 68 | segment->format == GST_FORMAT_DEFAULT, buffer); |
92 | 68 | g_return_val_if_fail (GST_IS_BUFFER (buffer), NULL); |
93 | | |
94 | 68 | if (!GST_BUFFER_PTS_IS_VALID (buffer)) |
95 | | /* No timestamp - assume the buffer is completely in the segment */ |
96 | 0 | 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 | 68 | meta = gst_buffer_get_audio_meta (buffer); |
103 | | |
104 | | /* these variables measure samples */ |
105 | 68 | trim = 0; |
106 | 68 | osize = size = meta ? meta->samples : (gst_buffer_get_size (buffer) / bpf); |
107 | | |
108 | | /* no data, nothing to clip */ |
109 | 68 | if (!size) |
110 | 0 | return buffer; |
111 | | |
112 | 68 | timestamp = GST_BUFFER_PTS (buffer); |
113 | 68 | GST_DEBUG ("timestamp %" GST_TIME_FORMAT, GST_TIME_ARGS (timestamp)); |
114 | 68 | if (GST_BUFFER_DURATION_IS_VALID (buffer)) { |
115 | 68 | duration = GST_BUFFER_DURATION (buffer); |
116 | 68 | } else { |
117 | 0 | change_duration = FALSE; |
118 | 0 | duration = gst_util_uint64_scale (size, GST_SECOND, rate); |
119 | 0 | } |
120 | | |
121 | 68 | if (GST_BUFFER_OFFSET_IS_VALID (buffer)) { |
122 | 0 | offset = GST_BUFFER_OFFSET (buffer); |
123 | 68 | } else { |
124 | 68 | change_offset = FALSE; |
125 | 68 | offset = 0; |
126 | 68 | } |
127 | | |
128 | 68 | if (GST_BUFFER_OFFSET_END_IS_VALID (buffer)) { |
129 | 0 | offset_end = GST_BUFFER_OFFSET_END (buffer); |
130 | 68 | } else { |
131 | 68 | change_offset_end = FALSE; |
132 | 68 | offset_end = offset + size; |
133 | 68 | } |
134 | | |
135 | 68 | if (segment->format == GST_FORMAT_TIME) { |
136 | | /* Handle clipping for GST_FORMAT_TIME */ |
137 | | |
138 | 68 | guint64 start, stop, cstart, cstop, diff; |
139 | | |
140 | 68 | start = timestamp; |
141 | 68 | stop = timestamp + duration; |
142 | | |
143 | 68 | if (gst_segment_clip (segment, GST_FORMAT_TIME, |
144 | 68 | start, stop, &cstart, &cstop)) { |
145 | | |
146 | 68 | diff = cstart - start; |
147 | 68 | if (diff > 0) { |
148 | 0 | timestamp = cstart; |
149 | |
|
150 | 0 | if (change_duration) |
151 | 0 | duration -= diff; |
152 | |
|
153 | 0 | diff = gst_util_uint64_scale (diff, rate, GST_SECOND); |
154 | 0 | if (change_offset) |
155 | 0 | offset += diff; |
156 | 0 | trim += diff; |
157 | 0 | size -= diff; |
158 | 0 | } |
159 | | |
160 | 68 | diff = stop - cstop; |
161 | 68 | if (diff > 0) { |
162 | | /* duration is always valid if stop is valid */ |
163 | 0 | duration -= diff; |
164 | |
|
165 | 0 | diff = gst_util_uint64_scale (diff, rate, GST_SECOND); |
166 | 0 | if (change_offset_end) |
167 | 0 | offset_end -= diff; |
168 | 0 | size -= diff; |
169 | 0 | } |
170 | 68 | } else { |
171 | 0 | gst_buffer_unref (buffer); |
172 | 0 | return NULL; |
173 | 0 | } |
174 | 68 | } else { |
175 | | /* Handle clipping for GST_FORMAT_DEFAULT */ |
176 | 0 | guint64 start, stop, cstart, cstop, diff; |
177 | |
|
178 | 0 | g_return_val_if_fail (GST_BUFFER_OFFSET_IS_VALID (buffer), buffer); |
179 | | |
180 | 0 | start = offset; |
181 | 0 | stop = offset_end; |
182 | |
|
183 | 0 | if (gst_segment_clip (segment, GST_FORMAT_DEFAULT, |
184 | 0 | start, stop, &cstart, &cstop)) { |
185 | |
|
186 | 0 | diff = cstart - start; |
187 | 0 | if (diff > 0) { |
188 | 0 | offset = cstart; |
189 | |
|
190 | 0 | timestamp = gst_util_uint64_scale (cstart, GST_SECOND, rate); |
191 | |
|
192 | 0 | if (change_duration) |
193 | 0 | duration -= gst_util_uint64_scale (diff, GST_SECOND, rate); |
194 | |
|
195 | 0 | trim += diff; |
196 | 0 | size -= diff; |
197 | 0 | } |
198 | |
|
199 | 0 | diff = stop - cstop; |
200 | 0 | if (diff > 0) { |
201 | 0 | offset_end = cstop; |
202 | |
|
203 | 0 | if (change_duration) |
204 | 0 | duration -= gst_util_uint64_scale (diff, GST_SECOND, rate); |
205 | |
|
206 | 0 | size -= diff; |
207 | 0 | } |
208 | 0 | } else { |
209 | 0 | gst_buffer_unref (buffer); |
210 | 0 | return NULL; |
211 | 0 | } |
212 | 0 | } |
213 | | |
214 | 68 | if (trim == 0 && size == osize) { |
215 | 68 | ret = buffer; |
216 | | |
217 | 68 | if (GST_BUFFER_PTS (ret) != timestamp) { |
218 | 0 | ret = gst_buffer_make_writable (ret); |
219 | 0 | GST_BUFFER_PTS (ret) = timestamp; |
220 | 0 | } |
221 | 68 | if (GST_BUFFER_DURATION (ret) != duration) { |
222 | 0 | ret = gst_buffer_make_writable (ret); |
223 | 0 | GST_BUFFER_DURATION (ret) = duration; |
224 | 0 | } |
225 | 68 | } else { |
226 | | /* cut out all the samples that are no longer relevant */ |
227 | 0 | GST_DEBUG ("trim %" G_GSIZE_FORMAT " size %" G_GSIZE_FORMAT, trim, size); |
228 | 0 | ret = gst_audio_buffer_truncate (buffer, bpf, trim, size); |
229 | |
|
230 | 0 | GST_DEBUG ("timestamp %" GST_TIME_FORMAT, GST_TIME_ARGS (timestamp)); |
231 | 0 | if (ret) { |
232 | 0 | GST_BUFFER_PTS (ret) = timestamp; |
233 | |
|
234 | 0 | if (change_duration) |
235 | 0 | GST_BUFFER_DURATION (ret) = duration; |
236 | 0 | if (change_offset) |
237 | 0 | GST_BUFFER_OFFSET (ret) = offset; |
238 | 0 | if (change_offset_end) |
239 | 0 | GST_BUFFER_OFFSET_END (ret) = offset_end; |
240 | 0 | } else { |
241 | 0 | GST_ERROR ("gst_audio_buffer_truncate failed"); |
242 | 0 | } |
243 | 0 | } |
244 | 68 | return ret; |
245 | 68 | } |
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 |
271 | | * |
272 | | * Since: 1.16 |
273 | | */ |
274 | | GstBuffer * |
275 | | gst_audio_buffer_truncate (GstBuffer * buffer, gint bpf, gsize trim, |
276 | | gsize samples) |
277 | 0 | { |
278 | 0 | GstAudioMeta *meta = NULL; |
279 | 0 | GstBuffer *ret = NULL; |
280 | 0 | gsize orig_samples; |
281 | 0 | gint i; |
282 | 0 | GstClockTime orig_ts, orig_offset; |
283 | |
|
284 | 0 | g_return_val_if_fail (GST_IS_BUFFER (buffer), NULL); |
285 | | |
286 | 0 | meta = gst_buffer_get_audio_meta (buffer); |
287 | 0 | orig_samples = meta ? meta->samples : gst_buffer_get_size (buffer) / bpf; |
288 | 0 | orig_ts = GST_BUFFER_PTS (buffer); |
289 | 0 | orig_offset = GST_BUFFER_OFFSET (buffer); |
290 | |
|
291 | 0 | g_return_val_if_fail (trim < orig_samples, NULL); |
292 | 0 | g_return_val_if_fail (samples == -1 || trim + samples <= orig_samples, NULL); |
293 | | |
294 | 0 | if (samples == -1) |
295 | 0 | samples = orig_samples - trim; |
296 | | |
297 | | /* nothing to truncate */ |
298 | 0 | if (samples == orig_samples) |
299 | 0 | return buffer; |
300 | | |
301 | 0 | GST_DEBUG ("Truncating %" G_GSIZE_FORMAT " to %" G_GSIZE_FORMAT |
302 | 0 | " (trim start %" G_GSIZE_FORMAT ", end %" G_GSIZE_FORMAT ")", |
303 | 0 | orig_samples, samples, trim, orig_samples - trim - samples); |
304 | |
|
305 | 0 | if (!meta || meta->info.layout == GST_AUDIO_LAYOUT_INTERLEAVED) { |
306 | | /* interleaved */ |
307 | 0 | ret = gst_buffer_copy_region (buffer, GST_BUFFER_COPY_ALL, trim * bpf, |
308 | 0 | samples * bpf); |
309 | 0 | gst_buffer_unref (buffer); |
310 | |
|
311 | 0 | if ((meta = gst_buffer_get_audio_meta (ret))) |
312 | 0 | meta->samples = samples; |
313 | 0 | } else { |
314 | | /* non-interleaved */ |
315 | 0 | ret = gst_buffer_make_writable (buffer); |
316 | 0 | meta = gst_buffer_get_audio_meta (ret); |
317 | 0 | meta->samples = samples; |
318 | 0 | for (i = 0; i < meta->info.channels; i++) { |
319 | 0 | meta->offsets[i] += trim * bpf / meta->info.channels; |
320 | 0 | } |
321 | 0 | } |
322 | |
|
323 | 0 | GST_BUFFER_DTS (ret) = GST_CLOCK_TIME_NONE; |
324 | 0 | if (GST_CLOCK_TIME_IS_VALID (orig_ts) && trim == 0) { |
325 | 0 | GST_BUFFER_PTS (ret) = orig_ts; |
326 | 0 | } else { |
327 | 0 | GST_BUFFER_PTS (ret) = GST_CLOCK_TIME_NONE; |
328 | 0 | } |
329 | | /* If duration was the same, it would have meant there's no trimming to be |
330 | | * done, so we have an early return further up */ |
331 | 0 | GST_BUFFER_DURATION (ret) = GST_CLOCK_TIME_NONE; |
332 | 0 | if (orig_offset != GST_BUFFER_OFFSET_NONE) { |
333 | 0 | GST_BUFFER_OFFSET (ret) = orig_offset + trim; |
334 | 0 | GST_BUFFER_OFFSET_END (ret) = GST_BUFFER_OFFSET (ret) + samples; |
335 | 0 | } else { |
336 | 0 | GST_BUFFER_OFFSET (ret) = GST_BUFFER_OFFSET_NONE; |
337 | 0 | GST_BUFFER_OFFSET_END (ret) = GST_BUFFER_OFFSET_NONE; |
338 | 0 | } |
339 | |
|
340 | 0 | return ret; |
341 | 0 | } |