LCOV - code coverage report
Current view: top level - libavfilter - vf_readvitc.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 88 94 93.6 %
Date: 2017-12-15 11:05:35 Functions: 9 9 100.0 %

          Line data    Source code
       1             : /*
       2             :  * Copyright (c) 2016 Tobias Rapp
       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 the vertical interval timecode (VITC).
      24             :  * See also https://en.wikipedia.org/wiki/Vertical_interval_timecode
      25             :  */
      26             : 
      27             : #include "libavutil/common.h"
      28             : #include "libavutil/internal.h"
      29             : #include "libavutil/opt.h"
      30             : #include "libavutil/pixdesc.h"
      31             : #include "libavutil/timecode.h"
      32             : #include "avfilter.h"
      33             : #include "formats.h"
      34             : #include "internal.h"
      35             : 
      36             : #define LINE_DATA_SIZE 9
      37             : 
      38             : typedef struct ReadVitcContext {
      39             :     const AVClass *class;
      40             : 
      41             :     int scan_max;
      42             :     double thr_b;
      43             :     double thr_w;
      44             : 
      45             :     int threshold_black;
      46             :     int threshold_white;
      47             :     int threshold_gray;
      48             :     int grp_width;
      49             :     uint8_t line_data[LINE_DATA_SIZE];
      50             :     char tcbuf[AV_TIMECODE_STR_SIZE];
      51             : } ReadVitcContext;
      52             : 
      53             : #define OFFSET(x) offsetof(ReadVitcContext, x)
      54             : #define FLAGS AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_FILTERING_PARAM
      55             : 
      56             : static const AVOption readvitc_options[] = {
      57             :     { "scan_max", "maximum line numbers to scan for VITC data", OFFSET(scan_max), AV_OPT_TYPE_INT, {.i64 = 45 }, -1, INT_MAX, FLAGS },
      58             :     { "thr_b",    "black color threshold", OFFSET(thr_b), AV_OPT_TYPE_DOUBLE, {.dbl = 0.2 }, 0, 1.0, FLAGS },
      59             :     { "thr_w",    "white color threshold", OFFSET(thr_w), AV_OPT_TYPE_DOUBLE, {.dbl = 0.6 }, 0, 1.0, FLAGS },
      60             :     { NULL }
      61             : };
      62             : 
      63             : AVFILTER_DEFINE_CLASS(readvitc);
      64             : 
      65           8 : static uint8_t get_vitc_crc( uint8_t *line ) {
      66             :     uint8_t crc;
      67             : 
      68           8 :     crc = 0x01 | (line[0] << 2);
      69           8 :     crc ^= (line[0] >> 6) | 0x04 | (line[1] << 4);
      70           8 :     crc ^= (line[1] >> 4) | 0x10 | (line[2] << 6);
      71           8 :     crc ^= (line[2] >> 2) | 0x40;
      72           8 :     crc ^= line[3];
      73           8 :     crc ^= 0x01 | (line[4] << 2);
      74           8 :     crc ^= (line[4] >> 6) | 0x04 | (line[5] << 4);
      75           8 :     crc ^= (line[5] >> 4) | 0x10 | (line[6] << 6);
      76           8 :     crc ^= (line[6] >> 2) | 0x40;
      77           8 :     crc ^= line[7];
      78           8 :     crc ^= 0x01;
      79           8 :     crc = (crc >> 2) | (crc << 6);  // rotate byte right by two bits
      80           8 :     return crc;
      81             : }
      82             : 
      83         720 : static inline uint8_t get_pit_avg3( uint8_t *line, int i ) {
      84         720 :     return ((line[i-1] + line[i] + line[i+1]) / 3);
      85             : }
      86             : 
      87          10 : static int read_vitc_line( ReadVitcContext *ctx, uint8_t *src, int line_size, int width, int height )
      88             : {
      89             :     uint8_t *scan_line;
      90             :     int grp_index, pit_index;
      91             :     int grp_start_pos;
      92             :     uint8_t pit_value;
      93          10 :     int x, y, res = 0;
      94             : 
      95          10 :     if (ctx->scan_max >= 0)
      96          10 :         height = FFMIN(height, ctx->scan_max);
      97             : 
      98             :     // scan lines for VITC data, starting from the top
      99         292 :     for (y = 0; y < height; y++) {
     100         290 :         scan_line = src;
     101         290 :         memset(ctx->line_data, 0, LINE_DATA_SIZE);
     102         290 :         grp_index = 0;
     103         290 :         x = 0;
     104         652 :         while ((x < width) && (grp_index < 9)) {
     105             :             // search next sync pattern
     106      200729 :             while ((x < width) && (scan_line[x] < ctx->threshold_white))
     107      200021 :                 x++;
     108        4746 :             while ((x < width) && (scan_line[x] > ctx->threshold_black))
     109        4038 :                 x++;
     110         354 :             x = FFMAX(x - ((ctx->grp_width+10) / 20), 1);  // step back a half pit
     111         354 :             grp_start_pos = x;
     112         354 :             if ((grp_start_pos + ctx->grp_width) > width)
     113         282 :                 break;  // not enough pixels for reading a whole pit group
     114          72 :             pit_value = get_pit_avg3(scan_line, x);
     115          72 :             if (pit_value < ctx->threshold_white)
     116           0 :                break;  // first sync bit mismatch
     117          72 :             x = grp_start_pos + ((ctx->grp_width) / 10);
     118          72 :             pit_value = get_pit_avg3(scan_line, x);
     119          72 :             if (pit_value > ctx->threshold_black )
     120           0 :                 break;  // second sync bit mismatch
     121         648 :             for (pit_index = 0; pit_index <= 7; pit_index++) {
     122         576 :                 x = grp_start_pos + (((pit_index+2)*ctx->grp_width) / 10);
     123         576 :                 pit_value = get_pit_avg3(scan_line, x);
     124         576 :                 if (pit_value > ctx->threshold_gray)
     125         146 :                     ctx->line_data[grp_index] |= (1 << pit_index);
     126             :             }
     127          72 :             grp_index++;
     128             :         }
     129         290 :         if ((grp_index == 9) && (get_vitc_crc(ctx->line_data) == ctx->line_data[8])) {
     130           8 :             res = 1;
     131           8 :             break;
     132             :         }
     133         282 :         src += line_size;
     134             :     }
     135             : 
     136          10 :     return res;
     137             : }
     138             : 
     139          32 : static unsigned bcd2uint(uint8_t high, uint8_t low)
     140             : {
     141          32 :    if (high > 9 || low > 9)
     142           0 :        return 0;
     143          32 :    return 10*high + low;
     144             : }
     145             : 
     146           8 : static char *make_vitc_tc_string(char *buf, uint8_t *line)
     147             : {
     148           8 :     unsigned hh   = bcd2uint(line[7] & 0x03, line[6] & 0x0f);  // 6-bit hours
     149           8 :     unsigned mm   = bcd2uint(line[5] & 0x07, line[4] & 0x0f);  // 7-bit minutes
     150           8 :     unsigned ss   = bcd2uint(line[3] & 0x07, line[2] & 0x0f);  // 7-bit seconds
     151           8 :     unsigned ff   = bcd2uint(line[1] & 0x03, line[0] & 0x0f);  // 6-bit frames
     152           8 :     unsigned drop = (line[1] & 0x04);                          // 1-bit drop flag
     153           8 :     snprintf(buf, AV_TIMECODE_STR_SIZE, "%02u:%02u:%02u%c%02u",
     154             :              hh, mm, ss, drop ? ';' : ':', ff);
     155           8 :     return buf;
     156             : }
     157             : 
     158           2 : static av_cold int init(AVFilterContext *ctx)
     159             : {
     160           2 :     ReadVitcContext *s = ctx->priv;
     161             : 
     162           2 :     s->threshold_black = s->thr_b * UINT8_MAX;
     163           2 :     s->threshold_white = s->thr_w * UINT8_MAX;
     164           2 :     if (s->threshold_black > s->threshold_white) {
     165           0 :         av_log(ctx, AV_LOG_WARNING, "Black color threshold is higher than white color threshold (%g > %g)\n",
     166             :                 s->thr_b, s->thr_w);
     167           0 :         return AVERROR(EINVAL);
     168             :     }
     169           2 :     s->threshold_gray = s->threshold_white - ((s->threshold_white - s->threshold_black) / 2);
     170           2 :     av_log(ctx, AV_LOG_DEBUG, "threshold_black:%d threshold_white:%d threshold_gray:%d\n",
     171             :             s->threshold_black, s->threshold_white, s->threshold_gray);
     172             : 
     173           2 :     return 0;
     174             : }
     175             : 
     176           2 : static int config_props(AVFilterLink *inlink)
     177             : {
     178           2 :     AVFilterContext *ctx = inlink->dst;
     179           2 :     ReadVitcContext *s = ctx->priv;
     180             : 
     181           2 :     s->grp_width = inlink->w * 5 / 48;
     182           2 :     av_log(ctx, AV_LOG_DEBUG, "w:%d h:%d grp_width:%d scan_max:%d\n",
     183             :             inlink->w, inlink->h, s->grp_width, s->scan_max);
     184           2 :     return 0;
     185             : }
     186             : 
     187           2 : static int query_formats(AVFilterContext *ctx)
     188             : {
     189             :     static const enum AVPixelFormat pixel_fmts[] = {
     190             :         AV_PIX_FMT_GRAY8,
     191             :         AV_PIX_FMT_NV12,
     192             :         AV_PIX_FMT_NV16,
     193             :         AV_PIX_FMT_NV21,
     194             :         AV_PIX_FMT_YUV410P,
     195             :         AV_PIX_FMT_YUV411P,
     196             :         AV_PIX_FMT_YUV420P,
     197             :         AV_PIX_FMT_YUV422P,
     198             :         AV_PIX_FMT_YUV440P,
     199             :         AV_PIX_FMT_YUV444P,
     200             :         AV_PIX_FMT_YUVA420P,
     201             :         AV_PIX_FMT_YUVA422P,
     202             :         AV_PIX_FMT_YUVA444P,
     203             :         AV_PIX_FMT_YUVJ411P,
     204             :         AV_PIX_FMT_YUVJ420P,
     205             :         AV_PIX_FMT_YUVJ422P,
     206             :         AV_PIX_FMT_YUVJ440P,
     207             :         AV_PIX_FMT_YUVJ444P,
     208             :         AV_PIX_FMT_NONE
     209             :     };
     210           2 :     AVFilterFormats *fmts_list = ff_make_format_list(pixel_fmts);
     211           2 :     if (!fmts_list)
     212           0 :         return AVERROR(ENOMEM);
     213           2 :     return ff_set_common_formats(ctx, fmts_list);
     214             : }
     215             : 
     216          10 : static int filter_frame(AVFilterLink *inlink, AVFrame *frame)
     217             : {
     218          10 :     AVFilterContext *ctx = inlink->dst;
     219          10 :     AVFilterLink *outlink = ctx->outputs[0];
     220          10 :     ReadVitcContext *s = ctx->priv;
     221             :     int found;
     222             : 
     223          10 :     found = read_vitc_line(s, frame->data[0], frame->linesize[0], inlink->w, inlink->h);
     224          10 :     av_dict_set(&frame->metadata, "lavfi.readvitc.found", (found ? "1" : "0"), 0);
     225          10 :     if (found)
     226           8 :         av_dict_set(&frame->metadata, "lavfi.readvitc.tc_str", make_vitc_tc_string(s->tcbuf, s->line_data), 0);
     227             : 
     228          10 :     return ff_filter_frame(outlink, frame);
     229             : }
     230             : 
     231             : static const AVFilterPad inputs[] = {
     232             :     {
     233             :         .name         = "default",
     234             :         .type         = AVMEDIA_TYPE_VIDEO,
     235             :         .filter_frame = filter_frame,
     236             :         .config_props = config_props,
     237             :     },
     238             :     { NULL }
     239             : };
     240             : 
     241             : static const AVFilterPad outputs[] = {
     242             :     {
     243             :         .name = "default",
     244             :         .type = AVMEDIA_TYPE_VIDEO,
     245             :     },
     246             :     { NULL }
     247             : };
     248             : 
     249             : AVFilter ff_vf_readvitc = {
     250             :     .name          = "readvitc",
     251             :     .description   = NULL_IF_CONFIG_SMALL("Read vertical interval timecode and write it to frame metadata."),
     252             :     .priv_size     = sizeof(ReadVitcContext),
     253             :     .priv_class    = &readvitc_class,
     254             :     .inputs        = inputs,
     255             :     .outputs       = outputs,
     256             :     .init          = init,
     257             :     .query_formats = query_formats,
     258             : };

Generated by: LCOV version 1.13