[FFmpeg-devel,07/11] decklink: Add support for SCTE-104 to decklink capture

Submitted by Devin Heitmueller on Jan. 9, 2018, 1:16 a.m.

Details

Message ID 20180109011658.72370-8-dheitmueller@ltnglobal.com
State New
Headers show

Commit Message

Devin Heitmueller Jan. 9, 2018, 1:16 a.m.
Make use of libklvanc to parse SCTE-104 packets and announce them
as a new stream.  Right now we just pass the payload straight
through, but once this is hooked into libklscte35 we'll be able
to generate SCTE-35 messages in the MPEG TS stream.

Note that this feature needs to be explicitly enabled by the user
through the "-enable_scte_104" option, since we cannot autodetect
the presence of SCTE-104 (because unlike with 708/AFD messages are
not set except when trigger occurs, thus the stream wouldn't get
created during the read_header phase).

Updated to reflect feedback from Derek Buitenhuis <derek.buitenhuis@gmail.com>
and Aaron Levinson <alevinsn_dev@levland.net>

Signed-off-by: Devin Heitmueller <dheitmueller@ltnglobal.com>
---
 doc/indevs.texi                 |  4 +++
 libavcodec/avcodec.h            |  1 +
 libavcodec/codec_desc.c         |  6 ++++
 libavdevice/decklink_common.h   |  6 ++++
 libavdevice/decklink_common_c.h |  1 +
 libavdevice/decklink_dec.cpp    | 61 ++++++++++++++++++++++++++++++++++++++++-
 libavdevice/decklink_dec_c.c    |  1 +
 libavdevice/version.h           |  2 +-
 8 files changed, 80 insertions(+), 2 deletions(-)

Comments

Marton Balint Jan. 12, 2018, 7:25 p.m.
On Mon, 8 Jan 2018, Devin Heitmueller wrote:

> Make use of libklvanc to parse SCTE-104 packets and announce them
> as a new stream.  Right now we just pass the payload straight
> through, but once this is hooked into libklscte35 we'll be able
> to generate SCTE-35 messages in the MPEG TS stream.
>
> Note that this feature needs to be explicitly enabled by the user
> through the "-enable_scte_104" option, since we cannot autodetect
> the presence of SCTE-104 (because unlike with 708/AFD messages are
> not set except when trigger occurs, thus the stream wouldn't get
> created during the read_header phase).
>
> Updated to reflect feedback from Derek Buitenhuis <derek.buitenhuis@gmail.com>
> and Aaron Levinson <alevinsn_dev@levland.net>
>
> Signed-off-by: Devin Heitmueller <dheitmueller@ltnglobal.com>
> ---
> doc/indevs.texi                 |  4 +++
> libavcodec/avcodec.h            |  1 +
> libavcodec/codec_desc.c         |  6 ++++
> libavdevice/decklink_common.h   |  6 ++++
> libavdevice/decklink_common_c.h |  1 +
> libavdevice/decklink_dec.cpp    | 61 ++++++++++++++++++++++++++++++++++++++++-
> libavdevice/decklink_dec_c.c    |  1 +
> libavdevice/version.h           |  2 +-
> 8 files changed, 80 insertions(+), 2 deletions(-)
>
> diff --git a/doc/indevs.texi b/doc/indevs.texi
> index 4760d70..63dfbd4 100644
> --- a/doc/indevs.texi
> +++ b/doc/indevs.texi
> @@ -323,6 +323,10 @@ Defaults to @samp{1073741824}.
> Sets the audio sample bit depth. Must be @samp{16} or @samp{32}.
> Defaults to @samp{16}.
> 
> +@item enable_scte_104

You can loose the "enable_" from the name if you want, some other 
boolean options don't have it. I don't mind either way.

