Fixed MP3 decoding hanging at the end of the file
parent
1b46139563
commit
27fc345488
|
@ -19,6 +19,7 @@
|
||||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
#include "libavutil/opt.h"
|
#include "libavutil/opt.h"
|
||||||
#include "libavutil/avstring.h"
|
#include "libavutil/avstring.h"
|
||||||
#include "libavutil/intreadwrite.h"
|
#include "libavutil/intreadwrite.h"
|
||||||
|
@ -28,8 +29,11 @@
|
||||||
#include "internal.h"
|
#include "internal.h"
|
||||||
#include "id3v2.h"
|
#include "id3v2.h"
|
||||||
#include "id3v1.h"
|
#include "id3v1.h"
|
||||||
|
#include "apetag.h"
|
||||||
#include "libavcodec/mpegaudiodecheader.h"
|
#include "libavcodec/mpegaudiodecheader.h"
|
||||||
|
|
||||||
|
#define MP3_RESYNC_TOLERANCE_BYTES 65536
|
||||||
|
|
||||||
#define XING_FLAG_FRAMES 0x01
|
#define XING_FLAG_FRAMES 0x01
|
||||||
#define XING_FLAG_SIZE 0x02
|
#define XING_FLAG_SIZE 0x02
|
||||||
#define XING_FLAG_TOC 0x04
|
#define XING_FLAG_TOC 0x04
|
||||||
|
@ -130,6 +134,8 @@ static int mp3_parse_vbr_tags(AVFormatContext *s, AVStream *st, int64_t base)
|
||||||
MPADecodeHeader c;
|
MPADecodeHeader c;
|
||||||
int vbrtag_size = 0;
|
int vbrtag_size = 0;
|
||||||
int is_cbr;
|
int is_cbr;
|
||||||
|
AVDictionaryEntry *de;
|
||||||
|
uint64_t duration = 0;
|
||||||
|
|
||||||
v = avio_rb32(s->pb);
|
v = avio_rb32(s->pb);
|
||||||
if(ff_mpa_check_header(v) < 0)
|
if(ff_mpa_check_header(v) < 0)
|
||||||
|
@ -140,6 +146,9 @@ static int mp3_parse_vbr_tags(AVFormatContext *s, AVStream *st, int64_t base)
|
||||||
if(c.layer != 3)
|
if(c.layer != 3)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
|
mp3->start_pad = 0;
|
||||||
|
mp3->end_pad = 0;
|
||||||
|
|
||||||
spf = c.lsf ? 576 : 1152; /* Samples per frame, layer 3 */
|
spf = c.lsf ? 576 : 1152; /* Samples per frame, layer 3 */
|
||||||
|
|
||||||
/* Check for Xing / Info tag */
|
/* Check for Xing / Info tag */
|
||||||
|
@ -164,7 +173,12 @@ static int mp3_parse_vbr_tags(AVFormatContext *s, AVStream *st, int64_t base)
|
||||||
v= avio_rb24(s->pb);
|
v= avio_rb24(s->pb);
|
||||||
mp3->start_pad = v>>12;
|
mp3->start_pad = v>>12;
|
||||||
mp3-> end_pad = v&4095;
|
mp3-> end_pad = v&4095;
|
||||||
|
if (mp3->end_pad >= 528 + 1) {
|
||||||
|
mp3->end_pad = mp3->end_pad - (528 + 1);
|
||||||
st->skip_samples = mp3->start_pad + 528 + 1;
|
st->skip_samples = mp3->start_pad + 528 + 1;
|
||||||
|
} else {
|
||||||
|
st->skip_samples = mp3->start_pad;
|
||||||
|
}
|
||||||
if (!st->start_time)
|
if (!st->start_time)
|
||||||
st->start_time = av_rescale_q(st->skip_samples,
|
st->start_time = av_rescale_q(st->skip_samples,
|
||||||
(AVRational){1, c.sample_rate},
|
(AVRational){1, c.sample_rate},
|
||||||
|
@ -186,17 +200,57 @@ static int mp3_parse_vbr_tags(AVFormatContext *s, AVStream *st, int64_t base)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!frames && !size)
|
if (!frames)
|
||||||
|
vbrtag_size = 0;
|
||||||
|
|
||||||
|
if (s->metadata && (de = av_dict_get(s->metadata, "iTunSMPB", NULL, 0))) {
|
||||||
|
uint32_t zero, start_pad, end_pad;
|
||||||
|
uint64_t last_eight_frames_offset;
|
||||||
|
if (sscanf(de->value, "%x %x %x %llx %x %llx", &zero, &start_pad, &end_pad, &duration, &zero, &last_eight_frames_offset) < 6) {
|
||||||
|
duration = 0;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
mp3->start_pad = start_pad;
|
||||||
|
mp3->end_pad = end_pad;
|
||||||
|
if (end_pad >= 528 + 1)
|
||||||
|
mp3->end_pad = end_pad - (528 + 1);
|
||||||
|
st->skip_samples = mp3->start_pad + 528 + 1;
|
||||||
|
av_log(s, AV_LOG_DEBUG, "pad %d %d\n", mp3->start_pad, mp3->end_pad);
|
||||||
|
if (s->pb->seekable) {
|
||||||
|
int i;
|
||||||
|
size = last_eight_frames_offset;
|
||||||
|
avio_seek(s->pb, base + vbrtag_size + last_eight_frames_offset, SEEK_SET);
|
||||||
|
for (i = 0; i < 8; ++i) {
|
||||||
|
v = avio_rb32(s->pb);
|
||||||
|
if (ff_mpa_check_header(v) < 0)
|
||||||
|
return -1;
|
||||||
|
if (avpriv_mpegaudio_decode_header(&c, v) != 0)
|
||||||
|
break;
|
||||||
|
size += c.frame_size;
|
||||||
|
avio_skip(s->pb, c.frame_size - 4);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!frames && !size && !duration)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
/* Skip the vbr tag frame */
|
/* Skip the vbr tag frame */
|
||||||
avio_seek(s->pb, base + vbrtag_size, SEEK_SET);
|
avio_seek(s->pb, base + vbrtag_size, SEEK_SET);
|
||||||
|
|
||||||
if(frames)
|
if (duration)
|
||||||
|
st->duration = av_rescale_q(duration, (AVRational){1, c.sample_rate}, st->time_base);
|
||||||
|
else if(frames)
|
||||||
st->duration = av_rescale_q(frames, (AVRational){spf, c.sample_rate},
|
st->duration = av_rescale_q(frames, (AVRational){spf, c.sample_rate},
|
||||||
st->time_base);
|
st->time_base) - av_rescale_q(mp3->end_pad, (AVRational){1, c.sample_rate}, st->time_base);
|
||||||
if (size && frames && !is_cbr)
|
|
||||||
|
if (size) {
|
||||||
|
if (duration)
|
||||||
|
st->codec->bit_rate = av_rescale(size, 8 * c.sample_rate, duration);
|
||||||
|
else if (frames)
|
||||||
st->codec->bit_rate = av_rescale(size, 8 * c.sample_rate, frames * (int64_t)spf);
|
st->codec->bit_rate = av_rescale(size, 8 * c.sample_rate, frames * (int64_t)spf);
|
||||||
|
}
|
||||||
|
|
||||||
mp3->is_cbr = is_cbr;
|
mp3->is_cbr = is_cbr;
|
||||||
mp3->header_filesize = size;
|
mp3->header_filesize = size;
|
||||||
|
@ -231,8 +285,60 @@ static int mp3_read_header(AVFormatContext *s)
|
||||||
if(s->pb->seekable)
|
if(s->pb->seekable)
|
||||||
mp3->filesize = avio_size(s->pb);
|
mp3->filesize = avio_size(s->pb);
|
||||||
|
|
||||||
if (mp3_parse_vbr_tags(s, st, off) < 0)
|
if (mp3_parse_vbr_tags(s, st, off) < 0 && s->pb->seekable)
|
||||||
|
{
|
||||||
|
uint64_t duration = 0;
|
||||||
|
uint8_t buf[8];
|
||||||
|
int sample_rate = 0;
|
||||||
|
int retry_count;
|
||||||
|
/* Time for a full parse! */
|
||||||
|
avio_seek(s->pb, mp3->filesize - 128, SEEK_SET);
|
||||||
|
avio_read(s->pb, buf, 3);
|
||||||
|
if (buf[0] == 'T' && buf[1] == 'A' && buf[2] == 'G')
|
||||||
|
mp3->filesize -= 128;
|
||||||
|
avio_seek(s->pb, mp3->filesize - APE_TAG_FOOTER_BYTES, SEEK_SET);
|
||||||
|
avio_read(s->pb, buf, 8);
|
||||||
|
if (memcmp(buf, APE_TAG_PREAMBLE, 8) == 0)
|
||||||
|
{
|
||||||
|
avio_seek(s->pb, 4, SEEK_CUR);
|
||||||
|
mp3->filesize -= avio_rl32(s->pb) + APE_TAG_FOOTER_BYTES;
|
||||||
|
}
|
||||||
avio_seek(s->pb, off, SEEK_SET);
|
avio_seek(s->pb, off, SEEK_SET);
|
||||||
|
retry_count = MP3_RESYNC_TOLERANCE_BYTES;
|
||||||
|
while (avio_tell(s->pb) < mp3->filesize)
|
||||||
|
{
|
||||||
|
MPADecodeHeader c;
|
||||||
|
uint32_t v, spf;
|
||||||
|
|
||||||
|
v = avio_rb32(s->pb);
|
||||||
|
|
||||||
|
if(ff_mpa_check_header(v) < 0)
|
||||||
|
{
|
||||||
|
if (--retry_count)
|
||||||
|
{
|
||||||
|
avio_seek(s->pb, -3, SEEK_CUR);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
else break;
|
||||||
|
}
|
||||||
|
|
||||||
|
retry_count = MP3_RESYNC_TOLERANCE_BYTES;
|
||||||
|
|
||||||
|
if (avpriv_mpegaudio_decode_header(&c, v) != 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (!sample_rate)
|
||||||
|
sample_rate = c.sample_rate;
|
||||||
|
|
||||||
|
spf = c.lsf ? 576 : 1152; /* Samples per frame, layer 3 */
|
||||||
|
|
||||||
|
duration += spf;
|
||||||
|
|
||||||
|
avio_skip(s->pb, c.frame_size - 4);
|
||||||
|
}
|
||||||
|
avio_seek(s->pb, off, SEEK_SET);
|
||||||
|
st->duration = duration && sample_rate ? av_rescale_q(duration, (AVRational){1, sample_rate}, st->time_base) : 0;
|
||||||
|
}
|
||||||
|
|
||||||
/* the parameters will be extracted from the compressed bitstream */
|
/* the parameters will be extracted from the compressed bitstream */
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -362,7 +468,7 @@ static const AVClass demuxer_class = {
|
||||||
|
|
||||||
AVInputFormat ff_mp3_demuxer = {
|
AVInputFormat ff_mp3_demuxer = {
|
||||||
.name = "mp3",
|
.name = "mp3",
|
||||||
.long_name = "MP2/3 (MPEG audio layer 2/3)",
|
.long_name = NULL_IF_CONFIG_SMALL("MP2/3 (MPEG audio layer 2/3)"),
|
||||||
.read_probe = mp3_read_probe,
|
.read_probe = mp3_read_probe,
|
||||||
.read_header = mp3_read_header,
|
.read_header = mp3_read_header,
|
||||||
.read_packet = mp3_read_packet,
|
.read_packet = mp3_read_packet,
|
||||||
|
|
|
@ -179,6 +179,9 @@ int lockmgr_callback(void ** mutex, enum AVLockOp op)
|
||||||
|
|
||||||
- (int)readAudio:(void *)buf frames:(UInt32)frames
|
- (int)readAudio:(void *)buf frames:(UInt32)frames
|
||||||
{
|
{
|
||||||
|
if ( framesRead >= totalFrames )
|
||||||
|
return 0;
|
||||||
|
|
||||||
int frameSize = channels * (bitsPerSample / 8);
|
int frameSize = channels * (bitsPerSample / 8);
|
||||||
int gotFrame = 0;
|
int gotFrame = 0;
|
||||||
int dataSize = 0;
|
int dataSize = 0;
|
||||||
|
|
Loading…
Reference in New Issue