Index: NuppelVideoPlayer.h
===================================================================
--- NuppelVideoPlayer.h	(revision 8818)
+++ NuppelVideoPlayer.h	(working copy)
@@ -260,6 +260,7 @@
 
     // DVD public stuff
     void ChangeDVDTrack(bool ffw);
+    void ActivateDVDButton(void);
 
   protected:
     void DisplayPauseFrame(void);
@@ -363,6 +364,9 @@
     void  SwitchToProgram(void);
     void  JumpToProgram(void);
 
+   // Private DVD stuff
+   void DisplayDVDButton(void);
+
   private:
     VideoOutputType forceVideoOutput;
 
@@ -588,6 +592,9 @@
     LiveTVChain *livetvchain;
     TV *m_tv;
 
+    // DVD
+    bool indvdstillframe;
+
     // Debugging variables
     Jitterometer *output_jmeter;
 };
Index: NuppelVideoPlayer.cpp
===================================================================
--- NuppelVideoPlayer.cpp	(revision 8818)
+++ NuppelVideoPlayer.cpp	(working copy)
@@ -169,6 +169,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)
 {
@@ -813,7 +815,8 @@
             deleteIter = deleteMap.begin();
         }
     }
-    bookmarkseek = GetBookmark();
+    if (!ringBuffer->isDVD()) // need this til proper DVD bookmarking is implemented
+      bookmarkseek = GetBookmark();
 
     return IsErrored() ? -1 : 0;
 }
