Coverage Report

Created: 2025-11-09 06:39

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/vlc/src/audio_output/common.c
Line
Count
Source
1
/*****************************************************************************
2
 * common.c : audio output management of common data structures
3
 *****************************************************************************
4
 * Copyright (C) 2002-2007 VLC authors and VideoLAN
5
 *
6
 * Authors: Christophe Massiot <massiot@via.ecp.fr>
7
 *
8
 * This program is free software; you can redistribute it and/or modify it
9
 * under the terms of the GNU Lesser General Public License as published by
10
 * the Free Software Foundation; either version 2.1 of the License, or
11
 * (at your option) any later version.
12
 *
13
 * This program is distributed in the hope that it will be useful,
14
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16
 * GNU Lesser General Public License for more details.
17
 *
18
 * You should have received a copy of the GNU Lesser General Public License
19
 * along with this program; if not, write to the Free Software Foundation,
20
 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
21
 *****************************************************************************/
22
23
#ifdef HAVE_CONFIG_H
24
# include "config.h"
25
#endif
26
27
#include <limits.h>
28
#include <assert.h>
29
30
#include <vlc_common.h>
31
#include <vlc_aout.h>
32
#include "aout_internal.h"
33
34
/*
35
 * Formats management (internal and external)
36
 */
37
38
/*****************************************************************************
39
 * aout_BitsPerSample : get the number of bits per sample
40
 *****************************************************************************/
41
unsigned int aout_BitsPerSample( vlc_fourcc_t i_format )
42
2.22M
{
43
2.22M
    switch( vlc_fourcc_GetCodec( AUDIO_ES, i_format ) )
44
2.22M
    {
45
1.05M
    case VLC_CODEC_U8:
46
1.05M
    case VLC_CODEC_S8:
47
1.05M
    case VLC_CODEC_ALAW:
48
1.05M
    case VLC_CODEC_MULAW:
49
1.05M
        return 8;
50
51
0
    case VLC_CODEC_U16L:
52
1.14M
    case VLC_CODEC_S16L:
53
1.14M
    case VLC_CODEC_U16B:
54
1.15M
    case VLC_CODEC_S16B:
55
1.15M
        return 16;
56
57
0
    case VLC_CODEC_U24L:
58
3.71k
    case VLC_CODEC_S24L:
59
3.71k
    case VLC_CODEC_U24B:
60
4.26k
    case VLC_CODEC_S24B:
61
4.26k
        return 24;
62
63
0
    case VLC_CODEC_S24L32:
64
0
    case VLC_CODEC_S24B32:
65
0
    case VLC_CODEC_U32L:
66
0
    case VLC_CODEC_U32B:
67
4.36k
    case VLC_CODEC_S32L:
68
7.22k
    case VLC_CODEC_S32B:
69
14.1k
    case VLC_CODEC_F32L:
70
15.3k
    case VLC_CODEC_F32B:
71
15.3k
        return 32;
72
73
156
    case VLC_CODEC_F64L:
74
278
    case VLC_CODEC_F64B:
75
278
        return 64;
76
77
4.52k
    default:
78
        /* For these formats the caller has to indicate the parameters
79
         * by hand. */
80
4.52k
        return 0;
81
2.22M
    }
82
2.22M
}
83
84
/*****************************************************************************
85
 * aout_FormatPrepare : compute the number of bytes per frame & frame length
86
 *****************************************************************************/
87
void aout_FormatPrepare( audio_sample_format_t * p_format )
88
1.98M
{
89
90
1.98M
    unsigned i_channels = aout_FormatNbChannels( p_format );
91
1.98M
    if( i_channels > 0 )
92
1.86M
        p_format->i_channels = i_channels;
93
1.98M
    p_format->i_bitspersample = aout_BitsPerSample( p_format->i_format );
94
1.98M
    if( p_format->i_bitspersample > 0 )
95
1.98M
    {
96
1.98M
        p_format->i_bytes_per_frame = ( p_format->i_bitspersample / 8 )
97
1.98M
                                    * p_format->i_channels;
98
1.98M
        p_format->i_frame_length = 1;
99
1.98M
    }
100
1.98M
}
101
102
/*****************************************************************************
103
 * aout_FormatPrintChannels : print a channel in a human-readable form
104
 *****************************************************************************/
