diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/lib/ab_transcoder.cc | 2 | ||||
| -rw-r--r-- | src/lib/decoder.cc | 105 | ||||
| -rw-r--r-- | src/lib/decoder.h | 39 | ||||
| -rw-r--r-- | src/lib/encoder.h | 4 | ||||
| -rw-r--r-- | src/lib/examine_content_job.cc | 6 | ||||
| -rw-r--r-- | src/lib/ffmpeg_decoder.cc | 189 | ||||
| -rw-r--r-- | src/lib/ffmpeg_decoder.h | 17 | ||||
| -rw-r--r-- | src/lib/film.cc | 201 | ||||
| -rw-r--r-- | src/lib/film.h | 54 | ||||
| -rw-r--r-- | src/lib/imagemagick_decoder.h | 4 | ||||
| -rw-r--r-- | src/lib/imagemagick_encoder.h | 3 | ||||
| -rw-r--r-- | src/lib/j2k_still_encoder.h | 3 | ||||
| -rw-r--r-- | src/lib/j2k_wav_encoder.cc | 76 | ||||
| -rw-r--r-- | src/lib/j2k_wav_encoder.h | 2 | ||||
| -rw-r--r-- | src/lib/stream.cc | 4 | ||||
| -rw-r--r-- | src/lib/stream.h | 25 | ||||
| -rw-r--r-- | src/lib/tiff_decoder.cc | 7 | ||||
| -rw-r--r-- | src/lib/tiff_decoder.h | 1 | ||||
| -rw-r--r-- | src/lib/transcoder.cc | 7 | ||||
| -rw-r--r-- | src/lib/util.h | 2 | ||||
| -rw-r--r-- | src/wx/film_editor.cc | 504 | ||||
| -rw-r--r-- | src/wx/film_editor.h | 27 | ||||
| -rw-r--r-- | src/wx/wx_util.cc | 28 | ||||
| -rw-r--r-- | src/wx/wx_util.h | 4 |
24 files changed, 739 insertions, 575 deletions
diff --git a/src/lib/ab_transcoder.cc b/src/lib/ab_transcoder.cc index c40c0916e..4183749c8 100644 --- a/src/lib/ab_transcoder.cc +++ b/src/lib/ab_transcoder.cc @@ -100,7 +100,7 @@ ABTranscoder::process_video (shared_ptr<Image> yuv, SourceFrame frame, shared_pt void ABTranscoder::go () { - _encoder->process_begin (_da->audio_channel_layout()); + _encoder->process_begin (); _da->process_begin (); _db->process_begin (); diff --git a/src/lib/decoder.cc b/src/lib/decoder.cc index c9d8f063a..2f66aff68 100644 --- a/src/lib/decoder.cc +++ b/src/lib/decoder.cc @@ -43,6 +43,7 @@ using std::min; using std::pair; using std::list; using boost::shared_ptr; +using boost::optional; /** @param f Film. * @param o Options. @@ -73,31 +74,33 @@ Decoder::~Decoder () void Decoder::process_begin () { - _delay_in_frames = _film->audio_delay() * audio_sample_rate() / 1000; - _delay_line = new DelayLine (audio_channels(), _delay_in_frames); + if (_audio_stream) { + _delay_in_frames = _film->audio_delay() * _audio_stream.get().sample_rate() / 1000; + _delay_line = new DelayLine (_audio_stream.get().channels(), _delay_in_frames); + } } /** Finish off a decode processing run */ void Decoder::process_end () { - if (_delay_in_frames < 0 && _opt->decode_audio && audio_channels()) { - shared_ptr<AudioBuffers> b (new AudioBuffers (audio_channels(), -_delay_in_frames)); + if (_delay_in_frames < 0 && _opt->decode_audio && _audio_stream) { + shared_ptr<AudioBuffers> b (new AudioBuffers (_audio_stream.get().channels(), -_delay_in_frames)); b->make_silent (); emit_audio (b); } - if (_opt->decode_audio && audio_channels()) { + if (_opt->decode_audio && _audio_stream) { /* Ensure that our video and audio emissions are the same length */ - int64_t audio_short_by_frames = video_frames_to_audio_frames (_video_frame, audio_sample_rate(), frames_per_second()) - _audio_frame; + int64_t audio_short_by_frames = video_frames_to_audio_frames (_video_frame, _audio_stream.get().sample_rate(), frames_per_second()) - _audio_frame; _film->log()->log ( String::compose ( "Decoder has emitted %1 video frames (which equals %2 audio frames) and %3 audio frames", _video_frame, - video_frames_to_audio_frames (_video_frame, audio_sample_rate(), frames_per_second()), + video_frames_to_audio_frames (_video_frame, _audio_stream.get().sample_rate(), frames_per_second()), _audio_frame ) ); @@ -107,7 +110,7 @@ Decoder::process_end () _film->log()->log (String::compose ("Emitted %1 too many audio frames", -audio_short_by_frames)); /* We have emitted more audio than video. Emit enough black video frames so that we reverse this */ - int const black_video_frames = ceil (-audio_short_by_frames * frames_per_second() / audio_sample_rate()); + int const black_video_frames = ceil (-audio_short_by_frames * frames_per_second() / _audio_stream.get().sample_rate()); _film->log()->log (String::compose ("Emitting %1 frames of black video", black_video_frames)); @@ -118,12 +121,12 @@ Decoder::process_end () } /* Now recompute our check value */ - audio_short_by_frames = video_frames_to_audio_frames (_video_frame, audio_sample_rate(), frames_per_second()) - _audio_frame; + audio_short_by_frames = video_frames_to_audio_frames (_video_frame, _audio_stream.get().sample_rate(), frames_per_second()) - _audio_frame; } if (audio_short_by_frames > 0) { _film->log()->log (String::compose ("Emitted %1 too few audio frames", audio_short_by_frames)); - shared_ptr<AudioBuffers> b (new AudioBuffers (audio_channels(), audio_short_by_frames)); + shared_ptr<AudioBuffers> b (new AudioBuffers (_audio_stream.get().channels(), audio_short_by_frames)); b->make_silent (); emit_audio (b); } @@ -154,77 +157,13 @@ Decoder::go () * @param size Number of bytes of data. */ void -Decoder::process_audio (uint8_t* data, int size) +Decoder::process_audio (shared_ptr<AudioBuffers> audio) { - /* XXX: could this be removed? */ - if (size == 0) { - return; - } - - assert (_film->audio_channels()); - assert (bytes_per_audio_sample()); - - /* Deinterleave and convert to float */ - - assert ((size % (bytes_per_audio_sample() * audio_channels())) == 0); - - int const total_samples = size / bytes_per_audio_sample(); - int const frames = total_samples / _film->audio_channels(); - shared_ptr<AudioBuffers> audio (new AudioBuffers (audio_channels(), frames)); - - switch (audio_sample_format()) { - case AV_SAMPLE_FMT_S16: - { - int16_t* p = (int16_t *) data; - int sample = 0; - int channel = 0; - for (int i = 0; i < total_samples; ++i) { - audio->data(channel)[sample] = float(*p++) / (1 << 15); - - ++channel; - if (channel == _film->audio_channels()) { - channel = 0; - ++sample; - } - } - } - break; - - case AV_SAMPLE_FMT_S32: - { - int32_t* p = (int32_t *) data; - int sample = 0; - int channel = 0; - for (int i = 0; i < total_samples; ++i) { - audio->data(channel)[sample] = float(*p++) / (1 << 31); - - ++channel; - if (channel == _film->audio_channels()) { - channel = 0; - ++sample; - } - } - } - - case AV_SAMPLE_FMT_FLTP: - { - float* p = reinterpret_cast<float*> (data); - for (int i = 0; i < _film->audio_channels(); ++i) { - memcpy (audio->data(i), p, frames * sizeof(float)); - p += frames; - } - } - break; - - default: - assert (false); - } - /* Maybe apply gain */ if (_film->audio_gain() != 0) { float const linear_gain = pow (10, _film->audio_gain() / 20); - for (int i = 0; i < _film->audio_channels(); ++i) { - for (int j = 0; j < frames; ++j) { + for (int i = 0; i < audio->channels(); ++i) { + for (int j = 0; j < audio->frames(); ++j) { audio->data(i)[j] *= linear_gain; } } @@ -308,8 +247,14 @@ Decoder::process_subtitle (shared_ptr<TimedSubtitle> s) } } -int -Decoder::bytes_per_audio_sample () const +void +Decoder::set_audio_stream (optional<AudioStream> s) +{ + _audio_stream = s; +} + +void +Decoder::set_subtitle_stream (optional<SubtitleStream> s) { - return av_get_bytes_per_sample (audio_sample_format ()); + _subtitle_stream = s; } diff --git a/src/lib/decoder.h b/src/lib/decoder.h index 9f47bf425..d0e20b03a 100644 --- a/src/lib/decoder.h +++ b/src/lib/decoder.h @@ -61,14 +61,6 @@ public: virtual float frames_per_second () const = 0; /** @return native size in pixels */ virtual Size native_size () const = 0; - /** @return number of audio channels */ - virtual int audio_channels () const = 0; - /** @return audio sampling rate in Hz */ - virtual int audio_sample_rate () const = 0; - /** @return format of audio samples */ - virtual AVSampleFormat audio_sample_format () const = 0; - virtual int64_t audio_channel_layout () const = 0; - virtual bool has_subtitles () const = 0; virtual int time_base_numerator () const = 0; virtual int time_base_denominator () const = 0; @@ -84,12 +76,23 @@ public: return _video_frame; } - virtual std::vector<AudioStream> audio_streams () const { - return std::vector<AudioStream> (); + virtual void set_audio_stream (boost::optional<AudioStream>); + virtual void set_subtitle_stream (boost::optional<SubtitleStream>); + + boost::optional<AudioStream> audio_stream () const { + return _audio_stream; + } + + boost::optional<SubtitleStream> subtitle_stream () const { + return _subtitle_stream; + } + + std::vector<AudioStream> audio_streams () const { + return _audio_streams; } - virtual std::vector<SubtitleStream> subtitle_streams () const { - return std::vector<SubtitleStream> (); + std::vector<SubtitleStream> subtitle_streams () const { + return _subtitle_streams; } /** Emitted when a video frame is ready. @@ -107,12 +110,10 @@ protected: virtual PixelFormat pixel_format () const = 0; void process_video (AVFrame *); - void process_audio (uint8_t *, int); + void process_audio (boost::shared_ptr<AudioBuffers>); void process_subtitle (boost::shared_ptr<TimedSubtitle>); void repeat_last_video (); - int bytes_per_audio_sample () const; - /** our Film */ boost::shared_ptr<Film> _film; /** our options */ @@ -120,13 +121,19 @@ protected: /** associated Job, or 0 */ Job* _job; + boost::optional<AudioStream> _audio_stream; + boost::optional<SubtitleStream> _subtitle_stream; + + std::vector<AudioStream> _audio_streams; + std::vector<SubtitleStream> _subtitle_streams; + private: void emit_video (boost::shared_ptr<Image>, boost::shared_ptr<Subtitle>); void emit_audio (boost::shared_ptr<AudioBuffers>); SourceFrame _video_frame; int64_t _audio_frame; - + std::list<boost::shared_ptr<FilterGraph> > _filter_graphs; DelayLine* _delay_line; diff --git a/src/lib/encoder.h b/src/lib/encoder.h index 2d9780b52..e04397f78 100644 --- a/src/lib/encoder.h +++ b/src/lib/encoder.h @@ -55,7 +55,7 @@ public: Encoder (boost::shared_ptr<const Film> f, boost::shared_ptr<const Options> o); /** Called to indicate that a processing run is about to begin */ - virtual void process_begin (int64_t audio_channel_layout) = 0; + virtual void process_begin () {} /** Call with a frame of video. * @param i Video frame image. @@ -68,7 +68,7 @@ public: void process_audio (boost::shared_ptr<const AudioBuffers>, int64_t); /** Called when a processing run has finished */ - virtual void process_end () = 0; + virtual void process_end () {} float current_frames_per_second () const; bool skipping () const; diff --git a/src/lib/examine_content_job.cc b/src/lib/examine_content_job.cc index 4646fe8fc..b13e9d9d5 100644 --- a/src/lib/examine_content_job.cc +++ b/src/lib/examine_content_job.cc @@ -59,10 +59,16 @@ void ExamineContentJob::run () { /* Decode the content to get an accurate length */ + + /* We don't want to use any existing length here, as progress + will be messed up. + */ + _film->unset_length (); shared_ptr<Options> o (new Options ("", "", "")); o->out_size = Size (512, 512); o->apply_crop = false; + o->decode_audio = false; descend (0.5); diff --git a/src/lib/ffmpeg_decoder.cc b/src/lib/ffmpeg_decoder.cc index a2ca739e2..1cac25ca1 100644 --- a/src/lib/ffmpeg_decoder.cc +++ b/src/lib/ffmpeg_decoder.cc @@ -54,13 +54,12 @@ using std::string; using std::vector; using std::stringstream; using boost::shared_ptr; +using boost::optional; FFmpegDecoder::FFmpegDecoder (shared_ptr<Film> f, shared_ptr<const Options> o, Job* j) : Decoder (f, o, j) , _format_context (0) , _video_stream (-1) - , _audio_stream (-1) - , _subtitle_stream (-1) , _frame (0) , _video_codec_context (0) , _video_codec (0) @@ -116,28 +115,12 @@ FFmpegDecoder::setup_general () if (s->codec->codec_type == AVMEDIA_TYPE_VIDEO) { _video_stream = i; } else if (s->codec->codec_type == AVMEDIA_TYPE_AUDIO) { - if (_audio_stream == -1) { - _audio_stream = i; - } - _audio_streams.push_back (AudioStream (stream_name (s), i, s->codec->channels)); + _audio_streams.push_back (AudioStream (stream_name (s), i, s->codec->sample_rate, s->codec->channel_layout)); } else if (s->codec->codec_type == AVMEDIA_TYPE_SUBTITLE) { - if (_subtitle_stream == -1) { - _subtitle_stream = i; - } _subtitle_streams.push_back (SubtitleStream (stream_name (s), i)); } } - /* Now override audio and subtitle streams with those from the Film, if it has any */ - - if (_film->audio_stream_index() != -1) { - _audio_stream = _film->audio_stream().id(); - } - - if (_film->subtitle_stream_index() != -1) { - _subtitle_stream = _film->subtitle_stream().id (); - } - if (_video_stream < 0) { throw DecodeError ("could not find video stream"); } @@ -173,11 +156,11 @@ FFmpegDecoder::setup_video () void FFmpegDecoder::setup_audio () { - if (_audio_stream < 0) { + if (!_audio_stream) { return; } - _audio_codec_context = _format_context->streams[_audio_stream]->codec; + _audio_codec_context = _format_context->streams[_audio_stream.get().id()]->codec; _audio_codec = avcodec_find_decoder (_audio_codec_context->codec_id); if (_audio_codec == 0) { @@ -193,18 +176,18 @@ FFmpegDecoder::setup_audio () */ if (_audio_codec_context->channel_layout == 0) { - _audio_codec_context->channel_layout = av_get_default_channel_layout (audio_channels ()); + _audio_codec_context->channel_layout = av_get_default_channel_layout (_audio_stream.get().channels()); } } void FFmpegDecoder::setup_subtitle () { - if (_subtitle_stream < 0) { + if (!_subtitle_stream) { return; } - _subtitle_codec_context = _format_context->streams[_subtitle_stream]->codec; + _subtitle_codec_context = _format_context->streams[_subtitle_stream.get().id()]->codec; _subtitle_codec = avcodec_find_decoder (_subtitle_codec_context->codec_id); if (_subtitle_codec == 0) { @@ -243,14 +226,14 @@ FFmpegDecoder::pass () process_video (_frame); } - if (_audio_stream >= 0 && _opt->decode_audio) { + if (_audio_stream && _opt->decode_audio && _film->use_source_audio()) { while (avcodec_decode_audio4 (_audio_codec_context, _frame, &frame_finished, &_packet) >= 0 && frame_finished) { int const data_size = av_samples_get_buffer_size ( 0, _audio_codec_context->channels, _frame->nb_samples, audio_sample_format (), 1 ); assert (_audio_codec_context->channels == _film->audio_channels()); - process_audio (_frame->data[0], data_size); + process_audio (deinterleave_audio (_frame->data[0], data_size)); } } @@ -307,7 +290,7 @@ FFmpegDecoder::pass () } } - } else if (_audio_stream >= 0 && _packet.stream_index == _audio_stream && _opt->decode_audio) { + } else if (_audio_stream && _packet.stream_index == _audio_stream.get().id() && _opt->decode_audio && _film->use_source_audio()) { int frame_finished; if (avcodec_decode_audio4 (_audio_codec_context, _frame, &frame_finished, &_packet) >= 0 && frame_finished) { @@ -331,25 +314,19 @@ FFmpegDecoder::pass () */ /* frames of silence that we must push */ - int const s = rint ((_first_audio.get() - _first_video.get()) * audio_sample_rate ()); + int const s = rint ((_first_audio.get() - _first_video.get()) * _audio_stream.get().sample_rate ()); _film->log()->log ( String::compose ( "First video at %1, first audio at %2, pushing %3 frames of silence for %4 channels (%5 bytes per sample)", - _first_video.get(), _first_audio.get(), s, audio_channels(), bytes_per_audio_sample() + _first_video.get(), _first_audio.get(), s, _audio_stream.get().channels(), bytes_per_audio_sample() ) ); if (s) { - /* hence bytes */ - int const b = s * audio_channels() * bytes_per_audio_sample(); - - /* XXX: this assumes that it won't be too much, and there are shaky assumptions - that all sound representations are silent with memset()ed zero data. - */ - uint8_t silence[b]; - memset (silence, 0, b); - process_audio (silence, b); + shared_ptr<AudioBuffers> audio (new AudioBuffers (_audio_stream.get().channels(), s)); + audio->make_silent (); + process_audio (audio); } } @@ -358,11 +335,11 @@ FFmpegDecoder::pass () ); assert (_audio_codec_context->channels == _film->audio_channels()); - process_audio (_frame->data[0], data_size); + process_audio (deinterleave_audio (_frame->data[0], data_size)); } } - } else if (_subtitle_stream >= 0 && _packet.stream_index == _subtitle_stream && _opt->decode_subtitles && _first_video) { + } else if (_subtitle_stream && _packet.stream_index == _subtitle_stream.get().id() && _opt->decode_subtitles && _first_video) { int got_subtitle; AVSubtitle sub; @@ -383,36 +360,81 @@ FFmpegDecoder::pass () return false; } -float -FFmpegDecoder::frames_per_second () const +shared_ptr<AudioBuffers> +FFmpegDecoder::deinterleave_audio (uint8_t* data, int size) { - AVStream* s = _format_context->streams[_video_stream]; - - if (s->avg_frame_rate.num && s->avg_frame_rate.den) { - return av_q2d (s->avg_frame_rate); + assert (_film->audio_channels()); + assert (bytes_per_audio_sample()); + + /* Deinterleave and convert to float */ + + assert ((size % (bytes_per_audio_sample() * _audio_stream.get().channels())) == 0); + + int const total_samples = size / bytes_per_audio_sample(); + int const frames = total_samples / _film->audio_channels(); + shared_ptr<AudioBuffers> audio (new AudioBuffers (_audio_stream.get().channels(), frames)); + + switch (audio_sample_format()) { + case AV_SAMPLE_FMT_S16: + { + int16_t* p = (int16_t *) data; + int sample = 0; + int channel = 0; + for (int i = 0; i < total_samples; ++i) { + audio->data(channel)[sample] = float(*p++) / (1 << 15); + + ++channel; + if (channel == _film->audio_channels()) { + channel = 0; + ++sample; + } + } + } + break; + + case AV_SAMPLE_FMT_S32: + { + int32_t* p = (int32_t *) data; + int sample = 0; + int channel = 0; + for (int i = 0; i < total_samples; ++i) { + audio->data(channel)[sample] = float(*p++) / (1 << 31); + + ++channel; + if (channel == _film->audio_channels()) { + channel = 0; + ++sample; + } + } } - return av_q2d (s->r_frame_rate); -} + case AV_SAMPLE_FMT_FLTP: + { + float* p = reinterpret_cast<float*> (data); + for (int i = 0; i < _film->audio_channels(); ++i) { + memcpy (audio->data(i), p, frames * sizeof(float)); + p += frames; + } + } + break; -int -FFmpegDecoder::audio_channels () const -{ - if (_audio_codec_context == 0) { - return 0; + default: + assert (false); } - return _audio_codec_context->channels; + return audio; } -int -FFmpegDecoder::audio_sample_rate () const +float +FFmpegDecoder::frames_per_second () const { - if (_audio_codec_context == 0) { - return 0; + AVStream* s = _format_context->streams[_video_stream]; + + if (s->avg_frame_rate.num && s->avg_frame_rate.den) { + return av_q2d (s->avg_frame_rate); } - - return _audio_codec_context->sample_rate; + + return av_q2d (s->r_frame_rate); } AVSampleFormat @@ -425,16 +447,6 @@ FFmpegDecoder::audio_sample_format () const return _audio_codec_context->sample_fmt; } -int64_t -FFmpegDecoder::audio_channel_layout () const -{ - if (_audio_codec_context == 0) { - return 0; - } - - return _audio_codec_context->channel_layout; -} - Size FFmpegDecoder::native_size () const { @@ -471,24 +483,6 @@ FFmpegDecoder::sample_aspect_ratio_denominator () const return _video_codec_context->sample_aspect_ratio.den; } -bool -FFmpegDecoder::has_subtitles () const -{ - return (_subtitle_stream != -1); -} - -vector<AudioStream> -FFmpegDecoder::audio_streams () const -{ - return _audio_streams; -} - -vector<SubtitleStream> -FFmpegDecoder::subtitle_streams () const -{ - return _subtitle_streams; -} - string FFmpegDecoder::stream_name (AVStream* s) const { @@ -514,3 +508,22 @@ FFmpegDecoder::stream_name (AVStream* s) const return n.str (); } +int +FFmpegDecoder::bytes_per_audio_sample () const +{ + return av_get_bytes_per_sample (audio_sample_format ()); +} + +void +FFmpegDecoder::set_audio_stream (optional<AudioStream> s) +{ + Decoder::set_audio_stream (s); + setup_audio (); +} + +void +FFmpegDecoder::set_subtitle_stream (optional<SubtitleStream> s) +{ + Decoder::set_subtitle_stream (s); + setup_subtitle (); +} diff --git a/src/lib/ffmpeg_decoder.h b/src/lib/ffmpeg_decoder.h index 0265f7478..856ac0801 100644 --- a/src/lib/ffmpeg_decoder.h +++ b/src/lib/ffmpeg_decoder.h @@ -58,14 +58,9 @@ public: /* Methods to query our input video */ float frames_per_second () const; Size native_size () const; - int audio_channels () const; - int audio_sample_rate () const; - AVSampleFormat audio_sample_format () const; - int64_t audio_channel_layout () const; - bool has_subtitles () const; - std::vector<AudioStream> audio_streams () const; - std::vector<SubtitleStream> subtitle_streams () const; + void set_audio_stream (boost::optional<AudioStream>); + void set_subtitle_stream (boost::optional<SubtitleStream>); private: @@ -75,6 +70,8 @@ private: int time_base_denominator () const; int sample_aspect_ratio_numerator () const; int sample_aspect_ratio_denominator () const; + AVSampleFormat audio_sample_format () const; + int bytes_per_audio_sample () const; void setup_general (); void setup_video (); @@ -82,19 +79,15 @@ private: void setup_subtitle (); void maybe_add_subtitle (); + boost::shared_ptr<AudioBuffers> deinterleave_audio (uint8_t* data, int size); std::string stream_name (AVStream* s) const; AVFormatContext* _format_context; int _video_stream; - int _audio_stream; ///< may be < 0 if there is no audio - int _subtitle_stream; ///< may be < 0 if there is no subtitle AVFrame* _frame; - std::vector<AudioStream> _audio_streams; - std::vector<SubtitleStream> _subtitle_streams; - AVCodecContext* _video_codec_context; AVCodec* _video_codec; AVCodecContext* _audio_codec_context; ///< may be 0 if there is no audio diff --git a/src/lib/film.cc b/src/lib/film.cc index d5bf79d09..3ce3a8df3 100644 --- a/src/lib/film.cc +++ b/src/lib/film.cc @@ -69,6 +69,7 @@ using boost::lexical_cast; using boost::to_upper_copy; using boost::ends_with; using boost::starts_with; +using boost::optional; /** Construct a Film object in a given directory, reading any metadata * file that exists in that directory. An exception will be thrown if @@ -86,16 +87,13 @@ Film::Film (string d, bool must_exist) , _dcp_trim_start (0) , _dcp_trim_end (0) , _dcp_ab (false) - , _audio_stream (-1) + , _use_source_audio (true) , _audio_gain (0) , _audio_delay (0) , _still_duration (10) - , _subtitle_stream (-1) , _with_subtitles (false) , _subtitle_offset (0) , _subtitle_scale (1) - , _audio_sample_rate (0) - , _has_subtitles (false) , _frames_per_second (0) , _dirty (false) { @@ -147,7 +145,9 @@ Film::Film (Film const & o) , _dcp_trim_start (o._dcp_trim_start) , _dcp_trim_end (o._dcp_trim_end) , _dcp_ab (o._dcp_ab) + , _use_source_audio (o._use_source_audio) , _audio_stream (o._audio_stream) + , _external_audio (o._external_audio) , _audio_gain (o._audio_gain) , _audio_delay (o._audio_delay) , _still_duration (o._still_duration) @@ -165,9 +165,7 @@ Film::Film (Film const & o) , _thumbs (o._thumbs) , _size (o._size) , _length (o._length) - , _audio_sample_rate (o._audio_sample_rate) , _content_digest (o._content_digest) - , _has_subtitles (o._has_subtitles) , _audio_streams (o._audio_streams) , _subtitle_streams (o._subtitle_streams) , _frames_per_second (o._frames_per_second) @@ -260,10 +258,12 @@ Film::make_dcp (bool transcode) o->ratio = format()->ratio_as_float (shared_from_this ()); if (dcp_length ()) { o->video_decode_range = make_pair (dcp_trim_start(), dcp_trim_start() + dcp_length().get()); - o->audio_decode_range = make_pair ( - video_frames_to_audio_frames (o->video_decode_range.get().first, audio_sample_rate(), frames_per_second()), - video_frames_to_audio_frames (o->video_decode_range.get().second, audio_sample_rate(), frames_per_second()) - ); + if (audio_stream()) { + o->audio_decode_range = make_pair ( + video_frames_to_audio_frames (o->video_decode_range.get().first, audio_stream().get().sample_rate(), frames_per_second()), + video_frames_to_audio_frames (o->video_decode_range.get().second, audio_stream().get().sample_rate(), frames_per_second()) + ); + } } o->decode_subtitles = with_subtitles (); @@ -418,11 +418,19 @@ Film::write_metadata () const f << "dcp_trim_start " << _dcp_trim_start << "\n"; f << "dcp_trim_end " << _dcp_trim_end << "\n"; f << "dcp_ab " << (_dcp_ab ? "1" : "0") << "\n"; - f << "selected_audio_stream " << _audio_stream << "\n"; + f << "use_source_audio " << (_use_source_audio ? "1" : "0") << "\n"; + if (_audio_stream) { + f << "selected_audio_stream " << _audio_stream.get().to_string() << "\n"; + } + for (vector<string>::const_iterator i = _external_audio.begin(); i != _external_audio.end(); ++i) { + f << "external_audio " << *i << "\n"; + } f << "audio_gain " << _audio_gain << "\n"; f << "audio_delay " << _audio_delay << "\n"; f << "still_duration " << _still_duration << "\n"; - f << "selected_subtitle_stream " << _subtitle_stream << "\n"; + if (_subtitle_stream) { + f << "selected_subtitle_stream " << _subtitle_stream.get().to_string() << "\n"; + } f << "with_subtitles " << _with_subtitles << "\n"; f << "subtitle_offset " << _subtitle_offset << "\n"; f << "subtitle_scale " << _subtitle_scale << "\n"; @@ -443,9 +451,7 @@ Film::write_metadata () const f << "width " << _size.width << "\n"; f << "height " << _size.height << "\n"; f << "length " << _length.get_value_or(0) << "\n"; - f << "audio_sample_rate " << _audio_sample_rate << "\n"; f << "content_digest " << _content_digest << "\n"; - f << "has_subtitles " << _has_subtitles << "\n"; for (vector<AudioStream>::const_iterator i = _audio_streams.begin(); i != _audio_streams.end(); ++i) { f << "audio_stream " << i->to_string () << "\n"; @@ -465,6 +471,11 @@ void Film::read_metadata () { boost::mutex::scoped_lock lm (_state_mutex); + + _external_audio.clear (); + _thumbs.clear (); + _audio_streams.clear (); + _subtitle_streams.clear (); ifstream f (file ("metadata").c_str()); multimap<string, string> kv = read_key_value (f); @@ -501,8 +512,12 @@ Film::read_metadata () _dcp_trim_end = atoi (v.c_str ()); } else if (k == "dcp_ab") { _dcp_ab = (v == "1"); + } else if (k == "use_source_audio") { + _use_source_audio = (v == "1"); } else if (k == "selected_audio_stream") { - _audio_stream = atoi (v.c_str ()); + _audio_stream = AudioStream (v); + } else if (k == "external_audio") { + _external_audio.push_back (v); } else if (k == "audio_gain") { _audio_gain = atof (v.c_str ()); } else if (k == "audio_delay") { @@ -510,7 +525,7 @@ Film::read_metadata () } else if (k == "still_duration") { _still_duration = atoi (v.c_str ()); } else if (k == "selected_subtitle_stream") { - _subtitle_stream = atoi (v.c_str ()); + _subtitle_stream = SubtitleStream (v); } else if (k == "with_subtitles") { _with_subtitles = (v == "1"); } else if (k == "subtitle_offset") { @@ -549,12 +564,8 @@ Film::read_metadata () if (vv) { _length = vv; } - } else if (k == "audio_sample_rate") { - _audio_sample_rate = atoi (v.c_str ()); } else if (k == "content_digest") { _content_digest = v; - } else if (k == "has_subtitles") { - _has_subtitles = (v == "1"); } else if (k == "audio_stream") { _audio_streams.push_back (AudioStream (v)); } else if (k == "subtitle_stream") { @@ -695,7 +706,7 @@ int Film::target_audio_sample_rate () const { /* Resample to a DCI-approved sample rate */ - double t = dcp_audio_sample_rate (audio_sample_rate()); + double t = dcp_audio_sample_rate (audio_stream().get().sample_rate()); DCPFrameRate dfr = dcp_frame_rate (frames_per_second ()); @@ -723,11 +734,9 @@ Film::dcp_length () const string Film::dci_name () const { - boost::mutex::scoped_lock lm (_state_mutex); - stringstream d; - string fixed_name = to_upper_copy (_name); + string fixed_name = to_upper_copy (name()); for (size_t i = 0; i < fixed_name.length(); ++i) { if (fixed_name[i] == ' ') { fixed_name[i] = '-'; @@ -741,18 +750,18 @@ Film::dci_name () const d << fixed_name << "_"; - if (_dcp_content_type) { - d << _dcp_content_type->dci_name() << "_"; + if (dcp_content_type()) { + d << dcp_content_type()->dci_name() << "_"; } - if (_format) { - d << _format->dci_name() << "_"; + if (format()) { + d << format()->dci_name() << "_"; } - if (!_audio_language.empty ()) { - d << _audio_language; - if (!_subtitle_language.empty() && _with_subtitles) { - d << "-" << _subtitle_language; + if (!audio_language().empty ()) { + d << audio_language(); + if (!subtitle_language().empty() && with_subtitles()) { + d << "-" << subtitle_language(); } else { d << "-XX"; } @@ -760,45 +769,43 @@ Film::dci_name () const d << "_"; } - if (!_territory.empty ()) { - d << _territory; - if (!_rating.empty ()) { - d << "-" << _rating; + if (!territory().empty ()) { + d << territory(); + if (!rating().empty ()) { + d << "-" << rating(); } d << "_"; } - if (_audio_stream != -1) { - switch (_audio_streams[_audio_stream].channels()) { - case 1: - d << "10_"; - break; - case 2: - d << "20_"; - break; - case 6: - d << "51_"; - break; - case 8: - d << "71_"; - break; - } + switch (audio_channels()) { + case 1: + d << "10_"; + break; + case 2: + d << "20_"; + break; + case 6: + d << "51_"; + break; + case 8: + d << "71_"; + break; } d << "2K_"; - if (!_studio.empty ()) { - d << _studio << "_"; + if (!studio().empty ()) { + d << studio() << "_"; } d << boost::gregorian::to_iso_string (_dci_date) << "_"; - if (!_facility.empty ()) { - d << _facility << "_"; + if (!facility().empty ()) { + d << facility() << "_"; } - if (!_package_type.empty ()) { - d << _package_type; + if (!package_type().empty ()) { + d << package_type(); } return d.str (); @@ -880,6 +887,9 @@ Film::set_content (string c) _content = c; } + _audio_stream = optional<AudioStream> (); + _subtitle_stream = optional<SubtitleStream> (); + /* Create a temporary decoder so that we can get information about the content. */ @@ -892,12 +902,17 @@ Film::set_content (string c) set_size (d->native_size ()); set_frames_per_second (d->frames_per_second ()); - set_audio_sample_rate (d->audio_sample_rate ()); - set_has_subtitles (d->has_subtitles ()); set_audio_streams (d->audio_streams ()); set_subtitle_streams (d->subtitle_streams ()); - set_audio_stream (audio_streams().empty() ? -1 : 0); - set_subtitle_stream (subtitle_streams().empty() ? -1 : 0); + + /* Start off with the first audio and subtitle streams */ + if (!d->audio_streams().empty()) { + set_audio_stream (d->audio_streams().front()); + } + + if (!d->subtitle_streams().empty()) { + set_subtitle_stream (d->subtitle_streams().front()); + } { boost::mutex::scoped_lock lm (_state_mutex); @@ -1057,7 +1072,17 @@ Film::set_dcp_ab (bool a) } void -Film::set_audio_stream (int s) +Film::set_use_source_audio (bool s) +{ + { + boost::mutex::scoped_lock lm (_state_mutex); + _use_source_audio = s; + } + signal_changed (USE_SOURCE_AUDIO); +} + +void +Film::set_audio_stream (optional<AudioStream> s) { { boost::mutex::scoped_lock lm (_state_mutex); @@ -1067,6 +1092,16 @@ Film::set_audio_stream (int s) } void +Film::set_external_audio (vector<string> a) +{ + { + boost::mutex::scoped_lock lm (_state_mutex); + _external_audio = a; + } + signal_changed (EXTERNAL_AUDIO); +} + +void Film::set_audio_gain (float g) { { @@ -1097,7 +1132,7 @@ Film::set_still_duration (int d) } void -Film::set_subtitle_stream (int s) +Film::set_subtitle_stream (optional<SubtitleStream> s) { { boost::mutex::scoped_lock lm (_state_mutex); @@ -1247,16 +1282,6 @@ Film::unset_length () } void -Film::set_audio_sample_rate (int r) -{ - { - boost::mutex::scoped_lock lm (_state_mutex); - _audio_sample_rate = r; - } - signal_changed (AUDIO_SAMPLE_RATE); -} - -void Film::set_content_digest (string d) { { @@ -1267,16 +1292,6 @@ Film::set_content_digest (string d) } void -Film::set_has_subtitles (bool s) -{ - { - boost::mutex::scoped_lock lm (_state_mutex); - _has_subtitles = s; - } - signal_changed (HAS_SUBTITLES); -} - -void Film::set_audio_streams (vector<AudioStream> s) { { @@ -1323,11 +1338,23 @@ int Film::audio_channels () const { boost::mutex::scoped_lock lm (_state_mutex); - if (_audio_stream == -1) { - return 0; - } - return _audio_streams[_audio_stream].channels (); + if (_use_source_audio) { + if (_audio_stream) { + return _audio_stream.get().channels (); + } + } else { + int last_filled = -1; + for (size_t i = 0; i < _external_audio.size(); ++i) { + if (!_external_audio[i].empty()) { + last_filled = i; + } + } + + return last_filled + 1; + } + + return 0; } void diff --git a/src/lib/film.h b/src/lib/film.h index 049af45e2..af1f49e2e 100644 --- a/src/lib/film.h +++ b/src/lib/film.h @@ -120,7 +120,9 @@ public: DCP_TRIM_START, DCP_TRIM_END, DCP_AB, + USE_SOURCE_AUDIO, AUDIO_STREAM, + EXTERNAL_AUDIO, AUDIO_GAIN, AUDIO_DELAY, STILL_DURATION, @@ -132,8 +134,6 @@ public: THUMBS, SIZE, LENGTH, - AUDIO_SAMPLE_RATE, - HAS_SUBTITLES, AUDIO_STREAMS, SUBTITLE_STREAMS, FRAMES_PER_SECOND, @@ -202,15 +202,19 @@ public: return _dcp_ab; } - int audio_stream_index () const { + bool use_source_audio () const { + boost::mutex::scoped_lock lm (_state_mutex); + return _use_source_audio; + } + + boost::optional<AudioStream> audio_stream () const { boost::mutex::scoped_lock lm (_state_mutex); return _audio_stream; } - AudioStream audio_stream () const { + std::vector<std::string> external_audio () const { boost::mutex::scoped_lock lm (_state_mutex); - assert (_audio_stream < int (_audio_streams.size())); - return _audio_streams[_audio_stream]; + return _external_audio; } float audio_gain () const { @@ -228,17 +232,11 @@ public: return _still_duration; } - int subtitle_stream_index () const { + boost::optional<SubtitleStream> subtitle_stream () const { boost::mutex::scoped_lock lm (_state_mutex); return _subtitle_stream; } - SubtitleStream subtitle_stream () const { - boost::mutex::scoped_lock lm (_state_mutex); - assert (_subtitle_stream < int (_subtitle_streams.size())); - return _subtitle_streams[_subtitle_stream]; - } - bool with_subtitles () const { boost::mutex::scoped_lock lm (_state_mutex); return _with_subtitles; @@ -304,21 +302,11 @@ public: return _length; } - int audio_sample_rate () const { - boost::mutex::scoped_lock lm (_state_mutex); - return _audio_sample_rate; - } - std::string content_digest () const { boost::mutex::scoped_lock lm (_state_mutex); return _content_digest; } - bool has_subtitles () const { - boost::mutex::scoped_lock lm (_state_mutex); - return _has_subtitles; - } - std::vector<AudioStream> audio_streams () const { boost::mutex::scoped_lock lm (_state_mutex); return _audio_streams; @@ -353,11 +341,13 @@ public: void set_dcp_trim_start (int); void set_dcp_trim_end (int); void set_dcp_ab (bool); - void set_audio_stream (int); + void set_use_source_audio (bool); + void set_audio_stream (boost::optional<AudioStream>); + void set_external_audio (std::vector<std::string>); void set_audio_gain (float); void set_audio_delay (int); void set_still_duration (int); - void set_subtitle_stream (int); + void set_subtitle_stream (boost::optional<SubtitleStream>); void set_with_subtitles (bool); void set_subtitle_offset (int); void set_subtitle_scale (float); @@ -372,9 +362,7 @@ public: void set_size (Size); void set_length (SourceFrame); void unset_length (); - void set_audio_sample_rate (int); void set_content_digest (std::string); - void set_has_subtitles (bool); void set_audio_streams (std::vector<AudioStream>); void set_subtitle_streams (std::vector<SubtitleStream>); void set_frames_per_second (float); @@ -432,16 +420,16 @@ private: has the specified filters and post-processing. */ bool _dcp_ab; - /** An index into our _audio_streams vector for the stream to use for audio, or -1 if there is none */ - int _audio_stream; + bool _use_source_audio; + boost::optional<AudioStream> _audio_stream; + std::vector<std::string> _external_audio; /** Gain to apply to audio in dB */ float _audio_gain; /** Delay to apply to audio (positive moves audio later) in milliseconds */ int _audio_delay; /** Duration to make still-sourced films (in seconds) */ int _still_duration; - /** An index into our _subtitle_streams vector for the stream to use for subtitles, or -1 if there is none */ - int _subtitle_stream; + boost::optional<SubtitleStream> _subtitle_stream; /** True if subtitles should be shown for this film */ bool _with_subtitles; /** y offset for placing subtitles, in source pixels; +ve is further down @@ -468,12 +456,8 @@ private: Size _size; /** Actual length of the source (in video frames) from examining it */ boost::optional<SourceFrame> _length; - /** Sample rate of the source audio, in Hz */ - int _audio_sample_rate; /** MD5 digest of our content file */ std::string _content_digest; - /** true if the source has subtitles */ - bool _has_subtitles; /** the audio streams that the source has */ std::vector<AudioStream> _audio_streams; /** the subtitle streams that the source has */ diff --git a/src/lib/imagemagick_decoder.h b/src/lib/imagemagick_decoder.h index b7ab9af18..1bf50378b 100644 --- a/src/lib/imagemagick_decoder.h +++ b/src/lib/imagemagick_decoder.h @@ -42,10 +42,6 @@ public: return 0; } - AVSampleFormat audio_sample_format () const { - return AV_SAMPLE_FMT_NONE; - } - int64_t audio_channel_layout () const { return 0; } diff --git a/src/lib/imagemagick_encoder.h b/src/lib/imagemagick_encoder.h index 9adfbf56b..02458fc5b 100644 --- a/src/lib/imagemagick_encoder.h +++ b/src/lib/imagemagick_encoder.h @@ -36,9 +36,6 @@ class ImageMagickEncoder : public Encoder public: ImageMagickEncoder (boost::shared_ptr<const Film> f, boost::shared_ptr<const Options> o); - void process_begin (int64_t audio_channel_layout) {} - void process_end () {} - private: void do_process_video (boost::shared_ptr<const Image>, SourceFrame, boost::shared_ptr<Subtitle>); void do_process_audio (boost::shared_ptr<const AudioBuffers>) {} diff --git a/src/lib/j2k_still_encoder.h b/src/lib/j2k_still_encoder.h index bc324e967..65cfa7cac 100644 --- a/src/lib/j2k_still_encoder.h +++ b/src/lib/j2k_still_encoder.h @@ -36,9 +36,6 @@ class J2KStillEncoder : public Encoder public: J2KStillEncoder (boost::shared_ptr<const Film>, boost::shared_ptr<const Options>); - void process_begin (int64_t audio_channel_layout) {} - void process_end () {} - private: void do_process_video (boost::shared_ptr<const Image>, SourceFrame, boost::shared_ptr<Subtitle>); void do_process_audio (boost::shared_ptr<const AudioBuffers>) {} diff --git a/src/lib/j2k_wav_encoder.cc b/src/lib/j2k_wav_encoder.cc index 6d777babb..108320d32 100644 --- a/src/lib/j2k_wav_encoder.cc +++ b/src/lib/j2k_wav_encoder.cc @@ -58,20 +58,22 @@ J2KWAVEncoder::J2KWAVEncoder (shared_ptr<const Film> f, shared_ptr<const Options , _audio_frames_written (0) , _process_end (false) { - /* Create sound output files with .tmp suffixes; we will rename - them if and when we complete. - */ - for (int i = 0; i < _film->audio_channels(); ++i) { - SF_INFO sf_info; - sf_info.samplerate = dcp_audio_sample_rate (_film->audio_sample_rate()); - /* We write mono files */ - sf_info.channels = 1; - sf_info.format = SF_FORMAT_WAV | SF_FORMAT_PCM_24; - SNDFILE* f = sf_open (_opt->multichannel_audio_out_path (i, true).c_str (), SFM_WRITE, &sf_info); - if (f == 0) { - throw CreateFileError (_opt->multichannel_audio_out_path (i, true)); + if (_film->audio_stream()) { + /* Create sound output files with .tmp suffixes; we will rename + them if and when we complete. + */ + for (int i = 0; i < _film->audio_channels(); ++i) { + SF_INFO sf_info; + sf_info.samplerate = dcp_audio_sample_rate (_film->audio_stream().get().sample_rate()); + /* We write mono files */ + sf_info.channels = 1; + sf_info.format = SF_FORMAT_WAV | SF_FORMAT_PCM_24; + SNDFILE* f = sf_open (_opt->multichannel_audio_out_path (i, true).c_str (), SFM_WRITE, &sf_info); + if (f == 0) { + throw CreateFileError (_opt->multichannel_audio_out_path (i, true)); + } + _sound_files.push_back (f); } - _sound_files.push_back (f); } } @@ -225,24 +227,24 @@ J2KWAVEncoder::encoder_thread (ServerDescription* server) } void -J2KWAVEncoder::process_begin (int64_t audio_channel_layout) +J2KWAVEncoder::process_begin () { - if (_film->audio_sample_rate() != _film->target_audio_sample_rate()) { + if (_film->audio_stream() && _film->audio_stream().get().sample_rate() != _film->target_audio_sample_rate()) { #ifdef HAVE_SWRESAMPLE stringstream s; - s << "Will resample audio from " << _film->audio_sample_rate() << " to " << _film->target_audio_sample_rate(); + s << "Will resample audio from " << _film->audio_stream().get().sample_rate() << " to " << _film->target_audio_sample_rate(); _film->log()->log (s.str ()); /* We will be using planar float data when we call the resampler */ _swr_context = swr_alloc_set_opts ( 0, - audio_channel_layout, + _film->audio_stream().get().channel_layout(), AV_SAMPLE_FMT_FLTP, _film->target_audio_sample_rate(), - audio_channel_layout, + _film->audio_stream().get().channel_layout(), AV_SAMPLE_FMT_FLTP, - _film->audio_sample_rate(), + _film->audio_stream().get().sample_rate(), 0, 0 ); @@ -310,9 +312,9 @@ J2KWAVEncoder::process_end () } #if HAVE_SWRESAMPLE - if (_swr_context) { + if (_film->audio_stream() && _swr_context) { - shared_ptr<AudioBuffers> out (new AudioBuffers (_film->audio_channels(), 256)); + shared_ptr<AudioBuffers> out (new AudioBuffers (_film->audio_stream().get().channels(), 256)); while (1) { int const frames = swr_convert (_swr_context, (uint8_t **) out->data(), 256, 0, 0); @@ -333,20 +335,22 @@ J2KWAVEncoder::process_end () } #endif - int const dcp_sr = dcp_audio_sample_rate (_film->audio_sample_rate ()); - int64_t const extra_audio_frames = dcp_sr - (_audio_frames_written % dcp_sr); - shared_ptr<AudioBuffers> silence (new AudioBuffers (_film->audio_channels(), extra_audio_frames)); - silence->make_silent (); - write_audio (silence); - - close_sound_files (); - - /* Rename .wav.tmp files to .wav */ - for (int i = 0; i < _film->audio_channels(); ++i) { - if (boost::filesystem::exists (_opt->multichannel_audio_out_path (i, false))) { - boost::filesystem::remove (_opt->multichannel_audio_out_path (i, false)); + if (_film->audio_stream()) { + int const dcp_sr = dcp_audio_sample_rate (_film->audio_stream().get().sample_rate ()); + int64_t const extra_audio_frames = dcp_sr - (_audio_frames_written % dcp_sr); + shared_ptr<AudioBuffers> silence (new AudioBuffers (_film->audio_stream().get().channels(), extra_audio_frames)); + silence->make_silent (); + write_audio (silence); + + close_sound_files (); + + /* Rename .wav.tmp files to .wav */ + for (int i = 0; i < _film->audio_channels(); ++i) { + if (boost::filesystem::exists (_opt->multichannel_audio_out_path (i, false))) { + boost::filesystem::remove (_opt->multichannel_audio_out_path (i, false)); + } + boost::filesystem::rename (_opt->multichannel_audio_out_path (i, true), _opt->multichannel_audio_out_path (i, false)); } - boost::filesystem::rename (_opt->multichannel_audio_out_path (i, true), _opt->multichannel_audio_out_path (i, false)); } } @@ -360,9 +364,9 @@ J2KWAVEncoder::do_process_audio (shared_ptr<const AudioBuffers> audio) if (_swr_context) { /* Compute the resampled frames count and add 32 for luck */ - int const max_resampled_frames = ceil (audio->frames() * _film->target_audio_sample_rate() / _film->audio_sample_rate()) + 32; + int const max_resampled_frames = ceil (audio->frames() * _film->target_audio_sample_rate() / _film->audio_stream().get().sample_rate()) + 32; - resampled.reset (new AudioBuffers (_film->audio_channels(), max_resampled_frames)); + resampled.reset (new AudioBuffers (_film->audio_stream().get().channels(), max_resampled_frames)); /* Resample audio */ int const resampled_frames = swr_convert ( diff --git a/src/lib/j2k_wav_encoder.h b/src/lib/j2k_wav_encoder.h index b494be8e5..5cf508cff 100644 --- a/src/lib/j2k_wav_encoder.h +++ b/src/lib/j2k_wav_encoder.h @@ -50,7 +50,7 @@ public: J2KWAVEncoder (boost::shared_ptr<const Film>, boost::shared_ptr<const Options>); ~J2KWAVEncoder (); - void process_begin (int64_t audio_channel_layout); + void process_begin (); void process_end (); private: diff --git a/src/lib/stream.cc b/src/lib/stream.cc index d1c2b5a9e..9d10813f7 100644 --- a/src/lib/stream.cc +++ b/src/lib/stream.cc @@ -26,7 +26,7 @@ using namespace std; AudioStream::AudioStream (string t) { stringstream n (t); - n >> _id >> _channels; + n >> _id >> _sample_rate >> _channel_layout; for (int i = 0; i < 2; ++i) { size_t const s = t.find (' '); @@ -41,7 +41,7 @@ AudioStream::AudioStream (string t) string AudioStream::to_string () const { - return String::compose ("%1 %2 %3", _id, _channels, _name); + return String::compose ("%1 %2 %3 %4", _id, _sample_rate, _channel_layout, _name); } SubtitleStream::SubtitleStream (string t) diff --git a/src/lib/stream.h b/src/lib/stream.h index 2db63c620..d6c4ca382 100644 --- a/src/lib/stream.h +++ b/src/lib/stream.h @@ -20,6 +20,11 @@ #ifndef DVDOMATIC_STREAM_H #define DVDOMATIC_STREAM_H +#include <stdint.h> +extern "C" { +#include <libavutil/audioconvert.h> +} + class Stream { public: @@ -52,19 +57,29 @@ struct AudioStream : public Stream public: AudioStream (std::string t); - AudioStream (std::string n, int i, int c) - : Stream (n, i) - , _channels (c) + AudioStream (std::string n, int id, int r, int64_t l) + : Stream (n, id) + , _sample_rate (r) + , _channel_layout (l) {} std::string to_string () const; int channels () const { - return _channels; + return av_get_channel_layout_nb_channels (_channel_layout); + } + + int sample_rate () const { + return _sample_rate; + } + + int64_t channel_layout () const { + return _channel_layout; } private: - int _channels; + int _sample_rate; + int64_t _channel_layout; }; class SubtitleStream : public Stream diff --git a/src/lib/tiff_decoder.cc b/src/lib/tiff_decoder.cc index 7cca511dd..c92e080d7 100644 --- a/src/lib/tiff_decoder.cc +++ b/src/lib/tiff_decoder.cc @@ -114,13 +114,6 @@ TIFFDecoder::audio_sample_rate () const return 0; } -AVSampleFormat -TIFFDecoder::audio_sample_format () const -{ - return AV_SAMPLE_FMT_NONE; -} - - int64_t TIFFDecoder::audio_channel_layout () const { diff --git a/src/lib/tiff_decoder.h b/src/lib/tiff_decoder.h index 1c33443cf..e6821eec6 100644 --- a/src/lib/tiff_decoder.h +++ b/src/lib/tiff_decoder.h @@ -49,7 +49,6 @@ public: Size native_size () const; int audio_channels () const; int audio_sample_rate () const; - AVSampleFormat audio_sample_format () const; int64_t audio_channel_layout () const; bool has_subtitles () const { return false; diff --git a/src/lib/transcoder.cc b/src/lib/transcoder.cc index 66d5606af..ac908768e 100644 --- a/src/lib/transcoder.cc +++ b/src/lib/transcoder.cc @@ -29,6 +29,7 @@ #include "transcoder.h" #include "encoder.h" #include "decoder_factory.h" +#include "film.h" using std::string; using boost::shared_ptr; @@ -45,6 +46,10 @@ Transcoder::Transcoder (shared_ptr<Film> f, shared_ptr<const Options> o, Job* j, , _decoder (decoder_factory (f, o, j)) { assert (_encoder); + + /* Set up the decoder to use the film's set streams */ + _decoder->set_audio_stream (f->audio_stream ()); + _decoder->set_subtitle_stream (f->subtitle_stream ()); _decoder->Video.connect (bind (&Encoder::process_video, e, _1, _2, _3)); _decoder->Audio.connect (bind (&Encoder::process_audio, e, _1, _2)); @@ -56,7 +61,7 @@ Transcoder::Transcoder (shared_ptr<Film> f, shared_ptr<const Options> o, Job* j, void Transcoder::go () { - _encoder->process_begin (_decoder->audio_channel_layout()); + _encoder->process_begin (); try { _decoder->go (); } catch (...) { diff --git a/src/lib/util.h b/src/lib/util.h index 7aa9f25e1..577b9ba1b 100644 --- a/src/lib/util.h +++ b/src/lib/util.h @@ -41,6 +41,8 @@ extern "C" { #define TIMING(...) #endif +#define MAX_AUDIO_CHANNELS 6 + class Scaler; extern std::string seconds_to_hms (int); diff --git a/src/wx/film_editor.cc b/src/wx/film_editor.cc index 703059780..e49bf6272 100644 --- a/src/wx/film_editor.cc +++ b/src/wx/film_editor.cc @@ -24,6 +24,7 @@ #include <iostream> #include <iomanip> #include <wx/wx.h> +#include <wx/notebook.h> #include <boost/thread.hpp> #include <boost/filesystem.hpp> #include <boost/lexical_cast.hpp> @@ -45,6 +46,7 @@ #include "scaler.h" using std::string; +using std::cout; using std::stringstream; using std::pair; using std::fixed; @@ -59,225 +61,300 @@ FilmEditor::FilmEditor (shared_ptr<Film> f, wxWindow* parent) , _film (f) , _generally_sensitive (true) { - _sizer = new wxFlexGridSizer (2, 4, 4); - SetSizer (_sizer); + wxSizer* s = new wxBoxSizer (wxVERTICAL); + SetSizer (s); + _notebook = new wxNotebook (this, wxID_ANY); + s->Add (_notebook, 1); + + make_film_panel (); + _notebook->AddPage (_film_panel, _("Film"), true); + make_video_panel (); + _notebook->AddPage (_video_panel, _("Video"), false); + make_audio_panel (); + _notebook->AddPage (_audio_panel, _("Audio"), false); + make_subtitle_panel (); + _notebook->AddPage (_subtitle_panel, _("Subtitles"), false); + + set_film (_film); + connect_to_widgets (); + + JobManager::instance()->ActiveJobsChanged.connect ( + bind (&FilmEditor::active_jobs_changed, this, _1) + ); + + setup_visibility (); + setup_formats (); +} + +void +FilmEditor::make_film_panel () +{ + _film_panel = new wxPanel (_notebook); + _film_sizer = new wxFlexGridSizer (2, 4, 4); + _film_panel->SetSizer (_film_sizer); + + add_label_to_sizer (_film_sizer, _film_panel, "Name"); + _name = new wxTextCtrl (_film_panel, wxID_ANY); + _film_sizer->Add (_name, 1, wxEXPAND); + + add_label_to_sizer (_film_sizer, _film_panel, "DCP Name"); + _dcp_name = new wxStaticText (_film_panel, wxID_ANY, wxT ("")); + _film_sizer->Add (_dcp_name, 0, wxALIGN_CENTER_VERTICAL | wxSHRINK); + + _use_dci_name = new wxCheckBox (_film_panel, wxID_ANY, wxT ("Use DCI name")); + _film_sizer->Add (_use_dci_name, 1, wxEXPAND); + _edit_dci_button = new wxButton (_film_panel, wxID_ANY, wxT ("Details...")); + _film_sizer->Add (_edit_dci_button, 0); + + add_label_to_sizer (_film_sizer, _film_panel, "Content"); + _content = new wxFilePickerCtrl (_film_panel, wxID_ANY, wxT (""), wxT ("Select Content File"), wxT("*.*")); + _film_sizer->Add (_content, 1, wxEXPAND); + + add_label_to_sizer (_film_sizer, _film_panel, "Content Type"); + _dcp_content_type = new wxComboBox (_film_panel, wxID_ANY); + _film_sizer->Add (_dcp_content_type); + + video_control (add_label_to_sizer (_film_sizer, _film_panel, "Frames Per Second")); + _frames_per_second = new wxStaticText (_film_panel, wxID_ANY, wxT ("")); + _film_sizer->Add (video_control (_frames_per_second), 1, wxALIGN_CENTER_VERTICAL); + + video_control (add_label_to_sizer (_film_sizer, _film_panel, "Original Size")); + _original_size = new wxStaticText (_film_panel, wxID_ANY, wxT ("")); + _film_sizer->Add (video_control (_original_size), 1, wxALIGN_CENTER_VERTICAL); + + video_control (add_label_to_sizer (_film_sizer, _film_panel, "Length")); + _length = new wxStaticText (_film_panel, wxID_ANY, wxT ("")); + _film_sizer->Add (video_control (_length), 1, wxALIGN_CENTER_VERTICAL); - add_label_to_sizer (_sizer, this, "Name"); - _name = new wxTextCtrl (this, wxID_ANY); - _sizer->Add (_name, 1, wxEXPAND); - add_label_to_sizer (_sizer, this, "DCP Name"); - _dcp_name = new wxStaticText (this, wxID_ANY, wxT ("")); - _sizer->Add (_dcp_name, 0, wxALIGN_CENTER_VERTICAL | wxSHRINK); + { + video_control (add_label_to_sizer (_film_sizer, _film_panel, "Trim frames")); + wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL); + add_label_to_sizer (s, _film_panel, "Start"); + _dcp_trim_start = new wxSpinCtrl (_film_panel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize (64, -1)); + s->Add (_dcp_trim_start); + add_label_to_sizer (s, _film_panel, "End"); + _dcp_trim_end = new wxSpinCtrl (_film_panel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize (64, -1)); + s->Add (_dcp_trim_end); - _use_dci_name = new wxCheckBox (this, wxID_ANY, wxT ("Use DCI name")); - _sizer->Add (_use_dci_name, 1, wxEXPAND); - _edit_dci_button = new wxButton (this, wxID_ANY, wxT ("Details...")); - _sizer->Add (_edit_dci_button, 0); + _film_sizer->Add (s); + } - add_label_to_sizer (_sizer, this, "Content"); - _content = new wxFilePickerCtrl (this, wxID_ANY, wxT (""), wxT ("Select Content File"), wxT("*.*")); - _sizer->Add (_content, 1, wxEXPAND); + _dcp_ab = new wxCheckBox (_film_panel, wxID_ANY, wxT ("A/B")); + video_control (_dcp_ab); + _film_sizer->Add (_dcp_ab, 1); + _film_sizer->AddSpacer (0); + + /* STILL-only stuff */ + { + still_control (add_label_to_sizer (_film_sizer, _film_panel, "Duration")); + wxSizer* s = new wxBoxSizer (wxHORIZONTAL); + _still_duration = new wxSpinCtrl (_film_panel); + still_control (_still_duration); + s->Add (_still_duration, 1, wxEXPAND); + still_control (add_label_to_sizer (s, _film_panel, "s")); + _film_sizer->Add (s); + } + + vector<DCPContentType const *> const ct = DCPContentType::all (); + for (vector<DCPContentType const *>::const_iterator i = ct.begin(); i != ct.end(); ++i) { + _dcp_content_type->Append (std_to_wx ((*i)->pretty_name ())); + } +} + +void +FilmEditor::connect_to_widgets () +{ + _name->Connect (wxID_ANY, wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler (FilmEditor::name_changed), 0, this); + _use_dci_name->Connect (wxID_ANY, wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler (FilmEditor::use_dci_name_toggled), 0, this); + _edit_dci_button->Connect (wxID_ANY, wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler (FilmEditor::edit_dci_button_clicked), 0, this); + _format->Connect (wxID_ANY, wxEVT_COMMAND_COMBOBOX_SELECTED, wxCommandEventHandler (FilmEditor::format_changed), 0, this); + _content->Connect (wxID_ANY, wxEVT_COMMAND_FILEPICKER_CHANGED, wxCommandEventHandler (FilmEditor::content_changed), 0, this); + _left_crop->Connect (wxID_ANY, wxEVT_COMMAND_SPINCTRL_UPDATED, wxCommandEventHandler (FilmEditor::left_crop_changed), 0, this); + _right_crop->Connect (wxID_ANY, wxEVT_COMMAND_SPINCTRL_UPDATED, wxCommandEventHandler (FilmEditor::right_crop_changed), 0, this); + _top_crop->Connect (wxID_ANY, wxEVT_COMMAND_SPINCTRL_UPDATED, wxCommandEventHandler (FilmEditor::top_crop_changed), 0, this); + _bottom_crop->Connect (wxID_ANY, wxEVT_COMMAND_SPINCTRL_UPDATED, wxCommandEventHandler (FilmEditor::bottom_crop_changed), 0, this); + _filters_button->Connect (wxID_ANY, wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler (FilmEditor::edit_filters_clicked), 0, this); + _scaler->Connect (wxID_ANY, wxEVT_COMMAND_COMBOBOX_SELECTED, wxCommandEventHandler (FilmEditor::scaler_changed), 0, this); + _dcp_content_type->Connect (wxID_ANY, wxEVT_COMMAND_COMBOBOX_SELECTED, wxCommandEventHandler (FilmEditor::dcp_content_type_changed), 0, this); + _dcp_ab->Connect (wxID_ANY, wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler (FilmEditor::dcp_ab_toggled), 0, this); + _still_duration->Connect (wxID_ANY, wxEVT_COMMAND_SPINCTRL_UPDATED, wxCommandEventHandler (FilmEditor::still_duration_changed), 0, this); + _dcp_trim_start->Connect (wxID_ANY, wxEVT_COMMAND_SPINCTRL_UPDATED, wxCommandEventHandler (FilmEditor::dcp_trim_start_changed), 0, this); + _dcp_trim_end->Connect (wxID_ANY, wxEVT_COMMAND_SPINCTRL_UPDATED, wxCommandEventHandler (FilmEditor::dcp_trim_end_changed), 0, this); + _with_subtitles->Connect (wxID_ANY, wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler (FilmEditor::with_subtitles_toggled), 0, this); + _subtitle_offset->Connect (wxID_ANY, wxEVT_COMMAND_SPINCTRL_UPDATED, wxCommandEventHandler (FilmEditor::subtitle_offset_changed), 0, this); + _subtitle_scale->Connect (wxID_ANY, wxEVT_COMMAND_SPINCTRL_UPDATED, wxCommandEventHandler (FilmEditor::subtitle_scale_changed), 0, this); + _subtitle_stream->Connect (wxID_ANY, wxEVT_COMMAND_COMBOBOX_SELECTED, wxCommandEventHandler (FilmEditor::subtitle_stream_changed), 0, this); + _audio_stream->Connect (wxID_ANY, wxEVT_COMMAND_COMBOBOX_SELECTED, wxCommandEventHandler (FilmEditor::audio_stream_changed), 0, this); + _audio_gain->Connect (wxID_ANY, wxEVT_COMMAND_SPINCTRL_UPDATED, wxCommandEventHandler (FilmEditor::audio_gain_changed), 0, this); + _audio_gain_calculate_button->Connect ( + wxID_ANY, wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler (FilmEditor::audio_gain_calculate_button_clicked), 0, this + ); + _audio_delay->Connect (wxID_ANY, wxEVT_COMMAND_SPINCTRL_UPDATED, wxCommandEventHandler (FilmEditor::audio_delay_changed), 0, this); + _use_source_audio->Connect (wxID_ANY, wxEVT_COMMAND_RADIOBUTTON_SELECTED, wxCommandEventHandler (FilmEditor::use_audio_changed), 0, this); + _use_external_audio->Connect (wxID_ANY, wxEVT_COMMAND_RADIOBUTTON_SELECTED, wxCommandEventHandler (FilmEditor::use_audio_changed), 0, this); + for (int i = 0; i < MAX_AUDIO_CHANNELS; ++i) { + _external_audio[i]->Connect ( + wxID_ANY, wxEVT_COMMAND_FILEPICKER_CHANGED, wxCommandEventHandler (FilmEditor::external_audio_changed), 0, this + ); + } +} - add_label_to_sizer (_sizer, this, "Content Type"); - _dcp_content_type = new wxComboBox (this, wxID_ANY); - _sizer->Add (_dcp_content_type); +void +FilmEditor::make_video_panel () +{ + _video_panel = new wxPanel (_notebook); + _video_sizer = new wxFlexGridSizer (2, 4, 4); + _video_panel->SetSizer (_video_sizer); - add_label_to_sizer (_sizer, this, "Format"); - _format = new wxComboBox (this, wxID_ANY); - _sizer->Add (_format); + add_label_to_sizer (_video_sizer, _video_panel, "Format"); + _format = new wxComboBox (_video_panel, wxID_ANY); + _video_sizer->Add (_format); { - add_label_to_sizer (_sizer, this, "Crop"); + add_label_to_sizer (_video_sizer, _video_panel, "Crop"); wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL); - add_label_to_sizer (s, this, "L"); - _left_crop = new wxSpinCtrl (this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize (64, -1)); + add_label_to_sizer (s, _video_panel, "L"); + _left_crop = new wxSpinCtrl (_video_panel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize (64, -1)); s->Add (_left_crop, 0); - add_label_to_sizer (s, this, "R"); - _right_crop = new wxSpinCtrl (this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize (64, -1)); + add_label_to_sizer (s, _video_panel, "R"); + _right_crop = new wxSpinCtrl (_video_panel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize (64, -1)); s->Add (_right_crop, 0); - add_label_to_sizer (s, this, "T"); - _top_crop = new wxSpinCtrl (this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize (64, -1)); + add_label_to_sizer (s, _video_panel, "T"); + _top_crop = new wxSpinCtrl (_video_panel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize (64, -1)); s->Add (_top_crop, 0); - add_label_to_sizer (s, this, "B"); - _bottom_crop = new wxSpinCtrl (this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize (64, -1)); + add_label_to_sizer (s, _video_panel, "B"); + _bottom_crop = new wxSpinCtrl (_video_panel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize (64, -1)); s->Add (_bottom_crop, 0); - _sizer->Add (s); + _video_sizer->Add (s); } /* VIDEO-only stuff */ { - video_control (add_label_to_sizer (_sizer, this, "Filters")); + video_control (add_label_to_sizer (_video_sizer, _video_panel, "Filters")); wxSizer* s = new wxBoxSizer (wxHORIZONTAL); - _filters = new wxStaticText (this, wxID_ANY, wxT ("None")); + _filters = new wxStaticText (_video_panel, wxID_ANY, wxT ("None")); video_control (_filters); s->Add (_filters, 1, wxEXPAND | wxALIGN_CENTER_VERTICAL | wxTOP | wxBOTTOM | wxRIGHT, 6); - _filters_button = new wxButton (this, wxID_ANY, wxT ("Edit...")); + _filters_button = new wxButton (_video_panel, wxID_ANY, wxT ("Edit...")); video_control (_filters_button); s->Add (_filters_button, 0); - _sizer->Add (s, 1); + _video_sizer->Add (s, 1); } - video_control (add_label_to_sizer (_sizer, this, "Scaler")); - _scaler = new wxComboBox (this, wxID_ANY); - _sizer->Add (video_control (_scaler), 1); + video_control (add_label_to_sizer (_video_sizer, _video_panel, "Scaler")); + _scaler = new wxComboBox (_video_panel, wxID_ANY); + _video_sizer->Add (video_control (_scaler), 1); - { - video_control (add_label_to_sizer (_sizer, this, "Audio Stream")); - wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL); - _audio_stream = new wxComboBox (this, wxID_ANY); - s->Add (video_control (_audio_stream), 1); - _audio = new wxStaticText (this, wxID_ANY, wxT ("")); - s->Add (video_control (_audio), 1, wxALIGN_CENTER_VERTICAL | wxLEFT, 8); - _sizer->Add (s, 1, wxEXPAND); + vector<Scaler const *> const sc = Scaler::all (); + for (vector<Scaler const *>::const_iterator i = sc.begin(); i != sc.end(); ++i) { + _scaler->Append (std_to_wx ((*i)->name())); } + _left_crop->SetRange (0, 1024); + _top_crop->SetRange (0, 1024); + _right_crop->SetRange (0, 1024); + _bottom_crop->SetRange (0, 1024); + _still_duration->SetRange (0, 60 * 60); + _dcp_trim_start->SetRange (0, 100); + _dcp_trim_end->SetRange (0, 100); +} + +void +FilmEditor::make_audio_panel () +{ + _audio_panel = new wxPanel (_notebook); + _audio_sizer = new wxFlexGridSizer (2, 4, 4); + _audio_panel->SetSizer (_audio_sizer); + { - video_control (add_label_to_sizer (_sizer, this, "Audio Gain")); + video_control (add_label_to_sizer (_audio_sizer, _audio_panel, "Audio Gain")); wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL); - _audio_gain = new wxSpinCtrl (this); + _audio_gain = new wxSpinCtrl (_audio_panel); s->Add (video_control (_audio_gain), 1); - video_control (add_label_to_sizer (s, this, "dB")); - _audio_gain_calculate_button = new wxButton (this, wxID_ANY, _("Calculate...")); + video_control (add_label_to_sizer (s, _audio_panel, "dB")); + _audio_gain_calculate_button = new wxButton (_audio_panel, wxID_ANY, _("Calculate...")); video_control (_audio_gain_calculate_button); s->Add (_audio_gain_calculate_button, 1, wxEXPAND); - _sizer->Add (s); + _audio_sizer->Add (s); } { - video_control (add_label_to_sizer (_sizer, this, "Audio Delay")); + video_control (add_label_to_sizer (_audio_sizer, _audio_panel, "Audio Delay")); wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL); - _audio_delay = new wxSpinCtrl (this); + _audio_delay = new wxSpinCtrl (_audio_panel); s->Add (video_control (_audio_delay), 1); - video_control (add_label_to_sizer (s, this, "ms")); - _sizer->Add (s); + video_control (add_label_to_sizer (s, _audio_panel, "ms")); + _audio_sizer->Add (s); } - _with_subtitles = new wxCheckBox (this, wxID_ANY, wxT("With Subtitles")); - video_control (_with_subtitles); - _sizer->Add (_with_subtitles, 1); - - _subtitle_stream = new wxComboBox (this, wxID_ANY); - _sizer->Add (_subtitle_stream); - - video_control (add_label_to_sizer (_sizer, this, "Subtitle Offset")); - _subtitle_offset = new wxSpinCtrl (this); - _sizer->Add (video_control (_subtitle_offset), 1); - { - video_control (add_label_to_sizer (_sizer, this, "Subtitle Scale")); + _use_source_audio = new wxRadioButton (_audio_panel, wxID_ANY, _("Use source audio"), wxDefaultPosition, wxDefaultSize, wxRB_GROUP); + _audio_sizer->Add (video_control (_use_source_audio)); wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL); - _subtitle_scale = new wxSpinCtrl (this); - s->Add (video_control (_subtitle_scale)); - video_control (add_label_to_sizer (s, this, "%")); - _sizer->Add (s); - } - - video_control (add_label_to_sizer (_sizer, this, "Frames Per Second")); - _frames_per_second = new wxStaticText (this, wxID_ANY, wxT ("")); - _sizer->Add (video_control (_frames_per_second), 1, wxALIGN_CENTER_VERTICAL); - - video_control (add_label_to_sizer (_sizer, this, "Original Size")); - _original_size = new wxStaticText (this, wxID_ANY, wxT ("")); - _sizer->Add (video_control (_original_size), 1, wxALIGN_CENTER_VERTICAL); - - video_control (add_label_to_sizer (_sizer, this, "Length")); - _length = new wxStaticText (this, wxID_ANY, wxT ("")); - _sizer->Add (video_control (_length), 1, wxALIGN_CENTER_VERTICAL); - - - { - video_control (add_label_to_sizer (_sizer, this, "Trim frames")); - wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL); - add_label_to_sizer (s, this, "Start"); - _dcp_trim_start = new wxSpinCtrl (this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize (64, -1)); - s->Add (_dcp_trim_start); - add_label_to_sizer (s, this, "End"); - _dcp_trim_end = new wxSpinCtrl (this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize (64, -1)); - s->Add (_dcp_trim_end); - - _sizer->Add (s); + _audio_stream = new wxComboBox (_audio_panel, wxID_ANY); + s->Add (video_control (_audio_stream), 1); + _audio = new wxStaticText (_audio_panel, wxID_ANY, wxT ("")); + s->Add (video_control (_audio), 1, wxALIGN_CENTER_VERTICAL | wxLEFT, 8); + _audio_sizer->Add (s, 1, wxEXPAND); } - _dcp_ab = new wxCheckBox (this, wxID_ANY, wxT ("A/B")); - video_control (_dcp_ab); - _sizer->Add (_dcp_ab, 1); - _sizer->AddSpacer (0); - - /* STILL-only stuff */ - { - still_control (add_label_to_sizer (_sizer, this, "Duration")); - wxSizer* s = new wxBoxSizer (wxHORIZONTAL); - _still_duration = new wxSpinCtrl (this); - still_control (_still_duration); - s->Add (_still_duration, 1, wxEXPAND); - still_control (add_label_to_sizer (s, this, "s")); - _sizer->Add (s); + _use_external_audio = new wxRadioButton (_audio_panel, wxID_ANY, _("Use external audio")); + _audio_sizer->Add (video_control (_use_external_audio)); + _audio_sizer->AddSpacer (0); + + assert (MAX_AUDIO_CHANNELS == 6); + + char const * channels[] = { + "L", + "R", + "C", + "Lfe", + "Ls", + "Rs" + }; + + for (int i = 0; i < MAX_AUDIO_CHANNELS; ++i) { + add_label_to_sizer (_audio_sizer, _audio_panel, channels[i]); + _external_audio[i] = new wxFilePickerCtrl (_audio_panel, wxID_ANY, wxT (""), wxT ("Select Audio File"), wxT ("*.wav")); + _audio_sizer->Add (video_control (_external_audio[i]), 1, wxEXPAND); } - /* Set up our editing widgets */ - - _left_crop->SetRange (0, 1024); - _top_crop->SetRange (0, 1024); - _right_crop->SetRange (0, 1024); - _bottom_crop->SetRange (0, 1024); _audio_gain->SetRange (-60, 60); _audio_delay->SetRange (-1000, 1000); - _still_duration->SetRange (0, 60 * 60); - _subtitle_offset->SetRange (-1024, 1024); - _subtitle_scale->SetRange (1, 1000); - _dcp_trim_start->SetRange (0, 100); - _dcp_trim_end->SetRange (0, 100); +} - vector<DCPContentType const *> const ct = DCPContentType::all (); - for (vector<DCPContentType const *>::const_iterator i = ct.begin(); i != ct.end(); ++i) { - _dcp_content_type->Append (std_to_wx ((*i)->pretty_name ())); - } +void +FilmEditor::make_subtitle_panel () +{ + _subtitle_panel = new wxPanel (_notebook); + _subtitle_sizer = new wxFlexGridSizer (2, 4, 4); + _subtitle_panel->SetSizer (_subtitle_sizer); - vector<Scaler const *> const sc = Scaler::all (); - for (vector<Scaler const *>::const_iterator i = sc.begin(); i != sc.end(); ++i) { - _scaler->Append (std_to_wx ((*i)->name())); - } + _with_subtitles = new wxCheckBox (_subtitle_panel, wxID_ANY, wxT("With Subtitles")); + video_control (_with_subtitles); + _subtitle_sizer->Add (_with_subtitles, 1); + + _subtitle_stream = new wxComboBox (_subtitle_panel, wxID_ANY); + _subtitle_sizer->Add (_subtitle_stream); - JobManager::instance()->ActiveJobsChanged.connect ( - bind (&FilmEditor::active_jobs_changed, this, _1) - ); + video_control (add_label_to_sizer (_subtitle_sizer, _subtitle_panel, "Subtitle Offset")); + _subtitle_offset = new wxSpinCtrl (_subtitle_panel); + _subtitle_sizer->Add (video_control (_subtitle_offset), 1); - /* And set their values from the Film */ - set_film (f); - - /* Now connect to them, since initial values are safely set */ - _name->Connect (wxID_ANY, wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler (FilmEditor::name_changed), 0, this); - _use_dci_name->Connect (wxID_ANY, wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler (FilmEditor::use_dci_name_toggled), 0, this); - _edit_dci_button->Connect (wxID_ANY, wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler (FilmEditor::edit_dci_button_clicked), 0, this); - _format->Connect (wxID_ANY, wxEVT_COMMAND_COMBOBOX_SELECTED, wxCommandEventHandler (FilmEditor::format_changed), 0, this); - _content->Connect (wxID_ANY, wxEVT_COMMAND_FILEPICKER_CHANGED, wxCommandEventHandler (FilmEditor::content_changed), 0, this); - _left_crop->Connect (wxID_ANY, wxEVT_COMMAND_SPINCTRL_UPDATED, wxCommandEventHandler (FilmEditor::left_crop_changed), 0, this); - _right_crop->Connect (wxID_ANY, wxEVT_COMMAND_SPINCTRL_UPDATED, wxCommandEventHandler (FilmEditor::right_crop_changed), 0, this); - _top_crop->Connect (wxID_ANY, wxEVT_COMMAND_SPINCTRL_UPDATED, wxCommandEventHandler (FilmEditor::top_crop_changed), 0, this); - _bottom_crop->Connect (wxID_ANY, wxEVT_COMMAND_SPINCTRL_UPDATED, wxCommandEventHandler (FilmEditor::bottom_crop_changed), 0, this); - _filters_button->Connect (wxID_ANY, wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler (FilmEditor::edit_filters_clicked), 0, this); - _scaler->Connect (wxID_ANY, wxEVT_COMMAND_COMBOBOX_SELECTED, wxCommandEventHandler (FilmEditor::scaler_changed), 0, this); - _dcp_content_type->Connect (wxID_ANY, wxEVT_COMMAND_COMBOBOX_SELECTED, wxCommandEventHandler (FilmEditor::dcp_content_type_changed), 0, this); - _dcp_ab->Connect (wxID_ANY, wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler (FilmEditor::dcp_ab_toggled), 0, this); - _audio_gain->Connect (wxID_ANY, wxEVT_COMMAND_SPINCTRL_UPDATED, wxCommandEventHandler (FilmEditor::audio_gain_changed), 0, this); - _audio_gain_calculate_button->Connect ( - wxID_ANY, wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler (FilmEditor::audio_gain_calculate_button_clicked), 0, this - ); - _audio_delay->Connect (wxID_ANY, wxEVT_COMMAND_SPINCTRL_UPDATED, wxCommandEventHandler (FilmEditor::audio_delay_changed), 0, this); - _still_duration->Connect (wxID_ANY, wxEVT_COMMAND_SPINCTRL_UPDATED, wxCommandEventHandler (FilmEditor::still_duration_changed), 0, this); - _dcp_trim_start->Connect (wxID_ANY, wxEVT_COMMAND_SPINCTRL_UPDATED, wxCommandEventHandler (FilmEditor::dcp_trim_start_changed), 0, this); - _dcp_trim_end->Connect (wxID_ANY, wxEVT_COMMAND_SPINCTRL_UPDATED, wxCommandEventHandler (FilmEditor::dcp_trim_end_changed), 0, this); - _with_subtitles->Connect (wxID_ANY, wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler (FilmEditor::with_subtitles_toggled), 0, this); - _subtitle_offset->Connect (wxID_ANY, wxEVT_COMMAND_SPINCTRL_UPDATED, wxCommandEventHandler (FilmEditor::subtitle_offset_changed), 0, this); - _subtitle_scale->Connect (wxID_ANY, wxEVT_COMMAND_SPINCTRL_UPDATED, wxCommandEventHandler (FilmEditor::subtitle_scale_changed), 0, this); - _audio_stream->Connect (wxID_ANY, wxEVT_COMMAND_COMBOBOX_SELECTED, wxCommandEventHandler (FilmEditor::audio_stream_changed), 0, this); - _subtitle_stream->Connect (wxID_ANY, wxEVT_COMMAND_COMBOBOX_SELECTED, wxCommandEventHandler (FilmEditor::subtitle_stream_changed), 0, this); + { + video_control (add_label_to_sizer (_subtitle_sizer, _subtitle_panel, "Subtitle Scale")); + wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL); + _subtitle_scale = new wxSpinCtrl (_subtitle_panel); + s->Add (video_control (_subtitle_scale)); + video_control (add_label_to_sizer (s, _subtitle_panel, "%")); + _subtitle_sizer->Add (s); + } - setup_visibility (); - setup_formats (); + _subtitle_offset->SetRange (-1024, 1024); + _subtitle_scale->SetRange (1, 1000); } /** Called when the left crop widget has been changed */ @@ -408,12 +485,9 @@ FilmEditor::film_changed (Film::Property p) setup_subtitle_control_sensitivity (); setup_streams (); break; - case Film::HAS_SUBTITLES: - setup_subtitle_control_sensitivity (); - setup_streams (); - break; case Film::AUDIO_STREAMS: case Film::SUBTITLE_STREAMS: + setup_subtitle_control_sensitivity (); setup_streams (); break; case Film::FORMAT: @@ -443,7 +517,7 @@ FilmEditor::film_changed (Film::Property p) string const b = p.first + " " + p.second; _filters->SetLabel (std_to_wx (b)); } - _sizer->Layout (); + _film_sizer->Layout (); break; } case Film::NAME: @@ -455,9 +529,6 @@ FilmEditor::film_changed (Film::Property p) s << fixed << setprecision(2) << _film->frames_per_second(); _frames_per_second->SetLabel (std_to_wx (s.str ())); break; - case Film::AUDIO_SAMPLE_RATE: - setup_audio_details (); - break; case Film::SIZE: if (_film->size().width == 0 && _film->size().height == 0) { _original_size->SetLabel (wxT ("")); @@ -526,14 +597,31 @@ FilmEditor::film_changed (Film::Property p) _dcp_name->SetLabel (std_to_wx (_film->dcp_name ())); break; case Film::AUDIO_STREAM: - checked_set (_audio_stream, _film->audio_stream_index ()); + if (_film->audio_stream()) { + checked_set (_audio_stream, _film->audio_stream().get().to_string()); + } _dcp_name->SetLabel (std_to_wx (_film->dcp_name ())); setup_audio_details (); break; case Film::SUBTITLE_STREAM: - checked_set (_subtitle_stream, _film->subtitle_stream_index ()); + if (_film->subtitle_stream()) { + checked_set (_subtitle_stream, _film->subtitle_stream().get().to_string()); + } + break; + case Film::USE_SOURCE_AUDIO: + checked_set (_use_source_audio, _film->use_source_audio ()); + checked_set (_use_external_audio, !_film->use_source_audio ()); + setup_audio_control_sensitivity (); + break; + case Film::EXTERNAL_AUDIO: + { + vector<string> a = _film->external_audio (); + for (size_t i = 0; i < a.size() && i < MAX_AUDIO_CHANNELS; ++i) { + checked_set (_external_audio[i], a[i]); + } break; } + } } /** Called when the format widget has been changed */ @@ -592,16 +680,17 @@ FilmEditor::set_film (shared_ptr<Film> f) film_changed (Film::DCP_TRIM_START); film_changed (Film::DCP_TRIM_END); film_changed (Film::DCP_AB); + film_changed (Film::USE_SOURCE_AUDIO); + film_changed (Film::AUDIO_STREAM); + film_changed (Film::EXTERNAL_AUDIO); film_changed (Film::SIZE); film_changed (Film::LENGTH); film_changed (Film::FRAMES_PER_SECOND); - film_changed (Film::AUDIO_SAMPLE_RATE); film_changed (Film::SCALER); film_changed (Film::AUDIO_GAIN); film_changed (Film::AUDIO_DELAY); film_changed (Film::STILL_DURATION); film_changed (Film::WITH_SUBTITLES); - film_changed (Film::HAS_SUBTITLES); film_changed (Film::SUBTITLE_OFFSET); film_changed (Film::SUBTITLE_SCALE); film_changed (Film::USE_DCI_NAME); @@ -638,6 +727,7 @@ FilmEditor::set_things_sensitive (bool s) _still_duration->Enable (s); setup_subtitle_control_sensitivity (); + setup_audio_control_sensitivity (); } /** Called when the `Edit filters' button has been clicked */ @@ -715,7 +805,7 @@ FilmEditor::setup_visibility () (*i)->Show (c == STILL); } - _sizer->Layout (); + _film_sizer->Layout (); } void @@ -800,7 +890,7 @@ FilmEditor::setup_formats () _format->Append (std_to_wx ((*i)->name ())); } - _sizer->Layout (); + _film_sizer->Layout (); } void @@ -818,7 +908,7 @@ FilmEditor::setup_subtitle_control_sensitivity () { bool h = false; if (_generally_sensitive && _film) { - h = _film->has_subtitles(); + h = !_film->subtitle_streams().empty(); } _with_subtitles->Enable (h); @@ -828,6 +918,21 @@ FilmEditor::setup_subtitle_control_sensitivity () } void +FilmEditor::setup_audio_control_sensitivity () +{ + _use_source_audio->Enable (_generally_sensitive); + _use_external_audio->Enable (_generally_sensitive); + + bool const source = _generally_sensitive && _use_source_audio->GetValue(); + bool const external = _generally_sensitive && _use_external_audio->GetValue(); + + _audio_stream->Enable (source); + for (int i = 0; i < MAX_AUDIO_CHANNELS; ++i) { + _external_audio[i]->Enable (external); + } +} + +void FilmEditor::use_dci_name_toggled (wxCommandEvent &) { if (!_film) { @@ -855,16 +960,21 @@ FilmEditor::setup_streams () _audio_stream->Clear (); vector<AudioStream> a = _film->audio_streams (); for (vector<AudioStream>::iterator i = a.begin(); i != a.end(); ++i) { - _audio_stream->Append (std_to_wx (i->name())); + _audio_stream->Append (std_to_wx (i->name()), new wxStringClientData (std_to_wx (i->to_string ()))); + } + + if (_film->audio_stream()) { + checked_set (_audio_stream, _film->audio_stream().get().to_string()); } - _audio_stream->SetSelection (_film->audio_stream_index ()); _subtitle_stream->Clear (); vector<SubtitleStream> s = _film->subtitle_streams (); for (vector<SubtitleStream>::iterator i = s.begin(); i != s.end(); ++i) { - _subtitle_stream->Append (std_to_wx (i->name())); + _subtitle_stream->Append (std_to_wx (i->name()), new wxStringClientData (std_to_wx (i->to_string ()))); + } + if (_film->subtitle_stream()) { + checked_set (_subtitle_stream, _film->subtitle_stream().get().to_string()); } - _subtitle_stream->SetSelection (_film->subtitle_stream_index ()); } void @@ -874,7 +984,7 @@ FilmEditor::audio_stream_changed (wxCommandEvent &) return; } - _film->set_audio_stream (_audio_stream->GetSelection ()); + _film->set_audio_stream (AudioStream (string_client_data (_audio_stream->GetClientObject (_audio_stream->GetSelection ())))); } void @@ -884,17 +994,17 @@ FilmEditor::subtitle_stream_changed (wxCommandEvent &) return; } - _film->set_subtitle_stream (_subtitle_stream->GetSelection ()); + _film->set_subtitle_stream (SubtitleStream (string_client_data (_subtitle_stream->GetClientObject (_subtitle_stream->GetSelection ())))); } void FilmEditor::setup_audio_details () { - if (_film->audio_channels() == 0 && _film->audio_sample_rate() == 0) { + if (!_film->audio_stream()) { _audio->SetLabel (wxT ("")); } else { stringstream s; - s << _film->audio_channels () << " channels, " << _film->audio_sample_rate() << "Hz"; + s << _film->audio_stream().get().channels () << " channels, " << _film->audio_stream().get().sample_rate() << "Hz"; _audio->SetLabel (std_to_wx (s.str ())); } } @@ -904,3 +1014,21 @@ FilmEditor::active_jobs_changed (bool a) { set_things_sensitive (!a); } + +void +FilmEditor::use_audio_changed (wxCommandEvent &) +{ + _film->set_use_source_audio (_use_source_audio->GetValue ()); + setup_audio_control_sensitivity (); +} + +void +FilmEditor::external_audio_changed (wxCommandEvent &) +{ + vector<string> a; + for (int i = 0; i < MAX_AUDIO_CHANNELS; ++i) { + a.push_back (wx_to_std (_external_audio[i]->GetPath())); + } + + _film->set_external_audio (a); +} diff --git a/src/wx/film_editor.h b/src/wx/film_editor.h index c5fe4b6cc..2890df244 100644 --- a/src/wx/film_editor.h +++ b/src/wx/film_editor.h @@ -28,6 +28,8 @@ #include <boost/signals2.hpp> #include "lib/film.h" +class wxNotebook; + class Film; /** @class FilmEditor @@ -44,6 +46,12 @@ public: boost::signals2::signal<void (std::string)> FileChanged; private: + void make_film_panel (); + void make_video_panel (); + void make_audio_panel (); + void make_subtitle_panel (); + void connect_to_widgets (); + /* Handle changes to the view */ void name_changed (wxCommandEvent &); void use_dci_name_toggled (wxCommandEvent &); @@ -68,17 +76,19 @@ private: void still_duration_changed (wxCommandEvent &); void audio_stream_changed (wxCommandEvent &); void subtitle_stream_changed (wxCommandEvent &); + void use_audio_changed (wxCommandEvent &); + void external_audio_changed (wxCommandEvent &); /* Handle changes to the model */ void film_changed (Film::Property); /* Button clicks */ void edit_filters_clicked (wxCommandEvent &); - void change_dcp_range_clicked (wxCommandEvent &); void set_things_sensitive (bool); void setup_formats (); void setup_subtitle_control_sensitivity (); + void setup_audio_control_sensitivity (); void setup_streams (); void setup_audio_details (); @@ -87,6 +97,16 @@ private: void active_jobs_changed (bool); + wxNotebook* _notebook; + wxPanel* _film_panel; + wxSizer* _film_sizer; + wxPanel* _video_panel; + wxSizer* _video_sizer; + wxPanel* _audio_panel; + wxSizer* _audio_sizer; + wxPanel* _subtitle_panel; + wxSizer* _subtitle_sizer; + /** The film we are editing */ boost::shared_ptr<Film> _film; /** The Film's name */ @@ -112,7 +132,10 @@ private: wxButton* _filters_button; /** The Film's scaler */ wxComboBox* _scaler; + wxRadioButton* _use_source_audio; wxComboBox* _audio_stream; + wxRadioButton* _use_external_audio; + wxFilePickerCtrl* _external_audio[MAX_AUDIO_CHANNELS]; /** The Film's audio gain */ wxSpinCtrl* _audio_gain; /** A button to open the gain calculation dialogue */ @@ -146,7 +169,5 @@ private: std::vector<Format const *> _formats; - wxSizer* _sizer; - bool _generally_sensitive; }; diff --git a/src/wx/wx_util.cc b/src/wx/wx_util.cc index a677fd9ac..4196dd632 100644 --- a/src/wx/wx_util.cc +++ b/src/wx/wx_util.cc @@ -109,6 +109,12 @@ ThreadedStaticText::thread_finished (wxCommandEvent& ev) SetLabel (ev.GetString ()); } +string +string_client_data (wxClientData* o) +{ + return wx_to_std (dynamic_cast<wxStringClientData*>(o)->GetData()); +} + void checked_set (wxFilePickerCtrl* widget, string value) { @@ -134,6 +140,20 @@ checked_set (wxComboBox* widget, int value) } void +checked_set (wxComboBox* widget, string value) +{ + wxClientData* o = widget->GetClientObject (widget->GetSelection ()); + + if (!o || string_client_data(o) != value) { + for (unsigned int i = 0; i < widget->GetCount(); ++i) { + if (string_client_data (widget->GetClientObject (i)) == value) { + widget->SetSelection (i); + } + } + } +} + +void checked_set (wxTextCtrl* widget, string value) { if (widget->GetValue() != std_to_wx (value)) { @@ -148,3 +168,11 @@ checked_set (wxCheckBox* widget, bool value) widget->SetValue (value); } } + +void +checked_set (wxRadioButton* widget, bool value) +{ + if (widget->GetValue() != value) { + widget->SetValue (value); + } +} diff --git a/src/wx/wx_util.h b/src/wx/wx_util.h index c2c3b6dde..6cb7fd002 100644 --- a/src/wx/wx_util.h +++ b/src/wx/wx_util.h @@ -54,8 +54,12 @@ private: static const int _update_event_id; }; +extern std::string string_client_data (wxClientData* o); + extern void checked_set (wxFilePickerCtrl* widget, std::string value); extern void checked_set (wxSpinCtrl* widget, int value); extern void checked_set (wxComboBox* widget, int value); +extern void checked_set (wxComboBox* widget, std::string value); extern void checked_set (wxTextCtrl* widget, std::string value); extern void checked_set (wxCheckBox* widget, bool value); +extern void checked_set (wxRadioButton* widget, bool value); |
