Allow specification of trusted devices by thumbprint rather than
[dcpomatic.git] / src / lib / film.cc
index 846e8ac51cd2c48f12c110a2c4c98d0249fc929b..426c7f81ddeb30bb0b90b4a26db8e6edacf8c5d6 100644 (file)
@@ -51,6 +51,8 @@
 #include "dcp_content.h"
 #include "screen_kdm.h"
 #include "cinema.h"
+#include "change_signaller.h"
+#include "check_content_change_job.h"
 #include <libcxml/cxml.h>
 #include <dcp/cpl.h>
 #include <dcp/certificate_chain.h>
@@ -155,6 +157,7 @@ Film::Film (optional<boost::filesystem::path> dir)
        , _reel_type (REELTYPE_SINGLE)
        , _reel_length (2000000000)
        , _upload_after_make_dcp (Config::instance()->default_upload_after_make_dcp())
+       , _reencode_j2k (false)
        , _state_version (current_state_version)
        , _dirty (false)
 {
@@ -347,7 +350,8 @@ Film::make_dcp ()
 
        shared_ptr<TranscodeJob> tj (new TranscodeJob (shared_from_this()));
        tj->set_encoder (shared_ptr<Encoder> (new DCPEncoder (shared_from_this(), tj)));
-       JobManager::instance()->add (tj);
+       shared_ptr<CheckContentChangeJob> cc (new CheckContentChangeJob (shared_from_this(), tj));
+       JobManager::instance()->add (cc);
 }
 
 /** Start a job to send our DCP to the configured TMS */
@@ -395,11 +399,19 @@ Film::metadata (bool with_content_paths) const
        root->add_child("ReelType")->add_child_text (raw_convert<string> (static_cast<int> (_reel_type)));
        root->add_child("ReelLength")->add_child_text (raw_convert<string> (_reel_length));
        root->add_child("UploadAfterMakeDCP")->add_child_text (_upload_after_make_dcp ? "1" : "0");
+       root->add_child("ReencodeJ2K")->add_child_text (_reencode_j2k ? "1" : "0");
        _playlist->as_xml (root->add_child ("Playlist"), with_content_paths);
 
        return doc;
 }
 
+void
+Film::write_metadata (boost::filesystem::path path) const
+{
+       shared_ptr<xmlpp::Document> doc = metadata ();
+       doc->write_to_file_formatted (path.string());
+}
+
 /** Write state to our `metadata' file */
 void
 Film::write_metadata () const
@@ -502,6 +514,7 @@ Film::read_metadata (optional<boost::filesystem::path> path)
        _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);
        _upload_after_make_dcp = f.optional_bool_child("UploadAfterMakeDCP").get_value_or (false);
+       _reencode_j2k = f.optional_bool_child("ReencodeJ2K").get_value_or(false);
 
        list<string> notes;
        /* This method is the only one that can return notes (so far) */
