Line | Branch | Exec | Source |
---|---|---|---|
1 | /** | ||
2 | * Copyright (c) 2016 Davinder Singh (DSM_) <ds.mudhar<@gmail.com> | ||
3 | * | ||
4 | * This file is part of FFmpeg. | ||
5 | * | ||
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. | ||
10 | * | ||
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. | ||
15 | * | ||
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 | ||
19 | */ | ||
20 | |||
21 | #include "motion_estimation.h" | ||
22 | #include "libavcodec/mathops.h" | ||
23 | #include "libavutil/common.h" | ||
24 | #include "libavutil/mem.h" | ||
25 | #include "libavutil/opt.h" | ||
26 | #include "libavutil/motion_vector.h" | ||
27 | #include "avfilter.h" | ||
28 | #include "filters.h" | ||
29 | #include "video.h" | ||
30 | |||
31 | typedef struct MEContext { | ||
32 | const AVClass *class; | ||
33 | AVMotionEstContext me_ctx; | ||
34 | int method; ///< motion estimation method | ||
35 | |||
36 | int mb_size; ///< macroblock size | ||
37 | int search_param; ///< search parameter | ||
38 | int b_width, b_height, b_count; | ||
39 | int log2_mb_size; | ||
40 | |||
41 | AVFrame *prev, *cur, *next; | ||
42 | |||
43 | int (*mv_table[3])[2][2]; ///< motion vectors of current & prev 2 frames | ||
44 | } MEContext; | ||
45 | |||
46 | #define OFFSET(x) offsetof(MEContext, x) | ||
47 | #define FLAGS AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_FILTERING_PARAM | ||
48 | #define CONST(name, help, val, u) { name, help, 0, AV_OPT_TYPE_CONST, {.i64=val}, 0, 0, FLAGS, .unit = u } | ||
49 | |||
50 | static const AVOption mestimate_options[] = { | ||
51 | { "method", "motion estimation method", OFFSET(method), AV_OPT_TYPE_INT, {.i64 = AV_ME_METHOD_ESA}, AV_ME_METHOD_ESA, AV_ME_METHOD_UMH, FLAGS, .unit = "method" }, | ||
52 | CONST("esa", "exhaustive search", AV_ME_METHOD_ESA, "method"), | ||
53 | CONST("tss", "three step search", AV_ME_METHOD_TSS, "method"), | ||
54 | CONST("tdls", "two dimensional logarithmic search", AV_ME_METHOD_TDLS, "method"), | ||
55 | CONST("ntss", "new three step search", AV_ME_METHOD_NTSS, "method"), | ||
56 | CONST("fss", "four step search", AV_ME_METHOD_FSS, "method"), | ||
57 | CONST("ds", "diamond search", AV_ME_METHOD_DS, "method"), | ||
58 | CONST("hexbs", "hexagon-based search", AV_ME_METHOD_HEXBS, "method"), | ||
59 | CONST("epzs", "enhanced predictive zonal search", AV_ME_METHOD_EPZS, "method"), | ||
60 | CONST("umh", "uneven multi-hexagon search", AV_ME_METHOD_UMH, "method"), | ||
61 | { "mb_size", "macroblock size", OFFSET(mb_size), AV_OPT_TYPE_INT, {.i64 = 16}, 8, INT_MAX, FLAGS }, | ||
62 | { "search_param", "search parameter", OFFSET(search_param), AV_OPT_TYPE_INT, {.i64 = 7}, 4, INT_MAX, FLAGS }, | ||
63 | { NULL } | ||
64 | }; | ||
65 | |||
66 | AVFILTER_DEFINE_CLASS(mestimate); | ||
67 | |||
68 | static const enum AVPixelFormat pix_fmts[] = { | ||
69 | AV_PIX_FMT_YUV410P, AV_PIX_FMT_YUV411P, | ||
70 | AV_PIX_FMT_YUV420P, AV_PIX_FMT_YUV422P, | ||
71 | AV_PIX_FMT_YUV440P, AV_PIX_FMT_YUV444P, | ||
72 | AV_PIX_FMT_YUVJ444P, AV_PIX_FMT_YUVJ440P, | ||
73 | AV_PIX_FMT_YUVJ422P, AV_PIX_FMT_YUVJ420P, | ||
74 | AV_PIX_FMT_YUVJ411P, | ||
75 | AV_PIX_FMT_YUVA420P, AV_PIX_FMT_YUVA422P, AV_PIX_FMT_YUVA444P, | ||
76 | AV_PIX_FMT_GRAY8, | ||
77 | AV_PIX_FMT_NONE | ||
78 | }; | ||
79 | |||
80 | 2 | static int config_input(AVFilterLink *inlink) | |
81 | { | ||
82 | 2 | MEContext *s = inlink->dst->priv; | |
83 | int i; | ||
84 | |||
85 | 2 | s->log2_mb_size = av_ceil_log2_c(s->mb_size); | |
86 | 2 | s->mb_size = 1 << s->log2_mb_size; | |
87 | |||
88 | 2 | s->b_width = inlink->w >> s->log2_mb_size; | |
89 | 2 | s->b_height = inlink->h >> s->log2_mb_size; | |
90 | 2 | s->b_count = s->b_width * s->b_height; | |
91 | |||
92 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
|
2 | if (s->b_count == 0) |
93 | ✗ | return AVERROR(EINVAL); | |
94 | |||
95 |
2/2✓ Branch 0 taken 6 times.
✓ Branch 1 taken 2 times.
|
8 | for (i = 0; i < 3; i++) { |
96 | 6 | s->mv_table[i] = av_calloc(s->b_count, sizeof(*s->mv_table[0])); | |
97 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
|
6 | if (!s->mv_table[i]) |
98 | ✗ | return AVERROR(ENOMEM); | |
99 | } | ||
100 | |||
101 | 2 | ff_me_init_context(&s->me_ctx, s->mb_size, s->search_param, inlink->w, inlink->h, 0, (s->b_width - 1) << s->log2_mb_size, 0, (s->b_height - 1) << s->log2_mb_size); | |
102 | |||
103 | 2 | return 0; | |
104 | } | ||
105 | |||
106 | 229680 | static void add_mv_data(AVMotionVector *mv, int mb_size, | |
107 | int x, int y, int x_mv, int y_mv, int dir) | ||
108 | { | ||
109 | 229680 | mv->w = mb_size; | |
110 | 229680 | mv->h = mb_size; | |
111 | 229680 | mv->dst_x = x + (mb_size >> 1); | |
112 | 229680 | mv->dst_y = y + (mb_size >> 1); | |
113 | 229680 | mv->src_x = x_mv + (mb_size >> 1); | |
114 | 229680 | mv->src_y = y_mv + (mb_size >> 1); | |
115 |
2/2✓ Branch 0 taken 114840 times.
✓ Branch 1 taken 114840 times.
|
229680 | mv->source = dir ? 1 : -1; |
116 | 229680 | mv->flags = 0; | |
117 | 229680 | } | |
118 | |||
119 | #define SEARCH_MV(method)\ | ||
120 | do {\ | ||
121 | for (mb_y = 0; mb_y < s->b_height; mb_y++)\ | ||
122 | for (mb_x = 0; mb_x < s->b_width; mb_x++) {\ | ||
123 | const int x_mb = mb_x << s->log2_mb_size;\ | ||
124 | const int y_mb = mb_y << s->log2_mb_size;\ | ||
125 | int mv[2] = {x_mb, y_mb};\ | ||
126 | ff_me_search_##method(me_ctx, x_mb, y_mb, mv);\ | ||
127 | add_mv_data(((AVMotionVector *) sd->data) + mv_count++, me_ctx->mb_size, x_mb, y_mb, mv[0], mv[1], dir);\ | ||
128 | }\ | ||
129 | } while (0) | ||
130 | |||
131 | #define ADD_PRED(preds, px, py)\ | ||
132 | do {\ | ||
133 | preds.mvs[preds.nb][0] = px;\ | ||
134 | preds.mvs[preds.nb][1] = py;\ | ||
135 | preds.nb++;\ | ||
136 | } while(0) | ||
137 | |||
138 | 20 | static int filter_frame(AVFilterLink *inlink, AVFrame *frame) | |
139 | { | ||
140 | 20 | AVFilterContext *ctx = inlink->dst; | |
141 | 20 | MEContext *s = ctx->priv; | |
142 | 20 | AVMotionEstContext *me_ctx = &s->me_ctx; | |
143 | AVFrameSideData *sd; | ||
144 | AVFrame *out; | ||
145 | int mb_x, mb_y, dir; | ||
146 | 20 | int32_t mv_count = 0; | |
147 | int ret; | ||
148 | |||
149 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 20 times.
|
20 | if (frame->pts == AV_NOPTS_VALUE) { |
150 | ✗ | ret = ff_filter_frame(ctx->outputs[0], frame); | |
151 | ✗ | return ret; | |
152 | } | ||
153 | |||
154 | 20 | av_frame_free(&s->prev); | |
155 | 20 | s->prev = s->cur; | |
156 | 20 | s->cur = s->next; | |
157 | 20 | s->next = frame; | |
158 | |||
159 | 20 | s->mv_table[2] = memcpy(s->mv_table[2], s->mv_table[1], sizeof(*s->mv_table[1]) * s->b_count); | |
160 | 20 | s->mv_table[1] = memcpy(s->mv_table[1], s->mv_table[0], sizeof(*s->mv_table[0]) * s->b_count); | |
161 | |||
162 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 18 times.
|
20 | if (!s->cur) { |
163 | 2 | s->cur = av_frame_clone(frame); | |
164 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
|
2 | if (!s->cur) |
165 | ✗ | return AVERROR(ENOMEM); | |
166 | } | ||
167 | |||
168 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 18 times.
|
20 | if (!s->prev) |
169 | 2 | return 0; | |
170 | |||
171 | 18 | out = av_frame_clone(s->cur); | |
172 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 18 times.
|
18 | if (!out) |
173 | ✗ | return AVERROR(ENOMEM); | |
174 | |||
175 | 18 | sd = av_frame_new_side_data(out, AV_FRAME_DATA_MOTION_VECTORS, 2 * s->b_count * sizeof(AVMotionVector)); | |
176 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 18 times.
|
18 | if (!sd) { |
177 | ✗ | av_frame_free(&out); | |
178 | ✗ | return AVERROR(ENOMEM); | |
179 | } | ||
180 | |||
181 | 18 | me_ctx->data_cur = s->cur->data[0]; | |
182 | 18 | me_ctx->linesize = s->cur->linesize[0]; | |
183 | |||
184 |
2/2✓ Branch 0 taken 36 times.
✓ Branch 1 taken 18 times.
|
54 | for (dir = 0; dir < 2; dir++) { |
185 |
2/2✓ Branch 0 taken 18 times.
✓ Branch 1 taken 18 times.
|
36 | me_ctx->data_ref = (dir ? s->next : s->prev)->data[0]; |
186 | |||
187 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 36 times.
|
36 | if (s->method == AV_ME_METHOD_DS) |
188 | ✗ | SEARCH_MV(ds); | |
189 |
1/2✓ Branch 0 taken 36 times.
✗ Branch 1 not taken.
|
36 | else if (s->method == AV_ME_METHOD_ESA) |
190 |
4/4✓ Branch 2 taken 229680 times.
✓ Branch 3 taken 3546 times.
✓ Branch 4 taken 3546 times.
✓ Branch 5 taken 36 times.
|
233262 | SEARCH_MV(esa); |
191 | ✗ | else if (s->method == AV_ME_METHOD_FSS) | |
192 | ✗ | SEARCH_MV(fss); | |
193 | ✗ | else if (s->method == AV_ME_METHOD_NTSS) | |
194 | ✗ | SEARCH_MV(ntss); | |
195 | ✗ | else if (s->method == AV_ME_METHOD_TDLS) | |
196 | ✗ | SEARCH_MV(tdls); | |
197 | ✗ | else if (s->method == AV_ME_METHOD_TSS) | |
198 | ✗ | SEARCH_MV(tss); | |
199 | ✗ | else if (s->method == AV_ME_METHOD_HEXBS) | |
200 | ✗ | SEARCH_MV(hexbs); | |
201 | ✗ | else if (s->method == AV_ME_METHOD_UMH) { | |
202 | ✗ | for (mb_y = 0; mb_y < s->b_height; mb_y++) | |
203 | ✗ | for (mb_x = 0; mb_x < s->b_width; mb_x++) { | |
204 | ✗ | const int mb_i = mb_x + mb_y * s->b_width; | |
205 | ✗ | const int x_mb = mb_x << s->log2_mb_size; | |
206 | ✗ | const int y_mb = mb_y << s->log2_mb_size; | |
207 | ✗ | int mv[2] = {x_mb, y_mb}; | |
208 | |||
209 | ✗ | AVMotionEstPredictor *preds = me_ctx->preds; | |
210 | ✗ | preds[0].nb = 0; | |
211 | |||
212 | ✗ | ADD_PRED(preds[0], 0, 0); | |
213 | |||
214 | //left mb in current frame | ||
215 | ✗ | if (mb_x > 0) | |
216 | ✗ | ADD_PRED(preds[0], s->mv_table[0][mb_i - 1][dir][0], s->mv_table[0][mb_i - 1][dir][1]); | |
217 | |||
218 | ✗ | if (mb_y > 0) { | |
219 | //top mb in current frame | ||
220 | ✗ | ADD_PRED(preds[0], s->mv_table[0][mb_i - s->b_width][dir][0], s->mv_table[0][mb_i - s->b_width][dir][1]); | |
221 | |||
222 | //top-right mb in current frame | ||
223 | ✗ | if (mb_x + 1 < s->b_width) | |
224 | ✗ | ADD_PRED(preds[0], s->mv_table[0][mb_i - s->b_width + 1][dir][0], s->mv_table[0][mb_i - s->b_width + 1][dir][1]); | |
225 | //top-left mb in current frame | ||
226 | ✗ | else if (mb_x > 0) | |
227 | ✗ | ADD_PRED(preds[0], s->mv_table[0][mb_i - s->b_width - 1][dir][0], s->mv_table[0][mb_i - s->b_width - 1][dir][1]); | |
228 | } | ||
229 | |||
230 | //median predictor | ||
231 | ✗ | if (preds[0].nb == 4) { | |
232 | ✗ | me_ctx->pred_x = mid_pred(preds[0].mvs[1][0], preds[0].mvs[2][0], preds[0].mvs[3][0]); | |
233 | ✗ | me_ctx->pred_y = mid_pred(preds[0].mvs[1][1], preds[0].mvs[2][1], preds[0].mvs[3][1]); | |
234 | ✗ | } else if (preds[0].nb == 3) { | |
235 | ✗ | me_ctx->pred_x = mid_pred(0, preds[0].mvs[1][0], preds[0].mvs[2][0]); | |
236 | ✗ | me_ctx->pred_y = mid_pred(0, preds[0].mvs[1][1], preds[0].mvs[2][1]); | |
237 | ✗ | } else if (preds[0].nb == 2) { | |
238 | ✗ | me_ctx->pred_x = preds[0].mvs[1][0]; | |
239 | ✗ | me_ctx->pred_y = preds[0].mvs[1][1]; | |
240 | } else { | ||
241 | ✗ | me_ctx->pred_x = 0; | |
242 | ✗ | me_ctx->pred_y = 0; | |
243 | } | ||
244 | |||
245 | ✗ | ff_me_search_umh(me_ctx, x_mb, y_mb, mv); | |
246 | |||
247 | ✗ | s->mv_table[0][mb_i][dir][0] = mv[0] - x_mb; | |
248 | ✗ | s->mv_table[0][mb_i][dir][1] = mv[1] - y_mb; | |
249 | ✗ | add_mv_data(((AVMotionVector *) sd->data) + mv_count++, me_ctx->mb_size, x_mb, y_mb, mv[0], mv[1], dir); | |
250 | } | ||
251 | |||
252 | ✗ | } else if (s->method == AV_ME_METHOD_EPZS) { | |
253 | |||
254 | ✗ | for (mb_y = 0; mb_y < s->b_height; mb_y++) | |
255 | ✗ | for (mb_x = 0; mb_x < s->b_width; mb_x++) { | |
256 | ✗ | const int mb_i = mb_x + mb_y * s->b_width; | |
257 | ✗ | const int x_mb = mb_x << s->log2_mb_size; | |
258 | ✗ | const int y_mb = mb_y << s->log2_mb_size; | |
259 | ✗ | int mv[2] = {x_mb, y_mb}; | |
260 | |||
261 | ✗ | AVMotionEstPredictor *preds = me_ctx->preds; | |
262 | ✗ | preds[0].nb = 0; | |
263 | ✗ | preds[1].nb = 0; | |
264 | |||
265 | ✗ | ADD_PRED(preds[0], 0, 0); | |
266 | |||
267 | //left mb in current frame | ||
268 | ✗ | if (mb_x > 0) | |
269 | ✗ | ADD_PRED(preds[0], s->mv_table[0][mb_i - 1][dir][0], s->mv_table[0][mb_i - 1][dir][1]); | |
270 | |||
271 | //top mb in current frame | ||
272 | ✗ | if (mb_y > 0) | |
273 | ✗ | ADD_PRED(preds[0], s->mv_table[0][mb_i - s->b_width][dir][0], s->mv_table[0][mb_i - s->b_width][dir][1]); | |
274 | |||
275 | //top-right mb in current frame | ||
276 | ✗ | if (mb_y > 0 && mb_x + 1 < s->b_width) | |
277 | ✗ | ADD_PRED(preds[0], s->mv_table[0][mb_i - s->b_width + 1][dir][0], s->mv_table[0][mb_i - s->b_width + 1][dir][1]); | |
278 | |||
279 | //median predictor | ||
280 | ✗ | if (preds[0].nb == 4) { | |
281 | ✗ | me_ctx->pred_x = mid_pred(preds[0].mvs[1][0], preds[0].mvs[2][0], preds[0].mvs[3][0]); | |
282 | ✗ | me_ctx->pred_y = mid_pred(preds[0].mvs[1][1], preds[0].mvs[2][1], preds[0].mvs[3][1]); | |
283 | ✗ | } else if (preds[0].nb == 3) { | |
284 | ✗ | me_ctx->pred_x = mid_pred(0, preds[0].mvs[1][0], preds[0].mvs[2][0]); | |
285 | ✗ | me_ctx->pred_y = mid_pred(0, preds[0].mvs[1][1], preds[0].mvs[2][1]); | |
286 | ✗ | } else if (preds[0].nb == 2) { | |
287 | ✗ | me_ctx->pred_x = preds[0].mvs[1][0]; | |
288 | ✗ | me_ctx->pred_y = preds[0].mvs[1][1]; | |
289 | } else { | ||
290 | ✗ | me_ctx->pred_x = 0; | |
291 | ✗ | me_ctx->pred_y = 0; | |
292 | } | ||
293 | |||
294 | //collocated mb in prev frame | ||
295 | ✗ | ADD_PRED(preds[0], s->mv_table[1][mb_i][dir][0], s->mv_table[1][mb_i][dir][1]); | |
296 | |||
297 | //accelerator motion vector of collocated block in prev frame | ||
298 | ✗ | ADD_PRED(preds[1], s->mv_table[1][mb_i][dir][0] + (s->mv_table[1][mb_i][dir][0] - s->mv_table[2][mb_i][dir][0]), | |
299 | s->mv_table[1][mb_i][dir][1] + (s->mv_table[1][mb_i][dir][1] - s->mv_table[2][mb_i][dir][1])); | ||
300 | |||
301 | //left mb in prev frame | ||
302 | ✗ | if (mb_x > 0) | |
303 | ✗ | ADD_PRED(preds[1], s->mv_table[1][mb_i - 1][dir][0], s->mv_table[1][mb_i - 1][dir][1]); | |
304 | |||
305 | //top mb in prev frame | ||
306 | ✗ | if (mb_y > 0) | |
307 | ✗ | ADD_PRED(preds[1], s->mv_table[1][mb_i - s->b_width][dir][0], s->mv_table[1][mb_i - s->b_width][dir][1]); | |
308 | |||
309 | //right mb in prev frame | ||
310 | ✗ | if (mb_x + 1 < s->b_width) | |
311 | ✗ | ADD_PRED(preds[1], s->mv_table[1][mb_i + 1][dir][0], s->mv_table[1][mb_i + 1][dir][1]); | |
312 | |||
313 | //bottom mb in prev frame | ||
314 | ✗ | if (mb_y + 1 < s->b_height) | |
315 | ✗ | ADD_PRED(preds[1], s->mv_table[1][mb_i + s->b_width][dir][0], s->mv_table[1][mb_i + s->b_width][dir][1]); | |
316 | |||
317 | ✗ | ff_me_search_epzs(me_ctx, x_mb, y_mb, mv); | |
318 | |||
319 | ✗ | s->mv_table[0][mb_i][dir][0] = mv[0] - x_mb; | |
320 | ✗ | s->mv_table[0][mb_i][dir][1] = mv[1] - y_mb; | |
321 | ✗ | add_mv_data(((AVMotionVector *) sd->data) + mv_count++, s->mb_size, x_mb, y_mb, mv[0], mv[1], dir); | |
322 | } | ||
323 | } | ||
324 | } | ||
325 | |||
326 | 18 | return ff_filter_frame(ctx->outputs[0], out); | |
327 | } | ||
328 | |||
329 | 2 | static av_cold void uninit(AVFilterContext *ctx) | |
330 | { | ||
331 | 2 | MEContext *s = ctx->priv; | |
332 | int i; | ||
333 | |||
334 | 2 | av_frame_free(&s->prev); | |
335 | 2 | av_frame_free(&s->cur); | |
336 | 2 | av_frame_free(&s->next); | |
337 | |||
338 |
2/2✓ Branch 0 taken 6 times.
✓ Branch 1 taken 2 times.
|
8 | for (i = 0; i < 3; i++) |
339 | 6 | av_freep(&s->mv_table[i]); | |
340 | 2 | } | |
341 | |||
342 | static const AVFilterPad mestimate_inputs[] = { | ||
343 | { | ||
344 | .name = "default", | ||
345 | .type = AVMEDIA_TYPE_VIDEO, | ||
346 | .filter_frame = filter_frame, | ||
347 | .config_props = config_input, | ||
348 | }, | ||
349 | }; | ||
350 | |||
351 | const AVFilter ff_vf_mestimate = { | ||
352 | .name = "mestimate", | ||
353 | .description = NULL_IF_CONFIG_SMALL("Generate motion vectors."), | ||
354 | .priv_size = sizeof(MEContext), | ||
355 | .priv_class = &mestimate_class, | ||
356 | .uninit = uninit, | ||
357 | .flags = AVFILTER_FLAG_METADATA_ONLY, | ||
358 | FILTER_INPUTS(mestimate_inputs), | ||
359 | FILTER_OUTPUTS(ff_video_default_filterpad), | ||
360 | FILTER_PIXFMTS_ARRAY(pix_fmts), | ||
361 | }; | ||
362 |