From ad70feebe3a9a89865185b05e084b326637ff81e Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Fri, 20 Jul 2018 19:55:22 +0100 Subject: [PATCH] Untested; allow viewing of subtitles or closed captions in the preview. --- src/lib/active_captions.cc | 6 ++--- src/lib/active_captions.h | 2 +- src/lib/ffmpeg_encoder.cc | 2 +- src/lib/player.cc | 52 ++++++++++++++++++++----------------- src/lib/player.h | 10 +++---- src/lib/player_video.cc | 36 +++++++++++++------------- src/lib/player_video.h | 4 +-- src/wx/film_viewer.cc | 53 +++++++++++++++++++++++++++----------- src/wx/film_viewer.h | 5 ++-- test/client_server_test.cc | 4 +-- test/player_test.cc | 4 +-- 11 files changed, 104 insertions(+), 74 deletions(-) diff --git a/src/lib/active_captions.cc b/src/lib/active_captions.cc index d41270382..bb64f995d 100644 --- a/src/lib/active_captions.cc +++ b/src/lib/active_captions.cc @@ -33,10 +33,10 @@ using boost::optional; /** Get the subtitles that should be burnt into a given period. * @param period Period of interest. - * @param always_burn_subtitles Always burn subtitles even if their content is not set to burn. + * @param always_burn_captions Always burn captions even if their content is not set to burn. */ list -ActiveCaptions::get_burnt (DCPTimePeriod period, bool always_burn_subtitles) const +ActiveCaptions::get_burnt (DCPTimePeriod period, bool always_burn_captions) const { list ps; @@ -47,7 +47,7 @@ ActiveCaptions::get_burnt (DCPTimePeriod period, bool always_burn_subtitles) con continue; } - if (!piece->content->caption->use() || (!always_burn_subtitles && !piece->content->caption->burn())) { + if (!piece->content->caption->use() || (!always_burn_captions && !piece->content->caption->burn())) { /* Not burning this piece */ continue; } diff --git a/src/lib/active_captions.h b/src/lib/active_captions.h index 718ba393e..e0e8acf8e 100644 --- a/src/lib/active_captions.h +++ b/src/lib/active_captions.h @@ -36,7 +36,7 @@ class Piece; class ActiveCaptions : public boost::noncopyable { public: - std::list get_burnt (DCPTimePeriod period, bool always_burn_subtitles) const; + std::list get_burnt (DCPTimePeriod period, bool always_burn_captions) const; void clear_before (DCPTime time); void clear (); void add_from (boost::weak_ptr piece, PlayerCaption ps, DCPTime from); diff --git a/src/lib/ffmpeg_encoder.cc b/src/lib/ffmpeg_encoder.cc index cc2591498..10d439f94 100644 --- a/src/lib/ffmpeg_encoder.cc +++ b/src/lib/ffmpeg_encoder.cc @@ -72,7 +72,7 @@ FFmpegEncoder::FFmpegEncoder (shared_ptr film, weak_ptr job, bo break; } - _player->set_always_burn_subtitles (true); + _player->set_always_burn_captions (CAPTION_OPEN); _player->set_play_referenced (); int const ch = film->audio_channels (); diff --git a/src/lib/player.cc b/src/lib/player.cc index 7040bd530..719e59acc 100644 --- a/src/lib/player.cc +++ b/src/lib/player.cc @@ -89,7 +89,6 @@ Player::Player (shared_ptr film, shared_ptr playlist , _have_valid_pieces (false) , _ignore_video (false) , _ignore_subtitle (false) - , _always_burn_subtitles (false) , _fast (false) , _play_referenced (false) , _audio_merger (_film->audio_frame_rate()) @@ -431,14 +430,14 @@ Player::set_ignore_subtitle () _ignore_subtitle = true; } -/** Set whether or not this player should always burn text subtitles into the image, +/** Set a type of caption that this player should always burn into the image, * regardless of the content settings. - * @param burn true to always burn subtitles, false to obey content settings. + * @param type type of captions to burn. */ void -Player::set_always_burn_subtitles (bool burn) +Player::set_always_burn_captions (CaptionType type) { - _always_burn_subtitles = burn; + _always_burn_captions = type; } /** Sets up the player to be faster, possibly at the expense of quality */ @@ -657,30 +656,36 @@ Player::pass () } optional -Player::subtitles_for_frame (DCPTime time) const +Player::captions_for_frame (DCPTime time) const { - list subtitles; + list captions; int const vfr = _film->video_frame_rate(); - BOOST_FOREACH (PlayerCaption i, _active_captions[CAPTION_OPEN].get_burnt (DCPTimePeriod(time, time + DCPTime::from_frames(1, vfr)), _always_burn_subtitles)) { - - /* Image subtitles */ - list c = transform_bitmap_captions (i.image); - copy (c.begin(), c.end(), back_inserter (subtitles)); - - /* Text subtitles (rendered to an image) */ - if (!i.text.empty ()) { - list s = render_text (i.text, i.fonts, _video_container_size, time, vfr); - copy (s.begin(), s.end(), back_inserter (subtitles)); + for (int i = 0; i < CAPTION_COUNT; ++i) { + bool const always = _always_burn_captions && *_always_burn_captions == i; + BOOST_FOREACH ( + PlayerCaption j, + _active_captions[i].get_burnt(DCPTimePeriod(time, time + DCPTime::from_frames(1, vfr)), always) + ) { + + /* Image subtitles */ + list 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 s = render_text (j.text, j.fonts, _video_container_size, time, vfr); + copy (s.begin(), s.end(), back_inserter (captions)); + } } } - if (subtitles.empty ()) { + if (captions.empty ()) { return optional (); } - return merge (subtitles); + return merge (captions); } void @@ -928,7 +933,8 @@ Player::subtitle_stop (weak_ptr wp, ContentTime to, CaptionType type) pair from = _active_captions[type].add_to (wp, dcp_to); - if (piece->content->caption->use() && !_always_burn_subtitles && !piece->content->caption->burn()) { + bool const always = _always_burn_captions && *_always_burn_captions == type; + if (piece->content->caption->use() && !always && !piece->content->caption->burn()) { Caption (from.first, type, DCPTimePeriod (from.second, dcp_to)); } } @@ -1017,9 +1023,9 @@ Player::do_emit_video (shared_ptr pv, DCPTime time) } } - optional subtitles = subtitles_for_frame (time); - if (subtitles) { - pv->set_subtitle (subtitles.get ()); + optional captions = captions_for_frame (time); + if (captions) { + pv->set_caption (captions.get ()); } Video (pv, time); diff --git a/src/lib/player.h b/src/lib/player.h index 5b6a0b7b4..cbadb11f6 100644 --- a/src/lib/player.h +++ b/src/lib/player.h @@ -79,7 +79,7 @@ public: void set_video_container_size (dcp::Size); void set_ignore_video (); void set_ignore_subtitle (); - void set_always_burn_subtitles (bool burn); + void set_always_burn_captions (CaptionType type); void set_fast (); void set_play_referenced (); void set_dcp_decode_reduction (boost::optional reduction); @@ -134,7 +134,7 @@ private: std::pair, DCPTime> discard_audio ( boost::shared_ptr audio, DCPTime time, DCPTime discard_to ) const; - boost::optional subtitles_for_frame (DCPTime time) const; + boost::optional captions_for_frame (DCPTime time) const; void emit_video (boost::shared_ptr pv, DCPTime time); void do_emit_video (boost::shared_ptr pv, DCPTime time); void emit_audio (boost::shared_ptr data, DCPTime time); @@ -154,10 +154,10 @@ private: bool _ignore_video; /** true if the player should ignore all audio; i.e. never produce any */ bool _ignore_subtitle; - /** true if the player should always burn subtitles into the video regardless - of content settings + /** Type of captions that the player should always burn into the video regardless + of content settings. */ - bool _always_burn_subtitles; + boost::optional _always_burn_captions; /** true if we should try to be fast rather than high quality */ bool _fast; /** true if we should `play' (i.e output) referenced DCP data (e.g. for preview) */ diff --git a/src/lib/player_video.cc b/src/lib/player_video.cc index a50b196a2..c8fb044aa 100644 --- a/src/lib/player_video.cc +++ b/src/lib/player_video.cc @@ -92,14 +92,14 @@ PlayerVideo::PlayerVideo (shared_ptr node, shared_ptr socket image->read_from_socket (socket); - _subtitle = PositionImage (image, Position (node->number_child ("SubtitleX"), node->number_child ("SubtitleY"))); + _caption = PositionImage (image, Position (node->number_child ("SubtitleX"), node->number_child ("SubtitleY"))); } } void -PlayerVideo::set_subtitle (PositionImage image) +PlayerVideo::set_caption (PositionImage image) { - _subtitle = image; + _caption = image; } /** Create an image for this frame. @@ -153,8 +153,8 @@ PlayerVideo::image (dcp::NoteHandler note, functionpixel_format()), aligned, fast ); - if (_subtitle) { - out->alpha_blend (Image::ensure_aligned (_subtitle->image), _subtitle->position); + if (_caption) { + out->alpha_blend (Image::ensure_aligned (_caption->image), _caption->position); } if (_fade) { @@ -181,11 +181,11 @@ PlayerVideo::add_metadata (xmlpp::Node* node) const if (_colour_conversion) { _colour_conversion.get().as_xml (node); } - if (_subtitle) { - node->add_child ("SubtitleWidth")->add_child_text (raw_convert (_subtitle->image->size().width)); - node->add_child ("SubtitleHeight")->add_child_text (raw_convert (_subtitle->image->size().height)); - node->add_child ("SubtitleX")->add_child_text (raw_convert (_subtitle->position.x)); - node->add_child ("SubtitleY")->add_child_text (raw_convert (_subtitle->position.y)); + if (_caption) { + node->add_child ("SubtitleWidth")->add_child_text (raw_convert (_caption->image->size().width)); + node->add_child ("SubtitleHeight")->add_child_text (raw_convert (_caption->image->size().height)); + node->add_child ("SubtitleX")->add_child_text (raw_convert (_caption->position.x)); + node->add_child ("SubtitleY")->add_child_text (raw_convert (_caption->position.y)); } } @@ -193,8 +193,8 @@ void PlayerVideo::send_binary (shared_ptr socket) const { _in->send_binary (socket); - if (_subtitle) { - _subtitle->image->write_to_socket (socket); + if (_caption) { + _caption->image->write_to_socket (socket); } } @@ -208,7 +208,7 @@ PlayerVideo::has_j2k () const return false; } - return _crop == Crop () && _out_size == j2k->size() && !_subtitle && !_fade && !_colour_conversion; + return _crop == Crop () && _out_size == j2k->size() && !_caption && !_fade && !_colour_conversion; } Data @@ -239,13 +239,13 @@ PlayerVideo::same (shared_ptr other) const return false; } - if ((!_subtitle && other->_subtitle) || (_subtitle && !other->_subtitle)) { - /* One has a subtitle and the other doesn't */ + if ((!_caption && other->_caption) || (_caption && !other->_caption)) { + /* One has a caption and the other doesn't */ return false; } - if (_subtitle && other->_subtitle && !_subtitle->same (other->_subtitle.get ())) { - /* They both have subtitles but they are different */ + if (_caption && other->_caption && !_caption->same (other->_caption.get ())) { + /* They both have captions but they are different */ return false; } @@ -278,7 +278,7 @@ PlayerVideo::memory_used () const return _in->memory_used(); } -/** @return Shallow copy of this; _in and _subtitle are shared between the original and the copy */ +/** @return Shallow copy of this; _in and _caption are shared between the original and the copy */ shared_ptr PlayerVideo::shallow_copy () const { diff --git a/src/lib/player_video.h b/src/lib/player_video.h index f4bf2a471..96878e0bd 100644 --- a/src/lib/player_video.h +++ b/src/lib/player_video.h @@ -60,7 +60,7 @@ public: boost::shared_ptr shallow_copy () const; - void set_subtitle (PositionImage); + void set_caption (PositionImage); void prepare (); boost::shared_ptr image (dcp::NoteHandler note, boost::function pixel_format, bool aligned, bool fast) const; @@ -109,7 +109,7 @@ private: Eyes _eyes; Part _part; boost::optional _colour_conversion; - boost::optional _subtitle; + boost::optional _caption; /** Content that we came from. This is so that reset_metadata() can work */ boost::weak_ptr _content; /** Video frame that we came from. Again, this is for reset_metadata() */ diff --git a/src/wx/film_viewer.cc b/src/wx/film_viewer.cc index 1e615da6f..56342d44f 100644 --- a/src/wx/film_viewer.cc +++ b/src/wx/film_viewer.cc @@ -76,8 +76,7 @@ FilmViewer::FilmViewer (wxWindow* p, bool outline_content, bool jump_to_selected : wxPanel (p) , _panel (new wxPanel (this)) , _outline_content (0) - , _left_eye (new wxRadioButton (this, wxID_ANY, _("Left eye"), wxDefaultPosition, wxDefaultSize, wxRB_GROUP)) - , _right_eye (new wxRadioButton (this, wxID_ANY, _("Right eye"))) + , _eye (0) , _jump_to_selected (0) , _slider (new wxSlider (this, wxID_ANY, 0, 0, 4096)) , _rewind_button (new wxButton (this, wxID_ANY, wxT("|<"))) @@ -110,14 +109,26 @@ FilmViewer::FilmViewer (wxWindow* p, bool outline_content, bool jump_to_selected wxBoxSizer* view_options = new wxBoxSizer (wxHORIZONTAL); if (outline_content) { _outline_content = new wxCheckBox (this, wxID_ANY, _("Outline content")); - view_options->Add (_outline_content, 0, wxRIGHT, DCPOMATIC_SIZER_GAP); + view_options->Add (_outline_content, 0, wxRIGHT | wxALIGN_CENTER_VERTICAL, DCPOMATIC_SIZER_GAP); } - view_options->Add (_left_eye, 0, wxLEFT | wxRIGHT, DCPOMATIC_SIZER_GAP); - view_options->Add (_right_eye, 0, wxLEFT | wxRIGHT, DCPOMATIC_SIZER_GAP); + + _eye = new wxChoice (this, wxID_ANY); + _eye->Append (_("Left")); + _eye->Append (_("Right")); + _eye->SetSelection (0); + view_options->Add (_eye, 0, wxLEFT | wxRIGHT | wxALIGN_CENTER_VERTICAL, DCPOMATIC_SIZER_GAP); + if (jump_to_selected) { _jump_to_selected = new wxCheckBox (this, wxID_ANY, _("Jump to selected content")); - view_options->Add (_jump_to_selected, 0, wxLEFT | wxRIGHT, DCPOMATIC_SIZER_GAP); + view_options->Add (_jump_to_selected, 0, wxLEFT | wxRIGHT | wxALIGN_CENTER_VERTICAL, DCPOMATIC_SIZER_GAP); } + + _captions = new wxChoice (this, wxID_ANY); + _captions->Append (_("Open captions (subtitles)")); + _captions->Append (_("Closed captions")); + _captions->SetSelection (0); + view_options->Add (_captions, 0, wxLEFT | wxRIGHT, DCPOMATIC_SIZER_GAP); + _v_sizer->Add (view_options, 0, wxALL, DCPOMATIC_SIZER_GAP); wxBoxSizer* h_sizer = new wxBoxSizer (wxHORIZONTAL); @@ -145,8 +156,8 @@ FilmViewer::FilmViewer (wxWindow* p, bool outline_content, bool jump_to_selected if (_outline_content) { _outline_content->Bind (wxEVT_CHECKBOX, boost::bind (&FilmViewer::refresh_panel, this)); } - _left_eye->Bind (wxEVT_RADIOBUTTON, boost::bind (&FilmViewer::slow_refresh, this)); - _right_eye->Bind (wxEVT_RADIOBUTTON, boost::bind (&FilmViewer::slow_refresh, this)); + _eye->Bind (wxEVT_CHOICE, boost::bind (&FilmViewer::slow_refresh, this)); + _captions->Bind (wxEVT_CHOICE, boost::bind (&FilmViewer::captions_changed, this)); _slider->Bind (wxEVT_SCROLL_THUMBTRACK, boost::bind (&FilmViewer::slider_moved, this, false)); _slider->Bind (wxEVT_SCROLL_PAGEUP, boost::bind (&FilmViewer::slider_moved, this, true)); _slider->Bind (wxEVT_SCROLL_PAGEDOWN, boost::bind (&FilmViewer::slider_moved, this, true)); @@ -214,10 +225,8 @@ FilmViewer::set_film (shared_ptr film) return; } - /* Always burn in subtitles, even if content is set not to, otherwise we won't see them - in the preview. - */ - _player->set_always_burn_subtitles (true); + /* Start off burning in subtitles, as that's the initial setting of the dropdown */ + _player->set_always_burn_captions (CAPTION_OPEN); _player->set_play_referenced (); _film->Changed.connect (boost::bind (&FilmViewer::film_changed, this, _1)); @@ -291,7 +300,7 @@ FilmViewer::get () _player_video = _butler->get_video (); } while ( _film->three_d() && - ((_left_eye->GetValue() && _player_video.first->eyes() == EYES_RIGHT) || (_right_eye->GetValue() && _player_video.first->eyes() == EYES_LEFT)) + ((_eye->GetSelection() == 0 && _player_video.first->eyes() == EYES_RIGHT) || (_eye->GetSelection() == 1 && _player_video.first->eyes() == EYES_LEFT)) ); _butler->rethrow (); @@ -708,8 +717,7 @@ FilmViewer::setup_sensitivity () _jump_to_selected->Enable (c); } - _left_eye->Enable (c && _film->three_d ()); - _right_eye->Enable (c && _film->three_d ()); + _eye->Enable (c && _film->three_d ()); } void @@ -722,6 +730,21 @@ FilmViewer::film_changed (Film::Property p) } } +void +FilmViewer::captions_changed () +{ + switch (_captions->GetSelection()) { + case 0: + _player->set_always_burn_captions (CAPTION_OPEN); + break; + case 1: + _player->set_always_burn_captions (CAPTION_CLOSED); + break; + } + + slow_refresh (); +} + /** Re-get the current frame slowly by seeking */ void FilmViewer::slow_refresh () diff --git a/src/wx/film_viewer.h b/src/wx/film_viewer.h index 11d06cc46..cb372391a 100644 --- a/src/wx/film_viewer.h +++ b/src/wx/film_viewer.h @@ -87,6 +87,7 @@ private: void slider_released (); void play_clicked (); void timer (); + void captions_changed (); void calculate_sizes (); void check_play_state (); void active_jobs_changed (boost::optional); @@ -120,9 +121,9 @@ private: /** The area that we put our image in */ wxPanel* _panel; wxCheckBox* _outline_content; - wxRadioButton* _left_eye; - wxRadioButton* _right_eye; + wxChoice* _eye; wxCheckBox* _jump_to_selected; + wxChoice* _captions; wxSlider* _slider; wxButton* _rewind_button; wxButton* _back_button; diff --git a/test/client_server_test.cc b/test/client_server_test.cc index d77ed4c15..74f0cae59 100644 --- a/test/client_server_test.cc +++ b/test/client_server_test.cc @@ -101,7 +101,7 @@ BOOST_AUTO_TEST_CASE (client_server_test_rgb) ) ); - pvf->set_subtitle (PositionImage (sub_image, Position (50, 60))); + pvf->set_caption (PositionImage (sub_image, Position (50, 60))); shared_ptr frame ( new DCPVideo ( @@ -186,7 +186,7 @@ BOOST_AUTO_TEST_CASE (client_server_test_yuv) ) ); - pvf->set_subtitle (PositionImage (sub_image, Position (50, 60))); + pvf->set_caption (PositionImage (sub_image, Position (50, 60))); shared_ptr frame ( new DCPVideo ( diff --git a/test/player_test.cc b/test/player_test.cc index 4ff79b436..510083236 100644 --- a/test/player_test.cc +++ b/test/player_test.cc @@ -215,7 +215,7 @@ BOOST_AUTO_TEST_CASE (player_seek_test) shared_ptr player (new Player (film, film->playlist())); player->set_fast (); - player->set_always_burn_subtitles (true); + player->set_always_burn_captions (CAPTION_OPEN); player->set_play_referenced (); shared_ptr butler (new Butler (player, film->log(), AudioMapping(), 2)); @@ -246,7 +246,7 @@ BOOST_AUTO_TEST_CASE (player_seek_test2) shared_ptr player (new Player (film, film->playlist())); player->set_fast (); - player->set_always_burn_subtitles (true); + player->set_always_burn_captions (CAPTION_OPEN); player->set_play_referenced (); shared_ptr butler (new Butler (player, film->log(), AudioMapping(), 2)); -- 2.30.2