Another attempt to do external audio moderately nicely.
authorCarl Hetherington <cth@carlh.net>
Sat, 17 Nov 2012 22:06:12 +0000 (22:06 +0000)
committerCarl Hetherington <cth@carlh.net>
Sat, 17 Nov 2012 22:06:12 +0000 (22:06 +0000)
20 files changed:
src/lib/ab_transcoder.cc
src/lib/audio_decoder.cc
src/lib/audio_decoder.h
src/lib/decoder_factory.cc
src/lib/external_audio_decoder.cc
src/lib/external_audio_decoder.h
src/lib/ffmpeg_decoder.cc
src/lib/ffmpeg_decoder.h
src/lib/film.cc
src/lib/film.h
src/lib/j2k_wav_encoder.cc
src/lib/stream.cc
src/lib/stream.h
src/lib/transcoder.cc
src/lib/util.cc
src/lib/video_decoder.cc
src/lib/video_decoder.h
src/wx/film_editor.cc
test/metadata.ref
test/test.cc

index e9277a96fe05a11ad05f803d791748b73cc58859..f280ab48c339c2b5689b5dcfe8f485f657b54d8e 100644 (file)
@@ -60,9 +60,9 @@ ABTranscoder::ABTranscoder (
        _db = decoder_factory (_film_b, o, j);
 
        if (_film_a->audio_stream()) {
-               AudioStream st = _film_a->audio_stream().get();
-               _matcher.reset (new Matcher (_film_a->log(), st.sample_rate(), _film_a->frames_per_second()));
-               _delay_line.reset (new DelayLine (_film_a->log(), st.channels(), _film_a->audio_delay() * st.sample_rate() / 1000));
+               shared_ptr<AudioStream> st = _film_a->audio_stream();
+               _matcher.reset (new Matcher (_film_a->log(), st->sample_rate(), _film_a->frames_per_second()));
+               _delay_line.reset (new DelayLine (_film_a->log(), st->channels(), _film_a->audio_delay() * st->sample_rate() / 1000));
                _gain.reset (new Gain (_film_a->log(), _film_a->audio_gain()));
        }
 
index da39ef2ec78b178ded5c11119588f9022ee92493..70f0effd9faa742bf5e744ab71d2dd8ff35e35b0 100644 (file)
@@ -30,7 +30,7 @@ AudioDecoder::AudioDecoder (shared_ptr<Film> f, shared_ptr<const Options> o, Job
 }
 
 void
-AudioDecoder::set_audio_stream (optional<AudioStream> s)
+AudioDecoder::set_audio_stream (shared_ptr<AudioStream> s)
 {
        _audio_stream = s;
 }
index 6e89f6144da605a16071439b63879971e70d1918..e9fbd6f62f19f1605f1e77ec3685f98601d71a0c 100644 (file)
@@ -29,19 +29,19 @@ class AudioDecoder : public AudioSource, public virtual Decoder
 public:
        AudioDecoder (boost::shared_ptr<Film>, boost::shared_ptr<const Options>, Job *);
 
-       virtual void set_audio_stream (boost::optional<AudioStream>);
+       virtual void set_audio_stream (boost::shared_ptr<AudioStream>);
 
-       boost::optional<AudioStream> audio_stream () const {
+       boost::shared_ptr<AudioStream> audio_stream () const {
                return _audio_stream;
        }
 
-       std::vector<AudioStream> audio_streams () const {
+       std::vector<boost::shared_ptr<AudioStream> > audio_streams () const {
                return _audio_streams;
        }
 
 protected:
-       boost::optional<AudioStream> _audio_stream;
-       std::vector<AudioStream> _audio_streams;
+       boost::shared_ptr<AudioStream> _audio_stream;
+       std::vector<boost::shared_ptr<AudioStream> > _audio_streams;
 };
 
 #endif
index e9b5bfa9e755682f5d4bf72e1dba2b5c31dbadea..b2118ef744832649f6a176223153dbc4326b83b3 100644 (file)
@@ -31,6 +31,7 @@ using std::string;
 using std::pair;
 using std::make_pair;
 using boost::shared_ptr;
+using boost::dynamic_pointer_cast;
 
 pair<shared_ptr<VideoDecoder>, shared_ptr<AudioDecoder> >
 decoder_factory (
@@ -49,6 +50,6 @@ decoder_factory (
        if (f->use_content_audio()) {
                return make_pair (fd, fd);
        }
-       
+
        return make_pair (fd, shared_ptr<AudioDecoder> (new ExternalAudioDecoder (f, o, j)));
 }
index 2297ac4da48fb7ddc01a836a3ba46e6b067e8541..29668d922836fc5ceea9100d28e041690a4a7cd4 100644 (file)
 
 using std::vector;
 using std::string;
+using std::stringstream;
 using std::min;
 using std::cout;
 using boost::shared_ptr;
+using boost::optional;
 
 ExternalAudioDecoder::ExternalAudioDecoder (shared_ptr<Film> f, shared_ptr<const Options> o, Job* j)
        : Decoder (f, o, j)
        , AudioDecoder (f, o, j)
 {
-
+       sf_count_t frames;
+       vector<SNDFILE*> sf = open_files (frames);
+       close_files (sf);
 }
 
-bool
-ExternalAudioDecoder::pass ()
+vector<SNDFILE*>
+ExternalAudioDecoder::open_files (sf_count_t & frames)
 {
        vector<string> const files = _film->external_audio ();
 
@@ -48,11 +52,11 @@ ExternalAudioDecoder::pass ()
        }
 
        if (N == 0) {
-               return true;
+               return vector<SNDFILE*> ();
        }
 
        bool first = true;
-       sf_count_t frames = 0;
+       frames = 0;
        
        vector<SNDFILE*> sndfiles;
        for (size_t i = 0; i < (size_t) N; ++i) {
@@ -72,8 +76,12 @@ ExternalAudioDecoder::pass ()
                        sndfiles.push_back (s);
 
                        if (first) {
-                               /* XXX: nasty magic value */
-                               AudioStream st ("DVDOMATIC-EXTERNAL", -1, info.samplerate, av_get_default_channel_layout (N));
+                               shared_ptr<ExternalAudioStream> st (
+                                       new ExternalAudioStream (
+                                               info.samplerate, av_get_default_channel_layout (N)
+                                               )
+                                       );
+                               
                                _audio_streams.push_back (st);
                                _audio_stream = st;
                                frames = info.frames;
@@ -86,9 +94,23 @@ ExternalAudioDecoder::pass ()
                }
        }
 
+       return sndfiles;
+}
+
+bool
+ExternalAudioDecoder::pass ()
+{
+       sf_count_t frames;
+       vector<SNDFILE*> sndfiles = open_files (frames);
+       if (sndfiles.empty()) {
+               return true;
+       }
+
        sf_count_t const block = 65536;
 
-       shared_ptr<AudioBuffers> audio (new AudioBuffers (_audio_stream.get().channels(), block));
+       cout << frames << " audio frames.\n";
+
+       shared_ptr<AudioBuffers> audio (new AudioBuffers (_audio_stream->channels(), block));
        while (frames > 0) {
                sf_count_t const this_time = min (block, frames);
                for (size_t i = 0; i < sndfiles.size(); ++i) {
@@ -103,9 +125,59 @@ ExternalAudioDecoder::pass ()
                frames -= this_time;
        }
 
+       close_files (sndfiles);
+
+       return true;
+}
+
+void
+ExternalAudioDecoder::close_files (vector<SNDFILE*> const & sndfiles)
+{
        for (size_t i = 0; i < sndfiles.size(); ++i) {
                sf_close (sndfiles[i]);
        }
-       
-       return true;
+}
+
+shared_ptr<ExternalAudioStream>
+ExternalAudioStream::create ()
+{
+       return shared_ptr<ExternalAudioStream> (new ExternalAudioStream);
+}
+
+shared_ptr<ExternalAudioStream>
+ExternalAudioStream::create (string t, optional<int> v)
+{
+       if (!v) {
+               /* version < 1; no type in the string, and there's only FFmpeg streams anyway */
+               return shared_ptr<ExternalAudioStream> ();
+       }
+
+       stringstream s (t);
+       string type;
+       s >> type;
+       if (type != "external") {
+               return shared_ptr<ExternalAudioStream> ();
+       }
+
+       return shared_ptr<ExternalAudioStream> (new ExternalAudioStream (t, v));
+}
+
+ExternalAudioStream::ExternalAudioStream (string t, optional<int> v)
+{
+       assert (v);
+
+       stringstream s (t);
+       string type;
+       s >> type >> _sample_rate >> _channel_layout;
+}
+
+ExternalAudioStream::ExternalAudioStream ()
+{
+
+}
+
+string
+ExternalAudioStream::to_string () const
+{
+       return String::compose ("external %1 %2", _sample_rate, _channel_layout);
 }
index 79820936ef1ad84d1f78f73f74738327363eb345..45a2a809c214569e27f927a3c46b680dbbebe132 100644 (file)
 
 */
 
+#include <sndfile.h>
 #include "decoder.h"
 #include "audio_decoder.h"
+#include "stream.h"
+
+class ExternalAudioStream : public AudioStream
+{
+public:
+       ExternalAudioStream (int sample_rate, int64_t layout)
+               : AudioStream (sample_rate, layout)
+       {}
+                              
+       std::string to_string () const;
+
+       static boost::shared_ptr<ExternalAudioStream> create ();
+       static boost::shared_ptr<ExternalAudioStream> create (std::string t, boost::optional<int> v);
+
+private:
+       friend class stream_test;
+       
+       ExternalAudioStream ();
+       ExternalAudioStream (std::string t, boost::optional<int> v);
+};
 
 class ExternalAudioDecoder : public AudioDecoder
 {
@@ -26,4 +47,8 @@ public:
        ExternalAudioDecoder (boost::shared_ptr<Film>, boost::shared_ptr<const Options>, Job *);
 
        bool pass ();
+
+private:
+       std::vector<SNDFILE*> open_files (sf_count_t &);
+       void close_files (std::vector<SNDFILE*> const &);
 };
index 60f192c85213ec7b0d61bc4828c80c1a7cffbb56..4a6e236c3747c2326171bb68a60a4e05ba6a2ca9 100644 (file)
@@ -57,6 +57,7 @@ using std::stringstream;
 using std::list;
 using boost::shared_ptr;
 using boost::optional;
+using boost::dynamic_pointer_cast;
 
 FFmpegDecoder::FFmpegDecoder (shared_ptr<Film> f, shared_ptr<const Options> o, Job* j)
        : Decoder (f, o, j)
@@ -119,9 +120,17 @@ FFmpegDecoder::setup_general ()
                if (s->codec->codec_type == AVMEDIA_TYPE_VIDEO) {
                        _video_stream = i;
                } else if (s->codec->codec_type == AVMEDIA_TYPE_AUDIO) {
-                       _audio_streams.push_back (AudioStream (stream_name (s), i, s->codec->sample_rate, s->codec->channel_layout));
+                       _audio_streams.push_back (
+                               shared_ptr<AudioStream> (
+                                       new FFmpegAudioStream (stream_name (s), i, s->codec->sample_rate, s->codec->channel_layout)
+                                       )
+                               );
                } else if (s->codec->codec_type == AVMEDIA_TYPE_SUBTITLE) {
-                       _subtitle_streams.push_back (SubtitleStream (stream_name (s), i));
+                       _subtitle_streams.push_back (
+                               shared_ptr<SubtitleStream> (
+                                       new SubtitleStream (stream_name (s), i)
+                                       )
+                               );
                }
        }
 
@@ -163,8 +172,11 @@ FFmpegDecoder::setup_audio ()
        if (!_audio_stream) {
                return;
        }
+
+       shared_ptr<FFmpegAudioStream> ffa = dynamic_pointer_cast<FFmpegAudioStream> (_audio_stream);
+       assert (ffa);
        
-       _audio_codec_context = _format_context->streams[_audio_stream.get().id()]->codec;
+       _audio_codec_context = _format_context->streams[ffa->id()]->codec;
        _audio_codec = avcodec_find_decoder (_audio_codec_context->codec_id);
 
        if (_audio_codec == 0) {
@@ -180,7 +192,7 @@ FFmpegDecoder::setup_audio ()
        */
 
        if (_audio_codec_context->channel_layout == 0) {
-               _audio_codec_context->channel_layout = av_get_default_channel_layout (_audio_stream.get().channels());
+               _audio_codec_context->channel_layout = av_get_default_channel_layout (ffa->channels());
        }
 }
 
@@ -191,7 +203,7 @@ FFmpegDecoder::setup_subtitle ()
                return;
        }
 
-       _subtitle_codec_context = _format_context->streams[_subtitle_stream.get().id()]->codec;
+       _subtitle_codec_context = _format_context->streams[_subtitle_stream->id()]->codec;
        _subtitle_codec = avcodec_find_decoder (_subtitle_codec_context->codec_id);
 
        if (_subtitle_codec == 0) {
@@ -230,7 +242,7 @@ FFmpegDecoder::pass ()
                        filter_and_emit_video (_frame);
                }
 
-               if (_audio_stream && _opt->decode_audio && _film->use_content_audio()) {
+               if (_audio_stream && _opt->decode_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
@@ -245,6 +257,8 @@ FFmpegDecoder::pass ()
        }
 
        avcodec_get_frame_defaults (_frame);
+
+       shared_ptr<FFmpegAudioStream> ffa = dynamic_pointer_cast<FFmpegAudioStream> (_audio_stream);
        
        if (_packet.stream_index == _video_stream) {
 
@@ -294,7 +308,7 @@ FFmpegDecoder::pass ()
                        }
                }
 
-       } else if (_audio_stream && _packet.stream_index == _audio_stream.get().id() && _opt->decode_audio && _film->use_content_audio()) {
+       } else if (ffa && _packet.stream_index == ffa->id() && _opt->decode_audio) {
 
                int frame_finished;
                if (avcodec_decode_audio4 (_audio_codec_context, _frame, &frame_finished, &_packet) >= 0 && frame_finished) {
@@ -318,17 +332,17 @@ FFmpegDecoder::pass ()
                                        */
                        
                                        /* frames of silence that we must push */
-                                       int const s = rint ((_first_audio.get() - _first_video.get()) * _audio_stream.get().sample_rate ());
+                                       int const s = rint ((_first_audio.get() - _first_video.get()) * ffa->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_stream.get().channels(), bytes_per_audio_sample()
+                                                       _first_video.get(), _first_audio.get(), s, ffa->channels(), bytes_per_audio_sample()
                                                        )
                                                );
                                        
                                        if (s) {
-                                               shared_ptr<AudioBuffers> audio (new AudioBuffers (_audio_stream.get().channels(), s));
+                                               shared_ptr<AudioBuffers> audio (new AudioBuffers (ffa->channels(), s));
                                                audio->make_silent ();
                                                Audio (audio);
                                        }
@@ -343,7 +357,7 @@ FFmpegDecoder::pass ()
                        }
                }
                        
