Index: libs/libmythtv/NuppelVideoPlayer.cpp
===================================================================
--- libs/libmythtv/NuppelVideoPlayer.cpp	(revision 9406)
+++ libs/libmythtv/NuppelVideoPlayer.cpp	(working copy)
@@ -972,9 +972,7 @@
         }
     }
   
-    // need this til proper DVD bookmarking is implemented
-    if (!ringBuffer->isDVD())
-        bookmarkseek = GetBookmark();
+    bookmarkseek = GetBookmark();
 
     return IsErrored() ? -1 : 0;
 }
@@ -3345,6 +3343,13 @@
     if (!m_playbackinfo || !osd)
         return;
 
+    if (ringBuffer->isDVD())
+    {
+        if (ringBuffer->InDVDMenuOrStillFrame())
+            return;
+        else
+            SetDVDBookmark(framesPlayed);
+    }
     m_playbackinfo->SetBookmark(framesPlayed);
     osd->SetSettingsText(QObject::tr("Position Saved"), 1);
 }
@@ -3354,7 +3359,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);
 }
 
@@ -3363,6 +3371,9 @@
     if (!m_playbackinfo)
         return 0;
 
+    if (ringBuffer->isDVD())
+        return GetDVDBookmark();
+
     return m_playbackinfo->GetBookmark();
 }
 
@@ -5830,6 +5841,56 @@
         ringBuffer->DVD()->GoToNextProgram();
 }
 
+long long NuppelVideoPlayer::GetDVDBookmark(void) const
+{
+    QStringList dvdbookmark = QStringList();
+    const char *name;
+    const char *serialid;
+    long long frames = 0;
+    bool delbookmark,donotPlay;
+    delbookmark = donotPlay = ringBuffer->DVD()->IgnorePlayTitleAndPart();
+    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());
+            int part  = atoi((*++it).ascii());
+            frames = (long long)(atoi((*++it).ascii()) & 0xffffffffLL);
+            if (!donotPlay)
+                ringBuffer->DVD()->PlayTitleAndPart(title, part);
+            ringBuffer->DVD()->IgnorePlayTitleAndPart(false);
+        }
+    }
+    return frames;
+}
+
+void NuppelVideoPlayer::SetDVDBookmark(long long frames)
+{
+    QStringList fields;
+    const char *name;
+    const char *serialid;
+    int title = 0;
+    int part = 0;
+    ringBuffer->DVD()->GetNameAndSerialNum(&name, &serialid);
+    if (frames != 0)
+        ringBuffer->DVD()->GetPartAndTitle(part, title);
+    if (m_playbackinfo && ringBuffer->isDVD() && 
+            !ringBuffer->InDVDMenuOrStillFrame() && 
+            ringBuffer->DVD()->GetTotalTimeOfTitle() > 300)
+    {
+        fields += QString(serialid);
+        fields += QString(name);
+        fields += QString("%1").arg(title);
+        fields += QString("%1").arg(part);
+        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/DVDRingBuffer.cpp
===================================================================
--- libs/libmythtv/DVDRingBuffer.cpp	(revision 9406)
+++ libs/libmythtv/DVDRingBuffer.cpp	(working copy)
@@ -37,7 +37,8 @@
       lastvobid(0), cellRepeated(false), 
       buttonstreamid(0), gotoCellStart(false), 
       menupktpts(0), autoselectaudio(true),
-      autoselectsubtitle(true), parent(0)
+      autoselectsubtitle(true), 
+      ignoredvdbookmark(false), parent(0)
 {
 }
 
@@ -125,8 +126,10 @@
                         .arg(curTitle).arg(titleParts));
             }
         }
-
+        
         dvdnav_current_title_info(dvdnav, &title, &part);
+        dvdnav_get_title_string(dvdnav,&dvdname);
+        dvdnav_get_serial_number(dvdnav,&serialnumber);
         return true;
     }
 }
@@ -909,6 +912,18 @@
     }
 }
 
+double DVDRingBufferPriv::GetFrameRate(void)
+{
+    float dvdfps = 0;
+    int format = dvdnav_get_video_format(dvdnav);
+    if (format)
+        dvdfps = 23.97;
+    else 
+        dvdfps = 29.97;
+
+    return dvdfps;
+}
+
 void DVDRingBufferPriv::guess_palette(uint32_t *rgba_palette,uint8_t *palette,
                                         uint8_t *alpha)
 {
Index: libs/libmythtv/NuppelVideoPlayer.h
===================================================================
--- libs/libmythtv/NuppelVideoPlayer.h	(revision 9406)
+++ libs/libmythtv/NuppelVideoPlayer.h	(working copy)
@@ -369,6 +369,8 @@
         else
             textDisplayMode &= ~kTrackTypeSubtitle;
     }
+    long long GetDVDBookmark(void) const;
+        
 
   protected:
     void DisplayPauseFrame(void);
@@ -476,6 +478,7 @@
 
     // Private DVD stuff
     void DisplayDVDButton(void);
+    void SetDVDBookmark(long long frames);
 
   private:
     VideoOutputType forceVideoOutput;
