+ _start_time = Time (xml->string_child("StartTime"), _time_code_rate);
+ }
+
+ /* Now we need to drop down to xmlpp */
+
+ vector<ParseState> ps;
+ for (auto i: xml->node()->get_children()) {
+ auto const e = dynamic_cast<xmlpp::Element const *>(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<ASDCP::TimedText::MXFReader> reader, shared_ptr<DecryptionContext> 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<int>(result)));
+ case ASDCP::TimedText::MT_PNG:
+ throw ReadError(String::compose("Could not read subtitle image from MXF file (%1)", static_cast<int>(result)));
+ default:
+ throw ReadError(String::compose("Could not read resource from MXF file (%1)", static_cast<int>(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<SubtitleImage>(*j)) || dynamic_pointer_cast<SubtitleImage>(*j)->id() != id)) {
+ ++j;
+ }
+
+ if (j != _subtitles.end()) {
+ dynamic_pointer_cast<SubtitleImage>(*j)->set_png_image(ArrayData(buffer.RoData(), buffer.Size()));
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ }
+}
+
+
+void
+SMPTESubtitleAsset::read_mxf_descriptor (shared_ptr<ASDCP::TimedText::MXFReader> 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<bool>(_key);
+ auto const had_key_id = static_cast<bool>(_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;