[MPlayer-dev-eng] [PATCH] Timestamp-based mpg seeking

Michael Behrisch behrisch at informatik.hu-berlin.de
Fri Oct 15 14:24:56 CEST 2004


On Wed, Oct 13, 2004 at 02:22:55PM +0200, Aurelien Jacobs wrote:
> On Tue, 12 Oct 2004 13:06:17 +0200
> Michael Behrisch <behrisch at informatik.hu-berlin.de> wrote:
> 
> > 
> > This patch will reduce the number of "-ss does not work"
> > and "dvd length is wrong" messages by more than 80% ;-)!
> > It enables indeed precise seeking and retrieves the total
> > time of the movie almost always correctly (and it can also 
> > walk your dog).
> 
> :-)
> 
> I've tested it with some mpeg files and DVDs. It works very well !
> I think this is a real improvement, and something like this should
> be commited.
> 
> About the code:
> 
> +/// Open an mpg physical stream
> +int demux_mpg_open(demuxer_t* demuxer) {
> +  off_t pos = stream_tell(demuxer->stream);
> +  if (!ds_fill_buffer(demuxer->video)) return 0;
> +  mpg_demuxer_t* mpg_d = (mpg_demuxer_t*)calloc(1,size...
> 
> This is generally a bad idea to declare variables in the middle
> of a function. gcc-2.95 don't accept this.

There are a lot of positions in the code where this happens
(maybe only at beginnings of blocks?) Nevertheless fixed.

All cosmetics fixed as well.

Michael
-------------- next part --------------
diff -u libmpdemux.old/demuxer.c libmpdemux/demuxer.c
--- libmpdemux.old/demuxer.c	2004-10-12 12:58:51.000000000 +0200
+++ libmpdemux/demuxer.c	2004-10-14 22:18:01.061965160 +0200
@@ -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
diff -u libmpdemux.old/demux_mpg.c libmpdemux/demux_mpg.c
--- libmpdemux.old/demux_mpg.c	2004-09-24 12:41:20.000000000 +0200
+++ libmpdemux/demux_mpg.c	2004-10-14 22:44:38.743080712 +0200
@@ -17,8 +17,57 @@
 //#define MAX_PS_PACKETSIZE 2048
 #define MAX_PS_PACKETSIZE (224*1024)
 
+typedef struct mpg_demuxer {
+  float last_pts;
+  float final_pts;
+} 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);
+  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;
+  // 500000 is a wild guess
+  off_t end_seq_start = demuxer->movi_end-500000;
+  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->final_pts = 0.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 +271,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,8 +407,23 @@
     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;
 
   //================= seek in MPEG ==========================
+  //calculate the pts to seek to
+    float newpts = flags & 1 ? 0.0 : oldpts;
+    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;
+
     off_t newpos=(flags&1)?demuxer->movi_start:demuxer->filepos;
 	
     if(flags&2){
@@ -366,12 +431,17 @@
 	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->final_pts > 0.0)
+          newpos += rel_seek_secs * (demuxer->movi_end - demuxer->movi_start) / mpg_d->final_pts;
+        else if (mpg_d && mpg_d->last_pts > 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 +477,16 @@
           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;
+        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 +494,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->final_pts > 0.0) {
+              *((unsigned long *)arg)=(long)mpg_d->final_pts;
+              return DEMUXER_CTRL_GUESS;
+            }
 	    if(!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 +510,10 @@
 	case DEMUXER_CTRL_GET_PERCENT_POS:
 	    if (demuxer->movi_end==demuxer->movi_start) 
     		return DEMUXER_CTRL_DONTKNOW;
+            if (mpg_d && 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;
 


More information about the MPlayer-dev-eng mailing list