summaryrefslogtreecommitdiff
path: root/src/lib
diff options
context:
space:
mode:
authorCarl Hetherington <cth@carlh.net>2017-06-28 10:09:53 +0100
committerCarl Hetherington <cth@carlh.net>2017-06-29 11:26:13 +0100
commitbaf84885a777378b9ff5c05ef24d6361560822a6 (patch)
tree18b953a0e1a6b05609f98193a766a679de434551 /src/lib
parent4fbe44913582d6cc48a7d61145f3170fb0eec595 (diff)
Fixes for silence in projects, various cleanups.
Diffstat (limited to 'src/lib')
-rw-r--r--src/lib/audio_decoder.cc21
-rw-r--r--src/lib/audio_decoder.h2
-rw-r--r--src/lib/dcpomatic_time_coalesce.h48
-rw-r--r--src/lib/empty.cc89
-rw-r--r--src/lib/empty.h49
-rw-r--r--src/lib/player.cc129
-rw-r--r--src/lib/player.h5
-rw-r--r--src/lib/playlist.cc6
-rw-r--r--src/lib/wscript1
9 files changed, 272 insertions, 78 deletions
diff --git a/src/lib/audio_decoder.cc b/src/lib/audio_decoder.cc
index 69d86c57b..5425798f6 100644
--- a/src/lib/audio_decoder.cc
+++ b/src/lib/audio_decoder.cc
@@ -60,6 +60,11 @@ AudioDecoder::emit (AudioStreamPtr stream, shared_ptr<const AudioBuffers> data,
we just count samples, as it seems that ContentTimes are unreliable from
FFmpegDecoder (not quite continuous; perhaps due to some rounding error).
*/
+ if (_content->delay() > 0) {
+ /* Insert silence to give the delay */
+ silence (_content->delay ());
+ }
+ time += ContentTime::from_seconds (_content->delay() / 1000.0);
_positions[stream] = time.frames_round (stream->frame_rate ());
}
@@ -139,4 +144,20 @@ AudioDecoder::flush ()
_positions[i->first] += ro->frames();
}
}
+
+ if (_content->delay() < 0) {
+ /* Finish off with the gap caused by the delay */
+ silence (-_content->delay ());
+ }
+}
+
+void
+AudioDecoder::silence (int milliseconds)
+{
+ BOOST_FOREACH (AudioStreamPtr i, _content->streams ()) {
+ int const samples = ContentTime::from_seconds(milliseconds / 1000.0).frames_round(i->frame_rate());
+ shared_ptr<AudioBuffers> silence (new AudioBuffers (i->channels(), samples));
+ silence->make_silent ();
+ Data (i, ContentAudio (silence, _positions[i]));
+ }
}
diff --git a/src/lib/audio_decoder.h b/src/lib/audio_decoder.h
index 624b5c94a..19d103543 100644
--- a/src/lib/audio_decoder.h
+++ b/src/lib/audio_decoder.h
@@ -56,6 +56,8 @@ public:
boost::signals2::signal<void (AudioStreamPtr, ContentAudio)> Data;
private:
+ void silence (int milliseconds);
+
boost::shared_ptr<const AudioContent> _content;
/** Frame after the last one that was emitted from Data for each AudioStream */
std::map<AudioStreamPtr, Frame> _positions;
diff --git a/src/lib/dcpomatic_time_coalesce.h b/src/lib/dcpomatic_time_coalesce.h
new file mode 100644
index 000000000..e103e80e6
--- /dev/null
+++ b/src/lib/dcpomatic_time_coalesce.h
@@ -0,0 +1,48 @@
+/*
+ Copyright (C) 2017 Carl Hetherington <cth@carlh.net>
+
+ This file is part of DCP-o-matic.
+
+ DCP-o-matic is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ DCP-o-matic is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with DCP-o-matic. If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#include "dcpomatic_time.h"
+#include <iostream>
+
+/** @param periods Set of periods in ascending order of from time */
+template <class T>
+std::list<TimePeriod<T> > coalesce (std::list<TimePeriod<T> > periods)
+{
+ bool did_something;
+ std::list<TimePeriod<T> > coalesced;
+ do {
+ coalesced.clear ();
+ did_something = false;
+ for (typename std::list<TimePeriod<T> >::const_iterator i = periods.begin(); i != periods.end(); ++i) {
+ typename std::list<TimePeriod<T> >::const_iterator j = i;
+ ++j;
+ if (j != periods.end() && (i->overlap(*j) || i->to == j->from)) {
+ coalesced.push_back (TimePeriod<T> (i->from, j->to));
+ did_something = true;
+ ++i;
+ } else {
+ coalesced.push_back (*i);
+ }
+ }
+ periods = coalesced;
+ } while (did_something);
+
+ return periods;
+}
diff --git a/src/lib/empty.cc b/src/lib/empty.cc
new file mode 100644
index 000000000..2233b4342
--- /dev/null
+++ b/src/lib/empty.cc
@@ -0,0 +1,89 @@
+/*
+ Copyright (C) 2017 Carl Hetherington <cth@carlh.net>
+
+ This file is part of DCP-o-matic.
+
+ DCP-o-matic is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ DCP-o-matic is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with DCP-o-matic. If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#include "empty.h"
+#include "playlist.h"
+#include "content.h"
+#include "content_part.h"
+#include "dcp_content.h"
+#include "dcpomatic_time_coalesce.h"
+#include <boost/foreach.hpp>
+#include <iostream>
+
+using std::cout;
+using std::list;
+using boost::shared_ptr;
+using boost::dynamic_pointer_cast;
+using boost::function;
+
+Empty::Empty (shared_ptr<const Playlist> playlist, function<shared_ptr<ContentPart> (Content *)> part)
+{
+ list<DCPTimePeriod> full;
+ BOOST_FOREACH (shared_ptr<Content> i, playlist->content()) {
+ if (part (i.get())) {
+ full.push_back (DCPTimePeriod (i->position(), i->end()));
+ }
+ }
+
+ _periods = subtract (DCPTimePeriod(DCPTime(), playlist->length()), coalesce(full));
+}
+
+void
+Empty::set_position (DCPTime position)
+{
+ _position = position;
+
+ BOOST_FOREACH (DCPTimePeriod i, _periods) {
+ if (i.contains(_position)) {
+ return;
+ }
+ }
+
+ BOOST_FOREACH (DCPTimePeriod i, _periods) {
+ if (i.from > _position) {
+ _position = i.from;
+ return;
+ }
+ }
+}
+
+DCPTimePeriod
+Empty::period_at_position () const
+{
+ BOOST_FOREACH (DCPTimePeriod i, _periods) {
+ if (i.contains(_position)) {
+ return DCPTimePeriod (_position, i.to);
+ }
+ }
+
+ DCPOMATIC_ASSERT (false);
+}
+
+bool
+Empty::done () const
+{
+ BOOST_FOREACH (DCPTimePeriod i, _periods) {
+ if (i.contains(_position)) {
+ return false;
+ }
+ }
+
+ return true;
+}
diff --git a/src/lib/empty.h b/src/lib/empty.h
new file mode 100644
index 000000000..3c676deeb
--- /dev/null
+++ b/src/lib/empty.h
@@ -0,0 +1,49 @@
+/*
+ Copyright (C) 2017 Carl Hetherington <cth@carlh.net>
+
+ This file is part of DCP-o-matic.
+
+ DCP-o-matic is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ DCP-o-matic is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with DCP-o-matic. If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#include "playlist.h"
+#include "dcpomatic_time.h"
+#include "content_part.h"
+#include <list>
+
+struct empty_test1;
+
+class Empty
+{
+public:
+ Empty () {}
+ Empty (boost::shared_ptr<const Playlist> playlist, boost::function<boost::shared_ptr<ContentPart> (Content *)> part);
+
+ DCPTime position () const {
+ return _position;
+ }
+
+ DCPTimePeriod period_at_position () const;
+
+ bool done () const;
+
+ void set_position (DCPTime amount);
+
+private:
+ friend struct ::empty_test1;
+
+ std::list<DCPTimePeriod> _periods;
+ DCPTime _position;
+};
diff --git a/src/lib/player.cc b/src/lib/player.cc
index 523475160..be6ff2bb4 100644
--- a/src/lib/player.cc
+++ b/src/lib/player.cc
@@ -156,6 +156,9 @@ Player::setup_pieces ()
}
}
+ _black = Empty (_playlist, bind(&Content::video, _1));
+ _silent = Empty (_playlist, bind(&Content::audio, _1));
+
_last_video_time = DCPTime ();
_last_audio_time = DCPTime ();
_have_valid_pieces = true;
@@ -356,8 +359,9 @@ DCPTime
Player::resampled_audio_to_dcp (shared_ptr<const Piece> piece, Frame f) const
{
/* See comment in dcp_to_content_video */
- DCPTime const d = DCPTime::from_frames (f, _film->audio_frame_rate()) - DCPTime (piece->content->trim_start(), piece->frc);
- return max (DCPTime (), d + piece->content->position ());
+ return DCPTime::from_frames (f, _film->audio_frame_rate())
+ - DCPTime (piece->content->trim_start(), piece->frc)
+ + piece->content->position();
}
ContentTime
@@ -499,34 +503,7 @@ Player::pass ()
setup_pieces ();
}
- bool filled = false;
-
- if (_last_video_time && !_playlist->video_content_at(*_last_video_time) && *_last_video_time < _playlist->length()) {
- /* _last_video_time is the time just after the last video we emitted, and there is no video content
- at this time so we need to emit some black.
- */
- emit_video (black_player_video_frame(), *_last_video_time);
- filled = true;
- } else if (_playlist->length() == DCPTime()) {
- /* Special case of an empty Film; just give one black frame */
- emit_video (black_player_video_frame(), DCPTime());
- filled = true;
- }
-
- if (_last_audio_time && !_playlist->audio_content_at(*_last_audio_time) && *_last_audio_time < _playlist->length()) {
- /* _last_audio_time is the time just after the last audio we emitted. There is no audio here
- so we need to emit some silence.
- */
- shared_ptr<Content> next = _playlist->next_audio_content(*_last_audio_time);
- DCPTimePeriod period (*_last_audio_time, next ? next->position() : _playlist->length());
- if (period.duration() > one_video_frame()) {
- period = DCPTimePeriod (*_last_audio_time, *_last_audio_time + one_video_frame());
- }
- fill_audio (period);
- filled = true;
- }
-
- /* Now pass() the decoder which is farthest behind where we are */
+ /* Find the decoder or empty which is farthest behind where we are and make it emit some data */
shared_ptr<Piece> earliest;
DCPTime earliest_content;
@@ -541,8 +518,27 @@ Player::pass ()
}
}
- if (!filled && earliest) {
+ bool done = false;
+
+ if (!_black.done() && (!earliest ||_black.position() < earliest_content)) {
+ /* There is some black that must be emitted */
+ emit_video (black_player_video_frame(), _black.position());
+ _black.set_position (_black.position() + one_video_frame());
+ } else if (!_silent.done() && (!earliest || _silent.position() < earliest_content)) {
+ /* There is some silence that must be emitted */
+ DCPTimePeriod period (_silent.period_at_position());
+ if (period.duration() > one_video_frame()) {
+ period.to = period.from + one_video_frame();
+ }
+ fill_audio (period);
+ _silent.set_position (period.to);
+ } else if (_playlist->length() == DCPTime()) {
+ /* Special case of an empty Film; just give one black frame */
+ emit_video (black_player_video_frame(), DCPTime());
+ } else if (earliest) {
earliest->done = earliest->decoder->pass ();
+ } else {
+ done = true;
}
/* Emit any audio that is ready */
@@ -567,17 +563,10 @@ Player::pass ()
*i = cut;
}
- if (_last_audio_time) {
- /* Fill in the gap before delayed audio; this doesn't need to take into account
- periods with no audio as it should only occur in delayed audio case.
- */
- fill_audio (DCPTimePeriod (*_last_audio_time, i->second));
- }
-
emit_audio (i->first, i->second);
}
- return !earliest && !filled;
+ return done;
}
optional<PositionImage>
@@ -663,37 +652,6 @@ Player::video (weak_ptr<Piece> wp, ContentVideo video)
emit_video (_last_video[wp], time);
}
-/** Do our common processing on some audio */
-void
-Player::audio_transform (shared_ptr<AudioContent> content, AudioStreamPtr stream, ContentAudio content_audio, DCPTime time)
-{
- DCPOMATIC_ASSERT (content_audio.audio->frames() > 0);
-
- /* Gain */
-
- if (content->gain() != 0) {
- shared_ptr<AudioBuffers> gain (new AudioBuffers (content_audio.audio));
- gain->apply_gain (content->gain ());
- content_audio.audio = gain;
- }
-
- /* Remap */
-
- content_audio.audio = remap (content_audio.audio, _film->audio_channels(), stream->mapping());
-
- /* Process */
-
- if (_audio_processor) {
- content_audio.audio = _audio_processor->run (content_audio.audio, _film->audio_channels ());
- }
-
- /* Push */
-
- _audio_merger.push (content_audio.audio, time);
- DCPOMATIC_ASSERT (_stream_states.find (stream) != _stream_states.end ());
- _stream_states[stream].last_push_end = time + DCPTime::from_frames (content_audio.audio->frames(), _film->audio_frame_rate());
-}
-
void
Player::audio (weak_ptr<Piece> wp, AudioStreamPtr stream, ContentAudio content_audio)
{
@@ -708,7 +666,7 @@ Player::audio (weak_ptr<Piece> wp, AudioStreamPtr stream, ContentAudio content_a
DCPOMATIC_ASSERT (content);
/* Compute time in the DCP */
- DCPTime time = resampled_audio_to_dcp (piece, content_audio.frame) + DCPTime::from_seconds (content->delay() / 1000.0);
+ DCPTime time = resampled_audio_to_dcp (piece, content_audio.frame);
/* And the end of this block in the DCP */
DCPTime end = time + DCPTime::from_frames(content_audio.audio->frames(), content->resampled_frame_rate());
@@ -734,7 +692,31 @@ Player::audio (weak_ptr<Piece> wp, AudioStreamPtr stream, ContentAudio content_a
content_audio.audio = cut;
}
- audio_transform (content, stream, content_audio, time);
+ DCPOMATIC_ASSERT (content_audio.audio->frames() > 0);
+
+ /* Gain */
+
+ if (content->gain() != 0) {
+ shared_ptr<AudioBuffers> gain (new AudioBuffers (content_audio.audio));
+ gain->apply_gain (content->gain ());
+ content_audio.audio = gain;
+ }
+
+ /* Remap */
+
+ content_audio.audio = remap (content_audio.audio, _film->audio_channels(), stream->mapping());
+
+ /* Process */
+
+ if (_audio_processor) {
+ content_audio.audio = _audio_processor->run (content_audio.audio, _film->audio_channels ());
+ }
+
+ /* Push */
+
+ _audio_merger.push (content_audio.audio, time);
+ DCPOMATIC_ASSERT (_stream_states.find (stream) != _stream_states.end ());
+ _stream_states[stream].last_push_end = time + DCPTime::from_frames (content_audio.audio->frames(), _film->audio_frame_rate());
}
void
@@ -857,6 +839,9 @@ Player::seek (DCPTime time, bool accurate)
_last_audio_time = optional<DCPTime>();
}
+ _black.set_position (time);
+ _silent.set_position (time);
+
_last_video.clear ();
}
diff --git a/src/lib/player.h b/src/lib/player.h
index b4d00223b..0ceff016c 100644
--- a/src/lib/player.h
+++ b/src/lib/player.h
@@ -32,6 +32,7 @@
#include "content_subtitle.h"
#include "audio_stream.h"
#include "audio_merger.h"
+#include "empty.h"
#include <boost/shared_ptr.hpp>
#include <boost/enable_shared_from_this.hpp>
#include <list>
@@ -108,7 +109,6 @@ private:
void subtitle_stop (boost::weak_ptr<Piece>, ContentTime);
DCPTime one_video_frame () const;
void fill_audio (DCPTimePeriod period);
- void audio_transform (boost::shared_ptr<AudioContent> content, AudioStreamPtr stream, ContentAudio content_audio, DCPTime time);
std::pair<boost::shared_ptr<AudioBuffers>, DCPTime> discard_audio (
boost::shared_ptr<const AudioBuffers> audio, DCPTime time, DCPTime discard_to
) const;
@@ -165,6 +165,9 @@ private:
};
std::map<AudioStreamPtr, StreamState> _stream_states;
+ Empty _black;
+ Empty _silent;
+
ActiveSubtitles _active_subtitles;
boost::shared_ptr<AudioProcessor> _audio_processor;
diff --git a/src/lib/playlist.cc b/src/lib/playlist.cc
index 3609f9eb3..12832cfd7 100644
--- a/src/lib/playlist.cc
+++ b/src/lib/playlist.cc
@@ -563,11 +563,7 @@ Playlist::audio_content_at (DCPTime time) const
if (!i->audio) {
continue;
}
- DCPTime end = i->end ();
- if (i->audio->delay() < 0) {
- end += DCPTime::from_seconds (i->audio->delay() / 1000.0);
- }
- if (i->position() <= time && time < end) {
+ if (i->position() <= time && time < i->end()) {
return true;
}
}
diff --git a/src/lib/wscript b/src/lib/wscript
index cbb01730f..39ebc5af2 100644
--- a/src/lib/wscript
+++ b/src/lib/wscript
@@ -67,6 +67,7 @@ sources = """
dkdm_wrapper.cc
dolby_cp750.cc
emailer.cc
+ empty.cc
encoder.cc
encode_server.cc
encode_server_finder.cc