+
+
+void
+Butler::player_change (ChangeType type, int property)
+{
+ if (property == VideoContentProperty::CROP) {
+ if (type == ChangeType::DONE) {
+ auto film = _film.lock();
+ if (film) {
+ _video.reset_metadata(film, _player.video_container_size());
+ }
+ }
+ return;
+ }
+
+ boost::mutex::scoped_lock lm (_mutex);
+
+ if (type == ChangeType::PENDING) {
+ ++_suspended;
+ } else if (type == ChangeType::DONE) {
+ --_suspended;
+ if (_died || _pending_seek_position) {
+ lm.unlock ();
+ _summon.notify_all ();
+ return;
+ }
+
+ DCPTime seek_to;
+ auto next = _video.get().second;
+ if (_awaiting && _awaiting > next) {
+ /* We have recently done a player_changed seek and our buffers haven't been refilled yet,
+ so assume that we're seeking to the same place as last time.
+ */
+ seek_to = *_awaiting;
+ } else {
+ seek_to = next;
+ }
+
+ seek_unlocked (seek_to, true);
+ _awaiting = seek_to;
+ } else if (type == ChangeType::CANCELLED) {
+ --_suspended;
+ }
+
+ lm.unlock ();
+ _summon.notify_all ();
+}
+
+
+void
+Butler::text (PlayerText pt, TextType type, optional<DCPTextTrack> track, DCPTimePeriod period)
+{
+ if (type != TextType::CLOSED_CAPTION) {
+ return;
+ }
+
+ DCPOMATIC_ASSERT (track);
+
+ _closed_caption.put (pt, *track, period);
+}
+
+
+string
+Butler::Error::summary () const
+{
+ switch (code)
+ {
+ case Error::Code::NONE:
+ return "No error registered";
+ case Error::Code::AGAIN:
+ return "Butler not ready";
+ case Error::Code::DIED:
+ return String::compose("Butler died (%1)", message);
+ case Error::Code::FINISHED:
+ return "Butler finished";
+ }
+
+ return "";
+}
+