diff --git a/mythtv/libs/libmyth/programinfo.cpp b/mythtv/libs/libmyth/programinfo.cpp
index 8764134..9757c18 100644
--- a/mythtv/libs/libmyth/programinfo.cpp
+++ b/mythtv/libs/libmyth/programinfo.cpp
@@ -2695,6 +2695,40 @@ uint64_t ProgramInfo::QueryBookmark(uint chanid, const QDateTime &recstartts)
     return (bookmarkmap.isEmpty()) ? 0 : bookmarkmap.begin().key();
 }
 
+/** \brief Gets any progstart position in database,
+ *         unless the ignore progstart flag is set.
+ *
+ *  \return Progstart position in frames if the query is executed
+ *          and succeeds, zero otherwise.
+ */
+uint64_t ProgramInfo::QueryProgStart(void) const
+{
+    if (programflags & FL_IGNOREPROGSTART)
+        return 0;
+
+    frm_dir_map_t bookmarkmap;
+    QueryMarkupMap(bookmarkmap, MARK_UTIL_PROGSTART);
+
+    return (bookmarkmap.isEmpty()) ? 0 : bookmarkmap.begin().key();
+}
+
+/** \brief Gets any lastplaypos position in database,
+ *         unless the ignore lastplaypos flag is set.
+ *
+ *  \return LastPlayPos position in frames if the query is executed
+ *          and succeeds, zero otherwise.
+ */
+uint64_t ProgramInfo::QueryLastPlayPos(void) const
+{
+    if (programflags & FL_IGNORELASTPLAYPOS)
+        return 0;
+
+    frm_dir_map_t bookmarkmap;
+    QueryMarkupMap(bookmarkmap, MARK_UTIL_LASTPLAYPOS);
+
+    return (bookmarkmap.isEmpty()) ? 0 : bookmarkmap.begin().key();
+}
+
 /** \brief Queries "dvdbookmark" table for bookmarking DVD serial
  *         number. Deletes old dvd bookmarks if "delbookmark" is set.
  *
diff --git a/mythtv/libs/libmyth/programinfo.h b/mythtv/libs/libmyth/programinfo.h
index ce8159d..a37ac05 100644
--- a/mythtv/libs/libmyth/programinfo.h
+++ b/mythtv/libs/libmyth/programinfo.h
@@ -452,7 +452,7 @@ class MPUBLIC ProgramInfo
 
     uint32_t GetProgramFlags(void)        const { return programflags; }
     ProgramInfoType GetProgramInfoType(void) const
-        { return (ProgramInfoType)((programflags & FL_TYPEMASK) >> 16); }
+        { return (ProgramInfoType)((programflags & FL_TYPEMASK) >> 20); }
     bool IsGeneric(void) const;
     bool IsInUsePlaying(void)   const { return programflags & FL_INUSEPLAYING;}
     bool IsCommercialFree(void) const { return programflags & FL_CHANCOMMFREE;}
@@ -489,7 +489,7 @@ class MPUBLIC ProgramInfo
     // Quick sets
     void SetTitle(const QString &t) { title = t; title.detach(); }
     void SetProgramInfoType(ProgramInfoType t)
-        { programflags &= ~FL_TYPEMASK; programflags |= ((uint32_t)t<<16); }
+        { programflags &= ~FL_TYPEMASK; programflags |= ((uint32_t)t<<20); }
     void SetPathname(const QString&) const;
     void SetChanID(uint _chanid) { chanid = _chanid; }
     void SetScheduledStartTime(const QDateTime &dt) { startts      = dt;    }
@@ -532,6 +532,21 @@ class MPUBLIC ProgramInfo
         programflags &= ~FL_IGNOREBOOKMARK;
         programflags |= (ignore) ? FL_IGNOREBOOKMARK : 0;
     }
+    /// \brief If "ignore" is true QueryProgStart() will return 0, otherwise
+    ///        QueryProgStart() will return the progstart position if it exists.
+    void SetIgnoreProgStart(bool ignore)
+    {
+        programflags &= ~FL_IGNOREPROGSTART;
+        programflags |= (ignore) ? FL_IGNOREPROGSTART : 0;
+    }
+    /// \brief If "ignore" is true QueryLastPlayPos() will return 0, otherwise
+    ///        QueryLastPlayPos() will return the last playback position
+    ///        if it exists.
+    void SetIgnoreLastPlayPos(bool ignore)
+    {
+        programflags &= ~FL_IGNORELASTPLAYPOS;
+        programflags |= (ignore) ? FL_IGNORELASTPLAYPOS : 0;
+    }
     virtual void SetRecordingID(uint _recordedid) { recordedid = _recordedid; }
     void SetRecordingStatus(RecStatus::Type status) { recstatus = status; }
     void SetRecordingRuleType(RecordingType type) { rectype   = type;   }
@@ -544,6 +559,8 @@ class MPUBLIC ProgramInfo
     uint        QueryMplexID(void) const;
     QDateTime   QueryBookmarkTimeStamp(void) const;
     uint64_t    QueryBookmark(void) const;
+    uint64_t    QueryProgStart(void) const;
+    uint64_t    QueryLastPlayPos(void) const;
     CategoryType QueryCategoryType(void) const;
     QStringList QueryDVDBookmark(const QString &serialid) const;
     bool        QueryIsEditing(void) const;
@@ -658,7 +675,6 @@ class MPUBLIC ProgramInfo
     static QMap<QString,bool> QueryJobsRunning(int type);
     static QStringList LoadFromScheduler(const QString &altTable, int recordid);
 
-  protected:
     // Flagging map support methods
     void QueryMarkupMap(frm_dir_map_t&, MarkTypes type,
                         bool merge = false) const;
@@ -667,6 +683,7 @@ class MPUBLIC ProgramInfo
     void ClearMarkupMap(MarkTypes type = MARK_ALL,
                         int64_t min_frm = -1, int64_t max_frm = -1) const;
 
+  protected:
     // Creates a basename from the start and end times
     QString CreateRecordBasename(const QString &ext) const;
 
diff --git a/mythtv/libs/libmyth/programtypes.cpp b/mythtv/libs/libmyth/programtypes.cpp
index 26b5357..26d3935 100644
--- a/mythtv/libs/libmyth/programtypes.cpp
+++ b/mythtv/libs/libmyth/programtypes.cpp
@@ -53,6 +53,8 @@ QString toString(MarkTypes type)
         case MARK_VIDEO_RATE:   return "VIDEO_RATE";
         case MARK_DURATION_MS:  return "DURATION_MS";
         case MARK_TOTAL_FRAMES: return "TOTAL_FRAMES";
+        case MARK_UTIL_PROGSTART: return "UTIL_PROGSTART";
+        case MARK_UTIL_LASTPLAYPOS: return "UTIL_LASTPLAYPOS";
     }
 
     return "unknown";
diff --git a/mythtv/libs/libmyth/programtypes.h b/mythtv/libs/libmyth/programtypes.h
index d85ee94..373d3ad 100644
--- a/mythtv/libs/libmyth/programtypes.h
+++ b/mythtv/libs/libmyth/programtypes.h
@@ -74,6 +74,8 @@ typedef enum {
     MARK_VIDEO_RATE    = 32,
     MARK_DURATION_MS   = 33,
     MARK_TOTAL_FRAMES  = 34,
+    MARK_UTIL_PROGSTART = 40,
+    MARK_UTIL_LASTPLAYPOS = 41,
 } MarkTypes;
 MPUBLIC QString toString(MarkTypes type);
 
@@ -145,11 +147,13 @@ typedef enum FlagMask {
     FL_DUPLICATE      = 0x00002000,
     FL_REACTIVATE     = 0x00004000,
     FL_IGNOREBOOKMARK = 0x00008000,
+    FL_IGNOREPROGSTART   = 0x00010000,
+    FL_IGNORELASTPLAYPOS = 0x00020000,
     // if you move the type mask please edit {Set,Get}ProgramInfoType()
-    FL_TYPEMASK       = 0x000F0000,
-    FL_INUSERECORDING = 0x00100000,
-    FL_INUSEPLAYING   = 0x00200000,
-    FL_INUSEOTHER     = 0x00400000,
+    FL_TYPEMASK       = 0x00F00000,
+    FL_INUSERECORDING = 0x01000000,
+    FL_INUSEPLAYING   = 0x02000000,
+    FL_INUSEOTHER     = 0x04000000,
 } ProgramFlag;
 
 typedef enum ProgramInfoType {
diff --git a/mythtv/libs/libmythtv/mythplayer.cpp b/mythtv/libs/libmythtv/mythplayer.cpp
index e52b3c9..6c6abfb 100644
--- a/mythtv/libs/libmythtv/mythplayer.cpp
+++ b/mythtv/libs/libmythtv/mythplayer.cpp
@@ -2879,7 +2879,14 @@ void MythPlayer::EventStart(void)
     player_ctx->LockPlayingInfo(__FILE__, __LINE__);
     {
         if (player_ctx->playingInfo)
+        {
+            // When initial playback gets underway, we override the ProgramInfo
+            // flags such that future calls to GetBookmark() will consider only
+            // an actual bookmark and not progstart or lastplaypos information.
             player_ctx->playingInfo->SetIgnoreBookmark(false);
+            player_ctx->playingInfo->SetIgnoreProgStart(true);
+            player_ctx->playingInfo->SetIgnoreLastPlayPos(true);
+        }
     }
     player_ctx->UnlockPlayingInfo(__FILE__, __LINE__);
     commBreakMap.LoadMap(player_ctx, framesPlayed);
@@ -3610,7 +3617,13 @@ uint64_t MythPlayer::GetBookmark(void)
     {
         player_ctx->LockPlayingInfo(__FILE__, __LINE__);
         if (player_ctx->playingInfo)
+        {
             bookmark = player_ctx->playingInfo->QueryBookmark();
+            if (bookmark == 0)
+                bookmark = player_ctx->playingInfo->QueryProgStart();
+            if (bookmark == 0)
+                bookmark = player_ctx->playingInfo->QueryLastPlayPos();
+        }
         player_ctx->UnlockPlayingInfo(__FILE__, __LINE__);
     }
 
diff --git a/mythtv/libs/libmythtv/recorders/recorderbase.cpp b/mythtv/libs/libmythtv/recorders/recorderbase.cpp
index 669c872..a6fc9fa 100644
--- a/mythtv/libs/libmythtv/recorders/recorderbase.cpp
+++ b/mythtv/libs/libmythtv/recorders/recorderbase.cpp
@@ -56,7 +56,8 @@ RecorderBase::RecorderBase(TVRec *rec)
       request_pause(false),     paused(false),
       request_recording(false), recording(false),
       nextRingBuffer(NULL),     nextRecording(NULL),
-      positionMapType(MARK_GOP_BYFRAME)
+      positionMapType(MARK_GOP_BYFRAME),
+      estimatedProgStartMS(0), lastSavedKeyframe(0), lastSavedDuration(0)
 {
     ClearStatistics();
     QMutexLocker locker(avcodeclock);
@@ -114,6 +115,12 @@ void RecorderBase::SetRecording(const RecordingInfo *pginfo)
         //       instance which may lead to the possibility that changes made
         //       in the database by one are overwritten by the other
         curRecording = new RecordingInfo(*pginfo);
+        // Compute an estimate of the actual progstart delay for setting the
+        // MARK_UTIL_PROGSTART mark.  We can't reliably use
+        // curRecording->GetRecordingStartTime() because the scheduler rounds it
+        // to the nearest minute, so we use the current time instead.
+        estimatedProgStartMS =
+            MythDate::current().msecsTo(curRecording->GetScheduledStartTime());
         RecordingFile *recFile = curRecording->GetRecordingFile();
         recFile->m_containerFormat = m_containerFormat;
         recFile->Save();
@@ -579,15 +586,15 @@ void RecorderBase::SavePositionMap(bool force, bool finished)
     bool needToSave = force;
     positionMapLock.lock();
 
-    uint delta_size = positionMapDelta.size();
+    bool has_delta = !positionMapDelta.empty();
     // set pm_elapsed to a fake large value if the timer hasn't yet started
     uint pm_elapsed = (positionMapTimer.isRunning()) ?
         positionMapTimer.elapsed() : ~0;
     // save on every 1.5 seconds if in the first few frames of a recording
     needToSave |= (positionMap.size() < 30) &&
-        (delta_size >= 1) && (pm_elapsed >= 1500);
+        has_delta && (pm_elapsed >= 1500);
     // save every 10 seconds later on
-    needToSave |= (delta_size >= 1) && (pm_elapsed >= 10000);
+    needToSave |= has_delta && (pm_elapsed >= 10000);
     // Assume that durationMapDelta is the same size as
     // positionMapDelta and implicitly use the same logic about when
     // to same durationMapDelta.
@@ -595,7 +602,7 @@ void RecorderBase::SavePositionMap(bool force, bool finished)
     if (curRecording && needToSave)
     {
         positionMapTimer.start();
-        if (delta_size)
+        if (has_delta)
         {
             // copy the delta map because most times we are called it will be in
             // another thread and we don't want to lock the main recorder thread
@@ -609,6 +616,8 @@ void RecorderBase::SavePositionMap(bool force, bool finished)
             curRecording->SavePositionMapDelta(deltaCopy, positionMapType);
             curRecording->SavePositionMapDelta(durationDeltaCopy,
                                                MARK_DURATION_MS);
+
+            TryWriteProgStartMark(durationDeltaCopy);
         }
         else
         {
@@ -626,6 +635,81 @@ void RecorderBase::SavePositionMap(bool force, bool finished)
     }
 }
 
+void RecorderBase::TryWriteProgStartMark(const frm_pos_map_t &durationDeltaCopy)
+{
+    // Note: all log strings contain "progstart mark" for searching.
+    if (estimatedProgStartMS <= 0)
+    {
+        // Do nothing because no progstart mark is needed.
+        LOG(VB_RECORD, LOG_DEBUG,
+            QString("No progstart mark needed because delta=%1")
+            .arg(estimatedProgStartMS));
+        return;
+    }
+    frm_pos_map_t::const_iterator last_it = durationDeltaCopy.end();
+    --last_it;
+    long long bookmarkFrame = 0;
+    LOG(VB_RECORD, LOG_DEBUG,
+        QString("durationDeltaCopy.begin() = (%1,%2)")
+        .arg(durationDeltaCopy.begin().key())
+        .arg(durationDeltaCopy.begin().value()));
+    if (estimatedProgStartMS > *last_it)
+    {
+        // Do nothing because we haven't reached recstartts yet.
+        LOG(VB_RECORD, LOG_DEBUG,
+            QString("No progstart mark yet because estimatedProgStartMS=%1 "
+                    "and *last_it=%2")
+            .arg(estimatedProgStartMS).arg(*last_it));
+    }
+    else if (lastSavedDuration <= estimatedProgStartMS &&
+             estimatedProgStartMS < *durationDeltaCopy.begin())
+    {
+        // Set progstart mark @ lastSavedKeyframe
+        LOG(VB_RECORD, LOG_DEBUG,
+            QString("Set progstart mark=%1 because %2<=%3<%4")
+            .arg(lastSavedKeyframe).arg(lastSavedDuration)
+            .arg(estimatedProgStartMS).arg(*durationDeltaCopy.begin()));
+        bookmarkFrame = lastSavedKeyframe;
+    }
+    else if (*durationDeltaCopy.begin() <= estimatedProgStartMS &&
+             estimatedProgStartMS < *last_it)
+    {
+        frm_pos_map_t::const_iterator upper_it = durationDeltaCopy.begin();
+        for (; upper_it != durationDeltaCopy.end(); ++upper_it)
+        {
+            if (*upper_it > estimatedProgStartMS)
+            {
+                --upper_it;
+                // Set progstart mark @ upper_it.key()
+                LOG(VB_RECORD, LOG_DEBUG,
+                    QString("Set progstart mark=%1 because "
+                            "estimatedProgStartMS=%2 and upper_it.value()=%3")
+                    .arg(upper_it.key()).arg(estimatedProgStartMS)
+                    .arg(upper_it.value()));
+                bookmarkFrame = upper_it.key();
+                break;
+            }
+        }
+    }
+    else
+    {
+        // do nothing
+        LOG(VB_RECORD, LOG_DEBUG, "No progstart mark due to fallthrough");
+    }
+    if (bookmarkFrame)
+    {
+        frm_dir_map_t progStartMap;
+        progStartMap[bookmarkFrame] = MARK_UTIL_PROGSTART;
+        curRecording->SaveMarkupMap(progStartMap, MARK_UTIL_PROGSTART);
+    }
+    lastSavedKeyframe = last_it.key();
+    lastSavedDuration = last_it.value();
+    LOG(VB_RECORD, LOG_DEBUG,
+        QString("Setting lastSavedKeyframe=%1 lastSavedDuration=%2 "
+                "for progstart mark calculations")
+        .arg(lastSavedKeyframe).arg(lastSavedDuration));
+}
+
 void RecorderBase::AspectChange(uint aspect, long long frame)
 {
     MarkTypes mark = MARK_ASPECT_4_3;
diff --git a/mythtv/libs/libmythtv/recorders/recorderbase.h b/mythtv/libs/libmythtv/recorders/recorderbase.h
index 8d00836..9931413 100644
--- a/mythtv/libs/libmythtv/recorders/recorderbase.h
+++ b/mythtv/libs/libmythtv/recorders/recorderbase.h
@@ -292,6 +292,8 @@ class MTV_PUBLIC RecorderBase : public QRunnable
      */
     void SetTotalFrames(uint64_t total_frames);
 
