From 0946c0ec177dc48ef0677f890aa42d95e667c417 Mon Sep 17 00:00:00 2001 From: Jon Morley Date: Thu, 31 May 2018 02:45:07 -0700 Subject: [PATCH] avdevice/decklink_dec: capture timecode to metadata when requested If the user provides a valid timecode_format look for timecode of that format in the capture and if found store it on the video avstream's metadata. Slightly modified by Marton Balint to capture per-frame timecode as well. Signed-off-by: Marton Balint --- doc/indevs.texi | 6 +++++ libavdevice/decklink_common.h | 12 ++++++++++ libavdevice/decklink_common_c.h | 1 + libavdevice/decklink_dec.cpp | 40 +++++++++++++++++++++++++++++++++ libavdevice/decklink_dec_c.c | 9 ++++++++ libavdevice/version.h | 2 +- 6 files changed, 69 insertions(+), 1 deletion(-) diff --git a/doc/indevs.texi b/doc/indevs.texi index 6951940a93..632d1e4743 100644 --- a/doc/indevs.texi +++ b/doc/indevs.texi @@ -326,6 +326,12 @@ Defaults to @samp{2}. Sets the decklink device duplex mode. Must be @samp{unset}, @samp{half} or @samp{full}. Defaults to @samp{unset}. +@item timecode_format +Timecode type to include in the frame and video stream metadata. Must be +@samp{none}, @samp{rp188vitc}, @samp{rp188vitc2}, @samp{rp188ltc}, +@samp{rp188any}, @samp{vitc}, @samp{vitc2}, or @samp{serial}. Defaults to +@samp{none} (not included). + @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_common.h b/libavdevice/decklink_common.h index 8064abdcb9..96b001c2d8 100644 --- a/libavdevice/decklink_common.h +++ b/libavdevice/decklink_common.h @@ -93,6 +93,7 @@ struct decklink_ctx { BMDDisplayMode bmd_mode; BMDVideoConnection video_input; BMDAudioConnection audio_input; + BMDTimecodeFormat tc_format; int bmd_width; int bmd_height; int bmd_field_dominance; @@ -169,6 +170,17 @@ static const BMDVideoConnection decklink_video_connection_map[] = { bmdVideoConnectionSVideo, }; +static const BMDTimecodeFormat decklink_timecode_format_map[] = { + (BMDTimecodeFormat)0, + bmdTimecodeRP188VITC1, + bmdTimecodeRP188VITC2, + bmdTimecodeRP188LTC, + bmdTimecodeRP188Any, + bmdTimecodeVITC, + bmdTimecodeVITCField2, + bmdTimecodeSerial, +}; + HRESULT ff_decklink_get_display_name(IDeckLink *This, const char **displayName); int ff_decklink_set_configs(AVFormatContext *avctx, decklink_direction_t direction); int ff_decklink_set_format(AVFormatContext *avctx, int width, int height, int tb_num, int tb_den, enum AVFieldOrder field_order, decklink_direction_t direction = DIRECTION_OUT, int num = 0); diff --git a/libavdevice/decklink_common_c.h b/libavdevice/decklink_common_c.h index 08e9f9bbd5..32a5d70ee1 100644 --- a/libavdevice/decklink_common_c.h +++ b/libavdevice/decklink_common_c.h @@ -50,6 +50,7 @@ struct decklink_cctx { DecklinkPtsSource video_pts_source; int audio_input; int video_input; + int tc_format; int draw_bars; char *format_code; int raw_format; diff --git a/libavdevice/decklink_dec.cpp b/libavdevice/decklink_dec.cpp index 974ee1d94c..7fabef231c 100644 --- a/libavdevice/decklink_dec.cpp +++ b/libavdevice/decklink_dec.cpp @@ -752,6 +752,35 @@ HRESULT decklink_input_callback::VideoInputFrameArrived( "- Frames dropped %u\n", ctx->frameCount, ++ctx->dropped); } no_video = 0; + + // 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(); + if (tc) { + AVDictionary* metadata_dict = NULL; + int metadata_len; + uint8_t* packed_metadata; + if (av_dict_set(&metadata_dict, "timecode", tc, AV_DICT_DONT_STRDUP_VAL) >= 0) { + packed_metadata = av_packet_pack_dictionary(metadata_dict, &metadata_len); + av_dict_free(&metadata_dict); + if (packed_metadata) { + if (av_packet_add_side_data(&pkt, AV_PKT_DATA_STRINGS_METADATA, packed_metadata, metadata_len) < 0) + av_freep(&packed_metadata); + } + } + } + } else { + av_log(avctx, AV_LOG_DEBUG, "Unable to find timecode.\n"); + } + } } pkt.pts = get_pkt_pts(videoFrame, audioFrame, wallclock, abs_wallclock, ctx->video_pts_source, ctx->video_st->time_base, &initial_video_pts, cctx->copyts); @@ -969,6 +998,8 @@ av_cold int ff_decklink_read_header(AVFormatContext *avctx) ctx->teletext_lines = cctx->teletext_lines; ctx->preroll = cctx->preroll; ctx->duplex_mode = cctx->duplex_mode; + if (cctx->tc_format > 0 && (unsigned int)cctx->tc_format < FF_ARRAY_ELEMS(decklink_timecode_format_map)) + ctx->tc_format = decklink_timecode_format_map[cctx->tc_format]; if (cctx->video_input > 0 && (unsigned int)cctx->video_input < FF_ARRAY_ELEMS(decklink_video_connection_map)) ctx->video_input = decklink_video_connection_map[cctx->video_input]; if (cctx->audio_input > 0 && (unsigned int)cctx->audio_input < FF_ARRAY_ELEMS(decklink_audio_connection_map)) @@ -1222,6 +1253,15 @@ int ff_decklink_read_packet(AVFormatContext *avctx, AVPacket *pkt) avpacket_queue_get(&ctx->queue, pkt, 1); + if (ctx->tc_format && !(av_dict_get(ctx->video_st->metadata, "timecode", NULL, 0))) { + int size; + const uint8_t *side_metadata = av_packet_get_side_data(pkt, AV_PKT_DATA_STRINGS_METADATA, &size); + if (side_metadata) { + if (av_packet_unpack_dictionary(side_metadata, size, &ctx->video_st->metadata) < 0) + av_log(avctx, AV_LOG_ERROR, "Unable to set timecode\n"); + } + } + return 0; } diff --git a/libavdevice/decklink_dec_c.c b/libavdevice/decklink_dec_c.c index 47018dc681..6ab3819375 100644 --- a/libavdevice/decklink_dec_c.c +++ b/libavdevice/decklink_dec_c.c @@ -48,6 +48,15 @@ static const AVOption options[] = { { "unset", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 0}, 0, 0, DEC, "duplex_mode"}, { "half", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 1}, 0, 0, DEC, "duplex_mode"}, { "full", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 2}, 0, 0, DEC, "duplex_mode"}, + { "timecode_format", "timecode format", OFFSET(tc_format), AV_OPT_TYPE_INT, { .i64 = 0}, 0, 7, DEC, "tc_format"}, + { "none", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 0}, 0, 0, DEC, "tc_format"}, + { "rp188vitc", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 1}, 0, 0, DEC, "tc_format"}, + { "rp188vitc2", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 2}, 0, 0, DEC, "tc_format"}, + { "rp188ltc", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 3}, 0, 0, DEC, "tc_format"}, + { "rp188any", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 4}, 0, 0, DEC, "tc_format"}, + { "vitc", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 5}, 0, 0, DEC, "tc_format"}, + { "vitc2", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 6}, 0, 0, DEC, "tc_format"}, + { "serial", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 7}, 0, 0, DEC, "tc_format"}, { "video_input", "video input", OFFSET(video_input), AV_OPT_TYPE_INT, { .i64 = 0}, 0, 6, DEC, "video_input"}, { "unset", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 0}, 0, 0, DEC, "video_input"}, { "sdi", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 1}, 0, 0, DEC, "video_input"}, diff --git a/libavdevice/version.h b/libavdevice/version.h index 38d3d6ac78..9904149288 100644 --- a/libavdevice/version.h +++ b/libavdevice/version.h @@ -29,7 +29,7 @@ #define LIBAVDEVICE_VERSION_MAJOR 58 #define LIBAVDEVICE_VERSION_MINOR 4 -#define LIBAVDEVICE_VERSION_MICRO 100 +#define LIBAVDEVICE_VERSION_MICRO 101 #define LIBAVDEVICE_VERSION_INT AV_VERSION_INT(LIBAVDEVICE_VERSION_MAJOR, \ LIBAVDEVICE_VERSION_MINOR, \