Hopefully-correct PKL and AssetMap when using Interop PNG subtitles.
authorCarl Hetherington <cth@carlh.net>
Sun, 2 Sep 2018 22:39:01 +0000 (23:39 +0100)
committerCarl Hetherington <cth@carlh.net>
Sun, 2 Sep 2018 22:39:01 +0000 (23:39 +0100)
14 files changed:
src/asset.cc
src/asset.h
src/dcp.cc
src/interop_subtitle_asset.cc
src/interop_subtitle_asset.h
src/subtitle_image.cc
src/subtitle_image.h
src/util.cc
src/util.h
test/data/write_interop_subtitle_test3.xml [deleted file]
test/ref/write_interop_subtitle_test3/ASSETMAP [new file with mode: 0644]
test/ref/write_interop_subtitle_test3/pkl.xml [new file with mode: 0644]
test/ref/write_interop_subtitle_test3/subs.xml [new file with mode: 0644]
test/write_subtitle_test.cc

index 24fdfe6c317015792449d4679a4dd80dea512f7a..5b64b6ccfe73ffe72bd1049a1c3e227d6d19ddff 100644 (file)
@@ -101,10 +101,15 @@ void
 Asset::write_to_assetmap (xmlpp::Node* node, boost::filesystem::path root) const
 {
        DCP_ASSERT (_file);
+       write_file_to_assetmap (node, root, _file.get(), _id);
+}
 
+void
+Asset::write_file_to_assetmap (xmlpp::Node* node, boost::filesystem::path root, boost::filesystem::path file, string id)
+{
        optional<boost::filesystem::path> path = relative_to_root (
                boost::filesystem::canonical (root),
-               boost::filesystem::canonical (_file.get())
+               boost::filesystem::canonical (file)
                );
 
        if (!path) {
@@ -115,14 +120,14 @@ Asset::write_to_assetmap (xmlpp::Node* node, boost::filesystem::path root) const
        }
 
        xmlpp::Node* asset = node->add_child ("Asset");
-       asset->add_child("Id")->add_child_text ("urn:uuid:" + _id);
+       asset->add_child("Id")->add_child_text ("urn:uuid:" + id);
        xmlpp::Node* chunk_list = asset->add_child ("ChunkList");
        xmlpp::Node* chunk = chunk_list->add_child ("Chunk");
 
        chunk->add_child("Path")->add_child_text (path.get().generic_string());
        chunk->add_child("VolumeIndex")->add_child_text ("1");
        chunk->add_child("Offset")->add_child_text ("0");
-       chunk->add_child("Length")->add_child_text (raw_convert<string> (boost::filesystem::file_size (_file.get())));
+       chunk->add_child("Length")->add_child_text (raw_convert<string> (boost::filesystem::file_size (file)));
 }
 
 string
index f69be5a168f38c32c68ace428f389669408cc4ca..b6ab8fd26101fd48a41b628c59dceab8931266a8 100644 (file)
@@ -76,9 +76,9 @@ public:
        /** Write details of the asset to a ASSETMAP.
         *  @param node Parent node.
         */
-       void write_to_assetmap (xmlpp::Node* node, boost::filesystem::path root) const;
+       virtual void write_to_assetmap (xmlpp::Node* node, boost::filesystem::path root) const;
 
-       void add_to_pkl (boost::shared_ptr<PKL> pkl, boost::filesystem::path root) const;
+       virtual void add_to_pkl (boost::shared_ptr<PKL> pkl, boost::filesystem::path root) const;
 
        /** @return the most recent disk file used to read or write this asset, if there is one */
        boost::optional<boost::filesystem::path> file () const {
@@ -97,6 +97,8 @@ protected:
        /** The most recent disk file used to read or write this asset */
        mutable boost::optional<boost::filesystem::path> _file;
 
+       static void write_file_to_assetmap (xmlpp::Node* node, boost::filesystem::path root, boost::filesystem::path file, std::string id);
+
 private:
        friend struct ::asset_test;
 
index 58d05651942cde1c688aadaccb3114e67b9e5016..fa8ffabf8f6d8f6aa78926ed7edb03ecfc82ec01 100644 (file)
@@ -162,7 +162,6 @@ DCP::read (bool keep_going, ReadErrors* errors, bool ignore_incorrect_picture_mx
                        break;
                }
                }
