[001/200] Add a new channel layout API

Message ID 20170517174712.8625-2-vittorio.giovara@gmail.com
State New
Headers show

Commit Message

Vittorio Giovara May 17, 2017, 5:46 p.m.
From: Anton Khirnov <anton@khirnov.net>

The new API is more extensible and allows for custom layouts.
More accurate information is exported, eg for decoders that do not
set a channel layout, lavc will not make one up for them.

Deprecate the old API working with just uint64_t bitmasks.

Expanded and completed by Vittorio Giovara <vittorio.giovara@gmail.com>.
Signed-off-by: Vittorio Giovara <vittorio.giovara@gmail.com>
---
 libavutil/channel_layout.c | 387 +++++++++++++++++++++++++++++++++++++--------
 libavutil/channel_layout.h | 363 +++++++++++++++++++++++++++++++++++++++---
 libavutil/version.h        |   3 +
 3 files changed, 663 insertions(+), 90 deletions(-)

Comments

wm4 May 18, 2017, 3:16 p.m. | #1
On Wed, 17 May 2017 13:46:51 -0400
Vittorio Giovara <vittorio.giovara@gmail.com> wrote:

> From: Anton Khirnov <anton@khirnov.net>
> 
> The new API is more extensible and allows for custom layouts.
> More accurate information is exported, eg for decoders that do not
> set a channel layout, lavc will not make one up for them.
> 
> Deprecate the old API working with just uint64_t bitmasks.
> 
> Expanded and completed by Vittorio Giovara <vittorio.giovara@gmail.com>.
> Signed-off-by: Vittorio Giovara <vittorio.giovara@gmail.com>
> ---


> +enum AVChannelOrder {
> +    /**
> +     * The native channel order, i.e. the channels are in the same order in
> +     * which they are defined in the AVChannel enum. This supports up to 63
> +     * different channels.
> +     */
> +    AV_CHANNEL_ORDER_NATIVE,
> +    /**
> +     * The channel order does not correspond to any other predefined order and
> +     * is stored as an explicit map. For example, this could be used to support
> +     * layouts with 64 or more channels, or with channels that could be skipped.
> +     */
> +    AV_CHANNEL_ORDER_CUSTOM,
> +    /**
> +     * Only the channel count is specified, without any further information
> +     * about the channel order.
> +     */
> +    AV_CHANNEL_ORDER_UNSPEC,

Wouldn't it be better to make the value 0 something special like maybe
AV_CHANNEL_ORDER_INVALID? Otherwise it could lead to awkward error
messages etc. if something doesn't set the channel layout at all (it
will look like an invalid channel mask).

> +};
> +

> +    /**
> +     * Details about which channels are present in this layout.
> +     * For AV_CHANNEL_ORDER_UNSPEC, this field is undefined and must not be
> +     * used.
> +     */
> +    union {
> +        /**
> +         * This member must be used for AV_CHANNEL_ORDER_NATIVE.
> +         * It is a bitmask, where the position of each set bit means that the
> +         * AVChannel with the corresponding value is present.
> +         *
> +         * I.e. when (mask & (1 << AV_CHAN_FOO)) is non-zero, then AV_CHAN_FOO
> +         * is present in the layout. Otherwise it is not present.
> +         *
> +         * @note when a channel layout using a bitmask is constructed or
> +         * modified manually (i.e.  not using any of the av_channel_layout_*
> +         * functions), the code doing it must ensure that the number of set bits
> +         * is equal to nb_channels.
> +         */
> +        uint64_t mask;
> +        /**
> +         * This member must be used when the channel order is
> +         * AV_CHANNEL_ORDER_CUSTOM. It is a nb_channels-sized array, with each
> +         * element signalling the presend of the AVChannel with the
> +         * corresponding value.
> +         *
> +         * I.e. when map[i] is equal to AV_CHAN_FOO, then AV_CH_FOO is the i-th
> +         * channel in the audio data.
> +         */
> +        uint8_t *map;

I think I'd still like some better documentation on this one. For
example, what does it mean if a channel label appears twice in the same
map. Would it contain the same data? An alternative version of the data
(and different how)? What value does it even take?

Also, why is this only 1 byte per channel? Seems a little short-sighted.

Maybe it would be better not to include ORDER_CUSTOM for now? And
ambisonics could be a different AV_CHANNEL_ORDER.

What's the rationale for making sizeof(AVChannelLayout) part of the
ABI? It seems generally would need to carefully use special operations
for copying and uninitializing the struct anyway. Why not make it an
opaque object?
Vittorio Giovara May 18, 2017, 7:01 p.m. | #2
On Thu, May 18, 2017 at 11:16 AM, wm4 <nfxjfg@googlemail.com> wrote:
> On Wed, 17 May 2017 13:46:51 -0400
> Vittorio Giovara <vittorio.giovara@gmail.com> wrote:
>
>> From: Anton Khirnov <anton@khirnov.net>
>>
>> The new API is more extensible and allows for custom layouts.
>> More accurate information is exported, eg for decoders that do not
>> set a channel layout, lavc will not make one up for them.
>>
>> Deprecate the old API working with just uint64_t bitmasks.
>>
>> Expanded and completed by Vittorio Giovara <vittorio.giovara@gmail.com>.
>> Signed-off-by: Vittorio Giovara <vittorio.giovara@gmail.com>
>> ---
>
>
>> +enum AVChannelOrder {
>> +    /**
>> +     * The native channel order, i.e. the channels are in the same order in
>> +     * which they are defined in the AVChannel enum. This supports up to 63
>> +     * different channels.
>> +     */
>> +    AV_CHANNEL_ORDER_NATIVE,
>> +    /**
>> +     * The channel order does not correspond to any other predefined order and
>> +     * is stored as an explicit map. For example, this could be used to support
>> +     * layouts with 64 or more channels, or with channels that could be skipped.
>> +     */
>> +    AV_CHANNEL_ORDER_CUSTOM,
>> +    /**
>> +     * Only the channel count is specified, without any further information
>> +     * about the channel order.
>> +     */
>> +    AV_CHANNEL_ORDER_UNSPEC,
>
> Wouldn't it be better to make the value 0 something special like maybe
> AV_CHANNEL_ORDER_INVALID? Otherwise it could lead to awkward error
> messages etc. if something doesn't set the channel layout at all (it
> will look like an invalid channel mask).

well, if we use AV_CHANNEL_ORDER_INVALID as value 0 we make it a
little more cumbersome to API users as they have to manually set this
order every time. Right now this is done only for UNSPEC which is
relatively rare, but I would preserve the 0 = ok philosophy for the
most common case (native). Also in the upcoming series every
channel_layout will be properly checked with av_channel_layout_check()
which will be the only source for correctness of the layout.

>> +};
>> +
>
>> +    /**
>> +     * Details about which channels are present in this layout.
>> +     * For AV_CHANNEL_ORDER_UNSPEC, this field is undefined and must not be
>> +     * used.
>> +     */
>> +    union {
>> +        /**
>> +         * This member must be used for AV_CHANNEL_ORDER_NATIVE.
>> +         * It is a bitmask, where the position of each set bit means that the
>> +         * AVChannel with the corresponding value is present.
>> +         *
>> +         * I.e. when (mask & (1 << AV_CHAN_FOO)) is non-zero, then AV_CHAN_FOO
>> +         * is present in the layout. Otherwise it is not present.
>> +         *
>> +         * @note when a channel layout using a bitmask is constructed or
>> +         * modified manually (i.e.  not using any of the av_channel_layout_*
>> +         * functions), the code doing it must ensure that the number of set bits
>> +         * is equal to nb_channels.
>> +         */
>> +        uint64_t mask;
>> +        /**
>> +         * This member must be used when the channel order is
>> +         * AV_CHANNEL_ORDER_CUSTOM. It is a nb_channels-sized array, with each
>> +         * element signalling the presend of the AVChannel with the
>> +         * corresponding value.
>> +         *
>> +         * I.e. when map[i] is equal to AV_CHAN_FOO, then AV_CH_FOO is the i-th
>> +         * channel in the audio data.
>> +         */
>> +        uint8_t *map;
>
> I think I'd still like some better documentation on this one. For
> example, what does it mean if a channel label appears twice in the same
> map. Would it contain the same data? An alternative version of the data
> (and different how)? What value does it even take?

Yes that could be improved but I'm not sure how (I'm open to suggestions).

If a new channel in a different position would simply mean it's a
completely new channel.
Eg. 16 mono channels to recreate a surround field could be expressed
as a map of 16 elements all initialized to AV_CHAN_FRONT_CENTER. Since
the layout is custom, it's up to the container or codec decide what to
do with them, such as read additional signalling describing how they
are positioned.

> Also, why is this only 1 byte per channel? Seems a little short-sighted.

Probably because even in the long run there won't be more than 255
different channels so 1 byte should be enough.

