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 <cus@passwd.hu>tags/n4.1
| @@ -326,6 +326,12 @@ Defaults to @samp{2}. | |||||
| Sets the decklink device duplex mode. Must be @samp{unset}, @samp{half} or @samp{full}. | Sets the decklink device duplex mode. Must be @samp{unset}, @samp{half} or @samp{full}. | ||||
| Defaults to @samp{unset}. | 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 | @item video_input | ||||
| Sets the video input source. Must be @samp{unset}, @samp{sdi}, @samp{hdmi}, | Sets the video input source. Must be @samp{unset}, @samp{sdi}, @samp{hdmi}, | ||||
| @samp{optical_sdi}, @samp{component}, @samp{composite} or @samp{s_video}. | @samp{optical_sdi}, @samp{component}, @samp{composite} or @samp{s_video}. | ||||
| @@ -93,6 +93,7 @@ struct decklink_ctx { | |||||
| BMDDisplayMode bmd_mode; | BMDDisplayMode bmd_mode; | ||||
| BMDVideoConnection video_input; | BMDVideoConnection video_input; | ||||
| BMDAudioConnection audio_input; | BMDAudioConnection audio_input; | ||||
| BMDTimecodeFormat tc_format; | |||||
| int bmd_width; | int bmd_width; | ||||
| int bmd_height; | int bmd_height; | ||||
| int bmd_field_dominance; | int bmd_field_dominance; | ||||
| @@ -169,6 +170,17 @@ static const BMDVideoConnection decklink_video_connection_map[] = { | |||||
| bmdVideoConnectionSVideo, | 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); | 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_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); | 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); | ||||
| @@ -50,6 +50,7 @@ struct decklink_cctx { | |||||
| DecklinkPtsSource video_pts_source; | DecklinkPtsSource video_pts_source; | ||||
| int audio_input; | int audio_input; | ||||
| int video_input; | int video_input; | ||||
| int tc_format; | |||||
| int draw_bars; | int draw_bars; | ||||
| char *format_code; | char *format_code; | ||||
| int raw_format; | int raw_format; | ||||
| @@ -752,6 +752,35 @@ HRESULT decklink_input_callback::VideoInputFrameArrived( | |||||
| "- Frames dropped %u\n", ctx->frameCount, ++ctx->dropped); | "- Frames dropped %u\n", ctx->frameCount, ++ctx->dropped); | ||||
| } | } | ||||
| no_video = 0; | 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); | 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->teletext_lines = cctx->teletext_lines; | ||||
| ctx->preroll = cctx->preroll; | ctx->preroll = cctx->preroll; | ||||
| ctx->duplex_mode = cctx->duplex_mode; | 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)) | 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]; | 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)) | 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); | 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; | return 0; | ||||
| } | } | ||||
| @@ -48,6 +48,15 @@ static const AVOption options[] = { | |||||
| { "unset", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 0}, 0, 0, DEC, "duplex_mode"}, | { "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"}, | { "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"}, | { "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"}, | { "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"}, | { "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"}, | { "sdi", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 1}, 0, 0, DEC, "video_input"}, | ||||
| @@ -29,7 +29,7 @@ | |||||
| #define LIBAVDEVICE_VERSION_MAJOR 58 | #define LIBAVDEVICE_VERSION_MAJOR 58 | ||||
| #define LIBAVDEVICE_VERSION_MINOR 4 | #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, \ | #define LIBAVDEVICE_VERSION_INT AV_VERSION_INT(LIBAVDEVICE_VERSION_MAJOR, \ | ||||
| LIBAVDEVICE_VERSION_MINOR, \ | LIBAVDEVICE_VERSION_MINOR, \ | ||||