2 * Copyright (c) 2013 Georg Martius <georg dot martius at web dot de>
4 * This file is part of FFmpeg.
6 * FFmpeg is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * FFmpeg is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with FFmpeg; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21 #define DEFAULT_RESULT_NAME "transforms.trf"
23 #include <vid.stab/libvidstab.h>
25 #include "libavutil/common.h"
26 #include "libavutil/opt.h"
27 #include "libavutil/imgutils.h"
31 #include "vidstabutils.h"
37 VSMotionDetectConfig conf
;
44 #define OFFSET(x) offsetof(StabData, x)
45 #define OFFSETC(x) (offsetof(StabData, conf)+offsetof(VSMotionDetectConfig, x))
46 #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
48 static const AVOption vidstabdetect_options
[] = {
49 {"result", "path to the file used to write the transforms", OFFSET(result
), AV_OPT_TYPE_STRING
, {.str
= DEFAULT_RESULT_NAME
}, .flags
= FLAGS
},
50 {"shakiness", "how shaky is the video and how quick is the camera?"
51 " 1: little (fast) 10: very strong/quick (slow)", OFFSETC(shakiness
), AV_OPT_TYPE_INT
, {.i64
= 5}, 1, 10, FLAGS
},
52 {"accuracy", "(>=shakiness) 1: low 15: high (slow)", OFFSETC(accuracy
), AV_OPT_TYPE_INT
, {.i64
= 15}, 1, 15, FLAGS
},
53 {"stepsize", "region around minimum is scanned with 1 pixel resolution", OFFSETC(stepSize
), AV_OPT_TYPE_INT
, {.i64
= 6}, 1, 32, FLAGS
},
54 {"mincontrast", "below this contrast a field is discarded (0-1)", OFFSETC(contrastThreshold
), AV_OPT_TYPE_DOUBLE
, {.dbl
= 0.25}, 0.0, 1.0, FLAGS
},
55 {"show", "0: draw nothing; 1,2: show fields and transforms", OFFSETC(show
), AV_OPT_TYPE_INT
, {.i64
= 0}, 0, 2, FLAGS
},
56 {"tripod", "virtual tripod mode (if >0): motion is compared to a reference"
57 " reference frame (frame # is the value)", OFFSETC(virtualTripod
), AV_OPT_TYPE_INT
, {.i64
= 0}, 0, INT_MAX
, FLAGS
},
61 AVFILTER_DEFINE_CLASS(vidstabdetect
);
63 static av_cold
int init(AVFilterContext
*ctx
)
65 StabData
*sd
= ctx
->priv
;
67 sd
->class = &vidstabdetect_class
;
68 av_log(ctx
, AV_LOG_VERBOSE
, "vidstabdetect filter: init %s\n", LIBVIDSTAB_VERSION
);
72 static av_cold
void uninit(AVFilterContext
*ctx
)
74 StabData
*sd
= ctx
->priv
;
75 VSMotionDetect
*md
= &(sd
->md
);
82 vsMotionDetectionCleanup(md
);
85 static int query_formats(AVFilterContext
*ctx
)
87 // If you add something here also add it in vidstabutils.c
88 static const enum AVPixelFormat pix_fmts
[] = {
89 AV_PIX_FMT_YUV444P
, AV_PIX_FMT_YUV422P
, AV_PIX_FMT_YUV420P
,
90 AV_PIX_FMT_YUV411P
, AV_PIX_FMT_YUV410P
, AV_PIX_FMT_YUVA420P
,
91 AV_PIX_FMT_YUV440P
, AV_PIX_FMT_GRAY8
,
92 AV_PIX_FMT_RGB24
, AV_PIX_FMT_BGR24
, AV_PIX_FMT_RGBA
,
96 ff_set_common_formats(ctx
, ff_make_format_list(pix_fmts
));
100 static int config_input(AVFilterLink
*inlink
)
102 AVFilterContext
*ctx
= inlink
->dst
;
103 StabData
*sd
= ctx
->priv
;
105 VSMotionDetect
* md
= &(sd
->md
);
107 const AVPixFmtDescriptor
*desc
= av_pix_fmt_desc_get(inlink
->format
);
109 vsFrameInfoInit(&fi
, inlink
->w
, inlink
->h
,
110 ff_av2vs_pixfmt(ctx
, inlink
->format
));
111 if (fi
.bytesPerPixel
!= av_get_bits_per_pixel(desc
)/8) {
112 av_log(ctx
, AV_LOG_ERROR
, "pixel-format error: wrong bits/per/pixel, please report a BUG");
113 return AVERROR(EINVAL
);
115 if (fi
.log2ChromaW
!= desc
->log2_chroma_w
) {
116 av_log(ctx
, AV_LOG_ERROR
, "pixel-format error: log2_chroma_w, please report a BUG");
117 return AVERROR(EINVAL
);
120 if (fi
.log2ChromaH
!= desc
->log2_chroma_h
) {
121 av_log(ctx
, AV_LOG_ERROR
, "pixel-format error: log2_chroma_h, please report a BUG");
122 return AVERROR(EINVAL
);
125 // set values that are not initialized by the options
127 sd
->conf
.modName
= "vidstabdetect";
128 if (vsMotionDetectInit(md
, &sd
->conf
, &fi
) != VS_OK
) {
129 av_log(ctx
, AV_LOG_ERROR
, "initialization of Motion Detection failed, please report a BUG");
130 return AVERROR(EINVAL
);
133 vsMotionDetectGetConfig(&sd
->conf
, md
);
134 av_log(ctx
, AV_LOG_INFO
, "Video stabilization settings (pass 1/2):\n");
135 av_log(ctx
, AV_LOG_INFO
, " shakiness = %d\n", sd
->conf
.shakiness
);
136 av_log(ctx
, AV_LOG_INFO
, " accuracy = %d\n", sd
->conf
.accuracy
);
137 av_log(ctx
, AV_LOG_INFO
, " stepsize = %d\n", sd
->conf
.stepSize
);
138 av_log(ctx
, AV_LOG_INFO
, " mincontrast = %f\n", sd
->conf
.contrastThreshold
);
139 av_log(ctx
, AV_LOG_INFO
, " tripod = %d\n", sd
->conf
.virtualTripod
);
140 av_log(ctx
, AV_LOG_INFO
, " show = %d\n", sd
->conf
.show
);
141 av_log(ctx
, AV_LOG_INFO
, " result = %s\n", sd
->result
);
143 sd
->f
= fopen(sd
->result
, "w");
145 av_log(ctx
, AV_LOG_ERROR
, "cannot open transform file %s\n", sd
->result
);
146 return AVERROR(EINVAL
);
148 if (vsPrepareFile(md
, sd
->f
) != VS_OK
) {
149 av_log(ctx
, AV_LOG_ERROR
, "cannot write to transform file %s\n", sd
->result
);
150 return AVERROR(EINVAL
);
156 static int filter_frame(AVFilterLink
*inlink
, AVFrame
*in
)
158 AVFilterContext
*ctx
= inlink
->dst
;
159 StabData
*sd
= ctx
->priv
;
160 VSMotionDetect
*md
= &(sd
->md
);
161 LocalMotions localmotions
;
163 AVFilterLink
*outlink
= inlink
->dst
->outputs
[0];
167 if (sd
->conf
.show
> 0 && !av_frame_is_writable(in
))
168 av_frame_make_writable(in
);
170 for (plane
= 0; plane
< md
->fi
.planes
; plane
++) {
171 frame
.data
[plane
] = in
->data
[plane
];
172 frame
.linesize
[plane
] = in
->linesize
[plane
];
174 if (vsMotionDetection(md
, &localmotions
, &frame
) != VS_OK
) {
175 av_log(ctx
, AV_LOG_ERROR
, "motion detection failed");
176 return AVERROR(AVERROR_EXTERNAL
);
178 if (vsWriteToFile(md
, sd
->f
, &localmotions
) != VS_OK
) {
179 av_log(ctx
, AV_LOG_ERROR
, "cannot write to transform file");
180 return AVERROR(errno
);
182 vs_vector_del(&localmotions
);
185 return ff_filter_frame(outlink
, in
);
188 static const AVFilterPad avfilter_vf_vidstabdetect_inputs
[] = {
191 .type
= AVMEDIA_TYPE_VIDEO
,
192 .filter_frame
= filter_frame
,
193 .config_props
= config_input
,
198 static const AVFilterPad avfilter_vf_vidstabdetect_outputs
[] = {
201 .type
= AVMEDIA_TYPE_VIDEO
,
206 AVFilter ff_vf_vidstabdetect
= {
207 .name
= "vidstabdetect",
208 .description
= NULL_IF_CONFIG_SMALL("Extract relative transformations, "
209 "pass 1 of 2 for stabilization "
210 "(see vidstabtransform for pass 2)."),
211 .priv_size
= sizeof(StabData
),
214 .query_formats
= query_formats
,
215 .inputs
= avfilter_vf_vidstabdetect_inputs
,
216 .outputs
= avfilter_vf_vidstabdetect_outputs
,
217 .priv_class
= &vidstabdetect_class
,