Fix problems when adding KDMs to a VF, before adding the OV. v1.8.2
authorCarl Hetherington <cth@carlh.net>
Fri, 8 Oct 2021 10:08:10 +0000 (12:08 +0200)
committerCarl Hetherington <cth@carlh.net>
Fri, 8 Oct 2021 10:48:43 +0000 (12:48 +0200)
If we have a KDM which contains keys for all the assets referred
to by a VF (even the ones in the OV) we would previously throw
an exception because we tried to give the KDM to the resolved
assets when it was given to the Reel.

Here we change things so that the addition of KDMs to assets can
be deferred if the asset is not yet resolved.

src/reel.cc
src/reel.h
src/sound_asset.h
test/kdm_test.cc
test/test.cc
test/test.h
test/verify_test.cc

index daa1067c80c3dacb4ba4229cabeabebabf28b616..cca62ed820e739db58c9876972d4e7698a8ce881 100644 (file)
@@ -312,28 +312,38 @@ Reel::all_encrypted () const
 void
 Reel::add (DecryptedKDM const & kdm)
 {
-       auto keys = kdm.keys ();
+       give_kdm_to_assets (kdm);
+       /* We have to keep the KDMs that we are given, as they will not be passed to unresolved assets.
+        * After we resolve some assets we will re-call give_kdm_to_assets() with all the KDMs that
+        * we have been given so far.
+        */
+       _kdms.push_back (kdm);
+}
+
 
