620c934178723f21d421e458630686e23e4a9708
[dcpomatic.git] / test / reels_test.cc
1 /*
2     Copyright (C) 2015-2016 Carl Hetherington <cth@carlh.net>
3
4     This file is part of DCP-o-matic.
5
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.
10
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.
15
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/>.
18
19 */
20
21 /** @file  test/reels_test.cc
22  *  @brief Check manipulation of reels in various ways.
23  *  @ingroup specific
24  */
25
26 #include "lib/film.h"
27 #include "lib/ratio.h"
28 #include "lib/ffmpeg_content.h"
29 #include "lib/image_content.h"
30 #include "lib/dcp_content_type.h"
31 #include "lib/dcp_content.h"
32 #include "lib/video_content.h"
33 #include "lib/string_text_file_content.h"
34 #include "lib/content_factory.h"
35 #include "test.h"
36 #include <boost/test/unit_test.hpp>
37 #include <boost/foreach.hpp>
38
39 using std::list;
40 using std::cout;
41 using boost::shared_ptr;
42 using namespace dcpomatic;
43
44 /** Test Film::reels() */
45 BOOST_AUTO_TEST_CASE (reels_test1)
46 {
47         shared_ptr<Film> film = new_test_film ("reels_test1");
48         film->set_container (Ratio::from_id ("185"));
49         shared_ptr<FFmpegContent> A (new FFmpegContent("test/data/test.mp4"));
50         film->examine_and_add_content (A);
51         shared_ptr<FFmpegContent> B (new FFmpegContent("test/data/test.mp4"));
52         film->examine_and_add_content (B);
53         BOOST_REQUIRE (!wait_for_jobs());
54         BOOST_CHECK_EQUAL (A->full_length(film).get(), 288000);
55
56         film->set_reel_type (REELTYPE_SINGLE);
57         list<DCPTimePeriod> r = film->reels ();
58         BOOST_CHECK_EQUAL (r.size(), 1);
59         BOOST_CHECK_EQUAL (r.front().from.get(), 0);
60         BOOST_CHECK_EQUAL (r.front().to.get(), 288000 * 2);
61
62         film->set_reel_type (REELTYPE_BY_VIDEO_CONTENT);
63         r = film->reels ();
64         BOOST_CHECK_EQUAL (r.size(), 2);
65         BOOST_CHECK_EQUAL (r.front().from.get(), 0);
66         BOOST_CHECK_EQUAL (r.front().to.get(), 288000);
67         BOOST_CHECK_EQUAL (r.back().from.get(), 288000);
68         BOOST_CHECK_EQUAL (r.back().to.get(), 288000 * 2);
69
70         film->set_j2k_bandwidth (100000000);
71         film->set_reel_type (REELTYPE_BY_LENGTH);
72         /* This is just over 2.5s at 100Mbit/s; should correspond to 60 frames */
73         film->set_reel_length (31253154);
74         r = film->reels ();
75         BOOST_CHECK_EQUAL (r.size(), 3);
76         list<DCPTimePeriod>::const_iterator i = r.begin ();
77         BOOST_CHECK_EQUAL (i->from.get(), 0);
78         BOOST_CHECK_EQUAL (i->to.get(), DCPTime::from_frames(60, 24).get());
79         ++i;
80         BOOST_CHECK_EQUAL (i->from.get(), DCPTime::from_frames(60, 24).get());
81         BOOST_CHECK_EQUAL (i->to.get(), DCPTime::from_frames(120, 24).get());
82         ++i;
83         BOOST_CHECK_EQUAL (i->from.get(), DCPTime::from_frames(120, 24).get());
84         BOOST_CHECK_EQUAL (i->to.get(), DCPTime::from_frames(144, 24).get());
85 }
86
87 /** Make a short DCP with multi reels split by video content, then import
88  *  this into a new project and make a new DCP referencing it.
89  */
90 BOOST_AUTO_TEST_CASE (reels_test2)
91 {
92         shared_ptr<Film> film = new_test_film ("reels_test2");
93         film->set_name ("reels_test2");
94         film->set_container (Ratio::from_id ("185"));
95         film->set_interop (false);
96         film->set_dcp_content_type (DCPContentType::from_isdcf_name ("TST"));
97
98         {
99                 shared_ptr<ImageContent> c (new ImageContent("test/data/flat_red.png"));
100                 film->examine_and_add_content (c);
101                 BOOST_REQUIRE (!wait_for_jobs());
102                 c->video->set_length (24);
103         }
104
105         {
106                 shared_ptr<ImageContent> c (new ImageContent("test/data/flat_green.png"));
107                 film->examine_and_add_content (c);
108                 BOOST_REQUIRE (!wait_for_jobs());
109                 c->video->set_length (24);
110         }
111
112         {
113                 shared_ptr<ImageContent> c (new ImageContent("test/data/flat_blue.png"));
114                 film->examine_and_add_content (c);
115                 BOOST_REQUIRE (!wait_for_jobs());
116                 c->video->set_length (24);
117         }
118
119         film->set_reel_type (REELTYPE_BY_VIDEO_CONTENT);
120         BOOST_REQUIRE (!wait_for_jobs());
121
122         film->make_dcp ();
123         BOOST_REQUIRE (!wait_for_jobs());
124
125         check_dcp ("test/data/reels_test2", film->dir (film->dcp_name()));
126
127         shared_ptr<Film> film2 = new_test_film ("reels_test2b");
128         film2->set_name ("reels_test2b");
129         film2->set_container (Ratio::from_id ("185"));
130         film2->set_dcp_content_type (DCPContentType::from_isdcf_name ("TST"));
131         film2->set_reel_type (REELTYPE_BY_VIDEO_CONTENT);
132
133         shared_ptr<DCPContent> c (new DCPContent(film->dir(film->dcp_name())));
134         film2->examine_and_add_content (c);
135         BOOST_REQUIRE (!wait_for_jobs ());
136
137         list<DCPTimePeriod> r = film2->reels ();
138         BOOST_CHECK_EQUAL (r.size(), 3);
139         list<DCPTimePeriod>::const_iterator i = r.begin ();
140         BOOST_CHECK_EQUAL (i->from.get(), 0);
141         BOOST_CHECK_EQUAL (i->to.get(), 96000);
142         ++i;
143         BOOST_CHECK_EQUAL (i->from.get(), 96000);
144         BOOST_CHECK_EQUAL (i->to.get(), 96000 * 2);
145         ++i;
146         BOOST_CHECK_EQUAL (i->from.get(), 96000 * 2);
147         BOOST_CHECK_EQUAL (i->to.get(), 96000 * 3);
148
149         c->set_reference_video (true);
150         c->set_reference_audio (true);
151
152         film2->make_dcp ();
153         BOOST_REQUIRE (!wait_for_jobs());
154 }
155
156 /** Check that REELTYPE_BY_VIDEO_CONTENT adds an extra reel, if necessary, at the end
157  *  of all the video content to mop up anything afterward.
158  */
159 BOOST_AUTO_TEST_CASE (reels_test3)
160 {
161         shared_ptr<Film> film = new_test_film ("reels_test3");
162         film->set_name ("reels_test3");
163         film->set_container (Ratio::from_id ("185"));
164         film->set_dcp_content_type (DCPContentType::from_isdcf_name ("TST"));
165         film->set_reel_type (REELTYPE_BY_VIDEO_CONTENT);
166
167         shared_ptr<Content> dcp (new DCPContent("test/data/reels_test2"));
168         film->examine_and_add_content (dcp);
169         shared_ptr<Content> sub (new StringTextFileContent("test/data/subrip.srt"));
170         film->examine_and_add_content (sub);
171         BOOST_REQUIRE (!wait_for_jobs());
172
173         list<DCPTimePeriod> reels = film->reels();
174         BOOST_REQUIRE_EQUAL (reels.size(), 4);
175         list<DCPTimePeriod>::const_iterator i = reels.begin ();
176         BOOST_CHECK_EQUAL (i->from.get(), 0);
177         BOOST_CHECK_EQUAL (i->to.get(), 96000);
178         ++i;
179         BOOST_CHECK_EQUAL (i->from.get(), 96000);
180         BOOST_CHECK_EQUAL (i->to.get(), 96000 * 2);
181         ++i;
182         BOOST_CHECK_EQUAL (i->from.get(), 96000 * 2);
183         BOOST_CHECK_EQUAL (i->to.get(), 96000 * 3);
184         ++i;
185         BOOST_CHECK_EQUAL (i->from.get(), 96000 * 3);
186         BOOST_CHECK_EQUAL (i->to.get(), sub->full_length(film).ceil(film->video_frame_rate()).get());
187 }
188
189 /** Check creation of a multi-reel DCP with a single .srt subtitle file;
190  *  make sure that the reel subtitle timing is done right.
191  */
192 BOOST_AUTO_TEST_CASE (reels_test4)
193 {
194         shared_ptr<Film> film = new_test_film ("reels_test4");
195         film->set_name ("reels_test4");
196         film->set_container (Ratio::from_id ("185"));
197         film->set_dcp_content_type (DCPContentType::from_isdcf_name ("TST"));
198         film->set_reel_type (REELTYPE_BY_VIDEO_CONTENT);
199         film->set_interop (false);
200
201         /* 4 piece of 1s-long content */
202         shared_ptr<ImageContent> content[4];
203         for (int i = 0; i < 4; ++i) {
204                 content[i].reset (new ImageContent("test/data/flat_green.png"));
205                 film->examine_and_add_content (content[i]);
206                 BOOST_REQUIRE (!wait_for_jobs());
207                 content[i]->video->set_length (24);
208         }
209
210         shared_ptr<StringTextFileContent> subs (new StringTextFileContent("test/data/subrip3.srt"));
211         film->examine_and_add_content (subs);
212         BOOST_REQUIRE (!wait_for_jobs());
213
214         list<DCPTimePeriod> reels = film->reels();
215         BOOST_REQUIRE_EQUAL (reels.size(), 4);
216         list<DCPTimePeriod>::const_iterator i = reels.begin ();
217         BOOST_CHECK_EQUAL (i->from.get(), 0);
218         BOOST_CHECK_EQUAL (i->to.get(), 96000);
219         ++i;
220         BOOST_CHECK_EQUAL (i->from.get(), 96000);
221         BOOST_CHECK_EQUAL (i->to.get(), 96000 * 2);
222         ++i;
223         BOOST_CHECK_EQUAL (i->from.get(), 96000 * 2);
224         BOOST_CHECK_EQUAL (i->to.get(), 96000 * 3);
225         ++i;
226         BOOST_CHECK_EQUAL (i->from.get(), 96000 * 3);
227         BOOST_CHECK_EQUAL (i->to.get(), 96000 * 4);
228
229         film->make_dcp ();
230         BOOST_REQUIRE (!wait_for_jobs());
231
232         check_dcp ("test/data/reels_test4", film->dir (film->dcp_name()));
233 }
234
235 BOOST_AUTO_TEST_CASE (reels_test5)
236 {
237         shared_ptr<Film> film = new_test_film ("reels_test5");
238         film->set_sequence (false);
239         shared_ptr<DCPContent> dcp (new DCPContent("test/data/reels_test4"));
240         film->examine_and_add_content (dcp);
241         BOOST_REQUIRE (!wait_for_jobs ());
242
243         /* Set to 2123 but it will be rounded up to the next frame (4000) */
244         dcp->set_position(film, DCPTime(2123));
245
246         {
247                 list<DCPTimePeriod> p = dcp->reels (film);
248                 BOOST_REQUIRE_EQUAL (p.size(), 4);
249                 list<DCPTimePeriod>::const_iterator i = p.begin();
250                 BOOST_CHECK (*i++ == DCPTimePeriod (DCPTime(4000 + 0), DCPTime(4000 + 96000)));
251                 BOOST_CHECK (*i++ == DCPTimePeriod (DCPTime(4000 + 96000), DCPTime(4000 + 192000)));
252                 BOOST_CHECK (*i++ == DCPTimePeriod (DCPTime(4000 + 192000), DCPTime(4000 + 288000)));
253                 BOOST_CHECK (*i++ == DCPTimePeriod (DCPTime(4000 + 288000), DCPTime(4000 + 384000)));
254         }
255
256         {
257                 dcp->set_trim_start (ContentTime::from_seconds (0.5));
258                 list<DCPTimePeriod> p = dcp->reels (film);
259                 BOOST_REQUIRE_EQUAL (p.size(), 4);
260                 list<DCPTimePeriod>::const_iterator i = p.begin();
261                 BOOST_CHECK (*i++ == DCPTimePeriod (DCPTime(4000 + 0), DCPTime(4000 + 48000)));
262                 BOOST_CHECK (*i++ == DCPTimePeriod (DCPTime(4000 + 48000), DCPTime(4000 + 144000)));
263                 BOOST_CHECK (*i++ == DCPTimePeriod (DCPTime(4000 + 144000), DCPTime(4000 + 240000)));
264                 BOOST_CHECK (*i++ == DCPTimePeriod (DCPTime(4000 + 240000), DCPTime(4000 + 336000)));
265         }
266
267         {
268                 dcp->set_trim_end (ContentTime::from_seconds (0.5));
269                 list<DCPTimePeriod> p = dcp->reels (film);
270                 BOOST_REQUIRE_EQUAL (p.size(), 4);
271                 list<DCPTimePeriod>::const_iterator i = p.begin();
272                 BOOST_CHECK (*i++ == DCPTimePeriod (DCPTime(4000 + 0), DCPTime(4000 + 48000)));
273                 BOOST_CHECK (*i++ == DCPTimePeriod (DCPTime(4000 + 48000), DCPTime(4000 + 144000)));
274                 BOOST_CHECK (*i++ == DCPTimePeriod (DCPTime(4000 + 144000), DCPTime(4000 + 240000)));
275                 BOOST_CHECK (*i++ == DCPTimePeriod (DCPTime(4000 + 240000), DCPTime(4000 + 288000)));
276         }
277
278         {
279                 dcp->set_trim_start (ContentTime::from_seconds (1.5));
280                 list<DCPTimePeriod> p = dcp->reels (film);
281                 BOOST_REQUIRE_EQUAL (p.size(), 3);
282                 list<DCPTimePeriod>::const_iterator i = p.begin();
283                 BOOST_CHECK (*i++ == DCPTimePeriod (DCPTime(4000 + 0), DCPTime(4000 + 48000)));
284                 BOOST_CHECK (*i++ == DCPTimePeriod (DCPTime(4000 + 48000), DCPTime(4000 + 144000)));
285                 BOOST_CHECK (*i++ == DCPTimePeriod (DCPTime(4000 + 144000), DCPTime(4000 + 192000)));
286         }
287 }
288
289 /** Check reel split with a muxed video/audio source */
290 BOOST_AUTO_TEST_CASE (reels_test6)
291 {
292         shared_ptr<Film> film = new_test_film ("reels_test6");
293         film->set_name ("reels_test6");
294         film->set_container (Ratio::from_id ("185"));
295         film->set_dcp_content_type (DCPContentType::from_isdcf_name ("TST"));
296         shared_ptr<FFmpegContent> A (new FFmpegContent("test/data/test2.mp4"));
297         film->examine_and_add_content (A);
298         BOOST_REQUIRE (!wait_for_jobs ());
299
300         film->set_j2k_bandwidth (100000000);
301         film->set_reel_type (REELTYPE_BY_LENGTH);
302         /* This is just over 2.5s at 100Mbit/s; should correspond to 60 frames */
303         film->set_reel_length (31253154);
304         film->make_dcp ();
305         BOOST_REQUIRE (!wait_for_jobs ());
306 }
307
308 /** Check the case where the last bit of audio hangs over the end of the video
309  *  and we are using REELTYPE_BY_VIDEO_CONTENT.
310  */
311 BOOST_AUTO_TEST_CASE (reels_test7)
312 {
313         shared_ptr<Film> film = new_test_film ("reels_test7");
314         film->set_name ("reels_test7");
315         film->set_container (Ratio::from_id ("185"));
316         film->set_dcp_content_type (DCPContentType::from_isdcf_name ("TST"));
317         shared_ptr<Content> A = content_factory("test/data/flat_red.png").front();
318         film->examine_and_add_content (A);
319         BOOST_REQUIRE (!wait_for_jobs ());
320         shared_ptr<Content> B = content_factory("test/data/awkward_length.wav").front();
321         film->examine_and_add_content (B);
322         BOOST_REQUIRE (!wait_for_jobs ());
323         film->set_video_frame_rate (24);
324         A->video->set_length (3 * 24);
325
326         film->set_reel_type (REELTYPE_BY_VIDEO_CONTENT);
327         BOOST_REQUIRE_EQUAL (film->reels().size(), 2);
328         BOOST_CHECK (film->reels().front() == DCPTimePeriod(DCPTime(0), DCPTime::from_frames(3 * 24, 24)));
329         BOOST_CHECK (film->reels().back() == DCPTimePeriod(DCPTime::from_frames(3 * 24, 24), DCPTime::from_frames(3 * 24 + 1, 24)));
330
331         film->make_dcp ();
332         BOOST_REQUIRE (!wait_for_jobs ());
333 }
334
335 /** Check a reels-related error; make_dcp() would raise a ProgrammingError */
336 BOOST_AUTO_TEST_CASE (reels_test8)
337 {
338         shared_ptr<Film> film = new_test_film ("reels_test8");
339         film->set_name ("reels_test8");
340         film->set_container (Ratio::from_id ("185"));
341         film->set_dcp_content_type (DCPContentType::from_isdcf_name ("TST"));
342         shared_ptr<FFmpegContent> A (new FFmpegContent("test/data/test2.mp4"));
343         film->examine_and_add_content (A);
344         BOOST_REQUIRE (!wait_for_jobs ());
345
346         A->set_trim_end (ContentTime::from_seconds (1));
347         film->make_dcp ();
348         BOOST_REQUIRE (!wait_for_jobs ());
349 }
350
351 /** Check another reels-related error; make_dcp() would raise a ProgrammingError */
352 BOOST_AUTO_TEST_CASE (reels_test9)
353 {
354         shared_ptr<Film> film = new_test_film2("reels_test9a");
355         shared_ptr<FFmpegContent> A(new FFmpegContent("test/data/flat_red.png"));
356         film->examine_and_add_content(A);
357         BOOST_REQUIRE(!wait_for_jobs());
358         A->video->set_length(5 * 24);
359         film->set_video_frame_rate(24);
360         film->make_dcp();
361         BOOST_REQUIRE(!wait_for_jobs());
362
363         shared_ptr<Film> film2 = new_test_film2("reels_test9b");
364         shared_ptr<DCPContent> B(new DCPContent(film->dir(film->dcp_name())));
365         film2->examine_and_add_content(B);
366         film2->examine_and_add_content(content_factory("test/data/dcp_sub4.xml").front());
367         B->set_reference_video(true);
368         B->set_reference_audio(true);
369         BOOST_REQUIRE(!wait_for_jobs());
370         film2->set_reel_type(REELTYPE_BY_VIDEO_CONTENT);
371         film2->write_metadata();
372         film2->make_dcp();
373         BOOST_REQUIRE(!wait_for_jobs());
374 }
375
376 /** Another reels-related error; make_dcp() would raise a ProgrammingError
377  *  in AudioBuffers::allocate due to an attempt to allocate a negatively-sized buffer.
378  *  This was triggered by a VF where there are referenced audio reels followed by
379  *  VF audio.  When the VF audio arrives the Writer did not correctly skip over the
380  *  referenced reels.
381  */
382 BOOST_AUTO_TEST_CASE (reels_test10)
383 {
384         /* Make the OV */
385         shared_ptr<Film> ov = new_test_film2("reels_test10_ov");
386         shared_ptr<FFmpegContent> A(new FFmpegContent("test/data/flat_red.png"));
387         ov->examine_and_add_content (A);
388         BOOST_REQUIRE (!wait_for_jobs());
389         A->video->set_length (5 * 24);
390
391         shared_ptr<FFmpegContent> B(new FFmpegContent("test/data/flat_red.png"));
392         ov->examine_and_add_content (B);
393         BOOST_REQUIRE (!wait_for_jobs());
394         B->video->set_length (5 * 24);
395
396         ov->set_reel_type (REELTYPE_BY_VIDEO_CONTENT);
397         ov->make_dcp ();
398         BOOST_REQUIRE (!wait_for_jobs());
399         ov->write_metadata ();
400
401         /* Now try to make the VF; this used to fail */
402         shared_ptr<Film> vf = new_test_film2("reels_test10_vf");
403         shared_ptr<DCPContent> ov_dcp(new DCPContent(ov->dir(ov->dcp_name())));
404         vf->examine_and_add_content (ov_dcp);
405         BOOST_REQUIRE (!wait_for_jobs());
406         vf->set_reel_type (REELTYPE_BY_VIDEO_CONTENT);
407         ov_dcp->set_reference_video (true);
408         ov_dcp->set_reference_audio (true);
409         vf->examine_and_add_content (content_factory("test/data/15s.srt").front());
410         BOOST_REQUIRE (!wait_for_jobs());
411
412         vf->make_dcp ();
413         BOOST_REQUIRE (!wait_for_jobs());
414         vf->write_metadata ();
415 }