diff options
author | Luca Barbato <lu_zero@gentoo.org> | 2015-07-11 17:38:26 +0200 |
---|---|---|
committer | Luca Barbato <lu_zero@gentoo.org> | 2015-07-11 18:45:45 +0200 |
commit | c1d647b15afa69fa70f999a9ddcb98346409fb4d (patch) | |
tree | 61661361644cc2753119f160d4cf8dd1d12fff88 /libavformat/mp3dec.c | |
parent | d80811c94e068085aab797f9ba35790529126f85 (diff) | |
download | ffmpeg-c1d647b15afa69fa70f999a9ddcb98346409fb4d.tar.gz |
mp3: Make the seek more robust
Try to parse up to 4 packets to find the closest packet.
Reported-By: jan.schlueter@ofai.at
Diffstat (limited to 'libavformat/mp3dec.c')
-rw-r--r-- | libavformat/mp3dec.c | 76 |
1 files changed, 65 insertions, 11 deletions
diff --git a/libavformat/mp3dec.c b/libavformat/mp3dec.c index cba67783f9..72fa60b330 100644 --- a/libavformat/mp3dec.c +++ b/libavformat/mp3dec.c @@ -366,6 +366,67 @@ static int mp3_read_packet(AVFormatContext *s, AVPacket *pkt) return ret; } +#define SEEK_PACKETS 4 +#define SEEK_WINDOW (SEEK_PACKETS * MP3_PACKET_SIZE) + +/* The toc entry can position to the wrong byte offset, try to pick + * the closest frame by probing the data in a window of 4 packets. + */ + +static int check(AVIOContext *pb, int64_t pos, int64_t *out_pos) +{ + MPADecodeHeader mh = { 0 }; + int i; + uint32_t header; + int64_t off = 0; + + + for (i = 0; i < SEEK_PACKETS; i++) { + off = avio_seek(pb, pos + mh.frame_size, SEEK_SET); + if (off < 0) + break; + + header = avio_rb32(pb); + + if (ff_mpa_check_header(header) < 0 || + avpriv_mpegaudio_decode_header(&mh, header)) + break; + out_pos[i] = off; + } + + return i; +} + +static int reposition(AVFormatContext *s, int64_t pos) +{ + int ret, best_valid = -1; + int64_t p, best_pos = -1; + + for (p = FFMAX(pos - SEEK_WINDOW / 2, 0); p < pos + SEEK_WINDOW / 2; p++) { + int64_t out_pos[SEEK_PACKETS]; + ret = check(s->pb, p, out_pos); + + if (best_valid < ret) { + int i; + for (i = 0; i < ret; i++) { + if (llabs(best_pos - pos) > llabs(out_pos[i] - pos)) { + best_pos = out_pos[i]; + best_valid = ret; + } + } + if (best_pos == pos && best_valid == SEEK_PACKETS) + break; + } + } + + if (best_valid <= 0) + return AVERROR(ENOSYS); + + avio_seek(s->pb, best_pos, SEEK_SET); + + return 0; +} + static int mp3_seek(AVFormatContext *s, int stream_index, int64_t timestamp, int flags) { @@ -373,7 +434,6 @@ static int mp3_seek(AVFormatContext *s, int stream_index, int64_t timestamp, AVIndexEntry *ie; AVStream *st = s->streams[0]; int64_t ret = av_index_search_timestamp(st, timestamp, flags); - uint32_t header = 0; if (!mp3->xing_toc) return AVERROR(ENOSYS); @@ -382,20 +442,14 @@ static int mp3_seek(AVFormatContext *s, int stream_index, int64_t timestamp, return ret; ie = &st->index_entries[ret]; - ret = avio_seek(s->pb, ie->pos, SEEK_SET); + + ret = reposition(s, ie->pos); if (ret < 0) return ret; - while (!s->pb->eof_reached) { - header = (header << 8) + avio_r8(s->pb); - if (ff_mpa_check_header(header) >= 0) { - ff_update_cur_dts(s, st, ie->timestamp); - ret = avio_seek(s->pb, -4, SEEK_CUR); - return (ret >= 0) ? 0 : ret; - } - } + ff_update_cur_dts(s, st, ie->timestamp); - return AVERROR_EOF; + return 0; } AVInputFormat ff_mp3_demuxer = { |