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 <cus@passwd.hu>
This commit is contained in:
		
							parent
							
								
									fb480a1f1e
								
							
						
					
					
						commit
						0946c0ec17
					
				@ -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, \
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user