Various more hacks; basically trying to remove Regions as an unnecessary complexity.
authorCarl Hetherington <cth@carlh.net>
Tue, 21 May 2013 16:32:28 +0000 (17:32 +0100)
committerCarl Hetherington <cth@carlh.net>
Tue, 21 May 2013 16:32:28 +0000 (17:32 +0100)
22 files changed:
src/lib/audio_content.h
src/lib/content.cc
src/lib/content.h
src/lib/ffmpeg_content.cc
src/lib/ffmpeg_content.h
src/lib/ffmpeg_decoder.cc
src/lib/ffmpeg_decoder.h
src/lib/film.cc
src/lib/film.h
src/lib/player.cc
src/lib/player.h
src/lib/playlist.cc
src/lib/playlist.h
src/lib/sndfile_content.h
src/wx/audio_mapping_view.cc
src/wx/ffmpeg_content_dialog.cc
src/wx/ffmpeg_content_dialog.h
src/wx/film_editor.cc
src/wx/film_editor.h
src/wx/imagemagick_content_dialog.cc
src/wx/imagemagick_content_dialog.h
src/wx/timeline.cc

index 36c36992ea79fe9439e50366f3f26ddded6d248f..51f05efb0c9afcdfe3e506940d6d4dac3ba9b5c7 100644 (file)
@@ -23,6 +23,7 @@
 #define DCPOMATIC_AUDIO_CONTENT_H
 
 #include "content.h"
+#include "audio_mapping.h"
 
 namespace cxml {
        class Node;
@@ -49,6 +50,7 @@ public:
         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
index 9c3bcd39dcf6cd5585c20d7e9d70a4d8f52cb9c9..578dafd675807fa8a13e8b9ef0a4852638ee6b82 100644 (file)
@@ -1,3 +1,5 @@
+/* -*- 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)
 {
 
 }
@@ -36,12 +40,14 @@ Content::Content (shared_ptr<const cxml::Node> node)
 {
        _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)
 {
 
 }
@@ -52,6 +58,7 @@ Content::as_xml (xmlpp::Node* node) const
        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
index 8db35891a4979af6f795421bcc026cd923e17a4f..fd6288acce7aaa279d737eed78ac64fd4ddc1a27 100644 (file)
@@ -60,6 +60,11 @@ public:
                return _digest;
        }
 
+       Time time () const {
+               boost::mutex::scoped_lock lm (_mutex);
+               return _time;
+       }
+
        boost::signals2::signal<void (boost::weak_ptr<Content>, int)> Changed;
 
 protected:
@@ -70,6 +75,7 @@ protected:
 private:
        boost::filesystem::path _file;
        std::string _digest;
+       Time _time;
 };
 
 #endif
index e898c02d0a6c99c9e3bacf2dadb3bf2fce6d4353..fcc775f0a9488efb0e9e7836d97b6efd15d3a05b 100644 (file)
@@ -57,7 +57,7 @@ FFmpegContent::FFmpegContent (shared_ptr<const cxml::Node> node)
 {
        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 ();
                }
@@ -65,7 +65,7 @@ FFmpegContent::FFmpegContent (shared_ptr<const cxml::Node> node)
 
        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 ();
                }
@@ -94,20 +94,20 @@ FFmpegContent::as_xml (xmlpp::Node* node) const
 
        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);
        }
 }
 
@@ -181,7 +181,7 @@ FFmpegContent::information () const
 }
 
 void
-FFmpegContent::set_subtitle_stream (FFmpegSubtitleStream s)
+FFmpegContent::set_subtitle_stream (shared_ptr<FFmpegSubtitleStream> s)
 {
         {
                 boost::mutex::scoped_lock lm (_mutex);
@@ -192,7 +192,7 @@ FFmpegContent::set_subtitle_stream (FFmpegSubtitleStream s)
 }
 
 void
-FFmpegContent::set_audio_stream (FFmpegAudioStream s)
+FFmpegContent::set_audio_stream (shared_ptr<FFmpegAudioStream> s)
 {
         {
                 boost::mutex::scoped_lock lm (_mutex);
@@ -205,16 +205,23 @@ FFmpegContent::set_audio_stream (FFmpegAudioStream s)
 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;
         }
@@ -225,6 +232,8 @@ FFmpegContent::audio_channels () const
 int
 FFmpegContent::content_audio_frame_rate () const
 {
+       boost::mutex::scoped_lock lm (_mutex);
+
         if (!_audio_stream) {
                 return 0;
         }
@@ -271,6 +280,7 @@ FFmpegAudioStream::FFmpegAudioStream (shared_ptr<const cxml::Node> node)
        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
@@ -280,6 +290,7 @@ FFmpegAudioStream::as_xml (xmlpp::Node* root) const
        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().
@@ -311,3 +322,17 @@ FFmpegContent::length (shared_ptr<const Film> film) const
        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;
+}
+
index 6c141b6c13173ae111cfc01b2ef690a73956e425..d79e4ec3556d6d9fae89eb8b5ade0e24d1bba143 100644 (file)
@@ -34,6 +34,7 @@ public:
                 , id (i)
                 , frame_rate (f)
                , channels (c)
+               , mapping (c)
         {}
 
        FFmpegAudioStream (boost::shared_ptr<const cxml::Node>);
@@ -44,6 +45,7 @@ public:
         int id;
         int frame_rate;
        int channels;
+       AudioMapping mapping;
 };
 
 extern bool operator== (FFmpegAudioStream const & a, FFmpegAudioStream const & b);
@@ -98,35 +100,36 @@ public:
         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
index adf16c9408a36d39976d730e86e8c29a894ec505..a637160ae918228ad73c7e9aba46ce49dbdc1e7d 100644 (file)
@@ -141,11 +141,13 @@ FFmpegDecoder::setup_general ()
                        }
                        
                        _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)));
                }
        }
 
index 760fc084dd36c61ccf856b428794592597c9fbb9..f3f6b126b3a183cdb9a2c1b931699f9b0ac0b24e 100644 (file)
@@ -1,3 +1,5 @@
+/* -*- c-basic-offset: 8; default-tab-width: 8; -*- */
+
 /*
     Copyright (C) 2012 Carl Hetherington <cth@carlh.net>
 
@@ -68,11 +70,11 @@ public:
        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;
        }
 
@@ -126,8 +128,8 @@ private:
        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;
index 1b5779f2d0f43fbcb023b553edeba7b897b7126e..37af4cb20c4de0e28e115bf7d05192c93d1938c3 100644 (file)
@@ -317,7 +317,7 @@ Film::make_dcp ()
                throw MissingSettingError (_("format"));
        }
 
-       if (_playlist->regions().empty ()) {
+       if (_playlist->content().empty ()) {
                throw StringError (_("You must add some content to the DCP before creating it"));
        }
 
@@ -697,11 +697,11 @@ Film::set_trust_content_headers (bool t)
        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);
                }
        }
 }
@@ -1040,10 +1040,10 @@ Film::playlist () const
        return _playlist;
 }
 
-Playlist::RegionList
-Film::regions () const
+Playlist::ContentList
+Film::content () const
 {
-       return _playlist->regions ();
+       return _playlist->content ();
 }
 
 void
index 03d472cee3e85a609ecea48f318787896d356674..c0417382ff4f4629ff78fcf4899972c1aba78309 100644 (file)
@@ -111,7 +111,7 @@ public:
 
        /* Proxies for some Playlist methods */
 
-       Playlist::RegionList regions () const;
+       Playlist::ContentList content () const;
 
        Time length () const;
        bool has_subtitles () const;
index 675fcae5cc1b6a3d927e926f8037885610cb0ca4..5d4635e5a865b0a7334e5381f50f59f5aa873ddd 100644 (file)
@@ -89,12 +89,12 @@ Player::pass ()
         */
 
         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) {
@@ -121,24 +121,24 @@ Player::pass ()
 }
 
 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 */
 