@@ -884,7 +887,8 @@
 void NuppelVideoPlayer::ReleaseNextVideoFrame(VideoFrame *buffer,
                                               long long timecode)
 {
-    WrapTimecode(timecode, TC_VIDEO);
+    if (!ringBuffer->isDVD())
+      WrapTimecode(timecode, TC_VIDEO);
     buffer->timecode = timecode;
 
     videoOutput->ReleaseFrame(buffer);
@@ -924,7 +928,8 @@
 void NuppelVideoPlayer::AddTextData(char *buffer, int len,
                                     long long timecode, char type)
 {
-    WrapTimecode(timecode, TC_CC);
+    if (!ringBuffer->isDVD())
+        WrapTimecode(timecode, TC_CC);
 
     if (subtitlesOn)
     {
@@ -1687,7 +1692,7 @@
     {
         // If audio is way ahead of video, adjust for it...
         // by cutting the frame rate in half for the length of this frame
-
+        
         avsync_adjustment = frame_interval;
         lastsync = true;
         VERBOSE(VB_PLAYBACK, LOC + 
@@ -1802,6 +1807,9 @@
         return;
     }
 
+    if (ringBuffer->IsInDVDMenu())
+        DisplayDVDButton();
+
     videofiltersLock.lock();
     videoOutput->ProcessFrame(NULL, osd, videoFilters, pipplayer);
     videofiltersLock.unlock();
@@ -1816,6 +1824,10 @@
     video_actually_paused = false;
     resetvideo = false;
 
+    if (ringBuffer->IsDVDWaiting() || ringBuffer->InCellWithDVDStillFrame())
+        goto displayframe;
+
+    
     prebuffering_lock.lock();
     if (prebuffering)
     {
@@ -1877,6 +1889,8 @@
     prebuffer_tries = 0;
     prebuffering_lock.unlock();
 
+displayframe:
+
     videoOutput->StartDisplayingFrame();
 
     VideoFrame *frame = videoOutput->GetLastShownFrame();
@@ -1915,6 +1929,9 @@
         yuv_wait.wakeAll();
     }
 
+    if (ringBuffer->IsInDVDMenu())
+        DisplayDVDButton();
+    
     if (subtitlesOn)
     {
         ShowText();
@@ -2007,6 +2024,29 @@
             needsetpipplayer = false;
         }
 
+        if (ringBuffer->isDVD())
+        {
+            int nbframes = videoOutput->ValidVideoFrames();
+            if (ringBuffer->InCellWithDVDStillFrame() && nbframes < 2)
+            {
+                indvdstillframe = true;
+                if (nbframes == 0)
+                    ringBuffer->Seek(ringBuffer->GetDVDCellStartPos(),SEEK_SET);
+                if (!pausevideo)
+                {
+                    PauseVideo(false);
+                    ringBuffer->HideDVDMenuButton(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);
@@ -2131,7 +2171,8 @@
 {
     ClearAfterSeek();
 
-    framesPlayed = 0;
+    if (!ringBuffer->isDVD())
+       framesPlayed = 0;
 
     GetDecoder()->Reset();
     errored |= GetDecoder()->IsErrored();
@@ -2764,7 +2805,8 @@
  */
 void NuppelVideoPlayer::AddAudioData(char *buffer, int len, long long timecode)
 {
-    WrapTimecode(timecode, TC_AUDIO);
+    if (!ringBuffer->isDVD())
+       WrapTimecode(timecode, TC_AUDIO);
 
     int samplesize = (audio_channels * audio_bits) / 8; // bytes per sample
     if ((samplesize <= 0) || !audioOutput)
@@ -2826,7 +2868,8 @@
 {
     char *buffers[2];
 
-    WrapTimecode(timecode, TC_AUDIO);
+    if (!ringBuffer->isDVD())
+       WrapTimecode(timecode, TC_AUDIO);
 
     if (!audioOutput)
         return;
@@ -4502,7 +4545,7 @@
     posInfo.progBefore = false;
     posInfo.progAfter = false;
 
-    if (ringBuffer->isDVD())
+    if (ringBuffer->IsInDVDMenu())
     {
         long long rPos = ringBuffer->GetReadPosition();
         long long tPos = 1;//ringBuffer->GetTotalReadPosition();
@@ -4527,7 +4570,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));
 
@@ -5075,3 +5125,58 @@
     ClearAfterSeek();
 }
 
+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;
+       uint btnX = ringBuffer->DVDButtonPosX();
+       uint btnY = ringBuffer->DVDButtonPosY();
+       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();
+       QImage scaledImage = hl_button.smoothScale(w+1,h+1);
+       image->SetPosition(QPoint(btnX-1, btnY+1));
+       image->LoadFromQImage(scaledImage);
+       subtitleOSD->AddType(image);
+       osd->SetVisible(subtitleOSD,0);
+   }
+   subtitleLock.unlock();
+}
+
+void NuppelVideoPlayer::ActivateDVDButton(void)
+{
+    if (!ringBuffer->isDVD())
+       return;
+    
+    ringBuffer->ActivateDVDButton();
+    osd->HideSet("subtitles");
+    osd->ClearAll("subtitles");
+    ringBuffer->HideDVDMenuButton(true);
+}
+
Index: RingBuffer.cpp
===================================================================
--- RingBuffer.cpp	(revision 8818)
+++ RingBuffer.cpp	(working copy)
@@ -1212,3 +1212,268 @@
     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);
+}
+
+/** \fn RingBuffer::GetDVDCellStartPos(void)
+ *  \brief Calls DVDRingBufferPriv::GetDVDCellStartPos(void)
+ */
+long long RingBuffer::GetDVDCellStartPos(void)
+{
+   if (dvdPriv)
+       return dvdPriv->GetCellStartPos();    
+   return false;
+}
+
+/** \fn RingBuffer::HideDVDMenuButton(bool hide)
+ *  \brief Calls DVDRingBufferPriv::HideMenuButton(bool hide)
+ */
+void RingBuffer::HideDVDMenuButton(bool hide)
+{
+   if (dvdPriv)
+       dvdPriv->HideMenuButton(hide);
+}
+
+/** \fn RingBuffer::DVDButtonPosX(void)
+ *  \brief Calls DVDRingBufferPriv::ButtonPosX(void)
+ */
+uint RingBuffer::DVDButtonPosX(void)
+{
+   if (dvdPriv)
+       return dvdPriv->ButtonPosX();
+   return 0;
+}
+
+/** \fn RingBuffer::DVDButtonPosY(void)
+ *  \brief Calls DVDRingBufferPriv::ButtonPosY(void)
+ */
+uint RingBuffer::DVDButtonPosY(void)
+{
+   if (dvdPriv)
+       return dvdPriv->ButtonPosY();
+   return 0;
+}
+
+/** \fn RingBuffer::GetDVDAudioLanguage(int stream_id)
+ *  \brief Calls DVDRingBufferPriv::GetAudioLanguage(stream_id)
+ */
+uint RingBuffer::GetDVDAudioLanguage(int id)
+{
+   if (dvdPriv)
+       return dvdPriv->GetAudioLanguage(id);
+   return 0;
+}
+
+/** \fn RingBuffer::GetDVDSubtitleLanguage(int stream_id)
+ *  \brief Calls DVDRingBufferPriv::GetSubtitleLanguage(stream_id)
+ */
+uint RingBuffer::GetDVDSubtitleLanguage(int id)
+{
+   if (dvdPriv)
+       return dvdPriv->GetSubtitleLanguage(id);
+   return 0;
+}
Index: DVDRingBuffer.h
===================================================================
--- DVDRingBuffer.h	(revision 8818)
+++ 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,13 +58,26 @@
     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; }
+    long long GetCellStartPos(void) { return cellstartPos; }
+    void HideMenuButton(bool hide);
+    uint ButtonPosX(void) { return hl_startx; }
+    uint ButtonPosY(void) { return hl_starty; }
+    uint GetAudioLanguage(int id);
+    uint GetSubtitleLanguage(int id);
+    
     // commands
     bool OpenFile(const QString &filename);
     void close(void);
@@ -70,6 +85,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 +113,44 @@
     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];
+    uint8_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;
+    long long cellstartPos;
+    bool buttonSelected;
+    
+    bool DrawMenuButton(uint8_t *spu_pkt, int buf_size);
+    bool DVDButtonUpdate(bool b_mode);
+    void ClearMenuSPUParameters(void);
+    bool MenuButtonChanged(void);
+    // would be nice if iso639.h had this function..
+    uint ConvertLangCode(uint16_t code); // converts 2char key to 3char key
+    
+    /* 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 8818)
+++ avformatdecoder.cpp	(working copy)
@@ -424,6 +424,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)
@@ -478,8 +482,11 @@
 
         prevgoppos = 0;
         gopset = false;
-        framesPlayed = lastKey;
-        framesRead = lastKey; 
+        if (!ringBuffer->isDVD())
+        {
+           framesPlayed = lastKey;
+           framesRead = lastKey;
+        }
     }
 
     // Skip all the desired number of skipFrames
@@ -511,6 +518,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
@@ -643,7 +657,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;
@@ -725,7 +742,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. " +
@@ -739,7 +770,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
@@ -1838,7 +1871,12 @@
 
     for (uint i = 0; i < audioStreams.size(); i++)
     {
-        QString msg  = iso639_key_toName(audioStreams[i].language);
+        QString msg;
+        if (ringBuffer->isDVD())
+            msg = iso639_key_toName(ringBuffer->GetDVDAudioLanguage(i));
+        else
+            msg = iso639_key_toName(audioStreams[i].language);
+            
         AVStream *s  = ic->streams[audioStreams[i].av_stream_index];
         if (s)
         {
@@ -2081,7 +2119,11 @@
 
     for (uint i = 0; i < subtitleStreams.size(); i++)
     {
-        QString msg  = iso639_key_toName(subtitleStreams[i].language);
+        QString msg;
+        if (ringBuffer->isDVD())
+            msg = iso639_key_toName(ringBuffer->GetDVDSubtitleLanguage(i));
+        else
+            msg  = iso639_key_toName(subtitleStreams[i].language);
         list += QString("%1: %2").arg(i+1).arg(msg);
     }
 
@@ -2104,6 +2146,9 @@
  */
 bool AvFormatDecoder::autoSelectSubtitleTrack(void)
 {
+    if (ringBuffer->IsInDVDMenu())
+        return true;
+        
     uint numStreams = subtitleStreams.size();
 
     if ((currentSubtitleTrack >= 0) && 
@@ -2210,11 +2256,26 @@
             // disable audio request if there are no audio streams anymore
             onlyvideo = 1;
         }
-
+        
+        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));
+        } 
+                    
         if (gotvideo)
         {
+	        bool draindvdpkts = (ringBuffer->IsInDVDMenu());
             if (lowbuffers && onlyvideo == 0 && lastapts < lastvpts + 100 &&
-                lastapts > lastvpts - 10000)
+                lastapts > lastvpts - 10000 && !draindvdpkts)
             {
                 //cout << "behind: " << lastapts << " " << lastvpts << endl;
                 storevideoframes = true;
@@ -2275,6 +2336,15 @@
         if (pkt->dts != (int64_t)AV_NOPTS_VALUE)
             pts = (long long)(av_q2d(curstream->time_base) * pkt->dts * 1000);
 
+        if (ringBuffer->InCellWithDVDStillFrame() &&
+            curstream->codec->codec_type == CODEC_TYPE_AUDIO)
+        {
+            VERBOSE(VB_PLAYBACK, LOC + "Audio in DVD Still Frame not supported yet. "
+                    "Dropping Frame");
+            av_free_packet(pkt);
+            continue;
+        }
+        
         if (storevideoframes &&
             curstream->codec->codec_type == CODEC_TYPE_VIDEO)
         {
@@ -2506,8 +2576,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);
+                    }
                     avcodeclock.unlock();
 
                     if (ret < 0)
