X-Git-Url: https://git.carlh.net/gitweb/?a=blobdiff_plain;f=src%2Flib%2Ffilm.cc;h=ab636bdfcc3a651fe79cd1b87dd94d15693e93ed;hb=cb4572c5c4b0a543d42b05a47c37d5db2dc44bb8;hp=dc308078c2e31aaa72ea672b8aa4f1ee38ccf347;hpb=0b760c0526b0b9d13def519ab8afba1e511d8111;p=dcpomatic.git diff --git a/src/lib/film.cc b/src/lib/film.cc index dc308078c..ab636bdfc 100644 --- a/src/lib/film.cc +++ b/src/lib/film.cc @@ -51,6 +51,7 @@ #include "video_decoder.h" #include "audio_decoder.h" #include "external_audio_decoder.h" +#include "analyse_audio_job.h" using std::string; using std::stringstream; @@ -63,7 +64,6 @@ using std::ofstream; using std::setfill; using std::min; using std::make_pair; -using std::cout; using boost::shared_ptr; using boost::lexical_cast; using boost::to_upper_copy; @@ -72,7 +72,7 @@ using boost::starts_with; using boost::optional; using libdcp::Size; -int const Film::state_version = 2; +int const Film::state_version = 3; /** Construct a Film object in a given directory, reading any metadata * file that exists in that directory. An exception will be thrown if @@ -100,9 +100,12 @@ Film::Film (string d, bool must_exist) , _subtitle_scale (1) , _colour_lut (0) , _j2k_bandwidth (200000000) + , _dci_metadata (Config::instance()->default_dci_metadata ()) , _frames_per_second (0) , _dirty (false) { + set_dci_date_today (); + /* Make state.directory a complete path without ..s (where possible) (Code swiped from Adam Bowen on stackoverflow) */ @@ -138,7 +141,6 @@ Film::Film (string d, bool must_exist) } _log = new FileLog (file ("log")); - set_dci_date_today (); } Film::Film (Film const & o) @@ -169,13 +171,8 @@ 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) , _size (o._size) , _length (o._length) , _dcp_intrinsic_duration (o._dcp_intrinsic_duration) @@ -218,30 +215,41 @@ Film::video_state_identifier () const return s.str (); } -/** @return The path to the directory to write video frame hash files to */ +/** @return The path to the directory to write video frame info files to */ string -Film::hash_dir () const +Film::info_dir () const { boost::filesystem::path p; - p /= "hash"; + p /= "info"; p /= video_state_identifier (); return dir (p.string()); } string -Film::video_mxf_path () const +Film::video_mxf_dir () const { boost::filesystem::path p; - p /= "video"; - p /= video_state_identifier (); - return file (p.string()); + return dir ("video"); } -/** 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_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 (); @@ -298,13 +306,24 @@ Film::make_dcp (bool transcode) shared_ptr r; - if (transcode) { - if (dcp_ab()) { - 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, 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; } + + _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 */ @@ -315,11 +334,20 @@ 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::analyse_audio_finished () +{ + ensure_ui_thread (); + _analyse_audio_job.reset (); + + AudioAnalysisFinished (); +} + void Film::examine_content_finished () { @@ -330,7 +358,7 @@ Film::examine_content_finished () 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); } @@ -345,7 +373,7 @@ Film::encoded_frames () const } int N = 0; - for (boost::filesystem::directory_iterator i = boost::filesystem::directory_iterator (hash_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 (); } @@ -375,7 +403,7 @@ Film::write_metadata () const f << "content " << _content << "\n"; f << "trust_content_header " << (_trust_content_header ? "1" : "0") << "\n"; if (_dcp_content_type) { - f << "dcp_content_type " << _dcp_content_type->pretty_name () << "\n"; + f << "dcp_content_type " << _dcp_content_type->dci_name () << "\n"; } if (_format) { f << "format " << _format->as_metadata () << "\n"; @@ -409,14 +437,8 @@ Film::write_metadata () const 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"; - + _dci_metadata.write (f); + f << "dci_date " << boost::gregorian::to_iso_string (_dci_date) << "\n"; f << "width " << _size.width << "\n"; f << "height " << _size.height << "\n"; f << "length " << _length.get_value_or(0) << "\n"; @@ -457,7 +479,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); @@ -486,7 +508,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") { @@ -501,9 +527,9 @@ Film::read_metadata () _filters.push_back (Filter::from_id (v)); } else if (k == "scaler") { _scaler = Scaler::from_id (v); - } else if ( ((!version || version < 2) && k == "trim_start") || k == "trim_start") { + } else if ( ((!version || version < 2) && k == "dcp_trim_start") || k == "trim_start") { _trim_start = atoi (v.c_str ()); - } else if ( ((!version || version < 2) && k == "trim_end") || k == "trim_end") { + } 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"); @@ -539,21 +565,11 @@ 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); } + + _dci_metadata.read (k, v); /* Cached stuff */ if (k == "width") { @@ -621,23 +637,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 (); } @@ -704,7 +728,7 @@ Film::still_duration_in_frames () const /** @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; @@ -730,10 +754,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"; } @@ -741,10 +767,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 << "_"; } @@ -766,18 +792,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 (); @@ -785,10 +815,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(); @@ -828,16 +858,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; @@ -871,7 +893,7 @@ Film::set_content (string c) */ try { - Decoders d = decoder_factory (shared_from_this(), DecodeOptions(), 0); + Decoders d = decoder_factory (shared_from_this(), DecodeOptions()); set_size (d.video->native_size ()); set_frames_per_second (d.video->frames_per_second ()); @@ -880,6 +902,13 @@ Film::set_content (string c) 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()); @@ -889,13 +918,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 (...) { @@ -1093,7 +1115,7 @@ Film::set_external_audio (vector a) _external_audio = a; } - shared_ptr decoder (new ExternalAudioDecoder (shared_from_this(), DecodeOptions(), 0)); + shared_ptr decoder (new ExternalAudioDecoder (shared_from_this(), DecodeOptions())); if (decoder->audio_stream()) { _external_audio_stream = decoder->audio_stream (); } @@ -1203,71 +1225,11 @@ 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; - } - 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) -{ - { - boost::mutex::scoped_lock lm (_state_mutex); - _territory = t; - } - signal_changed (DCI_METADATA); -} - -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; + _dci_metadata = m; } signal_changed (DCI_METADATA); } @@ -1393,16 +1355,20 @@ Film::audio_stream () const } string -Film::hash_path (int f) const +Film::info_path (int f) const { boost::filesystem::path p; - p /= hash_dir (); + 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 (); } @@ -1422,7 +1388,23 @@ Film::j2c_path (int f, bool t) const } p /= s.str(); - return p.string (); + 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; +}