2 Copyright (C) 2012-2020 Carl Hetherington <cth@carlh.net>
4 This file is part of libdcp.
6 libdcp is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
11 libdcp is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with libdcp. If not, see <http://www.gnu.org/licenses/>.
19 In addition, as a special exception, the copyright holders give
20 permission to link the code of portions of this program with the
21 OpenSSL library under certain conditions as described in each
22 individual source file, and distribute linked combinations
25 You must obey the GNU General Public License in all respects
26 for all of the code used other than OpenSSL. If you modify
27 file(s) with this exception, you may extend this exception to your
28 version of the file(s), but you are not obligated to do so. If you
29 do not wish to do so, delete this exception statement from your
30 version. If you delete this exception statement from all source
31 files in the program, then also delete it here.
34 #define BOOST_TEST_DYN_LINK
35 #define BOOST_TEST_MODULE libdcp_test
36 #include "compose.hpp"
40 #include "interop_subtitle_asset.h"
41 #include "mono_picture_asset.h"
42 #include "picture_asset_writer.h"
44 #include "reel_mono_picture_asset.h"
45 #include "reel_sound_asset.h"
46 #include "reel_closed_caption_asset.h"
47 #include "reel_subtitle_asset.h"
48 #include "sound_asset.h"
49 #include "sound_asset_writer.h"
50 #include "smpte_subtitle_asset.h"
51 #include "mono_picture_asset.h"
52 #include "openjpeg_image.h"
54 #include "picture_asset_writer.h"
55 #include "reel_mono_picture_asset.h"
56 #include "reel_asset.h"
59 #include <asdcp/KM_util.h>
60 #include <asdcp/KM_prng.h>
62 #include <libxml++/libxml++.h>
63 #include <boost/test/unit_test.hpp>
70 using boost::shared_ptr;
71 using boost::optional;
74 boost::filesystem::path private_test;
75 boost::filesystem::path xsd_test = "build/test/xsd with spaces";
83 if (boost::unit_test::framework::master_test_suite().argc >= 2) {
84 private_test = boost::unit_test::framework::master_test_suite().argv[1];
87 using namespace boost::filesystem;
88 boost::system::error_code ec;
89 remove_all (xsd_test, ec);
90 boost::filesystem::create_directory (xsd_test);
91 for (directory_iterator i = directory_iterator("xsd"); i != directory_iterator(); ++i) {
92 copy_file (*i, xsd_test / i->path().filename());
98 check_xml (xmlpp::Element* ref, xmlpp::Element* test, list<string> ignore)
100 BOOST_CHECK_EQUAL (ref->get_name (), test->get_name ());
101 BOOST_CHECK_EQUAL (ref->get_namespace_prefix (), test->get_namespace_prefix ());
103 if (find (ignore.begin(), ignore.end(), ref->get_name()) != ignore.end ()) {
107 xmlpp::Element::NodeList ref_children = ref->get_children ();
108 xmlpp::Element::NodeList test_children = test->get_children ();
109 BOOST_REQUIRE_MESSAGE (
110 ref_children.size () == test_children.size (),
111 "child counts of " << ref->get_name() << " differ; ref has " << ref_children.size() << ", test has " << test_children.size()
114 xmlpp::Element::NodeList::iterator k = ref_children.begin ();
115 xmlpp::Element::NodeList::iterator l = test_children.begin ();
116 while (k != ref_children.end ()) {
118 /* XXX: should be doing xmlpp::EntityReference, xmlpp::XIncludeEnd, xmlpp::XIncludeStart */
120 xmlpp::Element* ref_el = dynamic_cast<xmlpp::Element*> (*k);
121 xmlpp::Element* test_el = dynamic_cast<xmlpp::Element*> (*l);
122 BOOST_CHECK ((ref_el && test_el) || (!ref_el && !test_el));
123 if (ref_el && test_el) {
124 check_xml (ref_el, test_el, ignore);
127 xmlpp::ContentNode* ref_cn = dynamic_cast<xmlpp::ContentNode*> (*k);
128 xmlpp::ContentNode* test_cn = dynamic_cast<xmlpp::ContentNode*> (*l);
129 BOOST_CHECK ((ref_cn && test_cn) || (!ref_cn && !test_cn));
130 if (ref_cn && test_cn) {
131 BOOST_CHECK_EQUAL (ref_cn->get_content(), test_cn->get_content ());
138 xmlpp::Element::AttributeList ref_attributes = ref->get_attributes ();
139 xmlpp::Element::AttributeList test_attributes = test->get_attributes ();
140 BOOST_CHECK_EQUAL (ref_attributes.size(), test_attributes.size ());
142 xmlpp::Element::AttributeList::const_iterator m = ref_attributes.begin();
143 xmlpp::Element::AttributeList::const_iterator n = test_attributes.begin();
144 while (m != ref_attributes.end ()) {
145 BOOST_CHECK_EQUAL ((*m)->get_name(), (*n)->get_name());
146 BOOST_CHECK_EQUAL ((*m)->get_value(), (*n)->get_value());
154 check_xml (string ref, string test, list<string> ignore)
156 xmlpp::DomParser* ref_parser = new xmlpp::DomParser ();
157 ref_parser->parse_memory (ref);
158 xmlpp::Element* ref_root = ref_parser->get_document()->get_root_node ();
159 xmlpp::DomParser* test_parser = new xmlpp::DomParser ();
160 test_parser->parse_memory (test);
161 xmlpp::Element* test_root = test_parser->get_document()->get_root_node ();
163 check_xml (ref_root, test_root, ignore);
167 check_file (boost::filesystem::path ref, boost::filesystem::path check)
169 uintmax_t N = boost::filesystem::file_size (ref);
170 BOOST_CHECK_EQUAL (N, boost::filesystem::file_size (check));
171 FILE* ref_file = dcp::fopen_boost (ref, "rb");
172 BOOST_CHECK (ref_file);
173 FILE* check_file = dcp::fopen_boost (check, "rb");
174 BOOST_CHECK (check_file);
176 int const buffer_size = 65536;
177 uint8_t* ref_buffer = new uint8_t[buffer_size];
178 uint8_t* check_buffer = new uint8_t[buffer_size];
181 error = "File " + check.string() + " differs from reference " + ref.string();
184 uintmax_t this_time = min (uintmax_t (buffer_size), N);
185 size_t r = fread (ref_buffer, 1, this_time, ref_file);
186 BOOST_CHECK_EQUAL (r, this_time);
187 r = fread (check_buffer, 1, this_time, check_file);
188 BOOST_CHECK_EQUAL (r, this_time);
190 BOOST_CHECK_MESSAGE (memcmp (ref_buffer, check_buffer, this_time) == 0, error);
191 if (memcmp (ref_buffer, check_buffer, this_time)) {
199 delete[] check_buffer;
206 RNGFixer::RNGFixer ()
208 Kumu::cth_test = true;
209 Kumu::FortunaRNG().Reset();
213 RNGFixer::~RNGFixer ()
215 Kumu::cth_test = false;
219 shared_ptr<dcp::MonoPictureAsset>
220 simple_picture (boost::filesystem::path path, string suffix)
222 dcp::MXFMetadata mxf_meta;
223 mxf_meta.company_name = "OpenDCP";
224 mxf_meta.product_name = "OpenDCP";
225 mxf_meta.product_version = "0.0.25";
227 shared_ptr<dcp::MonoPictureAsset> mp (new dcp::MonoPictureAsset (dcp::Fraction (24, 1), dcp::SMPTE));
228 mp->set_metadata (mxf_meta);
229 shared_ptr<dcp::PictureAssetWriter> picture_writer = mp->start_write (path / dcp::String::compose("video%1.mxf", suffix), false);
230 dcp::File j2c ("test/data/32x32_red_square.j2c");
231 for (int i = 0; i < 24; ++i) {
232 picture_writer->write (j2c.data (), j2c.size ());
234 picture_writer->finalize ();
241 make_simple (boost::filesystem::path path, int reels)
243 /* Some known metadata */
244 dcp::XMLMetadata xml_meta;
245 xml_meta.annotation_text = "A Test DCP";
246 xml_meta.issuer = "OpenDCP 0.0.25";
247 xml_meta.creator = "OpenDCP 0.0.25";
248 xml_meta.issue_date = "2012-07-17T04:45:18+00:00";
249 dcp::MXFMetadata mxf_meta;
250 mxf_meta.company_name = "OpenDCP";
251 mxf_meta.product_name = "OpenDCP";
252 mxf_meta.product_version = "0.0.25";
254 boost::filesystem::remove_all (path);
255 boost::filesystem::create_directories (path);
256 shared_ptr<dcp::DCP> d (new dcp::DCP (path));
257 shared_ptr<dcp::CPL> cpl (new dcp::CPL ("A Test DCP", dcp::FEATURE));
258 cpl->set_content_version_id ("urn:uuid:75ac29aa-42ac-1234-ecae-49251abefd11");
259 cpl->set_content_version_label_text ("content-version-label-text");
260 cpl->set_metadata (xml_meta);
262 for (int i = 0; i < reels; ++i) {
263 string suffix = reels == 1 ? "" : dcp::String::compose("%1", i);
265 shared_ptr<dcp::MonoPictureAsset> mp = simple_picture (path, suffix);
267 shared_ptr<dcp::SoundAsset> ms (new dcp::SoundAsset (dcp::Fraction (24, 1), 48000, 1, dcp::SMPTE));
268 ms->set_metadata (mxf_meta);
269 shared_ptr<dcp::SoundAssetWriter> sound_writer = ms->start_write (path / dcp::String::compose("audio%1.mxf", suffix));
273 SNDFILE* sndfile = sf_open ("test/data/1s_24-bit_48k_silence.wav", SFM_READ, &info);
274 BOOST_CHECK (sndfile);
275 float buffer[4096*6];
277 channels[0] = buffer;
279 sf_count_t N = sf_readf_float (sndfile, buffer, 4096);
280 sound_writer->write (channels, N);
286 sound_writer->finalize ();
288 cpl->add (shared_ptr<dcp::Reel> (
290 shared_ptr<dcp::ReelMonoPictureAsset>(new dcp::ReelMonoPictureAsset(mp, 0)),
291 shared_ptr<dcp::ReelSoundAsset>(new dcp::ReelSoundAsset(ms, 0))
301 shared_ptr<dcp::Subtitle>
304 return shared_ptr<dcp::Subtitle>(
305 new dcp::SubtitleString(
310 dcp::Colour(255, 255, 255),
313 dcp::Time(0, 0, 4, 0, 24),
314 dcp::Time(0, 0, 8, 0, 24),
322 dcp::Colour(255, 255, 255),
331 make_simple_with_interop_subs (boost::filesystem::path path)
333 shared_ptr<dcp::DCP> dcp = make_simple (path);
335 shared_ptr<dcp::InteropSubtitleAsset> subs(new dcp::InteropSubtitleAsset());
336 subs->add (simple_subtitle());
338 boost::filesystem::create_directory (path / "subs");
339 dcp::Data data(4096);
340 data.write (path / "subs" / "font.ttf");
341 subs->add_font ("afont", path / "subs" / "font.ttf");
342 subs->write (path / "subs" / "subs.xml");
344 shared_ptr<dcp::ReelSubtitleAsset> reel_subs(new dcp::ReelSubtitleAsset(subs, dcp::Fraction(24, 1), 240, 0));
345 dcp->cpls().front()->reels().front()->add (reel_subs);
352 make_simple_with_smpte_subs (boost::filesystem::path path)
354 shared_ptr<dcp::DCP> dcp = make_simple (path);
356 shared_ptr<dcp::SMPTESubtitleAsset> subs(new dcp::SMPTESubtitleAsset());
357 subs->add (simple_subtitle());
359 dcp::Data data(4096);
360 subs->write (path / "subs.mxf");
362 shared_ptr<dcp::ReelSubtitleAsset> reel_subs(new dcp::ReelSubtitleAsset(subs, dcp::Fraction(24, 1), 240, 0));
363 dcp->cpls().front()->reels().front()->add (reel_subs);
370 make_simple_with_interop_ccaps (boost::filesystem::path path)
372 shared_ptr<dcp::DCP> dcp = make_simple (path);
374 shared_ptr<dcp::InteropSubtitleAsset> subs(new dcp::InteropSubtitleAsset());
375 subs->add (simple_subtitle());
376 subs->write (path / "ccap.xml");
378 shared_ptr<dcp::ReelClosedCaptionAsset> reel_caps(new dcp::ReelClosedCaptionAsset(subs, dcp::Fraction(24, 1), 240, 0));
379 dcp->cpls().front()->reels().front()->add (reel_caps);
386 make_simple_with_smpte_ccaps (boost::filesystem::path path)
388 shared_ptr<dcp::DCP> dcp = make_simple (path);
390 shared_ptr<dcp::SMPTESubtitleAsset> subs(new dcp::SMPTESubtitleAsset());
391 subs->add (simple_subtitle());
392 subs->write (path / "ccap.mxf");
394 shared_ptr<dcp::ReelClosedCaptionAsset> reel_caps(new dcp::ReelClosedCaptionAsset(subs, dcp::Fraction(24, 1), 240, 0));
395 dcp->cpls().front()->reels().front()->add (reel_caps);
401 shared_ptr<dcp::OpenJPEGImage>
404 shared_ptr<dcp::OpenJPEGImage> image(new dcp::OpenJPEGImage(dcp::Size(1998, 1080)));
405 int const pixels = 1998 * 1080;
406 for (int i = 0; i < 3; ++i) {
407 memset (image->data(i), 0, pixels * sizeof(int));
413 shared_ptr<dcp::ReelAsset>
414 black_picture_asset (boost::filesystem::path dir, int frames)
416 shared_ptr<dcp::OpenJPEGImage> image = black_image ();
417 dcp::Data frame = dcp::compress_j2k (image, 100000000, 24, false, false);
418 BOOST_REQUIRE (frame.size() < 230000000 / (24 * 8));
420 shared_ptr<dcp::MonoPictureAsset> asset(new dcp::MonoPictureAsset(dcp::Fraction(24, 1), dcp::SMPTE));
421 boost::filesystem::create_directories (dir);
422 shared_ptr<dcp::PictureAssetWriter> writer = asset->start_write (dir / "pic.mxf", true);
423 for (int i = 0; i < frames; ++i) {
424 writer->write (frame.data().get(), frame.size());
428 return shared_ptr<dcp::ReelAsset>(new dcp::ReelMonoPictureAsset(asset, 0));
432 BOOST_GLOBAL_FIXTURE (TestConfig);