changeset 13738:39004f891def

seeking based on the largest timestamp in an mpeg stream It is often more accurate than the current seeking and it has the additional benefit of giving the (almost) precise total time of the movie. patch by Michael Behrisch < behrisch at informatik.hu-berlin.de >
author aurel
date Sat, 23 Oct 2004 00:05:38 +0000
parents dc0536401cca
children 09b6ce70be8e
files libmpdemux/demux_mpg.c libmpdemux/demuxer.c
diffstat 2 files changed, 101 insertions(+), 3 deletions(-) [+]
line wrap: on
line diff
--- a/libmpdemux/demux_mpg.c	Fri Oct 22 17:23:34 2004 +0000
+++ b/libmpdemux/demux_mpg.c	Sat Oct 23 00:05:38 2004 +0000
@@ -17,8 +17,58 @@
 //#define MAX_PS_PACKETSIZE 2048
 #define MAX_PS_PACKETSIZE (224*1024)
 
+typedef struct mpg_demuxer {
+  float last_pts;
+  float final_pts;
+  int has_valid_timestamps;
+} mpg_demuxer_t;
+
 static int mpeg_pts_error=0;
 
+/// Open an mpg physical stream
+int demux_mpg_open(demuxer_t* demuxer) {
+  stream_t *s = demuxer->stream;
+  off_t pos = stream_tell(s);
+  off_t end_seq_start = demuxer->movi_end-500000; // 500000 is a wild guess
+  float half_pts = 0.0;
+  mpg_demuxer_t* mpg_d;
+
+  if (!ds_fill_buffer(demuxer->video)) return 0;
+  mpg_d = (mpg_demuxer_t*)calloc(1,sizeof(mpg_demuxer_t));
+  demuxer->priv = mpg_d;
+  mpg_d->final_pts = 0.0;
+  mpg_d->has_valid_timestamps = 1;
+  if (demuxer->seekable && stream_tell(demuxer->stream) < end_seq_start) {
+    stream_seek(s,(pos + end_seq_start / 2));
+    while ((!s->eof) && ds_fill_buffer(demuxer->video) && half_pts == 0.0) {
+      half_pts = mpg_d->last_pts;
+    }
+    stream_seek(s,end_seq_start);
+    while ((!s->eof) && ds_fill_buffer(demuxer->video)) {
+      if (mpg_d->final_pts < mpg_d->last_pts) mpg_d->final_pts = mpg_d->last_pts;
+    }
+    // educated guess about validity of timestamps
+    if (mpg_d->final_pts > 3 * half_pts || mpg_d->final_pts < 1.5 * half_pts) {
+      mpg_d->has_valid_timestamps = 0;
+    }
+    ds_free_packs(demuxer->audio);
+    ds_free_packs(demuxer->video);
+    demuxer->stream->eof=0; // clear eof flag
+    demuxer->video->eof=0;
+    demuxer->audio->eof=0;
+    
+    stream_seek(s,pos);
+    ds_fill_buffer(demuxer->video);
+  }
+  return 1;
+}
+
+void demux_close_mpg(demuxer_t* demuxer) {
+  mpg_demuxer_t* mpg_d = demuxer->priv;
+  if (mpg_d) free(mpg_d);
+}
+
+
 static unsigned int read_mpeg_timestamp(stream_t *s,int c){
   int d,e;
   unsigned int pts;
@@ -222,6 +272,7 @@
     mp_dbg(MSGT_DEMUX,MSGL_DBG2,"DEMUX_MPG: Read %d data bytes from packet %04X\n",len,id);
 //    printf("packet start = 0x%X  \n",stream_tell(demux->stream)-packet_start_pos);
     ds_read_packet(ds,demux->stream,len,pts/90000.0f,demux->filepos,0);
+    if (demux->priv) ((mpg_demuxer_t*)demux->priv)->last_pts = pts/90000.0f;
 //    if(ds==demux->sub) parse_dvdsub(ds->last->buffer,ds->last->len);
     return 1;
   }
@@ -357,21 +408,41 @@
     demux_stream_t *d_video=demuxer->video;
     sh_audio_t *sh_audio=d_audio->sh;
     sh_video_t *sh_video=d_video->sh;
+    mpg_demuxer_t *mpg_d=(mpg_demuxer_t*)demuxer->priv;
+    int precision = 1;
+    float oldpts = mpg_d->last_pts;
+    off_t oldpos = demuxer->filepos;
+    float newpts = (flags & 1) ? 0.0 : oldpts;
+    off_t newpos = (flags & 1) ? demuxer->movi_start : oldpos;
 
   //================= seek in MPEG ==========================
-    off_t newpos=(flags&1)?demuxer->movi_start:demuxer->filepos;
+  //calculate the pts to seek to
+    if(flags & 2) {
+      if (mpg_d->final_pts > 0.0)
+        newpts += mpg_d->final_pts * rel_seek_secs;
+      else
+        newpts += rel_seek_secs * (demuxer->movi_end - demuxer->movi_start) * oldpts / oldpos;
+    } else
+      newpts += rel_seek_secs;
+    if (newpts < 0) newpts = 0;
 	
     if(flags&2){
 	// float seek 0..1
 	newpos+=(demuxer->movi_end-demuxer->movi_start)*rel_seek_secs;
     } else {
 	// time seek (secs)
-        if(!sh_video || !sh_video->i_bps) // unspecified or VBR
+        if (mpg_d && mpg_d->has_valid_timestamps) {
+          if (mpg_d->final_pts > 0.0)
+            newpos += rel_seek_secs * (demuxer->movi_end - demuxer->movi_start) / mpg_d->final_pts;
+          else if (oldpts > 0.0)
+            newpos += rel_seek_secs * (oldpos - demuxer->movi_start) / oldpts;
+        } else if(!sh_video || !sh_video->i_bps) // unspecified or VBR
           newpos+=2324*75*rel_seek_secs; // 174.3 kbyte/sec
         else
           newpos+=sh_video->i_bps*rel_seek_secs;
     }
 