105
const char * aout_FormatPrintChannels( const audio_sample_format_t * p_format )
106
0
{
107
0
    if (p_format->channel_type == AUDIO_CHANNEL_TYPE_AMBISONICS)
108
0
        return "Ambisonics";
109
110
    /* AUDIO_CHANNEL_TYPE_BITMAP */
111
0
    switch ( p_format->i_physical_channels )
112
0
    {
113
0
    case AOUT_CHAN_LEFT:
114
0
    case AOUT_CHAN_RIGHT:
115
0
    case AOUT_CHAN_CENTER:
116
0
        if ( (p_format->i_physical_channels & AOUT_CHAN_CENTER)
117
0
              || (p_format->i_physical_channels
118
0
                   & (AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT)) )
119
0
            return "Mono";
120
0
        else if ( p_format->i_physical_channels & AOUT_CHAN_LEFT )
121
0
            return "Left";
122
0
        return "Right";
123
0
    case AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT:
124
0
        if ( p_format->i_chan_mode & AOUT_CHANMODE_DOLBYSTEREO )
125
0
            return "Dolby";
126
0
        else if ( p_format->i_chan_mode & AOUT_CHANMODE_DUALMONO )
127
0
            return "Dual-mono";
128
0
        else if ( p_format->i_physical_channels == AOUT_CHAN_CENTER )
129
0
            return "Stereo/Mono";
130
0
        else if ( !(p_format->i_physical_channels & AOUT_CHAN_RIGHT) )
131
0
            return "Stereo/Left";
132
0
        else if ( !(p_format->i_physical_channels & AOUT_CHAN_LEFT) )
133
0
            return "Stereo/Right";
134
0
        return "Stereo";
135
0
    case AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_CENTER:
136
0
        return "3F";
137
0
    case AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_REARCENTER:
138
0
        return "2F1R";
139
0
    case AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_CENTER
140
0
          | AOUT_CHAN_REARCENTER:
141
0
        return "3F1R";
142
0
    case AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT
143
0
          | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT:
144
0
        return "2F2R";
145
0
    case AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT
146
0
          | AOUT_CHAN_MIDDLELEFT | AOUT_CHAN_MIDDLERIGHT:
147
0
        return "2F2M";
148
0
    case AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_CENTER
149
0
          | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT:
150
0
        return "3F2R";
151
0
    case AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_CENTER
152
0
          | AOUT_CHAN_MIDDLELEFT | AOUT_CHAN_MIDDLERIGHT:
153
0
        return "3F2M";
154
155
0
    case AOUT_CHAN_CENTER | AOUT_CHAN_LFE:
156
0
        if ( (p_format->i_physical_channels & AOUT_CHAN_CENTER)
157
0
              || (p_format->i_physical_channels
158
0
                   & (AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT)) )
159
0
            return "Mono/LFE";
160
0
        else if ( p_format->i_physical_channels & AOUT_CHAN_LEFT )
161
0
            return "Left/LFE";
162
0
        return "Right/LFE";
163
0
    case AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_LFE:
164
0
        if ( p_format->i_chan_mode & AOUT_CHANMODE_DOLBYSTEREO )
165
0
            return "Dolby/LFE";
166
0
        else if ( p_format->i_chan_mode & AOUT_CHANMODE_DUALMONO )
167
0
            return "Dual-mono/LFE";
168
0
        else if ( p_format->i_physical_channels == AOUT_CHAN_CENTER )
169
0
            return "Mono/LFE";
170
0
        else if ( !(p_format->i_physical_channels & AOUT_CHAN_RIGHT) )
171
0
            return "Stereo/Left/LFE";
172
0
        else if ( !(p_format->i_physical_channels & AOUT_CHAN_LEFT) )
173
0
            return "Stereo/Right/LFE";
174
0
         return "Stereo/LFE";
175
0
    case AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_CENTER | AOUT_CHAN_LFE:
176
0
        return "3F/LFE";
177
0
    case AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_REARCENTER
178
0
          | AOUT_CHAN_LFE:
179
0
        return "2F1R/LFE";
180
0
    case AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_CENTER
181
0
          | AOUT_CHAN_REARCENTER | AOUT_CHAN_LFE:
182
0
        return "3F1R/LFE";
183
0
    case AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT
184
0
          | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT | AOUT_CHAN_LFE:
185
0
        return "2F2R/LFE";
186
0
    case AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT
187
0
          | AOUT_CHAN_MIDDLELEFT | AOUT_CHAN_MIDDLERIGHT | AOUT_CHAN_LFE:
188
0
        return "2F2M/LFE";
189
0
    case AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_CENTER
190
0
          | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT | AOUT_CHAN_LFE:
191
0
        return "3F2R/LFE";
192
0
    case AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_CENTER
193
0
          | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT | AOUT_CHAN_REARCENTER
194
0
          | AOUT_CHAN_LFE:
195
0
        return "3F3R/LFE";
196
0
    case AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_CENTER
197
0
          | AOUT_CHAN_MIDDLELEFT | AOUT_CHAN_MIDDLERIGHT | AOUT_CHAN_LFE:
198
0
        return "3F2M/LFE";
199
0
    case AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT
200
0
          | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT | AOUT_CHAN_MIDDLELEFT
201
0
          | AOUT_CHAN_MIDDLERIGHT:
202
0
        return "2F2M2R";
203
0
    case AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_CENTER
204
0
          | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT | AOUT_CHAN_MIDDLELEFT
205
0
          | AOUT_CHAN_MIDDLERIGHT:
206
0
        return "3F2M2R";
207
0
    case AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_CENTER
208
0
          | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT | AOUT_CHAN_MIDDLELEFT
209
0
          | AOUT_CHAN_MIDDLERIGHT | AOUT_CHAN_LFE:
210
0
        return "3F2M2R/LFE";
211
0
    case AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_CENTER
212
0
          | AOUT_CHAN_REARCENTER | AOUT_CHAN_MIDDLELEFT
213
0
          | AOUT_CHAN_MIDDLERIGHT | AOUT_CHAN_LFE:
214
0
        return "3F2M1R/LFE";
215
0
    case AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_CENTER
216
0
          | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT | AOUT_CHAN_REARCENTER
217
0
          | AOUT_CHAN_MIDDLELEFT | AOUT_CHAN_MIDDLERIGHT | AOUT_CHAN_LFE:
218
0
        return "3F2M3R/LFE";
219
0
    }
220
221
0
    return "Unknown-chan-mask";
222
0
}
223
224
#undef aout_FormatPrint
225
void aout_FormatPrint( vlc_object_t *obj, const char *psz_text,
226
                       const audio_sample_format_t *p_format )
