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/time_calculation_test.cc
23 * @brief Test calculation of timings when frame rates change.
29 #include "lib/ffmpeg_content.h"
30 #include "lib/video_content.h"
31 #include "lib/player.h"
32 #include "lib/audio_content.h"
34 #include <boost/test/unit_test.hpp>
38 using std::make_shared;
39 using std::shared_ptr;
41 using namespace dcpomatic;
44 static string const xml = "<Content>"
46 "<BurnSubtitles>0</BurnSubtitles>"
47 "<BitsPerPixel>8</BitsPerPixel>"
48 "<Path>test/data/red_24.mp4</Path>"
49 "<Digest>2760e03c7251480f7f02c01a907792673784335</Digest>"
50 "<Position>0</Position>"
51 "<TrimStart>0</TrimStart>"
52 "<TrimEnd>0</TrimEnd>"
53 "<VideoLength>1353600</VideoLength>"
54 "<VideoWidth>1280</VideoWidth>"
55 "<VideoHeight>720</VideoHeight>"
56 "<VideoFrameRate>25</VideoFrameRate>"
57 "<VideoFrameType>2d</VideoFrameType>"
58 "<LeftCrop>0</LeftCrop>"
59 "<RightCrop>0</RightCrop>"
60 "<TopCrop>0</TopCrop>"
61 "<BottomCrop>0</BottomCrop>"
66 "<InputTransferFunction>"
67 "<Type>ModifiedGamma</Type>"
68 "<Power>2.222222222222222</Power>"
69 "<Threshold>0.081</Threshold>"
72 "</InputTransferFunction>"
75 "<GreenX>0.3</GreenX>"
76 "<GreenY>0.6</GreenY>"
79 "<WhiteX>0.3127</WhiteX>"
80 "<WhiteY>0.329</WhiteY>"
81 "<OutputGamma>2.6</OutputGamma>"
84 "<FadeOut>0</FadeOut>"
85 "<AudioGain>0</AudioGain>"
86 "<AudioDelay>0</AudioDelay>"
87 "<UseSubtitles>0</UseSubtitles>"
88 "<SubtitleXOffset>0</SubtitleXOffset>"
89 "<SubtitleYOffset>0</SubtitleYOffset>"
90 "<SubtitleXScale>1</SubtitleXScale>"
91 "<SubtitleYScale>1</SubtitleYScale>"
92 "<SubtitleLanguage></SubtitleLanguage>"
94 "<Selected>1</Selected>"
95 "<Name>und; 2 channels</Name>"
97 "<FrameRate>44100</FrameRate>"
98 "<Length>44100</Length>"
99 "<Channels>2</Channels>"
100 "<FirstAudio>0</FirstAudio>"
102 "<InputChannels>2</InputChannels>"
103 "<OutputChannels>12</OutputChannels>"
104 "<Gain Input=\"0\" Output=\"0\">1</Gain>"
105 "<Gain Input=\"0\" Output=\"1\">0</Gain>"
106 "<Gain Input=\"0\" Output=\"2\">0</Gain>"
107 "<Gain Input=\"0\" Output=\"3\">0</Gain>"
108 "<Gain Input=\"0\" Output=\"4\">0</Gain>"
109 "<Gain Input=\"0\" Output=\"5\">0</Gain>"
110 "<Gain Input=\"0\" Output=\"6\">0</Gain>"
111 "<Gain Input=\"0\" Output=\"7\">0</Gain>"
112 "<Gain Input=\"0\" Output=\"8\">0</Gain>"
113 "<Gain Input=\"0\" Output=\"9\">0</Gain>"
114 "<Gain Input=\"0\" Output=\"10\">0</Gain>"
115 "<Gain Input=\"0\" Output=\"11\">0</Gain>"
116 "<Gain Input=\"1\" Output=\"0\">0</Gain>"
117 "<Gain Input=\"1\" Output=\"1\">1</Gain>"
118 "<Gain Input=\"1\" Output=\"2\">0</Gain>"
119 "<Gain Input=\"1\" Output=\"3\">0</Gain>"
120 "<Gain Input=\"1\" Output=\"4\">0</Gain>"
121 "<Gain Input=\"1\" Output=\"5\">0</Gain>"
122 "<Gain Input=\"1\" Output=\"6\">0</Gain>"
123 "<Gain Input=\"1\" Output=\"7\">0</Gain>"
124 "<Gain Input=\"1\" Output=\"8\">0</Gain>"
125 "<Gain Input=\"1\" Output=\"9\">0</Gain>"
126 "<Gain Input=\"1\" Output=\"10\">0</Gain>"
127 "<Gain Input=\"1\" Output=\"11\">0</Gain>"
130 "<FirstVideo>0</FirstVideo>"
134 BOOST_AUTO_TEST_CASE (ffmpeg_time_calculation_test)
136 auto film = new_test_film ("ffmpeg_time_calculation_test");
138 auto doc = make_shared<cxml::Document>();
139 doc->read_string (xml);
142 auto content = make_shared<FFmpegContent>(doc, film->state_version(), notes);
144 /* 25fps content, 25fps DCP */
145 film->set_video_frame_rate (25);
146 BOOST_CHECK_EQUAL (content->full_length_dcp(film).get(), DCPTime::from_seconds(content->video->length() / 25.0).get());
147 /* 25fps content, 24fps DCP; length should be increased */
148 film->set_video_frame_rate (24);
149 BOOST_CHECK_EQUAL (content->full_length_dcp(film).get(), DCPTime::from_seconds(content->video->length() / 24.0).get());
150 /* 25fps content, 30fps DCP; length should be decreased */
151 film->set_video_frame_rate (30);
152 BOOST_CHECK_EQUAL (content->full_length_dcp(film).get(), DCPTime::from_seconds(content->video->length() / 30.0).get());
153 /* 25fps content, 50fps DCP; length should be the same */
154 film->set_video_frame_rate (50);
155 BOOST_CHECK_EQUAL (content->full_length_dcp(film).get(), DCPTime::from_seconds(content->video->length() / 25.0).get());
156 /* 25fps content, 60fps DCP; length should be decreased */
157 film->set_video_frame_rate (60);
158 BOOST_CHECK_EQUAL (content->full_length_dcp(film).get(), DCPTime::from_seconds(content->video->length() * (50.0 / 60) / 25.0).get());
160 /* Make the content audio-only */
161 content->video.reset ();
163 /* 24fps content, 24fps DCP */
164 film->set_video_frame_rate (24);
165 content->set_video_frame_rate (24);
166 BOOST_CHECK_EQUAL (content->full_length_dcp(film).get(), DCPTime::from_seconds(1).get());
167 /* 25fps content, 25fps DCP */
168 film->set_video_frame_rate (25);
169 content->set_video_frame_rate (25);
170 BOOST_CHECK_EQUAL (content->full_length_dcp(film).get(), DCPTime::from_seconds(1).get());
171 /* 25fps content, 24fps DCP; length should be increased */
172 film->set_video_frame_rate (24);
173 BOOST_CHECK_SMALL (labs (content->full_length_dcp(film).get() - DCPTime::from_seconds(25.0 / 24).get()), 2L);
174 /* 25fps content, 30fps DCP; length should be decreased */
175 film->set_video_frame_rate (30);
176 BOOST_CHECK_EQUAL (content->full_length_dcp(film).get(), DCPTime::from_seconds(25.0 / 30).get());
177 /* 25fps content, 50fps DCP; length should be the same */
178 film->set_video_frame_rate (50);
179 BOOST_CHECK_EQUAL (content->full_length_dcp(film).get(), DCPTime::from_seconds(1).get());
180 /* 25fps content, 60fps DCP; length should be decreased */
181 film->set_video_frame_rate (60);
182 BOOST_CHECK_EQUAL (content->full_length_dcp(film).get(), DCPTime::from_seconds(50.0 / 60).get());
187 /** Test Player::content_video_to_dcp */
188 BOOST_AUTO_TEST_CASE (player_time_calculation_test2)
190 auto film = new_test_film ("player_time_calculation_test2");
192 auto doc = make_shared<cxml::Document>();
193 doc->read_string (xml);
196 auto content = make_shared<FFmpegContent>(doc, film->state_version(), notes);
197 film->set_sequence (false);
198 film->add_content (content);
200 auto player = make_shared<Player>(film);
202 /* Position 0, no trim, content rate = DCP rate */
203 content->set_position (film, DCPTime());
204 content->set_trim_start (ContentTime ());
205 content->set_video_frame_rate (24);
206 film->set_video_frame_rate (24);
207 player->setup_pieces ();
208 BOOST_REQUIRE_EQUAL (player->_pieces.size(), 1U);
209 shared_ptr<Piece> piece = player->_pieces.front ();
210 BOOST_CHECK_EQUAL (piece->content_video_to_dcp(0).get(), 0);
211 BOOST_CHECK_EQUAL (piece->content_video_to_dcp(12).get(), DCPTime::from_seconds(0.5).get());
212 BOOST_CHECK_EQUAL (piece->content_video_to_dcp(72).get(), DCPTime::from_seconds(3.0).get());
214 /* Position 3s, no trim, content rate = DCP rate */
215 content->set_position (film, DCPTime::from_seconds(3));
216 content->set_trim_start (ContentTime ());
217 content->set_video_frame_rate (24);
218 film->set_video_frame_rate (24);
219 player->setup_pieces ();
220 BOOST_REQUIRE_EQUAL (player->_pieces.size(), 1U);
221 piece = player->_pieces.front ();
222 BOOST_CHECK_EQUAL (piece->content_video_to_dcp(0).get(), DCPTime::from_seconds(3.00).get());
223 BOOST_CHECK_EQUAL (piece->content_video_to_dcp(36).get(), DCPTime::from_seconds(4.50).get());
224 BOOST_CHECK_EQUAL (piece->content_video_to_dcp(162).get(), DCPTime::from_seconds(9.75).get());
226 /* Position 3s, 1.5s trim, content rate = DCP rate */
227 content->set_position (film, DCPTime::from_seconds(3));
228 content->set_trim_start (ContentTime::from_seconds (1.5));
229 content->set_video_frame_rate (24);
230 film->set_video_frame_rate (24);
231 player->setup_pieces ();
232 BOOST_REQUIRE_EQUAL (player->_pieces.size(), 1U);
233 piece = player->_pieces.front ();
234 BOOST_CHECK_EQUAL (piece->content_video_to_dcp(0).get(), DCPTime::from_seconds(1.50).get());
235 BOOST_CHECK_EQUAL (piece->content_video_to_dcp(36).get(), DCPTime::from_seconds(3.00).get());
236 BOOST_CHECK_EQUAL (piece->content_video_to_dcp(72).get(), DCPTime::from_seconds(4.50).get());
237 BOOST_CHECK_EQUAL (piece->content_video_to_dcp(198).get(), DCPTime::from_seconds(9.75).get());
239 /* Position 0, no trim, content rate 24, DCP rate 25.
240 Now, for example, a DCPTime position of 3s means 3s at 25fps. Since we run the video
241 fast (at 25fps) in this case, this means 75 frames of content video will be used.
243 content->set_position (film, DCPTime());
244 content->set_trim_start (ContentTime ());
245 content->set_video_frame_rate (24);
246 film->set_video_frame_rate (25);
247 player->setup_pieces ();
248 BOOST_REQUIRE_EQUAL (player->_pieces.size(), 1U);
249 piece = player->_pieces.front ();
250 BOOST_CHECK_EQUAL (piece->content_video_to_dcp(0).get(), 0);
251 BOOST_CHECK_EQUAL (piece->content_video_to_dcp(15).get(), DCPTime::from_seconds(0.6).get());
252 BOOST_CHECK_EQUAL (piece->content_video_to_dcp(75).get(), DCPTime::from_seconds(3.0).get());
254 /* Position 3s, no trim, content rate 24, DCP rate 25 */
255 content->set_position (film, DCPTime::from_seconds(3));
256 content->set_trim_start (ContentTime ());
257 content->set_video_frame_rate (24);
258 film->set_video_frame_rate (25);
259 player->setup_pieces ();
260 BOOST_REQUIRE_EQUAL (player->_pieces.size(), 1U);
261 piece = player->_pieces.front ();
262 BOOST_CHECK_EQUAL (piece->content_video_to_dcp(0).get(), DCPTime::from_seconds(3.00).get());
263 BOOST_CHECK_EQUAL (piece->content_video_to_dcp(40).get(), DCPTime::from_seconds(4.60).get());
264 BOOST_CHECK_EQUAL (piece->content_video_to_dcp(169).get(), DCPTime::from_seconds(9.76).get());
266 /* Position 3s, 1.6s trim, content rate 24, DCP rate 25, so the 1.6s trim is at 24fps */
267 content->set_position (film, DCPTime::from_seconds(3));
268 content->set_trim_start (ContentTime::from_seconds (1.6));
269 content->set_video_frame_rate (24);
270 film->set_video_frame_rate (25);
271 player->setup_pieces ();
272 BOOST_REQUIRE_EQUAL (player->_pieces.size(), 1U);
273 piece = player->_pieces.front ();
274 BOOST_CHECK_EQUAL (piece->content_video_to_dcp(0).get(), 142080);
275 BOOST_CHECK_EQUAL (piece->content_video_to_dcp(40).get(), 295680);
276 BOOST_CHECK_EQUAL (piece->content_video_to_dcp(80).get(), 449280);
277 BOOST_CHECK_EQUAL (piece->content_video_to_dcp(209).get(), 944640);
279 /* Position 0, no trim, content rate 24, DCP rate 48
280 Now, for example, a DCPTime position of 3s means 3s at 48fps. Since we run the video
281 with repeated frames in this case, 3 * 24 frames of content video will
282 be used to make 3 * 48 frames of DCP video. The results should be the same as the
283 content rate = DCP rate case.
285 content->set_position (film, DCPTime());
286 content->set_trim_start (ContentTime ());
287 content->set_video_frame_rate (24);
288 film->set_video_frame_rate (48);
289 player->setup_pieces ();
290 BOOST_REQUIRE_EQUAL (player->_pieces.size(), 1U);
291 piece = player->_pieces.front ();
292 BOOST_CHECK_EQUAL (piece->content_video_to_dcp(0).get(), 0);
293 BOOST_CHECK_EQUAL (piece->content_video_to_dcp(12).get(), DCPTime::from_seconds(0.5).get());
294 BOOST_CHECK_EQUAL (piece->content_video_to_dcp(72).get(), DCPTime::from_seconds(3.0).get());
296 /* Position 3s, no trim, content rate 24, DCP rate 48 */
297 content->set_position (film, DCPTime::from_seconds(3));
298 content->set_trim_start (ContentTime ());
299 content->set_video_frame_rate (24);
300 film->set_video_frame_rate (48);
301 player->setup_pieces ();
302 BOOST_REQUIRE_EQUAL (player->_pieces.size(), 1U);
303 piece = player->_pieces.front ();
304 BOOST_CHECK_EQUAL (piece->content_video_to_dcp(0).get(), DCPTime::from_seconds(3.00).get());
305 BOOST_CHECK_EQUAL (piece->content_video_to_dcp(36).get(), DCPTime::from_seconds(4.50).get());
306 BOOST_CHECK_EQUAL (piece->content_video_to_dcp(162).get(), DCPTime::from_seconds(9.75).get());
308 /* Position 3s, 1.5s trim, content rate 24, DCP rate 48 */
309 content->set_position (film, DCPTime::from_seconds(3));
310 content->set_trim_start (ContentTime::from_seconds (1.5));
311 content->set_video_frame_rate (24);
312 film->set_video_frame_rate (48);
313 player->setup_pieces ();
314 BOOST_REQUIRE_EQUAL (player->_pieces.size(), 1U);
315 piece = player->_pieces.front ();
316 BOOST_CHECK_EQUAL (piece->content_video_to_dcp(0).get(), DCPTime::from_seconds(1.50).get());
317 BOOST_CHECK_EQUAL (piece->content_video_to_dcp(36).get(), DCPTime::from_seconds(3.00).get());
318 BOOST_CHECK_EQUAL (piece->content_video_to_dcp(72).get(), DCPTime::from_seconds(4.50).get());
319 BOOST_CHECK_EQUAL (piece->content_video_to_dcp(198).get(), DCPTime::from_seconds(9.75).get());
321 /* Position 0, no trim, content rate 48, DCP rate 24
322 Now, for example, a DCPTime position of 3s means 3s at 24fps. Since we run the video
323 with skipped frames in this case, 3 * 48 frames of content video will
324 be used to make 3 * 24 frames of DCP video.
326 content->set_position (film, DCPTime());
327 content->set_trim_start (ContentTime ());
328 content->set_video_frame_rate (48);
329 film->set_video_frame_rate (24);
330 player->setup_pieces ();
331 BOOST_REQUIRE_EQUAL (player->_pieces.size(), 1U);
332 piece = player->_pieces.front ();
333 BOOST_CHECK_EQUAL (piece->content_video_to_dcp(0).get(), 0);
334 BOOST_CHECK_EQUAL (piece->content_video_to_dcp(24).get(), DCPTime::from_seconds(0.5).get());
335 BOOST_CHECK_EQUAL (piece->content_video_to_dcp(144).get(), DCPTime::from_seconds(3.0).get());
337 /* Position 3s, no trim, content rate 24, DCP rate 48 */
338 content->set_position (film, DCPTime::from_seconds(3));
339 content->set_trim_start (ContentTime ());
340 content->set_video_frame_rate (48);
341 film->set_video_frame_rate (24);
342 player->setup_pieces ();
343 BOOST_REQUIRE_EQUAL (player->_pieces.size(), 1U);
344 piece = player->_pieces.front ();
345 BOOST_CHECK_EQUAL (piece->content_video_to_dcp(0).get(), DCPTime::from_seconds(3.00).get());
346 BOOST_CHECK_EQUAL (piece->content_video_to_dcp(72).get(), DCPTime::from_seconds(4.50).get());
347 BOOST_CHECK_EQUAL (piece->content_video_to_dcp(324).get(), DCPTime::from_seconds(9.75).get());
349 /* Position 3s, 1.5s trim, content rate 24, DCP rate 48 */
350 content->set_position (film, DCPTime::from_seconds(3));
351 content->set_trim_start (ContentTime::from_seconds (1.5));
352 content->set_video_frame_rate (48);
353 film->set_video_frame_rate (24);
354 player->setup_pieces ();
355 BOOST_REQUIRE_EQUAL (player->_pieces.size(), 1U);
356 piece = player->_pieces.front ();
357 BOOST_CHECK_EQUAL (piece->content_video_to_dcp(0).get(), DCPTime::from_seconds(1.50).get());
358 BOOST_CHECK_EQUAL (piece->content_video_to_dcp(72).get(), DCPTime::from_seconds(3.00).get());
359 BOOST_CHECK_EQUAL (piece->content_video_to_dcp(144).get(), DCPTime::from_seconds(4.50).get());
360 BOOST_CHECK_EQUAL (piece->content_video_to_dcp(396).get(), DCPTime::from_seconds(9.75).get());