avdevice/decklink_dec: add support for 50/60 fps timecode
Signed-off-by: Marton Balint <cus@passwd.hu>
This commit is contained in:
parent
eca12f4d5a
commit
ff373bb4a8
@ -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}.
|
@samp{rp188hfr}, @samp{rp188any}, @samp{vitc}, @samp{vitc2}, or @samp{serial}.
|
||||||
Defaults to @samp{none} (not included).
|
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
|
@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}.
|
||||||
|
@ -789,6 +789,52 @@ static int64_t get_pkt_pts(IDeckLinkVideoInputFrame *videoFrame,
|
|||||||
return pts;
|
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(
|
HRESULT decklink_input_callback::VideoInputFrameArrived(
|
||||||
IDeckLinkVideoInputFrame *videoFrame, IDeckLinkAudioInputPacket *audioFrame)
|
IDeckLinkVideoInputFrame *videoFrame, IDeckLinkAudioInputPacket *audioFrame)
|
||||||
{
|
{
|
||||||
@ -870,22 +916,16 @@ HRESULT decklink_input_callback::VideoInputFrameArrived(
|
|||||||
|
|
||||||
// Handle Timecode (if requested)
|
// Handle Timecode (if requested)
|
||||||
if (ctx->tc_format) {
|
if (ctx->tc_format) {
|
||||||
IDeckLinkTimecode *timecode;
|
AVTimecode tcr;
|
||||||
if (videoFrame->GetTimecode(ctx->tc_format, &timecode) == S_OK) {
|
if (get_frame_timecode(avctx, ctx, &tcr, videoFrame) >= 0) {
|
||||||
const char *tc = NULL;
|
char tcstr[AV_TIMECODE_STR_SIZE];
|
||||||
DECKLINK_STR decklink_tc;
|
const char *tc = av_timecode_make_string(&tcr, tcstr, 0);
|
||||||
if (timecode->GetString(&decklink_tc) == S_OK) {
|
|
||||||
tc = DECKLINK_STRDUP(decklink_tc);
|
|
||||||
DECKLINK_FREE(decklink_tc);
|
|
||||||
}
|
|
||||||
timecode->Release();
|
|
||||||
if (tc) {
|
if (tc) {
|
||||||
AVDictionary* metadata_dict = NULL;
|
AVDictionary* metadata_dict = NULL;
|
||||||
int metadata_len;
|
int metadata_len;
|
||||||
uint8_t* packed_metadata;
|
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);
|
uint32_t tc_data = av_timecode_get_smpte_from_framenum(&tcr, 0);
|
||||||
int size = sizeof(uint32_t) * 4;
|
int size = sizeof(uint32_t) * 4;
|
||||||
uint32_t *sd = (uint32_t *)av_packet_new_side_data(&pkt, AV_PKT_DATA_S12M_TIMECODE, size);
|
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);
|
packed_metadata = av_packet_pack_dictionary(metadata_dict, &metadata_len);
|
||||||
av_dict_free(&metadata_dict);
|
av_dict_free(&metadata_dict);
|
||||||
if (packed_metadata) {
|
if (packed_metadata) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user