Bv2.1 8.5: FFOC and LFOC should be present and have particular values.
[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         reel->add(simple_markers());
231
232         auto cpl = make_shared<dcp::CPL>("A Test DCP", dcp::TRAILER);
233         cpl->add (reel);
234
235         B.add (cpl);
236         B.write_xml (dcp::SMPTE);
237
238         dcp::EqualityOptions eq;
239         eq.reel_hashes_can_differ = true;
240         eq.max_audio_sample_error = 0;
241         if (modify) {
242                 BOOST_CHECK (!A.equals(B, eq, boost::bind(&note, _1, _2)));
243         } else {
244                 BOOST_CHECK (A.equals(B, eq, boost::bind(&note, _1, _2)));
245         }
246 }
247
248 /** Test comparison of a DCP with another that has the same picture and the same (but re-written) sound */
249 BOOST_AUTO_TEST_CASE (dcp_test9)
250 {
251         test_rewriting_sound ("dcp_test9", false);
252 }
253
254 /** Test comparison of a DCP with another that has the same picture and very slightly modified sound */
255 BOOST_AUTO_TEST_CASE (dcp_test10)
256 {
257         test_rewriting_sound ("dcp_test10", true);
258 }
259
260 /** Test creation of a 2D DCP with an Atmos track */
261 BOOST_AUTO_TEST_CASE (dcp_test5)
262 {
263         RNGFixer fix;
264
265         /* Some known metadata */
266         dcp::MXFMetadata mxf_meta;
267         mxf_meta.company_name = "OpenDCP";
268         mxf_meta.product_name = "OpenDCP";
269         mxf_meta.product_version = "0.0.25";
270
271         /* We're making build/test/DCP/dcp_test5 */
272         boost::filesystem::remove_all ("build/test/DCP/dcp_test5");
273         boost::filesystem::create_directories ("build/test/DCP/dcp_test5");
274         dcp::DCP d ("build/test/DCP/dcp_test5");
275         shared_ptr<dcp::CPL> cpl (new dcp::CPL ("A Test DCP", dcp::FEATURE));
276         cpl->set_content_version (
277                 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                 );
279         cpl->set_issuer ("OpenDCP 0.0.25");
280         cpl->set_creator ("OpenDCP 0.0.25");
281         cpl->set_issue_date ("2012-07-17T04:45:18+00:00");
282         cpl->set_annotation_text ("A Test DCP");
283
284         shared_ptr<dcp::MonoPictureAsset> mp (new dcp::MonoPictureAsset (dcp::Fraction (24, 1), dcp::SMPTE));
285         mp->set_metadata (mxf_meta);
286         shared_ptr<dcp::PictureAssetWriter> picture_writer = mp->start_write ("build/test/DCP/dcp_test5/video.mxf", false);
287         dcp::ArrayData j2c ("test/data/flat_red.j2c");
288         for (int i = 0; i < 24; ++i) {
289                 picture_writer->write (j2c.data (), j2c.size ());
290         }
291         picture_writer->finalize ();
292
293         shared_ptr<dcp::SoundAsset> ms (new dcp::SoundAsset(dcp::Fraction(24, 1), 48000, 1, dcp::LanguageTag("en-GB"), dcp::SMPTE));
294         ms->set_metadata (mxf_meta);
295         shared_ptr<dcp::SoundAssetWriter> sound_writer = ms->start_write ("build/test/DCP/dcp_test5/audio.mxf", vector<dcp::Channel>());
296
297         SF_INFO info;
298         info.format = 0;
299         SNDFILE* sndfile = sf_open ("test/data/1s_24-bit_48k_silence.wav", SFM_READ, &info);
300         BOOST_CHECK (sndfile);
301         float buffer[4096*6];
302         float* channels[1];
303         channels[0] = buffer;
304         while (true) {
305                 sf_count_t N = sf_readf_float (sndfile, buffer, 4096);
306                 sound_writer->write (channels, N);
307                 if (N < 4096) {
308                         break;
309                 }
310         }
311
312         sound_writer->finalize ();
313
314         shared_ptr<dcp::AtmosAsset> am (new dcp::AtmosAsset (private_test / "20160218_NameOfFilm_FTR_OV_EN_A_dcs_r01.mxf"));
315
316         cpl->add (shared_ptr<dcp::Reel> (
317                           new dcp::Reel (
318                                   shared_ptr<dcp::ReelMonoPictureAsset> (new dcp::ReelMonoPictureAsset (mp, 0)),
319                                   shared_ptr<dcp::ReelSoundAsset> (new dcp::ReelSoundAsset (ms, 0)),
320                                   shared_ptr<dcp::ReelSubtitleAsset> (),
321                                   shared_ptr<dcp::ReelMarkersAsset> (),
322                                   shared_ptr<dcp::ReelAtmosAsset> (new dcp::ReelAtmosAsset (am, 0))
323                                   )
324                           ));
325
326         d.add (cpl);
327
328         d.write_xml (dcp::SMPTE, "OpenDCP 0.0.25", "OpenDCP 0.0.25", "2012-07-17T04:45:18+00:00", "Created by libdcp");
329
330         /* build/test/DCP/dcp_test5 is checked against test/ref/DCP/dcp_test5 by run/tests */
331 }
332
333 /** Basic tests of reading a 2D DCP with an Atmos track */
334 BOOST_AUTO_TEST_CASE (dcp_test6)
335 {
336         dcp::DCP dcp ("test/ref/DCP/dcp_test5");
337         dcp.read ();
338
339         BOOST_REQUIRE_EQUAL (dcp.cpls().size(), 1);
340         BOOST_REQUIRE_EQUAL (dcp.cpls().front()->reels().size(), 1);
341         BOOST_CHECK (dcp.cpls().front()->reels().front()->main_picture());
342         BOOST_CHECK (dcp.cpls().front()->reels().front()->main_sound());
343         BOOST_CHECK (!dcp.cpls().front()->reels().front()->main_subtitle());
344         BOOST_CHECK (dcp.cpls().front()->reels().front()->atmos());
345 }
346
347 /** Test creation of a 2D Interop DCP from very simple inputs */
348 BOOST_AUTO_TEST_CASE (dcp_test7)
349 {
350         RNGFixer fix;
351
352         make_simple("build/test/DCP/dcp_test7")->write_xml(
353                 dcp::INTEROP,  "OpenDCP 0.0.25", "OpenDCP 0.0.25", "2012-07-17T04:45:18+00:00", "Created by libdcp"
354                 );
355
356         /* build/test/DCP/dcp_test7 is checked against test/ref/DCP/dcp_test7 by run/tests */
357 }
358
359 /** Test reading of a DCP with multiple CPLs */
360 BOOST_AUTO_TEST_CASE (dcp_test8)
361 {
362         dcp::DCP dcp (private_test / "data/SMPTE_TST-B1PB2P_S_EN-EN-CCAP_5171-HI-VI_2K_ISDCF_20151123_DPPT_SMPTE_combo/");
363         dcp.read ();
364
365         BOOST_REQUIRE_EQUAL (dcp.cpls().size(), 2);
366 }
367
368
369 /** Test reading a DCP whose ASSETMAP contains assets not used by any PKL */
370 BOOST_AUTO_TEST_CASE (dcp_things_in_assetmap_not_in_pkl)
371 {
372         dcp::DCP dcp ("test/data/extra_assetmap");
373         BOOST_CHECK_NO_THROW (dcp.read());
374 }