aboutsummaryrefslogtreecommitdiff
path: root/libavformat/mp3enc.c
diff options
context:
space:
mode:
Diffstat (limited to 'libavformat/mp3enc.c')
-rw-r--r--libavformat/mp3enc.c164
1 files changed, 106 insertions, 58 deletions
diff --git a/libavformat/mp3enc.c b/libavformat/mp3enc.c
index 476d7f71cb..7e6b2e1795 100644
--- a/libavformat/mp3enc.c
+++ b/libavformat/mp3enc.c
@@ -2,20 +2,20 @@
* MP3 muxer
* Copyright (c) 2003 Fabrice Bellard
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -51,11 +51,12 @@ static int id3v1_create_tag(AVFormatContext *s, uint8_t *buf)
buf[0] = 'T';
buf[1] = 'A';
buf[2] = 'G';
- count += id3v1_set_string(s, "TIT2", buf + 3, 30); //title
- count += id3v1_set_string(s, "TPE1", buf + 33, 30); //author|artist
- count += id3v1_set_string(s, "TALB", buf + 63, 30); //album
- count += id3v1_set_string(s, "TDRL", buf + 93, 4); //date
- count += id3v1_set_string(s, "comment", buf + 97, 30);
+ /* we knowingly overspecify each tag length by one byte to compensate for the mandatory null byte added by av_strlcpy */
+ count += id3v1_set_string(s, "TIT2", buf + 3, 30 + 1); //title
+ count += id3v1_set_string(s, "TPE1", buf + 33, 30 + 1); //author|artist
+ count += id3v1_set_string(s, "TALB", buf + 63, 30 + 1); //album
+ count += id3v1_set_string(s, "TDRL", buf + 93, 4 + 1); //date
+ count += id3v1_set_string(s, "comment", buf + 97, 30 + 1);
if ((tag = av_dict_get(s->metadata, "TRCK", NULL, 0))) { //track
buf[125] = 0;
buf[126] = atoi(tag->value);
@@ -111,22 +112,23 @@ static const uint8_t xing_offtbl[2][2] = {{32, 17}, {17, 9}};
/*
* Write an empty XING header and initialize respective data.
*/
-static void mp3_write_xing(AVFormatContext *s)
+static int mp3_write_xing(AVFormatContext *s)
{
MP3Context *mp3 = s->priv_data;
AVCodecContext *codec = s->streams[mp3->audio_stream_idx]->codec;
- int32_t header;
+ int32_t header;
MPADecodeHeader mpah;
int srate_idx, i, channels;
int bitrate_idx;
- int best_bitrate_idx;
+ int best_bitrate_idx = -1;
int best_bitrate_error = INT_MAX;
int xing_offset;
int ver = 0;
- int lsf, bytes_needed;
+ int bytes_needed;
+ const char *vendor = (s->flags & AVFMT_FLAG_BITEXACT) ? "Lavf" : LIBAVFORMAT_IDENT;
if (!s->pb->seekable || !mp3->write_xing)
- return;
+ return 0;
for (i = 0; i < FF_ARRAY_ELEMS(avpriv_mpa_freq_tab); i++) {
const uint16_t base_freq = avpriv_mpa_freq_tab[i];
@@ -140,9 +142,8 @@ static void mp3_write_xing(AVFormatContext *s)
break;
}
if (i == FF_ARRAY_ELEMS(avpriv_mpa_freq_tab)) {
- av_log(s, AV_LOG_WARNING, "Unsupported sample rate, not writing Xing "
- "header.\n");
- return;
+ av_log(s, AV_LOG_WARNING, "Unsupported sample rate, not writing Xing header.\n");
+ return -1;
}
switch (codec->channels) {
@@ -150,41 +151,43 @@ static void mp3_write_xing(AVFormatContext *s)
case 2: channels = MPA_STEREO; break;
default: av_log(s, AV_LOG_WARNING, "Unsupported number of channels, "
"not writing Xing header.\n");
- return;
+ return -1;
}
/* dummy MPEG audio header */
- header = 0xff << 24; // sync
+ header = 0xffU << 24; // sync
header |= (0x7 << 5 | ver << 3 | 0x1 << 1 | 0x1) << 16; // sync/audio-version/layer 3/no crc*/
header |= (srate_idx << 2) << 8;
header |= channels << 6;
- lsf = !((header & (1 << 20) && header & (1 << 19)));
-
- xing_offset = xing_offtbl[ver != 3][channels == 1];
- bytes_needed = 4 // header
- + xing_offset
- + 4 // xing tag
- + 4 // frames/size/toc flags
- + 4 // frames
- + 4 // size
- + XING_TOC_SIZE; // toc
-
for (bitrate_idx = 1; bitrate_idx < 15; bitrate_idx++) {
- int bit_rate = 1000 * avpriv_mpa_bitrate_tab[lsf][3 - 1][bitrate_idx];
+ int bit_rate = 1000 * avpriv_mpa_bitrate_tab[ver != 3][3 - 1][bitrate_idx];
int error = FFABS(bit_rate - codec->bit_rate);
- if (error < best_bitrate_error){
+ if (error < best_bitrate_error) {
best_bitrate_error = error;
best_bitrate_idx = bitrate_idx;
}
}
+ av_assert0(best_bitrate_idx >= 0);
- for (bitrate_idx = best_bitrate_idx; bitrate_idx < 15; bitrate_idx++) {
+ for (bitrate_idx = best_bitrate_idx; ; bitrate_idx++) {
int32_t mask = bitrate_idx << (4 + 8);
+ if (15 == bitrate_idx)
+ return -1;
header |= mask;
avpriv_mpegaudio_decode_header(&mpah, header);
+ xing_offset=xing_offtbl[mpah.lsf == 1][mpah.nb_channels == 1];
+ bytes_needed = 4 // header
+ + xing_offset
+ + 4 // xing tag
+ + 4 // frames/size/toc flags
+ + 4 // frames
+ + 4 // size
+ + XING_TOC_SIZE // toc
+ + 24
+ ;
if (bytes_needed <= mpah.frame_size)
break;
@@ -194,26 +197,32 @@ static void mp3_write_xing(AVFormatContext *s)
avio_wb32(s->pb, header);
- avpriv_mpegaudio_decode_header(&mpah, header);
-
- av_assert0(mpah.frame_size >= XING_MAX_SIZE);
-
ffio_fill(s->pb, 0, xing_offset);
mp3->xing_offset = avio_tell(s->pb);
ffio_wfourcc(s->pb, "Xing");
avio_wb32(s->pb, 0x01 | 0x02 | 0x04); // frames / size / TOC
mp3->size = mpah.frame_size;
- mp3->want = 1;
+ mp3->want=1;
+ mp3->seen=0;
+ mp3->pos=0;
avio_wb32(s->pb, 0); // frames
avio_wb32(s->pb, 0); // size
- // TOC
- for (i = 0; i < XING_TOC_SIZE; i++)
- avio_w8(s->pb, 255 * i / XING_TOC_SIZE);
+ // toc
+ for (i = 0; i < XING_TOC_SIZE; ++i)
+ avio_w8(s->pb, (uint8_t)(255 * i / XING_TOC_SIZE));
+
+ for (i = 0; i < strlen(vendor); ++i)
+ avio_w8(s->pb, vendor[i]);
+ for (; i < 21; ++i)
+ avio_w8(s->pb, 0);
+ avio_wb24(s->pb, FFMAX(codec->delay - 528 - 1, 0)<<12);
ffio_fill(s->pb, 0, mpah.frame_size - bytes_needed);
+
+ return 0;
}
/*
@@ -234,7 +243,7 @@ static void mp3_xing_add_frame(MP3Context *mp3, AVPacket *pkt)
if (XING_NUM_BAGS == ++mp3->pos) {
/* shrink table to half size by throwing away each second bag. */
for (i = 1; i < XING_NUM_BAGS; i += 2)
- mp3->bag[i / 2] = mp3->bag[i];
+ mp3->bag[i >> 1] = mp3->bag[i];
/* double wanted amount per bag. */
mp3->want *= 2;
@@ -250,20 +259,43 @@ static int mp3_write_audio_packet(AVFormatContext *s, AVPacket *pkt)
{
MP3Context *mp3 = s->priv_data;
- if (mp3->xing_offset && pkt->size >= 4) {
- MPADecodeHeader c;
+ if (pkt->data && pkt->size >= 4) {
+ MPADecodeHeader mpah;
+ int av_unused base;
uint32_t h;
h = AV_RB32(pkt->data);
if (ff_mpa_check_header(h) == 0) {
- avpriv_mpegaudio_decode_header(&c, h);
+ avpriv_mpegaudio_decode_header(&mpah, h);
if (!mp3->initial_bitrate)
- mp3->initial_bitrate = c.bit_rate;
- if ((c.bit_rate == 0) || (mp3->initial_bitrate != c.bit_rate))
+ mp3->initial_bitrate = mpah.bit_rate;
+ if ((mpah.bit_rate == 0) || (mp3->initial_bitrate != mpah.bit_rate))
mp3->has_variable_bitrate = 1;
+ } else {
+ av_log(s, AV_LOG_WARNING, "Audio packet of size %d (starting with %08X...) "
+ "is invalid, writing it anyway.\n", pkt->size, h);
+ }
+
+#ifdef FILTER_VBR_HEADERS
+ /* filter out XING and INFO headers. */
+ base = 4 + xing_offtbl[mpah.lsf == 1][mpah.nb_channels == 1];
+
+ if (base + 4 <= pkt->size) {
+ uint32_t v = AV_RB32(pkt->data + base);
+
+ if (MKBETAG('X','i','n','g') == v || MKBETAG('I','n','f','o') == v)
+ return 0;
}
- mp3_xing_add_frame(mp3, pkt);
+ /* filter out VBRI headers. */
+ base = 4 + 32;
+
+ if (base + 4 <= pkt->size && MKBETAG('V','B','R','I') == AV_RB32(pkt->data + base))
+ return 0;
+#endif
+
+ if (mp3->xing_offset)
+ mp3_xing_add_frame(mp3, pkt);
}
return ff_raw_write_packet(s, pkt);
@@ -275,7 +307,7 @@ static int mp3_queue_flush(AVFormatContext *s)
AVPacketList *pktl;
int ret = 0, write = 1;
- ff_id3v2_finish(&mp3->id3, s->pb);
+ ff_id3v2_finish(&mp3->id3, s->pb, s->metadata_header_padding);
mp3_write_xing(s);
while ((pktl = mp3->queue)) {
@@ -337,11 +369,22 @@ static int mp3_write_trailer(struct AVFormatContext *s)
return 0;
}
+static int query_codec(enum AVCodecID id, int std_compliance)
+{
+ const CodecMime *cm= ff_id3v2_mime_tags;
+ while(cm->id != AV_CODEC_ID_NONE) {
+ if(id == cm->id)
+ return MKTAG('A', 'P', 'I', 'C');
+ cm++;
+ }
+ return -1;
+}
+
#if CONFIG_MP2_MUXER
AVOutputFormat ff_mp2_muxer = {
.name = "mp2",
.long_name = NULL_IF_CONFIG_SMALL("MP2 (MPEG audio layer 2)"),
- .mime_type = "audio/x-mpeg",
+ .mime_type = "audio/mpeg",
.extensions = "mp2,m2a,mpa",
.audio_codec = AV_CODEC_ID_MP2,
.video_codec = AV_CODEC_ID_NONE,
@@ -377,14 +420,18 @@ static int mp3_write_packet(AVFormatContext *s, AVPacket *pkt)
if (mp3->pics_to_write) {
/* buffer audio packets until we get all the pictures */
AVPacketList *pktl = av_mallocz(sizeof(*pktl));
- if (!pktl)
- return AVERROR(ENOMEM);
+ int ret;
+ if (!pktl) {
+ av_log(s, AV_LOG_WARNING, "Not enough memory to buffer audio. Skipping picture streams\n");
+ mp3->pics_to_write = 0;
+ mp3_queue_flush(s);
+ return mp3_write_audio_packet(s, pkt);
+ }
- pktl->pkt = *pkt;
- pktl->pkt.buf = av_buffer_ref(pkt->buf);
- if (!pktl->pkt.buf) {
+ ret = av_copy_packet(&pktl->pkt, pkt);
+ if (ret < 0) {
av_freep(&pktl);
- return AVERROR(ENOMEM);
+ return ret;
}
if (mp3->queue_end)
@@ -473,7 +520,7 @@ static int mp3_write_header(struct AVFormatContext *s)
if (!mp3->pics_to_write) {
if (mp3->id3v2_version)
- ff_id3v2_finish(&mp3->id3, s->pb);
+ ff_id3v2_finish(&mp3->id3, s->pb, s->metadata_header_padding);
mp3_write_xing(s);
}
@@ -483,7 +530,7 @@ static int mp3_write_header(struct AVFormatContext *s)
AVOutputFormat ff_mp3_muxer = {
.name = "mp3",
.long_name = NULL_IF_CONFIG_SMALL("MP3 (MPEG audio layer 3)"),
- .mime_type = "audio/x-mpeg",
+ .mime_type = "audio/mpeg",
.extensions = "mp3",
.priv_data_size = sizeof(MP3Context),
.audio_codec = AV_CODEC_ID_MP3,
@@ -491,6 +538,7 @@ AVOutputFormat ff_mp3_muxer = {
.write_header = mp3_write_header,
.write_packet = mp3_write_packet,
.write_trailer = mp3_write_trailer,
+ .query_codec = query_codec,
.flags = AVFMT_NOTIMESTAMPS,
.priv_class = &mp3_muxer_class,
};