227
0
{
228
0
    msg_Dbg( obj, "%s '%4.4s' %d Hz %s frame=%u samples/%u bytes", psz_text,
229
0
             (char *)&p_format->i_format, p_format->i_rate,
230
0
             aout_FormatPrintChannels( p_format ),
231
0
             p_format->i_frame_length, p_format->i_bytes_per_frame );
232
0
}
233
234
#undef aout_FormatsPrint
235
/**
236
 * Prints two formats in a human-readable form
237
 */
238
void aout_FormatsPrint( vlc_object_t *obj, const char * psz_text,
239
                        const audio_sample_format_t * p_format1,
240
                        const audio_sample_format_t * p_format2 )
241
0
{
242
0
    msg_Dbg( obj, "%s '%4.4s'->'%4.4s' %u Hz->%u Hz %s->%s",
243
0
             psz_text,
244
0
             (char *)&p_format1->i_format, (char *)&p_format2->i_format,
245
0
             p_format1->i_rate, p_format2->i_rate,
246
0
             aout_FormatPrintChannels( p_format1 ),
247
0
             aout_FormatPrintChannels( p_format2 ) );
248
0
}
249
250
/*****************************************************************************
251
 * aout_CheckChannelReorder : Check if we need to do some channel re-ordering
252
 *****************************************************************************/
