FFmpeg coverage


Directory: ../../../ffmpeg/
File: src/libavformat/id3v2enc.c
Date: 2026-04-24 15:23:18
Exec Total Coverage
Lines: 219 237 92.4%
Functions: 15 15 100.0%
Branches: 112 148 75.7%

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 55 static void id3v2_put_size(AVIOContext *pb, int size)
34 {
35 55 avio_w8(pb, size >> 21 & 0x7f);
36 55 avio_w8(pb, size >> 14 & 0x7f);
37 55 avio_w8(pb, size >> 7 & 0x7f);
38 55 avio_w8(pb, size & 0x7f);
39 55 }
40
41 20 static int string_is_ascii(const uint8_t *str)
42 {
43
4/4
✓ Branch 0 taken 80 times.
✓ Branch 1 taken 16 times.
✓ Branch 2 taken 76 times.
✓ Branch 3 taken 4 times.
96 while (*str && *str < 128) str++;
44 20 return !*str;
45 }
46
47 72 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 68 times.
72 if (enc == ID3v2_ENCODING_UTF16BOM) {
53 4 avio_wl16(pb, 0xFEFF); /* BOM */
54 4 put = avio_put_str16le;
55 } else
56 68 put = avio_put_str;
57
58 72 put(pb, str);
59 72 }
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 51 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 51 times.
51 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 14 times.
✓ Branch 1 taken 37 times.
✓ Branch 3 taken 11 times.
✓ Branch 4 taken 3 times.
✓ Branch 5 taken 4 times.
✓ Branch 6 taken 7 times.
51 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 11 enc = ID3v2_ENCODING_ISO8859;
80
81 51 avio_w8(dyn_buf, enc);
82 51 id3v2_encode_string(dyn_buf, str1, enc);
83
2/2
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 39 times.
51 if (str2)
84 12 id3v2_encode_string(dyn_buf, str2, enc);
85 51 len = avio_get_dyn_buf(dyn_buf, &pb);
86
87 51 avio_wb32(avioc, tag);
88 /* ID3v2.3 frame size is not sync-safe */
89
2/2
✓ Branch 0 taken 14 times.
✓ Branch 1 taken 37 times.
51 if (id3->version == 3)
90 14 avio_wb32(avioc, len);
91 else
92 37 id3v2_put_size(avioc, len);
93 51 avio_wb16(avioc, 0);
94 51 avio_write(avioc, pb, len);
95
96 51 ffio_free_dyn_buf(&dyn_buf);
97 51 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 76 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 48 times.
✓ Branch 1 taken 28 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 48 times.
76 if (t->key[0] != 'T' || strlen(t->key) != 4)
160 28 return -1;
161 48 tag = AV_RB32(t->key);
162
2/2
✓ Branch 0 taken 714 times.
✓ Branch 1 taken 9 times.
723 for (i = 0; *table[i]; i++)
163
2/2
✓ Branch 0 taken 39 times.
✓ Branch 1 taken 675 times.
714 if (tag == AV_RB32(table[i]))
164 39 return id3v2_put_ttag(id3, pb, t->value, NULL, tag, enc);
165 9 return -1;
166 }
167
168 4 static void id3v2_3_metadata_split_date(AVDictionary **pm)
169 {
170 4 const AVDictionaryEntry *mtag = NULL;
171 4 AVDictionary *dst = NULL;
172 const char *key, *value;
173 4 char year[5] = {0}, day_month[5] = {0};
174 int i;
175
176
2/2
✓ Branch 1 taken 14 times.
✓ Branch 2 taken 4 times.
18 while ((mtag = av_dict_iterate(*pm, mtag))) {
177 14 key = mtag->key;
178
2/2
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 12 times.
14 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 12 av_dict_set(&dst, key, mtag->value, 0);
201 }
202 4 av_dict_free(pm);
203 4 *pm = dst;
204 4 }
205
206 9 void ff_id3v2_start(ID3v2EncContext *id3, AVIOContext *pb, int id3v2_version,
207 const char *magic)
208 {
209 9 id3->version = id3v2_version;
210
211 9 avio_wb32(pb, MKBETAG(magic[0], magic[1], magic[2], id3v2_version));
212 9 avio_w8(pb, 0);
213 9 avio_w8(pb, 0); /* flags */
214
215 /* reserve space for size */
216 9 id3->size_pos = avio_tell(pb);
217 9 avio_wb32(pb, 0);
218 9 }
219
220 14 static int write_metadata(AVIOContext *pb, AVDictionary **metadata,
221 ID3v2EncContext *id3, int enc)
222 {
223 14 const AVDictionaryEntry *t = NULL;
224 int ret;
225
226 14 ff_metadata_conv(metadata, ff_id3v2_34_metadata_conv, NULL);
227
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 10 times.
14 if (id3->version == 3)
228 4 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 54 times.
✓ Branch 2 taken 14 times.
68 while ((t = av_dict_iterate(*metadata, t))) {
233
2/2
✓ Branch 1 taken 32 times.
✓ Branch 2 taken 22 times.
54 if ((ret = id3v2_check_write_tag(id3, pb, t, ff_id3v2_tags, enc)) > 0) {
234 32 id3->len += ret;
235 32 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 14 return 0;
257 }
258
259 9 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 8 times.
✓ Branch 1 taken 1 times.
9 if (s->nb_chapters == 0)
267 8 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 9 int ff_id3v2_write_metadata(AVFormatContext *s, ID3v2EncContext *id3)
332 {
333
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 5 times.
9 int enc = id3->version == 3 ? ID3v2_ENCODING_UTF16BOM :
334 ID3v2_ENCODING_UTF8;
335 int i, ret;
336
337 9 ff_standardize_creation_time(s);
338
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 9 times.
9 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 9 times.
9 if ((ret = write_ctoc(s, id3, enc)) < 0)
342 return ret;
343
344
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 9 times.
14 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 9 return 0;
350 }
351
352 9 int ff_id3v2_write_apic(AVFormatContext *s, ID3v2EncContext *id3, AVPacket *pkt)
353 {
354 9 AVStream *st = s->streams[pkt->stream_index];
355 AVDictionaryEntry *e;
356
357 AVIOContext *dyn_buf;
358 uint8_t *buf;
359 9 const CodecMime *mime = ff_id3v2_mime_tags;
360 9 const char *mimetype = NULL, *desc = "";
361
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 7 times.
9 int enc = id3->version == 3 ? ID3v2_ENCODING_UTF16BOM :
362 ID3v2_ENCODING_UTF8;
363 9 int i, len, type = 0, ret;
364
365 /* get the mimetype*/
366
1/2
✓ Branch 0 taken 36 times.
✗ Branch 1 not taken.
36 while (mime->id != AV_CODEC_ID_NONE) {
367
2/2
✓ Branch 0 taken 9 times.
✓ Branch 1 taken 27 times.
36 if (mime->id == st->codecpar->codec_id) {
368 9 mimetype = mime->str;
369 9 break;
370 }
371 27 mime++;
372 }
373
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 9 times.
9 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 9 e = av_dict_get(st->metadata, "comment", NULL, 0);
381
3/4
✓ Branch 0 taken 55 times.
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 55 times.
✗ Branch 3 not taken.
57 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 4 times.
9 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
4/4
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 7 times.
✓ Branch 3 taken 1 times.
✓ Branch 4 taken 1 times.
9 if (enc == ID3v2_ENCODING_UTF16BOM && string_is_ascii(desc))
394 1 enc = ID3v2_ENCODING_ISO8859;
395
396 /* start writing */
397
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 9 times.
9 if ((ret = avio_open_dyn_buf(&dyn_buf)) < 0)
398 return ret;
399
400 9 avio_w8(dyn_buf, enc);
401 9 avio_put_str(dyn_buf, mimetype);
402 9 avio_w8(dyn_buf, type);
403 9 id3v2_encode_string(dyn_buf, desc, enc);
404 9 avio_write(dyn_buf, pkt->data, pkt->size);
405 9 len = avio_get_dyn_buf(dyn_buf, &buf);
406
407 9 avio_wb32(s->pb, MKBETAG('A', 'P', 'I', 'C'));
408
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 7 times.
9 if (id3->version == 3)
409 2 avio_wb32(s->pb, len);
410 else
411 7 id3v2_put_size(s->pb, len);
412 9 avio_wb16(s->pb, 0);
413 9 avio_write(s->pb, buf, len);
414 9 ffio_free_dyn_buf(&dyn_buf);
415
416 9 id3->len += len + ID3v2_HEADER_SIZE;
417
418 9 return 0;
419 }
420
421 9 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 8 times.
✓ Branch 1 taken 1 times.
9 if (padding_bytes < 0)
427 8 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 9 padding_bytes = av_clip(padding_bytes, 10, 268435455 - id3->len);
436 9 ffio_fill(pb, 0, padding_bytes);
437 9 id3->len += padding_bytes;
438
439 9 cur_pos = avio_tell(pb);
440 9 avio_seek(pb, id3->size_pos, SEEK_SET);
441 9 id3v2_put_size(pb, id3->len);
442 9 avio_seek(pb, cur_pos, SEEK_SET);
443 9 }
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