Index: libs/libmythtv/NuppelVideoPlayer.cpp
===================================================================
--- libs/libmythtv/NuppelVideoPlayer.cpp	(revision 9487)
+++ libs/libmythtv/NuppelVideoPlayer.cpp	(working copy)
@@ -980,11 +980,12 @@
             deleteIter = deleteMap.begin();
         }
     }
-  
-    // need this til proper DVD bookmarking is implemented
-    if (!ringBuffer->isDVD())
-        bookmarkseek = GetBookmark();
+ 
+    if (ringBuffer->isDVD())
+       ringBuffer->DVD()->JumpToTitle(false);
 
+    bookmarkseek = GetBookmark();
+
     return IsErrored() ? -1 : 0;
 }
 
@@ -2894,13 +2895,21 @@
         GetDecoder()->setExactSeeks(false);
 
         fftime = bookmarkseek;
+        if (ringBuffer->isDVD())
+            GetDVDBookmark();
+
         DoFastForward();
         fftime = 0;
 
         GetDecoder()->setExactSeeks(seeks);
 
         if (gContext->GetNumSetting("ClearSavedPosition", 1))
-            m_playbackinfo->SetBookmark(0);
+        {
+            if (ringBuffer->isDVD())
+                SetDVDBookmark(0);
+            else
+                m_playbackinfo->SetBookmark(0);
+        }
     }
 
     commBreakMapLock.lock();
@@ -3361,6 +3370,13 @@
     if (!m_playbackinfo || !osd)
         return;
 
+    if (ringBuffer->isDVD())
+    {
+        if (ringBuffer->InDVDMenuOrStillFrame())
+            SetDVDBookmark(0);
+        else
+            SetDVDBookmark(framesPlayed);
+    }
     m_playbackinfo->SetBookmark(framesPlayed);
     osd->SetSettingsText(QObject::tr("Position Saved"), 1);
 }
@@ -3370,7 +3386,10 @@
     if (!m_playbackinfo || !osd)
         return;
 
-    m_playbackinfo->SetBookmark(0);
+    if (ringBuffer->isDVD())
+        SetDVDBookmark(0);
+    else
+        m_playbackinfo->SetBookmark(0);
     osd->SetSettingsText(QObject::tr("Position Cleared"), 1);
 }
 
@@ -3379,6 +3398,9 @@
     if (!m_playbackinfo)
         return 0;
 
+    if (ringBuffer->isDVD())
+        return GetDVDBookmark();
+
     return m_playbackinfo->GetBookmark();
 }
 
@@ -5905,6 +5927,77 @@
         ringBuffer->DVD()->GoToNextProgram();
 }
 
