swaroop: beginnings of DCP -> ecinema support. v2.15.12
authorCarl Hetherington <cth@carlh.net>
Mon, 15 Jul 2019 00:31:20 +0000 (01:31 +0100)
committerCarl Hetherington <cth@carlh.net>
Mon, 15 Jul 2019 00:31:20 +0000 (01:31 +0100)
src/lib/ffmpeg_encoder.cc
src/lib/ffmpeg_encoder.h
src/lib/ffmpeg_file_encoder.cc
src/lib/ffmpeg_file_encoder.h
src/lib/util.cc
src/lib/util.h
src/tools/dcpomatic.cc
src/tools/dcpomatic_cli.cc
src/tools/dcpomatic_ecinema.cc
test/ffmpeg_encoder_test.cc

index e29d37cec96915ed916618e45dad4182b8daf57f..7c641f5ab5e684720bc0187ef48307bdff4cea88 100644 (file)
@@ -41,8 +41,10 @@ using std::map;
 using boost::shared_ptr;
 using boost::bind;
 using boost::weak_ptr;
+using boost::optional;
 using namespace dcpomatic;
 
+/** @param key Key to use to encrypt MP4 outputs */
 FFmpegEncoder::FFmpegEncoder (
        shared_ptr<const Film> film,
        weak_ptr<Job> job,
@@ -51,6 +53,10 @@ FFmpegEncoder::FFmpegEncoder (
        bool mixdown_to_stereo,
        bool split_reels,
        int x264_crf
+#ifdef DCPOMATIC_VARIANT_SWAROOP
+       , optional<dcp::Key> key
+       , optional<string> id
+#endif
        )
        : Encoder (film, job)
        , _history (1000)
@@ -79,6 +85,10 @@ FFmpegEncoder::FFmpegEncoder (
                                _film->three_d(),
                                filename,
                                extension
+#ifdef DCPOMATIC_VARIANT_SWAROOP
+                               , key
+                               , id
+#endif
                                )
                        );
        }
@@ -196,20 +206,36 @@ FFmpegEncoder::FileEncoderSet::FileEncoderSet (
        bool three_d,
        boost::filesystem::path output,
        string extension
+#ifdef DCPOMATIC_VARIANT_SWAROOP
+       , optional<dcp::Key> key
+       , optional<string> id
+#endif
        )
 {
        if (three_d) {
                /// TRANSLATORS: L here is an abbreviation for "left", to indicate the left-eye part of a 3D export
                _encoders[EYES_LEFT] = shared_ptr<FFmpegFileEncoder>(
-                       new FFmpegFileEncoder(video_frame_size, video_frame_rate, audio_frame_rate, channels, format, x264_crf, String::compose("%1_%2%3", output.string(), _("L"), extension))
+                       new FFmpegFileEncoder(video_frame_size, video_frame_rate, audio_frame_rate, channels, format, x264_crf, String::compose("%1_%2%3", output.string(), _("L"), extension)
+#ifdef DCPOMATIC_VARIANT_SWAROOP
+                                             , key, id
+#endif
+                               )
                        );
                /// TRANSLATORS: R here is an abbreviation for "right", to indicate the right-eye part of a 3D export
                _encoders[EYES_RIGHT] = shared_ptr<FFmpegFileEncoder>(
-                       new FFmpegFileEncoder(video_frame_size, video_frame_rate, audio_frame_rate, channels, format, x264_crf, String::compose("%1_%2%3", output.string(), _("R"), extension))
+                       new FFmpegFileEncoder(video_frame_size, video_frame_rate, audio_frame_rate, channels, format, x264_crf, String::compose("%1_%2%3", output.string(), _("R"), extension)
+#ifdef DCPOMATIC_VARIANT_SWAROOP
+                                             , key, id
+#endif
+                               )
                        );
        } else {
                _encoders[EYES_BOTH]  = shared_ptr<FFmpegFileEncoder>(
-                       new FFmpegFileEncoder(video_frame_size, video_frame_rate, audio_frame_rate, channels, format, x264_crf, String::compose("%1%2", output.string(), extension))
+                       new FFmpegFileEncoder(video_frame_size, video_frame_rate, audio_frame_rate, channels, format, x264_crf, String::compose("%1%2", output.string(), extension)
+#ifdef DCPOMATIC_VARIANT_SWAROOP
+                                             , key, id
+#endif
+                               )
                        );
        }
 }
