2 Copyright (C) 2015-2021 Carl Hetherington <cth@carlh.net>
4 This file is part of DCP-o-matic.
6 DCP-o-matic 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 DCP-o-matic 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 DCP-o-matic. If not, see <http://www.gnu.org/licenses/>.
22 /** @file test/reels_test.cc
23 * @brief Check manipulation of reels in various ways.
28 #include "lib/content_factory.h"
29 #include "lib/dcp_content.h"
30 #include "lib/dcp_content_type.h"
31 #include "lib/ffmpeg_content.h"
33 #include "lib/image_content.h"
34 #include "lib/make_dcp.h"
35 #include "lib/ratio.h"
36 #include "lib/string_text_file_content.h"
37 #include "lib/video_content.h"
41 #include <dcp/reel_picture_asset.h>
42 #include <dcp/reel_sound_asset.h>
43 #include <boost/test/unit_test.hpp>
50 using std::make_shared;
51 using std::shared_ptr;
54 using namespace dcpomatic;
59 filter_ok(std::vector<dcp::VerificationNote>& notes)
61 notes.erase(std::remove_if(notes.begin(), notes.end(), [](dcp::VerificationNote const& note) { return note.type() == dcp::VerificationNote::Type::OK; }), notes.end());
65 /** Test Film::reels() */
66 BOOST_AUTO_TEST_CASE (reels_test1)
68 auto film = new_test_film ("reels_test1");
69 film->set_container (Ratio::from_id ("185"));
70 auto A = make_shared<FFmpegContent>("test/data/test.mp4");
71 film->examine_and_add_content (A);
72 auto B = make_shared<FFmpegContent>("test/data/test.mp4");
73 film->examine_and_add_content (B);
74 BOOST_REQUIRE (!wait_for_jobs());
75 BOOST_CHECK_EQUAL (A->full_length(film).get(), 288000);
77 film->set_reel_type (ReelType::SINGLE);
78 auto r = film->reels ();
79 BOOST_CHECK_EQUAL (r.size(), 1U);
80 BOOST_CHECK_EQUAL (r.front().from.get(), 0);
81 BOOST_CHECK_EQUAL (r.front().to.get(), 288000 * 2);
83 film->set_reel_type (ReelType::BY_VIDEO_CONTENT);
85 BOOST_CHECK_EQUAL (r.size(), 2U);
86 BOOST_CHECK_EQUAL (r.front().from.get(), 0);
87 BOOST_CHECK_EQUAL (r.front().to.get(), 288000);
88 BOOST_CHECK_EQUAL (r.back().from.get(), 288000);
89 BOOST_CHECK_EQUAL (r.back().to.get(), 288000 * 2);
91 film->set_j2k_bandwidth (100000000);
92 film->set_reel_type (ReelType::BY_LENGTH);
93 /* This is just over 2.5s at 100Mbit/s; should correspond to 60 frames */
94 film->set_reel_length (31253154);
96 BOOST_CHECK_EQUAL (r.size(), 3U);
98 BOOST_CHECK_EQUAL (i->from.get(), 0);
99 BOOST_CHECK_EQUAL (i->to.get(), DCPTime::from_frames(60, 24).get());
101 BOOST_CHECK_EQUAL (i->from.get(), DCPTime::from_frames(60, 24).get());
102 BOOST_CHECK_EQUAL (i->to.get(), DCPTime::from_frames(120, 24).get());
104 BOOST_CHECK_EQUAL (i->from.get(), DCPTime::from_frames(120, 24).get());
105 BOOST_CHECK_EQUAL (i->to.get(), DCPTime::from_frames(144, 24).get());
109 /** Make a short DCP with multi reels split by video content, then import
110 * this into a new project and make a new DCP referencing it.
112 BOOST_AUTO_TEST_CASE (reels_test2)
114 auto film = new_test_film ("reels_test2");
115 film->set_name ("reels_test2");
116 film->set_container (Ratio::from_id ("185"));
117 film->set_interop (false);
118 film->set_dcp_content_type (DCPContentType::from_isdcf_name ("TST"));
121 auto c = make_shared<ImageContent>("test/data/flat_red.png");
122 film->examine_and_add_content (c);
123 BOOST_REQUIRE (!wait_for_jobs());
124 c->video->set_length (24);
128 auto c = make_shared<ImageContent>("test/data/flat_green.png");
129 film->examine_and_add_content (c);
130 BOOST_REQUIRE (!wait_for_jobs());
131 c->video->set_length (24);
135 auto c = make_shared<ImageContent>("test/data/flat_blue.png");
136 film->examine_and_add_content (c);
137 BOOST_REQUIRE (!wait_for_jobs());
138 c->video->set_length (24);
141 film->set_reel_type (ReelType::BY_VIDEO_CONTENT);
142 BOOST_CHECK_EQUAL (film->reels().size(), 3U);
143 BOOST_REQUIRE (!wait_for_jobs());
145 film->set_audio_channels(16);
147 make_and_verify_dcp (film);
149 check_dcp ("test/data/reels_test2", film->dir (film->dcp_name()));
151 auto c = make_shared<DCPContent>(film->dir(film->dcp_name()));
152 auto film2 = new_test_film2 ("reels_test2b", {c});
153 film2->set_reel_type (ReelType::BY_VIDEO_CONTENT);
154 film2->set_audio_channels(16);
156 auto r = film2->reels ();
157 BOOST_CHECK_EQUAL (r.size(), 3U);
159 BOOST_CHECK_EQUAL (i->from.get(), 0);
160 BOOST_CHECK_EQUAL (i->to.get(), 96000);
162 BOOST_CHECK_EQUAL (i->from.get(), 96000);
163 BOOST_CHECK_EQUAL (i->to.get(), 96000 * 2);
165 BOOST_CHECK_EQUAL (i->from.get(), 96000 * 2);
166 BOOST_CHECK_EQUAL (i->to.get(), 96000 * 3);
168 c->set_reference_video (true);
169 c->set_reference_audio (true);
171 make_and_verify_dcp(film2, {dcp::VerificationNote::Code::EXTERNAL_ASSET}, false);
175 /** Check that ReelType::BY_VIDEO_CONTENT adds an extra reel, if necessary, at the end
176 * of all the video content to mop up anything afterward.
178 BOOST_AUTO_TEST_CASE (reels_test3)
180 auto dcp = make_shared<DCPContent>("test/data/reels_test2");
181 auto sub = make_shared<StringTextFileContent>("test/data/subrip.srt");
182 auto film = new_test_film2 ("reels_test3", {dcp, sub});
183 film->set_reel_type (ReelType::BY_VIDEO_CONTENT);
185 auto reels = film->reels();
186 BOOST_REQUIRE_EQUAL (reels.size(), 4U);
187 auto i = reels.begin ();
188 BOOST_CHECK_EQUAL (i->from.get(), 0);
189 BOOST_CHECK_EQUAL (i->to.get(), 96000);
191 BOOST_CHECK_EQUAL (i->from.get(), 96000);
192 BOOST_CHECK_EQUAL (i->to.get(), 96000 * 2);
194 BOOST_CHECK_EQUAL (i->from.get(), 96000 * 2);
195 BOOST_CHECK_EQUAL (i->to.get(), 96000 * 3);
197 BOOST_CHECK_EQUAL (i->from.get(), 96000 * 3);
198 BOOST_CHECK_EQUAL (i->to.get(), sub->full_length(film).ceil(film->video_frame_rate()).get());
202 /** Check creation of a multi-reel DCP with a single .srt subtitle file;
203 * make sure that the reel subtitle timing is done right.
205 BOOST_AUTO_TEST_CASE (reels_test4)
207 auto film = new_test_film2 ("reels_test4");
208 film->set_reel_type (ReelType::BY_VIDEO_CONTENT);
209 film->set_interop (false);
211 /* 4 piece of 1s-long content */
212 shared_ptr<ImageContent> content[4];
213 for (int i = 0; i < 4; ++i) {
214 content[i] = make_shared<ImageContent>("test/data/flat_green.png");
215 film->examine_and_add_content (content[i]);
216 BOOST_REQUIRE (!wait_for_jobs());
217 content[i]->video->set_length (24);
220 auto subs = make_shared<StringTextFileContent>("test/data/subrip3.srt");
221 film->examine_and_add_content (subs);
222 BOOST_REQUIRE (!wait_for_jobs());
224 film->set_audio_channels(16);
226 auto reels = film->reels();
227 BOOST_REQUIRE_EQUAL (reels.size(), 4U);
228 auto i = reels.begin ();
229 BOOST_CHECK_EQUAL (i->from.get(), 0);
230 BOOST_CHECK_EQUAL (i->to.get(), 96000);
232 BOOST_CHECK_EQUAL (i->from.get(), 96000);
233 BOOST_CHECK_EQUAL (i->to.get(), 96000 * 2);
235 BOOST_CHECK_EQUAL (i->from.get(), 96000 * 2);
236 BOOST_CHECK_EQUAL (i->to.get(), 96000 * 3);
238 BOOST_CHECK_EQUAL (i->from.get(), 96000 * 3);
239 BOOST_CHECK_EQUAL (i->to.get(), 96000 * 4);
241 make_and_verify_dcp (
244 dcp::VerificationNote::Code::MISSING_SUBTITLE_LANGUAGE,
245 dcp::VerificationNote::Code::INVALID_SUBTITLE_FIRST_TEXT_TIME,
246 dcp::VerificationNote::Code::INVALID_SUBTITLE_DURATION
249 check_dcp ("test/data/reels_test4", film->dir (film->dcp_name()));
253 BOOST_AUTO_TEST_CASE (reels_test5)
255 auto dcp = make_shared<DCPContent>("test/data/reels_test4");
256 dcp->check_font_ids();
257 auto film = new_test_film2 ("reels_test5", {dcp});
258 film->set_sequence (false);
260 /* Set to 2123 but it will be rounded up to the next frame (4000) */
261 dcp->set_position(film, DCPTime(2123));
264 auto p = dcp->reels (film);
265 BOOST_REQUIRE_EQUAL (p.size(), 4U);
267 BOOST_CHECK (*i++ == DCPTimePeriod (DCPTime(4000 + 0), DCPTime(4000 + 96000)));
268 BOOST_CHECK (*i++ == DCPTimePeriod (DCPTime(4000 + 96000), DCPTime(4000 + 192000)));
269 BOOST_CHECK (*i++ == DCPTimePeriod (DCPTime(4000 + 192000), DCPTime(4000 + 288000)));
270 BOOST_CHECK (*i++ == DCPTimePeriod (DCPTime(4000 + 288000), DCPTime(4000 + 384000)));
274 dcp->set_trim_start(film, ContentTime::from_seconds(0.5));
275 auto p = dcp->reels (film);
276 BOOST_REQUIRE_EQUAL (p.size(), 4U);
278 BOOST_CHECK (*i++ == DCPTimePeriod (DCPTime(4000 + 0), DCPTime(4000 + 48000)));
279 BOOST_CHECK (*i++ == DCPTimePeriod (DCPTime(4000 + 48000), DCPTime(4000 + 144000)));
280 BOOST_CHECK (*i++ == DCPTimePeriod (DCPTime(4000 + 144000), DCPTime(4000 + 240000)));
281 BOOST_CHECK (*i++ == DCPTimePeriod (DCPTime(4000 + 240000), DCPTime(4000 + 336000)));
285 dcp->set_trim_end (ContentTime::from_seconds (0.5));
286 auto p = dcp->reels (film);
287 BOOST_REQUIRE_EQUAL (p.size(), 4U);
289 BOOST_CHECK (*i++ == DCPTimePeriod (DCPTime(4000 + 0), DCPTime(4000 + 48000)));
290 BOOST_CHECK (*i++ == DCPTimePeriod (DCPTime(4000 + 48000), DCPTime(4000 + 144000)));
291 BOOST_CHECK (*i++ == DCPTimePeriod (DCPTime(4000 + 144000), DCPTime(4000 + 240000)));
292 BOOST_CHECK (*i++ == DCPTimePeriod (DCPTime(4000 + 240000), DCPTime(4000 + 288000)));
296 dcp->set_trim_start(film, ContentTime::from_seconds(1.5));
297 auto p = dcp->reels (film);
298 BOOST_REQUIRE_EQUAL (p.size(), 3U);
300 BOOST_CHECK (*i++ == DCPTimePeriod (DCPTime(4000 + 0), DCPTime(4000 + 48000)));
301 BOOST_CHECK (*i++ == DCPTimePeriod (DCPTime(4000 + 48000), DCPTime(4000 + 144000)));
302 BOOST_CHECK (*i++ == DCPTimePeriod (DCPTime(4000 + 144000), DCPTime(4000 + 192000)));
307 /** Check reel split with a muxed video/audio source */
308 BOOST_AUTO_TEST_CASE (reels_test6)
310 auto A = make_shared<FFmpegContent>("test/data/test2.mp4");
311 auto film = new_test_film2 ("reels_test6", {A});
313 film->set_j2k_bandwidth (100000000);
314 film->set_reel_type (ReelType::BY_LENGTH);
315 /* This is just over 2.5s at 100Mbit/s; should correspond to 60 frames */
316 film->set_reel_length (31253154);
317 /* dcp_inspect and clairmeta both give errors about reel <1s in length */
318 make_and_verify_dcp (
321 dcp::VerificationNote::Code::INVALID_INTRINSIC_DURATION,
322 dcp::VerificationNote::Code::INVALID_DURATION,
330 /** Check the case where the last bit of audio hangs over the end of the video
331 * and we are using ReelType::BY_VIDEO_CONTENT.
333 BOOST_AUTO_TEST_CASE (reels_test7)
335 auto A = content_factory("test/data/flat_red.png")[0];
336 auto B = content_factory("test/data/awkward_length.wav")[0];
337 auto film = new_test_film2 ("reels_test7", { A, B });
338 film->set_video_frame_rate (24);
339 A->video->set_length (2 * 24);
341 film->set_reel_type (ReelType::BY_VIDEO_CONTENT);
342 BOOST_REQUIRE_EQUAL (film->reels().size(), 2U);
343 BOOST_CHECK (film->reels().front() == DCPTimePeriod(DCPTime(0), DCPTime::from_frames(2 * 24, 24)));
344 BOOST_CHECK (film->reels().back() == DCPTimePeriod(DCPTime::from_frames(2 * 24, 24), DCPTime::from_frames(3 * 24 + 1, 24)));
346 make_and_verify_dcp (film);
350 /** Check a reels-related error; make_dcp() would raise a ProgrammingError */
351 BOOST_AUTO_TEST_CASE (reels_test8)
353 auto A = make_shared<FFmpegContent>("test/data/test2.mp4");
354 auto film = new_test_film2 ("reels_test8", {A});
356 A->set_trim_end (ContentTime::from_seconds (1));
357 make_and_verify_dcp (film);
361 /** Check another reels-related error; make_dcp() would raise a ProgrammingError */
362 BOOST_AUTO_TEST_CASE (reels_test9)
364 auto A = make_shared<FFmpegContent>("test/data/flat_red.png");
365 auto film = new_test_film2("reels_test9a", {A});
366 A->video->set_length(5 * 24);
367 film->set_video_frame_rate(24);
368 make_and_verify_dcp (film);
370 auto B = make_shared<DCPContent>(film->dir(film->dcp_name()));
371 auto film2 = new_test_film2("reels_test9b", {B, content_factory("test/data/dcp_sub4.xml")[0]});
372 B->set_reference_video(true);
373 B->set_reference_audio(true);
374 film2->set_reel_type(ReelType::BY_VIDEO_CONTENT);
375 film2->write_metadata();
376 make_and_verify_dcp (
379 dcp::VerificationNote::Code::MISSING_SUBTITLE_LANGUAGE,
380 dcp::VerificationNote::Code::INVALID_SUBTITLE_FIRST_TEXT_TIME
385 /** Another reels-related error; make_dcp() would raise a ProgrammingError
386 * in AudioBuffers::allocate due to an attempt to allocate a negatively-sized buffer.
387 * This was triggered by a VF where there are referenced audio reels followed by
388 * VF audio. When the VF audio arrives the Writer did not correctly skip over the
391 BOOST_AUTO_TEST_CASE (reels_test10)
394 auto A = make_shared<FFmpegContent>("test/data/flat_red.png");
395 auto B = make_shared<FFmpegContent>("test/data/flat_red.png");
396 auto ov = new_test_film2("reels_test10_ov", {A, B});
397 A->video->set_length (5 * 24);
398 B->video->set_length (5 * 24);
400 ov->set_reel_type (ReelType::BY_VIDEO_CONTENT);
401 make_and_verify_dcp (ov);
402 ov->write_metadata ();
404 /* Now try to make the VF; this used to fail */
405 auto ov_dcp = make_shared<DCPContent>(ov->dir(ov->dcp_name()));
406 auto vf = new_test_film2("reels_test10_vf", {ov_dcp, content_factory("test/data/15s.srt")[0]});
407 vf->set_reel_type (ReelType::BY_VIDEO_CONTENT);
408 ov_dcp->set_reference_video (true);
409 ov_dcp->set_reference_audio (true);
411 make_and_verify_dcp (
414 dcp::VerificationNote::Code::EXTERNAL_ASSET,
415 dcp::VerificationNote::Code::MISSING_SUBTITLE_LANGUAGE,
416 dcp::VerificationNote::Code::INVALID_SUBTITLE_FIRST_TEXT_TIME,
417 dcp::VerificationNote::Code::INVALID_SUBTITLE_DURATION,
423 /** Another reels error; ReelType::BY_VIDEO_CONTENT when the first content is not
426 BOOST_AUTO_TEST_CASE (reels_test11)
428 auto A = make_shared<FFmpegContent>("test/data/flat_red.png");
429 auto film = new_test_film2 ("reels_test11", {A});
430 film->set_video_frame_rate (24);
431 A->video->set_length (240);
432 A->set_video_frame_rate(film, 24);
433 A->set_position (film, DCPTime::from_seconds(1));
434 film->set_reel_type (ReelType::BY_VIDEO_CONTENT);
435 make_and_verify_dcp (film);
436 BOOST_CHECK_EQUAL (A->position().get(), DCPTime::from_seconds(1).get());
437 BOOST_CHECK_EQUAL (A->end(film).get(), DCPTime::from_seconds(1 + 10).get());
439 auto r = film->reels ();
440 BOOST_CHECK_EQUAL (r.size(), 2U);
441 BOOST_CHECK_EQUAL (r.front().from.get(), 0);
442 BOOST_CHECK_EQUAL (r.front().to.get(), DCPTime::from_seconds(1).get());
443 BOOST_CHECK_EQUAL (r.back().from.get(), DCPTime::from_seconds(1).get());
444 BOOST_CHECK_EQUAL (r.back().to.get(), DCPTime::from_seconds(1 + 10).get());
448 /** For VFs to work right we have to make separate reels for empty bits between
451 BOOST_AUTO_TEST_CASE (reels_test12)
453 auto A = make_shared<FFmpegContent>("test/data/flat_red.png");
454 auto B = make_shared<FFmpegContent>("test/data/flat_red.png");
455 auto film = new_test_film2 ("reels_test12", {A, B});
456 film->set_video_frame_rate (24);
457 film->set_reel_type (ReelType::BY_VIDEO_CONTENT);
458 film->set_sequence (false);
460 A->video->set_length (240);
461 A->set_video_frame_rate(film, 24);
462 A->set_position (film, DCPTime::from_seconds(1));
464 B->video->set_length (120);
465 B->set_video_frame_rate(film, 24);
466 B->set_position (film, DCPTime::from_seconds(14));
468 auto r = film->reels ();
469 BOOST_REQUIRE_EQUAL (r.size(), 4U);
472 BOOST_CHECK_EQUAL (i->from.get(), 0);
473 BOOST_CHECK_EQUAL (i->to.get(), DCPTime::from_seconds(1).get());
475 BOOST_CHECK_EQUAL (i->from.get(), DCPTime::from_seconds(1).get());
476 BOOST_CHECK_EQUAL (i->to.get(), DCPTime::from_seconds(11).get());
478 BOOST_CHECK_EQUAL (i->from.get(), DCPTime::from_seconds(11).get());
479 BOOST_CHECK_EQUAL (i->to.get(), DCPTime::from_seconds(14).get());
481 BOOST_CHECK_EQUAL (i->from.get(), DCPTime::from_seconds(14).get());
482 BOOST_CHECK_EQUAL (i->to.get(), DCPTime::from_seconds(19).get());
493 dump_notes (vector<dcp::VerificationNote> const & notes)
495 for (auto i: notes) {
496 std::cout << dcp::note_to_string(i) << "\n";
501 /** Using less than 1 second's worth of content should not result in a reel
502 * of less than 1 second's duration.
504 BOOST_AUTO_TEST_CASE (reels_should_not_be_short1)
506 auto A = make_shared<FFmpegContent>("test/data/flat_red.png");
507 auto B = make_shared<FFmpegContent>("test/data/flat_red.png");
508 auto film = new_test_film2 ("reels_should_not_be_short1", {A, B});
509 film->set_video_frame_rate (24);
511 A->video->set_length (23);
513 B->video->set_length (23);
514 B->set_position (film, DCPTime::from_frames(23, 24));
516 make_and_verify_dcp (film);
518 vector<boost::filesystem::path> dirs = { film->dir(film->dcp_name(false)) };
519 auto result = dcp::verify(dirs, {}, boost::bind(&no_op), boost::bind(&no_op), {}, TestPaths::xsd());
520 filter_ok(result.notes);
521 dump_notes(result.notes);
522 BOOST_REQUIRE(result.notes.empty());
526 /** Leaving less than 1 second's gap between two pieces of content with
527 * ReelType::BY_VIDEO_CONTENT should not make a <1s reel.
529 BOOST_AUTO_TEST_CASE (reels_should_not_be_short2)
531 auto A = make_shared<FFmpegContent>("test/data/flat_red.png");
532 auto B = make_shared<FFmpegContent>("test/data/flat_red.png");
533 auto film = new_test_film2 ("reels_should_not_be_short2", {A, B});
534 film->set_video_frame_rate (24);
535 film->set_reel_type (ReelType::BY_VIDEO_CONTENT);
537 A->video->set_length (240);
539 B->video->set_length (240);
540 B->set_position (film, DCPTime::from_seconds(10.2));
542 make_and_verify_dcp (film);
544 vector<boost::filesystem::path> dirs = { film->dir(film->dcp_name(false)) };
545 auto result = dcp::verify(dirs, {}, boost::bind(&no_op), boost::bind(&no_op), {}, TestPaths::xsd());
546 filter_ok(result.notes);
547 dump_notes(result.notes);
548 BOOST_REQUIRE(result.notes.empty());
552 /** Setting ReelType::BY_LENGTH and using a small length value should not make
555 BOOST_AUTO_TEST_CASE (reels_should_not_be_short3)
557 auto A = make_shared<FFmpegContent>("test/data/flat_red.png");
558 auto film = new_test_film2 ("reels_should_not_be_short3", {A});
559 film->set_video_frame_rate (24);
560 film->set_reel_type (ReelType::BY_LENGTH);
561 film->set_reel_length (1024 * 1024 * 10);
563 A->video->set_length (240);
565 make_and_verify_dcp (film);
567 auto result = dcp::verify({}, {}, boost::bind(&no_op), boost::bind(&no_op), {}, TestPaths::xsd());
568 filter_ok(result.notes);
569 dump_notes(result.notes);
570 BOOST_REQUIRE(result.notes.empty());
574 /** Having one piece of content less than 1s long in ReelType::BY_VIDEO_CONTENT
575 * should not make a reel less than 1s long.
577 BOOST_AUTO_TEST_CASE (reels_should_not_be_short4)
579 auto A = make_shared<FFmpegContent>("test/data/flat_red.png");
580 auto B = make_shared<FFmpegContent>("test/data/flat_red.png");
581 auto film = new_test_film2 ("reels_should_not_be_short4", {A, B});
582 film->set_video_frame_rate (24);
583 film->set_reel_type (ReelType::BY_VIDEO_CONTENT);
585 A->video->set_length (240);
587 B->video->set_length (23);
588 B->set_position (film, DCPTime::from_frames(240, 24));
590 BOOST_CHECK_EQUAL (film->reels().size(), 1U);
591 BOOST_CHECK (film->reels().front() == dcpomatic::DCPTimePeriod(dcpomatic::DCPTime(), dcpomatic::DCPTime::from_frames(263, 24)));
593 film->write_metadata ();
594 make_dcp (film, TranscodeJob::ChangedBehaviour::IGNORE);
595 BOOST_REQUIRE (!wait_for_jobs());
597 vector<boost::filesystem::path> dirs = { film->dir(film->dcp_name(false)) };
598 auto result = dcp::verify(dirs, {}, boost::bind(&no_op), boost::bind(&no_op), {}, TestPaths::xsd());
599 filter_ok(result.notes);
600 dump_notes(result.notes);
601 BOOST_REQUIRE(result.notes.empty());
605 /** Create a long DCP A then insert it repeatedly into a new project, trimming it differently each time.
606 * Make a DCP B from that project which refers to A and splits into reels. This was found to go wrong
607 * when looking at #2268.
609 BOOST_AUTO_TEST_CASE (repeated_dcp_into_reels)
612 auto A = make_shared<FFmpegContent>("test/data/flat_red.png");
613 auto film1 = new_test_film2("repeated_dcp_into_reels1", { A });
614 auto constexpr frame_rate = 24;
615 auto constexpr length_in_seconds = 20;
616 auto constexpr total_frames = frame_rate * length_in_seconds;
617 film1->set_video_frame_rate(frame_rate);
618 A->video->set_length(total_frames);
619 make_and_verify_dcp(film1);
621 /* Make a new project that includes this long DCP 4 times, each
622 * trimmed to a quarter of the original, i.e.
623 * /----------------------|----------------------|----------------------|----------------------\
624 * | 1st quarter of film1 | 2nd quarter of film1 | 3rd quarter of film1 | 4th quarter of film1 |
625 * \----------------------|----------------------|----------------------|_---------------------/
628 shared_ptr<DCPContent> original_dcp[4] = {
629 make_shared<DCPContent>(film1->dir(film1->dcp_name(false))),
630 make_shared<DCPContent>(film1->dir(film1->dcp_name(false))),
631 make_shared<DCPContent>(film1->dir(film1->dcp_name(false))),
632 make_shared<DCPContent>(film1->dir(film1->dcp_name(false)))
635 auto film2 = new_test_film2("repeated_dcp_into_reels2", { original_dcp[0], original_dcp[1], original_dcp[2], original_dcp[3] });
636 film2->set_reel_type(ReelType::BY_VIDEO_CONTENT);
637 film2->set_video_frame_rate(frame_rate);
638 film2->set_sequence(false);
640 for (int i = 0; i < 4; ++i) {
641 original_dcp[i]->set_position(film2, DCPTime::from_frames(total_frames * i / 4, frame_rate));
642 original_dcp[i]->set_trim_start(film2, ContentTime::from_frames(total_frames * i / 4, frame_rate));
643 original_dcp[i]->set_trim_end (ContentTime::from_frames(total_frames * (4 - i - 1) / 4, frame_rate));
644 original_dcp[i]->set_reference_video(true);
645 original_dcp[i]->set_reference_audio(true);
648 make_and_verify_dcp(film2, { dcp::VerificationNote::Code::EXTERNAL_ASSET }, false);
650 dcp::DCP check1(film1->dir(film1->dcp_name()));
652 BOOST_REQUIRE(!check1.cpls().empty());
653 BOOST_REQUIRE(!check1.cpls()[0]->reels().empty());
654 auto picture = check1.cpls()[0]->reels()[0]->main_picture()->asset();
655 BOOST_REQUIRE(picture);
656 auto sound = check1.cpls()[0]->reels()[0]->main_sound()->asset();
657 BOOST_REQUIRE(sound);
659 dcp::DCP check2(film2->dir(film2->dcp_name()));
661 BOOST_REQUIRE(!check2.cpls().empty());
662 auto cpl = check2.cpls()[0];
663 BOOST_REQUIRE_EQUAL(cpl->reels().size(), 4U);
664 for (int i = 0; i < 4; ++i) {
665 BOOST_REQUIRE_EQUAL(cpl->reels()[i]->main_picture()->entry_point().get_value_or(0), total_frames * i / 4);
666 BOOST_REQUIRE_EQUAL(cpl->reels()[i]->main_picture()->duration().get_value_or(0), total_frames / 4);
667 BOOST_REQUIRE_EQUAL(cpl->reels()[i]->main_picture()->id(), picture->id());
668 BOOST_REQUIRE_EQUAL(cpl->reels()[i]->main_sound()->entry_point().get_value_or(0), total_frames * i / 4);
669 BOOST_REQUIRE_EQUAL(cpl->reels()[i]->main_sound()->duration().get_value_or(0), total_frames / 4);
670 BOOST_REQUIRE_EQUAL(cpl->reels()[i]->main_sound()->id(), sound->id());