* [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