+    while (1) {
         if(newpos<demuxer->movi_start){
 	    if(demuxer->stream->type!=STREAMTYPE_VCD) demuxer->movi_start=0; // for VCD
 	    if(newpos<demuxer->movi_start) newpos=demuxer->movi_start;
@@ -407,6 +478,20 @@
           if(i==0x1B3 || i==0x1B8) break; // found it!
           if(!i || !skip_video_packet(d_video)) break; // EOF?
         }
+        if (!precision || abs(newpts - mpg_d->last_pts) < 0.5 || (mpg_d->last_pts == oldpts)) break;
+        if ((newpos - oldpos) * (mpg_d->last_pts - oldpts) < 0) { // invalid timestamps
+          mpg_d->has_valid_timestamps = 0;
+          break;
+        }
+        precision--;
+        //prepare another seek because we are off by more than 0.5s
+        newpos += (newpts - mpg_d->last_pts) * (newpos - oldpos) / (mpg_d->last_pts - oldpts);
+        ds_free_packs(d_audio);
+        ds_free_packs(d_video);
+        demuxer->stream->eof=0; // clear eof flag
+        d_video->eof=0;
+        d_audio->eof=0;
+    }
 }
 
 int demux_mpg_control(demuxer_t *demuxer,int cmd, void *arg){
@@ -414,9 +499,14 @@
     demux_stream_t *d_video=demuxer->video;
 /*    sh_audio_t *sh_audio=d_audio->sh;*/
     sh_video_t *sh_video=d_video->sh;
+    mpg_demuxer_t *mpg_d=(mpg_demuxer_t*)demuxer->priv;
 
     switch(cmd) {
 	case DEMUXER_CTRL_GET_TIME_LENGTH:
+            if (mpg_d && mpg_d->has_valid_timestamps) {
+              *((unsigned long *)arg)=(long)mpg_d->final_pts;
+              return DEMUXER_CTRL_GUESS;
+            }
 	    if(!sh_video || !sh_video->i_bps)  // unspecified or VBR 
     		return DEMUXER_CTRL_DONTKNOW;
 	    *((unsigned long *)arg)=(demuxer->movi_end-demuxer->movi_start)/sh_video->i_bps;
@@ -425,6 +515,10 @@
 	case DEMUXER_CTRL_GET_PERCENT_POS:
 	    if (demuxer->movi_end==demuxer->movi_start) 
     		return DEMUXER_CTRL_DONTKNOW;
+            if (mpg_d && mpg_d->has_valid_timestamps && mpg_d->final_pts > 0.0) {
+              *((int *)arg)=(int)(100 * mpg_d->last_pts / mpg_d->final_pts);
+              return DEMUXER_CTRL_OK;
+            }
     	    *((int *)arg)=(int)((demuxer->filepos-demuxer->movi_start)/((demuxer->movi_end-demuxer->movi_start)/100));
 	    return DEMUXER_CTRL_OK;
 
--- a/libmpdemux/demuxer.c	Fri Oct 22 17:23:34 2004 +0000
+++ b/libmpdemux/demuxer.c	Sat Oct 23 00:05:38 2004 +0000
@@ -134,6 +134,7 @@
 extern void demux_close_nuv(demuxer_t* demuxer);
 extern void demux_close_audio(demuxer_t* demuxer);
 extern void demux_close_ogg(demuxer_t* demuxer);
+extern void demux_close_mpg(demuxer_t* demuxer);
 extern void demux_close_rtp(demuxer_t* demuxer);
 extern void demux_close_demuxers(demuxer_t* demuxer);
 extern void demux_close_avi(demuxer_t *demuxer);
@@ -232,6 +233,8 @@
     case DEMUXER_TYPE_MPEG_TS:
     case DEMUXER_TYPE_MPEG4_IN_TS:
       demux_close_ts(demuxer); break;
+    case DEMUXER_TYPE_MPEG_PS:
+      demux_close_mpg(demuxer); break;
     case DEMUXER_TYPE_REALAUDIO:
       demux_close_ra(demuxer); break;
 #ifdef USE_LIBAVFORMAT
@@ -616,6 +619,7 @@
 extern void demux_open_nuv(demuxer_t *demuxer);
 extern int demux_audio_open(demuxer_t* demuxer);
 extern int demux_ogg_open(demuxer_t* demuxer);
+extern int demux_mpg_open(demuxer_t* demuxer);
 extern int demux_rawaudio_open(demuxer_t* demuxer);
 extern int demux_rawvideo_open(demuxer_t* demuxer);
 extern int smjpeg_check_file(demuxer_t *demuxer);
@@ -990,7 +994,7 @@
   num_h264_pps=0;
   num_mp3audio_packets=0;
 
-  if(ds_fill_buffer(demuxer->video)){
+  if(demux_mpg_open(demuxer)){
     if(!pes)
       mp_msg(MSGT_DEMUXER,MSGL_INFO,MSGTR_Detected_XXX_FileFormat,"MPEG-PES");
     else