From ff373bb4a87a5b17dfa82aba5c7e74b125298fa8 Mon Sep 17 00:00:00 2001 From: Marton Balint Date: Sat, 5 Sep 2020 23:29:24 +0200 Subject: [PATCH] avdevice/decklink_dec: add support for 50/60 fps timecode Signed-off-by: Marton Balint --- doc/indevs.texi | 5 +++ libavdevice/decklink_dec.cpp | 64 +++++++++++++++++++++++++++++------- 2 files changed, 57 insertions(+), 12 deletions(-) diff --git a/doc/indevs.texi b/doc/indevs.texi index 62b6ebb3e3..3924d03908 100644 --- a/doc/indevs.texi +++ b/doc/indevs.texi @@ -353,6 +353,11 @@ Timecode type to include in the frame and video stream metadata. Must be @samp{rp188hfr}, @samp{rp188any}, @samp{vitc}, @samp{vitc2}, or @samp{serial}. Defaults to @samp{none} (not included). +In order to properly support 50/60 fps timecodes, the ordering of the queried +timecode types for @samp{rp188any} is HFR, VITC1, VITC2 and LTC for >30 fps +content. Note that this is slightly different to the ordering used by the +DeckLink API, which is HFR, VITC1, LTC, VITC2. + @item video_input Sets the video input source. Must be @samp{unset}, @samp{sdi}, @samp{hdmi}, @samp{optical_sdi}, @samp{component}, @samp{composite} or @samp{s_video}. diff --git a/libavdevice/decklink_dec.cpp b/libavdevice/decklink_dec.cpp index 2e41b587e8..090437a6b5 100644 --- a/libavdevice/decklink_dec.cpp +++ b/libavdevice/decklink_dec.cpp @@ -789,6 +789,52 @@ static int64_t get_pkt_pts(IDeckLinkVideoInputFrame *videoFrame, return pts; } +int get_bmd_timecode(AVFormatContext *avctx, AVTimecode *tc, AVRational frame_rate, BMDTimecodeFormat tc_format, IDeckLinkVideoInputFrame *videoFrame) +{ + IDeckLinkTimecode *timecode; + int ret = AVERROR(ENOENT); +#if BLACKMAGIC_DECKLINK_API_VERSION >= 0x0b000000 + int hfr = (tc_format == bmdTimecodeRP188HighFrameRate); +#else + int hfr = 0; +#endif + if (videoFrame->GetTimecode(tc_format, &timecode) == S_OK) { + uint8_t hh, mm, ss, ff; + if (timecode->GetComponents(&hh, &mm, &ss, &ff) == S_OK) { + int flags = (timecode->GetFlags() & bmdTimecodeIsDropFrame) ? AV_TIMECODE_FLAG_DROPFRAME : 0; + if (!hfr && av_cmp_q(frame_rate, av_make_q(30, 1)) == 1) + ff = ff << 1 | !!(timecode->GetFlags() & bmdTimecodeFieldMark); + ret = av_timecode_init_from_components(tc, frame_rate, flags, hh, mm, ss, ff, avctx); + } + timecode->Release(); + } + return ret; +} + +int get_frame_timecode(AVFormatContext *avctx, decklink_ctx *ctx, AVTimecode *tc, IDeckLinkVideoInputFrame *videoFrame) +{ + AVRational frame_rate = ctx->video_st->r_frame_rate; + int ret; + /* 50/60 fps content has alternating VITC1 and VITC2 timecode (see SMPTE ST + * 12-2, section 7), so the native ordering of RP188Any (HFR, VITC1, LTC, + * VITC2) would not work because LTC might not contain the field flag. + * Therefore we query the types manually. */ + if (ctx->tc_format == bmdTimecodeRP188Any && av_cmp_q(frame_rate, av_make_q(30, 1)) == 1) { +#if BLACKMAGIC_DECKLINK_API_VERSION >= 0x0b000000 + ret = get_bmd_timecode(avctx, tc, frame_rate, bmdTimecodeRP188HighFrameRate, videoFrame); + if (ret == AVERROR(ENOENT)) +#endif + ret = get_bmd_timecode(avctx, tc, frame_rate, bmdTimecodeRP188VITC1, videoFrame); + if (ret == AVERROR(ENOENT)) + ret = get_bmd_timecode(avctx, tc, frame_rate, bmdTimecodeRP188VITC2, videoFrame); + if (ret == AVERROR(ENOENT)) + ret = get_bmd_timecode(avctx, tc, frame_rate, bmdTimecodeRP188LTC, videoFrame); + } else { + ret = get_bmd_timecode(avctx, tc, frame_rate, ctx->tc_format, videoFrame); + } + return ret; +} + HRESULT decklink_input_callback::VideoInputFrameArrived( IDeckLinkVideoInputFrame *videoFrame, IDeckLinkAudioInputPacket *audioFrame) { @@ -870,22 +916,16 @@ HRESULT decklink_input_callback::VideoInputFrameArrived( // Handle Timecode (if requested) if (ctx->tc_format) { - IDeckLinkTimecode *timecode; - if (videoFrame->GetTimecode(ctx->tc_format, &timecode) == S_OK) { - const char *tc = NULL; - DECKLINK_STR decklink_tc; - if (timecode->GetString(&decklink_tc) == S_OK) { - tc = DECKLINK_STRDUP(decklink_tc); - DECKLINK_FREE(decklink_tc); - } - timecode->Release(); + AVTimecode tcr; + if (get_frame_timecode(avctx, ctx, &tcr, videoFrame) >= 0) { + char tcstr[AV_TIMECODE_STR_SIZE]; + const char *tc = av_timecode_make_string(&tcr, tcstr, 0); if (tc) { AVDictionary* metadata_dict = NULL; int metadata_len; uint8_t* packed_metadata; - AVTimecode tcr; - if (av_timecode_init_from_string(&tcr, ctx->video_st->r_frame_rate, tc, ctx) >= 0) { + if (av_cmp_q(ctx->video_st->r_frame_rate, av_make_q(60, 1)) < 1) { uint32_t tc_data = av_timecode_get_smpte_from_framenum(&tcr, 0); int size = sizeof(uint32_t) * 4; uint32_t *sd = (uint32_t *)av_packet_new_side_data(&pkt, AV_PKT_DATA_S12M_TIMECODE, size); @@ -896,7 +936,7 @@ HRESULT decklink_input_callback::VideoInputFrameArrived( } } - if (av_dict_set(&metadata_dict, "timecode", tc, AV_DICT_DONT_STRDUP_VAL) >= 0) { + if (av_dict_set(&metadata_dict, "timecode", tc, 0) >= 0) { packed_metadata = av_packet_pack_dictionary(metadata_dict, &metadata_len); av_dict_free(&metadata_dict); if (packed_metadata) {