/*
- Copyright (C) 2012-2019 Carl Hetherington <cth@carlh.net>
+ Copyright (C) 2012-2021 Carl Hetherington <cth@carlh.net>
This file is part of DCP-o-matic.
*/
+
/** @file src/film_viewer.cc
* @brief A wx widget to view a preview of a Film.
*/
-#include "film_viewer.h"
-#include "playhead_to_timecode_dialog.h"
-#include "playhead_to_frame_dialog.h"
-#include "wx_util.h"
+
#include "closed_captions_dialog.h"
+#include "film_viewer.h"
#include "gl_video_view.h"
+#include "nag_dialog.h"
+#include "playhead_to_frame_dialog.h"
+#include "playhead_to_timecode_dialog.h"
#include "simple_video_view.h"
+#include "wx_util.h"
#include "lib/film.h"
#include "lib/ratio.h"
#include "lib/util.h"
using std::bad_alloc;
using std::make_pair;
using std::exception;
-using boost::shared_ptr;
-using boost::dynamic_pointer_cast;
-using boost::weak_ptr;
+using std::shared_ptr;
+using std::dynamic_pointer_cast;
+using std::vector;
+using std::weak_ptr;
using boost::optional;
#if BOOST_VERSION >= 106100
using namespace boost::placeholders;
, _closed_captions_dialog (new ClosedCaptionsDialog(p, this))
, _outline_content (false)
, _pad_black (false)
-#ifdef DCPOMATIC_VARIANT_SWAROOP
- , _background_image (false)
-#endif
, _idle_get (false)
{
switch (Config::instance()->video_view_type()) {
}
_video_view->Sized.connect (boost::bind(&FilmViewer::video_view_sized, this));
+ _video_view->TooManyDropped.connect (boost::bind(&FilmViewer::too_many_frames_dropped, this));
set_film (shared_ptr<Film> ());
return;
}
- if (_video_view->display_next_frame(true)) {
- _idle_get = false;
- } else {
+ if (_video_view->display_next_frame(true) == VideoView::AGAIN) {
/* get() could not complete quickly so we'll try again later */
signal_manager->when_idle (boost::bind(&FilmViewer::idle_handler, this));
+ } else {
+ _idle_get = false;
}
}
_player->set_play_referenced ();
_film->Change.connect (boost::bind (&FilmViewer::film_change, this, _1, _2));
- _film->ContentChange.connect (boost::bind(&FilmViewer::content_change, this, _1, _3));
_film->LengthChange.connect (boost::bind(&FilmViewer::film_length_change, this));
_player->Change.connect (boost::bind (&FilmViewer::player_change, this, _1, _2, _3));
- film_change (CHANGE_TYPE_DONE, Film::VIDEO_FRAME_RATE);
- film_change (CHANGE_TYPE_DONE, Film::THREE_D);
+ film_change (ChangeType::DONE, Film::Property::VIDEO_FRAME_RATE);
+ film_change (ChangeType::DONE, Film::Property::THREE_D);
film_length_change ();
/* Keep about 1 second's worth of history samples */
_butler.reset(
new Butler(
+ _film,
_player,
Config::instance()->audio_mapping(_audio_channels),
_audio_channels,
bind(&PlayerVideo::force, _1, AV_PIX_FMT_RGB24),
+ VideoRange::FULL,
false,
true
)
}
_playing = true;
- _video_view->start ();
+ /* Calling start() below may directly result in Stopped being emitted, and if that
+ * happens we want it to come after the Started signal, so do that first.
+ */
Started (position());
+ _video_view->start ();
}
bool
void
FilmViewer::player_change (ChangeType type, int property, bool frequent)
{
- if (type != CHANGE_TYPE_DONE || frequent) {
+ if (type != ChangeType::DONE || frequent) {
return;
}
return;
}
+ player_change ({property});
+}
+
+void
+FilmViewer::player_change (vector<int> properties)
+{
calculate_sizes ();
- bool refreshed = false;
- if (
- property == VideoContentProperty::CROP ||
- property == VideoContentProperty::SCALE ||
- property == VideoContentProperty::FADE_IN ||
- property == VideoContentProperty::FADE_OUT ||
- property == VideoContentProperty::COLOUR_CONVERSION ||
- property == PlayerProperty::VIDEO_CONTAINER_SIZE ||
- property == PlayerProperty::FILM_CONTAINER
- ) {
- refreshed = quick_refresh ();
- }
-
- if (!refreshed) {
+
+ bool try_quick_refresh = false;
+ bool update_ccap_tracks = false;
+
+ for (auto i: properties) {
+ if (
+ i == VideoContentProperty::CROP ||
+ i == VideoContentProperty::SCALE ||
+ i == VideoContentProperty::FADE_IN ||
+ i == VideoContentProperty::FADE_OUT ||
+ i == VideoContentProperty::COLOUR_CONVERSION ||
+ i == PlayerProperty::VIDEO_CONTAINER_SIZE ||
+ i == PlayerProperty::FILM_CONTAINER
+ ) {
+ try_quick_refresh = true;
+ }
+
+ if (i == TextContentProperty::USE || i == TextContentProperty::TYPE || i == TextContentProperty::DCP_TRACK) {
+ update_ccap_tracks = true;
+ }
+ }
+
+ if (!try_quick_refresh || !quick_refresh()) {
slow_refresh ();
}
+
+ if (update_ccap_tracks) {
+ _closed_captions_dialog->update_tracks (_film);
+ }
}
void
FilmViewer::film_change (ChangeType type, Film::Property p)
{
- if (type != CHANGE_TYPE_DONE) {
+ if (type != ChangeType::DONE) {
return;
}
- if (p == Film::AUDIO_CHANNELS) {
+ if (p == Film::Property::AUDIO_CHANNELS) {
recreate_butler ();
- } else if (p == Film::VIDEO_FRAME_RATE) {
+ } else if (p == Film::Property::VIDEO_FRAME_RATE) {
_video_view->set_video_frame_rate (_film->video_frame_rate());
- } else if (p == Film::THREE_D) {
+ } else if (p == Film::Property::THREE_D) {
_video_view->set_three_d (_film->three_d());
- } else if (p == Film::CONTENT) {
+ } else if (p == Film::Property::CONTENT) {
_closed_captions_dialog->update_tracks (_film);
}
}
if (!_video_view || !_film || !_player) {
return true;
}
- return _video_view->refresh_metadata (_film, _player->video_container_size(), _film->frame_size());
+ return _video_view->reset_metadata (_film, _player->video_container_size());
}
void
_coalesce_player_changes = c;
if (!c) {
- BOOST_FOREACH (int i, _pending_player_changes) {
- player_change (CHANGE_TYPE_DONE, i, false);
- }
+ player_change (_pending_player_changes);
_pending_player_changes.clear ();
}
}
/* We're going to start playing again straight away
so wait for the seek to finish.
*/
- while (!_video_view->display_next_frame(false)) {}
+ while (_video_view->display_next_frame(false) == VideoView::AGAIN) {}
}
resume ();
void
FilmViewer::config_changed (Config::Property p)
{
-#ifdef DCPOMATIC_VARIANT_SWAROOP
- if (p == Config::PLAYER_BACKGROUND_IMAGE) {
- _video_view->update ();
- return;
- }
-#endif
-
if (p == Config::AUDIO_MAPPING) {
recreate_butler ();
return;
if (_audio.getDeviceInfo(st).name == Config::instance()->sound_output().get()) {
break;
}
-#ifdef DCPOMATIC_USE_RTERROR
- } catch (RtError&) {
-#else
} catch (RtAudioError&) {
-#endif
/* Something went wrong with that device so we don't want to use it anyway */
}
++st;
sp.nChannels = _audio_channels;
sp.firstChannel = 0;
_audio.openStream (&sp, 0, RTAUDIO_FLOAT32, 48000, &_audio_block_size, &rtaudio_callback, this);
-#ifdef DCPOMATIC_USE_RTERROR
- } catch (RtError& e) {
-#else
} catch (RtAudioError& e) {
-#endif
_audio_channels = 0;
error_dialog (
_video_view->get(),
}
Frame total = 0;
- BOOST_FOREACH (Frame i, _latency_history) {
+ for (auto i: _latency_history) {
total += i;
}
void
-FilmViewer::content_change (ChangeType type, int property)
+FilmViewer::image_changed (shared_ptr<PlayerVideo> pv)
{
- if (type != CHANGE_TYPE_DONE) {
- return;
- }
+ emit (boost::bind(boost::ref(ImageChanged), pv));
+}
- if (property == TextContentProperty::USE || property == TextContentProperty::TYPE || property == TextContentProperty::DCP_TRACK) {
- _closed_captions_dialog->update_tracks (_film);
+
+void
+FilmViewer::too_many_frames_dropped ()
+{
+ if (!Config::instance()->nagged(Config::NAG_TOO_MANY_DROPPED_FRAMES)) {
+ stop ();
}
-}
+ NagDialog::maybe_nag (
+ panel(),
+ Config::NAG_TOO_MANY_DROPPED_FRAMES,
+ _("The player is dropping a lot of frames, so playback may not be accurate.\n\n"
+ "<b>This does not necessarily mean that the DCP you are playing is defective!</b>\n\n"
+ "You may be able to improve player performance by:\n"
+ "• choosing 'decode at half resolution' or 'decode at quarter resolution' from the View menu\n"
+ "• using a more powerful computer.\n"
+ )
+ );
+}