diff mbox series

[FFmpeg-devel,001/281] Add a new channel layout API

Message ID 20220113015101.4-2-jamrial@gmail.com
State New
Headers show
Series New channel layout API | expand

Commit Message

James Almer Jan. 13, 2022, 1:49 a.m. UTC
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>
and James Almer <jamrial@gmail.com>.
Signed-off-by: Vittorio Giovara <vittorio.giovara@gmail.com>
Signed-off-by: James Almer <jamrial@gmail.com>
---
 libavutil/channel_layout.c | 629 ++++++++++++++++++++++++++++++++-----
 libavutil/channel_layout.h | 542 ++++++++++++++++++++++++++++++--
 libavutil/version.h        |   1 +
 3 files changed, 1069 insertions(+), 103 deletions(-)

Comments

Lynne Jan. 13, 2022, 2:08 p.m. UTC | #1
13 Jan 2022, 02:49 by jamrial@gmail.com:

> 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>
> and James Almer <jamrial@gmail.com>.
> Signed-off-by: Vittorio Giovara <vittorio.giovara@gmail.com>
> Signed-off-by: James Almer <jamrial@gmail.com>
> ---
>  libavutil/channel_layout.c | 629 ++++++++++++++++++++++++++++++++-----
>  libavutil/channel_layout.h | 542 ++++++++++++++++++++++++++++++--
>  libavutil/version.h        |   1 +
>  3 files changed, 1069 insertions(+), 103 deletions(-)
>

API looks good to me now.
Nicolas George Jan. 16, 2022, 11:27 a.m. UTC | #2
James Almer (12022-01-12):
> 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>
> and James Almer <jamrial@gmail.com>.
> Signed-off-by: Vittorio Giovara <vittorio.giovara@gmail.com>
> Signed-off-by: James Almer <jamrial@gmail.com>
> ---
>  libavutil/channel_layout.c | 629 ++++++++++++++++++++++++++++++++-----
>  libavutil/channel_layout.h | 542 ++++++++++++++++++++++++++++++--
>  libavutil/version.h        |   1 +
>  3 files changed, 1069 insertions(+), 103 deletions(-)

Thank you. I have no fundamental objection to the design of the API as
it is, but the user interface and documentation is still missing, that
needs to be addressed before the patch goes in.

(But IIRC, Marton had other requirements, so let us wait for him to
weigh in.)

> +/**
> + * 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. "5c")
> + *  - the number of unordered channels (eg. "4", "4C", or "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);

The documentation for the syntax needs to be in the user documentation,
with examples, not just in the API documentation.

> +/**
> + * This is the inverse function of @ref av_channel_name().
> + *
> + * @return the channel with the given name
> + *         AV_CHAN_NONE when name does not identify a known channel
> + */
> +enum AVChannel av_channel_from_string(const char *name);

> +/**
> + * Get a channel described by the given string.
> + *
> + * This function accepts channel names in the same format as
> + * @ref av_channel_from_string().
> + *
> + * @param channel_layout input channel layout
> + * @return a channel described by the given string, or a negative AVERROR value.
> + */
> +int av_channel_layout_channel_from_string(const AVChannelLayout *channel_layout,
> +                                          const char *name);

This looks to be the preferred function for when the user will specify a
channel in a layout.

First, this fact should be stated clearly in the introduction of the
documentation. Otherwise, people will likely use other functions,
probably av_channel_layout_channel_from_index().

Second, the "name" argument cannot be just a name argument: the user
must be able to say "the third FC channel" or "the FC channel with name
'piano'". And probably both at once.

idx = av_channel_layout_channel_from_string(layout, "FC");
idx = av_channel_layout_channel_from_string(layout, "FC#2");
idx = av_channel_layout_channel_from_string(layout, "FC[piano]");
idx = av_channel_layout_channel_from_string(layout, "FC[piano]#2");

(I think it would be acceptable to limit the name, for example "names
with non-alphanumeric ASCII characters are not supported.)

And this need to go in the user documentation.

I am not sure if we also need a function to extract "all the FL channels
with name 'piano'".

Regards,
Marton Balint Jan. 16, 2022, 10:54 p.m. UTC | #3
On Sun, 16 Jan 2022, Nicolas George wrote:

> James Almer (12022-01-12):
>> 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>
>> and James Almer <jamrial@gmail.com>.
>> Signed-off-by: Vittorio Giovara <vittorio.giovara@gmail.com>
>> Signed-off-by: James Almer <jamrial@gmail.com>
>> ---
>>  libavutil/channel_layout.c | 629 ++++++++++++++++++++++++++++++++-----
>>  libavutil/channel_layout.h | 542 ++++++++++++++++++++++++++++++--
>>  libavutil/version.h        |   1 +
>>  3 files changed, 1069 insertions(+), 103 deletions(-)
>
> Thank you. I have no fundamental objection to the design of the API as
> it is, but the user interface and documentation is still missing, that
> needs to be addressed before the patch goes in.
>
> (But IIRC, Marton had other requirements, so let us wait for him to
> weigh in.)

My extensible metadata idea was not popular, so I am willing to let it go.

>
>> +/**
>> + * 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. "5c")
>> + *  - the number of unordered channels (eg. "4", "4C", or "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);
>
> The documentation for the syntax needs to be in the user documentation,
> with examples, not just in the API documentation.
>
>> +/**
>> + * This is the inverse function of @ref av_channel_name().
>> + *
>> + * @return the channel with the given name
>> + *         AV_CHAN_NONE when name does not identify a known channel
>> + */
>> +enum AVChannel av_channel_from_string(const char *name);
>
>> +/**
>> + * Get a channel described by the given string.
>> + *
>> + * This function accepts channel names in the same format as
>> + * @ref av_channel_from_string().
>> + *
>> + * @param channel_layout input channel layout
>> + * @return a channel described by the given string, or a negative AVERROR value.
>> + */
>> +int av_channel_layout_channel_from_string(const AVChannelLayout *channel_layout,
>> +                                          const char *name);
>
> This looks to be the preferred function for when the user will specify a
> channel in a layout.
>
> First, this fact should be stated clearly in the introduction of the
> documentation. Otherwise, people will likely use other functions,
> probably av_channel_layout_channel_from_index().
>
> Second, the "name" argument cannot be just a name argument: the user
> must be able to say "the third FC channel" or "the FC channel with name
> 'piano'". And probably both at once.
>
> idx = av_channel_layout_channel_from_string(layout, "FC");
> idx = av_channel_layout_channel_from_string(layout, "FC#2");
> idx = av_channel_layout_channel_from_string(layout, "FC[piano]");
> idx = av_channel_layout_channel_from_string(layout, "FC[piano]#2");
>
> (I think it would be acceptable to limit the name, for example "names
> with non-alphanumeric ASCII characters are not supported.)
>
> And this need to go in the user documentation.
>
> I am not sure if we also need a function to extract "all the FL channels
> with name 'piano'".

Before discussing channel selection syntax, can we discuss serialization?

Should the serializaton and unserialization functions store/parse both 
the channel label and the channel designation? As far as I see right now 
it is kind of inconsistent: av_channel_layout_describe_print() prints the 
label (if exists), not the designation, but av_channel_layout_from_string() 
expects the designations only, never the custom label.

There is still confusion what custom channel labels should be used for. Is 
it only used for string labels for user channel designations or is it used 
by the end user to tag its channels unrelated to their designation? 
"Piano" example suggest the latter, but API is designed for the former...

