changeset 25824:eb338d06c638

Add support for dvdnav still frames playback. Based on various patches from Otvos Attila and MPlayer'ized by me. N.B. Always use -vc ffmpeg12 with dvdnav://
author ben
date Sat, 26 Jan 2008 11:51:34 +0000
parents 60fda6c0c7dc
children 4c17a62383a4
files command.c mp_core.h mplayer.c stream/stream_dvdnav.c stream/stream_dvdnav.h
diffstat 5 files changed, 345 insertions(+), 33 deletions(-) [+]
line wrap: on
line diff
--- a/command.c	Fri Jan 25 19:27:42 2008 +0000
+++ b/command.c	Sat Jan 26 11:51:34 2008 +0000
@@ -3124,12 +3124,8 @@
 		if (mpctx->stream->type != STREAMTYPE_DVDNAV)
 		    break;
 
-		if (mp_dvdnav_handle_input
-		    (mpctx->stream, cmd->args[0].v.i, &button)) {
-		    uninit_player(INITED_ALL - (INITED_STREAM | INITED_INPUT |
-				   (fixed_vo ? INITED_VO : 0)));
-		    brk_cmd = 2;
-		} else if (button > 0)
+		mp_dvdnav_handle_input(mpctx->stream,cmd->args[0].v.i,&button);
+		if (button > 0)
 		    set_osd_msg(OSD_MSG_TEXT, 1, osd_duration,
 				"Selected button number %d", button);
 	    }
--- a/mp_core.h	Fri Jan 25 19:27:42 2008 +0000
+++ b/mp_core.h	Sat Jan 26 11:51:34 2008 +0000
@@ -102,6 +102,13 @@
 #endif
 
     int was_paused;
+
+#ifdef USE_DVDNAV
+    mp_image_t    *nav_smpi;     ///< last decoded dvdnav video image
+    unsigned char *nav_buffer;   ///< last read dvdnav video frame
+    unsigned char *nav_start;    ///< pointer to last read video buffer
+    int            nav_in_size;  ///< last read size
+#endif
 } MPContext;
 
 
--- a/mplayer.c	Fri Jan 25 19:27:42 2008 +0000
+++ b/mplayer.c	Sat Jan 26 11:51:34 2008 +0000
@@ -573,6 +573,17 @@
 /// step size of mixer changes
 int volstep = 3;
 
+#ifdef USE_DVDNAV
+static void mp_dvdnav_context_free(MPContext *ctx){
+    if (ctx->nav_smpi) free_mp_image(ctx->nav_smpi);
+    ctx->nav_smpi = NULL;
+    if (ctx->nav_buffer) free(ctx->nav_buffer);
+    ctx->nav_buffer = NULL;
+    ctx->nav_start = NULL;
+    ctx->nav_in_size = 0;
+}
+#endif
+
 void uninit_player(unsigned int mask){
   mask=inited_flags&mask;
 
@@ -622,6 +633,9 @@
     current_module="uninit_vo";
     mpctx->video_out->uninit();
     mpctx->video_out=NULL;
+#ifdef USE_DVDNAV
+    mp_dvdnav_context_free(mpctx);
+#endif
   }
 
   // Must be after libvo uninit, as few vo drivers (svgalib) have tty code.
@@ -1767,6 +1781,136 @@
     return time_frame;
 }
 
