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_tags, bool ignore_whitespace)
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_tags.begin(), ignore_tags.end(), ref->get_name()) != ignore_tags.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_tags, ignore_whitespace);
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) {
132 !ignore_whitespace ||
133 ref_cn->get_content().find_first_not_of(" \t\r\n") != string::npos ||
134 test_cn->get_content().find_first_not_of(" \t\r\n") != string::npos) {
136 BOOST_CHECK_EQUAL (ref_cn->get_content(), test_cn->get_content ());
144 xmlpp::Element::AttributeList ref_attributes = ref->get_attributes ();
145 xmlpp::Element::AttributeList test_attributes = test->get_attributes ();
146 BOOST_CHECK_EQUAL (ref_attributes.size(), test_attributes.size ());
148 xmlpp::Element::AttributeList::const_iterator m = ref_attributes.begin();
149 xmlpp::Element::AttributeList::const_iterator n = test_attributes.begin();
150 while (m != ref_attributes.end ()) {
151 BOOST_CHECK_EQUAL ((*m)->get_name(), (*n)->get_name());
152 BOOST_CHECK_EQUAL ((*m)->get_value(), (*n)->get_value());
160 check_xml (string ref, string test, list<string> ignore)
162 xmlpp::DomParser* ref_parser = new xmlpp::DomParser ();
163 ref_parser->parse_memory (ref);
164 xmlpp::Element* ref_root = ref_parser->get_document()->get_root_node ();
165 xmlpp::DomParser* test_parser = new xmlpp::DomParser ();
166 test_parser->parse_memory (test);
167 xmlpp::Element* test_root = test_parser->get_document()->get_root_node ();
169 check_xml (ref_root, test_root, ignore);
173 check_file (boost::filesystem::path ref, boost::filesystem::path check)
175 uintmax_t N = boost::filesystem::file_size (ref);
176 BOOST_CHECK_EQUAL (N, boost::filesystem::file_size (check));
177 FILE* ref_file = dcp::fopen_boost (ref, "rb");
178 BOOST_CHECK (ref_file);
179 FILE* check_file = dcp::fopen_boost (check, "rb");
180 BOOST_CHECK (check_file);
182 int const buffer_size = 65536;
183 uint8_t* ref_buffer = new uint8_t[buffer_size];
184 uint8_t* check_buffer = new uint8_t[buffer_size];
187 error = "File " + check.string() + " differs from reference " + ref.string();
190 uintmax_t this_time = min (uintmax_t (buffer_size), N);
191 size_t r = fread (ref_buffer, 1, this_time, ref_file);
192 BOOST_CHECK_EQUAL (r, this_time);
193 r = fread (check_buffer, 1, this_time, check_file);
194 BOOST_CHECK_EQUAL (r, this_time);
196 BOOST_CHECK_MESSAGE (memcmp (ref_buffer, check_buffer, this_time) == 0, error);
197 if (memcmp (ref_buffer, check_buffer, this_time)) {
205 delete[] check_buffer;
212 RNGFixer::RNGFixer ()
214 Kumu::cth_test = true;
215 Kumu::FortunaRNG().Reset();
219 RNGFixer::~RNGFixer ()
221 Kumu::cth_test = false;
225 shared_ptr<dcp::MonoPictureAsset>
226 simple_picture (boost::filesystem::path path, string suffix)
228 dcp::MXFMetadata mxf_meta;
229 mxf_meta.company_name = "OpenDCP";
230 mxf_meta.product_name = "OpenDCP";
231 mxf_meta.product_version = "0.0.25";
233 shared_ptr<dcp::MonoPictureAsset> mp (new dcp::MonoPictureAsset (dcp::Fraction (24, 1), dcp::SMPTE));
234 mp->set_metadata (mxf_meta);
235 shared_ptr<dcp::PictureAssetWriter> picture_writer = mp->start_write (path / dcp::String::compose("video%1.mxf", suffix), false);
236 dcp::File j2c ("test/data/32x32_red_square.j2c");
237 for (int i = 0; i < 24; ++i) {
238 picture_writer->write (j2c.data (), j2c.size ());
240 picture_writer->finalize ();
247 make_simple (boost::filesystem::path path, int reels)
249 /* Some known metadata */
250 dcp::XMLMetadata xml_meta;
251 xml_meta.annotation_text = "A Test DCP";
252 xml_meta.issuer = "OpenDCP 0.0.25";
253 xml_meta.creator = "OpenDCP 0.0.25";
254 xml_meta.issue_date = "2012-07-17T04:45:18+00:00";
255 dcp::MXFMetadata mxf_meta;
256 mxf_meta.company_name = "OpenDCP";
257 mxf_meta.product_name = "OpenDCP";
258 mxf_meta.product_version = "0.0.25";
260 boost::filesystem::remove_all (path);
261 boost::filesystem::create_directories (path);
262 shared_ptr<dcp::DCP> d (new dcp::DCP (path));
263 shared_ptr<dcp::CPL> cpl (new dcp::CPL ("A Test DCP", dcp::FEATURE));
264 cpl->set_content_version_id ("urn:uuid:75ac29aa-42ac-1234-ecae-49251abefd11");
265 cpl->set_content_version_label_text ("content-version-label-text");
266 cpl->set_metadata (xml_meta);
268 for (int i = 0; i < reels; ++i) {
269 string suffix = reels == 1 ? "" : dcp::String::compose("%1", i);
271 shared_ptr<dcp::MonoPictureAsset> mp = simple_picture (path, suffix);
273 shared_ptr<dcp::SoundAsset> ms (new dcp::SoundAsset (dcp::Fraction (24, 1), 48000, 1, dcp::SMPTE));
274 ms->set_metadata (mxf_meta);
275 shared_ptr<dcp::SoundAssetWriter> sound_writer = ms->start_write (path / dcp::String::compose("audio%1.mxf", suffix));
279 SNDFILE* sndfile = sf_open ("test/data/1s_24-bit_48k_silence.wav", SFM_READ, &info);
280 BOOST_CHECK (sndfile);
281 float buffer[4096*6];
283 channels[0] = buffer;
285 sf_count_t N = sf_readf_float (sndfile, buffer, 4096);
286 sound_writer->write (channels, N);
292 sound_writer->finalize ();
294 cpl->add (shared_ptr<dcp::Reel> (
296 shared_ptr<dcp::ReelMonoPictureAsset>(new dcp::ReelMonoPictureAsset(mp, 0)),
297 shared_ptr<dcp::ReelSoundAsset>(new dcp::ReelSoundAsset(ms, 0))
307 shared_ptr<dcp::Subtitle>
310 return shared_ptr<dcp::Subtitle>(
311 new dcp::SubtitleString(
316 dcp::Colour(255, 255, 255),
319 dcp::Time(0, 0, 4, 0, 24),
320 dcp::Time(0, 0, 8, 0, 24),
328 dcp::Colour(255, 255, 255),
337 make_simple_with_interop_subs (boost::filesystem::path path)
339 shared_ptr<dcp::DCP> dcp = make_simple (path);
341 shared_ptr<dcp::InteropSubtitleAsset> subs(new dcp::InteropSubtitleAsset());
342 subs->add (simple_subtitle());
344 boost::filesystem::create_directory (path / "subs");
345 dcp::Data data(4096);
346 data.write (path / "subs" / "font.ttf");
347 subs->add_font ("afont", path / "subs" / "font.ttf");
348 subs->write (path / "subs" / "subs.xml");
350 shared_ptr<dcp::ReelSubtitleAsset> reel_subs(new dcp::ReelSubtitleAsset(subs, dcp::Fraction(24, 1), 240, 0));
351 dcp->cpls().front()->reels().front()->add (reel_subs);
358 make_simple_with_smpte_subs (boost::filesystem::path path)
360 shared_ptr<dcp::DCP> dcp = make_simple (path);
362 shared_ptr<dcp::SMPTESubtitleAsset> subs(new dcp::SMPTESubtitleAsset());
363 subs->add (simple_subtitle());
365 dcp::Data data(4096);
366 subs->write (path / "subs.mxf");
368 shared_ptr<dcp::ReelSubtitleAsset> reel_subs(new dcp::ReelSubtitleAsset(subs, dcp::Fraction(24, 1), 240, 0));
369 dcp->cpls().front()->reels().front()->add (reel_subs);
376 make_simple_with_interop_ccaps (boost::filesystem::path path)
378 shared_ptr<dcp::DCP> dcp = make_simple (path);
380 shared_ptr<dcp::InteropSubtitleAsset> subs(new dcp::InteropSubtitleAsset());
381 subs->add (simple_subtitle());
382 subs->write (path / "ccap.xml");
384 shared_ptr<dcp::ReelClosedCaptionAsset> reel_caps(new dcp::ReelClosedCaptionAsset(subs, dcp::Fraction(24, 1), 240, 0));
385 dcp->cpls().front()->reels().front()->add (reel_caps);
392 make_simple_with_smpte_ccaps (boost::filesystem::path path)
394 shared_ptr<dcp::DCP> dcp = make_simple (path);
396 shared_ptr<dcp::SMPTESubtitleAsset> subs(new dcp::SMPTESubtitleAsset());
397 subs->add (simple_subtitle());
398 subs->write (path / "ccap.mxf");
400 shared_ptr<dcp::ReelClosedCaptionAsset> reel_caps(new dcp::ReelClosedCaptionAsset(subs, dcp::Fraction(24, 1), 240, 0));
401 dcp->cpls().front()->reels().front()->add (reel_caps);
407 shared_ptr<dcp::OpenJPEGImage>
410 shared_ptr<dcp::OpenJPEGImage> image(new dcp::OpenJPEGImage(dcp::Size(1998, 1080)));
411 int const pixels = 1998 * 1080;
412 for (int i = 0; i < 3; ++i) {
413 memset (image->data(i), 0, pixels * sizeof(int));
419 shared_ptr<dcp::ReelAsset>
420 black_picture_asset (boost::filesystem::path dir, int frames)
422 shared_ptr<dcp::OpenJPEGImage> image = black_image ();
423 dcp::Data frame = dcp::compress_j2k (image, 100000000, 24, false, false);
424 BOOST_REQUIRE (frame.size() < 230000000 / (24 * 8));
426 shared_ptr<dcp::MonoPictureAsset> asset(new dcp::MonoPictureAsset(dcp::Fraction(24, 1), dcp::SMPTE));
427 boost::filesystem::create_directories (dir);
428 shared_ptr<dcp::PictureAssetWriter> writer = asset->start_write (dir / "pic.mxf", true);
429 for (int i = 0; i < frames; ++i) {
430 writer->write (frame.data().get(), frame.size());
434 return shared_ptr<dcp::ReelAsset>(new dcp::ReelMonoPictureAsset(asset, 0));
438 BOOST_GLOBAL_FIXTURE (TestConfig);