X-Git-Url: https://git.carlh.net/gitweb/?a=blobdiff_plain;f=src%2Flib%2Ffilm.cc;h=dc308078c2e31aaa72ea672b8aa4f1ee38ccf347;hb=0b760c0526b0b9d13def519ab8afba1e511d8111;hp=f0441c9e0b038733b86a7797cc32b3706cc5a028;hpb=1e8e6fa6f6bfdc474da95403cb220ea388be269d;p=dcpomatic.git diff --git a/src/lib/film.cc b/src/lib/film.cc index f0441c9e0..dc308078c 100644 --- a/src/lib/film.cc +++ b/src/lib/film.cc @@ -39,7 +39,6 @@ #include "ab_transcode_job.h" #include "transcode_job.h" #include "scp_dcp_job.h" -#include "make_dcp_job.h" #include "log.h" #include "options.h" #include "exceptions.h" @@ -47,7 +46,6 @@ #include "scaler.h" #include "decoder_factory.h" #include "config.h" -#include "check_hashes_job.h" #include "version.h" #include "ui_signaller.h" #include "video_decoder.h" @@ -72,8 +70,9 @@ using boost::to_upper_copy; using boost::ends_with; using boost::starts_with; using boost::optional; +using libdcp::Size; -int const Film::state_version = 1; +int const Film::state_version = 2; /** Construct a Film object in a given directory, reading any metadata * file that exists in that directory. An exception will be thrown if @@ -89,8 +88,8 @@ Film::Film (string d, bool must_exist) , _dcp_content_type (0) , _format (0) , _scaler (Scaler::from_id ("bicubic")) - , _dcp_trim_start (0) - , _dcp_trim_end (0) + , _trim_start (0) + , _trim_end (0) , _dcp_ab (false) , _use_content_audio (true) , _audio_gain (0) @@ -155,9 +154,8 @@ Film::Film (Film const & o) , _crop (o._crop) , _filters (o._filters) , _scaler (o._scaler) - , _dcp_trim_start (o._dcp_trim_start) - , _dcp_trim_end (o._dcp_trim_end) - , _reel_size (o._reel_size) + , _trim_start (o._trim_start) + , _trim_end (o._trim_end) , _dcp_ab (o._dcp_ab) , _content_audio_stream (o._content_audio_stream) , _external_audio (o._external_audio) @@ -180,6 +178,7 @@ Film::Film (Film const & o) , _package_type (o._package_type) , _size (o._size) , _length (o._length) + , _dcp_intrinsic_duration (o._dcp_intrinsic_duration) , _content_digest (o._content_digest) , _content_audio_streams (o._content_audio_streams) , _external_audio_stream (o._external_audio_stream) @@ -194,24 +193,14 @@ Film::~Film () { delete _log; } - -/** @return The path to the directory to write JPEG2000 files to */ + string -Film::j2k_dir () const +Film::video_state_identifier () const { - assert (format()); - - boost::filesystem::path p; - - /* Start with j2c */ - p /= "j2c"; + assert (format ()); pair f = Filter::ffmpeg_strings (filters()); - /* Write stuff to specify the filter / post-processing settings that are in use, - so that we don't get confused about J2K files generated using different - settings. - */ stringstream s; s << format()->id() << "_" << content_digest() @@ -221,19 +210,33 @@ Film::j2k_dir () const << "_" << j2k_bandwidth() << "_" << boost::lexical_cast (colour_lut()); - p /= s.str (); - - /* Similarly for the A/B case */ if (dcp_ab()) { - stringstream s; pair fa = Filter::ffmpeg_strings (Config::instance()->reference_filters()); s << "ab_" << Config::instance()->reference_scaler()->id() << "_" << fa.first << "_" << fa.second; - p /= s.str (); } - + + return s.str (); +} + +/** @return The path to the directory to write video frame hash files to */ +string +Film::hash_dir () const +{ + boost::filesystem::path p; + p /= "hash"; + p /= video_state_identifier (); return dir (p.string()); } +string +Film::video_mxf_path () const +{ + boost::filesystem::path p; + p /= "video"; + p /= video_state_identifier (); + return file (p.string()); +} + /** Add suitable Jobs to the JobManager to create a DCP for this Film. * @param true to transcode, false to use the WAV and J2K files that are already there. */ @@ -255,7 +258,9 @@ Film::make_dcp (bool transcode) } log()->log (String::compose ("Content is %1; type %2", content_path(), (content_type() == STILL ? "still" : "video"))); - log()->log (String::compose ("Content length %1", length().get())); + if (length()) { + log()->log (String::compose ("Content length %1", length().get())); + } log()->log (String::compose ("Content digest %1", content_digest())); log()->log (String::compose ("%1 threads", Config::instance()->num_local_encoding_threads())); log()->log (String::compose ("J2K bandwidth %1", j2k_bandwidth())); @@ -288,47 +293,18 @@ Film::make_dcp (bool transcode) throw MissingSettingError ("name"); } - shared_ptr 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 ()) { - oe->video_range = make_pair (dcp_trim_start(), dcp_trim_start() + dcp_length().get()); - if (audio_stream()) { - oe->audio_range = make_pair ( - - video_frames_to_audio_frames ( - oe->video_range.get().first, - dcp_audio_sample_rate (audio_stream()->sample_rate()), - dcp_frame_rate (frames_per_second()).frames_per_second - ), - - video_frames_to_audio_frames ( - oe->video_range.get().second, - dcp_audio_sample_rate (audio_stream()->sample_rate()), - dcp_frame_rate (frames_per_second()).frames_per_second - ) - ); - } - - } - - oe->video_skip = dcp_frame_rate (frames_per_second()).skip; - - shared_ptr od (new DecodeOptions); - od->decode_subtitles = with_subtitles (); + DecodeOptions od; + od.decode_subtitles = with_subtitles (); shared_ptr r; if (transcode) { if (dcp_ab()) { - r = JobManager::instance()->add (shared_ptr (new ABTranscodeJob (shared_from_this(), od, oe, shared_ptr ()))); + r = JobManager::instance()->add (shared_ptr (new ABTranscodeJob (shared_from_this(), od, shared_ptr ()))); } else { - r = JobManager::instance()->add (shared_ptr (new TranscodeJob (shared_from_this(), od, oe, shared_ptr ()))); + r = JobManager::instance()->add (shared_ptr (new TranscodeJob (shared_from_this(), od, shared_ptr ()))); } } - - r = JobManager::instance()->add (shared_ptr (new CheckHashesJob (shared_from_this(), od, oe, r))); - JobManager::instance()->add (shared_ptr (new MakeDCPJob (shared_from_this(), oe, r))); } /** Start a job to examine our content file */ @@ -369,7 +345,7 @@ Film::encoded_frames () const } int N = 0; - for (boost::filesystem::directory_iterator i = boost::filesystem::directory_iterator (j2k_dir ()); i != boost::filesystem::directory_iterator(); ++i) { + for (boost::filesystem::directory_iterator i = boost::filesystem::directory_iterator (hash_dir ()); i != boost::filesystem::directory_iterator(); ++i) { ++N; boost::this_thread::interruption_point (); } @@ -412,11 +388,8 @@ Film::write_metadata () const f << "filter " << (*i)->id () << "\n"; } f << "scaler " << _scaler->id () << "\n"; - f << "dcp_trim_start " << _dcp_trim_start << "\n"; - f << "dcp_trim_end " << _dcp_trim_end << "\n"; - if (_reel_size) { - f << "reel_size " << _reel_size.get() << "\n"; - } + f << "trim_start " << _trim_start << "\n"; + f << "trim_end " << _trim_end << "\n"; f << "dcp_ab " << (_dcp_ab ? "1" : "0") << "\n"; if (_content_audio_stream) { f << "selected_content_audio_stream " << _content_audio_stream->to_string() << "\n"; @@ -447,6 +420,7 @@ Film::write_metadata () const f << "width " << _size.width << "\n"; f << "height " << _size.height << "\n"; f << "length " << _length.get_value_or(0) << "\n"; + f << "dcp_intrinsic_duration " << _dcp_intrinsic_duration.get_value_or(0) << "\n"; f << "content_digest " << _content_digest << "\n"; for (vector >::const_iterator i = _content_audio_streams.begin(); i != _content_audio_streams.end(); ++i) { @@ -527,12 +501,10 @@ Film::read_metadata () _filters.push_back (Filter::from_id (v)); } else if (k == "scaler") { _scaler = Scaler::from_id (v); - } else if (k == "dcp_trim_start") { - _dcp_trim_start = atoi (v.c_str ()); - } else if (k == "dcp_trim_end") { - _dcp_trim_end = atoi (v.c_str ()); - } else if (k == "reel_size") { - _reel_size = boost::lexical_cast (v); + } else if ( ((!version || version < 2) && k == "trim_start") || k == "trim_start") { + _trim_start = atoi (v.c_str ()); + } else if ( ((!version || version < 2) && k == "trim_end") || k == "trim_end") { + _trim_end = atoi (v.c_str ()); } else if (k == "dcp_ab") { _dcp_ab = (v == "1"); } else if (k == "selected_content_audio_stream" || (!version && k == "selected_audio_stream")) { @@ -593,6 +565,11 @@ Film::read_metadata () if (vv) { _length = vv; } + } else if (k == "dcp_intrinsic_duration") { + int const vv = atoi (v.c_str ()); + if (vv) { + _dcp_intrinsic_duration = vv; + } } else if (k == "content_digest") { _content_digest = v; } else if (k == "content_audio_stream" || (!version && k == "audio_stream")) { @@ -628,8 +605,8 @@ Film::read_metadata () _dirty = false; } -Size -Film::cropped_size (Size s) const +libdcp::Size +Film::cropped_size (libdcp::Size s) const { boost::mutex::scoped_lock lm (_state_mutex); s.width -= _crop.left + _crop.right; @@ -704,30 +681,25 @@ Film::target_audio_sample_rate () const /* Resample to a DCI-approved sample rate */ double t = dcp_audio_sample_rate (audio_stream()->sample_rate()); - DCPFrameRate dfr = dcp_frame_rate (frames_per_second ()); + DCPFrameRate dfr (frames_per_second ()); - /* Compensate for the fact that video will be rounded to the - nearest integer number of frames per second. + /* 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 + look different in the DCP compared to the source (slower or faster). + skip/repeat doesn't come into effect here. */ - if (dfr.run_fast) { - t *= _frames_per_second * dfr.skip / dfr.frames_per_second; + + if (dfr.change_speed) { + t *= _frames_per_second * dfr.factor() / dfr.frames_per_second; } return rint (t); } -boost::optional -Film::dcp_length () const +int +Film::still_duration_in_frames () const { - if (content_type() == STILL) { - return _still_duration * frames_per_second(); - } - - if (!length()) { - return boost::optional (); - } - - return length().get() - dcp_trim_start() - dcp_trim_end(); + return still_duration() * frames_per_second(); } /** @return a DCI-compliant name for a DCP of this film */ @@ -899,8 +871,7 @@ Film::set_content (string c) */ try { - shared_ptr o (new DecodeOptions); - Decoders d = decoder_factory (shared_from_this(), o, 0); + Decoders d = decoder_factory (shared_from_this(), DecodeOptions(), 0); set_size (d.video->native_size ()); set_frames_per_second (d.video->frames_per_second ()); @@ -1075,43 +1046,23 @@ Film::set_scaler (Scaler const * s) } void -Film::set_dcp_trim_start (int t) +Film::set_trim_start (int t) { { boost::mutex::scoped_lock lm (_state_mutex); - _dcp_trim_start = t; + _trim_start = t; } - signal_changed (DCP_TRIM_START); + signal_changed (TRIM_START); } void -Film::set_dcp_trim_end (int t) +Film::set_trim_end (int t) { { boost::mutex::scoped_lock lm (_state_mutex); - _dcp_trim_end = t; + _trim_end = t; } - signal_changed (DCP_TRIM_END); -} - -void -Film::set_reel_size (uint64_t s) -{ - { - boost::mutex::scoped_lock lm (_state_mutex); - _reel_size = s; - } - signal_changed (REEL_SIZE); -} - -void -Film::unset_reel_size () -{ - { - boost::mutex::scoped_lock lm (_state_mutex); - _reel_size = boost::optional (); - } - signal_changed (REEL_SIZE); + signal_changed (TRIM_END); } void @@ -1142,8 +1093,7 @@ Film::set_external_audio (vector a) _external_audio = a; } - shared_ptr o (new DecodeOptions); - shared_ptr decoder (new ExternalAudioDecoder (shared_from_this(), o, 0)); + shared_ptr decoder (new ExternalAudioDecoder (shared_from_this(), DecodeOptions(), 0)); if (decoder->audio_stream()) { _external_audio_stream = decoder->audio_stream (); } @@ -1323,7 +1273,7 @@ Film::set_package_type (string p) } void -Film::set_size (Size s) +Film::set_size (libdcp::Size s) { { boost::mutex::scoped_lock lm (_state_mutex); @@ -1350,7 +1300,17 @@ Film::unset_length () _length = boost::none; } signal_changed (LENGTH); -} +} + +void +Film::set_dcp_intrinsic_duration (int d) +{ + { + boost::mutex::scoped_lock lm (_state_mutex); + _dcp_intrinsic_duration = d; + } + signal_changed (DCP_INTRINSIC_DURATION); +} void Film::set_content_digest (string d) @@ -1431,3 +1391,38 @@ Film::audio_stream () const return _external_audio_stream; } + +string +Film::hash_path (int f) const +{ + boost::filesystem::path p; + p /= hash_dir (); + + stringstream s; + s.width (8); + s << setfill('0') << f << ".md5"; + + p /= s.str(); + return p.string (); +} + +string +Film::j2c_path (int f, bool t) const +{ + boost::filesystem::path p; + p /= "j2c"; + p /= video_state_identifier (); + + stringstream s; + s.width (8); + s << setfill('0') << f << ".j2c"; + + if (t) { + s << ".tmp"; + } + + p /= s.str(); + return p.string (); +} + +