| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | /* | ||
| 2 | * Copyright (c) 2006 Michael Niedermayer <michaelni@gmx.at> | ||
| 3 | * | ||
| 4 | * This file is part of FFmpeg. | ||
| 5 | * | ||
| 6 | * FFmpeg is free software; you can redistribute it and/or modify | ||
| 7 | * it under the terms of the GNU General Public License as published by | ||
| 8 | * the Free Software Foundation; either version 2 of the License, or | ||
| 9 | * (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 | ||
| 14 | * GNU General Public License for more details. | ||
| 15 | * | ||
| 16 | * You should have received a copy of the GNU General Public License along | ||
| 17 | * with FFmpeg; if not, write to the Free Software Foundation, Inc., | ||
| 18 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||
| 19 | */ | ||
| 20 | |||
| 21 | /** | ||
| 22 | * @file | ||
| 23 | * Motion Compensation Deinterlacer | ||
| 24 | * Ported from MPlayer libmpcodecs/vf_mcdeint.c. | ||
| 25 | * | ||
| 26 | * Known Issues: | ||
| 27 | * | ||
| 28 | * The motion estimation is somewhat at the mercy of the input, if the | ||
| 29 | * input frames are created purely based on spatial interpolation then | ||
| 30 | * for example a thin black line or another random and not | ||
| 31 | * interpolateable pattern will cause problems. | ||
| 32 | * Note: completely ignoring the "unavailable" lines during motion | ||
| 33 | * estimation did not look any better, so the most obvious solution | ||
| 34 | * would be to improve tfields or penalize problematic motion vectors. | ||
| 35 | * | ||
| 36 | * If non iterative ME is used then snow currently ignores the OBMC | ||
| 37 | * window and as a result sometimes creates artifacts. | ||
| 38 | * | ||
| 39 | * Only past frames are used, we should ideally use future frames too, | ||
| 40 | * something like filtering the whole movie in forward and then | ||
| 41 | * backward direction seems like an interesting idea but the current | ||
| 42 | * filter framework is FAR from supporting such things. | ||
| 43 | * | ||
| 44 | * Combining the motion compensated image with the input image also is | ||
| 45 | * not as trivial as it seems, simple blindly taking even lines from | ||
| 46 | * one and odd ones from the other does not work at all as ME/MC | ||
| 47 | * sometimes has nothing in the previous frames which matches the | ||
| 48 | * current. The current algorithm has been found by trial and error | ||
| 49 | * and almost certainly can be improved... | ||
| 50 | */ | ||
| 51 | |||
| 52 | #include "libavutil/opt.h" | ||
| 53 | #include "libavcodec/avcodec.h" | ||
| 54 | #include "libavutil/pixdesc.h" | ||
| 55 | #include "avfilter.h" | ||
| 56 | #include "filters.h" | ||
| 57 | #include "video.h" | ||
| 58 | |||
| 59 | enum MCDeintMode { | ||
| 60 | MODE_FAST = 0, | ||
| 61 | MODE_MEDIUM, | ||
| 62 | MODE_SLOW, | ||
| 63 | MODE_EXTRA_SLOW, | ||
| 64 | MODE_NB, | ||
| 65 | }; | ||
| 66 | |||
| 67 | enum MCDeintParity { | ||
| 68 | PARITY_TFF = 0, ///< top field first | ||
| 69 | PARITY_BFF = 1, ///< bottom field first | ||
| 70 | }; | ||
| 71 | |||
| 72 | typedef struct MCDeintContext { | ||
| 73 | const AVClass *class; | ||
| 74 | int mode; ///< MCDeintMode | ||
| 75 | int parity; ///< MCDeintParity | ||
| 76 | int qp; | ||
| 77 | AVPacket *pkt; | ||
| 78 | AVFrame *frame_dec; | ||
| 79 | AVCodecContext *enc_ctx; | ||
| 80 | } MCDeintContext; | ||
| 81 | |||
| 82 | #define OFFSET(x) offsetof(MCDeintContext, x) | ||
| 83 | #define FLAGS AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_FILTERING_PARAM | ||
| 84 | #define CONST(name, help, val, u) { name, help, 0, AV_OPT_TYPE_CONST, {.i64=val}, INT_MIN, INT_MAX, FLAGS, .unit = u } | ||
| 85 | |||
| 86 | static const AVOption mcdeint_options[] = { | ||
| 87 | { "mode", "set mode", OFFSET(mode), AV_OPT_TYPE_INT, {.i64=MODE_FAST}, 0, MODE_NB-1, FLAGS, .unit="mode" }, | ||
| 88 | CONST("fast", NULL, MODE_FAST, "mode"), | ||
| 89 | CONST("medium", NULL, MODE_MEDIUM, "mode"), | ||
| 90 | CONST("slow", NULL, MODE_SLOW, "mode"), | ||
| 91 | CONST("extra_slow", NULL, MODE_EXTRA_SLOW, "mode"), | ||
| 92 | |||
| 93 | { "parity", "set the assumed picture field parity", OFFSET(parity), AV_OPT_TYPE_INT, {.i64=PARITY_BFF}, -1, 1, FLAGS, .unit = "parity" }, | ||
| 94 | CONST("tff", "assume top field first", PARITY_TFF, "parity"), | ||
| 95 | CONST("bff", "assume bottom field first", PARITY_BFF, "parity"), | ||
| 96 | |||
| 97 | { "qp", "set qp", OFFSET(qp), AV_OPT_TYPE_INT, {.i64=1}, INT_MIN, INT_MAX, FLAGS }, | ||
| 98 | { NULL } | ||
| 99 | }; | ||
| 100 | |||
| 101 | AVFILTER_DEFINE_CLASS(mcdeint); | ||
| 102 | |||
| 103 | 2 | static int config_props(AVFilterLink *inlink) | |
| 104 | { | ||
| 105 | 2 | AVFilterContext *ctx = inlink->dst; | |
| 106 | 2 | MCDeintContext *mcdeint = ctx->priv; | |
| 107 | const AVCodec *enc; | ||
| 108 | AVCodecContext *enc_ctx; | ||
| 109 | 2 | AVDictionary *opts = NULL; | |
| 110 | int ret; | ||
| 111 | |||
| 112 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
|
2 | if (!(enc = avcodec_find_encoder(AV_CODEC_ID_SNOW))) { |
| 113 | ✗ | av_log(ctx, AV_LOG_ERROR, "Snow encoder is not enabled in libavcodec\n"); | |
| 114 | ✗ | return AVERROR(EINVAL); | |
| 115 | } | ||
| 116 | |||
| 117 | 2 | mcdeint->pkt = av_packet_alloc(); | |
| 118 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
|
2 | if (!mcdeint->pkt) |
| 119 | ✗ | return AVERROR(ENOMEM); | |
| 120 | 2 | mcdeint->frame_dec = av_frame_alloc(); | |
| 121 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
|
2 | if (!mcdeint->frame_dec) |
| 122 | ✗ | return AVERROR(ENOMEM); | |
| 123 | 2 | mcdeint->enc_ctx = avcodec_alloc_context3(enc); | |
| 124 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
|
2 | if (!mcdeint->enc_ctx) |
| 125 | ✗ | return AVERROR(ENOMEM); | |
| 126 | 2 | enc_ctx = mcdeint->enc_ctx; | |
| 127 | 2 | enc_ctx->width = inlink->w; | |
| 128 | 2 | enc_ctx->height = inlink->h; | |
| 129 | 2 | enc_ctx->time_base = (AVRational){1,25}; // meaningless | |
| 130 | 2 | enc_ctx->gop_size = INT_MAX; | |
| 131 | 2 | enc_ctx->max_b_frames = 0; | |
| 132 | 2 | enc_ctx->pix_fmt = inlink->format; | |
| 133 | 2 | enc_ctx->flags = AV_CODEC_FLAG_QSCALE | AV_CODEC_FLAG_LOW_DELAY | AV_CODEC_FLAG_RECON_FRAME; | |
| 134 | 2 | enc_ctx->strict_std_compliance = FF_COMPLIANCE_EXPERIMENTAL; | |
| 135 | 2 | enc_ctx->global_quality = 1; | |
| 136 | 2 | enc_ctx->me_cmp = enc_ctx->me_sub_cmp = FF_CMP_SAD; | |
| 137 | 2 | enc_ctx->mb_cmp = FF_CMP_SSE; | |
| 138 | 2 | av_dict_set(&opts, "memc_only", "1", 0); | |
| 139 | 2 | av_dict_set(&opts, "no_bitstream", "1", 0); | |
| 140 | |||
| 141 |
2/5✗ Branch 0 not taken.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
|
2 | switch (mcdeint->mode) { |
| 142 | ✗ | case MODE_EXTRA_SLOW: | |
| 143 | ✗ | enc_ctx->refs = 3; | |
| 144 | ✗ | case MODE_SLOW: | |
| 145 | ✗ | av_dict_set(&opts, "motion_est", "iter", 0); | |
| 146 | 1 | case MODE_MEDIUM: | |
| 147 | 1 | enc_ctx->flags |= AV_CODEC_FLAG_4MV; | |
| 148 | 1 | enc_ctx->dia_size = 2; | |
| 149 | 2 | case MODE_FAST: | |
| 150 | 2 | enc_ctx->flags |= AV_CODEC_FLAG_QPEL; | |
| 151 | } | ||
| 152 | |||
| 153 | 2 | ret = avcodec_open2(enc_ctx, enc, &opts); | |
| 154 | 2 | av_dict_free(&opts); | |
| 155 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
|
2 | if (ret < 0) |
| 156 | ✗ | return ret; | |
| 157 | |||
| 158 | 2 | return 0; | |
| 159 | } | ||
| 160 | |||
| 161 | 4 | static av_cold void uninit(AVFilterContext *ctx) | |
| 162 | { | ||
| 163 | 4 | MCDeintContext *mcdeint = ctx->priv; | |
| 164 | |||
| 165 | 4 | av_packet_free(&mcdeint->pkt); | |
| 166 | 4 | avcodec_free_context(&mcdeint->enc_ctx); | |
| 167 | 4 | av_frame_free(&mcdeint->frame_dec); | |
| 168 | 4 | } | |
| 169 | |||
| 170 | 62 | static int filter_frame(AVFilterLink *inlink, AVFrame *inpic) | |
| 171 | { | ||
| 172 | 62 | MCDeintContext *mcdeint = inlink->dst->priv; | |
| 173 | 62 | AVFilterLink *outlink = inlink->dst->outputs[0]; | |
| 174 | 62 | AVFrame *outpic, *frame_dec = mcdeint->frame_dec; | |
| 175 | 62 | AVPacket *pkt = mcdeint->pkt; | |
| 176 | 62 | const AVPixFmtDescriptor *pix_fmt_desc = av_pix_fmt_desc_get(inlink->format); | |
| 177 | int x, y, i, ret; | ||
| 178 | |||
| 179 | 62 | outpic = ff_get_video_buffer(outlink, outlink->w, outlink->h); | |
| 180 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 62 times.
|
62 | if (!outpic) { |
| 181 | ✗ | av_frame_free(&inpic); | |
| 182 | ✗ | return AVERROR(ENOMEM); | |
| 183 | } | ||
| 184 | 62 | av_frame_copy_props(outpic, inpic); | |
| 185 | 62 | inpic->quality = mcdeint->qp * FF_QP2LAMBDA; | |
| 186 | |||
| 187 | 62 | ret = avcodec_send_frame(mcdeint->enc_ctx, inpic); | |
| 188 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 62 times.
|
62 | if (ret < 0) { |
| 189 | ✗ | av_log(mcdeint->enc_ctx, AV_LOG_ERROR, "Error sending a frame for encoding\n"); | |
| 190 | ✗ | goto end; | |
| 191 | } | ||
| 192 | 62 | ret = avcodec_receive_packet(mcdeint->enc_ctx, pkt); | |
| 193 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 62 times.
|
62 | if (ret < 0) { |
| 194 | ✗ | av_log(mcdeint->enc_ctx, AV_LOG_ERROR, "Error receiving a packet from encoding\n"); | |
| 195 | ✗ | goto end; | |
| 196 | } | ||
| 197 | 62 | av_packet_unref(pkt); | |
| 198 | 62 | ret = avcodec_receive_frame(mcdeint->enc_ctx, frame_dec); | |
| 199 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 62 times.
|
62 | if (ret < 0) { |
| 200 | ✗ | av_log(mcdeint->enc_ctx, AV_LOG_ERROR, "Error receiving a frame from encoding\n"); | |
| 201 | ✗ | goto end; | |
| 202 | } | ||
| 203 | |||
| 204 |
2/2✓ Branch 0 taken 186 times.
✓ Branch 1 taken 62 times.
|
248 | for (i = 0; i < 3; i++) { |
| 205 | 186 | int is_chroma = !!i; | |
| 206 | int w, h; | ||
| 207 |
2/2✓ Branch 0 taken 124 times.
✓ Branch 1 taken 62 times.
|
186 | if (is_chroma) { |
| 208 | 124 | w = AV_CEIL_RSHIFT(inlink->w, pix_fmt_desc->log2_chroma_w); | |
| 209 | 124 | h = AV_CEIL_RSHIFT(inlink->h, pix_fmt_desc->log2_chroma_h); | |
| 210 | } else { | ||
| 211 | 62 | w = inlink->w; | |
| 212 | 62 | h = inlink->h; | |
| 213 | } | ||
| 214 | 186 | int fils = frame_dec->linesize[i]; | |
| 215 | 186 | int srcs = inpic ->linesize[i]; | |
| 216 | 186 | int dsts = outpic ->linesize[i]; | |
| 217 | |||
| 218 |
2/2✓ Branch 0 taken 71424 times.
✓ Branch 1 taken 186 times.
|
71610 | for (y = 0; y < h; y++) { |
| 219 |
2/2✓ Branch 0 taken 35712 times.
✓ Branch 1 taken 35712 times.
|
71424 | if ((y ^ mcdeint->parity) & 1) { |
| 220 |
2/2✓ Branch 0 taken 19284480 times.
✓ Branch 1 taken 35712 times.
|
19320192 | for (x = 0; x < w; x++) { |
| 221 | 19284480 | uint8_t *filp = &frame_dec->data[i][x + y*fils]; | |
| 222 | 19284480 | uint8_t *srcp = &inpic ->data[i][x + y*srcs]; | |
| 223 | 19284480 | uint8_t *dstp = &outpic ->data[i][x + y*dsts]; | |
| 224 | |||
| 225 |
4/4✓ Branch 0 taken 19238400 times.
✓ Branch 1 taken 46080 times.
✓ Branch 2 taken 19195200 times.
✓ Branch 3 taken 43200 times.
|
19284480 | if (y > 0 && y < h-1){ |
| 226 |
4/4✓ Branch 0 taken 19088622 times.
✓ Branch 1 taken 106578 times.
✓ Branch 2 taken 106578 times.
✓ Branch 3 taken 18982044 times.
|
19195200 | int is_edge = x < 3 || x > w-4; |
| 227 | 19195200 | int diff0 = filp[-fils] - srcp[-srcs]; | |
| 228 | 19195200 | int diff1 = filp[+fils] - srcp[+srcs]; | |
| 229 | 19195200 | int temp = filp[0]; | |
| 230 | |||
| 231 | #define DELTA(j) av_clip(j, -x, w-1-x) | ||
| 232 | |||
| 233 | #define GET_SCORE_EDGE(j)\ | ||
| 234 | FFABS(srcp[-srcs+DELTA(-1+(j))] - srcp[+srcs+DELTA(-1-(j))])+\ | ||
| 235 | FFABS(srcp[-srcs+DELTA(j) ] - srcp[+srcs+DELTA( -(j))])+\ | ||
| 236 | FFABS(srcp[-srcs+DELTA(1+(j)) ] - srcp[+srcs+DELTA( 1-(j))]) | ||
| 237 | |||
| 238 | #define GET_SCORE(j)\ | ||
| 239 | FFABS(srcp[-srcs-1+(j)] - srcp[+srcs-1-(j)])+\ | ||
| 240 | FFABS(srcp[-srcs +(j)] - srcp[+srcs -(j)])+\ | ||
| 241 | FFABS(srcp[-srcs+1+(j)] - srcp[+srcs+1-(j)]) | ||
| 242 | |||
| 243 | #define CHECK_EDGE(j)\ | ||
| 244 | { int score = GET_SCORE_EDGE(j);\ | ||
| 245 | if (score < spatial_score){\ | ||
| 246 | spatial_score = score;\ | ||
| 247 | diff0 = filp[-fils+DELTA(j)] - srcp[-srcs+DELTA(j)];\ | ||
| 248 | diff1 = filp[+fils+DELTA(-(j))] - srcp[+srcs+DELTA(-(j))];\ | ||
| 249 | |||
| 250 | #define CHECK(j)\ | ||
| 251 | { int score = GET_SCORE(j);\ | ||
| 252 | if (score < spatial_score){\ | ||
| 253 | spatial_score= score;\ | ||
| 254 | diff0 = filp[-fils+(j)] - srcp[-srcs+(j)];\ | ||
| 255 | diff1 = filp[+fils-(j)] - srcp[+srcs-(j)];\ | ||
| 256 | |||
| 257 |
2/2✓ Branch 0 taken 213156 times.
✓ Branch 1 taken 18982044 times.
|
19195200 | if (is_edge) { |
| 258 | 213156 | int spatial_score = GET_SCORE_EDGE(0) - 1; | |
| 259 |
4/4✓ Branch 0 taken 1768 times.
✓ Branch 1 taken 211388 times.
✓ Branch 2 taken 554 times.
✓ Branch 3 taken 1214 times.
|
213156 | CHECK_EDGE(-1) CHECK_EDGE(-2) }} }} |
| 260 |
4/4✓ Branch 0 taken 1738 times.
✓ Branch 1 taken 211418 times.
✓ Branch 2 taken 576 times.
✓ Branch 3 taken 1162 times.
|
213156 | CHECK_EDGE( 1) CHECK_EDGE( 2) }} }} |
| 261 | } else { | ||
| 262 | 18982044 | int spatial_score = GET_SCORE(0) - 1; | |
| 263 |
4/4✓ Branch 0 taken 3318352 times.
✓ Branch 1 taken 15663692 times.
✓ Branch 2 taken 1399408 times.
✓ Branch 3 taken 1918944 times.
|
18982044 | CHECK(-1) CHECK(-2) }} }} |
| 264 |
4/4✓ Branch 0 taken 2960014 times.
✓ Branch 1 taken 16022030 times.
✓ Branch 2 taken 1136916 times.
✓ Branch 3 taken 1823098 times.
|
18982044 | CHECK( 1) CHECK( 2) }} }} |
| 265 | } | ||
| 266 | |||
| 267 | |||
| 268 |
2/2✓ Branch 0 taken 8155364 times.
✓ Branch 1 taken 11039836 times.
|
19195200 | if (diff0 + diff1 > 0) |
| 269 | 8155364 | temp -= (diff0 + diff1 - FFABS(FFABS(diff0) - FFABS(diff1)) / 2) / 2; | |
| 270 | else | ||
| 271 | 11039836 | temp -= (diff0 + diff1 + FFABS(FFABS(diff0) - FFABS(diff1)) / 2) / 2; | |
| 272 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 19195200 times.
|
19195200 | *filp = *dstp = temp > 255U ? ~(temp>>31) : temp; |
| 273 | } else { | ||
| 274 | 89280 | *dstp = *filp; | |
| 275 | } | ||
| 276 | } | ||
| 277 | } | ||
| 278 | } | ||
| 279 | |||
| 280 |
2/2✓ Branch 0 taken 71424 times.
✓ Branch 1 taken 186 times.
|
71610 | for (y = 0; y < h; y++) { |
| 281 |
2/2✓ Branch 0 taken 35712 times.
✓ Branch 1 taken 35712 times.
|
71424 | if (!((y ^ mcdeint->parity) & 1)) { |
| 282 |
2/2✓ Branch 0 taken 19284480 times.
✓ Branch 1 taken 35712 times.
|
19320192 | for (x = 0; x < w; x++) { |
| 283 | 19284480 | frame_dec->data[i][x + y*fils] = | |
| 284 | 19284480 | outpic ->data[i][x + y*dsts] = inpic->data[i][x + y*srcs]; | |
| 285 | } | ||
| 286 | } | ||
| 287 | } | ||
| 288 | } | ||
| 289 | 62 | mcdeint->parity ^= 1; | |
| 290 | |||
| 291 | 62 | end: | |
| 292 | 62 | av_packet_unref(pkt); | |
| 293 | 62 | av_frame_free(&inpic); | |
| 294 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 62 times.
|
62 | if (ret < 0) { |
| 295 | ✗ | av_frame_free(&outpic); | |
| 296 | ✗ | return ret; | |
| 297 | } | ||
| 298 | 62 | return ff_filter_frame(outlink, outpic); | |
| 299 | } | ||
| 300 | |||
| 301 | static const AVFilterPad mcdeint_inputs[] = { | ||
| 302 | { | ||
| 303 | .name = "default", | ||
| 304 | .type = AVMEDIA_TYPE_VIDEO, | ||
| 305 | .filter_frame = filter_frame, | ||
| 306 | .config_props = config_props, | ||
| 307 | }, | ||
| 308 | }; | ||
| 309 | |||
| 310 | const FFFilter ff_vf_mcdeint = { | ||
| 311 | .p.name = "mcdeint", | ||
| 312 | .p.description = NULL_IF_CONFIG_SMALL("Apply motion compensating deinterlacing."), | ||
| 313 | .p.priv_class = &mcdeint_class, | ||
| 314 | .priv_size = sizeof(MCDeintContext), | ||
| 315 | .uninit = uninit, | ||
| 316 | FILTER_INPUTS(mcdeint_inputs), | ||
| 317 | FILTER_OUTPUTS(ff_video_default_filterpad), | ||
| 318 | FILTER_PIXFMTS(AV_PIX_FMT_YUV420P, AV_PIX_FMT_YUV444P), | ||
| 319 | }; | ||
| 320 |