• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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