FFmpeg coverage


Directory: ../../../ffmpeg/
File: src/libavformat/id3v2enc.c
Date: 2024-11-20 23:03:26
Exec Total Coverage
Lines: 218 237 92.0%
Functions: 15 15 100.0%
Branches: 111 148 75.0%

Line Branch Exec Source
1 /*
2 * ID3v2 header writer
3 *
4 * This file is part of FFmpeg.
5 *
6 * FFmpeg is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * FFmpeg is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with FFmpeg; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19 */
20
21 #include <stdint.h>
22 #include <string.h>
23
24 #include "libavutil/avstring.h"
25 #include "libavutil/dict.h"
26 #include "libavutil/intreadwrite.h"
27 #include "avformat.h"
28 #include "avio.h"
29 #include "avio_internal.h"
30 #include "id3v2.h"
31 #include "mux.h"
32
33 54 static void id3v2_put_size(AVIOContext *pb, int size)
34 {
35 54 avio_w8(pb, size >> 21 & 0x7f);
36 54 avio_w8(pb, size >> 14 & 0x7f);
37 54 avio_w8(pb, size >> 7 & 0x7f);
38 54 avio_w8(pb, size & 0x7f);
39 54 }
40
41 18 static int string_is_ascii(const uint8_t *str)
42 {
43
4/4
✓ Branch 0 taken 67 times.
✓ Branch 1 taken 14 times.
✓ Branch 2 taken 63 times.
✓ Branch 3 taken 4 times.
81 while (*str && *str < 128) str++;
44 18 return !*str;
45 }
46
47 70 static void id3v2_encode_string(AVIOContext *pb, const uint8_t *str,
48 enum ID3v2Encoding enc)
49 {
50 int (*put)(AVIOContext*, const char*);
51
52
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 66 times.
70 if (enc == ID3v2_ENCODING_UTF16BOM) {
53 4 avio_wl16(pb, 0xFEFF); /* BOM */
54 4 put = avio_put_str16le;
55 } else
56 66 put = avio_put_str;
57
58 70 put(pb, str);
59 70 }
60
61 /**
62 * Write a text frame with one (normal frames) or two (TXXX frames) strings
63 * according to encoding (only UTF-8 or UTF-16+BOM supported).
64 * @return number of bytes written or a negative error code.
65 */
66 50 static int id3v2_put_ttag(ID3v2EncContext *id3, AVIOContext *avioc, const char *str1, const char *str2,
67 uint32_t tag, enum ID3v2Encoding enc)
68 {
69 int len, ret;
70 uint8_t *pb;
71 AVIOContext *dyn_buf;
72
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 50 times.
50 if ((ret = avio_open_dyn_buf(&dyn_buf)) < 0)
73 return ret;
74
75 /* check if the strings are ASCII-only and use UTF16 only if
76 * they're not */
77
6/6
✓ Branch 0 taken 13 times.
✓ Branch 1 taken 37 times.
✓ Branch 3 taken 10 times.
✓ Branch 4 taken 3 times.
✓ Branch 5 taken 4 times.
✓ Branch 6 taken 6 times.
50 if (enc == ID3v2_ENCODING_UTF16BOM && string_is_ascii(str1) &&
78
1/2
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
4 (!str2 || string_is_ascii(str2)))
79 10 enc = ID3v2_ENCODING_ISO8859;
80
81 50 avio_w8(dyn_buf, enc);
82 50 id3v2_encode_string(dyn_buf, str1, enc);
83
2/2
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 38 times.
50 if (str2)
84 12 id3v2_encode_string(dyn_buf, str2, enc);
85 50 len = avio_get_dyn_buf(dyn_buf, &pb);
86
87 50 avio_wb32(avioc, tag);
88 /* ID3v2.3 frame size is not sync-safe */
89
2/2
✓ Branch 0 taken 13 times.
✓ Branch 1 taken 37 times.
50 if (id3->version == 3)
90 13 avio_wb32(avioc, len);
91 else
92 37 id3v2_put_size(avioc, len);
93 50 avio_wb16(avioc, 0);
94 50 avio_write(avioc, pb, len);
95
96 50 ffio_free_dyn_buf(&dyn_buf);
97 50 return len + ID3v2_HEADER_SIZE;
98 }
99
100 /**
101 * Write a priv frame with owner and data. 'key' is the owner prepended with
102 * ID3v2_PRIV_METADATA_PREFIX. 'data' is provided as a string. Any \xXX
103 * (where 'X' is a valid hex digit) will be unescaped to the byte value.
104 */
105 15 static int id3v2_put_priv(ID3v2EncContext *id3, AVIOContext *avioc, const char *key, const char *data)
106 {
107 int len, ret;
108 uint8_t *pb;
109 AVIOContext *dyn_buf;
110
111
2/2
✓ Branch 1 taken 12 times.
✓ Branch 2 taken 3 times.
15 if (!av_strstart(key, ID3v2_PRIV_METADATA_PREFIX, &key)) {
112 12 return 0;
113 }
114
115
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
3 if ((ret = avio_open_dyn_buf(&dyn_buf)) < 0)
116 return ret;
117
118 // owner + null byte.
119 3 avio_write(dyn_buf, key, strlen(key) + 1);
120
121
2/2
✓ Branch 0 taken 15 times.
✓ Branch 1 taken 3 times.
18 while (*data) {
122
2/2
✓ Branch 1 taken 4 times.
✓ Branch 2 taken 11 times.
15 if (av_strstart(data, "\\x", &data)) {
123
4/8
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 4 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 4 times.
✗ Branch 7 not taken.
4 if (data[0] && data[1] && av_isxdigit(data[0]) && av_isxdigit(data[1])) {
124 4 char digits[] = {data[0], data[1], 0};
125 4 avio_w8(dyn_buf, strtol(digits, NULL, 16));
126 4 data += 2;
127 } else {
128 ffio_free_dyn_buf(&dyn_buf);
129 av_log(avioc, AV_LOG_ERROR, "Invalid escape '\\x%.2s' in metadata tag '"
130 ID3v2_PRIV_METADATA_PREFIX "%s'.\n", data, key);
131 return AVERROR(EINVAL);
132 }
133 } else {
134 11 avio_write(dyn_buf, data++, 1);
135 }
136 }
137
138 3 len = avio_get_dyn_buf(dyn_buf, &pb);
139
140 3 avio_wb32(avioc, MKBETAG('P', 'R', 'I', 'V'));
141
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 2 times.
3 if (id3->version == 3)
142 1 avio_wb32(avioc, len);
143 else
144 2 id3v2_put_size(avioc, len);
145 3 avio_wb16(avioc, 0);
146 3 avio_write(avioc, pb, len);
147
148 3 ffio_free_dyn_buf(&dyn_buf);
149
150 3 return len + ID3v2_HEADER_SIZE;
151 }
152
153 75 static int id3v2_check_write_tag(ID3v2EncContext *id3, AVIOContext *pb, const AVDictionaryEntry *t,
154 const char table[][4], enum ID3v2Encoding enc)
155 {
156 uint32_t tag;
157 int i;
158
159
3/4
✓ Branch 0 taken 47 times.
✓ Branch 1 taken 28 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 47 times.
75 if (t->key[0] != 'T' || strlen(t->key) != 4)
160 28 return -1;
161 47 tag = AV_RB32(t->key);
162
2/2
✓ Branch 0 taken 682 times.
✓ Branch 1 taken 9 times.
691 for (i = 0; *table[i]; i++)
163
2/2
✓ Branch 0 taken 38 times.
✓ Branch 1 taken 644 times.
682 if (tag == AV_RB32(table[i]))
164 38 return id3v2_put_ttag(id3, pb, t->value, NULL, tag, enc);
165 9 return -1;
166 }
167
168 3 static void id3v2_3_metadata_split_date(AVDictionary **pm)
169 {
170 3 const AVDictionaryEntry *mtag = NULL;
171 3 AVDictionary *dst = NULL;
172 const char *key, *value;
173 3 char year[5] = {0}, day_month[5] = {0};
174 int i;
175
176
2/2
✓ Branch 1 taken 13 times.
✓ Branch 2 taken 3 times.
16 while ((mtag = av_dict_iterate(*pm, mtag))) {
177 13 key = mtag->key;
178
2/2
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 11 times.
13 if (!av_strcasecmp(key, "date")) {
179 /* split date tag using "YYYY-MM-DD" format into year and month/day segments */
180 2 value = mtag->value;
181 2 i = 0;
182
3/4
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
10 while (value[i] >= '0' && value[i] <= '9') i++;
183
3/4
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
2 if (value[i] == '\0' || value[i] == '-') {
184 2 av_strlcpy(year, value, sizeof(year));
185 2 av_dict_set(&dst, "TYER", year, 0);
186
187
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
2 if (value[i] == '-' &&
188
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
1 value[i+1] >= '0' && value[i+1] <= '1' &&
189
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
1 value[i+2] >= '0' && value[i+2] <= '9' &&
190
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 value[i+3] == '-' &&
191
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
1 value[i+4] >= '0' && value[i+4] <= '3' &&
192
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
1 value[i+5] >= '0' && value[i+5] <= '9' &&
193
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
1 (value[i+6] == '\0' || value[i+6] == ' ')) {
194 1 snprintf(day_month, sizeof(day_month), "%.2s%.2s", value + i + 4, value + i + 1);
195 1 av_dict_set(&dst, "TDAT", day_month, 0);
196 }
197 } else
198 av_dict_set(&dst, key, value, 0);
199 } else
200 11 av_dict_set(&dst, key, mtag->value, 0);
201 }
202 3 av_dict_free(pm);
203 3 *pm = dst;
204 3 }
205
206 8 void ff_id3v2_start(ID3v2EncContext *id3, AVIOContext *pb, int id3v2_version,
207 const char *magic)
208 {
209 8 id3->version = id3v2_version;
210
211 8 avio_wb32(pb, MKBETAG(magic[0], magic[1], magic[2], id3v2_version));
212 8 avio_w8(pb, 0);
213 8 avio_w8(pb, 0); /* flags */
214
215 /* reserve space for size */
216 8 id3->size_pos = avio_tell(pb);
217 8 avio_wb32(pb, 0);
218 8 }
219
220 13 static int write_metadata(AVIOContext *pb, AVDictionary **metadata,
221 ID3v2EncContext *id3, int enc)
222 {
223 13 const AVDictionaryEntry *t = NULL;
224 int ret;
225
226 13 ff_metadata_conv(metadata, ff_id3v2_34_metadata_conv, NULL);
227
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 10 times.
13 if (id3->version == 3)
228 3 id3v2_3_metadata_split_date(metadata);
229
1/2
✓ Branch 0 taken 10 times.
✗ Branch 1 not taken.
10 else if (id3->version == 4)
230 10 ff_metadata_conv(metadata, ff_id3v2_4_metadata_conv, NULL);
231
232
2/2
✓ Branch 1 taken 53 times.
✓ Branch 2 taken 13 times.
66 while ((t = av_dict_iterate(*metadata, t))) {
233
2/2
✓ Branch 1 taken 31 times.
✓ Branch 2 taken 22 times.
53 if ((ret = id3v2_check_write_tag(id3, pb, t, ff_id3v2_tags, enc)) > 0) {
234 31 id3->len += ret;
235 31 continue;
236 }
237
4/4
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 14 times.
✓ Branch 3 taken 7 times.
✓ Branch 4 taken 15 times.
22 if ((ret = id3v2_check_write_tag(id3, pb, t, id3->version == 3 ?
238 ff_id3v2_3_tags : ff_id3v2_4_tags, enc)) > 0) {
239 7 id3->len += ret;
240 7 continue;
241 }
242
243
2/2
✓ Branch 1 taken 3 times.
✓ Branch 2 taken 12 times.
15 if ((ret = id3v2_put_priv(id3, pb, t->key, t->value)) > 0) {
244 3 id3->len += ret;
245 3 continue;
246
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 12 times.
12 } else if (ret < 0) {
247 return ret;
248 }
249
250 /* unknown tag, write as TXXX frame */
251
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 12 times.
12 if ((ret = id3v2_put_ttag(id3, pb, t->key, t->value, MKBETAG('T', 'X', 'X', 'X'), enc)) < 0)
252 return ret;
253 12 id3->len += ret;
254 }
255
256 13 return 0;
257 }
258
259 8 static int write_ctoc(AVFormatContext *s, ID3v2EncContext *id3, int enc)
260 {
261 uint8_t *dyn_buf;
262 AVIOContext *dyn_bc;
263 char name[123];
264 int len, ret;
265
266
2/2
✓ Branch 0 taken 7 times.
✓ Branch 1 taken 1 times.
8 if (s->nb_chapters == 0)
267 7 return 0;
268
269
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
1 if ((ret = avio_open_dyn_buf(&dyn_bc)) < 0)
270 return ret;
271
272 1 avio_put_str(dyn_bc, "toc");
273 1 avio_w8(dyn_bc, 0x03);
274 1 avio_w8(dyn_bc, s->nb_chapters);
275
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 1 times.
6 for (int i = 0; i < s->nb_chapters; i++) {
276 5 snprintf(name, 122, "ch%d", i);
277 5 avio_put_str(dyn_bc, name);
278 }
279 1 len = avio_get_dyn_buf(dyn_bc, &dyn_buf);
280 1 id3->len += len + ID3v2_HEADER_SIZE;
281
282 1 avio_wb32(s->pb, MKBETAG('C', 'T', 'O', 'C'));
283 1 avio_wb32(s->pb, len);
284 1 avio_wb16(s->pb, 0);
285 1 avio_write(s->pb, dyn_buf, len);
286
287 1 ffio_free_dyn_buf(&dyn_bc);
288
289 1 return ret;
290 }
291
292 5 static int write_chapter(AVFormatContext *s, ID3v2EncContext *id3, int id, int enc)
293 {
294 const AVRational time_base = {1, 1000};
295 5 AVChapter *ch = s->chapters[id];
296 uint8_t *dyn_buf;
297 AVIOContext *dyn_bc;
298 char name[123];
299 int len, start, end, ret;
300
301
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 5 times.
5 if ((ret = avio_open_dyn_buf(&dyn_bc)) < 0)
302 return ret;
303
304 5 start = av_rescale_q(ch->start, ch->time_base, time_base);
305 5 end = av_rescale_q(ch->end, ch->time_base, time_base);
306
307 5 snprintf(name, 122, "ch%d", id);
308 5 id3->len += avio_put_str(dyn_bc, name);
309 5 avio_wb32(dyn_bc, start);
310 5 avio_wb32(dyn_bc, end);
311 5 avio_wb32(dyn_bc, 0xFFFFFFFFu);
312 5 avio_wb32(dyn_bc, 0xFFFFFFFFu);
313
314
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 5 times.
5 if ((ret = write_metadata(dyn_bc, &ch->metadata, id3, enc)) < 0)
315 goto fail;
316
317 5 len = avio_get_dyn_buf(dyn_bc, &dyn_buf);
318 5 id3->len += 16 + ID3v2_HEADER_SIZE;
319
320 5 avio_wb32(s->pb, MKBETAG('C', 'H', 'A', 'P'));
321 5 avio_wb32(s->pb, len);
322 5 avio_wb16(s->pb, 0);
323 5 avio_write(s->pb, dyn_buf, len);
324
325 5 fail:
326 5 ffio_free_dyn_buf(&dyn_bc);
327
328 5 return ret;
329 }
330
331 8 int ff_id3v2_write_metadata(AVFormatContext *s, ID3v2EncContext *id3)
332 {
333
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 5 times.
8 int enc = id3->version == 3 ? ID3v2_ENCODING_UTF16BOM :
334 ID3v2_ENCODING_UTF8;
335 int i, ret;
336
337 8 ff_standardize_creation_time(s);
338
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 8 times.
8 if ((ret = write_metadata(s->pb, &s->metadata, id3, enc)) < 0)
339 return ret;
340
341
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 8 times.
8 if ((ret = write_ctoc(s, id3, enc)) < 0)
342 return ret;
343
344
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 8 times.
13 for (i = 0; i < s->nb_chapters; i++) {
345
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 5 times.
5 if ((ret = write_chapter(s, id3, i, enc)) < 0)
346 return ret;
347 }
348
349 8 return 0;
350 }
351
352 8 int ff_id3v2_write_apic(AVFormatContext *s, ID3v2EncContext *id3, AVPacket *pkt)
353 {
354 8 AVStream *st = s->streams[pkt->stream_index];
355 AVDictionaryEntry *e;
356
357 AVIOContext *dyn_buf;
358 uint8_t *buf;
359 8 const CodecMime *mime = ff_id3v2_mime_tags;
360 8 const char *mimetype = NULL, *desc = "";
361
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 7 times.
8 int enc = id3->version == 3 ? ID3v2_ENCODING_UTF16BOM :
362 ID3v2_ENCODING_UTF8;
363 8 int i, len, type = 0, ret;
364
365 /* get the mimetype*/
366
1/2
✓ Branch 0 taken 28 times.
✗ Branch 1 not taken.
28 while (mime->id != AV_CODEC_ID_NONE) {
367
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 20 times.
28 if (mime->id == st->codecpar->codec_id) {
368 8 mimetype = mime->str;
369 8 break;
370 }
371 20 mime++;
372 }
373
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
8 if (!mimetype) {
374 av_log(s, AV_LOG_ERROR, "No mimetype is known for stream %d, cannot "
375 "write an attached picture.\n", st->index);
376 return AVERROR(EINVAL);
377 }
378
379 /* get the picture type */
380 8 e = av_dict_get(st->metadata, "comment", NULL, 0);
381
3/4
✓ Branch 0 taken 55 times.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 55 times.
✗ Branch 3 not taken.
56 for (i = 0; e && i < FF_ARRAY_ELEMS(ff_id3v2_picture_types); i++) {
382
2/2
✓ Branch 1 taken 7 times.
✓ Branch 2 taken 48 times.
55 if (!av_strcasecmp(e->value, ff_id3v2_picture_types[i])) {
383 7 type = i;
384 7 break;
385 }
386 }
387
388 /* get the description */
389
2/2
✓ Branch 1 taken 5 times.
✓ Branch 2 taken 3 times.
8 if ((e = av_dict_get(st->metadata, "title", NULL, 0)))
390 5 desc = e->value;
391
392 /* use UTF16 only for non-ASCII strings */
393
3/4
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 7 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 1 times.
8 if (enc == ID3v2_ENCODING_UTF16BOM && string_is_ascii(desc))
394 enc = ID3v2_ENCODING_ISO8859;
395
396 /* start writing */
397
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 8 times.
8 if ((ret = avio_open_dyn_buf(&dyn_buf)) < 0)
398 return ret;
399
400 8 avio_w8(dyn_buf, enc);
401 8 avio_put_str(dyn_buf, mimetype);
402 8 avio_w8(dyn_buf, type);
403 8 id3v2_encode_string(dyn_buf, desc, enc);
404 8 avio_write(dyn_buf, pkt->data, pkt->size);
405 8 len = avio_get_dyn_buf(dyn_buf, &buf);
406
407 8 avio_wb32(s->pb, MKBETAG('A', 'P', 'I', 'C'));
408
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 7 times.
8 if (id3->version == 3)
409 1 avio_wb32(s->pb, len);
410 else
411 7 id3v2_put_size(s->pb, len);
412 8 avio_wb16(s->pb, 0);
413 8 avio_write(s->pb, buf, len);
414 8 ffio_free_dyn_buf(&dyn_buf);
415
416 8 id3->len += len + ID3v2_HEADER_SIZE;
417
418 8 return 0;
419 }
420
421 8 void ff_id3v2_finish(ID3v2EncContext *id3, AVIOContext *pb,
422 int padding_bytes)
423 {
424 int64_t cur_pos;
425
426
2/2
✓ Branch 0 taken 7 times.
✓ Branch 1 taken 1 times.
8 if (padding_bytes < 0)
427 7 padding_bytes = 10;
428
429 /* The ID3v2.3 specification states that 28 bits are used to represent the
430 * size of the whole tag. Therefore the current size of the tag needs to be
431 * subtracted from the upper limit of 2^28-1 to clip the value correctly. */
432 /* The minimum of 10 is an arbitrary amount of padding at the end of the tag
433 * to fix cover art display with some software such as iTunes, Traktor,
434 * Serato, Torq. */
435 8 padding_bytes = av_clip(padding_bytes, 10, 268435455 - id3->len);
436 8 ffio_fill(pb, 0, padding_bytes);
437 8 id3->len += padding_bytes;
438
439 8 cur_pos = avio_tell(pb);
440 8 avio_seek(pb, id3->size_pos, SEEK_SET);
441 8 id3v2_put_size(pb, id3->len);
442 8 avio_seek(pb, cur_pos, SEEK_SET);
443 8 }
444
445 2 int ff_id3v2_write_simple(struct AVFormatContext *s, int id3v2_version,
446 const char *magic)
447 {
448 2 ID3v2EncContext id3 = { 0 };
449 int ret;
450
451 2 ff_id3v2_start(&id3, s->pb, id3v2_version, magic);
452
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
2 if ((ret = ff_id3v2_write_metadata(s, &id3)) < 0)
453 return ret;
454 2 ff_id3v2_finish(&id3, s->pb, s->metadata_header_padding);
455
456 2 return 0;
457 }
458