> +If set to @samp{true}, enables capture of SCTE-104 packets over SDI and
> +creation of the corresponding stream at startup.  Defaults to @samp{false}.
> +
> @end table
> 
> @subsection Examples
> diff --git a/libavcodec/avcodec.h b/libavcodec/avcodec.h
> index 5fa028e..c61b8a1 100644
> --- a/libavcodec/avcodec.h
> +++ b/libavcodec/avcodec.h
> @@ -668,6 +668,7 @@ enum AVCodecID {
>     AV_CODEC_ID_TTF = 0x18000,
>
>     AV_CODEC_ID_SCTE_35, ///< Contain timestamp estimated through PCR of program stream.
> +    AV_CODEC_ID_SCTE_104,
>     AV_CODEC_ID_BINTEXT    = 0x18800,
>     AV_CODEC_ID_XBIN,
>     AV_CODEC_ID_IDF,
> diff --git a/libavcodec/codec_desc.c b/libavcodec/codec_desc.c
> index c3688de..e198985 100644
> --- a/libavcodec/codec_desc.c
> +++ b/libavcodec/codec_desc.c
> @@ -3103,6 +3103,12 @@ static const AVCodecDescriptor codec_descriptors[] = {
>         .name      = "scte_35",
>         .long_name = NULL_IF_CONFIG_SMALL("SCTE 35 Message Queue"),
>     },
> +    {
> +        .id        = AV_CODEC_ID_SCTE_104,
> +        .type      = AVMEDIA_TYPE_DATA,
> +        .name      = "scte_104",
> +        .long_name = NULL_IF_CONFIG_SMALL("SCTE 104 Digital Program Insertion"),
> +    },
>
>     /* deprecated codec ids */
> };
> diff --git a/libavdevice/decklink_common.h b/libavdevice/decklink_common.h
> index b262780..ffc0d17 100644
> --- a/libavdevice/decklink_common.h
> +++ b/libavdevice/decklink_common.h
> @@ -41,6 +41,10 @@
>    Actual number for any particular model of card may be lower */
> #define DECKLINK_MAX_AUDIO_CHANNELS 32
> 
> +/* This isn't actually tied to the Blackmagic hardware - it's an arbitrary
> +   number used to size the array of streams */
> +#define DECKLINK_MAX_DATA_STREAMS 16

As long as only SCTE is supported, you can define this to 1...

> +
> class decklink_output_callback;
> class decklink_input_callback;
> 
> @@ -92,6 +96,8 @@ struct decklink_ctx {
>     unsigned int dropped;
>     AVStream *audio_st[DECKLINK_MAX_AUDIO_CHANNELS];
>     int num_audio_streams;
> +    AVStream *data_st[DECKLINK_MAX_DATA_STREAMS];
> +    int num_data_streams;
>     AVStream *video_st;
>     AVStream *teletext_st;
>     uint16_t cdp_sequence_num;
> diff --git a/libavdevice/decklink_common_c.h b/libavdevice/decklink_common_c.h
> index 3a21bae..3f22094 100644
> --- a/libavdevice/decklink_common_c.h
> +++ b/libavdevice/decklink_common_c.h
> @@ -58,6 +58,7 @@ struct decklink_cctx {
>     char *format_code;
>     int raw_format;
>     int64_t queue_size;
> +    int enable_scte_104;
> };
> 
> #endif /* AVDEVICE_DECKLINK_COMMON_C_H */
> diff --git a/libavdevice/decklink_dec.cpp b/libavdevice/decklink_dec.cpp
> index bab3588..1074dc7 100644
> --- a/libavdevice/decklink_dec.cpp
> +++ b/libavdevice/decklink_dec.cpp
> @@ -674,6 +674,30 @@ error:
>     return ret;
> }
> 
> +static int setup_data(AVFormatContext *avctx)

I'd rather call this setup_data_streams.

