2009-11-11 23:50:21 +00:00
/*
2010-11-28 10:22:58 +00:00
* Copyright (c) 2007 Bobby Bingham
2009-11-11 23:50:21 +00:00
*
* 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
*/
/**
2010-04-20 14:45:34 +00:00
* @file
2009-11-11 23:50:21 +00:00
* scale video filter
*/
2012-08-06 16:49:32 +03:00
#include <stdio.h>
#include <string.h>
2009-11-11 23:50:21 +00:00
#include "avfilter.h"
2012-05-30 10:12:55 +02:00
#include "formats.h"
2012-06-12 20:12:42 +02:00
#include "internal.h"
2012-05-30 11:20:32 +02:00
#include "video.h"
2011-03-27 22:58:08 +02:00
#include "libavutil/avstring.h"
#include "libavutil/eval.h"
2012-08-06 16:49:32 +03:00
#include "libavutil/internal.h"
2011-06-04 12:58:23 +01:00
#include "libavutil/mathematics.h"
2012-04-09 06:05:50 +02:00
#include "libavutil/opt.h"
2012-10-29 11:48:53 +01:00
#include "libavutil/parseutils.h"
2010-01-31 16:33:29 +00:00
#include "libavutil/pixdesc.h"
2012-04-08 15:11:13 +02:00
#include "libavutil/imgutils.h"
2011-03-28 00:09:58 +02:00
#include "libavutil/avassert.h"
2009-11-11 23:50:21 +00:00
#include "libswscale/swscale.h"
2012-02-20 00:42:33 -08:00
static const char * const var_names [] = {
2011-03-27 22:58:08 +02:00
"in_w" , "iw" ,
"in_h" , "ih" ,
"out_w" , "ow" ,
"out_h" , "oh" ,
2011-07-27 10:58:24 +02:00
"a" ,
2011-07-02 17:27:31 +02:00
"sar" ,
2011-07-27 10:58:24 +02:00
"dar" ,
2011-03-27 22:58:08 +02:00
"hsub" ,
"vsub" ,
2013-11-04 14:18:09 +01:00
"ohsub" ,
"ovsub" ,
2011-03-27 22:58:08 +02:00
NULL
};
enum var_name {
VAR_IN_W , VAR_IW ,
VAR_IN_H , VAR_IH ,
VAR_OUT_W , VAR_OW ,
VAR_OUT_H , VAR_OH ,
2011-07-27 10:58:24 +02:00
VAR_A ,
2011-07-02 17:27:31 +02:00
VAR_SAR ,
2011-07-27 10:58:24 +02:00
VAR_DAR ,
2011-03-27 22:58:08 +02:00
VAR_HSUB ,
VAR_VSUB ,
2013-11-04 14:18:09 +01:00
VAR_OHSUB ,
VAR_OVSUB ,
2011-03-27 22:58:08 +02:00
VARS_NB
};
2016-01-18 18:44:09 +01:00
enum EvalMode {
EVAL_MODE_INIT ,
EVAL_MODE_FRAME ,
EVAL_MODE_NB
};
2014-04-11 11:54:15 +02:00
typedef struct ScaleContext {
2012-10-06 12:30:26 +02:00
const AVClass * class ;
2009-11-11 23:50:21 +00:00
struct SwsContext * sws ; ///< software scaler context
2011-03-28 00:09:58 +02:00
struct SwsContext * isws [ 2 ]; ///< software scaler context for interlaced material
2013-08-15 22:42:56 +02:00
AVDictionary * opts ;
2009-11-11 23:50:21 +00:00
/**
* New dimensions. Special values are:
* 0 = original width/height
* -1 = keep original aspect
2014-01-25 15:02:15 +01:00
* -N = try to keep aspect but make sure it is divisible by N
2009-11-11 23:50:21 +00:00
*/
int w , h ;
2012-10-29 11:48:53 +01:00
char * size_str ;
2010-05-07 09:20:45 +00:00
unsigned int flags ; ///sws flags
2015-08-25 23:04:12 +02:00
double param [ 2 ]; // sws params
2009-11-11 23:50:21 +00:00
int hsub , vsub ; ///< chroma subsampling
int slice_y ; ///< top of current output slice
int input_is_pal ; ///< set to 1 if the input format is paletted
2012-04-08 14:59:53 +02:00
int output_is_pal ; ///< set to 1 if the output format is paletted
2011-03-28 00:09:58 +02:00
int interlaced ;
2011-03-27 22:58:08 +02:00
2012-10-06 12:30:26 +02:00
char * w_expr ; ///< width expression string
char * h_expr ; ///< height expression string
2013-02-25 21:21:29 +01:00
char * flags_str ;
2013-07-20 22:11:50 +02:00
char * in_color_matrix ;
char * out_color_matrix ;
2013-07-20 22:12:42 +02:00
int in_range ;
int out_range ;
2013-07-14 05:03:06 +02:00
int out_h_chr_pos ;
int out_v_chr_pos ;
2013-07-24 18:33:58 +02:00
int in_h_chr_pos ;
int in_v_chr_pos ;
2013-08-03 16:30:00 -07:00
int force_original_aspect_ratio ;
2015-12-18 15:44:33 +01:00
int nb_slices ;
2016-01-18 18:44:09 +01:00
int eval_mode ; ///< expression evaluation mode
2009-11-11 23:50:21 +00:00
} ScaleContext ;
2015-08-15 18:38:06 +02:00
AVFilter ff_vf_scale2ref ;
2013-08-15 22:42:56 +02:00
static av_cold int init_dict ( AVFilterContext * ctx , AVDictionary ** opts )
2009-11-11 23:50:21 +00:00
{
ScaleContext * scale = ctx -> priv ;
2012-10-06 12:30:26 +02:00
int ret ;
2012-10-29 11:48:53 +01:00
if ( scale -> size_str && ( scale -> w_expr || scale -> h_expr )) {
av_log ( ctx , AV_LOG_ERROR ,
"Size and width/height expressions cannot be set at the same time. \n " );
return AVERROR ( EINVAL );
}
2013-04-12 02:34:40 +02:00
if ( scale -> w_expr && ! scale -> h_expr )
FFSWAP ( char * , scale -> w_expr , scale -> size_str );
2012-10-29 11:48:53 +01:00
if ( scale -> size_str ) {
char buf [ 32 ];
if (( ret = av_parse_video_size ( & scale -> w , & scale -> h , scale -> size_str )) < 0 ) {
av_log ( ctx , AV_LOG_ERROR ,
"Invalid size '%s' \n " , scale -> size_str );
return ret ;
}
snprintf ( buf , sizeof ( buf ) - 1 , "%d" , scale -> w );
av_opt_set ( scale , "w" , buf , 0 );
snprintf ( buf , sizeof ( buf ) - 1 , "%d" , scale -> h );
av_opt_set ( scale , "h" , buf , 0 );
}
if ( ! scale -> w_expr )
av_opt_set ( scale , "w" , "iw" , 0 );
if ( ! scale -> h_expr )
av_opt_set ( scale , "h" , "ih" , 0 );
2012-10-31 15:45:28 +01:00
av_log ( ctx , AV_LOG_VERBOSE , "w:%s h:%s flags:'%s' interl:%d \n " ,
scale -> w_expr , scale -> h_expr , ( char * ) av_x_if_null ( scale -> flags_str , "" ), scale -> interlaced );
2011-03-27 22:58:08 +02:00
2013-10-06 12:33:32 +02:00
scale -> flags = 0 ;
2013-04-12 02:34:40 +02:00
2012-10-06 12:30:26 +02:00
if ( scale -> flags_str ) {
const AVClass * class = sws_get_class ();
const AVOption * o = av_opt_find ( & class , "sws_flags" , NULL , 0 ,
AV_OPT_SEARCH_FAKE_OBJ );
int ret = av_opt_eval_flags ( & class , o , scale -> flags_str , & scale -> flags );
if ( ret < 0 )
return ret ;
2010-05-07 09:20:45 +00:00
}
2013-08-15 22:42:56 +02:00
scale -> opts = * opts ;
* opts = NULL ;
2009-11-11 23:50:21 +00:00
return 0 ;
}
static av_cold void uninit ( AVFilterContext * ctx )
{
ScaleContext * scale = ctx -> priv ;
sws_freeContext ( scale -> sws );
2011-03-28 00:09:58 +02:00
sws_freeContext ( scale -> isws [ 0 ]);
sws_freeContext ( scale -> isws [ 1 ]);
2009-11-11 23:50:21 +00:00
scale -> sws = NULL ;
2013-08-15 22:42:56 +02:00
av_dict_free ( & scale -> opts );
2009-11-11 23:50:21 +00:00
}
static int query_formats ( AVFilterContext * ctx )
{
AVFilterFormats * formats ;
2012-10-06 12:10:34 +02:00
enum AVPixelFormat pix_fmt ;
2010-01-08 23:48:32 +00:00
int ret ;
2009-11-11 23:50:21 +00:00
if ( ctx -> inputs [ 0 ]) {
2014-05-24 11:15:15 +02:00
const AVPixFmtDescriptor * desc = NULL ;
2010-01-08 23:48:32 +00:00
formats = NULL ;
2014-05-24 11:15:15 +02:00
while (( desc = av_pix_fmt_desc_next ( desc ))) {
pix_fmt = av_pix_fmt_desc_get_id ( desc );
2013-04-26 14:49:43 +02:00
if (( sws_isSupportedInput ( pix_fmt ) ||
sws_isSupportedEndiannessConversion ( pix_fmt ))
2012-05-30 10:12:55 +02:00
&& ( ret = ff_add_format ( & formats , pix_fmt )) < 0 ) {
2010-01-08 23:48:32 +00:00
return ret ;
}
2014-05-24 11:15:15 +02:00
}
2015-10-04 23:39:25 -04:00
if (( ret = ff_formats_ref ( formats , & ctx -> inputs [ 0 ] -> out_formats )) < 0 )
return ret ;
2009-11-11 23:50:21 +00:00
}
if ( ctx -> outputs [ 0 ]) {
2014-05-24 11:15:15 +02:00
const AVPixFmtDescriptor * desc = NULL ;
2010-01-08 23:48:32 +00:00
formats = NULL ;
2014-05-24 11:15:15 +02:00
while (( desc = av_pix_fmt_desc_next ( desc ))) {
pix_fmt = av_pix_fmt_desc_get_id ( desc );
2013-05-07 11:23:55 +02:00
if (( sws_isSupportedOutput ( pix_fmt ) || pix_fmt == AV_PIX_FMT_PAL8 ||
2013-04-26 14:49:43 +02:00
sws_isSupportedEndiannessConversion ( pix_fmt ))
2012-05-30 10:12:55 +02:00
&& ( ret = ff_add_format ( & formats , pix_fmt )) < 0 ) {
2010-01-08 23:48:32 +00:00
return ret ;
}
2014-05-24 11:15:15 +02:00
}
2015-10-04 23:39:25 -04:00
if (( ret = ff_formats_ref ( formats , & ctx -> outputs [ 0 ] -> in_formats )) < 0 )
return ret ;
2009-11-11 23:50:21 +00:00
}
return 0 ;
}
2013-07-20 22:11:50 +02:00
static const int * parse_yuv_type ( const char * s , enum AVColorSpace colorspace )
{
if ( ! s )
s = "bt601" ;
if ( s && strstr ( s , "bt709" )) {
colorspace = AVCOL_SPC_BT709 ;
} else if ( s && strstr ( s , "fcc" )) {
colorspace = AVCOL_SPC_FCC ;
} else if ( s && strstr ( s , "smpte240m" )) {
colorspace = AVCOL_SPC_SMPTE240M ;
} else if ( s && ( strstr ( s , "bt601" ) || strstr ( s , "bt470" ) || strstr ( s , "smpte170m" ))) {
colorspace = AVCOL_SPC_BT470BG ;
2016-04-15 14:26:37 -04:00
} else if ( s && strstr ( s , "bt2020" )) {
colorspace = AVCOL_SPC_BT2020_NCL ;
2013-07-20 22:11:50 +02:00
}
2016-04-15 14:26:37 -04:00
if ( colorspace < 1 || colorspace > 10 || colorspace == 8 ) {
2013-07-20 22:11:50 +02:00
colorspace = AVCOL_SPC_BT470BG ;
}
2013-07-26 23:03:58 +02:00
return sws_getCoefficients ( colorspace );
2013-07-20 22:11:50 +02:00
}
2009-11-11 23:50:21 +00:00
static int config_props ( AVFilterLink * outlink )
{
AVFilterContext * ctx = outlink -> src ;
2015-08-15 18:38:06 +02:00
AVFilterLink * inlink0 = outlink -> src -> inputs [ 0 ];
AVFilterLink * inlink = ctx -> filter == & ff_vf_scale2ref ?
outlink -> src -> inputs [ 1 ] :
outlink -> src -> inputs [ 0 ];
2012-10-08 20:54:00 +02:00
enum AVPixelFormat outfmt = outlink -> format ;
2009-11-11 23:50:21 +00:00
ScaleContext * scale = ctx -> priv ;
2012-10-06 13:29:37 +02:00
const AVPixFmtDescriptor * desc = av_pix_fmt_desc_get ( inlink -> format );
2013-11-04 14:18:09 +01:00
const AVPixFmtDescriptor * out_desc = av_pix_fmt_desc_get ( outlink -> format );
2009-11-11 23:50:21 +00:00
int64_t w , h ;
2011-03-27 22:58:08 +02:00
double var_values [ VARS_NB ], res ;
char * expr ;
int ret ;
2014-01-25 15:00:02 +01:00
int factor_w , factor_h ;
2011-03-27 22:58:08 +02:00
var_values [ VAR_IN_W ] = var_values [ VAR_IW ] = inlink -> w ;
var_values [ VAR_IN_H ] = var_values [ VAR_IH ] = inlink -> h ;
var_values [ VAR_OUT_W ] = var_values [ VAR_OW ] = NAN ;
var_values [ VAR_OUT_H ] = var_values [ VAR_OH ] = NAN ;
2012-10-06 13:23:05 +02:00
var_values [ VAR_A ] = ( double ) inlink -> w / inlink -> h ;
2011-07-02 17:27:31 +02:00
var_values [ VAR_SAR ] = inlink -> sample_aspect_ratio . num ?
2012-10-05 14:45:30 +02:00
( double ) inlink -> sample_aspect_ratio . num / inlink -> sample_aspect_ratio . den : 1 ;
2011-07-27 10:58:24 +02:00
var_values [ VAR_DAR ] = var_values [ VAR_A ] * var_values [ VAR_SAR ];
2012-10-06 13:29:37 +02:00
var_values [ VAR_HSUB ] = 1 << desc -> log2_chroma_w ;
var_values [ VAR_VSUB ] = 1 << desc -> log2_chroma_h ;
2013-11-04 14:18:09 +01:00
var_values [ VAR_OHSUB ] = 1 << out_desc -> log2_chroma_w ;
var_values [ VAR_OVSUB ] = 1 << out_desc -> log2_chroma_h ;
2011-03-27 22:58:08 +02:00
/* evaluate width and height */
av_expr_parse_and_eval ( & res , ( expr = scale -> w_expr ),
var_names , var_values ,
NULL , NULL , NULL , NULL , NULL , 0 , ctx );
2011-04-17 17:23:48 +02:00
scale -> w = var_values [ VAR_OUT_W ] = var_values [ VAR_OW ] = res ;
2011-03-27 22:58:08 +02:00
if (( ret = av_expr_parse_and_eval ( & res , ( expr = scale -> h_expr ),
var_names , var_values ,
NULL , NULL , NULL , NULL , NULL , 0 , ctx )) < 0 )
goto fail ;
2011-04-17 17:23:48 +02:00
scale -> h = var_values [ VAR_OUT_H ] = var_values [ VAR_OH ] = res ;
2011-03-27 22:58:08 +02:00
/* evaluate again the width, as it may depend on the output height */
if (( ret = av_expr_parse_and_eval ( & res , ( expr = scale -> w_expr ),
var_names , var_values ,
NULL , NULL , NULL , NULL , NULL , 0 , ctx )) < 0 )
goto fail ;
scale -> w = res ;
w = scale -> w ;
h = scale -> h ;
2014-01-25 14:40:48 +01:00
/* Check if it is requested that the result has to be divisible by a some
2014-01-25 16:54:24 +01:00
* factor (w or h = -n with n being the factor). */
2014-01-25 15:00:02 +01:00
factor_w = 1 ;
factor_h = 1 ;
2014-01-25 14:40:48 +01:00
if ( w < - 1 ) {
factor_w = - w ;
2011-03-27 22:58:08 +02:00
}
2014-01-25 14:40:48 +01:00
if ( h < - 1 ) {
factor_h = - h ;
}
2014-01-25 16:54:24 +01:00
if ( w < 0 && h < 0 )
2011-03-27 22:58:08 +02:00
scale -> w = scale -> h = 0 ;
2009-11-11 23:50:21 +00:00
if ( ! ( w = scale -> w ))
w = inlink -> w ;
if ( ! ( h = scale -> h ))
h = inlink -> h ;
2014-01-25 14:40:48 +01:00
/* Make sure that the result is divisible by the factor we determined
* earlier. If no factor was set, it is nothing will happen as the default
* factor is 1 */
2014-01-25 16:54:24 +01:00
if ( w < 0 )
2014-01-25 16:48:13 +01:00
w = av_rescale ( h , inlink -> w , inlink -> h * factor_w ) * factor_w ;
2014-01-25 16:54:24 +01:00
if ( h < 0 )
2014-01-25 16:48:13 +01:00
h = av_rescale ( w , inlink -> h , inlink -> w * factor_h ) * factor_h ;
2014-01-25 14:40:48 +01:00
/* Note that force_original_aspect_ratio may overwrite the previous set
* dimensions so that it is not divisible by the set factors anymore. */
2013-08-03 16:30:00 -07:00
if ( scale -> force_original_aspect_ratio ) {
int tmp_w = av_rescale ( h , inlink -> w , inlink -> h );
int tmp_h = av_rescale ( w , inlink -> h , inlink -> w );
if ( scale -> force_original_aspect_ratio == 1 ) {
w = FFMIN ( tmp_w , w );
h = FFMIN ( tmp_h , h );
} else {
w = FFMAX ( tmp_w , w );
h = FFMAX ( tmp_h , h );
}
}
2009-11-11 23:50:21 +00:00
if ( w > INT_MAX || h > INT_MAX ||
( h * inlink -> w ) > INT_MAX ||
( w * inlink -> h ) > INT_MAX )
av_log ( ctx , AV_LOG_ERROR , "Rescaled value for width or height is too big. \n " );
outlink -> w = w ;
outlink -> h = h ;
/* TODO: make algorithm configurable */
2013-05-12 15:41:49 +02:00
scale -> input_is_pal = desc -> flags & AV_PIX_FMT_FLAG_PAL ||
desc -> flags & AV_PIX_FMT_FLAG_PSEUDOPAL ;
2012-10-08 20:54:00 +02:00
if ( outfmt == AV_PIX_FMT_PAL8 ) outfmt = AV_PIX_FMT_BGR8 ;
2013-05-15 10:50:20 +02:00
scale -> output_is_pal = av_pix_fmt_desc_get ( outfmt ) -> flags & AV_PIX_FMT_FLAG_PAL ||
av_pix_fmt_desc_get ( outfmt ) -> flags & AV_PIX_FMT_FLAG_PSEUDOPAL ;
2009-11-11 23:50:21 +00:00
2011-05-02 12:35:39 +02:00
if ( scale -> sws )
2011-03-09 03:30:24 +01:00
sws_freeContext ( scale -> sws );
2013-07-14 05:19:46 +02:00
if ( scale -> isws [ 0 ])
sws_freeContext ( scale -> isws [ 0 ]);
if ( scale -> isws [ 1 ])
sws_freeContext ( scale -> isws [ 1 ]);
scale -> isws [ 0 ] = scale -> isws [ 1 ] = scale -> sws = NULL ;
2015-08-15 18:38:06 +02:00
if ( inlink0 -> w == outlink -> w &&
inlink0 -> h == outlink -> h &&
2015-08-30 23:49:04 +02:00
! scale -> out_color_matrix &&
2015-08-30 23:46:11 +02:00
scale -> in_range == scale -> out_range &&
2015-08-15 18:38:06 +02:00
inlink0 -> format == outlink -> format )
2013-07-14 05:19:46 +02:00
;
2012-03-29 07:02:27 +02:00
else {
2013-07-14 05:19:46 +02:00
struct SwsContext ** swscs [ 3 ] = { & scale -> sws , & scale -> isws [ 0 ], & scale -> isws [ 1 ]};
int i ;
for ( i = 0 ; i < 3 ; i ++ ) {
struct SwsContext ** s = swscs [ i ];
* s = sws_alloc_context ();
if ( !* s )
return AVERROR ( ENOMEM );
2015-08-15 18:38:06 +02:00
av_opt_set_int ( * s , "srcw" , inlink0 -> w , 0 );
av_opt_set_int ( * s , "srch" , inlink0 -> h >> !! i , 0 );
av_opt_set_int ( * s , "src_format" , inlink0 -> format , 0 );
2013-07-14 05:19:46 +02:00
av_opt_set_int ( * s , "dstw" , outlink -> w , 0 );
av_opt_set_int ( * s , "dsth" , outlink -> h >> !! i , 0 );
av_opt_set_int ( * s , "dst_format" , outfmt , 0 );
av_opt_set_int ( * s , "sws_flags" , scale -> flags , 0 );
2015-08-31 10:07:34 +02:00
av_opt_set_int ( * s , "param0" , scale -> param [ 0 ], 0 );
av_opt_set_int ( * s , "param1" , scale -> param [ 1 ], 0 );
2015-09-01 09:17:26 +02:00
if ( scale -> in_range != AVCOL_RANGE_UNSPECIFIED )
av_opt_set_int ( * s , "src_range" ,
scale -> in_range == AVCOL_RANGE_JPEG , 0 );
if ( scale -> out_range != AVCOL_RANGE_UNSPECIFIED )
av_opt_set_int ( * s , "dst_range" ,
scale -> out_range == AVCOL_RANGE_JPEG , 0 );
2013-07-14 05:19:46 +02:00
2015-08-08 14:43:12 +02:00
if ( scale -> opts ) {
AVDictionaryEntry * e = NULL ;
while (( e = av_dict_get ( scale -> opts , "" , e , AV_DICT_IGNORE_SUFFIX ))) {
if (( ret = av_opt_set ( * s , e -> key , e -> value , 0 )) < 0 )
return ret ;
}
}
2015-09-23 15:06:01 +02:00
/* Override YUV420P default settings to have the correct (MPEG-2) chroma positions
2014-12-24 23:52:46 +00:00
* MPEG-2 chroma positions are used by convention
* XXX: support other 4:2:0 pixel formats */
2015-09-23 15:06:01 +02:00
if ( inlink0 -> format == AV_PIX_FMT_YUV420P && scale -> in_v_chr_pos == - 513 ) {
2014-12-24 23:52:46 +00:00
scale -> in_v_chr_pos = ( i == 0 ) ? 128 : ( i == 1 ) ? 64 : 192 ;
}
2015-09-23 15:06:01 +02:00
if ( outlink -> format == AV_PIX_FMT_YUV420P && scale -> out_v_chr_pos == - 513 ) {
2014-12-24 23:52:46 +00:00
scale -> out_v_chr_pos = ( i == 0 ) ? 128 : ( i == 1 ) ? 64 : 192 ;
}
2013-07-24 18:33:58 +02:00
av_opt_set_int ( * s , "src_h_chr_pos" , scale -> in_h_chr_pos , 0 );
av_opt_set_int ( * s , "src_v_chr_pos" , scale -> in_v_chr_pos , 0 );
2013-07-14 05:03:06 +02:00
av_opt_set_int ( * s , "dst_h_chr_pos" , scale -> out_h_chr_pos , 0 );
av_opt_set_int ( * s , "dst_v_chr_pos" , scale -> out_v_chr_pos , 0 );
2013-07-14 05:19:46 +02:00
if (( ret = sws_init_context ( * s , NULL , NULL )) < 0 )
return ret ;
2013-07-18 11:03:56 +02:00
if ( ! scale -> interlaced )
break ;
2013-07-14 05:19:46 +02:00
}
2012-03-29 07:02:27 +02:00
}
2010-05-07 22:12:13 +00:00
2011-08-27 01:49:55 +02:00
if ( inlink -> sample_aspect_ratio . num ){
outlink -> sample_aspect_ratio = av_mul_q (( AVRational ){ outlink -> h * inlink -> w , outlink -> w * inlink -> h }, inlink -> sample_aspect_ratio );
} else
outlink -> sample_aspect_ratio = inlink -> sample_aspect_ratio ;
2012-07-14 18:26:04 +02:00
av_log ( ctx , AV_LOG_VERBOSE , "w:%d h:%d fmt:%s sar:%d/%d -> w:%d h:%d fmt:%s sar:%d/%d flags:0x%0x \n " ,
2012-10-12 17:04:58 +02:00
inlink -> w , inlink -> h , av_get_pix_fmt_name ( inlink -> format ),
2012-03-17 12:28:35 +01:00
inlink -> sample_aspect_ratio . num , inlink -> sample_aspect_ratio . den ,
2012-10-12 17:04:58 +02:00
outlink -> w , outlink -> h , av_get_pix_fmt_name ( outlink -> format ),
2012-03-17 12:28:35 +01:00
outlink -> sample_aspect_ratio . num , outlink -> sample_aspect_ratio . den ,
scale -> flags );
2011-01-16 20:11:46 +00:00
return 0 ;
2011-03-27 22:58:08 +02:00
fail :
av_log ( NULL , AV_LOG_ERROR ,
2011-12-04 23:33:40 +01:00
"Error when evaluating the expression '%s'. \n "
"Maybe the expression for out_w:'%s' or for out_h:'%s' is self-referencing. \n " ,
expr , scale -> w_expr , scale -> h_expr );
2011-03-27 22:58:08 +02:00
return ret ;
2009-11-11 23:50:21 +00:00
}
2015-08-15 18:38:06 +02:00
static int config_props_ref ( AVFilterLink * outlink )
{
AVFilterLink * inlink = outlink -> src -> inputs [ 1 ];
outlink -> w = inlink -> w ;
outlink -> h = inlink -> h ;
outlink -> sample_aspect_ratio = inlink -> sample_aspect_ratio ;
2015-08-18 15:06:10 +02:00
outlink -> time_base = inlink -> time_base ;
2015-08-15 18:38:06 +02:00
return 0 ;
}
2015-08-18 15:10:30 +02:00
static int request_frame ( AVFilterLink * outlink )
{
return ff_request_frame ( outlink -> src -> inputs [ 0 ]);
}
static int request_frame_ref ( AVFilterLink * outlink )
{
return ff_request_frame ( outlink -> src -> inputs [ 1 ]);
}
2013-03-10 01:30:30 +01:00
static int scale_slice ( AVFilterLink * link , AVFrame * out_buf , AVFrame * cur_pic , struct SwsContext * sws , int y , int h , int mul , int field )
2009-11-11 23:50:21 +00:00
{
ScaleContext * scale = link -> dst -> priv ;
2011-06-01 20:26:54 +02:00
const uint8_t * in [ 4 ];
uint8_t * out [ 4 ];
2011-03-28 00:09:58 +02:00
int in_stride [ 4 ], out_stride [ 4 ];
int i ;
for ( i = 0 ; i < 4 ; i ++ ){
int vsub = (( i + 1 ) & 2 ) ? scale -> vsub : 0 ;
in_stride [ i ] = cur_pic -> linesize [ i ] * mul ;
out_stride [ i ] = out_buf -> linesize [ i ] * mul ;
in [ i ] = cur_pic -> data [ i ] + (( y >> vsub ) + field ) * cur_pic -> linesize [ i ];
out [ i ] = out_buf -> data [ i ] + field * out_buf -> linesize [ i ];
}
2012-04-08 14:59:53 +02:00
if ( scale -> input_is_pal )
2011-03-28 00:09:58 +02:00
in [ 1 ] = cur_pic -> data [ 1 ];
2012-04-08 14:59:53 +02:00
if ( scale -> output_is_pal )
2011-03-28 00:09:58 +02:00
out [ 1 ] = out_buf -> data [ 1 ];
return sws_scale ( sws , in , in_stride , y / mul , h ,
out , out_stride );
}
2012-11-28 08:41:07 +01:00
static int filter_frame ( AVFilterLink * link , AVFrame * in )
2009-11-11 23:50:21 +00:00
{
ScaleContext * scale = link -> dst -> priv ;
2012-11-29 01:19:35 +01:00
AVFilterLink * outlink = link -> dst -> outputs [ 0 ];
2012-11-28 08:41:07 +01:00
AVFrame * out ;
2012-11-29 01:19:35 +01:00
const AVPixFmtDescriptor * desc = av_pix_fmt_desc_get ( link -> format );
char buf [ 32 ];
2013-07-25 01:27:00 +02:00
int in_range ;
2009-11-11 23:50:21 +00:00
2015-04-13 13:45:41 +02:00
if ( av_frame_get_colorspace ( in ) == AVCOL_SPC_YCGCO )
av_log ( link -> dst , AV_LOG_WARNING , "Detected unsupported YCgCo colorspace. \n " );
2013-03-10 01:30:30 +01:00
if ( in -> width != link -> w
|| in -> height != link -> h
2016-01-18 18:44:09 +01:00
|| in -> format != link -> format
|| in -> sample_aspect_ratio . den != link -> sample_aspect_ratio . den || in -> sample_aspect_ratio . num != link -> sample_aspect_ratio . num ) {
2012-11-29 01:19:35 +01:00
int ret ;
2016-01-18 18:44:09 +01:00
if ( scale -> eval_mode == EVAL_MODE_INIT ) {
snprintf ( buf , sizeof ( buf ) - 1 , "%d" , outlink -> w );
av_opt_set ( scale , "w" , buf , 0 );
snprintf ( buf , sizeof ( buf ) - 1 , "%d" , outlink -> h );
av_opt_set ( scale , "h" , buf , 0 );
}
2012-11-29 01:19:35 +01:00
link -> dst -> inputs [ 0 ] -> format = in -> format ;
2013-03-10 01:30:30 +01:00
link -> dst -> inputs [ 0 ] -> w = in -> width ;
link -> dst -> inputs [ 0 ] -> h = in -> height ;
2012-11-29 01:19:35 +01:00
2016-01-18 18:44:09 +01:00
link -> dst -> inputs [ 0 ] -> sample_aspect_ratio . den = in -> sample_aspect_ratio . den ;
link -> dst -> inputs [ 0 ] -> sample_aspect_ratio . num = in -> sample_aspect_ratio . num ;
2012-11-29 01:19:35 +01:00
if (( ret = config_props ( outlink )) < 0 )
return ret ;
2012-03-29 07:02:27 +02:00
}
2012-11-29 01:19:35 +01:00
if ( ! scale -> sws )
return ff_filter_frame ( outlink , in );
scale -> hsub = desc -> log2_chroma_w ;
scale -> vsub = desc -> log2_chroma_h ;
2012-11-28 08:41:07 +01:00
out = ff_get_video_buffer ( outlink , outlink -> w , outlink -> h );
2012-11-29 01:19:35 +01:00
if ( ! out ) {
2012-11-28 08:41:07 +01:00
av_frame_free ( & in );
2012-11-29 01:19:35 +01:00
return AVERROR ( ENOMEM );
}
2012-11-28 08:41:07 +01:00
av_frame_copy_props ( out , in );
out -> width = outlink -> w ;
out -> height = outlink -> h ;
2012-11-29 01:19:35 +01:00
if ( scale -> output_is_pal )
avpriv_set_systematic_pal2 (( uint32_t * ) out -> data [ 1 ], outlink -> format == AV_PIX_FMT_PAL8 ? AV_PIX_FMT_BGR8 : outlink -> format );
2013-07-25 01:27:00 +02:00
in_range = av_frame_get_color_range ( in );
2013-07-20 22:11:50 +02:00
if ( scale -> in_color_matrix
|| scale -> out_color_matrix
|| scale -> in_range != AVCOL_RANGE_UNSPECIFIED
2013-07-25 01:27:00 +02:00
|| in_range != AVCOL_RANGE_UNSPECIFIED
2013-07-20 22:11:50 +02:00
|| scale -> out_range != AVCOL_RANGE_UNSPECIFIED ) {
int in_full , out_full , brightness , contrast , saturation ;
const int * inv_table , * table ;
sws_getColorspaceDetails ( scale -> sws , ( int ** ) & inv_table , & in_full ,
( int ** ) & table , & out_full ,
& brightness , & contrast , & saturation );
if ( scale -> in_color_matrix )
inv_table = parse_yuv_type ( scale -> in_color_matrix , av_frame_get_colorspace ( in ));
if ( scale -> out_color_matrix )
table = parse_yuv_type ( scale -> out_color_matrix , AVCOL_SPC_UNSPECIFIED );
2015-08-31 00:17:50 +02:00
else if ( scale -> in_color_matrix )
table = inv_table ;
2013-07-20 22:11:50 +02:00
2013-07-20 22:12:42 +02:00
if ( scale -> in_range != AVCOL_RANGE_UNSPECIFIED )
in_full = ( scale -> in_range == AVCOL_RANGE_JPEG );
2013-07-25 01:27:00 +02:00
else if ( in_range != AVCOL_RANGE_UNSPECIFIED )
in_full = ( in_range == AVCOL_RANGE_JPEG );
2013-07-20 22:12:42 +02:00
if ( scale -> out_range != AVCOL_RANGE_UNSPECIFIED )
out_full = ( scale -> out_range == AVCOL_RANGE_JPEG );
2013-07-20 22:11:50 +02:00
sws_setColorspaceDetails ( scale -> sws , inv_table , in_full ,
table , out_full ,
brightness , contrast , saturation );
if ( scale -> isws [ 0 ])
2013-07-20 22:20:25 +02:00
sws_setColorspaceDetails ( scale -> isws [ 0 ], inv_table , in_full ,
table , out_full ,
brightness , contrast , saturation );
2013-07-20 22:11:50 +02:00
if ( scale -> isws [ 1 ])
2013-07-20 22:20:25 +02:00
sws_setColorspaceDetails ( scale -> isws [ 1 ], inv_table , in_full ,
table , out_full ,
brightness , contrast , saturation );
2015-12-31 00:01:21 +01:00
av_frame_set_color_range ( out , out_full ? AVCOL_RANGE_JPEG : AVCOL_RANGE_MPEG );
2013-07-20 22:11:50 +02:00
}
2012-11-28 08:41:07 +01:00
av_reduce ( & out -> sample_aspect_ratio . num , & out -> sample_aspect_ratio . den ,
( int64_t ) in -> sample_aspect_ratio . num * outlink -> h * link -> w ,
( int64_t ) in -> sample_aspect_ratio . den * outlink -> w * link -> h ,
2012-11-29 01:19:35 +01:00
INT_MAX );
2009-11-11 23:50:21 +00:00
2013-03-10 01:30:30 +01:00
if ( scale -> interlaced > 0 || ( scale -> interlaced < 0 && in -> interlaced_frame )){
2012-11-29 01:19:35 +01:00
scale_slice ( link , out , in , scale -> isws [ 0 ], 0 , ( link -> h + 1 ) / 2 , 2 , 0 );
scale_slice ( link , out , in , scale -> isws [ 1 ], 0 , link -> h / 2 , 2 , 1 );
2015-12-18 15:44:33 +01:00
} else if ( scale -> nb_slices ) {
int i , slice_h , slice_start , slice_end = 0 ;
const int nb_slices = FFMIN ( scale -> nb_slices , link -> h );
for ( i = 0 ; i < nb_slices ; i ++ ) {
slice_start = slice_end ;
slice_end = ( link -> h * ( i + 1 )) / nb_slices ;
slice_h = slice_end - slice_start ;
scale_slice ( link , out , in , scale -> sws , slice_start , slice_h , 1 , 0 );
}
2011-03-28 00:09:58 +02:00
} else {
2012-11-29 01:19:35 +01:00
scale_slice ( link , out , in , scale -> sws , 0 , link -> h , 1 , 0 );
2011-03-28 00:09:58 +02:00
}
2009-11-11 23:50:21 +00:00
2012-11-28 08:41:07 +01:00
av_frame_free ( & in );
2012-11-29 01:19:35 +01:00
return ff_filter_frame ( outlink , out );
2009-11-11 23:50:21 +00:00
}
2015-08-15 18:38:06 +02:00
static int filter_frame_ref ( AVFilterLink * link , AVFrame * in )
{
AVFilterLink * outlink = link -> dst -> outputs [ 1 ];
return ff_filter_frame ( outlink , in );
}
2015-07-21 12:45:43 +02:00
static int process_command ( AVFilterContext * ctx , const char * cmd , const char * args ,
char * res , int res_len , int flags )
{
ScaleContext * scale = ctx -> priv ;
int ret ;
if ( ! strcmp ( cmd , "width" ) || ! strcmp ( cmd , "w" )
|| ! strcmp ( cmd , "height" ) || ! strcmp ( cmd , "h" )) {
int old_w = scale -> w ;
int old_h = scale -> h ;
AVFilterLink * outlink = ctx -> outputs [ 0 ];
av_opt_set ( scale , cmd , args , 0 );
if (( ret = config_props ( outlink )) < 0 ) {
scale -> w = old_w ;
scale -> h = old_h ;
}
} else
ret = AVERROR ( ENOSYS );
return ret ;
}
2013-08-15 22:42:56 +02:00
static const AVClass * child_class_next ( const AVClass * prev )
{
return prev ? NULL : sws_get_class ();
}
2013-02-25 21:21:29 +01:00
#define OFFSET(x) offsetof(ScaleContext, x)
2013-04-10 20:29:25 +02:00
#define FLAGS AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_FILTERING_PARAM
static const AVOption scale_options [] = {
2013-04-12 02:34:40 +02:00
{ "w" , "Output video width" , OFFSET ( w_expr ), AV_OPT_TYPE_STRING , . flags = FLAGS },
{ "width" , "Output video width" , OFFSET ( w_expr ), AV_OPT_TYPE_STRING , . flags = FLAGS },
{ "h" , "Output video height" , OFFSET ( h_expr ), AV_OPT_TYPE_STRING , . flags = FLAGS },
{ "height" , "Output video height" , OFFSET ( h_expr ), AV_OPT_TYPE_STRING , . flags = FLAGS },
2013-02-25 21:21:29 +01:00
{ "flags" , "Flags to pass to libswscale" , OFFSET ( flags_str ), AV_OPT_TYPE_STRING , { . str = "bilinear" }, . flags = FLAGS },
2015-11-21 22:04:54 +01:00
{ "interl" , "set interlacing" , OFFSET ( interlaced ), AV_OPT_TYPE_BOOL , {. i64 = 0 }, - 1 , 1 , FLAGS },
2013-04-10 20:29:25 +02:00
{ "size" , "set video size" , OFFSET ( size_str ), AV_OPT_TYPE_STRING , {. str = NULL }, 0 , FLAGS },
{ "s" , "set video size" , OFFSET ( size_str ), AV_OPT_TYPE_STRING , {. str = NULL }, 0 , FLAGS },
2013-07-27 01:16:07 +02:00
{ "in_color_matrix" , "set input YCbCr type" , OFFSET ( in_color_matrix ), AV_OPT_TYPE_STRING , { . str = "auto" }, . flags = FLAGS },
2013-07-20 22:11:50 +02:00
{ "out_color_matrix" , "set output YCbCr type" , OFFSET ( out_color_matrix ), AV_OPT_TYPE_STRING , { . str = NULL }, . flags = FLAGS },
2013-07-20 22:12:42 +02:00
{ "in_range" , "set input color range" , OFFSET ( in_range ), AV_OPT_TYPE_INT , {. i64 = AVCOL_RANGE_UNSPECIFIED }, 0 , 2 , FLAGS , "range" },
{ "out_range" , "set output color range" , OFFSET ( out_range ), AV_OPT_TYPE_INT , {. i64 = AVCOL_RANGE_UNSPECIFIED }, 0 , 2 , FLAGS , "range" },
{ "auto" , NULL , 0 , AV_OPT_TYPE_CONST , {. i64 = AVCOL_RANGE_UNSPECIFIED }, 0 , 0 , FLAGS , "range" },
{ "full" , NULL , 0 , AV_OPT_TYPE_CONST , {. i64 = AVCOL_RANGE_JPEG }, 0 , 0 , FLAGS , "range" },
{ "jpeg" , NULL , 0 , AV_OPT_TYPE_CONST , {. i64 = AVCOL_RANGE_JPEG }, 0 , 0 , FLAGS , "range" },
{ "mpeg" , NULL , 0 , AV_OPT_TYPE_CONST , {. i64 = AVCOL_RANGE_MPEG }, 0 , 0 , FLAGS , "range" },
2013-10-23 12:50:07 +02:00
{ "tv" , NULL , 0 , AV_OPT_TYPE_CONST , {. i64 = AVCOL_RANGE_MPEG }, 0 , 0 , FLAGS , "range" },
{ "pc" , NULL , 0 , AV_OPT_TYPE_CONST , {. i64 = AVCOL_RANGE_JPEG }, 0 , 0 , FLAGS , "range" },
2014-09-21 12:12:50 +02:00
{ "in_v_chr_pos" , "input vertical chroma position in luma grid/256" , OFFSET ( in_v_chr_pos ), AV_OPT_TYPE_INT , { . i64 = - 513 }, - 513 , 512 , FLAGS },
{ "in_h_chr_pos" , "input horizontal chroma position in luma grid/256" , OFFSET ( in_h_chr_pos ), AV_OPT_TYPE_INT , { . i64 = - 513 }, - 513 , 512 , FLAGS },
{ "out_v_chr_pos" , "output vertical chroma position in luma grid/256" , OFFSET ( out_v_chr_pos ), AV_OPT_TYPE_INT , { . i64 = - 513 }, - 513 , 512 , FLAGS },
{ "out_h_chr_pos" , "output horizontal chroma position in luma grid/256" , OFFSET ( out_h_chr_pos ), AV_OPT_TYPE_INT , { . i64 = - 513 }, - 513 , 512 , FLAGS },
2013-08-03 16:30:00 -07:00
{ "force_original_aspect_ratio" , "decrease or increase w/h if necessary to keep the original AR" , OFFSET ( force_original_aspect_ratio ), AV_OPT_TYPE_INT , { . i64 = 0 }, 0 , 2 , FLAGS , "force_oar" },
{ "disable" , NULL , 0 , AV_OPT_TYPE_CONST , {. i64 = 0 }, 0 , 0 , FLAGS , "force_oar" },
{ "decrease" , NULL , 0 , AV_OPT_TYPE_CONST , {. i64 = 1 }, 0 , 0 , FLAGS , "force_oar" },
{ "increase" , NULL , 0 , AV_OPT_TYPE_CONST , {. i64 = 2 }, 0 , 0 , FLAGS , "force_oar" },
2015-08-25 23:04:12 +02:00
{ "param0" , "Scaler param 0" , OFFSET ( param [ 0 ]), AV_OPT_TYPE_DOUBLE , { . dbl = SWS_PARAM_DEFAULT }, INT_MIN , INT_MAX , FLAGS },
{ "param1" , "Scaler param 1" , OFFSET ( param [ 1 ]), AV_OPT_TYPE_DOUBLE , { . dbl = SWS_PARAM_DEFAULT }, INT_MIN , INT_MAX , FLAGS },
2015-12-18 15:44:33 +01:00
{ "nb_slices" , "set the number of slices (debug purpose only)" , OFFSET ( nb_slices ), AV_OPT_TYPE_INT , { . i64 = 0 }, 0 , INT_MAX , FLAGS },
2016-01-18 18:44:09 +01:00
{ "eval" , "specify when to evaluate expressions" , OFFSET ( eval_mode ), AV_OPT_TYPE_INT , {. i64 = EVAL_MODE_INIT }, 0 , EVAL_MODE_NB - 1 , FLAGS , "eval" },
{ "init" , "eval expressions once during initialization" , 0 , AV_OPT_TYPE_CONST , {. i64 = EVAL_MODE_INIT }, . flags = FLAGS , . unit = "eval" },
{ "frame" , "eval expressions during initialization and per-frame" , 0 , AV_OPT_TYPE_CONST , {. i64 = EVAL_MODE_FRAME }, . flags = FLAGS , . unit = "eval" },
2013-09-07 12:13:50 +00:00
{ NULL }
2013-02-25 21:21:29 +01:00
};
2013-08-15 22:42:56 +02:00
static const AVClass scale_class = {
. class_name = "scale" ,
. item_name = av_default_item_name ,
. option = scale_options ,
. version = LIBAVUTIL_VERSION_INT ,
2014-07-28 05:15:16 +02:00
. category = AV_CLASS_CATEGORY_FILTER ,
2013-08-15 22:42:56 +02:00
. child_class_next = child_class_next ,
};
2013-02-25 21:21:29 +01:00
2012-07-24 14:14:01 +01:00
static const AVFilterPad avfilter_vf_scale_inputs [] = {
{
2013-09-07 12:13:50 +00:00
. name = "default" ,
. type = AVMEDIA_TYPE_VIDEO ,
2012-11-29 01:19:35 +01:00
. filter_frame = filter_frame ,
2012-07-24 14:14:01 +01:00
},
{ NULL }
};
static const AVFilterPad avfilter_vf_scale_outputs [] = {
{
. name = "default" ,
. type = AVMEDIA_TYPE_VIDEO ,
. config_props = config_props ,
},
{ NULL }
};
2013-10-28 07:44:24 +01:00
AVFilter ff_vf_scale = {
2015-07-21 12:45:43 +02:00
. name = "scale" ,
. description = NULL_IF_CONFIG_SMALL ( "Scale the input video size and/or convert the image format." ),
. init_dict = init_dict ,
. uninit = uninit ,
. query_formats = query_formats ,
. priv_size = sizeof ( ScaleContext ),
. priv_class = & scale_class ,
. inputs = avfilter_vf_scale_inputs ,
. outputs = avfilter_vf_scale_outputs ,
. process_command = process_command ,
2009-11-11 23:50:21 +00:00
};
2015-08-15 18:38:06 +02:00
static const AVClass scale2ref_class = {
. class_name = "scale2ref" ,
. item_name = av_default_item_name ,
. option = scale_options ,
. version = LIBAVUTIL_VERSION_INT ,
. category = AV_CLASS_CATEGORY_FILTER ,
. child_class_next = child_class_next ,
};
static const AVFilterPad avfilter_vf_scale2ref_inputs [] = {
{
. name = "default" ,
. type = AVMEDIA_TYPE_VIDEO ,
. filter_frame = filter_frame ,
},
{
. name = "ref" ,
. type = AVMEDIA_TYPE_VIDEO ,
. filter_frame = filter_frame_ref ,
},
{ NULL }
};
static const AVFilterPad avfilter_vf_scale2ref_outputs [] = {
{
. name = "default" ,
. type = AVMEDIA_TYPE_VIDEO ,
. config_props = config_props ,
2015-08-18 15:10:30 +02:00
. request_frame = request_frame ,
2015-08-15 18:38:06 +02:00
},
{
. name = "ref" ,
. type = AVMEDIA_TYPE_VIDEO ,
. config_props = config_props_ref ,
2015-08-18 15:10:30 +02:00
. request_frame = request_frame_ref ,
2015-08-15 18:38:06 +02:00
},
{ NULL }
};
AVFilter ff_vf_scale2ref = {
. name = "scale2ref" ,
. description = NULL_IF_CONFIG_SMALL ( "Scale the input video size and/or convert the image format to the given reference." ),
. init_dict = init_dict ,
. uninit = uninit ,
. query_formats = query_formats ,
. priv_size = sizeof ( ScaleContext ),
. priv_class = & scale2ref_class ,
. inputs = avfilter_vf_scale2ref_inputs ,
. outputs = avfilter_vf_scale2ref_outputs ,
. process_command = process_command ,
};