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. + +@item signal_loss_action +Sets the action to take in the event of a signal loss. Accepts one of the +following values: + +@table @option +@item 1, none +Do nothing on signal loss. This usually results in black frames. +@item 2, bars +Draw color bars on signal loss. Only supported for 8-bit input signals. +@item 3, repeat +Repeat the last video frame on signal loss. +@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..54d698bc12 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) @@ -729,6 +733,7 @@ HRESULT decklink_input_callback::VideoInputFrameArrived( BMDTimeValue frameTime; BMDTimeValue frameDuration; int64_t wallclock = 0, abs_wallclock = 0; + int64_t video_pkt_pts, audio_pkt_pts; struct decklink_cctx *cctx = (struct decklink_cctx *) avctx->priv_data; if (ctx->autodetect) { @@ -755,6 +760,8 @@ HRESULT decklink_input_callback::VideoInputFrameArrived( wallclock = av_gettime_relative(); if (ctx->audio_pts_source == PTS_SRC_ABS_WALLCLOCK || ctx->video_pts_source == PTS_SRC_ABS_WALLCLOCK) abs_wallclock = av_gettime(); + video_pkt_pts = get_pkt_pts(videoFrame, audioFrame, wallclock, abs_wallclock, ctx->video_pts_source, ctx->video_st->time_base, &initial_video_pts, cctx->copyts); + audio_pkt_pts = get_pkt_pts(videoFrame, audioFrame, wallclock, abs_wallclock, ctx->audio_pts_source, ctx->audio_st->time_base, &initial_audio_pts, cctx->copyts); // Handle Video Frame if (videoFrame) { @@ -773,7 +780,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 +792,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) { + videoFrame = last_video_frame; + videoFrame->GetBytes(&frameBytes); } if (!no_video) { @@ -793,6 +803,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); @@ -846,7 +862,7 @@ HRESULT decklink_input_callback::VideoInputFrameArrived( return S_OK; } - pkt.pts = get_pkt_pts(videoFrame, audioFrame, wallclock, abs_wallclock, ctx->video_pts_source, ctx->video_st->time_base, &initial_video_pts, cctx->copyts); + pkt.pts = video_pkt_pts; pkt.dts = pkt.pts; pkt.duration = frameDuration; @@ -949,7 +965,7 @@ HRESULT decklink_input_callback::VideoInputFrameArrived( pkt.size = audioFrame->GetSampleFrameCount() * ctx->audio_st->codecpar->ch_layout.nb_channels * (ctx->audio_depth / 8); audioFrame->GetBytes(&audioFrameBytes); audioFrame->GetPacketTime(&audio_pts, ctx->audio_st->time_base.den); - pkt.pts = get_pkt_pts(videoFrame, audioFrame, wallclock, abs_wallclock, ctx->audio_pts_source, ctx->audio_st->time_base, &initial_audio_pts, cctx->copyts); + pkt.pts = audio_pkt_pts; pkt.dts = pkt.pts; //fprintf(stderr,"Audio Frame size %d ts %d\n", pkt.size, pkt.pts); @@ -1036,6 +1052,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 +1091,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 }, }; diff --git a/libavdevice/version.h b/libavdevice/version.h index 5cd01a1672..0796e41221 100644 --- a/libavdevice/version.h +++ b/libavdevice/version.h @@ -30,7 +30,7 @@ #include "version_major.h" #define LIBAVDEVICE_VERSION_MINOR 2 -#define LIBAVDEVICE_VERSION_MICRO 100 +#define LIBAVDEVICE_VERSION_MICRO 101 #define LIBAVDEVICE_VERSION_INT AV_VERSION_INT(LIBAVDEVICE_VERSION_MAJOR, \ LIBAVDEVICE_VERSION_MINOR, \