You've already forked FFmpeg
							
							
				mirror of
				https://github.com/FFmpeg/FFmpeg.git
				synced 2025-10-30 23:18:11 +02:00 
			
		
		
		
	avfilter/vf_readeia608: add support for slice threads
This commit is contained in:
		| @@ -57,20 +57,28 @@ typedef struct CodeItem { | ||||
|     int size; | ||||
| } CodeItem; | ||||
|  | ||||
| typedef struct ReadEIA608Context { | ||||
|     const AVClass *class; | ||||
|     int start, end; | ||||
|     int nb_found; | ||||
| typedef struct ScanItem { | ||||
|     int nb_line; | ||||
|     int found; | ||||
|     int white; | ||||
|     int black; | ||||
|     uint64_t histogram[256]; | ||||
|     uint8_t byte[2]; | ||||
|  | ||||
|     CodeItem *code; | ||||
|     LineItem *line; | ||||
| } ScanItem; | ||||
|  | ||||
| typedef struct ReadEIA608Context { | ||||
|     const AVClass *class; | ||||
|  | ||||
|     int start, end; | ||||
|     float spw; | ||||
|     int chp; | ||||
|     int lp; | ||||
|  | ||||
|     uint64_t histogram[256]; | ||||
|  | ||||
|     CodeItem *code; | ||||
|     LineItem *line; | ||||
|     int nb_allocated; | ||||
|     ScanItem *scan; | ||||
| } ReadEIA608Context; | ||||
|  | ||||
| #define OFFSET(x) offsetof(ReadEIA608Context, x) | ||||
| @@ -105,53 +113,80 @@ static int query_formats(AVFilterContext *ctx) | ||||
|     return ff_set_common_formats(ctx, formats); | ||||
| } | ||||
|  | ||||
| static int config_input(AVFilterLink *inlink) | ||||
| static int config_filter(AVFilterContext *ctx, int start, int end) | ||||
| { | ||||
|     AVFilterContext *ctx = inlink->dst; | ||||
|     ReadEIA608Context *s = ctx->priv; | ||||
|     AVFilterLink *inlink = ctx->inputs[0]; | ||||
|     int size = inlink->w + LAG; | ||||
|  | ||||
|     if (s->end >= inlink->h) { | ||||
|     if (end >= inlink->h) { | ||||
|         av_log(ctx, AV_LOG_WARNING, "Last line to scan too large, clipping.\n"); | ||||
|         s->end = inlink->h - 1; | ||||
|         end = inlink->h - 1; | ||||
|     } | ||||
|  | ||||
|     if (s->start > s->end) { | ||||
|     if (start > end) { | ||||
|         av_log(ctx, AV_LOG_ERROR, "Invalid range.\n"); | ||||
|         return AVERROR(EINVAL); | ||||
|     } | ||||
|  | ||||
|     s->line = av_calloc(size, sizeof(*s->line)); | ||||
|     s->code = av_calloc(size, sizeof(*s->code)); | ||||
|     if (!s->line || !s->code) | ||||
|         return AVERROR(ENOMEM); | ||||
|     if (s->nb_allocated < end - start + 1) { | ||||
|         const int diff = end - start + 1 - s->nb_allocated; | ||||
|  | ||||
|         s->scan = av_realloc_f(s->scan, end - start + 1, sizeof(*s->scan)); | ||||
|         if (!s->scan) | ||||
|             return AVERROR(ENOMEM); | ||||
|         memset(&s->scan[s->nb_allocated], 0, diff * sizeof(*s->scan)); | ||||
|         s->nb_allocated = end - start + 1; | ||||
|     } | ||||
|  | ||||
|     for (int i = 0; i < s->nb_allocated; i++) { | ||||
|         ScanItem *scan = &s->scan[i]; | ||||
|  | ||||
|         if (!scan->line) | ||||
|             scan->line = av_calloc(size, sizeof(*scan->line)); | ||||
|         if (!scan->code) | ||||
|             scan->code = av_calloc(size, sizeof(*scan->code)); | ||||
|         if (!scan->line || !scan->code) | ||||
|             return AVERROR(ENOMEM); | ||||
|     } | ||||
|  | ||||
|     s->start = start; | ||||
|     s->end = end; | ||||
|  | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| static void build_histogram(ReadEIA608Context *s, const LineItem *line, int len) | ||||
| static int config_input(AVFilterLink *inlink) | ||||
| { | ||||
|     memset(s->histogram, 0, sizeof(s->histogram)); | ||||
|     AVFilterContext *ctx = inlink->dst; | ||||
|     ReadEIA608Context *s = ctx->priv; | ||||
|  | ||||
|     for (int i = LAG; i < len + LAG; i++) | ||||
|         s->histogram[line[i].input]++; | ||||
|     return config_filter(ctx, s->start, s->end); | ||||
| } | ||||
|  | ||||
| static void find_black_and_white(ReadEIA608Context *s) | ||||
| static void build_histogram(ReadEIA608Context *s, ScanItem *scan, const LineItem *line, int len) | ||||
| { | ||||
|     memset(scan->histogram, 0, sizeof(scan->histogram)); | ||||
|  | ||||
|     for (int i = LAG; i < len + LAG; i++) | ||||
|         scan->histogram[line[i].input]++; | ||||
| } | ||||
|  | ||||
| static void find_black_and_white(ReadEIA608Context *s, ScanItem *scan) | ||||
| { | ||||
|     int start = 0, end = 0, middle; | ||||
|     int black = 0, white = 0; | ||||
|     int cnt; | ||||
|  | ||||
|     for (int i = 0; i < 256; i++) { | ||||
|         if (s->histogram[i]) { | ||||
|         if (scan->histogram[i]) { | ||||
|             start = i; | ||||
|             break; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     for (int i = 255; i >= 0; i--) { | ||||
|         if (s->histogram[i]) { | ||||
|         if (scan->histogram[i]) { | ||||
|             end = i; | ||||
|             break; | ||||
|         } | ||||
| @@ -161,22 +196,22 @@ static void find_black_and_white(ReadEIA608Context *s) | ||||
|  | ||||
|     cnt = 0; | ||||
|     for (int i = start; i <= middle; i++) { | ||||
|         if (s->histogram[i] > cnt) { | ||||
|             cnt = s->histogram[i]; | ||||
|         if (scan->histogram[i] > cnt) { | ||||
|             cnt = scan->histogram[i]; | ||||
|             black = i; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     cnt = 0; | ||||
|     for (int i = end; i >= middle; i--) { | ||||
|         if (s->histogram[i] > cnt) { | ||||
|             cnt = s->histogram[i]; | ||||
|         if (scan->histogram[i] > cnt) { | ||||
|             cnt = scan->histogram[i]; | ||||
|             white = i; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     s->black = black; | ||||
|     s->white = white; | ||||
|     scan->black = black; | ||||
|     scan->white = white; | ||||
| } | ||||
|  | ||||
| static float meanf(const LineItem *line, int len) | ||||
| @@ -202,7 +237,7 @@ static float stddevf(const LineItem *line, int len) | ||||
|     return sqrtf(standard_deviation / (len - 1)); | ||||
| } | ||||
|  | ||||
| static void thresholding(ReadEIA608Context *s, LineItem *line, | ||||
| static void thresholding(ReadEIA608Context *s, ScanItem *scan, LineItem *line, | ||||
|                          int lag, float threshold, float influence, int len) | ||||
| { | ||||
|     for (int i = lag; i < len + lag; i++) { | ||||
| @@ -230,8 +265,8 @@ static void thresholding(ReadEIA608Context *s, LineItem *line, | ||||
|         } else { | ||||
|             int distance_from_black, distance_from_white; | ||||
|  | ||||
|             distance_from_black = FFABS(line[i].input - s->black); | ||||
|             distance_from_white = FFABS(line[i].input - s->white); | ||||
|             distance_from_black = FFABS(line[i].input - scan->black); | ||||
|             distance_from_white = FFABS(line[i].input - scan->white); | ||||
|  | ||||
|             line[i].output = distance_from_black <= distance_from_white ? 0 : 255; | ||||
|         } | ||||
| @@ -264,29 +299,28 @@ static int periods(const LineItem *line, CodeItem *code, int len) | ||||
|     return cnt + 1; | ||||
| } | ||||
|  | ||||
| static void dump_code(AVFilterContext *ctx, int len, int item) | ||||
| static void dump_code(AVFilterContext *ctx, ScanItem *scan, int len, int item) | ||||
| { | ||||
|     ReadEIA608Context *s = ctx->priv; | ||||
|  | ||||
|     av_log(ctx, AV_LOG_DEBUG, "%d:", item); | ||||
|     for (int i = 0; i < len; i++) { | ||||
|         av_log(ctx, AV_LOG_DEBUG, " %03d", s->code[i].size); | ||||
|         av_log(ctx, AV_LOG_DEBUG, " %03d", scan->code[i].size); | ||||
|     } | ||||
|     av_log(ctx, AV_LOG_DEBUG, "\n"); | ||||
| } | ||||
|  | ||||
| static void extract_line(AVFilterContext *ctx, AVFrame *in, int w, int nb_line) | ||||
| static void extract_line(AVFilterContext *ctx, AVFrame *in, ScanItem *scan, int w, int nb_line) | ||||
| { | ||||
|     ReadEIA608Context *s = ctx->priv; | ||||
|     LineItem *line = s->line; | ||||
|     LineItem *line = scan->line; | ||||
|     int i, j, ch, len; | ||||
|     const uint8_t *src; | ||||
|     uint8_t byte[2] = { 0 }; | ||||
|     uint8_t codes[19] = { 0 }; | ||||
|     float bit_size = 0.f; | ||||
|     int parity; | ||||
|  | ||||
|     memset(line, 0, (w + LAG) * sizeof(*line)); | ||||
|     scan->byte[0] = scan->byte[1] = 0; | ||||
|     scan->found = 0; | ||||
|  | ||||
|     src = &in->data[0][nb_line * in->linesize[0]]; | ||||
|     if (s->lp) { | ||||
| @@ -306,42 +340,42 @@ static void extract_line(AVFilterContext *ctx, AVFrame *in, int w, int nb_line) | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     build_histogram(s, line, w); | ||||
|     find_black_and_white(s); | ||||
|     if (s->white - s->black < 5) | ||||
|     build_histogram(s, scan, line, w); | ||||
|     find_black_and_white(s, scan); | ||||
|     if (scan->white - scan->black < 5) | ||||
|         return; | ||||
|  | ||||
|     thresholding(s, line, LAG, 1, 0, w); | ||||
|     len = periods(line, s->code, w); | ||||
|     dump_code(ctx, len, nb_line); | ||||
|     thresholding(s, scan, line, LAG, 1, 0, w); | ||||
|     len = periods(line, scan->code, w); | ||||
|     dump_code(ctx, scan, len, nb_line); | ||||
|     if (len < 15 || | ||||
|         s->code[14].bit != 0 || | ||||
|         w / (float)s->code[14].size < SYNC_BITSIZE_MIN || | ||||
|         w / (float)s->code[14].size > SYNC_BITSIZE_MAX) { | ||||
|         scan->code[14].bit != 0 || | ||||
|         w / (float)scan->code[14].size < SYNC_BITSIZE_MIN || | ||||
|         w / (float)scan->code[14].size > SYNC_BITSIZE_MAX) { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     for (i = 14; i < len; i++) { | ||||
|         bit_size += s->code[i].size; | ||||
|         bit_size += scan->code[i].size; | ||||
|     } | ||||
|  | ||||
|     bit_size /= 19.f; | ||||
|     for (i = 1; i < 14; i++) { | ||||
|         if (s->code[i].size / bit_size > CLOCK_BITSIZE_MAX || | ||||
|             s->code[i].size / bit_size < CLOCK_BITSIZE_MIN) { | ||||
|         if (scan->code[i].size / bit_size > CLOCK_BITSIZE_MAX || | ||||
|             scan->code[i].size / bit_size < CLOCK_BITSIZE_MIN) { | ||||
|             return; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     if (s->code[15].size / bit_size < 0.45f) { | ||||
|     if (scan->code[15].size / bit_size < 0.45f) { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     for (j = 0, i = 14; i < len; i++) { | ||||
|         int run, bit; | ||||
|  | ||||
|         run = lrintf(s->code[i].size / bit_size); | ||||
|         bit = s->code[i].bit; | ||||
|         run = lrintf(scan->code[i].size / bit_size); | ||||
|         bit = scan->code[i].bit; | ||||
|  | ||||
|         for (int k = 0; j < 19 && k < run; k++) { | ||||
|             codes[j++] = bit; | ||||
| @@ -361,33 +395,37 @@ static void extract_line(AVFilterContext *ctx, AVFrame *in, int w, int nb_line) | ||||
|             } else { | ||||
|                 b = 0; | ||||
|             } | ||||
|             byte[ch] |= b << i; | ||||
|             scan->byte[ch] |= b << i; | ||||
|         } | ||||
|  | ||||
|         if (s->chp) { | ||||
|             if (!(parity & 1)) { | ||||
|                 byte[ch] = 0x7F; | ||||
|                 scan->byte[ch] = 0x7F; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     { | ||||
|         uint8_t key[128], value[128]; | ||||
|     scan->nb_line = nb_line; | ||||
|     scan->found = 1; | ||||
| } | ||||
|  | ||||
|         //snprintf(key, sizeof(key), "lavfi.readeia608.%d.bits", s->nb_found); | ||||
|         //snprintf(value, sizeof(value), "0b%d%d%d%d%d%d%d%d 0b%d%d%d%d%d%d%d%d", codes[3]==255,codes[4]==255,codes[5]==255,codes[6]==255,codes[7]==255,codes[8]==255,codes[9]==255,codes[10]==255,codes[11]==255,codes[12]==255,codes[13]==255,codes[14]==255,codes[15]==255,codes[16]==255,codes[17]==255,codes[18]==255); | ||||
|         //av_dict_set(&in->metadata, key, value, 0); | ||||
| static int extract_lines(AVFilterContext *ctx, void *arg, | ||||
|                          int job, int nb_jobs) | ||||
| { | ||||
|     ReadEIA608Context *s = ctx->priv; | ||||
|     AVFilterLink *inlink = ctx->inputs[0]; | ||||
|     const int h = s->end - s->start + 1; | ||||
|     const int start = (h * job) / nb_jobs; | ||||
|     const int end   = (h * (job+1)) / nb_jobs; | ||||
|     AVFrame *in = arg; | ||||
|  | ||||
|         snprintf(key, sizeof(key), "lavfi.readeia608.%d.cc", s->nb_found); | ||||
|         snprintf(value, sizeof(value), "0x%02X%02X", byte[0], byte[1]); | ||||
|         av_dict_set(&in->metadata, key, value, 0); | ||||
|     for (int i = start; i < end; i++) { | ||||
|         ScanItem *scan = &s->scan[i]; | ||||
|  | ||||
|         snprintf(key, sizeof(key), "lavfi.readeia608.%d.line", s->nb_found); | ||||
|         snprintf(value, sizeof(value), "%d", nb_line); | ||||
|         av_dict_set(&in->metadata, key, value, 0); | ||||
|         extract_line(ctx, in, scan, inlink->w, i); | ||||
|     } | ||||
|  | ||||
|     s->nb_found++; | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| static int filter_frame(AVFilterLink *inlink, AVFrame *in) | ||||
| @@ -395,11 +433,33 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in) | ||||
|     AVFilterContext *ctx  = inlink->dst; | ||||
|     AVFilterLink *outlink = ctx->outputs[0]; | ||||
|     ReadEIA608Context *s = ctx->priv; | ||||
|     int i; | ||||
|     int nb_found; | ||||
|  | ||||
|     s->nb_found = 0; | ||||
|     for (i = s->start; i <= s->end; i++) | ||||
|         extract_line(ctx, in, inlink->w, i); | ||||
|     ctx->internal->execute(ctx, extract_lines, in, NULL, FFMIN(FFMAX(s->end - s->start + 1, 1), | ||||
|                                                                ff_filter_get_nb_threads(ctx))); | ||||
|  | ||||
|     nb_found = 0; | ||||
|     for (int i = 0; i < s->end - s->start + 1; i++) { | ||||
|         ScanItem *scan = &s->scan[i]; | ||||
|         uint8_t key[128], value[128]; | ||||
|  | ||||
|         if (!scan->found) | ||||
|             continue; | ||||
|  | ||||
|         //snprintf(key, sizeof(key), "lavfi.readeia608.%d.bits", nb_found); | ||||
|         //snprintf(value, sizeof(value), "0b%d%d%d%d%d%d%d%d 0b%d%d%d%d%d%d%d%d", codes[3]==255,codes[4]==255,codes[5]==255,codes[6]==255,codes[7]==255,codes[8]==255,codes[9]==255,codes[10]==255,codes[11]==255,codes[12]==255,codes[13]==255,codes[14]==255,codes[15]==255,codes[16]==255,codes[17]==255,codes[18]==255); | ||||
|         //av_dict_set(&in->metadata, key, value, 0); | ||||
|  | ||||
|         snprintf(key, sizeof(key), "lavfi.readeia608.%d.cc", nb_found); | ||||
|         snprintf(value, sizeof(value), "0x%02X%02X", scan->byte[0], scan->byte[1]); | ||||
|         av_dict_set(&in->metadata, key, value, 0); | ||||
|  | ||||
|         snprintf(key, sizeof(key), "lavfi.readeia608.%d.line", nb_found); | ||||
|         snprintf(value, sizeof(value), "%d", scan->nb_line); | ||||
|         av_dict_set(&in->metadata, key, value, 0); | ||||
|  | ||||
|         nb_found++; | ||||
|     } | ||||
|  | ||||
|     return ff_filter_frame(outlink, in); | ||||
| } | ||||
| @@ -408,8 +468,34 @@ static av_cold void uninit(AVFilterContext *ctx) | ||||
| { | ||||
|     ReadEIA608Context *s = ctx->priv; | ||||
|  | ||||
|     av_freep(&s->code); | ||||
|     av_freep(&s->line); | ||||
|     for (int i = 0; i < s->nb_allocated; i++) { | ||||
|         ScanItem *scan = &s->scan[i]; | ||||
|  | ||||
|         av_freep(&scan->code); | ||||
|         av_freep(&scan->line); | ||||
|     } | ||||
|  | ||||
|     s->nb_allocated = 0; | ||||
|     av_freep(&s->scan); | ||||
| } | ||||
|  | ||||
| static int process_command(AVFilterContext *ctx, const char *cmd, const char *args, | ||||
|                            char *res, int res_len, int flags) | ||||
| { | ||||
|     ReadEIA608Context *s = ctx->priv; | ||||
|     int ret, start = s->start, end = s->end; | ||||
|  | ||||
|     ret = ff_filter_process_command(ctx, cmd, args, res, res_len, flags); | ||||
|     if (ret < 0) | ||||
|         return ret; | ||||
|  | ||||
|     ret = config_filter(ctx, s->start, s->end); | ||||
|     if (ret < 0) { | ||||
|         s->start = start; | ||||
|         s->end = end; | ||||
|     } | ||||
|  | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| static const AVFilterPad readeia608_inputs[] = { | ||||
| @@ -439,6 +525,6 @@ AVFilter ff_vf_readeia608 = { | ||||
|     .inputs        = readeia608_inputs, | ||||
|     .outputs       = readeia608_outputs, | ||||
|     .uninit        = uninit, | ||||
|     .flags         = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC, | ||||
|     .process_command = ff_filter_process_command, | ||||
|     .flags         = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS, | ||||
|     .process_command = process_command, | ||||
| }; | ||||
|   | ||||
		Reference in New Issue
	
	Block a user