-
        }
 
        if (!pkl_path) {
@@ -261,6 +260,8 @@ DCP::read (bool keep_going, ReadErrors* errors, bool ignore_incorrect_picture_mx
                        }
                } else if (pkl_type == FontAsset::static_pkl_type(*_standard)) {
                        other_assets.push_back (shared_ptr<FontAsset> (new FontAsset (i->first, path)));
+               } else if (pkl_type == "image/png") {
+                       /* It's an Interop PNG subtitle; let it go */
                } else {
                        throw DCPReadError (String::compose("Unknown asset type %1 in PKL", pkl_type));
                }
index e2873fb656853c7f31e671709f47bfbc0a262318..d250b752e9d1f0aa10d00a1398237073d8ab853c 100644 (file)
@@ -83,7 +83,7 @@ InteropSubtitleAsset::InteropSubtitleAsset (boost::filesystem::path file)
        BOOST_FOREACH (shared_ptr<Subtitle> i, _subtitles) {
                shared_ptr<SubtitleImage> si = dynamic_pointer_cast<SubtitleImage>(i);
                if (si) {
-                       si->set_png_image (Data (file.parent_path() / String::compose("%1.png", si->id())));
+                       si->read_png_file (file.parent_path() / String::compose("%1.png", si->id()));
                }
        }
 }
@@ -189,7 +189,7 @@ InteropSubtitleAsset::write (boost::filesystem::path p) const
        BOOST_FOREACH (shared_ptr<dcp::Subtitle> i, _subtitles) {
                shared_ptr<dcp::SubtitleImage> im = dynamic_pointer_cast<dcp::SubtitleImage> (i);
                if (im) {
-                       im->png_image().write (p.parent_path() / String::compose("%1.png", im->id()));
+                       im->write_png_file(p.parent_path() / String::compose("%1.png", im->id()));
                }
        }
 
@@ -249,3 +249,31 @@ InteropSubtitleAsset::add_font_assets (list<shared_ptr<Asset> >& assets)
                assets.push_back (shared_ptr<FontAsset> (new FontAsset (i.uuid, i.file.get ())));
        }
 }
+
+void
+InteropSubtitleAsset::write_to_assetmap (xmlpp::Node* node, boost::filesystem::path root) const
+{
+       Asset::write_to_assetmap (node, root);
+
+       BOOST_FOREACH (shared_ptr<dcp::Subtitle> i, _subtitles) {
+               shared_ptr<dcp::SubtitleImage> im = dynamic_pointer_cast<dcp::SubtitleImage> (i);
+               if (im) {
+                       DCP_ASSERT (im->file());
+                       write_file_to_assetmap (node, root, im->file().get(), im->id());
+               }
+       }
+}
+
+void
+InteropSubtitleAsset::add_to_pkl (shared_ptr<PKL> pkl, boost::filesystem::path root) const
+{
+       Asset::add_to_pkl (pkl, root);
+
+       BOOST_FOREACH (shared_ptr<dcp::Subtitle> i, _subtitles) {
+               shared_ptr<dcp::SubtitleImage> im = dynamic_pointer_cast<dcp::SubtitleImage> (i);
+               if (im) {
+                       Data png_image = im->png_image ();
+                       pkl->add_asset (im->id(), optional<string>(), make_digest(png_image), png_image.size(), "image/png");
+               }
+       }
+}
index 703229a77cb788e243cfb73b9a58c2db348b151f..ba8b5edf16ef08cfda87b365178ec3217a2bce9c 100644 (file)
@@ -59,6 +59,9 @@ public:
                NoteHandler note
                ) const;
 
+       void write_to_assetmap (xmlpp::Node* node, boost::filesystem::path root) const;
+       void add_to_pkl (boost::shared_ptr<PKL> pkl, boost::filesystem::path root) const;
+
        std::list<boost::shared_ptr<LoadFontNode> > load_font_nodes () const;
 
        void add_font (std::string load_id, boost::filesystem::path file);
