diff mbox

[FFmpeg-devel,2/3] lavfi/loudnorm: add an internal libebur128 library

Message ID 1476646324-15429-2-git-send-email-cus@passwd.hu
State Superseded
Headers show

Commit Message

Marton Balint Oct. 16, 2016, 7:32 p.m. UTC
Also contains the following changes to the library:
- add ff_ prefix to functions
- remove cplusplus defines.
- add FF_ prefix to contants and some structs
- remove true peak calculation feature, since it uses its own resampler, and
  af_audnorm does not need it.
- remove version info and some fprintf(stderr) functions
- convert to use av_malloc
- always use histogram mode for LRA calculation, otherwise LRA data is slowly
  consuming memory making af_loudnorm unfit for 24/7 operation. It also uses a
  BSD style linked list implementation which is probably not available on all
  platforms. So let's just remove the classic mode which not uses histogram.
- add ff_thread_once for calculating static histogram tables
- convert some functions to void which cannot fail
- remove intrinsics and some unused headers
- add support for planar audio

Signed-off-by: Marton Balint <cus@passwd.hu>
---
 configure                 |   5 -
 libavfilter/Makefile      |   2 +-
 libavfilter/af_loudnorm.c |  60 ++--
 libavfilter/ebur128.c     | 850 ++++++++++++++++++++++++++++++++++++++++++++++
 libavfilter/ebur128.h     | 359 ++++++++++++++++++++
 5 files changed, 1240 insertions(+), 36 deletions(-)
 create mode 100644 libavfilter/ebur128.c
 create mode 100644 libavfilter/ebur128.h

Comments

wm4 Oct. 17, 2016, 3:09 p.m. UTC | #1
On Sun, 16 Oct 2016 21:32:03 +0200
Marton Balint <cus@passwd.hu> wrote:

> Also contains the following changes to the library:
> - add ff_ prefix to functions
> - remove cplusplus defines.
> - add FF_ prefix to contants and some structs
> - remove true peak calculation feature, since it uses its own resampler, and
>   af_audnorm does not need it.
> - remove version info and some fprintf(stderr) functions
> - convert to use av_malloc
> - always use histogram mode for LRA calculation, otherwise LRA data is slowly
>   consuming memory making af_loudnorm unfit for 24/7 operation. It also uses a
>   BSD style linked list implementation which is probably not available on all
>   platforms. So let's just remove the classic mode which not uses histogram.
> - add ff_thread_once for calculating static histogram tables
> - convert some functions to void which cannot fail
> - remove intrinsics and some unused headers
> - add support for planar audio
> 
> Signed-off-by: Marton Balint <cus@passwd.hu>
> ---

Does this copy parts of libebur128 to FFmpeg?

Why?
Moritz Barsnick Oct. 17, 2016, 3:20 p.m. UTC | #2
On Mon, Oct 17, 2016 at 17:09:15 +0200, wm4 wrote:
> Does this copy parts of libebur128 to FFmpeg?
> Why?

There was a long discussion regarding this patch:

http://lists.ffmpeg.org/pipermail/ffmpeg-devel/2016-April/192668.html