index 687d2981ee11108dd72ecb86a3d663c2d720fff9..d8ffd2c6b8acc2c23e9abf3cf184c15172d7fe00 100644 (file)
@@ -39,6 +39,10 @@ public:
                bool mixdown_to_stereo,
                bool split_reels,
                int x264_crf
+#ifdef DCPOMATIC_VARIANT_SWAROOP
+               , boost::optional<dcp::Key> key
+               , boost::optional<std::string> id
+#endif
                );
 
        void go ();
@@ -64,6 +68,10 @@ private:
                        bool three_d,
                        boost::filesystem::path output,
                        std::string extension
+#ifdef DCPOMATIC_VARIANT_SWAROOP
+                       , boost::optional<dcp::Key> key
+                       , boost::optional<std::string> id
+#endif
                        );
 
                boost::shared_ptr<FFmpegFileEncoder> get (Eyes eyes) const;
index eb7036429fb44bbeb05893bffbfa6f2ab89ee331..af6066bfd4cfb524aefcd8ab240ea6d5b514a7a0 100644 (file)
@@ -38,6 +38,7 @@ using std::pair;
 using boost::shared_ptr;
 using boost::bind;
 using boost::weak_ptr;
+using boost::optional;
 using namespace dcpomatic;
 
 int FFmpegFileEncoder::_video_stream_index = 0;
@@ -51,6 +52,10 @@ FFmpegFileEncoder::FFmpegFileEncoder (
        ExportFormat format,
        int x264_crf,
        boost::filesystem::path output
+#ifdef DCPOMATIC_VARIANT_SWAROOP
+       , optional<dcp::Key> key
+       , optional<string> id
+#endif
        )
        : _video_options (0)
        , _audio_channels (channels)
