Merge commit 'c1d647b15afa69fa70f999a9ddcb98346409fb4d'
authorMichael Niedermayer <michael@niedermayer.cc>
Sun, 12 Jul 2015 13:55:17 +0000 (15:55 +0200)
committerMichael Niedermayer <michael@niedermayer.cc>
Sun, 12 Jul 2015 14:01:31 +0000 (16:01 +0200)
* commit 'c1d647b15afa69fa70f999a9ddcb98346409fb4d':
  mp3: Make the seek more robust

Conflicts:
libavformat/mp3dec.c

Mostly not merged

See: b6267901c466c482b2f1af3578b0a6d88265d144 and various later commits

The bug is also not reproducable in FFmpeg

Merged-by: Michael Niedermayer <michael@niedermayer.cc>
1  2 
libavformat/mp3dec.c

@@@ -54,8 -48,6 +54,8 @@@ typedef struct 
      int is_cbr;
  } MP3DecContext;
  
- static int check(AVFormatContext *s, int64_t pos);
++static int check(AVIOContext *pb, int64_t pos);
 +
  /* mp3 read */
  
  static int mp3_read_probe(AVProbeData *p)
@@@ -372,22 -340,6 +372,22 @@@ static int mp3_read_header(AVFormatCont
      if (ret < 0)
          return ret;
  
-         if (check(s, off + i) >= 0) {
 +    off = avio_tell(s->pb);
 +    for (i = 0; i < 64 * 1024; i++) {
 +        if (!(i&1023))
 +            ffio_ensure_seekback(s->pb, i + 1024 + 4);
++        if (check(s->pb, off + i) >= 0) {
 +            av_log(s, AV_LOG_INFO, "Skipping %d bytes of junk at %"PRId64".\n", i, off);
 +            avio_seek(s->pb, off + i, SEEK_SET);
 +            break;
 +        }
 +        avio_seek(s->pb, off, SEEK_SET);
 +    }
 +
 +    // the seek index is relative to the end of the xing vbr headers
 +    for (i = 0; i < st->nb_index_entries; i++)
 +        st->index_entries[i].pos += avio_tell(s->pb);
 +
      /* the parameters will be extracted from the compressed bitstream */
      return 0;
  }
@@@ -418,89 -359,91 +418,93 @@@ static int mp3_read_packet(AVFormatCont
      return ret;
  }
  
- static int check(AVFormatContext *s, int64_t pos)
 -#define SEEK_PACKETS 4
 -#define SEEK_WINDOW (SEEK_PACKETS * MP3_PACKET_SIZE)
++#define SEEK_WINDOW 4096
 -/* 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)
++static int check(AVIOContext *pb, int64_t pos)
  {
-     int64_t ret = avio_seek(s->pb, pos, SEEK_SET);
 -    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);
++    int64_t ret = avio_seek(pb, pos, SEEK_SET);
 +    unsigned header;
 +    MPADecodeHeader sd;
 +    if (ret < 0)
 +        return ret;
-     header = avio_rb32(s->pb);
 -    avio_seek(s->pb, best_pos, SEEK_SET);
++    header = avio_rb32(pb);
 +    if (ff_mpa_check_header(header) < 0)
 +        return -1;
 +    if (avpriv_mpegaudio_decode_header(&sd, header) == 1)
 +        return -1;
 -    return 0;
 +    return sd.frame_size;
  }
  
  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;
 +    int dir = (flags&AVSEEK_FLAG_BACKWARD) ? -1 : 1;
 +    int64_t best_pos;
 +    int best_score;
 +
 +    if (mp3->usetoc == 2)
 +        return -1; // generic index code
 +
 +    if (   mp3->is_cbr
 +        && (mp3->usetoc == 0 || !mp3->xing_toc)
 +        && st->duration > 0
 +        && mp3->header_filesize > s->internal->data_offset
 +        && mp3->frames) {
 +        ie = &ie1;
 +        timestamp = av_clip64(timestamp, 0, st->duration);
 +        ie->timestamp = timestamp;
 +        ie->pos       = av_rescale(timestamp, mp3->header_filesize, st->duration) + s->internal->data_offset;
 +    } else if (mp3->xing_toc) {
 +        if (ret < 0)
 +            return ret;
 +
 +        ie = &st->index_entries[ret];
 +    } else {
 +        return -1;
 +    }
  
-     avio_seek(s->pb, FFMAX(ie->pos - 4096, 0), SEEK_SET);
 -    if (!mp3->xing_toc)
 -        return AVERROR(ENOSYS);
 -
++    avio_seek(s->pb, FFMAX(ie->pos - SEEK_WINDOW, 0), SEEK_SET);
 +    ret = avio_seek(s->pb, ie->pos, SEEK_SET);
      if (ret < 0)
          return ret;
  
 -    ie = &st->index_entries[ret];
 +#define MIN_VALID 3
 +    best_pos = ie->pos;
 +    best_score = 999;
-     for(i=0; i<4096; i++) {
-         int64_t pos = ie->pos + (dir > 0 ? i - 1024 : -i);
++    for(i=0; i<SEEK_WINDOW; i++) {
++        int64_t pos = ie->pos + (dir > 0 ? i - SEEK_WINDOW/4 : -i);
 +        int64_t candidate = -1;
 +        int score = 999;
 +
 +        if (pos < 0)
 +            continue;
 +
 +        for(j=0; j<MIN_VALID; j++) {
-             ret = check(s, pos);
++            ret = check(s->pb, pos);
 +            if(ret < 0)
 +                break;
 +            if ((ie->pos - pos)*dir <= 0 && abs(MIN_VALID/2-j) < score) {
 +                candidate = pos;
 +                score = abs(MIN_VALID/2-j);
 +            }
 +            pos += ret;
 +        }
 +        if (best_score > score && j == MIN_VALID) {
 +            best_pos = candidate;
 +            best_score = score;
 +            if(score == 0)
 +                break;
 +        }
 +    }
  
 -    ret = reposition(s, ie->pos);
 +    ret = avio_seek(s->pb, best_pos, SEEK_SET);
      if (ret < 0)
          return ret;