diff mbox

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

Message ID 20171229181230.99473-8-dheitmueller@ltnglobal.com
State Superseded
Headers show

Commit Message

Devin Heitmueller Dec. 29, 2017, 6:12 p.m. UTC
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 hoooked 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>

Signed-off-by: Devin Heitmueller <dheitmueller@ltnglobal.com>
---
 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 +-
 7 files changed, 76 insertions(+), 2 deletions(-)

Comments

Aaron Levinson Dec. 30, 2017, 8:11 a.m. UTC | #1
On 12/29/2017 10:12 AM, 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 hoooked into libklscte35 we'll be able

"hoooked" -> "hooked"

> 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>
> 
> Signed-off-by: Devin Heitmueller <dheitmueller@ltnglobal.com>
> ---
>   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 +-
>   7 files changed, 76 insertions(+), 2 deletions(-)
> 
> diff --git a/libavcodec/avcodec.h b/libavcodec/avcodec.h
> index 4f7b6df09d..2b4a0bd669 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 c3688de1d6..e198985bb4 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 06b241029e..4d2052ea79 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 02011ed53b..cb73ec990b 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 86db6d8fbd..f3f0d989bf 100644
> --- a/libavdevice/decklink_dec.cpp
> +++ b/libavdevice/decklink_dec.cpp
> @@ -672,6 +672,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;

I wonder if there is any reason to set time_base here.  Aren't the 
triggers relatively infrequent?

> +        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 {
> @@ -731,12 +755,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,
>   };
> @@ -1285,6 +1341,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_DISCRETE) {
>           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 d3d8c848cf..122da08fa1 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 364404d65e..0d4477f82e 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, \
> 

Aaron Levinson
Devin Heitmueller Jan. 5, 2018, 7:40 p.m. UTC | #2
> On Dec 30, 2017, at 3:11 AM, Aaron Levinson <alevinsn_dev@levland.net> wrote:
> 
> On 12/29/2017 10:12 AM, 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 hoooked into libklscte35 we'll be able
> 
> "hoooked" -> “hooked"

Ok.

>> +        }
>> +        st->codecpar->codec_type  = AVMEDIA_TYPE_DATA;
>> +        st->time_base.den         = ctx->bmd_tb_den;
>> +        st->time_base.num         = ctx->bmd_tb_num;
> 
> I wonder if there is any reason to set time_base here.  Aren't the triggers relatively infrequent?

The triggers are relatively infrequent, but we want them to be frame accurate and when they do occur they may happen in bursts over multiple frames, and we want the clock to be accurate enough to ensure they are inserted into the correct video frame.  Putting them on the same clock as the video helps with that.

Devin
diff mbox

Patch

diff --git a/libavcodec/avcodec.h b/libavcodec/avcodec.h
index 4f7b6df09d..2b4a0bd669 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 c3688de1d6..e198985bb4 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 06b241029e..4d2052ea79 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 02011ed53b..cb73ec990b 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 86db6d8fbd..f3f0d989bf 100644
--- a/libavdevice/decklink_dec.cpp
+++ b/libavdevice/decklink_dec.cpp
@@ -672,6 +672,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 {
@@ -731,12 +755,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,
 };
@@ -1285,6 +1341,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_DISCRETE) {
         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 d3d8c848cf..122da08fa1 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 364404d65e..0d4477f82e 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, \