Regards,
Marton
James Almer Jan. 17, 2022, 1:22 p.m. UTC | #4
On 1/16/2022 7:54 PM, Marton Balint wrote:
> 
> 
> On Sun, 16 Jan 2022, Nicolas George wrote:
> 
>> James Almer (12022-01-12):
>>> 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>
>>> and James Almer <jamrial@gmail.com>.
>>> Signed-off-by: Vittorio Giovara <vittorio.giovara@gmail.com>
>>> Signed-off-by: James Almer <jamrial@gmail.com>
>>> ---
>>>  libavutil/channel_layout.c | 629 ++++++++++++++++++++++++++++++++-----
>>>  libavutil/channel_layout.h | 542 ++++++++++++++++++++++++++++++--
>>>  libavutil/version.h        |   1 +
>>>  3 files changed, 1069 insertions(+), 103 deletions(-)
>>
>> Thank you. I have no fundamental objection to the design of the API as
>> it is, but the user interface and documentation is still missing, that
>> needs to be addressed before the patch goes in.
>>
>> (But IIRC, Marton had other requirements, so let us wait for him to
>> weigh in.)
> 
> My extensible metadata idea was not popular, so I am willing to let it go.
> 
>>
>>> +/**
>>> + * 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. "5c")
>>> + *  - the number of unordered channels (eg. "4", "4C", or "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);
>>
>> The documentation for the syntax needs to be in the user documentation,
>> with examples, not just in the API documentation.
>>
>>> +/**
>>> + * This is the inverse function of @ref av_channel_name().
>>> + *
>>> + * @return the channel with the given name
>>> + *         AV_CHAN_NONE when name does not identify a known channel
>>> + */
>>> +enum AVChannel av_channel_from_string(const char *name);
>>
>>> +/**
>>> + * Get a channel described by the given string.
>>> + *
>>> + * This function accepts channel names in the same format as
>>> + * @ref av_channel_from_string().
>>> + *
>>> + * @param channel_layout input channel layout
>>> + * @return a channel described by the given string, or a negative 
>>> AVERROR value.
>>> + */
>>> +int av_channel_layout_channel_from_string(const AVChannelLayout 
>>> *channel_layout,
>>> +                                          const char *name);
>>
>> This looks to be the preferred function for when the user will specify a
>> channel in a layout.
>>
>> First, this fact should be stated clearly in the introduction of the
>> documentation. Otherwise, people will likely use other functions,
>> probably av_channel_layout_channel_from_index().
>>
>> Second, the "name" argument cannot be just a name argument: the user
>> must be able to say "the third FC channel" or "the FC channel with name
>> 'piano'". And probably both at once.
>>
>> idx = av_channel_layout_channel_from_string(layout, "FC");
>> idx = av_channel_layout_channel_from_string(layout, "FC#2");
>> idx = av_channel_layout_channel_from_string(layout, "FC[piano]");
>> idx = av_channel_layout_channel_from_string(layout, "FC[piano]#2");
>>
>> (I think it would be acceptable to limit the name, for example "names
>> with non-alphanumeric ASCII characters are not supported.)
>>
>> And this need to go in the user documentation.
>>
>> I am not sure if we also need a function to extract "all the FL channels
>> with name 'piano'".
> 
> Before discussing channel selection syntax, can we discuss serialization?
> 
> Should the serializaton and unserialization functions store/parse both 
> the channel label and the channel designation? As far as I see right now 
> it is kind of inconsistent: av_channel_layout_describe_print() prints 
> the label (if exists), not the designation, but 
> av_channel_layout_from_string() expects the designations only, never the 
> custom label.

You're still thinking there's a distinction, when i already told you 
that there is none. I added the name field because people wanted to give 
non standard channel names, and maybe also change the standard ones too. 
It's not a label to go alongside a designation, it's *a* name.
There are about 20 channels that have a standard name from waveformat 
and extensions, while the rest lack one. You can obviously have a non 
standard speaker setup with 50 channels, and all those extra speakers 
can surely have a name based on their position (Say, RTFC, to refer to 
"right of top front center"), so the field lets you give it to them if 
that's convenient for you and you want a pretty print output of the 
layout without seeing things like USR49. That's it.

Yes, av_channel_layout_from_string() will not be able to parse the 
output of av_channel_layout_from_describe() if you gave channels a 
custom name, since they are specifically from that other layout. There's 
no way around that, as we can't make describe() output some string that 
from_string() can interpret for those because then describe() will be 
useless for printing the layout for human readability purposes.
It is in fact a good reason to either remove the name field or stop 
making these helpers look at it, since describe() is meant to create 
strings from_string() can parse.

I personally would do just that and keep the opaque fields alone. 
Otherwise I'll make the helpers stop looking at it, since after your 
request, non standard channels can be addressed as USR#, so you can 
create layout from strings with them just fine.
The opaque field is more than enough to give channels custom names and 
even implement all kinds of crazy unconventional inter-filter usage 
within libavfilter like Nicolas wants to without overdesigning the 
helpers for virtually unused scenarios outside of our own libraries.

> 
> There is still confusion what custom channel labels should be used for. 
> Is it only used for string labels for user channel designations or is it 
> used by the end user to tag its channels unrelated to their designation? 
> "Piano" example suggest the latter, but API is designed for the former...

It's what i said above. The piano stuff is Nicolas personal usecase for 
libavfilter internal behavior that's beyond the scope of these helpers.

> 
> Regards,
> Marton
> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel@ffmpeg.org
> https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
> 
> To unsubscribe, visit link above, or email
> ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
James Almer Jan. 17, 2022, 1:32 p.m. UTC | #5
On 1/16/2022 8:27 AM, Nicolas George wrote:
> James Almer (12022-01-12):
>> 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>
>> and James Almer <jamrial@gmail.com>.
>> Signed-off-by: Vittorio Giovara <vittorio.giovara@gmail.com>
>> Signed-off-by: James Almer <jamrial@gmail.com>
>> ---
>>   libavutil/channel_layout.c | 629 ++++++++++++++++++++++++++++++++-----
>>   libavutil/channel_layout.h | 542 ++++++++++++++++++++++++++++++--
>>   libavutil/version.h        |   1 +
>>   3 files changed, 1069 insertions(+), 103 deletions(-)
> 
> Thank you. I have no fundamental objection to the design of the API as
> it is, but the user interface and documentation is still missing, that
> needs to be addressed before the patch goes in.
> 
> (But IIRC, Marton had other requirements, so let us wait for him to
> weigh in.)
> 
>> +/**
>> + * 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. "5c")
>> + *  - the number of unordered channels (eg. "4", "4C", or "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);
> 
> The documentation for the syntax needs to be in the user documentation,
> with examples, not just in the API documentation.

I'll look into it.

> 
>> +/**
>> + * This is the inverse function of @ref av_channel_name().
>> + *
>> + * @return the channel with the given name
>> + *         AV_CHAN_NONE when name does not identify a known channel
>> + */
>> +enum AVChannel av_channel_from_string(const char *name);
> 
>> +/**
>> + * Get a channel described by the given string.
>> + *
>> + * This function accepts channel names in the same format as
>> + * @ref av_channel_from_string().
>> + *
>> + * @param channel_layout input channel layout
>> + * @return a channel described by the given string, or a negative AVERROR value.
>> + */
>> +int av_channel_layout_channel_from_string(const AVChannelLayout *channel_layout,
>> +                                          const char *name);
> 
> This looks to be the preferred function for when the user will specify a
> channel in a layout.
> 
> First, this fact should be stated clearly in the introduction of the
> documentation. Otherwise, people will likely use other functions,
> probably av_channel_layout_channel_from_index().

And they can if they want to. It has a very specific purpose and it 
fulfills it.

> 
> Second, the "name" argument cannot be just a name argument: the user
> must be able to say "the third FC channel" or "the FC channel with name
> 'piano'". And probably both at once.
> 
> idx = av_channel_layout_channel_from_string(layout, "FC");
> idx = av_channel_layout_channel_from_string(layout, "FC#2");
> idx = av_channel_layout_channel_from_string(layout, "FC[piano]");
> idx = av_channel_layout_channel_from_string(layout, "FC[piano]#2");

Please, stop asking for this. It's an incredibly niche usecase you want 
for libavfilter, so you can and should implement it there. The API is 
there and you can use it, you don't need to overdesign these general 
purpose helpers to create these bizarre scenarios just to remove one or 
two filters from your chain.

You have the opaque field in both the layout and each channel. You can 
store a pointer there to anything you want at any point in your chain, 
like an internal and refcounted lavfi struct that stores all this 
information filters in the chain can parse and use.