@@ -172,7 +172,7 @@ Player::process_audio (shared_ptr<RegionDecoder> rd, shared_ptr<const AudioBuffe
 
         _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 */
@@ -212,34 +212,34 @@ Player::seek_forward ()
 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;
@@ -248,21 +248,21 @@ Player::setup_decoders ()
 
                        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;
index 4979778ede20425c8cb3040bc944129c27650ded..44b95e07643a9599586bcd7e89a926a3be5a28b3 100644 (file)
@@ -62,19 +62,19 @@ public:
 
 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);
@@ -90,7 +90,7 @@ private:
 
        /** 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;
index 8db5c5fc2bced2a8745a28effd3d0a33b7824ff1..ad71f87d749bd489d2cb35106d7a353901547d25 100644 (file)
@@ -58,11 +58,17 @@ Playlist::Playlist ()
 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)
 {
@@ -74,14 +80,14 @@ Playlist::audio_digest () const
 {
        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);
                }
@@ -97,13 +103,13 @@ Playlist::video_digest () const
 {
        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;
                }
@@ -114,22 +120,34 @@ Playlist::video_digest () const
        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));
@@ -138,20 +156,21 @@ Playlist::as_xml (xmlpp::Node* node)
 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 ();
        }
 }
@@ -166,8 +185,8 @@ Playlist::set_loop (int l)
 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;
                }
@@ -176,40 +195,6 @@ Playlist::has_subtitles () const
        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:
@@ -248,8 +233,8 @@ Playlist::best_dcp_frame_rate () const
        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;
                        }
@@ -276,10 +261,25 @@ Time
 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)));
+       }
+}
+
index f677f0ad78b2f7bd7197edd22c194130539c1994..e8cac0247d46877ea5e73bfa5771eafa2699d574 100644 (file)
@@ -41,6 +41,7 @@ class SndfileContent;
 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
@@ -57,6 +58,7 @@ class Playlist
 public:
        Playlist ();
        Playlist (boost::shared_ptr<const Playlist>);
+       ~Playlist ();
 
        void as_xml (xmlpp::Node *);
        void set_from_xml (boost::shared_ptr<const cxml::Node>);
@@ -66,30 +68,10 @@ public:
 
        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;
@@ -109,9 +91,11 @@ public:
        
 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
index d930d70619e95e426dd93e82ecbe1bcdae2f2b32..1ef4b3f026514526bbb26e0157d218581ab4fcc8 100644 (file)
@@ -62,6 +62,11 @@ public:
        }
 
         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);
 
@@ -69,4 +74,5 @@ private:
        int _audio_channels;
        ContentAudioFrame _audio_length;
        int _audio_frame_rate;
+       AudioMapping _audio_mapping;
 };
index d0deca69e5a52088c1b6ce59e8cf4b1bb6a0a833..43119ef4e245c69f7ff57e06382c585e563b6765 100644 (file)
@@ -136,9 +136,9 @@ AudioMappingView::left_click (wxGridEvent& ev)
 
        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));
                        }
                }
        }
index 8bf8a8e3553b3ec9eb0fab9c39b1ab5c05fa12a1..06b58c58c1d9be97257ac46c0f994901e800e71b 100644 (file)
@@ -33,10 +33,9 @@ using boost::shared_ptr;
 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);
@@ -52,9 +51,6 @@ FFmpegContentDialog::FFmpegContentDialog (wxWindow* parent, shared_ptr<Playlist:
        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) {
@@ -85,7 +81,7 @@ FFmpegContentDialog::FFmpegContentDialog (wxWindow* parent, shared_ptr<Playlist:
        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);
@@ -166,6 +162,7 @@ FFmpegContentDialog::audio_mapping_changed (AudioMapping m)
                return;
        }
 
-       /* XXX: set mapping in playlist */
+       cout << "setting map in stream " << content->audio_stream().get() << "\n";
+       content->audio_stream()->mapping = m;
 }
 