@@ -80,7 +85,11 @@ FFmpegFileEncoder::FFmpegFileEncoder (
        setup_video ();
        setup_audio ();
 
+#ifdef DCPOMATIC_VARIANT_SWAROOP
+       int r = avformat_alloc_output_context2 (&_format_context, av_guess_format("mp4", 0, 0), 0, 0);
+#else
        int r = avformat_alloc_output_context2 (&_format_context, 0, 0, _output.string().c_str());
+#endif
        if (!_format_context) {
                throw runtime_error (String::compose("could not allocate FFmpeg format context (%1)", r));
        }
@@ -116,6 +125,22 @@ FFmpegFileEncoder::FFmpegFileEncoder (
                throw runtime_error ("could not open FFmpeg output file");
        }
 
+#ifdef DCPOMATIC_VARIANT_SWAROOP
+       if (key) {
+               AVDictionary* options = 0;
+               av_dict_set (&options, "encryption_key", key->hex().c_str(), 0);
+               /* XXX: is this OK? */
+               av_dict_set (&options, "encryption_kid", "00000000000000000000000000000000", 0);
+               av_dict_set (&options, "encryption_scheme", "cenc-aes-ctr", 0);
+       }
+
+       if (id) {
+               if (av_dict_set(&_format_context->metadata, SWAROOP_ID_TAG, id->c_str(), 0) < 0) {
+                       throw runtime_error ("Could not write ID to output");
+               }
+       }
+#endif
+
        if (avformat_write_header (_format_context, 0) < 0) {
                throw runtime_error ("could not write header to FFmpeg output file");
        }
index 5308947e6e2900b5f6d97a5f936518a67a5f3436..d763c7eaf7c05a02fd0cc58b4b5598e7a4bf8785 100644 (file)
@@ -25,6 +25,7 @@
 #include "event_history.h"
 #include "audio_mapping.h"
 #include "log.h"
+#include <dcp/key.h>
 extern "C" {
 #include <libavcodec/avcodec.h>
 #include <libavformat/avformat.h>
@@ -41,6 +42,10 @@ public:
                ExportFormat,
                int x264_crf,
                boost::filesystem::path output
+#ifdef DCPOMATIC_VARIANT_SWAROOP
+               , boost::optional<dcp::Key> key
+               , boost::optional<std::string> id
+#endif
                );
 
        void video (boost::shared_ptr<PlayerVideo>, dcpomatic::DCPTime);
index 1f6fba96302b4f9b3dc8943374860221fcc7b1cf..3b6be6dcc227625d28574da53b3af4419d893d28 100644 (file)
@@ -44,6 +44,7 @@
 #include "ffmpeg_image_proxy.h"
 #include "image.h"
 #include "text_decoder.h"
+#include "job_manager.h"
 #include <dcp/locale_convert.h>
 #include <dcp/util.h>
 #include <dcp/raw_convert.h>
@@ -945,6 +946,70 @@ emit_subtitle_image (ContentTimePeriod period, dcp::SubtitleImage sub, dcp::Size
        decoder->emit_bitmap (period, image, rect);
 }
 
+bool
+show_jobs_on_console (bool progress)
+{
+       bool should_stop = false;
+       bool first = true;
+       bool error = false;
+       while (!should_stop) {
+
+               dcpomatic_sleep (5);
+
+               list<shared_ptr<Job> > jobs = JobManager::instance()->get();
+
+               if (!first && progress) {
+                       for (size_t i = 0; i < jobs.size(); ++i) {
+                               cout << "\033[1A\033[2K";
+                       }
+                       cout.flush ();
+               }
+
+               first = false;
+
+               int unfinished = 0;
+               int finished_in_error = 0;
+
+               BOOST_FOREACH (shared_ptr<Job> i, jobs) {
+                       if (progress) {
+                               cout << i->name();
+                               if (!i->sub_name().empty()) {
+                                       cout << "; " << i->sub_name();
+                               }
+                               cout << ": ";
+
+                               if (i->progress ()) {
+                                       cout << i->status() << "                            \n";
+                               } else {
+                                       cout << ": Running           \n";
+                               }
+                       }
+
+                       if (!i->finished ()) {
+                               ++unfinished;
+                       }
+
+                       if (i->finished_in_error ()) {
+                               ++finished_in_error;
+                               error = true;
+                       }
+
+                       if (!progress && i->finished_in_error ()) {
+                               /* We won't see this error if we haven't been showing progress,
+                                  so show it now.
+                               */
+                               cout << i->status() << "\n";
+                       }
+               }
+
+               if (unfinished == 0 || finished_in_error != 0) {
+                       should_stop = true;
+               }
+       }
+
+       return error;
+}
+
 #ifdef DCPOMATIC_VARIANT_SWAROOP
 
 /* Make up a key from the machine UUID */
index f5218653dafb05e106e11546da72c14ad79ac825..d90053cdc19991279337a7d5932d6cf766e8faf0 100644 (file)
@@ -110,6 +110,7 @@ extern void checked_fwrite (void const * ptr, size_t size, FILE* stream, boost::
 extern size_t utf8_strlen (std::string s);
 extern std::string day_of_week_to_string (boost::gregorian::greg_weekday d);
 extern void emit_subtitle_image (dcpomatic::ContentTimePeriod period, dcp::SubtitleImage sub, dcp::Size size, boost::shared_ptr<TextDecoder> decoder);
+extern bool show_jobs_on_console (bool progress);
 #ifdef DCPOMATIC_VARIANT_SWAROOP
 extern boost::shared_ptr<dcp::CertificateChain> read_swaroop_chain (boost::filesystem::path path);
 extern void write_swaroop_chain (boost::shared_ptr<const dcp::CertificateChain> chain, boost::filesystem::path output);
index 96770914d38d1758083131d79b19a21b42c37945..5b2e1870be1d09d706c706f0cd0b14b6a5f024de 100644 (file)
@@ -913,7 +913,11 @@ private:
                        shared_ptr<TranscodeJob> job (new TranscodeJob (_film));
                        job->set_encoder (
                                shared_ptr<FFmpegEncoder> (
-                                       new FFmpegEncoder (_film, job, d->path(), d->format(), d->mixdown_to_stereo(), d->split_reels(), d->x264_crf())
+                                       new FFmpegEncoder (_film, job, d->path(), d->format(), d->mixdown_to_stereo(), d->split_reels(), d->x264_crf()
+#ifdef DCPOMATIC_VARIANT_SWAROOP
+                                                          , optional<dcp::Key>(), optional<string>()
+#endif
+                                               )
                                        )
                                );
                        JobManager::instance()->add (job);
index f049c0c919cc6fc12a6b87150ba6b0a0b85a8976..9388112f6e613861a621ab20d86a222982389109 100644 (file)
@@ -350,64 +350,7 @@ main (int argc, char* argv[])
        }
 
        film->make_dcp ();
-
-       bool should_stop = false;
-       bool first = true;
-       bool error = false;
-       while (!should_stop) {
-
-               dcpomatic_sleep (5);
-
-               list<shared_ptr<Job> > jobs = JobManager::instance()->get ();
-
-               if (!first && progress) {
-                       for (size_t i = 0; i < jobs.size(); ++i) {
-                               cout << "\033[1A\033[2K";
-                       }
-                       cout.flush ();
-               }
-
-               first = false;
-
-               int unfinished = 0;
-               int finished_in_error = 0;
-
-               BOOST_FOREACH (shared_ptr<Job> i, jobs) {
-                       if (progress) {
-                               cout << i->name();
-                               if (!i->sub_name().empty()) {
-                                       cout << "; " << i->sub_name();
-                               }
-                               cout << ": ";
-
-                               if (i->progress ()) {
-                                       cout << i->status() << "                            \n";
-                               } else {
-                                       cout << ": Running           \n";
-                               }
-                       }
-
-                       if (!i->finished ()) {
-                               ++unfinished;
-                       }
-
-                       if (i->finished_in_error ()) {
-                               ++finished_in_error;
-                               error = true;
-                       }
-
-                       if (!progress && i->finished_in_error ()) {
-                               /* We won't see this error if we haven't been showing progress,
-                                  so show it now.
-                               */
-                               cout << i->status() << "\n";
-                       }
-               }
-
-               if (unfinished == 0 || finished_in_error != 0) {
-                       should_stop = true;
-               }
-       }
+       bool const error = show_jobs_on_console (progress);
 
        if (keep_going) {
                while (true) {
index f0ba80b35c18e82292ffacb908fac99ccf806ecf..f91dbb584271db03555155bbee03f80c5f0afa82 100644 (file)
 #include "lib/decrypted_ecinema_kdm.h"
 #include "lib/config.h"
 #include "lib/util.h"
+#include "lib/film.h"
+#include "lib/dcp_content.h"
+#include "lib/job_manager.h"
+#include "lib/cross.h"
+#include "lib/transcode_job.h"
+#include "lib/ffmpeg_encoder.h"
+#include "lib/signal_manager.h"
 #include <dcp/key.h>
 extern "C" {
 #include <libavformat/avformat.h>
@@ -41,6 +48,11 @@ using std::cerr;
 using std::cout;
 using std::ofstream;
 using boost::optional;
+using boost::shared_ptr;
+
+static void convert_dcp (boost::filesystem::path input, boost::filesystem::path output_file, boost::optional<boost::filesystem::path> kdm, int crf);
+static void convert_ffmpeg (boost::filesystem::path input, boost::filesystem::path output_file, string format, boost::optional<boost::filesystem::path> kdm);
+static void write_kdm (string id, string name, dcp::Key key, optional<boost::filesystem::path> kdm);
 
 static void
 help (string n)
@@ -51,6 +63,7 @@ help (string n)
             << "  -o, --output         output directory\n"
             << "  -f, --format         output format (mov or mp4; defaults to mov)\n"
             << "  -k, --kdm            KDM output filename (defaults to stdout)\n"
+            << "  -c, --crf            quality (CRF) when transcoding from DCP (0 is best, 51 is worst, defaults to 23)\n"
             << "\n"
             << "<FILE> is the unencrypted .mp4 file.\n";
 }
@@ -59,8 +72,9 @@ int
 main (int argc, char* argv[])
 {
        optional<boost::filesystem::path> output;
-       optional<boost::filesystem::path> format;
+       optional<string> format;
        optional<boost::filesystem::path> kdm;
+       int crf = 23;
 
        int option_index = 0;
        while (true) {
@@ -70,9 +84,10 @@ main (int argc, char* argv[])
                        { "output", required_argument, 0, 'o' },
                        { "format", required_argument, 0, 'f' },
                        { "kdm", required_argument, 0, 'k' },
+                       { "crf", required_argument, 0, 'c' },
                };
 
-               int c = getopt_long (argc, argv, "vho:f:k:", long_options, &option_index);
+               int c = getopt_long (argc, argv, "vho:f:k:c:", long_options, &option_index);
 
                if (c == -1) {
                        break;
@@ -94,6 +109,9 @@ main (int argc, char* argv[])
                case 'k':
                        kdm = optarg;
                        break;
+               case 'c':
+                       crf = atoi(optarg);
+                       break;
                }
        }
 
@@ -116,8 +134,17 @@ main (int argc, char* argv[])
                exit (EXIT_FAILURE);
        }
 
+       dcpomatic_setup_path_encoding ();
+       dcpomatic_setup ();
+       signal_manager = new SignalManager ();
+
        boost::filesystem::path input = argv[optind];
-       boost::filesystem::path output_file = *output / (input.filename().string() + ".ecinema");
+       boost::filesystem::path output_file;
+       if (boost::filesystem::is_directory(input)) {
+               output_file = *output / (input.parent_path().filename().string() + ".ecinema");
+       } else {
+               output_file = *output / (input.filename().string() + ".ecinema");
+       }
 
        if (!boost::filesystem::is_directory(*output)) {
                boost::filesystem::create_directory (*output);
@@ -125,6 +152,17 @@ main (int argc, char* argv[])
 
        av_register_all ();
 
+       if (boost::filesystem::is_directory(input)) {
+               /* Assume input is a DCP */
+               convert_dcp (input, output_file, kdm, crf);
+       } else {
+               convert_ffmpeg (input, output_file, *format, kdm);
+       }
+}
+
+static void
+convert_ffmpeg (boost::filesystem::path input, boost::filesystem::path output_file, string format, optional<boost::filesystem::path> kdm)
+{
        AVFormatContext* input_fc = avformat_alloc_context ();
        if (avformat_open_input(&input_fc, input.string().c_str(), 0, 0) < 0) {
                cerr << "Could not open input file\n";
@@ -137,7 +175,7 @@ main (int argc, char* argv[])
        }
 
        AVFormatContext* output_fc;
-       avformat_alloc_output_context2 (&output_fc, av_guess_format(format->c_str(), 0, 0), 0, 0);
+       avformat_alloc_output_context2 (&output_fc, av_guess_format(format.c_str(), 0, 0), 0, 0);
 
        for (uint32_t i = 0; i < input_fc->nb_streams; ++i) {
                AVStream* is = input_fc->streams[i];
@@ -217,7 +255,13 @@ main (int argc, char* argv[])
        avformat_free_context (input_fc);
        avformat_free_context (output_fc);
 
-       DecryptedECinemaKDM decrypted_kdm (id, output_file.filename().string(), key, optional<dcp::LocalTime>(), optional<dcp::LocalTime>());
+       write_kdm (id, output_file.filename().string(), key, kdm);
+}
+
+static void
+write_kdm (string id, string name, dcp::Key key, optional<boost::filesystem::path> kdm)
+{
+       DecryptedECinemaKDM decrypted_kdm (id, name, key, optional<dcp::LocalTime>(), optional<dcp::LocalTime>());
        EncryptedECinemaKDM encrypted_kdm = decrypted_kdm.encrypt (Config::instance()->decryption_chain()->leaf());
 
        if (kdm) {
@@ -227,3 +271,32 @@ main (int argc, char* argv[])
                cout << encrypted_kdm.as_xml() << "\n";
        }
 }
+
+static void
+convert_dcp (boost::filesystem::path input, boost::filesystem::path output_file, optional<boost::filesystem::path> kdm, int crf)
+{
+       shared_ptr<Film> film (new Film(boost::optional<boost::filesystem::path>()));
+       shared_ptr<DCPContent> dcp (new DCPContent(input));
+       film->examine_and_add_content (dcp);
+
+       JobManager* jm = JobManager::instance ();
+       while (jm->work_to_do ()) {
+               while (signal_manager->ui_idle ()) {}
+               dcpomatic_sleep (1);
+       }
+       DCPOMATIC_ASSERT (!jm->errors());
+
+       string id = dcp::make_uuid ();
+       dcp::Key key (AES_CTR_KEY_SIZE);
+
+       shared_ptr<TranscodeJob> job (new TranscodeJob(film));
+       job->set_encoder (
+               shared_ptr<FFmpegEncoder>(
+                       new FFmpegEncoder(film, job, output_file, EXPORT_FORMAT_H264, false, false, crf, key, id)
+                       )
+               );
+       jm->add (job);
+       show_jobs_on_console (true);
+
+       write_kdm (id, output_file.filename().string(), key, kdm);
+}
index dd10bd4a142287dc65ddf1e415c87e7b8e75cac5..877e39d49169c6bd91a1a699234ec51e83c910ad 100644 (file)
@@ -36,6 +36,7 @@
 
 using std::string;
 using boost::shared_ptr;
+using boost::optional;
 using namespace dcpomatic;
 
 static void
@@ -67,7 +68,11 @@ ffmpeg_content_test (int number, boost::filesystem::path content, ExportFormat f
 
        film->write_metadata ();
        shared_ptr<Job> job (new TranscodeJob (film));
-       FFmpegEncoder encoder (film, job, String::compose("build/test/%1.%2", name, extension), format, false, false, 23);
+       FFmpegEncoder encoder (film, job, String::compose("build/test/%1.%2", name, extension), format, false, false, 23
+#ifdef DCPOMATIC_VARIANT_SWAROOP
+                              , optional<dcp::Key>(), optional<string>()
+#endif
+               );
        encoder.go ();
 }
 
@@ -111,7 +116,11 @@ BOOST_AUTO_TEST_CASE (ffmpeg_encoder_prores_test5)
 
        film->write_metadata ();
        shared_ptr<Job> job (new TranscodeJob (film));
-       FFmpegEncoder encoder (film, job, "build/test/ffmpeg_encoder_prores_test5.mov", EXPORT_FORMAT_PRORES, false, false, 23);
+       FFmpegEncoder encoder (film, job, "build/test/ffmpeg_encoder_prores_test5.mov", EXPORT_FORMAT_PRORES, false, false, 23
+#ifdef DCPOMATIC_VARIANT_SWAROOP
+                              , optional<dcp::Key>(), optional<string>()
+#endif
+               );
        encoder.go ();
 }
 
@@ -132,7 +141,11 @@ BOOST_AUTO_TEST_CASE (ffmpeg_encoder_prores_test6)
        film->write_metadata();
 
        shared_ptr<Job> job (new TranscodeJob (film));
-       FFmpegEncoder encoder (film, job, "build/test/ffmpeg_encoder_prores_test6.mov", EXPORT_FORMAT_PRORES, false, false, 23);
+       FFmpegEncoder encoder (film, job, "build/test/ffmpeg_encoder_prores_test6.mov", EXPORT_FORMAT_PRORES, false, false, 23
+#ifdef DCPOMATIC_VARIANT_SWAROOP
+                              , optional<dcp::Key>(), optional<string>()
+#endif
+                                        );
        encoder.go ();
 }
 
@@ -156,7 +169,11 @@ BOOST_AUTO_TEST_CASE (ffmpeg_encoder_prores_test7)
        s->only_text()->set_effect_colour (dcp::Colour (0, 255, 255));
 
        shared_ptr<Job> job (new TranscodeJob (film));
-       FFmpegEncoder encoder (film, job, "build/test/ffmpeg_encoder_prores_test7.mov", EXPORT_FORMAT_PRORES, false, false, 23);
+       FFmpegEncoder encoder (film, job, "build/test/ffmpeg_encoder_prores_test7.mov", EXPORT_FORMAT_PRORES, false, false, 23
+#ifdef DCPOMATIC_VARIANT_SWAROOP
+                              , optional<dcp::Key>(), optional<string>()
+#endif
+               );
        encoder.go ();
 }
 
