2 Copyright (C) 2013-2014 Carl Hetherington <cth@carlh.net>
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 #include <libcxml/cxml.h>
22 #include <libdcp/colour_matrix.h>
23 #include <libdcp/raw_convert.h>
24 #include "video_content.h"
25 #include "video_examiner.h"
26 #include "compose.hpp"
29 #include "colour_conversion.h"
32 #include "exceptions.h"
33 #include "frame_rate_change.h"
34 #include "safe_stringstream.h"
38 int const VideoContentProperty::VIDEO_SIZE = 0;
39 int const VideoContentProperty::VIDEO_FRAME_RATE = 1;
40 int const VideoContentProperty::VIDEO_FRAME_TYPE = 2;
41 int const VideoContentProperty::VIDEO_CROP = 3;
42 int const VideoContentProperty::VIDEO_SCALE = 4;
43 int const VideoContentProperty::COLOUR_CONVERSION = 5;
46 using std::setprecision;
51 using boost::shared_ptr;
52 using boost::optional;
53 using boost::dynamic_pointer_cast;
54 using libdcp::raw_convert;
56 VideoContent::VideoContent (shared_ptr<const Film> f)
59 , _original_video_frame_rate (0)
60 , _video_frame_rate (0)
61 , _video_frame_type (VIDEO_FRAME_TYPE_2D)
62 , _scale (Config::instance()->default_scale ())
64 setup_default_colour_conversion ();
67 VideoContent::VideoContent (shared_ptr<const Film> f, Time s, VideoContent::Frame len)
70 , _original_video_frame_rate (0)
71 , _video_frame_rate (0)
72 , _video_frame_type (VIDEO_FRAME_TYPE_2D)
73 , _scale (Config::instance()->default_scale ())
75 setup_default_colour_conversion ();
78 VideoContent::VideoContent (shared_ptr<const Film> f, boost::filesystem::path p)
81 , _original_video_frame_rate (0)
82 , _video_frame_rate (0)
83 , _video_frame_type (VIDEO_FRAME_TYPE_2D)
84 , _scale (Config::instance()->default_scale ())
86 setup_default_colour_conversion ();
89 VideoContent::VideoContent (shared_ptr<const Film> f, shared_ptr<const cxml::Node> node, int version)
92 _video_length = node->number_child<VideoContent::Frame> ("VideoLength");
93 _video_size.width = node->number_child<int> ("VideoWidth");
94 _video_size.height = node->number_child<int> ("VideoHeight");
95 _video_frame_rate = node->number_child<float> ("VideoFrameRate");
96 _original_video_frame_rate = node->optional_number_child<float> ("OriginalVideoFrameRate").get_value_or (_video_frame_rate);
97 _video_frame_type = static_cast<VideoFrameType> (node->number_child<int> ("VideoFrameType"));
98 _crop.left = node->number_child<int> ("LeftCrop");
99 _crop.right = node->number_child<int> ("RightCrop");
100 _crop.top = node->number_child<int> ("TopCrop");
101 _crop.bottom = node->number_child<int> ("BottomCrop");
104 optional<string> r = node->optional_string_child ("Ratio");
106 _scale = VideoContentScale (Ratio::from_id (r.get ()));
109 _scale = VideoContentScale (node->node_child ("Scale"));
112 _colour_conversion = ColourConversion (node->node_child ("ColourConversion"));
115 VideoContent::VideoContent (shared_ptr<const Film> f, vector<shared_ptr<Content> > c)
119 shared_ptr<VideoContent> ref = dynamic_pointer_cast<VideoContent> (c[0]);
122 for (size_t i = 0; i < c.size(); ++i) {
123 shared_ptr<VideoContent> vc = dynamic_pointer_cast<VideoContent> (c[i]);
125 if (vc->video_size() != ref->video_size()) {
126 throw JoinError (_("Content to be joined must have the same picture size."));
129 if (vc->video_frame_rate() != ref->video_frame_rate()) {
130 throw JoinError (_("Content to be joined must have the same video frame rate."));
133 if (vc->video_frame_type() != ref->video_frame_type()) {
134 throw JoinError (_("Content to be joined must have the same video frame type."));
137 if (vc->crop() != ref->crop()) {
138 throw JoinError (_("Content to be joined must have the same crop."));
141 if (vc->scale() != ref->scale()) {
142 throw JoinError (_("Content to be joined must have the same scale setting."));
145 if (vc->colour_conversion() != ref->colour_conversion()) {
146 throw JoinError (_("Content to be joined must have the same colour conversion."));
149 _video_length += vc->video_length ();
152 _video_size = ref->video_size ();
153 _original_video_frame_rate = ref->original_video_frame_rate ();
154 _video_frame_rate = ref->video_frame_rate ();
155 _video_frame_type = ref->video_frame_type ();
156 _crop = ref->crop ();
157 _scale = ref->scale ();
158 _colour_conversion = ref->colour_conversion ();
162 VideoContent::as_xml (xmlpp::Node* node) const
164 boost::mutex::scoped_lock lm (_mutex);
165 node->add_child("VideoLength")->add_child_text (raw_convert<string> (_video_length));
166 node->add_child("VideoWidth")->add_child_text (raw_convert<string> (_video_size.width));
167 node->add_child("VideoHeight")->add_child_text (raw_convert<string> (_video_size.height));
168 node->add_child("VideoFrameRate")->add_child_text (raw_convert<string> (_video_frame_rate));
169 node->add_child("OriginalVideoFrameRate")->add_child_text (raw_convert<string> (_original_video_frame_rate));
170 node->add_child("VideoFrameType")->add_child_text (raw_convert<string> (static_cast<int> (_video_frame_type)));
172 _scale.as_xml (node->add_child("Scale"));
173 _colour_conversion.as_xml (node->add_child("ColourConversion"));
177 VideoContent::setup_default_colour_conversion ()
179 _colour_conversion = PresetColourConversion (_("sRGB"), 2.4, true, libdcp::colour_matrix::srgb_to_xyz, 2.6).conversion;
183 VideoContent::take_from_video_examiner (shared_ptr<VideoExaminer> d)
185 /* These examiner calls could call other content methods which take a lock on the mutex */
186 libdcp::Size const vs = d->video_size ();
187 float const vfr = d->video_frame_rate ();
190 boost::mutex::scoped_lock lm (_mutex);
192 _video_frame_rate = vfr;
193 _original_video_frame_rate = vfr;
196 signal_changed (VideoContentProperty::VIDEO_SIZE);
197 signal_changed (VideoContentProperty::VIDEO_FRAME_RATE);
202 VideoContent::information () const
204 if (video_size().width == 0 || video_size().height == 0) {
210 s << String::compose (
211 _("%1x%2 pixels (%3:1)"),
214 setprecision (3), video_size().ratio ()
221 VideoContent::set_left_crop (int c)
224 boost::mutex::scoped_lock lm (_mutex);
226 if (_crop.left == c) {
233 signal_changed (VideoContentProperty::VIDEO_CROP);
237 VideoContent::set_right_crop (int c)
240 boost::mutex::scoped_lock lm (_mutex);
241 if (_crop.right == c) {
248 signal_changed (VideoContentProperty::VIDEO_CROP);
252 VideoContent::set_top_crop (int c)
255 boost::mutex::scoped_lock lm (_mutex);
256 if (_crop.top == c) {
263 signal_changed (VideoContentProperty::VIDEO_CROP);
267 VideoContent::set_bottom_crop (int c)
270 boost::mutex::scoped_lock lm (_mutex);
271 if (_crop.bottom == c) {
278 signal_changed (VideoContentProperty::VIDEO_CROP);
282 VideoContent::set_scale (VideoContentScale s)
285 boost::mutex::scoped_lock lm (_mutex);
293 signal_changed (VideoContentProperty::VIDEO_SCALE);
296 /** @return string which includes everything about how this content looks */
298 VideoContent::identifier () const
301 s << Content::identifier()
302 << "_" << crop().left
303 << "_" << crop().right
305 << "_" << crop().bottom
306 << "_" << scale().id()
307 << "_" << colour_conversion().identifier ();
313 VideoContent::set_video_frame_type (VideoFrameType t)
316 boost::mutex::scoped_lock lm (_mutex);
317 _video_frame_type = t;
320 signal_changed (VideoContentProperty::VIDEO_FRAME_TYPE);
324 VideoContent::technical_summary () const
326 return String::compose (
327 "video: length %1, size %2x%3, rate %4",
328 video_length_after_3d_combine(), video_size().width, video_size().height, video_frame_rate()
333 VideoContent::video_size_after_3d_split () const
335 libdcp::Size const s = video_size ();
336 switch (video_frame_type ()) {
337 case VIDEO_FRAME_TYPE_2D:
338 case VIDEO_FRAME_TYPE_3D_ALTERNATE:
339 case VIDEO_FRAME_TYPE_3D_LEFT:
340 case VIDEO_FRAME_TYPE_3D_RIGHT:
342 case VIDEO_FRAME_TYPE_3D_LEFT_RIGHT:
343 return libdcp::Size (s.width / 2, s.height);
344 case VIDEO_FRAME_TYPE_3D_TOP_BOTTOM:
345 return libdcp::Size (s.width, s.height / 2);
352 VideoContent::set_colour_conversion (ColourConversion c)
355 boost::mutex::scoped_lock lm (_mutex);
356 _colour_conversion = c;
359 signal_changed (VideoContentProperty::COLOUR_CONVERSION);
362 /** @return Video size after 3D split and crop */
364 VideoContent::video_size_after_crop () const
366 return crop().apply (video_size_after_3d_split ());
369 /** @param t A time offset from the start of this piece of content.
370 * @return Corresponding frame index.
373 VideoContent::time_to_content_video_frames (Time t) const
375 shared_ptr<const Film> film = _film.lock ();
378 FrameRateChange frc (video_frame_rate(), film->video_frame_rate());
380 /* Here we are converting from time (in the DCP) to a frame number in the content.
381 Hence we need to use the DCP's frame rate and the double/skip correction, not
384 return t * film->video_frame_rate() / (frc.factor() * TIME_HZ);
388 VideoContent::scale_and_crop_to_fit_width ()
390 shared_ptr<const Film> film = _film.lock ();
393 set_scale (VideoContentScale (film->container ()));
395 int const crop = max (0, int (video_size().height - double (film->frame_size().height) * video_size().width / film->frame_size().width));
396 set_top_crop (crop / 2);
397 set_bottom_crop (crop / 2);
401 VideoContent::scale_and_crop_to_fit_height ()
403 shared_ptr<const Film> film = _film.lock ();
406 set_scale (VideoContentScale (film->container ()));
408 int const crop = max (0, int (video_size().width - double (film->frame_size().width) * video_size().height / film->frame_size().height));
409 set_left_crop (crop / 2);
410 set_right_crop (crop / 2);
414 VideoContent::set_video_frame_rate (float r)
417 boost::mutex::scoped_lock lm (_mutex);
418 if (_video_frame_rate == r) {
422 _video_frame_rate = r;
425 signal_changed (VideoContentProperty::VIDEO_FRAME_RATE);