diff options
| author | Carl Hetherington <cth@carlh.net> | 2017-06-28 10:09:53 +0100 |
|---|---|---|
| committer | Carl Hetherington <cth@carlh.net> | 2017-06-29 11:26:13 +0100 |
| commit | baf84885a777378b9ff5c05ef24d6361560822a6 (patch) | |
| tree | 18b953a0e1a6b05609f98193a766a679de434551 /src/lib | |
| parent | 4fbe44913582d6cc48a7d61145f3170fb0eec595 (diff) | |
Fixes for silence in projects, various cleanups.
Diffstat (limited to 'src/lib')
| -rw-r--r-- | src/lib/audio_decoder.cc | 21 | ||||
| -rw-r--r-- | src/lib/audio_decoder.h | 2 | ||||
| -rw-r--r-- | src/lib/dcpomatic_time_coalesce.h | 48 | ||||
| -rw-r--r-- | src/lib/empty.cc | 89 | ||||
| -rw-r--r-- | src/lib/empty.h | 49 | ||||
| -rw-r--r-- | src/lib/player.cc | 129 | ||||
| -rw-r--r-- | src/lib/player.h | 5 | ||||
| -rw-r--r-- | src/lib/playlist.cc | 6 | ||||
| -rw-r--r-- | src/lib/wscript | 1 |
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 |
