Remove 32x32 test image.
[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 "file.h"
47 #include "reel_mono_picture_asset.h"
48 #include "reel_stereo_picture_asset.h"
49 #include "reel_sound_asset.h"
50 #include "reel_atmos_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 #if BOOST_VERSION >= 106100
60 using namespace boost::placeholders;
61 #endif
62
63
64 /** Test creation of a 2D SMPTE DCP from very simple inputs */
65 BOOST_AUTO_TEST_CASE (dcp_test1)
66 {
67         RNGFixer fixer;
68
69         make_simple("build/test/DCP/dcp_test1")->write_xml(
70                 dcp::SMPTE, "OpenDCP 0.0.25", "OpenDCP 0.0.25", "2012-07-17T04:45:18+00:00", "Created by libdcp"
71                 );
72
73         /* build/test/DCP/dcp_test1 is checked against test/ref/DCP/dcp_test1 by run/tests */
74 }
75
76 /** Test creation of a 3D DCP from very simple inputs */
77 BOOST_AUTO_TEST_CASE (dcp_test2)
78 {
79         RNGFixer fix;
80
81         /* Some known metadata */
82         dcp::MXFMetadata mxf_meta;
83         mxf_meta.company_name = "OpenDCP";
84         mxf_meta.product_name = "OpenDCP";
85         mxf_meta.product_version = "0.0.25";
86
87         /* We're making build/test/DCP/dcp_test2 */
88         boost::filesystem::remove_all ("build/test/DCP/dcp_test2");
89         boost::filesystem::create_directories ("build/test/DCP/dcp_test2");
90         dcp::DCP d ("build/test/DCP/dcp_test2");
91         shared_ptr<dcp::CPL> cpl (new dcp::CPL ("A Test DCP", dcp::FEATURE));
92         cpl->set_content_version (
93                 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")
94                 );
95         cpl->set_issuer ("OpenDCP 0.0.25");
96         cpl->set_creator ("OpenDCP 0.0.25");
97         cpl->set_issue_date ("2012-07-17T04:45:18+00:00");
98         cpl->set_annotation_text ("A Test DCP");
99
100         shared_ptr<dcp::StereoPictureAsset> mp (new dcp::StereoPictureAsset (dcp::Fraction (24, 1), dcp::SMPTE));
101         mp->set_metadata (mxf_meta);
102         shared_ptr<dcp::PictureAssetWriter> picture_writer = mp->start_write ("build/test/DCP/dcp_test2/video.mxf", false);
103         dcp::File j2c ("test/data/flat_red.j2c");
104         for (int i = 0; i < 24; ++i) {
105                 /* Left */
106                 picture_writer->write (j2c.data (), j2c.size ());
107                 /* Right */
108                 picture_writer->write (j2c.data (), j2c.size ());
109         }
110         picture_writer->finalize ();
111
112         shared_ptr<dcp::SoundAsset> ms (new dcp::SoundAsset(dcp::Fraction(24, 1), 48000, 1, dcp::LanguageTag("en-GB"), dcp::SMPTE));
113         ms->set_metadata (mxf_meta);
114         shared_ptr<dcp::SoundAssetWriter> sound_writer = ms->start_write ("build/test/DCP/dcp_test2/audio.mxf", vector<dcp::Channel>());
115
116         SF_INFO info;
117         info.format = 0;
118         SNDFILE* sndfile = sf_open ("test/data/1s_24-bit_48k_silence.wav", SFM_READ, &info);
119         BOOST_CHECK (sndfile);
120         float buffer[4096*6];
121         float* channels[1];
122         channels[0] = buffer;
123         while (1) {
124                 sf_count_t N = sf_readf_float (sndfile, buffer, 4096);
125                 sound_writer->write (channels, N);
126                 if (N < 4096) {
127                         break;
128                 }
129         }
130
131         sound_writer->finalize ();
132
133         cpl->add (shared_ptr<dcp::Reel> (
134                           new dcp::Reel (
135                                   shared_ptr<dcp::ReelStereoPictureAsset> (new dcp::ReelStereoPictureAsset (mp, 0)),
136                                   shared_ptr<dcp::ReelSoundAsset> (new dcp::ReelSoundAsset (ms, 0))
137                                   )
138                           ));
139
140         d.add (cpl);
141
142         d.write_xml (dcp::SMPTE, "OpenDCP 0.0.25", "OpenDCP 0.0.25", "2012-07-17T04:45:18+00:00", "Created by libdcp");
143
144         /* build/test/DCP/dcp_test2 is checked against test/ref/DCP/dcp_test2 by run/tests */
145 }
146
147 static void
148 note (dcp::NoteType, string)
149 {
150
151 }
152
153 /** Test comparison of a DCP with itself */
154 BOOST_AUTO_TEST_CASE (dcp_test3)
155 {
156         dcp::DCP A ("test/ref/DCP/dcp_test1");
157         A.read ();
158         dcp::DCP B ("test/ref/DCP/dcp_test1");
159         B.read ();
160
161         BOOST_CHECK (A.equals (B, dcp::EqualityOptions(), boost::bind (&note, _1, _2)));
162 }
163
164 /** Test comparison of a DCP with a different DCP */
165 BOOST_AUTO_TEST_CASE (dcp_test4)
166 {
167         dcp::DCP A ("test/ref/DCP/dcp_test1");
168         A.read ();
169         dcp::DCP B ("test/ref/DCP/dcp_test2");
170         B.read ();
171
172         BOOST_CHECK (!A.equals (B, dcp::EqualityOptions(), boost::bind (&note, _1, _2)));
173 }
174
175 static
176 void
177 test_rewriting_sound(string name, bool modify)
178 {
179         dcp::DCP A ("test/ref/DCP/dcp_test1");
180         A.read ();
181
182         BOOST_REQUIRE (!A.cpls().empty());
183         BOOST_REQUIRE (!A.cpls().front()->reels().empty());
184         shared_ptr<dcp::ReelMonoPictureAsset> A_picture = dynamic_pointer_cast<dcp::ReelMonoPictureAsset>(A.cpls().front()->reels().front()->main_picture());
185         BOOST_REQUIRE (A_picture);
186         shared_ptr<dcp::ReelSoundAsset> A_sound = dynamic_pointer_cast<dcp::ReelSoundAsset>(A.cpls().front()->reels().front()->main_sound());
187
188         boost::filesystem::remove_all ("build/test/" + name);
189         dcp::DCP B ("build/test/" + name);
190         shared_ptr<dcp::Reel> reel(new dcp::Reel());
191
192         BOOST_REQUIRE (A_picture->mono_asset());
193         BOOST_REQUIRE (A_picture->mono_asset()->file());
194         boost::filesystem::copy_file (A_picture->mono_asset()->file().get(), "build/test/" +name + "/picture.mxf");
195         reel->add(
196                 shared_ptr<dcp::ReelMonoPictureAsset>(
197                         new dcp::ReelMonoPictureAsset(shared_ptr<dcp::MonoPictureAsset>(new dcp::MonoPictureAsset("build/test/" + name + "/picture.mxf")), 0)
198                         )
199                 );
200
201         shared_ptr<dcp::SoundAssetReader> reader = A_sound->asset()->start_read();
202         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));
203         shared_ptr<dcp::SoundAssetWriter> writer = sound->start_write("build/test/" + name + "/sound.mxf", vector<dcp::Channel>());
204
205         bool need_to_modify = modify;
206         for (int i = 0; i < A_sound->asset()->intrinsic_duration(); ++i) {
207                 shared_ptr<const dcp::SoundFrame> sf = reader->get_frame (i);
208                 float* out[sf->channels()];
209                 for (int j = 0; j < sf->channels(); ++j) {
210                         out[j] = new float[sf->samples()];
211                 }
212                 for (int j = 0; j < sf->samples(); ++j) {
213                         for (int k = 0; k < sf->channels(); ++k) {
214                                 out[k][j] = static_cast<float>(sf->get(k, j)) / (1 << 23);
215                                 if (need_to_modify) {
216                                         out[k][j] += 1.0 / (1 << 23);
217                                         need_to_modify = false;
218                                 }
219                         }
220                 }
221                 writer->write (out, sf->samples());
222                 for (int j = 0; j < sf->channels(); ++j) {
223                         delete[] out[j];
224                 }
225         }
226         writer->finalize();
227
228         reel->add(shared_ptr<dcp::ReelSoundAsset>(new dcp::ReelSoundAsset(sound, 0)));
229
230         shared_ptr<dcp::CPL> cpl(new dcp::CPL("A Test DCP", dcp::FEATURE));
231         cpl->add (reel);
232
233         B.add (cpl);
234         B.write_xml (dcp::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         shared_ptr<dcp::CPL> cpl (new dcp::CPL ("A Test DCP", dcp::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         shared_ptr<dcp::MonoPictureAsset> mp (new dcp::MonoPictureAsset (dcp::Fraction (24, 1), dcp::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::File 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         shared_ptr<dcp::SoundAsset> ms (new dcp::SoundAsset(dcp::Fraction(24, 1), 48000, 1, dcp::LanguageTag("en-GB"), dcp::SMPTE));
292         ms->set_metadata (mxf_meta);
293         shared_ptr<dcp::SoundAssetWriter> sound_writer = ms->start_write ("build/test/DCP/dcp_test5/audio.mxf", vector<dcp::Channel>());
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::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::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 }