From b8c3a20f5e3d8b51a2528afdb129d3fac03ad9fd Mon Sep 17 00:00:00 2001 From: mplayer-svn Date: Thu, 27 Oct 2011 20:09:09 +0000 Subject: [PATCH] demux_audo: fix wrong runtime and average bitrate for VBR MP3 Do so by determining the number of frames from the Xing/Info/VBRI headers and by calculation a correct duration which leads to an reasonable average bytes per seconds. git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@34273 b3059339-0415-0410-9bf9-f77b7e298cf2 Author: ib --- libmpdemux/demux_audio.c | 68 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 67 insertions(+), 1 deletion(-) diff --git a/libmpdemux/demux_audio.c b/libmpdemux/demux_audio.c index e58bb55aae..d3720d2e24 100644 --- a/libmpdemux/demux_audio.c +++ b/libmpdemux/demux_audio.c @@ -257,6 +257,69 @@ get_flac_metadata (demuxer_t* demuxer) } #endif +/** + * @brief Determine the number of frames of a file encoded with + * variable bitrate mode (VBR). + * + * @param s stream to be read + * @param off offset in stream to start reading from + * + * @return 0 (error or no variable bitrate mode) or number of frames + */ +static unsigned int mp3_vbr_frames(stream_t *s, off_t off) { + static const int xing_offset[2][2] = {{32, 17}, {17, 9}}; + unsigned int data; + unsigned char hdr[4]; + int framesize, chans, spf, layer; + + if ((s->flags & MP_STREAM_SEEK) == MP_STREAM_SEEK) { + + if (!stream_seek(s, off)) return 0; + + data = stream_read_dword(s); + hdr[0] = data >> 24; + hdr[1] = data >> 16; + hdr[2] = data >> 8; + hdr[3] = data; + + if (!mp_check_mp3_header(data)) return 0; + + framesize = mp_get_mp3_header(hdr, &chans, NULL, &spf, &layer, NULL); + + if (framesize == -1 || layer != 3) return 0; + + /* Xing / Info (at variable position: 32, 17 or 9 bytes after header) */ + + if (!stream_skip(s, xing_offset[spf < 1152][chans == 1])) return 0; + + data = stream_read_dword(s); + + if (data == MKBETAG('X','i','n','g') || data == MKBETAG('I','n','f','o')) { + data = stream_read_dword(s); + + if (data & 0x1) // frames field is present + return stream_read_dword(s); // frames + } + + /* VBRI (at fixed position: 32 bytes after header) */ + + if (!stream_seek(s, off + 4 + 32)) return 0; + + data = stream_read_dword(s); + + if (data == MKBETAG('V','B','R','I')) { + data = stream_read_word(s); + + if (data == 1) { // check version + if (!stream_skip(s, 8)) return 0; // skip delay, quality and bytes + return stream_read_dword(s); // frames + } + } + } + + return 0; +} + static int demux_audio_open(demuxer_t* demuxer) { stream_t *s; sh_audio_t* sh_audio; @@ -266,6 +329,7 @@ static int demux_audio_open(demuxer_t* demuxer) { // mp3_hdrs list is sorted first by next_frame_pos and then by frame_pos mp3_hdr_t *mp3_hdrs = NULL, *mp3_found = NULL; da_priv_t* priv; + double duration; s = demuxer->stream; @@ -342,7 +406,7 @@ static int demux_audio_open(demuxer_t* demuxer) { sh_audio->wf->nBlockAlign = mp3_found->mpa_spf; sh_audio->wf->wBitsPerSample = 16; sh_audio->wf->cbSize = 0; - sh_audio->i_bps = sh_audio->wf->nAvgBytesPerSec; + duration = (double) mp3_vbr_frames(s, demuxer->movi_start) * mp3_found->mpa_spf / mp3_found->mp3_freq; free(mp3_found); mp3_found = NULL; if(s->end_pos && (s->flags & MP_STREAM_SEEK) == MP_STREAM_SEEK) { @@ -378,6 +442,8 @@ static int demux_audio_open(demuxer_t* demuxer) { demux_info_add(demuxer,"Genre",genres[g]); } } + if (duration && demuxer->movi_end && demuxer->movi_end > demuxer->movi_start) sh_audio->wf->nAvgBytesPerSec = (demuxer->movi_end - demuxer->movi_start) / duration; + sh_audio->i_bps = sh_audio->wf->nAvgBytesPerSec; break; case WAV: { unsigned int chunk_type;