Player::Player (shared_ptr<const Film> film, shared_ptr<const Playlist> playlist)
: _film (film)
, _playlist (playlist)
- , _have_valid_pieces (false)
+ , _suspended (false)
, _ignore_video (false)
, _ignore_audio (false)
, _ignore_text (false)
, _shuffler (0)
{
_film_changed_connection = _film->Changed.connect (bind (&Player::film_changed, this, _1));
- _playlist_changed_connection = _playlist->Changed.connect (bind (&Player::playlist_changed, this));
- _playlist_content_changed_connection = _playlist->ContentChanged.connect (bind (&Player::playlist_content_changed, this, _1, _2, _3));
+ _playlist_change_connection = _playlist->Change.connect (bind (&Player::playlist_change, this, _1));
+ _playlist_content_change_connection = _playlist->ContentChange.connect (bind(&Player::playlist_content_change, this, _1, _3, _4));
set_video_container_size (_film->frame_size ());
film_changed (Film::AUDIO_PROCESSOR);
+ setup_pieces ();
seek (DCPTime (), true);
}
void
Player::setup_pieces ()
+{
+ boost::mutex::scoped_lock lm (_mutex);
+ setup_pieces_unlocked ();
+}
+
+void
+Player::setup_pieces_unlocked ()
{
_pieces.clear ();
_last_video_time = DCPTime ();
_last_video_eyes = EYES_BOTH;
_last_audio_time = DCPTime ();
- _have_valid_pieces = true;
+ _suspended = false;
}
void
-Player::playlist_content_changed (weak_ptr<Content> w, int property, bool frequent)
+Player::playlist_content_change (ChangeType type, int property, bool frequent)
{
- shared_ptr<Content> c = w.lock ();
- if (!c) {
- return;
+ if (type == CHANGE_TYPE_PENDING) {
+ boost::mutex::scoped_lock lm (_mutex);
+ /* The player content is probably about to change, so we can't carry on
+ until that has happened and we've rebuilt our pieces. Stop pass()
+ and seek() from working until then.
+ */
+ _suspended = true;
+ } else if (type == CHANGE_TYPE_DONE) {
+ /* A change in our content has gone through. Re-build our pieces. */
+ setup_pieces ();
}
- if (
- property == ContentProperty::POSITION ||
- property == ContentProperty::LENGTH ||
- property == ContentProperty::TRIM_START ||
- property == ContentProperty::TRIM_END ||
- property == ContentProperty::PATH ||
- property == VideoContentProperty::FRAME_TYPE ||
- property == VideoContentProperty::COLOUR_CONVERSION ||
- property == AudioContentProperty::STREAMS ||
- property == DCPContentProperty::NEEDS_ASSETS ||
- property == DCPContentProperty::NEEDS_KDM ||
- property == TextContentProperty::COLOUR ||
- property == TextContentProperty::EFFECT ||
- property == TextContentProperty::EFFECT_COLOUR ||
- property == FFmpegContentProperty::SUBTITLE_STREAM ||
- property == FFmpegContentProperty::FILTERS
- ) {
-
- {
- boost::mutex::scoped_lock lm (_mutex);
- _have_valid_pieces = false;
- }
-
- Changed (property, frequent);
-
- } else if (
- property == TextContentProperty::LINE_SPACING ||
- property == TextContentProperty::OUTLINE_WIDTH ||
- property == TextContentProperty::Y_SCALE ||
- property == TextContentProperty::FADE_IN ||
- property == TextContentProperty::FADE_OUT ||
- property == ContentProperty::VIDEO_FRAME_RATE ||
- property == TextContentProperty::USE ||
- property == TextContentProperty::X_OFFSET ||
- property == TextContentProperty::Y_OFFSET ||
- property == TextContentProperty::X_SCALE ||
- property == TextContentProperty::FONTS ||
- property == TextContentProperty::TYPE ||
- property == VideoContentProperty::CROP ||
- property == VideoContentProperty::SCALE ||
- property == VideoContentProperty::FADE_IN ||
- property == VideoContentProperty::FADE_OUT
- ) {
-
- Changed (property, frequent);
- }
+ Change (type, property, frequent);
}
void
_black_image->make_black ();
}
- Changed (PlayerProperty::VIDEO_CONTAINER_SIZE, false);
+ Change (CHANGE_TYPE_DONE, PlayerProperty::VIDEO_CONTAINER_SIZE, false);
}
void
-Player::playlist_changed ()
+Player::playlist_change (ChangeType type)
{
- {
- boost::mutex::scoped_lock lm (_mutex);
- _have_valid_pieces = false;
+ if (type == CHANGE_TYPE_DONE) {
+ setup_pieces ();
}
-
- Changed (PlayerProperty::PLAYLIST, false);
+ Change (type, PlayerProperty::PLAYLIST, false);
}
void
*/
if (p == Film::CONTAINER) {
- Changed (PlayerProperty::FILM_CONTAINER, false);
+ Change (CHANGE_TYPE_PENDING, PlayerProperty::FILM_CONTAINER, false);
} else if (p == Film::VIDEO_FRAME_RATE) {
/* Pieces contain a FrameRateChange which contains the DCP frame rate,
so we need new pieces here.
*/
- {
- boost::mutex::scoped_lock lm (_mutex);
- _have_valid_pieces = false;
- }
- Changed (PlayerProperty::FILM_VIDEO_FRAME_RATE, false);
+ /* XXX: missing PENDING! */
+ setup_pieces ();
+ Change (CHANGE_TYPE_DONE, PlayerProperty::FILM_VIDEO_FRAME_RATE, false);
} else if (p == Film::AUDIO_PROCESSOR) {
if (_film->audio_processor ()) {
boost::mutex::scoped_lock lm (_mutex);
list<shared_ptr<Font> >
Player::get_subtitle_fonts ()
{
- /* Does not require a lock on _mutex as it's only called from DCPEncoder */
-
- if (!_have_valid_pieces) {
- setup_pieces ();
- }
+ boost::mutex::scoped_lock lm (_mutex);
list<shared_ptr<Font> > fonts;
BOOST_FOREACH (shared_ptr<Piece> i, _pieces) {
{
boost::mutex::scoped_lock lm (_mutex);
_ignore_video = true;
- _have_valid_pieces = false;
+ setup_pieces_unlocked ();
}
void
{
boost::mutex::scoped_lock lm (_mutex);
_ignore_audio = true;
- _have_valid_pieces = false;
+ setup_pieces_unlocked ();
}
void
{
boost::mutex::scoped_lock lm (_mutex);
_ignore_text = true;
+ setup_pieces_unlocked ();
}
/** Set the player to always burn open texts into the image regardless of the content settings */
{
boost::mutex::scoped_lock lm (_mutex);
_fast = true;
- _have_valid_pieces = false;
+ setup_pieces_unlocked ();
}
void
{
boost::mutex::scoped_lock lm (_mutex);
_play_referenced = true;
- _have_valid_pieces = false;
+ setup_pieces_unlocked ();
}
list<ReferencedReelAsset>
{
boost::mutex::scoped_lock lm (_mutex);
- if (!_have_valid_pieces) {
- /* This should only happen when we are under the control of the butler. In this case, _have_valid_pieces
- will be false if something in the Player has changed and we are waiting for the butler to notice
- and do a seek back to the place we were at before. During this time we don't want pass() to do anything,
- as just after setup_pieces the new decoders will be back to time 0 until the seek has gone through. Just do nothing
- here and assume that the seek will be forthcoming.
- */
+ if (_suspended) {
+ /* We can't pass in this state */
return false;
}
{
boost::mutex::scoped_lock lm (_mutex);
- if (!_have_valid_pieces) {
- setup_pieces ();
+ if (_suspended) {
+ /* We can't seek in this state */
+ return;
}
if (_shuffler) {
void
Player::set_dcp_decode_reduction (optional<int> reduction)
{
+ Change (CHANGE_TYPE_PENDING, PlayerProperty::DCP_DECODE_REDUCTION, false);
+
{
boost::mutex::scoped_lock lm (_mutex);
if (reduction == _dcp_decode_reduction) {
+ lm.unlock ();
+ Change (CHANGE_TYPE_CANCELLED, PlayerProperty::DCP_DECODE_REDUCTION, false);
return;
}
_dcp_decode_reduction = reduction;
- _have_valid_pieces = false;
+ setup_pieces_unlocked ();
}
- Changed (PlayerProperty::DCP_DECODE_REDUCTION, false);
+ Change (CHANGE_TYPE_DONE, PlayerProperty::DCP_DECODE_REDUCTION, false);
}
-DCPTime
+optional<DCPTime>
Player::content_time_to_dcp (shared_ptr<Content> content, ContentTime t)
{
boost::mutex::scoped_lock lm (_mutex);
- if (_have_valid_pieces) {
- setup_pieces ();
- }
-
BOOST_FOREACH (shared_ptr<Piece> i, _pieces) {
if (i->content == content) {
return content_time_to_dcp (i, t);
}
}
- DCPOMATIC_ASSERT (false);
- return DCPTime ();
+ /* We couldn't find this content; perhaps things are being changed over */
+ return optional<DCPTime>();
}