X-Git-Url: https://git.carlh.net/gitweb/?a=blobdiff_plain;f=src%2Fsmpte_subtitle_asset.cc;h=c85afc2e268cd2ba8588b6b406047f095b67a053;hb=d4b350cc524543b8142ac803232b31af751885e2;hp=0c0181f42d5c1604252d55d37b4e9b40129fc71b;hpb=e4d5298e7a179d4103581cba05cbc516f94acf60;p=libdcp.git diff --git a/src/smpte_subtitle_asset.cc b/src/smpte_subtitle_asset.cc index 0c0181f4..c85afc2e 100644 --- a/src/smpte_subtitle_asset.cc +++ b/src/smpte_subtitle_asset.cc @@ -1,5 +1,5 @@ /* - Copyright (C) 2012-2015 Carl Hetherington + Copyright (C) 2012-2016 Carl Hetherington This file is part of libdcp. @@ -37,13 +37,14 @@ #include "smpte_subtitle_asset.h" #include "smpte_load_font_node.h" -#include "font_node.h" #include "exceptions.h" #include "xml.h" #include "raw_convert.h" #include "dcp_assert.h" #include "util.h" #include "compose.hpp" +#include "encryption_context.h" +#include "decryption_context.h" #include #include #include @@ -65,6 +66,7 @@ SMPTESubtitleAsset::SMPTESubtitleAsset () : _intrinsic_duration (0) , _edit_rate (24, 1) , _time_code_rate (24) + , _xml_id (make_uuid ()) { } @@ -78,29 +80,44 @@ SMPTESubtitleAsset::SMPTESubtitleAsset (boost::filesystem::path file) shared_ptr xml (new cxml::Document ("SubtitleReel")); shared_ptr reader (new ASDCP::TimedText::MXFReader ()); - Kumu::Result_t r = reader->OpenRead (file.string().c_str ()); - + Kumu::Result_t r = reader->OpenRead (_file->string().c_str ()); if (!ASDCP_FAILURE (r)) { - string s; - reader->ReadTimedTextResource (s, 0, 0); - xml->read_string (s); + /* MXF-wrapped */ ASDCP::WriterInfo info; reader->FillWriterInfo (info); _id = read_writer_info (info); + if (!_key_id) { + /* Not encrypted; read it in now */ + string s; + reader->ReadTimedTextResource (s); + xml->read_string (s); + parse_xml (xml); + read_mxf_descriptor (reader, shared_ptr (new DecryptionContext ())); + } } else { - reader.reset (); + /* Plain XML */ try { + xml.reset (new cxml::Document ("SubtitleReel")); xml->read_file (file); - _id = remove_urn_uuid (xml->string_child ("Id")); + parse_xml (xml); + _id = _xml_id = remove_urn_uuid (xml->string_child ("Id")); } catch (cxml::Error& e) { boost::throw_exception ( DCPReadError ( - String::compose ("MXF failed with %1, XML failed with %2", file, static_cast (r), e.what ()) + String::compose ( + "Failed to read subtitle file %1; MXF failed with %2, XML failed with %3", + file, static_cast (r), e.what () + ) ) ); } } +} +void +SMPTESubtitleAsset::parse_xml (shared_ptr xml) +{ + _xml_id = remove_urn_uuid(xml->string_child("Id")); _load_font_nodes = type_children (xml, "LoadFont"); _content_title_text = xml->string_child ("ContentTitleText"); @@ -126,59 +143,97 @@ SMPTESubtitleAsset::SMPTESubtitleAsset (boost::filesystem::path file) _start_time = Time (xml->string_child ("StartTime"), _time_code_rate); } - shared_ptr subtitle_list = xml->optional_node_child ("SubtitleList"); - - list > font_nodes; - BOOST_FOREACH (cxml::NodePtr const & i, subtitle_list->node_children ("Font")) { - font_nodes.push_back (shared_ptr (new FontNode (i, _time_code_rate, SMPTE))); - } + /* Now we need to drop down to xmlpp */ - list > subtitle_nodes; - BOOST_FOREACH (cxml::NodePtr const & i, subtitle_list->node_children ("Subtitle")) { - subtitle_nodes.push_back (shared_ptr (new SubtitleNode (i, _time_code_rate, SMPTE))); + list ps; + xmlpp::Node::NodeList c = xml->node()->get_children (); + for (xmlpp::Node::NodeList::const_iterator i = c.begin(); i != c.end(); ++i) { + xmlpp::Element const * e = dynamic_cast (*i); + if (e && e->get_name() == "SubtitleList") { + parse_subtitles (e, ps, _time_code_rate, SMPTE); + } } - parse_subtitles (xml, font_nodes, subtitle_nodes); + /* Guess intrinsic duration */ + _intrinsic_duration = latest_subtitle_out().as_editable_units (_edit_rate.numerator / _edit_rate.denominator); +} - if (reader) { - ASDCP::TimedText::TimedTextDescriptor descriptor; - reader->FillTimedTextDescriptor (descriptor); +void +SMPTESubtitleAsset::read_mxf_descriptor (shared_ptr reader, shared_ptr dec) +{ + ASDCP::TimedText::TimedTextDescriptor descriptor; + reader->FillTimedTextDescriptor (descriptor); - /* Load fonts */ + /* Load fonts */ - for ( - ASDCP::TimedText::ResourceList_t::const_iterator i = descriptor.ResourceList.begin(); - i != descriptor.ResourceList.end(); - ++i) { + for ( + ASDCP::TimedText::ResourceList_t::const_iterator i = descriptor.ResourceList.begin(); + i != descriptor.ResourceList.end(); + ++i) { - if (i->Type == ASDCP::TimedText::MT_OPENTYPE) { - ASDCP::TimedText::FrameBuffer buffer; - buffer.Capacity (10 * 1024 * 1024); - reader->ReadAncillaryResource (i->ResourceID, buffer); + if (i->Type == ASDCP::TimedText::MT_OPENTYPE) { + ASDCP::TimedText::FrameBuffer buffer; + buffer.Capacity (10 * 1024 * 1024); + reader->ReadAncillaryResource (i->ResourceID, buffer, dec->decryption()); - char id[64]; - Kumu::bin2UUIDhex (i->ResourceID, ASDCP::UUIDlen, id, sizeof (id)); + char id[64]; + Kumu::bin2UUIDhex (i->ResourceID, ASDCP::UUIDlen, id, sizeof (id)); - shared_array data (new uint8_t[buffer.Size()]); - memcpy (data.get(), buffer.RoData(), buffer.Size()); + shared_array data (new uint8_t[buffer.Size()]); + memcpy (data.get(), buffer.RoData(), buffer.Size()); - list >::const_iterator j = _load_font_nodes.begin (); - while (j != _load_font_nodes.end() && (*j)->urn != id) { - ++j; - } + list >::const_iterator 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, Data (data, buffer.Size ()))); - } + if (j != _load_font_nodes.end ()) { + _fonts.push_back (Font ((*j)->id, (*j)->urn, Data (data, buffer.Size ()))); } } + } - /* Get intrinsic duration */ - _intrinsic_duration = descriptor.ContainerDuration; - } else { - /* Guess intrinsic duration */ - _intrinsic_duration = latest_subtitle_out().as_editable_units (_edit_rate.numerator / _edit_rate.denominator); + /* Get intrinsic duration */ + _intrinsic_duration = descriptor.ContainerDuration; +} + +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. + */ + bool const had_key = static_cast (_key); + + MXF::set_key (key); + + if (!_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 */ + + shared_ptr reader (new ASDCP::TimedText::MXFReader ()); + Kumu::Result_t r = reader->OpenRead (_file->string().c_str ()); + if (ASDCP_FAILURE (r)) { + boost::throw_exception ( + DCPReadError ( + String::compose ("Could not read encrypted subtitle MXF (%1)", static_cast (r)) + ) + ); } + + string s; + shared_ptr dec (new DecryptionContext (key)); + reader->ReadTimedTextResource (s, dec->decryption()); + shared_ptr xml (new cxml::Document ("SubtitleReel")); + xml->read_string (s); + parse_xml (xml); + read_mxf_descriptor (reader, dec); } list > @@ -205,7 +260,7 @@ SMPTESubtitleAsset::xml_as_string () const root->set_namespace_declaration ("http://www.smpte-ra.org/schemas/428-7/2010/DCST", "dcst"); root->set_namespace_declaration ("http://www.w3.org/2001/XMLSchema", "xs"); - root->add_child("Id", "dcst")->add_child_text ("urn:uuid:" + _id); + root->add_child("Id", "dcst")->add_child_text ("urn:uuid:" + _xml_id); root->add_child("ContentTitleText", "dcst")->add_child_text (_content_title_text); if (_annotation_text) { root->add_child("AnnotationText", "dcst")->add_child_text (_annotation_text.get ()); @@ -231,13 +286,15 @@ SMPTESubtitleAsset::xml_as_string () const subtitles_as_xml (root->add_child ("SubtitleList", "dcst"), _time_code_rate, SMPTE); - return doc.write_to_string_formatted ("UTF-8"); + return doc.write_to_string ("UTF-8"); } /** Write this content to a MXF file */ void SMPTESubtitleAsset::write (boost::filesystem::path p) const { + EncryptionContext enc (key (), SMPTE); + ASDCP::WriterInfo writer_info; fill_writer_info (&writer_info, _id, SMPTE); @@ -271,7 +328,7 @@ SMPTESubtitleAsset::write (boost::filesystem::path p) const } /* XXX: no encryption */ - r = writer.WriteTimedTextResource (xml_as_string ()); + r = writer.WriteTimedTextResource (xml_as_string (), enc.encryption(), enc.hmac()); if (ASDCP_FAILURE (r)) { boost::throw_exception (MXFFileError ("could not write XML to timed text resource", p.string(), r)); } @@ -285,7 +342,7 @@ SMPTESubtitleAsset::write (boost::filesystem::path p) const ASDCP::TimedText::FrameBuffer buffer; buffer.SetData (j->data.data().get(), j->data.size()); buffer.Size (j->data.size()); - r = writer.WriteAncillaryResource (buffer); + r = writer.WriteAncillaryResource (buffer, enc.encryption(), enc.hmac()); if (ASDCP_FAILURE (r)) { boost::throw_exception (MXFFileError ("could not write font to timed text resource", p.string(), r)); }