+long long NuppelVideoPlayer::GetDVDBookmark(void) const
+{
+    if (!ringBuffer->isDVD())
+        return 0;
+    
+    QStringList dvdbookmark = QStringList();
+    const char *name;
+    const char *serialid;
+    long long frames = 0;
+    bool delbookmark, jumptotitle;
+    delbookmark = jumptotitle = ringBuffer->DVD()->JumpToTitle();
+    if (m_playbackinfo && ringBuffer->isDVD())
+    {
+        ringBuffer->DVD()->GetNameAndSerialNum(&name,&serialid);
+        dvdbookmark = m_playbackinfo->GetDVDBookmark(QString(serialid),
+                                                        !delbookmark);
+        if (!dvdbookmark.empty())
+        {
+            QStringList::Iterator it = dvdbookmark.begin();
+            int title = atoi((*it).ascii());
+            frames = (long long)(atoi((*++it).ascii()) & 0xffffffffLL);
+            if (jumptotitle)
+            {
+                ringBuffer->DVD()->PlayTitleAndPart(title, 1);
+                int audiotrack = atoi((*++it).ascii());
+                int subtitletrack = atoi((*++it).ascii());
+                ringBuffer->DVD()->SetTrack(kTrackTypeAudio, audiotrack);
+                ringBuffer->DVD()->SetTrack(kTrackTypeSubtitle, subtitletrack);
+            }
+            ringBuffer->DVD()->JumpToTitle(true);
+        }
+    }
+    return frames;
+}
+
+void NuppelVideoPlayer::SetDVDBookmark(long long frames)
+{
+    if (!ringBuffer->isDVD())
+        return;
+
+    QStringList fields;
+    const char *name;
+    const char *serialid;
+    int title = 0;
+    int part;
+    int audiotrack = -1;
+    int subtitletrack = -1;
+    ringBuffer->DVD()->GetNameAndSerialNum(&name, &serialid);
+    
+    if (!ringBuffer->InDVDMenuOrStillFrame() &&
+            ringBuffer->DVD()->GetTotalTimeOfTitle() > 300 &&
+            frames > 0)
+    {
+        audiotrack = GetTrack(kTrackTypeAudio);
+        if (GetCaptionMode())
+            subtitletrack = GetTrack(kTrackTypeSubtitle);
+        ringBuffer->DVD()->GetPartAndTitle(part,title);
+    }
+    
+    if (m_playbackinfo) 
+    {
+        fields += QString(serialid);
+        fields += QString(name);
+        fields += QString("%1").arg(title);
+        fields += QString("%1").arg(audiotrack);
+        fields += QString("%1").arg(subtitletrack);
+        fields += QString("%1").arg(frames);
+        m_playbackinfo->SetDVDBookmark(fields);
+    }   
+}
+
 // EIA-708 caption support -- begin
 void NuppelVideoPlayer::SetCurrentWindow(uint service_num, int window_id)
 {
Index: libs/libmythtv/NuppelVideoPlayer.h
===================================================================
--- libs/libmythtv/NuppelVideoPlayer.h	(revision 9487)
+++ libs/libmythtv/NuppelVideoPlayer.h	(working copy)
@@ -477,6 +477,8 @@
 
     // Private DVD stuff
     void DisplayDVDButton(void);
+    long long GetDVDBookmark(void) const;
+    void SetDVDBookmark(long long frames);
 
   private:
     VideoOutputType forceVideoOutput;
Index: libs/libmythtv/tv_play.cpp
===================================================================
--- libs/libmythtv/tv_play.cpp	(revision 9487)
+++ libs/libmythtv/tv_play.cpp	(working copy)
@@ -2398,7 +2398,7 @@
                 DoSeek(jumptime * 60, tr("Jump Ahead"));
             }
         }
