2011-06-25 11:06:24 +00:00
/*
2017-04-20 21:33:39 +00:00
* Copyright ( c ) 2017 Thomas Mundt < tmundt75 @ gmail . com >
2011-06-25 11:06:24 +00:00
* Copyright ( c ) 2011 Stefano Sabatini
* Copyright ( c ) 2010 Baptiste Coudurier
* Copyright ( c ) 2003 Michael Zucchi < notzed @ ximian . com >
*
* This file is part of FFmpeg .
*
* FFmpeg is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; either version 2 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 General Public License for more details .
*
* You should have received a copy of the GNU 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 .
*/
/**
* @ file
* temporal field interlace filter , ported from MPlayer / libmpcodecs
*/
2012-12-06 21:03:02 +00:00
# include "libavutil/opt.h"
2011-06-25 11:06:24 +00:00
# include "libavutil/imgutils.h"
2012-07-08 01:46:59 +00:00
# include "libavutil/avassert.h"
2011-06-25 11:06:24 +00:00
# include "avfilter.h"
# include "internal.h"
2014-11-15 02:09:28 +00:00
# include "tinterlace.h"
2011-06-25 11:06:24 +00:00
2012-12-06 21:03:02 +00:00
# define OFFSET(x) offsetof(TInterlaceContext, x)
# define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
static const AVOption tinterlace_options [ ] = {
{ " mode " , " select interlace mode " , OFFSET ( mode ) , AV_OPT_TYPE_INT , { . i64 = MODE_MERGE } , 0 , MODE_NB - 1 , FLAGS , " mode " } ,
{ " merge " , " merge fields " , 0 , AV_OPT_TYPE_CONST , { . i64 = MODE_MERGE } , INT_MIN , INT_MAX , FLAGS , " mode " } ,
{ " drop_even " , " drop even fields " , 0 , AV_OPT_TYPE_CONST , { . i64 = MODE_DROP_EVEN } , INT_MIN , INT_MAX , FLAGS , " mode " } ,
{ " drop_odd " , " drop odd fields " , 0 , AV_OPT_TYPE_CONST , { . i64 = MODE_DROP_ODD } , INT_MIN , INT_MAX , FLAGS , " mode " } ,
{ " pad " , " pad alternate lines with black " , 0 , AV_OPT_TYPE_CONST , { . i64 = MODE_PAD } , INT_MIN , INT_MAX , FLAGS , " mode " } ,
{ " interleave_top " , " interleave top and bottom fields " , 0 , AV_OPT_TYPE_CONST , { . i64 = MODE_INTERLEAVE_TOP } , INT_MIN , INT_MAX , FLAGS , " mode " } ,
{ " interleave_bottom " , " interleave bottom and top fields " , 0 , AV_OPT_TYPE_CONST , { . i64 = MODE_INTERLEAVE_BOTTOM } , INT_MIN , INT_MAX , FLAGS , " mode " } ,
{ " interlacex2 " , " interlace fields from two consecutive frames " , 0 , AV_OPT_TYPE_CONST , { . i64 = MODE_INTERLACEX2 } , INT_MIN , INT_MAX , FLAGS , " mode " } ,
2015-09-30 13:35:55 +00:00
{ " mergex2 " , " merge fields keeping same frame rate " , 0 , AV_OPT_TYPE_CONST , { . i64 = MODE_MERGEX2 } , INT_MIN , INT_MAX , FLAGS , " mode " } ,
2012-12-06 21:03:02 +00:00
2012-12-30 14:02:16 +00:00
{ " flags " , " set flags " , OFFSET ( flags ) , AV_OPT_TYPE_FLAGS , { . i64 = 0 } , 0 , INT_MAX , 0 , " flags " } ,
{ " low_pass_filter " , " enable vertical low-pass filter " , 0 , AV_OPT_TYPE_CONST , { . i64 = TINTERLACE_FLAG_VLPF } , INT_MIN , INT_MAX , FLAGS , " flags " } ,
{ " vlpf " , " enable vertical low-pass filter " , 0 , AV_OPT_TYPE_CONST , { . i64 = TINTERLACE_FLAG_VLPF } , INT_MIN , INT_MAX , FLAGS , " flags " } ,
2017-04-20 21:33:39 +00:00
{ " complex_filter " , " enable complex vertical low-pass filter " , 0 , AV_OPT_TYPE_CONST , { . i64 = TINTERLACE_FLAG_CVLPF } , INT_MIN , INT_MAX , FLAGS , " flags " } ,
{ " cvlpf " , " enable complex vertical low-pass filter " , 0 , AV_OPT_TYPE_CONST , { . i64 = TINTERLACE_FLAG_CVLPF } , INT_MIN , INT_MAX , FLAGS , " flags " } ,
2014-12-01 19:52:10 +00:00
{ " exact_tb " , " force a timebase which can represent timestamps exactly " , 0 , AV_OPT_TYPE_CONST , { . i64 = TINTERLACE_FLAG_EXACT_TB } , INT_MIN , INT_MAX , FLAGS , " flags " } ,
2019-12-06 10:01:11 +00:00
{ " bypass_il " , " bypass already interlaced frames " , 0 , AV_OPT_TYPE_CONST , { . i64 = TINTERLACE_FLAG_BYPASS_IL } , INT_MIN , INT_MAX , FLAGS , " flags " } ,
2012-12-30 14:02:16 +00:00
2012-12-06 21:03:02 +00:00
{ NULL }
} ;
AVFILTER_DEFINE_CLASS ( tinterlace ) ;
2018-04-17 10:48:28 +00:00
static const AVOption interlace_options [ ] = {
2018-08-23 21:37:10 +00:00
{ " scan " , " scanning mode " , OFFSET ( mode ) , AV_OPT_TYPE_INT , { . i64 = MODE_TFF } , 0 , 1 , FLAGS , " mode " } ,
{ " tff " , " top field first " , 0 , AV_OPT_TYPE_CONST , { . i64 = MODE_TFF } , INT_MIN , INT_MAX , FLAGS , . unit = " mode " } ,
{ " bff " , " bottom field first " , 0 , AV_OPT_TYPE_CONST , { . i64 = MODE_BFF } , INT_MIN , INT_MAX , FLAGS , . unit = " mode " } ,
2019-12-06 09:35:41 +00:00
{ " lowpass " , " set vertical low-pass filter " , OFFSET ( lowpass ) , AV_OPT_TYPE_INT , { . i64 = VLPF_LIN } , 0 , 2 , FLAGS , " lowpass " } ,
{ " off " , " disable vertical low-pass filter " , 0 , AV_OPT_TYPE_CONST , { . i64 = VLPF_OFF } , INT_MIN , INT_MAX , FLAGS , " lowpass " } ,
{ " linear " , " linear vertical low-pass filter " , 0 , AV_OPT_TYPE_CONST , { . i64 = VLPF_LIN } , INT_MIN , INT_MAX , FLAGS , " lowpass " } ,
{ " complex " , " complex vertical low-pass filter " , 0 , AV_OPT_TYPE_CONST , { . i64 = VLPF_CMP } , INT_MIN , INT_MAX , FLAGS , " lowpass " } ,
2018-04-17 10:48:28 +00:00
{ NULL }
} ;
AVFILTER_DEFINE_CLASS ( interlace ) ;
2011-06-25 11:06:24 +00:00
# define FULL_SCALE_YUVJ_FORMATS \
2012-10-08 18:54:00 +00:00
AV_PIX_FMT_YUVJ420P , AV_PIX_FMT_YUVJ422P , AV_PIX_FMT_YUVJ444P , AV_PIX_FMT_YUVJ440P
2011-06-25 11:06:24 +00:00
2014-08-29 16:42:47 +00:00
static const enum AVPixelFormat full_scale_yuvj_pix_fmts [ ] = {
2012-10-08 18:54:00 +00:00
FULL_SCALE_YUVJ_FORMATS , AV_PIX_FMT_NONE
2011-06-25 11:06:24 +00:00
} ;
2014-12-01 19:52:10 +00:00
static const AVRational standard_tbs [ ] = {
{ 1 , 25 } ,
{ 1 , 30 } ,
{ 1001 , 30000 } ,
} ;
2021-09-29 13:57:43 +00:00
static const enum AVPixelFormat pix_fmts [ ] = {
AV_PIX_FMT_YUV410P , AV_PIX_FMT_YUV411P ,
AV_PIX_FMT_YUV420P , AV_PIX_FMT_YUV422P ,
AV_PIX_FMT_YUV440P , AV_PIX_FMT_YUV444P ,
AV_PIX_FMT_YUV420P10LE , AV_PIX_FMT_YUV422P10LE ,
AV_PIX_FMT_YUV440P10LE , AV_PIX_FMT_YUV444P10LE ,
AV_PIX_FMT_YUV420P12LE , AV_PIX_FMT_YUV422P12LE ,
AV_PIX_FMT_YUV440P12LE , AV_PIX_FMT_YUV444P12LE ,
AV_PIX_FMT_YUVA420P , AV_PIX_FMT_YUVA422P , AV_PIX_FMT_YUVA444P ,
AV_PIX_FMT_YUVA420P10LE , AV_PIX_FMT_YUVA422P10LE , AV_PIX_FMT_YUVA444P10LE ,
AV_PIX_FMT_GRAY8 , FULL_SCALE_YUVJ_FORMATS ,
AV_PIX_FMT_NONE
} ;
2011-06-25 11:06:24 +00:00
2014-11-15 02:20:02 +00:00
static void lowpass_line_c ( uint8_t * dstp , ptrdiff_t width , const uint8_t * srcp ,
2017-09-19 20:23:23 +00:00
ptrdiff_t mref , ptrdiff_t pref , int clip_max )
2014-11-15 02:20:02 +00:00
{
2017-04-20 21:26:59 +00:00
const uint8_t * srcp_above = srcp + mref ;
const uint8_t * srcp_below = srcp + pref ;
2014-11-15 02:20:02 +00:00
int i ;
for ( i = 0 ; i < width ; i + + ) {
// this calculation is an integer representation of
// '0.5 * current + 0.25 * above + 0.25 * below'
// '1 +' is for rounding.
dstp [ i ] = ( 1 + srcp [ i ] + srcp [ i ] + srcp_above [ i ] + srcp_below [ i ] ) > > 2 ;
}
}
2017-09-19 20:23:23 +00:00
static void lowpass_line_c_16 ( uint8_t * dst8 , ptrdiff_t width , const uint8_t * src8 ,
ptrdiff_t mref , ptrdiff_t pref , int clip_max )
{
uint16_t * dstp = ( uint16_t * ) dst8 ;
const uint16_t * srcp = ( const uint16_t * ) src8 ;
const uint16_t * srcp_above = srcp + mref / 2 ;
const uint16_t * srcp_below = srcp + pref / 2 ;
int i , src_x ;
for ( i = 0 ; i < width ; i + + ) {
// this calculation is an integer representation of
// '0.5 * current + 0.25 * above + 0.25 * below'
// '1 +' is for rounding.
src_x = av_le2ne16 ( srcp [ i ] ) < < 1 ;
dstp [ i ] = av_le2ne16 ( ( 1 + src_x + av_le2ne16 ( srcp_above [ i ] )
+ av_le2ne16 ( srcp_below [ i ] ) ) > > 2 ) ;
}
}
2017-04-20 21:33:39 +00:00
static void lowpass_line_complex_c ( uint8_t * dstp , ptrdiff_t width , const uint8_t * srcp ,
2017-09-19 20:23:23 +00:00
ptrdiff_t mref , ptrdiff_t pref , int clip_max )
2017-04-20 21:33:39 +00:00
{
const uint8_t * srcp_above = srcp + mref ;
const uint8_t * srcp_below = srcp + pref ;
const uint8_t * srcp_above2 = srcp + mref * 2 ;
const uint8_t * srcp_below2 = srcp + pref * 2 ;
2017-09-19 20:49:09 +00:00
int i , src_x , src_ab ;
2017-04-20 21:33:39 +00:00
for ( i = 0 ; i < width ; i + + ) {
// this calculation is an integer representation of
// '0.75 * current + 0.25 * above + 0.25 * below - 0.125 * above2 - 0.125 * below2'
// '4 +' is for rounding.
2017-09-19 20:49:09 +00:00
src_x = srcp [ i ] < < 1 ;
src_ab = srcp_above [ i ] + srcp_below [ i ] ;
dstp [ i ] = av_clip_uint8 ( ( 4 + ( ( srcp [ i ] + src_x + src_ab ) < < 1 )
2017-08-30 01:37:18 +00:00
- srcp_above2 [ i ] - srcp_below2 [ i ] ) > > 3 ) ;
// Prevent over-sharpening:
// dst must not exceed src when the average of above and below
// is less than src. And the other way around.
2017-09-19 20:49:09 +00:00
if ( src_ab > src_x ) {
2017-08-30 01:37:18 +00:00
if ( dstp [ i ] < srcp [ i ] )
dstp [ i ] = srcp [ i ] ;
} else if ( dstp [ i ] > srcp [ i ] )
dstp [ i ] = srcp [ i ] ;
2017-04-20 21:33:39 +00:00
}
}
2017-09-19 20:23:23 +00:00
static void lowpass_line_complex_c_16 ( uint8_t * dst8 , ptrdiff_t width , const uint8_t * src8 ,
ptrdiff_t mref , ptrdiff_t pref , int clip_max )
{
uint16_t * dstp = ( uint16_t * ) dst8 ;
const uint16_t * srcp = ( const uint16_t * ) src8 ;
const uint16_t * srcp_above = srcp + mref / 2 ;
const uint16_t * srcp_below = srcp + pref / 2 ;
const uint16_t * srcp_above2 = srcp + mref ;
const uint16_t * srcp_below2 = srcp + pref ;
int i , dst_le , src_le , src_x , src_ab ;
for ( i = 0 ; i < width ; i + + ) {
// this calculation is an integer representation of
// '0.75 * current + 0.25 * above + 0.25 * below - 0.125 * above2 - 0.125 * below2'
// '4 +' is for rounding.
src_le = av_le2ne16 ( srcp [ i ] ) ;
src_x = src_le < < 1 ;
src_ab = av_le2ne16 ( srcp_above [ i ] ) + av_le2ne16 ( srcp_below [ i ] ) ;
dst_le = av_clip ( ( 4 + ( ( src_le + src_x + src_ab ) < < 1 )
- av_le2ne16 ( srcp_above2 [ i ] )
- av_le2ne16 ( srcp_below2 [ i ] ) ) > > 3 , 0 , clip_max ) ;
// Prevent over-sharpening:
// dst must not exceed src when the average of above and below
// is less than src. And the other way around.
if ( src_ab > src_x ) {
if ( dst_le < src_le )
dstp [ i ] = av_le2ne16 ( src_le ) ;
else
dstp [ i ] = av_le2ne16 ( dst_le ) ;
} else if ( dst_le > src_le ) {
dstp [ i ] = av_le2ne16 ( src_le ) ;
} else
dstp [ i ] = av_le2ne16 ( dst_le ) ;
}
}
2011-06-25 11:06:24 +00:00
static av_cold void uninit ( AVFilterContext * ctx )
{
TInterlaceContext * tinterlace = ctx - > priv ;
2013-03-10 00:30:30 +00:00
av_frame_free ( & tinterlace - > cur ) ;
av_frame_free ( & tinterlace - > next ) ;
2022-12-09 00:22:44 +00:00
av_freep ( & tinterlace - > black_data [ 0 ] [ 0 ] ) ;
av_freep ( & tinterlace - > black_data [ 1 ] [ 0 ] ) ;
2011-06-25 11:06:24 +00:00
}
static int config_out_props ( AVFilterLink * outlink )
{
AVFilterContext * ctx = outlink - > src ;
AVFilterLink * inlink = outlink - > src - > inputs [ 0 ] ;
2012-10-20 04:43:48 +00:00
const AVPixFmtDescriptor * desc = av_pix_fmt_desc_get ( outlink - > format ) ;
2011-06-25 11:06:24 +00:00
TInterlaceContext * tinterlace = ctx - > priv ;
2014-12-01 19:52:10 +00:00
int i ;
2011-06-25 11:06:24 +00:00
tinterlace - > vsub = desc - > log2_chroma_h ;
outlink - > w = inlink - > w ;
2015-09-30 13:35:55 +00:00
outlink - > h = tinterlace - > mode = = MODE_MERGE | | tinterlace - > mode = = MODE_PAD | | tinterlace - > mode = = MODE_MERGEX2 ?
2011-06-25 11:06:24 +00:00
inlink - > h * 2 : inlink - > h ;
2015-09-30 13:35:55 +00:00
if ( tinterlace - > mode = = MODE_MERGE | | tinterlace - > mode = = MODE_PAD | | tinterlace - > mode = = MODE_MERGEX2 )
2015-06-04 12:11:29 +00:00
outlink - > sample_aspect_ratio = av_mul_q ( inlink - > sample_aspect_ratio ,
av_make_q ( 2 , 1 ) ) ;
2011-06-25 11:06:24 +00:00
2012-04-29 14:45:35 +00:00
if ( tinterlace - > mode = = MODE_PAD ) {
2017-09-18 21:57:17 +00:00
uint8_t black [ 4 ] = { 0 , 0 , 0 , 16 } ;
int ret ;
ff_draw_init ( & tinterlace - > draw , outlink - > format , 0 ) ;
ff_draw_color ( & tinterlace - > draw , & tinterlace - > color , black ) ;
2022-12-09 00:22:44 +00:00
/* limited range */
if ( ! ff_fmt_is_in ( outlink - > format , full_scale_yuvj_pix_fmts ) ) {
ret = av_image_alloc ( tinterlace - > black_data [ 0 ] , tinterlace - > black_linesize ,
outlink - > w , outlink - > h , outlink - > format , 16 ) ;
if ( ret < 0 )
return ret ;
ff_fill_rectangle ( & tinterlace - > draw , & tinterlace - > color , tinterlace - > black_data [ 0 ] ,
tinterlace - > black_linesize , 0 , 0 , outlink - > w , outlink - > h ) ;
}
/* full range */
tinterlace - > color . comp [ 0 ] . u8 [ 0 ] = 0 ;
ret = av_image_alloc ( tinterlace - > black_data [ 1 ] , tinterlace - > black_linesize ,
2016-02-14 14:51:10 +00:00
outlink - > w , outlink - > h , outlink - > format , 16 ) ;
2011-06-25 11:06:24 +00:00
if ( ret < 0 )
return ret ;
2022-12-09 00:22:44 +00:00
ff_fill_rectangle ( & tinterlace - > draw , & tinterlace - > color , tinterlace - > black_data [ 1 ] ,
2017-09-18 21:57:17 +00:00
tinterlace - > black_linesize , 0 , 0 , outlink - > w , outlink - > h ) ;
2011-06-25 11:06:24 +00:00
}
2017-09-18 02:41:31 +00:00
if ( tinterlace - > flags & ( TINTERLACE_FLAG_VLPF | TINTERLACE_FLAG_CVLPF )
2012-12-30 14:02:16 +00:00
& & ! ( tinterlace - > mode = = MODE_INTERLEAVE_TOP
| | tinterlace - > mode = = MODE_INTERLEAVE_BOTTOM ) ) {
2017-04-20 21:33:39 +00:00
av_log ( ctx , AV_LOG_WARNING , " low_pass_filter flags ignored with mode %d \n " ,
2012-12-30 14:02:16 +00:00
tinterlace - > mode ) ;
2017-09-18 02:41:31 +00:00
tinterlace - > flags & = ~ ( TINTERLACE_FLAG_VLPF | TINTERLACE_FLAG_CVLPF ) ;
2012-12-30 14:02:16 +00:00
}
2014-12-01 19:52:10 +00:00
tinterlace - > preout_time_base = inlink - > time_base ;
2014-03-16 15:29:30 +00:00
if ( tinterlace - > mode = = MODE_INTERLACEX2 ) {
2014-12-01 19:52:10 +00:00
tinterlace - > preout_time_base . den * = 2 ;
2014-03-16 15:29:30 +00:00
outlink - > frame_rate = av_mul_q ( inlink - > frame_rate , ( AVRational ) { 2 , 1 } ) ;
2014-12-01 19:52:10 +00:00
outlink - > time_base = av_mul_q ( inlink - > time_base , ( AVRational ) { 1 , 2 } ) ;
2015-09-30 13:35:55 +00:00
} else if ( tinterlace - > mode = = MODE_MERGEX2 ) {
outlink - > frame_rate = inlink - > frame_rate ;
outlink - > time_base = inlink - > time_base ;
2014-11-12 22:39:45 +00:00
} else if ( tinterlace - > mode ! = MODE_PAD ) {
outlink - > frame_rate = av_mul_q ( inlink - > frame_rate , ( AVRational ) { 1 , 2 } ) ;
2014-12-01 19:52:10 +00:00
outlink - > time_base = av_mul_q ( inlink - > time_base , ( AVRational ) { 2 , 1 } ) ;
}
for ( i = 0 ; i < FF_ARRAY_ELEMS ( standard_tbs ) ; i + + ) {
if ( ! av_cmp_q ( standard_tbs [ i ] , outlink - > time_base ) )
break ;
2014-03-16 15:29:30 +00:00
}
2014-12-01 19:52:10 +00:00
if ( i = = FF_ARRAY_ELEMS ( standard_tbs ) | |
( tinterlace - > flags & TINTERLACE_FLAG_EXACT_TB ) )
outlink - > time_base = tinterlace - > preout_time_base ;
2014-03-16 15:29:30 +00:00
2017-09-19 20:23:23 +00:00
tinterlace - > csp = av_pix_fmt_desc_get ( outlink - > format ) ;
2017-04-20 21:33:39 +00:00
if ( tinterlace - > flags & TINTERLACE_FLAG_CVLPF ) {
2017-09-19 20:23:23 +00:00
if ( tinterlace - > csp - > comp [ 0 ] . depth > 8 )
tinterlace - > lowpass_line = lowpass_line_complex_c_16 ;
else
tinterlace - > lowpass_line = lowpass_line_complex_c ;
2022-06-12 03:51:12 +00:00
# if ARCH_X86
ff_tinterlace_init_x86 ( tinterlace ) ;
# endif
2017-04-20 21:33:39 +00:00
} else if ( tinterlace - > flags & TINTERLACE_FLAG_VLPF ) {
2017-09-19 20:23:23 +00:00
if ( tinterlace - > csp - > comp [ 0 ] . depth > 8 )
tinterlace - > lowpass_line = lowpass_line_c_16 ;
else
tinterlace - > lowpass_line = lowpass_line_c ;
2022-06-12 03:51:12 +00:00
# if ARCH_X86
ff_tinterlace_init_x86 ( tinterlace ) ;
# endif
2014-11-15 02:20:02 +00:00
}
2017-04-20 21:33:39 +00:00
av_log ( ctx , AV_LOG_VERBOSE , " mode:%d filter:%s h:%d -> h:%d \n " , tinterlace - > mode ,
( tinterlace - > flags & TINTERLACE_FLAG_CVLPF ) ? " complex " :
( tinterlace - > flags & TINTERLACE_FLAG_VLPF ) ? " linear " : " off " ,
2012-12-30 14:02:16 +00:00
inlink - > h , outlink - > h ) ;
2011-06-25 11:06:24 +00:00
return 0 ;
}
# define FIELD_UPPER 0
# define FIELD_LOWER 1
# define FIELD_UPPER_AND_LOWER 2
/**
* Copy picture field from src to dst .
*
* @ param src_field copy from upper , lower field or both
2012-04-27 20:11:15 +00:00
* @ param interleave leave a padding line between each copied line
2011-06-25 11:06:24 +00:00
* @ param dst_field copy to upper or lower field ,
* only meaningful when interleave is selected
2012-12-30 14:02:16 +00:00
* @ param flags context flags
2011-06-25 11:06:24 +00:00
*/
static inline
2014-11-15 02:20:02 +00:00
void copy_picture_field ( TInterlaceContext * tinterlace ,
uint8_t * dst [ 4 ] , int dst_linesize [ 4 ] ,
2012-09-05 08:37:12 +00:00
const uint8_t * src [ 4 ] , int src_linesize [ 4 ] ,
2012-10-08 18:54:00 +00:00
enum AVPixelFormat format , int w , int src_h ,
2012-12-30 14:02:16 +00:00
int src_field , int interleave , int dst_field ,
int flags )
2011-06-25 11:06:24 +00:00
{
2012-10-20 04:43:48 +00:00
const AVPixFmtDescriptor * desc = av_pix_fmt_desc_get ( format ) ;
2015-01-06 22:20:18 +00:00
int hsub = desc - > log2_chroma_w ;
2011-06-25 11:06:24 +00:00
int plane , vsub = desc - > log2_chroma_h ;
int k = src_field = = FIELD_UPPER_AND_LOWER ? 1 : 2 ;
2014-11-16 00:06:18 +00:00
int h ;
2011-06-25 11:06:24 +00:00
for ( plane = 0 ; plane < desc - > nb_components ; plane + + ) {
2016-01-27 15:19:38 +00:00
int lines = plane = = 1 | | plane = = 2 ? AV_CEIL_RSHIFT ( src_h , vsub ) : src_h ;
int cols = plane = = 1 | | plane = = 2 ? AV_CEIL_RSHIFT ( w , hsub ) : w ;
2011-06-25 11:06:24 +00:00
uint8_t * dstp = dst [ plane ] ;
2012-09-05 08:37:12 +00:00
const uint8_t * srcp = src [ plane ] ;
2017-09-14 19:20:24 +00:00
int srcp_linesize = src_linesize [ plane ] * k ;
int dstp_linesize = dst_linesize [ plane ] * ( interleave ? 2 : 1 ) ;
2017-09-19 20:23:23 +00:00
int clip_max = ( 1 < < tinterlace - > csp - > comp [ plane ] . depth ) - 1 ;
2012-11-03 01:21:49 +00:00
2013-04-29 10:26:15 +00:00
lines = ( lines + ( src_field = = FIELD_UPPER ) ) / k ;
2011-06-25 11:06:24 +00:00
if ( src_field = = FIELD_LOWER )
srcp + = src_linesize [ plane ] ;
if ( interleave & & dst_field = = FIELD_LOWER )
dstp + = dst_linesize [ plane ] ;
2017-04-20 21:33:39 +00:00
// Low-pass filtering is required when creating an interlaced destination from
// a progressive source which contains high-frequency vertical detail.
// Filtering will reduce interlace 'twitter' and Moire patterning.
2017-09-18 02:41:31 +00:00
if ( flags & ( TINTERLACE_FLAG_VLPF | TINTERLACE_FLAG_CVLPF ) ) {
int x = ! ! ( flags & TINTERLACE_FLAG_CVLPF ) ;
2017-04-20 21:33:39 +00:00
for ( h = lines ; h > 0 ; h - - ) {
ptrdiff_t pref = src_linesize [ plane ] ;
ptrdiff_t mref = - pref ;
2017-09-14 19:20:24 +00:00
if ( h > = ( lines - x ) ) mref = 0 ; // there is no line above
else if ( h < = ( 1 + x ) ) pref = 0 ; // there is no line below
2014-11-15 02:20:02 +00:00
2017-09-19 20:23:23 +00:00
tinterlace - > lowpass_line ( dstp , cols , srcp , mref , pref , clip_max ) ;
2012-12-30 14:02:16 +00:00
dstp + = dstp_linesize ;
srcp + = srcp_linesize ;
}
} else {
2017-09-19 20:23:23 +00:00
if ( tinterlace - > csp - > comp [ plane ] . depth > 8 )
cols * = 2 ;
2017-09-14 19:20:24 +00:00
av_image_copy_plane ( dstp , dstp_linesize , srcp , srcp_linesize , cols , lines ) ;
2012-12-30 14:02:16 +00:00
}
2011-06-25 11:06:24 +00:00
}
}
2013-03-10 00:30:30 +00:00
static int filter_frame ( AVFilterLink * inlink , AVFrame * picref )
2011-06-25 11:06:24 +00:00
{
AVFilterContext * ctx = inlink - > dst ;
2012-12-06 13:40:36 +00:00
AVFilterLink * outlink = ctx - > outputs [ 0 ] ;
2011-06-25 11:06:24 +00:00
TInterlaceContext * tinterlace = ctx - > priv ;
2013-03-10 00:30:30 +00:00
AVFrame * cur , * next , * out ;
2022-12-09 00:22:44 +00:00
int field , tff , full , ret ;
2011-06-25 11:06:24 +00:00
2013-03-10 00:30:30 +00:00
av_frame_free ( & tinterlace - > cur ) ;
2011-06-25 11:06:24 +00:00
tinterlace - > cur = tinterlace - > next ;
tinterlace - > next = picref ;
2012-12-06 13:40:36 +00:00
cur = tinterlace - > cur ;
next = tinterlace - > next ;
2011-06-25 11:06:24 +00:00
/* we need at least two frames */
if ( ! tinterlace - > cur )
2012-07-22 20:57:02 +00:00
return 0 ;
2011-06-25 11:06:24 +00:00
switch ( tinterlace - > mode ) {
2015-09-30 13:35:55 +00:00
case MODE_MERGEX2 : /* move the odd frame into the upper field of the new image, even into
* the lower field , generating a double - height video at same framerate */
2012-04-29 14:45:35 +00:00
case MODE_MERGE : /* move the odd frame into the upper field of the new image, even into
2011-06-25 11:06:24 +00:00
* the lower field , generating a double - height video at half framerate */
2013-03-10 00:30:30 +00:00
out = ff_get_video_buffer ( outlink , outlink - > w , outlink - > h ) ;
2012-12-06 13:40:36 +00:00
if ( ! out )
return AVERROR ( ENOMEM ) ;
2013-03-10 00:30:30 +00:00
av_frame_copy_props ( out , cur ) ;
out - > height = outlink - > h ;
out - > interlaced_frame = 1 ;
out - > top_field_first = 1 ;
2015-06-04 12:11:29 +00:00
out - > sample_aspect_ratio = av_mul_q ( cur - > sample_aspect_ratio , av_make_q ( 2 , 1 ) ) ;
2011-06-25 11:06:24 +00:00
/* write odd frame lines into the upper field of the new frame */
2014-11-15 02:20:02 +00:00
copy_picture_field ( tinterlace , out - > data , out - > linesize ,
2012-09-05 08:37:12 +00:00
( const uint8_t * * ) cur - > data , cur - > linesize ,
2011-06-25 11:06:24 +00:00
inlink - > format , inlink - > w , inlink - > h ,
2020-07-11 11:20:45 +00:00
FIELD_UPPER_AND_LOWER , 1 , tinterlace - > mode = = MODE_MERGEX2 ? ( 1 + inlink - > frame_count_out ) & 1 ? FIELD_LOWER : FIELD_UPPER : FIELD_UPPER , tinterlace - > flags ) ;
2011-06-25 11:06:24 +00:00
/* write even frame lines into the lower field of the new frame */
2014-11-15 02:20:02 +00:00
copy_picture_field ( tinterlace , out - > data , out - > linesize ,
2012-09-05 08:37:12 +00:00
( const uint8_t * * ) next - > data , next - > linesize ,
2011-06-25 11:06:24 +00:00
inlink - > format , inlink - > w , inlink - > h ,
2020-07-11 11:20:45 +00:00
FIELD_UPPER_AND_LOWER , 1 , tinterlace - > mode = = MODE_MERGEX2 ? ( 1 + inlink - > frame_count_out ) & 1 ? FIELD_UPPER : FIELD_LOWER : FIELD_LOWER , tinterlace - > flags ) ;
2015-09-30 13:35:55 +00:00
if ( tinterlace - > mode ! = MODE_MERGEX2 )
av_frame_free ( & tinterlace - > next ) ;
2011-06-25 11:06:24 +00:00
break ;
2012-04-29 14:45:35 +00:00
case MODE_DROP_ODD : /* only output even frames, odd frames are dropped; height unchanged, half framerate */
case MODE_DROP_EVEN : /* only output odd frames, even frames are dropped; height unchanged, half framerate */
2013-03-10 00:30:30 +00:00
out = av_frame_clone ( tinterlace - > mode = = MODE_DROP_EVEN ? cur : next ) ;
2013-11-19 17:08:58 +00:00
if ( ! out )
return AVERROR ( ENOMEM ) ;
2013-03-10 00:30:30 +00:00
av_frame_free ( & tinterlace - > next ) ;
2011-06-25 11:06:24 +00:00
break ;
2012-04-29 14:45:35 +00:00
case MODE_PAD : /* expand each frame to double height, but pad alternate
* lines with black ; framerate unchanged */
2013-03-10 00:30:30 +00:00
out = ff_get_video_buffer ( outlink , outlink - > w , outlink - > h ) ;
2013-04-08 18:48:16 +00:00
if ( ! out )
return AVERROR ( ENOMEM ) ;
2013-03-10 00:30:30 +00:00
av_frame_copy_props ( out , cur ) ;
out - > height = outlink - > h ;
2015-06-04 12:11:29 +00:00
out - > sample_aspect_ratio = av_mul_q ( cur - > sample_aspect_ratio , av_make_q ( 2 , 1 ) ) ;
2011-06-25 11:06:24 +00:00
2020-07-11 11:20:02 +00:00
field = ( 1 + outlink - > frame_count_in ) & 1 ? FIELD_UPPER : FIELD_LOWER ;
2022-12-09 00:22:44 +00:00
full = out - > color_range = = AVCOL_RANGE_JPEG | | ff_fmt_is_in ( out - > format , full_scale_yuvj_pix_fmts ) ;
2011-06-25 11:06:24 +00:00
/* copy upper and lower fields */
2014-11-15 02:20:02 +00:00
copy_picture_field ( tinterlace , out - > data , out - > linesize ,
2012-09-05 08:37:12 +00:00
( const uint8_t * * ) cur - > data , cur - > linesize ,
2011-06-25 11:06:24 +00:00
inlink - > format , inlink - > w , inlink - > h ,
2012-12-30 14:02:16 +00:00
FIELD_UPPER_AND_LOWER , 1 , field , tinterlace - > flags ) ;
2011-06-25 11:06:24 +00:00
/* pad with black the other field */
2014-11-15 02:20:02 +00:00
copy_picture_field ( tinterlace , out - > data , out - > linesize ,
2022-12-09 00:22:44 +00:00
( const uint8_t * * ) tinterlace - > black_data [ full ] , tinterlace - > black_linesize ,
2011-06-25 11:06:24 +00:00
inlink - > format , inlink - > w , inlink - > h ,
2012-12-30 14:02:16 +00:00
FIELD_UPPER_AND_LOWER , 1 , ! field , tinterlace - > flags ) ;
2011-06-25 11:06:24 +00:00
break ;
2011-12-31 13:12:01 +00:00
/* interleave upper/lower lines from odd frames with lower/upper lines from even frames,
* halving the frame rate and preserving image height */
2012-04-29 14:45:35 +00:00
case MODE_INTERLEAVE_TOP : /* top field first */
case MODE_INTERLEAVE_BOTTOM : /* bottom field first */
2019-12-06 10:01:11 +00:00
if ( ( tinterlace - > flags & TINTERLACE_FLAG_BYPASS_IL ) & & cur - > interlaced_frame ) {
av_log ( ctx , AV_LOG_WARNING ,
" video is already interlaced, adjusting framerate only \n " ) ;
out = av_frame_clone ( cur ) ;
if ( ! out )
return AVERROR ( ENOMEM ) ;
out - > pts / = 2 ; // adjust pts to new framerate
ret = ff_filter_frame ( outlink , out ) ;
return ret ;
}
2012-04-29 14:45:35 +00:00
tff = tinterlace - > mode = = MODE_INTERLEAVE_TOP ;
2013-03-10 00:30:30 +00:00
out = ff_get_video_buffer ( outlink , outlink - > w , outlink - > h ) ;
2012-12-06 13:40:36 +00:00
if ( ! out )
return AVERROR ( ENOMEM ) ;
2013-03-10 00:30:30 +00:00
av_frame_copy_props ( out , cur ) ;
out - > interlaced_frame = 1 ;
out - > top_field_first = tff ;
2011-06-25 11:06:24 +00:00
2011-12-31 13:12:01 +00:00
/* copy upper/lower field from cur */
2014-11-15 02:20:02 +00:00
copy_picture_field ( tinterlace , out - > data , out - > linesize ,
2012-09-05 08:37:12 +00:00
( const uint8_t * * ) cur - > data , cur - > linesize ,
2011-06-25 11:06:24 +00:00
inlink - > format , inlink - > w , inlink - > h ,
2012-12-30 14:02:16 +00:00
tff ? FIELD_UPPER : FIELD_LOWER , 1 , tff ? FIELD_UPPER : FIELD_LOWER ,
tinterlace - > flags ) ;
2011-12-31 13:12:01 +00:00
/* copy lower/upper field from next */
2014-11-15 02:20:02 +00:00
copy_picture_field ( tinterlace , out - > data , out - > linesize ,
2012-09-05 08:37:12 +00:00
( const uint8_t * * ) next - > data , next - > linesize ,
2011-06-25 11:06:24 +00:00
inlink - > format , inlink - > w , inlink - > h ,
2012-12-30 14:02:16 +00:00
tff ? FIELD_LOWER : FIELD_UPPER , 1 , tff ? FIELD_LOWER : FIELD_UPPER ,
tinterlace - > flags ) ;
2013-03-10 00:30:30 +00:00
av_frame_free ( & tinterlace - > next ) ;
2011-06-25 11:06:24 +00:00
break ;
2012-04-29 14:45:35 +00:00
case MODE_INTERLACEX2 : /* re-interlace preserving image height, double frame rate */
2012-04-29 13:25:57 +00:00
/* output current frame first */
2013-03-10 00:30:30 +00:00
out = av_frame_clone ( cur ) ;
2012-12-06 13:40:36 +00:00
if ( ! out )
return AVERROR ( ENOMEM ) ;
2013-03-10 00:30:30 +00:00
out - > interlaced_frame = 1 ;
2014-03-16 15:29:30 +00:00
if ( cur - > pts ! = AV_NOPTS_VALUE )
out - > pts = cur - > pts * 2 ;
2012-04-29 13:25:57 +00:00
2014-12-01 19:52:10 +00:00
out - > pts = av_rescale_q ( out - > pts , tinterlace - > preout_time_base , outlink - > time_base ) ;
2012-12-06 13:40:36 +00:00
if ( ( ret = ff_filter_frame ( outlink , out ) ) < 0 )
return ret ;
2012-04-29 13:25:57 +00:00
/* output mix of current and next frame */
2013-03-10 00:30:30 +00:00
tff = next - > top_field_first ;
out = ff_get_video_buffer ( outlink , outlink - > w , outlink - > h ) ;
2012-12-06 13:40:36 +00:00
if ( ! out )
return AVERROR ( ENOMEM ) ;
2013-03-10 00:30:30 +00:00
av_frame_copy_props ( out , next ) ;
out - > interlaced_frame = 1 ;
2014-11-13 23:04:51 +00:00
out - > top_field_first = ! tff ;
2012-04-29 13:25:57 +00:00
2014-03-16 15:29:30 +00:00
if ( next - > pts ! = AV_NOPTS_VALUE & & cur - > pts ! = AV_NOPTS_VALUE )
out - > pts = cur - > pts + next - > pts ;
else
out - > pts = AV_NOPTS_VALUE ;
2012-04-29 13:25:57 +00:00
/* write current frame second field lines into the second field of the new frame */
2014-11-15 02:20:02 +00:00
copy_picture_field ( tinterlace , out - > data , out - > linesize ,
2012-09-05 08:37:12 +00:00
( const uint8_t * * ) cur - > data , cur - > linesize ,
2012-04-29 13:25:57 +00:00
inlink - > format , inlink - > w , inlink - > h ,
2012-12-30 14:02:16 +00:00
tff ? FIELD_LOWER : FIELD_UPPER , 1 , tff ? FIELD_LOWER : FIELD_UPPER ,
tinterlace - > flags ) ;
2012-04-29 13:25:57 +00:00
/* write next frame first field lines into the first field of the new frame */
2014-11-15 02:20:02 +00:00
copy_picture_field ( tinterlace , out - > data , out - > linesize ,
2012-09-05 08:37:12 +00:00
( const uint8_t * * ) next - > data , next - > linesize ,
2012-04-29 13:25:57 +00:00
inlink - > format , inlink - > w , inlink - > h ,
2012-12-30 14:02:16 +00:00
tff ? FIELD_UPPER : FIELD_LOWER , 1 , tff ? FIELD_UPPER : FIELD_LOWER ,
tinterlace - > flags ) ;
2012-04-29 13:25:57 +00:00
break ;
2012-12-10 22:55:21 +00:00
default :
av_assert0 ( 0 ) ;
2011-06-25 11:06:24 +00:00
}
2014-12-01 19:52:10 +00:00
out - > pts = av_rescale_q ( out - > pts , tinterlace - > preout_time_base , outlink - > time_base ) ;
2022-10-05 09:11:08 +00:00
out - > duration = av_rescale_q ( 1 , av_inv_q ( outlink - > frame_rate ) , outlink - > time_base ) ;
2012-12-06 13:40:36 +00:00
ret = ff_filter_frame ( outlink , out ) ;
2012-07-22 20:57:02 +00:00
2012-12-06 13:40:36 +00:00
return ret ;
2011-06-25 11:06:24 +00:00
}
2018-08-23 21:37:10 +00:00
static int init_interlace ( AVFilterContext * ctx )
{
TInterlaceContext * tinterlace = ctx - > priv ;
if ( tinterlace - > mode < = MODE_BFF )
tinterlace - > mode + = MODE_INTERLEAVE_TOP ;
2019-12-06 10:02:36 +00:00
tinterlace - > flags | = TINTERLACE_FLAG_BYPASS_IL ;
2019-12-06 09:35:41 +00:00
if ( tinterlace - > lowpass = = VLPF_LIN )
tinterlace - > flags | = TINTERLACE_FLAG_VLPF ;
if ( tinterlace - > lowpass = = VLPF_CMP )
tinterlace - > flags | = TINTERLACE_FLAG_CVLPF ;
2018-08-23 21:37:10 +00:00
return 0 ;
}
2012-11-28 19:01:59 +00:00
static const AVFilterPad tinterlace_inputs [ ] = {
{
. name = " default " ,
. type = AVMEDIA_TYPE_VIDEO ,
2012-12-06 13:40:36 +00:00
. filter_frame = filter_frame ,
2012-11-28 19:01:59 +00:00
} ,
} ;
static const AVFilterPad tinterlace_outputs [ ] = {
{
2013-09-07 12:13:50 +00:00
. name = " default " ,
. type = AVMEDIA_TYPE_VIDEO ,
. config_props = config_out_props ,
2012-11-28 19:01:59 +00:00
} ,
} ;
2021-04-19 16:33:56 +00:00
const AVFilter ff_vf_tinterlace = {
2011-06-25 11:06:24 +00:00
. name = " tinterlace " ,
. description = NULL_IF_CONFIG_SMALL ( " Perform temporal field interlacing. " ) ,
. priv_size = sizeof ( TInterlaceContext ) ,
. uninit = uninit ,
2021-08-12 11:05:31 +00:00
FILTER_INPUTS ( tinterlace_inputs ) ,
FILTER_OUTPUTS ( tinterlace_outputs ) ,
2021-09-27 15:31:22 +00:00
FILTER_PIXFMTS_ARRAY ( pix_fmts ) ,
2012-12-06 21:03:02 +00:00
. priv_class = & tinterlace_class ,
2011-06-25 11:06:24 +00:00
} ;
2018-04-17 10:48:28 +00:00
2021-04-19 16:33:56 +00:00
const AVFilter ff_vf_interlace = {
2018-04-17 10:48:28 +00:00
. name = " interlace " ,
. description = NULL_IF_CONFIG_SMALL ( " Convert progressive video into interlaced. " ) ,
. priv_size = sizeof ( TInterlaceContext ) ,
2018-08-23 21:37:10 +00:00
. init = init_interlace ,
2018-04-17 10:48:28 +00:00
. uninit = uninit ,
2021-08-12 11:05:31 +00:00
FILTER_INPUTS ( tinterlace_inputs ) ,
FILTER_OUTPUTS ( tinterlace_outputs ) ,
2021-09-27 15:31:22 +00:00
FILTER_PIXFMTS_ARRAY ( pix_fmts ) ,
2018-04-17 10:48:28 +00:00
. priv_class = & interlace_class ,
} ;