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