-       } else if (_subtitle_stream && _packet.stream_index == _subtitle_stream.get().id() && _opt->decode_subtitles && _first_video) {
+       } else if (_subtitle_stream && _packet.stream_index == _subtitle_stream->id() && _opt->decode_subtitles && _first_video) {
 
                int got_subtitle;
                AVSubtitle sub;
@@ -369,14 +383,17 @@ FFmpegDecoder::deinterleave_audio (uint8_t* data, int size)
 {
        assert (_film->audio_channels());
        assert (bytes_per_audio_sample());
+
+       shared_ptr<FFmpegAudioStream> ffa = dynamic_pointer_cast<FFmpegAudioStream> (_audio_stream);
+       assert (ffa);
        
        /* Deinterleave and convert to float */
 
-       assert ((size % (bytes_per_audio_sample() * _audio_stream.get().channels())) == 0);
+       assert ((size % (bytes_per_audio_sample() * ffa->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));
+       shared_ptr<AudioBuffers> audio (new AudioBuffers (ffa->channels(), frames));
 
        switch (audio_sample_format()) {
        case AV_SAMPLE_FMT_S16:
@@ -519,14 +536,14 @@ FFmpegDecoder::bytes_per_audio_sample () const
 }
 
 void
-FFmpegDecoder::set_audio_stream (optional<AudioStream> s)
+FFmpegDecoder::set_audio_stream (shared_ptr<AudioStream> s)
 {
        AudioDecoder::set_audio_stream (s);
        setup_audio ();
 }
 
 void
-FFmpegDecoder::set_subtitle_stream (optional<SubtitleStream> s)
+FFmpegDecoder::set_subtitle_stream (shared_ptr<SubtitleStream> s)
 {
        VideoDecoder::set_subtitle_stream (s);
        setup_subtitle ();
@@ -556,3 +573,56 @@ FFmpegDecoder::filter_and_emit_video (AVFrame* frame)
                emit_video (*i);
        }
 }
+
+shared_ptr<FFmpegAudioStream>
+FFmpegAudioStream::create (string t, optional<int> v)
+{
+       if (!v) {
+               /* version < 1; no type in the string, and there's only FFmpeg streams anyway */
+               return shared_ptr<FFmpegAudioStream> (new FFmpegAudioStream (t, v));
+       }
+
+       stringstream s (t);
+       string type;
+       s >> type;
+       if (type != "ffmpeg") {
+               return shared_ptr<FFmpegAudioStream> ();
+       }
+
+       return shared_ptr<FFmpegAudioStream> (new FFmpegAudioStream (t, v));
+}
+
+FFmpegAudioStream::FFmpegAudioStream (string t, optional<int> version)
+{
+       stringstream n (t);
+       
+       int name_index = 4;
+       if (!version) {
+               name_index = 2;
+               int channels;
+               n >> _id >> channels;
+               _channel_layout = av_get_default_channel_layout (channels);
+               _sample_rate = 0;
+       } else {
+               string type;
+               /* Current (marked version 1) */
+               n >> type >> _id >> _sample_rate >> _channel_layout;
+               assert (type == "ffmpeg");
+       }
+
+       for (int i = 0; i < name_index; ++i) {
+               size_t const s = t.find (' ');
+               if (s != string::npos) {
+                       t = t.substr (s + 1);
+               }
+       }
+
+       _name = t;
+}
+
+string
+FFmpegAudioStream::to_string () const
+{
+       return String::compose ("ffmpeg %1 %2 %3 %4", _id, _sample_rate, _channel_layout, _name);
+}
+
index a4a9246f0bb16189dd4f30ea63fb79752fa5bff1..87eebe1ecc4ba7b83f92fb23f096435caf5bdd2b 100644 (file)
@@ -48,6 +48,36 @@ class Options;
 class Image;
 class Log;
 
+class FFmpegAudioStream : public AudioStream
+{
+public:
+       FFmpegAudioStream (std::string n, int i, int s, int64_t c)
+               : AudioStream (s, c)
+               , _name (n)
+               , _id (i)
+       {}
+                 
+       std::string to_string () const;
+
+       std::string name () const {
+               return _name;
+       }
+
+       int id () const {
+               return _id;
+       }
+
+       static boost::shared_ptr<FFmpegAudioStream> create (std::string t, boost::optional<int> v);
+
+private:
+       friend class stream_test;
+       
+       FFmpegAudioStream (std::string t, boost::optional<int> v);
+       
+       std::string _name;
+       int _id;
+};
+
 /** @class FFmpegDecoder
  *  @brief A decoder using FFmpeg to decode content.
  */
@@ -64,8 +94,8 @@ public:
        int sample_aspect_ratio_numerator () const;
        int sample_aspect_ratio_denominator () const;
 
-       void set_audio_stream (boost::optional<AudioStream>);
-       void set_subtitle_stream (boost::optional<SubtitleStream>);
+       void set_audio_stream (boost::shared_ptr<AudioStream>);
+       void set_subtitle_stream (boost::shared_ptr<SubtitleStream>);
 
 private:
 
index 2330e3c7304ed54728a3b9848cb3e3fc403a57f2..ce551041bfc4506239ec1cc177371d3fd0de65eb 100644 (file)
@@ -54,6 +54,7 @@
 #include "ui_signaller.h"
 #include "video_decoder.h"
 #include "audio_decoder.h"
+#include "external_audio_decoder.h"
 
 using std::string;
 using std::stringstream;
@@ -66,6 +67,7 @@ using std::ofstream;
 using std::setfill;
 using std::min;
 using std::make_pair;
+using std::cout;
 using boost::shared_ptr;
 using boost::lexical_cast;
 using boost::to_upper_copy;
@@ -129,6 +131,8 @@ Film::Film (string d, bool must_exist)
                }
        }
 
