annotate ffmpeg/libavformat/id3v2enc.c @ 13:844d341cf643 tip

Back up before ISMIR
author Yading Song <yading.song@eecs.qmul.ac.uk>
date Thu, 31 Oct 2013 13:17:06 +0000
parents f445c3017523
children
rev   line source
yading@11 1 /*
yading@11 2 * ID3v2 header writer
yading@11 3 *
yading@11 4 * This file is part of FFmpeg.
yading@11 5 *
yading@11 6 * FFmpeg is free software; you can redistribute it and/or
yading@11 7 * modify it under the terms of the GNU Lesser General Public
yading@11 8 * License as published by the Free Software Foundation; either
yading@11 9 * version 2.1 of the License, or (at your option) any later version.
yading@11 10 *
yading@11 11 * FFmpeg is distributed in the hope that it will be useful,
yading@11 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
yading@11 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
yading@11 14 * Lesser General Public License for more details.
yading@11 15 *
yading@11 16 * You should have received a copy of the GNU Lesser General Public
yading@11 17 * License along with FFmpeg; if not, write to the Free Software
yading@11 18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
yading@11 19 */
yading@11 20
yading@11 21 #include <stdint.h>
yading@11 22 #include <string.h>
yading@11 23
yading@11 24 #include "libavutil/avstring.h"
yading@11 25 #include "libavutil/dict.h"
yading@11 26 #include "libavutil/intreadwrite.h"
yading@11 27 #include "avformat.h"
yading@11 28 #include "avio.h"
yading@11 29 #include "id3v2.h"
yading@11 30
yading@11 31 static void id3v2_put_size(AVIOContext *pb, int size)
yading@11 32 {
yading@11 33 avio_w8(pb, size >> 21 & 0x7f);
yading@11 34 avio_w8(pb, size >> 14 & 0x7f);
yading@11 35 avio_w8(pb, size >> 7 & 0x7f);
yading@11 36 avio_w8(pb, size & 0x7f);
yading@11 37 }
yading@11 38
yading@11 39 static int string_is_ascii(const uint8_t *str)
yading@11 40 {
yading@11 41 while (*str && *str < 128) str++;
yading@11 42 return !*str;
yading@11 43 }
yading@11 44
yading@11 45 static void id3v2_encode_string(AVIOContext *pb, const uint8_t *str,
yading@11 46 enum ID3v2Encoding enc)
yading@11 47 {
yading@11 48 int (*put)(AVIOContext*, const char*);
yading@11 49
yading@11 50 if (enc == ID3v2_ENCODING_UTF16BOM) {
yading@11 51 avio_wl16(pb, 0xFEFF); /* BOM */
yading@11 52 put = avio_put_str16le;
yading@11 53 } else
yading@11 54 put = avio_put_str;
yading@11 55
yading@11 56 put(pb, str);
yading@11 57 }
yading@11 58
yading@11 59 /**
yading@11 60 * Write a text frame with one (normal frames) or two (TXXX frames) strings
yading@11 61 * according to encoding (only UTF-8 or UTF-16+BOM supported).
yading@11 62 * @return number of bytes written or a negative error code.
yading@11 63 */
yading@11 64 static int id3v2_put_ttag(ID3v2EncContext *id3, AVIOContext *avioc, const char *str1, const char *str2,
yading@11 65 uint32_t tag, enum ID3v2Encoding enc)
yading@11 66 {
yading@11 67 int len;
yading@11 68 uint8_t *pb;
yading@11 69 AVIOContext *dyn_buf;
yading@11 70 if (avio_open_dyn_buf(&dyn_buf) < 0)
yading@11 71 return AVERROR(ENOMEM);
yading@11 72
yading@11 73 /* check if the strings are ASCII-only and use UTF16 only if
yading@11 74 * they're not */
yading@11 75 if (enc == ID3v2_ENCODING_UTF16BOM && string_is_ascii(str1) &&
yading@11 76 (!str2 || string_is_ascii(str2)))
yading@11 77 enc = ID3v2_ENCODING_ISO8859;
yading@11 78
yading@11 79 avio_w8(dyn_buf, enc);
yading@11 80 id3v2_encode_string(dyn_buf, str1, enc);
yading@11 81 if (str2)
yading@11 82 id3v2_encode_string(dyn_buf, str2, enc);
yading@11 83 len = avio_close_dyn_buf(dyn_buf, &pb);
yading@11 84
yading@11 85 avio_wb32(avioc, tag);
yading@11 86 /* ID3v2.3 frame size is not synchsafe */
yading@11 87 if (id3->version == 3)
yading@11 88 avio_wb32(avioc, len);
yading@11 89 else
yading@11 90 id3v2_put_size(avioc, len);
yading@11 91 avio_wb16(avioc, 0);
yading@11 92 avio_write(avioc, pb, len);
yading@11 93
yading@11 94 av_freep(&pb);
yading@11 95 return len + ID3v2_HEADER_SIZE;
yading@11 96 }
yading@11 97
yading@11 98 static int id3v2_check_write_tag(ID3v2EncContext *id3, AVIOContext *pb, AVDictionaryEntry *t,
yading@11 99 const char table[][4], enum ID3v2Encoding enc)
yading@11 100 {
yading@11 101 uint32_t tag;
yading@11 102 int i;
yading@11 103
yading@11 104 if (t->key[0] != 'T' || strlen(t->key) != 4)
yading@11 105 return -1;
yading@11 106 tag = AV_RB32(t->key);
yading@11 107 for (i = 0; *table[i]; i++)
yading@11 108 if (tag == AV_RB32(table[i]))
yading@11 109 return id3v2_put_ttag(id3, pb, t->value, NULL, tag, enc);
yading@11 110 return -1;
yading@11 111 }
yading@11 112
yading@11 113 static void id3v2_3_metadata_split_date(AVDictionary **pm)
yading@11 114 {
yading@11 115 AVDictionaryEntry *mtag = NULL;
yading@11 116 AVDictionary *dst = NULL;
yading@11 117 const char *key, *value;
yading@11 118 char year[5] = {0}, day_month[5] = {0};
yading@11 119 int i;
yading@11 120
yading@11 121 while ((mtag = av_dict_get(*pm, "", mtag, AV_DICT_IGNORE_SUFFIX))) {
yading@11 122 key = mtag->key;
yading@11 123 if (!av_strcasecmp(key, "date")) {
yading@11 124 /* split date tag using "YYYY-MM-DD" format into year and month/day segments */
yading@11 125 value = mtag->value;
yading@11 126 i = 0;
yading@11 127 while (value[i] >= '0' && value[i] <= '9') i++;
yading@11 128 if (value[i] == '\0' || value[i] == '-') {
yading@11 129 av_strlcpy(year, value, sizeof(year));
yading@11 130 av_dict_set(&dst, "TYER", year, 0);
yading@11 131
yading@11 132 if (value[i] == '-' &&
yading@11 133 value[i+1] >= '0' && value[i+1] <= '1' &&
yading@11 134 value[i+2] >= '0' && value[i+2] <= '9' &&
yading@11 135 value[i+3] == '-' &&
yading@11 136 value[i+4] >= '0' && value[i+4] <= '3' &&
yading@11 137 value[i+5] >= '0' && value[i+5] <= '9' &&
yading@11 138 (value[i+6] == '\0' || value[i+6] == ' ')) {
yading@11 139 snprintf(day_month, sizeof(day_month), "%.2s%.2s", value + i + 4, value + i + 1);
yading@11 140 av_dict_set(&dst, "TDAT", day_month, 0);
yading@11 141 }
yading@11 142 } else
yading@11 143 av_dict_set(&dst, key, value, 0);
yading@11 144 } else
yading@11 145 av_dict_set(&dst, key, mtag->value, 0);
yading@11 146 }
yading@11 147 av_dict_free(pm);
yading@11 148 *pm = dst;
yading@11 149 }
yading@11 150
yading@11 151 void ff_id3v2_start(ID3v2EncContext *id3, AVIOContext *pb, int id3v2_version,
yading@11 152 const char *magic)
yading@11 153 {
yading@11 154 id3->version = id3v2_version;
yading@11 155
yading@11 156 avio_wb32(pb, MKBETAG(magic[0], magic[1], magic[2], id3v2_version));
yading@11 157 avio_w8(pb, 0);
yading@11 158 avio_w8(pb, 0); /* flags */
yading@11 159
yading@11 160 /* reserve space for size */
yading@11 161 id3->size_pos = avio_tell(pb);
yading@11 162 avio_wb32(pb, 0);
yading@11 163 }
yading@11 164
yading@11 165 int ff_id3v2_write_metadata(AVFormatContext *s, ID3v2EncContext *id3)
yading@11 166 {
yading@11 167 AVDictionaryEntry *t = NULL;
yading@11 168 int enc = id3->version == 3 ? ID3v2_ENCODING_UTF16BOM :
yading@11 169 ID3v2_ENCODING_UTF8;
yading@11 170
yading@11 171 ff_metadata_conv(&s->metadata, ff_id3v2_34_metadata_conv, NULL);
yading@11 172 if (id3->version == 3)
yading@11 173 id3v2_3_metadata_split_date(&s->metadata);
yading@11 174 else if (id3->version == 4)
yading@11 175 ff_metadata_conv(&s->metadata, ff_id3v2_4_metadata_conv, NULL);
yading@11 176
yading@11 177 while ((t = av_dict_get(s->metadata, "", t, AV_DICT_IGNORE_SUFFIX))) {
yading@11 178 int ret;
yading@11 179
yading@11 180 if ((ret = id3v2_check_write_tag(id3, s->pb, t, ff_id3v2_tags, enc)) > 0) {
yading@11 181 id3->len += ret;
yading@11 182 continue;
yading@11 183 }
yading@11 184 if ((ret = id3v2_check_write_tag(id3, s->pb, t, id3->version == 3 ?
yading@11 185 ff_id3v2_3_tags : ff_id3v2_4_tags, enc)) > 0) {
yading@11 186 id3->len += ret;
yading@11 187 continue;
yading@11 188 }
yading@11 189
yading@11 190 /* unknown tag, write as TXXX frame */
yading@11 191 if ((ret = id3v2_put_ttag(id3, s->pb, t->key, t->value, MKBETAG('T', 'X', 'X', 'X'), enc)) < 0)
yading@11 192 return ret;
yading@11 193 id3->len += ret;
yading@11 194 }
yading@11 195
yading@11 196 return 0;
yading@11 197 }
yading@11 198
yading@11 199 int ff_id3v2_write_apic(AVFormatContext *s, ID3v2EncContext *id3, AVPacket *pkt)
yading@11 200 {
yading@11 201 AVStream *st = s->streams[pkt->stream_index];
yading@11 202 AVDictionaryEntry *e;
yading@11 203
yading@11 204 AVIOContext *dyn_buf;
yading@11 205 uint8_t *buf;
yading@11 206 const CodecMime *mime = ff_id3v2_mime_tags;
yading@11 207 const char *mimetype = NULL, *desc = "";
yading@11 208 int enc = id3->version == 3 ? ID3v2_ENCODING_UTF16BOM :
yading@11 209 ID3v2_ENCODING_UTF8;
yading@11 210 int i, len, type = 0;
yading@11 211
yading@11 212 /* get the mimetype*/
yading@11 213 while (mime->id != AV_CODEC_ID_NONE) {
yading@11 214 if (mime->id == st->codec->codec_id) {
yading@11 215 mimetype = mime->str;
yading@11 216 break;
yading@11 217 }
yading@11 218 mime++;
yading@11 219 }
yading@11 220 if (!mimetype) {
yading@11 221 av_log(s, AV_LOG_ERROR, "No mimetype is known for stream %d, cannot "
yading@11 222 "write an attached picture.\n", st->index);
yading@11 223 return AVERROR(EINVAL);
yading@11 224 }
yading@11 225
yading@11 226 /* get the picture type */
yading@11 227 e = av_dict_get(st->metadata, "comment", NULL, 0);
yading@11 228 for (i = 0; e && i < FF_ARRAY_ELEMS(ff_id3v2_picture_types); i++) {
yading@11 229 if (strstr(ff_id3v2_picture_types[i], e->value) == ff_id3v2_picture_types[i]) {
yading@11 230 type = i;
yading@11 231 break;
yading@11 232 }
yading@11 233 }
yading@11 234
yading@11 235 /* get the description */
yading@11 236 if ((e = av_dict_get(st->metadata, "title", NULL, 0)))
yading@11 237 desc = e->value;
yading@11 238
yading@11 239 /* start writing */
yading@11 240 if (avio_open_dyn_buf(&dyn_buf) < 0)
yading@11 241 return AVERROR(ENOMEM);
yading@11 242
yading@11 243 avio_w8(dyn_buf, enc);
yading@11 244 avio_put_str(dyn_buf, mimetype);
yading@11 245 avio_w8(dyn_buf, type);
yading@11 246 id3v2_encode_string(dyn_buf, desc, enc);
yading@11 247 avio_write(dyn_buf, pkt->data, pkt->size);
yading@11 248 len = avio_close_dyn_buf(dyn_buf, &buf);
yading@11 249
yading@11 250 avio_wb32(s->pb, MKBETAG('A', 'P', 'I', 'C'));
yading@11 251 if (id3->version == 3)
yading@11 252 avio_wb32(s->pb, len);
yading@11 253 else
yading@11 254 id3v2_put_size(s->pb, len);
yading@11 255 avio_wb16(s->pb, 0);
yading@11 256 avio_write(s->pb, buf, len);
yading@11 257 av_freep(&buf);
yading@11 258
yading@11 259 id3->len += len + ID3v2_HEADER_SIZE;
yading@11 260
yading@11 261 return 0;
yading@11 262 }
yading@11 263
yading@11 264 void ff_id3v2_finish(ID3v2EncContext *id3, AVIOContext *pb)
yading@11 265 {
yading@11 266 int64_t cur_pos = avio_tell(pb);
yading@11 267 avio_seek(pb, id3->size_pos, SEEK_SET);
yading@11 268 id3v2_put_size(pb, id3->len);
yading@11 269 avio_seek(pb, cur_pos, SEEK_SET);
yading@11 270 }
yading@11 271
yading@11 272 int ff_id3v2_write_simple(struct AVFormatContext *s, int id3v2_version,
yading@11 273 const char *magic)
yading@11 274 {
yading@11 275 ID3v2EncContext id3 = { 0 };
yading@11 276 int ret;
yading@11 277
yading@11 278 ff_id3v2_start(&id3, s->pb, id3v2_version, magic);
yading@11 279 if ((ret = ff_id3v2_write_metadata(s, &id3)) < 0)
yading@11 280 return ret;
yading@11 281 ff_id3v2_finish(&id3, s->pb);
yading@11 282
yading@11 283 return 0;
yading@11 284 }