+
+ if (sample_aspect_ratio ()) {
+ s += String::compose (N_(", sample aspect ratio %1"), (sample_aspect_ratio().get ()));
+ }
+
+ return s;
+}
+
+dcp::Size
+VideoContent::size_after_3d_split () const
+{
+ dcp::Size const s = size ();
+ switch (frame_type ()) {
+ case VIDEO_FRAME_TYPE_2D:
+ case VIDEO_FRAME_TYPE_3D:
+ case VIDEO_FRAME_TYPE_3D_ALTERNATE:
+ case VIDEO_FRAME_TYPE_3D_LEFT:
+ case VIDEO_FRAME_TYPE_3D_RIGHT:
+ return s;
+ case VIDEO_FRAME_TYPE_3D_LEFT_RIGHT:
+ return dcp::Size (s.width / 2, s.height);
+ case VIDEO_FRAME_TYPE_3D_TOP_BOTTOM:
+ return dcp::Size (s.width, s.height / 2);
+ }
+
+ DCPOMATIC_ASSERT (false);
+}
+
+/** @return Video size after 3D split and crop */
+dcp::Size
+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.
+ */
+optional<double>
+VideoContent::fade (shared_ptr<const Film> film, Frame f) const
+{
+ DCPOMATIC_ASSERT (f >= 0);
+
+ double const vfr = _parent->active_video_frame_rate(film);
+
+ Frame const ts = _parent->trim_start().frames_round(vfr);
+ if ((f - ts) < fade_in()) {
+ return double (f - ts) / fade_in();
+ }
+
+ Frame fade_out_start = length() - _parent->trim_end().frames_round(vfr) - fade_out();
+ if (f >= fade_out_start) {
+ return 1 - double (f - fade_out_start) / fade_out();
+ }
+
+ return optional<double> ();
+}
+
+string
+VideoContent::processing_description (shared_ptr<const Film> film) const
+{
+ string d;
+ char buffer[256];
+
+ if (size().width && size().height) {
+ d += String::compose (
+ _("Content video is %1x%2"),
+ size_after_3d_split().width,
+ size_after_3d_split().height
+ );
+
+
+ double ratio = size_after_3d_split().ratio ();
+
+ if (sample_aspect_ratio ()) {
+ snprintf (buffer, sizeof(buffer), _(", pixel aspect ratio %.2f:1"), sample_aspect_ratio().get());
+ d += buffer;
+ ratio *= sample_aspect_ratio().get ();
+ }
+
+ snprintf (buffer, sizeof(buffer), _("\nDisplay aspect ratio %.2f:1"), ratio);
+ d += buffer;
+ }
+
+ if ((crop().left || crop().right || crop().top || crop().bottom) && size() != dcp::Size (0, 0)) {
+ dcp::Size cropped = size_after_crop ();
+ d += String::compose (
+ _("\nCropped to %1x%2"),
+ cropped.width, cropped.height
+ );
+
+ snprintf (buffer, sizeof(buffer), " (%.2f:1)", cropped.ratio());
+ d += buffer;
+ }
+
+ dcp::Size const container_size = film->frame_size ();
+ dcp::Size const scaled = scale().size (shared_from_this(), container_size, container_size);
+
+ if (scaled != size_after_crop ()) {
+ d += String::compose (
+ _("\nScaled to %1x%2"),
+ scaled.width, scaled.height
+ );
+
+ snprintf (buffer, sizeof(buffer), _(" (%.2f:1)"), scaled.ratio());
+ d += buffer;
+ }
+
+ if (scaled != container_size) {
+ d += String::compose (
+ _("\nPadded with black to fit container %1 (%2x%3)"),
+ film->container()->container_nickname (),
+ container_size.width, container_size.height
+ );
+
+ snprintf (buffer, sizeof(buffer), _(" (%.2f:1)"), container_size.ratio());
+ d += buffer;
+ }
+
+ if (_parent->video_frame_rate()) {
+ double const vfr = _parent->video_frame_rate().get ();
+
+ snprintf (buffer, sizeof(buffer), _("\nContent frame rate %.4f\n"), vfr);
+ d += buffer;
+
+ FrameRateChange frc (vfr, film->video_frame_rate ());
+ d += frc.description ();
+ }
+
+ return d;
+}
+
+void
+VideoContent::add_properties (list<UserProperty>& p) const
+{
+ p.push_back (UserProperty (UserProperty::VIDEO, _("Length"), length (), _("video frames")));
+ p.push_back (UserProperty (UserProperty::VIDEO, _("Size"), String::compose ("%1x%2", size().width, size().height)));
+}
+
+void
+VideoContent::set_length (Frame len)
+{
+ maybe_set (_length, len, ContentProperty::LENGTH);
+}
+
+void
+VideoContent::set_left_crop (int c)
+{
+ maybe_set (_crop.left, c, VideoContentProperty::CROP);
+}
+
+void
+VideoContent::set_right_crop (int c)
+{
+ maybe_set (_crop.right, c, VideoContentProperty::CROP);
+}
+
+void
+VideoContent::set_top_crop (int c)
+{
+ maybe_set (_crop.top, c, VideoContentProperty::CROP);
+}
+
+void
+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)
+{
+ maybe_set (_frame_type, t, VideoContentProperty::FRAME_TYPE);
+}
+
+void
+VideoContent::unset_colour_conversion ()
+{
+ maybe_set (_colour_conversion, boost::optional<ColourConversion> (), VideoContentProperty::COLOUR_CONVERSION);
+}
+
+void
+VideoContent::set_colour_conversion (ColourConversion c)
+{
+ maybe_set (_colour_conversion, c, VideoContentProperty::COLOUR_CONVERSION);
+}
+
+void
+VideoContent::set_fade_in (Frame t)
+{
+ maybe_set (_fade_in, t, VideoContentProperty::FADE_IN);
+}
+
+void
+VideoContent::set_fade_out (Frame t)
+{
+ maybe_set (_fade_out, t, VideoContentProperty::FADE_OUT);
+}
+
+void
+VideoContent::set_range (VideoRange r)
+{
+ maybe_set (_range, r, VideoContentProperty::RANGE);
+}
+
+void
+VideoContent::set_use (bool u)
+{
+ maybe_set (_use, u, VideoContentProperty::USE);
+}
+
+void
+VideoContent::take_settings_from (shared_ptr<const VideoContent> c)
+{
+ if (c->_colour_conversion) {
+ set_colour_conversion (c->_colour_conversion.get());
+ } else {
+ unset_colour_conversion ();
+ }
+ set_use (c->_use);
+ set_frame_type (c->_frame_type);
+ set_left_crop (c->_crop.left);
+ set_right_crop (c->_crop.right);
+ set_top_crop (c->_crop.top);
+ set_bottom_crop (c->_crop.bottom);
+ set_scale (c->_scale);
+ set_fade_in (c->_fade_in);
+ set_fade_out (c->_fade_out);
+}
+
+void
+VideoContent::modify_position (shared_ptr<const Film> film, DCPTime& pos) const
+{
+ pos = pos.round (film->video_frame_rate());
+}
+
+void
+VideoContent::modify_trim_start (ContentTime& trim) const
+{
+ if (_parent->video_frame_rate()) {
+ trim = trim.round (_parent->video_frame_rate().get());
+ }