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