+#ifdef USE_DVDNAV
+#ifndef FF_B_TYPE
+#define FF_B_TYPE 3
+#endif
+/// store decoded video image
+static mp_image_t * mp_dvdnav_copy_mpi(mp_image_t *to_mpi,
+                                       mp_image_t *from_mpi) {
+    mp_image_t *mpi;
+
+    /// Do not store B-frames
+    if (from_mpi->pict_type == FF_B_TYPE)
+        return to_mpi;
+
+    if (to_mpi &&
+        to_mpi->w == from_mpi->w &&
+        to_mpi->h == from_mpi->h &&
+        to_mpi->imgfmt == from_mpi->imgfmt)
+        mpi = to_mpi;
+    else {
+        if (to_mpi)
+            free_mp_image(to_mpi);
+        if (from_mpi->w == 0 || from_mpi->h == 0)
+            return NULL;
+        mpi = alloc_mpi(from_mpi->w,from_mpi->h,from_mpi->imgfmt);
+    }
+
+    copy_mpi(mpi,from_mpi);
+    return mpi;
+}
+
+static void mp_dvdnav_reset_stream (MPContext *ctx) {
+    if (ctx->sh_video) {
+        /// clear video pts
+        ctx->d_video->pts = 0.0f;
+        ctx->sh_video->pts = 0.0f;
+        ctx->sh_video->i_pts = 0.0f;
+        ctx->sh_video->last_pts = 0.0f;
+        ctx->sh_video->num_buffered_pts = 0;
+        ctx->sh_video->num_frames = 0;
+        ctx->sh_video->num_frames_decoded = 0;
+        ctx->sh_video->timer = 0.0f;
+        ctx->sh_video->stream_delay = 0.0f;
+        ctx->sh_video->timer = 0;
+        ctx->demuxer->stream_pts = MP_NOPTS_VALUE;
+    }
+
+    if (ctx->sh_audio) {
+        /// free audio packets and reset
+        ds_free_packs(ctx->d_audio);
+        audio_delay -= ctx->sh_audio->stream_delay;
+        ctx->delay =- audio_delay;
+        ctx->audio_out->reset();
+        resync_audio_stream(ctx->sh_audio);
+    }
+
+    if (ctx->d_sub) dvdsub_id = -2;
+        
+    audio_delay = 0.0f;
+    correct_pts = 0;
+
+    /// clear all EOF related flags
+    ctx->d_video->eof = ctx->d_audio->eof = ctx->stream->eof = 0;
+}
+
+/// Restore last decoded DVDNAV (still frame)
+static mp_image_t *mp_dvdnav_restore_smpi(int *in_size,
+                                          unsigned char **start,
+                                          mp_image_t *decoded_frame)
+{
+    if (mpctx->stream->type != STREAMTYPE_DVDNAV)
+        return decoded_frame;
+
+    /// a change occured in dvdnav stream
+    if (mp_dvdnav_cell_has_changed(mpctx->stream,0)) {
+        mp_dvdnav_read_wait(mpctx->stream, 1, 1);
+        mp_dvdnav_context_free(mpctx);
+        mp_dvdnav_reset_stream(mpctx);
+        mp_dvdnav_read_wait(mpctx->stream, 0, 1);
+        mp_dvdnav_cell_has_changed(mpctx->stream,1);
+    }
+
+    if (*in_size < 0) {
+        float len;
+
+        /// Display still frame, if any
+        if (mpctx->nav_smpi && !mpctx->nav_buffer)
+            decoded_frame = mpctx->nav_smpi;
+
+        /// increment video frame : continue playing after still frame
+        len = demuxer_get_time_length(mpctx->demuxer);
+        if (mpctx->sh_video->pts >= len &&
+            mpctx->sh_video->pts > 0.0 && len > 0.0) {
+            mp_dvdnav_skip_still(mpctx->stream);
+            mp_dvdnav_skip_wait(mpctx->stream);
+        }
+        mpctx->sh_video->pts += 1 / mpctx->sh_video->fps;
+    
+        if (mpctx->nav_buffer) {
+            *start = mpctx->nav_start;
+            *in_size = mpctx->nav_in_size;
+            if (mpctx->nav_start)
+                memcpy(*start,mpctx->nav_buffer,mpctx->nav_in_size);
+        }
+    }
+
+    return decoded_frame;
+}
+
+/// Save last decoded DVDNAV (still frame)
+static void mp_dvdnav_save_smpi(int in_size,
+                                unsigned char *start,
+                                mp_image_t *decoded_frame)
+{
+    if (mpctx->stream->type != STREAMTYPE_DVDNAV)
+        return;
+  
+    if (mpctx->nav_buffer)
+        free(mpctx->nav_buffer);
+
+    mpctx->nav_buffer = malloc(in_size);
+    mpctx->nav_start = start;
+    mpctx->nav_in_size = mpctx->nav_buffer ? in_size : -1;
+    if (mpctx->nav_buffer)
+        memcpy(mpctx->nav_buffer,start,in_size);
+
+    if (decoded_frame && mpctx->nav_smpi != decoded_frame)
+        mpctx->nav_smpi = mp_dvdnav_copy_mpi(mpctx->nav_smpi,decoded_frame);
+}
+#endif /* USE_DVDNAV */
+
 static void adjust_sync_and_print_status(int between_frames, float timing_error)
 {
     current_module="av_sync";
@@ -2079,7 +2223,7 @@
     *blit_frame = 0; // Don't blit if we hit EOF
     if (!correct_pts) {
 	unsigned char* start=NULL;
-	void *decoded_frame;
+	void *decoded_frame = NULL;
 	int drop_frame=0;
 	int in_size;
 
@@ -2087,6 +2231,15 @@
 	frame_time = sh_video->next_frame_time;
 	in_size = video_read_frame(sh_video, &sh_video->next_frame_time,
 				   &start, force_fps);
+#ifdef USE_DVDNAV
+	/// wait, still frame or EOF
+	if (mpctx->stream->type == STREAMTYPE_DVDNAV && in_size < 0) {
+	    if (mp_dvdnav_is_eof(mpctx->stream)) return -1;
+	    if (mpctx->d_video) mpctx->d_video->eof = 0;
+	    if (mpctx->d_audio) mpctx->d_audio->eof = 0;
+	    mpctx->stream->eof = 0;
+	} else
+#endif
 	if (in_size < 0)
 	    return -1;
 	if (in_size > max_framesize)
@@ -2117,8 +2270,17 @@
 	update_teletext(sh_video, mpctx->demuxer, 0);
 	update_osd_msg();
 	current_module = "decode_video";
+#ifdef USE_DVDNAV
+	decoded_frame = mp_dvdnav_restore_smpi(&in_size,&start,decoded_frame);
+	/// still frame has been reached, no need to decode
+	if (in_size > 0 && !decoded_frame)
+#endif
 	decoded_frame = decode_video(sh_video, start, in_size, drop_frame,
 				     sh_video->pts);
+#ifdef USE_DVDNAV
+	/// save back last still frame for future display
+	mp_dvdnav_save_smpi(in_size,start,decoded_frame);
+#endif
 	current_module = "filter_video";
 	*blit_frame = (decoded_frame && filter_video(sh_video, decoded_frame,
 						    sh_video->pts));
@@ -3463,6 +3625,13 @@
     end_at.type = END_AT_NONE;
 }
 
+#ifdef USE_DVDNAV
+mp_dvdnav_context_free(mpctx);
+if (mpctx->stream->type == STREAMTYPE_DVDNAV) {
+    mp_dvdnav_read_wait(mpctx->stream, 0, 1);
+    mp_dvdnav_cell_has_changed(mpctx->stream,1);
+}
+#endif
 
 while(!mpctx->eof){
     float aq_sleep_time=0;
--- a/stream/stream_dvdnav.c	Fri Jan 25 19:27:42 2008 +0000
+++ b/stream/stream_dvdnav.c	Sat Jan 26 11:51:34 2008 +0000
@@ -18,6 +18,18 @@
 #include "m_option.h"
 #include "m_struct.h"
 #include "help_mp.h"
+#include "stream_dvd_common.h"
+
+/* state flags */
+typedef enum {
+  NAV_FLAG_EOF                  = 1 << 0,  /* end of stream has been reached */
+  NAV_FLAG_WAIT                 = 1 << 1,  /* wait event */
+  NAV_FLAG_WAIT_SKIP            = 1 << 2,  /* wait skip disable */
+  NAV_FLAG_CELL_CHANGED         = 1 << 3,  /* cell change event */
+  NAV_FLAG_WAIT_READ_AUTO       = 1 << 4,  /* wait read auto mode */
+  NAV_FLAG_WAIT_READ            = 1 << 5,  /* suspend read from stream */
+  NAV_FLAG_VTS_DOMAIN           = 1 << 6,  /* vts domain */
+} dvdnav_state_t;
 
 typedef struct {
   dvdnav_t *       dvdnav;              /* handle to libdvdnav stuff */
@@ -27,6 +39,8 @@
   int              title;
   unsigned int     spu_clut[16], spu_set;
   dvdnav_highlight_event_t hlev;
+  int              still_length;        /* still frame duration */
+  unsigned int     state;
 } dvdnav_priv_t;
 
 extern char *dvd_device;
@@ -146,6 +160,10 @@
   }
 }
 
