Partial hacks to use of libdcp 1.0.
[dcpomatic.git] / src / lib / video_content.cc
index 7eca53c3de8de202aad5c38d5f6c5e934a06e3aa..80515ca4d8ed87ff99f5e154ce2f68717f240ddb 100644 (file)
 
 #include <iomanip>
 #include <libcxml/cxml.h>
+#include <libdcp/colour_matrix.h>
 #include "video_content.h"
 #include "video_examiner.h"
 #include "ratio.h"
 #include "compose.hpp"
+#include "config.h"
+#include "colour_conversion.h"
+#include "util.h"
+#include "film.h"
+#include "exceptions.h"
 
 #include "i18n.h"
 
-int const VideoContentProperty::VIDEO_SIZE      = 0;
-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_SIZE       = 0;
+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::COLOUR_CONVERSION = 5;
 
 using std::string;
 using std::stringstream;
 using std::setprecision;
+using std::cout;
+using std::vector;
 using boost::shared_ptr;
 using boost::lexical_cast;
 using boost::optional;
+using boost::dynamic_pointer_cast;
 
-VideoContent::VideoContent (shared_ptr<const Film> f, Time s, VideoContent::Frame len)
+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"))
+{
+       setup_default_colour_conversion ();
+}
+
+VideoContent::VideoContent (shared_ptr<const Film> f, DCPTime s, VideoFrame len)
        : Content (f, s)
        , _video_length (len)
        , _video_frame_rate (0)
        , _video_frame_type (VIDEO_FRAME_TYPE_2D)
        , _ratio (Ratio::from_id ("185"))
 {
-
+       setup_default_colour_conversion ();
 }
 
 VideoContent::VideoContent (shared_ptr<const Film> f, boost::filesystem::path p)
@@ -56,13 +76,14 @@ VideoContent::VideoContent (shared_ptr<const Film> f, boost::filesystem::path p)
        , _video_frame_type (VIDEO_FRAME_TYPE_2D)
        , _ratio (Ratio::from_id ("185"))
 {
-
+       setup_default_colour_conversion ();
 }
 
 VideoContent::VideoContent (shared_ptr<const Film> f, shared_ptr<const cxml::Node> node)
        : Content (f, node)
