vf_blackframe.c 4.36 KB
Newer Older
1
/*
2 3 4 5
 * Copyright (c) 2010 Stefano Sabatini
 * Copyright (c) 2006 Ivo van Poorten
 * Copyright (c) 2006 Julian Hall
 * Copyright (c) 2002-2003 Brian J. Murrell
6
 *
7
 * This file is part of Libav.
8
 *
9
 * Libav is free software; you can redistribute it and/or modify
10 11 12 13
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
14
 * Libav is distributed in the hope that it will be useful,
15 16 17 18 19
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along
20
 * with Libav; if not, write to the Free Software Foundation, Inc.,
21 22 23 24 25 26 27 28 29 30
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */

/**
 * @file
 * Search for black frames to detect scene transitions.
 * Ported from MPlayer libmpcodecs/vf_blackframe.c.
 */

#include "avfilter.h"
31
#include "video.h"
32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100

typedef struct {
    unsigned int bamount; ///< black amount
    unsigned int bthresh; ///< black threshold
    unsigned int frame;   ///< frame number
    unsigned int nblack;  ///< number of black pixels counted so far
} BlackFrameContext;

static int query_formats(AVFilterContext *ctx)
{
    static const enum PixelFormat pix_fmts[] = {
        PIX_FMT_YUV410P, PIX_FMT_YUV420P, PIX_FMT_GRAY8, PIX_FMT_NV12,
        PIX_FMT_NV21, PIX_FMT_YUV444P, PIX_FMT_YUV422P, PIX_FMT_YUV411P,
        PIX_FMT_NONE
    };

    avfilter_set_common_formats(ctx, avfilter_make_format_list(pix_fmts));
    return 0;
}

static av_cold int init(AVFilterContext *ctx, const char *args, void *opaque)
{
    BlackFrameContext *blackframe = ctx->priv;

    blackframe->bamount = 98;
    blackframe->bthresh = 32;
    blackframe->nblack = 0;
    blackframe->frame = 0;

    if (args)
        sscanf(args, "%u:%u", &blackframe->bamount, &blackframe->bthresh);

    av_log(ctx, AV_LOG_INFO, "bamount:%u bthresh:%u\n",
           blackframe->bamount, blackframe->bthresh);

    if (blackframe->bamount > 100 || blackframe->bthresh > 255) {
        av_log(ctx, AV_LOG_ERROR, "Too big value for bamount (max is 100) or bthresh (max is 255)\n");
        return AVERROR(EINVAL);
    }

    return 0;
}

static void draw_slice(AVFilterLink *inlink, int y, int h, int slice_dir)
{
    AVFilterContext *ctx = inlink->dst;
    BlackFrameContext *blackframe = ctx->priv;
    AVFilterBufferRef *picref = inlink->cur_buf;
    int x, i;
    uint8_t *p = picref->data[0] + y * picref->linesize[0];

    for (i = 0; i < h; i++) {
        for (x = 0; x < inlink->w; x++)
            blackframe->nblack += p[x] < blackframe->bthresh;
        p += picref->linesize[0];
    }

    avfilter_draw_slice(ctx->outputs[0], y, h, slice_dir);
}

static void end_frame(AVFilterLink *inlink)
{
    AVFilterContext *ctx = inlink->dst;
    BlackFrameContext *blackframe = ctx->priv;
    AVFilterBufferRef *picref = inlink->cur_buf;
    int pblack = 0;

    pblack = blackframe->nblack * 100 / (inlink->w * inlink->h);
    if (pblack >= blackframe->bamount)
101 102
        av_log(ctx, AV_LOG_INFO, "frame:%u pblack:%u pos:%"PRId64" pts:%"PRId64" t:%f\n",
               blackframe->frame, pblack, picref->pos, picref->pts,
103
               picref->pts == AV_NOPTS_VALUE ? -1 : picref->pts * av_q2d(inlink->time_base));
104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121

    blackframe->frame++;
    blackframe->nblack = 0;
    avfilter_end_frame(inlink->dst->outputs[0]);
}

AVFilter avfilter_vf_blackframe = {
    .name        = "blackframe",
    .description = NULL_IF_CONFIG_SMALL("Detect frames that are (almost) black."),

    .priv_size = sizeof(BlackFrameContext),
    .init      = init,

    .query_formats = query_formats,

    .inputs    = (AVFilterPad[]) {{ .name = "default",
                                    .type             = AVMEDIA_TYPE_VIDEO,
                                    .draw_slice       = draw_slice,
122 123
                                    .get_video_buffer = ff_null_get_video_buffer,
                                    .start_frame      = ff_null_start_frame,
124 125 126 127 128 129 130
                                    .end_frame        = end_frame, },
                                  { .name = NULL}},

    .outputs   = (AVFilterPad[]) {{ .name             = "default",
                                    .type             = AVMEDIA_TYPE_VIDEO },
                                  { .name = NULL}},
};