[FFmpeg-devel] [PATCH] lavfi/select/WIP: add scene detection.

Clément Bœsch ubitux at gmail.com
Sat May 26 12:41:01 CEST 2012


eg: ffmpeg -i ~/samples/GoneNutty.avi -vf scale=160:120,select='gt(scene_score\,60)*gt(scene_diff\,60)',tile -frames:v 1 out.png

TODO: documentation

Based on the shotdetect algorithm.
---
 libavfilter/vf_select.c |   63 +++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 63 insertions(+)

diff --git a/libavfilter/vf_select.c b/libavfilter/vf_select.c
index d3f649c..5a9bdce 100644
--- a/libavfilter/vf_select.c
+++ b/libavfilter/vf_select.c
@@ -25,7 +25,9 @@
 
 #include "libavutil/eval.h"
 #include "libavutil/fifo.h"
+#include "libavutil/timestamp.h"
 #include "avfilter.h"
+#include "formats.h"
 #include "video.h"
 
 static const char *const var_names[] = {
@@ -62,6 +64,9 @@ static const char *const var_names[] = {
     "key",               ///< tell if the frame is a key frame
     "pos",               ///< original position in the file of the frame
 
+    "scene_diff",
+    "scene_score",
+
     NULL
 };
 
@@ -99,6 +104,9 @@ enum var_name {
     VAR_KEY,
     VAR_POS,
 
+    VAR_SCENE_DIFF,
+    VAR_SCENE_SCORE,
+
     VAR_VARS_NB
 };
 
@@ -107,6 +115,9 @@ enum var_name {
 typedef struct {
     AVExpr *expr;
     double var_values[VAR_VARS_NB];
+    int do_scene_detect;
+    int64_t prev_score;             ///< score of the previous frame (scene detect only)
+    AVFilterBufferRef *prev_picref; ///< previous frame
     double select;
     int cache_frames;
     AVFifoBuffer *pending_frames; ///< FIFO buffer of video frames
@@ -128,6 +139,8 @@ static av_cold int init(AVFilterContext *ctx, const char *args, void *opaque)
         av_log(ctx, AV_LOG_ERROR, "Failed to allocate pending frames buffer.\n");
         return AVERROR(ENOMEM);
     }
+
+    select->do_scene_detect = args && strstr(args, "scene_");
     return 0;
 }
 
@@ -166,12 +179,44 @@ static int config_input(AVFilterLink *inlink)
 #define D2TS(d)  (isnan(d) ? AV_NOPTS_VALUE : (int64_t)(d))
 #define TS2D(ts) ((ts) == AV_NOPTS_VALUE ? NAN : (double)(ts))
 
+static void set_scene_values(AVFilterContext *ctx, AVFilterBufferRef *picref)
+{
+    SelectContext *select = ctx->priv;
+    AVFilterBufferRef *prev_picref = select->prev_picref;
+
+    if (prev_picref &&
+        picref->video->h == prev_picref->video->h &&
+        picref->video->w == prev_picref->video->w) {
+        int64_t score = 0, diff;
+        int x, y;
+
+#define ABSSC(a) abs((int)p[a] - (int)q[a])
+        for (y = 0; y < picref->video->h; y++) {
+            for (x = 0; x < picref->video->w; x++) {
+                const uint8_t *p =      picref->data[0] + y *      picref->linesize[0] + x*3;
+                const uint8_t *q = prev_picref->data[0] + y * prev_picref->linesize[0] + x*3;
+                score += ABSSC(0) + ABSSC(1) + ABSSC(2);
+            }
+        }
+        score /= picref->video->h * picref->video->w;
+        diff = llabs(score - select->prev_score);
+        select->var_values[VAR_SCENE_SCORE] = score;
+        select->var_values[VAR_SCENE_DIFF]  = diff;
+        select->prev_score = score;
+        avfilter_unref_buffer(prev_picref);
+    }
+
+    select->prev_picref = avfilter_ref_buffer(picref, ~0);
+}
+
 static int select_frame(AVFilterContext *ctx, AVFilterBufferRef *picref)
 {
     SelectContext *select = ctx->priv;
     AVFilterLink *inlink = ctx->inputs[0];
     double res;
 
+    if (select->do_scene_detect)
+        set_scene_values(ctx, picref);
     if (isnan(select->var_values[VAR_START_PTS]))
         select->var_values[VAR_START_PTS] = TS2D(picref->pts);
     if (isnan(select->var_values[VAR_START_T]))
@@ -315,6 +360,23 @@ static av_cold void uninit(AVFilterContext *ctx)
         avfilter_unref_buffer(picref);
     av_fifo_free(select->pending_frames);
     select->pending_frames = NULL;
+    avfilter_unref_bufferp(&select->prev_picref);
+}
+
+static int query_formats(AVFilterContext *ctx)
+{
+    SelectContext *select = ctx->priv;
+
+    if (!select->do_scene_detect) {
+        ff_default_query_formats(ctx);
+    } else {
+        static const enum PixelFormat pix_fmts[] = {
+            PIX_FMT_RGB24, PIX_FMT_BGR24,
+            PIX_FMT_NONE
+        };
+        avfilter_set_common_pixel_formats(ctx, avfilter_make_format_list(pix_fmts));
+    }
+    return 0;
 }
 
 AVFilter avfilter_vf_select = {
@@ -322,6 +384,7 @@ AVFilter avfilter_vf_select = {
     .description = NULL_IF_CONFIG_SMALL("Select frames to pass in output."),
     .init      = init,
     .uninit    = uninit,
+    .query_formats = query_formats,
 
     .priv_size = sizeof(SelectContext),
 
-- 
1.7.10.2



More information about the ffmpeg-devel mailing list