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