Libav 0.7.1
|
00001 /* 00002 * MP3 muxer 00003 * Copyright (c) 2003 Fabrice Bellard 00004 * 00005 * This file is part of Libav. 00006 * 00007 * Libav is free software; you can redistribute it and/or 00008 * modify it under the terms of the GNU Lesser General Public 00009 * License as published by the Free Software Foundation; either 00010 * version 2.1 of the License, or (at your option) any later version. 00011 * 00012 * Libav is distributed in the hope that it will be useful, 00013 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00015 * Lesser General Public License for more details. 00016 * 00017 * You should have received a copy of the GNU Lesser General Public 00018 * License along with Libav; if not, write to the Free Software 00019 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 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/avstring.h" 00028 #include "libavutil/intreadwrite.h" 00029 #include "libavutil/opt.h" 00030 #include "libavutil/dict.h" 00031 00032 static int id3v1_set_string(AVFormatContext *s, const char *key, 00033 uint8_t *buf, int buf_size) 00034 { 00035 AVDictionaryEntry *tag; 00036 if ((tag = av_dict_get(s->metadata, key, NULL, 0))) 00037 av_strlcpy(buf, tag->value, buf_size); 00038 return !!tag; 00039 } 00040 00041 static int id3v1_create_tag(AVFormatContext *s, uint8_t *buf) 00042 { 00043 AVDictionaryEntry *tag; 00044 int i, count = 0; 00045 00046 memset(buf, 0, ID3v1_TAG_SIZE); /* fail safe */ 00047 buf[0] = 'T'; 00048 buf[1] = 'A'; 00049 buf[2] = 'G'; 00050 count += id3v1_set_string(s, "TIT2", buf + 3, 30); //title 00051 count += id3v1_set_string(s, "TPE1", buf + 33, 30); //author|artist 00052 count += id3v1_set_string(s, "TALB", buf + 63, 30); //album 00053 count += id3v1_set_string(s, "TDRL", buf + 93, 4); //date 00054 count += id3v1_set_string(s, "comment", buf + 97, 30); 00055 if ((tag = av_dict_get(s->metadata, "TRCK", NULL, 0))) { //track 00056 buf[125] = 0; 00057 buf[126] = atoi(tag->value); 00058 count++; 00059 } 00060 buf[127] = 0xFF; /* default to unknown genre */ 00061 if ((tag = av_dict_get(s->metadata, "TCON", NULL, 0))) { //genre 00062 for(i = 0; i <= ID3v1_GENRE_MAX; i++) { 00063 if (!strcasecmp(tag->value, ff_id3v1_genre_str[i])) { 00064 buf[127] = i; 00065 count++; 00066 break; 00067 } 00068 } 00069 } 00070 return count; 00071 } 00072 00073 /* simple formats */ 00074 00075 static void id3v2_put_size(AVFormatContext *s, int size) 00076 { 00077 avio_w8(s->pb, size >> 21 & 0x7f); 00078 avio_w8(s->pb, size >> 14 & 0x7f); 00079 avio_w8(s->pb, size >> 7 & 0x7f); 00080 avio_w8(s->pb, size & 0x7f); 00081 } 00082 00083 static int string_is_ascii(const uint8_t *str) 00084 { 00085 while (*str && *str < 128) str++; 00086 return !*str; 00087 } 00088 00094 static int id3v2_put_ttag(AVFormatContext *s, const char *str1, const char *str2, 00095 uint32_t tag, enum ID3v2Encoding enc) 00096 { 00097 int len; 00098 uint8_t *pb; 00099 int (*put)(AVIOContext*, const char*); 00100 AVIOContext *dyn_buf; 00101 if (avio_open_dyn_buf(&dyn_buf) < 0) 00102 return AVERROR(ENOMEM); 00103 00104 /* check if the strings are ASCII-only and use UTF16 only if 00105 * they're not */ 00106 if (enc == ID3v2_ENCODING_UTF16BOM && string_is_ascii(str1) && 00107 (!str2 || string_is_ascii(str2))) 00108 enc = ID3v2_ENCODING_ISO8859; 00109 00110 avio_w8(dyn_buf, enc); 00111 if (enc == ID3v2_ENCODING_UTF16BOM) { 00112 avio_wl16(dyn_buf, 0xFEFF); /* BOM */ 00113 put = avio_put_str16le; 00114 } else 00115 put = avio_put_str; 00116 00117 put(dyn_buf, str1); 00118 if (str2) 00119 put(dyn_buf, str2); 00120 len = avio_close_dyn_buf(dyn_buf, &pb); 00121 00122 avio_wb32(s->pb, tag); 00123 id3v2_put_size(s, len); 00124 avio_wb16(s->pb, 0); 00125 avio_write(s->pb, pb, len); 00126 00127 av_freep(&pb); 00128 return len + ID3v2_HEADER_SIZE; 00129 } 00130 00131 static int mp3_write_trailer(struct AVFormatContext *s) 00132 { 00133 uint8_t buf[ID3v1_TAG_SIZE]; 00134 00135 /* write the id3v1 tag */ 00136 if (id3v1_create_tag(s, buf) > 0) { 00137 avio_write(s->pb, buf, ID3v1_TAG_SIZE); 00138 avio_flush(s->pb); 00139 } 00140 return 0; 00141 } 00142 00143 #if CONFIG_MP2_MUXER 00144 AVOutputFormat ff_mp2_muxer = { 00145 "mp2", 00146 NULL_IF_CONFIG_SMALL("MPEG audio layer 2"), 00147 "audio/x-mpeg", 00148 "mp2,m2a", 00149 0, 00150 CODEC_ID_MP2, 00151 CODEC_ID_NONE, 00152 NULL, 00153 ff_raw_write_packet, 00154 mp3_write_trailer, 00155 }; 00156 #endif 00157 00158 #if CONFIG_MP3_MUXER 00159 typedef struct MP3Context { 00160 const AVClass *class; 00161 int id3v2_version; 00162 } MP3Context; 00163 00164 static const AVOption options[] = { 00165 { "id3v2_version", "Select ID3v2 version to write. Currently 3 and 4 are supported.", 00166 offsetof(MP3Context, id3v2_version), FF_OPT_TYPE_INT, {.dbl = 4}, 3, 4, AV_OPT_FLAG_ENCODING_PARAM}, 00167 { NULL }, 00168 }; 00169 00170 static const AVClass mp3_muxer_class = { 00171 .class_name = "MP3 muxer", 00172 .item_name = av_default_item_name, 00173 .option = options, 00174 .version = LIBAVUTIL_VERSION_INT, 00175 }; 00176 00177 static int id3v2_check_write_tag(AVFormatContext *s, AVDictionaryEntry *t, const char table[][4], 00178 enum ID3v2Encoding enc) 00179 { 00180 uint32_t tag; 00181 int i; 00182 00183 if (t->key[0] != 'T' || strlen(t->key) != 4) 00184 return -1; 00185 tag = AV_RB32(t->key); 00186 for (i = 0; *table[i]; i++) 00187 if (tag == AV_RB32(table[i])) 00188 return id3v2_put_ttag(s, t->value, NULL, tag, enc); 00189 return -1; 00190 } 00191 00196 static int mp3_write_header(struct AVFormatContext *s) 00197 { 00198 MP3Context *mp3 = s->priv_data; 00199 AVDictionaryEntry *t = NULL; 00200 int totlen = 0, enc = mp3->id3v2_version == 3 ? ID3v2_ENCODING_UTF16BOM : 00201 ID3v2_ENCODING_UTF8; 00202 int64_t size_pos, cur_pos; 00203 00204 avio_wb32(s->pb, MKBETAG('I', 'D', '3', mp3->id3v2_version)); 00205 avio_w8(s->pb, 0); 00206 avio_w8(s->pb, 0); /* flags */ 00207 00208 /* reserve space for size */ 00209 size_pos = avio_tell(s->pb); 00210 avio_wb32(s->pb, 0); 00211 00212 ff_metadata_conv(&s->metadata, ff_id3v2_34_metadata_conv, NULL); 00213 if (mp3->id3v2_version == 4) 00214 ff_metadata_conv(&s->metadata, ff_id3v2_4_metadata_conv, NULL); 00215 00216 while ((t = av_dict_get(s->metadata, "", t, AV_DICT_IGNORE_SUFFIX))) { 00217 int ret; 00218 00219 if ((ret = id3v2_check_write_tag(s, t, ff_id3v2_tags, enc)) > 0) { 00220 totlen += ret; 00221 continue; 00222 } 00223 if ((ret = id3v2_check_write_tag(s, t, mp3->id3v2_version == 3 ? 00224 ff_id3v2_3_tags : ff_id3v2_4_tags, enc)) > 0) { 00225 totlen += ret; 00226 continue; 00227 } 00228 00229 /* unknown tag, write as TXXX frame */ 00230 if ((ret = id3v2_put_ttag(s, t->key, t->value, MKBETAG('T', 'X', 'X', 'X'), enc)) < 0) 00231 return ret; 00232 totlen += ret; 00233 } 00234 00235 cur_pos = avio_tell(s->pb); 00236 avio_seek(s->pb, size_pos, SEEK_SET); 00237 id3v2_put_size(s, totlen); 00238 avio_seek(s->pb, cur_pos, SEEK_SET); 00239 00240 return 0; 00241 } 00242 00243 AVOutputFormat ff_mp3_muxer = { 00244 "mp3", 00245 NULL_IF_CONFIG_SMALL("MPEG audio layer 3"), 00246 "audio/x-mpeg", 00247 "mp3", 00248 sizeof(MP3Context), 00249 CODEC_ID_MP3, 00250 CODEC_ID_NONE, 00251 mp3_write_header, 00252 ff_raw_write_packet, 00253 mp3_write_trailer, 00254 AVFMT_NOTIMESTAMPS, 00255 .priv_class = &mp3_muxer_class, 00256 }; 00257 #endif