X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=src%2Ftools%2Fdcpomatic_player.cc;h=03ace74c98c632dcadb937949360253069cd59d8;hb=23583dfb56181c76126e652042e9d1940367aa27;hp=c59d925236c4538bbf9c5f1701e43e8d3bddedec;hpb=d661797fed639f9e6a6cf2160cf0d2e4de04694c;p=dcpomatic.git diff --git a/src/tools/dcpomatic_player.cc b/src/tools/dcpomatic_player.cc index c59d92523..03ace74c9 100644 --- a/src/tools/dcpomatic_player.cc +++ b/src/tools/dcpomatic_player.cc @@ -26,17 +26,24 @@ #include "lib/dcp_content.h" #include "lib/job_manager.h" #include "lib/job.h" +#include "lib/film.h" #include "lib/video_content.h" -#include "lib/subtitle_content.h" +#include "lib/text_content.h" #include "lib/ratio.h" +#include "lib/verify_dcp_job.h" +#include "lib/dcp_examiner.h" +#include "lib/examine_content_job.h" +#include "lib/server.h" +#include "lib/dcpomatic_socket.h" #include "wx/wx_signal_manager.h" #include "wx/wx_util.h" #include "wx/about_dialog.h" #include "wx/report_problem_dialog.h" -#include "wx/film_viewer.h" +#include "wx/control_film_viewer.h" #include "wx/player_information.h" #include "wx/update_dialog.h" #include "wx/player_config_dialog.h" +#include "wx/verify_dcp_dialog.h" #include #include #include @@ -53,12 +60,19 @@ #undef check #endif +#define MAX_CPLS 32 + using std::string; using std::cout; +using std::list; using std::exception; using std::vector; using boost::shared_ptr; +using boost::scoped_array; using boost::optional; +using boost::dynamic_pointer_cast; +using boost::thread; +using boost::bind; enum { ID_file_open = 1, @@ -67,12 +81,20 @@ enum { ID_file_history, /* Allow spare IDs after _history for the recent files list */ ID_file_close = 100, + ID_view_cpl, + /* Allow spare IDs for CPLs */ + ID_view_closed_captions = 200, ID_view_scale_appropriate, ID_view_scale_full, ID_view_scale_half, ID_view_scale_quarter, ID_help_report_a_problem, + ID_tools_verify, ID_tools_check_for_updates, + /* IDs for shortcuts (with no associated menu item) */ + ID_start_stop, + ID_back_frame, + ID_forward_frame }; class DOMFrame : public wxFrame @@ -114,12 +136,14 @@ public: Bind (wxEVT_MENU, boost::bind (&DOMFrame::file_close, this), ID_file_close); Bind (wxEVT_MENU, boost::bind (&DOMFrame::file_exit, this), wxID_EXIT); Bind (wxEVT_MENU, boost::bind (&DOMFrame::edit_preferences, this), wxID_PREFERENCES); - Bind (wxEVT_MENU, boost::bind (&DOMFrame::set_decode_reduction, this, optional()), ID_view_scale_appropriate); + Bind (wxEVT_MENU, boost::bind (&DOMFrame::view_closed_captions, this), ID_view_closed_captions); + Bind (wxEVT_MENU, boost::bind (&DOMFrame::view_cpl, this, _1), ID_view_cpl, ID_view_cpl + MAX_CPLS); Bind (wxEVT_MENU, boost::bind (&DOMFrame::set_decode_reduction, this, optional(0)), ID_view_scale_full); Bind (wxEVT_MENU, boost::bind (&DOMFrame::set_decode_reduction, this, optional(1)), ID_view_scale_half); Bind (wxEVT_MENU, boost::bind (&DOMFrame::set_decode_reduction, this, optional(2)), ID_view_scale_quarter); Bind (wxEVT_MENU, boost::bind (&DOMFrame::help_about, this), wxID_ABOUT); Bind (wxEVT_MENU, boost::bind (&DOMFrame::help_report_a_problem, this), ID_help_report_a_problem); + Bind (wxEVT_MENU, boost::bind (&DOMFrame::tools_verify, this), ID_tools_verify); Bind (wxEVT_MENU, boost::bind (&DOMFrame::tools_check_for_updates, this), ID_tools_check_for_updates); /* Use a panel as the only child of the Frame so that we avoid @@ -127,7 +151,7 @@ public: */ wxPanel* overall_panel = new wxPanel (this, wxID_ANY); - _viewer = new FilmViewer (overall_panel, false, false); + _viewer = new ControlFilmViewer (overall_panel, false, false); _viewer->set_dcp_decode_reduction (Config::instance()->decode_reduction ()); _info = new PlayerInformation (overall_panel, _viewer); wxSizer* main_sizer = new wxBoxSizer (wxVERTICAL); @@ -136,12 +160,25 @@ public: overall_panel->SetSizer (main_sizer); #ifdef __WXOSX__ - wxAcceleratorEntry* accel = new wxAcceleratorEntry[1]; - accel[0].Set(wxACCEL_CTRL, static_cast('W'), ID_file_close); - wxAcceleratorTable accel_table (1, accel); + int accelerators = 4; +#else + int accelerators = 3; +#endif + + wxAcceleratorEntry* accel = new wxAcceleratorEntry[accelerators]; + accel[0].Set(wxACCEL_NORMAL, WXK_SPACE, ID_start_stop); + accel[1].Set(wxACCEL_NORMAL, WXK_LEFT, ID_back_frame); + accel[2].Set(wxACCEL_NORMAL, WXK_RIGHT, ID_forward_frame); +#ifdef __WXOSX__ + accel[3].Set(wxACCEL_CTRL, static_cast('W'), ID_file_close); +#endif + wxAcceleratorTable accel_table (accelerators, accel); SetAcceleratorTable (accel_table); delete[] accel; -#endif + + Bind (wxEVT_MENU, boost::bind (&DOMFrame::start_stop_pressed, this), ID_start_stop); + Bind (wxEVT_MENU, boost::bind (&DOMFrame::back_frame, this), ID_back_frame); + Bind (wxEVT_MENU, boost::bind (&DOMFrame::forward_frame, this), ID_forward_frame); UpdateChecker::instance()->StateChanged.connect (boost::bind (&DOMFrame::update_checker_state_changed, this)); } @@ -165,33 +202,15 @@ public: } _film->examine_and_add_content (dcp, true); - - JobManager* jm = JobManager::instance (); - - wxProgressDialog* progress = new wxProgressDialog (_("DCP-o-matic Player"), _("Loading DCP")); - - while (jm->work_to_do() || signal_manager->ui_idle()) { - dcpomatic_sleep (1); - progress->Pulse (); - } - - progress->Destroy (); - - DCPOMATIC_ASSERT (!jm->get().empty()); - - shared_ptr last = jm->get().back(); - if (last->finished_in_error()) { - error_dialog (this, std_to_wx (last->error_summary()) + ".\n"); + bool const ok = progress (_("Loading DCP")); + if (!ok || !report_errors_from_last_job()) { return; } - if (dcp->subtitle) { - dcp->subtitle->set_use (true); - } + setup_from_dcp (dcp); - Ratio const * r = Ratio::nearest_from_ratio(dcp->video->size().ratio()); - if (r) { - _film->set_container(r); + if (dcp->three_d()) { + _film->set_three_d (true); } _viewer->set_film (_film); @@ -201,6 +220,22 @@ public: Config::instance()->add_to_player_history (dir); set_menu_sensitivity (); + + wxMenuItemList old = _cpl_menu->GetMenuItems(); + for (wxMenuItemList::iterator i = old.begin(); i != old.end(); ++i) { + _cpl_menu->Remove (*i); + } + + DCPExaminer ex (dcp); + int id = ID_view_cpl; + BOOST_FOREACH (shared_ptr i, ex.cpls()) { + wxMenuItem* j = _cpl_menu->AppendRadioItem( + id, + wxString::Format("%s (%s)", std_to_wx(i->annotation_text()).data(), std_to_wx(i->id()).data()) + ); + j->Check(!dcp->cpl() || i->id() == *dcp->cpl()); + ++id; + } } private: @@ -231,14 +266,21 @@ private: edit->Append (wxID_PREFERENCES, _("&Preferences...\tCtrl-P")); #endif + _cpl_menu = new wxMenu; + wxMenu* view = new wxMenu; optional c = Config::instance()->decode_reduction(); + _view_cpl = view->Append(ID_view_cpl, _("CPL"), _cpl_menu); + view->Append(ID_view_closed_captions, _("Closed captions...")); + view->AppendSeparator(); view->AppendRadioItem(ID_view_scale_appropriate, _("Set decode resolution to match display"))->Check(!static_cast(c)); view->AppendRadioItem(ID_view_scale_full, _("Decode at full resolution"))->Check(c && c.get() == 0); view->AppendRadioItem(ID_view_scale_half, _("Decode at half resolution"))->Check(c && c.get() == 1); view->AppendRadioItem(ID_view_scale_quarter, _("Decode at quarter resolution"))->Check(c && c.get() == 2); wxMenu* tools = new wxMenu; + _tools_verify = tools->Append (ID_tools_verify, _("Verify DCP")); + tools->AppendSeparator (); tools->Append (ID_tools_check_for_updates, _("Check for updates")); wxMenu* help = new wxMenu; @@ -310,7 +352,12 @@ private: shared_ptr dcp = boost::dynamic_pointer_cast(_film->content().front()); DCPOMATIC_ASSERT (dcp); dcp->add_ov (wx_to_std(c->GetPath())); - dcp->examine (shared_ptr()); + JobManager::instance()->add(shared_ptr(new ExamineContentJob (_film, dcp))); + bool const ok = progress (_("Loading DCP")); + if (!ok || !report_errors_from_last_job()) { + return; + } + setup_from_dcp (dcp); } c->Destroy (); @@ -369,6 +416,51 @@ private: _config_dialog->Show (this); } + void view_cpl (wxCommandEvent& ev) + { + shared_ptr dcp = boost::dynamic_pointer_cast(_film->content().front()); + DCPOMATIC_ASSERT (dcp); + DCPExaminer ex (dcp); + int id = ev.GetId() - ID_view_cpl; + DCPOMATIC_ASSERT (id >= 0); + DCPOMATIC_ASSERT (id < int(ex.cpls().size())); + list > cpls = ex.cpls(); + list >::iterator i = cpls.begin(); + while (id > 0) { + ++i; + --id; + } + + dcp->set_cpl ((*i)->id()); + dcp->examine (shared_ptr()); + } + + void view_closed_captions () + { + _viewer->show_closed_captions (); + } + + void tools_verify () + { + shared_ptr dcp = boost::dynamic_pointer_cast(_film->content().front()); + DCPOMATIC_ASSERT (dcp); + + JobManager* jm = JobManager::instance (); + jm->add (shared_ptr (new VerifyDCPJob (dcp->directories()))); + bool const ok = progress (_("Verifying DCP")); + if (!ok) { + return; + } + + DCPOMATIC_ASSERT (!jm->get().empty()); + shared_ptr last = dynamic_pointer_cast (jm->get().back()); + DCPOMATIC_ASSERT (last); + + VerifyDCPDialog* d = new VerifyDCPDialog (this, last); + d->ShowModal (); + d->Destroy (); + } + void tools_check_for_updates () { UpdateChecker::instance()->run (); @@ -467,22 +559,102 @@ private: void set_menu_sensitivity () { + _tools_verify->Enable (static_cast(_film)); _file_add_ov->Enable (static_cast(_film)); _file_add_kdm->Enable (static_cast(_film)); + _view_cpl->Enable (static_cast(_film)); + } + + void start_stop_pressed () + { + if (_viewer->playing()) { + _viewer->stop(); + } else { + _viewer->start(); + } + } + + void back_frame () + { + _viewer->back_frame (); + } + + void forward_frame () + { + _viewer->forward_frame (); + } + +private: + + /** @return false if the task was cancelled */ + bool progress (wxString task) + { + JobManager* jm = JobManager::instance (); + + wxProgressDialog* progress = new wxProgressDialog (_("DCP-o-matic Player"), task, 100, 0, wxPD_CAN_ABORT); + + bool ok = true; + + while (jm->work_to_do() || signal_manager->ui_idle()) { + dcpomatic_sleep (1); + if (!progress->Pulse()) { + /* user pressed cancel */ + BOOST_FOREACH (shared_ptr i, jm->get()) { + i->cancel(); + } + ok = false; + break; + } + } + + progress->Destroy (); + return ok; + } + + bool report_errors_from_last_job () + { + JobManager* jm = JobManager::instance (); + + DCPOMATIC_ASSERT (!jm->get().empty()); + + shared_ptr last = jm->get().back(); + if (last->finished_in_error()) { + error_dialog(this, std_to_wx(last->error_summary()) + ".\n", std_to_wx(last->error_details())); + return false; + } + + return true; + } + + void setup_from_dcp (shared_ptr dcp) + { + BOOST_FOREACH (shared_ptr i, dcp->text) { + i->set_use (true); + } + + if (dcp->video) { + Ratio const * r = Ratio::nearest_from_ratio(dcp->video->size().ratio()); + if (r) { + _film->set_container(r); + } + } } bool _update_news_requested; PlayerInformation* _info; wxPreferencesEditor* _config_dialog; wxMenu* _file_menu; + wxMenuItem* _view_cpl; + wxMenu* _cpl_menu; int _history_items; int _history_position; wxMenuItem* _history_separator; - FilmViewer* _viewer; + ControlFilmViewer* _viewer; boost::shared_ptr _film; boost::signals2::scoped_connection _config_changed_connection; wxMenuItem* _file_add_ov; wxMenuItem* _file_add_kdm; + wxMenuItem* _tools_verify; }; static const wxCmdLineEntryDesc command_line_description[] = { @@ -490,6 +662,32 @@ static const wxCmdLineEntryDesc command_line_description[] = { { wxCMD_LINE_NONE, "", "", "", wxCmdLineParamType (0), 0 } }; +class PlayServer : public Server +{ +public: + explicit PlayServer (DOMFrame* frame) + : Server (PLAYER_PLAY_PORT) + , _frame (frame) + {} + + void handle (shared_ptr socket) + { + try { + int const length = socket->read_uint32 (); + scoped_array buffer (new char[length]); + socket->read (reinterpret_cast (buffer.get()), length); + string s (buffer.get()); + signal_manager->when_idle (bind (&DOMFrame::load_dcp, _frame, s)); + socket->write (reinterpret_cast ("OK"), 3); + } catch (...) { + + } + } + +private: + DOMFrame* _frame; +}; + /** @class App * @brief The magic App class for wxWidgets. */ @@ -559,6 +757,9 @@ private: signal_manager = new wxSignalManager (this); + PlayServer* server = new PlayServer (_frame); + new thread (boost::bind (&PlayServer::run, server)); + if (!_dcp_to_load.empty() && boost::filesystem::is_directory (_dcp_to_load)) { try { _frame->load_dcp (_dcp_to_load);