+2014-03-04 Carl Hetherington <cth@carlh.net>
+
+ * Add option to disable all scaling of the input video.
+
2014-03-03 Carl Hetherington <cth@carlh.net>
* Fix rounding of timecodes in at least some cases (#323).
/*
- Copyright (C) 2013 Carl Hetherington <cth@carlh.net>
+ Copyright (C) 2013-2014 Carl Hetherington <cth@carlh.net>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
FFmpegContent::FFmpegContent (shared_ptr<const Film> f, shared_ptr<const cxml::Node> node, int version)
: Content (f, node)
- , VideoContent (f, node)
+ , VideoContent (f, node, version)
, AudioContent (f, node)
, SubtitleContent (f, node, version)
{
* AudioMapping XML changed.
* 6 -> 7
* Subtitle offset changed to subtitle y offset, and subtitle x offset added.
+ * 7 -> 8
+ * Use <Scale> tag in <VideoContent> rather than <Ratio>.
*/
-int const Film::current_state_version = 7;
+int const Film::current_state_version = 8;
/** Construct a Film object in a given directory.
*
#include "exceptions.h"
#include "scaler.h"
+#include "i18n.h"
+
using std::string;
using std::min;
using std::cout;
+using std::cerr;
using boost::shared_ptr;
using libdcp::Size;
*/
assert (aligned ());
+ assert (out_size.width >= inter_size.width);
+ assert (out_size.height >= inter_size.height);
+
+ /* Here's an image of out_size */
shared_ptr<Image> out (new Image (out_format, out_size, out_aligned));
out->make_black ();
-
- libdcp::Size cropped_size = crop.apply (size ());
+ /* Size of the image after any crop */
+ libdcp::Size const cropped_size = crop.apply (size ());
+
+ /* Scale context for a scale from cropped_size to inter_size */
struct SwsContext* scale_context = sws_getContext (
cropped_size.width, cropped_size.height, pixel_format(),
inter_size.width, inter_size.height, out_format,
scaler->ffmpeg_id (), 0, 0, 0
);
+ if (!scale_context) {
+ throw StringError (N_("Could not allocate SwsContext"));
+ }
+
+ /* Prepare input data pointers with crop */
uint8_t* scale_in_data[components()];
for (int c = 0; c < components(); ++c) {
scale_in_data[c] = data()[c] + int (rint (bytes_per_pixel(c) * crop.left)) + stride()[c] * (crop.top / line_factor(c));
}
+ /* Corner of the image within out_size */
Position<int> const corner ((out_size.width - inter_size.width) / 2, (out_size.height - inter_size.height) / 2);
- uint8_t* scale_out_data[components()];
- for (int c = 0; c < components(); ++c) {
+ uint8_t* scale_out_data[out->components()];
+ for (int c = 0; c < out->components(); ++c) {
scale_out_data[c] = out->data()[c] + int (rint (out->bytes_per_pixel(c) * corner.x)) + out->stride()[c] * corner.y;
}
/*
- Copyright (C) 2013 Carl Hetherington <cth@carlh.net>
+ Copyright (C) 2013-2014 Carl Hetherington <cth@carlh.net>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
}
-ImageContent::ImageContent (shared_ptr<const Film> f, shared_ptr<const cxml::Node> node, int)
+ImageContent::ImageContent (shared_ptr<const Film> f, shared_ptr<const cxml::Node> node, int version)
: Content (f, node)
- , VideoContent (f, node)
+ , VideoContent (f, node, version)
{
}
/*
- Copyright (C) 2013 Carl Hetherington <cth@carlh.net>
+ Copyright (C) 2013-2014 Carl Hetherington <cth@carlh.net>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
}
Time const time = content->position() + relative_time + extra - content->trim_start ();
- float const ratio = content->ratio() ? content->ratio()->ratio() : content->video_size_after_crop().ratio();
- libdcp::Size const image_size = fit_ratio_within (ratio, _video_container_size);
+ libdcp::Size const image_size = content->scale().size (content, _video_container_size);
shared_ptr<PlayerImage> pi (
new PlayerImage (
Changed (frequent);
} else if (
- property == VideoContentProperty::VIDEO_CROP || property == VideoContentProperty::VIDEO_RATIO ||
+ property == VideoContentProperty::VIDEO_CROP || property == VideoContentProperty::VIDEO_SCALE ||
property == VideoContentProperty::VIDEO_FRAME_RATE
) {
#include "ratio.h"
#include "job.h"
#include "cross.h"
+#include "video_content.h"
#ifdef DCPOMATIC_WINDOWS
#include "stack.hpp"
#endif
libdcp::init ();
Ratio::setup_ratios ();
+ VideoContentScale::setup_scales ();
DCPContentType::setup_dcp_content_types ();
Scaler::setup_scalers ();
Filter::setup_filters ();
/*
- Copyright (C) 2013 Carl Hetherington <cth@carlh.net>
+ Copyright (C) 2013-2014 Carl Hetherington <cth@carlh.net>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
#include <libdcp/colour_matrix.h>
#include "video_content.h"
#include "video_examiner.h"
-#include "ratio.h"
#include "compose.hpp"
+#include "ratio.h"
#include "config.h"
#include "colour_conversion.h"
#include "util.h"
int const VideoContentProperty::VIDEO_FRAME_RATE = 1;
int const VideoContentProperty::VIDEO_FRAME_TYPE = 2;
int const VideoContentProperty::VIDEO_CROP = 3;
-int const VideoContentProperty::VIDEO_RATIO = 4;
+int const VideoContentProperty::VIDEO_SCALE = 4;
int const VideoContentProperty::COLOUR_CONVERSION = 5;
using std::string;
using boost::optional;
using boost::dynamic_pointer_cast;
+vector<VideoContentScale> VideoContentScale::_scales;
+
VideoContent::VideoContent (shared_ptr<const Film> f)
: Content (f)
, _video_length (0)
, _video_frame_rate (0)
, _video_frame_type (VIDEO_FRAME_TYPE_2D)
- , _ratio (Ratio::from_id ("185"))
+ , _scale (Ratio::from_id ("185"))
{
setup_default_colour_conversion ();
}
, _video_length (len)
, _video_frame_rate (0)
, _video_frame_type (VIDEO_FRAME_TYPE_2D)
- , _ratio (Ratio::from_id ("185"))
+ , _scale (Ratio::from_id ("185"))
{
setup_default_colour_conversion ();
}
, _video_length (0)
, _video_frame_rate (0)
, _video_frame_type (VIDEO_FRAME_TYPE_2D)
- , _ratio (Ratio::from_id ("185"))
+ , _scale (Ratio::from_id ("185"))
{
setup_default_colour_conversion ();
}
-VideoContent::VideoContent (shared_ptr<const Film> f, shared_ptr<const cxml::Node> node)
+VideoContent::VideoContent (shared_ptr<const Film> f, shared_ptr<const cxml::Node> node, int version)
: Content (f, node)
- , _ratio (0)
{
_video_length = node->number_child<VideoContent::Frame> ("VideoLength");
_video_size.width = node->number_child<int> ("VideoWidth");
_crop.right = node->number_child<int> ("RightCrop");
_crop.top = node->number_child<int> ("TopCrop");
_crop.bottom = node->number_child<int> ("BottomCrop");
- optional<string> r = node->optional_string_child ("Ratio");
- if (r) {
- _ratio = Ratio::from_id (r.get ());
+
+ if (version <= 7) {
+ optional<string> r = node->optional_string_child ("Ratio");
+ if (r) {
+ _scale = VideoContentScale (Ratio::from_id (r.get ()));
+ }
+ } else {
+ _scale = VideoContentScale (node->node_child ("Scale"));
}
+
_colour_conversion = ColourConversion (node->node_child ("ColourConversion"));
}
throw JoinError (_("Content to be joined must have the same crop."));
}
- if (vc->ratio() != ref->ratio()) {
- throw JoinError (_("Content to be joined must have the same ratio."));
+ if (vc->scale() != ref->scale()) {
+ throw JoinError (_("Content to be joined must have the same scale setting."));
}
if (vc->colour_conversion() != ref->colour_conversion()) {
_video_frame_rate = ref->video_frame_rate ();
_video_frame_type = ref->video_frame_type ();
_crop = ref->crop ();
- _ratio = ref->ratio ();
+ _scale = ref->scale ();
_colour_conversion = ref->colour_conversion ();
}
node->add_child("RightCrop")->add_child_text (boost::lexical_cast<string> (_crop.right));
node->add_child("TopCrop")->add_child_text (boost::lexical_cast<string> (_crop.top));
node->add_child("BottomCrop")->add_child_text (boost::lexical_cast<string> (_crop.bottom));
- if (_ratio) {
- node->add_child("Ratio")->add_child_text (_ratio->id ());
- }
+ _scale.as_xml (node->add_child("Scale"));
_colour_conversion.as_xml (node->add_child("ColourConversion"));
}
}
void
-VideoContent::set_ratio (Ratio const * r)
+VideoContent::set_scale (VideoContentScale s)
{
{
boost::mutex::scoped_lock lm (_mutex);
- if (_ratio == r) {
+ if (_scale == s) {
return;
}
- _ratio = r;
+ _scale = s;
}
- signal_changed (VideoContentProperty::VIDEO_RATIO);
+ signal_changed (VideoContentProperty::VIDEO_SCALE);
}
/** @return string which includes everything about how this content looks */
<< "_" << crop().right
<< "_" << crop().top
<< "_" << crop().bottom
+ << "_" << scale().id()
<< "_" << colour_conversion().identifier ();
- if (ratio()) {
- s << "_" << ratio()->id ();
- }
-
return s.str ();
}
*/
return t * film->video_frame_rate() / (frc.factor() * TIME_HZ);
}
+
+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
+{
+ stringstream s;
+
+ if (_ratio) {
+ s << _ratio->id () << "_";
+ } else {
+ s << (_scale ? "S1" : "S0");
+ }
+
+ return s.str ();
+}
+
+string
+VideoContentScale::name () const
+{
+ if (_ratio) {
+ return _ratio->nickname ();
+ }
+
+ if (_scale) {
+ return _("No stretch");
+ }
+
+ return _("No scale");
+}
+
+libdcp::Size
+VideoContentScale::size (shared_ptr<const VideoContent> c, libdcp::Size container) const
+{
+ if (_ratio) {
+ return fit_ratio_within (_ratio->ratio (), container);
+ }
+
+ /* Force scale if the container is smaller than the content's image */
+ if (_scale || container.width < c->video_size().width || container.height < c->video_size().height) {
+ return fit_ratio_within (c->video_size().ratio (), container);
+ }
+
+ return c->video_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());
+}
static int const VIDEO_FRAME_RATE;
static int const VIDEO_FRAME_TYPE;
static int const VIDEO_CROP;
- static int const VIDEO_RATIO;
+ static int const VIDEO_SCALE;
static int const COLOUR_CONVERSION;
};
+class VideoContentScale
+{
+public:
+ VideoContentScale ();
+ VideoContentScale (Ratio const *);
+ VideoContentScale (bool);
+ VideoContentScale (boost::shared_ptr<cxml::Node>);
+
+ libdcp::Size size (boost::shared_ptr<const VideoContent>, libdcp::Size) 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:
+ Ratio const * _ratio;
+ bool _scale;
+
+ static std::vector<VideoContentScale> _scales;
+};
+
+bool operator== (VideoContentScale const & a, VideoContentScale const & b);
+bool operator!= (VideoContentScale const & a, VideoContentScale const & b);
+
class VideoContent : public virtual Content
{
public:
VideoContent (boost::shared_ptr<const Film>);
VideoContent (boost::shared_ptr<const Film>, Time, VideoContent::Frame);
VideoContent (boost::shared_ptr<const Film>, boost::filesystem::path);
- VideoContent (boost::shared_ptr<const Film>, boost::shared_ptr<const cxml::Node>);
+ VideoContent (boost::shared_ptr<const Film>, boost::shared_ptr<const cxml::Node>, int);
VideoContent (boost::shared_ptr<const Film>, std::vector<boost::shared_ptr<Content> >);
void as_xml (xmlpp::Node *) const;
void set_top_crop (int);
void set_bottom_crop (int);
+ void set_scale (VideoContentScale);
void set_colour_conversion (ColourConversion);
-
+
VideoFrameType video_frame_type () const {
boost::mutex::scoped_lock lm (_mutex);
return _video_frame_type;
boost::mutex::scoped_lock lm (_mutex);
return _crop.bottom;
}
-
- void set_ratio (Ratio const *);
- /** @return ratio to scale to, or 0 if the content's own ratio should be preserved. */
- Ratio const * ratio () const {
+ /** @return Description of how to scale this content (if indeed it should be scaled) */
+ VideoContentScale scale () const {
boost::mutex::scoped_lock lm (_mutex);
- return _ratio;
+ return _scale;
}
ColourConversion colour_conversion () const {
libdcp::Size _video_size;
VideoFrameType _video_frame_type;
Crop _crop;
- Ratio const * _ratio;
+ VideoContentScale _scale;
ColourConversion _colour_conversion;
};
shared_ptr<Content> c = content_factory (film, argv[i]);
shared_ptr<VideoContent> vc = dynamic_pointer_cast<VideoContent> (c);
if (vc) {
- vc->set_ratio (content_ratio);
+ vc->set_scale (VideoContentScale (content_ratio));
}
film->examine_and_add_content (c);
}
/*
- Copyright (C) 2012-2013 Carl Hetherington <cth@carlh.net>
+ Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
*/
#include <wx/spinctrl.h>
-#include "lib/ratio.h"
#include "lib/filter.h"
#include "lib/ffmpeg_content.h"
#include "lib/colour_conversion.h"
#include "lib/config.h"
#include "lib/util.h"
+#include "lib/ratio.h"
#include "filter_dialog.h"
#include "video_panel.h"
#include "wx_util.h"
using boost::bind;
using boost::optional;
-static Ratio const *
-index_to_ratio (int n)
+static VideoContentScale
+index_to_scale (int n)
{
+ vector<VideoContentScale> scales = VideoContentScale::all ();
assert (n >= 0);
-
- vector<Ratio const *> ratios = Ratio::all ();
- if (n >= int (ratios.size ())) {
- return 0;
- }
-
- return ratios[n];
+ assert (n < int (scales.size ()));
+ return scales[n];
}
static int
-ratio_to_index (Ratio const * r)
+scale_to_index (VideoContentScale scale)
{
- vector<Ratio const *> ratios = Ratio::all ();
- size_t i = 0;
- while (i < ratios.size() && ratios[i] != r) {
- ++i;
+ vector<VideoContentScale> scales = VideoContentScale::all ();
+ for (size_t i = 0; i < scales.size(); ++i) {
+ if (scales[i] == scale) {
+ return i;
+ }
}
- return i;
+ assert (false);
}
VideoPanel::VideoPanel (FilmEditor* e)
++r;
add_label_to_grid_bag_sizer (grid, this, _("Scale to"), true, wxGBPosition (r, 0));
- _ratio = new ContentChoice<VideoContent, Ratio const *> (
+ _scale = new ContentChoice<VideoContent, VideoContentScale> (
this,
new wxChoice (this, wxID_ANY),
- VideoContentProperty::VIDEO_RATIO,
- boost::mem_fn (&VideoContent::ratio),
- boost::mem_fn (&VideoContent::set_ratio),
- &index_to_ratio,
- &ratio_to_index
+ VideoContentProperty::VIDEO_SCALE,
+ boost::mem_fn (&VideoContent::scale),
+ boost::mem_fn (&VideoContent::set_scale),
+ &index_to_scale,
+ &scale_to_index
);
- _ratio->add (grid, wxGBPosition (r, 1));
+ _scale->add (grid, wxGBPosition (r, 1));
++r;
{
_right_crop->wrapped()->SetRange (0, 1024);
_bottom_crop->wrapped()->SetRange (0, 1024);
- vector<Ratio const *> ratios = Ratio::all ();
- _ratio->wrapped()->Clear ();
- for (vector<Ratio const *>::iterator i = ratios.begin(); i != ratios.end(); ++i) {
- _ratio->wrapped()->Append (std_to_wx ((*i)->nickname ()));
+ vector<VideoContentScale> scales = VideoContentScale::all ();
+ _scale->wrapped()->Clear ();
+ for (vector<VideoContentScale>::iterator i = scales.begin(); i != scales.end(); ++i) {
+ _scale->wrapped()->Append (std_to_wx (i->name ()));
}
- _ratio->wrapped()->Append (_("No stretch"));
_frame_type->wrapped()->Append (_("2D"));
_frame_type->wrapped()->Append (_("3D left/right"));
setup_description ();
} else if (property == VideoContentProperty::VIDEO_CROP) {
setup_description ();
- } else if (property == VideoContentProperty::VIDEO_RATIO) {
+ } else if (property == VideoContentProperty::VIDEO_SCALE) {
setup_description ();
} else if (property == VideoContentProperty::VIDEO_FRAME_RATE) {
setup_description ();
++lines;
}
- Ratio const * ratio = vcs->ratio ();
- libdcp::Size container_size = fit_ratio_within (_editor->film()->container()->ratio (), _editor->film()->full_frame ());
- float const ratio_value = ratio ? ratio->ratio() : vcs->video_size_after_crop().ratio ();
+ libdcp::Size const container_size = fit_ratio_within (_editor->film()->container()->ratio (), _editor->film()->full_frame ());
+ libdcp::Size const scaled = vcs->scale().size (vcs, container_size);
- /* We have a specified ratio to scale to */
- libdcp::Size const scaled = fit_ratio_within (ratio_value, container_size);
-
- d << wxString::Format (
- _("Scaled to %dx%d (%.2f:1)\n"),
- scaled.width, scaled.height,
- scaled.ratio ()
- );
- ++lines;
+ if (scaled != vcs->video_size_after_crop ()) {
+ d << wxString::Format (
+ _("Scaled to %dx%d (%.2f:1)\n"),
+ scaled.width, scaled.height,
+ scaled.ratio ()
+ );
+ ++lines;
+ }
if (scaled != container_size) {
d << wxString::Format (
_top_crop->set_content (sel);
_bottom_crop->set_content (sel);
_frame_type->set_content (sel);
- _ratio->set_content (sel);
+ _scale->set_content (sel);
/* Things that are only allowed with single selections */
_filters_button->Enable (single);
void setup_description ();
- ContentChoice<VideoContent, VideoFrameType>* _frame_type;
- ContentSpinCtrl<VideoContent>* _left_crop;
- ContentSpinCtrl<VideoContent>* _right_crop;
- ContentSpinCtrl<VideoContent>* _top_crop;
- ContentSpinCtrl<VideoContent>* _bottom_crop;
- ContentChoice<VideoContent, Ratio const *>* _ratio;
+ ContentChoice<VideoContent, VideoFrameType>* _frame_type;
+ ContentSpinCtrl<VideoContent>* _left_crop;
+ ContentSpinCtrl<VideoContent>* _right_crop;
+ ContentSpinCtrl<VideoContent>* _top_crop;
+ ContentSpinCtrl<VideoContent>* _bottom_crop;
+ ContentChoice<VideoContent, VideoContentScale>* _scale;
wxStaticText* _description;
wxStaticText* _filters;
wxButton* _filters_button;
shared_ptr<Film> film = new_test_film ("4k_test");
film->set_name ("4k_test");
shared_ptr<FFmpegContent> c (new FFmpegContent (film, "test/data/test.mp4"));
- c->set_ratio (Ratio::from_id ("185"));
+ c->set_scale (VideoContentScale (Ratio::from_id ("185")));
film->set_resolution (RESOLUTION_4K);
film->set_dcp_content_type (DCPContentType::from_dci_name ("FTR"));
film->set_container (Ratio::from_id ("185"));
film->set_container (Ratio::from_id ("185"));
film->set_sequence_video (false);
shared_ptr<ImageContent> contentA (new ImageContent (film, "test/data/simple_testcard_640x480.png"));
- contentA->set_ratio (Ratio::from_id ("185"));
+ contentA->set_scale (VideoContentScale (Ratio::from_id ("185")));
shared_ptr<ImageContent> contentB (new ImageContent (film, "test/data/simple_testcard_640x480.png"));
- contentB->set_ratio (Ratio::from_id ("185"));
+ contentB->set_scale (VideoContentScale (Ratio::from_id ("185")));
film->examine_and_add_content (contentA);
film->examine_and_add_content (contentB);
shared_ptr<Film> film = new_test_film ("ffmpeg_audio_test");
film->set_name ("ffmpeg_audio_test");
shared_ptr<FFmpegContent> c (new FFmpegContent (film, "test/data/staircase.mov"));
- c->set_ratio (Ratio::from_id ("185"));
+ c->set_scale (VideoContentScale (Ratio::from_id ("185")));
film->examine_and_add_content (c);
wait_for_jobs ();
shared_ptr<Film> film = new_test_film ("ffmpeg_dcp_test");
film->set_name ("test_film2");
shared_ptr<FFmpegContent> c (new FFmpegContent (film, "test/data/test.mp4"));
- c->set_ratio (Ratio::from_id ("185"));
+ c->set_scale (VideoContentScale (Ratio::from_id ("185")));
film->examine_and_add_content (c);
wait_for_jobs ();
static void scaling_test_for (shared_ptr<Film> film, shared_ptr<VideoContent> content, string image, string container)
{
- content->set_ratio (Ratio::from_id (image));
+ content->set_scale (VideoContentScale (Ratio::from_id (image)));
film->set_container (Ratio::from_id (container));
film->make_dcp ();
shared_ptr<Film> film = new_test_film ("threed_test");
film->set_name ("test_film2");
shared_ptr<FFmpegContent> c (new FFmpegContent (film, "test/data/test.mp4"));
- c->set_ratio (Ratio::from_id ("185"));
+ c->set_scale (VideoContentScale (Ratio::from_id ("185")));
c->set_video_frame_type (VIDEO_FRAME_TYPE_3D_LEFT_RIGHT);
film->examine_and_add_content (c);