+       , _ratio (0)
 {
-       _video_length = node->number_child<VideoContent::Frame> ("VideoLength");
+       _video_length = node->number_child<VideoFrame> ("VideoLength");
        _video_size.width = node->number_child<int> ("VideoWidth");
        _video_size.height = node->number_child<int> ("VideoHeight");
        _video_frame_rate = node->number_child<float> ("VideoFrameRate");
@@ -75,6 +96,52 @@ VideoContent::VideoContent (shared_ptr<const Film> f, shared_ptr<const cxml::Nod
        if (r) {
                _ratio = Ratio::from_id (r.get ());
        }
+       _colour_conversion = ColourConversion (node->node_child ("ColourConversion"));
+}
+
+VideoContent::VideoContent (shared_ptr<const Film> f, vector<shared_ptr<Content> > c)
+       : Content (f, c)
+       , _video_length (0)
+{
+       shared_ptr<VideoContent> ref = dynamic_pointer_cast<VideoContent> (c[0]);
+       assert (ref);
+
+       for (size_t i = 0; i < c.size(); ++i) {
+               shared_ptr<VideoContent> vc = dynamic_pointer_cast<VideoContent> (c[i]);
+
+               if (vc->video_size() != ref->video_size()) {
+                       throw JoinError (_("Content to be joined must have the same picture size."));
+               }
+
+               if (vc->video_frame_rate() != ref->video_frame_rate()) {
+                       throw JoinError (_("Content to be joined must have the same video frame rate."));
+               }
+
+               if (vc->video_frame_type() != ref->video_frame_type()) {
+                       throw JoinError (_("Content to be joined must have the same video frame type."));
+               }
+
+               if (vc->crop() != ref->crop()) {
+                       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->colour_conversion() != ref->colour_conversion()) {
+                       throw JoinError (_("Content to be joined must have the same colour conversion."));
+               }
+
+               _video_length += vc->video_length ();
+       }
+
+       _video_size = ref->video_size ();
+       _video_frame_rate = ref->video_frame_rate ();
+       _video_frame_type = ref->video_frame_type ();
+       _crop = ref->crop ();
+       _ratio = ref->ratio ();
+       _colour_conversion = ref->colour_conversion ();
 }
 
 void
@@ -93,13 +160,20 @@ VideoContent::as_xml (xmlpp::Node* node) const
        if (_ratio) {
                node->add_child("Ratio")->add_child_text (_ratio->id ());
        }
+       _colour_conversion.as_xml (node->add_child("ColourConversion"));
+}
+
+void
+VideoContent::setup_default_colour_conversion ()
+{
+       _colour_conversion = PresetColourConversion (_("sRGB"), 2.4, true, dcp::colour_matrix::srgb_to_xyz, 2.6).conversion;
 }
 
 void
 VideoContent::take_from_video_examiner (shared_ptr<VideoExaminer> d)
 {
        /* These examiner calls could call other content methods which take a lock on the mutex */
-       libdcp::Size const vs = d->video_size ();
+       dcp::Size const vs = d->video_size ();
        float const vfr = d->video_frame_rate ();
        
        {
@@ -126,7 +200,7 @@ VideoContent::information () const
                _("%1x%2 pixels (%3:1)"),
                video_size().width,
                video_size().height,
-               setprecision (3), float (video_size().width) / video_size().height
+               setprecision (3), video_size().ratio ()
                );
        
        return s.str ();
@@ -213,11 +287,12 @@ string
 VideoContent::identifier () const
 {
        stringstream s;
-       s << Content::digest()
+       s << Content::identifier()
          << "_" << crop().left
          << "_" << crop().right
          << "_" << crop().top
-         << "_" << crop().bottom;
+         << "_" << crop().bottom
+         << "_" << colour_conversion().identifier ();
 
        if (ratio()) {
                s << "_" << ratio()->id ();
@@ -231,8 +306,68 @@ VideoContent::set_video_frame_type (VideoFrameType t)
 {
        {
                boost::mutex::scoped_lock lm (_mutex);
-               _video_frame_rate = t;
+               _video_frame_type = t;
        }
 
        signal_changed (VideoContentProperty::VIDEO_FRAME_TYPE);
 }
+
+string
+VideoContent::technical_summary () const
+{
+       return String::compose ("video: length %1, size %2x%3, rate %4", video_length(), video_size().width, video_size().height, video_frame_rate());
+}
+
+dcp::Size
+VideoContent::video_size_after_3d_split () const
+{
+       dcp::Size const s = video_size ();
+       switch (video_frame_type ()) {
+       case VIDEO_FRAME_TYPE_2D:
+               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);
+       }
+
+       assert (false);
+}
+
+void
+VideoContent::set_colour_conversion (ColourConversion c)
+{
+       {
+               boost::mutex::scoped_lock lm (_mutex);
+               _colour_conversion = c;
+       }
+
+       signal_changed (VideoContentProperty::COLOUR_CONVERSION);
+}
+
+/** @return Video size after 3D split and crop */
+dcp::Size
+VideoContent::video_size_after_crop () const
+{
+       return crop().apply (video_size_after_3d_split ());
+}
+
+/** @param t A time offset from the start of this piece of content.
+ *  @return Corresponding frame index, rounded up so that the frame index
+ *  is that of the next complete frame which starts after `t'.
+ */
+VideoFrame
+VideoContent::time_to_content_video_frames (DCPTime t) const
+{
+       shared_ptr<const Film> film = _film.lock ();
+       assert (film);
+       
+       /* Here we are converting from time (in the DCP) to a frame number in the content.
+          Hence we need to use the DCP's frame rate and the double/skip correction, not
+          the source's rate; source rate will be equal to DCP rate if we ignore
+          double/skip.  There's no need to call Film::active_frame_rate_change() here
+          as we know that we are it (since we're video).
+       */
+       FrameRateChange frc (video_frame_rate(), film->video_frame_rate());
+       return ceil (t * film->video_frame_rate() / (frc.factor() * TIME_HZ));
+}