Use feature not trailer for some tests to avoid verification errors about FFEC/FFMC.
[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::SMPTE, "OpenDCP 0.0.25", "OpenDCP 0.0.25", "2012-07-17T04:45:18+00:00", "Created by libdcp"
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         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")
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::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::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>());
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 (shared_ptr<dcp::Reel> (
135                           new dcp::Reel (
136                                   shared_ptr<dcp::ReelStereoPictureAsset> (new dcp::ReelStereoPictureAsset (mp, 0)),
137                                   shared_ptr<dcp::ReelSoundAsset> (new dcp::ReelSoundAsset (ms, 0))
138                                   )
139                           ));
140
141         d.add (cpl);
142
143         d.write_xml (dcp::SMPTE, "OpenDCP 0.0.25", "OpenDCP 0.0.25", "2012-07-17T04:45:18+00:00", "Created by libdcp");
144
145         /* build/test/DCP/dcp_test2 is checked against test/ref/DCP/dcp_test2 by run/tests */
146 }
147
148 static void
149 note (dcp::NoteType, string)
150 {
151
152 }
153
154 /** Test comparison of a DCP with itself */
155 BOOST_AUTO_TEST_CASE (dcp_test3)
156 {
157         dcp::DCP A ("test/ref/DCP/dcp_test1");
158         A.read ();
159         dcp::DCP B ("test/ref/DCP/dcp_test1");
160         B.read ();
161
162         BOOST_CHECK (A.equals (B, dcp::EqualityOptions(), boost::bind (&note, _1, _2)));
163 }
164
165 /** Test comparison of a DCP with a different DCP */
166 BOOST_AUTO_TEST_CASE (dcp_test4)
167 {
168         dcp::DCP A ("test/ref/DCP/dcp_test1");
169         A.read ();
170         dcp::DCP B ("test/ref/DCP/dcp_test2");
171         B.read ();
172
173         BOOST_CHECK (!A.equals (B, dcp::EqualityOptions(), boost::bind (&note, _1, _2)));
174 }
175
176 static
177 void
178 test_rewriting_sound(string name, bool modify)
179 {
180         using namespace boost::filesystem;
181
182         dcp::DCP A ("test/ref/DCP/dcp_test1");
183         A.read ();
184
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());
190
191         string const picture = "j2c_5279f9aa-94d7-42a6-b0e0-e4eaec4e2a15.mxf";
192
193         remove_all ("build/test/" + name);
194         dcp::DCP B ("build/test/" + name);
195         auto reel = make_shared<dcp::Reel>();
196
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));
201
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", {});
205
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()];
212                 }
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;
219                                 }
220                         }
221                 }
222                 writer->write (out, sf->samples());
223                 for (int j = 0; j < sf->channels(); ++j) {
224                         delete[] out[j];
225                 }
226         }
227         writer->finalize();
228
229         reel->add(make_shared<dcp::ReelSoundAsset>(sound, 0));
230
231         auto cpl = make_shared<dcp::CPL>("A Test DCP", dcp::TRAILER);
232         cpl->add (reel);
233
234         B.add (cpl);
235         B.write_xml (dcp::SMPTE);
236
237         dcp::EqualityOptions eq;
238         eq.reel_hashes_can_differ = true;
239         eq.max_audio_sample_error = 0;
240         if (modify) {
241                 BOOST_CHECK (!A.equals(B, eq, boost::bind(&note, _1, _2)));
242         } else {
243                 BOOST_CHECK (A.equals(B, eq, boost::bind(&note, _1, _2)));
244         }
245 }
246
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)
249 {
250         test_rewriting_sound ("dcp_test9", false);
251 }
252
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)
255 {
256         test_rewriting_sound ("dcp_test10", true);
257 }
258
259 /** Test creation of a 2D DCP with an Atmos track */
260 BOOST_AUTO_TEST_CASE (dcp_test5)
261 {
262         RNGFixer fix;
263
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";
269
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")
277                 );
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");
282
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 ());
289         }
290         picture_writer->finalize ();
291
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>());
295
296         SF_INFO info;
297         info.format = 0;
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];
301         float* channels[1];
302         channels[0] = buffer;
303         while (true) {
304                 sf_count_t N = sf_readf_float (sndfile, buffer, 4096);
305                 sound_writer->write (channels, N);
306                 if (N < 4096) {
307                         break;
308                 }
309         }
310
311         sound_writer->finalize ();
312
313         shared_ptr<dcp::AtmosAsset> am (new dcp::AtmosAsset (private_test / "20160218_NameOfFilm_FTR_OV_EN_A_dcs_r01.mxf"));
314
315         cpl->add (shared_ptr<dcp::Reel> (
316                           new 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))
322                                   )
323                           ));
324
325         d.add (cpl);
326
327         d.write_xml (dcp::SMPTE, "OpenDCP 0.0.25", "OpenDCP 0.0.25", "2012-07-17T04:45:18+00:00", "Created by libdcp");
328
329         /* build/test/DCP/dcp_test5 is checked against test/ref/DCP/dcp_test5 by run/tests */
330 }
331
332 /** Basic tests of reading a 2D DCP with an Atmos track */
333 BOOST_AUTO_TEST_CASE (dcp_test6)
334 {
335         dcp::DCP dcp ("test/ref/DCP/dcp_test5");
336         dcp.read ();
337
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());
344 }
345
346 /** Test creation of a 2D Interop DCP from very simple inputs */
347 BOOST_AUTO_TEST_CASE (dcp_test7)
348 {
349         RNGFixer fix;
350
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"
353                 );
354
355         /* build/test/DCP/dcp_test7 is checked against test/ref/DCP/dcp_test7 by run/tests */
356 }
357
358 /** Test reading of a DCP with multiple CPLs */
359 BOOST_AUTO_TEST_CASE (dcp_test8)
360 {
361         dcp::DCP dcp (private_test / "data/SMPTE_TST-B1PB2P_S_EN-EN-CCAP_5171-HI-VI_2K_ISDCF_20151123_DPPT_SMPTE_combo/");
362         dcp.read ();
363
364         BOOST_REQUIRE_EQUAL (dcp.cpls().size(), 2);
365 }
366
367
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)
370 {
371         dcp::DCP dcp ("test/data/extra_assetmap");
372         BOOST_CHECK_NO_THROW (dcp.read());
373 }