-       for (auto const& i: keys) {
-               if (_main_picture && i.id() == _main_picture->key_id()) {
+void
+Reel::give_kdm_to_assets (DecryptedKDM const & kdm)
+{
+       for (auto const& i: kdm.keys()) {
+               if (_main_picture && i.id() == _main_picture->key_id() && _main_picture->asset_ref().resolved()) {
                        _main_picture->asset()->set_key (i.key());
                }
-               if (_main_sound && i.id() == _main_sound->key_id()) {
+               if (_main_sound && i.id() == _main_sound->key_id() && _main_sound->asset_ref().resolved()) {
                        _main_sound->asset()->set_key (i.key());
                }
                if (_main_subtitle) {
                        auto smpte = dynamic_pointer_cast<ReelSMPTESubtitleAsset>(_main_subtitle);
-                       if (smpte && i.id() == smpte->key_id()) {
+                       if (smpte && i.id() == smpte->key_id() && smpte->asset_ref().resolved()) {
                                smpte->smpte_asset()->set_key(i.key());
                        }
                }
                for (auto j: _closed_captions) {
                        auto smpte = dynamic_pointer_cast<ReelSMPTESubtitleAsset>(j);
-                       if (smpte && i.id() == smpte->key_id()) {
+                       if (smpte && i.id() == smpte->key_id() && smpte->asset_ref().resolved()) {
                                smpte->smpte_asset()->set_key(i.key());
                        }
                }
-               if (_atmos && i.id() == _atmos->key_id()) {
+               if (_atmos && i.id() == _atmos->key_id() && _atmos->asset_ref().resolved()) {
                        _atmos->asset()->set_key (i.key());
                }
        }
@@ -424,6 +434,10 @@ Reel::resolve_refs (vector<shared_ptr<Asset>> assets)
        if (_atmos) {
                _atmos->asset_ref().resolve (assets);
        }
+
+       for (auto const& i: _kdms) {
+               give_kdm_to_assets (i);
+       }
 }
 
 
index d3ea7f0c10b90cb25bdf75f7b9302ceb4d5a6065..9a3c38d3d61abaa0f6972dcc9ae174e389572531 100644 (file)
 #define LIBDCP_REEL_H
 
 
+#include "decrypted_kdm.h"
 #include "key.h"
-#include "types.h"
 #include "ref.h"
-#include <memory>
+#include "types.h"
 #include <boost/function.hpp>
+#include <memory>
 
 
 namespace cxml {
@@ -136,12 +137,16 @@ public:
        void resolve_refs (std::vector<std::shared_ptr<Asset>>);
 
 private:
+       void give_kdm_to_assets (dcp::DecryptedKDM const& kdm);
+
        std::shared_ptr<ReelPictureAsset> _main_picture;
        std::shared_ptr<ReelSoundAsset> _main_sound;
        std::shared_ptr<ReelSubtitleAsset> _main_subtitle;
        std::shared_ptr<ReelMarkersAsset> _main_markers;
        std::vector<std::shared_ptr<ReelClosedCaptionAsset>> _closed_captions;
        std::shared_ptr<ReelAtmosAsset> _atmos;
+
+       std::vector<dcp::DecryptedKDM> _kdms;
 };
 
 }
index 799e671ce77726616a38d7017d8f4c3106036a2d..c943d88bd4a43ae8b320012e1e27a5025501fe00 100644 (file)
@@ -55,7 +55,7 @@ namespace dcp {
 
 
 extern std::shared_ptr<dcp::SoundAsset> simple_sound (
-       boost::filesystem::path path, std::string suffix, dcp::MXFMetadata mxf_meta, std::string language, int frames, int sample_rate
+       boost::filesystem::path path, std::string suffix, dcp::MXFMetadata mxf_meta, std::string language, int frames, int sample_rate, boost::optional<dcp::Key>
        );
 
 
@@ -112,7 +112,7 @@ public:
 private:
        friend class SoundAssetWriter;
        friend std::shared_ptr<dcp::SoundAsset> (::simple_sound) (
-               boost::filesystem::path path, std::string suffix, dcp::MXFMetadata mxf_meta, std::string language, int frames, int sample_rate
+               boost::filesystem::path path, std::string suffix, dcp::MXFMetadata mxf_meta, std::string language, int frames, int sample_rate, boost::optional<dcp::Key>
                );
 
        std::string pkl_type (Standard standard) const override {
index 5b0ae6222031565f700fc1dcf1c167d066f48526..d5cd456f39268eb389fda64087cf14898b390530 100644 (file)
@@ -40,6 +40,9 @@
 #include "picture_asset_writer.h"
 #include "reel.h"
 #include "reel_mono_picture_asset.h"
+#include "reel_sound_asset.h"
+#include "reel_smpte_subtitle_asset.h"
+#include "smpte_subtitle_asset.h"
 #include "test.h"
 #include "types.h"
 #include "util.h"
@@ -51,11 +54,11 @@ LIBDCP_ENABLE_WARNINGS
 #include <boost/test/unit_test.hpp>
 
 
-using std::list;
-using std::string;
-using std::vector;
+using std::dynamic_pointer_cast;
 using std::make_shared;
 using std::shared_ptr;
+using std::string;
+using std::vector;
 using boost::optional;
 
 
@@ -296,3 +299,85 @@ BOOST_AUTO_TEST_CASE (validity_period_test1)
                dcp::BadKDMDateError
                );
 }
+
+
+/** Test the case where we have:
+ *  - a OV + VF
+ *  - a single KDM which has keys for both OV and VF assets
+ *  and we load VF, then KDM, then the OV.  The OV's assets should have their
+ *  KDMs properly assigned.
+ */
+BOOST_AUTO_TEST_CASE (vf_kdm_test)
+{
+       /* Make OV */
+
+       boost::filesystem::path const ov_path = "build/test/vf_kdm_test_ov";
+       boost::filesystem::path const vf_path = "build/test/vf_kdm_test_vf";
+       dcp::Key key;
+
+       auto ov = make_simple(ov_path, 1, 48, dcp::Standard::SMPTE, key);
+       ov->write_xml ();
+
+       auto ov_reel = ov->cpls()[0]->reels()[0];
+       auto ov_reel_picture = make_shared<dcp::ReelMonoPictureAsset>(dynamic_pointer_cast<dcp::ReelMonoPictureAsset>(ov_reel->main_picture())->mono_asset(), 0);
+       auto ov_reel_sound = make_shared<dcp::ReelSoundAsset>(ov_reel->main_sound()->asset(), 0);
+
+       /* Make VF */
+
+       auto subs = make_shared<dcp::SMPTESubtitleAsset>();
+       subs->add(simple_subtitle());
+       subs->set_key(key);
+
+       boost::filesystem::remove_all (vf_path);
+       boost::filesystem::create_directory (vf_path);
+       dcp::ArrayData data(4096);
+       subs->add_font ("afont", data);
+       subs->write (vf_path / "subs.xml");
+
+       auto reel_subs = make_shared<dcp::ReelSMPTESubtitleAsset>(subs, dcp::Fraction(24, 1), 192, 0);
+
+       auto reel = make_shared<dcp::Reel>(ov_reel_picture, ov_reel_sound, reel_subs);
+
+       auto cpl = make_shared<dcp::CPL>("A Test DCP", dcp::ContentKind::TRAILER, dcp::Standard::SMPTE);
+       cpl->add (reel);
+
+       auto vf = make_shared<dcp::DCP>("build/test/vf_kdm_test_vf");
+       vf->add (cpl);
+       vf->write_xml ();
+
+       /* Make KDM for VF */
+
+       auto kdm = dcp::DecryptedKDM(
+               cpl,
+               key,
+               dcp::LocalTime ("2016-01-01T00:00:00+00:00"),
+               dcp::LocalTime ("2017-01-08T00:00:00+00:00"),
+               "libdcp",
+               "test",
+               "2012-07-17T04:45:18+00:00"
+               );
+
+       /* Decrypt VF with the KDM */
+
+       dcp::DCP reload_vf(vf_path);
+       reload_vf.read();
+       reload_vf.add (kdm);
+
+       /* Add the OV */
+
+       dcp::DCP reload_ov(ov_path);
+       reload_ov.read();
+       reload_vf.resolve_refs(reload_ov.assets());
+
+       /* Check that we can decrypt the VF */
+
+       BOOST_REQUIRE_EQUAL(reload_vf.cpls().size(), 1U);
+       BOOST_REQUIRE_EQUAL(reload_vf.cpls()[0]->reels().size(), 1U);
+       BOOST_REQUIRE(reload_vf.cpls()[0]->reels()[0]->main_picture());
+       BOOST_REQUIRE(reload_vf.cpls()[0]->reels()[0]->main_picture()->asset());
+       auto mono_asset = dynamic_pointer_cast<dcp::MonoPictureAsset>(reload_vf.cpls()[0]->reels()[0]->main_picture()->asset());
+       BOOST_REQUIRE(mono_asset);
+       auto reader = mono_asset->start_read();
+       reader->set_check_hmac(false);
+       reader->get_frame(0)->xyz_image();
+}
index 3cb40aaf424f169aa19759720b49c5561884dc38..f946c5dcfed90da0b10a3a796cc4178157b03aa3 100644 (file)
@@ -266,16 +266,19 @@ RNGFixer::~RNGFixer ()
 
 
 shared_ptr<dcp::MonoPictureAsset>
-simple_picture (boost::filesystem::path path, string suffix, int frames)
+simple_picture (boost::filesystem::path path, string suffix, int frames, optional<dcp::Key> key)
 {
        dcp::MXFMetadata mxf_meta;
        mxf_meta.company_name = "OpenDCP";
        mxf_meta.product_name = "OpenDCP";
        mxf_meta.product_version = "0.0.25";
 
-       shared_ptr<dcp::MonoPictureAsset> mp (new dcp::MonoPictureAsset (dcp::Fraction (24, 1), dcp::Standard::SMPTE));
+       auto mp = make_shared<dcp::MonoPictureAsset>(dcp::Fraction (24, 1), dcp::Standard::SMPTE);
        mp->set_metadata (mxf_meta);
-       shared_ptr<dcp::PictureAssetWriter> picture_writer = mp->start_write (path / dcp::String::compose("video%1.mxf", suffix), false);
+       if (key) {
+               mp->set_key (*key);
+       }
+       auto picture_writer = mp->start_write (path / dcp::String::compose("video%1.mxf", suffix), false);
 
        dcp::Size const size (1998, 1080);
        auto image = make_shared<dcp::OpenJPEGImage>(size);
@@ -294,12 +297,15 @@ simple_picture (boost::filesystem::path path, string suffix, int frames)
 
 
 shared_ptr<dcp::SoundAsset>
-simple_sound (boost::filesystem::path path, string suffix, dcp::MXFMetadata mxf_meta, string language, int frames, int sample_rate)
+simple_sound (boost::filesystem::path path, string suffix, dcp::MXFMetadata mxf_meta, string language, int frames, int sample_rate, optional<dcp::Key> key)
 {
        int const channels = 6;
 
        /* Set a valid language, then overwrite it, so that the language parameter can be badly formed */
-       shared_ptr<dcp::SoundAsset> ms (new dcp::SoundAsset(dcp::Fraction(24, 1), sample_rate, channels, dcp::LanguageTag("en-US"), dcp::Standard::SMPTE));
+       auto ms = make_shared<dcp::SoundAsset>(dcp::Fraction(24, 1), sample_rate, channels, dcp::LanguageTag("en-US"), dcp::Standard::SMPTE);
+       if (key) {
+               ms->set_key (*key);
+       }
        ms->_language = language;
        ms->set_metadata (mxf_meta);
        shared_ptr<dcp::SoundAssetWriter> sound_writer = ms->start_write (path / dcp::String::compose("audio%1.mxf", suffix));
@@ -327,7 +333,7 @@ simple_sound (boost::filesystem::path path, string suffix, dcp::MXFMetadata mxf_
 
 
 shared_ptr<dcp::DCP>
-make_simple (boost::filesystem::path path, int reels, int frames, dcp::Standard standard)
+make_simple (boost::filesystem::path path, int reels, int frames, dcp::Standard standard, optional<dcp::Key> key)
 {
        /* Some known metadata */
        dcp::MXFMetadata mxf_meta;
@@ -335,6 +341,8 @@ make_simple (boost::filesystem::path path, int reels, int frames, dcp::Standard
        mxf_meta.product_name = "OpenDCP";
        mxf_meta.product_version = "0.0.25";
 
+       auto constexpr sample_rate = 48000;
+
        boost::filesystem::remove_all (path);
        boost::filesystem::create_directories (path);
        auto d = make_shared<dcp::DCP>(path);
@@ -347,7 +355,7 @@ make_simple (boost::filesystem::path path, int reels, int frames, dcp::Standard
                dcp::ContentVersion("urn:uuid:75ac29aa-42ac-1234-ecae-49251abefd11", "content-version-label-text")
                );
        cpl->set_main_sound_configuration("51/L,R,C,LFE,Ls,Rs");
-       cpl->set_main_sound_sample_rate(48000);
+       cpl->set_main_sound_sample_rate(sample_rate);
        cpl->set_main_picture_stored_area(dcp::Size(1998, 1080));
        cpl->set_main_picture_active_area(dcp::Size(1998, 1080));
        cpl->set_version_number(1);
@@ -355,8 +363,8 @@ make_simple (boost::filesystem::path path, int reels, int frames, dcp::Standard
        for (int i = 0; i < reels; ++i) {
                string suffix = reels == 1 ? "" : dcp::String::compose("%1", i);
 
-               shared_ptr<dcp::MonoPictureAsset> mp = simple_picture (path, suffix, frames);
-               shared_ptr<dcp::SoundAsset> ms = simple_sound (path, suffix, mxf_meta, "en-US", frames);
+               auto mp = simple_picture (path, suffix, frames, key);
+               auto ms = simple_sound (path, suffix, mxf_meta, "en-US", frames, sample_rate, key);
 
                auto reel = make_shared<dcp::Reel>(
                        shared_ptr<dcp::ReelMonoPictureAsset>(new dcp::ReelMonoPictureAsset(mp, 0)),
index 8733c7c319d42d92c986e95ea933b4eae5dd9320..339dd8aad9f84485671227c787c9665856df104b 100644 (file)
@@ -43,11 +43,30 @@ extern boost::filesystem::path xsd_test;
 extern void check_xml (xmlpp::Element* ref, xmlpp::Element* test, std::vector<std::string> ignore_tags, bool ignore_whitespace = false);
 extern void check_xml (std::string ref, std::string test, std::vector<std::string> ignore, bool ignore_whitespace = false);
 extern void check_file (boost::filesystem::path ref, boost::filesystem::path check);
-extern std::shared_ptr<dcp::MonoPictureAsset> simple_picture (boost::filesystem::path path, std::string suffix, int frames = 24);
-extern std::shared_ptr<dcp::SoundAsset> simple_sound (boost::filesystem::path path, std::string suffix, dcp::MXFMetadata mxf_meta, std::string language, int frames = 24, int sample_rate = 48000);
+extern std::shared_ptr<dcp::MonoPictureAsset> simple_picture (
+       boost::filesystem::path path,
+       std::string suffix,
+       int frames = 24,
+       boost::optional<dcp::Key> key = boost::optional<dcp::Key>()
+       );
+extern std::shared_ptr<dcp::SoundAsset> simple_sound (
+       boost::filesystem::path path,
+       std::string suffix,
+       dcp::MXFMetadata mxf_meta,
+       std::string language,
+       int frames = 24,
+       int sample_rate = 48000,
+       boost::optional<dcp::Key> key = boost::optional<dcp::Key>()
+       );
 extern std::shared_ptr<dcp::Subtitle> simple_subtitle ();
 extern std::shared_ptr<dcp::ReelMarkersAsset> simple_markers (int frames = 24);
-extern std::shared_ptr<dcp::DCP> make_simple (boost::filesystem::path path, int reels = 1, int frames = 24, dcp::Standard = dcp::Standard::SMPTE);
+extern std::shared_ptr<dcp::DCP> make_simple (
+       boost::filesystem::path path,
+       int reels = 1,
+       int frames = 24,
+       dcp::Standard = dcp::Standard::SMPTE,
+       boost::optional<dcp::Key> key = boost::optional<dcp::Key>()
+       );
 extern std::shared_ptr<dcp::DCP> make_simple_with_interop_subs (boost::filesystem::path path);
 extern std::shared_ptr<dcp::DCP> make_simple_with_smpte_subs (boost::filesystem::path path);
 extern std::shared_ptr<dcp::DCP> make_simple_with_interop_ccaps (boost::filesystem::path path);
index 62bb7f31746ef2a6e140b51604282c50f08b98b2..707eed210dbbcba7da50b04cd997d014b342f200 100644 (file)
@@ -1961,7 +1961,7 @@ BOOST_AUTO_TEST_CASE (verify_invalid_sound_frame_rate)
        auto reel_picture = make_shared<dcp::ReelMonoPictureAsset>(picture, 0);
        auto reel = make_shared<dcp::Reel>();
        reel->add (reel_picture);
-       auto sound = simple_sound (dir, "foo", dcp::MXFMetadata(), "de-DE", 24, 96000);
+       auto sound = simple_sound (dir, "foo", dcp::MXFMetadata(), "de-DE", 24, 96000, boost::none);
        auto reel_sound = make_shared<dcp::ReelSoundAsset>(sound, 0);
        reel->add (reel_sound);
        reel->add (simple_markers());