Hacks to support MXF-wrapped subtitles.
authorCarl Hetherington <cth@carlh.net>
Wed, 26 Feb 2014 17:48:29 +0000 (17:48 +0000)
committerCarl Hetherington <cth@carlh.net>
Wed, 26 Feb 2014 17:48:29 +0000 (17:48 +0000)
src/parse/subtitle.cc
src/parse/subtitle.h
src/subtitle_asset.cc
src/subtitle_asset.h

index 612af716b2f47b1b418cdffeda652fe66c713047..914be677050f5981d88e3b2c0277171e24be116f 100644 (file)
@@ -84,10 +84,14 @@ Font::Font (list<shared_ptr<Font> > const & font_nodes)
 
 LoadFont::LoadFont (shared_ptr<const cxml::Node> node)
 {
-       id = node->string_attribute ("Id");
-       uri = node->string_attribute ("URI");
+       optional<string> x = node->optional_string_attribute ("Id");
+       if (!x) {
+               x = node->optional_string_attribute ("ID");
+       }
+       id = x.get_value_or ("");
+               
+       uri = node->optional_string_attribute ("URI");
 }
-       
 
 Subtitle::Subtitle (shared_ptr<const cxml::Node> node)
 {
@@ -123,9 +127,19 @@ Subtitle::fade_time (shared_ptr<const cxml::Node> node, string name)
 Text::Text (shared_ptr<const cxml::Node> node)
        : v_align (CENTER)
 {
+       /* Vertical position */
        text = node->content ();
-       v_position = node->number_attribute<float> ("VPosition");
+       optional<float> x = node->optional_number_attribute<float> ("VPosition");
+       if (!x) {
+               x = node->number_attribute<float> ("Vposition");
+       }
+       v_position = x.get ();
+
+       /* Vertical alignment */
        optional<string> v = node->optional_string_attribute ("VAlign");
+       if (!v) {
+               v = node->optional_string_attribute ("Valign");
+       }
        if (v) {
                v_align = string_to_valign (v.get ());
        }
index c20278a3c689877027479c7c221d4be8535d8ea1..3d99d9bc929ec7a9b9bd4948d8da7b17e739a384 100644 (file)
@@ -92,7 +92,7 @@ public:
        LoadFont (boost::shared_ptr<const cxml::Node> node);
 
        std::string id;
-       std::string uri;
+       boost::optional<std::string> uri;
 };
 
 }
index a48a614d12dd529da3eb62fce7c572ab52bc5f10..4eb1a9cdf54fe95257d5784f22cf01bf8474a8ba 100644 (file)
 */
 
 #include <fstream>
+#include <cerrno>
 #include <boost/lexical_cast.hpp>
 #include <boost/algorithm/string.hpp>
 #include <libxml++/nodes/element.h>
+#include "AS_DCP.h"
+#include "KM_util.h"
 #include "subtitle_asset.h"
 #include "parse/subtitle.h"
 #include "util.h"
@@ -31,16 +34,34 @@ using std::list;
 using std::ostream;
 using std::ofstream;
 using std::stringstream;
+using std::cout;
 using boost::shared_ptr;
 using boost::lexical_cast;
 using boost::optional;
 using namespace libdcp;
 
-SubtitleAsset::SubtitleAsset (string directory, string xml_file)
-       : Asset (directory, xml_file)
+SubtitleAsset::SubtitleAsset (string directory, string file)
+       : Asset (directory, file)
        , _need_sort (false)
 {
-       read_xml (path().string());
+       /* Grotesque hack: we should look in the PKL to see what type this file is;
+          instead we'll look at the first character to decide what to do.
+          I think this is easily fixable (properly) in 1.0.
+       */
+
+       FILE* f = fopen_boost (path(), "r");
+       if (!f) {
+               throw FileError ("Could not open file for reading", file, errno);
+       }
+       unsigned char test[1];
+       fread (test, 1, 1, f);
+       fclose (f);
+
+       if (test[0] == '<' || test[0] == 0xef) {
+               read_xml (path().string());
+       } else {
+               read_mxf (path().string());
+       }
 }
 
 SubtitleAsset::SubtitleAsset (string directory, string movie_title, string language)
@@ -53,14 +74,46 @@ SubtitleAsset::SubtitleAsset (string directory, string movie_title, string langu
 
 }
 
