[MPlayer-dev-eng] [Patch] Even better seeking in ogg ;-)
Michael Behrisch
behrisch at informatik.hu-berlin.de
Tue Mar 9 21:37:36 CET 2004
Hello,
a new version of my patch is on the horizon,
hopefully it converges to something stable :-)
I did some changes to ogg_read_packet I'm not quite
sure of. Please watch them carefully.
Michael
P.S: I think with this patch it should also be easier to
fix the theora hack in seeking, but I dont have theora (and
dont want to try)
-------------- next part --------------
--- libmpdemux/demux_ogg.old 2004-02-02 10:58:12.000000000 +0100
+++ libmpdemux/demux_ogg.c 2004-03-09 21:13:38.000000000 +0100
@@ -370,13 +370,16 @@
data = pack->packet;
#endif /* HAVE_FLAC */
} else {
+ if(*pack->packet & PACKET_TYPE_HEADER)
+ os->hdr_packets++;
+ else {
// Find data start
int16_t hdrlen = (*pack->packet & PACKET_LEN_BITS01)>>6;
hdrlen |= (*pack->packet & PACKET_LEN_BITS2) <<1;
data = pack->packet + 1 + hdrlen;
// Calculate the timestamp
if(pack->granulepos == -1)
- pack->granulepos = os->lastpos + os->lastsize;
+ pack->granulepos = os->lastpos + (os->lastsize ? os->lastsize : 1);
// If we alredy have a timestamp it can be a syncpoint
if(*pack->packet & PACKET_IS_SYNCPOINT)
*flags = 1;
@@ -390,6 +393,7 @@
}
os->lastpos = pack->granulepos;
}
+ }
return data;
}
@@ -444,7 +448,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;
@@ -461,9 +466,9 @@
// Reset the stream
if(index_mode == 2) {
stream_seek(s,demuxer->movi_start);
- }
- else {
- stream_seek(s,demuxer->movi_end-20*BLOCK_SIZE);
+ } else {
+ //the 270000 are just a wild guess
+ stream_seek(s,max(ogg_d->pos,demuxer->movi_end-270000));
}
ogg_sync_reset(sync);
@@ -1072,16 +1077,23 @@
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;
+ void *context = NULL;
+ off_t pos, old_pos;
if(demuxer->video->id >= 0) {
ds = demuxer->video;
+ /* demux_ogg_read_packet needs decoder context for Theora streams */
+ if (((sh_video_t*)demuxer->video->sh)->format == 0xFFFC)
+ context = ((sh_video_t*)demuxer->video->sh)->context;
rate = ogg_d->subs[ds->id].samplerate;
} else {
ds = demuxer->audio;
+ /* demux_ogg_read_packet needs decoder context for Vorbis streams */
+ if(((sh_audio_t*)demuxer->audio->sh)->format == 0xFFFE)
+ context = ((sh_audio_t*)demuxer->audio->sh)->context;
vi = &((ov_struct_t*)((sh_audio_t*)ds->sh)->context)->vi;
rate = (float)vi->rate;
}
@@ -1089,37 +1101,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 +1155,92 @@
}
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;
}
-
+ float pts;
+ int pflags;
+ demux_ogg_read_packet(os,&op,context,&pts,&pflags);
+ if (precision && op.granulepos >= 0) {
+ precision--;
+ if (abs(gp - op.granulepos) > rate) {
+ //prepare another seek because we are off by more than 1s
+ 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) ) &&
+ (gp < old_gp || os->lastpos >= gp)) {
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