Index: NuppelVideoPlayer.h
===================================================================
--- NuppelVideoPlayer.h	(revision 8623)
+++ NuppelVideoPlayer.h	(working copy)
@@ -255,6 +255,7 @@
 
     // DVD public stuff
     void ChangeDVDTrack(bool ffw);
+    void ActivateDVDButton(void);
 
   protected:
     void DisplayPauseFrame(void);
@@ -358,6 +359,9 @@
     void  SwitchToProgram(void);
     void  JumpToProgram(void);
 
+   // Private DVD stuff
+   void DisplayDVDButton(void);
+
   private:
     VideoOutputType forceVideoOutput;
 
@@ -568,6 +572,9 @@
     LiveTVChain *livetvchain;
     TV *m_tv;
 
+    //DVD
+    bool indvdstillframe;
+
     // Debugging variables
     Jitterometer *output_jmeter;
 };
Index: NuppelVideoPlayer.cpp
===================================================================
--- NuppelVideoPlayer.cpp	(revision 8623)
+++ NuppelVideoPlayer.cpp	(working copy)
@@ -163,6 +163,8 @@
       tc_avcheck_framecounter(0),   tc_diff_estimate(0),
       // LiveTVChain stuff
       livetvchain(NULL), m_tv(NULL),
+      // DVD stuff
+      indvdstillframe(false),
       // Debugging variables
       output_jmeter(NULL)
 {
@@ -783,7 +785,8 @@
             deleteIter = deleteMap.begin();
         }
     }
-    bookmarkseek = GetBookmark();
+    if (!ringBuffer->isDVD()) // need this til proper DVD bookmarking is implemented
+      bookmarkseek = GetBookmark();
 
     return 0;
 }
@@ -1685,6 +1688,9 @@
         return;
     }
 
+    if (ringBuffer->IsInDVDMenu())
+        DisplayDVDButton();
+
     videofiltersLock.lock();
     videoOutput->ProcessFrame(NULL, osd, videoFilters, pipplayer);
     videofiltersLock.unlock();
@@ -1699,6 +1705,9 @@
     video_actually_paused = false;
     resetvideo = false;
 
