00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021 #include <stdint.h>
00022 #include <string.h>
00023
00024 #include "libavutil/avstring.h"
00025 #include "libavutil/dict.h"
00026 #include "libavutil/intreadwrite.h"
00027 #include "avformat.h"
00028 #include "avio.h"
00029 #include "id3v2.h"
00030
00031 static void id3v2_put_size(AVIOContext *pb, int size)
00032 {
00033 avio_w8(pb, size >> 21 & 0x7f);
00034 avio_w8(pb, size >> 14 & 0x7f);
00035 avio_w8(pb, size >> 7 & 0x7f);
00036 avio_w8(pb, size & 0x7f);
00037 }
00038
00039 static int string_is_ascii(const uint8_t *str)
00040 {
00041 while (*str && *str < 128) str++;
00042 return !*str;
00043 }
00044
00045 static void id3v2_encode_string(AVIOContext *pb, const uint8_t *str,
00046 enum ID3v2Encoding enc)
00047 {
00048 int (*put)(AVIOContext*, const char*);
00049
00050 if (enc == ID3v2_ENCODING_UTF16BOM) {
00051 avio_wl16(pb, 0xFEFF);
00052 put = avio_put_str16le;
00053 } else
00054 put = avio_put_str;
00055
00056 put(pb, str);
00057 }
00058
00064 static int id3v2_put_ttag(ID3v2EncContext *id3, AVIOContext *avioc, const char *str1, const char *str2,
00065 uint32_t tag, enum ID3v2Encoding enc)
00066 {
00067 int len;
00068 uint8_t *pb;
00069 AVIOContext *dyn_buf;
00070 if (avio_open_dyn_buf(&dyn_buf) < 0)
00071 return AVERROR(ENOMEM);
00072
00073
00074
00075 if (enc == ID3v2_ENCODING_UTF16BOM && string_is_ascii(str1) &&
00076 (!str2 || string_is_ascii(str2)))
00077 enc = ID3v2_ENCODING_ISO8859;
00078
00079 avio_w8(dyn_buf, enc);
00080 id3v2_encode_string(dyn_buf, str1, enc);
00081 if (str2)
00082 id3v2_encode_string(dyn_buf, str2, enc);
00083 len = avio_close_dyn_buf(dyn_buf, &pb);
00084
00085 avio_wb32(avioc, tag);
00086
00087 if (id3->version == 3)
00088 avio_wb32(avioc, len);
00089 else
00090 id3v2_put_size(avioc, len);
00091 avio_wb16(avioc, 0);
00092 avio_write(avioc, pb, len);
00093
00094 av_freep(&pb);
00095 return len + ID3v2_HEADER_SIZE;
00096 }
00097
00098 static int id3v2_check_write_tag(ID3v2EncContext *id3, AVIOContext *pb, AVDictionaryEntry *t,
00099 const char table[][4], enum ID3v2Encoding enc)
00100 {
00101 uint32_t tag;
00102 int i;
00103
00104 if (t->key[0] != 'T' || strlen(t->key) != 4)
00105 return -1;
00106 tag = AV_RB32(t->key);
00107 for (i = 0; *table[i]; i++)
00108 if (tag == AV_RB32(table[i]))
00109 return id3v2_put_ttag(id3, pb, t->value, NULL, tag, enc);
00110 return -1;
00111 }
00112
00113 static void id3v2_3_metadata_split_date(AVDictionary **pm)
00114 {
00115 AVDictionaryEntry *mtag = NULL;
00116 AVDictionary *dst = NULL;
00117 const char *key, *value;
00118 char year[5] = {0}, day_month[5] = {0};
00119 int i;
00120
00121 while ((mtag = av_dict_get(*pm, "", mtag, AV_DICT_IGNORE_SUFFIX))) {
00122 key = mtag->key;
00123 if (!av_strcasecmp(key, "date")) {
00124
00125 value = mtag->value;
00126 i = 0;
00127 while (value[i] >= '0' && value[i] <= '9') i++;
00128 if (value[i] == '\0' || value[i] == '-') {
00129 av_strlcpy(year, value, sizeof(year));
00130 av_dict_set(&dst, "TYER", year, 0);
00131
00132 if (value[i] == '-' &&
00133 value[i+1] >= '0' && value[i+1] <= '1' &&
00134 value[i+2] >= '0' && value[i+2] <= '9' &&
00135 value[i+3] == '-' &&
00136 value[i+4] >= '0' && value[i+4] <= '3' &&
00137 value[i+5] >= '0' && value[i+5] <= '9' &&
00138 (value[i+6] == '\0' || value[i+6] == ' ')) {
00139 snprintf(day_month, sizeof(day_month), "%.2s%.2s", value + i + 4, value + i + 1);
00140 av_dict_set(&dst, "TDAT", day_month, 0);
00141 }
00142 } else
00143 av_dict_set(&dst, key, value, 0);
00144 } else
00145 av_dict_set(&dst, key, mtag->value, 0);
00146 }
00147 av_dict_free(pm);
00148 *pm = dst;
00149 }
00150
00151 void ff_id3v2_start(ID3v2EncContext *id3, AVIOContext *pb, int id3v2_version,
00152 const char *magic)
00153 {
00154 id3->version = id3v2_version;
00155
00156 avio_wb32(pb, MKBETAG(magic[0], magic[1], magic[2], id3v2_version));
00157 avio_w8(pb, 0);
00158 avio_w8(pb, 0);
00159
00160
00161 id3->size_pos = avio_tell(pb);
00162 avio_wb32(pb, 0);
00163 }
00164
00165 int ff_id3v2_write_metadata(AVFormatContext *s, ID3v2EncContext *id3)
00166 {
00167 AVDictionaryEntry *t = NULL;
00168 int enc = id3->version == 3 ? ID3v2_ENCODING_UTF16BOM :
00169 ID3v2_ENCODING_UTF8;
00170
00171 ff_metadata_conv(&s->metadata, ff_id3v2_34_metadata_conv, NULL);
00172 if (id3->version == 3)
00173 id3v2_3_metadata_split_date(&s->metadata);
00174 else if (id3->version == 4)
00175 ff_metadata_conv(&s->metadata, ff_id3v2_4_metadata_conv, NULL);
00176
00177 while ((t = av_dict_get(s->metadata, "", t, AV_DICT_IGNORE_SUFFIX))) {
00178 int ret;
00179
00180 if ((ret = id3v2_check_write_tag(id3, s->pb, t, ff_id3v2_tags, enc)) > 0) {
00181 id3->len += ret;
00182 continue;
00183 }
00184 if ((ret = id3v2_check_write_tag(id3, s->pb, t, id3->version == 3 ?
00185 ff_id3v2_3_tags : ff_id3v2_4_tags, enc)) > 0) {
00186 id3->len += ret;
00187 continue;
00188 }
00189
00190
00191 if ((ret = id3v2_put_ttag(id3, s->pb, t->key, t->value, MKBETAG('T', 'X', 'X', 'X'), enc)) < 0)
00192 return ret;
00193 id3->len += ret;
00194 }
00195
00196 return 0;
00197 }
00198
00199 int ff_id3v2_write_apic(AVFormatContext *s, ID3v2EncContext *id3, AVPacket *pkt)
00200 {
00201 AVStream *st = s->streams[pkt->stream_index];
00202 AVDictionaryEntry *e;
00203
00204 AVIOContext *dyn_buf;
00205 uint8_t *buf;
00206 const CodecMime *mime = ff_id3v2_mime_tags;
00207 const char *mimetype = NULL, *desc = "";
00208 int enc = id3->version == 3 ? ID3v2_ENCODING_UTF16BOM :
00209 ID3v2_ENCODING_UTF8;
00210 int i, len, type = 0;
00211
00212
00213 while (mime->id != CODEC_ID_NONE) {
00214 if (mime->id == st->codec->codec_id) {
00215 mimetype = mime->str;
00216 break;
00217 }
00218 mime++;
00219 }
00220 if (!mimetype) {
00221 av_log(s, AV_LOG_ERROR, "No mimetype is known for stream %d, cannot "
00222 "write an attached picture.\n", st->index);
00223 return AVERROR(EINVAL);
00224 }
00225
00226
00227 e = av_dict_get(st->metadata, "comment", NULL, 0);
00228 for (i = 0; e && i < FF_ARRAY_ELEMS(ff_id3v2_picture_types); i++) {
00229 if (strstr(ff_id3v2_picture_types[i], e->value) == ff_id3v2_picture_types[i]) {
00230 type = i;
00231 break;
00232 }
00233 }
00234
00235
00236 if ((e = av_dict_get(st->metadata, "title", NULL, 0)))
00237 desc = e->value;
00238
00239
00240 if (avio_open_dyn_buf(&dyn_buf) < 0)
00241 return AVERROR(ENOMEM);
00242
00243 avio_w8(dyn_buf, enc);
00244 avio_put_str(dyn_buf, mimetype);
00245 avio_w8(dyn_buf, type);
00246 id3v2_encode_string(dyn_buf, desc, enc);
00247 avio_write(dyn_buf, pkt->data, pkt->size);
00248 len = avio_close_dyn_buf(dyn_buf, &buf);
00249
00250 avio_wb32(s->pb, MKBETAG('A', 'P', 'I', 'C'));
00251 if (id3->version == 3)
00252 avio_wb32(s->pb, len);
00253 else
00254 id3v2_put_size(s->pb, len);
00255 avio_wb16(s->pb, 0);
00256 avio_write(s->pb, buf, len);
00257 av_freep(&buf);
00258
00259 id3->len += len + ID3v2_HEADER_SIZE;
00260
00261 return 0;
00262 }
00263
00264 void ff_id3v2_finish(ID3v2EncContext *id3, AVIOContext *pb)
00265 {
00266 int64_t cur_pos = avio_tell(pb);
00267 avio_seek(pb, id3->size_pos, SEEK_SET);
00268 id3v2_put_size(pb, id3->len);
00269 avio_seek(pb, cur_pos, SEEK_SET);
00270 }
00271
00272 int ff_id3v2_write_simple(struct AVFormatContext *s, int id3v2_version,
00273 const char *magic)
00274 {
00275 ID3v2EncContext id3 = { 0 };
00276 int ret;
00277
00278 ff_id3v2_start(&id3, s->pb, id3v2_version, magic);
00279 if ((ret = ff_id3v2_write_metadata(s, &id3)) < 0)
00280 return ret;
00281 ff_id3v2_finish(&id3, s->pb);
00282
00283 return 0;
00284 }