2 Copyright (C) 2012-2015 Carl Hetherington <cth@carlh.net>
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 /** @file src/smpte_subtitle_asset.cc
21 * @brief SMPTESubtitleAsset class.
24 #include "smpte_subtitle_asset.h"
25 #include "smpte_load_font_node.h"
26 #include "font_node.h"
27 #include "exceptions.h"
31 #include "raw_convert.h"
32 #include <libxml++/libxml++.h>
33 #include <boost/foreach.hpp>
34 #include <boost/algorithm/string.hpp>
38 using std::stringstream;
41 using boost::shared_ptr;
43 using boost::is_any_of;
46 SMPTESubtitleAsset::SMPTESubtitleAsset ()
52 /** Construct a SMPTESubtitleAsset by reading an XML or MXF file.
53 * @param file Filename.
54 * @param mxf true if file is an MXF, false if it is XML.
56 SMPTESubtitleAsset::SMPTESubtitleAsset (boost::filesystem::path file, bool mxf)
57 : SubtitleAsset (file)
59 shared_ptr<cxml::Document> xml (new cxml::Document ("SubtitleReel"));
62 ASDCP::TimedText::MXFReader reader;
63 Kumu::Result_t r = reader.OpenRead (file.string().c_str ());
64 if (ASDCP_FAILURE (r)) {
65 boost::throw_exception (MXFFileError ("could not open MXF file for reading", file, r));
69 reader.ReadTimedTextResource (s, 0, 0);
74 ASDCP::WriterInfo info;
75 reader.FillWriterInfo (info);
76 _id = read_writer_info (info);
78 xml->read_file (file);
79 _id = xml->string_child("Id").substr (9);
82 _load_font_nodes = type_children<dcp::SMPTELoadFontNode> (xml, "LoadFont");
84 _content_title_text = xml->string_child ("ContentTitleText");
85 _annotation_text = xml->optional_string_child ("AnnotationText");
86 _issue_date = LocalTime (xml->string_child ("IssueDate"));
87 _reel_number = xml->optional_number_child<int> ("ReelNumber");
88 _language = xml->optional_string_child ("Language");
90 /* This is supposed to be two numbers, but a single number has been seen in the wild */
91 string const er = xml->string_child ("EditRate");
92 vector<string> er_parts;
93 split (er_parts, er, is_any_of (" "));
94 if (er_parts.size() == 1) {
95 _edit_rate = Fraction (raw_convert<int> (er_parts[0]), 1);
96 } else if (er_parts.size() == 2) {
97 _edit_rate = Fraction (raw_convert<int> (er_parts[0]), raw_convert<int> (er_parts[1]));
99 throw XMLError ("malformed EditRate " + er);
102 _time_code_rate = xml->number_child<int> ("TimeCodeRate");
103 if (xml->optional_string_child ("StartTime")) {
104 _start_time = Time (xml->string_child ("StartTime"), _time_code_rate);
107 shared_ptr<cxml::Node> subtitle_list = xml->optional_node_child ("SubtitleList");
109 list<cxml::NodePtr> f = subtitle_list->node_children ("Font");
110 list<shared_ptr<dcp::FontNode> > font_nodes;
111 BOOST_FOREACH (cxml::NodePtr& i, f) {
112 font_nodes.push_back (shared_ptr<FontNode> (new FontNode (i, _time_code_rate)));
115 parse_subtitles (xml, font_nodes);
118 list<shared_ptr<LoadFontNode> >
119 SMPTESubtitleAsset::load_font_nodes () const
121 list<shared_ptr<LoadFontNode> > lf;
122 copy (_load_font_nodes.begin(), _load_font_nodes.end(), back_inserter (lf));
127 SMPTESubtitleAsset::valid_mxf (boost::filesystem::path file)
129 ASDCP::TimedText::MXFReader reader;
130 Kumu::Result_t r = reader.OpenRead (file.string().c_str ());
131 return !ASDCP_FAILURE (r);
135 SMPTESubtitleAsset::xml_as_string () const
138 xmlpp::Element* root = doc.create_root_node ("dcst:SubtitleReel");
139 root->set_namespace_declaration ("http://www.smpte-ra.org/schemas/428-7/2010/DCST", "dcst");
140 root->set_namespace_declaration ("http://www.w3.org/2001/XMLSchema", "xs");
142 root->add_child("ID", "dcst")->add_child_text (_id);
143 root->add_child("ContentTitleText", "dcst")->add_child_text (_content_title_text);
144 if (_annotation_text) {
145 root->add_child("AnnotationText", "dcst")->add_child_text (_annotation_text.get ());
147 root->add_child("IssueDate", "dcst")->add_child_text (_issue_date.as_string (true));
149 root->add_child("ReelNumber", "dcst")->add_child_text (raw_convert<string> (_reel_number.get ()));
152 root->add_child("Language", "dcst")->add_child_text (_language.get ());
154 root->add_child("EditRate", "dcst")->add_child_text (_edit_rate.as_string ());
155 root->add_child("TimeCodeRate", "dcst")->add_child_text (raw_convert<string> (_time_code_rate));
157 root->add_child("StartTime", "dcst")->add_child_text (_start_time.get().as_string ());
160 BOOST_FOREACH (shared_ptr<SMPTELoadFontNode> i, _load_font_nodes) {
161 xmlpp::Element* load_font = root->add_child("LoadFont", "dcst");
162 load_font->add_child_text (i->urn);
163 load_font->set_attribute ("ID", i->id);
166 subtitles_as_xml (root->add_child ("SubtitleList", "dcst"), _time_code_rate, "dcst");
168 return doc.write_to_string_formatted ("UTF-8");
171 /** Write this content to a MXF file */
173 SMPTESubtitleAsset::write (boost::filesystem::path p) const
175 ASDCP::WriterInfo writer_info;
176 fill_writer_info (&writer_info, _id, SMPTE);
178 ASDCP::TimedText::TimedTextDescriptor descriptor;
179 descriptor.EditRate = ASDCP::Rational (_edit_rate.numerator, _edit_rate.denominator);
180 descriptor.EncodingName = "UTF-8";
181 descriptor.ResourceList.clear ();
182 descriptor.NamespaceName = "dcst";
183 memcpy (descriptor.AssetID, writer_info.AssetUUID, ASDCP::UUIDlen);
184 descriptor.ContainerDuration = latest_subtitle_out().as_editable_units (_edit_rate.numerator / _edit_rate.denominator);
186 /* XXX: should write fonts into the file somehow */
188 ASDCP::TimedText::MXFWriter writer;
189 ASDCP::Result_t r = writer.OpenWrite (p.string().c_str(), writer_info, descriptor);
190 if (ASDCP_FAILURE (r)) {
191 boost::throw_exception (FileError ("could not open subtitle MXF for writing", p.string(), r));
194 /* XXX: no encryption */
195 r = writer.WriteTimedTextResource (xml_as_string ());
196 if (ASDCP_FAILURE (r)) {
197 boost::throw_exception (MXFFileError ("could not write XML to timed text resource", p.string(), r));
206 SMPTESubtitleAsset::equals (shared_ptr<const Asset> other_asset, EqualityOptions options, NoteHandler note) const