/src/gstreamer/subprojects/gst-plugins-base/gst-libs/gst/audio/gstaudiometa.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* GStreamer |
2 | | * Copyright (C) <2011> Wim Taymans <wim.taymans@gmail.com> |
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 | | /** |
21 | | * SECTION:gstaudiometa |
22 | | * @title: GstAudio meta |
23 | | * @short_description: Buffer metadata for audio downmix matrix handling |
24 | | * |
25 | | * #GstAudioDownmixMeta defines an audio downmix matrix to be send along with |
26 | | * audio buffers. These functions in this module help to create and attach the |
27 | | * meta as well as extracting it. |
28 | | */ |
29 | | #ifdef HAVE_CONFIG_H |
30 | | #include "config.h" |
31 | | #endif |
32 | | |
33 | | #include "gstaudiometa.h" |
34 | | |
35 | | #include <string.h> |
36 | | #include <gst/base/base.h> |
37 | | |
38 | | static gboolean |
39 | | gst_audio_downmix_meta_init (GstMeta * meta, gpointer params, |
40 | | GstBuffer * buffer) |
41 | 0 | { |
42 | 0 | GstAudioDownmixMeta *dmeta = (GstAudioDownmixMeta *) meta; |
43 | |
|
44 | 0 | dmeta->from_position = dmeta->to_position = NULL; |
45 | 0 | dmeta->from_channels = dmeta->to_channels = 0; |
46 | 0 | dmeta->matrix = NULL; |
47 | |
|
48 | 0 | return TRUE; |
49 | 0 | } |
50 | | |
51 | | static void |
52 | | gst_audio_downmix_meta_free (GstMeta * meta, GstBuffer * buffer) |
53 | 0 | { |
54 | 0 | GstAudioDownmixMeta *dmeta = (GstAudioDownmixMeta *) meta; |
55 | |
|
56 | 0 | g_free (dmeta->from_position); |
57 | 0 | if (dmeta->matrix) { |
58 | 0 | g_free (*dmeta->matrix); |
59 | 0 | g_free (dmeta->matrix); |
60 | 0 | } |
61 | 0 | } |
62 | | |
63 | | static gboolean |
64 | | gst_audio_downmix_meta_transform (GstBuffer * dest, GstMeta * meta, |
65 | | GstBuffer * buffer, GQuark type, gpointer data) |
66 | 0 | { |
67 | 0 | GstAudioDownmixMeta *smeta, *dmeta; |
68 | |
|
69 | 0 | smeta = (GstAudioDownmixMeta *) meta; |
70 | |
|
71 | 0 | if (GST_META_TRANSFORM_IS_COPY (type)) { |
72 | 0 | dmeta = gst_buffer_add_audio_downmix_meta (dest, smeta->from_position, |
73 | 0 | smeta->from_channels, smeta->to_position, smeta->to_channels, |
74 | 0 | (const gfloat **) smeta->matrix); |
75 | 0 | if (!dmeta) |
76 | 0 | return FALSE; |
77 | 0 | } else { |
78 | | /* return FALSE, if transform type is not supported */ |
79 | 0 | return FALSE; |
80 | 0 | } |
81 | | |
82 | 0 | return TRUE; |
83 | 0 | } |
84 | | |
85 | | /** |
86 | | * gst_buffer_get_audio_downmix_meta_for_channels: |
87 | | * @buffer: a #GstBuffer |
88 | | * @to_position: (array length=to_channels): the channel positions of |
89 | | * the destination |
90 | | * @to_channels: The number of channels of the destination |
91 | | * |
92 | | * Find the #GstAudioDownmixMeta on @buffer for the given destination |
93 | | * channel positions. |
94 | | * |
95 | | * Returns: (transfer none): the #GstAudioDownmixMeta on @buffer. |
96 | | */ |
97 | | GstAudioDownmixMeta * |
98 | | gst_buffer_get_audio_downmix_meta_for_channels (GstBuffer * buffer, |
99 | | const GstAudioChannelPosition * to_position, gint to_channels) |
100 | 0 | { |
101 | 0 | gpointer state = NULL; |
102 | 0 | GstMeta *meta; |
103 | 0 | const GstMetaInfo *info = GST_AUDIO_DOWNMIX_META_INFO; |
104 | |
|
105 | 0 | while ((meta = gst_buffer_iterate_meta (buffer, &state))) { |
106 | 0 | if (meta->info->api == info->api) { |
107 | 0 | GstAudioDownmixMeta *ameta = (GstAudioDownmixMeta *) meta; |
108 | 0 | if (ameta->to_channels == to_channels && |
109 | 0 | memcmp (ameta->to_position, to_position, |
110 | 0 | sizeof (GstAudioChannelPosition) * to_channels) == 0) |
111 | 0 | return ameta; |
112 | 0 | } |
113 | 0 | } |
114 | 0 | return NULL; |
115 | 0 | } |
116 | | |
117 | | /** |
118 | | * gst_buffer_add_audio_downmix_meta: |
119 | | * @buffer: a #GstBuffer |
120 | | * @from_position: (array length=from_channels): the channel positions |
121 | | * of the source |
122 | | * @from_channels: The number of channels of the source |
123 | | * @to_position: (array length=to_channels): the channel positions of |
124 | | * the destination |
125 | | * @to_channels: The number of channels of the destination |
126 | | * @matrix: The matrix coefficients. |
127 | | * |
128 | | * Attaches #GstAudioDownmixMeta metadata to @buffer with the given parameters. |
129 | | * |
130 | | * @matrix is an two-dimensional array of @to_channels times @from_channels |
131 | | * coefficients, i.e. the i-th output channels is constructed by multiplicating |
132 | | * the input channels with the coefficients in @matrix[i] and taking the sum |
133 | | * of the results. |
134 | | * |
135 | | * Returns: (transfer none): the #GstAudioDownmixMeta on @buffer. |
136 | | */ |
137 | | GstAudioDownmixMeta * |
138 | | gst_buffer_add_audio_downmix_meta (GstBuffer * buffer, |
139 | | const GstAudioChannelPosition * from_position, gint from_channels, |
140 | | const GstAudioChannelPosition * to_position, gint to_channels, |
141 | | const gfloat ** matrix) |
142 | 0 | { |
143 | 0 | GstAudioDownmixMeta *meta; |
144 | 0 | gint i; |
145 | |
|
146 | 0 | g_return_val_if_fail (from_position != NULL, NULL); |
147 | 0 | g_return_val_if_fail (from_channels > 0, NULL); |
148 | 0 | g_return_val_if_fail (to_position != NULL, NULL); |
149 | 0 | g_return_val_if_fail (to_channels > 0, NULL); |
150 | 0 | g_return_val_if_fail (matrix != NULL, NULL); |
151 | | |
152 | 0 | meta = |
153 | 0 | (GstAudioDownmixMeta *) gst_buffer_add_meta (buffer, |
154 | 0 | GST_AUDIO_DOWNMIX_META_INFO, NULL); |
155 | |
|
156 | 0 | meta->from_channels = from_channels; |
157 | 0 | meta->to_channels = to_channels; |
158 | |
|
159 | 0 | meta->from_position = |
160 | 0 | g_new (GstAudioChannelPosition, meta->from_channels + meta->to_channels); |
161 | 0 | meta->to_position = meta->from_position + meta->from_channels; |
162 | 0 | memcpy (meta->from_position, from_position, |
163 | 0 | sizeof (GstAudioChannelPosition) * meta->from_channels); |
164 | 0 | memcpy (meta->to_position, to_position, |
165 | 0 | sizeof (GstAudioChannelPosition) * meta->to_channels); |
166 | |
|
167 | 0 | meta->matrix = g_new (gfloat *, meta->to_channels); |
168 | 0 | meta->matrix[0] = g_new (gfloat, meta->from_channels * meta->to_channels); |
169 | 0 | memcpy (meta->matrix[0], matrix[0], sizeof (gfloat) * meta->from_channels); |
170 | 0 | for (i = 1; i < meta->to_channels; i++) { |
171 | 0 | meta->matrix[i] = meta->matrix[0] + i * meta->from_channels; |
172 | 0 | memcpy (meta->matrix[i], matrix[i], sizeof (gfloat) * meta->from_channels); |
173 | 0 | } |
174 | |
|
175 | 0 | return meta; |
176 | 0 | } |
177 | | |
178 | | GType |
179 | | gst_audio_downmix_meta_api_get_type (void) |
180 | 0 | { |
181 | 0 | static GType type; |
182 | 0 | static const gchar *tags[] = |
183 | 0 | { GST_META_TAG_AUDIO_STR, GST_META_TAG_AUDIO_CHANNELS_STR, NULL }; |
184 | |
|
185 | 0 | if (g_once_init_enter (&type)) { |
186 | 0 | GType _type = gst_meta_api_type_register ("GstAudioDownmixMetaAPI", tags); |
187 | 0 | g_once_init_leave (&type, _type); |
188 | 0 | } |
189 | 0 | return type; |
190 | 0 | } |
191 | | |
192 | | const GstMetaInfo * |
193 | | gst_audio_downmix_meta_get_info (void) |
194 | 0 | { |
195 | 0 | static const GstMetaInfo *audio_downmix_meta_info = NULL; |
196 | |
|
197 | 0 | if (g_once_init_enter ((GstMetaInfo **) & audio_downmix_meta_info)) { |
198 | 0 | const GstMetaInfo *meta = |
199 | 0 | gst_meta_register (GST_AUDIO_DOWNMIX_META_API_TYPE, |
200 | 0 | "GstAudioDownmixMeta", sizeof (GstAudioDownmixMeta), |
201 | 0 | gst_audio_downmix_meta_init, gst_audio_downmix_meta_free, |
202 | 0 | gst_audio_downmix_meta_transform); |
203 | 0 | g_once_init_leave ((GstMetaInfo **) & audio_downmix_meta_info, |
204 | 0 | (GstMetaInfo *) meta); |
205 | 0 | } |
206 | 0 | return audio_downmix_meta_info; |
207 | 0 | } |
208 | | |
209 | | static gboolean |
210 | | gst_audio_clipping_meta_init (GstMeta * meta, gpointer params, |
211 | | GstBuffer * buffer) |
212 | 0 | { |
213 | 0 | GstAudioClippingMeta *cmeta = (GstAudioClippingMeta *) meta; |
214 | |
|
215 | 0 | cmeta->format = GST_FORMAT_UNDEFINED; |
216 | 0 | cmeta->start = cmeta->end = 0; |
217 | |
|
218 | 0 | return TRUE; |
219 | 0 | } |
220 | | |
221 | | static gboolean |
222 | | gst_audio_clipping_meta_transform (GstBuffer * dest, GstMeta * meta, |
223 | | GstBuffer * buffer, GQuark type, gpointer data) |
224 | 0 | { |
225 | 0 | GstAudioClippingMeta *smeta, *dmeta; |
226 | |
|
227 | 0 | smeta = (GstAudioClippingMeta *) meta; |
228 | |
|
229 | 0 | if (GST_META_TRANSFORM_IS_COPY (type)) { |
230 | 0 | GstMetaTransformCopy *copy = data; |
231 | |
|
232 | 0 | if (copy->region) |
233 | 0 | return FALSE; |
234 | | |
235 | 0 | dmeta = |
236 | 0 | gst_buffer_add_audio_clipping_meta (dest, smeta->format, smeta->start, |
237 | 0 | smeta->end); |
238 | 0 | if (!dmeta) |
239 | 0 | return FALSE; |
240 | 0 | } else { |
241 | | /* TODO: Could implement an automatic transform for resampling */ |
242 | | /* return FALSE, if transform type is not supported */ |
243 | 0 | return FALSE; |
244 | 0 | } |
245 | | |
246 | 0 | return TRUE; |
247 | 0 | } |
248 | | |
249 | | /** |
250 | | * gst_buffer_add_audio_clipping_meta: |
251 | | * @buffer: a #GstBuffer |
252 | | * @format: GstFormat of @start and @stop, GST_FORMAT_DEFAULT is samples |
253 | | * @start: Amount of audio to clip from start of buffer |
254 | | * @end: Amount of to clip from end of buffer |
255 | | * |
256 | | * Attaches #GstAudioClippingMeta metadata to @buffer with the given parameters. |
257 | | * |
258 | | * Returns: (transfer none): the #GstAudioClippingMeta on @buffer. |
259 | | * |
260 | | * Since: 1.8 |
261 | | */ |
262 | | GstAudioClippingMeta * |
263 | | gst_buffer_add_audio_clipping_meta (GstBuffer * buffer, |
264 | | GstFormat format, guint64 start, guint64 end) |
265 | 0 | { |
266 | 0 | GstAudioClippingMeta *meta; |
267 | |
|
268 | 0 | g_return_val_if_fail (format != GST_FORMAT_UNDEFINED, NULL); |
269 | | |
270 | 0 | meta = |
271 | 0 | (GstAudioClippingMeta *) gst_buffer_add_meta (buffer, |
272 | 0 | GST_AUDIO_CLIPPING_META_INFO, NULL); |
273 | |
|
274 | 0 | meta->format = format; |
275 | 0 | meta->start = start; |
276 | 0 | meta->end = end; |
277 | |
|
278 | 0 | return meta; |
279 | 0 | } |
280 | | |
281 | | GType |
282 | | gst_audio_clipping_meta_api_get_type (void) |
283 | 0 | { |
284 | 0 | static GType type; |
285 | 0 | static const gchar *tags[] = |
286 | 0 | { GST_META_TAG_AUDIO_STR, GST_META_TAG_AUDIO_RATE_STR, NULL }; |
287 | |
|
288 | 0 | if (g_once_init_enter (&type)) { |
289 | 0 | GType _type = gst_meta_api_type_register ("GstAudioClippingMetaAPI", tags); |
290 | 0 | g_once_init_leave (&type, _type); |
291 | 0 | } |
292 | 0 | return type; |
293 | 0 | } |
294 | | |
295 | | const GstMetaInfo * |
296 | | gst_audio_clipping_meta_get_info (void) |
297 | 0 | { |
298 | 0 | static const GstMetaInfo *audio_clipping_meta_info = NULL; |
299 | |
|
300 | 0 | if (g_once_init_enter ((GstMetaInfo **) & audio_clipping_meta_info)) { |
301 | 0 | const GstMetaInfo *meta = |
302 | 0 | gst_meta_register (GST_AUDIO_CLIPPING_META_API_TYPE, |
303 | 0 | "GstAudioClippingMeta", sizeof (GstAudioClippingMeta), |
304 | 0 | gst_audio_clipping_meta_init, NULL, |
305 | 0 | gst_audio_clipping_meta_transform); |
306 | 0 | g_once_init_leave ((GstMetaInfo **) & audio_clipping_meta_info, |
307 | 0 | (GstMetaInfo *) meta); |
308 | 0 | } |
309 | 0 | return audio_clipping_meta_info; |
310 | 0 | } |
311 | | |
312 | | |
313 | | static gboolean |
314 | | gst_audio_meta_init (GstMeta * meta, gpointer params, GstBuffer * buffer) |
315 | 0 | { |
316 | 0 | GstAudioMeta *ameta = (GstAudioMeta *) meta; |
317 | |
|
318 | 0 | gst_audio_info_init (&ameta->info); |
319 | 0 | ameta->samples = 0; |
320 | 0 | ameta->offsets = NULL; |
321 | |
|
322 | 0 | return TRUE; |
323 | 0 | } |
324 | | |
325 | | static void |
326 | | gst_audio_meta_free (GstMeta * meta, GstBuffer * buffer) |
327 | 0 | { |
328 | 0 | GstAudioMeta *ameta = (GstAudioMeta *) meta; |
329 | |
|
330 | 0 | if (ameta->offsets && ameta->offsets != ameta->priv_offsets_arr) |
331 | 0 | g_free (ameta->offsets); |
332 | 0 | } |
333 | | |
334 | | static gboolean |
335 | | gst_audio_meta_transform (GstBuffer * dest, GstMeta * meta, |
336 | | GstBuffer * buffer, GQuark type, gpointer data) |
337 | 0 | { |
338 | 0 | GstAudioMeta *smeta, *dmeta; |
339 | |
|
340 | 0 | smeta = (GstAudioMeta *) meta; |
341 | |
|
342 | 0 | if (GST_META_TRANSFORM_IS_COPY (type)) { |
343 | 0 | dmeta = gst_buffer_add_audio_meta (dest, &smeta->info, smeta->samples, |
344 | 0 | smeta->offsets); |
345 | 0 | if (!dmeta) |
346 | 0 | return FALSE; |
347 | 0 | } else { |
348 | | /* return FALSE, if transform type is not supported */ |
349 | 0 | return FALSE; |
350 | 0 | } |
351 | | |
352 | 0 | return TRUE; |
353 | 0 | } |
354 | | |
355 | | static gboolean |
356 | | gst_audio_meta_serialize (const GstMeta * meta, GstByteArrayInterface * data, |
357 | | guint8 * version) |
358 | 0 | { |
359 | 0 | GstAudioMeta *ameta = (GstAudioMeta *) meta; |
360 | | |
361 | | /* Position is limited to 64 */ |
362 | 0 | gint n_position = ameta->info.channels > 64 ? 0 : ameta->info.channels; |
363 | |
|
364 | 0 | gsize size = 28 + n_position * 4 + ameta->info.channels * 8; |
365 | 0 | guint8 *ptr = gst_byte_array_interface_append (data, size); |
366 | 0 | if (ptr == NULL) |
367 | 0 | return FALSE; |
368 | | |
369 | 0 | GstByteWriter bw; |
370 | 0 | gboolean success = TRUE; |
371 | 0 | gst_byte_writer_init_with_data (&bw, ptr, size, FALSE); |
372 | 0 | success &= gst_byte_writer_put_int32_le (&bw, ameta->info.finfo->format); |
373 | 0 | success &= gst_byte_writer_put_int32_le (&bw, ameta->info.flags); |
374 | 0 | success &= gst_byte_writer_put_int32_le (&bw, ameta->info.layout); |
375 | 0 | success &= gst_byte_writer_put_int32_le (&bw, ameta->info.rate); |
376 | 0 | success &= gst_byte_writer_put_int32_le (&bw, ameta->info.channels); |
377 | 0 | for (int i = 0; i < n_position; i++) |
378 | 0 | success &= gst_byte_writer_put_int32_le (&bw, ameta->info.position[i]); |
379 | 0 | success &= gst_byte_writer_put_uint64_le (&bw, ameta->samples); |
380 | 0 | for (int i = 0; i < ameta->info.channels; i++) |
381 | 0 | success &= gst_byte_writer_put_uint64_le (&bw, ameta->offsets[i]); |
382 | 0 | g_assert (success); |
383 | | |
384 | 0 | return TRUE; |
385 | 0 | } |
386 | | |
387 | | static GstMeta * |
388 | | gst_audio_meta_deserialize (const GstMetaInfo * info, GstBuffer * buffer, |
389 | | const guint8 * data, gsize size, guint8 version) |
390 | 0 | { |
391 | 0 | GstAudioMeta *ameta = NULL; |
392 | 0 | gint32 format; |
393 | 0 | gint32 flags; |
394 | 0 | gint32 layout; |
395 | 0 | gint32 rate; |
396 | 0 | gint32 channels; |
397 | |
|
398 | 0 | if (version != 0) |
399 | 0 | return NULL; |
400 | | |
401 | 0 | GstByteReader br; |
402 | 0 | gboolean success = TRUE; |
403 | 0 | gst_byte_reader_init (&br, data, size); |
404 | 0 | success &= gst_byte_reader_get_int32_le (&br, &format); |
405 | 0 | success &= gst_byte_reader_get_int32_le (&br, &flags); |
406 | 0 | success &= gst_byte_reader_get_int32_le (&br, &layout); |
407 | 0 | success &= gst_byte_reader_get_int32_le (&br, &rate); |
408 | 0 | success &= gst_byte_reader_get_int32_le (&br, &channels); |
409 | |
|
410 | 0 | if (!success) |
411 | 0 | return NULL; |
412 | | |
413 | | /* Position is limited to 64 */ |
414 | 0 | gint n_position = channels > 64 ? 0 : channels; |
415 | 0 | gint32 *position = g_new (gint32, n_position); |
416 | 0 | guint64 *offsets64 = g_new (guint64, channels); |
417 | 0 | guint64 samples = 0; |
418 | |
|
419 | 0 | for (int i = 0; i < n_position; i++) |
420 | 0 | success &= gst_byte_reader_get_int32_le (&br, &position[i]); |
421 | 0 | success &= gst_byte_reader_get_uint64_le (&br, &samples); |
422 | 0 | for (int i = 0; i < channels; i++) |
423 | 0 | success &= gst_byte_reader_get_uint64_le (&br, &offsets64[i]); |
424 | |
|
425 | 0 | if (!success) { |
426 | 0 | g_free (position); |
427 | 0 | g_free (offsets64); |
428 | 0 | return NULL; |
429 | 0 | } |
430 | | #if GLIB_SIZEOF_SIZE_T != 8 |
431 | | gsize *offsets = g_new (gsize, channels); |
432 | | for (int i = 0; i < channels; i++) { |
433 | | if (offsets64[i] > G_MAXSIZE) { |
434 | | g_free (offsets64); |
435 | | g_free (offsets); |
436 | | g_free (position); |
437 | | return NULL; |
438 | | } |
439 | | offsets[i] = offsets64[i]; |
440 | | } |
441 | | g_free (offsets64); |
442 | | #else |
443 | 0 | gsize *offsets = (gsize *) offsets64; |
444 | 0 | #endif |
445 | |
|
446 | 0 | GstAudioInfo audio_info; |
447 | 0 | gst_audio_info_set_format (&audio_info, format, rate, channels, |
448 | 0 | (channels > 64) ? NULL : position); |
449 | 0 | audio_info.flags = flags; |
450 | 0 | audio_info.layout = layout; |
451 | |
|
452 | 0 | ameta = gst_buffer_add_audio_meta (buffer, &audio_info, samples, offsets); |
453 | |
|
454 | 0 | g_free (offsets); |
455 | 0 | g_free (position); |
456 | |
|
457 | 0 | return (GstMeta *) ameta; |
458 | 0 | } |
459 | | |
460 | | /** |
461 | | * gst_buffer_add_audio_meta: |
462 | | * @buffer: a #GstBuffer |
463 | | * @info: the audio properties of the buffer |
464 | | * @samples: the number of valid samples in the buffer |
465 | | * @offsets: (nullable): the offsets (in bytes) where each channel plane starts |
466 | | * in the buffer or %NULL to calculate it (see below); must be %NULL also |
467 | | * when @info->layout is %GST_AUDIO_LAYOUT_INTERLEAVED |
468 | | * |
469 | | * Allocates and attaches a #GstAudioMeta on @buffer, which must be writable |
470 | | * for that purpose. The fields of the #GstAudioMeta are directly populated |
471 | | * from the arguments of this function. |
472 | | * |
473 | | * When @info->layout is %GST_AUDIO_LAYOUT_NON_INTERLEAVED and @offsets is |
474 | | * %NULL, the offsets are calculated with a formula that assumes the planes are |
475 | | * tightly packed and in sequence: |
476 | | * offsets[channel] = channel * @samples * sample_stride |
477 | | * |
478 | | * It is not allowed for channels to overlap in memory, |
479 | | * i.e. for each i in [0, channels), the range |
480 | | * [@offsets[i], @offsets[i] + @samples * sample_stride) must not overlap |
481 | | * with any other such range. This function will assert if the parameters |
482 | | * specified cause this restriction to be violated. |
483 | | * |
484 | | * It is, obviously, also not allowed to specify parameters that would cause |
485 | | * out-of-bounds memory access on @buffer. This is also checked, which means |
486 | | * that you must add enough memory on the @buffer before adding this meta. |
487 | | * |
488 | | * Returns: (transfer none): the #GstAudioMeta that was attached on the @buffer |
489 | | * |
490 | | * Since: 1.16 |
491 | | */ |
492 | | GstAudioMeta * |
493 | | gst_buffer_add_audio_meta (GstBuffer * buffer, const GstAudioInfo * info, |
494 | | gsize samples, gsize offsets[]) |
495 | 0 | { |
496 | 0 | GstAudioMeta *meta; |
497 | 0 | gint i; |
498 | 0 | gsize plane_size; |
499 | |
|
500 | 0 | g_return_val_if_fail (GST_IS_BUFFER (buffer), FALSE); |
501 | 0 | g_return_val_if_fail (info != NULL, NULL); |
502 | 0 | g_return_val_if_fail (GST_AUDIO_INFO_IS_VALID (info), NULL); |
503 | 0 | g_return_val_if_fail (GST_AUDIO_INFO_FORMAT (info) != |
504 | 0 | GST_AUDIO_FORMAT_UNKNOWN, NULL); |
505 | 0 | g_return_val_if_fail (info->layout == GST_AUDIO_LAYOUT_NON_INTERLEAVED |
506 | 0 | || !offsets, NULL); |
507 | | |
508 | 0 | meta = |
509 | 0 | (GstAudioMeta *) gst_buffer_add_meta (buffer, GST_AUDIO_META_INFO, NULL); |
510 | |
|
511 | 0 | meta->info = *info; |
512 | 0 | meta->samples = samples; |
513 | 0 | plane_size = samples * info->finfo->width / 8; |
514 | |
|
515 | 0 | if (info->layout == GST_AUDIO_LAYOUT_NON_INTERLEAVED) { |
516 | 0 | #ifndef G_DISABLE_CHECKS |
517 | 0 | gsize max_offset = 0; |
518 | 0 | gint j; |
519 | 0 | #endif |
520 | |
|
521 | 0 | if (G_UNLIKELY (info->channels > 8)) |
522 | 0 | meta->offsets = g_new (gsize, info->channels); |
523 | 0 | else |
524 | 0 | meta->offsets = meta->priv_offsets_arr; |
525 | |
|
526 | 0 | if (offsets) { |
527 | 0 | for (i = 0; i < info->channels; i++) { |
528 | 0 | meta->offsets[i] = offsets[i]; |
529 | 0 | #ifndef G_DISABLE_CHECKS |
530 | 0 | max_offset = MAX (max_offset, offsets[i]); |
531 | 0 | for (j = 0; j < info->channels; j++) { |
532 | 0 | if (i != j && !(offsets[j] + plane_size <= offsets[i] |
533 | 0 | || offsets[i] + plane_size <= offsets[j])) { |
534 | 0 | g_critical ("GstAudioMeta properties would cause channel memory " |
535 | 0 | "areas to overlap! offsets: %" G_GSIZE_FORMAT " (%d), %" |
536 | 0 | G_GSIZE_FORMAT " (%d) with plane size %" G_GSIZE_FORMAT, |
537 | 0 | offsets[i], i, offsets[j], j, plane_size); |
538 | 0 | gst_buffer_remove_meta (buffer, (GstMeta *) meta); |
539 | 0 | return NULL; |
540 | 0 | } |
541 | 0 | } |
542 | 0 | #endif |
543 | 0 | } |
544 | 0 | } else { |
545 | | /* default offsets assume channels are laid out sequentially in memory */ |
546 | 0 | for (i = 0; i < info->channels; i++) |
547 | 0 | meta->offsets[i] = i * plane_size; |
548 | 0 | #ifndef G_DISABLE_CHECKS |
549 | 0 | max_offset = meta->offsets[info->channels - 1]; |
550 | 0 | #endif |
551 | 0 | } |
552 | | |
553 | 0 | #ifndef G_DISABLE_CHECKS |
554 | 0 | if (max_offset + plane_size > gst_buffer_get_size (buffer)) { |
555 | 0 | g_critical ("GstAudioMeta properties would cause " |
556 | 0 | "out-of-bounds memory access on the buffer: max_offset %" |
557 | 0 | G_GSIZE_FORMAT ", samples %" G_GSIZE_FORMAT ", bps %u, buffer size %" |
558 | 0 | G_GSIZE_FORMAT, max_offset, samples, info->finfo->width / 8, |
559 | 0 | gst_buffer_get_size (buffer)); |
560 | 0 | gst_buffer_remove_meta (buffer, (GstMeta *) meta); |
561 | 0 | return NULL; |
562 | 0 | } |
563 | 0 | #endif |
564 | 0 | } |
565 | | |
566 | 0 | return meta; |
567 | 0 | } |
568 | | |
569 | | GType |
570 | | gst_audio_meta_api_get_type (void) |
571 | 136 | { |
572 | 136 | static GType type; |
573 | 136 | static const gchar *tags[] = { |
574 | 136 | GST_META_TAG_AUDIO_STR, GST_META_TAG_AUDIO_CHANNELS_STR, |
575 | 136 | GST_META_TAG_AUDIO_RATE_STR, NULL |
576 | 136 | }; |
577 | | |
578 | 136 | if (g_once_init_enter (&type)) { |
579 | 1 | GType _type = gst_meta_api_type_register ("GstAudioMetaAPI", tags); |
580 | 1 | g_once_init_leave (&type, _type); |
581 | 1 | } |
582 | 136 | return type; |
583 | 136 | } |
584 | | |
585 | | const GstMetaInfo * |
586 | | gst_audio_meta_get_info (void) |
587 | 0 | { |
588 | 0 | static const GstMetaInfo *audio_meta_info = NULL; |
589 | |
|
590 | 0 | if (g_once_init_enter ((GstMetaInfo **) & audio_meta_info)) { |
591 | 0 | GstMetaInfo *info = gst_meta_info_new (GST_AUDIO_META_API_TYPE, |
592 | 0 | "GstAudioMeta", sizeof (GstAudioMeta)); |
593 | |
|
594 | 0 | info->init_func = gst_audio_meta_init; |
595 | 0 | info->free_func = gst_audio_meta_free; |
596 | 0 | info->transform_func = gst_audio_meta_transform; |
597 | 0 | info->serialize_func = gst_audio_meta_serialize; |
598 | 0 | info->deserialize_func = gst_audio_meta_deserialize; |
599 | 0 | const GstMetaInfo *meta = gst_meta_info_register (info); |
600 | |
|
601 | 0 | g_once_init_leave ((GstMetaInfo **) & audio_meta_info, |
602 | 0 | (GstMetaInfo *) meta); |
603 | 0 | } |
604 | 0 | return audio_meta_info; |
605 | 0 | } |
606 | | |
607 | | /** |
608 | | * gst_audio_level_meta_api_get_type: |
609 | | * |
610 | | * Return the #GType associated with #GstAudioLevelMeta. |
611 | | * |
612 | | * Returns: a #GType |
613 | | * |
614 | | * Since: 1.20 |
615 | | */ |
616 | | GType |
617 | | gst_audio_level_meta_api_get_type (void) |
618 | 0 | { |
619 | 0 | static GType type = 0; |
620 | 0 | static const gchar *tags[] = { NULL }; |
621 | |
|
622 | 0 | if (g_once_init_enter (&type)) { |
623 | 0 | GType _type = gst_meta_api_type_register ("GstAudioLevelMetaAPI", tags); |
624 | 0 | g_once_init_leave (&type, _type); |
625 | 0 | } |
626 | 0 | return type; |
627 | 0 | } |
628 | | |
629 | | static gboolean |
630 | | gst_audio_level_meta_init (GstMeta * meta, gpointer params, GstBuffer * buffer) |
631 | 0 | { |
632 | 0 | GstAudioLevelMeta *dmeta = (GstAudioLevelMeta *) meta; |
633 | |
|
634 | 0 | dmeta->level = 127; |
635 | 0 | dmeta->voice_activity = FALSE; |
636 | |
|
637 | 0 | return TRUE; |
638 | 0 | } |
639 | | |
640 | | static gboolean |
641 | | gst_audio_level_meta_transform (GstBuffer * dst, GstMeta * meta, |
642 | | GstBuffer * src, GQuark type, gpointer data) |
643 | 0 | { |
644 | 0 | if (GST_META_TRANSFORM_IS_COPY (type)) { |
645 | 0 | GstAudioLevelMeta *smeta = (GstAudioLevelMeta *) meta; |
646 | 0 | GstAudioLevelMeta *dmeta; |
647 | |
|
648 | 0 | dmeta = gst_buffer_add_audio_level_meta (dst, smeta->level, |
649 | 0 | smeta->voice_activity); |
650 | 0 | if (dmeta == NULL) |
651 | 0 | return FALSE; |
652 | 0 | } else { |
653 | | /* return FALSE, if transform type is not supported */ |
654 | 0 | return FALSE; |
655 | 0 | } |
656 | | |
657 | 0 | return TRUE; |
658 | 0 | } |
659 | | |
660 | | /** |
661 | | * gst_audio_level_meta_get_info: |
662 | | * |
663 | | * Return the #GstMetaInfo associated with #GstAudioLevelMeta. |
664 | | * |
665 | | * Returns: (transfer none): a #GstMetaInfo |
666 | | * |
667 | | * Since: 1.20 |
668 | | */ |
669 | | const GstMetaInfo * |
670 | | gst_audio_level_meta_get_info (void) |
671 | 0 | { |
672 | 0 | static const GstMetaInfo *audio_level_meta_info = NULL; |
673 | |
|
674 | 0 | if (g_once_init_enter (&audio_level_meta_info)) { |
675 | 0 | const GstMetaInfo *meta = gst_meta_register (GST_AUDIO_LEVEL_META_API_TYPE, |
676 | 0 | "GstAudioLevelMeta", |
677 | 0 | sizeof (GstAudioLevelMeta), |
678 | 0 | gst_audio_level_meta_init, |
679 | 0 | (GstMetaFreeFunction) NULL, |
680 | 0 | gst_audio_level_meta_transform); |
681 | 0 | g_once_init_leave (&audio_level_meta_info, meta); |
682 | 0 | } |
683 | 0 | return audio_level_meta_info; |
684 | 0 | } |
685 | | |
686 | | /** |
687 | | * gst_buffer_add_audio_level_meta: |
688 | | * @buffer: a #GstBuffer |
689 | | * @level: the -dBov from 0-127 (127 is silence). |
690 | | * @voice_activity: whether the buffer contains voice activity. |
691 | | * |
692 | | * Attaches audio level information to @buffer. (RFC 6464) |
693 | | * |
694 | | * Returns: (transfer none) (nullable): the #GstAudioLevelMeta on @buffer. |
695 | | * |
696 | | * Since: 1.20 |
697 | | */ |
698 | | GstAudioLevelMeta * |
699 | | gst_buffer_add_audio_level_meta (GstBuffer * buffer, guint8 level, |
700 | | gboolean voice_activity) |
701 | 0 | { |
702 | 0 | GstAudioLevelMeta *meta; |
703 | |
|
704 | 0 | g_return_val_if_fail (buffer != NULL, NULL); |
705 | | |
706 | 0 | meta = (GstAudioLevelMeta *) gst_buffer_add_meta (buffer, |
707 | 0 | GST_AUDIO_LEVEL_META_INFO, NULL); |
708 | 0 | if (!meta) |
709 | 0 | return NULL; |
710 | | |
711 | 0 | meta->level = level; |
712 | 0 | meta->voice_activity = voice_activity; |
713 | |
|
714 | 0 | return meta; |
715 | 0 | } |
716 | | |
717 | | /** |
718 | | * gst_buffer_get_audio_level_meta: |
719 | | * @buffer: a #GstBuffer |
720 | | * |
721 | | * Find the #GstAudioLevelMeta on @buffer. |
722 | | * |
723 | | * Returns: (transfer none) (nullable): the #GstAudioLevelMeta or %NULL when |
724 | | * there is no such metadata on @buffer. |
725 | | * |
726 | | * Since: 1.20 |
727 | | */ |
728 | | GstAudioLevelMeta * |
729 | | gst_buffer_get_audio_level_meta (GstBuffer * buffer) |
730 | 0 | { |
731 | 0 | return (GstAudioLevelMeta *) gst_buffer_get_meta (buffer, |
732 | 0 | gst_audio_level_meta_api_get_type ()); |
733 | 0 | } |