#undef NDEBUG
#include <assert.h>
+#define HIST_SIZE 4
+
+typedef enum {
+ TFF,
+ BFF,
+ PROGRSSIVE,
+ UNDETERMINED,
+} Type;
+
typedef struct {
float interlace_threshold;
float progressive_threshold;
+ Type last_type;
+ Type prestat[4];
+ Type poststat[4];
+
+ uint8_t history[HIST_SIZE];
+
AVFilterBufferRef *cur;
AVFilterBufferRef *next;
AVFilterBufferRef *prev;
AVFilterBufferRef *out;
- int (*filter_line)(uint8_t *prev, uint8_t *cur, uint8_t *next, int w);
+ int (*filter_line)(const uint8_t *prev, const uint8_t *cur, const uint8_t *next, int w);
const AVPixFmtDescriptor *csp;
} IDETContext;
+static const char *type2str(Type type)
+{
+ switch(type) {
+ case TFF : return "Top Field First ";
+ case BFF : return "Bottom Field First";
+ case PROGRSSIVE : return "Progressive ";
+ case UNDETERMINED: return "Undetermined ";
+ }
+ return NULL;
+}
static int filter_line_c(const uint8_t *a, const uint8_t *b, const uint8_t *c, int w)
{
int y, i;
int64_t alpha[2]={0};
int64_t delta=0;
- static int p=0, t=0, b=0, u=0;
+ Type type, best_type;
+ int match = 0;
for (i = 0; i < idet->csp->nb_components; i++) {
int w = idet->cur->video->w;
int h = idet->cur->video->h;
int refs = idet->cur->linesize[i];
- int df = (idet->csp->comp[i].depth_minus1 + 8) / 8;
if (i && i<3) {
w >>= idet->csp->log2_chroma_w;
#endif
if (alpha[0] / (float)alpha[1] > idet->interlace_threshold){
- av_log(0, AV_LOG_INFO, "Interlaced, top field first\n");
- t++;
+ type = TFF;
}else if(alpha[1] / (float)alpha[0] > idet->interlace_threshold){
- av_log(0, AV_LOG_INFO, "Interlaced, bottom field first\n");
- b++;
+ type = BFF;
}else if(alpha[1] / (float)delta > idet->progressive_threshold){
- av_log(0, AV_LOG_INFO, "Progressive\n");
- p++;
+ type = PROGRSSIVE;
}else{
- av_log(0, AV_LOG_INFO, "Undetermined\n");
- u++;
+ type = UNDETERMINED;
}
-// av_log(0,0, "t%d b%d p%d u%d\n", t,b,p,u);
-}
-static void return_frame(AVFilterContext *ctx)
-{
+ memmove(idet->history+1, idet->history, HIST_SIZE-1);
+ idet->history[0] = type;
+ best_type = UNDETERMINED;
+ for(i=0; i<HIST_SIZE; i++){
+ if(idet->history[i] != UNDETERMINED){
+ if(best_type == UNDETERMINED)
+ best_type = idet->history[i];
+
+ if(idet->history[i] == best_type) {
+ match++;
+ }else{
+ match=0;
+ break;
+ }
+ }
+ }
+ if(idet->last_type == UNDETERMINED){
+ if(match ) idet->last_type = best_type;
+ }else{
+ if(match>2) idet->last_type = best_type;
+ }
+
+ if (idet->last_type == TFF){
+ idet->cur->video->top_field_first = 1;
+ idet->cur->video->interlaced = 1;
+ }else if(idet->last_type == BFF){
+ idet->cur->video->top_field_first = 0;
+ idet->cur->video->interlaced = 1;
+ }else if(idet->last_type == PROGRSSIVE){
+ idet->cur->video->interlaced = 0;
+ }
+
+ idet->prestat [ type] ++;
+ idet->poststat[idet->last_type] ++;
+ av_log(ctx, AV_LOG_DEBUG, "Single frame:%s, Multi frame:%s\n", type2str(type), type2str(idet->last_type));
}
static void start_frame(AVFilterLink *link, AVFilterBufferRef *picref)
{
IDETContext *idet = ctx->priv;
+ av_log(ctx, AV_LOG_INFO, "Single frame detection: TFF:%d BFF:%d Progressive:%d Undetermined:%d\n",
+ idet->prestat[TFF],
+ idet->prestat[BFF],
+ idet->prestat[PROGRSSIVE],
+ idet->prestat[UNDETERMINED]
+ );
+ av_log(ctx, AV_LOG_INFO, "Multi frame detection: TFF:%d BFF:%d Progressive:%d Undetermined:%d\n",
+ idet->poststat[TFF],
+ idet->poststat[BFF],
+ idet->poststat[PROGRSSIVE],
+ idet->poststat[UNDETERMINED]
+ );
+
if (idet->prev) avfilter_unref_buffer(idet->prev);
if (idet->cur ) avfilter_unref_buffer(idet->cur );
if (idet->next) avfilter_unref_buffer(idet->next);
static av_cold int init(AVFilterContext *ctx, const char *args, void *opaque)
{
IDETContext *idet = ctx->priv;
- int cpu_flags = av_get_cpu_flags();
idet->csp = NULL;
if (args) sscanf(args, "%f:%f", &idet->interlace_threshold, &idet->progressive_threshold);
+ idet->last_type = UNDETERMINED;
+ memset(idet->history, UNDETERMINED, HIST_SIZE);
+
idet->filter_line = filter_line_c;
return 0;
.query_formats = query_formats,
.inputs = (const AVFilterPad[]) {{ .name = "default",
- .type = AVMEDIA_TYPE_VIDEO,
- .start_frame = start_frame,
- .draw_slice = null_draw_slice,
- .end_frame = end_frame,
- .rej_perms = AV_PERM_REUSE2, },
- { .name = NULL}},
+ .type = AVMEDIA_TYPE_VIDEO,
+ .start_frame = start_frame,
+ .draw_slice = null_draw_slice,
+ .end_frame = end_frame,
+ .rej_perms = AV_PERM_REUSE2, },
+ { .name = NULL}},
.outputs = (const AVFilterPad[]) {{ .name = "default",
- .type = AVMEDIA_TYPE_VIDEO,
- .poll_frame = poll_frame,
- .request_frame = request_frame, },
- { .name = NULL}},
+ .type = AVMEDIA_TYPE_VIDEO,
+ .poll_frame = poll_frame,
+ .request_frame = request_frame, },
+ { .name = NULL}},
};