From df17bbd25da69fc38eb2dcd8b4a2531cf0bab0bc Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Mon, 23 Jul 2018 11:20:12 +0100 Subject: More automated renaming. ActiveCaptions -> ActiveText BitmapCaption -> BitmapText ContentCaption -> ContentText ContentTextCaption -> ContentStringText TextCaptionFileContent -> StringTextFileContent TextCaptionFileDecoder -> StringTextFileDecoder TextCaptionFile -> StringTextFile TextCaption -> StringText PlayerCaption -> PlayerText CaptionContent -> TextContent CaptionDecoder -> TextDecoder CaptionPanel -> TextPanel CaptionView -> TextView CaptionAppearanceDialog -> SubtitleAppearanceDialog CaptionType -> TextType --- src/lib/active_captions.cc | 162 ---------- src/lib/active_captions.h | 68 ---- src/lib/active_text.cc | 162 ++++++++++ src/lib/active_text.h | 68 ++++ src/lib/bitmap_caption.h | 48 --- src/lib/bitmap_text.h | 48 +++ src/lib/caption_content.cc | 579 ----------------------------------- src/lib/caption_content.h | 222 -------------- src/lib/caption_decoder.cc | 257 ---------------- src/lib/caption_decoder.h | 74 ----- src/lib/content.cc | 18 +- src/lib/content.h | 6 +- src/lib/content_caption.h | 81 ----- src/lib/content_factory.cc | 6 +- src/lib/content_text.h | 81 +++++ src/lib/dcp_content.cc | 16 +- src/lib/dcp_content.h | 6 +- src/lib/dcp_decoder.cc | 10 +- src/lib/dcp_decoder.h | 2 +- src/lib/dcp_encoder.cc | 6 +- src/lib/dcp_encoder.h | 4 +- src/lib/dcp_examiner.h | 2 +- src/lib/dcp_subtitle_content.cc | 6 +- src/lib/dcp_subtitle_decoder.cc | 4 +- src/lib/dcp_subtitle_decoder.h | 2 +- src/lib/decoder.cc | 10 +- src/lib/decoder.h | 6 +- src/lib/decoder_factory.cc | 8 +- src/lib/encoder.h | 2 +- src/lib/ffmpeg_content.cc | 8 +- src/lib/ffmpeg_decoder.cc | 6 +- src/lib/ffmpeg_encoder.cc | 2 +- src/lib/ffmpeg_encoder.h | 2 +- src/lib/film.cc | 10 +- src/lib/hints.cc | 4 +- src/lib/player.cc | 72 ++--- src/lib/player.h | 20 +- src/lib/player_caption.cc | 42 --- src/lib/player_caption.h | 42 --- src/lib/player_text.cc | 42 +++ src/lib/player_text.h | 42 +++ src/lib/playlist.cc | 4 +- src/lib/reel_writer.cc | 6 +- src/lib/reel_writer.h | 4 +- src/lib/render_text.cc | 12 +- src/lib/render_text.h | 6 +- src/lib/string_text.h | 46 +++ src/lib/string_text_file.cc | 116 +++++++ src/lib/string_text_file.h | 53 ++++ src/lib/string_text_file_content.cc | 95 ++++++ src/lib/string_text_file_content.h | 46 +++ src/lib/string_text_file_decoder.cc | 89 ++++++ src/lib/string_text_file_decoder.h | 44 +++ src/lib/text_caption.h | 46 --- src/lib/text_caption_file.cc | 116 ------- src/lib/text_caption_file.h | 53 ---- src/lib/text_caption_file_content.cc | 95 ------ src/lib/text_caption_file_content.h | 46 --- src/lib/text_caption_file_decoder.cc | 89 ------ src/lib/text_caption_file_decoder.h | 44 --- src/lib/text_content.cc | 579 +++++++++++++++++++++++++++++++++++ src/lib/text_content.h | 222 ++++++++++++++ src/lib/text_decoder.cc | 257 ++++++++++++++++ src/lib/text_decoder.h | 74 +++++ src/lib/types.cc | 6 +- src/lib/types.h | 10 +- src/lib/writer.cc | 2 +- src/lib/writer.h | 4 +- src/lib/wscript | 14 +- 69 files changed, 2217 insertions(+), 2217 deletions(-) delete mode 100644 src/lib/active_captions.cc delete mode 100644 src/lib/active_captions.h create mode 100644 src/lib/active_text.cc create mode 100644 src/lib/active_text.h delete mode 100644 src/lib/bitmap_caption.h create mode 100644 src/lib/bitmap_text.h delete mode 100644 src/lib/caption_content.cc delete mode 100644 src/lib/caption_content.h delete mode 100644 src/lib/caption_decoder.cc delete mode 100644 src/lib/caption_decoder.h delete mode 100644 src/lib/content_caption.h create mode 100644 src/lib/content_text.h delete mode 100644 src/lib/player_caption.cc delete mode 100644 src/lib/player_caption.h create mode 100644 src/lib/player_text.cc create mode 100644 src/lib/player_text.h create mode 100644 src/lib/string_text.h create mode 100644 src/lib/string_text_file.cc create mode 100644 src/lib/string_text_file.h create mode 100644 src/lib/string_text_file_content.cc create mode 100644 src/lib/string_text_file_content.h create mode 100644 src/lib/string_text_file_decoder.cc create mode 100644 src/lib/string_text_file_decoder.h delete mode 100644 src/lib/text_caption.h delete mode 100644 src/lib/text_caption_file.cc delete mode 100644 src/lib/text_caption_file.h delete mode 100644 src/lib/text_caption_file_content.cc delete mode 100644 src/lib/text_caption_file_content.h delete mode 100644 src/lib/text_caption_file_decoder.cc delete mode 100644 src/lib/text_caption_file_decoder.h create mode 100644 src/lib/text_content.cc create mode 100644 src/lib/text_content.h create mode 100644 src/lib/text_decoder.cc create mode 100644 src/lib/text_decoder.h (limited to 'src/lib') diff --git a/src/lib/active_captions.cc b/src/lib/active_captions.cc deleted file mode 100644 index 1d3a53609..000000000 --- a/src/lib/active_captions.cc +++ /dev/null @@ -1,162 +0,0 @@ -/* - Copyright (C) 2017-2018 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 "active_captions.h" -#include "caption_content.h" -#include -#include - -using std::list; -using std::pair; -using std::make_pair; -using boost::weak_ptr; -using boost::shared_ptr; -using boost::optional; - -void -ActiveCaptions::add (DCPTimePeriod period, list& pc, list p) const -{ - BOOST_FOREACH (Period i, p) { - DCPTimePeriod test (i.from, i.to.get_value_or(DCPTime::max())); - optional overlap = period.overlap (test); - if (overlap && overlap->duration() > DCPTime(period.duration().get() / 2)) { - pc.push_back (i.subs); - } - } -} - -list -ActiveCaptions::get (DCPTimePeriod period) const -{ - list ps; - - for (Map::const_iterator i = _data.begin(); i != _data.end(); ++i) { - - shared_ptr 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. - */ -list -ActiveCaptions::get_burnt (DCPTimePeriod period, bool always_burn_captions) const -{ - list ps; - - for (Map::const_iterator i = _data.begin(); i != _data.end(); ++i) { - - shared_ptr caption = i->first.lock (); - if (!caption) { - continue; - } - - if (!caption->use() || (!always_burn_captions && !caption->burn())) { - /* Not burning this content */ - continue; - } - - add (period, ps, i->second); - } - - return ps; -} - -/** Remove subtitles that finish before a given time from our list. - * @param time Time to remove before. - */ -void -ActiveCaptions::clear_before (DCPTime time) -{ - Map updated; - for (Map::const_iterator i = _data.begin(); i != _data.end(); ++i) { - list as; - BOOST_FOREACH (Period j, i->second) { - if (!j.to || j.to.get() >= time) { - as.push_back (j); - } - } - if (!as.empty ()) { - updated[i->first] = as; - } - } - _data = updated; -} - -/** Add a new subtitle with a from time. - * @param content Content that the subtitle is from. - * @param ps Subtitles. - * @param from From time for these subtitles. - */ -void -ActiveCaptions::add_from (weak_ptr content, PlayerCaption ps, DCPTime from) -{ - if (_data.find(content) == _data.end()) { - _data[content] = list(); - } - _data[content].push_back (Period (ps, from)); -} - -/** Add the to time for the last subtitle added from a piece of content. - * @param content Content that the subtitle is from. - * @param to To time for the last subtitle submitted to add_from for this content. - * @return Return the corresponding subtitles and their from time. - */ -pair -ActiveCaptions::add_to (weak_ptr content, DCPTime to) -{ - DCPOMATIC_ASSERT (_data.find(content) != _data.end()); - - _data[content].back().to = to; - - BOOST_FOREACH (TextCaption& i, _data[content].back().subs.text) { - i.set_out (dcp::Time(to.seconds(), 1000)); - } - - return make_pair (_data[content].back().subs, _data[content].back().from); -} - -/** @param content Some content. - * @return true if we have any active subtitles from this content. - */ -bool -ActiveCaptions::have (weak_ptr content) const -{ - Map::const_iterator i = _data.find(content); - if (i == _data.end()) { - return false; - } - - return !i->second.empty(); -} - -void -ActiveCaptions::clear () -{ - _data.clear (); -} diff --git a/src/lib/active_captions.h b/src/lib/active_captions.h deleted file mode 100644 index 8e38564f5..000000000 --- a/src/lib/active_captions.h +++ /dev/null @@ -1,68 +0,0 @@ -/* - Copyright (C) 2017-2018 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 . - -*/ - -/** @file src/lib/active_captions.h - * @brief ActiveCaptions class. - */ - -#include "dcpomatic_time.h" -#include "player_caption.h" -#include -#include -#include - -class CaptionContent; - -/** @class ActiveCaptions - * @brief A class to maintain information on active subtitles for Player. - */ -class ActiveCaptions : public boost::noncopyable -{ -public: - std::list get (DCPTimePeriod period) 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 content, PlayerCaption ps, DCPTime from); - std::pair add_to (boost::weak_ptr content, DCPTime to); - bool have (boost::weak_ptr content) const; - -private: - class Period - { - public: - Period () {} - - Period (PlayerCaption s, DCPTime f) - : subs (s) - , from (f) - {} - - PlayerCaption subs; - DCPTime from; - boost::optional to; - }; - - typedef std::map, std::list > Map; - - void add (DCPTimePeriod period, std::list& pc, std::list p) const; - - Map _data; -}; diff --git a/src/lib/active_text.cc b/src/lib/active_text.cc new file mode 100644 index 000000000..de3dc8165 --- /dev/null +++ b/src/lib/active_text.cc @@ -0,0 +1,162 @@ +/* + Copyright (C) 2017-2018 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 "active_text.h" +#include "text_content.h" +#include +#include + +using std::list; +using std::pair; +using std::make_pair; +using boost::weak_ptr; +using boost::shared_ptr; +using boost::optional; + +void +ActiveText::add (DCPTimePeriod period, list& pc, list p) const +{ + BOOST_FOREACH (Period i, p) { + DCPTimePeriod test (i.from, i.to.get_value_or(DCPTime::max())); + optional overlap = period.overlap (test); + if (overlap && overlap->duration() > DCPTime(period.duration().get() / 2)) { + pc.push_back (i.subs); + } + } +} + +list +ActiveText::get (DCPTimePeriod period) const +{ + list ps; + + for (Map::const_iterator i = _data.begin(); i != _data.end(); ++i) { + + shared_ptr 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. + */ +list +ActiveText::get_burnt (DCPTimePeriod period, bool always_burn_captions) const +{ + list ps; + + for (Map::const_iterator i = _data.begin(); i != _data.end(); ++i) { + + shared_ptr caption = i->first.lock (); + if (!caption) { + continue; + } + + if (!caption->use() || (!always_burn_captions && !caption->burn())) { + /* Not burning this content */ + continue; + } + + add (period, ps, i->second); + } + + return ps; +} + +/** Remove subtitles that finish before a given time from our list. + * @param time Time to remove before. + */ +void +ActiveText::clear_before (DCPTime time) +{ + Map updated; + for (Map::const_iterator i = _data.begin(); i != _data.end(); ++i) { + list as; + BOOST_FOREACH (Period j, i->second) { + if (!j.to || j.to.get() >= time) { + as.push_back (j); + } + } + if (!as.empty ()) { + updated[i->first] = as; + } + } + _data = updated; +} + +/** Add a new subtitle with a from time. + * @param content Content that the subtitle is from. + * @param ps Subtitles. + * @param from From time for these subtitles. + */ +void +ActiveText::add_from (weak_ptr content, PlayerText ps, DCPTime from) +{ + if (_data.find(content) == _data.end()) { + _data[content] = list(); + } + _data[content].push_back (Period (ps, from)); +} + +/** Add the to time for the last subtitle added from a piece of content. + * @param content Content that the subtitle is from. + * @param to To time for the last subtitle submitted to add_from for this content. + * @return Return the corresponding subtitles and their from time. + */ +pair +ActiveText::add_to (weak_ptr content, DCPTime to) +{ + DCPOMATIC_ASSERT (_data.find(content) != _data.end()); + + _data[content].back().to = to; + + BOOST_FOREACH (StringText& i, _data[content].back().subs.text) { + i.set_out (dcp::Time(to.seconds(), 1000)); + } + + return make_pair (_data[content].back().subs, _data[content].back().from); +} + +/** @param content Some content. + * @return true if we have any active subtitles from this content. + */ +bool +ActiveText::have (weak_ptr content) const +{ + Map::const_iterator i = _data.find(content); + if (i == _data.end()) { + return false; + } + + return !i->second.empty(); +} + +void +ActiveText::clear () +{ + _data.clear (); +} diff --git a/src/lib/active_text.h b/src/lib/active_text.h new file mode 100644 index 000000000..10ce30f08 --- /dev/null +++ b/src/lib/active_text.h @@ -0,0 +1,68 @@ +/* + Copyright (C) 2017-2018 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 . + +*/ + +/** @file src/lib/active_captions.h + * @brief ActiveText class. + */ + +#include "dcpomatic_time.h" +#include "player_text.h" +#include +#include +#include + +class TextContent; + +/** @class ActiveText + * @brief A class to maintain information on active subtitles for Player. + */ +class ActiveText : public boost::noncopyable +{ +public: + std::list get (DCPTimePeriod period) 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 content, PlayerText ps, DCPTime from); + std::pair add_to (boost::weak_ptr content, DCPTime to); + bool have (boost::weak_ptr content) const; + +private: + class Period + { + public: + Period () {} + + Period (PlayerText s, DCPTime f) + : subs (s) + , from (f) + {} + + PlayerText subs; + DCPTime from; + boost::optional to; + }; + + typedef std::map, std::list > Map; + + void add (DCPTimePeriod period, std::list& pc, std::list p) const; + + Map _data; +}; diff --git a/src/lib/bitmap_caption.h b/src/lib/bitmap_caption.h deleted file mode 100644 index 8883272ad..000000000 --- a/src/lib/bitmap_caption.h +++ /dev/null @@ -1,48 +0,0 @@ -/* - Copyright (C) 2014-2018 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 . - -*/ - -#ifndef DCPOMATIC_BITMAP_CAPTION_H -#define DCPOMATIC_BITMAP_CAPTION_H - -#include "rect.h" -#include - -class Image; - -class BitmapCaption -{ -public: - BitmapCaption (boost::shared_ptr i, dcpomatic::Rect r) - : image (i) - , rectangle (r) - {} - - boost::shared_ptr image; - /** Area that the subtitle covers on its corresponding video, expressed in - * proportions of the image size; e.g. rectangle.x = 0.5 would mean that - * the rectangle starts half-way across the video. - * - * This rectangle may or may not have had a TextContent's offsets and - * scale applied to it, depending on context. - */ - dcpomatic::Rect rectangle; -}; - -#endif diff --git a/src/lib/bitmap_text.h b/src/lib/bitmap_text.h new file mode 100644 index 000000000..2314c2db0 --- /dev/null +++ b/src/lib/bitmap_text.h @@ -0,0 +1,48 @@ +/* + Copyright (C) 2014-2018 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 . + +*/ + +#ifndef DCPOMATIC_BITMAP_CAPTION_H +#define DCPOMATIC_BITMAP_CAPTION_H + +#include "rect.h" +#include + +class Image; + +class BitmapText +{ +public: + BitmapText (boost::shared_ptr i, dcpomatic::Rect r) + : image (i) + , rectangle (r) + {} + + boost::shared_ptr image; + /** Area that the subtitle covers on its corresponding video, expressed in + * proportions of the image size; e.g. rectangle.x = 0.5 would mean that + * the rectangle starts half-way across the video. + * + * This rectangle may or may not have had a TextContent's offsets and + * scale applied to it, depending on context. + */ + dcpomatic::Rect rectangle; +}; + +#endif diff --git a/src/lib/caption_content.cc b/src/lib/caption_content.cc deleted file mode 100644 index 4d0795190..000000000 --- a/src/lib/caption_content.cc +++ /dev/null @@ -1,579 +0,0 @@ -/* - Copyright (C) 2013-2018 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 "caption_content.h" -#include "util.h" -#include "exceptions.h" -#include "font.h" -#include "content.h" -#include -#include -#include -#include -#include - -#include "i18n.h" - -using std::string; -using std::vector; -using std::cout; -using std::list; -using boost::shared_ptr; -using boost::dynamic_pointer_cast; -using boost::optional; -using dcp::raw_convert; - -int const CaptionContentProperty::X_OFFSET = 500; -int const CaptionContentProperty::Y_OFFSET = 501; -int const CaptionContentProperty::X_SCALE = 502; -int const CaptionContentProperty::Y_SCALE = 503; -int const CaptionContentProperty::USE = 504; -int const CaptionContentProperty::BURN = 505; -int const CaptionContentProperty::LANGUAGE = 506; -int const CaptionContentProperty::FONTS = 507; -int const CaptionContentProperty::COLOUR = 508; -int const CaptionContentProperty::EFFECT = 509; -int const CaptionContentProperty::EFFECT_COLOUR = 510; -int const CaptionContentProperty::LINE_SPACING = 511; -int const CaptionContentProperty::FADE_IN = 512; -int const CaptionContentProperty::FADE_OUT = 513; -int const CaptionContentProperty::OUTLINE_WIDTH = 514; -int const CaptionContentProperty::TYPE = 515; - -CaptionContent::CaptionContent (Content* parent, CaptionType original_type) - : ContentPart (parent) - , _use (false) - , _burn (false) - , _x_offset (0) - , _y_offset (0) - , _x_scale (1) - , _y_scale (1) - , _line_spacing (1) - , _outline_width (2) - , _type (original_type) - , _original_type (original_type) -{ - -} - -/** @return CaptionContents from node or nodes under node (according to version). - * The list could be empty if no CaptionContents are found. - */ -list > -CaptionContent::from_xml (Content* parent, cxml::ConstNodePtr node, int version) -{ - if (version < 34) { - /* With old metadata FFmpeg content has the subtitle-related tags even with no - subtitle streams, so check for that. - */ - if (node->string_child("Type") == "FFmpeg" && node->node_children("SubtitleStream").empty()) { - return list >(); - } - - /* Otherwise we can drop through to the newer logic */ - } - - if (version < 37) { - if (!node->optional_number_child("SubtitleXOffset") && !node->optional_number_child("SubtitleOffset")) { - return list >(); - } - list > c; - c.push_back (shared_ptr (new CaptionContent (parent, node, version))); - return c; - } - - if (!node->optional_node_child("Caption")) { - return list >(); - } - - list > c; - BOOST_FOREACH (cxml::ConstNodePtr i, node->node_children("Caption")) { - c.push_back (shared_ptr (new CaptionContent (parent, i, version))); - } - return c; -} - -CaptionContent::CaptionContent (Content* parent, cxml::ConstNodePtr node, int version) - : ContentPart (parent) - , _use (false) - , _burn (false) - , _x_offset (0) - , _y_offset (0) - , _x_scale (1) - , _y_scale (1) - , _line_spacing (node->optional_number_child("LineSpacing").get_value_or (1)) - , _outline_width (node->optional_number_child("OutlineWidth").get_value_or (2)) - , _type (CAPTION_OPEN) - , _original_type (CAPTION_OPEN) -{ - if (version >= 37) { - _use = node->bool_child ("Use"); - _burn = node->bool_child ("Burn"); - } else if (version >= 32) { - _use = node->bool_child ("UseSubtitles"); - _burn = node->bool_child ("BurnSubtitles"); - } - - if (version >= 37) { - _x_offset = node->number_child ("XOffset"); - _y_offset = node->number_child ("YOffset"); - } else if (version >= 7) { - _x_offset = node->number_child ("SubtitleXOffset"); - _y_offset = node->number_child ("SubtitleYOffset"); - } else { - _y_offset = node->number_child ("SubtitleOffset"); - } - - if (node->optional_bool_child("Outline").get_value_or(false)) { - _effect = dcp::BORDER; - } else if (node->optional_bool_child("Shadow").get_value_or(false)) { - _effect = dcp::SHADOW; - } else { - _effect = dcp::NONE; - } - - optional effect = node->optional_string_child("Effect"); - if (effect) { - if (*effect == "none") { - _effect = dcp::NONE; - } else if (*effect == "outline") { - _effect = dcp::BORDER; - } else if (*effect == "shadow") { - _effect = dcp::SHADOW; - } - } - - if (version >= 37) { - _x_scale = node->number_child ("XScale"); - _y_scale = node->number_child ("YScale"); - } else if (version >= 10) { - _x_scale = node->number_child ("SubtitleXScale"); - _y_scale = node->number_child ("SubtitleYScale"); - } else { - _x_scale = _y_scale = node->number_child ("SubtitleScale"); - } - - optional r = node->optional_number_child("Red"); - optional g = node->optional_number_child("Green"); - optional b = node->optional_number_child("Blue"); - if (r && g && b) { - _colour = dcp::Colour (*r, *g, *b); - } - - if (version >= 36) { - optional er = node->optional_number_child("EffectRed"); - optional eg = node->optional_number_child("EffectGreen"); - optional eb = node->optional_number_child("EffectBlue"); - if (er && eg && eb) { - _effect_colour = dcp::Colour (*er, *eg, *eb); - } - } else { - _effect_colour = dcp::Colour ( - node->optional_number_child("OutlineRed").get_value_or(255), - node->optional_number_child("OutlineGreen").get_value_or(255), - node->optional_number_child("OutlineBlue").get_value_or(255) - ); - } - - optional fi; - if (version >= 37) { - fi = node->optional_number_child("FadeIn"); - } else { - fi = node->optional_number_child("SubtitleFadeIn"); - } - if (fi) { - _fade_in = ContentTime (*fi); - } - - optional fo; - if (version >= 37) { - fo = node->optional_number_child("FadeOut"); - } else { - fo = node->optional_number_child("SubtitleFadeOut"); - } - if (fo) { - _fade_out = ContentTime (*fo); - } - - if (version >= 37) { - _language = node->optional_string_child ("Language").get_value_or (""); - } else { - _language = node->optional_string_child ("SubtitleLanguage").get_value_or (""); - } - - list fonts = node->node_children ("Font"); - for (list::const_iterator i = fonts.begin(); i != fonts.end(); ++i) { - _fonts.push_back (shared_ptr (new Font (*i))); - } - - connect_to_fonts (); - - _type = string_to_caption_type (node->optional_string_child("Type").get_value_or("open")); - _original_type = string_to_caption_type (node->optional_string_child("OriginalType").get_value_or("open")); -} - -CaptionContent::CaptionContent (Content* parent, vector > c) - : ContentPart (parent) -{ - /* This constructor is for join which is only supported for content types - that have a single caption, so we can use only_caption() here. - */ - shared_ptr ref = c[0]->only_caption(); - DCPOMATIC_ASSERT (ref); - list > ref_fonts = ref->fonts (); - - for (size_t i = 1; i < c.size(); ++i) { - - if (c[i]->only_caption()->use() != ref->use()) { - throw JoinError (_("Content to be joined must have the same 'use subtitles' setting.")); - } - - if (c[i]->only_caption()->burn() != ref->burn()) { - throw JoinError (_("Content to be joined must have the same 'burn subtitles' setting.")); - } - - if (c[i]->only_caption()->x_offset() != ref->x_offset()) { - throw JoinError (_("Content to be joined must have the same subtitle X offset.")); - } - - if (c[i]->only_caption()->y_offset() != ref->y_offset()) { - throw JoinError (_("Content to be joined must have the same subtitle Y offset.")); - } - - if (c[i]->only_caption()->x_scale() != ref->x_scale()) { - throw JoinError (_("Content to be joined must have the same subtitle X scale.")); - } - - if (c[i]->only_caption()->y_scale() != ref->y_scale()) { - throw JoinError (_("Content to be joined must have the same subtitle Y scale.")); - } - - if (c[i]->only_caption()->line_spacing() != ref->line_spacing()) { - throw JoinError (_("Content to be joined must have the same subtitle line spacing.")); - } - - if ((c[i]->only_caption()->fade_in() != ref->fade_in()) || (c[i]->only_caption()->fade_out() != ref->fade_out())) { - throw JoinError (_("Content to be joined must have the same subtitle fades.")); - } - - if ((c[i]->only_caption()->outline_width() != ref->outline_width())) { - throw JoinError (_("Content to be joined must have the same outline width.")); - } - - list > fonts = c[i]->only_caption()->fonts (); - if (fonts.size() != ref_fonts.size()) { - throw JoinError (_("Content to be joined must use the same fonts.")); - } - - list >::const_iterator j = ref_fonts.begin (); - list >::const_iterator k = fonts.begin (); - - while (j != ref_fonts.end ()) { - if (**j != **k) { - throw JoinError (_("Content to be joined must use the same fonts.")); - } - ++j; - ++k; - } - } - - _use = ref->use (); - _burn = ref->burn (); - _x_offset = ref->x_offset (); - _y_offset = ref->y_offset (); - _x_scale = ref->x_scale (); - _y_scale = ref->y_scale (); - _language = ref->language (); - _fonts = ref_fonts; - _line_spacing = ref->line_spacing (); - _fade_in = ref->fade_in (); - _fade_out = ref->fade_out (); - _outline_width = ref->outline_width (); - _type = ref->type (); - _original_type = ref->original_type (); - - connect_to_fonts (); -} - -/** _mutex must not be held on entry */ -void -CaptionContent::as_xml (xmlpp::Node* root) const -{ - boost::mutex::scoped_lock lm (_mutex); - - xmlpp::Element* caption = root->add_child ("Caption"); - - caption->add_child("Use")->add_child_text (_use ? "1" : "0"); - caption->add_child("Burn")->add_child_text (_burn ? "1" : "0"); - caption->add_child("XOffset")->add_child_text (raw_convert (_x_offset)); - caption->add_child("YOffset")->add_child_text (raw_convert (_y_offset)); - caption->add_child("XScale")->add_child_text (raw_convert (_x_scale)); - caption->add_child("YScale")->add_child_text (raw_convert (_y_scale)); - caption->add_child("Language")->add_child_text (_language); - if (_colour) { - caption->add_child("Red")->add_child_text (raw_convert (_colour->r)); - caption->add_child("Green")->add_child_text (raw_convert (_colour->g)); - caption->add_child("Blue")->add_child_text (raw_convert (_colour->b)); - } - if (_effect) { - switch (*_effect) { - case dcp::NONE: - caption->add_child("Effect")->add_child_text("none"); - break; - case dcp::BORDER: - caption->add_child("Effect")->add_child_text("outline"); - break; - case dcp::SHADOW: - caption->add_child("Effect")->add_child_text("shadow"); - break; - } - } - if (_effect_colour) { - caption->add_child("EffectRed")->add_child_text (raw_convert (_effect_colour->r)); - caption->add_child("EffectGreen")->add_child_text (raw_convert (_effect_colour->g)); - caption->add_child("EffectBlue")->add_child_text (raw_convert (_effect_colour->b)); - } - caption->add_child("LineSpacing")->add_child_text (raw_convert (_line_spacing)); - if (_fade_in) { - caption->add_child("FadeIn")->add_child_text (raw_convert (_fade_in->get())); - } - if (_fade_out) { - caption->add_child("FadeOut")->add_child_text (raw_convert (_fade_out->get())); - } - caption->add_child("OutlineWidth")->add_child_text (raw_convert (_outline_width)); - - for (list >::const_iterator i = _fonts.begin(); i != _fonts.end(); ++i) { - (*i)->as_xml (caption->add_child("Font")); - } - - caption->add_child("Type")->add_child_text (caption_type_to_string(_type)); - caption->add_child("OriginalType")->add_child_text (caption_type_to_string(_original_type)); -} - -string -CaptionContent::identifier () const -{ - string s = raw_convert (x_scale()) - + "_" + raw_convert (y_scale()) - + "_" + raw_convert (x_offset()) - + "_" + raw_convert (y_offset()) - + "_" + raw_convert (line_spacing()) - + "_" + raw_convert (fade_in().get_value_or(ContentTime()).get()) - + "_" + raw_convert (fade_out().get_value_or(ContentTime()).get()) - + "_" + raw_convert (outline_width()) - + "_" + raw_convert (colour().get_value_or(dcp::Colour(255, 255, 255)).to_argb_string()) - + "_" + raw_convert (dcp::effect_to_string(effect().get_value_or(dcp::NONE))) - + "_" + raw_convert (effect_colour().get_value_or(dcp::Colour(0, 0, 0)).to_argb_string()); - - /* XXX: I suppose really _fonts shouldn't be in here, since not all - types of subtitle content involve fonts. - */ - BOOST_FOREACH (shared_ptr f, _fonts) { - for (int i = 0; i < FontFiles::VARIANTS; ++i) { - s += "_" + f->file(static_cast(i)).get_value_or("Default").string(); - } - } - - /* The language is for metadata only, and doesn't affect - how this content looks. - */ - - return s; -} - -void -CaptionContent::add_font (shared_ptr font) -{ - _fonts.push_back (font); - connect_to_fonts (); -} - -void -CaptionContent::connect_to_fonts () -{ - BOOST_FOREACH (boost::signals2::connection& i, _font_connections) { - i.disconnect (); - } - - _font_connections.clear (); - - BOOST_FOREACH (shared_ptr i, _fonts) { - _font_connections.push_back (i->Changed.connect (boost::bind (&CaptionContent::font_changed, this))); - } -} - -void -CaptionContent::font_changed () -{ - _parent->signal_changed (CaptionContentProperty::FONTS); -} - -void -CaptionContent::set_colour (dcp::Colour colour) -{ - maybe_set (_colour, colour, CaptionContentProperty::COLOUR); -} - -void -CaptionContent::unset_colour () -{ - maybe_set (_colour, optional(), CaptionContentProperty::COLOUR); -} - -void -CaptionContent::set_effect (dcp::Effect e) -{ - maybe_set (_effect, e, CaptionContentProperty::EFFECT); -} - -void -CaptionContent::unset_effect () -{ - maybe_set (_effect, optional(), CaptionContentProperty::EFFECT); -} - -void -CaptionContent::set_effect_colour (dcp::Colour colour) -{ - maybe_set (_effect_colour, colour, CaptionContentProperty::EFFECT_COLOUR); -} - -void -CaptionContent::unset_effect_colour () -{ - maybe_set (_effect_colour, optional(), CaptionContentProperty::EFFECT_COLOUR); -} - -void -CaptionContent::set_use (bool u) -{ - maybe_set (_use, u, CaptionContentProperty::USE); -} - -void -CaptionContent::set_burn (bool b) -{ - maybe_set (_burn, b, CaptionContentProperty::BURN); -} - -void -CaptionContent::set_x_offset (double o) -{ - maybe_set (_x_offset, o, CaptionContentProperty::X_OFFSET); -} - -void -CaptionContent::set_y_offset (double o) -{ - maybe_set (_y_offset, o, CaptionContentProperty::Y_OFFSET); -} - -void -CaptionContent::set_x_scale (double s) -{ - maybe_set (_x_scale, s, CaptionContentProperty::X_SCALE); -} - -void -CaptionContent::set_y_scale (double s) -{ - maybe_set (_y_scale, s, CaptionContentProperty::Y_SCALE); -} - -void -CaptionContent::set_language (string language) -{ - maybe_set (_language, language, CaptionContentProperty::LANGUAGE); -} - -void -CaptionContent::set_line_spacing (double s) -{ - maybe_set (_line_spacing, s, CaptionContentProperty::LINE_SPACING); -} - -void -CaptionContent::set_fade_in (ContentTime t) -{ - maybe_set (_fade_in, t, CaptionContentProperty::FADE_IN); -} - -void -CaptionContent::unset_fade_in () -{ - maybe_set (_fade_in, optional(), CaptionContentProperty::FADE_IN); -} - -void -CaptionContent::set_fade_out (ContentTime t) -{ - maybe_set (_fade_out, t, CaptionContentProperty::FADE_OUT); -} - -void -CaptionContent::unset_fade_out () -{ - maybe_set (_fade_out, optional(), CaptionContentProperty::FADE_OUT); -} - -void -CaptionContent::set_type (CaptionType type) -{ - maybe_set (_type, type, CaptionContentProperty::TYPE); -} - -void -CaptionContent::set_outline_width (int w) -{ - maybe_set (_outline_width, w, CaptionContentProperty::OUTLINE_WIDTH); -} - -void -CaptionContent::take_settings_from (shared_ptr c) -{ - set_use (c->_use); - set_burn (c->_burn); - set_x_offset (c->_x_offset); - set_y_offset (c->_y_offset); - set_x_scale (c->_x_scale); - set_y_scale (c->_y_scale); - maybe_set (_fonts, c->_fonts, CaptionContentProperty::FONTS); - if (c->_colour) { - set_colour (*c->_colour); - } else { - unset_colour (); - } - if (c->_effect) { - set_effect (*c->_effect); - } - if (c->_effect_colour) { - set_effect_colour (*c->_effect_colour); - } else { - unset_effect_colour (); - } - set_line_spacing (c->_line_spacing); - if (c->_fade_in) { - set_fade_in (*c->_fade_in); - } - if (c->_fade_out) { - set_fade_out (*c->_fade_out); - } - set_outline_width (c->_outline_width); -} diff --git a/src/lib/caption_content.h b/src/lib/caption_content.h deleted file mode 100644 index 4f5780d23..000000000 --- a/src/lib/caption_content.h +++ /dev/null @@ -1,222 +0,0 @@ -/* - Copyright (C) 2013-2018 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 . - -*/ - -#ifndef DCPOMATIC_CAPTION_CONTENT_H -#define DCPOMATIC_CAPTION_CONTENT_H - -#include "content_part.h" -#include -#include -#include - -class Font; - -class CaptionContentProperty -{ -public: - static int const X_OFFSET; - static int const Y_OFFSET; - static int const X_SCALE; - static int const Y_SCALE; - static int const USE; - static int const BURN; - static int const LANGUAGE; - static int const FONTS; - static int const COLOUR; - static int const EFFECT; - static int const EFFECT_COLOUR; - static int const LINE_SPACING; - static int const FADE_IN; - static int const FADE_OUT; - static int const OUTLINE_WIDTH; - static int const TYPE; -}; - -/** @class CaptionContent - * @brief Description of how some text content should be presented. - * - * There are `bitmap' subtitles and `plain' subtitles (plain text), - * and not all of the settings in this class correspond to both types. - */ -class CaptionContent : public ContentPart -{ -public: - CaptionContent (Content* parent, CaptionType original_type); - CaptionContent (Content* parent, std::vector >); - - void as_xml (xmlpp::Node *) const; - std::string identifier () const; - void take_settings_from (boost::shared_ptr c); - - void add_font (boost::shared_ptr font); - - void set_use (bool); - void set_burn (bool); - void set_x_offset (double); - void set_y_offset (double); - void set_x_scale (double); - void set_y_scale (double); - void set_language (std::string language); - void set_colour (dcp::Colour); - void unset_colour (); - void set_effect (dcp::Effect); - void unset_effect (); - void set_effect_colour (dcp::Colour); - void unset_effect_colour (); - void set_line_spacing (double s); - void set_fade_in (ContentTime); - void unset_fade_in (); - void set_fade_out (ContentTime); - void set_outline_width (int); - void unset_fade_out (); - void set_type (CaptionType type); - - bool use () const { - boost::mutex::scoped_lock lm (_mutex); - return _use; - } - - bool burn () const { - boost::mutex::scoped_lock lm (_mutex); - return _burn; - } - - double x_offset () const { - boost::mutex::scoped_lock lm (_mutex); - return _x_offset; - } - - double y_offset () const { - boost::mutex::scoped_lock lm (_mutex); - return _y_offset; - } - - double x_scale () const { - boost::mutex::scoped_lock lm (_mutex); - return _x_scale; - } - - double y_scale () const { - boost::mutex::scoped_lock lm (_mutex); - return _y_scale; - } - - std::list > fonts () const { - boost::mutex::scoped_lock lm (_mutex); - return _fonts; - } - - std::string language () const { - boost::mutex::scoped_lock lm (_mutex); - return _language; - } - - boost::optional colour () const { - boost::mutex::scoped_lock lm (_mutex); - return _colour; - } - - boost::optional effect () const { - boost::mutex::scoped_lock lm (_mutex); - return _effect; - } - - boost::optional effect_colour () const { - boost::mutex::scoped_lock lm (_mutex); - return _effect_colour; - } - - double line_spacing () const { - boost::mutex::scoped_lock lm (_mutex); - return _line_spacing; - } - - boost::optional fade_in () const { - boost::mutex::scoped_lock lm (_mutex); - return _fade_in; - } - - boost::optional fade_out () const { - boost::mutex::scoped_lock lm (_mutex); - return _fade_out; - } - - int outline_width () const { - boost::mutex::scoped_lock lm (_mutex); - return _outline_width; - } - - CaptionType type () const { - boost::mutex::scoped_lock lm (_mutex); - return _type; - } - - CaptionType original_type () const { - boost::mutex::scoped_lock lm (_mutex); - return _original_type; - } - - static std::list > from_xml (Content* parent, cxml::ConstNodePtr, int version); - -protected: - /** subtitle language (e.g. "German") or empty if it is not known */ - std::string _language; - -private: - friend struct ffmpeg_pts_offset_test; - - CaptionContent (Content* parent, cxml::ConstNodePtr, int version); - void font_changed (); - void connect_to_fonts (); - - std::list _font_connections; - - bool _use; - bool _burn; - /** x offset for placing subtitles, as a proportion of the container width; - * +ve is further right, -ve is further left. - */ - double _x_offset; - /** y offset for placing subtitles, as a proportion of the container height; - * +ve is further down the frame, -ve is further up. - */ - double _y_offset; - /** x scale factor to apply to subtitles */ - double _x_scale; - /** y scale factor to apply to subtitles */ - double _y_scale; - std::list > _fonts; - boost::optional _colour; - boost::optional _effect; - boost::optional _effect_colour; - /** scaling factor for line spacing; 1 is "standard", < 1 is closer together, > 1 is further apart */ - double _line_spacing; - boost::optional _fade_in; - boost::optional _fade_out; - int _outline_width; - /** what these captions will be used for in the output DCP (not necessarily what - * they were originally). - */ - CaptionType _type; - /** the original type of these captions in their content */ - CaptionType _original_type; -}; - -#endif diff --git a/src/lib/caption_decoder.cc b/src/lib/caption_decoder.cc deleted file mode 100644 index 1a2221024..000000000 --- a/src/lib/caption_decoder.cc +++ /dev/null @@ -1,257 +0,0 @@ -/* - Copyright (C) 2013-2017 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 "caption_decoder.h" -#include "caption_content.h" -#include "util.h" -#include "log.h" -#include "compose.hpp" -#include -#include -#include -#include -#include - -using std::list; -using std::cout; -using std::string; -using std::min; -using boost::shared_ptr; -using boost::optional; -using boost::function; - -CaptionDecoder::CaptionDecoder ( - Decoder* parent, - shared_ptr c, - shared_ptr log, - ContentTime first - ) - : DecoderPart (parent, log) - , _content (c) - , _position (first) -{ - -} - -/** 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 -CaptionDecoder::emit_bitmap_start (ContentTime from, shared_ptr image, dcpomatic::Rect rect) -{ - BitmapStart (ContentBitmapCaption (from, _content->type(), image, rect)); - _position = from; -} - -void -CaptionDecoder::emit_plain_start (ContentTime from, list s) -{ - BOOST_FOREACH (dcp::SubtitleString& i, 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.) - */ - string t = i.text (); - boost::algorithm::replace_all (t, "<", "<"); - boost::algorithm::replace_all (t, ">", ">"); - i.set_text (t); - - /* Set any forced appearance */ - if (content()->colour()) { - i.set_colour (*content()->colour()); - } - if (content()->effect_colour()) { - i.set_effect_colour (*content()->effect_colour()); - } - if (content()->effect()) { - i.set_effect (*content()->effect()); - } - if (content()->fade_in()) { - i.set_fade_up_time (dcp::Time(content()->fade_in()->seconds(), 1000)); - } - if (content()->fade_out()) { - i.set_fade_down_time (dcp::Time(content()->fade_out()->seconds(), 1000)); - } - } - - PlainStart (ContentTextCaption (from, _content->type(), s)); - _position = from; -} - -void -CaptionDecoder::emit_plain_start (ContentTime from, sub::Subtitle const & subtitle) -{ - /* See if our next subtitle needs to be vertically placed on screen by us */ - bool needs_placement = false; - optional bottom_line; - BOOST_FOREACH (sub::Line i, subtitle.lines) { - if (!i.vertical_position.reference || i.vertical_position.reference.get() == sub::TOP_OF_SUBTITLE) { - needs_placement = true; - DCPOMATIC_ASSERT (i.vertical_position.line); - if (!bottom_line || bottom_line.get() < i.vertical_position.line.get()) { - bottom_line = i.vertical_position.line.get(); - } - } - } - - /* Find the lowest proportional position */ - optional lowest_proportional; - BOOST_FOREACH (sub::Line i, subtitle.lines) { - if (i.vertical_position.proportional) { - if (!lowest_proportional) { - lowest_proportional = i.vertical_position.proportional; - } else { - lowest_proportional = min (lowest_proportional.get(), i.vertical_position.proportional.get()); - } - } - } - - list out; - BOOST_FOREACH (sub::Line i, subtitle.lines) { - BOOST_FOREACH (sub::Block j, i.blocks) { - - if (!j.font_size.specified()) { - /* Fallback default font size if no other has been specified */ - j.font_size.set_points (48); - } - - float v_position; - dcp::VAlign v_align; - if (needs_placement) { - DCPOMATIC_ASSERT (i.vertical_position.line); - /* This 1.015 is an arbitrary value to lift the bottom sub off the bottom - of the screen a bit to a pleasing degree. - */ - v_position = 1.015 - - (1 + bottom_line.get() - i.vertical_position.line.get()) - * 1.2 * content()->line_spacing() * content()->y_scale() * j.font_size.proportional (72 * 11); - - v_align = dcp::VALIGN_TOP; - } else { - DCPOMATIC_ASSERT (i.vertical_position.proportional); - DCPOMATIC_ASSERT (i.vertical_position.reference); - v_position = i.vertical_position.proportional.get(); - - if (lowest_proportional) { - /* Adjust line spacing */ - v_position = ((v_position - lowest_proportional.get()) * content()->line_spacing()) + lowest_proportional.get(); - } - - switch (i.vertical_position.reference.get()) { - case sub::TOP_OF_SCREEN: - v_align = dcp::VALIGN_TOP; - break; - case sub::VERTICAL_CENTRE_OF_SCREEN: - v_align = dcp::VALIGN_CENTER; - break; - case sub::BOTTOM_OF_SCREEN: - v_align = dcp::VALIGN_BOTTOM; - break; - default: - v_align = dcp::VALIGN_TOP; - break; - } - } - - dcp::HAlign h_align; - switch (i.horizontal_position.reference) { - case sub::LEFT_OF_SCREEN: - h_align = dcp::HALIGN_LEFT; - break; - case sub::HORIZONTAL_CENTRE_OF_SCREEN: - h_align = dcp::HALIGN_CENTER; - break; - case sub::RIGHT_OF_SCREEN: - h_align = dcp::HALIGN_RIGHT; - break; - default: - h_align = dcp::HALIGN_CENTER; - break; - } - - /* The idea here (rightly or wrongly) is that we set the appearance based on the - values in the libsub objects, and these are overridden with values from the - content by the other emit_plain_start() above. - */ - - out.push_back ( - dcp::SubtitleString ( - string(TEXT_FONT_ID), - j.italic, - j.bold, - j.underline, - j.colour.dcp(), - j.font_size.points (72 * 11), - 1.0, - dcp::Time (from.seconds(), 1000), - /* XXX: hmm; this is a bit ugly (we don't know the to time yet) */ - dcp::Time (), - i.horizontal_position.proportional, - h_align, - v_position, - v_align, - dcp::DIRECTION_LTR, - j.text, - dcp::NONE, - j.effect_colour.get_value_or(sub::Colour(0, 0, 0)).dcp(), - /* Hack: we should use subtitle.fade_up and subtitle.fade_down here - but the times of these often don't have a frame rate associated - with them so the sub::Time won't convert them to milliseconds without - throwing an exception. Since only DCP subs fill those in (and we don't - use libsub for DCP subs) we can cheat by just putting 0 in here. - */ - dcp::Time (), - dcp::Time () - ) - ); - } - } - - emit_plain_start (from, out); -} - -void -CaptionDecoder::emit_stop (ContentTime to) -{ - Stop (to, _content->type()); -} - -void -CaptionDecoder::emit_plain (ContentTimePeriod period, list s) -{ - emit_plain_start (period.from, s); - emit_stop (period.to); -} - -void -CaptionDecoder::emit_plain (ContentTimePeriod period, sub::Subtitle const & s) -{ - emit_plain_start (period.from, s); - emit_stop (period.to); -} - -void -CaptionDecoder::seek () -{ - _position = ContentTime (); -} diff --git a/src/lib/caption_decoder.h b/src/lib/caption_decoder.h deleted file mode 100644 index d555446c7..000000000 --- a/src/lib/caption_decoder.h +++ /dev/null @@ -1,74 +0,0 @@ -/* - Copyright (C) 2013-2018 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 . - -*/ - -#ifndef DCPOMATIC_CAPTION_DECODER_H -#define DCPOMATIC_CAPTION_DECODER_H - -#include "decoder.h" -#include "rect.h" -#include "types.h" -#include "content_caption.h" -#include "decoder_part.h" -#include -#include - -namespace sub { - class Subtitle; -} - -class Image; - -class CaptionDecoder : public DecoderPart -{ -public: - CaptionDecoder ( - Decoder* parent, - boost::shared_ptr, - boost::shared_ptr log, - ContentTime first - ); - - ContentTime position () const { - return _position; - } - - void emit_bitmap_start (ContentTime from, boost::shared_ptr image, dcpomatic::Rect rect); - void emit_plain_start (ContentTime from, std::list s); - void emit_plain_start (ContentTime from, sub::Subtitle const & subtitle); - void emit_plain (ContentTimePeriod period, std::list s); - void emit_plain (ContentTimePeriod period, sub::Subtitle const & subtitle); - void emit_stop (ContentTime to); - - void seek (); - - boost::shared_ptr content () const { - return _content; - } - - boost::signals2::signal BitmapStart; - boost::signals2::signal PlainStart; - boost::signals2::signal Stop; - -private: - boost::shared_ptr _content; - ContentTime _position; -}; - -#endif diff --git a/src/lib/content.cc b/src/lib/content.cc index 629672b73..7a8088289 100644 --- a/src/lib/content.cc +++ b/src/lib/content.cc @@ -27,7 +27,7 @@ #include "content_factory.h" #include "video_content.h" #include "audio_content.h" -#include "caption_content.h" +#include "text_content.h" #include "exceptions.h" #include "film.h" #include "job.h" @@ -437,8 +437,8 @@ Content::take_settings_from (shared_ptr c) audio->take_settings_from (c->audio); } - list >::iterator i = caption.begin (); - list >::const_iterator j = c->caption.begin (); + list >::iterator i = caption.begin (); + list >::const_iterator j = c->caption.begin (); while (i != caption.end() && j != c->caption.end()) { (*i)->take_settings_from (*j); ++i; @@ -446,24 +446,24 @@ Content::take_settings_from (shared_ptr c) } } -shared_ptr +shared_ptr Content::only_caption () const { DCPOMATIC_ASSERT (caption.size() < 2); if (caption.empty ()) { - return shared_ptr (); + return shared_ptr (); } return caption.front (); } -shared_ptr -Content::caption_of_original_type (CaptionType type) const +shared_ptr +Content::caption_of_original_type (TextType type) const { - BOOST_FOREACH (shared_ptr i, caption) { + BOOST_FOREACH (shared_ptr i, caption) { if (i->original_type() == type) { return i; } } - return shared_ptr (); + return shared_ptr (); } diff --git a/src/lib/content.h b/src/lib/content.h index d84f636fb..850cb0891 100644 --- a/src/lib/content.h +++ b/src/lib/content.h @@ -182,10 +182,10 @@ public: boost::shared_ptr video; boost::shared_ptr audio; - std::list > caption; + std::list > caption; - boost::shared_ptr only_caption () const; - boost::shared_ptr caption_of_original_type (CaptionType type) const; + boost::shared_ptr only_caption () const; + boost::shared_ptr caption_of_original_type (TextType type) const; void signal_changed (int); diff --git a/src/lib/content_caption.h b/src/lib/content_caption.h deleted file mode 100644 index ad83fcb89..000000000 --- a/src/lib/content_caption.h +++ /dev/null @@ -1,81 +0,0 @@ -/* - Copyright (C) 2014-2018 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 . - -*/ - -#ifndef DCPOMATIC_CONTENT_TEXT_H -#define DCPOMATIC_CONTENT_TEXT_H - -#include "dcpomatic_time.h" -#include "rect.h" -#include "types.h" -#include "bitmap_caption.h" -#include -#include - -class Image; - -class ContentCaption -{ -public: - explicit ContentCaption (ContentTime f, CaptionType t) - : _from (f) - , _type (t) - {} - - ContentTime from () const { - return _from; - } - - CaptionType type () const { - return _type; - } - -private: - ContentTime _from; - CaptionType _type; -}; - -class ContentBitmapCaption : public ContentCaption -{ -public: - ContentBitmapCaption (ContentTime f, CaptionType type, boost::shared_ptr im, dcpomatic::Rect r) - : ContentCaption (f, type) - , sub (im, r) - {} - - /* Our text, with its rectangle unmodified by any offsets or scales that the content specifies */ - BitmapCaption sub; -}; - -/** A text caption. We store the time period separately (as well as in the dcp::SubtitleStrings) - * as the dcp::SubtitleString timings are sometimes quite heavily quantised and this causes problems - * when we want to compare the quantised periods to the unquantised ones. - */ -class ContentTextCaption : public ContentCaption -{ -public: - ContentTextCaption (ContentTime f, CaptionType type, std::list s) - : ContentCaption (f, type) - , subs (s) - {} - - std::list subs; -}; - -#endif diff --git a/src/lib/content_factory.cc b/src/lib/content_factory.cc index 8c473b7bc..077ef529e 100644 --- a/src/lib/content_factory.cc +++ b/src/lib/content_factory.cc @@ -26,7 +26,7 @@ #include "audio_content.h" #include "image_content.h" #include "atmos_mxf_content.h" -#include "text_caption_file_content.h" +#include "string_text_file_content.h" #include "dcp_content.h" #include "dcp_subtitle_content.h" #include "util.h" @@ -88,7 +88,7 @@ content_factory (shared_ptr film, cxml::NodePtr node, int version, l ); } else if (type == "SubRip" || type == "TextSubtitle") { - content.reset (new TextCaptionFileContent (film, node, version)); + content.reset (new StringTextFileContent (film, node, version)); } else if (type == "DCP") { content.reset (new DCPContent (film, node, version)); } else if (type == "DCPSubtitle") { @@ -210,7 +210,7 @@ content_factory (shared_ptr film, boost::filesystem::path path) if (valid_image_file (path)) { single.reset (new ImageContent (film, path)); } else if (ext == ".srt" || ext == ".ssa" || ext == ".ass") { - single.reset (new TextCaptionFileContent (film, path)); + single.reset (new StringTextFileContent (film, path)); } else if (ext == ".xml") { cxml::Document doc; doc.read_file (path); diff --git a/src/lib/content_text.h b/src/lib/content_text.h new file mode 100644 index 000000000..3ef011f61 --- /dev/null +++ b/src/lib/content_text.h @@ -0,0 +1,81 @@ +/* + Copyright (C) 2014-2018 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 . + +*/ + +#ifndef DCPOMATIC_CONTENT_TEXT_H +#define DCPOMATIC_CONTENT_TEXT_H + +#include "dcpomatic_time.h" +#include "rect.h" +#include "types.h" +#include "bitmap_text.h" +#include +#include + +class Image; + +class ContentText +{ +public: + explicit ContentText (ContentTime f, TextType t) + : _from (f) + , _type (t) + {} + + ContentTime from () const { + return _from; + } + + TextType type () const { + return _type; + } + +private: + ContentTime _from; + TextType _type; +}; + +class ContentBitmapText : public ContentText +{ +public: + ContentBitmapText (ContentTime f, TextType type, boost::shared_ptr im, dcpomatic::Rect r) + : ContentText (f, type) + , sub (im, r) + {} + + /* Our text, with its rectangle unmodified by any offsets or scales that the content specifies */ + BitmapText sub; +}; + +/** A text caption. We store the time period separately (as well as in the dcp::SubtitleStrings) + * as the dcp::SubtitleString timings are sometimes quite heavily quantised and this causes problems + * when we want to compare the quantised periods to the unquantised ones. + */ +class ContentStringText : public ContentText +{ +public: + ContentStringText (ContentTime f, TextType type, std::list s) + : ContentText (f, type) + , subs (s) + {} + + std::list subs; +}; + +#endif diff --git a/src/lib/dcp_content.cc b/src/lib/dcp_content.cc index 414a22256..aaf8064f3 100644 --- a/src/lib/dcp_content.cc +++ b/src/lib/dcp_content.cc @@ -28,7 +28,7 @@ #include "overlaps.h" #include "compose.hpp" #include "dcp_decoder.h" -#include "caption_content.h" +#include "text_content.h" #include #include #include @@ -84,7 +84,7 @@ DCPContent::DCPContent (shared_ptr film, cxml::ConstNodePtr node, in { video = VideoContent::from_xml (this, node, version); audio = AudioContent::from_xml (this, node, version); - caption = CaptionContent::from_xml (this, node, version); + caption = TextContent::from_xml (this, node, version); for (int i = 0; i < CAPTION_COUNT; ++i) { _reference_caption[i] = false; @@ -192,8 +192,8 @@ DCPContent::examine (shared_ptr job) boost::mutex::scoped_lock lm (_mutex); _name = examiner->name (); for (int i = 0; i < CAPTION_COUNT; ++i) { - if (examiner->has_caption(static_cast(i))) { - caption.push_back (shared_ptr(new CaptionContent(this, static_cast(i)))); + if (examiner->has_caption(static_cast(i))) { + caption.push_back (shared_ptr(new TextContent(this, static_cast(i)))); } } captions = caption.size (); @@ -267,7 +267,7 @@ DCPContent::as_xml (xmlpp::Node* node, bool with_paths) const audio->stream()->mapping().as_xml (node->add_child("AudioMapping")); } - BOOST_FOREACH (shared_ptr i, caption) { + BOOST_FOREACH (shared_ptr i, caption) { i->as_xml (node); } @@ -323,7 +323,7 @@ DCPContent::identifier () const s += video->identifier() + "_"; } - BOOST_FOREACH (shared_ptr i, caption) { + BOOST_FOREACH (shared_ptr i, caption) { s += i->identifier () + " "; } @@ -416,7 +416,7 @@ DCPContent::set_reference_audio (bool r) } void -DCPContent::set_reference_caption (CaptionType type, bool r) +DCPContent::set_reference_caption (TextType type, bool r) { { boost::mutex::scoped_lock lm (_mutex); @@ -596,7 +596,7 @@ bool check_caption (shared_ptr c) return !c->caption.empty(); } bool -DCPContent::can_reference_caption (CaptionType type, string& why_not) const +DCPContent::can_reference_caption (TextType type, string& why_not) const { shared_ptr decoder; try { diff --git a/src/lib/dcp_content.h b/src/lib/dcp_content.h index f01790b89..fedc43d64 100644 --- a/src/lib/dcp_content.h +++ b/src/lib/dcp_content.h @@ -108,17 +108,17 @@ public: bool can_reference_audio (std::string &) const; - void set_reference_caption (CaptionType type, bool r); + void set_reference_caption (TextType type, bool r); /** @param type Original type of captions in the DCP. * @return true if these captions are to be referenced. */ - bool reference_caption (CaptionType type) const { + bool reference_caption (TextType type) const { boost::mutex::scoped_lock lm (_mutex); return _reference_caption[type]; } - bool can_reference_caption (CaptionType type, std::string &) const; + bool can_reference_caption (TextType type, std::string &) const; void set_cpl (std::string id); diff --git a/src/lib/dcp_decoder.cc b/src/lib/dcp_decoder.cc index ab655ebf8..86152c817 100644 --- a/src/lib/dcp_decoder.cc +++ b/src/lib/dcp_decoder.cc @@ -24,7 +24,7 @@ #include "video_decoder.h" #include "audio_decoder.h" #include "j2k_image_proxy.h" -#include "caption_decoder.h" +#include "text_decoder.h" #include "image.h" #include "config.h" #include @@ -63,9 +63,9 @@ DCPDecoder::DCPDecoder (shared_ptr c, shared_ptr log, boo if (c->audio) { audio.reset (new AudioDecoder (this, c->audio, log, fast)); } - BOOST_FOREACH (shared_ptr i, c->caption) { + BOOST_FOREACH (shared_ptr i, c->caption) { /* XXX: this time here should be the time of the first subtitle, not 0 */ - caption.push_back (shared_ptr (new CaptionDecoder (this, i, log, ContentTime()))); + caption.push_back (shared_ptr (new TextDecoder (this, i, log, ContentTime()))); } list > cpl_list = cpls (); @@ -193,7 +193,7 @@ DCPDecoder::pass () void DCPDecoder::pass_captions (ContentTime next) { - list >::const_iterator decoder = caption.begin (); + list >::const_iterator decoder = caption.begin (); if ((*_reel)->main_subtitle()) { pass_captions ( next, (*_reel)->main_subtitle()->asset(), _dcp_content->reference_caption(CAPTION_OPEN), (*_reel)->main_subtitle()->entry_point(), *decoder @@ -209,7 +209,7 @@ DCPDecoder::pass_captions (ContentTime next) } void -DCPDecoder::pass_captions (ContentTime next, shared_ptr asset, bool reference, int64_t entry_point, shared_ptr decoder) +DCPDecoder::pass_captions (ContentTime next, shared_ptr asset, bool reference, int64_t entry_point, shared_ptr decoder) { double const vfr = _dcp_content->active_video_frame_rate (); /* Frame within the (played part of the) reel that is coming up next */ diff --git a/src/lib/dcp_decoder.h b/src/lib/dcp_decoder.h index 898b84e5d..2fbdb884a 100644 --- a/src/lib/dcp_decoder.h +++ b/src/lib/dcp_decoder.h @@ -58,7 +58,7 @@ private: void next_reel (); void get_readers (); void pass_captions (ContentTime next); - void pass_captions (ContentTime next, boost::shared_ptr asset, bool reference, int64_t entry_point, boost::shared_ptr decoder); + void pass_captions (ContentTime next, boost::shared_ptr asset, bool reference, int64_t entry_point, boost::shared_ptr decoder); /** Time of next thing to return from pass relative to the start of _reel */ ContentTime _next; diff --git a/src/lib/dcp_encoder.cc b/src/lib/dcp_encoder.cc index f518aefef..42984887d 100644 --- a/src/lib/dcp_encoder.cc +++ b/src/lib/dcp_encoder.cc @@ -35,7 +35,7 @@ #include "writer.h" #include "compose.hpp" #include "referenced_reel_asset.h" -#include "caption_content.h" +#include "text_content.h" #include "player_video.h" #include #include @@ -64,7 +64,7 @@ DCPEncoder::DCPEncoder (shared_ptr film, weak_ptr job) _player_caption_connection = _player->Caption.connect (bind (&DCPEncoder::caption, this, _1, _2, _3)); BOOST_FOREACH (shared_ptr c, film->content ()) { - BOOST_FOREACH (shared_ptr i, c->caption) { + BOOST_FOREACH (shared_ptr i, c->caption) { if (i->use() && !i->burn()) { _non_burnt_subtitles = true; } @@ -143,7 +143,7 @@ DCPEncoder::audio (shared_ptr data, DCPTime time) } void -DCPEncoder::caption (PlayerCaption data, CaptionType type, DCPTimePeriod period) +DCPEncoder::caption (PlayerText data, TextType type, DCPTimePeriod period) { if (type == CAPTION_CLOSED || _non_burnt_subtitles) { _writer->write (data, type, period); diff --git a/src/lib/dcp_encoder.h b/src/lib/dcp_encoder.h index 9808987c3..23b05d0dc 100644 --- a/src/lib/dcp_encoder.h +++ b/src/lib/dcp_encoder.h @@ -19,7 +19,7 @@ */ #include "types.h" -#include "player_caption.h" +#include "player_text.h" #include "encoder.h" #include @@ -52,7 +52,7 @@ private: void video (boost::shared_ptr, DCPTime); void audio (boost::shared_ptr, DCPTime); - void caption (PlayerCaption, CaptionType, DCPTimePeriod); + void caption (PlayerText, TextType, DCPTimePeriod); boost::shared_ptr _writer; boost::shared_ptr _j2k_encoder; diff --git a/src/lib/dcp_examiner.h b/src/lib/dcp_examiner.h index 16385e07a..29fcc48d2 100644 --- a/src/lib/dcp_examiner.h +++ b/src/lib/dcp_examiner.h @@ -83,7 +83,7 @@ public: return _audio_frame_rate.get_value_or (48000); } - bool has_caption (CaptionType type) const { + bool has_caption (TextType type) const { return _has_caption[type]; } diff --git a/src/lib/dcp_subtitle_content.cc b/src/lib/dcp_subtitle_content.cc index 779361f63..6a7e38153 100644 --- a/src/lib/dcp_subtitle_content.cc +++ b/src/lib/dcp_subtitle_content.cc @@ -21,7 +21,7 @@ #include "font.h" #include "dcp_subtitle_content.h" #include "film.h" -#include "caption_content.h" +#include "text_content.h" #include #include #include @@ -40,14 +40,14 @@ using dcp::raw_convert; DCPSubtitleContent::DCPSubtitleContent (shared_ptr film, boost::filesystem::path path) : Content (film, path) { - caption.push_back (shared_ptr (new CaptionContent (this, CAPTION_OPEN))); + caption.push_back (shared_ptr (new TextContent (this, CAPTION_OPEN))); } DCPSubtitleContent::DCPSubtitleContent (shared_ptr film, cxml::ConstNodePtr node, int version) : Content (film, node) , _length (node->number_child ("Length")) { - caption = CaptionContent::from_xml (this, node, version); + caption = TextContent::from_xml (this, node, version); } void diff --git a/src/lib/dcp_subtitle_decoder.cc b/src/lib/dcp_subtitle_decoder.cc index 3ed4a6827..05d815265 100644 --- a/src/lib/dcp_subtitle_decoder.cc +++ b/src/lib/dcp_subtitle_decoder.cc @@ -39,7 +39,7 @@ DCPSubtitleDecoder::DCPSubtitleDecoder (shared_ptr con if (_next != _subtitles.end()) { first = content_time_period(*_next).from; } - caption.push_back (shared_ptr (new CaptionDecoder (this, content->only_caption(), log, first))); + caption.push_back (shared_ptr (new TextDecoder (this, content->only_caption(), log, first))); } void @@ -64,7 +64,7 @@ DCPSubtitleDecoder::pass () /* Gather all subtitles with the same time period that are next on the list. We must emit all subtitles for the same time period with the same plain_text() call otherwise the - CaptionDecoder will assume there is nothing else at the + TextDecoder will assume there is nothing else at the time of emit the first. */ diff --git a/src/lib/dcp_subtitle_decoder.h b/src/lib/dcp_subtitle_decoder.h index ef4dad3f5..984d98826 100644 --- a/src/lib/dcp_subtitle_decoder.h +++ b/src/lib/dcp_subtitle_decoder.h @@ -18,7 +18,7 @@ */ -#include "caption_decoder.h" +#include "text_decoder.h" #include "dcp_subtitle.h" class DCPSubtitleContent; diff --git a/src/lib/decoder.cc b/src/lib/decoder.cc index 2fddddc91..52949a098 100644 --- a/src/lib/decoder.cc +++ b/src/lib/decoder.cc @@ -21,7 +21,7 @@ #include "decoder.h" #include "video_decoder.h" #include "audio_decoder.h" -#include "caption_decoder.h" +#include "text_decoder.h" #include #include @@ -43,7 +43,7 @@ Decoder::position () const pos = audio->position(); } - BOOST_FOREACH (shared_ptr i, caption) { + BOOST_FOREACH (shared_ptr i, caption) { if (!i->ignore() && (!pos || i->position() < *pos)) { pos = i->position(); } @@ -61,17 +61,17 @@ Decoder::seek (ContentTime, bool) if (audio) { audio->seek (); } - BOOST_FOREACH (shared_ptr i, caption) { + BOOST_FOREACH (shared_ptr i, caption) { i->seek (); } } -shared_ptr +shared_ptr Decoder::only_caption () const { DCPOMATIC_ASSERT (caption.size() < 2); if (caption.empty ()) { - return shared_ptr (); + return shared_ptr (); } return caption.front (); } diff --git a/src/lib/decoder.h b/src/lib/decoder.h index d48df7517..1b330316e 100644 --- a/src/lib/decoder.h +++ b/src/lib/decoder.h @@ -32,7 +32,7 @@ class Decoded; class VideoDecoder; class AudioDecoder; -class CaptionDecoder; +class TextDecoder; class DecoderPart; /** @class Decoder. @@ -45,9 +45,9 @@ public: boost::shared_ptr video; boost::shared_ptr audio; - std::list > caption; + std::list > caption; - boost::shared_ptr only_caption () const; + boost::shared_ptr only_caption () const; /** Do some decoding and perhaps emit video, audio or subtitle data. * @return true if this decoder will emit no more data unless a seek() happens. diff --git a/src/lib/decoder_factory.cc b/src/lib/decoder_factory.cc index bb3bdb13e..02d7064f4 100644 --- a/src/lib/decoder_factory.cc +++ b/src/lib/decoder_factory.cc @@ -24,8 +24,8 @@ #include "dcp_decoder.h" #include "image_content.h" #include "image_decoder.h" -#include "text_caption_file_content.h" -#include "text_caption_file_decoder.h" +#include "string_text_file_content.h" +#include "string_text_file_decoder.h" #include "dcp_subtitle_content.h" #include "dcp_subtitle_decoder.h" #include "video_mxf_content.h" @@ -54,9 +54,9 @@ decoder_factory (shared_ptr content, shared_ptr log, bool fa return shared_ptr (new ImageDecoder (ic, log)); } - shared_ptr rc = dynamic_pointer_cast (content); + shared_ptr rc = dynamic_pointer_cast (content); if (rc) { - return shared_ptr (new TextCaptionFileDecoder (rc, log)); + return shared_ptr (new StringTextFileDecoder (rc, log)); } shared_ptr dsc = dynamic_pointer_cast (content); diff --git a/src/lib/encoder.h b/src/lib/encoder.h index 27fa0745f..f4116c50c 100644 --- a/src/lib/encoder.h +++ b/src/lib/encoder.h @@ -22,7 +22,7 @@ #define DCPOMATIC_ENCODER_H #include "types.h" -#include "player_caption.h" +#include "player_text.h" #include #include diff --git a/src/lib/ffmpeg_content.cc b/src/lib/ffmpeg_content.cc index 3f0a692cd..86965ff49 100644 --- a/src/lib/ffmpeg_content.cc +++ b/src/lib/ffmpeg_content.cc @@ -32,7 +32,7 @@ #include "log.h" #include "exceptions.h" #include "frame_rate_change.h" -#include "caption_content.h" +#include "text_content.h" #include #include extern "C" { @@ -85,7 +85,7 @@ FFmpegContent::FFmpegContent (shared_ptr film, cxml::ConstNodePtr no { video = VideoContent::from_xml (this, node, version); audio = AudioContent::from_xml (this, node, version); - caption = CaptionContent::from_xml (this, node, version); + caption = TextContent::from_xml (this, node, version); list c = node->node_children ("SubtitleStream"); for (list::const_iterator i = c.begin(); i != c.end(); ++i) { @@ -163,7 +163,7 @@ FFmpegContent::FFmpegContent (shared_ptr film, vector (new CaptionContent (this, c))); + caption.push_back (shared_ptr (new TextContent (this, c))); } shared_ptr ref = dynamic_pointer_cast (c[0]); @@ -304,7 +304,7 @@ FFmpegContent::examine (shared_ptr job) _subtitle_streams = examiner->subtitle_streams (); if (!_subtitle_streams.empty ()) { caption.clear (); - caption.push_back (shared_ptr (new CaptionContent (this, CAPTION_OPEN))); + caption.push_back (shared_ptr (new TextContent (this, CAPTION_OPEN))); _subtitle_stream = _subtitle_streams.front (); } diff --git a/src/lib/ffmpeg_decoder.cc b/src/lib/ffmpeg_decoder.cc index 1f2fcfef8..665ee3e13 100644 --- a/src/lib/ffmpeg_decoder.cc +++ b/src/lib/ffmpeg_decoder.cc @@ -28,7 +28,7 @@ #include "util.h" #include "log.h" #include "ffmpeg_decoder.h" -#include "caption_decoder.h" +#include "text_decoder.h" #include "ffmpeg_audio_stream.h" #include "ffmpeg_subtitle_stream.h" #include "video_filter_graph.h" @@ -39,7 +39,7 @@ #include "film.h" #include "audio_decoder.h" #include "compose.hpp" -#include "caption_content.h" +#include "text_content.h" #include "audio_content.h" #include #include @@ -99,7 +99,7 @@ FFmpegDecoder::FFmpegDecoder (shared_ptr c, shared_ptr if (c->only_caption()) { /* XXX: this time here should be the time of the first subtitle, not 0 */ - caption.push_back (shared_ptr (new CaptionDecoder (this, c->only_caption(), log, ContentTime()))); + caption.push_back (shared_ptr (new TextDecoder (this, c->only_caption(), log, ContentTime()))); } _next_time.resize (_format_context->nb_streams); diff --git a/src/lib/ffmpeg_encoder.cc b/src/lib/ffmpeg_encoder.cc index 29334e8ed..f82290c6c 100644 --- a/src/lib/ffmpeg_encoder.cc +++ b/src/lib/ffmpeg_encoder.cc @@ -411,7 +411,7 @@ FFmpegEncoder::audio_frame (int size) } void -FFmpegEncoder::subtitle (PlayerCaption, DCPTimePeriod) +FFmpegEncoder::subtitle (PlayerText, DCPTimePeriod) { } diff --git a/src/lib/ffmpeg_encoder.h b/src/lib/ffmpeg_encoder.h index 35fd85064..b1d07eb7f 100644 --- a/src/lib/ffmpeg_encoder.h +++ b/src/lib/ffmpeg_encoder.h @@ -54,7 +54,7 @@ public: private: void video (boost::shared_ptr, DCPTime); void audio (boost::shared_ptr); - void subtitle (PlayerCaption, DCPTimePeriod); + void subtitle (PlayerText, DCPTimePeriod); void setup_video (); void setup_audio (); diff --git a/src/lib/film.cc b/src/lib/film.cc index b56f85dbe..475d28b4f 100644 --- a/src/lib/film.cc +++ b/src/lib/film.cc @@ -46,7 +46,7 @@ #include "screen.h" #include "audio_content.h" #include "video_content.h" -#include "caption_content.h" +#include "text_content.h" #include "ffmpeg_content.h" #include "dcp_content.h" #include "screen_kdm.h" @@ -125,7 +125,7 @@ string const Film::metadata_file = "metadata.xml"; * 35 -> 36 * EffectColour rather than OutlineColour in Subtitle. * 36 -> 37 - * CaptionContent can be in a Caption tag, and some of the tag names + * TextContent can be in a Caption tag, and some of the tag names * have had Subtitle prefixes or suffixes removed. */ int const Film::current_state_version = 37; @@ -704,7 +704,7 @@ Film::isdcf_name (bool if_created_now) const bool burnt_in = true; bool ccap = false; BOOST_FOREACH (shared_ptr i, content()) { - BOOST_FOREACH (shared_ptr j, i->caption) { + BOOST_FOREACH (shared_ptr j, i->caption) { if (j->type() == CAPTION_OPEN && j->use() && !j->burn()) { burnt_in = false; } else if (j->type() == CAPTION_CLOSED) { @@ -784,7 +784,7 @@ Film::isdcf_name (bool if_created_now) const bool any_caption = false; for (int i = 0; i < CAPTION_COUNT; ++i) { - if (dc->reference_caption(static_cast(i))) { + if (dc->reference_caption(static_cast(i))) { any_caption = true; } } @@ -1391,7 +1391,7 @@ Film::subtitle_language () const set languages; BOOST_FOREACH (shared_ptr i, content()) { - BOOST_FOREACH (shared_ptr j, i->caption) { + BOOST_FOREACH (shared_ptr j, i->caption) { languages.insert (j->language ()); } } diff --git a/src/lib/hints.cc b/src/lib/hints.cc index 33c2faba5..8cc0cfbfb 100644 --- a/src/lib/hints.cc +++ b/src/lib/hints.cc @@ -23,7 +23,7 @@ #include "film.h" #include "content.h" #include "video_content.h" -#include "caption_content.h" +#include "text_content.h" #include "audio_processor.h" #include "font.h" #include "ratio.h" @@ -56,7 +56,7 @@ get_hints (shared_ptr film) bool big_font_files = false; if (film->interop ()) { BOOST_FOREACH (shared_ptr i, content) { - BOOST_FOREACH (shared_ptr j, i->caption) { + BOOST_FOREACH (shared_ptr j, i->caption) { BOOST_FOREACH (shared_ptr k, j->fonts()) { for (int l = 0; l < FontFiles::VARIANTS; ++l) { optional const p = k->file (static_cast(l)); diff --git a/src/lib/player.cc b/src/lib/player.cc index 4635233ff..22526edef 100644 --- a/src/lib/player.cc +++ b/src/lib/player.cc @@ -40,8 +40,8 @@ #include "decoder.h" #include "video_decoder.h" #include "audio_decoder.h" -#include "caption_content.h" -#include "caption_decoder.h" +#include "text_content.h" +#include "text_decoder.h" #include "ffmpeg_content.h" #include "audio_content.h" #include "dcp_decoder.h" @@ -138,7 +138,7 @@ Player::setup_pieces () } if (_ignore_caption) { - BOOST_FOREACH (shared_ptr i, decoder->caption) { + BOOST_FOREACH (shared_ptr i, decoder->caption) { i->set_ignore (true); } } @@ -167,17 +167,17 @@ Player::setup_pieces () decoder->audio->Data.connect (bind (&Player::audio, this, weak_ptr (piece), _1, _2)); } - list >::const_iterator j = decoder->caption.begin(); + list >::const_iterator j = decoder->caption.begin(); while (j != decoder->caption.end()) { (*j)->BitmapStart.connect ( - bind(&Player::bitmap_text_start, this, weak_ptr(piece), weak_ptr((*j)->content()), _1) + bind(&Player::bitmap_text_start, this, weak_ptr(piece), weak_ptr((*j)->content()), _1) ); (*j)->PlainStart.connect ( - bind(&Player::plain_text_start, this, weak_ptr(piece), weak_ptr((*j)->content()), _1) + bind(&Player::plain_text_start, this, weak_ptr(piece), weak_ptr((*j)->content()), _1) ); (*j)->Stop.connect ( - bind(&Player::subtitle_stop, this, weak_ptr(piece), weak_ptr((*j)->content()), _1, _2) + bind(&Player::subtitle_stop, this, weak_ptr(piece), weak_ptr((*j)->content()), _1, _2) ); ++j; @@ -221,9 +221,9 @@ Player::playlist_content_changed (weak_ptr w, int property, bool freque property == AudioContentProperty::STREAMS || property == DCPContentProperty::NEEDS_ASSETS || property == DCPContentProperty::NEEDS_KDM || - property == CaptionContentProperty::COLOUR || - property == CaptionContentProperty::EFFECT || - property == CaptionContentProperty::EFFECT_COLOUR || + property == TextContentProperty::COLOUR || + property == TextContentProperty::EFFECT || + property == TextContentProperty::EFFECT_COLOUR || property == FFmpegContentProperty::SUBTITLE_STREAM || property == FFmpegContentProperty::FILTERS ) { @@ -232,18 +232,18 @@ Player::playlist_content_changed (weak_ptr w, int property, bool freque Changed (property, frequent); } else if ( - property == CaptionContentProperty::LINE_SPACING || - property == CaptionContentProperty::OUTLINE_WIDTH || - property == CaptionContentProperty::Y_SCALE || - property == CaptionContentProperty::FADE_IN || - property == CaptionContentProperty::FADE_OUT || + property == TextContentProperty::LINE_SPACING || + property == TextContentProperty::OUTLINE_WIDTH || + property == TextContentProperty::Y_SCALE || + property == TextContentProperty::FADE_IN || + property == TextContentProperty::FADE_OUT || property == ContentProperty::VIDEO_FRAME_RATE || - property == CaptionContentProperty::USE || - property == CaptionContentProperty::X_OFFSET || - property == CaptionContentProperty::Y_OFFSET || - property == CaptionContentProperty::X_SCALE || - property == CaptionContentProperty::FONTS || - property == CaptionContentProperty::TYPE || + property == TextContentProperty::USE || + property == TextContentProperty::X_OFFSET || + property == TextContentProperty::Y_OFFSET || + property == TextContentProperty::X_SCALE || + property == TextContentProperty::FONTS || + property == TextContentProperty::TYPE || property == VideoContentProperty::CROP || property == VideoContentProperty::SCALE || property == VideoContentProperty::FADE_IN || @@ -302,11 +302,11 @@ Player::film_changed (Film::Property p) } list -Player::transform_bitmap_captions (list subs) const +Player::transform_bitmap_captions (list subs) const { list all; - for (list::const_iterator i = subs.begin(); i != subs.end(); ++i) { + for (list::const_iterator i = subs.begin(); i != subs.end(); ++i) { if (!i->image) { continue; } @@ -419,7 +419,7 @@ Player::get_subtitle_fonts () list > fonts; BOOST_FOREACH (shared_ptr i, _pieces) { - BOOST_FOREACH (shared_ptr j, i->content->caption) { + BOOST_FOREACH (shared_ptr j, i->content->caption) { /* XXX: things may go wrong if there are duplicate font IDs with different font files. */ @@ -676,7 +676,7 @@ Player::pass () return done; } -list +list Player::closed_captions_for_frame (DCPTime time) const { return _active_captions[CAPTION_CLOSED].get ( @@ -692,7 +692,7 @@ Player::open_captions_for_frame (DCPTime time) const int const vfr = _film->video_frame_rate(); BOOST_FOREACH ( - PlayerCaption j, + PlayerText j, _active_captions[CAPTION_OPEN].get_burnt(DCPTimePeriod(time, time + DCPTime::from_frames(1, vfr)), _always_burn_open_captions) ) { @@ -870,10 +870,10 @@ Player::audio (weak_ptr wp, AudioStreamPtr stream, ContentAudio content_a } void -Player::bitmap_text_start (weak_ptr wp, weak_ptr wc, ContentBitmapCaption subtitle) +Player::bitmap_text_start (weak_ptr wp, weak_ptr wc, ContentBitmapText subtitle) { shared_ptr piece = wp.lock (); - shared_ptr caption = wc.lock (); + shared_ptr caption = wc.lock (); if (!piece || !caption) { return; } @@ -890,7 +890,7 @@ Player::bitmap_text_start (weak_ptr wp, weak_ptr wc subtitle.sub.rectangle.width *= caption->x_scale (); subtitle.sub.rectangle.height *= caption->y_scale (); - PlayerCaption ps; + PlayerText ps; ps.image.push_back (subtitle.sub); DCPTime from (content_time_to_dcp (piece, subtitle.from())); @@ -898,15 +898,15 @@ Player::bitmap_text_start (weak_ptr wp, weak_ptr wc } void -Player::plain_text_start (weak_ptr wp, weak_ptr wc, ContentTextCaption subtitle) +Player::plain_text_start (weak_ptr wp, weak_ptr wc, ContentStringText subtitle) { shared_ptr piece = wp.lock (); - shared_ptr caption = wc.lock (); + shared_ptr caption = wc.lock (); if (!piece || !caption) { return; } - PlayerCaption ps; + PlayerText ps; DCPTime const from (content_time_to_dcp (piece, subtitle.from())); if (from > piece->content->end()) { @@ -934,7 +934,7 @@ Player::plain_text_start (weak_ptr wp, weak_ptr wc, } s.set_in (dcp::Time(from.seconds(), 1000)); - ps.text.push_back (TextCaption (s, caption->outline_width())); + ps.text.push_back (StringText (s, caption->outline_width())); ps.add_fonts (caption->fonts ()); } @@ -942,14 +942,14 @@ Player::plain_text_start (weak_ptr wp, weak_ptr wc, } void -Player::subtitle_stop (weak_ptr wp, weak_ptr wc, ContentTime to, CaptionType type) +Player::subtitle_stop (weak_ptr wp, weak_ptr wc, ContentTime to, TextType type) { if (!_active_captions[type].have (wc)) { return; } shared_ptr piece = wp.lock (); - shared_ptr caption = wc.lock (); + shared_ptr caption = wc.lock (); if (!piece || !caption) { return; } @@ -960,7 +960,7 @@ Player::subtitle_stop (weak_ptr wp, weak_ptr wc, Co return; } - pair from = _active_captions[type].add_to (wc, dcp_to); + pair from = _active_captions[type].add_to (wc, dcp_to); bool const always = type == CAPTION_OPEN && _always_burn_open_captions; if (caption->use() && !always && !caption->burn()) { diff --git a/src/lib/player.h b/src/lib/player.h index c086b9697..280373738 100644 --- a/src/lib/player.h +++ b/src/lib/player.h @@ -21,9 +21,9 @@ #ifndef DCPOMATIC_PLAYER_H #define DCPOMATIC_PLAYER_H -#include "player_caption.h" -#include "active_captions.h" -#include "content_caption.h" +#include "player_text.h" +#include "active_text.h" +#include "content_text.h" #include "film.h" #include "content.h" #include "position_image.h" @@ -86,7 +86,7 @@ public: DCPTime content_time_to_dcp (boost::shared_ptr content, ContentTime t); - std::list closed_captions_for_frame (DCPTime time) const; + std::list 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 @@ -103,7 +103,7 @@ public: /** Emitted when a caption is ready. This signal may be emitted considerably * after the corresponding Video. */ - boost::signals2::signal Caption; + boost::signals2::signal Caption; private: friend class PlayerWrapper; @@ -118,7 +118,7 @@ private: void film_changed (Film::Property); void playlist_changed (); void playlist_content_changed (boost::weak_ptr, int, bool); - std::list transform_bitmap_captions (std::list) const; + std::list transform_bitmap_captions (std::list) const; Frame dcp_to_content_video (boost::shared_ptr piece, DCPTime t) const; DCPTime content_video_to_dcp (boost::shared_ptr piece, Frame f) const; Frame dcp_to_resampled_audio (boost::shared_ptr piece, DCPTime t) const; @@ -128,9 +128,9 @@ private: boost::shared_ptr black_player_video_frame (Eyes eyes) const; void video (boost::weak_ptr, ContentVideo); void audio (boost::weak_ptr, AudioStreamPtr, ContentAudio); - void bitmap_text_start (boost::weak_ptr, boost::weak_ptr, ContentBitmapCaption); - void plain_text_start (boost::weak_ptr, boost::weak_ptr, ContentTextCaption); - void subtitle_stop (boost::weak_ptr, boost::weak_ptr, ContentTime, CaptionType); + void bitmap_text_start (boost::weak_ptr, boost::weak_ptr, ContentBitmapText); + void plain_text_start (boost::weak_ptr, boost::weak_ptr, ContentStringText); + void subtitle_stop (boost::weak_ptr, boost::weak_ptr, ContentTime, TextType); DCPTime one_video_frame () const; void fill_audio (DCPTimePeriod period); std::pair, DCPTime> discard_audio ( @@ -195,7 +195,7 @@ private: Empty _black; Empty _silent; - ActiveCaptions _active_captions[CAPTION_COUNT]; + ActiveText _active_captions[CAPTION_COUNT]; boost::shared_ptr _audio_processor; boost::signals2::scoped_connection _film_changed_connection; diff --git a/src/lib/player_caption.cc b/src/lib/player_caption.cc deleted file mode 100644 index 74c847aae..000000000 --- a/src/lib/player_caption.cc +++ /dev/null @@ -1,42 +0,0 @@ -/* - Copyright (C) 2014-2018 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 "player_caption.h" -#include "font.h" -#include - -using std::list; -using boost::shared_ptr; - -void -PlayerCaption::add_fonts (list > fonts_) -{ - BOOST_FOREACH (shared_ptr i, fonts_) { - bool got = false; - BOOST_FOREACH (shared_ptr j, fonts) { - if (*i == *j) { - got = true; - } - } - if (!got) { - fonts.push_back (i); - } - } -} diff --git a/src/lib/player_caption.h b/src/lib/player_caption.h deleted file mode 100644 index 2f7e909aa..000000000 --- a/src/lib/player_caption.h +++ /dev/null @@ -1,42 +0,0 @@ -/* - Copyright (C) 2014-2018 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 . - -*/ - -#ifndef DCPOMATIC_PLAYER_CAPTION_H -#define DCPOMATIC_PLAYER_CAPTION_H - -#include "bitmap_caption.h" -#include "dcpomatic_time.h" -#include "text_caption.h" - -class Font; - -/** A set of text (subtitle/CCAP) which span the same time period */ -class PlayerCaption -{ -public: - void add_fonts (std::list > fonts_); - std::list > fonts; - - /** BitmapCaptions, with their rectangles transformed as specified by their content */ - std::list image; - std::list text; -}; - -#endif diff --git a/src/lib/player_text.cc b/src/lib/player_text.cc new file mode 100644 index 000000000..16e89b0f1 --- /dev/null +++ b/src/lib/player_text.cc @@ -0,0 +1,42 @@ +/* + Copyright (C) 2014-2018 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 "player_text.h" +#include "font.h" +#include + +using std::list; +using boost::shared_ptr; + +void +PlayerText::add_fonts (list > fonts_) +{ + BOOST_FOREACH (shared_ptr i, fonts_) { + bool got = false; + BOOST_FOREACH (shared_ptr j, fonts) { + if (*i == *j) { + got = true; + } + } + if (!got) { + fonts.push_back (i); + } + } +} diff --git a/src/lib/player_text.h b/src/lib/player_text.h new file mode 100644 index 000000000..5a8799116 --- /dev/null +++ b/src/lib/player_text.h @@ -0,0 +1,42 @@ +/* + Copyright (C) 2014-2018 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 . + +*/ + +#ifndef DCPOMATIC_PLAYER_CAPTION_H +#define DCPOMATIC_PLAYER_CAPTION_H + +#include "bitmap_text.h" +#include "dcpomatic_time.h" +#include "string_text.h" + +class Font; + +/** A set of text (subtitle/CCAP) which span the same time period */ +class PlayerText +{ +public: + void add_fonts (std::list > fonts_); + std::list > fonts; + + /** BitmapTexts, with their rectangles transformed as specified by their content */ + std::list image; + std::list text; +}; + +#endif diff --git a/src/lib/playlist.cc b/src/lib/playlist.cc index 6c7fd7f4e..a55893291 100644 --- a/src/lib/playlist.cc +++ b/src/lib/playlist.cc @@ -20,7 +20,7 @@ #include "playlist.h" #include "video_content.h" -#include "caption_content.h" +#include "text_content.h" #include "ffmpeg_decoder.h" #include "ffmpeg_content.h" #include "image_decoder.h" @@ -156,7 +156,7 @@ Playlist::video_identifier () const BOOST_FOREACH (shared_ptr i, _content) { bool burn = false; - BOOST_FOREACH (shared_ptr j, i->caption) { + BOOST_FOREACH (shared_ptr j, i->caption) { if (j->burn()) { burn = true; } diff --git a/src/lib/reel_writer.cc b/src/lib/reel_writer.cc index 1836b6543..7f5572c36 100644 --- a/src/lib/reel_writer.cc +++ b/src/lib/reel_writer.cc @@ -545,7 +545,7 @@ ReelWriter::write (shared_ptr audio) } void -ReelWriter::write (PlayerCaption subs, CaptionType type, DCPTimePeriod period) +ReelWriter::write (PlayerText subs, TextType type, DCPTimePeriod period) { if (!_caption_asset[type]) { string lang = _film->subtitle_language (); @@ -573,14 +573,14 @@ ReelWriter::write (PlayerCaption subs, CaptionType type, DCPTimePeriod period) } } - BOOST_FOREACH (TextCaption i, subs.text) { + BOOST_FOREACH (StringText i, subs.text) { /* XXX: couldn't / shouldn't we use period here rather than getting time from the subtitle? */ i.set_in (i.in() - dcp::Time (_period.from.seconds(), i.in().tcr)); i.set_out (i.out() - dcp::Time (_period.from.seconds(), i.out().tcr)); _caption_asset[type]->add (shared_ptr(new dcp::SubtitleString(i))); } - BOOST_FOREACH (BitmapCaption i, subs.image) { + BOOST_FOREACH (BitmapText i, subs.image) { _caption_asset[type]->add ( shared_ptr( new dcp::SubtitleImage( diff --git a/src/lib/reel_writer.h b/src/lib/reel_writer.h index 24a5e9a8a..8007b7f29 100644 --- a/src/lib/reel_writer.h +++ b/src/lib/reel_writer.h @@ -21,7 +21,7 @@ #include "types.h" #include "dcpomatic_time.h" #include "referenced_reel_asset.h" -#include "player_caption.h" +#include "player_text.h" #include #include @@ -60,7 +60,7 @@ public: void fake_write (Frame frame, Eyes eyes, int size); void repeat_write (Frame frame, Eyes eyes); void write (boost::shared_ptr audio); - void write (PlayerCaption text, CaptionType type, DCPTimePeriod period); + void write (PlayerText text, TextType type, DCPTimePeriod period); void finish (); boost::shared_ptr create_reel (std::list const & refs, std::list > const & fonts); diff --git a/src/lib/render_text.cc b/src/lib/render_text.cc index 63ff2b74c..cbc19efc9 100644 --- a/src/lib/render_text.cc +++ b/src/lib/render_text.cc @@ -51,11 +51,11 @@ static FcConfig* fc_config = 0; static list > fc_config_fonts; string -marked_up (list subtitles, int target_height, float fade_factor) +marked_up (list subtitles, int target_height, float fade_factor) { string out; - BOOST_FOREACH (TextCaption const & i, subtitles) { + BOOST_FOREACH (StringText const & i, subtitles) { out += " context, dcp::Colour colour, floa * at the same time and with the same fade in/out. */ static PositionImage -render_line (list subtitles, list > fonts, dcp::Size target, DCPTime time, int frame_rate) +render_line (list subtitles, list > fonts, dcp::Size target, DCPTime time, int frame_rate) { /* XXX: this method can only handle italic / bold changes mid-line, nothing else yet. @@ -365,12 +365,12 @@ render_line (list subtitles, list > fonts, dcp::Si * @param frame_rate DCP frame rate. */ list -render_text (list subtitles, list > fonts, dcp::Size target, DCPTime time, int frame_rate) +render_text (list subtitles, list > fonts, dcp::Size target, DCPTime time, int frame_rate) { - list pending; + list pending; list images; - BOOST_FOREACH (TextCaption const & i, subtitles) { + BOOST_FOREACH (StringText const & i, subtitles) { if (!pending.empty() && fabs (i.v_position() - pending.back().v_position()) > 1e-4) { images.push_back (render_line (pending, fonts, target, time, frame_rate)); pending.clear (); diff --git a/src/lib/render_text.h b/src/lib/render_text.h index 0a8e97bc9..99b4391db 100644 --- a/src/lib/render_text.h +++ b/src/lib/render_text.h @@ -20,12 +20,12 @@ #include "position_image.h" #include "dcpomatic_time.h" -#include "text_caption.h" +#include "string_text.h" #include class Font; -std::string marked_up (std::list subtitles, int target_height, float fade_factor); +std::string marked_up (std::list subtitles, int target_height, float fade_factor); std::list render_text ( - std::list, std::list > fonts, dcp::Size, DCPTime, int + std::list, std::list > fonts, dcp::Size, DCPTime, int ); diff --git a/src/lib/string_text.h b/src/lib/string_text.h new file mode 100644 index 000000000..feef90d61 --- /dev/null +++ b/src/lib/string_text.h @@ -0,0 +1,46 @@ +/* + Copyright (C) 2016-2018 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 . + +*/ + +#ifndef DCPOMATIC_PLAIN_TEXT_H +#define DCPOMATIC_PLAIN_TEXT_H + +#include + +/** A wrapper for SubtitleString which allows us to include settings that are not + * applicable to true DCP subtitles. For example, we can set outline width for burn-in + * but this cannot be specified in DCP XML. + */ +class StringText : public dcp::SubtitleString +{ +public: + explicit StringText (dcp::SubtitleString dcp_) + : dcp::SubtitleString (dcp_) + , outline_width (2) + {} + + StringText (dcp::SubtitleString dcp_, int outline_width_) + : dcp::SubtitleString (dcp_) + , outline_width (outline_width_) + {} + + int outline_width; +}; + +#endif diff --git a/src/lib/string_text_file.cc b/src/lib/string_text_file.cc new file mode 100644 index 000000000..c7eadbbb1 --- /dev/null +++ b/src/lib/string_text_file.cc @@ -0,0 +1,116 @@ +/* + Copyright (C) 2014-2018 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 "string_text_file.h" +#include "cross.h" +#include "exceptions.h" +#include "string_text_file_content.h" +#include +#include +#include +#include +#include +#include + +#include "i18n.h" + +using std::vector; +using std::cout; +using std::string; +using boost::shared_ptr; +using boost::scoped_array; +using boost::optional; +using dcp::Data; + +StringTextFile::StringTextFile (shared_ptr content) +{ + Data in (content->path (0)); + + UErrorCode status = U_ZERO_ERROR; + UCharsetDetector* detector = ucsdet_open (&status); + ucsdet_setText (detector, reinterpret_cast (in.data().get()), in.size(), &status); + + UCharsetMatch const * match = ucsdet_detect (detector, &status); + char const * in_charset = ucsdet_getName (match, &status); + + UConverter* to_utf16 = ucnv_open (in_charset, &status); + /* This is a guess; I think we should be able to encode any input in 4 times its input size */ + scoped_array utf16 (new uint16_t[in.size() * 2]); + int const utf16_len = ucnv_toUChars ( + to_utf16, reinterpret_cast(utf16.get()), in.size() * 2, + reinterpret_cast (in.data().get()), in.size(), + &status + ); + + UConverter* to_utf8 = ucnv_open ("UTF-8", &status); + /* Another guess */ + scoped_array utf8 (new char[utf16_len * 2]); + ucnv_fromUChars (to_utf8, utf8.get(), utf16_len * 2, reinterpret_cast(utf16.get()), utf16_len, &status); + + /* Fix OS X line endings */ + size_t utf8_len = strlen (utf8.get ()); + for (size_t i = 0; i < utf8_len; ++i) { + if (utf8[i] == '\r' && ((i == utf8_len - 1) || utf8[i + 1] != '\n')) { + utf8[i] = '\n'; + } + } + + ucsdet_close (detector); + ucnv_close (to_utf16); + ucnv_close (to_utf8); + + sub::Reader* reader = 0; + + string ext = content->path(0).extension().string(); + transform (ext.begin(), ext.end(), ext.begin(), ::tolower); + + if (ext == ".srt") { + reader = new sub::SubripReader (utf8.get()); + } else if (ext == ".ssa" || ext == ".ass") { + reader = new sub::SSAReader (utf8.get()); + } + + if (reader) { + _subtitles = sub::collect > (reader->subtitles ()); + } + + delete reader; +} + +/** @return time of first subtitle, if there is one */ +optional +StringTextFile::first () const +{ + if (_subtitles.empty()) { + return optional(); + } + + return ContentTime::from_seconds(_subtitles[0].from.all_as_seconds()); +} + +ContentTime +StringTextFile::length () const +{ + if (_subtitles.empty ()) { + return ContentTime (); + } + + return ContentTime::from_seconds (_subtitles.back().to.all_as_seconds ()); +} diff --git a/src/lib/string_text_file.h b/src/lib/string_text_file.h new file mode 100644 index 000000000..0091973a4 --- /dev/null +++ b/src/lib/string_text_file.h @@ -0,0 +1,53 @@ +/* + Copyright (C) 2014-2018 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 . + +*/ + +#ifndef DCPOMATIC_TEXT_CAPTION_FILE_H +#define DCPOMATIC_TEXT_CAPTION_FILE_H + +#include "dcpomatic_time.h" +#include +#include +#include + +class StringTextFileContent; +class plain_text_time_test; +class plain_text_coordinate_test; +class plain_text_content_test; +class plain_text_parse_test; + +/** @class StringTextFile + * @brief Base for StringTextFile decoder and examiner. + * + * In fact this is sufficient for the examiner, so it's used as-is rather than deriving + * a pointless StringTextFileExaminer. + */ +class StringTextFile +{ +public: + explicit StringTextFile (boost::shared_ptr); + + boost::optional first () const; + ContentTime length () const; + +protected: + std::vector _subtitles; +}; + +#endif diff --git a/src/lib/string_text_file_content.cc b/src/lib/string_text_file_content.cc new file mode 100644 index 000000000..6c181bb91 --- /dev/null +++ b/src/lib/string_text_file_content.cc @@ -0,0 +1,95 @@ +/* + Copyright (C) 2014-2018 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 "string_text_file_content.h" +#include "util.h" +#include "string_text_file.h" +#include "film.h" +#include "font.h" +#include "text_content.h" +#include +#include +#include + +#include "i18n.h" + +using std::string; +using std::cout; +using boost::shared_ptr; +using dcp::raw_convert; + +StringTextFileContent::StringTextFileContent (shared_ptr film, boost::filesystem::path path) + : Content (film, path) +{ + caption.push_back (shared_ptr (new TextContent (this, CAPTION_OPEN))); +} + +StringTextFileContent::StringTextFileContent (shared_ptr film, cxml::ConstNodePtr node, int version) + : Content (film, node) + , _length (node->number_child ("Length")) +{ + caption = TextContent::from_xml (this, node, version); +} + +void +StringTextFileContent::examine (boost::shared_ptr job) +{ + Content::examine (job); + StringTextFile s (shared_from_this ()); + + /* Default to turning these subtitles on */ + only_caption()->set_use (true); + + boost::mutex::scoped_lock lm (_mutex); + _length = s.length (); + only_caption()->add_font (shared_ptr (new Font (TEXT_FONT_ID))); +} + +string +StringTextFileContent::summary () const +{ + return path_summary() + " " + _("[subtitles]"); +} + +string +StringTextFileContent::technical_summary () const +{ + return Content::technical_summary() + " - " + _("Text subtitles"); +} + +void +StringTextFileContent::as_xml (xmlpp::Node* node, bool with_paths) const +{ + node->add_child("Type")->add_child_text ("TextSubtitle"); + Content::as_xml (node, with_paths); + + if (only_caption()) { + only_caption()->as_xml (node); + } + + node->add_child("Length")->add_child_text (raw_convert (_length.get ())); +} + +DCPTime +StringTextFileContent::full_length () const +{ + FrameRateChange const frc (active_video_frame_rate(), film()->video_frame_rate ()); + return DCPTime (_length, frc); +} diff --git a/src/lib/string_text_file_content.h b/src/lib/string_text_file_content.h new file mode 100644 index 000000000..c2de86ce5 --- /dev/null +++ b/src/lib/string_text_file_content.h @@ -0,0 +1,46 @@ +/* + Copyright (C) 2014-2018 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 "content.h" + +class Job; + +/** @class StringTextFileContent + * @brief A SubRip, SSA or ASS file. + */ +class StringTextFileContent : public Content +{ +public: + StringTextFileContent (boost::shared_ptr, boost::filesystem::path); + StringTextFileContent (boost::shared_ptr, cxml::ConstNodePtr, int); + + boost::shared_ptr shared_from_this () { + return boost::dynamic_pointer_cast (Content::shared_from_this ()); + } + + void examine (boost::shared_ptr); + std::string summary () const; + std::string technical_summary () const; + void as_xml (xmlpp::Node *, bool with_paths) const; + DCPTime full_length () const; + +private: + ContentTime _length; +}; diff --git a/src/lib/string_text_file_decoder.cc b/src/lib/string_text_file_decoder.cc new file mode 100644 index 000000000..a81f2592d --- /dev/null +++ b/src/lib/string_text_file_decoder.cc @@ -0,0 +1,89 @@ +/* + Copyright (C) 2014-2018 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 "string_text_file_decoder.h" +#include "string_text_file_content.h" +#include "text_content.h" +#include "text_decoder.h" +#include +#include +#include + +using std::list; +using std::vector; +using std::string; +using std::cout; +using std::max; +using boost::shared_ptr; +using boost::optional; +using boost::dynamic_pointer_cast; + +StringTextFileDecoder::StringTextFileDecoder (shared_ptr content, shared_ptr log) + : StringTextFile (content) + , _next (0) +{ + ContentTime first; + if (!_subtitles.empty()) { + first = content_time_period(_subtitles[0]).from; + } + caption.push_back (shared_ptr (new TextDecoder (this, content->only_caption(), log, first))); +} + +void +StringTextFileDecoder::seek (ContentTime time, bool accurate) +{ + /* It's worth back-tracking a little here as decoding is cheap and it's nice if we don't miss + too many subtitles when seeking. + */ + time -= ContentTime::from_seconds (5); + if (time < ContentTime()) { + time = ContentTime(); + } + + Decoder::seek (time, accurate); + + _next = 0; + while (_next < _subtitles.size() && ContentTime::from_seconds (_subtitles[_next].from.all_as_seconds ()) < time) { + ++_next; + } +} + +bool +StringTextFileDecoder::pass () +{ + if (_next >= _subtitles.size ()) { + return true; + } + + ContentTimePeriod const p = content_time_period (_subtitles[_next]); + only_caption()->emit_plain (p, _subtitles[_next]); + + ++_next; + return false; +} + +ContentTimePeriod +StringTextFileDecoder::content_time_period (sub::Subtitle s) const +{ + return ContentTimePeriod ( + ContentTime::from_seconds (s.from.all_as_seconds()), + ContentTime::from_seconds (s.to.all_as_seconds()) + ); +} diff --git a/src/lib/string_text_file_decoder.h b/src/lib/string_text_file_decoder.h new file mode 100644 index 000000000..6a9552bbe --- /dev/null +++ b/src/lib/string_text_file_decoder.h @@ -0,0 +1,44 @@ +/* + Copyright (C) 2014-2018 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 . + +*/ + +#ifndef DCPOMATIC_TEXT_CAPTION_FILE_DECODER_H +#define DCPOMATIC_TEXT_CAPTION_FILE_DECODER_H + +#include "string_text_file.h" +#include "decoder.h" + +class StringTextFileContent; +class Log; + +class StringTextFileDecoder : public Decoder, public StringTextFile +{ +public: + StringTextFileDecoder (boost::shared_ptr, boost::shared_ptr log); + + void seek (ContentTime time, bool accurate); + bool pass (); + +private: + ContentTimePeriod content_time_period (sub::Subtitle s) const; + + size_t _next; +}; + +#endif diff --git a/src/lib/text_caption.h b/src/lib/text_caption.h deleted file mode 100644 index b4400f5bb..000000000 --- a/src/lib/text_caption.h +++ /dev/null @@ -1,46 +0,0 @@ -/* - Copyright (C) 2016-2018 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 . - -*/ - -#ifndef DCPOMATIC_PLAIN_TEXT_H -#define DCPOMATIC_PLAIN_TEXT_H - -#include - -/** A wrapper for SubtitleString which allows us to include settings that are not - * applicable to true DCP subtitles. For example, we can set outline width for burn-in - * but this cannot be specified in DCP XML. - */ -class TextCaption : public dcp::SubtitleString -{ -public: - explicit TextCaption (dcp::SubtitleString dcp_) - : dcp::SubtitleString (dcp_) - , outline_width (2) - {} - - TextCaption (dcp::SubtitleString dcp_, int outline_width_) - : dcp::SubtitleString (dcp_) - , outline_width (outline_width_) - {} - - int outline_width; -}; - -#endif diff --git a/src/lib/text_caption_file.cc b/src/lib/text_caption_file.cc deleted file mode 100644 index 11d4517db..000000000 --- a/src/lib/text_caption_file.cc +++ /dev/null @@ -1,116 +0,0 @@ -/* - Copyright (C) 2014-2018 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_caption_file.h" -#include "cross.h" -#include "exceptions.h" -#include "text_caption_file_content.h" -#include -#include -#include -#include -#include -#include - -#include "i18n.h" - -using std::vector; -using std::cout; -using std::string; -using boost::shared_ptr; -using boost::scoped_array; -using boost::optional; -using dcp::Data; - -TextCaptionFile::TextCaptionFile (shared_ptr content) -{ - Data in (content->path (0)); - - UErrorCode status = U_ZERO_ERROR; - UCharsetDetector* detector = ucsdet_open (&status); - ucsdet_setText (detector, reinterpret_cast (in.data().get()), in.size(), &status); - - UCharsetMatch const * match = ucsdet_detect (detector, &status); - char const * in_charset = ucsdet_getName (match, &status); - - UConverter* to_utf16 = ucnv_open (in_charset, &status); - /* This is a guess; I think we should be able to encode any input in 4 times its input size */ - scoped_array utf16 (new uint16_t[in.size() * 2]); - int const utf16_len = ucnv_toUChars ( - to_utf16, reinterpret_cast(utf16.get()), in.size() * 2, - reinterpret_cast (in.data().get()), in.size(), - &status - ); - - UConverter* to_utf8 = ucnv_open ("UTF-8", &status); - /* Another guess */ - scoped_array utf8 (new char[utf16_len * 2]); - ucnv_fromUChars (to_utf8, utf8.get(), utf16_len * 2, reinterpret_cast(utf16.get()), utf16_len, &status); - - /* Fix OS X line endings */ - size_t utf8_len = strlen (utf8.get ()); - for (size_t i = 0; i < utf8_len; ++i) { - if (utf8[i] == '\r' && ((i == utf8_len - 1) || utf8[i + 1] != '\n')) { - utf8[i] = '\n'; - } - } - - ucsdet_close (detector); - ucnv_close (to_utf16); - ucnv_close (to_utf8); - - sub::Reader* reader = 0; - - string ext = content->path(0).extension().string(); - transform (ext.begin(), ext.end(), ext.begin(), ::tolower); - - if (ext == ".srt") { - reader = new sub::SubripReader (utf8.get()); - } else if (ext == ".ssa" || ext == ".ass") { - reader = new sub::SSAReader (utf8.get()); - } - - if (reader) { - _subtitles = sub::collect > (reader->subtitles ()); - } - - delete reader; -} - -/** @return time of first subtitle, if there is one */ -optional -TextCaptionFile::first () const -{ - if (_subtitles.empty()) { - return optional(); - } - - return ContentTime::from_seconds(_subtitles[0].from.all_as_seconds()); -} - -ContentTime -TextCaptionFile::length () const -{ - if (_subtitles.empty ()) { - return ContentTime (); - } - - return ContentTime::from_seconds (_subtitles.back().to.all_as_seconds ()); -} diff --git a/src/lib/text_caption_file.h b/src/lib/text_caption_file.h deleted file mode 100644 index 4a5657a08..000000000 --- a/src/lib/text_caption_file.h +++ /dev/null @@ -1,53 +0,0 @@ -/* - Copyright (C) 2014-2018 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 . - -*/ - -#ifndef DCPOMATIC_TEXT_CAPTION_FILE_H -#define DCPOMATIC_TEXT_CAPTION_FILE_H - -#include "dcpomatic_time.h" -#include -#include -#include - -class TextCaptionFileContent; -class plain_text_time_test; -class plain_text_coordinate_test; -class plain_text_content_test; -class plain_text_parse_test; - -/** @class TextCaptionFile - * @brief Base for TextCaptionFile decoder and examiner. - * - * In fact this is sufficient for the examiner, so it's used as-is rather than deriving - * a pointless TextCaptionFileExaminer. - */ -class TextCaptionFile -{ -public: - explicit TextCaptionFile (boost::shared_ptr); - - boost::optional first () const; - ContentTime length () const; - -protected: - std::vector _subtitles; -}; - -#endif diff --git a/src/lib/text_caption_file_content.cc b/src/lib/text_caption_file_content.cc deleted file mode 100644 index aa64ca572..000000000 --- a/src/lib/text_caption_file_content.cc +++ /dev/null @@ -1,95 +0,0 @@ -/* - Copyright (C) 2014-2018 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_caption_file_content.h" -#include "util.h" -#include "text_caption_file.h" -#include "film.h" -#include "font.h" -#include "caption_content.h" -#include -#include -#include - -#include "i18n.h" - -using std::string; -using std::cout; -using boost::shared_ptr; -using dcp::raw_convert; - -TextCaptionFileContent::TextCaptionFileContent (shared_ptr film, boost::filesystem::path path) - : Content (film, path) -{ - caption.push_back (shared_ptr (new CaptionContent (this, CAPTION_OPEN))); -} - -TextCaptionFileContent::TextCaptionFileContent (shared_ptr film, cxml::ConstNodePtr node, int version) - : Content (film, node) - , _length (node->number_child ("Length")) -{ - caption = CaptionContent::from_xml (this, node, version); -} - -void -TextCaptionFileContent::examine (boost::shared_ptr job) -{ - Content::examine (job); - TextCaptionFile s (shared_from_this ()); - - /* Default to turning these subtitles on */ - only_caption()->set_use (true); - - boost::mutex::scoped_lock lm (_mutex); - _length = s.length (); - only_caption()->add_font (shared_ptr (new Font (TEXT_FONT_ID))); -} - -string -TextCaptionFileContent::summary () const -{ - return path_summary() + " " + _("[subtitles]"); -} - -string -TextCaptionFileContent::technical_summary () const -{ - return Content::technical_summary() + " - " + _("Text subtitles"); -} - -void -TextCaptionFileContent::as_xml (xmlpp::Node* node, bool with_paths) const -{ - node->add_child("Type")->add_child_text ("TextSubtitle"); - Content::as_xml (node, with_paths); - - if (only_caption()) { - only_caption()->as_xml (node); - } - - node->add_child("Length")->add_child_text (raw_convert (_length.get ())); -} - -DCPTime -TextCaptionFileContent::full_length () const -{ - FrameRateChange const frc (active_video_frame_rate(), film()->video_frame_rate ()); - return DCPTime (_length, frc); -} diff --git a/src/lib/text_caption_file_content.h b/src/lib/text_caption_file_content.h deleted file mode 100644 index f8f4a9851..000000000 --- a/src/lib/text_caption_file_content.h +++ /dev/null @@ -1,46 +0,0 @@ -/* - Copyright (C) 2014-2018 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 "content.h" - -class Job; - -/** @class TextCaptionFileContent - * @brief A SubRip, SSA or ASS file. - */ -class TextCaptionFileContent : public Content -{ -public: - TextCaptionFileContent (boost::shared_ptr, boost::filesystem::path); - TextCaptionFileContent (boost::shared_ptr, cxml::ConstNodePtr, int); - - boost::shared_ptr shared_from_this () { - return boost::dynamic_pointer_cast (Content::shared_from_this ()); - } - - void examine (boost::shared_ptr); - std::string summary () const; - std::string technical_summary () const; - void as_xml (xmlpp::Node *, bool with_paths) const; - DCPTime full_length () const; - -private: - ContentTime _length; -}; diff --git a/src/lib/text_caption_file_decoder.cc b/src/lib/text_caption_file_decoder.cc deleted file mode 100644 index 65de6a562..000000000 --- a/src/lib/text_caption_file_decoder.cc +++ /dev/null @@ -1,89 +0,0 @@ -/* - Copyright (C) 2014-2018 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_caption_file_decoder.h" -#include "text_caption_file_content.h" -#include "caption_content.h" -#include "caption_decoder.h" -#include -#include -#include - -using std::list; -using std::vector; -using std::string; -using std::cout; -using std::max; -using boost::shared_ptr; -using boost::optional; -using boost::dynamic_pointer_cast; - -TextCaptionFileDecoder::TextCaptionFileDecoder (shared_ptr content, shared_ptr log) - : TextCaptionFile (content) - , _next (0) -{ - ContentTime first; - if (!_subtitles.empty()) { - first = content_time_period(_subtitles[0]).from; - } - caption.push_back (shared_ptr (new CaptionDecoder (this, content->only_caption(), log, first))); -} - -void -TextCaptionFileDecoder::seek (ContentTime time, bool accurate) -{ - /* It's worth back-tracking a little here as decoding is cheap and it's nice if we don't miss - too many subtitles when seeking. - */ - time -= ContentTime::from_seconds (5); - if (time < ContentTime()) { - time = ContentTime(); - } - - Decoder::seek (time, accurate); - - _next = 0; - while (_next < _subtitles.size() && ContentTime::from_seconds (_subtitles[_next].from.all_as_seconds ()) < time) { - ++_next; - } -} - -bool -TextCaptionFileDecoder::pass () -{ - if (_next >= _subtitles.size ()) { - return true; - } - - ContentTimePeriod const p = content_time_period (_subtitles[_next]); - only_caption()->emit_plain (p, _subtitles[_next]); - - ++_next; - return false; -} - -ContentTimePeriod -TextCaptionFileDecoder::content_time_period (sub::Subtitle s) const -{ - return ContentTimePeriod ( - ContentTime::from_seconds (s.from.all_as_seconds()), - ContentTime::from_seconds (s.to.all_as_seconds()) - ); -} diff --git a/src/lib/text_caption_file_decoder.h b/src/lib/text_caption_file_decoder.h deleted file mode 100644 index 769777571..000000000 --- a/src/lib/text_caption_file_decoder.h +++ /dev/null @@ -1,44 +0,0 @@ -/* - Copyright (C) 2014-2018 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 . - -*/ - -#ifndef DCPOMATIC_TEXT_CAPTION_FILE_DECODER_H -#define DCPOMATIC_TEXT_CAPTION_FILE_DECODER_H - -#include "text_caption_file.h" -#include "decoder.h" - -class TextCaptionFileContent; -class Log; - -class TextCaptionFileDecoder : public Decoder, public TextCaptionFile -{ -public: - TextCaptionFileDecoder (boost::shared_ptr, boost::shared_ptr log); - - void seek (ContentTime time, bool accurate); - bool pass (); - -private: - ContentTimePeriod content_time_period (sub::Subtitle s) const; - - size_t _next; -}; - -#endif diff --git a/src/lib/text_content.cc b/src/lib/text_content.cc new file mode 100644 index 000000000..fbc7d82af --- /dev/null +++ b/src/lib/text_content.cc @@ -0,0 +1,579 @@ +/* + Copyright (C) 2013-2018 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_content.h" +#include "util.h" +#include "exceptions.h" +#include "font.h" +#include "content.h" +#include +#include +#include +#include +#include + +#include "i18n.h" + +using std::string; +using std::vector; +using std::cout; +using std::list; +using boost::shared_ptr; +using boost::dynamic_pointer_cast; +using boost::optional; +using dcp::raw_convert; + +int const TextContentProperty::X_OFFSET = 500; +int const TextContentProperty::Y_OFFSET = 501; +int const TextContentProperty::X_SCALE = 502; +int const TextContentProperty::Y_SCALE = 503; +int const TextContentProperty::USE = 504; +int const TextContentProperty::BURN = 505; +int const TextContentProperty::LANGUAGE = 506; +int const TextContentProperty::FONTS = 507; +int const TextContentProperty::COLOUR = 508; +int const TextContentProperty::EFFECT = 509; +int const TextContentProperty::EFFECT_COLOUR = 510; +int const TextContentProperty::LINE_SPACING = 511; +int const TextContentProperty::FADE_IN = 512; +int const TextContentProperty::FADE_OUT = 513; +int const TextContentProperty::OUTLINE_WIDTH = 514; +int const TextContentProperty::TYPE = 515; + +TextContent::TextContent (Content* parent, TextType original_type) + : ContentPart (parent) + , _use (false) + , _burn (false) + , _x_offset (0) + , _y_offset (0) + , _x_scale (1) + , _y_scale (1) + , _line_spacing (1) + , _outline_width (2) + , _type (original_type) + , _original_type (original_type) +{ + +} + +/** @return TextContents from node or nodes under node (according to version). + * The list could be empty if no TextContents are found. + */ +list > +TextContent::from_xml (Content* parent, cxml::ConstNodePtr node, int version) +{ + if (version < 34) { + /* With old metadata FFmpeg content has the subtitle-related tags even with no + subtitle streams, so check for that. + */ + if (node->string_child("Type") == "FFmpeg" && node->node_children("SubtitleStream").empty()) { + return list >(); + } + + /* Otherwise we can drop through to the newer logic */ + } + + if (version < 37) { + if (!node->optional_number_child("SubtitleXOffset") && !node->optional_number_child("SubtitleOffset")) { + return list >(); + } + list > c; + c.push_back (shared_ptr (new TextContent (parent, node, version))); + return c; + } + + if (!node->optional_node_child("Caption")) { + return list >(); + } + + list > c; + BOOST_FOREACH (cxml::ConstNodePtr i, node->node_children("Caption")) { + c.push_back (shared_ptr (new TextContent (parent, i, version))); + } + return c; +} + +TextContent::TextContent (Content* parent, cxml::ConstNodePtr node, int version) + : ContentPart (parent) + , _use (false) + , _burn (false) + , _x_offset (0) + , _y_offset (0) + , _x_scale (1) + , _y_scale (1) + , _line_spacing (node->optional_number_child("LineSpacing").get_value_or (1)) + , _outline_width (node->optional_number_child("OutlineWidth").get_value_or (2)) + , _type (CAPTION_OPEN) + , _original_type (CAPTION_OPEN) +{ + if (version >= 37) { + _use = node->bool_child ("Use"); + _burn = node->bool_child ("Burn"); + } else if (version >= 32) { + _use = node->bool_child ("UseSubtitles"); + _burn = node->bool_child ("BurnSubtitles"); + } + + if (version >= 37) { + _x_offset = node->number_child ("XOffset"); + _y_offset = node->number_child ("YOffset"); + } else if (version >= 7) { + _x_offset = node->number_child ("SubtitleXOffset"); + _y_offset = node->number_child ("SubtitleYOffset"); + } else { + _y_offset = node->number_child ("SubtitleOffset"); + } + + if (node->optional_bool_child("Outline").get_value_or(false)) { + _effect = dcp::BORDER; + } else if (node->optional_bool_child("Shadow").get_value_or(false)) { + _effect = dcp::SHADOW; + } else { + _effect = dcp::NONE; + } + + optional effect = node->optional_string_child("Effect"); + if (effect) { + if (*effect == "none") { + _effect = dcp::NONE; + } else if (*effect == "outline") { + _effect = dcp::BORDER; + } else if (*effect == "shadow") { + _effect = dcp::SHADOW; + } + } + + if (version >= 37) { + _x_scale = node->number_child ("XScale"); + _y_scale = node->number_child ("YScale"); + } else if (version >= 10) { + _x_scale = node->number_child ("SubtitleXScale"); + _y_scale = node->number_child ("SubtitleYScale"); + } else { + _x_scale = _y_scale = node->number_child ("SubtitleScale"); + } + + optional r = node->optional_number_child("Red"); + optional g = node->optional_number_child("Green"); + optional b = node->optional_number_child("Blue"); + if (r && g && b) { + _colour = dcp::Colour (*r, *g, *b); + } + + if (version >= 36) { + optional er = node->optional_number_child("EffectRed"); + optional eg = node->optional_number_child("EffectGreen"); + optional eb = node->optional_number_child("EffectBlue"); + if (er && eg && eb) { + _effect_colour = dcp::Colour (*er, *eg, *eb); + } + } else { + _effect_colour = dcp::Colour ( + node->optional_number_child("OutlineRed").get_value_or(255), + node->optional_number_child("OutlineGreen").get_value_or(255), + node->optional_number_child("OutlineBlue").get_value_or(255) + ); + } + + optional fi; + if (version >= 37) { + fi = node->optional_number_child("FadeIn"); + } else { + fi = node->optional_number_child("SubtitleFadeIn"); + } + if (fi) { + _fade_in = ContentTime (*fi); + } + + optional fo; + if (version >= 37) { + fo = node->optional_number_child("FadeOut"); + } else { + fo = node->optional_number_child("SubtitleFadeOut"); + } + if (fo) { + _fade_out = ContentTime (*fo); + } + + if (version >= 37) { + _language = node->optional_string_child ("Language").get_value_or (""); + } else { + _language = node->optional_string_child ("SubtitleLanguage").get_value_or (""); + } + + list fonts = node->node_children ("Font"); + for (list::const_iterator i = fonts.begin(); i != fonts.end(); ++i) { + _fonts.push_back (shared_ptr (new Font (*i))); + } + + connect_to_fonts (); + + _type = string_to_caption_type (node->optional_string_child("Type").get_value_or("open")); + _original_type = string_to_caption_type (node->optional_string_child("OriginalType").get_value_or("open")); +} + +TextContent::TextContent (Content* parent, vector > c) + : ContentPart (parent) +{ + /* This constructor is for join which is only supported for content types + that have a single caption, so we can use only_caption() here. + */ + shared_ptr ref = c[0]->only_caption(); + DCPOMATIC_ASSERT (ref); + list > ref_fonts = ref->fonts (); + + for (size_t i = 1; i < c.size(); ++i) { + + if (c[i]->only_caption()->use() != ref->use()) { + throw JoinError (_("Content to be joined must have the same 'use subtitles' setting.")); + } + + if (c[i]->only_caption()->burn() != ref->burn()) { + throw JoinError (_("Content to be joined must have the same 'burn subtitles' setting.")); + } + + if (c[i]->only_caption()->x_offset() != ref->x_offset()) { + throw JoinError (_("Content to be joined must have the same subtitle X offset.")); + } + + if (c[i]->only_caption()->y_offset() != ref->y_offset()) { + throw JoinError (_("Content to be joined must have the same subtitle Y offset.")); + } + + if (c[i]->only_caption()->x_scale() != ref->x_scale()) { + throw JoinError (_("Content to be joined must have the same subtitle X scale.")); + } + + if (c[i]->only_caption()->y_scale() != ref->y_scale()) { + throw JoinError (_("Content to be joined must have the same subtitle Y scale.")); + } + + if (c[i]->only_caption()->line_spacing() != ref->line_spacing()) { + throw JoinError (_("Content to be joined must have the same subtitle line spacing.")); + } + + if ((c[i]->only_caption()->fade_in() != ref->fade_in()) || (c[i]->only_caption()->fade_out() != ref->fade_out())) { + throw JoinError (_("Content to be joined must have the same subtitle fades.")); + } + + if ((c[i]->only_caption()->outline_width() != ref->outline_width())) { + throw JoinError (_("Content to be joined must have the same outline width.")); + } + + list > fonts = c[i]->only_caption()->fonts (); + if (fonts.size() != ref_fonts.size()) { + throw JoinError (_("Content to be joined must use the same fonts.")); + } + + list >::const_iterator j = ref_fonts.begin (); + list >::const_iterator k = fonts.begin (); + + while (j != ref_fonts.end ()) { + if (**j != **k) { + throw JoinError (_("Content to be joined must use the same fonts.")); + } + ++j; + ++k; + } + } + + _use = ref->use (); + _burn = ref->burn (); + _x_offset = ref->x_offset (); + _y_offset = ref->y_offset (); + _x_scale = ref->x_scale (); + _y_scale = ref->y_scale (); + _language = ref->language (); + _fonts = ref_fonts; + _line_spacing = ref->line_spacing (); + _fade_in = ref->fade_in (); + _fade_out = ref->fade_out (); + _outline_width = ref->outline_width (); + _type = ref->type (); + _original_type = ref->original_type (); + + connect_to_fonts (); +} + +/** _mutex must not be held on entry */ +void +TextContent::as_xml (xmlpp::Node* root) const +{ + boost::mutex::scoped_lock lm (_mutex); + + xmlpp::Element* caption = root->add_child ("Caption"); + + caption->add_child("Use")->add_child_text (_use ? "1" : "0"); + caption->add_child("Burn")->add_child_text (_burn ? "1" : "0"); + caption->add_child("XOffset")->add_child_text (raw_convert (_x_offset)); + caption->add_child("YOffset")->add_child_text (raw_convert (_y_offset)); + caption->add_child("XScale")->add_child_text (raw_convert (_x_scale)); + caption->add_child("YScale")->add_child_text (raw_convert (_y_scale)); + caption->add_child("Language")->add_child_text (_language); + if (_colour) { + caption->add_child("Red")->add_child_text (raw_convert (_colour->r)); + caption->add_child("Green")->add_child_text (raw_convert (_colour->g)); + caption->add_child("Blue")->add_child_text (raw_convert (_colour->b)); + } + if (_effect) { + switch (*_effect) { + case dcp::NONE: + caption->add_child("Effect")->add_child_text("none"); + break; + case dcp::BORDER: + caption->add_child("Effect")->add_child_text("outline"); + break; + case dcp::SHADOW: + caption->add_child("Effect")->add_child_text("shadow"); + break; + } + } + if (_effect_colour) { + caption->add_child("EffectRed")->add_child_text (raw_convert (_effect_colour->r)); + caption->add_child("EffectGreen")->add_child_text (raw_convert (_effect_colour->g)); + caption->add_child("EffectBlue")->add_child_text (raw_convert (_effect_colour->b)); + } + caption->add_child("LineSpacing")->add_child_text (raw_convert (_line_spacing)); + if (_fade_in) { + caption->add_child("FadeIn")->add_child_text (raw_convert (_fade_in->get())); + } + if (_fade_out) { + caption->add_child("FadeOut")->add_child_text (raw_convert (_fade_out->get())); + } + caption->add_child("OutlineWidth")->add_child_text (raw_convert (_outline_width)); + + for (list >::const_iterator i = _fonts.begin(); i != _fonts.end(); ++i) { + (*i)->as_xml (caption->add_child("Font")); + } + + caption->add_child("Type")->add_child_text (caption_type_to_string(_type)); + caption->add_child("OriginalType")->add_child_text (caption_type_to_string(_original_type)); +} + +string +TextContent::identifier () const +{ + string s = raw_convert (x_scale()) + + "_" + raw_convert (y_scale()) + + "_" + raw_convert (x_offset()) + + "_" + raw_convert (y_offset()) + + "_" + raw_convert (line_spacing()) + + "_" + raw_convert (fade_in().get_value_or(ContentTime()).get()) + + "_" + raw_convert (fade_out().get_value_or(ContentTime()).get()) + + "_" + raw_convert (outline_width()) + + "_" + raw_convert (colour().get_value_or(dcp::Colour(255, 255, 255)).to_argb_string()) + + "_" + raw_convert (dcp::effect_to_string(effect().get_value_or(dcp::NONE))) + + "_" + raw_convert (effect_colour().get_value_or(dcp::Colour(0, 0, 0)).to_argb_string()); + + /* XXX: I suppose really _fonts shouldn't be in here, since not all + types of subtitle content involve fonts. + */ + BOOST_FOREACH (shared_ptr f, _fonts) { + for (int i = 0; i < FontFiles::VARIANTS; ++i) { + s += "_" + f->file(static_cast(i)).get_value_or("Default").string(); + } + } + + /* The language is for metadata only, and doesn't affect + how this content looks. + */ + + return s; +} + +void +TextContent::add_font (shared_ptr font) +{ + _fonts.push_back (font); + connect_to_fonts (); +} + +void +TextContent::connect_to_fonts () +{ + BOOST_FOREACH (boost::signals2::connection& i, _font_connections) { + i.disconnect (); + } + + _font_connections.clear (); + + BOOST_FOREACH (shared_ptr i, _fonts) { + _font_connections.push_back (i->Changed.connect (boost::bind (&TextContent::font_changed, this))); + } +} + +void +TextContent::font_changed () +{ + _parent->signal_changed (TextContentProperty::FONTS); +} + +void +TextContent::set_colour (dcp::Colour colour) +{ + maybe_set (_colour, colour, TextContentProperty::COLOUR); +} + +void +TextContent::unset_colour () +{ + maybe_set (_colour, optional(), TextContentProperty::COLOUR); +} + +void +TextContent::set_effect (dcp::Effect e) +{ + maybe_set (_effect, e, TextContentProperty::EFFECT); +} + +void +TextContent::unset_effect () +{ + maybe_set (_effect, optional(), TextContentProperty::EFFECT); +} + +void +TextContent::set_effect_colour (dcp::Colour colour) +{ + maybe_set (_effect_colour, colour, TextContentProperty::EFFECT_COLOUR); +} + +void +TextContent::unset_effect_colour () +{ + maybe_set (_effect_colour, optional(), TextContentProperty::EFFECT_COLOUR); +} + +void +TextContent::set_use (bool u) +{ + maybe_set (_use, u, TextContentProperty::USE); +} + +void +TextContent::set_burn (bool b) +{ + maybe_set (_burn, b, TextContentProperty::BURN); +} + +void +TextContent::set_x_offset (double o) +{ + maybe_set (_x_offset, o, TextContentProperty::X_OFFSET); +} + +void +TextContent::set_y_offset (double o) +{ + maybe_set (_y_offset, o, TextContentProperty::Y_OFFSET); +} + +void +TextContent::set_x_scale (double s) +{ + maybe_set (_x_scale, s, TextContentProperty::X_SCALE); +} + +void +TextContent::set_y_scale (double s) +{ + maybe_set (_y_scale, s, TextContentProperty::Y_SCALE); +} + +void +TextContent::set_language (string language) +{ + maybe_set (_language, language, TextContentProperty::LANGUAGE); +} + +void +TextContent::set_line_spacing (double s) +{ + maybe_set (_line_spacing, s, TextContentProperty::LINE_SPACING); +} + +void +TextContent::set_fade_in (ContentTime t) +{ + maybe_set (_fade_in, t, TextContentProperty::FADE_IN); +} + +void +TextContent::unset_fade_in () +{ + maybe_set (_fade_in, optional(), TextContentProperty::FADE_IN); +} + +void +TextContent::set_fade_out (ContentTime t) +{ + maybe_set (_fade_out, t, TextContentProperty::FADE_OUT); +} + +void +TextContent::unset_fade_out () +{ + maybe_set (_fade_out, optional(), TextContentProperty::FADE_OUT); +} + +void +TextContent::set_type (TextType type) +{ + maybe_set (_type, type, TextContentProperty::TYPE); +} + +void +TextContent::set_outline_width (int w) +{ + maybe_set (_outline_width, w, TextContentProperty::OUTLINE_WIDTH); +} + +void +TextContent::take_settings_from (shared_ptr c) +{ + set_use (c->_use); + set_burn (c->_burn); + set_x_offset (c->_x_offset); + set_y_offset (c->_y_offset); + set_x_scale (c->_x_scale); + set_y_scale (c->_y_scale); + maybe_set (_fonts, c->_fonts, TextContentProperty::FONTS); + if (c->_colour) { + set_colour (*c->_colour); + } else { + unset_colour (); + } + if (c->_effect) { + set_effect (*c->_effect); + } + if (c->_effect_colour) { + set_effect_colour (*c->_effect_colour); + } else { + unset_effect_colour (); + } + set_line_spacing (c->_line_spacing); + if (c->_fade_in) { + set_fade_in (*c->_fade_in); + } + if (c->_fade_out) { + set_fade_out (*c->_fade_out); + } + set_outline_width (c->_outline_width); +} diff --git a/src/lib/text_content.h b/src/lib/text_content.h new file mode 100644 index 000000000..5aacc7ecf --- /dev/null +++ b/src/lib/text_content.h @@ -0,0 +1,222 @@ +/* + Copyright (C) 2013-2018 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 . + +*/ + +#ifndef DCPOMATIC_CAPTION_CONTENT_H +#define DCPOMATIC_CAPTION_CONTENT_H + +#include "content_part.h" +#include +#include +#include + +class Font; + +class TextContentProperty +{ +public: + static int const X_OFFSET; + static int const Y_OFFSET; + static int const X_SCALE; + static int const Y_SCALE; + static int const USE; + static int const BURN; + static int const LANGUAGE; + static int const FONTS; + static int const COLOUR; + static int const EFFECT; + static int const EFFECT_COLOUR; + static int const LINE_SPACING; + static int const FADE_IN; + static int const FADE_OUT; + static int const OUTLINE_WIDTH; + static int const TYPE; +}; + +/** @class TextContent + * @brief Description of how some text content should be presented. + * + * There are `bitmap' subtitles and `plain' subtitles (plain text), + * and not all of the settings in this class correspond to both types. + */ +class TextContent : public ContentPart +{ +public: + TextContent (Content* parent, TextType original_type); + TextContent (Content* parent, std::vector >); + + void as_xml (xmlpp::Node *) const; + std::string identifier () const; + void take_settings_from (boost::shared_ptr c); + + void add_font (boost::shared_ptr font); + + void set_use (bool); + void set_burn (bool); + void set_x_offset (double); + void set_y_offset (double); + void set_x_scale (double); + void set_y_scale (double); + void set_language (std::string language); + void set_colour (dcp::Colour); + void unset_colour (); + void set_effect (dcp::Effect); + void unset_effect (); + void set_effect_colour (dcp::Colour); + void unset_effect_colour (); + void set_line_spacing (double s); + void set_fade_in (ContentTime); + void unset_fade_in (); + void set_fade_out (ContentTime); + void set_outline_width (int); + void unset_fade_out (); + void set_type (TextType type); + + bool use () const { + boost::mutex::scoped_lock lm (_mutex); + return _use; + } + + bool burn () const { + boost::mutex::scoped_lock lm (_mutex); + return _burn; + } + + double x_offset () const { + boost::mutex::scoped_lock lm (_mutex); + return _x_offset; + } + + double y_offset () const { + boost::mutex::scoped_lock lm (_mutex); + return _y_offset; + } + + double x_scale () const { + boost::mutex::scoped_lock lm (_mutex); + return _x_scale; + } + + double y_scale () const { + boost::mutex::scoped_lock lm (_mutex); + return _y_scale; + } + + std::list > fonts () const { + boost::mutex::scoped_lock lm (_mutex); + return _fonts; + } + + std::string language () const { + boost::mutex::scoped_lock lm (_mutex); + return _language; + } + + boost::optional colour () const { + boost::mutex::scoped_lock lm (_mutex); + return _colour; + } + + boost::optional effect () const { + boost::mutex::scoped_lock lm (_mutex); + return _effect; + } + + boost::optional effect_colour () const { + boost::mutex::scoped_lock lm (_mutex); + return _effect_colour; + } + + double line_spacing () const { + boost::mutex::scoped_lock lm (_mutex); + return _line_spacing; + } + + boost::optional fade_in () const { + boost::mutex::scoped_lock lm (_mutex); + return _fade_in; + } + + boost::optional fade_out () const { + boost::mutex::scoped_lock lm (_mutex); + return _fade_out; + } + + int outline_width () const { + boost::mutex::scoped_lock lm (_mutex); + return _outline_width; + } + + TextType type () const { + boost::mutex::scoped_lock lm (_mutex); + return _type; + } + + TextType original_type () const { + boost::mutex::scoped_lock lm (_mutex); + return _original_type; + } + + static std::list > from_xml (Content* parent, cxml::ConstNodePtr, int version); + +protected: + /** subtitle language (e.g. "German") or empty if it is not known */ + std::string _language; + +private: + friend struct ffmpeg_pts_offset_test; + + TextContent (Content* parent, cxml::ConstNodePtr, int version); + void font_changed (); + void connect_to_fonts (); + + std::list _font_connections; + + bool _use; + bool _burn; + /** x offset for placing subtitles, as a proportion of the container width; + * +ve is further right, -ve is further left. + */ + double _x_offset; + /** y offset for placing subtitles, as a proportion of the container height; + * +ve is further down the frame, -ve is further up. + */ + double _y_offset; + /** x scale factor to apply to subtitles */ + double _x_scale; + /** y scale factor to apply to subtitles */ + double _y_scale; + std::list > _fonts; + boost::optional _colour; + boost::optional _effect; + boost::optional _effect_colour; + /** scaling factor for line spacing; 1 is "standard", < 1 is closer together, > 1 is further apart */ + double _line_spacing; + boost::optional _fade_in; + boost::optional _fade_out; + int _outline_width; + /** what these captions will be used for in the output DCP (not necessarily what + * they were originally). + */ + TextType _type; + /** the original type of these captions in their content */ + TextType _original_type; +}; + +#endif diff --git a/src/lib/text_decoder.cc b/src/lib/text_decoder.cc new file mode 100644 index 000000000..3b6a06ea1 --- /dev/null +++ b/src/lib/text_decoder.cc @@ -0,0 +1,257 @@ +/* + Copyright (C) 2013-2017 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_decoder.h" +#include "text_content.h" +#include "util.h" +#include "log.h" +#include "compose.hpp" +#include +#include +#include +#include +#include + +using std::list; +using std::cout; +using std::string; +using std::min; +using boost::shared_ptr; +using boost::optional; +using boost::function; + +TextDecoder::TextDecoder ( + Decoder* parent, + shared_ptr c, + shared_ptr log, + ContentTime first + ) + : DecoderPart (parent, log) + , _content (c) + , _position (first) +{ + +} + +/** 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 +TextDecoder::emit_bitmap_start (ContentTime from, shared_ptr image, dcpomatic::Rect rect) +{ + BitmapStart (ContentBitmapText (from, _content->type(), image, rect)); + _position = from; +} + +void +TextDecoder::emit_plain_start (ContentTime from, list s) +{ + BOOST_FOREACH (dcp::SubtitleString& i, 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.) + */ + string t = i.text (); + boost::algorithm::replace_all (t, "<", "<"); + boost::algorithm::replace_all (t, ">", ">"); + i.set_text (t); + + /* Set any forced appearance */ + if (content()->colour()) { + i.set_colour (*content()->colour()); + } + if (content()->effect_colour()) { + i.set_effect_colour (*content()->effect_colour()); + } + if (content()->effect()) { + i.set_effect (*content()->effect()); + } + if (content()->fade_in()) { + i.set_fade_up_time (dcp::Time(content()->fade_in()->seconds(), 1000)); + } + if (content()->fade_out()) { + i.set_fade_down_time (dcp::Time(content()->fade_out()->seconds(), 1000)); + } + } + + PlainStart (ContentStringText (from, _content->type(), s)); + _position = from; +} + +void +TextDecoder::emit_plain_start (ContentTime from, sub::Subtitle const & subtitle) +{ + /* See if our next subtitle needs to be vertically placed on screen by us */ + bool needs_placement = false; + optional bottom_line; + BOOST_FOREACH (sub::Line i, subtitle.lines) { + if (!i.vertical_position.reference || i.vertical_position.reference.get() == sub::TOP_OF_SUBTITLE) { + needs_placement = true; + DCPOMATIC_ASSERT (i.vertical_position.line); + if (!bottom_line || bottom_line.get() < i.vertical_position.line.get()) { + bottom_line = i.vertical_position.line.get(); + } + } + } + + /* Find the lowest proportional position */ + optional lowest_proportional; + BOOST_FOREACH (sub::Line i, subtitle.lines) { + if (i.vertical_position.proportional) { + if (!lowest_proportional) { + lowest_proportional = i.vertical_position.proportional; + } else { + lowest_proportional = min (lowest_proportional.get(), i.vertical_position.proportional.get()); + } + } + } + + list out; + BOOST_FOREACH (sub::Line i, subtitle.lines) { + BOOST_FOREACH (sub::Block j, i.blocks) { + + if (!j.font_size.specified()) { + /* Fallback default font size if no other has been specified */ + j.font_size.set_points (48); + } + + float v_position; + dcp::VAlign v_align; + if (needs_placement) { + DCPOMATIC_ASSERT (i.vertical_position.line); + /* This 1.015 is an arbitrary value to lift the bottom sub off the bottom + of the screen a bit to a pleasing degree. + */ + v_position = 1.015 - + (1 + bottom_line.get() - i.vertical_position.line.get()) + * 1.2 * content()->line_spacing() * content()->y_scale() * j.font_size.proportional (72 * 11); + + v_align = dcp::VALIGN_TOP; + } else { + DCPOMATIC_ASSERT (i.vertical_position.proportional); + DCPOMATIC_ASSERT (i.vertical_position.reference); + v_position = i.vertical_position.proportional.get(); + + if (lowest_proportional) { + /* Adjust line spacing */ + v_position = ((v_position - lowest_proportional.get()) * content()->line_spacing()) + lowest_proportional.get(); + } + + switch (i.vertical_position.reference.get()) { + case sub::TOP_OF_SCREEN: + v_align = dcp::VALIGN_TOP; + break; + case sub::VERTICAL_CENTRE_OF_SCREEN: + v_align = dcp::VALIGN_CENTER; + break; + case sub::BOTTOM_OF_SCREEN: + v_align = dcp::VALIGN_BOTTOM; + break; + default: + v_align = dcp::VALIGN_TOP; + break; + } + } + + dcp::HAlign h_align; + switch (i.horizontal_position.reference) { + case sub::LEFT_OF_SCREEN: + h_align = dcp::HALIGN_LEFT; + break; + case sub::HORIZONTAL_CENTRE_OF_SCREEN: + h_align = dcp::HALIGN_CENTER; + break; + case sub::RIGHT_OF_SCREEN: + h_align = dcp::HALIGN_RIGHT; + break; + default: + h_align = dcp::HALIGN_CENTER; + break; + } + + /* The idea here (rightly or wrongly) is that we set the appearance based on the + values in the libsub objects, and these are overridden with values from the + content by the other emit_plain_start() above. + */ + + out.push_back ( + dcp::SubtitleString ( + string(TEXT_FONT_ID), + j.italic, + j.bold, + j.underline, + j.colour.dcp(), + j.font_size.points (72 * 11), + 1.0, + dcp::Time (from.seconds(), 1000), + /* XXX: hmm; this is a bit ugly (we don't know the to time yet) */ + dcp::Time (), + i.horizontal_position.proportional, + h_align, + v_position, + v_align, + dcp::DIRECTION_LTR, + j.text, + dcp::NONE, + j.effect_colour.get_value_or(sub::Colour(0, 0, 0)).dcp(), + /* Hack: we should use subtitle.fade_up and subtitle.fade_down here + but the times of these often don't have a frame rate associated + with them so the sub::Time won't convert them to milliseconds without + throwing an exception. Since only DCP subs fill those in (and we don't + use libsub for DCP subs) we can cheat by just putting 0 in here. + */ + dcp::Time (), + dcp::Time () + ) + ); + } + } + + emit_plain_start (from, out); +} + +void +TextDecoder::emit_stop (ContentTime to) +{ + Stop (to, _content->type()); +} + +void +TextDecoder::emit_plain (ContentTimePeriod period, list s) +{ + emit_plain_start (period.from, s); + emit_stop (period.to); +} + +void +TextDecoder::emit_plain (ContentTimePeriod period, sub::Subtitle const & s) +{ + emit_plain_start (period.from, s); + emit_stop (period.to); +} + +void +TextDecoder::seek () +{ + _position = ContentTime (); +} diff --git a/src/lib/text_decoder.h b/src/lib/text_decoder.h new file mode 100644 index 000000000..25125e701 --- /dev/null +++ b/src/lib/text_decoder.h @@ -0,0 +1,74 @@ +/* + Copyright (C) 2013-2018 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 . + +*/ + +#ifndef DCPOMATIC_CAPTION_DECODER_H +#define DCPOMATIC_CAPTION_DECODER_H + +#include "decoder.h" +#include "rect.h" +#include "types.h" +#include "content_text.h" +#include "decoder_part.h" +#include +#include + +namespace sub { + class Subtitle; +} + +class Image; + +class TextDecoder : public DecoderPart +{ +public: + TextDecoder ( + Decoder* parent, + boost::shared_ptr, + boost::shared_ptr log, + ContentTime first + ); + + ContentTime position () const { + return _position; + } + + void emit_bitmap_start (ContentTime from, boost::shared_ptr image, dcpomatic::Rect rect); + void emit_plain_start (ContentTime from, std::list s); + void emit_plain_start (ContentTime from, sub::Subtitle const & subtitle); + void emit_plain (ContentTimePeriod period, std::list s); + void emit_plain (ContentTimePeriod period, sub::Subtitle const & subtitle); + void emit_stop (ContentTime to); + + void seek (); + + boost::shared_ptr content () const { + return _content; + } + + boost::signals2::signal BitmapStart; + boost::signals2::signal PlainStart; + boost::signals2::signal Stop; + +private: + boost::shared_ptr _content; + ContentTime _position; +}; + +#endif diff --git a/src/lib/types.cc b/src/lib/types.cc index 5474b609a..7f0bf4305 100644 --- a/src/lib/types.cc +++ b/src/lib/types.cc @@ -93,7 +93,7 @@ Crop::as_xml (xmlpp::Node* node) const node->add_child("BottomCrop")->add_child_text (raw_convert (bottom)); } -CaptionType +TextType string_to_caption_type (string s) { if (s == "open") { @@ -106,7 +106,7 @@ string_to_caption_type (string s) } string -caption_type_to_string (CaptionType t) +caption_type_to_string (TextType t) { switch (t) { case CAPTION_OPEN: @@ -119,7 +119,7 @@ caption_type_to_string (CaptionType t) } string -caption_type_to_name (CaptionType t) +caption_type_to_name (TextType t) { switch (t) { case CAPTION_OPEN: diff --git a/src/lib/types.h b/src/lib/types.h index 6a05f66c9..6e3732d31 100644 --- a/src/lib/types.h +++ b/src/lib/types.h @@ -31,7 +31,7 @@ class Content; class VideoContent; class AudioContent; -class CaptionContent; +class TextContent; class FFmpegContent; namespace cxml { @@ -139,16 +139,16 @@ enum ReelType * There is also still use of the word `subtitle' in the code; these are the * same as open captions in DoM. */ -enum CaptionType +enum TextType { CAPTION_OPEN, CAPTION_CLOSED, CAPTION_COUNT }; -extern std::string caption_type_to_string (CaptionType t); -extern std::string caption_type_to_name (CaptionType t); -extern CaptionType string_to_caption_type (std::string s); +extern std::string caption_type_to_string (TextType t); +extern std::string caption_type_to_name (TextType t); +extern TextType string_to_caption_type (std::string s); /** @struct Crop * @brief A description of the crop of an image or video. diff --git a/src/lib/writer.cc b/src/lib/writer.cc index c0a774f03..3ab1cc5b9 100644 --- a/src/lib/writer.cc +++ b/src/lib/writer.cc @@ -665,7 +665,7 @@ Writer::can_fake_write (Frame frame) const } void -Writer::write (PlayerCaption text, CaptionType type, DCPTimePeriod period) +Writer::write (PlayerText text, TextType type, DCPTimePeriod period) { while (_caption_reel[type]->period().to <= period.from) { ++_caption_reel[type]; diff --git a/src/lib/writer.h b/src/lib/writer.h index f67419072..71d11b773 100644 --- a/src/lib/writer.h +++ b/src/lib/writer.h @@ -23,7 +23,7 @@ */ #include "types.h" -#include "player_caption.h" +#include "player_text.h" #include "exception_store.h" #include #include @@ -104,7 +104,7 @@ public: bool can_repeat (Frame) const; void repeat (Frame, Eyes); void write (boost::shared_ptr, DCPTime time); - void write (PlayerCaption text, CaptionType type, DCPTimePeriod period); + void write (PlayerText text, TextType type, DCPTimePeriod period); void write (std::list > fonts); void write (ReferencedReelAsset asset); void finish (); diff --git a/src/lib/wscript b/src/lib/wscript index a841e5b55..a9ed0c621 100644 --- a/src/lib/wscript +++ b/src/lib/wscript @@ -21,7 +21,7 @@ import os import i18n sources = """ - active_captions.cc + active_text.cc analyse_audio_job.cc atmos_mxf_content.cc audio_analysis.cc @@ -38,8 +38,8 @@ sources = """ audio_ring_buffers.cc audio_stream.cc butler.cc - caption_content.cc - caption_decoder.cc + text_content.cc + text_decoder.cc case_insensitive_sorter.cc cinema.cc cinema_kdms.cc @@ -114,7 +114,7 @@ sources = """ mid_side_decoder.cc overlaps.cc player.cc - player_caption.cc + player_text.cc player_video.cc playlist.cc position_image.cc @@ -134,9 +134,9 @@ sources = """ server.cc shuffler.cc string_log_entry.cc - text_caption_file.cc - text_caption_file_content.cc - text_caption_file_decoder.cc + string_text_file.cc + string_text_file_content.cc + string_text_file_decoder.cc timer.cc transcode_job.cc types.cc -- cgit v1.2.3