Verify a whole bunch of DCPs made by unit tests.
[dcpomatic.git] / test / torture_test.cc
1 /*
2     Copyright (C) 2017 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/torture_test.cc
22  *  @brief Tricky arrangements of content whose resulting DCPs are checked programmatically.
23  *  @ingroup completedcp
24  */
25
26 #include "lib/audio_content.h"
27 #include "lib/film.h"
28 #include "lib/dcp_content_type.h"
29 #include "lib/ratio.h"
30 #include "lib/content_factory.h"
31 #include "lib/video_content.h"
32 #include "test.h"
33 #include <dcp/cpl.h>
34 #include <dcp/reel.h>
35 #include <dcp/reel_sound_asset.h>
36 #include <dcp/reel_picture_asset.h>
37 #include <dcp/sound_asset.h>
38 #include <dcp/mono_picture_asset.h>
39 #include <dcp/mono_picture_frame.h>
40 #include <dcp/openjpeg_image.h>
41 #include <boost/test/unit_test.hpp>
42 #include <iostream>
43
44 using std::list;
45 using std::cout;
46 using std::shared_ptr;
47 using std::dynamic_pointer_cast;
48 using namespace dcpomatic;
49
50 /** Test start/end trim and positioning of some audio content */
51 BOOST_AUTO_TEST_CASE (torture_test1)
52 {
53         auto film = new_test_film2 ("torture_test1");
54         film->set_sequence (false);
55
56         /* Staircase at an offset of 2000 samples, trimmed both start and end, with a gain of exactly 2 (linear) */
57         auto staircase = content_factory("test/data/staircase.wav").front();
58         film->examine_and_add_content (staircase);
59         BOOST_REQUIRE (!wait_for_jobs());
60         staircase->set_position (film, DCPTime::from_frames(2000, film->audio_frame_rate()));
61         staircase->set_trim_start (ContentTime::from_frames(12, 48000));
62         staircase->set_trim_end (ContentTime::from_frames (35, 48000));
63         staircase->audio->set_gain (20 * log10(2));
64
65         /* And again at an offset of 50000 samples, trimmed both start and end, with a gain of exactly 2 (linear) */
66         staircase = content_factory("test/data/staircase.wav").front ();
67         film->examine_and_add_content (staircase);
68         BOOST_REQUIRE (!wait_for_jobs());
69         staircase->set_position (film, DCPTime::from_frames(50000, film->audio_frame_rate()));
70         staircase->set_trim_start (ContentTime::from_frames(12, 48000));
71         staircase->set_trim_end (ContentTime::from_frames(35, 48000));
72         staircase->audio->set_gain (20 * log10(2));
73
74         /* 1s of red at 5s in */
75         auto red = content_factory("test/data/flat_red.png").front();
76         film->examine_and_add_content (red);
77         BOOST_REQUIRE (!wait_for_jobs());
78         red->set_position (film, DCPTime::from_seconds(5));
79         red->video->set_length (24);
80
81         film->set_video_frame_rate (24);
82         make_and_verify_dcp (film);
83
84         dcp::DCP dcp ("build/test/torture_test1/" + film->dcp_name(false));
85         dcp.read ();
86
87         auto cpls = dcp.cpls ();
88         BOOST_REQUIRE_EQUAL (cpls.size(), 1U);
89         auto reels = cpls.front()->reels ();
90         BOOST_REQUIRE_EQUAL (reels.size(), 1U);
91
92         /* Check sound */
93
94         auto reel_sound = reels.front()->main_sound();
95         BOOST_REQUIRE (reel_sound);
96         auto sound = reel_sound->asset();
97         BOOST_REQUIRE (sound);
98         BOOST_CHECK_EQUAL (sound->intrinsic_duration(), 144);
99
100         auto sound_reader = sound->start_read ();
101
102         /* First frame silent */
103         auto fr = sound_reader->get_frame (0);
104         for (int i = 0; i < fr->samples(); ++i) {
105                 for (int j = 0; j < 6; ++j) {
106                         BOOST_CHECK_EQUAL (fr->get(j, i), 0);
107                 }
108         }
109
110         /* The first staircase is 4800 - 12 - 35 = 4753 samples.  One frame is 2000 samples, so we span 3 frames */
111
112         BOOST_REQUIRE_EQUAL (fr->samples(), 2000);
113
114         int stair = 12;
115
116         BOOST_TEST_CONTEXT("First staircase, frame #1") {
117                 fr = sound_reader->get_frame (1);
118                 for (int i = 0; i < fr->samples(); ++i) {
119                         for (int j = 0; j < 6; ++j) {
120                                 if (j == 2) {
121                                         BOOST_CHECK_EQUAL ((fr->get(j, i) + 128) >> 8, stair * 2);
122                                         ++stair;
123                                 } else {
124                                         BOOST_CHECK_EQUAL (fr->get(j, i), 0);
125                                 }
126                         }
127                 }
128         }
129
130         BOOST_TEST_CONTEXT("First staircase, frame #2") {
131                 fr = sound_reader->get_frame (2);
132                 for (int i = 0; i < fr->samples(); ++i) {
133                         for (int j = 0; j < 6; ++j) {
134                                 if (j == 2) {
135                                         BOOST_CHECK_EQUAL ((fr->get(j, i) + 128) >> 8, stair * 2);
136                                         ++stair;
137                                 } else {
138                                         BOOST_CHECK_EQUAL (fr->get(j, i), 0);
139                                 }
140                         }
141                 }
142         }
143
144         BOOST_TEST_CONTEXT("First staircase, frame #3") {
145                 fr = sound_reader->get_frame (3);
146                 for (int i = 0; i < fr->samples(); ++i) {
147                         for (int j = 0; j < 6; ++j) {
148                                 if (j == 2 && i < (4753 - (2000 * 2))) {
149                                         BOOST_CHECK_EQUAL ((fr->get(j, i) + 128) >> 8, stair * 2);
150                                         ++stair;
151                                 } else {
152                                         BOOST_CHECK_EQUAL (fr->get(j, i), 0);
153                                 }
154                         }
155                 }
156         }
157
158         /* Then some silence */
159
160         BOOST_TEST_CONTEXT("Silence") {
161                 for (int i = 4; i < 24; ++i) {
162                         fr = sound_reader->get_frame (i);
163                         for (int j = 0; j < fr->samples(); ++j) {
164                                 for (int k = 0; k < 6; ++k) {
165                                         BOOST_CHECK_EQUAL (fr->get(k, j), 0);
166                                 }
167                         }
168                 }
169         }
170
171         /* Then the same thing again */
172
173         stair = 12;
174
175         fr = sound_reader->get_frame (25);
176         for (int i = 0; i < fr->samples(); ++i) {
177                 for (int j = 0; j < 6; ++j) {
178                         if (j == 2) {
179                                 BOOST_CHECK_EQUAL ((fr->get(j, i) + 128) >> 8, stair * 2);
180                                 ++stair;
181                         } else {
182                                 BOOST_CHECK_EQUAL (fr->get(j, i), 0);
183                         }
184                 }
185         }
186
187         fr = sound_reader->get_frame (26);
188         for (int i = 0; i < fr->samples(); ++i) {
189                 for (int j = 0; j < 6; ++j) {
190                         if (j == 2) {
191                                 BOOST_CHECK_EQUAL ((fr->get(j, i) + 128) >> 8, stair * 2);
192                                 ++stair;
193                         } else {
194                                 BOOST_CHECK_EQUAL (fr->get(j, i), 0);
195                         }
196                 }
197         }
198
199         fr = sound_reader->get_frame (27);
200         for (int i = 0; i < fr->samples(); ++i) {
201                 for (int j = 0; j < 6; ++j) {
202                         if (j == 2 && i < (4753 - (2000 * 2))) {
203                                 BOOST_CHECK_EQUAL ((fr->get(j, i) + 128) >> 8, stair * 2);
204                                 ++stair;
205                         } else {
206                                 BOOST_CHECK_EQUAL (fr->get(j, i), 0);
207                         }
208                 }
209         }
210
211         /* Then some silence */
212
213         for (int i = 28; i < 144; ++i) {
214                 fr = sound_reader->get_frame (i);
215                 for (int j = 0; j < fr->samples(); ++j) {
216                         for (int k = 0; k < 6; ++k) {
217                                 BOOST_CHECK_EQUAL (fr->get(k, j), 0);
218                         }
219                 }
220         }
221
222         /* Check picture */
223
224         auto reel_picture = reels.front()->main_picture();
225         BOOST_REQUIRE (reel_picture);
226         auto picture = dynamic_pointer_cast<dcp::MonoPictureAsset> (reel_picture->asset());
227         BOOST_REQUIRE (picture);
228         BOOST_CHECK_EQUAL (picture->intrinsic_duration(), 144);
229
230         auto picture_reader = picture->start_read ();
231
232         /* First 5 * 24 = 120 frames should be black, possibly with a little noise to raise the bitrate */
233
234         shared_ptr<dcp::OpenJPEGImage> ref;
235         for (int i = 0; i < 120; ++i) {
236                 auto fr = picture_reader->get_frame (i);
237                 auto image = fr->xyz_image ();
238                 auto const size = image->size ();
239                 if (i == 0) {
240                         /* Check the first frame pixel by pixel... */
241                         for (int c = 0; c < 3; ++c) {
242                                 for (int y = 0; y < size.height; ++y) {
243                                         for (int x = 0; x < size.width; ++x) {
244                                                 BOOST_REQUIRE (image->data(c)[y * size.height + x] <= 3);
245                                         }
246                                 }
247                         }
248                         ref = image;
249                 } else {
250                         /* ... then all the others should be the same */
251                         for (int c = 0; c < 3; ++c) {
252                                 BOOST_REQUIRE_MESSAGE (
253                                         memcmp (image->data(c), ref->data(c), size.width * size.height * sizeof(int)) == 0,
254                                         "failed on frame " << i << " component " << c
255                                         );
256                         }
257                 }
258         }
259
260         /* Then 24 red, perhaps also with some noise */
261
262         for (int i = 120; i < 144; ++i) {
263                 auto fr = picture_reader->get_frame (i);
264                 auto image = fr->xyz_image ();
265                 auto const size = image->size ();
266                 if (i == 120) {
267                         for (int y = 0; y < size.height; ++y) {
268                                 for (int x = 0; x < size.width; ++x) {
269                                         BOOST_REQUIRE_MESSAGE (
270                                                 abs(image->data(0)[y * size.height + x] - 2808) <= 5,
271                                                 "failed on frame " << i << " with image data " << image->data(0)[y * size.height + x]
272                                                 );
273                                         BOOST_REQUIRE_MESSAGE (
274                                                 abs(image->data(1)[y * size.height + x] - 2176) <= 5,
275                                                 "failed on frame " << i << " with image data " << image->data(1)[y * size.height + x]
276                                                 );
277                                         BOOST_REQUIRE_MESSAGE (
278                                                 abs(image->data(2)[y * size.height + x] - 865) <= 5,
279                                                 "failed on frame " << i << " with image data " << image->data(2)[y * size.height + x]
280                                                 );
281                                 }
282                         }
283                         ref = image;
284                 } else {
285                         for (int c = 0; c < 3; ++c) {
286                                 BOOST_REQUIRE_MESSAGE (
287                                         memcmp (image->data(c), ref->data(c), size.width * size.height * sizeof(int)) == 0,
288                                         "failed on frame " << i << " component " << c
289                                         );
290                         }
291                 }
292         }
293
294 }