> Maybe it would be better not to include ORDER_CUSTOM for now? And
> ambisonics could be a different AV_CHANNEL_ORDER.

I'd rather keep it since I find it helpful to have the necessary
checks in the various av_channel_layout_* functions already in place.
Plus API users can start fiddling with it in way we haven't thought of
yet. On your second point, yes, ambisonic will used a different order
entirely, and use a mask only for non diegetic channels if present.

> What's the rationale for making sizeof(AVChannelLayout) part of the
> ABI? It seems generally would need to carefully use special operations
> for copying and uninitializing the struct anyway. Why not make it an
> opaque object?

I find that this made it simpler to use and more similar to what is
currently done now, eg setting the channels and layout directly.
Also I'm not a fan of getters/setters or opaque objects. I think that
sizeof(AVChannelLayout) part of the ABI works of for this particular
struct as we can just expand the union for future orders.
wm4 May 19, 2017, 10:40 a.m. | #3
On Thu, 18 May 2017 15:01:54 -0400
Vittorio Giovara <vittorio.giovara@gmail.com> wrote:

> On Thu, May 18, 2017 at 11:16 AM, wm4 <nfxjfg@googlemail.com> wrote:
> > On Wed, 17 May 2017 13:46:51 -0400
> > Vittorio Giovara <vittorio.giovara@gmail.com> wrote:
> >  
> >> From: Anton Khirnov <anton@khirnov.net>
> >>
> >> The new API is more extensible and allows for custom layouts.
> >> More accurate information is exported, eg for decoders that do not
> >> set a channel layout, lavc will not make one up for them.
> >>
> >> Deprecate the old API working with just uint64_t bitmasks.
> >>
> >> Expanded and completed by Vittorio Giovara <vittorio.giovara@gmail.com>.
> >> Signed-off-by: Vittorio Giovara <vittorio.giovara@gmail.com>
> >> ---  
> >
> >  
> >> +enum AVChannelOrder {
> >> +    /**
> >> +     * The native channel order, i.e. the channels are in the same order in
> >> +     * which they are defined in the AVChannel enum. This supports up to 63
> >> +     * different channels.
> >> +     */
> >> +    AV_CHANNEL_ORDER_NATIVE,
> >> +    /**
> >> +     * The channel order does not correspond to any other predefined order and
> >> +     * is stored as an explicit map. For example, this could be used to support
> >> +     * layouts with 64 or more channels, or with channels that could be skipped.
> >> +     */
> >> +    AV_CHANNEL_ORDER_CUSTOM,
> >> +    /**
> >> +     * Only the channel count is specified, without any further information
> >> +     * about the channel order.
> >> +     */
> >> +    AV_CHANNEL_ORDER_UNSPEC,  
> >
> > Wouldn't it be better to make the value 0 something special like maybe
> > AV_CHANNEL_ORDER_INVALID? Otherwise it could lead to awkward error
> > messages etc. if something doesn't set the channel layout at all (it
> > will look like an invalid channel mask).  
> 
> well, if we use AV_CHANNEL_ORDER_INVALID as value 0 we make it a
> little more cumbersome to API users as they have to manually set this
> order every time. Right now this is done only for UNSPEC which is
> relatively rare, but I would preserve the 0 = ok philosophy for the
> most common case (native). Also in the upcoming series every
> channel_layout will be properly checked with av_channel_layout_check()
> which will be the only source for correctness of the layout.

Won't they just use av_channel_layout_from_mask()? They have to set the
channel count too, which the function does.

> >> +};
> >> +  
> >  
> >> +    /**
> >> +     * Details about which channels are present in this layout.
> >> +     * For AV_CHANNEL_ORDER_UNSPEC, this field is undefined and must not be
> >> +     * used.
> >> +     */
> >> +    union {
> >> +        /**
> >> +         * This member must be used for AV_CHANNEL_ORDER_NATIVE.
> >> +         * It is a bitmask, where the position of each set bit means that the
> >> +         * AVChannel with the corresponding value is present.
> >> +         *
> >> +         * I.e. when (mask & (1 << AV_CHAN_FOO)) is non-zero, then AV_CHAN_FOO
> >> +         * is present in the layout. Otherwise it is not present.
> >> +         *
> >> +         * @note when a channel layout using a bitmask is constructed or
> >> +         * modified manually (i.e.  not using any of the av_channel_layout_*
> >> +         * functions), the code doing it must ensure that the number of set bits
> >> +         * is equal to nb_channels.
> >> +         */
> >> +        uint64_t mask;
> >> +        /**
> >> +         * This member must be used when the channel order is
> >> +         * AV_CHANNEL_ORDER_CUSTOM. It is a nb_channels-sized array, with each
> >> +         * element signalling the presend of the AVChannel with the
> >> +         * corresponding value.
> >> +         *
> >> +         * I.e. when map[i] is equal to AV_CHAN_FOO, then AV_CH_FOO is the i-th
> >> +         * channel in the audio data.
> >> +         */
> >> +        uint8_t *map;  
> >
> > I think I'd still like some better documentation on this one. For
> > example, what does it mean if a channel label appears twice in the same
> > map. Would it contain the same data? An alternative version of the data
> > (and different how)? What value does it even take?  
> 
> Yes that could be improved but I'm not sure how (I'm open to suggestions).
> 
> If a new channel in a different position would simply mean it's a
> completely new channel.
> Eg. 16 mono channels to recreate a surround field could be expressed
> as a map of 16 elements all initialized to AV_CHAN_FRONT_CENTER. Since
> the layout is custom, it's up to the container or codec decide what to
> do with them, such as read additional signalling describing how they
> are positioned.

Well, that's the thing - the map us _supposed_ to inform the user about
the semantics of each channel, otherwise it's worthless. So in the
example you mentioned, it'd be better to use an UNSPEC layout in the
first place.

When I did something similar in mpv, I just strictly defined that no
channel can appear more than once (except zero-padding channels).

> > Also, why is this only 1 byte per channel? Seems a little short-sighted.  
> 
> Probably because even in the long run there won't be more than 255
> different channels so 1 byte should be enough.

Well that seems short-sighted :)

I'm starting to wonder whether it should probably be a permutation map
instead. The meaning of each channel would be strictly defined by the
AV_CHANNEL_ORDER enum value and channel count. _Except_ you could
permutate the order of the channels with an extra map.

Maybe I'm going off a tangent here.

But I'm still trying to gauge how useful the map array is going to be.

> > Maybe it would be better not to include ORDER_CUSTOM for now? And
> > ambisonics could be a different AV_CHANNEL_ORDER.  
> 
> I'd rather keep it since I find it helpful to have the necessary
> checks in the various av_channel_layout_* functions already in place.
> Plus API users can start fiddling with it in way we haven't thought of
> yet. On your second point, yes, ambisonic will used a different order
> entirely, and use a mask only for non diegetic channels if present.

Dunno...

At this point I have to say that av_channel_layout_get_channel() looks
dangerous to me, because it might lead the user to assume the list of
AVChannels defines the channel layout completely. Which is probably not
true.

(Also, it returns an AVERROR as AVChannel enum, which won't work
because a compiler could e.g. make the underlying type uint8_t.)

> > What's the rationale for making sizeof(AVChannelLayout) part of the
> > ABI? It seems generally would need to carefully use special operations
> > for copying and uninitializing the struct anyway. Why not make it an
> > opaque object?  
> 
> I find that this made it simpler to use and more similar to what is
> currently done now, eg setting the channels and layout directly.
> Also I'm not a fan of getters/setters or opaque objects. I think that
> sizeof(AVChannelLayout) part of the ABI works of for this particular
> struct as we can just expand the union for future orders.

I think setter/getters are much safer for a stable ABI in the long run.
You can bet all libav* users hate the constant API changes.

If we had setters/getters for the channel layout (designed in the right
way), it might have been possible to extend the channel layout cleanly
without ABI break.
Vittorio Giovara May 19, 2017, 3:25 p.m. | #4
On Fri, May 19, 2017 at 6:40 AM, wm4 <nfxjfg@googlemail.com> wrote:
>> >> +    /**
>> >> +     * Only the channel count is specified, without any further information
>> >> +     * about the channel order.
>> >> +     */
>> >> +    AV_CHANNEL_ORDER_UNSPEC,
>> >
>> > Wouldn't it be better to make the value 0 something special like maybe
>> > AV_CHANNEL_ORDER_INVALID? Otherwise it could lead to awkward error
>> > messages etc. if something doesn't set the channel layout at all (it
>> > will look like an invalid channel mask).
>>
>> well, if we use AV_CHANNEL_ORDER_INVALID as value 0 we make it a
>> little more cumbersome to API users as they have to manually set this
>> order every time. Right now this is done only for UNSPEC which is
>> relatively rare, but I would preserve the 0 = ok philosophy for the
>> most common case (native). Also in the upcoming series every
>> channel_layout will be properly checked with av_channel_layout_check()
>> which will be the only source for correctness of the layout.
>
> Won't they just use av_channel_layout_from_mask()? They have to set the
> channel count too, which the function does.

