diff mbox

[FFmpeg-devel,1/5] cbs: Add some common code for read/write of miscellaneous user data

Message ID 20180325174137.14749-1-sw@jkqxz.net
State New
Headers show

Commit Message

Mark Thompson March 25, 2018, 5:41 p.m. UTC
Supports closed captions, active format and bar data as defined by
SCTE 128 part 1 or A/53 part 4, suitable for use with both MPEG-2
and H.264.
---
 libavcodec/cbs_misc.c                 | 216 ++++++++++++++++++++++++++++++++++
 libavcodec/cbs_misc.h                 | 109 +++++++++++++++++
 libavcodec/cbs_misc_syntax_template.c | 150 +++++++++++++++++++++++
 3 files changed, 475 insertions(+)
 create mode 100644 libavcodec/cbs_misc.c
 create mode 100644 libavcodec/cbs_misc.h
 create mode 100644 libavcodec/cbs_misc_syntax_template.c

Comments

Aman Karmani July 27, 2019, 1:21 a.m. UTC | #1
On Sun, Mar 25, 2018 at 10:41 AM Mark Thompson <sw@jkqxz.net> wrote:

> Supports closed captions, active format and bar data as defined by
> SCTE 128 part 1 or A/53 part 4, suitable for use with both MPEG-2
> and H.264.
> ---
>  libavcodec/cbs_misc.c                 | 216
> ++++++++++++++++++++++++++++++++++
>  libavcodec/cbs_misc.h                 | 109 +++++++++++++++++
>  libavcodec/cbs_misc_syntax_template.c | 150 +++++++++++++++++++++++
>  3 files changed, 475 insertions(+)
>  create mode 100644 libavcodec/cbs_misc.c
>  create mode 100644 libavcodec/cbs_misc.h
>  create mode 100644 libavcodec/cbs_misc_syntax_template.c
>
> diff --git a/libavcodec/cbs_misc.c b/libavcodec/cbs_misc.c
> new file mode 100644
> index 0000000000..cdf01fe229
> --- /dev/null
> +++ b/libavcodec/cbs_misc.c
> @@ -0,0 +1,216 @@
> +/*
> + * This file is part of FFmpeg.
> + *
> + * FFmpeg is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License as published by the Free Software Foundation; either
> + * version 2.1 of the License, or (at your option) any later version.
> + *
> + * FFmpeg is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with FFmpeg; if not, write to the Free Software
> + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
> 02110-1301 USA
> + */
> +
> +#include "libavutil/attributes.h"
> +#include "libavutil/avassert.h"
> +
> +#include "cbs.h"
> +#include "cbs_internal.h"
> +#include "cbs_misc.h"
> +
> +#define CHECK(call) do { \
> +        err = (call); \
> +        if (err < 0) \
> +            return err; \
> +    } while (0)
> +
> +#define FUNC_NAME(rw, codec, name) cbs_ ## codec ## _ ## rw ## _ ## name
> +#define FUNC_MISC(rw, name) FUNC_NAME(rw, misc, name)
> +#define FUNC(name) FUNC_MISC(READWRITE, name)
> +
> +
> +#define READWRITE read
> +#define RWContext GetBitContext
> +
> +#define xui(width, name, var) do { \
> +        uint32_t value = 0; \
> +        CHECK(ff_cbs_read_unsigned(ctx, rw, width, #name, \
> +                                   &value, 0, MAX_UINT_BITS(width))); \
> +        var = value; \
> +    } while (0)
> +
> +#define ui(width, name) \
> +        xui(width, name, current->name)
> +
> +#define fixed(width, name, expected) do { \
> +        av_unused uint32_t value; \
> +        CHECK(ff_cbs_read_unsigned(ctx, rw, width, #name, &value, \
> +                                   expected, expected)); \
> +    } while (0)
> +
> +#include "cbs_misc_syntax_template.c"
> +
> +#undef READWRITE
> +#undef RWContext
> +#undef xui
> +#undef ui
> +#undef fixed
> +
> +
> +#define READWRITE write
> +#define RWContext PutBitContext
> +
> +#define xui(width, name, var) do { \
> +        CHECK(ff_cbs_write_unsigned(ctx, rw, width, #name, \
> +                                    var, 0, MAX_UINT_BITS(width))); \
> +    } while (0)
> +
> +#define ui(width, name) \
> +        xui(width, name, current->name)
> +
> +#define fixed(width, name, value) do { \
> +        CHECK(ff_cbs_write_unsigned(ctx, rw, width, #name, value, \
> +                                    value, value)); \
> +    } while (0)
> +
> +#include "cbs_misc_syntax_template.c"
> +
> +#undef READWRITE
> +#undef RWContext
> +#undef xui
> +#undef ui
> +#undef fixed
> +
> +
> +int ff_cbs_read_a53_user_data(CodedBitstreamContext *ctx,
> +                              A53UserData *data,
> +                              const uint8_t *read_buffer, size_t length)
> +{
> +    GetBitContext gbc;
> +    int err;
> +
> +    err = init_get_bits(&gbc, read_buffer, 8 * length);
> +    if (err < 0)
> +        return err;
> +
> +    return cbs_misc_read_a53_user_data(ctx, &gbc, data);
> +}
> +
> +int ff_cbs_write_a53_user_data(CodedBitstreamContext *ctx,
> +                               uint8_t *write_buffer, size_t *length,
> +                               A53UserData *data)
> +{
> +    PutBitContext pbc;
> +    int err;
> +
> +    init_put_bits(&pbc, write_buffer, *length);
> +
> +    err = cbs_misc_write_a53_user_data(ctx, &pbc, data);
> +    if (err < 0) {
> +        // Includes AVERROR(ENOSPC).
> +        return err;
> +    }
> +
> +    // That output must be aligned.
> +    av_assert0(put_bits_count(&pbc) % 8 == 0);
> +
> +    *length = put_bits_count(&pbc) / 8;
> +
> +    flush_put_bits(&pbc);
> +
> +    return 0;
> +}
> +
> +int ff_cbs_read_a53_cc_side_data(CodedBitstreamContext *ctx,
> +                                 A53UserData *data,
> +                                 const uint8_t *side_data,
> +                                 size_t side_data_size)
> +{
> +    GetBitContext gbc;
> +    CEA708CCData *cc;
> +    int err, i, cc_count;
> +
> +    if (side_data_size % 3) {
> +        av_log(ctx->log_ctx, AV_LOG_ERROR, "A53 CC side data length must "
> +               "be a multiple of 3 (got %zu).\n", side_data_size);
> +        return AVERROR(EINVAL);
> +    }
> +    cc_count = side_data_size / 3;
> +    if (cc_count > 31) {
> +        av_log(ctx->log_ctx, AV_LOG_ERROR, "A53 CC can only fit 31
> packets "
> +               "in a single user data block (got %d).\n", cc_count);
> +        return AVERROR(EINVAL);
> +    }
> +
> +    *data = (A53UserData) {
> +        .user_identifier = A53_USER_IDENTIFIER_ATSC,
> +
> +        .atsc = {
> +            .user_data_type_code = A53_USER_DATA_TYPE_CODE_CC_DATA,
> +
> +            .cc_data = {
> +                .process_em_data_flag = 0,
> +                .process_cc_data_flag = 1,
> +                .additional_data_flag = 0,
> +
> +                .em_data = 0,
> +
> +                .cc_count = cc_count,
> +            },
> +        },
> +    };
> +    cc = &data->atsc.cc_data;
> +
> +    err = init_get_bits(&gbc, side_data, 8 * side_data_size);
> +    if (err < 0)
> +        return err;
> +
> +    for (i = 0; i < cc->cc_count; i++) {
> +        err = cbs_misc_read_cea708_cc_data_packet(ctx, &gbc,
> +                                                  &cc->cc_data_pkts[i]);
> +        if (err < 0)
> +            return err;
> +    }
> +
> +    return 0;
> +}
> +
> +int ff_cbs_write_a53_cc_side_data(CodedBitstreamContext *ctx,
> +                                  uint8_t **side_data,
> +                                  size_t *side_data_size,
> +                                  A53UserData *data)
> +{
> +    PutBitContext pbc;
> +    CEA708CCData *cc;
> +    int err, i;
> +
> +    if (data->user_identifier != A53_USER_IDENTIFIER_ATSC ||
> +        data->atsc.user_data_type_code != A53_USER_DATA_TYPE_CODE_CC_DATA)
> +        return AVERROR(EINVAL);
> +
> +    cc = &data->atsc.cc_data;
> +
> +    err = av_reallocp(side_data, *side_data_size + 3 * cc->cc_count);
> +    if (err < 0)
> +        return err;
> +
> +    init_put_bits(&pbc, *side_data + *side_data_size, 3 * cc->cc_count);
> +
> +    for (i = 0; i < cc->cc_count; i++) {
> +        err = cbs_misc_write_cea708_cc_data_packet(ctx, &pbc,
> +                                                   &cc->cc_data_pkts[i]);
> +        if (err < 0) {
> +            av_freep(side_data);
> +            return err;
> +        }
> +    }
> +
> +    *side_data_size += 3 * cc->cc_count;
>

Missing flush_put_bits() here.


> +
> +    return 0;
> +}
> diff --git a/libavcodec/cbs_misc.h b/libavcodec/cbs_misc.h
> new file mode 100644
> index 0000000000..0d7ab2c8e7
> --- /dev/null
> +++ b/libavcodec/cbs_misc.h
> @@ -0,0 +1,109 @@
> +/*
> + * This file is part of FFmpeg.
> + *
> + * FFmpeg is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License as published by the Free Software Foundation; either
> + * version 2.1 of the License, or (at your option) any later version.
> + *
> + * FFmpeg is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with FFmpeg; if not, write to the Free Software
> + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
> 02110-1301 USA
> + */
> +
> +#ifndef AVCODEC_CBS_MISC_H
> +#define AVCODEC_CBS_MISC_H
> +
> +#include <stddef.h>
> +#include <stdint.h>
> +
> +#include "libavutil/common.h"
> +
> +
> +enum {
> +    A53_USER_IDENTIFIER_ATSC = MKBETAG('G', 'A', '9', '4'),
> +    A53_USER_IDENTIFIER_AFD  = MKBETAG('D', 'T', 'G', '1'),
> +};
> +
> +enum {
> +    A53_USER_DATA_TYPE_CODE_CC_DATA  = 0x03,
> +    A53_USER_DATA_TYPE_CODE_BAR_DATA = 0x06,
> +};
> +
> +typedef struct A53BarData {
> +    uint8_t top_bar_flag;
> +    uint8_t bottom_bar_flag;
> +    uint8_t left_bar_flag;
> +    uint8_t right_bar_flag;
> +
> +    uint16_t line_number_end_of_top_bar;
> +    uint16_t line_number_end_of_bottom_bar;
> +    uint16_t line_number_end_of_left_bar;
> +    uint16_t line_number_end_of_right_bar;
> +} A53BarData;
> +
> +typedef struct CEA708CCDataPacket {
> +    uint8_t cc_valid;
> +    uint8_t cc_type;
> +    uint8_t cc_data_1;
> +    uint8_t cc_data_2;
> +} CEA708CCDataPacket;
> +
> +typedef struct CEA708CCData {
> +    uint8_t process_em_data_flag;
> +    uint8_t process_cc_data_flag;
> +    uint8_t additional_data_flag;
> +
> +    uint8_t em_data;
> +
> +    uint8_t cc_count;
> +    CEA708CCDataPacket cc_data_pkts[31];
> +} CEA708CCData;
> +
> +typedef struct A53ATSCUserData {
> +    uint8_t user_data_type_code;
> +    union {
> +        CEA708CCData cc_data;
> +        A53BarData bar_data;
> +    };
> +} A53ATSCUserData;
> +
> +typedef struct A53AFDData {
> +    uint8_t active_format_flag;
> +    uint8_t active_format;
> +} A53AFDData;
> +
> +typedef struct A53UserData {
> +    uint32_t user_identifier;
> +    union {
> +        A53ATSCUserData atsc;
> +        A53AFDData afd;
> +    };
> +} A53UserData;
> +
> +
> +int ff_cbs_read_a53_user_data(CodedBitstreamContext *ctx,
> +                              A53UserData *data,
> +                              const uint8_t *read_buffer, size_t length);
> +
> +int ff_cbs_write_a53_user_data(CodedBitstreamContext *ctx,
> +                               uint8_t *write_buffer, size_t *length,
> +                               A53UserData *data);
> +
> +int ff_cbs_read_a53_cc_side_data(CodedBitstreamContext *ctx,
> +                                 A53UserData *data,
> +                                 const uint8_t *side_data,
> +                                 size_t side_data_size);
> +
> +int ff_cbs_write_a53_cc_side_data(CodedBitstreamContext *ctx,
> +                                  uint8_t **side_data,
> +                                  size_t *side_data_length,
> +                                  A53UserData *data);
> +
> +
> +#endif /* AVCODEC_CBS_MISC_H */
> diff --git a/libavcodec/cbs_misc_syntax_template.c
> b/libavcodec/cbs_misc_syntax_template.c
> new file mode 100644
> index 0000000000..7b98c7cc85
> --- /dev/null
> +++ b/libavcodec/cbs_misc_syntax_template.c
> @@ -0,0 +1,150 @@
> +/*
> + * This file is part of FFmpeg.
> + *
> + * FFmpeg is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License as published by the Free Software Foundation; either
> + * version 2.1 of the License, or (at your option) any later version.
> + *
> + * FFmpeg is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with FFmpeg; if not, write to the Free Software
> + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
> 02110-1301 USA
> + */
> +
> +static int FUNC(a53_bar_data)(CodedBitstreamContext *ctx, RWContext *rw,
> +                              A53BarData *current)
> +{
> +    int err;
> +
> +    ui(1, top_bar_flag);
> +    ui(1, bottom_bar_flag);
> +    ui(1, left_bar_flag);
> +    ui(1, right_bar_flag);
> +    fixed(4, reserved, 0xf);
> +
> +    if (current->top_bar_flag) {
> +        fixed(2, one_bits, 3);
> +        ui(14, line_number_end_of_top_bar);
> +    }
> +    if (current->bottom_bar_flag) {
> +        fixed(2, one_bits, 3);
> +        ui(14, line_number_end_of_bottom_bar);
> +    }
> +    if (current->left_bar_flag) {
> +        fixed(2, one_bits, 3);
> +        ui(14, line_number_end_of_left_bar);
> +    }
> +    if (current->right_bar_flag) {
> +        fixed(2, one_bits, 3);
> +        ui(14, line_number_end_of_right_bar);
> +    }
> +
> +    return 0;
> +}
> +
> +static int FUNC(cea708_cc_data_packet)(CodedBitstreamContext *ctx,
> +                                       RWContext *rw,
> +                                       CEA708CCDataPacket *current)
> +{
> +    int err;
> +
> +    fixed(5, marker_bits, 0x1f);
> +    ui(1, cc_valid);
> +    ui(2, cc_type);
> +
> +    ui(8, cc_data_1);
> +    ui(8, cc_data_2);
> +
> +    return 0;
> +}
> +
> +static int FUNC(cea708_cc_data)(CodedBitstreamContext *ctx, RWContext *rw,
> +                                CEA708CCData *current)
> +{
> +    int err, i;
> +
> +    ui(1, process_em_data_flag);
> +    ui(1, process_cc_data_flag);
> +    ui(1, additional_data_flag);
> +
> +    ui(5, cc_count);
> +
> +    ui(8, em_data);
> +
> +    for (i = 0; i < current->cc_count; i++) {
> +        CHECK(FUNC(cea708_cc_data_packet)(ctx, rw,
> +                                          &current->cc_data_pkts[i]));
> +    }
> +
> +    fixed(8, marker_bits, 0xff);
> +
> +    if (current->additional_data_flag) {
> +        // Ignored.
> +    }
> +
> +    return 0;
> +}
> +
> +static int FUNC(a53_atsc_user_data)(CodedBitstreamContext *ctx, RWContext
> *rw,
> +                                    A53ATSCUserData *current)
> +{
> +    int err;
> +
> +    ui(8, user_data_type_code);
> +
> +    switch (current->user_data_type_code) {
> +    case A53_USER_DATA_TYPE_CODE_CC_DATA:
> +        return FUNC(cea708_cc_data)(ctx, rw, &current->cc_data);
> +    case A53_USER_DATA_TYPE_CODE_BAR_DATA:
> +        return FUNC(a53_bar_data)(ctx, rw, &current->bar_data);
> +    default:
> +        av_log(ctx->log_ctx, AV_LOG_WARNING,
> +               "Unknown ATSC user data found: type code %#02x.\n",
> +               current->user_data_type_code);
> +    }
> +
> +    return 0;
> +}
> +
> +static int FUNC(a53_afd_data)(CodedBitstreamContext *ctx, RWContext *rw,
> +                              A53AFDData *current)
> +{
> +    int err;
> +
> +    fixed(1, zero_bit, 0);
> +    ui(1, active_format_flag);
> +    fixed(6, alignment_bits, 1);
> +
> +    if (current->active_format_flag) {
> +        fixed(4, reserved, 0xf);
> +        ui(4, active_format);
> +    }
> +
> +    return 0;
> +}
> +
> +static int FUNC(a53_user_data)(CodedBitstreamContext *ctx, RWContext *rw,
> +                               A53UserData *current)
> +{
> +    int err;
> +
> +    ui(32, user_identifier);
> +
> +    switch (current->user_identifier) {
> +    case A53_USER_IDENTIFIER_ATSC:
> +        return FUNC(a53_atsc_user_data)(ctx, rw, &current->atsc);
> +    case A53_USER_IDENTIFIER_AFD:
> +        return FUNC(a53_afd_data)(ctx, rw, &current->afd);
> +    default:
> +        av_log(ctx->log_ctx, AV_LOG_WARNING,
> +               "Unknown registered user data found: identifier %#08x.\n",
> +               current->user_identifier);
> +    }
> +
> +    return 0;
> +}
> --
> 2.16.1
>
> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel@ffmpeg.org
> http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
>
diff mbox