@@ -183,7 +200,11 @@ BOOST_AUTO_TEST_CASE (ffmpeg_encoder_h264_test2)
        film->write_metadata();
 
        shared_ptr<Job> job (new TranscodeJob (film));
-       FFmpegEncoder encoder (film, job, "build/test/ffmpeg_encoder_h264_test2.mp4", EXPORT_FORMAT_H264, false, false, 23);
+       FFmpegEncoder encoder (film, job, "build/test/ffmpeg_encoder_h264_test2.mp4", EXPORT_FORMAT_H264, false, false, 23
+#ifdef DCPOMATIC_VARIANT_SWAROOP
+                              , optional<dcp::Key>(), optional<string>()
+#endif
+               );
        encoder.go ();
 }
 
@@ -208,7 +229,11 @@ BOOST_AUTO_TEST_CASE (ffmpeg_encoder_h264_test3)
        film->write_metadata();
 
        shared_ptr<Job> job (new TranscodeJob (film));
-       FFmpegEncoder encoder (film, job, "build/test/ffmpeg_encoder_h264_test3.mp4", EXPORT_FORMAT_H264, false, false, 23);
+       FFmpegEncoder encoder (film, job, "build/test/ffmpeg_encoder_h264_test3.mp4", EXPORT_FORMAT_H264, false, false, 23
+#ifdef DCPOMATIC_VARIANT_SWAROOP
+                              , optional<dcp::Key>(), optional<string>()
+#endif
+               );
        encoder.go ();
 }
 
