From 4784b34c4a2b297f7bb0eb4805f55ac713204893 Mon Sep 17 00:00:00 2001
From: rogerdpack <rogerpack2005@gmail.com>
Date: Wed, 5 Apr 2023 14:51:18 -0600
Subject: [PATCH 1/2] Closed caption: add ability to read EIA 608 byte pairs,
 useful for analog capture devices

---
 configure                 |   1 +
 libavcodec/Makefile       |   1 +
 libavcodec/allcodecs.c    |   1 +
 libavcodec/ccaption_dec.c | 107 +++++++++++++++++++++++++++-----------
 libavcodec/codec_desc.c   |   7 +++
 libavcodec/codec_id.h     |   1 +
 libavcodec/version.h      |   2 +-
 7 files changed, 90 insertions(+), 30 deletions(-)

diff --git a/configure b/configure
index ed9efad985..9eb86102c7 100755
--- a/configure
+++ b/configure
@@ -2832,6 +2832,7 @@ bink_decoder_select="blockdsp hpeldsp"
 binkaudio_dct_decoder_select="wma_freqs"
 binkaudio_rdft_decoder_select="wma_freqs"
 cavs_decoder_select="blockdsp golomb h264chroma idctdsp qpeldsp videodsp"
+ccaption_eia_608_decoder_select="ccaption_decoder"
 clearvideo_decoder_select="idctdsp"
 cllc_decoder_select="bswapdsp"
 comfortnoise_encoder_select="lpc"
diff --git a/libavcodec/Makefile b/libavcodec/Makefile
index 723bfa25c7..e81110d5d9 100644
--- a/libavcodec/Makefile
+++ b/libavcodec/Makefile
@@ -284,6 +284,7 @@ OBJS-$(CONFIG_CAVS_DECODER)            += cavs.o cavsdec.o cavsdsp.o \
                                           cavsdata.o
 OBJS-$(CONFIG_CBD2_DECODER)            += dpcm.o
 OBJS-$(CONFIG_CCAPTION_DECODER)        += ccaption_dec.o ass.o
+OBJS-$(CONFIG_CCAPTION_EIA_608_DECODER) += ccaption_dec.o ass.o
 OBJS-$(CONFIG_CDGRAPHICS_DECODER)      += cdgraphics.o
 OBJS-$(CONFIG_CDTOONS_DECODER)         += cdtoons.o
 OBJS-$(CONFIG_CDXL_DECODER)            += cdxl.o
diff --git a/libavcodec/allcodecs.c b/libavcodec/allcodecs.c
index 11c136ef59..ffd3d4b8c7 100644
--- a/libavcodec/allcodecs.c
+++ b/libavcodec/allcodecs.c
@@ -711,6 +711,7 @@ extern const FFCodec ff_ssa_decoder;
 extern const FFCodec ff_ass_encoder;
 extern const FFCodec ff_ass_decoder;
 extern const FFCodec ff_ccaption_decoder;
+extern const FFCodec ff_ccaption_eia_608_decoder;
 extern const FFCodec ff_dvbsub_encoder;
 extern const FFCodec ff_dvbsub_decoder;
 extern const FFCodec ff_dvdsub_encoder;
diff --git a/libavcodec/ccaption_dec.c b/libavcodec/ccaption_dec.c
index f91c92b373..41ae1573db 100644
--- a/libavcodec/ccaption_dec.c
+++ b/libavcodec/ccaption_dec.c
@@ -349,41 +349,54 @@ static void write_char(CCaptionSubContext *ctx, struct Screen *screen, char ch)
 }
 
 /**
- * This function after validating parity bit, also remove it from data pair.
+ * This function accepts an EIA 608 data pair
  * The first byte doesn't pass parity, we replace it with a solid blank
  * and process the pair.
  * If the second byte doesn't pass parity, it returns INVALIDDATA
- * user can ignore the whole pair and pass the other pair.
+ * user can ignore the whole pair and pass the next pair.
  */