Patch

diff --git a/libavcodec/cbs_misc.c b/libavcodec/cbs_misc.c
new file mode 100644
index 0000000000..cdf01fe229
--- /dev/null
+++ b/libavcodec/cbs_misc.c
@@ -0,0 +1,216 @@ 
+/*
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "libavutil/attributes.h"
+#include "libavutil/avassert.h"
+
+#include "cbs.h"
+#include "cbs_internal.h"
+#include "cbs_misc.h"
+
+#define CHECK(call) do { \
+        err = (call); \
+        if (err < 0) \
+            return err; \
+    } while (0)
+
+#define FUNC_NAME(rw, codec, name) cbs_ ## codec ## _ ## rw ## _ ## name
+#define FUNC_MISC(rw, name) FUNC_NAME(rw, misc, name)
+#define FUNC(name) FUNC_MISC(READWRITE, name)
+
+
+#define READWRITE read
+#define RWContext GetBitContext
+
+#define xui(width, name, var) do { \
+        uint32_t value = 0; \
+        CHECK(ff_cbs_read_unsigned(ctx, rw, width, #name, \
+                                   &value, 0, MAX_UINT_BITS(width))); \
+        var = value; \
+    } while (0)
+
+#define ui(width, name) \
+        xui(width, name, current->name)
+
+#define fixed(width, name, expected) do { \
+        av_unused uint32_t value; \
+        CHECK(ff_cbs_read_unsigned(ctx, rw, width, #name, &value, \
+                                   expected, expected)); \
+    } while (0)
+
+#include "cbs_misc_syntax_template.c"
+
+#undef READWRITE
+#undef RWContext
+#undef xui
+#undef ui
+#undef fixed
+
+
+#define READWRITE write
+#define RWContext PutBitContext
+
+#define xui(width, name, var) do { \
+        CHECK(ff_cbs_write_unsigned(ctx, rw, width, #name, \
+                                    var, 0, MAX_UINT_BITS(width))); \
+    } while (0)
+
+#define ui(width, name) \
+        xui(width, name, current->name)
+
+#define fixed(width, name, value) do { \
+        CHECK(ff_cbs_write_unsigned(ctx, rw, width, #name, value, \
+                                    value, value)); \
+    } while (0)
+
+#include "cbs_misc_syntax_template.c"
+
+#undef READWRITE
+#undef RWContext
+#undef xui
+#undef ui
+#undef fixed
+
+
+int ff_cbs_read_a53_user_data(CodedBitstreamContext *ctx,
+                              A53UserData *data,
+                              const uint8_t *read_buffer, size_t length)
+{
+    GetBitContext gbc;
+    int err;
+
+    err = init_get_bits(&gbc, read_buffer, 8 * length);
+    if (err < 0)
+        return err;
+
+    return cbs_misc_read_a53_user_data(ctx, &gbc, data);
+}
+
+int ff_cbs_write_a53_user_data(CodedBitstreamContext *ctx,
+                               uint8_t *write_buffer, size_t *length,
+                               A53UserData *data)
+{
+    PutBitContext pbc;
+    int err;
+
+    init_put_bits(&pbc, write_buffer, *length);
+
+    err = cbs_misc_write_a53_user_data(ctx, &pbc, data);
+    if (err < 0) {
+        // Includes AVERROR(ENOSPC).
+        return err;
+    }
+
+    // That output must be aligned.
+    av_assert0(put_bits_count(&pbc) % 8 == 0);
+
+    *length = put_bits_count(&pbc) / 8;
+
+    flush_put_bits(&pbc);
+
+    return 0;
+}
+
+int ff_cbs_read_a53_cc_side_data(CodedBitstreamContext *ctx,
+                                 A53UserData *data,
+                                 const uint8_t *side_data,
+                                 size_t side_data_size)
+{
+    GetBitContext gbc;
+    CEA708CCData *cc;
+    int err, i, cc_count;
+
+    if (side_data_size % 3) {
+        av_log(ctx->log_ctx, AV_LOG_ERROR, "A53 CC side data length must "
+               "be a multiple of 3 (got %zu).\n", side_data_size);
+        return AVERROR(EINVAL);
+    }
+    cc_count = side_data_size / 3;
+    if (cc_count > 31) {
+        av_log(ctx->log_ctx, AV_LOG_ERROR, "A53 CC can only fit 31 packets "
+               "in a single user data block (got %d).\n", cc_count);
+        return AVERROR(EINVAL);
+    }
+
+    *data = (A53UserData) {
+        .user_identifier = A53_USER_IDENTIFIER_ATSC,
+
+        .atsc = {
+            .user_data_type_code = A53_USER_DATA_TYPE_CODE_CC_DATA,
+
+            .cc_data = {
+                .process_em_data_flag = 0,
+                .process_cc_data_flag = 1,
+                .additional_data_flag = 0,
+
+                .em_data = 0,
+
+                .cc_count = cc_count,
+            },
+        },
+    };
+    cc = &data->atsc.cc_data;
+
+    err = init_get_bits(&gbc, side_data, 8 * side_data_size);
+    if (err < 0)
+        return err;
+
+    for (i = 0; i < cc->cc_count; i++) {
+        err = cbs_misc_read_cea708_cc_data_packet(ctx, &gbc,
+                                                  &cc->cc_data_pkts[i]);
+        if (err < 0)
+            return err;
+    }
+
+    return 0;
+}
+
+int ff_cbs_write_a53_cc_side_data(CodedBitstreamContext *ctx,
+                                  uint8_t **side_data,
+                                  size_t *side_data_size,
+                                  A53UserData *data)
+{
+    PutBitContext pbc;
+    CEA708CCData *cc;
+    int err, i;
+
+    if (data->user_identifier != A53_USER_IDENTIFIER_ATSC ||
+        data->atsc.user_data_type_code != A53_USER_DATA_TYPE_CODE_CC_DATA)
+        return AVERROR(EINVAL);
+
+    cc = &data->atsc.cc_data;
+
+    err = av_reallocp(side_data, *side_data_size + 3 * cc->cc_count);
+    if (err < 0)
+        return err;
+
+    init_put_bits(&pbc, *side_data + *side_data_size, 3 * cc->cc_count);
+
+    for (i = 0; i < cc->cc_count; i++) {
+        err = cbs_misc_write_cea708_cc_data_packet(ctx, &pbc,
+                                                   &cc->cc_data_pkts[i]);
+        if (err < 0) {
+            av_freep(side_data);
+            return err;
+        }
+    }
+
+    *side_data_size += 3 * cc->cc_count;
+
+    return 0;
+}
diff --git a/libavcodec/cbs_misc.h b/libavcodec/cbs_misc.h
new file mode 100644
index 0000000000..0d7ab2c8e7
--- /dev/null
+++ b/libavcodec/cbs_misc.h
@@ -0,0 +1,109 @@ 
+/*
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef AVCODEC_CBS_MISC_H
+#define AVCODEC_CBS_MISC_H
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include "libavutil/common.h"
+
+
+enum {
+    A53_USER_IDENTIFIER_ATSC = MKBETAG('G', 'A', '9', '4'),
+    A53_USER_IDENTIFIER_AFD  = MKBETAG('D', 'T', 'G', '1'),
+};
+
+enum {
+    A53_USER_DATA_TYPE_CODE_CC_DATA  = 0x03,
+    A53_USER_DATA_TYPE_CODE_BAR_DATA = 0x06,
+};
+
+typedef struct A53BarData {
+    uint8_t top_bar_flag;
+    uint8_t bottom_bar_flag;
+    uint8_t left_bar_flag;
+    uint8_t right_bar_flag;
+
+    uint16_t line_number_end_of_top_bar;
+    uint16_t line_number_end_of_bottom_bar;
+    uint16_t line_number_end_of_left_bar;
+    uint16_t line_number_end_of_right_bar;
+} A53BarData;
+
+typedef struct CEA708CCDataPacket {
+    uint8_t cc_valid;
+    uint8_t cc_type;
+    uint8_t cc_data_1;
+    uint8_t cc_data_2;
+} CEA708CCDataPacket;
+
+typedef struct CEA708CCData {
+    uint8_t process_em_data_flag;
+    uint8_t process_cc_data_flag;
+    uint8_t additional_data_flag;
+
+    uint8_t em_data;
+
+    uint8_t cc_count;
+    CEA708CCDataPacket cc_data_pkts[31];
+} CEA708CCData;
+
+typedef struct A53ATSCUserData {
+    uint8_t user_data_type_code;
+    union {
+        CEA708CCData cc_data;
+        A53BarData bar_data;
+    };
+} A53ATSCUserData;
+
+typedef struct A53AFDData {
+    uint8_t active_format_flag;
+    uint8_t active_format;
+} A53AFDData;
+
+typedef struct A53UserData {
+    uint32_t user_identifier;
+    union {
+        A53ATSCUserData atsc;
+        A53AFDData afd;
+    };
+} A53UserData;
+
+
+int ff_cbs_read_a53_user_data(CodedBitstreamContext *ctx,
+                              A53UserData *data,
+                              const uint8_t *read_buffer, size_t length);
+
+int ff_cbs_write_a53_user_data(CodedBitstreamContext *ctx,
+                               uint8_t *write_buffer, size_t *length,
+                               A53UserData *data);
+
+int ff_cbs_read_a53_cc_side_data(CodedBitstreamContext *ctx,
+                                 A53UserData *data,
+                                 const uint8_t *side_data,
+                                 size_t side_data_size);
+
+int ff_cbs_write_a53_cc_side_data(CodedBitstreamContext *ctx,
+                                  uint8_t **side_data,
+                                  size_t *side_data_length,
+                                  A53UserData *data);
+
+
+#endif /* AVCODEC_CBS_MISC_H */
diff --git a/libavcodec/cbs_misc_syntax_template.c b/libavcodec/cbs_misc_syntax_template.c
new file mode 100644
index 0000000000..7b98c7cc85
--- /dev/null
+++ b/libavcodec/cbs_misc_syntax_template.c
@@ -0,0 +1,150 @@ 
+/*
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+static int FUNC(a53_bar_data)(CodedBitstreamContext *ctx, RWContext *rw,
+                              A53BarData *current)
+{
+    int err;
+
+    ui(1, top_bar_flag);
+    ui(1, bottom_bar_flag);
+    ui(1, left_bar_flag);
+    ui(1, right_bar_flag);
+    fixed(4, reserved, 0xf);
+
+    if (current->top_bar_flag) {
+        fixed(2, one_bits, 3);
+        ui(14, line_number_end_of_top_bar);
+    }
+    if (current->bottom_bar_flag) {
+        fixed(2, one_bits, 3);
+        ui(14, line_number_end_of_bottom_bar);
+    }
+    if (current->left_bar_flag) {
+        fixed(2, one_bits, 3);
+        ui(14, line_number_end_of_left_bar);
+    }
+    if (current->right_bar_flag) {
+        fixed(2, one_bits, 3);
+        ui(14, line_number_end_of_right_bar);
+    }
+
+    return 0;
+}
+
+static int FUNC(cea708_cc_data_packet)(CodedBitstreamContext *ctx,
+                                       RWContext *rw,
+                                       CEA708CCDataPacket *current)
+{
+    int err;
+
+    fixed(5, marker_bits, 0x1f);
+    ui(1, cc_valid);
+    ui(2, cc_type);
+
+    ui(8, cc_data_1);
+    ui(8, cc_data_2);
+
+    return 0;
+}
+
+static int FUNC(cea708_cc_data)(CodedBitstreamContext *ctx, RWContext *rw,
+                                CEA708CCData *current)
+{
+    int err, i;
+
+    ui(1, process_em_data_flag);
+    ui(1, process_cc_data_flag);
+    ui(1, additional_data_flag);
+
+    ui(5, cc_count);
+
+    ui(8, em_data);
+
+    for (i = 0; i < current->cc_count; i++) {
+        CHECK(FUNC(cea708_cc_data_packet)(ctx, rw,
+                                          &current->cc_data_pkts[i]));
+    }
+
+    fixed(8, marker_bits, 0xff);
+
+    if (current->additional_data_flag) {
+        // Ignored.
+    }
+
+    return 0;
+}
+
+static int FUNC(a53_atsc_user_data)(CodedBitstreamContext *ctx, RWContext *rw,
+                                    A53ATSCUserData *current)
+{
+    int err;
+
+    ui(8, user_data_type_code);
+
+    switch (current->user_data_type_code) {
+    case A53_USER_DATA_TYPE_CODE_CC_DATA:
+        return FUNC(cea708_cc_data)(ctx, rw, &current->cc_data);
+    case A53_USER_DATA_TYPE_CODE_BAR_DATA:
+        return FUNC(a53_bar_data)(ctx, rw, &current->bar_data);
+    default:
+        av_log(ctx->log_ctx, AV_LOG_WARNING,
+               "Unknown ATSC user data found: type code %#02x.\n",
+               current->user_data_type_code);
+    }
+
+    return 0;
+}
+
+static int FUNC(a53_afd_data)(CodedBitstreamContext *ctx, RWContext *rw,
+                              A53AFDData *current)
+{
+    int err;
+
+    fixed(1, zero_bit, 0);
+    ui(1, active_format_flag);
+    fixed(6, alignment_bits, 1);
+
+    if (current->active_format_flag) {
+        fixed(4, reserved, 0xf);
+        ui(4, active_format);
+    }
+
+    return 0;
+}
+
+static int FUNC(a53_user_data)(CodedBitstreamContext *ctx, RWContext *rw,
+                               A53UserData *current)
+{
+    int err;
+
+    ui(32, user_identifier);
+
+    switch (current->user_identifier) {
+    case A53_USER_IDENTIFIER_ATSC:
+        return FUNC(a53_atsc_user_data)(ctx, rw, &current->atsc);
+    case A53_USER_IDENTIFIER_AFD:
+        return FUNC(a53_afd_data)(ctx, rw, &current->afd);
+    default:
+        av_log(ctx->log_ctx, AV_LOG_WARNING,
+               "Unknown registered user data found: identifier %#08x.\n",
+               current->user_identifier);
+    }
+
+    return 0;
+}