@@ -2606,7 +2682,9 @@
                     int gotSubtitles = 0;
                     AVSubtitle subtitle;
 
-                    if (pkt->stream_index == subIdx)
+                    if (ringBuffer->IsInDVDMenu())
+                        ringBuffer->GetDVDMenuSPUPkt(ptr,len);
+                    else if (pkt->stream_index == subIdx)
                     {
                         QMutexLocker locker(&avcodeclock);
                         avcodec_decode_subtitle(curstream->codec,
Index: decoderbase.h
===================================================================
--- decoderbase.h	(revision 8818)
+++ 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 8818)
+++ 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",
@@ -1155,6 +1156,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]");
@@ -2115,7 +2122,8 @@
                 AddKeyToInputQueue(0);
             }            
         }
-        else if (action == "SEEKFFWD")
+        else if (action == "SEEKFFWD" &&
+                   !prbuffer->IsInDVDMenu())
         {
             if (HasQueuedInput())
                 DoArbSeek(ARBSEEK_FORWARD);
@@ -2131,7 +2139,8 @@
             else
                 ChangeFFRew(1);
         }
-        else if (action == "FFWDSTICKY")
+        else if (action == "FFWDSTICKY" &&
+                   !prbuffer->IsInDVDMenu())
         {
             if (HasQueuedInput())
                 DoArbSeek(ARBSEEK_END);
@@ -2140,7 +2149,8 @@
             else
                 ChangeFFRew(1);
         }
