UI tweaks for still image DCPs with audio.
[dcpomatic.git] / src / lib / film.cc
index ce551041bfc4506239ec1cc177371d3fd0de65eb..9da15a73baf31ffc36f27bbfa05f87e61ad4310a 100644 (file)
@@ -31,7 +31,6 @@
 #include <boost/date_time.hpp>
 #include "film.h"
 #include "format.h"
-#include "imagemagick_encoder.h"
 #include "job.h"
 #include "filter.h"
 #include "transcoder.h"
@@ -40,7 +39,6 @@
 #include "ab_transcode_job.h"
 #include "transcode_job.h"
 #include "scp_dcp_job.h"
-#include "copy_from_dvd_job.h"
 #include "make_dcp_job.h"
 #include "log.h"
 #include "options.h"
@@ -87,6 +85,7 @@ int const Film::state_version = 1;
 
 Film::Film (string d, bool must_exist)
        : _use_dci_name (true)
+       , _trust_content_header (true)
        , _dcp_content_type (0)
        , _format (0)
        , _scaler (Scaler::from_id ("bicubic"))
@@ -145,6 +144,7 @@ Film::Film (Film const & o)
        , _name              (o._name)
        , _use_dci_name      (o._use_dci_name)
        , _content           (o._content)
+       , _trust_content_header (o._trust_content_header)
        , _dcp_content_type  (o._dcp_content_type)
        , _format            (o._format)
        , _crop              (o._crop)
@@ -170,7 +170,6 @@ Film::Film (Film const & o)
        , _studio            (o._studio)
        , _facility          (o._facility)
        , _package_type      (o._package_type)
-       , _thumbs            (o._thumbs)
        , _size              (o._size)
        , _length            (o._length)
        , _content_digest    (o._content_digest)
@@ -261,35 +260,37 @@ Film::make_dcp (bool transcode)
                throw MissingSettingError ("name");
        }
 
-       shared_ptr<Options> o (new Options (j2k_dir(), ".j2c", dir ("wavs")));
-       o->out_size = format()->dcp_size ();
-       o->padding = format()->dcp_padding (shared_from_this ());
-       o->ratio = format()->ratio_as_float (shared_from_this ());
+       shared_ptr<EncodeOptions> oe (new EncodeOptions (j2k_dir(), ".j2c", dir ("wavs")));
+       oe->out_size = format()->dcp_size ();
+       oe->padding = format()->dcp_padding (shared_from_this ());
        if (dcp_length ()) {
-               o->video_decode_range = make_pair (dcp_trim_start(), dcp_trim_start() + dcp_length().get());
+               oe->video_range = make_pair (dcp_trim_start(), dcp_trim_start() + dcp_length().get());
                if (audio_stream()) {
-                       o->audio_decode_range = make_pair (
-                               video_frames_to_audio_frames (o->video_decode_range.get().first, audio_stream()->sample_rate(), frames_per_second()),
-                               video_frames_to_audio_frames (o->video_decode_range.get().second, audio_stream()->sample_rate(), frames_per_second())
+                       oe->audio_range = make_pair (
+                               video_frames_to_audio_frames (oe->video_range.get().first, audio_stream()->sample_rate(), frames_per_second()),
+                               video_frames_to_audio_frames (oe->video_range.get().second, audio_stream()->sample_rate(), frames_per_second())
                                );
                }
                        
        }
-       o->decode_subtitles = with_subtitles ();
-       o->decode_video_skip = dcp_frame_rate (frames_per_second()).skip;
+       
+       oe->video_skip = dcp_frame_rate (frames_per_second()).skip;
+
+       shared_ptr<DecodeOptions> od (new DecodeOptions);
+       od->decode_subtitles = with_subtitles ();
 
        shared_ptr<Job> r;
 
        if (transcode) {
                if (dcp_ab()) {
-                       r = JobManager::instance()->add (shared_ptr<Job> (new ABTranscodeJob (shared_from_this(), o, shared_ptr<Job> ())));
+                       r = JobManager::instance()->add (shared_ptr<Job> (new ABTranscodeJob (shared_from_this(), od, oe, shared_ptr<Job> ())));
                } else {
-                       r = JobManager::instance()->add (shared_ptr<Job> (new TranscodeJob (shared_from_this(), o, shared_ptr<Job> ())));
+                       r = JobManager::instance()->add (shared_ptr<Job> (new TranscodeJob (shared_from_this(), od, oe, shared_ptr<Job> ())));
                }
        }
 
-       r = JobManager::instance()->add (shared_ptr<Job> (new CheckHashesJob (shared_from_this(), o, r)));
-       JobManager::instance()->add (shared_ptr<Job> (new MakeDCPJob (shared_from_this(), o, r)));
+       r = JobManager::instance()->add (shared_ptr<Job> (new CheckHashesJob (shared_from_this(), od, oe, r)));
+       JobManager::instance()->add (shared_ptr<Job> (new MakeDCPJob (shared_from_this(), oe, r)));
 }
 
 /** Start a job to examine our content file */