+       _external_audio_stream = ExternalAudioStream::create ();
+
        read_metadata ();
 
        _log = new FileLog (file ("log"));
@@ -149,9 +153,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_content_audio (o._use_content_audio)
-       , _audio_stream      (o._audio_stream)
+       , _content_audio_stream (o._content_audio_stream)
        , _external_audio    (o._external_audio)
+       , _use_content_audio (o._use_content_audio)
        , _audio_gain        (o._audio_gain)
        , _audio_delay       (o._audio_delay)
        , _still_duration    (o._still_duration)
@@ -170,7 +174,8 @@ Film::Film (Film const & o)
        , _size              (o._size)
        , _length            (o._length)
        , _content_digest    (o._content_digest)
-       , _audio_streams     (o._audio_streams)
+       , _content_audio_streams (o._content_audio_streams)
+       , _external_audio_stream (o._external_audio_stream)
        , _subtitle_streams  (o._subtitle_streams)
        , _frames_per_second (o._frames_per_second)
        , _dirty             (o._dirty)
@@ -264,8 +269,8 @@ Film::make_dcp (bool transcode)
                o->video_decode_range = make_pair (dcp_trim_start(), dcp_trim_start() + dcp_length().get());
                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())
+                               video_frames_to_audio_frames (o->video_decode_range.get().first, audio_stream()->sample_rate(), frames_per_second()),
+                               video_frames_to_audio_frames (o->video_decode_range.get().second, audio_stream()->sample_rate(), frames_per_second())
                                );
                }
                        
