Implement Film::reels().
[dcpomatic.git] / src / lib / film.cc
index e48b08f3b70b7216b6035c7750b22df3cdbcd942..8cf46815020d82eb14e6b419ea3100d1e62e94f5 100644 (file)
@@ -48,6 +48,8 @@
 #include "video_content.h"
 #include "subtitle_content.h"
 #include "ffmpeg_content.h"
+#include "dcp_content.h"
+#include "screen_kdm.h"
 #include <libcxml/cxml.h>
 #include <dcp/cpl.h>
 #include <dcp/certificate_chain.h>
@@ -84,8 +86,8 @@ using boost::dynamic_pointer_cast;
 using boost::optional;
 using boost::is_any_of;
 
-#define LOG_GENERAL(...) log()->log (String::compose (__VA_ARGS__), Log::TYPE_GENERAL);
-#define LOG_GENERAL_NC(...) log()->log (__VA_ARGS__, Log::TYPE_GENERAL);
+#define LOG_GENERAL(...) log()->log (String::compose (__VA_ARGS__), LogEntry::TYPE_GENERAL);
+#define LOG_GENERAL_NC(...) log()->log (__VA_ARGS__, LogEntry::TYPE_GENERAL);
 
 /* 5 -> 6
  * AudioMapping XML changed.
@@ -122,8 +124,10 @@ Film::Film (boost::filesystem::path dir, bool log)
        , _audio_channels (6)
        , _three_d (false)
        , _sequence_video (true)
-       , _interop (false)
+       , _interop (Config::instance()->default_interop ())
        , _audio_processor (0)
+       , _reel_type (REELTYPE_SINGLE)
+       , _reel_length (2000000000)
        , _state_version (current_state_version)
        , _dirty (false)
 {
@@ -206,11 +210,11 @@ Film::video_identifier () const
 
 /** @return The file to write video frame info to */
 boost::filesystem::path
-Film::info_file () const
+Film::info_file (DCPTimePeriod period) const
 {
        boost::filesystem::path p;
        p /= "info";
-       p /= video_identifier ();
+       p /= video_identifier () + "_" + raw_convert<string> (period.from.get()) + "_" + raw_convert<string> (period.to.get());
        return file (p);
 }
 
@@ -221,9 +225,9 @@ Film::internal_video_asset_dir () const
 }
 
 boost::filesystem::path
-Film::internal_video_asset_filename () const
+Film::internal_video_asset_filename (DCPTimePeriod p) const
 {
-       return video_identifier() + ".mxf";
+       return video_identifier() + "_" + raw_convert<string> (p.from.get()) + "_" + raw_convert<string> (p.to.get()) + ".mxf";
 }
 
 boost::filesystem::path
@@ -342,6 +346,8 @@ Film::metadata () const
        if (_audio_processor) {
                root->add_child("AudioProcessor")->add_child_text (_audio_processor->id ());
        }
+       root->add_child("ReelType")->add_child_text (raw_convert<string> (_reel_type));
+       root->add_child("ReelLength")->add_child_text (raw_convert<string> (_reel_length));
        _playlist->as_xml (root->add_child ("Playlist"));
 
        return doc;
@@ -425,6 +431,9 @@ Film::read_metadata ()
                _audio_processor = 0;
        }
 
+       _reel_type = static_cast<ReelType> (f.optional_number_child<int>("ReelType").get_value_or (static_cast<int>(REELTYPE_SINGLE)));
+       _reel_length = f.optional_number_child<int64_t>("ReelLength").get_value_or (2000000000);
+
        list<string> notes;
        /* This method is the only one that can return notes (so far) */
        _playlist->set_from_xml (shared_from_this(), f.node_child ("Playlist"), _state_version, notes);
@@ -674,8 +683,18 @@ Film::isdcf_name (bool if_created_now) const
                d << "-3D";
        }
 
