[MPlayer-dev-eng] [Patch] Even better seeking in ogg ;-)
Michael Behrisch
behrisch at informatik.hu-berlin.de
Fri Feb 27 15:18:14 CET 2004
On Wed, Feb 25, 2004 at 09:47:36PM +0100, Moritz Bunkus wrote:
> Heya,
>
> hmm...
>
> http://www.bunkus.org/bob-short.ogm
> (Upload will be done in 10minutes, it's a 20MB sample, over a minute long)
>
> Without -forceidx seeking works fine, but mplayer can segfault when you
> seek backwards to before the start.
fixed, annoying division by zero if final_granulepos is not set.
I made the range for seeking for final_granulepos more than three times
as big (270K). Is this OK? How far can one go? In the file You gave are
granulepos dists up to 400K. If this is also fine please increase the 270000
further.
>
> With -forceidx seeking does not work at all: mplayer always jumps to the
> start.
>
Your file starts with invalid granulepos. I made the scanner jump over the
first if it is larger than the second. Please try again also with the
File 1 from the last mail. I think it is the same problem with an invalid
granulepos, although I do not know whether the first granulepos may occur
as late as 12s. So maybe there are invalid granulepos also later in ogm-files?
Michael
-------------- next part --------------
--- libmpdemux/demux_ogg.old 2004-02-02 10:58:12.000000000 +0100
+++ libmpdemux/demux_ogg.c 2004-02-26 21:45:12.000000000 +0100
@@ -444,7 +444,8 @@
return 1;
}
-/// Build a table of all syncpoints to make seeking easier
+/// if -forceidx build a table of all syncpoints to make seeking easier
+/// otherwise try to get at least the final_granulepos
void demux_ogg_scan_stream(demuxer_t* demuxer) {
ogg_demuxer_t* ogg_d = demuxer->priv;
stream_t *s = demuxer->stream;
@@ -463,7 +464,8 @@
stream_seek(s,demuxer->movi_start);
}
else {
- stream_seek(s,demuxer->movi_end-20*BLOCK_SIZE);
+ //the 270000 are just a wild guess
+ stream_seek(s,max(demuxer->movi_start,demuxer->movi_end-270000));
}
ogg_sync_reset(sync);
@@ -516,7 +518,12 @@
demux_ogg_read_packet(os,&op,context,&pts,&flags);
if(flags || (os->vorbis && op.granulepos >= 0)) {
if(index_mode == 2) {
+ if(ogg_d->num_syncpoint == 1 && ogg_d->syncpoints[0].granulepos > op.granulepos) {
+ ogg_d->num_syncpoint--;
+ mp_msg(MSGT_DEMUX,MSGL_INFO,"Invalid granulepos %lld at start\n",ogg_d->syncpoints[0].granulepos);
+ } else {
ogg_d->syncpoints = (ogg_syncpoint_t*)realloc(ogg_d->syncpoints,(ogg_d->num_syncpoint+1)*sizeof(ogg_syncpoint_t));
+ }
ogg_d->syncpoints[ogg_d->num_syncpoint].granulepos = op.granulepos;
ogg_d->syncpoints[ogg_d->num_syncpoint].page_pos = (ogg_page_continued(page) && p == 0) ? last_pos : pos;
ogg_d->num_syncpoint++;
@@ -1072,10 +1079,10 @@
sh_audio_t* sh_audio = demuxer->audio->sh;
ogg_packet op;
float rate;
- int i,sp,first;
+ int i,sp,first,precision=1,do_seek=1;
vorbis_info* vi = NULL;
- int64_t gp = 0;
- off_t pos;
+ int64_t gp = 0, old_gp;
+ off_t pos, old_pos;
if(demuxer->video->id >= 0) {
ds = demuxer->video;
@@ -1089,37 +1096,52 @@
os = &ogg_d->subs[ds->id];
oss = &os->stream;
- if(ogg_d->syncpoints) {
+ old_gp = os->lastpos;
+ old_pos = ogg_d->pos;
+
+ //calculate the granulepos to seek to
gp = flags & 1 ? 0 : os->lastpos;
- if(flags & 2)
- gp += ogg_d->syncpoints[ogg_d->num_syncpoint].granulepos * rel_seek_secs;
+ if(flags & 2) {
+ if (ogg_d->final_granulepos > 0)
+ gp += ogg_d->final_granulepos * rel_seek_secs;
else
+ gp += rel_seek_secs * (demuxer->movi_end - demuxer->movi_start) * os->lastpos / ogg_d->pos;
+ } else
gp += rel_seek_secs * rate;
+ if (gp < 0) gp = 0;
+ //calculate the filepos to seek to
+ if(ogg_d->syncpoints) {
for(sp = 0; sp < ogg_d->num_syncpoint ; sp++) {
if(ogg_d->syncpoints[sp].granulepos >= gp) break;
}
- if(sp >= ogg_d->num_syncpoint)
- return;
+ if(sp >= ogg_d->num_syncpoint) return;
+ if (sp > 0 && ogg_d->syncpoints[sp].granulepos - gp > gp - ogg_d->syncpoints[sp-1].granulepos)
+ sp--;
+ if (ogg_d->syncpoints[sp].granulepos == os->lastpos) {
+ if (sp > 0 && gp < os->lastpos) sp--;
+ if (sp < ogg_d->num_syncpoint-1 && gp > os->lastpos) sp++;
+ }
pos = ogg_d->syncpoints[sp].page_pos;
-
+ precision = 0;
} else {
pos = flags & 1 ? 0 : ogg_d->pos;
if(flags & 2)
pos += (demuxer->movi_end - demuxer->movi_start) * rel_seek_secs;
else {
- if (ogg_d->final_granulepos > 0)
+ if (ogg_d->final_granulepos > 0) {
pos += rel_seek_secs * (demuxer->movi_end - demuxer->movi_start) / (ogg_d->final_granulepos / rate);
- else
+ } else if (os->lastpos > 0) {
pos += rel_seek_secs * ogg_d->pos / (os->lastpos / rate);
}
- if (pos < 0)
- pos = 0;
- else if (pos > (demuxer->movi_end - demuxer->movi_start))
- return;
}
+ if (pos < 0) pos = 0;
+ if (pos > (demuxer->movi_end - demuxer->movi_start)) return;
+ } // if(ogg_d->syncpoints)
+ while(1) {
+ if (do_seek) {
stream_seek(demuxer->stream,pos+demuxer->movi_start);
ogg_sync_reset(sync);
for(i = 0 ; i < ogg_d->num_sub ; i++) {
@@ -1128,68 +1150,87 @@
}
ogg_d->pos = pos;
ogg_d->last_size = 0;
-
+ /* we just guess that we reached correct granulepos, in case a
+ subsequent search occurs before we read a valid granulepos */
+ os->lastpos = gp;
first = 1;
- while(1) {
+ do_seek=0;
+ }
int np;
ogg_d->pos += ogg_d->last_size;
ogg_d->last_size = 0;
np = ogg_sync_pageseek(sync,page);
- if(np < 0)
- ogg_d->pos -= np;
+ if(np < 0) ogg_d->pos -= np;
if(np <= 0) { // We need more data
char* buf = ogg_sync_buffer(sync,BLOCK_SIZE);
int len = stream_read(demuxer->stream,buf,BLOCK_SIZE);
if(len == 0 && demuxer->stream->eof) {
- mp_msg(MSGT_DEMUX,MSGL_ERR,"EOF while trying to seek !!!!\n");
+ mp_msg(MSGT_DEMUX,MSGL_ERR,"EOF while trying to seek!\n");
break;
}
ogg_sync_wrote(sync,len);
continue;
}
ogg_d->last_size = np;
- if(ogg_page_serialno(page) != oss->serialno)
- continue;
-
- if(ogg_stream_pagein(oss,page) != 0)
+ if(ogg_page_serialno(page) != oss->serialno || ogg_stream_pagein(oss,page) != 0)
continue;
while(1) {
np = ogg_stream_packetout(oss,&op);
- if(np < 0)
- continue;
- else if(np == 0)
- break;
+ if(np < 0) continue;
+ if(np == 0) break;
if (first) { /* Discard the first packet as it's probably broken,
and we don't have any other means to decide whether it is
complete or not. */
first = 0;
break;
}
-
+ if (op.granulepos >= 0) os->lastpos = op.granulepos;
+ if (precision && op.granulepos >= 0) {
+ //prepare another seek because we are probably not precise enough
+ precision--;
+ if (ogg_d->final_granulepos > 0) {
+ pos = ogg_d->pos + (gp - op.granulepos)
+ * (demuxer->movi_end - demuxer->movi_start) / ogg_d->final_granulepos;
+ } else if (old_gp > 0) {
+ pos = ogg_d->pos + (gp - op.granulepos) * old_pos / old_gp;
+ }
+ if (pos < 0) pos = 0;
+ if (pos < (demuxer->movi_end - demuxer->movi_start)) {
+ do_seek=1;
+ break;
+ }
+ }
+ if (op.granulepos >= 0 && pos > 0 && pos < old_pos
+ && 2 * (old_gp - op.granulepos) < old_gp - gp) {
+ /* prepare another seek because looking for a syncpoint
+ destroyed the backward search */
+ pos = old_pos - 1.5 * (old_pos - pos);
+ if (pos < 0) pos = 0;
+ if (pos < (demuxer->movi_end - demuxer->movi_start)) {
+ do_seek=1;
+ break;
+ }
+ }
/* the detection of keyframes for theora is somewhat a hack: granulepos
for theora packets is `keyframeNumber<<shift | iframeNumber'; `shift'
is *variable*, with its excact value encoded in the theora header.
This code just hopes that it is greater than 9 and that keyframes
distance will never overflow 512. */
- if( (((*op.packet & PACKET_IS_SYNCPOINT) && !os->theora) ||
- os->vorbis || (os->theora && (op.granulepos&0x1FF) == 0)) &&
- (!ogg_d->syncpoints || op.granulepos >= gp) ) {
+ if(!precision && ( ((*op.packet & PACKET_IS_SYNCPOINT) && !os->theora) ||
+ os->vorbis || (os->theora && (op.granulepos&0x1FF) == 0) ) ) {
ogg_sub.lines = 0;
vo_sub = &ogg_sub;
vo_osd_changed(OSDTYPE_SUBTITLE);
clear_sub = -1;
demux_ogg_add_packet(ds,os,&op);
- if(sh_audio)
- resync_audio_stream(sh_audio);
+ if(sh_audio) resync_audio_stream(sh_audio);
return;
}
}
}
-
mp_msg(MSGT_DEMUX,MSGL_ERR,"Can't find the good packet :(\n");
-
}
void demux_close_ogg(demuxer_t* demuxer) {
More information about the MPlayer-dev-eng
mailing list