/*
- Copyright (C) 2016-2017 Carl Hetherington <cth@carlh.net>
+ Copyright (C) 2016-2018 Carl Hetherington <cth@carlh.net>
This file is part of DCP-o-matic.
using boost::shared_ptr;
using boost::bind;
using boost::optional;
+using boost::function;
/** Minimum video readahead in frames */
#define MINIMUM_VIDEO_READAHEAD 10
/** Minimum audio readahead in frames; should never be reached unless there are bugs in Player */
#define MAXIMUM_AUDIO_READAHEAD (48000 * MAXIMUM_VIDEO_READAHEAD / 24)
-Butler::Butler (shared_ptr<Player> player, shared_ptr<Log> log, AudioMapping audio_mapping, int audio_channels)
+/** @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 fast Same as above for the `fast' flag.
+ */
+Butler::Butler (
+ shared_ptr<Player> player,
+ shared_ptr<Log> log,
+ AudioMapping audio_mapping,
+ int audio_channels,
+ function<AVPixelFormat (AVPixelFormat)> pixel_format,
+ bool aligned,
+ bool fast
+ )
: _player (player)
, _log (log)
, _prepare_work (new boost::asio::io_service::work (_prepare_service))
, _audio_mapping (audio_mapping)
, _audio_channels (audio_channels)
, _disable_audio (false)
+ , _pixel_format (pixel_format)
+ , _aligned (aligned)
+ , _fast (fast)
{
_player_video_connection = _player->Video.connect (bind (&Butler::video, this, _1, _2));
_player_audio_connection = _player->Audio.connect (bind (&Butler::audio, this, _1, _2));
LOG_TIMING("start-prepare in %1", thread_id());
}
- video->prepare ();
+ video->prepare (_pixel_format, _aligned, _fast);
if (_log) {
LOG_TIMING("finish-prepare in %1", thread_id());
class Butler : public ExceptionStore, public boost::noncopyable
{
public:
- Butler (boost::shared_ptr<Player> player, boost::shared_ptr<Log> log, AudioMapping map, int audio_channels);
+ Butler (
+ boost::shared_ptr<Player> player,
+ boost::shared_ptr<Log> log,
+ AudioMapping map,
+ int audio_channels,
+ boost::function<AVPixelFormat (AVPixelFormat)> pixel_format,
+ bool aligned,
+ bool fast
+ );
+
~Butler ();
void seek (DCPTime position, bool accurate);
bool _disable_audio;
+ boost::function<AVPixelFormat (AVPixelFormat)> _pixel_format;
+ bool _aligned;
+ bool _fast;
+
/** If we are waiting to be refilled following a seek, this is the time we were
seeking to.
*/
}
}
- _butler.reset (new Butler (_player, film->log(), map, _output_audio_channels));
+ _butler.reset (new Butler (_player, film->log(), map, _output_audio_channels, bind(&PlayerVideo::force, _1, FFmpegFileEncoder::pixel_format(format)), true, false));
}
void
#include "log.h"
#include "image.h"
#include "cross.h"
-#include "butler.h"
#include "compose.hpp"
#include <iostream>
, _audio_frame_rate (audio_frame_rate)
, _log (log)
{
+ _pixel_format = pixel_format (format);
+
switch (format) {
case EXPORT_FORMAT_PRORES:
- _pixel_format = AV_PIX_FMT_YUV422P10;
_sample_format = AV_SAMPLE_FMT_S16;
_video_codec_name = "prores_ks";
_audio_codec_name = "pcm_s16le";
av_dict_set (&_video_options, "threads", "auto", 0);
break;
case EXPORT_FORMAT_H264:
- _pixel_format = AV_PIX_FMT_YUV420P;
_sample_format = AV_SAMPLE_FMT_FLTP;
_video_codec_name = "libx264";
_audio_codec_name = "aac";
_pending_audio.reset (new AudioBuffers(channels, 0));
}
+AVPixelFormat
+FFmpegFileEncoder::pixel_format (ExportFormat format)
+{
+ switch (format) {
+ case EXPORT_FORMAT_PRORES:
+ return AV_PIX_FMT_YUV422P10;
+ case EXPORT_FORMAT_H264:
+ return AV_PIX_FMT_YUV420P;
+ default:
+ DCPOMATIC_ASSERT (false);
+ }
+
+ return AV_PIX_FMT_YUV422P10;
+}
+
void
FFmpegFileEncoder::setup_video ()
{
void flush ();
+ static AVPixelFormat pixel_format (ExportFormat format);
+
private:
void setup_video ();
void setup_audio ();
_text = image;
}
-/** Create an image for this frame.
+shared_ptr<Image>
+PlayerVideo::image (function<AVPixelFormat (AVPixelFormat)> pixel_format, bool aligned, bool fast) const
+{
+ /* XXX: this assumes that image() and prepare() are only ever called with the same parameters */
+
+ boost::mutex::scoped_lock lm (_mutex);
+ if (!_image) {
+ make_image (pixel_format, aligned, fast);
+ }
+ return _image;
+}
+
+/** Create an image for this frame. A lock must be held on _mutex.
* @param pixel_format Function which is called to decide what pixel format the output image should be;
* it is passed the pixel format of the input image from the ImageProxy, and should return the desired
* output pixel format. Two functions force and keep_xyz_or_rgb are provided for use here.
* @param aligned true if the output image should be aligned to 32-byte boundaries.
* @param fast true to be fast at the expense of quality.
*/
-shared_ptr<Image>
-PlayerVideo::image (function<AVPixelFormat (AVPixelFormat)> pixel_format, bool aligned, bool fast) const
+void
+PlayerVideo::make_image (function<AVPixelFormat (AVPixelFormat)> pixel_format, bool aligned, bool fast) const
{
pair<shared_ptr<Image>, int> prox = _in->image (_inter_size);
shared_ptr<Image> im = prox.first;
yuv_to_rgb = _colour_conversion.get().yuv_to_rgb();
}
- shared_ptr<Image> out = im->crop_scale_window (
+ _image = im->crop_scale_window (
total_crop, _inter_size, _out_size, yuv_to_rgb, pixel_format (im->pixel_format()), aligned, fast
);
if (_text) {
- out->alpha_blend (Image::ensure_aligned (_text->image), _text->position);
+ _image->alpha_blend (Image::ensure_aligned (_text->image), _text->position);
}
if (_fade) {
- out->fade (_fade.get ());
+ _image->fade (_fade.get ());
}
-
- return out;
}
void
}
void
-PlayerVideo::prepare ()
+PlayerVideo::prepare (function<AVPixelFormat (AVPixelFormat)> pixel_format, bool aligned, bool fast)
{
_in->prepare (_inter_size);
+ boost::mutex::scoped_lock lm (_mutex);
+ if (!_image) {
+ make_image (pixel_format, aligned, fast);
+ }
}
size_t
void set_text (PositionImage);
- void prepare ();
+ void prepare (boost::function<AVPixelFormat (AVPixelFormat)> pixel_format, bool aligned, bool fast);
boost::shared_ptr<Image> image (boost::function<AVPixelFormat (AVPixelFormat)> pixel_format, bool aligned, bool fast) const;
static AVPixelFormat force (AVPixelFormat, AVPixelFormat);
}
private:
+ void make_image (boost::function<AVPixelFormat (AVPixelFormat)> pixel_format, bool aligned, bool fast) const;
+
boost::shared_ptr<const ImageProxy> _in;
Crop _crop;
boost::optional<double> _fade;
boost::weak_ptr<Content> _content;
/** Video frame that we came from. Again, this is for reset_metadata() */
boost::optional<Frame> _video_frame;
+
+ mutable boost::mutex _mutex;
+ mutable boost::shared_ptr<Image> _image;
};
#endif
map.set (dcp::RS, 1, 1 / sqrt(2)); // Rs -> Rt
}
- _butler.reset (new Butler (_player, _film->log(), map, _audio_channels));
+ _butler.reset (new Butler(_player, _film->log(), map, _audio_channels, bind(&PlayerVideo::force, _1, AV_PIX_FMT_RGB24), false, true));
if (!Config::instance()->sound() && !_audio.isStreamOpen()) {
_butler->disable_audio ();
}
* image and convert it (from whatever the user has said it is) to RGB.
*/
- _frame = _player_video.first->image (
- bind (&PlayerVideo::force, _1, AV_PIX_FMT_RGB24),
- false, true
- );
+ _frame = _player_video.first->image (bind(&PlayerVideo::force, _1, AV_PIX_FMT_RGB24), false, true);
ImageChanged (_player_video.first);
map.set (i, i, 1);
}
- Butler butler (shared_ptr<Player>(new Player(film, film->playlist())), film->log(), map, 6);
+ Butler butler (shared_ptr<Player>(new Player(film, film->playlist())), film->log(), map, 6, bind(&PlayerVideo::force, _1, AV_PIX_FMT_RGB24), false, false);
BOOST_CHECK (butler.get_video().second == DCPTime());
BOOST_CHECK (butler.get_video().second == DCPTime::from_frames(1, 24));
film->examine_and_add_content (content);
wait_for_jobs ();
- shared_ptr<Butler> butler (new Butler(shared_ptr<Player>(new Player(film, film->playlist())), shared_ptr<Log>(), AudioMapping(6, 6), 6));
+ shared_ptr<Butler> butler (
+ new Butler(
+ shared_ptr<Player>(new Player(film, film->playlist())),
+ shared_ptr<Log>(),
+ AudioMapping(6, 6),
+ 6,
+ bind(&PlayerVideo::force, _1, AV_PIX_FMT_RGB24),
+ false,
+ true)
+ );
float* audio_buffer = new float[2000*6];
while (true) {
pair<shared_ptr<PlayerVideo>, DCPTime> p = butler->get_video ();
player->set_always_burn_open_subtitles ();
player->set_play_referenced ();
- shared_ptr<Butler> butler (new Butler (player, film->log(), AudioMapping(), 2));
+ shared_ptr<Butler> butler (new Butler (player, film->log(), AudioMapping(), 2, bind(PlayerVideo::force, _1, AV_PIX_FMT_RGB24), false, true));
butler->disable_audio();
for (int i = 0; i < 10; ++i) {
player->set_always_burn_open_subtitles ();
player->set_play_referenced ();
- shared_ptr<Butler> butler (new Butler (player, film->log(), AudioMapping(), 2));
+ shared_ptr<Butler> butler (new Butler(player, film->log(), AudioMapping(), 2, bind(PlayerVideo::force, _1, AV_PIX_FMT_RGB24), false, true));
butler->disable_audio();
butler->seek(DCPTime::from_seconds(5), true);