This is a layout of speaker positions. If you want metadata, use metadata.

> 
> (I think it would be acceptable to limit the name, for example "names
> with non-alphanumeric ASCII characters are not supported.)
> 
> And this need to go in the user documentation.
> 
> I am not sure if we also need a function to extract "all the FL channels
> with name 'piano'".
> 
> Regards,
> 
> 
> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel@ffmpeg.org
> https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
> 
> To unsubscribe, visit link above, or email
> ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
Nicolas George Jan. 17, 2022, 1:51 p.m. UTC | #6
James Almer (12022-01-17):
> And they can if they want to. It has a very specific purpose and it fulfills
> it.

Just be clearer in the documentation.

> Please, stop asking for this. It's an incredibly niche usecase you want for
> libavfilter, so you can and should implement it there. The API is there and
> you can use it, you don't need to overdesign these general purpose helpers
> to create these bizarre scenarios just to remove one or two filters from
> your chain.

I will not stop asking for this, it is not specific to libavfilter: with
a quick search, I spot -map_channel that requires exactly the same
feature.

There may be filters that require an option to distinguish one channel
in particular; even if there are no now, there may be in the future. Can
you guarantee it does not and will not happen?

This use case happens at several places, therefore the parsing belongs
in a common API.

Regards,
Nicolas George Jan. 17, 2022, 1:53 p.m. UTC | #7
Marton Balint (12022-01-16):
> Should the serializaton and unserialization functions store/parse both the
> channel label and the channel designation? As far as I see right now it is
> kind of inconsistent: av_channel_layout_describe_print() prints the label
> (if exists), not the designation, but av_channel_layout_from_string()
> expects the designations only, never the custom label.

Oh, excellent point. av_channel_layout_from_string() should be able to
parse the output of av_channel_layout_describe_bprint() in all cases.
And it should be covered by FATE.

Regards,
James Almer Jan. 17, 2022, 1:54 p.m. UTC | #8
On 1/17/2022 10:53 AM, Nicolas George wrote:
> Marton Balint (12022-01-16):
>> Should the serializaton and unserialization functions store/parse both the
>> channel label and the channel designation? As far as I see right now it is
>> kind of inconsistent: av_channel_layout_describe_print() prints the label
>> (if exists), not the designation, but av_channel_layout_from_string()
>> expects the designations only, never the custom label.
> 
> Oh, excellent point. av_channel_layout_from_string() should be able to
> parse the output of av_channel_layout_describe_bprint() in all cases.
> And it should be covered by FATE.

Yes, which is why I'll make describe() stop looking at the name field.

> 
> Regards,
> 
> 
> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel@ffmpeg.org
> https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
> 
> To unsubscribe, visit link above, or email
> ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
Nicolas George Jan. 17, 2022, 1:56 p.m. UTC | #9
James Almer (12022-01-17):
> Yes, which is why I'll make describe() stop looking at the name field.

Unacceptable.
James Almer Jan. 17, 2022, 2:02 p.m. UTC | #10
On 1/17/2022 10:56 AM, Nicolas George wrote:
> James Almer (12022-01-17):
>> Yes, which is why I'll make describe() stop looking at the name field.
> 
> Unacceptable.

Either that, or the field is removed. The opaque field is more than 
enough for your usecase. lavfi can and should use it to do everything 
you need.
Alternative, you can, at any time, extend this API yourself to cover all 
kinds of crazy musical instrumental scenarios. Create a new layout order 
and go nuts. But I'm not going to overdesign this API or its helpers 
just because you want less code in lavfi.

There are real world files and real world scenarios waiting for this API 
to land, unlike front center pianos and clarinets wishing they could 
skip one entry in your -filter_complex line, so it's not an excuse to 
delay it any longer.

> 
> 
> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel@ffmpeg.org
> https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
> 
> To unsubscribe, visit link above, or email
> ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
James Almer Jan. 17, 2022, 2:12 p.m. UTC | #11
On 1/17/2022 10:51 AM, Nicolas George wrote:
> James Almer (12022-01-17):
>> And they can if they want to. It has a very specific purpose and it fulfills
>> it.
> 
> Just be clearer in the documentation.
> 
>> Please, stop asking for this. It's an incredibly niche usecase you want for
>> libavfilter, so you can and should implement it there. The API is there and
>> you can use it, you don't need to overdesign these general purpose helpers
>> to create these bizarre scenarios just to remove one or two filters from
>> your chain.
> 
> I will not stop asking for this, it is not specific to libavfilter: with
> a quick search, I spot -map_channel that requires exactly the same
> feature.

-map_channel works perfectly with the current bitmask API, and the new one.

> 
> There may be filters that require an option to distinguish one channel
> in particular; even if there are no now, there may be in the future. Can
> you guarantee it does not and will not happen?

Look at their IDs? Look at their names? Give them names if you need to?
And again, you keep talking about filtering scenarios. Lavfi can and 
should handle all this on its own internally, just like it was designed 
to, and not pollute an API meant to define the position a channel is 
supposed to be.

> 
> This use case happens at several places, therefore the parsing belongs
> in a common API.
> 
> Regards,
> 
> 
> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel@ffmpeg.org
> https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
> 
> To unsubscribe, visit link above, or email
> ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
Nicolas George Jan. 17, 2022, 4:48 p.m. UTC | #12
James Almer (12022-01-17):
> -map_channel works perfectly with the current bitmask API, and the new one.

Not if there are several times the same channel with different labels.
It should.
Nicolas George Jan. 17, 2022, 4:50 p.m. UTC | #13
James Almer (12022-01-17):
> Either that, or the field is removed.

Are we discussing together to design the best API possible or are you a
dictator making threats?

The label field stays, and the parse and stringify functions must be
reciprocal of each other. The API is unacceptable otherwise.
James Almer Jan. 17, 2022, 4:50 p.m. UTC | #14
On 1/17/2022 1:48 PM, Nicolas George wrote:
> James Almer (12022-01-17):
>> -map_channel works perfectly with the current bitmask API, and the new one.
> 
> Not if there are several times the same channel with different labels.
> It should.

Patches are always welcome for new or updated functionality.

> 
> 
> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel@ffmpeg.org
> https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
> 
> To unsubscribe, visit link above, or email
> ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
James Almer Jan. 17, 2022, 4:52 p.m. UTC | #15
On 1/17/2022 1:50 PM, Nicolas George wrote:
> James Almer (12022-01-17):
>> Either that, or the field is removed.
> 
> Are we discussing together to design the best API possible or are you a
> dictator making threats?

Like you said below, the functions must be reciprocal, so they can't 
take that field into account. Removing the field is not necessary, but 
is another option.

> 
> The label field stays, and the parse and stringify functions must be
> reciprocal of each other. The API is unacceptable otherwise.
> 
> 
> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel@ffmpeg.org
> https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
> 
> To unsubscribe, visit link above, or email
> ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
Nicolas George Jan. 17, 2022, 4:54 p.m. UTC | #16
James Almer (12022-01-17):
> Like you said below, the functions must be reciprocal, so they can't take
> that field into account.

A serializaion function is supposed to serialize the whole structure. I
will not accept less.
James Almer Jan. 17, 2022, 4:57 p.m. UTC | #17
On 1/17/2022 1:54 PM, Nicolas George wrote:
> James Almer (12022-01-17):
>> Like you said below, the functions must be reciprocal, so they can't take
>> that field into account.
> 
> A serializaion function is supposed to serialize the whole structure. I
> will not accept less.

It will print the whole structure, what makes you think it wont? It 
doesn't need custom names to be set to do that.

> 
> 
> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel@ffmpeg.org
> https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
> 
> To unsubscribe, visit link above, or email
> ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
Nicolas George Jan. 17, 2022, 5:55 p.m. UTC | #18
James Almer (12022-01-17):
> It will print the whole structure, what makes you think it wont? It doesn't
> need custom names to be set to do that.

If it prints the whole structure, including the names, and if the
parsing function parses the whole result, including the names, it is
good. Anything else... needs more work.
Marton Balint Jan. 17, 2022, 8:18 p.m. UTC | #19
On Mon, 17 Jan 2022, James Almer wrote:

>
>
> You're still thinking there's a distinction, when i already told you that 
> there is none. I added the name field because people wanted to give non 
> standard channel names, and maybe also change the standard ones too. It's not 
> a label to go alongside a designation, it's *a* name.
> There are about 20 channels that have a standard name from waveformat and 
> extensions, while the rest lack one. You can obviously have a non standard 
> speaker setup with 50 channels, and all those extra speakers can surely have 
> a name based on their position (Say, RTFC, to refer to "right of top front 
> center"), so the field lets you give it to them if that's convenient for you 
> and you want a pretty print output of the layout without seeing things like 
> USR49. That's it.

OK, but shouldn't the user be able to specify if it means a builtin name 
or a custom name when specifying a channel name?

That is why I suggested some additinal syntax for custom names 
in av_channel_layout_index_from_string() and 
av_channel_layout_channel_from_string(). Like "FL" is a builtin name, 
"@name" is a custom name.

>
> Yes, av_channel_layout_from_string() will not be able to parse the output of 
> av_channel_layout_from_describe() if you gave channels a custom name, since 
> they are specifically from that other layout. There's no way around that, as 
> we can't make describe() output some string that from_string() can interpret 
> for those because then describe() will be useless for printing the layout for 
> human readability purposes.
> It is in fact a good reason to either remove the name field or stop making 
> these helpers look at it, since describe() is meant to create strings 
> from_string() can parse.
>
> I personally would do just that and keep the opaque fields alone. Otherwise 
> I'll make the helpers stop looking at it, since after your request, non 
> standard channels can be addressed as USR#, so you can create layout from 
> strings with them just fine.

Removing the custom names is fine with me. Maybe it's a compromise nobody 
is particularly happy about.

Regards,
Marton
James Almer Jan. 17, 2022, 8:27 p.m. UTC | #20
On 1/17/2022 5:18 PM, Marton Balint wrote:
> 
> 
> On Mon, 17 Jan 2022, James Almer wrote:
> 
>>
>>
>> You're still thinking there's a distinction, when i already told you 
>> that there is none. I added the name field because people wanted to 
>> give non standard channel names, and maybe also change the standard 
>> ones too. It's not a label to go alongside a designation, it's *a* name.
>> There are about 20 channels that have a standard name from waveformat 
>> and extensions, while the rest lack one. You can obviously have a non 
>> standard speaker setup with 50 channels, and all those extra speakers 
>> can surely have a name based on their position (Say, RTFC, to refer to 
>> "right of top front center"), so the field lets you give it to them if 
>> that's convenient for you and you want a pretty print output of the 
>> layout without seeing things like USR49. That's it.
> 
> OK, but shouldn't the user be able to specify if it means a builtin name 
> or a custom name when specifying a channel name?
> 
> That is why I suggested some additinal syntax for custom names in 
> av_channel_layout_index_from_string() and 
> av_channel_layout_channel_from_string(). Like "FL" is a builtin name, 
> "@name" is a custom name.
> 
>>
>> Yes, av_channel_layout_from_string() will not be able to parse the 
>> output of av_channel_layout_from_describe() if you gave channels a 
>> custom name, since they are specifically from that other layout. 
>> There's no way around that, as we can't make describe() output some 
>> string that from_string() can interpret for those because then 
>> describe() will be useless for printing the layout for human 
>> readability purposes.
>> It is in fact a good reason to either remove the name field or stop 
>> making these helpers look at it, since describe() is meant to create 
>> strings from_string() can parse.
>>
>> I personally would do just that and keep the opaque fields alone. 
>> Otherwise I'll make the helpers stop looking at it, since after your 
>> request, non standard channels can be addressed as USR#, so you can 
>> create layout from strings with them just fine.
> 
> Removing the custom names is fine with me. Maybe it's a compromise 
> nobody is particularly happy about.

I will not remove it. It has its uses even if helpers don't make 
extended use of it at first.

Advanced syntax can be implemented later. See my comments in v4.

> 
> Regards,
> Marton
> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel@ffmpeg.org
> https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
> 
> To unsubscribe, visit link above, or email
> ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
diff mbox series

Patch

diff --git a/libavutil/channel_layout.c b/libavutil/channel_layout.c
index ac773a9e63..68b40cc37c 100644
--- a/libavutil/channel_layout.c
+++ b/libavutil/channel_layout.c
@@ -37,81 +37,151 @@  struct channel_name {
 };
 
 static const struct channel_name 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"       },
-    [36] = { "TSL",       "top side left"         },
-    [37] = { "TSR",       "top side right"        },
-    [38] = { "BFC",       "bottom front center"   },
-    [39] = { "BFL",       "bottom front left"     },
-    [40] = { "BFR",       "bottom front right"    },
+    [AV_CHAN_FRONT_LEFT           ] = { "FL",        "front left"            },
+    [AV_CHAN_FRONT_RIGHT          ] = { "FR",        "front right"           },
+    [AV_CHAN_FRONT_CENTER         ] = { "FC",        "front center"          },
+    [AV_CHAN_LOW_FREQUENCY        ] = { "LFE",       "low frequency"         },
+    [AV_CHAN_BACK_LEFT            ] = { "BL",        "back left"             },
+    [AV_CHAN_BACK_RIGHT           ] = { "BR",        "back right"            },
+    [AV_CHAN_FRONT_LEFT_OF_CENTER ] = { "FLC",       "front left-of-center"  },
+    [AV_CHAN_FRONT_RIGHT_OF_CENTER] = { "FRC",       "front right-of-center" },
+    [AV_CHAN_BACK_CENTER          ] = { "BC",        "back center"           },
+    [AV_CHAN_SIDE_LEFT            ] = { "SL",        "side left"             },
+    [AV_CHAN_SIDE_RIGHT           ] = { "SR",        "side right"            },
+    [AV_CHAN_TOP_CENTER           ] = { "TC",        "top center"            },
+    [AV_CHAN_TOP_FRONT_LEFT       ] = { "TFL",       "top front left"        },
+    [AV_CHAN_TOP_FRONT_CENTER     ] = { "TFC",       "top front center"      },
+    [AV_CHAN_TOP_FRONT_RIGHT      ] = { "TFR",       "top front right"       },
+    [AV_CHAN_TOP_BACK_LEFT        ] = { "TBL",       "top back left"         },
+    [AV_CHAN_TOP_BACK_CENTER      ] = { "TBC",       "top back center"       },
+    [AV_CHAN_TOP_BACK_RIGHT       ] = { "TBR",       "top back right"        },
+    [AV_CHAN_STEREO_LEFT          ] = { "DL",        "downmix left"          },
+    [AV_CHAN_STEREO_RIGHT         ] = { "DR",        "downmix right"         },
+    [AV_CHAN_WIDE_LEFT            ] = { "WL",        "wide left"             },
+    [AV_CHAN_WIDE_RIGHT           ] = { "WR",        "wide right"            },
+    [AV_CHAN_SURROUND_DIRECT_LEFT ] = { "SDL",       "surround direct left"  },
+    [AV_CHAN_SURROUND_DIRECT_RIGHT] = { "SDR",       "surround direct right" },
+    [AV_CHAN_LOW_FREQUENCY_2      ] = { "LFE2",      "low frequency 2"       },
+    [AV_CHAN_TOP_SIDE_LEFT        ] = { "TSL",       "top side left"         },
+    [AV_CHAN_TOP_SIDE_RIGHT       ] = { "TSR",       "top side right"        },
+    [AV_CHAN_BOTTOM_FRONT_CENTER  ] = { "BFC",       "bottom front center"   },
+    [AV_CHAN_BOTTOM_FRONT_LEFT    ] = { "BFL",       "bottom front left"     },
+    [AV_CHAN_BOTTOM_FRONT_RIGHT   ] = { "BFR",       "bottom front right"    },
 };
 