+static inline int dvdnav_get_duration (int length) {
+  return (length == 255) ? 0 : length * 1000;
+}
+
 static int dvdnav_stream_read(dvdnav_priv_t * priv, unsigned char *buf, int *len) {
   int event = DVDNAV_NOP;
 
@@ -160,7 +178,14 @@
       case DVDNAV_NAV_PACKET:
         return event;
       case DVDNAV_STILL_FRAME: {
-        dvdnav_still_skip(priv->dvdnav); // don't let dvdnav stall on this image
+        dvdnav_still_event_t *still_event = (dvdnav_still_event_t *) buf;
+        priv->still_length = still_event->length;
+        /* set still frame duration */
+        priv->duration = dvdnav_get_duration (priv->still_length);
+        if (priv->still_length <= 1) {
+          pci_t *pnavpci = dvdnav_get_current_nav_pci (priv->dvdnav);
+          priv->duration = mp_dvdtimetomsec (&pnavpci->pci_gi.e_eltm);
+        }
         break;
       }
       case DVDNAV_HIGHLIGHT: {
@@ -169,8 +194,27 @@
       }
       case DVDNAV_CELL_CHANGE: {
         dvdnav_cell_change_event_t *ev =  (dvdnav_cell_change_event_t*)buf;
+        uint32_t nextstill;
+
+        priv->state &= ~NAV_FLAG_WAIT_SKIP;
         if(ev->pgc_length)
           priv->duration = ev->pgc_length/90;
+
+        if (dvdnav_is_domain_vts(priv->dvdnav))
+          priv->state &= ~NAV_FLAG_VTS_DOMAIN;
+        else
+          priv->state |= NAV_FLAG_VTS_DOMAIN;
+
+        nextstill = dvdnav_get_next_still_flag (priv->dvdnav);
+        if (nextstill) {
+          priv->duration = dvdnav_get_duration (nextstill);
+          priv->still_length = nextstill;
+          if (priv->still_length <= 1) {
+            pci_t *pnavpci = dvdnav_get_current_nav_pci (priv->dvdnav);
+            priv->duration = mp_dvdtimetomsec (&pnavpci->pci_gi.e_eltm);
+          }
+        }
+
         break;
       }
       case DVDNAV_SPU_CLUT_CHANGE: {
@@ -178,9 +222,18 @@
         priv->spu_set = 1;
         break;
       }
-      case DVDNAV_WAIT:
-        dvdnav_wait_skip(priv->dvdnav);
+      case DVDNAV_WAIT: {
+        if ((priv->state & NAV_FLAG_WAIT_SKIP) &&
+            !(priv->state & NAV_FLAG_WAIT))
+          dvdnav_wait_skip (priv->dvdnav);
+        else
+          priv->state |= NAV_FLAG_WAIT;
         break;
+      }
+      case DVDNAV_VTS_CHANGE: {
+        priv->state &= ~NAV_FLAG_WAIT_SKIP;
+        break;
+      }
     }
 
     *len=0;
