00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00030 #include "avfilter.h"
00031 #include "internal.h"
00032
00033 #define HIST_SIZE (3*256)
00034
00035 struct thumb_frame {
00036 AVFilterBufferRef *buf;
00037 int histogram[HIST_SIZE];
00038 };
00039
00040 typedef struct {
00041 int n;
00042 int n_frames;
00043 struct thumb_frame *frames;
00044 } ThumbContext;
00045
00046 static av_cold int init(AVFilterContext *ctx, const char *args)
00047 {
00048 ThumbContext *thumb = ctx->priv;
00049
00050 if (!args) {
00051 thumb->n_frames = 100;
00052 } else {
00053 int n = sscanf(args, "%d", &thumb->n_frames);
00054 if (n != 1 || thumb->n_frames < 2) {
00055 thumb->n_frames = 0;
00056 av_log(ctx, AV_LOG_ERROR,
00057 "Invalid number of frames specified (minimum is 2).\n");
00058 return AVERROR(EINVAL);
00059 }
00060 }
00061 thumb->frames = av_calloc(thumb->n_frames, sizeof(*thumb->frames));
00062 if (!thumb->frames) {
00063 av_log(ctx, AV_LOG_ERROR,
00064 "Allocation failure, try to lower the number of frames\n");
00065 return AVERROR(ENOMEM);
00066 }
00067 av_log(ctx, AV_LOG_VERBOSE, "batch size: %d frames\n", thumb->n_frames);
00068 return 0;
00069 }
00070
00077 static double frame_sum_square_err(const int *hist, const double *median)
00078 {
00079 int i;
00080 double err, sum_sq_err = 0;
00081
00082 for (i = 0; i < HIST_SIZE; i++) {
00083 err = median[i] - (double)hist[i];
00084 sum_sq_err += err*err;
00085 }
00086 return sum_sq_err;
00087 }
00088
00089 static int filter_frame(AVFilterLink *inlink, AVFilterBufferRef *frame)
00090 {
00091 int i, j, best_frame_idx = 0;
00092 double avg_hist[HIST_SIZE] = {0}, sq_err, min_sq_err = -1;
00093 AVFilterContext *ctx = inlink->dst;
00094 ThumbContext *thumb = ctx->priv;
00095 AVFilterLink *outlink = ctx->outputs[0];
00096 AVFilterBufferRef *picref;
00097 int *hist = thumb->frames[thumb->n].histogram;
00098 const uint8_t *p = frame->data[0];
00099
00100
00101 thumb->frames[thumb->n].buf = frame;
00102
00103
00104 for (j = 0; j < inlink->h; j++) {
00105 for (i = 0; i < inlink->w; i++) {
00106 hist[0*256 + p[i*3 ]]++;
00107 hist[1*256 + p[i*3 + 1]]++;
00108 hist[2*256 + p[i*3 + 2]]++;
00109 }
00110 p += frame->linesize[0];
00111 }
00112
00113
00114 if (thumb->n < thumb->n_frames - 1) {
00115 thumb->n++;
00116 return 0;
00117 }
00118
00119
00120 for (j = 0; j < FF_ARRAY_ELEMS(avg_hist); j++) {
00121 for (i = 0; i < thumb->n_frames; i++)
00122 avg_hist[j] += (double)thumb->frames[i].histogram[j];
00123 avg_hist[j] /= thumb->n_frames;
00124 }
00125
00126
00127 for (i = 0; i < thumb->n_frames; i++) {
00128 sq_err = frame_sum_square_err(thumb->frames[i].histogram, avg_hist);
00129 if (i == 0 || sq_err < min_sq_err)
00130 best_frame_idx = i, min_sq_err = sq_err;
00131 }
00132
00133
00134 for (i = 0; i < thumb->n_frames; i++) {
00135 memset(thumb->frames[i].histogram, 0, sizeof(thumb->frames[i].histogram));
00136 if (i == best_frame_idx)
00137 continue;
00138 avfilter_unref_bufferp(&thumb->frames[i].buf);
00139 }
00140 thumb->n = 0;
00141
00142
00143 picref = thumb->frames[best_frame_idx].buf;
00144 av_log(ctx, AV_LOG_INFO, "frame id #%d (pts_time=%f) selected\n",
00145 best_frame_idx, picref->pts * av_q2d(inlink->time_base));
00146 thumb->frames[best_frame_idx].buf = NULL;
00147 return ff_filter_frame(outlink, picref);
00148 }
00149
00150 static av_cold void uninit(AVFilterContext *ctx)
00151 {
00152 int i;
00153 ThumbContext *thumb = ctx->priv;
00154 for (i = 0; i < thumb->n_frames && thumb->frames[i].buf; i++)
00155 avfilter_unref_bufferp(&thumb->frames[i].buf);
00156 av_freep(&thumb->frames);
00157 }
00158
00159 static int request_frame(AVFilterLink *link)
00160 {
00161 ThumbContext *thumb = link->src->priv;
00162
00163
00164
00165 do {
00166 int ret = ff_request_frame(link->src->inputs[0]);
00167 if (ret < 0)
00168 return ret;
00169 } while (thumb->n);
00170 return 0;
00171 }
00172
00173 static int poll_frame(AVFilterLink *link)
00174 {
00175 ThumbContext *thumb = link->src->priv;
00176 AVFilterLink *inlink = link->src->inputs[0];
00177 int ret, available_frames = ff_poll_frame(inlink);
00178
00179
00180
00181 if (!available_frames)
00182 return 0;
00183
00184
00185
00186 if (thumb->n == thumb->n_frames - 1)
00187 return 1;
00188
00189
00190
00191 ret = ff_request_frame(inlink);
00192 return ret < 0 ? ret : 0;
00193 }
00194
00195 static int query_formats(AVFilterContext *ctx)
00196 {
00197 static const enum AVPixelFormat pix_fmts[] = {
00198 AV_PIX_FMT_RGB24, AV_PIX_FMT_BGR24,
00199 AV_PIX_FMT_NONE
00200 };
00201 ff_set_common_formats(ctx, ff_make_format_list(pix_fmts));
00202 return 0;
00203 }
00204
00205 static const AVFilterPad thumbnail_inputs[] = {
00206 {
00207 .name = "default",
00208 .type = AVMEDIA_TYPE_VIDEO,
00209 .get_video_buffer = ff_null_get_video_buffer,
00210 .min_perms = AV_PERM_PRESERVE,
00211 .filter_frame = filter_frame,
00212 },
00213 { NULL }
00214 };
00215
00216 static const AVFilterPad thumbnail_outputs[] = {
00217 {
00218 .name = "default",
00219 .type = AVMEDIA_TYPE_VIDEO,
00220 .request_frame = request_frame,
00221 .poll_frame = poll_frame,
00222 },
00223 { NULL }
00224 };
00225
00226 AVFilter avfilter_vf_thumbnail = {
00227 .name = "thumbnail",
00228 .description = NULL_IF_CONFIG_SMALL("Select the most representative frame in a given sequence of consecutive frames."),
00229 .priv_size = sizeof(ThumbContext),
00230 .init = init,
00231 .uninit = uninit,
00232 .query_formats = query_formats,
00233 .inputs = thumbnail_inputs,
00234 .outputs = thumbnail_outputs,
00235 };