On Mon, 28 Sep 2015 21:31:59 -0500
Ray Cole <the.other.ray.c...@gmail.com> wrote:
> This is the first time I've offered up code to any open source
> project...so be gentle :-)
ehe, patches are what the devs want, either git diff or diff -u...
i've downloaded and diff'ed your filter to ffmpeg git master and
attached it in this mail.
-compn
--- vf_decimate.c Tue Sep 29 08:30:08 2015
+++ vf_decimatex.c Tue Sep 29 08:28:22 2015
@@ -51,6 +51,10 @@
int bdiffsize;
int64_t *bdiffs;
+ /* Ray */
+ int lastdrop;
+ int64_t drop_count[5]; // drop counts
+
/* options */
int cycle;
double dupthresh_flt;
@@ -60,6 +64,9 @@
int blockx, blocky;
int ppsrc;
int chroma;
+ int force_drop;
+ int lock_on;
+
} DecimateContext;
#define OFFSET(x) offsetof(DecimateContext, x)
@@ -71,9 +78,13 @@
{ "scthresh", "set scene change threshold", OFFSET(scthresh_flt),
AV_OPT_TYPE_DOUBLE, {.dbl = 15.0}, 0, 100, FLAGS },
{ "blockx", "set the size of the x-axis blocks used during metric
calculations", OFFSET(blockx), AV_OPT_TYPE_INT, {.i64 = 32}, 4, 1<<9, FLAGS },
{ "blocky", "set the size of the y-axis blocks used during metric
calculations", OFFSET(blocky), AV_OPT_TYPE_INT, {.i64 = 32}, 4, 1<<9, FLAGS },
- { "ppsrc", "mark main input as a pre-processed input and activate
clean source input stream", OFFSET(ppsrc), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1,
FLAGS },
- { "chroma", "set whether or not chroma is considered in the metric
calculations", OFFSET(chroma), AV_OPT_TYPE_BOOL, {.i64=1}, 0, 1, FLAGS },
+ { "ppsrc", "mark main input as a pre-processed input and activate
clean source input stream", OFFSET(ppsrc), AV_OPT_TYPE_INT, {.i64=0}, 0, 1,
FLAGS },
+ { "chroma", "set whether or not chroma is considered in the metric
calculations", OFFSET(chroma), AV_OPT_TYPE_INT, {.i64=1}, 0, 1, FLAGS },
+ { "force_drop", "set to forcefully drop frame X in cycle",
OFFSET(force_drop), AV_OPT_TYPE_INT, {.i64=-1}, -1, 4, FLAGS },
+ { "lock_on", "set to lock on to a cycle", OFFSET(lock_on),
AV_OPT_TYPE_INT, {.i64=0}, 0, 1, FLAGS },
+
{ NULL }
+
};
AVFILTER_DEFINE_CLASS(decimate);
@@ -140,13 +151,15 @@
q->totdiff = 0;
for (i = 0; i < dm->bdiffsize; i++)
q->totdiff += bdiffs[i];
+
q->maxbdiff = maxdiff;
+
}
static int filter_frame(AVFilterLink *inlink, AVFrame *in)
{
int scpos = -1, duppos = -1;
- int drop = INT_MIN, i, lowest = 0, ret;
+ int drop = INT_MIN, i, lowest = 0, lowest_tot = 0, ret =0;
AVFilterContext *ctx = inlink->dst;
AVFilterLink *outlink = ctx->outputs[0];
DecimateContext *dm = ctx->priv;
@@ -176,17 +189,166 @@
dm->last = av_frame_clone(in);
dm->fid = 0;
+
+// The major change starts here
+
+// First we will NOT consider the 'next' frame in the drop detection because
that would be wrong.
+// The old code would allow frame 0 to be dropped immediately after frame 4.
I've not seen a case where that makes sense and
+// frame 0 could never be followed by a drop of 1, nor could frame 1 be
followed by 2, etc. because of the way detection is
+// performed 5 frames at a time. So first we start at what _should_ be a
reasonable point to be closer inline with what can
+// happen when frames 0, 1 and 2 are the drops.
+
+ int iStart = 0;
+
+ if (dm->lastdrop == (dm->cycle - 1)) // Do NOT allow it to drop 2
frames in a row...because that WOULD be wrong...
+ iStart = 2; // Last frame of cycle runs
chance of consecutive frame drop without this...
+ // Perhaps should force 2 frames
inbetween? Sure - let's start at 2 for 4, 1 for 3
+ else
+ if (dm->lastdrop == (dm->cycle - 2)) // Do NOT allow it to drop 2
frames in a row...because that WOULD be wrong...
+ iStart = 1; // Make sure 2 frames skipped
before next drop
+
/* we have a complete cycle, select the frame to drop */
- lowest = 0;
+ lowest = iStart;
+ lowest_tot = iStart;
+
+// We will now locate the lowest frame by diff and by total.
+
for (i = 0; i < dm->cycle; i++) {
if (dm->queue[i].totdiff > dm->scthresh)
scpos = i;
- if (dm->queue[i].maxbdiff < dm->queue[lowest].maxbdiff)
- lowest = i;
+
+ if (i >= iStart || scpos >= 0) // if in range of eligible for
pattern drop
+ {
+ if (dm->queue[lowest].maxbdiff == 0 ||
+ dm->queue[i].maxbdiff < dm->queue[lowest].maxbdiff)
+ lowest = i;
+
+ if (dm->queue[lowest_tot].totdiff == 0 ||
+ dm->queue[i].totdiff < dm->queue[lowest_tot].totdiff)
+ lowest_tot = i;
+ };
}
+
if (dm->queue[lowest].maxbdiff < dm->dupthresh)
duppos = lowest;
- drop = scpos >= 0 && duppos < 0 ? scpos : lowest;
+
+// If the lowest from by max and total do not agree, and this is not the first
cycle,
+// then we need to figure out how to resolve the conflict.
+// To resolve it we first find out the most commonly dropped frame that we
have been
+// seeing recently. If either of the two 'lowest' values match the
commonly-dropped
+// frame then we assume the cycle continues - majority rules. If the lowests
and
+// common do not agree then we assume the last frame we dropped is probably
OK...
+
+ if (lowest_tot != lowest && // the two methods disagree
+ dm->lastdrop >= 0) // and this isn't our first drop
+
+ {
+
+ short idx = 1;
+ short iCommon = 0;
+ while (idx < dm->cycle)
+ {
+
+ if (dm->drop_count[idx] > dm->drop_count[iCommon])
+ iCommon = idx;
+
+ idx ++;
+
+ };
+
+ int origLowest = lowest;
+ if (iCommon == lowest) // if most common match lowest
+ lowest = iCommon; // use common
+ else // otherwise
+ if (iCommon == lowest_tot) // if most common matches lowest by total
+ lowest = iCommon; // use common
+ else // otherwise
+ lowest = dm->lastdrop;
+
+#ifdef _SRC_
+ av_log(ctx, AV_LOG_INFO, "\n Max/Tot Disagree (%d %d). Using drop
of %d\n",
+ origLowest,
+ lowest_tot,
+ lowest);
+#endif
+
+ };
+
+// Scene change detection seemed difficult to tune so this code ignores scene
changes entirely.
+// The default duplicate detection didn't work for me and I never could find a
value that was good.
+// If scene detection is enabled I really think it should gather some
statistics from early frames
+// and compute what is likely a good duplicate threshold for duplicates.
Frankly I have yet to come
+// across anything where scene change detection bought me anything...
+
+// drop = scpos >= 0 && duppos < 0 ? scpos : lowest;
+ drop = lowest;
+
+ dm->drop_count[drop] ++;
+
+ if (dm->drop_count[drop] > 8) // if reached a "stable" threshold
+ {
+
+// This is experimental. If you know the cycle NEVER changes then this
+// will lock onto the cycle permanently. Limited use.
+
+ if (dm->lock_on == 1 && dm->force_drop < 0)
+ {
+ av_log(ctx, AV_LOG_INFO, "\nLocking onto pattern - will
forcefully drop %d.\n", drop);
+ dm->force_drop = drop;
+ }
+ else
+ {
+ if (dm->force_drop >= 0 && drop != dm->force_drop)
+ {
+ av_log(ctx, AV_LOG_ERROR, "\nForced pattern may not be
working...ending.\n");
+ return -4;
+ }
+ }
+
+// Reset the drop counts, but keep the dominate drop 'high'.
+
+ memset(&dm->drop_count, 0, sizeof(dm->drop_count)); // reset cycles
+ dm->drop_count[drop] = 4; // keep this
one 'high' for now
+
+ }
+
+// If we are forcing a drop then make it force but note the difference.
Forcing a drop is
+// only useful if you know the cycle. I had such a clip that I used to test
the other algorithms
+// and this allowed me to note areas where an incorrect frame was dropped.
With the changes in
+// determining the drop based on both tot and max diffs these 'errors' tended
to happen only in
+// blackout scenes or stills - harmless errors.
+ if (dm->force_drop >= 0 && drop != dm->force_drop) // if forcing drop
+ {
+ av_log(ctx, AV_LOG_INFO, "\n Wanted to drop %d, but forcefully
dropping %d.\n",
+ drop, dm->force_drop);
+ drop = dm->force_drop;
+ };
+
+// A change in cycle has been detected. Note it for diagnostic purposes.
Probably shouldn't be
+// an 'INFO' but made it easier to debug.
+
+ if (drop != dm->lastdrop)
+ {
+ av_log(ctx, AV_LOG_INFO, "\n Pulldown dropping %d\n", drop);
+
+#ifdef _SRC_
+ av_log(ctx, AV_LOG_INFO, "MaxDiffs: %lld %lld %lld %lld %lld\n",
+ dm->queue[0].maxbdiff,
+ dm->queue[1].maxbdiff,
+ dm->queue[2].maxbdiff,
+ dm->queue[3].maxbdiff,
+ dm->queue[4].maxbdiff);
+ av_log(ctx, AV_LOG_INFO, "TotDiffs: %lld %lld %lld %lld %lld\n",
+ dm->queue[0].totdiff,
+ dm->queue[1].totdiff,
+ dm->queue[2].totdiff,
+ dm->queue[3].totdiff,
+ dm->queue[4].totdiff);
+#endif
+
+ };
+
+ dm->lastdrop = drop;
}
/* metrics debug */
@@ -203,7 +365,7 @@
}
/* push all frames except the drop */
- ret = 0;
+
for (i = 0; i < dm->cycle && dm->queue[i].frame; i++) {
if (i == drop) {
if (dm->ppsrc)
@@ -239,7 +401,7 @@
dm->hsub = pix_desc->log2_chroma_w;
dm->vsub = pix_desc->log2_chroma_h;
- dm->depth = pix_desc->comp[0].depth;
+ dm->depth = pix_desc->comp[0].depth_minus1 + 1;
max_value = (1 << dm->depth) - 1;
dm->scthresh = (int64_t)(((int64_t)max_value * w * h *
dm->scthresh_flt) / 100);
dm->dupthresh = (int64_t)(((int64_t)max_value * dm->blockx * dm->blocky *
dm->dupthresh_flt) / 100);
@@ -248,6 +410,14 @@
dm->bdiffsize = dm->nxblocks * dm->nyblocks;
dm->bdiffs = av_malloc_array(dm->bdiffsize, sizeof(*dm->bdiffs));
dm->queue = av_calloc(dm->cycle, sizeof(*dm->queue));
+ dm->lastdrop = -1;
+ dm->drop_count[0] = 0;
+ dm->drop_count[1] = 0;
+ dm->drop_count[2] = 0;
+ dm->drop_count[3] = 0;
+ dm->drop_count[4] = 0;
+
+ av_log(ctx, AV_LOG_INFO, "Cycle: %d\n", dm->cycle);
if (!dm->bdiffs || !dm->queue)
return AVERROR(ENOMEM);
@@ -372,6 +542,7 @@
fps = av_mul_q(fps, (AVRational){dm->cycle - 1, dm->cycle});
av_log(ctx, AV_LOG_VERBOSE, "FPS: %d/%d -> %d/%d\n",
inlink->frame_rate.num, inlink->frame_rate.den, fps.num, fps.den);
+ outlink->flags |= FF_LINK_FLAG_REQUEST_LOOP;
outlink->time_base = inlink->time_base;
outlink->frame_rate = fps;
outlink->sample_aspect_ratio = inlink->sample_aspect_ratio;
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
http://ffmpeg.org/mailman/listinfo/ffmpeg-devel