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
id3v2_put_size(AVIOContext * pb,int size)33 static void id3v2_put_size(AVIOContext *pb, int size)
34 {
35 avio_w8(pb, size >> 21 & 0x7f);
36 avio_w8(pb, size >> 14 & 0x7f);
37 avio_w8(pb, size >> 7 & 0x7f);
38 avio_w8(pb, size & 0x7f);
39 }
40
string_is_ascii(const uint8_t * str)41 static int string_is_ascii(const uint8_t *str)
42 {
43 while (*str && *str < 128) str++;
44 return !*str;
45 }
46
id3v2_encode_string(AVIOContext * pb,const uint8_t * str,enum ID3v2Encoding enc)47 static void id3v2_encode_string(AVIOContext *pb, const uint8_t *str,
48 enum ID3v2Encoding enc)
49 {
50 int (*put)(AVIOContext*, const char*);
51
52 if (enc == ID3v2_ENCODING_UTF16BOM) {
53 avio_wl16(pb, 0xFEFF); /* BOM */
54 put = avio_put_str16le;
55 } else
56 put = avio_put_str;
57
58 put(pb, str);
59 }
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 */
id3v2_put_ttag(ID3v2EncContext * id3,AVIOContext * avioc,const char * str1,const char * str2,uint32_t tag,enum ID3v2Encoding enc)66 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 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 if (enc == ID3v2_ENCODING_UTF16BOM && string_is_ascii(str1) &&
78 (!str2 || string_is_ascii(str2)))
79 enc = ID3v2_ENCODING_ISO8859;
80
81 avio_w8(dyn_buf, enc);
82 id3v2_encode_string(dyn_buf, str1, enc);
83 if (str2)
84 id3v2_encode_string(dyn_buf, str2, enc);
85 len = avio_get_dyn_buf(dyn_buf, &pb);
86
87 avio_wb32(avioc, tag);
88 /* ID3v2.3 frame size is not sync-safe */
89 if (id3->version == 3)
90 avio_wb32(avioc, len);
91 else
92 id3v2_put_size(avioc, len);
93 avio_wb16(avioc, 0);
94 avio_write(avioc, pb, len);
95
96 ffio_free_dyn_buf(&dyn_buf);
97 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 */
id3v2_put_priv(ID3v2EncContext * id3,AVIOContext * avioc,const char * key,const char * data)105 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 if (!av_strstart(key, ID3v2_PRIV_METADATA_PREFIX, &key)) {
112 return 0;
113 }
114
115 if ((ret = avio_open_dyn_buf(&dyn_buf)) < 0)
116 return ret;
117
118 // owner + null byte.
119 avio_write(dyn_buf, key, strlen(key) + 1);
120
121 while (*data) {
122 if (av_strstart(data, "\\x", &data)) {
123 if (data[0] && data[1] && av_isxdigit(data[0]) && av_isxdigit(data[1])) {
124 char digits[] = {data[0], data[1], 0};
125 avio_w8(dyn_buf, strtol(digits, NULL, 16));
126 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 avio_write(dyn_buf, data++, 1);
135 }
136 }
137
138 len = avio_get_dyn_buf(dyn_buf, &pb);
139
140 avio_wb32(avioc, MKBETAG('P', 'R', 'I', 'V'));
141 if (id3->version == 3)
142 avio_wb32(avioc, len);
143 else
144 id3v2_put_size(avioc, len);
145 avio_wb16(avioc, 0);
146 avio_write(avioc, pb, len);
147
148 ffio_free_dyn_buf(&dyn_buf);
149
150 return len + ID3v2_HEADER_SIZE;
151 }
152
id3v2_check_write_tag(ID3v2EncContext * id3,AVIOContext * pb,AVDictionaryEntry * t,const char table[][4],enum ID3v2Encoding enc)153 static int id3v2_check_write_tag(ID3v2EncContext *id3, AVIOContext *pb, AVDictionaryEntry *t,
154 const char table[][4], enum ID3v2Encoding enc)
155 {
156 uint32_t tag;
157 int i;
158
159 if (t->key[0] != 'T' || strlen(t->key) != 4)
160 return -1;
161 tag = AV_RB32(t->key);
162 for (i = 0; *table[i]; i++)
163 if (tag == AV_RB32(table[i]))
164 return id3v2_put_ttag(id3, pb, t->value, NULL, tag, enc);
165 return -1;
166 }
167
id3v2_3_metadata_split_date(AVDictionary ** pm)168 static void id3v2_3_metadata_split_date(AVDictionary **pm)
169 {
170 AVDictionaryEntry *mtag = NULL;
171 AVDictionary *dst = NULL;
172 const char *key, *value;
173 char year[5] = {0}, day_month[5] = {0};
174 int i;
175
176 while ((mtag = av_dict_get(*pm, "", mtag, AV_DICT_IGNORE_SUFFIX))) {
177 key = mtag->key;
178 if (!av_strcasecmp(key, "date")) {
179 /* split date tag using "YYYY-MM-DD" format into year and month/day segments */
180 value = mtag->value;
181 i = 0;
182 while (value[i] >= '0' && value[i] <= '9') i++;
183 if (value[i] == '\0' || value[i] == '-') {
184 av_strlcpy(year, value, sizeof(year));
185 av_dict_set(&dst, "TYER", year, 0);
186
187 if (value[i] == '-' &&
188 value[i+1] >= '0' && value[i+1] <= '1' &&
189 value[i+2] >= '0' && value[i+2] <= '9' &&
190 value[i+3] == '-' &&
191 value[i+4] >= '0' && value[i+4] <= '3' &&
192 value[i+5] >= '0' && value[i+5] <= '9' &&
193 (value[i+6] == '\0' || value[i+6] == ' ')) {
194 snprintf(day_month, sizeof(day_month), "%.2s%.2s", value + i + 4, value + i + 1);
195 av_dict_set(&dst, "TDAT", day_month, 0);
196 }
197 } else
198 av_dict_set(&dst, key, value, 0);
199 } else
200 av_dict_set(&dst, key, mtag->value, 0);
201 }
202 av_dict_free(pm);
203 *pm = dst;
204 }
205
ff_id3v2_start(ID3v2EncContext * id3,AVIOContext * pb,int id3v2_version,const char * magic)206 void ff_id3v2_start(ID3v2EncContext *id3, AVIOContext *pb, int id3v2_version,
207 const char *magic)
208 {
209 id3->version = id3v2_version;
210
211 avio_wb32(pb, MKBETAG(magic[0], magic[1], magic[2], id3v2_version));
212 avio_w8(pb, 0);
213 avio_w8(pb, 0); /* flags */
214
215 /* reserve space for size */
216 id3->size_pos = avio_tell(pb);
217 avio_wb32(pb, 0);
218 }
219
write_metadata(AVIOContext * pb,AVDictionary ** metadata,ID3v2EncContext * id3,int enc)220 static int write_metadata(AVIOContext *pb, AVDictionary **metadata,
221 ID3v2EncContext *id3, int enc)
222 {
223 AVDictionaryEntry *t = NULL;
224 int ret;
225
226 ff_metadata_conv(metadata, ff_id3v2_34_metadata_conv, NULL);
227 if (id3->version == 3)
228 id3v2_3_metadata_split_date(metadata);
229 else if (id3->version == 4)
230 ff_metadata_conv(metadata, ff_id3v2_4_metadata_conv, NULL);
231
232 while ((t = av_dict_get(*metadata, "", t, AV_DICT_IGNORE_SUFFIX))) {
233 if ((ret = id3v2_check_write_tag(id3, pb, t, ff_id3v2_tags, enc)) > 0) {
234 id3->len += ret;
235 continue;
236 }
237 if ((ret = id3v2_check_write_tag(id3, pb, t, id3->version == 3 ?
238 ff_id3v2_3_tags : ff_id3v2_4_tags, enc)) > 0) {
239 id3->len += ret;
240 continue;
241 }
242
243 if ((ret = id3v2_put_priv(id3, pb, t->key, t->value)) > 0) {
244 id3->len += ret;
245 continue;
246 } else if (ret < 0) {
247 return ret;
248 }
249
250 /* unknown tag, write as TXXX frame */
251 if ((ret = id3v2_put_ttag(id3, pb, t->key, t->value, MKBETAG('T', 'X', 'X', 'X'), enc)) < 0)
252 return ret;
253 id3->len += ret;
254 }
255
256 return 0;
257 }
258
write_ctoc(AVFormatContext * s,ID3v2EncContext * id3,int enc)259 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 if (s->nb_chapters == 0)
267 return 0;
268
269 if ((ret = avio_open_dyn_buf(&dyn_bc)) < 0)
270 return ret;
271
272 avio_put_str(dyn_bc, "toc");
273 avio_w8(dyn_bc, 0x03);
274 avio_w8(dyn_bc, s->nb_chapters);
275 for (int i = 0; i < s->nb_chapters; i++) {
276 snprintf(name, 122, "ch%d", i);
277 avio_put_str(dyn_bc, name);
278 }
279 len = avio_get_dyn_buf(dyn_bc, &dyn_buf);
280 id3->len += len + ID3v2_HEADER_SIZE;
281
282 avio_wb32(s->pb, MKBETAG('C', 'T', 'O', 'C'));
283 avio_wb32(s->pb, len);
284 avio_wb16(s->pb, 0);
285 avio_write(s->pb, dyn_buf, len);
286
287 ffio_free_dyn_buf(&dyn_bc);
288
289 return ret;
290 }
291
write_chapter(AVFormatContext * s,ID3v2EncContext * id3,int id,int enc)292 static int write_chapter(AVFormatContext *s, ID3v2EncContext *id3, int id, int enc)
293 {
294 const AVRational time_base = {1, 1000};
295 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 if ((ret = avio_open_dyn_buf(&dyn_bc)) < 0)
302 return ret;
303
304 start = av_rescale_q(ch->start, ch->time_base, time_base);
305 end = av_rescale_q(ch->end, ch->time_base, time_base);
306
307 snprintf(name, 122, "ch%d", id);
308 id3->len += avio_put_str(dyn_bc, name);
309 avio_wb32(dyn_bc, start);
310 avio_wb32(dyn_bc, end);
311 avio_wb32(dyn_bc, 0xFFFFFFFFu);
312 avio_wb32(dyn_bc, 0xFFFFFFFFu);
313
314 if ((ret = write_metadata(dyn_bc, &ch->metadata, id3, enc)) < 0)
315 goto fail;
316
317 len = avio_get_dyn_buf(dyn_bc, &dyn_buf);
318 id3->len += 16 + ID3v2_HEADER_SIZE;
319
320 avio_wb32(s->pb, MKBETAG('C', 'H', 'A', 'P'));
321 avio_wb32(s->pb, len);
322 avio_wb16(s->pb, 0);
323 avio_write(s->pb, dyn_buf, len);
324
325 fail:
326 ffio_free_dyn_buf(&dyn_bc);
327
328 return ret;
329 }
330
ff_id3v2_write_metadata(AVFormatContext * s,ID3v2EncContext * id3)331 int ff_id3v2_write_metadata(AVFormatContext *s, ID3v2EncContext *id3)
332 {
333 int enc = id3->version == 3 ? ID3v2_ENCODING_UTF16BOM :
334 ID3v2_ENCODING_UTF8;
335 int i, ret;
336
337 ff_standardize_creation_time(s);
338 if ((ret = write_metadata(s->pb, &s->metadata, id3, enc)) < 0)
339 return ret;
340
341 if ((ret = write_ctoc(s, id3, enc)) < 0)
342 return ret;
343
344 for (i = 0; i < s->nb_chapters; i++) {
345 if ((ret = write_chapter(s, id3, i, enc)) < 0)
346 return ret;
347 }
348
349 return 0;
350 }
351
ff_id3v2_write_apic(AVFormatContext * s,ID3v2EncContext * id3,AVPacket * pkt)352 int ff_id3v2_write_apic(AVFormatContext *s, ID3v2EncContext *id3, AVPacket *pkt)
353 {
354 AVStream *st = s->streams[pkt->stream_index];
355 AVDictionaryEntry *e;
356
357 AVIOContext *dyn_buf;
358 uint8_t *buf;
359 const CodecMime *mime = ff_id3v2_mime_tags;
360 const char *mimetype = NULL, *desc = "";
361 int enc = id3->version == 3 ? ID3v2_ENCODING_UTF16BOM :
362 ID3v2_ENCODING_UTF8;
363 int i, len, type = 0, ret;
364
365 /* get the mimetype*/
366 while (mime->id != AV_CODEC_ID_NONE) {
367 if (mime->id == st->codecpar->codec_id) {
368 mimetype = mime->str;
369 break;
370 }
371 mime++;
372 }
373 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 e = av_dict_get(st->metadata, "comment", NULL, 0);
381 for (i = 0; e && i < FF_ARRAY_ELEMS(ff_id3v2_picture_types); i++) {
382 if (!av_strcasecmp(e->value, ff_id3v2_picture_types[i])) {
383 type = i;
384 break;
385 }
386 }
387
388 /* get the description */
389 if ((e = av_dict_get(st->metadata, "title", NULL, 0)))
390 desc = e->value;
391
392 /* use UTF16 only for non-ASCII strings */
393 if (enc == ID3v2_ENCODING_UTF16BOM && string_is_ascii(desc))
394 enc = ID3v2_ENCODING_ISO8859;
395
396 /* start writing */
397 if ((ret = avio_open_dyn_buf(&dyn_buf)) < 0)
398 return ret;
399
400 avio_w8(dyn_buf, enc);
401 avio_put_str(dyn_buf, mimetype);
402 avio_w8(dyn_buf, type);
403 id3v2_encode_string(dyn_buf, desc, enc);
404 avio_write(dyn_buf, pkt->data, pkt->size);
405 len = avio_get_dyn_buf(dyn_buf, &buf);
406
407 avio_wb32(s->pb, MKBETAG('A', 'P', 'I', 'C'));
408 if (id3->version == 3)
409 avio_wb32(s->pb, len);
410 else
411 id3v2_put_size(s->pb, len);
412 avio_wb16(s->pb, 0);
413 avio_write(s->pb, buf, len);
414 ffio_free_dyn_buf(&dyn_buf);
415
416 id3->len += len + ID3v2_HEADER_SIZE;
417
418 return 0;
419 }
420
ff_id3v2_finish(ID3v2EncContext * id3,AVIOContext * pb,int padding_bytes)421 void ff_id3v2_finish(ID3v2EncContext *id3, AVIOContext *pb,
422 int padding_bytes)
423 {
424 int64_t cur_pos;
425
426 if (padding_bytes < 0)
427 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 padding_bytes = av_clip(padding_bytes, 10, 268435455 - id3->len);
436 ffio_fill(pb, 0, padding_bytes);
437 id3->len += padding_bytes;
438
439 cur_pos = avio_tell(pb);
440 avio_seek(pb, id3->size_pos, SEEK_SET);
441 id3v2_put_size(pb, id3->len);
442 avio_seek(pb, cur_pos, SEEK_SET);
443 }
444
ff_id3v2_write_simple(struct AVFormatContext * s,int id3v2_version,const char * magic)445 int ff_id3v2_write_simple(struct AVFormatContext *s, int id3v2_version,
446 const char *magic)
447 {
448 ID3v2EncContext id3 = { 0 };
449 int ret;
450
451 ff_id3v2_start(&id3, s->pb, id3v2_version, magic);
452 if ((ret = ff_id3v2_write_metadata(s, &id3)) < 0)
453 return ret;
454 ff_id3v2_finish(&id3, s->pb, s->metadata_header_padding);
455
456 return 0;
457 }
458