Merge master.
[dcpomatic.git] / src / lib / film.cc
index 98c6c061061504a72c3821873f4d3a095c76d994..487499e32acd8698ed2ca6f6b87ab0e24e7e558c 100644 (file)
@@ -1,3 +1,5 @@
+/* -*- c-basic-offset: 8; default-tab-width: 8; -*- */
+
 /*
     Copyright (C) 2012 Carl Hetherington <cth@carlh.net>
 
@@ -93,8 +95,8 @@ Film::Film (string d, bool must_exist)
        : _playlist (new Playlist)
        , _use_dci_name (true)
        , _trust_content_headers (true)
-       , _dcp_content_type (0)
-       , _format (Format::from_id ("185"))
+       , _dcp_content_type (Config::instance()->default_dcp_content_type ())
+       , _format (Config::instance()->default_format ())
        , _scaler (Scaler::from_id ("bicubic"))
        , _trim_start (0)
        , _trim_end (0)
@@ -108,12 +110,13 @@ Film::Film (string d, bool must_exist)
        , _colour_lut (0)
        , _j2k_bandwidth (200000000)
        , _dci_metadata (Config::instance()->default_dci_metadata ())
-       , _dcp_frame_rate (0)
+       , _dcp_video_frame_rate (0)
        , _dirty (false)
 {
        set_dci_date_today ();
 
-       _playlist->ContentChanged.connect (bind (&Film::content_changed, this, _1, _2));
+       _playlist->Changed.connect (bind (&Film::playlist_changed, this));
+       _playlist->ContentChanged.connect (bind (&Film::playlist_content_changed, this, _1, _2));
        
        /* Make state.directory a complete path without ..s (where possible)
           (Code swiped from Adam Bowen on stackoverflow)
@@ -156,7 +159,7 @@ Film::Film (Film const & o)
        : boost::enable_shared_from_this<Film> (o)
        /* note: the copied film shares the original's log */
        , _log               (o._log)
-       , _playlist          (new Playlist)
+       , _playlist          (new Playlist (o._playlist))
        , _directory         (o._directory)
        , _name              (o._name)
        , _use_dci_name      (o._use_dci_name)
@@ -178,17 +181,11 @@ Film::Film (Film const & o)
        , _colour_lut        (o._colour_lut)
        , _j2k_bandwidth     (o._j2k_bandwidth)
        , _dci_metadata      (o._dci_metadata)
-       , _dcp_frame_rate    (o._dcp_frame_rate)
+       , _dcp_video_frame_rate (o._dcp_video_frame_rate)
        , _dci_date          (o._dci_date)
        , _dirty             (o._dirty)
 {
-       for (ContentList::const_iterator i = o._content.begin(); i != o._content.end(); ++i) {
-               _content.push_back ((*i)->clone ());
-       }
-       
-       _playlist->ContentChanged.connect (bind (&Film::content_changed, this, _1, _2));
-       
-       _playlist->setup (_content);
+       _playlist->ContentChanged.connect (bind (&Film::playlist_content_changed, this, _1, _2));
 }
 
 string
@@ -203,7 +200,7 @@ Film::video_state_identifier () const
        s << format()->id()
          << "_" << _playlist->video_digest()
          << "_" << crop().left << "_" << crop().right << "_" << crop().top << "_" << crop().bottom
-         << "_" << _dcp_frame_rate
+         << "_" << _dcp_video_frame_rate
          << "_" << f.first << "_" << f.second
          << "_" << scaler()->id()
          << "_" << j2k_bandwidth()
@@ -320,8 +317,8 @@ Film::make_dcp ()
                throw MissingSettingError (_("format"));
        }
 
