2011-06-25 13:06:24 +02:00
/*
2017-04-20 23:33:39 +02:00
* Copyright (c) 2017 Thomas Mundt <tmundt75@gmail.com>
2011-06-25 13:06:24 +02: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 22:03:02 +01:00
#include "libavutil/opt.h"
2011-06-25 13:06:24 +02:00
#include "libavutil/imgutils.h"
2012-07-08 03:46:59 +02:00
#include "libavutil/avassert.h"
2011-06-25 13:06:24 +02:00
#include "avfilter.h"
#include "internal.h"
2014-11-15 03:09:28 +01:00
#include "tinterlace.h"
2011-06-25 13:06:24 +02:00
2012-12-06 22:03:02 +01: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 15:35:55 +02: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 22:03:02 +01:00
2012-12-30 15:02:16 +01: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 23:33:39 +02: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 20:52:10 +01: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 11:01:11 +01: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 15:02:16 +01:00
2012-12-06 22:03:02 +01:00
{ NULL }
};
AVFILTER_DEFINE_CLASS ( tinterlace );
2018-04-17 13:48:28 +03:00
static const AVOption interlace_options [] = {
2018-08-23 23:37:10 +02: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 10:35:41 +01: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 13:48:28 +03:00
{ NULL }
};
AVFILTER_DEFINE_CLASS ( interlace );
2011-06-25 13:06:24 +02:00
#define FULL_SCALE_YUVJ_FORMATS \
2012-10-08 20:54:00 +02:00
AV_PIX_FMT_YUVJ420P, AV_PIX_FMT_YUVJ422P, AV_PIX_FMT_YUVJ444P, AV_PIX_FMT_YUVJ440P
2011-06-25 13:06:24 +02:00
2014-08-29 18:42:47 +02:00
static const enum AVPixelFormat full_scale_yuvj_pix_fmts [] = {
2012-10-08 20:54:00 +02:00
FULL_SCALE_YUVJ_FORMATS , AV_PIX_FMT_NONE
2011-06-25 13:06:24 +02:00
};
2014-12-01 20:52:10 +01:00
static const AVRational standard_tbs [] = {
{ 1 , 25 },
{ 1 , 30 },
{ 1001 , 30000 },
};
2021-09-29 15:57:43 +02: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 13:06:24 +02:00
2014-11-15 03:20:02 +01:00
static void lowpass_line_c ( uint8_t * dstp , ptrdiff_t width , const uint8_t * srcp ,
2017-09-19 22:23:23 +02:00
ptrdiff_t mref , ptrdiff_t pref , int clip_max )
2014-11-15 03:20:02 +01:00
{
2017-04-20 23:26:59 +02:00
const uint8_t * srcp_above = srcp + mref ;
const uint8_t * srcp_below = srcp + pref ;
2014-11-15 03:20:02 +01: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 22:23:23 +02: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 23:33:39 +02:00
static void lowpass_line_complex_c ( uint8_t * dstp , ptrdiff_t width , const uint8_t * srcp ,
2017-09-19 22:23:23 +02:00
ptrdiff_t mref , ptrdiff_t pref , int clip_max )
2017-04-20 23:33:39 +02: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 22:49:09 +02:00
int i , src_x , src_ab ;
2017-04-20 23:33:39 +02: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 22:49:09 +02: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 03:37:18 +02: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 22:49:09 +02:00
if ( src_ab > src_x ) {
2017-08-30 03:37:18 +02:00
if ( dstp [ i ] < srcp [ i ])
dstp [ i ] = srcp [ i ];
} else if ( dstp [ i ] > srcp [ i ])
dstp [ i ] = srcp [ i ];
2017-04-20 23:33:39 +02:00
}
}
2017-09-19 22:23:23 +02: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 13:06:24 +02:00
static av_cold void uninit ( AVFilterContext * ctx )
{
TInterlaceContext * tinterlace = ctx -> priv ;
2013-03-10 01:30:30 +01:00
av_frame_free ( & tinterlace -> cur );
av_frame_free ( & tinterlace -> next );
2011-06-25 13:06:24 +02:00
av_freep ( & tinterlace -> black_data [ 0 ]);
}
static int config_out_props ( AVFilterLink * outlink )
{
AVFilterContext * ctx = outlink -> src ;
AVFilterLink * inlink = outlink -> src -> inputs [ 0 ];
2012-10-20 06:43:48 +02:00
const AVPixFmtDescriptor * desc = av_pix_fmt_desc_get ( outlink -> format );
2011-06-25 13:06:24 +02:00
TInterlaceContext * tinterlace = ctx -> priv ;
2014-12-01 20:52:10 +01:00
int i ;
2011-06-25 13:06:24 +02:00
tinterlace -> vsub = desc -> log2_chroma_h ;
outlink -> w = inlink -> w ;
2015-09-30 15:35:55 +02:00
outlink -> h = tinterlace -> mode == MODE_MERGE || tinterlace -> mode == MODE_PAD || tinterlace -> mode == MODE_MERGEX2 ?
2011-06-25 13:06:24 +02:00
inlink -> h * 2 : inlink -> h ;
2015-09-30 15:35:55 +02:00
if ( tinterlace -> mode == MODE_MERGE || tinterlace -> mode == MODE_PAD || tinterlace -> mode == MODE_MERGEX2 )
2015-06-04 14:11:29 +02:00
outlink -> sample_aspect_ratio = av_mul_q ( inlink -> sample_aspect_ratio ,
av_make_q ( 2 , 1 ));
2011-06-25 13:06:24 +02:00
2012-04-29 16:45:35 +02:00
if ( tinterlace -> mode == MODE_PAD ) {
2017-09-18 23:57:17 +02: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 );
2011-06-25 13:06:24 +02:00
if ( ff_fmt_is_in ( outlink -> format , full_scale_yuvj_pix_fmts ))
2017-09-18 23:57:17 +02:00
tinterlace -> color . comp [ 0 ]. u8 [ 0 ] = 0 ;
2011-06-25 13:06:24 +02:00
ret = av_image_alloc ( tinterlace -> black_data , tinterlace -> black_linesize ,
2016-02-14 15:51:10 +01:00
outlink -> w , outlink -> h , outlink -> format , 16 );
2011-06-25 13:06:24 +02:00
if ( ret < 0 )
return ret ;
2017-09-18 23:57:17 +02:00
ff_fill_rectangle ( & tinterlace -> draw , & tinterlace -> color , tinterlace -> black_data ,
tinterlace -> black_linesize , 0 , 0 , outlink -> w , outlink -> h );
2011-06-25 13:06:24 +02:00
}
2017-09-17 23:41:31 -03:00
if ( tinterlace -> flags & ( TINTERLACE_FLAG_VLPF | TINTERLACE_FLAG_CVLPF )
2012-12-30 15:02:16 +01:00
&& ! ( tinterlace -> mode == MODE_INTERLEAVE_TOP
|| tinterlace -> mode == MODE_INTERLEAVE_BOTTOM )) {
2017-04-20 23:33:39 +02:00
av_log ( ctx , AV_LOG_WARNING , "low_pass_filter flags ignored with mode %d \n " ,
2012-12-30 15:02:16 +01:00
tinterlace -> mode );
2017-09-17 23:41:31 -03:00
tinterlace -> flags &= ~ ( TINTERLACE_FLAG_VLPF | TINTERLACE_FLAG_CVLPF );
2012-12-30 15:02:16 +01:00
}
2014-12-01 20:52:10 +01:00
tinterlace -> preout_time_base = inlink -> time_base ;
2014-03-16 15:29:30 +00:00
if ( tinterlace -> mode == MODE_INTERLACEX2 ) {
2014-12-01 20:52:10 +01: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 20:52:10 +01:00
outlink -> time_base = av_mul_q ( inlink -> time_base , ( AVRational ){ 1 , 2 });
2015-09-30 15:35:55 +02:00
} else if ( tinterlace -> mode == MODE_MERGEX2 ) {
outlink -> frame_rate = inlink -> frame_rate ;
outlink -> time_base = inlink -> time_base ;
2014-11-13 01:39:45 +03:00
} else if ( tinterlace -> mode != MODE_PAD ) {
outlink -> frame_rate = av_mul_q ( inlink -> frame_rate , ( AVRational ){ 1 , 2 });
2014-12-01 20:52:10 +01:00
outlink -> time_base = av_mul_q ( inlink -> time_base , ( AVRational ){ 2 , 1 });
2014-03-16 15:29:30 +00:00
}
2014-12-01 20:52:10 +01:00
for ( i = 0 ; i < FF_ARRAY_ELEMS ( standard_tbs ); i ++ ){
if ( ! av_cmp_q ( standard_tbs [ i ], outlink -> time_base ))
break ;
}
if ( i == FF_ARRAY_ELEMS ( standard_tbs ) ||
( tinterlace -> flags & TINTERLACE_FLAG_EXACT_TB ))
outlink -> time_base = tinterlace -> preout_time_base ;
2017-09-19 22:23:23 +02:00
tinterlace -> csp = av_pix_fmt_desc_get ( outlink -> format );
2017-04-20 23:33:39 +02:00
if ( tinterlace -> flags & TINTERLACE_FLAG_CVLPF ) {
2017-09-19 22:23:23 +02: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 05:51:12 +02:00
#if ARCH_X86
ff_tinterlace_init_x86 ( tinterlace );
#endif
2017-04-20 23:33:39 +02:00
} else if ( tinterlace -> flags & TINTERLACE_FLAG_VLPF ) {
2017-09-19 22:23:23 +02: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 05:51:12 +02:00
#if ARCH_X86
ff_tinterlace_init_x86 ( tinterlace );
#endif
2014-11-15 03:20:02 +01:00
}
2017-04-20 23:33:39 +02: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 15:02:16 +01:00
inlink -> h , outlink -> h );
2011-06-25 13:06:24 +02: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 22:11:15 +02:00
* @param interleave leave a padding line between each copied line
2011-06-25 13:06:24 +02:00
* @param dst_field copy to upper or lower field,
* only meaningful when interleave is selected
2012-12-30 15:02:16 +01:00
* @param flags context flags
2011-06-25 13:06:24 +02:00
*/
static inline
2014-11-15 03:20:02 +01:00
void copy_picture_field ( TInterlaceContext * tinterlace ,
uint8_t * dst [ 4 ], int dst_linesize [ 4 ],
2012-09-05 10:37:12 +02:00
const uint8_t * src [ 4 ], int src_linesize [ 4 ],
2012-10-08 20:54:00 +02:00
enum AVPixelFormat format , int w , int src_h ,
2012-12-30 15:02:16 +01:00
int src_field , int interleave , int dst_field ,
int flags )
2011-06-25 13:06:24 +02:00
{
2012-10-20 06:43:48 +02:00
const AVPixFmtDescriptor * desc = av_pix_fmt_desc_get ( format );
2015-01-06 23:20:18 +01:00
int hsub = desc -> log2_chroma_w ;
2011-06-25 13:06:24 +02:00
int plane , vsub = desc -> log2_chroma_h ;
int k = src_field == FIELD_UPPER_AND_LOWER ? 1 : 2 ;
2014-11-16 01:06:18 +01:00
int h ;
2011-06-25 13:06:24 +02: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 13:06:24 +02:00
uint8_t * dstp = dst [ plane ];
2012-09-05 10:37:12 +02:00
const uint8_t * srcp = src [ plane ];
2017-09-14 21:20:24 +02:00
int srcp_linesize = src_linesize [ plane ] * k ;
int dstp_linesize = dst_linesize [ plane ] * ( interleave ? 2 : 1 );
2017-09-19 22:23:23 +02:00
int clip_max = ( 1 << tinterlace -> csp -> comp [ plane ]. depth ) - 1 ;
2012-11-03 02:21:49 +01:00
2013-04-29 12:26:15 +02:00
lines = ( lines + ( src_field == FIELD_UPPER )) / k ;
2011-06-25 13:06:24 +02:00
if ( src_field == FIELD_LOWER )
srcp += src_linesize [ plane ];
if ( interleave && dst_field == FIELD_LOWER )
dstp += dst_linesize [ plane ];
2017-04-20 23:33:39 +02: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-17 23:41:31 -03:00
if ( flags & ( TINTERLACE_FLAG_VLPF | TINTERLACE_FLAG_CVLPF )) {
int x = !! ( flags & TINTERLACE_FLAG_CVLPF );
2017-04-20 23:33:39 +02:00
for ( h = lines ; h > 0 ; h -- ) {
ptrdiff_t pref = src_linesize [ plane ];
ptrdiff_t mref = - pref ;
2017-09-14 21:20:24 +02: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 03:20:02 +01:00
2017-09-19 22:23:23 +02:00
tinterlace -> lowpass_line ( dstp , cols , srcp , mref , pref , clip_max );
2012-12-30 15:02:16 +01:00
dstp += dstp_linesize ;
srcp += srcp_linesize ;
}
} else {
2017-09-19 22:23:23 +02:00
if ( tinterlace -> csp -> comp [ plane ]. depth > 8 )
cols *= 2 ;
2017-09-14 21:20:24 +02:00
av_image_copy_plane ( dstp , dstp_linesize , srcp , srcp_linesize , cols , lines );
2012-12-30 15:02:16 +01:00
}
2011-06-25 13:06:24 +02:00
}
}
2013-03-10 01:30:30 +01:00
static int filter_frame ( AVFilterLink * inlink , AVFrame * picref )
2011-06-25 13:06:24 +02:00
{
AVFilterContext * ctx = inlink -> dst ;
AVFilterLink * outlink = ctx -> outputs [ 0 ];
TInterlaceContext * tinterlace = ctx -> priv ;
2013-03-10 01:30:30 +01:00
AVFrame * cur , * next , * out ;
2012-12-06 14:40:36 +01:00
int field , tff , ret ;
2011-06-25 13:06:24 +02:00
2013-03-10 01:30:30 +01:00
av_frame_free ( & tinterlace -> cur );
2012-12-06 14:40:36 +01:00
tinterlace -> cur = tinterlace -> next ;
tinterlace -> next = picref ;
cur = tinterlace -> cur ;
next = tinterlace -> next ;
2011-06-25 13:06:24 +02:00
/* we need at least two frames */
if ( ! tinterlace -> cur )
2012-07-22 22:57:02 +02:00
return 0 ;
2011-06-25 13:06:24 +02:00
switch ( tinterlace -> mode ) {
2015-09-30 15:35:55 +02: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 16:45:35 +02:00
case MODE_MERGE : /* move the odd frame into the upper field of the new image, even into
2011-06-25 13:06:24 +02:00
* the lower field, generating a double-height video at half framerate */
2013-03-10 01:30:30 +01:00
out = ff_get_video_buffer ( outlink , outlink -> w , outlink -> h );
2012-12-06 14:40:36 +01:00
if ( ! out )
return AVERROR ( ENOMEM );
2013-03-10 01:30:30 +01:00
av_frame_copy_props ( out , cur );
out -> height = outlink -> h ;
out -> interlaced_frame = 1 ;
out -> top_field_first = 1 ;
2015-06-04 14:11:29 +02:00
out -> sample_aspect_ratio = av_mul_q ( cur -> sample_aspect_ratio , av_make_q ( 2 , 1 ));
2011-06-25 13:06:24 +02:00
/* write odd frame lines into the upper field of the new frame */
2014-11-15 03:20:02 +01:00
copy_picture_field ( tinterlace , out -> data , out -> linesize ,
2012-09-05 10:37:12 +02:00
( const uint8_t ** ) cur -> data , cur -> linesize ,
2011-06-25 13:06:24 +02:00
inlink -> format , inlink -> w , inlink -> h ,
2020-07-11 13:20:45 +02: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 13:06:24 +02:00
/* write even frame lines into the lower field of the new frame */
2014-11-15 03:20:02 +01:00
copy_picture_field ( tinterlace , out -> data , out -> linesize ,
2012-09-05 10:37:12 +02:00
( const uint8_t ** ) next -> data , next -> linesize ,
2011-06-25 13:06:24 +02:00
inlink -> format , inlink -> w , inlink -> h ,
2020-07-11 13:20:45 +02: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 15:35:55 +02:00
if ( tinterlace -> mode != MODE_MERGEX2 )
av_frame_free ( & tinterlace -> next );
2011-06-25 13:06:24 +02:00
break ;
2012-04-29 16:45:35 +02: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 01:30:30 +01:00
out = av_frame_clone ( tinterlace -> mode == MODE_DROP_EVEN ? cur : next );
2013-11-19 18:08:58 +01:00
if ( ! out )
return AVERROR ( ENOMEM );
2013-03-10 01:30:30 +01:00
av_frame_free ( & tinterlace -> next );
2011-06-25 13:06:24 +02:00
break ;
2012-04-29 16:45:35 +02:00
case MODE_PAD : /* expand each frame to double height, but pad alternate
* lines with black; framerate unchanged */
2013-03-10 01:30:30 +01: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 01:30:30 +01:00
av_frame_copy_props ( out , cur );
out -> height = outlink -> h ;
2015-06-04 14:11:29 +02:00
out -> sample_aspect_ratio = av_mul_q ( cur -> sample_aspect_ratio , av_make_q ( 2 , 1 ));
2011-06-25 13:06:24 +02:00
2020-07-11 13:20:02 +02:00
field = ( 1 + outlink -> frame_count_in ) & 1 ? FIELD_UPPER : FIELD_LOWER ;
2011-06-25 13:06:24 +02:00
/* copy upper and lower fields */
2014-11-15 03:20:02 +01:00
copy_picture_field ( tinterlace , out -> data , out -> linesize ,
2012-09-05 10:37:12 +02:00
( const uint8_t ** ) cur -> data , cur -> linesize ,
2011-06-25 13:06:24 +02:00
inlink -> format , inlink -> w , inlink -> h ,
2012-12-30 15:02:16 +01:00
FIELD_UPPER_AND_LOWER , 1 , field , tinterlace -> flags );
2011-06-25 13:06:24 +02:00
/* pad with black the other field */
2014-11-15 03:20:02 +01:00
copy_picture_field ( tinterlace , out -> data , out -> linesize ,
2012-09-05 10:37:12 +02:00
( const uint8_t ** ) tinterlace -> black_data , tinterlace -> black_linesize ,
2011-06-25 13:06:24 +02:00
inlink -> format , inlink -> w , inlink -> h ,
2012-12-30 15:02:16 +01:00
FIELD_UPPER_AND_LOWER , 1 , ! field , tinterlace -> flags );
2011-06-25 13:06:24 +02:00
break ;
2011-12-31 14:12:01 +01: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 16:45:35 +02:00
case MODE_INTERLEAVE_TOP : /* top field first */
case MODE_INTERLEAVE_BOTTOM : /* bottom field first */
2019-12-06 11:01:11 +01: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 16:45:35 +02:00
tff = tinterlace -> mode == MODE_INTERLEAVE_TOP ;
2013-03-10 01:30:30 +01:00
out = ff_get_video_buffer ( outlink , outlink -> w , outlink -> h );
2012-12-06 14:40:36 +01:00
if ( ! out )
return AVERROR ( ENOMEM );
2013-03-10 01:30:30 +01:00
av_frame_copy_props ( out , cur );
out -> interlaced_frame = 1 ;
out -> top_field_first = tff ;
2011-06-25 13:06:24 +02:00
2011-12-31 14:12:01 +01:00
/* copy upper/lower field from cur */
2014-11-15 03:20:02 +01:00
copy_picture_field ( tinterlace , out -> data , out -> linesize ,
2012-09-05 10:37:12 +02:00
( const uint8_t ** ) cur -> data , cur -> linesize ,
2011-06-25 13:06:24 +02:00
inlink -> format , inlink -> w , inlink -> h ,
2012-12-30 15:02:16 +01:00
tff ? FIELD_UPPER : FIELD_LOWER , 1 , tff ? FIELD_UPPER : FIELD_LOWER ,
tinterlace -> flags );
2011-12-31 14:12:01 +01:00
/* copy lower/upper field from next */
2014-11-15 03:20:02 +01:00
copy_picture_field ( tinterlace , out -> data , out -> linesize ,
2012-09-05 10:37:12 +02:00
( const uint8_t ** ) next -> data , next -> linesize ,
2011-06-25 13:06:24 +02:00
inlink -> format , inlink -> w , inlink -> h ,
2012-12-30 15:02:16 +01:00
tff ? FIELD_LOWER : FIELD_UPPER , 1 , tff ? FIELD_LOWER : FIELD_UPPER ,
tinterlace -> flags );
2013-03-10 01:30:30 +01:00
av_frame_free ( & tinterlace -> next );
2011-06-25 13:06:24 +02:00
break ;
2012-04-29 16:45:35 +02:00
case MODE_INTERLACEX2 : /* re-interlace preserving image height, double frame rate */
2012-04-29 15:25:57 +02:00
/* output current frame first */
2013-03-10 01:30:30 +01:00
out = av_frame_clone ( cur );
2012-12-06 14:40:36 +01:00
if ( ! out )
return AVERROR ( ENOMEM );
2013-03-10 01:30:30 +01: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 15:25:57 +02:00
2014-12-01 20:52:10 +01:00
out -> pts = av_rescale_q ( out -> pts , tinterlace -> preout_time_base , outlink -> time_base );
2012-12-06 14:40:36 +01:00
if (( ret = ff_filter_frame ( outlink , out )) < 0 )
return ret ;
2012-04-29 15:25:57 +02:00
/* output mix of current and next frame */
2013-03-10 01:30:30 +01:00
tff = next -> top_field_first ;
out = ff_get_video_buffer ( outlink , outlink -> w , outlink -> h );
2012-12-06 14:40:36 +01:00
if ( ! out )
return AVERROR ( ENOMEM );
2013-03-10 01:30:30 +01:00
av_frame_copy_props ( out , next );
out -> interlaced_frame = 1 ;
2014-11-14 00:04:51 +01:00
out -> top_field_first = ! tff ;
2012-04-29 15:25:57 +02: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 15:25:57 +02:00
/* write current frame second field lines into the second field of the new frame */
2014-11-15 03:20:02 +01:00
copy_picture_field ( tinterlace , out -> data , out -> linesize ,
2012-09-05 10:37:12 +02:00
( const uint8_t ** ) cur -> data , cur -> linesize ,
2012-04-29 15:25:57 +02:00
inlink -> format , inlink -> w , inlink -> h ,
2012-12-30 15:02:16 +01:00
tff ? FIELD_LOWER : FIELD_UPPER , 1 , tff ? FIELD_LOWER : FIELD_UPPER ,
tinterlace -> flags );
2012-04-29 15:25:57 +02:00
/* write next frame first field lines into the first field of the new frame */
2014-11-15 03:20:02 +01:00
copy_picture_field ( tinterlace , out -> data , out -> linesize ,
2012-09-05 10:37:12 +02:00
( const uint8_t ** ) next -> data , next -> linesize ,
2012-04-29 15:25:57 +02:00
inlink -> format , inlink -> w , inlink -> h ,
2012-12-30 15:02:16 +01:00
tff ? FIELD_UPPER : FIELD_LOWER , 1 , tff ? FIELD_UPPER : FIELD_LOWER ,
tinterlace -> flags );
2012-04-29 15:25:57 +02:00
break ;
2012-12-10 23:55:21 +01:00
default :
av_assert0 ( 0 );
2011-06-25 13:06:24 +02:00
}
2014-12-01 20:52:10 +01:00
out -> pts = av_rescale_q ( out -> pts , tinterlace -> preout_time_base , outlink -> time_base );
2022-10-05 11:11:08 +02:00
out -> duration = av_rescale_q ( 1 , av_inv_q ( outlink -> frame_rate ), outlink -> time_base );
2012-12-06 14:40:36 +01:00
ret = ff_filter_frame ( outlink , out );
2012-07-22 22:57:02 +02:00
2012-12-06 14:40:36 +01:00
return ret ;
2011-06-25 13:06:24 +02:00
}
2018-08-23 23:37:10 +02:00
static int init_interlace ( AVFilterContext * ctx )
{
TInterlaceContext * tinterlace = ctx -> priv ;
if ( tinterlace -> mode <= MODE_BFF )
tinterlace -> mode += MODE_INTERLEAVE_TOP ;
2019-12-06 11:02:36 +01:00
tinterlace -> flags |= TINTERLACE_FLAG_BYPASS_IL ;
2019-12-06 10:35:41 +01:00
if ( tinterlace -> lowpass == VLPF_LIN )
tinterlace -> flags |= TINTERLACE_FLAG_VLPF ;
if ( tinterlace -> lowpass == VLPF_CMP )
tinterlace -> flags |= TINTERLACE_FLAG_CVLPF ;
2018-08-23 23:37:10 +02:00
return 0 ;
}
2012-11-28 20:01:59 +01:00
static const AVFilterPad tinterlace_inputs [] = {
{
. name = "default" ,
. type = AVMEDIA_TYPE_VIDEO ,
2012-12-06 14:40:36 +01:00
. filter_frame = filter_frame ,
2012-11-28 20:01:59 +01: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 20:01:59 +01:00
},
};
2021-04-19 18:33:56 +02:00
const AVFilter ff_vf_tinterlace = {
2011-06-25 13:06:24 +02:00
. name = "tinterlace" ,
. description = NULL_IF_CONFIG_SMALL ( "Perform temporal field interlacing." ),
. priv_size = sizeof ( TInterlaceContext ),
. uninit = uninit ,
2021-08-12 13:05:31 +02:00
FILTER_INPUTS ( tinterlace_inputs ),
FILTER_OUTPUTS ( tinterlace_outputs ),
2021-09-27 17:31:22 +02:00
FILTER_PIXFMTS_ARRAY ( pix_fmts ),
2012-12-06 22:03:02 +01:00
. priv_class = & tinterlace_class ,
2011-06-25 13:06:24 +02:00
};
2018-04-17 13:48:28 +03:00
2021-04-19 18:33:56 +02:00
const AVFilter ff_vf_interlace = {
2018-04-17 13:48:28 +03:00
. name = "interlace" ,
. description = NULL_IF_CONFIG_SMALL ( "Convert progressive video into interlaced." ),
. priv_size = sizeof ( TInterlaceContext ),
2018-08-23 23:37:10 +02:00
. init = init_interlace ,
2018-04-17 13:48:28 +03:00
. uninit = uninit ,
2021-08-12 13:05:31 +02:00
FILTER_INPUTS ( tinterlace_inputs ),
FILTER_OUTPUTS ( tinterlace_outputs ),
2021-09-27 17:31:22 +02:00
FILTER_PIXFMTS_ARRAY ( pix_fmts ),
2018-04-17 13:48:28 +03:00
. priv_class = & interlace_class ,
};