diff options
| author | Carl Hetherington <cth@carlh.net> | 2013-03-31 15:09:49 +0100 |
|---|---|---|
| committer | Carl Hetherington <cth@carlh.net> | 2013-03-31 15:09:49 +0100 |
| commit | 127672223cca569986e35c91265e269ed5a6561c (patch) | |
| tree | 853793c1b929d4c38ebdf5456808e466083989b7 /src/lib | |
| parent | ed78fd3d138114185e43edf81ffe91db17377da0 (diff) | |
Runs.
Diffstat (limited to 'src/lib')
57 files changed, 1103 insertions, 1289 deletions
diff --git a/src/lib/ab_transcode_job.cc b/src/lib/ab_transcode_job.cc index 4ffdd9af6..f17d43916 100644 --- a/src/lib/ab_transcode_job.cc +++ b/src/lib/ab_transcode_job.cc @@ -54,7 +54,7 @@ ABTranscodeJob::run () { try { /* _film_b is the one with reference filters */ - ABTranscoder w (_film_b, _film, _decode_opt, this, shared_ptr<Encoder> (new Encoder (_film))); + ABTranscoder w (_film_b, _film, _decode_opt, shared_from_this ()); w.go (); set_progress (1); set_state (FINISHED_OK); diff --git a/src/lib/ab_transcode_job.h b/src/lib/ab_transcode_job.h index 8e3cbe2d8..5d029a18b 100644 --- a/src/lib/ab_transcode_job.h +++ b/src/lib/ab_transcode_job.h @@ -46,8 +46,7 @@ public: void run (); private: - DecodeOptions _decode_opt; - /** Copy of our Film using the reference filters and scaler */ boost::shared_ptr<Film> _film_b; + DecodeOptions _decode_opt; }; diff --git a/src/lib/ab_transcoder.cc b/src/lib/ab_transcoder.cc index 3a1cd83d7..0c687008d 100644 --- a/src/lib/ab_transcoder.cc +++ b/src/lib/ab_transcoder.cc @@ -21,13 +21,11 @@ #include <boost/shared_ptr.hpp> #include "ab_transcoder.h" #include "film.h" -#include "video_decoder.h" -#include "audio_decoder.h" #include "encoder.h" #include "job.h" #include "options.h" #include "image.h" -#include "decoder_factory.h" +#include "playlist.h" #include "matcher.h" #include "delay_line.h" #include "gain.h" @@ -49,31 +47,23 @@ using boost::dynamic_pointer_cast; * @param e Encoder to use. */ -ABTranscoder::ABTranscoder ( - shared_ptr<Film> a, shared_ptr<Film> b, DecodeOptions o, Job* j, shared_ptr<Encoder> e) +ABTranscoder::ABTranscoder (shared_ptr<Film> a, shared_ptr<Film> b, DecodeOptions o, shared_ptr<Job> j) : _film_a (a) , _film_b (b) + , _playlist_a (_film_a->playlist ()) + , _playlist_b (_film_b->playlist ()) , _job (j) - , _encoder (e) + , _encoder (new Encoder (_film_a, _playlist_a)) , _combiner (new Combiner (a->log())) { - _da = decoder_factory (_film_a, o); - _db = decoder_factory (_film_b, o); - - if (_film_a->audio_stream()) { - shared_ptr<AudioStream> st = _film_a->audio_stream(); - _matcher.reset (new Matcher (_film_a->log(), st->sample_rate(), _film_a->source_frame_rate())); - _delay_line.reset (new DelayLine (_film_a->log(), st->channels(), _film_a->audio_delay() * st->sample_rate() / 1000)); + if (_playlist_a->has_audio ()) { + _matcher.reset (new Matcher (_film_a->log(), _playlist_a->audio_frame_rate(), _playlist_a->video_frame_rate())); + _delay_line.reset (new DelayLine (_film_a->log(), _playlist_a->audio_channels(), _film_a->audio_delay() * _playlist_a->audio_frame_rate() / 1000)); _gain.reset (new Gain (_film_a->log(), _film_a->audio_gain())); } - /* Set up the decoder to use the film's set streams */ - _da.video->set_subtitle_stream (_film_a->subtitle_stream ()); - _db.video->set_subtitle_stream (_film_a->subtitle_stream ()); - _da.audio->set_audio_stream (_film_a->audio_stream ()); - - _da.video->Video.connect (bind (&Combiner::process_video, _combiner, _1, _2, _3)); - _db.video->Video.connect (bind (&Combiner::process_video_b, _combiner, _1, _2, _3)); + _playlist_a->Video.connect (bind (&Combiner::process_video, _combiner, _1, _2, _3)); + _playlist_b->Video.connect (bind (&Combiner::process_video_b, _combiner, _1, _2, _3)); if (_matcher) { _combiner->connect_video (_matcher); @@ -83,7 +73,7 @@ ABTranscoder::ABTranscoder ( } if (_matcher && _delay_line) { - _da.audio->connect_audio (_delay_line); + _playlist_a->connect_audio (_delay_line); _delay_line->connect_audio (_matcher); _matcher->connect_audio (_gain); _gain->connect_audio (_encoder); @@ -95,23 +85,17 @@ ABTranscoder::go () { _encoder->process_begin (); - bool done[3] = { false, false, false }; + bool done[2] = { false, false }; while (1) { - done[0] = _da.video->pass (); - done[1] = _db.video->pass (); - - if (!done[2] && _da.audio && dynamic_pointer_cast<Decoder> (_da.audio) != dynamic_pointer_cast<Decoder> (_da.video)) { - done[2] = _da.audio->pass (); - } else { - done[2] = true; - } + done[0] = _playlist_a->pass (); + done[1] = _playlist_b->pass (); if (_job) { - _da.video->set_progress (_job); + _playlist_a->set_progress (_job); } - if (done[0] && done[1] && done[2]) { + if (done[0] && done[1]) { break; } } diff --git a/src/lib/ab_transcoder.h b/src/lib/ab_transcoder.h index 58a08af04..14277c562 100644 --- a/src/lib/ab_transcoder.h +++ b/src/lib/ab_transcoder.h @@ -29,16 +29,14 @@ class Job; class Encoder; -class VideoDecoder; -class AudioDecoder; class Image; class Log; -class Subtitle; class Film; class Matcher; class DelayLine; class Gain; class Combiner; +class Playlist; /** @class ABTranscoder * @brief A transcoder which uses one Film for the left half of the screen, and a different one @@ -51,8 +49,7 @@ public: boost::shared_ptr<Film> a, boost::shared_ptr<Film> b, DecodeOptions o, - Job* j, - boost::shared_ptr<Encoder> e + boost::shared_ptr<Job> j ); void go (); @@ -60,10 +57,10 @@ public: private: boost::shared_ptr<Film> _film_a; boost::shared_ptr<Film> _film_b; - Job* _job; + boost::shared_ptr<Playlist> _playlist_a; + boost::shared_ptr<Playlist> _playlist_b; + boost::shared_ptr<Job> _job; boost::shared_ptr<Encoder> _encoder; - Decoders _da; - Decoders _db; boost::shared_ptr<Combiner> _combiner; boost::shared_ptr<Matcher> _matcher; boost::shared_ptr<DelayLine> _delay_line; diff --git a/src/lib/analyse_audio_job.cc b/src/lib/analyse_audio_job.cc index 43eecbcbd..74491943b 100644 --- a/src/lib/analyse_audio_job.cc +++ b/src/lib/analyse_audio_job.cc @@ -22,8 +22,7 @@ #include "compose.hpp" #include "film.h" #include "options.h" -#include "decoder_factory.h" -#include "audio_decoder.h" +#include "playlist.h" #include "i18n.h" @@ -52,29 +51,18 @@ AnalyseAudioJob::name () const void AnalyseAudioJob::run () { - if (!_film->audio_stream () || !_film->length()) { - set_progress (1); - set_state (FINISHED_ERROR); - return; - } - - DecodeOptions options; - options.decode_video = false; - - Decoders decoders = decoder_factory (_film, options); - assert (decoders.audio); + shared_ptr<Playlist> playlist = _film->playlist (); + playlist->disable_video (); - decoders.audio->set_audio_stream (_film->audio_stream ()); - decoders.audio->Audio.connect (bind (&AnalyseAudioJob::audio, this, _1)); + playlist->Audio.connect (bind (&AnalyseAudioJob::audio, this, _1)); - int64_t total_audio_frames = video_frames_to_audio_frames (_film->length().get(), _film->audio_stream()->sample_rate(), _film->source_frame_rate()); - _samples_per_point = max (int64_t (1), total_audio_frames / _num_points); + _samples_per_point = max (int64_t (1), playlist->audio_length() / _num_points); - _current.resize (_film->audio_stream()->channels ()); - _analysis.reset (new AudioAnalysis (_film->audio_stream()->channels())); + _current.resize (playlist->audio_channels ()); + _analysis.reset (new AudioAnalysis (playlist->audio_channels())); - while (!decoders.audio->pass()) { - set_progress (float (_done) / total_audio_frames); + while (!playlist->pass()) { + set_progress (float (_done) / playlist->audio_length ()); } _analysis->write (_film->audio_analysis_path ()); diff --git a/src/lib/audio_content.cc b/src/lib/audio_content.cc new file mode 100644 index 000000000..e9eacfd79 --- /dev/null +++ b/src/lib/audio_content.cc @@ -0,0 +1,7 @@ +#include "audio_content.h" + +AudioContent::AudioContent (boost::filesystem::path f) + : Content (f) +{ + +} diff --git a/src/lib/audio_content.h b/src/lib/audio_content.h index 95fd681a7..e18d1082e 100644 --- a/src/lib/audio_content.h +++ b/src/lib/audio_content.h @@ -1,8 +1,19 @@ +#ifndef DVDOMATIC_AUDIO_CONTENT_H +#define DVDOMATIC_AUDIO_CONTENT_H + #include "content.h" +#include "util.h" class AudioContent : public virtual Content { public: - + AudioContent (boost::filesystem::path); + virtual int audio_channels () const = 0; + virtual ContentAudioFrame audio_length () const = 0; + virtual int audio_frame_rate () const = 0; + virtual int64_t audio_channel_layout () const = 0; + }; + +#endif diff --git a/src/lib/audio_decoder.cc b/src/lib/audio_decoder.cc index a54c14843..a72cf11bb 100644 --- a/src/lib/audio_decoder.cc +++ b/src/lib/audio_decoder.cc @@ -18,19 +18,12 @@ */ #include "audio_decoder.h" -#include "stream.h" using boost::optional; using boost::shared_ptr; -AudioDecoder::AudioDecoder (shared_ptr<Film> f, DecodeOptions o) +AudioDecoder::AudioDecoder (shared_ptr<const Film> f, shared_ptr<AudioContent> c, DecodeOptions o) : Decoder (f, o) { } - -void -AudioDecoder::set_audio_stream (shared_ptr<AudioStream> s) -{ - _audio_stream = s; -} diff --git a/src/lib/audio_decoder.h b/src/lib/audio_decoder.h index 9bef8e0e7..7d2a2bb62 100644 --- a/src/lib/audio_decoder.h +++ b/src/lib/audio_decoder.h @@ -25,34 +25,17 @@ #define DVDOMATIC_AUDIO_DECODER_H #include "audio_source.h" -#include "stream.h" #include "decoder.h" +class AudioContent; + /** @class AudioDecoder. * @brief Parent class for audio decoders. */ class AudioDecoder : public AudioSource, public virtual Decoder { public: - AudioDecoder (boost::shared_ptr<Film>, DecodeOptions); - - virtual void set_audio_stream (boost::shared_ptr<AudioStream>); - - /** @return Audio stream that we are using */ - boost::shared_ptr<AudioStream> audio_stream () const { - return _audio_stream; - } - - /** @return All available audio streams */ - std::vector<boost::shared_ptr<AudioStream> > audio_streams () const { - return _audio_streams; - } - -protected: - /** Audio stream that we are using */ - boost::shared_ptr<AudioStream> _audio_stream; - /** All available audio streams */ - std::vector<boost::shared_ptr<AudioStream> > _audio_streams; + AudioDecoder (boost::shared_ptr<const Film>, boost::shared_ptr<AudioContent>, DecodeOptions); }; #endif diff --git a/src/lib/content.cc b/src/lib/content.cc new file mode 100644 index 000000000..2fb94e959 --- /dev/null +++ b/src/lib/content.cc @@ -0,0 +1,20 @@ +#include <boost/thread/mutex.hpp> +#include "content.h" +#include "util.h" + +using std::string; +using boost::shared_ptr; + +Content::Content (boost::filesystem::path f) + : _file (f) +{ + +} + +void +Content::examine (shared_ptr<Film>, shared_ptr<Job>, bool) +{ + string const d = md5_digest (_file); + boost::mutex::scoped_lock lm (_mutex); + _digest = d; +} diff --git a/src/lib/content.h b/src/lib/content.h index c848860aa..25c097424 100644 --- a/src/lib/content.h +++ b/src/lib/content.h @@ -1,17 +1,34 @@ +#ifndef DVDOMATIC_CONTENT_H +#define DVDOMATIC_CONTENT_H + #include <boost/filesystem.hpp> +#include <boost/signals2.hpp> #include <boost/thread/mutex.hpp> +class Job; +class Film; + class Content { public: + Content (boost::filesystem::path); + + virtual void examine (boost::shared_ptr<Film>, boost::shared_ptr<Job>, bool); + virtual std::string summary () const = 0; + boost::filesystem::path file () const { boost::mutex::scoped_lock lm (_mutex); return _file; } + boost::signals2::signal<void (int)> Changed; + protected: - boost::mutex _mutex; + mutable boost::mutex _mutex; private: boost::filesystem::path _file; + std::string _digest; }; + +#endif diff --git a/src/lib/decoder.cc b/src/lib/decoder.cc index 52b22fa06..2fe265c14 100644 --- a/src/lib/decoder.cc +++ b/src/lib/decoder.cc @@ -22,34 +22,21 @@ */ #include <iostream> -#include <stdint.h> -#include <boost/lexical_cast.hpp> #include "film.h" -#include "format.h" #include "options.h" #include "exceptions.h" -#include "image.h" #include "util.h" -#include "log.h" #include "decoder.h" -#include "delay_line.h" -#include "subtitle.h" -#include "filter_graph.h" #include "i18n.h" using std::string; -using std::stringstream; -using std::min; -using std::pair; -using std::list; using boost::shared_ptr; -using boost::optional; /** @param f Film. * @param o Decode options. */ -Decoder::Decoder (boost::shared_ptr<Film> f, DecodeOptions o) +Decoder::Decoder (shared_ptr<const Film> f, DecodeOptions o) : _film (f) , _opt (o) { diff --git a/src/lib/decoder.h b/src/lib/decoder.h index f2f523516..50aa16dba 100644 --- a/src/lib/decoder.h +++ b/src/lib/decoder.h @@ -30,7 +30,6 @@ #include <boost/shared_ptr.hpp> #include <boost/signals2.hpp> #include "util.h" -#include "stream.h" #include "video_source.h" #include "audio_source.h" #include "film.h" @@ -53,7 +52,7 @@ class FilterGraph; class Decoder { public: - Decoder (boost::shared_ptr<Film>, DecodeOptions); + Decoder (boost::shared_ptr<const Film>, DecodeOptions); virtual ~Decoder () {} virtual bool pass () = 0; @@ -63,14 +62,13 @@ public: boost::signals2::signal<void()> OutputChanged; protected: - /** our Film */ - boost::shared_ptr<Film> _film; + boost::shared_ptr<const Film> _film; /** our decode options */ DecodeOptions _opt; private: virtual void film_changed (Film::Property) {} - + boost::signals2::scoped_connection _film_connection; }; diff --git a/src/lib/decoder_factory.cc b/src/lib/decoder_factory.cc index f7f9f4074..feaa1c7ef 100644 --- a/src/lib/decoder_factory.cc +++ b/src/lib/decoder_factory.cc @@ -39,22 +39,5 @@ decoder_factory ( shared_ptr<Film> f, DecodeOptions o ) { - if (f->content().empty()) { - return Decoders (); - } - - if (boost::filesystem::is_directory (f->content_path()) || f->content_type() == STILL) { - /* A single image file, or a directory of them */ - return Decoders ( - shared_ptr<VideoDecoder> (new ImageMagickDecoder (f, o)), - shared_ptr<AudioDecoder> (new SndfileDecoder (f, o)) - ); - } - - shared_ptr<FFmpegDecoder> fd (new FFmpegDecoder (f, o)); - if (f->use_content_audio()) { - return Decoders (fd, fd); - } - - return Decoders (fd, shared_ptr<AudioDecoder> (new SndfileDecoder (f, o))); + return Decoders (); } diff --git a/src/lib/encoder.cc b/src/lib/encoder.cc index 7b338407e..970213793 100644 --- a/src/lib/encoder.cc +++ b/src/lib/encoder.cc @@ -38,6 +38,7 @@ #include "format.h" #include "cross.h" #include "writer.h" +#include "playlist.h" #include "i18n.h" @@ -53,8 +54,9 @@ using namespace boost; int const Encoder::_history_size = 25; /** @param f Film that we are encoding */ -Encoder::Encoder (shared_ptr<Film> f) +Encoder::Encoder (shared_ptr<Film> f, shared_ptr<Playlist> p) : _film (f) + , _playlist (p) , _video_frames_in (0) , _video_frames_out (0) #ifdef HAVE_SWRESAMPLE @@ -77,22 +79,22 @@ Encoder::~Encoder () void Encoder::process_begin () { - if (_film->audio_stream() && _film->audio_stream()->sample_rate() != _film->target_audio_sample_rate()) { + if (_playlist->has_audio() && _playlist->audio_frame_rate() != _film->target_audio_sample_rate()) { #ifdef HAVE_SWRESAMPLE stringstream s; - s << String::compose (N_("Will resample audio from %1 to %2"), _film->audio_stream()->sample_rate(), _film->target_audio_sample_rate()); + s << String::compose (N_("Will resample audio from %1 to %2"), _playlist->audio_frame_rate(), _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()->channel_layout(), + _playlist->audio_channel_layout(), AV_SAMPLE_FMT_FLTP, _film->target_audio_sample_rate(), - _film->audio_stream()->channel_layout(), + _playlist->audio_channel_layout(), AV_SAMPLE_FMT_FLTP, - _film->audio_stream()->sample_rate(), + _playlist->audio_frame_rate(), 0, 0 ); @@ -118,7 +120,7 @@ Encoder::process_begin () } } - _writer.reset (new Writer (_film)); + _writer.reset (new Writer (_film, _playlist)); } @@ -126,9 +128,9 @@ void Encoder::process_end () { #if HAVE_SWRESAMPLE - if (_film->audio_stream() && _film->audio_stream()->channels() && _swr_context) { + if (_playlist->has_audio() && _playlist->audio_channels() && _swr_context) { - shared_ptr<AudioBuffers> out (new AudioBuffers (_film->audio_stream()->channels(), 256)); + shared_ptr<AudioBuffers> out (new AudioBuffers (_playlist->audio_channels(), 256)); while (1) { int const frames = swr_convert (_swr_context, (uint8_t **) out->data(), 256, 0, 0); @@ -233,7 +235,7 @@ Encoder::frame_done () void Encoder::process_video (shared_ptr<Image> image, bool same, boost::shared_ptr<Subtitle> sub) { - FrameRateConversion frc (_film->source_frame_rate(), _film->dcp_frame_rate()); + FrameRateConversion frc (_playlist->video_frame_rate(), _film->dcp_frame_rate()); if (frc.skip && (_video_frames_in % 2)) { ++_video_frames_in; @@ -271,7 +273,7 @@ Encoder::process_video (shared_ptr<Image> image, bool same, boost::shared_ptr<Su TIMING ("adding to queue of %1", _queue.size ()); _queue.push_back (boost::shared_ptr<DCPVideoFrame> ( new DCPVideoFrame ( - image, sub, _film->format()->dcp_size(), _film->format()->dcp_padding (_film), + image, sub, _film->format()->dcp_size(), _film->format()->dcp_padding (_playlist), _film->subtitle_offset(), _film->subtitle_scale(), _film->scaler(), _video_frames_out, _film->dcp_frame_rate(), s.second, _film->colour_lut(), _film->j2k_bandwidth(), @@ -301,9 +303,9 @@ Encoder::process_audio (shared_ptr<AudioBuffers> data) if (_swr_context) { /* Compute the resampled frames count and add 32 for luck */ - int const max_resampled_frames = ceil ((int64_t) data->frames() * _film->target_audio_sample_rate() / _film->audio_stream()->sample_rate()) + 32; + int const max_resampled_frames = ceil ((int64_t) data->frames() * _film->target_audio_sample_rate() / _playlist->audio_frame_rate()) + 32; - shared_ptr<AudioBuffers> resampled (new AudioBuffers (_film->audio_stream()->channels(), max_resampled_frames)); + shared_ptr<AudioBuffers> resampled (new AudioBuffers (_playlist->audio_channels(), max_resampled_frames)); /* Resample audio */ int const resampled_frames = swr_convert ( @@ -425,8 +427,8 @@ Encoder::encoder_thread (ServerDescription* server) void Encoder::write_audio (shared_ptr<const AudioBuffers> data) { - AudioMapping m (_film->audio_channels ()); - if (m.dcp_channels() != _film->audio_channels()) { + AudioMapping m (_playlist->audio_channels ()); + if (m.dcp_channels() != _playlist->audio_channels()) { /* Remap (currently just for mono -> 5.1) */ diff --git a/src/lib/encoder.h b/src/lib/encoder.h index 86880bc34..c84ee027a 100644 --- a/src/lib/encoder.h +++ b/src/lib/encoder.h @@ -51,6 +51,7 @@ class ServerDescription; class DCPVideoFrame; class EncodedData; class Writer; +class Playlist; /** @class Encoder * @brief Encoder to J2K and WAV for DCP. @@ -62,7 +63,7 @@ class Writer; class Encoder : public VideoSink, public AudioSink { public: - Encoder (boost::shared_ptr<Film> f); + Encoder (boost::shared_ptr<Film> f, boost::shared_ptr<Playlist>); virtual ~Encoder (); /** Called to indicate that a processing run is about to begin */ @@ -95,6 +96,7 @@ private: /** Film that we are encoding */ boost::shared_ptr<Film> _film; + boost::shared_ptr<Playlist> _playlist; /** Mutex for _time_history and _last_frame */ mutable boost::mutex _history_mutex; diff --git a/src/lib/examine_content_job.cc b/src/lib/examine_content_job.cc index 4b30c9431..c600132c3 100644 --- a/src/lib/examine_content_job.cc +++ b/src/lib/examine_content_job.cc @@ -17,29 +17,21 @@ */ -/** @file src/examine_content_job.cc - * @brief A class to run through content at high speed to find its length. - */ - #include <boost/filesystem.hpp> #include "examine_content_job.h" #include "options.h" -#include "decoder_factory.h" -#include "decoder.h" -#include "transcoder.h" #include "log.h" -#include "film.h" -#include "video_decoder.h" +#include "content.h" #include "i18n.h" using std::string; -using std::vector; -using std::pair; using boost::shared_ptr; -ExamineContentJob::ExamineContentJob (shared_ptr<Film> f) +ExamineContentJob::ExamineContentJob (shared_ptr<Film> f, shared_ptr<Content> c, bool q) : Job (f) + , _content (c) + , _quick (q) { } @@ -51,60 +43,13 @@ ExamineContentJob::~ExamineContentJob () string ExamineContentJob::name () const { - if (_film->name().empty ()) { - return _("Examine content"); - } - - return String::compose (_("Examine content of %1"), _film->name()); + return _("Examine content"); } void ExamineContentJob::run () { - descend (0.5); - _film->set_content_digest (md5_digest (_film->content_path ())); - ascend (); - - descend (0.5); - - /* Set the film's length to either - a) a length judged by running through the content or - b) the length from a decoder's header. - */ - if (!_film->trust_content_header()) { - /* 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 (); - _film->set_crop (Crop ()); - - DecodeOptions o; - o.decode_audio = false; - - Decoders decoders = decoder_factory (_film, o); - - set_progress_unknown (); - while (!decoders.video->pass()) { - /* keep going */ - } - - _film->set_length (decoders.video->video_frame()); - - _film->log()->log (String::compose (N_("Video length examined as %1 frames"), _film->length().get())); - - } else { - - /* Get a quick decoder to get the content's length from its header */ - - Decoders d = decoder_factory (_film, DecodeOptions()); - _film->set_length (d.video->length()); - - _film->log()->log (String::compose (N_("Video length obtained from header as %1 frames"), _film->length().get())); - } - - ascend (); + _content->examine (_film, shared_from_this (), _quick); set_progress (1); set_state (FINISHED_OK); } diff --git a/src/lib/examine_content_job.h b/src/lib/examine_content_job.h index 8ee4f0d60..dc0d53fff 100644 --- a/src/lib/examine_content_job.h +++ b/src/lib/examine_content_job.h @@ -17,22 +17,23 @@ */ -/** @file src/examine_content_job.h - * @brief A class to obtain the length and MD5 digest of a content file. - */ - +#include <boost/shared_ptr.hpp> #include "job.h" -/** @class ExamineContentJob - * @brief A class to obtain the length and MD5 digest of a content file. - */ +class Content; +class Log; + class ExamineContentJob : public Job { public: - ExamineContentJob (boost::shared_ptr<Film>); + ExamineContentJob (boost::shared_ptr<Film>, boost::shared_ptr<Content>, bool); ~ExamineContentJob (); std::string name () const; void run (); + +private: + boost::shared_ptr<Content> _content; + bool _quick; }; diff --git a/src/lib/exceptions.h b/src/lib/exceptions.h index e45a62353..6920556e5 100644 --- a/src/lib/exceptions.h +++ b/src/lib/exceptions.h @@ -112,6 +112,7 @@ class OpenFileError : public FileError { public: /** @param f File that we were trying to open */ + /* XXX: should be boost::filesystem::path */ OpenFileError (std::string f); }; diff --git a/src/lib/ffmpeg_content.cc b/src/lib/ffmpeg_content.cc new file mode 100644 index 000000000..25efb1ebf --- /dev/null +++ b/src/lib/ffmpeg_content.cc @@ -0,0 +1,156 @@ +#include "ffmpeg_content.h" +#include "ffmpeg_decoder.h" +#include "options.h" +#include "compose.hpp" +#include "job.h" +#include "util.h" +#include "log.h" + +#include "i18n.h" + +using std::string; +using boost::shared_ptr; + +int const FFmpegContentProperty::SUBTITLE_STREAMS = 100; +int const FFmpegContentProperty::SUBTITLE_STREAM = 101; +int const FFmpegContentProperty::AUDIO_STREAMS = 102; +int const FFmpegContentProperty::AUDIO_STREAM = 103; + +FFmpegContent::FFmpegContent (boost::filesystem::path f) + : Content (f) + , VideoContent (f) + , AudioContent (f) +{ + +} + +void +FFmpegContent::examine (shared_ptr<Film> film, shared_ptr<Job> job, bool quick) +{ + job->descend (0.5); + Content::examine (film, job, quick); + job->ascend (); + + job->set_progress_unknown (); + + DecodeOptions o; + o.decode_audio = false; + shared_ptr<FFmpegDecoder> decoder (new FFmpegDecoder (film, shared_from_this (), o)); + + ContentVideoFrame video_length = 0; + if (quick) { + video_length = decoder->video_length (); + film->log()->log (String::compose ("Video length obtained from header as %1 frames", decoder->video_length ())); + } else { + while (!decoder->pass ()) { + /* keep going */ + } + + video_length = decoder->video_frame (); + film->log()->log (String::compose ("Video length examined as %1 frames", decoder->video_frame ())); + } + + { + boost::mutex::scoped_lock lm (_mutex); + + _video_length = video_length; + + _subtitle_streams = decoder->subtitle_streams (); + if (!_subtitle_streams.empty ()) { + _subtitle_stream = _subtitle_streams.front (); + } + + _audio_streams = decoder->audio_streams (); + if (!_audio_streams.empty ()) { + _audio_stream = _audio_streams.front (); + } + } + + take_from_video_decoder (decoder); + + Changed (VideoContentProperty::VIDEO_LENGTH); + Changed (FFmpegContentProperty::SUBTITLE_STREAMS); + Changed (FFmpegContentProperty::SUBTITLE_STREAM); + Changed (FFmpegContentProperty::AUDIO_STREAMS); + Changed (FFmpegContentProperty::AUDIO_STREAM); +} + +string +FFmpegContent::summary () const +{ + return String::compose (_("Movie: %1"), file().filename ()); +} + +void +FFmpegContent::set_subtitle_stream (FFmpegSubtitleStream s) +{ + { + boost::mutex::scoped_lock lm (_mutex); + _subtitle_stream = s; + } + + Changed (FFmpegContentProperty::SUBTITLE_STREAM); +} + +void +FFmpegContent::set_audio_stream (FFmpegAudioStream s) +{ + { + boost::mutex::scoped_lock lm (_mutex); + _audio_stream = s; + } + + Changed (FFmpegContentProperty::AUDIO_STREAM); +} + +ContentAudioFrame +FFmpegContent::audio_length () const +{ + if (!_audio_stream) { + return 0; + } + + return video_frames_to_audio_frames (_video_length, audio_frame_rate(), video_frame_rate()); +} + +int +FFmpegContent::audio_channels () const +{ + if (!_audio_stream) { + return 0; + } + + return _audio_stream->channels (); +} + +int +FFmpegContent::audio_frame_rate () const +{ + if (!_audio_stream) { + return 0; + } + + return _audio_stream->frame_rate; +} + +int64_t +FFmpegContent::audio_channel_layout () const +{ + if (!_audio_stream) { + return 0; + } + + return _audio_stream->channel_layout; +} + +bool +operator== (FFmpegSubtitleStream const & a, FFmpegSubtitleStream const & b) +{ + return a.id == b.id; +} + +bool +operator== (FFmpegAudioStream const & a, FFmpegAudioStream const & b) +{ + return a.id == b.id; +} diff --git a/src/lib/ffmpeg_content.h b/src/lib/ffmpeg_content.h index 8b3eca62d..83474ea66 100644 --- a/src/lib/ffmpeg_content.h +++ b/src/lib/ffmpeg_content.h @@ -1,8 +1,97 @@ -#include "content.h" +#ifndef DVDOMATIC_FFMPEG_CONTENT_H +#define DVDOMATIC_FFMPEG_CONTENT_H -class FFmpegContent : public VideoContent, public AudioContent +#include <boost/enable_shared_from_this.hpp> +#include "video_content.h" +#include "audio_content.h" + +class FFmpegAudioStream +{ +public: + FFmpegAudioStream (std::string n, int i, int f, int64_t c) + : name (n) + , id (i) + , frame_rate (f) + , channel_layout (c) + {} + + int channels () const { + return av_get_channel_layout_nb_channels (channel_layout); + } + + std::string name; + int id; + int frame_rate; + int64_t channel_layout; +}; + +extern bool operator== (FFmpegAudioStream const & a, FFmpegAudioStream const & b); + +class FFmpegSubtitleStream +{ +public: + FFmpegSubtitleStream (std::string n, int i) + : name (n) + , id (i) + {} + + std::string name; + int id; +}; + +extern bool operator== (FFmpegSubtitleStream const & a, FFmpegSubtitleStream const & b); + +class FFmpegContentProperty : public VideoContentProperty +{ +public: + static int const SUBTITLE_STREAMS; + static int const SUBTITLE_STREAM; + static int const AUDIO_STREAMS; + static int const AUDIO_STREAM; +}; + +class FFmpegContent : public VideoContent, public AudioContent, public boost::enable_shared_from_this<FFmpegContent> { public: + FFmpegContent (boost::filesystem::path); + void examine (boost::shared_ptr<Film>, boost::shared_ptr<Job>, bool); + std::string summary () const; + /* AudioContent */ + int audio_channels () const; + ContentAudioFrame audio_length () const; + int audio_frame_rate () const; + int64_t audio_channel_layout () const; + + std::vector<FFmpegSubtitleStream> subtitle_streams () const { + boost::mutex::scoped_lock lm (_mutex); + return _subtitle_streams; + } + + boost::optional<FFmpegSubtitleStream> subtitle_stream () const { + boost::mutex::scoped_lock lm (_mutex); + return _subtitle_stream; + } + + std::vector<FFmpegAudioStream> audio_streams () const { + boost::mutex::scoped_lock lm (_mutex); + return _audio_streams; + } + + boost::optional<FFmpegAudioStream> audio_stream () const { + boost::mutex::scoped_lock lm (_mutex); + return _audio_stream; + } + + void set_subtitle_stream (FFmpegSubtitleStream); + void set_audio_stream (FFmpegAudioStream); + +private: + std::vector<FFmpegSubtitleStream> _subtitle_streams; + boost::optional<FFmpegSubtitleStream> _subtitle_stream; + std::vector<FFmpegAudioStream> _audio_streams; + boost::optional<FFmpegAudioStream> _audio_stream; }; + +#endif diff --git a/src/lib/ffmpeg_decoder.cc b/src/lib/ffmpeg_decoder.cc index ac25844e3..c8e46776f 100644 --- a/src/lib/ffmpeg_decoder.cc +++ b/src/lib/ffmpeg_decoder.cc @@ -62,10 +62,11 @@ using boost::optional; using boost::dynamic_pointer_cast; using libdcp::Size; -FFmpegDecoder::FFmpegDecoder (shared_ptr<Film> f, DecodeOptions o) +FFmpegDecoder::FFmpegDecoder (shared_ptr<const Film> f, shared_ptr<FFmpegContent> c, DecodeOptions o) : Decoder (f, o) - , VideoDecoder (f, o) - , AudioDecoder (f, o) + , VideoDecoder (f, c, o) + , AudioDecoder (f, c, o) + , _ffmpeg_content (c) , _format_context (0) , _video_stream (-1) , _frame (0) @@ -110,8 +111,8 @@ FFmpegDecoder::setup_general () { av_register_all (); - if (avformat_open_input (&_format_context, _film->content_path().c_str(), 0, 0) < 0) { - throw OpenFileError (_film->content_path ()); + if (avformat_open_input (&_format_context, _ffmpeg_content->file().string().c_str(), 0, 0) < 0) { + throw OpenFileError (_ffmpeg_content->file().string ()); } if (avformat_find_stream_info (_format_context, 0) < 0) { @@ -135,17 +136,11 @@ FFmpegDecoder::setup_general () } _audio_streams.push_back ( - shared_ptr<AudioStream> ( - new FFmpegAudioStream (stream_name (s), i, s->codec->sample_rate, s->codec->channel_layout) - ) + 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 ( - shared_ptr<SubtitleStream> ( - new SubtitleStream (stream_name (s), i) - ) - ); + _subtitle_streams.push_back (FFmpegSubtitleStream (stream_name (s), i)); } } @@ -177,14 +172,11 @@ FFmpegDecoder::setup_video () void FFmpegDecoder::setup_audio () { - if (!_audio_stream) { + if (!_ffmpeg_content->audio_stream ()) { return; } - shared_ptr<FFmpegAudioStream> ffa = dynamic_pointer_cast<FFmpegAudioStream> (_audio_stream); - assert (ffa); - - _audio_codec_context = _format_context->streams[ffa->id()]->codec; + _audio_codec_context = _format_context->streams[_ffmpeg_content->audio_stream()->id]->codec; _audio_codec = avcodec_find_decoder (_audio_codec_context->codec_id); if (_audio_codec == 0) { @@ -199,11 +191,11 @@ FFmpegDecoder::setup_audio () void FFmpegDecoder::setup_subtitle () { - if (!_subtitle_stream || _subtitle_stream->id() >= int (_format_context->nb_streams)) { + if (!_ffmpeg_content->subtitle_stream() || _ffmpeg_content->subtitle_stream()->id >= int (_format_context->nb_streams)) { return; } - _subtitle_codec_context = _format_context->streams[_subtitle_stream->id()]->codec; + _subtitle_codec_context = _format_context->streams[_ffmpeg_content->subtitle_stream()->id]->codec; _subtitle_codec = avcodec_find_decoder (_subtitle_codec_context->codec_id); if (_subtitle_codec == 0) { @@ -244,7 +236,7 @@ FFmpegDecoder::pass () } } - if (_audio_stream && _opt.decode_audio) { + if (_ffmpeg_content->audio_stream() && _opt.decode_audio) { decode_audio_packet (); } @@ -253,8 +245,6 @@ FFmpegDecoder::pass () avcodec_get_frame_defaults (_frame); - shared_ptr<FFmpegAudioStream> ffa = dynamic_pointer_cast<FFmpegAudioStream> (_audio_stream); - if (_packet.stream_index == _video_stream && _opt.decode_video) { int frame_finished; @@ -272,9 +262,9 @@ FFmpegDecoder::pass () } } - } else if (ffa && _packet.stream_index == ffa->id() && _opt.decode_audio) { + } else if (_ffmpeg_content->audio_stream() && _packet.stream_index == _ffmpeg_content->audio_stream()->id && _opt.decode_audio) { decode_audio_packet (); - } else if (_subtitle_stream && _packet.stream_index == _subtitle_stream->id() && _opt.decode_subtitles && _first_video) { + } else if (_ffmpeg_content->subtitle_stream() && _packet.stream_index == _ffmpeg_content->subtitle_stream()->id && _opt.decode_subtitles && _first_video) { int got_subtitle; AVSubtitle sub; @@ -306,19 +296,16 @@ FFmpegDecoder::pass () shared_ptr<AudioBuffers> FFmpegDecoder::deinterleave_audio (uint8_t** data, int size) { - assert (_film->audio_channels()); + assert (_ffmpeg_content->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() * ffa->channels())) == 0); + assert ((size % (bytes_per_audio_sample() * _ffmpeg_content->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 (ffa->channels(), frames)); + int const frames = total_samples / _ffmpeg_content->audio_channels(); + shared_ptr<AudioBuffers> audio (new AudioBuffers (_ffmpeg_content->audio_channels(), frames)); switch (audio_sample_format()) { case AV_SAMPLE_FMT_S16: @@ -330,7 +317,7 @@ FFmpegDecoder::deinterleave_audio (uint8_t** data, int size) audio->data(channel)[sample] = float(*p++) / (1 << 15); ++channel; - if (channel == _film->audio_channels()) { + if (channel == _ffmpeg_content->audio_channels()) { channel = 0; ++sample; } @@ -341,7 +328,7 @@ FFmpegDecoder::deinterleave_audio (uint8_t** data, int size) case AV_SAMPLE_FMT_S16P: { int16_t** p = reinterpret_cast<int16_t **> (data); - for (int i = 0; i < _film->audio_channels(); ++i) { + for (int i = 0; i < _ffmpeg_content->audio_channels(); ++i) { for (int j = 0; j < frames; ++j) { audio->data(i)[j] = static_cast<float>(p[i][j]) / (1 << 15); } @@ -358,7 +345,7 @@ FFmpegDecoder::deinterleave_audio (uint8_t** data, int size) audio->data(channel)[sample] = static_cast<float>(*p++) / (1 << 31); ++channel; - if (channel == _film->audio_channels()) { + if (channel == _ffmpeg_content->audio_channels()) { channel = 0; ++sample; } @@ -375,7 +362,7 @@ FFmpegDecoder::deinterleave_audio (uint8_t** data, int size) audio->data(channel)[sample] = *p++; ++channel; - if (channel == _film->audio_channels()) { + if (channel == _ffmpeg_content->audio_channels()) { channel = 0; ++sample; } @@ -386,7 +373,7 @@ FFmpegDecoder::deinterleave_audio (uint8_t** data, int size) case AV_SAMPLE_FMT_FLTP: { float** p = reinterpret_cast<float**> (data); - for (int i = 0; i < _film->audio_channels(); ++i) { + for (int i = 0; i < _ffmpeg_content->audio_channels(); ++i) { memcpy (audio->data(i), p[i], frames * sizeof(float)); } } @@ -489,21 +476,6 @@ FFmpegDecoder::bytes_per_audio_sample () const } void -FFmpegDecoder::set_audio_stream (shared_ptr<AudioStream> s) -{ - AudioDecoder::set_audio_stream (s); - setup_audio (); -} - -void -FFmpegDecoder::set_subtitle_stream (shared_ptr<SubtitleStream> s) -{ - VideoDecoder::set_subtitle_stream (s); - setup_subtitle (); - OutputChanged (); -} - -void FFmpegDecoder::filter_and_emit_video (AVFrame* frame) { boost::mutex::scoped_lock lm (_filter_graphs_mutex); @@ -561,58 +533,6 @@ FFmpegDecoder::do_seek (double p, bool backwards) return r < 0; } -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 != N_("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 == N_("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 (N_("ffmpeg %1 %2 %3 %4"), _id, _sample_rate, _channel_layout, _name); -} - void FFmpegDecoder::out_with_sync () { @@ -678,8 +598,8 @@ FFmpegDecoder::film_changed (Film::Property p) } /** @return Length (in video frames) according to our content's header */ -SourceFrame -FFmpegDecoder::length () const +ContentVideoFrame +FFmpegDecoder::video_length () const { return (double(_format_context->duration) / AV_TIME_BASE) * frames_per_second(); } @@ -693,9 +613,6 @@ FFmpegDecoder::frame_time () const void FFmpegDecoder::decode_audio_packet () { - shared_ptr<FFmpegAudioStream> ffa = dynamic_pointer_cast<FFmpegAudioStream> (_audio_stream); - assert (ffa); - /* Audio packets can contain multiple frames, so we may have to call avcodec_decode_audio4 several times. */ @@ -727,17 +644,17 @@ FFmpegDecoder::decode_audio_packet () */ /* frames of silence that we must push */ - int const s = rint ((_first_audio.get() - _first_video.get()) * ffa->sample_rate ()); + int const s = rint ((_first_audio.get() - _first_video.get()) * _ffmpeg_content->audio_frame_rate ()); _film->log()->log ( String::compose ( N_("First video at %1, first audio at %2, pushing %3 audio frames of silence for %4 channels (%5 bytes per sample)"), - _first_video.get(), _first_audio.get(), s, ffa->channels(), bytes_per_audio_sample() + _first_video.get(), _first_audio.get(), s, _ffmpeg_content->audio_channels(), bytes_per_audio_sample() ) ); if (s) { - shared_ptr<AudioBuffers> audio (new AudioBuffers (ffa->channels(), s)); + shared_ptr<AudioBuffers> audio (new AudioBuffers (_ffmpeg_content->audio_channels(), s)); audio->make_silent (); Audio (audio); } @@ -747,7 +664,7 @@ FFmpegDecoder::decode_audio_packet () 0, _audio_codec_context->channels, _frame->nb_samples, audio_sample_format (), 1 ); - assert (_audio_codec_context->channels == _film->audio_channels()); + assert (_audio_codec_context->channels == _ffmpeg_content->audio_channels()); Audio (deinterleave_audio (_frame->data, data_size)); } } diff --git a/src/lib/ffmpeg_decoder.h b/src/lib/ffmpeg_decoder.h index 1bb14ce9c..a0900d89f 100644 --- a/src/lib/ffmpeg_decoder.h +++ b/src/lib/ffmpeg_decoder.h @@ -36,6 +36,7 @@ extern "C" { #include "video_decoder.h" #include "audio_decoder.h" #include "film.h" +#include "ffmpeg_content.h" struct AVFilterGraph; struct AVCodecContext; @@ -50,62 +51,37 @@ 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. */ class FFmpegDecoder : public VideoDecoder, public AudioDecoder { public: - FFmpegDecoder (boost::shared_ptr<Film>, DecodeOptions); + FFmpegDecoder (boost::shared_ptr<const Film>, boost::shared_ptr<FFmpegContent>, DecodeOptions); ~FFmpegDecoder (); float frames_per_second () const; libdcp::Size native_size () const; - SourceFrame length () const; + ContentVideoFrame video_length () const; int time_base_numerator () const; int time_base_denominator () const; int sample_aspect_ratio_numerator () const; int sample_aspect_ratio_denominator () const; - void set_audio_stream (boost::shared_ptr<AudioStream>); - void set_subtitle_stream (boost::shared_ptr<SubtitleStream>); + std::vector<FFmpegSubtitleStream> subtitle_streams () const { + return _subtitle_streams; + } + + std::vector<FFmpegAudioStream> audio_streams () const { + return _audio_streams; + } bool seek (double); bool seek_to_last (); + bool pass (); private: - bool pass (); bool do_seek (double p, bool); PixelFormat pixel_format () const; AVSampleFormat audio_sample_format () const; @@ -129,6 +105,8 @@ private: std::string stream_name (AVStream* s) const; + boost::shared_ptr<FFmpegContent> _ffmpeg_content; + AVFormatContext* _format_context; int _video_stream; @@ -148,4 +126,7 @@ private: std::list<boost::shared_ptr<FilterGraph> > _filter_graphs; boost::mutex _filter_graphs_mutex; + + std::vector<FFmpegSubtitleStream> _subtitle_streams; + std::vector<FFmpegAudioStream> _audio_streams; }; diff --git a/src/lib/film.cc b/src/lib/film.cc index 0d969f16d..f69f63fd8 100644 --- a/src/lib/film.cc +++ b/src/lib/film.cc @@ -52,6 +52,7 @@ #include "audio_decoder.h" #include "sndfile_decoder.h" #include "analyse_audio_job.h" +#include "playlist.h" #include "i18n.h" @@ -135,8 +136,6 @@ Film::Film (string d, bool must_exist) } } - _sndfile_stream = SndfileStream::create (); - if (must_exist) { read_metadata (); } @@ -162,7 +161,6 @@ Film::Film (Film const & o) , _dcp_ab (o._dcp_ab) , _audio_gain (o._audio_gain) , _audio_delay (o._audio_delay) - , _still_duration (o._still_duration) , _with_subtitles (o._with_subtitles) , _subtitle_offset (o._subtitle_offset) , _subtitle_scale (o._subtitle_scale) @@ -186,6 +184,10 @@ Film::video_state_identifier () const { assert (format ()); + return "XXX"; + +#if 0 + pair<string, string> f = Filter::ffmpeg_strings (filters()); stringstream s; @@ -204,6 +206,7 @@ Film::video_state_identifier () const } return s.str (); +#endif } /** @return The path to the directory to write video frame info files to */ @@ -234,7 +237,7 @@ Film::audio_analysis_path () const { boost::filesystem::path p; p /= "analysis"; - p /= content_digest(); + p /= "XXX";//content_digest(); return file (p.string ()); } @@ -256,12 +259,12 @@ Film::make_dcp () log()->log (String::compose ("Starting to make DCP on %1", buffer)); } - log()->log (String::compose ("Content is %1; type %2", content_path(), (content_type() == STILL ? _("still") : _("video")))); - if (length()) { - log()->log (String::compose ("Content length %1", length().get())); - } - log()->log (String::compose ("Content digest %1", content_digest())); - log()->log (String::compose ("Content at %1 fps, DCP at %2 fps", source_frame_rate(), dcp_frame_rate())); +// log()->log (String::compose ("Content is %1; type %2", content_path(), (content_type() == STILL ? _("still") : _("video")))); +// if (length()) { +// log()->log (String::compose ("Content length %1", length().get())); +// } +// log()->log (String::compose ("Content digest %1", content_digest())); +// log()->log (String::compose ("Content at %1 fps, DCP at %2 fps", source_frame_rate(), dcp_frame_rate())); log()->log (String::compose ("%1 threads", Config::instance()->num_local_encoding_threads())); log()->log (String::compose ("J2K bandwidth %1", j2k_bandwidth())); #ifdef DVDOMATIC_DEBUG @@ -318,17 +321,13 @@ Film::analyse_audio () JobManager::instance()->add (_analyse_audio_job); } -/** Start a job to examine our content file */ +/** Start a job to examine a piece of content */ void -Film::examine_content () +Film::examine_content (shared_ptr<Content> c) { - if (_examine_content_job) { - return; - } - - _examine_content_job.reset (new ExamineContentJob (shared_from_this())); - _examine_content_job->Finished.connect (bind (&Film::examine_content_finished, this)); - JobManager::instance()->add (_examine_content_job); + shared_ptr<Job> j (new ExamineContentJob (shared_from_this(), c, trust_content_header ())); + j->Finished.connect (bind (&Film::examine_content_finished, this)); + JobManager::instance()->add (j); } void @@ -346,7 +345,7 @@ Film::analyse_audio_finished () void Film::examine_content_finished () { - _examine_content_job.reset (); + /* XXX */ } /** Start a job to send our DCP to the configured TMS */ @@ -395,7 +394,7 @@ Film::write_metadata () const /* User stuff */ f << "name " << _name << endl; f << "use_dci_name " << _use_dci_name << endl; - f << "content " << _content << endl; +// f << "content " << _content << endl; f << "trust_content_header " << (_trust_content_header ? "1" : "0") << endl; if (_dcp_content_type) { f << "dcp_content_type " << _dcp_content_type->dci_name () << endl; @@ -414,19 +413,19 @@ Film::write_metadata () const f << "trim_start " << _trim_start << endl; f << "trim_end " << _trim_end << endl; f << "dcp_ab " << (_dcp_ab ? "1" : "0") << endl; - if (_content_audio_stream) { - f << "selected_content_audio_stream " << _content_audio_stream->to_string() << endl; - } - for (vector<string>::const_iterator i = _external_audio.begin(); i != _external_audio.end(); ++i) { - f << "external_audio " << *i << endl; - } - f << "use_content_audio " << (_use_content_audio ? "1" : "0") << endl; +// if (_content_audio_stream) { +// f << "selected_content_audio_stream " << _content_audio_stream->to_string() << endl; +// } +// for (vector<string>::const_iterator i = _external_audio.begin(); i != _external_audio.end(); ++i) { +// f << "external_audio " << *i << endl; +// } +// f << "use_content_audio " << (_use_content_audio ? "1" : "0") << endl; f << "audio_gain " << _audio_gain << endl; f << "audio_delay " << _audio_delay << endl; - f << "still_duration " << _still_duration << endl; - if (_subtitle_stream) { - f << "selected_subtitle_stream " << _subtitle_stream->to_string() << endl; - } +// f << "still_duration " << _still_duration << endl; +// if (_subtitle_stream) { +// f << "selected_subtitle_stream " << _subtitle_stream->to_string() << endl; +// } f << "with_subtitles " << _with_subtitles << endl; f << "subtitle_offset " << _subtitle_offset << endl; f << "subtitle_scale " << _subtitle_scale << endl; @@ -435,22 +434,22 @@ Film::write_metadata () const _dci_metadata.write (f); f << "dci_date " << boost::gregorian::to_iso_string (_dci_date) << endl; f << "dcp_frame_rate " << _dcp_frame_rate << endl; - f << "width " << _size.width << endl; - f << "height " << _size.height << endl; - f << "length " << _length.get_value_or(0) << endl; - f << "content_digest " << _content_digest << endl; +// f << "width " << _size.width << endl; +// f << "height " << _size.height << endl; +// f << "length " << _length.get_value_or(0) << endl; +// f << "content_digest " << _content_digest << endl; - 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 () << endl; - } +// 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 () << endl; +// } - f << "external_audio_stream " << _sndfile_stream->to_string() << endl; +// f << "external_audio_stream " << _sndfile_stream->to_string() << endl; - for (vector<shared_ptr<SubtitleStream> >::const_iterator i = _subtitle_streams.begin(); i != _subtitle_streams.end(); ++i) { - f << "subtitle_stream " << (*i)->to_string () << endl; - } +// for (vector<shared_ptr<SubtitleStream> >::const_iterator i = _subtitle_streams.begin(); i != _subtitle_streams.end(); ++i) { +// f << "subtitle_stream " << (*i)->to_string () << endl; +// } - f << "source_frame_rate " << _source_frame_rate << endl; +// f << "source_frame_rate " << _source_frame_rate << endl; _dirty = false; } @@ -461,9 +460,9 @@ Film::read_metadata () { boost::mutex::scoped_lock lm (_state_mutex); - _external_audio.clear (); - _content_audio_streams.clear (); - _subtitle_streams.clear (); +// _external_audio.clear (); +// _content_audio_streams.clear (); +// _subtitle_streams.clear (); boost::optional<int> version; @@ -499,7 +498,7 @@ Film::read_metadata () } else if (k == "use_dci_name") { _use_dci_name = (v == "1"); } else if (k == "content") { - _content = v; +// _content = v; } else if (k == "trust_content_header") { _trust_content_header = (v == "1"); } else if (k == "dcp_content_type") { @@ -532,23 +531,23 @@ Film::read_metadata () if (!version) { audio_stream_index = atoi (v.c_str ()); } else { - _content_audio_stream = audio_stream_factory (v, version); +// _content_audio_stream = audio_stream_factory (v, version); } } else if (k == "external_audio") { - _external_audio.push_back (v); +// _external_audio.push_back (v); } else if (k == "use_content_audio") { - _use_content_audio = (v == "1"); +// _use_content_audio = (v == "1"); } else if (k == "audio_gain") { _audio_gain = atof (v.c_str ()); } else if (k == "audio_delay") { _audio_delay = atoi (v.c_str ()); } else if (k == "still_duration") { - _still_duration = atoi (v.c_str ()); +// _still_duration = atoi (v.c_str ()); } else if (k == "selected_subtitle_stream") { if (!version) { subtitle_stream_index = atoi (v.c_str ()); } else { - _subtitle_stream = subtitle_stream_factory (v, version); +// _subtitle_stream = subtitle_stream_factory (v, version); } } else if (k == "with_subtitles") { _with_subtitles = (v == "1"); @@ -570,50 +569,31 @@ Film::read_metadata () /* Cached stuff */ if (k == "width") { - _size.width = atoi (v.c_str ()); +// _size.width = atoi (v.c_str ()); } else if (k == "height") { - _size.height = atoi (v.c_str ()); +// _size.height = atoi (v.c_str ()); } else if (k == "length") { int const vv = atoi (v.c_str ()); if (vv) { - _length = vv; +// _length = vv; } } else if (k == "content_digest") { - _content_digest = v; +// _content_digest = v; } else if (k == "content_audio_stream" || (!version && k == "audio_stream")) { - _content_audio_streams.push_back (audio_stream_factory (v, version)); +// _content_audio_streams.push_back (audio_stream_factory (v, version)); } else if (k == "external_audio_stream") { - _sndfile_stream = audio_stream_factory (v, version); +// _sndfile_stream = audio_stream_factory (v, version); } else if (k == "subtitle_stream") { - _subtitle_streams.push_back (subtitle_stream_factory (v, version)); +// _subtitle_streams.push_back (subtitle_stream_factory (v, version)); } else if (k == "source_frame_rate") { - _source_frame_rate = atof (v.c_str ()); +// _source_frame_rate = atof (v.c_str ()); } else if (version < 4 && k == "frames_per_second") { - _source_frame_rate = atof (v.c_str ()); +// _source_frame_rate = atof (v.c_str ()); /* Fill in what would have been used for DCP frame rate by the older version */ - _dcp_frame_rate = best_dcp_frame_rate (_source_frame_rate); +// _dcp_frame_rate = best_dcp_frame_rate (_source_frame_rate); } } - if (!version) { - if (audio_sample_rate) { - /* version < 1 didn't specify sample rate in the audio streams, so fill it in here */ - 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) _content_audio_streams.size()) { - _content_audio_stream = _content_audio_streams[audio_stream_index.get()]; - } - - /* similarly the subtitle */ - if (subtitle_stream_index && subtitle_stream_index.get() >= 0 && subtitle_stream_index.get() < (int) _subtitle_streams.size()) { - _subtitle_stream = _subtitle_streams[subtitle_stream_index.get()]; - } - } - _dirty = false; } @@ -661,47 +641,21 @@ Film::file (string f) const return p.string (); } -/** @return full path of the content (actual video) file - * of the Film. - */ -string -Film::content_path () const -{ - boost::mutex::scoped_lock lm (_state_mutex); - if (boost::filesystem::path(_content).has_root_directory ()) { - return _content; - } - - return file (_content); -} - -ContentType -Film::content_type () const -{ - if (boost::filesystem::is_directory (_content)) { - /* Directory of images, we assume */ - return VIDEO; - } - - if (still_image_file (_content)) { - return STILL; - } - - return VIDEO; -} - /** @return The sampling rate that we will resample the audio to */ int Film::target_audio_sample_rate () const { - if (!audio_stream()) { + /* XXX: how often is this method called? */ + + boost::shared_ptr<Playlist> p = playlist (); + if (p->has_audio ()) { return 0; } /* Resample to a DCI-approved sample rate */ - double t = dcp_audio_sample_rate (audio_stream()->sample_rate()); + double t = dcp_audio_sample_rate (p->audio_frame_rate()); - FrameRateConversion frc (source_frame_rate(), dcp_frame_rate()); + FrameRateConversion frc (p->video_frame_rate(), dcp_frame_rate()); /* Compensate if the DCP is being run at a different frame rate to the source; that is, if the video is run such that it will @@ -710,18 +664,12 @@ Film::target_audio_sample_rate () const */ if (frc.change_speed) { - t *= source_frame_rate() * frc.factor() / dcp_frame_rate(); + t *= p->video_frame_rate() * frc.factor() / dcp_frame_rate(); } return rint (t); } -int -Film::still_duration_in_frames () const -{ - return still_duration() * source_frame_rate(); -} - /** @return a DCI-compliant name for a DCP of this film */ string Film::dci_name (bool if_created_now) const @@ -768,7 +716,8 @@ Film::dci_name (bool if_created_now) const } } - switch (audio_channels()) { + /* XXX */ + switch (2) { case 1: d << "_10"; break; @@ -847,98 +796,6 @@ Film::set_use_dci_name (bool u) } void -Film::set_content (string c) -{ - string check = directory (); - - boost::filesystem::path slash ("/"); - string platform_slash = slash.make_preferred().string (); - - if (!ends_with (check, platform_slash)) { - check += platform_slash; - } - - if (boost::filesystem::path(c).has_root_directory () && starts_with (c, check)) { - c = c.substr (_directory.length() + 1); - } - - string old_content; - - { - boost::mutex::scoped_lock lm (_state_mutex); - if (c == _content) { - return; - } - - old_content = _content; - _content = c; - } - - /* Reset streams here in case the new content doesn't have one or the other */ - _content_audio_stream = shared_ptr<AudioStream> (); - _subtitle_stream = shared_ptr<SubtitleStream> (); - - /* Start off using content audio */ - set_use_content_audio (true); - - /* Create a temporary decoder so that we can get information - about the content. - */ - - try { - Decoders d = decoder_factory (shared_from_this(), DecodeOptions()); - - set_size (d.video->native_size ()); - set_source_frame_rate (d.video->frames_per_second ()); - set_dcp_frame_rate (best_dcp_frame_rate (source_frame_rate ())); - set_subtitle_streams (d.video->subtitle_streams ()); - if (d.audio) { - set_content_audio_streams (d.audio->audio_streams ()); - } - - { - boost::mutex::scoped_lock lm (_state_mutex); - _content = c; - } - - signal_changed (CONTENT); - - /* Start off with the first audio and subtitle streams */ - if (d.audio && !d.audio->audio_streams().empty()) { - set_content_audio_stream (d.audio->audio_streams().front()); - } - - if (!d.video->subtitle_streams().empty()) { - set_subtitle_stream (d.video->subtitle_streams().front()); - } - - examine_content (); - - } catch (...) { - - boost::mutex::scoped_lock lm (_state_mutex); - _content = old_content; - throw; - - } - - /* Default format */ - switch (content_type()) { - case STILL: - set_format (Format::from_id ("var-185")); - break; - case VIDEO: - set_format (Format::from_id ("185")); - break; - } - - /* Still image DCPs must use external audio */ - if (content_type() == STILL) { - set_use_content_audio (false); - } -} - -void Film::set_trust_content_header (bool t) { { @@ -950,7 +807,8 @@ Film::set_trust_content_header (bool t) if (!_trust_content_header && !content().empty()) { /* We just said that we don't trust the content's header */ - examine_content (); + /* XXX */ +// examine_content (); } } @@ -1092,43 +950,6 @@ Film::set_dcp_ab (bool a) } void -Film::set_content_audio_stream (shared_ptr<AudioStream> s) -{ - { - boost::mutex::scoped_lock lm (_state_mutex); - _content_audio_stream = s; - } - signal_changed (CONTENT_AUDIO_STREAM); -} - -void -Film::set_external_audio (vector<string> a) -{ - { - boost::mutex::scoped_lock lm (_state_mutex); - _external_audio = a; - } - - shared_ptr<SndfileDecoder> decoder (new SndfileDecoder (shared_from_this(), DecodeOptions())); - if (decoder->audio_stream()) { - _sndfile_stream = decoder->audio_stream (); - } - - signal_changed (EXTERNAL_AUDIO); -} - -void -Film::set_use_content_audio (bool e) -{ - { - boost::mutex::scoped_lock lm (_state_mutex); - _use_content_audio = e; - } - - signal_changed (USE_CONTENT_AUDIO); -} - -void Film::set_audio_gain (float g) { { @@ -1149,26 +970,6 @@ Film::set_audio_delay (int d) } void -Film::set_still_duration (int d) -{ - { - boost::mutex::scoped_lock lm (_state_mutex); - _still_duration = d; - } - signal_changed (STILL_DURATION); -} - -void -Film::set_subtitle_stream (shared_ptr<SubtitleStream> s) -{ - { - boost::mutex::scoped_lock lm (_state_mutex); - _subtitle_stream = s; - } - signal_changed (SUBTITLE_STREAM); -} - -void Film::set_with_subtitles (bool w) { { @@ -1240,76 +1041,6 @@ Film::set_dcp_frame_rate (int f) } void -Film::set_size (libdcp::Size s) -{ - { - boost::mutex::scoped_lock lm (_state_mutex); - _size = s; - } - signal_changed (SIZE); -} - -void -Film::set_length (SourceFrame l) -{ - { - boost::mutex::scoped_lock lm (_state_mutex); - _length = l; - } - signal_changed (LENGTH); -} - -void -Film::unset_length () -{ - { - boost::mutex::scoped_lock lm (_state_mutex); - _length = boost::none; - } - signal_changed (LENGTH); -} - -void -Film::set_content_digest (string d) -{ - { - boost::mutex::scoped_lock lm (_state_mutex); - _content_digest = d; - } - _dirty = true; -} - -void -Film::set_content_audio_streams (vector<shared_ptr<AudioStream> > s) -{ - { - boost::mutex::scoped_lock lm (_state_mutex); - _content_audio_streams = s; - } - signal_changed (CONTENT_AUDIO_STREAMS); -} - -void -Film::set_subtitle_streams (vector<shared_ptr<SubtitleStream> > s) -{ - { - boost::mutex::scoped_lock lm (_state_mutex); - _subtitle_streams = s; - } - signal_changed (SUBTITLE_STREAMS); -} - -void -Film::set_source_frame_rate (float f) -{ - { - boost::mutex::scoped_lock lm (_state_mutex); - _source_frame_rate = f; - } - signal_changed (SOURCE_FRAME_RATE); -} - -void Film::signal_changed (Property p) { { @@ -1322,33 +1053,12 @@ Film::signal_changed (Property p) } } -int -Film::audio_channels () const -{ - shared_ptr<AudioStream> s = audio_stream (); - if (!s) { - return 0; - } - - return s->channels (); -} - void 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 _sndfile_stream; -} - string Film::info_path (int f) const { @@ -1404,20 +1114,22 @@ Film::have_dcp () const return true; } -bool -Film::has_audio () const +shared_ptr<Playlist> +Film::playlist () const { - if (use_content_audio()) { - return audio_stream(); - } + boost::mutex::scoped_lock lm (_state_mutex); + return shared_ptr<Playlist> (new Playlist (shared_from_this (), _content)); +} - vector<string> const e = external_audio (); - for (vector<string>::const_iterator i = e.begin(); i != e.end(); ++i) { - if (!i->empty ()) { - return true; - } +void +Film::add_content (shared_ptr<Content> c) +{ + { + boost::mutex::scoped_lock lm (_state_mutex); + _content.push_back (c); } - return false; -} + signal_changed (CONTENT); + examine_content (c); +} diff --git a/src/lib/film.h b/src/lib/film.h index 2e05d64f4..afd57b7c2 100644 --- a/src/lib/film.h +++ b/src/lib/film.h @@ -37,7 +37,6 @@ extern "C" { } #include "dcp_content_type.h" #include "util.h" -#include "stream.h" #include "dci_metadata.h" class Format; @@ -48,6 +47,7 @@ class ExamineContentJob; class AnalyseAudioJob; class ExternalAudioStream; class Content; +class Playlist; /** @class Film * @brief A representation of a video, maybe with sound. @@ -69,7 +69,7 @@ public: std::string video_mxf_filename () const; std::string audio_analysis_path () const; - void examine_content (); + void examine_content (boost::shared_ptr<Content>); void analyse_audio (); void send_dcp_to_tms (); @@ -87,9 +87,6 @@ public: std::string file (std::string f) const; std::string dir (std::string d) const; - std::string content_path () const; - ContentType content_type () const; - int target_audio_sample_rate () const; void write_metadata () const; @@ -104,12 +101,12 @@ public: return _dirty; } - int audio_channels () const; - void set_dci_date_today (); bool have_dcp () const; + boost::shared_ptr<Playlist> playlist () const; + /** Identifiers for the parts of our state; used for signalling changes. */ @@ -118,6 +115,7 @@ public: NAME, USE_DCI_NAME, TRUST_CONTENT_HEADER, + CONTENT, DCP_CONTENT_TYPE, FORMAT, CROP, @@ -162,6 +160,11 @@ public: return _trust_content_header; } + std::list<boost::shared_ptr<Content> > content () const { + boost::mutex::scoped_lock lm (_state_mutex); + return _content; + } + DCPContentType const * dcp_content_type () const { boost::mutex::scoped_lock lm (_state_mutex); return _dcp_content_type; @@ -212,11 +215,6 @@ public: return _audio_delay; } - boost::shared_ptr<SubtitleStream> subtitle_stream () const { - boost::mutex::scoped_lock lm (_state_mutex); - return _subtitle_stream; - } - bool with_subtitles () const { boost::mutex::scoped_lock lm (_state_mutex); return _with_subtitles; @@ -252,15 +250,13 @@ public: return _dcp_frame_rate; } - boost::shared_ptr<AudioStream> audio_stream () const; - bool has_audio () const; - /* SET */ void set_directory (std::string); void set_name (std::string); void set_use_dci_name (bool); void set_trust_content_header (bool); + void add_content (boost::shared_ptr<Content>); void set_dcp_content_type (DCPContentType const *); void set_format (Format const *); void set_crop (Crop); @@ -297,8 +293,6 @@ private: /** Log to write to */ boost::shared_ptr<Log> _log; - /** Any running ExamineContentJob, or 0 */ - boost::shared_ptr<ExamineContentJob> _examine_content_job; /** Any running AnalyseAudioJob, or 0 */ boost::shared_ptr<AnalyseAudioJob> _analyse_audio_job; @@ -318,7 +312,8 @@ private: std::string _name; /** True if a auto-generated DCI-compliant name should be used for our DCP */ bool _use_dci_name; - std::list<boost::shared_ptr<Content> > _content; + typedef std::list<boost::shared_ptr<Content> > ContentList; + ContentList _content; /** If this is true, we will believe the length specified by the content * file's header; if false, we will run through the whole content file * the first time we see it in order to obtain the length. diff --git a/src/lib/filter_graph.cc b/src/lib/filter_graph.cc index 045cbaa6a..a52c030fe 100644 --- a/src/lib/filter_graph.cc +++ b/src/lib/filter_graph.cc @@ -57,7 +57,7 @@ using libdcp::Size; * @param s Size of the images to process. * @param p Pixel format of the images to process. */ -FilterGraph::FilterGraph (shared_ptr<Film> film, FFmpegDecoder* decoder, libdcp::Size s, AVPixelFormat p) +FilterGraph::FilterGraph (shared_ptr<const Film> film, FFmpegDecoder* decoder, libdcp::Size s, AVPixelFormat p) : _buffer_src_context (0) , _buffer_sink_context (0) , _size (s) diff --git a/src/lib/filter_graph.h b/src/lib/filter_graph.h index 7e4e8422b..db86a677d 100644 --- a/src/lib/filter_graph.h +++ b/src/lib/filter_graph.h @@ -37,7 +37,7 @@ class FFmpegDecoder; class FilterGraph { public: - FilterGraph (boost::shared_ptr<Film> film, FFmpegDecoder* decoder, libdcp::Size s, AVPixelFormat p); + FilterGraph (boost::shared_ptr<const Film>, FFmpegDecoder* decoder, libdcp::Size s, AVPixelFormat p); bool can_process (libdcp::Size s, AVPixelFormat p) const; std::list<boost::shared_ptr<Image> > process (AVFrame const * frame); diff --git a/src/lib/format.cc b/src/lib/format.cc index b506c7000..05b71f2e5 100644 --- a/src/lib/format.cc +++ b/src/lib/format.cc @@ -29,6 +29,7 @@ #include <iostream> #include "format.h" #include "film.h" +#include "playlist.h" #include "i18n.h" @@ -206,16 +207,16 @@ FixedFormat::FixedFormat (int r, libdcp::Size dcp, string id, string n, string d * (so there are dcp_padding() pixels on the left and dcp_padding() on the right) */ int -Format::dcp_padding (shared_ptr<const Film> f) const +Format::dcp_padding (shared_ptr<const Playlist> p) const { - int p = rint ((_dcp_size.width - (_dcp_size.height * ratio_as_integer(f) / 100.0)) / 2.0); + int pad = rint ((_dcp_size.width - (_dcp_size.height * ratio_as_integer(p) / 100.0)) / 2.0); /* This comes out -ve for Scope; bodge it */ - if (p < 0) { - p = 0; + if (pad < 0) { + pad = 0; } - return p; + return pad; } float @@ -231,15 +232,15 @@ VariableFormat::VariableFormat (libdcp::Size dcp, string id, string n, string d, } int -VariableFormat::ratio_as_integer (shared_ptr<const Film> f) const +VariableFormat::ratio_as_integer (shared_ptr<const Playlist> p) const { - return rint (ratio_as_float (f) * 100); + return rint (ratio_as_float (p) * 100); } float -VariableFormat::ratio_as_float (shared_ptr<const Film> f) const +VariableFormat::ratio_as_float (shared_ptr<const Playlist> p) const { - return float (f->size().width) / f->size().height; + return float (p->video_size().width) / p->video_size().height; } /** @return A name to be presented to the user */ diff --git a/src/lib/format.h b/src/lib/format.h index 305524628..94c2253de 100644 --- a/src/lib/format.h +++ b/src/lib/format.h @@ -26,7 +26,7 @@ #include <vector> #include "util.h" -class Film; +class Playlist; class Format { @@ -42,15 +42,15 @@ public: /** @return the aspect ratio multiplied by 100 * (e.g. 239 for Cinemascope 2.39:1) */ - virtual int ratio_as_integer (boost::shared_ptr<const Film> f) const = 0; + virtual int ratio_as_integer (boost::shared_ptr<const Playlist> f) const = 0; /** @return the ratio as a floating point number */ - virtual float ratio_as_float (boost::shared_ptr<const Film> f) const = 0; + virtual float ratio_as_float (boost::shared_ptr<const Playlist> f) const = 0; /** @return the ratio of the container (including any padding) as a floating point number */ float container_ratio_as_float () const; - int dcp_padding (boost::shared_ptr<const Film> f) const; + int dcp_padding (boost::shared_ptr<const Playlist>) const; /** @return size in pixels of the images that we should * put in a DCP for this ratio. This size will not correspond @@ -115,11 +115,11 @@ class FixedFormat : public Format public: FixedFormat (int, libdcp::Size, std::string, std::string, std::string, std::string); - int ratio_as_integer (boost::shared_ptr<const Film>) const { + int ratio_as_integer (boost::shared_ptr<const Playlist>) const { return _ratio; } - float ratio_as_float (boost::shared_ptr<const Film>) const { + float ratio_as_float (boost::shared_ptr<const Playlist>) const { return _ratio / 100.0; } @@ -136,8 +136,8 @@ class VariableFormat : public Format public: VariableFormat (libdcp::Size, std::string, std::string, std::string, std::string); - int ratio_as_integer (boost::shared_ptr<const Film> f) const; - float ratio_as_float (boost::shared_ptr<const Film> f) const; + int ratio_as_integer (boost::shared_ptr<const Playlist> f) const; + float ratio_as_float (boost::shared_ptr<const Playlist> f) const; std::string name () const; }; diff --git a/src/lib/imagemagick_content.cc b/src/lib/imagemagick_content.cc new file mode 100644 index 000000000..d0887c0aa --- /dev/null +++ b/src/lib/imagemagick_content.cc @@ -0,0 +1,2 @@ +#include "imagemagick_content.h" + diff --git a/src/lib/imagemagick_content.h b/src/lib/imagemagick_content.h index 2a8197b69..985aa0e8d 100644 --- a/src/lib/imagemagick_content.h +++ b/src/lib/imagemagick_content.h @@ -2,5 +2,6 @@ class ImageMagickContent : public VideoContent { - +public: + ImageMagickContent (boost::filesystem::path); }; diff --git a/src/lib/imagemagick_decoder.cc b/src/lib/imagemagick_decoder.cc index 5dc0b7b06..aa3c64f93 100644 --- a/src/lib/imagemagick_decoder.cc +++ b/src/lib/imagemagick_decoder.cc @@ -20,6 +20,7 @@ #include <iostream> #include <boost/filesystem.hpp> #include <Magick++.h> +#include "imagemagick_content.h" #include "imagemagick_decoder.h" #include "image.h" #include "film.h" @@ -32,37 +33,20 @@ using boost::shared_ptr; using libdcp::Size; ImageMagickDecoder::ImageMagickDecoder ( - boost::shared_ptr<Film> f, DecodeOptions o) + shared_ptr<const Film> f, shared_ptr<ImageMagickContent> c, DecodeOptions o) : Decoder (f, o) - , VideoDecoder (f, o) + , VideoDecoder (f, c, o) + , _imagemagick_content (c) + , _position (0) { - if (boost::filesystem::is_directory (_film->content_path())) { - for ( - boost::filesystem::directory_iterator i = boost::filesystem::directory_iterator (_film->content_path()); - i != boost::filesystem::directory_iterator(); - ++i) { - - if (still_image_file (i->path().string())) { - _files.push_back (i->path().string()); - } - } - } else { - _files.push_back (_film->content_path ()); - } - - _iter = _files.begin (); + } libdcp::Size ImageMagickDecoder::native_size () const { - if (_files.empty ()) { - throw DecodeError (_("no still image files found")); - } - - /* Look at the first file and assume its size holds for all */ using namespace MagickCore; - Magick::Image* image = new Magick::Image (_film->content_path ()); + Magick::Image* image = new Magick::Image (_imagemagick_content->file().string()); libdcp::Size const s = libdcp::Size (image->columns(), image->rows()); delete image; @@ -72,16 +56,15 @@ ImageMagickDecoder::native_size () const bool ImageMagickDecoder::pass () { - if (_iter == _files.end()) { - if (video_frame() >= _film->still_duration_in_frames()) { - return true; - } - + if (_position > 0 && _position < _imagemagick_content->video_length ()) { repeat_last_video (); + _position++; return false; + } else if (_position >= _imagemagick_content->video_length ()) { + return true; } - Magick::Image* magick_image = new Magick::Image (_film->content_path ()); + Magick::Image* magick_image = new Magick::Image (_imagemagick_content->file().string ()); libdcp::Size size = native_size (); shared_ptr<Image> image (new SimpleImage (PIX_FMT_RGB24, size, false)); @@ -104,7 +87,7 @@ ImageMagickDecoder::pass () emit_video (image, 0); - ++_iter; + ++_position; return false; } @@ -118,10 +101,8 @@ ImageMagickDecoder::pixel_format () const bool ImageMagickDecoder::seek_to_last () { - if (_iter == _files.end()) { - _iter = _files.begin(); - } else { - --_iter; + if (_position > 0) { + --_position; } return false; @@ -130,16 +111,14 @@ ImageMagickDecoder::seek_to_last () bool ImageMagickDecoder::seek (double t) { - int const f = t * frames_per_second(); - - _iter = _files.begin (); - for (int i = 0; i < f; ++i) { - if (_iter == _files.end()) { - return true; - } - ++_iter; + int const f = t * _imagemagick_content->video_frame_rate (); + + if (f >= _imagemagick_content->video_length()) { + _position = 0; + return true; } - + + _position = f; return false; } diff --git a/src/lib/imagemagick_decoder.h b/src/lib/imagemagick_decoder.h index 2f4e2c967..b04bd88b1 100644 --- a/src/lib/imagemagick_decoder.h +++ b/src/lib/imagemagick_decoder.h @@ -23,10 +23,12 @@ namespace Magick { class Image; } +class ImageMagickContent; + class ImageMagickDecoder : public VideoDecoder { public: - ImageMagickDecoder (boost::shared_ptr<Film>, DecodeOptions); + ImageMagickDecoder (boost::shared_ptr<const Film>, boost::shared_ptr<ImageMagickContent>, DecodeOptions); float frames_per_second () const { /* We don't know */ @@ -35,7 +37,7 @@ public: libdcp::Size native_size () const; - SourceFrame length () const { + ContentVideoFrame video_length () const { /* We don't know */ return 0; } @@ -54,9 +56,9 @@ public: bool seek (double); bool seek_to_last (); + bool pass (); protected: - bool pass (); PixelFormat pixel_format () const; int time_base_numerator () const { @@ -79,7 +81,7 @@ protected: private: void film_changed (Film::Property); - - std::list<std::string> _files; - std::list<std::string>::iterator _iter; + + boost::shared_ptr<ImageMagickContent> _imagemagick_content; + ContentVideoFrame _position; }; diff --git a/src/lib/job.cc b/src/lib/job.cc index ace02b8b3..ff0332d6d 100644 --- a/src/lib/job.cc +++ b/src/lib/job.cc @@ -34,8 +34,6 @@ using std::list; using std::stringstream; using boost::shared_ptr; -/** @param s Film that we are operating on. - */ Job::Job (shared_ptr<Film> f) : _film (f) , _thread (0) diff --git a/src/lib/job.h b/src/lib/job.h index fd036bce2..f5175c525 100644 --- a/src/lib/job.h +++ b/src/lib/job.h @@ -38,7 +38,7 @@ class Film; class Job : public boost::enable_shared_from_this<Job> { public: - Job (boost::shared_ptr<Film> s); + Job (boost::shared_ptr<Film>); virtual ~Job() {} /** @return user-readable name of this job */ @@ -87,7 +87,6 @@ protected: void set_state (State); void set_error (std::string s, std::string d); - /** Film for this job */ boost::shared_ptr<Film> _film; private: diff --git a/src/lib/playlist.cc b/src/lib/playlist.cc new file mode 100644 index 000000000..17ecd2f37 --- /dev/null +++ b/src/lib/playlist.cc @@ -0,0 +1,266 @@ +/* + Copyright (C) 2013 Carl Hetherington <cth@carlh.net> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include <boost/shared_ptr.hpp> +#include "playlist.h" +#include "sndfile_content.h" +#include "sndfile_decoder.h" +#include "ffmpeg_content.h" +#include "ffmpeg_decoder.h" +#include "imagemagick_content.h" +#include "imagemagick_decoder.h" + +using std::list; +using boost::shared_ptr; +using boost::dynamic_pointer_cast; + +Playlist::Playlist (shared_ptr<const Film> f, list<shared_ptr<Content> > c) + : _video_from (VIDEO_NONE) + , _audio_from (AUDIO_NONE) + , _ffmpeg_decoder_done (false) +{ + for (list<shared_ptr<Content> >::const_iterator i = c.begin(); i != c.end(); ++i) { + shared_ptr<FFmpegContent> fc = dynamic_pointer_cast<FFmpegContent> (*i); + if (fc) { + assert (!_ffmpeg); + _ffmpeg = fc; + _video_from = VIDEO_FFMPEG; + if (_audio_from == AUDIO_NONE) { + _audio_from = AUDIO_FFMPEG; + } + } + + shared_ptr<ImageMagickContent> ic = dynamic_pointer_cast<ImageMagickContent> (*i); + if (ic) { + _imagemagick.push_back (ic); + if (_video_from == VIDEO_NONE) { + _video_from = VIDEO_IMAGEMAGICK; + } + } + + shared_ptr<SndfileContent> sc = dynamic_pointer_cast<SndfileContent> (*i); + if (sc) { + _sndfile.push_back (sc); + _audio_from = AUDIO_SNDFILE; + } + } + + if (_video_from == VIDEO_FFMPEG || _audio_from == AUDIO_FFMPEG) { + DecodeOptions o; + /* XXX: decodeoptions */ + _ffmpeg_decoder.reset (new FFmpegDecoder (f, _ffmpeg, o)); + } + + if (_video_from == VIDEO_FFMPEG) { + _ffmpeg_decoder->connect_video (shared_from_this ()); + } + + if (_audio_from == AUDIO_FFMPEG) { + _ffmpeg_decoder->connect_audio (shared_from_this ()); + } + + if (_video_from == VIDEO_IMAGEMAGICK) { + for (list<shared_ptr<ImageMagickContent> >::iterator i = _imagemagick.begin(); i != _imagemagick.end(); ++i) { + DecodeOptions o; + /* XXX: decodeoptions */ + shared_ptr<ImageMagickDecoder> d (new ImageMagickDecoder (f, *i, o)); + _imagemagick_decoders.push_back (d); + d->connect_video (shared_from_this ()); + } + + _imagemagick_decoder = _imagemagick_decoders.begin (); + } + + if (_audio_from == AUDIO_SNDFILE) { + for (list<shared_ptr<SndfileContent> >::iterator i = _sndfile.begin(); i != _sndfile.end(); ++i) { + DecodeOptions o; + /* XXX: decodeoptions */ + shared_ptr<SndfileDecoder> d (new SndfileDecoder (f, *i, o)); + _sndfile_decoders.push_back (d); + d->connect_audio (shared_from_this ()); + } + } +} + +ContentAudioFrame +Playlist::audio_length () const +{ + switch (_audio_from) { + case AUDIO_NONE: + return 0; + case AUDIO_FFMPEG: + return _ffmpeg->audio_length (); + case AUDIO_SNDFILE: + { + ContentAudioFrame l = 0; + for (list<shared_ptr<SndfileContent> >::const_iterator i = _sndfile.begin(); i != _sndfile.end(); ++i) { + l += (*i)->audio_length (); + } + return l; + } + } + + return 0; +} + +int +Playlist::audio_channels () const +{ + switch (_audio_from) { + case AUDIO_NONE: + return 0; + case AUDIO_FFMPEG: + return _ffmpeg->audio_channels (); + case AUDIO_SNDFILE: + { + int c = 0; + for (list<shared_ptr<SndfileContent> >::const_iterator i = _sndfile.begin(); i != _sndfile.end(); ++i) { + c += (*i)->audio_channels (); + } + return c; + } + } + + return 0; +} + +int +Playlist::audio_frame_rate () const +{ + switch (_audio_from) { + case AUDIO_NONE: + return 0; + case AUDIO_FFMPEG: + return _ffmpeg->audio_frame_rate (); + case AUDIO_SNDFILE: + return _sndfile.front()->audio_frame_rate (); + } + + return 0; +} + +int64_t +Playlist::audio_channel_layout () const +{ + switch (_audio_from) { + case AUDIO_NONE: + return 0; + case AUDIO_FFMPEG: + return _ffmpeg->audio_channel_layout (); + case AUDIO_SNDFILE: + /* XXX */ + return 0; + } + + return 0; +} + +float +Playlist::video_frame_rate () const +{ + switch (_video_from) { + case VIDEO_NONE: + return 0; + case VIDEO_FFMPEG: + return _ffmpeg->video_frame_rate (); + case VIDEO_IMAGEMAGICK: + return 24; + } + + return 0; +} + +libdcp::Size +Playlist::video_size () const +{ + switch (_video_from) { + case VIDEO_NONE: + return libdcp::Size (); + case VIDEO_FFMPEG: + return _ffmpeg->video_size (); + case VIDEO_IMAGEMAGICK: + /* XXX */ + return _imagemagick.front()->video_size (); + } + + return libdcp::Size (); +} + +bool +Playlist::has_audio () const +{ + return _audio_from != AUDIO_NONE; +} + +void +Playlist::disable_video () +{ + _video_from = VIDEO_NONE; +} + +bool +Playlist::pass () +{ + bool done = true; + + if (_video_from == VIDEO_FFMPEG || _audio_from == AUDIO_FFMPEG) { + if (!_ffmpeg_decoder_done) { + if (_ffmpeg_decoder->pass ()) { + _ffmpeg_decoder_done = true; + } else { + done = false; + } + } + } + + if (_video_from == VIDEO_IMAGEMAGICK) { + if (_imagemagick_decoder != _imagemagick_decoders.end ()) { + if ((*_imagemagick_decoder)->pass ()) { + _imagemagick_decoder++; + } + + if (_imagemagick_decoder != _imagemagick_decoders.end ()) { + done = false; + } + } + } + + /* XXX: sndfile */ + + return done; +} + +void +Playlist::set_progress (shared_ptr<Job> job) +{ + /* XXX */ +} + +void +Playlist::process_video (shared_ptr<Image> i, bool same, shared_ptr<Subtitle> s) +{ + Video (i, same, s); +} + +void +Playlist::process_audio (shared_ptr<AudioBuffers> b) +{ + Audio (b); +} + diff --git a/src/lib/playlist.h b/src/lib/playlist.h index 4add76344..b42d46036 100644 --- a/src/lib/playlist.h +++ b/src/lib/playlist.h @@ -17,14 +17,66 @@ */ +#include <list> #include <boost/shared_ptr.hpp> +#include <boost/enable_shared_from_this.hpp> #include "video_source.h" +#include "audio_source.h" #include "video_sink.h" +#include "audio_sink.h" class Content; +class FFmpegContent; +class FFmpegDecoder; +class ImageMagickContent; +class ImageMagickDecoder; +class SndfileContent; +class SndfileDecoder; +class Job; +class Film; -class Playlist : public VideoSource, public AudioSource +class Playlist : public VideoSource, public AudioSource, public VideoSink, public AudioSink, public boost::enable_shared_from_this<Playlist> { public: - Playlist (std::list<boost::shared_ptr<Content> >); + Playlist (boost::shared_ptr<const Film>, std::list<boost::shared_ptr<Content> >); + + ContentAudioFrame audio_length () const; + int audio_channels () const; + int audio_frame_rate () const; + int64_t audio_channel_layout () const; + bool has_audio () const; + + float video_frame_rate () const; + libdcp::Size video_size () const; + + void disable_video (); + + bool pass (); + void set_progress (boost::shared_ptr<Job>); + +private: + void process_video (boost::shared_ptr<Image> i, bool same, boost::shared_ptr<Subtitle> s); + void process_audio (boost::shared_ptr<AudioBuffers>); + + enum { + VIDEO_NONE, + VIDEO_FFMPEG, + VIDEO_IMAGEMAGICK + } _video_from; + + enum { + AUDIO_NONE, + AUDIO_FFMPEG, + AUDIO_SNDFILE + } _audio_from; + + boost::shared_ptr<FFmpegContent> _ffmpeg; + std::list<boost::shared_ptr<ImageMagickContent> > _imagemagick; + std::list<boost::shared_ptr<SndfileContent> > _sndfile; + + boost::shared_ptr<FFmpegDecoder> _ffmpeg_decoder; + bool _ffmpeg_decoder_done; + std::list<boost::shared_ptr<ImageMagickDecoder> > _imagemagick_decoders; + std::list<boost::shared_ptr<ImageMagickDecoder> >::iterator _imagemagick_decoder; + std::list<boost::shared_ptr<SndfileDecoder> > _sndfile_decoders; }; diff --git a/src/lib/scp_dcp_job.h b/src/lib/scp_dcp_job.h index 08d8e2c78..8c16d53fb 100644 --- a/src/lib/scp_dcp_job.h +++ b/src/lib/scp_dcp_job.h @@ -34,7 +34,7 @@ public: private: void set_status (std::string); - + mutable boost::mutex _status_mutex; std::string _status; }; diff --git a/src/lib/sndfile_content.cc b/src/lib/sndfile_content.cc new file mode 100644 index 000000000..8f5b28901 --- /dev/null +++ b/src/lib/sndfile_content.cc @@ -0,0 +1,41 @@ +#include "sndfile_content.h" +#include "compose.hpp" + +#include "i18n.h" + +using namespace std; + +string +SndfileContent::summary () const +{ + return String::compose (_("Sound file: %1"), file().filename ()); +} + +int +SndfileContent::audio_channels () const +{ + /* XXX */ + return 0; +} + +ContentAudioFrame +SndfileContent::audio_length () const +{ + /* XXX */ + return 0; +} + +int +SndfileContent::audio_frame_rate () const +{ + /* XXX */ + return 0; +} + +int64_t +SndfileContent::audio_channel_layout () const +{ + /* XXX */ + return 0; +} + diff --git a/src/lib/sndfile_content.h b/src/lib/sndfile_content.h index 382e55c40..e84617ed3 100644 --- a/src/lib/sndfile_content.h +++ b/src/lib/sndfile_content.h @@ -3,5 +3,11 @@ class SndfileContent : public AudioContent { public: + std::string summary () const; + /* AudioDecoder */ + int audio_channels () const; + ContentAudioFrame audio_length () const; + int audio_frame_rate () const; + int64_t audio_channel_layout () const; }; diff --git a/src/lib/sndfile_decoder.cc b/src/lib/sndfile_decoder.cc index 0e3e5e234..8848ff4f8 100644 --- a/src/lib/sndfile_decoder.cc +++ b/src/lib/sndfile_decoder.cc @@ -19,6 +19,7 @@ #include <iostream> #include <sndfile.h> +#include "sndfile_content.h" #include "sndfile_decoder.h" #include "film.h" #include "exceptions.h" @@ -33,156 +34,53 @@ using std::cout; using boost::shared_ptr; using boost::optional; -SndfileDecoder::SndfileDecoder (shared_ptr<Film> f, DecodeOptions o) +/* XXX */ + +SndfileDecoder::SndfileDecoder (shared_ptr<const Film> f, shared_ptr<SndfileContent> c, DecodeOptions o) : Decoder (f, o) - , AudioDecoder (f, o) + , AudioDecoder (f, c, o) { sf_count_t frames; - vector<SNDFILE*> sf = open_files (frames); - close_files (sf); + SNDFILE* sf = open_file (frames); + sf_close (sf); } -vector<SNDFILE*> -SndfileDecoder::open_files (sf_count_t & frames) +SNDFILE* +SndfileDecoder::open_file (sf_count_t & frames) { - vector<string> const files = _film->external_audio (); - - int N = 0; - for (size_t i = 0; i < files.size(); ++i) { - if (!files[i].empty()) { - N = i + 1; - } - } - - if (N == 0) { - return vector<SNDFILE*> (); - } - - bool first = true; frames = 0; - vector<SNDFILE*> sndfiles; - for (size_t i = 0; i < (size_t) N; ++i) { - if (files[i].empty ()) { - sndfiles.push_back (0); - } else { - SF_INFO info; - SNDFILE* s = sf_open (files[i].c_str(), SFM_READ, &info); - if (!s) { - throw DecodeError (_("could not open external audio file for reading")); - } - - if (info.channels != 1) { - throw DecodeError (_("external audio files must be mono")); - } - - sndfiles.push_back (s); - - if (first) { - shared_ptr<SndfileStream> st ( - new SndfileStream ( - info.samplerate, av_get_default_channel_layout (N) - ) - ); - - _audio_streams.push_back (st); - _audio_stream = st; - frames = info.frames; - first = false; - } else { - if (info.frames != frames) { - throw DecodeError (_("external audio files have differing lengths")); - } - } - } + SF_INFO info; + SNDFILE* s = sf_open (_sndfile_content->file().string().c_str(), SFM_READ, &info); + if (!s) { + throw DecodeError (_("could not open external audio file for reading")); } - return sndfiles; + frames = info.frames; + return s; } bool SndfileDecoder::pass () { sf_count_t frames; - vector<SNDFILE*> sndfiles = open_files (frames); - if (sndfiles.empty()) { - return true; - } + SNDFILE* sndfile = open_file (frames); /* Do things in half second blocks as I think there may be limits to what FFmpeg (and in particular the resampler) can cope with. */ - sf_count_t const block = _audio_stream->sample_rate() / 2; + sf_count_t const block = _sndfile_content->audio_frame_rate() / 2; - shared_ptr<AudioBuffers> audio (new AudioBuffers (_audio_stream->channels(), block)); + shared_ptr<AudioBuffers> audio (new AudioBuffers (_sndfile_content->audio_channels(), block)); while (frames > 0) { sf_count_t const this_time = min (block, frames); - for (size_t i = 0; i < sndfiles.size(); ++i) { - if (!sndfiles[i]) { - audio->make_silent (i); - } else { - sf_read_float (sndfiles[i], audio->data(i), block); - } - } - + sf_read_float (sndfile, audio->data(0), this_time); audio->set_frames (this_time); Audio (audio); frames -= this_time; } - close_files (sndfiles); + sf_close (sndfile); return true; } - -void -SndfileDecoder::close_files (vector<SNDFILE*> const & sndfiles) -{ - for (size_t i = 0; i < sndfiles.size(); ++i) { - sf_close (sndfiles[i]); - } -} - -shared_ptr<SndfileStream> -SndfileStream::create () -{ - return shared_ptr<SndfileStream> (new SndfileStream); -} - -shared_ptr<SndfileStream> -SndfileStream::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<SndfileStream> (); - } - - stringstream s (t); - string type; - s >> type; - if (type != N_("external")) { - return shared_ptr<SndfileStream> (); - } - - return shared_ptr<SndfileStream> (new SndfileStream (t, v)); -} - -SndfileStream::SndfileStream (string t, optional<int> v) -{ - assert (v); - - stringstream s (t); - string type; - s >> type >> _sample_rate >> _channel_layout; -} - -SndfileStream::SndfileStream () -{ - -} - -string -SndfileStream::to_string () const -{ - return String::compose (N_("external %1 %2"), _sample_rate, _channel_layout); -} diff --git a/src/lib/sndfile_decoder.h b/src/lib/sndfile_decoder.h index e16eab673..c06b97a60 100644 --- a/src/lib/sndfile_decoder.h +++ b/src/lib/sndfile_decoder.h @@ -20,35 +20,19 @@ #include <sndfile.h> #include "decoder.h" #include "audio_decoder.h" -#include "stream.h" -class SndfileStream : public AudioStream -{ -public: - SndfileStream (int sample_rate, int64_t layout) - : AudioStream (sample_rate, layout) - {} - - std::string to_string () const; - - static boost::shared_ptr<SndfileStream> create (); - static boost::shared_ptr<SndfileStream> create (std::string t, boost::optional<int> v); - -private: - friend class stream_test; - - SndfileStream (); - SndfileStream (std::string t, boost::optional<int> v); -}; +class SndfileContent; class SndfileDecoder : public AudioDecoder { public: - SndfileDecoder (boost::shared_ptr<Film>, DecodeOptions); + SndfileDecoder (boost::shared_ptr<const Film>, boost::shared_ptr<SndfileContent>, DecodeOptions); bool pass (); private: - std::vector<SNDFILE*> open_files (sf_count_t &); - void close_files (std::vector<SNDFILE*> const &); + SNDFILE* open_file (sf_count_t &); + void close_file (SNDFILE*); + + boost::shared_ptr<SndfileContent> _sndfile_content; }; diff --git a/src/lib/stream.cc b/src/lib/stream.cc deleted file mode 100644 index bfe7b5eb4..000000000 --- a/src/lib/stream.cc +++ /dev/null @@ -1,92 +0,0 @@ -/* - Copyright (C) 2012 Carl Hetherington <cth@carlh.net> - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - -*/ - -#include <sstream> -#include "compose.hpp" -#include "stream.h" -#include "ffmpeg_decoder.h" -#include "sndfile_decoder.h" - -#include "i18n.h" - -using std::string; -using std::stringstream; -using boost::shared_ptr; -using boost::optional; - -/** Construct a SubtitleStream from a value returned from to_string(). - * @param t String returned from to_string(). - * @param v State file version. - */ -SubtitleStream::SubtitleStream (string t, boost::optional<int>) -{ - stringstream n (t); - n >> _id; - - size_t const s = t.find (' '); - if (s != string::npos) { - _name = t.substr (s + 1); - } -} - -/** @return A canonical string representation of this stream */ -string -SubtitleStream::to_string () const -{ - return String::compose (N_("%1 %2"), _id, _name); -} - -/** Create a SubtitleStream from a value returned from to_string(). - * @param t String returned from to_string(). - * @param v State file version. - */ -shared_ptr<SubtitleStream> -SubtitleStream::create (string t, optional<int> v) -{ - return shared_ptr<SubtitleStream> (new SubtitleStream (t, v)); -} - -/** Create an AudioStream from a string returned from to_string(). - * @param t String returned from to_string(). - * @param v State file version. - * @return AudioStream, or 0. - */ -shared_ptr<AudioStream> -audio_stream_factory (string t, optional<int> v) -{ - shared_ptr<AudioStream> s; - - s = FFmpegAudioStream::create (t, v); - if (!s) { - s = SndfileStream::create (t, v); - } - - return s; -} - -/** Create a SubtitleStream from a string returned from to_string(). - * @param t String returned from to_string(). - * @param v State file version. - * @return SubtitleStream, or 0. - */ -shared_ptr<SubtitleStream> -subtitle_stream_factory (string t, optional<int> v) -{ - return SubtitleStream::create (t, v); -} diff --git a/src/lib/stream.h b/src/lib/stream.h deleted file mode 100644 index 16b06e4bc..000000000 --- a/src/lib/stream.h +++ /dev/null @@ -1,121 +0,0 @@ -/* - Copyright (C) 2012 Carl Hetherington <cth@carlh.net> - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - -*/ - -/** @file src/lib/stream.h - * @brief Representations of audio and subtitle streams. - * - * Some content may have multiple `streams' of audio and/or subtitles; perhaps - * for multiple languages, or for stereo / surround mixes. These classes represent - * those streams, and know about their details. - */ - -#ifndef DVDOMATIC_STREAM_H -#define DVDOMATIC_STREAM_H - -#include <stdint.h> -#include <boost/shared_ptr.hpp> -#include <boost/optional.hpp> -extern "C" { -#include <libavutil/audioconvert.h> -} - -/** @class Stream - * @brief Parent class for streams. - */ -class Stream -{ -public: - virtual ~Stream () {} - virtual std::string to_string () const = 0; -}; - -/** @class AudioStream - * @brief A stream of audio data. - */ -struct AudioStream : public Stream -{ -public: - AudioStream (int r, int64_t l) - : _sample_rate (r) - , _channel_layout (l) - {} - - /* Only used for backwards compatibility for state file version < 1 */ - void set_sample_rate (int s) { - _sample_rate = s; - } - - int channels () const { - return av_get_channel_layout_nb_channels (_channel_layout); - } - - int sample_rate () const { - return _sample_rate; - } - - int64_t channel_layout () const { - return _channel_layout; - } - -protected: - AudioStream () - : _sample_rate (0) - , _channel_layout (0) - {} - - int _sample_rate; - int64_t _channel_layout; -}; - -/** @class SubtitleStream - * @brief A stream of subtitle data. - */ -class SubtitleStream : public Stream -{ -public: - SubtitleStream (std::string n, int 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 diff --git a/src/lib/transcode_job.cc b/src/lib/transcode_job.cc index 234ebe051..f8810975b 100644 --- a/src/lib/transcode_job.cc +++ b/src/lib/transcode_job.cc @@ -62,8 +62,7 @@ TranscodeJob::run () _film->log()->log (N_("Transcode job starting")); _film->log()->log (String::compose (N_("Audio delay is %1ms"), _film->audio_delay())); - _encoder.reset (new Encoder (_film)); - Transcoder w (_film, _decode_opt, this, _encoder); + Transcoder w (_film, _decode_opt, shared_from_this ()); w.go (); set_progress (1); set_state (FINISHED_OK); @@ -83,11 +82,13 @@ TranscodeJob::run () string TranscodeJob::status () const { - if (!_encoder) { - return _("0%"); - } +// if (!_encoder) { +// return _("0%"); +// } - float const fps = _encoder->current_frames_per_second (); + /* XXX */ +// float const fps = _encoder->current_frames_per_second (); + float const fps = 0; if (fps == 0) { return Job::status (); } @@ -106,12 +107,15 @@ TranscodeJob::status () const int TranscodeJob::remaining_time () const { + return 0; +#if 0 + XXX float fps = _encoder->current_frames_per_second (); if (fps == 0) { return 0; } - if (!_film->length()) { + if (!_video->length()) { return 0; } @@ -126,4 +130,5 @@ TranscodeJob::remaining_time () const /* We assume that dcp_length() is valid, if it is set */ int const left = length - _encoder->video_frames_out(); return left / fps; +#endif } diff --git a/src/lib/transcode_job.h b/src/lib/transcode_job.h index 9b69e4e65..a409ec97e 100644 --- a/src/lib/transcode_job.h +++ b/src/lib/transcode_job.h @@ -44,5 +44,4 @@ protected: private: DecodeOptions _decode_opt; - boost::shared_ptr<Encoder> _encoder; }; diff --git a/src/lib/transcoder.cc b/src/lib/transcoder.cc index e0f3a03a2..19d067149 100644 --- a/src/lib/transcoder.cc +++ b/src/lib/transcoder.cc @@ -36,6 +36,7 @@ #include "gain.h" #include "video_decoder.h" #include "audio_decoder.h" +#include "playlist.h" using std::string; using boost::shared_ptr; @@ -47,68 +48,42 @@ using boost::dynamic_pointer_cast; * @param j Job that we are running under, or 0. * @param e Encoder to use. */ -Transcoder::Transcoder (shared_ptr<Film> f, DecodeOptions o, Job* j, shared_ptr<Encoder> e) +Transcoder::Transcoder (shared_ptr<Film> f, DecodeOptions o, shared_ptr<Job> j) : _job (j) - , _encoder (e) - , _decoders (decoder_factory (f, o)) + , _playlist (f->playlist ()) + , _encoder (new Encoder (f, _playlist)) { - assert (_encoder); - - if (f->audio_stream()) { - shared_ptr<AudioStream> st = f->audio_stream(); - _matcher.reset (new Matcher (f->log(), st->sample_rate(), f->source_frame_rate())); - _delay_line.reset (new DelayLine (f->log(), st->channels(), f->audio_delay() * st->sample_rate() / 1000)); + if (_playlist->has_audio ()) { + _matcher.reset (new Matcher (f->log(), _playlist->audio_frame_rate(), _playlist->video_frame_rate())); + _delay_line.reset (new DelayLine (f->log(), _playlist->audio_channels(), f->audio_delay() * _playlist->audio_frame_rate() / 1000)); _gain.reset (new Gain (f->log(), f->audio_gain())); } - /* Set up the decoder to use the film's set streams */ - _decoders.video->set_subtitle_stream (f->subtitle_stream ()); - if (_decoders.audio) { - _decoders.audio->set_audio_stream (f->audio_stream ()); - } - if (_matcher) { - _decoders.video->connect_video (_matcher); + _playlist->connect_video (_matcher); _matcher->connect_video (_encoder); } else { - _decoders.video->connect_video (_encoder); + _playlist->connect_video (_encoder); } - if (_matcher && _delay_line && _decoders.audio) { - _decoders.audio->connect_audio (_delay_line); + if (_matcher && _delay_line && _playlist->has_audio ()) { + _playlist->connect_audio (_delay_line); _delay_line->connect_audio (_matcher); _matcher->connect_audio (_gain); _gain->connect_audio (_encoder); } } -/** Run the decoder, passing its output to the encoder, until the decoder - * has no more data to present. - */ void Transcoder::go () { _encoder->process_begin (); try { - bool done[2] = { false, false }; - while (1) { - if (!done[0]) { - done[0] = _decoders.video->pass (); - if (_job) { - _decoders.video->set_progress (_job); - } - } - - if (!done[1] && _decoders.audio && dynamic_pointer_cast<Decoder> (_decoders.audio) != dynamic_pointer_cast<Decoder> (_decoders.video)) { - done[1] = _decoders.audio->pass (); - } else { - done[1] = true; - } - - if (done[0] && done[1]) { + if (_playlist->pass ()) { break; } + _playlist->set_progress (_job); } } catch (...) { diff --git a/src/lib/transcoder.h b/src/lib/transcoder.h index b0c263d07..8d34af948 100644 --- a/src/lib/transcoder.h +++ b/src/lib/transcoder.h @@ -32,9 +32,8 @@ class Encoder; class Matcher; class VideoFilter; class Gain; -class VideoDecoder; -class AudioDecoder; class DelayLine; +class Playlist; /** @class Transcoder * @brief A class which takes a Film and some Options, then uses those to transcode the film. @@ -48,23 +47,16 @@ public: Transcoder ( boost::shared_ptr<Film> f, DecodeOptions o, - Job* j, - boost::shared_ptr<Encoder> e + boost::shared_ptr<Job> j ); void go (); - boost::shared_ptr<VideoDecoder> video_decoder () const { - return _decoders.video; - } - protected: /** A Job that is running this Transcoder, or 0 */ - Job* _job; - /** The encoder that we will use */ + boost::shared_ptr<Job> _job; + boost::shared_ptr<Playlist> _playlist; boost::shared_ptr<Encoder> _encoder; - /** The decoders that we will use */ - Decoders _decoders; boost::shared_ptr<Matcher> _matcher; boost::shared_ptr<DelayLine> _delay_line; boost::shared_ptr<Gain> _gain; diff --git a/src/lib/util.cc b/src/lib/util.cc index 48cb17c26..024740867 100644 --- a/src/lib/util.cc +++ b/src/lib/util.cc @@ -347,11 +347,11 @@ md5_digest (void const * data, int size) * @return MD5 digest of file's contents. */ string -md5_digest (string file) +md5_digest (boost::filesystem::path file) { - ifstream f (file.c_str(), ios::binary); + ifstream f (file.string().c_str(), ios::binary); if (!f.good ()) { - throw OpenFileError (file); + throw OpenFileError (file.string()); } f.seekg (0, ios::end); diff --git a/src/lib/util.h b/src/lib/util.h index 3d251cf06..87274cfff 100644 --- a/src/lib/util.h +++ b/src/lib/util.h @@ -57,7 +57,7 @@ extern double seconds (struct timeval); extern void dvdomatic_setup (); extern void dvdomatic_setup_i18n (std::string); extern std::vector<std::string> split_at_spaces_considering_quotes (std::string); -extern std::string md5_digest (std::string); +extern std::string md5_digest (boost::filesystem::path); extern std::string md5_digest (void const *, int); extern void ensure_ui_thread (); extern std::string audio_channel_name (int); @@ -66,6 +66,8 @@ extern boost::filesystem::path mo_path (); #endif typedef int SourceFrame; +typedef int64_t ContentAudioFrame; +typedef int ContentVideoFrame; struct FrameRateConversion { diff --git a/src/lib/video_content.cc b/src/lib/video_content.cc new file mode 100644 index 000000000..9fc5cf1a2 --- /dev/null +++ b/src/lib/video_content.cc @@ -0,0 +1,28 @@ +#include "video_content.h" +#include "video_decoder.h" + +int const VideoContentProperty::VIDEO_LENGTH = 0; +int const VideoContentProperty::VIDEO_SIZE = 1; +int const VideoContentProperty::VIDEO_FRAME_RATE = 2; + +using boost::shared_ptr; + +VideoContent::VideoContent (boost::filesystem::path f) + : Content (f) + , _video_length (0) +{ + +} + +void +VideoContent::take_from_video_decoder (shared_ptr<VideoDecoder> d) +{ + { + boost::mutex::scoped_lock lm (_mutex); + _video_size = d->native_size (); + _video_frame_rate = d->frames_per_second (); + } + + Changed (VideoContentProperty::VIDEO_SIZE); + Changed (VideoContentProperty::VIDEO_FRAME_RATE); +} diff --git a/src/lib/video_content.h b/src/lib/video_content.h index ee4a64fc4..219130668 100644 --- a/src/lib/video_content.h +++ b/src/lib/video_content.h @@ -1,8 +1,47 @@ +#ifndef DVDOMATIC_VIDEO_CONTENT_H +#define DVDOMATIC_VIDEO_CONTENT_H + #include "content.h" +#include "util.h" + +class VideoDecoder; + +class VideoContentProperty +{ +public: + static int const VIDEO_LENGTH; + static int const VIDEO_SIZE; + static int const VIDEO_FRAME_RATE; +}; class VideoContent : public virtual Content { public: + VideoContent (boost::filesystem::path); + + ContentVideoFrame video_length () const { + boost::mutex::scoped_lock lm (_mutex); + return _video_length; + } + + libdcp::Size video_size () const { + boost::mutex::scoped_lock lm (_mutex); + return _video_size; + } + float video_frame_rate () const { + boost::mutex::scoped_lock lm (_mutex); + return _video_frame_rate; + } + +protected: + void take_from_video_decoder (boost::shared_ptr<VideoDecoder>); + ContentVideoFrame _video_length; + +private: + libdcp::Size _video_size; + float _video_frame_rate; }; + +#endif diff --git a/src/lib/video_decoder.cc b/src/lib/video_decoder.cc index 891720f6b..ca1e7ab56 100644 --- a/src/lib/video_decoder.cc +++ b/src/lib/video_decoder.cc @@ -30,7 +30,7 @@ using boost::shared_ptr; using boost::optional; -VideoDecoder::VideoDecoder (shared_ptr<Film> f, DecodeOptions o) +VideoDecoder::VideoDecoder (shared_ptr<const Film> f, shared_ptr<VideoContent> c, DecodeOptions o) : Decoder (f, o) , _video_frame (0) , _last_source_time (0) @@ -102,21 +102,15 @@ VideoDecoder::emit_subtitle (shared_ptr<TimedSubtitle> s) } } -/** Set which stream of subtitles we should use from our source. - * @param s Stream to use. - */ -void -VideoDecoder::set_subtitle_stream (shared_ptr<SubtitleStream> s) -{ - _subtitle_stream = s; -} - void VideoDecoder::set_progress (Job* j) const { assert (j); - + +#if 0 + XXX if (_film->length()) { j->set_progress (float (_video_frame) / _film->length().get()); } +#endif } diff --git a/src/lib/video_decoder.h b/src/lib/video_decoder.h index 283ab5d88..a52e5448a 100644 --- a/src/lib/video_decoder.h +++ b/src/lib/video_decoder.h @@ -21,42 +21,33 @@ #define DVDOMATIC_VIDEO_DECODER_H #include "video_source.h" -#include "stream.h" #include "decoder.h" +class VideoContent; + class VideoDecoder : public VideoSource, public virtual Decoder { public: - VideoDecoder (boost::shared_ptr<Film>, DecodeOptions); + VideoDecoder (boost::shared_ptr<const Film>, boost::shared_ptr<VideoContent>, DecodeOptions); /** @return video frames per second, or 0 if unknown */ virtual float frames_per_second () const = 0; /** @return native size in pixels */ virtual libdcp::Size native_size () const = 0; - /** @return length (in source video frames), according to our content's header */ - virtual SourceFrame length () const = 0; + /** @return length according to our content's header */ + virtual ContentVideoFrame video_length () const = 0; virtual int time_base_numerator () const = 0; virtual int time_base_denominator () const = 0; virtual int sample_aspect_ratio_numerator () const = 0; virtual int sample_aspect_ratio_denominator () const = 0; - virtual void set_subtitle_stream (boost::shared_ptr<SubtitleStream>); - void set_progress (Job *) const; int video_frame () const { return _video_frame; } - boost::shared_ptr<SubtitleStream> subtitle_stream () const { - return _subtitle_stream; - } - - std::vector<boost::shared_ptr<SubtitleStream> > subtitle_streams () const { - return _subtitle_streams; - } - double last_source_time () const { return _last_source_time; } @@ -69,11 +60,6 @@ protected: void emit_subtitle (boost::shared_ptr<TimedSubtitle>); void repeat_last_video (); - /** Subtitle stream to use when decoding */ - boost::shared_ptr<SubtitleStream> _subtitle_stream; - /** Subtitle streams that this decoder's content has */ - std::vector<boost::shared_ptr<SubtitleStream> > _subtitle_streams; - private: void signal_video (boost::shared_ptr<Image>, bool, boost::shared_ptr<Subtitle>); diff --git a/src/lib/writer.cc b/src/lib/writer.cc index 2d7ee9ba3..5f5dae98f 100644 --- a/src/lib/writer.cc +++ b/src/lib/writer.cc @@ -28,6 +28,7 @@ #include "format.h" #include "log.h" #include "dcp_video_frame.h" +#include "playlist.h" #include "i18n.h" @@ -41,8 +42,9 @@ using boost::shared_ptr; int const Writer::_maximum_frames_in_memory = 8; -Writer::Writer (shared_ptr<Film> f) +Writer::Writer (shared_ptr<Film> f, shared_ptr<Playlist> p) : _film (f) + , _playlist (p) , _first_nonexistant_frame (0) , _thread (0) , _finish (false) @@ -74,7 +76,7 @@ Writer::Writer (shared_ptr<Film> f) _picture_asset_writer = _picture_asset->start_write (_first_nonexistant_frame > 0); - AudioMapping m (_film->audio_channels ()); + AudioMapping m (_playlist->audio_channels ()); if (m.dcp_channels() > 0) { _sound_asset.reset ( @@ -83,7 +85,7 @@ Writer::Writer (shared_ptr<Film> f) N_("audio.mxf"), _film->dcp_frame_rate (), m.dcp_channels (), - dcp_audio_sample_rate (_film->audio_stream()->sample_rate()) + dcp_audio_sample_rate (_playlist->audio_frame_rate()) ) ); diff --git a/src/lib/writer.h b/src/lib/writer.h index beb16c7b9..920f592b6 100644 --- a/src/lib/writer.h +++ b/src/lib/writer.h @@ -26,6 +26,7 @@ class Film; class EncodedData; class AudioBuffers; +class Playlist; namespace libdcp { class MonoPictureAsset; @@ -63,7 +64,7 @@ bool operator== (QueueItem const & a, QueueItem const & b); class Writer : public ExceptionStore { public: - Writer (boost::shared_ptr<Film>); + Writer (boost::shared_ptr<Film>, boost::shared_ptr<Playlist>); bool can_fake_write (int) const; @@ -80,6 +81,7 @@ private: /** our Film */ boost::shared_ptr<Film> _film; + boost::shared_ptr<Playlist> _playlist; /** the first frame index that does not already exist in our MXF */ int _first_nonexistant_frame; diff --git a/src/lib/wscript b/src/lib/wscript index 5d9f5626a..5510d48a8 100644 --- a/src/lib/wscript +++ b/src/lib/wscript @@ -6,10 +6,12 @@ sources = """ ab_transcoder.cc analyse_audio_job.cc audio_analysis.cc + audio_content.cc audio_decoder.cc audio_source.cc config.cc combiner.cc + content.cc cross.cc dci_metadata.cc dcp_content_type.cc @@ -23,24 +25,27 @@ sources = """ exceptions.cc filter_graph.cc ffmpeg_compatibility.cc + ffmpeg_content.cc ffmpeg_decoder.cc film.cc filter.cc format.cc gain.cc image.cc + imagemagick_content.cc imagemagick_decoder.cc job.cc job_manager.cc log.cc lut.cc matcher.cc + playlist.cc scp_dcp_job.cc scaler.cc server.cc + sndfile_content.cc sndfile_decoder.cc sound_processor.cc - stream.cc subtitle.cc timer.cc transcode_job.cc @@ -48,6 +53,7 @@ sources = """ ui_signaller.cc util.cc version.cc + video_content.cc video_decoder.cc video_source.cc writer.cc |
