Add Dolby EDR metadata support (GH #12).
[libdcp.git] / test / cpl_metadata_test.cc
index 19b0ef8ef036f70adad02bceb29a95be0b3b21df..a811e56f2ff29e10d63c0fd2835f292d6f3d18cb 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2020 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2020-2021 Carl Hetherington <cth@carlh.net>
 
     This file is part of libdcp.
 
 
 
 #include "certificate_chain.h"
+#include "compose.hpp"
 #include "cpl.h"
 #include "exceptions.h"
 #include "language_tag.h"
 #include "reel.h"
-#include "reel_subtitle_asset.h"
+#include "reel_mono_picture_asset.h"
+#include "reel_smpte_subtitle_asset.h"
+#include "reel_sound_asset.h"
+#include "stream_operators.h"
 #include "test.h"
 #include <memory>
 #include <boost/test/unit_test.hpp>
 
 
 using std::list;
+using std::make_shared;
+using std::shared_ptr;
 using std::string;
 using std::vector;
-using std::shared_ptr;
 
 
 BOOST_AUTO_TEST_CASE (cpl_metadata_bad_values_test)
 {
-       dcp::CPL cpl("", dcp::FEATURE);
+       dcp::CPL cpl("", dcp::ContentKind::FEATURE, dcp::Standard::SMPTE);
        BOOST_CHECK_THROW (cpl.set_version_number(-1), dcp::BadSettingError);
 
-       vector<dcp::ContentVersion> cv;
-       cv.push_back (dcp::ContentVersion("same-id", "version 1"));
-       cv.push_back (dcp::ContentVersion("same-id", "version 2"));
+       vector<dcp::ContentVersion> cv = {
+               dcp::ContentVersion("same-id", "version 1"),
+               dcp::ContentVersion("same-id", "version 2")
+       };
        BOOST_CHECK_THROW (cpl.set_content_versions(cv), dcp::DuplicateIdError);
 }
 
@@ -66,11 +72,11 @@ BOOST_AUTO_TEST_CASE (main_sound_configuration_test1)
        dcp::MainSoundConfiguration msc("51/L,R,C,LFE,-,-");
        BOOST_CHECK_EQUAL (msc.to_string(), "51/L,R,C,LFE,-,-");
        BOOST_CHECK_EQUAL (msc.channels(), 6);
-       BOOST_CHECK_EQUAL (msc.field(), dcp::FIVE_POINT_ONE);
-       BOOST_CHECK_EQUAL (msc.mapping(0).get(), dcp::LEFT);
-       BOOST_CHECK_EQUAL (msc.mapping(1).get(), dcp::RIGHT);
-       BOOST_CHECK_EQUAL (msc.mapping(2).get(), dcp::CENTRE);
-       BOOST_CHECK_EQUAL (msc.mapping(3).get(), dcp::LFE);
+       BOOST_CHECK_EQUAL (msc.field(), dcp::MCASoundField::FIVE_POINT_ONE);
+       BOOST_CHECK_EQUAL (msc.mapping(0).get(), dcp::Channel::LEFT);
+       BOOST_CHECK_EQUAL (msc.mapping(1).get(), dcp::Channel::RIGHT);
+       BOOST_CHECK_EQUAL (msc.mapping(2).get(), dcp::Channel::CENTRE);
+       BOOST_CHECK_EQUAL (msc.mapping(3).get(), dcp::Channel::LFE);
        BOOST_CHECK (!msc.mapping(4));
        BOOST_CHECK (!msc.mapping(5));
 }
@@ -81,11 +87,11 @@ BOOST_AUTO_TEST_CASE (main_sound_configuration_test2)
        dcp::MainSoundConfiguration msc("71/L,R,C,LFE,-,-");
        BOOST_CHECK_EQUAL (msc.to_string(), "71/L,R,C,LFE,-,-");
        BOOST_CHECK_EQUAL (msc.channels(), 6);
