2 Copyright (C) 2013-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.
37 #include "mono_picture_asset.h"
38 #include "stereo_picture_asset.h"
39 #include "picture_asset_writer.h"
40 #include "reel_picture_asset.h"
41 #include "sound_asset_writer.h"
42 #include "sound_asset.h"
43 #include "atmos_asset.h"
46 #include "reel_mono_picture_asset.h"
47 #include "reel_stereo_picture_asset.h"
48 #include "reel_sound_asset.h"
49 #include "reel_atmos_asset.h"
50 #include "reel_markers_asset.h"
51 #include <asdcp/KM_util.h>
53 #include <boost/test/unit_test.hpp>
57 using std::dynamic_pointer_cast;
58 using std::shared_ptr;
59 using std::make_shared;
60 #if BOOST_VERSION >= 106100
61 using namespace boost::placeholders;
65 /** Test creation of a 2D SMPTE DCP from very simple inputs */
66 BOOST_AUTO_TEST_CASE (dcp_test1)
70 make_simple("build/test/DCP/dcp_test1")->write_xml(
71 dcp::SMPTE, "OpenDCP 0.0.25", "OpenDCP 0.0.25", "2012-07-17T04:45:18+00:00", "Created by libdcp"
74 /* build/test/DCP/dcp_test1 is checked against test/ref/DCP/dcp_test1 by run/tests */
77 /** Test creation of a 3D DCP from very simple inputs */
78 BOOST_AUTO_TEST_CASE (dcp_test2)
82 /* Some known metadata */
83 dcp::MXFMetadata mxf_meta;
84 mxf_meta.company_name = "OpenDCP";
85 mxf_meta.product_name = "OpenDCP";
86 mxf_meta.product_version = "0.0.25";
88 /* We're making build/test/DCP/dcp_test2 */
89 boost::filesystem::remove_all ("build/test/DCP/dcp_test2");
90 boost::filesystem::create_directories ("build/test/DCP/dcp_test2");
91 dcp::DCP d ("build/test/DCP/dcp_test2");
92 shared_ptr<dcp::CPL> cpl (new dcp::CPL ("A Test DCP", dcp::FEATURE));
93 cpl->set_content_version (
94 dcp::ContentVersion("urn:uri:81fb54df-e1bf-4647-8788-ea7ba154375b_2012-07-17T04:45:18+00:00", "81fb54df-e1bf-4647-8788-ea7ba154375b_2012-07-17T04:45:18+00:00")
96 cpl->set_issuer ("OpenDCP 0.0.25");
97 cpl->set_creator ("OpenDCP 0.0.25");
98 cpl->set_issue_date ("2012-07-17T04:45:18+00:00");
99 cpl->set_annotation_text ("A Test DCP");
101 shared_ptr<dcp::StereoPictureAsset> mp (new dcp::StereoPictureAsset (dcp::Fraction (24, 1), dcp::SMPTE));
102 mp->set_metadata (mxf_meta);
103 shared_ptr<dcp::PictureAssetWriter> picture_writer = mp->start_write ("build/test/DCP/dcp_test2/video.mxf", false);
104 dcp::ArrayData j2c ("test/data/flat_red.j2c");
105 for (int i = 0; i < 24; ++i) {
107 picture_writer->write (j2c.data (), j2c.size ());
109 picture_writer->write (j2c.data (), j2c.size ());
111 picture_writer->finalize ();
113 shared_ptr<dcp::SoundAsset> ms (new dcp::SoundAsset(dcp::Fraction(24, 1), 48000, 1, dcp::LanguageTag("en-GB"), dcp::SMPTE));
114 ms->set_metadata (mxf_meta);
115 shared_ptr<dcp::SoundAssetWriter> sound_writer = ms->start_write ("build/test/DCP/dcp_test2/audio.mxf", vector<dcp::Channel>());
119 SNDFILE* sndfile = sf_open ("test/data/1s_24-bit_48k_silence.wav", SFM_READ, &info);
120 BOOST_CHECK (sndfile);
121 float buffer[4096*6];
123 channels[0] = buffer;
125 sf_count_t N = sf_readf_float (sndfile, buffer, 4096);
126 sound_writer->write (channels, N);
132 sound_writer->finalize ();
134 cpl->add (shared_ptr<dcp::Reel> (
136 shared_ptr<dcp::ReelStereoPictureAsset> (new dcp::ReelStereoPictureAsset (mp, 0)),
137 shared_ptr<dcp::ReelSoundAsset> (new dcp::ReelSoundAsset (ms, 0))
143 d.write_xml (dcp::SMPTE, "OpenDCP 0.0.25", "OpenDCP 0.0.25", "2012-07-17T04:45:18+00:00", "Created by libdcp");
145 /* build/test/DCP/dcp_test2 is checked against test/ref/DCP/dcp_test2 by run/tests */
149 note (dcp::NoteType, string)
154 /** Test comparison of a DCP with itself */
155 BOOST_AUTO_TEST_CASE (dcp_test3)
157 dcp::DCP A ("test/ref/DCP/dcp_test1");
159 dcp::DCP B ("test/ref/DCP/dcp_test1");
162 BOOST_CHECK (A.equals (B, dcp::EqualityOptions(), boost::bind (¬e, _1, _2)));
165 /** Test comparison of a DCP with a different DCP */
166 BOOST_AUTO_TEST_CASE (dcp_test4)
168 dcp::DCP A ("test/ref/DCP/dcp_test1");
170 dcp::DCP B ("test/ref/DCP/dcp_test2");
173 BOOST_CHECK (!A.equals (B, dcp::EqualityOptions(), boost::bind (¬e, _1, _2)));
178 test_rewriting_sound(string name, bool modify)
180 using namespace boost::filesystem;
182 dcp::DCP A ("test/ref/DCP/dcp_test1");
185 BOOST_REQUIRE (!A.cpls().empty());
186 BOOST_REQUIRE (!A.cpls().front()->reels().empty());
187 auto A_picture = dynamic_pointer_cast<dcp::ReelMonoPictureAsset>(A.cpls().front()->reels().front()->main_picture());
188 BOOST_REQUIRE (A_picture);
189 auto A_sound = dynamic_pointer_cast<dcp::ReelSoundAsset>(A.cpls().front()->reels().front()->main_sound());
191 string const picture = "j2c_5279f9aa-94d7-42a6-b0e0-e4eaec4e2a15.mxf";
193 remove_all ("build/test/" + name);
194 dcp::DCP B ("build/test/" + name);
195 auto reel = make_shared<dcp::Reel>();
197 BOOST_REQUIRE (A_picture->mono_asset());
198 BOOST_REQUIRE (A_picture->mono_asset()->file());
199 copy_file (A_picture->mono_asset()->file().get(), path("build") / "test" / name / picture);
200 reel->add(make_shared<dcp::ReelMonoPictureAsset>(make_shared<dcp::MonoPictureAsset>(path("build") / "test" / name / picture), 0));
202 auto reader = A_sound->asset()->start_read();
203 auto sound = make_shared<dcp::SoundAsset>(A_sound->asset()->edit_rate(), A_sound->asset()->sampling_rate(), A_sound->asset()->channels(), dcp::LanguageTag("en-US"), dcp::SMPTE);
204 auto writer = sound->start_write(path("build") / "test" / name / "pcm_8246f87f-e1df-4c42-a290-f3b3069ff021.mxf", {});
206 bool need_to_modify = modify;
207 for (int i = 0; i < A_sound->asset()->intrinsic_duration(); ++i) {
208 auto sf = reader->get_frame (i);
209 float* out[sf->channels()];
210 for (int j = 0; j < sf->channels(); ++j) {
211 out[j] = new float[sf->samples()];
213 for (int j = 0; j < sf->samples(); ++j) {
214 for (int k = 0; k < sf->channels(); ++k) {
215 out[k][j] = static_cast<float>(sf->get(k, j)) / (1 << 23);
216 if (need_to_modify) {
217 out[k][j] += 1.0 / (1 << 23);
218 need_to_modify = false;
222 writer->write (out, sf->samples());
223 for (int j = 0; j < sf->channels(); ++j) {
229 reel->add(make_shared<dcp::ReelSoundAsset>(sound, 0));
230 reel->add(simple_markers());
232 auto cpl = make_shared<dcp::CPL>("A Test DCP", dcp::TRAILER);
236 B.write_xml (dcp::SMPTE);
238 dcp::EqualityOptions eq;
239 eq.reel_hashes_can_differ = true;
240 eq.max_audio_sample_error = 0;
242 BOOST_CHECK (!A.equals(B, eq, boost::bind(¬e, _1, _2)));
244 BOOST_CHECK (A.equals(B, eq, boost::bind(¬e, _1, _2)));
248 /** Test comparison of a DCP with another that has the same picture and the same (but re-written) sound */
249 BOOST_AUTO_TEST_CASE (dcp_test9)
251 test_rewriting_sound ("dcp_test9", false);
254 /** Test comparison of a DCP with another that has the same picture and very slightly modified sound */
255 BOOST_AUTO_TEST_CASE (dcp_test10)
257 test_rewriting_sound ("dcp_test10", true);
260 /** Test creation of a 2D DCP with an Atmos track */
261 BOOST_AUTO_TEST_CASE (dcp_test5)
265 /* Some known metadata */
266 dcp::MXFMetadata mxf_meta;
267 mxf_meta.company_name = "OpenDCP";
268 mxf_meta.product_name = "OpenDCP";
269 mxf_meta.product_version = "0.0.25";
271 /* We're making build/test/DCP/dcp_test5 */
272 boost::filesystem::remove_all ("build/test/DCP/dcp_test5");
273 boost::filesystem::create_directories ("build/test/DCP/dcp_test5");
274 dcp::DCP d ("build/test/DCP/dcp_test5");
275 shared_ptr<dcp::CPL> cpl (new dcp::CPL ("A Test DCP", dcp::FEATURE));
276 cpl->set_content_version (
277 dcp::ContentVersion("urn:uri:81fb54df-e1bf-4647-8788-ea7ba154375b_2012-07-17T04:45:18+00:00", "81fb54df-e1bf-4647-8788-ea7ba154375b_2012-07-17T04:45:18+00:00")
279 cpl->set_issuer ("OpenDCP 0.0.25");
280 cpl->set_creator ("OpenDCP 0.0.25");
281 cpl->set_issue_date ("2012-07-17T04:45:18+00:00");
282 cpl->set_annotation_text ("A Test DCP");
284 shared_ptr<dcp::MonoPictureAsset> mp (new dcp::MonoPictureAsset (dcp::Fraction (24, 1), dcp::SMPTE));
285 mp->set_metadata (mxf_meta);
286 shared_ptr<dcp::PictureAssetWriter> picture_writer = mp->start_write ("build/test/DCP/dcp_test5/video.mxf", false);
287 dcp::ArrayData j2c ("test/data/flat_red.j2c");
288 for (int i = 0; i < 24; ++i) {
289 picture_writer->write (j2c.data (), j2c.size ());
291 picture_writer->finalize ();
293 shared_ptr<dcp::SoundAsset> ms (new dcp::SoundAsset(dcp::Fraction(24, 1), 48000, 1, dcp::LanguageTag("en-GB"), dcp::SMPTE));
294 ms->set_metadata (mxf_meta);
295 shared_ptr<dcp::SoundAssetWriter> sound_writer = ms->start_write ("build/test/DCP/dcp_test5/audio.mxf", vector<dcp::Channel>());
299 SNDFILE* sndfile = sf_open ("test/data/1s_24-bit_48k_silence.wav", SFM_READ, &info);
300 BOOST_CHECK (sndfile);
301 float buffer[4096*6];
303 channels[0] = buffer;
305 sf_count_t N = sf_readf_float (sndfile, buffer, 4096);
306 sound_writer->write (channels, N);
312 sound_writer->finalize ();
314 shared_ptr<dcp::AtmosAsset> am (new dcp::AtmosAsset (private_test / "20160218_NameOfFilm_FTR_OV_EN_A_dcs_r01.mxf"));
316 cpl->add (shared_ptr<dcp::Reel> (
318 shared_ptr<dcp::ReelMonoPictureAsset> (new dcp::ReelMonoPictureAsset (mp, 0)),
319 shared_ptr<dcp::ReelSoundAsset> (new dcp::ReelSoundAsset (ms, 0)),
320 shared_ptr<dcp::ReelSubtitleAsset> (),
321 shared_ptr<dcp::ReelMarkersAsset> (),
322 shared_ptr<dcp::ReelAtmosAsset> (new dcp::ReelAtmosAsset (am, 0))
328 d.write_xml (dcp::SMPTE, "OpenDCP 0.0.25", "OpenDCP 0.0.25", "2012-07-17T04:45:18+00:00", "Created by libdcp");
330 /* build/test/DCP/dcp_test5 is checked against test/ref/DCP/dcp_test5 by run/tests */
333 /** Basic tests of reading a 2D DCP with an Atmos track */
334 BOOST_AUTO_TEST_CASE (dcp_test6)
336 dcp::DCP dcp ("test/ref/DCP/dcp_test5");
339 BOOST_REQUIRE_EQUAL (dcp.cpls().size(), 1);
340 BOOST_REQUIRE_EQUAL (dcp.cpls().front()->reels().size(), 1);
341 BOOST_CHECK (dcp.cpls().front()->reels().front()->main_picture());
342 BOOST_CHECK (dcp.cpls().front()->reels().front()->main_sound());
343 BOOST_CHECK (!dcp.cpls().front()->reels().front()->main_subtitle());
344 BOOST_CHECK (dcp.cpls().front()->reels().front()->atmos());
347 /** Test creation of a 2D Interop DCP from very simple inputs */
348 BOOST_AUTO_TEST_CASE (dcp_test7)
352 make_simple("build/test/DCP/dcp_test7")->write_xml(
353 dcp::INTEROP, "OpenDCP 0.0.25", "OpenDCP 0.0.25", "2012-07-17T04:45:18+00:00", "Created by libdcp"
356 /* build/test/DCP/dcp_test7 is checked against test/ref/DCP/dcp_test7 by run/tests */
359 /** Test reading of a DCP with multiple CPLs */
360 BOOST_AUTO_TEST_CASE (dcp_test8)
362 dcp::DCP dcp (private_test / "data/SMPTE_TST-B1PB2P_S_EN-EN-CCAP_5171-HI-VI_2K_ISDCF_20151123_DPPT_SMPTE_combo/");
365 BOOST_REQUIRE_EQUAL (dcp.cpls().size(), 2);
369 /** Test reading a DCP whose ASSETMAP contains assets not used by any PKL */
370 BOOST_AUTO_TEST_CASE (dcp_things_in_assetmap_not_in_pkl)
372 dcp::DCP dcp ("test/data/extra_assetmap");
373 BOOST_CHECK_NO_THROW (dcp.read());