/src/gstreamer/subprojects/gst-plugins-base/gst-libs/gst/audio/audio-channels.c
Line | Count | Source (jump to first uncovered line) |
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:gstaudiochannels |
21 | | * @title: Audio-channels |
22 | | * @short_description: Support library for audio channel handling |
23 | | * |
24 | | * This library contains some helper functions for multichannel audio. |
25 | | */ |
26 | | |
27 | | #ifdef HAVE_CONFIG_H |
28 | | # include "config.h" |
29 | | #endif |
30 | | |
31 | | #include <string.h> |
32 | | |
33 | | #include "audio-channels.h" |
34 | | |
35 | | #ifndef GST_DISABLE_GST_DEBUG |
36 | | #define GST_CAT_DEFAULT ensure_debug_category() |
37 | | static GstDebugCategory * |
38 | | ensure_debug_category (void) |
39 | 0 | { |
40 | 0 | static gsize cat_gonce = 0; |
41 | |
|
42 | 0 | if (g_once_init_enter (&cat_gonce)) { |
43 | 0 | gsize cat_done; |
44 | |
|
45 | 0 | cat_done = (gsize) _gst_debug_category_new ("audio-channels", 0, |
46 | 0 | "audio-channels object"); |
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 | | static const GstAudioChannelPosition default_channel_order[64] = { |
59 | | GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT, |
60 | | GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT, |
61 | | GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER, |
62 | | GST_AUDIO_CHANNEL_POSITION_LFE1, |
63 | | GST_AUDIO_CHANNEL_POSITION_REAR_LEFT, |
64 | | GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT, |
65 | | GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER, |
66 | | GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER, |
67 | | GST_AUDIO_CHANNEL_POSITION_REAR_CENTER, |
68 | | GST_AUDIO_CHANNEL_POSITION_LFE2, |
69 | | GST_AUDIO_CHANNEL_POSITION_SIDE_LEFT, |
70 | | GST_AUDIO_CHANNEL_POSITION_SIDE_RIGHT, |
71 | | GST_AUDIO_CHANNEL_POSITION_TOP_FRONT_LEFT, |
72 | | GST_AUDIO_CHANNEL_POSITION_TOP_FRONT_RIGHT, |
73 | | GST_AUDIO_CHANNEL_POSITION_TOP_FRONT_CENTER, |
74 | | GST_AUDIO_CHANNEL_POSITION_TOP_CENTER, |
75 | | GST_AUDIO_CHANNEL_POSITION_TOP_REAR_LEFT, |
76 | | GST_AUDIO_CHANNEL_POSITION_TOP_REAR_RIGHT, |
77 | | GST_AUDIO_CHANNEL_POSITION_TOP_SIDE_LEFT, |
78 | | GST_AUDIO_CHANNEL_POSITION_TOP_SIDE_RIGHT, |
79 | | GST_AUDIO_CHANNEL_POSITION_TOP_REAR_CENTER, |
80 | | GST_AUDIO_CHANNEL_POSITION_BOTTOM_FRONT_CENTER, |
81 | | GST_AUDIO_CHANNEL_POSITION_BOTTOM_FRONT_LEFT, |
82 | | GST_AUDIO_CHANNEL_POSITION_BOTTOM_FRONT_RIGHT, |
83 | | GST_AUDIO_CHANNEL_POSITION_WIDE_LEFT, |
84 | | GST_AUDIO_CHANNEL_POSITION_WIDE_RIGHT, |
85 | | GST_AUDIO_CHANNEL_POSITION_SURROUND_LEFT, |
86 | | GST_AUDIO_CHANNEL_POSITION_SURROUND_RIGHT, |
87 | | GST_AUDIO_CHANNEL_POSITION_INVALID, |
88 | | GST_AUDIO_CHANNEL_POSITION_INVALID, |
89 | | GST_AUDIO_CHANNEL_POSITION_INVALID, |
90 | | GST_AUDIO_CHANNEL_POSITION_INVALID, |
91 | | GST_AUDIO_CHANNEL_POSITION_INVALID, |
92 | | GST_AUDIO_CHANNEL_POSITION_INVALID, |
93 | | GST_AUDIO_CHANNEL_POSITION_INVALID, |
94 | | GST_AUDIO_CHANNEL_POSITION_INVALID, |
95 | | GST_AUDIO_CHANNEL_POSITION_INVALID, |
96 | | GST_AUDIO_CHANNEL_POSITION_INVALID, |
97 | | GST_AUDIO_CHANNEL_POSITION_INVALID, |
98 | | GST_AUDIO_CHANNEL_POSITION_INVALID, |
99 | | GST_AUDIO_CHANNEL_POSITION_INVALID, |
100 | | GST_AUDIO_CHANNEL_POSITION_INVALID, |
101 | | GST_AUDIO_CHANNEL_POSITION_INVALID, |
102 | | GST_AUDIO_CHANNEL_POSITION_INVALID, |
103 | | GST_AUDIO_CHANNEL_POSITION_INVALID, |
104 | | GST_AUDIO_CHANNEL_POSITION_INVALID, |
105 | | GST_AUDIO_CHANNEL_POSITION_INVALID, |
106 | | GST_AUDIO_CHANNEL_POSITION_INVALID, |
107 | | GST_AUDIO_CHANNEL_POSITION_INVALID, |
108 | | GST_AUDIO_CHANNEL_POSITION_INVALID, |
109 | | GST_AUDIO_CHANNEL_POSITION_INVALID, |
110 | | GST_AUDIO_CHANNEL_POSITION_INVALID, |
111 | | GST_AUDIO_CHANNEL_POSITION_INVALID, |
112 | | GST_AUDIO_CHANNEL_POSITION_INVALID, |
113 | | GST_AUDIO_CHANNEL_POSITION_INVALID, |
114 | | GST_AUDIO_CHANNEL_POSITION_INVALID, |
115 | | GST_AUDIO_CHANNEL_POSITION_INVALID, |
116 | | GST_AUDIO_CHANNEL_POSITION_INVALID, |
117 | | GST_AUDIO_CHANNEL_POSITION_INVALID, |
118 | | GST_AUDIO_CHANNEL_POSITION_INVALID, |
119 | | GST_AUDIO_CHANNEL_POSITION_INVALID, |
120 | | GST_AUDIO_CHANNEL_POSITION_INVALID, |
121 | | GST_AUDIO_CHANNEL_POSITION_INVALID, |
122 | | GST_AUDIO_CHANNEL_POSITION_INVALID |
123 | | }; |
124 | | |
125 | | /* |
126 | | * Compares @channels audio channel positions @p1 and @p2 if they are equal. |
127 | | * In other words, tells whether channel reordering is needed (unequal) or not (equal). |
128 | | * |
129 | | * Returns: %TRUE if the channel positions are equal, i.e. no reordering is needed. |
130 | | */ |
131 | | static gboolean |
132 | | gst_audio_channel_positions_equal (const GstAudioChannelPosition * p1, |
133 | | const GstAudioChannelPosition * p2, gint channels) |
134 | 0 | { |
135 | 0 | return memcmp (p1, p2, channels * sizeof (p1[0])) == 0; |
136 | 0 | } |
137 | | |
138 | | static gboolean |
139 | | check_valid_channel_positions (const GstAudioChannelPosition * position, |
140 | | gint channels, gboolean enforce_order, guint64 * channel_mask_out) |
141 | 146 | { |
142 | 146 | gint i, j; |
143 | 146 | guint64 channel_mask = 0; |
144 | | |
145 | 146 | if (channels == 1 && position[0] == GST_AUDIO_CHANNEL_POSITION_MONO) { |
146 | 146 | if (channel_mask_out) |
147 | 0 | *channel_mask_out = 0; |
148 | 146 | return TRUE; |
149 | 146 | } |
150 | | |
151 | 0 | if (channels > 0 && position[0] == GST_AUDIO_CHANNEL_POSITION_NONE) { |
152 | 0 | if (channel_mask_out) |
153 | 0 | *channel_mask_out = 0; |
154 | 0 | return TRUE; |
155 | 0 | } |
156 | | |
157 | 0 | j = 0; |
158 | 0 | for (i = 0; i < channels; i++) { |
159 | 0 | while (j < G_N_ELEMENTS (default_channel_order) |
160 | 0 | && default_channel_order[j] != position[i]) |
161 | 0 | j++; |
162 | |
|
163 | 0 | if (position[i] == GST_AUDIO_CHANNEL_POSITION_INVALID || |
164 | 0 | position[i] == GST_AUDIO_CHANNEL_POSITION_MONO || |
165 | 0 | position[i] == GST_AUDIO_CHANNEL_POSITION_NONE) |
166 | 0 | return FALSE; |
167 | | |
168 | | /* Is this in valid channel order? */ |
169 | 0 | if (enforce_order && j == G_N_ELEMENTS (default_channel_order)) |
170 | 0 | return FALSE; |
171 | 0 | j++; |
172 | |
|
173 | 0 | if ((channel_mask & (G_GUINT64_CONSTANT (1) << position[i]))) |
174 | 0 | return FALSE; |
175 | | |
176 | 0 | channel_mask |= (G_GUINT64_CONSTANT (1) << position[i]); |
177 | 0 | } |
178 | | |
179 | 0 | if (channel_mask_out) |
180 | 0 | *channel_mask_out = channel_mask; |
181 | |
|
182 | 0 | return TRUE; |
183 | 0 | } |
184 | | |
185 | | /** |
186 | | * gst_audio_reorder_channels_with_reorder_map: |
187 | | * @data: (array length=size) (element-type guint8): The pointer to |
188 | | * the memory. |
189 | | * @size: The size of the memory. |
190 | | * @bps: The number of bytes per sample. |
191 | | * @channels: The number of channels. |
192 | | * @reorder_map: (array length=channels): The channel reorder map. |
193 | | * |
194 | | * Reorders @data with the given @reorder_map. |
195 | | * |
196 | | * The reorder map can be retrieved for example with |
197 | | * gst_audio_get_channel_reorder_map(). |
198 | | * |
199 | | * Note: this function assumes the audio data is in interleaved layout |
200 | | * |
201 | | * Since: 1.26 |
202 | | */ |
203 | | void |
204 | | gst_audio_reorder_channels_with_reorder_map (gpointer data, gsize size, |
205 | | gint bps, gint channels, const gint * reorder_map) |
206 | 0 | { |
207 | 0 | gint bpf = bps * channels; |
208 | 0 | guint8 *ptr = data; |
209 | 0 | gsize n; |
210 | |
|
211 | 0 | g_return_if_fail (data != NULL); |
212 | 0 | g_return_if_fail (size % (bps * channels) == 0); |
213 | 0 | g_return_if_fail (bps > 0); |
214 | 0 | g_return_if_fail (bps <= 64); |
215 | 0 | g_return_if_fail (channels > 0); |
216 | 0 | g_return_if_fail (channels <= 64); |
217 | 0 | g_return_if_fail (reorder_map != NULL); |
218 | | |
219 | 0 | n = size / bpf; |
220 | 0 | for (gsize i = 0; i < n; i++) { |
221 | 0 | guint8 tmp[64 * 8]; |
222 | |
|
223 | 0 | memcpy (tmp, ptr, bpf); |
224 | 0 | for (gint j = 0; j < channels; j++) |
225 | 0 | memcpy (ptr + reorder_map[j] * bps, tmp + j * bps, bps); |
226 | |
|
227 | 0 | ptr += bpf; |
228 | 0 | } |
229 | 0 | } |
230 | | |
231 | | /** |
232 | | * gst_audio_reorder_channels: |
233 | | * @data: (array length=size) (element-type guint8): The pointer to |
234 | | * the memory. |
235 | | * @size: The size of the memory. |
236 | | * @format: The %GstAudioFormat of the buffer. |
237 | | * @channels: The number of channels. |
238 | | * @from: (array length=channels): The channel positions in the buffer. |
239 | | * @to: (array length=channels): The channel positions to convert to. |
240 | | * |
241 | | * Reorders @data from the channel positions @from to the channel |
242 | | * positions @to. @from and @to must contain the same number of |
243 | | * positions and the same positions, only in a different order. |
244 | | * |
245 | | * This function internally calls gst_audio_get_channel_reorder_map() and |
246 | | * gst_audio_reorder_channels_with_reorder_map(). It is more efficient to call |
247 | | * gst_audio_get_channel_reorder_map() once to retrieve the reorder map and |
248 | | * then call gst_audio_reorder_channels_with_reorder_map() with the same |
249 | | * reorder map until the channel positions change. |
250 | | * |
251 | | * Note: this function assumes the audio data is in interleaved layout |
252 | | * |
253 | | * Returns: %TRUE if the reordering was possible. |
254 | | */ |
255 | | gboolean |
256 | | gst_audio_reorder_channels (gpointer data, gsize size, GstAudioFormat format, |
257 | | gint channels, const GstAudioChannelPosition * from, |
258 | | const GstAudioChannelPosition * to) |
259 | 0 | { |
260 | 0 | const GstAudioFormatInfo *info; |
261 | 0 | gint reorder_map[64] = { 0, }; |
262 | |
|
263 | 0 | info = gst_audio_format_get_info (format); |
264 | |
|
265 | 0 | g_return_val_if_fail (data != NULL, FALSE); |
266 | 0 | g_return_val_if_fail (from != NULL, FALSE); |
267 | 0 | g_return_val_if_fail (to != NULL, FALSE); |
268 | 0 | g_return_val_if_fail (info != NULL && info->width > 0, FALSE); |
269 | 0 | g_return_val_if_fail (info->width > 0, FALSE); |
270 | 0 | g_return_val_if_fail (info->width <= 8 * 64, FALSE); |
271 | 0 | g_return_val_if_fail (size % ((info->width * channels) / 8) == 0, FALSE); |
272 | 0 | g_return_val_if_fail (channels > 0, FALSE); |
273 | | |
274 | 0 | if (size == 0) |
275 | 0 | return TRUE; |
276 | | |
277 | 0 | if (gst_audio_channel_positions_equal (from, to, channels)) |
278 | 0 | return TRUE; |
279 | | |
280 | 0 | g_return_val_if_fail (channels <= 64, FALSE); |
281 | | |
282 | 0 | if (!gst_audio_get_channel_reorder_map (channels, from, to, reorder_map)) |
283 | 0 | return FALSE; |
284 | | |
285 | 0 | gst_audio_reorder_channels_with_reorder_map (data, size, info->width / 8, |
286 | 0 | channels, reorder_map); |
287 | |
|
288 | 0 | return TRUE; |
289 | 0 | } |
290 | | |
291 | | static gboolean |
292 | | gst_audio_meta_reorder_channels (GstAudioMeta * meta, |
293 | | const GstAudioChannelPosition * from, const GstAudioChannelPosition * to) |
294 | 0 | { |
295 | 0 | gint reorder_map[64] = { 0, }; |
296 | 0 | gsize tmp_offsets[64] = { 0, }; |
297 | 0 | gint i; |
298 | |
|
299 | 0 | g_return_val_if_fail (meta, FALSE); |
300 | 0 | g_return_val_if_fail (meta->info.channels > 0, FALSE); |
301 | 0 | g_return_val_if_fail (meta->info.channels <= 64, FALSE); |
302 | 0 | g_return_val_if_fail (meta->offsets != NULL, FALSE); |
303 | | |
304 | 0 | if (!gst_audio_get_channel_reorder_map (meta->info.channels, from, to, |
305 | 0 | reorder_map)) |
306 | 0 | return FALSE; |
307 | | |
308 | 0 | memcpy (tmp_offsets, meta->offsets, meta->info.channels * sizeof (gsize)); |
309 | 0 | for (i = 0; i < meta->info.channels; i++) { |
310 | 0 | meta->offsets[reorder_map[i]] = tmp_offsets[i]; |
311 | 0 | } |
312 | |
|
313 | 0 | return TRUE; |
314 | 0 | } |
315 | | |
316 | | /** |
317 | | * gst_audio_buffer_reorder_channels: |
318 | | * @buffer: The buffer to reorder. |
319 | | * @format: The %GstAudioFormat of the buffer. |
320 | | * @channels: The number of channels. |
321 | | * @from: (array length=channels): The channel positions in the buffer. |
322 | | * @to: (array length=channels): The channel positions to convert to. |
323 | | * |
324 | | * Reorders @buffer from the channel positions @from to the channel |
325 | | * positions @to. @from and @to must contain the same number of |
326 | | * positions and the same positions, only in a different order. |
327 | | * @buffer must be writable. |
328 | | * |
329 | | * Returns: %TRUE if the reordering was possible. |
330 | | */ |
331 | | gboolean |
332 | | gst_audio_buffer_reorder_channels (GstBuffer * buffer, |
333 | | GstAudioFormat format, gint channels, |
334 | | const GstAudioChannelPosition * from, const GstAudioChannelPosition * to) |
335 | 0 | { |
336 | 0 | GstMapInfo info; |
337 | 0 | GstAudioMeta *meta; |
338 | 0 | gboolean ret = TRUE; |
339 | |
|
340 | 0 | g_return_val_if_fail (GST_IS_BUFFER (buffer), FALSE); |
341 | 0 | g_return_val_if_fail (gst_buffer_is_writable (buffer), FALSE); |
342 | | |
343 | 0 | if (gst_audio_channel_positions_equal (from, to, channels)) |
344 | 0 | return TRUE; |
345 | | |
346 | 0 | meta = gst_buffer_get_audio_meta (buffer); |
347 | 0 | if (meta && meta->info.layout == GST_AUDIO_LAYOUT_NON_INTERLEAVED) { |
348 | 0 | g_return_val_if_fail (channels == meta->info.channels, FALSE); |
349 | | |
350 | 0 | ret = gst_audio_meta_reorder_channels (meta, from, to); |
351 | 0 | } else { |
352 | 0 | if (!gst_buffer_map (buffer, &info, GST_MAP_READWRITE)) |
353 | 0 | return FALSE; |
354 | | |
355 | 0 | ret = gst_audio_reorder_channels (info.data, info.size, format, channels, |
356 | 0 | from, to); |
357 | |
|
358 | 0 | gst_buffer_unmap (buffer, &info); |
359 | 0 | } |
360 | 0 | return ret; |
361 | 0 | } |
362 | | |
363 | | /** |
364 | | * gst_audio_check_valid_channel_positions: |
365 | | * @position: (array length=channels): The %GstAudioChannelPositions |
366 | | * to check. |
367 | | * @channels: The number of channels. |
368 | | * @force_order: Only consider the GStreamer channel order. |
369 | | * |
370 | | * Checks if @position contains valid channel positions for |
371 | | * @channels channels. If @force_order is %TRUE it additionally |
372 | | * checks if the channels are in the order required by GStreamer. |
373 | | * |
374 | | * Returns: %TRUE if the channel positions are valid. |
375 | | */ |
376 | | gboolean |
377 | | gst_audio_check_valid_channel_positions (const GstAudioChannelPosition * |
378 | | position, gint channels, gboolean force_order) |
379 | 146 | { |
380 | 146 | return check_valid_channel_positions (position, channels, force_order, NULL); |
381 | 146 | } |
382 | | |
383 | | /** |
384 | | * gst_audio_channel_positions_to_mask: |
385 | | * @position: (array length=channels): The %GstAudioChannelPositions |
386 | | * @channels: The number of channels. |
387 | | * @force_order: Only consider the GStreamer channel order. |
388 | | * @channel_mask: (out): the output channel mask |
389 | | * |
390 | | * Convert the @position array of @channels channels to a bitmask. |
391 | | * |
392 | | * If @force_order is %TRUE it additionally checks if the channels are |
393 | | * in the order required by GStreamer. |
394 | | * |
395 | | * Returns: %TRUE if the channel positions are valid and could be converted. |
396 | | */ |
397 | | gboolean |
398 | | gst_audio_channel_positions_to_mask (const GstAudioChannelPosition * position, |
399 | | gint channels, gboolean force_order, guint64 * channel_mask) |
400 | 0 | { |
401 | 0 | return check_valid_channel_positions (position, channels, force_order, |
402 | 0 | channel_mask); |
403 | 0 | } |
404 | | |
405 | | /** |
406 | | * gst_audio_channel_positions_from_mask: |
407 | | * @channels: The number of channels |
408 | | * @channel_mask: The input channel_mask |
409 | | * @position: (array length=channels): The |
410 | | * %GstAudioChannelPosition<!-- -->s |
411 | | * |
412 | | * Convert the @channels present in @channel_mask to a @position array |
413 | | * (which should have at least @channels entries ensured by caller). |
414 | | * If @channel_mask is set to 0, it is considered as 'not present' for purpose |
415 | | * of conversion. |
416 | | * A partially valid @channel_mask with less bits set than the number |
417 | | * of channels is considered valid. |
418 | | * |
419 | | * Returns: %TRUE if channel and channel mask are valid and could be converted |
420 | | */ |
421 | | gboolean |
422 | | gst_audio_channel_positions_from_mask (gint channels, guint64 channel_mask, |
423 | | GstAudioChannelPosition * position) |
424 | 0 | { |
425 | 0 | g_return_val_if_fail (position != NULL, FALSE); |
426 | 0 | g_return_val_if_fail (channels != 0, FALSE); |
427 | | |
428 | 0 | GST_DEBUG ("converting %d channels for " |
429 | 0 | " channel mask 0x%016" G_GINT64_MODIFIER "x", channels, channel_mask); |
430 | |
|
431 | 0 | if (!channel_mask) { |
432 | 0 | if (channels == 1) { |
433 | 0 | position[0] = GST_AUDIO_CHANNEL_POSITION_MONO; |
434 | 0 | } else if (channels == 2) { |
435 | 0 | position[0] = GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT; |
436 | 0 | position[1] = GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT; |
437 | 0 | } else { |
438 | 0 | goto no_channel_mask; |
439 | 0 | } |
440 | 0 | } else { |
441 | 0 | gint i, j; |
442 | |
|
443 | 0 | j = 0; |
444 | 0 | for (i = 0; i < 64; i++) { |
445 | 0 | if ((channel_mask & (G_GUINT64_CONSTANT (1) << i))) { |
446 | 0 | if (j < channels) |
447 | 0 | position[j] = default_channel_order[i]; |
448 | 0 | j++; |
449 | 0 | } |
450 | 0 | } |
451 | 0 | if (j != channels) |
452 | 0 | GST_WARNING ("Only partially valid channel mask 0x%016" G_GINT64_MODIFIER |
453 | 0 | "x for %d channels", channel_mask, channels); |
454 | 0 | } |
455 | | |
456 | 0 | return TRUE; |
457 | | |
458 | | /* ERROR */ |
459 | 0 | no_channel_mask: |
460 | 0 | { |
461 | 0 | GST_ERROR ("no channel-mask property given"); |
462 | 0 | return FALSE; |
463 | 0 | } |
464 | 0 | } |
465 | | |
466 | | |
467 | | /** |
468 | | * gst_audio_get_channel_reorder_map: |
469 | | * @channels: The number of channels. |
470 | | * @from: (array length=channels): The channel positions to reorder from. |
471 | | * @to: (array length=channels): The channel positions to reorder to. |
472 | | * @reorder_map: (array length=channels): Pointer to the reorder map. |
473 | | * |
474 | | * Returns a reorder map for @from to @to that can be used in |
475 | | * custom channel reordering code, e.g. to convert from or to the |
476 | | * GStreamer channel order. @from and @to must contain the same |
477 | | * number of positions and the same positions, only in a |
478 | | * different order. |
479 | | * |
480 | | * The resulting @reorder_map can be used for reordering by assigning |
481 | | * channel i of the input to channel reorder_map[i] of the output. |
482 | | * |
483 | | * Returns: %TRUE if the channel positions are valid and reordering |
484 | | * is possible. |
485 | | */ |
486 | | gboolean |
487 | | gst_audio_get_channel_reorder_map (gint channels, |
488 | | const GstAudioChannelPosition * from, const GstAudioChannelPosition * to, |
489 | | gint * reorder_map) |
490 | 0 | { |
491 | 0 | gint i, j; |
492 | |
|
493 | 0 | g_return_val_if_fail (reorder_map != NULL, FALSE); |
494 | 0 | g_return_val_if_fail (channels > 0, FALSE); |
495 | 0 | g_return_val_if_fail (channels <= 64, FALSE); |
496 | 0 | g_return_val_if_fail (from != NULL, FALSE); |
497 | 0 | g_return_val_if_fail (to != NULL, FALSE); |
498 | 0 | g_return_val_if_fail (check_valid_channel_positions (from, channels, FALSE, |
499 | 0 | NULL), FALSE); |
500 | 0 | g_return_val_if_fail (check_valid_channel_positions (to, channels, FALSE, |
501 | 0 | NULL), FALSE); |
502 | | |
503 | | /* Build reorder map and check compatibility */ |
504 | 0 | for (i = 0; i < channels; i++) { |
505 | 0 | if (from[i] == GST_AUDIO_CHANNEL_POSITION_NONE |
506 | 0 | || to[i] == GST_AUDIO_CHANNEL_POSITION_NONE) |
507 | 0 | return FALSE; |
508 | 0 | if (from[i] == GST_AUDIO_CHANNEL_POSITION_INVALID |
509 | 0 | || to[i] == GST_AUDIO_CHANNEL_POSITION_INVALID) |
510 | 0 | return FALSE; |
511 | 0 | if (from[i] == GST_AUDIO_CHANNEL_POSITION_MONO |
512 | 0 | || to[i] == GST_AUDIO_CHANNEL_POSITION_MONO) |
513 | 0 | return FALSE; |
514 | | |
515 | 0 | for (j = 0; j < channels; j++) { |
516 | 0 | if (from[i] == to[j]) { |
517 | 0 | reorder_map[i] = j; |
518 | 0 | break; |
519 | 0 | } |
520 | 0 | } |
521 | | |
522 | | /* Not all channels present in both */ |
523 | 0 | if (j == channels) |
524 | 0 | return FALSE; |
525 | 0 | } |
526 | | |
527 | 0 | return TRUE; |
528 | 0 | } |
529 | | |
530 | | /** |
531 | | * gst_audio_channel_positions_to_valid_order: |
532 | | * @position: (array length=channels): The channel positions to |
533 | | * reorder to. |
534 | | * @channels: The number of channels. |
535 | | * |
536 | | * Reorders the channel positions in @position from any order to |
537 | | * the GStreamer channel order. |
538 | | * |
539 | | * Returns: %TRUE if the channel positions are valid and reordering |
540 | | * was successful. |
541 | | */ |
542 | | gboolean |
543 | | gst_audio_channel_positions_to_valid_order (GstAudioChannelPosition * position, |
544 | | gint channels) |
545 | 0 | { |
546 | 0 | GstAudioChannelPosition tmp[64]; |
547 | 0 | guint64 channel_mask = 0; |
548 | 0 | gint i, j; |
549 | |
|
550 | 0 | g_return_val_if_fail (channels > 0, FALSE); |
551 | 0 | g_return_val_if_fail (position != NULL, FALSE); |
552 | 0 | g_return_val_if_fail (check_valid_channel_positions (position, channels, |
553 | 0 | FALSE, NULL), FALSE); |
554 | | |
555 | 0 | if (channels == 1 && position[0] == GST_AUDIO_CHANNEL_POSITION_MONO) |
556 | 0 | return TRUE; |
557 | 0 | if (position[0] == GST_AUDIO_CHANNEL_POSITION_NONE) |
558 | 0 | return TRUE; |
559 | | |
560 | 0 | check_valid_channel_positions (position, channels, FALSE, &channel_mask); |
561 | |
|
562 | 0 | memset (tmp, 0xff, sizeof (tmp)); |
563 | 0 | j = 0; |
564 | 0 | for (i = 0; i < 64; i++) { |
565 | 0 | if ((channel_mask & (G_GUINT64_CONSTANT (1) << i))) { |
566 | 0 | tmp[j] = i; |
567 | 0 | j++; |
568 | 0 | } |
569 | 0 | } |
570 | |
|
571 | 0 | memcpy (position, tmp, sizeof (tmp[0]) * channels); |
572 | |
|
573 | 0 | return TRUE; |
574 | 0 | } |
575 | | |
576 | | #define _P(pos) (G_GUINT64_CONSTANT (1) << GST_AUDIO_CHANNEL_POSITION_ ##pos) |
577 | | |
578 | | static const guint64 default_masks[] = { |
579 | | /* 1 channel */ |
580 | | 0, |
581 | | /* 2 channels */ |
582 | | _P (FRONT_LEFT) | _P (FRONT_RIGHT), |
583 | | /* 3 channels (2.1) */ |
584 | | _P (FRONT_LEFT) | _P (FRONT_RIGHT) | _P (LFE1), |
585 | | /* 4 channels (4.0) */ |
586 | | _P (FRONT_LEFT) | _P (FRONT_RIGHT) | _P (REAR_LEFT) | _P (REAR_RIGHT), |
587 | | /* 5 channels */ |
588 | | _P (FRONT_LEFT) | _P (FRONT_RIGHT) | _P (REAR_LEFT) | _P (REAR_RIGHT) |
589 | | | _P (FRONT_CENTER), |
590 | | /* 6 channels (5.1) */ |
591 | | _P (FRONT_LEFT) | |
592 | | _P (FRONT_RIGHT) | |
593 | | _P (REAR_LEFT) | _P (REAR_RIGHT) | _P (FRONT_CENTER) | _P (LFE1), |
594 | | /* 7 channels (6.1) */ |
595 | | _P (FRONT_LEFT) | |
596 | | _P (FRONT_RIGHT) | |
597 | | _P (REAR_LEFT) | |
598 | | _P (REAR_RIGHT) | _P (FRONT_CENTER) | _P (LFE1) | _P (REAR_CENTER), |
599 | | /* 8 channels (7.1) */ |
600 | | _P (FRONT_LEFT) | |
601 | | _P (FRONT_RIGHT) | |
602 | | _P (REAR_LEFT) | |
603 | | _P (REAR_RIGHT) | |
604 | | _P (FRONT_CENTER) | _P (LFE1) | _P (SIDE_LEFT) | _P (SIDE_RIGHT), |
605 | | }; |
606 | | |
607 | | /** |
608 | | * gst_audio_channel_get_fallback_mask: |
609 | | * @channels: the number of channels |
610 | | * |
611 | | * Get the fallback channel-mask for the given number of channels. |
612 | | * |
613 | | * This function returns a reasonable fallback channel-mask and should be |
614 | | * called as a last resort when the specific channel map is unknown. |
615 | | * |
616 | | * Returns: a fallback channel-mask for @channels or 0 when there is no |
617 | | * mask and mono. |
618 | | * |
619 | | * Since: 1.8 |
620 | | */ |
621 | | guint64 |
622 | | gst_audio_channel_get_fallback_mask (gint channels) |
623 | 4 | { |
624 | 4 | g_return_val_if_fail (channels > 0, 0); |
625 | | |
626 | 4 | if (channels > 8) |
627 | 0 | return 0; |
628 | | |
629 | 4 | return default_masks[channels - 1]; |
630 | 4 | } |
631 | | |
632 | | static const gchar * |
633 | | position_to_string (GstAudioChannelPosition pos) |
634 | 0 | { |
635 | 0 | switch (pos) { |
636 | 0 | case GST_AUDIO_CHANNEL_POSITION_NONE: |
637 | 0 | return "NONE"; |
638 | 0 | case GST_AUDIO_CHANNEL_POSITION_MONO: |
639 | 0 | return "MONO"; |
640 | 0 | case GST_AUDIO_CHANNEL_POSITION_INVALID: |
641 | 0 | return "INVALID"; |
642 | 0 | case GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT: |
643 | 0 | return "FL"; |
644 | 0 | case GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT: |
645 | 0 | return "FR"; |
646 | 0 | case GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER: |
647 | 0 | return "FC"; |
648 | 0 | case GST_AUDIO_CHANNEL_POSITION_LFE1: |
649 | 0 | return "LFE1"; |
650 | 0 | case GST_AUDIO_CHANNEL_POSITION_REAR_LEFT: |
651 | 0 | return "RL"; |
652 | 0 | case GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT: |
653 | 0 | return "RR"; |
654 | 0 | case GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER: |
655 | 0 | return "FLoC"; |
656 | 0 | case GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER: |
657 | 0 | return "FRoC"; |
658 | 0 | case GST_AUDIO_CHANNEL_POSITION_REAR_CENTER: |
659 | 0 | return "RC"; |
660 | 0 | case GST_AUDIO_CHANNEL_POSITION_LFE2: |
661 | 0 | return "LF2"; |
662 | 0 | case GST_AUDIO_CHANNEL_POSITION_SIDE_LEFT: |
663 | 0 | return "SL"; |
664 | 0 | case GST_AUDIO_CHANNEL_POSITION_SIDE_RIGHT: |
665 | 0 | return "SR"; |
666 | 0 | case GST_AUDIO_CHANNEL_POSITION_TOP_FRONT_LEFT: |
667 | 0 | return "TFL"; |
668 | 0 | case GST_AUDIO_CHANNEL_POSITION_TOP_FRONT_RIGHT: |
669 | 0 | return "TFR"; |
670 | 0 | case GST_AUDIO_CHANNEL_POSITION_TOP_FRONT_CENTER: |
671 | 0 | return "TFC"; |
672 | 0 | case GST_AUDIO_CHANNEL_POSITION_TOP_CENTER: |
673 | 0 | return "TFC"; |
674 | 0 | case GST_AUDIO_CHANNEL_POSITION_TOP_REAR_LEFT: |
675 | 0 | return "TRL"; |
676 | 0 | case GST_AUDIO_CHANNEL_POSITION_TOP_REAR_RIGHT: |
677 | 0 | return "TRR"; |
678 | 0 | case GST_AUDIO_CHANNEL_POSITION_TOP_SIDE_LEFT: |
679 | 0 | return "TSL"; |
680 | 0 | case GST_AUDIO_CHANNEL_POSITION_TOP_SIDE_RIGHT: |
681 | 0 | return "TSR"; |
682 | 0 | case GST_AUDIO_CHANNEL_POSITION_TOP_REAR_CENTER: |
683 | 0 | return "TRC"; |
684 | 0 | case GST_AUDIO_CHANNEL_POSITION_BOTTOM_FRONT_CENTER: |
685 | 0 | return "BFC"; |
686 | 0 | case GST_AUDIO_CHANNEL_POSITION_BOTTOM_FRONT_LEFT: |
687 | 0 | return "BFL"; |
688 | 0 | case GST_AUDIO_CHANNEL_POSITION_BOTTOM_FRONT_RIGHT: |
689 | 0 | return "BFR"; |
690 | 0 | case GST_AUDIO_CHANNEL_POSITION_WIDE_LEFT: |
691 | 0 | return "WL"; |
692 | 0 | case GST_AUDIO_CHANNEL_POSITION_WIDE_RIGHT: |
693 | 0 | return "WR"; |
694 | 0 | case GST_AUDIO_CHANNEL_POSITION_SURROUND_LEFT: |
695 | 0 | return "SL"; |
696 | 0 | case GST_AUDIO_CHANNEL_POSITION_SURROUND_RIGHT: |
697 | 0 | return "SR"; |
698 | 0 | default: |
699 | 0 | break; |
700 | 0 | } |
701 | | |
702 | 0 | return "UNKNOWN"; |
703 | 0 | } |
704 | | |
705 | | /** |
706 | | * gst_audio_channel_positions_to_string: |
707 | | * @position: (array length=channels): The %GstAudioChannelPositions |
708 | | * to convert. |
709 | | * @channels: The number of channels. |
710 | | * |
711 | | * Converts @position to a human-readable string representation for |
712 | | * debugging purposes. |
713 | | * |
714 | | * Returns: (transfer full): a newly allocated string representing |
715 | | * @position |
716 | | * |
717 | | * Since: 1.10 |
718 | | */ |
719 | | gchar * |
720 | | gst_audio_channel_positions_to_string (const GstAudioChannelPosition * position, |
721 | | gint channels) |
722 | 0 | { |
723 | 0 | guint i; |
724 | 0 | GString *tmp; |
725 | |
|
726 | 0 | g_return_val_if_fail (channels > 0, FALSE); |
727 | 0 | g_return_val_if_fail (position != NULL, FALSE); |
728 | | |
729 | 0 | tmp = g_string_new ("["); |
730 | 0 | for (i = 0; i < channels; i++) |
731 | 0 | g_string_append_printf (tmp, " %s", position_to_string (position[i])); |
732 | 0 | g_string_append (tmp, " ]"); |
733 | |
|
734 | 0 | return g_string_free (tmp, FALSE); |
735 | 0 | } |