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