-       if (!dm.package_type.empty ()) {
-               d << "_" << dm.package_type;
+       bool vf = false;
+       BOOST_FOREACH (shared_ptr<Content> i, content ()) {
+               shared_ptr<const DCPContent> dc = dynamic_pointer_cast<const DCPContent> (i);
+               if (dc && (dc->reference_video() || dc->reference_audio() || dc->reference_subtitle())) {
+                       vf = true;
+               }
+       }
+
+       if (vf) {
+               d << "_VF";
+       } else {
+               d << "_OV";
        }
 
        return d.str ();
@@ -804,6 +823,20 @@ Film::set_audio_processor (AudioProcessor const * processor)
        signal_changed (AUDIO_CHANNELS);
 }
 
+void
+Film::set_reel_type (ReelType t)
+{
+       _reel_type = t;
+       signal_changed (REEL_TYPE);
+}
+
+void
+Film::set_reel_length (int64_t r)
+{
+       _reel_length = r;
+       signal_changed (REEL_LENGTH);
+}
+
 void
 Film::signal_changed (Property p)
 {
@@ -994,6 +1027,7 @@ Film::move_content_later (shared_ptr<Content> c)
        _playlist->move_later (c);
 }
 
+/** @return length of the film from time 0 to the last thing on the playlist */
 DCPTime
 Film::length () const
 {
@@ -1096,7 +1130,7 @@ Film::make_kdm (
                ).encrypt (signer, target, formulation);
 }
 
-list<dcp::EncryptedKDM>
+list<ScreenKDM>
 Film::make_kdms (
        list<shared_ptr<Screen> > screens,
        boost::filesystem::path dcp,
@@ -1105,11 +1139,11 @@ Film::make_kdms (
        dcp::Formulation formulation
        ) const
 {
-       list<dcp::EncryptedKDM> kdms;
+       list<ScreenKDM> kdms;
 
-       for (list<shared_ptr<Screen> >::iterator i = screens.begin(); i != screens.end(); ++i) {
-               if ((*i)->certificate) {
-                       kdms.push_back (make_kdm ((*i)->certificate.get(), dcp, from, until, formulation));
+       BOOST_FOREACH (shared_ptr<Screen> i, screens) {
+               if (i->certificate) {
+                       kdms.push_back (ScreenKDM (i, make_kdm (i->certificate.get(), dcp, from, until, formulation)));
                }
        }
 
@@ -1252,3 +1286,48 @@ Film::audio_analysis_finished ()
 {
        /* XXX */
 }
+
+list<DCPTimePeriod>
+Film::reels () const
+{
+       list<DCPTimePeriod> p;
+       DCPTime const len = length ();
+
+       switch (reel_type ()) {
+       case REELTYPE_SINGLE:
+               p.push_back (DCPTimePeriod (DCPTime (), len));
+               break;
+       case REELTYPE_ONE_PER_VIDEO:
+       {
+               optional<DCPTime> last;
+               BOOST_FOREACH (shared_ptr<Content> c, content ()) {
+                       shared_ptr<VideoContent> v = dynamic_pointer_cast<VideoContent> (c);
+                       if (v) {
+                               if (last) {
+                                       p.push_back (DCPTimePeriod (last.get(), v->position ()));
+                               }
+                               last = v->position ();
+                       }
+               }
+               if (last) {
+                       p.push_back (DCPTimePeriod (last.get(), len));
+               }
+               break;
+       }
+       case REELTYPE_BY_LENGTH:
+       {
+               DCPTime current;
+               /* Integer-divide reel length by the size of one frame to give the number of frames per reel */
+               Frame const reel_in_frames = _reel_length / ((j2k_bandwidth() / video_frame_rate()) / 8);
+               while (current < len) {
+                       DCPTime end = min (len, current + DCPTime::from_frames (reel_in_frames, video_frame_rate ()));
+                       p.push_back (DCPTimePeriod (current, end));
+                       current = end;
+               }
+               break;
+       }
+       }
+
+       return p;
+}
+