Fix the following issues with the keep option: 1. No similarity check during keep period: The code returned early during the keep period without checking if the frame was actually similar to the reference.
2. keep_count doesn't reset on different frames: The counter should track "consecutive similar frames," but when a different frame arrived mid-count, keep_count didn't reset to 0. It only reset after going negative (after a drop), causing the counter to accumulate across non-consecutive similar frames and drop frames earlier than expected. 3. Reference frame drift: When similar frames were kept due to keep_count, they became the new reference. Over time, tiny differences (sensor noise, compression artifacts) accumulated. Frame N got compared to frame N-1 instead of the last different frame, allowing gradual scene changes to slip through undetected. Now decimate_frame() checks similarity first, then applies keep_count logic, returning distinct values: 1 = drop (similar, over keep threshold) 0 = keep (different frame) - updates reference, resets keep_count -1 = keep (similar, under keep threshold) - preserves reference Signed-off-by: Dana Feng [email protected]<mailto:[email protected]> --- libavfilter/vf_mpdecimate.c | 49 +++++++++++++++++++++++++------------ 1 file changed, 34 insertions(+), 15 deletions(-) diff --git a/libavfilter/vf_mpdecimate.c b/libavfilter/vf_mpdecimate.c index 0fc95556b7..5b1654f671 100644 --- a/libavfilter/vf_mpdecimate.c +++ b/libavfilter/vf_mpdecimate.c @@ -109,19 +109,18 @@ static int diff_planes(AVFilterContext *ctx, /** * Tell if the frame should be decimated, for example if it is no much * different with respect to the reference frame ref. + * + * @return 1 if frame should be dropped (similar, over keep threshold), + * 0 if frame should be kept (different), + * -1 if frame should be kept (similar, under keep threshold) */ static int decimate_frame(AVFilterContext *ctx, AVFrame *cur, AVFrame *ref) { DecimateContext *decimate = ctx->priv; + int is_similar; int plane; - if (decimate->max_keep_count > 0 && - decimate->keep_count > -1 && - decimate->keep_count < decimate->max_keep_count) { - decimate->keep_count++; - return 0; - } if (decimate->max_drop_count > 0 && decimate->drop_count >= decimate->max_drop_count) return 0; @@ -129,6 +128,7 @@ static int decimate_frame(AVFilterContext *ctx, (decimate->drop_count-1) > decimate->max_drop_count) return 0; + is_similar = 1; for (plane = 0; ref->data[plane] && ref->linesize[plane]; plane++) { /* use 8x8 SAD even on subsampled planes. The blocks won't match up with * luma blocks, but hopefully nobody is depending on this to catch @@ -141,8 +141,21 @@ static int decimate_frame(AVFilterContext *ctx, cur->data[plane], cur->linesize[plane], ref->data[plane], ref->linesize[plane], AV_CEIL_RSHIFT(ref->width, hsub), - AV_CEIL_RSHIFT(ref->height, vsub))) - return 0; + AV_CEIL_RSHIFT(ref->height, vsub))) { + is_similar = 0; + break; + } + } + + if (!is_similar) + return 0; /* Frame is different - keep it */ + + /* Frame is similar - apply keep_count logic */ + if (decimate->max_keep_count > 0 && + decimate->keep_count > -1 && + decimate->keep_count < decimate->max_keep_count) { + decimate->keep_count++; + return -1; /* Similar but under keep threshold - keep without updating ref */ } return 1; @@ -200,29 +213,35 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *cur) DecimateContext *decimate = inlink->dst->priv; AVFilterLink *outlink = inlink->dst->outputs[0]; int ret; + int result = decimate->ref ? decimate_frame(inlink->dst, cur, decimate->ref) : 0; - if (decimate->ref && decimate_frame(inlink->dst, cur, decimate->ref)) { + if (result == 1) { + /* Drop: similar frame, over keep threshold */ decimate->drop_count = FFMAX(1, decimate->drop_count+1); - decimate->keep_count = -1; // do not keep any more frames until non-similar frames are detected + decimate->keep_count = -1; + } else if (result == -1) { + /* Keep: similar frame, under keep threshold - don't update ref */ + decimate->drop_count = FFMIN(-1, decimate->drop_count-1); + if ((ret = ff_filter_frame(outlink, cur)) < 0) + return ret; } else { + /* Keep: different frame - update ref and reset keep_count */ av_frame_free(&decimate->ref); decimate->ref = cur; decimate->drop_count = FFMIN(-1, decimate->drop_count-1); - if (decimate->keep_count < 0) // re-enable counting similar frames to ignore before dropping - decimate->keep_count = 0; - + decimate->keep_count = 0; if ((ret = ff_filter_frame(outlink, av_frame_clone(cur))) < 0) return ret; } av_log(inlink->dst, AV_LOG_DEBUG, "%s pts:%s pts_time:%s drop_count:%d keep_count:%d\n", - decimate->drop_count > 0 ? "drop" : "keep", + result > 0 ? "drop" : "keep", av_ts2str(cur->pts), av_ts2timestr(cur->pts, &inlink->time_base), decimate->drop_count, decimate->keep_count); - if (decimate->drop_count > 0) + if (result > 0) av_frame_free(&cur); return 0; -- 2.39.5 _______________________________________________ ffmpeg-devel mailing list -- [email protected] To unsubscribe send an email to [email protected]