-static int validate_cc_data_pair(const uint8_t *cc_data_pair, uint8_t *hi)
+static int validate_608_data_pair(const uint8_t *cc_data_pair, uint8_t *hi)
 {
-    uint8_t cc_valid = (*cc_data_pair & 4) >>2;
-    uint8_t cc_type = *cc_data_pair & 3;
+    *hi = cc_data_pair[0];
 
-    *hi = cc_data_pair[1];
-
-    if (!cc_valid)
+    if (!av_parity(cc_data_pair[1])) {
         return AVERROR_INVALIDDATA;
-
-    // if EIA-608 data then verify parity.
-    if (cc_type==0 || cc_type==1) {
-        if (!av_parity(cc_data_pair[2])) {
-            return AVERROR_INVALIDDATA;
-        }
-        if (!av_parity(cc_data_pair[1])) {
-            *hi = 0x7F;
-        }
+    }
+    if (!av_parity(cc_data_pair[0])) {
+        *hi = 0x7F;
     }
 
     //Skip non-data
-    if ((cc_data_pair[0] == 0xFA || cc_data_pair[0] == 0xFC || cc_data_pair[0] == 0xFD)
-         && (cc_data_pair[1] & 0x7F) == 0 && (cc_data_pair[2] & 0x7F) == 0)
+    if ((cc_data_pair[0] & 0x7F) == 0 && (cc_data_pair[1] & 0x7F) == 0)
         return AVERROR_PATCHWELCOME;
 
-    //skip 708 data
+    return 0;
+}
+
+
+/**
+ * This function accepts "cc_data_pair" = [708 header byte, EIA 608 first byte, EIA 608 second byte]
+ * The first byte doesn't pass parity, we replace it with a solid blank
+ * and process the pair.
+ * If the second byte doesn't pass parity, it returns INVALIDDATA
+ * user can ignore the whole pair and pass the next pair.
+ */
+static int validate_cc_data_pair(const uint8_t *cc_data_pair, uint8_t *hi)
+{
+    uint8_t cc_valid = (*cc_data_pair & 4) >>2;
+    uint8_t cc_type = *cc_data_pair & 3;
+    int ret;
+
+    if (!cc_valid)
+        return AVERROR_INVALIDDATA;
+
+    // skip 708 data, we only support "608 over 708" (types 0, 1) not native 708
     if (cc_type == 3 || cc_type == 2)
         return AVERROR_PATCHWELCOME;
 
+    if (ret = validate_608_data_pair(cc_data_pair + 1, hi))
+        return ret;
+
     return 0;
 }
 
@@ -845,20 +858,35 @@ static int decode(AVCodecContext *avctx, AVSubtitle *sub,
     int ret = 0;
     int i;
     unsigned nb_rect_allocated = 0;
+    int stride;
+    int byte_pairs = avctx->codec_id == AV_CODEC_ID_EIA_608_BYTE_PAIRS;
+    if (byte_pairs) {
+        stride = 2; // eia 608: 2 byte "per packet"
+    } else {
+        stride = 3; // eia 708: 1 byte header plus 2 608
+    }
 
-    for (i = 0; i < len; i += 3) {
-        uint8_t hi, cc_type = bptr[i] & 1;
+    for (i = 0; i < len; i += stride) {
+        uint8_t hi;
+        if (byte_pairs) {
+            // there is no concept of cc_type in 608
+            if (validate_608_data_pair(bptr, &hi))
+                continue;
+            ret = process_cc608(ctx, hi & 0x7f, bptr[i + 1] & 0x7f);
+        } else {
+          uint8_t cc_type = bptr[i] & 1;
 
-        if (ctx->data_field < 0)
-            ctx->data_field = cc_type;
+          if (ctx->data_field < 0)
+              ctx->data_field = cc_type;
 
-        if (validate_cc_data_pair(bptr + i, &hi))
-            continue;
+          if (validate_cc_data_pair(bptr + i, &hi))
+              continue;
 
-        if (cc_type != ctx->data_field)
-            continue;
+          if (cc_type != ctx->data_field)
+              continue;
 
-        ret = process_cc608(ctx, hi & 0x7f, bptr[i + 2] & 0x7f);
+          ret = process_cc608(ctx, hi & 0x7f, bptr[i + 2] & 0x7f);
+        }
         if (ret < 0)
             return ret;
 
@@ -939,6 +967,13 @@ static const AVClass ccaption_dec_class = {
     .version    = LIBAVUTIL_VERSION_INT,
 };
 
+static const AVClass ccaption_eia_608_dec_class = {
+    .class_name = "Closed caption Decoder EIA 608 Byte Pairs",
+    .item_name  = av_default_item_name,
+    .option     = options,
+    .version    = LIBAVUTIL_VERSION_INT,
+};
+
 const FFCodec ff_ccaption_decoder = {
     .p.name         = "cc_dec",
     CODEC_LONG_NAME("Closed Caption (EIA-608 / CEA-708)"),
@@ -952,3 +987,17 @@ const FFCodec ff_ccaption_decoder = {
     .flush          = flush_decoder,
     FF_CODEC_DECODE_SUB_CB(decode),
 };
+
+const FFCodec ff_ccaption_eia_608_decoder = {
+    .p.name         = "cc_dec_608_byte_pairs",
+    CODEC_LONG_NAME("Closed Caption (EIA-608 byte pairs)"),
+    .p.type         = AVMEDIA_TYPE_SUBTITLE,
+    .p.id           = AV_CODEC_ID_EIA_608_BYTE_PAIRS,
+    .p.priv_class   = &ccaption_eia_608_dec_class,
+    .p.capabilities = AV_CODEC_CAP_DELAY,
+    .priv_data_size = sizeof(CCaptionSubContext),
+    .init           = init_decoder,
+    .close          = close_decoder,
+    .flush          = flush_decoder,
+    FF_CODEC_DECODE_SUB_CB(decode),
+};
diff --git a/libavcodec/codec_desc.c b/libavcodec/codec_desc.c
index 4406dd8318..8a6e7de551 100644
--- a/libavcodec/codec_desc.c
+++ b/libavcodec/codec_desc.c
@@ -3596,6 +3596,13 @@ static const AVCodecDescriptor codec_descriptors[] = {
         .long_name = NULL_IF_CONFIG_SMALL("ARIB STD-B24 caption"),
         .profiles  = NULL_IF_CONFIG_SMALL(ff_arib_caption_profiles),
     },
+    {
+        .id        = AV_CODEC_ID_EIA_608_BYTE_PAIRS,
+        .type      = AVMEDIA_TYPE_SUBTITLE,
+        .name      = "eia_608_byte_pairs",
+        .long_name = NULL_IF_CONFIG_SMALL("EIA-608 closed caption byte pairs"),
+        .props     = AV_CODEC_PROP_TEXT_SUB,
+    },
 
     /* other kind of codecs and pseudo-codecs */
     {
diff --git a/libavcodec/codec_id.h b/libavcodec/codec_id.h
index a5a0cb8525..e33de68e1e 100644
--- a/libavcodec/codec_id.h
+++ b/libavcodec/codec_id.h
@@ -572,6 +572,7 @@ enum AVCodecID {
     AV_CODEC_ID_HDMV_TEXT_SUBTITLE,
     AV_CODEC_ID_TTML,
     AV_CODEC_ID_ARIB_CAPTION,
+    AV_CODEC_ID_EIA_608_BYTE_PAIRS,
 
     /* other specific kind of codecs (generally used for attachments) */
     AV_CODEC_ID_FIRST_UNKNOWN = 0x18000,           ///< A dummy ID pointing at the start of various fake codecs.
diff --git a/libavcodec/version.h b/libavcodec/version.h
index da6f3a84ac..9411511e04 100644
--- a/libavcodec/version.h
+++ b/libavcodec/version.h
@@ -29,7 +29,7 @@
 
 #include "version_major.h"
 
-#define LIBAVCODEC_VERSION_MINOR  21
+#define LIBAVCODEC_VERSION_MINOR  22
 #define LIBAVCODEC_VERSION_MICRO 100
 
 #define LIBAVCODEC_VERSION_INT  AV_VERSION_INT(LIBAVCODEC_VERSION_MAJOR, \
-- 
2.17.1