Not necessarily, some demuxers for example only set channels instead
of layout, leaving avconv  to "guess" the corresponding layout. One of
the benefits of this API is that it allows lavc not to lie any more
about it, and only provide a compat layer in few well-known places (eg
lavr).

>> >> +        /**
>> >> +         * This member must be used when the channel order is
>> >> +         * AV_CHANNEL_ORDER_CUSTOM. It is a nb_channels-sized array, with each
>> >> +         * element signalling the presend of the AVChannel with the
>> >> +         * corresponding value.
>> >> +         *
>> >> +         * I.e. when map[i] is equal to AV_CHAN_FOO, then AV_CH_FOO is the i-th
>> >> +         * channel in the audio data.
>> >> +         */
>> >> +        uint8_t *map;
>> >
>> > I think I'd still like some better documentation on this one. For
>> > example, what does it mean if a channel label appears twice in the same
>> > map. Would it contain the same data? An alternative version of the data
>> > (and different how)? What value does it even take?
>>
>> Yes that could be improved but I'm not sure how (I'm open to suggestions).
>>
>> If a new channel in a different position would simply mean it's a
>> completely new channel.
>> Eg. 16 mono channels to recreate a surround field could be expressed
>> as a map of 16 elements all initialized to AV_CHAN_FRONT_CENTER. Since
>> the layout is custom, it's up to the container or codec decide what to
>> do with them, such as read additional signalling describing how they
>> are positioned.
>
> Well, that's the thing - the map us _supposed_ to inform the user about
> the semantics of each channel, otherwise it's worthless. So in the
> example you mentioned, it'd be better to use an UNSPEC layout in the
> first place.

Well it depends on how much semantic overload we want this fields to carry.
UNSPEC means that you only know the the layout has N channels.
CUSTOM means that you know that the layout has N channels *and* they
are of type X, Y, Z as defined by the map.
While my example could maybe have been expressed in some way with
UNSPEC too, it seems safe to assume that there are combinations that
could not.

>> > Also, why is this only 1 byte per channel? Seems a little short-sighted.
>>
>> Probably because even in the long run there won't be more than 255
>> different channels so 1 byte should be enough.
>
> Well that seems short-sighted :)

Do you think changing it to (enum AVChannel *) is enough?

> I'm starting to wonder whether it should probably be a permutation map
> instead. The meaning of each channel would be strictly defined by the
> AV_CHANNEL_ORDER enum value and channel count. _Except_ you could
> permutate the order of the channels with an extra map.
>
> Maybe I'm going off a tangent here.
>
> But I'm still trying to gauge how useful the map array is going to be.

IMO it does seem a little bit complicated, but maybe you might be right too.
I leave the decision to the audience.

>> > Maybe it would be better not to include ORDER_CUSTOM for now? And
>> > ambisonics could be a different AV_CHANNEL_ORDER.
>>
>> I'd rather keep it since I find it helpful to have the necessary
>> checks in the various av_channel_layout_* functions already in place.
>> Plus API users can start fiddling with it in way we haven't thought of
>> yet. On your second point, yes, ambisonic will used a different order
>> entirely, and use a mask only for non diegetic channels if present.
>
> Dunno...
>
> At this point I have to say that av_channel_layout_get_channel() looks
> dangerous to me, because it might lead the user to assume the list of
> AVChannels defines the channel layout completely. Which is probably not
> true.

Well this particular case will be well documented.

> (Also, it returns an AVERROR as AVChannel enum, which won't work
> because a compiler could e.g. make the underlying type uint8_t.)

Yes I'll fix it thanks.
wm4 May 20, 2017, 11:18 a.m. | #5
On Fri, 19 May 2017 11:25:59 -0400
Vittorio Giovara <vittorio.giovara@gmail.com> wrote:

> >> > Also, why is this only 1 byte per channel? Seems a little short-sighted.  
> >>
> >> Probably because even in the long run there won't be more than 255
> >> different channels so 1 byte should be enough.  
> >
> > Well that seems short-sighted :)  
> 
> Do you think changing it to (enum AVChannel *) is enough?

Using enum in ABIs is somewhat risky, because some ABIs might make the
underlying type as small as possible. (Clarification on whether C
allows this and/or whether any platforms really do it would be welcome.)
Vittorio Giovara May 22, 2017, 8:08 p.m. | #6
On Sat, May 20, 2017 at 7:18 AM, wm4 <nfxjfg@googlemail.com> wrote:
> On Fri, 19 May 2017 11:25:59 -0400
> Vittorio Giovara <vittorio.giovara@gmail.com> wrote:
>
>> >> > Also, why is this only 1 byte per channel? Seems a little short-sighted.
>> >>
>> >> Probably because even in the long run there won't be more than 255
>> >> different channels so 1 byte should be enough.
>> >
>> > Well that seems short-sighted :)
>>
>> Do you think changing it to (enum AVChannel *) is enough?
>
> Using enum in ABIs is somewhat risky, because some ABIs might make the
> underlying type as small as possible. (Clarification on whether C
> allows this and/or whether any platforms really do it would be welcome.)

I'm ok with int* as well.
Luca Barbato May 23, 2017, 8:06 p.m. | #7
On 5/17/17 7:46 PM, Vittorio Giovara wrote:
> +enum AVChannel av_channel_from_string(const char *str)

Either return an int, return an int and store the enum in a field or
return an INVALID_CHANNEL enum value.

Same for av_channel_layout_get_channel

The rest looks fine.
Diego Biurrun May 24, 2017, 11:43 a.m. | #8
On Mon, May 22, 2017 at 04:08:53PM -0400, Vittorio Giovara wrote:
> On Sat, May 20, 2017 at 7:18 AM, wm4 <nfxjfg@googlemail.com> wrote:
> > On Fri, 19 May 2017 11:25:59 -0400
> > Vittorio Giovara <vittorio.giovara@gmail.com> wrote:
> >
> >> >> > Also, why is this only 1 byte per channel? Seems a little short-sighted.
> >> >>
> >> >> Probably because even in the long run there won't be more than 255
> >> >> different channels so 1 byte should be enough.
> >> >
> >> > Well that seems short-sighted :)
> >>
> >> Do you think changing it to (enum AVChannel *) is enough?
> >
> > Using enum in ABIs is somewhat risky, because some ABIs might make the
> > underlying type as small as possible. (Clarification on whether C
> > allows this and/or whether any platforms really do it would be welcome.)
> 
> I'm ok with int* as well.

C99 6.7.2.2 Enumeration specifiers:

  Each enumerated type shall be compatible with char, a signed integer type,
  or an unsigned integer type. The choice of type is implementation-defined,
  but shall be capable of representing the values of all the members of the
  enumeration.

I'd still go with the more type-safe enum type.

Diego

Patch

diff --git a/libavutil/channel_layout.c b/libavutil/channel_layout.c
index 41340ecdb6..0d91f998ad 100644
--- a/libavutil/channel_layout.c
+++ b/libavutil/channel_layout.c
@@ -31,77 +31,90 @@ 
 #include "common.h"
 
 static const char * const channel_names[] = {
-    [0]  = "FL",        /* front left */
-    [1]  = "FR",        /* front right */
-    [2]  = "FC",        /* front center */
-    [3]  = "LFE",       /* low frequency */
-    [4]  = "BL",        /* back left */
-    [5]  = "BR",        /* back right */
-    [6]  = "FLC",       /* front left-of-center  */
-    [7]  = "FRC",       /* front right-of-center */
-    [8]  = "BC",        /* back-center */
-    [9]  = "SL",        /* side left */
-    [10] = "SR",        /* side right */
-    [11] = "TC",        /* top center */
-    [12] = "TFL",       /* top front left */
-    [13] = "TFC",       /* top front center */
-    [14] = "TFR",       /* top front right */
-    [15] = "TBL",       /* top back left */
-    [16] = "TBC",       /* top back center */
-    [17] = "TBR",       /* top back right */
-    [29] = "DL",        /* downmix left */
-    [30] = "DR",        /* downmix right */
-    [31] = "WL",        /* wide left */
-    [32] = "WR",        /* wide right */
-    [33] = "SDL",       /* surround direct left */
-    [34] = "SDR",       /* surround direct right */
-    [35] = "LFE2",      /* low frequency 2 */
+    [AV_CHAN_FRONT_LEFT              ] = "FL",
+    [AV_CHAN_FRONT_RIGHT             ] = "FR",
+    [AV_CHAN_FRONT_CENTER            ] = "FC",
+    [AV_CHAN_LOW_FREQUENCY           ] = "LFE",
+    [AV_CHAN_BACK_LEFT               ] = "BL",
+    [AV_CHAN_BACK_RIGHT              ] = "BR",
+    [AV_CHAN_FRONT_LEFT_OF_CENTER    ] = "FLC",
+    [AV_CHAN_FRONT_RIGHT_OF_CENTER   ] = "FRC",
+    [AV_CHAN_BACK_CENTER             ] = "BC",
+    [AV_CHAN_SIDE_LEFT               ] = "SL",
+    [AV_CHAN_SIDE_RIGHT              ] = "SR",
+    [AV_CHAN_TOP_CENTER              ] = "TC",
+    [AV_CHAN_TOP_FRONT_LEFT          ] = "TFL",
+    [AV_CHAN_TOP_FRONT_CENTER        ] = "TFC",
+    [AV_CHAN_TOP_FRONT_RIGHT         ] = "TFR",
+    [AV_CHAN_TOP_BACK_LEFT           ] = "TBL",
+    [AV_CHAN_TOP_BACK_CENTER         ] = "TBC",
+    [AV_CHAN_TOP_BACK_RIGHT          ] = "TBR",
+    [AV_CHAN_STEREO_LEFT             ] = "DL",
+    [AV_CHAN_STEREO_RIGHT            ] = "DR",
+    [AV_CHAN_WIDE_LEFT               ] = "WL",
+    [AV_CHAN_WIDE_RIGHT              ] = "WR",
+    [AV_CHAN_SURROUND_DIRECT_LEFT    ] = "SDL",
+    [AV_CHAN_SURROUND_DIRECT_RIGHT   ] = "SDR",
+    [AV_CHAN_LOW_FREQUENCY_2         ] = "LFE2",
+    [AV_CHAN_SILENCE                 ] = "PAD",
 };
 
