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));
231 auto cpl = make_shared<dcp::CPL>("A Test DCP", dcp::TRAILER);
235 B.write_xml (dcp::SMPTE);
237 dcp::EqualityOptions eq;
238 eq.reel_hashes_can_differ = true;
239 eq.max_audio_sample_error = 0;
241 BOOST_CHECK (!A.equals(B, eq, boost::bind(¬e, _1, _2)));
243 BOOST_CHECK (A.equals(B, eq, boost::bind(¬e, _1, _2)));
247 /** Test comparison of a DCP with another that has the same picture and the same (but re-written) sound */
248 BOOST_AUTO_TEST_CASE (dcp_test9)
250 test_rewriting_sound ("dcp_test9", false);
253 /** Test comparison of a DCP with another that has the same picture and very slightly modified sound */
254 BOOST_AUTO_TEST_CASE (dcp_test10)
256 test_rewriting_sound ("dcp_test10", true);
259 /** Test creation of a 2D DCP with an Atmos track */
260 BOOST_AUTO_TEST_CASE (dcp_test5)
264 /* Some known metadata */
265 dcp::MXFMetadata mxf_meta;
266 mxf_meta.company_name = "OpenDCP";
267 mxf_meta.product_name = "OpenDCP";
268 mxf_meta.product_version = "0.0.25";
270 /* We're making build/test/DCP/dcp_test5 */
271 boost::filesystem::remove_all ("build/test/DCP/dcp_test5");
272 boost::filesystem::create_directories ("build/test/DCP/dcp_test5");
273 dcp::DCP d ("build/test/DCP/dcp_test5");
274 shared_ptr<dcp::CPL> cpl (new dcp::CPL ("A Test DCP", dcp::FEATURE));
275 cpl->set_content_version (
276 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")
278 cpl->set_issuer ("OpenDCP 0.0.25");
279 cpl->set_creator ("OpenDCP 0.0.25");
280 cpl->set_issue_date ("2012-07-17T04:45:18+00:00");
281 cpl->set_annotation_text ("A Test DCP");
283 shared_ptr<dcp::MonoPictureAsset> mp (new dcp::MonoPictureAsset (dcp::Fraction (24, 1), dcp::SMPTE));
284 mp->set_metadata (mxf_meta);
285 shared_ptr<dcp::PictureAssetWriter> picture_writer = mp->start_write ("build/test/DCP/dcp_test5/video.mxf", false);
286 dcp::ArrayData j2c ("test/data/flat_red.j2c");
287 for (int i = 0; i < 24; ++i) {
288 picture_writer->write (j2c.data (), j2c.size ());
290 picture_writer->finalize ();
292 shared_ptr<dcp::SoundAsset> ms (new dcp::SoundAsset(dcp::Fraction(24, 1), 48000, 1, dcp::LanguageTag("en-GB"), dcp::SMPTE));
293 ms->set_metadata (mxf_meta);
294 shared_ptr<dcp::SoundAssetWriter> sound_writer = ms->start_write ("build/test/DCP/dcp_test5/audio.mxf", vector<dcp::Channel>());
298 SNDFILE* sndfile = sf_open ("test/data/1s_24-bit_48k_silence.wav", SFM_READ, &info);
299 BOOST_CHECK (sndfile);
300 float buffer[4096*6];
302 channels[0] = buffer;
304 sf_count_t N = sf_readf_float (sndfile, buffer, 4096);
305 sound_writer->write (channels, N);
311 sound_writer->finalize ();
313 shared_ptr<dcp::AtmosAsset> am (new dcp::AtmosAsset (private_test / "20160218_NameOfFilm_FTR_OV_EN_A_dcs_r01.mxf"));
315 cpl->add (shared_ptr<dcp::Reel> (
317 shared_ptr<dcp::ReelMonoPictureAsset> (new dcp::ReelMonoPictureAsset (mp, 0)),
318 shared_ptr<dcp::ReelSoundAsset> (new dcp::ReelSoundAsset (ms, 0)),
319 shared_ptr<dcp::ReelSubtitleAsset> (),
320 shared_ptr<dcp::ReelMarkersAsset> (),
321 shared_ptr<dcp::ReelAtmosAsset> (new dcp::ReelAtmosAsset (am, 0))
327 d.write_xml (dcp::SMPTE, "OpenDCP 0.0.25", "OpenDCP 0.0.25", "2012-07-17T04:45:18+00:00", "Created by libdcp");
329 /* build/test/DCP/dcp_test5 is checked against test/ref/DCP/dcp_test5 by run/tests */
332 /** Basic tests of reading a 2D DCP with an Atmos track */
333 BOOST_AUTO_TEST_CASE (dcp_test6)
335 dcp::DCP dcp ("test/ref/DCP/dcp_test5");
338 BOOST_REQUIRE_EQUAL (dcp.cpls().size(), 1);
339 BOOST_REQUIRE_EQUAL (dcp.cpls().front()->reels().size(), 1);
340 BOOST_CHECK (dcp.cpls().front()->reels().front()->main_picture());
341 BOOST_CHECK (dcp.cpls().front()->reels().front()->main_sound());
342 BOOST_CHECK (!dcp.cpls().front()->reels().front()->main_subtitle());
343 BOOST_CHECK (dcp.cpls().front()->reels().front()->atmos());
346 /** Test creation of a 2D Interop DCP from very simple inputs */
347 BOOST_AUTO_TEST_CASE (dcp_test7)
351 make_simple("build/test/DCP/dcp_test7")->write_xml(
352 dcp::INTEROP, "OpenDCP 0.0.25", "OpenDCP 0.0.25", "2012-07-17T04:45:18+00:00", "Created by libdcp"
355 /* build/test/DCP/dcp_test7 is checked against test/ref/DCP/dcp_test7 by run/tests */
358 /** Test reading of a DCP with multiple CPLs */
359 BOOST_AUTO_TEST_CASE (dcp_test8)
361 dcp::DCP dcp (private_test / "data/SMPTE_TST-B1PB2P_S_EN-EN-CCAP_5171-HI-VI_2K_ISDCF_20151123_DPPT_SMPTE_combo/");
364 BOOST_REQUIRE_EQUAL (dcp.cpls().size(), 2);
368 /** Test reading a DCP whose ASSETMAP contains assets not used by any PKL */
369 BOOST_AUTO_TEST_CASE (dcp_things_in_assetmap_not_in_pkl)
371 dcp::DCP dcp ("test/data/extra_assetmap");
372 BOOST_CHECK_NO_THROW (dcp.read());