index 380b31f2154fbab50b9cd03dcbf199ac4e2a3441..302c8166f9d54c823cfea30086b50b535e5c5934 100644 (file)
 #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;
index 226feaca2ad263b764ca908adc410fe0487fb88b..177202d25264cf2dec6700c1484c7017780720ec 100644 (file)
@@ -712,7 +712,7 @@ FilmEditor::film_content_changed (weak_ptr<Content> content, int property)
        } 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) {
@@ -1119,16 +1119,16 @@ FilmEditor::setup_content ()
        
        _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);
        }
@@ -1166,43 +1166,45 @@ FilmEditor::content_add_clicked (wxCommandEvent &)
 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 ();
        }
@@ -1218,13 +1220,13 @@ FilmEditor::content_selection_changed (wxListEvent &)
 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
@@ -1232,31 +1234,31 @@ FilmEditor::setup_content_button_sensitivity ()
 {
         _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
index c74d631ad82088cb7a2f55517b3dec97c4d469e7..4bdba9979b1f53d3f0032cd04359547b1f5acf5a 100644 (file)
@@ -111,8 +111,8 @@ private:
        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;
index 24b92fdafee2d5e39339957630551f3d8167fed2..52f3cf1a78d4c28855618621682fcf781dc7a12f 100644 (file)
@@ -1,3 +1,5 @@
+/* -*- c-basic-offset: 8; default-tab-width: 8; -*- */
+
 /*
     Copyright (C) 2013 Carl Hetherington <cth@carlh.net>
 
@@ -23,6 +25,7 @@
 #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"))
index b22ff6817b90582d106f5e1dba26c7d88794c294..9a4ea26947cf852c091bb6546cff5e948d66a267 100644 (file)
@@ -1,3 +1,5 @@
+/* -*- c-basic-offset: 8; default-tab-width: 8; -*- */
+
 /*
     Copyright (C) 2013 Carl Hetherington <cth@carlh.net>
 
@@ -21,6 +23,7 @@
 
 class wxSpinCtrl;
 class ImageMagickContent;
+class Region;
 
 class ImageMagickContentDialog : public wxDialog
 {
index 7e2243ccd3fee15be9136c7a45c38990aee64e0d..3a833e701de09a6e4efc44214976f2ac7283ef2d 100644 (file)
@@ -57,10 +57,9 @@ protected:
 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)
        {
@@ -75,6 +74,7 @@ public:
                        return;
                }
 
+               Time const start = content->time ();
                Time const len = content->length (film);
 
                gc->SetPen (*wxBLACK_PEN);
@@ -96,11 +96,11 @@ public:
 #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);
 
@@ -111,8 +111,8 @@ public:
                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 ();
        }
 
@@ -124,7 +124,12 @@ public:
                        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) {
@@ -147,7 +152,6 @@ private:
        }
 
        boost::weak_ptr<const Content> _content;
-       Time _start;
        int _track;
        bool _selected;
 };
@@ -155,8 +159,8 @@ private:
 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:
@@ -174,8 +178,8 @@ 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:       
@@ -319,13 +323,13 @@ Timeline::playlist_changed ()
 
        _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)));
                }
        }