-       BOOST_CHECK_EQUAL (msc.field(), dcp::SEVEN_POINT_ONE);
-       BOOST_CHECK_EQUAL (msc.mapping(0).get(), dcp::LEFT);
-       BOOST_CHECK_EQUAL (msc.mapping(1).get(), dcp::RIGHT);
-       BOOST_CHECK_EQUAL (msc.mapping(2).get(), dcp::CENTRE);
-       BOOST_CHECK_EQUAL (msc.mapping(3).get(), dcp::LFE);
+       BOOST_CHECK_EQUAL (msc.field(), dcp::MCASoundField::SEVEN_POINT_ONE);
+       BOOST_CHECK_EQUAL (msc.mapping(0).get(), dcp::Channel::LEFT);
+       BOOST_CHECK_EQUAL (msc.mapping(1).get(), dcp::Channel::RIGHT);
+       BOOST_CHECK_EQUAL (msc.mapping(2).get(), dcp::Channel::CENTRE);
+       BOOST_CHECK_EQUAL (msc.mapping(3).get(), dcp::Channel::LFE);
        BOOST_CHECK (!msc.mapping(4));
        BOOST_CHECK (!msc.mapping(5));
 }
@@ -96,13 +102,13 @@ BOOST_AUTO_TEST_CASE (main_sound_configuration_test3)
        dcp::MainSoundConfiguration msc("71/L,-,C,LFE,Lss,Rss");
        BOOST_CHECK_EQUAL (msc.to_string(), "71/L,-,C,LFE,Lss,Rss");
        BOOST_CHECK_EQUAL (msc.channels(), 6);
-       BOOST_CHECK_EQUAL (msc.field(), dcp::SEVEN_POINT_ONE);
-       BOOST_CHECK_EQUAL (msc.mapping(0).get(), dcp::LEFT);
+       BOOST_CHECK_EQUAL (msc.field(), dcp::MCASoundField::SEVEN_POINT_ONE);
+       BOOST_CHECK_EQUAL (msc.mapping(0).get(), dcp::Channel::LEFT);
        BOOST_CHECK (!msc.mapping(1));
-       BOOST_CHECK_EQUAL (msc.mapping(2).get(), dcp::CENTRE);
-       BOOST_CHECK_EQUAL (msc.mapping(3).get(), dcp::LFE);
-       BOOST_CHECK_EQUAL (msc.mapping(4).get(), dcp::LS);
-       BOOST_CHECK_EQUAL (msc.mapping(5).get(), dcp::RS);
+       BOOST_CHECK_EQUAL (msc.mapping(2).get(), dcp::Channel::CENTRE);
+       BOOST_CHECK_EQUAL (msc.mapping(3).get(), dcp::Channel::LFE);
+       BOOST_CHECK_EQUAL (msc.mapping(4).get(), dcp::Channel::LS);
+       BOOST_CHECK_EQUAL (msc.mapping(5).get(), dcp::Channel::RS);
 }
 
 
@@ -111,13 +117,13 @@ BOOST_AUTO_TEST_CASE (main_sound_configuration_test4)
        dcp::MainSoundConfiguration msc("71/L,-,C,LFE,Lss,Rss,-,-,-,-,-,-,-,-,-");
        BOOST_CHECK_EQUAL (msc.to_string(), "71/L,-,C,LFE,Lss,Rss,-,-,-,-,-,-,-,-,-");
        BOOST_CHECK_EQUAL (msc.channels(), 15);
-       BOOST_CHECK_EQUAL (msc.field(), dcp::SEVEN_POINT_ONE);
-       BOOST_CHECK_EQUAL (msc.mapping(0).get(), dcp::LEFT);
+       BOOST_CHECK_EQUAL (msc.field(), dcp::MCASoundField::SEVEN_POINT_ONE);
+       BOOST_CHECK_EQUAL (msc.mapping(0).get(), dcp::Channel::LEFT);
        BOOST_CHECK (!msc.mapping(1));
-       BOOST_CHECK_EQUAL (msc.mapping(2).get(), dcp::CENTRE);
-       BOOST_CHECK_EQUAL (msc.mapping(3).get(), dcp::LFE);
-       BOOST_CHECK_EQUAL (msc.mapping(4).get(), dcp::LS);
-       BOOST_CHECK_EQUAL (msc.mapping(5).get(), dcp::RS);
+       BOOST_CHECK_EQUAL (msc.mapping(2).get(), dcp::Channel::CENTRE);
+       BOOST_CHECK_EQUAL (msc.mapping(3).get(), dcp::Channel::LFE);
+       BOOST_CHECK_EQUAL (msc.mapping(4).get(), dcp::Channel::LS);
+       BOOST_CHECK_EQUAL (msc.mapping(5).get(), dcp::Channel::RS);
        for (int i = 6; i < 15; ++i) {
                BOOST_CHECK (!msc.mapping(i));
        }
