Index: libs/libmythtv/NuppelVideoPlayer.h
===================================================================
--- libs/libmythtv/NuppelVideoPlayer.h	(revision 8948)
+++ libs/libmythtv/NuppelVideoPlayer.h	(working copy)
@@ -261,6 +261,9 @@
 
     // DVD public stuff
     void ChangeDVDTrack(bool ffw);
+    void ActivateDVDButton(void);
+    void GoToDVDMenu(QString str);
+    void GoToDVDProgram(bool direction);
 
   protected:
     void DisplayPauseFrame(void);
@@ -364,6 +367,9 @@
     void  SwitchToProgram(void);
     void  JumpToProgram(void);
 
+    // Private DVD stuff
+    void DisplayDVDButton(void);
+
   private:
     VideoOutputType forceVideoOutput;
 
@@ -590,6 +596,9 @@
     LiveTVChain *livetvchain;
     TV *m_tv;
 
+    // DVD
+    bool indvdstillframe;
+
     // Debugging variables
     Jitterometer *output_jmeter;
 };
Index: libs/libmythtv/NuppelVideoPlayer.cpp
===================================================================
--- libs/libmythtv/NuppelVideoPlayer.cpp	(revision 8948)
+++ libs/libmythtv/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)
 {
@@ -481,6 +483,9 @@
     vidExitLock.unlock();
 
     ClearAfterSeek();
+
+    if (ringBuffer->InDVDMenuOrStillFrame())
+        ringBuffer->Seek(ringBuffer->GetDVDCellStartPos(),SEEK_SET);
 }
 
 QString NuppelVideoPlayer::ReinitAudio(void)
@@ -814,7 +819,9 @@
             deleteIter = deleteMap.begin();
         }
     }
-    bookmarkseek = GetBookmark();
+    
+    if (!ringBuffer->isDVD()) // need this til proper DVD bookmarking is implemented
+        bookmarkseek = GetBookmark();
 
     return IsErrored() ? -1 : 0;
 }
@@ -885,7 +892,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);
@@ -925,7 +933,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)
     {
@@ -1803,6 +1812,9 @@
         return;
     }
 
+    if (ringBuffer->InDVDMenuOrStillFrame())
+        DisplayDVDButton();
+
     videofiltersLock.lock();
     videoOutput->ProcessFrame(NULL, osd, videoFilters, pipplayer);
     videofiltersLock.unlock();
@@ -1817,6 +1829,9 @@
     video_actually_paused = false;
     resetvideo = false;
 
+    if (ringBuffer->isDVD())
+        goto displayframe;
+   
     prebuffering_lock.lock();
     if (prebuffering)
     {
@@ -1878,6 +1893,8 @@
     prebuffer_tries = 0;
     prebuffering_lock.unlock();
 
+displayframe:
+
     videoOutput->StartDisplayingFrame();
 
     VideoFrame *frame = videoOutput->GetLastShownFrame();
@@ -1916,6 +1933,9 @@
         yuv_wait.wakeAll();
     }
 
+    if (ringBuffer->InDVDMenuOrStillFrame())
+        DisplayDVDButton();
+    
     if (subtitlesOn)
     {
         ShowText();
@@ -2008,6 +2028,42 @@
             needsetpipplayer = false;
         }
 
+        if (ringBuffer->isDVD())
+        {
+            int nbframes = videoOutput->ValidVideoFrames();
+            if (nbframes < 2) 
+            {
+                if (ringBuffer->IsDVDWaiting())
+                {
+                    ringBuffer->DVDWaitSkip();
+                    usleep(100000);
+                    continue;
+                }
+                if (ringBuffer->InDVDMenuOrStillFrame())
+                {
+                    if (nbframes == 0)
+                    {
+                        // wait to confirm there is no other frames been decoded.
+                        usleep(100000);
+                        nbframes = videoOutput->ValidVideoFrames();
+                        if (nbframes == 0)
+                        {
+                            ringBuffer->Seek(ringBuffer->GetDVDCellStartPos(),SEEK_SET);
+                            continue;
+                        }
+                    }
+                    indvdstillframe = true;
+                    if (!pausevideo && nbframes == 1)
+                        PauseVideo(false);
+                }
+            }
+            // restart playing after skipping still frame
+            if (indvdstillframe && nbframes > 1)
+            {
+                UnpauseVideo();
+                indvdstillframe = false;
+            } 
+        }
         if (pausevideo)
         {
             usleep(frame_interval);
@@ -2132,7 +2188,8 @@
 {
     ClearAfterSeek();
 
-    framesPlayed = 0;
+    if (!ringBuffer->isDVD())
+       framesPlayed = 0;
 
     GetDecoder()->Reset();
     errored |= GetDecoder()->IsErrored();
@@ -2777,14 +2834,21 @@
  */
 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)
         return;
 
     int samples = len / samplesize;
 
+    if (ringBuffer->isDVD())
+    {
+        audioOutput->Pause(false);
+        audioOutput->Drain();
+    }
+
     // If there is no warping, just send it to the audioOutput.
     if (!usevideotimebase)
     {
@@ -4551,7 +4615,7 @@
     posInfo.progBefore = false;
     posInfo.progAfter = false;
 
-    if (ringBuffer->isDVD())
+    if (ringBuffer->IsInDVDMenu())
     {
         long long rPos = ringBuffer->GetReadPosition();
         long long tPos = 1;//ringBuffer->GetTotalReadPosition();
@@ -4576,7 +4640,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));
 
@@ -5120,7 +5191,97 @@
        return;
 
     GetDecoder()->ChangeDVDTrack(ffw);
-    usleep(100000);
     ClearAfterSeek();
 }
 
