diff options
| author | Carl Hetherington <cth@carlh.net> | 2018-07-23 00:09:35 +0100 |
|---|---|---|
| committer | Carl Hetherington <cth@carlh.net> | 2018-07-23 00:13:56 +0100 |
| commit | bd763364d9c96928f3bd6bd5fe46ebe1a06e6d0a (patch) | |
| tree | 0ece2cfda3b6a1f4f68187eb89daac4d4a6e20b8 /src | |
| parent | 5274b881cc5569188eb5c80dc3151f5a8aaa1d23 (diff) | |
Very basic closed caption viewer.
Diffstat (limited to 'src')
| -rw-r--r-- | src/tools/dcpomatic.cc | 25 | ||||
| -rw-r--r-- | src/wx/caption_panel.cc | 1 | ||||
| -rw-r--r-- | src/wx/closed_captions_dialog.cc | 143 | ||||
| -rw-r--r-- | src/wx/closed_captions_dialog.h | 42 | ||||
| -rw-r--r-- | src/wx/closed_captions_view.cc | 143 | ||||
| -rw-r--r-- | src/wx/closed_captions_view.h | 42 | ||||
| -rw-r--r-- | src/wx/film_viewer.cc | 21 | ||||
| -rw-r--r-- | src/wx/film_viewer.h | 9 | ||||
| -rw-r--r-- | src/wx/wscript | 1 |
9 files changed, 420 insertions, 7 deletions
diff --git a/src/tools/dcpomatic.cc b/src/tools/dcpomatic.cc index 416bdf6c4..b6aa2188a 100644 --- a/src/tools/dcpomatic.cc +++ b/src/tools/dcpomatic.cc @@ -212,7 +212,8 @@ enum { ID_jobs_send_dcp_to_tms, ID_jobs_show_dcp, ID_jobs_open_dcp_in_player, - ID_tools_video_waveform, + ID_view_closed_captions, + ID_view_video_waveform, ID_tools_hints, ID_tools_encoding_servers, ID_tools_manage_templates, @@ -297,7 +298,8 @@ public: Bind (wxEVT_MENU, boost::bind (&DOMFrame::jobs_send_dcp_to_tms, this), ID_jobs_send_dcp_to_tms); Bind (wxEVT_MENU, boost::bind (&DOMFrame::jobs_show_dcp, this), ID_jobs_show_dcp); Bind (wxEVT_MENU, boost::bind (&DOMFrame::jobs_open_dcp_in_player, this), ID_jobs_open_dcp_in_player); - Bind (wxEVT_MENU, boost::bind (&DOMFrame::tools_video_waveform, this), ID_tools_video_waveform); + Bind (wxEVT_MENU, boost::bind (&DOMFrame::view_closed_captions, this), ID_view_closed_captions); + Bind (wxEVT_MENU, boost::bind (&DOMFrame::view_video_waveform, this), ID_view_video_waveform); Bind (wxEVT_MENU, boost::bind (&DOMFrame::tools_hints, this), ID_tools_hints); Bind (wxEVT_MENU, boost::bind (&DOMFrame::tools_encoding_servers, this), ID_tools_encoding_servers); Bind (wxEVT_MENU, boost::bind (&DOMFrame::tools_manage_templates, this), ID_tools_manage_templates); @@ -424,8 +426,10 @@ public: _film = film; _film_viewer->set_film (_film); _film_editor->set_film (_film); - delete _video_waveform_dialog; - _video_waveform_dialog = 0; + if (_video_waveform_dialog) { + _video_waveform_dialog->Destroy (); + _video_waveform_dialog = 0; + } set_menu_sensitivity (); if (_film->directory()) { Config::instance()->add_to_history (_film->directory().get()); @@ -882,7 +886,12 @@ private: #endif } - void tools_video_waveform () + void view_closed_captions () + { + _film_viewer->show_closed_captions (); + } + + void view_video_waveform () { if (!_video_waveform_dialog) { _video_waveform_dialog = new VideoWaveformDialog (this, _film, _film_viewer); @@ -1141,8 +1150,11 @@ private: add_item (jobs_menu, _("S&how DCP"), ID_jobs_show_dcp, NEEDS_FILM | NOT_DURING_DCP_CREATION | NEEDS_CPL); add_item (jobs_menu, _("Open DCP in &player"), ID_jobs_open_dcp_in_player, NEEDS_FILM | NOT_DURING_DCP_CREATION | NEEDS_CPL); + wxMenu* view = new wxMenu; + add_item (view, _("Closed captions..."), ID_view_closed_captions, NEEDS_FILM); + add_item (view, _("Video waveform..."), ID_view_video_waveform, NEEDS_FILM); + wxMenu* tools = new wxMenu; - add_item (tools, _("Video waveform..."), ID_tools_video_waveform, NEEDS_FILM); add_item (tools, _("Hints..."), ID_tools_hints, 0); add_item (tools, _("Encoding servers..."), ID_tools_encoding_servers, 0); add_item (tools, _("Manage templates..."), ID_tools_manage_templates, 0); @@ -1162,6 +1174,7 @@ private: m->Append (edit, _("&Edit")); m->Append (content, _("&Content")); m->Append (jobs_menu, _("&Jobs")); + m->Append (view, _("&View")); m->Append (tools, _("&Tools")); m->Append (help, _("&Help")); } diff --git a/src/wx/caption_panel.cc b/src/wx/caption_panel.cc index 3957c2a45..1d2f59258 100644 --- a/src/wx/caption_panel.cc +++ b/src/wx/caption_panel.cc @@ -40,6 +40,7 @@ using std::vector; using std::string; using std::list; +using std::cout; using boost::shared_ptr; using boost::dynamic_pointer_cast; diff --git a/src/wx/closed_captions_dialog.cc b/src/wx/closed_captions_dialog.cc new file mode 100644 index 000000000..be520057b --- /dev/null +++ b/src/wx/closed_captions_dialog.cc @@ -0,0 +1,143 @@ +/* + Copyright (C) 2018 Carl Hetherington <cth@carlh.net> + + This file is part of DCP-o-matic. + + DCP-o-matic is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + DCP-o-matic is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with DCP-o-matic. If not, see <http://www.gnu.org/licenses/>. + +*/ + +#include "closed_captions_view.h" +#include <boost/bind.hpp> + +using std::list; +using std::cout; +using std::make_pair; + +int const ClosedCaptionsDialog::_num_lines = 3; +int const ClosedCaptionsDialog::_num_chars_per_line = 30; + +ClosedCaptionsDialog::ClosedCaptionsDialog (wxWindow* parent) + : wxDialog (parent, wxID_ANY, _("Closed captions"), wxDefaultPosition, wxDefaultSize, +#ifdef DCPOMATIC_OSX + /* I can't get wxFRAME_FLOAT_ON_PARENT to work on OS X, and although wxSTAY_ON_TOP keeps + the window above all others (and not just our own) it's better than nothing for now. + */ + wxDEFAULT_FRAME_STYLE | wxRESIZE_BORDER | wxFULL_REPAINT_ON_RESIZE | wxSTAY_ON_TOP +#else + wxDEFAULT_FRAME_STYLE | wxRESIZE_BORDER | wxFULL_REPAINT_ON_RESIZE | wxFRAME_FLOAT_ON_PARENT +#endif + ) + +{ + _lines.resize (_num_lines); + Bind (wxEVT_PAINT, boost::bind (&ClosedCaptionsDialog::paint, this)); +} + +void +ClosedCaptionsDialog::paint () +{ + wxPaintDC dc (this); + dc.SetBackground (*wxBLACK_BRUSH); + dc.Clear (); + dc.SetTextForeground (*wxWHITE); + + /* Choose a font which fits vertically */ + int const line_height = dc.GetSize().GetHeight() / _num_lines; + wxFont font (*wxNORMAL_FONT); + font.SetPixelSize (wxSize (0, line_height * 0.8)); + dc.SetFont (font); + + for (int i = 0; i < _num_lines; ++i) { + if (_lines[i].IsEmpty()) { + dc.DrawText (wxString::Format("Line %d", i + 1), 8, line_height * i); + } else { + dc.DrawText (_lines[i], 8, line_height * i); + } + } +} + +class ClosedCaptionSorter +{ +public: + bool operator() (TextCaption const & a, TextCaption const & b) + { + return from_top(a) < from_top(b); + } + +private: + float from_top (TextCaption const & c) const + { + switch (c.v_align()) { + case dcp::VALIGN_TOP: + return c.v_position(); + case dcp::VALIGN_CENTER: + return c.v_position() + 0.5; + case dcp::VALIGN_BOTTOM: + return 1.0 - c.v_position(); + } + DCPOMATIC_ASSERT (false); + return 0; + } +}; + +void +ClosedCaptionsDialog::refresh (DCPTime time) +{ + list<TextCaption> to_show; + list<Caption>::iterator i = _captions.begin (); + while (i != _captions.end ()) { + if (time > i->second.to) { + list<Caption>::iterator tmp = i; + ++i; + _captions.erase (tmp); + } else if (i->second.contains (time)) { + BOOST_FOREACH (TextCaption j, i->first.text) { + to_show.push_back (j); + } + ++i; + } else { + ++i; + } + } + + for (int j = 0; j < _num_lines; ++j) { + _lines[j] = ""; + } + + to_show.sort (ClosedCaptionSorter()); + + list<TextCaption>::const_iterator j = to_show.begin(); + int k = 0; + while (j != to_show.end() && k < _num_lines) { + _lines[k] = j->text(); + ++j; + ++k; + } + + Refresh (); +} + +void +ClosedCaptionsDialog::caption (PlayerCaption caption, DCPTimePeriod period) +{ + _captions.push_back (make_pair (caption, period)); +} + +void +ClosedCaptionsDialog::clear () +{ + _captions.clear (); + Refresh (); +} diff --git a/src/wx/closed_captions_dialog.h b/src/wx/closed_captions_dialog.h new file mode 100644 index 000000000..ca19cc95a --- /dev/null +++ b/src/wx/closed_captions_dialog.h @@ -0,0 +1,42 @@ +/* + Copyright (C) 2018 Carl Hetherington <cth@carlh.net> + + This file is part of DCP-o-matic. + + DCP-o-matic is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + DCP-o-matic is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with DCP-o-matic. If not, see <http://www.gnu.org/licenses/>. + +*/ + +#include "lib/dcpomatic_time.h" +#include "lib/player_caption.h" +#include <wx/wx.h> + +class ClosedCaptionsView : public wxDialog +{ +public: + ClosedCaptionsView (wxWindow* parent); + + void refresh (DCPTime); + void caption (PlayerCaption, DCPTimePeriod); + void clear (); + +private: + void paint (); + + typedef std::pair<PlayerCaption, DCPTimePeriod> Caption; + std::list<Caption> _captions; + std::vector<wxString> _lines; + static int const _num_lines; + static int const _num_chars_per_line; +}; diff --git a/src/wx/closed_captions_view.cc b/src/wx/closed_captions_view.cc new file mode 100644 index 000000000..be520057b --- /dev/null +++ b/src/wx/closed_captions_view.cc @@ -0,0 +1,143 @@ +/* + Copyright (C) 2018 Carl Hetherington <cth@carlh.net> + + This file is part of DCP-o-matic. + + DCP-o-matic is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + DCP-o-matic is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with DCP-o-matic. If not, see <http://www.gnu.org/licenses/>. + +*/ + +#include "closed_captions_view.h" +#include <boost/bind.hpp> + +using std::list; +using std::cout; +using std::make_pair; + +int const ClosedCaptionsDialog::_num_lines = 3; +int const ClosedCaptionsDialog::_num_chars_per_line = 30; + +ClosedCaptionsDialog::ClosedCaptionsDialog (wxWindow* parent) + : wxDialog (parent, wxID_ANY, _("Closed captions"), wxDefaultPosition, wxDefaultSize, +#ifdef DCPOMATIC_OSX + /* I can't get wxFRAME_FLOAT_ON_PARENT to work on OS X, and although wxSTAY_ON_TOP keeps + the window above all others (and not just our own) it's better than nothing for now. + */ + wxDEFAULT_FRAME_STYLE | wxRESIZE_BORDER | wxFULL_REPAINT_ON_RESIZE | wxSTAY_ON_TOP +#else + wxDEFAULT_FRAME_STYLE | wxRESIZE_BORDER | wxFULL_REPAINT_ON_RESIZE | wxFRAME_FLOAT_ON_PARENT +#endif + ) + +{ + _lines.resize (_num_lines); + Bind (wxEVT_PAINT, boost::bind (&ClosedCaptionsDialog::paint, this)); +} + +void +ClosedCaptionsDialog::paint () +{ + wxPaintDC dc (this); + dc.SetBackground (*wxBLACK_BRUSH); + dc.Clear (); + dc.SetTextForeground (*wxWHITE); + + /* Choose a font which fits vertically */ + int const line_height = dc.GetSize().GetHeight() / _num_lines; + wxFont font (*wxNORMAL_FONT); + font.SetPixelSize (wxSize (0, line_height * 0.8)); + dc.SetFont (font); + + for (int i = 0; i < _num_lines; ++i) { + if (_lines[i].IsEmpty()) { + dc.DrawText (wxString::Format("Line %d", i + 1), 8, line_height * i); + } else { + dc.DrawText (_lines[i], 8, line_height * i); + } + } +} + +class ClosedCaptionSorter +{ +public: + bool operator() (TextCaption const & a, TextCaption const & b) + { + return from_top(a) < from_top(b); + } + +private: + float from_top (TextCaption const & c) const + { + switch (c.v_align()) { + case dcp::VALIGN_TOP: + return c.v_position(); + case dcp::VALIGN_CENTER: + return c.v_position() + 0.5; + case dcp::VALIGN_BOTTOM: + return 1.0 - c.v_position(); + } + DCPOMATIC_ASSERT (false); + return 0; + } +}; + +void +ClosedCaptionsDialog::refresh (DCPTime time) +{ + list<TextCaption> to_show; + list<Caption>::iterator i = _captions.begin (); + while (i != _captions.end ()) { + if (time > i->second.to) { + list<Caption>::iterator tmp = i; + ++i; + _captions.erase (tmp); + } else if (i->second.contains (time)) { + BOOST_FOREACH (TextCaption j, i->first.text) { + to_show.push_back (j); + } + ++i; + } else { + ++i; + } + } + + for (int j = 0; j < _num_lines; ++j) { + _lines[j] = ""; + } + + to_show.sort (ClosedCaptionSorter()); + + list<TextCaption>::const_iterator j = to_show.begin(); + int k = 0; + while (j != to_show.end() && k < _num_lines) { + _lines[k] = j->text(); + ++j; + ++k; + } + + Refresh (); +} + +void +ClosedCaptionsDialog::caption (PlayerCaption caption, DCPTimePeriod period) +{ + _captions.push_back (make_pair (caption, period)); +} + +void +ClosedCaptionsDialog::clear () +{ + _captions.clear (); + Refresh (); +} diff --git a/src/wx/closed_captions_view.h b/src/wx/closed_captions_view.h new file mode 100644 index 000000000..9469b975c --- /dev/null +++ b/src/wx/closed_captions_view.h @@ -0,0 +1,42 @@ +/* + Copyright (C) 2018 Carl Hetherington <cth@carlh.net> + + This file is part of DCP-o-matic. + + DCP-o-matic is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + DCP-o-matic is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with DCP-o-matic. If not, see <http://www.gnu.org/licenses/>. + +*/ + +#include "lib/dcpomatic_time.h" +#include "lib/player_caption.h" +#include <wx/wx.h> + +class ClosedCaptionsDialog : public wxDialog +{ +public: + ClosedCaptionsDialog (wxWindow* parent); + + void refresh (DCPTime); + void caption (PlayerCaption, DCPTimePeriod); + void clear (); + +private: + void paint (); + + typedef std::pair<PlayerCaption, DCPTimePeriod> Caption; + std::list<Caption> _captions; + std::vector<wxString> _lines; + static int const _num_lines; + static int const _num_chars_per_line; +}; diff --git a/src/wx/film_viewer.cc b/src/wx/film_viewer.cc index 18d16a461..9c714b562 100644 --- a/src/wx/film_viewer.cc +++ b/src/wx/film_viewer.cc @@ -26,6 +26,7 @@ #include "playhead_to_timecode_dialog.h" #include "playhead_to_frame_dialog.h" #include "wx_util.h" +#include "closed_captions_view.h" #include "lib/film.h" #include "lib/ratio.h" #include "lib/util.h" @@ -94,6 +95,7 @@ FilmViewer::FilmViewer (wxWindow* p, bool outline_content, bool jump_to_selected , _playing (false) , _latency_history_count (0) , _dropped (0) + , _closed_captions_dialog (new ClosedCaptionsDialog(GetParent())) { #ifndef __WXOSX__ _panel->SetDoubleBuffered (true); @@ -194,6 +196,7 @@ FilmViewer::set_film (shared_ptr<Film> film) _film = film; _frame.reset (); + _closed_captions_dialog->clear (); update_position_slider (); update_position_label (); @@ -223,6 +226,7 @@ FilmViewer::set_film (shared_ptr<Film> film) _film->Changed.connect (boost::bind (&FilmViewer::film_changed, this, _1)); _player->Changed.connect (boost::bind (&FilmViewer::player_changed, this, _1, _2)); + _player->Caption.connect (boost::bind (&FilmViewer::caption, this, _1, _2, _3)); /* Keep about 1 second's worth of history samples */ _latency_history_count = _film->audio_frame_rate() / _audio_block_size; @@ -349,6 +353,8 @@ FilmViewer::display_player_video () _inter_size = _player_video.first->inter_size (); refresh_panel (); + + _closed_captions_dialog->refresh (time()); } void @@ -811,6 +817,7 @@ FilmViewer::seek (DCPTime t, bool accurate) bool const was_running = stop (); + _closed_captions_dialog->clear (); _butler->seek (t, accurate); get (); @@ -935,3 +942,17 @@ FilmViewer::one_video_frame () const { return DCPTime::from_frames (1, _film->video_frame_rate()); } + +void +FilmViewer::show_closed_captions () +{ + _closed_captions_dialog->Show(); +} + +void +FilmViewer::caption (PlayerCaption c, CaptionType t, DCPTimePeriod p) +{ + if (t == CAPTION_CLOSED) { + _closed_captions_dialog->caption (c, p); + } +} diff --git a/src/wx/film_viewer.h b/src/wx/film_viewer.h index 385f6142b..6825ef2c0 100644 --- a/src/wx/film_viewer.h +++ b/src/wx/film_viewer.h @@ -1,5 +1,5 @@ /* - Copyright (C) 2012-2017 Carl Hetherington <cth@carlh.net> + Copyright (C) 2012-2018 Carl Hetherington <cth@carlh.net> This file is part of DCP-o-matic. @@ -24,6 +24,7 @@ #include "lib/film.h" #include "lib/config.h" +#include "lib/player_caption.h" #include <RtAudio.h> #include <wx/wx.h> @@ -34,6 +35,7 @@ class RGBPlusAlphaImage; class PlayerVideo; class Player; class Butler; +class ClosedCaptionsDialog; /** @class FilmViewer * @brief A wx widget to view a preview of a Film. @@ -78,6 +80,8 @@ public: int audio_callback (void* out, unsigned int frames); + void show_closed_captions (); + boost::signals2::signal<void (boost::weak_ptr<PlayerVideo>)> ImageChanged; private: @@ -112,6 +116,7 @@ private: DCPTime time () const; Frame average_latency () const; DCPTime one_video_frame () const; + void caption (PlayerCaption caption, CaptionType type, DCPTimePeriod period); boost::shared_ptr<Film> _film; boost::shared_ptr<Player> _player; @@ -160,5 +165,7 @@ private: int _dropped; boost::optional<int> _dcp_decode_reduction; + ClosedCaptionsDialog* _closed_captions_dialog; + boost::signals2::scoped_connection _config_changed_connection; }; diff --git a/src/wx/wscript b/src/wx/wscript index ec75bd99c..5668a7558 100644 --- a/src/wx/wscript +++ b/src/wx/wscript @@ -45,6 +45,7 @@ sources = """ content_panel.cc content_properties_dialog.cc content_sub_panel.cc + closed_captions_dialog.cc dcp_panel.cc email_dialog.cc image_sequence_dialog.cc |