(in summary: "please don't require yet another small external library,
rather port it to ffmpeg and maintain it") leading to this one:

http://ffmpeg.org/pipermail/ffmpeg-devel/2016-June/195284.html

Moritz
Martin Vignali Oct. 18, 2016, 1:19 p.m. UTC | #3
Hello,

Maybe you can also update the doc

removing this line :
To enable compilation of this filter you need to configure FFmpeg with
--enable-libebur128

Martin
Marton Balint Oct. 23, 2016, 10 p.m. UTC | #4
On Mon, 17 Oct 2016, Moritz Barsnick wrote:

> On Mon, Oct 17, 2016 at 17:09:15 +0200, wm4 wrote:
>> Does this copy parts of libebur128 to FFmpeg?
>> Why?
>
> There was a long discussion regarding this patch:
>
> http://lists.ffmpeg.org/pipermail/ffmpeg-devel/2016-April/192668.html
>
> (in summary: "please don't require yet another small external library,
> rather port it to ffmpeg and maintain it") leading to this one:
>
> http://ffmpeg.org/pipermail/ffmpeg-devel/2016-June/195284.html
>

Yeah, basically that's it. I stripped quite a bit of unused code from the 
library, so it is really small now. If there are still people having 
strong opinons against this, let me know, so we can start a vote or 
something.

Thanks,
Marton
Kyle Swanson Nov. 3, 2016, 7:49 p.m. UTC | #5
Hi,

>On Sun, Oct 16, 2016 at 2:32 PM, Marton Balint <cus@passwd.hu> wrote:

I haven't been checking ffmpeg-devel too much lately and just saw
these patches today. I'm glad to see that an ffmpeg libebur128 port is
being taken seriously now, I had some trouble getting traction on a
few similar patches earlier this year. A new version of libebur128 was
released a few days ago, so we can probably improve this port even
more. The loudnorm filter continues to be a popular ffmpeg feature. I
still get messages about af_loudnorm from users all the time, so it'd
be nice to remove the external library dependancy and open this up to
everyone.

> Also contains the following changes to the library:
> - add ff_ prefix to functions
> - remove cplusplus defines.
> - add FF_ prefix to contants and some structs
> - remove true peak calculation feature, since it uses its own resampler, and
>   af_audnorm does not need it.
I'd argue that we should include the true peak calculation because
after this port we'll want to replace the code in f_ebur128 as well.
Libebur128 v1.2 drops the speex resampler and includes its own small
resampler (fast and passes all the EBUR128 tests.) Also there's a
patch I sent in June which uses libavresample APIs.
> - remove version info and some fprintf(stderr) functions
> - convert to use av_malloc
> - always use histogram mode for LRA calculation, otherwise LRA data is slowly
>   consuming memory making af_loudnorm unfit for 24/7 operation. It also uses a
>   BSD style linked list implementation which is probably not available on all
>   platforms. So let's just remove the classic mode which not uses histogram.
Probably a good idea! Seems awkward to include the BSD `queue.h` file,
but probably not the end of the world.
> - add ff_thread_once for calculating static histogram tables
> - convert some functions to void which cannot fail
> - remove intrinsics and some unused headers
> - add support for planar audio

Any other thoughts? I'll take a closer look at the patches tonight.


Kyle
Hendrik Leppkes Nov. 3, 2016, 8:20 p.m. UTC | #6
On Mon, Oct 17, 2016 at 5:20 PM, Moritz Barsnick <barsnick@gmx.net> wrote:
> On Mon, Oct 17, 2016 at 17:09:15 +0200, wm4 wrote:
>> Does this copy parts of libebur128 to FFmpeg?
>> Why?
>
> There was a long discussion regarding this patch:
>
> http://lists.ffmpeg.org/pipermail/ffmpeg-devel/2016-April/192668.html
>
> (in summary: "please don't require yet another small external library,
> rather port it to ffmpeg and maintain it") leading to this one:
>

The generic idea was not to just copy/paste an external library into
internal code, but extend the ebur128 code we already have - at least
that way we get code written by one of our maintainers, code he knows
and can properly maintain.
If you just copy the implementation of a library, you might as well
just use that library - thats what it exists for. Why do we want to
increase the maintenance burden of our project when other people (ie.
the authors of libebur128) are already doing it as well?

- Hendrik
Marton Balint Nov. 4, 2016, 3:29 a.m. UTC | #7
On Thu, 3 Nov 2016, Kyle Swanson wrote:

> Hi,
>
>> On Sun, Oct 16, 2016 at 2:32 PM, Marton Balint <cus@passwd.hu> wrote:
>
> I haven't been checking ffmpeg-devel too much lately and just saw
> these patches today. I'm glad to see that an ffmpeg libebur128 port is
> being taken seriously now, I had some trouble getting traction on a
> few similar patches earlier this year. A new version of libebur128 was
> released a few days ago, so we can probably improve this port even
> more. The loudnorm filter continues to be a popular ffmpeg feature. I
> still get messages about af_loudnorm from users all the time, so it'd
> be nice to remove the external library dependancy and open this up to
> everyone.
>> Also contains the following changes to the library:
>> - add ff_ prefix to functions
>> - remove cplusplus defines.
>> - add FF_ prefix to contants and some structs
>> - remove true peak calculation feature, since it uses its own resampler, and
>>   af_audnorm does not need it.
> I'd argue that we should include the true peak calculation because
> after this port we'll want to replace the code in f_ebur128 as well.
> Libebur128 v1.2 drops the speex resampler and includes its own small
> resampler (fast and passes all the EBUR128 tests.) Also there's a
> patch I sent in June which uses libavresample APIs.

Yeah, I started the porting work on a version which already had it's own 
resampler, however I decided to strip down as many things as possible 
(which we not use), to make life easier. Also current version of the 
library would force you to use loudness measurement even if you only want 
true peak, which is not good performance-wise, so it needs some additional 
work to be implemented properly. Anyway, it can be added later to the 
codebase (if something needs it).

Regards,
Marton
Marton Balint Nov. 4, 2016, 4:19 a.m. UTC | #8
On Thu, 3 Nov 2016, Hendrik Leppkes wrote:

> On Mon, Oct 17, 2016 at 5:20 PM, Moritz Barsnick <barsnick@gmx.net> wrote:
>> On Mon, Oct 17, 2016 at 17:09:15 +0200, wm4 wrote:
>>> Does this copy parts of libebur128 to FFmpeg?
>>> Why?
>>
>> There was a long discussion regarding this patch:
>>
>> http://lists.ffmpeg.org/pipermail/ffmpeg-devel/2016-April/192668.html
>>
>> (in summary: "please don't require yet another small external library,
>> rather port it to ffmpeg and maintain it") leading to this one:
>>
>
> The generic idea was not to just copy/paste an external library into
> internal code, but extend the ebur128 code we already have - at least
> that way we get code written by one of our maintainers, code he knows
> and can properly maintain.

I copied the external library because we needed an API. The way the 
internals work, I used the library code because it was simply easier, than 
factoring out f_ebur128 stuff, also there are some features which 
f_ebur128.c does not have (variable sample rate support), and there 
was the licensing issue GPL v.s. LGPL.

> If you just copy the implementation of a library, you might as well
> just use that library - thats what it exists for. Why do we want to
> increase the maintenance burden of our project when other people (ie.
> the authors of libebur128) are already doing it as well?

In general I agree with people who think that for small code, it is better 
to integrate it into our codebase, because
- it can benefit from features we already have (e.g. resampling)
- makes it easier for developers to work on features based on this
- code gets more review than code in a small 3rd party library
- code and/or improvements have stronger requirements performance-wise
- code is better audited (Coverity, etc)

Yes, some additional maintenance burden is the price we pay for this, 
which is IMHO in this case is acceptable.

Regards,
Marton
Marton Balint Nov. 8, 2016, midnight UTC | #9
On Fri, 4 Nov 2016, Marton Balint wrote:

>
> On Thu, 3 Nov 2016, Hendrik Leppkes wrote:
>
>> On Mon, Oct 17, 2016 at 5:20 PM, Moritz Barsnick <barsnick@gmx.net> wrote:
>>> On Mon, Oct 17, 2016 at 17:09:15 +0200, wm4 wrote:
>>>> Does this copy parts of libebur128 to FFmpeg?
>>>> Why?
>>>
>>> There was a long discussion regarding this patch:
>>>
>>> http://lists.ffmpeg.org/pipermail/ffmpeg-devel/2016-April/192668.html
>>>
>>> (in summary: "please don't require yet another small external library,
>>> rather port it to ffmpeg and maintain it") leading to this one:
>>>
>>
>> The generic idea was not to just copy/paste an external library into
>> internal code, but extend the ebur128 code we already have - at least
>> that way we get code written by one of our maintainers, code he knows
>> and can properly maintain.
>
> I copied the external library because we needed an API. The way the 
> internals work, I used the library code because it was simply easier, than 
> factoring out f_ebur128 stuff, also there are some features which 
> f_ebur128.c does not have (variable sample rate support), and there 
> was the licensing issue GPL v.s. LGPL.
>
>> If you just copy the implementation of a library, you might as well
>> just use that library - thats what it exists for. Why do we want to
>> increase the maintenance burden of our project when other people (ie.
>> the authors of libebur128) are already doing it as well?
>
> In general I agree with people who think that for small code, it is better 
> to integrate it into our codebase, because
> - it can benefit from features we already have (e.g. resampling)
> - makes it easier for developers to work on features based on this
> - code gets more review than code in a small 3rd party library
> - code and/or improvements have stronger requirements performance-wise
> - code is better audited (Coverity, etc)
>
> Yes, some additional maintenance burden is the price we pay for this, 
> which is IMHO in this case is acceptable.
>

Is it fine to apply, or we should put this to a vote?

Thanks,
Marton
Kyle Swanson Nov. 8, 2016, 3:39 p.m. UTC | #10
On Mon, Nov 7, 2016 at 6:00 PM, Marton Balint <cus@passwd.hu> wrote:
>
> On Fri, 4 Nov 2016, Marton Balint wrote:
>
>>
>> On Thu, 3 Nov 2016, Hendrik Leppkes wrote:
>>
>>> On Mon, Oct 17, 2016 at 5:20 PM, Moritz Barsnick <barsnick@gmx.net>
>>> wrote:
>>>>
>>>> On Mon, Oct 17, 2016 at 17:09:15 +0200, wm4 wrote:
>>>>>
>>>>> Does this copy parts of libebur128 to FFmpeg?
>>>>> Why?
>>>>
>>>>
>>>> There was a long discussion regarding this patch:
>>>>
>>>> http://lists.ffmpeg.org/pipermail/ffmpeg-devel/2016-April/192668.html
>>>>
>>>> (in summary: "please don't require yet another small external library,
>>>> rather port it to ffmpeg and maintain it") leading to this one:
>>>>
>>>
>>> The generic idea was not to just copy/paste an external library into
>>> internal code, but extend the ebur128 code we already have - at least
>>> that way we get code written by one of our maintainers, code he knows
>>> and can properly maintain.
>>
>>
>> I copied the external library because we needed an API. The way the
>> internals work, I used the library code because it was simply easier, than
>> factoring out f_ebur128 stuff, also there are some features which
>> f_ebur128.c does not have (variable sample rate support), and there was the
>> licensing issue GPL v.s. LGPL.
>>
>>> If you just copy the implementation of a library, you might as well
>>> just use that library - thats what it exists for. Why do we want to
>>> increase the maintenance burden of our project when other people (ie.
>>> the authors of libebur128) are already doing it as well?
>>
>>
>> In general I agree with people who think that for small code, it is better
>> to integrate it into our codebase, because
>> - it can benefit from features we already have (e.g. resampling)
>> - makes it easier for developers to work on features based on this
>> - code gets more review than code in a small 3rd party library
>> - code and/or improvements have stronger requirements performance-wise
>> - code is better audited (Coverity, etc)
>>
>> Yes, some additional maintenance burden is the price we pay for this,
>> which is IMHO in this case is acceptable.
>>
>
> Is it fine to apply, or we should put this to a vote?

Give me another day to review the patch. Meant look at this last weekend.

>
> Thanks,
>
> Marton
> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel@ffmpeg.org
> http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
Kyle Swanson Nov. 10, 2016, 4:32 p.m. UTC | #11
On Tue, Nov 8, 2016 at 9:39 AM, Kyle Swanson <k@ylo.ph> wrote:
> On Mon, Nov 7, 2016 at 6:00 PM, Marton Balint <cus@passwd.hu> wrote:
>>
>> On Fri, 4 Nov 2016, Marton Balint wrote:
>>
>>>
>>> On Thu, 3 Nov 2016, Hendrik Leppkes wrote:
>>>
>>>> On Mon, Oct 17, 2016 at 5:20 PM, Moritz Barsnick <barsnick@gmx.net>
>>>> wrote:
>>>>>
>>>>> On Mon, Oct 17, 2016 at 17:09:15 +0200, wm4 wrote:
>>>>>>
>>>>>> Does this copy parts of libebur128 to FFmpeg?
>>>>>> Why?
>>>>>
>>>>>
>>>>> There was a long discussion regarding this patch:
>>>>>
>>>>> http://lists.ffmpeg.org/pipermail/ffmpeg-devel/2016-April/192668.html
>>>>>
>>>>> (in summary: "please don't require yet another small external library,
>>>>> rather port it to ffmpeg and maintain it") leading to this one:
>>>>>
>>>>
>>>> The generic idea was not to just copy/paste an external library into
>>>> internal code, but extend the ebur128 code we already have - at least
>>>> that way we get code written by one of our maintainers, code he knows
>>>> and can properly maintain.
>>>
>>>
>>> I copied the external library because we needed an API. The way the
>>> internals work, I used the library code because it was simply easier, than
>>> factoring out f_ebur128 stuff, also there are some features which
>>> f_ebur128.c does not have (variable sample rate support), and there was the
>>> licensing issue GPL v.s. LGPL.
>>>
>>>> If you just copy the implementation of a library, you might as well
>>>> just use that library - thats what it exists for. Why do we want to
>>>> increase the maintenance burden of our project when other people (ie.
>>>> the authors of libebur128) are already doing it as well?
>>>
>>>
>>> In general I agree with people who think that for small code, it is better
>>> to integrate it into our codebase, because
>>> - it can benefit from features we already have (e.g. resampling)
>>> - makes it easier for developers to work on features based on this
>>> - code gets more review than code in a small 3rd party library
>>> - code and/or improvements have stronger requirements performance-wise
>>> - code is better audited (Coverity, etc)
>>>
>>> Yes, some additional maintenance burden is the price we pay for this,
>>> which is IMHO in this case is acceptable.
>>>
>>
>> Is it fine to apply, or we should put this to a vote?
>
> Give me another day to review the patch. Meant look at this last weekend.

These patches look good to me. If we're going to do this, we really
need to keep the true peak mode in the libebur128 port. This is a huge
part of the R128 spec, and it's important that it stays in. Of course
redundant code is bad, so we'll need to update f_ebur128 as well
(which has a true peak requirement.) I already have a patch for
f_ebur128. Marton, it might be easier for you to update the patch to
include true peak mode but I could do it as well. It'd be interesting
to benchmark the libebur128 FIR resampler vs. swresample.

Also, this port is only like ~670 lines of C plus a header. It makes
sense to port it to FFmpeg instead of linking it. Also, on my systems
(osx, linux) af_loudnorm is ~5x faster then it was when libebur128 was
dynamically linked.

Thanks,
Kyle

>
>>
>> Thanks,
>>
>> Marton
>> _______________________________________________
>> ffmpeg-devel mailing list
>> ffmpeg-devel@ffmpeg.org
>> http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
Hendrik Leppkes Nov. 10, 2016, 4:49 p.m. UTC | #12
On Thu, Nov 10, 2016 at 5:32 PM, Kyle Swanson <k@ylo.ph> wrote:
> On Tue, Nov 8, 2016 at 9:39 AM, Kyle Swanson <k@ylo.ph> wrote:
>> On Mon, Nov 7, 2016 at 6:00 PM, Marton Balint <cus@passwd.hu> wrote:
>>>
>>> On Fri, 4 Nov 2016, Marton Balint wrote:
>>>
>>>>
>>>> On Thu, 3 Nov 2016, Hendrik Leppkes wrote:
>>>>
>>>>> On Mon, Oct 17, 2016 at 5:20 PM, Moritz Barsnick <barsnick@gmx.net>
>>>>> wrote:
>>>>>>
>>>>>> On Mon, Oct 17, 2016 at 17:09:15 +0200, wm4 wrote:
>>>>>>>
>>>>>>> Does this copy parts of libebur128 to FFmpeg?
>>>>>>> Why?
>>>>>>
>>>>>>
>>>>>> There was a long discussion regarding this patch:
>>>>>>
>>>>>> http://lists.ffmpeg.org/pipermail/ffmpeg-devel/2016-April/192668.html
>>>>>>
>>>>>> (in summary: "please don't require yet another small external library,
>>>>>> rather port it to ffmpeg and maintain it") leading to this one:
>>>>>>
>>>>>
>>>>> The generic idea was not to just copy/paste an external library into
>>>>> internal code, but extend the ebur128 code we already have - at least
>>>>> that way we get code written by one of our maintainers, code he knows
>>>>> and can properly maintain.
>>>>
>>>>
>>>> I copied the external library because we needed an API. The way the
>>>> internals work, I used the library code because it was simply easier, than
>>>> factoring out f_ebur128 stuff, also there are some features which
>>>> f_ebur128.c does not have (variable sample rate support), and there was the
>>>> licensing issue GPL v.s. LGPL.
>>>>
>>>>> If you just copy the implementation of a library, you might as well
>>>>> just use that library - thats what it exists for. Why do we want to
>>>>> increase the maintenance burden of our project when other people (ie.
>>>>> the authors of libebur128) are already doing it as well?
>>>>
>>>>
>>>> In general I agree with people who think that for small code, it is better
>>>> to integrate it into our codebase, because
>>>> - it can benefit from features we already have (e.g. resampling)
>>>> - makes it easier for developers to work on features based on this
>>>> - code gets more review than code in a small 3rd party library
>>>> - code and/or improvements have stronger requirements performance-wise
>>>> - code is better audited (Coverity, etc)
>>>>
>>>> Yes, some additional maintenance burden is the price we pay for this,
>>>> which is IMHO in this case is acceptable.
>>>>
>>>
>>> Is it fine to apply, or we should put this to a vote?
>>
>> Give me another day to review the patch. Meant look at this last weekend.
>
> These patches look good to me. If we're going to do this, we really
> need to keep the true peak mode in the libebur128 port. This is a huge
> part of the R128 spec, and it's important that it stays in. Of course
> redundant code is bad, so we'll need to update f_ebur128 as well
> (which has a true peak requirement.) I already have a patch for
> f_ebur128. Marton, it might be easier for you to update the patch to
> include true peak mode but I could do it as well. It'd be interesting
> to benchmark the libebur128 FIR resampler vs. swresample.

If this gets re-added, please make it use our resampler unless there
are good technical reasons against that (algorithm wise).
Keeping an extra small resampler just for some measurements seems like
something that can be surely avoided.

- Hendrik
Paul B Mahol Nov. 10, 2016, 6:11 p.m. UTC | #13
On 11/10/16, Hendrik Leppkes <h.leppkes@gmail.com> wrote:
> On Thu, Nov 10, 2016 at 5:32 PM, Kyle Swanson <k@ylo.ph> wrote:
>> On Tue, Nov 8, 2016 at 9:39 AM, Kyle Swanson <k@ylo.ph> wrote:
>>> On Mon, Nov 7, 2016 at 6:00 PM, Marton Balint <cus@passwd.hu> wrote:
>>>>
>>>> On Fri, 4 Nov 2016, Marton Balint wrote:
>>>>
>>>>>
>>>>> On Thu, 3 Nov 2016, Hendrik Leppkes wrote:
>>>>>
>>>>>> On Mon, Oct 17, 2016 at 5:20 PM, Moritz Barsnick <barsnick@gmx.net>
>>>>>> wrote:
>>>>>>>
>>>>>>> On Mon, Oct 17, 2016 at 17:09:15 +0200, wm4 wrote:
>>>>>>>>
>>>>>>>> Does this copy parts of libebur128 to FFmpeg?
>>>>>>>> Why?
>>>>>>>
>>>>>>>
>>>>>>> There was a long discussion regarding this patch:
>>>>>>>
>>>>>>> http://lists.ffmpeg.org/pipermail/ffmpeg-devel/2016-April/192668.html
>>>>>>>
>>>>>>> (in summary: "please don't require yet another small external
>>>>>>> library,
>>>>>>> rather port it to ffmpeg and maintain it") leading to this one:
>>>>>>>
>>>>>>
>>>>>> The generic idea was not to just copy/paste an external library into
>>>>>> internal code, but extend the ebur128 code we already have - at least
>>>>>> that way we get code written by one of our maintainers, code he knows
>>>>>> and can properly maintain.
>>>>>
>>>>>
>>>>> I copied the external library because we needed an API. The way the
>>>>> internals work, I used the library code because it was simply easier,
>>>>> than
>>>>> factoring out f_ebur128 stuff, also there are some features which
>>>>> f_ebur128.c does not have (variable sample rate support), and there was
>>>>> the
>>>>> licensing issue GPL v.s. LGPL.
>>>>>
>>>>>> If you just copy the implementation of a library, you might as well
>>>>>> just use that library - thats what it exists for. Why do we want to
>>>>>> increase the maintenance burden of our project when other people (ie.
>>>>>> the authors of libebur128) are already doing it as well?
>>>>>
>>>>>
>>>>> In general I agree with people who think that for small code, it is
>>>>> better
>>>>> to integrate it into our codebase, because
>>>>> - it can benefit from features we already have (e.g. resampling)
>>>>> - makes it easier for developers to work on features based on this
>>>>> - code gets more review than code in a small 3rd party library
>>>>> - code and/or improvements have stronger requirements performance-wise
>>>>> - code is better audited (Coverity, etc)
>>>>>
>>>>> Yes, some additional maintenance burden is the price we pay for this,
>>>>> which is IMHO in this case is acceptable.
>>>>>
>>>>
>>>> Is it fine to apply, or we should put this to a vote?
>>>
>>> Give me another day to review the patch. Meant look at this last weekend.
>>
>> These patches look good to me. If we're going to do this, we really
>> need to keep the true peak mode in the libebur128 port. This is a huge
>> part of the R128 spec, and it's important that it stays in. Of course
>> redundant code is bad, so we'll need to update f_ebur128 as well
>> (which has a true peak requirement.) I already have a patch for
>> f_ebur128. Marton, it might be easier for you to update the patch to
>> include true peak mode but I could do it as well. It'd be interesting
>> to benchmark the libebur128 FIR resampler vs. swresample.
>
> If this gets re-added, please make it use our resampler unless there
> are good technical reasons against that (algorithm wise).
> Keeping an extra small resampler just for some measurements seems like
> something that can be surely avoided.

Yes, I also agree. Is such small resampler really needed?
Marton Balint Nov. 10, 2016, 10:16 p.m. UTC | #14
On Thu, 10 Nov 2016, Kyle Swanson wrote:

> On Tue, Nov 8, 2016 at 9:39 AM, Kyle Swanson <k@ylo.ph> wrote:
>
> These patches look good to me. If we're going to do this, we really
> need to keep the true peak mode in the libebur128 port. This is a huge
> part of the R128 spec, and it's important that it stays in. Of course
> redundant code is bad, so we'll need to update f_ebur128 as well
> (which has a true peak requirement.) I already have a patch for
> f_ebur128. Marton, it might be easier for you to update the patch to
> include true peak mode but I could do it as well. It'd be interesting
> to benchmark the libebur128 FIR resampler vs. swresample.

I'd rather push the patch as it is, then if you are interested, you can 
start working on true peak. OK?

Since true peak calculation is so entirely different from loudness 
calculation, you might also create a separate API/Context for it. 
Just beacuse both loudness measurement and true peak is referenced in EBU 
R128 recommendation, we don't necessarily have to use the same API for 
them. For example, for true peak measurement, you might want to specify 
the oversampling factor, but not the channel layout...

Regards,
Marton
Kyle Swanson Nov. 11, 2016, 2:51 a.m. UTC | #15
On Thu, Nov 10, 2016 at 4:16 PM, Marton Balint <cus@passwd.hu> wrote:
>
> On Thu, 10 Nov 2016, Kyle Swanson wrote:
>
>> On Tue, Nov 8, 2016 at 9:39 AM, Kyle Swanson <k@ylo.ph> wrote:
>>
>> These patches look good to me. If we're going to do this, we really
>> need to keep the true peak mode in the libebur128 port. This is a huge
>> part of the R128 spec, and it's important that it stays in. Of course
>> redundant code is bad, so we'll need to update f_ebur128 as well
>> (which has a true peak requirement.) I already have a patch for
>> f_ebur128. Marton, it might be easier for you to update the patch to
>> include true peak mode but I could do it as well. It'd be interesting
>> to benchmark the libebur128 FIR resampler vs. swresample.
>
>
> I'd rather push the patch as it is, then if you are interested, you can
> start working on true peak. OK?

Cool, patch is attached. Resampling here is done via libswresample.

>
> Since true peak calculation is so entirely different from loudness
> calculation, you might also create a separate API/Context for it. Just
> beacuse both loudness measurement and true peak is referenced in EBU R128
> recommendation, we don't necessarily have to use the same API for them. For
> example, for true peak measurement, you might want to specify the
> oversampling factor, but not the channel layout...
>
> Regards,
>
> Marton
> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel@ffmpeg.org
> http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
Marton Balint Nov. 12, 2016, 12:12 a.m. UTC | #16
On Thu, 10 Nov 2016, Kyle Swanson wrote:

> On Thu, Nov 10, 2016 at 4:16 PM, Marton Balint <cus@passwd.hu> wrote:
>>
>> On Thu, 10 Nov 2016, Kyle Swanson wrote:
>>
>>> On Tue, Nov 8, 2016 at 9:39 AM, Kyle Swanson <k@ylo.ph> wrote:
>>>
>>> These patches look good to me. If we're going to do this, we really
>>> need to keep the true peak mode in the libebur128 port. This is a huge
>>> part of the R128 spec, and it's important that it stays in. Of course
>>> redundant code is bad, so we'll need to update f_ebur128 as well
>>> (which has a true peak requirement.) I already have a patch for
>>> f_ebur128. Marton, it might be easier for you to update the patch to
>>> include true peak mode but I could do it as well. It'd be interesting
>>> to benchmark the libebur128 FIR resampler vs. swresample.
>>
>>
>> I'd rather push the patch as it is, then if you are interested, you can
>> start working on true peak. OK?
>

Ok, I applied it.

> Cool, patch is attached. Resampling here is done via libswresample.
>

Yeah, I think I mentioned earlier that the library has a problem with true 
peak measurement, notably it cannot measure true peak without any loudness 
measurement, which is a shame performance-wise, since the user might neeed 
true peak only. Also, I am not sure that everybody who wants to 
measure true peak will want no oversampling at an above 192KHz.

Have you considered the separete context idea? If we want our API to be 
similar to the external lib, we still can use the separete true peak 
context inside the EBUR128 context if the user wants true peak.

>>
>> Since true peak calculation is so entirely different from loudness
>> calculation, you might also create a separate API/Context for it. Just
>> beacuse both loudness measurement and true peak is referenced in EBU R128
>> recommendation, we don't necessarily have to use the same API for them. For
>> example, for true peak measurement, you might want to specify the
>> oversampling factor, but not the channel layout...

Regards,
Marton
Kyle Swanson Nov. 12, 2016, 4:55 a.m. UTC | #17
On Fri, Nov 11, 2016 at 6:12 PM, Marton Balint <cus@passwd.hu> wrote:
>
> On Thu, 10 Nov 2016, Kyle Swanson wrote:
>
>> On Thu, Nov 10, 2016 at 4:16 PM, Marton Balint <cus@passwd.hu> wrote:
>>>
>>>
>>> On Thu, 10 Nov 2016, Kyle Swanson wrote:
>>>
>>>> On Tue, Nov 8, 2016 at 9:39 AM, Kyle Swanson <k@ylo.ph> wrote:
>>>>
>>>> These patches look good to me. If we're going to do this, we really
>>>> need to keep the true peak mode in the libebur128 port. This is a huge
>>>> part of the R128 spec, and it's important that it stays in. Of course
>>>> redundant code is bad, so we'll need to update f_ebur128 as well
>>>> (which has a true peak requirement.) I already have a patch for
>>>> f_ebur128. Marton, it might be easier for you to update the patch to
>>>> include true peak mode but I could do it as well. It'd be interesting
>>>> to benchmark the libebur128 FIR resampler vs. swresample.
>>>
>>>
>>>
>>> I'd rather push the patch as it is, then if you are interested, you can
>>> start working on true peak. OK?
>>
>>

Hi,

> Ok, I applied it.

Awesome. Thanks for your work on this!

>
>> Cool, patch is attached. Resampling here is done via libswresample.
>>
>
> Yeah, I think I mentioned earlier that the library has a problem with true
> peak measurement, notably it cannot measure true peak without any loudness
> measurement, which is a shame performance-wise, since the user might neeed
> true peak only.

I guess you're technically right about this, but if someone _only_
wanted true peak stats, they wouldn't/shouldn't be using the ebur128
API/filter. `-af aresample=192k,astats` already does the trick. The
EBU R128 spec says this: `A minimum feature set is required for all
EBU Mode loudness meters: an EBU Mode compliant meter shall be able to
measure and display the three main measures ‘Programme Loudness’,
‘Loudness Range’ and ‘Maximum True Peak Level’.` Basically, we need to
include true peak in our EBU R128 implementation, and it's clean and
convenient to leave it in as part of the API, just like libebur128
already has it.

https://tech.ebu.ch/docs/tech/tech3341.pdf
https://www.itu.int/dms_pubrec/itu-r/rec/bs/R-REC-BS.1770-4-201510-I!!PDF-E.pdf

> Also, I am not sure that everybody who wants to measure true
> peak will want no oversampling at an above 192KHz.

For EBU R128 / BS.1770, this is all the spec requires. There are
already many other ways to measure true peak if you're after another
flavor.

> Have you considered the separete context idea? If we want our API to be
> similar to the external lib, we still can use the separete true peak context
> inside the EBUR128 context if the user wants true peak.

I totally get what you're saying, but it seems to unnecessary to write
this little abstraction on top of libswresample (I also don't know
where else it'd be useful.) libswresample is already the separate
context. :)

Take a look at my patch, it should be good. I'll also be sending a
patch (in a new thread) to update f_ebur128 for this new API soon.

Thanks!
Kyle

>
>>>
>>> Since true peak calculation is so entirely different from loudness
>>> calculation, you might also create a separate API/Context for it. Just
>>> beacuse both loudness measurement and true peak is referenced in EBU R128
>>> recommendation, we don't necessarily have to use the same API for them.
>>> For
>>> example, for true peak measurement, you might want to specify the
>>> oversampling factor, but not the channel layout...
>
>
> Regards,
> Marton
> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel@ffmpeg.org
> http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
diff mbox

Patch

diff --git a/configure b/configure
index 96f575f..24d1906 100755
--- a/configure
+++ b/configure
@@ -223,8 +223,6 @@  External library support:
   --enable-libcdio         enable audio CD grabbing with libcdio [no]
   --enable-libdc1394       enable IIDC-1394 grabbing using libdc1394
                            and libraw1394 [no]
-  --enable-libebur128      enable libebur128 for EBU R128 measurement,
-                           needed for loudnorm filter [no]
   --enable-libfdk-aac      enable AAC de/encoding via libfdk-aac [no]
   --enable-libflite        enable flite (voice synthesis) support via libflite [no]
   --enable-libfontconfig   enable libfontconfig, useful for drawtext filter [no]
@@ -1486,7 +1484,6 @@  EXTERNAL_LIBRARY_LIST="
     libcdio
     libcelt
     libdc1394
-    libebur128
     libfdk_aac
     libflite
     libfontconfig
@@ -3045,7 +3042,6 @@  hqdn3d_filter_deps="gpl"
 interlace_filter_deps="gpl"
 kerndeint_filter_deps="gpl"
 ladspa_filter_deps="ladspa dlopen"
-loudnorm_filter_deps="libebur128"
 mcdeint_filter_deps="avcodec gpl"
 movie_filter_deps="avcodec avformat"
 mpdecimate_filter_deps="gpl"
@@ -5684,7 +5680,6 @@  enabled libcelt           && require libcelt celt/celt.h celt_decode -lcelt0 &&
                              { check_lib celt/celt.h celt_decoder_create_custom -lcelt0 ||
                                die "ERROR: libcelt must be installed and version must be >= 0.11.0."; }
 enabled libcaca           && require_pkg_config caca caca.h caca_create_canvas
-enabled libebur128        && require ebur128 ebur128.h ebur128_relative_threshold -lebur128
 enabled libfdk_aac        && { use_pkg_config fdk-aac "fdk-aac/aacenc_lib.h" aacEncOpen ||
                                { require libfdk_aac fdk-aac/aacenc_lib.h aacEncOpen -lfdk-aac &&
                                  warn "using libfdk without pkg-config"; } }
diff --git a/libavfilter/Makefile b/libavfilter/Makefile
index 7ed4696..cdddb1b 100644
--- a/libavfilter/Makefile
+++ b/libavfilter/Makefile
@@ -93,7 +93,7 @@  OBJS-$(CONFIG_HDCD_FILTER)                   += af_hdcd.o
 OBJS-$(CONFIG_HIGHPASS_FILTER)               += af_biquads.o
 OBJS-$(CONFIG_JOIN_FILTER)                   += af_join.o
 OBJS-$(CONFIG_LADSPA_FILTER)                 += af_ladspa.o
-OBJS-$(CONFIG_LOUDNORM_FILTER)               += af_loudnorm.o
+OBJS-$(CONFIG_LOUDNORM_FILTER)               += af_loudnorm.o ebur128.o
 OBJS-$(CONFIG_LOWPASS_FILTER)                += af_biquads.o
 OBJS-$(CONFIG_PAN_FILTER)                    += af_pan.o
 OBJS-$(CONFIG_REPLAYGAIN_FILTER)             += af_replaygain.o
diff --git a/libavfilter/af_loudnorm.c b/libavfilter/af_loudnorm.c
index 604697e..e470f03 100644
--- a/libavfilter/af_loudnorm.c
+++ b/libavfilter/af_loudnorm.c
@@ -24,7 +24,7 @@ 
 #include "avfilter.h"
 #include "internal.h"
 #include "audio.h"
-#include <ebur128.h>
+#include "ebur128.h"
 
 enum FrameType {
     FIRST_FRAME,
@@ -91,8 +91,8 @@  typedef struct LoudNormContext {
     int prev_nb_samples;
     int channels;
 
-    ebur128_state *r128_in;
-    ebur128_state *r128_out;
+    FFEBUR128State *r128_in;
+    FFEBUR128State *r128_out;
 } LoudNormContext;
 
 #define OFFSET(x) offsetof(LoudNormContext, x)
@@ -437,15 +437,15 @@  static int filter_frame(AVFilterLink *inlink, AVFrame *in)
     buf = s->buf;
     limiter_buf = s->limiter_buf;
 
-    ebur128_add_frames_double(s->r128_in, src, in->nb_samples);
+    ff_ebur128_add_il_frames_double(s->r128_in, src, in->nb_samples);
 
     if (s->frame_type == FIRST_FRAME && in->nb_samples < frame_size(inlink->sample_rate, 3000)) {
         double offset, offset_tp, true_peak;
 
-        ebur128_loudness_global(s->r128_in, &global);
+        ff_ebur128_loudness_global(s->r128_in, &global);
         for (c = 0; c < inlink->channels; c++) {
             double tmp;
-            ebur128_sample_peak(s->r128_in, c, &tmp);
+            ff_ebur128_sample_peak(s->r128_in, c, &tmp);
             if (c == 0 || tmp > true_peak)
                 true_peak = tmp;
         }
@@ -467,7 +467,7 @@  static int filter_frame(AVFilterLink *inlink, AVFrame *in)
             s->buf_index += inlink->channels;
         }
 
-        ebur128_loudness_shortterm(s->r128_in, &shortterm);
+        ff_ebur128_loudness_shortterm(s->r128_in, &shortterm);
 
         if (shortterm < s->measured_thresh) {
             s->above_threshold = 0;
@@ -497,7 +497,7 @@  static int filter_frame(AVFilterLink *inlink, AVFrame *in)
 
         subframe_length = frame_size(inlink->sample_rate, 100);
         true_peak_limiter(s, dst, subframe_length, inlink->channels);
-        ebur128_add_frames_double(s->r128_out, dst, subframe_length);
+        ff_ebur128_add_il_frames_double(s->r128_out, dst, subframe_length);
 
         s->pts +=
         out->nb_samples =
@@ -536,12 +536,12 @@  static int filter_frame(AVFilterLink *inlink, AVFrame *in)
         s->limiter_buf_index = s->limiter_buf_index + subframe_length < s->limiter_buf_size ? s->limiter_buf_index + subframe_length : s->limiter_buf_index + subframe_length - s->limiter_buf_size;
 
         true_peak_limiter(s, dst, in->nb_samples, inlink->channels);
-        ebur128_add_frames_double(s->r128_out, dst, in->nb_samples);
+        ff_ebur128_add_il_frames_double(s->r128_out, dst, in->nb_samples);
 
-        ebur128_loudness_range(s->r128_in, &lra);
-        ebur128_loudness_global(s->r128_in, &global);
-        ebur128_loudness_shortterm(s->r128_in, &shortterm);
-        ebur128_relative_threshold(s->r128_in, &relative_threshold);
+        ff_ebur128_loudness_range(s->r128_in, &lra);
+        ff_ebur128_loudness_global(s->r128_in, &global);
+        ff_ebur128_loudness_shortterm(s->r128_in, &shortterm);
+        ff_ebur128_relative_threshold(s->r128_in, &relative_threshold);
 
         if (s->above_threshold == 0) {
             double shortterm_out;
@@ -549,7 +549,7 @@  static int filter_frame(AVFilterLink *inlink, AVFrame *in)
             if (shortterm > s->measured_thresh)
                 s->prev_delta *= 1.0058;
 
-            ebur128_loudness_shortterm(s->r128_out, &shortterm_out);
+            ff_ebur128_loudness_shortterm(s->r128_out, &shortterm_out);
             if (shortterm_out >= s->target_i)
                 s->above_threshold = 1;
         }
@@ -611,7 +611,7 @@  static int filter_frame(AVFilterLink *inlink, AVFrame *in)
         }
 
         dst = (double *)out->data[0];
-        ebur128_add_frames_double(s->r128_out, dst, in->nb_samples);
+        ff_ebur128_add_il_frames_double(s->r128_out, dst, in->nb_samples);
         break;
 
     case LINEAR_MODE:
@@ -624,7 +624,7 @@  static int filter_frame(AVFilterLink *inlink, AVFrame *in)
         }
 
         dst = (double *)out->data[0];
-        ebur128_add_frames_double(s->r128_out, dst, in->nb_samples);
+        ff_ebur128_add_il_frames_double(s->r128_out, dst, in->nb_samples);
         s->pts += in->nb_samples;
         break;
     }
@@ -725,17 +725,17 @@  static int config_input(AVFilterLink *inlink)
     AVFilterContext *ctx = inlink->dst;
     LoudNormContext *s = ctx->priv;
 
-    s->r128_in = ebur128_init(inlink->channels, inlink->sample_rate, EBUR128_MODE_I | EBUR128_MODE_S | EBUR128_MODE_LRA | EBUR128_MODE_SAMPLE_PEAK);
+    s->r128_in = ff_ebur128_init(inlink->channels, inlink->sample_rate, FF_EBUR128_MODE_I | FF_EBUR128_MODE_S | FF_EBUR128_MODE_LRA | FF_EBUR128_MODE_SAMPLE_PEAK);
     if (!s->r128_in)
         return AVERROR(ENOMEM);
 
-    s->r128_out = ebur128_init(inlink->channels, inlink->sample_rate, EBUR128_MODE_I | EBUR128_MODE_S | EBUR128_MODE_LRA | EBUR128_MODE_SAMPLE_PEAK);
+    s->r128_out = ff_ebur128_init(inlink->channels, inlink->sample_rate, FF_EBUR128_MODE_I | FF_EBUR128_MODE_S | FF_EBUR128_MODE_LRA | FF_EBUR128_MODE_SAMPLE_PEAK);
     if (!s->r128_out)
         return AVERROR(ENOMEM);
 
     if (inlink->channels == 1 && s->dual_mono) {
-        ebur128_set_channel(s->r128_in,  0, EBUR128_DUAL_MONO);
-        ebur128_set_channel(s->r128_out, 0, EBUR128_DUAL_MONO);
+        ff_ebur128_set_channel(s->r128_in,  0, FF_EBUR128_DUAL_MONO);
+        ff_ebur128_set_channel(s->r128_out, 0, FF_EBUR128_DUAL_MONO);
     }
 
     s->buf_size = frame_size(inlink->sample_rate, 3000) * inlink->channels;
@@ -799,22 +799,22 @@  static av_cold void uninit(AVFilterContext *ctx)
     if (!s->r128_in || !s->r128_out)
         goto end;
 
-    ebur128_loudness_range(s->r128_in, &lra_in);
-    ebur128_loudness_global(s->r128_in, &i_in);
-    ebur128_relative_threshold(s->r128_in, &thresh_in);
+    ff_ebur128_loudness_range(s->r128_in, &lra_in);
+    ff_ebur128_loudness_global(s->r128_in, &i_in);
+    ff_ebur128_relative_threshold(s->r128_in, &thresh_in);
     for (c = 0; c < s->channels; c++) {
         double tmp;
-        ebur128_sample_peak(s->r128_in, c, &tmp);
+        ff_ebur128_sample_peak(s->r128_in, c, &tmp);
         if ((c == 0) || (tmp > tp_in))
             tp_in = tmp;
     }
 
-    ebur128_loudness_range(s->r128_out, &lra_out);
-    ebur128_loudness_global(s->r128_out, &i_out);
-    ebur128_relative_threshold(s->r128_out, &thresh_out);
+    ff_ebur128_loudness_range(s->r128_out, &lra_out);
+    ff_ebur128_loudness_global(s->r128_out, &i_out);
+    ff_ebur128_relative_threshold(s->r128_out, &thresh_out);
     for (c = 0; c < s->channels; c++) {
         double tmp;
-        ebur128_sample_peak(s->r128_out, c, &tmp);
+        ff_ebur128_sample_peak(s->r128_out, c, &tmp);
         if ((c == 0) || (tmp > tp_out))
             tp_out = tmp;
     }
@@ -881,9 +881,9 @@  static av_cold void uninit(AVFilterContext *ctx)
 
 end:
     if (s->r128_in)
-        ebur128_destroy(&s->r128_in);
+        ff_ebur128_destroy(&s->r128_in);
     if (s->r128_out)
-        ebur128_destroy(&s->r128_out);
+        ff_ebur128_destroy(&s->r128_out);
     av_freep(&s->limiter_buf);
     av_freep(&s->prev_smp);
     av_freep(&s->buf);
diff --git a/libavfilter/ebur128.c b/libavfilter/ebur128.c
new file mode 100644
index 0000000..318da2f
--- /dev/null
+++ b/libavfilter/ebur128.c
@@ -0,0 +1,850 @@ 
+/*
+ * Copyright (c) 2011 Jan Kokemüller
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * This file is based on libebur128 which is available at
+ * https://github.com/jiixyj/libebur128/
+ *
+ * Libebur128 has the following copyright:
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+*/
+
+#include "ebur128.h"
+
+#include <float.h>
+#include <limits.h>
+#include <math.h> /* You may have to define _USE_MATH_DEFINES if you use MSVC */
+
+#include "libavutil/mem.h"
+#include "libavutil/thread.h"
+
+#define CHECK_ERROR(condition, errorcode, goto_point)                          \
+  if ((condition)) {                                                           \
+    errcode = (errorcode);                                                     \
+    goto goto_point;                                                           \
+  }
+
+#define ALMOST_ZERO 0.000001
+
+struct FFEBUR128StateInternal {
+  /** Filtered audio data (used as ring buffer). */
+  double* audio_data;
+  /** Size of audio_data array. */
+  size_t audio_data_frames;
+  /** Current index for audio_data. */
+  size_t audio_data_index;
+  /** How many frames are needed for a gating block. Will correspond to 400ms
+   *  of audio at initialization, and 100ms after the first block (75% overlap
+   *  as specified in the 2011 revision of BS1770). */
+  unsigned long needed_frames;
+  /** The channel map. Has as many elements as there are channels. */
+  int* channel_map;
+  /** How many samples fit in 100ms (rounded). */
+  unsigned long samples_in_100ms;
+  /** BS.1770 filter coefficients (nominator). */
+  double b[5];
+  /** BS.1770 filter coefficients (denominator). */
+  double a[5];
+  /** BS.1770 filter state. */
+  double v[5][5];
+  /** Histograms, used to calculate LRA. */
+  unsigned long *block_energy_histogram;
+  unsigned long *short_term_block_energy_histogram;
+  /** Keeps track of when a new short term block is needed. */
+  size_t short_term_frame_counter;
+  /** Maximum sample peak, one per channel */
+  double* sample_peak;
+  /** The maximum window duration in ms. */
+  unsigned long window;
+  /** Data pointer array for interleaved data */
+  void  **data_ptrs;
+};
+
+static double relative_gate = -10.0;
+
+static AVOnce histogram_init = AV_ONCE_INIT;
+static double relative_gate_factor;
+static double minus_twenty_decibels;
+static double histogram_energies[1000];
+static double histogram_energy_boundaries[1001];
+
+static void ebur128_init_filter(FFEBUR128State* st) {
+  int i, j;
+
+  double f0 = 1681.974450955533;
+  double G  =    3.999843853973347;
+  double Q  =    0.7071752369554196;
+
+  double K  = tan(M_PI * f0 / (double) st->samplerate);
+  double Vh = pow(10.0, G / 20.0);
+  double Vb = pow(Vh, 0.4996667741545416);
+
+  double pb[3] = {0.0,  0.0, 0.0};
+  double pa[3] = {1.0,  0.0, 0.0};
+  double rb[3] = {1.0, -2.0, 1.0};
+  double ra[3] = {1.0,  0.0, 0.0};
+
+  double a0 =      1.0 + K / Q + K * K      ;
+  pb[0] =     (Vh + Vb * K / Q + K * K) / a0;
+  pb[1] =           2.0 * (K * K -  Vh) / a0;
+  pb[2] =     (Vh - Vb * K / Q + K * K) / a0;
+  pa[1] =           2.0 * (K * K - 1.0) / a0;
+  pa[2] =         (1.0 - K / Q + K * K) / a0;
+
+  f0 = 38.13547087602444;
+  Q  =  0.5003270373238773;
+  K  = tan(M_PI * f0 / (double) st->samplerate);
+
+  ra[1] =   2.0 * (K * K - 1.0) / (1.0 + K / Q + K * K);
+  ra[2] = (1.0 - K / Q + K * K) / (1.0 + K / Q + K * K);
+
+  st->d->b[0] = pb[0] * rb[0];
+  st->d->b[1] = pb[0] * rb[1] + pb[1] * rb[0];
+  st->d->b[2] = pb[0] * rb[2] + pb[1] * rb[1] + pb[2] * rb[0];
+  st->d->b[3] = pb[1] * rb[2] + pb[2] * rb[1];
+  st->d->b[4] = pb[2] * rb[2];
+
+  st->d->a[0] = pa[0] * ra[0];
+  st->d->a[1] = pa[0] * ra[1] + pa[1] * ra[0];
+  st->d->a[2] = pa[0] * ra[2] + pa[1] * ra[1] + pa[2] * ra[0];
+  st->d->a[3] = pa[1] * ra[2] + pa[2] * ra[1];
+  st->d->a[4] = pa[2] * ra[2];
+
+  for (i = 0; i < 5; ++i) {
+    for (j = 0; j < 5; ++j) {
+      st->d->v[i][j] = 0.0;
+    }
+  }
+}
+
+static int ebur128_init_channel_map(FFEBUR128State* st) {
+  size_t i;
+  st->d->channel_map = (int*) av_malloc_array(st->channels, sizeof(int));
+  if (!st->d->channel_map) return FF_EBUR128_ERROR_NOMEM;
+  if (st->channels == 4) {
+    st->d->channel_map[0] = FF_EBUR128_LEFT;
+    st->d->channel_map[1] = FF_EBUR128_RIGHT;
+    st->d->channel_map[2] = FF_EBUR128_LEFT_SURROUND;
+    st->d->channel_map[3] = FF_EBUR128_RIGHT_SURROUND;
+  } else if (st->channels == 5) {
+    st->d->channel_map[0] = FF_EBUR128_LEFT;
+    st->d->channel_map[1] = FF_EBUR128_RIGHT;
+    st->d->channel_map[2] = FF_EBUR128_CENTER;
+    st->d->channel_map[3] = FF_EBUR128_LEFT_SURROUND;
+    st->d->channel_map[4] = FF_EBUR128_RIGHT_SURROUND;
+  } else {
+    for (i = 0; i < st->channels; ++i) {
+      switch (i) {
+        case 0:  st->d->channel_map[i] = FF_EBUR128_LEFT;           break;
+        case 1:  st->d->channel_map[i] = FF_EBUR128_RIGHT;          break;
+        case 2:  st->d->channel_map[i] = FF_EBUR128_CENTER;         break;
+        case 3:  st->d->channel_map[i] = FF_EBUR128_UNUSED;         break;
+        case 4:  st->d->channel_map[i] = FF_EBUR128_LEFT_SURROUND;  break;
+        case 5:  st->d->channel_map[i] = FF_EBUR128_RIGHT_SURROUND; break;
+        default: st->d->channel_map[i] = FF_EBUR128_UNUSED;         break;
+      }
+    }
+  }
+  return FF_EBUR128_SUCCESS;
+}
+
+static inline void init_histogram(void)
+{
+  int i;
+  /* initialize static constants */
+  relative_gate_factor = pow(10.0, relative_gate / 10.0);
+  minus_twenty_decibels = pow(10.0, -20.0 / 10.0);
+  histogram_energy_boundaries[0] = pow(10.0, (-70.0 + 0.691) / 10.0);
+  for (i = 0; i < 1000; ++i) {
+    histogram_energies[i] = pow(10.0, ((double) i / 10.0 - 69.95 + 0.691) / 10.0);
+  }
+  for (i = 1; i < 1001; ++i) {
+    histogram_energy_boundaries[i] = pow(10.0, ((double) i / 10.0 - 70.0 + 0.691) / 10.0);
+  }
+}
+
+FFEBUR128State* ff_ebur128_init(unsigned int channels,
+                               unsigned long samplerate,
+                               int mode) {
+  int errcode;
+  FFEBUR128State* st;
+  unsigned int i;
+  size_t j;
+
+  st = (FFEBUR128State*) av_malloc(sizeof(FFEBUR128State));
+  CHECK_ERROR(!st, 0, exit)
+  st->d = (struct FFEBUR128StateInternal*)
+          av_malloc(sizeof(struct FFEBUR128StateInternal));
+  CHECK_ERROR(!st->d, 0, free_state)
+  st->channels = channels;
+  errcode = ebur128_init_channel_map(st);
+  CHECK_ERROR(errcode, 0, free_internal)
+
+  st->d->sample_peak = (double*) av_malloc_array(channels, sizeof(double));
+  CHECK_ERROR(!st->d->sample_peak, 0, free_channel_map)
+  for (i = 0; i < channels; ++i) {
+    st->d->sample_peak[i] = 0.0;
+  }
+
+  st->samplerate = samplerate;
+  st->d->samples_in_100ms = (st->samplerate + 5) / 10;
+  st->mode = mode;
+  if ((mode & FF_EBUR128_MODE_S) == FF_EBUR128_MODE_S) {
+    st->d->window = 3000;
+  } else if ((mode & FF_EBUR128_MODE_M) == FF_EBUR128_MODE_M) {
+    st->d->window = 400;
+  } else {
+    goto free_sample_peak;
+  }
+  st->d->audio_data_frames = st->samplerate * st->d->window / 1000;
+  if (st->d->audio_data_frames % st->d->samples_in_100ms) {
+    /* round up to multiple of samples_in_100ms */
+    st->d->audio_data_frames = st->d->audio_data_frames
+                             + st->d->samples_in_100ms
+                             - (st->d->audio_data_frames % st->d->samples_in_100ms);
+  }
+  st->d->audio_data = (double*) av_malloc_array(st->d->audio_data_frames,
+                                                st->channels * sizeof(double));
+  CHECK_ERROR(!st->d->audio_data, 0, free_sample_peak)
+  for (j = 0; j < st->d->audio_data_frames * st->channels; ++j) {
+    st->d->audio_data[i] = 0.0;
+  }
+
+  ebur128_init_filter(st);
+
+  st->d->block_energy_histogram = av_malloc(1000 * sizeof(unsigned long));
+  CHECK_ERROR(!st->d->block_energy_histogram, 0, free_audio_data)
+  for (i = 0; i < 1000; ++i) {
+    st->d->block_energy_histogram[i] = 0;
+  }
+  st->d->short_term_block_energy_histogram = av_malloc(1000 * sizeof(unsigned long));
+  CHECK_ERROR(!st->d->short_term_block_energy_histogram, 0, free_block_energy_histogram)
+  for (i = 0; i < 1000; ++i) {
+    st->d->short_term_block_energy_histogram[i] = 0;
+  }
+  st->d->short_term_frame_counter = 0;
+
+  /* the first block needs 400ms of audio data */
+  st->d->needed_frames = st->d->samples_in_100ms * 4;
+  /* start at the beginning of the buffer */
+  st->d->audio_data_index = 0;
+
+  if (ff_thread_once(&histogram_init, &init_histogram) != 0)
+      goto free_short_term_block_energy_histogram;
+
+  st->d->data_ptrs = av_malloc_array(channels, sizeof(void*));
+  CHECK_ERROR(!st->d->data_ptrs, 0, free_short_term_block_energy_histogram);
+
+  return st;
+
+free_short_term_block_energy_histogram:
+  av_free(st->d->short_term_block_energy_histogram);
+free_block_energy_histogram:
+  av_free(st->d->block_energy_histogram);
+free_audio_data:
+  av_free(st->d->audio_data);
+free_sample_peak:
+  av_free(st->d->sample_peak);
+free_channel_map:
+  av_free(st->d->channel_map);
+free_internal:
+  av_free(st->d);
+free_state:
+  av_free(st);
+exit:
+  return NULL;
+}
+
+void ff_ebur128_destroy(FFEBUR128State** st) {
+  av_free((*st)->d->block_energy_histogram);
+  av_free((*st)->d->short_term_block_energy_histogram);
+  av_free((*st)->d->audio_data);
+  av_free((*st)->d->channel_map);
+  av_free((*st)->d->sample_peak);
+  av_free((*st)->d->data_ptrs);
+  av_free((*st)->d);
+  av_free(*st);
+  *st = NULL;
+}
+
+#define EBUR128_FILTER(type, min_scale, max_scale)                             \
+static void ebur128_filter_##type(FFEBUR128State* st, const type** srcs,       \
+                                  size_t src_index, size_t frames,             \
+                                  int stride) {                                \
+  static double scaling_factor = -((double) min_scale) > (double) max_scale ?  \
+                                 -((double) min_scale) : (double) max_scale;   \
+  double* audio_data = st->d->audio_data + st->d->audio_data_index;            \
+  size_t i, c;                                                                 \
+                                                                               \
+  if ((st->mode & FF_EBUR128_MODE_SAMPLE_PEAK) == FF_EBUR128_MODE_SAMPLE_PEAK) {     \
+    for (c = 0; c < st->channels; ++c) {                                       \
+      double max = 0.0;                                                        \
+      for (i = 0; i < frames; ++i) {                                           \
+        type v = srcs[c][src_index + i * stride];                              \
+        if (v > max) {                                                         \
+          max =        v;                                                      \
+        } else if (-v > max) {                                                 \
+          max = -1.0 * v;                                                      \
+        }                                                                      \
+      }                                                                        \
+      max /= scaling_factor;                                                   \
+      if (max > st->d->sample_peak[c]) st->d->sample_peak[c] = max;            \
+    }                                                                          \
+  }                                                                            \
+  for (c = 0; c < st->channels; ++c) {                                         \
+    int ci = st->d->channel_map[c] - 1;                                        \
+    if (ci < 0) continue;                                                      \
+    else if (ci == FF_EBUR128_DUAL_MONO - 1) ci = 0; /*dual mono */               \
+    for (i = 0; i < frames; ++i) {                                             \
+      st->d->v[ci][0] = (double) (srcs[c][src_index + i * stride] / scaling_factor) \
+                   - st->d->a[1] * st->d->v[ci][1]                             \
+                   - st->d->a[2] * st->d->v[ci][2]                             \
+                   - st->d->a[3] * st->d->v[ci][3]                             \
+                   - st->d->a[4] * st->d->v[ci][4];                            \
+      audio_data[i * st->channels + c] =                                       \
+                     st->d->b[0] * st->d->v[ci][0]                             \
+                   + st->d->b[1] * st->d->v[ci][1]                             \
+                   + st->d->b[2] * st->d->v[ci][2]                             \
+                   + st->d->b[3] * st->d->v[ci][3]                             \
+                   + st->d->b[4] * st->d->v[ci][4];                            \
+      st->d->v[ci][4] = st->d->v[ci][3];                                       \
+      st->d->v[ci][3] = st->d->v[ci][2];                                       \
+      st->d->v[ci][2] = st->d->v[ci][1];                                       \
+      st->d->v[ci][1] = st->d->v[ci][0];                                       \
+    }                                                                          \
+    st->d->v[ci][4] = fabs(st->d->v[ci][4]) < DBL_MIN ? 0.0 : st->d->v[ci][4]; \
+    st->d->v[ci][3] = fabs(st->d->v[ci][3]) < DBL_MIN ? 0.0 : st->d->v[ci][3]; \
+    st->d->v[ci][2] = fabs(st->d->v[ci][2]) < DBL_MIN ? 0.0 : st->d->v[ci][2]; \
+    st->d->v[ci][1] = fabs(st->d->v[ci][1]) < DBL_MIN ? 0.0 : st->d->v[ci][1]; \
+  }                                                                            \
+}
+EBUR128_FILTER(short, SHRT_MIN, SHRT_MAX)
+EBUR128_FILTER(int, INT_MIN, INT_MAX)
+EBUR128_FILTER(float, -1.0f, 1.0f)
+EBUR128_FILTER(double, -1.0, 1.0)
+
+static double ebur128_energy_to_loudness(double energy) {
+  return 10 * (log(energy) / log(10.0)) - 0.691;
+}
+
+static size_t find_histogram_index(double energy) {
+  size_t index_min = 0;
+  size_t index_max = 1000;
+  size_t index_mid;
+
+  do {
+    index_mid = (index_min + index_max) / 2;
+    if (energy >= histogram_energy_boundaries[index_mid]) {
+      index_min = index_mid;
+    } else {
+      index_max = index_mid;
+    }
+  } while (index_max - index_min != 1);
+
+  return index_min;
+}
+
+static void ebur128_calc_gating_block(FFEBUR128State* st, size_t frames_per_block,
+                                     double* optional_output) {
+  size_t i, c;
+  double sum = 0.0;
+  double channel_sum;
+  for (c = 0; c < st->channels; ++c) {
+    if (st->d->channel_map[c] == FF_EBUR128_UNUSED) continue;
+    channel_sum = 0.0;
+    if (st->d->audio_data_index < frames_per_block * st->channels) {
+      for (i = 0; i < st->d->audio_data_index / st->channels; ++i) {
+        channel_sum += st->d->audio_data[i * st->channels + c] *
+                       st->d->audio_data[i * st->channels + c];
+      }
+      for (i = st->d->audio_data_frames -
+              (frames_per_block -
+               st->d->audio_data_index / st->channels);
+           i < st->d->audio_data_frames; ++i) {
+        channel_sum += st->d->audio_data[i * st->channels + c] *
+                       st->d->audio_data[i * st->channels + c];
+      }
+    } else {
+      for (i = st->d->audio_data_index / st->channels - frames_per_block;
+           i < st->d->audio_data_index / st->channels;
+           ++i) {
+        channel_sum += st->d->audio_data[i * st->channels + c] *
+                       st->d->audio_data[i * st->channels + c];
+      }
+    }
+    if (st->d->channel_map[c] == FF_EBUR128_Mp110 ||
+        st->d->channel_map[c] == FF_EBUR128_Mm110 ||
+        st->d->channel_map[c] == FF_EBUR128_Mp060 ||
+        st->d->channel_map[c] == FF_EBUR128_Mm060 ||
+        st->d->channel_map[c] == FF_EBUR128_Mp090 ||
+        st->d->channel_map[c] == FF_EBUR128_Mm090) {
+      channel_sum *= 1.41;
+    } else if (st->d->channel_map[c] == FF_EBUR128_DUAL_MONO) {
+      channel_sum *= 2.0;
+    }
+    sum += channel_sum;
+  }
+  sum /= (double) frames_per_block;
+  if (optional_output) {
+    *optional_output = sum;
+  } else if (sum >= histogram_energy_boundaries[0]) {
+    ++st->d->block_energy_histogram[find_histogram_index(sum)];
+  }
+}
+
+int ff_ebur128_set_channel(FFEBUR128State* st,
+                           unsigned int channel_number,
+                           int value) {
+  if (channel_number >= st->channels) {
+    return 1;
+  }
+  if (value == FF_EBUR128_DUAL_MONO &&
+      (st->channels != 1 || channel_number != 0)) {
+    return 1;
+  }
+  st->d->channel_map[channel_number] = value;
+  return 0;
+}
+
+int ff_ebur128_change_parameters(FFEBUR128State* st,
+                                 unsigned int channels,
+                                 unsigned long samplerate) {
+  int errcode = FF_EBUR128_SUCCESS;
+  size_t j;
+
+  if (channels == st->channels &&
+      samplerate == st->samplerate) {
+    return FF_EBUR128_ERROR_NO_CHANGE;
+  }
+  av_free(st->d->audio_data);
+  st->d->audio_data = NULL;
+
+  if (channels != st->channels) {
+    unsigned int i;
+
+    av_free(st->d->channel_map); st->d->channel_map = NULL;
+    av_free(st->d->sample_peak); st->d->sample_peak = NULL;
+    st->channels = channels;
+
+    errcode = ebur128_init_channel_map(st);
+    CHECK_ERROR(errcode, FF_EBUR128_ERROR_NOMEM, exit)
+
+    st->d->sample_peak = (double*) av_malloc_array(channels, sizeof(double));
+    CHECK_ERROR(!st->d->sample_peak, FF_EBUR128_ERROR_NOMEM, exit)
+    for (i = 0; i < channels; ++i) {
+      st->d->sample_peak[i] = 0.0;
+    }
+  }
+  if (samplerate != st->samplerate) {
+    st->samplerate = samplerate;
+    st->d->samples_in_100ms = (st->samplerate + 5) / 10;
+    ebur128_init_filter(st);
+  }
+  st->d->audio_data_frames = st->samplerate * st->d->window / 1000;
+  if (st->d->audio_data_frames % st->d->samples_in_100ms) {
+    /* round up to multiple of samples_in_100ms */
+    st->d->audio_data_frames = st->d->audio_data_frames
+                             + st->d->samples_in_100ms
+                             - (st->d->audio_data_frames % st->d->samples_in_100ms);
+  }
+  st->d->audio_data = (double*) av_malloc_array(st->d->audio_data_frames,
+                                                st->channels * sizeof(double));
+  CHECK_ERROR(!st->d->audio_data, FF_EBUR128_ERROR_NOMEM, exit)
+  for (j = 0; j < st->d->audio_data_frames * st->channels; ++j) {
+    st->d->audio_data[j] = 0.0;
+  }
+
+  /* the first block needs 400ms of audio data */
+  st->d->needed_frames = st->d->samples_in_100ms * 4;
+  /* start at the beginning of the buffer */
+  st->d->audio_data_index = 0;
+  /* reset short term frame counter */
+  st->d->short_term_frame_counter = 0;
+
+exit:
+  return errcode;
+}
+
+int ff_ebur128_set_max_window(FFEBUR128State* st, unsigned long window)
+{
+  int errcode = FF_EBUR128_SUCCESS;
+  size_t j;
+
+  if ((st->mode & FF_EBUR128_MODE_S) == FF_EBUR128_MODE_S && window < 3000) {
+    window = 3000;
+  } else if ((st->mode & FF_EBUR128_MODE_M) == FF_EBUR128_MODE_M && window < 400) {
+    window = 400;
+  }
+  if (window == st->d->window) {
+    return FF_EBUR128_ERROR_NO_CHANGE;
+  }
+
+  st->d->window = window;
+  av_free(st->d->audio_data);
+  st->d->audio_data = NULL;
+  st->d->audio_data_frames = st->samplerate * st->d->window / 1000;
+  if (st->d->audio_data_frames % st->d->samples_in_100ms) {
+    /* round up to multiple of samples_in_100ms */
+    st->d->audio_data_frames = st->d->audio_data_frames
+                             + st->d->samples_in_100ms
+                             - (st->d->audio_data_frames % st->d->samples_in_100ms);
+  }
+  st->d->audio_data = (double*) av_malloc_array(st->d->audio_data_frames,
+                                                st->channels * sizeof(double));
+  CHECK_ERROR(!st->d->audio_data, FF_EBUR128_ERROR_NOMEM, exit)
+  for (j = 0; j < st->d->audio_data_frames * st->channels; ++j) {
+    st->d->audio_data[j] = 0.0;
+  }
+
+  /* the first block needs 400ms of audio data */
+  st->d->needed_frames = st->d->samples_in_100ms * 4;
+  /* start at the beginning of the buffer */
+  st->d->audio_data_index = 0;
+  /* reset short term frame counter */
+  st->d->short_term_frame_counter = 0;
+
+exit:
+  return errcode;
+}
+
+
+static int ebur128_energy_shortterm(FFEBUR128State* st, double* out);
+#define FF_EBUR128_ADD_FRAMES(type)                                               \
+void ff_ebur128_add_frames_##type(FFEBUR128State* st, const type** srcs,       \
+                                 size_t frames, int stride) {                  \
+  size_t src_index = 0;                                                        \
+  while (frames > 0) {                                                         \
+    if (frames >= st->d->needed_frames) {                                      \
+      ebur128_filter_##type(st, srcs, src_index, st->d->needed_frames, stride);\
+      src_index += st->d->needed_frames * stride;                              \
+      frames -= st->d->needed_frames;                                          \
+      st->d->audio_data_index += st->d->needed_frames * st->channels;          \
+      /* calculate the new gating block */                                     \
+      if ((st->mode & FF_EBUR128_MODE_I) == FF_EBUR128_MODE_I) {                     \
+        ebur128_calc_gating_block(st, st->d->samples_in_100ms * 4, NULL);      \
+      }                                                                        \
+      if ((st->mode & FF_EBUR128_MODE_LRA) == FF_EBUR128_MODE_LRA) {                 \
+        st->d->short_term_frame_counter += st->d->needed_frames;               \
+        if (st->d->short_term_frame_counter == st->d->samples_in_100ms * 30) { \
+          double st_energy;                                                    \
+          ebur128_energy_shortterm(st, &st_energy);                            \
+          if (st_energy >= histogram_energy_boundaries[0]) {                   \
+            ++st->d->short_term_block_energy_histogram[                        \
+                                            find_histogram_index(st_energy)];  \
+          }                                                                    \
+          st->d->short_term_frame_counter = st->d->samples_in_100ms * 20;      \
+        }                                                                      \
+      }                                                                        \
+      /* 100ms are needed for all blocks besides the first one */              \
+      st->d->needed_frames = st->d->samples_in_100ms;                          \
+      /* reset audio_data_index when buffer full */                            \
+      if (st->d->audio_data_index == st->d->audio_data_frames * st->channels) {\
+        st->d->audio_data_index = 0;                                           \
+      }                                                                        \
+    } else {                                                                   \
+      ebur128_filter_##type(st, srcs, src_index, frames, stride);              \
+      st->d->audio_data_index += frames * st->channels;                        \
+      if ((st->mode & FF_EBUR128_MODE_LRA) == FF_EBUR128_MODE_LRA) {                 \
+        st->d->short_term_frame_counter += frames;                             \
+      }                                                                        \
+      st->d->needed_frames -= frames;                                          \
+      frames = 0;                                                              \
+    }                                                                          \
+  }                                                                            \
+}
+FF_EBUR128_ADD_FRAMES(short)
+FF_EBUR128_ADD_FRAMES(int)
+FF_EBUR128_ADD_FRAMES(float)
+FF_EBUR128_ADD_FRAMES(double)
+
+#define FF_EBUR128_ADD_IL_FRAMES(type)                                         \
+void ff_ebur128_add_il_frames_##type(FFEBUR128State* st, const type* src,       \
+                                    size_t frames) {                           \
+  int i;                                                                       \
+  const type **buf = (const type**)st->d->data_ptrs;                           \
+  for (i = 0; i < st->channels; i++)                                           \
+    buf[i] = src + i;                                                          \
+  ff_ebur128_add_frames_##type(st, buf, frames, st->channels);                 \
+}
+FF_EBUR128_ADD_IL_FRAMES(short)
+FF_EBUR128_ADD_IL_FRAMES(int)
+FF_EBUR128_ADD_IL_FRAMES(float)
+FF_EBUR128_ADD_IL_FRAMES(double)
+
+static int ebur128_calc_relative_threshold(FFEBUR128State* st,
+                                           size_t* above_thresh_counter,
+                                           double* relative_threshold) {
+  size_t i;
+  *relative_threshold = 0.0;
+  *above_thresh_counter = 0;
+
+  for (i = 0; i < 1000; ++i) {
+    *relative_threshold += st->d->block_energy_histogram[i] *
+                          histogram_energies[i];
+    *above_thresh_counter += st->d->block_energy_histogram[i];
+  }
+
+  if (*above_thresh_counter != 0) {
+    *relative_threshold /= (double) *above_thresh_counter;
+    *relative_threshold *= relative_gate_factor;
+  }
+
+  return FF_EBUR128_SUCCESS;
+}
+
+static int ebur128_gated_loudness(FFEBUR128State** sts, size_t size,
+                                  double* out) {
+  double gated_loudness = 0.0;
+  double relative_threshold;
+  size_t above_thresh_counter;
+  size_t i, j, start_index;
+
+  for (i = 0; i < size; i++) {
+    if (sts[i] && (sts[i]->mode & FF_EBUR128_MODE_I) != FF_EBUR128_MODE_I) {
+      return FF_EBUR128_ERROR_INVALID_MODE;
+    }
+  }
+
+  for (i = 0; i < size; i++) {
+    if (!sts[i]) continue;
+    ebur128_calc_relative_threshold(sts[i], &above_thresh_counter, &relative_threshold);
+  }
+  if (!above_thresh_counter) {
+    *out = -HUGE_VAL;
+    return FF_EBUR128_SUCCESS;
+  }
+
+  above_thresh_counter = 0;
+  if (relative_threshold < histogram_energy_boundaries[0]) {
+    start_index = 0;
+  } else {
+    start_index = find_histogram_index(relative_threshold);
+    if (relative_threshold > histogram_energies[start_index]) {
+      ++start_index;
+    }
+  }
+  for (i = 0; i < size; i++) {
+    if (!sts[i]) continue;
+    for (j = start_index; j < 1000; ++j) {
+      gated_loudness += sts[i]->d->block_energy_histogram[j] *
+                        histogram_energies[j];
+      above_thresh_counter += sts[i]->d->block_energy_histogram[j];
+    }
+  }
+  if (!above_thresh_counter) {
+    *out = -HUGE_VAL;
+    return FF_EBUR128_SUCCESS;
+  }
+  gated_loudness /= (double) above_thresh_counter;
+  *out = ebur128_energy_to_loudness(gated_loudness);
+  return FF_EBUR128_SUCCESS;
+}
+
+int ff_ebur128_relative_threshold(FFEBUR128State* st, double* out) {
+  double relative_threshold;
+  size_t above_thresh_counter;
+
+  if (st && (st->mode & FF_EBUR128_MODE_I) != FF_EBUR128_MODE_I)
+    return FF_EBUR128_ERROR_INVALID_MODE;
+
+  ebur128_calc_relative_threshold(st, &above_thresh_counter, &relative_threshold);
+
+  if (!above_thresh_counter) {
+      *out = -70.0;
+      return FF_EBUR128_SUCCESS;
+  }
+
+  *out = ebur128_energy_to_loudness(relative_threshold);
+  return FF_EBUR128_SUCCESS;
+}
+
+int ff_ebur128_loudness_global(FFEBUR128State* st, double* out) {
+  return ebur128_gated_loudness(&st, 1, out);
+}
+
+int ff_ebur128_loudness_global_multiple(FFEBUR128State** sts, size_t size,
+                                     double* out) {
+  return ebur128_gated_loudness(sts, size, out);
+}
+
+static int ebur128_energy_in_interval(FFEBUR128State* st,
+                                      size_t interval_frames,
+                                      double* out) {
+  if (interval_frames > st->d->audio_data_frames) {
+    return FF_EBUR128_ERROR_INVALID_MODE;
+  }
+  ebur128_calc_gating_block(st, interval_frames, out);
+  return FF_EBUR128_SUCCESS;
+}
+
+static int ebur128_energy_shortterm(FFEBUR128State* st, double* out) {
+  return ebur128_energy_in_interval(st, st->d->samples_in_100ms * 30, out);
+}
+
+int ff_ebur128_loudness_momentary(FFEBUR128State* st, double* out) {
+  double energy;
+  int error = ebur128_energy_in_interval(st, st->d->samples_in_100ms * 4,
+                                         &energy);
+  if (error) {
+    return error;
+  } else if (energy <= 0.0) {
+    *out = -HUGE_VAL;
+    return FF_EBUR128_SUCCESS;
+  }
+  *out = ebur128_energy_to_loudness(energy);
+  return FF_EBUR128_SUCCESS;
+}
+
+int ff_ebur128_loudness_shortterm(FFEBUR128State* st, double* out) {
+  double energy;
+  int error = ebur128_energy_shortterm(st, &energy);
+  if (error) {
+    return error;
+  } else if (energy <= 0.0) {
+    *out = -HUGE_VAL;
+    return FF_EBUR128_SUCCESS;
+  }
+  *out = ebur128_energy_to_loudness(energy);
+  return FF_EBUR128_SUCCESS;
+}
+
+int ff_ebur128_loudness_window(FFEBUR128State* st,
+                            unsigned long window,
+                            double* out) {
+  double energy;
+  size_t interval_frames = st->samplerate * window / 1000;
+  int error = ebur128_energy_in_interval(st, interval_frames, &energy);
+  if (error) {
+    return error;
+  } else if (energy <= 0.0) {
+    *out = -HUGE_VAL;
+    return FF_EBUR128_SUCCESS;
+  }
+  *out = ebur128_energy_to_loudness(energy);
+  return FF_EBUR128_SUCCESS;
+}
+
+/* EBU - TECH 3342 */
+int ff_ebur128_loudness_range_multiple(FFEBUR128State** sts, size_t size,
+                                       double* out) {
+  size_t i, j;
+  size_t stl_size;
+  double stl_power, stl_integrated;
+  /* High and low percentile energy */
+  double h_en, l_en;
+
+  for (i = 0; i < size; ++i) {
+    if (sts[i]) {
+      if ((sts[i]->mode & FF_EBUR128_MODE_LRA) != FF_EBUR128_MODE_LRA) {
+        return FF_EBUR128_ERROR_INVALID_MODE;
+      }
+    }
+  }
+
+  {
+    unsigned long hist[1000] = { 0 };
+    size_t percentile_low, percentile_high;
+    size_t index;
+
+    stl_size = 0;
+    stl_power = 0.0;
+    for (i = 0; i < size; ++i) {
+      if (!sts[i]) continue;
+      for (j = 0; j < 1000; ++j) {
+        hist[j]   += sts[i]->d->short_term_block_energy_histogram[j];
+        stl_size  += sts[i]->d->short_term_block_energy_histogram[j];
+        stl_power += sts[i]->d->short_term_block_energy_histogram[j]
+                     * histogram_energies[j];
+      }
+    }
+    if (!stl_size) {
+      *out = 0.0;
+      return FF_EBUR128_SUCCESS;
+    }
+
+    stl_power /= stl_size;
+    stl_integrated = minus_twenty_decibels * stl_power;
+
+    if (stl_integrated < histogram_energy_boundaries[0]) {
+      index = 0;
+    } else {
+      index = find_histogram_index(stl_integrated);
+      if (stl_integrated > histogram_energies[index]) {
+        ++index;
+      }
+    }
+    stl_size = 0;
+    for (j = index; j < 1000; ++j) {
+      stl_size += hist[j];
+    }
+    if (!stl_size) {
+      *out = 0.0;
+      return FF_EBUR128_SUCCESS;
+    }
+
+    percentile_low  = (size_t) ((stl_size - 1) * 0.1 + 0.5);
+    percentile_high = (size_t) ((stl_size - 1) * 0.95 + 0.5);
+
+    stl_size = 0;
+    j = index;
+    while (stl_size <= percentile_low) {
+      stl_size += hist[j++];
+    }
+    l_en = histogram_energies[j - 1];
+    while (stl_size <= percentile_high) {
+      stl_size += hist[j++];
+    }
+    h_en = histogram_energies[j - 1];
+    *out = ebur128_energy_to_loudness(h_en) - ebur128_energy_to_loudness(l_en);
+    return FF_EBUR128_SUCCESS;
+  }
+}
+
+int ff_ebur128_loudness_range(FFEBUR128State* st, double* out) {
+  return ff_ebur128_loudness_range_multiple(&st, 1, out);
+}
+
+int ff_ebur128_sample_peak(FFEBUR128State* st,
+                        unsigned int channel_number,
+                        double* out) {
+  if ((st->mode & FF_EBUR128_MODE_SAMPLE_PEAK) != FF_EBUR128_MODE_SAMPLE_PEAK) {
+    return FF_EBUR128_ERROR_INVALID_MODE;
+  } else if (channel_number >= st->channels) {
+    return FF_EBUR128_ERROR_INVALID_CHANNEL_INDEX;
+  }
+  *out = st->d->sample_peak[channel_number];
+  return FF_EBUR128_SUCCESS;
+}
+
diff --git a/libavfilter/ebur128.h b/libavfilter/ebur128.h
new file mode 100644
index 0000000..8541e05
--- /dev/null
+++ b/libavfilter/ebur128.h
@@ -0,0 +1,359 @@ 
+/*
+ * Copyright (c) 2011 Jan Kokemüller
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * This file is based on libebur128 which is available at
+ * https://github.com/jiixyj/libebur128/
+ *
+*/
+
+#ifndef AVFILTER_EBUR128_H
+#define AVFILTER_EBUR128_H
+
+/** \file ebur128.h
+ *  \brief libebur128 - a library for loudness measurement according to
+ *         the EBU R128 standard.
+ */
+
+#include <stddef.h>       /* for size_t */
+
+/** \enum channel
+ *  Use these values when setting the channel map with ebur128_set_channel().
+ *  See definitions in ITU R-REC-BS 1770-4
+ */
+enum channel {
+  FF_EBUR128_UNUSED = 0,     /**< unused channel (for example LFE channel) */
+  FF_EBUR128_LEFT,
+  FF_EBUR128_Mp030 = 1,      /**< itu M+030 */
+  FF_EBUR128_RIGHT,
+  FF_EBUR128_Mm030 = 2,      /**< itu M-030 */
+  FF_EBUR128_CENTER,
+  FF_EBUR128_Mp000 = 3,      /**< itu M+000 */
+  FF_EBUR128_LEFT_SURROUND,
+  FF_EBUR128_Mp110 = 4,      /**< itu M+110 */
+  FF_EBUR128_RIGHT_SURROUND,
+  FF_EBUR128_Mm110 = 5,      /**< itu M-110 */
+  FF_EBUR128_DUAL_MONO,      /**< a channel that is counted twice */
+  FF_EBUR128_MpSC,           /**< itu M+SC */
+  FF_EBUR128_MmSC,           /**< itu M-SC */
+  FF_EBUR128_Mp060,          /**< itu M+060 */
+  FF_EBUR128_Mm060,          /**< itu M-060 */
+  FF_EBUR128_Mp090,          /**< itu M+090 */
+  FF_EBUR128_Mm090,          /**< itu M-090 */
+  FF_EBUR128_Mp135,          /**< itu M+135 */
+  FF_EBUR128_Mm135,          /**< itu M-135 */
+  FF_EBUR128_Mp180,          /**< itu M+180 */
+  FF_EBUR128_Up000,          /**< itu U+000 */
+  FF_EBUR128_Up030,          /**< itu U+030 */
+  FF_EBUR128_Um030,          /**< itu U-030 */
+  FF_EBUR128_Up045,          /**< itu U+045 */
+  FF_EBUR128_Um045,          /**< itu U-030 */
+  FF_EBUR128_Up090,          /**< itu U+090 */
+  FF_EBUR128_Um090,          /**< itu U-090 */
+  FF_EBUR128_Up110,          /**< itu U+110 */
+  FF_EBUR128_Um110,          /**< itu U-110 */
+  FF_EBUR128_Up135,          /**< itu U+135 */
+  FF_EBUR128_Um135,          /**< itu U-135 */
+  FF_EBUR128_Up180,          /**< itu U+180 */
+  FF_EBUR128_Tp000,          /**< itu T+000 */
+  FF_EBUR128_Bp000,          /**< itu B+000 */
+  FF_EBUR128_Bp045,          /**< itu B+045 */
+  FF_EBUR128_Bm045           /**< itu B-045 */
+};
+
+/** \enum error
+ *  Error return values.
+ */
+enum error {
+  FF_EBUR128_SUCCESS = 0,
+  FF_EBUR128_ERROR_NOMEM,
+  FF_EBUR128_ERROR_INVALID_MODE,
+  FF_EBUR128_ERROR_INVALID_CHANNEL_INDEX,
+  FF_EBUR128_ERROR_NO_CHANGE
+};
+
+/** \enum mode
+ *  Use these values in ebur128_init (or'ed). Try to use the lowest possible
+ *  modes that suit your needs, as performance will be better.
+ */
+enum mode {
+  /** can call ebur128_loudness_momentary */
+  FF_EBUR128_MODE_M           = (1 << 0),
+  /** can call ebur128_loudness_shortterm */
+  FF_EBUR128_MODE_S           = (1 << 1) | FF_EBUR128_MODE_M,
+  /** can call ebur128_loudness_global_* and ebur128_relative_threshold */
+  FF_EBUR128_MODE_I           = (1 << 2) | FF_EBUR128_MODE_M,
+  /** can call ebur128_loudness_range */
+  FF_EBUR128_MODE_LRA         = (1 << 3) | FF_EBUR128_MODE_S,
+  /** can call ebur128_sample_peak */
+  FF_EBUR128_MODE_SAMPLE_PEAK = (1 << 4) | FF_EBUR128_MODE_M,
+};
+
+/** forward declaration of FFEBUR128StateInternal */
+struct FFEBUR128StateInternal;
+
+/** \brief Contains information about the state of a loudness measurement.
+ *
+ *  You should not need to modify this struct directly.
+ */
+typedef struct {
+  int mode;                           /**< The current mode. */
+  unsigned int channels;              /**< The number of channels. */
+  unsigned long samplerate;           /**< The sample rate. */
+  struct FFEBUR128StateInternal* d;   /**< Internal state. */
+} FFEBUR128State;
+
+/** \brief Initialize library state.
+ *
+ *  @param channels the number of channels.
+ *  @param samplerate the sample rate.
+ *  @param mode see the mode enum for possible values.
+ *  @return an initialized library state.
+ */
+FFEBUR128State* ff_ebur128_init(unsigned int channels,
+                               unsigned long samplerate,
+                               int mode);
+
+/** \brief Destroy library state.
+ *
+ *  @param st pointer to a library state.
+ */
+void ff_ebur128_destroy(FFEBUR128State** st);
+
+/** \brief Set channel type.
+ *
+ *  The default is:
+ *  - 0 -> FF_EBUR128_LEFT
+ *  - 1 -> FF_EBUR128_RIGHT
+ *  - 2 -> FF_EBUR128_CENTER
+ *  - 3 -> FF_EBUR128_UNUSED
+ *  - 4 -> FF_EBUR128_LEFT_SURROUND
+ *  - 5 -> FF_EBUR128_RIGHT_SURROUND
+ *
+ *  @param st library state.
+ *  @param channel_number zero based channel index.
+ *  @param value channel type from the "channel" enum.
+ *  @return
+ *    - FF_EBUR128_SUCCESS on success.
+ *    - FF_EBUR128_ERROR_INVALID_CHANNEL_INDEX if invalid channel index.
+ */
+int ff_ebur128_set_channel(FFEBUR128State* st,
+                           unsigned int channel_number,
+                           int value);
+
+/** \brief Change library parameters.
+ *
+ *  Note that the channel map will be reset when setting a different number of
+ *  channels. The current unfinished block will be lost.
+ *
+ *  @param st library state.
+ *  @param channels new number of channels.
+ *  @param samplerate new sample rate.
+ *  @return
+ *    - FF_EBUR128_SUCCESS on success.
+ *    - FF_EBUR128_ERROR_NOMEM on memory allocation error. The state will be
+ *      invalid and must be destroyed.
+ *    - FF_EBUR128_ERROR_NO_CHANGE if channels and sample rate were not changed.
+ */
+int ff_ebur128_change_parameters(FFEBUR128State* st,
+                                 unsigned int channels,
+                                 unsigned long samplerate);
+
+/** \brief Set the maximum window duration.
+ *
+ *  Set the maximum duration that will be used for ebur128_window_loudness().
+ *  Note that this destroys the current content of the audio buffer.
+ *
+ *  @param st library state.
+ *  @param window duration of the window in ms.
+ *  @return
+ *    - FF_EBUR128_SUCCESS on success.
+ *    - FF_EBUR128_ERROR_NOMEM on memory allocation error. The state will be
+ *      invalid and must be destroyed.
+ *    - FF_EBUR128_ERROR_NO_CHANGE if window duration not changed.
+ */
+int ff_ebur128_set_max_window(FFEBUR128State* st, unsigned long window);
+
+/** \brief Add frames to be processed.
+ *
+ *  @param st library state.
+ *  @param src array of source frames. Channels must be interleaved.
+ *  @param frames number of frames. Not number of samples!
+ */
+void ff_ebur128_add_il_frames_short(FFEBUR128State* st,
+                             const short* src,
+                             size_t frames);
+/** \brief See \ref ebur128_add_frames_short */
+void ff_ebur128_add_il_frames_int(FFEBUR128State* st,
+                             const int* src,
+                             size_t frames);
+/** \brief See \ref ebur128_add_frames_short */
+void ff_ebur128_add_il_frames_float(FFEBUR128State* st,
+                             const float* src,
+                             size_t frames);
+/** \brief See \ref ebur128_add_frames_short */
+void ff_ebur128_add_il_frames_double(FFEBUR128State* st,
+                             const double* src,
+                             size_t frames);
+
+/** \brief Add frames to be processed.
+ *
+ *  @param st library state.
+ *  @param srcs array of source frame channel data pointers
+ *  @param frames number of frames. Not number of samples!
+ *  @param stride number of samples to skip to for the next sample of the same channel
+ */
+void ff_ebur128_add_frames_short(FFEBUR128State* st,
+                             const short** srcs,
+                             size_t frames,
+                             int stride);
+/** \brief See \ref ebur128_add_frames_short */
+void ff_ebur128_add_frames_int(FFEBUR128State* st,
+                             const int** srcs,
+                             size_t frames,
+                             int stride);
+/** \brief See \ref ebur128_add_frames_short */
+void ff_ebur128_add_frames_float(FFEBUR128State* st,
+                             const float** srcs,
+                             size_t frames,
+                             int stride);
+/** \brief See \ref ebur128_add_frames_short */
+void ff_ebur128_add_frames_double(FFEBUR128State* st,
+                             const double** srcs,
+                             size_t frames,
+                             int stride);
+
+/** \brief Get global integrated loudness in LUFS.
+ *
+ *  @param st library state.
+ *  @param out integrated loudness in LUFS. -HUGE_VAL if result is negative
+ *             infinity.
+ *  @return
+ *    - FF_EBUR128_SUCCESS on success.
+ *    - FF_EBUR128_ERROR_INVALID_MODE if mode "FF_EBUR128_MODE_I" has not been set.
+ */
+int ff_ebur128_loudness_global(FFEBUR128State* st, double* out);
+/** \brief Get global integrated loudness in LUFS across multiple instances.
+ *
+ *  @param sts array of library states.
+ *  @param size length of sts
+ *  @param out integrated loudness in LUFS. -HUGE_VAL if result is negative
+ *             infinity.
+ *  @return
+ *    - FF_EBUR128_SUCCESS on success.
+ *    - FF_EBUR128_ERROR_INVALID_MODE if mode "FF_EBUR128_MODE_I" has not been set.
+ */
+int ff_ebur128_loudness_global_multiple(FFEBUR128State** sts,
+                                        size_t size,
+                                        double* out);
+
+/** \brief Get momentary loudness (last 400ms) in LUFS.
+ *
+ *  @param st library state.
+ *  @param out momentary loudness in LUFS. -HUGE_VAL if result is negative
+ *             infinity.
+ *  @return
+ *    - FF_EBUR128_SUCCESS on success.
+ */
+int ff_ebur128_loudness_momentary(FFEBUR128State* st, double* out);
+/** \brief Get short-term loudness (last 3s) in LUFS.
+ *
+ *  @param st library state.
+ *  @param out short-term loudness in LUFS. -HUGE_VAL if result is negative
+ *             infinity.
+ *  @return
+ *    - FF_EBUR128_SUCCESS on success.
+ *    - FF_EBUR128_ERROR_INVALID_MODE if mode "FF_EBUR128_MODE_S" has not been set.
+ */
+int ff_ebur128_loudness_shortterm(FFEBUR128State* st, double* out);
+
+/** \brief Get loudness of the specified window in LUFS.
+ *
+ *  window must not be larger than the current window set in st.
+ *  The current window can be changed by calling ebur128_set_max_window().
+ *
+ *  @param st library state.
+ *  @param window window in ms to calculate loudness.
+ *  @param out loudness in LUFS. -HUGE_VAL if result is negative infinity.
+ *  @return
+ *    - FF_EBUR128_SUCCESS on success.
+ *    - FF_EBUR128_ERROR_INVALID_MODE if window larger than current window in st.
+ */
+int ff_ebur128_loudness_window(FFEBUR128State* st,
+                               unsigned long window,
+                               double* out);
+
+/** \brief Get loudness range (LRA) of programme in LU.
+ *
+ *  Calculates loudness range according to EBU 3342.
+ *
+ *  @param st library state.
+ *  @param out loudness range (LRA) in LU. Will not be changed in case of
+ *             error. FF_EBUR128_ERROR_NOMEM or FF_EBUR128_ERROR_INVALID_MODE will be
+ *             returned in this case.
+ *  @return
+ *    - FF_EBUR128_SUCCESS on success.
+ *    - FF_EBUR128_ERROR_NOMEM in case of memory allocation error.
+ *    - FF_EBUR128_ERROR_INVALID_MODE if mode "FF_EBUR128_MODE_LRA" has not been set.
+ */
+int ff_ebur128_loudness_range(FFEBUR128State* st, double* out);
+/** \brief Get loudness range (LRA) in LU across multiple instances.
+ *
+ *  Calculates loudness range according to EBU 3342.
+ *
+ *  @param sts array of library states.
+ *  @param size length of sts
+ *  @param out loudness range (LRA) in LU. Will not be changed in case of
+ *             error. FF_EBUR128_ERROR_NOMEM or FF_EBUR128_ERROR_INVALID_MODE will be
+ *             returned in this case.
+ *  @return
+ *    - FF_EBUR128_SUCCESS on success.
+ *    - FF_EBUR128_ERROR_NOMEM in case of memory allocation error.
+ *    - FF_EBUR128_ERROR_INVALID_MODE if mode "FF_EBUR128_MODE_LRA" has not been set.
+ */
+int ff_ebur128_loudness_range_multiple(FFEBUR128State** sts,
+                                       size_t size,
+                                       double* out);
+
+/** \brief Get maximum sample peak of selected channel in float format.
+ *
+ *  @param st library state
+ *  @param channel_number channel to analyse
+ *  @param out maximum sample peak in float format (1.0 is 0 dBFS)
+ *  @return
+ *    - FF_EBUR128_SUCCESS on success.
+ *    - FF_EBUR128_ERROR_INVALID_MODE if mode "FF_EBUR128_MODE_SAMPLE_PEAK" has not
+ *      been set.
+ *    - FF_EBUR128_ERROR_INVALID_CHANNEL_INDEX if invalid channel index.
+ */
+int ff_ebur128_sample_peak(FFEBUR128State* st,
+                           unsigned int channel_number,
+                           double* out);
+
+/** \brief Get relative threshold in LUFS.
+ *
+ *  @param st library state
+ *  @param out relative threshold in LUFS.
+ *  @return
+ *    - FF_EBUR128_SUCCESS on success.
+ *    - FF_EBUR128_ERROR_INVALID_MODE if mode "FF_EBUR128_MODE_I" has not
+ *      been set.
+ */
+int ff_ebur128_relative_threshold(FFEBUR128State* st, double* out);
+
+#endif  /* AVFILTER_EBUR128_H */