-        else if (action == "SEEKRWND")
+        else if (action == "SEEKRWND" &&
+                   !prbuffer->IsInDVDMenu())
         {
             if (HasQueuedInput())
                 DoArbSeek(ARBSEEK_REWIND);
@@ -2155,7 +2165,8 @@
             else
                 ChangeFFRew(-1);
         }
-        else if (action == "RWNDSTICKY")
+        else if (action == "RWNDSTICKY" &&
+                   !prbuffer->IsInDVDMenu())
         {
             if (HasQueuedInput())
                 DoArbSeek(ARBSEEK_SET);
@@ -2168,9 +2179,18 @@
         {
             if (prbuffer->isDVD())
             {
-                nvp->ChangeDVDTrack(0);
-                UpdateOSDSeekMessage(tr("Previous Chapter"),
-                                     osd_general_timeout);
+                if (prbuffer->NumPartsInDVDTitle() < 2)
+                {
+                    prbuffer->GoToDVDPreviousProgram();
+                    UpdateOSDSeekMessage(tr("Previous Title/Chapter"), 
+                            osd_general_timeout);
+                }
+                else
+                {
+                    nvp->ChangeDVDTrack(0);
+                    UpdateOSDSeekMessage(tr("Previous Chapter"),
+                            osd_general_timeout);
+                }
             }
             else
             {
@@ -2181,8 +2201,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() > 2)
+                {
+                    nvp->ChangeDVDTrack(1);
+                    UpdateOSDSeekMessage(tr("Next Chapter"), 
+                            osd_general_timeout);
+                }
+                else 
+                {
+                   prbuffer->GoToDVDNextProgram();
+                   UpdateOSDSeekMessage(tr("Next Title"), 
+                           osd_general_timeout);
+                }
             }
             else
             {
@@ -2452,8 +2488,25 @@
             QString action = actions[i];
             handled = true;
 
-            if (action == "SELECT")
+            if (prbuffer->IsInDVDMenu())
             {
+               int nb_buttons = prbuffer->NumDVDMenuButtons();
+               if (nb_buttons > 0)
+               {
+                  if (action == "UP" || action == "CHANNELUP")
+                      prbuffer->MoveDVDButtonUp();
+                  else if (action == "DOWN" || action == "CHANNELDOWN")
+                      prbuffer->MoveDVDButtonDown();
+                  else if (action == "LEFT" || action == "SEEKRWND")
+                      prbuffer->MoveDVDButtonLeft();
+                  else if (action == "RIGHT" || action == "SEEKFFWD")
+                      prbuffer->MoveDVDButtonRight();
+                  else if (action == "SELECT")
+                      nvp->ActivateDVDButton();
+               }
+            }
+            else if (action == "SELECT")
+            {
                 if (!was_doing_ff_rew)
                 {
                     if (gContext->GetNumSetting("AltClearSavedPosition", 1)
@@ -2475,13 +2528,18 @@
                 exitPlayer = true;
                 wantsToQuit = true;
             }
+            else if (action == "JUMPTODVDMENU")
+            {
+                prbuffer->GoToDVDRootMenu();
+            }
             else if (action == "TOGGLEEDIT")
                 DoEditMode();
             else if (action == "TOGGLEBROWSE")
                 ShowOSDTreeMenu();
             else if (action == "CHANNELUP")
             {
-                if (prbuffer->isDVD())
+                if (prbuffer->isDVD() &&
+                       !prbuffer->IsInDVDMenu())
                 {
                     nvp->ChangeDVDTrack(0);
                     UpdateOSDSeekMessage(tr("Previous Chapter"),
@@ -2494,7 +2552,8 @@
             }    
             else if (action == "CHANNELDOWN")
             {
-                if (prbuffer->isDVD())
+                if (prbuffer->isDVD() &&
+                       !prbuffer->IsInDVDMenu())
                 {
                     nvp->ChangeDVDTrack(1);
                     UpdateOSDSeekMessage(tr("Next Chapter"),
@@ -2965,6 +3024,9 @@
 
 void TV::DoPause(void)
 {
+    if (prbuffer->IsInDVDMenu())
+       return;
+
     speed_index = 0;
     float time = 0.0;
 
@@ -5405,7 +5467,9 @@
     }
     else if (StateIsPlaying(internalState))
     {
-        if (action == "TOGGLEEDIT")
+        if (action == "JUMPTODVDMENU")
+            prbuffer->GoToDVDRootMenu();
+        else if (action == "TOGGLEEDIT")
             DoEditMode();
         else if (action == "TOGGLEAUTOEXPIRE")
             ToggleAutoExpire();
@@ -5496,6 +5560,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 8818)
+++ DVDRingBuffer.cpp	(working copy)
@@ -3,6 +3,7 @@
 #include "DVDRingBuffer.h"
 #include "mythcontext.h"
 #include <dvdnav/nav_read.h>
+#include "iso639.h"
 
 #define LOC QString("DVDRB: ")
 #define LOC_ERR QString("DVDRB, Error: ")
@@ -25,9 +26,15 @@
       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),cellstartPos(0),
+      buttonSelected(false)
 {
+    dvdMenuButton = (AVSubtitleRect*)av_mallocz(sizeof(AVSubtitleRect));
 }
 
 DVDRingBufferPriv::~DVDRingBufferPriv()
@@ -84,8 +91,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,18 +118,10 @@
                 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);
+        dvdnav_current_title_info(dvdnav, &title, &part);  
         return true;
     }
 }
@@ -140,17 +137,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;
@@ -214,13 +200,34 @@
                         .arg(pgLength).arg(pgcLength)
                         .arg(cellStart).arg(pgStart));
 
-                dvdnav_current_title_info(dvdnav, &title, &part);            
-                if (title == 0)
+                if (dvdnav_get_next_still_flag(dvdnav) > 0)
                 {
-                    pci_t* pci = dvdnav_get_current_nav_pci(dvdnav);
-                    dvdnav_button_select(dvdnav, pci, 1);
+                   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;
+                	
+		         cellstartPos = GetReadPosition();
+                buttonSelected = false;
+
                 if (blockBuf != dvdBlockWriteBuf)
                 {
                     dvdnav_free_cache_block(dvdnav, blockBuf);
@@ -228,6 +235,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 +249,13 @@
                                 "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 +279,23 @@
             break;
             case DVDNAV_NAV_PACKET:
                 lastNav = (dvdnav_t *)blockBuf;
+                if (IsInMenu() && NumMenuButtons() > 0 && !buttonSelected)
+                {
+                   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);
+                   buttonSelected = true;
+                   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 +334,42 @@
                     dvdnav_free_cache_block(dvdnav, blockBuf);
                 }                                                   
 
+                if (DVDButtonUpdate(false))
+                   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,7 +391,8 @@
 bool DVDRingBufferPriv::nextTrack(void)
 {
     int newPart = part + 1;
-    if (newPart < maxPart)
+    
+    if (newPart < titleParts)
     {
         dvdnav_part_play(dvdnav, title, newPart);
         gotStop = false;
@@ -374,4 +422,446 @@
     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 (buf_size == menuBuflength)
+        return;
+    else 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;
+    buttonCoords = 0;
+    
+    if (DVDButtonUpdate(false))
+        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 - x1;
+                    dvdMenuButton->y  = hl_starty - y1;
+                    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 (menuBuflength == 0)
+   return;
+
+   VERBOSE(VB_PLAYBACK,"Clearing Menu SPU Packet" );
+   if (DrawMenuButton(menuSpuPkt,menuBuflength))
+   {
+       av_free(dvdMenuButton->rgba_palette);
+       av_free(dvdMenuButton->bitmap);
+   }  
+   av_free(menuSpuPkt);
+   menuBuflength = 0;
+   dvdMenuButton->x = 0;
+   dvdMenuButton->y = 0;
+   hl_startx = hl_starty = 0;
+   hl_width = hl_height = 0;
+   buttonCoords = (720+480+100);
+}
+
+bool DVDRingBufferPriv::MenuButtonChanged(void)
+{
+   if (menuBuflength < 4 || buttonCoords > (720+576))
+       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;
+    if (numButtons > 0 && numButtons < 36) 
+       return  numButtons;
+    else
+       return 0;
+}
+
+void DVDRingBufferPriv::HideMenuButton(bool hide)
+{
+    if (hide)
+       buttonCoords = (720+480+100);
+    else
+       buttonCoords = 0;
+}
+
+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;
+}
+
+uint DVDRingBufferPriv::GetAudioLanguage(int id)
+{
+    int8_t channel = dvdnav_get_audio_logical_stream(dvdnav,id);
+    uint16_t lang = 0;
+    if (channel != -1)
+        lang = dvdnav_audio_stream_to_lang(dvdnav,channel);
+    return ConvertLangCode(lang);
+}
+
+uint DVDRingBufferPriv::GetSubtitleLanguage(int id)
+{
+    int8_t channel = dvdnav_get_spu_logical_stream(dvdnav,id);
+    uint16_t lang = 0;
+    if (channel != -1)
+        lang = dvdnav_spu_stream_to_lang(dvdnav,channel);
+    return ConvertLangCode(lang);
+}
+
+uint DVDRingBufferPriv::ConvertLangCode(uint16_t code)
+{
+   if (code == 0)
+        return 0;
+
+   QChar str2[2];
+   str2[0] = QChar(code >> 8);
+   str2[1] = QChar(code & 0xff);
+   QString str3 = iso639_str2_to_str3(QString(str2,2));
+   if (str3)
+     return iso639_str3_to_key(str3);
+   return 0;
+}
+
+void DVDRingBufferPriv::guess_palette(uint32_t *rgba_palette,uint8_t *palette,
+                                        uint8_t *alpha)
+{
+    int i,r,g,b,y,cr,cb;
+    uint32_t yuv;
+
+    for(i = 0; i < 4; i++)
+        rgba_palette[i] = 0;
+
+    for ( i=0 ; i < 4 ; i++)
+    {
+        yuv = clut[palette[i]];
+        y = ((yuv >> 16) & 0xff);
+        cr = ((yuv >> 8) & 0xff);
+        cb = ((yuv >> 0) & 0xff);
+        r  = int(y + 1.4022 * (cr - 128));
+        b  = int(y + 1.7710 * (cb - 128));
+        g  = int(1.7047 * y - (0.1952 * b) - (0.5647 * r)) ;
+        if (r < 0) r = 0;
+        if (g < 0) g = 0;
+        if (b < 0) b = 0;
+        if (r > 0xff) r = 0xff;
+        if (g > 0xff) g = 0xff;
+        if (b > 0xff) b = 0xff;
+        rgba_palette[i] = ((alpha[i] * 17) << 24) | (r << 16 )| (g << 8) | b;
+    }
+}
+
+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 8818)
+++ 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,34 @@
     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 GetDVDCellStartPos(void);
+    void HideDVDMenuButton(bool hide);
+    uint DVDButtonPosX(void);
+    uint DVDButtonPosY(void);
+    uint GetDVDAudioLanguage(int id);
+    uint GetDVDSubtitleLanguage(int id);
 
     long long SetAdjustFilesize(void);
     
Index: decoderbase.cpp
===================================================================
--- decoderbase.cpp	(revision 8818)
+++ 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: */
