diff options
Diffstat (limited to 'src/lib')
47 files changed, 336 insertions, 546 deletions
diff --git a/src/lib/ab_transcode_job.cc b/src/lib/ab_transcode_job.cc index c9fd5bc97..b9538ce2e 100644 --- a/src/lib/ab_transcode_job.cc +++ b/src/lib/ab_transcode_job.cc @@ -33,9 +33,10 @@ using boost::shared_ptr; /** @param f Film to compare. * @param o Options. */ -ABTranscodeJob::ABTranscodeJob (shared_ptr<Film> f, shared_ptr<const Options> o, shared_ptr<Job> req) +ABTranscodeJob::ABTranscodeJob (shared_ptr<Film> f, shared_ptr<const DecodeOptions> od, shared_ptr<const EncodeOptions> oe, shared_ptr<Job> req) : Job (f, req) - , _opt (o) + , _decode_opt (od) + , _encode_opt (oe) { _film_b.reset (new Film (*_film)); _film_b->set_scaler (Config::instance()->reference_scaler ()); @@ -53,7 +54,7 @@ ABTranscodeJob::run () { try { /* _film_b is the one with reference filters */ - ABTranscoder w (_film_b, _film, _opt, this, encoder_factory (_film, _opt)); + ABTranscoder w (_film_b, _film, _decode_opt, this, encoder_factory (_film, _encode_opt)); 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 8331edf76..86a2a81b8 100644 --- a/src/lib/ab_transcode_job.h +++ b/src/lib/ab_transcode_job.h @@ -25,6 +25,8 @@ #include "job.h" class Film; +class DecodeOptions; +class EncodeOptions; /** @class ABTranscodeJob * @brief Job to run a transcoder which produces output for A/B comparison of various settings. @@ -36,13 +38,19 @@ class Film; class ABTranscodeJob : public Job { public: - ABTranscodeJob (boost::shared_ptr<Film> f, boost::shared_ptr<const Options> o, boost::shared_ptr<Job> req); + ABTranscodeJob ( + boost::shared_ptr<Film> f, + boost::shared_ptr<const DecodeOptions> od, + boost::shared_ptr<const EncodeOptions> oe, + boost::shared_ptr<Job> req + ); std::string name () const; void run (); private: - boost::shared_ptr<const Options> _opt; + boost::shared_ptr<const DecodeOptions> _decode_opt; + boost::shared_ptr<const EncodeOptions> _encode_opt; /** Copy of our Film using the reference filters and scaler */ boost::shared_ptr<Film> _film_b; diff --git a/src/lib/ab_transcoder.cc b/src/lib/ab_transcoder.cc index 537cb4dd7..d85f078a5 100644 --- a/src/lib/ab_transcoder.cc +++ b/src/lib/ab_transcoder.cc @@ -43,16 +43,15 @@ using boost::shared_ptr; /** @param a Film to use for the left half of the screen. * @param b Film to use for the right half of the screen. - * @param o Options. + * @param o Decoder options. * @param j Job that we are associated with. * @param e Encoder to use. */ ABTranscoder::ABTranscoder ( - shared_ptr<Film> a, shared_ptr<Film> b, shared_ptr<const Options> o, Job* j, shared_ptr<Encoder> e) + shared_ptr<Film> a, shared_ptr<Film> b, shared_ptr<const DecodeOptions> o, Job* j, shared_ptr<Encoder> e) : _film_a (a) , _film_b (b) - , _opt (o) , _job (j) , _encoder (e) { @@ -67,12 +66,12 @@ ABTranscoder::ABTranscoder ( } /* Set up the decoder to use the film's set streams */ - _da.first->set_subtitle_stream (_film_a->subtitle_stream ()); - _db.first->set_subtitle_stream (_film_a->subtitle_stream ()); - _da.second->set_audio_stream (_film_a->audio_stream ()); + _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.first->Video.connect (bind (&Combiner::process_video, _combiner, _1, _2)); - _db.first->Video.connect (bind (&Combiner::process_video_b, _combiner, _1, _2)); + _da.video->Video.connect (bind (&Combiner::process_video, _combiner, _1, _2)); + _db.video->Video.connect (bind (&Combiner::process_video_b, _combiner, _1, _2)); if (_matcher) { _combiner->connect_video (_matcher); @@ -82,7 +81,7 @@ ABTranscoder::ABTranscoder ( } if (_matcher && _delay_line) { - _da.second->connect_audio (_delay_line); + _da.audio->connect_audio (_delay_line); _delay_line->connect_audio (_matcher); _matcher->connect_audio (_gain); _gain->connect_audio (_encoder); @@ -95,11 +94,11 @@ ABTranscoder::go () _encoder->process_begin (); while (1) { - bool const va = _da.first->pass (); - bool const vb = _db.first->pass (); - bool const a = _da.first->pass (); + bool const va = _da.video->pass (); + bool const vb = _db.video->pass (); + bool const a = _da.audio->pass (); - _da.first->set_progress (); + _da.video->set_progress (); if (va && vb && a) { break; diff --git a/src/lib/ab_transcoder.h b/src/lib/ab_transcoder.h index 9b57e4f73..7bfcb393c 100644 --- a/src/lib/ab_transcoder.h +++ b/src/lib/ab_transcoder.h @@ -25,12 +25,13 @@ #include <boost/shared_ptr.hpp> #include <stdint.h> #include "util.h" +#include "decoder_factory.h" class Job; class Encoder; class VideoDecoder; class AudioDecoder; -class Options; +class DecodeOptions; class Image; class Log; class Subtitle; @@ -50,7 +51,7 @@ public: ABTranscoder ( boost::shared_ptr<Film> a, boost::shared_ptr<Film> b, - boost::shared_ptr<const Options> o, + boost::shared_ptr<const DecodeOptions> o, Job* j, boost::shared_ptr<Encoder> e ); @@ -60,11 +61,10 @@ public: private: boost::shared_ptr<Film> _film_a; boost::shared_ptr<Film> _film_b; - boost::shared_ptr<const Options> _opt; Job* _job; boost::shared_ptr<Encoder> _encoder; - std::pair<boost::shared_ptr<VideoDecoder>, boost::shared_ptr<AudioDecoder> > _da; - std::pair<boost::shared_ptr<VideoDecoder>, boost::shared_ptr<AudioDecoder> > _db; + 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/audio_decoder.cc b/src/lib/audio_decoder.cc index 70f0effd9..9d8de971c 100644 --- a/src/lib/audio_decoder.cc +++ b/src/lib/audio_decoder.cc @@ -23,7 +23,7 @@ using boost::optional; using boost::shared_ptr; -AudioDecoder::AudioDecoder (shared_ptr<Film> f, shared_ptr<const Options> o, Job* j) +AudioDecoder::AudioDecoder (shared_ptr<Film> f, shared_ptr<const DecodeOptions> o, Job* j) : Decoder (f, o, j) { diff --git a/src/lib/audio_decoder.h b/src/lib/audio_decoder.h index 1570fe3b0..013a6327f 100644 --- a/src/lib/audio_decoder.h +++ b/src/lib/audio_decoder.h @@ -34,7 +34,7 @@ class AudioDecoder : public AudioSource, public virtual Decoder { public: - AudioDecoder (boost::shared_ptr<Film>, boost::shared_ptr<const Options>, Job *); + AudioDecoder (boost::shared_ptr<Film>, boost::shared_ptr<const DecodeOptions>, Job *); virtual void set_audio_stream (boost::shared_ptr<AudioStream>); diff --git a/src/lib/check_hashes_job.cc b/src/lib/check_hashes_job.cc index 3967d0d70..50d86c523 100644 --- a/src/lib/check_hashes_job.cc +++ b/src/lib/check_hashes_job.cc @@ -34,9 +34,10 @@ using std::stringstream; using std::ifstream; using boost::shared_ptr; -CheckHashesJob::CheckHashesJob (shared_ptr<Film> f, shared_ptr<const Options> o, shared_ptr<Job> req) +CheckHashesJob::CheckHashesJob (shared_ptr<Film> f, shared_ptr<const DecodeOptions> od, shared_ptr<const EncodeOptions> oe, shared_ptr<Job> req) : Job (f, req) - , _opt (o) + , _decode_opt (od) + , _encode_opt (oe) , _bad (0) { @@ -61,7 +62,7 @@ CheckHashesJob::run () DCPFrameRate const dfr = dcp_frame_rate (_film->frames_per_second ()); for (SourceFrame i = _film->dcp_trim_start(); i < N; i += dfr.skip) { - string const j2k_file = _opt->frame_out_path (i, false); + string const j2k_file = _encode_opt->frame_out_path (i, false); string const hash_file = j2k_file + ".md5"; if (!boost::filesystem::exists (j2k_file)) { @@ -91,13 +92,13 @@ CheckHashesJob::run () shared_ptr<Job> tc; if (_film->dcp_ab()) { - tc.reset (new ABTranscodeJob (_film, _opt, shared_from_this())); + tc.reset (new ABTranscodeJob (_film, _decode_opt, _encode_opt, shared_from_this())); } else { - tc.reset (new TranscodeJob (_film, _opt, shared_from_this())); + tc.reset (new TranscodeJob (_film, _decode_opt, _encode_opt, shared_from_this())); } JobManager::instance()->add_after (shared_from_this(), tc); - JobManager::instance()->add_after (tc, shared_ptr<Job> (new CheckHashesJob (_film, _opt, tc))); + JobManager::instance()->add_after (tc, shared_ptr<Job> (new CheckHashesJob (_film, _decode_opt, _encode_opt, tc))); } set_progress (1); diff --git a/src/lib/check_hashes_job.h b/src/lib/check_hashes_job.h index e0ed6a64a..c41af9d3f 100644 --- a/src/lib/check_hashes_job.h +++ b/src/lib/check_hashes_job.h @@ -19,16 +19,25 @@ #include "job.h" +class DecodeOptions; +class EncodeOptions; + class CheckHashesJob : public Job { public: - CheckHashesJob (boost::shared_ptr<Film> f, boost::shared_ptr<const Options> o, boost::shared_ptr<Job> req); + CheckHashesJob ( + boost::shared_ptr<Film> f, + boost::shared_ptr<const DecodeOptions> od, + boost::shared_ptr<const EncodeOptions> oe, + boost::shared_ptr<Job> req + ); std::string name () const; void run (); std::string status () const; private: - boost::shared_ptr<const Options> _opt; + boost::shared_ptr<const DecodeOptions> _decode_opt; + boost::shared_ptr<const EncodeOptions> _encode_opt; int _bad; }; diff --git a/src/lib/dcp_video_frame.cc b/src/lib/dcp_video_frame.cc index c185de0f4..996aff33f 100644 --- a/src/lib/dcp_video_frame.cc +++ b/src/lib/dcp_video_frame.cc @@ -376,7 +376,7 @@ DCPVideoFrame::encode_remotely (ServerDescription const * serv) * @param frame Frame index. */ void -EncodedData::write (shared_ptr<const Options> opt, SourceFrame frame) +EncodedData::write (shared_ptr<const EncodeOptions> opt, SourceFrame frame) { string const tmp_j2k = opt->frame_out_path (frame, true); diff --git a/src/lib/dcp_video_frame.h b/src/lib/dcp_video_frame.h index 5ae53f1e8..57e7e6203 100644 --- a/src/lib/dcp_video_frame.h +++ b/src/lib/dcp_video_frame.h @@ -26,7 +26,7 @@ */ class FilmState; -class Options; +class EncodeOptions; class ServerDescription; class Scaler; class Image; @@ -50,7 +50,7 @@ public: virtual ~EncodedData () {} void send (boost::shared_ptr<Socket> socket); - void write (boost::shared_ptr<const Options>, SourceFrame); + void write (boost::shared_ptr<const EncodeOptions>, SourceFrame); /** @return data */ uint8_t* data () const { @@ -122,7 +122,6 @@ public: private: void create_openjpeg_container (); - void write_encoded (boost::shared_ptr<const Options>, uint8_t *, int); boost::shared_ptr<const Image> _input; ///< the input image boost::shared_ptr<Subtitle> _subtitle; ///< any subtitle that should be on the image diff --git a/src/lib/decoder.cc b/src/lib/decoder.cc index 2bacf58e7..507708345 100644 --- a/src/lib/decoder.cc +++ b/src/lib/decoder.cc @@ -49,10 +49,16 @@ using boost::optional; * @param o Options. * @param j Job that we are running within, or 0 */ -Decoder::Decoder (boost::shared_ptr<Film> f, boost::shared_ptr<const Options> o, Job* j) +Decoder::Decoder (boost::shared_ptr<Film> f, boost::shared_ptr<const DecodeOptions> o, Job* j) : _film (f) , _opt (o) , _job (j) { } + +bool +Decoder::seek (SourceFrame f) +{ + throw DecodeError ("decoder does not support seek"); +} diff --git a/src/lib/decoder.h b/src/lib/decoder.h index e757e5401..0d35ebb3a 100644 --- a/src/lib/decoder.h +++ b/src/lib/decoder.h @@ -35,7 +35,7 @@ #include "audio_source.h" class Job; -class Options; +class DecodeOptions; class Image; class Log; class DelayLine; @@ -54,16 +54,22 @@ class FilterGraph; class Decoder { public: - Decoder (boost::shared_ptr<Film>, boost::shared_ptr<const Options>, Job *); + Decoder (boost::shared_ptr<Film>, boost::shared_ptr<const DecodeOptions>, Job *); virtual ~Decoder () {} virtual bool pass () = 0; + /** Seek. + * @return true on error. + */ + virtual bool seek (SourceFrame); + + boost::signals2::signal<void()> OutputChanged; protected: /** our Film */ boost::shared_ptr<Film> _film; /** our options */ - boost::shared_ptr<const Options> _opt; + boost::shared_ptr<const DecodeOptions> _opt; /** associated Job, or 0 */ Job* _job; }; diff --git a/src/lib/decoder_factory.cc b/src/lib/decoder_factory.cc index b2118ef74..1d8d12cd5 100644 --- a/src/lib/decoder_factory.cc +++ b/src/lib/decoder_factory.cc @@ -26,6 +26,7 @@ #include "imagemagick_decoder.h" #include "film.h" #include "external_audio_decoder.h" +#include "decoder_factory.h" using std::string; using std::pair; @@ -33,14 +34,14 @@ using std::make_pair; using boost::shared_ptr; using boost::dynamic_pointer_cast; -pair<shared_ptr<VideoDecoder>, shared_ptr<AudioDecoder> > +Decoders decoder_factory ( - shared_ptr<Film> f, shared_ptr<const Options> o, Job* j + shared_ptr<Film> f, shared_ptr<const DecodeOptions> o, Job* j ) { if (boost::filesystem::is_directory (f->content_path()) || f->content_type() == STILL) { /* A single image file, or a directory of them */ - return make_pair ( + return Decoders ( shared_ptr<VideoDecoder> (new ImageMagickDecoder (f, o, j)), shared_ptr<AudioDecoder> () ); @@ -48,8 +49,8 @@ decoder_factory ( shared_ptr<FFmpegDecoder> fd (new FFmpegDecoder (f, o, j)); if (f->use_content_audio()) { - return make_pair (fd, fd); + return Decoders (fd, fd); } - return make_pair (fd, shared_ptr<AudioDecoder> (new ExternalAudioDecoder (f, o, j))); + return Decoders (fd, shared_ptr<AudioDecoder> (new ExternalAudioDecoder (f, o, j))); } diff --git a/src/lib/decoder_factory.h b/src/lib/decoder_factory.h index 1f3690611..47d977ce7 100644 --- a/src/lib/decoder_factory.h +++ b/src/lib/decoder_factory.h @@ -17,16 +17,33 @@ */ +#ifndef DVDOMATIC_DECODER_FACTORY_H +#define DVDOMATIC_DECODER_FACTORY_H + /** @file src/decoder_factory.h * @brief A method to create appropriate decoders for some content. */ class Film; -class Options; +class DecodeOptions; class Job; class VideoDecoder; class AudioDecoder; -extern std::pair<boost::shared_ptr<VideoDecoder>, boost::shared_ptr<AudioDecoder> > decoder_factory ( - boost::shared_ptr<Film>, boost::shared_ptr<const Options>, Job * +struct Decoders { + Decoders () {} + + Decoders (boost::shared_ptr<VideoDecoder> v, boost::shared_ptr<AudioDecoder> a) + : video (v) + , audio (a) + {} + + boost::shared_ptr<VideoDecoder> video; + boost::shared_ptr<AudioDecoder> audio; +}; + +extern Decoders decoder_factory ( + boost::shared_ptr<Film>, boost::shared_ptr<const DecodeOptions>, Job * ); + +#endif diff --git a/src/lib/encoder.cc b/src/lib/encoder.cc index 17a6726a6..1fc7d5997 100644 --- a/src/lib/encoder.cc +++ b/src/lib/encoder.cc @@ -33,7 +33,7 @@ int const Encoder::_history_size = 25; /** @param f Film that we are encoding. * @param o Options. */ -Encoder::Encoder (shared_ptr<const Film> f, shared_ptr<const Options> o) +Encoder::Encoder (shared_ptr<const Film> f, shared_ptr<const EncodeOptions> o) : _film (f) , _opt (o) , _just_skipped (false) @@ -107,13 +107,13 @@ Encoder::frame_skipped () void Encoder::process_video (shared_ptr<Image> i, boost::shared_ptr<Subtitle> s) { - if (_opt->decode_video_skip != 0 && (_video_frame % _opt->decode_video_skip) != 0) { + if (_opt->video_skip != 0 && (_video_frame % _opt->video_skip) != 0) { ++_video_frame; return; } - if (_opt->video_decode_range) { - pair<SourceFrame, SourceFrame> const r = _opt->video_decode_range.get(); + if (_opt->video_range) { + pair<SourceFrame, SourceFrame> const r = _opt->video_range.get(); if (_video_frame < r.first || _video_frame >= r.second) { ++_video_frame; return; @@ -127,12 +127,12 @@ Encoder::process_video (shared_ptr<Image> i, boost::shared_ptr<Subtitle> s) void Encoder::process_audio (shared_ptr<AudioBuffers> data) { - if (_opt->audio_decode_range) { + if (_opt->audio_range) { shared_ptr<AudioBuffers> trimmed (new AudioBuffers (*data.get ())); /* Range that we are encoding */ - pair<int64_t, int64_t> required_range = _opt->audio_decode_range.get(); + pair<int64_t, int64_t> required_range = _opt->audio_range.get(); /* Range of this block of data */ pair<int64_t, int64_t> this_range (_audio_frame, _audio_frame + trimmed->frames()); diff --git a/src/lib/encoder.h b/src/lib/encoder.h index b12bd0d48..64f113d74 100644 --- a/src/lib/encoder.h +++ b/src/lib/encoder.h @@ -35,7 +35,7 @@ extern "C" { #include "video_sink.h" #include "audio_sink.h" -class Options; +class EncodeOptions; class Image; class Subtitle; class AudioBuffers; @@ -54,7 +54,7 @@ class Film; class Encoder : public VideoSink, public AudioSink { public: - Encoder (boost::shared_ptr<const Film> f, boost::shared_ptr<const Options> o); + Encoder (boost::shared_ptr<const Film> f, boost::shared_ptr<const EncodeOptions> o); virtual ~Encoder () {} /** Called to indicate that a processing run is about to begin */ @@ -93,7 +93,7 @@ protected: /** Film that we are encoding */ boost::shared_ptr<const Film> _film; /** Options */ - boost::shared_ptr<const Options> _opt; + boost::shared_ptr<const EncodeOptions> _opt; /** Mutex for _time_history, _just_skipped and _last_frame */ mutable boost::mutex _history_mutex; diff --git a/src/lib/encoder_factory.cc b/src/lib/encoder_factory.cc index 2da021ad8..fe4d50ef3 100644 --- a/src/lib/encoder_factory.cc +++ b/src/lib/encoder_factory.cc @@ -29,7 +29,7 @@ using boost::shared_ptr; shared_ptr<Encoder> -encoder_factory (shared_ptr<const Film> f, shared_ptr<const Options> o) +encoder_factory (shared_ptr<const Film> f, shared_ptr<const EncodeOptions> o) { if (!boost::filesystem::is_directory (f->content_path()) && f->content_type() == STILL) { return shared_ptr<Encoder> (new J2KStillEncoder (f, o)); diff --git a/src/lib/encoder_factory.h b/src/lib/encoder_factory.h index 1bc4e18df..5ac5c9559 100644 --- a/src/lib/encoder_factory.h +++ b/src/lib/encoder_factory.h @@ -22,9 +22,9 @@ */ class Encoder; -class Options; +class EncodeOptions; class Job; class Log; class Film; -extern boost::shared_ptr<Encoder> encoder_factory (boost::shared_ptr<const Film>, boost::shared_ptr<const Options>); +extern boost::shared_ptr<Encoder> encoder_factory (boost::shared_ptr<const Film>, boost::shared_ptr<const EncodeOptions>); diff --git a/src/lib/examine_content_job.cc b/src/lib/examine_content_job.cc index 93333605b..70a04b825 100644 --- a/src/lib/examine_content_job.cc +++ b/src/lib/examine_content_job.cc @@ -26,7 +26,6 @@ #include "options.h" #include "decoder_factory.h" #include "decoder.h" -#include "imagemagick_encoder.h" #include "transcoder.h" #include "log.h" #include "film.h" @@ -60,13 +59,12 @@ ExamineContentJob::name () const void ExamineContentJob::run () { - float progress_remaining = 1; + descend (1); /* 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 */ @@ -74,97 +72,33 @@ ExamineContentJob::run () will be messed up. */ _film->unset_length (); + _film->set_crop (Crop ()); - shared_ptr<Options> o (new Options ("", "", "")); - o->out_size = Size (512, 512); - o->apply_crop = false; + shared_ptr<DecodeOptions> o (new DecodeOptions); o->decode_audio = false; - descend (0.5); - - pair<shared_ptr<VideoDecoder>, shared_ptr<AudioDecoder> > decoders = decoder_factory (_film, o, this); + Decoders decoders = decoder_factory (_film, o, this); set_progress_unknown (); - while (!decoders.first->pass()) { + while (!decoders.video->pass()) { /* keep going */ } - _film->set_length (decoders.first->video_frame()); + _film->set_length (decoders.video->video_frame()); _film->log()->log (String::compose ("Video length examined as %1 frames", _film->length().get())); - ascend (); - - progress_remaining -= 0.5; - } else { - /* Get a quick decoder to get the content's length from its header. - It would have been nice to just use the thumbnail transcoder's decoder, - but that's a bit fiddly, and this isn't too expensive. - */ + /* Get a quick decoder to get the content's length from its header */ - shared_ptr<Options> o (new Options ("", "", "")); - o->out_size = Size (1024, 1024); - pair<shared_ptr<VideoDecoder>, shared_ptr<AudioDecoder> > d = decoder_factory (_film, o, 0); - _film->set_length (d.first->length()); + shared_ptr<DecodeOptions> o (new DecodeOptions); + Decoders d = decoder_factory (_film, o, 0); + _film->set_length (d.video->length()); _film->log()->log (String::compose ("Video length obtained from header as %1 frames", _film->length().get())); } - /* Now make thumbnails for it */ - - descend (progress_remaining); - - try { - shared_ptr<Options> o (new Options (_film->dir ("thumbs"), ".png", "")); - o->out_size = _film->size (); - o->apply_crop = false; - o->decode_audio = false; - o->decode_video_skip = _film->length().get() / 128; - o->decode_subtitles = true; - shared_ptr<ImageMagickEncoder> e (new ImageMagickEncoder (_film, o)); - Transcoder w (_film, o, this, e); - w.go (); - - /* Now set the film's length from the transcoder's decoder, since we - went to all the trouble of going through the content. - */ - - _film->set_length (w.video_decoder()->video_frame()); - - } catch (std::exception& e) { - - ascend (); - set_progress (1); - set_error (e.what ()); - set_state (FINISHED_ERROR); - return; - - } - - string const tdir = _film->dir ("thumbs"); - vector<SourceFrame> thumbs; - - for (boost::filesystem::directory_iterator i = boost::filesystem::directory_iterator (tdir); i != boost::filesystem::directory_iterator(); ++i) { - - /* Aah, the sweet smell of progress */ -#if BOOST_FILESYSTEM_VERSION == 3 - string const l = boost::filesystem::path(*i).leaf().generic_string(); -#else - string const l = i->leaf (); -#endif - - size_t const d = l.find (".png"); - size_t const t = l.find (".tmp"); - if (d != string::npos && t == string::npos) { - thumbs.push_back (atoi (l.substr (0, d).c_str())); - } - } - - sort (thumbs.begin(), thumbs.end()); - _film->set_thumbs (thumbs); - ascend (); set_progress (1); set_state (FINISHED_OK); diff --git a/src/lib/external_audio_decoder.cc b/src/lib/external_audio_decoder.cc index 9b121235a..25c8068b6 100644 --- a/src/lib/external_audio_decoder.cc +++ b/src/lib/external_audio_decoder.cc @@ -31,7 +31,7 @@ using std::cout; using boost::shared_ptr; using boost::optional; -ExternalAudioDecoder::ExternalAudioDecoder (shared_ptr<Film> f, shared_ptr<const Options> o, Job* j) +ExternalAudioDecoder::ExternalAudioDecoder (shared_ptr<Film> f, shared_ptr<const DecodeOptions> o, Job* j) : Decoder (f, o, j) , AudioDecoder (f, o, j) { diff --git a/src/lib/external_audio_decoder.h b/src/lib/external_audio_decoder.h index 45a2a809c..2558955eb 100644 --- a/src/lib/external_audio_decoder.h +++ b/src/lib/external_audio_decoder.h @@ -44,7 +44,7 @@ private: class ExternalAudioDecoder : public AudioDecoder { public: - ExternalAudioDecoder (boost::shared_ptr<Film>, boost::shared_ptr<const Options>, Job *); + ExternalAudioDecoder (boost::shared_ptr<Film>, boost::shared_ptr<const DecodeOptions>, Job *); bool pass (); diff --git a/src/lib/ffmpeg_decoder.cc b/src/lib/ffmpeg_decoder.cc index acaf149f4..136843190 100644 --- a/src/lib/ffmpeg_decoder.cc +++ b/src/lib/ffmpeg_decoder.cc @@ -59,7 +59,7 @@ using boost::shared_ptr; using boost::optional; using boost::dynamic_pointer_cast; -FFmpegDecoder::FFmpegDecoder (shared_ptr<Film> f, shared_ptr<const Options> o, Job* j) +FFmpegDecoder::FFmpegDecoder (shared_ptr<Film> f, shared_ptr<const DecodeOptions> o, Job* j) : Decoder (f, o, j) , VideoDecoder (f, o, j) , AudioDecoder (f, o, j) @@ -77,6 +77,8 @@ FFmpegDecoder::FFmpegDecoder (shared_ptr<Film> f, shared_ptr<const Options> o, J setup_video (); setup_audio (); setup_subtitle (); + + _film_connection = f->Changed.connect (bind (&FFmpegDecoder::film_changed, this, _1)); } FFmpegDecoder::~FFmpegDecoder () @@ -101,11 +103,9 @@ FFmpegDecoder::~FFmpegDecoder () void FFmpegDecoder::setup_general () { - int r; - av_register_all (); - if ((r = avformat_open_input (&_format_context, _film->content_path().c_str(), 0, 0)) != 0) { + if (avformat_open_input (&_format_context, _film->content_path().c_str(), 0, 0) < 0) { throw OpenFileError (_film->content_path ()); } @@ -265,46 +265,10 @@ FFmpegDecoder::pass () _film->log()->log (String::compose ("Used only %1 bytes of %2 in packet", r, _packet.size)); } - /* Where we are in the output, in seconds */ - double const out_pts_seconds = video_frame() / frames_per_second(); - - /* Where we are in the source, in seconds */ - double const source_pts_seconds = av_q2d (_format_context->streams[_packet.stream_index]->time_base) - * av_frame_get_best_effort_timestamp(_frame); - - _film->log()->log ( - String::compose ("Source video frame ready; source at %1, output at %2", source_pts_seconds, out_pts_seconds), - Log::VERBOSE - ); - - if (!_first_video) { - _first_video = source_pts_seconds; - } - - /* Difference between where we are and where we should be */ - double const delta = source_pts_seconds - _first_video.get() - out_pts_seconds; - double const one_frame = 1 / frames_per_second(); - - /* Insert frames if required to get out_pts_seconds up to pts_seconds */ - if (delta > one_frame) { - int const extra = rint (delta / one_frame); - for (int i = 0; i < extra; ++i) { - repeat_last_video (); - _film->log()->log ( - String::compose ( - "Extra video frame inserted at %1s; source frame %2, source PTS %3 (at %4 fps)", - out_pts_seconds, video_frame(), source_pts_seconds, frames_per_second() - ) - ); - } - } - - if (delta > -one_frame) { - /* Process this frame */ - filter_and_emit_video (_frame); + if (_opt->video_sync) { + out_with_sync (); } else { - /* Otherwise we are omitting a frame to keep things right */ - _film->log()->log (String::compose ("Frame removed at %1s", out_pts_seconds)); + filter_and_emit_video (_frame); } } @@ -557,6 +521,8 @@ FFmpegDecoder::set_subtitle_stream (shared_ptr<SubtitleStream> s) void FFmpegDecoder::filter_and_emit_video (AVFrame* frame) { + boost::mutex::scoped_lock lm (_filter_graphs_mutex); + shared_ptr<FilterGraph> graph; list<shared_ptr<FilterGraph> >::iterator i = _filter_graphs.begin(); @@ -565,7 +531,7 @@ FFmpegDecoder::filter_and_emit_video (AVFrame* frame) } if (i == _filter_graphs.end ()) { - graph.reset (new FilterGraph (_film, this, _opt->apply_crop, Size (frame->width, frame->height), (AVPixelFormat) frame->format)); + graph.reset (new FilterGraph (_film, this, Size (frame->width, frame->height), (AVPixelFormat) frame->format)); _filter_graphs.push_back (graph); _film->log()->log (String::compose ("New graph for %1x%2, pixel format %3", frame->width, frame->height, frame->format)); } else { @@ -574,11 +540,23 @@ FFmpegDecoder::filter_and_emit_video (AVFrame* frame) list<shared_ptr<Image> > images = graph->process (frame); + SourceFrame const sf = av_q2d (_format_context->streams[_video_stream]->time_base) + * av_frame_get_best_effort_timestamp(_frame) * frames_per_second(); + for (list<shared_ptr<Image> >::iterator i = images.begin(); i != images.end(); ++i) { - emit_video (*i); + emit_video (*i, sf); } } +bool +FFmpegDecoder::seek (SourceFrame f) +{ + int64_t const t = static_cast<int64_t>(f) / (av_q2d (_format_context->streams[_video_stream]->time_base) * frames_per_second()); + int const r = av_seek_frame (_format_context, _video_stream, t, 0); + avcodec_flush_buffers (_video_codec_context); + return r < 0; +} + shared_ptr<FFmpegAudioStream> FFmpegAudioStream::create (string t, optional<int> v) { @@ -631,9 +609,73 @@ FFmpegAudioStream::to_string () const return String::compose ("ffmpeg %1 %2 %3 %4", _id, _sample_rate, _channel_layout, _name); } +void +FFmpegDecoder::out_with_sync () +{ + /* Where we are in the output, in seconds */ + double const out_pts_seconds = video_frame() / frames_per_second(); + + /* Where we are in the source, in seconds */ + double const source_pts_seconds = av_q2d (_format_context->streams[_packet.stream_index]->time_base) + * av_frame_get_best_effort_timestamp(_frame); + + _film->log()->log ( + String::compose ("Source video frame ready; source at %1, output at %2", source_pts_seconds, out_pts_seconds), + Log::VERBOSE + ); + + if (!_first_video) { + _first_video = source_pts_seconds; + } + + /* Difference between where we are and where we should be */ + double const delta = source_pts_seconds - _first_video.get() - out_pts_seconds; + double const one_frame = 1 / frames_per_second(); + + /* Insert frames if required to get out_pts_seconds up to pts_seconds */ + if (delta > one_frame) { + int const extra = rint (delta / one_frame); + for (int i = 0; i < extra; ++i) { + repeat_last_video (); + _film->log()->log ( + String::compose ( + "Extra video frame inserted at %1s; source frame %2, source PTS %3 (at %4 fps)", + out_pts_seconds, video_frame(), source_pts_seconds, frames_per_second() + ) + ); + } + } + + if (delta > -one_frame) { + /* Process this frame */ + filter_and_emit_video (_frame); + } else { + /* Otherwise we are omitting a frame to keep things right */ + _film->log()->log (String::compose ("Frame removed at %1s", out_pts_seconds)); + } +} + +void +FFmpegDecoder::film_changed (Film::Property p) +{ + switch (p) { + case Film::CROP: + { + boost::mutex::scoped_lock lm (_filter_graphs_mutex); + _filter_graphs.clear (); + } + OutputChanged (); + break; + + default: + break; + } +} + /** @return Length (in video frames) according to our content's header */ SourceFrame FFmpegDecoder::length () const { return (double(_format_context->duration) / AV_TIME_BASE) * frames_per_second(); } + diff --git a/src/lib/ffmpeg_decoder.h b/src/lib/ffmpeg_decoder.h index 1771551fc..35688003e 100644 --- a/src/lib/ffmpeg_decoder.h +++ b/src/lib/ffmpeg_decoder.h @@ -26,6 +26,7 @@ #include <stdint.h> #include <boost/shared_ptr.hpp> #include <boost/optional.hpp> +#include <boost/thread/mutex.hpp> extern "C" { #include <libavcodec/avcodec.h> #include <libpostproc/postprocess.h> @@ -34,6 +35,7 @@ extern "C" { #include "decoder.h" #include "video_decoder.h" #include "audio_decoder.h" +#include "film.h" struct AVFilterGraph; struct AVCodecContext; @@ -84,7 +86,7 @@ private: class FFmpegDecoder : public VideoDecoder, public AudioDecoder { public: - FFmpegDecoder (boost::shared_ptr<Film>, boost::shared_ptr<const Options>, Job *); + FFmpegDecoder (boost::shared_ptr<Film>, boost::shared_ptr<const DecodeOptions>, Job *); ~FFmpegDecoder (); float frames_per_second () const; @@ -98,6 +100,8 @@ public: void set_audio_stream (boost::shared_ptr<AudioStream>); void set_subtitle_stream (boost::shared_ptr<SubtitleStream>); + bool seek (SourceFrame); + private: bool pass (); @@ -105,6 +109,7 @@ private: AVSampleFormat audio_sample_format () const; int bytes_per_audio_sample () const; + void out_with_sync (); void filter_and_emit_video (AVFrame *); void setup_general (); @@ -115,6 +120,9 @@ private: void maybe_add_subtitle (); boost::shared_ptr<AudioBuffers> deinterleave_audio (uint8_t* data, int size); + void film_changed (Film::Property); + boost::signals2::scoped_connection _film_connection; + std::string stream_name (AVStream* s) const; AVFormatContext* _format_context; @@ -135,4 +143,5 @@ private: boost::optional<double> _first_audio; std::list<boost::shared_ptr<FilterGraph> > _filter_graphs; + boost::mutex _filter_graphs_mutex; }; diff --git a/src/lib/film.cc b/src/lib/film.cc index e2a4cbeda..4cfe7de0a 100644 --- a/src/lib/film.cc +++ b/src/lib/film.cc @@ -31,7 +31,6 @@ #include <boost/date_time.hpp> #include "film.h" #include "format.h" -#include "imagemagick_encoder.h" #include "job.h" #include "filter.h" #include "transcoder.h" @@ -171,7 +170,6 @@ Film::Film (Film const & o) , _studio (o._studio) , _facility (o._facility) , _package_type (o._package_type) - , _thumbs (o._thumbs) , _size (o._size) , _length (o._length) , _content_digest (o._content_digest) @@ -262,35 +260,37 @@ Film::make_dcp (bool transcode) throw MissingSettingError ("name"); } - shared_ptr<Options> o (new Options (j2k_dir(), ".j2c", dir ("wavs"))); - o->out_size = format()->dcp_size (); - o->padding = format()->dcp_padding (shared_from_this ()); - o->ratio = format()->ratio_as_float (shared_from_this ()); + shared_ptr<EncodeOptions> oe (new EncodeOptions (j2k_dir(), ".j2c", dir ("wavs"))); + oe->out_size = format()->dcp_size (); + oe->padding = format()->dcp_padding (shared_from_this ()); if (dcp_length ()) { - o->video_decode_range = make_pair (dcp_trim_start(), dcp_trim_start() + dcp_length().get()); + oe->video_range = make_pair (dcp_trim_start(), dcp_trim_start() + dcp_length().get()); if (audio_stream()) { - o->audio_decode_range = make_pair ( - video_frames_to_audio_frames (o->video_decode_range.get().first, audio_stream()->sample_rate(), frames_per_second()), - video_frames_to_audio_frames (o->video_decode_range.get().second, audio_stream()->sample_rate(), frames_per_second()) + oe->audio_range = make_pair ( + video_frames_to_audio_frames (oe->video_range.get().first, audio_stream()->sample_rate(), frames_per_second()), + video_frames_to_audio_frames (oe->video_range.get().second, audio_stream()->sample_rate(), frames_per_second()) ); } } - o->decode_subtitles = with_subtitles (); - o->decode_video_skip = dcp_frame_rate (frames_per_second()).skip; + + oe->video_skip = dcp_frame_rate (frames_per_second()).skip; + + shared_ptr<DecodeOptions> od (new DecodeOptions); + od->decode_subtitles = with_subtitles (); shared_ptr<Job> r; if (transcode) { if (dcp_ab()) { - r = JobManager::instance()->add (shared_ptr<Job> (new ABTranscodeJob (shared_from_this(), o, shared_ptr<Job> ()))); + r = JobManager::instance()->add (shared_ptr<Job> (new ABTranscodeJob (shared_from_this(), od, oe, shared_ptr<Job> ()))); } else { - r = JobManager::instance()->add (shared_ptr<Job> (new TranscodeJob (shared_from_this(), o, shared_ptr<Job> ()))); + r = JobManager::instance()->add (shared_ptr<Job> (new TranscodeJob (shared_from_this(), od, oe, shared_ptr<Job> ()))); } } - r = JobManager::instance()->add (shared_ptr<Job> (new CheckHashesJob (shared_from_this(), o, r))); - JobManager::instance()->add (shared_ptr<Job> (new MakeDCPJob (shared_from_this(), o, r))); + r = JobManager::instance()->add (shared_ptr<Job> (new CheckHashesJob (shared_from_this(), od, oe, r))); + JobManager::instance()->add (shared_ptr<Job> (new MakeDCPJob (shared_from_this(), oe, r))); } /** Start a job to examine our content file */ @@ -301,12 +301,6 @@ Film::examine_content () return; } - set_thumbs (vector<SourceFrame> ()); - boost::filesystem::remove_all (dir ("thumbs")); - - /* This call will recreate the directory */ - dir ("thumbs"); - _examine_content_job.reset (new ExamineContentJob (shared_from_this(), shared_ptr<Job> ())); _examine_content_job->Finished.connect (bind (&Film::examine_content_finished, this)); JobManager::instance()->add (_examine_content_job); @@ -357,35 +351,6 @@ Film::encoded_frames () const return N; } -/** Return the filename of a subtitle image if one exists for a given thumb index. - * @param Thumbnail index. - * @return Position of the image within the source frame, and the image filename, if one exists. - * Otherwise the filename will be empty. - */ -pair<Position, string> -Film::thumb_subtitle (int n) const -{ - string sub_file = thumb_base(n) + ".sub"; - if (!boost::filesystem::exists (sub_file)) { - return pair<Position, string> (); - } - - pair<Position, string> sub; - - ifstream f (sub_file.c_str ()); - multimap<string, string> kv = read_key_value (f); - for (map<string, string>::const_iterator i = kv.begin(); i != kv.end(); ++i) { - if (i->first == "x") { - sub.first.x = lexical_cast<int> (i->second); - } else if (i->first == "y") { - sub.first.y = lexical_cast<int> (i->second); - sub.second = String::compose ("%1.sub.png", thumb_base(n)); - } - } - - return sub; -} - /** Write state to our `metadata' file */ void Film::write_metadata () const @@ -448,12 +413,6 @@ Film::write_metadata () const f << "facility " << _facility << "\n"; f << "package_type " << _package_type << "\n"; - /* Cached stuff; this is information about our content; we could - look it up each time, but that's slow. - */ - for (vector<SourceFrame>::const_iterator i = _thumbs.begin(); i != _thumbs.end(); ++i) { - f << "thumb " << *i << "\n"; - } f << "width " << _size.width << "\n"; f << "height " << _size.height << "\n"; f << "length " << _length.get_value_or(0) << "\n"; @@ -481,7 +440,6 @@ Film::read_metadata () boost::mutex::scoped_lock lm (_state_mutex); _external_audio.clear (); - _thumbs.clear (); _content_audio_streams.clear (); _subtitle_streams.clear (); @@ -585,13 +543,7 @@ Film::read_metadata () } /* Cached stuff */ - if (k == "thumb") { - int const n = atoi (v.c_str ()); - /* Only add it to the list if it still exists */ - if (boost::filesystem::exists (thumb_file_for_frame (n))) { - _thumbs.push_back (n); - } - } else if (k == "width") { + if (k == "width") { _size.width = atoi (v.c_str ()); } else if (k == "height") { _size.height = atoi (v.c_str ()); @@ -635,61 +587,6 @@ Film::read_metadata () _dirty = false; } -/** @param n A thumb index. - * @return The path to the thumb's image file. - */ -string -Film::thumb_file (int n) const -{ - return thumb_file_for_frame (thumb_frame (n)); -} - -/** @param n A frame index within the Film's source. - * @return The path to the thumb's image file for this frame; - * we assume that it exists. - */ -string -Film::thumb_file_for_frame (SourceFrame n) const -{ - return thumb_base_for_frame(n) + ".png"; -} - -/** @param n Thumb index. - * Must not be called with the _state_mutex locked. - */ -string -Film::thumb_base (int n) const -{ - return thumb_base_for_frame (thumb_frame (n)); -} - -string -Film::thumb_base_for_frame (SourceFrame n) const -{ - stringstream s; - s.width (8); - s << setfill('0') << n; - - boost::filesystem::path p; - p /= dir ("thumbs"); - p /= s.str (); - - return p.string (); -} - -/** @param n A thumb index. - * @return The frame within the Film's source that it is for. - * - * Must not be called with the _state_mutex locked. - */ -SourceFrame -Film::thumb_frame (int n) const -{ - boost::mutex::scoped_lock lm (_state_mutex); - assert (n < int (_thumbs.size ())); - return _thumbs[n]; -} - Size Film::cropped_size (Size s) const { @@ -954,23 +851,21 @@ Film::set_content (string c) */ try { - shared_ptr<Options> o (new Options ("", "", "")); - o->out_size = Size (1024, 1024); - - pair<shared_ptr<VideoDecoder>, shared_ptr<AudioDecoder> > d = decoder_factory (shared_from_this(), o, 0); + shared_ptr<DecodeOptions> o (new DecodeOptions); + Decoders d = decoder_factory (shared_from_this(), o, 0); - set_size (d.first->native_size ()); - set_frames_per_second (d.first->frames_per_second ()); - set_subtitle_streams (d.first->subtitle_streams ()); - set_content_audio_streams (d.second->audio_streams ()); + set_size (d.video->native_size ()); + set_frames_per_second (d.video->frames_per_second ()); + set_subtitle_streams (d.video->subtitle_streams ()); + set_content_audio_streams (d.audio->audio_streams ()); /* Start off with the first audio and subtitle streams */ - if (!d.second->audio_streams().empty()) { - set_content_audio_stream (d.second->audio_streams().front()); + if (!d.audio->audio_streams().empty()) { + set_content_audio_stream (d.audio->audio_streams().front()); } - if (!d.first->subtitle_streams().empty()) { - set_subtitle_stream (d.first->subtitle_streams().front()); + if (!d.video->subtitle_streams().empty()) { + set_subtitle_stream (d.video->subtitle_streams().front()); } { @@ -1003,7 +898,7 @@ Film::set_trust_content_header (bool t) signal_changed (TRUST_CONTENT_HEADER); - if (!_trust_content_header) && !content().empty()) { + if (!_trust_content_header && !content().empty()) { /* We just said that we don't trust the content's header */ examine_content (); } @@ -1164,8 +1059,7 @@ Film::set_external_audio (vector<string> a) _external_audio = a; } - shared_ptr<Options> o (new Options ("", "", "")); - o->decode_audio = true; + shared_ptr<DecodeOptions> o (new DecodeOptions); shared_ptr<ExternalAudioDecoder> decoder (new ExternalAudioDecoder (shared_from_this(), o, 0)); if (decoder->audio_stream()) { _external_audio_stream = decoder->audio_stream (); @@ -1326,16 +1220,6 @@ Film::set_package_type (string p) } void -Film::set_thumbs (vector<SourceFrame> t) -{ - { - boost::mutex::scoped_lock lm (_state_mutex); - _thumbs = t; - } - signal_changed (THUMBS); -} - -void Film::set_size (Size s) { { diff --git a/src/lib/film.h b/src/lib/film.h index 8cd55a227..642f2d7da 100644 --- a/src/lib/film.h +++ b/src/lib/film.h @@ -61,7 +61,6 @@ public: std::string j2k_dir () const; std::vector<std::string> audio_files () const; - std::pair<Position, std::string> thumb_subtitle (int) const; void examine_content (); void send_dcp_to_tms (); @@ -83,10 +82,6 @@ public: std::string content_path () const; ContentType content_type () const; - std::string thumb_file (int) const; - std::string thumb_base (int) const; - SourceFrame thumb_frame (int) const; - int target_audio_sample_rate () const; void write_metadata () const; @@ -130,7 +125,6 @@ public: SUBTITLE_OFFSET, SUBTITLE_SCALE, DCI_METADATA, - THUMBS, SIZE, LENGTH, CONTENT_AUDIO_STREAMS, @@ -291,11 +285,6 @@ public: return _package_type; } - std::vector<SourceFrame> thumbs () const { - boost::mutex::scoped_lock lm (_state_mutex); - return _thumbs; - } - Size size () const { boost::mutex::scoped_lock lm (_state_mutex); return _size; @@ -365,7 +354,6 @@ public: void set_studio (std::string); void set_facility (std::string); void set_package_type (std::string); - void set_thumbs (std::vector<SourceFrame>); void set_size (Size); void set_length (SourceFrame); void unset_length (); @@ -391,8 +379,6 @@ private: /** The date that we should use in a DCI name */ boost::gregorian::date _dci_date; - std::string thumb_file_for_frame (SourceFrame) const; - std::string thumb_base_for_frame (SourceFrame) const; void signal_changed (Property); void examine_content_finished (); @@ -466,8 +452,6 @@ private: /* Data which are cached to speed things up */ - /** Vector of frame indices for each of our `thumbnails' */ - std::vector<SourceFrame> _thumbs; /** Size, in pixels, of the source (ignoring cropping) */ Size _size; /** Actual length of the source (in video frames) from examining it */ diff --git a/src/lib/filter_graph.cc b/src/lib/filter_graph.cc index 7320070fe..17107a05b 100644 --- a/src/lib/filter_graph.cc +++ b/src/lib/filter_graph.cc @@ -49,11 +49,10 @@ using boost::shared_ptr; /** Construct a FilterGraph for the settings in a film. * @param film Film. * @param decoder Decoder that we are using. - * @param crop true to apply crop, otherwise false. * @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, bool crop, Size s, AVPixelFormat p) +FilterGraph::FilterGraph (shared_ptr<Film> film, FFmpegDecoder* decoder, Size s, AVPixelFormat p) : _buffer_src_context (0) , _buffer_sink_context (0) , _size (s) @@ -64,11 +63,7 @@ FilterGraph::FilterGraph (shared_ptr<Film> film, FFmpegDecoder* decoder, bool cr filters += ","; } - if (crop) { - filters += crop_string (Position (film->crop().left, film->crop().top), film->cropped_size (decoder->native_size())); - } else { - filters += crop_string (Position (0, 0), decoder->native_size()); - } + filters += crop_string (Position (film->crop().left, film->crop().top), film->cropped_size (decoder->native_size())); avfilter_register_all (); diff --git a/src/lib/filter_graph.h b/src/lib/filter_graph.h index 3842e9f7d..a4b9ef75f 100644 --- a/src/lib/filter_graph.h +++ b/src/lib/filter_graph.h @@ -36,7 +36,7 @@ class FFmpegDecoder; class FilterGraph { public: - FilterGraph (boost::shared_ptr<Film> film, FFmpegDecoder* decoder, bool crop, Size s, AVPixelFormat p); + FilterGraph (boost::shared_ptr<Film> film, FFmpegDecoder* decoder, Size s, AVPixelFormat p); bool can_process (Size s, AVPixelFormat p) const; std::list<boost::shared_ptr<Image> > process (AVFrame const * frame); diff --git a/src/lib/image.cc b/src/lib/image.cc index 72828ed46..748e9ae4b 100644 --- a/src/lib/image.cc +++ b/src/lib/image.cc @@ -125,7 +125,7 @@ Image::scale_and_convert_to_rgb (Size out_size, int padding, Scaler const * scal Size content_size = out_size; content_size.width -= (padding * 2); - shared_ptr<Image> rgb (new AlignedImage (PIX_FMT_RGB24, content_size)); + shared_ptr<Image> rgb (new CompactImage (PIX_FMT_RGB24, content_size)); struct SwsContext* scale_context = sws_getContext ( size().width, size().height, pixel_format(), diff --git a/src/lib/imagemagick_decoder.cc b/src/lib/imagemagick_decoder.cc index d68c1648f..9d11e043f 100644 --- a/src/lib/imagemagick_decoder.cc +++ b/src/lib/imagemagick_decoder.cc @@ -29,7 +29,7 @@ using std::cout; using boost::shared_ptr; ImageMagickDecoder::ImageMagickDecoder ( - boost::shared_ptr<Film> f, boost::shared_ptr<const Options> o, Job* j) + boost::shared_ptr<Film> f, boost::shared_ptr<const DecodeOptions> o, Job* j) : Decoder (f, o, j) , VideoDecoder (f, o, j) { @@ -92,7 +92,7 @@ ImageMagickDecoder::pass () delete magick_image; - emit_video (image); + emit_video (image, 0); ++_iter; return false; diff --git a/src/lib/imagemagick_decoder.h b/src/lib/imagemagick_decoder.h index de49c1b56..cfcf4b4f6 100644 --- a/src/lib/imagemagick_decoder.h +++ b/src/lib/imagemagick_decoder.h @@ -26,7 +26,7 @@ namespace Magick { class ImageMagickDecoder : public VideoDecoder { public: - ImageMagickDecoder (boost::shared_ptr<Film>, boost::shared_ptr<const Options>, Job *); + ImageMagickDecoder (boost::shared_ptr<Film>, boost::shared_ptr<const DecodeOptions>, Job *); float frames_per_second () const { /* We don't know */ diff --git a/src/lib/imagemagick_encoder.cc b/src/lib/imagemagick_encoder.cc deleted file mode 100644 index 480dec8bc..000000000 --- a/src/lib/imagemagick_encoder.cc +++ /dev/null @@ -1,91 +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/imagemagick_encoder.cc - * @brief An encoder that writes image files using ImageMagick (and does nothing with audio). - */ - -#include <stdexcept> -#include <vector> -#include <sstream> -#include <iomanip> -#include <iostream> -#include <fstream> -#include <boost/filesystem.hpp> -#include <Magick++/Image.h> -#include "imagemagick_encoder.h" -#include "film.h" -#include "options.h" -#include "exceptions.h" -#include "image.h" -#include "subtitle.h" - -using std::string; -using std::ofstream; -using boost::shared_ptr; - -/** @param f Film that we are encoding. - * @param o Options. - */ -ImageMagickEncoder::ImageMagickEncoder (shared_ptr<const Film> f, shared_ptr<const Options> o) - : Encoder (f, o) -{ - -} - -void -ImageMagickEncoder::do_process_video (shared_ptr<Image> image, shared_ptr<Subtitle> sub) -{ - shared_ptr<Image> scaled = image->scale_and_convert_to_rgb (_opt->out_size, _opt->padding, _film->scaler()); - shared_ptr<Image> compact (new CompactImage (scaled)); - - string tmp_file = _opt->frame_out_path (_video_frame, true); - Magick::Image thumb (compact->size().width, compact->size().height, "RGB", MagickCore::CharPixel, compact->data()[0]); - thumb.magick ("PNG"); - thumb.write (tmp_file); - boost::filesystem::rename (tmp_file, _opt->frame_out_path (_video_frame, false)); - - if (sub) { - float const x_scale = float (_opt->out_size.width) / _film->size().width; - float const y_scale = float (_opt->out_size.height) / _film->size().height; - - string tmp_metadata_file = _opt->frame_out_path (_video_frame, false, ".sub"); - ofstream metadata (tmp_metadata_file.c_str ()); - - Size new_size = sub->image()->size (); - new_size.width *= x_scale; - new_size.height *= y_scale; - shared_ptr<Image> scaled = sub->image()->scale (new_size, _film->scaler()); - shared_ptr<Image> compact (new CompactImage (scaled)); - - string tmp_sub_file = _opt->frame_out_path (_video_frame, true, ".sub.png"); - Magick::Image sub_thumb (compact->size().width, compact->size().height, "RGBA", MagickCore::CharPixel, compact->data()[0]); - sub_thumb.magick ("PNG"); - sub_thumb.write (tmp_sub_file); - boost::filesystem::rename (tmp_sub_file, _opt->frame_out_path (_video_frame, false, ".sub.png")); - - metadata << "x " << sub->position().x << "\n" - << "y " << sub->position().y << "\n"; - - metadata.close (); - boost::filesystem::rename (tmp_metadata_file, _opt->frame_out_path (_video_frame, false, ".sub")); - } - - frame_done (); -} diff --git a/src/lib/imagemagick_encoder.h b/src/lib/imagemagick_encoder.h deleted file mode 100644 index dfc741cb2..000000000 --- a/src/lib/imagemagick_encoder.h +++ /dev/null @@ -1,42 +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/imagemagick_encoder.h - * @brief An encoder that writes image files using ImageMagick (and does nothing with audio). - */ - -#include <string> -#include <sndfile.h> -#include "encoder.h" - -class FilmState; -class Log; - -/** @class ImageMagickEncoder - * @brief An encoder that writes image files using ImageMagick files (and does nothing with audio). - */ -class ImageMagickEncoder : public Encoder -{ -public: - ImageMagickEncoder (boost::shared_ptr<const Film> f, boost::shared_ptr<const Options> o); - -private: - void do_process_video (boost::shared_ptr<Image>, boost::shared_ptr<Subtitle>); - void do_process_audio (boost::shared_ptr<AudioBuffers>) {} -}; diff --git a/src/lib/j2k_still_encoder.cc b/src/lib/j2k_still_encoder.cc index dd6ef49b2..68088377b 100644 --- a/src/lib/j2k_still_encoder.cc +++ b/src/lib/j2k_still_encoder.cc @@ -42,7 +42,7 @@ using std::string; using std::pair; using boost::shared_ptr; -J2KStillEncoder::J2KStillEncoder (shared_ptr<const Film> f, shared_ptr<const Options> o) +J2KStillEncoder::J2KStillEncoder (shared_ptr<const Film> f, shared_ptr<const EncodeOptions> o) : Encoder (f, o) { diff --git a/src/lib/j2k_still_encoder.h b/src/lib/j2k_still_encoder.h index 4ffe876af..6069637d0 100644 --- a/src/lib/j2k_still_encoder.h +++ b/src/lib/j2k_still_encoder.h @@ -27,6 +27,7 @@ class Image; class Log; +class EncodeOptions; /** @class J2KStillEncoder * @brief An encoder which writes repeated JPEG2000 files from a single decoded input. @@ -34,7 +35,7 @@ class Log; class J2KStillEncoder : public Encoder { public: - J2KStillEncoder (boost::shared_ptr<const Film>, boost::shared_ptr<const Options>); + J2KStillEncoder (boost::shared_ptr<const Film>, boost::shared_ptr<const EncodeOptions>); private: void do_process_video (boost::shared_ptr<Image>, boost::shared_ptr<Subtitle>); diff --git a/src/lib/j2k_wav_encoder.cc b/src/lib/j2k_wav_encoder.cc index 134d74623..e76591552 100644 --- a/src/lib/j2k_wav_encoder.cc +++ b/src/lib/j2k_wav_encoder.cc @@ -51,7 +51,7 @@ using boost::shared_ptr; using boost::thread; using boost::lexical_cast; -J2KWAVEncoder::J2KWAVEncoder (shared_ptr<const Film> f, shared_ptr<const Options> o) +J2KWAVEncoder::J2KWAVEncoder (shared_ptr<const Film> f, shared_ptr<const EncodeOptions> o) : Encoder (f, o) #ifdef HAVE_SWRESAMPLE , _swr_context (0) diff --git a/src/lib/j2k_wav_encoder.h b/src/lib/j2k_wav_encoder.h index f3340ba72..064f4221e 100644 --- a/src/lib/j2k_wav_encoder.h +++ b/src/lib/j2k_wav_encoder.h @@ -47,7 +47,7 @@ class AudioBuffers; class J2KWAVEncoder : public Encoder { public: - J2KWAVEncoder (boost::shared_ptr<const Film>, boost::shared_ptr<const Options>); + J2KWAVEncoder (boost::shared_ptr<const Film>, boost::shared_ptr<const EncodeOptions>); ~J2KWAVEncoder (); void process_begin (); diff --git a/src/lib/job.h b/src/lib/job.h index 41cefb9be..f32cfa811 100644 --- a/src/lib/job.h +++ b/src/lib/job.h @@ -30,7 +30,6 @@ #include <boost/signals2.hpp> class Film; -class Options; /** @class Job * @brief A parent class to represent long-running tasks which are run in their own thread. diff --git a/src/lib/make_dcp_job.cc b/src/lib/make_dcp_job.cc index 65cd272e7..4605d1724 100644 --- a/src/lib/make_dcp_job.cc +++ b/src/lib/make_dcp_job.cc @@ -42,7 +42,7 @@ using boost::shared_ptr; /** @param f Film we are making the DCP for. * @param o Options. */ -MakeDCPJob::MakeDCPJob (shared_ptr<Film> f, shared_ptr<const Options> o, shared_ptr<Job> req) +MakeDCPJob::MakeDCPJob (shared_ptr<Film> f, shared_ptr<const EncodeOptions> o, shared_ptr<Job> req) : Job (f, req) , _opt (o) { diff --git a/src/lib/make_dcp_job.h b/src/lib/make_dcp_job.h index 442bb55f5..1aa906b0a 100644 --- a/src/lib/make_dcp_job.h +++ b/src/lib/make_dcp_job.h @@ -23,13 +23,15 @@ #include "job.h" +class EncodeOptions; + /** @class MakeDCPJob * @brief A job to create DCPs */ class MakeDCPJob : public Job { public: - MakeDCPJob (boost::shared_ptr<Film>, boost::shared_ptr<const Options>, boost::shared_ptr<Job> req); + MakeDCPJob (boost::shared_ptr<Film>, boost::shared_ptr<const EncodeOptions>, boost::shared_ptr<Job> req); std::string name () const; void run (); @@ -39,6 +41,6 @@ private: std::string j2c_path (int) const; std::string wav_path (libdcp::Channel) const; - boost::shared_ptr<const Options> _opt; + boost::shared_ptr<const EncodeOptions> _opt; }; diff --git a/src/lib/options.h b/src/lib/options.h index 29b3b71cd..4457969f3 100644 --- a/src/lib/options.h +++ b/src/lib/options.h @@ -27,22 +27,19 @@ #include <boost/optional.hpp> #include "util.h" -/** @class Options - * @brief Options for a transcoding operation. +/** @class EncodeOptions + * @brief EncodeOptions for an encoding operation. * * These are settings which may be different, in different circumstances, for - * the same film; ie they are options for a particular transcode operation. + * the same film; ie they are options for a particular operation. */ -class Options +class EncodeOptions { public: - Options (std::string f, std::string e, std::string m) + EncodeOptions (std::string f, std::string e, std::string m) : padding (0) - , apply_crop (true) - , decode_video_skip (0) - , decode_audio (true) - , decode_subtitles (false) + , video_skip (0) , _frame_out_path (f) , _frame_out_extension (e) , _multichannel_audio_out_path (m) @@ -94,21 +91,17 @@ public: } Size out_size; ///< size of output images - float ratio; ///< ratio of the wanted output image (not considering padding) int padding; ///< number of pixels of padding (in terms of the output size) each side of the image - bool apply_crop; ///< true to apply cropping /** Range of video frames to decode */ - boost::optional<std::pair<SourceFrame, SourceFrame> > video_decode_range; + boost::optional<std::pair<SourceFrame, SourceFrame> > video_range; /** Range of audio frames to decode */ - boost::optional<std::pair<int64_t, int64_t> > audio_decode_range; + boost::optional<std::pair<int64_t, int64_t> > audio_range; /** Skip frames such that we don't decode any frame where (index % decode_video_skip) != 0; e.g. * 1 for every frame, 2 for every other frame, etc. */ - SourceFrame decode_video_skip; - bool decode_audio; ///< true to decode audio, otherwise false - bool decode_subtitles; + SourceFrame video_skip; private: /** Path of the directory to write video frames to */ @@ -118,3 +111,18 @@ private: /** Path of the directory to write audio files to */ std::string _multichannel_audio_out_path; }; + + +class DecodeOptions +{ +public: + DecodeOptions () + : decode_audio (true) + , decode_subtitles (false) + , video_sync (true) + {} + + bool decode_audio; + bool decode_subtitles; + bool video_sync; +}; diff --git a/src/lib/transcode_job.cc b/src/lib/transcode_job.cc index 081e04252..54619c39f 100644 --- a/src/lib/transcode_job.cc +++ b/src/lib/transcode_job.cc @@ -41,9 +41,10 @@ using boost::shared_ptr; * @param o Options. * @param req Job that must be completed before this job is run. */ -TranscodeJob::TranscodeJob (shared_ptr<Film> f, shared_ptr<const Options> o, shared_ptr<Job> req) +TranscodeJob::TranscodeJob (shared_ptr<Film> f, shared_ptr<const DecodeOptions> od, shared_ptr<const EncodeOptions> oe, shared_ptr<Job> req) : Job (f, req) - , _opt (o) + , _decode_opt (od) + , _encode_opt (oe) { } @@ -62,8 +63,8 @@ TranscodeJob::run () _film->log()->log ("Transcode job starting"); _film->log()->log (String::compose ("Audio delay is %1ms", _film->audio_delay())); - _encoder = encoder_factory (_film, _opt); - Transcoder w (_film, _opt, this, _encoder); + _encoder = encoder_factory (_film, _encode_opt); + Transcoder w (_film, _decode_opt, this, _encoder); w.go (); set_progress (1); set_state (FINISHED_OK); diff --git a/src/lib/transcode_job.h b/src/lib/transcode_job.h index 1decea070..97f655e15 100644 --- a/src/lib/transcode_job.h +++ b/src/lib/transcode_job.h @@ -25,6 +25,8 @@ #include "job.h" class Encoder; +class DecodeOptions; +class EncodeOptions; /** @class TranscodeJob * @brief A job which transcodes from one format to another. @@ -32,7 +34,7 @@ class Encoder; class TranscodeJob : public Job { public: - TranscodeJob (boost::shared_ptr<Film> f, boost::shared_ptr<const Options> o, boost::shared_ptr<Job> req); + TranscodeJob (boost::shared_ptr<Film> f, boost::shared_ptr<const DecodeOptions> od, boost::shared_ptr<const EncodeOptions> oe, boost::shared_ptr<Job> req); std::string name () const; void run (); @@ -42,6 +44,7 @@ protected: int remaining_time () const; private: - boost::shared_ptr<const Options> _opt; + boost::shared_ptr<const DecodeOptions> _decode_opt; + boost::shared_ptr<const EncodeOptions> _encode_opt; boost::shared_ptr<Encoder> _encoder; }; diff --git a/src/lib/transcoder.cc b/src/lib/transcoder.cc index a7e79b05f..87a1fb3f2 100644 --- a/src/lib/transcoder.cc +++ b/src/lib/transcoder.cc @@ -44,11 +44,11 @@ using boost::dynamic_pointer_cast; /** Construct a transcoder using a Decoder that we create and a supplied Encoder. * @param f Film that we are transcoding. - * @param o Options. + * @param o Decode options. * @param j Job that we are running under, or 0. * @param e Encoder to use. */ -Transcoder::Transcoder (shared_ptr<Film> f, shared_ptr<const Options> o, Job* j, shared_ptr<Encoder> e) +Transcoder::Transcoder (shared_ptr<Film> f, shared_ptr<const DecodeOptions> o, Job* j, shared_ptr<Encoder> e) : _job (j) , _encoder (e) , _decoders (decoder_factory (f, o, j)) @@ -63,20 +63,20 @@ Transcoder::Transcoder (shared_ptr<Film> f, shared_ptr<const Options> o, Job* j, } /* Set up the decoder to use the film's set streams */ - _decoders.first->set_subtitle_stream (f->subtitle_stream ()); - if (_decoders.second) { - _decoders.second->set_audio_stream (f->audio_stream ()); + _decoders.video->set_subtitle_stream (f->subtitle_stream ()); + if (_decoders.audio) { + _decoders.audio->set_audio_stream (f->audio_stream ()); } if (_matcher) { - _decoders.first->connect_video (_matcher); + _decoders.video->connect_video (_matcher); _matcher->connect_video (_encoder); } else { - _decoders.first->connect_video (_encoder); + _decoders.video->connect_video (_encoder); } - if (_matcher && _delay_line && _decoders.second) { - _decoders.second->connect_audio (_delay_line); + if (_matcher && _delay_line && _decoders.audio) { + _decoders.audio->connect_audio (_delay_line); _delay_line->connect_audio (_matcher); _matcher->connect_audio (_gain); _gain->connect_audio (_encoder); @@ -95,12 +95,12 @@ Transcoder::go () while (1) { if (!done[0]) { - done[0] = _decoders.first->pass (); - _decoders.first->set_progress (); + done[0] = _decoders.video->pass (); + _decoders.video->set_progress (); } - if (!done[1] && _decoders.second && dynamic_pointer_cast<Decoder> (_decoders.second) != dynamic_pointer_cast<Decoder> (_decoders.first)) { - done[1] = _decoders.second->pass (); + 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; } diff --git a/src/lib/transcoder.h b/src/lib/transcoder.h index 4a9667b3c..b50113742 100644 --- a/src/lib/transcoder.h +++ b/src/lib/transcoder.h @@ -24,6 +24,8 @@ * as a parameter to the constructor. */ +#include "decoder_factory.h" + class Film; class Job; class Encoder; @@ -34,7 +36,8 @@ class Gain; class VideoDecoder; class AudioDecoder; class DelayLine; -class Options; +class EncodeOptions; +class DecodeOptions; /** @class Transcoder * @brief A class which takes a FilmState and some Options, then uses those to transcode a Film. @@ -45,12 +48,17 @@ class Options; class Transcoder { public: - Transcoder (boost::shared_ptr<Film> f, boost::shared_ptr<const Options> o, Job* j, boost::shared_ptr<Encoder> e); + Transcoder ( + boost::shared_ptr<Film> f, + boost::shared_ptr<const DecodeOptions> o, + Job* j, + boost::shared_ptr<Encoder> e + ); void go (); boost::shared_ptr<VideoDecoder> video_decoder () const { - return _decoders.first; + return _decoders.video; } protected: @@ -59,7 +67,7 @@ protected: /** The encoder that we will use */ boost::shared_ptr<Encoder> _encoder; /** The decoders that we will use */ - std::pair<boost::shared_ptr<VideoDecoder>, boost::shared_ptr<AudioDecoder> > _decoders; + Decoders _decoders; boost::shared_ptr<Matcher> _matcher; boost::shared_ptr<DelayLine> _delay_line; boost::shared_ptr<Gain> _gain; diff --git a/src/lib/video_decoder.cc b/src/lib/video_decoder.cc index 23a69f958..d3b441fbf 100644 --- a/src/lib/video_decoder.cc +++ b/src/lib/video_decoder.cc @@ -28,19 +28,20 @@ using boost::shared_ptr; using boost::optional; -VideoDecoder::VideoDecoder (shared_ptr<Film> f, shared_ptr<const Options> o, Job* j) +VideoDecoder::VideoDecoder (shared_ptr<Film> f, shared_ptr<const DecodeOptions> o, Job* j) : Decoder (f, o, j) , _video_frame (0) + , _last_source_frame (0) { } /** Called by subclasses to tell the world that some video data is ready. * We find a subtitle then emit it for listeners. - * @param frame to decode; caller manages memory. + * @param frame to emit. */ void -VideoDecoder::emit_video (shared_ptr<Image> image) +VideoDecoder::emit_video (shared_ptr<Image> image, SourceFrame f) { shared_ptr<Subtitle> sub; if (_timed_subtitle && _timed_subtitle->displayed_at (double (video_frame()) / _film->frames_per_second())) { @@ -48,6 +49,7 @@ VideoDecoder::emit_video (shared_ptr<Image> image) } signal_video (image, sub); + _last_source_frame = f; } void @@ -77,7 +79,7 @@ VideoDecoder::emit_subtitle (shared_ptr<TimedSubtitle> s) { _timed_subtitle = s; - if (_timed_subtitle && _opt->apply_crop) { + if (_timed_subtitle) { Position const p = _timed_subtitle->subtitle()->position (); _timed_subtitle->subtitle()->set_position (Position (p.x - _film->crop().left, p.y - _film->crop().top)); } diff --git a/src/lib/video_decoder.h b/src/lib/video_decoder.h index 685138a58..f682941d1 100644 --- a/src/lib/video_decoder.h +++ b/src/lib/video_decoder.h @@ -27,7 +27,7 @@ class VideoDecoder : public VideoSource, public virtual Decoder { public: - VideoDecoder (boost::shared_ptr<Film>, boost::shared_ptr<const Options>, Job *); + VideoDecoder (boost::shared_ptr<Film>, boost::shared_ptr<const DecodeOptions>, Job *); /** @return video frames per second, or 0 if unknown */ virtual float frames_per_second () const = 0; @@ -57,11 +57,15 @@ public: return _subtitle_streams; } + SourceFrame last_source_frame () const { + return _last_source_frame; + } + protected: virtual PixelFormat pixel_format () const = 0; - void emit_video (boost::shared_ptr<Image>); + void emit_video (boost::shared_ptr<Image>, SourceFrame); void emit_subtitle (boost::shared_ptr<TimedSubtitle>); void repeat_last_video (); @@ -74,7 +78,8 @@ private: void signal_video (boost::shared_ptr<Image>, boost::shared_ptr<Subtitle>); SourceFrame _video_frame; - + SourceFrame _last_source_frame; + boost::shared_ptr<TimedSubtitle> _timed_subtitle; boost::shared_ptr<Image> _last_image; diff --git a/src/lib/wscript b/src/lib/wscript index c1786bb81..5d3fbb906 100644 --- a/src/lib/wscript +++ b/src/lib/wscript @@ -37,7 +37,6 @@ def build(bld): gain.cc image.cc imagemagick_decoder.cc - imagemagick_encoder.cc j2k_still_encoder.cc j2k_wav_encoder.cc job.cc |
