Allow export with one audio stream per channel. v2.15.95
authorCarl Hetherington <cth@carlh.net>
Wed, 5 Aug 2020 16:32:59 +0000 (18:32 +0200)
committerCarl Hetherington <cth@carlh.net>
Wed, 5 Aug 2020 19:46:44 +0000 (21:46 +0200)
src/lib/ffmpeg_encoder.cc
src/lib/ffmpeg_encoder.h
src/lib/ffmpeg_file_encoder.cc
src/lib/ffmpeg_file_encoder.h
src/tools/dcpomatic.cc
src/wx/export_dialog.cc
src/wx/export_dialog.h
test/ffmpeg_encoder_test.cc

index a3d2ff86f3446d2429bc2ccb979a8d57cbbd7635..cf91a9fae6ae0b19849347899071bed925e25980 100644 (file)
@@ -52,6 +52,7 @@ FFmpegEncoder::FFmpegEncoder (
        ExportFormat format,
        bool mixdown_to_stereo,
        bool split_reels,
+       bool audio_stream_per_channel,
        int x264_crf
 #ifdef DCPOMATIC_VARIANT_SWAROOP
        , optional<dcp::Key> key
@@ -123,6 +124,7 @@ FFmpegEncoder::FFmpegEncoder (
                                _film->audio_frame_rate(),
                                _output_audio_channels,
                                format,
+                               audio_stream_per_channel,
                                x264_crf,
                                _film->three_d(),
                                filename,
@@ -230,6 +232,7 @@ FFmpegEncoder::FileEncoderSet::FileEncoderSet (
        int audio_frame_rate,
        int channels,
        ExportFormat format,
+       bool audio_stream_per_channel,
        int x264_crf,
        bool three_d,
        boost::filesystem::path output,
@@ -243,7 +246,9 @@ FFmpegEncoder::FileEncoderSet::FileEncoderSet (
        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,
+                               audio_stream_per_channel, x264_crf, String::compose("%1_%2%3", output.string(), _("L"), extension)
 #ifdef DCPOMATIC_VARIANT_SWAROOP
                                              , key, id
 #endif
@@ -251,7 +256,9 @@ FFmpegEncoder::FileEncoderSet::FileEncoderSet (
                        );
                /// 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,
+                               audio_stream_per_channel, x264_crf, String::compose("%1_%2%3", output.string(), _("R"), extension)
 #ifdef DCPOMATIC_VARIANT_SWAROOP
                                              , key, id
 #endif
@@ -259,7 +266,9 @@ FFmpegEncoder::FileEncoderSet::FileEncoderSet (
                        );
        } 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,
+                               audio_stream_per_channel, x264_crf, String::compose("%1%2", output.string(), extension)
 #ifdef DCPOMATIC_VARIANT_SWAROOP
                                              , key, id
 #endif
index df2dcfcc866f5a7535c697274244dd4a3a02d6fa..4bd264c8bdf6a760cdb251d3b4950557632b8e68 100644 (file)
@@ -38,6 +38,7 @@ public:
                ExportFormat format,
                bool mixdown_to_stereo,
                bool split_reels,
+               bool audio_stream_per_channel,
                int x264_crf
 #ifdef DCPOMATIC_VARIANT_SWAROOP
                , boost::optional<dcp::Key> key
@@ -64,6 +65,7 @@ private:
                        int audio_frame_rate,
                        int channels,
                        ExportFormat,
+                       bool audio_stream_per_channel,
                        int x264_crf,
                        bool three_d,
                        boost::filesystem::path output,
index f8bd14e760280392dead846d66178e997465b5a9..c11d12a8263442252a33f2b8a241da1b1b5a2074 100644 (file)
@@ -42,7 +42,168 @@ using boost::optional;
 using namespace dcpomatic;
 
 int FFmpegFileEncoder::_video_stream_index = 0;
-int FFmpegFileEncoder::_audio_stream_index = 1;
+int FFmpegFileEncoder::_audio_stream_index_base = 1;
+
+
+class ExportAudioStream : public boost::noncopyable
+{
+public:
+       ExportAudioStream (string codec_name, int channels, int frame_rate, AVSampleFormat sample_format, AVFormatContext* format_context, int stream_index)
+               : _format_context (format_context)
+               , _stream_index (stream_index)
+       {
+               _codec = avcodec_find_encoder_by_name (codec_name.c_str());
+               if (!_codec) {
+                       throw runtime_error (String::compose("could not find FFmpeg encoder %1", codec_name));
+               }
+
+               _codec_context = avcodec_alloc_context3 (_codec);
+               if (!_codec_context) {
+                       throw runtime_error ("could not allocate FFmpeg audio context");
+               }
+
+               avcodec_get_context_defaults3 (_codec_context, _codec);
+
+               /* XXX: configurable */
+               _codec_context->bit_rate = channels * 128 * 1024;
+               _codec_context->sample_fmt = sample_format;
+               _codec_context->sample_rate = frame_rate;
+               _codec_context->channel_layout = av_get_default_channel_layout (channels);
+               _codec_context->channels = channels;
+
+               _stream = avformat_new_stream (format_context, _codec);
+               if (!_stream) {
+                       throw runtime_error ("could not create FFmpeg output audio stream");
+               }
+
+               _stream->id = stream_index;
+DCPOMATIC_DISABLE_WARNINGS
+               _stream->codec = _codec_context;
+DCPOMATIC_ENABLE_WARNINGS
+
+               int r = avcodec_open2 (_codec_context, _codec, 0);
+               if (r < 0) {
+                       char buffer[256];
+                       av_strerror (r, buffer, sizeof(buffer));
+                       throw runtime_error (String::compose("could not open FFmpeg audio codec (%1)", buffer));
+               }
+       }
+
+       ~ExportAudioStream ()
+       {
+               avcodec_close (_codec_context);
+       }
+
+       int frame_size () const {
+               return _codec_context->frame_size;
+       }
+
+       bool flush ()
+       {
+               AVPacket packet;
+               av_init_packet (&packet);
+               packet.data = 0;
+               packet.size = 0;
+               bool flushed = false;
+
+               int got_packet;
+DCPOMATIC_DISABLE_WARNINGS
+               avcodec_encode_audio2 (_codec_context, &packet, 0, &got_packet);
+DCPOMATIC_ENABLE_WARNINGS
+               if (got_packet) {
+                       packet.stream_index = 0;
+                       av_interleaved_write_frame (_format_context, &packet);
+               } else {
+                       flushed = true;
+               }
+               av_packet_unref (&packet);
+               return flushed;
+       }
+
+       void write (int size, int channel_offset, int channels, float** data, int64_t sample_offset)
+       {
+               DCPOMATIC_ASSERT (size);
+
+               AVFrame* frame = av_frame_alloc ();
+               DCPOMATIC_ASSERT (frame);
+
+               int const buffer_size = av_samples_get_buffer_size (0, channels, size, _codec_context->sample_fmt, 0);
+               DCPOMATIC_ASSERT (buffer_size >= 0);
+
+               void* samples = av_malloc (buffer_size);
+               DCPOMATIC_ASSERT (samples);
+
+               frame->nb_samples = size;
+               int r = avcodec_fill_audio_frame (frame, channels, _codec_context->sample_fmt, (const uint8_t *) samples, buffer_size, 0);
+               DCPOMATIC_ASSERT (r >= 0);
+
+               switch (_codec_context->sample_fmt) {
+               case AV_SAMPLE_FMT_S16:
+               {
+                       int16_t* q = reinterpret_cast<int16_t*> (samples);
+                       for (int i = 0; i < size; ++i) {
+                               for (int j = 0; j < channels; ++j) {
+                                       *q++ = data[j + channel_offset][i] * 32767;
+                               }
+                       }
+                       break;
+               }
+               case AV_SAMPLE_FMT_S32:
+               {
+                       int32_t* q = reinterpret_cast<int32_t*> (samples);
+                       for (int i = 0; i < size; ++i) {
+                               for (int j = 0; j < channels; ++j) {
+                                       *q++ = data[j + channel_offset][i] * 2147483647;
+                               }
+                       }
+                       break;
+               }
+               case AV_SAMPLE_FMT_FLTP:
+               {
+                       float* q = reinterpret_cast<float*> (samples);
+                       for (int i = 0; i < channels; ++i) {
+                               memcpy (q, data[i + channel_offset], sizeof(float) * size);
+                               q += size;
+                       }
+                       break;
+               }
+               default:
+                       DCPOMATIC_ASSERT (false);
+               }
+
+               DCPOMATIC_ASSERT (_codec_context->time_base.num == 1);
+               frame->pts = sample_offset * _codec_context->time_base.den / _codec_context->sample_rate;
+
+               AVPacket packet;
+               av_init_packet (&packet);
+               packet.data = 0;
+               packet.size = 0;
+               int got_packet;
+
+               DCPOMATIC_DISABLE_WARNINGS
+               if (avcodec_encode_audio2 (_codec_context, &packet, frame, &got_packet) < 0) {
+                       throw EncodeError ("FFmpeg audio encode failed");
+               }
+               DCPOMATIC_ENABLE_WARNINGS
+
+               if (got_packet && packet.size) {
+                       packet.stream_index = _stream_index;
+                       av_interleaved_write_frame (_format_context, &packet);
+                       av_packet_unref (&packet);
+               }
+
+               av_free (samples);
+               av_frame_free (&frame);
+       }
+
+private:
+       AVFormatContext* _format_context;
+       AVCodec* _codec;
+       AVCodecContext* _codec_context;
+       AVStream* _stream;
+       int _stream_index;
+};
+
 
 FFmpegFileEncoder::FFmpegFileEncoder (
        dcp::Size video_frame_size,
@@ -50,6 +211,7 @@ FFmpegFileEncoder::FFmpegFileEncoder (
        int audio_frame_rate,
        int channels,
        ExportFormat format,
+       bool audio_stream_per_channel,
        int x264_crf,
        boost::filesystem::path output
 #ifdef DCPOMATIC_VARIANT_SWAROOP
@@ -57,7 +219,8 @@ FFmpegFileEncoder::FFmpegFileEncoder (
        , optional<string> id
 #endif
        )
-       : _video_options (0)
+       : _audio_stream_per_channel (audio_stream_per_channel)
+       , _video_options (0)
        , _audio_channels (channels)
        , _output (output)
        , _video_frame_size (video_frame_size)
@@ -91,9 +254,6 @@ FFmpegFileEncoder::FFmpegFileEncoder (
                DCPOMATIC_ASSERT (false);
        }
 
-       setup_video ();
-       setup_audio ();
-
 #ifdef DCPOMATIC_VARIANT_SWAROOP
        int r = avformat_alloc_output_context2 (&_format_context, av_guess_format("mov", 0, 0), 0, 0);
 #else
@@ -103,34 +263,8 @@ FFmpegFileEncoder::FFmpegFileEncoder (
                throw runtime_error (String::compose("could not allocate FFmpeg format context (%1)", r));
        }
 
-       _video_stream = avformat_new_stream (_format_context, _video_codec);
-       if (!_video_stream) {
-               throw runtime_error ("could not create FFmpeg output video stream");
-       }
-
-       _audio_stream = avformat_new_stream (_format_context, _audio_codec);
-       if (!_audio_stream) {
-               throw runtime_error ("could not create FFmpeg output audio stream");
-       }
-
-DCPOMATIC_DISABLE_WARNINGS
-       _video_stream->id = _video_stream_index;
-       _video_stream->codec = _video_codec_context;
-
-       _audio_stream->id = _audio_stream_index;
-       _audio_stream->codec = _audio_codec_context;
-DCPOMATIC_ENABLE_WARNINGS
-
-       if (avcodec_open2 (_video_codec_context, _video_codec, &_video_options) < 0) {
-               throw runtime_error ("could not open FFmpeg video codec");
-       }
-
-       r = avcodec_open2 (_audio_codec_context, _audio_codec, 0);
-       if (r < 0) {
-               char buffer[256];
-               av_strerror (r, buffer, sizeof(buffer));
-               throw runtime_error (String::compose ("could not open FFmpeg audio codec (%1)", buffer));
-       }
+       setup_video ();
+       setup_audio ();
 
        r = avio_open_boost (&_format_context->pb, _output, AVIO_FLAG_WRITE);
        if (r < 0) {
@@ -199,31 +333,39 @@ FFmpegFileEncoder::setup_video ()
        _video_codec_context->time_base = (AVRational) { 1, _video_frame_rate };
        _video_codec_context->pix_fmt = _pixel_format;
        _video_codec_context->flags |= AV_CODEC_FLAG_QSCALE | AV_CODEC_FLAG_GLOBAL_HEADER;
-}
 
-void
-FFmpegFileEncoder::setup_audio ()
-{
-       _audio_codec = avcodec_find_encoder_by_name (_audio_codec_name.c_str());
-       if (!_audio_codec) {
-               throw runtime_error (String::compose ("could not find FFmpeg encoder %1", _audio_codec_name));
+       _video_stream = avformat_new_stream (_format_context, _video_codec);
+       if (!_video_stream) {
+               throw runtime_error ("could not create FFmpeg output video stream");
        }
 
-       _audio_codec_context = avcodec_alloc_context3 (_audio_codec);
-       if (!_audio_codec_context) {
-               throw runtime_error ("could not allocate FFmpeg audio context");
+DCPOMATIC_DISABLE_WARNINGS
+       _video_stream->id = _video_stream_index;
+       _video_stream->codec = _video_codec_context;
+DCPOMATIC_ENABLE_WARNINGS
+
+       if (avcodec_open2 (_video_codec_context, _video_codec, &_video_options) < 0) {
+               throw runtime_error ("could not open FFmpeg video codec");
        }
+}
 
-       avcodec_get_context_defaults3 (_audio_codec_context, _audio_codec);
 
-       /* XXX: configurable */
-       _audio_codec_context->bit_rate = _audio_channels * 128 * 1024;
-       _audio_codec_context->sample_fmt = _sample_format;
-       _audio_codec_context->sample_rate = _audio_frame_rate;
-       _audio_codec_context->channel_layout = av_get_default_channel_layout (_audio_channels);
-       _audio_codec_context->channels = _audio_channels;
+void
+FFmpegFileEncoder::setup_audio ()
+{
+       int const streams = _audio_stream_per_channel ? _audio_channels : 1;
+       int const channels_per_stream = _audio_stream_per_channel ? 1 : _audio_channels;
+
+       for (int i = 0; i < streams; ++i) {
+               _audio_streams.push_back(
+                       shared_ptr<ExportAudioStream>(
+                               new ExportAudioStream(_audio_codec_name, channels_per_stream, _audio_frame_rate, _sample_format, _format_context, _audio_stream_index_base + i)
+                               )
+                       );
+       }
 }
 
+
 void
 FFmpegFileEncoder::flush ()
 {
@@ -252,26 +394,18 @@ DCPOMATIC_ENABLE_WARNINGS
                }
                av_packet_unref (&packet);
 
-               av_init_packet (&packet);
-               packet.data = 0;
-               packet.size = 0;
-
-DCPOMATIC_DISABLE_WARNINGS
-               avcodec_encode_audio2 (_audio_codec_context, &packet, 0, &got_packet);
-DCPOMATIC_ENABLE_WARNINGS
-               if (got_packet) {
-                       packet.stream_index = 0;
-                       av_interleaved_write_frame (_format_context, &packet);
-               } else {
-                       flushed_audio = true;
+               flushed_audio = true;
+               BOOST_FOREACH (shared_ptr<ExportAudioStream> i, _audio_streams) {
+                       if (!i->flush()) {
+                               flushed_audio = false;
+                       }
                }
-               av_packet_unref (&packet);
        }
 
        av_write_trailer (_format_context);
 
+       _audio_streams.clear ();
        avcodec_close (_video_codec_context);
-       avcodec_close (_audio_codec_context);
        avio_close (_format_context->pb);
        avformat_free_context (_format_context);
 }
@@ -335,7 +469,8 @@ FFmpegFileEncoder::audio (shared_ptr<AudioBuffers> audio)
 {
        _pending_audio->append (audio);
 
-       int frame_size = _audio_codec_context->frame_size;
+       DCPOMATIC_ASSERT (!_audio_streams.empty());
+       int frame_size = _audio_streams[0]->frame_size();
        if (frame_size == 0) {
                /* codec has AV_CODEC_CAP_VARIABLE_FRAME_SIZE */
                frame_size = _audio_frame_rate / _video_frame_rate;
@@ -349,82 +484,17 @@ FFmpegFileEncoder::audio (shared_ptr<AudioBuffers> audio)
 void
 FFmpegFileEncoder::audio_frame (int size)
 {
-       DCPOMATIC_ASSERT (size);
-
-       AVFrame* frame = av_frame_alloc ();
-       DCPOMATIC_ASSERT (frame);
-
-       int const channels = _pending_audio->channels();
-       DCPOMATIC_ASSERT (channels);
-
-       int const buffer_size = av_samples_get_buffer_size (0, channels, size, _audio_codec_context->sample_fmt, 0);
-       DCPOMATIC_ASSERT (buffer_size >= 0);
-
-       void* samples = av_malloc (buffer_size);
-       DCPOMATIC_ASSERT (samples);
-
-       frame->nb_samples = size;
-       int r = avcodec_fill_audio_frame (frame, channels, _audio_codec_context->sample_fmt, (const uint8_t *) samples, buffer_size, 0);
-       DCPOMATIC_ASSERT (r >= 0);
-
-       float** p = _pending_audio->data ();
-       switch (_audio_codec_context->sample_fmt) {
-       case AV_SAMPLE_FMT_S16:
-       {
-               int16_t* q = reinterpret_cast<int16_t*> (samples);
-               for (int i = 0; i < size; ++i) {
-                       for (int j = 0; j < channels; ++j) {
-                               *q++ = p[j][i] * 32767;
-                       }
+       if (_audio_stream_per_channel) {
+               int offset = 0;
+               BOOST_FOREACH (shared_ptr<ExportAudioStream> i, _audio_streams) {
+                       i->write (size, offset, 1, _pending_audio->data(), _audio_frames);
+                       ++offset;
                }
-               break;
+       } else {
+               DCPOMATIC_ASSERT (!_audio_streams.empty());
+               DCPOMATIC_ASSERT (_pending_audio->channels());
+               _audio_streams[0]->write (size, 0, _pending_audio->channels(), _pending_audio->data(), _audio_frames);
        }
-       case AV_SAMPLE_FMT_S32:
-       {
-               int32_t* q = reinterpret_cast<int32_t*> (samples);
-               for (int i = 0; i < size; ++i) {
-                       for (int j = 0; j < channels; ++j) {
-                               *q++ = p[j][i] * 2147483647;
-                       }
-               }
-               break;
-       }
-       case AV_SAMPLE_FMT_FLTP:
-       {
-               float* q = reinterpret_cast<float*> (samples);
-               for (int i = 0; i < channels; ++i) {
-                       memcpy (q, p[i], sizeof(float) * size);
-                       q += size;
-               }
-               break;
-       }
-       default:
-               DCPOMATIC_ASSERT (false);
-       }
-
-       DCPOMATIC_ASSERT (_audio_stream->time_base.num == 1);
-       frame->pts = _audio_frames * _audio_stream->time_base.den / _audio_frame_rate;
-
-       AVPacket packet;
-       av_init_packet (&packet);
-       packet.data = 0;
-       packet.size = 0;
-
-       int got_packet;
-DCPOMATIC_DISABLE_WARNINGS
-       if (avcodec_encode_audio2 (_audio_codec_context, &packet, frame, &got_packet) < 0) {
-               throw EncodeError ("FFmpeg audio encode failed");
-       }
-DCPOMATIC_ENABLE_WARNINGS
-
-       if (got_packet && packet.size) {
-               packet.stream_index = _audio_stream_index;
-               av_interleaved_write_frame (_format_context, &packet);
-               av_packet_unref (&packet);
-       }
-
-       av_free (samples);
-       av_frame_free (&frame);
 
        _pending_audio->trim_start (size);
        _audio_frames += size;
index 57f9135fa5e88e0a272ca8e601663374caebfd87..1c23d59027c0359f5bb670ad52db2a407e574305 100644 (file)
@@ -31,6 +31,10 @@ extern "C" {
 #include <libavformat/avformat.h>
 }
 
+
+class ExportAudioStream;
+
+
 class FFmpegFileEncoder
 {
 public:
@@ -40,6 +44,7 @@ public:
                int audio_frame_rate,
                int channels,
                ExportFormat,
+               bool audio_stream_per_channel,
                int x264_crf,
                boost::filesystem::path output
 #ifdef DCPOMATIC_VARIANT_SWAROOP
@@ -67,11 +72,10 @@ private:
 
        AVCodec* _video_codec;
        AVCodecContext* _video_codec_context;
-       AVCodec* _audio_codec;
-       AVCodecContext* _audio_codec_context;
+       std::vector<boost::shared_ptr<ExportAudioStream> > _audio_streams;
+       bool _audio_stream_per_channel;
        AVFormatContext* _format_context;
        AVStream* _video_stream;
-       AVStream* _audio_stream;
        AVPixelFormat _pixel_format;
        AVSampleFormat _sample_format;
        AVDictionary* _video_options;
@@ -95,7 +99,7 @@ private:
        boost::mutex _pending_images_mutex;
 
        static int _video_stream_index;
-       static int _audio_stream_index;
+       static int _audio_stream_index_base;
 };
 
 #endif
index 3680e2802dd8d7fb98a34cbcc4f640b828770a05..1a6c1195cdba6fa2c0b612cab637f2fc60064817 100644 (file)
@@ -966,7 +966,7 @@ private:
                        } else {
                                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->split_streams(), d->x264_crf()
 #ifdef DCPOMATIC_VARIANT_SWAROOP
                                                                   , optional<dcp::Key>(), optional<string>()
 #endif
index fa627cd81d51a36c12e9f2e9ad10d95caeb885b1..ca723ddeb9ef39575d17c77c3dc66377402aacbb 100644 (file)
@@ -70,6 +70,9 @@ ExportDialog::ExportDialog (wxWindow* parent, string name)
        add_spacer ();
        _split_reels = new CheckBox (this, _("Write reels into separate files"));
        add (_split_reels, false);
+       add_spacer ();
+       _split_streams = new CheckBox (this, _("Write each audio channel to its own stream"));
+       add (_split_streams, false);
        _x264_crf_label[0] = add (_("Quality"), true);
        _x264_crf = new wxSlider (this, wxID_ANY, 23, 0, 51, wxDefaultPosition, wxDefaultSize, wxSL_HORIZONTAL | wxSL_LABELS);
        add (_x264_crf, false);
@@ -149,6 +152,12 @@ ExportDialog::split_reels () const
        return _split_reels->GetValue ();
 }
 
+bool
+ExportDialog::split_streams () const
+{
+       return _split_streams->GetValue ();
+}
+
 int
 ExportDialog::x264_crf () const
 {
index f1e893b5329b887238cc89b028c30ca76f660195..fc22d036a070404a2f1270011bb1ba8d1346e089 100644 (file)
@@ -34,6 +34,7 @@ public:
        ExportFormat format () const;
        bool mixdown_to_stereo () const;
        bool split_reels () const;
+       bool split_streams () const;
        int x264_crf () const;
 
 private:
@@ -44,6 +45,7 @@ private:
        wxChoice* _format;
        wxCheckBox* _mixdown;
        wxCheckBox* _split_reels;
+       wxCheckBox* _split_streams;
        wxSlider* _x264_crf;
        wxStaticText* _x264_crf_label[2];
        FilePickerCtrl* _file;
index 50dea30e5e85a6845d2dfbb7ecf076d10b07e448..178ed52969b8953baaa1a8f1a40ece592a7abdad 100644 (file)
@@ -71,7 +71,7 @@ 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, false, 23
 #ifdef DCPOMATIC_VARIANT_SWAROOP
                               , optional<dcp::Key>(), optional<string>()
 #endif
@@ -119,7 +119,7 @@ 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, false, 23
 #ifdef DCPOMATIC_VARIANT_SWAROOP
                               , optional<dcp::Key>(), optional<string>()
 #endif
@@ -144,7 +144,7 @@ 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, false, 23
 #ifdef DCPOMATIC_VARIANT_SWAROOP
                               , optional<dcp::Key>(), optional<string>()
 #endif
@@ -172,7 +172,7 @@ 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, false, 23
 #ifdef DCPOMATIC_VARIANT_SWAROOP
                               , optional<dcp::Key>(), optional<string>()
 #endif
@@ -203,7 +203,7 @@ 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_AAC, false, false, 23
+       FFmpegEncoder encoder (film, job, "build/test/ffmpeg_encoder_h264_test2.mp4", EXPORT_FORMAT_H264_AAC, false, false, false, 23
 #ifdef DCPOMATIC_VARIANT_SWAROOP
                               , optional<dcp::Key>(), optional<string>()
 #endif
@@ -232,7 +232,7 @@ 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_AAC, false, false, 23
+       FFmpegEncoder encoder (film, job, "build/test/ffmpeg_encoder_h264_test3.mp4", EXPORT_FORMAT_H264_AAC, false, false, false, 23
 #ifdef DCPOMATIC_VARIANT_SWAROOP
                               , optional<dcp::Key>(), optional<string>()
 #endif
@@ -250,7 +250,7 @@ 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_AAC, false, false, 23
+       FFmpegEncoder encoder(film, job, "build/test/ffmpeg_encoder_h264_test4.mp4", EXPORT_FORMAT_H264_AAC, false, false, false, 23
 #ifdef DCPOMATIC_VARIANT_SWAROOP
                               , optional<dcp::Key>(), optional<string>()
 #endif
@@ -308,7 +308,7 @@ 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_AAC, true, false, 23
+       FFmpegEncoder encoder (film, job, "build/test/ffmpeg_encoder_h264_test5.mp4", EXPORT_FORMAT_H264_AAC, true, false, false, 23
 #ifdef DCPOMATIC_VARIANT_SWAROOP
                               , optional<dcp::Key>(), optional<string>()
 #endif
@@ -340,7 +340,7 @@ 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_AAC, true, false, 23
+       FFmpegEncoder encoder (film2, job, "build/test/ffmpeg_encoder_h264_test6_vf.mp4", EXPORT_FORMAT_H264_AAC, true, false, false, 23
 #ifdef DCPOMATIC_VARIANT_SWAROOP
                               , optional<dcp::Key>(), optional<string>()
 #endif
@@ -371,7 +371,7 @@ BOOST_AUTO_TEST_CASE (ffmpeg_encoder_h264_test7)
        BOOST_REQUIRE (!wait_for_jobs());
 
        shared_ptr<Job> job (new TranscodeJob (film2));
-       FFmpegEncoder encoder (film2, job, "build/test/ffmpeg_encoder_h264_test7.mp4", EXPORT_FORMAT_H264_AAC, true, false, 23
+       FFmpegEncoder encoder (film2, job, "build/test/ffmpeg_encoder_h264_test7.mp4", EXPORT_FORMAT_H264_AAC, true, false, false, 23
 #ifdef DCPOMATIC_VARIANT_SWAROOP
                               , optional<dcp::Key>(), optional<string>()
 #endif
@@ -389,7 +389,7 @@ BOOST_AUTO_TEST_CASE (ffmpeg_encoder_h264_test8)
        film->set_audio_channels (2);
 
        shared_ptr<Job> job(new TranscodeJob(film));
-       FFmpegEncoder encoder(film, job, "build/test/ffmpeg_encoder_h264_test8.mp4", EXPORT_FORMAT_H264_AAC, true, false, 23
+       FFmpegEncoder encoder(film, job, "build/test/ffmpeg_encoder_h264_test8.mp4", EXPORT_FORMAT_H264_AAC, true, false, false, 23
 #ifdef DCPOMATIC_VARIANT_SWAROOP
                               , optional<dcp::Key>(), optional<string>()
 #endif
@@ -414,7 +414,7 @@ BOOST_AUTO_TEST_CASE (ffmpeg_encoder_h264_test9)
 
        film->write_metadata ();
        shared_ptr<Job> job (new TranscodeJob (film));
-       FFmpegEncoder encoder (film, job, "build/test/ffmpeg_encoder_prores_test9.mov", EXPORT_FORMAT_H264_AAC, false, false, 23
+       FFmpegEncoder encoder (film, job, "build/test/ffmpeg_encoder_prores_test9.mov", EXPORT_FORMAT_H264_AAC, false, false, false, 23
 #ifdef DCPOMATIC_VARIANT_SWAROOP
                               , optional<dcp::Key>(), optional<string>()
 #endif