Re-work again so that there's just one encoder; various tweaks to still-image-with...
[dcpomatic.git] / src / lib / film.cc
index de3a734655737058aeb0f3569aa0c642836e176b..e7687fe3c14f90e09a8ccbbe0ba63d1e8eece7ad 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"))
@@ -132,8 +131,10 @@ Film::Film (string d, bool must_exist)
        }
 
        _external_audio_stream = ExternalAudioStream::create ();
-
-       read_metadata ();
+       
+       if (must_exist) {
+               read_metadata ();
+       }
 
        _log = new FileLog (file ("log"));
        set_dci_date_today ();
@@ -145,6 +146,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 +172,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 +262,47 @@ 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,
+                                       dcp_audio_sample_rate (audio_stream()->sample_rate()),
+                                       dcp_frame_rate (frames_per_second()).frames_per_second
+                                       ),
+                               
+                               video_frames_to_audio_frames (
+                                       oe->video_range.get().second,
+                                       dcp_audio_sample_rate (audio_stream()->sample_rate()),
+                                       dcp_frame_rate (frames_per_second()).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 +313,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 +344,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 +363,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 +383,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 +425,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 +452,6 @@ Film::read_metadata ()
        boost::mutex::scoped_lock lm (_state_mutex);
 
        _external_audio.clear ();
-       _thumbs.clear ();
        _content_audio_streams.clear ();
        _subtitle_streams.clear ();
 
@@ -496,8 +461,12 @@ Film::read_metadata ()
        boost::optional<int> audio_sample_rate;
        boost::optional<int> audio_stream_index;
        boost::optional<int> subtitle_stream_index;
-       
+
        ifstream f (file ("metadata").c_str());
+       if (!f.good()) {
+               throw OpenFileError (file("metadata"));
+       }
+       
        multimap<string, string> kv = read_key_value (f);
 
        /* We need version before anything else */
@@ -521,6 +490,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") {
@@ -588,13 +559,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 +603,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
 {
@@ -781,11 +691,15 @@ Film::target_audio_sample_rate () const
        return rint (t);
 }
 
-boost::optional<SourceFrame>
+boost::optional<int>
 Film::dcp_length () const
 {
+       if (content_type() == STILL) {
+               return _still_duration * frames_per_second();
+       }
+       
        if (!length()) {
-               return boost::optional<SourceFrame> ();
+               return boost::optional<int> ();
        }
 
        return length().get() - dcp_trim_start() - dcp_trim_end();
@@ -948,31 +862,35 @@ 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> ();
 
+       /* Start off using content audio */
+       set_use_content_audio (true);
+
        /* Create a temporary decoder so that we can get information
           about the content.
        */
 
        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 +900,6 @@ Film::set_content (string c)
                
                signal_changed (CONTENT);
                
-               set_content_digest (md5_digest (content_path ()));
-               
                examine_content ();
 
        } catch (...) {
@@ -993,6 +909,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 +1097,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 +1257,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)
 {