Add Piece::decoder_before().
[dcpomatic.git] / test / time_calculation_test.cc
1 /*
2     Copyright (C) 2015-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/time_calculation_test.cc
23  *  @brief Test calculation of timings when frame rates change.
24  *  @ingroup feature
25  */
26
27
28 #include "lib/film.h"
29 #include "lib/ffmpeg_content.h"
30 #include "lib/video_content.h"
31 #include "lib/player.h"
32 #include "lib/audio_content.h"
33 #include "test.h"
34 #include <boost/test/unit_test.hpp>
35
36
37 using std::list;
38 using std::make_shared;
39 using std::shared_ptr;
40 using std::string;
41 using namespace dcpomatic;
42
43
44 static string const xml = "<Content>"
45         "<Type>FFmpeg</Type>"
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>"
62         "<Scale>"
63         "<Ratio>178</Ratio>"
64         "</Scale>"
65         "<ColourConversion>"
66         "<InputTransferFunction>"
67         "<Type>ModifiedGamma</Type>"
68         "<Power>2.222222222222222</Power>"
69         "<Threshold>0.081</Threshold>"
70         "<A>0.099</A>"
71         "<B>4.5</B>"
72         "</InputTransferFunction>"
73         "<RedX>0.64</RedX>"
74         "<RedY>0.33</RedY>"
75         "<GreenX>0.3</GreenX>"
76         "<GreenY>0.6</GreenY>"
77         "<BlueX>0.15</BlueX>"
78         "<BlueY>0.06</BlueY>"
79         "<WhiteX>0.3127</WhiteX>"
80         "<WhiteY>0.329</WhiteY>"
81         "<OutputGamma>2.6</OutputGamma>"
82         "</ColourConversion>"
83         "<FadeIn>0</FadeIn>"
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>"
93         "<AudioStream>"
94         "<Selected>1</Selected>"
95         "<Name>und; 2 channels</Name>"
96         "<Id>1</Id>"
97         "<FrameRate>44100</FrameRate>"
98         "<Length>44100</Length>"
99         "<Channels>2</Channels>"
100         "<FirstAudio>0</FirstAudio>"
101         "<Mapping>"
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>"
128         "</Mapping>"
129         "</AudioStream>"
130         "<FirstVideo>0</FirstVideo>"
131         "</Content>";
132
133
134 BOOST_AUTO_TEST_CASE (ffmpeg_time_calculation_test)
135 {
136         auto film = new_test_film ("ffmpeg_time_calculation_test");
137
138         auto doc = make_shared<cxml::Document>();
139         doc->read_string (xml);
140
141         list<string> notes;
142         auto content = make_shared<FFmpegContent>(doc, film->state_version(), notes);
143
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());
159
160         /* Make the content audio-only */
161         content->video.reset ();
162
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());
183
184 }
185
186
187 /** Test Player::content_video_to_dcp */
188 BOOST_AUTO_TEST_CASE (player_time_calculation_test2)
189 {
190         auto film = new_test_film ("player_time_calculation_test2");
191
192         auto doc = make_shared<cxml::Document>();
193         doc->read_string (xml);
194
195         list<string> notes;
196         auto content = make_shared<FFmpegContent>(doc, film->state_version(), notes);
197         film->set_sequence (false);
198         film->add_content (content);
199
200         auto player = make_shared<Player>(film);
201
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());
213
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());
225
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());
238
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.
242         */
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());
253
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());
265
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);
278
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.
284         */
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());
295
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());
307
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());
320
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.
325         */
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());
336
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());
348
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());
361 }
362