@@ -824,133 +837,149 @@ Film::set_directory (boost::filesystem::path d)
 void
 Film::set_name (string n)
 {
+       ChangeSignaller<Film> ch (this, NAME);
        _name = n;
-       signal_changed (NAME);
 }
 
 void
 Film::set_use_isdcf_name (bool u)
 {
+       ChangeSignaller<Film> ch (this, USE_ISDCF_NAME);
        _use_isdcf_name = u;
-       signal_changed (USE_ISDCF_NAME);
 }
 
 void
 Film::set_dcp_content_type (DCPContentType const * t)
 {
+       ChangeSignaller<Film> ch (this, DCP_CONTENT_TYPE);
        _dcp_content_type = t;
-       signal_changed (DCP_CONTENT_TYPE);
 }
 
 void
 Film::set_container (Ratio const * c)
 {
+       ChangeSignaller<Film> ch (this, CONTAINER);
        _container = c;
-       signal_changed (CONTAINER);
 }
 
 void
 Film::set_resolution (Resolution r)
 {
+       ChangeSignaller<Film> ch (this, RESOLUTION);
        _resolution = r;
-       signal_changed (RESOLUTION);
 }
 
 void
 Film::set_j2k_bandwidth (int b)
 {
+       ChangeSignaller<Film> ch (this, J2K_BANDWIDTH);
        _j2k_bandwidth = b;
-       signal_changed (J2K_BANDWIDTH);
 }
 
 void
 Film::set_isdcf_metadata (ISDCFMetadata m)
 {
+       ChangeSignaller<Film> ch (this, ISDCF_METADATA);
        _isdcf_metadata = m;
-       signal_changed (ISDCF_METADATA);
 }
 
 void
 Film::set_video_frame_rate (int f)
 {
+       ChangeSignaller<Film> ch (this, VIDEO_FRAME_RATE);
        _video_frame_rate = f;
-       signal_changed (VIDEO_FRAME_RATE);
 }
 
 void
 Film::set_audio_channels (int c)
 {
+       ChangeSignaller<Film> ch (this, AUDIO_CHANNELS);
        _audio_channels = c;
-       signal_changed (AUDIO_CHANNELS);
 }
 
 void
 Film::set_three_d (bool t)
 {
+       ChangeSignaller<Film> ch (this, THREE_D);
        _three_d = t;
-       signal_changed (THREE_D);
 
        if (_three_d && _isdcf_metadata.two_d_version_of_three_d) {
+               ChangeSignaller<Film> ch (this, ISDCF_METADATA);
                _isdcf_metadata.two_d_version_of_three_d = false;
-               signal_changed (ISDCF_METADATA);
        }
 }
 
 void
 Film::set_interop (bool i)
 {
+       ChangeSignaller<Film> ch (this, INTEROP);
        _interop = i;
-       signal_changed (INTEROP);
 }
 
 void
 Film::set_audio_processor (AudioProcessor const * processor)
 {
+       ChangeSignaller<Film> ch1 (this, AUDIO_PROCESSOR);
+       ChangeSignaller<Film> ch2 (this, AUDIO_CHANNELS);
        _audio_processor = processor;
-       signal_changed (AUDIO_PROCESSOR);
-       signal_changed (AUDIO_CHANNELS);
 }
 
 void
 Film::set_reel_type (ReelType t)
 {
+       ChangeSignaller<Film> ch (this, REEL_TYPE);
        _reel_type = t;
-       signal_changed (REEL_TYPE);
 }
 
 /** @param r Desired reel length in bytes */
 void
 Film::set_reel_length (int64_t r)
 {
+       ChangeSignaller<Film> ch (this, REEL_LENGTH);
        _reel_length = r;
-       signal_changed (REEL_LENGTH);
 }
 
 void
 Film::set_upload_after_make_dcp (bool u)
 {
+       ChangeSignaller<Film> ch (this, UPLOAD_AFTER_MAKE_DCP);
        _upload_after_make_dcp = u;
-       signal_changed (UPLOAD_AFTER_MAKE_DCP);
 }
 
 void
-Film::signal_changed (Property p)
+Film::set_reencode_j2k (bool r)
 {
-       _dirty = true;
+       ChangeSignaller<Film> ch (this, REENCODE_J2K);
+       _reencode_j2k = r;
+}
 
-       switch (p) {
-       case Film::CONTENT:
-               set_video_frame_rate (_playlist->best_video_frame_rate ());
-               break;
-       case Film::VIDEO_FRAME_RATE:
-       case Film::SEQUENCE:
-               _playlist->maybe_sequence ();
-               break;
-       default:
-               break;
-       }
+void
+Film::signal_change (ChangeType type, int p)
+{
+       signal_change (type, static_cast<Property>(p));
+}
 
-       emit (boost::bind (boost::ref (Changed), p));
+void
+Film::signal_change (ChangeType type, Property p)
+{
+       if (type == CHANGE_TYPE_DONE) {
+               _dirty = true;
+
+               if (p == Film::CONTENT) {
+                       set_video_frame_rate (_playlist->best_video_frame_rate ());
+               }
+
+               emit (boost::bind (boost::ref (Change), type, p));
+
+               if (p == Film::VIDEO_FRAME_RATE || p == Film::SEQUENCE) {
+                       /* We want to call Playlist::maybe_sequence but this must happen after the
+                          main signal emission (since the butler will see that emission and un-suspend itself).
+                       */
+                       emit (boost::bind(&Playlist::maybe_sequence, _playlist.get()));
+               }
+       } else {
+               Change (type, p);
+       }
 }
 
 void
@@ -1027,22 +1056,22 @@ Film::cpls () const
 void
 Film::set_signed (bool s)
 {
+       ChangeSignaller<Film> ch (this, SIGNED);
        _signed = s;
-       signal_changed (SIGNED);
 }
 
 void
 Film::set_encrypted (bool e)
 {
+       ChangeSignaller<Film> ch (this, ENCRYPTED);
        _encrypted = e;
-       signal_changed (ENCRYPTED);
 }
 
 void
 Film::set_key (dcp::Key key)
 {
+       ChangeSignaller<Film> ch (this, KEY);
        _key = key;
-       signal_changed (KEY);
 }
 
 ContentList
