2 Copyright (C) 2018-2020 Carl Hetherington <cth@carlh.net>
4 This file is part of libdcp.
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.
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.
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/>.
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
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.
38 #include "reel_mono_picture_asset.h"
39 #include "reel_sound_asset.h"
42 #include "openjpeg_image.h"
43 #include "mono_picture_asset.h"
44 #include "stereo_picture_asset.h"
45 #include "mono_picture_asset_writer.h"
46 #include "interop_subtitle_asset.h"
47 #include "smpte_subtitle_asset.h"
48 #include "reel_closed_caption_asset.h"
49 #include "reel_stereo_picture_asset.h"
50 #include "reel_subtitle_asset.h"
51 #include "compose.hpp"
53 #include <boost/test/unit_test.hpp>
54 #include <boost/foreach.hpp>
55 #include <boost/algorithm/string.hpp>
64 using boost::optional;
65 using std::shared_ptr;
68 static list<pair<string, optional<boost::filesystem::path> > > stages;
71 stage (string s, optional<boost::filesystem::path> p)
73 stages.push_back (make_pair (s, p));
83 prepare_directory (boost::filesystem::path path)
85 using namespace boost::filesystem;
87 create_directories (path);
91 static vector<boost::filesystem::path>
92 setup (int reference_number, int verify_test_number)
94 prepare_directory (dcp::String::compose("build/test/verify_test%1", verify_test_number));
95 for (boost::filesystem::directory_iterator i(dcp::String::compose("test/ref/DCP/dcp_test%1", reference_number)); i != boost::filesystem::directory_iterator(); ++i) {
96 boost::filesystem::copy_file (i->path(), dcp::String::compose("build/test/verify_test%1", verify_test_number) / i->path().filename());
99 vector<boost::filesystem::path> directories;
100 directories.push_back (dcp::String::compose("build/test/verify_test%1", verify_test_number));
106 /** Class that can alter a file by searching and replacing strings within it.
107 * On destruction modifies the file whose name was given to the constructor.
112 Editor (boost::filesystem::path path)
115 _content = dcp::file_to_string (_path);
120 FILE* f = fopen(_path.string().c_str(), "w");
122 fwrite (_content.c_str(), _content.length(), 1, f);
126 void replace (string a, string b)
128 boost::algorithm::replace_all (_content, a, b);
132 boost::filesystem::path _path;
133 std::string _content;
139 dump_notes (list<dcp::VerificationNote> const & notes)
141 BOOST_FOREACH (dcp::VerificationNote i, notes) {
142 std::cout << dcp::note_to_string(i) << "\n";
146 /* Check DCP as-is (should be OK) */
147 BOOST_AUTO_TEST_CASE (verify_test1)
150 vector<boost::filesystem::path> directories = setup (1, 1);
151 list<dcp::VerificationNote> notes = dcp::verify (directories, &stage, &progress, xsd_test);
153 boost::filesystem::path const cpl_file = "build/test/verify_test1/cpl_81fb54df-e1bf-4647-8788-ea7ba154375b.xml";
154 boost::filesystem::path const pkl_file = "build/test/verify_test1/pkl_cd49971e-bf4c-4594-8474-54ebef09a40c.xml";
155 boost::filesystem::path const assetmap_file = "build/test/verify_test1/ASSETMAP.xml";
157 list<pair<string, optional<boost::filesystem::path> > >::const_iterator st = stages.begin();
158 BOOST_CHECK_EQUAL (st->first, "Checking DCP");
159 BOOST_REQUIRE (st->second);
160 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical("build/test/verify_test1"));
162 BOOST_CHECK_EQUAL (st->first, "Checking CPL");
163 BOOST_REQUIRE (st->second);
164 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical(cpl_file));
166 BOOST_CHECK_EQUAL (st->first, "Checking reel");
167 BOOST_REQUIRE (!st->second);
169 BOOST_CHECK_EQUAL (st->first, "Checking picture asset hash");
170 BOOST_REQUIRE (st->second);
171 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical("build/test/verify_test1/video.mxf"));
173 BOOST_CHECK_EQUAL (st->first, "Checking picture frame sizes");
174 BOOST_REQUIRE (st->second);
175 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical("build/test/verify_test1/video.mxf"));
177 BOOST_CHECK_EQUAL (st->first, "Checking sound asset hash");
178 BOOST_REQUIRE (st->second);
179 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical("build/test/verify_test1/audio.mxf"));
181 BOOST_CHECK_EQUAL (st->first, "Checking sound asset metadata");
182 BOOST_REQUIRE (st->second);
183 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical("build/test/verify_test1/audio.mxf"));
185 BOOST_CHECK_EQUAL (st->first, "Checking PKL");
186 BOOST_REQUIRE (st->second);
187 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical(pkl_file));
189 BOOST_CHECK_EQUAL (st->first, "Checking ASSETMAP");
190 BOOST_REQUIRE (st->second);
191 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical(assetmap_file));
193 BOOST_REQUIRE (st == stages.end());
195 BOOST_CHECK_EQUAL (notes.size(), 0);
198 /* Corrupt the MXFs and check that this is spotted */
199 BOOST_AUTO_TEST_CASE (verify_test2)
201 vector<boost::filesystem::path> directories = setup (1, 2);
203 FILE* mod = fopen("build/test/verify_test2/video.mxf", "r+b");
205 fseek (mod, 4096, SEEK_SET);
207 fwrite (&x, sizeof(x), 1, mod);
210 mod = fopen("build/test/verify_test2/audio.mxf", "r+b");
212 BOOST_REQUIRE_EQUAL (fseek(mod, -64, SEEK_END), 0);
213 BOOST_REQUIRE (fwrite (&x, sizeof(x), 1, mod) == 1);
216 list<dcp::VerificationNote> notes;
218 dcp::ASDCPErrorSuspender sus;
219 notes = dcp::verify (directories, &stage, &progress, xsd_test);
222 BOOST_REQUIRE_EQUAL (notes.size(), 2);
223 BOOST_CHECK_EQUAL (notes.front().type(), dcp::VerificationNote::VERIFY_ERROR);
224 BOOST_CHECK_EQUAL (notes.front().code(), dcp::VerificationNote::PICTURE_HASH_INCORRECT);
225 BOOST_CHECK_EQUAL (notes.back().type(), dcp::VerificationNote::VERIFY_ERROR);
226 BOOST_CHECK_EQUAL (notes.back().code(), dcp::VerificationNote::SOUND_HASH_INCORRECT);
229 /* Corrupt the hashes in the PKL and check that the disagreement between CPL and PKL is spotted */
230 BOOST_AUTO_TEST_CASE (verify_test3)
232 vector<boost::filesystem::path> directories = setup (1, 3);
235 Editor e ("build/test/verify_test3/pkl_cd49971e-bf4c-4594-8474-54ebef09a40c.xml");
236 e.replace ("<Hash>", "<Hash>x");
239 list<dcp::VerificationNote> notes = dcp::verify (directories, &stage, &progress, xsd_test);
241 BOOST_REQUIRE_EQUAL (notes.size(), 6);
242 list<dcp::VerificationNote>::const_iterator i = notes.begin();
243 BOOST_CHECK_EQUAL (i->type(), dcp::VerificationNote::VERIFY_ERROR);
244 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::CPL_HASH_INCORRECT);
246 BOOST_CHECK_EQUAL (i->type(), dcp::VerificationNote::VERIFY_ERROR);
247 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::PKL_CPL_PICTURE_HASHES_DIFFER);
249 BOOST_CHECK_EQUAL (i->type(), dcp::VerificationNote::VERIFY_ERROR);
250 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::PKL_CPL_SOUND_HASHES_DIFFER);
252 BOOST_CHECK_EQUAL (i->type(), dcp::VerificationNote::VERIFY_ERROR);
253 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::XML_VALIDATION_ERROR);
255 BOOST_CHECK_EQUAL (i->type(), dcp::VerificationNote::VERIFY_ERROR);
256 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::XML_VALIDATION_ERROR);
258 BOOST_CHECK_EQUAL (i->type(), dcp::VerificationNote::VERIFY_ERROR);
259 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::XML_VALIDATION_ERROR);
263 /* Corrupt the ContentKind in the CPL */
264 BOOST_AUTO_TEST_CASE (verify_test4)
266 vector<boost::filesystem::path> directories = setup (1, 4);
269 Editor e ("build/test/verify_test4/cpl_81fb54df-e1bf-4647-8788-ea7ba154375b.xml");
270 e.replace ("<ContentKind>", "<ContentKind>x");
273 list<dcp::VerificationNote> notes = dcp::verify (directories, &stage, &progress, xsd_test);
275 BOOST_REQUIRE_EQUAL (notes.size(), 1);
276 BOOST_CHECK_EQUAL (notes.front().code(), dcp::VerificationNote::GENERAL_READ);
277 BOOST_CHECK_EQUAL (*notes.front().note(), "Bad content kind 'xfeature'");
281 boost::filesystem::path
284 return dcp::String::compose("build/test/verify_test%1/cpl_81fb54df-e1bf-4647-8788-ea7ba154375b.xml", n);
288 boost::filesystem::path
291 return dcp::String::compose("build/test/verify_test%1/pkl_cd49971e-bf4c-4594-8474-54ebef09a40c.xml", n);
295 boost::filesystem::path
298 return dcp::String::compose("build/test/verify_test%1/ASSETMAP.xml", n);
302 void check_after_replace (int n, boost::function<boost::filesystem::path (int)> file, string from, string to, dcp::VerificationNote::Code code1)
304 vector<boost::filesystem::path> directories = setup (1, n);
308 e.replace (from, to);
311 list<dcp::VerificationNote> notes = dcp::verify (directories, &stage, &progress, xsd_test);
313 BOOST_REQUIRE_EQUAL (notes.size(), 1);
314 BOOST_CHECK_EQUAL (notes.front().code(), code1);
318 void check_after_replace (int n, boost::function<boost::filesystem::path (int)> file, string from, string to, dcp::VerificationNote::Code code1, dcp::VerificationNote::Code code2)
320 vector<boost::filesystem::path> directories = setup (1, n);
324 e.replace (from, to);
327 list<dcp::VerificationNote> notes = dcp::verify (directories, &stage, &progress, xsd_test);
329 BOOST_REQUIRE_EQUAL (notes.size(), 2);
330 BOOST_CHECK_EQUAL (notes.front().code(), code1);
331 BOOST_CHECK_EQUAL (notes.back().code(), code2);
335 void check_after_replace (
336 int n, boost::function<boost::filesystem::path (int)> file,
339 dcp::VerificationNote::Code code1,
340 dcp::VerificationNote::Code code2,
341 dcp::VerificationNote::Code code3
344 vector<boost::filesystem::path> directories = setup (1, n);
348 e.replace (from, to);
351 list<dcp::VerificationNote> notes = dcp::verify (directories, &stage, &progress, xsd_test);
353 BOOST_REQUIRE_EQUAL (notes.size(), 3);
354 list<dcp::VerificationNote>::const_iterator i = notes.begin ();
355 BOOST_CHECK_EQUAL (i->code(), code1);
357 BOOST_CHECK_EQUAL (i->code(), code2);
359 BOOST_CHECK_EQUAL (i->code(), code3);
363 BOOST_AUTO_TEST_CASE (verify_test5)
365 check_after_replace (
367 "<FrameRate>24 1", "<FrameRate>99 1",
368 dcp::VerificationNote::CPL_HASH_INCORRECT,
369 dcp::VerificationNote::INVALID_PICTURE_FRAME_RATE
374 BOOST_AUTO_TEST_CASE (verify_test6)
376 vector<boost::filesystem::path> directories = setup (1, 6);
378 boost::filesystem::remove ("build/test/verify_test6/video.mxf");
379 list<dcp::VerificationNote> notes = dcp::verify (directories, &stage, &progress, xsd_test);
381 BOOST_REQUIRE_EQUAL (notes.size(), 1);
382 BOOST_CHECK_EQUAL (notes.front().type(), dcp::VerificationNote::VERIFY_ERROR);
383 BOOST_CHECK_EQUAL (notes.front().code(), dcp::VerificationNote::MISSING_ASSET);
387 boost::filesystem::path
390 return dcp::String::compose("build/test/verify_test%1/ASSETMAP.xml", n);
393 /* Empty asset filename in ASSETMAP */
394 BOOST_AUTO_TEST_CASE (verify_test7)
396 check_after_replace (
398 "<Path>video.mxf</Path>", "<Path></Path>",
399 dcp::VerificationNote::EMPTY_ASSET_PATH
403 /* Mismatched standard */
404 BOOST_AUTO_TEST_CASE (verify_test8)
406 check_after_replace (
408 "http://www.smpte-ra.org/schemas/429-7/2006/CPL", "http://www.digicine.com/PROTO-ASDCP-CPL-20040511#",
409 dcp::VerificationNote::MISMATCHED_STANDARD,
410 dcp::VerificationNote::XML_VALIDATION_ERROR,
411 dcp::VerificationNote::CPL_HASH_INCORRECT
415 /* Badly formatted <Id> in CPL */
416 BOOST_AUTO_TEST_CASE (verify_test9)
418 /* There's no CPL_HASH_INCORRECT error here because it can't find the correct hash by ID (since the ID is wrong) */
419 check_after_replace (
421 "<Id>urn:uuid:81fb54df-e1bf-4647-8788-ea7ba154375b", "<Id>urn:uuid:81fb54df-e1bf-4647-8788-ea7ba154375",
422 dcp::VerificationNote::XML_VALIDATION_ERROR
426 /* Badly formatted <IssueDate> in CPL */
427 BOOST_AUTO_TEST_CASE (verify_test10)
429 check_after_replace (
431 "<IssueDate>", "<IssueDate>x",
432 dcp::VerificationNote::XML_VALIDATION_ERROR,
433 dcp::VerificationNote::CPL_HASH_INCORRECT
437 /* Badly-formatted <Id> in PKL */
438 BOOST_AUTO_TEST_CASE (verify_test11)
440 check_after_replace (
442 "<Id>urn:uuid:cd4", "<Id>urn:uuid:xd4",
443 dcp::VerificationNote::XML_VALIDATION_ERROR
447 /* Badly-formatted <Id> in ASSETMAP */
448 BOOST_AUTO_TEST_CASE (verify_test12)
450 check_after_replace (
452 "<Id>urn:uuid:63c", "<Id>urn:uuid:x3c",
453 dcp::VerificationNote::XML_VALIDATION_ERROR
457 /* Basic test of an Interop DCP */
458 BOOST_AUTO_TEST_CASE (verify_test13)
461 vector<boost::filesystem::path> directories = setup (3, 13);
462 list<dcp::VerificationNote> notes = dcp::verify (directories, &stage, &progress, xsd_test);
464 boost::filesystem::path const cpl_file = "build/test/verify_test13/cpl_cbfd2bc0-21cf-4a8f-95d8-9cddcbe51296.xml";
465 boost::filesystem::path const pkl_file = "build/test/verify_test13/pkl_d87a950c-bd6f-41f6-90cc-56ccd673e131.xml";
466 boost::filesystem::path const assetmap_file = "build/test/verify_test13/ASSETMAP";
468 list<pair<string, optional<boost::filesystem::path> > >::const_iterator st = stages.begin();
469 BOOST_CHECK_EQUAL (st->first, "Checking DCP");
470 BOOST_REQUIRE (st->second);
471 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical("build/test/verify_test13"));
473 BOOST_CHECK_EQUAL (st->first, "Checking CPL");
474 BOOST_REQUIRE (st->second);
475 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical(cpl_file));
477 BOOST_CHECK_EQUAL (st->first, "Checking reel");
478 BOOST_REQUIRE (!st->second);
480 BOOST_CHECK_EQUAL (st->first, "Checking picture asset hash");
481 BOOST_REQUIRE (st->second);
482 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical("build/test/verify_test13/j2c_c6035f97-b07d-4e1c-944d-603fc2ddc242.mxf"));
484 BOOST_CHECK_EQUAL (st->first, "Checking picture frame sizes");
485 BOOST_REQUIRE (st->second);
486 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical("build/test/verify_test13/j2c_c6035f97-b07d-4e1c-944d-603fc2ddc242.mxf"));
488 BOOST_CHECK_EQUAL (st->first, "Checking sound asset hash");
489 BOOST_REQUIRE (st->second);
490 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical("build/test/verify_test13/pcm_69cf9eaf-9a99-4776-b022-6902208626c3.mxf"));
492 BOOST_CHECK_EQUAL (st->first, "Checking sound asset metadata");
493 BOOST_REQUIRE (st->second);
494 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical("build/test/verify_test13/pcm_69cf9eaf-9a99-4776-b022-6902208626c3.mxf"));
496 BOOST_CHECK_EQUAL (st->first, "Checking PKL");
497 BOOST_REQUIRE (st->second);
498 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical(pkl_file));
500 BOOST_CHECK_EQUAL (st->first, "Checking ASSETMAP");
501 BOOST_REQUIRE (st->second);
502 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical(assetmap_file));
504 BOOST_REQUIRE (st == stages.end());
506 BOOST_REQUIRE_EQUAL (notes.size(), 1U);
507 list<dcp::VerificationNote>::const_iterator i = notes.begin ();
508 BOOST_CHECK_EQUAL (i->type(), dcp::VerificationNote::VERIFY_BV21_ERROR);
509 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::NOT_SMPTE);
512 /* DCP with a short asset */
513 BOOST_AUTO_TEST_CASE (verify_test14)
515 vector<boost::filesystem::path> directories = setup (8, 14);
516 list<dcp::VerificationNote> notes = dcp::verify (directories, &stage, &progress, xsd_test);
518 BOOST_REQUIRE_EQUAL (notes.size(), 5);
519 list<dcp::VerificationNote>::const_iterator i = notes.begin ();
520 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::NOT_SMPTE);
522 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::DURATION_TOO_SMALL);
524 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::INTRINSIC_DURATION_TOO_SMALL);
526 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::DURATION_TOO_SMALL);
528 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::INTRINSIC_DURATION_TOO_SMALL);
535 dcp_from_frame (dcp::ArrayData const& frame, boost::filesystem::path dir)
537 shared_ptr<dcp::MonoPictureAsset> asset(new dcp::MonoPictureAsset(dcp::Fraction(24, 1), dcp::SMPTE));
538 boost::filesystem::create_directories (dir);
539 shared_ptr<dcp::PictureAssetWriter> writer = asset->start_write (dir / "pic.mxf", true);
540 for (int i = 0; i < 24; ++i) {
541 writer->write (frame.data(), frame.size());
545 shared_ptr<dcp::ReelAsset> reel_asset(new dcp::ReelMonoPictureAsset(asset, 0));
546 shared_ptr<dcp::Reel> reel(new dcp::Reel());
547 reel->add (reel_asset);
548 shared_ptr<dcp::CPL> cpl(new dcp::CPL("hello", dcp::FEATURE));
550 shared_ptr<dcp::DCP> dcp(new dcp::DCP(dir));
552 dcp->write_xml (dcp::SMPTE);
556 /* DCP with an over-sized JPEG2000 frame */
557 BOOST_AUTO_TEST_CASE (verify_test15)
559 int const too_big = 1302083 * 2;
561 /* Compress a black image */
562 shared_ptr<dcp::OpenJPEGImage> image = black_image ();
563 dcp::ArrayData frame = dcp::compress_j2k (image, 100000000, 24, false, false);
564 BOOST_REQUIRE (frame.size() < too_big);
566 /* Place it in a bigger block with some zero padding at the end */
567 dcp::ArrayData oversized_frame(too_big);
568 memcpy (oversized_frame.data(), frame.data(), frame.size());
569 memset (oversized_frame.data() + frame.size(), 0, too_big - frame.size());
571 boost::filesystem::path const dir("build/test/verify_test15");
572 boost::filesystem::remove_all (dir);
573 dcp_from_frame (oversized_frame, dir);
575 vector<boost::filesystem::path> dirs;
576 dirs.push_back (dir);
577 list<dcp::VerificationNote> notes = dcp::verify (dirs, &stage, &progress, xsd_test);
578 BOOST_REQUIRE_EQUAL (notes.size(), 1);
579 BOOST_CHECK_EQUAL (notes.front().code(), dcp::VerificationNote::PICTURE_FRAME_TOO_LARGE_IN_BYTES);
583 /* DCP with a nearly over-sized JPEG2000 frame */
584 BOOST_AUTO_TEST_CASE (verify_test16)
586 int const nearly_too_big = 1302083 * 0.98;
588 /* Compress a black image */
589 shared_ptr<dcp::OpenJPEGImage> image = black_image ();
590 dcp::ArrayData frame = dcp::compress_j2k (image, 100000000, 24, false, false);
591 BOOST_REQUIRE (frame.size() < nearly_too_big);
593 /* Place it in a bigger block with some zero padding at the end */
594 dcp::ArrayData oversized_frame(nearly_too_big);
595 memcpy (oversized_frame.data(), frame.data(), frame.size());
596 memset (oversized_frame.data() + frame.size(), 0, nearly_too_big - frame.size());
598 boost::filesystem::path const dir("build/test/verify_test16");
599 boost::filesystem::remove_all (dir);
600 dcp_from_frame (oversized_frame, dir);
602 vector<boost::filesystem::path> dirs;
603 dirs.push_back (dir);
604 list<dcp::VerificationNote> notes = dcp::verify (dirs, &stage, &progress, xsd_test);
605 BOOST_REQUIRE_EQUAL (notes.size(), 1);
606 BOOST_CHECK_EQUAL (notes.front().code(), dcp::VerificationNote::PICTURE_FRAME_NEARLY_TOO_LARGE_IN_BYTES);
610 /* DCP with a within-range JPEG2000 frame */
611 BOOST_AUTO_TEST_CASE (verify_test17)
613 /* Compress a black image */
614 shared_ptr<dcp::OpenJPEGImage> image = black_image ();
615 dcp::ArrayData frame = dcp::compress_j2k (image, 100000000, 24, false, false);
616 BOOST_REQUIRE (frame.size() < 230000000 / (24 * 8));
618 boost::filesystem::path const dir("build/test/verify_test17");
619 boost::filesystem::remove_all (dir);
620 dcp_from_frame (frame, dir);
622 vector<boost::filesystem::path> dirs;
623 dirs.push_back (dir);
624 list<dcp::VerificationNote> notes = dcp::verify (dirs, &stage, &progress, xsd_test);
625 BOOST_REQUIRE_EQUAL (notes.size(), 0);
629 /* DCP with valid Interop subtitles */
630 BOOST_AUTO_TEST_CASE (verify_test18)
632 boost::filesystem::path const dir("build/test/verify_test18");
633 prepare_directory (dir);
634 boost::filesystem::copy_file ("test/data/subs1.xml", dir / "subs.xml");
635 shared_ptr<dcp::InteropSubtitleAsset> asset(new dcp::InteropSubtitleAsset(dir / "subs.xml"));
636 shared_ptr<dcp::ReelAsset> reel_asset(new dcp::ReelSubtitleAsset(asset, dcp::Fraction(24, 1), 16 * 24, 0));
637 shared_ptr<dcp::Reel> reel(new dcp::Reel());
638 reel->add (reel_asset);
639 shared_ptr<dcp::CPL> cpl(new dcp::CPL("hello", dcp::FEATURE));
641 shared_ptr<dcp::DCP> dcp(new dcp::DCP(dir));
643 dcp->write_xml (dcp::INTEROP);
645 vector<boost::filesystem::path> dirs;
646 dirs.push_back (dir);
647 list<dcp::VerificationNote> notes = dcp::verify (dirs, &stage, &progress, xsd_test);
648 BOOST_REQUIRE_EQUAL (notes.size(), 1U);
649 list<dcp::VerificationNote>::const_iterator i = notes.begin ();
650 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::NOT_SMPTE);
654 /* DCP with broken Interop subtitles */
655 BOOST_AUTO_TEST_CASE (verify_test19)
657 boost::filesystem::path const dir("build/test/verify_test19");
658 prepare_directory (dir);
659 boost::filesystem::copy_file ("test/data/subs1.xml", dir / "subs.xml");
660 shared_ptr<dcp::InteropSubtitleAsset> asset(new dcp::InteropSubtitleAsset(dir / "subs.xml"));
661 shared_ptr<dcp::ReelAsset> reel_asset(new dcp::ReelSubtitleAsset(asset, dcp::Fraction(24, 1), 16 * 24, 0));
662 shared_ptr<dcp::Reel> reel(new dcp::Reel());
663 reel->add (reel_asset);
664 shared_ptr<dcp::CPL> cpl(new dcp::CPL("hello", dcp::FEATURE));
666 shared_ptr<dcp::DCP> dcp(new dcp::DCP(dir));
668 dcp->write_xml (dcp::INTEROP);
671 Editor e (dir / "subs.xml");
672 e.replace ("</ReelNumber>", "</ReelNumber><Foo></Foo>");
675 vector<boost::filesystem::path> dirs;
676 dirs.push_back (dir);
677 list<dcp::VerificationNote> notes = dcp::verify (dirs, &stage, &progress, xsd_test);
678 BOOST_REQUIRE_EQUAL (notes.size(), 3);
679 list<dcp::VerificationNote>::const_iterator i = notes.begin ();
680 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::NOT_SMPTE);
682 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::XML_VALIDATION_ERROR);
684 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::XML_VALIDATION_ERROR);
689 /* DCP with valid SMPTE subtitles */
690 BOOST_AUTO_TEST_CASE (verify_test20)
692 boost::filesystem::path const dir("build/test/verify_test20");
693 prepare_directory (dir);
694 boost::filesystem::copy_file ("test/data/subs.mxf", dir / "subs.mxf");
695 shared_ptr<dcp::SMPTESubtitleAsset> asset(new dcp::SMPTESubtitleAsset(dir / "subs.mxf"));
696 shared_ptr<dcp::ReelAsset> reel_asset(new dcp::ReelSubtitleAsset(asset, dcp::Fraction(24, 1), 16 * 24, 0));
697 shared_ptr<dcp::Reel> reel(new dcp::Reel());
698 reel->add (reel_asset);
699 shared_ptr<dcp::CPL> cpl(new dcp::CPL("hello", dcp::FEATURE));
701 shared_ptr<dcp::DCP> dcp(new dcp::DCP(dir));
703 dcp->write_xml (dcp::SMPTE);
705 vector<boost::filesystem::path> dirs;
706 dirs.push_back (dir);
707 list<dcp::VerificationNote> notes = dcp::verify (dirs, &stage, &progress, xsd_test);
709 BOOST_REQUIRE_EQUAL (notes.size(), 0);
713 /* DCP with broken SMPTE subtitles */
714 BOOST_AUTO_TEST_CASE (verify_test21)
716 boost::filesystem::path const dir("build/test/verify_test21");
717 prepare_directory (dir);
718 boost::filesystem::copy_file ("test/data/broken_smpte.mxf", dir / "subs.mxf");
719 shared_ptr<dcp::SMPTESubtitleAsset> asset(new dcp::SMPTESubtitleAsset(dir / "subs.mxf"));
720 shared_ptr<dcp::ReelAsset> reel_asset(new dcp::ReelSubtitleAsset(asset, dcp::Fraction(24, 1), 16 * 24, 0));
721 shared_ptr<dcp::Reel> reel(new dcp::Reel());
722 reel->add (reel_asset);
723 shared_ptr<dcp::CPL> cpl(new dcp::CPL("hello", dcp::FEATURE));
725 shared_ptr<dcp::DCP> dcp(new dcp::DCP(dir));
727 dcp->write_xml (dcp::SMPTE);
729 vector<boost::filesystem::path> dirs;
730 dirs.push_back (dir);
731 list<dcp::VerificationNote> notes = dcp::verify (dirs, &stage, &progress, xsd_test);
732 BOOST_REQUIRE_EQUAL (notes.size(), 3);
733 list<dcp::VerificationNote>::const_iterator i = notes.begin();
734 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::XML_VALIDATION_ERROR);
736 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::XML_VALIDATION_ERROR);
738 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::MISSING_SUBTITLE_START_TIME);
743 BOOST_AUTO_TEST_CASE (verify_test22)
745 boost::filesystem::path const ov_dir("build/test/verify_test22_ov");
746 prepare_directory (ov_dir);
748 shared_ptr<dcp::OpenJPEGImage> image = black_image ();
749 dcp::ArrayData frame = dcp::compress_j2k (image, 100000000, 24, false, false);
750 BOOST_REQUIRE (frame.size() < 230000000 / (24 * 8));
751 dcp_from_frame (frame, ov_dir);
753 dcp::DCP ov (ov_dir);
756 boost::filesystem::path const vf_dir("build/test/verify_test22_vf");
757 prepare_directory (vf_dir);
759 shared_ptr<dcp::Reel> reel(new dcp::Reel());
760 reel->add (ov.cpls().front()->reels().front()->main_picture());
761 shared_ptr<dcp::CPL> cpl(new dcp::CPL("hello", dcp::FEATURE));
763 dcp::DCP vf (vf_dir);
765 vf.write_xml (dcp::SMPTE);
767 vector<boost::filesystem::path> dirs;
768 dirs.push_back (vf_dir);
769 list<dcp::VerificationNote> notes = dcp::verify (dirs, &stage, &progress, xsd_test);
770 BOOST_REQUIRE_EQUAL (notes.size(), 1);
771 BOOST_CHECK_EQUAL (notes.front().code(), dcp::VerificationNote::EXTERNAL_ASSET);
775 /* DCP with valid CompositionMetadataAsset */
776 BOOST_AUTO_TEST_CASE (verify_test23)
778 boost::filesystem::path const dir("build/test/verify_test23");
779 prepare_directory (dir);
781 boost::filesystem::copy_file ("test/data/subs.mxf", dir / "subs.mxf");
782 shared_ptr<dcp::SMPTESubtitleAsset> asset(new dcp::SMPTESubtitleAsset(dir / "subs.mxf"));
783 shared_ptr<dcp::ReelAsset> reel_asset(new dcp::ReelSubtitleAsset(asset, dcp::Fraction(24, 1), 16 * 24, 0));
785 shared_ptr<dcp::Reel> reel(new dcp::Reel());
786 reel->add (reel_asset);
787 shared_ptr<dcp::CPL> cpl(new dcp::CPL("hello", dcp::FEATURE));
789 cpl->set_main_sound_configuration ("L,C,R,Lfe,-,-");
790 cpl->set_main_sound_sample_rate (48000);
791 cpl->set_main_picture_stored_area (dcp::Size(1998, 1080));
792 cpl->set_main_picture_active_area (dcp::Size(1440, 1080));
796 dcp.write_xml (dcp::SMPTE);
798 vector<boost::filesystem::path> dirs;
799 dirs.push_back (dir);
800 list<dcp::VerificationNote> notes = dcp::verify (dirs, &stage, &progress, xsd_test);
804 boost::filesystem::path find_cpl (boost::filesystem::path dir)
806 for (boost::filesystem::directory_iterator i = boost::filesystem::directory_iterator(dir); i != boost::filesystem::directory_iterator(); i++) {
807 if (boost::starts_with(i->path().filename().string(), "cpl_")) {
812 BOOST_REQUIRE (false);
813 return boost::filesystem::path();
817 /* DCP with invalid CompositionMetadataAsset */
818 BOOST_AUTO_TEST_CASE (verify_test24)
820 boost::filesystem::path const dir("build/test/verify_test24");
821 prepare_directory (dir);
823 shared_ptr<dcp::Reel> reel(new dcp::Reel());
824 reel->add (black_picture_asset(dir));
825 shared_ptr<dcp::CPL> cpl(new dcp::CPL("hello", dcp::FEATURE));
827 cpl->set_main_sound_configuration ("L,C,R,Lfe,-,-");
828 cpl->set_main_sound_sample_rate (48000);
829 cpl->set_main_picture_stored_area (dcp::Size(1998, 1080));
830 cpl->set_main_picture_active_area (dcp::Size(1440, 1080));
834 dcp.write_xml (dcp::SMPTE);
837 Editor e (find_cpl("build/test/verify_test24"));
838 e.replace ("MainSound", "MainSoundX");
841 vector<boost::filesystem::path> dirs;
842 dirs.push_back (dir);
843 list<dcp::VerificationNote> notes = dcp::verify (dirs, &stage, &progress, xsd_test);
844 BOOST_REQUIRE_EQUAL (notes.size(), 4);
846 list<dcp::VerificationNote>::const_iterator i = notes.begin ();
847 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::XML_VALIDATION_ERROR);
849 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::XML_VALIDATION_ERROR);
851 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::XML_VALIDATION_ERROR);
853 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::CPL_HASH_INCORRECT);
858 /* DCP with invalid CompositionMetadataAsset */
859 BOOST_AUTO_TEST_CASE (verify_test25)
861 boost::filesystem::path const dir("build/test/verify_test25");
862 prepare_directory (dir);
864 boost::filesystem::copy_file ("test/data/subs.mxf", dir / "subs.mxf");
865 shared_ptr<dcp::SMPTESubtitleAsset> asset(new dcp::SMPTESubtitleAsset(dir / "subs.mxf"));
866 shared_ptr<dcp::ReelAsset> reel_asset(new dcp::ReelSubtitleAsset(asset, dcp::Fraction(24, 1), 16 * 24, 0));
868 shared_ptr<dcp::Reel> reel(new dcp::Reel());
869 reel->add (reel_asset);
870 shared_ptr<dcp::CPL> cpl(new dcp::CPL("hello", dcp::FEATURE));
872 cpl->set_main_sound_configuration ("L,C,R,Lfe,-,-");
873 cpl->set_main_sound_sample_rate (48000);
874 cpl->set_main_picture_stored_area (dcp::Size(1998, 1080));
875 cpl->set_main_picture_active_area (dcp::Size(1440, 1080));
879 dcp.write_xml (dcp::SMPTE);
882 Editor e (find_cpl("build/test/verify_test25"));
883 e.replace ("</MainPictureActiveArea>", "</MainPictureActiveArea><BadTag></BadTag>");
886 vector<boost::filesystem::path> dirs;
887 dirs.push_back (dir);
888 list<dcp::VerificationNote> notes = dcp::verify (dirs, &stage, &progress, xsd_test);
892 /* SMPTE DCP with invalid <Language> in the MainSubtitle reel and also in the XML within the MXF */
893 BOOST_AUTO_TEST_CASE (verify_test26)
895 boost::filesystem::path const dir("build/test/verify_test26");
896 prepare_directory (dir);
897 boost::filesystem::copy_file ("test/data/subs.mxf", dir / "subs.mxf");
898 shared_ptr<dcp::SMPTESubtitleAsset> asset(new dcp::SMPTESubtitleAsset(dir / "subs.mxf"));
899 asset->_language = "wrong-andbad";
900 asset->write (dir / "subs.mxf");
901 shared_ptr<dcp::ReelSubtitleAsset> reel_asset(new dcp::ReelSubtitleAsset(asset, dcp::Fraction(24, 1), 16 * 24, 0));
902 reel_asset->_language = "badlang";
903 shared_ptr<dcp::Reel> reel(new dcp::Reel());
904 reel->add (reel_asset);
905 shared_ptr<dcp::CPL> cpl(new dcp::CPL("hello", dcp::FEATURE));
907 shared_ptr<dcp::DCP> dcp(new dcp::DCP(dir));
909 dcp->write_xml (dcp::SMPTE);
911 vector<boost::filesystem::path> dirs;
912 dirs.push_back (dir);
913 list<dcp::VerificationNote> notes = dcp::verify (dirs, &stage, &progress, xsd_test);
914 BOOST_REQUIRE_EQUAL (notes.size(), 2U);
915 list<dcp::VerificationNote>::const_iterator i = notes.begin ();
916 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::BAD_LANGUAGE);
917 BOOST_REQUIRE (i->note());
918 BOOST_CHECK_EQUAL (*i->note(), "badlang");
920 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::BAD_LANGUAGE);
921 BOOST_REQUIRE (i->note());
922 BOOST_CHECK_EQUAL (*i->note(), "wrong-andbad");
926 /* SMPTE DCP with invalid <Language> in the MainClosedCaption reel and also in the XML within the MXF */
927 BOOST_AUTO_TEST_CASE (verify_invalid_closed_caption_languages)
929 boost::filesystem::path const dir("build/test/verify_invalid_closed_caption_languages");
930 prepare_directory (dir);
931 boost::filesystem::copy_file ("test/data/subs.mxf", dir / "subs.mxf");
932 shared_ptr<dcp::SMPTESubtitleAsset> asset(new dcp::SMPTESubtitleAsset(dir / "subs.mxf"));
933 asset->_language = "wrong-andbad";
934 asset->write (dir / "subs.mxf");
935 shared_ptr<dcp::ReelClosedCaptionAsset> reel_asset(new dcp::ReelClosedCaptionAsset(asset, dcp::Fraction(24, 1), 16 * 24, 0));
936 reel_asset->_language = "badlang";
937 shared_ptr<dcp::Reel> reel(new dcp::Reel());
938 reel->add (reel_asset);
939 shared_ptr<dcp::CPL> cpl(new dcp::CPL("hello", dcp::FEATURE));
941 shared_ptr<dcp::DCP> dcp(new dcp::DCP(dir));
943 dcp->write_xml (dcp::SMPTE);
945 vector<boost::filesystem::path> dirs;
946 dirs.push_back (dir);
947 list<dcp::VerificationNote> notes = dcp::verify (dirs, &stage, &progress, xsd_test);
948 BOOST_REQUIRE_EQUAL (notes.size(), 2U);
949 list<dcp::VerificationNote>::const_iterator i = notes.begin ();
950 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::BAD_LANGUAGE);
951 BOOST_REQUIRE (i->note());
952 BOOST_CHECK_EQUAL (*i->note(), "badlang");
954 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::BAD_LANGUAGE);
955 BOOST_REQUIRE (i->note());
956 BOOST_CHECK_EQUAL (*i->note(), "wrong-andbad");
960 /* SMPTE DCP with invalid <Language> in the MainSound reel, the CPL additional subtitles languages and
961 * the release territory.
963 BOOST_AUTO_TEST_CASE (verify_various_invalid_languages)
965 boost::filesystem::path const dir("build/test/verify_various_invalid_languages");
966 prepare_directory (dir);
968 shared_ptr<dcp::MonoPictureAsset> picture = simple_picture (dir, "foo");
969 shared_ptr<dcp::ReelPictureAsset> reel_picture(new dcp::ReelMonoPictureAsset(picture, 0));
970 shared_ptr<dcp::Reel> reel(new dcp::Reel());
971 reel->add (reel_picture);
972 shared_ptr<dcp::SoundAsset> sound = simple_sound (dir, "foo", dcp::MXFMetadata(), "frobozz");
973 shared_ptr<dcp::ReelSoundAsset> reel_sound(new dcp::ReelSoundAsset(sound, 0));
974 reel->add (reel_sound);
975 shared_ptr<dcp::CPL> cpl(new dcp::CPL("hello", dcp::FEATURE));
977 cpl->_additional_subtitle_languages.push_back("this-is-wrong");
978 cpl->_additional_subtitle_languages.push_back("andso-is-this");
979 cpl->set_main_sound_configuration ("L,C,R,Lfe,-,-");
980 cpl->set_main_sound_sample_rate (48000);
981 cpl->set_main_picture_stored_area (dcp::Size(1998, 1080));
982 cpl->set_main_picture_active_area (dcp::Size(1440, 1080));
983 cpl->_release_territory = "fred-jim";
984 shared_ptr<dcp::DCP> dcp(new dcp::DCP(dir));
986 dcp->write_xml (dcp::SMPTE);
988 vector<boost::filesystem::path> dirs;
989 dirs.push_back (dir);
990 list<dcp::VerificationNote> notes = dcp::verify (dirs, &stage, &progress, xsd_test);
991 BOOST_REQUIRE_EQUAL (notes.size(), 4U);
992 list<dcp::VerificationNote>::const_iterator i = notes.begin ();
993 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::BAD_LANGUAGE);
994 BOOST_REQUIRE (i->note());
995 BOOST_CHECK_EQUAL (*i->note(), "this-is-wrong");
997 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::BAD_LANGUAGE);
998 BOOST_REQUIRE (i->note());
999 BOOST_CHECK_EQUAL (*i->note(), "andso-is-this");
1001 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::BAD_LANGUAGE);
1002 BOOST_REQUIRE (i->note());
1003 BOOST_CHECK_EQUAL (*i->note(), "fred-jim");
1005 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::BAD_LANGUAGE);
1006 BOOST_REQUIRE (i->note());
1007 BOOST_CHECK_EQUAL (*i->note(), "frobozz");
1013 list<dcp::VerificationNote>
1014 check_picture_size (int width, int height, int frame_rate, bool three_d)
1016 using namespace boost::filesystem;
1018 path dcp_path = "build/test/verify_picture_test";
1019 remove_all (dcp_path);
1020 create_directories (dcp_path);
1022 shared_ptr<dcp::PictureAsset> mp;
1024 mp.reset (new dcp::StereoPictureAsset(dcp::Fraction(frame_rate, 1), dcp::SMPTE));
1026 mp.reset (new dcp::MonoPictureAsset(dcp::Fraction(frame_rate, 1), dcp::SMPTE));
1028 shared_ptr<dcp::PictureAssetWriter> picture_writer = mp->start_write (dcp_path / "video.mxf", false);
1030 shared_ptr<dcp::OpenJPEGImage> image = black_image (dcp::Size(width, height));
1031 dcp::ArrayData j2c = dcp::compress_j2k (image, 100000000, frame_rate, three_d, width > 2048);
1032 int const length = three_d ? frame_rate * 2 : frame_rate;
1033 for (int i = 0; i < length; ++i) {
1034 picture_writer->write (j2c.data(), j2c.size());
1036 picture_writer->finalize ();
1038 shared_ptr<dcp::DCP> d (new dcp::DCP(dcp_path));
1039 shared_ptr<dcp::CPL> cpl (new dcp::CPL("A Test DCP", dcp::FEATURE));
1040 cpl->set_annotation_text ("A Test DCP");
1041 cpl->set_issue_date ("2012-07-17T04:45:18+00:00");
1043 shared_ptr<dcp::Reel> reel(new dcp::Reel());
1047 shared_ptr<dcp::ReelPictureAsset>(
1048 new dcp::ReelStereoPictureAsset(
1049 std::dynamic_pointer_cast<dcp::StereoPictureAsset>(mp),
1055 shared_ptr<dcp::ReelPictureAsset>(
1056 new dcp::ReelMonoPictureAsset(
1057 std::dynamic_pointer_cast<dcp::MonoPictureAsset>(mp),
1066 d->write_xml (dcp::SMPTE);
1068 vector<boost::filesystem::path> dirs;
1069 dirs.push_back (dcp_path);
1070 return dcp::verify (dirs, &stage, &progress, xsd_test);
1076 check_picture_size_ok (int width, int height, int frame_rate, bool three_d)
1078 list<dcp::VerificationNote> notes = check_picture_size(width, height, frame_rate, three_d);
1080 BOOST_CHECK_EQUAL (notes.size(), 0U);
1086 check_picture_size_bad_frame_size (int width, int height, int frame_rate, bool three_d)
1088 list<dcp::VerificationNote> notes = check_picture_size(width, height, frame_rate, three_d);
1089 BOOST_REQUIRE_EQUAL (notes.size(), 1U);
1090 BOOST_CHECK_EQUAL (notes.front().type(), dcp::VerificationNote::VERIFY_BV21_ERROR);
1091 BOOST_CHECK_EQUAL (notes.front().code(), dcp::VerificationNote::PICTURE_ASSET_INVALID_SIZE_IN_PIXELS);
1097 check_picture_size_bad_2k_frame_rate (int width, int height, int frame_rate, bool three_d)
1099 list<dcp::VerificationNote> notes = check_picture_size(width, height, frame_rate, three_d);
1100 BOOST_REQUIRE_EQUAL (notes.size(), 2U);
1101 BOOST_CHECK_EQUAL (notes.back().type(), dcp::VerificationNote::VERIFY_BV21_ERROR);
1102 BOOST_CHECK_EQUAL (notes.back().code(), dcp::VerificationNote::PICTURE_ASSET_INVALID_FRAME_RATE_FOR_2K);
1108 check_picture_size_bad_4k_frame_rate (int width, int height, int frame_rate, bool three_d)
1110 list<dcp::VerificationNote> notes = check_picture_size(width, height, frame_rate, three_d);
1111 BOOST_REQUIRE_EQUAL (notes.size(), 1U);
1112 BOOST_CHECK_EQUAL (notes.front().type(), dcp::VerificationNote::VERIFY_BV21_ERROR);
1113 BOOST_CHECK_EQUAL (notes.front().code(), dcp::VerificationNote::PICTURE_ASSET_INVALID_FRAME_RATE_FOR_4K);
1117 BOOST_AUTO_TEST_CASE (verify_picture_size)
1119 using namespace boost::filesystem;
1122 check_picture_size_ok (2048, 858, 24, false);
1123 check_picture_size_ok (2048, 858, 25, false);
1124 check_picture_size_ok (2048, 858, 48, false);
1125 check_picture_size_ok (2048, 858, 24, true);
1126 check_picture_size_ok (2048, 858, 25, true);
1127 check_picture_size_ok (2048, 858, 48, true);
1130 check_picture_size_ok (1998, 1080, 24, false);
1131 check_picture_size_ok (1998, 1080, 25, false);
1132 check_picture_size_ok (1998, 1080, 48, false);
1133 check_picture_size_ok (1998, 1080, 24, true);
1134 check_picture_size_ok (1998, 1080, 25, true);
1135 check_picture_size_ok (1998, 1080, 48, true);
1138 check_picture_size_ok (4096, 1716, 24, false);
1141 check_picture_size_ok (3996, 2160, 24, false);
1143 /* Bad frame size */
1144 check_picture_size_bad_frame_size (2050, 858, 24, false);
1145 check_picture_size_bad_frame_size (2048, 658, 25, false);
1146 check_picture_size_bad_frame_size (1920, 1080, 48, true);
1147 check_picture_size_bad_frame_size (4000, 3000, 24, true);
1149 /* Bad 2K frame rate */
1150 check_picture_size_bad_2k_frame_rate (2048, 858, 26, false);
1151 check_picture_size_bad_2k_frame_rate (2048, 858, 31, false);
1152 check_picture_size_bad_2k_frame_rate (1998, 1080, 50, true);
1154 /* Bad 4K frame rate */
1155 check_picture_size_bad_4k_frame_rate (3996, 2160, 25, false);
1156 check_picture_size_bad_4k_frame_rate (3996, 2160, 48, false);
1159 list<dcp::VerificationNote> notes = check_picture_size(3996, 2160, 24, true);
1160 BOOST_REQUIRE_EQUAL (notes.size(), 1U);
1161 BOOST_CHECK_EQUAL (notes.front().type(), dcp::VerificationNote::VERIFY_BV21_ERROR);
1162 BOOST_CHECK_EQUAL (notes.front().code(), dcp::VerificationNote::PICTURE_ASSET_4K_3D);
1166 BOOST_AUTO_TEST_CASE (verify_closed_caption_xml_too_large)
1168 boost::filesystem::path const dir("build/test/verify_closed_caption_xml_too_large");
1169 prepare_directory (dir);
1171 shared_ptr<dcp::SMPTESubtitleAsset> asset(new dcp::SMPTESubtitleAsset());
1172 for (int i = 0; i < 2048; ++i) {
1174 shared_ptr<dcp::Subtitle>(
1175 new dcp::SubtitleString(
1183 dcp::Time(i * 24, 24, 24),
1184 dcp::Time(i * 24 + 20, 24, 24),
1199 asset->set_language (dcp::LanguageTag("de-DE"));
1200 asset->write (dir / "subs.mxf");
1201 shared_ptr<dcp::ReelClosedCaptionAsset> reel_asset(new dcp::ReelClosedCaptionAsset(asset, dcp::Fraction(24, 1), 16 * 24, 0));
1202 shared_ptr<dcp::Reel> reel(new dcp::Reel());
1203 reel->add (reel_asset);
1204 shared_ptr<dcp::CPL> cpl(new dcp::CPL("hello", dcp::FEATURE));
1206 shared_ptr<dcp::DCP> dcp(new dcp::DCP(dir));
1208 dcp->write_xml (dcp::SMPTE);
1210 vector<boost::filesystem::path> dirs;
1211 dirs.push_back (dir);
1212 list<dcp::VerificationNote> notes = dcp::verify (dirs, &stage, &progress, xsd_test);
1213 BOOST_REQUIRE_EQUAL (notes.size(), 2U);
1214 list<dcp::VerificationNote>::const_iterator i = notes.begin();
1215 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::MISSING_SUBTITLE_START_TIME);
1217 BOOST_CHECK_EQUAL (i->type(), dcp::VerificationNote::VERIFY_BV21_ERROR);
1218 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::CLOSED_CAPTION_XML_TOO_LARGE_IN_BYTES);
1223 shared_ptr<dcp::SMPTESubtitleAsset>
1224 make_large_subtitle_asset (boost::filesystem::path font_file)
1226 shared_ptr<dcp::SMPTESubtitleAsset> asset(new dcp::SMPTESubtitleAsset());
1227 dcp::ArrayData big_fake_font(1024 * 1024);
1228 big_fake_font.write (font_file);
1229 for (int i = 0; i < 116; ++i) {
1230 asset->add_font (dcp::String::compose("big%1", i), big_fake_font);
1238 verify_timed_text_asset_too_large (string name)
1240 boost::filesystem::path const dir = boost::filesystem::path("build/test") / name;
1241 prepare_directory (dir);
1242 shared_ptr<dcp::SMPTESubtitleAsset> asset = make_large_subtitle_asset (dir / "font.ttf");
1244 shared_ptr<dcp::Subtitle>(
1245 new dcp::SubtitleString(
1253 dcp::Time(0, 24, 24),
1254 dcp::Time(20, 24, 24),
1268 asset->set_language (dcp::LanguageTag("de-DE"));
1269 asset->write (dir / "subs.mxf");
1271 shared_ptr<T> reel_asset(new T(asset, dcp::Fraction(24, 1), 16 * 24, 0));
1272 shared_ptr<dcp::Reel> reel(new dcp::Reel());
1273 reel->add (reel_asset);
1274 shared_ptr<dcp::CPL> cpl(new dcp::CPL("hello", dcp::FEATURE));
1276 shared_ptr<dcp::DCP> dcp(new dcp::DCP(dir));
1278 dcp->write_xml (dcp::SMPTE);
1280 vector<boost::filesystem::path> dirs;
1281 dirs.push_back (dir);
1282 list<dcp::VerificationNote> notes = dcp::verify (dirs, &stage, &progress, xsd_test);
1283 BOOST_REQUIRE_EQUAL (notes.size(), 3U);
1284 list<dcp::VerificationNote>::const_iterator i = notes.begin();
1285 BOOST_CHECK_EQUAL (i->type(), dcp::VerificationNote::VERIFY_BV21_ERROR);
1286 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::TIMED_TEXT_ASSET_TOO_LARGE_IN_BYTES);
1288 BOOST_CHECK_EQUAL (i->type(), dcp::VerificationNote::VERIFY_BV21_ERROR);
1289 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::TIMED_TEXT_FONTS_TOO_LARGE_IN_BYTES);
1291 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::MISSING_SUBTITLE_START_TIME);
1295 BOOST_AUTO_TEST_CASE (verify_subtitle_asset_too_large)
1297 verify_timed_text_asset_too_large<dcp::ReelSubtitleAsset>("verify_subtitle_asset_too_large");
1298 verify_timed_text_asset_too_large<dcp::ReelClosedCaptionAsset>("verify_closed_caption_asset_too_large");
1302 BOOST_AUTO_TEST_CASE (verify_missing_language_tag_in_subtitle_xml)
1304 boost::filesystem::path dir = "build/test/verify_missing_language_tag_in_subtitle_xml";
1305 prepare_directory (dir);
1306 shared_ptr<dcp::DCP> dcp = make_simple (dir, 1);
1309 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
1310 "<SubtitleReel xmlns=\"http://www.smpte-ra.org/schemas/428-7/2010/DCST\" xmlns:xs=\"http://www.w3.org/2001/schema\">"
1311 "<Id>urn:uuid:e6a8ae03-ebbf-41ed-9def-913a87d1493a</Id>"
1312 "<ContentTitleText>Content</ContentTitleText>"
1313 "<AnnotationText>Annotation</AnnotationText>"
1314 "<IssueDate>2018-10-02T12:25:14+02:00</IssueDate>"
1315 "<ReelNumber>1</ReelNumber>"
1316 "<EditRate>25 1</EditRate>"
1317 "<TimeCodeRate>25</TimeCodeRate>"
1318 "<StartTime>00:00:00:00</StartTime>"
1319 "<LoadFont ID=\"arial\">urn:uuid:e4f0ff0a-9eba-49e0-92ee-d89a88a575f6</LoadFont>"
1321 "<Font ID=\"arial\" Color=\"FFFEFEFE\" Weight=\"normal\" Size=\"42\" Effect=\"border\" EffectColor=\"FF181818\" AspectAdjust=\"1.00\">"
1322 "<Subtitle SpotNumber=\"1\" TimeIn=\"00:00:03:00\" TimeOut=\"00:00:04:10\" FadeUpTime=\"00:00:00:00\" FadeDownTime=\"00:00:00:00\">"
1323 "<Text Hposition=\"0.0\" Halign=\"center\" Valign=\"bottom\" Vposition=\"13.5\" Direction=\"ltr\">Hello world</Text>"
1329 FILE* xml_file = dcp::fopen_boost (dir / "subs.xml", "w");
1330 BOOST_REQUIRE (xml_file);
1331 fwrite (xml.c_str(), xml.size(), 1, xml_file);
1333 shared_ptr<dcp::SMPTESubtitleAsset> subs (new dcp::SMPTESubtitleAsset(dir / "subs.xml"));
1334 subs->write (dir / "subs.mxf");
1336 shared_ptr<dcp::ReelSubtitleAsset> reel_subs (new dcp::ReelSubtitleAsset(subs, dcp::Fraction(24, 1), 100, 0));
1337 dcp->cpls().front()->reels().front()->add (reel_subs);
1338 dcp->write_xml (dcp::SMPTE);
1340 vector<boost::filesystem::path> dirs;
1341 dirs.push_back (dir);
1342 list<dcp::VerificationNote> notes = dcp::verify (dirs, &stage, &progress, xsd_test);
1343 BOOST_REQUIRE_EQUAL (notes.size(), 1U);
1344 BOOST_CHECK_EQUAL (notes.front().type(), dcp::VerificationNote::VERIFY_BV21_ERROR);
1345 BOOST_CHECK_EQUAL (notes.front().code(), dcp::VerificationNote::MISSING_SUBTITLE_LANGUAGE);
1349 BOOST_AUTO_TEST_CASE (verify_inconsistent_subtitle_languages)
1351 boost::filesystem::path path ("build/test/verify_inconsistent_subtitle_languages");
1352 shared_ptr<dcp::DCP> dcp = make_simple (path, 2);
1353 shared_ptr<dcp::CPL> cpl = dcp->cpls().front();
1356 shared_ptr<dcp::SMPTESubtitleAsset> subs(new dcp::SMPTESubtitleAsset());
1357 subs->set_language (dcp::LanguageTag("de-DE"));
1358 subs->add (simple_subtitle());
1359 subs->write (path / "subs1.mxf");
1360 shared_ptr<dcp::ReelSubtitleAsset> reel_subs(new dcp::ReelSubtitleAsset(subs, dcp::Fraction(24, 1), 240, 0));
1361 cpl->reels().front()->add (reel_subs);
1365 shared_ptr<dcp::SMPTESubtitleAsset> subs(new dcp::SMPTESubtitleAsset());
1366 subs->set_language (dcp::LanguageTag("en-US"));
1367 subs->add (simple_subtitle());
1368 subs->write (path / "subs2.mxf");
1369 shared_ptr<dcp::ReelSubtitleAsset> reel_subs(new dcp::ReelSubtitleAsset(subs, dcp::Fraction(24, 1), 240, 0));
1370 cpl->reels().back()->add (reel_subs);
1373 dcp->write_xml (dcp::SMPTE);
1375 vector<boost::filesystem::path> dirs;
1376 dirs.push_back (path);
1377 list<dcp::VerificationNote> notes = dcp::verify (dirs, &stage, &progress, xsd_test);
1378 BOOST_REQUIRE_EQUAL (notes.size(), 3U);
1379 list<dcp::VerificationNote>::const_iterator i = notes.begin();
1380 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::MISSING_SUBTITLE_START_TIME);
1382 BOOST_CHECK_EQUAL (i->type(), dcp::VerificationNote::VERIFY_BV21_ERROR);
1384 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::MISSING_SUBTITLE_START_TIME);
1388 BOOST_AUTO_TEST_CASE (verify_missing_start_time_tag_in_subtitle_xml)
1390 boost::filesystem::path dir = "build/test/verify_missing_start_time_tag_in_subtitle_xml";
1391 prepare_directory (dir);
1392 shared_ptr<dcp::DCP> dcp = make_simple (dir, 1);
1395 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
1396 "<SubtitleReel xmlns=\"http://www.smpte-ra.org/schemas/428-7/2010/DCST\" xmlns:xs=\"http://www.w3.org/2001/schema\">"
1397 "<Id>urn:uuid:e6a8ae03-ebbf-41ed-9def-913a87d1493a</Id>"
1398 "<ContentTitleText>Content</ContentTitleText>"
1399 "<AnnotationText>Annotation</AnnotationText>"
1400 "<IssueDate>2018-10-02T12:25:14+02:00</IssueDate>"
1401 "<ReelNumber>1</ReelNumber>"
1402 "<Language>de-DE</Language>"
1403 "<EditRate>25 1</EditRate>"
1404 "<TimeCodeRate>25</TimeCodeRate>"
1405 "<LoadFont ID=\"arial\">urn:uuid:e4f0ff0a-9eba-49e0-92ee-d89a88a575f6</LoadFont>"
1407 "<Font ID=\"arial\" Color=\"FFFEFEFE\" Weight=\"normal\" Size=\"42\" Effect=\"border\" EffectColor=\"FF181818\" AspectAdjust=\"1.00\">"
1408 "<Subtitle SpotNumber=\"1\" TimeIn=\"00:00:03:00\" TimeOut=\"00:00:04:10\" FadeUpTime=\"00:00:00:00\" FadeDownTime=\"00:00:00:00\">"
1409 "<Text Hposition=\"0.0\" Halign=\"center\" Valign=\"bottom\" Vposition=\"13.5\" Direction=\"ltr\">Hello world</Text>"
1415 FILE* xml_file = dcp::fopen_boost (dir / "subs.xml", "w");
1416 BOOST_REQUIRE (xml_file);
1417 fwrite (xml.c_str(), xml.size(), 1, xml_file);
1419 shared_ptr<dcp::SMPTESubtitleAsset> subs (new dcp::SMPTESubtitleAsset(dir / "subs.xml"));
1420 subs->write (dir / "subs.mxf");
1422 shared_ptr<dcp::ReelSubtitleAsset> reel_subs (new dcp::ReelSubtitleAsset(subs, dcp::Fraction(24, 1), 100, 0));
1423 dcp->cpls().front()->reels().front()->add (reel_subs);
1424 dcp->write_xml (dcp::SMPTE);
1426 vector<boost::filesystem::path> dirs;
1427 dirs.push_back (dir);
1428 list<dcp::VerificationNote> notes = dcp::verify (dirs, &stage, &progress, xsd_test);
1429 BOOST_REQUIRE_EQUAL (notes.size(), 1U);
1430 BOOST_CHECK_EQUAL (notes.front().type(), dcp::VerificationNote::VERIFY_BV21_ERROR);
1431 BOOST_CHECK_EQUAL (notes.front().code(), dcp::VerificationNote::MISSING_SUBTITLE_START_TIME);
1435 BOOST_AUTO_TEST_CASE (verify_non_zero_start_time_tag_in_subtitle_xml)
1437 boost::filesystem::path dir = "build/test/verify_non_zero_start_time_tag_in_subtitle_xml";
1438 prepare_directory (dir);
1439 shared_ptr<dcp::DCP> dcp = make_simple (dir, 1);
1442 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
1443 "<SubtitleReel xmlns=\"http://www.smpte-ra.org/schemas/428-7/2010/DCST\" xmlns:xs=\"http://www.w3.org/2001/schema\">"
1444 "<Id>urn:uuid:e6a8ae03-ebbf-41ed-9def-913a87d1493a</Id>"
1445 "<ContentTitleText>Content</ContentTitleText>"
1446 "<AnnotationText>Annotation</AnnotationText>"
1447 "<IssueDate>2018-10-02T12:25:14+02:00</IssueDate>"
1448 "<ReelNumber>1</ReelNumber>"
1449 "<Language>de-DE</Language>"
1450 "<EditRate>25 1</EditRate>"
1451 "<TimeCodeRate>25</TimeCodeRate>"
1452 "<StartTime>00:00:02:00</StartTime>"
1453 "<LoadFont ID=\"arial\">urn:uuid:e4f0ff0a-9eba-49e0-92ee-d89a88a575f6</LoadFont>"
1455 "<Font ID=\"arial\" Color=\"FFFEFEFE\" Weight=\"normal\" Size=\"42\" Effect=\"border\" EffectColor=\"FF181818\" AspectAdjust=\"1.00\">"
1456 "<Subtitle SpotNumber=\"1\" TimeIn=\"00:00:03:00\" TimeOut=\"00:00:04:10\" FadeUpTime=\"00:00:00:00\" FadeDownTime=\"00:00:00:00\">"
1457 "<Text Hposition=\"0.0\" Halign=\"center\" Valign=\"bottom\" Vposition=\"13.5\" Direction=\"ltr\">Hello world</Text>"
1463 FILE* xml_file = dcp::fopen_boost (dir / "subs.xml", "w");
1464 BOOST_REQUIRE (xml_file);
1465 fwrite (xml.c_str(), xml.size(), 1, xml_file);
1467 shared_ptr<dcp::SMPTESubtitleAsset> subs (new dcp::SMPTESubtitleAsset(dir / "subs.xml"));
1468 subs->write (dir / "subs.mxf");
1470 shared_ptr<dcp::ReelSubtitleAsset> reel_subs (new dcp::ReelSubtitleAsset(subs, dcp::Fraction(24, 1), 100, 0));
1471 dcp->cpls().front()->reels().front()->add (reel_subs);
1472 dcp->write_xml (dcp::SMPTE);
1474 vector<boost::filesystem::path> dirs;
1475 dirs.push_back (dir);
1476 list<dcp::VerificationNote> notes = dcp::verify (dirs, &stage, &progress, xsd_test);
1477 BOOST_REQUIRE_EQUAL (notes.size(), 1U);
1478 BOOST_CHECK_EQUAL (notes.front().type(), dcp::VerificationNote::VERIFY_BV21_ERROR);
1479 BOOST_CHECK_EQUAL (notes.front().code(), dcp::VerificationNote::SUBTITLE_START_TIME_NON_ZERO);