Index: libs/libmythtv/DVDRingBuffer.h
===================================================================
--- libs/libmythtv/DVDRingBuffer.h	(revision 9406)
+++ libs/libmythtv/DVDRingBuffer.h	(working copy)
@@ -39,12 +39,12 @@
     long long GetTotalReadPosition(void) { return titleLength; }
     void GetDescForPos(QString &desc) const;
     void GetPartAndTitle(int &_part, int &_title) const
-        { _part  = part; _title = _title; }
+        { _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; }
+    int  NumPartsInTitle(void) { return titleParts; }
     void GetMenuSPUPkt(uint8_t *buf, int len, int stream_id);
     AVSubtitleRect *GetMenuButton(void);
     bool IgnoringStillorWait(void) { return skipstillorwait; }
@@ -57,9 +57,16 @@
     long long GetMenuPktPts(void) { return menupktpts; }
     bool DecodeSubtitles(AVSubtitle * sub, int * gotSubtitles, 
                          const uint8_t * buf, int buf_size);
+    void GetNameAndSerialNum(const char **_name, const char **_serial)
+        { (*_name) = dvdname; (*_serial) = serialnumber; }
 
+    bool IgnorePlayTitleAndPart(void) { return ignoredvdbookmark; }
+    double GetFrameRate(void);
+    
     // commands
     bool OpenFile(const QString &filename);
+    void PlayTitleAndPart(int _title, int _part) 
+        { dvdnav_part_play(dvdnav, _title, _part); }
     void close(void);
     bool nextTrack(void);
     void prevTrack(void);
@@ -83,7 +90,8 @@
     uint8_t GetNumAudioChannels(int id);
     void AutoSelectAudio(bool setting) { autoselectaudio = setting; }
     void AutoSelectSubtitle(bool setting) { autoselectsubtitle = setting; }
-
+    void IgnorePlayTitleAndPart(bool change) { ignoredvdbookmark = change; }
+    
     void SetParent(NuppelVideoPlayer *p) { parent = p; }
     
   protected:
@@ -131,6 +139,9 @@
     long long      menupktpts;
     bool           autoselectaudio;
     bool           autoselectsubtitle;
+    const char     *dvdname;
+    const char     *serialnumber;
+    bool           ignoredvdbookmark;
 
     NuppelVideoPlayer *parent;
 
Index: libs/libmythtv/tv_play.cpp
===================================================================
--- libs/libmythtv/tv_play.cpp	(revision 9406)
+++ libs/libmythtv/tv_play.cpp	(working copy)
@@ -2453,12 +2453,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);
             }
