diff options
author | Anton Khirnov <anton@khirnov.net> | 2011-03-21 21:02:50 +0100 |
---|---|---|
committer | Anton Khirnov <anton@khirnov.net> | 2011-07-08 22:47:24 +0200 |
commit | bda168d2b0210dda84f1a9d32c8aa4653d1674d5 (patch) | |
tree | 9f7df7e1dc49364d05406e1e67dff952e82f54ec /libavformat/mp3enc.c | |
parent | 1c6d2b7df061de2a9205ed711f4344926237bb5f (diff) | |
download | ffmpeg-bda168d2b0210dda84f1a9d32c8aa4653d1674d5.tar.gz |
mp3enc: write a xing frame containing number of frames in the file
Diffstat (limited to 'libavformat/mp3enc.c')
-rw-r--r-- | libavformat/mp3enc.c | 75 |
1 files changed, 70 insertions, 5 deletions
diff --git a/libavformat/mp3enc.c b/libavformat/mp3enc.c index 092d16ecc1..9d0ec5d929 100644 --- a/libavformat/mp3enc.c +++ b/libavformat/mp3enc.c @@ -21,10 +21,14 @@ #include <strings.h> #include "avformat.h" +#include "avio_internal.h" #include "id3v1.h" #include "id3v2.h" #include "rawenc.h" #include "libavutil/avstring.h" +#include "libavcodec/mpegaudio.h" +#include "libavcodec/mpegaudiodata.h" +#include "libavcodec/mpegaudiodecheader.h" #include "libavutil/intreadwrite.h" #include "libavutil/opt.h" #include "libavutil/dict.h" @@ -128,15 +132,31 @@ static int id3v2_put_ttag(AVFormatContext *s, const char *str1, const char *str2 return len + ID3v2_HEADER_SIZE; } +typedef struct MP3Context { + const AVClass *class; + int id3v2_version; + int64_t nb_frames_offset; +} MP3Context; + static int mp3_write_trailer(struct AVFormatContext *s) { uint8_t buf[ID3v1_TAG_SIZE]; + MP3Context *mp3 = s->priv_data; /* write the id3v1 tag */ if (id3v1_create_tag(s, buf) > 0) { avio_write(s->pb, buf, ID3v1_TAG_SIZE); - avio_flush(s->pb); } + + /* write number of frames */ + if (mp3 && mp3->nb_frames_offset) { + avio_seek(s->pb, mp3->nb_frames_offset, SEEK_SET); + avio_wb32(s->pb, s->streams[0]->nb_frames); + avio_seek(s->pb, 0, SEEK_END); + } + + avio_flush(s->pb); + return 0; } @@ -156,10 +176,6 @@ AVOutputFormat ff_mp2_muxer = { #endif #if CONFIG_MP3_MUXER -typedef struct MP3Context { - const AVClass *class; - int id3v2_version; -} MP3Context; static const AVOption options[] = { { "id3v2_version", "Select ID3v2 version to write. Currently 3 and 4 are supported.", @@ -189,6 +205,52 @@ static int id3v2_check_write_tag(AVFormatContext *s, AVDictionaryEntry *t, const return -1; } +/* insert a dummy frame containing number of frames */ +static void mp3_write_xing(AVFormatContext *s) +{ + 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) { + srate_idx = i; + break; + } + if (i == FF_ARRAY_ELEMS(ff_mpa_freq_tab)) { + av_log(s, AV_LOG_ERROR, "Unsupported sample rate.\n"); + return; + } + + 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; + } + + /* 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 |= channels << 6; + avio_wb32(s->pb, header); + + ff_mpegaudio_decode_header(&mpah, 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); + + mpah.frame_size -= 4 + xing_offset + 4 + 4 + 4; + ffio_fill(s->pb, 0, mpah.frame_size); +} + /** * Write an ID3v2 header at beginning of stream */ @@ -237,6 +299,9 @@ static int mp3_write_header(struct AVFormatContext *s) id3v2_put_size(s, totlen); avio_seek(s->pb, cur_pos, SEEK_SET); + if (s->pb->seekable) + mp3_write_xing(s); + return 0; } |