#define DCPOMATIC_AUDIO_CONTENT_H
#include "content.h"
+#include "audio_mapping.h"
namespace cxml {
class Node;
virtual ContentAudioFrame audio_length () const = 0;
virtual int content_audio_frame_rate () const = 0;
virtual int output_audio_frame_rate (boost::shared_ptr<const Film>) const = 0;
+ virtual AudioMapping audio_mapping () const = 0;
};
#endif
+/* -*- c-basic-offset: 8; default-tab-width: 8; -*- */
+
/*
Copyright (C) 2013 Carl Hetherington <cth@carlh.net>
using std::string;
using boost::shared_ptr;
+using boost::lexical_cast;
Content::Content (boost::filesystem::path f)
: _file (f)
+ , _time (0)
{
}
{
_file = node->string_child ("File");
_digest = node->string_child ("Digest");
+ _time = node->number_child<Time> ("Time");
}
Content::Content (Content const & o)
: boost::enable_shared_from_this<Content> (o)
, _file (o._file)
, _digest (o._digest)
+ , _time (o._time)
{
}
boost::mutex::scoped_lock lm (_mutex);
node->add_child("File")->add_child_text (_file.string());
node->add_child("Digest")->add_child_text (_digest);
+ node->add_child("Time")->add_child_text (lexical_cast<string> (_time));
}
void
return _digest;
}
+ Time time () const {
+ boost::mutex::scoped_lock lm (_mutex);
+ return _time;
+ }
+
boost::signals2::signal<void (boost::weak_ptr<Content>, int)> Changed;
protected:
private:
boost::filesystem::path _file;
std::string _digest;
+ Time _time;
};
#endif
{
list<shared_ptr<cxml::Node> > c = node->node_children ("SubtitleStream");
for (list<shared_ptr<cxml::Node> >::const_iterator i = c.begin(); i != c.end(); ++i) {
- _subtitle_streams.push_back (FFmpegSubtitleStream (*i));
+ _subtitle_streams.push_back (shared_ptr<FFmpegSubtitleStream> (new FFmpegSubtitleStream (*i)));
if ((*i)->optional_number_child<int> ("Selected")) {
_subtitle_stream = _subtitle_streams.back ();
}
c = node->node_children ("AudioStream");
for (list<shared_ptr<cxml::Node> >::const_iterator i = c.begin(); i != c.end(); ++i) {
- _audio_streams.push_back (FFmpegAudioStream (*i));
+ _audio_streams.push_back (shared_ptr<FFmpegAudioStream> (new FFmpegAudioStream (*i)));
if ((*i)->optional_number_child<int> ("Selected")) {
_audio_stream = _audio_streams.back ();
}
boost::mutex::scoped_lock lm (_mutex);
- for (vector<FFmpegSubtitleStream>::const_iterator i = _subtitle_streams.begin(); i != _subtitle_streams.end(); ++i) {
+ for (vector<shared_ptr<FFmpegSubtitleStream> >::const_iterator i = _subtitle_streams.begin(); i != _subtitle_streams.end(); ++i) {
xmlpp::Node* t = node->add_child("SubtitleStream");
- if (_subtitle_stream && *i == _subtitle_stream.get()) {
+ if (_subtitle_stream && *i == _subtitle_stream) {
t->add_child("Selected")->add_child_text("1");
}
- i->as_xml (t);
+ (*i)->as_xml (t);
}
- for (vector<FFmpegAudioStream>::const_iterator i = _audio_streams.begin(); i != _audio_streams.end(); ++i) {
+ for (vector<shared_ptr<FFmpegAudioStream> >::const_iterator i = _audio_streams.begin(); i != _audio_streams.end(); ++i) {
xmlpp::Node* t = node->add_child("AudioStream");
- if (_audio_stream && *i == _audio_stream.get()) {
+ if (_audio_stream && *i == _audio_stream) {
t->add_child("Selected")->add_child_text("1");
}
- i->as_xml (t);
+ (*i)->as_xml (t);
}
}
}
void
-FFmpegContent::set_subtitle_stream (FFmpegSubtitleStream s)
+FFmpegContent::set_subtitle_stream (shared_ptr<FFmpegSubtitleStream> s)
{
{
boost::mutex::scoped_lock lm (_mutex);
}
void
-FFmpegContent::set_audio_stream (FFmpegAudioStream s)
+FFmpegContent::set_audio_stream (shared_ptr<FFmpegAudioStream> s)
{
{
boost::mutex::scoped_lock lm (_mutex);
ContentAudioFrame
FFmpegContent::audio_length () const
{
+ int const cafr = content_audio_frame_rate ();
+ int const vfr = video_frame_rate ();
+ ContentVideoFrame const vl = video_length ();
+
+ boost::mutex::scoped_lock lm (_mutex);
if (!_audio_stream) {
return 0;
}
- return video_frames_to_audio_frames (_video_length, content_audio_frame_rate(), video_frame_rate());
+ return video_frames_to_audio_frames (vl, cafr, vfr);
}
int
FFmpegContent::audio_channels () const
{
+ boost::mutex::scoped_lock lm (_mutex);
+
if (!_audio_stream) {
return 0;
}
int
FFmpegContent::content_audio_frame_rate () const
{
+ boost::mutex::scoped_lock lm (_mutex);
+
if (!_audio_stream) {
return 0;
}
id = node->number_child<int> ("Id");
frame_rate = node->number_child<int> ("FrameRate");
channels = node->number_child<int64_t> ("Channels");
+ mapping = AudioMapping (node->node_child ("Mapping"));
}
void
root->add_child("Id")->add_child_text (lexical_cast<string> (id));
root->add_child("FrameRate")->add_child_text (lexical_cast<string> (frame_rate));
root->add_child("Channels")->add_child_text (lexical_cast<string> (channels));
+ mapping.as_xml (root->add_child("Mapping"));
}
/** Construct a SubtitleStream from a value returned from to_string().
FrameRateConversion frc (video_frame_rate (), film->dcp_video_frame_rate ());
return video_length() * frc.factor() * TIME_HZ / film->dcp_video_frame_rate ();
}
+
+AudioMapping
+FFmpegContent::audio_mapping () const
+{
+ boost::mutex::scoped_lock lm (_mutex);
+
+ if (!_audio_stream) {
+ return AudioMapping ();
+ }
+
+ cout << "returning am from stream " << _audio_stream.get() << ".\n";
+ return _audio_stream->mapping;
+}
+
, id (i)
, frame_rate (f)
, channels (c)
+ , mapping (c)
{}
FFmpegAudioStream (boost::shared_ptr<const cxml::Node>);
int id;
int frame_rate;
int channels;
+ AudioMapping mapping;
};
extern bool operator== (FFmpegAudioStream const & a, FFmpegAudioStream const & b);
ContentAudioFrame audio_length () const;
int content_audio_frame_rate () const;
int output_audio_frame_rate (boost::shared_ptr<const Film>) const;
+ AudioMapping audio_mapping () const;
- std::vector<FFmpegSubtitleStream> subtitle_streams () const {
+ std::vector<boost::shared_ptr<FFmpegSubtitleStream> > subtitle_streams () const {
boost::mutex::scoped_lock lm (_mutex);
return _subtitle_streams;
}
- boost::optional<FFmpegSubtitleStream> subtitle_stream () const {
+ boost::shared_ptr<FFmpegSubtitleStream> subtitle_stream () const {
boost::mutex::scoped_lock lm (_mutex);
return _subtitle_stream;
}
- std::vector<FFmpegAudioStream> audio_streams () const {
+ std::vector<boost::shared_ptr<FFmpegAudioStream> > audio_streams () const {
boost::mutex::scoped_lock lm (_mutex);
return _audio_streams;
}
- boost::optional<FFmpegAudioStream> audio_stream () const {
+ boost::shared_ptr<FFmpegAudioStream> audio_stream () const {
boost::mutex::scoped_lock lm (_mutex);
return _audio_stream;
}
- void set_subtitle_stream (FFmpegSubtitleStream);
- void set_audio_stream (FFmpegAudioStream);
+ void set_subtitle_stream (boost::shared_ptr<FFmpegSubtitleStream>);
+ void set_audio_stream (boost::shared_ptr<FFmpegAudioStream>);
private:
- std::vector<FFmpegSubtitleStream> _subtitle_streams;
- boost::optional<FFmpegSubtitleStream> _subtitle_stream;
- std::vector<FFmpegAudioStream> _audio_streams;
- boost::optional<FFmpegAudioStream> _audio_stream;
+ std::vector<boost::shared_ptr<FFmpegSubtitleStream> > _subtitle_streams;
+ boost::shared_ptr<FFmpegSubtitleStream> _subtitle_stream;
+ std::vector<boost::shared_ptr<FFmpegAudioStream> > _audio_streams;
+ boost::shared_ptr<FFmpegAudioStream> _audio_stream;
};
#endif
}
_audio_streams.push_back (
- FFmpegAudioStream (stream_name (s), i, s->codec->sample_rate, s->codec->channels)
+ shared_ptr<FFmpegAudioStream> (
+ new FFmpegAudioStream (stream_name (s), i, s->codec->sample_rate, s->codec->channels)
+ )
);
} else if (s->codec->codec_type == AVMEDIA_TYPE_SUBTITLE) {
- _subtitle_streams.push_back (FFmpegSubtitleStream (stream_name (s), i));
+ _subtitle_streams.push_back (shared_ptr<FFmpegSubtitleStream> (new FFmpegSubtitleStream (stream_name (s), i)));
}
}
+/* -*- c-basic-offset: 8; default-tab-width: 8; -*- */
+
/*
Copyright (C) 2012 Carl Hetherington <cth@carlh.net>
int sample_aspect_ratio_numerator () const;
int sample_aspect_ratio_denominator () const;
- std::vector<FFmpegSubtitleStream> subtitle_streams () const {
+ std::vector<boost::shared_ptr<FFmpegSubtitleStream> > subtitle_streams () const {
return _subtitle_streams;
}
- std::vector<FFmpegAudioStream> audio_streams () const {
+ std::vector<boost::shared_ptr<FFmpegAudioStream> > audio_streams () const {
return _audio_streams;
}
std::list<boost::shared_ptr<FilterGraph> > _filter_graphs;
boost::mutex _filter_graphs_mutex;
- std::vector<FFmpegSubtitleStream> _subtitle_streams;
- std::vector<FFmpegAudioStream> _audio_streams;
+ std::vector<boost::shared_ptr<FFmpegSubtitleStream> > _subtitle_streams;
+ std::vector<boost::shared_ptr<FFmpegAudioStream> > _audio_streams;
bool _decode_video;
bool _decode_audio;
throw MissingSettingError (_("format"));
}
- if (_playlist->regions().empty ()) {
+ if (_playlist->content().empty ()) {
throw StringError (_("You must add some content to the DCP before creating it"));
}
signal_changed (TRUST_CONTENT_HEADERS);
- Playlist::RegionList regions = _playlist->regions ();
- if (!_trust_content_headers && !regions.empty()) {
+ Playlist::ContentList content = _playlist->content ();
+ if (!_trust_content_headers && !content.empty()) {
/* We just said that we don't trust the content's header */
- for (Playlist::RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
- examine_content ((*i)->content);
+ for (Playlist::ContentList::iterator i = content.begin(); i != content.end(); ++i) {
+ examine_content (*i);
}
}
}
return _playlist;
}
-Playlist::RegionList
-Film::regions () const
+Playlist::ContentList
+Film::content () const
{
- return _playlist->regions ();
+ return _playlist->content ();
}
void
/* Proxies for some Playlist methods */
- Playlist::RegionList regions () const;
+ Playlist::ContentList content () const;
Time length () const;
bool has_subtitles () const;
*/
Time earliest_pos = TIME_MAX;
- shared_ptr<RegionDecoder> earliest;
+ shared_ptr<DecoderRecord> earliest;
Time next_wait = TIME_MAX;
- for (list<shared_ptr<RegionDecoder> >::iterator i = _decoders.begin(); i != _decoders.end(); ++i) {
- Time const ts = (*i)->region->time;
- Time const te = (*i)->region->time + (*i)->region->content->length (_film);
+ for (list<shared_ptr<DecoderRecord> >::iterator i = _decoders.begin(); i != _decoders.end(); ++i) {
+ Time const ts = (*i)->content->time();
+ Time const te = (*i)->content->time() + (*i)->content->length (_film);
if (ts <= _position && te > _position) {
Time const pos = ts + (*i)->last;
if (pos < earliest_pos) {
}
void
-Player::process_video (shared_ptr<RegionDecoder> rd, shared_ptr<const Image> image, bool same, shared_ptr<Subtitle> sub, Time time)
+Player::process_video (shared_ptr<DecoderRecord> dr, shared_ptr<const Image> image, bool same, shared_ptr<Subtitle> sub, Time time)
{
- shared_ptr<VideoDecoder> vd = dynamic_pointer_cast<VideoDecoder> (rd->decoder);
+ shared_ptr<VideoDecoder> vd = dynamic_pointer_cast<VideoDecoder> (dr->decoder);
- Time const global_time = rd->region->time + time;
+ Time const global_time = dr->content->time() + time;
while ((global_time - _last_video) > 1) {
/* Fill in with black */
emit_black_frame ();
}
Video (image, same, sub, global_time);
- rd->last = time;
+ dr->last = time;
_last_video = global_time;
_last_was_black = false;
}
void
-Player::process_audio (shared_ptr<RegionDecoder> rd, shared_ptr<const AudioBuffers> audio, Time time)
+Player::process_audio (shared_ptr<DecoderRecord> dr, shared_ptr<const AudioBuffers> audio, Time time)
{
/* XXX: mapping */
_audio_buffers.ensure_size (time - _last_audio + audio->frames());
_audio_buffers.accumulate (audio.get(), 0, _film->time_to_audio_frames (time - _last_audio));
- rd->last = time + _film->audio_frames_to_time (audio->frames ());
+ dr->last = time + _film->audio_frames_to_time (audio->frames ());
}
/** @return true on error */
void
Player::setup_decoders ()
{
- list<shared_ptr<RegionDecoder> > old_decoders = _decoders;
+ list<shared_ptr<DecoderRecord> > old_decoders = _decoders;
_decoders.clear ();
- Playlist::RegionList regions = _playlist->regions ();
- for (Playlist::RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
+ Playlist::ContentList content = _playlist->content ();
+ for (Playlist::ContentList::iterator i = content.begin(); i != content.end(); ++i) {
- shared_ptr<RegionDecoder> rd (new RegionDecoder);
- rd->region = *i;
+ shared_ptr<DecoderRecord> dr (new DecoderRecord);
+ dr->content = *i;
/* XXX: into content? */
- shared_ptr<const FFmpegContent> fc = dynamic_pointer_cast<const FFmpegContent> ((*i)->content);
+ shared_ptr<const FFmpegContent> fc = dynamic_pointer_cast<const FFmpegContent> (*i);
if (fc) {
shared_ptr<FFmpegDecoder> fd (new FFmpegDecoder (_film, fc, _video, _audio, _subtitles));
- fd->Video.connect (bind (&Player::process_video, this, rd, _1, _2, _3, _4));
- fd->Audio.connect (bind (&Player::process_audio, this, rd, _1, _2));
+ fd->Video.connect (bind (&Player::process_video, this, dr, _1, _2, _3, _4));
+ fd->Audio.connect (bind (&Player::process_audio, this, dr, _1, _2));
- rd->decoder = fd;
+ dr->decoder = fd;
}
- shared_ptr<const ImageMagickContent> ic = dynamic_pointer_cast<const ImageMagickContent> ((*i)->content);
+ shared_ptr<const ImageMagickContent> ic = dynamic_pointer_cast<const ImageMagickContent> (*i);
if (ic) {
shared_ptr<ImageMagickDecoder> id;
/* See if we can re-use an old ImageMagickDecoder */
- for (list<shared_ptr<RegionDecoder> >::const_iterator i = old_decoders.begin(); i != old_decoders.end(); ++i) {
+ for (list<shared_ptr<DecoderRecord> >::const_iterator i = old_decoders.begin(); i != old_decoders.end(); ++i) {
shared_ptr<ImageMagickDecoder> imd = dynamic_pointer_cast<ImageMagickDecoder> ((*i)->decoder);
if (imd && imd->content() == ic) {
id = imd;
if (!id) {
id.reset (new ImageMagickDecoder (_film, ic));
- id->Video.connect (bind (&Player::process_video, this, rd, _1, _2, _3, _4));
+ id->Video.connect (bind (&Player::process_video, this, dr, _1, _2, _3, _4));
}
- rd->decoder = id;
+ dr->decoder = id;
}
- shared_ptr<const SndfileContent> sc = dynamic_pointer_cast<const SndfileContent> ((*i)->content);
+ shared_ptr<const SndfileContent> sc = dynamic_pointer_cast<const SndfileContent> (*i);
if (sc) {
shared_ptr<AudioDecoder> sd (new SndfileDecoder (_film, sc));
- sd->Audio.connect (bind (&Player::process_audio, this, rd, _1, _2));
+ sd->Audio.connect (bind (&Player::process_audio, this, dr, _1, _2));
- rd->decoder = sd;
+ dr->decoder = sd;
}
- _decoders.push_back (rd);
+ _decoders.push_back (dr);
}
_position = 0;
private:
- struct RegionDecoder
+ struct DecoderRecord
{
- RegionDecoder ()
+ DecoderRecord ()
: last (0)
{}
- boost::shared_ptr<Playlist::Region> region;
+ boost::shared_ptr<Content> content;
boost::shared_ptr<Decoder> decoder;
Time last;
};
- void process_video (boost::shared_ptr<RegionDecoder>, boost::shared_ptr<const Image>, bool, boost::shared_ptr<Subtitle>, Time);
- void process_audio (boost::shared_ptr<RegionDecoder>, boost::shared_ptr<const AudioBuffers>, Time);
+ void process_video (boost::shared_ptr<DecoderRecord>, boost::shared_ptr<const Image>, bool, boost::shared_ptr<Subtitle>, Time);
+ void process_audio (boost::shared_ptr<DecoderRecord>, boost::shared_ptr<const AudioBuffers>, Time);
void setup_decoders ();
void playlist_changed ();
void content_changed (boost::weak_ptr<Content>, int);
/** Our decoders are ready to go; if this is false the decoders must be (re-)created before they are used */
bool _have_valid_decoders;
- std::list<boost::shared_ptr<RegionDecoder> > _decoders;
+ std::list<boost::shared_ptr<DecoderRecord> > _decoders;
Time _position;
AudioBuffers _audio_buffers;
Playlist::Playlist (shared_ptr<const Playlist> other)
: _loop (other->_loop)
{
- for (RegionList::const_iterator i = other->_regions.begin(); i != other->_regions.end(); ++i) {
- _regions.push_back (shared_ptr<Region> (new Region ((*i)->content->clone(), (*i)->time, this)));
+ for (ContentList::const_iterator i = other->_content.begin(); i != other->_content.end(); ++i) {
+ _content.push_back ((*i)->clone ());
}
}
+Playlist::~Playlist ()
+{
+ _content.clear ();
+ reconnect ();
+}
+
void
Playlist::content_changed (weak_ptr<Content> c, int p)
{
{
string t;
- for (RegionList::const_iterator i = _regions.begin(); i != _regions.end(); ++i) {
- if (!dynamic_pointer_cast<const AudioContent> ((*i)->content)) {
+ for (ContentList::const_iterator i = _content.begin(); i != _content.end(); ++i) {
+ if (!dynamic_pointer_cast<const AudioContent> (*i)) {
continue;
}
- t += (*i)->content->digest ();
+ t += (*i)->digest ();
- shared_ptr<const FFmpegContent> fc = dynamic_pointer_cast<const FFmpegContent> ((*i)->content);
+ shared_ptr<const FFmpegContent> fc = dynamic_pointer_cast<const FFmpegContent> (*i);
if (fc) {
t += lexical_cast<string> (fc->audio_stream()->id);
}
{
string t;
- for (RegionList::const_iterator i = _regions.begin(); i != _regions.end(); ++i) {
- if (!dynamic_pointer_cast<const VideoContent> ((*i)->content)) {
+ for (ContentList::const_iterator i = _content.begin(); i != _content.end(); ++i) {
+ if (!dynamic_pointer_cast<const VideoContent> (*i)) {
continue;
}
- t += (*i)->content->digest ();
- shared_ptr<const FFmpegContent> fc = dynamic_pointer_cast<const FFmpegContent> ((*i)->content);
+ t += (*i)->digest ();
+ shared_ptr<const FFmpegContent> fc = dynamic_pointer_cast<const FFmpegContent> (*i);
if (fc && fc->subtitle_stream()) {
t += fc->subtitle_stream()->id;
}
return md5_digest (t.c_str(), t.length());
}
+/** @param node <Playlist> node */
void
Playlist::set_from_xml (shared_ptr<const cxml::Node> node)
{
- list<shared_ptr<cxml::Node> > c = node->node_children ("Region");
+ list<shared_ptr<cxml::Node> > c = node->node_children ("Content");
for (list<shared_ptr<cxml::Node> >::iterator i = c.begin(); i != c.end(); ++i) {
- _regions.push_back (shared_ptr<Region> (new Region (*i, this)));
+ string const type = (*i)->string_child ("Type");
+
+ boost::shared_ptr<Content> content;
+
+ if (type == "FFmpeg") {
+ content.reset (new FFmpegContent (*i));
+ } else if (type == "ImageMagick") {
+ content.reset (new ImageMagickContent (*i));
+ } else if (type == "Sndfile") {
+ content.reset (new SndfileContent (*i));
+ }
}
_loop = node->number_child<int> ("Loop");
}
+/** @param node <Playlist> node */
void
Playlist::as_xml (xmlpp::Node* node)
{
- for (RegionList::iterator i = _regions.begin(); i != _regions.end(); ++i) {
- (*i)->as_xml (node->add_child ("Region"));
+ for (ContentList::iterator i = _content.begin(); i != _content.end(); ++i) {
+ (*i)->as_xml (node->add_child ("Content"));
}
node->add_child("Loop")->add_child_text(lexical_cast<string> (_loop));
void
Playlist::add (shared_ptr<Content> c)
{
- _regions.push_back (shared_ptr<Region> (new Region (c, 0, this)));
+ _content.push_back (c);
+ reconnect ();
Changed ();
}
void
Playlist::remove (shared_ptr<Content> c)
{
- RegionList::iterator i = _regions.begin ();
- while (i != _regions.end() && (*i)->content != c) {
+ ContentList::iterator i = _content.begin ();
+ while (i != _content.end() && *i != c) {
++i;
}
- if (i != _regions.end ()) {
- _regions.erase (i);
+ if (i != _content.end ()) {
+ _content.erase (i);
Changed ();
}
}
bool
Playlist::has_subtitles () const
{
- for (RegionList::const_iterator i = _regions.begin(); i != _regions.end(); ++i) {
- shared_ptr<const FFmpegContent> fc = dynamic_pointer_cast<FFmpegContent> ((*i)->content);
+ for (ContentList::const_iterator i = _content.begin(); i != _content.end(); ++i) {
+ shared_ptr<const FFmpegContent> fc = dynamic_pointer_cast<FFmpegContent> (*i);
if (fc && !fc->subtitle_streams().empty()) {
return true;
}
return false;
}
-Playlist::Region::Region (shared_ptr<Content> c, Time t, Playlist* p)
- : content (c)
- , time (t)
-{
- connection = c->Changed.connect (bind (&Playlist::content_changed, p, _1, _2));
-}
-
-Playlist::Region::Region (shared_ptr<const cxml::Node> node, Playlist* p)
-{
- shared_ptr<const cxml::Node> content_node = node->node_child ("Content");
- string const type = content_node->string_child ("Type");
-
- if (type == "FFmpeg") {
- content.reset (new FFmpegContent (content_node));
- } else if (type == "ImageMagick") {
- content.reset (new ImageMagickContent (content_node));
- } else if (type == "Sndfile") {
- content.reset (new SndfileContent (content_node));
- }
-
- time = node->number_child<Time> ("Time");
- audio_mapping = AudioMapping (node->node_child ("AudioMapping"));
- connection = content->Changed.connect (bind (&Playlist::content_changed, p, _1, _2));
-}
-
-void
-Playlist::Region::as_xml (xmlpp::Node* node) const
-{
- xmlpp::Node* sub = node->add_child ("Content");
- content->as_xml (sub);
- node->add_child ("Time")->add_child_text (lexical_cast<string> (time));
- audio_mapping.as_xml (node->add_child ("AudioMapping"));
-}
-
class FrameRateCandidate
{
public:
while (i != candidates.end()) {
float this_error = std::numeric_limits<float>::max ();
- for (RegionList::const_iterator j = _regions.begin(); j != _regions.end(); ++j) {
- shared_ptr<VideoContent> vc = dynamic_pointer_cast<VideoContent> ((*j)->content);
+ for (ContentList::const_iterator j = _content.begin(); j != _content.end(); ++j) {
+ shared_ptr<VideoContent> vc = dynamic_pointer_cast<VideoContent> (*j);
if (!vc) {
continue;
}
Playlist::length (shared_ptr<const Film> film) const
{
Time len = 0;
- for (RegionList::const_iterator i = _regions.begin(); i != _regions.end(); ++i) {
- Time const t = (*i)->time + (*i)->content->length (film);
+ for (ContentList::const_iterator i = _content.begin(); i != _content.end(); ++i) {
+ Time const t = (*i)->time() + (*i)->length (film);
len = max (len, t);
}
return len;
}
+
+void
+Playlist::reconnect ()
+{
+ for (list<boost::signals2::connection>::iterator i = _content_connections.begin(); i != _content_connections.end(); ++i) {
+ i->disconnect ();
+ }
+
+ _content_connections.clear ();
+
+ for (ContentList::iterator i = _content.begin(); i != _content.end(); ++i) {
+ _content_connections.push_back ((*i)->Changed.connect (bind (&Playlist::content_changed, this, _1, _2)));
+ }
+}
+
class SndfileDecoder;
class Job;
class Film;
+class Region;
/** @class Playlist
* @brief A set of content files (video and audio), with knowledge of how they should be arranged into
public:
Playlist ();
Playlist (boost::shared_ptr<const Playlist>);
+ ~Playlist ();
void as_xml (xmlpp::Node *);
void set_from_xml (boost::shared_ptr<const cxml::Node>);
bool has_subtitles () const;
- struct Region
- {
- Region ()
- : time (0)
- {}
-
- Region (boost::shared_ptr<Content> c, Time t, Playlist* p);
- Region (boost::shared_ptr<const cxml::Node>, Playlist* p);
-
- void as_xml (xmlpp::Node *) const;
-
- boost::shared_ptr<Content> content;
- Time time;
- /* XXX: obviously not used for video-only; there should
- really by AudioRegion / VideoRegion etc.
- */
- AudioMapping audio_mapping;
- boost::signals2::connection connection;
- };
-
- typedef std::vector<boost::shared_ptr<Region> > RegionList;
+ typedef std::vector<boost::shared_ptr<Content> > ContentList;
- RegionList regions () const {
- return _regions;
+ ContentList content () const {
+ return _content;
}
std::string audio_digest () const;
private:
void content_changed (boost::weak_ptr<Content>, int);
+ void reconnect ();
- RegionList _regions;
+ ContentList _content;
int _loop;
+ std::list<boost::signals2::connection> _content_connections;
};
#endif
}
int output_audio_frame_rate (boost::shared_ptr<const Film>) const;
+
+ AudioMapping audio_mapping () const {
+ boost::mutex::scoped_lock lm (_mutex);
+ return _audio_mapping;
+ }
static bool valid_file (boost::filesystem::path);
int _audio_channels;
ContentAudioFrame _audio_length;
int _audio_frame_rate;
+ AudioMapping _audio_mapping;
};
AudioMapping mapping;
for (int i = 0; i < _grid->GetNumberRows(); ++i) {
- for (int j = 0; j < _grid->GetNumberCols(); ++j) {
+ for (int j = 1; j < _grid->GetNumberCols(); ++j) {
if (_grid->GetCellValue (i, j) == wxT ("1")) {
- mapping.add (i, static_cast<libdcp::Channel> (j));
+ mapping.add (i, static_cast<libdcp::Channel> (j - 1));
}
}
}
using boost::lexical_cast;
using boost::dynamic_pointer_cast;
-FFmpegContentDialog::FFmpegContentDialog (wxWindow* parent, shared_ptr<Playlist::Region> region)
+FFmpegContentDialog::FFmpegContentDialog (wxWindow* parent, shared_ptr<FFmpegContent> content)
: wxDialog (parent, wxID_ANY, _("Video"))
- , _region (region)
- , _content (dynamic_pointer_cast<FFmpegContent> (region->content))
+ , _content (content)
{
wxFlexGridSizer* grid = new wxFlexGridSizer (3, 6, 6);
grid->AddGrowableCol (1, 1);
grid->Add (_subtitle_stream, 1, wxEXPAND | wxALL, 6);
grid->AddSpacer (0);
- shared_ptr<FFmpegContent> content = _content.lock ();
- assert (content);
-
_audio_stream->Clear ();
vector<FFmpegAudioStream> a = content->audio_streams ();
for (vector<FFmpegAudioStream>::iterator i = a.begin(); i != a.end(); ++i) {
overall_sizer->Add (grid, 1, wxEXPAND | wxALL, 6);
_audio_mapping = new AudioMappingView (this);
- _audio_mapping->set_mapping (region->audio_mapping);
+ _audio_mapping->set_mapping (content->audio_mapping ());
overall_sizer->Add (_audio_mapping, 1, wxEXPAND | wxALL, 6);
wxSizer* buttons = CreateSeparatedButtonSizer (wxOK);
return;
}
- /* XXX: set mapping in playlist */
+ cout << "setting map in stream " << content->audio_stream().get() << "\n";
+ content->audio_stream()->mapping = m;
}
#include <boost/shared_ptr.hpp>
#include <boost/weak_ptr.hpp>
#include "lib/audio_mapping.h"
-#include "lib/playlist.h"
class wxSpinCtrl;
class FFmpegContent;
class AudioMappingView;
+class Region;
class FFmpegContentDialog : public wxDialog
{
public:
- FFmpegContentDialog (wxWindow *, boost::shared_ptr<Playlist::Region>);
+ FFmpegContentDialog (wxWindow *, boost::shared_ptr<FFmpegContent>);
private:
void audio_stream_changed (wxCommandEvent &);
void subtitle_stream_changed (wxCommandEvent &);
void audio_mapping_changed (AudioMapping);
- boost::weak_ptr<Playlist::Region> _region;
boost::weak_ptr<FFmpegContent> _content;
wxChoice* _audio_stream;
wxStaticText* _audio_description;
} else if (property == VideoContentProperty::VIDEO_LENGTH || property == AudioContentProperty::AUDIO_LENGTH) {
setup_length ();
boost::shared_ptr<Content> c = content.lock ();
- if (selected_region() && c == selected_region()->content) {
+ if (selected_content() && c == selected_content()) {
setup_content_information ();
}
} else if (property == FFmpegContentProperty::AUDIO_STREAM) {
_content->DeleteAllItems ();
- Playlist::RegionList regions = _film->regions ();
- for (Playlist::RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
+ Playlist::ContentList content = _film->content ();
+ for (Playlist::ContentList::iterator i = content.begin(); i != content.end(); ++i) {
int const t = _content->GetItemCount ();
- _content->InsertItem (t, std_to_wx ((*i)->content->summary ()));
- if ((*i)->content->summary() == selected_summary) {
+ _content->InsertItem (t, std_to_wx ((*i)->summary ()));
+ if ((*i)->summary() == selected_summary) {
_content->SetItemState (t, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED);
}
}
- if (selected_summary.empty () && !regions.empty ()) {
+ if (selected_summary.empty () && !content.empty ()) {
/* Select the item of content if non was selected before */
_content->SetItemState (0, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED);
}
void
FilmEditor::content_remove_clicked (wxCommandEvent &)
{
- shared_ptr<Playlist::Region> r = selected_region ();
- if (r) {
- _film->remove_content (r->content);
+ shared_ptr<Content> c = selected_content ();
+ if (c) {
+ _film->remove_content (c);
}
}
void
FilmEditor::content_activated (wxListEvent& ev)
{
- Playlist::RegionList r = _film->regions ();
- assert (ev.GetIndex() >= 0 && size_t (ev.GetIndex()) < r.size ());
+ Playlist::ContentList c = _film->content ();
+ assert (ev.GetIndex() >= 0 && size_t (ev.GetIndex()) < c.size ());
- region_properties (r[ev.GetIndex()]);
+ content_properties (c[ev.GetIndex()]);
}
void
FilmEditor::content_properties_clicked (wxCommandEvent &)
{
- shared_ptr<Playlist::Region> r = selected_region ();
- if (!r) {
+ shared_ptr<Content> c = selected_content ();
+ if (!c) {
return;
}
- content_properties (r);
+ content_properties (c);
}
void
-FilmEditor::region_properties (shared_ptr<Playlist::Region> region)
+FilmEditor::content_properties (shared_ptr<Content> content)
{
- if (dynamic_pointer_cast<ImageMagickContent> (region->content)) {
- ImageMagickContentDialog* d = new ImageMagickContentDialog (this, region);
+ shared_ptr<ImageMagickContent> ic = dynamic_pointer_cast<ImageMagickContent> (content);
+ if (ic) {
+ ImageMagickContentDialog* d = new ImageMagickContentDialog (this, ic);
d->ShowModal ();
d->Destroy ();
}
- if (dynamic_pointer_cast<FFmpegContent> (region->content)) {
- FFmpegContentDialog* d = new FFmpegContentDialog (this, region);
+ shared_ptr<FFmpegContent> fc = dynamic_pointer_cast<FFmpegContent> (content);
+ if (fc) {
+ FFmpegContentDialog* d = new FFmpegContentDialog (this, fc);
d->ShowModal ();
d->Destroy ();
}
void
FilmEditor::setup_content_information ()
{
- shared_ptr<Playlist::Region> r = selected_region ();
- if (!r) {
+ shared_ptr<Content> c = selected_content ();
+ if (!c) {
_content_information->SetValue (wxT (""));
return;
}
- _content_information->SetValue (std_to_wx (r->content->information ()));
+ _content_information->SetValue (std_to_wx (c->information ()));
}
void
{
_content_add->Enable (_generally_sensitive);
- shared_ptr<Playlist::Region> selection = selected_region ();
+ shared_ptr<Content> selection = selected_content ();
_content_properties->Enable (
selection && _generally_sensitive &&
- (dynamic_pointer_cast<ImageMagickContent> (selection->content) || dynamic_pointer_cast<FFmpegContent> (selection->content))
+ (dynamic_pointer_cast<ImageMagickContent> (selection) || dynamic_pointer_cast<FFmpegContent> (selection))
);
_content_remove->Enable (selection && _generally_sensitive);
_content_timeline->Enable (_generally_sensitive);
}
-shared_ptr<Playlist::Region>
-FilmEditor::selected_region ()
+shared_ptr<Content>
+FilmEditor::selected_content ()
{
int const s = _content->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
if (s == -1) {
- return shared_ptr<Playlist::Region> ();
+ return shared_ptr<Content> ();
}
- Playlist::RegionList r = _film->regions ();
- if (s < 0 || size_t (s) >= r.size ()) {
- return shared_ptr<Playlist::Region> ();
+ Playlist::ContentList c = _film->content ();
+ if (s < 0 || size_t (s) >= c.size ()) {
+ return shared_ptr<Content> ();
}
- return r[s];
+ return c[s];
}
void
void setup_loop_sensitivity ();
void active_jobs_changed (bool);
- boost::shared_ptr<Playlist::Region> selected_region ();
- void region_properties (boost::shared_ptr<Playlist::Region>);
+ boost::shared_ptr<Content> selected_content ();
+ void content_properties (boost::shared_ptr<Content>);
wxNotebook* _notebook;
wxPanel* _film_panel;
+/* -*- c-basic-offset: 8; default-tab-width: 8; -*- */
+
/*
Copyright (C) 2013 Carl Hetherington <cth@carlh.net>
#include "wx_util.h"
using boost::shared_ptr;
+using boost::dynamic_pointer_cast;
ImageMagickContentDialog::ImageMagickContentDialog (wxWindow* parent, shared_ptr<ImageMagickContent> content)
: wxDialog (parent, wxID_ANY, _("Image"))
+/* -*- c-basic-offset: 8; default-tab-width: 8; -*- */
+
/*
Copyright (C) 2013 Carl Hetherington <cth@carlh.net>
class wxSpinCtrl;
class ImageMagickContent;
+class Region;
class ImageMagickContentDialog : public wxDialog
{
class ContentView : public View
{
public:
- ContentView (Timeline& tl, shared_ptr<const Content> c, Time s, int t)
+ ContentView (Timeline& tl, shared_ptr<const Content> c, int t)
: View (tl)
, _content (c)
- , _start (s)
, _track (t)
, _selected (false)
{
return;
}
+ Time const start = content->time ();
Time const len = content->length (film);
gc->SetPen (*wxBLACK_PEN);
#endif
wxGraphicsPath path = gc->CreatePath ();
- path.MoveToPoint (time_x (_start), y_pos (_track));
- path.AddLineToPoint (time_x (_start + len), y_pos (_track));
- path.AddLineToPoint (time_x (_start + len), y_pos (_track + 1));
- path.AddLineToPoint (time_x (_start), y_pos (_track + 1));
- path.AddLineToPoint (time_x (_start), y_pos (_track));
+ path.MoveToPoint (time_x (start), y_pos (_track));
+ path.AddLineToPoint (time_x (start + len), y_pos (_track));
+ path.AddLineToPoint (time_x (start + len), y_pos (_track + 1));
+ path.AddLineToPoint (time_x (start), y_pos (_track + 1));
+ path.AddLineToPoint (time_x (start), y_pos (_track));
gc->StrokePath (path);
gc->FillPath (path);
wxDouble name_leading;
gc->GetTextExtent (name, &name_width, &name_height, &name_descent, &name_leading);
- gc->Clip (wxRegion (time_x (_start), y_pos (_track), len * _timeline.pixels_per_time_unit(), _timeline.track_height()));
- gc->DrawText (name, time_x (_start) + 12, y_pos (_track + 1) - name_height - 4);
+ gc->Clip (wxRegion (time_x (start), y_pos (_track), len * _timeline.pixels_per_time_unit(), _timeline.track_height()));
+ gc->DrawText (name, time_x (start) + 12, y_pos (_track + 1) - name_height - 4);
gc->ResetClip ();
}
return Rect ();
}
- return Rect (time_x (_start), y_pos (_track), content->length (film) * _timeline.pixels_per_time_unit(), _timeline.track_height());
+ return Rect (
+ time_x (content->time ()),
+ y_pos (_track),
+ content->length (film) * _timeline.pixels_per_time_unit(),
+ _timeline.track_height()
+ );
}
void set_selected (bool s) {
}
boost::weak_ptr<const Content> _content;
- Time _start;
int _track;
bool _selected;
};
class AudioContentView : public ContentView
{
public:
- AudioContentView (Timeline& tl, shared_ptr<const Content> c, Time s, int t)
- : ContentView (tl, c, s, t)
+ AudioContentView (Timeline& tl, shared_ptr<const Content> c, int t)
+ : ContentView (tl, c, t)
{}
private:
class VideoContentView : public ContentView
{
public:
- VideoContentView (Timeline& tl, shared_ptr<const Content> c, Time s, int t)
- : ContentView (tl, c, s, t)
+ VideoContentView (Timeline& tl, shared_ptr<const Content> c, int t)
+ : ContentView (tl, c, t)
{}
private:
_views.clear ();
- Playlist::RegionList regions = fl->playlist()->regions ();
+ Playlist::ContentList content = fl->playlist()->content ();
- for (Playlist::RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
- if (dynamic_pointer_cast<VideoContent> ((*i)->content)) {
- _views.push_back (shared_ptr<View> (new VideoContentView (*this, (*i)->content, (*i)->time, 0)));
+ for (Playlist::ContentList::iterator i = content.begin(); i != content.end(); ++i) {
+ if (dynamic_pointer_cast<VideoContent> (*i)) {
+ _views.push_back (shared_ptr<View> (new VideoContentView (*this, *i, 0)));
} else {
- _views.push_back (shared_ptr<View> (new AudioContentView (*this, (*i)->content, (*i)->time, 1)));
+ _views.push_back (shared_ptr<View> (new AudioContentView (*this, *i, 1)));
}
}