@@ -129,49 +135,69 @@ BOOST_AUTO_TEST_CASE (main_sound_configuration_test5)
        dcp::MainSoundConfiguration msc("71/L,-,C,LFE,Lss,Rss,HI,VIN,-,-,Lrs,Rrs,DBOX,FSKSync,SLVS");
        BOOST_CHECK_EQUAL (msc.to_string(), "71/L,-,C,LFE,Lss,Rss,HI,VIN,-,-,Lrs,Rrs,DBOX,FSKSync,SLVS");
        BOOST_CHECK_EQUAL (msc.channels(), 15);
-       BOOST_CHECK_EQUAL (msc.field(), dcp::SEVEN_POINT_ONE);
-       BOOST_CHECK_EQUAL (msc.mapping(0).get(), dcp::LEFT);
+       BOOST_CHECK_EQUAL (msc.field(), dcp::MCASoundField::SEVEN_POINT_ONE);
+       BOOST_CHECK_EQUAL (msc.mapping(0).get(), dcp::Channel::LEFT);
        BOOST_CHECK (!msc.mapping(1));
-       BOOST_CHECK_EQUAL (msc.mapping(2).get(), dcp::CENTRE);
-       BOOST_CHECK_EQUAL (msc.mapping(3).get(), dcp::LFE);
-       BOOST_CHECK_EQUAL (msc.mapping(4).get(), dcp::LS);
-       BOOST_CHECK_EQUAL (msc.mapping(5).get(), dcp::RS);
-       BOOST_CHECK_EQUAL (msc.mapping(6).get(), dcp::HI);
-       BOOST_CHECK_EQUAL (msc.mapping(7).get(), dcp::VI);
+       BOOST_CHECK_EQUAL (msc.mapping(2).get(), dcp::Channel::CENTRE);
+       BOOST_CHECK_EQUAL (msc.mapping(3).get(), dcp::Channel::LFE);
+       BOOST_CHECK_EQUAL (msc.mapping(4).get(), dcp::Channel::LS);
+       BOOST_CHECK_EQUAL (msc.mapping(5).get(), dcp::Channel::RS);
+       BOOST_CHECK_EQUAL (msc.mapping(6).get(), dcp::Channel::HI);
+       BOOST_CHECK_EQUAL (msc.mapping(7).get(), dcp::Channel::VI);
        BOOST_CHECK (!msc.mapping(8));
        BOOST_CHECK (!msc.mapping(9));
