mp3dec: detect CBR and use CBR axiom to seek
authorMichael Niedermayer <michaelni@gmx.at>
Sun, 7 Jul 2013 11:15:32 +0000 (13:15 +0200)
committerMichael Niedermayer <michaelni@gmx.at>
Mon, 8 Jul 2013 15:12:51 +0000 (17:12 +0200)
This should also work reasonable with truncated and growing mp3s.
Fixes Ticket2590

Signed-off-by: Michael Niedermayer <michaelni@gmx.at>
libavformat/mp3dec.c

index d2f35d0..c9973c5 100644 (file)
 typedef struct {
     AVClass *class;
     int64_t filesize;
+    int64_t header_filesize;
     int xing_toc;
     int start_pad;
     int end_pad;
     int usetoc;
+    int is_cbr;
 } MP3DecContext;
 
 /* mp3 read */
@@ -190,6 +192,9 @@ static int mp3_parse_vbr_tags(AVFormatContext *s, AVStream *st, int64_t base)
     if (size && frames && !is_cbr)
         st->codec->bit_rate = av_rescale(size, 8 * c.sample_rate, frames * (int64_t)spf);
 
+    mp3->is_cbr          = is_cbr;
+    mp3->header_filesize = size;
+
     return 0;
 }
 
@@ -279,21 +284,33 @@ static int mp3_seek(AVFormatContext *s, int stream_index, int64_t timestamp,
                     int flags)
 {
     MP3DecContext *mp3 = s->priv_data;
-    AVIndexEntry *ie;
+    AVIndexEntry *ie, ie1;
     AVStream *st = s->streams[0];
     int64_t ret  = av_index_search_timestamp(st, timestamp, flags);
     int i, j;
 
-    if (!mp3->xing_toc) {
+    if (mp3->is_cbr && st->duration > 0 && mp3->header_filesize > s->data_offset) {
+        int64_t filesize = avio_size(s->pb);
+        int64_t duration;
+        if (filesize <= s->data_offset)
+            filesize = mp3->header_filesize;
+        filesize -= s->data_offset;
+        duration = av_rescale(st->duration, filesize, mp3->header_filesize - s->data_offset);
+        ie = &ie1;
+        timestamp = av_clip64(timestamp, 0, duration);
+        ie->timestamp = timestamp;
+        ie->pos       = av_rescale(timestamp, filesize, duration) + s->data_offset;
+    } else if (mp3->xing_toc) {
+        if (ret < 0)
+            return ret;
+
+        ie = &st->index_entries[ret];
+    } else {
         st->skip_samples = timestamp <= 0 ? mp3->start_pad + 528 + 1 : 0;
 
         return -1;
     }
 
-    if (ret < 0)
-        return ret;
-
-    ie = &st->index_entries[ret];
     ret = avio_seek(s->pb, ie->pos, SEEK_SET);
     if (ret < 0)
         return ret;