253
unsigned aout_CheckChannelReorder( const uint32_t *chans_in,
254
                                   const uint32_t *chans_out,
255
                                   uint32_t mask, uint8_t *restrict table )
256
20.8k
{
257
20.8k
    static_assert(AOUT_CHAN_MAX <= (sizeof (mask) * CHAR_BIT), "Missing bits");
258
259
20.8k
    unsigned channels = 0;
260
261
20.8k
    if( chans_in == NULL )
262
1
        chans_in = pi_vlc_chan_order_wg4;
263
20.8k
    if( chans_out == NULL )
264
20.8k
        chans_out = pi_vlc_chan_order_wg4;
265
266
122k
    for( unsigned i = 0; chans_in[i]; i++ )
267
101k
    {
268
101k
        const uint32_t chan = chans_in[i];
269
101k
        if( !(mask & chan) )
270
5.09k
            continue;
271
272
96.6k
        unsigned index = 0;
273
383k
        for( unsigned j = 0; chan != chans_out[j]; j++ )
274
286k
            if( mask & chans_out[j] )
275
207k
                index++;
276
277
96.6k
        table[channels++] = index;
278
96.6k
    }
279
280
90.2k
    for( unsigned i = 0; i < channels; i++ )
281
75.9k
        if( table[i] != i )
282
6.53k
            return channels;
283
14.3k
    return 0;
284
20.8k
}
285
286
void aout_ChannelReorder( void *ptr, size_t bytes, uint8_t channels,
287
                          const uint8_t *restrict chans_table, vlc_fourcc_t fourcc )
288
2.63k
{
289
2.63k
    if( unlikely(bytes == 0) )
290
0
        return;
291
292
2.63k
    assert( channels != 0 );
293
294
    /* The audio formats supported in audio output are inlined. For other
295
     * formats (used in demuxers and muxers), memcpy() is used to avoid
296
     * breaking type punning. */
297
2.63k
#define REORDER_TYPE(type) \
298
1.80k
do { \
299
1.80k
    const size_t frames = (bytes / sizeof (type)) / channels; \
300
1.80k
    type *buf = ptr; \
301
1.80k
\
302
317k
    for( size_t i = 0; i < frames; i++ ) \
303
315k
    { \
304
315k
        type tmp[AOUT_CHAN_MAX]; \
305
315k
\
306
2.87M
        for( size_t j = 0; j < channels; j++ ) \
307
2.56M
            tmp[chans_table[j]] = buf[j]; \
308
315k
        memcpy( buf, tmp, sizeof (type) * channels ); \
309
315k
        buf += channels; \
310
315k
    } \
311
1.80k
} while(0)
312
313
2.63k
    if( likely(channels <= AOUT_CHAN_MAX) )
314
2.63k
    {
315
2.63k
        switch( fourcc )
316
2.63k
        {
317
607
            case VLC_CODEC_U8:   REORDER_TYPE(uint8_t); return;
318
301
            case VLC_CODEC_S16N: REORDER_TYPE(int16_t); return;
319
406
            case VLC_CODEC_FL32: REORDER_TYPE(float);   return;
320
266
            case VLC_CODEC_S32N: REORDER_TYPE(int32_t); return;
321
223
            case VLC_CODEC_FL64: REORDER_TYPE(double);  return;
322
2.63k
        }
323
2.63k
    }
324
325
835
    unsigned size = aout_BitsPerSample( fourcc ) / 8;
326
835
    assert( size != 0 && size <= 8 );
327
328
835
    const size_t frames = bytes / (size * channels);
329
835
    unsigned char *buf = ptr;
330
331
2.08k
    for( size_t i = 0; i < frames; i++ )
332
1.24k
    {
333
1.24k
        unsigned char tmp[256 * 8];
334
335
7.86k
        for( size_t j = 0; j < channels; j++ )
336
6.61k
             memcpy( tmp + size * chans_table[j], buf + size * j, size );
337
1.24k
         memcpy( buf, tmp, size * channels );
338
1.24k
         buf += size * channels;
339
1.24k
    }
340
835
}
341
342
/**
343
 * Interleaves audio samples within a block of samples.
344
 * \param dst destination buffer for interleaved samples
345
 * \param srcv source buffers (one per plane) of uninterleaved samples
346
 * \param samples number of samples (per channel/per plane)
347
 * \param chans channels/planes count
348
 * \param fourcc sample format (must be a linear sample format)
349
 * \note The samples must be naturally aligned in memory.
350
 * \warning Destination and source buffers MUST NOT overlap.
351
 */
