Restore digests for content.
[dcpomatic.git] / src / lib / playlist.cc
index 17ecd2f373ddb8874b070965f7cb46beba7f355e..58086d02b67cd3e57b3526d878829f71196f8fb9 100644 (file)
 */
 
 #include <boost/shared_ptr.hpp>
+#include <boost/lexical_cast.hpp>
 #include "playlist.h"
 #include "sndfile_content.h"
 #include "sndfile_decoder.h"
-#include "ffmpeg_content.h"
+#include "video_content.h"
 #include "ffmpeg_decoder.h"
-#include "imagemagick_content.h"
+#include "ffmpeg_content.h"
 #include "imagemagick_decoder.h"
+#include "job.h"
 
 using std::list;
+using std::cout;
+using std::vector;
+using std::min;
+using std::max;
+using std::string;
 using boost::shared_ptr;
+using boost::weak_ptr;
 using boost::dynamic_pointer_cast;
+using boost::lexical_cast;
 
-Playlist::Playlist (shared_ptr<const Film> f, list<shared_ptr<Content> > c)
-       : _video_from (VIDEO_NONE)
-       , _audio_from (AUDIO_NONE)
-       , _ffmpeg_decoder_done (false)
+Playlist::Playlist ()
+       : _audio_from (AUDIO_FFMPEG)
 {
-       for (list<shared_ptr<Content> >::const_iterator i = c.begin(); i != c.end(); ++i) {
-               shared_ptr<FFmpegContent> fc = dynamic_pointer_cast<FFmpegContent> (*i);
-               if (fc) {
-                       assert (!_ffmpeg);
-                       _ffmpeg = fc;
-                       _video_from = VIDEO_FFMPEG;
-                       if (_audio_from == AUDIO_NONE) {
-                               _audio_from = AUDIO_FFMPEG;
-                       }
+
+}
+
+void
+Playlist::setup (ContentList content)
+{
+       _audio_from = AUDIO_FFMPEG;
+
+       _video.clear ();
+       _sndfile.clear ();
+
+       for (list<boost::signals2::connection>::iterator i = _content_connections.begin(); i != _content_connections.end(); ++i) {
+               i->disconnect ();
+       }
+       
+       _content_connections.clear ();
+
+       for (ContentList::const_iterator i = content.begin(); i != content.end(); ++i) {
+               shared_ptr<VideoContent> vc = dynamic_pointer_cast<VideoContent> (*i);
+               if (vc) {
+                       _video.push_back (vc);
                }
                
-               shared_ptr<ImageMagickContent> ic = dynamic_pointer_cast<ImageMagickContent> (*i);
-               if (ic) {
-                       _imagemagick.push_back (ic);
-                       if (_video_from == VIDEO_NONE) {
-                               _video_from = VIDEO_IMAGEMAGICK;
-                       }
-               }
-
                shared_ptr<SndfileContent> sc = dynamic_pointer_cast<SndfileContent> (*i);
                if (sc) {
                        _sndfile.push_back (sc);
                        _audio_from = AUDIO_SNDFILE;
                }
-       }
-
-       if (_video_from == VIDEO_FFMPEG || _audio_from == AUDIO_FFMPEG) {
-               DecodeOptions o;
-               /* XXX: decodeoptions */
-               _ffmpeg_decoder.reset (new FFmpegDecoder (f, _ffmpeg, o));
-       }
-       
-       if (_video_from == VIDEO_FFMPEG) {
-               _ffmpeg_decoder->connect_video (shared_from_this ());
-       }
-
-       if (_audio_from == AUDIO_FFMPEG) {
-               _ffmpeg_decoder->connect_audio (shared_from_this ());
-       }
-
-       if (_video_from == VIDEO_IMAGEMAGICK) {
-               for (list<shared_ptr<ImageMagickContent> >::iterator i = _imagemagick.begin(); i != _imagemagick.end(); ++i) {
-                       DecodeOptions o;
-                       /* XXX: decodeoptions */
-                       shared_ptr<ImageMagickDecoder> d (new ImageMagickDecoder (f, *i, o));
-                       _imagemagick_decoders.push_back (d);
-                       d->connect_video (shared_from_this ());
-               }
 
-               _imagemagick_decoder = _imagemagick_decoders.begin ();
+               _content_connections.push_back ((*i)->Changed.connect (bind (&Playlist::content_changed, this, _1, _2)));
        }
 
-       if (_audio_from == AUDIO_SNDFILE) {
-               for (list<shared_ptr<SndfileContent> >::iterator i = _sndfile.begin(); i != _sndfile.end(); ++i) {
-                       DecodeOptions o;
-                       /* XXX: decodeoptions */
-                       shared_ptr<SndfileDecoder> d (new SndfileDecoder (f, *i, o));
-                       _sndfile_decoders.push_back (d);
-                       d->connect_audio (shared_from_this ());
-               }
-       }
+       Changed ();
 }
 
 ContentAudioFrame
 Playlist::audio_length () const
 {
+       ContentAudioFrame len = 0;
+       
        switch (_audio_from) {
-       case AUDIO_NONE:
-               return 0;
        case AUDIO_FFMPEG:
-               return _ffmpeg->audio_length ();
+               for (list<shared_ptr<const VideoContent> >::const_iterator i = _video.begin(); i != _video.end(); ++i) {
+                       shared_ptr<const FFmpegContent> fc = dynamic_pointer_cast<const FFmpegContent> (*i);
+                       if (fc) {
+                               len += fc->audio_length ();
+                       }
+               }
+               break;
        case AUDIO_SNDFILE:
-       {
-               ContentAudioFrame l = 0;
-               for (list<shared_ptr<SndfileContent> >::const_iterator i = _sndfile.begin(); i != _sndfile.end(); ++i) {
-                       l += (*i)->audio_length ();
+               for (list<shared_ptr<const SndfileContent> >::const_iterator i = _sndfile.begin(); i != _sndfile.end(); ++i) {
+                       len += (*i)->audio_length ();
                }
-               return l;
-       }
+               break;
        }
 
-       return 0;
+       return len;
 }
 
 int
 Playlist::audio_channels () const
 {
+       int channels = 0;
+       
        switch (_audio_from) {
-       case AUDIO_NONE:
-               return 0;
        case AUDIO_FFMPEG:
-               return _ffmpeg->audio_channels ();
+               for (list<shared_ptr<const VideoContent> >::const_iterator i = _video.begin(); i != _video.end(); ++i) {
+                       shared_ptr<const FFmpegContent> fc = dynamic_pointer_cast<const FFmpegContent> (*i);
+                       if (fc) {
+                               channels = max (channels, fc->audio_channels ());
+                       }
+               }
+               break;
        case AUDIO_SNDFILE:
-       {
-               int c = 0;
-               for (list<shared_ptr<SndfileContent> >::const_iterator i = _sndfile.begin(); i != _sndfile.end(); ++i) {
-                       c += (*i)->audio_channels ();
+               for (list<shared_ptr<const SndfileContent> >::const_iterator i = _sndfile.begin(); i != _sndfile.end(); ++i) {
+                       channels += (*i)->audio_channels ();
                }
-               return c;
-       }
+               break;
        }
 
-       return 0;
+       return channels;
 }
 
 int
 Playlist::audio_frame_rate () const
 {
+       /* XXX: assuming that all content has the same rate */
+       
        switch (_audio_from) {
-       case AUDIO_NONE:
-               return 0;
        case AUDIO_FFMPEG:
-               return _ffmpeg->audio_frame_rate ();
-       case AUDIO_SNDFILE:
-               return _sndfile.front()->audio_frame_rate ();
+       {
+               shared_ptr<const FFmpegContent> fc = first_ffmpeg ();
+               if (fc) {
+                       return fc->audio_frame_rate ();
+               }
+               break;
        }
-
-       return 0;
-}
-
-int64_t
-Playlist::audio_channel_layout () const
-{
-       switch (_audio_from) {
-       case AUDIO_NONE:
-               return 0;
-       case AUDIO_FFMPEG:
-               return _ffmpeg->audio_channel_layout ();
        case AUDIO_SNDFILE:
-               /* XXX */
-               return 0;
+               return _sndfile.front()->audio_frame_rate ();
        }
 
        return 0;
@@ -174,93 +149,149 @@ Playlist::audio_channel_layout () const
 float
 Playlist::video_frame_rate () const
 {
-       switch (_video_from) {
-       case VIDEO_NONE:
+       if (_video.empty ()) {
                return 0;
-       case VIDEO_FFMPEG:
-               return _ffmpeg->video_frame_rate ();
-       case VIDEO_IMAGEMAGICK:
-               return 24;
        }
-
-       return 0;
+       
+       /* XXX: assuming all the same */
+       return _video.front()->video_frame_rate ();
 }
 
 libdcp::Size
 Playlist::video_size () const
 {
-       switch (_video_from) {
-       case VIDEO_NONE:
+       if (_video.empty ()) {
                return libdcp::Size ();
-       case VIDEO_FFMPEG:
-               return _ffmpeg->video_size ();
-       case VIDEO_IMAGEMAGICK:
-               /* XXX */
-               return _imagemagick.front()->video_size ();
        }
 
-       return libdcp::Size ();
+       /* XXX: assuming all the same */
+       return _video.front()->video_size ();
+}
+
+ContentVideoFrame
+Playlist::video_length () const
+{
+       ContentVideoFrame len = 0;
+       for (list<shared_ptr<const VideoContent> >::const_iterator i = _video.begin(); i != _video.end(); ++i) {
+               len += (*i)->video_length ();
+       }
+       
+       return len;
 }
 
 bool
 Playlist::has_audio () const
 {
-       return _audio_from != AUDIO_NONE;
+       /* XXX */
+       return true;
 }
-               
+
 void
-Playlist::disable_video ()
+Playlist::content_changed (weak_ptr<Content> c, int p)
 {
-       _video_from = VIDEO_NONE;
+       ContentChanged (c, p);
 }
 
-bool
-Playlist::pass ()
+shared_ptr<const FFmpegContent>
+Playlist::first_ffmpeg () const
 {
-       bool done = true;
+       for (list<shared_ptr<const VideoContent> >::const_iterator i = _video.begin(); i != _video.end(); ++i) {
+               shared_ptr<const FFmpegContent> fc = dynamic_pointer_cast<const FFmpegContent> (*i);
+               if (fc) {
+                       return fc;
+               }
+       }
+
+       return shared_ptr<const FFmpegContent> ();
+}
        
-       if (_video_from == VIDEO_FFMPEG || _audio_from == AUDIO_FFMPEG) {
-               if (!_ffmpeg_decoder_done) {
-                       if (_ffmpeg_decoder->pass ()) {
-                               _ffmpeg_decoder_done = true;
-                       } else {
-                               done = false;
+
+AudioMapping
+Playlist::default_audio_mapping () const
+{
+       AudioMapping m;
+
+       switch (_audio_from) {
+       case AUDIO_FFMPEG:
+       {
+               shared_ptr<const FFmpegContent> fc = first_ffmpeg ();
+               if (!fc) {
+                       break;
+               }
+               
+               /* XXX: assumes all the same */
+               if (fc->audio_channels() == 1) {
+                       /* Map mono sources to centre */
+                       m.add (AudioMapping::Channel (fc, 0), libdcp::CENTRE);
+               } else {
+                       int const N = min (fc->audio_channels (), MAX_AUDIO_CHANNELS);
+                       /* Otherwise just start with a 1:1 mapping */
+                       for (int i = 0; i < N; ++i) {
+                               m.add (AudioMapping::Channel (fc, i), (libdcp::Channel) i);
                        }
                }
+               break;
        }
 
-       if (_video_from == VIDEO_IMAGEMAGICK) {
-               if (_imagemagick_decoder != _imagemagick_decoders.end ()) {
-                       if ((*_imagemagick_decoder)->pass ()) {
-                               _imagemagick_decoder++;
+       case AUDIO_SNDFILE:
+       {
+               int n = 0;
+               for (list<shared_ptr<const SndfileContent> >::const_iterator i = _sndfile.begin(); i != _sndfile.end(); ++i) {
+                       for (int j = 0; j < (*i)->audio_channels(); ++j) {
+                               m.add (AudioMapping::Channel (*i, j), (libdcp::Channel) n);
+                               ++n;
+                               if (n >= MAX_AUDIO_CHANNELS) {
+                                       break;
+                               }
                        }
-
-                       if (_imagemagick_decoder != _imagemagick_decoders.end ()) {
-                               done = false;
+                       if (n >= MAX_AUDIO_CHANNELS) {
+                               break;
                        }
                }
+               break;
+       }
        }
 
-       /* XXX: sndfile */
-
-       return done;
+       return m;
 }
 
-void
-Playlist::set_progress (shared_ptr<Job> job)
+string
+Playlist::audio_digest () const
 {
-       /* XXX */
-}
+       string t;
+       
+       switch (_audio_from) {
+       case AUDIO_FFMPEG:
+               for (list<shared_ptr<const VideoContent> >::const_iterator i = _video.begin(); i != _video.end(); ++i) {
+                       shared_ptr<const FFmpegContent> fc = dynamic_pointer_cast<const FFmpegContent> (*i);
+                       if (fc) {
+                               t += (*i)->digest ();
+                               t += lexical_cast<string> (fc->audio_stream()->id);
+                       }
+               }
+               break;
+       case AUDIO_SNDFILE:
+               for (list<shared_ptr<const SndfileContent> >::const_iterator i = _sndfile.begin(); i != _sndfile.end(); ++i) {
+                       t += (*i)->digest ();
+               }
+               break;
+       }
 
-void
-Playlist::process_video (shared_ptr<Image> i, bool same, shared_ptr<Subtitle> s)
-{
-       Video (i, same, s);
+       return md5_digest (t.c_str(), t.length());
 }
 
-void
-Playlist::process_audio (shared_ptr<AudioBuffers> b)
+string
+Playlist::video_digest () const
 {
-       Audio (b);
-}
+       string t;
+       
+       for (list<shared_ptr<const VideoContent> >::const_iterator i = _video.begin(); i != _video.end(); ++i) {
+               t += (*i)->digest ();
+               shared_ptr<const FFmpegContent> fc = dynamic_pointer_cast<const FFmpegContent> (*i);
+               if (fc) {
+                       t += fc->subtitle_stream()->id;
+               }
+       }
 
+       return md5_digest (t.c_str(), t.length());
+}