+void NuppelVideoPlayer::DisplayDVDButton(void)
+{
+    if (!ringBuffer->InDVDMenuOrStillFrame())
+        return;
+
+    long long spupts = ringBuffer->DVDMenuSpuPts();
+    long long ptslow = spupts - 100;
+    long long ptshigh = spupts + 100;
+    VideoFrame *buffer = videoOutput->GetLastShownFrame();
+    if (!osd->IsSetDisplaying("subtitles") &&
+            (buffer->timecode > 0) &&
+            ((buffer->timecode < ptslow) || 
+             (buffer->timecode > ptshigh)))
+    {
+        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;
+
+    if (videoOutput->ValidVideoFrames() > 20)
+        DiscardVideoFrames(true);
+    ringBuffer->ActivateDVDButton();
+    ringBuffer->HideDVDMenuButton(true);
+}
+
+void NuppelVideoPlayer::GoToDVDMenu(QString str)
+{
+    if (!ringBuffer->isDVD())
+        return;
+
+    if (videoOutput->ValidVideoFrames() > 20)
+        DiscardVideoFrames(true);
+    ringBuffer->GoToDVDMenu(str);
+}
+
+/** \fn NuppelVideoPlayer::GoToDVDProgram(bool direction)
+ *  \brief Go forward or back by one DVD program
+*/
+void NuppelVideoPlayer::GoToDVDProgram(bool direction)
+{
+    if (!ringBuffer->isDVD())
+        return;
+    
+    if (videoOutput->ValidVideoFrames() > 20)
+        DiscardVideoFrames(true);
+    if (direction == 0)
+        ringBuffer->GoToDVDPreviousProgram();
+    else
+        ringBuffer->GoToDVDNextProgram();
+}
+
Index: libs/libmythtv/RingBuffer.cpp
===================================================================
--- libs/libmythtv/RingBuffer.cpp	(revision 8948)
+++ libs/libmythtv/RingBuffer.cpp	(working copy)
@@ -1269,3 +1269,274 @@
     return 0;
 }
 