@@ -237,6 +290,8 @@
     int event;
 
     dvdnav_priv_t* priv=s->priv;
+    if (priv->state & NAV_FLAG_WAIT_READ) /* read is suspended */
+      return -1;
     len=0;
     if(!s->end_pos)
       update_title_len(s);
@@ -249,16 +304,30 @@
         return 0;
       }
       switch (event) {
-        case DVDNAV_STOP:
+        case DVDNAV_STOP: {
+          priv->state |= NAV_FLAG_EOF;
+          return len;
+        }
         case DVDNAV_BLOCK_OK:
         case DVDNAV_NAV_PACKET:
+        case DVDNAV_STILL_FRAME:
           return len;
+        case DVDNAV_WAIT: {
+          if (priv->state & NAV_FLAG_WAIT)
+            return len;
+          break;
+        }
         case DVDNAV_VTS_CHANGE: {
           int tit = 0, part = 0;
           dvdnav_vts_change_event_t *vts_event = (dvdnav_vts_change_event_t *)s->buffer;
           mp_msg(MSGT_CPLAYER,MSGL_INFO, "DVDNAV, switched to title: %d\r\n", vts_event->new_vtsN);
+          priv->state |= NAV_FLAG_CELL_CHANGED;
+          priv->state &= ~NAV_FLAG_WAIT_SKIP;
+          priv->state &= ~NAV_FLAG_WAIT;
           s->end_pos = 0;
           update_title_len(s);
+          if (priv->state & NAV_FLAG_WAIT_READ_AUTO)
+            priv->state |= NAV_FLAG_WAIT_READ;
           if(dvdnav_current_title_info(priv->dvdnav, &tit, &part) == DVDNAV_STATUS_OK) {
             mp_msg(MSGT_CPLAYER,MSGL_V, "\r\nDVDNAV, NEW TITLE %d\r\n", tit);
             dvdnav_get_highlight (priv, 0);
@@ -268,6 +337,11 @@
           break;
         }
         case DVDNAV_CELL_CHANGE: {
+          priv->state |= NAV_FLAG_CELL_CHANGED;
+          priv->state &= ~NAV_FLAG_WAIT_SKIP;
+          priv->state &= ~NAV_FLAG_WAIT;
+          if (priv->state & NAV_FLAG_WAIT_READ_AUTO)
+            priv->state |= NAV_FLAG_WAIT_READ;
           if(priv->title > 0 && dvd_last_chapter > 0) {
             int tit=0, part=0;
             if(dvdnav_current_title_info(priv->dvdnav, &tit, &part) == DVDNAV_STATUS_OK && part > dvd_last_chapter)
@@ -317,7 +391,7 @@
     }
     case STREAM_CTRL_GET_TIME_LENGTH:
     {
-      if(priv->duration)
+      if(priv->duration || priv->still_length)
       {
         *((double *)arg) = (double)priv->duration / 1000.0;
         return 1;
@@ -484,11 +558,9 @@
     }
     if(dvd_chapter > 0)
       dvdnav_part_play(priv->dvdnav, p->track, dvd_chapter);
-  } else if(p->track == -1)
-    dvdnav_menu_call(priv->dvdnav, DVD_MENU_Root);
-  else {
-    mp_msg(MSGT_OPEN,MSGL_INFO,"dvdnav_stream, you didn't specify a track number (as in dvdnav://1), playing whole disc\n");
-    dvdnav_menu_call(priv->dvdnav, DVD_MENU_Title);
+  } else if (p->track == 0) {
+    if(dvdnav_menu_call(priv->dvdnav, DVD_MENU_Root) != DVDNAV_STATUS_OK)
+      dvdnav_menu_call(priv->dvdnav, DVD_MENU_Title);
   }
   if(mp_msg_test(MSGT_IDENTIFY, MSGL_INFO))
     identify(priv, p);
@@ -508,7 +580,7 @@
   *file_format = DEMUXER_TYPE_MPEG_PS;
 
   update_title_len(stream);
-  if(!stream->pos)
+  if(!stream->pos && p->track > 0)
     mp_msg(MSGT_OPEN,MSGL_ERR, "INIT ERROR: couldn't get init pos %s\r\n", dvdnav_err_to_string(priv->dvdnav));
 
   mp_msg(MSGT_OPEN,MSGL_INFO, "Remember to disable MPlayer's cache when playing dvdnav:// streams (adding -nocache to your command line)\r\n");
@@ -517,15 +589,14 @@
 }
 
 
-int mp_dvdnav_handle_input(stream_t *stream, int cmd, int *button) {
+void mp_dvdnav_handle_input(stream_t *stream, int cmd, int *button) {
   dvdnav_priv_t * priv=(dvdnav_priv_t*)stream->priv;
   dvdnav_t *nav = priv->dvdnav;
   dvdnav_status_t status=DVDNAV_STATUS_ERR;
   pci_t *pci = dvdnav_get_current_nav_pci(nav);
-  int reset = 0;
 
   if(cmd != MP_CMD_DVDNAV_SELECT && !pci)
-    return 0;
+    return;
 
   switch(cmd) {
     case MP_CMD_DVDNAV_UP:
@@ -542,26 +613,22 @@
       break;
     case MP_CMD_DVDNAV_MENU:
       status = dvdnav_menu_call(nav,DVD_MENU_Root);
-      reset = 1;
       break;
     case MP_CMD_DVDNAV_PREVMENU: {
       int title=0, part=0;
 
       dvdnav_current_title_info(nav, &title, &part);
       if(title) {
-        if(dvdnav_menu_call(nav, DVD_MENU_Part) == DVDNAV_STATUS_OK
-           || dvdnav_menu_call(nav, DVD_MENU_Title) == DVDNAV_STATUS_OK) {
-          reset = 1;
+        if((status=dvdnav_menu_call(nav, DVD_MENU_Part)) == DVDNAV_STATUS_OK)
           break;
-        }
       }
-      if(dvdnav_menu_call(nav, DVD_MENU_Root) == DVDNAV_STATUS_OK)
-        reset = 1;
+      if((status=dvdnav_menu_call(nav, DVD_MENU_Title)) == DVDNAV_STATUS_OK)
+        break;
+      status=dvdnav_menu_call(nav, DVD_MENU_Root);
       }
       break;
     case MP_CMD_DVDNAV_SELECT:
       status = dvdnav_button_activate(nav, pci);
-      if(status == DVDNAV_STATUS_OK) reset = 1;
       break;
     case MP_CMD_DVDNAV_MOUSECLICK:
       /*
@@ -572,7 +639,6 @@
         This last call always works well
       */
       status = dvdnav_mouse_activate(nav, pci, priv->mousex, priv->mousey);
-      if(status == DVDNAV_STATUS_OK) reset = 1;
       break;
     default:
       mp_msg(MSGT_CPLAYER, MSGL_V, "Unknown DVDNAV cmd %d\n", cmd);
@@ -581,8 +647,6 @@
 
   if(status == DVDNAV_STATUS_OK)
     dvdnav_get_current_highlight(nav, button);
-
-  return reset;
 }
 
 void mp_dvdnav_update_mouse_pos(stream_t *stream, int32_t x, int32_t y, int* button) {
@@ -722,6 +786,7 @@
   dvdnav_priv_t * priv=(dvdnav_priv_t*)stream->priv;
   uint8_t lg, k, n=0;
 
+  if (priv->state & NAV_FLAG_VTS_DOMAIN) return 0;
   for(k=0; k<32; k++) {
     lg = dvdnav_get_spu_logical_stream(priv->dvdnav, k);
     if(lg == 0xff) continue;
@@ -765,6 +830,76 @@
     dvdnav_title_play (priv->dvdnav, title);
 }
 
+/**
+ * \brief Check if end of stream has been reached
+ * \param stream: - stream pointer
+ * \return 1 on really eof
+ */
+int mp_dvdnav_is_eof (stream_t *stream) {
+  return ((dvdnav_priv_t *) stream->priv)->state & NAV_FLAG_EOF;
+}
+
+/**
+ * \brief Skip still frame
+ * \param stream: - stream pointer
+ * \return 0 on success
+ */
+int mp_dvdnav_skip_still (stream_t *stream) {
+  dvdnav_priv_t *priv = stream->priv;
+  if (priv->still_length == 0xff)
+    return 1;
+  dvdnav_still_skip(priv->dvdnav);
+  return 0;
+}
+
+/**
+ * \brief Skip wait event
+ * \param stream: - stream pointer
+ * \return 0 on success
+ */
+int mp_dvdnav_skip_wait (stream_t *stream) {
+  dvdnav_priv_t *priv = stream->priv;
+  if (!(priv->state & NAV_FLAG_WAIT))
+    return 1;
+  priv->state &= ~NAV_FLAG_WAIT;
+  dvdnav_wait_skip(priv->dvdnav);
+  return 0;
+}
+
+/**
+ * \brief Set wait mode
+ * \param stream  : - stream pointer
+ * \param mode    : - if true, then suspend block read
+ * \param automode: - if true, then VTS or cell change set wait mode
+ */
+void mp_dvdnav_read_wait (stream_t *stream, int mode, int automode) {
+  dvdnav_priv_t *priv = stream->priv;
+  if (mode == 0)
+    priv->state &= ~NAV_FLAG_WAIT_READ;
+  if (mode > 0)
+    priv->state |= NAV_FLAG_WAIT_READ;
+  if (automode == 0)
+    priv->state &= ~NAV_FLAG_WAIT_READ_AUTO;
+  if (automode > 0)
+    priv->state |= NAV_FLAG_WAIT_READ_AUTO;
+}
+
+/**
+ * \brief Check if cell has changed
+ * \param stream: - stream pointer
+ * \param clear : - if true, then clear cell change flag
+ * \return 1 if cell has changed
+ */
+int mp_dvdnav_cell_has_changed (stream_t *stream, int clear) {
+  dvdnav_priv_t *priv = stream->priv;
+  if (!(priv->state & NAV_FLAG_CELL_CHANGED))
+    return 0;
+  if (clear)
+    priv->state &= ~NAV_FLAG_CELL_CHANGED;
+  return 1;
+}
+
+
 const stream_info_t stream_info_dvdnav = {
   "DVDNAV stream",
   "null",
--- a/stream/stream_dvdnav.h	Fri Jan 25 19:27:42 2008 +0000
+++ b/stream/stream_dvdnav.h	Sat Jan 26 11:51:34 2008 +0000
@@ -17,10 +17,15 @@
 int dvdnav_lang_from_aid(stream_t *stream, int id, unsigned char *buf);
 int dvdnav_sid_from_lang(stream_t *stream, unsigned char *language);
 int dvdnav_lang_from_sid(stream_t *stream, int sid, unsigned char *buf);
-int mp_dvdnav_handle_input(stream_t *stream, int cmd, int *button);
+void mp_dvdnav_handle_input(stream_t *stream, int cmd, int *button);
 void mp_dvdnav_update_mouse_pos(stream_t *stream, int32_t x, int32_t y, int* button);
 void mp_dvdnav_get_highlight (stream_t *stream, nav_highlight_t *hl);
 unsigned int *mp_dvdnav_get_spu_clut(stream_t *stream);
 void mp_dvdnav_switch_title(stream_t *stream, int title);
+int mp_dvdnav_is_eof (stream_t *stream);
+int mp_dvdnav_skip_still (stream_t *stream);
+int mp_dvdnav_skip_wait (stream_t *stream);
+void mp_dvdnav_read_wait (stream_t *stream, int mode, int automode);
+int mp_dvdnav_cell_has_changed (stream_t *stream, int clear);
 
 #endif /* MPLAYER_STREAM_DVDNAV_H */