+void
+SubtitleAsset::read_mxf (string mxf_file)
+{
+       ASDCP::TimedText::MXFReader reader;
+       Kumu::Result_t r = reader.OpenRead (mxf_file.c_str ());
+       if (ASDCP_FAILURE (r)) {
+               boost::throw_exception (MXFFileError ("could not open MXF file for reading", mxf_file, r));
+       }
+
+       string s;
+       reader.ReadTimedTextResource (s, 0, 0);
+       shared_ptr<cxml::Document> xml (new cxml::Document ("SubtitleReel"));
+       stringstream t;
+       t << s;
+       xml->read_stream (t);
+       read_xml (xml);
+}
+
 void
 SubtitleAsset::read_xml (string xml_file)
 {
        shared_ptr<cxml::Document> xml (new cxml::Document ("DCSubtitle"));
        xml->read_file (xml_file);
+       read_xml (xml);
+}
+
+void
+SubtitleAsset::read_xml (shared_ptr<cxml::Document> xml)
+{
+       /* XXX: hacks aplenty in here; need separate parsers for DCSubtitle and SubtitleReel */
        
-       _uuid = xml->string_child ("SubtitleID");
-       _movie_title = xml->string_child ("MovieTitle");
+       /* DCSubtitle */
+       optional<string> x = xml->optional_string_child ("SubtitleID");
+       if (!x) {
+               /* SubtitleReel */
+               x = xml->optional_string_child ("Id");
+       }
+       _uuid = x.get_value_or ("");
+
+       _movie_title = xml->optional_string_child ("MovieTitle");
        _reel_number = xml->string_child ("ReelNumber");
        _language = xml->string_child ("Language");
 
@@ -73,6 +126,12 @@ SubtitleAsset::read_xml (string xml_file)
           in a sane way.
        */
 
+       shared_ptr<cxml::Node> subtitle_list = xml->optional_node_child ("SubtitleList");
+       if (subtitle_list) {
+               list<shared_ptr<libdcp::parse::Font> > font = type_children<libdcp::parse::Font> (subtitle_list, "Font");
+               copy (font.begin(), font.end(), back_inserter (font_nodes));
+       }
+       
        ParseState parse_state;
        examine_font_nodes (xml, font_nodes, parse_state);
 }
@@ -182,7 +241,7 @@ SubtitleAsset::font_id_to_name (string id) const
                return "";
        }
 
-       if ((*i)->uri == "arial.ttf") {
+       if ((*i)->uri && (*i)->uri.get() == "arial.ttf") {
                return "Arial";
        }
 
@@ -316,7 +375,9 @@ SubtitleAsset::xml_as_string () const
        root->set_attribute ("Version", "1.0");
 
        root->add_child("SubtitleID")->add_child_text (_uuid);
-       root->add_child("MovieTitle")->add_child_text (_movie_title);
+       if (_movie_title) {
+               root->add_child("MovieTitle")->add_child_text (_movie_title.get ());
+       }
        root->add_child("ReelNumber")->add_child_text (lexical_cast<string> (_reel_number));
        root->add_child("Language")->add_child_text (_language);
 
@@ -327,7 +388,9 @@ SubtitleAsset::xml_as_string () const
        if (!_load_font_nodes.empty ()) {
                xmlpp::Element* load_font = root->add_child("LoadFont");
                load_font->set_attribute("Id", _load_font_nodes.front()->id);
-               load_font->set_attribute("URI",  _load_font_nodes.front()->uri);
+               if (_load_font_nodes.front()->uri) {
+                       load_font->set_attribute("URI",  _load_font_nodes.front()->uri.get ());
+               }
        }
 
        list<shared_ptr<Subtitle> > sorted = _subtitles;
index 0598a29667006b9c518d58f351c9cd2acd975f53..a7ec641c5ba023af44b800ce8ddbcd7d3b4804d2 100644 (file)
@@ -169,6 +169,8 @@ protected:
 
 private:
        std::string font_id_to_name (std::string id) const;
+       void read_mxf (std::string);
+       void read_xml (boost::shared_ptr<cxml::Document>);
 
        struct ParseState {
                std::list<boost::shared_ptr<parse::Font> > font_nodes;
@@ -190,7 +192,7 @@ private:
                ParseState& parse_state
                );
 
-       std::string _movie_title;
+       boost::optional<std::string> _movie_title;
        /* strangely, this is sometimes a string */
        std::string _reel_number;
        std::string _language;