@@ -300,12 +301,6 @@ Film::examine_content ()
                return;
        }
 
-       set_thumbs (vector<SourceFrame> ());
-       boost::filesystem::remove_all (dir ("thumbs"));
-
-       /* This call will recreate the directory */
-       dir ("thumbs");
-       
        _examine_content_job.reset (new ExamineContentJob (shared_from_this(), shared_ptr<Job> ()));
        _examine_content_job->Finished.connect (bind (&Film::examine_content_finished, this));
        JobManager::instance()->add (_examine_content_job);
@@ -337,13 +332,6 @@ Film::send_dcp_to_tms ()
        JobManager::instance()->add (j);
 }
 
-void
-Film::copy_from_dvd ()
-{
-       shared_ptr<Job> j (new CopyFromDVDJob (shared_from_this(), shared_ptr<Job> ()));
-       JobManager::instance()->add (j);
-}
-
 /** Count the number of frames that have been encoded for this film.
  *  @return frame count.
  */
@@ -363,35 +351,6 @@ Film::encoded_frames () const
        return N;
 }
 
-/** Return the filename of a subtitle image if one exists for a given thumb index.
- *  @param Thumbnail index.
- *  @return Position of the image within the source frame, and the image filename, if one exists.
- *  Otherwise the filename will be empty.
- */
-pair<Position, string>
-Film::thumb_subtitle (int n) const
-{
-       string sub_file = thumb_base(n) + ".sub";
-       if (!boost::filesystem::exists (sub_file)) {
-               return pair<Position, string> ();
-       }
-
-       pair<Position, string> sub;
-       
-       ifstream f (sub_file.c_str ());
-       multimap<string, string> kv = read_key_value (f);
-       for (map<string, string>::const_iterator i = kv.begin(); i != kv.end(); ++i) {
-               if (i->first == "x") {
-                       sub.first.x = lexical_cast<int> (i->second);
-               } else if (i->first == "y") {
-                       sub.first.y = lexical_cast<int> (i->second);
-                       sub.second = String::compose ("%1.sub.png", thumb_base(n));
-               }
-       }
-       
-       return sub;
-}
-
 /** Write state to our `metadata' file */
 void
 Film::write_metadata () const
@@ -412,6 +371,7 @@ Film::write_metadata () const
        f << "name " << _name << "\n";
        f << "use_dci_name " << _use_dci_name << "\n";
        f << "content " << _content << "\n";
+       f << "trust_content_header " << (_trust_content_header ? "1" : "0") << "\n";
        if (_dcp_content_type) {
                f << "dcp_content_type " << _dcp_content_type->pretty_name () << "\n";
        }
@@ -453,12 +413,6 @@ Film::write_metadata () const
        f << "facility " << _facility << "\n";
        f << "package_type " << _package_type << "\n";
 
