X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;ds=sidebyside;f=src%2Fsmpte_subtitle_asset.cc;h=8408a57e40217f8ecd4bacbb72ddac9855b782db;hb=f380b960018e5b81b6a10834822fa962bfab2c7b;hp=3b30a0c71a5be7edd1dff883c3cbfe48cb5f1976;hpb=732f931e0559f29100b56f7f4a6e6be8820f0ff7;p=libdcp.git diff --git a/src/smpte_subtitle_asset.cc b/src/smpte_subtitle_asset.cc index 3b30a0c7..8408a57e 100644 --- a/src/smpte_subtitle_asset.cc +++ b/src/smpte_subtitle_asset.cc @@ -1,5 +1,5 @@ /* - Copyright (C) 2012-2016 Carl Hetherington + Copyright (C) 2012-2018 Carl Hetherington This file is part of libdcp. @@ -43,10 +43,11 @@ #include "dcp_assert.h" #include "util.h" #include "compose.hpp" -#include "encryption_context.h" -#include "decryption_context.h" +#include "crypto_context.h" +#include "subtitle_image.h" #include #include +#include #include #include #include @@ -60,12 +61,15 @@ using boost::split; using boost::is_any_of; using boost::shared_array; using boost::dynamic_pointer_cast; +using boost::optional; using namespace dcp; SMPTESubtitleAsset::SMPTESubtitleAsset () - : _intrinsic_duration (0) + : MXF (SMPTE) + , _intrinsic_duration (0) , _edit_rate (24, 1) , _time_code_rate (24) + , _xml_id (make_uuid ()) { } @@ -91,7 +95,7 @@ SMPTESubtitleAsset::SMPTESubtitleAsset (boost::filesystem::path file) reader->ReadTimedTextResource (s); xml->read_string (s); parse_xml (xml); - read_mxf_descriptor (reader, shared_ptr (new DecryptionContext ())); + read_mxf_descriptor (reader, shared_ptr (new DecryptionContext (optional(), SMPTE))); } } else { /* Plain XML */ @@ -99,7 +103,7 @@ SMPTESubtitleAsset::SMPTESubtitleAsset (boost::filesystem::path file) xml.reset (new cxml::Document ("SubtitleReel")); xml->read_file (file); parse_xml (xml); - _id = remove_urn_uuid (xml->string_child ("Id")); + _id = _xml_id = remove_urn_uuid (xml->string_child ("Id")); } catch (cxml::Error& e) { boost::throw_exception ( DCPReadError ( @@ -116,6 +120,7 @@ SMPTESubtitleAsset::SMPTESubtitleAsset (boost::filesystem::path file) 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"); @@ -169,17 +174,19 @@ SMPTESubtitleAsset::read_mxf_descriptor (shared_ptr 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, dec->decryption()); + ASDCP::TimedText::FrameBuffer buffer; + buffer.Capacity (10 * 1024 * 1024); + reader->ReadAncillaryResource (i->ResourceID, buffer, dec->context(), dec->hmac()); - 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()); + switch (i->Type) { + case ASDCP::TimedText::MT_OPENTYPE: + { list >::const_iterator j = _load_font_nodes.begin (); while (j != _load_font_nodes.end() && (*j)->urn != id) { ++j; @@ -188,6 +195,22 @@ SMPTESubtitleAsset::read_mxf_descriptor (shared_ptr if (j != _load_font_nodes.end ()) { _fonts.push_back (Font ((*j)->id, (*j)->urn, Data (data, buffer.Size ()))); } + break; + } + case ASDCP::TimedText::MT_PNG: + { + list >::const_iterator 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 (Data(data, buffer.Size())); + } + break; + } + default: + break; } } @@ -198,11 +221,17 @@ SMPTESubtitleAsset::read_mxf_descriptor (shared_ptr 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) { - /* Either we don't have any data to read, or it wasn't - encrypted, so we don't need to do anything else. + 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; } @@ -220,8 +249,8 @@ SMPTESubtitleAsset::set_key (Key key) } string s; - shared_ptr dec (new DecryptionContext (key)); - reader->ReadTimedTextResource (s, dec->decryption()); + shared_ptr dec (new DecryptionContext (key, SMPTE)); + reader->ReadTimedTextResource (s, dec->context(), dec->hmac()); shared_ptr xml (new cxml::Document ("SubtitleReel")); xml->read_string (s); parse_xml (xml); @@ -240,7 +269,9 @@ bool SMPTESubtitleAsset::valid_mxf (boost::filesystem::path file) { ASDCP::TimedText::MXFReader reader; + Kumu::DefaultLogSink().UnsetFilterFlag(Kumu::LOG_ALLOW_ALL); Kumu::Result_t r = reader.OpenRead (file.string().c_str ()); + Kumu::DefaultLogSink().SetFilterFlag(Kumu::LOG_ALLOW_ALL); return !ASDCP_FAILURE (r); } @@ -252,7 +283,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 ()); @@ -278,22 +309,24 @@ 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); + EncryptionContext enc (key(), SMPTE); ASDCP::WriterInfo writer_info; - fill_writer_info (&writer_info, _id, SMPTE); + 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 */ + BOOST_FOREACH (shared_ptr i, _load_font_nodes) { list::const_iterator j = _fonts.begin (); while (j != _fonts.end() && j->load_id != i->id) { @@ -309,6 +342,20 @@ SMPTESubtitleAsset::write (boost::filesystem::path p) const } } + /* Image subtitle references */ + + BOOST_FOREACH (shared_ptr i, _subtitles) { + shared_ptr 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 = "dcst"; memcpy (descriptor.AssetID, writer_info.AssetUUID, ASDCP::UUIDlen); descriptor.ContainerDuration = _intrinsic_duration; @@ -319,12 +366,13 @@ SMPTESubtitleAsset::write (boost::filesystem::path p) const boost::throw_exception (FileError ("could not open subtitle MXF for writing", p.string(), r)); } - /* XXX: no encryption */ - r = writer.WriteTimedTextResource (xml_as_string (), enc.encryption(), enc.hmac()); + r = writer.WriteTimedTextResource (xml_as_string (), 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 */ + BOOST_FOREACH (shared_ptr i, _load_font_nodes) { list::const_iterator j = _fonts.begin (); while (j != _fonts.end() && j->load_id != i->id) { @@ -334,13 +382,28 @@ 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, enc.encryption(), enc.hmac()); + 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 */ + + BOOST_FOREACH (shared_ptr i, _subtitles) { + shared_ptr si = dynamic_pointer_cast(i); + if (si) { + ASDCP::TimedText::FrameBuffer buffer; + buffer.SetData (si->png_image().data().get(), 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; @@ -433,7 +496,7 @@ SMPTESubtitleAsset::add_font (string load_id, boost::filesystem::path file) } void -SMPTESubtitleAsset::add (dcp::SubtitleString s) +SMPTESubtitleAsset::add (shared_ptr s) { SubtitleAsset::add (s); _intrinsic_duration = latest_subtitle_out().as_editable_units (_edit_rate.numerator / _edit_rate.denominator);