summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMartin Lambers <marlam@marlam.de>2011-08-06 14:58:23 +0200
committerMartin Lambers <marlam@marlam.de>2011-08-06 14:58:23 +0200
commit381a4ade2d43e3708ebff79f9725d918aee860b2 (patch)
tree0feff372749645b4dec993d5ef585352eaa5a42e
parent093a435d2444e1dfd9dcc4f01f4f05e928628fff (diff)
downloadbino-381a4ade2d43e3708ebff79f9725d918aee860b2.tar.gz
Initialize subtitle renderer in a separate thread in the background.
If fontconfig needs to build a cache first (mostly on Windows and Mac OS X), the initialization of libass can take a long time. Therefore we do it in a background thread and hope that it is finished when the first subtitle has to be displayed. If it is not, we wait for it and display a message to the user. Video playback is paused for the duration of the wait so that no frames are dropped.
-rw-r--r--src/base/thread.cpp17
-rw-r--r--src/base/thread.h9
-rw-r--r--src/player.cpp4
-rw-r--r--src/player_equalizer.cpp24
-rw-r--r--src/subtitle_renderer.cpp81
-rw-r--r--src/subtitle_renderer.h31
-rw-r--r--src/video_output.cpp1
-rw-r--r--src/video_output.h7
-rw-r--r--src/video_output_qt.cpp56
-rw-r--r--src/video_output_qt.h1
10 files changed, 209 insertions, 22 deletions
diff --git a/src/base/thread.cpp b/src/base/thread.cpp
index 0ee3057..0062203 100644
--- a/src/base/thread.cpp
+++ b/src/base/thread.cpp
@@ -125,7 +125,10 @@ void *thread::__run(void *p)
}
catch (...)
{
- t->__exception = exc(_("Unknown exception."));
+ // We must assume this means thread cancellation. In this
+ // case, we *must* rethrow the exception.
+ t->__running = false;
+ throw;
}
t->__running = false;
return NULL;
@@ -168,3 +171,15 @@ void thread::finish()
throw exception();
}
}
+
+void thread::cancel()
+{
+ __wait_mutex.lock();
+ int e = pthread_cancel(__thread_id);
+ if (e != 0)
+ {
+ __wait_mutex.unlock();
+ throw exc(str::asprintf(_("Cannot cancel thread: %s"), std::strerror(e)), e);
+ }
+ __wait_mutex.unlock();
+}
diff --git a/src/base/thread.h b/src/base/thread.h
index 8a07439..a4a9c80 100644
--- a/src/base/thread.h
+++ b/src/base/thread.h
@@ -130,6 +130,12 @@ public:
// running, this function does nothing.
void start();
+ // Returns whether this thread is currently running.
+ bool is_running()
+ {
+ return __running;
+ }
+
// Wait for the thread to finish. If the thread is not running, this function
// returns immediately.
void wait();
@@ -138,6 +144,9 @@ public:
// run() function might have thrown during its execution.
void finish();
+ // Cancel a thread. This is dangerous and should not be used.
+ void cancel();
+
// Get an exception that the run() function might have thrown.
const exc &exception() const
{
diff --git a/src/player.cpp b/src/player.cpp
index bff89a1..7edf4a2 100644
--- a/src/player.cpp
+++ b/src/player.cpp
@@ -685,6 +685,10 @@ bool player::run_step()
}
if (prep_frame)
{
+ if (_current_subtitle_box.is_valid() && _video_output)
+ {
+ _master_time_start += _video_output->wait_for_subtitle_renderer();
+ }
_video_output->prepare_next_frame(_video_frame, _current_subtitle_box);
}
else if (drop_frame)
diff --git a/src/player_equalizer.cpp b/src/player_equalizer.cpp
index 343cee6..17ac219 100644
--- a/src/player_equalizer.cpp
+++ b/src/player_equalizer.cpp
@@ -176,6 +176,26 @@ protected:
void trigger_resize(int, int) { }
public:
+ int64_t wait_for_subtitle_renderer()
+ {
+ if (!_subtitle_renderer.is_initialized())
+ {
+ msg::wrn(_("Waiting for subtitle renderer initialization..."));
+ try
+ {
+ while (!_subtitle_renderer.is_initialized())
+ {
+ usleep(10000);
+ }
+ }
+ catch (std::exception &e)
+ {
+ msg::err("%s", e.what());
+ abort();
+ }
+ }
+ return 0;
+ }
bool supports_stereo() const { return false; }
int screen_width() { return _channel->getPixelViewport().w / _channel->getViewport().w; }
int screen_height() { return _channel->getPixelViewport().h / _channel->getViewport().h; }
@@ -999,6 +1019,10 @@ protected:
if (node->frame_data.prep_frame)
{
getWindow()->makeCurrent();
+ if (node->frame_data.subtitle.is_valid())
+ {
+ _video_output.wait_for_subtitle_renderer();
+ }
_video_output.prepare_next_frame(node->get_video_frame(), node->frame_data.subtitle);
}
if (node->frame_data.display_frame)
diff --git a/src/subtitle_renderer.cpp b/src/subtitle_renderer.cpp
index 93c2adc..dc6d470 100644
--- a/src/subtitle_renderer.cpp
+++ b/src/subtitle_renderer.cpp
@@ -45,6 +45,7 @@ extern "C"
#include "gettext.h"
#define _(string) gettext(string)
+#include "dbg.h"
#include "exc.h"
#include "str.h"
#include "blob.h"
@@ -54,6 +55,17 @@ extern "C"
#include "subtitle_renderer.h"
+subtitle_renderer_initializer::subtitle_renderer_initializer(subtitle_renderer &renderer) :
+ _subtitle_renderer(renderer)
+{
+}
+
+void subtitle_renderer_initializer::run()
+{
+ _subtitle_renderer.init();
+}
+
+
/* Rendering subtitles with LibASS is not thread-safe.
*
* We have multiple concurrent subtitle rendering threads if we are running from Equalizer
@@ -70,16 +82,28 @@ extern "C"
static mutex global_libass_mutex;
subtitle_renderer::subtitle_renderer() :
- _ass_initialized(false),
+ _initializer(*this),
+ _initialized(false),
_fontconfig_conffile(NULL),
_ass_library(NULL),
_ass_renderer(NULL),
_ass_track(NULL)
{
+ _initializer.start();
}
subtitle_renderer::~subtitle_renderer()
{
+ if (_initializer.is_running())
+ {
+ try
+ {
+ _initializer.cancel();
+ }
+ catch (...)
+ {
+ }
+ }
if (_ass_track)
{
ass_free_track(_ass_track);
@@ -98,6 +122,20 @@ subtitle_renderer::~subtitle_renderer()
}
}
+bool subtitle_renderer::is_initialized()
+{
+ if (_initializer.is_running())
+ {
+ return false;
+ }
+ else
+ {
+ // rethrow a possible exception if something went wrong
+ _initializer.finish();
+ return _initialized;
+ }
+}
+
bool subtitle_renderer::render_to_display_size(const subtitle_box &box) const
{
return (box.format != subtitle_box::image);
@@ -108,12 +146,12 @@ void subtitle_renderer::prerender(const subtitle_box &box, int64_t timestamp,
int width, int height, float pixel_aspect_ratio,
int &bb_x, int &bb_y, int &bb_w, int &bb_h)
{
+ assert(_initialized);
_fmt = box.format;
switch (_fmt)
{
case subtitle_box::text:
case subtitle_box::ass:
- init_ass();
prerender_ass(box, timestamp, params, width, height, pixel_aspect_ratio);
break;
case subtitle_box::image:
@@ -254,30 +292,37 @@ static void libass_msg_callback(int level, const char *fmt, va_list args, void *
msg::msg(l, std::string("LibASS: ") + s);
}
-void subtitle_renderer::init_ass()
+void subtitle_renderer::init()
{
- if (!_ass_initialized)
+ if (!_initialized)
{
- global_libass_mutex.lock();
- _ass_library = ass_library_init();
- if (!_ass_library)
+ try
{
+ global_libass_mutex.lock();
+ _ass_library = ass_library_init();
+ if (!_ass_library)
+ {
+ throw exc(_("Cannot initialize LibASS."));
+ }
+ ass_set_message_cb(_ass_library, libass_msg_callback, NULL);
+ ass_set_extract_fonts(_ass_library, 1);
+ _ass_renderer = ass_renderer_init(_ass_library);
+ if (!_ass_renderer)
+ {
+ throw exc(_("Cannot initialize LibASS renderer."));
+ }
+ ass_set_hinting(_ass_renderer, ASS_HINTING_NATIVE);
+ _fontconfig_conffile = get_fontconfig_conffile();
+ ass_set_fonts(_ass_renderer, NULL, "sans-serif", 1, _fontconfig_conffile, 1);
+ _initialized = true;
global_libass_mutex.unlock();
- throw exc(_("Cannot initialize LibASS."));
}
- ass_set_message_cb(_ass_library, libass_msg_callback, NULL);
- ass_set_extract_fonts(_ass_library, 1);
- _ass_renderer = ass_renderer_init(_ass_library);
- if (!_ass_renderer)
+ catch (...)
{
+ // Either we threw this exception ourselves, or the thread was cancelled.
global_libass_mutex.unlock();
- throw exc(_("Cannot initialize LibASS renderer."));
+ throw;
}
- ass_set_hinting(_ass_renderer, ASS_HINTING_NATIVE);
- _fontconfig_conffile = get_fontconfig_conffile();
- ass_set_fonts(_ass_renderer, NULL, "sans-serif", 1, _fontconfig_conffile, 1);
- _ass_initialized = true;
- global_libass_mutex.unlock();
}
}
diff --git a/src/subtitle_renderer.h b/src/subtitle_renderer.h
index f8a2dce..6b7b032 100644
--- a/src/subtitle_renderer.h
+++ b/src/subtitle_renderer.h
@@ -31,14 +31,33 @@ extern "C"
#include <ass/ass.h>
}
+#include "thread.h"
+
#include "media_data.h"
+class subtitle_renderer;
+
+class subtitle_renderer_initializer : public thread
+{
+private:
+ subtitle_renderer &_subtitle_renderer;
+
+public:
+ subtitle_renderer_initializer(subtitle_renderer &renderer);
+ void run();
+};
+
class subtitle_renderer
{
private:
+ // Initialization
+ subtitle_renderer_initializer _initializer;
+ bool _initialized;
+ void init();
+ friend class subtitle_renderer_initializer;
+
// Static ASS data
- bool _ass_initialized;
const char *_fontconfig_conffile;
ASS_Library *_ass_library;
ASS_Renderer *_ass_renderer;
@@ -52,7 +71,6 @@ private:
// ASS helper functions
const char *get_fontconfig_conffile();
- void init_ass();
void blend_ass_image(const ASS_Image *img, uint32_t *buf);
void set_ass_parameters(const parameters &params);
@@ -70,6 +88,15 @@ public:
subtitle_renderer();
~subtitle_renderer();
+ // Check if the subtitle renderer is initialized. Since initialization may
+ // take a long time on systems where fontconfig needs to create its cache first,
+ // it is done in a separate thread in the background.
+ // You must make sure that the renderer is initialized before calling any of
+ // the functions below!
+ // In the case of initialization failure, this function will throw the appropriate
+ // exception.
+ bool is_initialized();
+
/*
* To render a subtitle, the following steps are necessary:
* 1. Call render_to_display_size() to determine if the subtitle
diff --git a/src/video_output.cpp b/src/video_output.cpp
index d6b728c..3bf98b3 100644
--- a/src/video_output.cpp
+++ b/src/video_output.cpp
@@ -404,6 +404,7 @@ void video_output::update_subtitle_tex(int index, const video_frame &frame, cons
int height = 0;
if (subtitle.is_valid())
{
+ assert(_subtitle_renderer.is_initialized());
if (_subtitle_renderer.render_to_display_size(subtitle))
{
width = video_display_width();
diff --git a/src/video_output.h b/src/video_output.h
index a02f02d..2e8d6c8 100644
--- a/src/video_output.h
+++ b/src/video_output.h
@@ -36,7 +36,6 @@
class video_output : public controller
{
private:
- subtitle_renderer _subtitle_renderer;
bool _initialized;
bool _srgb_textures_are_broken; // XXX: Hack: work around broken SRGB texture implementations
@@ -95,6 +94,8 @@ private:
void update_subtitle_tex(int index, const video_frame &frame, const subtitle_box &subtitle, const parameters &params);
protected:
+ subtitle_renderer _subtitle_renderer;
+
// Get total size of the video display area. For single window output, this
// is the same as the current viewport. The Equalizer video output can override
// this function.
@@ -129,6 +130,10 @@ public:
/* Initialize the video output, or throw an exception */
virtual void init();
+ /* Wait for subtitle renderer initialization to finish. Has to be called
+ * if the video has subtitles. Returns the number of microseconds that the
+ * waiting took. */
+ virtual int64_t wait_for_subtitle_renderer() = 0;
/* Deinitialize the video output */
virtual void deinit();
diff --git a/src/video_output_qt.cpp b/src/video_output_qt.cpp
index e293959..9721c19 100644
--- a/src/video_output_qt.cpp
+++ b/src/video_output_qt.cpp
@@ -30,6 +30,7 @@ static GLEWContext* glewGetContext() { return &_glewContext; }
#include <cmath>
#include <cstdlib>
+#include <unistd.h>
#include <QApplication>
#include <QDesktopWidget>
@@ -39,6 +40,8 @@ static GLEWContext* glewGetContext() { return &_glewContext; }
#include <QMessageBox>
#include <QPalette>
#include <QProcess>
+#include <QDialog>
+#include <QLabel>
#ifdef Q_WS_X11
# include <QX11Info>
# include <X11/Xlib.h>
@@ -51,6 +54,7 @@ static GLEWContext* glewGetContext() { return &_glewContext; }
#include "msg.h"
#include "str.h"
#include "dbg.h"
+#include "timer.h"
#include "qt_app.h"
#include "video_output_qt.h"
@@ -329,6 +333,58 @@ void video_output_qt::init()
}
}
+int64_t video_output_qt::wait_for_subtitle_renderer()
+{
+ if (_subtitle_renderer.is_initialized())
+ {
+ return 0;
+ }
+ int64_t wait_start = timer::get_microseconds(timer::monotonic);
+ exc init_exception;
+ QDialog *mbox = NULL;
+ // Show a dialog only in GUI mode
+ if (_container_is_external && !_fullscreen)
+ {
+ mbox = new QDialog(_container_widget);
+ mbox->setModal(true);
+ mbox->setWindowTitle(_("Please wait"));
+ QGridLayout *mbox_layout = new QGridLayout;
+ QLabel *mbox_label = new QLabel(_("Waiting for subtitle renderer initialization..."));
+ mbox_layout->addWidget(mbox_label, 0, 0);
+ mbox->setLayout(mbox_layout);
+ mbox->show();
+ }
+ else
+ {
+ msg::wrn(_("Waiting for subtitle renderer initialization..."));
+ }
+ QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
+ try
+ {
+ while (!_subtitle_renderer.is_initialized())
+ {
+ process_events();
+ usleep(10000);
+ }
+ }
+ catch (std::exception &e)
+ {
+ init_exception = e;
+ }
+ QApplication::restoreOverrideCursor();
+ if (mbox)
+ {
+ mbox->hide();
+ delete mbox;
+ }
+ if (!init_exception.empty())
+ {
+ throw init_exception;
+ }
+ int64_t wait_stop = timer::get_microseconds(timer::monotonic);
+ return (wait_stop - wait_start);
+}
+
void video_output_qt::deinit()
{
if (_widget)
diff --git a/src/video_output_qt.h b/src/video_output_qt.h
index 3e4c9f6..8e07aa6 100644
--- a/src/video_output_qt.h
+++ b/src/video_output_qt.h
@@ -123,6 +123,7 @@ public:
void grab_focus();
virtual void init();
+ virtual int64_t wait_for_subtitle_renderer();
virtual void deinit();
virtual bool supports_stereo() const;