index 49c00f77789536141186336f4d47c4d4bf39cfd9..e1f123bc916933f92ceaea1797847fbfbdf71923 100644 (file)
@@ -75,6 +75,20 @@ SubtitleImage::SubtitleImage (
 
 }
 
+void
+SubtitleImage::read_png_file (boost::filesystem::path file)
+{
+       _file = file;
+       _png_image = Data (file);
+}
+
+void
+SubtitleImage::write_png_file (boost::filesystem::path file) const
+{
+       _file = file;
+       png_image().write (file);
+}
+
 bool
 dcp::operator== (SubtitleImage const & a, SubtitleImage const & b)
 {
index 062df56b36aa09b02574cbbfe4347d76e78d687e..3c4b3fe8b7a07e193483036a281eeea6ebb864a1 100644 (file)
@@ -82,17 +82,26 @@ public:
                return _png_image;
        }
 
-       void set_png_image (Data d) {
-               _png_image = d;
+       void set_png_image (Data png) {
+               _png_image = png;
        }
 
+       void read_png_file (boost::filesystem::path file);
+       void write_png_file (boost::filesystem::path file) const;
+
        std::string id () const {
                return _id;
        }
 
+       /** @return the most recent disk file used to read or write this asset, if there is one */
+       boost::optional<boost::filesystem::path> file () const {
+               return _file;
+       }
+
 private:
        Data _png_image;
        std::string _id;
+       mutable boost::optional<boost::filesystem::path> _file;
 };
 
 bool operator== (SubtitleImage const & a, SubtitleImage const & b);
index ac2246c30f851c0abb99146180e43eb6f9eb6a77..3d90ccaa4fe7f6e1155c45cd8e2d6e7ec43e2c88 100644 (file)
@@ -88,6 +88,17 @@ dcp::make_uuid ()
        return string (buffer);
 }
 