352
void aout_Interleave( void *restrict dst, const void *const *srcv,
353
                      unsigned samples, unsigned chans, vlc_fourcc_t fourcc )
354
0
{
355
0
#define INTERLEAVE_TYPE(type) \
356
0
do { \
357
0
    type *d = dst; \
358
0
    for( size_t i = 0; i < chans; i++ ) { \
359
0
        const type *s = srcv[i]; \
360
0
        for( size_t j = 0, k = 0; j < samples; j++, k += chans ) \
361
0
            d[k] = *(s++); \
362
0
        d++; \
363
0
    } \
364
0
} while(0)
365
366
0
    switch( fourcc )
367
0
    {
368
0
        case VLC_CODEC_U8:   INTERLEAVE_TYPE(uint8_t);  break;
369
0
        case VLC_CODEC_S16N: INTERLEAVE_TYPE(int16_t);  break;
370
0
        case VLC_CODEC_FL32: INTERLEAVE_TYPE(float);    break;
371
0
        case VLC_CODEC_S32N: INTERLEAVE_TYPE(int32_t);  break;
372
0
        case VLC_CODEC_FL64: INTERLEAVE_TYPE(double);   break;
373
0
        default:             vlc_assert_unreachable();
374
0
    }
375
0
#undef INTERLEAVE_TYPE
376
0
}
377
378
/**
379
 * Deinterleaves audio samples within a block of samples.
380
 * \param dst destination buffer for planar samples
381
 * \param src source buffer with interleaved samples
382
 * \param samples number of samples (per channel/per plane)
383
 * \param chans channels/planes count
384
 * \param fourcc sample format (must be a linear sample format)
385
 * \note The samples must be naturally aligned in memory.
386
 * \warning Destination and source buffers MUST NOT overlap.
387
 */
388
void aout_Deinterleave( void *restrict dst, const void *restrict src,
389
                      unsigned samples, unsigned chans, vlc_fourcc_t fourcc )
390
0
{
391
0
#define DEINTERLEAVE_TYPE(type) \
392
0
do { \
393
0
    type *d = dst; \
394
0
    const type *s = src; \
395
0
    for( size_t i = 0; i < chans; i++ ) { \
396
0
        for( size_t j = 0, k = 0; j < samples; j++, k += chans ) \
397
0
            *(d++) = s[k]; \
398
0
        s++; \
399
0
    } \
400
0
} while(0)
401
402
0
    switch( fourcc )
403
0
    {
404
0
        case VLC_CODEC_U8:   DEINTERLEAVE_TYPE(uint8_t);  break;
405
0
        case VLC_CODEC_S16N: DEINTERLEAVE_TYPE(int16_t);  break;
406
0
        case VLC_CODEC_FL32: DEINTERLEAVE_TYPE(float);    break;
407
0
        case VLC_CODEC_S32N: DEINTERLEAVE_TYPE(int32_t);  break;
408
0
        case VLC_CODEC_FL64: DEINTERLEAVE_TYPE(double);   break;
409
0
        default:             vlc_assert_unreachable();
410
0
    }
411
0
#undef DEINTERLEAVE_TYPE
412
0
}
413
414
/*****************************************************************************
415
 * aout_ChannelExtract:
416
 *****************************************************************************/
417
static inline void ExtractChannel( uint8_t *pi_dst, int i_dst_channels,
418
                                   const uint8_t *pi_src, int i_src_channels,
419
                                   int i_sample_count,
420
                                   const int *pi_selection, int i_bytes )
