avformat/mp3dec: prefer "fast_seek" to TOC seek for CBR files.

"Fast seek" uses linear interpolation to find the position of the
requested seek time. For CBR this is more direct than using the
mp3 TOC and bypassing the TOC avoids problems with TOC precision.
(see https://crbug.com/545914#c13)

For VBR, fast seek is not precise, so continue to prefer the TOC
when available (the lesser of two evils).

Also, some re-ordering of the logic in mp3_seek to simplify and
give usetoc=1 precedence over fastseek flag.

Signed-off-by: wm4 <nfxjfg@googlemail.com>
This commit is contained in:
Chris Cunningham 2015-12-01 10:54:38 -08:00 committed by wm4
parent e1057babdf
commit 5e6ce28dab
3 changed files with 26 additions and 24 deletions

View File

@ -115,7 +115,8 @@ static void read_xing_toc(AVFormatContext *s, int64_t filesize, int64_t duration
{ {
int i; int i;
MP3DecContext *mp3 = s->priv_data; MP3DecContext *mp3 = s->priv_data;
int fill_index = mp3->usetoc == 1 && duration > 0; int fast_seek = s->flags & AVFMT_FLAG_FAST_SEEK;
int fill_index = (mp3->usetoc || fast_seek) && duration > 0;
if (!filesize && if (!filesize &&
!(filesize = avio_size(s->pb))) { !(filesize = avio_size(s->pb))) {
@ -344,9 +345,6 @@ static int mp3_read_header(AVFormatContext *s)
int ret; int ret;
int i; int i;
if (mp3->usetoc < 0)
mp3->usetoc = (s->flags & AVFMT_FLAG_FAST_SEEK) ? 1 : 2;
st = avformat_new_stream(s, NULL); st = avformat_new_stream(s, NULL);
if (!st) if (!st)
return AVERROR(ENOMEM); return AVERROR(ENOMEM);
@ -501,35 +499,37 @@ static int mp3_seek(AVFormatContext *s, int stream_index, int64_t timestamp,
MP3DecContext *mp3 = s->priv_data; MP3DecContext *mp3 = s->priv_data;
AVIndexEntry *ie, ie1; AVIndexEntry *ie, ie1;
AVStream *st = s->streams[0]; AVStream *st = s->streams[0];
int64_t ret = av_index_search_timestamp(st, timestamp, flags);
int64_t best_pos; int64_t best_pos;
int fast_seek = (s->flags & AVFMT_FLAG_FAST_SEEK) ? 1 : 0; int fast_seek = s->flags & AVFMT_FLAG_FAST_SEEK;
int64_t filesize = mp3->header_filesize; int64_t filesize = mp3->header_filesize;
if (mp3->usetoc == 2)
return -1; // generic index code
if (filesize <= 0) { if (filesize <= 0) {
int64_t size = avio_size(s->pb); int64_t size = avio_size(s->pb);
if (size > 0 && size > s->internal->data_offset) if (size > 0 && size > s->internal->data_offset)
filesize = size - s->internal->data_offset; filesize = size - s->internal->data_offset;
} }
if ( (mp3->is_cbr || fast_seek) if (mp3->xing_toc && (mp3->usetoc || (fast_seek && !mp3->is_cbr))) {
&& (mp3->usetoc == 0 || !mp3->xing_toc) int64_t ret = av_index_search_timestamp(st, timestamp, flags);
&& st->duration > 0
&& filesize > 0) { // NOTE: The MP3 TOC is not a precise lookup table. Accuracy is worse
ie = &ie1; // for bigger files.
timestamp = av_clip64(timestamp, 0, st->duration); av_log(s, AV_LOG_WARNING, "Using MP3 TOC to seek; may be imprecise.\n");
ie->timestamp = timestamp;
ie->pos = av_rescale(timestamp, filesize, st->duration) + s->internal->data_offset;
} else if (mp3->xing_toc) {
if (ret < 0) if (ret < 0)
return ret; return ret;
ie = &st->index_entries[ret]; ie = &st->index_entries[ret];
} else if (fast_seek && st->duration > 0 && filesize > 0) {
if (!mp3->is_cbr)
av_log(s, AV_LOG_WARNING, "Using scaling to seek VBR MP3; may be imprecise.\n");
ie = &ie1;
timestamp = av_clip64(timestamp, 0, st->duration);
ie->timestamp = timestamp;
ie->pos = av_rescale(timestamp, filesize, st->duration) + s->internal->data_offset;
} else { } else {
return -1; return -1; // generic index code
} }
best_pos = mp3_sync(s, ie->pos, flags); best_pos = mp3_sync(s, ie->pos, flags);
@ -546,7 +546,7 @@ static int mp3_seek(AVFormatContext *s, int stream_index, int64_t timestamp,
} }
static const AVOption options[] = { static const AVOption options[] = {
{ "usetoc", "use table of contents", offsetof(MP3DecContext, usetoc), AV_OPT_TYPE_INT, {.i64 = -1}, -1, 2, AV_OPT_FLAG_DECODING_PARAM}, { "usetoc", "use table of contents", offsetof(MP3DecContext, usetoc), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 1, AV_OPT_FLAG_DECODING_PARAM},
{ NULL }, { NULL },
}; };

View File

@ -56,7 +56,7 @@ static void ts_str(char buffer[60], int64_t ts, AVRational base)
int main(int argc, char **argv) int main(int argc, char **argv)
{ {
const char *filename; const char *filename;
AVFormatContext *ic = NULL; AVFormatContext *ic = avformat_alloc_context();
int i, ret, stream_id; int i, ret, stream_id;
int j; int j;
int64_t timestamp; int64_t timestamp;
@ -76,8 +76,10 @@ int main(int argc, char **argv)
frame_count = atoi(argv[i+1]); frame_count = atoi(argv[i+1]);
} else if(!strcmp(argv[i], "-duration")){ } else if(!strcmp(argv[i], "-duration")){
duration = atoi(argv[i+1]); duration = atoi(argv[i+1]);
} else if(!strcmp(argv[i], "-usetoc")) { } else if(!strcmp(argv[i], "-fastseek")) {
av_dict_set(&format_opts, "usetoc", argv[i+1], 0); if (atoi(argv[i+1])) {
ic->flags |= AVFMT_FLAG_FAST_SEEK;
}
} else { } else {
argc = 1; argc = 1;
} }

View File

@ -244,7 +244,7 @@ FATE_SEEK += $(FATE_SEEK_LAVF-yes:%=fate-seek-lavf-%)
# extra files # extra files
FATE_SEEK_EXTRA-$(CONFIG_MP3_DEMUXER) += fate-seek-extra-mp3 FATE_SEEK_EXTRA-$(CONFIG_MP3_DEMUXER) += fate-seek-extra-mp3
fate-seek-extra-mp3: CMD = run libavformat/seek-test$(EXESUF) $(TARGET_SAMPLES)/gapless/gapless.mp3 -usetoc 0 fate-seek-extra-mp3: CMD = run libavformat/seek-test$(EXESUF) $(TARGET_SAMPLES)/gapless/gapless.mp3 -fastseek 1
FATE_SEEK_EXTRA += $(FATE_SEEK_EXTRA-yes) FATE_SEEK_EXTRA += $(FATE_SEEK_EXTRA-yes)