Coverage Report

Created: 2025-12-28 06:39

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
}