421
0
{
422
0
    for( int i = 0; i < i_sample_count; i++ )
423
0
    {
424
0
        for( int j = 0; j < i_dst_channels; j++ )
425
0
            memcpy( &pi_dst[j * i_bytes], &pi_src[pi_selection[j] * i_bytes], i_bytes );
426
0
        pi_dst += i_dst_channels * i_bytes;
427
0
        pi_src += i_src_channels * i_bytes;
428
0
    }
429
0
}
430
431
void aout_ChannelExtract( void *p_dst, int i_dst_channels,
432
                          const void *p_src, int i_src_channels,
433
                          int i_sample_count, const int *pi_selection, int i_bits_per_sample )
434
0
{
435
    /* It does not work in place */
436
0
    assert( p_dst != p_src );
437
438
    /* Force the compiler to inline for the specific cases so it can optimize */
439
0
    if( i_bits_per_sample == 8 )
440
0
        ExtractChannel( p_dst, i_dst_channels, p_src, i_src_channels, i_sample_count, pi_selection, 1 );
441
0
    else  if( i_bits_per_sample == 16 )
442
0
        ExtractChannel( p_dst, i_dst_channels, p_src, i_src_channels, i_sample_count, pi_selection, 2 );
443
0
    else  if( i_bits_per_sample == 32 )
444
0
        ExtractChannel( p_dst, i_dst_channels, p_src, i_src_channels, i_sample_count, pi_selection, 4 );
445
0
    else  if( i_bits_per_sample == 64 )
446
0
        ExtractChannel( p_dst, i_dst_channels, p_src, i_src_channels, i_sample_count, pi_selection, 8 );
447
0
}
448
449
bool aout_CheckChannelExtraction( int *pi_selection,
450
                                  uint32_t *pi_layout, int *pi_channels,
451
                                  const uint32_t pi_order_dst[AOUT_CHAN_MAX],
452
                                  const uint32_t *pi_order_src, int i_channels )
