Bv2.1 7.2.2: Check that subtitle languages are the same for all reels.
[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 <asdcp/KM_util.h>
51 #include <sndfile.h>
52 #include <boost/test/unit_test.hpp>
53
54 using std::string;
55 using std::vector;
56 using std::dynamic_pointer_cast;
57 using std::shared_ptr;
58 #if BOOST_VERSION >= 106100
59 using namespace boost::placeholders;
60 #endif
61
62
63 /** Test creation of a 2D SMPTE DCP from very simple inputs */
64 BOOST_AUTO_TEST_CASE (dcp_test1)
65 {
66         RNGFixer fixer;
67
68         make_simple("build/test/DCP/dcp_test1")->write_xml(
69                 dcp::SMPTE, "OpenDCP 0.0.25", "OpenDCP 0.0.25", "2012-07-17T04:45:18+00:00", "Created by libdcp"
70                 );
71
72         /* build/test/DCP/dcp_test1 is checked against test/ref/DCP/dcp_test1 by run/tests */
73 }
74
75 /** Test creation of a 3D DCP from very simple inputs */
76 BOOST_AUTO_TEST_CASE (dcp_test2)
77 {
78         RNGFixer fix;
79
80         /* Some known metadata */
81         dcp::MXFMetadata mxf_meta;
82         mxf_meta.company_name = "OpenDCP";
83         mxf_meta.product_name = "OpenDCP";
84         mxf_meta.product_version = "0.0.25";
85
86         /* We're making build/test/DCP/dcp_test2 */
87         boost::filesystem::remove_all ("build/test/DCP/dcp_test2");
88         boost::filesystem::create_directories ("build/test/DCP/dcp_test2");
89         dcp::DCP d ("build/test/DCP/dcp_test2");
90         shared_ptr<dcp::CPL> cpl (new dcp::CPL ("A Test DCP", dcp::FEATURE));
91         cpl->set_content_version (
92                 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")
93                 );
94         cpl->set_issuer ("OpenDCP 0.0.25");
95         cpl->set_creator ("OpenDCP 0.0.25");
96         cpl->set_issue_date ("2012-07-17T04:45:18+00:00");
97         cpl->set_annotation_text ("A Test DCP");
98
99         shared_ptr<dcp::StereoPictureAsset> mp (new dcp::StereoPictureAsset (dcp::Fraction (24, 1), dcp::SMPTE));
100         mp->set_metadata (mxf_meta);
101         shared_ptr<dcp::PictureAssetWriter> picture_writer = mp->start_write ("build/test/DCP/dcp_test2/video.mxf", false);
102         dcp::ArrayData j2c ("test/data/flat_red.j2c");
103         for (int i = 0; i < 24; ++i) {
104                 /* Left */
105                 picture_writer->write (j2c.data (), j2c.size ());
106                 /* Right */
107                 picture_writer->write (j2c.data (), j2c.size ());
108         }
109         picture_writer->finalize ();
110
111         shared_ptr<dcp::SoundAsset> ms (new dcp::SoundAsset(dcp::Fraction(24, 1), 48000, 1, dcp::LanguageTag("en-GB"), dcp::SMPTE));
112         ms->set_metadata (mxf_meta);
113         shared_ptr<dcp::SoundAssetWriter> sound_writer = ms->start_write ("build/test/DCP/dcp_test2/audio.mxf", vector<dcp::Channel>());
114
115         SF_INFO info;
116         info.format = 0;
117         SNDFILE* sndfile = sf_open ("test/data/1s_24-bit_48k_silence.wav", SFM_READ, &info);
118         BOOST_CHECK (sndfile);
119         float buffer[4096*6];
120         float* channels[1];
121         channels[0] = buffer;
122         while (1) {
123                 sf_count_t N = sf_readf_float (sndfile, buffer, 4096);
124                 sound_writer->write (channels, N);
125                 if (N < 4096) {
126                         break;
127                 }
128         }
129
130         sound_writer->finalize ();
131
132         cpl->add (shared_ptr<dcp::Reel> (
133                           new dcp::Reel (
134                                   shared_ptr<dcp::ReelStereoPictureAsset> (new dcp::ReelStereoPictureAsset (mp, 0)),
135                                   shared_ptr<dcp::ReelSoundAsset> (new dcp::ReelSoundAsset (ms, 0))
136                                   )
137                           ));
138
139         d.add (cpl);
140
141         d.write_xml (dcp::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         dcp::DCP A ("test/ref/DCP/dcp_test1");
179         A.read ();
180
181         BOOST_REQUIRE (!A.cpls().empty());
182         BOOST_REQUIRE (!A.cpls().front()->reels().empty());
183         shared_ptr<dcp::ReelMonoPictureAsset> A_picture = dynamic_pointer_cast<dcp::ReelMonoPictureAsset>(A.cpls().front()->reels().front()->main_picture());
184         BOOST_REQUIRE (A_picture);
185         shared_ptr<dcp::ReelSoundAsset> A_sound = dynamic_pointer_cast<dcp::ReelSoundAsset>(A.cpls().front()->reels().front()->main_sound());
186
187         boost::filesystem::remove_all ("build/test/" + name);
188         dcp::DCP B ("build/test/" + name);
189         shared_ptr<dcp::Reel> reel(new dcp::Reel());
190
191         BOOST_REQUIRE (A_picture->mono_asset());
192         BOOST_REQUIRE (A_picture->mono_asset()->file());
193         boost::filesystem::copy_file (A_picture->mono_asset()->file().get(), "build/test/" +name + "/picture.mxf");
194         reel->add(
195                 shared_ptr<dcp::ReelMonoPictureAsset>(
196                         new dcp::ReelMonoPictureAsset(shared_ptr<dcp::MonoPictureAsset>(new dcp::MonoPictureAsset("build/test/" + name + "/picture.mxf")), 0)
197                         )
198                 );
199
200         shared_ptr<dcp::SoundAssetReader> reader = A_sound->asset()->start_read();
201         shared_ptr<dcp::SoundAsset> sound(new dcp::SoundAsset(A_sound->asset()->edit_rate(), A_sound->asset()->sampling_rate(), A_sound->asset()->channels(), dcp::LanguageTag("en-US"), dcp::SMPTE));
202         shared_ptr<dcp::SoundAssetWriter> writer = sound->start_write("build/test/" + name + "/sound.mxf", vector<dcp::Channel>());
203
204         bool need_to_modify = modify;
205         for (int i = 0; i < A_sound->asset()->intrinsic_duration(); ++i) {
206                 shared_ptr<const dcp::SoundFrame> 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(shared_ptr<dcp::ReelSoundAsset>(new dcp::ReelSoundAsset(sound, 0)));
228
229         shared_ptr<dcp::CPL> cpl(new dcp::CPL("A Test DCP", dcp::FEATURE));
230         cpl->add (reel);
231
232         B.add (cpl);
233         B.write_xml (dcp::SMPTE);
234
235         dcp::EqualityOptions eq;
236         eq.reel_hashes_can_differ = true;
237         eq.max_audio_sample_error = 0;
238         if (modify) {
239                 BOOST_CHECK (!A.equals(B, eq, boost::bind(&note, _1, _2)));
240         } else {
241                 BOOST_CHECK (A.equals(B, eq, boost::bind(&note, _1, _2)));
242         }
243 }
244
245 /** Test comparison of a DCP with another that has the same picture and the same (but re-written) sound */
246 BOOST_AUTO_TEST_CASE (dcp_test9)
247 {
248         test_rewriting_sound ("dcp_test9", false);
249 }
250
251 /** Test comparison of a DCP with another that has the same picture and very slightly modified sound */
252 BOOST_AUTO_TEST_CASE (dcp_test10)
253 {
254         test_rewriting_sound ("dcp_test10", true);
255 }
256
257 /** Test creation of a 2D DCP with an Atmos track */
258 BOOST_AUTO_TEST_CASE (dcp_test5)
259 {
260         RNGFixer fix;
261
262         /* Some known metadata */
263         dcp::MXFMetadata mxf_meta;
264         mxf_meta.company_name = "OpenDCP";
265         mxf_meta.product_name = "OpenDCP";
266         mxf_meta.product_version = "0.0.25";
267
268         /* We're making build/test/DCP/dcp_test5 */
269         boost::filesystem::remove_all ("build/test/DCP/dcp_test5");
270         boost::filesystem::create_directories ("build/test/DCP/dcp_test5");
271         dcp::DCP d ("build/test/DCP/dcp_test5");
272         shared_ptr<dcp::CPL> cpl (new dcp::CPL ("A Test DCP", dcp::FEATURE));
273         cpl->set_content_version (
274                 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")
275                 );
276         cpl->set_issuer ("OpenDCP 0.0.25");
277         cpl->set_creator ("OpenDCP 0.0.25");
278         cpl->set_issue_date ("2012-07-17T04:45:18+00:00");
279         cpl->set_annotation_text ("A Test DCP");
280
281         shared_ptr<dcp::MonoPictureAsset> mp (new dcp::MonoPictureAsset (dcp::Fraction (24, 1), dcp::SMPTE));
282         mp->set_metadata (mxf_meta);
283         shared_ptr<dcp::PictureAssetWriter> picture_writer = mp->start_write ("build/test/DCP/dcp_test5/video.mxf", false);
284         dcp::ArrayData j2c ("test/data/flat_red.j2c");
285         for (int i = 0; i < 24; ++i) {
286                 picture_writer->write (j2c.data (), j2c.size ());
287         }
288         picture_writer->finalize ();
289
290         shared_ptr<dcp::SoundAsset> ms (new dcp::SoundAsset(dcp::Fraction(24, 1), 48000, 1, dcp::LanguageTag("en-GB"), dcp::SMPTE));
291         ms->set_metadata (mxf_meta);
292         shared_ptr<dcp::SoundAssetWriter> sound_writer = ms->start_write ("build/test/DCP/dcp_test5/audio.mxf", vector<dcp::Channel>());
293
294         SF_INFO info;
295         info.format = 0;
296         SNDFILE* sndfile = sf_open ("test/data/1s_24-bit_48k_silence.wav", SFM_READ, &info);
297         BOOST_CHECK (sndfile);
298         float buffer[4096*6];
299         float* channels[1];
300         channels[0] = buffer;
301         while (true) {
302                 sf_count_t N = sf_readf_float (sndfile, buffer, 4096);
303                 sound_writer->write (channels, N);
304                 if (N < 4096) {
305                         break;
306                 }
307         }
308
309         sound_writer->finalize ();
310
311         shared_ptr<dcp::AtmosAsset> am (new dcp::AtmosAsset (private_test / "20160218_NameOfFilm_FTR_OV_EN_A_dcs_r01.mxf"));
312
313         cpl->add (shared_ptr<dcp::Reel> (
314                           new dcp::Reel (
315                                   shared_ptr<dcp::ReelMonoPictureAsset> (new dcp::ReelMonoPictureAsset (mp, 0)),
316                                   shared_ptr<dcp::ReelSoundAsset> (new dcp::ReelSoundAsset (ms, 0)),
317                                   shared_ptr<dcp::ReelSubtitleAsset> (),
318                                   shared_ptr<dcp::ReelMarkersAsset> (),
319                                   shared_ptr<dcp::ReelAtmosAsset> (new dcp::ReelAtmosAsset (am, 0))
320                                   )
321                           ));
322
323         d.add (cpl);
324
325         d.write_xml (dcp::SMPTE, "OpenDCP 0.0.25", "OpenDCP 0.0.25", "2012-07-17T04:45:18+00:00", "Created by libdcp");
326
327         /* build/test/DCP/dcp_test5 is checked against test/ref/DCP/dcp_test5 by run/tests */
328 }
329
330 /** Basic tests of reading a 2D DCP with an Atmos track */
331 BOOST_AUTO_TEST_CASE (dcp_test6)
332 {
333         dcp::DCP dcp ("test/ref/DCP/dcp_test5");
334         dcp.read ();
335
336         BOOST_REQUIRE_EQUAL (dcp.cpls().size(), 1);
337         BOOST_REQUIRE_EQUAL (dcp.cpls().front()->reels().size(), 1);
338         BOOST_CHECK (dcp.cpls().front()->reels().front()->main_picture());
339         BOOST_CHECK (dcp.cpls().front()->reels().front()->main_sound());
340         BOOST_CHECK (!dcp.cpls().front()->reels().front()->main_subtitle());
341         BOOST_CHECK (dcp.cpls().front()->reels().front()->atmos());
342 }
343
344 /** Test creation of a 2D Interop DCP from very simple inputs */
345 BOOST_AUTO_TEST_CASE (dcp_test7)
346 {
347         RNGFixer fix;
348
349         make_simple("build/test/DCP/dcp_test7")->write_xml(
350                 dcp::INTEROP,  "OpenDCP 0.0.25", "OpenDCP 0.0.25", "2012-07-17T04:45:18+00:00", "Created by libdcp"
351                 );
352
353         /* build/test/DCP/dcp_test7 is checked against test/ref/DCP/dcp_test7 by run/tests */
354 }
355
356 /** Test reading of a DCP with multiple CPLs */
357 BOOST_AUTO_TEST_CASE (dcp_test8)
358 {
359         dcp::DCP dcp (private_test / "data/SMPTE_TST-B1PB2P_S_EN-EN-CCAP_5171-HI-VI_2K_ISDCF_20151123_DPPT_SMPTE_combo/");
360         dcp.read ();
361
362         BOOST_REQUIRE_EQUAL (dcp.cpls().size(), 2);
363 }
364
365
366 /** Test reading a DCP whose ASSETMAP contains assets not used by any PKL */
367 BOOST_AUTO_TEST_CASE (dcp_things_in_assetmap_not_in_pkl)
368 {
369         dcp::DCP dcp ("test/data/extra_assetmap");
370         BOOST_CHECK_NO_THROW (dcp.read());
371 }