-       BOOST_CHECK_EQUAL (msc.mapping(10).get(), dcp::BSL);
-       BOOST_CHECK_EQUAL (msc.mapping(11).get(), dcp::BSR);
-       BOOST_CHECK_EQUAL (msc.mapping(12).get(), dcp::MOTION_DATA);
-       BOOST_CHECK_EQUAL (msc.mapping(13).get(), dcp::SYNC_SIGNAL);
-       BOOST_CHECK_EQUAL (msc.mapping(14).get(), dcp::SIGN_LANGUAGE);
+       BOOST_CHECK_EQUAL (msc.mapping(10).get(), dcp::Channel::BSL);
+       BOOST_CHECK_EQUAL (msc.mapping(11).get(), dcp::Channel::BSR);
+       BOOST_CHECK_EQUAL (msc.mapping(12).get(), dcp::Channel::MOTION_DATA);
+       BOOST_CHECK_EQUAL (msc.mapping(13).get(), dcp::Channel::SYNC_SIGNAL);
+       BOOST_CHECK_EQUAL (msc.mapping(14).get(), dcp::Channel::SIGN_LANGUAGE);
+}
+
+
+/* 482-12 says that implementations may use case-insensitive comparisons for the channel identifiers,
+ * and there is one DCP in the private test suite (made by Disney) that uses LS for left surround.
+ */
+BOOST_AUTO_TEST_CASE(main_sound_configuration_test_case_insensitive)
+{
+       dcp::MainSoundConfiguration msc("51/L,-,C,LFE,LS,RS,HI,VIN");
+       BOOST_CHECK_EQUAL(msc.to_string(), "51/L,-,C,LFE,Ls,Rs,HI,VIN");
+       BOOST_CHECK_EQUAL(msc.channels(), 8);
+       BOOST_CHECK_EQUAL(msc.field(), dcp::MCASoundField::FIVE_POINT_ONE);
+       BOOST_CHECK_EQUAL(msc.mapping(0).get(), dcp::Channel::LEFT);
+       BOOST_CHECK(!msc.mapping(1));
+       BOOST_CHECK_EQUAL(msc.mapping(2).get(), dcp::Channel::CENTRE);
+       BOOST_CHECK_EQUAL(msc.mapping(3).get(), dcp::Channel::LFE);
+       BOOST_CHECK_EQUAL(msc.mapping(4).get(), dcp::Channel::LS);
+       BOOST_CHECK_EQUAL(msc.mapping(5).get(), dcp::Channel::RS);
+       BOOST_CHECK_EQUAL(msc.mapping(6).get(), dcp::Channel::HI);
+       BOOST_CHECK_EQUAL(msc.mapping(7).get(), dcp::Channel::VI);
 }
 
 
 BOOST_AUTO_TEST_CASE (luminance_test1)
 {
-       BOOST_CHECK_NO_THROW (dcp::Luminance(4, dcp::Luminance::CANDELA_PER_SQUARE_METRE));
-       BOOST_CHECK_THROW (dcp::Luminance(-4, dcp::Luminance::CANDELA_PER_SQUARE_METRE), dcp::MiscError);
+       BOOST_CHECK_NO_THROW (dcp::Luminance(4, dcp::Luminance::Unit::CANDELA_PER_SQUARE_METRE));
+       BOOST_CHECK_THROW (dcp::Luminance(-4, dcp::Luminance::Unit::CANDELA_PER_SQUARE_METRE), dcp::MiscError);
 }
 
 
 BOOST_AUTO_TEST_CASE (luminance_test2)
 {
-       shared_ptr<cxml::Document> doc (new cxml::Document("Luminance"));
+       auto doc = make_shared<cxml::Document>("Luminance");
 
        doc->read_string (
                "<Luminance units=\"candela-per-square-metre\">4.5</Luminance>"
                );
 
        dcp::Luminance lum (doc);
-       BOOST_CHECK (lum.unit() == dcp::Luminance::CANDELA_PER_SQUARE_METRE);
+       BOOST_CHECK (lum.unit() == dcp::Luminance::Unit::CANDELA_PER_SQUARE_METRE);
        BOOST_CHECK_CLOSE (lum.value(), 4.5, 0.1);
 }
 
 
 BOOST_AUTO_TEST_CASE (luminance_test3)
 {
-       shared_ptr<cxml::Document> doc (new cxml::Document("Luminance"));
+       auto doc = make_shared<cxml::Document>("Luminance");
 
        doc->read_string (
                "<Luminance units=\"candela-per-square-motre\">4.5</Luminance>"
@@ -183,7 +209,7 @@ BOOST_AUTO_TEST_CASE (luminance_test3)
 
 BOOST_AUTO_TEST_CASE (luminance_test4)
 {
-       shared_ptr<cxml::Document> doc (new cxml::Document("Luminance"));
+       auto doc = make_shared<cxml::Document>("Luminance");
 
        doc->read_string (
                "<Luminance units=\"candela-per-square-metre\">-4.5</Luminance>"
@@ -191,7 +217,7 @@ BOOST_AUTO_TEST_CASE (luminance_test4)
 
        /* We tolerate out-of-range values when reading from XML */
        dcp::Luminance lum (doc);
-       BOOST_CHECK (lum.unit() == dcp::Luminance::CANDELA_PER_SQUARE_METRE);
+       BOOST_CHECK (lum.unit() == dcp::Luminance::Unit::CANDELA_PER_SQUARE_METRE);
        BOOST_CHECK_CLOSE (lum.value(), -4.5, 0.1);
 }
 
@@ -205,17 +231,17 @@ BOOST_AUTO_TEST_CASE (cpl_metadata_read_test1)
        BOOST_CHECK (cpl.full_content_title_text_language().get() == "de");
        BOOST_CHECK (cpl.release_territory().get() == "ES");
        BOOST_CHECK_EQUAL (cpl.version_number().get(), 2);
-       BOOST_CHECK_EQUAL (cpl.status().get(), dcp::FINAL);
+       BOOST_CHECK_EQUAL (cpl.status().get(), dcp::Status::FINAL);
        BOOST_CHECK_EQUAL (cpl.chain().get(), "the-chain");
        BOOST_CHECK_EQUAL (cpl.distributor().get(), "the-distributor");
        BOOST_CHECK_EQUAL (cpl.facility().get(), "the-facility");
-       BOOST_CHECK (cpl.luminance() == dcp::Luminance(4.5, dcp::Luminance::FOOT_LAMBERT));
+       BOOST_CHECK (cpl.luminance() == dcp::Luminance(4.5, dcp::Luminance::Unit::FOOT_LAMBERT));
 
        dcp::MainSoundConfiguration msc(cpl.main_sound_configuration().get());
-       BOOST_CHECK_EQUAL (msc.mapping(0).get(), dcp::LEFT);
-       BOOST_CHECK_EQUAL (msc.mapping(1).get(), dcp::RIGHT);
-       BOOST_CHECK_EQUAL (msc.mapping(2).get(), dcp::CENTRE);
-       BOOST_CHECK_EQUAL (msc.mapping(3).get(), dcp::LFE);
+       BOOST_CHECK_EQUAL (msc.mapping(0).get(), dcp::Channel::LEFT);
+       BOOST_CHECK_EQUAL (msc.mapping(1).get(), dcp::Channel::RIGHT);
+       BOOST_CHECK_EQUAL (msc.mapping(2).get(), dcp::Channel::CENTRE);
+       BOOST_CHECK_EQUAL (msc.mapping(3).get(), dcp::Channel::LFE);
        BOOST_CHECK (!msc.mapping(4));
        BOOST_CHECK (!msc.mapping(5));
        BOOST_CHECK (!msc.mapping(6));
@@ -225,19 +251,19 @@ BOOST_AUTO_TEST_CASE (cpl_metadata_read_test1)
        BOOST_CHECK (!msc.mapping(10));
        BOOST_CHECK (!msc.mapping(11));
        BOOST_CHECK (!msc.mapping(12));
-       BOOST_CHECK_EQUAL (msc.mapping(13).get(), dcp::SYNC_SIGNAL);
+       BOOST_CHECK_EQUAL (msc.mapping(13).get(), dcp::Channel::SYNC_SIGNAL);
 
        BOOST_CHECK_EQUAL (cpl.main_sound_sample_rate().get(), 48000);
        BOOST_CHECK (cpl.main_picture_stored_area().get() == dcp::Size(1998, 1080));
        BOOST_CHECK (cpl.main_picture_active_area().get() == dcp::Size(1440, 1080));
 
-       list<shared_ptr<dcp::Reel> > reels = cpl.reels ();
-       BOOST_REQUIRE_EQUAL (reels.size(), 1);
+       auto reels = cpl.reels ();
+       BOOST_REQUIRE_EQUAL (reels.size(), 1U);
        BOOST_REQUIRE (reels.front()->main_subtitle()->language());
        BOOST_CHECK_EQUAL (reels.front()->main_subtitle()->language().get(), "de-DE");
 
-       vector<string> asl = cpl.additional_subtitle_languages();
-       BOOST_REQUIRE_EQUAL (asl.size(), 2);
+       auto asl = cpl.additional_subtitle_languages();
+       BOOST_REQUIRE_EQUAL (asl.size(), 2U);
        BOOST_CHECK_EQUAL (asl[0], "en-US");
        BOOST_CHECK_EQUAL (asl[1], "fr-ZA");
 
@@ -250,39 +276,40 @@ BOOST_AUTO_TEST_CASE (cpl_metadata_write_test1)
 {
        RNGFixer fix;
 
-       dcp::CPL cpl("", dcp::FEATURE);
+       dcp::CPL cpl("", dcp::ContentKind::FEATURE, dcp::Standard::SMPTE);
        cpl.set_issue_date ("2020-08-28T13:35:06+02:00");
 
-       vector<dcp::ContentVersion> cv;
-       cv.push_back (dcp::ContentVersion("some-id", "version 1"));
-       cv.push_back (dcp::ContentVersion("another-id", "version 2"));
+       vector<dcp::ContentVersion> cv = {
+               dcp::ContentVersion("some-id", "version 1"),
+               dcp::ContentVersion("another-id", "version 2")
+       };;
        cpl.set_content_versions (cv);
 
        cpl.set_full_content_title_text ("full-content-title");
        cpl.set_full_content_title_text_language (dcp::LanguageTag("de"));
        cpl.set_release_territory (dcp::LanguageTag::RegionSubtag("ES"));
        cpl.set_version_number (2);
-       cpl.set_status (dcp::FINAL);
+       cpl.set_status (dcp::Status::FINAL);
        cpl.set_chain ("the-chain");
        cpl.set_distributor ("the-distributor");
        cpl.set_facility ("the-facility");
-       cpl.set_luminance (dcp::Luminance(4.5, dcp::Luminance::FOOT_LAMBERT));
+       cpl.set_luminance (dcp::Luminance(4.5, dcp::Luminance::Unit::FOOT_LAMBERT));
        cpl.set_issuer ("libdcp1.6.4devel");
        cpl.set_creator ("libdcp1.6.4devel");
 
-       dcp::MainSoundConfiguration msc(dcp::SEVEN_POINT_ONE, 16);
-       msc.set_mapping (0, dcp::LEFT);
-       msc.set_mapping (1, dcp::RIGHT);
-       msc.set_mapping (2, dcp::CENTRE);
-       msc.set_mapping (3, dcp::LFE);
-       msc.set_mapping (13, dcp::SYNC_SIGNAL);
-       cpl.set_main_sound_configuration (msc.to_string());
+       dcp::MainSoundConfiguration msc(dcp::MCASoundField::SEVEN_POINT_ONE, 16);
+       msc.set_mapping (0, dcp::Channel::LEFT);
+       msc.set_mapping (1, dcp::Channel::RIGHT);
+       msc.set_mapping (2, dcp::Channel::CENTRE);
+       msc.set_mapping (3, dcp::Channel::LFE);
+       msc.set_mapping (13, dcp::Channel::SYNC_SIGNAL);
+       cpl.set_main_sound_configuration(msc);
 
        cpl.set_main_sound_sample_rate (48000);
        cpl.set_main_picture_stored_area (dcp::Size(1998, 1080));
        cpl.set_main_picture_active_area (dcp::Size(1440, 1080));
 
-       shared_ptr<cxml::Document> doc (new cxml::Document("MainSubtitle"));
+       auto doc = make_shared<cxml::Document>("MainSubtitle");
 
        doc->read_string (
                "<MainSubtitle>"
@@ -298,21 +325,21 @@ BOOST_AUTO_TEST_CASE (cpl_metadata_write_test1)
                "</MainSubtitle>"
                );
 
-       shared_ptr<dcp::Reel> reel(new dcp::Reel());
+       auto reel = make_shared<dcp::Reel>();
        reel->add (black_picture_asset("build/test/cpl_metadata_write_test1"));
-       reel->add (shared_ptr<dcp::ReelSubtitleAsset>(new dcp::ReelSubtitleAsset(doc)));
+       reel->add (make_shared<dcp::ReelSMPTESubtitleAsset>(doc));
        cpl.add (reel);
 
-       vector<dcp::LanguageTag> lt;
-       lt.push_back(dcp::LanguageTag("en-US"));
-       lt.push_back(dcp::LanguageTag("fr-ZA"));
+       auto lt = { dcp::LanguageTag("en-US"), dcp::LanguageTag("fr-ZA") };
        cpl.set_additional_subtitle_languages (lt);
 
-       cpl.write_xml ("build/test/cpl_metadata_write_test1.xml", dcp::SMPTE, shared_ptr<dcp::CertificateChain>());
+       cpl.set_sign_language_video_language (dcp::LanguageTag("bzs"));
+
+       cpl.write_xml ("build/test/cpl_metadata_write_test1.xml", {});
        check_xml (
                dcp::file_to_string("test/ref/cpl_metadata_test1.xml"),
                dcp::file_to_string("build/test/cpl_metadata_write_test1.xml"),
-               list<string>()
+               {"Id", "Hash"}
                );
 }
 
@@ -321,13 +348,11 @@ BOOST_AUTO_TEST_CASE (cpl_metadata_write_test1)
 BOOST_AUTO_TEST_CASE (cpl_metadata_roundtrip_test_1)
 {
        dcp::CPL cpl ("test/ref/cpl_metadata_test1.xml");
-       cpl.write_xml ("build/test/cpl_metadata_roundtrip_test1.xml", dcp::SMPTE, shared_ptr<dcp::CertificateChain>());
-       list<string> ignore;
-       ignore.push_back ("Id");
+       cpl.write_xml ("build/test/cpl_metadata_roundtrip_test1.xml", shared_ptr<dcp::CertificateChain>());
        check_xml (
                dcp::file_to_string("test/ref/cpl_metadata_test1.xml"),
                dcp::file_to_string("build/test/cpl_metadata_roundtrip_test1.xml"),
-               ignore
+               {"Id"}
                );
 }
 
@@ -337,33 +362,33 @@ BOOST_AUTO_TEST_CASE (cpl_metadata_write_test2)
 {
        RNGFixer fix;
 
-       dcp::CPL cpl("", dcp::FEATURE);
+       dcp::CPL cpl("", dcp::ContentKind::FEATURE, dcp::Standard::SMPTE);
        cpl.set_issue_date ("2020-08-28T13:35:06+02:00");
        cpl.set_content_version (dcp::ContentVersion("id", "version"));
        cpl.set_issuer ("libdcp1.6.4devel");
        cpl.set_creator ("libdcp1.6.4devel");
 
-       dcp::MainSoundConfiguration msc(dcp::SEVEN_POINT_ONE, 16);
-       msc.set_mapping (0, dcp::LEFT);
-       msc.set_mapping (1, dcp::RIGHT);
-       msc.set_mapping (2, dcp::CENTRE);
-       msc.set_mapping (3, dcp::LFE);
-       msc.set_mapping (13, dcp::SYNC_SIGNAL);
-       cpl.set_main_sound_configuration (msc.to_string());
+       dcp::MainSoundConfiguration msc(dcp::MCASoundField::SEVEN_POINT_ONE, 16);
+       msc.set_mapping (0, dcp::Channel::LEFT);
+       msc.set_mapping (1, dcp::Channel::RIGHT);
+       msc.set_mapping (2, dcp::Channel::CENTRE);
+       msc.set_mapping (3, dcp::Channel::LFE);
+       msc.set_mapping (13, dcp::Channel::SYNC_SIGNAL);
+       cpl.set_main_sound_configuration(msc);
 
        cpl.set_main_sound_sample_rate (48000);
        cpl.set_main_picture_stored_area (dcp::Size(1998, 1080));
        cpl.set_main_picture_active_area (dcp::Size(1440, 1080));
 
-       shared_ptr<dcp::Reel> reel(new dcp::Reel());
+       auto reel = make_shared<dcp::Reel>();
        reel->add (black_picture_asset("build/test/cpl_metadata_write_test1"));
        cpl.add (reel);
 
-       cpl.write_xml ("build/test/cpl_metadata_write_test2.xml", dcp::SMPTE, shared_ptr<dcp::CertificateChain>());
+       cpl.write_xml ("build/test/cpl_metadata_write_test2.xml", {});
        check_xml (
                dcp::file_to_string("test/ref/cpl_metadata_test2.xml"),
                dcp::file_to_string("build/test/cpl_metadata_write_test2.xml"),
-               list<string>()
+               {"Id", "Hash"}
                );
 }
 
@@ -384,10 +409,10 @@ BOOST_AUTO_TEST_CASE (cpl_metadata_read_test2)
        BOOST_CHECK (!cpl.luminance());
 
        dcp::MainSoundConfiguration msc(cpl.main_sound_configuration().get());
-       BOOST_CHECK_EQUAL (msc.mapping(0).get(), dcp::LEFT);
-       BOOST_CHECK_EQUAL (msc.mapping(1).get(), dcp::RIGHT);
-       BOOST_CHECK_EQUAL (msc.mapping(2).get(), dcp::CENTRE);
-       BOOST_CHECK_EQUAL (msc.mapping(3).get(), dcp::LFE);
+       BOOST_CHECK_EQUAL (msc.mapping(0).get(), dcp::Channel::LEFT);
+       BOOST_CHECK_EQUAL (msc.mapping(1).get(), dcp::Channel::RIGHT);
+       BOOST_CHECK_EQUAL (msc.mapping(2).get(), dcp::Channel::CENTRE);
+       BOOST_CHECK_EQUAL (msc.mapping(3).get(), dcp::Channel::LFE);
        BOOST_CHECK (!msc.mapping(4));
        BOOST_CHECK (!msc.mapping(5));
        BOOST_CHECK (!msc.mapping(6));
@@ -397,14 +422,14 @@ BOOST_AUTO_TEST_CASE (cpl_metadata_read_test2)
        BOOST_CHECK (!msc.mapping(10));
        BOOST_CHECK (!msc.mapping(11));
        BOOST_CHECK (!msc.mapping(12));
-       BOOST_CHECK_EQUAL (msc.mapping(13).get(), dcp::SYNC_SIGNAL);
+       BOOST_CHECK_EQUAL (msc.mapping(13).get(), dcp::Channel::SYNC_SIGNAL);
 
        BOOST_CHECK_EQUAL (cpl.main_sound_sample_rate().get(), 48000);
        BOOST_CHECK (cpl.main_picture_stored_area().get() == dcp::Size(1998, 1080));
        BOOST_CHECK (cpl.main_picture_active_area().get() == dcp::Size(1440, 1080));
 
-       list<shared_ptr<dcp::Reel> > reels = cpl.reels ();
-       BOOST_REQUIRE_EQUAL (reels.size(), 1);
+       auto reels = cpl.reels ();
+       BOOST_REQUIRE_EQUAL (reels.size(), 1U);
 }
 
 
@@ -412,13 +437,50 @@ BOOST_AUTO_TEST_CASE (cpl_metadata_read_test2)
 BOOST_AUTO_TEST_CASE (cpl_metadata_roundtrip_test_2)
 {
        dcp::CPL cpl ("test/ref/cpl_metadata_test2.xml");
-       cpl.write_xml ("build/test/cpl_metadata_roundtrip_test2.xml", dcp::SMPTE, shared_ptr<dcp::CertificateChain>());
-       list<string> ignore;
-       ignore.push_back ("Id");
+       cpl.write_xml ("build/test/cpl_metadata_roundtrip_test2.xml", shared_ptr<dcp::CertificateChain>());
        check_xml (
                dcp::file_to_string("test/ref/cpl_metadata_test2.xml"),
                dcp::file_to_string("build/test/cpl_metadata_roundtrip_test2.xml"),
-               ignore
+               {"Id"}
+               );
+}
+
+
+BOOST_AUTO_TEST_CASE(check_that_missing_full_content_title_text_is_tolerated)
+{
+       dcp::CPL cpl("test/ref/cpl_metadata_test3.xml");
+}
+
+
+BOOST_AUTO_TEST_CASE(check_sign_language_video_language)
+{
+       dcp::CPL cpl("test/ref/cpl_metadata_test3.xml");
+       cpl.set_sign_language_video_language(dcp::LanguageTag("es-PT"));
+       cpl.write_xml("build/test/check_sign_language_video_language.xml", {});
+       check_xml(
+               dcp::file_to_string("test/ref/cpl_metadata_test4.xml"),
+               dcp::file_to_string("build/test/check_sign_language_video_language.xml"),
+               {"Id"}
+               );
+
+       dcp::CPL check("build/test/check_sign_language_video_language.xml");
+       BOOST_CHECK_EQUAL(check.sign_language_video_language().get_value_or(""), "es-PT");
+
+}
+
+
+BOOST_AUTO_TEST_CASE(check_dolby_edr_metadata)
+{
+       dcp::CPL cpl("test/ref/cpl_metadata_test3.xml");
+       cpl.set_dolby_edr_image_transfer_function("PQ10K");
+       cpl.write_xml("build/test/check_dolby_edr_metadata.xml", {});
+       check_xml(
+               dcp::file_to_string("test/ref/cpl_metadata_test5.xml"),
+               dcp::file_to_string("build/test/check_dolby_edr_metadata.xml"),
+               {"Id"}
                );
+
+       dcp::CPL check("build/test/check_dolby_edr_metadata.xml");
+       BOOST_CHECK_EQUAL(check.dolby_edr_image_transfer_function().get_value_or(""), "PQ10K");
 }