Rearrange checking (and re-examining) content.
[dcpomatic.git] / test / ffmpeg_encoder_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 #include "lib/audio_content.h"
23 #include "lib/compose.hpp"
24 #include "lib/config.h"
25 #include "lib/content_factory.h"
26 #include "lib/dcp_content.h"
27 #include "lib/dcpomatic_log.h"
28 #include "lib/ffmpeg_content.h"
29 #include "lib/ffmpeg_encoder.h"
30 #include "lib/ffmpeg_examiner.h"
31 #include "lib/film.h"
32 #include "lib/image_content.h"
33 #include "lib/ratio.h"
34 #include "lib/string_text_file_content.h"
35 #include "lib/text_content.h"
36 #include "lib/transcode_job.h"
37 #include "lib/video_content.h"
38 #include "test.h"
39 #include <boost/test/unit_test.hpp>
40
41
42 using std::string;
43 using std::shared_ptr;
44 using std::make_shared;
45 using namespace dcpomatic;
46
47
48 static void
49 ffmpeg_content_test (int number, boost::filesystem::path content, ExportFormat format)
50 {
51         Cleanup cl;
52
53         string name = "ffmpeg_encoder_";
54         string extension;
55         switch (format) {
56         case ExportFormat::H264_AAC:
57                 name += "h264";
58                 extension = "mp4";
59                 break;
60         case ExportFormat::PRORES:
61                 name += "prores";
62                 extension = "mov";
63                 break;
64         case ExportFormat::H264_PCM:
65         case ExportFormat::SUBTITLES_DCP:
66                 BOOST_REQUIRE (false);
67         }
68
69         name = String::compose("%1_test%2", name, number);
70
71         auto c = make_shared<FFmpegContent>(content);
72         auto film = new_test_film2 (name, {c}, &cl);
73         film->set_name (name);
74         film->set_audio_channels (6);
75
76         film->write_metadata ();
77         auto job = make_shared<TranscodeJob>(film, TranscodeJob::ChangedBehaviour::IGNORE);
78         auto file = boost::filesystem::path("build") / "test" / String::compose("%1.%2", name, extension);
79         cl.add (file);
80         FFmpegEncoder encoder (film, job, file, format, false, false, false, 23);
81         encoder.go ();
82
83         cl.run ();
84 }
85
86
87 /** Red / green / blue MP4 -> Prores */
88 BOOST_AUTO_TEST_CASE (ffmpeg_encoder_prores_test1)
89 {
90         ffmpeg_content_test (1, "test/data/test.mp4", ExportFormat::PRORES);
91 }
92
93
94 /** Dolby Aurora trailer VOB -> Prores */
95 BOOST_AUTO_TEST_CASE (ffmpeg_encoder_prores_test2)
96 {
97         ffmpeg_content_test (2, TestPaths::private_data() / "dolby_aurora.vob", ExportFormat::PRORES);
98 }
99
100
101 /** Sintel trailer -> Prores */
102 BOOST_AUTO_TEST_CASE (ffmpeg_encoder_prores_test3)
103 {
104         ffmpeg_content_test (3, TestPaths::private_data() / "Sintel_Trailer1.480p.DivX_Plus_HD.mkv", ExportFormat::PRORES);
105 }
106
107
108 /** Big Buck Bunny trailer -> Prores */
109 BOOST_AUTO_TEST_CASE (ffmpeg_encoder_prores_test4)
110 {
111         ffmpeg_content_test (4, TestPaths::private_data() / "big_buck_bunny_trailer_480p.mov", ExportFormat::PRORES);
112 }
113
114
115 /** Still image -> Prores */
116 BOOST_AUTO_TEST_CASE (ffmpeg_encoder_prores_test5)
117 {
118         auto film = new_test_film ("ffmpeg_encoder_prores_test5");
119         film->set_name ("ffmpeg_encoder_prores_test5");
120         auto c = make_shared<ImageContent>(TestPaths::private_data() / "bbc405.png");
121         film->set_container (Ratio::from_id ("185"));
122         film->set_audio_channels (6);
123
124         film->examine_and_add_content (c);
125         BOOST_REQUIRE (!wait_for_jobs ());
126
127         c->video->set_length (240);
128
129         film->write_metadata ();
130         auto job = make_shared<TranscodeJob>(film, TranscodeJob::ChangedBehaviour::IGNORE);
131         FFmpegEncoder encoder (film, job, "build/test/ffmpeg_encoder_prores_test5.mov", ExportFormat::PRORES, false, false, false, 23);
132         encoder.go ();
133 }
134
135
136 /** Subs -> Prores */
137 BOOST_AUTO_TEST_CASE (ffmpeg_encoder_prores_test6)
138 {
139         auto film = new_test_film ("ffmpeg_encoder_prores_test6");
140         film->set_name ("ffmpeg_encoder_prores_test6");
141         film->set_container (Ratio::from_id ("185"));
142         film->set_audio_channels (6);
143
144         auto s = make_shared<StringTextFileContent>("test/data/subrip2.srt");
145         film->examine_and_add_content (s);
146         BOOST_REQUIRE (!wait_for_jobs ());
147         s->only_text()->set_colour (dcp::Colour (255, 255, 0));
148         s->only_text()->set_effect (dcp::Effect::SHADOW);
149         s->only_text()->set_effect_colour (dcp::Colour (0, 255, 255));
150         film->write_metadata();
151
152         auto job = make_shared<TranscodeJob> (film, TranscodeJob::ChangedBehaviour::IGNORE);
153         FFmpegEncoder encoder (film, job, "build/test/ffmpeg_encoder_prores_test6.mov", ExportFormat::PRORES, false, false, false, 23);
154         encoder.go ();
155 }
156
157
158 /** Video + subs -> Prores */
159 BOOST_AUTO_TEST_CASE (ffmpeg_encoder_prores_test7)
160 {
161         auto film = new_test_film ("ffmpeg_encoder_prores_test7");
162         film->set_name ("ffmpeg_encoder_prores_test7");
163         film->set_container (Ratio::from_id ("185"));
164         film->set_audio_channels (6);
165
166         auto c = make_shared<FFmpegContent>("test/data/test.mp4");
167         film->examine_and_add_content (c);
168         BOOST_REQUIRE (!wait_for_jobs ());
169
170         auto s = make_shared<StringTextFileContent>("test/data/subrip.srt");
171         film->examine_and_add_content (s);
172         BOOST_REQUIRE (!wait_for_jobs ());
173         s->only_text()->set_colour (dcp::Colour (255, 255, 0));
174         s->only_text()->set_effect (dcp::Effect::SHADOW);
175         s->only_text()->set_effect_colour (dcp::Colour (0, 255, 255));
176
177         auto job = make_shared<TranscodeJob>(film, TranscodeJob::ChangedBehaviour::IGNORE);
178         FFmpegEncoder encoder (film, job, "build/test/ffmpeg_encoder_prores_test7.mov", ExportFormat::PRORES, false, false, false, 23);
179         encoder.go ();
180 }
181
182
183 /** Red / green / blue MP4 -> H264 */
184 BOOST_AUTO_TEST_CASE (ffmpeg_encoder_h264_test1)
185 {
186         ffmpeg_content_test(1, "test/data/test.mp4", ExportFormat::H264_AAC);
187 }
188
189
190 /** Just subtitles -> H264 */
191 BOOST_AUTO_TEST_CASE (ffmpeg_encoder_h264_test2)
192 {
193         auto film = new_test_film ("ffmpeg_encoder_h264_test2");
194         film->set_name ("ffmpeg_encoder_h264_test2");
195         film->set_container (Ratio::from_id ("185"));
196         film->set_audio_channels (6);
197
198         auto s = make_shared<StringTextFileContent>("test/data/subrip2.srt");
199         film->examine_and_add_content (s);
200         BOOST_REQUIRE (!wait_for_jobs ());
201         s->only_text()->set_colour (dcp::Colour (255, 255, 0));
202         s->only_text()->set_effect (dcp::Effect::SHADOW);
203         s->only_text()->set_effect_colour (dcp::Colour (0, 255, 255));
204         film->write_metadata();
205
206         auto job = make_shared<TranscodeJob>(film, TranscodeJob::ChangedBehaviour::IGNORE);
207         FFmpegEncoder encoder (film, job, "build/test/ffmpeg_encoder_h264_test2.mp4", ExportFormat::H264_AAC, false, false, false, 23);
208         encoder.go ();
209 }
210
211
212 /** Video + subs -> H264 */
213 BOOST_AUTO_TEST_CASE (ffmpeg_encoder_h264_test3)
214 {
215         auto film = new_test_film ("ffmpeg_encoder_h264_test3");
216         film->set_name ("ffmpeg_encoder_h264_test3");
217         film->set_container (Ratio::from_id ("185"));
218         film->set_audio_channels (6);
219
220         auto c = make_shared<FFmpegContent>("test/data/test.mp4");
221         film->examine_and_add_content (c);
222         BOOST_REQUIRE (!wait_for_jobs());
223
224         auto s = make_shared<StringTextFileContent>("test/data/subrip.srt");
225         film->examine_and_add_content (s);
226         BOOST_REQUIRE (!wait_for_jobs ());
227         s->only_text()->set_colour (dcp::Colour (255, 255, 0));
228         s->only_text()->set_effect (dcp::Effect::SHADOW);
229         s->only_text()->set_effect_colour (dcp::Colour (0, 255, 255));
230         film->write_metadata();
231
232         auto job = make_shared<TranscodeJob> (film, TranscodeJob::ChangedBehaviour::IGNORE);
233         FFmpegEncoder encoder (film, job, "build/test/ffmpeg_encoder_h264_test3.mp4", ExportFormat::H264_AAC, false, false, false, 23);
234         encoder.go ();
235 }
236
237
238 /** Scope-in-flat DCP -> H264 */
239 BOOST_AUTO_TEST_CASE (ffmpeg_encoder_h264_test4)
240 {
241         auto film = new_test_film2("ffmpeg_encoder_h264_test4", {make_shared<DCPContent>("test/data/scope_dcp")});
242         BOOST_REQUIRE(!wait_for_jobs());
243
244         film->set_container(Ratio::from_id("185"));
245
246         auto job = make_shared<TranscodeJob>(film, TranscodeJob::ChangedBehaviour::IGNORE);
247         FFmpegEncoder encoder(film, job, "build/test/ffmpeg_encoder_h264_test4.mp4", ExportFormat::H264_AAC, false, false, false, 23);
248         encoder.go();
249 }
250
251
252 /** Test mixdown from 5.1 to stereo */
253 BOOST_AUTO_TEST_CASE (ffmpeg_encoder_h264_test5)
254 {
255         auto film = new_test_film ("ffmpeg_transcoder_h264_test5");
256         film->set_name ("ffmpeg_transcoder_h264_test5");
257         film->set_container (Ratio::from_id ("185"));
258         film->set_audio_channels (6);
259
260         auto L = make_shared<FFmpegContent>("test/data/L.wav");
261         film->examine_and_add_content (L);
262         auto R = make_shared<FFmpegContent>("test/data/R.wav");
263         film->examine_and_add_content (R);
264         auto C = make_shared<FFmpegContent>("test/data/C.wav");
265         film->examine_and_add_content (C);
266         auto Ls = make_shared<FFmpegContent>("test/data/Ls.wav");
267         film->examine_and_add_content (Ls);
268         auto Rs = make_shared<FFmpegContent>("test/data/Rs.wav");
269         film->examine_and_add_content (Rs);
270         auto Lfe = make_shared<FFmpegContent>("test/data/Lfe.wav");
271         film->examine_and_add_content (Lfe);
272         BOOST_REQUIRE (!wait_for_jobs ());
273
274         AudioMapping map (1, MAX_DCP_AUDIO_CHANNELS);
275
276         L->set_position (film, DCPTime::from_seconds(0));
277         map.make_zero ();
278         map.set (0, 0, 1);
279         L->audio->set_mapping (map);
280         R->set_position (film, DCPTime::from_seconds(1));
281         map.make_zero ();
282         map.set (0, 1, 1);
283         R->audio->set_mapping (map);
284         C->set_position (film, DCPTime::from_seconds(2));
285         map.make_zero ();
286         map.set (0, 2, 1);
287         C->audio->set_mapping (map);
288         Lfe->set_position (film, DCPTime::from_seconds(3));
289         map.make_zero ();
290         map.set (0, 3, 1);
291         Lfe->audio->set_mapping (map);
292         Ls->set_position (film, DCPTime::from_seconds(4));
293         map.make_zero ();
294         map.set (0, 4, 1);
295         Ls->audio->set_mapping (map);
296         Rs->set_position (film, DCPTime::from_seconds(5));
297         map.make_zero ();
298         map.set (0, 5, 1);
299         Rs->audio->set_mapping (map);
300
301         auto job = make_shared<TranscodeJob>(film, TranscodeJob::ChangedBehaviour::IGNORE);
302         FFmpegEncoder encoder (film, job, "build/test/ffmpeg_encoder_h264_test5.mp4", ExportFormat::H264_AAC, true, false, false, 23);
303         encoder.go ();
304
305         check_ffmpeg ("build/test/ffmpeg_encoder_h264_test5.mp4", "test/data/ffmpeg_encoder_h264_test5.mp4", 1);
306 }
307
308
309 /** Test export of a VF */
310 BOOST_AUTO_TEST_CASE (ffmpeg_encoder_h264_test6)
311 {
312         auto film = new_test_film2 ("ffmpeg_encoder_h264_test6_ov");
313         film->examine_and_add_content (make_shared<ImageContent>(TestPaths::private_data() / "bbc405.png"));
314         BOOST_REQUIRE (!wait_for_jobs());
315         make_and_verify_dcp (film);
316
317         auto film2 = new_test_film2 ("ffmpeg_encoder_h264_test6_vf");
318         auto ov = make_shared<DCPContent>("build/test/ffmpeg_encoder_h264_test6_ov/" + film->dcp_name(false));
319         film2->examine_and_add_content (ov);
320         BOOST_REQUIRE (!wait_for_jobs());
321         ov->set_reference_video (true);
322         auto subs = content_factory("test/data/subrip.srt").front();
323         film2->examine_and_add_content (subs);
324         BOOST_REQUIRE (!wait_for_jobs());
325         for (auto i: subs->text) {
326                 i->set_use (true);
327         }
328
329         auto job = make_shared<TranscodeJob>(film2, TranscodeJob::ChangedBehaviour::IGNORE);
330         FFmpegEncoder encoder (film2, job, "build/test/ffmpeg_encoder_h264_test6_vf.mp4", ExportFormat::H264_AAC, true, false, false, 23);
331         encoder.go ();
332 }
333
334
335 /** Test export of a 3D DCP in a 2D project */
336 BOOST_AUTO_TEST_CASE (ffmpeg_encoder_3d_dcp_to_h264)
337 {
338         auto dcp = make_shared<DCPContent>(TestPaths::private_data() / "xm");
339         auto film2 = new_test_film2 ("ffmpeg_encoder_3d_dcp_to_h264_export", {dcp});
340
341         auto job = make_shared<TranscodeJob>(film2, TranscodeJob::ChangedBehaviour::IGNORE);
342         FFmpegEncoder encoder (film2, job, "build/test/ffmpeg_encoder_3d_dcp_to_h264.mp4", ExportFormat::H264_AAC, true, false, false, 23);
343         encoder.go ();
344 }
345
346
347 /** Test export of a 3D DCP in a 2D project */
348 BOOST_AUTO_TEST_CASE (ffmpeg_encoder_h264_test7)
349 {
350         auto L = make_shared<ImageContent>(TestPaths::private_data() / "bbc405.png");
351         auto R = make_shared<ImageContent>(TestPaths::private_data() / "bbc405.png");
352         auto film = new_test_film2 ("ffmpeg_encoder_h264_test7_data", {L, R});
353         L->video->set_frame_type (VideoFrameType::THREE_D_LEFT);
354         L->set_position (film, DCPTime());
355         R->video->set_frame_type (VideoFrameType::THREE_D_RIGHT);
356         R->set_position (film, DCPTime());
357         film->set_three_d (true);
358         make_and_verify_dcp (film);
359
360         auto dcp = make_shared<DCPContent>(film->dir(film->dcp_name()));
361         auto film2 = new_test_film2 ("ffmpeg_encoder_h264_test7_export", {dcp});
362
363         auto job = make_shared<TranscodeJob> (film2, TranscodeJob::ChangedBehaviour::IGNORE);
364         FFmpegEncoder encoder (film2, job, "build/test/ffmpeg_encoder_h264_test7.mp4", ExportFormat::H264_AAC, true, false, false, 23);
365         encoder.go ();
366 }
367
368
369 /** Stereo project with mixdown-to-stereo set */
370 BOOST_AUTO_TEST_CASE (ffmpeg_encoder_h264_test8)
371 {
372         auto film = new_test_film2("ffmpeg_encoder_h264_test4");
373         film->examine_and_add_content(make_shared<DCPContent>("test/data/scope_dcp"));
374         BOOST_REQUIRE(!wait_for_jobs());
375         film->set_audio_channels (2);
376
377         auto job = make_shared<TranscodeJob>(film, TranscodeJob::ChangedBehaviour::IGNORE);
378         FFmpegEncoder encoder(film, job, "build/test/ffmpeg_encoder_h264_test8.mp4", ExportFormat::H264_AAC, true, false, false, 23);
379         encoder.go();
380 }
381
382
383 /** 7.1/HI/VI (i.e. 12-channel) project */
384 BOOST_AUTO_TEST_CASE (ffmpeg_encoder_h264_test9)
385 {
386         shared_ptr<Film> film = new_test_film ("ffmpeg_encoder_prores_test9");
387         film->set_name ("ffmpeg_encoder_prores_test9");
388         auto c = make_shared<ImageContent>(TestPaths::private_data() / "bbc405.png");
389         film->set_container (Ratio::from_id ("185"));
390         film->set_audio_channels (12);
391
392         film->examine_and_add_content (c);
393         BOOST_REQUIRE (!wait_for_jobs ());
394
395         c->video->set_length (240);
396
397         film->write_metadata ();
398         auto job = make_shared<TranscodeJob>(film, TranscodeJob::ChangedBehaviour::IGNORE);
399         FFmpegEncoder encoder (film, job, "build/test/ffmpeg_encoder_prores_test9.mov", ExportFormat::H264_AAC, false, false, false, 23);
400         encoder.go ();
401 }
402
403
404 /** DCP -> Prores with crop */
405 BOOST_AUTO_TEST_CASE (ffmpeg_encoder_prores_from_dcp_with_crop)
406 {
407         auto dcp = make_shared<DCPContent>("test/data/import_dcp_test2");
408         auto film = new_test_film2 ("ffmpeg_encoder_prores_from_dcp_with_crop", { dcp });
409         dcp->video->set_left_crop (32);
410         dcp->video->set_right_crop (32);
411         film->write_metadata ();
412
413         auto job = make_shared<TranscodeJob>(film, TranscodeJob::ChangedBehaviour::IGNORE);
414         FFmpegEncoder encoder (film, job, "build/test/ffmpeg_encoder_prores_from_dcp_with_crop.mov", ExportFormat::PRORES, false, false, false, 23);
415         encoder.go ();
416 }
417
418
419 /** DCP -> H264 with crop */
420 BOOST_AUTO_TEST_CASE (ffmpeg_encoder_h264_from_dcp_with_crop)
421 {
422         auto dcp = make_shared<DCPContent>("test/data/import_dcp_test2");
423         auto film = new_test_film2 ("ffmpeg_encoder_h264_from_dcp_with_crop", { dcp });
424         dcp->video->set_left_crop (32);
425         dcp->video->set_right_crop (32);
426         film->write_metadata ();
427
428         auto job = make_shared<TranscodeJob>(film, TranscodeJob::ChangedBehaviour::IGNORE);
429         FFmpegEncoder encoder (film, job, "build/test/ffmpeg_encoder_prores_from_dcp_with_crop.mov", ExportFormat::H264_AAC, false, false, false, 23);
430         encoder.go ();
431 }
432
433
434 /** Export to H264 with reels */
435 BOOST_AUTO_TEST_CASE (ffmpeg_encoder_h264_with_reels)
436 {
437         auto content1 = content_factory("test/data/flat_red.png").front();
438         auto content2 = content_factory("test/data/flat_red.png").front();
439         auto film = new_test_film2 ("ffmpeg_encoder_h264_with_reels", { content1, content2 });
440         film->set_reel_type (ReelType::BY_VIDEO_CONTENT);
441         content1->video->set_length (240);
442         content2->video->set_length (240);
443
444         auto job = make_shared<TranscodeJob>(film, TranscodeJob::ChangedBehaviour::IGNORE);
445         FFmpegEncoder encoder (film, job, "build/test/ffmpeg_encoder_h264_with_reels.mov", ExportFormat::H264_AAC, false, true, false, 23);
446         encoder.go ();
447
448         auto check = [](boost::filesystem::path path) {
449                 auto reel = std::dynamic_pointer_cast<FFmpegContent>(content_factory(path).front());
450                 BOOST_REQUIRE (reel);
451                 FFmpegExaminer examiner(reel);
452                 BOOST_CHECK_EQUAL (examiner.video_length(), 240U);
453         };
454
455         check ("build/test/ffmpeg_encoder_h264_with_reels_reel1.mov");
456         check ("build/test/ffmpeg_encoder_h264_with_reels_reel2.mov");
457 }
458
459
460 /** Regression test for "Error during decoding: Butler finished" (#2097) */
461 BOOST_AUTO_TEST_CASE (ffmpeg_encoder_prores_regression_1)
462 {
463         auto content = content_factory(TestPaths::private_data() / "arrietty_JP-EN.mkv").front();
464         auto film = new_test_film2 ("ffmpeg_encoder_prores_regression_1", { content });
465
466         auto job = make_shared<TranscodeJob>(film, TranscodeJob::ChangedBehaviour::IGNORE);
467         FFmpegEncoder encoder (film, job, "build/test/ffmpeg_encoder_prores_regression_1.mov", ExportFormat::PRORES, false, true, false, 23);
468         encoder.go ();
469 }
470
471
472 /** Regression test for Butler video buffers reached 480 frames (audio is 0) (#2101) */
473 BOOST_AUTO_TEST_CASE (ffmpeg_encoder_prores_regression_2)
474 {
475         auto logs = dcpomatic_log->types();
476         dcpomatic_log->set_types(logs | LogEntry::TYPE_DEBUG_PLAYER);
477
478         auto content = content_factory(TestPaths::private_data() / "tge_clip.mkv").front();
479         auto film = new_test_film2 ("ffmpeg_encoder_prores_regression_2", { content });
480
481         auto job = make_shared<TranscodeJob>(film, TranscodeJob::ChangedBehaviour::IGNORE);
482         FFmpegEncoder encoder (film, job, "build/test/ffmpeg_encoder_prores_regression_2.mov", ExportFormat::PRORES, false, true, false, 23);
483         encoder.go ();
484
485         dcpomatic_log->set_types(logs);
486 }
487