summaryrefslogtreecommitdiff
path: root/src/lib
diff options
context:
space:
mode:
authorCarl Hetherington <cth@carlh.net>2020-05-12 20:34:42 +0200
committerCarl Hetherington <cth@carlh.net>2020-05-12 20:34:42 +0200
commit4edc14c8b1410e24f68b510cc14409b96c0338a3 (patch)
tree20669816386ba0cd28d1cef2c4c7836cb47098f5 /src/lib
parent3e96f929fdf740f414b114c5d9765e22fcc46de6 (diff)
parentc6475f24a7b21fe2291ebc809431ecc2654d3355 (diff)
Re-work scaling following excellent insights by Carsten Kurz, described
on #1669. Now the scaling of video content is guessed based on its aspect ratio (taking into account pixel aspect ratio) and there is a "custom" option for overriding incorrectly-marked-up content or content which actually needs its shape to be changed. Essentially we are defaulting to what used to be called "no stretch". This commit also adds buttons to change left/right and top/bottom crop together, and auto-guessing of DCP container size when any change results in there being just one piece of video content in a project. These changes include breaking changes to metadata.xml; old projects should load (with new settings that preserve the project's apperance) but once saved they will not load into 2.14.x. DCP-o-matic saves a backup file (metadata.37.xml) when 2.15.x loads an old project.
Diffstat (limited to 'src/lib')
-rw-r--r--src/lib/config.cc12
-rw-r--r--src/lib/config.h9
-rw-r--r--src/lib/create_cli.cc29
-rw-r--r--src/lib/create_cli.h1
-rw-r--r--src/lib/dcp.cc4
-rw-r--r--src/lib/film.cc83
-rw-r--r--src/lib/film.h7
-rw-r--r--src/lib/hints.cc2
-rw-r--r--src/lib/player.cc4
-rw-r--r--src/lib/player.h4
-rw-r--r--src/lib/player_video.cc2
-rw-r--r--src/lib/util.cc19
-rw-r--r--src/lib/util.h1
-rw-r--r--src/lib/video_content.cc141
-rw-r--r--src/lib/video_content.h42
-rw-r--r--src/lib/video_content_scale.cc171
-rw-r--r--src/lib/video_content_scale.h81
-rw-r--r--src/lib/wscript1
18 files changed, 235 insertions, 378 deletions
diff --git a/src/lib/config.cc b/src/lib/config.cc
index ebb6ece93..18955a896 100644
--- a/src/lib/config.cc
+++ b/src/lib/config.cc
@@ -99,7 +99,6 @@ Config::set_defaults ()
_language = optional<string> ();
_default_still_length = 10;
_default_container = Ratio::from_id ("185");
- _default_scale_to = 0;
_default_dcp_content_type = DCPContentType::from_isdcf_name ("FTR");
_default_dcp_audio_channels = 6;
_default_j2k_bandwidth = 150000000;
@@ -309,11 +308,6 @@ try
_default_container = Ratio::from_id ("185");
}
- c = f.optional_string_child ("DefaultScaleTo");
- if (c) {
- _default_scale_to = Ratio::from_id (c.get ());
- }
-
_default_dcp_content_type = DCPContentType::from_isdcf_name(f.optional_string_child("DefaultDCPContentType").get_value_or("FTR"));
_default_dcp_audio_channels = f.optional_number_child<int>("DefaultDCPAudioChannels").get_value_or (6);
@@ -719,12 +713,6 @@ Config::write_config () const
*/
root->add_child("DefaultContainer")->add_child_text (_default_container->id ());
}
- if (_default_scale_to) {
- /* [XML:opt] DefaultScaleTo ID of default ratio to scale content to when creating new films
- (see <code>DefaultContainer</code> for IDs).
- */
- root->add_child("DefaultScaleTo")->add_child_text (_default_scale_to->id ());
- }
if (_default_dcp_content_type) {
/* [XML:opt] DefaultDCPContentType Default content type ot use when creating new films (<code>FTR</code>, <code>SHR</code>,
<code>TLR</code>, <code>TST</code>, <code>XSN</code>, <code>RTG</code>, <code>TSR</code>, <code>POL</code>,
diff --git a/src/lib/config.h b/src/lib/config.h
index f54ca3814..23f7ce0b3 100644
--- a/src/lib/config.h
+++ b/src/lib/config.h
@@ -186,10 +186,6 @@ public:
return _default_container;
}
- Ratio const * default_scale_to () const {
- return _default_scale_to;
- }
-
DCPContentType const * default_dcp_content_type () const {
return _default_dcp_content_type;
}
@@ -686,10 +682,6 @@ public:
maybe_set (_default_container, c);
}
- void set_default_scale_to (Ratio const * c) {
- maybe_set (_default_scale_to, c);
- }
-
void set_default_dcp_content_type (DCPContentType const * t) {
maybe_set (_default_dcp_content_type, t);
}
@@ -1233,7 +1225,6 @@ private:
/** Default length of still image content (seconds) */
int _default_still_length;
Ratio const * _default_container;
- Ratio const * _default_scale_to;
DCPContentType const * _default_dcp_content_type;
int _default_dcp_audio_channels;
std::string _dcp_issuer;
diff --git a/src/lib/create_cli.cc b/src/lib/create_cli.cc
index 878ee6fdd..b670282b4 100644
--- a/src/lib/create_cli.cc
+++ b/src/lib/create_cli.cc
@@ -42,7 +42,6 @@ string CreateCLI::_help =
" -c, --dcp-content-type <type> FTR, SHR, TLR, TST, XSN, RTG, TSR, POL, PSA or ADV\n"
" -f, --dcp-frame-rate <rate> set DCP video frame rate (otherwise guessed from content)\n"
" --container-ratio <ratio> 119, 133, 137, 138, 166, 178, 185 or 239\n"
- " --content-ratio <ratio> 119, 133, 137, 138, 166, 178, 185 or 239\n"
" -s, --still-length <n> number of seconds that still content should last\n"
" --standard <standard> SMPTE or interop (default SMPTE)\n"
" --no-use-isdcf-name do not use an ISDCF name; use the specified name unmodified\n"
@@ -79,7 +78,6 @@ CreateCLI::CreateCLI (int argc, char* argv[])
, threed (false)
, dcp_content_type (0)
, container_ratio (0)
- , content_ratio (0)
, still_length (10)
, standard (dcp::SMPTE)
, no_use_isdcf_name (false)
@@ -87,7 +85,6 @@ CreateCLI::CreateCLI (int argc, char* argv[])
, fourk (false)
{
string dcp_content_type_string = "TST";
- string content_ratio_string;
string container_ratio_string;
string standard_string = "SMPTE";
int dcp_frame_rate_int = 0;
@@ -136,7 +133,6 @@ CreateCLI::CreateCLI (int argc, char* argv[])
argument_option(i, argc, argv, "-c", "--dcp-content-type", &claimed, &error, &dcp_content_type_string);
argument_option(i, argc, argv, "-f", "--dcp-frame-rate", &claimed, &error, &dcp_frame_rate_int);
argument_option(i, argc, argv, "", "--container-ratio", &claimed, &error, &container_ratio_string);
- argument_option(i, argc, argv, "", "--content-ratio", &claimed, &error, &content_ratio_string);
argument_option(i, argc, argv, "-s", "--still-length", &claimed, &error, &still_length);
argument_option(i, argc, argv, "", "--standard", &claimed, &error, &standard_string);
argument_option(i, argc, argv, "", "--config", &claimed, &error, &config_dir_string);
@@ -185,25 +181,12 @@ CreateCLI::CreateCLI (int argc, char* argv[])
return;
}
- if (content_ratio_string.empty()) {
- error = String::compose("%1: missing required option --content-ratio", argv[0]);
- return;
- }
-
- content_ratio = Ratio::from_id (content_ratio_string);
- if (!content_ratio) {
- error = String::compose("%1: unrecognised content ratio %2", content_ratio_string);
- return;
- }
-
- if (container_ratio_string.empty()) {
- container_ratio_string = content_ratio_string;
- }
-
- container_ratio = Ratio::from_id (container_ratio_string);
- if (!container_ratio) {
- error = String::compose("%1: unrecognised container ratio %2", argv[0], container_ratio_string);
- return;
+ if (!container_ratio_string.empty()) {
+ container_ratio = Ratio::from_id (container_ratio_string);
+ if (!container_ratio) {
+ error = String::compose("%1: unrecognised container ratio %2", argv[0], container_ratio_string);
+ return;
+ }
}
if (standard_string != "SMPTE" && standard_string != "interop") {
diff --git a/src/lib/create_cli.h b/src/lib/create_cli.h
index a9d9daa22..01e8e6633 100644
--- a/src/lib/create_cli.h
+++ b/src/lib/create_cli.h
@@ -45,7 +45,6 @@ public:
DCPContentType const * dcp_content_type;
boost::optional<int> dcp_frame_rate;
Ratio const * container_ratio;
- Ratio const * content_ratio;
int still_length;
dcp::Standard standard;
bool no_use_isdcf_name;
diff --git a/src/lib/dcp.cc b/src/lib/dcp.cc
index c62b339fa..477b61e9f 100644
--- a/src/lib/dcp.cc
+++ b/src/lib/dcp.cc
@@ -75,9 +75,9 @@ DCP::cpls () const
list<dcp::VerificationNote> notes;
dcp->read (&notes, true);
if (!_tolerant) {
- /** We accept and ignore EmptyAssetPathError but everything else is bad */
+ /** We accept and ignore EMPTY_ASSET_PATH and EXTERNAL_ASSET but everything else is bad */
BOOST_FOREACH (dcp::VerificationNote j, notes) {
- if (j.code() == dcp::VerificationNote::EMPTY_ASSET_PATH) {
+ if (j.code() == dcp::VerificationNote::EMPTY_ASSET_PATH || j.code() == dcp::VerificationNote::EXTERNAL_ASSET) {
LOG_WARNING("Empty path in ASSETMAP of %1", i.string());
} else {
boost::throw_exception(dcp::ReadError(dcp::note_to_string(j)));
diff --git a/src/lib/film.cc b/src/lib/film.cc
index b233e5ee6..2f631bd89 100644
--- a/src/lib/film.cc
+++ b/src/lib/film.cc
@@ -132,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.
*
@@ -162,6 +164,8 @@ Film::Film (optional<boost::filesystem::path> 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)
@@ -464,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;
@@ -613,6 +619,10 @@ Film::read_metadata (optional<boost::filesystem::path> 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<string> notes;
_playlist->set_from_xml (shared_from_this(), f.node_child ("Playlist"), _state_version, notes);
@@ -787,11 +797,7 @@ Film::isdcf_name (bool if_created_now) const
BOOST_FOREACH (shared_ptr<Content> 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;
}
}
@@ -971,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<Film> 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<Film> ch (this, RESOLUTION);
_resolution = r;
+
+ if (explicit_user) {
+ _user_explicit_resolution = true;
+ }
}
+
void
Film::set_j2k_bandwidth (int b)
{
@@ -1269,12 +1294,54 @@ Film::add_content (shared_ptr<Content> 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<VideoContent> video;
+ BOOST_FOREACH (shared_ptr<const Content> 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<Content> c)
{
_playlist->remove (c);
+ maybe_set_container_and_resolution ();
}
void
diff --git a/src/lib/film.h b/src/lib/film.h
index 40d366f8f..b03b0258e 100644
--- a/src/lib/film.h
+++ b/src/lib/film.h
@@ -351,8 +351,8 @@ public:
void move_content_earlier (boost::shared_ptr<Content>);
void move_content_later (boost::shared_ptr<Content>);
void set_dcp_content_type (DCPContentType const *);
- void set_container (Ratio const *);
- void set_resolution (Resolution);
+ void set_container (Ratio const *, bool user_explicit = true);
+ void set_resolution (Resolution, bool user_explicit = true);
void set_signed (bool);
void set_encrypted (bool);
void set_key (dcp::Key key);
@@ -409,6 +409,7 @@ private:
void maybe_add_content (boost::weak_ptr<Job>, boost::weak_ptr<Content>, bool disable_audio_analysis);
void audio_analysis_finished ();
void check_settings_consistency ();
+ void maybe_set_container_and_resolution ();
static std::string const metadata_file;
@@ -462,6 +463,8 @@ private:
bool _reencode_j2k;
/** true if the user has ever explicitly set the video frame rate of this film */
bool _user_explicit_video_frame_rate;
+ bool _user_explicit_container;
+ bool _user_explicit_resolution;
std::map<dcp::Marker, dcpomatic::DCPTime> _markers;
std::vector<dcp::Rating> _ratings;
std::string _content_version;
diff --git a/src/lib/hints.cc b/src/lib/hints.cc
index 581d63972..0f18835ef 100644
--- a/src/lib/hints.cc
+++ b/src/lib/hints.cc
@@ -123,7 +123,7 @@ Hints::thread ()
int scope = 0;
BOOST_FOREACH (shared_ptr<const Content> i, content) {
if (i->video) {
- Ratio const * r = i->video->scale().ratio ();
+ Ratio const * r = Ratio::nearest_from_ratio(i->video->scaled_size(film->frame_size()).ratio());
if (r && r->id() == "239") {
++scope;
} else if (r && r->id() != "239" && r->id() != "190") {
diff --git a/src/lib/player.cc b/src/lib/player.cc
index e41cecf13..059465724 100644
--- a/src/lib/player.cc
+++ b/src/lib/player.cc
@@ -864,9 +864,7 @@ Player::video (weak_ptr<Piece> wp, ContentVideo video)
video.image,
piece->content->video->crop (),
piece->content->video->fade (_film, video.frame),
- piece->content->video->scale().size (
- piece->content->video, _video_container_size, _film->frame_size ()
- ),
+ scale_for_display(piece->content->video->scaled_size(_film->frame_size()), _video_container_size, _film->frame_size()),
_video_container_size,
video.eyes,
video.part,
diff --git a/src/lib/player.h b/src/lib/player.h
index 51de78982..bb2a0c613 100644
--- a/src/lib/player.h
+++ b/src/lib/player.h
@@ -159,7 +159,9 @@ private:
boost::atomic<int> _suspended;
std::list<boost::shared_ptr<Piece> > _pieces;
- /** Size of the image in the DCP (e.g. 1990x1080 for flat) */
+ /** Size of the image we are rendering to; this may be the DCP frame size, or
+ * the size of preview in a window.
+ */
dcp::Size _video_container_size;
boost::shared_ptr<Image> _black_image;
diff --git a/src/lib/player_video.cc b/src/lib/player_video.cc
index cb2a55724..10e798ed5 100644
--- a/src/lib/player_video.cc
+++ b/src/lib/player_video.cc
@@ -339,7 +339,7 @@ PlayerVideo::reset_metadata (shared_ptr<const Film> film, dcp::Size video_contai
_crop = content->video->crop();
_fade = content->video->fade(film, _video_frame.get());
- _inter_size = content->video->scale().size(content->video, video_container_size, film_frame_size);
+ _inter_size = scale_for_display(content->video->scaled_size(film_frame_size), video_container_size, film_frame_size);
_out_size = video_container_size;
_colour_conversion = content->video->colour_conversion();
_video_range = content->video->range();
diff --git a/src/lib/util.cc b/src/lib/util.cc
index d04bbdf24..c1ef4245f 100644
--- a/src/lib/util.cc
+++ b/src/lib/util.cc
@@ -394,7 +394,6 @@ dcpomatic_setup ()
Ratio::setup_ratios ();
PresetColourConversion::setup_colour_conversion_presets ();
- VideoContentScale::setup_scales ();
DCPContentType::setup_dcp_content_types ();
Filter::setup_filters ();
CinemaSoundProcessor::setup_cinema_sound_processors ();
@@ -1184,3 +1183,21 @@ linear_to_db (double linear)
return 20 * log10(linear);
}
+
+dcp::Size
+scale_for_display (dcp::Size s, dcp::Size display_container, dcp::Size film_container)
+{
+ /* Now scale it down if the display container is smaller than the film container */
+ if (display_container != film_container) {
+ float const scale = min (
+ float (display_container.width) / film_container.width,
+ float (display_container.height) / film_container.height
+ );
+
+ s.width = lrintf (s.width * scale);
+ s.height = lrintf (s.height * scale);
+ }
+
+ return s;
+}
+
diff --git a/src/lib/util.h b/src/lib/util.h
index f8e9409f1..f7b4704ac 100644
--- a/src/lib/util.h
+++ b/src/lib/util.h
@@ -116,6 +116,7 @@ extern void copy_in_bits (boost::filesystem::path from, boost::filesystem::path
extern boost::shared_ptr<dcp::CertificateChain> read_swaroop_chain (boost::filesystem::path path);
extern void write_swaroop_chain (boost::shared_ptr<const dcp::CertificateChain> chain, boost::filesystem::path output);
#endif
+extern dcp::Size scale_for_display (dcp::Size s, dcp::Size display_container, dcp::Size film_container);
template <class T>
std::list<T>
diff --git a/src/lib/video_content.cc b/src/lib/video_content.cc
index 685336367..b4ad30f70 100644
--- a/src/lib/video_content.cc
+++ b/src/lib/video_content.cc
@@ -1,5 +1,5 @@
/*
- Copyright (C) 2013-2019 Carl Hetherington <cth@carlh.net>
+ Copyright (C) 2013-2020 Carl Hetherington <cth@carlh.net>
This file is part of DCP-o-matic.
@@ -48,6 +48,8 @@ int const VideoContentProperty::COLOUR_CONVERSION = 5;
int const VideoContentProperty::FADE_IN = 6;
int const VideoContentProperty::FADE_OUT = 7;
int const VideoContentProperty::RANGE = 8;
+int const VideoContentProperty::CUSTOM_RATIO = 9;
+int const VideoContentProperty::CUSTOM_SIZE = 10;
using std::string;
using std::setprecision;
@@ -70,7 +72,6 @@ VideoContent::VideoContent (Content* parent)
, _use (true)
, _length (0)
, _frame_type (VIDEO_FRAME_TYPE_2D)
- , _scale (VideoContentScale (Ratio::from_id ("178")))
, _yuv (true)
, _fade_in (0)
, _fade_out (0)
@@ -139,10 +140,29 @@ VideoContent::VideoContent (Content* parent, cxml::ConstNodePtr node, int versio
if (version <= 7) {
optional<string> r = node->optional_string_child ("Ratio");
if (r) {
- _scale = VideoContentScale (Ratio::from_id (r.get ()));
+ _legacy_ratio = Ratio::from_id(r.get())->ratio();
}
+ } else if (version <= 37) {
+ optional<string> ratio = node->node_child("Scale")->optional_string_child("Ratio");
+ if (ratio) {
+ _legacy_ratio = Ratio::from_id(ratio.get())->ratio();
+ }
+ optional<bool> scale = node->node_child("Scale")->optional_bool_child("Scale");
+ if (scale) {
+ if (*scale) {
+ /* This is what we used to call "no stretch" */
+ _legacy_ratio = _size.ratio();
+ } else {
+ /* This is what we used to call "no scale" */
+ _custom_size = _size;
+ }
+ }
+
} else {
- _scale = VideoContentScale (node->node_child ("Scale"));
+ _custom_ratio = node->optional_number_child<float>("CustomRatio");
+ if (node->optional_number_child<int>("CustomWidth")) {
+ _custom_size = dcp::Size (node->number_child<int>("CustomWidth"), node->number_child<int>("CustomHeight"));
+ }
}
if (node->optional_node_child ("ColourConversion")) {
@@ -190,8 +210,12 @@ VideoContent::VideoContent (Content* parent, vector<shared_ptr<Content> > c)
throw JoinError (_("Content to be joined must have the same crop."));
}
- if (c[i]->video->scale() != ref->scale()) {
- throw JoinError (_("Content to be joined must have the same scale setting."));
+ if (c[i]->video->custom_ratio() != ref->custom_ratio()) {
+ throw JoinError (_("Content to be joined must have the same custom ratio setting."));
+ }
+
+ if (c[i]->video->custom_size() != ref->custom_size()) {
+ throw JoinError (_("Content to be joined must have the same custom size setting."));
}
if (c[i]->video->colour_conversion() != ref->colour_conversion()) {
@@ -213,7 +237,7 @@ VideoContent::VideoContent (Content* parent, vector<shared_ptr<Content> > c)
_size = ref->size ();
_frame_type = ref->frame_type ();
_crop = ref->crop ();
- _scale = ref->scale ();
+ _custom_ratio = ref->custom_ratio ();
_colour_conversion = ref->colour_conversion ();
_fade_in = ref->fade_in ();
_fade_out = ref->fade_out ();
@@ -233,7 +257,13 @@ VideoContent::as_xml (xmlpp::Node* node) const
node->add_child("SampleAspectRatio")->add_child_text (raw_convert<string> (_sample_aspect_ratio.get ()));
}
_crop.as_xml (node);
- _scale.as_xml (node->add_child("Scale"));
+ if (_custom_ratio) {
+ node->add_child("CustomRatio")->add_child_text(raw_convert<string>(*_custom_ratio));
+ }
+ if (_custom_size) {
+ node->add_child("CustomWidth")->add_child_text(raw_convert<string>(_custom_size->width));
+ node->add_child("CustomHeight")->add_child_text(raw_convert<string>(_custom_size->height));
+ }
if (_colour_conversion) {
_colour_conversion.get().as_xml (node->add_child("ColourConversion"));
}
@@ -265,15 +295,6 @@ VideoContent::take_from_examiner (shared_ptr<VideoExaminer> d)
_sample_aspect_ratio = ar;
_yuv = yuv;
_range = range;
-
- if (Config::instance()->default_scale_to ()) {
- _scale = VideoContentScale (Config::instance()->default_scale_to ());
- } else {
- /* Guess correct scale from size and sample aspect ratio */
- _scale = VideoContentScale (
- Ratio::nearest_from_ratio (double (_size.width) * ar.get_value_or (1) / _size.height)
- );
- }
}
LOG_GENERAL ("Video length obtained from header as %1 frames", _length);
@@ -289,13 +310,15 @@ VideoContent::identifier () const
{
char buffer[256];
snprintf (
- buffer, sizeof(buffer), "%d_%d_%d_%d_%d_%s_%" PRId64 "_%" PRId64 "_%d",
+ buffer, sizeof(buffer), "%d_%d_%d_%d_%d_%f_%d_%d%" PRId64 "_%" PRId64 "_%d",
(_use ? 1 : 0),
crop().left,
crop().right,
crop().top,
crop().bottom,
- scale().id().c_str(),
+ _custom_ratio.get_value_or(0),
+ _custom_size ? _custom_size->width : 0,
+ _custom_size ? _custom_size->height : 0,
_fade_in,
_fade_out,
_range == VIDEO_RANGE_FULL ? 0 : 1
@@ -354,29 +377,6 @@ VideoContent::size_after_crop () const
return crop().apply (size_after_3d_split ());
}
-void
-VideoContent::scale_and_crop_to_fit_width (shared_ptr<const Film> film)
-{
- set_scale (VideoContentScale(film->container()));
-
- int const crop = max (0, int (size().height - double (film->frame_size().height) * size().width / film->frame_size().width));
- set_left_crop (0);
- set_right_crop (0);
- set_top_crop (crop / 2);
- set_bottom_crop (crop / 2);
-}
-
-void
-VideoContent::scale_and_crop_to_fit_height (shared_ptr<const Film> film)
-{
- set_scale (VideoContentScale(film->container()));
-
- int const crop = max (0, int (size().width - double (film->frame_size().width) * size().height / film->frame_size().height));
- set_left_crop (crop / 2);
- set_right_crop (crop / 2);
- set_top_crop (0);
- set_bottom_crop (0);
-}
/** @param f Frame index within the whole (untrimmed) content.
* @return Fade factor (between 0 and 1) or unset if there is no fade.
@@ -402,7 +402,7 @@ VideoContent::fade (shared_ptr<const Film> film, Frame f) const
}
string
-VideoContent::processing_description (shared_ptr<const Film> film) const
+VideoContent::processing_description (shared_ptr<const Film> film)
{
string d;
char buffer[256];
@@ -439,7 +439,7 @@ VideoContent::processing_description (shared_ptr<const Film> film) const
}
dcp::Size const container_size = film->frame_size ();
- dcp::Size const scaled = scale().size (shared_from_this(), container_size, container_size);
+ dcp::Size const scaled = scaled_size (container_size);
if (scaled != size_after_crop ()) {
d += String::compose (
@@ -512,11 +512,6 @@ VideoContent::set_bottom_crop (int c)
maybe_set (_crop.bottom, c, VideoContentProperty::CROP);
}
-void
-VideoContent::set_scale (VideoContentScale s)
-{
- maybe_set (_scale, s, VideoContentProperty::SCALE);
-}
void
VideoContent::set_frame_type (VideoFrameType t)
@@ -574,7 +569,8 @@ VideoContent::take_settings_from (shared_ptr<const VideoContent> c)
set_right_crop (c->_crop.right);
set_top_crop (c->_crop.top);
set_bottom_crop (c->_crop.bottom);
- set_scale (c->_scale);
+ set_custom_ratio (c->_custom_ratio);
+ set_custom_size (c->_custom_size);
set_fade_in (c->_fade_in);
set_fade_out (c->_fade_out);
}
@@ -592,3 +588,48 @@ VideoContent::modify_trim_start (ContentTime& trim) const
trim = trim.round (_parent->video_frame_rate().get());
}
}
+
+
+/** @param film_container The size of the container for the DCP that we are working on */
+dcp::Size
+VideoContent::scaled_size (dcp::Size film_container)
+{
+ if (_custom_ratio) {
+ return fit_ratio_within(*_custom_ratio, film_container);
+ }
+
+ if (_custom_size) {
+ return *_custom_size;
+ }
+
+ dcp::Size size = size_after_crop ();
+ size.width *= _sample_aspect_ratio.get_value_or(1);
+
+ /* This is what we will return unless there is any legacy stuff to take into account */
+ dcp::Size auto_size = fit_ratio_within (size.ratio(), film_container);
+
+ if (_legacy_ratio) {
+ if (fit_ratio_within(*_legacy_ratio, film_container) != auto_size) {
+ _custom_ratio = *_legacy_ratio;
+ _legacy_ratio = optional<float>();
+ return fit_ratio_within(*_custom_ratio, film_container);
+ }
+ _legacy_ratio = 0;
+ }
+
+ return auto_size;
+}
+
+
+void
+VideoContent::set_custom_ratio (optional<float> ratio)
+{
+ maybe_set (_custom_ratio, ratio, VideoContentProperty::CUSTOM_RATIO);
+}
+
+
+void
+VideoContent::set_custom_size (optional<dcp::Size> size)
+{
+ maybe_set (_custom_size, size, VideoContentProperty::CUSTOM_SIZE);
+}
diff --git a/src/lib/video_content.h b/src/lib/video_content.h
index 37223c457..b478666bb 100644
--- a/src/lib/video_content.h
+++ b/src/lib/video_content.h
@@ -1,5 +1,5 @@
/*
- Copyright (C) 2013-2019 Carl Hetherington <cth@carlh.net>
+ Copyright (C) 2013-2020 Carl Hetherington <cth@carlh.net>
This file is part of DCP-o-matic.
@@ -22,7 +22,6 @@
#define DCPOMATIC_VIDEO_CONTENT_H
#include "colour_conversion.h"
-#include "video_content_scale.h"
#include "dcpomatic_time.h"
#include "user_property.h"
#include "types.h"
@@ -48,6 +47,8 @@ public:
static int const FADE_IN;
static int const FADE_OUT;
static int const RANGE;
+ static int const CUSTOM_RATIO;
+ static int const CUSTOM_SIZE;
};
class VideoContent : public ContentPart, public boost::enable_shared_from_this<VideoContent>
@@ -87,7 +88,9 @@ public:
void set_top_crop (int);
void set_bottom_crop (int);
- void set_scale (VideoContentScale);
+ void set_custom_ratio (boost::optional<float> ratio);
+ void set_custom_size (boost::optional<dcp::Size> size);
+
void unset_colour_conversion ();
void set_colour_conversion (ColourConversion);
@@ -127,12 +130,19 @@ public:
return _crop.bottom;
}
- /** @return Description of how to scale this content (if indeed it should be scaled) */
- VideoContentScale scale () const {
+
+ boost::optional<float> custom_ratio () const {
+ boost::mutex::scoped_lock lm (_mutex);
+ return _custom_ratio;
+ }
+
+
+ boost::optional<dcp::Size> custom_size () const {
boost::mutex::scoped_lock lm (_mutex);
- return _scale;
+ return _custom_size;
}
+
boost::optional<ColourConversion> colour_conversion () const {
boost::mutex::scoped_lock lm (_mutex);
return _colour_conversion;
@@ -168,15 +178,14 @@ public:
return _use;
}
+ /* XXX: names for these? */
dcp::Size size_after_3d_split () const;
dcp::Size size_after_crop () const;
+ dcp::Size scaled_size (dcp::Size container_size);
boost::optional<double> fade (boost::shared_ptr<const Film> film, Frame) const;
- void scale_and_crop_to_fit_width (boost::shared_ptr<const Film> film);
- void scale_and_crop_to_fit_height (boost::shared_ptr<const Film> film);
-
- std::string processing_description (boost::shared_ptr<const Film> film) const;
+ std::string processing_description (boost::shared_ptr<const Film> film);
void set_length (Frame);
@@ -194,6 +203,9 @@ private:
friend struct best_dcp_frame_rate_test_single;
friend struct best_dcp_frame_rate_test_double;
friend struct audio_sampling_rate_test;
+ friend struct scaled_size_test1;
+ friend struct scaled_size_test2;
+ friend struct scaled_size_legacy_test;
VideoContent (Content* parent, cxml::ConstNodePtr, int);
void setup_default_colour_conversion ();
@@ -204,7 +216,15 @@ private:
dcp::Size _size;
VideoFrameType _frame_type;
Crop _crop;
- VideoContentScale _scale;
+ /** ratio to scale cropped image to (or none to guess); i.e. if set, scale to _custom_ratio:1 */
+ boost::optional<float> _custom_ratio;
+ /** size to scale cropped image to; only used if _custom_ratio is none */
+ boost::optional<dcp::Size> _custom_size;
+ /** ratio obtained from an older metadata file; will be used to set up
+ * _custom_{ratio,size} (or not, if not required) on the first call to
+ * scaled_size()
+ */
+ boost::optional<float> _legacy_ratio;
/** Sample aspect ratio obtained from the content file's header, if there is one */
boost::optional<double> _sample_aspect_ratio;
bool _yuv;
diff --git a/src/lib/video_content_scale.cc b/src/lib/video_content_scale.cc
deleted file mode 100644
index 73dfd27fe..000000000
--- a/src/lib/video_content_scale.cc
+++ /dev/null
@@ -1,171 +0,0 @@
-/*
- Copyright (C) 2013-2018 Carl Hetherington <cth@carlh.net>
-
- This file is part of DCP-o-matic.
-
- DCP-o-matic 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.
-
- DCP-o-matic 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 DCP-o-matic. If not, see <http://www.gnu.org/licenses/>.
-
-*/
-
-#include "video_content_scale.h"
-#include "video_content.h"
-#include "ratio.h"
-#include "util.h"
-#include <libcxml/cxml.h>
-#include <libxml++/libxml++.h>
-#include <boost/optional.hpp>
-#include <iostream>
-
-#include "i18n.h"
-
-using std::vector;
-using std::string;
-using std::min;
-using std::cout;
-using boost::shared_ptr;
-using boost::optional;
-
-vector<VideoContentScale> VideoContentScale::_scales;
-
-VideoContentScale::VideoContentScale (Ratio const * r)
- : _ratio (r)
- , _scale (true)
-{
-
-}
-
-VideoContentScale::VideoContentScale ()
- : _ratio (0)
- , _scale (false)
-{
-
-}
-
-VideoContentScale::VideoContentScale (bool scale)
- : _ratio (0)
- , _scale (scale)
-{
-
-}
-
-VideoContentScale::VideoContentScale (shared_ptr<cxml::Node> node)
- : _ratio (0)
- , _scale (true)
-{
- optional<string> r = node->optional_string_child ("Ratio");
- if (r) {
- _ratio = Ratio::from_id (r.get ());
- } else {
- _scale = node->bool_child ("Scale");
- }
-}
-
-void
-VideoContentScale::as_xml (xmlpp::Node* node) const
-{
- if (_ratio) {
- node->add_child("Ratio")->add_child_text (_ratio->id ());
- } else {
- node->add_child("Scale")->add_child_text (_scale ? "1" : "0");
- }
-}
-
-string
-VideoContentScale::id () const
-{
- if (_ratio) {
- return _ratio->id ();
- }
-
- return (_scale ? "S1" : "S0");
-}
-
-string
-VideoContentScale::name () const
-{
- if (_ratio) {
- return _ratio->image_nickname ();
- }
-
- if (_scale) {
- return _("No stretch");
- }
-
- return _("No scale");
-}
-
-/** @param display_container Size of the container that we are displaying this content in.
- * @param film_container The size of the film's image.
- * @return Size, in pixels that the VideoContent's image should be scaled to (taking into account its pixel aspect ratio)
- */
-dcp::Size
-VideoContentScale::size (shared_ptr<const VideoContent> c, dcp::Size display_container, dcp::Size film_container) const
-{
- /* Work out the size of the content if it were put inside film_container */
-
- dcp::Size video_size_after_crop = c->size_after_crop();
- video_size_after_crop.width *= c->sample_aspect_ratio().get_value_or(1);
-
- dcp::Size size;
-
- if (_ratio) {
- /* Stretch to fit the requested ratio */
- size = fit_ratio_within (_ratio->ratio (), film_container);
- } else if (_scale || video_size_after_crop.width > film_container.width || video_size_after_crop.height > film_container.height) {
- /* Scale, preserving aspect ratio; this is either if we have been asked to scale with no stretch
- or if the unscaled content is too big for film_container.
- */
- size = fit_ratio_within (video_size_after_crop.ratio(), film_container);
- } else {
- /* No stretch nor scale */
- size = video_size_after_crop;
- }
-
- /* Now scale it down if the display container is smaller than the film container */
- if (display_container != film_container) {
- float const scale = min (
- float (display_container.width) / film_container.width,
- float (display_container.height) / film_container.height
- );
-
- size.width = lrintf (size.width * scale);
- size.height = lrintf (size.height * scale);
- }
-
- return size;
-}
-
-void
-VideoContentScale::setup_scales ()
-{
- vector<Ratio const *> ratios = Ratio::all ();
- for (vector<Ratio const *>::const_iterator i = ratios.begin(); i != ratios.end(); ++i) {
- _scales.push_back (VideoContentScale (*i));
- }
-
- _scales.push_back (VideoContentScale (true));
- _scales.push_back (VideoContentScale (false));
-}
-
-bool
-operator== (VideoContentScale const & a, VideoContentScale const & b)
-{
- return (a.ratio() == b.ratio() && a.scale() == b.scale());
-}
-
-bool
-operator!= (VideoContentScale const & a, VideoContentScale const & b)
-{
- return (a.ratio() != b.ratio() || a.scale() != b.scale());
-}
diff --git a/src/lib/video_content_scale.h b/src/lib/video_content_scale.h
deleted file mode 100644
index 845c71a52..000000000
--- a/src/lib/video_content_scale.h
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- Copyright (C) 2013-2018 Carl Hetherington <cth@carlh.net>
-
- This file is part of DCP-o-matic.
-
- DCP-o-matic 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.
-
- DCP-o-matic 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 DCP-o-matic. If not, see <http://www.gnu.org/licenses/>.
-
-*/
-
-#ifndef DCPOMATIC_VIDEO_CONTENT_SCALE_H
-#define DCPOMATIC_VIDEO_CONTENT_SCALE_H
-
-#include <dcp/util.h>
-#include <boost/shared_ptr.hpp>
-#include <vector>
-
-namespace cxml {
- class Node;
-}
-
-namespace xmlpp {
- class Node;
-}
-
-class Ratio;
-class VideoContent;
-
-class VideoContentScale
-{
-public:
- VideoContentScale ();
- explicit VideoContentScale (Ratio const *);
- explicit VideoContentScale (bool);
- explicit VideoContentScale (boost::shared_ptr<cxml::Node>);
-
- dcp::Size size (boost::shared_ptr<const VideoContent>, dcp::Size display_container, dcp::Size film_container) const;
- std::string id () const;
- std::string name () const;
- void as_xml (xmlpp::Node *) const;
-
- Ratio const * ratio () const {
- return _ratio;
- }
-
- bool scale () const {
- return _scale;
- }
-
- static void setup_scales ();
- static std::vector<VideoContentScale> all () {
- return _scales;
- }
-
-private:
- /** a ratio to stretch the content to, or 0 for no stretch */
- Ratio const * _ratio;
- /** true if we want to change the size of the content in any way */
- bool _scale;
-
- /* If _ratio is 0 and _scale is false there is no scale at all (i.e.
- the content is used at its original size)
- */
-
- static std::vector<VideoContentScale> _scales;
-};
-
-bool operator== (VideoContentScale const & a, VideoContentScale const & b);
-bool operator!= (VideoContentScale const & a, VideoContentScale const & b);
-
-#endif
diff --git a/src/lib/wscript b/src/lib/wscript
index 0f2a5d197..fb222fcf4 100644
--- a/src/lib/wscript
+++ b/src/lib/wscript
@@ -177,7 +177,6 @@ sources = """
util.cc
verify_dcp_job.cc
video_content.cc
- video_content_scale.cc
video_decoder.cc
video_filter_graph.cc
video_mxf_content.cc