forked from mia/Aegisub
Avoid making a full copy of the file for ThreadedFrameSource when possible
When the set of changed lines is populated, only copy those lines rather than the entire file. On large files, this makes amend commits roughly twice as fast when video is open.
This commit is contained in:
parent
fee60be5db
commit
229e5d98c5
4 changed files with 60 additions and 13 deletions
|
@ -123,6 +123,7 @@ void *ThreadedFrameSource::Entry() {
|
||||||
double time;
|
double time;
|
||||||
int frameNum;
|
int frameNum;
|
||||||
std::unique_ptr<AssFile> newSubs;
|
std::unique_ptr<AssFile> newSubs;
|
||||||
|
std::deque<std::pair<size_t, std::unique_ptr<AssEntry>>> updates;
|
||||||
{
|
{
|
||||||
wxMutexLocker jobLocker(jobMutex);
|
wxMutexLocker jobLocker(jobMutex);
|
||||||
|
|
||||||
|
@ -138,11 +139,27 @@ void *ThreadedFrameSource::Entry() {
|
||||||
frameNum = nextFrame;
|
frameNum = nextFrame;
|
||||||
nextTime = -1.;
|
nextTime = -1.;
|
||||||
newSubs = move(nextSubs);
|
newSubs = move(nextSubs);
|
||||||
|
updates = move(changedSubs);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (newSubs) {
|
if (newSubs || updates.size()) {
|
||||||
wxMutexLocker fileLocker(fileMutex);
|
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;
|
singleFrame = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -194,13 +211,27 @@ ThreadedFrameSource::~ThreadedFrameSource() {
|
||||||
Wait();
|
Wait();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ThreadedFrameSource::LoadSubtitles(AssFile *subs) throw() {
|
void ThreadedFrameSource::LoadSubtitles(const AssFile *subs) throw() {
|
||||||
subs = new AssFile(*subs);
|
AssFile *copy = new AssFile(*subs);
|
||||||
wxMutexLocker locker(jobMutex);
|
wxMutexLocker locker(jobMutex);
|
||||||
// Set nextSubs and let the worker thread move it to subs so that we don't
|
// 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
|
// have to lock fileMutex on the GUI thread, as that can be locked for
|
||||||
// extended periods of time with slow-rendering subtitles
|
// extended periods of time with slow-rendering subtitles
|
||||||
nextSubs.reset(subs);
|
nextSubs.reset(copy);
|
||||||
|
changedSubs.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ThreadedFrameSource::UpdateSubtitles(const AssFile *subs, std::set<const AssEntry*> changes) throw() {
|
||||||
|
std::deque<std::pair<size_t, std::unique_ptr<AssEntry>>> changed;
|
||||||
|
size_t i = 0;
|
||||||
|
for (auto const& e : subs->Line) {
|
||||||
|
if (changes.count(&e))
|
||||||
|
changed.emplace_back(i, std::unique_ptr<AssEntry>(e.Clone()));
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
|
||||||
|
wxMutexLocker locker(jobMutex);
|
||||||
|
changedSubs = std::move(changed);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ThreadedFrameSource::RequestFrame(int frame, double time) throw() {
|
void ThreadedFrameSource::RequestFrame(int frame, double time) throw() {
|
||||||
|
|
|
@ -19,7 +19,9 @@
|
||||||
/// @ingroup video
|
/// @ingroup video
|
||||||
///
|
///
|
||||||
|
|
||||||
|
#include <deque>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <set>
|
||||||
|
|
||||||
#include <wx/event.h>
|
#include <wx/event.h>
|
||||||
#include <wx/thread.h>
|
#include <wx/thread.h>
|
||||||
|
@ -28,6 +30,7 @@
|
||||||
#include <libaegisub/scoped_ptr.h>
|
#include <libaegisub/scoped_ptr.h>
|
||||||
|
|
||||||
class AegiVideoFrame;
|
class AegiVideoFrame;
|
||||||
|
class AssEntry;
|
||||||
class AssFile;
|
class AssFile;
|
||||||
class SubtitlesProvider;
|
class SubtitlesProvider;
|
||||||
class VideoProvider;
|
class VideoProvider;
|
||||||
|
@ -46,6 +49,7 @@ class ThreadedFrameSource : public wxThread {
|
||||||
int nextFrame; ///< Next queued frame, or -1 for none
|
int nextFrame; ///< Next queued frame, or -1 for none
|
||||||
double nextTime; ///< Next queued time
|
double nextTime; ///< Next queued time
|
||||||
std::unique_ptr<AssFile> nextSubs; ///< Next queued AssFile
|
std::unique_ptr<AssFile> nextSubs; ///< Next queued AssFile
|
||||||
|
std::deque<std::pair<size_t, std::unique_ptr<AssEntry>>> changedSubs;
|
||||||
|
|
||||||
/// Subtitles to be loaded the next time a frame is requested
|
/// Subtitles to be loaded the next time a frame is requested
|
||||||
std::unique_ptr<AssFile> subs;
|
std::unique_ptr<AssFile> subs;
|
||||||
|
@ -70,7 +74,15 @@ public:
|
||||||
///
|
///
|
||||||
/// This function blocks until is it is safe for the calling thread to
|
/// This function blocks until is it is safe for the calling thread to
|
||||||
/// modify subs
|
/// 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<const AssEntry *> changes) throw();
|
||||||
|
|
||||||
/// @brief Queue a request for a frame
|
/// @brief Queue a request for a frame
|
||||||
/// @brief frame Frame number
|
/// @brief frame Frame number
|
||||||
|
|
|
@ -233,10 +233,13 @@ void VideoContext::Reload() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void VideoContext::OnSubtitlesCommit() {
|
void VideoContext::OnSubtitlesCommit(int type, std::set<const AssEntry *> const& changed) {
|
||||||
if (!IsLoaded()) return;
|
if (!IsLoaded()) return;
|
||||||
|
|
||||||
provider->LoadSubtitles(context->ass);
|
if (changed.empty())
|
||||||
|
provider->LoadSubtitles(context->ass);
|
||||||
|
else
|
||||||
|
provider->UpdateSubtitles(context->ass, changed);
|
||||||
if (!IsPlaying())
|
if (!IsPlaying())
|
||||||
GetFrameAsync(frame_n);
|
GetFrameAsync(frame_n);
|
||||||
}
|
}
|
||||||
|
@ -446,7 +449,7 @@ void VideoContext::LoadTimecodes(wxString filename) {
|
||||||
ovrFPS = agi::vfr::Framerate(STD_STR(filename));
|
ovrFPS = agi::vfr::Framerate(STD_STR(filename));
|
||||||
ovrTimecodeFile = filename;
|
ovrTimecodeFile = filename;
|
||||||
config::mru->Add("Timecodes", STD_STR(filename));
|
config::mru->Add("Timecodes", STD_STR(filename));
|
||||||
OnSubtitlesCommit();
|
OnSubtitlesCommit(0, std::set<const AssEntry*>());
|
||||||
TimecodesOpen(ovrFPS);
|
TimecodesOpen(ovrFPS);
|
||||||
}
|
}
|
||||||
catch (const agi::FileSystemError&) {
|
catch (const agi::FileSystemError&) {
|
||||||
|
@ -469,7 +472,7 @@ void VideoContext::SaveTimecodes(wxString filename) {
|
||||||
void VideoContext::CloseTimecodes() {
|
void VideoContext::CloseTimecodes() {
|
||||||
ovrFPS = agi::vfr::Framerate();
|
ovrFPS = agi::vfr::Framerate();
|
||||||
ovrTimecodeFile.clear();
|
ovrTimecodeFile.clear();
|
||||||
OnSubtitlesCommit();
|
OnSubtitlesCommit(0, std::set<const AssEntry*>());
|
||||||
TimecodesOpen(videoFPS);
|
TimecodesOpen(videoFPS);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -32,10 +32,10 @@
|
||||||
/// @ingroup video
|
/// @ingroup video
|
||||||
///
|
///
|
||||||
|
|
||||||
#include <time.h>
|
#include <ctime>
|
||||||
|
|
||||||
#include <list>
|
#include <list>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <set>
|
||||||
|
|
||||||
#include <wx/timer.h>
|
#include <wx/timer.h>
|
||||||
#include <wx/stopwatch.h>
|
#include <wx/stopwatch.h>
|
||||||
|
@ -45,6 +45,7 @@
|
||||||
#include <libaegisub/vfr.h>
|
#include <libaegisub/vfr.h>
|
||||||
|
|
||||||
class AegiVideoFrame;
|
class AegiVideoFrame;
|
||||||
|
class AssEntry;
|
||||||
class SubtitlesProviderErrorEvent;
|
class SubtitlesProviderErrorEvent;
|
||||||
class ThreadedFrameSource;
|
class ThreadedFrameSource;
|
||||||
class VideoProvider;
|
class VideoProvider;
|
||||||
|
@ -139,7 +140,7 @@ class VideoContext : public wxEvtHandler {
|
||||||
void OnVideoError(VideoProviderErrorEvent const& err);
|
void OnVideoError(VideoProviderErrorEvent const& err);
|
||||||
void OnSubtitlesError(SubtitlesProviderErrorEvent const& err);
|
void OnSubtitlesError(SubtitlesProviderErrorEvent const& err);
|
||||||
|
|
||||||
void OnSubtitlesCommit();
|
void OnSubtitlesCommit(int type, std::set<const AssEntry *> const& changed);
|
||||||
void OnSubtitlesSave();
|
void OnSubtitlesSave();
|
||||||
|
|
||||||
/// Close the video, keyframes and timecodes
|
/// Close the video, keyframes and timecodes
|
||||||
|
|
Loading…
Reference in a new issue