-static const char *get_channel_name(int channel_id)
+const char *av_channel_name(enum AVChannel channel_id)
 {
-    if (channel_id < 0 || channel_id >= FF_ARRAY_ELEMS(channel_names))
-        return NULL;
+    if ((unsigned) channel_id >= FF_ARRAY_ELEMS(channel_names))
+        return "?";
     return channel_names[channel_id];
 }
 
+enum AVChannel av_channel_from_string(const char *str)
+{
+    int i;
+    for (i = 0; i < FF_ARRAY_ELEMS(channel_names); i++) {
+        if (channel_names[i] && !strcmp(str, channel_names[i])) {
+            return i;
+        }
+    }
+    return AVERROR(EINVAL);
+}
+
 static const struct {
     const char *name;
-    int         nb_channels;
-    uint64_t     layout;
+    AVChannelLayout layout;
 } channel_layout_map[] = {
-    { "mono",        1,  AV_CH_LAYOUT_MONO },
-    { "stereo",      2,  AV_CH_LAYOUT_STEREO },
-    { "stereo",      2,  AV_CH_LAYOUT_STEREO_DOWNMIX },
-    { "2.1",         3,  AV_CH_LAYOUT_2POINT1 },
-    { "3.0",         3,  AV_CH_LAYOUT_SURROUND },
-    { "3.0(back)",   3,  AV_CH_LAYOUT_2_1 },
-    { "3.1",         4,  AV_CH_LAYOUT_3POINT1 },
-    { "4.0",         4,  AV_CH_LAYOUT_4POINT0 },
-    { "quad",        4,  AV_CH_LAYOUT_QUAD },
-    { "quad(side)",  4,  AV_CH_LAYOUT_2_2 },
-    { "4.1",         5,  AV_CH_LAYOUT_4POINT1 },
-    { "5.0",         5,  AV_CH_LAYOUT_5POINT0 },
-    { "5.0",         5,  AV_CH_LAYOUT_5POINT0_BACK },
-    { "5.1",         6,  AV_CH_LAYOUT_5POINT1 },
-    { "5.1",         6,  AV_CH_LAYOUT_5POINT1_BACK },
-    { "6.0",         6,  AV_CH_LAYOUT_6POINT0 },
-    { "6.0(front)",  6,  AV_CH_LAYOUT_6POINT0_FRONT },
-    { "hexagonal",   6,  AV_CH_LAYOUT_HEXAGONAL },
-    { "6.1",         7,  AV_CH_LAYOUT_6POINT1 },
-    { "6.1",         7,  AV_CH_LAYOUT_6POINT1_BACK },
-    { "6.1(front)",  7,  AV_CH_LAYOUT_6POINT1_FRONT },
-    { "7.0",         7,  AV_CH_LAYOUT_7POINT0 },
-    { "7.0(front)",  7,  AV_CH_LAYOUT_7POINT0_FRONT },
-    { "7.1",         8,  AV_CH_LAYOUT_7POINT1 },
-    { "7.1(wide)",   8,  AV_CH_LAYOUT_7POINT1_WIDE },
-    { "7.1(wide)",   8,  AV_CH_LAYOUT_7POINT1_WIDE_BACK },
-    { "octagonal",   8,  AV_CH_LAYOUT_OCTAGONAL },
-    { "hexadecagonal", 16, AV_CH_LAYOUT_HEXADECAGONAL },
-    { "downmix",     2,  AV_CH_LAYOUT_STEREO_DOWNMIX, },
+    { "mono",           AV_CHANNEL_LAYOUT_MONO                },
+    { "stereo",         AV_CHANNEL_LAYOUT_STEREO              },
+    { "stereo",         AV_CHANNEL_LAYOUT_STEREO_DOWNMIX      },
+    { "2.1",            AV_CHANNEL_LAYOUT_2POINT1             },
+    { "3.0",            AV_CHANNEL_LAYOUT_SURROUND            },
+    { "3.0(back)",      AV_CHANNEL_LAYOUT_2_1                 },
+    { "3.1",            AV_CHANNEL_LAYOUT_3POINT1             },
+    { "4.0",            AV_CHANNEL_LAYOUT_4POINT0             },
+    { "quad",           AV_CHANNEL_LAYOUT_QUAD                },
+    { "quad(side)",     AV_CHANNEL_LAYOUT_2_2                 },
+    { "4.1",            AV_CHANNEL_LAYOUT_4POINT1             },
+    { "5.0",            AV_CHANNEL_LAYOUT_5POINT0             },
+    { "5.0(back)",      AV_CHANNEL_LAYOUT_5POINT0_BACK        },
+    { "5.1",            AV_CHANNEL_LAYOUT_5POINT1             },
+    { "5.1(back)",      AV_CHANNEL_LAYOUT_5POINT1_BACK        },
+    { "6.0",            AV_CHANNEL_LAYOUT_6POINT0             },
+    { "6.0(front)",     AV_CHANNEL_LAYOUT_6POINT0_FRONT       },
+    { "hexagonal",      AV_CHANNEL_LAYOUT_HEXAGONAL           },
+    { "6.1",            AV_CHANNEL_LAYOUT_6POINT1             },
+    { "6.1(back)",      AV_CHANNEL_LAYOUT_6POINT1_BACK        },
+    { "6.1(front)",     AV_CHANNEL_LAYOUT_6POINT1_FRONT       },
+    { "7.0",            AV_CHANNEL_LAYOUT_7POINT0             },
+    { "7.0(front)",     AV_CHANNEL_LAYOUT_7POINT0_FRONT       },
+    { "7.1",            AV_CHANNEL_LAYOUT_7POINT1             },
+    { "7.1(wide)",      AV_CHANNEL_LAYOUT_7POINT1_WIDE        },
+    { "7.1(wide-back)", AV_CHANNEL_LAYOUT_7POINT1_WIDE_BACK   },
+    { "octagonal",      AV_CHANNEL_LAYOUT_OCTAGONAL           },
+    { "hexadecagonal",  AV_CHANNEL_LAYOUT_HEXADECAGONAL       },
+    { "downmix",        AV_CHANNEL_LAYOUT_STEREO_DOWNMIX,     },
     { 0 }
 };
 
