#include "wx/system_information_dialog.h"
#include "wx/player_stress_tester.h"
#include "wx/verify_dcp_progress_dialog.h"
+#include "wx/nag_dialog.h"
#include "lib/cross.h"
#include "lib/config.h"
#include "lib/util.h"
#include "lib/ffmpeg_content.h"
#include "lib/dcpomatic_log.h"
#include "lib/file_log.h"
+#include <dcp/cpl.h>
#include <dcp/dcp.h>
#include <dcp/raw_convert.h>
#include <dcp/exceptions.h>
{
public:
DOMFrame ()
- : wxFrame (0, -1, _("DCP-o-matic Player"))
- , _dual_screen (0)
- , _update_news_requested (false)
- , _info (0)
+ : wxFrame (nullptr, -1, _("DCP-o-matic Player"))
, _mode (Config::instance()->player_mode())
- , _config_dialog (0)
- , _file_menu (0)
- , _history_items (0)
- , _history_position (0)
- , _history_separator (0)
- , _system_information_dialog (0)
- , _view_full_screen (0)
- , _view_dual_screen (0)
- , _main_sizer (new wxBoxSizer (wxVERTICAL))
+ , _main_sizer (new wxBoxSizer(wxVERTICAL))
{
dcpomatic_log = make_shared<NullLog>();
*/
_overall_panel = new wxPanel (this, wxID_ANY);
- _viewer.reset (new FilmViewer (_overall_panel));
+ _viewer = make_shared<FilmViewer>(_overall_panel);
if (Config::instance()->player_mode() == Config::PLAYER_MODE_DUAL) {
auto pc = new PlaylistControls (_overall_panel, _viewer);
_controls = pc;
_controls = new StandardControls (_overall_panel, _viewer, false);
}
_viewer->set_dcp_decode_reduction (Config::instance()->decode_reduction ());
+ _viewer->set_optimise_for_j2k (true);
_viewer->PlaybackPermitted.connect (bind(&DOMFrame::playback_permitted, this));
- _viewer->Started.connect (bind(&DOMFrame::playback_started, this, _1));
- _viewer->Stopped.connect (bind(&DOMFrame::playback_stopped, this, _1));
+ _viewer->TooManyDropped.connect (bind(&DOMFrame::too_many_frames_dropped, this));
_info = new PlayerInformation (_overall_panel, _viewer);
setup_main_sizer (Config::instance()->player_mode());
#ifdef __WXOSX__
reset_film ();
- UpdateChecker::instance()->StateChanged.connect (boost::bind (&DOMFrame::update_checker_state_changed, this));
+ UpdateChecker::instance()->StateChanged.connect (boost::bind(&DOMFrame::update_checker_state_changed, this));
setup_screen ();
_stress.LoadDCP.connect (boost::bind(&DOMFrame::load_dcp, this, _1));
}
+ ~DOMFrame ()
+ {
+ /* It's important that this is stopped before our frame starts destroying its children,
+ * otherwise UI elements that it depends on will disappear from under it.
+ */
+ _viewer.reset ();
+ }
+
void setup_main_sizer (Config::PlayerMode mode)
{
_main_sizer->Detach (_viewer->panel());
return ok;
}
- void playback_started (DCPTime time)
- {
- /* XXX: this only logs the first piece of content; probably should be each piece? */
- if (_film->content().empty()) {
- return;
- }
-
- auto dcp = dynamic_pointer_cast<DCPContent>(_film->content().front());
- if (dcp) {
- DCPExaminer ex (dcp, true);
- shared_ptr<dcp::CPL> playing_cpl;
- for (auto i: ex.cpls()) {
- if (!dcp->cpl() || i->id() == *dcp->cpl()) {
- playing_cpl = i;
- }
- }
- DCPOMATIC_ASSERT (playing_cpl);
-
- _controls->log (
- wxString::Format(
- "playback-started %s %s %s",
- time.timecode(_film->video_frame_rate()).c_str(),
- dcp->directories().front().string().c_str(),
- playing_cpl->annotation_text().get_value_or("").c_str()
- )
- );
- }
- auto ffmpeg = dynamic_pointer_cast<FFmpegContent>(_film->content().front());
- if (ffmpeg) {
- _controls->log (
- wxString::Format(
- "playback-started %s %s",
- time.timecode(_film->video_frame_rate()).c_str(),
- ffmpeg->path(0).string().c_str()
- )
- );
+ void too_many_frames_dropped ()
+ {
+ if (!Config::instance()->nagged(Config::NAG_TOO_MANY_DROPPED_FRAMES)) {
+ _viewer->stop ();
}
- }
- void playback_stopped (DCPTime time)
- {
- _controls->log (wxString::Format("playback-stopped %s", time.timecode(_film->video_frame_rate()).c_str()));
+ NagDialog::maybe_nag (
+ this,
+ Config::NAG_TOO_MANY_DROPPED_FRAMES,
+ _(wxS("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"
+ ))
+ );
}
void set_decode_reduction (optional<int> reduction)
reset_film ();
try {
_stress.set_suspended (true);
+ // here
auto dcp = make_shared<DCPContent>(dir);
auto job = make_shared<ExamineContentJob>(_film, dcp);
_examine_job_connection = job->Finished.connect(bind(&DOMFrame::add_dcp_to_film, this, weak_ptr<Job>(job), weak_ptr<Content>(dcp)));
return;
}
Config::instance()->add_to_player_history (dir);
+ } catch (ProjectFolderError &) {
+ error_dialog (
+ this,
+ wxString::Format(_("Could not load a DCP from %s"), std_to_wx(dir.string())),
+ _(
+ "This looks like a DCP-o-matic project folder, which cannot be loaded into the player. "
+ "Choose the DCP directory inside the DCP-o-matic project folder if that's what you want to play."
+ )
+ );
} catch (dcp::ReadError& e) {
error_dialog (this, wxString::Format(_("Could not load a DCP from %s"), std_to_wx(dir.string())), std_to_wx(e.what()));
} catch (DCPError& e) {
_viewer->seek (_film->length() - _viewer->one_video_frame(), true);
}
- wxFrame* _dual_screen;
- bool _update_news_requested;
- PlayerInformation* _info;
+ wxFrame* _dual_screen = nullptr;
+ bool _update_news_requested = false;
+ PlayerInformation* _info = nullptr;
Config::PlayerMode _mode;
- wxPreferencesEditor* _config_dialog;
- wxPanel* _overall_panel;
- wxMenu* _file_menu;
- wxMenuItem* _view_cpl;
- wxMenu* _cpl_menu;
- int _history_items;
- int _history_position;
- wxMenuItem* _history_separator;
+ wxPreferencesEditor* _config_dialog = nullptr;
+ wxPanel* _overall_panel = nullptr;
+ wxMenu* _file_menu = nullptr;
+ wxMenuItem* _view_cpl = nullptr;
+ wxMenu* _cpl_menu = nullptr;
+ int _history_items = 0;
+ int _history_position = 0;
+ wxMenuItem* _history_separator = nullptr;
shared_ptr<FilmViewer> _viewer;
Controls* _controls;
- SystemInformationDialog* _system_information_dialog;
+ SystemInformationDialog* _system_information_dialog = nullptr;
std::shared_ptr<Film> _film;
boost::signals2::scoped_connection _config_changed_connection;
boost::signals2::scoped_connection _examine_job_connection;
- wxMenuItem* _file_add_ov;
- wxMenuItem* _file_add_kdm;
- wxMenuItem* _tools_verify;
- wxMenuItem* _view_full_screen;
- wxMenuItem* _view_dual_screen;
- wxSizer* _main_sizer;
+ wxMenuItem* _file_add_ov = nullptr;
+ wxMenuItem* _file_add_kdm = nullptr;
+ wxMenuItem* _tools_verify = nullptr;
+ wxMenuItem* _view_full_screen = nullptr;
+ wxMenuItem* _view_dual_screen = nullptr;
+ wxSizer* _main_sizer = nullptr;
PlayerStressTester _stress;
};
, _frame (frame)
{}
- void handle (shared_ptr<Socket> socket)
+ void handle (shared_ptr<Socket> socket) override
{
try {
int const length = socket->read_uint32 ();
public:
App ()
: wxApp ()
- , _frame (0)
{
#ifdef DCPOMATIC_LINUX
XInitThreads ();
private:
- bool OnInit ()
+ bool OnInit () override
{
- wxSplashScreen* splash = 0;
+ wxSplashScreen* splash = nullptr;
try {
wxInitAllImageHandlers ();
_frame->Maximize ();
if (splash) {
splash->Destroy ();
- splash = 0;
+ splash = nullptr;
}
_frame->Show ();
- PlayServer* server = new PlayServer (_frame);
- new thread (boost::bind (&PlayServer::run, server));
+ try {
+ auto server = new PlayServer (_frame);
+ new thread (boost::bind (&PlayServer::run, server));
+ } catch (std::exception& e) {
+ /* This is not the end of the world; probably a failure to bind the server socket
+ * because there's already another player running.
+ */
+ LOG_DEBUG_PLAYER ("Failed to start play server (%1)", e.what());
+ }
if (!_dcp_to_load.empty() && boost::filesystem::is_directory (_dcp_to_load)) {
try {
return true;
}
- void OnInitCmdLine (wxCmdLineParser& parser)
+ void OnInitCmdLine (wxCmdLineParser& parser) override
{
parser.SetDesc (command_line_description);
parser.SetSwitchChars (wxT ("-"));
}
- bool OnCmdLineParsed (wxCmdLineParser& parser)
+ bool OnCmdLineParsed (wxCmdLineParser& parser) override
{
if (parser.GetParamCount() > 0) {
_dcp_to_load = wx_to_std (parser.GetParam (0));
}
/* An unhandled exception has occurred inside the main event loop */
- bool OnExceptionInMainLoop ()
+ bool OnExceptionInMainLoop () override
{
report_exception ();
/* This will terminate the program */
return false;
}
- void OnUnhandledException ()
+ void OnUnhandledException () override
{
report_exception ();
}
message_dialog (_frame, std_to_wx (m));
}
- DOMFrame* _frame;
+ DOMFrame* _frame = nullptr;
string _dcp_to_load;
boost::optional<string> _stress;
};