ffmpeg / libavformat / mp3enc.c @ 6cc7f139
History | View | Annotate | Download (7.1 KB)
1 | 115329f1 | Diego Biurrun | /*
|
---|---|---|---|
2 | d08928bb | Anton Khirnov | * MP3 muxer
|
3 | 406792e7 | Diego Biurrun | * Copyright (c) 2003 Fabrice Bellard
|
4 | 6a58e151 | Fabrice Bellard | *
|
5 | 2912e87a | Mans Rullgard | * This file is part of Libav.
|
6 | b78e7197 | Diego Biurrun | *
|
7 | 2912e87a | Mans Rullgard | * Libav is free software; you can redistribute it and/or
|
8 | 6a58e151 | Fabrice Bellard | * modify it under the terms of the GNU Lesser General Public
|
9 | * License as published by the Free Software Foundation; either
|
||
10 | b78e7197 | Diego Biurrun | * version 2.1 of the License, or (at your option) any later version.
|
11 | 6a58e151 | Fabrice Bellard | *
|
12 | 2912e87a | Mans Rullgard | * Libav is distributed in the hope that it will be useful,
|
13 | 6a58e151 | Fabrice Bellard | * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||
15 | * Lesser General Public License for more details.
|
||
16 | *
|
||
17 | * You should have received a copy of the GNU Lesser General Public
|
||
18 | 2912e87a | Mans Rullgard | * License along with Libav; if not, write to the Free Software
|
19 | 5509bffa | Diego Biurrun | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
20 | 6a58e151 | Fabrice Bellard | */
|
21 | 245976da | Diego Biurrun | |
22 | ea452b54 | Aurelien Jacobs | #include <strings.h> |
23 | 6a58e151 | Fabrice Bellard | #include "avformat.h" |
24 | 75411182 | Patrick Dehne | #include "id3v1.h" |
25 | d08928bb | Anton Khirnov | #include "id3v2.h" |
26 | fed5676f | Anton Khirnov | #include "rawenc.h" |
27 | d08928bb | Anton Khirnov | #include "libavutil/intreadwrite.h" |
28 | 22272f61 | Anton Khirnov | #include "libavutil/opt.h" |
29 | 52b53f86 | Andreas Öman | |
30 | b182eeb0 | Aurelien Jacobs | static int id3v1_set_string(AVFormatContext *s, const char *key, |
31 | uint8_t *buf, int buf_size)
|
||
32 | 58607ca7 | Diego Biurrun | { |
33 | b182eeb0 | Aurelien Jacobs | AVMetadataTag *tag; |
34 | if ((tag = av_metadata_get(s->metadata, key, NULL, 0))) |
||
35 | strncpy(buf, tag->value, buf_size); |
||
36 | return !!tag;
|
||
37 | } |
||
38 | |||
39 | static int id3v1_create_tag(AVFormatContext *s, uint8_t *buf) |
||
40 | { |
||
41 | AVMetadataTag *tag; |
||
42 | int i, count = 0; |
||
43 | 58607ca7 | Diego Biurrun | |
44 | memset(buf, 0, ID3v1_TAG_SIZE); /* fail safe */ |
||
45 | buf[0] = 'T'; |
||
46 | buf[1] = 'A'; |
||
47 | buf[2] = 'G'; |
||
48 | 90d9d97d | James Darnley | count += id3v1_set_string(s, "TIT2", buf + 3, 30); //title |
49 | count += id3v1_set_string(s, "TPE1", buf + 33, 30); //author|artist |
||
50 | count += id3v1_set_string(s, "TALB", buf + 63, 30); //album |
||
51 | count += id3v1_set_string(s, "TDRL", buf + 93, 4); //date |
||
52 | b182eeb0 | Aurelien Jacobs | count += id3v1_set_string(s, "comment", buf + 97, 30); |
53 | 90d9d97d | James Darnley | if ((tag = av_metadata_get(s->metadata, "TRCK", NULL, 0))) { //track |
54 | 58607ca7 | Diego Biurrun | buf[125] = 0; |
55 | b182eeb0 | Aurelien Jacobs | buf[126] = atoi(tag->value);
|
56 | count++; |
||
57 | 58607ca7 | Diego Biurrun | } |
58 | ea25c063 | Justin Ruggles | buf[127] = 0xFF; /* default to unknown genre */ |
59 | 90d9d97d | James Darnley | if ((tag = av_metadata_get(s->metadata, "TCON", NULL, 0))) { //genre |
60 | ee56cab3 | Aurelien Jacobs | for(i = 0; i <= ID3v1_GENRE_MAX; i++) { |
61 | 75411182 | Patrick Dehne | if (!strcasecmp(tag->value, ff_id3v1_genre_str[i])) {
|
62 | ee56cab3 | Aurelien Jacobs | buf[127] = i;
|
63 | count++; |
||
64 | break;
|
||
65 | } |
||
66 | 58607ca7 | Diego Biurrun | } |
67 | } |
||
68 | b182eeb0 | Aurelien Jacobs | return count;
|
69 | 58607ca7 | Diego Biurrun | } |
70 | |||
71 | 6a58e151 | Fabrice Bellard | /* simple formats */
|
72 | 0dc56bfa | Andreas Öman | |
73 | static void id3v2_put_size(AVFormatContext *s, int size) |
||
74 | { |
||
75 | 77eb5504 | Anton Khirnov | avio_w8(s->pb, size >> 21 & 0x7f); |
76 | avio_w8(s->pb, size >> 14 & 0x7f); |
||
77 | avio_w8(s->pb, size >> 7 & 0x7f); |
||
78 | avio_w8(s->pb, size & 0x7f);
|
||
79 | 0dc56bfa | Andreas Öman | } |
80 | |||
81 | 5a6de4e7 | Anton Khirnov | static int string_is_ascii(const uint8_t *str) |
82 | { |
||
83 | while (*str && *str < 128) str++; |
||
84 | return !*str;
|
||
85 | } |
||
86 | |||
87 | 187e2347 | Anton Khirnov | /**
|
88 | * Write a text frame with one (normal frames) or two (TXXX frames) strings
|
||
89 | * according to encoding (only UTF-8 or UTF-16+BOM supported).
|
||
90 | 8c3caf7f | Anton Khirnov | * @return number of bytes written or a negative error code.
|
91 | 187e2347 | Anton Khirnov | */
|
92 | static int id3v2_put_ttag(AVFormatContext *s, const char *str1, const char *str2, |
||
93 | 8c3caf7f | Anton Khirnov | uint32_t tag, enum ID3v2Encoding enc)
|
94 | 0dc56bfa | Andreas Öman | { |
95 | 187e2347 | Anton Khirnov | int len;
|
96 | uint8_t *pb; |
||
97 | ae628ec1 | Anton Khirnov | int (*put)(AVIOContext*, const char*); |
98 | AVIOContext *dyn_buf; |
||
99 | b92c5452 | Anton Khirnov | if (avio_open_dyn_buf(&dyn_buf) < 0) |
100 | 8c3caf7f | Anton Khirnov | return AVERROR(ENOMEM);
|
101 | 187e2347 | Anton Khirnov | |
102 | 5a6de4e7 | Anton Khirnov | /* check if the strings are ASCII-only and use UTF16 only if
|
103 | * they're not */
|
||
104 | if (enc == ID3v2_ENCODING_UTF16BOM && string_is_ascii(str1) &&
|
||
105 | (!str2 || string_is_ascii(str2))) |
||
106 | enc = ID3v2_ENCODING_ISO8859; |
||
107 | |||
108 | 77eb5504 | Anton Khirnov | avio_w8(dyn_buf, enc); |
109 | 187e2347 | Anton Khirnov | if (enc == ID3v2_ENCODING_UTF16BOM) {
|
110 | 77eb5504 | Anton Khirnov | avio_wl16(dyn_buf, 0xFEFF); /* BOM */ |
111 | 187e2347 | Anton Khirnov | put = avio_put_str16le; |
112 | 8c3caf7f | Anton Khirnov | } else
|
113 | put = avio_put_str; |
||
114 | |||
115 | 187e2347 | Anton Khirnov | put(dyn_buf, str1); |
116 | if (str2)
|
||
117 | put(dyn_buf, str2); |
||
118 | 6dc7d80d | Anton Khirnov | len = avio_close_dyn_buf(dyn_buf, &pb); |
119 | 187e2347 | Anton Khirnov | |
120 | 77eb5504 | Anton Khirnov | avio_wb32(s->pb, tag); |
121 | 187e2347 | Anton Khirnov | id3v2_put_size(s, len); |
122 | 77eb5504 | Anton Khirnov | avio_wb16(s->pb, 0);
|
123 | avio_write(s->pb, pb, len); |
||
124 | 187e2347 | Anton Khirnov | |
125 | av_freep(&pb); |
||
126 | return len + ID3v2_HEADER_SIZE;
|
||
127 | 0dc56bfa | Andreas Öman | } |
128 | |||
129 | 0cd0a224 | Diego Biurrun | static int mp3_write_trailer(struct AVFormatContext *s) |
130 | { |
||
131 | uint8_t buf[ID3v1_TAG_SIZE]; |
||
132 | |||
133 | /* write the id3v1 tag */
|
||
134 | if (id3v1_create_tag(s, buf) > 0) { |
||
135 | 77eb5504 | Anton Khirnov | avio_write(s->pb, buf, ID3v1_TAG_SIZE); |
136 | b7f2fdde | Anton Khirnov | avio_flush(s->pb); |
137 | 0cd0a224 | Diego Biurrun | } |
138 | return 0; |
||
139 | } |
||
140 | |||
141 | #if CONFIG_MP2_MUXER
|
||
142 | c6610a21 | Diego Elio Pettenò | AVOutputFormat ff_mp2_muxer = { |
143 | 0cd0a224 | Diego Biurrun | "mp2",
|
144 | NULL_IF_CONFIG_SMALL("MPEG audio layer 2"),
|
||
145 | "audio/x-mpeg",
|
||
146 | "mp2,m2a",
|
||
147 | 0,
|
||
148 | CODEC_ID_MP2, |
||
149 | CODEC_ID_NONE, |
||
150 | NULL,
|
||
151 | fed5676f | Anton Khirnov | ff_raw_write_packet, |
152 | 0cd0a224 | Diego Biurrun | mp3_write_trailer, |
153 | }; |
||
154 | #endif
|
||
155 | |||
156 | #if CONFIG_MP3_MUXER
|
||
157 | 22272f61 | Anton Khirnov | typedef struct MP3Context { |
158 | const AVClass *class;
|
||
159 | int id3v2_version;
|
||
160 | } MP3Context; |
||
161 | |||
162 | static const AVOption options[] = { |
||
163 | { "id3v2_version", "Select ID3v2 version to write. Currently 3 and 4 are supported.", |
||
164 | offsetof(MP3Context, id3v2_version), FF_OPT_TYPE_INT, 4, 3, 4, AV_OPT_FLAG_ENCODING_PARAM}, |
||
165 | { NULL },
|
||
166 | }; |
||
167 | |||
168 | static const AVClass mp3_muxer_class = { |
||
169 | "MP3 muxer",
|
||
170 | av_default_item_name, |
||
171 | options, |
||
172 | LIBAVUTIL_VERSION_INT, |
||
173 | }; |
||
174 | |||
175 | static int id3v2_check_write_tag(AVFormatContext *s, AVMetadataTag *t, const char table[][4], |
||
176 | enum ID3v2Encoding enc)
|
||
177 | cb6bc576 | Anton Khirnov | { |
178 | uint32_t tag; |
||
179 | int i;
|
||
180 | |||
181 | if (t->key[0] != 'T' || strlen(t->key) != 4) |
||
182 | return -1; |
||
183 | tag = AV_RB32(t->key); |
||
184 | for (i = 0; *table[i]; i++) |
||
185 | if (tag == AV_RB32(table[i]))
|
||
186 | 22272f61 | Anton Khirnov | return id3v2_put_ttag(s, t->value, NULL, tag, enc); |
187 | cb6bc576 | Anton Khirnov | return -1; |
188 | } |
||
189 | |||
190 | 0dc56bfa | Andreas Öman | /**
|
191 | 22272f61 | Anton Khirnov | * Write an ID3v2 header at beginning of stream
|
192 | 0dc56bfa | Andreas Öman | */
|
193 | |||
194 | 6a58e151 | Fabrice Bellard | static int mp3_write_header(struct AVFormatContext *s) |
195 | { |
||
196 | 22272f61 | Anton Khirnov | MP3Context *mp3 = s->priv_data; |
197 | 078d89a2 | Anton Khirnov | AVMetadataTag *t = NULL;
|
198 | 22272f61 | Anton Khirnov | int totlen = 0, enc = mp3->id3v2_version == 3 ? ID3v2_ENCODING_UTF16BOM : |
199 | ID3v2_ENCODING_UTF8; |
||
200 | 078d89a2 | Anton Khirnov | int64_t size_pos, cur_pos; |
201 | 0dc56bfa | Andreas Öman | |
202 | 77eb5504 | Anton Khirnov | avio_wb32(s->pb, MKBETAG('I', 'D', '3', mp3->id3v2_version)); |
203 | avio_w8(s->pb, 0);
|
||
204 | avio_w8(s->pb, 0); /* flags */ |
||
205 | 0dc56bfa | Andreas Öman | |
206 | 078d89a2 | Anton Khirnov | /* reserve space for size */
|
207 | a2704c97 | Anton Khirnov | size_pos = avio_tell(s->pb); |
208 | 77eb5504 | Anton Khirnov | avio_wb32(s->pb, 0);
|
209 | 078d89a2 | Anton Khirnov | |
210 | cb6bc576 | Anton Khirnov | ff_metadata_conv(&s->metadata, ff_id3v2_34_metadata_conv, NULL);
|
211 | 22272f61 | Anton Khirnov | if (mp3->id3v2_version == 4) |
212 | ff_metadata_conv(&s->metadata, ff_id3v2_4_metadata_conv, NULL);
|
||
213 | |||
214 | 078d89a2 | Anton Khirnov | while ((t = av_metadata_get(s->metadata, "", t, AV_METADATA_IGNORE_SUFFIX))) { |
215 | 8c3caf7f | Anton Khirnov | int ret;
|
216 | 078d89a2 | Anton Khirnov | |
217 | 22272f61 | Anton Khirnov | if ((ret = id3v2_check_write_tag(s, t, ff_id3v2_tags, enc)) > 0) { |
218 | cb6bc576 | Anton Khirnov | totlen += ret; |
219 | continue;
|
||
220 | 078d89a2 | Anton Khirnov | } |
221 | 22272f61 | Anton Khirnov | if ((ret = id3v2_check_write_tag(s, t, mp3->id3v2_version == 3 ? |
222 | ff_id3v2_3_tags : ff_id3v2_4_tags, enc)) > 0) {
|
||
223 | 8c3caf7f | Anton Khirnov | totlen += ret; |
224 | cb6bc576 | Anton Khirnov | continue;
|
225 | 078d89a2 | Anton Khirnov | } |
226 | cb6bc576 | Anton Khirnov | |
227 | /* unknown tag, write as TXXX frame */
|
||
228 | 22272f61 | Anton Khirnov | if ((ret = id3v2_put_ttag(s, t->key, t->value, MKBETAG('T', 'X', 'X', 'X'), enc)) < 0) |
229 | cb6bc576 | Anton Khirnov | return ret;
|
230 | totlen += ret; |
||
231 | 078d89a2 | Anton Khirnov | } |
232 | |||
233 | a2704c97 | Anton Khirnov | cur_pos = avio_tell(s->pb); |
234 | 6b4aa5da | Anton Khirnov | avio_seek(s->pb, size_pos, SEEK_SET); |
235 | 0dc56bfa | Andreas Öman | id3v2_put_size(s, totlen); |
236 | 6b4aa5da | Anton Khirnov | avio_seek(s->pb, cur_pos, SEEK_SET); |
237 | 0dc56bfa | Andreas Öman | |
238 | 6a58e151 | Fabrice Bellard | return 0; |
239 | } |
||
240 | |||
241 | c6610a21 | Diego Elio Pettenò | AVOutputFormat ff_mp3_muxer = { |
242 | 6a58e151 | Fabrice Bellard | "mp3",
|
243 | bde15e74 | Stefano Sabatini | NULL_IF_CONFIG_SMALL("MPEG audio layer 3"),
|
244 | 6a58e151 | Fabrice Bellard | "audio/x-mpeg",
|
245 | "mp3",
|
||
246 | 22272f61 | Anton Khirnov | sizeof(MP3Context),
|
247 | 6a58e151 | Fabrice Bellard | CODEC_ID_MP3, |
248 | d9133126 | Carl Eugen Hoyos | CODEC_ID_NONE, |
249 | 6a58e151 | Fabrice Bellard | mp3_write_header, |
250 | fed5676f | Anton Khirnov | ff_raw_write_packet, |
251 | 6a58e151 | Fabrice Bellard | mp3_write_trailer, |
252 | 2293a2e6 | Baptiste Coudurier | AVFMT_NOTIMESTAMPS, |
253 | 22272f61 | Anton Khirnov | .priv_class = &mp3_muxer_class, |
254 | 6a58e151 | Fabrice Bellard | }; |
255 | #endif |