@ -24,8 +24,8 @@
* Calculate the VMAF between two input videos .
*/
# include <pthread.h>
# include <libvmaf.h>
# include "libavutil/avstring.h"
# include "libavutil/opt.h"
# include "libavutil/pixdesc.h"
@ -39,23 +39,9 @@
typedef struct LIBVMAFContext {
const AVClass * class ;
FFFrameSync fs ;
const AVPixFmtDescriptor * desc ;
int width ;
int height ;
double vmaf_score ;
int vmaf_thread_created ;
pthread_t vmaf_thread ;
pthread_mutex_t lock ;
pthread_cond_t cond ;
int eof ;
AVFrame * gmain ;
AVFrame * gref ;
int frame_set ;
char * model_path ;
char * log_path ;
char * log_fmt ;
int disable_clip ;
int disable_avx ;
int enable_transform ;
int phone_model ;
int psnr ;
@ -65,184 +51,487 @@ typedef struct LIBVMAFContext {
int n_threads ;
int n_subsample ;
int enable_conf_interval ;
int error ;
char * model_cfg ;
char * feature_cfg ;
VmafContext * vmaf ;
VmafModel * * model ;
unsigned model_cnt ;
unsigned frame_cnt ;
unsigned bpc ;
} LIBVMAFContext ;
# define OFFSET(x) offsetof(LIBVMAFContext, x)
# define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
static const AVOption libvmaf_options [ ] = {
{ " model_path " , " Set the model to be used for computing vmaf ." , OFFSET ( model_path ) , AV_OPT_TYPE_STRING , { . str = " /usr/local/share/model/vmaf_v0.6.1.pkl " } , 0 , 1 , FLAGS } ,
{ " log_path " , " Set the file path to be used to store logs . " , OFFSET ( log_path ) , AV_OPT_TYPE_STRING , { . str = NULL } , 0 , 1 , FLAGS } ,
{ " log_fmt " , " Set the format of the log (csv, json or xml ). " , OFFSET ( log_fmt ) , AV_OPT_TYPE_STRING , { . str = NULL } , 0 , 1 , FLAGS } ,
{ " enable_transform " , " Enables transform for computing vmaf ." , OFFSET ( enable_transform ) , AV_OPT_TYPE_BOOL , { . i64 = 0 } , 0 , 1 , FLAGS } ,
{ " phone_model " , " Invokes the phone model that will generate higher VMAF scores ." , OFFSET ( phone_model ) , AV_OPT_TYPE_BOOL , { . i64 = 0 } , 0 , 1 , FLAGS } ,
{ " psnr " , " Enables computing psnr along with vmaf ." , OFFSET ( psnr ) , AV_OPT_TYPE_BOOL , { . i64 = 0 } , 0 , 1 , FLAGS } ,
{ " ssim " , " Enables computing ssim along with vmaf ." , OFFSET ( ssim ) , AV_OPT_TYPE_BOOL , { . i64 = 0 } , 0 , 1 , FLAGS } ,
{ " ms_ssim " , " Enables computing ms-ssim along with vmaf ." , OFFSET ( ms_ssim ) , AV_OPT_TYPE_BOOL , { . i64 = 0 } , 0 , 1 , FLAGS } ,
{ " model_path " , " use model='path=...' ." , OFFSET ( model_path ) , AV_OPT_TYPE_STRING , { . str = NULL } , 0 , 1 , FLAGS | AV_OPT_FLAG_DEPRECATED } ,
{ " log_path " , " Set the file path to be used to write log . " , OFFSET ( log_path ) , AV_OPT_TYPE_STRING , { . str = NULL } , 0 , 1 , FLAGS } ,
{ " log_fmt " , " Set the format of the log (csv, json, xml, or sub ). " , OFFSET ( log_fmt ) , AV_OPT_TYPE_STRING , { . str = " xml " } , 0 , 1 , FLAGS } ,
{ " enable_transform " , " use model='enable_transform=true' ." , OFFSET ( enable_transform ) , AV_OPT_TYPE_BOOL , { . i64 = 0 } , 0 , 1 , FLAGS | AV_OPT_FLAG_DEPRECATED } ,
{ " phone_model " , " use model='enable_transform=true' ." , OFFSET ( phone_model ) , AV_OPT_TYPE_BOOL , { . i64 = 0 } , 0 , 1 , FLAGS | AV_OPT_FLAG_DEPRECATED } ,
{ " psnr " , " use feature='name=psnr' ." , OFFSET ( psnr ) , AV_OPT_TYPE_BOOL , { . i64 = 0 } , 0 , 1 , FLAGS | AV_OPT_FLAG_DEPRECATED } ,
{ " ssim " , " use feature='name=ssim' ." , OFFSET ( ssim ) , AV_OPT_TYPE_BOOL , { . i64 = 0 } , 0 , 1 , FLAGS | AV_OPT_FLAG_DEPRECATED } ,
{ " ms_ssim " , " use feature='name=ms_ssim' ." , OFFSET ( ms_ssim ) , AV_OPT_TYPE_BOOL , { . i64 = 0 } , 0 , 1 , FLAGS | AV_OPT_FLAG_DEPRECATED } ,
{ " pool " , " Set the pool method to be used for computing vmaf. " , OFFSET ( pool ) , AV_OPT_TYPE_STRING , { . str = NULL } , 0 , 1 , FLAGS } ,
{ " n_threads " , " Set number of threads to be used when computing vmaf. " , OFFSET ( n_threads ) , AV_OPT_TYPE_INT , { . i64 = 0 } , 0 , UINT_MAX , FLAGS } ,
{ " n_subsample " , " Set interval for frame subsampling used when computing vmaf. " , OFFSET ( n_subsample ) , AV_OPT_TYPE_INT , { . i64 = 1 } , 1 , UINT_MAX , FLAGS } ,
{ " enable_conf_interval " , " Enables confidence interval. " , OFFSET ( enable_conf_interval ) , AV_OPT_TYPE_BOOL , { . i64 = 0 } , 0 , 1 , FLAGS } ,
{ " enable_conf_interval " , " model='enable_conf_interval=true'. " , OFFSET ( enable_conf_interval ) , AV_OPT_TYPE_BOOL , { . i64 = 0 } , 0 , 1 , FLAGS | AV_OPT_FLAG_DEPRECATED } ,
{ " model " , " Set the model to be used for computing vmaf. " , OFFSET ( model_cfg ) , AV_OPT_TYPE_STRING , { . str = " version=vmaf_v0.6.1 " } , 0 , 1 , FLAGS } ,
{ " feature " , " Set the feature to be used for computing vmaf. " , OFFSET ( feature_cfg ) , AV_OPT_TYPE_STRING , { . str = NULL } , 0 , 1 , FLAGS } ,
{ NULL }
} ;
FRAMESYNC_DEFINE_CLASS ( libvmaf , LIBVMAFContext , fs ) ;
# define read_frame_fn(type, bits) \
static int read_frame_ # # bits # # bit ( float * ref_data , float * main_data , \
float * temp_data , int stride , void * ctx ) \
{ \
LIBVMAFContext * s = ( LIBVMAFContext * ) ctx ; \
int ret ; \
\
pthread_mutex_lock ( & s - > lock ) ; \
\
while ( ! s - > frame_set & & ! s - > eof ) { \
pthread_cond_wait ( & s - > cond , & s - > lock ) ; \
} \
\
if ( s - > frame_set ) { \
int ref_stride = s - > gref - > linesize [ 0 ] ; \
int main_stride = s - > gmain - > linesize [ 0 ] ; \
\
const type * ref_ptr = ( const type * ) s - > gref - > data [ 0 ] ; \
const type * main_ptr = ( const type * ) s - > gmain - > data [ 0 ] ; \
\
float * ptr = ref_data ; \
float factor = 1.f / ( 1 < < ( bits - 8 ) ) ; \
\
int h = s - > height ; \
int w = s - > width ; \
\
int i , j ; \
\
for ( i = 0 ; i < h ; i + + ) { \
for ( j = 0 ; j < w ; j + + ) { \
ptr [ j ] = ref_ptr [ j ] * factor ; \
} \
ref_ptr + = ref_stride / sizeof ( * ref_ptr ) ; \
ptr + = stride / sizeof ( * ptr ) ; \
} \
\
ptr = main_data ; \
\
for ( i = 0 ; i < h ; i + + ) { \
for ( j = 0 ; j < w ; j + + ) { \
ptr [ j ] = main_ptr [ j ] * factor ; \
} \
main_ptr + = main_stride / sizeof ( * main_ptr ) ; \
ptr + = stride / sizeof ( * ptr ) ; \
} \
} \
\
ret = ! s - > frame_set ; \
\
av_frame_unref ( s - > gref ) ; \
av_frame_unref ( s - > gmain ) ; \
s - > frame_set = 0 ; \
\
pthread_cond_signal ( & s - > cond ) ; \
pthread_mutex_unlock ( & s - > lock ) ; \
\
if ( ret ) { \
return 2 ; \
} \
\
return 0 ; \
static enum VmafPixelFormat pix_fmt_map ( enum AVPixelFormat av_pix_fmt )
{
switch ( av_pix_fmt ) {
case AV_PIX_FMT_YUV420P :
case AV_PIX_FMT_YUV420P10LE :
case AV_PIX_FMT_YUV420P12LE :
case AV_PIX_FMT_YUV420P16LE :
return VMAF_PIX_FMT_YUV420P ;
case AV_PIX_FMT_YUV422P :
case AV_PIX_FMT_YUV422P10LE :
case AV_PIX_FMT_YUV422P12LE :
case AV_PIX_FMT_YUV422P16LE :
return VMAF_PIX_FMT_YUV422P ;
case AV_PIX_FMT_YUV444P :
case AV_PIX_FMT_YUV444P10LE :
case AV_PIX_FMT_YUV444P12LE :
case AV_PIX_FMT_YUV444P16LE :
return VMAF_PIX_FMT_YUV444P ;
default :
return VMAF_PIX_FMT_UNKNOWN ;
}
}
read_frame_fn ( uint8_t , 8 ) ;
read_frame_fn ( uint16_t , 10 ) ;
static int copy_picture_data ( AVFrame * src , VmafPicture * dst , unsigned bpc )
{
int err = vmaf_picture_alloc ( dst , pix_fmt_map ( src - > format ) , bpc ,
src - > width , src - > height ) ;
if ( err )
return AVERROR ( ENOMEM ) ;
for ( unsigned i = 0 ; i < 3 ; i + + ) {
uint8_t * src_data = src - > data [ i ] ;
uint8_t * dst_data = dst - > data [ i ] ;
for ( unsigned j = 0 ; j < dst - > h [ i ] ; j + + ) {
memcpy ( dst_data , src_data , sizeof ( * dst_data ) * dst - > w [ i ] ) ;
src_data + = src - > linesize [ i ] ;
dst_data + = dst - > stride [ i ] ;
}
}
return 0 ;
}
static void compute_vmaf_score ( LIBVMAFContext * s )
static int do_vmaf ( FFFrameSync * fs )
{
int ( * read_frame ) ( float * ref_data , float * main_data , float * temp_data ,
int stride , void * ctx ) ;
char * format ;
AVFilterContext * ctx = fs - > parent ;
LIBVMAFContext * s = ctx - > priv ;
VmafPicture pic_ref , pic_dist ;
AVFrame * ref , * dist ;
int err = 0 ;
if ( s - > desc - > comp [ 0 ] . depth < = 8 ) {
read_frame = read_frame_8bit ;
} else {
read_frame = read_frame_10bit ;
int ret = ff_framesync_dualinput_get ( fs , & dist , & ref ) ;
if ( ret < 0 )
return ret ;
if ( ctx - > is_disabled | | ! ref )
return ff_filter_frame ( ctx - > outputs [ 0 ] , dist ) ;
err = copy_picture_data ( ref , & pic_ref , s - > bpc ) ;
if ( err ) {
av_log ( s , AV_LOG_ERROR , " problem during vmaf_picture_alloc. \n " ) ;
return AVERROR ( ENOMEM ) ;
}
err = copy_picture_data ( dist , & pic_dist , s - > bpc ) ;
if ( err ) {
av_log ( s , AV_LOG_ERROR , " problem during vmaf_picture_alloc. \n " ) ;
vmaf_picture_unref ( & pic_ref ) ;
return AVERROR ( ENOMEM ) ;
}
format = ( char * ) s - > desc - > name ;
err = vmaf_read_pictures ( s - > vmaf , & pic_ref , & pic_dist , s - > frame_cnt + + ) ;
if ( err ) {
av_log ( s , AV_LOG_ERROR , " problem during vmaf_read_pictures. \n " ) ;
return AVERROR ( EINVAL ) ;
}
s - > error = compute_vmaf ( & s - > vmaf_score , format , s - > width , s - > height ,
read_frame , s , s - > model_path , s - > log_path ,
s - > log_fmt , 0 , 0 , s - > enable_transform ,
s - > phone_model , s - > psnr , s - > ssim ,
s - > ms_ssim , s - > pool ,
s - > n_threads , s - > n_subsample , s - > enable_conf_interval ) ;
return ff_filter_frame ( ctx - > outputs [ 0 ] , dist ) ;
}
static void * call_vmaf ( void * ctx )
static AVDictionary * * delimited_dict_parse ( char * str , unsigned * cnt )
{
LIBVMAFContext * s = ( LIBVMAFContext * ) ctx ;
compute_vmaf_score ( s ) ;
if ( ! s - > error ) {
av_log ( ctx , AV_LOG_INFO , " VMAF score: %f \n " , s - > vmaf_score ) ;
} else {
pthread_mutex_lock ( & s - > lock ) ;
pthread_cond_signal ( & s - > cond ) ;
pthread_mutex_unlock ( & s - > lock ) ;
AVDictionary * * dict = NULL ;
char * str_copy = NULL ;
char * saveptr = NULL ;
unsigned cnt2 ;
int err = 0 ;
if ( ! str )
return NULL ;
cnt2 = 1 ;
for ( char * p = str ; * p ; p + + ) {
if ( * p = = ' | ' )
cnt2 + + ;
}
dict = av_calloc ( cnt2 , sizeof ( * dict ) ) ;
if ( ! dict )
goto fail ;
str_copy = av_strdup ( str ) ;
if ( ! str_copy )
goto fail ;
* cnt = 0 ;
for ( unsigned i = 0 ; i < cnt2 ; i + + ) {
char * s = av_strtok ( i = = 0 ? str_copy : NULL , " | " , & saveptr ) ;
if ( ! s )
continue ;
err = av_dict_parse_string ( & dict [ ( * cnt ) + + ] , s , " = " , " : " , 0 ) ;
if ( err )
goto fail ;
}
av_free ( str_copy ) ;
return dict ;
fail :
if ( dict ) {
for ( unsigned i = 0 ; i < * cnt ; i + + ) {
if ( dict [ i ] )
av_dict_free ( & dict [ i ] ) ;
}
av_free ( dict ) ;
}
pthread_exit ( NULL ) ;
av_free ( str_copy ) ;
* cnt = 0 ;
return NULL ;
}
static int do_vmaf ( FFFrameSync * fs )
static int parse_features ( AVFilterContext * ctx )
{
AVFilterContext * ctx = fs - > parent ;
LIBVMAFContext * s = ctx - > priv ;
AVFrame * master , * ref ;
int ret ;
AVDictionary * * dict = NULL ;
unsigned dict_cnt ;
int err = 0 ;
ret = ff_framesync_dualinput_get ( fs , & master , & ref ) ;
if ( ret < 0 )
return ret ;
if ( ! ref )
return ff_filter_frame ( ctx - > outputs [ 0 ] , master ) ;
if ( ! s - > feature_cfg )
return 0 ;
dict = delimited_dict_parse ( s - > feature_cfg , & dict_cnt ) ;
if ( ! dict ) {
av_log ( ctx , AV_LOG_ERROR ,
" could not parse feature config: %s \n " , s - > feature_cfg ) ;
return AVERROR ( EINVAL ) ;
}
pthread_mutex_lock ( & s - > lock ) ;
for ( unsigned i = 0 ; i < dict_cnt ; i + + ) {
char * feature_name = NULL ;
VmafFeatureDictionary * feature_opts_dict = NULL ;
AVDictionaryEntry * e = NULL ;
while ( e = av_dict_get ( dict [ i ] , " " , e , AV_DICT_IGNORE_SUFFIX ) ) {
if ( av_stristr ( e - > key , " name " ) ) {
feature_name = e - > value ;
continue ;
}
err = vmaf_feature_dictionary_set ( & feature_opts_dict , e - > key ,
e - > value ) ;
if ( err ) {
av_log ( ctx , AV_LOG_ERROR ,
" could not set feature option: %s.%s=%s \n " ,
feature_name , e - > key , e - > value ) ;
goto exit ;
}
}
err = vmaf_use_feature ( s - > vmaf , feature_name , feature_opts_dict ) ;
if ( err ) {
av_log ( ctx , AV_LOG_ERROR ,
" problem during vmaf_use_feature: %s \n " , feature_name ) ;
goto exit ;
}
}
while ( s - > frame_set & & ! s - > error ) {
pthread_cond_wait ( & s - > cond , & s - > lock ) ;
exit :
for ( unsigned i = 0 ; i < dict_cnt ; i + + ) {
if ( dict [ i ] )
av_dict_free ( & dict [ i ] ) ;
}
av_free ( dict ) ;
return err ;
}
static int parse_models ( AVFilterContext * ctx )
{
LIBVMAFContext * s = ctx - > priv ;
AVDictionary * * dict ;
unsigned dict_cnt ;
int err = 0 ;
if ( ! s - > model_cfg ) return 0 ;
if ( s - > error ) {
dict_cnt = 0 ;
dict = delimited_dict_parse ( s - > model_cfg , & dict_cnt ) ;
if ( ! dict ) {
av_log ( ctx , AV_LOG_ERROR ,
" libvmaf encountered an error, check log for details \n " ) ;
pthread_mutex_unlock ( & s - > lock ) ;
" could not parse model config: %s \n " , s - > model_cfg ) ;
return AVERROR ( EINVAL ) ;
}
av_frame_ref ( s - > gref , ref ) ;
av_frame_ref ( s - > gmain , master ) ;
s - > model_cnt = dict_cnt ;
s - > model = av_calloc ( s - > model_cnt , sizeof ( * s - > model ) ) ;
if ( ! s - > model )
return AVERROR ( ENOMEM ) ;
for ( unsigned i = 0 ; i < dict_cnt ; i + + ) {
VmafModelConfig model_cfg = { 0 } ;
AVDictionaryEntry * e = NULL ;
char * version = NULL ;
char * path = NULL ;
while ( e = av_dict_get ( dict [ i ] , " " , e , AV_DICT_IGNORE_SUFFIX ) ) {
if ( av_stristr ( e - > key , " disable_clip " ) ) {
model_cfg . flags | = av_stristr ( e - > value , " true " ) ?
VMAF_MODEL_FLAG_DISABLE_CLIP : 0 ;
continue ;
}
if ( av_stristr ( e - > key , " enable_transform " ) ) {
model_cfg . flags | = av_stristr ( e - > value , " true " ) ?
VMAF_MODEL_FLAG_ENABLE_TRANSFORM : 0 ;
continue ;
}
if ( av_stristr ( e - > key , " name " ) ) {
model_cfg . name = e - > value ;
continue ;
}
if ( av_stristr ( e - > key , " version " ) ) {
version = e - > value ;
continue ;
}
if ( av_stristr ( e - > key , " path " ) ) {
path = e - > value ;
continue ;
}
}
if ( version ) {
err = vmaf_model_load ( & s - > model [ i ] , & model_cfg , version ) ;
if ( err ) {
av_log ( ctx , AV_LOG_ERROR ,
" could not load libvmaf model with version: %s \n " ,
version ) ;
goto exit ;
}
}
if ( path & & ! s - > model [ i ] ) {
err = vmaf_model_load_from_path ( & s - > model [ i ] , & model_cfg , path ) ;
if ( err ) {
av_log ( ctx , AV_LOG_ERROR ,
" could not load libvmaf model with path: %s \n " ,
path ) ;
goto exit ;
}
}
if ( ! s - > model [ i ] ) {
av_log ( ctx , AV_LOG_ERROR ,
" could not load libvmaf model with config: %s \n " ,
s - > model_cfg ) ;
goto exit ;
}
while ( e = av_dict_get ( dict [ i ] , " " , e , AV_DICT_IGNORE_SUFFIX ) ) {
VmafFeatureDictionary * feature_opts_dict = NULL ;
char * feature_opt = NULL ;
char * feature_name = av_strtok ( e - > key , " . " , & feature_opt ) ;
if ( ! feature_opt )
continue ;
err = vmaf_feature_dictionary_set ( & feature_opts_dict ,
feature_opt , e - > value ) ;
if ( err ) {
av_log ( ctx , AV_LOG_ERROR ,
" could not set feature option: %s.%s=%s \n " ,
feature_name , feature_opt , e - > value ) ;
err = AVERROR ( EINVAL ) ;
goto exit ;
}
err = vmaf_model_feature_overload ( s - > model [ i ] , feature_name ,
feature_opts_dict ) ;
if ( err ) {
av_log ( ctx , AV_LOG_ERROR ,
" could not overload feature: %s \n " , feature_name ) ;
err = AVERROR ( EINVAL ) ;
goto exit ;
}
}
}
for ( unsigned i = 0 ; i < s - > model_cnt ; i + + ) {
err = vmaf_use_features_from_model ( s - > vmaf , s - > model [ i ] ) ;
if ( err ) {
av_log ( ctx , AV_LOG_ERROR ,
" problem during vmaf_use_features_from_model \n " ) ;
err = AVERROR ( EINVAL ) ;
goto exit ;
}
}
exit :
for ( unsigned i = 0 ; i < dict_cnt ; i + + ) {
if ( dict [ i ] )
av_dict_free ( & dict [ i ] ) ;
}
av_free ( dict ) ;
return err ;
}
static enum VmafLogLevel log_level_map ( int log_level )
{
switch ( log_level ) {
case AV_LOG_QUIET :
return VMAF_LOG_LEVEL_NONE ;
case AV_LOG_ERROR :
return VMAF_LOG_LEVEL_ERROR ;
case AV_LOG_WARNING :
return VMAF_LOG_LEVEL_WARNING ;
case AV_LOG_INFO :
return VMAF_LOG_LEVEL_INFO ;
case AV_LOG_DEBUG :
return VMAF_LOG_LEVEL_DEBUG ;
default :
return VMAF_LOG_LEVEL_INFO ;
}
}
static int parse_deprecated_options ( AVFilterContext * ctx )
{
LIBVMAFContext * s = ctx - > priv ;
VmafModel * model = NULL ;
VmafModelCollection * model_collection = NULL ;
enum VmafModelFlags flags = VMAF_MODEL_FLAGS_DEFAULT ;
int err = 0 ;
VmafModelConfig model_cfg = {
. name = " vmaf " ,
. flags = flags ,
} ;
if ( s - > enable_transform | | s - > phone_model )
flags | = VMAF_MODEL_FLAG_ENABLE_TRANSFORM ;
if ( ! s - > model_path )
goto extra_metrics_only ;
if ( s - > enable_conf_interval ) {
err = vmaf_model_collection_load_from_path ( & model , & model_collection ,
& model_cfg , s - > model_path ) ;
if ( err ) {
av_log ( ctx , AV_LOG_ERROR ,
" problem loading model file: %s \n " , s - > model_path ) ;
goto exit ;
}
err = vmaf_use_features_from_model_collection ( s - > vmaf , model_collection ) ;
if ( err ) {
av_log ( ctx , AV_LOG_ERROR ,
" problem loading feature extractors from model file: %s \n " ,
s - > model_path ) ;
goto exit ;
}
} else {
err = vmaf_model_load_from_path ( & model , & model_cfg , s - > model_path ) ;
if ( err ) {
av_log ( ctx , AV_LOG_ERROR ,
" problem loading model file: %s \n " , s - > model_path ) ;
goto exit ;
}
err = vmaf_use_features_from_model ( s - > vmaf , model ) ;
if ( err ) {
av_log ( ctx , AV_LOG_ERROR ,
" problem loading feature extractors from model file: %s \n " ,
s - > model_path ) ;
goto exit ;
}
}
extra_metrics_only :
if ( s - > psnr ) {
VmafFeatureDictionary * d = NULL ;
vmaf_feature_dictionary_set ( & d , " enable_chroma " , " false " ) ;
err = vmaf_use_feature ( s - > vmaf , " psnr " , d ) ;
if ( err ) {
av_log ( ctx , AV_LOG_ERROR ,
" problem loading feature extractor: psnr \n " ) ;
goto exit ;
}
}
s - > frame_set = 1 ;
if ( s - > ssim ) {
err = vmaf_use_feature ( s - > vmaf , " float_ssim " , NULL ) ;
if ( err ) {
av_log ( ctx , AV_LOG_ERROR ,
" problem loading feature extractor: ssim \n " ) ;
goto exit ;
}
}
pthread_cond_signal ( & s - > cond ) ;
pthread_mutex_unlock ( & s - > lock ) ;
if ( s - > ms_ssim ) {
err = vmaf_use_feature ( s - > vmaf , " float_ms_ssim " , NULL ) ;
if ( err ) {
av_log ( ctx , AV_LOG_ERROR ,
" problem loading feature extractor: ms_ssim \n " ) ;
goto exit ;
}
}
return ff_filter_frame ( ctx - > outputs [ 0 ] , master ) ;
exit :
return err ;
}
static av_cold int init ( AVFilterContext * ctx )
{
LIBVMAFContext * s = ctx - > priv ;
int err = 0 ;
s - > gref = av_frame_alloc ( ) ;
s - > gmain = av_frame_alloc ( ) ;
if ( ! s - > gref | | ! s - > gmain )
return AVERROR ( ENOMEM ) ;
VmafConfiguration cfg = {
. log_level = log_level_map ( av_log_get_level ( ) ) ,
. n_subsample = s - > n_subsample ,
. n_threads = s - > n_threads ,
} ;
err = vmaf_init ( & s - > vmaf , cfg ) ;
if ( err )
return AVERROR ( EINVAL ) ;
err = parse_deprecated_options ( ctx ) ;
if ( err )
return err ;
s - > error = 0 ;
err = parse_models ( ctx ) ;
if ( err )
return err ;
s - > vmaf_thread_created = 0 ;
pthread_mutex_init ( & s - > lock , NULL ) ;
pthread_cond_init ( & s - > cond , NULL ) ;
err = parse_features ( ctx ) ;
if ( err )
return err ;
s - > fs . on_event = do_vmaf ;
return 0 ;
@ -256,26 +545,31 @@ static const enum AVPixelFormat pix_fmts[] = {
static int config_input_ref ( AVFilterLink * inlink )
{
AVFilterContext * ctx = inlink - > dst ;
AVFilterContext * ctx = inlink - > dst ;
LIBVMAFContext * s = ctx - > priv ;
int th ;
const AVPixFmtDescriptor * desc ;
int err = 0 ;
if ( ctx - > inputs [ 0 ] - > w ! = ctx - > inputs [ 1 ] - > w | |
ctx - > inputs [ 0 ] - > h ! = ctx - > inputs [ 1 ] - > h ) {
av_log ( ctx , AV_LOG_ERROR , " Width and height of input videos must be same. \n " ) ;
return AVERROR ( EINVAL ) ;
if ( ctx - > inputs [ 0 ] - > w ! = ctx - > inputs [ 1 ] - > w ) {
av_log ( ctx , AV_LOG_ERROR , " input width must match. \n " ) ;
err | = AVERROR ( EINVAL ) ;
}
s - > desc = av_pix_fmt_desc_get ( inlink - > format ) ;
s - > width = ctx - > inputs [ 0 ] - > w ;
s - > height = ctx - > inputs [ 0 ] - > h ;
if ( ctx - > inputs [ 0 ] - > h ! = ctx - > inputs [ 1 ] - > h ) {
av_log ( ctx , AV_LOG_ERROR , " input height must match. \n " ) ;
err | = AVERROR ( EINVAL ) ;
}
th = pthread_create ( & s - > vmaf_thread , NULL , call_vmaf , ( void * ) s ) ;
if ( th ) {
av_log ( ctx , AV_LOG_ERROR , " Thread creation failed. \n " ) ;
return AVERROR ( EINVAL ) ;
if ( ctx - > inputs [ 0 ] - > format ! = ctx - > inputs [ 1 ] - > format ) {
av_log ( ctx , AV_LOG_ERROR , " input pix_fmt must match. \n " ) ;
err | = AVERROR ( EINVAL ) ;
}
s - > vmaf_thread_created = 1 ;
if ( err )
return err ;
desc = av_pix_fmt_desc_get ( inlink - > format ) ;
s - > bpc = desc - > comp [ 0 ] . depth ;
return 0 ;
}
@ -307,28 +601,80 @@ static int activate(AVFilterContext *ctx)
return ff_framesync_activate ( & s - > fs ) ;
}
static enum VmafOutputFormat log_fmt_map ( const char * log_fmt )
{
if ( log_fmt ) {
if ( av_stristr ( log_fmt , " xml " ) )
return VMAF_OUTPUT_FORMAT_XML ;
if ( av_stristr ( log_fmt , " json " ) )
return VMAF_OUTPUT_FORMAT_JSON ;
if ( av_stristr ( log_fmt , " csv " ) )
return VMAF_OUTPUT_FORMAT_CSV ;
if ( av_stristr ( log_fmt , " sub " ) )
return VMAF_OUTPUT_FORMAT_SUB ;
}
return VMAF_OUTPUT_FORMAT_XML ;
}
static enum VmafPoolingMethod pool_method_map ( const char * pool_method )
{
if ( pool_method ) {
if ( av_stristr ( pool_method , " min " ) )
return VMAF_POOL_METHOD_MIN ;
if ( av_stristr ( pool_method , " mean " ) )
return VMAF_POOL_METHOD_MEAN ;
if ( av_stristr ( pool_method , " harmonic_mean " ) )
return VMAF_POOL_METHOD_HARMONIC_MEAN ;
}
return VMAF_POOL_METHOD_MEAN ;
}
static av_cold void uninit ( AVFilterContext * ctx )
{
LIBVMAFContext * s = ctx - > priv ;
int err = 0 ;
ff_framesync_uninit ( & s - > fs ) ;
pthread_mutex_lock ( & s - > lock ) ;
s - > eof = 1 ;
pthread_cond_signal ( & s - > cond ) ;
pthread_mutex_unlock ( & s - > lock ) ;
if ( ! s - > frame_cnt )
goto clean_up ;
if ( s - > vmaf_thread_created )
{
pthread_join ( s - > vmaf_thread , NULL ) ;
s - > vmaf_thread_created = 0 ;
err = vmaf_read_pictures ( s - > vmaf , NULL , NULL , 0 ) ;
if ( err ) {
av_log ( ctx , AV_LOG_ERROR ,
" problem flushing libvmaf context. \n " ) ;
}
av_frame_free ( & s - > gref ) ;
av_frame_free ( & s - > gmain ) ;
for ( unsigned i = 0 ; i < s - > model_cnt ; i + + ) {
double vmaf_score ;
err = vmaf_score_pooled ( s - > vmaf , s - > model [ i ] , pool_method_map ( s - > pool ) ,
& vmaf_score , 0 , s - > frame_cnt - 1 ) ;
if ( err ) {
av_log ( ctx , AV_LOG_ERROR ,
" problem getting pooled vmaf score. \n " ) ;
}
av_log ( ctx , AV_LOG_INFO , " VMAF score: %f \n " , vmaf_score ) ;
}
if ( s - > vmaf ) {
if ( s - > log_path & & ! err )
vmaf_write_output ( s - > vmaf , s - > log_path , log_fmt_map ( s - > log_fmt ) ) ;
}
clean_up :
if ( s - > model ) {
for ( unsigned i = 0 ; i < s - > model_cnt ; i + + ) {
if ( s - > model [ i ] )
vmaf_model_destroy ( s - > model [ i ] ) ;
}
av_free ( s - > model ) ;
}
pthread_mutex_destroy ( & s - > lock ) ;
pthread_cond_destroy ( & s - > cond ) ;
if ( s - > vmaf )
vmaf_close ( s - > vmaf ) ;
}
static const AVFilterPad libvmaf_inputs [ ] = {