X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=libavformat%2Fmp3enc.c;h=793c9c62af12a721303a84157604b9aefeed2e49;hb=86931dc9300f544ac9d9c2bfbffab4372071e779;hp=07c28e176d889cef55375cd0367ea8c4a0179213;hpb=145f741e115c75eac511e0ceb7a3c44585e871e3;p=ffmpeg diff --git a/libavformat/mp3enc.c b/libavformat/mp3enc.c index 07c28e176d8..793c9c62af1 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 */ @@ -31,6 +31,10 @@ #include "libavcodec/mpegaudiodecheader.h" #include "libavutil/intreadwrite.h" #include "libavutil/opt.h" +#include "libavcodec/mpegaudio.h" +#include "libavcodec/mpegaudiodata.h" +#include "libavcodec/mpegaudiodecheader.h" +#include "libavformat/avio_internal.h" #include "libavutil/dict.h" static int id3v1_set_string(AVFormatContext *s, const char *key, @@ -74,72 +78,23 @@ static int id3v1_create_tag(AVFormatContext *s, uint8_t *buf) return count; } -/* simple formats */ - -static void id3v2_put_size(AVFormatContext *s, int size) -{ - avio_w8(s->pb, size >> 21 & 0x7f); - avio_w8(s->pb, size >> 14 & 0x7f); - avio_w8(s->pb, size >> 7 & 0x7f); - avio_w8(s->pb, size & 0x7f); -} - -static int string_is_ascii(const uint8_t *str) -{ - while (*str && *str < 128) str++; - return !*str; -} - -/** - * Write a text frame with one (normal frames) or two (TXXX frames) strings - * according to encoding (only UTF-8 or UTF-16+BOM supported). - * @return number of bytes written or a negative error code. - */ -static int id3v2_put_ttag(AVFormatContext *s, const char *str1, const char *str2, - uint32_t tag, enum ID3v2Encoding enc) -{ - int len; - uint8_t *pb; - int (*put)(AVIOContext*, const char*); - AVIOContext *dyn_buf; - if (avio_open_dyn_buf(&dyn_buf) < 0) - return AVERROR(ENOMEM); - - /* check if the strings are ASCII-only and use UTF16 only if - * they're not */ - if (enc == ID3v2_ENCODING_UTF16BOM && string_is_ascii(str1) && - (!str2 || string_is_ascii(str2))) - enc = ID3v2_ENCODING_ISO8859; - - avio_w8(dyn_buf, enc); - if (enc == ID3v2_ENCODING_UTF16BOM) { - avio_wl16(dyn_buf, 0xFEFF); /* BOM */ - put = avio_put_str16le; - } else - put = avio_put_str; - - put(dyn_buf, str1); - if (str2) - put(dyn_buf, str2); - len = avio_close_dyn_buf(dyn_buf, &pb); - - avio_wb32(s->pb, tag); - id3v2_put_size(s, len); - avio_wb16(s->pb, 0); - avio_write(s->pb, pb, len); - - av_freep(&pb); - return len + ID3v2_HEADER_SIZE; -} +#define VBR_NUM_BAGS 400 +#define VBR_TOC_SIZE 100 typedef struct MP3Context { const AVClass *class; int id3v2_version; int write_id3v1; - int64_t nb_frames_offset; + int64_t frames_offset; + int32_t frames; + int32_t size; + uint32_t want; + uint32_t seen; + uint32_t pos; + uint64_t bag[VBR_NUM_BAGS]; } MP3Context; -static int mp3_write_trailer(struct AVFormatContext *s) +static int mp2_write_trailer(struct AVFormatContext *s) { uint8_t buf[ID3v1_TAG_SIZE]; MP3Context *mp3 = s->priv_data; @@ -150,8 +105,8 @@ static int mp3_write_trailer(struct AVFormatContext *s) } /* write number of frames */ - if (mp3 && mp3->nb_frames_offset) { - avio_seek(s->pb, mp3->nb_frames_offset, SEEK_SET); + if (mp3 && mp3->frames_offset) { + avio_seek(s->pb, mp3->frames_offset, SEEK_SET); avio_wb32(s->pb, s->streams[0]->nb_frames); avio_seek(s->pb, 0, SEEK_END); } @@ -170,7 +125,8 @@ AVOutputFormat ff_mp2_muxer = { .audio_codec = CODEC_ID_MP2, .video_codec = CODEC_ID_NONE, .write_packet = ff_raw_write_packet, - .write_trailer = mp3_write_trailer, + .write_trailer = mp2_write_trailer, + .flags = AVFMT_NOTIMESTAMPS, }; #endif @@ -191,65 +147,141 @@ static const AVClass mp3_muxer_class = { .version = LIBAVUTIL_VERSION_INT, }; -static int id3v2_check_write_tag(AVFormatContext *s, AVDictionaryEntry *t, const char table[][4], - enum ID3v2Encoding enc) -{ - uint32_t tag; - int i; - - if (t->key[0] != 'T' || strlen(t->key) != 4) - return -1; - tag = AV_RB32(t->key); - for (i = 0; *table[i]; i++) - if (tag == AV_RB32(table[i])) - return id3v2_put_ttag(s, t->value, NULL, tag, enc); - return -1; -} +static const int64_t xing_offtbl[2][2] = {{32, 17}, {17,9}}; -/* insert a dummy frame containing number of frames */ -static void mp3_write_xing(AVFormatContext *s) +/* + * Write an empty XING header and initialize respective data. + */ +static int mp3_write_xing(AVFormatContext *s) { - AVCodecContext *codec = s->streams[0]->codec; + AVCodecContext *codec = s->streams[0]->codec; MP3Context *mp3 = s->priv_data; - int bitrate_idx = 1; // 32 kbps - int64_t xing_offset = (codec->channels == 2) ? 32 : 17; - int32_t header; - MPADecodeHeader mpah; - int srate_idx, i, channels; - - for (i = 0; i < FF_ARRAY_ELEMS(ff_mpa_freq_tab); i++) - if (ff_mpa_freq_tab[i] == codec->sample_rate) { + int bitrate_idx = 3; + int64_t xing_offset; + int32_t mask, header; + MPADecodeHeader c; + int srate_idx, i, channels; + int needed; + + for (i = 0; i < FF_ARRAY_ELEMS(avpriv_mpa_freq_tab); i++) + if (avpriv_mpa_freq_tab[i] == codec->sample_rate) { srate_idx = i; break; } - if (i == FF_ARRAY_ELEMS(ff_mpa_freq_tab)) { + if (i == FF_ARRAY_ELEMS(avpriv_mpa_freq_tab)) { av_log(s, AV_LOG_ERROR, "Unsupported sample rate.\n"); - return; + return -1; } switch (codec->channels) { case 1: channels = MPA_MONO; break; case 2: channels = MPA_STEREO; break; - default: av_log(s, AV_LOG_ERROR, "Unsupported number of channels.\n"); return; + default: av_log(s, AV_LOG_ERROR, "Unsupported number of channels.\n"); return -1; } /* dummy MPEG audio header */ header = 0xff << 24; // sync header |= (0x7 << 5 | 0x3 << 3 | 0x1 << 1 | 0x1) << 16; // sync/mpeg-1/layer 3/no crc*/ - header |= (bitrate_idx << 4 | srate_idx << 2) << 8; + header |= (srate_idx << 2) << 8; header |= channels << 6; - avio_wb32(s->pb, header); - ff_mpegaudio_decode_header(&mpah, header); + for (;;) { + if (15 == bitrate_idx) + return -1; + + mask = (bitrate_idx << 4) << 8; + header |= mask; + avpriv_mpegaudio_decode_header(&c, header); + xing_offset=xing_offtbl[c.lsf == 1][c.nb_channels == 1]; + needed = 4 // header + + xing_offset + + 4 // xing tag + + 4 // frames/size/toc flags + + 4 // frames + + 4 // size + + VBR_TOC_SIZE; // toc + + if (needed <= c.frame_size) + break; + + header &= ~mask; + ++bitrate_idx; + } + avio_wb32(s->pb, header); ffio_fill(s->pb, 0, xing_offset); - ffio_wfourcc(s->pb, "Xing"); - avio_wb32(s->pb, 0x1); // only number of frames - mp3->nb_frames_offset = avio_tell(s->pb); - avio_wb32(s->pb, 0); + avio_wb32(s->pb, MKBETAG('X', 'i', 'n', 'g')); + avio_wb32(s->pb, 0x01 | 0x02 | 0x04); // frames/size/toc + + mp3->frames_offset = avio_tell(s->pb); + mp3->size = c.frame_size; + mp3->want=1; + mp3->seen=0; + mp3->pos=0; + + avio_wb32(s->pb, 0); // frames + avio_wb32(s->pb, 0); // size - mpah.frame_size -= 4 + xing_offset + 4 + 4 + 4; - ffio_fill(s->pb, 0, mpah.frame_size); + // toc + for (i = 0; i < VBR_TOC_SIZE; ++i) + avio_w8(s->pb, (uint8_t)(255 * i / VBR_TOC_SIZE)); + + ffio_fill(s->pb, 0, c.frame_size - needed); + avio_flush(s->pb); + + return 0; +} + +/* + * Add a frame to XING data. + * Following lame's "VbrTag.c". + */ +static void mp3_xing_add_frame(AVFormatContext *s, AVPacket *pkt) +{ + MP3Context *mp3 = s->priv_data; + int i; + + ++mp3->frames; + mp3->size += pkt->size; + + if (mp3->want == ++mp3->seen) { + mp3->bag[mp3->pos] = mp3->size; + + if (VBR_NUM_BAGS == ++mp3->pos) { + /* shrink table to half size by throwing away each second bag. */ + for (i = 1; i < VBR_NUM_BAGS; i += 2) + mp3->bag[i >> 1] = mp3->bag[i]; + + /* double wanted amount per bag. */ + mp3->want <<= 1; + /* adjust current position to half of table size. */ + mp3->pos >>= 1; + } + + mp3->seen = 0; + } +} + +static void mp3_fix_xing(AVFormatContext *s) +{ + MP3Context *mp3 = s->priv_data; + int i; + + avio_flush(s->pb); + avio_seek(s->pb, mp3->frames_offset, SEEK_SET); + avio_wb32(s->pb, mp3->frames); + avio_wb32(s->pb, mp3->size); + + avio_w8(s->pb, 0); // first toc entry has to be zero. + + for (i = 1; i < VBR_TOC_SIZE; ++i) { + int j = i * mp3->pos / VBR_TOC_SIZE; + int seek_point = 256LL * mp3->bag[j] / mp3->size; + avio_w8(s->pb, FFMIN(seek_point, 255)); + } + + avio_flush(s->pb); + avio_seek(s->pb, 0, SEEK_END); } /** @@ -259,49 +291,64 @@ static void mp3_write_xing(AVFormatContext *s) static int mp3_write_header(struct AVFormatContext *s) { MP3Context *mp3 = s->priv_data; - AVDictionaryEntry *t = NULL; - int totlen = 0, enc = mp3->id3v2_version == 3 ? ID3v2_ENCODING_UTF16BOM : - ID3v2_ENCODING_UTF8; - int64_t size_pos, cur_pos; + int ret; - avio_wb32(s->pb, MKBETAG('I', 'D', '3', mp3->id3v2_version)); - avio_w8(s->pb, 0); - avio_w8(s->pb, 0); /* flags */ + ret = ff_id3v2_write(s, mp3->id3v2_version, ID3v2_DEFAULT_MAGIC); + if (ret < 0) + return ret; - /* reserve space for size */ - size_pos = avio_tell(s->pb); - avio_wb32(s->pb, 0); + if (s->pb->seekable) + mp3_write_xing(s); + + return 0; +} - ff_metadata_conv(&s->metadata, ff_id3v2_34_metadata_conv, NULL); - if (mp3->id3v2_version == 4) - ff_metadata_conv(&s->metadata, ff_id3v2_4_metadata_conv, NULL); +static int mp3_write_packet(AVFormatContext *s, AVPacket *pkt) +{ + if (! pkt || ! pkt->data || pkt->size < 4) + return ff_raw_write_packet(s, pkt); + else { + MP3Context *mp3 = s->priv_data; +#ifdef FILTER_VBR_HEADERS + MPADecodeHeader c; + int base; - while ((t = av_dict_get(s->metadata, "", t, AV_DICT_IGNORE_SUFFIX))) { - int ret; + ff_mpegaudio_decode_header(&c, AV_RB32(pkt->data)); - if ((ret = id3v2_check_write_tag(s, t, ff_id3v2_tags, enc)) > 0) { - totlen += ret; - continue; - } - if ((ret = id3v2_check_write_tag(s, t, mp3->id3v2_version == 3 ? - ff_id3v2_3_tags : ff_id3v2_4_tags, enc)) > 0) { - totlen += ret; - continue; + /* filter out XING and INFO headers. */ + base = 4 + xing_offtbl[c.lsf == 1][c.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; } - /* unknown tag, write as TXXX frame */ - if ((ret = id3v2_put_ttag(s, t->key, t->value, MKBETAG('T', 'X', 'X', 'X'), enc)) < 0) - return ret; - totlen += ret; + /* 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->frames_offset) + mp3_xing_add_frame(s, pkt); + + return ff_raw_write_packet(s, pkt); } +} + +static int mp3_write_trailer(AVFormatContext *s) +{ + MP3Context *mp3 = s->priv_data; + int ret=mp2_write_trailer(s); - cur_pos = avio_tell(s->pb); - avio_seek(s->pb, size_pos, SEEK_SET); - id3v2_put_size(s, totlen); - avio_seek(s->pb, cur_pos, SEEK_SET); + if (ret < 0) + return ret; - if (s->pb->seekable) - mp3_write_xing(s); + if (mp3->frames_offset) + mp3_fix_xing(s); return 0; } @@ -315,7 +362,7 @@ AVOutputFormat ff_mp3_muxer = { .audio_codec = CODEC_ID_MP3, .video_codec = CODEC_ID_NONE, .write_header = mp3_write_header, - .write_packet = ff_raw_write_packet, + .write_packet = mp3_write_packet, .write_trailer = mp3_write_trailer, .flags = AVFMT_NOTIMESTAMPS, .priv_class = &mp3_muxer_class,