@@ -2608,6 +2622,9 @@
             {
                 if (!was_doing_ff_rew)
                 {
+                    if (prbuffer->isDVD())
+                        prbuffer->DVD()->IgnorePlayTitleAndPart(true);
+                    
                     if (gContext->GetNumSetting("AltClearSavedPosition", 1)
                         && nvp->GetBookmark())
                         nvp->ClearBookmark(); 
Index: libs/libmythtv/programinfo.h
===================================================================
--- libs/libmythtv/programinfo.h	(revision 9406)
+++ 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/decoderbase.cpp
===================================================================
--- libs/libmythtv/decoderbase.cpp	(revision 9406)
+++ libs/libmythtv/decoderbase.cpp	(working copy)
@@ -107,6 +107,7 @@
     {
         long long totframes;
         keyframedist = 15;
+        fps = ringBuffer->DVD()->GetFrameRate();
         if (fps < 26 && fps > 24)
            keyframedist = 12;
         totframes = (long long)(ringBuffer->DVD()->GetTotalTimeOfTitle() * fps);
Index: libs/libmythtv/programinfo.cpp
===================================================================
--- libs/libmythtv/programinfo.cpp	(revision 9406)
+++ libs/libmythtv/programinfo.cpp	(working copy)
@@ -1703,6 +1703,84 @@
     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 , part , frame " 
+                    " FROM dvdbookmark "
+                    " WHERE serialid = ? ");
+    query.addBindValue(serialid);
+
+    if (query.exec() && query.isActive() && query.size() > 0)
+    {
+        query.next();
+        for (int i = 0; i < 3 ; 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 part = *(++it);
+    QString frame = *(++it);
+    
+    query.prepare("INSERT IGNORE INTO dvdbookmark "
+                    " ( serialid, name, title, part, frame, timestamp )"
+                    " VALUES ( :SERIALID , :NAME , 0 , 0 , 0, NOW() );");
+    query.bindValue(":SERIALID", serialid);
+    query.bindValue(":NAME", name);
+   
+    if (!query.exec() || !query.isActive())
+        MythContext::DBError("SetDVDBookmark inserting", query);
+
+
+    query.prepare(" UPDATE dvdbookmark  "
+                    " SET title = ? , "
+                    "      part = ? , "
+                    "     frame = ? , "
+                    " timestamp = NOW() "
+                    " WHERE serialid = ? ;");
+    query.addBindValue(title);
+    query.addBindValue(part);
+    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.
Index: libs/libmythdvdnav/dvdnav.c
===================================================================
--- libs/libmythdvdnav/dvdnav.c	(revision 9406)
+++ libs/libmythdvdnav/dvdnav.c	(working copy)
@@ -817,6 +817,17 @@
   return DVDNAV_STATUS_OK;
 }
 
+dvdnav_status_t dvdnav_get_serial_number(dvdnav_t *this, const char **serial_str) {
+
+  if(!this || !serial_str) {
+     printerr("Passed a NULL pointer.");
+     return DVDNAV_STATUS_ERR;
+  }
+
+  (*serial_str) = this->vm->serial_number;
+  return DVDNAV_STATUS_OK;
+}
+
 uint8_t dvdnav_get_video_aspect(dvdnav_t *this) {
   uint8_t         retval;
   
@@ -855,6 +866,25 @@
   return retval;
 }
 
+uint8_t dvdnav_get_video_format(dvdnav_t *this) {
+  uint8_t         retval;
+
+  if(!this) {
+    printerr("Passed a NULL pointer.");
+    return -1;
+  }
+  if(!this->started) {
+   printerr("Virtual DVD machine not started.");
+   return -1;
+  }
+  
+  pthread_mutex_lock(&this->vm_lock);
+  retval = (uint8_t)vm_get_video_format(this->vm);
+  pthread_mutex_unlock(&this->vm_lock);
+
+  return retval;
+}
+
 uint16_t dvdnav_audio_stream_to_lang(dvdnav_t *this, uint8_t stream) {
   audio_attr_t  attr;
   
Index: libs/libmythdvdnav/vm.c
===================================================================
--- libs/libmythdvdnav/vm.c	(revision 9406)
+++ libs/libmythdvdnav/vm.c	(working copy)
@@ -124,7 +124,7 @@
 }
 #endif
 
-static void dvd_read_name(char *name, const char *device) {
+static void dvd_read_name(char *name, char *serial, const char *device) {
     /* Because we are compiling with _FILE_OFFSET_BITS=64
      * all off_t are 64bit.
      */
@@ -160,6 +160,7 @@
               fprintf(MSG_OUT, " ");
             } 
           }
+          strncpy(serial, &data[73], 16);
           fprintf(MSG_OUT, "\nlibdvdnav: DVD Title (Alternative): ");
           for(i=89; i < 128; i++ ) {
             if((data[i] == 0)) break;
@@ -320,7 +321,7 @@
       fprintf(MSG_OUT, "libdvdnav: vm: faild to open/read the DVD\n");
       return 0;
     }
-    dvd_read_name(vm->dvd_name, dvdroot);
+    dvd_read_name(vm->dvd_name, vm->serial_number, dvdroot);
     vm->map  = remap_loadmap(vm->dvd_name);
     vm->vmgi = ifoOpenVMGI(vm->dvd);
     if(!vm->vmgi) {
@@ -860,6 +861,10 @@
   return vm_get_video_attr(vm).permitted_df;
 }
 
+int vm_get_video_format(vm_t *vm) {
+  return vm_get_video_attr(vm).video_format;
+}
+
 video_attr_t vm_get_video_attr(vm_t *vm) {
   switch ((vm->state).domain) {
   case VTS_DOMAIN:
Index: libs/libmythdvdnav/dvdnav.h
===================================================================
--- libs/libmythdvdnav/dvdnav.h	(revision 9406)
+++ libs/libmythdvdnav/dvdnav.h	(working copy)
@@ -526,6 +526,11 @@
 dvdnav_status_t dvdnav_get_title_string(dvdnav_t *self, const char **title_str);
 
 /*
+ * Return a string describing the serial number of the DVD
+ */
+dvdnav_status_t dvdnav_get_serial_number(dvdnav_t *self, const char **serial_str);
+ 
+/*
  * Get video aspect code.
  * The aspect code does only change on VTS boundaries.
  * See the DVDNAV_VTS_CHANGE event.
@@ -544,6 +549,12 @@
 uint8_t dvdnav_get_video_scale_permission(dvdnav_t *self);
 
 /*
+ * Get video format
+ * 0 - NTSC , 1 - PAL
+ */
+uint8_t dvdnav_get_video_format(dvdnav_t *self);
+
+/*
  * Converts a *logical* audio stream id into language code
  * (returns 0xffff if no such stream).
  */
Index: libs/libmythdvdnav/vm.h
===================================================================
--- libs/libmythdvdnav/vm.h	(revision 9406)
+++ libs/libmythdvdnav/vm.h	(working copy)
@@ -88,6 +88,7 @@
   dvd_state_t   state;
   int32_t       hop_channel;
   char          dvd_name[50];
+  char          serial_number[19];
   remap_t      *map;
   int           stopped;
 } vm_t;
@@ -168,6 +169,7 @@
 #endif
 int  vm_get_video_aspect(vm_t *vm);
 int  vm_get_video_scale_permission(vm_t *vm);
+int  vm_get_video_format(vm_t *vm);
 video_attr_t vm_get_video_attr(vm_t *vm);
 audio_attr_t vm_get_audio_attr(vm_t *vm, int streamN);
 subp_attr_t  vm_get_subp_attr(vm_t *vm, int streamN);
