diff --git a/aegisub/src/threaded_frame_source.cpp b/aegisub/src/threaded_frame_source.cpp index 0225161c6..a362098c1 100644 --- a/aegisub/src/threaded_frame_source.cpp +++ b/aegisub/src/threaded_frame_source.cpp @@ -123,6 +123,7 @@ void *ThreadedFrameSource::Entry() { double time; int frameNum; std::unique_ptr newSubs; + std::deque>> updates; { wxMutexLocker jobLocker(jobMutex); @@ -138,11 +139,27 @@ void *ThreadedFrameSource::Entry() { frameNum = nextFrame; nextTime = -1.; newSubs = move(nextSubs); + updates = move(changedSubs); } - if (newSubs) { + if (newSubs || updates.size()) { wxMutexLocker fileLocker(fileMutex); - subs = move(newSubs); + + if (newSubs) + subs = move(newSubs); + + if (updates.size()) { + size_t i = 0; + auto it = subs->Line.begin(); + // Replace each changed line in subs with the updated version + for (auto& update : updates) { + advance(it, update.first - i); + i = update.first; + subs->Line.insert(it, *update.second.release()); + delete &*it--; + } + } + singleFrame = -1; } @@ -194,13 +211,27 @@ ThreadedFrameSource::~ThreadedFrameSource() { Wait(); } -void ThreadedFrameSource::LoadSubtitles(AssFile *subs) throw() { - subs = new AssFile(*subs); +void ThreadedFrameSource::LoadSubtitles(const AssFile *subs) throw() { + AssFile *copy = new AssFile(*subs); wxMutexLocker locker(jobMutex); // Set nextSubs and let the worker thread move it to subs so that we don't // have to lock fileMutex on the GUI thread, as that can be locked for // extended periods of time with slow-rendering subtitles - nextSubs.reset(subs); + nextSubs.reset(copy); + changedSubs.clear(); +} + +void ThreadedFrameSource::UpdateSubtitles(const AssFile *subs, std::set changes) throw() { + std::deque>> changed; + size_t i = 0; + for (auto const& e : subs->Line) { + if (changes.count(&e)) + changed.emplace_back(i, std::unique_ptr(e.Clone())); + ++i; + } + + wxMutexLocker locker(jobMutex); + changedSubs = std::move(changed); } void ThreadedFrameSource::RequestFrame(int frame, double time) throw() { diff --git a/aegisub/src/threaded_frame_source.h b/aegisub/src/threaded_frame_source.h index a6080d25d..62b101793 100644 --- a/aegisub/src/threaded_frame_source.h +++ b/aegisub/src/threaded_frame_source.h @@ -19,7 +19,9 @@ /// @ingroup video /// +#include #include +#include #include #include @@ -28,6 +30,7 @@ #include class AegiVideoFrame; +class AssEntry; class AssFile; class SubtitlesProvider; class VideoProvider; @@ -46,6 +49,7 @@ class ThreadedFrameSource : public wxThread { int nextFrame; ///< Next queued frame, or -1 for none double nextTime; ///< Next queued time std::unique_ptr nextSubs; ///< Next queued AssFile + std::deque>> changedSubs; /// Subtitles to be loaded the next time a frame is requested std::unique_ptr subs; @@ -70,7 +74,15 @@ public: /// /// This function blocks until is it is safe for the calling thread to /// modify subs - void LoadSubtitles(AssFile *subs) throw(); + void LoadSubtitles(const AssFile *subs) throw(); + + /// @brief Update a previously loaded subtitle file + /// @param subs Subtitle file which was last passed to LoadSubtitles + /// @param changes Set of lines which have changed + /// + /// This function only supports changes to existing lines, and not + /// insertions or deletions. + void UpdateSubtitles(const AssFile *subs, std::set changes) throw(); /// @brief Queue a request for a frame /// @brief frame Frame number diff --git a/aegisub/src/video_context.cpp b/aegisub/src/video_context.cpp index 7ec26e3df..325ee4bc5 100644 --- a/aegisub/src/video_context.cpp +++ b/aegisub/src/video_context.cpp @@ -233,10 +233,13 @@ void VideoContext::Reload() { } } -void VideoContext::OnSubtitlesCommit() { +void VideoContext::OnSubtitlesCommit(int type, std::set const& changed) { if (!IsLoaded()) return; - provider->LoadSubtitles(context->ass); + if (changed.empty()) + provider->LoadSubtitles(context->ass); + else + provider->UpdateSubtitles(context->ass, changed); if (!IsPlaying()) GetFrameAsync(frame_n); } @@ -446,7 +449,7 @@ void VideoContext::LoadTimecodes(wxString filename) { ovrFPS = agi::vfr::Framerate(STD_STR(filename)); ovrTimecodeFile = filename; config::mru->Add("Timecodes", STD_STR(filename)); - OnSubtitlesCommit(); + OnSubtitlesCommit(0, std::set()); TimecodesOpen(ovrFPS); } catch (const agi::FileSystemError&) { @@ -469,7 +472,7 @@ void VideoContext::SaveTimecodes(wxString filename) { void VideoContext::CloseTimecodes() { ovrFPS = agi::vfr::Framerate(); ovrTimecodeFile.clear(); - OnSubtitlesCommit(); + OnSubtitlesCommit(0, std::set()); TimecodesOpen(videoFPS); } diff --git a/aegisub/src/video_context.h b/aegisub/src/video_context.h index a9cd637ea..37e4e4fa2 100644 --- a/aegisub/src/video_context.h +++ b/aegisub/src/video_context.h @@ -32,10 +32,10 @@ /// @ingroup video /// -#include - +#include #include #include +#include #include #include @@ -45,6 +45,7 @@ #include class AegiVideoFrame; +class AssEntry; class SubtitlesProviderErrorEvent; class ThreadedFrameSource; class VideoProvider; @@ -139,7 +140,7 @@ class VideoContext : public wxEvtHandler { void OnVideoError(VideoProviderErrorEvent const& err); void OnSubtitlesError(SubtitlesProviderErrorEvent const& err); - void OnSubtitlesCommit(); + void OnSubtitlesCommit(int type, std::set const& changed); void OnSubtitlesSave(); /// Close the video, keyframes and timecodes