Add background image configuration to swaroop variant.
[dcpomatic.git] / src / lib / film.cc
index fe7fcbfaee4375f8d3bacb0bb999fc90031d6c74..bd5724828b1d196ee279f428a963a2bcb5ee5611 100644 (file)
 #include "screen.h"
 #include "audio_content.h"
 #include "video_content.h"
-#include "caption_content.h"
+#include "text_content.h"
 #include "ffmpeg_content.h"
 #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>
@@ -125,7 +127,7 @@ string const Film::metadata_file = "metadata.xml";
  * 35 -> 36
  * EffectColour rather than OutlineColour in Subtitle.
  * 36 -> 37
- * CaptionContent can be in a Caption tag, and some of the tag names
+ * TextContent can be in a Caption tag, and some of the tag names
  * have had Subtitle prefixes or suffixes removed.
  */
 int const Film::current_state_version = 37;
@@ -160,9 +162,9 @@ Film::Film (optional<boost::filesystem::path> dir)
 {
        set_isdcf_date_today ();
 
-       _playlist_changed_connection = _playlist->Changed.connect (bind (&Film::playlist_changed, this));
+       _playlist_change_connection = _playlist->Change.connect (bind (&Film::playlist_change, this, _1));
        _playlist_order_changed_connection = _playlist->OrderChanged.connect (bind (&Film::playlist_order_changed, this));
-       _playlist_content_changed_connection = _playlist->ContentChanged.connect (bind (&Film::playlist_content_changed, this, _1, _2, _3));
+       _playlist_content_change_connection = _playlist->ContentChange.connect (bind (&Film::playlist_content_change, this, _1, _2, _3, _4));
 
        if (dir) {
                /* Make state.directory a complete path without ..s (where possible)
@@ -347,7 +349,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 */
@@ -704,10 +707,10 @@ Film::isdcf_name (bool if_created_now) const
                        bool burnt_in = true;
                        bool ccap = false;
                        BOOST_FOREACH (shared_ptr<Content> i, content()) {
-                               BOOST_FOREACH (shared_ptr<CaptionContent> j, i->caption) {
-                                       if (j->type() == CAPTION_OPEN && j->use() && !j->burn()) {
+                               BOOST_FOREACH (shared_ptr<TextContent> j, i->text) {
+                                       if (j->type() == TEXT_OPEN_SUBTITLE && j->use() && !j->burn()) {
                                                burnt_in = false;
-                                       } else if (j->type() == CAPTION_CLOSED) {
+                                       } else if (j->type() == TEXT_CLOSED_CAPTION) {
                                                ccap = true;
                                        }
                                }
@@ -778,13 +781,17 @@ Film::isdcf_name (bool if_created_now) const
        bool vf = false;
        BOOST_FOREACH (shared_ptr<Content> i, content ()) {
                shared_ptr<const DCPContent> dc = dynamic_pointer_cast<const DCPContent> (i);
-               bool any_caption = false;
-               for (int i = 0; i < CAPTION_COUNT; ++i) {
-                       if (dc->reference_caption(static_cast<CaptionType>(i))) {
-                               any_caption = true;
+               if (!dc) {
+                       continue;
+               }
+
+               bool any_text = false;
+               for (int i = 0; i < TEXT_COUNT; ++i) {
+                       if (dc->reference_text(static_cast<TextType>(i))) {
+                               any_text = true;
                        }
                }
-               if (dc && (dc->reference_video() || dc->reference_audio() || any_caption)) {
+               if (dc->reference_video() || dc->reference_audio() || any_text) {
                        vf = true;
                }
        }
@@ -820,133 +827,142 @@ 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::signal_change (ChangeType type, int p)
 {
-       _dirty = true;
+       signal_change (type, static_cast<Property>(p));
+}
 
-       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, Property p)
+{
+       if (type == CHANGE_TYPE_DONE) {
+               _dirty = true;
 
-       emit (boost::bind (boost::ref (Changed), p));
+               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
@@ -1023,22 +1039,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
@@ -1098,8 +1114,8 @@ Film::add_content (shared_ptr<Content> c)
        /* Add {video,subtitle} content after any existing {video,subtitle} content */
        if (c->video) {
                c->set_position (_playlist->video_end());
-       } else if (!c->caption.empty()) {
-               c->set_position (_playlist->caption_end());
+       } else if (!c->text.empty()) {
+               c->set_position (_playlist->text_end());
        }
 
        if (_template_film) {
@@ -1150,30 +1166,33 @@ Film::active_frame_rate_change (DCPTime t) const
 }
 
 void
-Film::playlist_content_changed (weak_ptr<Content> c, int p, bool frequent)
+Film::playlist_content_change (ChangeType type, weak_ptr<Content> c, int p, bool frequent)
 {
-       _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 (ContentChanged), 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_changed ()
+Film::playlist_change (ChangeType type)
 {
-       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
@@ -1195,9 +1214,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 */
@@ -1387,7 +1406,7 @@ Film::subtitle_language () const
        set<string> languages;
 
        BOOST_FOREACH (shared_ptr<Content> i, content()) {
-               BOOST_FOREACH (shared_ptr<CaptionContent> j, i->caption) {
+               BOOST_FOREACH (shared_ptr<TextContent> j, i->text) {
                        languages.insert (j->language ());
                }
        }
@@ -1617,3 +1636,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;
+}