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-buffer.c
Line
Count
Source
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
#ifdef HAVE_CONFIG_H
22
#  include "config.h"
23
#endif
24
25
#include "audio-buffer.h"
26
27
28
static void
29
gst_audio_buffer_unmap_internal (GstAudioBuffer * buffer, guint n_unmap)
30
0
{
31
0
  guint i;
32
33
  /* Allow to unmap even if not mapped, to work nicely with
34
   * g_auto (GstAudioBuffer) buffer = GST_AUDIO_BUFFER_INIT;
35
   * This is also more consistent with gst_buffer_unmap() */
36
0
  if (!buffer->buffer)
37
0
    return;
38
39
0
  for (i = 0; i < n_unmap; i++) {
40
0
    gst_buffer_unmap (buffer->buffer, &buffer->map_infos[i]);
41
0
  }
42
0
  if (buffer->planes != buffer->priv_planes_arr)
43
0
    g_clear_pointer (&buffer->planes, g_free);
44
0
  if (buffer->map_infos != buffer->priv_map_infos_arr)
45
0
    g_clear_pointer (&buffer->map_infos, g_free);
46
47
  /* Reset various fields to avoid use-after-frees.
48
   * This also makes it possible to call unmap() twice. */
49
0
  buffer->buffer = NULL;
50
0
  memset (&buffer->priv_planes_arr, 0, sizeof (buffer->priv_planes_arr));
51
0
  memset (&buffer->priv_map_infos_arr, 0, sizeof (buffer->priv_map_infos_arr));
52
0
}
53
54
/**
55
 * gst_audio_buffer_unmap:
56
 * @buffer: the #GstAudioBuffer to unmap
57
 *
58
 * Unmaps an audio buffer that was previously mapped with
59
 * gst_audio_buffer_map().
60
 *
61
 * Since: 1.16
62
 */
63
void
64
gst_audio_buffer_unmap (GstAudioBuffer * buffer)
65
0
{
66
0
  gst_audio_buffer_unmap_internal (buffer, buffer->n_planes);
67
0
}
68
69
/**
70
 * gst_audio_buffer_map:
71
 * @buffer: (out caller-allocates): pointer to a #GstAudioBuffer
72
 * @info: the audio properties of the buffer
73
 * @gstbuffer: (transfer none): the #GstBuffer to be mapped
74
 * @flags: the access mode for the memory
75
 *
76
 * Maps an audio @gstbuffer so that it can be read or written and stores the
77
 * result of the map operation in @buffer.
78
 *
79
 * This is especially useful when the @gstbuffer is in non-interleaved (planar)
80
 * layout, in which case this function will use the information in the
81
 * @gstbuffer's attached #GstAudioMeta in order to map each channel in a
82
 * separate "plane" in #GstAudioBuffer. If a #GstAudioMeta is not attached
83
 * on the @gstbuffer, then it must be in interleaved layout.
84
 *
85
 * If a #GstAudioMeta is attached, then the #GstAudioInfo on the meta is checked
86
 * against @info. Normally, they should be equal, but in case they are not,
87
 * a g_critical will be printed and the #GstAudioInfo from the meta will be
88
 * used.
89
 *
90
 * In non-interleaved buffers, it is possible to have each channel on a separate
91
 * #GstMemory. In this case, each memory will be mapped separately to avoid
92
 * copying their contents in a larger memory area. Do note though that it is
93
 * not supported to have a single channel spanning over two or more different
94
 * #GstMemory objects. Although the map operation will likely succeed in this
95
 * case, it will be highly sub-optimal and it is recommended to merge all the
96
 * memories in the buffer before calling this function.
97
 *
98
 * Note: The actual #GstBuffer is not ref'ed, but it is required to stay valid
99
 * as long as it's mapped.
100
 *
101
 * Returns: %TRUE if the map operation succeeded or %FALSE on failure
102
 *
103
 * Since: 1.16
104
 */
105
gboolean
106
gst_audio_buffer_map (GstAudioBuffer * buffer, const GstAudioInfo * info,
107
    GstBuffer * gstbuffer, GstMapFlags flags)