@@ -222,7 +247,11 @@ BOOST_AUTO_TEST_CASE (ffmpeg_encoder_h264_test4)
        film->set_container(Ratio::from_id("185"));
 
        shared_ptr<Job> job(new TranscodeJob(film));
-       FFmpegEncoder encoder(film, job, "build/test/ffmpeg_encoder_h264_test4.mp4", EXPORT_FORMAT_H264, false, false, 23);
+       FFmpegEncoder encoder(film, job, "build/test/ffmpeg_encoder_h264_test4.mp4", EXPORT_FORMAT_H264, false, false, 23
+#ifdef DCPOMATIC_VARIANT_SWAROOP
+                              , optional<dcp::Key>(), optional<string>()
+#endif
+               );
        encoder.go();
 }
 
@@ -276,7 +305,11 @@ BOOST_AUTO_TEST_CASE (ffmpeg_encoder_h264_test5)
        Rs->audio->set_mapping (map);
 
        shared_ptr<Job> job (new TranscodeJob (film));
-       FFmpegEncoder encoder (film, job, "build/test/ffmpeg_encoder_h264_test5.mp4", EXPORT_FORMAT_H264, true, false, 23);
+       FFmpegEncoder encoder (film, job, "build/test/ffmpeg_encoder_h264_test5.mp4", EXPORT_FORMAT_H264, true, false, 23
+#ifdef DCPOMATIC_VARIANT_SWAROOP
+                              , optional<dcp::Key>(), optional<string>()
+#endif
+               );
        encoder.go ();
 
        check_ffmpeg ("build/test/ffmpeg_encoder_h264_test5.mp4", "test/data/ffmpeg_encoder_h264_test5.mp4", 1);
@@ -304,6 +337,10 @@ BOOST_AUTO_TEST_CASE (ffmpeg_encoder_h264_test6)
        }
 
        shared_ptr<Job> job (new TranscodeJob (film2));
-       FFmpegEncoder encoder (film2, job, "build/test/ffmpeg_encoder_h264_test6_vf.mp4", EXPORT_FORMAT_H264, true, false, 23);
+       FFmpegEncoder encoder (film2, job, "build/test/ffmpeg_encoder_h264_test6_vf.mp4", EXPORT_FORMAT_H264, true, false, 23
+#ifdef DCPOMATIC_VARIANT_SWAROOP
+                              , optional<dcp::Key>(), optional<string>()
+#endif
+               );
        encoder.go ();
 }