[FFmpeg-devel] mpeg12dec fix up DVD caption handling

Jonathan Campbell jonathan at impactstudiopro.com
Tue Sep 13 01:28:24 EEST 2016


These patches fix up the DVD caption handling in mpeg12dec.c to better handle odd cases.
It's based on code I've written elsewhere to handle captions.
While it's common for these packets to contain 15 frames worth and start on the odd field there are also DVDs that start on even field or even encode extra fields and switch starting fields.
Part of the patch is to document comprehensively the format of the DVD caption packet.

Jonathan Campbell
-------------- next part --------------
>From 9213012c7d8ceef2af43fe3c218b1b50728e8f80 Mon Sep 17 00:00:00 2001
From: Jonathan Campbell <jonathan at castus.tv>
Date: Mon, 12 Sep 2016 12:34:48 -0700
Subject: [PATCH 1/2] add comments documenting the format of the DVD CC
 user-data packet. this is to aid development and maintenance of that code.

---
 libavcodec/mpeg12dec.c | 27 ++++++++++++++++++++++++++-
 1 file changed, 26 insertions(+), 1 deletion(-)

diff --git a/libavcodec/mpeg12dec.c b/libavcodec/mpeg12dec.c
index 204a578..522621a 100644
--- a/libavcodec/mpeg12dec.c
+++ b/libavcodec/mpeg12dec.c
@@ -2262,7 +2262,32 @@ static int mpeg_decode_a53_cc(AVCodecContext *avctx,
         return 1;
     } else if (buf_size >= 11 &&
                p[0] == 'C' && p[1] == 'C' && p[2] == 0x01 && p[3] == 0xf8) {
-        /* extract DVD CC data */
+        /* extract DVD CC data
+         * for more information see: [https://en.wikipedia.org/wiki/EIA-608#DVD_GOP_User_Data_Insertion]
+         *
+         * uint32_t   user_data_start_code        0x000001B2    (big endian)
+         * uint16_t   user_identifier             0x4343 "CC"
+         * uint8_t    user_data_type_code         0x01
+         * uint8_t    caption_block_size          0xF8
+         * uint8_t
+         *   bit 7    caption_odd_field_first     1=odd field (CC1/CC2) first  0=even field (CC3/CC4) first
+         *   bit 6    caption_filler              0
+         *   bit 5:1  caption_block_count         number of caption blocks (pairs of caption words = frames). Most DVDs use 15 per start of GOP.
+         *   bit 0    caption_extra_field_added   1=one additional caption word
+         *
+         * struct caption_field_block {
+         *   uint8_t
+         *     bit 7:1 caption_filler             0x7F (all 1s)
+         *     bit 0   caption_field_odd          1=odd field (this is CC1/CC2)  0=even field (this is CC3/CC4)
+         *   uint8_t   caption_first_byte
+         *   uint8_t   caption_second_byte
+         * } caption_block[(caption_block_count * 2) + caption_extra_field_added];
+         *
+         * Some DVDs encode caption data for both fields with caption_field_odd=1. The only way to decode the fields
+         * correctly is to start on the field indicated by caption_odd_field_first and count between odd/even fields.
+         * Don't assume that the first caption word is the odd field. There do exist MPEG files in the wild that start
+         * on the even field. There also exist DVDs in the wild that encode an odd field count and the
+         * caption_extra_field_added/caption_odd_field_first bits change per packet to allow that. */
         int cc_count = 0;
         int i;
         // There is a caption count field in the data, but it is often
-- 
2.2.2

-------------- next part --------------
>From b95df051ae0b544c43c685a73b24c8b5123d593b Mon Sep 17 00:00:00 2001
From: Jonathan Campbell <jonathan at castus.tv>
Date: Mon, 12 Sep 2016 14:45:10 -0700
Subject: [PATCH 2/2] read caption words field-wise, count properly and limit
 to cc_count. transfer each CC word taking into consideration immediate CC
 field bit or for DVDs that don't use it, keep track according to first field
 specified at start of DVD caption packet.

---
 libavcodec/mpeg12dec.c | 45 ++++++++++++++++++++++++++++++++-------------
 1 file changed, 32 insertions(+), 13 deletions(-)

diff --git a/libavcodec/mpeg12dec.c b/libavcodec/mpeg12dec.c
index 522621a..8f88e82 100644
--- a/libavcodec/mpeg12dec.c
+++ b/libavcodec/mpeg12dec.c
@@ -2266,6 +2266,7 @@ static int mpeg_decode_a53_cc(AVCodecContext *avctx,
          * for more information see: [https://en.wikipedia.org/wiki/EIA-608#DVD_GOP_User_Data_Insertion]
          *
          * uint32_t   user_data_start_code        0x000001B2    (big endian)
+         * -------------------- p[0] starts here ---------------------
          * uint16_t   user_identifier             0x4343 "CC"
          * uint8_t    user_data_type_code         0x01
          * uint8_t    caption_block_size          0xF8
@@ -2274,7 +2275,7 @@ static int mpeg_decode_a53_cc(AVCodecContext *avctx,
          *   bit 6    caption_filler              0
          *   bit 5:1  caption_block_count         number of caption blocks (pairs of caption words = frames). Most DVDs use 15 per start of GOP.
          *   bit 0    caption_extra_field_added   1=one additional caption word
-         *
+         * -------------------- p[5] starts here ---------------------
          * struct caption_field_block {
          *   uint8_t
          *     bit 7:1 caption_filler             0x7F (all 1s)
@@ -2288,30 +2289,48 @@ static int mpeg_decode_a53_cc(AVCodecContext *avctx,
          * Don't assume that the first caption word is the odd field. There do exist MPEG files in the wild that start
          * on the even field. There also exist DVDs in the wild that encode an odd field count and the
          * caption_extra_field_added/caption_odd_field_first bits change per packet to allow that. */
-        int cc_count = 0;
+        int caption_block_count = p[4] & 0x3F; /* you can treat bits 5:0 as number of fields */
+        int cc_count = 0; /* number of caption fields */
         int i;
-        // There is a caption count field in the data, but it is often
-        // incorrect.  So count the number of captions present.
-        for (i = 5; i + 6 <= buf_size && ((p[i] & 0xfe) == 0xfe); i += 6)
+
+        for (i = 5; cc_count < caption_block_count && (i + 3) <= buf_size; i += 3) {
+            if ((p[i] & 0xfe) != 0xfe) {
+                av_log(avctx, AV_LOG_DEBUG, "cc_count is too large (%u > %u) or junk data in DVD caption packet",(unsigned int)caption_block_count,(unsigned int)cc_count);
+                break;
+            }
+
             cc_count++;
+        }
+
         // Transform the DVD format into A53 Part 4 format
         if (cc_count > 0) {
             av_freep(&s1->a53_caption);
-            s1->a53_caption_size = cc_count * 6;
+            s1->a53_caption_size = cc_count * 3;
             s1->a53_caption      = av_malloc(s1->a53_caption_size);
             if (s1->a53_caption) {
-                uint8_t field1 = !!(p[4] & 0x80);
+                uint8_t field1 = (p[4] >> 7) & 1; /* caption_odd_field_first */
+                uint8_t pfield = 0xFF; /* DVDs that don't use the caption_field_odd bit always seem to leave it on */
                 uint8_t *cap = s1->a53_caption;
+
                 p += 5;
                 for (i = 0; i < cc_count; i++) {
-                    cap[0] = (p[0] == 0xff && field1) ? 0xfc : 0xfd;
+                    /* if the source actually uses the caption_odd_field bit, then use that to determine the field.
+                     * else, toggle between fields to keep track for DVDs where p[0] == 0xFF at all times. */
+                    if (p[0] != pfield)
+                        field1 = p[0] & 1; /* caption_field_odd */
+
+                    /* in A53 part 4, 0xFC = odd field, 0xFD = even field */
+                    cap[0] = field1 ? 0xFC : 0xFD;
                     cap[1] = p[1];
                     cap[2] = p[2];
-                    cap[3] = (p[3] == 0xff && !field1) ? 0xfc : 0xfd;
-                    cap[4] = p[4];
-                    cap[5] = p[5];
-                    cap += 6;
-                    p += 6;
+
+                    av_log(avctx, AV_LOG_DEBUG, "DVD CC field1=%u(%s) 0x%02x%02x prev=0x%02x cur=0x%02x\n",
+                        field1,field1?"odd":"even",cap[1],cap[2],pfield,p[0]);
+
+                    pfield = p[0];
+                    field1 ^= 1;
+                    cap += 3;
+                    p += 3;
                 }
             }
         }
-- 
2.2.2



More information about the ffmpeg-devel mailing list