317ca9a617b8a8be744e08b473d35d97fef14d82
[dcpomatic.git] / test / threed_test.cc
1 /*
2     Copyright (C) 2013-2021 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
22 /** @file  test/threed_test.cc
23  *  @brief Create some 3D DCPs (without comparing the results to anything).
24  *  @ingroup completedcp
25  */
26
27
28 #include "lib/butler.h"
29 #include "lib/config.h"
30 #include "lib/content_factory.h"
31 #include "lib/cross.h"
32 #include "lib/dcp_content_type.h"
33 #include "lib/dcpomatic_log.h"
34 #include "lib/ffmpeg_content.h"
35 #include "lib/film.h"
36 #include "lib/image.h"
37 #include "lib/job.h"
38 #include "lib/job_manager.h"
39 #include "lib/log_entry.h"
40 #include "lib/make_dcp.h"
41 #include "lib/player.h"
42 #include "lib/ratio.h"
43 #include "lib/util.h"
44 #include "lib/video_content.h"
45 #include "test.h"
46 #include <boost/test/unit_test.hpp>
47 #include <iostream>
48
49
50 using std::cout;
51 using std::make_shared;
52 using std::shared_ptr;
53
54
55 /** Basic sanity check of THREE_D_LEFT_RIGHT */
56 BOOST_AUTO_TEST_CASE (threed_test1)
57 {
58         auto film = new_test_film ("threed_test1");
59         film->set_name ("test_film1");
60         auto c = make_shared<FFmpegContent>("test/data/test.mp4");
61         film->examine_and_add_content (c);
62         BOOST_REQUIRE (!wait_for_jobs());
63
64         c->video->set_frame_type (VideoFrameType::THREE_D_LEFT_RIGHT);
65
66         film->set_container (Ratio::from_id ("185"));
67         film->set_dcp_content_type (DCPContentType::from_isdcf_name ("TST"));
68         film->set_three_d (true);
69         make_and_verify_dcp (film);
70 }
71
72
73 /** Basic sanity check of THREE_D_ALTERNATE; at the moment this is just to make sure
74  *  that such a transcode completes without error.
75  */
76 BOOST_AUTO_TEST_CASE (threed_test2)
77 {
78         auto film = new_test_film ("threed_test2");
79         film->set_name ("test_film2");
80         auto c = make_shared<FFmpegContent>("test/data/test.mp4");
81         film->examine_and_add_content (c);
82         BOOST_REQUIRE (!wait_for_jobs());
83
84         c->video->set_frame_type (VideoFrameType::THREE_D_ALTERNATE);
85
86         film->set_container (Ratio::from_id ("185"));
87         film->set_dcp_content_type (DCPContentType::from_isdcf_name ("TST"));
88         film->set_three_d (true);
89         make_and_verify_dcp (film);
90 }
91
92
93 /** Basic sanity check of THREE_D_LEFT and THREE_D_RIGHT; at the moment this is just to make sure
94  *  that such a transcode completes without error.
95  */
96 BOOST_AUTO_TEST_CASE (threed_test3)
97 {
98         shared_ptr<Film> film = new_test_film2 ("threed_test3");
99         auto L = make_shared<FFmpegContent>("test/data/test.mp4");
100         film->examine_and_add_content (L);
101         auto R = make_shared<FFmpegContent>("test/data/test.mp4");
102         film->examine_and_add_content (R);
103         BOOST_REQUIRE (!wait_for_jobs());
104
105         L->video->set_frame_type (VideoFrameType::THREE_D_LEFT);
106         R->video->set_frame_type (VideoFrameType::THREE_D_RIGHT);
107
108         film->set_three_d (true);
109         make_and_verify_dcp (film);
110 }
111
112
113 BOOST_AUTO_TEST_CASE (threed_test4)
114 {
115         ConfigRestorer cr;
116
117         /* Try to stop out-of-memory crashes on my laptop */
118         Config::instance()->set_master_encoding_threads (boost::thread::hardware_concurrency() / 4);
119
120         auto film = new_test_film2 ("threed_test4");
121         auto L = make_shared<FFmpegContent>(TestPaths::private_data() / "LEFT_TEST_DCP3D4K.mov");
122         film->examine_and_add_content (L);
123         auto R = make_shared<FFmpegContent>(TestPaths::private_data() / "RIGHT_TEST_DCP3D4K.mov");
124         film->examine_and_add_content (R);
125         BOOST_REQUIRE (!wait_for_jobs());
126
127         L->video->set_frame_type (VideoFrameType::THREE_D_LEFT);
128         R->video->set_frame_type (VideoFrameType::THREE_D_RIGHT);
129         /* There doesn't seem much point in encoding the whole input, especially as we're only
130          * checking for errors during the encode and not the result.  Also decoding these files
131          * (4K HQ Prores) is very slow.
132          */
133         L->set_trim_end (dcpomatic::ContentTime::from_seconds(22));
134         R->set_trim_end (dcpomatic::ContentTime::from_seconds(22));
135
136         film->set_three_d (true);
137         make_and_verify_dcp (film, {dcp::VerificationNote::Code::INVALID_PICTURE_ASSET_RESOLUTION_FOR_3D});
138 }
139
140
141 BOOST_AUTO_TEST_CASE (threed_test5)
142 {
143         auto film = new_test_film2 ("threed_test5");
144         auto L = make_shared<FFmpegContent>(TestPaths::private_data() / "boon_telly.mkv");
145         film->examine_and_add_content (L);
146         auto R = make_shared<FFmpegContent>(TestPaths::private_data() / "boon_telly.mkv");
147         film->examine_and_add_content (R);
148         BOOST_REQUIRE (!wait_for_jobs());
149
150         L->video->set_frame_type (VideoFrameType::THREE_D_LEFT);
151         R->video->set_frame_type (VideoFrameType::THREE_D_RIGHT);
152         /* There doesn't seem much point in encoding the whole input, especially as we're only
153          * checking for errors during the encode and not the result.
154          */
155         L->set_trim_end (dcpomatic::ContentTime::from_seconds(3 * 60 + 20));
156         R->set_trim_end (dcpomatic::ContentTime::from_seconds(3 * 60 + 20));
157
158         film->set_three_d (true);
159         make_and_verify_dcp (film, {dcp::VerificationNote::Code::INVALID_PICTURE_FRAME_RATE_FOR_2K});
160 }
161
162
163 BOOST_AUTO_TEST_CASE (threed_test6)
164 {
165         auto film = new_test_film2 ("threed_test6");
166         auto L = make_shared<FFmpegContent>("test/data/3dL.mp4");
167         film->examine_and_add_content (L);
168         auto R = make_shared<FFmpegContent>("test/data/3dR.mp4");
169         film->examine_and_add_content (R);
170         BOOST_REQUIRE (!wait_for_jobs());
171
172         L->video->set_frame_type (VideoFrameType::THREE_D_LEFT);
173         R->video->set_frame_type (VideoFrameType::THREE_D_RIGHT);
174
175         film->set_three_d (true);
176         make_and_verify_dcp (film);
177         check_dcp ("test/data/threed_test6", film->dir(film->dcp_name()));
178 }
179
180
181 /** Check 2D content set as being 3D; this should give an informative error */
182 BOOST_AUTO_TEST_CASE (threed_test7)
183 {
184         using boost::filesystem::path;
185
186         auto film = new_test_film2 ("threed_test7");
187         path const content_path = "test/data/flat_red.png";
188         auto c = content_factory(content_path).front();
189         film->examine_and_add_content (c);
190         BOOST_REQUIRE (!wait_for_jobs());
191
192         c->video->set_frame_type (VideoFrameType::THREE_D);
193         c->video->set_length (24);
194
195         film->set_three_d (true);
196         make_dcp (film, TranscodeJob::ChangedBehaviour::IGNORE);
197         film->write_metadata ();
198
199         auto jm = JobManager::instance ();
200         while (jm->work_to_do ()) {
201                 while (signal_manager->ui_idle()) {}
202                 dcpomatic_sleep_seconds (1);
203         }
204
205         while (signal_manager->ui_idle ()) {}
206
207         BOOST_REQUIRE (jm->errors());
208         shared_ptr<Job> failed;
209         for (auto i: jm->_jobs) {
210                 if (i->finished_in_error()) {
211                         BOOST_REQUIRE (!failed);
212                         failed = i;
213                 }
214         }
215         BOOST_REQUIRE (failed);
216         BOOST_CHECK_EQUAL (failed->error_summary(), String::compose("The content file %1 is set as 3D but does not appear to contain 3D images.  Please set it to 2D.  You can still make a 3D DCP from this content by ticking the 3D option in the DCP video tab.", content_path.string()));
217
218         while (signal_manager->ui_idle ()) {}
219
220         JobManager::drop ();
221 }
222
223
224 /** Trigger a -114 error by trying to make a 3D DCP out of two files with slightly
225  *  different lengths.
226  */
227 BOOST_AUTO_TEST_CASE (threed_test_separate_files_slightly_different_lengths)
228 {
229         shared_ptr<Film> film = new_test_film2 ("threed_test3");
230         auto L = make_shared<FFmpegContent>("test/data/test.mp4");
231         film->examine_and_add_content (L);
232         auto R = make_shared<FFmpegContent>("test/data/test.mp4");
233         film->examine_and_add_content (R);
234         BOOST_REQUIRE (!wait_for_jobs());
235
236         L->video->set_frame_type (VideoFrameType::THREE_D_LEFT);
237         R->video->set_frame_type (VideoFrameType::THREE_D_RIGHT);
238         R->set_trim_end (dcpomatic::ContentTime::from_frames(1, 24));
239
240         film->set_three_d (true);
241         make_and_verify_dcp (film);
242 }
243
244
245 /** Trigger a -114 error by trying to make a 3D DCP out of two files with very
246  *  different lengths.
247  */
248 BOOST_AUTO_TEST_CASE (threed_test_separate_files_very_different_lengths)
249 {
250         shared_ptr<Film> film = new_test_film2 ("threed_test3");
251         auto L = make_shared<FFmpegContent>("test/data/test.mp4");
252         film->examine_and_add_content (L);
253         auto R = make_shared<FFmpegContent>("test/data/test.mp4");
254         film->examine_and_add_content (R);
255         BOOST_REQUIRE (!wait_for_jobs());
256
257         L->video->set_frame_type (VideoFrameType::THREE_D_LEFT);
258         R->video->set_frame_type (VideoFrameType::THREE_D_RIGHT);
259         R->set_trim_end (dcpomatic::ContentTime::from_seconds(1.5));
260
261         film->set_three_d (true);
262         make_and_verify_dcp (film);
263 }
264
265
266 BOOST_AUTO_TEST_CASE (threed_test_butler_overfill)
267 {
268         auto film = new_test_film2("threed_test_butler_overfill");
269         auto A = make_shared<FFmpegContent>(TestPaths::private_data() / "arrietty_JP-EN.mkv");
270         film->examine_and_add_content(A);
271         auto B = make_shared<FFmpegContent>(TestPaths::private_data() / "arrietty_JP-EN.mkv");
272         film->examine_and_add_content(B);
273         BOOST_REQUIRE (!wait_for_jobs());
274
275         auto player = std::make_shared<Player>(film, Image::Alignment::COMPACT);
276         int const audio_channels = 2;
277         auto butler = std::make_shared<Butler>(film, player, AudioMapping(), audio_channels, boost::bind(PlayerVideo::force, AV_PIX_FMT_RGB24), VideoRange::FULL, Image::Alignment::PADDED, true, false);
278
279         int const audio_frames = 1920;
280         std::vector<float> audio(audio_frames * audio_channels);
281
282         B->video->set_frame_type(VideoFrameType::THREE_D_RIGHT);
283         B->set_position(film, dcpomatic::DCPTime());
284
285         butler->seek(dcpomatic::DCPTime(), true);
286         Butler::Error error;
287         for (auto i = 0; i < 960; ++i) {
288                 butler->get_video(Butler::Behaviour::BLOCKING, &error);
289                 butler->get_audio(Butler::Behaviour::BLOCKING, audio.data(), audio_frames);
290         }
291         BOOST_REQUIRE (error.code == Butler::Error::Code::NONE);
292 }
293
294
295 /** Check #2253 in which there was a reel length error with these inputs,
296  *  which are quite different in length (R is about 5s longer than L).
297  */
298 BOOST_AUTO_TEST_CASE (threed_test_incorrect_reel_length)
299 {
300         dcpomatic_log->set_types (
301                 LogEntry::TYPE_GENERAL | LogEntry::TYPE_WARNING |
302                 LogEntry::TYPE_ERROR | LogEntry::TYPE_DISK | LogEntry::TYPE_DEBUG_PLAYER |
303                 LogEntry::TYPE_DEBUG_THREE_D
304                 );
305
306         auto L = make_shared<FFmpegContent>(TestPaths::private_data() / "01 - L 2048x1080.mp4");
307         auto R = make_shared<FFmpegContent>(TestPaths::private_data() / "02 - R 2048x1080.mp4");
308         auto film = new_test_film2("threed_test_incorrect_reel_length", {L, R});
309
310         L->video->set_frame_type(VideoFrameType::THREE_D_LEFT);
311         R->video->set_frame_type(VideoFrameType::THREE_D_RIGHT);
312
313         film->set_three_d(true);
314
315         make_and_verify_dcp (film, {dcp::VerificationNote::Code::INVALID_PICTURE_FRAME_RATE_FOR_2K});
316 }
317