+/** \fn RingBuffer::InDVDStillFrame(void)
+ *  \brief Calls DVDRingBufferPriv::InStillFrame(void)
+ */
+bool RingBuffer::InDVDStillFrame(void)
+{
+    if (dvdPriv)
+        return dvdPriv->InStillFrame();
+    return false;
+}
+
+/** \fn RingBuffer::DVDSkipStillFrame(void)
+ *  \brief Calls DVDRingBufferPriv::SkipStillFrame(void)
+ */
+void RingBuffer::DVDSkipStillFrame(void)
+{
+    if (dvdPriv)
+        dvdPriv->SkipStillFrame();
+}
+
+/** \fn RingBuffer::DVDWaitSkip(void)
+ *  \brief Calls DVDRingBufferPriv::WaitSkip(void)
+ */
+void RingBuffer::DVDWaitSkip(void)
+{
+    if (dvdPriv)
+        dvdPriv->WaitSkip();
+}
+
+/** \fn RingBuffer::IsDVDWaiting(void)
+ *  \brief Calls DVDRingBufferPriv::IsWaiting(void)
+ */
+bool RingBuffer::IsDVDWaiting(void)
+{
+    if (dvdPriv)
+        return dvdPriv->IsWaiting();
+    return false;
+}
+
+/** \fn RingBuffer::IsInDVDMenu(void)
+ *  \brief Calls DVDRingBufferPriv::IsInMenu(void)
+ */
+bool RingBuffer::IsInDVDMenu(void)
+{
+    if (dvdPriv)
+        return dvdPriv->IsInMenu();
+    return false;
+}
+
+/** \fn RingBuffer::GoToDVDMenu(QString str)
+ *  \brief Calls DVDRingBufferPriv::GoToMenu(str)
+ */
+void RingBuffer::GoToDVDMenu(QString str)
+{
+    if (dvdPriv)
+        dvdPriv->GoToMenu(str);
+}
+
+/** \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::MoveButtonLeft(void)
+ */
+void RingBuffer::MoveDVDButtonLeft(void)
+{
+    if (dvdPriv)
+        dvdPriv->MoveButtonLeft();
+}
+
+/** \fn RingBuffer::MoveDVDButtonRight(void)
+ *  \brief Calls DVDRingBufferPriv::MoveButtonRight(void)
+ */
+void RingBuffer::MoveDVDButtonRight(void)
+{
+    if (dvdPriv)
+        dvdPriv->MoveButtonRight();
+}
+
+/** \fn RingBuffer::MoveDVDButtonUp(void)
+ *  \brief Calls DVDRingBufferPriv::MoveButtonUp(void)
+ */
+void RingBuffer::MoveDVDButtonUp(void)
+{
+    if (dvdPriv)
+        dvdPriv->MoveButtonUp();
+}
+
+/** \fn RingBuffer::MoveDVDButtonDown(void)
+ *  \brief Calls DVDRingBufferPriv::MoveButtonDown(void)
+ */
+void RingBuffer::MoveDVDButtonDown(void)
+{
+    if (dvdPriv)
+        dvdPriv->MoveButtonDown();
+}
+
+/** \fn RingBuffer::ActivateDVDButton(void)
+ *  \brief Calls DVDRingBufferPriv::ActivateButton(void)
+ */
+void RingBuffer::ActivateDVDButton(void)
+{
+    if (dvdPriv)
+        dvdPriv->ActivateButton();
+}
+
+/** \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,long long pts)
+ *  \brief Calls DVDRingBufferPriv::GetMenuSPUPkt()
+ */
+void RingBuffer::GetDVDMenuSPUPkt(uint8_t *buf, int buf_size,long long pts)
+{
+    if (dvdPriv)
+        dvdPriv->GetMenuSPUPkt(buf,buf_size,pts);
+}
+
+/** \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::IgnoringStillorWait()
+ */
+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::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;
+}
+
+bool RingBuffer::InDVDMenuOrStillFrame(void)
+{
+    if (dvdPriv)
+        return (IsInDVDMenu() || InDVDStillFrame());
+    return false;
+}
+
+/** \fn RingBuffer::DVDMenuSpuPts(void)
+ *  \brief Calls DVDRingBufferPriv::MenuSpuPts()
+ */
+long long RingBuffer::DVDMenuSpuPts(void)
+{
+    if (dvdPriv)
+        return dvdPriv->MenuSpuPts();
+    return 0;
+}
+
+int RingBuffer::DVDCellChange(void)
+{
+    if (dvdPriv)
+        return dvdPriv->CellChange();
+    return 0;
+}
+
Index: libs/libmythtv/DVDRingBuffer.h
===================================================================
--- libs/libmythtv/DVDRingBuffer.h	(revision 8948)
+++ libs/libmythtv/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,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 InStillFrame(void) { return cellHasStillFrame; }
+    bool IsWaiting(void) { return dvdWaiting; }
+    int    NumPartsInTitle(void) { return titleParts; }
+    void GetMenuSPUPkt(uint8_t *buf, int buf_size,long long pts);
+    AVSubtitleRect *GetMenuButton(void);
+    bool IgnoringStillorWait(void) { return skipstillorwait; }
+    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);
+    long long MenuSpuPts(void) { return menuspupts; }
+    int CellChange(void) { return cellChange; }
 
     // commands
     bool OpenFile(const QString &filename);
@@ -70,6 +86,19 @@
     void prevTrack(void);
     int  safe_read(void *data, unsigned sz);
     long long Seek(long long pos, int whence);
+    void SkipStillFrame(void);
+    void WaitSkip(void);
+    void GoToMenu(const QString str);
+    void GoToNextProgram(void);
+    void GoToPreviousProgram(void);
+    void MoveButtonLeft(void);
+    void MoveButtonRight(void);
+    void MoveButtonUp(void);
+    void MoveButtonDown(void);
+    void ActivateButton(void);
+    int NumMenuButtons(void);
+    void IgnoreStillOrWait(bool skip) { skipstillorwait = skip; }
+    uint GetCurrentTime(void);
         
   protected:
     dvdnav_t      *dvdnav;
@@ -84,9 +113,45 @@
     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;
+    long long cellstartPos;
+    bool buttonSelected;
+    bool buttonExists;
+    long long menuspupts;
+    int cellChange;
+
+    bool DrawMenuButton(uint8_t *spu_pkt, int buf_size);
+    bool DVDButtonUpdate(bool b_mode);
+    void ClearMenuSPUParameters(void);
+    bool MenuButtonChanged(void);
+    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: libs/libmythtv/avformatdecoder.cpp
===================================================================
--- libs/libmythtv/avformatdecoder.cpp	(revision 8948)
+++ libs/libmythtv/avformatdecoder.cpp	(working copy)
@@ -272,7 +272,10 @@
       // Subtitle selection
       wantedSubtitleStream(),       selectedSubtitleStream(),
       // language preference