> +{
> +    struct decklink_cctx *cctx = (struct decklink_cctx *)avctx->priv_data;
> +    struct decklink_ctx *ctx = (struct decklink_ctx *)cctx->ctx;
> +    AVStream *st;
> +
> +    if (cctx->enable_scte_104) {
> +        st = avformat_new_stream(avctx, NULL);
> +        if (!st) {
> +            av_log(avctx, AV_LOG_ERROR, "Cannot add data stream\n");
> +            return AVERROR(ENOMEM);
> +        }
> +        st->codecpar->codec_type  = AVMEDIA_TYPE_DATA;
> +        st->time_base.den         = ctx->bmd_tb_den;
> +        st->time_base.num         = ctx->bmd_tb_num;
> +        st->codecpar->codec_id    = AV_CODEC_ID_SCTE_104;
> +        avpriv_set_pts_info(st, 64, 1, 1000000);  /* 64 bits pts in us */
> +        ctx->data_st[ctx->num_data_streams] = st;
> +        ctx->num_data_streams++;
> +    }
> +
> +    return 0;
> +}
> +
> #if CONFIG_LIBKLVANC
> /* VANC Callbacks */
> struct vanc_cb_ctx {
> @@ -733,12 +757,44 @@ static int cb_EIA_708B(void *callback_context, struct klvanc_context_s *ctx,
>     return 0;
> }
> 
> +static int cb_SCTE_104(void *callback_context, struct klvanc_context_s *ctx,
> +                       struct klvanc_packet_scte_104_s *pkt)
> +{
> +    struct vanc_cb_ctx *cb_ctx = (struct vanc_cb_ctx *)callback_context;
> +    decklink_cctx *decklink_cctx = (struct decklink_cctx *)cb_ctx->avctx->priv_data;
> +    struct decklink_ctx *decklink_ctx = (struct decklink_ctx *)decklink_cctx->ctx;
> +    AVPacket avpkt;
> +    av_init_packet(&avpkt);
> +
> +    avpkt.stream_index = -1;
> +    for (int i = 0; i < decklink_ctx->num_data_streams; i++) {
> +        if (decklink_ctx->data_st[i]->codecpar->codec_id = AV_CODEC_ID_SCTE_104) {
> +            avpkt.stream_index = decklink_ctx->data_st[i]->index;
> +            break;
> +        }
> +    }

You might just simply save the scte_stream_id in the context, I don't 
quite see what might be the benefit of dynamically finding the stream.

> +    if (avpkt.stream_index == -1) {
> +        /* SCTE-104 packet received but forwarding is disabled */
> +        return 0;
> +    }
> +
> +    avpkt.pts = cb_ctx->pkt->pts;
> +    avpkt.dts = cb_ctx->pkt->dts;
> +    avpkt.data = pkt->payload;
> +    avpkt.size = pkt->payloadLengthBytes;
> +    if (avpacket_queue_put(&decklink_ctx->queue, &avpkt) < 0) {
> +        ++decklink_ctx->dropped;
> +    }
> +
> +    return 0;
> +}
> +
> static struct klvanc_callbacks_s callbacks =
> {
>     cb_AFD,
>     cb_EIA_708B,
>     NULL,
> -    NULL,
> +    cb_SCTE_104,
>     NULL,
>     NULL,
> };
> @@ -1289,6 +1345,9 @@ av_cold int ff_decklink_read_header(AVFormatContext *avctx)
>         ctx->teletext_st = st;
>     }
> 
> +    /* Setup streams. */
> +    setup_data(avctx);
> +
>     if (cctx->audio_mode == AUDIO_MODE_BUNDLED) {
>         av_log(avctx, AV_LOG_VERBOSE, "Using %d input audio channels\n", ctx->audio_st[0]->codecpar->channels);
>         result = ctx->dli->EnableAudioInput(bmdAudioSampleRate48kHz,
> diff --git a/libavdevice/decklink_dec_c.c b/libavdevice/decklink_dec_c.c
> index fe8e9fd..35895fb 100644
> --- a/libavdevice/decklink_dec_c.c
> +++ b/libavdevice/decklink_dec_c.c
> @@ -76,6 +76,7 @@ static const AVOption options[] = {
>     { "draw_bars",     "draw bars on signal loss" , OFFSET(draw_bars),    AV_OPT_TYPE_BOOL,  { .i64 = 1}, 0, 1, DEC },
>     { "queue_size",    "input queue buffer size",   OFFSET(queue_size),   AV_OPT_TYPE_INT64, { .i64 = (1024 * 1024 * 1024)}, 0, INT64_MAX, DEC },
>     { "audio_depth",   "audio bitdepth (16 or 32)", OFFSET(audio_depth),  AV_OPT_TYPE_INT,   { .i64 = 16}, 16, 32, DEC },
> +    { "enable_scte_104", "capture SCTE-104 VANC", OFFSET(enable_scte_104), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, DEC, "enable_scte_104"},

AV_OPT_TYPE_BOOL

>     { NULL },
> };
> 
> diff --git a/libavdevice/version.h b/libavdevice/version.h
> index 364404d..0d4477f 100644
> --- a/libavdevice/version.h
> +++ b/libavdevice/version.h
> @@ -29,7 +29,7 @@
> 
> #define LIBAVDEVICE_VERSION_MAJOR  58
> #define LIBAVDEVICE_VERSION_MINOR   0
> -#define LIBAVDEVICE_VERSION_MICRO 100
> +#define LIBAVDEVICE_VERSION_MICRO 101
> 
> #define LIBAVDEVICE_VERSION_INT AV_VERSION_INT(LIBAVDEVICE_VERSION_MAJOR, \
>                                                LIBAVDEVICE_VERSION_MINOR, \
> --

Regards,
Marton

Patch hide | download patch | download mbox

diff --git a/doc/indevs.texi b/doc/indevs.texi
index 4760d70..63dfbd4 100644
--- a/doc/indevs.texi
+++ b/doc/indevs.texi
@@ -323,6 +323,10 @@  Defaults to @samp{1073741824}.
 Sets the audio sample bit depth. Must be @samp{16} or @samp{32}.
 Defaults to @samp{16}.
 
+@item enable_scte_104
+If set to @samp{true}, enables capture of SCTE-104 packets over SDI and
+creation of the corresponding stream at startup.  Defaults to @samp{false}.
+
 @end table
 
 @subsection Examples
diff --git a/libavcodec/avcodec.h b/libavcodec/avcodec.h
index 5fa028e..c61b8a1 100644
--- a/libavcodec/avcodec.h
+++ b/libavcodec/avcodec.h
@@ -668,6 +668,7 @@  enum AVCodecID {
     AV_CODEC_ID_TTF = 0x18000,
 
     AV_CODEC_ID_SCTE_35, ///< Contain timestamp estimated through PCR of program stream.
+    AV_CODEC_ID_SCTE_104,
     AV_CODEC_ID_BINTEXT    = 0x18800,
     AV_CODEC_ID_XBIN,
     AV_CODEC_ID_IDF,
diff --git a/libavcodec/codec_desc.c b/libavcodec/codec_desc.c
index c3688de..e198985 100644
--- a/libavcodec/codec_desc.c
+++ b/libavcodec/codec_desc.c
@@ -3103,6 +3103,12 @@  static const AVCodecDescriptor codec_descriptors[] = {
         .name      = "scte_35",
         .long_name = NULL_IF_CONFIG_SMALL("SCTE 35 Message Queue"),
     },
+    {
+        .id        = AV_CODEC_ID_SCTE_104,
+        .type      = AVMEDIA_TYPE_DATA,
+        .name      = "scte_104",
+        .long_name = NULL_IF_CONFIG_SMALL("SCTE 104 Digital Program Insertion"),
+    },
 
     /* deprecated codec ids */
 };
diff --git a/libavdevice/decklink_common.h b/libavdevice/decklink_common.h
index b262780..ffc0d17 100644
--- a/libavdevice/decklink_common.h
+++ b/libavdevice/decklink_common.h
@@ -41,6 +41,10 @@ 
    Actual number for any particular model of card may be lower */
 #define DECKLINK_MAX_AUDIO_CHANNELS 32
 
+/* This isn't actually tied to the Blackmagic hardware - it's an arbitrary
+   number used to size the array of streams */
+#define DECKLINK_MAX_DATA_STREAMS 16
+
 class decklink_output_callback;
 class decklink_input_callback;
 
@@ -92,6 +96,8 @@  struct decklink_ctx {
     unsigned int dropped;
     AVStream *audio_st[DECKLINK_MAX_AUDIO_CHANNELS];
     int num_audio_streams;
+    AVStream *data_st[DECKLINK_MAX_DATA_STREAMS];
+    int num_data_streams;
     AVStream *video_st;
     AVStream *teletext_st;
     uint16_t cdp_sequence_num;
diff --git a/libavdevice/decklink_common_c.h b/libavdevice/decklink_common_c.h
index 3a21bae..3f22094 100644
--- a/libavdevice/decklink_common_c.h
+++ b/libavdevice/decklink_common_c.h
@@ -58,6 +58,7 @@  struct decklink_cctx {
     char *format_code;
     int raw_format;
     int64_t queue_size;
+    int enable_scte_104;
 };
 
 #endif /* AVDEVICE_DECKLINK_COMMON_C_H */
diff --git a/libavdevice/decklink_dec.cpp b/libavdevice/decklink_dec.cpp
index bab3588..1074dc7 100644
--- a/libavdevice/decklink_dec.cpp
+++ b/libavdevice/decklink_dec.cpp
@@ -674,6 +674,30 @@  error:
     return ret;
 }
 
+static int setup_data(AVFormatContext *avctx)
+{
+    struct decklink_cctx *cctx = (struct decklink_cctx *)avctx->priv_data;
+    struct decklink_ctx *ctx = (struct decklink_ctx *)cctx->ctx;
+    AVStream *st;
+
+    if (cctx->enable_scte_104) {
+        st = avformat_new_stream(avctx, NULL);
+        if (!st) {
+            av_log(avctx, AV_LOG_ERROR, "Cannot add data stream\n");
+            return AVERROR(ENOMEM);
+        }
+        st->codecpar->codec_type  = AVMEDIA_TYPE_DATA;
+        st->time_base.den         = ctx->bmd_tb_den;
+        st->time_base.num         = ctx->bmd_tb_num;
+        st->codecpar->codec_id    = AV_CODEC_ID_SCTE_104;
+        avpriv_set_pts_info(st, 64, 1, 1000000);  /* 64 bits pts in us */
+        ctx->data_st[ctx->num_data_streams] = st;
+        ctx->num_data_streams++;
+    }
+
+    return 0;
+}
+
 #if CONFIG_LIBKLVANC
 /* VANC Callbacks */
 struct vanc_cb_ctx {
@@ -733,12 +757,44 @@  static int cb_EIA_708B(void *callback_context, struct klvanc_context_s *ctx,
     return 0;
 }
 
+static int cb_SCTE_104(void *callback_context, struct klvanc_context_s *ctx,
+                       struct klvanc_packet_scte_104_s *pkt)
+{
+    struct vanc_cb_ctx *cb_ctx = (struct vanc_cb_ctx *)callback_context;
+    decklink_cctx *decklink_cctx = (struct decklink_cctx *)cb_ctx->avctx->priv_data;
+    struct decklink_ctx *decklink_ctx = (struct decklink_ctx *)decklink_cctx->ctx;
+    AVPacket avpkt;
+    av_init_packet(&avpkt);
+
+    avpkt.stream_index = -1;
+    for (int i = 0; i < decklink_ctx->num_data_streams; i++) {
+        if (decklink_ctx->data_st[i]->codecpar->codec_id = AV_CODEC_ID_SCTE_104) {
+            avpkt.stream_index = decklink_ctx->data_st[i]->index;
+            break;
+        }
+    }
+    if (avpkt.stream_index == -1) {
+        /* SCTE-104 packet received but forwarding is disabled */
+        return 0;
+    }
+
+    avpkt.pts = cb_ctx->pkt->pts;
+    avpkt.dts = cb_ctx->pkt->dts;
+    avpkt.data = pkt->payload;
+    avpkt.size = pkt->payloadLengthBytes;
+    if (avpacket_queue_put(&decklink_ctx->queue, &avpkt) < 0) {
+        ++decklink_ctx->dropped;
+    }
+
+    return 0;
+}
+
 static struct klvanc_callbacks_s callbacks =
 {
     cb_AFD,
     cb_EIA_708B,
     NULL,
-    NULL,
+    cb_SCTE_104,
     NULL,
     NULL,
 };
@@ -1289,6 +1345,9 @@  av_cold int ff_decklink_read_header(AVFormatContext *avctx)
         ctx->teletext_st = st;
     }
 
+    /* Setup streams. */
+    setup_data(avctx);
+
     if (cctx->audio_mode == AUDIO_MODE_BUNDLED) {
         av_log(avctx, AV_LOG_VERBOSE, "Using %d input audio channels\n", ctx->audio_st[0]->codecpar->channels);
         result = ctx->dli->EnableAudioInput(bmdAudioSampleRate48kHz,
diff --git a/libavdevice/decklink_dec_c.c b/libavdevice/decklink_dec_c.c
index fe8e9fd..35895fb 100644
--- a/libavdevice/decklink_dec_c.c
+++ b/libavdevice/decklink_dec_c.c
@@ -76,6 +76,7 @@  static const AVOption options[] = {
     { "draw_bars",     "draw bars on signal loss" , OFFSET(draw_bars),    AV_OPT_TYPE_BOOL,  { .i64 = 1}, 0, 1, DEC },
     { "queue_size",    "input queue buffer size",   OFFSET(queue_size),   AV_OPT_TYPE_INT64, { .i64 = (1024 * 1024 * 1024)}, 0, INT64_MAX, DEC },
     { "audio_depth",   "audio bitdepth (16 or 32)", OFFSET(audio_depth),  AV_OPT_TYPE_INT,   { .i64 = 16}, 16, 32, DEC },
+    { "enable_scte_104", "capture SCTE-104 VANC", OFFSET(enable_scte_104), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, DEC, "enable_scte_104"},
     { NULL },
 };
 
diff --git a/libavdevice/version.h b/libavdevice/version.h
index 364404d..0d4477f 100644
--- a/libavdevice/version.h
+++ b/libavdevice/version.h
@@ -29,7 +29,7 @@ 
 
 #define LIBAVDEVICE_VERSION_MAJOR  58
 #define LIBAVDEVICE_VERSION_MINOR   0
-#define LIBAVDEVICE_VERSION_MICRO 100
+#define LIBAVDEVICE_VERSION_MICRO 101
 
 #define LIBAVDEVICE_VERSION_INT AV_VERSION_INT(LIBAVDEVICE_VERSION_MAJOR, \
                                                LIBAVDEVICE_VERSION_MINOR, \