-       if (content().empty ()) {
-               throw MissingSettingError (_("content"));
+       if (_playlist->content().empty ()) {
+               throw StringError (_("You must add some content to the DCP before creating it"));
        }
 
        if (dcp_content_type() == 0) {
@@ -405,8 +402,6 @@ Film::encoded_frames () const
 void
 Film::write_metadata () const
 {
-       ContentList the_content = content ();
-       
        boost::mutex::scoped_lock lm (_state_mutex);
        LocaleGuard lg;
 
@@ -457,13 +452,9 @@ Film::write_metadata () const
        root->add_child("ColourLUT")->add_child_text (boost::lexical_cast<string> (_colour_lut));
        root->add_child("J2KBandwidth")->add_child_text (boost::lexical_cast<string> (_j2k_bandwidth));
        _dci_metadata.as_xml (root->add_child ("DCIMetadata"));
-       root->add_child("DCPFrameRate")->add_child_text (boost::lexical_cast<string> (_dcp_frame_rate));
+       root->add_child("DCPVideoFrameRate")->add_child_text (boost::lexical_cast<string> (_dcp_video_frame_rate));
        root->add_child("DCIDate")->add_child_text (boost::gregorian::to_iso_string (_dci_date));
-       _audio_mapping.as_xml (root->add_child("AudioMapping"));
-
-       for (ContentList::iterator i = the_content.begin(); i != the_content.end(); ++i) {
-               (*i)->as_xml (root->add_child ("Content"));
-       }
+       _playlist->as_xml (root->add_child ("Playlist"));
 
        doc.write_to_file_formatted (file ("metadata.xml"));
        
@@ -534,32 +525,12 @@ Film::read_metadata ()
        _colour_lut = f.number_child<int> ("ColourLUT");
        _j2k_bandwidth = f.number_child<int> ("J2KBandwidth");
        _dci_metadata = DCIMetadata (f.node_child ("DCIMetadata"));
-       _dcp_frame_rate = f.number_child<int> ("DCPFrameRate");
+       _dcp_video_frame_rate = f.number_child<int> ("DCPVideoFrameRate");
        _dci_date = boost::gregorian::from_undelimited_string (f.string_child ("DCIDate"));
 
-       list<shared_ptr<cxml::Node> > c = f.node_children ("Content");
-       for (list<shared_ptr<cxml::Node> >::iterator i = c.begin(); i != c.end(); ++i) {
-
-               string const type = (*i)->string_child ("Type");
-               boost::shared_ptr<Content> c;
-               
-               if (type == "FFmpeg") {
-                       c.reset (new FFmpegContent (*i));
-               } else if (type == "ImageMagick") {
-                       c.reset (new ImageMagickContent (*i));
-               } else if (type == "Sndfile") {
-                       c.reset (new SndfileContent (*i));
-               }
-
-               _content.push_back (c);
-       }
-
-       /* This must come after we've loaded the content, as we're looking things up in _content */
-       _audio_mapping.set_from_xml (_content, f.node_child ("AudioMapping"));
+       _playlist->set_from_xml (f.node_child ("Playlist"));
 
        _dirty = false;
-
-       _playlist->setup (_content);
 }
 
 libdcp::Size
@@ -606,32 +577,6 @@ Film::file (string f) const
        return p.string ();
 }
 
-/** @return The sampling rate that we will resample the audio to */
-int
-Film::target_audio_sample_rate () const
-{
-       if (!has_audio ()) {
-               return 0;
-       }
-       
-       /* Resample to a DCI-approved sample rate */
-       double t = dcp_audio_sample_rate (audio_frame_rate());
-
-       FrameRateConversion frc (video_frame_rate(), dcp_frame_rate());
-
-       /* Compensate if the DCP is being run at a different frame rate
-          to the source; that is, if the video is run such that it will
-          look different in the DCP compared to the source (slower or faster).
-          skip/repeat doesn't come into effect here.
-       */
-
-       if (frc.change_speed) {
-               t *= video_frame_rate() * frc.factor() / dcp_frame_rate();
-       }
-
-       return rint (t);
-}
-
 /** @return a DCI-compliant name for a DCP of this film */
 string
 Film::dci_name (bool if_created_now) const
@@ -678,22 +623,7 @@ Film::dci_name (bool if_created_now) const
                }
        }
 
-       switch (audio_channels ()) {
-       case 1:
-               d << "_10";
-               break;
-       case 2:
-               d << "_20";
-               break;
-       case 6:
-               d << "_51";
-               break;
-       case 8:
-               d << "_71";
-               break;
-       }
-
-       d << "_2K";
+       d << "_51_2K";
 
        if (!dm.studio.empty ()) {
                d << "_" << dm.studio;
@@ -765,11 +695,11 @@ Film::set_trust_content_headers (bool t)
        }
        
        signal_changed (TRUST_CONTENT_HEADERS);
