diff --git a/libaegisub/common/dispatch.cpp b/libaegisub/common/dispatch.cpp index 5ef2682e1..66dc58af6 100644 --- a/libaegisub/common/dispatch.cpp +++ b/libaegisub/common/dispatch.cpp @@ -100,14 +100,21 @@ void Queue::Sync(Thunk thunk) { std::mutex m; std::condition_variable cv; std::unique_lock l(m); + std::exception_ptr e; bool done = false; DoInvoke([&]{ std::unique_lock l(m); - thunk(); + try { + thunk(); + } + catch (...) { + e = std::current_exception(); + } done = true; cv.notify_all(); }); cv.wait(l, [&]{ return done; }); + if (e) std::rethrow_exception(e); } Queue& Main() { diff --git a/src/subs_preview.cpp b/src/subs_preview.cpp index 85371e7a1..affbb97a9 100644 --- a/src/subs_preview.cpp +++ b/src/subs_preview.cpp @@ -132,10 +132,10 @@ void SubtitlesPreview::OnSize(wxSizeEvent &evt) { bmp = agi::util::make_unique(w, h, -1); vid.reset(new DummyVideoProvider(0.0, 10, w, h, back_color, true)); try { - if (!provider) { - DialogProgress progress(this); - provider = SubtitlesProviderFactory::GetProvider(&progress); - } + if (!progress) + progress = agi::util::make_unique(this); + if (!provider) + provider = SubtitlesProviderFactory::GetProvider(progress.get()); } catch (...) { wxMessageBox( diff --git a/src/subs_preview.h b/src/subs_preview.h index 3f3077e20..33cbc4672 100644 --- a/src/subs_preview.h +++ b/src/subs_preview.h @@ -38,6 +38,7 @@ class AssFile; class AssStyle; +class DialogProgress; class SubtitlesProvider; class VideoProvider; @@ -58,6 +59,8 @@ class SubtitlesPreview final : public wxWindow { /// Line used to render the specified text AssDialogue* line; + std::unique_ptr progress; + /// Regenerate the bitmap void UpdateBitmap(); /// Resize event handler diff --git a/src/subtitles_provider_libass.cpp b/src/subtitles_provider_libass.cpp index 404274089..e015118ef 100644 --- a/src/subtitles_provider_libass.cpp +++ b/src/subtitles_provider_libass.cpp @@ -49,11 +49,15 @@ #include #include +#include #include #include #include #include +#include +#include + #ifdef __APPLE__ #include #include @@ -88,10 +92,40 @@ void msg_callback(int level, const char *fmt, va_list args, void *) { #define CONFIG_PATH nullptr #endif +// Stuff used on the cache thread, owned by a shared_ptr in case the provider +// gets deleted before the cache finishing updating +struct cache_thread_shared { + ASS_Renderer *renderer = nullptr; + std::atomic ready{false}; + ~cache_thread_shared() { if (renderer) ass_renderer_done(renderer); } +}; + class LibassSubtitlesProvider final : public SubtitlesProvider { - ASS_Renderer* ass_renderer = nullptr; + agi::BackgroundRunner *br; + std::shared_ptr shared; ASS_Track* ass_track = nullptr; + ASS_Renderer *renderer() { + if (shared->ready) + return shared->renderer; + + auto block = [&]{ + br->Run([=](agi::ProgressSink *ps) { + ps->SetTitle(from_wx(_("Updating font index"))); + ps->SetMessage(from_wx(_("This may take several minutes"))); + ps->SetIndeterminate(); + while (!shared->ready && !ps->IsCancelled()) + agi::util::sleep_for(250); + }); + }; + + if (wxThread::IsMain()) + block(); + else + agi::dispatch::Main().Sync(block); + return shared->renderer; + } + public: LibassSubtitlesProvider(agi::BackgroundRunner *br); ~LibassSubtitlesProvider(); @@ -100,34 +134,24 @@ public: void DrawSubtitles(VideoFrame &dst, double time) override; }; -LibassSubtitlesProvider::LibassSubtitlesProvider(agi::BackgroundRunner *br) { - auto done = std::make_shared(false); - auto renderer = std::make_shared(nullptr); - cache_queue->Async([=]{ +LibassSubtitlesProvider::LibassSubtitlesProvider(agi::BackgroundRunner *br) +: br(br) +, shared(std::make_shared()) +{ + auto state = shared; + cache_queue->Async([state]{ auto ass_renderer = ass_renderer_init(library); if (ass_renderer) { ass_set_font_scale(ass_renderer, 1.); ass_set_fonts(ass_renderer, nullptr, "Sans", 1, CONFIG_PATH, true); } - *done = true; - *renderer = ass_renderer; + state->renderer = ass_renderer; + state->ready = true; }); - - br->Run([=](agi::ProgressSink *ps) { - ps->SetTitle(from_wx(_("Updating font index"))); - ps->SetMessage(from_wx(_("This may take several minutes"))); - ps->SetIndeterminate(); - while (!*done && !ps->IsCancelled()) - agi::util::sleep_for(250); - }); - - ass_renderer = *renderer; - if (!ass_renderer) throw "ass_renderer_init failed"; } LibassSubtitlesProvider::~LibassSubtitlesProvider() { if (ass_track) ass_free_track(ass_track); - if (ass_renderer) ass_renderer_done(ass_renderer); } struct Writer { @@ -166,9 +190,9 @@ void LibassSubtitlesProvider::LoadSubtitles(AssFile *subs) { #define _a(c) ((c)&0xFF) void LibassSubtitlesProvider::DrawSubtitles(VideoFrame &frame,double time) { - ass_set_frame_size(ass_renderer, frame.width, frame.height); + ass_set_frame_size(renderer(), frame.width, frame.height); - ASS_Image* img = ass_render_frame(ass_renderer, ass_track, int(time * 1000), nullptr); + ASS_Image* img = ass_render_frame(renderer(), ass_track, int(time * 1000), nullptr); // libass actually returns several alpha-masked monochrome images. // Here, we loop through their linked list, get the colour of the current, and blend into the frame. diff --git a/src/threaded_frame_source.cpp b/src/threaded_frame_source.cpp index 428cf6d56..fe181f577 100644 --- a/src/threaded_frame_source.cpp +++ b/src/threaded_frame_source.cpp @@ -94,7 +94,10 @@ std::shared_ptr ThreadedFrameSource::ProcFrame(int frame_number, dou } catch (std::string const& err) { throw SubtitlesProviderErrorEvent(err); } - subs_provider->DrawSubtitles(*frame, time / 1000.); + try { + subs_provider->DrawSubtitles(*frame, time / 1000.); + } + catch (agi::UserCancelException const&) { } return frame; } diff --git a/src/video_context.cpp b/src/video_context.cpp index 2a17c9396..a4206131a 100644 --- a/src/video_context.cpp +++ b/src/video_context.cpp @@ -56,6 +56,7 @@ #include #include #include +#include #include @@ -123,9 +124,10 @@ void VideoContext::SetVideo(const agi::fs::path &filename) { bool commit_subs = false; try { - DialogProgress progress(context->parent); + if (!progress) + progress = new DialogProgress(context->parent); auto old_matrix = context->ass->GetScriptInfo("YCbCr Matrix"); - provider.reset(new ThreadedFrameSource(filename, old_matrix, this, &progress)); + provider.reset(new ThreadedFrameSource(filename, old_matrix, this, progress)); video_provider = provider->GetVideoProvider(); video_filename = filename; diff --git a/src/video_context.h b/src/video_context.h index f5efbfdf3..5dc200b25 100644 --- a/src/video_context.h +++ b/src/video_context.h @@ -46,6 +46,7 @@ #include class AssDialogue; +class DialogProgress; class ThreadedFrameSource; class VideoProvider; struct SubtitlesProviderErrorEvent; @@ -85,6 +86,8 @@ class VideoContext final : public wxEvtHandler { agi::Context *context; + DialogProgress *progress = nullptr; + /// The video provider owned by the threaded frame source, or nullptr if no /// video is open VideoProvider *video_provider;