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;
57 /** Test Film::reels() */
58 BOOST_AUTO_TEST_CASE (reels_test1)
60 auto film = new_test_film ("reels_test1");
61 film->set_container (Ratio::from_id ("185"));
62 auto A = make_shared<FFmpegContent>("test/data/test.mp4");
63 film->examine_and_add_content (A);
64 auto B = make_shared<FFmpegContent>("test/data/test.mp4");
65 film->examine_and_add_content (B);
66 BOOST_REQUIRE (!wait_for_jobs());
67 BOOST_CHECK_EQUAL (A->full_length(film).get(), 288000);
69 film->set_reel_type (ReelType::SINGLE);
70 auto r = film->reels ();
71 BOOST_CHECK_EQUAL (r.size(), 1U);
72 BOOST_CHECK_EQUAL (r.front().from.get(), 0);
73 BOOST_CHECK_EQUAL (r.front().to.get(), 288000 * 2);
75 film->set_reel_type (ReelType::BY_VIDEO_CONTENT);
77 BOOST_CHECK_EQUAL (r.size(), 2U);
78 BOOST_CHECK_EQUAL (r.front().from.get(), 0);
79 BOOST_CHECK_EQUAL (r.front().to.get(), 288000);
80 BOOST_CHECK_EQUAL (r.back().from.get(), 288000);
81 BOOST_CHECK_EQUAL (r.back().to.get(), 288000 * 2);
83 film->set_j2k_bandwidth (100000000);
84 film->set_reel_type (ReelType::BY_LENGTH);
85 /* This is just over 2.5s at 100Mbit/s; should correspond to 60 frames */
86 film->set_reel_length (31253154);
88 BOOST_CHECK_EQUAL (r.size(), 3U);
90 BOOST_CHECK_EQUAL (i->from.get(), 0);
91 BOOST_CHECK_EQUAL (i->to.get(), DCPTime::from_frames(60, 24).get());
93 BOOST_CHECK_EQUAL (i->from.get(), DCPTime::from_frames(60, 24).get());
94 BOOST_CHECK_EQUAL (i->to.get(), DCPTime::from_frames(120, 24).get());
96 BOOST_CHECK_EQUAL (i->from.get(), DCPTime::from_frames(120, 24).get());
97 BOOST_CHECK_EQUAL (i->to.get(), DCPTime::from_frames(144, 24).get());
101 /** Make a short DCP with multi reels split by video content, then import
102 * this into a new project and make a new DCP referencing it.
104 BOOST_AUTO_TEST_CASE (reels_test2)
106 auto film = new_test_film ("reels_test2");
107 film->set_name ("reels_test2");
108 film->set_container (Ratio::from_id ("185"));
109 film->set_interop (false);
110 film->set_dcp_content_type (DCPContentType::from_isdcf_name ("TST"));
113 auto c = make_shared<ImageContent>("test/data/flat_red.png");
114 film->examine_and_add_content (c);
115 BOOST_REQUIRE (!wait_for_jobs());
116 c->video->set_length (24);
120 auto c = make_shared<ImageContent>("test/data/flat_green.png");
121 film->examine_and_add_content (c);
122 BOOST_REQUIRE (!wait_for_jobs());
123 c->video->set_length (24);
127 auto c = make_shared<ImageContent>("test/data/flat_blue.png");
128 film->examine_and_add_content (c);
129 BOOST_REQUIRE (!wait_for_jobs());
130 c->video->set_length (24);
133 film->set_reel_type (ReelType::BY_VIDEO_CONTENT);
134 BOOST_CHECK_EQUAL (film->reels().size(), 3U);
135 BOOST_REQUIRE (!wait_for_jobs());
137 film->set_audio_channels(16);
139 make_and_verify_dcp (film);
141 check_dcp ("test/data/reels_test2", film->dir (film->dcp_name()));
143 auto c = make_shared<DCPContent>(film->dir(film->dcp_name()));
144 auto film2 = new_test_film2 ("reels_test2b", {c});
145 film2->set_reel_type (ReelType::BY_VIDEO_CONTENT);
146 film2->set_audio_channels(16);
148 auto r = film2->reels ();
149 BOOST_CHECK_EQUAL (r.size(), 3U);
151 BOOST_CHECK_EQUAL (i->from.get(), 0);
152 BOOST_CHECK_EQUAL (i->to.get(), 96000);
154 BOOST_CHECK_EQUAL (i->from.get(), 96000);
155 BOOST_CHECK_EQUAL (i->to.get(), 96000 * 2);
157 BOOST_CHECK_EQUAL (i->from.get(), 96000 * 2);
158 BOOST_CHECK_EQUAL (i->to.get(), 96000 * 3);
160 c->set_reference_video (true);
161 c->set_reference_audio (true);
163 make_and_verify_dcp(film2, {dcp::VerificationNote::Code::EXTERNAL_ASSET}, false);
167 /** Check that ReelType::BY_VIDEO_CONTENT adds an extra reel, if necessary, at the end
168 * of all the video content to mop up anything afterward.
170 BOOST_AUTO_TEST_CASE (reels_test3)
172 auto dcp = make_shared<DCPContent>("test/data/reels_test2");
173 auto sub = make_shared<StringTextFileContent>("test/data/subrip.srt");
174 auto film = new_test_film2 ("reels_test3", {dcp, sub});
175 film->set_reel_type (ReelType::BY_VIDEO_CONTENT);
177 auto reels = film->reels();
178 BOOST_REQUIRE_EQUAL (reels.size(), 4U);
179 auto i = reels.begin ();
180 BOOST_CHECK_EQUAL (i->from.get(), 0);
181 BOOST_CHECK_EQUAL (i->to.get(), 96000);
183 BOOST_CHECK_EQUAL (i->from.get(), 96000);
184 BOOST_CHECK_EQUAL (i->to.get(), 96000 * 2);
186 BOOST_CHECK_EQUAL (i->from.get(), 96000 * 2);
187 BOOST_CHECK_EQUAL (i->to.get(), 96000 * 3);
189 BOOST_CHECK_EQUAL (i->from.get(), 96000 * 3);
190 BOOST_CHECK_EQUAL (i->to.get(), sub->full_length(film).ceil(film->video_frame_rate()).get());
194 /** Check creation of a multi-reel DCP with a single .srt subtitle file;
195 * make sure that the reel subtitle timing is done right.
197 BOOST_AUTO_TEST_CASE (reels_test4)
199 auto film = new_test_film2 ("reels_test4");
200 film->set_reel_type (ReelType::BY_VIDEO_CONTENT);
201 film->set_interop (false);
203 /* 4 piece of 1s-long content */
204 shared_ptr<ImageContent> content[4];
205 for (int i = 0; i < 4; ++i) {
206 content[i] = make_shared<ImageContent>("test/data/flat_green.png");
207 film->examine_and_add_content (content[i]);
208 BOOST_REQUIRE (!wait_for_jobs());
209 content[i]->video->set_length (24);
212 auto subs = make_shared<StringTextFileContent>("test/data/subrip3.srt");
213 film->examine_and_add_content (subs);
214 BOOST_REQUIRE (!wait_for_jobs());
216 film->set_audio_channels(16);
218 auto reels = film->reels();
219 BOOST_REQUIRE_EQUAL (reels.size(), 4U);
220 auto i = reels.begin ();
221 BOOST_CHECK_EQUAL (i->from.get(), 0);
222 BOOST_CHECK_EQUAL (i->to.get(), 96000);
224 BOOST_CHECK_EQUAL (i->from.get(), 96000);
225 BOOST_CHECK_EQUAL (i->to.get(), 96000 * 2);
227 BOOST_CHECK_EQUAL (i->from.get(), 96000 * 2);
228 BOOST_CHECK_EQUAL (i->to.get(), 96000 * 3);
230 BOOST_CHECK_EQUAL (i->from.get(), 96000 * 3);
231 BOOST_CHECK_EQUAL (i->to.get(), 96000 * 4);
233 make_and_verify_dcp (
236 dcp::VerificationNote::Code::MISSING_SUBTITLE_LANGUAGE,
237 dcp::VerificationNote::Code::INVALID_SUBTITLE_FIRST_TEXT_TIME,
238 dcp::VerificationNote::Code::INVALID_SUBTITLE_DURATION
241 check_dcp ("test/data/reels_test4", film->dir (film->dcp_name()));
245 BOOST_AUTO_TEST_CASE (reels_test5)
247 auto dcp = make_shared<DCPContent>("test/data/reels_test4");
248 dcp->check_font_ids();
249 auto film = new_test_film2 ("reels_test5", {dcp});
250 film->set_sequence (false);
252 /* Set to 2123 but it will be rounded up to the next frame (4000) */
253 dcp->set_position(film, DCPTime(2123));
256 auto p = dcp->reels (film);
257 BOOST_REQUIRE_EQUAL (p.size(), 4U);
259 BOOST_CHECK (*i++ == DCPTimePeriod (DCPTime(4000 + 0), DCPTime(4000 + 96000)));
260 BOOST_CHECK (*i++ == DCPTimePeriod (DCPTime(4000 + 96000), DCPTime(4000 + 192000)));
261 BOOST_CHECK (*i++ == DCPTimePeriod (DCPTime(4000 + 192000), DCPTime(4000 + 288000)));
262 BOOST_CHECK (*i++ == DCPTimePeriod (DCPTime(4000 + 288000), DCPTime(4000 + 384000)));
266 dcp->set_trim_start(film, ContentTime::from_seconds(0.5));
267 auto p = dcp->reels (film);
268 BOOST_REQUIRE_EQUAL (p.size(), 4U);
270 BOOST_CHECK (*i++ == DCPTimePeriod (DCPTime(4000 + 0), DCPTime(4000 + 48000)));
271 BOOST_CHECK (*i++ == DCPTimePeriod (DCPTime(4000 + 48000), DCPTime(4000 + 144000)));
272 BOOST_CHECK (*i++ == DCPTimePeriod (DCPTime(4000 + 144000), DCPTime(4000 + 240000)));
273 BOOST_CHECK (*i++ == DCPTimePeriod (DCPTime(4000 + 240000), DCPTime(4000 + 336000)));
277 dcp->set_trim_end (ContentTime::from_seconds (0.5));
278 auto p = dcp->reels (film);
279 BOOST_REQUIRE_EQUAL (p.size(), 4U);
281 BOOST_CHECK (*i++ == DCPTimePeriod (DCPTime(4000 + 0), DCPTime(4000 + 48000)));
282 BOOST_CHECK (*i++ == DCPTimePeriod (DCPTime(4000 + 48000), DCPTime(4000 + 144000)));
283 BOOST_CHECK (*i++ == DCPTimePeriod (DCPTime(4000 + 144000), DCPTime(4000 + 240000)));
284 BOOST_CHECK (*i++ == DCPTimePeriod (DCPTime(4000 + 240000), DCPTime(4000 + 288000)));
288 dcp->set_trim_start(film, ContentTime::from_seconds(1.5));
289 auto p = dcp->reels (film);
290 BOOST_REQUIRE_EQUAL (p.size(), 3U);
292 BOOST_CHECK (*i++ == DCPTimePeriod (DCPTime(4000 + 0), DCPTime(4000 + 48000)));
293 BOOST_CHECK (*i++ == DCPTimePeriod (DCPTime(4000 + 48000), DCPTime(4000 + 144000)));
294 BOOST_CHECK (*i++ == DCPTimePeriod (DCPTime(4000 + 144000), DCPTime(4000 + 192000)));
299 /** Check reel split with a muxed video/audio source */
300 BOOST_AUTO_TEST_CASE (reels_test6)
302 auto A = make_shared<FFmpegContent>("test/data/test2.mp4");
303 auto film = new_test_film2 ("reels_test6", {A});
305 film->set_j2k_bandwidth (100000000);
306 film->set_reel_type (ReelType::BY_LENGTH);
307 /* This is just over 2.5s at 100Mbit/s; should correspond to 60 frames */
308 film->set_reel_length (31253154);
309 /* dcp_inspect and clairmeta both give errors about reel <1s in length */
310 make_and_verify_dcp (
313 dcp::VerificationNote::Code::INVALID_INTRINSIC_DURATION,
314 dcp::VerificationNote::Code::INVALID_DURATION,
322 /** Check the case where the last bit of audio hangs over the end of the video
323 * and we are using ReelType::BY_VIDEO_CONTENT.
325 BOOST_AUTO_TEST_CASE (reels_test7)
327 auto A = content_factory("test/data/flat_red.png")[0];
328 auto B = content_factory("test/data/awkward_length.wav")[0];
329 auto film = new_test_film2 ("reels_test7", { A, B });
330 film->set_video_frame_rate (24);
331 A->video->set_length (2 * 24);
333 film->set_reel_type (ReelType::BY_VIDEO_CONTENT);
334 BOOST_REQUIRE_EQUAL (film->reels().size(), 2U);
335 BOOST_CHECK (film->reels().front() == DCPTimePeriod(DCPTime(0), DCPTime::from_frames(2 * 24, 24)));
336 BOOST_CHECK (film->reels().back() == DCPTimePeriod(DCPTime::from_frames(2 * 24, 24), DCPTime::from_frames(3 * 24 + 1, 24)));
338 make_and_verify_dcp (film);
342 /** Check a reels-related error; make_dcp() would raise a ProgrammingError */
343 BOOST_AUTO_TEST_CASE (reels_test8)
345 auto A = make_shared<FFmpegContent>("test/data/test2.mp4");
346 auto film = new_test_film2 ("reels_test8", {A});
348 A->set_trim_end (ContentTime::from_seconds (1));
349 make_and_verify_dcp (film);
353 /** Check another reels-related error; make_dcp() would raise a ProgrammingError */
354 BOOST_AUTO_TEST_CASE (reels_test9)
356 auto A = make_shared<FFmpegContent>("test/data/flat_red.png");
357 auto film = new_test_film2("reels_test9a", {A});
358 A->video->set_length(5 * 24);
359 film->set_video_frame_rate(24);
360 make_and_verify_dcp (film);
362 auto B = make_shared<DCPContent>(film->dir(film->dcp_name()));
363 auto film2 = new_test_film2("reels_test9b", {B, content_factory("test/data/dcp_sub4.xml")[0]});
364 B->set_reference_video(true);
365 B->set_reference_audio(true);
366 film2->set_reel_type(ReelType::BY_VIDEO_CONTENT);
367 film2->write_metadata();
368 make_and_verify_dcp (
371 dcp::VerificationNote::Code::MISSING_SUBTITLE_LANGUAGE,
372 dcp::VerificationNote::Code::INVALID_SUBTITLE_FIRST_TEXT_TIME
377 /** Another reels-related error; make_dcp() would raise a ProgrammingError
378 * in AudioBuffers::allocate due to an attempt to allocate a negatively-sized buffer.
379 * This was triggered by a VF where there are referenced audio reels followed by
380 * VF audio. When the VF audio arrives the Writer did not correctly skip over the
383 BOOST_AUTO_TEST_CASE (reels_test10)
386 auto A = make_shared<FFmpegContent>("test/data/flat_red.png");
387 auto B = make_shared<FFmpegContent>("test/data/flat_red.png");
388 auto ov = new_test_film2("reels_test10_ov", {A, B});
389 A->video->set_length (5 * 24);
390 B->video->set_length (5 * 24);
392 ov->set_reel_type (ReelType::BY_VIDEO_CONTENT);
393 make_and_verify_dcp (ov);
394 ov->write_metadata ();
396 /* Now try to make the VF; this used to fail */
397 auto ov_dcp = make_shared<DCPContent>(ov->dir(ov->dcp_name()));
398 auto vf = new_test_film2("reels_test10_vf", {ov_dcp, content_factory("test/data/15s.srt")[0]});
399 vf->set_reel_type (ReelType::BY_VIDEO_CONTENT);
400 ov_dcp->set_reference_video (true);
401 ov_dcp->set_reference_audio (true);
403 make_and_verify_dcp (
406 dcp::VerificationNote::Code::EXTERNAL_ASSET,
407 dcp::VerificationNote::Code::MISSING_SUBTITLE_LANGUAGE,
408 dcp::VerificationNote::Code::INVALID_SUBTITLE_FIRST_TEXT_TIME,
409 dcp::VerificationNote::Code::INVALID_SUBTITLE_DURATION,
415 /** Another reels error; ReelType::BY_VIDEO_CONTENT when the first content is not
418 BOOST_AUTO_TEST_CASE (reels_test11)
420 auto A = make_shared<FFmpegContent>("test/data/flat_red.png");
421 auto film = new_test_film2 ("reels_test11", {A});
422 film->set_video_frame_rate (24);
423 A->video->set_length (240);
424 A->set_video_frame_rate(film, 24);
425 A->set_position (film, DCPTime::from_seconds(1));
426 film->set_reel_type (ReelType::BY_VIDEO_CONTENT);
427 make_and_verify_dcp (film);
428 BOOST_CHECK_EQUAL (A->position().get(), DCPTime::from_seconds(1).get());
429 BOOST_CHECK_EQUAL (A->end(film).get(), DCPTime::from_seconds(1 + 10).get());
431 auto r = film->reels ();
432 BOOST_CHECK_EQUAL (r.size(), 2U);
433 BOOST_CHECK_EQUAL (r.front().from.get(), 0);
434 BOOST_CHECK_EQUAL (r.front().to.get(), DCPTime::from_seconds(1).get());
435 BOOST_CHECK_EQUAL (r.back().from.get(), DCPTime::from_seconds(1).get());
436 BOOST_CHECK_EQUAL (r.back().to.get(), DCPTime::from_seconds(1 + 10).get());
440 /** For VFs to work right we have to make separate reels for empty bits between
443 BOOST_AUTO_TEST_CASE (reels_test12)
445 auto A = make_shared<FFmpegContent>("test/data/flat_red.png");
446 auto B = make_shared<FFmpegContent>("test/data/flat_red.png");
447 auto film = new_test_film2 ("reels_test12", {A, B});
448 film->set_video_frame_rate (24);
449 film->set_reel_type (ReelType::BY_VIDEO_CONTENT);
450 film->set_sequence (false);
452 A->video->set_length (240);
453 A->set_video_frame_rate(film, 24);
454 A->set_position (film, DCPTime::from_seconds(1));
456 B->video->set_length (120);
457 B->set_video_frame_rate(film, 24);
458 B->set_position (film, DCPTime::from_seconds(14));
460 auto r = film->reels ();
461 BOOST_REQUIRE_EQUAL (r.size(), 4U);
464 BOOST_CHECK_EQUAL (i->from.get(), 0);
465 BOOST_CHECK_EQUAL (i->to.get(), DCPTime::from_seconds(1).get());
467 BOOST_CHECK_EQUAL (i->from.get(), DCPTime::from_seconds(1).get());
468 BOOST_CHECK_EQUAL (i->to.get(), DCPTime::from_seconds(11).get());
470 BOOST_CHECK_EQUAL (i->from.get(), DCPTime::from_seconds(11).get());
471 BOOST_CHECK_EQUAL (i->to.get(), DCPTime::from_seconds(14).get());
473 BOOST_CHECK_EQUAL (i->from.get(), DCPTime::from_seconds(14).get());
474 BOOST_CHECK_EQUAL (i->to.get(), DCPTime::from_seconds(19).get());
485 dump_notes (vector<dcp::VerificationNote> const & notes)
487 for (auto i: notes) {
488 std::cout << dcp::note_to_string(i) << "\n";
493 /** Using less than 1 second's worth of content should not result in a reel
494 * of less than 1 second's duration.
496 BOOST_AUTO_TEST_CASE (reels_should_not_be_short1)
498 auto A = make_shared<FFmpegContent>("test/data/flat_red.png");
499 auto B = make_shared<FFmpegContent>("test/data/flat_red.png");
500 auto film = new_test_film2 ("reels_should_not_be_short1", {A, B});
501 film->set_video_frame_rate (24);
503 A->video->set_length (23);
505 B->video->set_length (23);
506 B->set_position (film, DCPTime::from_frames(23, 24));
508 make_and_verify_dcp (film);
510 vector<boost::filesystem::path> dirs = { film->dir(film->dcp_name(false)) };
511 auto notes = dcp::verify(dirs, {}, boost::bind(&no_op), boost::bind(&no_op), {}, TestPaths::xsd());
513 BOOST_REQUIRE (notes.empty());
517 /** Leaving less than 1 second's gap between two pieces of content with
518 * ReelType::BY_VIDEO_CONTENT should not make a <1s reel.
520 BOOST_AUTO_TEST_CASE (reels_should_not_be_short2)
522 auto A = make_shared<FFmpegContent>("test/data/flat_red.png");
523 auto B = make_shared<FFmpegContent>("test/data/flat_red.png");
524 auto film = new_test_film2 ("reels_should_not_be_short2", {A, B});
525 film->set_video_frame_rate (24);
526 film->set_reel_type (ReelType::BY_VIDEO_CONTENT);
528 A->video->set_length (240);
530 B->video->set_length (240);
531 B->set_position (film, DCPTime::from_seconds(10.2));
533 make_and_verify_dcp (film);
535 vector<boost::filesystem::path> dirs = { film->dir(film->dcp_name(false)) };
536 auto const notes = dcp::verify(dirs, {}, boost::bind(&no_op), boost::bind(&no_op), {}, TestPaths::xsd());
538 BOOST_REQUIRE (notes.empty());
542 /** Setting ReelType::BY_LENGTH and using a small length value should not make
545 BOOST_AUTO_TEST_CASE (reels_should_not_be_short3)
547 auto A = make_shared<FFmpegContent>("test/data/flat_red.png");
548 auto film = new_test_film2 ("reels_should_not_be_short3", {A});
549 film->set_video_frame_rate (24);
550 film->set_reel_type (ReelType::BY_LENGTH);
551 film->set_reel_length (1024 * 1024 * 10);
553 A->video->set_length (240);
555 make_and_verify_dcp (film);
557 auto const notes = dcp::verify({}, {}, boost::bind(&no_op), boost::bind(&no_op), {}, TestPaths::xsd());
559 BOOST_REQUIRE (notes.empty());
563 /** Having one piece of content less than 1s long in ReelType::BY_VIDEO_CONTENT
564 * should not make a reel less than 1s long.
566 BOOST_AUTO_TEST_CASE (reels_should_not_be_short4)
568 auto A = make_shared<FFmpegContent>("test/data/flat_red.png");
569 auto B = make_shared<FFmpegContent>("test/data/flat_red.png");
570 auto film = new_test_film2 ("reels_should_not_be_short4", {A, B});
571 film->set_video_frame_rate (24);
572 film->set_reel_type (ReelType::BY_VIDEO_CONTENT);
574 A->video->set_length (240);
576 B->video->set_length (23);
577 B->set_position (film, DCPTime::from_frames(240, 24));
579 BOOST_CHECK_EQUAL (film->reels().size(), 1U);
580 BOOST_CHECK (film->reels().front() == dcpomatic::DCPTimePeriod(dcpomatic::DCPTime(), dcpomatic::DCPTime::from_frames(263, 24)));
582 film->write_metadata ();
583 make_dcp (film, TranscodeJob::ChangedBehaviour::IGNORE);
584 BOOST_REQUIRE (!wait_for_jobs());
586 vector<boost::filesystem::path> dirs = { film->dir(film->dcp_name(false)) };
587 auto const notes = dcp::verify(dirs, {}, boost::bind(&no_op), boost::bind(&no_op), {}, TestPaths::xsd());
589 BOOST_REQUIRE (notes.empty());
593 /** Create a long DCP A then insert it repeatedly into a new project, trimming it differently each time.
594 * Make a DCP B from that project which refers to A and splits into reels. This was found to go wrong
595 * when looking at #2268.
597 BOOST_AUTO_TEST_CASE (repeated_dcp_into_reels)
600 auto A = make_shared<FFmpegContent>("test/data/flat_red.png");
601 auto film1 = new_test_film2("repeated_dcp_into_reels1", { A });
602 auto constexpr frame_rate = 24;
603 auto constexpr length_in_seconds = 20;
604 auto constexpr total_frames = frame_rate * length_in_seconds;
605 film1->set_video_frame_rate(frame_rate);
606 A->video->set_length(total_frames);
607 make_and_verify_dcp(film1);
609 /* Make a new project that includes this long DCP 4 times, each
610 * trimmed to a quarter of the original, i.e.
611 * /----------------------|----------------------|----------------------|----------------------\
612 * | 1st quarter of film1 | 2nd quarter of film1 | 3rd quarter of film1 | 4th quarter of film1 |
613 * \----------------------|----------------------|----------------------|_---------------------/
616 shared_ptr<DCPContent> original_dcp[4] = {
617 make_shared<DCPContent>(film1->dir(film1->dcp_name(false))),
618 make_shared<DCPContent>(film1->dir(film1->dcp_name(false))),
619 make_shared<DCPContent>(film1->dir(film1->dcp_name(false))),
620 make_shared<DCPContent>(film1->dir(film1->dcp_name(false)))
623 auto film2 = new_test_film2("repeated_dcp_into_reels2", { original_dcp[0], original_dcp[1], original_dcp[2], original_dcp[3] });
624 film2->set_reel_type(ReelType::BY_VIDEO_CONTENT);
625 film2->set_video_frame_rate(frame_rate);
626 film2->set_sequence(false);
628 for (int i = 0; i < 4; ++i) {
629 original_dcp[i]->set_position(film2, DCPTime::from_frames(total_frames * i / 4, frame_rate));
630 original_dcp[i]->set_trim_start(film2, ContentTime::from_frames(total_frames * i / 4, frame_rate));
631 original_dcp[i]->set_trim_end (ContentTime::from_frames(total_frames * (4 - i - 1) / 4, frame_rate));
632 original_dcp[i]->set_reference_video(true);
633 original_dcp[i]->set_reference_audio(true);
636 make_and_verify_dcp(film2, { dcp::VerificationNote::Code::EXTERNAL_ASSET }, false);
638 dcp::DCP check1(film1->dir(film1->dcp_name()));
640 BOOST_REQUIRE(!check1.cpls().empty());
641 BOOST_REQUIRE(!check1.cpls()[0]->reels().empty());
642 auto picture = check1.cpls()[0]->reels()[0]->main_picture()->asset();
643 BOOST_REQUIRE(picture);
644 auto sound = check1.cpls()[0]->reels()[0]->main_sound()->asset();
645 BOOST_REQUIRE(sound);
647 dcp::DCP check2(film2->dir(film2->dcp_name()));
649 BOOST_REQUIRE(!check2.cpls().empty());
650 auto cpl = check2.cpls()[0];
651 BOOST_REQUIRE_EQUAL(cpl->reels().size(), 4U);
652 for (int i = 0; i < 4; ++i) {
653 BOOST_REQUIRE_EQUAL(cpl->reels()[i]->main_picture()->entry_point().get_value_or(0), total_frames * i / 4);
654 BOOST_REQUIRE_EQUAL(cpl->reels()[i]->main_picture()->duration().get_value_or(0), total_frames / 4);
655 BOOST_REQUIRE_EQUAL(cpl->reels()[i]->main_picture()->id(), picture->id());
656 BOOST_REQUIRE_EQUAL(cpl->reels()[i]->main_sound()->entry_point().get_value_or(0), total_frames * i / 4);
657 BOOST_REQUIRE_EQUAL(cpl->reels()[i]->main_sound()->duration().get_value_or(0), total_frames / 4);
658 BOOST_REQUIRE_EQUAL(cpl->reels()[i]->main_sound()->id(), sound->id());