2013-04-19 02:49:27 +03:00
/*
* Copyright ( c ) 2013 Georg Martius < georg dot martius at web dot de >
*
* 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
*/
# define DEFAULT_RESULT_NAME "transforms.trf"
# include <vid.stab/libvidstab.h>
# include "libavutil/common.h"
2022-08-27 23:33:06 +02:00
# include "libavutil/file_open.h"
2013-04-19 02:49:27 +03:00
# include "libavutil/opt.h"
2022-08-27 23:33:06 +02:00
# include "libavutil/pixdesc.h"
2013-04-19 02:49:27 +03:00
# include "avfilter.h"
2023-02-06 15:57:50 +02:00
# include "filters.h"
2023-08-03 14:37:51 +02:00
# include "video.h"
2013-04-19 02:49:27 +03:00
# include "vidstabutils.h"
2017-05-12 20:00:49 +02:00
typedef struct StabData {
2013-04-24 18:01:26 +03:00
const AVClass * class ;
2013-04-19 02:49:27 +03:00
VSMotionDetect md ;
VSMotionDetectConfig conf ;
2013-04-24 18:01:26 +03:00
char * result ;
2023-10-22 12:48:14 +02:00
int fileformat ;
2013-04-24 18:01:26 +03:00
FILE * f ;
2013-04-19 02:49:27 +03:00
} StabData ;
# define OFFSET(x) offsetof(StabData, x)
# define OFFSETC(x) (offsetof(StabData, conf)+offsetof(VSMotionDetectConfig, x))
# define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
2013-04-24 18:01:26 +03:00
static const AVOption vidstabdetect_options [ ] = {
2013-04-24 18:29:29 +03:00
{ " result " , " path to the file used to write the transforms " , OFFSET ( result ) , AV_OPT_TYPE_STRING , { . str = DEFAULT_RESULT_NAME } , . flags = FLAGS } ,
2013-04-19 02:49:27 +03:00
{ " shakiness " , " how shaky is the video and how quick is the camera? "
2013-04-24 18:29:29 +03:00
" 1: little (fast) 10: very strong/quick (slow) " , OFFSETC ( shakiness ) , AV_OPT_TYPE_INT , { . i64 = 5 } , 1 , 10 , FLAGS } ,
2013-10-09 00:57:17 +03:00
{ " accuracy " , " (>=shakiness) 1: low 15: high (slow) " , OFFSETC ( accuracy ) , AV_OPT_TYPE_INT , { . i64 = 15 } , 1 , 15 , FLAGS } ,
2013-04-24 18:29:29 +03:00
{ " stepsize " , " region around minimum is scanned with 1 pixel resolution " , OFFSETC ( stepSize ) , AV_OPT_TYPE_INT , { . i64 = 6 } , 1 , 32 , FLAGS } ,
{ " mincontrast " , " below this contrast a field is discarded (0-1) " , OFFSETC ( contrastThreshold ) , AV_OPT_TYPE_DOUBLE , { . dbl = 0.25 } , 0.0 , 1.0 , FLAGS } ,
{ " show " , " 0: draw nothing; 1,2: show fields and transforms " , OFFSETC ( show ) , AV_OPT_TYPE_INT , { . i64 = 0 } , 0 , 2 , FLAGS } ,
2013-04-19 02:49:27 +03:00
{ " tripod " , " virtual tripod mode (if >0): motion is compared to a reference "
2013-04-24 18:29:29 +03:00
" reference frame (frame # is the value) " , OFFSETC ( virtualTripod ) , AV_OPT_TYPE_INT , { . i64 = 0 } , 0 , INT_MAX , FLAGS } ,
2023-10-22 12:48:14 +02:00
# ifdef LIBVIDSTAB_FILE_FORMAT_VERSION
2024-02-11 16:41:05 +02:00
{ " fileformat " , " transforms data file format " , OFFSET ( fileformat ) , AV_OPT_TYPE_INT , { . i64 = BINARY_SERIALIZATION_MODE } , ASCII_SERIALIZATION_MODE , BINARY_SERIALIZATION_MODE , FLAGS , . unit = " file_format " } ,
{ " ascii " , " ASCII text " , 0 , AV_OPT_TYPE_CONST , { . i64 = ASCII_SERIALIZATION_MODE } , 0 , 0 , FLAGS , . unit = " file_format " } ,
{ " binary " , " binary " , 0 , AV_OPT_TYPE_CONST , { . i64 = BINARY_SERIALIZATION_MODE } , 0 , 0 , FLAGS , . unit = " file_format " } ,
2023-10-22 12:48:14 +02:00
# endif
2013-04-24 18:01:26 +03:00
{ NULL }
2013-04-19 02:49:27 +03:00
} ;
AVFILTER_DEFINE_CLASS ( vidstabdetect ) ;
static av_cold int init ( AVFilterContext * ctx )
{
2015-09-07 16:10:06 +02:00
StabData * s = ctx - > priv ;
2014-08-22 05:12:09 +03:00
ff_vs_init ( ) ;
2015-09-07 16:10:06 +02:00
s - > class = & vidstabdetect_class ;
2013-04-19 02:49:27 +03:00
av_log ( ctx , AV_LOG_VERBOSE , " vidstabdetect filter: init %s \n " , LIBVIDSTAB_VERSION ) ;
return 0 ;
}
static av_cold void uninit ( AVFilterContext * ctx )
{
2015-09-07 16:10:06 +02:00
StabData * s = ctx - > priv ;
VSMotionDetect * md = & ( s - > md ) ;
2013-04-19 02:49:27 +03:00
2015-09-07 16:10:06 +02:00
if ( s - > f ) {
fclose ( s - > f ) ;
s - > f = NULL ;
2013-04-19 02:49:27 +03:00
}
vsMotionDetectionCleanup ( md ) ;
}
static int config_input ( AVFilterLink * inlink )
{
AVFilterContext * ctx = inlink - > dst ;
2015-09-07 16:10:06 +02:00
StabData * s = ctx - > priv ;
2013-04-19 02:49:27 +03:00
2015-09-07 16:10:06 +02:00
VSMotionDetect * md = & ( s - > md ) ;
2013-04-19 02:49:27 +03:00
VSFrameInfo fi ;
const AVPixFmtDescriptor * desc = av_pix_fmt_desc_get ( inlink - > format ) ;
2017-12-23 12:44:25 +02:00
int is_planar = desc - > flags & AV_PIX_FMT_FLAG_PLANAR ;
2023-10-22 12:48:14 +02:00
const char * file_mode = " w " ;
# ifdef LIBVIDSTAB_FILE_FORMAT_VERSION
md - > serializationMode = s - > fileformat ;
if ( s - > fileformat = = BINARY_SERIALIZATION_MODE )
file_mode = " wb " ;
# endif
2013-04-19 02:49:27 +03:00
2014-08-22 05:12:09 +03:00
vsFrameInfoInit ( & fi , inlink - > w , inlink - > h ,
ff_av2vs_pixfmt ( ctx , inlink - > format ) ) ;
2017-12-23 12:44:25 +02:00
if ( ! is_planar & & fi . bytesPerPixel ! = av_get_bits_per_pixel ( desc ) / 8 ) {
2013-04-19 02:49:27 +03:00
av_log ( ctx , AV_LOG_ERROR , " pixel-format error: wrong bits/per/pixel, please report a BUG " ) ;
return AVERROR ( EINVAL ) ;
}
2013-04-24 18:01:26 +03:00
if ( fi . log2ChromaW ! = desc - > log2_chroma_w ) {
2013-04-19 02:49:27 +03:00
av_log ( ctx , AV_LOG_ERROR , " pixel-format error: log2_chroma_w, please report a BUG " ) ;
return AVERROR ( EINVAL ) ;
}
2013-04-24 18:01:26 +03:00
if ( fi . log2ChromaH ! = desc - > log2_chroma_h ) {
2013-04-19 02:49:27 +03:00
av_log ( ctx , AV_LOG_ERROR , " pixel-format error: log2_chroma_h, please report a BUG " ) ;
return AVERROR ( EINVAL ) ;
}
2013-04-24 18:01:26 +03:00
// set values that are not initialized by the options
2015-09-07 16:10:06 +02:00
s - > conf . algo = 1 ;
s - > conf . modName = " vidstabdetect " ;
if ( vsMotionDetectInit ( md , & s - > conf , & fi ) ! = VS_OK ) {
2013-04-19 02:49:27 +03:00
av_log ( ctx , AV_LOG_ERROR , " initialization of Motion Detection failed, please report a BUG " ) ;
return AVERROR ( EINVAL ) ;
}
2015-09-07 16:10:06 +02:00
vsMotionDetectGetConfig ( & s - > conf , md ) ;
2013-04-19 02:49:27 +03:00
av_log ( ctx , AV_LOG_INFO , " Video stabilization settings (pass 1/2): \n " ) ;
2015-09-07 16:10:06 +02:00
av_log ( ctx , AV_LOG_INFO , " shakiness = %d \n " , s - > conf . shakiness ) ;
av_log ( ctx , AV_LOG_INFO , " accuracy = %d \n " , s - > conf . accuracy ) ;
av_log ( ctx , AV_LOG_INFO , " stepsize = %d \n " , s - > conf . stepSize ) ;
av_log ( ctx , AV_LOG_INFO , " mincontrast = %f \n " , s - > conf . contrastThreshold ) ;
av_log ( ctx , AV_LOG_INFO , " tripod = %d \n " , s - > conf . virtualTripod ) ;
av_log ( ctx , AV_LOG_INFO , " show = %d \n " , s - > conf . show ) ;
av_log ( ctx , AV_LOG_INFO , " result = %s \n " , s - > result ) ;
2023-10-22 12:48:14 +02:00
s - > f = avpriv_fopen_utf8 ( s - > result , file_mode ) ;
2015-09-07 16:10:06 +02:00
if ( s - > f = = NULL ) {
av_log ( ctx , AV_LOG_ERROR , " cannot open transform file %s \n " , s - > result ) ;
2013-04-19 02:49:27 +03:00
return AVERROR ( EINVAL ) ;
2013-04-24 18:01:26 +03:00
} else {
2015-09-07 16:10:06 +02:00
if ( vsPrepareFile ( md , s - > f ) ! = VS_OK ) {
av_log ( ctx , AV_LOG_ERROR , " cannot write to transform file %s \n " , s - > result ) ;
2013-04-19 02:49:27 +03:00
return AVERROR ( EINVAL ) ;
}
}
return 0 ;
}
static int filter_frame ( AVFilterLink * inlink , AVFrame * in )
{
AVFilterContext * ctx = inlink - > dst ;
2015-09-07 16:10:06 +02:00
StabData * s = ctx - > priv ;
VSMotionDetect * md = & ( s - > md ) ;
2013-04-19 02:49:27 +03:00
LocalMotions localmotions ;
AVFilterLink * outlink = inlink - > dst - > outputs [ 0 ] ;
VSFrame frame ;
2023-02-06 15:57:50 +02:00
int plane , ret ;
2013-04-19 02:49:27 +03:00
2023-02-06 15:57:50 +02:00
if ( s - > conf . show > 0 & & ! av_frame_is_writable ( in ) ) {
ret = ff_inlink_make_frame_writable ( inlink , & in ) ;
if ( ret < 0 ) {
av_frame_free ( & in ) ;
return ret ;
}
}
2013-04-19 02:49:27 +03:00
2013-04-24 18:01:26 +03:00
for ( plane = 0 ; plane < md - > fi . planes ; plane + + ) {
2013-04-19 02:49:27 +03:00
frame . data [ plane ] = in - > data [ plane ] ;
frame . linesize [ plane ] = in - > linesize [ plane ] ;
}
2013-04-24 18:01:26 +03:00
if ( vsMotionDetection ( md , & localmotions , & frame ) ! = VS_OK ) {
2013-04-19 02:49:27 +03:00
av_log ( ctx , AV_LOG_ERROR , " motion detection failed " ) ;
2023-10-02 16:09:31 +02:00
return AVERROR_EXTERNAL ;
2013-04-19 02:49:27 +03:00
} else {
2015-09-07 16:10:06 +02:00
if ( vsWriteToFile ( md , s - > f , & localmotions ) ! = VS_OK ) {
2014-10-25 14:19:41 +03:00
int ret = AVERROR ( errno ) ;
2013-04-19 02:49:27 +03:00
av_log ( ctx , AV_LOG_ERROR , " cannot write to transform file " ) ;
2014-10-25 14:19:41 +03:00
return ret ;
2013-04-19 02:49:27 +03:00
}
vs_vector_del ( & localmotions ) ;
}
2013-04-24 18:55:55 +03:00
return ff_filter_frame ( outlink , in ) ;
2013-04-19 02:49:27 +03:00
}
static const AVFilterPad avfilter_vf_vidstabdetect_inputs [ ] = {
{
2013-09-07 15:13:50 +03:00
. name = " default " ,
. type = AVMEDIA_TYPE_VIDEO ,
. filter_frame = filter_frame ,
. config_props = config_input ,
2013-04-19 02:49:27 +03:00
} ,
} ;
2021-04-19 18:33:56 +02:00
const AVFilter ff_vf_vidstabdetect = {
2013-04-19 02:49:27 +03:00
. name = " vidstabdetect " ,
2013-04-24 18:01:50 +03:00
. description = NULL_IF_CONFIG_SMALL ( " Extract relative transformations, "
" pass 1 of 2 for stabilization "
" (see vidstabtransform for pass 2). " ) ,
2013-04-19 02:49:27 +03:00
. priv_size = sizeof ( StabData ) ,
. init = init ,
. uninit = uninit ,
2021-11-22 15:39:11 +02:00
. flags = AVFILTER_FLAG_METADATA_ONLY ,
2021-08-12 13:05:31 +02:00
FILTER_INPUTS ( avfilter_vf_vidstabdetect_inputs ) ,
2023-08-03 14:37:51 +02:00
FILTER_OUTPUTS ( ff_video_default_filterpad ) ,
2021-09-27 17:24:30 +02:00
FILTER_PIXFMTS_ARRAY ( ff_vidstab_pix_fmts ) ,
2013-04-19 02:49:27 +03:00
. priv_class = & vidstabdetect_class ,
} ;