-
-       if (!_trust_content_headers && !content().empty()) {
+       
+       Playlist::ContentList content = _playlist->content ();
+       if (!_trust_content_headers && !content.empty()) {
                /* We just said that we don't trust the content's header */
-               ContentList c = content ();
-               for (ContentList::iterator i = c.begin(); i != c.end(); ++i) {
+               for (Playlist::ContentList::iterator i = content.begin(); i != content.end(); ++i) {
                        examine_content (*i);
                }
        }
@@ -1004,13 +934,13 @@ Film::set_dci_metadata (DCIMetadata m)
 
 
 void
-Film::set_dcp_frame_rate (int f)
+Film::set_dcp_video_frame_rate (int f)
 {
        {
                boost::mutex::scoped_lock lm (_state_mutex);
-               _dcp_frame_rate = f;
+               _dcp_video_frame_rate = f;
        }
-       signal_changed (DCP_FRAME_RATE);
+       signal_changed (DCP_VIDEO_FRAME_RATE);
 }
 
 void
@@ -1023,9 +953,7 @@ Film::signal_changed (Property p)
 
        switch (p) {
        case Film::CONTENT:
-               _playlist->setup (content ());
-               set_dcp_frame_rate (best_dcp_frame_rate (video_frame_rate ()));
-               set_audio_mapping (_playlist->default_audio_mapping ());
+               set_dcp_video_frame_rate (_playlist->best_dcp_frame_rate ());
                break;
        default:
                break;
@@ -1104,222 +1032,107 @@ Film::player () const
        return shared_ptr<Player> (new Player (shared_from_this (), _playlist));
 }
 
-void
-Film::add_content (shared_ptr<Content> c)
+shared_ptr<Playlist>
+Film::playlist () const
 {
-       {
-               boost::mutex::scoped_lock lm (_state_mutex);
-               _content.push_back (c);
-       }
-
-       signal_changed (CONTENT);
-
-       examine_content (c);
+       boost::mutex::scoped_lock lm (_state_mutex);
+       return _playlist;
 }
 
-void
-Film::remove_content (shared_ptr<Content> c)
+Playlist::ContentList
+Film::content () const
 {
-       {
-               boost::mutex::scoped_lock lm (_state_mutex);
-               ContentList::iterator i = find (_content.begin(), _content.end(), c);
-               if (i != _content.end ()) {
-                       _content.erase (i);
-               }
-       }
-
-       signal_changed (CONTENT);
+       return _playlist->content ();
 }
 
 void
-Film::move_content_earlier (shared_ptr<Content> c)
+Film::add_content (shared_ptr<Content> c)
 {
-       {
-               boost::mutex::scoped_lock lm (_state_mutex);
-               ContentList::iterator i = find (_content.begin(), _content.end(), c);
-               if (i == _content.begin () || i == _content.end()) {
-                       return;
-               }
-
-               ContentList::iterator j = i;
-               --j;
-
-               swap (*i, *j);
-       }
-
-       signal_changed (CONTENT);
+       _playlist->add (c);
+       examine_content (c);
 }
 
 void
-Film::move_content_later (shared_ptr<Content> c)
-{
-       {
-               boost::mutex::scoped_lock lm (_state_mutex);
-               ContentList::iterator i = find (_content.begin(), _content.end(), c);
-               if (i == _content.end()) {
-                       return;
-               }
-
-               ContentList::iterator j = i;
-               ++j;
-               if (j == _content.end ()) {
-                       return;
-               }
-
-               swap (*i, *j);
-       }
-
-       signal_changed (CONTENT);
-
-}
-
-ContentAudioFrame
-Film::audio_length () const
-{
-       return _playlist->audio_length ();
-}
-
-int
-Film::audio_channels () const
+Film::remove_content (shared_ptr<Content> c)
 {
-       return _playlist->audio_channels ();
+       _playlist->remove (c);
 }
 
-int
-Film::audio_frame_rate () const
+Time
+Film::length () const
 {
-       return _playlist->audio_frame_rate ();
+       return _playlist->length (shared_from_this ());
 }
 
 bool
-Film::has_audio () const
-{
-       return _playlist->has_audio ();
-}
-
-float
-Film::video_frame_rate () const
-{
-       return _playlist->video_frame_rate ();
-}
-
-libdcp::Size
-Film::video_size () const
+Film::has_subtitles () const
 {
-       return _playlist->video_size ();
+       return _playlist->has_subtitles ();
 }
 
-ContentVideoFrame
-Film::video_length () const
+OutputVideoFrame
+Film::best_dcp_video_frame_rate () const
 {
-       return _playlist->video_length ();
+       return _playlist->best_dcp_frame_rate ();
 }
 
-ContentVideoFrame
-Film::content_length () const
+void
+Film::playlist_content_changed (boost::weak_ptr<Content> c, int p)
 {
-       return _playlist->content_length ();
-}
+       if (p == VideoContentProperty::VIDEO_FRAME_RATE) {
+               set_dcp_video_frame_rate (_playlist->best_dcp_frame_rate ());
+       } 
 
-/** Unfortunately this is needed as the GUI has FFmpeg-specific controls */
-shared_ptr<FFmpegContent>
-Film::ffmpeg () const
-{
-       boost::mutex::scoped_lock lm (_state_mutex);
-       
-       for (ContentList::const_iterator i = _content.begin (); i != _content.end(); ++i) {
-               shared_ptr<FFmpegContent> f = boost::dynamic_pointer_cast<FFmpegContent> (*i);
-               if (f) {
-                       return f;
-               }
+       if (ui_signaller) {
+               ui_signaller->emit (boost::bind (boost::ref (ContentChanged), c, p));
        }
-
-       return shared_ptr<FFmpegContent> ();
 }
 
-vector<FFmpegSubtitleStream>
-Film::ffmpeg_subtitle_streams () const
+void
+Film::playlist_changed ()
 {
-       shared_ptr<FFmpegContent> f = ffmpeg ();
-       if (f) {
-               return f->subtitle_streams ();
-       }
-
-       return vector<FFmpegSubtitleStream> ();
-}
+       signal_changed (CONTENT);
+}      
 
-boost::optional<FFmpegSubtitleStream>
-Film::ffmpeg_subtitle_stream () const
+int
+Film::loop () const
 {
-       shared_ptr<FFmpegContent> f = ffmpeg ();
-       if (f) {
-               return f->subtitle_stream ();
-       }
-
-       return boost::none;
+       return _playlist->loop ();
 }
 
-vector<FFmpegAudioStream>
-Film::ffmpeg_audio_streams () const
+void
+Film::set_loop (int c)
 {
-       shared_ptr<FFmpegContent> f = ffmpeg ();
-       if (f) {
-               return f->audio_streams ();
-       }
-
-       return vector<FFmpegAudioStream> ();
+       _playlist->set_loop (c);
 }
 
-boost::optional<FFmpegAudioStream>
-Film::ffmpeg_audio_stream () const
+OutputAudioFrame
+Film::time_to_audio_frames (Time t) const
 {
-       shared_ptr<FFmpegContent> f = ffmpeg ();
-       if (f) {
-               return f->audio_stream ();
-       }
-
-       return boost::none;
+       return t * dcp_audio_frame_rate () / TIME_HZ;
 }
 
-void
-Film::set_ffmpeg_subtitle_stream (FFmpegSubtitleStream s)
+OutputVideoFrame
+Film::time_to_video_frames (Time t) const
 {
-       shared_ptr<FFmpegContent> f = ffmpeg ();
-       if (f) {
-               f->set_subtitle_stream (s);
-       }
+       return t * dcp_video_frame_rate () / TIME_HZ;
 }
 
-void
-Film::set_ffmpeg_audio_stream (FFmpegAudioStream s)
+Time
+Film::audio_frames_to_time (OutputAudioFrame f) const
 {
-       shared_ptr<FFmpegContent> f = ffmpeg ();
-       if (f) {
-               f->set_audio_stream (s);
-       }
+       return f * TIME_HZ / dcp_audio_frame_rate ();
 }
 
-void
-Film::set_audio_mapping (AudioMapping m)
+Time
+Film::video_frames_to_time (OutputVideoFrame f) const
 {
-       {
-               boost::mutex::scoped_lock lm (_state_mutex);
-               _audio_mapping = m;
-       }
-
-       signal_changed (AUDIO_MAPPING);
+       return f * TIME_HZ / dcp_video_frame_rate ();
 }
 
-void
-Film::content_changed (boost::weak_ptr<Content> c, int p)
+OutputAudioFrame
+Film::dcp_audio_frame_rate () const
 {
-       if (p == VideoContentProperty::VIDEO_FRAME_RATE) {
-               set_dcp_frame_rate (best_dcp_frame_rate (video_frame_rate ()));
-       } else if (p == AudioContentProperty::AUDIO_CHANNELS) {
-               set_audio_mapping (_playlist->default_audio_mapping ());
-       } 
-
-       if (ui_signaller) {
-               ui_signaller->emit (boost::bind (boost::ref (ContentChanged), c, p));
-       }
+       /* XXX */
+       return 48000;
 }