summaryrefslogtreecommitdiff
path: root/libavformat/mp3dec.c
diff options
context:
space:
mode:
authorLuca Barbato <lu_zero@gentoo.org>2015-07-11 17:38:26 +0200
committerLuca Barbato <lu_zero@gentoo.org>2015-07-11 18:45:45 +0200
commitc1d647b15afa69fa70f999a9ddcb98346409fb4d (patch)
tree61661361644cc2753119f160d4cf8dd1d12fff88 /libavformat/mp3dec.c
parentd80811c94e068085aab797f9ba35790529126f85 (diff)
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.c76
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 = {