Git Inbox Mirror of the ffmpeg-devel mailing list - see https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
 help / color / mirror / Atom feed
* [FFmpeg-devel] [PATCH 0/7] matroska: support of timecode
@ 2025-09-09 12:37 Jerome Martinez via ffmpeg-devel
  2025-09-09 12:38 ` [FFmpeg-devel] [PATCH 1/7] matroskaenc: remove unused MaxBlockAdditionID Jerome Martinez via ffmpeg-devel
                   ` (7 more replies)
  0 siblings, 8 replies; 9+ messages in thread
From: Jerome Martinez via ffmpeg-devel @ 2025-09-09 12:37 UTC (permalink / raw)
  To: FFmpeg development discussions and patches; +Cc: Jerome Martinez

This patch series adds support of timecode in Matroska files, specs are at:
https://github.com/ietf-wg-cellar/matroska-specification/blob/master/cellar-codec/block_additional_mappings/smpte-st12-1-timecode.md

As ST 12-1 support was already partially implemented, using 32 bits 
rather than 64 bits (binary groups were not stored), the ST 12-1 side 
data format was changed from 32 bits to 64 bits rather than introducing 
another ST  12-1 side data internal "format", this may break code using 
it but not in the main repo (I found nothing using it in the main repo), 
is it fine?
If we keep internally 32 bits for ST 12-1, any non zero timecode binary 
group stored in MKVs would be trashed.

The first 3 patches refactor BlockAddition support in MKV encoder in 
order to have it more versatile
The 2 next patches adapt decklink_dec for writing 64 bit timecodes in 
side data rather than 32 bit timecodes
The last 2 patches adds timecode support in Matroska

_______________________________________________
ffmpeg-devel mailing list -- ffmpeg-devel@ffmpeg.org
To unsubscribe send an email to ffmpeg-devel-leave@ffmpeg.org

^ permalink raw reply	[flat|nested] 9+ messages in thread

* [FFmpeg-devel] [PATCH 1/7] matroskaenc: remove unused MaxBlockAdditionID
  2025-09-09 12:37 [FFmpeg-devel] [PATCH 0/7] matroska: support of timecode Jerome Martinez via ffmpeg-devel
@ 2025-09-09 12:38 ` Jerome Martinez via ffmpeg-devel
  2025-09-09 12:39 ` [FFmpeg-devel] [PATCH 2/7] matroskaenc: reserve_video_track_space option Jerome Martinez via ffmpeg-devel
                   ` (6 subsequent siblings)
  7 siblings, 0 replies; 9+ messages in thread
From: Jerome Martinez via ffmpeg-devel @ 2025-09-09 12:38 UTC (permalink / raw)
  To: ffmpeg-devel; +Cc: Jerome Martinez

[-- Attachment #1: 0001-matroskaenc-remove-unused-MaxBlockAdditionID.patch --]
[-- Type: text/plain, Size: 8790 bytes --]

>From 305cce5e1db5e6ae48497398bec024da206b9035 Mon Sep 17 00:00:00 2001
From: Jerome Martinez <jerome@mediaarea.net>
Date: Wed, 3 Sep 2025 19:54:10 +0200
Subject: [PATCH 1/7] matroskaenc: remove unused MaxBlockAdditionID

For non video tracks: it is never used, let's save 4 bytes
For video tracks: simplify the temporary storage with only a void element and MaxBlockAdditionID will be written when finishing the track if needed (not the default value)
---
 libavformat/matroskaenc.c     |  8 +++----
 tests/ref/lavf/mka            |  4 ++--
 tests/ref/lavf/mkv            |  4 ++--
 tests/ref/lavf/mkv_attachment |  4 ++--
 tests/ref/seek/lavf-mkv       | 44 +++++++++++++++++------------------
 5 files changed, 32 insertions(+), 32 deletions(-)

diff --git a/libavformat/matroskaenc.c b/libavformat/matroskaenc.c
index 8142d9125e..5339b6fd33 100644
--- a/libavformat/matroskaenc.c
+++ b/libavformat/matroskaenc.c
@@ -1706,16 +1706,16 @@ static void mkv_write_blockadditionmapping(AVFormatContext *s, const MatroskaMux
     const AVDOVIDecoderConfigurationRecord *dovi;
     const AVPacketSideData *sd;
 
-    if (IS_SEEKABLE(s->pb, mkv)) {
+    if (IS_SEEKABLE(s->pb, mkv) && par->codec_type == AVMEDIA_TYPE_VIDEO) {
         track->blockadditionmapping_offset = avio_tell(pb);
         // We can't know at this point if there will be a block with BlockAdditions, so
         // we either write the default value here, or a void element. Either of them will
         // be overwritten when finishing the track.
-        put_ebml_uint(pb, MATROSKA_ID_TRACKMAXBLKADDID, 0);
         if (par->codec_type == AVMEDIA_TYPE_VIDEO) {
             // Similarly, reserve space for an eventual
             // HDR10+ ITU T.35 metadata BlockAdditionMapping.
-            put_ebml_void(pb, 3 /* BlockAdditionMapping */
+            put_ebml_void(pb, 4 /* MaxBlockAdditionID */
+                            + 3 /* BlockAdditionMapping */
                             + 4 /* BlockAddIDValue */
                             + 4 /* BlockAddIDType */);
         }
@@ -3294,7 +3294,7 @@ after_cues:
             for (unsigned i = 0; i < s->nb_streams; i++) {
                 const mkv_track *track = &mkv->tracks[i];
 
-                if (!track->max_blockaddid)
+                if (!track->max_blockaddid || !track->blockadditionmapping_offset)
                     continue;
 
                 // We reserved a single byte to write this value.
diff --git a/tests/ref/lavf/mka b/tests/ref/lavf/mka
index 6f9a56b24a..6fe5ca8d9e 100644
--- a/tests/ref/lavf/mka
+++ b/tests/ref/lavf/mka
@@ -1,3 +1,3 @@
-b9c2d23dffba449cf1111f062b93c93f *tests/data/lavf/lavf.mka
-43588 tests/data/lavf/lavf.mka
+00381b9855734adb05bc96cfd2f3b6b2 *tests/data/lavf/lavf.mka
+43584 tests/data/lavf/lavf.mka
 tests/data/lavf/lavf.mka CRC=0x3a1da17e
diff --git a/tests/ref/lavf/mkv b/tests/ref/lavf/mkv
index 5a3c3b931e..b32b521cfa 100644
--- a/tests/ref/lavf/mkv
+++ b/tests/ref/lavf/mkv
@@ -1,3 +1,3 @@
-dd709c2b5e173eaca39cdd4a10aac3ec *tests/data/lavf/lavf.mkv
-320447 tests/data/lavf/lavf.mkv
+9397da7654fc3a00a439545584608637 *tests/data/lavf/lavf.mkv
+320443 tests/data/lavf/lavf.mkv
 tests/data/lavf/lavf.mkv CRC=0xec6c3c68
diff --git a/tests/ref/lavf/mkv_attachment b/tests/ref/lavf/mkv_attachment
index 1a086a4f24..aa3faf9d12 100644
--- a/tests/ref/lavf/mkv_attachment
+++ b/tests/ref/lavf/mkv_attachment
@@ -1,3 +1,3 @@
-7cd7b06892b74d66da217c8dda90bfac *tests/data/lavf/lavf.mkv_attachment
-472597 tests/data/lavf/lavf.mkv_attachment
+1c40de58fae4e878e0a499bfe61d78e0 *tests/data/lavf/lavf.mkv_attachment
+472593 tests/data/lavf/lavf.mkv_attachment
 tests/data/lavf/lavf.mkv_attachment CRC=0xec6c3c68
diff --git a/tests/ref/seek/lavf-mkv b/tests/ref/seek/lavf-mkv
index e327959058..2eb8506356 100644
--- a/tests/ref/seek/lavf-mkv
+++ b/tests/ref/seek/lavf-mkv
@@ -1,48 +1,48 @@
-ret: 0         st: 1 flags:1 dts:-0.011000 pts:-0.011000 pos:    689 size:   208
+ret: 0         st: 1 flags:1 dts:-0.011000 pts:-0.011000 pos:    685 size:   208
 ret: 0         st:-1 flags:0  ts:-1.000000
-ret: 0         st: 0 flags:1 dts: 0.000000 pts: 0.000000 pos:    905 size: 27837
+ret: 0         st: 0 flags:1 dts: 0.000000 pts: 0.000000 pos:    901 size: 27837
 ret: 0         st:-1 flags:1  ts: 1.894167
-ret: 0         st: 0 flags:1 dts: 0.960000 pts: 0.960000 pos: 292321 size: 27834
+ret: 0         st: 0 flags:1 dts: 0.960000 pts: 0.960000 pos: 292317 size: 27834
 ret: 0         st: 0 flags:0  ts: 0.788000
-ret: 0         st: 0 flags:1 dts: 0.960000 pts: 0.960000 pos: 292321 size: 27834
+ret: 0         st: 0 flags:1 dts: 0.960000 pts: 0.960000 pos: 292317 size: 27834
 ret: 0         st: 0 flags:1  ts:-0.317000
-ret: 0         st: 0 flags:1 dts: 0.000000 pts: 0.000000 pos:    905 size: 27837
+ret: 0         st: 0 flags:1 dts: 0.000000 pts: 0.000000 pos:    901 size: 27837
 ret:-1         st: 1 flags:0  ts: 2.577000
 ret: 0         st: 1 flags:1  ts: 1.471000
-ret: 0         st: 1 flags:1 dts: 0.982000 pts: 0.982000 pos: 320165 size:   209
+ret: 0         st: 1 flags:1 dts: 0.982000 pts: 0.982000 pos: 320161 size:   209
 ret: 0         st:-1 flags:0  ts: 0.365002
-ret: 0         st: 0 flags:1 dts: 0.480000 pts: 0.480000 pos: 146873 size: 27925
+ret: 0         st: 0 flags:1 dts: 0.480000 pts: 0.480000 pos: 146869 size: 27925
 ret: 0         st:-1 flags:1  ts:-0.740831
-ret: 0         st: 0 flags:1 dts: 0.000000 pts: 0.000000 pos:    905 size: 27837
+ret: 0         st: 0 flags:1 dts: 0.000000 pts: 0.000000 pos:    901 size: 27837
 ret:-1         st: 0 flags:0  ts: 2.153000
 ret: 0         st: 0 flags:1  ts: 1.048000
-ret: 0         st: 0 flags:1 dts: 0.960000 pts: 0.960000 pos: 292321 size: 27834
+ret: 0         st: 0 flags:1 dts: 0.960000 pts: 0.960000 pos: 292317 size: 27834
 ret: 0         st: 1 flags:0  ts:-0.058000
-ret: 0         st: 1 flags:1 dts:-0.011000 pts:-0.011000 pos:    689 size:   208
+ret: 0         st: 1 flags:1 dts:-0.011000 pts:-0.011000 pos:    685 size:   208
 ret: 0         st: 1 flags:1  ts: 2.836000
-ret: 0         st: 1 flags:1 dts: 0.982000 pts: 0.982000 pos: 320165 size:   209
+ret: 0         st: 1 flags:1 dts: 0.982000 pts: 0.982000 pos: 320161 size:   209
 ret:-1         st:-1 flags:0  ts: 1.730004
 ret: 0         st:-1 flags:1  ts: 0.624171
-ret: 0         st: 0 flags:1 dts: 0.480000 pts: 0.480000 pos: 146873 size: 27925
+ret: 0         st: 0 flags:1 dts: 0.480000 pts: 0.480000 pos: 146869 size: 27925
 ret: 0         st: 0 flags:0  ts:-0.482000
-ret: 0         st: 0 flags:1 dts: 0.000000 pts: 0.000000 pos:    905 size: 27837
+ret: 0         st: 0 flags:1 dts: 0.000000 pts: 0.000000 pos:    901 size: 27837
 ret: 0         st: 0 flags:1  ts: 2.413000
-ret: 0         st: 0 flags:1 dts: 0.960000 pts: 0.960000 pos: 292321 size: 27834
+ret: 0         st: 0 flags:1 dts: 0.960000 pts: 0.960000 pos: 292317 size: 27834
 ret:-1         st: 1 flags:0  ts: 1.307000
 ret: 0         st: 1 flags:1  ts: 0.201000
-ret: 0         st: 1 flags:1 dts:-0.011000 pts:-0.011000 pos:    689 size:   208
+ret: 0         st: 1 flags:1 dts:-0.011000 pts:-0.011000 pos:    685 size:   208
 ret: 0         st:-1 flags:0  ts:-0.904994
-ret: 0         st: 0 flags:1 dts: 0.000000 pts: 0.000000 pos:    905 size: 27837
+ret: 0         st: 0 flags:1 dts: 0.000000 pts: 0.000000 pos:    901 size: 27837
 ret: 0         st:-1 flags:1  ts: 1.989173
-ret: 0         st: 0 flags:1 dts: 0.960000 pts: 0.960000 pos: 292321 size: 27834
+ret: 0         st: 0 flags:1 dts: 0.960000 pts: 0.960000 pos: 292317 size: 27834
 ret: 0         st: 0 flags:0  ts: 0.883000
-ret: 0         st: 0 flags:1 dts: 0.960000 pts: 0.960000 pos: 292321 size: 27834
+ret: 0         st: 0 flags:1 dts: 0.960000 pts: 0.960000 pos: 292317 size: 27834
 ret: 0         st: 0 flags:1  ts:-0.222000
-ret: 0         st: 0 flags:1 dts: 0.000000 pts: 0.000000 pos:    905 size: 27837
+ret: 0         st: 0 flags:1 dts: 0.000000 pts: 0.000000 pos:    901 size: 27837
 ret:-1         st: 1 flags:0  ts: 2.672000
 ret: 0         st: 1 flags:1  ts: 1.566000
-ret: 0         st: 1 flags:1 dts: 0.982000 pts: 0.982000 pos: 320165 size:   209
+ret: 0         st: 1 flags:1 dts: 0.982000 pts: 0.982000 pos: 320161 size:   209
 ret: 0         st:-1 flags:0  ts: 0.460008
-ret: 0         st: 0 flags:1 dts: 0.480000 pts: 0.480000 pos: 146873 size: 27925
+ret: 0         st: 0 flags:1 dts: 0.480000 pts: 0.480000 pos: 146869 size: 27925
 ret: 0         st:-1 flags:1  ts:-0.645825
-ret: 0         st: 0 flags:1 dts: 0.000000 pts: 0.000000 pos:    905 size: 27837
+ret: 0         st: 0 flags:1 dts: 0.000000 pts: 0.000000 pos:    901 size: 27837
-- 
2.46.0.windows.1

_______________________________________________
ffmpeg-devel mailing list -- ffmpeg-devel@ffmpeg.org
To unsubscribe send an email to ffmpeg-devel-leave@ffmpeg.org

^ permalink raw reply	[flat|nested] 9+ messages in thread

* [FFmpeg-devel] [PATCH 2/7] matroskaenc: reserve_video_track_space option
  2025-09-09 12:37 [FFmpeg-devel] [PATCH 0/7] matroska: support of timecode Jerome Martinez via ffmpeg-devel
  2025-09-09 12:38 ` [FFmpeg-devel] [PATCH 1/7] matroskaenc: remove unused MaxBlockAdditionID Jerome Martinez via ffmpeg-devel
