X-Git-Url: https://git.carlh.net/gitweb/?a=blobdiff_plain;f=src%2Flib%2Ffilm.cc;h=8f545952b336f512e363a9f87d8d00f4a29faade;hb=5c57bfc3f39c5c7a3d4ae166e9574f2f745b0fe8;hp=f481399a97eb74b382e3d33094ba2af515e05155;hpb=63cf599696abce2a4f67ba9d34697c5b804cd1c5;p=dcpomatic.git diff --git a/src/lib/film.cc b/src/lib/film.cc index f481399a9..8f545952b 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,12 +46,14 @@ #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" #include "audio_decoder.h" #include "external_audio_decoder.h" +#include "analyse_audio_job.h" + +#include "i18n.h" using std::string; using std::stringstream; @@ -65,15 +66,16 @@ using std::ofstream; using std::setfill; using std::min; using std::make_pair; -using std::cout; +using std::endl; using boost::shared_ptr; using boost::lexical_cast; 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 = 4; /** 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 +91,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) @@ -101,9 +103,13 @@ Film::Film (string d, bool must_exist) , _subtitle_scale (1) , _colour_lut (0) , _j2k_bandwidth (200000000) - , _frames_per_second (0) + , _dci_metadata (Config::instance()->default_dci_metadata ()) + , _dcp_frame_rate (0) + , _source_frame_rate (0) , _dirty (false) { + set_dci_date_today (); + /* Make state.directory a complete path without ..s (where possible) (Code swiped from Adam Bowen on stackoverflow) */ @@ -139,7 +145,6 @@ Film::Film (string d, bool must_exist) } _log = new FileLog (file ("log")); - set_dci_date_today (); } Film::Film (Film const & o) @@ -155,9 +160,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) @@ -171,20 +175,17 @@ Film::Film (Film const & o) , _subtitle_scale (o._subtitle_scale) , _colour_lut (o._colour_lut) , _j2k_bandwidth (o._j2k_bandwidth) - , _audio_language (o._audio_language) - , _subtitle_language (o._subtitle_language) - , _territory (o._territory) - , _rating (o._rating) - , _studio (o._studio) - , _facility (o._facility) - , _package_type (o._package_type) + , _dci_metadata (o._dci_metadata) + , _dci_date (o._dci_date) + , _dcp_frame_rate (o._dcp_frame_rate) , _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) , _subtitle_streams (o._subtitle_streams) - , _frames_per_second (o._frames_per_second) + , _source_frame_rate (o._source_frame_rate) , _dirty (o._dirty) { @@ -194,56 +195,72 @@ 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() << "_" << crop().left << "_" << crop().right << "_" << crop().top << "_" << crop().bottom + << "_" << _dcp_frame_rate << "_" << f.first << "_" << f.second << "_" << scaler()->id() << "_" << 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 info files to */ +string +Film::info_dir () const +{ + boost::filesystem::path p; + p /= "info"; + p /= video_state_identifier (); return dir (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. - */ +string +Film::video_mxf_dir () const +{ + boost::filesystem::path p; + return dir ("video"); +} + +string +Film::video_mxf_filename () const +{ + return video_state_identifier() + ".mxf"; +} + +string +Film::audio_analysis_path () const +{ + boost::filesystem::path p; + p /= "analysis"; + p /= content_digest(); + return file (p.string ()); +} + +/** Add suitable Jobs to the JobManager to create a DCP for this Film */ void -Film::make_dcp (bool transcode) +Film::make_dcp () { set_dci_date_today (); if (dcp_name().find ("/") != string::npos) { - throw BadSettingError ("name", "cannot contain slashes"); + throw BadSettingError (_("name"), _("cannot contain slashes")); } log()->log (String::compose ("DVD-o-matic %1 git %2 using %3", dvdomatic_version, dvdomatic_git_commit, dependency_version_summary())); @@ -254,9 +271,12 @@ Film::make_dcp (bool transcode) 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"))); - log()->log (String::compose ("Content length %1", length().get())); + 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 @@ -273,62 +293,44 @@ Film::make_dcp (bool transcode) log()->log (String::compose ("CPU: %1, %2 processors", c.first, c.second)); if (format() == 0) { - throw MissingSettingError ("format"); + throw MissingSettingError (_("format")); } if (content().empty ()) { - throw MissingSettingError ("content"); + throw MissingSettingError (_("content")); } if (dcp_content_type() == 0) { - throw MissingSettingError ("content type"); + throw MissingSettingError (_("content type")); } if (name().empty()) { - 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 - ) - ); - } - + throw MissingSettingError (_("name")); } - - 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 ()))); - } else { - r = JobManager::instance()->add (shared_ptr (new TranscodeJob (shared_from_this(), od, oe, shared_ptr ()))); - } + if (dcp_ab()) { + r = JobManager::instance()->add (shared_ptr (new ABTranscodeJob (shared_from_this(), od))); + } else { + r = JobManager::instance()->add (shared_ptr (new TranscodeJob (shared_from_this(), od))); + } +} + +/** Start a job to analyse the audio of our content file */ +void +Film::analyse_audio () +{ + if (_analyse_audio_job) { + return; } - 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))); + _analyse_audio_job.reset (new AnalyseAudioJob (shared_from_this())); + _analyse_audio_job->Finished.connect (bind (&Film::analyse_audio_finished, this)); + JobManager::instance()->add (_analyse_audio_job); } /** Start a job to examine our content file */ @@ -339,34 +341,31 @@ Film::examine_content () return; } - _examine_content_job.reset (new ExamineContentJob (shared_from_this(), shared_ptr ())); + _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); } void -Film::examine_content_finished () +Film::analyse_audio_finished () { - _examine_content_job.reset (); + ensure_ui_thread (); + _analyse_audio_job.reset (); + + AudioAnalysisFinished (); } -/** @return full paths to any audio files that this Film has */ -vector -Film::audio_files () const +void +Film::examine_content_finished () { - vector f; - for (boost::filesystem::directory_iterator i = boost::filesystem::directory_iterator (dir("wavs")); i != boost::filesystem::directory_iterator(); ++i) { - f.push_back (i->path().string ()); - } - - return f; + _examine_content_job.reset (); } /** Start a job to send our DCP to the configured TMS */ void Film::send_dcp_to_tms () { - shared_ptr j (new SCPDCPJob (shared_from_this(), shared_ptr ())); + shared_ptr j (new SCPDCPJob (shared_from_this())); JobManager::instance()->add (j); } @@ -381,7 +380,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 (info_dir ()); i != boost::filesystem::directory_iterator(); ++i) { ++N; boost::this_thread::interruption_point (); } @@ -403,75 +402,68 @@ Film::write_metadata () const throw CreateFileError (m); } - f << "version " << state_version << "\n"; + f << "version " << state_version << endl; /* User stuff */ - f << "name " << _name << "\n"; - f << "use_dci_name " << _use_dci_name << "\n"; - f << "content " << _content << "\n"; - f << "trust_content_header " << (_trust_content_header ? "1" : "0") << "\n"; + f << "name " << _name << endl; + f << "use_dci_name " << _use_dci_name << 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->pretty_name () << "\n"; + f << "dcp_content_type " << _dcp_content_type->dci_name () << endl; } if (_format) { - f << "format " << _format->as_metadata () << "\n"; + f << "format " << _format->as_metadata () << endl; } - f << "left_crop " << _crop.left << "\n"; - f << "right_crop " << _crop.right << "\n"; - f << "top_crop " << _crop.top << "\n"; - f << "bottom_crop " << _crop.bottom << "\n"; + f << "left_crop " << _crop.left << endl; + f << "right_crop " << _crop.right << endl; + f << "top_crop " << _crop.top << endl; + f << "bottom_crop " << _crop.bottom << endl; for (vector::const_iterator i = _filters.begin(); i != _filters.end(); ++i) { - 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 << "filter " << (*i)->id () << endl; } - f << "dcp_ab " << (_dcp_ab ? "1" : "0") << "\n"; + f << "scaler " << _scaler->id () << endl; + 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() << "\n"; + f << "selected_content_audio_stream " << _content_audio_stream->to_string() << endl; } for (vector::const_iterator i = _external_audio.begin(); i != _external_audio.end(); ++i) { - f << "external_audio " << *i << "\n"; + f << "external_audio " << *i << endl; } - f << "use_content_audio " << (_use_content_audio ? "1" : "0") << "\n"; - f << "audio_gain " << _audio_gain << "\n"; - f << "audio_delay " << _audio_delay << "\n"; - f << "still_duration " << _still_duration << "\n"; + 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() << "\n"; - } - f << "with_subtitles " << _with_subtitles << "\n"; - f << "subtitle_offset " << _subtitle_offset << "\n"; - f << "subtitle_scale " << _subtitle_scale << "\n"; - f << "colour_lut " << _colour_lut << "\n"; - f << "j2k_bandwidth " << _j2k_bandwidth << "\n"; - f << "audio_language " << _audio_language << "\n"; - f << "subtitle_language " << _subtitle_language << "\n"; - f << "territory " << _territory << "\n"; - f << "rating " << _rating << "\n"; - f << "studio " << _studio << "\n"; - f << "facility " << _facility << "\n"; - f << "package_type " << _package_type << "\n"; - - f << "width " << _size.width << "\n"; - f << "height " << _size.height << "\n"; - f << "length " << _length.get_value_or(0) << "\n"; - f << "content_digest " << _content_digest << "\n"; + 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; + f << "colour_lut " << _colour_lut << endl; + f << "j2k_bandwidth " << _j2k_bandwidth << endl; + _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 << "dcp_intrinsic_duration " << _dcp_intrinsic_duration.get_value_or(0) << endl; + f << "content_digest " << _content_digest << endl; for (vector >::const_iterator i = _content_audio_streams.begin(); i != _content_audio_streams.end(); ++i) { - f << "content_audio_stream " << (*i)->to_string () << "\n"; + f << "content_audio_stream " << (*i)->to_string () << endl; } - f << "external_audio_stream " << _external_audio_stream->to_string() << "\n"; + f << "external_audio_stream " << _external_audio_stream->to_string() << endl; for (vector >::const_iterator i = _subtitle_streams.begin(); i != _subtitle_streams.end(); ++i) { - f << "subtitle_stream " << (*i)->to_string () << "\n"; + f << "subtitle_stream " << (*i)->to_string () << endl; } - f << "frames_per_second " << _frames_per_second << "\n"; + f << "source_frame_rate " << _source_frame_rate << endl; _dirty = false; } @@ -495,7 +487,7 @@ Film::read_metadata () ifstream f (file ("metadata").c_str()); if (!f.good()) { - throw OpenFileError (file("metadata")); + throw OpenFileError (file ("metadata")); } multimap kv = read_key_value (f); @@ -524,7 +516,11 @@ Film::read_metadata () } else if (k == "trust_content_header") { _trust_content_header = (v == "1"); } else if (k == "dcp_content_type") { - _dcp_content_type = DCPContentType::from_pretty_name (v); + if (version < 3) { + _dcp_content_type = DCPContentType::from_pretty_name (v); + } else { + _dcp_content_type = DCPContentType::from_dci_name (v); + } } else if (k == "format") { _format = Format::from_metadata (v); } else if (k == "left_crop") { @@ -539,12 +535,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 == "dcp_trim_start") || k == "trim_start") { + _trim_start = atoi (v.c_str ()); + } else if ( ((!version || version < 2) && k == "dcp_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")) { @@ -579,21 +573,13 @@ Film::read_metadata () _colour_lut = atoi (v.c_str ()); } else if (k == "j2k_bandwidth") { _j2k_bandwidth = atoi (v.c_str ()); - } else if (k == "audio_language") { - _audio_language = v; - } else if (k == "subtitle_language") { - _subtitle_language = v; - } else if (k == "territory") { - _territory = v; - } else if (k == "rating") { - _rating = v; - } else if (k == "studio") { - _studio = v; - } else if (k == "facility") { - _facility = v; - } else if (k == "package_type") { - _package_type = v; + } else if (k == "dci_date") { + _dci_date = boost::gregorian::from_undelimited_string (v); + } else if (k == "dcp_frame_rate") { + _dcp_frame_rate = atoi (v.c_str ()); } + + _dci_metadata.read (k, v); /* Cached stuff */ if (k == "width") { @@ -605,6 +591,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")) { @@ -613,8 +604,12 @@ Film::read_metadata () _external_audio_stream = audio_stream_factory (v, version); } else if (k == "subtitle_stream") { _subtitle_streams.push_back (subtitle_stream_factory (v, version)); - } else if (k == "frames_per_second") { - _frames_per_second = atof (v.c_str ()); + } else if (k == "source_frame_rate") { + _source_frame_rate = atof (v.c_str ()); + } else if (version < 4 && k == "frames_per_second") { + _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); } } @@ -640,8 +635,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; @@ -656,23 +651,31 @@ string Film::dir (string d) const { boost::mutex::scoped_lock lm (_directory_mutex); + boost::filesystem::path p; p /= _directory; p /= d; + boost::filesystem::create_directories (p); + return p.string (); } /** Given a file or directory name, return its full path within the Film's directory. * _directory_mutex must not be locked on entry. + * Any required parent directories will be created. */ string Film::file (string f) const { boost::mutex::scoped_lock lm (_directory_mutex); + boost::filesystem::path p; p /= _directory; p /= f; + + boost::filesystem::create_directories (p.parent_path ()); + return p.string (); } @@ -716,35 +719,30 @@ 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 ()); + FrameRateConversion frc (source_frame_rate(), dcp_frame_rate()); - /* 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 (frc.change_speed) { + t *= source_frame_rate() * frc.factor() / dcp_frame_rate(); } 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() * source_frame_rate(); } /** @return a DCI-compliant name for a DCP of this film */ string -Film::dci_name () const +Film::dci_name (bool if_created_now) const { stringstream d; @@ -770,10 +768,12 @@ Film::dci_name () const d << format()->dci_name() << "_"; } - if (!audio_language().empty ()) { - d << audio_language(); - if (!subtitle_language().empty() && with_subtitles()) { - d << "-" << subtitle_language(); + DCIMetadata const dm = dci_metadata (); + + if (!dm.audio_language.empty ()) { + d << dm.audio_language; + if (!dm.subtitle_language.empty() && with_subtitles()) { + d << "-" << dm.subtitle_language; } else { d << "-XX"; } @@ -781,10 +781,10 @@ Film::dci_name () const d << "_"; } - if (!territory().empty ()) { - d << territory(); - if (!rating().empty ()) { - d << "-" << rating(); + if (!dm.territory.empty ()) { + d << dm.territory; + if (!dm.rating.empty ()) { + d << "-" << dm.rating; } d << "_"; } @@ -806,18 +806,22 @@ Film::dci_name () const d << "2K_"; - if (!studio().empty ()) { - d << studio() << "_"; + if (!dm.studio.empty ()) { + d << dm.studio << "_"; } - d << boost::gregorian::to_iso_string (_dci_date) << "_"; + if (if_created_now) { + d << boost::gregorian::to_iso_string (boost::gregorian::day_clock::local_day ()) << "_"; + } else { + d << boost::gregorian::to_iso_string (_dci_date) << "_"; + } - if (!facility().empty ()) { - d << facility() << "_"; + if (!dm.facility.empty ()) { + d << dm.facility << "_"; } - if (!package_type().empty ()) { - d << package_type(); + if (!dm.package_type.empty ()) { + d << dm.package_type; } return d.str (); @@ -825,10 +829,10 @@ Film::dci_name () const /** @return name to give the DCP */ string -Film::dcp_name () const +Film::dcp_name (bool if_created_now) const { if (use_dci_name()) { - return dci_name (); + return dci_name (if_created_now); } return name(); @@ -868,16 +872,8 @@ Film::set_content (string c) { string check = directory (); -#if BOOST_FILESYSTEM_VERSION == 3 boost::filesystem::path slash ("/"); string platform_slash = slash.make_preferred().string (); -#else -#ifdef DVDOMATIC_WINDOWS - string platform_slash = "\\"; -#else - string platform_slash = "/"; -#endif -#endif if (!ends_with (check, platform_slash)) { check += platform_slash; @@ -911,16 +907,23 @@ 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()); set_size (d.video->native_size ()); - set_frames_per_second (d.video->frames_per_second ()); + 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()); @@ -930,13 +933,6 @@ Film::set_content (string c) set_subtitle_stream (d.video->subtitle_streams().front()); } - { - boost::mutex::scoped_lock lm (_state_mutex); - _content = c; - } - - signal_changed (CONTENT); - examine_content (); } catch (...) { @@ -1087,43 +1083,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 @@ -1154,8 +1130,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())); if (decoder->audio_stream()) { _external_audio_stream = decoder->audio_stream (); } @@ -1265,77 +1240,28 @@ Film::set_j2k_bandwidth (int b) } void -Film::set_audio_language (string l) +Film::set_dci_metadata (DCIMetadata m) { { boost::mutex::scoped_lock lm (_state_mutex); - _audio_language = l; + _dci_metadata = m; } signal_changed (DCI_METADATA); } -void -Film::set_subtitle_language (string l) -{ - { - boost::mutex::scoped_lock lm (_state_mutex); - _subtitle_language = l; - } - signal_changed (DCI_METADATA); -} void -Film::set_territory (string t) +Film::set_dcp_frame_rate (int f) { { boost::mutex::scoped_lock lm (_state_mutex); - _territory = t; + _dcp_frame_rate = f; } - signal_changed (DCI_METADATA); + signal_changed (DCP_FRAME_RATE); } void -Film::set_rating (string r) -{ - { - boost::mutex::scoped_lock lm (_state_mutex); - _rating = r; - } - signal_changed (DCI_METADATA); -} - -void -Film::set_studio (string s) -{ - { - boost::mutex::scoped_lock lm (_state_mutex); - _studio = s; - } - signal_changed (DCI_METADATA); -} - -void -Film::set_facility (string f) -{ - { - boost::mutex::scoped_lock lm (_state_mutex); - _facility = f; - } - signal_changed (DCI_METADATA); -} - -void -Film::set_package_type (string p) -{ - { - boost::mutex::scoped_lock lm (_state_mutex); - _package_type = p; - } - signal_changed (DCI_METADATA); -} - -void -Film::set_size (Size s) +Film::set_size (libdcp::Size s) { { boost::mutex::scoped_lock lm (_state_mutex); @@ -1362,7 +1288,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) @@ -1395,13 +1331,13 @@ Film::set_subtitle_streams (vector > s) } void -Film::set_frames_per_second (float f) +Film::set_source_frame_rate (float f) { { boost::mutex::scoped_lock lm (_state_mutex); - _frames_per_second = f; + _source_frame_rate = f; } - signal_changed (FRAMES_PER_SECOND); + signal_changed (SOURCE_FRAME_RATE); } void @@ -1443,3 +1379,58 @@ Film::audio_stream () const return _external_audio_stream; } + +string +Film::info_path (int f) const +{ + boost::filesystem::path p; + p /= info_dir (); + + stringstream s; + s.width (8); + s << setfill('0') << f << ".md5"; + + p /= s.str(); + + /* info_dir() will already have added any initial bit of the path, + so don't call file() on this. + */ + 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 file (p.string ()); +} + +/** Make an educated guess as to whether we have a complete DCP + * or not. + * @return true if we do. + */ + +bool +Film::have_dcp () const +{ + try { + libdcp::DCP dcp (dir (dcp_name())); + dcp.read (); + } catch (...) { + return false; + } + + return true; +}