453
0
{
454
0
    static_assert(AOUT_CHAN_MAX <= (sizeof (*pi_order_dst) * CHAR_BIT),
455
0
                  "Missing bits");
456
457
0
    uint32_t i_layout = 0;
458
0
    int i_out = 0;
459
0
    int pi_index[AOUT_CHAN_MAX];
460
461
    /* */
462
0
    if( !pi_order_dst )
463
0
        pi_order_dst = pi_vlc_chan_order_wg4;
464
465
    /* */
466
0
    for( int i = 0; i < i_channels; i++ )
467
0
    {
468
        /* Ignore unknown or duplicated channels or not present in output */
469
0
        if( !pi_order_src[i] || (i_layout & pi_order_src[i]) )
470
0
            continue;
471
472
0
        for( int j = 0; j < AOUT_CHAN_MAX; j++ )
473
0
        {
474
0
            if( pi_order_dst[j] == pi_order_src[i] )
475
0
            {
476
0
                assert( i_out < AOUT_CHAN_MAX );
477
0
                pi_index[i_out++] = i;
478
0
                i_layout |= pi_order_src[i];
479
0
                break;
480
0
            }
481
0
        }
482
0
    }
483
484
    /* */
485
0
    for( int i = 0, j = 0; i < AOUT_CHAN_MAX; i++ )
486
0
    {
487
0
        for( int k = 0; k < i_out; k++ )
488
0
        {
489
0
            if( pi_order_dst[i] == pi_order_src[pi_index[k]] )
490
0
            {
491
0
                pi_selection[j++] = pi_index[k];
492
0
                break;
493
0
            }
494
0
        }
495
0
    }
496
497
0
    *pi_layout = i_layout;
498
0
    *pi_channels = i_out;
499
500
0
    for( int i = 0; i < i_out; i++ )
501
0
    {
502
0
        if( pi_selection[i] != i )
503
0
            return true;
504
0
    }
505
0
    return i_out != i_channels;
506
0
}
507
508
/* Return the order in which filters should be inserted */
509
static int FilterOrder( const char *psz_name )
510
0
{
511
0
    static const struct {
512
0
        const char psz_name[10];
513
0
        int        i_order;
514
0
    } filter[] = {
515
0
        { "equalizer",  0 },
516
0
    };
517
0
    for( unsigned i = 0; i < ARRAY_SIZE(filter); i++ )
518
0
    {
519
0
        if( !strcmp( filter[i].psz_name, psz_name ) )
520
0
            return filter[i].i_order;
521
0
    }
522
0
    return INT_MAX;
523
0
}
524
525
int aout_EnableFilter( audio_output_t *p_aout, const char *psz_name, bool b_add )
526
0
{
527
0
    if( *psz_name == '\0' )
528
0
        return VLC_EGENERIC;
529
530
0
    const char *psz_variable = "audio-filter";
531
0
    char *psz_list;
532
0
    psz_list = var_GetString( p_aout, psz_variable );
533
534
    /* Split the string into an array of filters */
535
0
    int i_count = 1;
536
0
    for( char *p = psz_list; p && *p; p++ )
537
0
        i_count += *p == ':';
538
0
    i_count += b_add;
539
540
0
    const char **ppsz_filter = calloc( i_count, sizeof(*ppsz_filter) );
541
0
    if( !ppsz_filter )
542
0
    {
543
0
        free( psz_list );
544
0
        return VLC_ENOMEM;
545
0
    }
546
0
    bool b_present = false;
547
0
    i_count = 0;
548
0
    for( char *p = psz_list; p && *p; )
549
0
    {
550
0
        char *psz_end = strchr(p, ':');
551
0
        if( psz_end )
552
0
            *psz_end++ = '\0';
553
0
        else
554
0
            psz_end = p + strlen(p);
555
0
        if( *p )
556
0
        {
557
0
            b_present |= !strcmp( p, psz_name );
558
0
            ppsz_filter[i_count++] = p;
559
0
        }
560
0
        p = psz_end;
561
0
    }
562
0
    if( b_present == b_add )
563
0
    {
564
0
        free( ppsz_filter );
565
0
        free( psz_list );
566
0
        return VLC_EGENERIC;
567
0
    }
568
569
0
    if( b_add )
570
0
    {
571
0
        int i_order = FilterOrder( psz_name );
572
0
        int i;
573
0
        for( i = 0; i < i_count; i++ )
574
0
        {
575
0
            if( FilterOrder( ppsz_filter[i] ) > i_order )
576
0
                break;
577
0
        }
578
0
        if( i < i_count )
579
0
            memmove( &ppsz_filter[i+1], &ppsz_filter[i], (i_count - i) * sizeof(*ppsz_filter) );
580
0
        ppsz_filter[i] = psz_name;
581
0
        i_count++;
582
0
    }
583
0
    else
584
0
    {
585
0
        for( int i = 0; i < i_count; i++ )
586
0
        {
587
0
            if( !strcmp( ppsz_filter[i], psz_name ) )
588
0
                ppsz_filter[i] = "";
589
0
        }
590
0
    }
591
0
    size_t i_length = 0;
592
0
    for( int i = 0; i < i_count; i++ )
593
0
        i_length += 1 + strlen( ppsz_filter[i] );
594
595
0
    char *psz_new = malloc( i_length + 1 );
596
597
0
    if( unlikely( !psz_new ) )
598
0
    {
599
0
        free( ppsz_filter );
600
0
        free( psz_list );
601
0
        return VLC_ENOMEM;
602
0
    }
603
604
0
    *psz_new = '\0';
605
0
    for( int i = 0; i < i_count; i++ )
606
0
    {
607
0
        if( *ppsz_filter[i] == '\0' )
608
0
            continue;
609
0
        if( *psz_new )
610
0
            strcat( psz_new, ":" );
611
0
        strcat( psz_new, ppsz_filter[i] );
612
0
    }
613
0
    free( ppsz_filter );
614
0
    free( psz_list );
615
616
0
    var_SetString( p_aout, psz_variable, psz_new );
617
0
    free( psz_new );
618
619
0
    return VLC_SUCCESS;
620
0
}