From e3033879f7693d40f652f013b00c76deed6994da Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Mon, 4 Dec 2023 23:14:40 +0100 Subject: Rename everything. --- src/asset_factory.cc | 4 +- src/combine.cc | 4 +- src/dcp.cc | 12 +- src/interop_subtitle_asset.cc | 340 ---------- src/interop_subtitle_asset.h | 163 ----- src/interop_text_asset.cc | 334 ++++++++++ src/interop_text_asset.h | 163 +++++ src/reel.cc | 10 +- src/reel_closed_caption_asset.cc | 8 +- src/reel_closed_caption_asset.h | 12 +- src/reel_interop_closed_caption_asset.cc | 3 +- src/reel_interop_closed_caption_asset.h | 12 +- src/reel_interop_subtitle_asset.cc | 2 +- src/reel_interop_subtitle_asset.h | 16 +- src/reel_smpte_closed_caption_asset.cc | 2 +- src/reel_smpte_closed_caption_asset.h | 12 +- src/reel_smpte_subtitle_asset.cc | 4 +- src/reel_smpte_subtitle_asset.h | 14 +- src/reel_subtitle_asset.cc | 8 +- src/reel_subtitle_asset.h | 14 +- src/smpte_subtitle_asset.cc | 618 ------------------ src/smpte_subtitle_asset.h | 261 -------- src/smpte_text_asset.cc | 618 ++++++++++++++++++ src/smpte_text_asset.h | 261 ++++++++ src/subtitle.cc | 131 ---- src/subtitle.h | 170 ----- src/subtitle_asset.cc | 1006 ------------------------------ src/subtitle_asset.h | 234 ------- src/subtitle_asset_internal.cc | 287 --------- src/subtitle_asset_internal.h | 225 ------- src/subtitle_image.cc | 8 +- src/subtitle_image.h | 6 +- src/subtitle_standard.cc | 57 -- src/subtitle_standard.h | 57 -- src/subtitle_string.cc | 272 -------- src/subtitle_string.h | 238 ------- src/text.cc | 131 ++++ src/text.h | 170 +++++ src/text_asset.cc | 1000 +++++++++++++++++++++++++++++ src/text_asset.h | 234 +++++++ src/text_asset_internal.cc | 287 +++++++++ src/text_asset_internal.h | 225 +++++++ src/text_standard.cc | 57 ++ src/text_standard.h | 57 ++ src/text_string.cc | 272 ++++++++ src/text_string.h | 238 +++++++ src/verify.cc | 38 +- src/wscript | 30 +- 48 files changed, 4156 insertions(+), 4169 deletions(-) delete mode 100644 src/interop_subtitle_asset.cc delete mode 100644 src/interop_subtitle_asset.h create mode 100644 src/interop_text_asset.cc create mode 100644 src/interop_text_asset.h delete mode 100644 src/smpte_subtitle_asset.cc delete mode 100644 src/smpte_subtitle_asset.h create mode 100644 src/smpte_text_asset.cc create mode 100644 src/smpte_text_asset.h delete mode 100644 src/subtitle.cc delete mode 100644 src/subtitle.h delete mode 100644 src/subtitle_asset.cc delete mode 100644 src/subtitle_asset.h delete mode 100644 src/subtitle_asset_internal.cc delete mode 100644 src/subtitle_asset_internal.h delete mode 100644 src/subtitle_standard.cc delete mode 100644 src/subtitle_standard.h delete mode 100644 src/subtitle_string.cc delete mode 100644 src/subtitle_string.h create mode 100644 src/text.cc create mode 100644 src/text.h create mode 100644 src/text_asset.cc create mode 100644 src/text_asset.h create mode 100644 src/text_asset_internal.cc create mode 100644 src/text_asset_internal.h create mode 100644 src/text_standard.cc create mode 100644 src/text_standard.h create mode 100644 src/text_string.cc create mode 100644 src/text_string.h (limited to 'src') diff --git a/src/asset_factory.cc b/src/asset_factory.cc index d326ba68..4884f090 100644 --- a/src/asset_factory.cc +++ b/src/asset_factory.cc @@ -41,7 +41,7 @@ #include "atmos_asset.h" #include "compose.hpp" #include "mono_picture_asset.h" -#include "smpte_subtitle_asset.h" +#include "smpte_text_asset.h" #include "sound_asset.h" #include "stereo_picture_asset.h" #include "stereo_picture_asset.h" @@ -90,7 +90,7 @@ dcp::asset_factory (boost::filesystem::path path, bool ignore_incorrect_picture_ case ASDCP::ESS_JPEG_2000_S: return make_shared(path); case ASDCP::ESS_TIMED_TEXT: - return make_shared(path); + return make_shared(path); case ASDCP::ESS_DCDATA_DOLBY_ATMOS: return make_shared(path); default: diff --git a/src/combine.cc b/src/combine.cc index b7a625f0..74456bfb 100644 --- a/src/combine.cc +++ b/src/combine.cc @@ -45,7 +45,7 @@ #include "exceptions.h" #include "filesystem.h" #include "font_asset.h" -#include "interop_subtitle_asset.h" +#include "interop_text_asset.h" #include "raw_convert.h" #include #include @@ -139,7 +139,7 @@ dcp::combine ( continue; } - auto sub = dynamic_pointer_cast(j); + auto sub = dynamic_pointer_cast(j); if (sub) { /* Interop fonts are really fiddly. The font files are assets (in the ASSETMAP) * and also linked from the font XML by filename. We have to fix both these things, diff --git a/src/dcp.cc b/src/dcp.cc index d603cfae..a809529e 100644 --- a/src/dcp.cc +++ b/src/dcp.cc @@ -49,7 +49,7 @@ #include "exceptions.h" #include "filesystem.h" #include "font_asset.h" -#include "interop_subtitle_asset.h" +#include "interop_text_asset.h" #include "metadata.h" #include "mono_picture_asset.h" #include "picture_asset.h" @@ -57,7 +57,7 @@ #include "raw_convert.h" #include "reel_asset.h" #include "reel_subtitle_asset.h" -#include "smpte_subtitle_asset.h" +#include "smpte_text_asset.h" #include "sound_asset.h" #include "stereo_picture_asset.h" #include "util.h" @@ -221,7 +221,7 @@ DCP::read (vector* notes, bool ignore_incorrect_picture_m if ( pkl_type == remove_parameters(CPL::static_pkl_type(standard)) || - pkl_type == remove_parameters(InteropSubtitleAsset::static_pkl_type(standard))) { + pkl_type == remove_parameters(InteropTextAsset::static_pkl_type(standard))) { auto p = new xmlpp::DomParser; try { p->parse_file(dcp::filesystem::fix_long_path(path).string()); @@ -243,13 +243,13 @@ DCP::read (vector* notes, bool ignore_incorrect_picture_m if (standard == Standard::SMPTE && notes) { notes->push_back (VerificationNote(VerificationNote::Type::ERROR, VerificationNote::Code::MISMATCHED_STANDARD)); } - other_assets.push_back (make_shared(path)); + other_assets.push_back(make_shared(path)); } } else if ( *pkl_type == remove_parameters(PictureAsset::static_pkl_type(standard)) || *pkl_type == remove_parameters(SoundAsset::static_pkl_type(standard)) || *pkl_type == remove_parameters(AtmosAsset::static_pkl_type(standard)) || - *pkl_type == remove_parameters(SMPTESubtitleAsset::static_pkl_type(standard)) + *pkl_type == remove_parameters(SMPTETextAsset::static_pkl_type(standard)) ) { bool found_threed_marked_as_twod = false; @@ -542,7 +542,7 @@ DCP::assets (bool ignore_unresolved) const auto o = j->asset_ref().asset(); assets.push_back (o); /* More Interop special-casing */ - auto sub = dynamic_pointer_cast(o); + auto sub = dynamic_pointer_cast(o); if (sub) { add_to_container(assets, sub->font_assets()); } diff --git a/src/interop_subtitle_asset.cc b/src/interop_subtitle_asset.cc deleted file mode 100644 index 32c3f66a..00000000 --- a/src/interop_subtitle_asset.cc +++ /dev/null @@ -1,340 +0,0 @@ -/* - Copyright (C) 2012-2021 Carl Hetherington - - This file is part of libdcp. - - libdcp 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. - - libdcp 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 libdcp. If not, see . - - In addition, as a special exception, the copyright holders give - permission to link the code of portions of this program with the - OpenSSL library under certain conditions as described in each - individual source file, and distribute linked combinations - including the two. - - You must obey the GNU General Public License in all respects - for all of the code used other than OpenSSL. If you modify - file(s) with this exception, you may extend this exception to your - version of the file(s), but you are not obligated to do so. If you - do not wish to do so, delete this exception statement from your - version. If you delete this exception statement from all source - files in the program, then also delete it here. -*/ - - -/** @file src/interop_subtitle_asset.cc - * @brief InteropSubtitleAsset class - */ - - -#include "compose.hpp" -#include "dcp_assert.h" -#include "equality_options.h" -#include "filesystem.h" -#include "font_asset.h" -#include "file.h" -#include "interop_load_font_node.h" -#include "interop_subtitle_asset.h" -#include "raw_convert.h" -#include "subtitle_asset_internal.h" -#include "subtitle_image.h" -#include "util.h" -#include "warnings.h" -#include "xml.h" -LIBDCP_DISABLE_WARNINGS -#include -LIBDCP_ENABLE_WARNINGS -#include -#include -#include - - -using std::cerr; -using std::cout; -using std::dynamic_pointer_cast; -using std::make_shared; -using std::shared_ptr; -using std::string; -using std::vector; -using boost::optional; -using namespace dcp; - - -InteropSubtitleAsset::InteropSubtitleAsset (boost::filesystem::path file) - : SubtitleAsset (file) -{ - _raw_xml = dcp::file_to_string (file); - - auto xml = make_shared("DCSubtitle"); - xml->read_file(dcp::filesystem::fix_long_path(file)); - _id = xml->string_child ("SubtitleID"); - _reel_number = xml->string_child ("ReelNumber"); - _language = xml->string_child ("Language"); - _movie_title = xml->string_child ("MovieTitle"); - _load_font_nodes = type_children (xml, "LoadFont"); - - /* Now we need to drop down to xmlpp */ - - vector ps; - for (auto i: xml->node()->get_children()) { - auto e = dynamic_cast(i); - if (e && (e->get_name() == "Font" || e->get_name() == "Subtitle")) { - parse_subtitles (e, ps, optional(), Standard::INTEROP); - } - } - - for (auto i: _subtitles) { - auto si = dynamic_pointer_cast(i); - if (si) { - si->read_png_file (file.parent_path() / String::compose("%1.png", si->id())); - } - } -} - - -InteropSubtitleAsset::InteropSubtitleAsset () -{ - -} - - -string -InteropSubtitleAsset::xml_as_string () const -{ - xmlpp::Document doc; - auto root = doc.create_root_node ("DCSubtitle"); - root->set_attribute ("Version", "1.0"); - - root->add_child("SubtitleID")->add_child_text (_id); - root->add_child("MovieTitle")->add_child_text (_movie_title); - root->add_child("ReelNumber")->add_child_text (raw_convert (_reel_number)); - root->add_child("Language")->add_child_text (_language); - - for (auto i: _load_font_nodes) { - auto load_font = root->add_child("LoadFont"); - load_font->set_attribute ("Id", i->id); - load_font->set_attribute ("URI", i->uri); - } - - subtitles_as_xml (root, 250, Standard::INTEROP); - - return format_xml(doc, {}); -} - - -void -InteropSubtitleAsset::add_font (string load_id, dcp::ArrayData data) -{ - _fonts.push_back (Font(load_id, make_uuid(), data)); - auto const uri = String::compose("font_%1.ttf", _load_font_nodes.size()); - _load_font_nodes.push_back (make_shared(load_id, uri)); -} - - -bool -InteropSubtitleAsset::equals(shared_ptr other_asset, EqualityOptions const& options, NoteHandler note) const -{ - if (!SubtitleAsset::equals (other_asset, options, note)) { - return false; - } - - auto other = dynamic_pointer_cast (other_asset); - if (!other) { - return false; - } - - if (!options.load_font_nodes_can_differ) { - auto i = _load_font_nodes.begin(); - auto j = other->_load_font_nodes.begin(); - - while (i != _load_font_nodes.end ()) { - if (j == other->_load_font_nodes.end ()) { - note (NoteType::ERROR, " nodes differ"); - return false; - } - - if (**i != **j) { - note (NoteType::ERROR, " nodes differ"); - return false; - } - - ++i; - ++j; - } - } - - if (_movie_title != other->_movie_title) { - note (NoteType::ERROR, "Subtitle movie titles differ"); - return false; - } - - return true; -} - - -vector> -InteropSubtitleAsset::load_font_nodes () const -{ - vector> lf; - copy (_load_font_nodes.begin(), _load_font_nodes.end(), back_inserter (lf)); - return lf; -} - - -void -InteropSubtitleAsset::write (boost::filesystem::path p) const -{ - File f(p, "wb"); - if (!f) { - throw FileError ("Could not open file for writing", p, -1); - } - - _raw_xml = xml_as_string (); - /* length() here gives bytes not characters */ - f.write(_raw_xml->c_str(), 1, _raw_xml->length()); - - _file = p; - - /* Image subtitles */ - for (auto i: _subtitles) { - auto im = dynamic_pointer_cast (i); - if (im) { - im->write_png_file(p.parent_path() / String::compose("%1.png", im->id())); - } - } - - /* Fonts */ - for (auto i: _load_font_nodes) { - auto file = p.parent_path() / i->uri; - auto font_with_id = std::find_if(_fonts.begin(), _fonts.end(), [i](Font const& font) { return font.load_id == i->id; }); - if (font_with_id != _fonts.end()) { - font_with_id->data.write(file); - font_with_id->file = file; - } - } -} - - -/** Look at a supplied list of assets and find the fonts. Then match these - * fonts up with anything requested by a so that _fonts contains - * a list of font ID, load ID and data. - */ -void -InteropSubtitleAsset::resolve_fonts (vector> assets) -{ - for (auto asset: assets) { - auto font = dynamic_pointer_cast(asset); - if (!font) { - continue; - } - - DCP_ASSERT(_file); - - for (auto load_font_node: _load_font_nodes) { - auto const path_in_load_font_node = _file->parent_path() / load_font_node->uri; - if (font->file() && path_in_load_font_node == *font->file()) { - auto existing = std::find_if(_fonts.begin(), _fonts.end(), [load_font_node](Font const& font) { return font.load_id == load_font_node->id; }); - if (existing != _fonts.end()) { - *existing = Font(load_font_node->id, asset->id(), font->file().get()); - } else { - _fonts.push_back(Font(load_font_node->id, asset->id(), font->file().get())); - } - } - } - } -} - - -vector> -InteropSubtitleAsset::font_assets() -{ - vector> assets; - for (auto const& i: _fonts) { - DCP_ASSERT (i.file); - assets.push_back(make_shared(i.uuid, i.file.get())); - } - return assets; -} - - -vector> -InteropSubtitleAsset::font_assets() const -{ - vector> assets; - for (auto const& i: _fonts) { - DCP_ASSERT (i.file); - assets.push_back(make_shared(i.uuid, i.file.get())); - } - return assets; -} - - -void -InteropSubtitleAsset::add_to_assetmap (AssetMap& asset_map, boost::filesystem::path root) const -{ - Asset::add_to_assetmap(asset_map, root); - - for (auto i: _subtitles) { - auto im = dynamic_pointer_cast(i); - if (im) { - DCP_ASSERT(im->file()); - add_file_to_assetmap(asset_map, root, im->file().get(), im->id()); - } - } -} - - -void -InteropSubtitleAsset::add_to_pkl (shared_ptr pkl, boost::filesystem::path root) const -{ - Asset::add_to_pkl (pkl, root); - - for (auto i: _subtitles) { - auto im = dynamic_pointer_cast (i); - if (im) { - auto png_image = im->png_image (); - pkl->add_asset(im->id(), optional(), make_digest(png_image), png_image.size(), "image/png", root.filename().string()); - } - } -} - - -void -InteropSubtitleAsset::set_font_file (string load_id, boost::filesystem::path file) -{ - for (auto& i: _fonts) { - if (i.load_id == load_id) { - i.file = file; - } - } - - for (auto i: _load_font_nodes) { - if (i->id == load_id) { - i->uri = file.filename().string(); - } - } -} - - -vector -InteropSubtitleAsset::unresolved_fonts() const -{ - vector unresolved; - for (auto load_font_node: _load_font_nodes) { - if (std::find_if(_fonts.begin(), _fonts.end(), [load_font_node](Font const& font) { return font.load_id == load_font_node->id; }) == _fonts.end()) { - unresolved.push_back(load_font_node->id); - } - } - return unresolved; -} - diff --git a/src/interop_subtitle_asset.h b/src/interop_subtitle_asset.h deleted file mode 100644 index f63740d5..00000000 --- a/src/interop_subtitle_asset.h +++ /dev/null @@ -1,163 +0,0 @@ -/* - Copyright (C) 2012-2021 Carl Hetherington - - This file is part of libdcp. - - libdcp 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. - - libdcp 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 libdcp. If not, see . - - In addition, as a special exception, the copyright holders give - permission to link the code of portions of this program with the - OpenSSL library under certain conditions as described in each - individual source file, and distribute linked combinations - including the two. - - You must obey the GNU General Public License in all respects - for all of the code used other than OpenSSL. If you modify - file(s) with this exception, you may extend this exception to your - version of the file(s), but you are not obligated to do so. If you - do not wish to do so, delete this exception statement from your - version. If you delete this exception statement from all source - files in the program, then also delete it here. -*/ - - -/** @file src/interop_subtitle_asset.h - * @brief InteropSubtitleAsset class - */ - - -#ifndef DCP_INTEROP_SUBTITLE_ASSET_H -#define DCP_INTEROP_SUBTITLE_ASSET_H - - -#include "subtitle_asset.h" -#include "subtitle_standard.h" -#include - - -namespace dcp { - - -class InteropLoadFontNode; - - -/** @class InteropSubtitleAsset - * @brief A set of subtitles to be read and/or written in the Inter-Op format - * - * Inter-Op subtitles are sometimes known as CineCanvas. - */ -class InteropSubtitleAsset : public SubtitleAsset -{ -public: - InteropSubtitleAsset (); - explicit InteropSubtitleAsset (boost::filesystem::path file); - - bool equals ( - std::shared_ptr, - EqualityOptions const&, - NoteHandler note - ) const override; - - void add_to_assetmap (AssetMap& asset_map, boost::filesystem::path root) const override; - void add_to_pkl (std::shared_ptr pkl, boost::filesystem::path root) const override; - - std::vector> load_font_nodes () const override; - - void add_font (std::string load_id, dcp::ArrayData data) override; - - std::string xml_as_string () const override; - - /** Write this content to an XML file with its fonts alongside */ - void write (boost::filesystem::path path) const override; - - void resolve_fonts (std::vector> assets); - void set_font_file (std::string load_id, boost::filesystem::path file); - std::vector> font_assets(); - std::vector> font_assets() const; - - /** @return the IDs of fonts for which we have not (yet) found a font asset. - * This could be because resolve_fonts() has not yet been called, or because there is - * a missing font file. - */ - std::vector unresolved_fonts() const; - - /** Set the reel number or sub-element identifier - * of these subtitles. - * @param n New reel number. - */ - void set_reel_number (std::string n) { - _reel_number = n; - } - - /** Set the language tag of these subtitles. - * @param l New language. - */ - void set_language (std::string l) { - _language = l; - } - - /** @return title of the movie that the subtitles are for */ - void set_movie_title (std::string m) { - _movie_title = m; - } - - /** @return reel number or sub-element of a programme that - * these subtitles refer to. - */ - std::string reel_number () const { - return _reel_number; - } - - /** @return language used in the subtitles */ - std::string language () const { - return _language; - } - - /** @return movie title that these subtitles are for */ - std::string movie_title () const { - return _movie_title; - } - - int time_code_rate () const override { - /* Interop can use either; just pick one */ - return 1000; - } - - SubtitleStandard subtitle_standard() const override { - return SubtitleStandard::INTEROP; - } - - static std::string static_pkl_type (Standard) { - return "text/xml;asdcpKind=Subtitle"; - } - -protected: - - std::string pkl_type (Standard s) const override { - return static_pkl_type (s); - } - -private: - std::string _reel_number; - std::string _language; - std::string _movie_title; - std::vector> _load_font_nodes; -}; - - -} - - -#endif - diff --git a/src/interop_text_asset.cc b/src/interop_text_asset.cc new file mode 100644 index 00000000..d9515573 --- /dev/null +++ b/src/interop_text_asset.cc @@ -0,0 +1,334 @@ +/* + Copyright (C) 2012-2021 Carl Hetherington + + This file is part of libdcp. + + libdcp 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. + + libdcp 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 libdcp. If not, see . + + In addition, as a special exception, the copyright holders give + permission to link the code of portions of this program with the + OpenSSL library under certain conditions as described in each + individual source file, and distribute linked combinations + including the two. + + You must obey the GNU General Public License in all respects + for all of the code used other than OpenSSL. If you modify + file(s) with this exception, you may extend this exception to your + version of the file(s), but you are not obligated to do so. If you + do not wish to do so, delete this exception statement from your + version. If you delete this exception statement from all source + files in the program, then also delete it here. +*/ + + +/** @file src/interop_subtitle_asset.cc + * @brief InteropTextAsset class + */ + + +#include "compose.hpp" +#include "dcp_assert.h" +#include "equality_options.h" +#include "filesystem.h" +#include "font_asset.h" +#include "file.h" +#include "interop_load_font_node.h" +#include "interop_text_asset.h" +#include "raw_convert.h" +#include "subtitle_image.h" +#include "text_asset_internal.h" +#include "util.h" +#include "warnings.h" +#include "xml.h" +LIBDCP_DISABLE_WARNINGS +#include +LIBDCP_ENABLE_WARNINGS +#include +#include +#include + + +using std::cerr; +using std::cout; +using std::dynamic_pointer_cast; +using std::make_shared; +using std::shared_ptr; +using std::string; +using std::vector; +using boost::optional; +using namespace dcp; + + +InteropTextAsset::InteropTextAsset(boost::filesystem::path file) + : TextAsset(file) +{ + _raw_xml = dcp::file_to_string (file); + + auto xml = make_shared("DCSubtitle"); + xml->read_file(dcp::filesystem::fix_long_path(file)); + _id = xml->string_child ("SubtitleID"); + _reel_number = xml->string_child ("ReelNumber"); + _language = xml->string_child ("Language"); + _movie_title = xml->string_child ("MovieTitle"); + _load_font_nodes = type_children (xml, "LoadFont"); + + /* Now we need to drop down to xmlpp */ + + vector ps; + for (auto i: xml->node()->get_children()) { + auto e = dynamic_cast(i); + if (e && (e->get_name() == "Font" || e->get_name() == "Subtitle")) { + parse_texts(e, ps, optional(), Standard::INTEROP); + } + } + + for (auto i: _texts) { + auto si = dynamic_pointer_cast(i); + if (si) { + si->read_png_file (file.parent_path() / String::compose("%1.png", si->id())); + } + } +} + + +string +InteropTextAsset::xml_as_string() const +{ + xmlpp::Document doc; + auto root = doc.create_root_node ("DCSubtitle"); + root->set_attribute ("Version", "1.0"); + + root->add_child("SubtitleID")->add_child_text (_id); + root->add_child("MovieTitle")->add_child_text (_movie_title); + root->add_child("ReelNumber")->add_child_text (raw_convert (_reel_number)); + root->add_child("Language")->add_child_text (_language); + + for (auto i: _load_font_nodes) { + auto load_font = root->add_child("LoadFont"); + load_font->set_attribute ("Id", i->id); + load_font->set_attribute ("URI", i->uri); + } + + texts_as_xml(root, 250, Standard::INTEROP); + + return format_xml(doc, {}); +} + + +void +InteropTextAsset::add_font(string load_id, dcp::ArrayData data) +{ + _fonts.push_back (Font(load_id, make_uuid(), data)); + auto const uri = String::compose("font_%1.ttf", _load_font_nodes.size()); + _load_font_nodes.push_back (make_shared(load_id, uri)); +} + + +bool +InteropTextAsset::equals(shared_ptr other_asset, EqualityOptions const& options, NoteHandler note) const +{ + if (!TextAsset::equals(other_asset, options, note)) { + return false; + } + + auto other = dynamic_pointer_cast(other_asset); + if (!other) { + return false; + } + + if (!options.load_font_nodes_can_differ) { + auto i = _load_font_nodes.begin(); + auto j = other->_load_font_nodes.begin(); + + while (i != _load_font_nodes.end ()) { + if (j == other->_load_font_nodes.end ()) { + note (NoteType::ERROR, " nodes differ"); + return false; + } + + if (**i != **j) { + note (NoteType::ERROR, " nodes differ"); + return false; + } + + ++i; + ++j; + } + } + + if (_movie_title != other->_movie_title) { + note (NoteType::ERROR, "Subtitle or closed caption movie titles differ"); + return false; + } + + return true; +} + + +vector> +InteropTextAsset::load_font_nodes() const +{ + vector> lf; + copy (_load_font_nodes.begin(), _load_font_nodes.end(), back_inserter (lf)); + return lf; +} + + +void +InteropTextAsset::write(boost::filesystem::path p) const +{ + File f(p, "wb"); + if (!f) { + throw FileError ("Could not open file for writing", p, -1); + } + + _raw_xml = xml_as_string (); + /* length() here gives bytes not characters */ + f.write(_raw_xml->c_str(), 1, _raw_xml->length()); + + _file = p; + + /* Image subtitles */ + for (auto i: _texts) { + auto im = dynamic_pointer_cast (i); + if (im) { + im->write_png_file(p.parent_path() / String::compose("%1.png", im->id())); + } + } + + /* Fonts */ + for (auto i: _load_font_nodes) { + auto file = p.parent_path() / i->uri; + auto font_with_id = std::find_if(_fonts.begin(), _fonts.end(), [i](Font const& font) { return font.load_id == i->id; }); + if (font_with_id != _fonts.end()) { + font_with_id->data.write(file); + font_with_id->file = file; + } + } +} + + +/** Look at a supplied list of assets and find the fonts. Then match these + * fonts up with anything requested by a so that _fonts contains + * a list of font ID, load ID and data. + */ +void +InteropTextAsset::resolve_fonts(vector> assets) +{ + for (auto asset: assets) { + auto font = dynamic_pointer_cast(asset); + if (!font) { + continue; + } + + DCP_ASSERT(_file); + + for (auto load_font_node: _load_font_nodes) { + auto const path_in_load_font_node = _file->parent_path() / load_font_node->uri; + if (font->file() && path_in_load_font_node == *font->file()) { + auto existing = std::find_if(_fonts.begin(), _fonts.end(), [load_font_node](Font const& font) { return font.load_id == load_font_node->id; }); + if (existing != _fonts.end()) { + *existing = Font(load_font_node->id, asset->id(), font->file().get()); + } else { + _fonts.push_back(Font(load_font_node->id, asset->id(), font->file().get())); + } + } + } + } +} + + +vector> +InteropTextAsset::font_assets() +{ + vector> assets; + for (auto const& i: _fonts) { + DCP_ASSERT (i.file); + assets.push_back(make_shared(i.uuid, i.file.get())); + } + return assets; +} + + +vector> +InteropTextAsset::font_assets() const +{ + vector> assets; + for (auto const& i: _fonts) { + DCP_ASSERT (i.file); + assets.push_back(make_shared(i.uuid, i.file.get())); + } + return assets; +} + + +void +InteropTextAsset::add_to_assetmap(AssetMap& asset_map, boost::filesystem::path root) const +{ + Asset::add_to_assetmap(asset_map, root); + + for (auto i: _texts) { + auto im = dynamic_pointer_cast(i); + if (im) { + DCP_ASSERT(im->file()); + add_file_to_assetmap(asset_map, root, im->file().get(), im->id()); + } + } +} + + +void +InteropTextAsset::add_to_pkl(shared_ptr pkl, boost::filesystem::path root) const +{ + Asset::add_to_pkl (pkl, root); + + for (auto i: _texts) { + auto im = dynamic_pointer_cast (i); + if (im) { + auto png_image = im->png_image (); + pkl->add_asset(im->id(), optional(), make_digest(png_image), png_image.size(), "image/png", root.filename().string()); + } + } +} + + +void +InteropTextAsset::set_font_file(string load_id, boost::filesystem::path file) +{ + for (auto& i: _fonts) { + if (i.load_id == load_id) { + i.file = file; + } + } + + for (auto i: _load_font_nodes) { + if (i->id == load_id) { + i->uri = file.filename().string(); + } + } +} + + +vector +InteropTextAsset::unresolved_fonts() const +{ + vector unresolved; + for (auto load_font_node: _load_font_nodes) { + if (std::find_if(_fonts.begin(), _fonts.end(), [load_font_node](Font const& font) { return font.load_id == load_font_node->id; }) == _fonts.end()) { + unresolved.push_back(load_font_node->id); + } + } + return unresolved; +} + diff --git a/src/interop_text_asset.h b/src/interop_text_asset.h new file mode 100644 index 00000000..b2602224 --- /dev/null +++ b/src/interop_text_asset.h @@ -0,0 +1,163 @@ +/* + Copyright (C) 2012-2021 Carl Hetherington + + This file is part of libdcp. + + libdcp 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. + + libdcp 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 libdcp. If not, see . + + In addition, as a special exception, the copyright holders give + permission to link the code of portions of this program with the + OpenSSL library under certain conditions as described in each + individual source file, and distribute linked combinations + including the two. + + You must obey the GNU General Public License in all respects + for all of the code used other than OpenSSL. If you modify + file(s) with this exception, you may extend this exception to your + version of the file(s), but you are not obligated to do so. If you + do not wish to do so, delete this exception statement from your + version. If you delete this exception statement from all source + files in the program, then also delete it here. +*/ + + +/** @file src/interop_text_asset.h + * @brief InteropTextAsset class + */ + + +#ifndef DCP_INTEROP_TEXT_ASSET_H +#define DCP_INTEROP_TEXT_ASSET_H + + +#include "text_asset.h" +#include "text_standard.h" +#include + + +namespace dcp { + + +class InteropLoadFontNode; + + +/** @class InteropTextAsset + * @brief A set of subtitles or closed captions to be read and/or written in the Inter-Op format + * + * Inter-Op subtitles are sometimes known as CineCanvas. + */ +class InteropTextAsset : public TextAsset +{ +public: + InteropTextAsset() = default; + explicit InteropTextAsset(boost::filesystem::path file); + + bool equals ( + std::shared_ptr, + EqualityOptions const&, + NoteHandler note + ) const override; + + void add_to_assetmap (AssetMap& asset_map, boost::filesystem::path root) const override; + void add_to_pkl (std::shared_ptr pkl, boost::filesystem::path root) const override; + + std::vector> load_font_nodes () const override; + + void add_font (std::string load_id, dcp::ArrayData data) override; + + std::string xml_as_string () const override; + + /** Write this content to an XML file with its fonts alongside */ + void write (boost::filesystem::path path) const override; + + void resolve_fonts (std::vector> assets); + void set_font_file (std::string load_id, boost::filesystem::path file); + std::vector> font_assets(); + std::vector> font_assets() const; + + /** @return the IDs of fonts for which we have not (yet) found a font asset. + * This could be because resolve_fonts() has not yet been called, or because there is + * a missing font file. + */ + std::vector unresolved_fonts() const; + + /** Set the reel number or sub-element identifier + * of these subtitles / closed captions. + * @param n New reel number. + */ + void set_reel_number (std::string n) { + _reel_number = n; + } + + /** Set the language tag of these subtitles / closed captions. + * @param l New language. + */ + void set_language (std::string l) { + _language = l; + } + + /** @return title of the movie that the subtitles / closed captions are for */ + void set_movie_title (std::string m) { + _movie_title = m; + } + + /** @return reel number or sub-element of a programme that + * these subtitles / closed captions refer to. + */ + std::string reel_number () const { + return _reel_number; + } + + /** @return language used in the subtitles / closed captions */ + std::string language () const { + return _language; + } + + /** @return movie title that these subtitles / closed captions are for */ + std::string movie_title () const { + return _movie_title; + } + + int time_code_rate () const override { + /* Interop can use either; just pick one */ + return 1000; + } + + TextStandard text_standard() const override { + return TextStandard::INTEROP; + } + + static std::string static_pkl_type (Standard) { + return "text/xml;asdcpKind=Subtitle"; + } + +protected: + + std::string pkl_type (Standard s) const override { + return static_pkl_type (s); + } + +private: + std::string _reel_number; + std::string _language; + std::string _movie_title; + std::vector> _load_font_nodes; +}; + + +} + + +#endif + diff --git a/src/reel.cc b/src/reel.cc index a8481d59..95769140 100644 --- a/src/reel.cc +++ b/src/reel.cc @@ -40,7 +40,7 @@ #include "decrypted_kdm.h" #include "decrypted_kdm_key.h" #include "equality_options.h" -#include "interop_subtitle_asset.h" +#include "interop_text_asset.h" #include "mono_picture_asset.h" #include "picture_asset.h" #include "reel.h" @@ -55,10 +55,10 @@ #include "reel_sound_asset.h" #include "reel_stereo_picture_asset.h" #include "reel_subtitle_asset.h" -#include "smpte_subtitle_asset.h" +#include "smpte_text_asset.h" #include "sound_asset.h" #include "stereo_picture_asset.h" -#include "subtitle_asset.h" +#include "text_asset.h" #include "util.h" #include #include @@ -409,7 +409,7 @@ Reel::resolve_refs (vector> assets) /* Interop subtitle handling is all special cases */ if (_main_subtitle->asset_ref().resolved()) { - auto iop = dynamic_pointer_cast (_main_subtitle->asset_ref().asset()); + auto iop = dynamic_pointer_cast(_main_subtitle->asset_ref().asset()); if (iop) { iop->resolve_fonts (assets); } @@ -421,7 +421,7 @@ Reel::resolve_refs (vector> assets) /* Interop subtitle handling is all special cases */ if (i->asset_ref().resolved()) { - auto iop = dynamic_pointer_cast (i->asset_ref().asset()); + auto iop = dynamic_pointer_cast(i->asset_ref().asset()); if (iop) { iop->resolve_fonts (assets); } diff --git a/src/reel_closed_caption_asset.cc b/src/reel_closed_caption_asset.cc index e5649d6a..a9ba32f5 100644 --- a/src/reel_closed_caption_asset.cc +++ b/src/reel_closed_caption_asset.cc @@ -39,8 +39,8 @@ #include "dcp_assert.h" #include "reel_closed_caption_asset.h" -#include "smpte_subtitle_asset.h" -#include "subtitle_asset.h" +#include "smpte_text_asset.h" +#include "text_asset.h" #include "warnings.h" LIBDCP_DISABLE_WARNINGS #include @@ -53,10 +53,10 @@ using std::dynamic_pointer_cast; using namespace dcp; -ReelClosedCaptionAsset::ReelClosedCaptionAsset (std::shared_ptr asset, Fraction edit_rate, int64_t intrinsic_duration, int64_t entry_point) +ReelClosedCaptionAsset::ReelClosedCaptionAsset(std::shared_ptr asset, Fraction edit_rate, int64_t intrinsic_duration, int64_t entry_point) : ReelFileAsset ( asset, - dynamic_pointer_cast(asset) ? dynamic_pointer_cast(asset)->key_id() : boost::none, + dynamic_pointer_cast(asset) ? dynamic_pointer_cast(asset)->key_id() : boost::none, asset->id(), edit_rate, intrinsic_duration, diff --git a/src/reel_closed_caption_asset.h b/src/reel_closed_caption_asset.h index 405de34b..adc1e1eb 100644 --- a/src/reel_closed_caption_asset.h +++ b/src/reel_closed_caption_asset.h @@ -44,7 +44,7 @@ #include "language_tag.h" #include "reel_asset.h" #include "reel_file_asset.h" -#include "subtitle_asset.h" +#include "text_asset.h" struct verify_invalid_language2; @@ -59,15 +59,15 @@ namespace dcp { class ReelClosedCaptionAsset : public ReelFileAsset { public: - ReelClosedCaptionAsset (std::shared_ptr asset, Fraction edit_rate, int64_t intrinsic_duration, int64_t entry_point); + ReelClosedCaptionAsset(std::shared_ptr asset, Fraction edit_rate, int64_t intrinsic_duration, int64_t entry_point); explicit ReelClosedCaptionAsset (std::shared_ptr); - std::shared_ptr asset () const { - return asset_of_type(); + std::shared_ptr asset () const { + return asset_of_type(); } - std::shared_ptr asset () { - return asset_of_type(); + std::shared_ptr asset () { + return asset_of_type(); } bool equals(std::shared_ptr, EqualityOptions const&, NoteHandler) const; diff --git a/src/reel_interop_closed_caption_asset.cc b/src/reel_interop_closed_caption_asset.cc index be968068..ea1b85c9 100644 --- a/src/reel_interop_closed_caption_asset.cc +++ b/src/reel_interop_closed_caption_asset.cc @@ -46,14 +46,13 @@ using std::string; using namespace dcp; -ReelInteropClosedCaptionAsset::ReelInteropClosedCaptionAsset (shared_ptr asset, Fraction edit_rate, int64_t intrinsic_duration, int64_t entry_point) +ReelInteropClosedCaptionAsset::ReelInteropClosedCaptionAsset(shared_ptr asset, Fraction edit_rate, int64_t intrinsic_duration, int64_t entry_point) : ReelClosedCaptionAsset (asset, edit_rate, intrinsic_duration, entry_point) { } - ReelInteropClosedCaptionAsset::ReelInteropClosedCaptionAsset (shared_ptr node) : ReelClosedCaptionAsset (node) { diff --git a/src/reel_interop_closed_caption_asset.h b/src/reel_interop_closed_caption_asset.h index 5e8f7c1e..7e1ee6c5 100644 --- a/src/reel_interop_closed_caption_asset.h +++ b/src/reel_interop_closed_caption_asset.h @@ -41,7 +41,7 @@ #define LIBDCP_REEL_INTEROP_CLOSED_CAPTION_ASSET_H -#include "interop_subtitle_asset.h" +#include "interop_text_asset.h" #include "reel_closed_caption_asset.h" @@ -51,15 +51,15 @@ namespace dcp { class ReelInteropClosedCaptionAsset : public ReelClosedCaptionAsset { public: - ReelInteropClosedCaptionAsset (std::shared_ptr asset, Fraction edit_rate, int64_t intrinsic_duration, int64_t entry_point); + ReelInteropClosedCaptionAsset(std::shared_ptr asset, Fraction edit_rate, int64_t intrinsic_duration, int64_t entry_point); explicit ReelInteropClosedCaptionAsset (std::shared_ptr); - std::shared_ptr interop_asset () const { - return asset_of_type(); + std::shared_ptr interop_asset() const { + return asset_of_type(); } - std::shared_ptr interop_asset () { - return asset_of_type(); + std::shared_ptr interop_asset() { + return asset_of_type(); } xmlpp::Node* write_to_cpl (xmlpp::Node* node, Standard standard) const override; diff --git a/src/reel_interop_subtitle_asset.cc b/src/reel_interop_subtitle_asset.cc index 5f295d53..6b9ef79c 100644 --- a/src/reel_interop_subtitle_asset.cc +++ b/src/reel_interop_subtitle_asset.cc @@ -50,7 +50,7 @@ using boost::optional; using namespace dcp; -ReelInteropSubtitleAsset::ReelInteropSubtitleAsset (std::shared_ptr asset, Fraction edit_rate, int64_t intrinsic_duration, int64_t entry_point) +ReelInteropSubtitleAsset::ReelInteropSubtitleAsset(std::shared_ptr asset, Fraction edit_rate, int64_t intrinsic_duration, int64_t entry_point) : ReelSubtitleAsset (asset, edit_rate, intrinsic_duration, entry_point) { diff --git a/src/reel_interop_subtitle_asset.h b/src/reel_interop_subtitle_asset.h index 7e90e9e0..1a7da9d4 100644 --- a/src/reel_interop_subtitle_asset.h +++ b/src/reel_interop_subtitle_asset.h @@ -32,12 +32,12 @@ */ -/** @file src/reel_interop_subtitle_asset.h - * @brief ReelInteropSubtitleAsset class +/** @file src/reel_interop_text_asset.h + * @brief ReelInteropTextAsset class */ -#include "interop_subtitle_asset.h" +#include "interop_text_asset.h" #include "reel_file_asset.h" #include "reel_subtitle_asset.h" @@ -51,15 +51,15 @@ namespace dcp { class ReelInteropSubtitleAsset : public ReelSubtitleAsset { public: - ReelInteropSubtitleAsset (std::shared_ptr asset, Fraction edit_rate, int64_t intrinsic_duration, int64_t entry_point); + ReelInteropSubtitleAsset(std::shared_ptr asset, Fraction edit_rate, int64_t intrinsic_duration, int64_t entry_point); explicit ReelInteropSubtitleAsset (std::shared_ptr); - std::shared_ptr interop_asset () const { - return asset_of_type(); + std::shared_ptr interop_asset () const { + return asset_of_type(); } - std::shared_ptr interop_asset () { - return asset_of_type(); + std::shared_ptr interop_asset () { + return asset_of_type(); } }; diff --git a/src/reel_smpte_closed_caption_asset.cc b/src/reel_smpte_closed_caption_asset.cc index a2a68202..22f23b8e 100644 --- a/src/reel_smpte_closed_caption_asset.cc +++ b/src/reel_smpte_closed_caption_asset.cc @@ -51,7 +51,7 @@ using std::string; using namespace dcp; -ReelSMPTEClosedCaptionAsset::ReelSMPTEClosedCaptionAsset (shared_ptr asset, Fraction edit_rate, int64_t intrinsic_duration, int64_t entry_point) +ReelSMPTEClosedCaptionAsset::ReelSMPTEClosedCaptionAsset(shared_ptr asset, Fraction edit_rate, int64_t intrinsic_duration, int64_t entry_point) : ReelClosedCaptionAsset (asset, edit_rate, intrinsic_duration, entry_point) { diff --git a/src/reel_smpte_closed_caption_asset.h b/src/reel_smpte_closed_caption_asset.h index 32a79efd..e4eb4f64 100644 --- a/src/reel_smpte_closed_caption_asset.h +++ b/src/reel_smpte_closed_caption_asset.h @@ -43,7 +43,7 @@ #include "reel_file_asset.h" #include "reel_closed_caption_asset.h" -#include "smpte_subtitle_asset.h" +#include "smpte_text_asset.h" namespace dcp { @@ -52,15 +52,15 @@ namespace dcp { class ReelSMPTEClosedCaptionAsset : public ReelClosedCaptionAsset { public: - ReelSMPTEClosedCaptionAsset (std::shared_ptr asset, Fraction edit_rate, int64_t intrinsic_duration, int64_t entry_point); + ReelSMPTEClosedCaptionAsset(std::shared_ptr asset, Fraction edit_rate, int64_t intrinsic_duration, int64_t entry_point); explicit ReelSMPTEClosedCaptionAsset (std::shared_ptr); - std::shared_ptr smpte_asset () { - return asset_of_type(); + std::shared_ptr smpte_asset() { + return asset_of_type(); } - std::shared_ptr smpte_asset () const { - return asset_of_type(); + std::shared_ptr smpte_asset() const { + return asset_of_type(); } xmlpp::Node* write_to_cpl (xmlpp::Node* node, Standard standard) const override; diff --git a/src/reel_smpte_subtitle_asset.cc b/src/reel_smpte_subtitle_asset.cc index 64440547..80762269 100644 --- a/src/reel_smpte_subtitle_asset.cc +++ b/src/reel_smpte_subtitle_asset.cc @@ -38,7 +38,7 @@ #include "reel_smpte_subtitle_asset.h" -#include "smpte_subtitle_asset.h" +#include "smpte_text_asset.h" #include "warnings.h" LIBDCP_DISABLE_WARNINGS #include @@ -51,7 +51,7 @@ using boost::optional; using namespace dcp; -ReelSMPTESubtitleAsset::ReelSMPTESubtitleAsset (shared_ptr asset, Fraction edit_rate, int64_t intrinsic_duration, int64_t entry_point) +ReelSMPTESubtitleAsset::ReelSMPTESubtitleAsset(shared_ptr asset, Fraction edit_rate, int64_t intrinsic_duration, int64_t entry_point) : ReelSubtitleAsset (asset, edit_rate, intrinsic_duration, entry_point) { diff --git a/src/reel_smpte_subtitle_asset.h b/src/reel_smpte_subtitle_asset.h index 2a097309..d2ba3fd4 100644 --- a/src/reel_smpte_subtitle_asset.h +++ b/src/reel_smpte_subtitle_asset.h @@ -38,13 +38,13 @@ #include "reel_subtitle_asset.h" -#include "smpte_subtitle_asset.h" +#include "smpte_text_asset.h" namespace dcp { -class SMPTESubtitleAsset; +class SMPTETextAsset; /** @class ReelSMPTESubtitleAsset @@ -53,15 +53,15 @@ class SMPTESubtitleAsset; class ReelSMPTESubtitleAsset : public ReelSubtitleAsset { public: - ReelSMPTESubtitleAsset (std::shared_ptr asset, Fraction edit_rate, int64_t intrinsic_duration, int64_t entry_point); + ReelSMPTESubtitleAsset(std::shared_ptr asset, Fraction edit_rate, int64_t intrinsic_duration, int64_t entry_point); explicit ReelSMPTESubtitleAsset (std::shared_ptr); - std::shared_ptr smpte_asset () const { - return asset_of_type(); + std::shared_ptr smpte_asset() const { + return asset_of_type(); } - std::shared_ptr smpte_asset () { - return asset_of_type(); + std::shared_ptr smpte_asset() { + return asset_of_type(); } private: diff --git a/src/reel_subtitle_asset.cc b/src/reel_subtitle_asset.cc index d856a05e..6e091c81 100644 --- a/src/reel_subtitle_asset.cc +++ b/src/reel_subtitle_asset.cc @@ -39,8 +39,8 @@ #include "language_tag.h" #include "reel_subtitle_asset.h" -#include "smpte_subtitle_asset.h" -#include "subtitle_asset.h" +#include "smpte_text_asset.h" +#include "text_asset.h" #include "warnings.h" LIBDCP_DISABLE_WARNINGS #include @@ -54,10 +54,10 @@ using boost::optional; using namespace dcp; -ReelSubtitleAsset::ReelSubtitleAsset (std::shared_ptr asset, Fraction edit_rate, int64_t intrinsic_duration, int64_t entry_point) +ReelSubtitleAsset::ReelSubtitleAsset(std::shared_ptr asset, Fraction edit_rate, int64_t intrinsic_duration, int64_t entry_point) : ReelFileAsset ( asset, - dynamic_pointer_cast(asset) ? dynamic_pointer_cast(asset)->key_id() : boost::none, + dynamic_pointer_cast(asset) ? dynamic_pointer_cast(asset)->key_id() : boost::none, asset->id(), edit_rate, intrinsic_duration, diff --git a/src/reel_subtitle_asset.h b/src/reel_subtitle_asset.h index 8b694fd6..7bc38a0f 100644 --- a/src/reel_subtitle_asset.h +++ b/src/reel_subtitle_asset.h @@ -44,7 +44,7 @@ #include "language_tag.h" #include "reel_asset.h" #include "reel_file_asset.h" -#include "subtitle_asset.h" +#include "text_asset.h" struct verify_invalid_language1; @@ -53,7 +53,7 @@ struct verify_invalid_language1; namespace dcp { -class SubtitleAsset; +class TextAsset; /** @class ReelSubtitleAsset @@ -62,15 +62,15 @@ class SubtitleAsset; class ReelSubtitleAsset : public ReelFileAsset { public: - ReelSubtitleAsset (std::shared_ptr asset, Fraction edit_rate, int64_t intrinsic_duration, int64_t entry_point); + ReelSubtitleAsset(std::shared_ptr asset, Fraction edit_rate, int64_t intrinsic_duration, int64_t entry_point); explicit ReelSubtitleAsset (std::shared_ptr); - std::shared_ptr asset () const { - return asset_of_type(); + std::shared_ptr asset() const { + return asset_of_type(); } - std::shared_ptr asset () { - return asset_of_type(); + std::shared_ptr asset() { + return asset_of_type(); } xmlpp::Node* write_to_cpl (xmlpp::Node* node, Standard standard) const override; diff --git a/src/smpte_subtitle_asset.cc b/src/smpte_subtitle_asset.cc deleted file mode 100644 index 0ff1d7ef..00000000 --- a/src/smpte_subtitle_asset.cc +++ /dev/null @@ -1,618 +0,0 @@ -/* - Copyright (C) 2012-2021 Carl Hetherington - - This file is part of libdcp. - - libdcp 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. - - libdcp 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 libdcp. If not, see . - - In addition, as a special exception, the copyright holders give - permission to link the code of portions of this program with the - OpenSSL library under certain conditions as described in each - individual source file, and distribute linked combinations - including the two. - - You must obey the GNU General Public License in all respects - for all of the code used other than OpenSSL. If you modify - file(s) with this exception, you may extend this exception to your - version of the file(s), but you are not obligated to do so. If you - do not wish to do so, delete this exception statement from your - version. If you delete this exception statement from all source - files in the program, then also delete it here. -*/ - - -/** @file src/smpte_subtitle_asset.cc - * @brief SMPTESubtitleAsset class - */ - - -#include "compose.hpp" -#include "crypto_context.h" -#include "dcp_assert.h" -#include "equality_options.h" -#include "exceptions.h" -#include "filesystem.h" -#include "raw_convert.h" -#include "smpte_load_font_node.h" -#include "smpte_subtitle_asset.h" -#include "subtitle_image.h" -#include "util.h" -#include "warnings.h" -#include "xml.h" -LIBDCP_DISABLE_WARNINGS -#include -#include -#include -#include -LIBDCP_ENABLE_WARNINGS -#include - - -using std::string; -using std::list; -using std::vector; -using std::map; -using std::shared_ptr; -using std::dynamic_pointer_cast; -using std::make_shared; -using boost::split; -using boost::is_any_of; -using boost::shared_array; -using boost::optional; -using boost::starts_with; -using namespace dcp; - - -static string const subtitle_smpte_ns_2007 = "http://www.smpte-ra.org/schemas/428-7/2007/DCST"; -static string const subtitle_smpte_ns_2010 = "http://www.smpte-ra.org/schemas/428-7/2010/DCST"; -static string const subtitle_smpte_ns_2014 = "http://www.smpte-ra.org/schemas/428-7/2014/DCST"; - - -SMPTESubtitleAsset::SMPTESubtitleAsset(SubtitleStandard standard) - : MXF(Standard::SMPTE) - , _edit_rate (24, 1) - , _time_code_rate (24) - , _subtitle_standard(standard) - , _xml_id (make_uuid()) -{ - -} - - -SMPTESubtitleAsset::SMPTESubtitleAsset (boost::filesystem::path file) - : SubtitleAsset (file) -{ - auto xml = make_shared("SubtitleReel"); - - auto reader = make_shared(); - auto r = Kumu::RESULT_OK; - { - ASDCPErrorSuspender sus; - r = reader->OpenRead(dcp::filesystem::fix_long_path(*_file).string().c_str()); - } - if (!ASDCP_FAILURE(r)) { - /* MXF-wrapped */ - ASDCP::WriterInfo info; - reader->FillWriterInfo (info); - _id = read_writer_info (info); - if (!_key_id) { - /* Not encrypted; read it in now */ - string xml_string; - reader->ReadTimedTextResource (xml_string); - _raw_xml = xml_string; - xml->read_string (xml_string); - parse_xml (xml); - read_mxf_descriptor (reader); - read_mxf_resources(reader, std::make_shared(optional(), Standard::SMPTE)); - } else { - read_mxf_descriptor (reader); - } - } else { - /* Plain XML */ - try { - _raw_xml = dcp::file_to_string (file); - xml = make_shared("SubtitleReel"); - xml->read_file(dcp::filesystem::fix_long_path(file)); - parse_xml (xml); - } catch (cxml::Error& e) { - boost::throw_exception ( - ReadError ( - String::compose ( - "Failed to read subtitle file %1; MXF failed with %2, XML failed with %3", - file, static_cast(r), e.what() - ) - ) - ); - } - - /* Try to read PNG files from the same folder that the XML is in; the wisdom of this is - debatable, at best... - */ - for (auto i: _subtitles) { - auto im = dynamic_pointer_cast(i); - if (im && im->png_image().size() == 0) { - /* Even more dubious; allow .png or urn:uuid:.png */ - auto p = file.parent_path() / String::compose("%1.png", im->id()); - if (filesystem::is_regular_file(p)) { - im->read_png_file (p); - } else if (starts_with (im->id(), "urn:uuid:")) { - p = file.parent_path() / String::compose("%1.png", remove_urn_uuid(im->id())); - if (filesystem::is_regular_file(p)) { - im->read_png_file (p); - } - } - } - } - _standard = Standard::SMPTE; - } - - /* Check that all required image data have been found */ - for (auto i: _subtitles) { - auto im = dynamic_pointer_cast(i); - if (im && im->png_image().size() == 0) { - throw MissingSubtitleImageError (im->id()); - } - } -} - - -void -SMPTESubtitleAsset::parse_xml (shared_ptr xml) -{ - if (xml->namespace_uri() == subtitle_smpte_ns_2007) { - _subtitle_standard = SubtitleStandard::SMPTE_2007; - } else if (xml->namespace_uri() == subtitle_smpte_ns_2010) { - _subtitle_standard = SubtitleStandard::SMPTE_2010; - } else if (xml->namespace_uri() == subtitle_smpte_ns_2014) { - _subtitle_standard = SubtitleStandard::SMPTE_2014; - } else { - throw XMLError("Unrecognised subtitle namespace " + xml->namespace_uri()); - } - _xml_id = remove_urn_uuid(xml->string_child("Id")); - _load_font_nodes = type_children (xml, "LoadFont"); - - _content_title_text = xml->string_child ("ContentTitleText"); - _annotation_text = xml->optional_string_child ("AnnotationText"); - _issue_date = LocalTime (xml->string_child ("IssueDate")); - _reel_number = xml->optional_number_child ("ReelNumber"); - _language = xml->optional_string_child ("Language"); - - /* This is supposed to be two numbers, but a single number has been seen in the wild */ - auto const er = xml->string_child ("EditRate"); - vector er_parts; - split (er_parts, er, is_any_of (" ")); - if (er_parts.size() == 1) { - _edit_rate = Fraction (raw_convert (er_parts[0]), 1); - } else if (er_parts.size() == 2) { - _edit_rate = Fraction (raw_convert (er_parts[0]), raw_convert (er_parts[1])); - } else { - throw XMLError ("malformed EditRate " + er); - } - - _time_code_rate = xml->number_child ("TimeCodeRate"); - if (xml->optional_string_child ("StartTime")) { - _start_time = Time (xml->string_child("StartTime"), _time_code_rate); - } - - /* Now we need to drop down to xmlpp */ - - vector ps; - for (auto i: xml->node()->get_children()) { - auto const e = dynamic_cast(i); - if (e && e->get_name() == "SubtitleList") { - parse_subtitles (e, ps, _time_code_rate, Standard::SMPTE); - } - } - - /* Guess intrinsic duration */ - _intrinsic_duration = latest_subtitle_out().as_editable_units_ceil(_edit_rate.numerator / _edit_rate.denominator); -} - - -void -SMPTESubtitleAsset::read_mxf_resources (shared_ptr reader, shared_ptr dec) -{ - ASDCP::TimedText::TimedTextDescriptor descriptor; - reader->FillTimedTextDescriptor (descriptor); - - /* Load fonts and images */ - - for ( - auto i = descriptor.ResourceList.begin(); - i != descriptor.ResourceList.end(); - ++i) { - - ASDCP::TimedText::FrameBuffer buffer; - buffer.Capacity(32 * 1024 * 1024); - auto const result = reader->ReadAncillaryResource(i->ResourceID, buffer, dec->context(), dec->hmac()); - if (ASDCP_FAILURE(result)) { - switch (i->Type) { - case ASDCP::TimedText::MT_OPENTYPE: - throw ReadError(String::compose("Could not read font from MXF file (%1)", static_cast(result))); - case ASDCP::TimedText::MT_PNG: - throw ReadError(String::compose("Could not read subtitle image from MXF file (%1)", static_cast(result))); - default: - throw ReadError(String::compose("Could not read resource from MXF file (%1)", static_cast(result))); - } - } - - char id[64]; - Kumu::bin2UUIDhex (i->ResourceID, ASDCP::UUIDlen, id, sizeof(id)); - - switch (i->Type) { - case ASDCP::TimedText::MT_OPENTYPE: - { - auto j = _load_font_nodes.begin(); - while (j != _load_font_nodes.end() && (*j)->urn != id) { - ++j; - } - - if (j != _load_font_nodes.end ()) { - _fonts.push_back(Font((*j)->id, (*j)->urn, ArrayData(buffer.RoData(), buffer.Size()))); - } - break; - } - case ASDCP::TimedText::MT_PNG: - { - auto j = _subtitles.begin(); - while (j != _subtitles.end() && ((!dynamic_pointer_cast(*j)) || dynamic_pointer_cast(*j)->id() != id)) { - ++j; - } - - if (j != _subtitles.end()) { - dynamic_pointer_cast(*j)->set_png_image(ArrayData(buffer.RoData(), buffer.Size())); - } - break; - } - default: - break; - } - } -} - - -void -SMPTESubtitleAsset::read_mxf_descriptor (shared_ptr reader) -{ - ASDCP::TimedText::TimedTextDescriptor descriptor; - reader->FillTimedTextDescriptor (descriptor); - - _intrinsic_duration = descriptor.ContainerDuration; - /* The thing which is called AssetID in the descriptor is also known as the - * ResourceID of the MXF. We store that, at present just for verification - * purposes. - */ - char id[64]; - Kumu::bin2UUIDhex (descriptor.AssetID, ASDCP::UUIDlen, id, sizeof(id)); - _resource_id = id; -} - - -void -SMPTESubtitleAsset::set_key (Key key) -{ - /* See if we already have a key; if we do, and we have a file, we'll already - have read that file. - */ - auto const had_key = static_cast(_key); - auto const had_key_id = static_cast(_key_id); - - MXF::set_key (key); - - if (!had_key_id || !_file || had_key) { - /* Either we don't have any data to read, it wasn't - encrypted, or we've already read it, so we don't - need to do anything else. - */ - return; - } - - /* Our data was encrypted; now we can decrypt it */ - - auto reader = make_shared(); - auto r = reader->OpenRead(dcp::filesystem::fix_long_path(*_file).string().c_str()); - if (ASDCP_FAILURE (r)) { - boost::throw_exception ( - ReadError ( - String::compose ("Could not read encrypted subtitle MXF (%1)", static_cast (r)) - ) - ); - } - - auto dec = make_shared(key, Standard::SMPTE); - string xml_string; - reader->ReadTimedTextResource (xml_string, dec->context(), dec->hmac()); - _raw_xml = xml_string; - auto xml = make_shared("SubtitleReel"); - xml->read_string (xml_string); - parse_xml (xml); - read_mxf_descriptor(reader); - read_mxf_resources (reader, dec); -} - - -vector> -SMPTESubtitleAsset::load_font_nodes () const -{ - vector> lf; - copy (_load_font_nodes.begin(), _load_font_nodes.end(), back_inserter(lf)); - return lf; -} - - -bool -SMPTESubtitleAsset::valid_mxf (boost::filesystem::path file) -{ - ASDCP::TimedText::MXFReader reader; - Kumu::DefaultLogSink().UnsetFilterFlag(Kumu::LOG_ALLOW_ALL); - auto r = reader.OpenRead(dcp::filesystem::fix_long_path(file).string().c_str()); - Kumu::DefaultLogSink().SetFilterFlag(Kumu::LOG_ALLOW_ALL); - return !ASDCP_FAILURE (r); -} - - -string -SMPTESubtitleAsset::xml_as_string () const -{ - xmlpp::Document doc; - auto root = doc.create_root_node ("SubtitleReel"); - - DCP_ASSERT (_xml_id); - root->add_child("Id")->add_child_text("urn:uuid:" + *_xml_id); - root->add_child("ContentTitleText")->add_child_text(_content_title_text); - if (_annotation_text) { - root->add_child("AnnotationText")->add_child_text(_annotation_text.get()); - } - root->add_child("IssueDate")->add_child_text(_issue_date.as_string(false, false)); - if (_reel_number) { - root->add_child("ReelNumber")->add_child_text(raw_convert(_reel_number.get())); - } - if (_language) { - root->add_child("Language")->add_child_text(_language.get()); - } - root->add_child("EditRate")->add_child_text(_edit_rate.as_string()); - root->add_child("TimeCodeRate")->add_child_text(raw_convert(_time_code_rate)); - if (_start_time) { - root->add_child("StartTime")->add_child_text(_start_time.get().as_string(Standard::SMPTE)); - } - - for (auto i: _load_font_nodes) { - auto load_font = root->add_child("LoadFont"); - load_font->add_child_text ("urn:uuid:" + i->urn); - load_font->set_attribute ("ID", i->id); - } - - subtitles_as_xml (root->add_child("SubtitleList"), _time_code_rate, Standard::SMPTE); - - return format_xml(doc, std::make_pair(string{}, schema_namespace())); -} - - -void -SMPTESubtitleAsset::write (boost::filesystem::path p) const -{ - EncryptionContext enc (key(), Standard::SMPTE); - - ASDCP::WriterInfo writer_info; - fill_writer_info (&writer_info, _id); - - ASDCP::TimedText::TimedTextDescriptor descriptor; - descriptor.EditRate = ASDCP::Rational (_edit_rate.numerator, _edit_rate.denominator); - descriptor.EncodingName = "UTF-8"; - - /* Font references */ - - for (auto i: _load_font_nodes) { - auto j = _fonts.begin(); - while (j != _fonts.end() && j->load_id != i->id) { - ++j; - } - if (j != _fonts.end ()) { - ASDCP::TimedText::TimedTextResourceDescriptor res; - unsigned int c; - Kumu::hex2bin (i->urn.c_str(), res.ResourceID, Kumu::UUID_Length, &c); - DCP_ASSERT (c == Kumu::UUID_Length); - res.Type = ASDCP::TimedText::MT_OPENTYPE; - descriptor.ResourceList.push_back (res); - } - } - - /* Image subtitle references */ - - for (auto i: _subtitles) { - auto si = dynamic_pointer_cast(i); - if (si) { - ASDCP::TimedText::TimedTextResourceDescriptor res; - unsigned int c; - Kumu::hex2bin (si->id().c_str(), res.ResourceID, Kumu::UUID_Length, &c); - DCP_ASSERT (c == Kumu::UUID_Length); - res.Type = ASDCP::TimedText::MT_PNG; - descriptor.ResourceList.push_back (res); - } - } - - descriptor.NamespaceName = schema_namespace(); - unsigned int c; - DCP_ASSERT (_xml_id); - Kumu::hex2bin (_xml_id->c_str(), descriptor.AssetID, ASDCP::UUIDlen, &c); - DCP_ASSERT (c == Kumu::UUID_Length); - descriptor.ContainerDuration = _intrinsic_duration; - - ASDCP::TimedText::MXFWriter writer; - /* This header size is a guess. Empirically it seems that each subtitle reference is 90 bytes, and we need some extra. - The default size is not enough for some feature-length PNG sub projects (see DCP-o-matic #1561). - */ - ASDCP::Result_t r = writer.OpenWrite(dcp::filesystem::fix_long_path(p).string().c_str(), writer_info, descriptor, _subtitles.size() * 90 + 16384); - if (ASDCP_FAILURE (r)) { - boost::throw_exception (FileError ("could not open subtitle MXF for writing", p.string(), r)); - } - - _raw_xml = xml_as_string (); - - r = writer.WriteTimedTextResource (*_raw_xml, enc.context(), enc.hmac()); - if (ASDCP_FAILURE (r)) { - boost::throw_exception (MXFFileError ("could not write XML to timed text resource", p.string(), r)); - } - - /* Font payload */ - - for (auto i: _load_font_nodes) { - auto j = _fonts.begin(); - while (j != _fonts.end() && j->load_id != i->id) { - ++j; - } - if (j != _fonts.end ()) { - ASDCP::TimedText::FrameBuffer buffer; - ArrayData data_copy(j->data); - buffer.SetData (data_copy.data(), data_copy.size()); - buffer.Size (j->data.size()); - r = writer.WriteAncillaryResource (buffer, enc.context(), enc.hmac()); - if (ASDCP_FAILURE(r)) { - boost::throw_exception (MXFFileError ("could not write font to timed text resource", p.string(), r)); - } - } - } - - /* Image subtitle payload */ - - for (auto i: _subtitles) { - auto si = dynamic_pointer_cast(i); - if (si) { - ASDCP::TimedText::FrameBuffer buffer; - buffer.SetData (si->png_image().data(), si->png_image().size()); - buffer.Size (si->png_image().size()); - r = writer.WriteAncillaryResource (buffer, enc.context(), enc.hmac()); - if (ASDCP_FAILURE(r)) { - boost::throw_exception (MXFFileError ("could not write PNG data to timed text resource", p.string(), r)); - } - } - } - - writer.Finalize (); - - _file = p; -} - -bool -SMPTESubtitleAsset::equals(shared_ptr other_asset, EqualityOptions const& options, NoteHandler note) const -{ - if (!SubtitleAsset::equals (other_asset, options, note)) { - return false; - } - - auto other = dynamic_pointer_cast(other_asset); - if (!other) { - note (NoteType::ERROR, "Subtitles are in different standards"); - return false; - } - - auto i = _load_font_nodes.begin(); - auto j = other->_load_font_nodes.begin(); - - while (i != _load_font_nodes.end ()) { - if (j == other->_load_font_nodes.end ()) { - note (NoteType::ERROR, " nodes differ"); - return false; - } - - if ((*i)->id != (*j)->id) { - note (NoteType::ERROR, " nodes differ"); - return false; - } - - ++i; - ++j; - } - - if (_content_title_text != other->_content_title_text) { - note (NoteType::ERROR, "Subtitle content title texts differ"); - return false; - } - - if (_language != other->_language) { - note (NoteType::ERROR, String::compose("Subtitle languages differ (`%1' vs `%2')", _language.get_value_or("[none]"), other->_language.get_value_or("[none]"))); - return false; - } - - if (_annotation_text != other->_annotation_text) { - note (NoteType::ERROR, "Subtitle annotation texts differ"); - return false; - } - - if (_issue_date != other->_issue_date) { - if (options.issue_dates_can_differ) { - note (NoteType::NOTE, "Subtitle issue dates differ"); - } else { - note (NoteType::ERROR, "Subtitle issue dates differ"); - return false; - } - } - - if (_reel_number != other->_reel_number) { - note (NoteType::ERROR, "Subtitle reel numbers differ"); - return false; - } - - if (_edit_rate != other->_edit_rate) { - note (NoteType::ERROR, "Subtitle edit rates differ"); - return false; - } - - if (_time_code_rate != other->_time_code_rate) { - note (NoteType::ERROR, "Subtitle time code rates differ"); - return false; - } - - if (_start_time != other->_start_time) { - note (NoteType::ERROR, "Subtitle start times differ"); - return false; - } - - return true; -} - - -void -SMPTESubtitleAsset::add_font (string load_id, dcp::ArrayData data) -{ - string const uuid = make_uuid (); - _fonts.push_back (Font(load_id, uuid, data)); - _load_font_nodes.push_back (make_shared(load_id, uuid)); -} - - -void -SMPTESubtitleAsset::add (shared_ptr s) -{ - SubtitleAsset::add (s); - _intrinsic_duration = latest_subtitle_out().as_editable_units_ceil(_edit_rate.numerator / _edit_rate.denominator); -} - - -string -SMPTESubtitleAsset::schema_namespace() const -{ - switch (_subtitle_standard) { - case SubtitleStandard::SMPTE_2007: - return subtitle_smpte_ns_2007; - case SubtitleStandard::SMPTE_2010: - return subtitle_smpte_ns_2010; - case SubtitleStandard::SMPTE_2014: - return subtitle_smpte_ns_2014; - default: - DCP_ASSERT(false); - } - - DCP_ASSERT(false); -} diff --git a/src/smpte_subtitle_asset.h b/src/smpte_subtitle_asset.h deleted file mode 100644 index 26144fe1..00000000 --- a/src/smpte_subtitle_asset.h +++ /dev/null @@ -1,261 +0,0 @@ -/* - Copyright (C) 2012-2021 Carl Hetherington - - This file is part of libdcp. - - libdcp 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. - - libdcp 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 libdcp. If not, see . - - In addition, as a special exception, the copyright holders give - permission to link the code of portions of this program with the - OpenSSL library under certain conditions as described in each - individual source file, and distribute linked combinations - including the two. - - You must obey the GNU General Public License in all respects - for all of the code used other than OpenSSL. If you modify - file(s) with this exception, you may extend this exception to your - version of the file(s), but you are not obligated to do so. If you - do not wish to do so, delete this exception statement from your - version. If you delete this exception statement from all source - files in the program, then also delete it here. -*/ - - -#ifndef LIBDCP_SMPTE_SUBTITLE_ASSET_H -#define LIBDCP_SMPTE_SUBTITLE_ASSET_H - - -/** @file src/smpte_subtitle_asset.h - * @brief SMPTESubtitleAsset class - */ - - -#include "crypto_context.h" -#include "language_tag.h" -#include "local_time.h" -#include "mxf.h" -#include "subtitle_asset.h" -#include "subtitle_standard.h" -#include - - -namespace ASDCP { - namespace TimedText { - class MXFReader; - } -} - - -struct verify_invalid_language1; -struct verify_invalid_language2; -struct write_subtitles_in_vertical_order_with_top_alignment; -struct write_subtitles_in_vertical_order_with_bottom_alignment; - - -namespace dcp { - - -class SMPTELoadFontNode; - - -/** @class SMPTESubtitleAsset - * @brief A set of subtitles to be read and/or written in the SMPTE format - */ -class SMPTESubtitleAsset : public SubtitleAsset, public MXF -{ -public: - explicit SMPTESubtitleAsset(SubtitleStandard standard = SubtitleStandard::SMPTE_2014); - - /** Construct a SMPTESubtitleAsset by reading an MXF or XML file - * @param file Filename - */ - explicit SMPTESubtitleAsset (boost::filesystem::path file); - - bool equals ( - std::shared_ptr, - EqualityOptions const&, - NoteHandler note - ) const override; - - std::vector> load_font_nodes () const override; - - std::string xml_as_string () const override; - - /** Write this content to a MXF file */ - void write (boost::filesystem::path path) const override; - - void add (std::shared_ptr) override; - void add_font (std::string id, dcp::ArrayData data) override; - void set_key (Key key) override; - - void set_content_title_text (std::string t) { - _content_title_text = t; - } - - void set_language (dcp::LanguageTag l) { - _language = l.to_string(); - } - - void set_issue_date (LocalTime t) { - _issue_date = t; - } - - void set_reel_number (int r) { - _reel_number = r; - } - - void set_edit_rate (Fraction e) { - _edit_rate = e; - } - - void set_time_code_rate (int t) { - _time_code_rate = t; - } - - void set_start_time (Time t) { - _start_time = t; - } - - void set_intrinsic_duration (int64_t d) { - _intrinsic_duration = d; - } - - int64_t intrinsic_duration () const { - return _intrinsic_duration; - } - - /** @return title of the film that these subtitles are for, - * to be presented to the user - */ - std::string content_title_text () const { - return _content_title_text; - } - - /** @return Language, if one was set. This should be a xs:language, but - * it might not be if a non-compliant DCP was read in. - */ - boost::optional language () const { - return _language; - } - - /** @return annotation text, to be presented to the user */ - boost::optional annotation_text () const { - return _annotation_text; - } - - /** @return file issue time and date */ - LocalTime issue_date () const { - return _issue_date; - } - - boost::optional reel_number () const { - return _reel_number; - } - - Fraction edit_rate () const { - return _edit_rate; - } - - /** @return subdivision of 1 second that is used for subtitle times; - * e.g. a time_code_rate of 250 means that a subtitle time of 0:0:0:001 - * represents 4ms. - */ - int time_code_rate () const override { - return _time_code_rate; - } - - boost::optional