, _audio_channels (0)
, _audio_block_size (1024)
, _playing (false)
+ , _suspended (0)
, _latency_history_count (0)
, _dropped (0)
, _closed_captions_dialog (new ClosedCaptionsDialog(p, this))
#endif
, _state_timer ("viewer")
, _gets (0)
+ , _idle_get (false)
{
switch (Config::instance()->video_view_type()) {
case Config::VIDEO_VIEW_OPENGL:
}
_video_view->Sized.connect (boost::bind(&FilmViewer::video_view_sized, this));
- _timer.Bind (wxEVT_TIMER, boost::bind(&FilmViewer::timer, this));
set_film (shared_ptr<Film> ());
stop ();
}
+/** Ask for ::get() to be called next time we are idle */
+void
+FilmViewer::request_idle_get ()
+{
+ if (_idle_get) {
+ return;
+ }
+
+ _idle_get = true;
+ DCPOMATIC_ASSERT (signal_manager);
+ signal_manager->when_idle (boost::bind(&FilmViewer::idle_handler, this));
+}
+
+void
+FilmViewer::idle_handler ()
+{
+ if (!_idle_get) {
+ return;
+ }
+
+ if (get(true)) {
+ _idle_get = false;
+ } else {
+ /* get() could not complete quickly so we'll try again later */
+ signal_manager->when_idle (boost::bind(&FilmViewer::idle_handler, this));
+ }
+}
+
void
FilmViewer::set_film (shared_ptr<Film> film)
{
void
FilmViewer::recreate_butler ()
{
- bool const was_running = stop ();
+ suspend ();
_butler.reset ();
if (!_film) {
+ resume ();
return;
}
- AudioMapping map = AudioMapping (_film->audio_channels(), _audio_channels);
-
- if (_audio_channels != 2 || _film->audio_channels() < 3) {
- for (int i = 0; i < min (_film->audio_channels(), _audio_channels); ++i) {
- map.set (i, i, 1);
- }
- } else {
- /* Special case: stereo output, at least 3 channel input.
- Map so that Lt = L(-3dB) + Ls(-3dB) + C(-6dB) + Lfe(-10dB)
- Rt = R(-3dB) + Rs(-3dB) + C(-6dB) + Lfe(-10dB)
- */
- map.set (dcp::LEFT, 0, 1 / sqrt(2)); // L -> Lt
- map.set (dcp::RIGHT, 1, 1 / sqrt(2)); // R -> Rt
- map.set (dcp::CENTRE, 0, 1 / 2.0); // C -> Lt
- map.set (dcp::CENTRE, 1, 1 / 2.0); // C -> Rt
- map.set (dcp::LFE, 0, 1 / sqrt(10)); // Lfe -> Lt
- map.set (dcp::LFE, 1, 1 / sqrt(10)); // Lfe -> Rt
- map.set (dcp::LS, 0, 1 / sqrt(2)); // Ls -> Lt
- map.set (dcp::RS, 1, 1 / sqrt(2)); // Rs -> Rt
- }
+ _butler.reset(
+ new Butler(
+ _player,
+ Config::instance()->audio_mapping(_audio_channels),
+ _audio_channels,
+ bind(&PlayerVideo::force, _1, AV_PIX_FMT_RGB24),
+ false,
+ true
+ )
+ );
- _butler.reset (new Butler(_player, map, _audio_channels, bind(&PlayerVideo::force, _1, AV_PIX_FMT_RGB24), false, true));
if (!Config::instance()->sound() && !_audio.isStreamOpen()) {
_butler->disable_audio ();
}
_closed_captions_dialog->set_film_and_butler (_film, _butler);
- if (was_running) {
- start ();
- }
+ resume ();
}
void
_state_timer.unset ();
}
-void
-FilmViewer::get ()
+/** Try to get a frame from the butler and display it.
+ * @param lazy true to return false quickly if no video is available quickly (i.e. we are waiting for the butler).
+ * false to ask the butler to block until it has video (unless it is suspended).
+ * @return true on success, false if we did nothing because it would have taken too long.
+ */
+bool
+FilmViewer::get (bool lazy)
{
DCPOMATIC_ASSERT (_butler);
++_gets;
do {
Butler::Error e;
- _player_video = _butler->get_video (&e);
+ _player_video = _butler->get_video (!lazy, &e);
if (!_player_video.first && e == Butler::AGAIN) {
- signal_manager->when_idle (boost::bind(&FilmViewer::get, this));
- return;
+ if (lazy) {
+ /* No video available; return saying we failed */
+ return false;
+ } else {
+ /* Player was suspended; come back later */
+ signal_manager->when_idle (boost::bind(&FilmViewer::get, this, false));
+ return false;
+ }
}
} while (
_player_video.first &&
_player_video.first->eyes() != EYES_BOTH
);
- _butler->rethrow ();
+ try {
+ _butler->rethrow ();
+ } catch (DecodeError& e) {
+ error_dialog (_video_view->get(), e.what());
+ }
display_player_video ();
+ PositionChanged ();
+
+ return true;
}
void
return;
}
- if (_playing && (time() - _player_video.second) > one_video_frame()) {
+ if (_playing && !_suspended && (time() - _player_video.second) > one_video_frame()) {
/* Too late; just drop this frame before we try to get its image (which will be the time-consuming
part if this frame is J2K).
*/
_closed_captions_dialog->update (time());
}
-void
-FilmViewer::timer ()
-{
- if (!_film || !_playing) {
- return;
- }
-
- get ();
- PositionChanged ();
- DCPTime const next = _video_position + one_video_frame();
-
- if (next >= _film->length()) {
- stop ();
- Finished ();
- return;
- }
-
- LOG_DEBUG_PLAYER("%1 -> %2; delay %3", next.seconds(), time().seconds(), max((next.seconds() - time().seconds()) * 1000, 1.0));
- _timer.Start (max ((next.seconds() - time().seconds()) * 1000, 1.0), wxTIMER_ONE_SHOT);
-
- if (_butler) {
- _butler->rethrow ();
- }
-}
-
void
FilmViewer::set_outline_content (bool o)
{
_player->set_video_container_size (_out_size);
}
+void
+FilmViewer::suspend ()
+{
+ ++_suspended;
+ if (_audio.isStreamRunning()) {
+ _audio.abortStream();
+ }
+}
+
+void
+FilmViewer::resume ()
+{
+ --_suspended;
+ if (_playing && !_suspended) {
+ if (_audio.isStreamOpen()) {
+ _audio.setStreamTime (_video_position.seconds());
+ _audio.startStream ();
+ }
+ timer ();
+ }
+}
+
void
FilmViewer::start ()
{
_playing = true;
_dropped = 0;
- timer ();
+ _video_view->timer ();
Started (position());
}
t = _film->length ();
}
- bool const was_running = stop ();
+ suspend ();
_closed_captions_dialog->clear ();
_butler->seek (t, accurate);
- get ();
- if (was_running) {
- start ();
+ if (!_playing) {
+ request_idle_get ();
+ } else {
+ /* Make sure we get a frame so that _video_position is set up before we resume */
+ while (!get(true)) {}
}
- PositionChanged ();
+ resume ();
}
void
}
#endif
+ if (p == Config::AUDIO_MAPPING) {
+ recreate_butler ();
+ return;
+ }
+
if (p != Config::SOUND && p != Config::SOUND_OUTPUT) {
return;
}
DCPTime
FilmViewer::one_video_frame () const
{
- return DCPTime::from_frames (1, _film->video_frame_rate());
+ return DCPTime::from_frames (1, _film ? _film->video_frame_rate() : 24);
}
/** Open a dialog box showing our film's closed captions */