-       /* Cached stuff; this is information about our content; we could
-          look it up each time, but that's slow.
-       */
-       for (vector<SourceFrame>::const_iterator i = _thumbs.begin(); i != _thumbs.end(); ++i) {
-               f << "thumb " << *i << "\n";
-       }
        f << "width " << _size.width << "\n";
        f << "height " << _size.height << "\n";
        f << "length " << _length.get_value_or(0) << "\n";
@@ -486,7 +440,6 @@ Film::read_metadata ()
        boost::mutex::scoped_lock lm (_state_mutex);
 
        _external_audio.clear ();
-       _thumbs.clear ();
        _content_audio_streams.clear ();
        _subtitle_streams.clear ();
 
@@ -521,6 +474,8 @@ Film::read_metadata ()
                        _use_dci_name = (v == "1");
                } else if (k == "content") {
                        _content = v;
+               } else if (k == "trust_content_header") {
+                       _trust_content_header = (v == "1");
                } else if (k == "dcp_content_type") {
                        _dcp_content_type = DCPContentType::from_pretty_name (v);
                } else if (k == "format") {
@@ -543,7 +498,7 @@ Film::read_metadata ()
                        _dcp_trim_end = atoi (v.c_str ());
                } else if (k == "dcp_ab") {
                        _dcp_ab = (v == "1");
-               } else if (k == "selected_content_audio_stream" || (!version && k == "content_audio_stream")) {
+               } else if (k == "selected_content_audio_stream" || (!version && k == "selected_audio_stream")) {
                        if (!version) {
                                audio_stream_index = atoi (v.c_str ());
                        } else {
@@ -588,13 +543,7 @@ Film::read_metadata ()
                }
                
                /* Cached stuff */
-               if (k == "thumb") {
-                       int const n = atoi (v.c_str ());
-                       /* Only add it to the list if it still exists */
-                       if (boost::filesystem::exists (thumb_file_for_frame (n))) {
-                               _thumbs.push_back (n);
-                       }
-               } else if (k == "width") {
+               if (k == "width") {
                        _size.width = atoi (v.c_str ());
                } else if (k == "height") {
                        _size.height = atoi (v.c_str ());
@@ -638,61 +587,6 @@ Film::read_metadata ()
        _dirty = false;
 }
 
-/** @param n A thumb index.
- *  @return The path to the thumb's image file.
- */
-string
-Film::thumb_file (int n) const
-{
-       return thumb_file_for_frame (thumb_frame (n));
-}
-
-/** @param n A frame index within the Film's source.
- *  @return The path to the thumb's image file for this frame;
- *  we assume that it exists.
- */
-string
-Film::thumb_file_for_frame (SourceFrame n) const
-{
-       return thumb_base_for_frame(n) + ".png";
-}
-
-/** @param n Thumb index.
- *  Must not be called with the _state_mutex locked.
- */
-string
-Film::thumb_base (int n) const
-{
-       return thumb_base_for_frame (thumb_frame (n));
-}
-
-string
-Film::thumb_base_for_frame (SourceFrame n) const
-{
-       stringstream s;
-       s.width (8);
-       s << setfill('0') << n;
-       
-       boost::filesystem::path p;
-       p /= dir ("thumbs");
-       p /= s.str ();
-               
-       return p.string ();
-}
-
-/** @param n A thumb index.
- *  @return The frame within the Film's source that it is for.
- *
- *  Must not be called with the _state_mutex locked.
- */
-SourceFrame
-Film::thumb_frame (int n) const
-{
-       boost::mutex::scoped_lock lm (_state_mutex);
-       assert (n < int (_thumbs.size ()));
-       return _thumbs[n];
-}
-
 Size
 Film::cropped_size (Size s) const
 {
@@ -784,6 +678,10 @@ Film::target_audio_sample_rate () const
 boost::optional<SourceFrame>
 Film::dcp_length () const
 {
+       if (content_type() == STILL) {
+               return _still_duration * frames_per_second();
+       }
+       
        if (!length()) {
                return boost::optional<SourceFrame> ();
        }
@@ -948,6 +846,7 @@ Film::set_content (string c)
                _content = c;
        }
 
+       /* Reset streams here in case the new content doesn't have one or the other */
        _content_audio_stream = shared_ptr<AudioStream> ();
        _subtitle_stream = shared_ptr<SubtitleStream> ();
 
@@ -956,23 +855,23 @@ Film::set_content (string c)
        */
 
        try {
-               shared_ptr<Options> o (new Options ("", "", ""));
-               o->out_size = Size (1024, 1024);
+               shared_ptr<DecodeOptions> o (new DecodeOptions);
+               Decoders d = decoder_factory (shared_from_this(), o, 0);
                
-               pair<shared_ptr<VideoDecoder>, shared_ptr<AudioDecoder> > d = decoder_factory (shared_from_this(), o, 0);
-               
-               set_size (d.first->native_size ());
-               set_frames_per_second (d.first->frames_per_second ());
-               set_subtitle_streams (d.first->subtitle_streams ());
-               set_content_audio_streams (d.second->audio_streams ());
+               set_size (d.video->native_size ());
+               set_frames_per_second (d.video->frames_per_second ());
+               set_subtitle_streams (d.video->subtitle_streams ());
+               if (d.audio) {
+                       set_content_audio_streams (d.audio->audio_streams ());
+               }
 
                /* Start off with the first audio and subtitle streams */
-               if (!d.second->audio_streams().empty()) {
-                       set_content_audio_stream (d.second->audio_streams().front());
+               if (d.audio && !d.audio->audio_streams().empty()) {
+                       set_content_audio_stream (d.audio->audio_streams().front());
                }
                
-               if (!d.first->subtitle_streams().empty()) {
-                       set_subtitle_stream (d.first->subtitle_streams().front());
+               if (!d.video->subtitle_streams().empty()) {
+                       set_subtitle_stream (d.video->subtitle_streams().front());
                }
                
                {
@@ -982,8 +881,6 @@ Film::set_content (string c)
                
                signal_changed (CONTENT);
                
-               set_content_digest (md5_digest (content_path ()));
-               
                examine_content ();
 
        } catch (...) {
@@ -993,6 +890,37 @@ Film::set_content (string c)
                throw;
 
        }
+
+       /* Default format */
+       switch (content_type()) {
+       case STILL:
+               set_format (Format::from_id ("var-185"));
+               break;
+       case VIDEO:
+               set_format (Format::from_id ("185"));
+               break;
+       }
+
+       /* Still image DCPs must use external audio */
+       if (content_type() == STILL) {
+               set_use_content_audio (false);
+       }
+}
+
+void
+Film::set_trust_content_header (bool t)
+{
+       {
+               boost::mutex::scoped_lock lm (_state_mutex);
+               _trust_content_header = t;
+       }
+       
+       signal_changed (TRUST_CONTENT_HEADER);
+
+       if (!_trust_content_header && !content().empty()) {
+               /* We just said that we don't trust the content's header */
+               examine_content ();
+       }
 }
               
 void
@@ -1150,8 +1078,7 @@ Film::set_external_audio (vector<string> a)
                _external_audio = a;
        }
 
-       shared_ptr<Options> o (new Options ("", "", ""));
-       o->decode_audio = true;
+       shared_ptr<DecodeOptions> o (new DecodeOptions);
        shared_ptr<ExternalAudioDecoder> decoder (new ExternalAudioDecoder (shared_from_this(), o, 0));
        if (decoder->audio_stream()) {
                _external_audio_stream = decoder->audio_stream ();
@@ -1311,16 +1238,6 @@ Film::set_package_type (string p)
        signal_changed (DCI_METADATA);
 }
 
-void
-Film::set_thumbs (vector<SourceFrame> t)
-{
-       {
-               boost::mutex::scoped_lock lm (_state_mutex);
-               _thumbs = t;
-       }
-       signal_changed (THUMBS);
-}
-
 void
 Film::set_size (Size s)
 {