fb3a605c694c537bf1688088b5aa31a5d5e876ad
[libdcp.git] / test / dcp_test.cc
1 /*
2     Copyright (C) 2013-2020 Carl Hetherington <cth@carlh.net>
3
4     This file is part of libdcp.
5
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.
10
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.
15
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/>.
18
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
23     including the two.
24
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.
32 */
33
34 #include "dcp.h"
35 #include "metadata.h"
36 #include "cpl.h"
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"
44 #include "reel.h"
45 #include "test.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>
52 #include <sndfile.h>
53 #include <boost/test/unit_test.hpp>
54
55 using std::string;
56 using std::vector;
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;
62 #endif
63
64
65 /** Test creation of a 2D SMPTE DCP from very simple inputs */
66 BOOST_AUTO_TEST_CASE (dcp_test1)
67 {
68         RNGFixer fixer;
69
70         make_simple("build/test/DCP/dcp_test1")->write_xml(
71                 dcp::Standard::SMPTE, "OpenDCP 0.0.25", "OpenDCP 0.0.25", "2012-07-17T04:45:18+00:00", "A Test DCP"
72                 );
73
74         /* build/test/DCP/dcp_test1 is checked against test/ref/DCP/dcp_test1 by run/tests */
75 }
76
77 /** Test creation of a 3D DCP from very simple inputs */
78 BOOST_AUTO_TEST_CASE (dcp_test2)
79 {
80         RNGFixer fix;
81
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";
87
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         auto cpl = make_shared<dcp::CPL>("A Test DCP", dcp::ContentKind::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")
95                 );
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");
100
101         shared_ptr<dcp::StereoPictureAsset> mp (new dcp::StereoPictureAsset (dcp::Fraction (24, 1), dcp::Standard::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) {
106                 /* Left */
107                 picture_writer->write (j2c.data (), j2c.size ());
108                 /* Right */
109                 picture_writer->write (j2c.data (), j2c.size ());
110         }
111         picture_writer->finalize ();
112
113         shared_ptr<dcp::SoundAsset> ms (new dcp::SoundAsset(dcp::Fraction(24, 1), 48000, 1, dcp::LanguageTag("en-GB"), dcp::Standard::SMPTE));
114         ms->set_metadata (mxf_meta);
115         shared_ptr<dcp::SoundAssetWriter> sound_writer = ms->start_write ("build/test/DCP/dcp_test2/audio.mxf");
116
117         SF_INFO info;
118         info.format = 0;
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];
122         float* channels[1];
123         channels[0] = buffer;
124         while (1) {
125                 sf_count_t N = sf_readf_float (sndfile, buffer, 4096);
126                 sound_writer->write (channels, N);
127                 if (N < 4096) {
128                         break;
129                 }
130         }
131
132         sound_writer->finalize ();
133
134         cpl->add (make_shared<dcp::Reel>(
135                           make_shared<dcp::ReelStereoPictureAsset>(mp, 0),
136                           make_shared<dcp::ReelSoundAsset>(ms, 0)
137                           ));
138
139         d.add (cpl);
140
141         d.write_xml (dcp::Standard::SMPTE, "OpenDCP 0.0.25", "OpenDCP 0.0.25", "2012-07-17T04:45:18+00:00", "Created by libdcp");
142
143         /* build/test/DCP/dcp_test2 is checked against test/ref/DCP/dcp_test2 by run/tests */
144 }
145
146 static void
147 note (dcp::NoteType, string)
148 {
149
150 }
151
152 /** Test comparison of a DCP with itself */
153 BOOST_AUTO_TEST_CASE (dcp_test3)
154 {
155         dcp::DCP A ("test/ref/DCP/dcp_test1");
156         A.read ();
157         dcp::DCP B ("test/ref/DCP/dcp_test1");
158         B.read ();
159
160         BOOST_CHECK (A.equals (B, dcp::EqualityOptions(), boost::bind (&note, _1, _2)));
161 }
162
163 /** Test comparison of a DCP with a different DCP */
164 BOOST_AUTO_TEST_CASE (dcp_test4)
165 {
166         dcp::DCP A ("test/ref/DCP/dcp_test1");
167         A.read ();
168         dcp::DCP B ("test/ref/DCP/dcp_test2");
169         B.read ();
170
171         BOOST_CHECK (!A.equals (B, dcp::EqualityOptions(), boost::bind (&note, _1, _2)));
172 }
173
174 static
175 void
176 test_rewriting_sound(string name, bool modify)
177 {
178         using namespace boost::filesystem;
179
180         dcp::DCP A ("test/ref/DCP/dcp_test1");
181         A.read ();
182
183         BOOST_REQUIRE (!A.cpls().empty());
184         BOOST_REQUIRE (!A.cpls().front()->reels().empty());
185         auto A_picture = dynamic_pointer_cast<dcp::ReelMonoPictureAsset>(A.cpls().front()->reels().front()->main_picture());
186         BOOST_REQUIRE (A_picture);
187         auto A_sound = dynamic_pointer_cast<dcp::ReelSoundAsset>(A.cpls().front()->reels().front()->main_sound());
188
189         string const picture = "j2c_5279f9aa-94d7-42a6-b0e0-e4eaec4e2a15.mxf";
190
191         remove_all ("build/test/" + name);
192         dcp::DCP B ("build/test/" + name);
193         auto reel = make_shared<dcp::Reel>();
194
195         BOOST_REQUIRE (A_picture->mono_asset());
196         BOOST_REQUIRE (A_picture->mono_asset()->file());
197         copy_file (A_picture->mono_asset()->file().get(), path("build") / "test" / name / picture);
198         reel->add(make_shared<dcp::ReelMonoPictureAsset>(make_shared<dcp::MonoPictureAsset>(path("build") / "test" / name / picture), 0));
199
200         auto reader = A_sound->asset()->start_read();
201         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::Standard::SMPTE);
202         auto writer = sound->start_write(path("build") / "test" / name / "pcm_8246f87f-e1df-4c42-a290-f3b3069ff021.mxf", {});
203
204         bool need_to_modify = modify;
205         for (int i = 0; i < A_sound->asset()->intrinsic_duration(); ++i) {
206                 auto sf = reader->get_frame (i);
207                 float* out[sf->channels()];
208                 for (int j = 0; j < sf->channels(); ++j) {
209                         out[j] = new float[sf->samples()];
210                 }
211                 for (int j = 0; j < sf->samples(); ++j) {
212                         for (int k = 0; k < sf->channels(); ++k) {
213                                 out[k][j] = static_cast<float>(sf->get(k, j)) / (1 << 23);
214                                 if (need_to_modify) {
215                                         out[k][j] += 1.0 / (1 << 23);
216                                         need_to_modify = false;
217                                 }
218                         }
219                 }
220                 writer->write (out, sf->samples());
221                 for (int j = 0; j < sf->channels(); ++j) {
222                         delete[] out[j];
223                 }
224         }
225         writer->finalize();
226
227         reel->add(make_shared<dcp::ReelSoundAsset>(sound, 0));
228         reel->add(simple_markers());
229
230         auto cpl = make_shared<dcp::CPL>("A Test DCP", dcp::ContentKind::TRAILER);
231         cpl->add (reel);
232
233         B.add (cpl);
234         B.write_xml (dcp::Standard::SMPTE);
235
236         dcp::EqualityOptions eq;
237         eq.reel_hashes_can_differ = true;
238         eq.max_audio_sample_error = 0;
239         if (modify) {
240                 BOOST_CHECK (!A.equals(B, eq, boost::bind(&note, _1, _2)));
241         } else {
242                 BOOST_CHECK (A.equals(B, eq, boost::bind(&note, _1, _2)));
243         }
244 }
245
246 /** Test comparison of a DCP with another that has the same picture and the same (but re-written) sound */
247 BOOST_AUTO_TEST_CASE (dcp_test9)
248 {
249         test_rewriting_sound ("dcp_test9", false);
250 }
251
252 /** Test comparison of a DCP with another that has the same picture and very slightly modified sound */
253 BOOST_AUTO_TEST_CASE (dcp_test10)
254 {
255         test_rewriting_sound ("dcp_test10", true);
256 }
257
258 /** Test creation of a 2D DCP with an Atmos track */
259 BOOST_AUTO_TEST_CASE (dcp_test5)
260 {
261         RNGFixer fix;
262
263         /* Some known metadata */
264         dcp::MXFMetadata mxf_meta;
265         mxf_meta.company_name = "OpenDCP";
266         mxf_meta.product_name = "OpenDCP";
267         mxf_meta.product_version = "0.0.25";
268
269         /* We're making build/test/DCP/dcp_test5 */
270         boost::filesystem::remove_all ("build/test/DCP/dcp_test5");
271         boost::filesystem::create_directories ("build/test/DCP/dcp_test5");
272         dcp::DCP d ("build/test/DCP/dcp_test5");
273         auto cpl = make_shared<dcp::CPL>("A Test DCP", dcp::ContentKind::FEATURE);
274         cpl->set_content_version (
275                 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")
276                 );
277         cpl->set_issuer ("OpenDCP 0.0.25");
278         cpl->set_creator ("OpenDCP 0.0.25");
279         cpl->set_issue_date ("2012-07-17T04:45:18+00:00");
280         cpl->set_annotation_text ("A Test DCP");
281
282         auto mp = make_shared<dcp::MonoPictureAsset>(dcp::Fraction (24, 1), dcp::Standard::SMPTE);
283         mp->set_metadata (mxf_meta);
284         shared_ptr<dcp::PictureAssetWriter> picture_writer = mp->start_write ("build/test/DCP/dcp_test5/video.mxf", false);
285         dcp::ArrayData j2c ("test/data/flat_red.j2c");
286         for (int i = 0; i < 24; ++i) {
287                 picture_writer->write (j2c.data (), j2c.size ());
288         }
289         picture_writer->finalize ();
290
291         auto ms = make_shared<dcp::SoundAsset>(dcp::Fraction(24, 1), 48000, 1, dcp::LanguageTag("en-GB"), dcp::Standard::SMPTE);
292         ms->set_metadata (mxf_meta);
293         shared_ptr<dcp::SoundAssetWriter> sound_writer = ms->start_write ("build/test/DCP/dcp_test5/audio.mxf");
294
295         SF_INFO info;
296         info.format = 0;
297         SNDFILE* sndfile = sf_open ("test/data/1s_24-bit_48k_silence.wav", SFM_READ, &info);
298         BOOST_CHECK (sndfile);
299         float buffer[4096*6];
300         float* channels[1];
301         channels[0] = buffer;
302         while (true) {
303                 sf_count_t N = sf_readf_float (sndfile, buffer, 4096);
304                 sound_writer->write (channels, N);
305                 if (N < 4096) {
306                         break;
307                 }
308         }
309
310         sound_writer->finalize ();
311
312         shared_ptr<dcp::AtmosAsset> am (new dcp::AtmosAsset (private_test / "20160218_NameOfFilm_FTR_OV_EN_A_dcs_r01.mxf"));
313
314         cpl->add (shared_ptr<dcp::Reel> (
315                           new dcp::Reel (
316                                   shared_ptr<dcp::ReelMonoPictureAsset> (new dcp::ReelMonoPictureAsset (mp, 0)),
317                                   shared_ptr<dcp::ReelSoundAsset> (new dcp::ReelSoundAsset (ms, 0)),
318                                   shared_ptr<dcp::ReelSubtitleAsset> (),
319                                   shared_ptr<dcp::ReelMarkersAsset> (),
320                                   shared_ptr<dcp::ReelAtmosAsset> (new dcp::ReelAtmosAsset (am, 0))
321                                   )
322                           ));
323
324         d.add (cpl);
325
326         d.write_xml (dcp::Standard::SMPTE, "OpenDCP 0.0.25", "OpenDCP 0.0.25", "2012-07-17T04:45:18+00:00", "Created by libdcp");
327
328         /* build/test/DCP/dcp_test5 is checked against test/ref/DCP/dcp_test5 by run/tests */
329 }
330
331 /** Basic tests of reading a 2D DCP with an Atmos track */
332 BOOST_AUTO_TEST_CASE (dcp_test6)
333 {
334         dcp::DCP dcp ("test/ref/DCP/dcp_test5");
335         dcp.read ();
336
337         BOOST_REQUIRE_EQUAL (dcp.cpls().size(), 1);
338         BOOST_REQUIRE_EQUAL (dcp.cpls().front()->reels().size(), 1);
339         BOOST_CHECK (dcp.cpls().front()->reels().front()->main_picture());
340         BOOST_CHECK (dcp.cpls().front()->reels().front()->main_sound());
341         BOOST_CHECK (!dcp.cpls().front()->reels().front()->main_subtitle());
342         BOOST_CHECK (dcp.cpls().front()->reels().front()->atmos());
343 }
344
345 /** Test creation of a 2D Interop DCP from very simple inputs */
346 BOOST_AUTO_TEST_CASE (dcp_test7)
347 {
348         RNGFixer fix;
349
350         make_simple("build/test/DCP/dcp_test7")->write_xml(
351                 dcp::Standard::INTEROP, "OpenDCP 0.0.25", "OpenDCP 0.0.25", "2012-07-17T04:45:18+00:00", "Created by libdcp"
352                 );
353
354         /* build/test/DCP/dcp_test7 is checked against test/ref/DCP/dcp_test7 by run/tests */
355 }
356
357 /** Test reading of a DCP with multiple CPLs */
358 BOOST_AUTO_TEST_CASE (dcp_test8)
359 {
360         dcp::DCP dcp (private_test / "data/SMPTE_TST-B1PB2P_S_EN-EN-CCAP_5171-HI-VI_2K_ISDCF_20151123_DPPT_SMPTE_combo/");
361         dcp.read ();
362
363         BOOST_REQUIRE_EQUAL (dcp.cpls().size(), 2);
364 }
365
366
367 /** Test reading a DCP whose ASSETMAP contains assets not used by any PKL */
368 BOOST_AUTO_TEST_CASE (dcp_things_in_assetmap_not_in_pkl)
369 {
370         dcp::DCP dcp ("test/data/extra_assetmap");
371         BOOST_CHECK_NO_THROW (dcp.read());
372 }