copy $ROOT src/dcpomatic/build/src/tools/dcpomatic2 "$approot/MacOS"
copy $ROOT src/dcpomatic/build/src/tools/dcpomatic2_cli "$approot/MacOS"
copy $ROOT src/dcpomatic/build/src/tools/dcpomatic2_create "$approot/MacOS"
+ copy $ROOT src/dcpomatic/build/src/tools/dcpomatic2_map "$approot/MacOS"
copy $ROOT bin/ffprobe "$approot/MacOS"
copy $ROOT src/openssl/apps/openssl "$approot/MacOS"
copy_verify
('playlist', 'Playlist Editor'),
('combiner', 'Combiner'),
('editor', 'Editor'),
+ ('map', 'Map'),
]
if disk:
--- /dev/null
+#!/bin/bash
+
+DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
+source $DIR/environment
+
+if [ "$1" == "--debug" ]; then
+ shift
+ gdb --args build/src/tools/dcpomatic2_map "$@"
+elif [ "$1" == "--valgrind" ]; then
+ shift
+ valgrind --tool="memcheck" --leak-check=full --show-reachable=yes build/src/tools/dcpomatic2_map "$@"
+else
+ build/src/tools/dcpomatic2_map "$@"
+fi
--- /dev/null
+/*
+ Copyright (C) 2023 Carl Hetherington <cth@carlh.net>
+
+ This file is part of DCP-o-matic.
+
+ DCP-o-matic is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ DCP-o-matic is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with DCP-o-matic. If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+
+#include "compose.hpp"
+#include "config.h"
+#include "util.h"
+#include <dcp/cpl.h>
+#include <dcp/dcp.h>
+#include <dcp/interop_subtitle_asset.h>
+#include <dcp/font_asset.h>
+#include <dcp/mono_picture_asset.h>
+#include <dcp/reel.h>
+#include <dcp/reel_atmos_asset.h>
+#include <dcp/reel_closed_caption_asset.h>
+#include <dcp/reel_file_asset.h>
+#include <dcp/reel_picture_asset.h>
+#include <dcp/reel_sound_asset.h>
+#include <dcp/reel_subtitle_asset.h>
+#include <dcp/smpte_subtitle_asset.h>
+#include <dcp/sound_asset.h>
+#include <dcp/stereo_picture_asset.h>
+#include <boost/optional.hpp>
+#include <getopt.h>
+#include <algorithm>
+#include <memory>
+#include <string>
+
+
+using std::dynamic_pointer_cast;
+using std::shared_ptr;
+using std::string;
+using std::make_shared;
+using std::vector;
+using boost::optional;
+
+
+static void
+help(std::function<void (string)> out)
+{
+ out(String::compose("Syntax: %1 [OPTION} <cpl-file> [<cpl-file> ... ]", program_name));
+ out(" -V, --version show libdcp version");
+ out(" -h, --help show this help");
+ out(" -o, --output output directory");
+ out(" -l, --hard-link using hard links instead of copying");
+ out(" -s, --soft-link using soft links instead of copying");
+ out(" -d, --assets-dir look in this directory for assets (can be given more than once)");
+ out(" -r, --rename rename all files to <uuid>.<mxf|xml>");
+}
+
+
+optional<string>
+map_cli(int argc, char* argv[], std::function<void (string)> out)
+{
+ optional<boost::filesystem::path> output_dir;
+ bool hard_link = false;
+ bool soft_link = false;
+ bool rename = false;
+ vector<boost::filesystem::path> assets_dir;
+
+ /* This makes it possible to call getopt several times in the same executable, for tests */
+ optind = 0;
+
+ int option_index = 0;
+ while (true) {
+ static struct option long_options[] = {
+ { "help", no_argument, 0, 'h' },
+ { "output", required_argument, 0, 'o' },
+ { "hard-link", no_argument, 0, 'l' },
+ { "soft-link", no_argument, 0, 's' },
+ { "assets-dir", required_argument, 0, 'd' },
+ { "rename", no_argument, 0, 'r' },
+ { 0, 0, 0, 0 }
+ };
+
+ int c = getopt_long(argc, argv, "ho:lsd:r", long_options, &option_index);
+
+ if (c == -1) {
+ break;
+ } else if (c == '?' || c == ':') {
+ exit(EXIT_FAILURE);
+ }
+
+ switch (c) {
+ case 'h':
+ help(out);
+ exit(EXIT_SUCCESS);
+ case 'o':
+ output_dir = optarg;
+ break;
+ case 'l':
+ hard_link = true;
+ break;
+ case 's':
+ soft_link = true;
+ break;
+ case 'd':
+ assets_dir.push_back(optarg);
+ break;
+ case 'r':
+ rename = true;
+ break;
+ }
+ }
+
+ program_name = argv[0];
+
+ if (argc <= optind) {
+ help(out);
+ exit(EXIT_FAILURE);
+ }
+
+ vector<boost::filesystem::path> cpl_filenames;
+ for (int i = optind; i < argc; ++i) {
+ cpl_filenames.push_back(argv[i]);
+ }
+
+ if (cpl_filenames.empty()) {
+ return string{"No CPL specified."};
+ }
+
+ if (!output_dir) {
+ return string{"Missing -o or --output"};
+ }
+
+ if (boost::filesystem::exists(*output_dir)) {
+ return String::compose("Output directory %1 already exists.", *output_dir);
+ }
+
+ if (hard_link && soft_link) {
+ return string{"Specify either -s,--soft-link or -l,--hard-link, not both."};
+ }
+
+ boost::system::error_code ec;
+ boost::filesystem::create_directory(*output_dir, ec);
+ if (ec) {
+ return String::compose("Could not create output directory %1: %2", *output_dir, ec.message());
+ }
+
+ /* Find all the assets in the asset directories. This assumes that the asset directories are in fact
+ * DCPs (with AssetMaps and so on). We could search for assets ourselves here but interop fonts are
+ * a little tricky because they don't contain their own UUID within the DCP.
+ */
+ vector<shared_ptr<dcp::Asset>> assets;
+ for (auto dir: assets_dir) {
+ dcp::DCP dcp(dir);
+ dcp.read();
+ auto dcp_assets = dcp.assets(true);
+ std::copy(dcp_assets.begin(), dcp_assets.end(), back_inserter(assets));
+ }
+
+ dcp::DCP dcp(*output_dir);
+
+ /* Find all the CPLs */
+ vector<shared_ptr<dcp::CPL>> cpls;
+ for (auto filename: cpl_filenames) {
+ try {
+ auto cpl = make_shared<dcp::CPL>(filename);
+ cpl->resolve_refs(assets);
+ cpls.push_back(cpl);
+ } catch (std::exception& e) {
+ return String::compose("Could not read CPL %1: %2", filename, e.what());
+ }
+ }
+
+ class CopyError : public std::runtime_error
+ {
+ public:
+ CopyError(std::string message) : std::runtime_error(message) {}
+ };
+
+ auto maybe_copy = [&assets, output_dir](
+ string asset_id,
+ bool rename,
+ bool hard_link,
+ bool soft_link,
+ boost::optional<boost::filesystem::path> extra = boost::none
+ ) {
+ auto iter = std::find_if(assets.begin(), assets.end(), [asset_id](shared_ptr<const dcp::Asset> a) { return a->id() == asset_id; });
+ if (iter != assets.end()) {
+ DCP_ASSERT((*iter)->file());
+
+ auto const input_path = (*iter)->file().get();
+ boost::filesystem::path output_path = *output_dir;
+ if (extra) {
+ output_path /= *extra;
+ }
+
+ if (rename) {
+ output_path /= String::compose("%1%2", (*iter)->id(), boost::filesystem::extension((*iter)->file().get()));
+ (*iter)->rename_file(output_path);
+ } else {
+ output_path /= (*iter)->file()->filename();
+ }
+
+ boost::filesystem::create_directories(output_path.parent_path());
+
+ boost::system::error_code ec;
+ if (hard_link) {
+ boost::filesystem::create_hard_link(input_path, output_path, ec);
+ if (ec) {
+ throw CopyError(String::compose("Could not hard-link asset %1: %2", input_path.string(), ec.message()));
+ }
+ } else if (soft_link) {
+ boost::filesystem::create_symlink(input_path, output_path, ec);
+ if (ec) {
+ throw CopyError(String::compose("Could not soft-link asset %1: %2", input_path.string(), ec.message()));
+ }
+ } else {
+ boost::filesystem::copy_file(input_path, output_path, ec);
+ if (ec) {
+ throw CopyError(String::compose("Could not copy asset %1: %2", input_path.string(), ec.message()));
+ }
+ }
+ (*iter)->set_file(output_path);
+ } else {
+ boost::system::error_code ec;
+ boost::filesystem::remove_all(*output_dir, ec);
+ throw CopyError(String::compose("Could not find required asset %1", asset_id));
+ }
+ };
+
+ auto maybe_copy_from_reel = [output_dir, &maybe_copy](
+ shared_ptr<dcp::ReelFileAsset> asset,
+ bool rename,
+ bool hard_link,
+ bool soft_link,
+ boost::optional<boost::filesystem::path> extra = boost::none
+ ) {
+ if (asset && asset->asset_ref().resolved()) {
+ maybe_copy(asset->asset_ref().id(), rename, hard_link, soft_link, extra);
+ }
+ };
+
+ /* Copy assets that the CPLs need */
+ try {
+ for (auto cpl: cpls) {
+ for (auto reel: cpl->reels()) {
+ maybe_copy_from_reel(reel->main_picture(), rename, hard_link, soft_link);
+ maybe_copy_from_reel(reel->main_sound(), rename, hard_link, soft_link);
+ boost::optional<boost::filesystem::path> extra;
+ if (reel->main_subtitle()) {
+ auto interop = dynamic_pointer_cast<dcp::InteropSubtitleAsset>(reel->main_subtitle()->asset());
+ if (interop) {
+ extra = interop->id();
+ for (auto font_asset: interop->font_assets()) {
+ maybe_copy(font_asset->id(), rename, hard_link, soft_link, extra);
+ }
+ }
+ }
+ maybe_copy_from_reel(reel->main_subtitle(), rename, hard_link, soft_link, extra);
+ for (auto ccap: reel->closed_captions()) {
+ maybe_copy_from_reel(ccap, rename, hard_link, soft_link);
+ }
+ maybe_copy_from_reel(reel->atmos(), rename, hard_link, soft_link);
+ }
+
+ dcp.add(cpl);
+ }
+ } catch (CopyError& e) {
+ return string{e.what()};
+ }
+
+ dcp.resolve_refs(assets);
+ dcp.set_annotation_text(cpls[0]->annotation_text().get_value_or(""));
+ dcp.write_xml(Config::instance()->signer_chain());
+
+ return {};
+}
+
--- /dev/null
+/*
+ Copyright (C) 2023 Carl Hetherington <cth@carlh.net>
+
+ This file is part of DCP-o-matic.
+
+ DCP-o-matic is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ DCP-o-matic is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with DCP-o-matic. If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+
+#include <boost/optional.hpp>
+#include <string>
+
+
+extern boost::optional<std::string> map_cli(int argc, char* argv[], std::function<void (std::string)> out);
+
log.cc
log_entry.cc
make_dcp.cc
+ map_cli.cc
maths_util.cc
memory_util.cc
mid_side_decoder.cc
--- /dev/null
+/*
+ Copyright (C) 2023 Carl Hetherington <cth@carlh.net>
+
+ This file is part of DCP-o-matic.
+
+ DCP-o-matic is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ DCP-o-matic is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with DCP-o-matic. If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+
+/** @file src/tools/dcpomatic_map.cc
+ * @brief Command-line program to assemble new DCPs from CPLs and assets.
+ */
+
+
+#include "lib/cross.h"
+#include "lib/map_cli.h"
+#include "lib/util.h"
+#include <iostream>
+
+
+int
+main(int argc, char* argv[])
+{
+ ArgFixer fixer(argc, argv);
+
+ dcpomatic_setup_path_encoding();
+ dcpomatic_setup();
+
+ auto error = map_cli(fixer.argc(), fixer.argv(), [](std::string s) { std::cout << s << "\n"; });
+ if (error) {
+ std::cerr << *error << "\n";
+ exit (EXIT_FAILURE);
+ }
+
+ return 0;
+}
+
if bld.env.TARGET_LINUX:
uselib += 'DL '
- cli_tools = ['dcpomatic_cli', 'dcpomatic_server_cli', 'server_test', 'dcpomatic_kdm_cli', 'dcpomatic_create']
+ cli_tools = ['dcpomatic_cli', 'dcpomatic_server_cli', 'server_test', 'dcpomatic_kdm_cli', 'dcpomatic_create', 'dcpomatic_map']
if bld.env.ENABLE_DISK and not bld.env.DISABLE_GUI:
cli_tools.append('dcpomatic_disk_writer')
--- /dev/null
+/*
+ Copyright (C) 2023 Carl Hetherington <cth@carlh.net>
+
+ This file is part of DCP-o-matic.
+
+ DCP-o-matic is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ DCP-o-matic is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with DCP-o-matic. If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+
+#include "lib/config.h"
+#include "lib/content.h"
+#include "lib/dcp_content.h"
+#include "lib/content_factory.h"
+#include "lib/film.h"
+#include "lib/map_cli.h"
+#include "test.h"
+#include <dcp/cpl.h>
+#include <dcp/dcp.h>
+#include <dcp/reel.h>
+#include <dcp/reel_picture_asset.h>
+#include <dcp/reel_sound_asset.h>
+#include <boost/algorithm/string.hpp>
+#include <boost/filesystem.hpp>
+#include <boost/optional.hpp>
+#include <boost/test/unit_test.hpp>
+
+
+using std::dynamic_pointer_cast;
+using std::make_shared;
+using std::shared_ptr;
+using std::string;
+using std::vector;
+using boost::optional;
+
+
+static
+optional<string>
+run(vector<string> const& args, vector<string>& output)
+{
+ vector<char*> argv(args.size() + 1);
+ for (auto i = 0U; i < args.size(); ++i) {
+ argv[i] = const_cast<char*>(args[i].c_str());
+ }
+ argv[args.size()] = nullptr;
+
+ auto error = map_cli(args.size(), argv.data(), [&output](string s) { output.push_back(s); });
+ if (error) {
+ std::cout << *error << "\n";
+ }
+
+ return error;
+}
+
+
+static
+boost::filesystem::path
+find_prefix(boost::filesystem::path dir, string prefix)
+{
+ auto iter = std::find_if(boost::filesystem::directory_iterator(dir), boost::filesystem::directory_iterator(), [prefix](boost::filesystem::path const& p) {
+ return boost::starts_with(p.filename().string(), prefix);
+ });
+
+ BOOST_REQUIRE(iter != boost::filesystem::directory_iterator());
+ return iter->path();
+}
+
+
+static
+boost::filesystem::path
+find_cpl(boost::filesystem::path dir)
+{
+ return find_prefix(dir, "cpl_");
+}
+
+
+/** Map a single DCP into a new DCP */
+BOOST_AUTO_TEST_CASE(map_simple_dcp_copy)
+{
+ string const name = "map_simple_dcp_copy";
+ string const out = String::compose("build/test/%1_out", name);
+
+ auto content = content_factory("test/data/flat_red.png");
+ auto film = new_test_film2(name + "_in", content);
+ make_and_verify_dcp(film);
+
+ vector<string> const args = {
+ "map_cli",
+ "-o", out,
+ "-d", film->dir(film->dcp_name()).string(),
+ find_cpl(film->dir(film->dcp_name())).string()
+ };
+
+ boost::filesystem::remove_all(out);
+
+ vector<string> output_messages;
+ auto error = run(args, output_messages);
+ BOOST_CHECK(!error);
+
+ verify_dcp(out, {});
+
+ BOOST_CHECK(boost::filesystem::is_regular_file(find_prefix(out, "j2c_")));
+ BOOST_CHECK(boost::filesystem::is_regular_file(find_prefix(out, "pcm_")));
+}
+
+
+/** Map a single DCP into a new DCP using the symlink option */
+BOOST_AUTO_TEST_CASE(map_simple_dcp_copy_with_symlinks)
+{
+ string const name = "map_simple_dcp_copy_with_symlinks";
+ string const out = String::compose("build/test/%1_out", name);
+
+ auto content = content_factory("test/data/flat_red.png");
+ auto film = new_test_film2(name + "_in", content);
+ make_and_verify_dcp(film);
+
+ vector<string> const args = {
+ "map_cli",
+ "-o", out,
+ "-d", film->dir(film->dcp_name()).string(),
+ "-s",
+ find_cpl(film->dir(film->dcp_name())).string()
+ };
+
+ boost::filesystem::remove_all(out);
+
+ vector<string> output_messages;
+ auto error = run(args, output_messages);
+ BOOST_CHECK(!error);
+
+ /* We can't verify this DCP because the symlinks will make it fail
+ * (as it should be, I think).
+ */
+
+ BOOST_CHECK(boost::filesystem::is_symlink(find_prefix(out, "j2c_")));
+ BOOST_CHECK(boost::filesystem::is_symlink(find_prefix(out, "pcm_")));
+}
+
+
+/** Map a single DCP into a new DCP using the hardlink option */
+BOOST_AUTO_TEST_CASE(map_simple_dcp_copy_with_hardlinks)
+{
+ string const name = "map_simple_dcp_copy_with_hardlinks";
+ string const out = String::compose("build/test/%1_out", name);
+
+ auto content = content_factory("test/data/flat_red.png");
+ auto film = new_test_film2(name + "_in", content);
+ make_and_verify_dcp(film);
+
+ vector<string> const args = {
+ "map_cli",
+ "-o", out,
+ "-d", film->dir(film->dcp_name()).string(),
+ "-l",
+ find_cpl(film->dir(film->dcp_name())).string()
+ };
+
+ boost::filesystem::remove_all(out);
+
+ vector<string> output_messages;
+ auto error = run(args, output_messages);
+ BOOST_CHECK(!error);
+
+ verify_dcp(out, {});
+
+ /* The video file will have 3 links because DoM also makes a link into the video directory */
+ BOOST_CHECK_EQUAL(boost::filesystem::hard_link_count(find_prefix(out, "j2c_")), 3U);
+ BOOST_CHECK_EQUAL(boost::filesystem::hard_link_count(find_prefix(out, "pcm_")), 2U);
+}
+
+
+/** Map a single Interop DCP with subs into a new DCP */
+BOOST_AUTO_TEST_CASE(map_simple_interop_dcp_with_subs)
+{
+ string const name = "map_simple_interop_dcp_with_subs";
+ string const out = String::compose("build/test/%1_out", name);
+
+ auto picture = content_factory("test/data/flat_red.png").front();
+ auto subs = content_factory("test/data/15s.srt").front();
+ auto film = new_test_film2(name + "_in", { picture, subs });
+ film->set_interop(true);
+ make_and_verify_dcp(film, {dcp::VerificationNote::Code::INVALID_STANDARD});
+
+ vector<string> const args = {
+ "map_cli",
+ "-o", out,
+ "-d", film->dir(film->dcp_name()).string(),
+ find_cpl(film->dir(film->dcp_name())).string()
+ };
+
+ boost::filesystem::remove_all(out);
+
+ vector<string> output_messages;
+ auto error = run(args, output_messages);
+ BOOST_CHECK(!error);
+
+ verify_dcp(out, {dcp::VerificationNote::Code::INVALID_STANDARD});
+}
+
+
+/** Map an OV and a VF into a single DCP */
+BOOST_AUTO_TEST_CASE(map_ov_vf_copy)
+{
+ string const name = "map_ov_vf_copy";
+ string const out = String::compose("build/test/%1_out", name);
+
+ auto ov_content = content_factory("test/data/flat_red.png");
+ auto ov_film = new_test_film2(name + "_ov", ov_content);
+ make_and_verify_dcp(ov_film);
+
+ auto const ov_dir = ov_film->dir(ov_film->dcp_name());
+
+ auto vf_ov = make_shared<DCPContent>(ov_dir);
+ auto vf_sound = content_factory("test/data/sine_440.wav").front();
+ auto vf_film = new_test_film2(name + "_vf", { vf_ov, vf_sound });
+ vf_ov->set_reference_video(true);
+ make_and_verify_dcp(vf_film, {dcp::VerificationNote::Code::EXTERNAL_ASSET});
+
+ auto const vf_dir = vf_film->dir(vf_film->dcp_name());
+
+ vector<string> const args = {
+ "map_cli",
+ "-o", out,
+ "-d", ov_dir.string(),
+ "-d", vf_dir.string(),
+ find_cpl(vf_dir).string()
+ };
+
+ boost::filesystem::remove_all(out);
+
+ vector<string> output_messages;
+ auto error = run(args, output_messages);
+ BOOST_CHECK(!error);
+
+ verify_dcp(out, {});
+
+ check_file(find_file(out, "cpl_"), find_file(vf_dir, "cpl_"));
+ check_file(find_file(out, "j2c_"), find_file(ov_dir, "j2c_"));
+ check_file(find_file(out, "pcm_"), find_file(vf_dir, "pcm_"));
+}
+
+
+/** Map a single DCP into a new DCP using the rename option */
+BOOST_AUTO_TEST_CASE(map_simple_dcp_copy_with_rename)
+{
+ ConfigRestorer cr;
+ Config::instance()->set_dcp_asset_filename_format(dcp::NameFormat("hello%c"));
+ string const name = "map_simple_dcp_copy_with_rename";
+ string const out = String::compose("build/test/%1_out", name);
+
+ auto content = content_factory("test/data/flat_red.png");
+ auto film = new_test_film2(name + "_in", content);
+ make_and_verify_dcp(film);
+
+ vector<string> const args = {
+ "map_cli",
+ "-o", out,
+ "-d", film->dir(film->dcp_name()).string(),
+ "-r",
+ find_cpl(film->dir(film->dcp_name())).string()
+ };
+
+ boost::filesystem::remove_all(out);
+
+ vector<string> output_messages;
+ auto error = run(args, output_messages);
+ BOOST_CHECK(!error);
+
+ verify_dcp(out, {});
+
+ dcp::DCP out_dcp(out);
+ out_dcp.read();
+
+ BOOST_REQUIRE_EQUAL(out_dcp.cpls().size(), 1U);
+ auto const cpl = out_dcp.cpls()[0];
+ BOOST_REQUIRE_EQUAL(cpl->reels().size(), 1U);
+ auto const reel = cpl->reels()[0];
+ BOOST_REQUIRE(reel->main_picture());
+ BOOST_REQUIRE(reel->main_sound());
+ auto const picture = reel->main_picture()->asset();
+ BOOST_REQUIRE(picture);
+ auto const sound = reel->main_sound()->asset();
+ BOOST_REQUIRE(sound);
+
+ BOOST_REQUIRE(picture->file());
+ BOOST_CHECK(picture->file().get().filename() == picture->id() + ".mxf");
+
+ BOOST_REQUIRE(sound->file());
+ BOOST_CHECK(sound->file().get().filename() == sound->id() + ".mxf");
+}
+
+
+static
+void
+test_two_cpls_each_with_subs(string name, bool interop)
+{
+ string const out = String::compose("build/test/%1_out", name);
+
+ vector<dcp::VerificationNote::Code> acceptable_errors;
+ if (interop) {
+ acceptable_errors.push_back(dcp::VerificationNote::Code::INVALID_STANDARD);
+ } else {
+ acceptable_errors.push_back(dcp::VerificationNote::Code::MISSING_SUBTITLE_LANGUAGE);
+ acceptable_errors.push_back(dcp::VerificationNote::Code::INVALID_SUBTITLE_FIRST_TEXT_TIME);
+ }
+
+ shared_ptr<Film> films[2];
+ for (auto i = 0; i < 2; ++i) {
+ auto picture = content_factory("test/data/flat_red.png").front();
+ auto subs = content_factory("test/data/15s.srt").front();
+ films[i] = new_test_film2(String::compose("%1_%2_in", name, i), { picture, subs });
+ films[i]->set_interop(interop);
+ make_and_verify_dcp(films[i], acceptable_errors);
+ }
+
+ vector<string> const args = {
+ "map_cli",
+ "-o", out,
+ "-d", films[0]->dir(films[0]->dcp_name()).string(),
+ "-d", films[1]->dir(films[1]->dcp_name()).string(),
+ find_cpl(films[0]->dir(films[0]->dcp_name())).string(),
+ find_cpl(films[1]->dir(films[1]->dcp_name())).string()
+ };
+
+ boost::filesystem::remove_all(out);
+
+ vector<string> output_messages;
+ auto error = run(args, output_messages);
+ BOOST_CHECK(!error);
+
+ verify_dcp(out, acceptable_errors);
+}
+
+
+BOOST_AUTO_TEST_CASE(map_two_interop_cpls_each_with_subs)
+{
+ test_two_cpls_each_with_subs("map_two_interop_cpls_each_with_subs", true);
+}
+
+
+BOOST_AUTO_TEST_CASE(map_two_smpte_cpls_each_with_subs)
+{
+ test_two_cpls_each_with_subs("map_two_smpte_cpls_each_with_subs", false);
+}
auto decryption = make_shared<dcp::CertificateChain>(dcp::file_to_string("test/data/decryption_chain"));
decryption->set_key(dcp::file_to_string("test/data/decryption_key"));
Config::instance()->set_decryption_chain (decryption);
+ Config::instance()->set_dcp_asset_filename_format(dcp::NameFormat("%t"));
}
kdm_util_test.cc
low_bitrate_test.cc
markers_test.cc
+ map_cli_test.cc
mca_subdescriptors_test.cc
no_use_video_test.cc
optimise_stills_test.cc