+#if FF_API_OLD_CHANNEL_LAYOUT
+FF_DISABLE_DEPRECATION_WARNINGS
 static uint64_t get_channel_layout_single(const char *name, int name_len)
 {
     int i;
@@ -111,7 +124,7 @@  static uint64_t get_channel_layout_single(const char *name, int name_len)
     for (i = 0; i < FF_ARRAY_ELEMS(channel_layout_map) - 1; i++) {
         if (strlen(channel_layout_map[i].name) == name_len &&
             !memcmp(channel_layout_map[i].name, name, name_len))
-            return channel_layout_map[i].layout;
+            return channel_layout_map[i].layout.u.mask;
     }
     for (i = 0; i < FF_ARRAY_ELEMS(channel_names); i++)
         if (channel_names[i] &&
@@ -153,8 +166,8 @@  void av_get_channel_layout_string(char *buf, int buf_size,
         nb_channels = av_get_channel_layout_nb_channels(channel_layout);
 
     for (i = 0; channel_layout_map[i].name; i++)
-        if (nb_channels    == channel_layout_map[i].nb_channels &&
-            channel_layout == channel_layout_map[i].layout) {
+        if (nb_channels    == channel_layout_map[i].layout.nb_channels &&
+            channel_layout == channel_layout_map[i].layout.u.mask) {
             av_strlcpy(buf, channel_layout_map[i].name, buf_size);
             return;
         }
@@ -165,7 +178,7 @@  void av_get_channel_layout_string(char *buf, int buf_size,
         av_strlcat(buf, " (", buf_size);
         for (i = 0, ch = 0; i < 64; i++) {
             if ((channel_layout & (UINT64_C(1) << i))) {
-                const char *name = get_channel_name(i);
+                const char *name = av_channel_name(i);
                 if (name) {
                     if (ch > 0)
                         av_strlcat(buf, "|", buf_size);
@@ -216,7 +229,7 @@  const char *av_get_channel_name(uint64_t channel)
         return NULL;
     for (i = 0; i < 64; i++)
         if ((1ULL<<i) & channel)
-            return get_channel_name(i);
+            return av_channel_name(i);
     return NULL;
 }
 
@@ -233,3 +246,249 @@  uint64_t av_channel_layout_extract_channel(uint64_t channel_layout, int index)
     }
     return 0;
 }
+FF_ENABLE_DEPRECATION_WARNINGS
+#endif
+
+void av_channel_layout_from_mask(AVChannelLayout *channel_layout,
+                                 uint64_t mask)
+{
+    channel_layout->order       = AV_CHANNEL_ORDER_NATIVE;
+    channel_layout->nb_channels = av_popcount64(mask);
+    channel_layout->u.mask      = mask;
+}
+
+int av_channel_layout_from_string(AVChannelLayout *channel_layout,
+                                  const char *str)
+{
+    int i, channels;
+    const char *dup = str;
+    uint64_t mask = 0;
+
+    /* channel layout names */
+    for (i = 0; i < FF_ARRAY_ELEMS(channel_layout_map); i++) {
+        if (channel_layout_map[i].name && !strcmp(str, channel_layout_map[i].name)) {
+            *channel_layout = channel_layout_map[i].layout;
+            return 0;
+        }
+    }
+
+    /* channel names */
+    while (*dup) {
+        char *chname = av_get_token(&dup, "|");
+        if (!chname)
+            return AVERROR(ENOMEM);
+        if (*dup)
+            dup++; // skip separator
+        for (i = 0; i < FF_ARRAY_ELEMS(channel_names); i++) {
+            if (channel_names[i] && !strcmp(chname, channel_names[i])) {
+                mask |= 1ULL << i;
+            }
+        }
+        av_free(chname);
+    }
+    if (mask) {
+        av_channel_layout_from_mask(channel_layout, mask);
+        return 0;
+    }
+
+    /* channel layout mask */
+    if (!strncmp(str, "0x", 2) && sscanf(str, "%llx", &mask) == 1) {
+        av_channel_layout_from_mask(channel_layout, mask);
+        return 0;
+    }
+
+    /* number of channels */
+    if (sscanf(str, "%d", &channels) == 1) {
+        av_channel_layout_default(channel_layout, channels);
+        return 0;
+    }
+
+    /* number of unordered channels */
+    if (sscanf(str, "%d channels", &channel_layout->nb_channels) == 1) {
+        channel_layout->order = AV_CHANNEL_ORDER_UNSPEC;
+        return 0;
+    }
+
+    return AVERROR_INVALIDDATA;
+}
+
+void av_channel_layout_uninit(AVChannelLayout *channel_layout)
+{
+    if (channel_layout->order == AV_CHANNEL_ORDER_CUSTOM)
+        av_freep(&channel_layout->u.map);
+    memset(channel_layout, 0, sizeof(*channel_layout));
+}
+
+int av_channel_layout_copy(AVChannelLayout *dst, const AVChannelLayout *src)
+{
+    *dst = *src;
+    if (src->order == AV_CHANNEL_ORDER_CUSTOM) {
+        dst->u.map = av_malloc(src->nb_channels * sizeof(*dst->u.map));
+        if (!dst->u.map)
+            return AVERROR(ENOMEM);
+        memcpy(dst->u.map, src->u.map, src->nb_channels * sizeof(*src->u.map));
+    }
+    return 0;
+}
+
+char *av_channel_layout_describe(const AVChannelLayout *channel_layout)
+{
+    int i;
+
+    switch (channel_layout->order) {
+    case AV_CHANNEL_ORDER_NATIVE:
+        for (i = 0; channel_layout_map[i].name; i++)
+            if (channel_layout->u.mask == channel_layout_map[i].layout.u.mask)
+                return av_strdup(channel_layout_map[i].name);
+        // fall-through
+    case AV_CHANNEL_ORDER_CUSTOM: {
+        // max 4 bytes for channel name + a separator
+        int size = 5 * channel_layout->nb_channels + 1;
+        char *ret;
+
+        ret = av_mallocz(size);
+        if (!ret)
+            return NULL;
+
+        for (i = 0; i < channel_layout->nb_channels; i++) {
+            enum AVChannel ch = av_channel_layout_get_channel(channel_layout, i);
+            const char *ch_name = av_channel_name(ch);
+
+            if (i)
+                av_strlcat(ret, "|", size);
+            av_strlcat(ret, ch_name, size);
+        }
+        return ret;
+        }
+    case AV_CHANNEL_ORDER_UNSPEC: {
+        char buf[64];
+        snprintf(buf, sizeof(buf), "%d channels", channel_layout->nb_channels);
+        return av_strdup(buf);
+        }
+    default:
+        return NULL;
+    }
+}
+
+enum AVChannel av_channel_layout_get_channel(const AVChannelLayout *channel_layout,
+                                             int idx)
+{
+    int i;
+
+    if (idx < 0 || idx >= channel_layout->nb_channels)
+        return AVERROR(EINVAL);
+
+    switch (channel_layout->order) {
+    case AV_CHANNEL_ORDER_CUSTOM:
+        return channel_layout->u.map[idx];
+    case AV_CHANNEL_ORDER_NATIVE:
+        for (i = 0; i < 64; i++) {
+            if ((1ULL << i) & channel_layout->u.mask && !idx--)
+                return i;
+        }
+    default:
+        return AVERROR(EINVAL);
+    }
+}
+
+int av_channel_layout_channel_index(const AVChannelLayout *channel_layout,
+                                    enum AVChannel channel)
+{
+    int i;
+
+    switch (channel_layout->order) {
+    case AV_CHANNEL_ORDER_CUSTOM:
+        for (i = 0; i < channel_layout->nb_channels; i++)
+            if (channel_layout->u.map[i] == channel)
+                return i;
+        return AVERROR(EINVAL);
+    case AV_CHANNEL_ORDER_NATIVE: {
+        uint64_t mask = channel_layout->u.mask;
+        if (!(mask & (1ULL << channel)))
+            return AVERROR(EINVAL);
+        mask &= (1ULL << channel) - 1;
+        return av_popcount64(mask);
+        }
+    default:
+        return AVERROR(EINVAL);
+    }
+}
+
+int av_channel_layout_check(const AVChannelLayout *channel_layout)
+{
+    if (!channel_layout || channel_layout->nb_channels <= 0)
+        return 0;
+
+    switch (channel_layout->order) {
+    case AV_CHANNEL_ORDER_NATIVE:
+        return av_popcount64(channel_layout->u.mask) == channel_layout->nb_channels;
+    case AV_CHANNEL_ORDER_CUSTOM:
+        return !!channel_layout->u.map;
+    case AV_CHANNEL_ORDER_UNSPEC:
+        return 1;
+    default:
+        return 0;
+    }
+}
+
+int av_channel_layout_compare(const AVChannelLayout *chl, const AVChannelLayout *chl1)
+{
+    int i;
+
+    /* different channel counts -> not equal */
+    if (chl->nb_channels != chl1->nb_channels)
+        return 1;
+
+    /* if only one is unspecified -> not equal */
+    if ((chl->order  == AV_CHANNEL_ORDER_UNSPEC) !=
+        (chl1->order == AV_CHANNEL_ORDER_UNSPEC))
+        return 1;
+    /* both are unspecified -> equal */
+    else if (chl->order == AV_CHANNEL_ORDER_UNSPEC)
+        return 0;
+
+    /* can compare masks directly */
+    if (chl->order != AV_CHANNEL_ORDER_CUSTOM &&
+        chl->order == chl1->order)
+        return chl->u.mask != chl1->u.mask;
+
+    /* compare channel by channel */
+    for (i = 0; i < chl->nb_channels; i++)
+        if (av_channel_layout_get_channel(chl,  i) !=
+            av_channel_layout_get_channel(chl1, i))
+            return 1;
+    return 0;
+}
+
+void av_channel_layout_default(AVChannelLayout *ch_layout, int nb_channels)
+{
+    switch (nb_channels) {
+    case 1: *ch_layout = (AVChannelLayout)AV_CHANNEL_LAYOUT_MONO;     break;
+    case 2: *ch_layout = (AVChannelLayout)AV_CHANNEL_LAYOUT_STEREO;   break;
+    case 3: *ch_layout = (AVChannelLayout)AV_CHANNEL_LAYOUT_SURROUND; break;
+    case 4: *ch_layout = (AVChannelLayout)AV_CHANNEL_LAYOUT_QUAD;     break;
+    case 5: *ch_layout = (AVChannelLayout)AV_CHANNEL_LAYOUT_5POINT0;  break;
+    case 6: *ch_layout = (AVChannelLayout)AV_CHANNEL_LAYOUT_5POINT1;  break;
+    case 7: *ch_layout = (AVChannelLayout)AV_CHANNEL_LAYOUT_6POINT1;  break;
+    case 8: *ch_layout = (AVChannelLayout)AV_CHANNEL_LAYOUT_7POINT1;  break;
+    default:
+        ch_layout->order       = AV_CHANNEL_ORDER_UNSPEC;
+        ch_layout->nb_channels = nb_channels;
+    }
+}
+
+uint64_t av_channel_layout_subset(const AVChannelLayout *channel_layout,
+                                  uint64_t mask)
+{
+    uint64_t ret = 0;
+    int i;
+
+    if (channel_layout->order == AV_CHANNEL_ORDER_NATIVE)
+        return channel_layout->u.mask & mask;
+
+    for (i = 0; i < 64; i++)
+        if (mask & (1ULL << i) && av_channel_layout_channel_index(channel_layout, i) >= 0)
+            ret |= (1ULL << i);
+
+    return ret;
+}
diff --git a/libavutil/channel_layout.h b/libavutil/channel_layout.h
index 5bd0c2ce6e..746baf53c1 100644
--- a/libavutil/channel_layout.h
+++ b/libavutil/channel_layout.h
@@ -24,6 +24,9 @@ 
 
 #include <stdint.h>
 
+#include "version.h"
+#include "attributes.h"
+
 /**
  * @file
  * audio channel layout utility functions
@@ -34,40 +37,99 @@ 
  * @{
  */
 
+enum AVChannel {
+    AV_CHAN_FRONT_LEFT,
+    AV_CHAN_FRONT_RIGHT,
+    AV_CHAN_FRONT_CENTER,
+    AV_CHAN_LOW_FREQUENCY,
+    AV_CHAN_BACK_LEFT,
+    AV_CHAN_BACK_RIGHT,
+    AV_CHAN_FRONT_LEFT_OF_CENTER,
+    AV_CHAN_FRONT_RIGHT_OF_CENTER,
+    AV_CHAN_BACK_CENTER,
+    AV_CHAN_SIDE_LEFT,
+    AV_CHAN_SIDE_RIGHT,
+    AV_CHAN_TOP_CENTER,
+    AV_CHAN_TOP_FRONT_LEFT,
+    AV_CHAN_TOP_FRONT_CENTER,
+    AV_CHAN_TOP_FRONT_RIGHT,
+    AV_CHAN_TOP_BACK_LEFT,
+    AV_CHAN_TOP_BACK_CENTER,
+    AV_CHAN_TOP_BACK_RIGHT,
+    /** Stereo downmix. */
+    AV_CHAN_STEREO_LEFT = 29,
+    /** See above. */
+    AV_CHAN_STEREO_RIGHT,
+    AV_CHAN_WIDE_LEFT,
+    AV_CHAN_WIDE_RIGHT,
+    AV_CHAN_SURROUND_DIRECT_LEFT,
+    AV_CHAN_SURROUND_DIRECT_RIGHT,
+    AV_CHAN_LOW_FREQUENCY_2,
+
+    /** Channel is empty can be safely skipped. */
+    AV_CHAN_SILENCE = 64,
+};
+
+enum AVChannelOrder {
+    /**
+     * The native channel order, i.e. the channels are in the same order in
+     * which they are defined in the AVChannel enum. This supports up to 63
+     * different channels.
+     */
+    AV_CHANNEL_ORDER_NATIVE,
+    /**
+     * The channel order does not correspond to any other predefined order and
+     * is stored as an explicit map. For example, this could be used to support
+     * layouts with 64 or more channels, or with channels that could be skipped.
+     */
+    AV_CHANNEL_ORDER_CUSTOM,
+    /**
+     * Only the channel count is specified, without any further information
+     * about the channel order.
+     */
+    AV_CHANNEL_ORDER_UNSPEC,
+};
+
+
 /**
  * @defgroup channel_masks Audio channel masks
  * @{
  */
-#define AV_CH_FRONT_LEFT             0x00000001
-#define AV_CH_FRONT_RIGHT            0x00000002
-#define AV_CH_FRONT_CENTER           0x00000004
-#define AV_CH_LOW_FREQUENCY          0x00000008
-#define AV_CH_BACK_LEFT              0x00000010
-#define AV_CH_BACK_RIGHT             0x00000020
-#define AV_CH_FRONT_LEFT_OF_CENTER   0x00000040
-#define AV_CH_FRONT_RIGHT_OF_CENTER  0x00000080
-#define AV_CH_BACK_CENTER            0x00000100
-#define AV_CH_SIDE_LEFT              0x00000200
-#define AV_CH_SIDE_RIGHT             0x00000400
-#define AV_CH_TOP_CENTER             0x00000800
-#define AV_CH_TOP_FRONT_LEFT         0x00001000
-#define AV_CH_TOP_FRONT_CENTER       0x00002000
-#define AV_CH_TOP_FRONT_RIGHT        0x00004000
-#define AV_CH_TOP_BACK_LEFT          0x00008000
-#define AV_CH_TOP_BACK_CENTER        0x00010000
-#define AV_CH_TOP_BACK_RIGHT         0x00020000
-#define AV_CH_STEREO_LEFT            0x20000000  ///< Stereo downmix.
-#define AV_CH_STEREO_RIGHT           0x40000000  ///< See AV_CH_STEREO_LEFT.
-#define AV_CH_WIDE_LEFT              0x0000000080000000ULL
-#define AV_CH_WIDE_RIGHT             0x0000000100000000ULL
-#define AV_CH_SURROUND_DIRECT_LEFT   0x0000000200000000ULL
-#define AV_CH_SURROUND_DIRECT_RIGHT  0x0000000400000000ULL
-#define AV_CH_LOW_FREQUENCY_2        0x0000000800000000ULL
+#define AV_CH_FRONT_LEFT             (1ULL << AV_CHAN_FRONT_LEFT           )
+#define AV_CH_FRONT_RIGHT            (1ULL << AV_CHAN_FRONT_RIGHT          )
+#define AV_CH_FRONT_CENTER           (1ULL << AV_CHAN_FRONT_CENTER         )
+#define AV_CH_LOW_FREQUENCY          (1ULL << AV_CHAN_LOW_FREQUENCY        )
+#define AV_CH_BACK_LEFT              (1ULL << AV_CHAN_BACK_LEFT            )
+#define AV_CH_BACK_RIGHT             (1ULL << AV_CHAN_BACK_RIGHT           )
+#define AV_CH_FRONT_LEFT_OF_CENTER   (1ULL << AV_CHAN_FRONT_LEFT_OF_CENTER )
+#define AV_CH_FRONT_RIGHT_OF_CENTER  (1ULL << AV_CHAN_FRONT_RIGHT_OF_CENTER)
+#define AV_CH_BACK_CENTER            (1ULL << AV_CHAN_BACK_CENTER          )
+#define AV_CH_SIDE_LEFT              (1ULL << AV_CHAN_SIDE_LEFT            )
+#define AV_CH_SIDE_RIGHT             (1ULL << AV_CHAN_SIDE_RIGHT           )
+#define AV_CH_TOP_CENTER             (1ULL << AV_CHAN_TOP_CENTER           )
+#define AV_CH_TOP_FRONT_LEFT         (1ULL << AV_CHAN_TOP_FRONT_LEFT       )
+#define AV_CH_TOP_FRONT_CENTER       (1ULL << AV_CHAN_TOP_FRONT_CENTER     )
+#define AV_CH_TOP_FRONT_RIGHT        (1ULL << AV_CHAN_TOP_FRONT_RIGHT      )
+#define AV_CH_TOP_BACK_LEFT          (1ULL << AV_CHAN_TOP_BACK_LEFT        )
+#define AV_CH_TOP_BACK_CENTER        (1ULL << AV_CHAN_TOP_BACK_CENTER      )
+#define AV_CH_TOP_BACK_RIGHT         (1ULL << AV_CHAN_TOP_BACK_RIGHT       )
+#define AV_CH_STEREO_LEFT            (1ULL << AV_CHAN_STEREO_LEFT          )
+#define AV_CH_STEREO_RIGHT           (1ULL << AV_CHAN_STEREO_RIGHT         )
+#define AV_CH_WIDE_LEFT              (1ULL << AV_CHAN_WIDE_LEFT            )
+#define AV_CH_WIDE_RIGHT             (1ULL << AV_CHAN_WIDE_RIGHT           )
+#define AV_CH_SURROUND_DIRECT_LEFT   (1ULL << AV_CHAN_SURROUND_DIRECT_LEFT )
+#define AV_CH_SURROUND_DIRECT_RIGHT  (1ULL << AV_CHAN_SURROUND_DIRECT_RIGHT)
+#define AV_CH_LOW_FREQUENCY_2        (1ULL << AV_CHAN_LOW_FREQUENCY_2      )
 
+#if FF_API_OLD_CHANNEL_LAYOUT
 /** Channel mask value used for AVCodecContext.request_channel_layout
     to indicate that the user requests the channel order of the decoder output
-    to be the native codec channel order. */
+    to be the native codec channel order.
+    @deprecated channel order is now indicated in a special field in
+                AVChannelLayout
+    */
 #define AV_CH_LAYOUT_NATIVE          0x8000000000000000ULL
+#endif
 
 /**
  * @}
@@ -119,6 +181,123 @@  enum AVMatrixEncoding {
  */
 
 /**
+ * An AVChannelLayout holds information about the channel layout of audio data.
+ *
+ * A channel layout here is defined as a set of channels ordered in a specific
+ * way (unless the channel order is AV_CHANNEL_ORDER_UNSPEC, in which case an
+ * AVChannelLayout carries only the channel count).
+ *
+ * Unlike most structures in Libav, sizeof(AVChannelLayout) is a part of the
+ * public ABI and may be used by the caller. E.g. it may be allocated on stack.
+ * No new fields may be added to it without a major version bump.
+ *
+ * An AVChannelLayout can be constructed using the convenience function
+ * av_channel_layout_from_mask() / av_channel_layout_from_string(), or it can be
+ * built manually by the caller.
+ */
+typedef struct AVChannelLayout {
+    /**
+     * Channel order used in this layout.
+     */
+    enum AVChannelOrder order;
+
+    /**
+     * Number of channels in this layout. Mandatory field.
+     */
+    int nb_channels;
+
+    /**
+     * Details about which channels are present in this layout.
+     * For AV_CHANNEL_ORDER_UNSPEC, this field is undefined and must not be
+     * used.
+     */
+    union {
+        /**
+         * This member must be used for AV_CHANNEL_ORDER_NATIVE.
+         * It is a bitmask, where the position of each set bit means that the
+         * AVChannel with the corresponding value is present.
+         *
+         * I.e. when (mask & (1 << AV_CHAN_FOO)) is non-zero, then AV_CHAN_FOO
+         * is present in the layout. Otherwise it is not present.
+         *
+         * @note when a channel layout using a bitmask is constructed or
+         * modified manually (i.e.  not using any of the av_channel_layout_*
+         * functions), the code doing it must ensure that the number of set bits
+         * is equal to nb_channels.
+         */
+        uint64_t mask;
+        /**
+         * This member must be used when the channel order is
+         * AV_CHANNEL_ORDER_CUSTOM. It is a nb_channels-sized array, with each
+         * element signalling the presend of the AVChannel with the
+         * corresponding value.
+         *
+         * I.e. when map[i] is equal to AV_CHAN_FOO, then AV_CH_FOO is the i-th
+         * channel in the audio data.
+         */
+        uint8_t *map;
+    } u;
+} AVChannelLayout;
+
+#define AV_CHANNEL_LAYOUT_MONO \
+    { .order = AV_CHANNEL_ORDER_NATIVE, .nb_channels = 1,  .u = { .mask = AV_CH_LAYOUT_MONO }}
+#define AV_CHANNEL_LAYOUT_STEREO \
+    { .order = AV_CHANNEL_ORDER_NATIVE, .nb_channels = 2,  .u = { .mask = AV_CH_LAYOUT_STEREO }}
+#define AV_CHANNEL_LAYOUT_2POINT1 \
+    { .order = AV_CHANNEL_ORDER_NATIVE, .nb_channels = 3,  .u = { .mask = AV_CH_LAYOUT_2POINT1 }}
+#define AV_CHANNEL_LAYOUT_2_1 \
+    { .order = AV_CHANNEL_ORDER_NATIVE, .nb_channels = 3,  .u = { .mask = AV_CH_LAYOUT_2_1 }}
+#define AV_CHANNEL_LAYOUT_SURROUND \
+    { .order = AV_CHANNEL_ORDER_NATIVE, .nb_channels = 3,  .u = { .mask = AV_CH_LAYOUT_SURROUND }}
+#define AV_CHANNEL_LAYOUT_3POINT1 \
+    { .order = AV_CHANNEL_ORDER_NATIVE, .nb_channels = 4,  .u = { .mask = AV_CH_LAYOUT_3POINT1 }}
+#define AV_CHANNEL_LAYOUT_4POINT0 \
+    { .order = AV_CHANNEL_ORDER_NATIVE, .nb_channels = 4,  .u = { .mask = AV_CH_LAYOUT_4POINT0 }}
+#define AV_CHANNEL_LAYOUT_4POINT1 \
+    { .order = AV_CHANNEL_ORDER_NATIVE, .nb_channels = 5,  .u = { .mask = AV_CH_LAYOUT_4POINT1 }}
+#define AV_CHANNEL_LAYOUT_2_2 \
+    { .order = AV_CHANNEL_ORDER_NATIVE, .nb_channels = 4,  .u = { .mask = AV_CH_LAYOUT_2_2 }}
+#define AV_CHANNEL_LAYOUT_QUAD \
+    { .order = AV_CHANNEL_ORDER_NATIVE, .nb_channels = 4,  .u = { .mask = AV_CH_LAYOUT_QUAD }}
+#define AV_CHANNEL_LAYOUT_5POINT0 \
+    { .order = AV_CHANNEL_ORDER_NATIVE, .nb_channels = 5,  .u = { .mask = AV_CH_LAYOUT_5POINT0 }}
+#define AV_CHANNEL_LAYOUT_5POINT1 \
+    { .order = AV_CHANNEL_ORDER_NATIVE, .nb_channels = 6,  .u = { .mask = AV_CH_LAYOUT_5POINT1 }}
+#define AV_CHANNEL_LAYOUT_5POINT0_BACK \
+    { .order = AV_CHANNEL_ORDER_NATIVE, .nb_channels = 5,  .u = { .mask = AV_CH_LAYOUT_5POINT0_BACK }}
+#define AV_CHANNEL_LAYOUT_5POINT1_BACK \
+    { .order = AV_CHANNEL_ORDER_NATIVE, .nb_channels = 6,  .u = { .mask = AV_CH_LAYOUT_5POINT1_BACK }}
+#define AV_CHANNEL_LAYOUT_6POINT0 \
+    { .order = AV_CHANNEL_ORDER_NATIVE, .nb_channels = 6,  .u = { .mask = AV_CH_LAYOUT_6POINT0 }}
+#define AV_CHANNEL_LAYOUT_6POINT0_FRONT \
+    { .order = AV_CHANNEL_ORDER_NATIVE, .nb_channels = 6,  .u = { .mask = AV_CH_LAYOUT_6POINT0_FRONT }}
+#define AV_CHANNEL_LAYOUT_HEXAGONAL \
+    { .order = AV_CHANNEL_ORDER_NATIVE, .nb_channels = 6,  .u = { .mask = AV_CH_LAYOUT_HEXAGONAL }}
+#define AV_CHANNEL_LAYOUT_6POINT1 \
+    { .order = AV_CHANNEL_ORDER_NATIVE, .nb_channels = 7,  .u = { .mask = AV_CH_LAYOUT_6POINT1 }}
+#define AV_CHANNEL_LAYOUT_6POINT1_BACK \
+    { .order = AV_CHANNEL_ORDER_NATIVE, .nb_channels = 7,  .u = { .mask = AV_CH_LAYOUT_6POINT1_BACK }}
+#define AV_CHANNEL_LAYOUT_6POINT1_FRONT \
+    { .order = AV_CHANNEL_ORDER_NATIVE, .nb_channels = 7,  .u = { .mask = AV_CH_LAYOUT_6POINT1_FRONT }}
+#define AV_CHANNEL_LAYOUT_7POINT0 \
+    { .order = AV_CHANNEL_ORDER_NATIVE, .nb_channels = 7,  .u = { .mask = AV_CH_LAYOUT_7POINT0 }}
+#define AV_CHANNEL_LAYOUT_7POINT0_FRONT \
+    { .order = AV_CHANNEL_ORDER_NATIVE, .nb_channels = 7,  .u = { .mask = AV_CH_LAYOUT_7POINT0_FRONT }}
+#define AV_CHANNEL_LAYOUT_7POINT1 \
+    { .order = AV_CHANNEL_ORDER_NATIVE, .nb_channels = 8,  .u = { .mask = AV_CH_LAYOUT_7POINT1 }}
+#define AV_CHANNEL_LAYOUT_7POINT1_WIDE \
+    { .order = AV_CHANNEL_ORDER_NATIVE, .nb_channels = 8,  .u = { .mask = AV_CH_LAYOUT_7POINT1_WIDE }}
+#define AV_CHANNEL_LAYOUT_7POINT1_WIDE_BACK \
+    { .order = AV_CHANNEL_ORDER_NATIVE, .nb_channels = 8,  .u = { .mask = AV_CH_LAYOUT_7POINT1_WIDE_BACK }}
+#define AV_CHANNEL_LAYOUT_OCTAGONAL \
+    { .order = AV_CHANNEL_ORDER_NATIVE, .nb_channels = 8,  .u = { .mask = AV_CH_LAYOUT_OCTAGONAL }}
+#define AV_CHANNEL_LAYOUT_HEXADECAGONAL \
+    { .order = AV_CHANNEL_ORDER_NATIVE, .nb_channels = 16, .u = { .mask = AV_CH_LAYOUT_HEXAGONAL }}
+#define AV_CHANNEL_LAYOUT_STEREO_DOWNMIX \
+    { .order = AV_CHANNEL_ORDER_NATIVE, .nb_channels = 2,  .u = { .mask = AV_CH_LAYOUT_STEREO_DOWNMIX }}
+
+#if FF_API_OLD_CHANNEL_LAYOUT
+/**
  * Return a channel layout id that matches name, or 0 if no match is found.
  *
  * name can be one or several of the following notations,
@@ -134,7 +313,10 @@  enum AVMatrixEncoding {
  *   AV_CH_* macros).
  *
  * Example: "stereo+FC" = "2+FC" = "2c+1c" = "0x7"
+ *
+ * @deprecated use av_channel_layout_from_string()
  */
+attribute_deprecated
 uint64_t av_get_channel_layout(const char *name);
 
 /**
@@ -143,17 +325,24 @@  uint64_t av_get_channel_layout(const char *name);
  *
  * @param buf put here the string containing the channel layout
  * @param buf_size size in bytes of the buffer
+ * @deprecated use av_channel_layout_describe()
  */
+attribute_deprecated
 void av_get_channel_layout_string(char *buf, int buf_size, int nb_channels, uint64_t channel_layout);
 
 /**
  * Return the number of channels in the channel layout.
+ * @deprecated use AVChannelLayout.nb_channels
  */
+attribute_deprecated
 int av_get_channel_layout_nb_channels(uint64_t channel_layout);
 
 /**
  * Return default channel layout for a given number of channels.
+ *
+ * @deprecated use av_channel_layout_default()
  */
+attribute_deprecated
 uint64_t av_get_default_channel_layout(int nb_channels);
 
 /**
@@ -164,21 +353,143 @@  uint64_t av_get_default_channel_layout(int nb_channels);
  *
  * @return index of channel in channel_layout on success, a negative AVERROR
  *         on error.
+ *
+ * @deprecated use av_channel_layout_channel_index()
  */
+attribute_deprecated
 int av_get_channel_layout_channel_index(uint64_t channel_layout,
                                         uint64_t channel);
 
 /**
  * Get the channel with the given index in channel_layout.
+ * @deprecated use av_channel_layout_get_channel()
  */
+attribute_deprecated
 uint64_t av_channel_layout_extract_channel(uint64_t channel_layout, int index);
 
 /**
  * Get the name of a given channel.
  *
  * @return channel name on success, NULL on error.
+ *
+ * @deprecated use av_channel_name()
  */
+attribute_deprecated
 const char *av_get_channel_name(uint64_t channel);
+#endif
+
+/**
+ * @return a string describing a given channel.
+ */
+const char *av_channel_name(enum AVChannel channel);
+
+/**
+ * @return a channel described by the given string.
+ */
+enum AVChannel av_channel_from_string(const char *name);
+
+/**
+ * Initialize a native channel layout from a bitmask indicating which channels
+ * are present.
+ */
+void av_channel_layout_from_mask(AVChannelLayout *channel_layout, uint64_t mask);
+
+/**
+ * Initialize a channel layout from a given string description.
+ * The input string can be represented by:
+ *  - the formal channel layout name (returned by av_channel_layout_describe())
+ *  - single or multiple channel names (returned by av_channel_name()
+ *    or concatenated with "|")
+ *  - a hexadecimal value of a channel layout (eg. "0x4")
+ *  - the number of channels with default layout (eg. "5")
+ *  - the number of unordered channels (eg. "4 channels")
+ *
+ * @param channel_layout input channel layout
+ * @param str string describing the channel layout
+ * @return 0 channel layout was detected, AVERROR_INVALIDATATA otherwise
+ */
+int av_channel_layout_from_string(AVChannelLayout *channel_layout,
+                                  const char *str);
+
+/**
+ * Get the default channel layout for a given number of channels.
+ */
+void av_channel_layout_default(AVChannelLayout *ch_layout, int nb_channels);
+
+/**
+ * Free any allocated data in the channel layout and reset the channel
+ * count to 0.
+ */
+void av_channel_layout_uninit(AVChannelLayout *channel_layout);
+
+/**
+ * Make a copy of a channel layout. This differs from just assigning src to dst
+ * in that it allocates and copies the map for AV_CHANNEL_ORDER_CUSTOM.
+ *
+ * @param dst destination channel layout
+ * @param src source channel layout
+ * @return 0 on success, a negative AVERROR on error.
+ */
+int av_channel_layout_copy(AVChannelLayout *dst, const AVChannelLayout *src);
+
+/**
+ * @return a string describing channel_layout or NULL on failure in the same
+ *         format that is accepted by av_channel_layout_from_string(). The
+ *         returned string allocated with av_malloc() and must be freed by the
+ *         caller with av_free().
+ */
+char *av_channel_layout_describe(const AVChannelLayout *channel_layout);
+
+/**
+ * Get the channel with the given index in a channel layout.
+ *
+ * @return channel with the index idx in channel_layout on success or a negative
+ *                 AVERROR on failure (if idx is not valid or the channel order
+ *                 is unspecified)
+ */
+enum AVChannel av_channel_layout_get_channel(const AVChannelLayout *channel_layout,
+                                             int idx);
+
+/**
+ * Get the index of a given channel in a channel layout.
+ *
+ * @return index of channel in channel_layout on success or a negative number if
+ *         channel is not present in channel_layout.
+ */
+int av_channel_layout_channel_index(const AVChannelLayout *channel_layout,
+                                    enum AVChannel channel);
+
+/**
+ * Find out what channels from a given set are present in a channel layout,
+ * without regard for their positions.
+ *
+ * @param mask a combination of AV_CH_* representing a set of channels
+ * @return a bitfield representing all the channels from mask that are present
+ *         in channel_layout
+ */
+uint64_t av_channel_layout_subset(const AVChannelLayout *channel_layout,
+                                  uint64_t mask);
+
+/**
+ * Check whether a channel layout is valid, i.e. can possibly describe audio
+ * data.
+ *
+ * @return 1 if channel_layout is valid, 0 otherwise.
+ */
+int av_channel_layout_check(const AVChannelLayout *channel_layout);
+
+/**
+ * Check whether two channel layouts are semantically the same, i.e. the same
+ * channels are present on the same positions in both.
+ *
+ * If one of the channel layouts is AV_CHANNEL_ORDER_UNSPEC, while the other is
+ * not, they are considered to be unequal. If both are AV_CHANNEL_ORDER_UNSPEC,
+ * they are considered equal iff the channel counts are the same in both.
+ *
+ * @return 0 if chl and chl1 are equal, 1 if they are not equal. A negative
+ * AVERROR code if one or both are invalid.
+ */
+int av_channel_layout_compare(const AVChannelLayout *chl, const AVChannelLayout *chl1);
 
 /**
  * @}
diff --git a/libavutil/version.h b/libavutil/version.h
index 7779755870..199a88433d 100644
--- a/libavutil/version.h
+++ b/libavutil/version.h
@@ -96,6 +96,9 @@ 
 #ifndef FF_API_CRYPTO_SIZE_T
 #define FF_API_CRYPTO_SIZE_T            (LIBAVUTIL_VERSION_MAJOR < 57)
 #endif
+#ifndef FF_API_OLD_CHANNEL_LAYOUT
+#define FF_API_OLD_CHANNEL_LAYOUT       (LIBAVUTIL_VERSION_MAJOR < 57)
+#endif
 
 
 /**