@ 2025-09-09 12:39 ` Jerome Martinez via ffmpeg-devel
  2025-09-09 12:39 ` [FFmpeg-devel] [PATCH 3/7] matroskaenc: increase default for reserved bytes in video Jerome Martinez via ffmpeg-devel
                   ` (5 subsequent siblings)
  7 siblings, 0 replies; 9+ messages in thread
From: Jerome Martinez via ffmpeg-devel @ 2025-09-09 12:39 UTC (permalink / raw)
  To: ffmpeg-devel; +Cc: Jerome Martinez

[-- Attachment #1: 0002-matroskaenc-reserve_video_track_space-option.patch --]
[-- Type: text/plain, Size: 10162 bytes --]

>From b8a59df6a484beb391898de9b8c38f7387ad7ee5 Mon Sep 17 00:00:00 2001
From: Jerome Martinez <jerome@mediaarea.net>
Date: Wed, 3 Sep 2025 21:33:50 +0200
Subject: [PATCH 2/7] matroskaenc: reserve_video_track_space option

The number of block additions is not known in advance, so we need to be able to set more reserved space for when we add a lot more block additions.
It is also possible to reduce the reserved space (or even eliminate it) for those who want to keep the smallest header possible.
---
 libavformat/matroskaenc.c | 82 ++++++++++++++++++++++++++++-----------
 1 file changed, 59 insertions(+), 23 deletions(-)

diff --git a/libavformat/matroskaenc.c b/libavformat/matroskaenc.c
index 5339b6fd33..c7b804a3e5 100644
--- a/libavformat/matroskaenc.c
+++ b/libavformat/matroskaenc.c
@@ -80,6 +80,8 @@
  * and so has avio_write(). */
 #define MAX_SUPPORTED_EBML_LENGTH FFMIN(MAX_EBML_LENGTH, INT_MAX)
 
+#define MAX_MATROSKA_BLOCK_ADD_ITU_T_T35 1 /* currently only 1 such element is supported */
+
 #define MODE_MATROSKAv2 0x01
 #define MODE_WEBM       0x02
 
@@ -196,6 +198,7 @@ typedef struct mkv_track {
     int64_t         duration;
     int64_t         duration_offset;
     uint64_t        max_blockaddid;
+    int             itu_t_t35_count;
     int64_t         blockadditionmapping_offset;
     int             codecpriv_offset;
     unsigned        codecpriv_size;     ///< size reserved for CodecPrivate excluding header+length field
@@ -249,6 +252,7 @@ typedef struct MatroskaMuxContext {
     int                 wrote_tags;
 
     int                 reserve_cues_space;
+    int                 reserve_video_track_space;
     int                 cluster_size_limit;
     int64_t             cluster_time_limit;
     int                 write_crc;
@@ -1706,19 +1710,12 @@ static void mkv_write_blockadditionmapping(AVFormatContext *s, const MatroskaMux
     const AVDOVIDecoderConfigurationRecord *dovi;
     const AVPacketSideData *sd;
 
-    if (IS_SEEKABLE(s->pb, mkv) && par->codec_type == AVMEDIA_TYPE_VIDEO) {
+    if (IS_SEEKABLE(s->pb, mkv) && par->codec_type == AVMEDIA_TYPE_VIDEO && mkv->reserve_video_track_space > 1) {
         track->blockadditionmapping_offset = avio_tell(pb);
         // We can't know at this point if there will be a block with BlockAdditions, so
         // we either write the default value here, or a void element. Either of them will
         // be overwritten when finishing the track.
-        if (par->codec_type == AVMEDIA_TYPE_VIDEO) {
-            // Similarly, reserve space for an eventual
-            // HDR10+ ITU T.35 metadata BlockAdditionMapping.
-            put_ebml_void(pb, 4 /* MaxBlockAdditionID */
-                            + 3 /* BlockAdditionMapping */
-                            + 4 /* BlockAddIDValue */
-                            + 4 /* BlockAddIDType */);
-        }
+        put_ebml_void(pb, mkv->reserve_video_track_space);
     }
 
     sd = av_packet_side_data_get(par->coded_side_data, par->nb_coded_side_data,
@@ -2796,6 +2793,31 @@ static void mkv_write_blockadditional(EbmlWriter *writer, const uint8_t *buf,
     ebml_writer_close_master(writer);
 }
 
+static int mkv_simulate_blockadditional_header(AVFormatContext *s, int* remaining_video_track_space, uint64_t additional_type, uint64_t additional_id)
+{
+    int size = 0;
+    size += uint_size(MATROSKA_ID_BLKADDIDTYPE) + 1 + uint_size(additional_type);
+    size += uint_size(MATROSKA_ID_BLKADDIDVALUE) + 1 + uint_size(additional_id);
+    size += uint_size(MATROSKA_ID_TRACKBLKADDMAPPING) + uint_size(size);
+    if (size > *remaining_video_track_space || *remaining_video_track_space - size == 1) { /* min element full size is 2 so 1 byte is not something which can be let alone */
+        av_log(s, AV_LOG_WARNING, "Block addition mapping in track header is not written for type %"PRIu64" id %"PRIu64" due to lack of reserved bytes.\n", additional_type, additional_id);
+        return 0;
+    }
+    *remaining_video_track_space -= size;
+    return 1;
+}
+
+static void mkv_write_blockadditional_header(AVFormatContext *s, uint64_t additional_type, uint64_t additional_id)
+{
+    /* when you modify this function, adapt mkv_simulate_blockadditional_header accordingly */
+    MatroskaMuxContext *mkv = s->priv_data;
+    AVIOContext *writer = mkv->track.bc;
+    ebml_master mapping_master = start_ebml_master(writer, MATROSKA_ID_TRACKBLKADDMAPPING, 8);
+    put_ebml_uint(writer, MATROSKA_ID_BLKADDIDTYPE, additional_type);
+    put_ebml_uint(writer, MATROSKA_ID_BLKADDIDVALUE, additional_id);
+    end_ebml_master(writer, mapping_master);
+}
+
 static int mkv_write_block(void *logctx, MatroskaMuxContext *mkv,
                            AVIOContext *pb, const AVCodecParameters *par,
                            mkv_track *track, const AVPacket *pkt,
@@ -2807,7 +2829,7 @@ static int mkv_write_block(void *logctx, MatroskaMuxContext *mkv,
     size_t side_data_size;
     uint64_t additional_id;
     unsigned track_number = track->track_num;
-    EBML_WRITER(12);
+    EBML_WRITER(11 + MAX_MATROSKA_BLOCK_ADD_ITU_T_T35);
     int ret;
 
     mkv->cur_block.track  = track;
@@ -2882,6 +2904,7 @@ static int mkv_write_block(void *logctx, MatroskaMuxContext *mkv,
                                       MATROSKA_BLOCK_ADD_ID_ITU_T_T35);
             track->max_blockaddid = FFMAX(track->max_blockaddid,
                                           MATROSKA_BLOCK_ADD_ID_ITU_T_T35);
+            track->itu_t_t35_count = 1;
         }
     }
 
@@ -3293,24 +3316,36 @@ after_cues:
 
             for (unsigned i = 0; i < s->nb_streams; i++) {
                 const mkv_track *track = &mkv->tracks[i];
+                int remaining_video_track_space = mkv->reserve_video_track_space;
+                int max_block_add_id_count = 0;
+                int max_block_add_id_size = 3 + uint_size(track->max_blockaddid);
+                int block_type_t35_count = 0;
 
-                if (!track->max_blockaddid || !track->blockadditionmapping_offset)
+                if (!track->max_blockaddid)
                     continue;
 
-                // We reserved a single byte to write this value.
-                av_assert0(track->max_blockaddid <= 0xFF);
+                /* check what is possible to write in the reserved space, in priority order */
+                for (int i = 0; i < track->itu_t_t35_count; i++) {
+                    block_type_t35_count += mkv_simulate_blockadditional_header(s, &remaining_video_track_space, MATROSKA_BLOCK_ADD_ID_TYPE_ITU_T_T35, MATROSKA_BLOCK_ADD_ID_TYPE_ITU_T_T35);
+                }                
+                if (remaining_video_track_space >= max_block_add_id_size && remaining_video_track_space != max_block_add_id_size + 1) { /* min element full size is 2 so 1 byte is not something which can be let alone */
+                    max_block_add_id_count++;
+                    remaining_video_track_space -= max_block_add_id_size;
+                }
 
+                /* write what is possible to write in the reserved space */
+                /* when you modify this part, adapt the check part above accordingly */
+                if (!track->blockadditionmapping_offset)
+                    continue;
                 avio_seek(track_bc, track->blockadditionmapping_offset, SEEK_SET);
-
-                put_ebml_uint(track_bc, MATROSKA_ID_TRACKMAXBLKADDID,
-                              track->max_blockaddid);
-                if (track->max_blockaddid == MATROSKA_BLOCK_ADD_ID_ITU_T_T35) {
-                    ebml_master mapping_master = start_ebml_master(track_bc, MATROSKA_ID_TRACKBLKADDMAPPING, 8);
-                    put_ebml_uint(track_bc, MATROSKA_ID_BLKADDIDTYPE,
-                                  MATROSKA_BLOCK_ADD_ID_TYPE_ITU_T_T35);
-                    put_ebml_uint(track_bc, MATROSKA_ID_BLKADDIDVALUE,
-                                  MATROSKA_BLOCK_ADD_ID_ITU_T_T35);
-                    end_ebml_master(track_bc, mapping_master);
+                if (max_block_add_id_count) {
+                    put_ebml_uint(track_bc, MATROSKA_ID_TRACKMAXBLKADDID, track->max_blockaddid);
+                }
+                for (int i = 0; i < block_type_t35_count; i++) {
+                    mkv_write_blockadditional_header(s, MATROSKA_BLOCK_ADD_ID_TYPE_ITU_T_T35, MATROSKA_BLOCK_ADD_ID_ITU_T_T35);
+                }
+                if (remaining_video_track_space > 1) {
+                    put_ebml_void(track_bc, remaining_video_track_space);
                 }
             }
         }
@@ -3545,6 +3580,7 @@ static const AVCodecTag additional_subtitle_tags[] = {
 #define FLAGS AV_OPT_FLAG_ENCODING_PARAM
 static const AVOption options[] = {
     { "reserve_index_space", "reserve a given amount of space (in bytes) at the beginning of the file for the index (cues)", OFFSET(reserve_cues_space), AV_OPT_TYPE_INT,   { .i64 = 0 },   0, INT_MAX,   FLAGS },
+    { "reserve_video_track_space", "reserve a given amount of space (in bytes) at the beginning of the file for the block additions (HDR, timecodes...)", OFFSET(reserve_video_track_space), AV_OPT_TYPE_INT, { .i64 = 15 }, 0, INT_MAX, FLAGS }, /* the number of block additions is not known in advance, so the default value strikes a balance between the size of the reserved header size and the maximum number of block additions that can be registered at the end of writing */
     { "cues_to_front", "move Cues (the index) to the front by shifting data if necessary", OFFSET(move_cues_to_front), AV_OPT_TYPE_BOOL, { .i64 = 0}, 0, 1, FLAGS },
     { "cluster_size_limit",  "store at most the provided amount of bytes in a cluster",                                     OFFSET(cluster_size_limit), AV_OPT_TYPE_INT  , { .i64 = -1 }, -1, INT_MAX,   FLAGS },
     { "cluster_time_limit",  "store at most the provided number of milliseconds in a cluster",                               OFFSET(cluster_time_limit), AV_OPT_TYPE_INT64, { .i64 = -1 }, -1, INT64_MAX, FLAGS },
-- 
2.46.0.windows.1

_______________________________________________
ffmpeg-devel mailing list -- ffmpeg-devel@ffmpeg.org
To unsubscribe send an email to ffmpeg-devel-leave@ffmpeg.org

^ permalink raw reply	[flat|nested] 9+ messages in thread

* [FFmpeg-devel] [PATCH 3/7] matroskaenc: increase default for reserved bytes in video
  2025-09-09 12:37 [FFmpeg-devel] [PATCH 0/7] matroska: support of timecode Jerome Martinez via ffmpeg-devel
  2025-09-09 12:38 ` [FFmpeg-devel] [PATCH 1/7] matroskaenc: remove unused MaxBlockAdditionID Jerome Martinez via ffmpeg-devel
  2025-09-09 12:39 ` [FFmpeg-devel] [PATCH 2/7] matroskaenc: reserve_video_track_space option Jerome Martinez via ffmpeg-devel
@ 2025-09-09 12:39 ` Jerome Martinez via ffmpeg-devel
  2025-09-09 12:40 ` [FFmpeg-devel] [PATCH 4/7] 32-bit timecode to 64-bit RFC 5484 timecode functions Jerome Martinez via ffmpeg-devel
                   ` (4 subsequent siblings)
  7 siblings, 0 replies; 9+ messages in thread
