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>
71 using boost::shared_ptr;
72 using boost::optional;
75 boost::filesystem::path private_test;
76 boost::filesystem::path xsd_test = "build/test/xsd with spaces";
84 if (boost::unit_test::framework::master_test_suite().argc >= 2) {
85 private_test = boost::unit_test::framework::master_test_suite().argv[1];
88 using namespace boost::filesystem;
89 boost::system::error_code ec;
90 remove_all (xsd_test, ec);
91 boost::filesystem::create_directory (xsd_test);
92 for (directory_iterator i = directory_iterator("xsd"); i != directory_iterator(); ++i) {
93 copy_file (*i, xsd_test / i->path().filename());
99 check_xml (xmlpp::Element* ref, xmlpp::Element* test, list<string> ignore_tags, bool ignore_whitespace)
101 BOOST_CHECK_EQUAL (ref->get_name (), test->get_name ());
102 BOOST_CHECK_EQUAL (ref->get_namespace_prefix (), test->get_namespace_prefix ());
104 if (find(ignore_tags.begin(), ignore_tags.end(), ref->get_name()) != ignore_tags.end()) {
108 xmlpp::Element::NodeList ref_children = ref->get_children ();
109 xmlpp::Element::NodeList test_children = test->get_children ();
110 BOOST_REQUIRE_MESSAGE (
111 ref_children.size () == test_children.size (),
112 "child counts of " << ref->get_name() << " differ; ref has " << ref_children.size() << ", test has " << test_children.size()
115 xmlpp::Element::NodeList::iterator k = ref_children.begin ();
116 xmlpp::Element::NodeList::iterator l = test_children.begin ();
117 while (k != ref_children.end ()) {
119 /* XXX: should be doing xmlpp::EntityReference, xmlpp::XIncludeEnd, xmlpp::XIncludeStart */
121 xmlpp::Element* ref_el = dynamic_cast<xmlpp::Element*> (*k);
122 xmlpp::Element* test_el = dynamic_cast<xmlpp::Element*> (*l);
123 BOOST_CHECK ((ref_el && test_el) || (!ref_el && !test_el));
124 if (ref_el && test_el) {
125 check_xml (ref_el, test_el, ignore_tags, ignore_whitespace);
128 xmlpp::ContentNode* ref_cn = dynamic_cast<xmlpp::ContentNode*> (*k);
129 xmlpp::ContentNode* test_cn = dynamic_cast<xmlpp::ContentNode*> (*l);
130 BOOST_CHECK ((ref_cn && test_cn) || (!ref_cn && !test_cn));
131 if (ref_cn && test_cn) {
133 !ignore_whitespace ||
134 ref_cn->get_content().find_first_not_of(" \t\r\n") != string::npos ||
135 test_cn->get_content().find_first_not_of(" \t\r\n") != string::npos) {
137 BOOST_CHECK_EQUAL (ref_cn->get_content(), test_cn->get_content ());
145 xmlpp::Element::AttributeList ref_attributes = ref->get_attributes ();
146 xmlpp::Element::AttributeList test_attributes = test->get_attributes ();
147 BOOST_CHECK_EQUAL (ref_attributes.size(), test_attributes.size ());
149 xmlpp::Element::AttributeList::const_iterator m = ref_attributes.begin();
150 xmlpp::Element::AttributeList::const_iterator n = test_attributes.begin();
151 while (m != ref_attributes.end ()) {
152 BOOST_CHECK_EQUAL ((*m)->get_name(), (*n)->get_name());
153 BOOST_CHECK_EQUAL ((*m)->get_value(), (*n)->get_value());
161 check_xml (string ref, string test, list<string> ignore)
163 xmlpp::DomParser* ref_parser = new xmlpp::DomParser ();
164 ref_parser->parse_memory (ref);
165 xmlpp::Element* ref_root = ref_parser->get_document()->get_root_node ();
166 xmlpp::DomParser* test_parser = new xmlpp::DomParser ();
167 test_parser->parse_memory (test);
168 xmlpp::Element* test_root = test_parser->get_document()->get_root_node ();
170 check_xml (ref_root, test_root, ignore);
174 check_file (boost::filesystem::path ref, boost::filesystem::path check)
176 uintmax_t N = boost::filesystem::file_size (ref);
177 BOOST_CHECK_EQUAL (N, boost::filesystem::file_size (check));
178 FILE* ref_file = dcp::fopen_boost (ref, "rb");
179 BOOST_REQUIRE (ref_file);
180 FILE* check_file = dcp::fopen_boost (check, "rb");
181 BOOST_REQUIRE (check_file);
183 int const buffer_size = 65536;
184 uint8_t* ref_buffer = new uint8_t[buffer_size];
185 uint8_t* check_buffer = new uint8_t[buffer_size];
188 error = "File " + check.string() + " differs from reference " + ref.string();
191 uintmax_t this_time = min (uintmax_t (buffer_size), N);
192 size_t r = fread (ref_buffer, 1, this_time, ref_file);
193 BOOST_CHECK_EQUAL (r, this_time);
194 r = fread (check_buffer, 1, this_time, check_file);
195 BOOST_CHECK_EQUAL (r, this_time);
197 BOOST_CHECK_MESSAGE (memcmp (ref_buffer, check_buffer, this_time) == 0, error);
198 if (memcmp (ref_buffer, check_buffer, this_time)) {
206 delete[] check_buffer;
213 RNGFixer::RNGFixer ()
215 Kumu::cth_test = true;
216 Kumu::FortunaRNG().Reset();
220 RNGFixer::~RNGFixer ()
222 Kumu::cth_test = false;
226 shared_ptr<dcp::MonoPictureAsset>
227 simple_picture (boost::filesystem::path path, string suffix)
229 dcp::MXFMetadata mxf_meta;
230 mxf_meta.company_name = "OpenDCP";
231 mxf_meta.product_name = "OpenDCP";
232 mxf_meta.product_version = "0.0.25";
234 shared_ptr<dcp::MonoPictureAsset> mp (new dcp::MonoPictureAsset (dcp::Fraction (24, 1), dcp::SMPTE));
235 mp->set_metadata (mxf_meta);
236 shared_ptr<dcp::PictureAssetWriter> picture_writer = mp->start_write (path / dcp::String::compose("video%1.mxf", suffix), false);
237 dcp::File j2c ("test/data/32x32_red_square.j2c");
238 for (int i = 0; i < 24; ++i) {
239 picture_writer->write (j2c.data (), j2c.size ());
241 picture_writer->finalize ();
248 make_simple (boost::filesystem::path path, int reels)
250 /* Some known metadata */
251 dcp::MXFMetadata mxf_meta;
252 mxf_meta.company_name = "OpenDCP";
253 mxf_meta.product_name = "OpenDCP";
254 mxf_meta.product_version = "0.0.25";
256 boost::filesystem::remove_all (path);
257 boost::filesystem::create_directories (path);
258 shared_ptr<dcp::DCP> d (new dcp::DCP (path));
259 shared_ptr<dcp::CPL> cpl (new dcp::CPL ("A Test DCP", dcp::FEATURE));
260 cpl->set_annotation_text ("A Test DCP");
261 cpl->set_issuer ("OpenDCP 0.0.25");
262 cpl->set_creator ("OpenDCP 0.0.25");
263 cpl->set_issue_date ("2012-07-17T04:45:18+00:00");
264 cpl->set_content_version (
265 dcp::ContentVersion("urn:uuid:75ac29aa-42ac-1234-ecae-49251abefd11", "content-version-label-text")
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::LanguageTag("en-US"), dcp::SMPTE));
274 ms->set_metadata (mxf_meta);
275 vector<dcp::Channel> active_channels;
276 active_channels.push_back (dcp::LEFT);
277 shared_ptr<dcp::SoundAssetWriter> sound_writer = ms->start_write (path / dcp::String::compose("audio%1.mxf", suffix), active_channels);
281 SNDFILE* sndfile = sf_open ("test/data/1s_24-bit_48k_silence.wav", SFM_READ, &info);
282 BOOST_CHECK (sndfile);
283 float buffer[4096*6];
285 channels[0] = buffer;
287 sf_count_t N = sf_readf_float (sndfile, buffer, 4096);
288 sound_writer->write (channels, N);
294 sound_writer->finalize ();
296 cpl->add (shared_ptr<dcp::Reel> (
298 shared_ptr<dcp::ReelMonoPictureAsset>(new dcp::ReelMonoPictureAsset(mp, 0)),
299 shared_ptr<dcp::ReelSoundAsset>(new dcp::ReelSoundAsset(ms, 0))
309 shared_ptr<dcp::Subtitle>
312 return shared_ptr<dcp::Subtitle>(
313 new dcp::SubtitleString(
318 dcp::Colour(255, 255, 255),
321 dcp::Time(0, 0, 4, 0, 24),
322 dcp::Time(0, 0, 8, 0, 24),
330 dcp::Colour(255, 255, 255),
339 make_simple_with_interop_subs (boost::filesystem::path path)
341 shared_ptr<dcp::DCP> dcp = make_simple (path);
343 shared_ptr<dcp::InteropSubtitleAsset> subs(new dcp::InteropSubtitleAsset());
344 subs->add (simple_subtitle());
346 boost::filesystem::create_directory (path / "subs");
347 dcp::ArrayData data(4096);
348 data.write (path / "subs" / "font.ttf");
349 subs->add_font ("afont", path / "subs" / "font.ttf");
350 subs->write (path / "subs" / "subs.xml");
352 shared_ptr<dcp::ReelSubtitleAsset> reel_subs(new dcp::ReelSubtitleAsset(subs, dcp::Fraction(24, 1), 240, 0));
353 dcp->cpls().front()->reels().front()->add (reel_subs);
360 make_simple_with_smpte_subs (boost::filesystem::path path)
362 shared_ptr<dcp::DCP> dcp = make_simple (path);
364 shared_ptr<dcp::SMPTESubtitleAsset> subs(new dcp::SMPTESubtitleAsset());
365 subs->add (simple_subtitle());
367 dcp::ArrayData data(4096);
368 subs->write (path / "subs.mxf");
370 shared_ptr<dcp::ReelSubtitleAsset> reel_subs(new dcp::ReelSubtitleAsset(subs, dcp::Fraction(24, 1), 240, 0));
371 dcp->cpls().front()->reels().front()->add (reel_subs);
378 make_simple_with_interop_ccaps (boost::filesystem::path path)
380 shared_ptr<dcp::DCP> dcp = make_simple (path);
382 shared_ptr<dcp::InteropSubtitleAsset> subs(new dcp::InteropSubtitleAsset());
383 subs->add (simple_subtitle());
384 subs->write (path / "ccap.xml");
386 shared_ptr<dcp::ReelClosedCaptionAsset> reel_caps(new dcp::ReelClosedCaptionAsset(subs, dcp::Fraction(24, 1), 240, 0));
387 dcp->cpls().front()->reels().front()->add (reel_caps);
394 make_simple_with_smpte_ccaps (boost::filesystem::path path)
396 shared_ptr<dcp::DCP> dcp = make_simple (path);
398 shared_ptr<dcp::SMPTESubtitleAsset> subs(new dcp::SMPTESubtitleAsset());
399 subs->add (simple_subtitle());
400 subs->write (path / "ccap.mxf");
402 shared_ptr<dcp::ReelClosedCaptionAsset> reel_caps(new dcp::ReelClosedCaptionAsset(subs, dcp::Fraction(24, 1), 240, 0));
403 dcp->cpls().front()->reels().front()->add (reel_caps);
409 shared_ptr<dcp::OpenJPEGImage>
412 shared_ptr<dcp::OpenJPEGImage> image(new dcp::OpenJPEGImage(dcp::Size(1998, 1080)));
413 int const pixels = 1998 * 1080;
414 for (int i = 0; i < 3; ++i) {
415 memset (image->data(i), 0, pixels * sizeof(int));
421 shared_ptr<dcp::ReelAsset>
422 black_picture_asset (boost::filesystem::path dir, int frames)
424 shared_ptr<dcp::OpenJPEGImage> image = black_image ();
425 dcp::ArrayData frame = dcp::compress_j2k (image, 100000000, 24, false, false);
426 BOOST_REQUIRE (frame.size() < 230000000 / (24 * 8));
428 shared_ptr<dcp::MonoPictureAsset> asset(new dcp::MonoPictureAsset(dcp::Fraction(24, 1), dcp::SMPTE));
429 boost::filesystem::create_directories (dir);
430 shared_ptr<dcp::PictureAssetWriter> writer = asset->start_write (dir / "pic.mxf", true);
431 for (int i = 0; i < frames; ++i) {
432 writer->write (frame.data().get(), frame.size());
436 return shared_ptr<dcp::ReelAsset>(new dcp::ReelMonoPictureAsset(asset, 0));
440 BOOST_GLOBAL_FIXTURE (TestConfig);