diff options
| author | Carl Hetherington <cth@carlh.net> | 2018-07-23 01:21:07 +0100 |
|---|---|---|
| committer | Carl Hetherington <cth@carlh.net> | 2018-07-23 01:21:07 +0100 |
| commit | ded71cffd18962ebb6b9611a5eb6dfafe9e8e4ec (patch) | |
| tree | 6da7f5da15b7b4cdeb82f7099b549abf537bf4fa /src | |
| parent | 07d75f41aed77e340d927cc092dc4e7d74d03897 (diff) | |
Get ccaps by asking the Player, rather than by listening to its emissions,
which is slightly cleaner and works when subtitles are emitted with an
unknown end time. Also add CCAPs to the player.
Diffstat (limited to 'src')
| -rw-r--r-- | src/lib/active_captions.cc | 40 | ||||
| -rw-r--r-- | src/lib/active_captions.h | 3 | ||||
| -rw-r--r-- | src/lib/caption_content.cc | 2 | ||||
| -rw-r--r-- | src/lib/player.cc | 43 | ||||
| -rw-r--r-- | src/lib/player.h | 4 | ||||
| -rw-r--r-- | src/tools/dcpomatic_player.cc | 15 | ||||
| -rw-r--r-- | src/wx/closed_captions_dialog.cc | 34 | ||||
| -rw-r--r-- | src/wx/closed_captions_dialog.h | 15 | ||||
| -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 | 16 | ||||
| -rw-r--r-- | src/wx/film_viewer.h | 1 |
12 files changed, 100 insertions, 258 deletions
diff --git a/src/lib/active_captions.cc b/src/lib/active_captions.cc index b4252e0c3..1d3a53609 100644 --- a/src/lib/active_captions.cc +++ b/src/lib/active_captions.cc @@ -30,7 +30,37 @@ using boost::weak_ptr; using boost::shared_ptr; using boost::optional; -/** Get the subtitles that should be burnt into a given period. +void +ActiveCaptions::add (DCPTimePeriod period, list<PlayerCaption>& pc, list<Period> p) const +{ + BOOST_FOREACH (Period i, p) { + DCPTimePeriod test (i.from, i.to.get_value_or(DCPTime::max())); + optional<DCPTimePeriod> overlap = period.overlap (test); + if (overlap && overlap->duration() > DCPTime(period.duration().get() / 2)) { + pc.push_back (i.subs); + } + } +} + +list<PlayerCaption> +ActiveCaptions::get (DCPTimePeriod period) const +{ + list<PlayerCaption> ps; + + for (Map::const_iterator i = _data.begin(); i != _data.end(); ++i) { + + shared_ptr<const CaptionContent> caption = i->first.lock (); + if (!caption || !caption->use()) { + continue; + } + + add (period, ps, i->second); + } + + return ps; +} + +/** Get the open captions that should be burnt into a given period. * @param period Period of interest. * @param always_burn_captions Always burn captions even if their content is not set to burn. */ @@ -51,13 +81,7 @@ ActiveCaptions::get_burnt (DCPTimePeriod period, bool always_burn_captions) cons continue; } - BOOST_FOREACH (Period j, i->second) { - DCPTimePeriod test (j.from, j.to.get_value_or(DCPTime::max())); - optional<DCPTimePeriod> overlap = period.overlap (test); - if (overlap && overlap->duration() > DCPTime(period.duration().get() / 2)) { - ps.push_back (j.subs); - } - } + add (period, ps, i->second); } return ps; diff --git a/src/lib/active_captions.h b/src/lib/active_captions.h index 10b0b5da9..8e38564f5 100644 --- a/src/lib/active_captions.h +++ b/src/lib/active_captions.h @@ -36,6 +36,7 @@ class CaptionContent; class ActiveCaptions : public boost::noncopyable { public: + std::list<PlayerCaption> get (DCPTimePeriod period) const; std::list<PlayerCaption> get_burnt (DCPTimePeriod period, bool always_burn_captions) const; void clear_before (DCPTime time); void clear (); @@ -61,5 +62,7 @@ private: typedef std::map<boost::weak_ptr<const CaptionContent>, std::list<Period> > Map; + void add (DCPTimePeriod period, std::list<PlayerCaption>& pc, std::list<Period> p) const; + Map _data; }; diff --git a/src/lib/caption_content.cc b/src/lib/caption_content.cc index 37a3e7c70..bbb1bacf3 100644 --- a/src/lib/caption_content.cc +++ b/src/lib/caption_content.cc @@ -67,7 +67,7 @@ CaptionContent::CaptionContent (Content* parent, CaptionType original_type) , _y_scale (1) , _line_spacing (1) , _outline_width (2) - , _type (CAPTION_OPEN) + , _type (original_type) , _original_type (original_type) { diff --git a/src/lib/player.cc b/src/lib/player.cc index dfd309774..4635233ff 100644 --- a/src/lib/player.cc +++ b/src/lib/player.cc @@ -676,29 +676,34 @@ Player::pass () return done; } +list<PlayerCaption> +Player::closed_captions_for_frame (DCPTime time) const +{ + return _active_captions[CAPTION_CLOSED].get ( + DCPTimePeriod(time, time + DCPTime::from_frames(1, _film->video_frame_rate())) + ); +} + +/** @return Open captions for the frame at the given time, converted to images */ optional<PositionImage> -Player::captions_for_frame (DCPTime time) const +Player::open_captions_for_frame (DCPTime time) const { list<PositionImage> captions; - int const vfr = _film->video_frame_rate(); - for (int i = 0; i < CAPTION_COUNT; ++i) { - bool const always = i == CAPTION_OPEN && _always_burn_open_captions; - BOOST_FOREACH ( - PlayerCaption j, - _active_captions[i].get_burnt(DCPTimePeriod(time, time + DCPTime::from_frames(1, vfr)), always) - ) { - - /* Image subtitles */ - list<PositionImage> c = transform_bitmap_captions (j.image); - copy (c.begin(), c.end(), back_inserter (captions)); - - /* Text subtitles (rendered to an image) */ - if (!j.text.empty ()) { - list<PositionImage> s = render_text (j.text, j.fonts, _video_container_size, time, vfr); - copy (s.begin(), s.end(), back_inserter (captions)); - } + BOOST_FOREACH ( + PlayerCaption j, + _active_captions[CAPTION_OPEN].get_burnt(DCPTimePeriod(time, time + DCPTime::from_frames(1, vfr)), _always_burn_open_captions) + ) { + + /* Image subtitles */ + list<PositionImage> c = transform_bitmap_captions (j.image); + copy (c.begin(), c.end(), back_inserter (captions)); + + /* Text subtitles (rendered to an image) */ + if (!j.text.empty ()) { + list<PositionImage> s = render_text (j.text, j.fonts, _video_container_size, time, vfr); + copy (s.begin(), s.end(), back_inserter (captions)); } } @@ -1047,7 +1052,7 @@ Player::do_emit_video (shared_ptr<PlayerVideo> pv, DCPTime time) } } - optional<PositionImage> captions = captions_for_frame (time); + optional<PositionImage> captions = open_captions_for_frame (time); if (captions) { pv->set_caption (captions.get ()); } diff --git a/src/lib/player.h b/src/lib/player.h index d54d927cd..8b85a011f 100644 --- a/src/lib/player.h +++ b/src/lib/player.h @@ -86,6 +86,8 @@ public: DCPTime content_time_to_dcp (boost::shared_ptr<Content> content, ContentTime t); + std::list<PlayerCaption> closed_captions_for_frame (DCPTime time) const; + /** Emitted when something has changed such that if we went back and emitted * the last frame again it would look different. This is not emitted after * a seek. @@ -134,7 +136,7 @@ private: std::pair<boost::shared_ptr<AudioBuffers>, DCPTime> discard_audio ( boost::shared_ptr<const AudioBuffers> audio, DCPTime time, DCPTime discard_to ) const; - boost::optional<PositionImage> captions_for_frame (DCPTime time) const; + boost::optional<PositionImage> open_captions_for_frame (DCPTime time) const; void emit_video (boost::shared_ptr<PlayerVideo> pv, DCPTime time); void do_emit_video (boost::shared_ptr<PlayerVideo> pv, DCPTime time); void emit_audio (boost::shared_ptr<AudioBuffers> data, DCPTime time); diff --git a/src/tools/dcpomatic_player.cc b/src/tools/dcpomatic_player.cc index d357e566b..e5745403d 100644 --- a/src/tools/dcpomatic_player.cc +++ b/src/tools/dcpomatic_player.cc @@ -82,7 +82,8 @@ enum { ID_file_close = 100, ID_view_cpl, /* Allow spare IDs for CPLs */ - ID_view_scale_appropriate = 200, + ID_view_closed_captions = 200, + ID_view_scale_appropriate, ID_view_scale_full, ID_view_scale_half, ID_view_scale_quarter, @@ -134,6 +135,7 @@ 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::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<int>(0)), ID_view_scale_full); Bind (wxEVT_MENU, boost::bind (&DOMFrame::set_decode_reduction, this, optional<int>(1)), ID_view_scale_half); @@ -267,6 +269,7 @@ private: wxMenu* view = new wxMenu; optional<int> 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<bool>(c)); view->AppendRadioItem(ID_view_scale_full, _("Decode at full resolution"))->Check(c && c.get() == 0); @@ -430,6 +433,11 @@ private: dcp->examine (shared_ptr<Job>()); } + void view_closed_captions () + { + _viewer->show_closed_captions (); + } + void tools_verify () { shared_ptr<DCPContent> dcp = boost::dynamic_pointer_cast<DCPContent>(_film->content().front()); @@ -619,10 +627,7 @@ private: void setup_from_dcp (shared_ptr<DCPContent> dcp) { BOOST_FOREACH (shared_ptr<CaptionContent> i, dcp->caption) { - /* XXX: we should offer the option to view closed captions */ - if (i->type() == CAPTION_OPEN) { - i->set_use (true); - } + i->set_use (true); } if (dcp->video) { diff --git a/src/wx/closed_captions_dialog.cc b/src/wx/closed_captions_dialog.cc index 3463ac27a..0b2e63035 100644 --- a/src/wx/closed_captions_dialog.cc +++ b/src/wx/closed_captions_dialog.cc @@ -18,12 +18,15 @@ */ -#include "closed_captions_view.h" +#include "closed_captions_dialog.h" +#include "lib/text_caption.h" #include <boost/bind.hpp> using std::list; using std::cout; using std::make_pair; +using boost::shared_ptr; +using boost::weak_ptr; int const ClosedCaptionsDialog::_num_lines = 3; int const ClosedCaptionsDialog::_num_chars_per_line = 30; @@ -89,22 +92,14 @@ private: }; void -ClosedCaptionsDialog::refresh (DCPTime time) +ClosedCaptionsDialog::update (DCPTime time) { + shared_ptr<Player> player = _player.lock (); + DCPOMATIC_ASSERT (player); 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; + BOOST_FOREACH (PlayerCaption i, player->closed_captions_for_frame(time)) { + BOOST_FOREACH (TextCaption j, i.text) { + to_show.push_back (j); } } @@ -126,14 +121,13 @@ ClosedCaptionsDialog::refresh (DCPTime time) } void -ClosedCaptionsDialog::caption (PlayerCaption caption, DCPTimePeriod period) +ClosedCaptionsDialog::clear () { - _captions.push_back (make_pair (caption, period)); + Refresh (); } void -ClosedCaptionsDialog::clear () +ClosedCaptionsDialog::set_player (weak_ptr<Player> player) { - _captions.clear (); - Refresh (); + _player = player; } diff --git a/src/wx/closed_captions_dialog.h b/src/wx/closed_captions_dialog.h index ca19cc95a..a599bc703 100644 --- a/src/wx/closed_captions_dialog.h +++ b/src/wx/closed_captions_dialog.h @@ -19,24 +19,25 @@ */ #include "lib/dcpomatic_time.h" -#include "lib/player_caption.h" +#include "lib/player.h" #include <wx/wx.h> -class ClosedCaptionsView : public wxDialog +class Player; + +class ClosedCaptionsDialog : public wxDialog { public: - ClosedCaptionsView (wxWindow* parent); + ClosedCaptionsDialog (wxWindow* parent); - void refresh (DCPTime); - void caption (PlayerCaption, DCPTimePeriod); + void update (DCPTime); void clear (); + void set_player (boost::weak_ptr<Player>); private: void paint (); - typedef std::pair<PlayerCaption, DCPTimePeriod> Caption; - std::list<Caption> _captions; std::vector<wxString> _lines; + boost::weak_ptr<Player> _player; 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 deleted file mode 100644 index be520057b..000000000 --- a/src/wx/closed_captions_view.cc +++ /dev/null @@ -1,143 +0,0 @@ -/* - 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 deleted file mode 100644 index 9469b975c..000000000 --- a/src/wx/closed_captions_view.h +++ /dev/null @@ -1,42 +0,0 @@ -/* - 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 9c714b562..651196d3c 100644 --- a/src/wx/film_viewer.cc +++ b/src/wx/film_viewer.cc @@ -26,7 +26,7 @@ #include "playhead_to_timecode_dialog.h" #include "playhead_to_frame_dialog.h" #include "wx_util.h" -#include "closed_captions_view.h" +#include "closed_captions_dialog.h" #include "lib/film.h" #include "lib/ratio.h" #include "lib/util.h" @@ -203,6 +203,7 @@ FilmViewer::set_film (shared_ptr<Film> film) if (!_film) { _player.reset (); + _closed_captions_dialog->set_player (_player); recreate_butler (); _frame.reset (); refresh_panel (); @@ -221,12 +222,13 @@ FilmViewer::set_film (shared_ptr<Film> film) return; } + _closed_captions_dialog->set_player (_player); + _player->set_always_burn_open_captions (); _player->set_play_referenced (); _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; @@ -354,7 +356,7 @@ FilmViewer::display_player_video () refresh_panel (); - _closed_captions_dialog->refresh (time()); + _closed_captions_dialog->update (time()); } void @@ -948,11 +950,3 @@ 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 6825ef2c0..266509a44 100644 --- a/src/wx/film_viewer.h +++ b/src/wx/film_viewer.h @@ -116,7 +116,6 @@ 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; |
