2 Copyright (C) 2017-2021 Carl Hetherington <cth@carlh.net>
4 This file is part of DCP-o-matic.
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.
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.
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/>.
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"
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"
39 #include <boost/test/unit_test.hpp>
43 using std::shared_ptr;
44 using std::make_shared;
45 using namespace dcpomatic;
49 ffmpeg_content_test (int number, boost::filesystem::path content, ExportFormat format)
53 string name = "ffmpeg_encoder_";
56 case ExportFormat::H264_AAC:
60 case ExportFormat::PRORES:
64 case ExportFormat::H264_PCM:
65 case ExportFormat::SUBTITLES_DCP:
66 BOOST_REQUIRE (false);
69 name = String::compose("%1_test%2", name, number);
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);
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);
80 FFmpegEncoder encoder (film, job, file, format, false, false, false, 23);
87 /** Red / green / blue MP4 -> Prores */
88 BOOST_AUTO_TEST_CASE (ffmpeg_encoder_prores_test1)
90 ffmpeg_content_test (1, "test/data/test.mp4", ExportFormat::PRORES);
94 /** Dolby Aurora trailer VOB -> Prores */
95 BOOST_AUTO_TEST_CASE (ffmpeg_encoder_prores_test2)
97 ffmpeg_content_test (2, TestPaths::private_data() / "dolby_aurora.vob", ExportFormat::PRORES);
101 /** Sintel trailer -> Prores */
102 BOOST_AUTO_TEST_CASE (ffmpeg_encoder_prores_test3)
104 ffmpeg_content_test (3, TestPaths::private_data() / "Sintel_Trailer1.480p.DivX_Plus_HD.mkv", ExportFormat::PRORES);
108 /** Big Buck Bunny trailer -> Prores */
109 BOOST_AUTO_TEST_CASE (ffmpeg_encoder_prores_test4)
111 ffmpeg_content_test (4, TestPaths::private_data() / "big_buck_bunny_trailer_480p.mov", ExportFormat::PRORES);
115 /** Still image -> Prores */
116 BOOST_AUTO_TEST_CASE (ffmpeg_encoder_prores_test5)
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);
124 film->examine_and_add_content (c);
125 BOOST_REQUIRE (!wait_for_jobs ());
127 c->video->set_length (240);
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);
136 /** Subs -> Prores */
137 BOOST_AUTO_TEST_CASE (ffmpeg_encoder_prores_test6)
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);
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();
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);
158 /** Video + subs -> Prores */
159 BOOST_AUTO_TEST_CASE (ffmpeg_encoder_prores_test7)
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);
166 auto c = make_shared<FFmpegContent>("test/data/test.mp4");
167 film->examine_and_add_content (c);
168 BOOST_REQUIRE (!wait_for_jobs ());
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));
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);
183 /** Red / green / blue MP4 -> H264 */
184 BOOST_AUTO_TEST_CASE (ffmpeg_encoder_h264_test1)
186 ffmpeg_content_test(1, "test/data/test.mp4", ExportFormat::H264_AAC);
190 /** Just subtitles -> H264 */
191 BOOST_AUTO_TEST_CASE (ffmpeg_encoder_h264_test2)
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);
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();
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);
212 /** Video + subs -> H264 */
213 BOOST_AUTO_TEST_CASE (ffmpeg_encoder_h264_test3)
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);
220 auto c = make_shared<FFmpegContent>("test/data/test.mp4");
221 film->examine_and_add_content (c);
222 BOOST_REQUIRE (!wait_for_jobs());
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();
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);
238 /** Scope-in-flat DCP -> H264 */
239 BOOST_AUTO_TEST_CASE (ffmpeg_encoder_h264_test4)
241 auto film = new_test_film2("ffmpeg_encoder_h264_test4", {make_shared<DCPContent>("test/data/scope_dcp")});
242 BOOST_REQUIRE(!wait_for_jobs());
244 film->set_container(Ratio::from_id("185"));
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);
252 /** Test mixdown from 5.1 to stereo */
253 BOOST_AUTO_TEST_CASE (ffmpeg_encoder_h264_test5)
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);
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 ());
274 AudioMapping map (1, MAX_DCP_AUDIO_CHANNELS);
276 L->set_position (film, DCPTime::from_seconds(0));
279 L->audio->set_mapping (map);
280 R->set_position (film, DCPTime::from_seconds(1));
283 R->audio->set_mapping (map);
284 C->set_position (film, DCPTime::from_seconds(2));
287 C->audio->set_mapping (map);
288 Lfe->set_position (film, DCPTime::from_seconds(3));
291 Lfe->audio->set_mapping (map);
292 Ls->set_position (film, DCPTime::from_seconds(4));
295 Ls->audio->set_mapping (map);
296 Rs->set_position (film, DCPTime::from_seconds(5));
299 Rs->audio->set_mapping (map);
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);
305 check_ffmpeg ("build/test/ffmpeg_encoder_h264_test5.mp4", "test/data/ffmpeg_encoder_h264_test5.mp4", 1);
309 /** Test export of a VF */
310 BOOST_AUTO_TEST_CASE (ffmpeg_encoder_h264_test6)
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);
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) {
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);
335 /** Test export of a 3D DCP in a 2D project */
336 BOOST_AUTO_TEST_CASE (ffmpeg_encoder_3d_dcp_to_h264)
338 auto dcp = make_shared<DCPContent>(TestPaths::private_data() / "xm");
339 auto film2 = new_test_film2 ("ffmpeg_encoder_3d_dcp_to_h264_export", {dcp});
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);
347 /** Test export of a 3D DCP in a 2D project */
348 BOOST_AUTO_TEST_CASE (ffmpeg_encoder_h264_test7)
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);
360 auto dcp = make_shared<DCPContent>(film->dir(film->dcp_name()));
361 auto film2 = new_test_film2 ("ffmpeg_encoder_h264_test7_export", {dcp});
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);
369 /** Stereo project with mixdown-to-stereo set */
370 BOOST_AUTO_TEST_CASE (ffmpeg_encoder_h264_test8)
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);
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);
383 /** 7.1/HI/VI (i.e. 12-channel) project */
384 BOOST_AUTO_TEST_CASE (ffmpeg_encoder_h264_test9)
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);
392 film->examine_and_add_content (c);
393 BOOST_REQUIRE (!wait_for_jobs ());
395 c->video->set_length (240);
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);
404 /** DCP -> Prores with crop */
405 BOOST_AUTO_TEST_CASE (ffmpeg_encoder_prores_from_dcp_with_crop)
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 ();
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);
419 /** DCP -> H264 with crop */
420 BOOST_AUTO_TEST_CASE (ffmpeg_encoder_h264_from_dcp_with_crop)
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 ();
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);
434 /** Export to H264 with reels */
435 BOOST_AUTO_TEST_CASE (ffmpeg_encoder_h264_with_reels)
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);
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);
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);
455 check ("build/test/ffmpeg_encoder_h264_with_reels_reel1.mov");
456 check ("build/test/ffmpeg_encoder_h264_with_reels_reel2.mov");
460 /** Regression test for "Error during decoding: Butler finished" (#2097) */
461 BOOST_AUTO_TEST_CASE (ffmpeg_encoder_prores_regression_1)
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 });
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);
472 /** Regression test for Butler video buffers reached 480 frames (audio is 0) (#2101) */
473 BOOST_AUTO_TEST_CASE (ffmpeg_encoder_prores_regression_2)
475 auto logs = dcpomatic_log->types();
476 dcpomatic_log->set_types(logs | LogEntry::TYPE_DEBUG_PLAYER);
478 auto content = content_factory(TestPaths::private_data() / "tge_clip.mkv").front();
479 auto film = new_test_film2 ("ffmpeg_encoder_prores_regression_2", { content });
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);
485 dcpomatic_log->set_types(logs);