/*
- Copyright (C) 2016-2020 Carl Hetherington <cth@carlh.net>
+ Copyright (C) 2016-2021 Carl Hetherington <cth@carlh.net>
This file is part of DCP-o-matic.
*/
+
#include "butler.h"
#include "player.h"
#include "util.h"
using std::shared_ptr;
using boost::bind;
using boost::optional;
-using boost::function;
+using std::function;
using namespace dcpomatic;
#if BOOST_VERSION >= 106100
using namespace boost::placeholders;
#endif
+
/** Minimum video readahead in frames */
#define MINIMUM_VIDEO_READAHEAD 10
/** Maximum video readahead in frames; should never be exceeded (by much) unless there are bugs in Player */
/** Maximum audio readahead in frames; should never be exceeded (by much) unless there are bugs in Player */
#define MAXIMUM_AUDIO_READAHEAD (48000 * MAXIMUM_VIDEO_READAHEAD / 24)
+
/** @param pixel_format Pixel format functor that will be used when calling ::image on PlayerVideos coming out of this
* butler. This will be used (where possible) to prepare the PlayerVideos so that calling image() on them is quick.
- * @param aligned Same as above for the `aligned' flag.
+ * @param alignment Same as above for the `alignment' value.
* @param fast Same as above for the `fast' flag.
*/
Butler::Butler (
int audio_channels,
function<AVPixelFormat (AVPixelFormat)> pixel_format,
VideoRange video_range,
- bool aligned,
- bool fast
+ Image::Alignment alignment,
+ bool fast,
+ bool prepare_only_proxy
)
: _film (film)
, _player (player)
- , _prepare_work (new boost::asio::io_service::work (_prepare_service))
+ , _prepare_work (new boost::asio::io_service::work(_prepare_service))
, _pending_seek_accurate (false)
, _suspended (0)
, _finished (false)
, _disable_audio (false)
, _pixel_format (pixel_format)
, _video_range (video_range)
- , _aligned (aligned)
+ , _alignment (alignment)
, _fast (fast)
+ , _prepare_only_proxy (prepare_only_proxy)
{
_player_video_connection = _player->Video.connect (bind (&Butler::video, this, _1, _2));
_player_audio_connection = _player->Audio.connect (bind (&Butler::audio, this, _1, _2, _3));
}
}
+
Butler::~Butler ()
{
boost::this_thread::disable_interruption dis;
return (_video.size() < MAXIMUM_VIDEO_READAHEAD) && (_audio.size() < MAXIMUM_AUDIO_READAHEAD);
}
+
void
Butler::thread ()
try
{
+ start_of_thread ("Butler");
+
while (true) {
boost::mutex::scoped_lock lm (_mutex);
_arrived.notify_all ();
}
-/** @param blocking true if we should block until video is available. If blocking is false
+
+/** @param behaviour BLOCKING if we should block until video is available. If behaviour is NON_BLOCKING
* and no video is immediately available the method will return a 0 PlayerVideo and the error AGAIN.
* @param e if non-0 this is filled with an error code (if an error occurs) or is untouched if no error occurs.
*/
pair<shared_ptr<PlayerVideo>, DCPTime>
-Butler::get_video (bool blocking, Error* e)
+Butler::get_video (Behaviour behaviour, Error* e)
{
boost::mutex::scoped_lock lm (_mutex);
auto setup_error = [this](Error* e, Error::Code fallback) {
if (e) {
if (_died) {
- e->code = Error::DIED;
+ e->code = Error::Code::DIED;
e->message = _died_message;
} else if (_finished) {
- e->code = Error::FINISHED;
+ e->code = Error::Code::FINISHED;
} else {
e->code = fallback;
}
}
};
- if (_video.empty() && (_finished || _died || (_suspended && !blocking))) {
- setup_error (e, Error::AGAIN);
+ if (_video.empty() && (_finished || _died || (_suspended && behaviour == Behaviour::NON_BLOCKING))) {
+ setup_error (e, Error::Code::AGAIN);
return make_pair(shared_ptr<PlayerVideo>(), DCPTime());
}
}
if (_video.empty()) {
- setup_error (e, Error::NONE);
+ setup_error (e, Error::Code::NONE);
return make_pair(shared_ptr<PlayerVideo>(), DCPTime());
}
return r;
}
+
optional<TextRingBuffers::Data>
Butler::get_closed_caption ()
{
return _closed_caption.get ();
}
+
void
Butler::seek (DCPTime position, bool accurate)
{
seek_unlocked (position, accurate);
}
+
void
Butler::seek_unlocked (DCPTime position, bool accurate)
{
_summon.notify_all ();
}
+
void
Butler::prepare (weak_ptr<PlayerVideo> weak_video)
try
{
- shared_ptr<PlayerVideo> video = weak_video.lock ();
+ 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);
+ video->prepare (_pixel_format, _video_range, _alignment, _fast, _prepare_only_proxy);
LOG_TIMING("finish-prepare in %1", thread_id());
}
}
_died = true;
}
+
void
Butler::video (shared_ptr<PlayerVideo> video, DCPTime time)
{
return;
}
- _prepare_service.post (bind (&Butler::prepare, this, weak_ptr<PlayerVideo>(video)));
+ _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)
{
return;
}
- _audio.put (remap (audio, _audio_channels, _audio_mapping), time, frame_rate);
+ _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.
+
+/** Try to get `frames' frames of audio and copy it into `out'.
+ * @param behaviour BLOCKING if we should block until audio is available. If behaviour is NON_BLOCKING
+ * and no audio is immediately available the buffer will be filled with silence and boost::none
+ * will be returned.
+ * @return time of this audio, or unset if blocking was false and no data was available.
*/
optional<DCPTime>
-Butler::get_audio (float* out, Frame frames)
+Butler::get_audio (Behaviour behaviour, float* out, Frame frames)
{
- optional<DCPTime> t = _audio.get (out, _audio_channels, frames);
+ boost::mutex::scoped_lock lm (_mutex);
+
+ while (behaviour == Behaviour::BLOCKING && !_finished && !_died && _audio.size() < frames) {
+ _arrived.wait (lm);
+ }
+
+ auto t = _audio.get (out, _audio_channels, frames);
_summon.notify_all ();
return t;
}
+
void
Butler::disable_audio ()
{
_disable_audio = true;
}
+
pair<size_t, string>
Butler::memory_used () const
{
return _video.memory_used();
}
+
void
Butler::player_change (ChangeType type, int property)
{
_summon.notify_all ();
}
+
void
Butler::text (PlayerText pt, TextType type, optional<DCPTextTrack> track, DCPTimePeriod period)
{
_closed_caption.put (pt, *track, period);
}
+
string
Butler::Error::summary () const
{
switch (code)
{
- case Error::NONE:
+ case Error::Code::NONE:
return "No error registered";
- case Error::AGAIN:
+ case Error::Code::AGAIN:
return "Butler not ready";
- case Error::DIED:
+ case Error::Code::DIED:
return String::compose("Butler died (%1)", message);
- case Error::FINISHED:
+ case Error::Code::FINISHED:
return "Butler finished";
}