00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022 #include <strings.h>
00023 #include "avformat.h"
00024 #include "id3v1.h"
00025 #include "id3v2.h"
00026 #include "rawenc.h"
00027 #include "libavutil/intreadwrite.h"
00028 #include "libavutil/opt.h"
00029
00030 static int id3v1_set_string(AVFormatContext *s, const char *key,
00031 uint8_t *buf, int buf_size)
00032 {
00033 AVMetadataTag *tag;
00034 if ((tag = av_metadata_get(s->metadata, key, NULL, 0)))
00035 strncpy(buf, tag->value, buf_size);
00036 return !!tag;
00037 }
00038
00039 static int id3v1_create_tag(AVFormatContext *s, uint8_t *buf)
00040 {
00041 AVMetadataTag *tag;
00042 int i, count = 0;
00043
00044 memset(buf, 0, ID3v1_TAG_SIZE);
00045 buf[0] = 'T';
00046 buf[1] = 'A';
00047 buf[2] = 'G';
00048 count += id3v1_set_string(s, "TIT2", buf + 3, 30);
00049 count += id3v1_set_string(s, "TPE1", buf + 33, 30);
00050 count += id3v1_set_string(s, "TALB", buf + 63, 30);
00051 count += id3v1_set_string(s, "TDRL", buf + 93, 4);
00052 count += id3v1_set_string(s, "comment", buf + 97, 30);
00053 if ((tag = av_metadata_get(s->metadata, "TRCK", NULL, 0))) {
00054 buf[125] = 0;
00055 buf[126] = atoi(tag->value);
00056 count++;
00057 }
00058 buf[127] = 0xFF;
00059 if ((tag = av_metadata_get(s->metadata, "TCON", NULL, 0))) {
00060 for(i = 0; i <= ID3v1_GENRE_MAX; i++) {
00061 if (!strcasecmp(tag->value, ff_id3v1_genre_str[i])) {
00062 buf[127] = i;
00063 count++;
00064 break;
00065 }
00066 }
00067 }
00068 return count;
00069 }
00070
00071
00072
00073 static void id3v2_put_size(AVFormatContext *s, int size)
00074 {
00075 avio_w8(s->pb, size >> 21 & 0x7f);
00076 avio_w8(s->pb, size >> 14 & 0x7f);
00077 avio_w8(s->pb, size >> 7 & 0x7f);
00078 avio_w8(s->pb, size & 0x7f);
00079 }
00080
00081 static int string_is_ascii(const uint8_t *str)
00082 {
00083 while (*str && *str < 128) str++;
00084 return !*str;
00085 }
00086
00092 static int id3v2_put_ttag(AVFormatContext *s, const char *str1, const char *str2,
00093 uint32_t tag, enum ID3v2Encoding enc)
00094 {
00095 int len;
00096 uint8_t *pb;
00097 int (*put)(AVIOContext*, const char*);
00098 AVIOContext *dyn_buf;
00099 if (url_open_dyn_buf(&dyn_buf) < 0)
00100 return AVERROR(ENOMEM);
00101
00102
00103
00104 if (enc == ID3v2_ENCODING_UTF16BOM && string_is_ascii(str1) &&
00105 (!str2 || string_is_ascii(str2)))
00106 enc = ID3v2_ENCODING_ISO8859;
00107
00108 avio_w8(dyn_buf, enc);
00109 if (enc == ID3v2_ENCODING_UTF16BOM) {
00110 avio_wl16(dyn_buf, 0xFEFF);
00111 put = avio_put_str16le;
00112 } else
00113 put = avio_put_str;
00114
00115 put(dyn_buf, str1);
00116 if (str2)
00117 put(dyn_buf, str2);
00118 len = url_close_dyn_buf(dyn_buf, &pb);
00119
00120 avio_wb32(s->pb, tag);
00121 id3v2_put_size(s, len);
00122 avio_wb16(s->pb, 0);
00123 avio_write(s->pb, pb, len);
00124
00125 av_freep(&pb);
00126 return len + ID3v2_HEADER_SIZE;
00127 }
00128
00129 static int mp3_write_trailer(struct AVFormatContext *s)
00130 {
00131 uint8_t buf[ID3v1_TAG_SIZE];
00132
00133
00134 if (id3v1_create_tag(s, buf) > 0) {
00135 avio_write(s->pb, buf, ID3v1_TAG_SIZE);
00136 avio_flush(s->pb);
00137 }
00138 return 0;
00139 }
00140
00141 #if CONFIG_MP2_MUXER
00142 AVOutputFormat ff_mp2_muxer = {
00143 "mp2",
00144 NULL_IF_CONFIG_SMALL("MPEG audio layer 2"),
00145 "audio/x-mpeg",
00146 "mp2,m2a",
00147 0,
00148 CODEC_ID_MP2,
00149 CODEC_ID_NONE,
00150 NULL,
00151 ff_raw_write_packet,
00152 mp3_write_trailer,
00153 };
00154 #endif
00155
00156 #if CONFIG_MP3_MUXER
00157 typedef struct MP3Context {
00158 const AVClass *class;
00159 int id3v2_version;
00160 } MP3Context;
00161
00162 static const AVOption options[] = {
00163 { "id3v2_version", "Select ID3v2 version to write. Currently 3 and 4 are supported.",
00164 offsetof(MP3Context, id3v2_version), FF_OPT_TYPE_INT, 4, 3, 4, AV_OPT_FLAG_ENCODING_PARAM},
00165 { NULL },
00166 };
00167
00168 static const AVClass mp3_muxer_class = {
00169 "MP3 muxer",
00170 av_default_item_name,
00171 options,
00172 LIBAVUTIL_VERSION_INT,
00173 };
00174
00175 static int id3v2_check_write_tag(AVFormatContext *s, AVMetadataTag *t, const char table[][4],
00176 enum ID3v2Encoding enc)
00177 {
00178 uint32_t tag;
00179 int i;
00180
00181 if (t->key[0] != 'T' || strlen(t->key) != 4)
00182 return -1;
00183 tag = AV_RB32(t->key);
00184 for (i = 0; *table[i]; i++)
00185 if (tag == AV_RB32(table[i]))
00186 return id3v2_put_ttag(s, t->value, NULL, tag, enc);
00187 return -1;
00188 }
00189
00194 static int mp3_write_header(struct AVFormatContext *s)
00195 {
00196 MP3Context *mp3 = s->priv_data;
00197 AVMetadataTag *t = NULL;
00198 int totlen = 0, enc = mp3->id3v2_version == 3 ? ID3v2_ENCODING_UTF16BOM :
00199 ID3v2_ENCODING_UTF8;
00200 int64_t size_pos, cur_pos;
00201
00202 avio_wb32(s->pb, MKBETAG('I', 'D', '3', mp3->id3v2_version));
00203 avio_w8(s->pb, 0);
00204 avio_w8(s->pb, 0);
00205
00206
00207 size_pos = avio_tell(s->pb);
00208 avio_wb32(s->pb, 0);
00209
00210 ff_metadata_conv(&s->metadata, ff_id3v2_34_metadata_conv, NULL);
00211 if (mp3->id3v2_version == 4)
00212 ff_metadata_conv(&s->metadata, ff_id3v2_4_metadata_conv, NULL);
00213
00214 while ((t = av_metadata_get(s->metadata, "", t, AV_METADATA_IGNORE_SUFFIX))) {
00215 int ret;
00216
00217 if ((ret = id3v2_check_write_tag(s, t, ff_id3v2_tags, enc)) > 0) {
00218 totlen += ret;
00219 continue;
00220 }
00221 if ((ret = id3v2_check_write_tag(s, t, mp3->id3v2_version == 3 ?
00222 ff_id3v2_3_tags : ff_id3v2_4_tags, enc)) > 0) {
00223 totlen += ret;
00224 continue;
00225 }
00226
00227
00228 if ((ret = id3v2_put_ttag(s, t->key, t->value, MKBETAG('T', 'X', 'X', 'X'), enc)) < 0)
00229 return ret;
00230 totlen += ret;
00231 }
00232
00233 cur_pos = avio_tell(s->pb);
00234 avio_seek(s->pb, size_pos, SEEK_SET);
00235 id3v2_put_size(s, totlen);
00236 avio_seek(s->pb, cur_pos, SEEK_SET);
00237
00238 return 0;
00239 }
00240
00241 AVOutputFormat ff_mp3_muxer = {
00242 "mp3",
00243 NULL_IF_CONFIG_SMALL("MPEG audio layer 3"),
00244 "audio/x-mpeg",
00245 "mp3",
00246 sizeof(MP3Context),
00247 CODEC_ID_MP3,
00248 CODEC_ID_NONE,
00249 mp3_write_header,
00250 ff_raw_write_packet,
00251 mp3_write_trailer,
00252 AVFMT_NOTIMESTAMPS,
00253 .priv_class = &mp3_muxer_class,
00254 };
00255 #endif