Delay showing the font caching dialog until subtitles are actually rendered

This commit is contained in:
Thomas Goyne 2014-03-25 13:21:57 -07:00
parent 39626db787
commit 821f54a372
7 changed files with 71 additions and 29 deletions

View File

@ -100,14 +100,21 @@ void Queue::Sync(Thunk thunk) {
std::mutex m;
std::condition_variable cv;
std::unique_lock<std::mutex> l(m);
std::exception_ptr e;
bool done = false;
DoInvoke([&]{
std::unique_lock<std::mutex> 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() {

View File

@ -132,10 +132,10 @@ void SubtitlesPreview::OnSize(wxSizeEvent &evt) {
bmp = agi::util::make_unique<wxBitmap>(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<DialogProgress>(this);
if (!provider)
provider = SubtitlesProviderFactory::GetProvider(progress.get());
}
catch (...) {
wxMessageBox(

View File

@ -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<DialogProgress> progress;
/// Regenerate the bitmap
void UpdateBitmap();
/// Resize event handler

View File

@ -49,11 +49,15 @@
#include <libaegisub/log.h>
#include <libaegisub/util.h>
#include <atomic>
#include <boost/gil/gil_all.hpp>
#include <boost/range/algorithm_ext/push_back.hpp>
#include <memory>
#include <mutex>
#include <wx/intl.h>
#include <wx/thread.h>
#ifdef __APPLE__
#include <sys/param.h>
#include <libaegisub/util_osx.h>
@ -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<bool> 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<cache_thread_shared> 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<bool>(false);
auto renderer = std::make_shared<ASS_Renderer*>(nullptr);
cache_queue->Async([=]{
LibassSubtitlesProvider::LibassSubtitlesProvider(agi::BackgroundRunner *br)
: br(br)
, shared(std::make_shared<cache_thread_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.

View File

@ -94,7 +94,10 @@ std::shared_ptr<VideoFrame> 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;
}

View File

@ -56,6 +56,7 @@
#include <libaegisub/fs.h>
#include <libaegisub/keyframe.h>
#include <libaegisub/path.h>
#include <libaegisub/util.h>
#include <wx/msgdlg.h>
@ -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;

View File

@ -46,6 +46,7 @@
#include <wx/timer.h>
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;