/*
- Copyright (C) 2020 Carl Hetherington <cth@carlh.net>
+ Copyright (C) 2020-2021 Carl Hetherington <cth@carlh.net>
This file is part of libdcp.
#include "combine.h"
#include "cpl.h"
#include "dcp.h"
+#include "equality_options.h"
#include "interop_subtitle_asset.h"
#include "reel_subtitle_asset.h"
#include "reel_mono_picture_asset.h"
#include "test.h"
#include "types.h"
#include "verify.h"
+#include "reel_interop_subtitle_asset.h"
+#include "reel_markers_asset.h"
#include <boost/algorithm/string.hpp>
-#include <boost/foreach.hpp>
#include <boost/optional.hpp>
#include <boost/test/unit_test.hpp>
#include <iostream>
using std::list;
using std::string;
+using std::make_shared;
using std::vector;
-using boost::optional;
using std::shared_ptr;
+using boost::optional;
static void
void
dump_notes (vector<dcp::VerificationNote> const & notes)
{
- BOOST_FOREACH (dcp::VerificationNote i, notes) {
+ for (auto i: notes) {
std::cout << dcp::note_to_string(i) << "\n";
}
}
{
vector<boost::filesystem::path> directories;
directories.push_back (path);
- auto notes = dcp::verify (directories, &stage, &progress, xsd_test);
+ auto notes = dcp::verify(directories, {}, &stage, &progress, {}, xsd_test);
vector<dcp::VerificationNote> filtered_notes;
std::copy_if (notes.begin(), notes.end(), std::back_inserter(filtered_notes), [](dcp::VerificationNote const& i) {
- return i.code() != dcp::VerificationNote::NOT_SMPTE;
+ return i.code() != dcp::VerificationNote::Code::INVALID_STANDARD && i.code() != dcp::VerificationNote::Code::INVALID_SUBTITLE_DURATION;
});
dump_notes (filtered_notes);
BOOST_CHECK (filtered_notes.empty());
template <class T>
shared_ptr<T>
-pointer_to_id_in_vector (shared_ptr<T> needle, vector<shared_ptr<T> > haystack)
+pointer_to_id_in_vector (shared_ptr<T> needle, vector<shared_ptr<T>> haystack)
{
- BOOST_FOREACH (shared_ptr<T> i, haystack) {
+ for (auto i: haystack) {
if (i->id() == needle->id()) {
return i;
}
dcp::EqualityOptions options;
options.load_font_nodes_can_differ = true;
- BOOST_FOREACH (boost::filesystem::path i, inputs)
- {
+ for (auto i: inputs) {
dcp::DCP input_dcp (i);
input_dcp.read ();
BOOST_REQUIRE (input_dcp.cpls().size() == 1);
- shared_ptr<dcp::CPL> input_cpl = input_dcp.cpls().front();
+ auto input_cpl = input_dcp.cpls().front();
- shared_ptr<dcp::CPL> output_cpl = pointer_to_id_in_vector (input_cpl, output_dcp.cpls());
+ auto output_cpl = pointer_to_id_in_vector (input_cpl, output_dcp.cpls());
BOOST_REQUIRE (output_cpl);
- BOOST_FOREACH (shared_ptr<dcp::Asset> i, input_dcp.assets(true)) {
- shared_ptr<dcp::Asset> o = pointer_to_id_in_vector(i, output_dcp.assets());
+ for (auto i: input_dcp.assets(true)) {
+ auto o = pointer_to_id_in_vector(i, output_dcp.assets());
BOOST_REQUIRE_MESSAGE (o, "Could not find " << i->id() << " in combined DCP.");
BOOST_CHECK (i->equals(o, options, note_handler));
}
remove_all (out);
vector<path> inputs;
inputs.push_back ("test/ref/DCP/dcp_test1");
- dcp::combine (inputs, out);
+ dcp::combine (
+ inputs,
+ out,
+ dcp::String::compose("libdcp %1", dcp::version),
+ dcp::String::compose("libdcp %1", dcp::version),
+ dcp::LocalTime().as_string(),
+ "A Test DCP"
+ );
check_no_errors (out);
check_combined (inputs, out);
using namespace boost::filesystem;
boost::filesystem::path const out = "build/test/combine_two_dcps_with_same_asset_filenames_test";
- shared_ptr<dcp::DCP> second = make_simple ("build/test/combine_input2");
- second->write_xml (dcp::SMPTE);
+ auto second = make_simple ("build/test/combine_input2");
+ second->write_xml ();
remove_all (out);
vector<path> inputs;
}
+BOOST_AUTO_TEST_CASE(combine_two_dcps_one_with_interop_subs_test)
+{
+ using namespace boost::algorithm;
+ using namespace boost::filesystem;
+ boost::filesystem::path const out = "build/test/combine_two_dcps_one_with_interop_subs_test";
+
+ auto first = make_simple("build/test/combine_input1", 1, 24, dcp::Standard::INTEROP);
+ first->write_xml ();
+
+ auto second = make_simple_with_interop_subs("build/test/combine_input2");
+ second->write_xml ();
+
+ remove_all (out);
+ vector<path> inputs = {"build/test/combine_input1", "build/test/combine_input2"};
+ dcp::combine (inputs, out);
+
+ check_no_errors (out);
+ check_combined (inputs, out);
+}
+
+
BOOST_AUTO_TEST_CASE (combine_two_dcps_with_interop_subs_test)
{
using namespace boost::algorithm;
using namespace boost::filesystem;
boost::filesystem::path const out = "build/test/combine_two_dcps_with_interop_subs_test";
- shared_ptr<dcp::DCP> first = make_simple_with_interop_subs ("build/test/combine_input1");
- first->write_xml (dcp::INTEROP);
+ auto first = make_simple_with_interop_subs ("build/test/combine_input1");
+ first->write_xml ();
- shared_ptr<dcp::DCP> second = make_simple_with_interop_subs ("build/test/combine_input2");
- second->write_xml (dcp::INTEROP);
+ auto second = make_simple_with_interop_subs ("build/test/combine_input2");
+ second->write_xml ();
remove_all (out);
- vector<path> inputs;
- inputs.push_back ("build/test/combine_input1");
- inputs.push_back ("build/test/combine_input2");
+ vector<path> inputs = {"build/test/combine_input1", "build/test/combine_input2"};
dcp::combine (inputs, out);
check_no_errors (out);
using namespace boost::filesystem;
boost::filesystem::path const out = "build/test/combine_two_dcps_with_smpte_subs_test";
- shared_ptr<dcp::DCP> first = make_simple_with_smpte_subs ("build/test/combine_input1");
- first->write_xml (dcp::SMPTE);
+ auto first = make_simple_with_smpte_subs ("build/test/combine_input1");
+ first->write_xml ();
- shared_ptr<dcp::DCP> second = make_simple_with_smpte_subs ("build/test/combine_input2");
- second->write_xml (dcp::SMPTE);
+ auto second = make_simple_with_smpte_subs ("build/test/combine_input2");
+ second->write_xml ();
remove_all (out);
vector<path> inputs;
using namespace boost::filesystem;
boost::filesystem::path const out = "build/test/combine_two_dcps_with_interop_ccaps_test";
- shared_ptr<dcp::DCP> first = make_simple_with_interop_ccaps ("build/test/combine_input1");
- first->write_xml (dcp::INTEROP);
+ auto first = make_simple_with_interop_ccaps ("build/test/combine_input1");
+ first->write_xml ();
- shared_ptr<dcp::DCP> second = make_simple_with_interop_ccaps ("build/test/combine_input2");
- second->write_xml (dcp::INTEROP);
+ auto second = make_simple_with_interop_ccaps ("build/test/combine_input2");
+ second->write_xml ();
remove_all (out);
vector<path> inputs;
using namespace boost::filesystem;
boost::filesystem::path const out = "build/test/combine_two_dcps_with_interop_ccaps_test";
- shared_ptr<dcp::DCP> first = make_simple_with_smpte_ccaps ("build/test/combine_input1");
- first->write_xml (dcp::SMPTE);
+ auto first = make_simple_with_smpte_ccaps ("build/test/combine_input1");
+ first->write_xml ();
- shared_ptr<dcp::DCP> second = make_simple_with_smpte_ccaps ("build/test/combine_input2");
- second->write_xml (dcp::SMPTE);
+ auto second = make_simple_with_smpte_ccaps ("build/test/combine_input2");
+ second->write_xml ();
remove_all (out);
vector<path> inputs;
using namespace boost::filesystem;
boost::filesystem::path const out = "build/test/combine_two_multi_reel_dcps";
- shared_ptr<dcp::DCP> first = make_simple ("build/test/combine_input1", 4);
- first->write_xml (dcp::SMPTE);
+ auto first = make_simple ("build/test/combine_input1", 4);
+ first->write_xml ();
- shared_ptr<dcp::DCP> second = make_simple ("build/test/combine_input2", 4);
- second->write_xml (dcp::SMPTE);
+ auto second = make_simple ("build/test/combine_input2", 4);
+ second->write_xml ();
remove_all (out);
vector<path> inputs;
using namespace boost::filesystem;
boost::filesystem::path const out = "build/test/combine_two_dcps_with_shared_asset";
- shared_ptr<dcp::DCP> first = make_simple ("build/test/combine_input1", 1);
- first->write_xml (dcp::SMPTE);
+ auto first = make_simple ("build/test/combine_input1", 1);
+ first->write_xml ();
remove_all ("build/test/combine_input2");
- shared_ptr<dcp::DCP> second(new dcp::DCP("build/test/combine_input2"));
+ auto second = make_shared<dcp::DCP>("build/test/combine_input2");
dcp::MXFMetadata mxf_meta;
mxf_meta.company_name = "OpenDCP";
mxf_meta.product_version = "0.0.25";
- shared_ptr<dcp::CPL> cpl (new dcp::CPL("A Test DCP", dcp::FEATURE));
+ auto cpl = make_shared<dcp::CPL>("A Test DCP", dcp::ContentKind::TRAILER, dcp::Standard::SMPTE);
cpl->set_content_version (
dcp::ContentVersion("urn:uuid:75ac29aa-42ac-1234-ecae-49251abefd11","content-version-label-text")
);
+ cpl->set_main_sound_configuration(dcp::MainSoundConfiguration("51/L,C,R,LFE,-,-"));
+ 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));
+ cpl->set_version_number(1);
+
+ auto pic = make_shared<dcp::ReelMonoPictureAsset>(simple_picture("build/test/combine_input2", ""), 0);
+ auto sound = make_shared<dcp::ReelSoundAsset>(first->cpls().front()->reels().front()->main_sound()->asset(), 0);
+ auto reel = make_shared<dcp::Reel>(pic, sound);
+ reel->add (simple_markers());
+ cpl->add (reel);
+ second->add (cpl);
+ second->write_xml ();
+
+ remove_all (out);
+ vector<path> inputs;
+ inputs.push_back ("build/test/combine_input1");
+ inputs.push_back ("build/test/combine_input2");
+ dcp::combine (inputs, out);
+
+ check_no_errors (out);
+ check_combined (inputs, out);
+}
+
+
+/** Two DCPs each with a copy of the exact same asset */
+BOOST_AUTO_TEST_CASE (combine_two_dcps_with_duplicated_asset)
+{
+ using namespace boost::filesystem;
+ boost::filesystem::path const out = "build/test/combine_two_dcps_with_duplicated_asset";
+
+ auto first = make_simple ("build/test/combine_input1", 1);
+ first->write_xml ();
- shared_ptr<dcp::ReelMonoPictureAsset> pic(new dcp::ReelMonoPictureAsset(simple_picture("build/test/combine_input2", ""), 0));
- shared_ptr<dcp::ReelSoundAsset> sound(new dcp::ReelSoundAsset(first->cpls().front()->reels().front()->main_sound()->asset(), 0));
- cpl->add (shared_ptr<dcp::Reel>(new dcp::Reel(pic, sound)));
+ remove_all ("build/test/combine_input2");
+ auto second = make_shared<dcp::DCP>("build/test/combine_input2");
+
+ dcp::MXFMetadata mxf_meta;
+ mxf_meta.company_name = "OpenDCP";
+ mxf_meta.product_version = "0.0.25";
+
+ auto cpl = make_shared<dcp::CPL>("A Test DCP", dcp::ContentKind::TRAILER, dcp::Standard::SMPTE);
+ cpl->set_content_version (
+ dcp::ContentVersion("urn:uuid:75ac29aa-42ac-1234-ecae-49251abefd11","content-version-label-text")
+ );
+ cpl->set_main_sound_configuration(dcp::MainSoundConfiguration("51/L,C,R,LFE,-,-"));
+ 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));
+ cpl->set_version_number(1);
+
+ auto pic = make_shared<dcp::ReelMonoPictureAsset>(simple_picture("build/test/combine_input2", ""), 0);
+ auto first_sound_asset = first->cpls()[0]->reels()[0]->main_sound()->asset()->file();
+ BOOST_REQUIRE (first_sound_asset);
+ boost::filesystem::path second_sound_asset = "build/test/combine_input2/my_great_audio.mxf";
+ boost::filesystem::copy_file (*first_sound_asset, second_sound_asset);
+ auto sound = make_shared<dcp::ReelSoundAsset>(make_shared<dcp::SoundAsset>(second_sound_asset), 0);
+ auto reel = make_shared<dcp::Reel>(pic, sound);
+ reel->add (simple_markers());
+ cpl->add (reel);
second->add (cpl);
- second->write_xml (dcp::SMPTE);
+ second->write_xml ();
remove_all (out);
vector<path> inputs;
check_no_errors (out);
check_combined (inputs, out);
+
+ BOOST_REQUIRE (!boost::filesystem::exists(out / "my_great_audio.mxf"));
+}
+
+
+BOOST_AUTO_TEST_CASE (check_cpls_unchanged_after_combine)
+{
+ boost::filesystem::path in = "build/test/combine_one_dcp_with_composition_metadata_in";
+ boost::filesystem::path out = "build/test/combine_one_dcp_with_composition_metadata_out";
+ auto dcp = make_simple (in);
+ dcp->write_xml ();
+
+ dcp::combine ({in}, out);
+
+ BOOST_REQUIRE_EQUAL (dcp->cpls().size(), 1U);
+ auto cpl = dcp->cpls()[0]->file();
+ BOOST_REQUIRE (cpl);
+ check_file (*cpl, out / cpl->filename());
+}
+
+
+/** The combine process would write multiple fonts with the same ID (#2402) */
+BOOST_AUTO_TEST_CASE(combine_multi_reel_subtitles)
+{
+ boost::filesystem::path in = "build/test/combine_multi_reel_subtitles_in";
+ boost::filesystem::path out = "build/test/combine_multi_reel_subtitles_out";
+ remove_all(out);
+
+ auto dcp = make_simple(in, 2, 24, dcp::Standard::INTEROP);
+
+ dcp::ArrayData data1(4096);
+ memset(data1.data(), 0, data1.size());
+
+ auto subs1 = make_shared<dcp::InteropSubtitleAsset>();
+ subs1->add(simple_subtitle());
+ boost::filesystem::create_directory(in / "subs1");
+ subs1->add_font("afont1", data1);
+ subs1->write(in / "subs1" / "subs1.xml");
+
+ dcp::ArrayData data2(4096);
+ memset(data2.data(), 1, data1.size());
+
+ auto subs2 = make_shared<dcp::InteropSubtitleAsset>();
+ subs2->add(simple_subtitle());
+ boost::filesystem::create_directory(in / "subs2");
+ subs2->add_font("afont2", data2);
+ subs2->write(in / "subs2" / "subs2.xml");
+
+ auto reel_subs1 = make_shared<dcp::ReelInteropSubtitleAsset>(subs1, dcp::Fraction(24, 1), 240, 0);
+ dcp->cpls()[0]->reels()[0]->add(reel_subs1);
+
+ auto reel_subs2 = make_shared<dcp::ReelInteropSubtitleAsset>(subs2, dcp::Fraction(24, 1), 240, 0);
+ dcp->cpls()[0]->reels()[1]->add(reel_subs2);
+
+ dcp->write_xml();
+
+ dcp::combine({in}, out);
+
+ check_combined({in}, out);
+
+ auto notes = dcp::verify({out}, {}, &stage, &progress, {}, xsd_test);
+ vector<dcp::VerificationNote> filtered_notes;
+ std::copy_if(notes.begin(), notes.end(), std::back_inserter(filtered_notes), [](dcp::VerificationNote const& i) {
+ return i.code() != dcp::VerificationNote::Code::INVALID_STANDARD && i.code() != dcp::VerificationNote::Code::MISMATCHED_PKL_ANNOTATION_TEXT_WITH_CPL;
+ });
+ dump_notes(filtered_notes);
+ BOOST_CHECK(filtered_notes.empty());
}