Bv2.1 7.2.2: Check that subtitle languages are the same for all reels.
[libdcp.git] / test / cpl_metadata_test.cc
1 /*
2     Copyright (C) 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
35 #include "certificate_chain.h"
36 #include "cpl.h"
37 #include "exceptions.h"
38 #include "language_tag.h"
39 #include "reel.h"
40 #include "reel_subtitle_asset.h"
41 #include "test.h"
42 #include <memory>
43 #include <boost/test/unit_test.hpp>
44
45
46 using std::list;
47 using std::string;
48 using std::vector;
49 using std::shared_ptr;
50
51
52 BOOST_AUTO_TEST_CASE (cpl_metadata_bad_values_test)
53 {
54         dcp::CPL cpl("", dcp::FEATURE);
55         BOOST_CHECK_THROW (cpl.set_version_number(-1), dcp::BadSettingError);
56
57         vector<dcp::ContentVersion> cv;
58         cv.push_back (dcp::ContentVersion("same-id", "version 1"));
59         cv.push_back (dcp::ContentVersion("same-id", "version 2"));
60         BOOST_CHECK_THROW (cpl.set_content_versions(cv), dcp::DuplicateIdError);
61 }
62
63
64 BOOST_AUTO_TEST_CASE (main_sound_configuration_test1)
65 {
66         dcp::MainSoundConfiguration msc("51/L,R,C,LFE,-,-");
67         BOOST_CHECK_EQUAL (msc.to_string(), "51/L,R,C,LFE,-,-");
68         BOOST_CHECK_EQUAL (msc.channels(), 6);
69         BOOST_CHECK_EQUAL (msc.field(), dcp::FIVE_POINT_ONE);
70         BOOST_CHECK_EQUAL (msc.mapping(0).get(), dcp::LEFT);
71         BOOST_CHECK_EQUAL (msc.mapping(1).get(), dcp::RIGHT);
72         BOOST_CHECK_EQUAL (msc.mapping(2).get(), dcp::CENTRE);
73         BOOST_CHECK_EQUAL (msc.mapping(3).get(), dcp::LFE);
74         BOOST_CHECK (!msc.mapping(4));
75         BOOST_CHECK (!msc.mapping(5));
76 }
77
78
79 BOOST_AUTO_TEST_CASE (main_sound_configuration_test2)
80 {
81         dcp::MainSoundConfiguration msc("71/L,R,C,LFE,-,-");
82         BOOST_CHECK_EQUAL (msc.to_string(), "71/L,R,C,LFE,-,-");
83         BOOST_CHECK_EQUAL (msc.channels(), 6);
84         BOOST_CHECK_EQUAL (msc.field(), dcp::SEVEN_POINT_ONE);
85         BOOST_CHECK_EQUAL (msc.mapping(0).get(), dcp::LEFT);
86         BOOST_CHECK_EQUAL (msc.mapping(1).get(), dcp::RIGHT);
87         BOOST_CHECK_EQUAL (msc.mapping(2).get(), dcp::CENTRE);
88         BOOST_CHECK_EQUAL (msc.mapping(3).get(), dcp::LFE);
89         BOOST_CHECK (!msc.mapping(4));
90         BOOST_CHECK (!msc.mapping(5));
91 }
92
93
94 BOOST_AUTO_TEST_CASE (main_sound_configuration_test3)
95 {
96         dcp::MainSoundConfiguration msc("71/L,-,C,LFE,Lss,Rss");
97         BOOST_CHECK_EQUAL (msc.to_string(), "71/L,-,C,LFE,Lss,Rss");
98         BOOST_CHECK_EQUAL (msc.channels(), 6);
99         BOOST_CHECK_EQUAL (msc.field(), dcp::SEVEN_POINT_ONE);
100         BOOST_CHECK_EQUAL (msc.mapping(0).get(), dcp::LEFT);
101         BOOST_CHECK (!msc.mapping(1));
102         BOOST_CHECK_EQUAL (msc.mapping(2).get(), dcp::CENTRE);
103         BOOST_CHECK_EQUAL (msc.mapping(3).get(), dcp::LFE);
104         BOOST_CHECK_EQUAL (msc.mapping(4).get(), dcp::LS);
105         BOOST_CHECK_EQUAL (msc.mapping(5).get(), dcp::RS);
106 }
107
108
109 BOOST_AUTO_TEST_CASE (main_sound_configuration_test4)
110 {
111         dcp::MainSoundConfiguration msc("71/L,-,C,LFE,Lss,Rss,-,-,-,-,-,-,-,-,-");
112         BOOST_CHECK_EQUAL (msc.to_string(), "71/L,-,C,LFE,Lss,Rss,-,-,-,-,-,-,-,-,-");
113         BOOST_CHECK_EQUAL (msc.channels(), 15);
114         BOOST_CHECK_EQUAL (msc.field(), dcp::SEVEN_POINT_ONE);
115         BOOST_CHECK_EQUAL (msc.mapping(0).get(), dcp::LEFT);
116         BOOST_CHECK (!msc.mapping(1));
117         BOOST_CHECK_EQUAL (msc.mapping(2).get(), dcp::CENTRE);
118         BOOST_CHECK_EQUAL (msc.mapping(3).get(), dcp::LFE);
119         BOOST_CHECK_EQUAL (msc.mapping(4).get(), dcp::LS);
120         BOOST_CHECK_EQUAL (msc.mapping(5).get(), dcp::RS);
121         for (int i = 6; i < 15; ++i) {
122                 BOOST_CHECK (!msc.mapping(i));
123         }
124 }
125
126
127 BOOST_AUTO_TEST_CASE (main_sound_configuration_test5)
128 {
129         dcp::MainSoundConfiguration msc("71/L,-,C,LFE,Lss,Rss,HI,VIN,-,-,Lrs,Rrs,DBOX,FSKSync,SLVS");
130         BOOST_CHECK_EQUAL (msc.to_string(), "71/L,-,C,LFE,Lss,Rss,HI,VIN,-,-,Lrs,Rrs,DBOX,FSKSync,SLVS");
131         BOOST_CHECK_EQUAL (msc.channels(), 15);
132         BOOST_CHECK_EQUAL (msc.field(), dcp::SEVEN_POINT_ONE);
133         BOOST_CHECK_EQUAL (msc.mapping(0).get(), dcp::LEFT);
134         BOOST_CHECK (!msc.mapping(1));
135         BOOST_CHECK_EQUAL (msc.mapping(2).get(), dcp::CENTRE);
136         BOOST_CHECK_EQUAL (msc.mapping(3).get(), dcp::LFE);
137         BOOST_CHECK_EQUAL (msc.mapping(4).get(), dcp::LS);
138         BOOST_CHECK_EQUAL (msc.mapping(5).get(), dcp::RS);
139         BOOST_CHECK_EQUAL (msc.mapping(6).get(), dcp::HI);
140         BOOST_CHECK_EQUAL (msc.mapping(7).get(), dcp::VI);
141         BOOST_CHECK (!msc.mapping(8));
142         BOOST_CHECK (!msc.mapping(9));
143         BOOST_CHECK_EQUAL (msc.mapping(10).get(), dcp::BSL);
144         BOOST_CHECK_EQUAL (msc.mapping(11).get(), dcp::BSR);
145         BOOST_CHECK_EQUAL (msc.mapping(12).get(), dcp::MOTION_DATA);
146         BOOST_CHECK_EQUAL (msc.mapping(13).get(), dcp::SYNC_SIGNAL);
147         BOOST_CHECK_EQUAL (msc.mapping(14).get(), dcp::SIGN_LANGUAGE);
148 }
149
150
151 BOOST_AUTO_TEST_CASE (luminance_test1)
152 {
153         BOOST_CHECK_NO_THROW (dcp::Luminance(4, dcp::Luminance::CANDELA_PER_SQUARE_METRE));
154         BOOST_CHECK_THROW (dcp::Luminance(-4, dcp::Luminance::CANDELA_PER_SQUARE_METRE), dcp::MiscError);
155 }
156
157
158 BOOST_AUTO_TEST_CASE (luminance_test2)
159 {
160         shared_ptr<cxml::Document> doc (new cxml::Document("Luminance"));
161
162         doc->read_string (
163                 "<Luminance units=\"candela-per-square-metre\">4.5</Luminance>"
164                 );
165
166         dcp::Luminance lum (doc);
167         BOOST_CHECK (lum.unit() == dcp::Luminance::CANDELA_PER_SQUARE_METRE);
168         BOOST_CHECK_CLOSE (lum.value(), 4.5, 0.1);
169 }
170
171
172 BOOST_AUTO_TEST_CASE (luminance_test3)
173 {
174         shared_ptr<cxml::Document> doc (new cxml::Document("Luminance"));
175
176         doc->read_string (
177                 "<Luminance units=\"candela-per-square-motre\">4.5</Luminance>"
178                 );
179
180         BOOST_CHECK_THROW (new dcp::Luminance(doc), dcp::XMLError);
181 }
182
183
184 BOOST_AUTO_TEST_CASE (luminance_test4)
185 {
186         shared_ptr<cxml::Document> doc (new cxml::Document("Luminance"));
187
188         doc->read_string (
189                 "<Luminance units=\"candela-per-square-metre\">-4.5</Luminance>"
190                 );
191
192         /* We tolerate out-of-range values when reading from XML */
193         dcp::Luminance lum (doc);
194         BOOST_CHECK (lum.unit() == dcp::Luminance::CANDELA_PER_SQUARE_METRE);
195         BOOST_CHECK_CLOSE (lum.value(), -4.5, 0.1);
196 }
197
198
199 /** A test where most CPL metadata is present */
200 BOOST_AUTO_TEST_CASE (cpl_metadata_read_test1)
201 {
202         dcp::CPL cpl("test/ref/cpl_metadata_test1.xml");
203
204         BOOST_CHECK_EQUAL (cpl.full_content_title_text().get(), "full-content-title");
205         BOOST_CHECK (cpl.full_content_title_text_language().get() == "de");
206         BOOST_CHECK (cpl.release_territory().get() == "ES");
207         BOOST_CHECK_EQUAL (cpl.version_number().get(), 2);
208         BOOST_CHECK_EQUAL (cpl.status().get(), dcp::FINAL);
209         BOOST_CHECK_EQUAL (cpl.chain().get(), "the-chain");
210         BOOST_CHECK_EQUAL (cpl.distributor().get(), "the-distributor");
211         BOOST_CHECK_EQUAL (cpl.facility().get(), "the-facility");
212         BOOST_CHECK (cpl.luminance() == dcp::Luminance(4.5, dcp::Luminance::FOOT_LAMBERT));
213
214         dcp::MainSoundConfiguration msc(cpl.main_sound_configuration().get());
215         BOOST_CHECK_EQUAL (msc.mapping(0).get(), dcp::LEFT);
216         BOOST_CHECK_EQUAL (msc.mapping(1).get(), dcp::RIGHT);
217         BOOST_CHECK_EQUAL (msc.mapping(2).get(), dcp::CENTRE);
218         BOOST_CHECK_EQUAL (msc.mapping(3).get(), dcp::LFE);
219         BOOST_CHECK (!msc.mapping(4));
220         BOOST_CHECK (!msc.mapping(5));
221         BOOST_CHECK (!msc.mapping(6));
222         BOOST_CHECK (!msc.mapping(7));
223         BOOST_CHECK (!msc.mapping(8));
224         BOOST_CHECK (!msc.mapping(9));
225         BOOST_CHECK (!msc.mapping(10));
226         BOOST_CHECK (!msc.mapping(11));
227         BOOST_CHECK (!msc.mapping(12));
228         BOOST_CHECK_EQUAL (msc.mapping(13).get(), dcp::SYNC_SIGNAL);
229
230         BOOST_CHECK_EQUAL (cpl.main_sound_sample_rate().get(), 48000);
231         BOOST_CHECK (cpl.main_picture_stored_area().get() == dcp::Size(1998, 1080));
232         BOOST_CHECK (cpl.main_picture_active_area().get() == dcp::Size(1440, 1080));
233
234         list<shared_ptr<dcp::Reel> > reels = cpl.reels ();
235         BOOST_REQUIRE_EQUAL (reels.size(), 1);
236         BOOST_REQUIRE (reels.front()->main_subtitle()->language());
237         BOOST_CHECK_EQUAL (reels.front()->main_subtitle()->language().get(), "de-DE");
238
239         vector<string> asl = cpl.additional_subtitle_languages();
240         BOOST_REQUIRE_EQUAL (asl.size(), 2);
241         BOOST_CHECK_EQUAL (asl[0], "en-US");
242         BOOST_CHECK_EQUAL (asl[1], "fr-ZA");
243
244         BOOST_CHECK (cpl.additional_subtitle_languages() == asl);
245 }
246
247
248 /** A test where most CPL metadata is present */
249 BOOST_AUTO_TEST_CASE (cpl_metadata_write_test1)
250 {
251         RNGFixer fix;
252
253         dcp::CPL cpl("", dcp::FEATURE);
254         cpl.set_issue_date ("2020-08-28T13:35:06+02:00");
255
256         vector<dcp::ContentVersion> cv;
257         cv.push_back (dcp::ContentVersion("some-id", "version 1"));
258         cv.push_back (dcp::ContentVersion("another-id", "version 2"));
259         cpl.set_content_versions (cv);
260
261         cpl.set_full_content_title_text ("full-content-title");
262         cpl.set_full_content_title_text_language (dcp::LanguageTag("de"));
263         cpl.set_release_territory (dcp::LanguageTag::RegionSubtag("ES"));
264         cpl.set_version_number (2);
265         cpl.set_status (dcp::FINAL);
266         cpl.set_chain ("the-chain");
267         cpl.set_distributor ("the-distributor");
268         cpl.set_facility ("the-facility");
269         cpl.set_luminance (dcp::Luminance(4.5, dcp::Luminance::FOOT_LAMBERT));
270
271         dcp::MainSoundConfiguration msc(dcp::SEVEN_POINT_ONE, 16);
272         msc.set_mapping (0, dcp::LEFT);
273         msc.set_mapping (1, dcp::RIGHT);
274         msc.set_mapping (2, dcp::CENTRE);
275         msc.set_mapping (3, dcp::LFE);
276         msc.set_mapping (13, dcp::SYNC_SIGNAL);
277         cpl.set_main_sound_configuration (msc.to_string());
278
279         cpl.set_main_sound_sample_rate (48000);
280         cpl.set_main_picture_stored_area (dcp::Size(1998, 1080));
281         cpl.set_main_picture_active_area (dcp::Size(1440, 1080));
282
283         shared_ptr<cxml::Document> doc (new cxml::Document("MainSubtitle"));
284
285         doc->read_string (
286                 "<MainSubtitle>"
287                 "<Id>urn:uuid:8bca1489-aab1-9259-a4fd-8150abc1de12</Id>"
288                 "<AnnotationText>Goodbye world!</AnnotationText>"
289                 "<EditRate>25 1</EditRate>"
290                 "<IntrinsicDuration>1870</IntrinsicDuration>"
291                 "<EntryPoint>0</EntryPoint>"
292                 "<Duration>525</Duration>"
293                 "<KeyId>urn:uuid:540cbf10-ab14-0233-ab1f-fb31501cabfa</KeyId>"
294                 "<Hash>3EABjX9BB1CAWhLUtHhrGSyLgOY=</Hash>"
295                 "<Language>de-DE</Language>"
296                 "</MainSubtitle>"
297                 );
298
299         shared_ptr<dcp::Reel> reel(new dcp::Reel());
300         reel->add (black_picture_asset("build/test/cpl_metadata_write_test1"));
301         reel->add (shared_ptr<dcp::ReelSubtitleAsset>(new dcp::ReelSubtitleAsset(doc)));
302         cpl.add (reel);
303
304         vector<dcp::LanguageTag> lt;
305         lt.push_back(dcp::LanguageTag("en-US"));
306         lt.push_back(dcp::LanguageTag("fr-ZA"));
307         cpl.set_additional_subtitle_languages (lt);
308
309         cpl.write_xml ("build/test/cpl_metadata_write_test1.xml", dcp::SMPTE, shared_ptr<dcp::CertificateChain>());
310         check_xml (
311                 dcp::file_to_string("test/ref/cpl_metadata_test1.xml"),
312                 dcp::file_to_string("build/test/cpl_metadata_write_test1.xml"),
313                 list<string>()
314                 );
315 }
316
317
318 /** A test where most CPL metadata is present */
319 BOOST_AUTO_TEST_CASE (cpl_metadata_roundtrip_test_1)
320 {
321         dcp::CPL cpl ("test/ref/cpl_metadata_test1.xml");
322         cpl.write_xml ("build/test/cpl_metadata_roundtrip_test1.xml", dcp::SMPTE, shared_ptr<dcp::CertificateChain>());
323         list<string> ignore;
324         ignore.push_back ("Id");
325         check_xml (
326                 dcp::file_to_string("test/ref/cpl_metadata_test1.xml"),
327                 dcp::file_to_string("build/test/cpl_metadata_roundtrip_test1.xml"),
328                 ignore
329                 );
330 }
331
332
333 /** A test where only a bare minimum of CPL metadata is present */
334 BOOST_AUTO_TEST_CASE (cpl_metadata_write_test2)
335 {
336         RNGFixer fix;
337
338         dcp::CPL cpl("", dcp::FEATURE);
339         cpl.set_issue_date ("2020-08-28T13:35:06+02:00");
340         cpl.set_content_version (dcp::ContentVersion("id", "version"));
341
342         dcp::MainSoundConfiguration msc(dcp::SEVEN_POINT_ONE, 16);
343         msc.set_mapping (0, dcp::LEFT);
344         msc.set_mapping (1, dcp::RIGHT);
345         msc.set_mapping (2, dcp::CENTRE);
346         msc.set_mapping (3, dcp::LFE);
347         msc.set_mapping (13, dcp::SYNC_SIGNAL);
348         cpl.set_main_sound_configuration (msc.to_string());
349
350         cpl.set_main_sound_sample_rate (48000);
351         cpl.set_main_picture_stored_area (dcp::Size(1998, 1080));
352         cpl.set_main_picture_active_area (dcp::Size(1440, 1080));
353
354         shared_ptr<dcp::Reel> reel(new dcp::Reel());
355         reel->add (black_picture_asset("build/test/cpl_metadata_write_test1"));
356         cpl.add (reel);
357
358         cpl.write_xml ("build/test/cpl_metadata_write_test2.xml", dcp::SMPTE, shared_ptr<dcp::CertificateChain>());
359         check_xml (
360                 dcp::file_to_string("test/ref/cpl_metadata_test2.xml"),
361                 dcp::file_to_string("build/test/cpl_metadata_write_test2.xml"),
362                 list<string>()
363                 );
364 }
365
366
367 /** A test where only a bare minimum of CPL metadata is present */
368 BOOST_AUTO_TEST_CASE (cpl_metadata_read_test2)
369 {
370         dcp::CPL cpl("test/ref/cpl_metadata_test2.xml");
371
372         BOOST_CHECK_EQUAL (cpl.full_content_title_text().get(), "");
373         BOOST_CHECK (!cpl.full_content_title_text_language());
374         BOOST_CHECK (!cpl.release_territory());
375         BOOST_CHECK (!cpl.version_number());
376         BOOST_CHECK (!cpl.status());
377         BOOST_CHECK (!cpl.chain());
378         BOOST_CHECK (!cpl.distributor());
379         BOOST_CHECK (!cpl.facility());
380         BOOST_CHECK (!cpl.luminance());
381
382         dcp::MainSoundConfiguration msc(cpl.main_sound_configuration().get());
383         BOOST_CHECK_EQUAL (msc.mapping(0).get(), dcp::LEFT);
384         BOOST_CHECK_EQUAL (msc.mapping(1).get(), dcp::RIGHT);
385         BOOST_CHECK_EQUAL (msc.mapping(2).get(), dcp::CENTRE);
386         BOOST_CHECK_EQUAL (msc.mapping(3).get(), dcp::LFE);
387         BOOST_CHECK (!msc.mapping(4));
388         BOOST_CHECK (!msc.mapping(5));
389         BOOST_CHECK (!msc.mapping(6));
390         BOOST_CHECK (!msc.mapping(7));
391         BOOST_CHECK (!msc.mapping(8));
392         BOOST_CHECK (!msc.mapping(9));
393         BOOST_CHECK (!msc.mapping(10));
394         BOOST_CHECK (!msc.mapping(11));
395         BOOST_CHECK (!msc.mapping(12));
396         BOOST_CHECK_EQUAL (msc.mapping(13).get(), dcp::SYNC_SIGNAL);
397
398         BOOST_CHECK_EQUAL (cpl.main_sound_sample_rate().get(), 48000);
399         BOOST_CHECK (cpl.main_picture_stored_area().get() == dcp::Size(1998, 1080));
400         BOOST_CHECK (cpl.main_picture_active_area().get() == dcp::Size(1440, 1080));
401
402         list<shared_ptr<dcp::Reel> > reels = cpl.reels ();
403         BOOST_REQUIRE_EQUAL (reels.size(), 1);
404 }
405
406
407 /** A test where only a bare minimum of CPL metadata is present */
408 BOOST_AUTO_TEST_CASE (cpl_metadata_roundtrip_test_2)
409 {
410         dcp::CPL cpl ("test/ref/cpl_metadata_test2.xml");
411         cpl.write_xml ("build/test/cpl_metadata_roundtrip_test2.xml", dcp::SMPTE, shared_ptr<dcp::CertificateChain>());
412         list<string> ignore;
413         ignore.push_back ("Id");
414         check_xml (
415                 dcp::file_to_string("test/ref/cpl_metadata_test2.xml"),
416                 dcp::file_to_string("build/test/cpl_metadata_roundtrip_test2.xml"),
417                 ignore
418                 );
419 }
420