2013-04-19 01:49:27 +02: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"
# include "libavutil/opt.h"
# include "libavutil/imgutils.h"
# include "avfilter.h"
# include "internal.h"
# include "vidstabutils.h"
typedef struct {
2013-04-24 17:01:26 +02:00
const AVClass * class ;
2013-04-19 01:49:27 +02:00
VSMotionDetect md ;
VSMotionDetectConfig conf ;
2013-04-24 17:01:26 +02:00
char * result ;
FILE * f ;
2013-04-19 01:49:27 +02: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 17:01:26 +02:00
static const AVOption vidstabdetect_options [ ] = {
2013-04-24 17:13:01 +02:00
{ " result " , " path to the file used to write the transforms (def:transforms.trf) " , OFFSET ( result ) , AV_OPT_TYPE_STRING , { . str = DEFAULT_RESULT_NAME } , . flags = FLAGS } ,
2013-04-19 01:49:27 +02:00
{ " shakiness " , " how shaky is the video and how quick is the camera? "
2013-04-24 17:01:26 +02:00
" 1: little (fast) 10: very strong/quick (slow) (def: 5) " , OFFSETC ( shakiness ) , AV_OPT_TYPE_INT , { . i64 = 5 } , 1 , 10 , FLAGS } ,
{ " accuracy " , " (>=shakiness) 1: low 15: high (slow) (def: 9) " , OFFSETC ( accuracy ) , AV_OPT_TYPE_INT , { . i64 = 9 } , 1 , 15 , FLAGS } ,
{ " stepsize " , " region around minimum is scanned with 1 pixel resolution (def: 6) " , OFFSETC ( stepSize ) , AV_OPT_TYPE_INT , { . i64 = 6 } , 1 , 32 , FLAGS } ,
{ " mincontrast " , " below this contrast a field is discarded (0-1) (def: 0.3) " , OFFSETC ( contrastThreshold ) , AV_OPT_TYPE_DOUBLE , { . dbl = 0.25 } , 0.0 , 1.0 , FLAGS } ,
{ " show " , " 0: draw nothing (def); 1,2: show fields and transforms " , OFFSETC ( show ) , AV_OPT_TYPE_INT , { . i64 = 0 } , 0 , 2 , FLAGS } ,
2013-04-19 01:49:27 +02:00
{ " tripod " , " virtual tripod mode (if >0): motion is compared to a reference "
2013-04-24 17:01:26 +02:00
" reference frame (frame # is the value) (def: 0) " , OFFSETC ( virtualTripod ) , AV_OPT_TYPE_INT , { . i64 = 0 } , 0 , INT_MAX , FLAGS } ,
{ NULL }
2013-04-19 01:49:27 +02:00
} ;
AVFILTER_DEFINE_CLASS ( vidstabdetect ) ;
static av_cold int init ( AVFilterContext * ctx )
{
2013-04-24 17:01:26 +02:00
StabData * sd = ctx - > priv ;
2013-04-19 01:49:27 +02:00
vs_set_mem_and_log_functions ( ) ;
sd - > class = & vidstabdetect_class ;
av_log ( ctx , AV_LOG_VERBOSE , " vidstabdetect filter: init %s \n " , LIBVIDSTAB_VERSION ) ;
return 0 ;
}
static av_cold void uninit ( AVFilterContext * ctx )
{
StabData * sd = ctx - > priv ;
2013-04-24 17:01:26 +02:00
VSMotionDetect * md = & ( sd - > md ) ;
2013-04-19 01:49:27 +02:00
if ( sd - > f ) {
fclose ( sd - > f ) ;
sd - > f = NULL ;
}
vsMotionDetectionCleanup ( md ) ;
}
static int query_formats ( AVFilterContext * ctx )
{
// If you add something here also add it in vidstabutils.c
static const enum AVPixelFormat pix_fmts [ ] = {
AV_PIX_FMT_YUV444P , AV_PIX_FMT_YUV422P , AV_PIX_FMT_YUV420P ,
AV_PIX_FMT_YUV411P , AV_PIX_FMT_YUV410P , AV_PIX_FMT_YUVA420P ,
AV_PIX_FMT_YUV440P , AV_PIX_FMT_GRAY8 ,
AV_PIX_FMT_RGB24 , AV_PIX_FMT_BGR24 , AV_PIX_FMT_RGBA ,
AV_PIX_FMT_NONE
} ;
ff_set_common_formats ( ctx , ff_make_format_list ( pix_fmts ) ) ;
return 0 ;
}
static int config_input ( AVFilterLink * inlink )
{
AVFilterContext * ctx = inlink - > dst ;
StabData * sd = ctx - > priv ;
VSMotionDetect * md = & ( sd - > md ) ;
VSFrameInfo fi ;
const AVPixFmtDescriptor * desc = av_pix_fmt_desc_get ( inlink - > format ) ;
2013-04-24 17:01:26 +02:00
vsFrameInfoInit ( & fi , inlink - > w , inlink - > h , av_2_vs_pixel_format ( ctx , inlink - > format ) ) ;
if ( fi . bytesPerPixel ! = av_get_bits_per_pixel ( desc ) / 8 ) {
2013-04-19 01:49:27 +02:00
av_log ( ctx , AV_LOG_ERROR , " pixel-format error: wrong bits/per/pixel, please report a BUG " ) ;
return AVERROR ( EINVAL ) ;
}
2013-04-24 17:01:26 +02:00
if ( fi . log2ChromaW ! = desc - > log2_chroma_w ) {
2013-04-19 01:49:27 +02:00
av_log ( ctx , AV_LOG_ERROR , " pixel-format error: log2_chroma_w, please report a BUG " ) ;
return AVERROR ( EINVAL ) ;
}
2013-04-24 17:01:26 +02:00
if ( fi . log2ChromaH ! = desc - > log2_chroma_h ) {
2013-04-19 01:49:27 +02:00
av_log ( ctx , AV_LOG_ERROR , " pixel-format error: log2_chroma_h, please report a BUG " ) ;
return AVERROR ( EINVAL ) ;
}
2013-04-24 17:01:26 +02:00
// set values that are not initialized by the options
2013-04-19 01:49:27 +02:00
sd - > conf . algo = 1 ;
sd - > conf . modName = " vidstabdetect " ;
2013-04-24 17:01:26 +02:00
if ( vsMotionDetectInit ( md , & sd - > conf , & fi ) ! = VS_OK ) {
2013-04-19 01:49:27 +02:00
av_log ( ctx , AV_LOG_ERROR , " initialization of Motion Detection failed, please report a BUG " ) ;
return AVERROR ( EINVAL ) ;
}
vsMotionDetectGetConfig ( & sd - > conf , md ) ;
av_log ( ctx , AV_LOG_INFO , " Video stabilization settings (pass 1/2): \n " ) ;
av_log ( ctx , AV_LOG_INFO , " shakiness = %d \n " , sd - > conf . shakiness ) ;
av_log ( ctx , AV_LOG_INFO , " accuracy = %d \n " , sd - > conf . accuracy ) ;
av_log ( ctx , AV_LOG_INFO , " stepsize = %d \n " , sd - > conf . stepSize ) ;
av_log ( ctx , AV_LOG_INFO , " mincontrast = %f \n " , sd - > conf . contrastThreshold ) ;
av_log ( ctx , AV_LOG_INFO , " show = %d \n " , sd - > conf . show ) ;
av_log ( ctx , AV_LOG_INFO , " result = %s \n " , sd - > result ) ;
sd - > f = fopen ( sd - > result , " w " ) ;
if ( sd - > f = = NULL ) {
av_log ( ctx , AV_LOG_ERROR , " cannot open transform file %s \n " , sd - > result ) ;
return AVERROR ( EINVAL ) ;
2013-04-24 17:01:26 +02:00
} else {
if ( vsPrepareFile ( md , sd - > f ) ! = VS_OK ) {
2013-04-19 01:49:27 +02:00
av_log ( ctx , AV_LOG_ERROR , " cannot write to transform file %s \n " , sd - > result ) ;
return AVERROR ( EINVAL ) ;
}
}
return 0 ;
}
static int filter_frame ( AVFilterLink * inlink , AVFrame * in )
{
AVFilterContext * ctx = inlink - > dst ;
StabData * sd = ctx - > priv ;
2013-04-24 17:01:26 +02:00
VSMotionDetect * md = & ( sd - > md ) ;
2013-04-19 01:49:27 +02:00
LocalMotions localmotions ;
AVFilterLink * outlink = inlink - > dst - > outputs [ 0 ] ;
int direct = 0 ;
AVFrame * out ;
VSFrame frame ;
int plane ;
if ( av_frame_is_writable ( in ) ) {
direct = 1 ;
out = in ;
} else {
out = ff_get_video_buffer ( outlink , outlink - > w , outlink - > h ) ;
if ( ! out ) {
av_frame_free ( & in ) ;
return AVERROR ( ENOMEM ) ;
}
av_frame_copy_props ( out , in ) ;
}
2013-04-24 17:01:26 +02:00
for ( plane = 0 ; plane < md - > fi . planes ; plane + + ) {
2013-04-19 01:49:27 +02:00
frame . data [ plane ] = in - > data [ plane ] ;
frame . linesize [ plane ] = in - > linesize [ plane ] ;
}
2013-04-24 17:01:26 +02:00
if ( vsMotionDetection ( md , & localmotions , & frame ) ! = VS_OK ) {
2013-04-19 01:49:27 +02:00
av_log ( ctx , AV_LOG_ERROR , " motion detection failed " ) ;
return AVERROR ( AVERROR_EXTERNAL ) ;
} else {
2013-04-24 17:01:26 +02:00
if ( vsWriteToFile ( md , sd - > f , & localmotions ) ! = VS_OK ) {
2013-04-19 01:49:27 +02:00
av_log ( ctx , AV_LOG_ERROR , " cannot write to transform file " ) ;
return AVERROR ( errno ) ;
}
vs_vector_del ( & localmotions ) ;
}
2013-04-24 17:01:26 +02:00
if ( sd - > conf . show > 0 & & ! direct ) {
2013-04-19 01:49:27 +02:00
av_image_copy ( out - > data , out - > linesize ,
( void * ) in - > data , in - > linesize ,
in - > format , in - > width , in - > height ) ;
}
if ( ! direct )
av_frame_free ( & in ) ;
return ff_filter_frame ( outlink , out ) ;
}
static const AVFilterPad avfilter_vf_vidstabdetect_inputs [ ] = {
{
. name = " default " ,
. type = AVMEDIA_TYPE_VIDEO ,
. filter_frame = filter_frame ,
. config_props = config_input ,
} ,
{ NULL }
} ;
static const AVFilterPad avfilter_vf_vidstabdetect_outputs [ ] = {
{
. name = " default " ,
. type = AVMEDIA_TYPE_VIDEO ,
} ,
{ NULL }
} ;
AVFilter avfilter_vf_vidstabdetect = {
. name = " vidstabdetect " ,
2013-04-24 17:01:50 +02:00
. description = NULL_IF_CONFIG_SMALL ( " Extract relative transformations, "
" pass 1 of 2 for stabilization "
" (see vidstabtransform for pass 2). " ) ,
2013-04-19 01:49:27 +02:00
. priv_size = sizeof ( StabData ) ,
. init = init ,
. uninit = uninit ,
. query_formats = query_formats ,
. inputs = avfilter_vf_vidstabdetect_inputs ,
. outputs = avfilter_vf_vidstabdetect_outputs ,
. priv_class = & vidstabdetect_class ,
} ;