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