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