From 1f4b29cfae0491bdbbf3daff78afb45844180bad Mon Sep 17 00:00:00 2001 From: James Darnley Date: Mon, 17 Jun 2024 18:37:23 +0200 Subject: [PATCH 01/10] upipe: add uref attribute for SMPTE 12M timecode Based on a patch by Josh de Kock. --- include/upipe/udict.h | 4 +++- include/upipe/uref_pic.h | 1 + lib/upipe/udict_inline.c | 1 + 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/include/upipe/udict.h b/include/upipe/udict.h index b16b2f121..fc42cdd3e 100644 --- a/include/upipe/udict.h +++ b/include/upipe/udict.h @@ -150,7 +150,9 @@ enum udict_type { /** p.cea_708 */ UDICT_TYPE_PIC_CEA_708, /** p.bar_data */ - UDICT_TYPE_PIC_BAR_DATA + UDICT_TYPE_PIC_BAR_DATA, + /** p.s12m */ + UDICT_TYPE_PIC_S12M, }; /** @This defines standard commands which udict modules may implement. */ diff --git a/include/upipe/uref_pic.h b/include/upipe/uref_pic.h index 490a1c8d8..63d6e5639 100644 --- a/include/upipe/uref_pic.h +++ b/include/upipe/uref_pic.h @@ -58,6 +58,7 @@ UREF_ATTR_VOID_SH(pic, tff, UDICT_TYPE_PIC_TFF, top field first) UREF_ATTR_SMALL_UNSIGNED_SH(pic, afd, UDICT_TYPE_PIC_AFD, active format description) UREF_ATTR_OPAQUE_SH(pic, cea_708, UDICT_TYPE_PIC_CEA_708, cea-708 captions) UREF_ATTR_OPAQUE_SH(pic, bar_data, UDICT_TYPE_PIC_BAR_DATA, afd bar data) +UREF_ATTR_OPAQUE_SH(pic, s12m, UDICT_TYPE_PIC_S12M, SMPTE 12M timecode compatible with ffmpeg AV_FRAME_DATA_S12M_TIMECODE) UREF_ATTR_UNSIGNED(pic, original_height, "p.original_height", original picture height before chunking) UREF_ATTR_VOID(pic, c_not_y, "p.c_not_y", whether ancillary data is found in chroma space) diff --git a/lib/upipe/udict_inline.c b/lib/upipe/udict_inline.c index 656fcc218..dd9a5ae3e 100644 --- a/lib/upipe/udict_inline.c +++ b/lib/upipe/udict_inline.c @@ -103,6 +103,7 @@ static const struct inline_shorthand inline_shorthands[] = { { "p.afd", UDICT_TYPE_SMALL_UNSIGNED }, { "p.cea_708", UDICT_TYPE_OPAQUE }, { "p.bar_data", UDICT_TYPE_OPAQUE }, + { "p.s12m", UDICT_TYPE_OPAQUE }, }; /** @This stores the size of the value of basic attribute types. */ From 47e3f2d41f4ae0369c416eadb56336a504f7a977 Mon Sep 17 00:00:00 2001 From: James Darnley Date: Wed, 19 Jun 2024 15:38:19 +0200 Subject: [PATCH 02/10] upipe: add a header to help with S12M timecodes --- include/upipe/Makefile.am | 1 + include/upipe/uref_attr_s12m.h | 184 +++++++++++++++++++++++++++++++++ 2 files changed, 185 insertions(+) create mode 100644 include/upipe/uref_attr_s12m.h diff --git a/include/upipe/Makefile.am b/include/upipe/Makefile.am index c215f472f..12e3f85d5 100644 --- a/include/upipe/Makefile.am +++ b/include/upipe/Makefile.am @@ -89,6 +89,7 @@ pkginclude_HEADERS = \ urefcount.h \ urefcount_helper.h \ uref_attr.h \ + uref_attr_s12m.h \ uref_block_flow.h \ uref_block.h \ uref_clock.h \ diff --git a/include/upipe/uref_attr_s12m.h b/include/upipe/uref_attr_s12m.h new file mode 100644 index 000000000..82791ea77 --- /dev/null +++ b/include/upipe/uref_attr_s12m.h @@ -0,0 +1,184 @@ +/* + * Copyright (C) 2024 Open Broadcast Systems Ltd + * + * Authors: James Darnley + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject + * to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +/** @file + * @short Upipe uref s12m attributes handling + * + * Helper functions to handle the s12m timecode attributes. See the SMPTE + * standards 12M-1 and 314M for more details. + */ + +#ifndef _UPIPE_UREF_ATTR_S12M_H_ +/** @hidden */ +#define _UPIPE_UREF_ATTR_S12M_H_ +#ifdef __cplusplus +extern "C" { +#endif + +#include "upipe/uref_pic.h" + +#include +#include + +/** @this returns a uint32_t read from possibly unaligned data + * + * Timecode packs and the count are native endian values. + */ +static inline uint32_t uref_attr_s12m_read(const uint8_t *data) +{ + /* TODO: big endian */ + return data[0] | data[1] << 8 | data[2] << 16 | (uint32_t)data[3] << 24; +} + +/** @This returns true if the data and size given repesents a valid s12m + * attribute. + */ +static inline bool uref_attr_s12m_check(const uint8_t *data, const size_t size) +{ + /* Size must be at least 4 for a single uint32_t. */ + if (size < 4) + return false; + const uint32_t count = uref_attr_s12m_read(data); + /* The count should be followed by that number of uint32_t. */ + if (size < sizeof(uint32_t) * (count+1)) + return false; + return true; +} + +/** @This returns a new timecode pack from the integer components and the drop + * frame flag. Does not validate values. + * + * @param hours 0-23 + * @param minutes 0-59 + * @param seconds 0-59 + * @param frames 0 to framerate + * @param drop true or false + * @return timecode pack + */ + +static inline uint32_t uref_attr_s12m_from_integers(const uint32_t hours, + const uint32_t minutes, const uint32_t seconds, const uint32_t frames, + const uint32_t drop) +{ + return drop << 30 + | hours % 10 + | hours / 10 << 4 + | minutes % 10 << 8 + | minutes / 10 << 12 + | seconds % 10 << 16 + | seconds / 10 << 20 + | frames % 10 << 24 + | frames / 10 << 28; +} + +/** @This splits a timecode pack into the integer components and the drop frame + * flag. Does not validate. + * values. + * + * @param timecode timecode pack + * @param hours pointer + * @param minutes pointer + * @param seconds pointer + * @param frames pointer + * @param drop pointer + */ + +static inline void uref_attr_s12m_to_integers(const uint32_t timecode, + uint8_t *hours, uint8_t *minutes, uint8_t *seconds, uint8_t *frames, + bool *drop) +{ + *hours = 10 * (timecode >> 4 & 0x3) + (timecode & 0xf); + *minutes = 10 * (timecode >> 12 & 0x7) + (timecode >> 8 & 0xf); + *seconds = 10 * (timecode >> 20 & 0x7) + (timecode >> 16 & 0xf); + *frames = 10 * (timecode >> 28 & 0x3) + (timecode >> 24 & 0xf); + *drop = (timecode & 1<<30) == 1<<30; +} + +/** @This returns a new timecode pack from the decimal components and the drop + * frame flag. Does not validate values. + * + * @param hours_10s 0-2 + * @param hours_1s 0-9 + * @param minutes_10s 0-5 + * @param minutes_1s 0-9 + * @param seconds_10s 0-5 + * @param seconds_1s 0-9 + * @param frames_10s 0 to tens of framerate + * @param frames_1s 0-9 + * @param drop true or false + * @return timecode pack + */ + +static inline uint32_t uref_attr_s12m_from_decimals(const uint32_t hours_10s, + const uint32_t hours_1s, const uint32_t minutes_10s, const uint32_t minutes_1s, + const uint32_t seconds_10s, const uint32_t seconds_1s, const uint32_t frames_10s, + const uint32_t frames_1s, const uint32_t drop) +{ + return drop << 30 + | hours_1s + | hours_10s << 4 + | minutes_1s << 8 + | minutes_10s << 12 + | seconds_1s << 16 + | seconds_10s << 20 + | frames_1s << 24 + | frames_10s << 28; +} + +/** @This splits a timecode pack into the decimal components and the drop frame + * flag. Does not validate values. + * + * @pack timecode timecode pack + * @param hours_10s pointer + * @param hours_1s pointer + * @param minutes_10s pointer + * @param minutes_1s pointer + * @param seconds_10s pointer + * @param seconds_1s pointer + * @param frames_10s pointer + * @param frames_1s pointer + * @param drop pointer + */ + +static inline void uref_attr_s12m_to_decimals(const uint32_t timecode, + uint8_t *hours_10s, uint8_t *hours_1s, uint8_t *minutes_10s, uint8_t *minutes_1s, + uint8_t *seconds_10s, uint8_t *seconds_1s, uint8_t *frames_10s, uint8_t *frames_1s, + bool *drop) +{ + *hours_10s = timecode >> 4 & 0x3; + *hours_1s = timecode & 0xf; + *minutes_10s = timecode >> 12 & 0x7; + *minutes_1s = timecode >> 8 & 0xf; + *seconds_10s = timecode >> 20 & 0x7; + *seconds_1s = timecode >> 16 & 0xf; + *frames_10s = timecode >> 28 & 0x3; + *frames_1s = timecode >> 24 & 0xf; + *drop = (timecode & 1 << 30) == 1 << 30; +} + +#ifdef __cplusplus +} +#endif +#endif From fcd45398e1599d4c87e5a51e53482588ec93352a Mon Sep 17 00:00:00 2001 From: James Darnley Date: Fri, 21 Jun 2024 16:07:30 +0200 Subject: [PATCH 03/10] upipe-av: add S12M timecode extraction from side-data Based on a patch by Josh de Kock. --- lib/upipe-av/upipe_avcodec_decode.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/lib/upipe-av/upipe_avcodec_decode.c b/lib/upipe-av/upipe_avcodec_decode.c index 71b2d5bd3..819fe20ec 100644 --- a/lib/upipe-av/upipe_avcodec_decode.c +++ b/lib/upipe-av/upipe_avcodec_decode.c @@ -31,6 +31,7 @@ #include "upipe/uclock.h" #include "upipe/ubuf.h" #include "upipe/uref.h" +#include "upipe/uref_attr_s12m.h" #include "upipe/uref_pic.h" #include "upipe/uref_flow.h" #include "upipe/uref_pic_flow.h" @@ -1324,6 +1325,13 @@ static void upipe_avcdec_output_pic(struct upipe *upipe, struct upump **upump_p) if (side_data) uref_pic_set_cea_708(uref, side_data->data, side_data->size); +#if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(56, 20, 100) + side_data = av_frame_get_side_data(frame, AV_FRAME_DATA_S12M_TIMECODE); + if (side_data && uref_attr_s12m_check(side_data->data, side_data->size)) { + uref_pic_set_s12m(uref, side_data->data, side_data->size); + } +#endif + /* various time-related attributes */ upipe_avcdec_set_time_attributes(upipe, uref); From 6365f1891fd920e8d502650ae9e66beae362c15c Mon Sep 17 00:00:00 2001 From: Josh de Kock Date: Tue, 23 Oct 2018 17:44:43 +0100 Subject: [PATCH 04/10] upipe-blackmagic: support timecode playback --- .../upipe_blackmagic_sink.cpp | 94 ++++++++++++++++++- 1 file changed, 91 insertions(+), 3 deletions(-) diff --git a/lib/upipe-blackmagic/upipe_blackmagic_sink.cpp b/lib/upipe-blackmagic/upipe_blackmagic_sink.cpp index e9d334086..36fde418a 100644 --- a/lib/upipe-blackmagic/upipe_blackmagic_sink.cpp +++ b/lib/upipe-blackmagic/upipe_blackmagic_sink.cpp @@ -78,6 +78,73 @@ extern "C" { static const unsigned max_samples = (uint64_t)48000 * 1001 / 24000; static const size_t audio_buf_size = max_samples * DECKLINK_CHANNELS * sizeof(int32_t); +inline static unsigned bcd2uint(uint8_t bcd) +{ + unsigned low = bcd & 0xf; + unsigned high = bcd >> 4; + if (low > 9 || high > 9) + return 0; + return low + 10*high; +} + +class upipe_bmd_sink_timecode : public IDeckLinkTimecode +{ +public: + upipe_bmd_sink_timecode(uint32_t _BCD) : BCD(_BCD) { } + + virtual BMDTimecodeBCD STDMETHODCALLTYPE GetBCD (void) { + return BCD; + } + + virtual HRESULT STDMETHODCALLTYPE GetComponents(uint8_t *hours, uint8_t *minutes, uint8_t *seconds, uint8_t *frames) { + *hours = bcd2uint( BCD & 0x3f); + *minutes = bcd2uint((BCD >> 8) & 0x7f); + *seconds = bcd2uint((BCD >> 16) & 0x7f); + *frames = bcd2uint((BCD >> 24) & 0x3f); + return S_OK; + } + + virtual BMDTimecodeFlags STDMETHODCALLTYPE GetFlags() { + return !!(BCD & (1 << 30)) ? bmdTimecodeIsDropFrame : bmdTimecodeFlagDefault; + } + + virtual HRESULT STDMETHODCALLTYPE GetTimecodeUserBits(BMDTimecodeUserBits *userBits) { + *userBits = GetBCD(); + return S_OK; + } + + virtual HRESULT GetString (const char **timecode) { + uint8_t h, m, s, f, drop = (this->GetFlags() == bmdTimecodeIsDropFrame); + GetComponents(&h, &m, &s, &f); + + if (!(*timecode = (const char*)calloc(16, sizeof(char)))) { + return S_FALSE; + } + + snprintf((char*)*timecode, 16, "%02u:%02u:%02u%c%02u", h, m, s, drop ? ';' : ':', f); + return S_OK; + } + + virtual ULONG STDMETHODCALLTYPE AddRef(void) { + return uatomic_fetch_add(&refcount, 1) + 1; + } + + virtual ULONG STDMETHODCALLTYPE Release(void) { + uint32_t new_ref = uatomic_fetch_sub(&refcount, 1) - 1; + if (new_ref == 0) + delete this; + return new_ref; + } + + virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid, LPVOID *ppv) { + return E_NOINTERFACE; + } + +private: + uint32_t BCD; + uatomic_uint32_t refcount; +}; + class upipe_bmd_sink_frame : public IDeckLinkVideoFrame { public: @@ -118,8 +185,14 @@ class upipe_bmd_sink_frame : public IDeckLinkVideoFrame } virtual HRESULT STDMETHODCALLTYPE GetTimecode(BMDTimecodeFormat format, - IDeckLinkTimecode **timecode) { - *timecode = NULL; + IDeckLinkTimecode **_timecode) { + timecode->AddRef(); + *_timecode = timecode; + return S_FALSE; + } + + virtual HRESULT STDMETHODCALLTYPE SetTimecode(upipe_bmd_sink_timecode &_timecode) { + timecode = &_timecode; return S_FALSE; } @@ -160,6 +233,7 @@ class upipe_bmd_sink_frame : public IDeckLinkVideoFrame uatomic_uint32_t refcount; IDeckLinkVideoFrameAncillary *frame_anc; + upipe_bmd_sink_timecode *timecode; public: uint64_t pts; @@ -325,6 +399,9 @@ struct upipe_bmd_sink { /** pass through teletext */ uatomic_uint32_t ttx; + /** pass through timecode */ + uatomic_uint32_t timecode; + /** last frame output */ upipe_bmd_sink_frame *video_frame; @@ -987,8 +1064,17 @@ static upipe_bmd_sink_frame *get_video_frame(struct upipe *upipe, pthread_mutex_unlock(&upipe_bmd_sink->lock); #endif - video_frame->SetAncillaryData(ancillary); + if (uatomic_load(&upipe_bmd_sink->timecode)) { + const uint32_t *tc_data; + size_t tc_data_size; + // bmdVideoOutputRP188 + if (ubase_check(uref_pic_get_s12m(uref, (const uint8_t**)&tc_data, &tc_data_size))) { + upipe_bmd_sink_timecode timecode(tc_data[1]); + video_frame->SetTimecode(timecode); + } + } + video_frame->SetAncillaryData(ancillary); video_frame->AddRef(); // we're gonna buffer this frame upipe_bmd_sink->video_frame = video_frame; @@ -1883,6 +1969,8 @@ static int upipe_bmd_sink_set_option(struct upipe *upipe, uatomic_store(&upipe_bmd_sink->cc, strcmp(v, "0")); } else if (!strcmp(k, "teletext")) { uatomic_store(&upipe_bmd_sink->ttx, strcmp(v, "0")); + } else if (!strcmp(k, "timecode")) { + uatomic_store(&upipe_bmd_sink->timecode, strcmp(v, "0")); } else return UBASE_ERR_INVALID; From f0167e902ed8b392a25d44ae6abae817e502e83b Mon Sep 17 00:00:00 2001 From: James Darnley Date: Fri, 21 Jun 2024 18:23:45 +0200 Subject: [PATCH 05/10] upipe_blackmagic_source: add S12M timecode capture Based on a patch by Josh de Kock. --- lib/upipe-blackmagic/upipe_blackmagic_source.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lib/upipe-blackmagic/upipe_blackmagic_source.cpp b/lib/upipe-blackmagic/upipe_blackmagic_source.cpp index 447e9d6a9..d9ebd937f 100644 --- a/lib/upipe-blackmagic/upipe_blackmagic_source.cpp +++ b/lib/upipe-blackmagic/upipe_blackmagic_source.cpp @@ -545,6 +545,12 @@ HRESULT DeckLinkCaptureDelegate::VideoInputFrameArrived( else if (upipe_bmd_src->tff) uref_pic_set_tff(uref); + IDeckLinkTimecode *timecode; + if (VideoFrame->GetTimecode(bmdTimecodeRP188Any, &timecode) == S_OK) { + uint32_t bcd[2] = { 1, timecode->GetBCD() }; + uref_pic_set_s12m(uref, (uint8_t*)&bcd, sizeof(bcd)); + } + if (!uqueue_push(&upipe_bmd_src->uqueue, uref)) uref_free(uref); } From 3766681b4446f6907243ab1acafece9308f69961 Mon Sep 17 00:00:00 2001 From: James Darnley Date: Tue, 9 Jul 2024 15:41:51 +0200 Subject: [PATCH 06/10] uref_attr_s12m: add some functions to validate the range of timecodes --- include/upipe/uref_attr_s12m.h | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/include/upipe/uref_attr_s12m.h b/include/upipe/uref_attr_s12m.h index 82791ea77..1b03e582d 100644 --- a/include/upipe/uref_attr_s12m.h +++ b/include/upipe/uref_attr_s12m.h @@ -67,6 +67,34 @@ static inline bool uref_attr_s12m_check(const uint8_t *data, const size_t size) return true; } +/** @This returns true if the integer values are permissible in a timecode. + */ +static inline bool uref_attr_s12m_validate_integers(const uint32_t hours, + const uint32_t minutes, const uint32_t seconds, const uint32_t frames) +{ + if (hours > 23) + return false; + if (minutes > 59) + return false; + if (seconds > 59) + return false; + if (frames > 29) + return false; + return true; +} + +/** @This returns true if the decimal values are permissible in a timecode. + */ +static inline bool uref_attr_s12m_validate_decimals(const uint32_t hours_10s, + const uint32_t hours_1s, const uint32_t minutes_10s, const uint32_t minutes_1s, + const uint32_t seconds_10s, const uint32_t seconds_1s, const uint32_t frames_10s, + const uint32_t frames_1s) +{ + return uref_attr_s12m_validate_integers(10*hours_10s + hours_1s, + 10*minutes_10s + minutes_1s, 10*seconds_10s + seconds_1s, + 10*frames_10s + frames_1s); +} + /** @This returns a new timecode pack from the integer components and the drop * frame flag. Does not validate values. * From 536ee4de381e9d6b0c7e043ce4d094f7e45769bf Mon Sep 17 00:00:00 2001 From: James Darnley Date: Wed, 10 Jul 2024 13:20:58 +0200 Subject: [PATCH 07/10] uref_attr_s12m: add functions to get and set the field flag --- include/upipe/uref_attr_s12m.h | 38 ++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/include/upipe/uref_attr_s12m.h b/include/upipe/uref_attr_s12m.h index 1b03e582d..49c7df766 100644 --- a/include/upipe/uref_attr_s12m.h +++ b/include/upipe/uref_attr_s12m.h @@ -206,6 +206,44 @@ static inline void uref_attr_s12m_to_decimals(const uint32_t timecode, *drop = (timecode & 1 << 30) == 1 << 30; } +/** @This gets the field flag in a timecode pack + * + * @param timecode timecode pack + * @param is_pal get the flag in the pal location instead of ntsc location + * @return the flag as a boolean + */ + +static inline bool uref_attr_s12m_get_field_flag(const uint32_t timecode, const bool is_pal) +{ + if (is_pal) + return (timecode & 1 << 7) == 1 << 7; + else + return (timecode & 1 << 23) == 1 << 23; +} + +/** @This sets the field flag in a timecode pack + * + * @param timecode old timecode pack + * @param is_pal set the flag in the pal location instead of ntsc location + * @param field field flag as a boolean + * @return new timecode pack + */ + +static inline uint32_t uref_attr_s12m_set_field_flag(const uint32_t timecode, const bool is_pal, const bool field) +{ + if (is_pal) { + if (field) + return timecode | 1 << 7; + else + return timecode & ~(1 << 7); + } else { + if (field) + return timecode | 1 << 23; + else + return timecode & ~(1 << 23); + } +} + #ifdef __cplusplus } #endif From e00ed37badc9ed1642b67ffcc200e5a9f6127e5f Mon Sep 17 00:00:00 2001 From: James Darnley Date: Fri, 26 Jul 2024 12:30:09 +0200 Subject: [PATCH 08/10] fixup! upipe_blackmagic_source: add S12M timecode capture --- lib/upipe-blackmagic/upipe_blackmagic_source.cpp | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/lib/upipe-blackmagic/upipe_blackmagic_source.cpp b/lib/upipe-blackmagic/upipe_blackmagic_source.cpp index d9ebd937f..d96f04469 100644 --- a/lib/upipe-blackmagic/upipe_blackmagic_source.cpp +++ b/lib/upipe-blackmagic/upipe_blackmagic_source.cpp @@ -32,6 +32,7 @@ #include "upipe/uprobe.h" #include "upipe/uclock.h" #include "upipe/uref.h" +#include "upipe/uref_attr_s12m.h" #include "upipe/uref_pic.h" #include "upipe/uref_pic_flow.h" #include "upipe/uref_sound_flow.h" @@ -547,7 +548,17 @@ HRESULT DeckLinkCaptureDelegate::VideoInputFrameArrived( IDeckLinkTimecode *timecode; if (VideoFrame->GetTimecode(bmdTimecodeRP188Any, &timecode) == S_OK) { - uint32_t bcd[2] = { 1, timecode->GetBCD() }; + uint8_t hours, minutes, seconds, frames; + timecode->GetComponents(&hours, &minutes, &seconds, &frames); + bool drop = timecode->GetFlags() & bmdTimecodeIsDropFrame; + + uint32_t timecode_s12m = uref_attr_s12m_from_integers(hours, + minutes, seconds, frames, !!drop); + timecode_s12m = uref_attr_s12m_set_field_flag(timecode_s12m, + upipe_bmd_src->fps.den != 1001, + timecode->GetFlags() & bmdTimecodeFieldMark); + + uint32_t bcd[2] = { 1, timecode_s12m }; uref_pic_set_s12m(uref, (uint8_t*)&bcd, sizeof(bcd)); } From 5df56fcf3f8dd4bf0d3edb384e29f41aeba600c6 Mon Sep 17 00:00:00 2001 From: James Darnley Date: Wed, 31 Jul 2024 17:45:41 +0200 Subject: [PATCH 09/10] upipe_blackmagic_sink: ask for timecodes when enabling video output --- lib/upipe-blackmagic/upipe_blackmagic_sink.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/upipe-blackmagic/upipe_blackmagic_sink.cpp b/lib/upipe-blackmagic/upipe_blackmagic_sink.cpp index 36fde418a..22a6eb829 100644 --- a/lib/upipe-blackmagic/upipe_blackmagic_sink.cpp +++ b/lib/upipe-blackmagic/upipe_blackmagic_sink.cpp @@ -1792,8 +1792,9 @@ static int upipe_bmd_open_vid(struct upipe *upipe) displayMode->GetFrameRate(&timeValue, &timeScale); upipe_bmd_sink->ticks_per_frame = UCLOCK_FREQ * timeValue / timeScale; + /* TODO: use timecode option to set bit */ result = deckLinkOutput->EnableVideoOutput(displayMode->GetDisplayMode(), - bmdVideoOutputVANC); + bmdVideoOutputVANC|bmdVideoOutputVITC|bmdVideoOutputRP188); if (result != S_OK) { upipe_err(upipe, "Failed to enable video output. Is another application using the card?\n"); From ef6208d0b1917eb5c5df59b0b8e07ac8df617c61 Mon Sep 17 00:00:00 2001 From: James Darnley Date: Wed, 31 Jul 2024 18:36:29 +0200 Subject: [PATCH 10/10] upipe_blackmagic_sink: correctly read data from the timecode attribute --- lib/upipe-blackmagic/upipe_blackmagic_sink.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/lib/upipe-blackmagic/upipe_blackmagic_sink.cpp b/lib/upipe-blackmagic/upipe_blackmagic_sink.cpp index 22a6eb829..8fec50b4a 100644 --- a/lib/upipe-blackmagic/upipe_blackmagic_sink.cpp +++ b/lib/upipe-blackmagic/upipe_blackmagic_sink.cpp @@ -34,6 +34,7 @@ #include "upipe/uprobe.h" #include "upipe/uclock.h" #include "upipe/uref.h" +#include "upipe/uref_attr_s12m.h" #include "upipe/uref_block.h" #include "upipe/uref_pic.h" #include "upipe/uref_pic_flow.h" @@ -1065,12 +1066,13 @@ static upipe_bmd_sink_frame *get_video_frame(struct upipe *upipe, #endif if (uatomic_load(&upipe_bmd_sink->timecode)) { - const uint32_t *tc_data; + const uint8_t *tc_data; size_t tc_data_size; // bmdVideoOutputRP188 - if (ubase_check(uref_pic_get_s12m(uref, (const uint8_t**)&tc_data, &tc_data_size))) { - upipe_bmd_sink_timecode timecode(tc_data[1]); - video_frame->SetTimecode(timecode); + if (ubase_check(uref_pic_get_s12m(uref, &tc_data, &tc_data_size)) + && uref_attr_s12m_check(tc_data, tc_data_size)) { + upipe_bmd_sink_timecode timecode(uref_attr_s12m_read(tc_data + sizeof(uint32_t))); + video_frame->SetTimecode(timecode); } }