+   if (ringBuffer->IsDVDWaiting() || ringBuffer->InCellWithDVDStillFrame())
+      goto displayframe;
+
     prebuffering_lock.lock();
     if (prebuffering)
     {
@@ -1760,10 +1769,15 @@
     prebuffer_tries = 0;
     prebuffering_lock.unlock();
 
+displayframe:
+
     videoOutput->StartDisplayingFrame();
 
     VideoFrame *frame = videoOutput->GetLastShownFrame();
 
+    if (ringBuffer->IsInDVDMenu())
+        DisplayDVDButton();
+
     if (subtitlesOn)
     {
         ShowText();
@@ -1856,6 +1870,25 @@
             needsetpipplayer = false;
         }
 
+        if (ringBuffer->isDVD())
+        {
+           int nbframes = videoOutput->ValidVideoFrames();
+           if (ringBuffer->InCellWithDVDStillFrame() && 
+                        nbframes == 1)
+           {
+               indvdstillframe = true;
+               if (!pausevideo)
+                   PauseVideo(false);
+           }
+           if (ringBuffer->IsDVDWaiting() && nbframes < 2)
+               ringBuffer->DVDWaitSkip();
+           // restart playing after skipping still frame
+           if (indvdstillframe && nbframes > 1)
+          {
+               UnpauseVideo();
+               indvdstillframe = false;
+          } 
+        }
         if (pausevideo)
         {
             usleep(frame_interval);
@@ -1980,7 +2013,8 @@
 {
     ClearAfterSeek();
 
-    framesPlayed = 0;
+    if (!ringBuffer->isDVD())
+       framesPlayed = 0;
 
     GetDecoder()->Reset();
     errored |= GetDecoder()->IsErrored();
@@ -4337,7 +4371,7 @@
     posInfo.progBefore = false;
     posInfo.progAfter = false;
 
-    if (ringBuffer->isDVD())
+    if (ringBuffer->IsInDVDMenu())
     {
         long long rPos = ringBuffer->GetReadPosition();
         long long tPos = 1;//ringBuffer->GetTotalReadPosition();
@@ -4362,7 +4396,14 @@
         playbackLen =
             (int)(((float)nvr_enc->GetFramesWritten() / video_frame_rate));
 
-    float secsplayed = ((float)framesPlayed / video_frame_rate);
+    float secsplayed;
+    if (ringBuffer->isDVD())
+    {
+       if (!ringBuffer->IsInDVDMenu())
+           secsplayed = ringBuffer->GetDVDCurrentTime();
+    }
+    else
+       secsplayed = ((float)framesPlayed / video_frame_rate);
     playbackLen = max(playbackLen, 1);
     secsplayed  = min((float)playbackLen, max(secsplayed, 0.0f));
 
@@ -4907,6 +4948,57 @@
 
     GetDecoder()->ChangeDVDTrack(ffw);
     usleep(100000);
-    ClearAfterSeek();
+    ResetPlaying();
 }
 
+void NuppelVideoPlayer::DisplayDVDButton(void)
+{
+   if (!ringBuffer->IsInDVDMenu())
+       return;
+
+   AVSubtitleRect *highlightButton;
+   OSDSet *subtitleOSD = NULL;
+   highlightButton = ringBuffer->GetDVDMenuButton();
+
+   subtitleLock.lock();
+   if (highlightButton != NULL)
+  {
+      osd->HideSet("subtitles");
+      osd->ClearAll("subtitles");
+       int h = highlightButton->h;
+       int w = highlightButton->w;
+       int linesize = highlightButton->linesize;
+       int x1 = highlightButton->x;
+       int y1 = highlightButton->y;
+       subtitleOSD = osd->GetSet("subtitles");
+
+       QImage hl_button(w,h,32);
+       hl_button.setAlphaBuffer(true);
+       for (int y = 0; y < h; y++)
+       {
+           for (int x = 0; x < w; x++)
+           {
+               const uint8_t color = highlightButton->bitmap[(y+y1)*linesize+(x+x1)];
+               const uint32_t pixel = highlightButton->rgba_palette[color];
+               hl_button.setPixel(x,y,pixel);
+           }
+       }
+       OSDTypeImage* image = new OSDTypeImage();
+       image->SetPosition(QPoint(x1, y1));
+       image->LoadFromQImage(hl_button);
+       subtitleOSD->AddType(image);
+       osd->SetVisible(subtitleOSD,0);
+   }
+   subtitleLock.unlock();
+}
+
+void NuppelVideoPlayer::ActivateDVDButton(void)
+{
+    if (!ringBuffer->isDVD())
+       return;
+    
+    ringBuffer->ActivateDVDButton();
+    usleep(10000);
+    osd->HideSet("subtitles");
+    osd->ClearAll("subtitles");
+}
Index: RingBuffer.cpp
===================================================================
--- RingBuffer.cpp	(revision 8623)
+++ RingBuffer.cpp	(working copy)
@@ -1185,3 +1185,209 @@
     return 0;
 }
 
+/** \fn RingBuffer::InCellWithDVDStillFrame(void)
+ *  \brief Calls DVDRingBufferPriv::InCellWithDVDStillFrame(void)
+ */
+bool RingBuffer::InCellWithDVDStillFrame(void)
+{
+    if (dvdPriv)
+        return dvdPriv->InCellWithDVDStillFrame();
+    return false;
+}
+
+/** \fn RingBuffer::DVDSkipStillFrame(void)
+ *  \brief Calls DVDRingBufferPriv::DVDSkipStillFrame(void)
+ */
+void RingBuffer::DVDSkipStillFrame(void)
+{
+    if (dvdPriv)
+        dvdPriv->SkipStillFrame();
+}
+
+/** \fn RingBuffer::DVDWaitSkip(void)
+ *  \brief Calls DVDRingBufferPriv::DVDWaitSkip(void)
+ */
+void RingBuffer::DVDWaitSkip(void)
+{
+   if (dvdPriv)
+       dvdPriv->DVDWaitSkip();
+}
+
+/** \fn RingBuffer::IsDVDWaiting(void)
+ *  \brief Calls DVDRingBufferPriv::IsDVDWaiting(void)
+ */
+bool RingBuffer::IsDVDWaiting(void)
+{
+   if (dvdPriv)
+       return dvdPriv->IsDVDWaiting();
+   return false;
+}
+
+/** \fn RingBuffer::IsInDVDMenu(void)
+ *  \brief Calls DVDRingBufferPriv::IsInDVDMenu(void)
+ */
+bool RingBuffer::IsInDVDMenu(void)
+{
+   if (dvdPriv)
+       return dvdPriv->IsInMenu();
+   return false;
+}
+
+/** \fn RingBuffer::GoToDVDRootMenu(void)
+ *  \brief Calls DVDRingBufferPriv::GoToDVDRootMenu(void)
+ */
+void RingBuffer::GoToDVDRootMenu(void)
+{
+   if (dvdPriv)
+       dvdPriv->GoToRootMenu();
+}
+
+/** \fn RingBuffer::GoToDVDNextProgram(void)
+ *  \brief Calls DVDRingBufferPriv::GoToNextProgram(void)
+ */
+void RingBuffer::GoToDVDNextProgram(void)
+{
+   if (dvdPriv)
+       dvdPriv->GoToNextProgram();
+}
+
+/** \fn RingBuffer::GoToDVDPreviousProgram(void)
+ *  \brief Calls DVDRingBufferPriv::GoToPreviousProgram(void)
+ */
+void RingBuffer::GoToDVDPreviousProgram(void)
+{
+   if (dvdPriv)
+       dvdPriv->GoToPreviousProgram();
+}
+
+/** \fn RingBuffer::MoveDVDButtonLeft(void)
+ *  \brief Calls DVDRingBufferPriv::MoveDVDButtonLeft(void)
+ */
+void RingBuffer::MoveDVDButtonLeft(void)
+{
+   if (dvdPriv)
+       dvdPriv->MoveDVDButtonLeft();
+}
+
+/** \fn RingBuffer::MoveDVDButtonRight(void)
+ *  \brief Calls DVDRingBufferPriv::MoveDVDButtonRight(void)
+ */
+void RingBuffer::MoveDVDButtonRight(void)
+{
+   if (dvdPriv)
+       dvdPriv->MoveDVDButtonRight();
+}
+
+/** \fn RingBuffer::MoveDVDButtonUp(void)
+ *  \brief Calls DVDRingBufferPriv::MoveDVDButtonUp(void)
+ */
+void RingBuffer::MoveDVDButtonUp(void)
+{
+   if (dvdPriv)
+       dvdPriv->MoveDVDButtonUp();
+}
+
+/** \fn RingBuffer::MoveDVDButtonDown(void)
+ *  \brief Calls DVDRingBufferPriv::MoveDVDButtonDown(void)
+ */
+void RingBuffer::MoveDVDButtonDown(void)
+{
+   if (dvdPriv)
+       dvdPriv->MoveDVDButtonDown();
+}
+
+/** \fn RingBuffer::ActivateDVDButton(void)
+ *  \brief Calls DVDRingBufferPriv::ActivateDVDButton(void)
+ */
+void RingBuffer::ActivateDVDButton(void)
+{
+   if (dvdPriv)
+       dvdPriv->ActivateDVDButton();
+}
+
+/** \fn RingBuffer::NumDVDMenuButtons(void)
+ *  \brief Calls DVDRingBufferPriv::NumMenuButtons(void)
+ */
+int RingBuffer::NumDVDMenuButtons(void)
+{
+   if (dvdPriv)
+       return dvdPriv->NumMenuButtons();
+   return 0;
+}
+
+/** \fn RingBuffer::NumPartsInDVDTitle(void)
+ *  \brief Calls DVDRingBufferPriv::NumPartsInTitle(void)
+ */
+int RingBuffer::NumPartsInDVDTitle(void)
+{
+   if (dvdPriv)
+       return dvdPriv->NumPartsInTitle();
+   return 0;
+}
+
+/** \fn RingBuffer::GetDVDMenuSPUPkt(uint8_t *buf, int buf_size)
+ *  \brief Calls DVDRingBufferPriv::GetMenuSPUPkt(uint8_t *buf, int buf_size)
+ */
+void RingBuffer::GetDVDMenuSPUPkt(uint8_t *buf, int buf_size)
+{
+   if (dvdPriv)
+       dvdPriv->GetMenuSPUPkt(buf,buf_size);
+}
+
+/** \fn RingBuffer::GetDVDMenuButton(void)
+ *  \brief Calls DVDRingBufferPriv::GetMenuButton(void)
+ */
+AVSubtitleRect *RingBuffer::GetDVDMenuButton(void)
+{
+   if (dvdPriv)
+       return dvdPriv->GetMenuButton();
+   return NULL;
+}
+
+/** \fn RingBuffer::IgnoringDVDStillorWait(void)
+ *  \brief Calls DVDRingBufferPriv::
+ */
+bool RingBuffer::IgnoringDVDStillorWait(void)
+{
+   if (dvdPriv)
+       return dvdPriv->IgnoringStillorWait();
+   return false;
+}
+
+/** \fn RingBuffer::IgnoreDVDStillOrWait(bool skip)
+ *  \brief Calls DVDRingBufferPriv::IgnoreStillOrWait()
+ */
+void RingBuffer::IgnoreDVDStillOrWait(bool skip)
+{
+   if (dvdPriv)
+       dvdPriv->IgnoreStillOrWait(skip);
+}
+
+/** \fn RingBuffer::GetDVDCurrentTime(void)
+ *  \brief Calls DVDRingBufferPriv::GetCurrentTime(void)
+ */
+uint RingBuffer::GetDVDCurrentTime(void)
+{
+   if (dvdPriv)
+       return dvdPriv->GetCurrentTime();
+   return 0;
+}
+
+/** \fn RingBuffer::HasDVDTitleChanged(void)
+ *  \brief Calls DVDRingBufferPriv::HasTitleChanged(void)
+ */
+bool RingBuffer::HasDVDTitleChanged(void)
+{
+   if (dvdPriv)
+       return dvdPriv->HasTitleChanged();
+   return false;
+}
+
+/** \fn RingBuffer::SetDVDTitleChanged(bool change)
+ *  \brief Calls DVDRingBufferPriv::SetDVDTitleChanged(bool change)
+ */
+void RingBuffer::SetDVDTitleChanged(bool change)
+{
+   if (dvdPriv)
+       dvdPriv->SetTitleChanged(change);
+}
Index: DVDRingBuffer.h
===================================================================
--- DVDRingBuffer.h	(revision 8623)
+++ DVDRingBuffer.h	(working copy)
@@ -7,6 +7,8 @@
 
 #include <qstring.h>
 #include <qobject.h>
+#include "util.h"
+#include "avcodec.h"
 
 #ifdef HAVE_DVDNAV
 #   include <dvdnav/dvdnav.h>
@@ -56,12 +58,19 @@
     bool IsInMenu(void) const { return (title == 0); }
     bool IsOpen(void)   const { return dvdnav;       }
     long long GetReadPosition(void);
-    long long GetTotalReadPosition(void);
+    long long GetTotalReadPosition(void) { return titleLength; }
     void GetDescForPos(QString &desc) const;
     void GetPartAndTitle(int &_part, int &_title) const
         { _part  = part; _title = _title; }
     uint GetTotalTimeOfTitle(void);
     uint GetCellStart(void);
+    bool InCellWithDVDStillFrame(void) { return cellHasStillFrame; }
+    bool IsDVDWaiting(void) { return dvdWaiting; }
+    int    NumPartsInTitle(void) { return titleParts; }
+    void GetMenuSPUPkt(uint8_t *buf, int buf_size);
+    AVSubtitleRect *GetMenuButton(void);
+    bool IgnoringStillorWait(void) { return skipstillorwait; }
+    bool HasTitleChanged(void) { return titleChanged; }
 
     // commands
     bool OpenFile(const QString &filename);
@@ -70,6 +79,20 @@
     void prevTrack(void);
     int  safe_read(void *data, unsigned sz);
     long long Seek(long long pos, int whence);
+    void SkipStillFrame(void);
+    void DVDWaitSkip(void);
+    void GoToRootMenu(void);
+    void GoToNextProgram(void);
+    void GoToPreviousProgram(void);
+    void MoveDVDButtonLeft(void);
+    void MoveDVDButtonRight(void);
+    void MoveDVDButtonUp(void);
+    void MoveDVDButtonDown(void);
+    void ActivateDVDButton(void);
+    int NumMenuButtons(void);
+    void IgnoreStillOrWait(bool skip) { skipstillorwait = skip; }
+    uint GetCurrentTime(void);
+    void SetTitleChanged(bool change) { titleChanged = change; }
         
   protected:
     dvdnav_t      *dvdnav;
@@ -84,9 +107,39 @@
     dvdnav_t      *lastNav; // This really belongs in the player.
     int            part;
     int            title;
-    int            maxPart;
-    int            mainTitle;
+    int            titleParts;
     bool           gotStop;
+    bool         cellHasStillFrame;
+    bool         dvdWaiting;
+    long long  titleLength;
+    MythTimer stillFrameTimer;
+    uint32_t   clut[16];
+    uint32_t   button_color[4];
+    uint8_t     button_alpha[4];
+    uint16_t   hl_startx;
+    uint16_t   hl_width;
+    uint16_t   hl_starty;
+    uint16_t   hl_height;
+    bool         spuchanged;
+    uint8_t     *menuSpuPkt;
+    int            menuBuflength;
+    uint8_t     *buttonBitmap;
+    AVSubtitleRect *dvdMenuButton;
+    int buttonCoords;
+    bool skipstillorwait;
+    bool spuStreamLetterbox;
+    bool titleChanged;
+
+    bool DrawMenuButton(uint8_t *spu_pkt, int buf_size);
+    bool DVDButtonUpdate(bool b_mode);
+    void ClearMenuSPUParameters(void);
+    bool MenuButtonChanged(void);
+    /* copied from dvdsub.c from ffmpeg */
+   int get_nibble(const uint8_t *buf, int nibble_offset);
+   int decode_rle(uint8_t *bitmap, int linesize, int w, int h,
+                           const uint8_t *buf, int nibble_offset, int buf_size);
+   void guess_palette(uint32_t *rgba_palette,uint8_t *palette,
+                                   uint8_t *alpha);
 };
 #endif // HAVE_DVDNAV
 #endif // DVD_RING_BUFFER_H_
Index: avformatdecoder.cpp
===================================================================
--- avformatdecoder.cpp	(revision 8623)
+++ avformatdecoder.cpp	(working copy)
@@ -18,6 +18,7 @@
 #include "mythdbcon.h"
 #include "iso639.h"
 #include "pespacket.h"
+#include "audiooutput.h"
 
 #ifdef USING_XVMC
 #include "videoout_xv.h"
@@ -429,6 +430,10 @@
 void AvFormatDecoder::SeekReset(long long newKey, uint skipFrames,
                                 bool doflush, bool discardFrames)
 {
+
+   if (ringBuffer->IsInDVDMenu())
+       return;
+
     VERBOSE(VB_PLAYBACK, LOC +
             QString("SeekReset(%1, %2, %3 flush, %4 discard)")
             .arg(newKey).arg(skipFrames)
@@ -483,8 +488,11 @@
 
         prevgoppos = 0;
         gopset = false;
-        framesPlayed = lastKey;
-        framesRead = lastKey; 
+        if (!ringBuffer->isDVD())
+        {
+           framesPlayed = lastKey;
+           framesRead = lastKey;
+        }
     }
 
     // Skip all the desired number of skipFrames
@@ -516,6 +524,13 @@
 void AvFormatDecoder::Reset()
 {
     DecoderBase::Reset();
+    if (ringBuffer->isDVD())
+    {
+       posmapStarted = false;
+       SyncPositionMap();
+       framesPlayed = DVDCurrentFrameNumber();
+       framesRead = framesPlayed;       
+    }
 #if 0
 // This is causing problems, and may not be needed anymore since
 // we do not reuse the same file for different streams anymore. -- dtk
@@ -648,7 +663,10 @@
     readcontext.max_packet_size = 0;
     readcontext.priv_data = this;
 
-    ic->pb.buffer_size = 32768;
+    if (ringBuffer->isDVD())
+       ic->pb.buffer_size = 2048;
+    else
+       ic->pb.buffer_size = 32768;
     ic->pb.buffer = (unsigned char *)av_malloc(ic->pb.buffer_size);
     ic->pb.buf_ptr = ic->pb.buffer;
     ic->pb.write_flag = 0;
@@ -731,7 +749,21 @@
         return -1;
     }
 
-    int ret = av_find_stream_info(ic);
+    int ret;
+    if (ringBuffer->isDVD())
+   {
+       AVPacket pkt1;
+       while (ic->nb_streams == 0)
+       {
+           ret = av_read_frame(ic,&pkt1);
+       }
+       av_free_packet(&pkt1);
+       ringBuffer->Seek(0,SEEK_SET);
+       ringBuffer->IgnoreDVDStillOrWait(false);
+   }
+   else
+       ret = av_find_stream_info(ic);
+
     if (ret < 0)
     {
         VERBOSE(VB_IMPORTANT, LOC_ERR + "Could not find codec parameters. " +
@@ -745,7 +777,9 @@
 
     fmt->flags &= ~AVFMT_NOFILE;
 
-    av_estimate_timings(ic);
+    if (!ringBuffer->isDVD())
+       av_estimate_timings(ic);
+
     av_read_frame_flush(ic);
 
     // Scan for the initial A/V streams
@@ -2041,6 +2075,17 @@
                     QString("Selected track %1 (A/V Stream #%2)")
                     .arg(list[currentAudioTrack])
                     .arg(selectedAudioStream.av_stream_index));
+	    if (ringBuffer->isDVD())
+	    {
+	       if (ringBuffer->IsInDVDMenu())
+	       {
+                   AudioOutput *aud = GetNVP()->getAudioOutput();
+                   if (aud)
+	              aud->Reset();
+	       }
+	       else
+	           GetNVP()->ResetPlaying();
+            }
         }
     }
 
@@ -2112,6 +2157,9 @@
 {
     uint numStreams = subtitleStreams.size();
 
+    if (ringBuffer->IsInDVDMenu())
+        return true;
+
     if ((currentSubtitleTrack >= 0) && 
         (currentSubtitleTrack < (int)numStreams))
     {
@@ -2168,6 +2216,7 @@
     if (wantedSubtitleStream.av_stream_index < 0)
         wantedSubtitleStream = selectedSubtitleStream;
      
+
     int lang = subtitleStreams[currentSubtitleTrack].language;
     VERBOSE(VB_PLAYBACK, LOC + 
             QString("Selected subtitle track #%1 in the %2 language(%3)")
@@ -2212,8 +2261,9 @@
 
         if (gotvideo)
         {
+	    bool draindvdpkts = (ringBuffer->InCellWithDVDStillFrame() | ringBuffer->IsDVDWaiting());
             if (lowbuffers && onlyvideo == 0 && lastapts < lastvpts + 100 &&
-                lastapts > lastvpts - 10000)
+                lastapts > lastvpts - 10000 && !draindvdpkts)
             {
                 //cout << "behind: " << lastapts << " " << lastvpts << endl;
                 storevideoframes = true;
@@ -2506,8 +2556,14 @@
                         ret = d->DecodeMPEG2Video(context, &mpa_pic,
                                                   &gotpicture, ptr, len);
                     else
+                   {
                         ret = avcodec_decode_video(context, &mpa_pic,
                                                    &gotpicture, ptr, len);
+                       // Reparse it to not drop the DVD still frame //
+                       if (ringBuffer->InCellWithDVDStillFrame())
+                           ret = avcodec_decode_video(context, &mpa_pic,
+                                                   &gotpicture, ptr, len);
+                    }
                     pthread_mutex_unlock(&avcodeclock);
 
                     if (ret < 0)
@@ -2589,6 +2645,20 @@
                     picframe->interlaced_frame = mpa_pic.interlaced_frame;
                     picframe->top_field_first = mpa_pic.top_field_first;
 
+                   if (ringBuffer->HasDVDTitleChanged() && 
+                        !ringBuffer->IsInDVDMenu())
+                    {
+                      ringBuffer->SetDVDTitleChanged(false);
+                      posmapStarted = false;
+                      m_positionMap.clear();
+                      SyncPositionMap();
+                      framesPlayed = DVDCurrentFrameNumber();
+                      framesRead = framesPlayed;
+                      VERBOSE(VB_PLAYBACK, LOC +
+                         QString("DVD Title Changed. Update framesPlayed: %1 ")
+                         .arg(framesPlayed));
+                    } 
+
                     picframe->frameNumber = framesPlayed;
                     GetNVP()->ReleaseNextVideoFrame(picframe, temppts);
                     if (d->HasMPEG2Dec() && mpa_pic.data[3])
@@ -2606,7 +2676,9 @@
                     int gotSubtitles = 0;
                     AVSubtitle subtitle;
 
-                    if (pkt->stream_index == subIdx)
+                    if (ringBuffer->IsInDVDMenu())
+                        ringBuffer->GetDVDMenuSPUPkt(ptr,len);
+                    else if (pkt->stream_index == subIdx)
                     {
                         pthread_mutex_lock(&avcodeclock);
                         avcodec_decode_subtitle(curstream->codec,
Index: decoderbase.h
===================================================================
--- decoderbase.h	(revision 8623)
+++ decoderbase.h	(working copy)
@@ -90,6 +90,7 @@
     // DVD public stuff
     void ChangeDVDTrack(bool ffw);
     long long DVDFindPosition(long long desiredFrame);
+    long long DVDCurrentFrameNumber(void);
 
   protected:
     void FileChanged(void);
Index: tv_play.cpp
===================================================================
--- tv_play.cpp	(revision 8623)
+++ tv_play.cpp	(working copy)
@@ -148,7 +148,8 @@
     REG_KEY("TV Playback", "PREVSUBTITLE", "Switch to the previous subtitle track", "");
     REG_KEY("TV Playback", "JUMPPREV", "Jump to previously played recording", "");
     REG_KEY("TV Playback", "SIGNALMON", "Monitor Signal Quality", "F7");
-    
+    REG_KEY("TV Playback", "JUMPTODVDMENU", "Jump to the DVD Root Menu", "");
+
     REG_KEY("TV Editing", "CLEARMAP", "Clear editing cut points", "C,Q,Home");
     REG_KEY("TV Editing", "INVERTMAP", "Invert Begin/End cut points", "I");
     REG_KEY("TV Editing", "LOADCOMMSKIP", "Load cut list from commercial skips",
@@ -1131,6 +1132,12 @@
 void TV::StopStuff(bool stopRingBuffers, bool stopPlayers, bool stopRecorders)
 {
     VERBOSE(VB_PLAYBACK, LOC + "StopStuff() -- begin");
+
+    if (prbuffer->isDVD())
+    {
+       VERBOSE(VB_PLAYBACK,LOC + " StopStuff() -- get dvd player out of still frame or wait status");
+       prbuffer->IgnoreDVDStillOrWait(true);
+    } 
     if (stopRingBuffers)
     {
         VERBOSE(VB_PLAYBACK, LOC + "StopStuff(): stopping ring buffer[s]");
@@ -2094,7 +2101,12 @@
         }
         else if (action == "SEEKFFWD")
         {
-            if (HasQueuedInput())
+	    if (prbuffer->IsInDVDMenu())
+	    {
+                if (prbuffer->NumDVDMenuButtons() > 1)
+                   prbuffer->MoveDVDButtonRight();
+            }
+            else if (HasQueuedInput())
                 DoArbSeek(ARBSEEK_FORWARD);
             else if (paused)
                 DoSeek(1.001 / frameRate, tr("Forward"));
@@ -2110,7 +2122,12 @@
         }
         else if (action == "FFWDSTICKY")
         {
-            if (HasQueuedInput())
+	    if (prbuffer->IsInDVDMenu())
+	    {
+               if (prbuffer->NumDVDMenuButtons() > 1)
+                   prbuffer->MoveDVDButtonRight();
+            }
+            else if (HasQueuedInput())
                 DoArbSeek(ARBSEEK_END);
             else if (paused)
                 DoSeek(1.0, tr("Forward"));
@@ -2119,7 +2136,12 @@
         }
         else if (action == "SEEKRWND")
         {
-            if (HasQueuedInput())
+	    if (prbuffer->IsInDVDMenu())
+	    {
+               if (prbuffer->NumDVDMenuButtons() > 1)
+                   prbuffer->MoveDVDButtonLeft();
+            }
+            else if (HasQueuedInput())
                 DoArbSeek(ARBSEEK_REWIND);
             else if (paused)
                 DoSeek(-1.001 / frameRate, tr("Rewind"));
@@ -2134,7 +2156,12 @@
         }
         else if (action == "RWNDSTICKY")
         {
-            if (HasQueuedInput())
+	    if (prbuffer->IsInDVDMenu())
+	    {
+               if (prbuffer->NumDVDMenuButtons() > 1)
+                   prbuffer->MoveDVDButtonLeft();
+            }
+            else if (HasQueuedInput())
                 DoArbSeek(ARBSEEK_SET);
             else if (paused)
                 DoSeek(-1.0, tr("Rewind"));
@@ -2145,9 +2172,18 @@
         {
             if (prbuffer->isDVD())
             {
-                nvp->ChangeDVDTrack(0);
-                UpdateOSDSeekMessage(tr("Previous Chapter"),
-                                     osd_general_timeout);
+                if (prbuffer->NumPartsInDVDTitle() < 1)
+		{
+                   prbuffer->GoToDVDPreviousProgram();
+		   UpdateOSDSeekMessage(tr("Previous Title/Chapter"), 
+		                        osd_general_timeout);
+		}
+                else
+                {
+                   nvp->ChangeDVDTrack(0);
+                   UpdateOSDSeekMessage(tr("Previous Chapter"),
+                                        osd_general_timeout);
+                }
             }
             else
             {
@@ -2158,8 +2194,24 @@
         {
             if (prbuffer->isDVD())
             {
-                nvp->ChangeDVDTrack(1);
-                UpdateOSDSeekMessage(tr("Next Chapter"), osd_general_timeout);
+                if (prbuffer->InCellWithDVDStillFrame())
+		{
+                       prbuffer->DVDSkipStillFrame();
+		       UpdateOSDSeekMessage(tr("Skip Still Frame"), 
+		                            osd_general_timeout);
+		}
+                else if (prbuffer->NumPartsInDVDTitle() > 1)
+                {
+                   nvp->ChangeDVDTrack(1);
+                   UpdateOSDSeekMessage(tr("Next Chapter"), 
+		                        osd_general_timeout);
+                }
+                else 
+		{
+                   prbuffer->GoToDVDNextProgram();
+		   UpdateOSDSeekMessage(tr("Next Title"), 
+		                        osd_general_timeout);
+		}
             }
             else
             {
@@ -2431,7 +2483,9 @@
 
             if (action == "SELECT")
             {
-                if (!was_doing_ff_rew)
+                if (prbuffer->NumDVDMenuButtons() > 0)
+                    nvp->ActivateDVDButton();
+                else if (!was_doing_ff_rew)
                 {
                     if (gContext->GetNumSetting("AltClearSavedPosition", 1)
                         && nvp->GetBookmark())
@@ -2452,6 +2506,8 @@
                 exitPlayer = true;
                 wantsToQuit = true;
             }
+            else if (action == "JUMPTODVDMENU")
+                prbuffer->GoToDVDRootMenu();
             else if (action == "TOGGLEEDIT")
                 DoEditMode();
             else if (action == "TOGGLEBROWSE")
@@ -2460,9 +2516,14 @@
             {
                 if (prbuffer->isDVD())
                 {
-                    nvp->ChangeDVDTrack(0);
-                    UpdateOSDSeekMessage(tr("Previous Chapter"),
+                    if (prbuffer->NumDVDMenuButtons() > 1)
+                          prbuffer->MoveDVDButtonUp();
+                    else
+                   {
+                       nvp->ChangeDVDTrack(0);
+                       UpdateOSDSeekMessage(tr("Previous Chapter"),
                                          osd_general_timeout);
+                   }
                 }
                 else
                 {
@@ -2473,9 +2534,14 @@
             {
                 if (prbuffer->isDVD())
                 {
-                    nvp->ChangeDVDTrack(1);
-                    UpdateOSDSeekMessage(tr("Next Chapter"),
+                   if (prbuffer->NumDVDMenuButtons() > 1)
+                          prbuffer->MoveDVDButtonDown();
+                   else
+                  {
+                       nvp->ChangeDVDTrack(1);
+                       UpdateOSDSeekMessage(tr("Next Chapter"),
                                          osd_general_timeout);
+                   }
                 }
                 else
                 {
@@ -2731,6 +2797,9 @@
 
 void TV::DoPause(void)
 {
+    if (prbuffer->IsInDVDMenu())
+       return;
+
     speed_index = 0;
     float time = 0.0;
 
@@ -5122,7 +5191,9 @@
     }
     else if (StateIsPlaying(internalState))
     {
-        if (action == "TOGGLEEDIT")
+        if (action == "JUMPTODVDMENU")
+            prbuffer->GoToDVDRootMenu();
+        else if (action == "TOGGLEEDIT")
             DoEditMode();
         else if (action == "TOGGLEAUTOEXPIRE")
             ToggleAutoExpire();
@@ -5213,6 +5284,9 @@
     }
     else if (StateIsPlaying(internalState))
     {
+        if (prbuffer->isDVD() && !prbuffer->IsInDVDMenu())
+           item = new OSDGenericTree(treeMenu,tr("DVD Root Menu"), "JUMPTODVDMENU");
+
         item = new OSDGenericTree(treeMenu, tr("Edit Recording"), "TOGGLEEDIT");
 
         if (lastProgram != NULL)
Index: DVDRingBuffer.cpp
===================================================================
--- DVDRingBuffer.cpp	(revision 8623)
+++ DVDRingBuffer.cpp	(working copy)
@@ -25,9 +25,14 @@
       pgLength(0),      pgcLength(0),
       cellStart(0),     pgStart(0),
       lastNav(NULL),    part(0),
-      title(0),         maxPart(0),
-      mainTitle(0),     gotStop(false)
+      title(0),         gotStop(false),
+      cellHasStillFrame(false), dvdWaiting(false),
+      titleLength(0),  spuchanged(false),
+      menuBuflength(0),  buttonCoords(0),
+      skipstillorwait(true), spuStreamLetterbox(false),
+      titleChanged(false)
 {
+   dvdMenuButton = (AVSubtitleRect*)av_mallocz(sizeof(AVSubtitleRect));
 }
 
 DVDRingBufferPriv::~DVDRingBufferPriv()
@@ -84,8 +89,6 @@
 
         int numTitles  = 0;
         int titleParts = 0;
-        maxPart        = 0;
-        mainTitle      = 0;
         dvdnav_title_play(dvdnav, 0);
         dvdRet = dvdnav_get_number_of_titles(dvdnav, &numTitles);
         if (numTitles == 0 )
@@ -113,17 +116,9 @@
                 VERBOSE(VB_IMPORTANT,
                         QString("There are title %1 has %2 parts.")
                         .arg(curTitle).arg(titleParts));
-                if (titleParts > maxPart)
-                {
-                    maxPart = titleParts;
-                    mainTitle = curTitle;
-                }
             }
-            VERBOSE(VB_IMPORTANT, QString("%1 selected as the main title.")
-                    .arg(mainTitle));
         }                
 
-        dvdnav_title_play(dvdnav, mainTitle);
         dvdnav_current_title_info(dvdnav, &title, &part);
         return true;
     }
@@ -140,17 +135,6 @@
     return pos * DVD_BLOCK_SIZE;
 }
 
-long long DVDRingBufferPriv::GetTotalReadPosition(void)
-{
-    uint32_t pos;
-    uint32_t length;
-
-    if (dvdnav)        
-        dvdnav_get_position(dvdnav, &pos, &length);
-
-    return length * DVD_BLOCK_SIZE;
-}
-
 int DVDRingBufferPriv::safe_read(void *data, unsigned sz)
 {
     dvdnav_status_t dvdStat;
@@ -183,7 +167,6 @@
                     .arg(dvdnav_err_to_string(dvdnav)));
             return -1;
         }
-
         switch (dvdEvent)
         {
             case DVDNAV_BLOCK_OK:
@@ -214,13 +197,31 @@
                         .arg(pgLength).arg(pgcLength)
                         .arg(cellStart).arg(pgStart));
 
-                dvdnav_current_title_info(dvdnav, &title, &part);            
-                if (title == 0)
-                {
-                    pci_t* pci = dvdnav_get_current_nav_pci(dvdnav);
-                    dvdnav_button_select(dvdnav, pci, 1);
+                if (dvdnav_get_next_still_flag(dvdnav) > 0)
+               {
+                   if (dvdnav_get_next_still_flag(dvdnav) < 0xff)
+                       stillFrameTimer.restart();
+                   cellHasStillFrame = true;
                 }
+                else
+                   cellHasStillFrame = false;
 
+                int tmptitle = 0;
+		part = 0;
+		titleParts = 0;
+                dvdnav_current_title_info(dvdnav, &tmptitle, &part);
+                dvdnav_get_number_of_parts(dvdnav, tmptitle, &titleParts);
+
+                if (tmptitle != title)
+                  titleChanged = true;
+                
+                title = tmptitle;
+
+                uint32_t pos;
+                uint32_t length;
+                dvdnav_get_position(dvdnav,&pos,&length);
+                titleLength = length *DVD_BLOCK_SIZE;
+
                 if (blockBuf != dvdBlockWriteBuf)
                 {
                     dvdnav_free_cache_block(dvdnav, blockBuf);
@@ -228,6 +229,7 @@
             }
             break;
             case DVDNAV_SPU_CLUT_CHANGE:
+                memcpy(clut,blockBuf, 16 * sizeof (uint32_t));
                 VERBOSE(VB_PLAYBACK, "DVDNAV_SPU_CLUT_CHANGE happened.");
                 break;
                     
@@ -241,7 +243,12 @@
                                 "physical_pan_scan==%3, logical==%4")
                         .arg(spu->physical_wide).arg(spu->physical_letterbox)
                         .arg(spu->physical_pan_scan).arg(spu->logical));
-                        
+                if (spu->physical_letterbox)
+                   spuStreamLetterbox = true;
+                else
+                   spuStreamLetterbox = false;
+                spuchanged = true;
+                ClearMenuSPUParameters();
                 if (blockBuf != dvdBlockWriteBuf)
                 {
                     dvdnav_free_cache_block(dvdnav, blockBuf);
@@ -265,6 +272,24 @@
             break;
             case DVDNAV_NAV_PACKET:
                 lastNav = (dvdnav_t *)blockBuf;
+                if (IsInMenu() && NumMenuButtons() > 0)
+                {
+                   int32_t button;
+                   pci_t *pci = dvdnav_get_current_nav_pci(dvdnav);
+                   dvdnav_get_current_highlight(dvdnav, &button);
+
+                   if (button > NumMenuButtons() || button < 1)
+                       dvdnav_button_select(dvdnav,pci,1);
+                   else
+                       dvdnav_button_select(dvdnav,pci,button);
+                   
+		   buttonCoords = 0;
+                   spuchanged = false;
+                }
+                if (blockBuf != dvdBlockWriteBuf)
+                {
+                    dvdnav_free_cache_block(dvdnav, blockBuf);
+                }       
                 break;
             case DVDNAV_HOP_CHANNEL:
                 VERBOSE(VB_PLAYBACK, "DVDNAV_HOP_CHANNEL happened.");
@@ -303,26 +328,43 @@
                     dvdnav_free_cache_block(dvdnav, blockBuf);
                 }                                                   
 
+                if (DVDButtonUpdate(true))
+                   DrawMenuButton(menuSpuPkt,menuBuflength);
             }
             break;
             case DVDNAV_STILL_FRAME:
             {
                 dvdnav_still_event_t* still =
                     (dvdnav_still_event_t*)(blockBuf);
-                VERBOSE(VB_PLAYBACK, "DVDNAV_STILL_FRAME: " +
-                        QString("needs displayed for %1 seconds")
-                        .arg(still->length));
+
+		if (skipstillorwait)
+		   SkipStillFrame();
+		else
+		{
+                   int elapsedTime = 0;
+                   if (still->length  < 0xff)
+                   {
+                       elapsedTime = stillFrameTimer.elapsed() / 1000; // in seconds
+                       if (elapsedTime < still->length)
+                           usleep(10000);
+                       else if (elapsedTime == still->length)
+                           SkipStillFrame();
+                   }
+		}
                 if (blockBuf != dvdBlockWriteBuf)
                 {
                     dvdnav_free_cache_block(dvdnav, blockBuf);
-                }                                                   
-                        
-                dvdnav_still_skip(dvdnav);
+                }
             }
             break;
             case DVDNAV_WAIT:
-                VERBOSE(VB_PLAYBACK, "DVDNAV_WAIT recieved clearing it");
-                dvdnav_wait_skip(dvdnav);
+                if (skipstillorwait)
+                    DVDWaitSkip();
+                else 
+                {
+                   dvdWaiting = true;
+                   usleep(10000);
+                }
                 break;
             case DVDNAV_STOP:
                 VERBOSE(VB_GENERAL, "DVDNAV_STOP");
@@ -344,24 +386,25 @@
 bool DVDRingBufferPriv::nextTrack(void)
 {
     int newPart = part + 1;
-    if (newPart < maxPart)
+    
+    if (newPart < titleParts)
     {
-        dvdnav_part_play(dvdnav, title, newPart);
-        gotStop = false;
-        return true;
+       dvdnav_part_play(dvdnav, title, newPart);
+       gotStop = false;
+       return true;
     }
     return false;
 }
 
 void DVDRingBufferPriv::prevTrack(void)
 {
-    int newPart = part - 1;
-
-    if (newPart > 0)
-        dvdnav_part_play(dvdnav, title, newPart);
-    else
-        Seek(0,SEEK_SET); // May cause picture to become jumpy.
-    gotStop = false;
+   int newPart = part - 1;
+  
+   if (newPart > 0)
+       dvdnav_part_play(dvdnav,title,newPart);
+   else
+       Seek(0,SEEK_SET); // May cause picture to be jumpy.
+   gotStop = false;
 }
 
 uint DVDRingBufferPriv::GetTotalTimeOfTitle(void)
@@ -374,4 +417,384 @@
     return cellStart / 90000;
 }
 
+void DVDRingBufferPriv::SkipStillFrame(void)
+{
+   dvdnav_still_skip(dvdnav);
+   cellHasStillFrame = false;
+}
+
+void DVDRingBufferPriv::DVDWaitSkip(void)
+{
+   dvdnav_wait_skip(dvdnav);
+   dvdWaiting = false;
+}
+
+void DVDRingBufferPriv::GoToRootMenu(void)
+{
+       dvdnav_menu_call(dvdnav,DVD_MENU_Root);
+}
+
+void DVDRingBufferPriv::GoToNextProgram(void)
+{
+   // if not in the menu feature, okay to skip allow to skip it.
+   //  if (!dvdnav_is_domain_vts(dvdnav))
+        dvdnav_next_pg_search(dvdnav);
+}
+
+void DVDRingBufferPriv::GoToPreviousProgram(void)
+{
+    // if (!dvdnav_is_domain_vts(dvdnav))
+        dvdnav_prev_pg_search(dvdnav);
+}
+
+void DVDRingBufferPriv::MoveDVDButtonLeft(void)
+{
+    if (IsInMenu() && (NumMenuButtons() > 0))
+   {
+       pci_t *pci = dvdnav_get_current_nav_pci(dvdnav);
+       dvdnav_left_button_select(dvdnav,pci);
+   }
+}
+
+void DVDRingBufferPriv::MoveDVDButtonRight(void)
+{
+   if (IsInMenu() &&(NumMenuButtons() > 0) )
+  {
+       pci_t *pci = dvdnav_get_current_nav_pci(dvdnav);
+       dvdnav_right_button_select(dvdnav,pci);
+   }
+}
+
+void DVDRingBufferPriv::MoveDVDButtonUp(void)
+{
+   if (IsInMenu() && (NumMenuButtons() > 0))
+  {
+       pci_t *pci = dvdnav_get_current_nav_pci(dvdnav);
+       dvdnav_upper_button_select(dvdnav,pci);
+   }
+}
+
+void DVDRingBufferPriv::MoveDVDButtonDown(void)
+{
+   if (IsInMenu() && (NumMenuButtons() > 0))
+   {
+       pci_t *pci = dvdnav_get_current_nav_pci(dvdnav);
+       dvdnav_lower_button_select(dvdnav,pci);
+   }
+}
+
+void DVDRingBufferPriv::ActivateDVDButton(void)
+{
+   if (IsInMenu() && (NumMenuButtons() > 0))
+   {
+       pci_t *pci = dvdnav_get_current_nav_pci(dvdnav);
+       dvdnav_button_activate(dvdnav,pci);
+   }
+}
+
+void DVDRingBufferPriv::GetMenuSPUPkt(uint8_t *buf, int buf_size)
+{    
+    if (buf_size < 4)
+       return;
+
+   if (spuStreamLetterbox)
+   {
+      if ((buf_size < menuBuflength) && menuBuflength > 0)
+         return;
+   }
+   else
+   {
+      if ((buf_size > menuBuflength) && (menuBuflength > 0))
+         return;
+   }
+    ClearMenuSPUParameters();
+
+    uint8_t *spu_pkt;
+    spu_pkt = (uint8_t*)av_malloc(buf_size);
+    memcpy(spu_pkt,buf,buf_size);
+    menuSpuPkt = spu_pkt;
+    menuBuflength = buf_size;
+ 
+    if (DVDButtonUpdate(true))
+       DrawMenuButton(menuSpuPkt,menuBuflength);
+}
+
+AVSubtitleRect *DVDRingBufferPriv::GetMenuButton(void)
+{
+   if (MenuButtonChanged())
+       return dvdMenuButton;
+
+   return NULL;
+}
+
+
+bool DVDRingBufferPriv::DrawMenuButton(uint8_t *spu_pkt, int buf_size)
+{
+   #define GETBE16(p) (((p)[0] << 8) | (p)[1])
+
+    int cmd_pos, pos,cmd,next_cmd_pos,offset1,offset2;
+    int x1,x2,y1,y2;
+    uint8_t alpha[4],palette[4];
+
+    x1 = x2 = y1 = y2 = 0;
+
+    if (!spu_pkt)
+       return false; 
+
+    for (int i = 0; i < 4 ; i++)
+    {
+       alpha[i]    = button_alpha[i];
+       palette[i] = button_color[i];
+    }
+ 
+    if (buf_size < 4)
+       return false;
+
+    cmd_pos = GETBE16(spu_pkt + 2);
+    while ((cmd_pos + 4) < buf_size)
+   {
+       offset1 = -1;
+       offset2 = -1;
+       next_cmd_pos = GETBE16(spu_pkt + cmd_pos + 2);
+       pos = cmd_pos + 4;
+       while (pos < buf_size)
+       {
+           cmd = spu_pkt[pos++];
+           switch(cmd) 
+           {
+               case 0x00:
+                   break;  
+               case 0x01:
+                   break;
+               case 0x02:
+                   break;
+               case 0x03:
+               {
+                   if ((buf_size - pos) < 2)
+                       goto fail;
+                   pos +=2;
+               }
+               break;
+               case 0x04:
+               {
+                   if ((buf_size - pos) < 2)
+                       goto fail;
+                   pos +=2;
+               }
+               break;
+               case 0x05:
+               {
+                   if ((buf_size - pos) < 6)
+                       goto fail;
+                   x1 = (spu_pkt[pos] << 4) | (spu_pkt[pos + 1] >> 4);
+                   x2 = ((spu_pkt[pos + 1] & 0x0f) << 8) | spu_pkt[pos + 2];
+                   y1 = (spu_pkt[pos + 3] << 4) | (spu_pkt[pos + 4] >> 4);
+                   y2 = ((spu_pkt[pos + 4] & 0x0f) << 8) | spu_pkt[pos + 5];
+                   pos +=6;
+                }
+                break;
+               case 0x06:
+               {
+                   if ((buf_size - pos) < 4)
+                       goto fail;
+                   offset1 = GETBE16(spu_pkt + pos);
+                   offset2 = GETBE16(spu_pkt + pos + 2);
+                   pos +=4;
+                }
+               break;
+               case 0xff:
+               default:
+                   goto the_end;
+           }
+       }
+       the_end:
+           if (offset1 >= 0) 
+           {
+               int w, h;
+               uint8_t *bitmap;
+               w = x2 - x1 + 1;
+               if (w < 0)
+                   w = 0;
+               h = y2 - y1;
+               if (h < 0)
+                   h = 0;
+               if (w > 0 && h > 0) 
+               {
+                   bitmap = (uint8_t*) av_malloc(w * h);
+                   dvdMenuButton->rgba_palette = (uint32_t*)av_malloc(4 *4);
+                   decode_rle(bitmap, w * 2, w, h / 2,
+                           spu_pkt, offset1 * 2, buf_size);
+                   decode_rle(bitmap + w, w * 2, w, h / 2,
+                           spu_pkt, offset2 * 2, buf_size);
+                   guess_palette(dvdMenuButton->rgba_palette,palette,alpha);
+                   dvdMenuButton->bitmap = bitmap;
+                   dvdMenuButton->x = hl_startx;
+                   dvdMenuButton->y  = hl_starty;
+                   dvdMenuButton->w  = hl_width;
+                   dvdMenuButton->h  = hl_height;
+                   dvdMenuButton->nb_colors = 4;
+                   dvdMenuButton->linesize = w;
+                   return true; 
+               }
+          }
+           if (next_cmd_pos == cmd_pos)
+               break;
+           cmd_pos = next_cmd_pos;
+   }
+   fail:
+       return false;
+}
+bool DVDRingBufferPriv::DVDButtonUpdate(bool b_mode)
+{
+    int32_t button;
+    pci_t *pci;
+    dvdnav_highlight_area_t hl;
+    dvdnav_get_current_highlight(dvdnav, &button);
+
+    pci = dvdnav_get_current_nav_pci(dvdnav);
+    dvdnav_get_highlight_area(pci,button,b_mode,&hl);
+
+    for (int i = 0 ; i < 4 ; i++)
+   {
+       button_alpha[i] = 0xf & (hl.palette >> (4 * i ));
+       button_color[i] = 0xf & (hl.palette >> (16+4 *i ));
+   }
+
+   hl_startx = hl.sx;
+   hl_width = hl.ex - hl.sx;
+   hl_starty = hl.sy;
+   hl_height  = hl.ey - hl.sy;
+
+   int total_start_pos = hl.sx + hl.sy;
+   if ( total_start_pos == 0 || total_start_pos > (720 + 480 ))
+       return false;
+
+   return true;
+}
+
+void DVDRingBufferPriv::ClearMenuSPUParameters(void)
+{
+   if (!dvdMenuButton->x)
+   return;
+
+   VERBOSE(VB_PLAYBACK,"Clearing Menu SPU Packet" );
+   av_free(menuSpuPkt);
+   menuBuflength = 0;
+   dvdMenuButton->x = 0;
+   dvdMenuButton->y = 0;
+   av_free(dvdMenuButton->rgba_palette);
+   av_free(dvdMenuButton->bitmap);
+}
+
+bool DVDRingBufferPriv::MenuButtonChanged(void)
+{
+   if (menuBuflength < 4)
+       return false;
+
+    int x = dvdMenuButton->x;
+    int y = dvdMenuButton->y;
+    if (buttonCoords != (x+y))
+    {
+        buttonCoords = (x+y);
+        return true;
+    }
+    return false;
+}
+
+int DVDRingBufferPriv::NumMenuButtons(void)
+{
+    pci_t *pci = dvdnav_get_current_nav_pci(dvdnav);
+    int numButtons = pci->hli.hl_gi.btn_ns;
+    return  numButtons;
+}
+
+uint DVDRingBufferPriv::GetCurrentTime(void)
+{
+    // Macro to convert Binary Coded Decimal to Decimal
+   // Obtained from VLC Code.
+    #define BCD2D(__x__) (((__x__ & 0xf0) >> 4) * 10 + (__x__ & 0x0f))
+
+    dsi_t *dvdnavDsi = dvdnav_get_current_nav_dsi(dvdnav);
+    dvd_time_t timeFromCellStart = dvdnavDsi->dsi_gi.c_eltm;
+    uint8_t hours = BCD2D(timeFromCellStart.hour);
+    uint8_t minutes = BCD2D(timeFromCellStart.minute);
+    uint8_t seconds = BCD2D(timeFromCellStart.second);
+    uint currentTime = GetCellStart() + (hours * 3600) + (minutes * 60) + seconds;
+    VERBOSE(VB_PLAYBACK,QString("cellStartTime == %1 current time: hours %2 minutes"
+                      "%3 seconds %4 currenttime %5").arg(GetCellStart()).arg(hours).
+                      arg(minutes).arg(seconds).arg(currentTime));
+    return currentTime;
+}
+
+void DVDRingBufferPriv::guess_palette(uint32_t *rgba_palette,uint8_t *palette,
+                                        uint8_t *alpha)
+{
+   int i,r,g,b;
+   uint32_t yuv;
+
+    for(i = 0; i < 4; i++)
+        rgba_palette[i] = 0;
+
+    for ( i=0 ; i < 4 ; i++)
+   {
+       yuv = clut[palette[i]];
+       r = ((yuv >> 24) & 0xff);
+       g = ((yuv >> 16) & 0xff);
+       b = ((yuv >> 8) & 0xff);
+       rgba_palette[i] = b | (g << 8) | (r << 16) | ((alpha[i] * 17)  << 24);
+   }
+}
+
+int DVDRingBufferPriv::decode_rle(uint8_t *bitmap, int linesize, int w, int h, 
+                                     const uint8_t *buf, int nibble_offset, int buf_size)
+{
+    unsigned int v;
+    int x, y, len, color, nibble_end;
+    uint8_t *d;
+
+    nibble_end = buf_size * 2;
+    x = 0;
+    y = 0;
+    d = bitmap;
+    for(;;) {
+        if (nibble_offset >= nibble_end)
+            return -1;
+        v = get_nibble(buf, nibble_offset++);
+        if (v < 0x4) {
+            v = (v << 4) | get_nibble(buf, nibble_offset++);
+            if (v < 0x10) {
+                v = (v << 4) | get_nibble(buf, nibble_offset++);
+                if (v < 0x040) {
+                    v = (v << 4) | get_nibble(buf, nibble_offset++);
+                    if (v < 4) {
+                        v |= (w - x) << 2;
+                    }
+                }
+            }
+        }
+        len = v >> 2;
+        if (len > (w - x))
+            len = (w - x);
+        color = v & 0x03;
+        memset(d + x, color, len);
+        x += len;
+        if (x >= w) {
+            y++;
+            if (y >= h)
+                break;
+            d += linesize;
+            x = 0;
+            /* byte align */
+            nibble_offset += (nibble_offset & 1);
+        }
+    }
+    return 0;
+}
+
+int DVDRingBufferPriv::get_nibble(const uint8_t *buf, int nibble_offset)
+{
+    return (buf[nibble_offset >> 1] >> ((1 - (nibble_offset & 1)) << 2)) & 0xf;
+}
+
+
 #endif // HAVE_DVDNAV
Index: RingBuffer.h
===================================================================
--- RingBuffer.h	(revision 8623)
+++ RingBuffer.h	(working copy)
@@ -5,6 +5,7 @@
 #include <qwaitcondition.h>
 #include <qmutex.h>
 #include <pthread.h>
+#include "avcodec.h"
 
 class RemoteFile;
 class RemoteEncoder;
@@ -82,6 +83,28 @@
     uint GetTotalTimeOfTitle(void);
     uint GetCellStart(void);
     long long GetTotalReadPosition(void);
+    bool InCellWithDVDStillFrame(void);
+    void DVDSkipStillFrame(void);
+    void DVDWaitSkip(void);
+    bool IsDVDWaiting(void);
+    bool IsInDVDMenu(void);
+    void GoToDVDRootMenu(void);
+    void GoToDVDNextProgram(void);
+    void GoToDVDPreviousProgram(void);
+    void MoveDVDButtonLeft(void);
+    void MoveDVDButtonRight(void);
+    void MoveDVDButtonUp(void);
+    void MoveDVDButtonDown(void);
+    void ActivateDVDButton(void);
+    int    NumDVDMenuButtons(void);
+    int NumPartsInDVDTitle(void);
+    void GetDVDMenuSPUPkt(uint8_t *buf, int buf_size);
+    AVSubtitleRect *GetDVDMenuButton(void);
+    bool IgnoringDVDStillorWait(void);
+    void IgnoreDVDStillOrWait(bool skip);
+    uint GetDVDCurrentTime(void);
+    bool HasDVDTitleChanged(void);
+    void SetDVDTitleChanged(bool change);
 
     long long SetAdjustFilesize(void);
     
Index: decoderbase.cpp
===================================================================
--- decoderbase.cpp	(revision 8623)
+++ decoderbase.cpp	(working copy)
@@ -715,4 +715,16 @@
     return (long long)(desiredFrame * multiplier);
 } 
 
+long long DecoderBase::DVDCurrentFrameNumber(void)
+{
+   if (!ringBuffer->isDVD())
+        return 0;
+
+   int size = m_positionMap.size() - 1;
+   long long currentpos = ringBuffer->GetReadPosition();
+   long long multiplier = (currentpos * m_positionMap[size].index);
+   long long currentframe = multiplier / m_positionMap[size].pos;
+   return currentframe;
+}
+
 /* vim: set expandtab tabstop=4 shiftwidth=4: */
