2013-04-22 15:38:24 +03:00
/*
* Copyright ( c ) 2009 Rob Sykes < robs @ users . sourceforge . net >
* Copyright ( c ) 2013 Paul B Mahol
*
* 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
*/
# include <float.h>
2019-04-22 11:47:04 +02:00
# include <math.h>
2013-04-22 15:38:24 +03:00
# include "libavutil/opt.h"
# include "audio.h"
# include "avfilter.h"
# include "internal.h"
2020-04-16 16:10:12 +02:00
# define HISTOGRAM_SIZE 8192
# define HISTOGRAM_MAX (HISTOGRAM_SIZE-1)
2019-03-01 02:32:00 +02:00
# define MEASURE_ALL UINT_MAX
# define MEASURE_NONE 0
# define MEASURE_DC_OFFSET (1 << 0)
# define MEASURE_MIN_LEVEL (1 << 1)
# define MEASURE_MAX_LEVEL (1 << 2)
# define MEASURE_MIN_DIFFERENCE (1 << 3)
# define MEASURE_MAX_DIFFERENCE (1 << 4)
# define MEASURE_MEAN_DIFFERENCE (1 << 5)
# define MEASURE_RMS_DIFFERENCE (1 << 6)
# define MEASURE_PEAK_LEVEL (1 << 7)
# define MEASURE_RMS_LEVEL (1 << 8)
# define MEASURE_RMS_PEAK (1 << 9)
# define MEASURE_RMS_TROUGH (1 << 10)
# define MEASURE_CREST_FACTOR (1 << 11)
# define MEASURE_FLAT_FACTOR (1 << 12)
# define MEASURE_PEAK_COUNT (1 << 13)
# define MEASURE_BIT_DEPTH (1 << 14)
# define MEASURE_DYNAMIC_RANGE (1 << 15)
# define MEASURE_ZERO_CROSSINGS (1 << 16)
# define MEASURE_ZERO_CROSSINGS_RATE (1 << 17)
# define MEASURE_NUMBER_OF_SAMPLES (1 << 18)
2019-04-22 11:47:04 +02:00
# define MEASURE_NUMBER_OF_NANS (1 << 19)
# define MEASURE_NUMBER_OF_INFS (1 << 20)
# define MEASURE_NUMBER_OF_DENORMALS (1 << 21)
2020-04-16 16:10:12 +02:00
# define MEASURE_NOISE_FLOOR (1 << 22)
2020-04-17 12:56:19 +02:00
# define MEASURE_NOISE_FLOOR_COUNT (1 << 23)
2019-03-01 02:32:00 +02:00
2019-03-02 01:10:21 +02:00
# define MEASURE_MINMAXPEAK (MEASURE_MIN_LEVEL | MEASURE_MAX_LEVEL | MEASURE_PEAK_LEVEL)
2013-04-22 15:38:24 +03:00
typedef struct ChannelStats {
double last ;
2018-09-12 14:54:22 +02:00
double last_non_zero ;
2017-07-19 20:44:14 +02:00
double min_non_zero ;
2013-04-22 15:38:24 +03:00
double sigma_x , sigma_x2 ;
double avg_sigma_x2 , min_sigma_x2 , max_sigma_x2 ;
double min , max ;
2016-08-15 22:01:16 +02:00
double nmin , nmax ;
2013-04-22 15:38:24 +03:00
double min_run , max_run ;
double min_runs , max_runs ;
2015-07-17 11:30:03 +02:00
double min_diff , max_diff ;
double diff1_sum ;
2017-05-12 18:13:17 +02:00
double diff1_sum_x2 ;
2016-08-03 20:27:28 +02:00
uint64_t mask , imask ;
2013-04-22 15:38:24 +03:00
uint64_t min_count , max_count ;
2020-04-17 12:56:19 +02:00
uint64_t noise_floor_count ;
2018-09-12 14:54:22 +02:00
uint64_t zero_runs ;
2013-04-22 15:38:24 +03:00
uint64_t nb_samples ;
2019-04-22 11:47:04 +02:00
uint64_t nb_nans ;
uint64_t nb_infs ;
uint64_t nb_denormals ;
2020-04-16 16:10:12 +02:00
double * win_samples ;
unsigned histogram [ HISTOGRAM_SIZE ] ;
int win_pos ;
int max_index ;
double noise_floor ;
2013-04-22 15:38:24 +03:00
} ChannelStats ;
2017-05-12 20:00:49 +02:00
typedef struct AudioStatsContext {
2013-04-22 15:38:24 +03:00
const AVClass * class ;
ChannelStats * chstats ;
int nb_channels ;
uint64_t tc_samples ;
double time_constant ;
double mult ;
2015-06-29 23:14:53 +02:00
int metadata ;
2015-07-01 10:03:19 +02:00
int reset_count ;
int nb_frames ;
2016-08-15 22:01:16 +02:00
int maxbitdepth ;
2019-03-01 02:32:00 +02:00
int measure_perchannel ;
int measure_overall ;
2019-04-22 11:47:04 +02:00
int is_float ;
int is_double ;
2013-04-22 15:38:24 +03:00
} AudioStatsContext ;
# define OFFSET(x) offsetof(AudioStatsContext, x)
# define FLAGS AV_OPT_FLAG_AUDIO_PARAM|AV_OPT_FLAG_FILTERING_PARAM
static const AVOption astats_options [ ] = {
{ " length " , " set the window length " , OFFSET ( time_constant ) , AV_OPT_TYPE_DOUBLE , { . dbl = .05 } , .01 , 10 , FLAGS } ,
2015-09-08 23:08:20 +02:00
{ " metadata " , " inject metadata in the filtergraph " , OFFSET ( metadata ) , AV_OPT_TYPE_BOOL , { . i64 = 0 } , 0 , 1 , FLAGS } ,
2015-07-01 10:03:19 +02:00
{ " reset " , " recalculate stats after this many frames " , OFFSET ( reset_count ) , AV_OPT_TYPE_INT , { . i64 = 0 } , 0 , INT_MAX , FLAGS } ,
2019-03-01 02:32:00 +02:00
{ " measure_perchannel " , " only measure_perchannel these per-channel statistics " , OFFSET ( measure_perchannel ) , AV_OPT_TYPE_FLAGS , { . i64 = MEASURE_ALL } , 0 , UINT_MAX , FLAGS , " measure " } ,
{ " none " , " " , 0 , AV_OPT_TYPE_CONST , { . i64 = MEASURE_NONE } , 0 , 0 , FLAGS , " measure " } ,
{ " all " , " " , 0 , AV_OPT_TYPE_CONST , { . i64 = MEASURE_ALL } , 0 , 0 , FLAGS , " measure " } ,
{ " DC_offset " , " " , 0 , AV_OPT_TYPE_CONST , { . i64 = MEASURE_DC_OFFSET } , 0 , 0 , FLAGS , " measure " } ,
{ " Min_level " , " " , 0 , AV_OPT_TYPE_CONST , { . i64 = MEASURE_MIN_LEVEL } , 0 , 0 , FLAGS , " measure " } ,
{ " Max_level " , " " , 0 , AV_OPT_TYPE_CONST , { . i64 = MEASURE_MAX_LEVEL } , 0 , 0 , FLAGS , " measure " } ,
{ " Min_difference " , " " , 0 , AV_OPT_TYPE_CONST , { . i64 = MEASURE_MIN_DIFFERENCE } , 0 , 0 , FLAGS , " measure " } ,
{ " Max_difference " , " " , 0 , AV_OPT_TYPE_CONST , { . i64 = MEASURE_MAX_DIFFERENCE } , 0 , 0 , FLAGS , " measure " } ,
{ " Mean_difference " , " " , 0 , AV_OPT_TYPE_CONST , { . i64 = MEASURE_MEAN_DIFFERENCE } , 0 , 0 , FLAGS , " measure " } ,
{ " RMS_difference " , " " , 0 , AV_OPT_TYPE_CONST , { . i64 = MEASURE_RMS_DIFFERENCE } , 0 , 0 , FLAGS , " measure " } ,
{ " Peak_level " , " " , 0 , AV_OPT_TYPE_CONST , { . i64 = MEASURE_PEAK_LEVEL } , 0 , 0 , FLAGS , " measure " } ,
{ " RMS_level " , " " , 0 , AV_OPT_TYPE_CONST , { . i64 = MEASURE_RMS_LEVEL } , 0 , 0 , FLAGS , " measure " } ,
{ " RMS_peak " , " " , 0 , AV_OPT_TYPE_CONST , { . i64 = MEASURE_RMS_PEAK } , 0 , 0 , FLAGS , " measure " } ,
{ " RMS_trough " , " " , 0 , AV_OPT_TYPE_CONST , { . i64 = MEASURE_RMS_TROUGH } , 0 , 0 , FLAGS , " measure " } ,
{ " Crest_factor " , " " , 0 , AV_OPT_TYPE_CONST , { . i64 = MEASURE_CREST_FACTOR } , 0 , 0 , FLAGS , " measure " } ,
{ " Flat_factor " , " " , 0 , AV_OPT_TYPE_CONST , { . i64 = MEASURE_FLAT_FACTOR } , 0 , 0 , FLAGS , " measure " } ,
{ " Peak_count " , " " , 0 , AV_OPT_TYPE_CONST , { . i64 = MEASURE_PEAK_COUNT } , 0 , 0 , FLAGS , " measure " } ,
{ " Bit_depth " , " " , 0 , AV_OPT_TYPE_CONST , { . i64 = MEASURE_BIT_DEPTH } , 0 , 0 , FLAGS , " measure " } ,
{ " Dynamic_range " , " " , 0 , AV_OPT_TYPE_CONST , { . i64 = MEASURE_DYNAMIC_RANGE } , 0 , 0 , FLAGS , " measure " } ,
{ " Zero_crossings " , " " , 0 , AV_OPT_TYPE_CONST , { . i64 = MEASURE_ZERO_CROSSINGS } , 0 , 0 , FLAGS , " measure " } ,
{ " Zero_crossings_rate " , " " , 0 , AV_OPT_TYPE_CONST , { . i64 = MEASURE_ZERO_CROSSINGS_RATE } , 0 , 0 , FLAGS , " measure " } ,
2020-04-16 16:10:12 +02:00
{ " Noise_floor " , " " , 0 , AV_OPT_TYPE_CONST , { . i64 = MEASURE_NOISE_FLOOR } , 0 , 0 , FLAGS , " measure " } ,
2020-04-17 12:56:19 +02:00
{ " Noise_floor_count " , " " , 0 , AV_OPT_TYPE_CONST , { . i64 = MEASURE_NOISE_FLOOR_COUNT } , 0 , 0 , FLAGS , " measure " } ,
2019-03-01 02:32:00 +02:00
{ " Number_of_samples " , " " , 0 , AV_OPT_TYPE_CONST , { . i64 = MEASURE_NUMBER_OF_SAMPLES } , 0 , 0 , FLAGS , " measure " } ,
2019-04-22 11:47:04 +02:00
{ " Number_of_NaNs " , " " , 0 , AV_OPT_TYPE_CONST , { . i64 = MEASURE_NUMBER_OF_NANS } , 0 , 0 , FLAGS , " measure " } ,
{ " Number_of_Infs " , " " , 0 , AV_OPT_TYPE_CONST , { . i64 = MEASURE_NUMBER_OF_INFS } , 0 , 0 , FLAGS , " measure " } ,
{ " Number_of_denormals " , " " , 0 , AV_OPT_TYPE_CONST , { . i64 = MEASURE_NUMBER_OF_DENORMALS } , 0 , 0 , FLAGS , " measure " } ,
2019-03-01 02:32:00 +02:00
{ " measure_overall " , " only measure_perchannel these overall statistics " , OFFSET ( measure_overall ) , AV_OPT_TYPE_FLAGS , { . i64 = MEASURE_ALL } , 0 , UINT_MAX , FLAGS , " measure " } ,
2013-09-07 15:13:50 +03:00
{ NULL }
2013-04-22 15:38:24 +03:00
} ;
AVFILTER_DEFINE_CLASS ( astats ) ;
static int query_formats ( AVFilterContext * ctx )
{
AVFilterFormats * formats ;
AVFilterChannelLayouts * layouts ;
static const enum AVSampleFormat sample_fmts [ ] = {
2016-08-15 22:01:16 +02:00
AV_SAMPLE_FMT_S16 , AV_SAMPLE_FMT_S16P ,
AV_SAMPLE_FMT_S32 , AV_SAMPLE_FMT_S32P ,
2016-08-18 11:02:31 +02:00
AV_SAMPLE_FMT_S64 , AV_SAMPLE_FMT_S64P ,
2016-08-15 22:01:16 +02:00
AV_SAMPLE_FMT_FLT , AV_SAMPLE_FMT_FLTP ,
2013-04-22 15:38:24 +03:00
AV_SAMPLE_FMT_DBL , AV_SAMPLE_FMT_DBLP ,
AV_SAMPLE_FMT_NONE
} ;
2015-04-03 19:55:18 +02:00
int ret ;
2013-04-22 15:38:24 +03:00
2015-09-11 20:51:10 +02:00
layouts = ff_all_channel_counts ( ) ;
2013-04-22 15:38:24 +03:00
if ( ! layouts )
return AVERROR ( ENOMEM ) ;
2015-04-03 19:55:18 +02:00
ret = ff_set_common_channel_layouts ( ctx , layouts ) ;
if ( ret < 0 )
return ret ;
2013-04-22 15:38:24 +03:00
formats = ff_make_format_list ( sample_fmts ) ;
if ( ! formats )
return AVERROR ( ENOMEM ) ;
2015-04-03 19:55:18 +02:00
ret = ff_set_common_formats ( ctx , formats ) ;
if ( ret < 0 )
return ret ;
2013-04-22 15:38:24 +03:00
formats = ff_all_samplerates ( ) ;
if ( ! formats )
return AVERROR ( ENOMEM ) ;
2015-04-03 19:55:18 +02:00
return ff_set_common_samplerates ( ctx , formats ) ;
2013-04-22 15:38:24 +03:00
}
2015-07-01 10:03:19 +02:00
static void reset_stats ( AudioStatsContext * s )
{
int c ;
for ( c = 0 ; c < s - > nb_channels ; c + + ) {
ChannelStats * p = & s - > chstats [ c ] ;
2016-08-15 22:01:16 +02:00
p - > min = p - > nmin = p - > min_sigma_x2 = DBL_MAX ;
2019-05-12 16:56:00 +02:00
p - > max = p - > nmax = p - > max_sigma_x2 = - DBL_MAX ;
2017-07-19 20:44:14 +02:00
p - > min_non_zero = DBL_MAX ;
2016-02-26 11:35:19 +02:00
p - > min_diff = DBL_MAX ;
2019-05-12 16:56:00 +02:00
p - > max_diff = 0 ;
2016-02-26 11:35:19 +02:00
p - > sigma_x = 0 ;
p - > sigma_x2 = 0 ;
p - > avg_sigma_x2 = 0 ;
p - > min_run = 0 ;
p - > max_run = 0 ;
p - > min_runs = 0 ;
p - > max_runs = 0 ;
p - > diff1_sum = 0 ;
2017-05-12 18:13:17 +02:00
p - > diff1_sum_x2 = 0 ;
2016-02-26 11:35:19 +02:00
p - > mask = 0 ;
2016-08-03 20:27:28 +02:00
p - > imask = 0xFFFFFFFFFFFFFFFF ;
2016-02-26 11:35:19 +02:00
p - > min_count = 0 ;
p - > max_count = 0 ;
2018-09-12 14:54:22 +02:00
p - > zero_runs = 0 ;
2016-02-26 11:35:19 +02:00
p - > nb_samples = 0 ;
2019-04-22 11:47:04 +02:00
p - > nb_nans = 0 ;
p - > nb_infs = 0 ;
p - > nb_denormals = 0 ;
2019-05-12 17:10:40 +02:00
p - > last = NAN ;
2020-04-16 16:10:12 +02:00
p - > noise_floor = NAN ;
2020-04-17 12:56:19 +02:00
p - > noise_floor_count = 0 ;
2020-04-16 16:10:12 +02:00
p - > win_pos = 0 ;
memset ( p - > win_samples , 0 , s - > tc_samples * sizeof ( * p - > win_samples ) ) ;
memset ( p - > histogram , 0 , sizeof ( p - > histogram ) ) ;
2015-07-01 10:03:19 +02:00
}
}
2013-04-22 15:38:24 +03:00
static int config_output ( AVFilterLink * outlink )
{
AudioStatsContext * s = outlink - > src - > priv ;
s - > chstats = av_calloc ( sizeof ( * s - > chstats ) , outlink - > channels ) ;
if ( ! s - > chstats )
return AVERROR ( ENOMEM ) ;
2020-04-16 16:10:12 +02:00
s - > tc_samples = 5 * s - > time_constant * outlink - > sample_rate + .5 ;
2013-04-22 15:38:24 +03:00
s - > nb_channels = outlink - > channels ;
2020-04-16 16:10:12 +02:00
for ( int i = 0 ; i < s - > nb_channels ; i + + ) {
ChannelStats * p = & s - > chstats [ i ] ;
p - > win_samples = av_calloc ( s - > tc_samples , sizeof ( * p - > win_samples ) ) ;
if ( ! p - > win_samples )
return AVERROR ( ENOMEM ) ;
}
2013-04-22 15:38:24 +03:00
s - > mult = exp ( ( - 1 / s - > time_constant / outlink - > sample_rate ) ) ;
2016-02-24 23:10:32 +02:00
s - > nb_frames = 0 ;
2016-08-15 22:01:16 +02:00
s - > maxbitdepth = av_get_bytes_per_sample ( outlink - > format ) * 8 ;
2019-04-22 11:47:04 +02:00
s - > is_double = outlink - > format = = AV_SAMPLE_FMT_DBL | |
outlink - > format = = AV_SAMPLE_FMT_DBLP ;
s - > is_float = outlink - > format = = AV_SAMPLE_FMT_FLT | |
outlink - > format = = AV_SAMPLE_FMT_FLTP ;
2013-04-22 15:38:24 +03:00
2015-07-01 10:03:19 +02:00
reset_stats ( s ) ;
2013-04-22 15:38:24 +03:00
return 0 ;
}
2016-08-15 22:01:16 +02:00
static void bit_depth ( AudioStatsContext * s , uint64_t mask , uint64_t imask , AVRational * depth )
2015-07-13 10:49:26 +02:00
{
2016-08-15 22:01:16 +02:00
unsigned result = s - > maxbitdepth ;
2015-07-13 10:49:26 +02:00
2016-08-03 20:27:28 +02:00
mask = mask & ( ~ imask ) ;
2015-07-13 10:49:26 +02:00
for ( ; result & & ! ( mask & 1 ) ; - - result , mask > > = 1 ) ;
2016-08-03 20:27:28 +02:00
depth - > den = result ;
depth - > num = 0 ;
for ( ; result ; - - result , mask > > = 1 )
if ( mask & 1 )
depth - > num + + ;
2015-07-13 10:49:26 +02:00
}
2019-03-02 01:10:21 +02:00
static inline void update_minmax ( AudioStatsContext * s , ChannelStats * p , double d )
{
if ( d < p - > min )
p - > min = d ;
if ( d > p - > max )
p - > max = d ;
}
2016-08-15 22:01:16 +02:00
static inline void update_stat ( AudioStatsContext * s , ChannelStats * p , double d , double nd , int64_t i )
2013-04-22 15:38:24 +03:00
{
2020-04-16 16:10:12 +02:00
double drop ;
int index ;
2013-04-22 15:38:24 +03:00
if ( d < p - > min ) {
p - > min = d ;
2016-08-15 22:01:16 +02:00
p - > nmin = nd ;
2013-04-22 15:38:24 +03:00
p - > min_run = 1 ;
p - > min_runs = 0 ;
p - > min_count = 1 ;
} else if ( d = = p - > min ) {
p - > min_count + + ;
p - > min_run = d = = p - > last ? p - > min_run + 1 : 1 ;
} else if ( p - > last = = p - > min ) {
p - > min_runs + = p - > min_run * p - > min_run ;
}
2017-07-19 20:44:14 +02:00
if ( d ! = 0 & & FFABS ( d ) < p - > min_non_zero )
p - > min_non_zero = FFABS ( d ) ;
2013-04-22 15:38:24 +03:00
if ( d > p - > max ) {
p - > max = d ;
2016-08-15 22:01:16 +02:00
p - > nmax = nd ;
2013-04-22 15:38:24 +03:00
p - > max_run = 1 ;
p - > max_runs = 0 ;
p - > max_count = 1 ;
} else if ( d = = p - > max ) {
p - > max_count + + ;
p - > max_run = d = = p - > last ? p - > max_run + 1 : 1 ;
} else if ( p - > last = = p - > max ) {
p - > max_runs + = p - > max_run * p - > max_run ;
}
2018-09-12 14:54:22 +02:00
if ( d ! = 0 ) {
p - > zero_runs + = FFSIGN ( d ) ! = FFSIGN ( p - > last_non_zero ) ;
p - > last_non_zero = d ;
}
2016-08-15 22:01:16 +02:00
p - > sigma_x + = nd ;
p - > sigma_x2 + = nd * nd ;
p - > avg_sigma_x2 = p - > avg_sigma_x2 * s - > mult + ( 1.0 - s - > mult ) * nd * nd ;
2019-05-12 17:10:40 +02:00
if ( ! isnan ( p - > last ) ) {
p - > min_diff = FFMIN ( p - > min_diff , fabs ( d - p - > last ) ) ;
p - > max_diff = FFMAX ( p - > max_diff , fabs ( d - p - > last ) ) ;
p - > diff1_sum + = fabs ( d - p - > last ) ;
p - > diff1_sum_x2 + = ( d - p - > last ) * ( d - p - > last ) ;
}
2013-04-22 15:38:24 +03:00
p - > last = d ;
2016-08-15 22:01:16 +02:00
p - > mask | = i ;
p - > imask & = i ;
2013-04-22 15:38:24 +03:00
2020-04-16 16:10:12 +02:00
drop = p - > win_samples [ p - > win_pos ] ;
p - > win_samples [ p - > win_pos ] = nd ;
2021-01-11 01:12:39 +02:00
index = av_clip ( lrint ( av_clipd ( FFABS ( nd ) , 0.0 , 1.0 ) * HISTOGRAM_MAX ) , 0 , HISTOGRAM_MAX ) ;
2020-04-16 16:10:12 +02:00
p - > max_index = FFMAX ( p - > max_index , index ) ;
p - > histogram [ index ] + + ;
if ( ! isnan ( p - > noise_floor ) )
2021-01-11 01:12:39 +02:00
p - > histogram [ av_clip ( lrint ( av_clipd ( FFABS ( drop ) , 0.0 , 1.0 ) * HISTOGRAM_MAX ) , 0 , HISTOGRAM_MAX ) ] - - ;
2020-04-16 16:10:12 +02:00
p - > win_pos + + ;
while ( p - > histogram [ p - > max_index ] = = 0 )
p - > max_index - - ;
if ( p - > win_pos > = s - > tc_samples | | ! isnan ( p - > noise_floor ) ) {
double noise_floor = 1. ;
for ( int i = p - > max_index ; i > = 0 ; i - - ) {
if ( p - > histogram [ i ] ) {
noise_floor = i / ( double ) HISTOGRAM_MAX ;
break ;
}
}
if ( isnan ( p - > noise_floor ) ) {
p - > noise_floor = noise_floor ;
2020-04-17 12:56:19 +02:00
p - > noise_floor_count = 1 ;
2020-04-16 16:10:12 +02:00
} else {
2020-04-17 12:56:19 +02:00
if ( noise_floor < p - > noise_floor ) {
p - > noise_floor = noise_floor ;
p - > noise_floor_count = 1 ;
} else if ( noise_floor = = p - > noise_floor ) {
p - > noise_floor_count + + ;
}
2020-04-16 16:10:12 +02:00
}
}
if ( p - > win_pos > = s - > tc_samples ) {
p - > win_pos = 0 ;
}
2013-04-22 15:38:24 +03:00
if ( p - > nb_samples > = s - > tc_samples ) {
p - > max_sigma_x2 = FFMAX ( p - > max_sigma_x2 , p - > avg_sigma_x2 ) ;
p - > min_sigma_x2 = FFMIN ( p - > min_sigma_x2 , p - > avg_sigma_x2 ) ;
}
p - > nb_samples + + ;
}
2019-04-22 11:47:04 +02:00
static inline void update_float_stat ( AudioStatsContext * s , ChannelStats * p , float d )
{
int type = fpclassify ( d ) ;
p - > nb_nans + = type = = FP_NAN ;
p - > nb_infs + = type = = FP_INFINITE ;
p - > nb_denormals + = type = = FP_SUBNORMAL ;
}
static inline void update_double_stat ( AudioStatsContext * s , ChannelStats * p , double d )
{
int type = fpclassify ( d ) ;
p - > nb_nans + = type = = FP_NAN ;
p - > nb_infs + = type = = FP_INFINITE ;
p - > nb_denormals + = type = = FP_SUBNORMAL ;
}
2015-06-29 23:14:53 +02:00
static void set_meta ( AVDictionary * * metadata , int chan , const char * key ,
const char * fmt , double val )
{
uint8_t value [ 128 ] ;
uint8_t key2 [ 128 ] ;
snprintf ( value , sizeof ( value ) , fmt , val ) ;
if ( chan )
snprintf ( key2 , sizeof ( key2 ) , " lavfi.astats.%d.%s " , chan , key ) ;
else
snprintf ( key2 , sizeof ( key2 ) , " lavfi.astats.%s " , key ) ;
av_dict_set ( metadata , key2 , value , 0 ) ;
}
# define LINEAR_TO_DB(x) (log10(x) * 20)
static void set_metadata ( AudioStatsContext * s , AVDictionary * * metadata )
{
2020-04-17 12:56:19 +02:00
uint64_t mask = 0 , imask = 0xFFFFFFFFFFFFFFFF , min_count = 0 , max_count = 0 , nb_samples = 0 , noise_floor_count = 0 ;
2019-04-22 11:47:04 +02:00
uint64_t nb_nans = 0 , nb_infs = 0 , nb_denormals = 0 ;
2015-06-29 23:14:53 +02:00
double min_runs = 0 , max_runs = 0 ,
2019-05-12 16:56:00 +02:00
min = DBL_MAX , max = - DBL_MAX , min_diff = DBL_MAX , max_diff = 0 ,
nmin = DBL_MAX , nmax = - DBL_MAX ,
2015-06-29 23:14:53 +02:00
max_sigma_x = 0 ,
2015-07-17 11:30:03 +02:00
diff1_sum = 0 ,
2017-05-12 18:13:17 +02:00
diff1_sum_x2 = 0 ,
2015-06-29 23:14:53 +02:00
sigma_x = 0 ,
sigma_x2 = 0 ,
2020-04-16 16:10:12 +02:00
noise_floor = 0 ,
2015-06-29 23:14:53 +02:00
min_sigma_x2 = DBL_MAX ,
2019-05-12 16:56:00 +02:00
max_sigma_x2 = - DBL_MAX ;
2016-08-03 20:27:28 +02:00
AVRational depth ;
2015-06-29 23:14:53 +02:00
int c ;
for ( c = 0 ; c < s - > nb_channels ; c + + ) {
ChannelStats * p = & s - > chstats [ c ] ;
if ( p - > nb_samples < s - > tc_samples )
p - > min_sigma_x2 = p - > max_sigma_x2 = p - > sigma_x2 / p - > nb_samples ;
min = FFMIN ( min , p - > min ) ;
max = FFMAX ( max , p - > max ) ;
2016-08-15 22:01:16 +02:00
nmin = FFMIN ( nmin , p - > nmin ) ;
nmax = FFMAX ( nmax , p - > nmax ) ;
2015-07-17 11:30:03 +02:00
min_diff = FFMIN ( min_diff , p - > min_diff ) ;
2015-07-15 21:00:25 +02:00
max_diff = FFMAX ( max_diff , p - > max_diff ) ;
2017-05-12 18:13:17 +02:00
diff1_sum + = p - > diff1_sum ;
diff1_sum_x2 + = p - > diff1_sum_x2 ;
2015-06-29 23:14:53 +02:00
min_sigma_x2 = FFMIN ( min_sigma_x2 , p - > min_sigma_x2 ) ;
max_sigma_x2 = FFMAX ( max_sigma_x2 , p - > max_sigma_x2 ) ;
sigma_x + = p - > sigma_x ;
sigma_x2 + = p - > sigma_x2 ;
2020-04-16 16:10:12 +02:00
noise_floor = FFMAX ( noise_floor , p - > noise_floor ) ;
2020-04-17 12:56:19 +02:00
noise_floor_count + = p - > noise_floor_count ;
2015-06-29 23:14:53 +02:00
min_count + = p - > min_count ;
max_count + = p - > max_count ;
min_runs + = p - > min_runs ;
max_runs + = p - > max_runs ;
2015-07-13 10:49:26 +02:00
mask | = p - > mask ;
2016-08-03 20:27:28 +02:00
imask & = p - > imask ;
2015-06-29 23:14:53 +02:00
nb_samples + = p - > nb_samples ;
2019-04-22 11:47:04 +02:00
nb_nans + = p - > nb_nans ;
nb_infs + = p - > nb_infs ;
nb_denormals + = p - > nb_denormals ;
2015-06-29 23:14:53 +02:00
if ( fabs ( p - > sigma_x ) > fabs ( max_sigma_x ) )
max_sigma_x = p - > sigma_x ;
2019-03-01 02:32:00 +02:00
if ( s - > measure_perchannel & MEASURE_DC_OFFSET )
2019-03-01 22:51:34 +02:00
set_meta ( metadata , c + 1 , " DC_offset " , " %f " , p - > sigma_x / p - > nb_samples ) ;
2019-03-01 02:32:00 +02:00
if ( s - > measure_perchannel & MEASURE_MIN_LEVEL )
2019-03-01 22:51:34 +02:00
set_meta ( metadata , c + 1 , " Min_level " , " %f " , p - > min ) ;
2019-03-01 02:32:00 +02:00
if ( s - > measure_perchannel & MEASURE_MAX_LEVEL )
2019-03-01 22:51:34 +02:00
set_meta ( metadata , c + 1 , " Max_level " , " %f " , p - > max ) ;
2019-03-01 02:32:00 +02:00
if ( s - > measure_perchannel & MEASURE_MIN_DIFFERENCE )
2019-03-01 22:51:34 +02:00
set_meta ( metadata , c + 1 , " Min_difference " , " %f " , p - > min_diff ) ;
2019-03-01 02:32:00 +02:00
if ( s - > measure_perchannel & MEASURE_MAX_DIFFERENCE )
2019-03-01 22:51:34 +02:00
set_meta ( metadata , c + 1 , " Max_difference " , " %f " , p - > max_diff ) ;
2019-03-01 02:32:00 +02:00
if ( s - > measure_perchannel & MEASURE_MEAN_DIFFERENCE )
2019-03-01 22:51:34 +02:00
set_meta ( metadata , c + 1 , " Mean_difference " , " %f " , p - > diff1_sum / ( p - > nb_samples - 1 ) ) ;
2019-03-01 02:32:00 +02:00
if ( s - > measure_perchannel & MEASURE_RMS_DIFFERENCE )
2019-03-01 22:51:34 +02:00
set_meta ( metadata , c + 1 , " RMS_difference " , " %f " , sqrt ( p - > diff1_sum_x2 / ( p - > nb_samples - 1 ) ) ) ;
2019-03-01 02:32:00 +02:00
if ( s - > measure_perchannel & MEASURE_PEAK_LEVEL )
2019-03-01 22:51:34 +02:00
set_meta ( metadata , c + 1 , " Peak_level " , " %f " , LINEAR_TO_DB ( FFMAX ( - p - > nmin , p - > nmax ) ) ) ;
2019-03-01 02:32:00 +02:00
if ( s - > measure_perchannel & MEASURE_RMS_LEVEL )
2019-03-01 22:51:34 +02:00
set_meta ( metadata , c + 1 , " RMS_level " , " %f " , LINEAR_TO_DB ( sqrt ( p - > sigma_x2 / p - > nb_samples ) ) ) ;
2019-03-01 02:32:00 +02:00
if ( s - > measure_perchannel & MEASURE_RMS_PEAK )
2019-03-01 22:51:34 +02:00
set_meta ( metadata , c + 1 , " RMS_peak " , " %f " , LINEAR_TO_DB ( sqrt ( p - > max_sigma_x2 ) ) ) ;
2019-03-01 02:32:00 +02:00
if ( s - > measure_perchannel & MEASURE_RMS_TROUGH )
2019-03-01 22:51:34 +02:00
set_meta ( metadata , c + 1 , " RMS_trough " , " %f " , LINEAR_TO_DB ( sqrt ( p - > min_sigma_x2 ) ) ) ;
2019-03-01 02:32:00 +02:00
if ( s - > measure_perchannel & MEASURE_CREST_FACTOR )
2019-03-01 22:51:34 +02:00
set_meta ( metadata , c + 1 , " Crest_factor " , " %f " , p - > sigma_x2 ? FFMAX ( - p - > min , p - > max ) / sqrt ( p - > sigma_x2 / p - > nb_samples ) : 1 ) ;
2019-03-01 02:32:00 +02:00
if ( s - > measure_perchannel & MEASURE_FLAT_FACTOR )
2019-03-01 22:51:34 +02:00
set_meta ( metadata , c + 1 , " Flat_factor " , " %f " , LINEAR_TO_DB ( ( p - > min_runs + p - > max_runs ) / ( p - > min_count + p - > max_count ) ) ) ;
2019-03-01 02:32:00 +02:00
if ( s - > measure_perchannel & MEASURE_PEAK_COUNT )
2019-03-01 22:51:34 +02:00
set_meta ( metadata , c + 1 , " Peak_count " , " %f " , ( float ) ( p - > min_count + p - > max_count ) ) ;
2020-04-16 16:10:12 +02:00
if ( s - > measure_perchannel & MEASURE_NOISE_FLOOR )
2020-04-17 12:56:19 +02:00
set_meta ( metadata , c + 1 , " Noise_floor " , " %f " , LINEAR_TO_DB ( p - > noise_floor ) ) ;
if ( s - > measure_perchannel & MEASURE_NOISE_FLOOR_COUNT )
set_meta ( metadata , c + 1 , " Noise_floor_count " , " %f " , p - > noise_floor_count ) ;
2019-03-01 02:32:00 +02:00
if ( s - > measure_perchannel & MEASURE_BIT_DEPTH ) {
2019-03-01 22:51:34 +02:00
bit_depth ( s , p - > mask , p - > imask , & depth ) ;
set_meta ( metadata , c + 1 , " Bit_depth " , " %f " , depth . num ) ;
set_meta ( metadata , c + 1 , " Bit_depth2 " , " %f " , depth . den ) ;
2019-03-01 02:32:00 +02:00
}
if ( s - > measure_perchannel & MEASURE_DYNAMIC_RANGE )
2019-03-01 22:51:34 +02:00
set_meta ( metadata , c + 1 , " Dynamic_range " , " %f " , LINEAR_TO_DB ( 2 * FFMAX ( FFABS ( p - > min ) , FFABS ( p - > max ) ) / p - > min_non_zero ) ) ;
2019-03-01 02:32:00 +02:00
if ( s - > measure_perchannel & MEASURE_ZERO_CROSSINGS )
2019-03-01 22:51:34 +02:00
set_meta ( metadata , c + 1 , " Zero_crossings " , " %f " , p - > zero_runs ) ;
2019-03-01 02:32:00 +02:00
if ( s - > measure_perchannel & MEASURE_ZERO_CROSSINGS_RATE )
2019-03-01 22:51:34 +02:00
set_meta ( metadata , c + 1 , " Zero_crossings_rate " , " %f " , p - > zero_runs / ( double ) p - > nb_samples ) ;
2019-04-22 11:47:04 +02:00
if ( ( s - > is_float | | s - > is_double ) & & s - > measure_perchannel & MEASURE_NUMBER_OF_NANS )
set_meta ( metadata , c + 1 , " Number of NaNs " , " %f " , p - > nb_nans ) ;
if ( ( s - > is_float | | s - > is_double ) & & s - > measure_perchannel & MEASURE_NUMBER_OF_INFS )
set_meta ( metadata , c + 1 , " Number of Infs " , " %f " , p - > nb_infs ) ;
if ( ( s - > is_float | | s - > is_double ) & & s - > measure_perchannel & MEASURE_NUMBER_OF_DENORMALS )
set_meta ( metadata , c + 1 , " Number of denormals " , " %f " , p - > nb_denormals ) ;
2015-06-29 23:14:53 +02:00
}
2019-03-01 02:32:00 +02:00
if ( s - > measure_overall & MEASURE_DC_OFFSET )
2019-03-01 22:51:34 +02:00
set_meta ( metadata , 0 , " Overall.DC_offset " , " %f " , max_sigma_x / ( nb_samples / s - > nb_channels ) ) ;
2019-03-01 02:32:00 +02:00
if ( s - > measure_overall & MEASURE_MIN_LEVEL )
2019-03-01 22:51:34 +02:00
set_meta ( metadata , 0 , " Overall.Min_level " , " %f " , min ) ;
2019-03-01 02:32:00 +02:00
if ( s - > measure_overall & MEASURE_MAX_LEVEL )
2019-03-01 22:51:34 +02:00
set_meta ( metadata , 0 , " Overall.Max_level " , " %f " , max ) ;
2019-03-01 02:32:00 +02:00
if ( s - > measure_overall & MEASURE_MIN_DIFFERENCE )
2019-03-01 22:51:34 +02:00
set_meta ( metadata , 0 , " Overall.Min_difference " , " %f " , min_diff ) ;
2019-03-01 02:32:00 +02:00
if ( s - > measure_overall & MEASURE_MAX_DIFFERENCE )
2019-03-01 22:51:34 +02:00
set_meta ( metadata , 0 , " Overall.Max_difference " , " %f " , max_diff ) ;
2019-03-01 02:32:00 +02:00
if ( s - > measure_overall & MEASURE_MEAN_DIFFERENCE )
2019-03-01 22:51:34 +02:00
set_meta ( metadata , 0 , " Overall.Mean_difference " , " %f " , diff1_sum / ( nb_samples - s - > nb_channels ) ) ;
2019-03-01 02:32:00 +02:00
if ( s - > measure_overall & MEASURE_RMS_DIFFERENCE )
2019-03-01 22:51:34 +02:00
set_meta ( metadata , 0 , " Overall.RMS_difference " , " %f " , sqrt ( diff1_sum_x2 / ( nb_samples - s - > nb_channels ) ) ) ;
2019-03-01 02:32:00 +02:00
if ( s - > measure_overall & MEASURE_PEAK_LEVEL )
2019-03-01 22:51:34 +02:00
set_meta ( metadata , 0 , " Overall.Peak_level " , " %f " , LINEAR_TO_DB ( FFMAX ( - nmin , nmax ) ) ) ;
2019-03-01 02:32:00 +02:00
if ( s - > measure_overall & MEASURE_RMS_LEVEL )
2019-03-01 22:51:34 +02:00
set_meta ( metadata , 0 , " Overall.RMS_level " , " %f " , LINEAR_TO_DB ( sqrt ( sigma_x2 / nb_samples ) ) ) ;
2019-03-01 02:32:00 +02:00
if ( s - > measure_overall & MEASURE_RMS_PEAK )
2019-03-01 22:51:34 +02:00
set_meta ( metadata , 0 , " Overall.RMS_peak " , " %f " , LINEAR_TO_DB ( sqrt ( max_sigma_x2 ) ) ) ;
2019-03-01 02:32:00 +02:00
if ( s - > measure_overall & MEASURE_RMS_TROUGH )
2019-03-01 22:51:34 +02:00
set_meta ( metadata , 0 , " Overall.RMS_trough " , " %f " , LINEAR_TO_DB ( sqrt ( min_sigma_x2 ) ) ) ;
2019-03-01 02:32:00 +02:00
if ( s - > measure_overall & MEASURE_FLAT_FACTOR )
2019-03-01 22:51:34 +02:00
set_meta ( metadata , 0 , " Overall.Flat_factor " , " %f " , LINEAR_TO_DB ( ( min_runs + max_runs ) / ( min_count + max_count ) ) ) ;
2019-03-01 02:32:00 +02:00
if ( s - > measure_overall & MEASURE_PEAK_COUNT )
2019-03-01 22:51:34 +02:00
set_meta ( metadata , 0 , " Overall.Peak_count " , " %f " , ( float ) ( min_count + max_count ) / ( double ) s - > nb_channels ) ;
2020-04-16 16:10:12 +02:00
if ( s - > measure_overall & MEASURE_NOISE_FLOOR )
set_meta ( metadata , 0 , " Overall.Noise_floor " , " %f " , LINEAR_TO_DB ( noise_floor ) ) ;
2020-04-17 12:56:19 +02:00
if ( s - > measure_overall & MEASURE_NOISE_FLOOR_COUNT )
set_meta ( metadata , 0 , " Overall.Noise_floor_count " , " %f " , noise_floor_count / ( double ) s - > nb_channels ) ;
2019-03-01 02:32:00 +02:00
if ( s - > measure_overall & MEASURE_BIT_DEPTH ) {
2019-03-01 22:51:34 +02:00
bit_depth ( s , mask , imask , & depth ) ;
set_meta ( metadata , 0 , " Overall.Bit_depth " , " %f " , depth . num ) ;
set_meta ( metadata , 0 , " Overall.Bit_depth2 " , " %f " , depth . den ) ;
2019-03-01 02:32:00 +02:00
}
if ( s - > measure_overall & MEASURE_NUMBER_OF_SAMPLES )
2019-03-01 22:51:34 +02:00
set_meta ( metadata , 0 , " Overall.Number_of_samples " , " %f " , nb_samples / s - > nb_channels ) ;
2019-04-22 11:47:04 +02:00
if ( ( s - > is_float | | s - > is_double ) & & s - > measure_overall & MEASURE_NUMBER_OF_NANS )
set_meta ( metadata , 0 , " Number of NaNs " , " %f " , nb_nans / ( float ) s - > nb_channels ) ;
if ( ( s - > is_float | | s - > is_double ) & & s - > measure_overall & MEASURE_NUMBER_OF_INFS )
set_meta ( metadata , 0 , " Number of Infs " , " %f " , nb_infs / ( float ) s - > nb_channels ) ;
if ( ( s - > is_float | | s - > is_double ) & & s - > measure_overall & MEASURE_NUMBER_OF_DENORMALS )
set_meta ( metadata , 0 , " Number of denormals " , " %f " , nb_denormals / ( float ) s - > nb_channels ) ;
2015-06-29 23:14:53 +02:00
}
2019-04-22 11:47:04 +02:00
# define UPDATE_STATS_P(type, update_func, update_float, channel_func) \
2020-04-16 17:55:11 +02:00
for ( int c = start ; c < end ; c + + ) { \
2019-03-02 00:09:54 +02:00
ChannelStats * p = & s - > chstats [ c ] ; \
const type * src = ( const type * ) data [ c ] ; \
2019-03-02 00:48:04 +02:00
const type * const srcend = src + samples ; \
2019-04-22 11:47:04 +02:00
for ( ; src < srcend ; src + + ) { \
2019-03-02 01:10:21 +02:00
update_func ; \
2019-04-22 11:47:04 +02:00
update_float ; \
} \
2019-03-02 01:10:21 +02:00
channel_func ; \
2019-03-02 00:09:54 +02:00
}
2019-04-22 11:47:04 +02:00
# define UPDATE_STATS_I(type, update_func, update_float, channel_func) \
2020-04-16 17:55:11 +02:00
for ( int c = start ; c < end ; c + + ) { \
2019-03-02 00:48:04 +02:00
ChannelStats * p = & s - > chstats [ c ] ; \
const type * src = ( const type * ) data [ 0 ] ; \
const type * const srcend = src + samples * channels ; \
2019-04-22 11:47:04 +02:00
for ( src + = c ; src < srcend ; src + = channels ) { \
2019-03-02 01:10:21 +02:00
update_func ; \
2019-04-22 11:47:04 +02:00
update_float ; \
} \
2019-03-02 01:10:21 +02:00
channel_func ; \
2019-03-02 00:09:54 +02:00
}
# define UPDATE_STATS(planar, type, sample, normalizer_suffix, int_sample) \
2019-03-02 01:10:21 +02:00
if ( ( s - > measure_overall | s - > measure_perchannel ) & ~ MEASURE_MINMAXPEAK ) { \
2019-04-29 11:03:52 +02:00
UPDATE_STATS_ # # planar ( type , update_stat ( s , p , sample , sample normalizer_suffix , int_sample ) , s - > is_float ? update_float_stat ( s , p , sample ) : s - > is_double ? update_double_stat ( s , p , sample ) : ( void ) NULL , ) ; \
2019-03-02 01:10:21 +02:00
} else { \
2019-04-22 11:47:04 +02:00
UPDATE_STATS_ # # planar ( type , update_minmax ( s , p , sample ) , , p - > nmin = p - > min normalizer_suffix ; p - > nmax = p - > max normalizer_suffix ; ) ; \
2019-03-02 01:10:21 +02:00
}
2019-03-02 00:09:54 +02:00
2020-04-16 17:55:11 +02:00
static int filter_channel ( AVFilterContext * ctx , void * arg , int jobnr , int nb_jobs )
2013-04-22 15:38:24 +03:00
{
2020-04-16 17:55:11 +02:00
AudioStatsContext * s = ctx - > priv ;
AVFilterLink * inlink = ctx - > inputs [ 0 ] ;
AVFrame * buf = arg ;
const uint8_t * const * const data = ( const uint8_t * const * ) buf - > extended_data ;
2013-04-22 15:38:24 +03:00
const int channels = s - > nb_channels ;
2019-03-02 00:09:54 +02:00
const int samples = buf - > nb_samples ;
2020-04-16 17:55:11 +02:00
const int start = ( buf - > channels * jobnr ) / nb_jobs ;
const int end = ( buf - > channels * ( jobnr + 1 ) ) / nb_jobs ;
2016-02-24 23:10:32 +02:00
2013-04-22 15:38:24 +03:00
switch ( inlink - > format ) {
case AV_SAMPLE_FMT_DBLP :
2019-03-02 00:09:54 +02:00
UPDATE_STATS ( P , double , * src , , llrint ( * src * ( UINT64_C ( 1 ) < < 63 ) ) ) ;
2013-04-22 15:38:24 +03:00
break ;
2019-03-02 00:09:54 +02:00
case AV_SAMPLE_FMT_DBL :
UPDATE_STATS ( I , double , * src , , llrint ( * src * ( UINT64_C ( 1 ) < < 63 ) ) ) ;
2016-08-15 22:01:16 +02:00
break ;
case AV_SAMPLE_FMT_FLTP :
2019-03-02 00:09:54 +02:00
UPDATE_STATS ( P , float , * src , , llrint ( * src * ( UINT64_C ( 1 ) < < 31 ) ) ) ;
2016-08-15 22:01:16 +02:00
break ;
2019-03-02 00:09:54 +02:00
case AV_SAMPLE_FMT_FLT :
UPDATE_STATS ( I , float , * src , , llrint ( * src * ( UINT64_C ( 1 ) < < 31 ) ) ) ;
2016-08-15 22:01:16 +02:00
break ;
2016-08-18 11:02:31 +02:00
case AV_SAMPLE_FMT_S64P :
2019-03-02 00:09:54 +02:00
UPDATE_STATS ( P , int64_t , * src , / ( double ) INT64_MAX , * src ) ;
2016-08-18 11:02:31 +02:00
break ;
2019-03-02 00:09:54 +02:00
case AV_SAMPLE_FMT_S64 :
UPDATE_STATS ( I , int64_t , * src , / ( double ) INT64_MAX , * src ) ;
2016-08-18 11:02:31 +02:00
break ;
2016-08-15 22:01:16 +02:00
case AV_SAMPLE_FMT_S32P :
2019-03-02 00:09:54 +02:00
UPDATE_STATS ( P , int32_t , * src , / ( double ) INT32_MAX , * src ) ;
2013-04-22 15:38:24 +03:00
break ;
2019-03-02 00:09:54 +02:00
case AV_SAMPLE_FMT_S32 :
UPDATE_STATS ( I , int32_t , * src , / ( double ) INT32_MAX , * src ) ;
2016-08-15 22:01:16 +02:00
break ;
case AV_SAMPLE_FMT_S16P :
2019-03-02 00:09:54 +02:00
UPDATE_STATS ( P , int16_t , * src , / ( double ) INT16_MAX , * src ) ;
2016-08-15 22:01:16 +02:00
break ;
2019-03-02 00:09:54 +02:00
case AV_SAMPLE_FMT_S16 :
UPDATE_STATS ( I , int16_t , * src , / ( double ) INT16_MAX , * src ) ;
2016-08-15 22:01:16 +02:00
break ;
2013-04-22 15:38:24 +03:00
}
2020-04-16 17:55:11 +02:00
return 0 ;
}
static int filter_frame ( AVFilterLink * inlink , AVFrame * buf )
{
AVFilterContext * ctx = inlink - > dst ;
AudioStatsContext * s = ctx - > priv ;
AVDictionary * * metadata = & buf - > metadata ;
if ( s - > reset_count > 0 ) {
if ( s - > nb_frames > = s - > reset_count ) {
reset_stats ( s ) ;
s - > nb_frames = 0 ;
}
s - > nb_frames + + ;
}
ctx - > internal - > execute ( ctx , filter_channel , buf , NULL , FFMIN ( inlink - > channels , ff_filter_get_nb_threads ( ctx ) ) ) ;
2015-06-29 23:14:53 +02:00
if ( s - > metadata )
set_metadata ( s , metadata ) ;
2013-04-22 15:38:24 +03:00
return ff_filter_frame ( inlink - > dst - > outputs [ 0 ] , buf ) ;
}
static void print_stats ( AVFilterContext * ctx )
{
AudioStatsContext * s = ctx - > priv ;
2020-04-17 12:56:19 +02:00
uint64_t mask = 0 , imask = 0xFFFFFFFFFFFFFFFF , min_count = 0 , max_count = 0 , nb_samples = 0 , noise_floor_count = 0 ;
2019-04-22 11:47:04 +02:00
uint64_t nb_nans = 0 , nb_infs = 0 , nb_denormals = 0 ;
2013-04-22 15:38:24 +03:00
double min_runs = 0 , max_runs = 0 ,
2019-05-12 16:56:00 +02:00
min = DBL_MAX , max = - DBL_MAX , min_diff = DBL_MAX , max_diff = 0 ,
nmin = DBL_MAX , nmax = - DBL_MAX ,
2013-04-22 15:38:24 +03:00
max_sigma_x = 0 ,
2017-05-12 18:13:17 +02:00
diff1_sum_x2 = 0 ,
2015-07-17 11:30:03 +02:00
diff1_sum = 0 ,
2013-04-22 15:38:24 +03:00
sigma_x = 0 ,
sigma_x2 = 0 ,
2020-04-16 16:10:12 +02:00
noise_floor = 0 ,
2013-04-22 15:38:24 +03:00
min_sigma_x2 = DBL_MAX ,
2019-05-12 16:56:00 +02:00
max_sigma_x2 = - DBL_MAX ;
2016-08-03 20:27:28 +02:00
AVRational depth ;
2013-04-22 15:38:24 +03:00
int c ;
for ( c = 0 ; c < s - > nb_channels ; c + + ) {
ChannelStats * p = & s - > chstats [ c ] ;
if ( p - > nb_samples < s - > tc_samples )
p - > min_sigma_x2 = p - > max_sigma_x2 = p - > sigma_x2 / p - > nb_samples ;
min = FFMIN ( min , p - > min ) ;
max = FFMAX ( max , p - > max ) ;
2016-08-15 22:01:16 +02:00
nmin = FFMIN ( nmin , p - > nmin ) ;
nmax = FFMAX ( nmax , p - > nmax ) ;
2015-07-17 11:30:03 +02:00
min_diff = FFMIN ( min_diff , p - > min_diff ) ;
2015-07-15 21:00:25 +02:00
max_diff = FFMAX ( max_diff , p - > max_diff ) ;
2017-05-12 18:13:17 +02:00
diff1_sum_x2 + = p - > diff1_sum_x2 ;
diff1_sum + = p - > diff1_sum ;
2013-04-22 15:38:24 +03:00
min_sigma_x2 = FFMIN ( min_sigma_x2 , p - > min_sigma_x2 ) ;
max_sigma_x2 = FFMAX ( max_sigma_x2 , p - > max_sigma_x2 ) ;
sigma_x + = p - > sigma_x ;
sigma_x2 + = p - > sigma_x2 ;
2020-04-16 16:10:12 +02:00
noise_floor = FFMAX ( noise_floor , p - > noise_floor ) ;
2013-04-22 15:38:24 +03:00
min_count + = p - > min_count ;
max_count + = p - > max_count ;
2020-04-17 12:56:19 +02:00
noise_floor_count + = p - > noise_floor_count ;
2013-04-22 15:38:24 +03:00
min_runs + = p - > min_runs ;
max_runs + = p - > max_runs ;
2015-07-13 10:49:26 +02:00
mask | = p - > mask ;
2016-08-03 20:27:28 +02:00
imask & = p - > imask ;
2013-04-22 15:38:24 +03:00
nb_samples + = p - > nb_samples ;
2019-04-22 11:47:04 +02:00
nb_nans + = p - > nb_nans ;
nb_infs + = p - > nb_infs ;
nb_denormals + = p - > nb_denormals ;
2013-04-22 15:38:24 +03:00
if ( fabs ( p - > sigma_x ) > fabs ( max_sigma_x ) )
max_sigma_x = p - > sigma_x ;
av_log ( ctx , AV_LOG_INFO , " Channel: %d \n " , c + 1 ) ;
2019-03-01 02:32:00 +02:00
if ( s - > measure_perchannel & MEASURE_DC_OFFSET )
2019-03-01 22:51:34 +02:00
av_log ( ctx , AV_LOG_INFO , " DC offset: %f \n " , p - > sigma_x / p - > nb_samples ) ;
2019-03-01 02:32:00 +02:00
if ( s - > measure_perchannel & MEASURE_MIN_LEVEL )
2019-03-01 22:51:34 +02:00
av_log ( ctx , AV_LOG_INFO , " Min level: %f \n " , p - > min ) ;
2019-03-01 02:32:00 +02:00
if ( s - > measure_perchannel & MEASURE_MAX_LEVEL )
2019-03-01 22:51:34 +02:00
av_log ( ctx , AV_LOG_INFO , " Max level: %f \n " , p - > max ) ;
2019-03-01 02:32:00 +02:00
if ( s - > measure_perchannel & MEASURE_MIN_DIFFERENCE )
2019-03-01 22:51:34 +02:00
av_log ( ctx , AV_LOG_INFO , " Min difference: %f \n " , p - > min_diff ) ;
2019-03-01 02:32:00 +02:00
if ( s - > measure_perchannel & MEASURE_MAX_DIFFERENCE )
2019-03-01 22:51:34 +02:00
av_log ( ctx , AV_LOG_INFO , " Max difference: %f \n " , p - > max_diff ) ;
2019-03-01 02:32:00 +02:00
if ( s - > measure_perchannel & MEASURE_MEAN_DIFFERENCE )
2019-03-01 22:51:34 +02:00
av_log ( ctx , AV_LOG_INFO , " Mean difference: %f \n " , p - > diff1_sum / ( p - > nb_samples - 1 ) ) ;
2019-03-01 02:32:00 +02:00
if ( s - > measure_perchannel & MEASURE_RMS_DIFFERENCE )
2019-03-01 22:51:34 +02:00
av_log ( ctx , AV_LOG_INFO , " RMS difference: %f \n " , sqrt ( p - > diff1_sum_x2 / ( p - > nb_samples - 1 ) ) ) ;
2019-03-01 02:32:00 +02:00
if ( s - > measure_perchannel & MEASURE_PEAK_LEVEL )
2019-03-01 22:51:34 +02:00
av_log ( ctx , AV_LOG_INFO , " Peak level dB: %f \n " , LINEAR_TO_DB ( FFMAX ( - p - > nmin , p - > nmax ) ) ) ;
2019-03-01 02:32:00 +02:00
if ( s - > measure_perchannel & MEASURE_RMS_LEVEL )
2019-03-01 22:51:34 +02:00
av_log ( ctx , AV_LOG_INFO , " RMS level dB: %f \n " , LINEAR_TO_DB ( sqrt ( p - > sigma_x2 / p - > nb_samples ) ) ) ;
2019-03-01 02:32:00 +02:00
if ( s - > measure_perchannel & MEASURE_RMS_PEAK )
2019-03-01 22:51:34 +02:00
av_log ( ctx , AV_LOG_INFO , " RMS peak dB: %f \n " , LINEAR_TO_DB ( sqrt ( p - > max_sigma_x2 ) ) ) ;
2019-03-01 02:32:00 +02:00
if ( s - > measure_perchannel & MEASURE_RMS_TROUGH )
2019-03-01 22:51:34 +02:00
if ( p - > min_sigma_x2 ! = 1 )
av_log ( ctx , AV_LOG_INFO , " RMS trough dB: %f \n " , LINEAR_TO_DB ( sqrt ( p - > min_sigma_x2 ) ) ) ;
2019-03-01 02:32:00 +02:00
if ( s - > measure_perchannel & MEASURE_CREST_FACTOR )
2019-03-01 22:51:34 +02:00
av_log ( ctx , AV_LOG_INFO , " Crest factor: %f \n " , p - > sigma_x2 ? FFMAX ( - p - > nmin , p - > nmax ) / sqrt ( p - > sigma_x2 / p - > nb_samples ) : 1 ) ;
2019-03-01 02:32:00 +02:00
if ( s - > measure_perchannel & MEASURE_FLAT_FACTOR )
2019-03-01 22:51:34 +02:00
av_log ( ctx , AV_LOG_INFO , " Flat factor: %f \n " , LINEAR_TO_DB ( ( p - > min_runs + p - > max_runs ) / ( p - > min_count + p - > max_count ) ) ) ;
2019-03-01 02:32:00 +02:00
if ( s - > measure_perchannel & MEASURE_PEAK_COUNT )
2019-03-01 22:51:34 +02:00
av_log ( ctx , AV_LOG_INFO , " Peak count: % " PRId64 " \n " , p - > min_count + p - > max_count ) ;
2020-04-16 16:10:12 +02:00
if ( s - > measure_perchannel & MEASURE_NOISE_FLOOR )
av_log ( ctx , AV_LOG_INFO , " Noise floor dB: %f \n " , LINEAR_TO_DB ( p - > noise_floor ) ) ;
2020-04-17 12:56:19 +02:00
if ( s - > measure_perchannel & MEASURE_NOISE_FLOOR_COUNT )
av_log ( ctx , AV_LOG_INFO , " Noise floor count: % " PRId64 " \n " , p - > noise_floor_count ) ;
2019-03-01 02:32:00 +02:00
if ( s - > measure_perchannel & MEASURE_BIT_DEPTH ) {
2019-03-01 22:51:34 +02:00
bit_depth ( s , p - > mask , p - > imask , & depth ) ;
av_log ( ctx , AV_LOG_INFO , " Bit depth: %u/%u \n " , depth . num , depth . den ) ;
2019-03-01 02:32:00 +02:00
}
if ( s - > measure_perchannel & MEASURE_DYNAMIC_RANGE )
2019-03-01 22:51:34 +02:00
av_log ( ctx , AV_LOG_INFO , " Dynamic range: %f \n " , LINEAR_TO_DB ( 2 * FFMAX ( FFABS ( p - > min ) , FFABS ( p - > max ) ) / p - > min_non_zero ) ) ;
2019-03-01 02:32:00 +02:00
if ( s - > measure_perchannel & MEASURE_ZERO_CROSSINGS )
2019-03-01 22:51:34 +02:00
av_log ( ctx , AV_LOG_INFO , " Zero crossings: % " PRId64 " \n " , p - > zero_runs ) ;
2019-03-01 02:32:00 +02:00
if ( s - > measure_perchannel & MEASURE_ZERO_CROSSINGS_RATE )
2019-03-01 22:51:34 +02:00
av_log ( ctx , AV_LOG_INFO , " Zero crossings rate: %f \n " , p - > zero_runs / ( double ) p - > nb_samples ) ;
2019-04-22 11:47:04 +02:00
if ( ( s - > is_float | | s - > is_double ) & & s - > measure_perchannel & MEASURE_NUMBER_OF_NANS )
av_log ( ctx , AV_LOG_INFO , " Number of NaNs: % " PRId64 " \n " , p - > nb_nans ) ;
if ( ( s - > is_float | | s - > is_double ) & & s - > measure_perchannel & MEASURE_NUMBER_OF_INFS )
av_log ( ctx , AV_LOG_INFO , " Number of Infs: % " PRId64 " \n " , p - > nb_infs ) ;
if ( ( s - > is_float | | s - > is_double ) & & s - > measure_perchannel & MEASURE_NUMBER_OF_DENORMALS )
av_log ( ctx , AV_LOG_INFO , " Number of denormals: % " PRId64 " \n " , p - > nb_denormals ) ;
2013-04-22 15:38:24 +03:00
}
av_log ( ctx , AV_LOG_INFO , " Overall \n " ) ;
2019-03-01 02:32:00 +02:00
if ( s - > measure_overall & MEASURE_DC_OFFSET )
2019-03-01 22:51:34 +02:00
av_log ( ctx , AV_LOG_INFO , " DC offset: %f \n " , max_sigma_x / ( nb_samples / s - > nb_channels ) ) ;
2019-03-01 02:32:00 +02:00
if ( s - > measure_overall & MEASURE_MIN_LEVEL )
2019-03-01 22:51:34 +02:00
av_log ( ctx , AV_LOG_INFO , " Min level: %f \n " , min ) ;
2019-03-01 02:32:00 +02:00
if ( s - > measure_overall & MEASURE_MAX_LEVEL )
2019-03-01 22:51:34 +02:00
av_log ( ctx , AV_LOG_INFO , " Max level: %f \n " , max ) ;
2019-03-01 02:32:00 +02:00
if ( s - > measure_overall & MEASURE_MIN_DIFFERENCE )
2019-03-01 22:51:34 +02:00
av_log ( ctx , AV_LOG_INFO , " Min difference: %f \n " , min_diff ) ;
2019-03-01 02:32:00 +02:00
if ( s - > measure_overall & MEASURE_MAX_DIFFERENCE )
2019-03-01 22:51:34 +02:00
av_log ( ctx , AV_LOG_INFO , " Max difference: %f \n " , max_diff ) ;
2019-03-01 02:32:00 +02:00
if ( s - > measure_overall & MEASURE_MEAN_DIFFERENCE )
2019-03-01 22:51:34 +02:00
av_log ( ctx , AV_LOG_INFO , " Mean difference: %f \n " , diff1_sum / ( nb_samples - s - > nb_channels ) ) ;
2019-03-01 02:32:00 +02:00
if ( s - > measure_overall & MEASURE_RMS_DIFFERENCE )
2019-03-01 22:51:34 +02:00
av_log ( ctx , AV_LOG_INFO , " RMS difference: %f \n " , sqrt ( diff1_sum_x2 / ( nb_samples - s - > nb_channels ) ) ) ;
2019-03-01 02:32:00 +02:00
if ( s - > measure_overall & MEASURE_PEAK_LEVEL )
2019-03-01 22:51:34 +02:00
av_log ( ctx , AV_LOG_INFO , " Peak level dB: %f \n " , LINEAR_TO_DB ( FFMAX ( - nmin , nmax ) ) ) ;
2019-03-01 02:32:00 +02:00
if ( s - > measure_overall & MEASURE_RMS_LEVEL )
2019-03-01 22:51:34 +02:00
av_log ( ctx , AV_LOG_INFO , " RMS level dB: %f \n " , LINEAR_TO_DB ( sqrt ( sigma_x2 / nb_samples ) ) ) ;
2019-03-01 02:32:00 +02:00
if ( s - > measure_overall & MEASURE_RMS_PEAK )
2019-03-01 22:51:34 +02:00
av_log ( ctx , AV_LOG_INFO , " RMS peak dB: %f \n " , LINEAR_TO_DB ( sqrt ( max_sigma_x2 ) ) ) ;
2019-03-01 02:32:00 +02:00
if ( s - > measure_overall & MEASURE_RMS_TROUGH )
2019-03-01 22:51:34 +02:00
if ( min_sigma_x2 ! = 1 )
av_log ( ctx , AV_LOG_INFO , " RMS trough dB: %f \n " , LINEAR_TO_DB ( sqrt ( min_sigma_x2 ) ) ) ;
2019-03-01 02:32:00 +02:00
if ( s - > measure_overall & MEASURE_FLAT_FACTOR )
2019-03-01 22:51:34 +02:00
av_log ( ctx , AV_LOG_INFO , " Flat factor: %f \n " , LINEAR_TO_DB ( ( min_runs + max_runs ) / ( min_count + max_count ) ) ) ;
2019-03-01 02:32:00 +02:00
if ( s - > measure_overall & MEASURE_PEAK_COUNT )
2019-03-01 22:51:34 +02:00
av_log ( ctx , AV_LOG_INFO , " Peak count: %f \n " , ( min_count + max_count ) / ( double ) s - > nb_channels ) ;
2020-04-16 16:10:12 +02:00
if ( s - > measure_overall & MEASURE_NOISE_FLOOR )
av_log ( ctx , AV_LOG_INFO , " Noise floor dB: %f \n " , LINEAR_TO_DB ( noise_floor ) ) ;
2020-04-17 12:56:19 +02:00
if ( s - > measure_overall & MEASURE_NOISE_FLOOR_COUNT )
av_log ( ctx , AV_LOG_INFO , " Noise floor count: %f \n " , noise_floor_count / ( double ) s - > nb_channels ) ;
2019-03-01 02:32:00 +02:00
if ( s - > measure_overall & MEASURE_BIT_DEPTH ) {
2019-03-01 22:51:34 +02:00
bit_depth ( s , mask , imask , & depth ) ;
av_log ( ctx , AV_LOG_INFO , " Bit depth: %u/%u \n " , depth . num , depth . den ) ;
2019-03-01 02:32:00 +02:00
}
if ( s - > measure_overall & MEASURE_NUMBER_OF_SAMPLES )
2019-03-01 22:51:34 +02:00
av_log ( ctx , AV_LOG_INFO , " Number of samples: % " PRId64 " \n " , nb_samples / s - > nb_channels ) ;
2019-04-22 11:47:04 +02:00
if ( ( s - > is_float | | s - > is_double ) & & s - > measure_overall & MEASURE_NUMBER_OF_NANS )
av_log ( ctx , AV_LOG_INFO , " Number of NaNs: %f \n " , nb_nans / ( float ) s - > nb_channels ) ;
if ( ( s - > is_float | | s - > is_double ) & & s - > measure_overall & MEASURE_NUMBER_OF_INFS )
av_log ( ctx , AV_LOG_INFO , " Number of Infs: %f \n " , nb_infs / ( float ) s - > nb_channels ) ;
if ( ( s - > is_float | | s - > is_double ) & & s - > measure_overall & MEASURE_NUMBER_OF_DENORMALS )
av_log ( ctx , AV_LOG_INFO , " Number of denormals: %f \n " , nb_denormals / ( float ) s - > nb_channels ) ;
2013-04-22 15:38:24 +03:00
}
2013-05-06 17:55:06 +03:00
static av_cold void uninit ( AVFilterContext * ctx )
2013-04-22 15:38:24 +03:00
{
AudioStatsContext * s = ctx - > priv ;
2015-03-08 13:06:45 +02:00
if ( s - > nb_channels )
print_stats ( ctx ) ;
2020-04-16 16:10:12 +02:00
if ( s - > chstats ) {
for ( int i = 0 ; i < s - > nb_channels ; i + + ) {
ChannelStats * p = & s - > chstats [ i ] ;
av_freep ( & p - > win_samples ) ;
}
}
2013-04-22 15:38:24 +03:00
av_freep ( & s - > chstats ) ;
}
static const AVFilterPad astats_inputs [ ] = {
{
. name = " default " ,
. type = AVMEDIA_TYPE_AUDIO ,
. filter_frame = filter_frame ,
} ,
{ NULL }
} ;
static const AVFilterPad astats_outputs [ ] = {
{
. name = " default " ,
. type = AVMEDIA_TYPE_AUDIO ,
. config_props = config_output ,
} ,
{ NULL }
} ;
2013-10-29 13:50:56 +03:00
AVFilter ff_af_astats = {
2013-04-22 15:38:24 +03:00
. name = " astats " ,
. description = NULL_IF_CONFIG_SMALL ( " Show time domain statistics about audio frames. " ) ,
. query_formats = query_formats ,
. priv_size = sizeof ( AudioStatsContext ) ,
. priv_class = & astats_class ,
. uninit = uninit ,
. inputs = astats_inputs ,
. outputs = astats_outputs ,
2020-04-16 17:55:11 +02:00
. flags = AVFILTER_FLAG_SLICE_THREADS ,
2013-04-22 15:38:24 +03:00
} ;