108
0
{
109
0
  GstAudioMeta *meta = NULL;
110
0
  guint i = 0, idx, length;
111
0
  gsize skip;
112
113
0
  g_return_val_if_fail (buffer != NULL, FALSE);
114
0
  g_return_val_if_fail (info != NULL, FALSE);
115
0
  g_return_val_if_fail (GST_AUDIO_INFO_IS_VALID (info), FALSE);
116
0
  g_return_val_if_fail (GST_AUDIO_INFO_FORMAT (info) !=
117
0
      GST_AUDIO_FORMAT_UNKNOWN, FALSE);
118
0
  g_return_val_if_fail (GST_IS_BUFFER (gstbuffer), FALSE);
119
120
0
  meta = gst_buffer_get_audio_meta (gstbuffer);
121
122
  /* be strict on the layout */
123
0
  g_return_val_if_fail ((!meta && info->layout == GST_AUDIO_LAYOUT_INTERLEAVED)
124
0
      || (meta && info->layout == meta->info.layout), FALSE);
125
126
  /* and not so strict on other fields */
127
0
  if (G_UNLIKELY (meta && !gst_audio_info_is_equal (&meta->info, info))) {
128
0
    g_critical ("the GstAudioInfo argument is not equal "
129
0
        "to the GstAudioMeta's attached info");
130
0
  }
131
132
0
  if (meta) {
133
    /* make sure that the meta doesn't imply having more samples than
134
     * what's actually possible to store in this buffer */
135
0
    g_return_val_if_fail (meta->samples <=
136
0
        gst_buffer_get_size (gstbuffer) / GST_AUDIO_INFO_BPF (&meta->info),
137
0
        FALSE);
138
0
    buffer->n_samples = meta->samples;
139
0
  } else {
140
0
    buffer->n_samples =
141
0
        gst_buffer_get_size (gstbuffer) / GST_AUDIO_INFO_BPF (info);
142
0
  }
143
144
0
  buffer->info = meta ? meta->info : *info;
145
0
  buffer->buffer = gstbuffer;
146
147
0
  if (GST_AUDIO_BUFFER_LAYOUT (buffer) == GST_AUDIO_LAYOUT_INTERLEAVED) {
148
    /* interleaved */
149
0
    buffer->n_planes = 1;
150
0
    buffer->planes = buffer->priv_planes_arr;
151
0
    buffer->map_infos = buffer->priv_map_infos_arr;
152
153
0
    if (!gst_buffer_map (gstbuffer, &buffer->map_infos[0], flags))
154
0
      return FALSE;
155
156
0
    buffer->planes[0] = buffer->map_infos[0].data;
157
0
  } else {
158
    /* non-interleaved */
159
0
    buffer->n_planes = GST_AUDIO_BUFFER_CHANNELS (buffer);
160
161
0
    if (G_UNLIKELY (buffer->n_planes > 8)) {
162
0
      buffer->planes = g_new (gpointer, buffer->n_planes);
163
0
      buffer->map_infos = g_new (GstMapInfo, buffer->n_planes);
164
0
    } else {
165
0
      buffer->planes = buffer->priv_planes_arr;
166
0
      buffer->map_infos = buffer->priv_map_infos_arr;
167
0
    }
168
169
0
    if (buffer->n_samples == 0) {
170
0
      memset (buffer->map_infos, 0,
171
0
          buffer->n_planes * sizeof (buffer->map_infos[0]));
172
0
      memset (buffer->planes, 0, buffer->n_planes * sizeof (buffer->planes[0]));
173
0
    } else {
174
0
      for (i = 0; i < buffer->n_planes; i++) {
175
0
        if (!gst_buffer_find_memory (gstbuffer, meta->offsets[i],
176
0
                GST_AUDIO_BUFFER_PLANE_SIZE (buffer), &idx, &length, &skip))
177
0
          goto no_memory;
178
179
0
        if (!gst_buffer_map_range (gstbuffer, idx, length,
180
0
                &buffer->map_infos[i], flags))
181
0
          goto cannot_map;
182
183
0
        buffer->planes[i] = buffer->map_infos[i].data + skip;
184
0
      }
185
0
    }
186
0
  }
187
188
0
  return TRUE;
189
190
0
no_memory:
191
0
  {
192
0
    GST_DEBUG ("plane %u, no memory at offset %" G_GSIZE_FORMAT, i,
193
0
        meta->offsets[i]);
194
0
    gst_audio_buffer_unmap_internal (buffer, i);
195
0
    return FALSE;
196
0
  }
197
0
cannot_map:
198
0
  {
199
0
    GST_DEBUG ("cannot map memory range %u-%u", idx, length);
200
0
    gst_audio_buffer_unmap_internal (buffer, i);
201
0
    return FALSE;
202
0
  }
203
0
}