@@ -424,18 +429,18 @@ 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 << "use_content_audio " << (_use_content_audio ? "1" : "0") << "\n";
-       if (_audio_stream) {
-               f << "selected_audio_stream " << _audio_stream.get().to_string() << "\n";
+       if (_content_audio_stream) {
+               f << "selected_content_audio_stream " << _content_audio_stream->to_string() << "\n";
        }
        for (vector<string>::const_iterator i = _external_audio.begin(); i != _external_audio.end(); ++i) {
                f << "external_audio " << *i << "\n";
        }
+       f << "use_content_audio " << (_use_content_audio ? "1" : "0") << "\n";
        f << "audio_gain " << _audio_gain << "\n";
        f << "audio_delay " << _audio_delay << "\n";
        f << "still_duration " << _still_duration << "\n";
        if (_subtitle_stream) {
-               f << "selected_subtitle_stream " << _subtitle_stream.get().to_string() << "\n";
+               f << "selected_subtitle_stream " << _subtitle_stream->to_string() << "\n";
        }
        f << "with_subtitles " << _with_subtitles << "\n";
        f << "subtitle_offset " << _subtitle_offset << "\n";
@@ -459,12 +464,14 @@ Film::write_metadata () const
        f << "length " << _length.get_value_or(0) << "\n";
        f << "content_digest " << _content_digest << "\n";
 
-       for (vector<AudioStream>::const_iterator i = _audio_streams.begin(); i != _audio_streams.end(); ++i) {
-               f << "audio_stream " << i->to_string () << "\n";
+       for (vector<shared_ptr<AudioStream> >::const_iterator i = _content_audio_streams.begin(); i != _content_audio_streams.end(); ++i) {
+               f << "content_audio_stream " << (*i)->to_string () << "\n";
        }
 
-       for (vector<SubtitleStream>::const_iterator i = _subtitle_streams.begin(); i != _subtitle_streams.end(); ++i) {
-               f << "subtitle_stream " << i->to_string () << "\n";
+       f << "external_audio_stream " << _external_audio_stream->to_string() << "\n";
+
+       for (vector<shared_ptr<SubtitleStream> >::const_iterator i = _subtitle_streams.begin(); i != _subtitle_streams.end(); ++i) {
+               f << "subtitle_stream " << (*i)->to_string () << "\n";
        }
 
        f << "frames_per_second " << _frames_per_second << "\n";
@@ -480,7 +487,7 @@ Film::read_metadata ()
 
        _external_audio.clear ();
        _thumbs.clear ();
-       _audio_streams.clear ();
+       _content_audio_streams.clear ();
        _subtitle_streams.clear ();
 
        boost::optional<int> version;
@@ -492,13 +499,18 @@ Film::read_metadata ()
        
        ifstream f (file ("metadata").c_str());
        multimap<string, string> kv = read_key_value (f);
+
+       /* We need version before anything else */
+       multimap<string, string>::iterator v = kv.find ("version");
+       if (v != kv.end ()) {
+               version = atoi (v->second.c_str());
+       }
+       
        for (multimap<string, string>::const_iterator i = kv.begin(); i != kv.end(); ++i) {
                string const k = i->first;
                string const v = i->second;
 
-               if (k == "version") {
-                       version = atoi (v.c_str());
-               } else if (k == "audio_sample_rate") {
+               if (k == "audio_sample_rate") {
                        audio_sample_rate = atoi (v.c_str());
                }
 
@@ -531,16 +543,16 @@ Film::read_metadata ()
                        _dcp_trim_end = atoi (v.c_str ());
                } else if (k == "dcp_ab") {
                        _dcp_ab = (v == "1");
-               } else if (k == "use_content_audio") {
-                       _use_content_audio = (v == "1");
-               } else if (k == "selected_audio_stream") {
+               } else if (k == "selected_content_audio_stream" || (!version && k == "content_audio_stream")) {
                        if (!version) {
                                audio_stream_index = atoi (v.c_str ());
                        } else {
-                               _audio_stream = AudioStream (v, version);
+                               _content_audio_stream = audio_stream_factory (v, version);
                        }
                } else if (k == "external_audio") {
                        _external_audio.push_back (v);
+               } else if (k == "use_content_audio") {
+                       _use_content_audio = (v == "1");
                } else if (k == "audio_gain") {
                        _audio_gain = atof (v.c_str ());
                } else if (k == "audio_delay") {
@@ -551,7 +563,7 @@ Film::read_metadata ()
                        if (!version) {
                                subtitle_stream_index = atoi (v.c_str ());
                        } else {
-                               _subtitle_stream = SubtitleStream (v);
+                               _subtitle_stream = subtitle_stream_factory (v, version);
                        }
                } else if (k == "with_subtitles") {
                        _with_subtitles = (v == "1");
@@ -593,10 +605,12 @@ Film::read_metadata ()
                        }
                } else if (k == "content_digest") {
                        _content_digest = v;
-               } else if (k == "audio_stream") {
-                       _audio_streams.push_back (AudioStream (v, version));
+               } else if (k == "content_audio_stream" || (!version && k == "audio_stream")) {
+                       _content_audio_streams.push_back (audio_stream_factory (v, version));
+               } else if (k == "external_audio_stream") {
+                       _external_audio_stream = audio_stream_factory (v, version);
                } else if (k == "subtitle_stream") {
-                       _subtitle_streams.push_back (SubtitleStream (v));
+                       _subtitle_streams.push_back (subtitle_stream_factory (v, version));
                } else if (k == "frames_per_second") {
                        _frames_per_second = atof (v.c_str ());
                }
@@ -605,14 +619,14 @@ Film::read_metadata ()
        if (!version) {
                if (audio_sample_rate) {
                        /* version < 1 didn't specify sample rate in the audio streams, so fill it in here */
-                       for (vector<AudioStream>::iterator i = _audio_streams.begin(); i != _audio_streams.end(); ++i) {
-                               i->set_sample_rate (audio_sample_rate.get());
+                       for (vector<shared_ptr<AudioStream> >::iterator i = _content_audio_streams.begin(); i != _content_audio_streams.end(); ++i) {
+                               (*i)->set_sample_rate (audio_sample_rate.get());
                        }
                }
 
                /* also the selected stream was specified as an index */
-               if (audio_stream_index && audio_stream_index.get() >= 0 && audio_stream_index.get() < (int) _audio_streams.size()) {
-                       _audio_stream = _audio_streams[audio_stream_index.get()];
+               if (audio_stream_index && audio_stream_index.get() >= 0 && audio_stream_index.get() < (int) _content_audio_streams.size()) {
+                       _content_audio_stream = _content_audio_streams[audio_stream_index.get()];
                }
 
                /* similarly the subtitle */
@@ -748,8 +762,12 @@ Film::content_type () const
 int
 Film::target_audio_sample_rate () const
 {
+       if (!audio_stream()) {
+               return 0;
+       }
+       
        /* Resample to a DCI-approved sample rate */
-       double t = dcp_audio_sample_rate (audio_stream().get().sample_rate());
+       double t = dcp_audio_sample_rate (audio_stream()->sample_rate());
 
        DCPFrameRate dfr = dcp_frame_rate (frames_per_second ());
 
@@ -930,8 +948,8 @@ Film::set_content (string c)
                _content = c;
        }
 
-       _audio_stream = optional<AudioStream> ();
-       _subtitle_stream = optional<SubtitleStream> ();
+       _content_audio_stream = shared_ptr<AudioStream> ();
+       _subtitle_stream = shared_ptr<SubtitleStream> ();
 
        /* Create a temporary decoder so that we can get information
           about the content.
@@ -946,11 +964,11 @@ Film::set_content (string c)
                set_size (d.first->native_size ());
                set_frames_per_second (d.first->frames_per_second ());
                set_subtitle_streams (d.first->subtitle_streams ());
-               set_audio_streams (d.second->audio_streams ());
+               set_content_audio_streams (d.second->audio_streams ());
 
                /* Start off with the first audio and subtitle streams */
                if (!d.second->audio_streams().empty()) {
-                       set_audio_stream (d.second->audio_streams().front());
+                       set_content_audio_stream (d.second->audio_streams().front());
                }
                
                if (!d.first->subtitle_streams().empty()) {
@@ -1115,33 +1133,42 @@ Film::set_dcp_ab (bool a)
 }
 
 void
-Film::set_use_content_audio (bool s)
+Film::set_content_audio_stream (shared_ptr<AudioStream> s)
 {
        {
                boost::mutex::scoped_lock lm (_state_mutex);
-               _use_content_audio = s;
+               _content_audio_stream = s;
        }
-       signal_changed (USE_CONTENT_AUDIO);
+       signal_changed (CONTENT_AUDIO_STREAM);
 }
 
 void
-Film::set_audio_stream (optional<AudioStream> s)
+Film::set_external_audio (vector<string> a)
 {
        {
                boost::mutex::scoped_lock lm (_state_mutex);
-               _audio_stream = s;
+               _external_audio = a;
+       }
+
+       shared_ptr<Options> o (new Options ("", "", ""));
+       o->decode_audio = true;
+       shared_ptr<ExternalAudioDecoder> decoder (new ExternalAudioDecoder (shared_from_this(), o, 0));
+       if (decoder->audio_stream()) {
+               _external_audio_stream = decoder->audio_stream ();
        }
-       signal_changed (AUDIO_STREAM);
+       
+       signal_changed (EXTERNAL_AUDIO);
 }
 
 void
-Film::set_external_audio (vector<string> a)
+Film::set_use_content_audio (bool e)
 {
        {
                boost::mutex::scoped_lock lm (_state_mutex);
-               _external_audio = a;
+               _use_content_audio = e;
        }
-       signal_changed (EXTERNAL_AUDIO);
+
+       signal_changed (USE_CONTENT_AUDIO);
 }
 
 void
@@ -1175,7 +1202,7 @@ Film::set_still_duration (int d)
 }
 
 void
-Film::set_subtitle_stream (optional<SubtitleStream> s)
+Film::set_subtitle_stream (shared_ptr<SubtitleStream> s)
 {
        {
                boost::mutex::scoped_lock lm (_state_mutex);
@@ -1335,23 +1362,23 @@ Film::set_content_digest (string d)
 }
 
 void
-Film::set_audio_streams (vector<AudioStream> s)
+Film::set_content_audio_streams (vector<shared_ptr<AudioStream> > s)
 {
        {
                boost::mutex::scoped_lock lm (_state_mutex);
-               _audio_streams = s;
+               _content_audio_streams = s;
        }
-       _dirty = true;
+       signal_changed (CONTENT_AUDIO_STREAMS);
 }
 
 void
-Film::set_subtitle_streams (vector<SubtitleStream> s)
+Film::set_subtitle_streams (vector<shared_ptr<SubtitleStream> > s)
 {
        {
                boost::mutex::scoped_lock lm (_state_mutex);
                _subtitle_streams = s;
        }
-       _dirty = true;
+       signal_changed (SUBTITLE_STREAMS);
 }
 
 void
@@ -1380,24 +1407,12 @@ Film::signal_changed (Property p)
 int
 Film::audio_channels () const
 {
-       boost::mutex::scoped_lock lm (_state_mutex);
-       
-       if (_use_content_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;
+       shared_ptr<AudioStream> s = audio_stream ();
+       if (!s) {
+               return 0;
        }
 
-       return 0;
+       return s->channels ();
 }
 
 void
@@ -1406,3 +1421,12 @@ Film::set_dci_date_today ()
        _dci_date = boost::gregorian::day_clock::local_day ();
 }
 
+boost::shared_ptr<AudioStream>
+Film::audio_stream () const
+{
+       if (use_content_audio()) {
+               return _content_audio_stream;
+       }
+
+       return _external_audio_stream;
+}
index 37eb13d253631a64c96a8aa27bd4ede747ad13b6..99957a00fdf019aca1e8d43eb76ae3fa68f965ff 100644 (file)
@@ -44,6 +44,7 @@ class Job;
 class Filter;
 class Log;
 class ExamineContentJob;
+class ExternalAudioStream;
 
 /** @class Film
  *  @brief A representation of a video with sound.
@@ -120,9 +121,9 @@ public:
                DCP_TRIM_START,
                DCP_TRIM_END,
                DCP_AB,
-               USE_CONTENT_AUDIO,
-               AUDIO_STREAM,
+               CONTENT_AUDIO_STREAM,
                EXTERNAL_AUDIO,
+               USE_CONTENT_AUDIO,
                AUDIO_GAIN,
                AUDIO_DELAY,
                STILL_DURATION,
@@ -134,7 +135,7 @@ public:
                THUMBS,
                SIZE,
                LENGTH,
-               AUDIO_STREAMS,
+               CONTENT_AUDIO_STREAMS,
                SUBTITLE_STREAMS,
                FRAMES_PER_SECOND,
        };
@@ -202,19 +203,19 @@ public:
                return _dcp_ab;
        }
 
-       bool use_content_audio () const {
+       boost::shared_ptr<AudioStream> content_audio_stream () const {
                boost::mutex::scoped_lock lm (_state_mutex);
-               return _use_content_audio;
+               return _content_audio_stream;
        }
 
-       boost::optional<AudioStream> audio_stream () const {
+       std::vector<std::string> external_audio () const {
                boost::mutex::scoped_lock lm (_state_mutex);
-               return _audio_stream;
+               return _external_audio;
        }
 
-       std::vector<std::string> external_audio () const {
+       bool use_content_audio () const {
                boost::mutex::scoped_lock lm (_state_mutex);
-               return _external_audio;
+               return _use_content_audio;
        }
        
        float audio_gain () const {
@@ -232,7 +233,7 @@ public:
                return _still_duration;
        }
 
-       boost::optional<SubtitleStream> subtitle_stream () const {
+       boost::shared_ptr<SubtitleStream> subtitle_stream () const {
                boost::mutex::scoped_lock lm (_state_mutex);
                return _subtitle_stream;
        }
@@ -307,12 +308,12 @@ public:
                return _content_digest;
        }
        
-       std::vector<AudioStream> audio_streams () const {
+       std::vector<boost::shared_ptr<AudioStream> > content_audio_streams () const {
                boost::mutex::scoped_lock lm (_state_mutex);
-               return _audio_streams;
+               return _content_audio_streams;
        }
 
-       std::vector<SubtitleStream> subtitle_streams () const {
+       std::vector<boost::shared_ptr<SubtitleStream> > subtitle_streams () const {
                boost::mutex::scoped_lock lm (_state_mutex);
                return _subtitle_streams;
        }
@@ -322,7 +323,9 @@ public:
                return _frames_per_second;
        }
 
+       boost::shared_ptr<AudioStream> audio_stream () const;
 
+       
        /* SET */
 
        void set_directory (std::string);
@@ -341,13 +344,13 @@ public:
        void set_dcp_trim_start (int);
        void set_dcp_trim_end (int);
        void set_dcp_ab (bool);
-       void set_use_content_audio (bool);
-       void set_audio_stream (boost::optional<AudioStream>);
+       void set_content_audio_stream (boost::shared_ptr<AudioStream>);
        void set_external_audio (std::vector<std::string>);
+       void set_use_content_audio (bool);
        void set_audio_gain (float);
        void set_audio_delay (int);
        void set_still_duration (int);
-       void set_subtitle_stream (boost::optional<SubtitleStream>);
+       void set_subtitle_stream (boost::shared_ptr<SubtitleStream>);
        void set_with_subtitles (bool);
        void set_subtitle_offset (int);
        void set_subtitle_scale (float);
@@ -363,8 +366,8 @@ public:
        void set_length (SourceFrame);
        void unset_length ();
        void set_content_digest (std::string);
-       void set_audio_streams (std::vector<AudioStream>);
-       void set_subtitle_streams (std::vector<SubtitleStream>);
+       void set_content_audio_streams (std::vector<boost::shared_ptr<AudioStream> >);
+       void set_subtitle_streams (std::vector<boost::shared_ptr<SubtitleStream> >);
        void set_frames_per_second (float);
 
        /** Emitted when some property has changed */
@@ -422,17 +425,16 @@ private:
            has the specified filters and post-processing.
        */
        bool _dcp_ab;
-       /** true to use the audio from the content file, false to use external audio */
-       bool _use_content_audio;
-       boost::optional<AudioStream> _audio_stream;
+       boost::shared_ptr<AudioStream> _content_audio_stream;
        std::vector<std::string> _external_audio;
+       bool _use_content_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;
-       boost::optional<SubtitleStream> _subtitle_stream;
+       boost::shared_ptr<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
@@ -461,10 +463,11 @@ private:
        boost::optional<SourceFrame> _length;
        /** MD5 digest of our content file */
        std::string _content_digest;
-       /** the audio streams that the source has */
-       std::vector<AudioStream> _audio_streams;
-       /** the subtitle streams that the source has */
-       std::vector<SubtitleStream> _subtitle_streams;
+       /** the audio streams in our content */
+       std::vector<boost::shared_ptr<AudioStream> > _content_audio_streams;
+       boost::shared_ptr<AudioStream> _external_audio_stream;
+       /** the subtitle streams that we can use */
+       std::vector<boost::shared_ptr<SubtitleStream> > _subtitle_streams;
        /** Frames per second of the source */
        float _frames_per_second;
 
index 847ee948617e979df7596d95c9fe854042a7a322..be1f96fd4d6e9a8d64a96daaf2e6f14608eff421 100644 (file)
@@ -46,6 +46,7 @@ using std::stringstream;
 using std::list;
 using std::vector;
 using std::pair;
+using std::cout;
 using boost::shared_ptr;
 using boost::thread;
 using boost::lexical_cast;
@@ -64,7 +65,7 @@ J2KWAVEncoder::J2KWAVEncoder (shared_ptr<const Film> f, shared_ptr<const Options
                */
                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());
+                       sf_info.samplerate = dcp_audio_sample_rate (_film->audio_stream()->sample_rate());
                        /* We write mono files */
                        sf_info.channels = 1;
                        sf_info.format = SF_FORMAT_WAV | SF_FORMAT_PCM_24;
@@ -229,22 +230,22 @@ J2KWAVEncoder::encoder_thread (ServerDescription* server)
 void
 J2KWAVEncoder::process_begin ()
 {
-       if (_film->audio_stream() && _film->audio_stream().get().sample_rate() != _film->target_audio_sample_rate()) {
+       if (_film->audio_stream() && _film->audio_stream()->sample_rate() != _film->target_audio_sample_rate()) {
 #ifdef HAVE_SWRESAMPLE
 
                stringstream s;
-               s << "Will resample audio from " << _film->audio_stream().get().sample_rate() << " to " << _film->target_audio_sample_rate();
+               s << "Will resample audio from " << _film->audio_stream()->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,
-                       _film->audio_stream().get().channel_layout(),
+                       _film->audio_stream()->channel_layout(),
                        AV_SAMPLE_FMT_FLTP,
                        _film->target_audio_sample_rate(),
-                       _film->audio_stream().get().channel_layout(),
+                       _film->audio_stream()->channel_layout(),
                        AV_SAMPLE_FMT_FLTP,
-                       _film->audio_stream().get().sample_rate(),
+                       _film->audio_stream()->sample_rate(),
                        0, 0
                        );
                
@@ -314,7 +315,7 @@ J2KWAVEncoder::process_end ()
 #if HAVE_SWRESAMPLE    
        if (_film->audio_stream() && _swr_context) {
 
-               shared_ptr<AudioBuffers> out (new AudioBuffers (_film->audio_stream().get().channels(), 256));
+               shared_ptr<AudioBuffers> out (new AudioBuffers (_film->audio_stream()->channels(), 256));
                        
                while (1) {
                        int const frames = swr_convert (_swr_context, (uint8_t **) out->data(), 256, 0, 0);
@@ -336,9 +337,9 @@ J2KWAVEncoder::process_end ()
 #endif
 
        if (_film->audio_stream()) {
-               int const dcp_sr = dcp_audio_sample_rate (_film->audio_stream().get().sample_rate ());
+               int const dcp_sr = dcp_audio_sample_rate (_film->audio_stream()->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));
+               shared_ptr<AudioBuffers> silence (new AudioBuffers (_film->audio_stream()->channels(), extra_audio_frames));
                silence->make_silent ();
                write_audio (silence);
                
@@ -364,9 +365,9 @@ J2KWAVEncoder::do_process_audio (shared_ptr<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_stream().get().sample_rate()) + 32;
+               int const max_resampled_frames = ceil ((int64_t) audio->frames() * _film->target_audio_sample_rate() / _film->audio_stream()->sample_rate()) + 32;
 
-               resampled.reset (new AudioBuffers (_film->audio_stream().get().channels(), max_resampled_frames));
+               resampled.reset (new AudioBuffers (_film->audio_stream()->channels(), max_resampled_frames));
 
                /* Resample audio */
                int const resampled_frames = swr_convert (
index 5bc184eb9cd5c15e4fbc8b71504c11f5d56e3af1..372077cd8eda9b4f8648819565505c0dece695ca 100644 (file)
 #include <sstream>
 #include "compose.hpp"
 #include "stream.h"
+#include "ffmpeg_decoder.h"
+#include "external_audio_decoder.h"
 
-using namespace std;
+using std::string;
+using std::stringstream;
+using boost::shared_ptr;
 using boost::optional;
 
-AudioStream::AudioStream (string t, optional<int> version)
+SubtitleStream::SubtitleStream (string t, boost::optional<int>)
 {
        stringstream n (t);
-       
-       int name_index = 3;
-       if (!version) {
-               name_index = 2;
-               int channels;
-               n >> _id >> channels;
-               _channel_layout = av_get_default_channel_layout (channels);
-               _sample_rate = 0;
-       } else {
-               /* Current (marked version 1) */
-               n >> _id >> _sample_rate >> _channel_layout;
-       }
+       n >> _id;
 
-       for (int i = 0; i < name_index; ++i) {
-               size_t const s = t.find (' ');
-               if (s != string::npos) {
-                       t = t.substr (s + 1);
-               }
+       size_t const s = t.find (' ');
+       if (s != string::npos) {
+               _name = t.substr (s + 1);
        }
-
-       _name = t;
 }
 
 string
-AudioStream::to_string () const
+SubtitleStream::to_string () const
 {
-       return String::compose ("%1 %2 %3 %4", _id, _sample_rate, _channel_layout, _name);
+       return String::compose ("%1 %2", _id, _name);
 }
 
-SubtitleStream::SubtitleStream (string t)
+shared_ptr<SubtitleStream>
+SubtitleStream::create (string t, optional<int> v)
 {
-       stringstream n (t);
-       n >> _id;
+       return shared_ptr<SubtitleStream> (new SubtitleStream (t, v));
+}
 
-       size_t const s = t.find (' ');
-       if (s != string::npos) {
-               _name = t.substr (s + 1);
+shared_ptr<AudioStream>
+audio_stream_factory (string t, optional<int> v)
+{
+       shared_ptr<AudioStream> s;
+
+       s = FFmpegAudioStream::create (t, v);
+       if (!s) {
+               s = ExternalAudioStream::create (t, v);
        }
+
+       return s;
 }
 
-string
-SubtitleStream::to_string () const
+shared_ptr<SubtitleStream>
+subtitle_stream_factory (string t, optional<int> v)
 {
-       return String::compose ("%1 %2", _id, _name);
+       return SubtitleStream::create (t, v);
 }
index 760befa022e8069bdab2b98a962645171dec701d..5dd91f5f4b56097c221e290434f3b96624821876 100644 (file)
@@ -21,6 +21,7 @@
 #define DVDOMATIC_STREAM_H
 
 #include <stdint.h>
+#include <boost/shared_ptr.hpp>
 #include <boost/optional.hpp>
 extern "C" {
 #include <libavutil/audioconvert.h>
@@ -29,48 +30,22 @@ extern "C" {
 class Stream
 {
 public:
-       Stream ()
-               : _id (-1)
-       {}
-       
-       Stream (std::string n, int i)
-               : _name (n)
-               , _id (i)
-       {}
-
        virtual std::string to_string () const = 0;
-       
-       std::string name () const {
-               return _name;
-       }
-
-       int id () const {
-               return _id;
-       }
-
-protected:
-       std::string _name;
-       int _id;
 };
 
 struct AudioStream : public Stream
 {
 public:
-       AudioStream (std::string t, boost::optional<int> v);
-       
-       AudioStream (std::string n, int id, int r, int64_t l)
-               : Stream (n, id)
-               , _sample_rate (r)
+       AudioStream (int r, int64_t l)
+               : _sample_rate (r)
                , _channel_layout (l)
        {}
 
-       /** Only used for state file version < 1 compatibility */
-       void set_sample_rate (int r) {
-               _sample_rate = r;
+       /* Only used for backwards compatibility for state file version < 1 */
+       void set_sample_rate (int s) {
+               _sample_rate = s;
        }
 
-       std::string to_string () const;
-
        int channels () const {
                return av_get_channel_layout_nb_channels (_channel_layout);
        }
@@ -83,7 +58,12 @@ public:
                return _channel_layout;
        }
 
-private:
+protected:
+       AudioStream ()
+               : _sample_rate (0)
+               , _channel_layout (0)
+       {}
+
        int _sample_rate;
        int64_t _channel_layout;
 };
@@ -91,13 +71,33 @@ private:
 class SubtitleStream : public Stream
 {
 public:
-       SubtitleStream (std::string t);
-
        SubtitleStream (std::string n, int i)
-               : Stream (n, i)
+               : _name (n)
+               , _id (i)
        {}
 
        std::string to_string () const;
+
+       std::string name () const {
+               return _name;
+       }
+
+       int id () const {
+               return _id;
+       }
+
+       static boost::shared_ptr<SubtitleStream> create (std::string t, boost::optional<int> v);
+
+private:
+       friend class stream_test;
+       
+       SubtitleStream (std::string t, boost::optional<int> v);
+       
+       std::string _name;
+       int _id;
 };
 
+boost::shared_ptr<AudioStream> audio_stream_factory (std::string t, boost::optional<int> version);
+boost::shared_ptr<SubtitleStream> subtitle_stream_factory (std::string t, boost::optional<int> version);
+
 #endif
index 2ee96790a270d9bc5c83235e6f42550f42cfcd91..cad76af6e5e7152cbb8ed6e98dd9737b33cc273e 100644 (file)
@@ -56,9 +56,9 @@ Transcoder::Transcoder (shared_ptr<Film> f, shared_ptr<const Options> o, Job* j,
        assert (_encoder);
 
        if (f->audio_stream()) {
-               AudioStream st = f->audio_stream().get();
-               _matcher.reset (new Matcher (f->log(), st.sample_rate(), f->frames_per_second()));
-               _delay_line.reset (new DelayLine (f->log(), st.channels(), f->audio_delay() * st.sample_rate() / 1000));
+               shared_ptr<AudioStream> st = f->audio_stream();
+               _matcher.reset (new Matcher (f->log(), st->sample_rate(), f->frames_per_second()));
+               _delay_line.reset (new DelayLine (f->log(), st->channels(), f->audio_delay() * st->sample_rate() / 1000));
                _gain.reset (new Gain (f->log(), f->audio_gain()));
        }
 
index ed9594692dca40ac3fd8f0afc336859c534f8ef0..aeb529702192a84119f6fc1abf4a86b837b2df87 100644 (file)
@@ -650,7 +650,7 @@ read_key_value (istream &s)
                if (line.empty ()) {
                        continue;
                }
-               
+
                if (line[0] == '#') {
                        continue;
                }
index 38a0e0e3462b108ed20fceaf2958b25497f7d26c..23a69f9586c08a96a39f6e6aa792640f1792de0d 100644 (file)
@@ -84,7 +84,7 @@ VideoDecoder::emit_subtitle (shared_ptr<TimedSubtitle> s)
 }
 
 void
-VideoDecoder::set_subtitle_stream (optional<SubtitleStream> s)
+VideoDecoder::set_subtitle_stream (shared_ptr<SubtitleStream> s)
 {
        _subtitle_stream = s;
 }
index fb782cd7eaea8ed30b4f136b56584be7c1d53f5f..5acce7c8d806701ec557140de9a42b465c0b2114 100644 (file)
@@ -39,7 +39,7 @@ public:
        virtual int sample_aspect_ratio_numerator () const = 0;
        virtual int sample_aspect_ratio_denominator () const = 0;
 
-       virtual void set_subtitle_stream (boost::optional<SubtitleStream>);
+       virtual void set_subtitle_stream (boost::shared_ptr<SubtitleStream>);
 
        void set_progress () const;
        
@@ -47,11 +47,11 @@ public:
                return _video_frame;
        }
 
-       boost::optional<SubtitleStream> subtitle_stream () const {
+       boost::shared_ptr<SubtitleStream> subtitle_stream () const {
                return _subtitle_stream;
        }
 
-       std::vector<SubtitleStream> subtitle_streams () const {
+       std::vector<boost::shared_ptr<SubtitleStream> > subtitle_streams () const {
                return _subtitle_streams;
        }
 
@@ -63,8 +63,8 @@ protected:
        void emit_subtitle (boost::shared_ptr<TimedSubtitle>);
        void repeat_last_video ();
 
-       boost::optional<SubtitleStream> _subtitle_stream;
-       std::vector<SubtitleStream> _subtitle_streams;
+       boost::shared_ptr<SubtitleStream> _subtitle_stream;
+       std::vector<boost::shared_ptr<SubtitleStream> > _subtitle_streams;
 
 private:
        void signal_video (boost::shared_ptr<Image>, boost::shared_ptr<Subtitle>);
index 6602435c492ee178930270c048b8dcc61d6b74ff..f2831d75abf4e692410425c659d7a8681cc3c694 100644 (file)
@@ -37,6 +37,8 @@
 #include "lib/filter.h"
 #include "lib/screen.h"
 #include "lib/config.h"
+#include "lib/ffmpeg_decoder.h"
+#include "lib/external_audio_decoder.h"
 #include "filter_dialog.h"
 #include "wx_util.h"
 #include "film_editor.h"
@@ -54,6 +56,7 @@ using std::setprecision;
 using std::list;
 using std::vector;
 using boost::shared_ptr;
+using boost::dynamic_pointer_cast;
 
 /** @param f Film to edit */
 FilmEditor::FilmEditor (shared_ptr<Film> f, wxWindow* parent)
@@ -485,11 +488,13 @@ FilmEditor::film_changed (Film::Property p)
                setup_subtitle_control_sensitivity ();
                setup_streams ();
                break;
-       case Film::AUDIO_STREAMS:
        case Film::SUBTITLE_STREAMS:
                setup_subtitle_control_sensitivity ();
                setup_streams ();
                break;
+       case Film::CONTENT_AUDIO_STREAMS:
+               setup_streams ();
+               break;
        case Film::FORMAT:
        {
                int n = 0;
@@ -600,31 +605,33 @@ FilmEditor::film_changed (Film::Property p)
        case Film::DCI_METADATA:
                _dcp_name->SetLabel (std_to_wx (_film->dcp_name ()));
                break;
-       case Film::AUDIO_STREAM:
-               if (_film->audio_stream()) {
-                       checked_set (_audio_stream, _film->audio_stream().get().to_string());
-               } else {
-                       checked_set (_audio_stream, wxNOT_FOUND);
+       case Film::CONTENT_AUDIO_STREAM:
+               if (_film->content_audio_stream()) {
+                       checked_set (_audio_stream, _film->content_audio_stream()->to_string());
                }
                _dcp_name->SetLabel (std_to_wx (_film->dcp_name ()));
                setup_audio_details ();
+               setup_audio_control_sensitivity ();
+               break;
+       case Film::USE_CONTENT_AUDIO:
+               checked_set (_use_content_audio, _film->use_content_audio());
+               checked_set (_use_external_audio, !_film->use_content_audio());
+               _dcp_name->SetLabel (std_to_wx (_film->dcp_name ()));
+               setup_audio_details ();
+               setup_audio_control_sensitivity ();
                break;
        case Film::SUBTITLE_STREAM:
                if (_film->subtitle_stream()) {
-                       checked_set (_subtitle_stream, _film->subtitle_stream().get().to_string());
+                       checked_set (_subtitle_stream, _film->subtitle_stream()->to_string());
                }
                break;
-       case Film::USE_CONTENT_AUDIO:
-               checked_set (_use_content_audio, _film->use_content_audio ());
-               checked_set (_use_external_audio, !_film->use_content_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]);
                }
+               setup_audio_details ();
                break;
        }
        }
@@ -688,9 +695,9 @@ 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_CONTENT_AUDIO);
-       film_changed (Film::AUDIO_STREAM);
+       film_changed (Film::CONTENT_AUDIO_STREAM);
        film_changed (Film::EXTERNAL_AUDIO);
+       film_changed (Film::USE_CONTENT_AUDIO);
        film_changed (Film::AUDIO_GAIN);
        film_changed (Film::AUDIO_DELAY);
        film_changed (Film::WITH_SUBTITLES);
@@ -699,7 +706,7 @@ FilmEditor::set_film (shared_ptr<Film> f)
        film_changed (Film::DCI_METADATA);
        film_changed (Film::SIZE);
        film_changed (Film::LENGTH);
-       film_changed (Film::AUDIO_STREAMS);
+       film_changed (Film::CONTENT_AUDIO_STREAMS);
        film_changed (Film::SUBTITLE_STREAMS);
        film_changed (Film::FRAMES_PER_SECOND);
 }
@@ -965,24 +972,23 @@ void
 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()), new wxStringClientData (std_to_wx (i->to_string ())));
+       vector<shared_ptr<AudioStream> > a = _film->content_audio_streams ();
+       for (vector<shared_ptr<AudioStream> >::iterator i = a.begin(); i != a.end(); ++i) {
+               shared_ptr<FFmpegAudioStream> ffa = dynamic_pointer_cast<FFmpegAudioStream> (*i);
+               _audio_stream->Append (std_to_wx (ffa->name()), new wxStringClientData (std_to_wx (ffa->to_string ())));
        }
        
-       if (_film->audio_stream()) {
-               checked_set (_audio_stream, _film->audio_stream().get().to_string());
-       } else {
-               _audio_stream->SetValue (wxT (""));
+       if (_film->use_content_audio() && _film->audio_stream()) {
+               checked_set (_audio_stream, _film->audio_stream()->to_string());
        }
 
        _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()), new wxStringClientData (std_to_wx (i->to_string ())));
+       vector<shared_ptr<SubtitleStream> > s = _film->subtitle_streams ();
+       for (vector<shared_ptr<SubtitleStream> >::iterator i = s.begin(); i != s.end(); ++i) {
+               _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());
+               checked_set (_subtitle_stream, _film->subtitle_stream()->to_string());
        } else {
                _subtitle_stream->SetValue (wxT (""));
        }
@@ -995,8 +1001,8 @@ FilmEditor::audio_stream_changed (wxCommandEvent &)
                return;
        }
 
-       _film->set_audio_stream (
-               AudioStream (
+       _film->set_content_audio_stream (
+               audio_stream_factory (
                        string_client_data (_audio_stream->GetClientObject (_audio_stream->GetSelection ())),
                        Film::state_version
                        )
@@ -1010,7 +1016,12 @@ FilmEditor::subtitle_stream_changed (wxCommandEvent &)
                return;
        }
 
-       _film->set_subtitle_stream (SubtitleStream (string_client_data (_subtitle_stream->GetClientObject (_subtitle_stream->GetSelection ()))));
+       _film->set_subtitle_stream (
+               subtitle_stream_factory (
+                       string_client_data (_subtitle_stream->GetClientObject (_subtitle_stream->GetSelection ())),
+                       Film::state_version
+                       )
+               );
 }
 
 void
@@ -1020,7 +1031,7 @@ FilmEditor::setup_audio_details ()
                _audio->SetLabel (wxT (""));
        } else {
                stringstream s;
-               s << _film->audio_stream().get().channels () << " channels, " << _film->audio_stream().get().sample_rate() << "Hz";
+               s << _film->audio_stream()->channels () << " channels, " << _film->audio_stream()->sample_rate() << "Hz";
                _audio->SetLabel (std_to_wx (s.str ()));
        }
 }
@@ -1034,8 +1045,7 @@ FilmEditor::active_jobs_changed (bool a)
 void
 FilmEditor::use_audio_changed (wxCommandEvent &)
 {
-       _film->set_use_content_audio (_use_content_audio->GetValue ());
-       setup_audio_control_sensitivity ();
+       _film->set_use_content_audio (_use_content_audio->GetValue());
 }
 
 void
index af6e9fddbffdeb4ac34af1092abb7ee039d2b9ee..3f129c6e269318da147f87b83f74ac0ef8242754 100644 (file)
@@ -1,5 +1,6 @@
+version 1
 name fred
-use_dci_name 0
+use_dci_name 1
 content 
 dcp_content_type Short
 format 185
@@ -31,4 +32,5 @@ width 0
 height 0
 length 0
 content_digest 
+external_audio_stream external 0 0
 frames_per_second 0
index f8f51ddf538803935d121ad92915302c44918a07..87ec15be9886e38bb93a0edd0fb532ec6955ea6e 100644 (file)
@@ -38,6 +38,8 @@
 #include "job.h"
 #include "subtitle.h"
 #include "scaler.h"
+#include "ffmpeg_decoder.h"
+#include "external_audio_decoder.h"
 #define BOOST_TEST_DYN_LINK
 #define BOOST_TEST_MODULE dvdomatic_test
 #include <boost/test/unit_test.hpp>
@@ -48,6 +50,7 @@ using std::stringstream;
 using std::vector;
 using boost::shared_ptr;
 using boost::thread;
+using boost::dynamic_pointer_cast;
 
 void
 setup_test_config ()
@@ -129,6 +132,39 @@ BOOST_AUTO_TEST_CASE (film_metadata_test)
        BOOST_CHECK_EQUAL (::system (s.str().c_str ()), 0);
 }
 
+BOOST_AUTO_TEST_CASE (stream_test)
+{
+       FFmpegAudioStream a ("ffmpeg 4 44100 1 hello there world", boost::optional<int> (1));
+       BOOST_CHECK_EQUAL (a.id(), 4);
+       BOOST_CHECK_EQUAL (a.sample_rate(), 44100);
+       BOOST_CHECK_EQUAL (a.channel_layout(), 1);
+       BOOST_CHECK_EQUAL (a.name(), "hello there world");
+       BOOST_CHECK_EQUAL (a.to_string(), "ffmpeg 4 44100 1 hello there world");
+
+       ExternalAudioStream e ("external 44100 1", boost::optional<int> (1));
+       BOOST_CHECK_EQUAL (e.sample_rate(), 44100);
+       BOOST_CHECK_EQUAL (e.channel_layout(), 1);
+       BOOST_CHECK_EQUAL (e.to_string(), "external 44100 1");
+
+       SubtitleStream s ("5 a b c", boost::optional<int> (1));
+       BOOST_CHECK_EQUAL (s.id(), 5);
+       BOOST_CHECK_EQUAL (s.name(), "a b c");
+
+       shared_ptr<AudioStream> ff = audio_stream_factory ("ffmpeg 4 44100 1 hello there world", boost::optional<int> (1));
+       shared_ptr<FFmpegAudioStream> cff = dynamic_pointer_cast<FFmpegAudioStream> (ff);
+       BOOST_CHECK (cff);
+       BOOST_CHECK_EQUAL (cff->id(), 4);
+       BOOST_CHECK_EQUAL (cff->sample_rate(), 44100);
+       BOOST_CHECK_EQUAL (cff->channel_layout(), 1);
+       BOOST_CHECK_EQUAL (cff->name(), "hello there world");
+       BOOST_CHECK_EQUAL (cff->to_string(), "ffmpeg 4 44100 1 hello there world");
+
+       shared_ptr<AudioStream> fe = audio_stream_factory ("external 44100 1", boost::optional<int> (1));
+       BOOST_CHECK_EQUAL (fe->sample_rate(), 44100);
+       BOOST_CHECK_EQUAL (fe->channel_layout(), 1);
+       BOOST_CHECK_EQUAL (fe->to_string(), "external 44100 1");
+}
+
 BOOST_AUTO_TEST_CASE (format_test)
 {
        Format::setup_formats ();
@@ -423,21 +459,21 @@ BOOST_AUTO_TEST_CASE (audio_sampling_rate_test)
        shared_ptr<Film> f = new_test_film ("audio_sampling_rate_test");
        f->set_frames_per_second (24);
 
-       f->set_audio_stream (AudioStream ("a", 42, 48000, 0));
+       f->set_content_audio_stream (shared_ptr<AudioStream> (new FFmpegAudioStream ("a", 42, 48000, 0)));
        BOOST_CHECK_EQUAL (f->target_audio_sample_rate(), 48000);
 
-       f->set_audio_stream (AudioStream ("a", 42, 44100, 0));
+       f->set_content_audio_stream (shared_ptr<AudioStream> (new FFmpegAudioStream ("a", 42, 44100, 0)));
        BOOST_CHECK_EQUAL (f->target_audio_sample_rate(), 48000);
 
-       f->set_audio_stream (AudioStream ("a", 42, 80000, 0));
+       f->set_content_audio_stream (shared_ptr<AudioStream> (new FFmpegAudioStream ("a", 42, 80000, 0)));
        BOOST_CHECK_EQUAL (f->target_audio_sample_rate(), 96000);
 
        f->set_frames_per_second (23.976);
-       f->set_audio_stream (AudioStream ("a", 42, 48000, 0));
+       f->set_content_audio_stream (shared_ptr<AudioStream> (new FFmpegAudioStream ("a", 42, 48000, 0)));
        BOOST_CHECK_EQUAL (f->target_audio_sample_rate(), 47952);
 
        f->set_frames_per_second (29.97);
-       f->set_audio_stream (AudioStream ("a", 42, 48000, 0));
+       f->set_content_audio_stream (shared_ptr<AudioStream> (new FFmpegAudioStream ("a", 42, 48000, 0)));
        BOOST_CHECK_EQUAL (f->target_audio_sample_rate(), 47952);
 }
 
@@ -518,16 +554,3 @@ BOOST_AUTO_TEST_CASE (job_manager_test)
        BOOST_CHECK_EQUAL (b->running(), false);
 }
 
-BOOST_AUTO_TEST_CASE (stream_test)
-{
-       AudioStream a ("4 44100 1 hello there world", boost::optional<int> (1));
-       BOOST_CHECK_EQUAL (a.id(), 4);
-       BOOST_CHECK_EQUAL (a.sample_rate(), 44100);
-       BOOST_CHECK_EQUAL (a.channel_layout(), 1);
-       BOOST_CHECK_EQUAL (a.name(), "hello there world");
-       BOOST_CHECK_EQUAL (a.to_string(), "4 44100 1 hello there world");
-
-       SubtitleStream s ("5 a b c");
-       BOOST_CHECK_EQUAL (s.id(), 5);
-       BOOST_CHECK_EQUAL (s.name(), "a b c");
-}