+    void TryWriteProgStartMark(const frm_pos_map_t &durationDeltaCopy);
+
     TVRec         *tvrec;
     RingBuffer    *ringBuffer;
     bool           weMadeBuffer;
@@ -341,6 +343,11 @@ class MTV_PUBLIC RecorderBase : public QRunnable
     frm_pos_map_t  durationMapDelta;
     MythTimer      positionMapTimer;
 
+    // ProgStart mark support
+    qint64         estimatedProgStartMS;
+    long long      lastSavedKeyframe;
+    long long      lastSavedDuration;
+
     // Statistics
     // Note: Once we enter RecorderBase::run(), only that thread can
     // update these values safely. These values are read in that thread
diff --git a/mythtv/libs/libmythtv/tv_play.cpp b/mythtv/libs/libmythtv/tv_play.cpp
index c9ee3de..138aeb2 100644
--- a/mythtv/libs/libmythtv/tv_play.cpp
+++ b/mythtv/libs/libmythtv/tv_play.cpp
@@ -137,6 +137,7 @@ const uint TV::kEndOfPlaybackFirstCheckTimer = 60000;
 #else
 const uint TV::kEndOfPlaybackFirstCheckTimer = 5000;
 #endif
+const uint TV::kSaveLastPlayPosTimeout       = 30000;
 
 /**
  * \brief stores last program info. maintains info so long as
@@ -341,6 +342,8 @@ bool TV::StartTV(ProgramInfo *tvrec, uint flags,
     {
         curProgram = new ProgramInfo(*tvrec);
         curProgram->SetIgnoreBookmark(flags & kStartTVIgnoreBookmark);
+        curProgram->SetIgnoreProgStart(flags & kStartTVIgnoreProgStart);
+        curProgram->SetIgnoreLastPlayPos(flags & kStartTVIgnoreLastPlayPos);
     }
 
     GetMythMainWindow()->PauseIdleTimer(true);
@@ -1064,7 +1067,8 @@ TV::TV(void)
       endOfPlaybackTimerId(0),      embedCheckTimerId(0),
       endOfRecPromptTimerId(0),     videoExitDialogTimerId(0),
       pseudoChangeChanTimerId(0),   speedChangeTimerId(0),
-      errorRecoveryTimerId(0),      exitPlayerTimerId(0)
+      errorRecoveryTimerId(0),      exitPlayerTimerId(0),
+      saveLastPlayPosTimerId(0)
 {
     LOG(VB_GENERAL, LOG_INFO, LOC + "Creating TV object");
     ctorTime.start();
@@ -1326,6 +1330,7 @@ bool TV::Init(bool createWindow)
     errorRecoveryTimerId = StartTimer(kErrorRecoveryCheckFrequency, __LINE__);
     lcdTimerId           = StartTimer(1, __LINE__);
     speedChangeTimerId   = StartTimer(kSpeedChangeCheckFrequency, __LINE__);
+    saveLastPlayPosTimerId = StartTimer(kSaveLastPlayPosTimeout, __LINE__);
 
     LOG(VB_PLAYBACK, LOG_DEBUG, LOC + "-- end");
     return true;
@@ -2777,6 +2782,8 @@ void TV::timerEvent(QTimerEvent *te)
         HandleSpeedChangeTimerEvent();
     else if (timer_id == pipChangeTimerId)
         HandlePxPTimerEvent();
+    else if (timer_id == saveLastPlayPosTimerId)
+        HandleSaveLastPlayPosEvent();
     else
         handled = false;
 
@@ -3321,10 +3328,18 @@ void TV::PrepareToExitPlayer(PlayerContext *ctx, int line, BookmarkAction bookma
             // Don't consider ourselves at the end if the recording is
             // in-progress.
             at_end &= !StateIsRecording(GetState(ctx));
+            bool clear_lastplaypos = true;
             if (at_end && allow_clear_at_end)
                 SetBookmark(ctx, true);
-            if (!at_end && allow_set_before_end)
+            else if (!at_end && allow_set_before_end)
                 SetBookmark(ctx, false);
+            else
+                clear_lastplaypos = false;
+            // If we are setting a bookmark upon exit (or equivalently clearing
+            // it due to exiting at the end), we clean up the unnecessary
+            // lastplaypos mark.
+            if (clear_lastplaypos && ctx->playingInfo)
+                ctx->playingInfo->ClearMarkupMap(MARK_UTIL_LASTPLAYPOS);
         }
         if (db_auto_set_watched)
             ctx->player->SetWatched();
@@ -13383,6 +13398,53 @@ bool TV::HandleOSDVideoExit(PlayerContext *ctx, QString action)
     return hide;
 }
 
+void TV::HandleSaveLastPlayPosEvent(void)
+{
+    // Helper class to save the latest playback position (in a background thread
+    // to avoid playback glitches).  The ctor makes a copy of the ProgramInfo
+    // struct to avoid race conditions if playback ends and deletes objects
+    // before or while the background thread runs.
+    class PositionSaver : public QRunnable
+    {
+    public:
+        PositionSaver(const ProgramInfo &pginfo, uint64_t frame) :
+            m_pginfo(pginfo), m_frame(frame) {}
+        virtual void run(void)
+        {
+            LOG(VB_PLAYBACK, LOG_DEBUG,
+                QString("PositionSaver frame=%1").arg(m_frame));
+            frm_dir_map_t lastPlayPosMap;
+            lastPlayPosMap[m_frame] = MARK_UTIL_LASTPLAYPOS;
+            m_pginfo.ClearMarkupMap(MARK_UTIL_LASTPLAYPOS);
+            m_pginfo.SaveMarkupMap(lastPlayPosMap, MARK_UTIL_LASTPLAYPOS);
+        }
+    private:
+        const ProgramInfo m_pginfo;
+        const uint64_t m_frame;
+    };
+
+    PlayerContext *mctx = GetPlayerReadLock(0, __FILE__, __LINE__);
+    for (uint i = 0; mctx && i < player.size(); ++i)
+    {
+        PlayerContext *ctx = GetPlayer(mctx, i);
+        ctx->LockDeletePlayer(__FILE__, __LINE__);
+        bool playing = ctx->player && !ctx->player->IsPaused();
+        if (playing) // Don't bother saving lastplaypos while paused
+        {
+            uint64_t framesPlayed = ctx->player->GetFramesPlayed();
+            MThreadPool::globalInstance()->
+                start(new PositionSaver(*ctx->playingInfo, framesPlayed),
+                      "PositionSaver");
+        }
+        ReturnPlayerLock(ctx);
+    }
+    ReturnPlayerLock(mctx);
+
+    QMutexLocker locker(&timerIdLock);
+    KillTimer(saveLastPlayPosTimerId);
+    saveLastPlayPosTimerId = StartTimer(kSaveLastPlayPosTimeout, __LINE__);
+}
+
 void TV::SetLastProgram(const ProgramInfo *rcinfo)
 {
     QMutexLocker locker(&lastProgramLock);
diff --git a/mythtv/libs/libmythtv/tv_play.h b/mythtv/libs/libmythtv/tv_play.h
index 8ab38b1..339aee5 100644
--- a/mythtv/libs/libmythtv/tv_play.h
+++ b/mythtv/libs/libmythtv/tv_play.h
@@ -115,6 +115,8 @@ enum {
     kStartTVInPlayList       = 0x02,
     kStartTVByNetworkCommand = 0x04,
     kStartTVIgnoreBookmark   = 0x08,
+    kStartTVIgnoreProgStart  = 0x10,
+    kStartTVIgnoreLastPlayPos= 0x20,
 };
 
 class AskProgramInfo
@@ -409,6 +411,7 @@ class MTV_PUBLIC TV : public QObject, public MenuItemDisplayer
     bool HandlePxPTimerEvent(void);
     bool HandleLCDTimerEvent(void);
     void HandleLCDVolumeTimerEvent(void);
+    void HandleSaveLastPlayPosEvent();
 
     // Commands used by frontend UI screens (PlaybackBox, GuideGrid etc)
     void EditSchedule(const PlayerContext*,
@@ -979,6 +982,7 @@ class MTV_PUBLIC TV : public QObject, public MenuItemDisplayer
     volatile int         speedChangeTimerId;
     volatile int         errorRecoveryTimerId;
     mutable volatile int exitPlayerTimerId;
+    volatile int         saveLastPlayPosTimerId;
     TimerContextMap      stateChangeTimerId;
     TimerContextMap      signalMonitorTimerId;
 
@@ -1095,6 +1099,7 @@ class MTV_PUBLIC TV : public QObject, public MenuItemDisplayer
     static const uint kErrorRecoveryCheckFrequency;
     static const uint kEndOfRecPromptCheckFrequency;
     static const uint kEndOfPlaybackFirstCheckTimer;
+    static const uint kSaveLastPlayPosTimeout;
 };
 
 #endif
diff --git a/mythtv/programs/mythfrontend/playbackbox.cpp b/mythtv/programs/mythfrontend/playbackbox.cpp
index 1050b4a..360d457 100644
--- a/mythtv/programs/mythfrontend/playbackbox.cpp
+++ b/mythtv/programs/mythfrontend/playbackbox.cpp
@@ -556,7 +556,7 @@ bool PlaybackBox::Create()
     connect(m_recordingList, SIGNAL(itemSelected(MythUIButtonListItem*)),
             SLOT(ItemSelected(MythUIButtonListItem*)));
     connect(m_recordingList, SIGNAL(itemClicked(MythUIButtonListItem*)),
-            SLOT(PlayFromBookmark(MythUIButtonListItem*)));
+            SLOT(PlayFromBookmarkOrProgStart(MythUIButtonListItem*)));
     connect(m_recordingList, SIGNAL(itemVisible(MythUIButtonListItem*)),
             SLOT(ItemVisible(MythUIButtonListItem*)));
     connect(m_recordingList, SIGNAL(itemLoaded(MythUIButtonListItem*)),
@@ -2197,6 +2197,25 @@ void PlaybackBox::playSelectedPlaylist(bool _random)
         this, new MythEvent("PLAY_PLAYLIST"));
 }
 
+void PlaybackBox::PlayFromBookmarkOrProgStart(MythUIButtonListItem *item)
+{
+    if (!item)
+        item = m_recordingList->GetItemCurrent();
+
+    if (!item)
+        return;
+
+    ProgramInfo *pginfo = item->GetData().value<ProgramInfo *>();
+
+    const bool ignoreBookmark = false;
+    const bool ignoreProgStart = false;
+    const bool ignoreLastPlayPos = true;
+    const bool underNetworkControl = false;
+    if (pginfo)
+        PlayX(*pginfo, ignoreBookmark, ignoreProgStart, ignoreLastPlayPos,
+              underNetworkControl);
+}
+
 void PlaybackBox::PlayFromBookmark(MythUIButtonListItem *item)
 {
     if (!item)
@@ -2207,8 +2226,13 @@ void PlaybackBox::PlayFromBookmark(MythUIButtonListItem *item)
 
     ProgramInfo *pginfo = item->GetData().value<ProgramInfo *>();
 
+    const bool ignoreBookmark = false;
+    const bool ignoreProgStart = true;
+    const bool ignoreLastPlayPos = true;
+    const bool underNetworkControl = false;
     if (pginfo)
-        PlayX(*pginfo, false, false);
+        PlayX(*pginfo, ignoreBookmark, ignoreProgStart, ignoreLastPlayPos,
+              underNetworkControl);
 }
 
 void PlaybackBox::PlayFromBeginning(MythUIButtonListItem *item)
@@ -2221,17 +2245,44 @@ void PlaybackBox::PlayFromBeginning(MythUIButtonListItem *item)
 
     ProgramInfo *pginfo = item->GetData().value<ProgramInfo *>();
 
+    const bool ignoreBookmark = true;
+    const bool ignoreProgStart = true;
+    const bool ignoreLastPlayPos = true;
+    const bool underNetworkControl = false;
+    if (pginfo)
+        PlayX(*pginfo, ignoreBookmark, ignoreProgStart, ignoreLastPlayPos,
+              underNetworkControl);
+}
+
+void PlaybackBox::PlayFromLastPlayPos(MythUIButtonListItem *item)
+{
+    if (!item)
+        item = m_recordingList->GetItemCurrent();
+
+    if (!item)
+        return;
+
+    ProgramInfo *pginfo = item->GetData().value<ProgramInfo *>();
+
+    const bool ignoreBookmark = true;
+    const bool ignoreProgStart = true;
+    const bool ignoreLastPlayPos = false;
+    const bool underNetworkControl = false;
     if (pginfo)
-        PlayX(*pginfo, true, false);
+        PlayX(*pginfo, ignoreBookmark, ignoreProgStart, ignoreLastPlayPos,
+              underNetworkControl);
 }
 
 void PlaybackBox::PlayX(const ProgramInfo &pginfo,
                         bool ignoreBookmark,
+                        bool ignoreProgStart,
+                        bool ignoreLastPlayPos,
                         bool underNetworkControl)
 {
     if (!m_player)
     {
-        Play(pginfo, false, ignoreBookmark, underNetworkControl);
+        Play(pginfo, false, ignoreBookmark, ignoreProgStart, ignoreLastPlayPos,
+             underNetworkControl);
         return;
     }
 
@@ -2242,6 +2293,7 @@ void PlaybackBox::PlayX(const ProgramInfo &pginfo,
             ignoreBookmark ? "1" : "0");
         m_player_selected_new_show.push_back(
             underNetworkControl ? "1" : "0");
+        // XXX add anything for ignoreProgStart and ignoreLastPlayPos?
     }
     Close();
 }
@@ -2324,7 +2376,7 @@ void PlaybackBox::selected(MythUIButtonListItem *item)
     if (!item)
         return;
 
-    PlayFromBookmark(item);
+    PlayFromBookmarkOrProgStart(item);
 }
 
 void PlaybackBox::popupClosed(QString which, int result)
@@ -2412,7 +2464,8 @@ void PlaybackBox::ShowGroupPopup()
 
 bool PlaybackBox::Play(
     const ProgramInfo &rec,
-    bool inPlaylist, bool ignoreBookmark, bool underNetworkControl)
+    bool inPlaylist, bool ignoreBookmark, bool ignoreProgStart,
+    bool ignoreLastPlayPos, bool underNetworkControl)
 {
     bool playCompleted = false;
 
@@ -2444,6 +2497,8 @@ bool PlaybackBox::Play(
     uint flags =
         (inPlaylist          ? kStartTVInPlayList       : kStartTVNoFlags) |
         (underNetworkControl ? kStartTVByNetworkCommand : kStartTVNoFlags) |
+        (ignoreLastPlayPos   ? kStartTVIgnoreLastPlayPos: kStartTVNoFlags) |
+        (ignoreProgStart     ? kStartTVIgnoreProgStart  : kStartTVNoFlags) |
         (ignoreBookmark      ? kStartTVIgnoreBookmark   : kStartTVNoFlags);
 
     playCompleted = TV::StartTV(&tvrec, flags);
@@ -2912,8 +2967,12 @@ MythMenu* PlaybackBox::createPlayFromMenu()
 
     MythMenu *menu = new MythMenu(title, this, "slotmenu");
 
+    if (pginfo->IsBookmarkSet())
         menu->AddItem(tr("Play from bookmark"), SLOT(PlayFromBookmark()));
     menu->AddItem(tr("Play from beginning"), SLOT(PlayFromBeginning()));
+    if (pginfo->QueryLastPlayPos())
+        menu->AddItem(tr("Play from last played position"),
+                      SLOT(PlayFromLastPlayPos()));
 
     return menu;
 }
@@ -3149,10 +3208,11 @@ void PlaybackBox::ShowActionPopup(const ProgramInfo &pginfo)
 
     if (!sameProgram)
     {
-        if (pginfo.IsBookmarkSet())
+        if (pginfo.IsBookmarkSet() || pginfo.QueryLastPlayPos())
             m_popupMenu->AddItem(tr("Play from..."), NULL, createPlayFromMenu());
         else
-            m_popupMenu->AddItem(tr("Play"), SLOT(PlayFromBookmark()));
+            m_popupMenu->AddItem(tr("Play"),
+                                 SLOT(PlayFromBookmarkOrProgStart()));
     }
 
     if (!m_player)
@@ -3743,8 +3803,12 @@ void PlaybackBox::processNetworkControlCommand(const QString &command)
 
                 pginfo.SetPathname(pginfo.GetPlaybackURL());
 
-                bool ignoreBookmark = (tokens[1] == "PLAY");
-                PlayX(pginfo, ignoreBookmark, true);
+                const bool ignoreBookmark = (tokens[1] == "PLAY");
+                const bool ignoreProgStart = true;
+                const bool ignoreLastPlayPos = true;
+                const bool underNetworkControl = true;
+                PlayX(pginfo, ignoreBookmark, ignoreProgStart,
+                      ignoreLastPlayPos, underNetworkControl);
             }
             else
             {
@@ -3877,7 +3941,7 @@ bool PlaybackBox::keyPressEvent(QKeyEvent *event)
             if (action == "DELETE")
                 deleteSelected(m_recordingList->GetItemCurrent());
             else if (action == ACTION_PLAYBACK)
-                PlayFromBookmark();
+                PlayFromBookmarkOrProgStart();
             else if (action == "DETAILS" || action == "INFO")
                 ShowDetails();
             else if (action == "CUSTOMEDIT")
@@ -4154,7 +4218,13 @@ void PlaybackBox::customEvent(QEvent *event)
                 else if (pginfo)
                 {
                     playnext = false;
-                    Play(*pginfo, kCheckForPlaylistAction == cat, false, false);
+                    const bool ignoreBookmark = false;
+                    const bool ignoreProgStart = false;
+                    const bool ignoreLastPlayPos = true;
+                    const bool underNetworkControl = false;
+                    Play(*pginfo, kCheckForPlaylistAction == cat,
+                         ignoreBookmark, ignoreProgStart, ignoreLastPlayPos,
+                         underNetworkControl);
                 }
             }
 
@@ -4183,8 +4253,13 @@ void PlaybackBox::customEvent(QEvent *event)
             }
 
             ProgramInfo *pginfo = FindProgramInUILists(recordingID);
+            const bool ignoreBookmark = false;
+            const bool ignoreProgStart = true;
+            const bool ignoreLastPlayPos = true;
+            const bool underNetworkControl = false;
             if (pginfo)
-                Play(*pginfo, true, false, false);
+                Play(*pginfo, true, ignoreBookmark, ignoreProgStart,
+                     ignoreLastPlayPos, underNetworkControl);
         }
         else if ((message == "SET_PLAYBACK_URL") && (me->ExtraDataCount() == 2))
         {
diff --git a/mythtv/programs/mythfrontend/playbackbox.h b/mythtv/programs/mythfrontend/playbackbox.h
index 18ae480..44a41e2 100644
--- a/mythtv/programs/mythfrontend/playbackbox.h
+++ b/mythtv/programs/mythfrontend/playbackbox.h
@@ -140,8 +140,10 @@ class PlaybackBox : public ScheduleCommon
     void ItemVisible(MythUIButtonListItem *item);
     void ItemLoaded(MythUIButtonListItem *item);
     void selected(MythUIButtonListItem *item);
+    void PlayFromBookmarkOrProgStart(MythUIButtonListItem *item = NULL);
     void PlayFromBookmark(MythUIButtonListItem *item = NULL);
     void PlayFromBeginning(MythUIButtonListItem *item = NULL);
+    void PlayFromLastPlayPos(MythUIButtonListItem *item = NULL);
     void deleteSelected(MythUIButtonListItem *item);
 
     void SwitchList(void);
@@ -270,11 +272,15 @@ class PlaybackBox : public ScheduleCommon
 
     void PlayX(const ProgramInfo &rec,
                bool ignoreBookmark,
+               bool ignoreProgStart,
+               bool ignoreLastPlayPos,
                bool underNetworkControl);
 
     bool Play(const ProgramInfo &rec,
               bool inPlaylist,
               bool ignoreBookmark,
+              bool ignoreProgStart,
+              bool ignoreLastPlayPos,
               bool underNetworkControl);
 
     virtual ProgramInfo *GetCurrentProgram(void) const;