-static const char *get_channel_name(int channel_id)
+static const char *get_channel_name(enum AVChannel channel_id)
 {
-    if (channel_id < 0 || channel_id >= FF_ARRAY_ELEMS(channel_names))
+    if ((unsigned) channel_id >= FF_ARRAY_ELEMS(channel_names) ||
+        !channel_names[channel_id].name)
         return NULL;
     return channel_names[channel_id].name;
 }
 
-static const struct {
+void av_channel_name_bprint(AVBPrint *bp, enum AVChannel channel_id)
+{
+    av_bprint_clear(bp);
+
+    if ((unsigned)channel_id < FF_ARRAY_ELEMS(channel_names))
+        av_bprintf(bp, "%s", channel_names[channel_id].name);
+    else
+        av_bprintf(bp, "USR%d", channel_id);
+}
+
+int av_channel_name(char *buf, size_t buf_size, enum AVChannel channel_id)
+{
+    AVBPrint bp;
+
+    if (!buf && buf_size)
+        return AVERROR(EINVAL);
+
+    av_bprint_init_for_buffer(&bp, buf, buf_size);
+    av_channel_name_bprint(&bp, channel_id);
+
+    return bp.len;
+}
+
+void av_channel_description_bprint(AVBPrint *bp, enum AVChannel channel_id)
+{
+    av_bprint_clear(bp);
+
+    if ((unsigned)channel_id < FF_ARRAY_ELEMS(channel_names))
+        av_bprintf(bp, "%s", channel_names[channel_id].description);
+    else
+        av_bprintf(bp, "user %d", channel_id);
+}
+
+int av_channel_description(char *buf, size_t buf_size, enum AVChannel channel_id)
+{
+    AVBPrint bp;
+
+    if (!buf && buf_size)
+        return AVERROR(EINVAL);
+
+    av_bprint_init_for_buffer(&bp, buf, buf_size);
+    av_channel_description_bprint(&bp, channel_id);
+
+    return bp.len;
+}
+
+enum AVChannel av_channel_from_string(const char *str)
+{
+    int i;
+    char *endptr = (char *)str;
+    enum AVChannel id = AV_CHAN_NONE;
+    for (i = 0; i < FF_ARRAY_ELEMS(channel_names); i++) {
+        if (channel_names[i].name && !strcmp(str, channel_names[i].name))
+            return i;
+    }
+    if (!strncmp(str, "USR", 3)) {
+        const char *p = str + 3;
+        id = strtol(p, &endptr, 0);
+    }
+    if (id >= 0 && !*endptr)
+        return id;
+
+    return AV_CHAN_NONE;
+}
+
+struct channel_layout_name {
     const char *name;
-    int         nb_channels;
-    uint64_t     layout;
-} channel_layout_map[] = {
-    { "mono",        1,  AV_CH_LAYOUT_MONO },
-    { "stereo",      2,  AV_CH_LAYOUT_STEREO },
-    { "2.1",         3,  AV_CH_LAYOUT_2POINT1 },
-    { "3.0",         3,  AV_CH_LAYOUT_SURROUND },
-    { "3.0(back)",   3,  AV_CH_LAYOUT_2_1 },
-    { "4.0",         4,  AV_CH_LAYOUT_4POINT0 },
-    { "quad",        4,  AV_CH_LAYOUT_QUAD },
-    { "quad(side)",  4,  AV_CH_LAYOUT_2_2 },
-    { "3.1",         4,  AV_CH_LAYOUT_3POINT1 },
-    { "5.0",         5,  AV_CH_LAYOUT_5POINT0_BACK },
-    { "5.0(side)",   5,  AV_CH_LAYOUT_5POINT0 },
-    { "4.1",         5,  AV_CH_LAYOUT_4POINT1 },
-    { "5.1",         6,  AV_CH_LAYOUT_5POINT1_BACK },
-    { "5.1(side)",   6,  AV_CH_LAYOUT_5POINT1 },
-    { "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(back)",   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_BACK },
-    { "7.1(wide-side)",   8,  AV_CH_LAYOUT_7POINT1_WIDE },
-    { "octagonal",   8,  AV_CH_LAYOUT_OCTAGONAL },
-    { "hexadecagonal", 16, AV_CH_LAYOUT_HEXADECAGONAL },
-    { "downmix",     2,  AV_CH_LAYOUT_STEREO_DOWNMIX, },
-    { "22.2",          24, AV_CH_LAYOUT_22POINT2, },
+    AVChannelLayout layout;
+};
+
+static const struct channel_layout_name channel_layout_map[] = {
+    { "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                 },
+    { "4.0",            AV_CHANNEL_LAYOUT_4POINT0             },
+    { "quad",           AV_CHANNEL_LAYOUT_QUAD                },
+    { "quad(side)",     AV_CHANNEL_LAYOUT_2_2                 },
+    { "3.1",            AV_CHANNEL_LAYOUT_3POINT1             },
+    { "5.0",            AV_CHANNEL_LAYOUT_5POINT0_BACK        },
+    { "5.0(side)",      AV_CHANNEL_LAYOUT_5POINT0             },
+    { "4.1",            AV_CHANNEL_LAYOUT_4POINT1             },
+    { "5.1",            AV_CHANNEL_LAYOUT_5POINT1_BACK        },
+    { "5.1(side)",      AV_CHANNEL_LAYOUT_5POINT1             },
+    { "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_BACK   },
+    { "7.1(wide-side)", AV_CHANNEL_LAYOUT_7POINT1_WIDE        },
+    { "octagonal",      AV_CHANNEL_LAYOUT_OCTAGONAL           },
+    { "hexadecagonal",  AV_CHANNEL_LAYOUT_HEXADECAGONAL       },
+    { "downmix",        AV_CHANNEL_LAYOUT_STEREO_DOWNMIX,     },
+    { "22.2",           AV_CHANNEL_LAYOUT_22POINT2,           },
 };
 
+#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;
@@ -121,7 +191,7 @@  static uint64_t get_channel_layout_single(const char *name, int name_len)
     for (i = 0; i < FF_ARRAY_ELEMS(channel_layout_map); 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].name &&
@@ -189,8 +259,8 @@  void av_bprint_channel_layout(struct AVBPrint *bp,
         nb_channels = av_get_channel_layout_nb_channels(channel_layout);
 
     for (i = 0; i < FF_ARRAY_ELEMS(channel_layout_map); 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_bprintf(bp, "%s", channel_layout_map[i].name);
             return;
         }
@@ -231,8 +301,8 @@  int av_get_channel_layout_nb_channels(uint64_t channel_layout)
 int64_t av_get_default_channel_layout(int nb_channels) {
     int i;
     for (i = 0; i < FF_ARRAY_ELEMS(channel_layout_map); i++)
-        if (nb_channels == channel_layout_map[i].nb_channels)
-            return channel_layout_map[i].layout;
+        if (nb_channels == channel_layout_map[i].layout.nb_channels)
+            return channel_layout_map[i].layout.u.mask;
     return 0;
 }
 
@@ -287,7 +357,424 @@  int av_get_standard_channel_layout(unsigned index, uint64_t *layout,
 {
     if (index >= FF_ARRAY_ELEMS(channel_layout_map))
         return AVERROR_EOF;
-    if (layout) *layout = channel_layout_map[index].layout;
+    if (layout) *layout = channel_layout_map[index].layout.u.mask;
     if (name)   *name   = channel_layout_map[index].name;
     return 0;
 }
+FF_ENABLE_DEPRECATION_WARNINGS
+#endif
+
+int av_channel_layout_from_mask(AVChannelLayout *channel_layout,
+                                uint64_t mask)
+{
+    if (!mask)
+        return AVERROR(EINVAL);
+
+    channel_layout->order       = AV_CHANNEL_ORDER_NATIVE;
+    channel_layout->nb_channels = av_popcount64(mask);
+    channel_layout->u.mask      = mask;
+
+    return 0;
+}
+
+int av_channel_layout_from_string(AVChannelLayout *channel_layout,
+                                  const char *str)
+{
+    int i;
+    int channels = 0, native = 1;
+    enum AVChannel highest_channel = AV_CHAN_NONE;
+    const char *dup = str;
+    char *end;
+    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].name && !strcmp(chname, channel_names[i].name)) {
+                if (i < highest_channel)
+                    native = 0; // Not a native layout, use a custom one
+                highest_channel = i;
+                mask |= 1ULL << i;
+                break;
+            }
+        }
+
+        if (i >= FF_ARRAY_ELEMS(channel_names)) {
+            char *endptr = chname;
+            enum AVChannel id = AV_CHAN_NONE;
+
+            if (!strncmp(chname, "USR", 3)) {
+                const char *p = chname + 3;
+                id = strtol(p, &endptr, 0);
+            }
+            if (id < 0 || *endptr) {
+                native = 0; // Unknown channel name
+                channels = 0;
+                mask = 0;
+                av_free(chname);
+                break;
+            }
+            if (id > 63)
+                native = 0; // Not a native layout, use a custom one
+            else {
+                if (id < highest_channel)
+                    native = 0; // Not a native layout, use a custom one
+                highest_channel = id;
+                mask |= 1ULL << id;
+            }
+        }
+        channels++;
+        av_free(chname);
+    }
+    if (mask && native) {
+        av_channel_layout_from_mask(channel_layout, mask);
+        return 0;
+    }
+
+    /* custom layout of channel names */
+    if (channels && !native) {
+        int idx = 0;
+
+        channel_layout->u.map = av_calloc(channels, sizeof(*channel_layout->u.map));
+        if (!channel_layout->u.map)
+            return AVERROR(ENOMEM);
+
+        channel_layout->order = AV_CHANNEL_ORDER_CUSTOM;
+        channel_layout->nb_channels = channels;
+
+        dup = str;
+        while (*dup) {
+            char *chname = av_get_token(&dup, "+");
+            if (!chname) {
+                av_freep(&channel_layout->u.map);
+                return AVERROR(ENOMEM);
+            }
+            if (*dup)
+                dup++; // skip separator
+            for (i = 0; i < FF_ARRAY_ELEMS(channel_names); i++) {
+                if (channel_names[i].name && !strcmp(chname, channel_names[i].name)) {
+                    channel_layout->u.map[idx++].id = i;
+                    break;
+                }
+            }
+            if (i >= FF_ARRAY_ELEMS(channel_names)) {
+                const char *p = chname + 3;
+                channel_layout->u.map[idx++].id = strtol(p, NULL, 0);
+            }
+            av_free(chname);
+        }
+
+        return 0;
+    }
+
+    /* channel layout mask */
+    if (!strncmp(str, "0x", 2) && sscanf(str + 2, "%"SCNx64, &mask) == 1) {
+        av_channel_layout_from_mask(channel_layout, mask);
+        return 0;
+    }
+
+    errno = 0;
+    channels = strtol(str, &end, 10);
+
+    /* number of channels */
+    if (!errno && *end == 'c' && !*(end + 1) && channels >= 0) {
+        av_channel_layout_default(channel_layout, channels);
+        return 0;
+    }
+
+    /* number of unordered channels */
+    if (!errno && (!*end || (*end == 'C' && !*(end + 1)) || av_strnstr(str, "channels", strlen(str)))
+        && channels >= 0) {
+        channel_layout->order = AV_CHANNEL_ORDER_UNSPEC;
+        channel_layout->nb_channels = channels;
+        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)
+{
+    av_channel_layout_uninit(dst);
+    *dst = *src;
+    if (src->order == AV_CHANNEL_ORDER_CUSTOM) {
+        dst->u.map = av_malloc_array(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;
+}
+
+int av_channel_layout_describe_bprint(const AVChannelLayout *channel_layout,
+                                      AVBPrint *bp)
+{
+    int i;
+
+    av_bprint_clear(bp);
+
+    switch (channel_layout->order) {
+    case AV_CHANNEL_ORDER_NATIVE:
+        for (i = 0; i < FF_ARRAY_ELEMS(channel_layout_map); i++)
+            if (channel_layout->u.mask == channel_layout_map[i].layout.u.mask) {
+                av_bprintf(bp, "%s", channel_layout_map[i].name);
+                return 0;
+            }
+        // fall-through
+    case AV_CHANNEL_ORDER_CUSTOM:
+        for (i = 0; i < channel_layout->nb_channels; i++) {
+            const char *ch_name = NULL;
+            enum AVChannel ch = AV_CHAN_NONE;
+
+            if (channel_layout->order == AV_CHANNEL_ORDER_CUSTOM &&
+                channel_layout->u.map[i].name[0])
+                ch_name = channel_layout->u.map[i].name;
+            if (channel_layout->order == AV_CHANNEL_ORDER_NATIVE || !ch_name) {
+                ch = av_channel_layout_channel_from_index(channel_layout, i);
+                ch_name = get_channel_name(ch);
+            }
+
+            if (i)
+                av_bprintf(bp, "+");
+            if (ch_name)
+                av_bprintf(bp, "%s", ch_name);
+            else
+                av_bprintf(bp, "USR%d", ch);
+        }
+        if (channel_layout->nb_channels)
+            return 0;
+        // fall-through
+    case AV_CHANNEL_ORDER_UNSPEC:
+        av_bprintf(bp, "%d channels", channel_layout->nb_channels);
+        return 0;
+    default:
+        return AVERROR(EINVAL);
+    }
+}
+
+int av_channel_layout_describe(const AVChannelLayout *channel_layout,
+                               char *buf, size_t buf_size)
+{
+    AVBPrint bp;
+    int ret;
+
+    if (!buf && buf_size)
+        return AVERROR(EINVAL);
+
+    av_bprint_init_for_buffer(&bp, buf, buf_size);
+    ret = av_channel_layout_describe_bprint(channel_layout, &bp);
+    if (ret < 0)
+        return ret;
+
+    return bp.len;
+}
+
+enum AVChannel
+av_channel_layout_channel_from_index(const AVChannelLayout *channel_layout,
+                                     unsigned int idx)
+{
+    int i;
+
+    if (idx >= channel_layout->nb_channels)
+        return AV_CHAN_NONE;
+
+    switch (channel_layout->order) {
+    case AV_CHANNEL_ORDER_CUSTOM:
+        return channel_layout->u.map[idx].id;
+    case AV_CHANNEL_ORDER_NATIVE:
+        for (i = 0; i < 64; i++) {
+            if ((1ULL << i) & channel_layout->u.mask && !idx--)
+                return i;
+        }
+    default:
+        return AV_CHAN_NONE;
+    }
+}
+
+enum AVChannel
+av_channel_layout_channel_from_string(const AVChannelLayout *channel_layout,
+                                      const char *name)
+{
+    int channel, ret;
+
+    switch (channel_layout->order) {
+    case AV_CHANNEL_ORDER_CUSTOM:
+        for (int i = 0; i < channel_layout->nb_channels; i++) {
+            if (channel_layout->u.map[i].name[0] && !strcmp(name, channel_layout->u.map[i].name))
+                return channel_layout->u.map[i].id;
+        }
+        // fall-through
+    case AV_CHANNEL_ORDER_NATIVE:
+        channel = av_channel_from_string(name);
+        if (channel == AV_CHAN_NONE)
+            return channel;
+        ret = av_channel_layout_index_from_channel(channel_layout, channel);
+        if (ret < 0)
+            return AV_CHAN_NONE;
+        return channel;
+    }
+
+    return AV_CHAN_NONE;
+}
+
+int av_channel_layout_index_from_channel(const AVChannelLayout *channel_layout,
+                                         enum AVChannel channel)
+{
+    int i;
+
+    if (channel == AV_CHAN_NONE)
+        return AVERROR(EINVAL);
+
+    switch (channel_layout->order) {
+    case AV_CHANNEL_ORDER_CUSTOM:
+        for (i = 0; i < channel_layout->nb_channels; i++)
+            if (channel_layout->u.map[i].id == 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_index_from_string(const AVChannelLayout *channel_layout,
+                                        const char *name)
+{
+    enum AVChannel ch;
+
+    switch (channel_layout->order) {
+    case AV_CHANNEL_ORDER_CUSTOM:
+        for (int i = 0; i < channel_layout->nb_channels; i++) {
+            if (channel_layout->u.map[i].name[0] && !strcmp(name, channel_layout->u.map[i].name))
+                return i;
+        }
+        // fall-through
+    case AV_CHANNEL_ORDER_NATIVE:
+        ch = av_channel_from_string(name);
+        if (ch == AV_CHAN_NONE)
+            return AVERROR(EINVAL);
+        return av_channel_layout_index_from_channel(channel_layout, ch);
+    }
+
+    return AVERROR(EINVAL);
+}
+
+int av_channel_layout_check(const AVChannelLayout *channel_layout)
+{
+    if (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:
+        if (!channel_layout->u.map)
+            return 0;
+        for (int i = 0; i < channel_layout->nb_channels; i++) {
+            if (channel_layout->u.map[i].id == AV_CHAN_NONE)
+                return 0;
+        }
+        return 1;
+    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_channel_from_index(chl,  i) !=
+            av_channel_layout_channel_from_index(chl1, i))
+            return 1;
+    return 0;
+}
+
+void av_channel_layout_default(AVChannelLayout *ch_layout, int nb_channels)
+{
+    int i;
+    for (i = 0; i < FF_ARRAY_ELEMS(channel_layout_map); i++)
+        if (nb_channels == channel_layout_map[i].layout.nb_channels) {
+            *ch_layout = channel_layout_map[i].layout;
+            return;
+        }
+
+    ch_layout->order       = AV_CHANNEL_ORDER_UNSPEC;
+    ch_layout->nb_channels = nb_channels;
+}
+
+const AVChannelLayout *av_channel_layout_standard(void **opaque)
+{
+    uintptr_t i = (uintptr_t)*opaque;
+    const AVChannelLayout *ch_layout = NULL;
+
+    if (i < FF_ARRAY_ELEMS(channel_layout_map)) {
+        ch_layout = &channel_layout_map[i].layout;
+        *opaque = (void*)(i + 1);
+    }
+
+    return ch_layout;
+}
+
+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_index_from_channel(channel_layout, i) >= 0)
+            ret |= (1ULL << i);
+
+    return ret;
+}
diff --git a/libavutil/channel_layout.h b/libavutil/channel_layout.h
index d39ae1177a..6356a9a38a 100644
--- a/libavutil/channel_layout.h
+++ b/libavutil/channel_layout.h
@@ -23,6 +23,10 @@ 
 #define AVUTIL_CHANNEL_LAYOUT_H
 
 #include <stdint.h>
+#include <stdlib.h>
+
+#include "version.h"
+#include "attributes.h"
 
 /**
  * @file
@@ -34,6 +38,71 @@ 
  * @{
  */
 
+enum AVChannel {
+    ///< Invalid channel index
+    AV_CHAN_NONE = -1,
+    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,
+    AV_CHAN_TOP_SIDE_LEFT,
+    AV_CHAN_TOP_SIDE_RIGHT,
+    AV_CHAN_BOTTOM_FRONT_CENTER,
+    AV_CHAN_BOTTOM_FRONT_LEFT,
+    AV_CHAN_BOTTOM_FRONT_RIGHT,
+
+    /** Channel is empty can be safely skipped. */
+    AV_CHAN_UNUSED = 64,
+
+    /** Channel contains data, but its position is unknown. */
+    AV_CHAN_UNKWNOWN = 128,
+};
+
+enum AVChannelOrder {
+    /**
+     * Only the channel count is specified, without any further information
+     * about the channel order.
+     */
+    AV_CHANNEL_ORDER_UNSPEC,
+    /**
+     * 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 empty/skipped (AV_CHAN_SILENCE)
+     * channels at arbitrary positions.
+     */
+    AV_CHANNEL_ORDER_CUSTOM,
+};
+
+
 /**
  * @defgroup channel_masks Audio channel masks
  *
@@ -46,41 +115,46 @@ 
  *
  * @{
  */
-#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_TOP_SIDE_LEFT          0x0000001000000000ULL
-#define AV_CH_TOP_SIDE_RIGHT         0x0000002000000000ULL
-#define AV_CH_BOTTOM_FRONT_CENTER    0x0000004000000000ULL
-#define AV_CH_BOTTOM_FRONT_LEFT      0x0000008000000000ULL
-#define AV_CH_BOTTOM_FRONT_RIGHT     0x0000010000000000ULL
+#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      )
+#define AV_CH_TOP_SIDE_LEFT          (1ULL << AV_CHAN_TOP_SIDE_LEFT        )
+#define AV_CH_TOP_SIDE_RIGHT         (1ULL << AV_CHAN_TOP_SIDE_RIGHT       )
+#define AV_CH_BOTTOM_FRONT_CENTER    (1ULL << AV_CHAN_BOTTOM_FRONT_CENTER  )
+#define AV_CH_BOTTOM_FRONT_LEFT      (1ULL << AV_CHAN_BOTTOM_FRONT_LEFT    )
+#define AV_CH_BOTTOM_FRONT_RIGHT     (1ULL << AV_CHAN_BOTTOM_FRONT_RIGHT   )
 
+#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
 
 /**
  * @}
@@ -128,6 +202,167 @@  enum AVMatrixEncoding {
     AV_MATRIX_ENCODING_NB
 };
 
+/**
+ * @}
+ */
+
+/**
+ * An AVChannelCustom defines a single channel within a custom order layout
+ *
+ * Unlike most structures in FFmpeg, sizeof(AVChannelCustom) is a part of the
+ * public ABI.
+ *
+ * No new fields may be added to it without a major version bump.
+ */
+typedef struct AVChannelCustom {
+    enum AVChannel id;
+    char name[16];
+    void *opaque;
+} AVChannelCustom;
+
+/**
+ * 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
+ * or embedded in caller-defined structs.
+ *
+ * AVChannelLayout can be initialized as follows:
+ * - default initialization with {0}, followed by by setting all used fields
+ *   correctly;
+ * - by assigning one of the predefined AV_CHANNEL_LAYOUT_* initializers;
+ * - with a constructor function, such as av_channel_layout_default(),
+ *   av_channel_layout_from_mask() or av_channel_layout_from_string().
+ *
+ * The channel layout must be unitialized with av_channel_layout_uninit()
+ *
+ * Copying an AVChannelLayout via assigning is forbidden,
+ * av_channel_layout_copy() must be used. instead (and its return value should
+ * be checked)
+ *
+ * No new fields may be added to it without a major version bump, except for
+ * new elements of the union fitting in sizeof(uint64_t).
+ */
+typedef struct AVChannelLayout {
+    /**
+     * Channel order used in this layout.
+     * This is a mandatory field.
+     */
+    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 presence of the AVChannel with the
+         * corresponding value in map[i].id.
+         *
+         * I.e. when map[i].id is equal to AV_CHAN_FOO, then AV_CH_FOO is the
+         * i-th channel in the audio data.
+         *
+         * map[i].name may be filled with a 0-terminated string, in which case
+         * it will be used for the purpose of identifying the channel with the
+         * convenience functions below. Otherise it must be zeroed.
+         */
+        AVChannelCustom *map;
+    } u;
+
+    /**
+     * For some private data of the user.
+     */
+    void *opaque;
+} 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 }}
+#define AV_CHANNEL_LAYOUT_22POINT2 \
+    { .order = AV_CHANNEL_ORDER_NATIVE, .nb_channels = 24, .u = { .mask = AV_CH_LAYOUT_22POINT2 }}
+
+struct AVBPrint;
+
+#if FF_API_OLD_CHANNEL_LAYOUT
 /**
  * Return a channel layout id that matches name, or 0 if no match is found.
  *
@@ -144,7 +379,10 @@  enum AVMatrixEncoding {
  *   AV_CH_* macros).
  *
  * Example: "stereo+FC" = "2c+FC" = "2c+1c" = "0x7"
+ *
+ * @deprecated use av_channel_layout_from_string()
  */
+attribute_deprecated
 uint64_t av_get_channel_layout(const char *name);
 
 /**
@@ -158,7 +396,9 @@  uint64_t av_get_channel_layout(const char *name);
  * @param[out] nb_channels      number of channels
  *
  * @return 0 on success, AVERROR(EINVAL) if the parsing fails.
+ * @deprecated use av_channel_layout_from_string()
  */
+attribute_deprecated
 int av_get_extended_channel_layout(const char *name, uint64_t* channel_layout, int* nb_channels);
 
 /**
@@ -167,23 +407,31 @@  int av_get_extended_channel_layout(const char *name, uint64_t* channel_layout, i
  *
  * @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);
 
-struct AVBPrint;
 /**
  * Append a description of a channel layout to a bprint buffer.
+ * @deprecated use av_channel_layout_describe()
  */
+attribute_deprecated
 void av_bprint_channel_layout(struct AVBPrint *bp, 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
 int64_t av_get_default_channel_layout(int nb_channels);
 
 /**
@@ -194,20 +442,28 @@  int64_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_index_from_channel()
  */
+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_channel_from_index()
  */
+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);
 
 /**
@@ -215,7 +471,9 @@  const char *av_get_channel_name(uint64_t channel);
  *
  * @param channel  a channel layout with a single channel
  * @return  channel description on success, NULL on error
+ * @deprecated use av_channel_description()
  */
+attribute_deprecated
 const char *av_get_channel_description(uint64_t channel);
 
 /**
@@ -226,9 +484,229 @@  const char *av_get_channel_description(uint64_t channel);
  * @param[out] name    name of the layout
  * @return  0  if the layout exists,
  *          <0 if index is beyond the limits
+ * @deprecated use av_channel_layout_standard()
  */
+attribute_deprecated
 int av_get_standard_channel_layout(unsigned index, uint64_t *layout,
                                    const char **name);
+#endif
+
+/**
+ * Get a human readable string in an abbreviated form describing a given channel,
+ * or "?" if not found.
+ * This is the inverse function of @ref av_channel_from_string().
+ *
+ * @param buf pre-allocated buffer where to put the generated string
+ * @param buf_size size in bytes of the buffer.
+ * @return amount of bytes needed to hold the output string, or a negative AVERROR
+ *         on failure. If the returned value is bigger than buf_size, then the
+ *         string was truncated.
+ */
+int av_channel_name(char *buf, size_t buf_size, enum AVChannel channel);
+
+/**
+ * bprint variant of av_channel_name().
+ */
+void av_channel_name_bprint(struct AVBPrint *bp, enum AVChannel channel_id);
+
+/**
+ * Get a human readable string describing a given channel, or "?" if not found.
+ *
+ * @param buf pre-allocated buffer where to put the generated string
+ * @param buf_size size in bytes of the buffer.
+ * @return amount of bytes needed to hold the output string, or a negative AVERROR
+ *         on failure. If the returned value is bigger than buf_size, then the
+ *         string was truncated.
+ */
+int av_channel_description(char *buf, size_t buf_size, enum AVChannel channel);
+
+/**
+ * bprint variant of av_channel_description().
+ */
+void av_channel_description_bprint(struct AVBPrint *bp, enum AVChannel channel_id);
+
+/**
+ * This is the inverse function of @ref av_channel_name().
+ *
+ * @return the channel with the given name
+ *         AV_CHAN_NONE when name does not identify a known channel
+ */
+enum AVChannel av_channel_from_string(const char *name);
+
+/**
+ * Initialize a native channel layout from a bitmask indicating which channels
+ * are present.
+ *
+ * @param channel_layout the layout structure to be initialized
+ * @param mask bitmask describing the channel layout
+ *
+ * @return 0 on success
+ *         AVERROR(EINVAL) for invalid mask values
+ */
+int 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. "5c")
+ *  - the number of unordered channels (eg. "4", "4C", or "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.
+ *
+ * @param channel_layout the layout structure to be initialized
+ * @param nb_channels number of channels
+ */
+void av_channel_layout_default(AVChannelLayout *ch_layout, int nb_channels);
+
+/**
+ * Iterate over all standard channel layouts.
+ *
+ * @param opaque a pointer where libavutil will store the iteration state. Must
+ *               point to NULL to start the iteration.
+ *
+ * @return the standard channel layout or NULL when the iteration is
+ *         finished
+ */
+const AVChannelLayout *av_channel_layout_standard(void **opaque);
+
+/**
+ * Free any allocated data in the channel layout and reset the channel
+ * count to 0.
+ *
+ * @param channel_layout the layout structure to be uninitialized
+ */
+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.
+ *
+ * @note the destination channel_layout will be always uninitialized before copy.
+ *
+ * @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);
+
+/**
+ * Get a human-readable string describing the channel layout properties.
+ * The string will be in the same format that is accepted by
+ * @ref av_channel_layout_from_string().
+ *
+ * @param channel_layout channel layout to be described
+ * @param buf pre-allocated buffer where to put the generated string
+ * @param buf_size size in bytes of the buffer.
+ * @return amount of bytes needed to hold the output string, or a negative AVERROR
+ *         on failure. If the returned value is bigger than buf_size, then the
+ *         string was truncated.
+ */
+int av_channel_layout_describe(const AVChannelLayout *channel_layout,
+                               char *buf, size_t buf_size);
+
+/**
+ * bprint variant of av_channel_layout_describe().
+ *
+ * @return 0 on success, or a negative AVERROR value on failure.
+ */
+int av_channel_layout_describe_bprint(const AVChannelLayout *channel_layout,
+                                      struct AVBPrint *bp);
+
+/**
+ * Get the channel with the given index in a channel layout.
+ *
+ * @param channel_layout input channel layout
+ * @return channel with the index idx in channel_layout on success or
+ *         AV_CHAN_NONE on failure (if idx is not valid or the channel order is
+ *         unspecified)
+ */
+enum AVChannel
+av_channel_layout_channel_from_index(const AVChannelLayout *channel_layout, unsigned int idx);
+
+/**
+ * Get the index of a given channel in a channel layout. In case multiple
+ * channels are found, only the first match will be returned.
+ *
+ * @param channel_layout input 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_index_from_channel(const AVChannelLayout *channel_layout,
+                                         enum AVChannel channel);
+
+/**
+ * Get the index in a channel layout of a channel described by the given string.
+ * In case multiple channels are found, only the first match will be returned.
+ *
+ * This function accepts channel names in the same format as
+ * @ref av_channel_from_string().
+ *
+ * @param channel_layout input channel layout
+ * @return a channel index described by the given string, or a negative AVERROR
+ *         value.
+ */
+int av_channel_layout_index_from_string(const AVChannelLayout *channel_layout,
+                                        const char *name);
+
+/**
+ * Get a channel described by the given string.
+ *
+ * This function accepts channel names in the same format as
+ * @ref av_channel_from_string().
+ *
+ * @param channel_layout input channel layout
+ * @return a channel described by the given string, or a negative AVERROR value.
+ */
+int av_channel_layout_channel_from_string(const AVChannelLayout *channel_layout,
+                                          const char *name);
+
+/**
+ * Find out what channels from a given set are present in a channel layout,
+ * without regard for their positions.
+ *
+ * @param channel_layout input channel layout
+ * @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.
+ *
+ * @param channel_layout input channel layout
+ * @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.
+ *
+ * @param chl input channel layout
+ * @param chl1 input channel layout
+ * @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 953aac9d94..4da667b923 100644
--- a/libavutil/version.h
+++ b/libavutil/version.h
@@ -110,6 +110,7 @@ 
 #define FF_API_COLORSPACE_NAME          (LIBAVUTIL_VERSION_MAJOR < 58)
 #define FF_API_AV_MALLOCZ_ARRAY         (LIBAVUTIL_VERSION_MAJOR < 58)
 #define FF_API_FIFO_PEEK2               (LIBAVUTIL_VERSION_MAJOR < 58)
+#define FF_API_OLD_CHANNEL_LAYOUT       (LIBAVUTIL_VERSION_MAJOR < 58)
 
 /**
  * @}