diff options
| -rw-r--r-- | src/combine.cc | 165 | ||||
| -rw-r--r-- | src/combine.h | 45 | ||||
| -rw-r--r-- | src/dcp.cc | 2 | ||||
| -rw-r--r-- | src/exceptions.cc | 6 | ||||
| -rw-r--r-- | src/exceptions.h | 7 | ||||
| -rw-r--r-- | src/wscript | 2 | ||||
| -rw-r--r-- | test/combine_test.cc | 341 | ||||
| -rw-r--r-- | test/test.cc | 193 | ||||
| -rw-r--r-- | test/test.h | 15 | ||||
| -rw-r--r-- | test/wscript | 1 |
10 files changed, 740 insertions, 37 deletions
diff --git a/src/combine.cc b/src/combine.cc new file mode 100644 index 00000000..0e262fce --- /dev/null +++ b/src/combine.cc @@ -0,0 +1,165 @@ +/* + Copyright (C) 2020 Carl Hetherington <cth@carlh.net> + + This file is part of libdcp. + + libdcp 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. + + libdcp 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 libdcp. If not, see <http://www.gnu.org/licenses/>. + + In addition, as a special exception, the copyright holders give + permission to link the code of portions of this program with the + OpenSSL library under certain conditions as described in each + individual source file, and distribute linked combinations + including the two. + + You must obey the GNU General Public License in all respects + for all of the code used other than OpenSSL. If you modify + file(s) with this exception, you may extend this exception to your + version of the file(s), but you are not obligated to do so. If you + do not wish to do so, delete this exception statement from your + version. If you delete this exception statement from all source + files in the program, then also delete it here. +*/ + + +#include "asset.h" +#include "combine.h" +#include "cpl.h" +#include "dcp.h" +#include "dcp_assert.h" +#include "exceptions.h" +#include "font_asset.h" +#include "interop_subtitle_asset.h" +#include "raw_convert.h" +#include <boost/filesystem.hpp> +#include <boost/foreach.hpp> +#include <set> +#include <string> +#include <vector> + + +using std::list; +using std::map; +using std::set; +using std::string; +using std::vector; +using boost::dynamic_pointer_cast; +using boost::optional; +using boost::shared_ptr; + + +boost::filesystem::path +make_unique (boost::filesystem::path path) +{ + if (!boost::filesystem::exists(path)) { + return path; + } + + for (int i = 0; i < 10000; ++i) { + boost::filesystem::path p = path.parent_path() / (path.stem().string() + dcp::raw_convert<string>(i) + path.extension().string()); + if (!boost::filesystem::exists(p)) { + return p; + } + } + + DCP_ASSERT (false); + return path; +} + + +static +void +create_hard_link_or_copy (boost::filesystem::path from, boost::filesystem::path to) +{ + try { + create_hard_link (from, to); + } catch (boost::filesystem::filesystem_error& e) { + if (e.code() == boost::system::errc::cross_device_link) { + copy_file (from, to); + } else { + throw; + } + } +} + + +void +dcp::combine (vector<boost::filesystem::path> inputs, boost::filesystem::path output, shared_ptr<const CertificateChain> signer) +{ + using namespace boost::filesystem; + + DCP_ASSERT (!inputs.empty()); + + DCP output_dcp (output); + optional<dcp::Standard> standard; + + BOOST_FOREACH (path i, inputs) { + DCP dcp (i); + dcp.read (); + if (!standard) { + standard = *dcp.standard(); + } else if (standard != dcp.standard()) { + throw CombineError ("Cannot combine Interop and SMPTE DCPs."); + } + } + + list<path> paths; + list<shared_ptr<dcp::Asset> > assets; + + BOOST_FOREACH (path i, inputs) { + DCP dcp (i); + dcp.read (); + + BOOST_FOREACH (shared_ptr<dcp::CPL> j, dcp.cpls()) { + output_dcp.add (j); + } + + BOOST_FOREACH (shared_ptr<dcp::Asset> j, dcp.assets(true)) { + if (dynamic_pointer_cast<dcp::CPL>(j)) { + continue; + } + + optional<path> file = j->file(); + DCP_ASSERT (file); + path new_path = make_unique(output / file->filename()); + + shared_ptr<dcp::InteropSubtitleAsset> sub = dynamic_pointer_cast<dcp::InteropSubtitleAsset>(j); + if (sub) { + /* Interop fonts are really fiddly. The font files are assets (in the ASSETMAP) + * and also linked from the font XML by filename. We have to fix both these things, + * and re-write the font XML file since the font URI might have changed if it's a duplicate + * with another DCP. + */ + map<string, path> fonts = sub->font_filenames (); + for (map<string, path>::const_iterator k = fonts.begin(); k != fonts.end(); ++k) { + sub->set_font_file (k->first, make_unique(output / k->second.filename())); + } + sub->write (new_path); + } else if (!dynamic_pointer_cast<dcp::FontAsset>(j)) { + /* Take care of everything else that's not a Interop subtitle asset, Interop font file + * or CPL. + */ + optional<path> file = j->file(); + DCP_ASSERT (file); + path new_path = make_unique(output / file->filename()); + create_hard_link_or_copy (*file, new_path); + j->set_file (new_path); + } + + assets.push_back (j); + } + } + + output_dcp.resolve_refs (assets); + output_dcp.write_xml (*standard, dcp::XMLMetadata(), signer); +} diff --git a/src/combine.h b/src/combine.h new file mode 100644 index 00000000..5d40d4d1 --- /dev/null +++ b/src/combine.h @@ -0,0 +1,45 @@ +/* + Copyright (C) 2020 Carl Hetherington <cth@carlh.net> + + This file is part of libdcp. + + libdcp 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. + + libdcp 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 libdcp. If not, see <http://www.gnu.org/licenses/>. + + In addition, as a special exception, the copyright holders give + permission to link the code of portions of this program with the + OpenSSL library under certain conditions as described in each + individual source file, and distribute linked combinations + including the two. + + You must obey the GNU General Public License in all respects + for all of the code used other than OpenSSL. If you modify + file(s) with this exception, you may extend this exception to your + version of the file(s), but you are not obligated to do so. If you + do not wish to do so, delete this exception statement from your + version. If you delete this exception statement from all source + files in the program, then also delete it here. +*/ + + +#include <boost/filesystem.hpp> + + +namespace dcp { + +class CertificateChain; + +void combine (std::vector<boost::filesystem::path> inputs, boost::filesystem::path output, boost::shared_ptr<const CertificateChain> signer = boost::shared_ptr<CertificateChain>()); + +} + @@ -117,7 +117,7 @@ DCP::read (list<dcp::VerificationNote>* notes, bool ignore_incorrect_picture_mxf } else if (boost::filesystem::exists (_directory / "ASSETMAP.xml")) { _asset_map = _directory / "ASSETMAP.xml"; } else { - boost::throw_exception (ReadError (String::compose ("could not find ASSETMAP nor ASSETMAP.xml in `%1'", _directory.string()))); + boost::throw_exception (ReadError(String::compose("Could not find ASSETMAP nor ASSETMAP.xml in '%1'", _directory.string()))); } cxml::Document asset_map ("AssetMap"); diff --git a/src/exceptions.cc b/src/exceptions.cc index 3357ebe9..0256c2b5 100644 --- a/src/exceptions.cc +++ b/src/exceptions.cc @@ -131,3 +131,9 @@ StartCompressionError::StartCompressionError (optional<int> code) , _code (code) {} + + +CombineError::CombineError (string message) + : runtime_error (message) +{} + diff --git a/src/exceptions.h b/src/exceptions.h index 7ed729dd..1fb5e4e7 100644 --- a/src/exceptions.h +++ b/src/exceptions.h @@ -240,6 +240,13 @@ private: boost::optional<int> _code; }; + +class CombineError : public std::runtime_error +{ +public: + explicit CombineError (std::string message); +}; + } #endif diff --git a/src/wscript b/src/wscript index 8dd856c6..fd215d76 100644 --- a/src/wscript +++ b/src/wscript @@ -44,6 +44,7 @@ def build(bld): certificate.cc chromaticity.cc colour_conversion.cc + combine.cc cpl.cc data.cc dcp.cc @@ -122,6 +123,7 @@ def build(bld): certificate.h chromaticity.h colour_conversion.h + combine.h cpl.h crypto_context.h dcp.h diff --git a/test/combine_test.cc b/test/combine_test.cc new file mode 100644 index 00000000..70c55b90 --- /dev/null +++ b/test/combine_test.cc @@ -0,0 +1,341 @@ +/* + Copyright (C) 2020 Carl Hetherington <cth@carlh.net> + + This file is part of libdcp. + + libdcp 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. + + libdcp 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 libdcp. If not, see <http://www.gnu.org/licenses/>. + + In addition, as a special exception, the copyright holders give + permission to link the code of portions of this program with the + OpenSSL library under certain conditions as described in each + individual source file, and distribute linked combinations + including the two. + + You must obey the GNU General Public License in all respects + for all of the code used other than OpenSSL. If you modify + file(s) with this exception, you may extend this exception to your + version of the file(s), but you are not obligated to do so. If you + do not wish to do so, delete this exception statement from your + version. If you delete this exception statement from all source + files in the program, then also delete it here. +*/ + + +#include "combine.h" +#include "cpl.h" +#include "dcp.h" +#include "interop_subtitle_asset.h" +#include "reel_subtitle_asset.h" +#include "reel_mono_picture_asset.h" +#include "reel_sound_asset.h" +#include "test.h" +#include "types.h" +#include "verify.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::vector; +using boost::optional; +using boost::shared_ptr; + + +static void +stage (string, optional<boost::filesystem::path>) +{ +} + + +static void +progress (float) +{ +} + + +static +void +dump_notes (list<dcp::VerificationNote> const & notes) +{ + BOOST_FOREACH (dcp::VerificationNote i, notes) { + std::cout << dcp::note_to_string(i) << "\n"; + } +} + + +static +void +check_no_errors (boost::filesystem::path path) +{ + vector<boost::filesystem::path> directories; + directories.push_back (path); + list<dcp::VerificationNote> notes = dcp::verify (directories, &stage, &progress, xsd_test); + dump_notes (notes); + BOOST_CHECK (notes.empty()); +} + + +template <class T> +shared_ptr<T> +pointer_to_id_in_list (shared_ptr<T> needle, list<shared_ptr<T> > haystack) +{ + BOOST_FOREACH (shared_ptr<T> i, haystack) { + if (i->id() == needle->id()) { + return i; + } + } + + return shared_ptr<T>(); +} + + +static +void +note_handler (dcp::NoteType, std::string) +{ + // std::cout << "> " << n << "\n"; +} + + +static +void +check_combined (vector<boost::filesystem::path> inputs, boost::filesystem::path output) +{ + dcp::DCP output_dcp (output); + output_dcp.read (); + + dcp::EqualityOptions options; + options.load_font_nodes_can_differ = true; + + BOOST_FOREACH (boost::filesystem::path 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(); + + shared_ptr<dcp::CPL> output_cpl = pointer_to_id_in_list (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_list(i, output_dcp.assets()); + BOOST_REQUIRE_MESSAGE (o, "Could not find " << i->id() << " in combined DCP."); + BOOST_CHECK (i->equals(o, options, note_handler)); + } + } +} + + +BOOST_AUTO_TEST_CASE (combine_single_dcp_test) +{ + using namespace boost::algorithm; + using namespace boost::filesystem; + boost::filesystem::path const out = "build/test/combine_single_dcp_test"; + + remove_all (out); + vector<path> inputs; + inputs.push_back ("test/ref/DCP/dcp_test1"); + dcp::combine (inputs, out); + + check_no_errors (out); + check_combined (inputs, out); +} + + +BOOST_AUTO_TEST_CASE (combine_two_dcps_with_same_asset_filenames_test) +{ + using namespace boost::algorithm; + 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, dcp::XMLMetadata()); + + remove_all (out); + vector<path> inputs; + inputs.push_back ("test/ref/DCP/dcp_test1"); + inputs.push_back ("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, dcp::XMLMetadata()); + + shared_ptr<dcp::DCP> second = make_simple_with_interop_subs ("build/test/combine_input2"); + second->write_xml (dcp::INTEROP, dcp::XMLMetadata()); + + 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); +} + + +BOOST_AUTO_TEST_CASE (combine_two_dcps_with_smpte_subs_test) +{ + using namespace boost::algorithm; + 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, dcp::XMLMetadata()); + + shared_ptr<dcp::DCP> second = make_simple_with_smpte_subs ("build/test/combine_input2"); + second->write_xml (dcp::SMPTE, dcp::XMLMetadata()); + + 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); +} + + +BOOST_AUTO_TEST_CASE (combine_two_dcps_with_interop_ccaps_test) +{ + using namespace boost::algorithm; + 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, dcp::XMLMetadata()); + + shared_ptr<dcp::DCP> second = make_simple_with_interop_ccaps ("build/test/combine_input2"); + second->write_xml (dcp::INTEROP, dcp::XMLMetadata()); + + 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); +} + + +BOOST_AUTO_TEST_CASE (combine_two_dcps_with_smpte_ccaps_test) +{ + using namespace boost::algorithm; + 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, dcp::XMLMetadata()); + + shared_ptr<dcp::DCP> second = make_simple_with_smpte_ccaps ("build/test/combine_input2"); + second->write_xml (dcp::SMPTE, dcp::XMLMetadata()); + + 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); +} + + +BOOST_AUTO_TEST_CASE (combine_two_multi_reel_dcps) +{ + using namespace boost::algorithm; + 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, dcp::XMLMetadata()); + + shared_ptr<dcp::DCP> second = make_simple ("build/test/combine_input2", 4); + second->write_xml (dcp::SMPTE, dcp::XMLMetadata()); + + 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); +} + + +BOOST_AUTO_TEST_CASE (combine_two_dcps_with_shared_asset) +{ + 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, dcp::XMLMetadata()); + + remove_all ("build/test/combine_input2"); + shared_ptr<dcp::DCP> second(new dcp::DCP("build/test/combine_input2")); + + /* Some known metadata */ + dcp::XMLMetadata xml_meta; + xml_meta.annotation_text = "A Test DCP"; + xml_meta.issuer = "OpenDCP 0.0.25"; + xml_meta.creator = "OpenDCP 0.0.25"; + xml_meta.issue_date = "2012-07-17T04:45:18+00:00"; + dcp::MXFMetadata mxf_meta; + mxf_meta.company_name = "OpenDCP"; + mxf_meta.product_name = "OpenDCP"; + mxf_meta.product_version = "0.0.25"; + + shared_ptr<dcp::CPL> cpl (new dcp::CPL("A Test DCP", dcp::FEATURE)); + cpl->set_content_version_id ("urn:uuid:75ac29aa-42ac-1234-ecae-49251abefd11"); + cpl->set_content_version_label_text ("content-version-label-text"); + cpl->set_metadata (xml_meta); + + 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))); + second->add (cpl); + second->write_xml (dcp::SMPTE, dcp::XMLMetadata()); + + 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); +} + + +/* XXX: same CPL names */ +/* XXX: Interop PNG subs */ diff --git a/test/test.cc b/test/test.cc index a53eedc9..60dabcd4 100644 --- a/test/test.cc +++ b/test/test.cc @@ -33,16 +33,21 @@ #define BOOST_TEST_DYN_LINK #define BOOST_TEST_MODULE libdcp_test +#include "compose.hpp" #include "cpl.h" #include "dcp.h" #include "file.h" +#include "interop_subtitle_asset.h" #include "mono_picture_asset.h" #include "picture_asset_writer.h" #include "reel.h" #include "reel_mono_picture_asset.h" #include "reel_sound_asset.h" +#include "reel_closed_caption_asset.h" +#include "reel_subtitle_asset.h" #include "sound_asset.h" #include "sound_asset_writer.h" +#include "smpte_subtitle_asset.h" #include "test.h" #include "util.h" #include <asdcp/KM_util.h> @@ -57,6 +62,7 @@ using std::string; using std::min; using std::list; using boost::shared_ptr; +using boost::optional; boost::filesystem::path private_test; boost::filesystem::path xsd_test = "build/test/xsd with spaces"; @@ -202,8 +208,29 @@ RNGFixer::~RNGFixer () } +shared_ptr<dcp::MonoPictureAsset> +simple_picture (boost::filesystem::path path, string suffix) +{ + 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::SMPTE)); + mp->set_metadata (mxf_meta); + shared_ptr<dcp::PictureAssetWriter> picture_writer = mp->start_write (path / dcp::String::compose("video%1.mxf", suffix), false); + dcp::File j2c ("test/data/32x32_red_square.j2c"); + for (int i = 0; i < 24; ++i) { + picture_writer->write (j2c.data (), j2c.size ()); + } + picture_writer->finalize (); + + return mp; +} + + shared_ptr<dcp::DCP> -make_simple (boost::filesystem::path path) +make_simple (boost::filesystem::path path, int reels) { /* Some known metadata */ dcp::XMLMetadata xml_meta; @@ -216,7 +243,6 @@ make_simple (boost::filesystem::path path) mxf_meta.product_name = "OpenDCP"; mxf_meta.product_version = "0.0.25"; - /* We're making build/test/DCP/dcp_test1 */ boost::filesystem::remove_all (path); boost::filesystem::create_directories (path); shared_ptr<dcp::DCP> d (new dcp::DCP (path)); @@ -225,46 +251,143 @@ make_simple (boost::filesystem::path path) cpl->set_content_version_label_text ("content-version-label-text"); cpl->set_metadata (xml_meta); - shared_ptr<dcp::MonoPictureAsset> mp (new dcp::MonoPictureAsset (dcp::Fraction (24, 1), dcp::SMPTE)); - mp->set_metadata (mxf_meta); - shared_ptr<dcp::PictureAssetWriter> picture_writer = mp->start_write (path / "video.mxf", false); - dcp::File j2c ("test/data/32x32_red_square.j2c"); - for (int i = 0; i < 24; ++i) { - picture_writer->write (j2c.data (), j2c.size ()); - } - picture_writer->finalize (); - - shared_ptr<dcp::SoundAsset> ms (new dcp::SoundAsset (dcp::Fraction (24, 1), 48000, 1, dcp::SMPTE)); - ms->set_metadata (mxf_meta); - shared_ptr<dcp::SoundAssetWriter> sound_writer = ms->start_write (path / "audio.mxf"); - - SF_INFO info; - info.format = 0; - SNDFILE* sndfile = sf_open ("test/data/1s_24-bit_48k_silence.wav", SFM_READ, &info); - BOOST_CHECK (sndfile); - float buffer[4096*6]; - float* channels[1]; - channels[0] = buffer; - while (true) { - sf_count_t N = sf_readf_float (sndfile, buffer, 4096); - sound_writer->write (channels, N); - if (N < 4096) { - break; + 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); + + shared_ptr<dcp::SoundAsset> ms (new dcp::SoundAsset (dcp::Fraction (24, 1), 48000, 1, dcp::SMPTE)); + ms->set_metadata (mxf_meta); + shared_ptr<dcp::SoundAssetWriter> sound_writer = ms->start_write (path / dcp::String::compose("audio%1.mxf", suffix)); + + SF_INFO info; + info.format = 0; + SNDFILE* sndfile = sf_open ("test/data/1s_24-bit_48k_silence.wav", SFM_READ, &info); + BOOST_CHECK (sndfile); + float buffer[4096*6]; + float* channels[1]; + channels[0] = buffer; + while (true) { + sf_count_t N = sf_readf_float (sndfile, buffer, 4096); + sound_writer->write (channels, N); + if (N < 4096) { + break; + } } - } - sound_writer->finalize (); + sound_writer->finalize (); - cpl->add (shared_ptr<dcp::Reel> ( - new dcp::Reel ( - shared_ptr<dcp::ReelMonoPictureAsset> (new dcp::ReelMonoPictureAsset (mp, 0)), - shared_ptr<dcp::ReelSoundAsset> (new dcp::ReelSoundAsset (ms, 0)) - ) - )); + cpl->add (shared_ptr<dcp::Reel> ( + new dcp::Reel ( + shared_ptr<dcp::ReelMonoPictureAsset>(new dcp::ReelMonoPictureAsset(mp, 0)), + shared_ptr<dcp::ReelSoundAsset>(new dcp::ReelSoundAsset(ms, 0)) + ) + )); + } d->add (cpl); return d; } +shared_ptr<dcp::Subtitle> +simple_subtitle () +{ + return shared_ptr<dcp::Subtitle>( + new dcp::SubtitleString( + optional<string>(), + false, + false, + false, + dcp::Colour(255, 255, 255), + 42, + 1, + dcp::Time(0, 0, 4, 0, 24), + dcp::Time(0, 0, 8, 0, 24), + 0.5, + dcp::HALIGN_CENTER, + 0.8, + dcp::VALIGN_TOP, + dcp::DIRECTION_LTR, + "Hello world", + dcp::NONE, + dcp::Colour(255, 255, 255), + dcp::Time(), + dcp::Time() + ) + ); +} + + +shared_ptr<dcp::DCP> +make_simple_with_interop_subs (boost::filesystem::path path) +{ + shared_ptr<dcp::DCP> dcp = make_simple (path); + + shared_ptr<dcp::InteropSubtitleAsset> subs(new dcp::InteropSubtitleAsset()); + subs->add (simple_subtitle()); + + boost::filesystem::create_directory (path / "subs"); + dcp::Data data(4096); + data.write (path / "subs" / "font.ttf"); + subs->add_font ("afont", path / "subs" / "font.ttf"); + subs->write (path / "subs" / "subs.xml"); + + shared_ptr<dcp::ReelSubtitleAsset> reel_subs(new dcp::ReelSubtitleAsset(subs, dcp::Fraction(24, 1), 240, 0)); + dcp->cpls().front()->reels().front()->add (reel_subs); + + return dcp; +} + + +shared_ptr<dcp::DCP> +make_simple_with_smpte_subs (boost::filesystem::path path) +{ + shared_ptr<dcp::DCP> dcp = make_simple (path); + + shared_ptr<dcp::SMPTESubtitleAsset> subs(new dcp::SMPTESubtitleAsset()); + subs->add (simple_subtitle()); + + dcp::Data data(4096); + subs->write (path / "subs.mxf"); + + shared_ptr<dcp::ReelSubtitleAsset> reel_subs(new dcp::ReelSubtitleAsset(subs, dcp::Fraction(24, 1), 240, 0)); + dcp->cpls().front()->reels().front()->add (reel_subs); + + return dcp; +} + + +shared_ptr<dcp::DCP> +make_simple_with_interop_ccaps (boost::filesystem::path path) +{ + shared_ptr<dcp::DCP> dcp = make_simple (path); + + shared_ptr<dcp::InteropSubtitleAsset> subs(new dcp::InteropSubtitleAsset()); + subs->add (simple_subtitle()); + subs->write (path / "ccap.xml"); + + shared_ptr<dcp::ReelClosedCaptionAsset> reel_caps(new dcp::ReelClosedCaptionAsset(subs, dcp::Fraction(24, 1), 240, 0)); + dcp->cpls().front()->reels().front()->add (reel_caps); + + return dcp; +} + + +shared_ptr<dcp::DCP> +make_simple_with_smpte_ccaps (boost::filesystem::path path) +{ + shared_ptr<dcp::DCP> dcp = make_simple (path); + + shared_ptr<dcp::SMPTESubtitleAsset> subs(new dcp::SMPTESubtitleAsset()); + subs->add (simple_subtitle()); + subs->write (path / "ccap.mxf"); + + shared_ptr<dcp::ReelClosedCaptionAsset> reel_caps(new dcp::ReelClosedCaptionAsset(subs, dcp::Fraction(24, 1), 240, 0)); + dcp->cpls().front()->reels().front()->add (reel_caps); + + return dcp; +} + + BOOST_GLOBAL_FIXTURE (TestConfig); diff --git a/test/test.h b/test/test.h index 4d53d49f..6fc2067d 100644 --- a/test/test.h +++ b/test/test.h @@ -17,6 +17,13 @@ along with libdcp. If not, see <http://www.gnu.org/licenses/>. */ + +#include "cpl.h" +#include "data.h" +#include "dcp.h" +#include "reel.h" +#include "reel_subtitle_asset.h" +#include "subtitle.h" #include <boost/filesystem.hpp> namespace xmlpp { @@ -25,6 +32,7 @@ namespace xmlpp { namespace dcp { class DCP; + class MonoPictureAsset; } extern boost::filesystem::path private_test; @@ -33,7 +41,12 @@ extern boost::filesystem::path xsd_test; extern void check_xml (xmlpp::Element* ref, xmlpp::Element* test, std::list<std::string> ignore); extern void check_xml (std::string ref, std::string test, std::list<std::string> ignore); extern void check_file (boost::filesystem::path ref, boost::filesystem::path check); -extern boost::shared_ptr<dcp::DCP> make_simple (boost::filesystem::path path); +extern boost::shared_ptr<dcp::MonoPictureAsset> simple_picture (boost::filesystem::path path, std::string suffix); +extern boost::shared_ptr<dcp::DCP> make_simple (boost::filesystem::path path, int reels = 1); +extern boost::shared_ptr<dcp::DCP> make_simple_with_interop_subs (boost::filesystem::path path); +extern boost::shared_ptr<dcp::DCP> make_simple_with_smpte_subs (boost::filesystem::path path); +extern boost::shared_ptr<dcp::DCP> make_simple_with_interop_ccaps (boost::filesystem::path path); +extern boost::shared_ptr<dcp::DCP> make_simple_with_smpte_ccaps (boost::filesystem::path path); /** Creating an object of this class will make asdcplib's random number generation * (more) predictable. diff --git a/test/wscript b/test/wscript index 8ea47108..d6eb078b 100644 --- a/test/wscript +++ b/test/wscript @@ -66,6 +66,7 @@ def build(bld): certificates_test.cc colour_test.cc colour_conversion_test.cc + combine_test.cc cpl_sar_test.cc cpl_ratings_test.cc dcp_font_test.cc |
