Ticket #11713: 11713_v1.patch
File 11713_v1.patch, 32.9 KB (added by , 9 years ago) |
---|
-
mythtv/libs/libmyth/programinfo.cpp
commit ef4b1a3076f60571c3f0ebcd504bfbf889809483 Author: Jim Stichnoth <jstichnoth@mythtv.org> Date: Wed Apr 15 07:45:15 2015 -0700 Add utility marks for preroll amount and latest playback position. ==== Preroll: The recorder writes a mark of the new type MARK_UTIL_PREROLL as soon as it encounters a keyframe close to the recording's scheduled start time. The frontend starts playback from this keyframe if the user selects Play and there is no explicit bookmark (but not if the user selects one of the "Play from..." menu items). ==== Playback position: During playback, a timer event auto-saves the current playback frame with the new MARK_UTIL_PLAYPOS mark type, every 30 seconds. The actual saving is done in a background thread to avoid playback glitches. Saving is done only during active playback, i.e. not while paused. The MARK_UTIL_PLAYPOS mark is tidied up (cleared) if the user saves a bookmark while exiting playback. The "Play from..." menu gets a new item (a new translatable string): Play from last played position This item is added to the menu only when the recording markup actually contains a MARK_UTIL_PLAYPOS mark. The motivation of MARK_UTIL_PLAYPOS is that if you accidentally exit playback without setting a bookmark (or the frontend crashes, or the OS crashes, or ...), this allows you to resume roughly where you left off. Because of the 30-second timer, if you start playing normally and realize you've made a mistake, you have 30 seconds to exit playback and instead "Play from last played position". Note that different frontends can fight over setting this mark, same as with a regular bookmark. Still to do: 1. Preview generator can do less guessing about preroll. 2. Provide MythVideo launcher with similar preroll/playpos options. diff --git a/mythtv/libs/libmyth/programinfo.cpp b/mythtv/libs/libmyth/programinfo.cpp index 8764134..f4b5a77 100644
a b uint64_t ProgramInfo::QueryBookmark(uint chanid, const QDateTime &recstartts) 2695 2695 return (bookmarkmap.isEmpty()) ? 0 : bookmarkmap.begin().key(); 2696 2696 } 2697 2697 2698 /** \brief Gets any preroll position in database, 2699 * unless the ignore preroll flag is set. 2700 * 2701 * \return Preroll position in frames if the query is executed 2702 * and succeeds, zero otherwise. 2703 */ 2704 uint64_t ProgramInfo::QueryPreroll(void) const 2705 { 2706 if (programflags & FL_IGNOREPREROLL) 2707 return 0; 2708 2709 frm_dir_map_t bookmarkmap; 2710 QueryMarkupMap(bookmarkmap, MARK_UTIL_PREROLL); 2711 2712 return (bookmarkmap.isEmpty()) ? 0 : bookmarkmap.begin().key(); 2713 } 2714 2715 /** \brief Gets any playpos position in database, 2716 * unless the ignore playpos flag is set. 2717 * 2718 * \return PlayPos position in frames if the query is executed 2719 * and succeeds, zero otherwise. 2720 */ 2721 uint64_t ProgramInfo::QueryPlayPos(void) const 2722 { 2723 if (programflags & FL_IGNOREPLAYPOS) 2724 return 0; 2725 2726 frm_dir_map_t bookmarkmap; 2727 QueryMarkupMap(bookmarkmap, MARK_UTIL_PLAYPOS); 2728 2729 return (bookmarkmap.isEmpty()) ? 0 : bookmarkmap.begin().key(); 2730 } 2731 2698 2732 /** \brief Queries "dvdbookmark" table for bookmarking DVD serial 2699 2733 * number. Deletes old dvd bookmarks if "delbookmark" is set. 2700 2734 * -
mythtv/libs/libmyth/programinfo.h
diff --git a/mythtv/libs/libmyth/programinfo.h b/mythtv/libs/libmyth/programinfo.h index ce8159d..b90e609 100644
a b class MPUBLIC ProgramInfo 452 452 453 453 uint32_t GetProgramFlags(void) const { return programflags; } 454 454 ProgramInfoType GetProgramInfoType(void) const 455 { return (ProgramInfoType)((programflags & FL_TYPEMASK) >> 16); }455 { return (ProgramInfoType)((programflags & FL_TYPEMASK) >> 20); } 456 456 bool IsGeneric(void) const; 457 457 bool IsInUsePlaying(void) const { return programflags & FL_INUSEPLAYING;} 458 458 bool IsCommercialFree(void) const { return programflags & FL_CHANCOMMFREE;} … … class MPUBLIC ProgramInfo 489 489 // Quick sets 490 490 void SetTitle(const QString &t) { title = t; title.detach(); } 491 491 void SetProgramInfoType(ProgramInfoType t) 492 { programflags &= ~FL_TYPEMASK; programflags |= ((uint32_t)t<< 16); }492 { programflags &= ~FL_TYPEMASK; programflags |= ((uint32_t)t<<20); } 493 493 void SetPathname(const QString&) const; 494 494 void SetChanID(uint _chanid) { chanid = _chanid; } 495 495 void SetScheduledStartTime(const QDateTime &dt) { startts = dt; } … … class MPUBLIC ProgramInfo 532 532 programflags &= ~FL_IGNOREBOOKMARK; 533 533 programflags |= (ignore) ? FL_IGNOREBOOKMARK : 0; 534 534 } 535 /// \brief If "ignore" is true QueryPreroll() will return 0, otherwise 536 /// QueryPreroll() will return the preroll position if it exists. 537 void SetIgnorePreroll(bool ignore) 538 { 539 programflags &= ~FL_IGNOREPREROLL; 540 programflags |= (ignore) ? FL_IGNOREPREROLL : 0; 541 } 542 /// \brief If "ignore" is true QueryPlayPos() will return 0, otherwise 543 /// QueryPlayPos() will return the playback position if it exists. 544 void SetIgnorePlayPos(bool ignore) 545 { 546 programflags &= ~FL_IGNOREPLAYPOS; 547 programflags |= (ignore) ? FL_IGNOREPLAYPOS : 0; 548 } 535 549 virtual void SetRecordingID(uint _recordedid) { recordedid = _recordedid; } 536 550 void SetRecordingStatus(RecStatus::Type status) { recstatus = status; } 537 551 void SetRecordingRuleType(RecordingType type) { rectype = type; } … … class MPUBLIC ProgramInfo 544 558 uint QueryMplexID(void) const; 545 559 QDateTime QueryBookmarkTimeStamp(void) const; 546 560 uint64_t QueryBookmark(void) const; 561 uint64_t QueryPreroll(void) const; 562 uint64_t QueryPlayPos(void) const; 547 563 CategoryType QueryCategoryType(void) const; 548 564 QStringList QueryDVDBookmark(const QString &serialid) const; 549 565 bool QueryIsEditing(void) const; … … class MPUBLIC ProgramInfo 658 674 static QMap<QString,bool> QueryJobsRunning(int type); 659 675 static QStringList LoadFromScheduler(const QString &altTable, int recordid); 660 676 661 protected:662 677 // Flagging map support methods 663 678 void QueryMarkupMap(frm_dir_map_t&, MarkTypes type, 664 679 bool merge = false) const; … … class MPUBLIC ProgramInfo 667 682 void ClearMarkupMap(MarkTypes type = MARK_ALL, 668 683 int64_t min_frm = -1, int64_t max_frm = -1) const; 669 684 685 protected: 670 686 // Creates a basename from the start and end times 671 687 QString CreateRecordBasename(const QString &ext) const; 672 688 -
mythtv/libs/libmyth/programtypes.cpp
diff --git a/mythtv/libs/libmyth/programtypes.cpp b/mythtv/libs/libmyth/programtypes.cpp index 26b5357..475484b 100644
a b QString toString(MarkTypes type) 53 53 case MARK_VIDEO_RATE: return "VIDEO_RATE"; 54 54 case MARK_DURATION_MS: return "DURATION_MS"; 55 55 case MARK_TOTAL_FRAMES: return "TOTAL_FRAMES"; 56 case MARK_UTIL_PREROLL: return "UTIL_PREROLL"; 57 case MARK_UTIL_PLAYPOS: return "UTIL_PLAYPOS"; 56 58 } 57 59 58 60 return "unknown"; -
mythtv/libs/libmyth/programtypes.h
diff --git a/mythtv/libs/libmyth/programtypes.h b/mythtv/libs/libmyth/programtypes.h index d85ee94..974adb1 100644
a b typedef enum { 74 74 MARK_VIDEO_RATE = 32, 75 75 MARK_DURATION_MS = 33, 76 76 MARK_TOTAL_FRAMES = 34, 77 MARK_UTIL_PREROLL = 40, 78 MARK_UTIL_PLAYPOS = 41, 77 79 } MarkTypes; 78 80 MPUBLIC QString toString(MarkTypes type); 79 81 … … typedef enum FlagMask { 145 147 FL_DUPLICATE = 0x00002000, 146 148 FL_REACTIVATE = 0x00004000, 147 149 FL_IGNOREBOOKMARK = 0x00008000, 150 FL_IGNOREPREROLL = 0x00010000, 151 FL_IGNOREPLAYPOS = 0x00020000, 148 152 // if you move the type mask please edit {Set,Get}ProgramInfoType() 149 FL_TYPEMASK = 0x00 0F0000,150 FL_INUSERECORDING = 0x0 0100000,151 FL_INUSEPLAYING = 0x0 0200000,152 FL_INUSEOTHER = 0x0 0400000,153 FL_TYPEMASK = 0x00F00000, 154 FL_INUSERECORDING = 0x01000000, 155 FL_INUSEPLAYING = 0x02000000, 156 FL_INUSEOTHER = 0x04000000, 153 157 } ProgramFlag; 154 158 155 159 typedef enum ProgramInfoType { -
mythtv/libs/libmythtv/mythplayer.cpp
diff --git a/mythtv/libs/libmythtv/mythplayer.cpp b/mythtv/libs/libmythtv/mythplayer.cpp index b62b86c..2ce4a75 100644
a b void MythPlayer::EventStart(void) 2870 2870 player_ctx->LockPlayingInfo(__FILE__, __LINE__); 2871 2871 { 2872 2872 if (player_ctx->playingInfo) 2873 { 2874 // When initial playback gets underway, we override the ProgramInfo 2875 // flags such that future calls to GetBookmark() will consider only 2876 // an actual bookmark and not preroll or playpos information. 2873 2877 player_ctx->playingInfo->SetIgnoreBookmark(false); 2878 player_ctx->playingInfo->SetIgnorePreroll(true); 2879 player_ctx->playingInfo->SetIgnorePlayPos(true); 2880 } 2874 2881 } 2875 2882 player_ctx->UnlockPlayingInfo(__FILE__, __LINE__); 2876 2883 commBreakMap.LoadMap(player_ctx, framesPlayed); … … uint64_t MythPlayer::GetBookmark(void) 3601 3608 { 3602 3609 player_ctx->LockPlayingInfo(__FILE__, __LINE__); 3603 3610 if (player_ctx->playingInfo) 3611 { 3604 3612 bookmark = player_ctx->playingInfo->QueryBookmark(); 3613 if (bookmark == 0) 3614 bookmark = player_ctx->playingInfo->QueryPreroll(); 3615 if (bookmark == 0) 3616 bookmark = player_ctx->playingInfo->QueryPlayPos(); 3617 } 3605 3618 player_ctx->UnlockPlayingInfo(__FILE__, __LINE__); 3606 3619 } 3607 3620 -
mythtv/libs/libmythtv/recorders/recorderbase.cpp
diff --git a/mythtv/libs/libmythtv/recorders/recorderbase.cpp b/mythtv/libs/libmythtv/recorders/recorderbase.cpp index 669c872..8654562 100644
a b RecorderBase::RecorderBase(TVRec *rec) 56 56 request_pause(false), paused(false), 57 57 request_recording(false), recording(false), 58 58 nextRingBuffer(NULL), nextRecording(NULL), 59 positionMapType(MARK_GOP_BYFRAME) 59 positionMapType(MARK_GOP_BYFRAME), 60 estimatedPrerollMS(0), lastSavedKeyframe(0), lastSavedDuration(0) 60 61 { 61 62 ClearStatistics(); 62 63 QMutexLocker locker(avcodeclock); … … void RecorderBase::SetRecording(const RecordingInfo *pginfo) 114 115 // instance which may lead to the possibility that changes made 115 116 // in the database by one are overwritten by the other 116 117 curRecording = new RecordingInfo(*pginfo); 118 // Compute an estimate of the actual preroll for setting the 119 // MARK_UTIL_PREROLL mark. We can't reliably use 120 // curRecording->GetRecordingStartTime() because the scheduler rounds it 121 // to the nearest minute, so we use the current time instead. 122 estimatedPrerollMS = 123 MythDate::current().msecsTo(curRecording->GetScheduledStartTime()); 117 124 RecordingFile *recFile = curRecording->GetRecordingFile(); 118 125 recFile->m_containerFormat = m_containerFormat; 119 126 recFile->Save(); … … void RecorderBase::SavePositionMap(bool force, bool finished) 579 586 bool needToSave = force; 580 587 positionMapLock.lock(); 581 588 582 uint delta_size = positionMapDelta.size();589 bool has_delta = !positionMapDelta.empty(); 583 590 // set pm_elapsed to a fake large value if the timer hasn't yet started 584 591 uint pm_elapsed = (positionMapTimer.isRunning()) ? 585 592 positionMapTimer.elapsed() : ~0; 586 593 // save on every 1.5 seconds if in the first few frames of a recording 587 594 needToSave |= (positionMap.size() < 30) && 588 (delta_size >= 1)&& (pm_elapsed >= 1500);595 has_delta && (pm_elapsed >= 1500); 589 596 // save every 10 seconds later on 590 needToSave |= (delta_size >= 1)&& (pm_elapsed >= 10000);597 needToSave |= has_delta && (pm_elapsed >= 10000); 591 598 // Assume that durationMapDelta is the same size as 592 599 // positionMapDelta and implicitly use the same logic about when 593 600 // to same durationMapDelta. … … void RecorderBase::SavePositionMap(bool force, bool finished) 595 602 if (curRecording && needToSave) 596 603 { 597 604 positionMapTimer.start(); 598 if ( delta_size)605 if (has_delta) 599 606 { 600 607 // copy the delta map because most times we are called it will be in 601 608 // another thread and we don't want to lock the main recorder thread … … void RecorderBase::SavePositionMap(bool force, bool finished) 609 616 curRecording->SavePositionMapDelta(deltaCopy, positionMapType); 610 617 curRecording->SavePositionMapDelta(durationDeltaCopy, 611 618 MARK_DURATION_MS); 619 620 TryWritePrerollMark(durationDeltaCopy); 612 621 } 613 622 else 614 623 { … … void RecorderBase::SavePositionMap(bool force, bool finished) 626 635 } 627 636 } 628 637 638 void RecorderBase::TryWritePrerollMark(const frm_pos_map_t &durationDeltaCopy) 639 { 640 // Note: all log strings contain "preroll bookmark" for searching. 641 if (estimatedPrerollMS <= 0) 642 { 643 // Do nothing because no preroll bookmark is needed. 644 LOG(VB_RECORD, LOG_DEBUG, 645 QString("No preroll bookmark needed because delta=%1") 646 .arg(estimatedPrerollMS)); 647 return; 648 } 649 frm_pos_map_t::const_iterator last_it = durationDeltaCopy.end(); 650 --last_it; 651 long long bookmarkFrame = 0; 652 LOG(VB_RECORD, LOG_DEBUG, 653 QString("durationDeltaCopy.begin() = (%1,%2)") 654 .arg(durationDeltaCopy.begin().key()) 655 .arg(durationDeltaCopy.begin().value())); 656 if (estimatedPrerollMS > *last_it) 657 { 658 // Do nothing because we haven't reached recstartts yet. 659 LOG(VB_RECORD, LOG_DEBUG, 660 QString("No preroll bookmark yet because estimatedPrerollMS=%1 " 661 "and *last_it=%2") 662 .arg(estimatedPrerollMS).arg(*last_it)); 663 } 664 else if (lastSavedDuration <= estimatedPrerollMS && 665 estimatedPrerollMS < *durationDeltaCopy.begin()) 666 { 667 // Set preroll bookmark @ lastSavedKeyframe 668 LOG(VB_RECORD, LOG_DEBUG, 669 QString("Set preroll bookmark=%1 because %2<=%3<%4") 670 .arg(lastSavedKeyframe).arg(lastSavedDuration) 671 .arg(estimatedPrerollMS).arg(*durationDeltaCopy.begin())); 672 bookmarkFrame = lastSavedKeyframe; 673 } 674 else if (*durationDeltaCopy.begin() <= estimatedPrerollMS && 675 estimatedPrerollMS < *last_it) 676 { 677 frm_pos_map_t::const_iterator upper_it = durationDeltaCopy.begin(); 678 for (; upper_it != durationDeltaCopy.end(); ++upper_it) 679 { 680 if (*upper_it > estimatedPrerollMS) 681 { 682 --upper_it; 683 // Set preroll bookmark @ upper_it.key() 684 LOG(VB_RECORD, LOG_DEBUG, 685 QString("Set preroll bookmark=%1 because " 686 "estimatedPrerollMS=%2 and upper_it.value()=%3") 687 .arg(upper_it.key()).arg(estimatedPrerollMS) 688 .arg(upper_it.value())); 689 bookmarkFrame = upper_it.key(); 690 break; 691 } 692 } 693 } 694 else 695 { 696 // do nothing 697 LOG(VB_RECORD, LOG_DEBUG, "No preroll bookmark due to fallthrough"); 698 } 699 if (bookmarkFrame) 700 { 701 frm_dir_map_t prerollMap; 702 prerollMap[bookmarkFrame] = MARK_UTIL_PREROLL; 703 curRecording->SaveMarkupMap(prerollMap, MARK_UTIL_PREROLL); 704 } 705 lastSavedKeyframe = last_it.key(); 706 lastSavedDuration = last_it.value(); 707 LOG(VB_RECORD, LOG_DEBUG, 708 QString("Setting lastSavedKeyframe=%1 lastSavedDuration=%2 " 709 "for preroll bookmark calculations") 710 .arg(lastSavedKeyframe).arg(lastSavedDuration)); 711 } 712 629 713 void RecorderBase::AspectChange(uint aspect, long long frame) 630 714 { 631 715 MarkTypes mark = MARK_ASPECT_4_3; -
mythtv/libs/libmythtv/recorders/recorderbase.h
diff --git a/mythtv/libs/libmythtv/recorders/recorderbase.h b/mythtv/libs/libmythtv/recorders/recorderbase.h index 8d00836..88e88a7 100644
a b class MTV_PUBLIC RecorderBase : public QRunnable 292 292 */ 293 293 void SetTotalFrames(uint64_t total_frames); 294 294 295 void TryWritePrerollMark(const frm_pos_map_t &durationDeltaCopy); 296 295 297 TVRec *tvrec; 296 298 RingBuffer *ringBuffer; 297 299 bool weMadeBuffer; … … class MTV_PUBLIC RecorderBase : public QRunnable 341 343 frm_pos_map_t durationMapDelta; 342 344 MythTimer positionMapTimer; 343 345 346 // Preroll bookmark support 347 qint64 estimatedPrerollMS; 348 long long lastSavedKeyframe; 349 long long lastSavedDuration; 350 344 351 // Statistics 345 352 // Note: Once we enter RecorderBase::run(), only that thread can 346 353 // update these values safely. These values are read in that thread -
mythtv/libs/libmythtv/tv_play.cpp
diff --git a/mythtv/libs/libmythtv/tv_play.cpp b/mythtv/libs/libmythtv/tv_play.cpp index ed338fa..55f9afb 100644
a b const uint TV::kEndOfPlaybackFirstCheckTimer = 60000; 138 138 #else 139 139 const uint TV::kEndOfPlaybackFirstCheckTimer = 5000; 140 140 #endif 141 const uint TV::kSavePlayPosTimeout = 30000; 141 142 142 143 /** 143 144 * \brief stores last program info. maintains info so long as … … bool TV::StartTV(ProgramInfo *tvrec, uint flags, 335 336 { 336 337 curProgram = new ProgramInfo(*tvrec); 337 338 curProgram->SetIgnoreBookmark(flags & kStartTVIgnoreBookmark); 339 curProgram->SetIgnorePreroll(flags & kStartTVIgnorePreroll); 340 curProgram->SetIgnorePlayPos(flags & kStartTVIgnorePlayPos); 338 341 } 339 342 340 343 PlaySettings settings(curProgram ? curProgram->GetPlaybackGroup() : "Default"); … … TV::TV(PlaySettings *settings) 1070 1073 endOfPlaybackTimerId(0), embedCheckTimerId(0), 1071 1074 endOfRecPromptTimerId(0), videoExitDialogTimerId(0), 1072 1075 pseudoChangeChanTimerId(0), speedChangeTimerId(0), 1073 errorRecoveryTimerId(0), exitPlayerTimerId(0) 1076 errorRecoveryTimerId(0), exitPlayerTimerId(0), 1077 savePlayPosTimerId(0) 1074 1078 { 1075 1079 LOG(VB_GENERAL, LOG_INFO, LOC + "Creating TV object"); 1076 1080 ctorTime.start(); … … bool TV::Init(bool createWindow) 1333 1337 errorRecoveryTimerId = StartTimer(kErrorRecoveryCheckFrequency, __LINE__); 1334 1338 lcdTimerId = StartTimer(1, __LINE__); 1335 1339 speedChangeTimerId = StartTimer(kSpeedChangeCheckFrequency, __LINE__); 1340 savePlayPosTimerId = StartTimer(kSavePlayPosTimeout, __LINE__); 1336 1341 1337 1342 LOG(VB_PLAYBACK, LOG_DEBUG, LOC + "-- end"); 1338 1343 return true; … … void TV::timerEvent(QTimerEvent *te) 2786 2791 HandleSpeedChangeTimerEvent(); 2787 2792 else if (timer_id == pipChangeTimerId) 2788 2793 HandlePxPTimerEvent(); 2794 else if (timer_id == savePlayPosTimerId) 2795 HandleSavePlayPosEvent(); 2789 2796 else 2790 2797 handled = false; 2791 2798 … … void TV::PrepareToExitPlayer(PlayerContext *ctx, int line, BookmarkAction bookma 3330 3337 // Don't consider ourselves at the end if the recording is 3331 3338 // in-progress. 3332 3339 at_end &= !StateIsRecording(GetState(ctx)); 3340 bool clear_playpos = true; 3333 3341 if (at_end && allow_clear_at_end) 3334 3342 SetBookmark(ctx, true); 3335 if (!at_end && allow_set_before_end)3343 else if (!at_end && allow_set_before_end) 3336 3344 SetBookmark(ctx, false); 3345 else 3346 clear_playpos = false; 3347 // If we are setting a bookmark upon exit (or equivalently clearing 3348 // it due to exiting at the end), we clean up the unnecessary 3349 // playpos mark. 3350 if (clear_playpos && ctx->playingInfo) 3351 ctx->playingInfo->ClearMarkupMap(MARK_UTIL_PLAYPOS); 3337 3352 } 3338 3353 if (db_auto_set_watched) 3339 3354 ctx->player->SetWatched(); … … bool TV::HandleOSDVideoExit(PlayerContext *ctx, QString action) 13392 13407 return hide; 13393 13408 } 13394 13409 13410 void TV::HandleSavePlayPosEvent(void) 13411 { 13412 // Helper class to save the latest playback position (in a background thread 13413 // to avoid playback glitches). The ctor makes a copy of the ProgramInfo 13414 // struct to avoid race conditions if playback ends and deletes objects 13415 // before or while the background thread runs. 13416 class PositionSaver : public QRunnable 13417 { 13418 public: 13419 PositionSaver(const ProgramInfo &pginfo, uint64_t frame) : 13420 m_pginfo(pginfo), m_frame(frame) {} 13421 virtual void run(void) 13422 { 13423 LOG(VB_PLAYBACK, LOG_DEBUG, 13424 QString("PositionSaver frame=%1").arg(m_frame)); 13425 frm_dir_map_t playPosMap; 13426 playPosMap[m_frame] = MARK_UTIL_PLAYPOS; 13427 m_pginfo.ClearMarkupMap(MARK_UTIL_PLAYPOS); 13428 m_pginfo.SaveMarkupMap(playPosMap, MARK_UTIL_PLAYPOS); 13429 } 13430 private: 13431 const ProgramInfo m_pginfo; 13432 const uint64_t m_frame; 13433 }; 13434 13435 PlayerContext *mctx = GetPlayerReadLock(0, __FILE__, __LINE__); 13436 for (uint i = 0; mctx && i < player.size(); ++i) 13437 { 13438 PlayerContext *ctx = GetPlayer(mctx, i); 13439 ctx->LockDeletePlayer(__FILE__, __LINE__); 13440 bool playing = ctx->player && !ctx->player->IsPaused(); 13441 if (playing) // Don't bother saving playpos while paused 13442 { 13443 uint64_t framesPlayed = ctx->player->GetFramesPlayed(); 13444 MThreadPool::globalInstance()-> 13445 start(new PositionSaver(*ctx->playingInfo, framesPlayed), 13446 "PositionSaver"); 13447 } 13448 ReturnPlayerLock(ctx); 13449 } 13450 ReturnPlayerLock(mctx); 13451 13452 QMutexLocker locker(&timerIdLock); 13453 KillTimer(savePlayPosTimerId); 13454 savePlayPosTimerId = StartTimer(kSavePlayPosTimeout, __LINE__); 13455 } 13456 13395 13457 void TV::SetLastProgram(const ProgramInfo *rcinfo) 13396 13458 { 13397 13459 QMutexLocker locker(&lastProgramLock); -
mythtv/libs/libmythtv/tv_play.h
diff --git a/mythtv/libs/libmythtv/tv_play.h b/mythtv/libs/libmythtv/tv_play.h index c55bb3a..39c31c9 100644
a b enum { 116 116 kStartTVInPlayList = 0x02, 117 117 kStartTVByNetworkCommand = 0x04, 118 118 kStartTVIgnoreBookmark = 0x08, 119 kStartTVIgnorePreroll = 0x10, 120 kStartTVIgnorePlayPos = 0x20, 119 121 }; 120 122 121 123 class AskProgramInfo … … class MTV_PUBLIC TV : public QObject, public MenuItemDisplayer 410 412 bool HandlePxPTimerEvent(void); 411 413 bool HandleLCDTimerEvent(void); 412 414 void HandleLCDVolumeTimerEvent(void); 415 void HandleSavePlayPosEvent(); 413 416 414 417 // Commands used by frontend UI screens (PlaybackBox, GuideGrid etc) 415 418 void EditSchedule(const PlayerContext*, … … class MTV_PUBLIC TV : public QObject, public MenuItemDisplayer 980 983 volatile int speedChangeTimerId; 981 984 volatile int errorRecoveryTimerId; 982 985 mutable volatile int exitPlayerTimerId; 986 volatile int savePlayPosTimerId; 983 987 TimerContextMap stateChangeTimerId; 984 988 TimerContextMap signalMonitorTimerId; 985 989 … … class MTV_PUBLIC TV : public QObject, public MenuItemDisplayer 1096 1100 static const uint kErrorRecoveryCheckFrequency; 1097 1101 static const uint kEndOfRecPromptCheckFrequency; 1098 1102 static const uint kEndOfPlaybackFirstCheckTimer; 1103 static const uint kSavePlayPosTimeout; 1099 1104 }; 1100 1105 1101 1106 #endif -
mythtv/programs/mythfrontend/playbackbox.cpp
diff --git a/mythtv/programs/mythfrontend/playbackbox.cpp b/mythtv/programs/mythfrontend/playbackbox.cpp index 1050b4a..116b1bf 100644
a b bool PlaybackBox::Create() 556 556 connect(m_recordingList, SIGNAL(itemSelected(MythUIButtonListItem*)), 557 557 SLOT(ItemSelected(MythUIButtonListItem*))); 558 558 connect(m_recordingList, SIGNAL(itemClicked(MythUIButtonListItem*)), 559 SLOT(PlayFromBookmark (MythUIButtonListItem*)));559 SLOT(PlayFromBookmarkOrPreroll(MythUIButtonListItem*))); 560 560 connect(m_recordingList, SIGNAL(itemVisible(MythUIButtonListItem*)), 561 561 SLOT(ItemVisible(MythUIButtonListItem*))); 562 562 connect(m_recordingList, SIGNAL(itemLoaded(MythUIButtonListItem*)), … … void PlaybackBox::playSelectedPlaylist(bool _random) 2197 2197 this, new MythEvent("PLAY_PLAYLIST")); 2198 2198 } 2199 2199 2200 void PlaybackBox::PlayFromBookmarkOrPreroll(MythUIButtonListItem *item) 2201 { 2202 if (!item) 2203 item = m_recordingList->GetItemCurrent(); 2204 2205 if (!item) 2206 return; 2207 2208 ProgramInfo *pginfo = item->GetData().value<ProgramInfo *>(); 2209 2210 const bool ignoreBookmark = false; 2211 const bool ignorePreroll = false; 2212 const bool ignorePlayPos = true; 2213 const bool underNetworkControl = false; 2214 if (pginfo) 2215 PlayX(*pginfo, ignoreBookmark, ignorePreroll, ignorePlayPos, 2216 underNetworkControl); 2217 } 2218 2200 2219 void PlaybackBox::PlayFromBookmark(MythUIButtonListItem *item) 2201 2220 { 2202 2221 if (!item) … … void PlaybackBox::PlayFromBookmark(MythUIButtonListItem *item) 2207 2226 2208 2227 ProgramInfo *pginfo = item->GetData().value<ProgramInfo *>(); 2209 2228 2229 const bool ignoreBookmark = false; 2230 const bool ignorePreroll = true; 2231 const bool ignorePlayPos = true; 2232 const bool underNetworkControl = false; 2210 2233 if (pginfo) 2211 PlayX(*pginfo, false, false); 2234 PlayX(*pginfo, ignoreBookmark, ignorePreroll, ignorePlayPos, 2235 underNetworkControl); 2212 2236 } 2213 2237 2214 2238 void PlaybackBox::PlayFromBeginning(MythUIButtonListItem *item) … … void PlaybackBox::PlayFromBeginning(MythUIButtonListItem *item) 2221 2245 2222 2246 ProgramInfo *pginfo = item->GetData().value<ProgramInfo *>(); 2223 2247 2248 const bool ignoreBookmark = true; 2249 const bool ignorePreroll = true; 2250 const bool ignorePlayPos = true; 2251 const bool underNetworkControl = false; 2252 if (pginfo) 2253 PlayX(*pginfo, ignoreBookmark, ignorePreroll, ignorePlayPos, 2254 underNetworkControl); 2255 } 2256 2257 void PlaybackBox::PlayFromPlayPos(MythUIButtonListItem *item) 2258 { 2259 if (!item) 2260 item = m_recordingList->GetItemCurrent(); 2261 2262 if (!item) 2263 return; 2264 2265 ProgramInfo *pginfo = item->GetData().value<ProgramInfo *>(); 2266 2267 const bool ignoreBookmark = true; 2268 const bool ignorePreroll = true; 2269 const bool ignorePlayPos = false; 2270 const bool underNetworkControl = false; 2224 2271 if (pginfo) 2225 PlayX(*pginfo, true, false); 2272 PlayX(*pginfo, ignoreBookmark, ignorePreroll, ignorePlayPos, 2273 underNetworkControl); 2226 2274 } 2227 2275 2228 2276 void PlaybackBox::PlayX(const ProgramInfo &pginfo, 2229 2277 bool ignoreBookmark, 2278 bool ignorePreroll, 2279 bool ignorePlayPos, 2230 2280 bool underNetworkControl) 2231 2281 { 2232 2282 if (!m_player) 2233 2283 { 2234 Play(pginfo, false, ignoreBookmark, underNetworkControl); 2284 Play(pginfo, false, ignoreBookmark, ignorePreroll, ignorePlayPos, 2285 underNetworkControl); 2235 2286 return; 2236 2287 } 2237 2288 … … void PlaybackBox::PlayX(const ProgramInfo &pginfo, 2242 2293 ignoreBookmark ? "1" : "0"); 2243 2294 m_player_selected_new_show.push_back( 2244 2295 underNetworkControl ? "1" : "0"); 2296 // XXX add anything for ignorePreroll and ignorePlayPos? 2245 2297 } 2246 2298 Close(); 2247 2299 } … … void PlaybackBox::selected(MythUIButtonListItem *item) 2324 2376 if (!item) 2325 2377 return; 2326 2378 2327 PlayFromBookmark (item);2379 PlayFromBookmarkOrPreroll(item); 2328 2380 } 2329 2381 2330 2382 void PlaybackBox::popupClosed(QString which, int result) … … void PlaybackBox::ShowGroupPopup() 2412 2464 2413 2465 bool PlaybackBox::Play( 2414 2466 const ProgramInfo &rec, 2415 bool inPlaylist, bool ignoreBookmark, bool underNetworkControl) 2467 bool inPlaylist, bool ignoreBookmark, bool ignorePreroll, 2468 bool ignorePlayPos, bool underNetworkControl) 2416 2469 { 2417 2470 bool playCompleted = false; 2418 2471 … … bool PlaybackBox::Play( 2444 2497 uint flags = 2445 2498 (inPlaylist ? kStartTVInPlayList : kStartTVNoFlags) | 2446 2499 (underNetworkControl ? kStartTVByNetworkCommand : kStartTVNoFlags) | 2500 (ignorePlayPos ? kStartTVIgnorePlayPos : kStartTVNoFlags) | 2501 (ignorePreroll ? kStartTVIgnorePreroll : kStartTVNoFlags) | 2447 2502 (ignoreBookmark ? kStartTVIgnoreBookmark : kStartTVNoFlags); 2448 2503 2449 2504 playCompleted = TV::StartTV(&tvrec, flags); … … MythMenu* PlaybackBox::createPlayFromMenu() 2912 2967 2913 2968 MythMenu *menu = new MythMenu(title, this, "slotmenu"); 2914 2969 2915 menu->AddItem(tr("Play from bookmark"), SLOT(PlayFromBookmark())); 2970 if (pginfo->IsBookmarkSet()) 2971 menu->AddItem(tr("Play from bookmark"), SLOT(PlayFromBookmark())); 2916 2972 menu->AddItem(tr("Play from beginning"), SLOT(PlayFromBeginning())); 2973 if (pginfo->QueryPlayPos()) 2974 menu->AddItem(tr("Play from last played position"), 2975 SLOT(PlayFromPlayPos())); 2917 2976 2918 2977 return menu; 2919 2978 } … … void PlaybackBox::ShowActionPopup(const ProgramInfo &pginfo) 3149 3208 3150 3209 if (!sameProgram) 3151 3210 { 3152 if (pginfo.IsBookmarkSet() )3211 if (pginfo.IsBookmarkSet() || pginfo.QueryPlayPos()) 3153 3212 m_popupMenu->AddItem(tr("Play from..."), NULL, createPlayFromMenu()); 3154 3213 else 3155 m_popupMenu->AddItem(tr("Play"), SLOT(PlayFromBookmark ()));3214 m_popupMenu->AddItem(tr("Play"), SLOT(PlayFromBookmarkOrPreroll())); 3156 3215 } 3157 3216 3158 3217 if (!m_player) … … void PlaybackBox::processNetworkControlCommand(const QString &command) 3743 3802 3744 3803 pginfo.SetPathname(pginfo.GetPlaybackURL()); 3745 3804 3746 bool ignoreBookmark = (tokens[1] == "PLAY"); 3747 PlayX(pginfo, ignoreBookmark, true); 3805 const bool ignoreBookmark = (tokens[1] == "PLAY"); 3806 const bool ignorePreroll = true; 3807 const bool ignorePlayPos = true; 3808 const bool underNetworkControl = true; 3809 PlayX(pginfo, ignoreBookmark, ignorePreroll, ignorePlayPos, 3810 underNetworkControl); 3748 3811 } 3749 3812 else 3750 3813 { … … bool PlaybackBox::keyPressEvent(QKeyEvent *event) 3877 3940 if (action == "DELETE") 3878 3941 deleteSelected(m_recordingList->GetItemCurrent()); 3879 3942 else if (action == ACTION_PLAYBACK) 3880 PlayFromBookmark ();3943 PlayFromBookmarkOrPreroll(); 3881 3944 else if (action == "DETAILS" || action == "INFO") 3882 3945 ShowDetails(); 3883 3946 else if (action == "CUSTOMEDIT") … … void PlaybackBox::customEvent(QEvent *event) 4154 4217 else if (pginfo) 4155 4218 { 4156 4219 playnext = false; 4157 Play(*pginfo, kCheckForPlaylistAction == cat, false, false); 4220 const bool ignoreBookmark = false; 4221 const bool ignorePreroll = false; 4222 const bool ignorePlayPos = true; 4223 const bool underNetworkControl = false; 4224 Play(*pginfo, kCheckForPlaylistAction == cat, 4225 ignoreBookmark, ignorePreroll, ignorePlayPos, 4226 underNetworkControl); 4158 4227 } 4159 4228 } 4160 4229 … … void PlaybackBox::customEvent(QEvent *event) 4183 4252 } 4184 4253 4185 4254 ProgramInfo *pginfo = FindProgramInUILists(recordingID); 4255 const bool ignoreBookmark = false; 4256 const bool ignorePreroll = true; 4257 const bool ignorePlayPos = true; 4258 const bool underNetworkControl = false; 4186 4259 if (pginfo) 4187 Play(*pginfo, true, false, false); 4260 Play(*pginfo, true, ignoreBookmark, ignorePreroll, 4261 ignorePlayPos, underNetworkControl); 4188 4262 } 4189 4263 else if ((message == "SET_PLAYBACK_URL") && (me->ExtraDataCount() == 2)) 4190 4264 { -
mythtv/programs/mythfrontend/playbackbox.h
diff --git a/mythtv/programs/mythfrontend/playbackbox.h b/mythtv/programs/mythfrontend/playbackbox.h index 18ae480..4415113 100644
a b class PlaybackBox : public ScheduleCommon 140 140 void ItemVisible(MythUIButtonListItem *item); 141 141 void ItemLoaded(MythUIButtonListItem *item); 142 142 void selected(MythUIButtonListItem *item); 143 void PlayFromBookmarkOrPreroll(MythUIButtonListItem *item = NULL); 143 144 void PlayFromBookmark(MythUIButtonListItem *item = NULL); 144 145 void PlayFromBeginning(MythUIButtonListItem *item = NULL); 146 void PlayFromPlayPos(MythUIButtonListItem *item = NULL); 145 147 void deleteSelected(MythUIButtonListItem *item); 146 148 147 149 void SwitchList(void); … … class PlaybackBox : public ScheduleCommon 270 272 271 273 void PlayX(const ProgramInfo &rec, 272 274 bool ignoreBookmark, 275 bool ignorePreroll, 276 bool ignorePlayPos, 273 277 bool underNetworkControl); 274 278 275 279 bool Play(const ProgramInfo &rec, 276 280 bool inPlaylist, 277 281 bool ignoreBookmark, 282 bool ignorePreroll, 283 bool ignorePlayPos, 278 284 bool underNetworkControl); 279 285 280 286 virtual ProgramInfo *GetCurrentProgram(void) const;