*/
#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;
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());
+}