From: Jerome Martinez via ffmpeg-devel @ 2025-09-09 12:39 UTC (permalink / raw)
  To: ffmpeg-devel; +Cc: Jerome Martinez

[-- Attachment #1: 0003-matroskaenc-increase-default-for-reserved-bytes-in-v.patch --]
[-- Type: text/plain, Size: 8658 bytes --]

>From becb8841223fb3efbd270df9ca8a26801da3444e Mon Sep 17 00:00:00 2001
From: Jerome Martinez <jerome@mediaarea.net>
Date: Thu, 4 Sep 2025 20:23:17 +0200
Subject: [PATCH 3/7] matroskaenc: increase default for reserved bytes in video
 track header

This permits to handle by default more e.g. timecodes without having to set the corresponding option
---
 libavformat/matroskaenc.c     |  2 +-
 tests/ref/lavf/mkv            |  4 ++--
 tests/ref/lavf/mkv_attachment |  4 ++--
 tests/ref/seek/lavf-mkv       | 44 +++++++++++++++++------------------
 4 files changed, 27 insertions(+), 27 deletions(-)

diff --git a/libavformat/matroskaenc.c b/libavformat/matroskaenc.c
index c7b804a3e5..d304b63663 100644
--- a/libavformat/matroskaenc.c
+++ b/libavformat/matroskaenc.c
@@ -3580,7 +3580,7 @@ static const AVCodecTag additional_subtitle_tags[] = {
 #define FLAGS AV_OPT_FLAG_ENCODING_PARAM
 static const AVOption options[] = {
     { "reserve_index_space", "reserve a given amount of space (in bytes) at the beginning of the file for the index (cues)", OFFSET(reserve_cues_space), AV_OPT_TYPE_INT,   { .i64 = 0 },   0, INT_MAX,   FLAGS },
-    { "reserve_video_track_space", "reserve a given amount of space (in bytes) at the beginning of the file for the block additions (HDR, timecodes...)", OFFSET(reserve_video_track_space), AV_OPT_TYPE_INT, { .i64 = 15 }, 0, INT_MAX, FLAGS }, /* the number of block additions is not known in advance, so the default value strikes a balance between the size of the reserved header size and the maximum number of block additions that can be registered at the end of writing */
+    { "reserve_video_track_space", "reserve a given amount of space (in bytes) at the beginning of the file for the block additions (HDR, timecodes...)", OFFSET(reserve_video_track_space), AV_OPT_TYPE_INT, { .i64 = 4 + 11 * 8 }, 0, INT_MAX, FLAGS }, /* the number of block additions is not known in advance, so the default value strikes a balance between the size of the reserved header size and the maximum number of block additions that can be registered at the end of writing */
     { "cues_to_front", "move Cues (the index) to the front by shifting data if necessary", OFFSET(move_cues_to_front), AV_OPT_TYPE_BOOL, { .i64 = 0}, 0, 1, FLAGS },
     { "cluster_size_limit",  "store at most the provided amount of bytes in a cluster",                                     OFFSET(cluster_size_limit), AV_OPT_TYPE_INT  , { .i64 = -1 }, -1, INT_MAX,   FLAGS },
     { "cluster_time_limit",  "store at most the provided number of milliseconds in a cluster",                               OFFSET(cluster_time_limit), AV_OPT_TYPE_INT64, { .i64 = -1 }, -1, INT64_MAX, FLAGS },
diff --git a/tests/ref/lavf/mkv b/tests/ref/lavf/mkv
index b32b521cfa..8fea09caf6 100644
--- a/tests/ref/lavf/mkv
+++ b/tests/ref/lavf/mkv
@@ -1,3 +1,3 @@
-9397da7654fc3a00a439545584608637 *tests/data/lavf/lavf.mkv
-320443 tests/data/lavf/lavf.mkv
+c95871d60c576ae73f360145cb61db90 *tests/data/lavf/lavf.mkv
+320520 tests/data/lavf/lavf.mkv
 tests/data/lavf/lavf.mkv CRC=0xec6c3c68
diff --git a/tests/ref/lavf/mkv_attachment b/tests/ref/lavf/mkv_attachment
index aa3faf9d12..221f4ddd49 100644
--- a/tests/ref/lavf/mkv_attachment
+++ b/tests/ref/lavf/mkv_attachment
@@ -1,3 +1,3 @@
-1c40de58fae4e878e0a499bfe61d78e0 *tests/data/lavf/lavf.mkv_attachment
-472593 tests/data/lavf/lavf.mkv_attachment
+fdfcb796a2e1a020bcc36ea926a243ce *tests/data/lavf/lavf.mkv_attachment
+472670 tests/data/lavf/lavf.mkv_attachment
 tests/data/lavf/lavf.mkv_attachment CRC=0xec6c3c68
diff --git a/tests/ref/seek/lavf-mkv b/tests/ref/seek/lavf-mkv
index 2eb8506356..f2253e1c59 100644
--- a/tests/ref/seek/lavf-mkv
+++ b/tests/ref/seek/lavf-mkv
@@ -1,48 +1,48 @@
-ret: 0         st: 1 flags:1 dts:-0.011000 pts:-0.011000 pos:    685 size:   208
+ret: 0         st: 1 flags:1 dts:-0.011000 pts:-0.011000 pos:    762 size:   208
 ret: 0         st:-1 flags:0  ts:-1.000000
-ret: 0         st: 0 flags:1 dts: 0.000000 pts: 0.000000 pos:    901 size: 27837
+ret: 0         st: 0 flags:1 dts: 0.000000 pts: 0.000000 pos:    978 size: 27837
 ret: 0         st:-1 flags:1  ts: 1.894167
-ret: 0         st: 0 flags:1 dts: 0.960000 pts: 0.960000 pos: 292317 size: 27834
+ret: 0         st: 0 flags:1 dts: 0.960000 pts: 0.960000 pos: 292394 size: 27834
 ret: 0         st: 0 flags:0  ts: 0.788000
-ret: 0         st: 0 flags:1 dts: 0.960000 pts: 0.960000 pos: 292317 size: 27834
+ret: 0         st: 0 flags:1 dts: 0.960000 pts: 0.960000 pos: 292394 size: 27834
 ret: 0         st: 0 flags:1  ts:-0.317000
-ret: 0         st: 0 flags:1 dts: 0.000000 pts: 0.000000 pos:    901 size: 27837
+ret: 0         st: 0 flags:1 dts: 0.000000 pts: 0.000000 pos:    978 size: 27837
 ret:-1         st: 1 flags:0  ts: 2.577000
 ret: 0         st: 1 flags:1  ts: 1.471000
-ret: 0         st: 1 flags:1 dts: 0.982000 pts: 0.982000 pos: 320161 size:   209
+ret: 0         st: 1 flags:1 dts: 0.982000 pts: 0.982000 pos: 320238 size:   209
 ret: 0         st:-1 flags:0  ts: 0.365002
-ret: 0         st: 0 flags:1 dts: 0.480000 pts: 0.480000 pos: 146869 size: 27925
+ret: 0         st: 0 flags:1 dts: 0.480000 pts: 0.480000 pos: 146946 size: 27925
 ret: 0         st:-1 flags:1  ts:-0.740831
-ret: 0         st: 0 flags:1 dts: 0.000000 pts: 0.000000 pos:    901 size: 27837
+ret: 0         st: 0 flags:1 dts: 0.000000 pts: 0.000000 pos:    978 size: 27837
 ret:-1         st: 0 flags:0  ts: 2.153000
 ret: 0         st: 0 flags:1  ts: 1.048000
-ret: 0         st: 0 flags:1 dts: 0.960000 pts: 0.960000 pos: 292317 size: 27834
+ret: 0         st: 0 flags:1 dts: 0.960000 pts: 0.960000 pos: 292394 size: 27834
 ret: 0         st: 1 flags:0  ts:-0.058000
-ret: 0         st: 1 flags:1 dts:-0.011000 pts:-0.011000 pos:    685 size:   208
+ret: 0         st: 1 flags:1 dts:-0.011000 pts:-0.011000 pos:    762 size:   208
 ret: 0         st: 1 flags:1  ts: 2.836000
-ret: 0         st: 1 flags:1 dts: 0.982000 pts: 0.982000 pos: 320161 size:   209
+ret: 0         st: 1 flags:1 dts: 0.982000 pts: 0.982000 pos: 320238 size:   209
 ret:-1         st:-1 flags:0  ts: 1.730004
 ret: 0         st:-1 flags:1  ts: 0.624171
-ret: 0         st: 0 flags:1 dts: 0.480000 pts: 0.480000 pos: 146869 size: 27925
+ret: 0         st: 0 flags:1 dts: 0.480000 pts: 0.480000 pos: 146946 size: 27925
 ret: 0         st: 0 flags:0  ts:-0.482000
-ret: 0         st: 0 flags:1 dts: 0.000000 pts: 0.000000 pos:    901 size: 27837
+ret: 0         st: 0 flags:1 dts: 0.000000 pts: 0.000000 pos:    978 size: 27837
 ret: 0         st: 0 flags:1  ts: 2.413000
-ret: 0         st: 0 flags:1 dts: 0.960000 pts: 0.960000 pos: 292317 size: 27834
+ret: 0         st: 0 flags:1 dts: 0.960000 pts: 0.960000 pos: 292394 size: 27834
 ret:-1         st: 1 flags:0  ts: 1.307000
 ret: 0         st: 1 flags:1  ts: 0.201000
-ret: 0         st: 1 flags:1 dts:-0.011000 pts:-0.011000 pos:    685 size:   208
+ret: 0         st: 1 flags:1 dts:-0.011000 pts:-0.011000 pos:    762 size:   208
 ret: 0         st:-1 flags:0  ts:-0.904994
-ret: 0         st: 0 flags:1 dts: 0.000000 pts: 0.000000 pos:    901 size: 27837
+ret: 0         st: 0 flags:1 dts: 0.000000 pts: 0.000000 pos:    978 size: 27837
 ret: 0         st:-1 flags:1  ts: 1.989173
-ret: 0         st: 0 flags:1 dts: 0.960000 pts: 0.960000 pos: 292317 size: 27834
+ret: 0         st: 0 flags:1 dts: 0.960000 pts: 0.960000 pos: 292394 size: 27834
 ret: 0         st: 0 flags:0  ts: 0.883000
-ret: 0         st: 0 flags:1 dts: 0.960000 pts: 0.960000 pos: 292317 size: 27834
+ret: 0         st: 0 flags:1 dts: 0.960000 pts: 0.960000 pos: 292394 size: 27834
 ret: 0         st: 0 flags:1  ts:-0.222000
-ret: 0         st: 0 flags:1 dts: 0.000000 pts: 0.000000 pos:    901 size: 27837
+ret: 0         st: 0 flags:1 dts: 0.000000 pts: 0.000000 pos:    978 size: 27837
 ret:-1         st: 1 flags:0  ts: 2.672000
 ret: 0         st: 1 flags:1  ts: 1.566000
-ret: 0         st: 1 flags:1 dts: 0.982000 pts: 0.982000 pos: 320161 size:   209
+ret: 0         st: 1 flags:1 dts: 0.982000 pts: 0.982000 pos: 320238 size:   209
 ret: 0         st:-1 flags:0  ts: 0.460008
-ret: 0         st: 0 flags:1 dts: 0.480000 pts: 0.480000 pos: 146869 size: 27925
+ret: 0         st: 0 flags:1 dts: 0.480000 pts: 0.480000 pos: 146946 size: 27925
 ret: 0         st:-1 flags:1  ts:-0.645825
-ret: 0         st: 0 flags:1 dts: 0.000000 pts: 0.000000 pos:    901 size: 27837
+ret: 0         st: 0 flags:1 dts: 0.000000 pts: 0.000000 pos:    978 size: 27837
-- 
2.46.0.windows.1

_______________________________________________
ffmpeg-devel mailing list -- ffmpeg-devel@ffmpeg.org
To unsubscribe send an email to ffmpeg-devel-leave@ffmpeg.org

^ permalink raw reply	[flat|nested] 9+ messages in thread

* [FFmpeg-devel] [PATCH 4/7] 32-bit timecode to 64-bit RFC 5484 timecode functions
  2025-09-09 12:37 [FFmpeg-devel] [PATCH 0/7] matroska: support of timecode Jerome Martinez via ffmpeg-devel
                   ` (2 preceding siblings ...)
  2025-09-09 12:39 ` [FFmpeg-devel] [PATCH 3/7] matroskaenc: increase default for reserved bytes in video Jerome Martinez via ffmpeg-devel
@ 2025-09-09 12:40 ` Jerome Martinez via ffmpeg-devel
  2025-09-09 12:41 ` [FFmpeg-devel] [PATCH 5/7] decklink_dec: store timecode in 64-bit RFC 5484 format Jerome Martinez via ffmpeg-devel
                   ` (3 subsequent siblings)
  7 siblings, 0 replies; 9+ messages in thread
From: Jerome Martinez via ffmpeg-devel @ 2025-09-09 12:40 UTC (permalink / raw)
  To: ffmpeg-devel; +Cc: Jerome Martinez

[-- Attachment #1: 0004-32-bit-timecode-to-64-bit-RFC-5484-timecode-function.patch --]
[-- Type: text/plain, Size: 6345 bytes --]

>From 36cc44569a898c8401944bc16b791632fd4ac985 Mon Sep 17 00:00:00 2001
From: Dave Rice <dave at dericed.com>
Date: Mon, 30 Jun 2025 00:00:00 +0200
Subject: [PATCH 4/7] 32-bit timecode to 64-bit RFC 5484 timecode functions

---
 libavutil/timecode.c | 104 +++++++++++++++++++++++++++++++++++++++++++
 libavutil/timecode.h |  22 +++++++++
 2 files changed, 126 insertions(+)

diff --git a/libavutil/timecode.c b/libavutil/timecode.c
index bca16b6ac2..81fb66a5e4 100644
--- a/libavutil/timecode.c
+++ b/libavutil/timecode.c
@@ -243,3 +243,107 @@ int av_timecode_init_from_string(AVTimecode *tc, AVRational rate, const char *st
 
     return av_timecode_init_from_components(tc, rate, flags, hh, mm, ss, ff, log_ctx);
 }
+
+static int bcd_to_int(int bcd) {
+    return ((bcd >> 4) & 0xF) * 10 + (bcd & 0xF);
+}
+
+uint64_t av_timecode_expand_to_64bit(uint32_t tc32)
+{
+    uint64_t tc64 = 0;
+
+    int hours_bcd   =  tc32        & 0x3F;  // bits 0-5
+    int bgf1        = (tc32 >> 6)  & 0x1;   // bit 6
+    int bgf2        = (tc32 >> 7)  & 0x1;   // bit 7
+    int minutes_bcd = (tc32 >> 8)  & 0x7F;  // bits 8-14
+    int bgf0        = (tc32 >> 15) & 0x1;   // bit 15
+    int seconds_bcd = (tc32 >> 16) & 0x7F;  // bits 16-22
+    int frames_bcd  = (tc32 >> 24) & 0x3F;  // bits 24-29
+    int drop        = (tc32 >> 30) & 0x1;   // bit 30
+    int color       = (tc32 >> 31) & 0x1;   // bit 31
+
+    int hours   = bcd_to_int(hours_bcd);
+    int minutes = bcd_to_int(minutes_bcd);
+    int seconds = bcd_to_int(seconds_bcd);
+    int frames  = bcd_to_int(frames_bcd);
+
+    // Units and tens
+    int uh = hours   % 10, th = hours   / 10;
+    int um = minutes % 10, tm = minutes / 10;
+    int us = seconds % 10, ts = seconds / 10;
+    int uf = frames  % 10, tf = frames  / 10;
+
+    // Assign bits as per RFC 5484 layout
+    tc64 |= (uint64_t)(uf & 0xF) << 0;   // Units of frames
+    tc64 |= 0                    << 4;   // First binary group
+    tc64 |= (uint64_t)(tf & 0x3) << 8;   // Tens of frames (2 bits)
+    tc64 |= (uint64_t)drop       << 10;  // Drop frame flag
+    tc64 |= (uint64_t)color      << 11;  // Color frame flag
+    tc64 |= 0                    << 12;  // Second binary group
+
+    tc64 |= (uint64_t)(us & 0xF) << 16;  // Units of seconds
+    tc64 |= 0                    << 20;  // Third binary group
+    tc64 |= (uint64_t)(ts & 0x7) << 24;  // Tens of seconds
+    tc64 |= 0                    << 27;  // Polarity correction
+    tc64 |= 0                    << 28;  // Fourth binary group
+
+    tc64 |= (uint64_t)(um & 0xF) << 32;  // Units of minutes
+    tc64 |= (uint64_t)0          << 36;  // Fifth binary group
+    tc64 |= (uint64_t)(tm & 0x7) << 40;  // Tens of minutes
+    tc64 |= (uint64_t)bgf0       << 43;  // BGF0
+    tc64 |= (uint64_t)0          << 44;  // Sixth binary group
+
+    tc64 |= (uint64_t)(uh & 0xF) << 48;  // Units of hours
+    tc64 |= (uint64_t)0          << 52;  // Seventh binary group
+    tc64 |= (uint64_t)(th & 0x3) << 56;  // Tens of hours
+    tc64 |= (uint64_t)bgf1       << 58;  // BGF1
+    tc64 |= (uint64_t)bgf2       << 59;  // BGF2
+    tc64 |= (uint64_t)0          << 60;  // Eighth binary group
+
+    return tc64;
+}
+
+static int int_to_bcd(int val) {
+    return ((val / 10) << 4) | (val % 10);
+}
+
+uint32_t av_timecode_parse_from_64bit(uint64_t tc64)
+{
+    uint32_t tc32 = 0;
+
+    int uf   = (tc64 >> 0)  & 0xF;   // Ones of frames
+    int tf   = (tc64 >> 8)  & 0x3;   // Tens of frames
+    int drop = (tc64 >> 10) & 0x1;
+    int color= (tc64 >> 11) & 0x1;
+
+    int us   = (tc64 >> 16) & 0xF;   // Ones of seconds
+    int ts   = (tc64 >> 24) & 0x7;   // Tens of seconds
+
+    int um   = (tc64 >> 32) & 0xF;   // Ones of minutes
+    int tm   = (tc64 >> 40) & 0x7;   // Tens of minutes
+    int bgf0 = (tc64 >> 43) & 0x1;
+
+    int uh   = (tc64 >> 48) & 0xF;   // Ones of hours
+    int th   = (tc64 >> 56) & 0x3;   // Tens of hours
+    int bgf1 = (tc64 >> 58) & 0x1;
+    int bgf2 = (tc64 >> 59) & 0x1;
+
+    int hours   = int_to_bcd(th * 10 + uh);
+    int minutes = int_to_bcd(tm * 10 + um);
+    int seconds = int_to_bcd(ts * 10 + us);
+    int frames  = int_to_bcd(tf * 10 + uf);
+
+    // Assemble tc32 using the FFmpeg SMPTE 32-bit format
+    tc32 |= (hours & 0x3F)        << 0;   // bits 0-5: hours (BCD)
+    tc32 |= (bgf1  & 0x1)         << 6;   // bit 6: BGF1
+    tc32 |= (bgf2  & 0x1)         << 7;   // bit 7: BGF2 (or FIELD)
+    tc32 |= (minutes & 0x7F)      << 8;   // bits 8-14: minutes (BCD)
+    tc32 |= (bgf0  & 0x1)         << 15;  // bit 15: BGF0
+    tc32 |= (seconds & 0x7F)      << 16;  // bits 16-22: seconds (BCD)
+    tc32 |= 0                     << 23;  // bit 23: FIELD (set to 0)
+    tc32 |= (frames  & 0x3F)      << 24;  // bits 24-29: frames (BCD)
+    tc32 |= (drop    & 0x1)       << 30;  // bit 30: drop frame flag
+    tc32 |= (color   & 0x1)       << 31;  // bit 31: color frame flag
+
+    return tc32;
+}
diff --git a/libavutil/timecode.h b/libavutil/timecode.h
index fe0fc83576..81713a9adf 100644
--- a/libavutil/timecode.h
+++ b/libavutil/timecode.h
@@ -196,4 +196,26 @@ int av_timecode_init_from_string(AVTimecode *tc, AVRational rate, const char *st
  */
 int av_timecode_check_frame_rate(AVRational rate);
 
+/**
+ * Convert a 32-bit SMPTE 12M timecode to 64-bit SMPTE 12M/RFC 5484 format.
+ *
+ * This maps the timecode as described in RFC 5484 Section 6.2,
+ * expanding BCD-encoded time values into bit fields without the sync word.
+ *
+ * @param tc32  The 32-bit SMPTE timecode (from DeckLink or av_timecode_get_smpte)
+ * @return      The 64-bit SMPTE 12M timecode format (without sync word)
+ */
+uint64_t av_timecode_expand_to_64bit(uint32_t tc32);
+
+/**
+ * Convert a 64-bit SMPTE 12M/RFC 5484 timecode to 32-bit SMPTE format.
+ *
+ * This reconstructs the packed 32-bit SMPTE timecode (DeckLink-style or FFmpeg output)
+ * from a full 64-bit representation.
+ *
+ * @param tc64 The 64-bit timecode value in RFC 5484 / SMPTE 12M format (without sync word)
+ * @return     A 32-bit SMPTE timecode
+ */
+uint32_t av_timecode_parse_from_64bit(uint64_t tc64);
+
 #endif /* AVUTIL_TIMECODE_H */
-- 
2.46.0.windows.1

_______________________________________________
ffmpeg-devel mailing list -- ffmpeg-devel@ffmpeg.org
To unsubscribe send an email to ffmpeg-devel-leave@ffmpeg.org

^ permalink raw reply	[flat|nested] 9+ messages in thread

* [FFmpeg-devel] [PATCH 5/7] decklink_dec: store timecode in 64-bit RFC 5484 format
  2025-09-09 12:37 [FFmpeg-devel] [PATCH 0/7] matroska: support of timecode Jerome Martinez via ffmpeg-devel
                   ` (3 preceding siblings ...)
  2025-09-09 12:40 ` [FFmpeg-devel] [PATCH 4/7] 32-bit timecode to 64-bit RFC 5484 timecode functions Jerome Martinez via ffmpeg-devel
@ 2025-09-09 12:41 ` Jerome Martinez via ffmpeg-devel
  2025-09-09 12:41 ` [FFmpeg-devel] [PATCH 6/7] matroskadec: read timecode in BlockAddition Jerome Martinez via ffmpeg-devel
                   ` (2 subsequent siblings)
  7 siblings, 0 replies; 9+ messages in thread
From: Jerome Martinez via ffmpeg-devel @ 2025-09-09 12:41 UTC (permalink / raw)
  To: ffmpeg-devel; +Cc: Jerome Martinez

[-- Attachment #1: 0005-decklink_dec-store-timecode-in-64-bit-RFC-5484-forma.patch --]
[-- Type: text/plain, Size: 1599 bytes --]

>From 873b025037eced830eb3869cf4f5891a7c9ed042 Mon Sep 17 00:00:00 2001
From: Jerome Martinez <jerome@mediaarea.net>
Date: Wed, 27 Aug 2025 13:36:36 +0200
Subject: [PATCH 5/7] decklink_dec: store timecode in 64-bit RFC 5484 format

---
 libavdevice/decklink_dec.cpp | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/libavdevice/decklink_dec.cpp b/libavdevice/decklink_dec.cpp
index 418701e4e0..1dacf1b129 100644
--- a/libavdevice/decklink_dec.cpp
+++ b/libavdevice/decklink_dec.cpp
@@ -826,9 +826,9 @@ HRESULT decklink_input_callback::VideoInputFrameArrived(
                         uint8_t* packed_metadata;
 
                         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);
-                            int size = sizeof(uint32_t) * 4;
-                            uint32_t *sd = (uint32_t *)av_packet_new_side_data(&pkt, AV_PKT_DATA_S12M_TIMECODE, size);
+                            uint64_t tc_data = av_timecode_expand_to_64bit(av_timecode_get_smpte_from_framenum(&tcr, 0));
+                            int size = sizeof(uint64_t) * 4;
+                            uint64_t *sd = (uint64_t *)av_packet_new_side_data(&pkt, AV_PKT_DATA_S12M_TIMECODE, size);
 
                             if (sd) {
                                 *sd       = 1;       // one TC
-- 
2.46.0.windows.1

_______________________________________________
ffmpeg-devel mailing list -- ffmpeg-devel@ffmpeg.org
To unsubscribe send an email to ffmpeg-devel-leave@ffmpeg.org

^ permalink raw reply	[flat|nested] 9+ messages in thread

* [FFmpeg-devel] [PATCH 6/7] matroskadec: read timecode in BlockAddition
  2025-09-09 12:37 [FFmpeg-devel] [PATCH 0/7] matroska: support of timecode Jerome Martinez via ffmpeg-devel
                   ` (4 preceding siblings ...)
  2025-09-09 12:41 ` [FFmpeg-devel] [PATCH 5/7] decklink_dec: store timecode in 64-bit RFC 5484 format Jerome Martinez via ffmpeg-devel
@ 2025-09-09 12:41 ` Jerome Martinez via ffmpeg-devel
  2025-09-09 12:41 ` [FFmpeg-devel] [PATCH 7/7] matroskaenc: write " Jerome Martinez via ffmpeg-devel
  2025-09-11 15:52 ` [FFmpeg-devel] Re: [PATCH 0/7] matroska: support of timecode Dave Rice via ffmpeg-devel
  7 siblings, 0 replies; 9+ messages in thread
From: Jerome Martinez via ffmpeg-devel @ 2025-09-09 12:41 UTC (permalink / raw)
  To: ffmpeg-devel; +Cc: Jerome Martinez

[-- Attachment #1: 0006-matroskadec-read-timecode-in-BlockAddition.patch --]
[-- Type: text/plain, Size: 3448 bytes --]

>From 887508f9f4e4ab21431a41cc78bea48039811cba Mon Sep 17 00:00:00 2001
From: Jerome Martinez <jerome@mediaarea.net>
Date: Thu, 4 Sep 2025 20:17:55 +0200
Subject: [PATCH 6/7] matroskadec: read timecode in BlockAddition

---
 libavformat/matroska.h    |  1 +
 libavformat/matroskadec.c | 34 ++++++++++++++++++++++++++++++++++
 2 files changed, 35 insertions(+)

diff --git a/libavformat/matroska.h b/libavformat/matroska.h
index 719f2ef796..d78b33d4b2 100644
--- a/libavformat/matroska.h
+++ b/libavformat/matroska.h
@@ -361,6 +361,7 @@ typedef enum {
   MATROSKA_BLOCK_ADD_ID_TYPE_DEFAULT                = 0,
   MATROSKA_BLOCK_ADD_ID_TYPE_OPAQUE                 = 1,
   MATROSKA_BLOCK_ADD_ID_TYPE_ITU_T_T35              = 4,
+  MATROSKA_BLOCK_ADD_ID_TYPE_SMPTE_12M              = 121,
   MATROSKA_BLOCK_ADD_ID_TYPE_DVCC                   = 0x64766343, // MKBETAG('d','v','c','C')
   MATROSKA_BLOCK_ADD_ID_TYPE_DVVC                   = 0x64767643, // MKBETAG('d','v','v','C')
 } MatroskaBlockAddIDType;
diff --git a/libavformat/matroskadec.c b/libavformat/matroskadec.c
index 1e0c75c51b..d8800b48a6 100644
--- a/libavformat/matroskadec.c
+++ b/libavformat/matroskadec.c
@@ -295,6 +295,7 @@ typedef struct MatroskaTrack {
 
     uint32_t palette[AVPALETTE_COUNT];
     int has_palette;
+    int add_block_timecode_count;
 } MatroskaTrack;
 
 typedef struct MatroskaAttachment {
@@ -2530,6 +2531,9 @@ static int mkv_parse_block_addition_mappings(AVFormatContext *s, AVStream *st, M
                     return AVERROR_INVALIDDATA;
             }
             break;
+        case MATROSKA_BLOCK_ADD_ID_TYPE_SMPTE_12M:
+                track->add_block_timecode_count++;
+                break;
         case MATROSKA_BLOCK_ADD_ID_TYPE_DVCC:
         case MATROSKA_BLOCK_ADD_ID_TYPE_DVVC:
             if ((ret = mkv_parse_dvcc_dvvc(s, st, track, &mapping->extradata)) < 0)
@@ -3954,6 +3958,36 @@ static int matroska_parse_block_additional(MatroskaDemuxContext *matroska,
 
         return 0;
     }
+    case MATROSKA_BLOCK_ADD_ID_TYPE_SMPTE_12M: {
+        if (size < 8) {
+            av_log(matroska->ctx, AV_LOG_WARNING, "SMPTE timecode from BlockAdditional is malformed.\n");
+            break;
+        }
+
+        size_t sd_size = 0;
+        uint8_t *sd = av_packet_get_side_data(pkt, AV_PKT_DATA_S12M_TIMECODE, &sd_size);
+        uint64_t count = sd ? *((uint64_t*)sd) : 0;
+        if (!count) {
+            sd_size = sizeof(uint64_t) * (1 + track->add_block_timecode_count);
+            sd = av_packet_new_side_data(pkt, AV_PKT_DATA_S12M_TIMECODE, sd_size);
+            count = 0;
+        }
+
+        if (count >= track->add_block_timecode_count) {
+            av_log(matroska->ctx, AV_LOG_DEBUG, "There are more timecodes in the block than the count indicated in the track header, extra timecodes are ignored.\n");
+        }
+        else if (sd) {
+            uint64_t tc = *((uint64_t*)data);
+            av_log(matroska->ctx, AV_LOG_DEBUG, "Reading SMPTE timecode from BlockAdditional: 0x%016lX (RFC 5484)\n", tc);
+
+            uint64_t *sd_64 = (uint64_t*)sd;
+            count++;
+            *sd_64 = count;
+            AV_WB64(sd_64 + count, tc);
+        }
+        
+        return 0;
+    }
     default:
         break;
     }
-- 
2.46.0.windows.1

_______________________________________________
ffmpeg-devel mailing list -- ffmpeg-devel@ffmpeg.org
To unsubscribe send an email to ffmpeg-devel-leave@ffmpeg.org

^ permalink raw reply	[flat|nested] 9+ messages in thread

* [FFmpeg-devel] [PATCH 7/7] matroskaenc: write timecode in BlockAddition
  2025-09-09 12:37 [FFmpeg-devel] [PATCH 0/7] matroska: support of timecode Jerome Martinez via ffmpeg-devel
                   ` (5 preceding siblings ...)
  2025-09-09 12:41 ` [FFmpeg-devel] [PATCH 6/7] matroskadec: read timecode in BlockAddition Jerome Martinez via ffmpeg-devel
@ 2025-09-09 12:41 ` Jerome Martinez via ffmpeg-devel
  2025-09-11 15:52 ` [FFmpeg-devel] Re: [PATCH 0/7] matroska: support of timecode Dave Rice via ffmpeg-devel
  7 siblings, 0 replies; 9+ messages in thread
From: Jerome Martinez via ffmpeg-devel @ 2025-09-09 12:41 UTC (permalink / raw)
  To: ffmpeg-devel; +Cc: Jerome Martinez

[-- Attachment #1: 0007-matroskaenc-write-timecode-in-BlockAddition.patch --]
[-- Type: text/plain, Size: 5981 bytes --]

>From f7aeb160a8ba2e2eefa6206af2ff5179a88f7343 Mon Sep 17 00:00:00 2001
From: Jerome Martinez <jerome@mediaarea.net>
Date: Thu, 4 Sep 2025 20:18:08 +0200
Subject: [PATCH 7/7] matroskaenc: write timecode in BlockAddition

---
 libavformat/matroskaenc.c | 45 ++++++++++++++++++++++++++++++++++++++-
 1 file changed, 44 insertions(+), 1 deletion(-)

diff --git a/libavformat/matroskaenc.c b/libavformat/matroskaenc.c
index d304b63663..aa7b8f4443 100644
--- a/libavformat/matroskaenc.c
+++ b/libavformat/matroskaenc.c
@@ -60,6 +60,7 @@
 #include "libavutil/rational.h"
 #include "libavutil/samplefmt.h"
 #include "libavutil/stereo3d.h"
+#include "libavutil/timecode.h"
 
 #include "libavcodec/av1.h"
 #include "libavcodec/bytestream.h"
@@ -81,6 +82,9 @@
 #define MAX_SUPPORTED_EBML_LENGTH FFMIN(MAX_EBML_LENGTH, INT_MAX)
 
 #define MAX_MATROSKA_BLOCK_ADD_ITU_T_T35 1 /* currently only 1 such element is supported */
+#define MAX_MATROSKA_BLOCK_ADD_SMPTE_12M 64 /* balance between intermediate buffer size and reality of non existence if so many timecodes streams for one content */
+
+#define MATROSKA_BLOCK_ADD_ID_SMPTE_12M 101 /* arbitrary value */
 
 #define MODE_MATROSKAv2 0x01
 #define MODE_WEBM       0x02
@@ -199,6 +203,7 @@ typedef struct mkv_track {
     int64_t         duration_offset;
     uint64_t        max_blockaddid;
     int             itu_t_t35_count;
+    int             timecode_count;
     int64_t         blockadditionmapping_offset;
     int             codecpriv_offset;
     unsigned        codecpriv_size;     ///< size reserved for CodecPrivate excluding header+length field
@@ -2825,11 +2830,12 @@ static int mkv_write_block(void *logctx, MatroskaMuxContext *mkv,
                            int force_blockgroup, int64_t relative_packet_pos)
 {
     uint8_t t35_buf[6 + AV_HDR_PLUS_MAX_PAYLOAD_SIZE];
+    uint8_t timecode_buf[MAX_MATROSKA_BLOCK_ADD_SMPTE_12M][8];
     uint8_t *side_data;
     size_t side_data_size;
     uint64_t additional_id;
     unsigned track_number = track->track_num;
-    EBML_WRITER(11 + MAX_MATROSKA_BLOCK_ADD_ITU_T_T35);
+    EBML_WRITER(11 + MAX_MATROSKA_BLOCK_ADD_ITU_T_T35 + MAX_MATROSKA_BLOCK_ADD_SMPTE_12M);
     int ret;
 
     mkv->cur_block.track  = track;
@@ -2908,6 +2914,36 @@ static int mkv_write_block(void *logctx, MatroskaMuxContext *mkv,
         }
     }
 
+    // Extract timecode from side data and write as BlockAdditional
+    if (par->codec_type == AVMEDIA_TYPE_VIDEO) {
+        side_data = av_packet_get_side_data(pkt, AV_PKT_DATA_S12M_TIMECODE, &side_data_size);
+        if (side_data && side_data_size >= sizeof(uint64_t)) {
+            uint64_t *side_data_64 = (uint64_t*)side_data;
+            uint64_t count = side_data_64[0];
+            if (side_data_size / sizeof(uint64_t) - 1 >= count ) {
+                uint64_t written_count = count;
+                side_data_64++;
+                if (count > MAX_MATROSKA_BLOCK_ADD_SMPTE_12M) {
+                    if (count > track->timecode_count) {
+                        av_log(logctx, AV_LOG_WARNING, "Too many SMPTE timecode streams in side data, discarding %"PRIu64" timecode streams.\n", count - MAX_MATROSKA_BLOCK_ADD_SMPTE_12M);
+                    }
+                    written_count = MAX_MATROSKA_BLOCK_ADD_SMPTE_12M;
+                }
+                for (uint64_t i = 0; i < written_count; i++) {
+                    uint64_t tc = side_data_64[i];
+                    uint8_t *payload = timecode_buf[i];
+                    AV_WB64(payload, tc);
+                    av_log(logctx, AV_LOG_DEBUG, "Writing SMPTE timecode from side data, pos %"PRIu64", to BlockAdditional: 0x%016lX (RFC 5484)\n", i + 1, tc);
+
+                    int blockaddid = MATROSKA_BLOCK_ADD_ID_SMPTE_12M + i;
+                    mkv_write_blockadditional(&writer, payload, 8, blockaddid);
+                    track->max_blockaddid = FFMAX(track->max_blockaddid, blockaddid);
+                }
+                track->timecode_count = FFMAX(track->timecode_count, count);
+            }
+        }
+    }
+
     ebml_writer_close_or_discard_master(&writer);
 
     if (!force_blockgroup && writer.nb_elements == 2) {
@@ -3320,11 +3356,15 @@ after_cues:
                 int max_block_add_id_count = 0;
                 int max_block_add_id_size = 3 + uint_size(track->max_blockaddid);
                 int block_type_t35_count = 0;
+                int block_type_timecode_count = 0;
 
                 if (!track->max_blockaddid)
                     continue;
 
                 /* check what is possible to write in the reserved space, in priority order */
+                for (int i = 0; i < track->timecode_count; i++) {
+                    block_type_timecode_count += mkv_simulate_blockadditional_header(s, &remaining_video_track_space, MATROSKA_BLOCK_ADD_ID_TYPE_SMPTE_12M, MATROSKA_BLOCK_ADD_ID_SMPTE_12M + i);
+                }
                 for (int i = 0; i < track->itu_t_t35_count; i++) {
                     block_type_t35_count += mkv_simulate_blockadditional_header(s, &remaining_video_track_space, MATROSKA_BLOCK_ADD_ID_TYPE_ITU_T_T35, MATROSKA_BLOCK_ADD_ID_TYPE_ITU_T_T35);
                 }                
@@ -3344,6 +3384,9 @@ after_cues:
                 for (int i = 0; i < block_type_t35_count; i++) {
                     mkv_write_blockadditional_header(s, MATROSKA_BLOCK_ADD_ID_TYPE_ITU_T_T35, MATROSKA_BLOCK_ADD_ID_ITU_T_T35);
                 }
+                for (int i = 0; i < block_type_timecode_count; i++) {
+                    mkv_write_blockadditional_header(s, MATROSKA_BLOCK_ADD_ID_TYPE_SMPTE_12M, MATROSKA_BLOCK_ADD_ID_SMPTE_12M + i);
+                }
                 if (remaining_video_track_space > 1) {
                     put_ebml_void(track_bc, remaining_video_track_space);
                 }
-- 
2.46.0.windows.1

_______________________________________________
ffmpeg-devel mailing list -- ffmpeg-devel@ffmpeg.org
To unsubscribe send an email to ffmpeg-devel-leave@ffmpeg.org

^ permalink raw reply	[flat|nested] 9+ messages in thread

* [FFmpeg-devel] Re: [PATCH 0/7] matroska: support of timecode
  2025-09-09 12:37 [FFmpeg-devel] [PATCH 0/7] matroska: support of timecode Jerome Martinez via ffmpeg-devel
                   ` (6 preceding siblings ...)
  2025-09-09 12:41 ` [FFmpeg-devel] [PATCH 7/7] matroskaenc: write " Jerome Martinez via ffmpeg-devel
@ 2025-09-11 15:52 ` Dave Rice via ffmpeg-devel
  7 siblings, 0 replies; 9+ messages in thread
From: Dave Rice via ffmpeg-devel @ 2025-09-11 15:52 UTC (permalink / raw)
  To: FFmpeg development discussions and patches; +Cc: Jerome Martinez, Dave Rice


> On Sep 9, 2025, at 8:37 AM, Jerome Martinez via ffmpeg-devel <ffmpeg-devel@ffmpeg.org> wrote:
> 
> This patch series adds support of timecode in Matroska files, specs are at:
> https://github.com/ietf-wg-cellar/matroska-specification/blob/master/cellar-codec/block_additional_mappings/smpte-st12-1-timecode.md
> 
> As ST 12-1 support was already partially implemented, using 32 bits rather than 64 bits (binary groups were not stored), the ST 12-1 side data format was changed from 32 bits to 64 bits rather than introducing another ST  12-1 side data internal "format", this may break code using it but not in the main repo (I found nothing using it in the main repo), is it fine?
> If we keep internally 32 bits for ST 12-1, any non zero timecode binary group stored in MKVs would be trashed.
> 
> The first 3 patches refactor BlockAddition support in MKV encoder in order to have it more versatile
> The 2 next patches adapt decklink_dec for writing 64 bit timecodes in side data rather than 32 bit timecodes
> The last 2 patches adds timecode support in Matroska

Tested with a decklink input with `./ffmpeg -y -f decklink -timecode_format rp188any -audio_input embedded -video_input sdi -format_code ntsc -i "UltraStudio 4K Mini" -c copy -t 1 output6.mkv`. Output includes the timecode values stored in Block Additionals. This is great!

Also noting that the timecode side data only seems to travel with streamcopy. If I switch the `-c ffv1` the side data is lost.

Kind Regards,
Dave

_______________________________________________
ffmpeg-devel mailing list -- ffmpeg-devel@ffmpeg.org
To unsubscribe send an email to ffmpeg-devel-leave@ffmpeg.org

^ permalink raw reply	[flat|nested] 9+ messages in thread

end of thread, other threads:[~2025-09-11 15:53 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2025-09-09 12:37 [FFmpeg-devel] [PATCH 0/7] matroska: support of timecode Jerome Martinez via ffmpeg-devel
2025-09-09 12:38 ` [FFmpeg-devel] [PATCH 1/7] matroskaenc: remove unused MaxBlockAdditionID Jerome Martinez via ffmpeg-devel
2025-09-09 12:39 ` [FFmpeg-devel] [PATCH 2/7] matroskaenc: reserve_video_track_space option Jerome Martinez via ffmpeg-devel
2025-09-09 12:39 ` [FFmpeg-devel] [PATCH 3/7] matroskaenc: increase default for reserved bytes in video Jerome Martinez via ffmpeg-devel
2025-09-09 12:40 ` [FFmpeg-devel] [PATCH 4/7] 32-bit timecode to 64-bit RFC 5484 timecode functions Jerome Martinez via ffmpeg-devel
2025-09-09 12:41 ` [FFmpeg-devel] [PATCH 5/7] decklink_dec: store timecode in 64-bit RFC 5484 format Jerome Martinez via ffmpeg-devel
2025-09-09 12:41 ` [FFmpeg-devel] [PATCH 6/7] matroskadec: read timecode in BlockAddition Jerome Martinez via ffmpeg-devel
2025-09-09 12:41 ` [FFmpeg-devel] [PATCH 7/7] matroskaenc: write " Jerome Martinez via ffmpeg-devel
2025-09-11 15:52 ` [FFmpeg-devel] Re: [PATCH 0/7] matroska: support of timecode Dave Rice via ffmpeg-devel

Git Inbox Mirror of the ffmpeg-devel mailing list - see https://ffmpeg.org/mailman/listinfo/ffmpeg-devel

This inbox may be cloned and mirrored by anyone:

	git clone --mirror https://master.gitmailbox.com/ffmpegdev/0 ffmpegdev/git/0.git

	# If you have public-inbox 1.1+ installed, you may
	# initialize and index your mirror using the following commands:
	public-inbox-init -V2 ffmpegdev ffmpegdev/ https://master.gitmailbox.com/ffmpegdev \
		ffmpegdev@gitmailbox.com
	public-inbox-index ffmpegdev

Example config snippet for mirrors.


AGPL code for this site: git clone https://public-inbox.org/public-inbox.git