[FFmpeg-devel] [PATCH v4] libavdevice/decklink: extend available actions on signal loss
Marton Balint
cus at passwd.hu
Tue Sep 10 00:49:56 EEST 2024
On Tue, 11 Jun 2024, Michael Riedl wrote:
> Deprecate the option 'draw_bars' in favor of the new option 'signal_loss_action',
> which controls the behavior when the input signal is not available
> (including the behavior previously available through draw_bars).
> The default behavior remains unchanged to be backwards compatible.
> The new option is more flexible for extending now and in the future.
>
> The new value 'repeat' repeats the last video frame.
> This is useful for very short dropouts and was not available before.
Sorry it took this long, but I finally checked the patch and applied a
slightly simplifed version.
Thanks,
Marton
>
> Signed-off-by: Michael Riedl <michael.riedl at nativewaves.com>
> ---
> doc/indevs.texi | 16 ++++++++++++++++
> libavdevice/decklink_common.h | 1 +
> libavdevice/decklink_common_c.h | 7 +++++++
> libavdevice/decklink_dec.cpp | 34 ++++++++++++++++++++++++++++-----
> libavdevice/decklink_dec_c.c | 6 +++++-
> 5 files changed, 58 insertions(+), 6 deletions(-)
>
> diff --git a/doc/indevs.texi b/doc/indevs.texi
> index 734fc65752..cdf44a6638 100644
> --- a/doc/indevs.texi
> +++ b/doc/indevs.texi
> @@ -396,6 +396,22 @@ Defaults to @samp{audio}.
> @item draw_bars
> If set to @samp{true}, color bars are drawn in the event of a signal loss.
> Defaults to @samp{true}.
> +This option is deprecated, please use the @code{signal_loss_action} option.
> +
> + at item signal_loss_action
> +Sets the action to take in the event of a signal loss. Accepts one of the
> +following values:
> +
> + at table @option
> + at item 1, none
> +Do nothing on signal loss. This usually results in black frames.
> + at item 2, bars
> +Draw color bars on signal loss. Only supported for 8-bit input signals.
> + at item 3, repeat
> +Repeat the last video frame on signal loss.
> + at end table
> +
> +Defaults to @samp{bars}.
>
> @item queue_size
> Sets maximum input buffer size in bytes. If the buffering reaches this value,
> diff --git a/libavdevice/decklink_common.h b/libavdevice/decklink_common.h
> index c54a635876..6b32dc2d09 100644
> --- a/libavdevice/decklink_common.h
> +++ b/libavdevice/decklink_common.h
> @@ -147,6 +147,7 @@ struct decklink_ctx {
> DecklinkPtsSource video_pts_source;
> int draw_bars;
> BMDPixelFormat raw_format;
> + DecklinkSignalLossAction signal_loss_action;
>
> int frames_preroll;
> int frames_buffer;
> diff --git a/libavdevice/decklink_common_c.h b/libavdevice/decklink_common_c.h
> index 9c55d89149..53d2c583e7 100644
> --- a/libavdevice/decklink_common_c.h
> +++ b/libavdevice/decklink_common_c.h
> @@ -37,6 +37,12 @@ typedef enum DecklinkPtsSource {
> PTS_SRC_NB
> } DecklinkPtsSource;
>
> +typedef enum DecklinkSignalLossAction {
> + SIGNAL_LOSS_NONE = 1,
> + SIGNAL_LOSS_REPEAT = 2,
> + SIGNAL_LOSS_BARS = 3
> +} DecklinkSignalLossAction;
> +
> struct decklink_cctx {
> const AVClass *cclass;
>
> @@ -68,6 +74,7 @@ struct decklink_cctx {
> int64_t timestamp_align;
> int timing_offset;
> int wait_for_tc;
> + DecklinkSignalLossAction signal_loss_action;
> };
>
> #endif /* AVDEVICE_DECKLINK_COMMON_C_H */
> diff --git a/libavdevice/decklink_dec.cpp b/libavdevice/decklink_dec.cpp
> index 671573853b..d057366378 100644
> --- a/libavdevice/decklink_dec.cpp
> +++ b/libavdevice/decklink_dec.cpp
> @@ -593,6 +593,7 @@ private:
> int no_video;
> int64_t initial_video_pts;
> int64_t initial_audio_pts;
> + IDeckLinkVideoInputFrame* last_video_frame;
> };
>
> decklink_input_callback::decklink_input_callback(AVFormatContext *_avctx) : _refs(1)
> @@ -602,10 +603,13 @@ decklink_input_callback::decklink_input_callback(AVFormatContext *_avctx) : _ref
> ctx = (struct decklink_ctx *)cctx->ctx;
> no_video = 0;
> initial_audio_pts = initial_video_pts = AV_NOPTS_VALUE;
> + last_video_frame = nullptr;
> }
>
> decklink_input_callback::~decklink_input_callback()
> {
> + if (last_video_frame)
> + last_video_frame->Release();
> }
>
> ULONG decklink_input_callback::AddRef(void)
> @@ -730,6 +734,7 @@ HRESULT decklink_input_callback::VideoInputFrameArrived(
> BMDTimeValue frameDuration;
> int64_t wallclock = 0, abs_wallclock = 0;
> struct decklink_cctx *cctx = (struct decklink_cctx *) avctx->priv_data;
> + IDeckLinkVideoInputFrame *currentVideoFrame = videoFrame; // video frame that holds frameBytes
>
> if (ctx->autodetect) {
> if (videoFrame && !(videoFrame->GetFlags() & bmdFrameHasNoInputSource) &&
> @@ -773,7 +778,7 @@ HRESULT decklink_input_callback::VideoInputFrameArrived(
> ctx->video_st->time_base.den);
>
> if (videoFrame->GetFlags() & bmdFrameHasNoInputSource) {
> - if (ctx->draw_bars && videoFrame->GetPixelFormat() == bmdFormat8BitYUV) {
> + if (ctx->signal_loss_action == SIGNAL_LOSS_BARS && videoFrame->GetPixelFormat() == bmdFormat8BitYUV) {
> unsigned bars[8] = {
> 0xEA80EA80, 0xD292D210, 0xA910A9A5, 0x90229035,
> 0x6ADD6ACA, 0x51EF515A, 0x286D28EF, 0x10801080 };
> @@ -785,6 +790,9 @@ HRESULT decklink_input_callback::VideoInputFrameArrived(
> for (int x = 0; x < width; x += 2)
> *p++ = bars[(x * 8) / width];
> }
> + } else if (ctx->signal_loss_action == SIGNAL_LOSS_REPEAT && last_video_frame) {
> + currentVideoFrame = last_video_frame;
> + currentVideoFrame->GetBytes(&frameBytes);
> }
>
> if (!no_video) {
> @@ -793,6 +801,12 @@ HRESULT decklink_input_callback::VideoInputFrameArrived(
> }
> no_video = 1;
> } else {
> + if (ctx->signal_loss_action == SIGNAL_LOSS_REPEAT) {
> + if (last_video_frame)
> + last_video_frame->Release();
> + last_video_frame = videoFrame;
> + last_video_frame->AddRef();
> + }
> if (no_video) {
> av_log(avctx, AV_LOG_WARNING, "Frame received (#%lu) - Input returned "
> "- Frames dropped %u\n", ctx->frameCount, ++ctx->dropped);
> @@ -854,8 +868,8 @@ HRESULT decklink_input_callback::VideoInputFrameArrived(
> pkt.flags |= AV_PKT_FLAG_KEY;
> pkt.stream_index = ctx->video_st->index;
> pkt.data = (uint8_t *)frameBytes;
> - pkt.size = videoFrame->GetRowBytes() *
> - videoFrame->GetHeight();
> + pkt.size = currentVideoFrame->GetRowBytes() *
> + currentVideoFrame->GetHeight();
> //fprintf(stderr,"Video Frame size %d ts %d\n", pkt.size, pkt.pts);
>
> if (!no_video) {
> @@ -931,9 +945,9 @@ HRESULT decklink_input_callback::VideoInputFrameArrived(
> }
> }
>
> - pkt.buf = av_buffer_create(pkt.data, pkt.size, decklink_object_free, videoFrame, 0);
> + pkt.buf = av_buffer_create(pkt.data, pkt.size, decklink_object_free, currentVideoFrame, 0);
> if (pkt.buf)
> - videoFrame->AddRef();
> + currentVideoFrame->AddRef();
>
> if (ff_decklink_packet_queue_put(&ctx->queue, &pkt) < 0) {
> ++ctx->dropped;
> @@ -1036,6 +1050,7 @@ av_cold int ff_decklink_read_close(AVFormatContext *avctx)
> ctx->dli->StopStreams();
> ctx->dli->DisableVideoInput();
> ctx->dli->DisableAudioInput();
> + ctx->dli->SetCallback(nullptr);
> }
>
> ff_decklink_cleanup(avctx);
> @@ -1074,6 +1089,15 @@ av_cold int ff_decklink_read_header(AVFormatContext *avctx)
> ctx->audio_pts_source = cctx->audio_pts_source;
> ctx->video_pts_source = cctx->video_pts_source;
> ctx->draw_bars = cctx->draw_bars;
> + ctx->signal_loss_action = cctx->signal_loss_action;
> + if (!ctx->draw_bars && ctx->signal_loss_action == SIGNAL_LOSS_BARS) {
> + ctx->signal_loss_action = SIGNAL_LOSS_NONE;
> + av_log(avctx, AV_LOG_WARNING, "Setting signal_loss_action to none because draw_bars is false\n");
> + }
> + if (!ctx->draw_bars && ctx->signal_loss_action != SIGNAL_LOSS_NONE) {
> + av_log(avctx, AV_LOG_ERROR, "options draw_bars and signal_loss_action are mutually exclusive\n");
> + return AVERROR(EINVAL);
> + }
> ctx->audio_depth = cctx->audio_depth;
> if (cctx->raw_format > 0 && (unsigned int)cctx->raw_format < FF_ARRAY_ELEMS(decklink_raw_format_map))
> ctx->raw_format = decklink_raw_format_map[cctx->raw_format];
> diff --git a/libavdevice/decklink_dec_c.c b/libavdevice/decklink_dec_c.c
> index e211c9d3f4..b8cdb7bd8e 100644
> --- a/libavdevice/decklink_dec_c.c
> +++ b/libavdevice/decklink_dec_c.c
> @@ -95,12 +95,16 @@ static const AVOption options[] = {
> { "reference", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = PTS_SRC_REFERENCE}, 0, 0, DEC, .unit = "pts_source"},
> { "wallclock", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = PTS_SRC_WALLCLOCK}, 0, 0, DEC, .unit = "pts_source"},
> { "abs_wallclock", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = PTS_SRC_ABS_WALLCLOCK}, 0, 0, DEC, .unit = "pts_source"},
> - { "draw_bars", "draw bars on signal loss" , OFFSET(draw_bars), AV_OPT_TYPE_BOOL, { .i64 = 1}, 0, 1, DEC },
> + { "draw_bars", "use option signal_loss_action instead" , OFFSET(draw_bars), AV_OPT_TYPE_BOOL, { .i64 = 1}, 0, 1, DEC | AV_OPT_FLAG_DEPRECATED },
> { "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 },
> { "decklink_copyts", "copy timestamps, do not remove the initial offset", OFFSET(copyts), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, DEC },
> { "timestamp_align", "capture start time alignment (in seconds)", OFFSET(timestamp_align), AV_OPT_TYPE_DURATION, { .i64 = 0 }, 0, INT_MAX, DEC },
> { "wait_for_tc", "drop frames till a frame with timecode is received. TC format must be set", OFFSET(wait_for_tc), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, DEC },
> + { "signal_loss_action", "action on signal loss", OFFSET(signal_loss_action), AV_OPT_TYPE_INT, { .i64 = SIGNAL_LOSS_BARS }, SIGNAL_LOSS_NONE, SIGNAL_LOSS_BARS, DEC, .unit = "signal_loss_action" },
> + { "none", "do not do anything (usually leads to black frames)", 0, AV_OPT_TYPE_CONST, { .i64 = SIGNAL_LOSS_NONE }, 0, 0, DEC, .unit = "signal_loss_action"},
> + { "bars", "draw color bars (only supported for 8-bit signals)", 0, AV_OPT_TYPE_CONST, { .i64 = SIGNAL_LOSS_BARS }, 0, 0, DEC, .unit = "signal_loss_action"},
> + { "repeat", "repeat the last video frame", 0, AV_OPT_TYPE_CONST, { .i64 = SIGNAL_LOSS_REPEAT }, 0, 0, DEC, .unit = "signal_loss_action"},
> { NULL },
> };
>
> --
> 2.45.2
>
> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel at ffmpeg.org
> https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
>
> To unsubscribe, visit link above, or email
> ffmpeg-devel-request at ffmpeg.org with subject "unsubscribe".
>
More information about the ffmpeg-devel
mailing list