-      languagePreference(iso639_get_language_key_list())
+      languagePreference(iso639_get_language_key_list()),
+      // DVD
+      lastdvdtitle(0), lastdvdpart(0),
+      lastdvdcellchange(0)
 {
     bzero(&params, sizeof(AVFormatParameters));
     bzero(prvpkt, 3 * sizeof(char));
@@ -424,6 +427,13 @@
 void AvFormatDecoder::SeekReset(long long newKey, uint skipFrames,
                                 bool doflush, bool discardFrames)
 {
+    if (ringBuffer->isDVD())
+    {
+        int totaltime = ringBuffer->GetTotalTimeOfTitle();
+        if (totaltime < 30 || ringBuffer->InDVDMenuOrStillFrame())
+            return;
+    }
+        
     VERBOSE(VB_PLAYBACK, LOC +
             QString("SeekReset(%1, %2, %3 flush, %4 discard)")
             .arg(newKey).arg(skipFrames)
@@ -478,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
@@ -511,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
@@ -643,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;
@@ -725,7 +748,19 @@
         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 +774,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
@@ -1842,7 +1879,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)
         {
@@ -2085,7 +2127,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);
     }
 
@@ -2108,6 +2154,9 @@
  */
 bool AvFormatDecoder::autoSelectSubtitleTrack(void)
 {
+    if (ringBuffer->InDVDMenuOrStillFrame())
+        return true;
+        
     uint numStreams = subtitleStreams.size();
 
     if ((currentSubtitleTrack >= 0) && 
@@ -2197,6 +2246,7 @@
 
     bool allowedquit = false;
     bool storevideoframes = false;
+    bool dvdvideopause = false;
 
     {
         QMutexLocker locker(&avcodeclock);
@@ -2214,11 +2264,44 @@
             // disable audio request if there are no audio streams anymore
             onlyvideo = 1;
         }
-
+        
+        if (ringBuffer->isDVD())
+        { 
+            int dvdtitle  = 0;
+            int dvdpart = 0;
+            ringBuffer->getPartAndTitle(dvdtitle,dvdpart);
+            int dvdcellchange = ringBuffer->DVDCellChange();
+            if ((dvdtitle != lastdvdtitle || dvdpart != lastdvdpart || 
+                        dvdcellchange != lastdvdcellchange))
+            {
+                GetNVP()->GetOSD()->HideSet("subtitles");
+                GetNVP()->GetOSD()->ClearAll("subtitles");
+            }
+            if (dvdtitle != lastdvdtitle)
+            {
+                posmapStarted = false;
+                m_positionMap.clear();
+                SyncPositionMap();
+                framesPlayed = DVDCurrentFrameNumber();
+                framesRead = framesPlayed;
+                VERBOSE(VB_PLAYBACK,
+                    QString(LOC + "DVD Title Changed. Update framesPlayed: %1 ")
+                            .arg(framesPlayed));
+            }
+            lastdvdtitle = dvdtitle;
+            lastdvdpart = dvdpart;
+            lastdvdcellchange = dvdcellchange;
+            
+            if (storedPackets.count() < 2 && !dvdvideopause)
+                storevideoframes = true;
+            else
+                storevideoframes = false;
+        }
+        
         if (gotvideo)
         {
             if (lowbuffers && onlyvideo == 0 && lastapts < lastvpts + 100 &&
-                lastapts > lastvpts - 10000)
+                lastapts > lastvpts - 10000 && !ringBuffer->isDVD())
             {
                 //cout << "behind: " << lastapts << " " << lastvpts << endl;
                 storevideoframes = true;
@@ -2230,6 +2313,13 @@
             }
         }
 
+        if (ringBuffer->InDVDStillFrame() && 
+                storedPackets.count() > 0)
+        {
+            storevideoframes = false;
+            dvdvideopause = true;
+        }
+     
         if (!storevideoframes && storedPackets.count() > 0)
         {
             if (pkt)
@@ -2279,6 +2369,13 @@
         if (pkt->dts != (int64_t)AV_NOPTS_VALUE)
             pts = (long long)(av_q2d(curstream->time_base) * pkt->dts * 1000);
 
+        if (ringBuffer->isDVD() && pkt->size == 4)
+        {
+            dvdvideopause = true;
+            av_free_packet(pkt);
+            continue;
+        }
+
         if (storevideoframes &&
             curstream->codec->codec_type == CODEC_TYPE_VIDEO)
         {
@@ -2481,6 +2578,9 @@
                     GetNVP()->AddAudioData((char *)audioSamples, data_size,
                                            temppts);
 
+                    if (ringBuffer->isDVD())
+                        allowedquit = true;
+
                     break;
                 }
                 case CODEC_TYPE_VIDEO:
@@ -2510,8 +2610,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 (dvdvideopause && storedPackets.count() == 0)
+                            ret = avcodec_decode_video(context, &mpa_pic,
+                                                        &gotpicture, ptr, len);
+                    }
                     avcodeclock.unlock();
 
                     if (ret < 0)
@@ -2610,7 +2716,9 @@
                     int gotSubtitles = 0;
                     AVSubtitle subtitle;
 
-                    if (pkt->stream_index == subIdx)
+                    if (ringBuffer->IsInDVDMenu())
+                        ringBuffer->GetDVDMenuSPUPkt(ptr,len,pts);
+                    else if (pkt->stream_index == subIdx)
                     {
                         QMutexLocker locker(&avcodeclock);
                         avcodec_decode_subtitle(curstream->codec,
Index: libs/libmythtv/decoderbase.h
===================================================================
--- libs/libmythtv/decoderbase.h	(revision 8948)
+++ libs/libmythtv/decoderbase.h	(working copy)
@@ -92,6 +92,7 @@
     // DVD public stuff
     void ChangeDVDTrack(bool ffw);
     long long DVDFindPosition(long long desiredFrame);
+    long long DVDCurrentFrameNumber(void);
 
   protected:
     void FileChanged(void);
Index: libs/libmythtv/tv_play.cpp
===================================================================
--- libs/libmythtv/tv_play.cpp	(revision 8948)
+++ libs/libmythtv/tv_play.cpp	(working copy)
@@ -150,7 +150,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", "JUMPTODVDROOTMENU", "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",
@@ -1163,6 +1164,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]");
@@ -2168,7 +2175,8 @@
                 AddKeyToInputQueue(0);
             }            
         }
-        else if (action == "SEEKFFWD")
+        else if (action == "SEEKFFWD" &&
+                !prbuffer->InDVDMenuOrStillFrame())
         {
             if (HasQueuedInput())
                 DoArbSeek(ARBSEEK_FORWARD);
@@ -2184,7 +2192,8 @@
             else
                 ChangeFFRew(1);
         }
-        else if (action == "FFWDSTICKY")
+        else if (action == "FFWDSTICKY" &&
+                !prbuffer->InDVDMenuOrStillFrame())
         {
             if (HasQueuedInput())
                 DoArbSeek(ARBSEEK_END);
@@ -2193,7 +2202,8 @@
             else
                 ChangeFFRew(1);
         }
-        else if (action == "SEEKRWND")
+        else if (action == "SEEKRWND" &&
+                !prbuffer->InDVDMenuOrStillFrame())
         {
             if (HasQueuedInput())
                 DoArbSeek(ARBSEEK_REWIND);
@@ -2208,7 +2218,8 @@
             else
                 ChangeFFRew(-1);
         }
-        else if (action == "RWNDSTICKY")
+        else if (action == "RWNDSTICKY" && 
+                !prbuffer->InDVDMenuOrStillFrame())
         {
             if (HasQueuedInput())
                 DoArbSeek(ARBSEEK_SET);
@@ -2221,9 +2232,18 @@
         {
             if (prbuffer->isDVD())
             {
-                nvp->ChangeDVDTrack(0);
-                UpdateOSDSeekMessage(tr("Previous Chapter"),
-                                     osd_general_timeout);
+                if (prbuffer->NumPartsInDVDTitle() < 2)
+                {
+                    nvp->GoToDVDProgram(0);
+                    UpdateOSDSeekMessage(tr("Previous Title/Chapter"),
+                            osd_general_timeout);
+                }
+                else
+                {
+                    nvp->ChangeDVDTrack(0);
+                    UpdateOSDSeekMessage(tr("Previous Chapter"),
+                            osd_general_timeout);
+                }
             }
             else
             {
@@ -2234,8 +2254,24 @@
         {
             if (prbuffer->isDVD())
             {
-                nvp->ChangeDVDTrack(1);
-                UpdateOSDSeekMessage(tr("Next Chapter"), osd_general_timeout);
+                if (prbuffer->InDVDStillFrame())
+                {
+                    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 
+                {
+                    nvp->GoToDVDProgram(1);
+                    UpdateOSDSeekMessage(tr("Next Title"), 
+                            osd_general_timeout);
+                }
             }
             else
             {
@@ -2513,8 +2549,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)
@@ -2536,6 +2589,8 @@
                 exitPlayer = true;
                 wantsToQuit = true;
             }
+            else if (action == "JUMPTODVDROOTMENU")
+                nvp->GoToDVDMenu("menu");
             else if (action == "GUIDE")
                 LoadMenu();
             else if (action == "FINDER")
@@ -2546,7 +2601,8 @@
                 ShowOSDTreeMenu();
             else if (action == "CHANNELUP")
             {
-                if (prbuffer->isDVD())
+                if (prbuffer->isDVD() &&
+                        !prbuffer->InDVDMenuOrStillFrame())
                 {
                     nvp->ChangeDVDTrack(0);
                     UpdateOSDSeekMessage(tr("Previous Chapter"),
@@ -2559,7 +2615,8 @@
             }    
             else if (action == "CHANNELDOWN")
             {
-                if (prbuffer->isDVD())
+                if (prbuffer->isDVD() &&
+                        !prbuffer->InDVDMenuOrStillFrame())
                 {
                     nvp->ChangeDVDTrack(1);
                     UpdateOSDSeekMessage(tr("Next Chapter"),
@@ -3034,6 +3091,9 @@
 
 void TV::DoPause(void)
 {
+    if (prbuffer->InDVDMenuOrStillFrame())
+        return;
+
     speed_index = 0;
     float time = 0.0;
 
@@ -5525,7 +5585,9 @@
     }
     else if (StateIsPlaying(internalState))
     {
-        if (action == "TOGGLEEDIT")
+        if (action == "JUMPTODVDROOTMENU")
+            nvp->GoToDVDMenu("menu");
+        else if (action == "TOGGLEEDIT")
             DoEditMode();
         else if (action == "TOGGLEAUTOEXPIRE")
             ToggleAutoExpire();
@@ -5616,42 +5678,47 @@
     }
     else if (StateIsPlaying(internalState))
     {
-        item = new OSDGenericTree(treeMenu, tr("Edit Recording"), "TOGGLEEDIT");
+        if (prbuffer->isDVD())
+            item = new OSDGenericTree(treeMenu,tr("DVD Root Menu"), "JUMPTODVDROOTMENU");
+        else if (!prbuffer->isDVD())
+        {
+            item = new OSDGenericTree(treeMenu, tr("Edit Recording"), "TOGGLEEDIT");
 
-        if (lastProgram != NULL)
-            item = new OSDGenericTree(treeMenu, tr("Previous Recording"), "JUMPPREV");
+            if (lastProgram != NULL)
+                item = new OSDGenericTree(treeMenu, tr("Previous Recording"), "JUMPPREV");
 
-        pbinfoLock.lock();
+            pbinfoLock.lock();
 
-        if (JobQueue::IsJobQueuedOrRunning(JOB_TRANSCODE,
+            if (JobQueue::IsJobQueuedOrRunning(JOB_TRANSCODE,
                                    playbackinfo->chanid, playbackinfo->startts))
-            item = new OSDGenericTree(treeMenu, tr("Stop Transcoding"), "QUEUETRANSCODE");
-        else
-            item = new OSDGenericTree(treeMenu, tr("Begin Transcoding"), "QUEUETRANSCODE");
+                item = new OSDGenericTree(treeMenu, tr("Stop Transcoding"), "QUEUETRANSCODE");
+            else
+                item = new OSDGenericTree(treeMenu, tr("Begin Transcoding"), "QUEUETRANSCODE");
 
 
-        item = new OSDGenericTree(treeMenu, tr("Commercial Auto-Skip"));
-        subitem = new OSDGenericTree(item, tr("Auto-Skip OFF"),
-                                     "TOGGLECOMMSKIP0",
-                                     (autoCommercialSkip == 0) ? 1 : 0, NULL,
-                                     "COMMSKIPGROUP");
-        subitem = new OSDGenericTree(item, tr("Auto-Skip Notify"),
-                                     "TOGGLECOMMSKIP2",
-                                     (autoCommercialSkip == 2) ? 1 : 0, NULL,
-                                     "COMMSKIPGROUP");
-        subitem = new OSDGenericTree(item, tr("Auto-Skip ON"),
-                                     "TOGGLECOMMSKIP1",
-                                     (autoCommercialSkip == 1) ? 1 : 0, NULL,
-                                     "COMMSKIPGROUP");
+            item = new OSDGenericTree(treeMenu, tr("Commercial Auto-Skip"));
+            subitem = new OSDGenericTree(item, tr("Auto-Skip OFF"),
+                                        "TOGGLECOMMSKIP0",
+                                        (autoCommercialSkip == 0) ? 1 : 0, NULL,
+                                        "COMMSKIPGROUP");
+            subitem = new OSDGenericTree(item, tr("Auto-Skip Notify"),
+                                         "TOGGLECOMMSKIP2",
+                                         (autoCommercialSkip == 2) ? 1 : 0, NULL,
+                                         "COMMSKIPGROUP");
+            subitem = new OSDGenericTree(item, tr("Auto-Skip ON"),
+                                         "TOGGLECOMMSKIP1",
+                                         (autoCommercialSkip == 1) ? 1 : 0, NULL,
+                                         "COMMSKIPGROUP");
 
-        if (playbackinfo->GetAutoExpireFromRecorded())
-            item = new OSDGenericTree(treeMenu, tr("Turn Auto-Expire OFF"),
-                                      "TOGGLEAUTOEXPIRE");
-        else
-            item = new OSDGenericTree(treeMenu, tr("Turn Auto-Expire ON"),
-                                      "TOGGLEAUTOEXPIRE");
+            if (playbackinfo->GetAutoExpireFromRecorded())
+                item = new OSDGenericTree(treeMenu, tr("Turn Auto-Expire OFF"),
+                                          "TOGGLEAUTOEXPIRE");
+            else
+                item = new OSDGenericTree(treeMenu, tr("Turn Auto-Expire ON"),
+                                          "TOGGLEAUTOEXPIRE");
 
-        pbinfoLock.unlock();
+            pbinfoLock.unlock();
+        }
     }
     
     const QStringList atracks = activenvp->listAudioTracks();
Index: libs/libmythtv/DVDRingBuffer.cpp
===================================================================
--- libs/libmythtv/DVDRingBuffer.cpp	(revision 8948)
+++ libs/libmythtv/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,16 @@
       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),
+      cellstartPos(0), buttonSelected(false), 
+      buttonExists(false), menuspupts(0),
+      cellChange(0)
 {
+    dvdMenuButton = (AVSubtitleRect*)av_mallocz(sizeof(AVSubtitleRect));
 }
 
 DVDRingBufferPriv::~DVDRingBufferPriv()
@@ -84,8 +92,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 +119,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 +138,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 +201,30 @@
                         .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;
 
+                part = 0;
+                titleParts = 0;
+                uint32_t pos;
+                uint32_t length;
+                
+                dvdnav_current_title_info(dvdnav, &title, &part);
+                dvdnav_get_number_of_parts(dvdnav, title, &titleParts);
+                dvdnav_get_position(dvdnav, &pos, &length);
+                titleLength = length *DVD_BLOCK_SIZE;
+                cellstartPos = GetReadPosition();
+                buttonSelected = false; 
+                if (cellChange == 100)
+                    cellChange = 0;
+                else
+                    cellChange++;
                 if (blockBuf != dvdBlockWriteBuf)
                 {
                     dvdnav_free_cache_block(dvdnav, blockBuf);
@@ -228,9 +232,11 @@
             }
             break;
             case DVDNAV_SPU_CLUT_CHANGE:
+            {
+                memcpy(clut,blockBuf, 16 * sizeof (uint32_t));
                 VERBOSE(VB_PLAYBACK, "DVDNAV_SPU_CLUT_CHANGE happened.");
-                break;
-                    
+            }
+            break;       
             case DVDNAV_SPU_STREAM_CHANGE:
             {
                 dvdnav_spu_stream_change_event_t* spu =
@@ -241,7 +247,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);
@@ -264,8 +276,28 @@
             }
             break;
             case DVDNAV_NAV_PACKET:
+            {
                 lastNav = (dvdnav_t *)blockBuf;
-                break;
+                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.");
                 break;                        
@@ -303,32 +335,51 @@
                     dvdnav_free_cache_block(dvdnav, blockBuf);
                 }                                                   
 
+                if (DVDButtonUpdate(false))
+                    buttonExists = 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));
+                usleep(10000);
+                if (skipstillorwait)
+                    SkipStillFrame();
+                else
+                {
+                    int elapsedTime = 0;
+                    if (still->length  < 0xff)
+                    {
+                        elapsedTime = stillFrameTimer.elapsed() / 1000; // in seconds
+                        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);
-                break;
+            {
+                if (skipstillorwait)
+                    WaitSkip();
+                else 
+                {
+                    dvdWaiting = true;
+                    usleep(10000);
+                }
+            }
+            break;
             case DVDNAV_STOP:
+            {
                 VERBOSE(VB_GENERAL, "DVDNAV_STOP");
                 sz = tot;
                 gotStop = true;
-                break;
+            }
+            break;
             default:
                 VERBOSE(VB_IMPORTANT, "Got DVD event "<<dvdEvent);
                 break;
@@ -344,7 +395,7 @@
 bool DVDRingBufferPriv::nextTrack(void)
 {
     int newPart = part + 1;
-    if (newPart < maxPart)
+    if (newPart < titleParts)
     {
         dvdnav_part_play(dvdnav, title, newPart);
         gotStop = false;
@@ -374,4 +425,453 @@
     return cellStart / 90000;
 }
 
+void DVDRingBufferPriv::SkipStillFrame(void)
+{
+    dvdnav_still_skip(dvdnav);
+    cellHasStillFrame = false;
+}
+
+void DVDRingBufferPriv::WaitSkip(void)
+{
+    dvdnav_wait_skip(dvdnav);
+    dvdWaiting = false;
+}
+
+void DVDRingBufferPriv::GoToMenu(const QString str)
+{
+    DVDMenuID_t menuid;
+    if (str.compare("part") == 0)
+        menuid = DVD_MENU_Part;
+    else if (str.compare("menu") == 0)
+        menuid = DVD_MENU_Root;
+    dvdnav_menu_call(dvdnav,menuid);
+}
+
+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::MoveButtonLeft(void)
+{
+    if (IsInMenu() && (NumMenuButtons() > 0))
+    {
+        pci_t *pci = dvdnav_get_current_nav_pci(dvdnav);
+        dvdnav_left_button_select(dvdnav, pci);
+    }
+}
+
+void DVDRingBufferPriv::MoveButtonRight(void)
+{
+    if (IsInMenu() &&(NumMenuButtons() > 0) )
+    {
+        pci_t *pci = dvdnav_get_current_nav_pci(dvdnav);
+        dvdnav_right_button_select(dvdnav, pci);
+    }
+}
+
+void DVDRingBufferPriv::MoveButtonUp(void)
+{
+    if (IsInMenu() && (NumMenuButtons() > 0))
+    {
+        pci_t *pci = dvdnav_get_current_nav_pci(dvdnav);
+        dvdnav_upper_button_select(dvdnav, pci);
+    }
+}
+
+void DVDRingBufferPriv::MoveButtonDown(void)
+{
+    if (IsInMenu() && (NumMenuButtons() > 0))
+    {
+        pci_t *pci = dvdnav_get_current_nav_pci(dvdnav);
+        dvdnav_lower_button_select(dvdnav, pci);
+    }
+}
+
+void DVDRingBufferPriv::ActivateButton(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,long long pts)
+{    
+    if (buf_size < 4)
+        return;
+
+    menuspupts = pts;    
+    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))
+        buttonExists = DrawMenuButton(menuSpuPkt,menuBuflength);
+}
+
+AVSubtitleRect *DVDRingBufferPriv::GetMenuButton(void)
+{
+    if (MenuButtonChanged() && buttonExists)
+        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;
+                if (hl_startx > x1)
+                    dvdMenuButton->x = hl_startx - x1;
+                else
+                    dvdMenuButton->x = 0;
+                if (hl_starty > y1)
+                    dvdMenuButton->y  = hl_starty - y1;
+                else
+                    dvdMenuButton->y = 0;
+                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 + 576 ))
+        return false;
+
+    return true;
+}
+
+void DVDRingBufferPriv::ClearMenuSPUParameters(void)
+{
+    if (menuBuflength == 0)
+        return;
+
+    VERBOSE(VB_PLAYBACK,LOC + "Clearing Menu SPU Packet" );
+    if (buttonExists)
+    {
+        av_free(dvdMenuButton->rgba_palette);
+        av_free(dvdMenuButton->bitmap);
+        buttonExists = false;
+    }  
+    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 = hl_startx;
+    int y = hl_starty;
+    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;
+    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: libs/libmythtv/RingBuffer.h
===================================================================
--- libs/libmythtv/RingBuffer.h	(revision 8948)
+++ libs/libmythtv/RingBuffer.h	(working copy)
@@ -7,6 +7,7 @@
 #include <qwaitcondition.h>
 #include <qmutex.h>
 #include <pthread.h>
+#include "avcodec.h"
 
 class RemoteFile;
 class RemoteEncoder;
@@ -89,6 +90,35 @@
     uint GetTotalTimeOfTitle(void);
     uint GetCellStart(void);
     long long GetTotalReadPosition(void);
+    bool InDVDStillFrame(void);
+    void DVDSkipStillFrame(void);
+    void DVDWaitSkip(void);
+    bool IsDVDWaiting(void);
+    bool IsInDVDMenu(void);
+    void GoToDVDMenu(QString str);
+    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,long long pts);
+    AVSubtitleRect *GetDVDMenuButton(void);
+    bool IgnoringDVDStillorWait(void);
+    void IgnoreDVDStillOrWait(bool skip);
+    uint GetDVDCurrentTime(void);
+    long long GetDVDCellStartPos(void);
+    void HideDVDMenuButton(bool hide);
+    uint DVDButtonPosX(void);
+    uint DVDButtonPosY(void);
+    uint GetDVDAudioLanguage(int id);
+    uint GetDVDSubtitleLanguage(int id);
+    bool InDVDMenuOrStillFrame(void);
+    long long DVDMenuSpuPts(void);
+    int DVDCellChange(void);
 
     long long SetAdjustFilesize(void);
     
Index: libs/libmythtv/decoderbase.cpp
===================================================================
--- libs/libmythtv/decoderbase.cpp	(revision 8948)
+++ libs/libmythtv/decoderbase.cpp	(working copy)
@@ -720,4 +720,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: */
Index: libs/libmythtv/avformatdecoder.h
===================================================================
--- libs/libmythtv/avformatdecoder.h	(revision 8948)
+++ libs/libmythtv/avformatdecoder.h	(working copy)
@@ -254,6 +254,11 @@
 
     // language preferences for auto-selection of streams
     vector<int>       languagePreference;
+
+    // DVD
+    int lastdvdtitle;
+    int lastdvdpart;
+    int lastdvdcellchange;
 };
 
 #endif
Index: libs/libavcodec/parser.c
===================================================================
--- libs/libavcodec/parser.c	(revision 8948)
+++ libs/libavcodec/parser.c	(working copy)
@@ -486,7 +486,7 @@
     ParseContext *pc= &pc1->pc;
     int next;
 
-    if(s->flags & PARSER_FLAG_COMPLETE_FRAMES){
+    if((s->flags & PARSER_FLAG_COMPLETE_FRAMES) || buf_size == 4){
         next= buf_size;
     }else{
         next= ff_mpeg1_find_frame_end(pc, buf, buf_size);
