using boost::dynamic_pointer_cast;
Playlist::Playlist ()
- : _sequence_video (true)
- , _sequencing_video (false)
+ : _sequence (true)
+ , _sequencing (false)
{
}
void
Playlist::content_changed (weak_ptr<Content> content, int property, bool frequent)
{
- /* Don't respond to position changes here, as:
- - sequencing after earlier/later changes is handled by move_earlier/move_later
- - any other position changes will be timeline drags which should not result in content
- being sequenced.
- */
-
if (property == ContentProperty::LENGTH || property == VideoContentProperty::VIDEO_FRAME_TYPE) {
- maybe_sequence_video ();
+ /* Don't respond to position changes here, as:
+ - sequencing after earlier/later changes is handled by move_earlier/move_later
+ - any other position changes will be timeline drags which should not result in content
+ being sequenced.
+ */
+ maybe_sequence ();
+ }
+
+ if (
+ property == ContentProperty::POSITION ||
+ property == ContentProperty::LENGTH ||
+ property == ContentProperty::TRIM_START ||
+ property == ContentProperty::TRIM_END) {
+
+ ContentList old = _content;
+ sort (_content.begin(), _content.end(), ContentSorter ());
+ if (_content != old) {
+ OrderChanged ();
+ }
}
ContentChanged (content, property, frequent);
}
void
-Playlist::maybe_sequence_video ()
+Playlist::maybe_sequence ()
{
- if (!_sequence_video || _sequencing_video) {
+ if (!_sequence || _sequencing) {
return;
}
- _sequencing_video = true;
+ _sequencing = true;
+
+ /* Keep track of the content that we've set the position of so that we don't
+ do it twice.
+ */
+ ContentList placed;
+
+ /* Video */
DCPTime next_left;
DCPTime next_right;
BOOST_FOREACH (shared_ptr<Content> i, _content) {
- shared_ptr<VideoContent> vc = dynamic_pointer_cast<VideoContent> (i);
- if (!vc) {
+ if (!i->video) {
continue;
}
- if (vc->video_frame_type() == VIDEO_FRAME_TYPE_3D_RIGHT) {
- vc->set_position (next_right);
- next_right = vc->end();
+ if (i->video->video_frame_type() == VIDEO_FRAME_TYPE_3D_RIGHT) {
+ i->set_position (next_right);
+ next_right = i->end();
} else {
- vc->set_position (next_left);
- next_left = vc->end();
+ i->set_position (next_left);
+ next_left = i->end();
+ }
+
+ placed.push_back (i);
+ }
+
+ /* Subtitles */
+
+ DCPTime next;
+ BOOST_FOREACH (shared_ptr<Content> i, _content) {
+ shared_ptr<SubtitleContent> sc = dynamic_pointer_cast<SubtitleContent> (i);
+ if (!sc || !sc->has_subtitles() || find (placed.begin(), placed.end(), i) != placed.end()) {
+ continue;
}
+
+ sc->set_position (next);
+ next = sc->end();
}
+
/* This won't change order, so it does not need a sort */
- _sequencing_video = false;
+ _sequencing = false;
}
string
_content.push_back (content_factory (film, i, version, notes));
}
+ /* This shouldn't be necessary but better safe than sorry (there could be old files) */
sort (_content.begin(), _content.end(), ContentSorter ());
reconnect ();
float this_error = 0;
BOOST_FOREACH (shared_ptr<Content> j, _content) {
- shared_ptr<VideoContent> vc = dynamic_pointer_cast<VideoContent> (j);
- if (!vc || !vc->has_own_video_frame_rate()) {
+ if (!j->video || !j->video->has_own_video_frame_rate()) {
continue;
}
/* Best error for this content; we could use the content as-is or double its rate */
float best_error = min (
- float (fabs (i->source - vc->video_frame_rate ())),
- float (fabs (i->source - vc->video_frame_rate () * 2))
+ float (fabs (i->source - j->video->video_frame_rate ())),
+ float (fabs (i->source - j->video->video_frame_rate () * 2))
);
/* Use the largest difference between DCP and source as the "error" */
return end;
}
+DCPTime
+Playlist::subtitle_end () const
+{
+ DCPTime end;
+ BOOST_FOREACH (shared_ptr<Content> i, _content) {
+ if (dynamic_pointer_cast<const SubtitleContent> (i)) {
+ end = max (end, i->end ());
+ }
+ }
+
+ return end;
+}
+
FrameRateChange
Playlist::active_frame_rate_change (DCPTime t, int dcp_video_frame_rate) const
{
for (ContentList::const_reverse_iterator i = _content.rbegin(); i != _content.rend(); ++i) {
- shared_ptr<const VideoContent> vc = dynamic_pointer_cast<const VideoContent> (*i);
- if (!vc) {
+ if (!(*i)->video) {
continue;
}
- if (vc->position() <= t) {
+ if ((*i)->position() <= t) {
/* This is the first piece of content (going backwards...) that starts before t,
so it's the active one.
*/
- return FrameRateChange (vc->video_frame_rate(), dcp_video_frame_rate);
+ return FrameRateChange ((*i)->video->video_frame_rate(), dcp_video_frame_rate);
}
}
}
void
-Playlist::set_sequence_video (bool s)
+Playlist::set_sequence (bool s)
{
- _sequence_video = s;
+ _sequence = s;
}
bool
ContentSorter::operator() (shared_ptr<Content> a, shared_ptr<Content> b)
{
- return a->position() < b->position();
+ if (a->position() != b->position()) {
+ return a->position() < b->position();
+ }
+
+ /* Put video before audio if they start at the same time */
+ if (a->video && !b->video) {
+ return true;
+ } else if (!a->video && b->video) {
+ return false;
+ }
+
+ /* Last resort */
+ return a->digest() < b->digest();
}
/** @return content in ascending order of position */
void
Playlist::move_earlier (shared_ptr<Content> c)
{
- sort (_content.begin(), _content.end(), ContentSorter ());
-
ContentList::iterator previous = _content.end ();
ContentList::iterator i = _content.begin();
while (i != _content.end() && *i != c) {
return;
}
+ shared_ptr<Content> previous_c = *previous;
- DCPTime const p = (*previous)->position ();
- (*previous)->set_position (p + c->length_after_trim ());
+ DCPTime const p = previous_c->position ();
+ previous_c->set_position (p + c->length_after_trim ());
c->set_position (p);
- sort (_content.begin(), _content.end(), ContentSorter ());
}
void
Playlist::move_later (shared_ptr<Content> c)
{
- sort (_content.begin(), _content.end(), ContentSorter ());
-
ContentList::iterator i = _content.begin();
while (i != _content.end() && *i != c) {
++i;
return;
}
- (*next)->set_position (c->position ());
- c->set_position (c->position() + (*next)->length_after_trim ());
- sort (_content.begin(), _content.end(), ContentSorter ());
+ shared_ptr<Content> next_c = *next;
+
+ next_c->set_position (c->position ());
+ c->set_position (c->position() + next_c->length_after_trim ());
}
int64_t