Coverage Report

Created: 2025-08-28 06:10

/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
}