/*
- Copyright (C) 2012-2018 Carl Hetherington <cth@carlh.net>
+ Copyright (C) 2012-2019 Carl Hetherington <cth@carlh.net>
This file is part of libdcp.
#include "subtitle_image.h"
#include <asdcp/AS_DCP.h>
#include <asdcp/KM_util.h>
+#include <asdcp/KM_log.h>
#include <libxml++/libxml++.h>
#include <boost/foreach.hpp>
#include <boost/algorithm/string.hpp>
using boost::shared_array;
using boost::dynamic_pointer_cast;
using boost::optional;
+using boost::starts_with;
using namespace dcp;
+static string const subtitle_smpte_ns = "http://www.smpte-ra.org/schemas/428-7/2010/DCST";
+
SMPTESubtitleAsset::SMPTESubtitleAsset ()
: MXF (SMPTE)
, _intrinsic_duration (0)
)
);
}
+
+ /* Try to read PNG files from the same folder that the XML is in; the wisdom of this is
+ debatable, at best...
+ */
+ BOOST_FOREACH (shared_ptr<Subtitle> i, _subtitles) {
+ shared_ptr<SubtitleImage> im = dynamic_pointer_cast<SubtitleImage>(i);
+ if (im && im->png_image().size() == 0) {
+ /* Even more dubious; allow <id>.png or urn:uuid:<id>.png */
+ boost::filesystem::path p = file.parent_path() / String::compose("%1.png", im->id());
+ if (boost::filesystem::is_regular_file(p)) {
+ im->read_png_file (p);
+ } else if (starts_with (im->id(), "urn:uuid:")) {
+ p = file.parent_path() / String::compose("%1.png", remove_urn_uuid(im->id()));
+ if (boost::filesystem::is_regular_file(p)) {
+ im->read_png_file (p);
+ }
+ }
+ }
+ }
+ }
+
+ /* Check that all required image data have been found */
+ BOOST_FOREACH (shared_ptr<Subtitle> i, _subtitles) {
+ shared_ptr<SubtitleImage> im = dynamic_pointer_cast<SubtitleImage>(i);
+ if (im && im->png_image().size() == 0) {
+ throw MissingSubtitleImageError (im->id());
+ }
}
}
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->context(), dec->hmac());
+ 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<uint8_t> data (new uint8_t[buffer.Size()]);
- memcpy (data.get(), buffer.RoData(), buffer.Size());
+ shared_array<uint8_t> data (new uint8_t[buffer.Size()]);
+ memcpy (data.get(), buffer.RoData(), buffer.Size());
+ switch (i->Type) {
+ case ASDCP::TimedText::MT_OPENTYPE:
+ {
list<shared_ptr<SMPTELoadFontNode> >::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 ())));
}
+ break;
+ }
+ case ASDCP::TimedText::MT_PNG:
+ {
+ list<shared_ptr<Subtitle> >::const_iterator 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 (Data(data, buffer.Size()));
+ }
+ break;
+ }
+ default:
+ break;
}
}
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);
}
{
xmlpp::Document doc;
xmlpp::Element* root = doc.create_root_node ("dcst:SubtitleReel");
- root->set_namespace_declaration ("http://www.smpte-ra.org/schemas/428-7/2010/DCST", "dcst");
+ root->set_namespace_declaration (subtitle_smpte_ns, "dcst");
root->set_namespace_declaration ("http://www.w3.org/2001/XMLSchema", "xs");
root->add_child("Id", "dcst")->add_child_text ("urn:uuid:" + _xml_id);
/* Image subtitle references */
- for (ImageUUIDMap::const_iterator i = _image_subtitle_uuid.begin(); i != _image_subtitle_uuid.end(); ++i) {
- ASDCP::TimedText::TimedTextResourceDescriptor res;
- unsigned int c;
- Kumu::hex2bin (i->second.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);
+ BOOST_FOREACH (shared_ptr<Subtitle> i, _subtitles) {
+ shared_ptr<SubtitleImage> si = dynamic_pointer_cast<SubtitleImage>(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.NamespaceName = subtitle_smpte_ns;
+ unsigned int c;
+ Kumu::hex2bin (_xml_id.c_str(), descriptor.AssetID, ASDCP::UUIDlen, &c);
+ DCP_ASSERT (c == Kumu::UUID_Length);
descriptor.ContainerDuration = _intrinsic_duration;
ASDCP::TimedText::MXFWriter writer;
- ASDCP::Result_t r = writer.OpenWrite (p.string().c_str(), writer_info, descriptor);
+ /* This header size is a guess. Empirically it seems that each subtitle reference is 90 bytes, and we need some extra.
+ The defualt size is not enough for some feature-length PNG sub projects (see DCP-o-matic #1561).
+ */
+ ASDCP::Result_t r = writer.OpenWrite (p.string().c_str(), writer_info, descriptor, _subtitles.size() * 90 + 16384);
if (ASDCP_FAILURE (r)) {
boost::throw_exception (FileError ("could not open subtitle MXF for writing", p.string(), r));
}
/* Image subtitle payload */
- for (ImageUUIDMap::const_iterator i = _image_subtitle_uuid.begin(); i != _image_subtitle_uuid.end(); ++i) {
- ASDCP::TimedText::FrameBuffer buffer;
- buffer.SetData (i->first->png_image().data().get(), i->first->png_image().size());
- buffer.Size (i->first->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));
+ BOOST_FOREACH (shared_ptr<Subtitle> i, _subtitles) {
+ shared_ptr<SubtitleImage> si = dynamic_pointer_cast<SubtitleImage>(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));
+ }
}
}