LCOV - code coverage report
Current view: top level - libavfilter - vf_readeia608.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 0 98 0.0 %
Date: 2017-12-16 21:16:39 Functions: 0 4 0.0 %

          Line data    Source code
       1             : /*
       2             :  * Copyright (c) 2017 Paul B Mahol
       3             :  *
       4             :  * This file is part of FFmpeg.
       5             :  *
       6             :  * FFmpeg is free software; you can redistribute it and/or
       7             :  * modify it under the terms of the GNU Lesser General Public
       8             :  * License as published by the Free Software Foundation; either
       9             :  * version 2.1 of the License, or (at your option) any later version.
      10             :  *
      11             :  * FFmpeg is distributed in the hope that it will be useful,
      12             :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      13             :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      14             :  * Lesser General Public License for more details.
      15             :  *
      16             :  * You should have received a copy of the GNU Lesser General Public
      17             :  * License along with FFmpeg; if not, write to the Free Software
      18             :  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
      19             :  */
      20             : 
      21             : /**
      22             :  * @file
      23             :  * Filter for reading closed captioning data (EIA-608).
      24             :  * See also https://en.wikipedia.org/wiki/EIA-608
      25             :  */
      26             : 
      27             : #include <string.h>
      28             : 
      29             : #include "libavutil/internal.h"
      30             : #include "libavutil/opt.h"
      31             : #include "libavutil/pixdesc.h"
      32             : #include "libavutil/timestamp.h"
      33             : 
      34             : #include "avfilter.h"
      35             : #include "formats.h"
      36             : #include "internal.h"
      37             : #include "video.h"
      38             : 
      39             : #define FALL 0
      40             : #define RISE 1
      41             : 
      42             : typedef struct ReadEIA608Context {
      43             :     const AVClass *class;
      44             :     int start, end;
      45             :     int min_range;
      46             :     int max_peak_diff;
      47             :     int max_period_diff;
      48             :     int max_start_diff;
      49             :     int nb_found;
      50             :     int white;
      51             :     int black;
      52             :     float mpd, mhd, msd, mac, spw, bhd, wth, bth;
      53             :     int chp;
      54             : } ReadEIA608Context;
      55             : 
      56             : #define OFFSET(x) offsetof(ReadEIA608Context, x)
      57             : #define FLAGS AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_FILTERING_PARAM
      58             : 
      59             : static const AVOption readeia608_options[] = {
      60             :     { "scan_min", "set from which line to scan for codes",                            OFFSET(start), AV_OPT_TYPE_INT,   {.i64=0},       0, INT_MAX, FLAGS },
      61             :     { "scan_max", "set to which line to scan for codes",                              OFFSET(end),   AV_OPT_TYPE_INT,   {.i64=29},      0, INT_MAX, FLAGS },
      62             :     { "mac",      "set minimal acceptable amplitude change for sync codes detection", OFFSET(mac),   AV_OPT_TYPE_FLOAT, {.dbl=.2},  0.001,       1, FLAGS },
      63             :     { "spw",      "set ratio of width reserved for sync code detection",              OFFSET(spw),   AV_OPT_TYPE_FLOAT, {.dbl=.27},   0.1,     0.7, FLAGS },
      64             :     { "mhd",      "set max peaks height difference for sync code detection",          OFFSET(mhd),   AV_OPT_TYPE_FLOAT, {.dbl=.1},      0,     0.5, FLAGS },
      65             :     { "mpd",      "set max peaks period difference for sync code detection",          OFFSET(mpd),   AV_OPT_TYPE_FLOAT, {.dbl=.1},      0,     0.5, FLAGS },
      66             :     { "msd",      "set first two max start code bits differences",                    OFFSET(msd),   AV_OPT_TYPE_FLOAT, {.dbl=.02},     0,     0.5, FLAGS },
      67             :     { "bhd",      "set min ratio of bits height compared to 3rd start code bit",      OFFSET(bhd),   AV_OPT_TYPE_FLOAT, {.dbl=.75},  0.01,       1, FLAGS },
      68             :     { "th_w",     "set white color threshold",                                        OFFSET(wth),   AV_OPT_TYPE_FLOAT, {.dbl=.35},   0.1,       1, FLAGS },
      69             :     { "th_b",     "set black color threshold",                                        OFFSET(bth),   AV_OPT_TYPE_FLOAT, {.dbl=.15},     0,     0.5, FLAGS },
      70             :     { "chp",      "check and apply parity bit",                                       OFFSET(chp),   AV_OPT_TYPE_BOOL,  {.i64= 0},      0,       1, FLAGS },
      71             :     { NULL }
      72             : };
      73             : 
      74             : AVFILTER_DEFINE_CLASS(readeia608);
      75             : 
      76           0 : static int query_formats(AVFilterContext *ctx)
      77             : {
      78             :     static const enum AVPixelFormat pixel_fmts[] = {
      79             :         AV_PIX_FMT_GRAY8,
      80             :         AV_PIX_FMT_YUV410P, AV_PIX_FMT_YUV411P,
      81             :         AV_PIX_FMT_YUV420P, AV_PIX_FMT_YUV422P,
      82             :         AV_PIX_FMT_YUV440P, AV_PIX_FMT_YUV444P,
      83             :         AV_PIX_FMT_YUVJ420P, AV_PIX_FMT_YUVJ422P,
      84             :         AV_PIX_FMT_YUVJ440P, AV_PIX_FMT_YUVJ444P,
      85             :         AV_PIX_FMT_YUVJ411P,
      86             :         AV_PIX_FMT_NONE
      87             :     };
      88           0 :     AVFilterFormats *formats = ff_make_format_list(pixel_fmts);
      89           0 :     if (!formats)
      90           0 :         return AVERROR(ENOMEM);
      91           0 :     return ff_set_common_formats(ctx, formats);
      92             : }
      93             : 
      94           0 : static int config_input(AVFilterLink *inlink)
      95             : {
      96           0 :     const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(inlink->format);
      97           0 :     AVFilterContext *ctx = inlink->dst;
      98           0 :     ReadEIA608Context *s = ctx->priv;
      99           0 :     int depth = desc->comp[0].depth;
     100             : 
     101           0 :     if (s->end >= inlink->h) {
     102           0 :         av_log(ctx, AV_LOG_WARNING, "Last line to scan too large, clipping.\n");
     103           0 :         s->end = inlink->h - 1;
     104             :     }
     105             : 
     106           0 :     if (s->start > s->end) {
     107           0 :         av_log(ctx, AV_LOG_ERROR, "Invalid range.\n");
     108           0 :         return AVERROR(EINVAL);
     109             :     }
     110             : 
     111           0 :     s->min_range = s->mac * ((1 << depth) - 1);
     112           0 :     s->max_peak_diff = s->mhd * ((1 << depth) - 1);
     113           0 :     s->max_period_diff = s->mpd * ((1 << depth) - 1);
     114           0 :     s->max_start_diff = s->msd * ((1 << depth) - 1);
     115           0 :     s->white = s->wth * ((1 << depth) - 1);
     116           0 :     s->black = s->bth * ((1 << depth) - 1);
     117             : 
     118           0 :     return 0;
     119             : }
     120             : 
     121           0 : static void extract_line(AVFilterContext *ctx, AVFilterLink *inlink, AVFrame *in, int line)
     122             : {
     123           0 :     ReadEIA608Context *s = ctx->priv;
     124           0 :     int max = 0, min = INT_MAX;
     125           0 :     int i, ch, range = 0;
     126             :     const uint8_t *src;
     127           0 :     uint16_t clock[8][2] = { { 0 } };
     128           0 :     const int sync_width = s->spw * in->width;
     129           0 :     int last = 0, peaks = 0, max_peak_diff = 0, dir = RISE;
     130           0 :     const int width_per_bit = (in->width - sync_width) / 19;
     131           0 :     uint8_t byte[2] = { 0 };
     132             :     int s1, s2, s3, parity;
     133             : 
     134           0 :     src = &in->data[0][line * in->linesize[0]];
     135           0 :     for (i = 0; i < sync_width; i++) {
     136           0 :         max = FFMAX(max, src[i]);
     137           0 :         min = FFMIN(min, src[i]);
     138             :     }
     139             : 
     140           0 :     range = max - min;
     141           0 :     if (range < s->min_range)
     142           0 :         return;
     143             : 
     144           0 :     for (i = 0; i < sync_width; i++) {
     145           0 :         int Y = src[i];
     146             : 
     147           0 :         if (dir == RISE) {
     148           0 :             if (Y < last) {
     149           0 :                 dir = FALL;
     150           0 :                 if (last >= s->white) {
     151           0 :                     clock[peaks][0] = last;
     152           0 :                     clock[peaks][1] = i;
     153           0 :                     peaks++;
     154           0 :                     if (peaks > 7)
     155           0 :                         break;
     156             :                 }
     157             :             }
     158           0 :         } else if (dir == FALL) {
     159           0 :             if (Y > last && last <= s->black) {
     160           0 :                 dir = RISE;
     161             :             }
     162             :         }
     163           0 :         last = Y;
     164             :     }
     165             : 
     166           0 :     if (peaks != 7)
     167           0 :         return;
     168             : 
     169           0 :     for (i = 1; i < 7; i++)
     170           0 :         max_peak_diff = FFMAX(max_peak_diff, FFABS(clock[i][0] - clock[i-1][0]));
     171             : 
     172           0 :     if (max_peak_diff > s->max_peak_diff)
     173           0 :         return;
     174             : 
     175           0 :     max = 0; min = INT_MAX;
     176           0 :     for (i = 1; i < 7; i++) {
     177           0 :         max = FFMAX(max, FFABS(clock[i][1] - clock[i-1][1]));
     178           0 :         min = FFMIN(min, FFABS(clock[i][1] - clock[i-1][1]));
     179             :     }
     180             : 
     181           0 :     range = max - min;
     182           0 :     if (range > s->max_period_diff)
     183           0 :         return;
     184             : 
     185           0 :     s1 = src[sync_width + width_per_bit * 0 + width_per_bit / 2];
     186           0 :     s2 = src[sync_width + width_per_bit * 1 + width_per_bit / 2];
     187           0 :     s3 = src[sync_width + width_per_bit * 2 + width_per_bit / 2];
     188             : 
     189           0 :     if (FFABS(s1 - s2) > s->max_start_diff || s1 > s->black || s2 > s->black || s3 < s->white)
     190           0 :         return;
     191             : 
     192           0 :     for (ch = 0; ch < 2; ch++) {
     193           0 :         for (parity = 0, i = 0; i < 8; i++) {
     194           0 :             int b = src[sync_width + width_per_bit * (i + 3 + 8 * ch) + width_per_bit / 2];
     195             : 
     196           0 :             if (b - s1 > (s3 - s1) * s->bhd) {
     197           0 :                 b = 1;
     198           0 :                 parity++;
     199             :             } else {
     200           0 :                 b = 0;
     201             :             }
     202           0 :             byte[ch] |= b << i;
     203             :         }
     204             : 
     205           0 :         if (s->chp) {
     206           0 :             if (!(parity & 1)) {
     207           0 :                 byte[ch] = 0;
     208             :             }
     209             :         }
     210             :     }
     211             : 
     212             :     {
     213             :         uint8_t key[128], value[128];
     214             : 
     215           0 :         snprintf(key, sizeof(key), "lavfi.readeia608.%d.cc", s->nb_found);
     216           0 :         snprintf(value, sizeof(value), "0x%02X%02X", byte[0], byte[1]);
     217           0 :         av_dict_set(&in->metadata, key, value, 0);
     218             : 
     219           0 :         snprintf(key, sizeof(key), "lavfi.readeia608.%d.line", s->nb_found);
     220           0 :         snprintf(value, sizeof(value), "%d", line);
     221           0 :         av_dict_set(&in->metadata, key, value, 0);
     222             :     }
     223             : 
     224           0 :     s->nb_found++;
     225             : }
     226             : 
     227           0 : static int filter_frame(AVFilterLink *inlink, AVFrame *in)
     228             : {
     229           0 :     AVFilterContext *ctx  = inlink->dst;
     230           0 :     AVFilterLink *outlink = ctx->outputs[0];
     231           0 :     ReadEIA608Context *s = ctx->priv;
     232             :     int i;
     233             : 
     234           0 :     s->nb_found = 0;
     235           0 :     for (i = s->start; i <= s->end; i++)
     236           0 :         extract_line(ctx, inlink, in, i);
     237             : 
     238           0 :     return ff_filter_frame(outlink, in);
     239             : }
     240             : 
     241             : static const AVFilterPad readeia608_inputs[] = {
     242             :     {
     243             :         .name         = "default",
     244             :         .type         = AVMEDIA_TYPE_VIDEO,
     245             :         .filter_frame = filter_frame,
     246             :         .config_props = config_input,
     247             :     },
     248             :     { NULL }
     249             : };
     250             : 
     251             : static const AVFilterPad readeia608_outputs[] = {
     252             :     {
     253             :         .name = "default",
     254             :         .type = AVMEDIA_TYPE_VIDEO,
     255             :     },
     256             :     { NULL }
     257             : };
     258             : 
     259             : AVFilter ff_vf_readeia608 = {
     260             :     .name          = "readeia608",
     261             :     .description   = NULL_IF_CONFIG_SMALL("Read EIA-608 Closed Caption codes from input video and write them to frame metadata."),
     262             :     .priv_size     = sizeof(ReadEIA608Context),
     263             :     .priv_class    = &readeia608_class,
     264             :     .query_formats = query_formats,
     265             :     .inputs        = readeia608_inputs,
     266             :     .outputs       = readeia608_outputs,
     267             :     .flags         = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC,
     268             : };

Generated by: LCOV version 1.13