X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=src%2Flib%2Ffilm.cc;h=2f631bd89c02173abba3fc0910289d92fc401208;hb=4edc14c8b1410e24f68b510cc14409b96c0338a3;hp=691ef58dab1012f0b2dfb8c96d7b141e7b34ffc2;hpb=bc36ddea65fda2088f7e8fa98390e3feac07df84;p=dcpomatic.git diff --git a/src/lib/film.cc b/src/lib/film.cc index 691ef58da..2f631bd89 100644 --- a/src/lib/film.cc +++ b/src/lib/film.cc @@ -1,5 +1,5 @@ /* - Copyright (C) 2012-2019 Carl Hetherington + Copyright (C) 2012-2020 Carl Hetherington This file is part of DCP-o-matic. @@ -50,10 +50,12 @@ #include "text_content.h" #include "ffmpeg_content.h" #include "dcp_content.h" -#include "screen_kdm.h" +#include "kdm_with_metadata.h" #include "cinema.h" #include "change_signaller.h" #include "check_content_change_job.h" +#include "ffmpeg_subtitle_stream.h" +#include "font.h" #include #include #include @@ -130,8 +132,10 @@ string const Film::metadata_file = "metadata.xml"; * 36 -> 37 * TextContent can be in a Caption tag, and some of the tag names * have had Subtitle prefixes or suffixes removed. + * 37 -> 38 + * VideoContent scale expressed just as "guess" or "custom" */ -int const Film::current_state_version = 37; +int const Film::current_state_version = 38; /** Construct a Film object in a given directory. * @@ -160,6 +164,8 @@ Film::Film (optional dir) , _upload_after_make_dcp (Config::instance()->default_upload_after_make_dcp()) , _reencode_j2k (false) , _user_explicit_video_frame_rate (false) + , _user_explicit_container (false) + , _user_explicit_resolution (false) , _state_version (current_state_version) , _dirty (false) , _tolerant (false) @@ -302,6 +308,39 @@ Film::audio_analysis_path (shared_ptr playlist) const return p; } + +boost::filesystem::path +Film::subtitle_analysis_path (shared_ptr content) const +{ + boost::filesystem::path p = dir ("analysis"); + + Digester digester; + digester.add (content->digest()); + + if (!content->text.empty()) { + shared_ptr tc = content->text.front(); + digester.add (tc->x_scale()); + digester.add (tc->y_scale()); + BOOST_FOREACH (shared_ptr i, tc->fonts()) { + digester.add (i->id()); + } + if (tc->effect()) { + digester.add (tc->effect().get()); + } + digester.add (tc->line_spacing()); + digester.add (tc->outline_width()); + } + + shared_ptr fc = dynamic_pointer_cast(content); + if (fc) { + digester.add (fc->subtitle_stream()->identifier()); + } + + p /= digester.get (); + return p; +} + + /** Add suitable Jobs to the JobManager to create a DCP for this Film. * @param gui true if this is being called from a GUI tool. * @param check true to check the content in the project for changes before making the DCP. @@ -429,6 +468,8 @@ Film::metadata (bool with_content_paths) const i.as_xml (root->add_child("Rating")); } root->add_child("ContentVersion")->add_child_text(_content_version); + root->add_child("UserExplicitContainer")->add_child_text(_user_explicit_container ? "1" : "0"); + root->add_child("UserExplicitResolution")->add_child_text(_user_explicit_resolution ? "1" : "0"); _playlist->as_xml (root->add_child ("Playlist"), with_content_paths); return doc; @@ -578,6 +619,10 @@ Film::read_metadata (optional path) _content_version = f.optional_string_child("ContentVersion").get_value_or(""); + /* Disable guessing for files made in previous DCP-o-matic versions */ + _user_explicit_container = f.optional_bool_child("UserExplicitContainer").get_value_or(true); + _user_explicit_resolution = f.optional_bool_child("UserExplicitResolution").get_value_or(true); + list notes; _playlist->set_from_xml (shared_from_this(), f.node_child ("Playlist"), _state_version, notes); @@ -752,11 +797,7 @@ Film::isdcf_name (bool if_created_now) const BOOST_FOREACH (shared_ptr i, content ()) { if (i->video) { /* Here's the first piece of video content */ - if (i->video->scale().ratio ()) { - content_ratio = i->video->scale().ratio (); - } else { - content_ratio = Ratio::from_ratio (i->video->size().ratio ()); - } + content_ratio = Ratio::nearest_from_ratio(i->video->scaled_size(frame_size()).ratio()); break; } } @@ -936,20 +977,39 @@ Film::set_dcp_content_type (DCPContentType const * t) _dcp_content_type = t; } + +/** @param explicit_user true if this is being set because of + * a direct user request, false if it is being done because + * DCP-o-matic is guessing the best container to use. + */ void -Film::set_container (Ratio const * c) +Film::set_container (Ratio const * c, bool explicit_user) { ChangeSignaller ch (this, CONTAINER); _container = c; + + if (explicit_user) { + _user_explicit_container = true; + } } + +/** @param explicit_user true if this is being set because of + * a direct user request, false if it is being done because + * DCP-o-matic is guessing the best resolution to use. + */ void -Film::set_resolution (Resolution r) +Film::set_resolution (Resolution r, bool explicit_user) { ChangeSignaller ch (this, RESOLUTION); _resolution = r; + + if (explicit_user) { + _user_explicit_resolution = true; + } } + void Film::set_j2k_bandwidth (int b) { @@ -1234,12 +1294,54 @@ Film::add_content (shared_ptr c) } _playlist->add (shared_from_this(), c); + + maybe_set_container_and_resolution (); +} + + +void +Film::maybe_set_container_and_resolution () +{ + /* Get the only piece of video content, if there is only one */ + shared_ptr video; + BOOST_FOREACH (shared_ptr i, _playlist->content()) { + if (i->video) { + if (!video) { + video = i->video; + } else { + video.reset (); + } + } + } + + if (video) { + /* This is the only piece of video content in this Film. Use it to make a guess for + * DCP container size and resolution, unless the user has already explicitly set these + * things. + */ + if (!_user_explicit_container) { + if (video->size().ratio() > 2.3) { + set_container (Ratio::from_id("239"), false); + } else { + set_container (Ratio::from_id("185"), false); + } + } + + if (!_user_explicit_resolution) { + if (video->size_after_crop().width > 2048 || video->size_after_crop().height > 1080) { + set_resolution (RESOLUTION_4K, false); + } else { + set_resolution (RESOLUTION_2K, false); + } + } + } } void Film::remove_content (shared_ptr c) { _playlist->remove (c); + maybe_set_container_and_resolution (); } void @@ -1254,11 +1356,13 @@ Film::move_content_later (shared_ptr c) _playlist->move_later (shared_from_this(), c); } -/** @return length of the film from time 0 to the last thing on the playlist */ +/** @return length of the film from time 0 to the last thing on the playlist, + * with a minimum length of 1 second. + */ DCPTime Film::length () const { - return _playlist->length(shared_from_this()).ceil(video_frame_rate()); + return max(DCPTime::from_seconds(1), _playlist->length(shared_from_this()).ceil(video_frame_rate())); } int @@ -1465,47 +1569,6 @@ Film::make_kdm ( ).encrypt (signer, recipient, trusted_devices, formulation, disable_forensic_marking_picture, disable_forensic_marking_audio); } -/** @param screens Screens to make KDMs for. - * @param cpl_file Path to CPL to make KDMs for. - * @param from KDM from time expressed as a local time in the time zone of the Screen's Cinema. - * @param until KDM to time expressed as a local time in the time zone of the Screen's Cinema. - * @param formulation KDM formulation to use. - * @param disable_forensic_marking_picture true to disable forensic marking of picture. - * @param disable_forensic_marking_audio if not set, don't disable forensic marking of audio. If set to 0, - * disable all forensic marking; if set above 0, disable forensic marking above that channel. - */ -list > -Film::make_kdms ( - list > screens, - boost::filesystem::path cpl_file, - boost::posix_time::ptime from, - boost::posix_time::ptime until, - dcp::Formulation formulation, - bool disable_forensic_marking_picture, - optional disable_forensic_marking_audio - ) const -{ - list > kdms; - - BOOST_FOREACH (shared_ptr i, screens) { - if (i->recipient) { - dcp::EncryptedKDM const kdm = make_kdm ( - i->recipient.get(), - i->trusted_device_thumbprints(), - cpl_file, - dcp::LocalTime (from, i->cinema ? i->cinema->utc_offset_hour() : 0, i->cinema ? i->cinema->utc_offset_minute() : 0), - dcp::LocalTime (until, i->cinema ? i->cinema->utc_offset_hour() : 0, i->cinema ? i->cinema->utc_offset_minute() : 0), - formulation, - disable_forensic_marking_picture, - disable_forensic_marking_audio - ); - - kdms.push_back (shared_ptr(new DCPScreenKDM(i, kdm))); - } - } - - return kdms; -} /** @return The approximate disk space required to encode a DCP of this film with the * current settings, in bytes. @@ -1641,21 +1704,27 @@ Film::reels () const split_points.sort (); split_points.unique (); - /* Make them into periods */ + /* Make them into periods, coalescing any that are less than 1 second long */ optional last; BOOST_FOREACH (DCPTime t, split_points) { - if (last) { + if (last && (t - *last) >= DCPTime::from_seconds(1)) { + /* Period from *last to t is long enough; use it and start a new one */ p.push_back (DCPTimePeriod(*last, t)); + last = t; + } else if (!last) { + /* That was the first time, so start a new period */ + last = t; } - last = t; } break; } case REELTYPE_BY_LENGTH: { DCPTime current; - /* Integer-divide reel length by the size of one frame to give the number of frames per reel */ - Frame const reel_in_frames = _reel_length / ((j2k_bandwidth() / video_frame_rate()) / 8); + /* Integer-divide reel length by the size of one frame to give the number of frames per reel, + * making sure we don't go less than 1s long. + */ + Frame const reel_in_frames = max(_reel_length / ((j2k_bandwidth() / video_frame_rate()) / 8), static_cast(video_frame_rate())); while (current < len) { DCPTime end = min (len, current + DCPTime::from_frames (reel_in_frames, video_frame_rate ())); p.push_back (DCPTimePeriod (current, end)); @@ -1770,6 +1839,15 @@ Film::unset_marker (dcp::Marker type) _markers.erase (type); } + +void +Film::clear_markers () +{ + ChangeSignaller ch (this, MARKERS); + _markers.clear (); +} + + void Film::set_ratings (vector r) {