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