+string
+dcp::make_digest (Data data)
+{
+       SHA_CTX sha;
+       SHA1_Init (&sha);
+       SHA1_Update (&sha, data.data().get(), data.size());
+       byte_t byte_buffer[SHA_DIGEST_LENGTH];
+       SHA1_Final (byte_buffer, &sha);
+       char digest[64];
+       return Kumu::base64encode (byte_buffer, SHA_DIGEST_LENGTH, digest, 64);
+}
 
 /** Create a digest for a file.
  *  @param filename File name.
index 20ea5ac571c991cc4a37f17bb1888e37423b3f0d..9803ad03aa2f38541a8a86fe58b817ff7e929723 100644 (file)
@@ -60,6 +60,7 @@ class OpenJPEGImage;
 
 extern std::string make_uuid ();
 extern std::string make_digest (boost::filesystem::path filename, boost::function<void (float)>);
+extern std::string make_digest (Data data);
 extern std::string content_kind_to_string (ContentKind kind);
 extern ContentKind content_kind_from_string (std::string kind);
 extern bool empty_or_white_space (std::string s);
diff --git a/test/data/write_interop_subtitle_test3.xml b/test/data/write_interop_subtitle_test3.xml
deleted file mode 100644 (file)
index 7ba72e4..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<DCSubtitle Version="1.0"><SubtitleID>a6c58cff-3e1e-4b38-acec-a42224475ef6</SubtitleID><MovieTitle>Test</MovieTitle><ReelNumber>1</ReelNumber><Language>EN</Language><Subtitle SpotNumber="1" TimeIn="00:04:09:229" TimeOut="00:04:11:229" FadeUpTime="0" FadeDownTime="0"><Image VAlign="top" VPosition="80">822bd341-c751-45b1-94d2-410e4ffcff1b.png</Image></Subtitle></DCSubtitle>
diff --git a/test/ref/write_interop_subtitle_test3/ASSETMAP b/test/ref/write_interop_subtitle_test3/ASSETMAP
new file mode 100644 (file)
index 0000000..82fe845
--- /dev/null
@@ -0,0 +1,2 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<AssetMap xmlns="http://www.digicine.com/PROTO-ASDCP-AM-20040311#"><Id>urn:uuid:5bdc7da4-eaf1-43e6-854f-7a1175edf94a</Id><AnnotationText>Created by libdcp1.6.1devel</AnnotationText><VolumeCount>1</VolumeCount><IssueDate>2018-09-02T04:45:18+00:00</IssueDate><Issuer>libdcp1.6.1devel</Issuer><Creator>libdcp1.6.1devel</Creator><AssetList><Asset><Id>urn:uuid:8d98d2e1-d2a1-458f-b96b-295e5b5d0860</Id><PackingList>true</PackingList><ChunkList><Chunk><Path>pkl_8d98d2e1-d2a1-458f-b96b-295e5b5d0860.xml</Path><VolumeIndex>1</VolumeIndex><Offset>0</Offset><Length>993</Length></Chunk></ChunkList></Asset><Asset><Id>urn:uuid:9f134b47-ab8d-4c3c-adbf-f494b1544283</Id><ChunkList><Chunk><Path>cpl_9f134b47-ab8d-4c3c-adbf-f494b1544283.xml</Path><VolumeIndex>1</VolumeIndex><Offset>0</Offset><Length>970</Length></Chunk></ChunkList></Asset><Asset><Id>urn:uuid:a6c58cff-3e1e-4b38-acec-a42224475ef6</Id><ChunkList><Chunk><Path>subs.xml</Path><VolumeIndex>1</VolumeIndex><Offset>0</Offset><Length>414</Length></Chunk></ChunkList></Asset><Asset><Id>urn:uuid:822bd341-c751-45b1-94d2-410e4ffcff1b</Id><ChunkList><Chunk><Path>822bd341-c751-45b1-94d2-410e4ffcff1b.png</Path><VolumeIndex>1</VolumeIndex><Offset>0</Offset><Length>44935</Length></Chunk></ChunkList></Asset></AssetList></AssetMap>
diff --git a/test/ref/write_interop_subtitle_test3/pkl.xml b/test/ref/write_interop_subtitle_test3/pkl.xml
new file mode 100644 (file)
index 0000000..995fabd
--- /dev/null
@@ -0,0 +1,2 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<PackingList xmlns="http://www.digicine.com/PROTO-ASDCP-PKL-20040311#"><Id>urn:uuid:8d98d2e1-d2a1-458f-b96b-295e5b5d0860</Id><AnnotationText>Created by libdcp1.6.1devel</AnnotationText><IssueDate>2018-09-02T04:45:18+00:00</IssueDate><Issuer>libdcp1.6.1devel</Issuer><Creator>libdcp1.6.1devel</Creator><AssetList><Asset><Id>urn:uuid:9f134b47-ab8d-4c3c-adbf-f494b1544283</Id><AnnotationText>9f134b47-ab8d-4c3c-adbf-f494b1544283</AnnotationText><Hash>X3ymjtLWzUZ0Av4L7y2/2X3wlB0=</Hash><Size>970</Size><Type>text/xml;asdcpKind=CPL</Type></Asset><Asset><Id>urn:uuid:a6c58cff-3e1e-4b38-acec-a42224475ef6</Id><AnnotationText>a6c58cff-3e1e-4b38-acec-a42224475ef6</AnnotationText><Hash>+k1Lnon0dJab5s8+wucSdGVy3Go=</Hash><Size>414</Size><Type>text/xml;asdcpKind=Subtitle</Type></Asset><Asset><Id>urn:uuid:822bd341-c751-45b1-94d2-410e4ffcff1b</Id><Hash>w0Xc4TUYpao08I0yPSDfFkaEwdg=</Hash><Size>44935</Size><Type>image/png</Type></Asset></AssetList></PackingList>
diff --git a/test/ref/write_interop_subtitle_test3/subs.xml b/test/ref/write_interop_subtitle_test3/subs.xml
new file mode 100644 (file)
index 0000000..7ba72e4
--- /dev/null
@@ -0,0 +1,2 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<DCSubtitle Version="1.0"><SubtitleID>a6c58cff-3e1e-4b38-acec-a42224475ef6</SubtitleID><MovieTitle>Test</MovieTitle><ReelNumber>1</ReelNumber><Language>EN</Language><Subtitle SpotNumber="1" TimeIn="00:04:09:229" TimeOut="00:04:11:229" FadeUpTime="0" FadeDownTime="0"><Image VAlign="top" VPosition="80">822bd341-c751-45b1-94d2-410e4ffcff1b.png</Image></Subtitle></DCSubtitle>
index cf595faf75bf56d8507c490b5246de7b60355f49..e57b0e7f43f663bf53b8520bf2c835a467b2261d 100644 (file)
 #include "subtitle_string.h"
 #include "subtitle_image.h"
 #include "subtitle_asset_internal.h"
+#include "reel_subtitle_asset.h"
+#include "reel.h"
+#include "cpl.h"
+#include "dcp.h"
 #include "test.h"
 #include "util.h"
 #include <boost/test/unit_test.hpp>
@@ -312,12 +316,12 @@ BOOST_AUTO_TEST_CASE (write_interop_subtitle_test2)
 /* Write some subtitle content as Interop XML using bitmaps and check that it is right */
 BOOST_AUTO_TEST_CASE (write_interop_subtitle_test3)
 {
-       dcp::InteropSubtitleAsset c;
-       c.set_reel_number ("1");
-       c.set_language ("EN");
-       c.set_movie_title ("Test");
+       shared_ptr<dcp::InteropSubtitleAsset> c (new dcp::InteropSubtitleAsset());
+       c->set_reel_number ("1");
+       c->set_language ("EN");
+       c->set_movie_title ("Test");
 
-       c.add (
+       c->add (
                shared_ptr<dcp::Subtitle> (
                        new dcp::SubtitleImage (
                                dcp::Data ("test/data/sub.png"),
@@ -333,17 +337,43 @@ BOOST_AUTO_TEST_CASE (write_interop_subtitle_test3)
                        )
                );
 
-       c._id = "a6c58cff-3e1e-4b38-acec-a42224475ef6";
-
+       c->_id = "a6c58cff-3e1e-4b38-acec-a42224475ef6";
        boost::filesystem::create_directories ("build/test/write_interop_subtitle_test3");
-       c.write ("build/test/write_interop_subtitle_test3/subs.xml");
+       c->write ("build/test/write_interop_subtitle_test3/subs.xml");
+
+       shared_ptr<dcp::Reel> reel (new dcp::Reel());
+       reel->add(shared_ptr<dcp::ReelSubtitleAsset>(new dcp::ReelSubtitleAsset(c, dcp::Fraction(24, 1), 6046, 0)));
+
+       dcp::XMLMetadata xml_meta;
+       xml_meta.issue_date = "2018-09-02T04:45:18+00:00";
+
+       shared_ptr<dcp::CPL> cpl (new dcp::CPL ("My film", dcp::FEATURE));
+       cpl->add (reel);
+       cpl->set_metadata (xml_meta);
+       cpl->set_content_version_label_text ("foo");
+
+       dcp::DCP dcp ("build/test/write_interop_subtitle_test3");
+       dcp.add (cpl);
+       dcp.write_xml (dcp::INTEROP, xml_meta);
 
        check_xml (
-               dcp::file_to_string("test/data/write_interop_subtitle_test3.xml"),
+               dcp::file_to_string("test/ref/write_interop_subtitle_test3/subs.xml"),
                dcp::file_to_string("build/test/write_interop_subtitle_test3/subs.xml"),
                list<string>()
                );
        check_file ("build/test/write_interop_subtitle_test3/822bd341-c751-45b1-94d2-410e4ffcff1b.png", "test/data/sub.png");
+
+       check_xml (
+               dcp::file_to_string("test/ref/write_interop_subtitle_test3/ASSETMAP"),
+               dcp::file_to_string("build/test/write_interop_subtitle_test3/ASSETMAP"),
+               list<string>()
+               );
+
+       check_xml (
+               dcp::file_to_string("test/ref/write_interop_subtitle_test3/pkl.xml"),
+               dcp::file_to_string("build/test/write_interop_subtitle_test3/pkl_8d98d2e1-d2a1-458f-b96b-295e5b5d0860.xml"),
+               list<string>()
+               );
 }
 
 /* Write some subtitle content as SMPTE XML and check that it is right */