From ba8a5a15cc27988e2bbc6acd470d8532f1d8e99f Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Wed, 26 Apr 2017 15:29:21 +0100 Subject: [PATCH] Initial work on removing storage of subtitle times. --- src/lib/content_subtitle.h | 18 +-- src/lib/ffmpeg_decoder.cc | 42 +++--- src/lib/ffmpeg_decoder.h | 6 +- src/lib/ffmpeg_examiner.cc | 109 +--------------- src/lib/ffmpeg_examiner.h | 3 +- src/lib/ffmpeg_subtitle_stream.cc | 152 +--------------------- src/lib/ffmpeg_subtitle_stream.h | 18 +-- src/lib/player.cc | 76 ++++++----- src/lib/player.h | 15 ++- src/lib/subtitle_decoder.cc | 45 +++++-- src/lib/subtitle_decoder.h | 12 +- src/wx/image_subtitle_colour_dialog.cc | 96 -------------- src/wx/image_subtitle_colour_dialog.h | 42 ------ src/wx/subtitle_panel.cc | 65 ++------- src/wx/subtitle_view.cc | 27 +++- src/wx/subtitle_view.h | 4 +- src/wx/text_subtitle_appearance_dialog.cc | 139 -------------------- src/wx/text_subtitle_appearance_dialog.h | 56 -------- src/wx/wscript | 3 +- test/dcp_subtitle_test.cc | 10 +- 20 files changed, 177 insertions(+), 761 deletions(-) delete mode 100644 src/wx/image_subtitle_colour_dialog.cc delete mode 100644 src/wx/image_subtitle_colour_dialog.h delete mode 100644 src/wx/text_subtitle_appearance_dialog.cc delete mode 100644 src/wx/text_subtitle_appearance_dialog.h diff --git a/src/lib/content_subtitle.h b/src/lib/content_subtitle.h index 8c0f29f4e..8751d56cb 100644 --- a/src/lib/content_subtitle.h +++ b/src/lib/content_subtitle.h @@ -32,23 +32,23 @@ class Image; class ContentSubtitle { public: - ContentSubtitle (ContentTimePeriod p) - : _period (p) + ContentSubtitle (ContentTime f) + : _from (f) {} - ContentTimePeriod period () const { - return _period; + ContentTime from () const { + return _from; } private: - ContentTimePeriod _period; + ContentTime _from; }; class ContentImageSubtitle : public ContentSubtitle { public: - ContentImageSubtitle (ContentTimePeriod p, boost::shared_ptr im, dcpomatic::Rect r) - : ContentSubtitle (p) + ContentImageSubtitle (ContentTime f, boost::shared_ptr im, dcpomatic::Rect r) + : ContentSubtitle (f) , sub (im, r) {} @@ -63,8 +63,8 @@ public: class ContentTextSubtitle : public ContentSubtitle { public: - ContentTextSubtitle (ContentTimePeriod p, std::list s) - : ContentSubtitle (p) + ContentTextSubtitle (ContentTime f, std::list s) + : ContentSubtitle (f) , subs (s) {} diff --git a/src/lib/ffmpeg_decoder.cc b/src/lib/ffmpeg_decoder.cc index 4635cd5a3..1c886284b 100644 --- a/src/lib/ffmpeg_decoder.cc +++ b/src/lib/ffmpeg_decoder.cc @@ -81,6 +81,7 @@ using dcp::Size; FFmpegDecoder::FFmpegDecoder (shared_ptr c, shared_ptr log) : FFmpeg (c) , _log (log) + , _have_current_subtitle (false) { if (c->video) { video.reset (new VideoDecoder (this, c, log)); @@ -346,6 +347,8 @@ FFmpegDecoder::seek (ContentTime time, bool accurate) if (subtitle_codec_context ()) { avcodec_flush_buffers (subtitle_codec_context ()); } + + _have_current_subtitle = false; } void @@ -477,10 +480,18 @@ FFmpegDecoder::decode_subtitle_packet () return; } + /* Stop any current subtitle, either at the time it was supposed to stop, or now if now is sooner */ + if (_have_current_subtitle) { + if (_current_subtitle_to) { + subtitle->emit_stop (min(*_current_subtitle_to, subtitle_period(sub).from + _pts_offset)); + } else { + subtitle->emit_stop (subtitle_period(sub).from + _pts_offset); + } + _have_current_subtitle = false; + } + if (sub.num_rects <= 0) { - /* Sometimes we get an empty AVSubtitle, which is used by some codecs to - indicate that the previous subtitle should stop. We can ignore it here. - */ + /* Nothing new in this subtitle */ return; } @@ -488,13 +499,12 @@ FFmpegDecoder::decode_subtitle_packet () source that we may have chopped off for the DCP). */ FFmpegSubtitlePeriod sub_period = subtitle_period (sub); - ContentTimePeriod period; - period.from = sub_period.from + _pts_offset; - /* We can't trust the `to' time from sub_period as there are some decoders which - give a sub_period time for `to' which is subsequently overridden by a `stop' subtitle; - see also FFmpegExaminer. - */ - period.to = ffmpeg_content()->subtitle_stream()->find_subtitle_to (subtitle_id (sub)); + ContentTime from; + from = sub_period.from + _pts_offset; + _have_current_subtitle = true; + if (sub_period.to) { + _current_subtitle_to = *sub_period.to + _pts_offset; + } for (unsigned int i = 0; i < sub.num_rects; ++i) { AVSubtitleRect const * rect = sub.rects[i]; @@ -503,13 +513,13 @@ FFmpegDecoder::decode_subtitle_packet () case SUBTITLE_NONE: break; case SUBTITLE_BITMAP: - decode_bitmap_subtitle (rect, period); + decode_bitmap_subtitle (rect, from); break; case SUBTITLE_TEXT: cout << "XXX: SUBTITLE_TEXT " << rect->text << "\n"; break; case SUBTITLE_ASS: - decode_ass_subtitle (rect->ass, period); + decode_ass_subtitle (rect->ass, from); break; } } @@ -518,7 +528,7 @@ FFmpegDecoder::decode_subtitle_packet () } void -FFmpegDecoder::decode_bitmap_subtitle (AVSubtitleRect const * rect, ContentTimePeriod period) +FFmpegDecoder::decode_bitmap_subtitle (AVSubtitleRect const * rect, ContentTime from) { /* Note RGBA is expressed little-endian, so the first byte in the word is R, second G, third B, fourth A. @@ -587,11 +597,11 @@ FFmpegDecoder::decode_bitmap_subtitle (AVSubtitleRect const * rect, ContentTimeP static_cast (rect->h) / target_height ); - subtitle->emit_image (period, image, scaled_rect); + subtitle->emit_image_start (from, image, scaled_rect); } void -FFmpegDecoder::decode_ass_subtitle (string ass, ContentTimePeriod period) +FFmpegDecoder::decode_ass_subtitle (string ass, ContentTime from) { /* We have no styles and no Format: line, so I'm assuming that FFmpeg produces a single format of Dialogue: lines... @@ -607,6 +617,6 @@ FFmpegDecoder::decode_ass_subtitle (string ass, ContentTimePeriod period) list raw = sub::SSAReader::parse_line (base, bits[9]); BOOST_FOREACH (sub::Subtitle const & i, sub::collect > (raw)) { - subtitle->emit_text (period, i); + subtitle->emit_text_start (from, i); } } diff --git a/src/lib/ffmpeg_decoder.h b/src/lib/ffmpeg_decoder.h index 65e4cf46a..bd7ba98b8 100644 --- a/src/lib/ffmpeg_decoder.h +++ b/src/lib/ffmpeg_decoder.h @@ -61,8 +61,8 @@ private: void decode_audio_packet (); void decode_subtitle_packet (); - void decode_bitmap_subtitle (AVSubtitleRect const * rect, ContentTimePeriod period); - void decode_ass_subtitle (std::string ass, ContentTimePeriod period); + void decode_bitmap_subtitle (AVSubtitleRect const * rect, ContentTime from); + void decode_ass_subtitle (std::string ass, ContentTime from); void maybe_add_subtitle (); boost::shared_ptr deinterleave_audio (boost::shared_ptr stream) const; @@ -73,4 +73,6 @@ private: boost::mutex _filter_graphs_mutex; ContentTime _pts_offset; + boost::optional _current_subtitle_to; + bool _have_current_subtitle; }; diff --git a/src/lib/ffmpeg_examiner.cc b/src/lib/ffmpeg_examiner.cc index eb02ba17b..a145ae25c 100644 --- a/src/lib/ffmpeg_examiner.cc +++ b/src/lib/ffmpeg_examiner.cc @@ -91,24 +91,13 @@ FFmpegExaminer::FFmpegExaminer (shared_ptr c, shared_ptrsub (_("Finding length and subtitles")); - } else if (!_subtitle_streams.empty()) { - job->sub (_("Finding subtitles")); - } else { - job->sub (_("Finding length")); - } + if (job && _need_video_length) { + job->sub (_("Finding length")); } /* Run through until we find: * - the first video. * - the first audio for each stream. - * - the subtitle periods for each stream. - * - * We have to note subtitle periods as otherwise we have no way of knowing - * where we should look for subtitles (video and audio are always present, - * so they are ok). */ int64_t const len = _file_group.length (); @@ -143,41 +132,13 @@ FFmpegExaminer::FFmpegExaminer (shared_ptr c, shared_ptruses_index (_format_context, _packet.stream_index)) { - subtitle_packet (context, _subtitle_streams[i]); - } - } - av_packet_unref (&_packet); - if (_first_video && got_all_audio && _subtitle_streams.empty ()) { + if (_first_video && got_all_audio) { /* All done */ break; } } - - /* Finish off any hanging subtitles at the end */ - for (LastSubtitleMap::const_iterator i = _last_subtitle_start.begin(); i != _last_subtitle_start.end(); ++i) { - if (i->second) { - if (i->first->unknown_to (i->second->id)) { - i->first->set_subtitle_to ( - i->second->id, - ContentTime::from_frames (video_length(), video_frame_rate().get_value_or (24)) - ); - } - } - } - - /* We just added subtitles to our streams without taking the PTS offset into account; - this is because we might not know the PTS offset when the first subtitle is seen. - Now we know the PTS offset so we can apply it to those subtitles. - */ - if (has_video() && video_frame_rate()) { - BOOST_FOREACH (shared_ptr i, _subtitle_streams) { - i->add_offset (pts_offset (_audio_streams, _first_video, video_frame_rate().get())); - } - } } void @@ -215,70 +176,6 @@ FFmpegExaminer::audio_packet (AVCodecContext* context, shared_ptr stream) -{ - int frame_finished; - AVSubtitle sub; - if (avcodec_decode_subtitle2 (context, &sub, &frame_finished, &_packet) >= 0 && frame_finished) { - string id = subtitle_id (sub); - FFmpegSubtitlePeriod const period = subtitle_period (sub); - bool const starts_image = subtitle_starts_image (sub); - - /* Some streams (notably DVB streams) have subtitles which have a specified end time - but which are then stopped earlier than this by a zero-num_rect subtitle. - */ - - LastSubtitleMap::iterator last = _last_subtitle_start.find (stream); - if (sub.num_rects == 0 && last != _last_subtitle_start.end() && last->second) { - /* Set (or fix) the `to' time for the last subtitle */ - stream->set_subtitle_to (last->second->id, period.from); - _last_subtitle_start[stream] = optional (); - } else if (sub.num_rects > 0) { - /* Add a subtitle; if we don't know the `to' time we set it to the from time and fix it later */ - if (starts_image) { - stream->add_image_subtitle (id, ContentTimePeriod (period.from, period.to.get_value_or (period.from))); - } else { - stream->add_text_subtitle (id, ContentTimePeriod (period.from, period.to.get_value_or (period.from))); - } - - _last_subtitle_start[stream] = SubtitleStart (id, starts_image, period.from); - } - - for (unsigned int i = 0; i < sub.num_rects; ++i) { - if (sub.rects[i]->type == SUBTITLE_BITMAP) { -#ifdef DCPOMATIC_HAVE_AVSUBTITLERECT_PICT - uint32_t* palette = (uint32_t *) sub.rects[i]->pict.data[1]; - for (int j = 0; j < sub.rects[i]->nb_colors; ++j) { - RGBA rgba ( - (palette[j] & 0x00ff0000) >> 16, - (palette[j] & 0x0000ff00) >> 8, - (palette[j] & 0x000000ff) >> 0, - (palette[j] & 0xff000000) >> 24 - ); - - stream->set_colour (rgba, rgba); - } -#else - uint32_t* palette = (uint32_t *) sub.rects[i]->data[1]; - for (int j = 0; j < sub.rects[i]->nb_colors; ++j) { - RGBA rgba ( - (palette[j] & 0x00ff0000) >> 16, - (palette[j] & 0x0000ff00) >> 8, - (palette[j] & 0x000000ff) >> 0, - (palette[j] & 0xff000000) >> 24 - ); - - stream->set_colour (rgba, rgba); - } -#endif - } - } - - avsubtitle_free (&sub); - } -} - optional FFmpegExaminer::frame_time (AVStream* s) const { diff --git a/src/lib/ffmpeg_examiner.h b/src/lib/ffmpeg_examiner.h index c3dde84fe..a511a2fc1 100644 --- a/src/lib/ffmpeg_examiner.h +++ b/src/lib/ffmpeg_examiner.h @@ -1,5 +1,5 @@ /* - Copyright (C) 2013-2016 Carl Hetherington + Copyright (C) 2013-2017 Carl Hetherington This file is part of DCP-o-matic. @@ -74,7 +74,6 @@ public: private: void video_packet (AVCodecContext *); void audio_packet (AVCodecContext *, boost::shared_ptr); - void subtitle_packet (AVCodecContext *, boost::shared_ptr); std::string stream_name (AVStream* s) const; std::string subtitle_stream_name (AVStream* s) const; diff --git a/src/lib/ffmpeg_subtitle_stream.cc b/src/lib/ffmpeg_subtitle_stream.cc index 3935e23a9..c3c6c1f86 100644 --- a/src/lib/ffmpeg_subtitle_stream.cc +++ b/src/lib/ffmpeg_subtitle_stream.cc @@ -1,5 +1,5 @@ /* - Copyright (C) 2013-2016 Carl Hetherington + Copyright (C) 2013-2017 Carl Hetherington This file is part of DCP-o-matic. @@ -38,54 +38,7 @@ using dcp::raw_convert; FFmpegSubtitleStream::FFmpegSubtitleStream (cxml::ConstNodePtr node, int version) : FFmpegStream (node) { - if (version == 32) { - BOOST_FOREACH (cxml::NodePtr i, node->node_children ("Period")) { - /* In version 32 we assumed that from times were unique, so they were - used as identifiers. All subtitles were image subtitles. - */ - add_image_subtitle ( - raw_convert (i->string_child ("From")), - ContentTimePeriod ( - ContentTime (i->number_child ("From")), - ContentTime (i->number_child ("To")) - ) - ); - } - } else { - /* In version 33 we use a hash of various parts of the subtitle as the id. - was initially used for image subtitles; later we have - and - */ - BOOST_FOREACH (cxml::NodePtr i, node->node_children ("Subtitle")) { - add_image_subtitle ( - raw_convert (i->string_child ("Id")), - ContentTimePeriod ( - ContentTime (i->number_child ("From")), - ContentTime (i->number_child ("To")) - ) - ); - } - - BOOST_FOREACH (cxml::NodePtr i, node->node_children ("ImageSubtitle")) { - add_image_subtitle ( - raw_convert (i->string_child ("Id")), - ContentTimePeriod ( - ContentTime (i->number_child ("From")), - ContentTime (i->number_child ("To")) - ) - ); - } - - BOOST_FOREACH (cxml::NodePtr i, node->node_children ("TextSubtitle")) { - add_text_subtitle ( - raw_convert (i->string_child ("Id")), - ContentTimePeriod ( - ContentTime (i->number_child ("From")), - ContentTime (i->number_child ("To")) - ) - ); - } - + if (version >= 33) { BOOST_FOREACH (cxml::NodePtr i, node->node_children ("Colour")) { _colours[RGBA(i->node_child("From"))] = RGBA (i->node_child("To")); } @@ -97,9 +50,6 @@ FFmpegSubtitleStream::as_xml (xmlpp::Node* root) const { FFmpegStream::as_xml (root); - as_xml (root, _image_subtitles, "ImageSubtitle"); - as_xml (root, _text_subtitles, "TextSubtitle"); - for (map::const_iterator i = _colours.begin(); i != _colours.end(); ++i) { xmlpp::Node* node = root->add_child("Colour"); i->first.as_xml (node->add_child("From")); @@ -107,76 +57,6 @@ FFmpegSubtitleStream::as_xml (xmlpp::Node* root) const } } -void -FFmpegSubtitleStream::as_xml (xmlpp::Node* root, PeriodMap const & subs, string node_name) const -{ - for (PeriodMap::const_iterator i = subs.begin(); i != subs.end(); ++i) { - xmlpp::Node* node = root->add_child (node_name); - node->add_child("Id")->add_child_text (i->first); - node->add_child("From")->add_child_text (raw_convert (i->second.from.get ())); - node->add_child("To")->add_child_text (raw_convert (i->second.to.get ())); - } -} - -void -FFmpegSubtitleStream::add_image_subtitle (string id, ContentTimePeriod period) -{ - DCPOMATIC_ASSERT (_image_subtitles.find (id) == _image_subtitles.end ()); - _image_subtitles[id] = period; -} - -void -FFmpegSubtitleStream::add_text_subtitle (string id, ContentTimePeriod period) -{ - DCPOMATIC_ASSERT (_text_subtitles.find (id) == _text_subtitles.end ()); - _text_subtitles[id] = period; -} - -ContentTime -FFmpegSubtitleStream::find_subtitle_to (string id) const -{ - PeriodMap::const_iterator i = _image_subtitles.find (id); - if (i != _image_subtitles.end ()) { - return i->second.to; - } - - i = _text_subtitles.find (id); - DCPOMATIC_ASSERT (i != _text_subtitles.end ()); - return i->second.to; -} - -/** @param id Subtitle id. - * @return true if the `from' and `to' times for this id are equal, which indicates - * that the `to' time is unknown. - */ -bool -FFmpegSubtitleStream::unknown_to (string id) const -{ - PeriodMap::const_iterator i = _image_subtitles.find (id); - if (i != _image_subtitles.end ()) { - return i->second.from == i->second.to; - } - - i = _text_subtitles.find (id); - DCPOMATIC_ASSERT (i != _text_subtitles.end ()); - return i->second.from == i->second.to; -} - -/** Add some offset to all the times in the stream */ -void -FFmpegSubtitleStream::add_offset (ContentTime offset) -{ - for (PeriodMap::iterator i = _image_subtitles.begin(); i != _image_subtitles.end(); ++i) { - i->second.from += offset; - i->second.to += offset; - } - - for (PeriodMap::iterator i = _text_subtitles.begin(); i != _text_subtitles.end(); ++i) { - i->second.from += offset; - i->second.to += offset; - } -} - map FFmpegSubtitleStream::colours () const { @@ -188,31 +68,3 @@ FFmpegSubtitleStream::set_colour (RGBA from, RGBA to) { _colours[from] = to; } - -bool -FFmpegSubtitleStream::has_text () const -{ - return !_text_subtitles.empty (); -} - -bool -FFmpegSubtitleStream::has_image () const -{ - return !_image_subtitles.empty (); -} - -void -FFmpegSubtitleStream::set_subtitle_to (string id, ContentTime to) -{ - PeriodMap::iterator i = _image_subtitles.find (id); - if (i != _image_subtitles.end ()) { - i->second.to = to; - } else { - i = _text_subtitles.find (id); - if (i != _text_subtitles.end ()) { - i->second.to = to; - } else { - DCPOMATIC_ASSERT (false); - } - } -} diff --git a/src/lib/ffmpeg_subtitle_stream.h b/src/lib/ffmpeg_subtitle_stream.h index 8b0274a5d..064c72f8d 100644 --- a/src/lib/ffmpeg_subtitle_stream.h +++ b/src/lib/ffmpeg_subtitle_stream.h @@ -1,5 +1,5 @@ /* - Copyright (C) 2013-2016 Carl Hetherington + Copyright (C) 2013-2017 Carl Hetherington This file is part of DCP-o-matic. @@ -34,25 +34,9 @@ public: void as_xml (xmlpp::Node *) const; - void add_image_subtitle (std::string id, ContentTimePeriod period); - void add_text_subtitle (std::string id, ContentTimePeriod period); - void set_subtitle_to (std::string id, ContentTime to); - ContentTime find_subtitle_to (std::string id) const; - bool unknown_to (std::string id) const; - void add_offset (ContentTime offset); void set_colour (RGBA from, RGBA to); std::map colours () const; - bool has_text () const; - bool has_image () const; - private: - - typedef std::map PeriodMap; - - void as_xml (xmlpp::Node *, PeriodMap const & subs, std::string node) const; - - PeriodMap _image_subtitles; - PeriodMap _text_subtitles; std::map _colours; }; diff --git a/src/lib/player.cc b/src/lib/player.cc index a090b7c26..72922d70a 100644 --- a/src/lib/player.cc +++ b/src/lib/player.cc @@ -143,8 +143,9 @@ Player::setup_pieces () } if (decoder->subtitle) { - decoder->subtitle->ImageData.connect (bind (&Player::image_subtitle, this, weak_ptr (piece), _1)); - decoder->subtitle->TextData.connect (bind (&Player::text_subtitle, this, weak_ptr (piece), _1)); + decoder->subtitle->ImageStart.connect (bind (&Player::image_subtitle_start, this, weak_ptr (piece), _1)); + decoder->subtitle->TextStart.connect (bind (&Player::text_subtitle_start, this, weak_ptr (piece), _1)); + decoder->subtitle->Stop.connect (bind (&Player::subtitle_stop, this, weak_ptr (piece), _1)); } } @@ -632,21 +633,28 @@ Player::video (weak_ptr wp, ContentVideo video) optional subtitles; - for (list >::const_iterator i = _subtitles.begin(); i != _subtitles.end(); ++i) { + for (ActiveSubtitles::const_iterator i = _active_subtitles.begin(); i != _active_subtitles.end(); ++i) { - if (!i->second.overlap (period)) { + shared_ptr sub_piece = i->first.lock (); + if (!sub_piece) { continue; } + if (!sub_piece->content->subtitle->use() || (!_always_burn_subtitles && !piece->content->subtitle->burn())) { + continue; + } + + pair sub = i->second; + list sub_images; /* Image subtitles */ - list c = transform_image_subtitles (i->first.image); + list c = transform_image_subtitles (sub.first.image); copy (c.begin(), c.end(), back_inserter (sub_images)); /* Text subtitles (rendered to an image) */ - if (!i->first.text.empty ()) { - list s = render_subtitles (i->first.text, i->first.fonts, _video_container_size, time); + if (!sub.first.text.empty ()) { + list s = render_subtitles (sub.first.text, sub.first.fonts, _video_container_size, time); copy (s.begin (), s.end (), back_inserter (sub_images)); } @@ -683,19 +691,6 @@ Player::video (weak_ptr wp, ContentVideo video) Video (_last_video, time); _last_video_time = time + one_video_frame (); - - /* Discard any subtitles we no longer need */ - - for (list >::iterator i = _subtitles.begin (); i != _subtitles.end(); ) { - list >::iterator tmp = i; - ++tmp; - - if (i->second.to < time) { - _subtitles.erase (i); - } - - i = tmp; - } } void @@ -825,7 +820,7 @@ Player::audio (weak_ptr wp, AudioStreamPtr stream, ContentAudio content_a } void -Player::image_subtitle (weak_ptr wp, ContentImageSubtitle subtitle) +Player::image_subtitle_start (weak_ptr wp, ContentImageSubtitle subtitle) { shared_ptr piece = wp.lock (); if (!piece) { @@ -846,17 +841,13 @@ Player::image_subtitle (weak_ptr wp, ContentImageSubtitle subtitle) PlayerSubtitles ps; ps.image.push_back (subtitle.sub); - DCPTimePeriod period (content_time_to_dcp (piece, subtitle.period().from), content_time_to_dcp (piece, subtitle.period().to)); + DCPTime from (content_time_to_dcp (piece, subtitle.from())); - if (piece->content->subtitle->use() && (piece->content->subtitle->burn() || _always_burn_subtitles)) { - _subtitles.push_back (make_pair (ps, period)); - } else { - Subtitle (ps, period); - } + _active_subtitles[wp] = make_pair (ps, from); } void -Player::text_subtitle (weak_ptr wp, ContentTextSubtitle subtitle) +Player::text_subtitle_start (weak_ptr wp, ContentTextSubtitle subtitle) { shared_ptr piece = wp.lock (); if (!piece) { @@ -864,7 +855,7 @@ Player::text_subtitle (weak_ptr wp, ContentTextSubtitle subtitle) } PlayerSubtitles ps; - DCPTimePeriod const period (content_time_to_dcp (piece, subtitle.period().from), content_time_to_dcp (piece, subtitle.period().to)); + DCPTime const from (content_time_to_dcp (piece, subtitle.from())); BOOST_FOREACH (dcp::SubtitleString s, subtitle.subs) { s.set_h_position (s.h_position() + piece->content->subtitle->x_offset ()); @@ -886,17 +877,31 @@ Player::text_subtitle (weak_ptr wp, ContentTextSubtitle subtitle) s.set_aspect_adjust (xs / ys); } - s.set_in (dcp::Time(period.from.seconds(), 1000)); - s.set_out (dcp::Time(period.to.seconds(), 1000)); + s.set_in (dcp::Time(from.seconds(), 1000)); ps.text.push_back (SubtitleString (s, piece->content->subtitle->outline_width())); ps.add_fonts (piece->content->subtitle->fonts ()); } - if (piece->content->subtitle->use() && (piece->content->subtitle->burn() || _always_burn_subtitles)) { - _subtitles.push_back (make_pair (ps, period)); - } else { - Subtitle (ps, period); + _active_subtitles[wp] = make_pair (ps, from); +} + +void +Player::subtitle_stop (weak_ptr wp, ContentTime to) +{ + if (_active_subtitles.find (wp) == _active_subtitles.end ()) { + return; } + + shared_ptr piece = wp.lock (); + if (!piece) { + return; + } + + if (piece->content->subtitle->use() && !_always_burn_subtitles && !piece->content->subtitle->burn()) { + Subtitle (_active_subtitles[wp].first, DCPTimePeriod (_active_subtitles[wp].second, content_time_to_dcp (piece, to))); + } + + _active_subtitles.erase (wp); } void @@ -912,6 +917,7 @@ Player::seek (DCPTime time, bool accurate) } _audio_merger.clear (); + _active_subtitles.clear (); BOOST_FOREACH (shared_ptr i, _pieces) { i->done = false; diff --git a/src/lib/player.h b/src/lib/player.h index 684de53f5..eb6589b56 100644 --- a/src/lib/player.h +++ b/src/lib/player.h @@ -78,6 +78,9 @@ public: boost::signals2::signal, DCPTime)> Video; boost::signals2::signal, DCPTime)> Audio; + /** Emitted when a subtitle is ready. This signal may be emitted considerably + * after the corresponding Video. + */ boost::signals2::signal Subtitle; private: @@ -104,15 +107,18 @@ private: std::list > overlaps (DCPTime from, DCPTime to, boost::function valid); void video (boost::weak_ptr, ContentVideo); void audio (boost::weak_ptr, AudioStreamPtr, ContentAudio); - void image_subtitle (boost::weak_ptr, ContentImageSubtitle); - void text_subtitle (boost::weak_ptr, ContentTextSubtitle); + void image_subtitle_start (boost::weak_ptr, ContentImageSubtitle); + void text_subtitle_start (boost::weak_ptr, ContentTextSubtitle); + void subtitle_stop (boost::weak_ptr, ContentTime); boost::shared_ptr resampler (boost::shared_ptr content, AudioStreamPtr stream, bool create); DCPTime one_video_frame () const; void fill_video (DCPTimePeriod period); void fill_audio (DCPTimePeriod period); void audio_flush (boost::shared_ptr, AudioStreamPtr stream); void audio_transform (boost::shared_ptr content, AudioStreamPtr stream, ContentAudio content_audio, DCPTime time); - std::pair, DCPTime> discard_audio (boost::shared_ptr audio, DCPTime time, DCPTime discard_to) const; + std::pair, DCPTime> discard_audio ( + boost::shared_ptr audio, DCPTime time, DCPTime discard_to + ) const; boost::shared_ptr _film; boost::shared_ptr _playlist; @@ -164,7 +170,8 @@ private: std::list _no_video; std::list _no_audio; - std::list > _subtitles; + typedef std::map, std::pair > ActiveSubtitles; + ActiveSubtitles _active_subtitles; boost::shared_ptr _audio_processor; typedef std::map, AudioStreamPtr>, boost::shared_ptr > ResamplerMap; diff --git a/src/lib/subtitle_decoder.cc b/src/lib/subtitle_decoder.cc index 2a9434370..43ee4c457 100644 --- a/src/lib/subtitle_decoder.cc +++ b/src/lib/subtitle_decoder.cc @@ -1,5 +1,5 @@ /* - Copyright (C) 2013-2016 Carl Hetherington + Copyright (C) 2013-2017 Carl Hetherington This file is part of DCP-o-matic. @@ -48,21 +48,21 @@ SubtitleDecoder::SubtitleDecoder ( } -/** Called by subclasses when an image subtitle is ready. - * @param period Period of the subtitle. +/** Called by subclasses when an image subtitle is starting. + * @param from From time of the subtitle. * @param image Subtitle image. * @param rect Area expressed as a fraction of the video frame that this subtitle * is for (e.g. a width of 0.5 means the width of the subtitle is half the width * of the video frame) */ void -SubtitleDecoder::emit_image (ContentTimePeriod period, shared_ptr image, dcpomatic::Rect rect) +SubtitleDecoder::emit_image_start (ContentTime from, shared_ptr image, dcpomatic::Rect rect) { - ImageData (ContentImageSubtitle (period, image, rect)); + ImageStart (ContentImageSubtitle (from, image, rect)); } void -SubtitleDecoder::emit_text (ContentTimePeriod period, list s) +SubtitleDecoder::emit_text_start (ContentTime from, list s) { /* We must escape < and > in strings, otherwise they might confuse our subtitle renderer (which uses some HTML-esque markup to do bold/italic etc.) @@ -74,12 +74,12 @@ SubtitleDecoder::emit_text (ContentTimePeriod period, list i.set_text (t); } - TextData (ContentTextSubtitle (period, s)); - _position = period.from; + TextStart (ContentTextSubtitle (from, s)); + _position = from; } void -SubtitleDecoder::emit_text (ContentTimePeriod period, sub::Subtitle const & subtitle) +SubtitleDecoder::emit_text_start (ContentTime from, sub::Subtitle const & subtitle) { /* See if our next subtitle needs to be placed on screen by us */ bool needs_placement = false; @@ -170,8 +170,9 @@ SubtitleDecoder::emit_text (ContentTimePeriod period, sub::Subtitle const & subt content()->colour(), j.font_size.points (72 * 11), 1.0, - dcp::Time (period.from.seconds(), 1000), - dcp::Time (period.to.seconds(), 1000), + dcp::Time (from.seconds(), 1000), + /* XXX: hmm; this is a bit ugly (we don't know the to time yet) */ + dcp::Time (), 0, dcp::HALIGN_CENTER, v_position, @@ -187,5 +188,25 @@ SubtitleDecoder::emit_text (ContentTimePeriod period, sub::Subtitle const & subt } } - emit_text (period, out); + emit_text_start (from, out); +} + +void +SubtitleDecoder::emit_stop (ContentTime to) +{ + Stop (to); +} + +void +SubtitleDecoder::emit_text (ContentTimePeriod period, list s) +{ + emit_text_start (period.from, s); + emit_stop (period.to); +} + +void +SubtitleDecoder::emit_text (ContentTimePeriod period, sub::Subtitle const & s) +{ + emit_text_start (period.from, s); + emit_stop (period.to); } diff --git a/src/lib/subtitle_decoder.h b/src/lib/subtitle_decoder.h index 6d0479638..92a6266de 100644 --- a/src/lib/subtitle_decoder.h +++ b/src/lib/subtitle_decoder.h @@ -1,5 +1,5 @@ /* - Copyright (C) 2013-2016 Carl Hetherington + Copyright (C) 2013-2017 Carl Hetherington This file is part of DCP-o-matic. @@ -52,16 +52,20 @@ public: return _position; } - void emit_image (ContentTimePeriod period, boost::shared_ptr image, dcpomatic::Rect rect); + void emit_image_start (ContentTime from, boost::shared_ptr image, dcpomatic::Rect rect); + void emit_text_start (ContentTime from, std::list s); + void emit_text_start (ContentTime from, sub::Subtitle const & subtitle); void emit_text (ContentTimePeriod period, std::list s); void emit_text (ContentTimePeriod period, sub::Subtitle const & subtitle); + void emit_stop (ContentTime to); boost::shared_ptr content () const { return _content; } - boost::signals2::signal ImageData; - boost::signals2::signal TextData; + boost::signals2::signal ImageStart; + boost::signals2::signal TextStart; + boost::signals2::signal Stop; private: boost::shared_ptr _content; diff --git a/src/wx/image_subtitle_colour_dialog.cc b/src/wx/image_subtitle_colour_dialog.cc deleted file mode 100644 index 0d102f239..000000000 --- a/src/wx/image_subtitle_colour_dialog.cc +++ /dev/null @@ -1,96 +0,0 @@ -/* - Copyright (C) 2016 Carl Hetherington - - 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 . - -*/ - -#include "image_subtitle_colour_dialog.h" -#include "rgba_colour_picker.h" -#include "wx_util.h" -#include "lib/ffmpeg_subtitle_stream.h" -#include "lib/ffmpeg_content.h" - -using std::map; -using std::cout; -using boost::shared_ptr; -using boost::bind; - -ImageSubtitleColourDialog::ImageSubtitleColourDialog (wxWindow* parent, shared_ptr content, shared_ptr stream) - : wxDialog (parent, wxID_ANY, _("Subtitle colours")) - , _content (content) - , _stream (stream) -{ - wxBoxSizer* overall_sizer = new wxBoxSizer (wxVERTICAL); - SetSizer (overall_sizer); - - wxScrolled* colours_panel = new wxScrolled (this); - colours_panel->EnableScrolling (false, true); - colours_panel->ShowScrollbars (wxSHOW_SB_NEVER, wxSHOW_SB_ALWAYS); - colours_panel->SetScrollRate (0, 16); - - wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP); - table->AddGrowableCol (1, 1); - - map colours = _stream->colours (); - - wxStaticText* t = new wxStaticText (colours_panel, wxID_ANY, ""); - t->SetLabelMarkup (_("Original colour")); - table->Add (t, 1, wxEXPAND); - t = new wxStaticText (colours_panel, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxALIGN_CENTRE_HORIZONTAL); - t->SetLabelMarkup (_("New colour")); - table->Add (t, 1, wxALIGN_CENTER); - - for (map::const_iterator i = colours.begin(); i != colours.end(); ++i) { - wxPanel* from = new wxPanel (colours_panel, wxID_ANY); - from->SetBackgroundColour (wxColour (i->first.r, i->first.g, i->first.b, i->first.a)); - table->Add (from, 1, wxEXPAND); - RGBAColourPicker* to = new RGBAColourPicker (colours_panel, i->second); - table->Add (to, 1, wxEXPAND); - _pickers[i->first] = to; - } - - colours_panel->SetSizer (table); - - overall_sizer->Add (colours_panel, 1, wxEXPAND | wxALL, DCPOMATIC_DIALOG_BORDER); - - wxButton* restore = new wxButton (this, wxID_ANY, _("Restore to original colours")); - restore->Bind (wxEVT_BUTTON, bind (&ImageSubtitleColourDialog::restore, this)); - overall_sizer->Add (restore, 0, wxALL, DCPOMATIC_SIZER_X_GAP); - - wxSizer* buttons = CreateSeparatedButtonSizer (wxOK | wxCANCEL); - if (buttons) { - overall_sizer->Add (buttons, wxSizerFlags().Expand().DoubleBorder()); - } -} - -void -ImageSubtitleColourDialog::apply () -{ - for (map::const_iterator i = _pickers.begin(); i != _pickers.end(); ++i) { - _stream->set_colour (i->first, i->second->colour ()); - } - - _content->signal_subtitle_stream_changed (); -} - -void -ImageSubtitleColourDialog::restore () -{ - for (map::const_iterator i = _pickers.begin(); i != _pickers.end(); ++i) { - i->second->set (i->first); - } -} diff --git a/src/wx/image_subtitle_colour_dialog.h b/src/wx/image_subtitle_colour_dialog.h deleted file mode 100644 index 133c3b22b..000000000 --- a/src/wx/image_subtitle_colour_dialog.h +++ /dev/null @@ -1,42 +0,0 @@ -/* - Copyright (C) 2016 Carl Hetherington - - 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 . - -*/ - -#include "table_dialog.h" -#include "lib/rgba.h" -#include - -class RGBAColourPicker; -class FFmpegContent; -class FFmpegSubtitleStream; - -class ImageSubtitleColourDialog : public wxDialog -{ -public: - ImageSubtitleColourDialog (wxWindow* parent, boost::shared_ptr content, boost::shared_ptr stream); - - void apply (); - -private: - void restore (); - - boost::shared_ptr _content; - boost::shared_ptr _stream; - std::map _pickers; -}; diff --git a/src/wx/subtitle_panel.cc b/src/wx/subtitle_panel.cc index 810cdf488..845b82198 100644 --- a/src/wx/subtitle_panel.cc +++ b/src/wx/subtitle_panel.cc @@ -24,8 +24,7 @@ #include "subtitle_view.h" #include "content_panel.h" #include "fonts_dialog.h" -#include "text_subtitle_appearance_dialog.h" -#include "image_subtitle_colour_dialog.h" +#include "subtitle_appearance_dialog.h" #include "lib/ffmpeg_content.h" #include "lib/text_subtitle_content.h" #include "lib/ffmpeg_subtitle_stream.h" @@ -251,8 +250,6 @@ SubtitlePanel::setup_sensitivity () { int any_subs = 0; int ffmpeg_subs = 0; - int text_subs = 0; - int image_subs = 0; ContentList sel = _parent->selected_subtitle (); BOOST_FOREACH (shared_ptr i, sel) { /* These are the content types that could include subtitles */ @@ -262,30 +259,15 @@ SubtitlePanel::setup_sensitivity () shared_ptr dsc = boost::dynamic_pointer_cast (i); if (fc) { if (fc->subtitle) { - DCPOMATIC_ASSERT (fc->subtitle_stream()); - /* This content has some subtitles; check the selected stream to decide what type */ - if (fc->subtitle_stream()->has_text()) { - ++text_subs; - } else if (fc->subtitle_stream()->has_image()) { - ++image_subs; - } ++ffmpeg_subs; ++any_subs; } } else if (sc || dc || dsc) { /* XXX: in the future there could be bitmap subs from DCPs */ - ++text_subs; ++any_subs; } } - if (image_subs) { - BOOST_FOREACH (shared_ptr i, sel) { - /* We must burn image subtitles at the moment */ - i->subtitle->set_burn (true); - } - } - /* Decide whether we can reference these subs */ shared_ptr dcp; @@ -302,16 +284,16 @@ SubtitlePanel::setup_sensitivity () /* Set up sensitivity */ _use->Enable (!reference && any_subs > 0); bool const use = _use->GetValue (); - _burn->Enable (!reference && any_subs > 0 && use && image_subs == 0); + _burn->Enable (!reference && any_subs > 0 && use); _x_offset->Enable (!reference && any_subs > 0 && use); _y_offset->Enable (!reference && any_subs > 0 && use); _x_scale->Enable (!reference && any_subs > 0 && use); _y_scale->Enable (!reference && any_subs > 0 && use); - _line_spacing->Enable (!reference && text_subs > 0 && use); + _line_spacing->Enable (!reference && use); _language->Enable (!reference && any_subs > 0 && use); _stream->Enable (!reference && ffmpeg_subs == 1); - _subtitle_view_button->Enable (!reference && text_subs == 1); - _fonts_dialog_button->Enable (!reference && text_subs == 1); + _subtitle_view_button->Enable (!reference); + _fonts_dialog_button->Enable (!reference); _appearance_dialog_button->Enable (!reference && any_subs > 0 && use); } @@ -458,38 +440,9 @@ SubtitlePanel::appearance_dialog_clicked () ContentList c = _parent->selected_subtitle (); DCPOMATIC_ASSERT (c.size() == 1); - bool text = false; - bool image = false; - - if ( - dynamic_pointer_cast (c.front()) || - dynamic_pointer_cast (c.front()) || - dynamic_pointer_cast (c.front())) { - - text = true; - } - - shared_ptr fc = dynamic_pointer_cast (c.front()); - if (fc) { - if (fc->subtitle_stream()->has_text()) { - text = true; - } else if (fc->subtitle_stream()->has_image()) { - image = true; - } - } - - if (text) { - TextSubtitleAppearanceDialog* d = new TextSubtitleAppearanceDialog (this, c.front()); - if (d->ShowModal () == wxID_OK) { - d->apply (); - } - d->Destroy (); - } else if (image) { - DCPOMATIC_ASSERT (fc); - ImageSubtitleColourDialog* d = new ImageSubtitleColourDialog (this, fc, fc->subtitle_stream ()); - if (d->ShowModal() == wxID_OK) { - d->apply (); - } - d->Destroy (); + SubtitleAppearanceDialog* d = new SubtitleAppearanceDialog (this, c.front()); + if (d->ShowModal () == wxID_OK) { + d->apply (); } + d->Destroy (); } diff --git a/src/wx/subtitle_view.cc b/src/wx/subtitle_view.cc index 7c66f1db2..bb336d16b 100644 --- a/src/wx/subtitle_view.cc +++ b/src/wx/subtitle_view.cc @@ -78,22 +78,35 @@ SubtitleView::SubtitleView (wxWindow* parent, shared_ptr film, shared_ptr< _subs = 0; _frc = film->active_frame_rate_change (position); - decoder->subtitle->TextData.connect (bind (&SubtitleView::data, this, _1)); + decoder->subtitle->TextStart.connect (bind (&SubtitleView::data_start, this, _1)); + decoder->subtitle->Stop.connect (bind (&SubtitleView::data_stop, this, _1)); while (!decoder->pass ()) {} SetSizerAndFit (sizer); } void -SubtitleView::data (ContentTextSubtitle cts) +SubtitleView::data_start (ContentTextSubtitle cts) { - for (list::const_iterator i = cts.subs.begin(); i != cts.subs.end(); ++i) { + BOOST_FOREACH (dcp::SubtitleString const & i, cts.subs) { wxListItem list_item; list_item.SetId (_subs); _list->InsertItem (list_item); - ContentTimePeriod const p = cts.period (); - _list->SetItem (_subs, 0, std_to_wx (p.from.timecode (_frc->source))); - _list->SetItem (_subs, 1, std_to_wx (p.to.timecode (_frc->source))); - _list->SetItem (_subs, 2, std_to_wx (i->text ())); + _list->SetItem (_subs, 0, std_to_wx (cts.from().timecode (_frc->source))); + _list->SetItem (_subs, 2, std_to_wx (i.text ())); ++_subs; } + + _last_count = cts.subs.size (); +} + +void +SubtitleView::data_stop (ContentTime time) +{ + if (!_last_count) { + return; + } + + for (int i = _subs - *_last_count; i < _subs; ++i) { + _list->SetItem (i, 1, std_to_wx (time.timecode (_frc->source))); + } } diff --git a/src/wx/subtitle_view.h b/src/wx/subtitle_view.h index f88bb490f..43a622705 100644 --- a/src/wx/subtitle_view.h +++ b/src/wx/subtitle_view.h @@ -31,9 +31,11 @@ public: SubtitleView (wxWindow *, boost::shared_ptr, boost::shared_ptr, DCPTime position); private: - void data (ContentTextSubtitle cts); + void data_start (ContentTextSubtitle cts); + void data_stop (ContentTime time); wxListCtrl* _list; int _subs; boost::optional _frc; + boost::optional _last_count; }; diff --git a/src/wx/text_subtitle_appearance_dialog.cc b/src/wx/text_subtitle_appearance_dialog.cc deleted file mode 100644 index 6f47d66eb..000000000 --- a/src/wx/text_subtitle_appearance_dialog.cc +++ /dev/null @@ -1,139 +0,0 @@ -/* - Copyright (C) 2015-2016 Carl Hetherington - - 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 . - -*/ - -#include "text_subtitle_appearance_dialog.h" -#include "lib/text_subtitle_content.h" -#include "lib/subtitle_content.h" -#include -#include -#include -#include - -using boost::shared_ptr; -using boost::bind; - -int const TextSubtitleAppearanceDialog::NONE = 0; -int const TextSubtitleAppearanceDialog::OUTLINE = 1; -int const TextSubtitleAppearanceDialog::SHADOW = 2; - -TextSubtitleAppearanceDialog::TextSubtitleAppearanceDialog (wxWindow* parent, shared_ptr content) - : wxDialog (parent, wxID_ANY, _("Subtitle appearance")) - , _content (content) -{ - wxSizer* overall_sizer = new wxBoxSizer (wxVERTICAL); - SetSizer (overall_sizer); - - _table = new wxGridBagSizer (DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP); - - overall_sizer->Add (_table, 1, wxEXPAND | wxALL, DCPOMATIC_DIALOG_BORDER); - - int r = 0; - - add_label_to_sizer (_table, this, _("Colour"), true, wxGBPosition (r, 0)); - _colour = new wxColourPickerCtrl (this, wxID_ANY); - _table->Add (_colour, wxGBPosition (r, 1)); - ++r; - - add_label_to_sizer (_table, this, _("Effect"), true, wxGBPosition (r, 0)); - _effect = new wxChoice (this, wxID_ANY); - _table->Add (_effect, wxGBPosition (r, 1)); - ++r; - - add_label_to_sizer (_table, this, _("Effect colour"), true, wxGBPosition (r, 0)); - _effect_colour = new wxColourPickerCtrl (this, wxID_ANY); - _table->Add (_effect_colour, wxGBPosition (r, 1)); - ++r; - - add_label_to_sizer (_table, this, _("Outline width"), true, wxGBPosition (r, 0)); - _outline_width = new wxSpinCtrl (this, wxID_ANY); - _table->Add (_outline_width, wxGBPosition (r, 1)); - ++r; - - add_label_to_sizer (_table, this, _("Fade in time"), true, wxGBPosition (r, 0)); - _fade_in = new Timecode (this); - _table->Add (_fade_in, wxGBPosition (r, 1)); - ++r; - - add_label_to_sizer (_table, this, _("Fade out time"), true, wxGBPosition (r, 0)); - _fade_out = new Timecode (this); - _table->Add (_fade_out, wxGBPosition (r, 1)); - ++r; - - wxSizer* buttons = CreateSeparatedButtonSizer (wxOK); - if (buttons) { - overall_sizer->Add (buttons, wxSizerFlags().Expand().DoubleBorder()); - } - - overall_sizer->Layout (); - overall_sizer->SetSizeHints (this); - - /* Keep these Appends() up to date with NONE/OUTLINE/SHADOW variables */ - _effect->Append (_("None")); - _effect->Append (_("Outline")); - _effect->Append (_("Shadow"));; - - _colour->SetColour (wxColour (_content->subtitle->colour().r, _content->subtitle->colour().g, _content->subtitle->colour().b)); - if (_content->subtitle->outline()) { - _effect->SetSelection (OUTLINE); - } else if (_content->subtitle->shadow()) { - _effect->SetSelection (SHADOW); - } else { - _effect->SetSelection (NONE); - } - _effect_colour->SetColour ( - wxColour (_content->subtitle->effect_colour().r, _content->subtitle->effect_colour().g, _content->subtitle->effect_colour().b) - ); - _fade_in->set (_content->subtitle->fade_in(), _content->active_video_frame_rate ()); - _fade_out->set (_content->subtitle->fade_out(), _content->active_video_frame_rate ()); - _outline_width->SetValue (_content->subtitle->outline_width ()); - - _effect->Bind (wxEVT_CHOICE, bind (&TextSubtitleAppearanceDialog::setup_sensitivity, this)); - _content_connection = _content->Changed.connect (bind (&TextSubtitleAppearanceDialog::setup_sensitivity, this)); - - setup_sensitivity (); -} - -void -TextSubtitleAppearanceDialog::apply () -{ - wxColour const c = _colour->GetColour (); - _content->subtitle->set_colour (dcp::Colour (c.Red(), c.Green(), c.Blue())); - _content->subtitle->set_outline (_effect->GetSelection() == OUTLINE); - _content->subtitle->set_shadow (_effect->GetSelection() == SHADOW); - wxColour const ec = _effect_colour->GetColour (); - _content->subtitle->set_effect_colour (dcp::Colour (ec.Red(), ec.Green(), ec.Blue())); - _content->subtitle->set_fade_in (_fade_in->get (_content->active_video_frame_rate ())); - _content->subtitle->set_fade_out (_fade_out->get (_content->active_video_frame_rate ())); - _content->subtitle->set_outline_width (_outline_width->GetValue ()); -} - -void -TextSubtitleAppearanceDialog::setup_sensitivity () -{ - _effect_colour->Enable (_effect->GetSelection() != NONE); - - bool const can_outline_width = _effect->GetSelection() == OUTLINE && _content->subtitle->burn (); - _outline_width->Enable (can_outline_width); - if (can_outline_width) { - _outline_width->UnsetToolTip (); - } else { - _outline_width->SetToolTip (_("Outline width cannot be set unless you are burning in subtitles")); - } -} diff --git a/src/wx/text_subtitle_appearance_dialog.h b/src/wx/text_subtitle_appearance_dialog.h deleted file mode 100644 index 10169fb50..000000000 --- a/src/wx/text_subtitle_appearance_dialog.h +++ /dev/null @@ -1,56 +0,0 @@ -/* - Copyright (C) 2015-2016 Carl Hetherington - - 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 . - -*/ - -#include "timecode.h" -#include -#include -#include - -class wxRadioButton; -class wxColourPickerCtrl; -class wxGridBagSizer; -class Content; - -class TextSubtitleAppearanceDialog : public wxDialog -{ -public: - TextSubtitleAppearanceDialog (wxWindow* parent, boost::shared_ptr content); - - void apply (); - -private: - void setup_sensitivity (); - - wxColourPickerCtrl* _colour; - wxChoice* _effect; - wxColourPickerCtrl* _effect_colour; - Timecode* _fade_in; - Timecode* _fade_out; - wxSpinCtrl* _outline_width; - wxGridBagSizer* _table; - - boost::shared_ptr _content; - - boost::signals2::scoped_connection _content_connection; - - static int const NONE; - static int const OUTLINE; - static int const SHADOW; -}; diff --git a/src/wx/wscript b/src/wx/wscript index fc4dfc19a..87fb27610 100644 --- a/src/wx/wscript +++ b/src/wx/wscript @@ -44,7 +44,6 @@ sources = """ dcp_panel.cc email_dialog.cc image_sequence_dialog.cc - image_subtitle_colour_dialog.cc isdcf_metadata_dialog.cc dir_picker_ctrl.cc dolby_doremi_certificate_panel.cc @@ -84,12 +83,12 @@ sources = """ self_dkdm_dialog.cc server_dialog.cc servers_list_dialog.cc + subtitle_appearance_dialog.cc subtitle_panel.cc subtitle_view.cc system_font_dialog.cc table_dialog.cc templates_dialog.cc - text_subtitle_appearance_dialog.cc time_picker.cc timecode.cc timeline.cc diff --git a/test/dcp_subtitle_test.cc b/test/dcp_subtitle_test.cc index 92a415ae1..927b8be69 100644 --- a/test/dcp_subtitle_test.cc +++ b/test/dcp_subtitle_test.cc @@ -83,7 +83,7 @@ BOOST_AUTO_TEST_CASE (dcp_subtitle_within_dcp_test) wait_for_jobs (); shared_ptr decoder (new DCPDecoder (content, film->log())); - decoder->subtitle->TextData.connect (bind (store, _1)); + decoder->subtitle->TextStart.connect (bind (store, _1)); stored = optional (); while (!decoder->pass() && !stored) {} @@ -106,11 +106,11 @@ BOOST_AUTO_TEST_CASE (dcp_subtitle_test2) wait_for_jobs (); shared_ptr decoder (new DCPSubtitleDecoder (content, film->log())); - decoder->subtitle->TextData.connect (bind (store, _1)); + decoder->subtitle->TextStart.connect (bind (store, _1)); stored = optional (); while (!decoder->pass ()) { - if (stored && stored->period().from == ContentTime(0)) { + if (stored && stored->from() == ContentTime(0)) { BOOST_CHECK_EQUAL (stored->subs.front().text(), "<b>Hello world!</b>"); } } @@ -134,8 +134,8 @@ BOOST_AUTO_TEST_CASE (dcp_subtitle_test3) shared_ptr decoder (new DCPSubtitleDecoder (content, film->log())); stored = optional (); while (!decoder->pass ()) { - decoder->subtitle->TextData.connect (bind (store, _1)); - if (stored && stored->period().from == ContentTime::from_seconds(0.08)) { + decoder->subtitle->TextStart.connect (bind (store, _1)); + if (stored && stored->from() == ContentTime::from_seconds(0.08)) { list s = stored->subs; list::const_iterator i = s.begin (); BOOST_CHECK_EQUAL (i->text(), "This"); -- 2.30.2