core: fix SEEK_FACTOR

Emulate percentage-seeks (SEEK_FACTOR) as normal time-seeks if possible.
This fixes some issues with (let's call it) low quality implementations
of SEEK_FACTOR (e.g. demux_mkv basically interprets this as byte-seek,
and also seeking to 99.9% makes it seek back to the start).

For weird MPEG formats the demuxer level SEEK_FACTOR is still used.
These formats, which can have timestamp resets, are identified by
setting demuxer->ts_resets_possible to true.

Also, have get_current_pos_ratio() follow the same rules, and calculate
the percentage position with the file position if timestamp resets are
possible.

This actually fixes percentage-seeks in .ts files with demux_lavf.c.
This kind of seek is not really used now, but it will be more important
when we add a progress bar.

Note: seeking in chained ogg files is still completely broken. The main
issue is that ffmpeg doesn't provide a sane API for dealing with
timestamp resets, and trying to do byte seeks with ogg confuses demuxer
and decoder (or something like this) and just does random things.
(Tested with two concatenated flac-in-ogg files).
This commit is contained in:
wm4 2013-03-01 13:20:33 +01:00
parent c82a83d8af
commit 8ddfabc535
6 changed files with 39 additions and 10 deletions

View File

@ -280,6 +280,7 @@ void queue_seek(struct MPContext *mpctx, enum seek_type type, double amount,
int exact);
int seek_chapter(struct MPContext *mpctx, int chapter, double *seek_pts);
double get_time_length(struct MPContext *mpctx);
double get_start_time(struct MPContext *mpctx);
double get_current_time(struct MPContext *mpctx);
int get_percent_pos(struct MPContext *mpctx);
double get_current_pos_ratio(struct MPContext *mpctx);

View File

@ -2769,9 +2769,12 @@ static int seek(MPContext *mpctx, struct seek_params seek,
&& seek.amount < mpctx->last_chapter_pts
|| seek.amount < 0)
mpctx->last_chapter_seek = -2;
if (mpctx->timeline && seek.type == MPSEEK_FACTOR) {
seek.amount *= mpctx->timeline[mpctx->num_timeline_parts].start;
seek.type = MPSEEK_ABSOLUTE;
if (seek.type == MPSEEK_FACTOR) {
double len = get_time_length(mpctx);
if (len > 0 && !mpctx->demuxer->ts_resets_possible) {
seek.amount = seek.amount * len + get_start_time(mpctx);
seek.type = MPSEEK_ABSOLUTE;
}
}
if ((mpctx->demuxer->accurate_seek || mpctx->timeline)
&& seek.type == MPSEEK_RELATIVE) {
@ -2807,9 +2810,11 @@ static int seek(MPContext *mpctx, struct seek_params seek,
int demuxer_style = 0;
switch (seek.type) {
case MPSEEK_FACTOR:
demuxer_style |= SEEK_FACTOR; // fallthrough
demuxer_style |= SEEK_ABSOLUTE | SEEK_FACTOR;
break;
case MPSEEK_ABSOLUTE:
demuxer_style |= SEEK_ABSOLUTE;
break;
}
if (hr_seek || seek.direction < 0)
demuxer_style |= SEEK_BACKWARD;
@ -2970,7 +2975,7 @@ double get_current_time(struct MPContext *mpctx)
return 0;
}
static double get_start_time(struct MPContext *mpctx)
double get_start_time(struct MPContext *mpctx)
{
struct demuxer *demuxer = mpctx->demuxer;
if (!demuxer)
@ -2990,7 +2995,7 @@ double get_current_pos_ratio(struct MPContext *mpctx)
double start = get_start_time(mpctx);
double len = get_time_length(mpctx);
double pos = get_current_time(mpctx);
if (len > 0) {
if (len > 0 && !demuxer->ts_resets_possible) {
ans = av_clipf((pos - start) / len, 0, 1);
} else {
int len = (demuxer->movi_end - demuxer->movi_start);

View File

@ -234,6 +234,8 @@ typedef struct demuxer {
/* Set if using absolute seeks for small movements is OK (no pts resets
* that would make pts ambigious, preferably supports back/forward flags */
bool accurate_seek;
// File format allows PTS resets (even if the current file is without)
bool ts_resets_possible;
enum timestamp_type timestamp_type;
bool warned_queue_overflow;

View File

@ -585,6 +585,8 @@ static demuxer_t *demux_open_lavf(demuxer_t *demuxer)
demuxer->video->id = -2; // audio-only / sub-only
}
demuxer->ts_resets_possible = priv->avif->flags & AVFMT_TS_DISCONT;
// disabled because unreliable per-stream bitrate values returned
// by libavformat trigger this heuristic incorrectly and break things
#if 0
@ -713,16 +715,27 @@ static void demux_seek_lavf(demuxer_t *demuxer, float rel_seek_secs,
priv->last_pts = 0;
else if (rel_seek_secs < 0)
avsflags = AVSEEK_FLAG_BACKWARD;
if (flags & SEEK_FORWARD)
avsflags = 0;
else if (flags & SEEK_BACKWARD)
avsflags = AVSEEK_FLAG_BACKWARD;
if (flags & SEEK_FACTOR) {
if (priv->avfc->duration == 0 || priv->avfc->duration == AV_NOPTS_VALUE)
return;
priv->last_pts += rel_seek_secs * priv->avfc->duration;
} else
if (demuxer->movi_end > 0 && demuxer->ts_resets_possible &&
!(priv->avif->flags & AVFMT_NO_BYTE_SEEK))
{
avsflags |= AVSEEK_FLAG_BYTE;
priv->last_pts = (demuxer->movi_end - demuxer->movi_start) *
rel_seek_secs;
} else if (priv->avfc->duration != 0 &&
priv->avfc->duration != AV_NOPTS_VALUE)
{
priv->last_pts = rel_seek_secs * priv->avfc->duration;
}
} else {
priv->last_pts += rel_seek_secs * AV_TIME_BASE;
}
if (!priv->avfc->iformat->read_seek2) {
// Normal seeking.

View File

@ -1134,6 +1134,8 @@ static demuxer_t* demux_mpg_es_open(demuxer_t* demuxer)
demuxer->video->sh = new_sh_video(demuxer,0); // create dummy video stream header, id=0
sh_video=demuxer->video->sh;sh_video->ds=demuxer->video;
demuxer->ts_resets_possible = true;
return demuxer;
}
@ -1142,6 +1144,9 @@ static demuxer_t *demux_mpg_gxf_open(demuxer_t *demuxer) {
demuxer->video->sh = new_sh_video(demuxer,0);
((sh_video_t *)demuxer->video->sh)->ds = demuxer->video;
demuxer->priv = (void *) 0xffffffff;
demuxer->ts_resets_possible = true;
return demuxer;
}
@ -1152,6 +1157,8 @@ static demuxer_t* demux_mpg_ps_open(demuxer_t* demuxer)
sh_video=demuxer->video->sh;sh_video->ds=demuxer->video;
demuxer->ts_resets_possible = true;
if(demuxer->audio->id!=-2) {
if(!ds_fill_buffer(demuxer->audio)){
mp_msg(MSGT_DEMUXER, MSGL_INFO, "MPEG: %s",

View File

@ -990,6 +990,7 @@ static demuxer_t *demux_open_ts(demuxer_t * demuxer)
demuxer->type= DEMUXER_TYPE_MPEG_TS;
demuxer->ts_resets_possible = true;
stream_reset(demuxer->stream);