FFmpeg coverage


Directory: ../../../ffmpeg/
File: src/libavformat/mccenc.c
Date: 2025-09-01 20:07:09
Exec Total Coverage
Lines: 143 199 71.9%
Functions: 4 5 80.0%
Branches: 75 146 51.4%

Line Branch Exec Source
1 /*
2 * MCC subtitle muxer
3 * Copyright (c) 2025 Jacob Lifshay
4 * Copyright (c) 2017 Paul B Mahol
5 *
6 * This file is part of FFmpeg.
7 *
8 * FFmpeg is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
12 *
13 * FFmpeg is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with FFmpeg; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21 */
22
23 #include "avformat.h"
24 #include "internal.h"
25 #include "mux.h"
26
27 #include "libavcodec/codec_id.h"
28 #include "libavcodec/smpte_436m.h"
29
30 #include "libavutil/avassert.h"
31 #include "libavutil/avstring.h"
32 #include "libavutil/error.h"
33 #include "libavutil/ffversion.h"
34 #include "libavutil/log.h"
35 #include "libavutil/macros.h"
36 #include "libavutil/opt.h"
37 #include "libavutil/parseutils.h"
38 #include "libavutil/rational.h"
39 #include "libavutil/time_internal.h" // for localtime_r
40 #include "libavutil/timecode.h"
41
42 typedef struct MCCContext {
43 const AVClass *class;
44 AVTimecode timecode;
45 int64_t twenty_four_hr;
46 char *override_time_code_rate;
47 int use_u_alias;
48 unsigned mcc_version;
49 char *creation_program;
50 char *creation_time;
51 } MCCContext;
52
53 typedef enum MCCVersion
54 {
55 MCC_VERSION_1 = 1,
56 MCC_VERSION_2 = 2,
57 MCC_VERSION_MIN = MCC_VERSION_1,
58 MCC_VERSION_MAX = MCC_VERSION_2,
59 } MCCVersion;
60
61 static const char mcc_header_v1[] = //
62 "File Format=MacCaption_MCC V1.0\n"
63 "\n"
64 "///////////////////////////////////////////////////////////////////////////////////\n"
65 "// Computer Prompting and Captioning Company\n"
66 "// Ancillary Data Packet Transfer File\n"
67 "//\n"
68 "// Permission to generate this format is granted provided that\n"
69 "// 1. This ANC Transfer file format is used on an as-is basis and no warranty is given, and\n"
70 "// 2. This entire descriptive information text is included in a generated .mcc file.\n"
71 "//\n"
72 "// General file format:\n"
73 "// HH:MM:SS:FF(tab)[Hexadecimal ANC data in groups of 2 characters]\n"
74 "// Hexadecimal data starts with the Ancillary Data Packet DID (Data ID defined in S291M)\n"
75 "// and concludes with the Check Sum following the User Data Words.\n"
76 "// Each time code line must contain at most one complete ancillary data packet.\n"
77 "// To transfer additional ANC Data successive lines may contain identical time code.\n"
78 "// Time Code Rate=[24, 25, 30, 30DF, 50, 60]\n"
79 "//\n"
80 "// ANC data bytes may be represented by one ASCII character according to the following schema:\n"
81 "// G FAh 00h 00h\n"
82 "// H 2 x (FAh 00h 00h)\n"
83 "// I 3 x (FAh 00h 00h)\n"
84 "// J 4 x (FAh 00h 00h)\n"
85 "// K 5 x (FAh 00h 00h)\n"
86 "// L 6 x (FAh 00h 00h)\n"
87 "// M 7 x (FAh 00h 00h)\n"
88 "// N 8 x (FAh 00h 00h)\n"
89 "// O 9 x (FAh 00h 00h)\n"
90 "// P FBh 80h 80h\n"
91 "// Q FCh 80h 80h\n"
92 "// R FDh 80h 80h\n"
93 "// S 96h 69h\n"
94 "// T 61h 01h\n"
95 "// U E1h 00h 00h 00h\n"
96 "// Z 00h\n"
97 "//\n"
98 "///////////////////////////////////////////////////////////////////////////////////\n";
99
100 static const char mcc_header_v2[] = //
101 "File Format=MacCaption_MCC V2.0\n"
102 "\n"
103 "///////////////////////////////////////////////////////////////////////////////////\n"
104 "// Computer Prompting and Captioning Company\n"
105 "// Ancillary Data Packet Transfer File\n"
106 "//\n"
107 "// Permission to generate this format is granted provided that\n"
108 "// 1. This ANC Transfer file format is used on an as-is basis and no warranty is given, and\n"
109 "// 2. This entire descriptive information text is included in a generated .mcc file.\n"
110 "//\n"
111 "// General file format:\n"
112 "// HH:MM:SS:FF(tab)[Hexadecimal ANC data in groups of 2 characters]\n"
113 "// Hexadecimal data starts with the Ancillary Data Packet DID (Data ID defined in S291M)\n"
114 "// and concludes with the Check Sum following the User Data Words.\n"
115 "// Each time code line must contain at most one complete ancillary data packet.\n"
116 "// To transfer additional ANC Data successive lines may contain identical time code.\n"
117 "// Time Code Rate=[24, 25, 30, 30DF, 50, 60, 60DF]\n"
118 "//\n"
119 "// ANC data bytes may be represented by one ASCII character according to the following schema:\n"
120 "// G FAh 00h 00h\n"
121 "// H 2 x (FAh 00h 00h)\n"
122 "// I 3 x (FAh 00h 00h)\n"
123 "// J 4 x (FAh 00h 00h)\n"
124 "// K 5 x (FAh 00h 00h)\n"
125 "// L 6 x (FAh 00h 00h)\n"
126 "// M 7 x (FAh 00h 00h)\n"
127 "// N 8 x (FAh 00h 00h)\n"
128 "// O 9 x (FAh 00h 00h)\n"
129 "// P FBh 80h 80h\n"
130 "// Q FCh 80h 80h\n"
131 "// R FDh 80h 80h\n"
132 "// S 96h 69h\n"
133 "// T 61h 01h\n"
134 "// U E1h 00h 00h 00h\n"
135 "// Z 00h\n"
136 "//\n"
137 "///////////////////////////////////////////////////////////////////////////////////\n";
138
139 /**
140 * generated with the bash command:
141 * ```bash
142 * URL="https://code.ffmpeg.org/FFmpeg/FFmpeg/src/branch/master/libavformat/mccenc.c"
143 * python3 -c "from uuid import *; print(str(uuid5(NAMESPACE_URL, '$URL')).upper())"
144 * ```
145 */
146 static const char mcc_ffmpeg_uuid[] = "0087C4F6-A6B4-5469-8C8E-BBF44950401D";
147
148 static AVRational valid_time_code_rates[] = {
149 { .num = 24, .den = 1 },
150 { .num = 25, .den = 1 },
151 { .num = 30000, .den = 1001 },
152 { .num = 30, .den = 1 },
153 { .num = 50, .den = 1 },
154 { .num = 60000, .den = 1001 },
155 { .num = 60, .den = 1 },
156 };
157
158 5 static int mcc_write_header(AVFormatContext *avf)
159 {
160 5 MCCContext *mcc = avf->priv_data;
161
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5 times.
5 if (avf->nb_streams != 1) {
162 av_log(avf, AV_LOG_ERROR, "mcc muxer supports at most one stream\n");
163 return AVERROR(EINVAL);
164 }
165 5 avpriv_set_pts_info(avf->streams[0], 64, mcc->timecode.rate.den, mcc->timecode.rate.num);
166 5 const char *mcc_header = mcc_header_v1;
167
1/3
✗ Branch 0 not taken.
✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
5 switch ((MCCVersion)mcc->mcc_version) {
168 case MCC_VERSION_1:
169 if (mcc->timecode.fps == 60 && mcc->timecode.flags & AV_TIMECODE_FLAG_DROPFRAME) {
170 av_log(avf, AV_LOG_FATAL, "MCC Version 1.0 doesn't support 60DF (59.94 fps drop-frame)");
171 return AVERROR(EINVAL);
172 }
173 break;
174 5 case MCC_VERSION_2:
175 5 mcc_header = mcc_header_v2;
176 5 break;
177 }
178 5 const char *creation_program = mcc->creation_program;
179
1/2
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
5 if (!creation_program) {
180
1/2
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
5 if (avf->flags & AVFMT_FLAG_BITEXACT)
181 5 creation_program = "FFmpeg";
182 else
183 creation_program = "FFmpeg version " FFMPEG_VERSION;
184 } else if (strchr(creation_program, '\n')) {
185 av_log(avf, AV_LOG_FATAL, "creation_program must not contain multiple lines of text");
186 return AVERROR(EINVAL);
187 }
188
2/4
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 5 times.
5 if (avf->flags & AVFMT_FLAG_BITEXACT && !av_strcasecmp(mcc->creation_time, "now"))
189 av_log(avf, AV_LOG_ERROR, "creation_time must be overridden for bit-exact output");
190 5 int64_t timeval = 0;
191 5 int ret = av_parse_time(&timeval, mcc->creation_time, 0);
192
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5 times.
5 if (ret < 0) {
193 av_log(avf, AV_LOG_FATAL, "can't parse creation_time");
194 return ret;
195 }
196 struct tm tm;
197
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 5 times.
5 if (!localtime_r((time_t[1]){ timeval / 1000000 }, &tm))
198 return AVERROR(EINVAL);
199 // we can't rely on having the C locale, so convert the date/time to a string ourselves:
200 static const char *const months[12] = {
201 "January",
202 "February",
203 "March",
204 "April",
205 "May",
206 "June",
207 "July",
208 "August",
209 "September",
210 "October",
211 "November",
212 "December",
213 };
214 // assert that values are sane so we don't index out of bounds
215
2/4
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 5 times.
5 av_assert0(tm.tm_mon >= 0 && tm.tm_mon <= FF_ARRAY_ELEMS(months));
216 5 const char *month = months[tm.tm_mon];
217
218 static const char *const weekdays[7] = {
219 "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"
220 };
221 // assert that values are sane so we don't index out of bounds
222
2/4
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 5 times.
5 av_assert0(tm.tm_wday >= 0 && tm.tm_wday < FF_ARRAY_ELEMS(weekdays));
223 5 const char *weekday = weekdays[tm.tm_wday];
224
225 5 avio_printf(avf->pb,
226 "%s\n"
227 "UUID=%s\n"
228 "Creation Program=%s\n"
229 "Creation Date=%s, %s %d, %d\n"
230 "Creation Time=%02d:%02d:%02d\n"
231 "Time Code Rate=%u%s\n\n",
232 mcc_header,
233 mcc_ffmpeg_uuid,
234 creation_program,
235 weekday,
236 month,
237 tm.tm_mday,
238 5 tm.tm_year + 1900,
239 tm.tm_hour,
240 tm.tm_min,
241 tm.tm_sec,
242 mcc->timecode.fps,
243
1/2
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
5 mcc->timecode.flags & AV_TIMECODE_FLAG_DROPFRAME ? "DF" : "");
244
245 5 return 0;
246 }
247
248 /// convert the input bytes to hexadecimal with mcc's aliases
249 346 static void mcc_bytes_to_hex(char *dest, const uint8_t *bytes, size_t bytes_size, int use_u_alias)
250 {
251
2/2
✓ Branch 0 taken 6120 times.
✓ Branch 1 taken 346 times.
6466 while (bytes_size != 0) {
252
6/7
✓ Branch 0 taken 256 times.
✓ Branch 1 taken 667 times.
✓ Branch 2 taken 332 times.
✓ Branch 3 taken 335 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 666 times.
✓ Branch 6 taken 3864 times.
6120 switch (bytes[0]) {
253 256 case 0xFA:
254 256 *dest = '\0';
255
2/2
✓ Branch 0 taken 2304 times.
✓ Branch 1 taken 256 times.
2560 for (unsigned char code = 'G'; code <= (unsigned char)'O'; code++) {
256
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2304 times.
2304 if (bytes_size < 3)
257 break;
258
3/6
✓ Branch 0 taken 2304 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2304 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 2304 times.
✗ Branch 5 not taken.
2304 if (bytes[0] != 0xFA || bytes[1] != 0 || bytes[2] != 0)
259 break;
260 2304 *dest = code;
261 2304 bytes += 3;
262 2304 bytes_size -= 3;
263 }
264
1/2
✓ Branch 0 taken 256 times.
✗ Branch 1 not taken.
256 if (*dest) {
265 256 dest++;
266 256 continue;
267 }
268 break;
269 667 case 0xFB:
270 case 0xFC:
271 case 0xFD:
272
5/6
✓ Branch 0 taken 664 times.
✓ Branch 1 taken 3 times.
✓ Branch 2 taken 603 times.
✓ Branch 3 taken 61 times.
✓ Branch 4 taken 603 times.
✗ Branch 5 not taken.
667 if (bytes_size >= 3 && bytes[1] == 0x80 && bytes[2] == 0x80) {
273 603 *dest++ = bytes[0] - 0xFB + 'P';
274 603 bytes += 3;
275 603 bytes_size -= 3;
276 603 continue;
277 }
278 64 break;
279 332 case 0x96:
280
3/4
✓ Branch 0 taken 332 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 330 times.
✓ Branch 3 taken 2 times.
332 if (bytes_size >= 2 && bytes[1] == 0x69) {
281 330 *dest++ = 'S';
282 330 bytes += 2;
283 330 bytes_size -= 2;
284 330 continue;
285 }
286 2 break;
287 335 case 0x61:
288
3/4
✓ Branch 0 taken 335 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 330 times.
✓ Branch 3 taken 5 times.
335 if (bytes_size >= 2 && bytes[1] == 0x01) {
289 330 *dest++ = 'T';
290 330 bytes += 2;
291 330 bytes_size -= 2;
292 330 continue;
293 }
294 5 break;
295 case 0xE1:
296 if (use_u_alias && bytes_size >= 4 && bytes[1] == 0 && bytes[2] == 0 && bytes[3] == 0) {
297 *dest++ = 'U';
298 bytes += 4;
299 bytes_size -= 4;
300 continue;
301 }
302 break;
303 666 case 0:
304 666 *dest++ = 'Z';
305 666 bytes++;
306 666 bytes_size--;
307 666 continue;
308 3864 default:
309 // any other bytes falls through to writing hex
310 3864 break;
311 }
312
2/2
✓ Branch 0 taken 7870 times.
✓ Branch 1 taken 3935 times.
11805 for (int shift = 4; shift >= 0; shift -= 4) {
313 7870 int v = (bytes[0] >> shift) & 0xF;
314
2/2
✓ Branch 0 taken 6174 times.
✓ Branch 1 taken 1696 times.
7870 if (v < 0xA)
315 6174 *dest++ = v + '0';
316 else
317 1696 *dest++ = v - 0xA + 'A';
318 }
319 3935 bytes++;
320 3935 bytes_size--;
321 }
322 346 *dest = '\0';
323 346 }
324
325 330 static int mcc_write_packet(AVFormatContext *avf, AVPacket *pkt)
326 {
327 330 MCCContext *mcc = avf->priv_data;
328 330 int64_t pts = pkt->pts;
329 int ret;
330
331
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 330 times.
330 if (pts == AV_NOPTS_VALUE) {
332 av_log(avf, AV_LOG_WARNING, "Insufficient timestamps.\n");
333 return 0;
334 }
335
336 char timecode_str[AV_TIMECODE_STR_SIZE];
337
338 // wrap pts values at 24hr ourselves since they can be bigger than fits in an int
339 330 av_timecode_make_string(&mcc->timecode, timecode_str, pts % mcc->twenty_four_hr);
340
341
2/2
✓ Branch 0 taken 3630 times.
✓ Branch 1 taken 330 times.
3960 for (char *p = timecode_str; *p; p++) {
342 // .mcc doesn't use ; for drop-frame time codes
343
2/2
✓ Branch 0 taken 330 times.
✓ Branch 1 taken 3300 times.
3630 if (*p == ';')
344 330 *p = ':';
345 }
346
347 AVSmpte436mAncIterator iter;
348 330 ret = av_smpte_436m_anc_iter_init(&iter, pkt->data, pkt->size);
349
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 330 times.
330 if (ret < 0)
350 return ret;
351 AVSmpte436mCodedAnc coded_anc;
352
2/2
✓ Branch 1 taken 346 times.
✓ Branch 2 taken 330 times.
676 while ((ret = av_smpte_436m_anc_iter_next(&iter, &coded_anc)) >= 0) {
353 AVSmpte291mAnc8bit anc;
354 346 ret = av_smpte_291m_anc_8bit_decode(
355 346 &anc, coded_anc.payload_sample_coding, coded_anc.payload_sample_count, coded_anc.payload, avf);
356
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 346 times.
346 if (ret < 0)
357 return ret;
358 // 4 for did, sdid_or_dbn, data_count, and checksum fields.
359 uint8_t mcc_anc[4 + AV_SMPTE_291M_ANC_PAYLOAD_CAPACITY];
360 346 size_t mcc_anc_len = 0;
361
362 346 mcc_anc[mcc_anc_len++] = anc.did;
363 346 mcc_anc[mcc_anc_len++] = anc.sdid_or_dbn;
364 346 mcc_anc[mcc_anc_len++] = anc.data_count;
365 346 memcpy(mcc_anc + mcc_anc_len, anc.payload, anc.data_count);
366 346 mcc_anc_len += anc.data_count;
367 346 mcc_anc[mcc_anc_len++] = anc.checksum;
368
369 unsigned field_number;
370
2/3
✓ Branch 0 taken 338 times.
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
346 switch (coded_anc.wrapping_type) {
371 338 case AV_SMPTE_436M_WRAPPING_TYPE_VANC_FRAME:
372 case AV_SMPTE_436M_WRAPPING_TYPE_VANC_FIELD_1:
373 case AV_SMPTE_436M_WRAPPING_TYPE_VANC_PROGRESSIVE_FRAME:
374 338 field_number = 0;
375 338 break;
376 8 case AV_SMPTE_436M_WRAPPING_TYPE_VANC_FIELD_2:
377 8 field_number = 1;
378 8 break;
379 default:
380 av_log(avf,
381 AV_LOG_WARNING,
382 "Unsupported SMPTE 436M ANC Wrapping Type %#x -- discarding ANC packet",
383 (unsigned)coded_anc.wrapping_type);
384 continue;
385 }
386
387 346 char field_and_line[32] = "";
388
2/2
✓ Branch 0 taken 48 times.
✓ Branch 1 taken 298 times.
346 if (coded_anc.line_number != 9) {
389 48 snprintf(field_and_line, sizeof(field_and_line), ".%u,%u", field_number, (unsigned)coded_anc.line_number);
390
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 298 times.
298 } else if (field_number != 0) {
391 snprintf(field_and_line, sizeof(field_and_line), ".%u", field_number);
392 }
393
394
1/3
✗ Branch 0 not taken.
✓ Branch 1 taken 346 times.
✗ Branch 2 not taken.
346 switch ((MCCVersion)mcc->mcc_version) {
395 case MCC_VERSION_1:
396 if (field_and_line[0] != '\0') {
397 av_log(avf,
398 AV_LOG_WARNING,
399 "MCC Version 1.0 doesn't support ANC packets where the field number (got %u) isn't 0 and "
400 "line number (got %u) isn't 9: discarding ANC packet",
401 field_number,
402 (unsigned)coded_anc.line_number);
403 continue;
404 }
405 break;
406 346 case MCC_VERSION_2:
407 346 break;
408 }
409
410 // 1 for terminating nul. 2 since there's 2 hex digits per byte.
411 char hex[1 + 2 * sizeof(mcc_anc)];
412 346 mcc_bytes_to_hex(hex, mcc_anc, mcc_anc_len, mcc->use_u_alias);
413 346 avio_printf(avf->pb, "%s%s\t%s\n", timecode_str, field_and_line, hex);
414 }
415
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 330 times.
330 if (ret != AVERROR_EOF)
416 return ret;
417 330 return 0;
418 }
419
420 5 static int mcc_init(AVFormatContext *avf)
421 {
422 5 MCCContext *mcc = avf->priv_data;
423 int ret;
424
425
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5 times.
5 if (avf->nb_streams != 1) {
426 av_log(avf, AV_LOG_ERROR, "mcc muxer supports at most one stream\n");
427 return AVERROR(EINVAL);
428 }
429
430 5 AVStream *st = avf->streams[0];
431 5 AVRational time_code_rate = st->avg_frame_rate;
432 5 int timecode_flags = 0;
433 AVTimecode twenty_four_hr;
434
435
2/4
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 5 times.
5 if (mcc->override_time_code_rate && (ret = av_parse_video_rate(&time_code_rate, mcc->override_time_code_rate)) < 0)
436 return ret;
437
438 5 ret = AVERROR(EINVAL);
439
440
1/2
✓ Branch 0 taken 15 times.
✗ Branch 1 not taken.
15 for (size_t i = 0; i < FF_ARRAY_ELEMS(valid_time_code_rates); i++) {
441
3/4
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 10 times.
✓ Branch 2 taken 5 times.
✗ Branch 3 not taken.
15 if (time_code_rate.num == valid_time_code_rates[i].num && time_code_rate.den == valid_time_code_rates[i].den) {
442 5 ret = 0;
443 5 break;
444 }
445 }
446
447
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5 times.
5 if (ret != 0) {
448 if (!mcc->override_time_code_rate && (time_code_rate.num <= 0 || time_code_rate.den <= 0)) {
449 av_log(avf, AV_LOG_FATAL, "time code rate not set, you need to use -override_time_code_rate to set it\n");
450 } else {
451 av_log(avf,
452 AV_LOG_FATAL,
453 "time code rate not supported by mcc: %d/%d\n",
454 time_code_rate.num,
455 time_code_rate.den);
456 }
457 return AVERROR(EINVAL);
458 }
459
460 5 avpriv_set_pts_info(st, 64, time_code_rate.den, time_code_rate.num);
461
462
2/4
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 5 times.
✗ Branch 3 not taken.
5 if (time_code_rate.den == 1001 && time_code_rate.num % 30000 == 0) {
463 5 timecode_flags |= AV_TIMECODE_FLAG_DROPFRAME;
464 }
465
466 5 ret = av_timecode_init(&mcc->timecode, time_code_rate, timecode_flags, 0, avf);
467
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5 times.
5 if (ret < 0)
468 return ret;
469
470 // get av_timecode to calculate how many frames are in 24hr
471 5 ret = av_timecode_init_from_components(&twenty_four_hr, time_code_rate, timecode_flags, 24, 0, 0, 0, avf);
472
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5 times.
5 if (ret < 0)
473 return ret;
474
475 5 mcc->twenty_four_hr = twenty_four_hr.start;
476
477
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 2 times.
5 if (st->codecpar->codec_id == AV_CODEC_ID_EIA_608) {
478 char args[64];
479 3 snprintf(args, sizeof(args), "cdp_frame_rate=%d/%d", time_code_rate.num, time_code_rate.den);
480 3 ret = ff_stream_add_bitstream_filter(st, "eia608_to_smpte436m", args);
481
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 if (ret < 0)
482 return ret;
483
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 } else if (st->codecpar->codec_id != AV_CODEC_ID_SMPTE_436M_ANC) {
484 av_log(avf,
485 AV_LOG_ERROR,
486 "mcc muxer supports only codec %s or codec %s\n",
487 avcodec_get_name(AV_CODEC_ID_SMPTE_436M_ANC),
488 avcodec_get_name(AV_CODEC_ID_EIA_608));
489 return AVERROR(EINVAL);
490 }
491
492 5 return 0;
493 }
494
495 static int mcc_query_codec(enum AVCodecID codec_id, int std_compliance)
496 {
497 (void)std_compliance;
498 if (codec_id == AV_CODEC_ID_EIA_608 || codec_id == AV_CODEC_ID_SMPTE_436M_ANC)
499 return 1;
500 return 0;
501 }
502
503 #define OFFSET(x) offsetof(MCCContext, x)
504 #define ENC AV_OPT_FLAG_ENCODING_PARAM
505 // clang-format off
506 static const AVOption options[] = {
507 { "override_time_code_rate", "override the `Time Code Rate` value in the output", OFFSET(override_time_code_rate), AV_OPT_TYPE_STRING, { .str = NULL }, 0, INT_MAX, ENC },
508 { "use_u_alias", "use the U alias for E1h 00h 00h 00h, disabled by default because some .mcc files disagree on whether it has 2 or 3 zero bytes", OFFSET(use_u_alias), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, ENC },
509 { "mcc_version", "the mcc file format version", OFFSET(mcc_version), AV_OPT_TYPE_UINT, { .i64 = MCC_VERSION_2 }, MCC_VERSION_MIN, MCC_VERSION_MAX, ENC },
510 { "creation_program", "the creation program", OFFSET(creation_program), AV_OPT_TYPE_STRING, { .str = NULL }, 0, INT_MAX, ENC },
511 { "creation_time", "the creation time", OFFSET(creation_time), AV_OPT_TYPE_STRING, { .str = "now" }, 0, INT_MAX, ENC },
512 { NULL },
513 };
514 // clang-format on
515
516 static const AVClass mcc_muxer_class = {
517 .class_name = "mcc muxer",
518 .item_name = av_default_item_name,
519 .option = options,
520 .version = LIBAVUTIL_VERSION_INT,
521 };
522
523 const FFOutputFormat ff_mcc_muxer = {
524 .p.name = "mcc",
525 .p.long_name = NULL_IF_CONFIG_SMALL("MacCaption"),
526 .p.extensions = "mcc",
527 .p.flags = AVFMT_GLOBALHEADER,
528 .p.video_codec = AV_CODEC_ID_NONE,
529 .p.audio_codec = AV_CODEC_ID_NONE,
530 .p.subtitle_codec = AV_CODEC_ID_EIA_608,
531 .p.priv_class = &mcc_muxer_class,
532 .priv_data_size = sizeof(MCCContext),
533 .init = mcc_init,
534 .query_codec = mcc_query_codec,
535 .write_header = mcc_write_header,
536 .write_packet = mcc_write_packet,
537 };
538