@@ -1156,34 +1185,31 @@ Film::active_frame_rate_change (DCPTime t) const
 void
 Film::playlist_content_change (ChangeType type, weak_ptr<Content> c, int p, bool frequent)
 {
-       if (type != CHANGE_TYPE_DONE) {
-               return;
-       }
-
-       _dirty = true;
-
        if (p == ContentProperty::VIDEO_FRAME_RATE) {
-               set_video_frame_rate (_playlist->best_video_frame_rate ());
+               signal_change (type, Film::CONTENT);
        } else if (p == AudioContentProperty::STREAMS) {
-               signal_changed (NAME);
+               signal_change (type, Film::NAME);
        }
 
-       emit (boost::bind (boost::ref (ContentChange), type, c, p, frequent));
+       if (type == CHANGE_TYPE_DONE) {
+               emit (boost::bind (boost::ref (ContentChange), type, c, p, frequent));
+       } else {
+               ContentChange (type, c, p, frequent);
+       }
 }
 
 void
 Film::playlist_change (ChangeType type)
 {
-       if (type == CHANGE_TYPE_DONE) {
-               signal_changed (CONTENT);
-               signal_changed (NAME);
-       }
+       signal_change (type, CONTENT);
+       signal_change (type, NAME);
 }
 
 void
 Film::playlist_order_changed ()
 {
-       signal_changed (CONTENT_ORDER);
+       /* XXX: missing PENDING */
+       signal_change (CHANGE_TYPE_DONE, CONTENT_ORDER);
 }
 
 int
@@ -1205,9 +1231,9 @@ Film::set_sequence (bool s)
                return;
        }
 
+       ChangeSignaller<Film> cc (this, SEQUENCE);
        _sequence = s;
        _playlist->set_sequence (s);
-       signal_changed (SEQUENCE);
 }
 
 /** @return Size of the largest possible image in whatever resolution we are using */
@@ -1233,7 +1259,7 @@ Film::frame_size () const
 }
 
 /** @param recipient KDM recipient certificate.
- *  @param trusted_devices Certificates of other trusted devices (can be empty).
+ *  @param trusted_devices Certificate thumbprints of other trusted devices (can be empty).
  *  @param cpl_file CPL filename.
  *  @param from KDM from time expressed as a local time with an offset from UTC.
  *  @param until KDM to time expressed as a local time with an offset from UTC.
@@ -1245,7 +1271,7 @@ Film::frame_size () const
 dcp::EncryptedKDM
 Film::make_kdm (
        dcp::Certificate recipient,
-       vector<dcp::Certificate> trusted_devices,
+       vector<string> trusted_devices,
        boost::filesystem::path cpl_file,
        dcp::LocalTime from,
        dcp::LocalTime until,
@@ -1331,7 +1357,7 @@ Film::make_kdms (
                if (i->recipient) {
                        dcp::EncryptedKDM const kdm = make_kdm (
                                i->recipient.get(),
-                               i->trusted_devices,
+                               i->trusted_device_thumbprints(),
                                cpl_file,
                                dcp::LocalTime (from, i->cinema->utc_offset_hour(), i->cinema->utc_offset_minute()),
                                dcp::LocalTime (until, i->cinema->utc_offset_hour(), i->cinema->utc_offset_minute()),
@@ -1627,3 +1653,20 @@ Film::references_dcp_audio () const
 
        return false;
 }
+
+list<DCPTextTrack>
+Film::closed_caption_tracks () const
+{
+       list<DCPTextTrack> tt;
+       BOOST_FOREACH (shared_ptr<Content> i, content()) {
+               BOOST_FOREACH (shared_ptr<TextContent> j, i->text) {
+                       /* XXX: Empty DCPTextTrack ends up being a magic value here */
+                       DCPTextTrack dtt = j->dcp_track().get_value_or(DCPTextTrack());
+                       if (j->type() == TEXT_CLOSED_CAPTION && find(tt.begin(), tt.end(), dtt) == tt.end()) {
+                               tt.push_back (dtt);
+                       }
+               }
+       }
+
+       return tt;
+}