+ _summon.notify_all ();
+}
+
+
+void
+Butler::prepare (weak_ptr<PlayerVideo> weak_video)
+try
+{
+ auto video = weak_video.lock ();
+ /* If the weak_ptr cannot be locked the video obviously no longer requires any work */
+ if (video) {
+ LOG_TIMING("start-prepare in %1", thread_id());
+ video->prepare (_pixel_format, _video_range, _aligned, _fast);
+ LOG_TIMING("finish-prepare in %1", thread_id());
+ }
+}
+catch (std::exception& e)
+{
+ store_current ();
+ boost::mutex::scoped_lock lm (_mutex);
+ _died = true;
+ _died_message = e.what ();
+}
+catch (...)
+{
+ store_current ();
+ boost::mutex::scoped_lock lm (_mutex);
+ _died = true;
+}
+
+
+void
+Butler::video (shared_ptr<PlayerVideo> video, DCPTime time)
+{
+ boost::mutex::scoped_lock lm (_mutex);
+
+ if (_pending_seek_position) {
+ /* Don't store any video in this case */
+ return;
+ }
+
+ _prepare_service.post (bind(&Butler::prepare, this, weak_ptr<PlayerVideo>(video)));
+
+ _video.put (video, time);
+}
+
+
+void
+Butler::audio (shared_ptr<AudioBuffers> audio, DCPTime time, int frame_rate)
+{
+ boost::mutex::scoped_lock lm (_mutex);
+ if (_pending_seek_position || _disable_audio) {
+ /* Don't store any audio in these cases */
+ return;
+ }
+
+ _audio.put (remap(audio, _audio_channels, _audio_mapping), time, frame_rate);
+}
+
+
+/** Try to get `frames' frames of audio and copy it into `out'. Silence
+ * will be filled if no audio is available.
+ * @return time of this audio, or unset if there was a buffer underrun.
+ */
+optional<DCPTime>
+Butler::get_audio (float* out, Frame frames)
+{
+ auto t = _audio.get (out, _audio_channels, frames);
+ _summon.notify_all ();
+ return t;
+}
+
+
+void
+Butler::disable_audio ()
+{
+ boost::mutex::scoped_lock lm (_mutex);
+ _disable_audio = true;
+}
+
+
+pair<size_t, string>
+Butler::memory_used () const
+{
+ /* XXX: should also look at _audio.memory_used() */
+ return _video.memory_used();
+}
+
+
+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;