diff options
Diffstat (limited to 'libavformat/mp3enc.c')
-rw-r--r-- | libavformat/mp3enc.c | 164 |
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, }; |