@ -74,12 +74,27 @@ static int id3v1_create_tag(AVFormatContext *s, uint8_t *buf)
return count ;
}
# define XING_NUM_BAGS 400
# define XING_TOC_SIZE 100
// maximum size of the xing frame: offset/Xing/flags/frames/size/TOC
# define XING_MAX_SIZE (32 + 4 + 4 + 4 + 4 + XING_TOC_SIZE)
typedef struct MP3Context {
const AVClass * class ;
ID3v2EncContext id3 ;
int id3v2_version ;
int write_id3v1 ;
int64_t nb_frames_offset ;
/* xing header */
int64_t xing_offset ;
int32_t frames ;
int32_t size ;
uint32_t want ;
uint32_t seen ;
uint32_t pos ;
uint64_t bag [ XING_NUM_BAGS ] ;
int initial_bitrate ;
int has_variable_bitrate ;
/* index of the audio stream */
int audio_stream_idx ;
@ -92,15 +107,17 @@ typedef struct MP3Context {
static const uint8_t xing_offtbl [ 2 ] [ 2 ] = { { 32 , 17 } , { 17 , 9 } } ;
/* insert a dummy frame containing number of frames */
/*
* Write an empty XING header and initialize respective data .
*/
static void mp3_write_xing ( AVFormatContext * s )
{
MP3Context * mp3 = s - > priv_data ;
AVCodecContext * codec = s - > streams [ mp3 - > audio_stream_idx ] - > codec ;
int bitrate_idx = 1 ; // 32 kbps
int32_t header ;
MPADecodeHeader mpah ;
int srate_idx , i , channels ;
int bitrate_idx ;
int xing_offset ;
int ver = 0 ;
@ -132,6 +149,9 @@ static void mp3_write_xing(AVFormatContext *s)
return ;
}
/* 64 kbps frame, should be large enough */
bitrate_idx = ( ver = = 3 ) ? 5 : 8 ;
/* dummy MPEG audio header */
header = 0xff < < 24 ; // sync
header | = ( 0x7 < < 5 | ver < < 3 | 0x1 < < 1 | 0x1 ) < < 16 ; // sync/audio-version/layer 3/no crc*/
@ -141,17 +161,78 @@ static void mp3_write_xing(AVFormatContext *s)
avpriv_mpegaudio_decode_header ( & mpah , header ) ;
av_assert0 ( mpah . frame_size > = XING_MAX_SIZE ) ;
xing_offset = xing_offtbl [ ver ! = 3 ] [ codec - > channels = = 1 ] ;
ffio_fill ( s - > pb , 0 , xing_offset ) ;
mp3 - > xing_offset = avio_tell ( s - > pb ) ;
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 , 0x01 | 0x02 | 0x04 ) ; // frames / size / TOC
mpah . frame_size - = 4 + xing_offset + 4 + 4 + 4 ;
mp3 - > size = mpah . frame_size ;
mp3 - > want = 1 ;
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 ) ;
mpah . frame_size - = 4 + xing_offset + 4 + 4 + 4 + 4 + XING_TOC_SIZE ;
ffio_fill ( s - > pb , 0 , mpah . frame_size ) ;
}
/*
* Add a frame to XING data .
* Following lame ' s " VbrTag.c " .
*/
static void mp3_xing_add_frame ( MP3Context * mp3 , AVPacket * pkt )
{
int i ;
mp3 - > frames + + ;
mp3 - > seen + + ;
mp3 - > size + = pkt - > size ;
if ( mp3 - > want = = mp3 - > seen ) {
mp3 - > bag [ mp3 - > pos ] = mp3 - > size ;
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 ] ;
/* double wanted amount per bag. */
mp3 - > want * = 2 ;
/* adjust current position to half of table size. */
mp3 - > pos = XING_NUM_BAGS / 2 ;
}
mp3 - > seen = 0 ;
}
}
static int mp3_write_audio_packet ( AVFormatContext * s , AVPacket * pkt )
{
MP3Context * mp3 = s - > priv_data ;
if ( mp3 - > xing_offset & & pkt - > size > = 4 ) {
MPADecodeHeader c ;
avpriv_mpegaudio_decode_header ( & c , AV_RB32 ( pkt - > data ) ) ;
if ( ! mp3 - > initial_bitrate )
mp3 - > initial_bitrate = c . bit_rate ;
if ( ( c . bit_rate = = 0 ) | | ( mp3 - > initial_bitrate ! = c . bit_rate ) )
mp3 - > has_variable_bitrate = 1 ;
mp3_xing_add_frame ( mp3 , pkt ) ;
}
return ff_raw_write_packet ( s , pkt ) ;
}
static int mp3_queue_flush ( AVFormatContext * s )
{
MP3Context * mp3 = s - > priv_data ;
@ -162,7 +243,7 @@ static int mp3_queue_flush(AVFormatContext *s)
mp3_write_xing ( s ) ;
while ( ( pktl = mp3 - > queue ) ) {
if ( write & & ( ret = ff_raw_write _packet( s , & pktl - > pkt ) ) < 0 )
if ( write & & ( ret = mp3_write_audio _packet( s , & pktl - > pkt ) ) < 0 )
write = 0 ;
av_free_packet ( & pktl - > pkt ) ;
mp3 - > queue = pktl - > next ;
@ -172,30 +253,50 @@ static int mp3_queue_flush(AVFormatContext *s)
return ret ;
}
static void mp3_update_xing ( AVFormatContext * s )
{
MP3Context * mp3 = s - > priv_data ;
int i ;
/* replace "Xing" identification string with "Info" for CBR files. */
if ( ! mp3 - > has_variable_bitrate ) {
avio_seek ( s - > pb , mp3 - > xing_offset , SEEK_SET ) ;
ffio_wfourcc ( s - > pb , " Info " ) ;
}
avio_seek ( s - > pb , mp3 - > xing_offset + 8 , 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 < XING_TOC_SIZE ; + + i ) {
int j = i * mp3 - > pos / XING_TOC_SIZE ;
int seek_point = 256LL * mp3 - > bag [ j ] / mp3 - > size ;
avio_w8 ( s - > pb , FFMIN ( seek_point , 255 ) ) ;
}
avio_seek ( s - > pb , 0 , SEEK_END ) ;
}
static int mp3_write_trailer ( struct AVFormatContext * s )
{
uint8_t buf [ ID3v1_TAG_SIZE ] ;
MP3Context * mp3 = s - > priv_data ;
if ( mp3 & & mp3 - > pics_to_write ) {
if ( mp3 - > pics_to_write ) {
av_log ( s , AV_LOG_WARNING , " No packets were sent for some of the "
" attached pictures. \n " ) ;
mp3_queue_flush ( s ) ;
}
/* write the id3v1 tag */
if ( mp3 & & mp3 - > write_id3v1 & & id3v1_create_tag ( s , buf ) > 0 ) {
if ( mp3 - > write_id3v1 & & id3v1_create_tag ( s , buf ) > 0 ) {
avio_write ( s - > pb , buf , ID3v1_TAG_SIZE ) ;
}
/* 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 [ mp3 - > audio_stream_idx ] - > nb_frames ) ;
avio_seek ( s - > pb , 0 , SEEK_END ) ;
}
avio_flush ( s - > pb ) ;
if ( mp3 - > xing_offset )
mp3_update_xing ( s ) ;
return 0 ;
}
@ -209,7 +310,6 @@ AVOutputFormat ff_mp2_muxer = {
. audio_codec = AV_CODEC_ID_MP2 ,
. video_codec = AV_CODEC_ID_NONE ,
. write_packet = ff_raw_write_packet ,
. write_trailer = mp3_write_trailer ,
. flags = AVFMT_NOTIMESTAMPS ,
} ;
# endif
@ -251,7 +351,7 @@ static int mp3_write_packet(AVFormatContext *s, AVPacket *pkt)
mp3 - > queue = pktl ;
mp3 - > queue_end = pktl ;
} else
return ff_raw_write _packet( s , pkt ) ;
return mp3_write_audio _packet( s , pkt ) ;
} else {
int ret ;