-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy path0019-decklink_dec-support-labels.patch
More file actions
729 lines (701 loc) · 33.1 KB
/
0019-decklink_dec-support-labels.patch
File metadata and controls
729 lines (701 loc) · 33.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
From 8f7c53e54877d1964b9e9aa7cdb623bd7226708c Mon Sep 17 00:00:00 2001
From: Jerome Martinez <jerome@mediaarea.net>
Date: Thu, 4 Dec 2025 17:31:32 +0100
Subject: [PATCH 19/19] decklink_dec: support labels
---
fftools/ffprobe.c | 27 +++++++--
libavdevice/decklink_dec.cpp | 75 +++++++++++------------
libavformat/avformat.c | 81 +++++++++++++++++++++++++
libavformat/avformat.h | 25 ++++++++
libavformat/dump.c | 45 +++++++++++---
libavformat/matroska.h | 1 +
libavformat/matroskadec.c | 79 +++++++++++++++++--------
libavformat/matroskaenc.c | 111 ++++++++++++++++++++++++++++++++---
libavutil/timecode.h | 2 +
9 files changed, 363 insertions(+), 83 deletions(-)
diff --git a/fftools/ffprobe.c b/fftools/ffprobe.c
index 13ef4a73bb..bae25764f7 100644
--- a/fftools/ffprobe.c
+++ b/fftools/ffprobe.c
@@ -975,6 +975,7 @@ static void print_pkt_side_data(AVTextFormatContext *tfc,
const char *name = av_packet_side_data_name(sd->type);
avtext_print_section_header(tfc, sd, id_data);
+ av_log(NULL, AV_LOG_ERROR, "%s \n", name ? name : "unknown");
print_str("side_data_type", name ? name : "unknown");
if (sd->type == AV_PKT_DATA_DISPLAYMATRIX && sd->size >= 9*4) {
print_displaymatrix(tfc, (const int32_t*)sd->data);
@@ -1319,14 +1320,32 @@ static void print_frame_side_data(AVTextFormatContext *tfc,
av_timecode_make_mpeg_tc_string(tcbuf, *(int64_t *)(sd->data));
print_str("timecode", tcbuf);
} else if (sd->type == AV_FRAME_DATA_S12M_TIMECODE && sd->size >= 8) {
- uint64_t *tc = (uint64_t*)sd->data;
- int m = FFMIN(tc[0],3);
+ uint8_t *sd_count = (uint8_t*) (sd->data + 4);
+ uint8_t count = *sd_count;
avtext_print_section_header(tfc, NULL, SECTION_ID_FRAME_SIDE_DATA_TIMECODE_LIST);
- for (int j = 1; j <= m ; j++) {
+ for (int j = 0; j < count ; j++) {
+ uint8_t *sd_base = sd->data + 8 + (8 + 4 + 16) * j;
+ uint64_t *sd_tc = (uint64_t*)sd_base;
+ uint32_t *sd_id = (uint32_t*)(sd_base + 8);
+ char* sd_title = (char*)(sd_base + 8 + 4);
char tcbuf[AV_TIMECODE_STR_SIZE];
- av_timecode_make_smpte_tc_string2(tcbuf, stream->avg_frame_rate, tc[j], 0, 0);
+ uint64_t id = *sd_id;
+ uint64_t tc = *sd_tc;
+ av_timecode_make_smpte_tc_string2(tcbuf, stream->avg_frame_rate, av_timecode_parse_from_64bit(tc), 0, 0);
avtext_print_section_header(tfc, NULL, SECTION_ID_FRAME_SIDE_DATA_TIMECODE);
print_str("value", tcbuf);
+ print_int("id", id);
+ char tctitlebuf[17];
+ int k = 0;
+ for (; k < 16; k++)
+ if (!sd_title[k])
+ break;
+ if (k >= 16) {
+ memcpy(tctitlebuf, sd_title, k);
+ tctitlebuf[k] = '\0';
+ sd_title = tctitlebuf;
+ }
+ print_str("title", sd_title);
avtext_print_section_footer(tfc);
}
avtext_print_section_footer(tfc);
diff --git a/libavdevice/decklink_dec.cpp b/libavdevice/decklink_dec.cpp
index 370efa9a4b..539482bcca 100644
--- a/libavdevice/decklink_dec.cpp
+++ b/libavdevice/decklink_dec.cpp
@@ -712,7 +712,24 @@ static int64_t get_pkt_pts(IDeckLinkVideoInputFrame_v14_2_1 *videoFrame,
return pts;
}
-static int get_bmd_timecode(AVFormatContext *avctx, AVTimecode *tc, AVRational frame_rate, BMDTimecodeFormat tc_format, IDeckLinkVideoInputFrame_v14_2_1 *videoFrame)
+static const char *BMDTimecodeFormat_to_label(BMDTimecodeFormat tc_format)
+{
+ switch(tc_format) {
+ case bmdTimecodeRP188VITC1: return "ATC_VITC";
+ case bmdTimecodeRP188VITC2: return "ATC_VITC2";
+ case bmdTimecodeRP188LTC: return "ATC_LTC";
+ case bmdTimecodeRP188Any: return "VITC";
+ case bmdTimecodeVITC: return "VITC";
+ case bmdTimecodeVITCField2: return "VITC2";
+ case bmdTimecodeSerial: return "9PIN";
+#if BLACKMAGIC_DECKLINK_API_VERSION >= 0x0b000000
+ case bmdTimecodeRP188HighFrameRate: return "HFRTC";
+#endif
+ default: return NULL;
+ }
+}
+
+static int get_bmd_timecode(AVFormatContext *avctx, AVTimecode *tc, const char **tc_kind, AVRational frame_rate, BMDTimecodeFormat tc_format, IDeckLinkVideoInputFrame_v14_2_1 *videoFrame)
{
IDeckLinkTimecode *timecode;
int ret = AVERROR(ENOENT);
@@ -722,6 +739,7 @@ static int get_bmd_timecode(AVFormatContext *avctx, AVTimecode *tc, AVRational f
int hfr = 0;
#endif
if (videoFrame->GetTimecode(tc_format, &timecode) == S_OK) {
+ *tc_kind = BMDTimecodeFormat_to_label(tc_format);
uint8_t hh, mm, ss, ff;
if (timecode->GetComponents(&hh, &mm, &ss, &ff) == S_OK) {
int flags = (timecode->GetFlags() & bmdTimecodeIsDropFrame) ? AV_TIMECODE_FLAG_DROPFRAME : 0;
@@ -734,53 +752,39 @@ static int get_bmd_timecode(AVFormatContext *avctx, AVTimecode *tc, AVRational f
return ret;
}
-static int get_frame_timecode(AVFormatContext *avctx, decklink_ctx *ctx, AVTimecode *tc, IDeckLinkVideoInputFrame_v14_2_1 *videoFrame)
+static int get_frame_timecode(AVFormatContext *avctx, decklink_ctx *ctx, AVTimecode *tc, const char **tc_kind, IDeckLinkVideoInputFrame_v14_2_1 *videoFrame)
{
AVRational frame_rate = ctx->video_st->r_frame_rate;
int ret;
if (ctx->tc_format == (BMDTimecodeFormat)1) {
int count = 0;
- ret = get_bmd_timecode(avctx, tc + count, frame_rate, bmdTimecodeRP188VITC1, videoFrame);
- if (ret != AVERROR(ENOENT))
- count++;
- ret = get_bmd_timecode(avctx, tc + count, frame_rate, bmdTimecodeRP188VITC2, videoFrame);
- if (ret != AVERROR(ENOENT))
- count++;
- ret = get_bmd_timecode(avctx, tc + count, frame_rate, bmdTimecodeRP188LTC, videoFrame);
- if (ret != AVERROR(ENOENT))
- count++;
+ count += get_bmd_timecode(avctx, tc + count, tc_kind + count, frame_rate, bmdTimecodeRP188VITC1, videoFrame) != AVERROR(ENOENT);
+ count += get_bmd_timecode(avctx, tc + count, tc_kind + count, frame_rate, bmdTimecodeRP188VITC2, videoFrame) != AVERROR(ENOENT);
+ count += get_bmd_timecode(avctx, tc + count, tc_kind + count, frame_rate, bmdTimecodeRP188LTC, videoFrame) != AVERROR(ENOENT);
+ count += get_bmd_timecode(avctx, tc + count, tc_kind + count, frame_rate, bmdTimecodeVITC, videoFrame) != AVERROR(ENOENT);
+ count += get_bmd_timecode(avctx, tc + count, tc_kind + count, frame_rate, bmdTimecodeVITCField2, videoFrame) != AVERROR(ENOENT);
+ count += get_bmd_timecode(avctx, tc + count, tc_kind + count, frame_rate, bmdTimecodeSerial, videoFrame) != AVERROR(ENOENT);
#if BLACKMAGIC_DECKLINK_API_VERSION >= 0x0b000000
- ret = get_bmd_timecode(avctx, tc + count, frame_rate, bmdTimecodeRP188HighFrameRate, videoFrame);
- if (ret != AVERROR(ENOENT))
- count++;
+ count += get_bmd_timecode(avctx, tc + count, tc_kind + count, frame_rate, bmdTimecodeRP188HighFrameRate, videoFrame) != AVERROR(ENOENT);
#endif
- ret = get_bmd_timecode(avctx, tc + count, frame_rate, bmdTimecodeVITC, videoFrame);
- if (ret != AVERROR(ENOENT))
- count++;
- ret = get_bmd_timecode(avctx, tc + count, frame_rate, bmdTimecodeVITCField2, videoFrame);
- if (ret != AVERROR(ENOENT))
- count++;
- ret = get_bmd_timecode(avctx, tc + count, frame_rate, bmdTimecodeSerial, videoFrame);
- if (ret != AVERROR(ENOENT))
- count++;
return count;
}
/* 50/60 fps content has alternating VITC1 and VITC2 timecode (see SMPTE ST
* 12-2, section 7), so the native ordering of RP188Any (HFR, VITC1, LTC,
* VITC2) would not work because LTC might not contain the field flag.
* Therefore we query the types manually. */
- if (ctx->tc_format == bmdTimecodeRP188Any && av_cmp_q(frame_rate, av_make_q(30, 1)) == 1) {
+ if (ctx->tc_format == bmdTimecodeRP188Any /*&& av_cmp_q(frame_rate, av_make_q(30, 1)) == 1*/) {
#if BLACKMAGIC_DECKLINK_API_VERSION >= 0x0b000000
- ret = get_bmd_timecode(avctx, tc, frame_rate, bmdTimecodeRP188HighFrameRate, videoFrame);
+ ret = get_bmd_timecode(avctx, tc, tc_kind, frame_rate, bmdTimecodeRP188HighFrameRate, videoFrame);
if (ret == AVERROR(ENOENT))
#endif
- ret = get_bmd_timecode(avctx, tc, frame_rate, bmdTimecodeRP188VITC1, videoFrame);
+ ret = get_bmd_timecode(avctx, tc, tc_kind, frame_rate, bmdTimecodeRP188VITC1, videoFrame);
if (ret == AVERROR(ENOENT))
- ret = get_bmd_timecode(avctx, tc, frame_rate, bmdTimecodeRP188VITC2, videoFrame);
+ ret = get_bmd_timecode(avctx, tc, tc_kind, frame_rate, bmdTimecodeRP188VITC2, videoFrame);
if (ret == AVERROR(ENOENT))
- ret = get_bmd_timecode(avctx, tc, frame_rate, bmdTimecodeRP188LTC, videoFrame);
+ ret = get_bmd_timecode(avctx, tc, tc_kind, frame_rate, bmdTimecodeRP188LTC, videoFrame);
} else {
- ret = get_bmd_timecode(avctx, tc, frame_rate, ctx->tc_format, videoFrame);
+ ret = get_bmd_timecode(avctx, tc, tc_kind, frame_rate, ctx->tc_format, videoFrame);
}
return ret >= 0;
}
@@ -878,7 +882,8 @@ HRESULT decklink_input_callback::VideoInputFrameArrived(
// Handle Timecode (if requested)
if (ctx->tc_format) {
AVTimecode tcr[8];
- int count = get_frame_timecode(avctx, ctx, &tcr[0], videoFrame);
+ const char *tcr_kind[8];
+ int count = get_frame_timecode(avctx, ctx, &tcr[0], &tcr_kind[0], videoFrame);
if (count > 0) {
char tcstr[AV_TIMECODE_STR_SIZE];
const char *tc = av_timecode_make_string(&tcr[0], tcstr, 0);
@@ -887,13 +892,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) {
- int size = sizeof(uint64_t) * (1 + count);
- uint64_t *sd = (uint64_t *)av_packet_new_side_data(&pkt, AV_PKT_DATA_S12M_TIMECODE, size);
-
- if (sd) {
- *sd = count;
- for (int i = 0; i < count; i++)
- *(sd + 1 + i) = av_timecode_expand_to_64bit(av_timecode_get_smpte_from_framenum(&tcr[i], 0));
+ for (int i = 0; i < count; i++) {
+ uint64_t tc = av_timecode_expand_to_64bit(av_timecode_get_smpte_from_framenum(&tcr[i], 0));
+ av_packet_add_s12m_timecode_to_side_data(avctx, &pkt, S12M_TIMECODE_FLAG_ID_PRESENT | S12M_TIMECODE_FLAG_TITLE_PRESENT, tc, 0, tcr_kind[i]);
}
}
diff --git a/libavformat/avformat.c b/libavformat/avformat.c
index 18ca4643ee..3014f37fc1 100644
--- a/libavformat/avformat.c
+++ b/libavformat/avformat.c
@@ -873,3 +873,84 @@ int ff_format_io_close(AVFormatContext *s, AVIOContext **pb)
*pb = NULL;
return ret;
}
+
+#define add_block_timecode_count 4 //TODO
+static size_t s12m_timecode_get_size_per_item(unsigned flags)
+{
+ return 8 + 4 + 16;
+}
+
+static size_t s12m_timecode_get_size(unsigned flags)
+{
+ return add_block_timecode_count * s12m_timecode_get_size_per_item(flags);
+}
+
+static void s12m_timecode_init(uint8_t *sd, size_t sd_size, unsigned flags)
+{
+ uint32_t *sd_allocated = (uint32_t*)sd;
+ *sd_allocated = sd_size;
+ uint8_t *sd_count = (uint8_t*) (sd + 4);
+ *sd_count = 0;
+ uint8_t *item_size = sd + 5;
+ *item_size = s12m_timecode_get_size_per_item(flags);
+}
+
+static int av_packet_add_s12m_timecode_to_side_data_internal(AVFormatContext *ctx, uint8_t *sd, size_t sd_size, unsigned flags, uint64_t tc, uint32_t id, const char *title)
+{
+ // header: 0-3 allocated size; 4 count; 5 byte size per item; 6 flags; 7 reserved
+ // flags: 0 tc present; 1 id present; 2 title present
+ // content: 8 bytes tc; 4 bytes id; 16 bytes title
+
+ uint8_t *sd_count = (uint8_t*) (sd + 4);
+ uint8_t count = *sd_count;
+ uint8_t *item_size = sd + 5;
+
+ if (count >= add_block_timecode_count) {
+ av_log(ctx, AV_LOG_DEBUG, "There are more timecodes in the block than the count supported, extra timecodes are ignored.\n");
+ return 0; // not an actual error, just ignored, TODO: manage more tc
+ }
+ av_log(ctx, AV_LOG_DEBUG, "Reading SMPTE timecode from BlockAdditional: 0x%016lX (RFC 5484)\n", tc);
+
+ *sd_count = count + 1;
+ uint8_t *sd_base = sd + 8 + *item_size * count;
+ uint64_t *sd_tc = (uint64_t*)sd_base;
+ uint32_t *sd_id = (uint32_t*)(sd_base + 8);
+ char* sd_title = (char*)(sd_base + 8 + 4);
+ *sd_tc = tc;
+ *sd_id = id;
+ size_t title_size = title ? strlen(title) : 0;
+ if (title_size)
+ memcpy(sd_title, title, title_size);
+ memset(sd_title + title_size, 0, 16 - title_size);
+
+ return 0;
+}
+
+int av_packet_add_s12m_timecode_to_side_data(AVFormatContext *ctx, AVPacket *pkt, unsigned flags, uint64_t tc, uint32_t id, const char *title)
+{
+ size_t sd_size = 0;
+ uint8_t *sd = av_packet_get_side_data(pkt, AV_PKT_DATA_S12M_TIMECODE, &sd_size);
+ if (!sd) {
+ sd_size = s12m_timecode_get_size(flags);
+ sd = av_packet_new_side_data(pkt, AV_PKT_DATA_S12M_TIMECODE, sd_size);
+ if (!sd)
+ return 1;
+ s12m_timecode_init(sd, sd_size, flags);
+ }
+ return av_packet_add_s12m_timecode_to_side_data_internal(ctx, sd, sd_size, flags, tc, id, title);
+}
+
+// Add timecode track information to track information (no actual data)
+int av_packet_side_data_add_s12m_timecode_to(AVFormatContext *ctx, AVPacketSideData **psd, int *pnb_sd, unsigned flags, uint64_t tc, uint32_t id, const char *title)
+{
+ size_t sd_size = 0;
+ const AVPacketSideData *sd = av_packet_side_data_get(*psd, *pnb_sd, AV_PKT_DATA_S12M_TIMECODE);
+ if (!sd) {
+ sd_size = s12m_timecode_get_size(flags);
+ sd = av_packet_side_data_new(psd, pnb_sd, AV_PKT_DATA_S12M_TIMECODE, sd_size, 0);
+ if (!sd || !sd->data)
+ return 1;
+ s12m_timecode_init(sd->data, sd_size, flags);
+ }
+ return av_packet_add_s12m_timecode_to_side_data_internal(ctx, sd->data, sd->size, flags, tc, id, title);
+}
\ No newline at end of file
diff --git a/libavformat/avformat.h b/libavformat/avformat.h
index bd34132e00..493b6e2976 100644
--- a/libavformat/avformat.h
+++ b/libavformat/avformat.h
@@ -3011,6 +3011,31 @@ attribute_deprecated
AVRational av_stream_get_codec_timebase(const AVStream *st);
#endif
+/**
+ *
+ */
+#define S12M_TIMECODE_FLAG_TC_PRESENT 1
+#define S12M_TIMECODE_FLAG_ID_PRESENT 2
+#define S12M_TIMECODE_FLAG_TITLE_PRESENT 4
+typedef struct S12MTimecodeData {
+ unsigned flags;
+ uint32_t id;
+ uint64_t tc;
+ const char *title; /**< title may NOT be null terminated */
+ unsigned title_size; /**< Size of title */
+} S12MTimecodeData;
+
+/**
+ *
+ */
+int av_packet_add_s12m_timecode_to_side_data(AVFormatContext *ctx, AVPacket *pkt, unsigned flags, uint64_t tc, uint32_t id, const char *title);
+S12MTimecodeData av_packet_get_s12m_timecode(AVFormatContext *ctx, const AVPacket *pkt, unsigned pos);
+
+/**
+ *
+ */
+int av_packet_side_data_add_s12m_timecode_to(AVFormatContext *ctx, AVPacketSideData **psd, int *pnb_sd, unsigned flags, uint64_t tc, uint32_t id, const char *title);
+S12MTimecodeData av_packet_s12m_timecode_get(AVFormatContext *ctx, const AVPacketSideData *psd, int pnb_sd, unsigned pos);
/**
* @}
diff --git a/libavformat/dump.c b/libavformat/dump.c
index 734a6e0bbf..85ddefceae 100644
--- a/libavformat/dump.c
+++ b/libavformat/dump.c
@@ -427,19 +427,46 @@ static void dump_dovi_conf(void *ctx, const AVPacketSideData *sd,
}
static void dump_s12m_timecode(void *ctx, AVRational avg_frame_rate, const AVPacketSideData *sd,
- int log_level)
+ int log_level, const char *indent)
{
- const uint32_t *tc = (const uint32_t *)sd->data;
-
- if ((sd->size != sizeof(uint32_t) * 4) || (tc[0] > 3)) {
+ if (sd->size < sizeof(uint32_t)) {
av_log(ctx, AV_LOG_ERROR, "invalid data\n");
return;
}
-
- for (int j = 1; j <= tc[0]; j++) {
+
+ uint8_t *sd_count = (uint8_t*) (sd->data + 4);
+ uint8_t count = *sd_count;
+ for (int j = 0; j < count ; j++) {
+ uint8_t *sd_base = sd->data + 8 + (8 + 4 + 16) * j;
+ uint64_t *sd_tc = (uint64_t*)sd_base;
+ uint32_t *sd_id = (uint32_t*)(sd_base + 8);
+ char* sd_title = (char*)(sd_base + 8 + 4);
char tcbuf[AV_TIMECODE_STR_SIZE];
- av_timecode_make_smpte_tc_string2(tcbuf, avg_frame_rate, tc[j], 0, 0);
- av_log(ctx, log_level, "timecode - %s%s", tcbuf, j != tc[0] ? ", " : "");
+ uint64_t id = *sd_id;
+ uint64_t tc = *sd_tc;
+ av_timecode_make_smpte_tc_string2(tcbuf, avg_frame_rate, av_timecode_parse_from_64bit(tc), 0, 0);
+ char tctitlebuf[17];
+ int k = 0;
+ for (; k < 16; k++)
+ if (!sd_title[k])
+ break;
+ if (k >= 16) {
+ memcpy(tctitlebuf, sd_title, k);
+ tctitlebuf[k] = '\0';
+ sd_title = tctitlebuf;
+ }
+ if (j) {
+ const char *name = av_packet_side_data_name(AV_PKT_DATA_S12M_TIMECODE);
+ av_log(ctx, log_level, "\n%s %s: ", indent, name ? name : "");
+ }
+ char id_string[33];
+ if (id) {
+ snprintf(id_string, sizeof(id_string), "%" PRIu64, id);
+ }
+ else {
+ id_string[0] = '\0';
+ }
+ av_log(ctx, log_level, "%s%s%s%s%s", id ? "id: " : "", id_string, *sd_title && id ? " " : "", *sd_title ? "title: " : "", *sd_title ? sd_title : "");
}
}
@@ -521,7 +548,7 @@ static void dump_sidedata(void *ctx, const AVPacketSideData *side_data, int nb_s
dump_dovi_conf(ctx, sd, log_level);
break;
case AV_PKT_DATA_S12M_TIMECODE:
- dump_s12m_timecode(ctx, avg_frame_rate, sd, log_level);
+ dump_s12m_timecode(ctx, avg_frame_rate, sd, log_level, indent);
break;
case AV_PKT_DATA_AMBIENT_VIEWING_ENVIRONMENT:
dump_ambient_viewing_environment_metadata(ctx, sd, log_level);
diff --git a/libavformat/matroska.h b/libavformat/matroska.h
index d78b33d4b2..84debcfd64 100644
--- a/libavformat/matroska.h
+++ b/libavformat/matroska.h
@@ -223,6 +223,7 @@
#define MATROSKA_ID_TAGTARGETS_TRACKUID 0x63C5
#define MATROSKA_ID_TAGTARGETS_CHAPTERUID 0x63C4
#define MATROSKA_ID_TAGTARGETS_ATTACHUID 0x63C6
+#define MATROSKA_ID_TAGTARGETS_BLOCKADDID 0x63C7
/* IDs in the seekhead master */
#define MATROSKA_ID_SEEKENTRY 0x4DBB
diff --git a/libavformat/matroskadec.c b/libavformat/matroskadec.c
index 2c9408359c..ea4083178b 100644
--- a/libavformat/matroskadec.c
+++ b/libavformat/matroskadec.c
@@ -49,6 +49,7 @@
#include "libavutil/opt.h"
#include "libavutil/pixdesc.h"
#include "libavutil/time_internal.h"
+#include "libavutil/timecode.h"
#include "libavutil/spherical.h"
#include "libavcodec/bytestream.h"
@@ -259,6 +260,7 @@ typedef struct MatroskaBlockAdditionMapping {
char *name;
uint64_t type;
EbmlBin extradata;
+ char *title;
} MatroskaBlockAdditionMapping;
typedef struct MatroskaTrack {
@@ -341,6 +343,7 @@ typedef struct MatroskaTagTarget {
uint64_t trackuid;
uint64_t chapteruid;
uint64_t attachuid;
+ uint64_t blockaddid;
} MatroskaTagTarget;
typedef struct MatroskaTags {
@@ -449,7 +452,7 @@ typedef struct MatroskaDemuxContext {
// Removing the sizes breaks MSVC.
static EbmlSyntax ebml_syntax[3], matroska_segment[9], matroska_track_video_color[15], matroska_track_video[19],
matroska_track[33], matroska_track_encoding[6], matroska_track_encodings[2],
- matroska_track_combine_planes[2], matroska_track_operation[2], matroska_block_addition_mapping[5], matroska_tracks[2],
+ matroska_track_combine_planes[2], matroska_track_operation[2], matroska_block_addition_mapping[6], matroska_tracks[2],
matroska_attachments[2], matroska_chapter_entry[9], matroska_chapter[6], matroska_chapters[2],
matroska_index_entry[3], matroska_index[2], matroska_tag[3], matroska_tags[2], matroska_seekhead[2],
matroska_blockadditions[2], matroska_blockgroup[8], matroska_cluster_parsing[8];
@@ -604,6 +607,7 @@ static EbmlSyntax matroska_block_addition_mapping[] = {
{ MATROSKA_ID_BLKADDIDNAME, EBML_STR, 0, 0, offsetof(MatroskaBlockAdditionMapping, name) },
{ MATROSKA_ID_BLKADDIDTYPE, EBML_UINT, 0, 0, offsetof(MatroskaBlockAdditionMapping, type), { .u = MATROSKA_BLOCK_ADD_ID_TYPE_DEFAULT } },
{ MATROSKA_ID_BLKADDIDEXTRADATA, EBML_BIN, 0, 0, offsetof(MatroskaBlockAdditionMapping, extradata) },
+ { MATROSKA_ID_BLKADDIDTYPE, EBML_UTF8, 0, 0, offsetof(MatroskaBlockAdditionMapping, title) }, //TODO: fake ID, used here for freeing the char* automatically
CHILD_OF(matroska_track)
};
@@ -731,6 +735,7 @@ static EbmlSyntax matroska_tagtargets[] = {
{ MATROSKA_ID_TAGTARGETS_TRACKUID, EBML_UINT, 0, 0, offsetof(MatroskaTagTarget, trackuid), { .u = 0 } },
{ MATROSKA_ID_TAGTARGETS_CHAPTERUID, EBML_UINT, 0, 0, offsetof(MatroskaTagTarget, chapteruid), { .u = 0 } },
{ MATROSKA_ID_TAGTARGETS_ATTACHUID, EBML_UINT, 0, 0, offsetof(MatroskaTagTarget, attachuid), { .u = 0 } },
+ { MATROSKA_ID_TAGTARGETS_BLOCKADDID, EBML_UINT, 0, 0, offsetof(MatroskaTagTarget, blockaddid), { .u = 0 } },
CHILD_OF(matroska_tag)
};
@@ -1846,6 +1851,35 @@ static void matroska_convert_tag(AVFormatContext *s, EbmlList *list,
ff_metadata_conv(metadata, NULL, ff_mkv_metadata_conv);
}
+static void matroska_convert_blockaddid_tag(AVFormatContext *s, EbmlList *list,
+ const EbmlList *mappings_list, uint64_t mappings_id)
+{
+ MatroskaTag *tags = list->elem;
+ MatroskaBlockAdditionMapping *mappings = mappings_list->elem;
+ int i;
+
+ for (i = 0; i < list->nb_elem; i++) {
+ const char *lang = tags[i].lang &&
+ strcmp(tags[i].lang, "und") ? tags[i].lang : NULL;
+
+ if (lang)
+ continue; // Only the default one
+
+ if (!tags[i].name) {
+ av_log(s, AV_LOG_WARNING, "Skipping invalid tag with no TagName.\n");
+ continue;
+ }
+ for (int j = 0; j < mappings_list->nb_elem; j++) {
+ MatroskaBlockAdditionMapping *mapping = &mappings[j];
+ uint64_t id = mapping->value;
+ if (mappings_id != id)
+ continue;
+ av_log(s, AV_LOG_INFO, "blockaddid %"PRId64", title is %s\n", id, tags[i].string);
+ mapping->title = av_strdup(tags[i].string);
+ }
+ }
+}
+
static void matroska_convert_tags(AVFormatContext *s)
{
MatroskaDemuxContext *matroska = s->priv_data;
@@ -1893,7 +1927,12 @@ static void matroska_convert_tags(AVFormatContext *s)
for (j = 0; j < matroska->tracks.nb_elem; j++) {
if (track[j].uid == tags[i].target.trackuid &&
track[j].stream) {
- matroska_convert_tag(s, &tags[i].tag,
+ if (tags[i].target.blockaddid) {
+ matroska_convert_blockaddid_tag(s, &tags[i].tag,
+ &track->block_addition_mappings, tags[i].target.blockaddid);
+ }
+ else
+ matroska_convert_tag(s, &tags[i].tag,
&track[j].stream->metadata, NULL);
found = 1;
}
@@ -3462,6 +3501,17 @@ static int matroska_read_header(AVFormatContext *s)
matroska_convert_tags(s);
+ MatroskaTrack *tracks = matroska->tracks.elem;
+ for (i = 0; i < matroska->tracks.nb_elem; i++) {
+ MatroskaTrack *track = &tracks[i];
+ const EbmlList *mappings_list = &track->block_addition_mappings;
+ MatroskaBlockAdditionMapping *mappings = mappings_list->elem;
+
+ for (int i = 0; i < mappings_list->nb_elem; i++) {
+ av_packet_side_data_add_s12m_timecode_to(s, &track->stream->codecpar->coded_side_data, &track->stream->codecpar->nb_coded_side_data, S12M_TIMECODE_FLAG_ID_PRESENT | S12M_TIMECODE_FLAG_TITLE_PRESENT, 0, mappings[i].value, mappings[i].title);
+ }
+ }
+
return 0;
}
@@ -3973,29 +4023,8 @@ static int matroska_parse_block_additional(MatroskaDemuxContext *matroska,
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;
+ uint64_t tc = AV_RB64(data);
+ return av_packet_add_s12m_timecode_to_side_data(matroska->ctx, pkt, S12M_TIMECODE_FLAG_ID_PRESENT | S12M_TIMECODE_FLAG_TITLE_PRESENT, tc, id, mapping->title);
}
default:
break;
diff --git a/libavformat/matroskaenc.c b/libavformat/matroskaenc.c
index f3e8b8e09f..655df38192 100644
--- a/libavformat/matroskaenc.c
+++ b/libavformat/matroskaenc.c
@@ -201,9 +201,11 @@ typedef struct mkv_track {
int64_t last_timestamp;
int64_t duration;
int64_t duration_offset;
+ int64_t more_tags_offset;
uint64_t max_blockaddid;
int itu_t_t35_count;
int timecode_count;
+ char timecode_label[8][16];
int64_t blockadditionmapping_offset;
int codecpriv_offset;
unsigned codecpriv_size; ///< size reserved for CodecPrivate excluding header+length field
@@ -3480,6 +3482,46 @@ static int mkv_write_tags(AVFormatContext *s)
return ret;
if (seekable)
track->duration_offset = avio_tell(mkv->tags.bc) - DURATION_SIMPLETAG_SIZE;
+
+ if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)
+ {
+ /*
+ AVIOContext *pb = mkv->tmp_bc;
+ ebml_master targets;
+
+ targets = start_ebml_master(pb, MATROSKA_ID_TAGTARGETS, 4 + 1 + 8);
+ put_ebml_uid(pb, MATROSKA_ID_TAGTARGETS_TRACKUID, track->uid);
+ put_ebml_uint(pb, MATROSKA_ID_TAGTARGETS_BLOCKADDID, 101);
+ end_ebml_master(pb, targets);
+
+ targets = start_ebml_master(pb, MATROSKA_ID_SIMPLETAG, 4 + 1 + 8);
+ put_ebml_string(pb, MATROSKA_ID_TAGNAME, "VITC");
+ end_ebml_master(pb, targets);
+
+
+
+ uint8_t *buf;
+ int ret = 0, size, tag_written = 0;
+
+ AVIOContext *const tmp_bc = pb;
+ size = avio_get_dyn_buf(tmp_bc, &buf);
+ if (tmp_bc->error) {
+ ret = tmp_bc->error;
+ //goto end;
+ }
+ if (!pb) {
+ ret = start_ebml_master_crc32(&pb, mkv);
+ //if (ret < 0)
+ //goto end;
+ }
+ ffio_reset_dyn_buf(pb);
+ put_ebml_binary(mkv->tags.bc, MATROSKA_ID_TAG, buf, size);
+ */
+
+ track->more_tags_offset = avio_tell(mkv->tags.bc);
+ put_ebml_void(mkv->tags.bc, 8 * 32);
+
+ }
}
if (mkv->nb_attachments && !IS_WEBM(mkv)) {
@@ -4089,12 +4131,16 @@ static int mkv_write_block(void *logctx, MatroskaMuxContext *mkv,
uint64_t count = 0;
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;
- count = side_data_64[0];
- if (side_data_size / sizeof(uint64_t) - 1 >= count ) {
+ if (side_data && side_data_size >= 8) {
+
+ // header: 0-3 allocated size; 4 count; 5 byte size per item; 6 flags; 7 reserved
+ // flags: 0 id present; 1 title present
+ // content: 8 bytes tc; 4 bytes id; 16 bytes 0 terminated or 16 bytes UTF8 title
+
+ uint8_t count = *(side_data + 4);
+ uint8_t item_size = *(side_data + 5);
+ if (item_size && (side_data_size - 8) / item_size >= 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);
@@ -4102,10 +4148,15 @@ static int mkv_write_block(void *logctx, MatroskaMuxContext *mkv,
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 *sd_base = side_data + 8 + item_size * i;
+ uint64_t *sd_tc = (uint64_t*)sd_base;
+ uint32_t *sd_id = (uint32_t*)(sd_base + 8);
+ char* sd_title = (char*)(sd_base + 8 + 4);
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);
+ AV_WB64(payload, *sd_tc);
+ memcpy(track->timecode_label[i], sd_title, 16);
+
+ av_log(logctx, AV_LOG_DEBUG, "Writing SMPTE timecode from side data, pos %"PRIu64", to BlockAdditional: 0x%016lX (RFC 5484)\n", i + 1, *sd_tc);
int blockaddid = MATROSKA_BLOCK_ADD_ID_SMPTE_12M + i;
mkv_write_blockadditional(&writer, payload, 8, blockaddid);
@@ -4582,6 +4633,50 @@ after_cues:
if (remaining_video_track_space > 1) {
put_ebml_void(track_bc, remaining_video_track_space);
}
+
+ int remaining_bytes = 8 * 32;
+ AVIOContext *pb = mkv->tmp_bc;
+
+ avio_seek(mkv->tags.bc, track->more_tags_offset, SEEK_SET);
+
+ for (int i = 0; i < track->timecode_count; i++) {
+ if (!*track->timecode_label[i])
+ continue;
+
+ ebml_master targets;
+
+ targets = start_ebml_master(pb, MATROSKA_ID_TAGTARGETS, 4 + 1 + 8);
+ put_ebml_uid(pb, MATROSKA_ID_TAGTARGETS_TRACKUID, track->uid);
+ put_ebml_uint(pb, MATROSKA_ID_TAGTARGETS_BLOCKADDID, 101 + i);
+ end_ebml_master(pb, targets);
+
+ targets = start_ebml_master(pb, MATROSKA_ID_SIMPLETAG, 4 + 1 + 8);
+ put_ebml_string(pb, MATROSKA_ID_TAGNAME, "TITLE");
+ put_ebml_string(pb, MATROSKA_ID_TAGSTRING, track->timecode_label[i]);
+ end_ebml_master(pb, targets);
+
+
+
+ uint8_t *buf;
+ int ret = 0, size, tag_written = 0;
+
+ AVIOContext *const tmp_bc = pb;
+ size = avio_get_dyn_buf(tmp_bc, &buf);
+ if (tmp_bc->error) {
+ ret = tmp_bc->error;
+ //goto end;
+ }
+ if (!pb) {
+ ret = start_ebml_master_crc32(&pb, mkv);
+ //if (ret < 0)
+ //goto end;
+ }
+ ffio_reset_dyn_buf(pb);
+
+ put_ebml_binary(mkv->tags.bc, MATROSKA_ID_TAG, buf, size);
+ remaining_bytes -= 3 + size;
+ }
+ put_ebml_void(mkv->tags.bc, remaining_bytes);
}
}
diff --git a/libavutil/timecode.h b/libavutil/timecode.h
index 81713a9adf..6c4af1afdc 100644
--- a/libavutil/timecode.h
+++ b/libavutil/timecode.h
@@ -28,6 +28,8 @@
#define AVUTIL_TIMECODE_H
#include <stdint.h>
+#include "libavformat/avformat.h"
+#include "libavcodec/packet.h"
#include "rational.h"
#define AV_TIMECODE_STR_SIZE 23
--
2.52.0