-        else if (action == "JUMPBKMRK")
+        else if (action == "JUMPBKMRK" && !prbuffer->isDVD())
         {
             int bookmark = activenvp->GetBookmark();
             if (bookmark > frameRate)
@@ -2483,12 +2483,26 @@
             {
                 nvp->Pause();
 
-                QString vmessage = tr("You are exiting this video");
+                QString vmessage;
+                if (prbuffer->isDVD())
+                    vmessage = tr("You are exiting this DVD");
+                else
+                    vmessage = tr("You are exiting this video");
 
                 QStringList voptions;
-                voptions += tr("Exit to the menu");
+                if (prbuffer->isDVD())
+                {
+                    voptions += tr("Save this position and go to the menu");
+                    voptions += tr("Do not save, just exit to the menu");                                
+                }
+                else
+                    voptions += tr("Exit to the menu");
+                    
                 voptions += tr("Keep watching");
-                dialogname = "videoexitplayoptions";
+                if (prbuffer->isDVD())
+                    dialogname = "exitplayoptions";
+                else
+                    dialogname = "videoexitplayoptions";
                 if (GetOSD())
                     GetOSD()->NewDialogBox(dialogname, vmessage, voptions, 0);
             }
@@ -2638,6 +2652,9 @@
             {
                 if (!was_doing_ff_rew)
                 {
+                    if (prbuffer->isDVD())
+                        prbuffer->DVD()->JumpToTitle(false);
+                    
                     if (gContext->GetNumSetting("AltClearSavedPosition", 1)
                         && nvp->GetBookmark())
                         nvp->ClearBookmark(); 
Index: libs/libmythtv/programinfo.h
===================================================================
--- libs/libmythtv/programinfo.h	(revision 9487)
+++ libs/libmythtv/programinfo.h	(working copy)
@@ -177,6 +177,7 @@
     // DB gets
     long long GetFilesize(void);
     long long GetBookmark(void) const;
+    QStringList GetDVDBookmark(QString serialid, bool delbookmark) const;
     bool IsEditing(void) const;
     bool IsCommFlagged(void) const;
     bool IsInUse(QString &byWho) const;
@@ -191,6 +192,7 @@
     // DB sets
     void SetFilesize(long long fsize);
     void SetBookmark(long long pos) const;
+    void SetDVDBookmark(QStringList fields) const;
     void SetEditing(bool edit) const;
     void SetTranscoded(int transFlag) const;
     void SetDeleteFlag(bool deleteFlag) const;
Index: libs/libmythtv/programinfo.cpp
===================================================================
--- libs/libmythtv/programinfo.cpp	(revision 9487)
+++ libs/libmythtv/programinfo.cpp	(working copy)
@@ -1703,6 +1703,87 @@
     return pos;
 }
 
+/** \fn ProgramInfo::GetDVDBookmark(QString serialid,bool delbookmark) const
+ *  \brief Queries "dvdbookmark" table for bookmarking using DVD 
+ *  serial number. Deletes old dvd bookmarks if "delete" is true.
+ *  
+ *  \return list containing title,part,frame number
+ */
+QStringList ProgramInfo::GetDVDBookmark(QString serialid, bool delbookmark) const
+{
+    QStringList fields = QStringList();
+    MSqlQuery query(MSqlQuery::InitCon());
+
+    if (ignoreBookmark)
+        return fields;
+
+    if (delbookmark)
+    {
+        int days = -(gContext->GetNumSetting("DVDBookmarkDays",10));
+        QDateTime removedate = mythCurrentDateTime().addDays(days);
+        query.prepare(" DELETE from dvdbookmark "
+                        " WHERE timestamp < ? ");
+        query.addBindValue(removedate.toString(Qt::ISODate));
+    
+        if (!query.exec() || !query.isActive())
+            MythContext::DBError("GetDVDBookmark deleting old entries", query);
+    }
+    
+    query.prepare(" SELECT title , framenum , audionum, subtitlenum " 
+                    " FROM dvdbookmark "
+                    " WHERE serialid = ? ");
+    query.addBindValue(serialid);
+
+    if (query.exec() && query.isActive() && query.size() > 0)
+    {
+        query.next();
+        for (int i = 0; i < 4 ; i++)
+            fields.append(query.value(i).toString());
+    }
+    
+    return fields;
+}
+
+void ProgramInfo::SetDVDBookmark(QStringList fields) const
+{
+
+    QStringList::Iterator it = fields.begin();
+    MSqlQuery query(MSqlQuery::InitCon());
+    
+    QString serialid = *(it);
+    QString name = *(++it);
+    QString title = *(++it);
+    QString audionum = *(++it);
+    QString subtitlenum = *(++it);
+    QString frame = *(++it);
+   
+    query.prepare("INSERT IGNORE INTO dvdbookmark "
+                    " ( serialid, name)"
+                    " VALUES ( :SERIALID , :NAME );");
+    query.bindValue(":SERIALID", serialid);
+    query.bindValue(":NAME", name);
+   
+    if (!query.exec() || !query.isActive())
+        MythContext::DBError("SetDVDBookmark inserting", query);
+
+
+    query.prepare(" UPDATE dvdbookmark  "
+                    " SET title          = ? , "
+                    "     audionum       = ? , "
+                    "     subtitlenum    = ? , "
+                    "     framenum       = ? , "
+                    "     timestamp      = NOW() "
+                    " WHERE serialid = ? ;");
+    query.addBindValue(title);
+    query.addBindValue(audionum);
+    query.addBindValue(subtitlenum);
+    query.addBindValue(frame);
+    query.addBindValue(serialid);
+
+    if (!query.exec() || !query.isActive())
+        MythContext::DBError("SetDVDBookmark updating", query);
+}
+
 /** \fn ProgramInfo::IsEditing(void) const
  *  \brief Queries "recorded" table for its "editing" field
  *         and returns true if it is set to true.
