return ()
else:
return (('openjpeg-cdist', None),
- ('ffmpeg-cdist', '488d5d4496af5e3a3b9d31d6b221e8eeada6b77e'),
+ ('ffmpeg-cdist', '7a23ec9c771184ab563cfe24ad9b427f38368961'),
('libdcp', 'v0.49'))
def build(env, target):
DelayLine::process_video (shared_ptr<const Image> image, bool same, boost::shared_ptr<Subtitle> sub, double t)
{
if (_seconds < 0) {
- t += _seconds;
+ t -= _seconds;
}
Video (image, same, sub, t);
void
Encoder::process_audio (shared_ptr<const AudioBuffers> data)
{
+ if (!data->frames ()) {
+ return;
+ }
+
#if HAVE_SWRESAMPLE
/* Maybe sample-rate convert */
if (_swr_context) {
+++ /dev/null
-/*
- Copyright (C) 2012 Carl Hetherington <cth@carlh.net>
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
-*/
-
-extern "C" {
-#include <libavfilter/avfiltergraph.h>
-}
-#include "exceptions.h"
-
-#include "i18n.h"
-
-#if LIBAVFILTER_VERSION_MAJOR == 2 && LIBAVFILTER_VERSION_MINOR == 15
-
-typedef struct {
- enum PixelFormat pix_fmt;
-} AVSinkContext;
-
-static int
-avsink_init (AVFilterContext* ctx, const char* args, void* opaque)
-{
- AVSinkContext* priv = (AVSinkContext *) ctx->priv;
- if (!opaque) {
- return AVERROR (EINVAL);
- }
-
- *priv = *(AVSinkContext *) opaque;
- return 0;
-}
-
-static void
-null_end_frame (AVFilterLink *)
-{
-
-}
-
-static int
-avsink_query_formats (AVFilterContext* ctx)
-{
- AVSinkContext* priv = (AVSinkContext *) ctx->priv;
- enum PixelFormat pix_fmts[] = {
- priv->pix_fmt,
- PIX_FMT_NONE
- };
-
- avfilter_set_common_formats (ctx, avfilter_make_format_list ((int *) pix_fmts));
- return 0;
-}
-
-#endif
-
-AVFilter*
-get_sink ()
-{
-#if LIBAVFILTER_VERSION_MAJOR == 2 && LIBAVFILTER_VERSION_MINOR == 15
- /* XXX does this leak stuff? */
- AVFilter* buffer_sink = new AVFilter;
- buffer_sink->name = av_strdup (N_("avsink"));
- buffer_sink->priv_size = sizeof (AVSinkContext);
- buffer_sink->init = avsink_init;
- buffer_sink->query_formats = avsink_query_formats;
- buffer_sink->inputs = new AVFilterPad[2];
- AVFilterPad* i0 = const_cast<AVFilterPad*> (&buffer_sink->inputs[0]);
- i0->name = N_("default");
- i0->type = AVMEDIA_TYPE_VIDEO;
- i0->min_perms = AV_PERM_READ;
- i0->rej_perms = 0;
- i0->start_frame = 0;
- i0->get_video_buffer = 0;
- i0->get_audio_buffer = 0;
- i0->end_frame = null_end_frame;
- i0->draw_slice = 0;
- i0->filter_samples = 0;
- i0->poll_frame = 0;
- i0->request_frame = 0;
- i0->config_props = 0;
- const_cast<AVFilterPad*> (&buffer_sink->inputs[1])->name = 0;
- buffer_sink->outputs = new AVFilterPad[1];
- const_cast<AVFilterPad*> (&buffer_sink->outputs[0])->name = 0;
- return buffer_sink;
-#else
- AVFilter* buffer_sink = avfilter_get_by_name(N_("buffersink"));
- if (buffer_sink == 0) {
- throw DecodeError (N_("Could not create buffer sink filter"));
- }
-
- return buffer_sink;
-#endif
-}
-
-#if LIBAVFILTER_VERSION_MAJOR == 2 && LIBAVFILTER_VERSION_MINOR == 15
-AVFilterInOut *
-avfilter_inout_alloc ()
-{
- return (AVFilterInOut *) av_malloc (sizeof (AVFilterInOut));
-}
-#endif
-
-#ifndef HAVE_AV_FRAME_GET_BEST_EFFORT_TIMESTAMP
-int64_t av_frame_get_best_effort_timestamp (AVFrame const * f)
-{
- return f->best_effort_timestamp;
-}
-
-#endif
+++ /dev/null
-/*
- Copyright (C) 2012 Carl Hetherington <cth@carlh.net>
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
-*/
-
-struct AVFilterInOut;
-
-extern AVFilter* get_sink ();
-extern AVFilterInOut* avfilter_inout_alloc ();
-
-#ifndef HAVE_AV_PIXEL_FORMAT
-#define AVPixelFormat PixelFormat
-#endif
-
-#ifndef HAVE_AV_FRAME_GET_BEST_EFFORT_TIMESTAMP
-extern int64_t av_frame_get_best_effort_timestamp (AVFrame const *);
-#endif
void
FFmpegDecoder::filter_and_emit_video ()
{
- boost::mutex::scoped_lock lm (_filter_graphs_mutex);
+ int64_t const bet = av_frame_get_best_effort_timestamp (_frame);
+ if (bet == AV_NOPTS_VALUE) {
+ _film->log()->log ("Dropping frame without PTS");
+ return;
+ }
shared_ptr<FilterGraph> graph;
- list<shared_ptr<FilterGraph> >::iterator i = _filter_graphs.begin();
- while (i != _filter_graphs.end() && !(*i)->can_process (libdcp::Size (_frame->width, _frame->height), (AVPixelFormat) _frame->format)) {
- ++i;
- }
-
- if (i == _filter_graphs.end ()) {
- graph.reset (new FilterGraph (_film, this, libdcp::Size (_frame->width, _frame->height), (AVPixelFormat) _frame->format));
- _filter_graphs.push_back (graph);
- _film->log()->log (String::compose (N_("New graph for %1x%2, pixel format %3"), _frame->width, _frame->height, _frame->format));
- } else {
- graph = *i;
+ {
+ boost::mutex::scoped_lock lm (_filter_graphs_mutex);
+
+ list<shared_ptr<FilterGraph> >::iterator i = _filter_graphs.begin();
+ while (i != _filter_graphs.end() && !(*i)->can_process (libdcp::Size (_frame->width, _frame->height), (AVPixelFormat) _frame->format)) {
+ ++i;
+ }
+
+ if (i == _filter_graphs.end ()) {
+ graph = filter_graph_factory (_film, this, libdcp::Size (_frame->width, _frame->height), (AVPixelFormat) _frame->format);
+ _filter_graphs.push_back (graph);
+ _film->log()->log (String::compose (N_("New graph for %1x%2, pixel format %3"), _frame->width, _frame->height, _frame->format));
+ } else {
+ graph = *i;
+ }
}
list<shared_ptr<Image> > images = graph->process (_frame);
for (list<shared_ptr<Image> >::iterator i = images.begin(); i != images.end(); ++i) {
- int64_t const bet = av_frame_get_best_effort_timestamp (_frame);
- if (bet != AV_NOPTS_VALUE) {
- emit_video (*i, false, bet * av_q2d (_format_context->streams[_video_stream]->time_base));
- } else {
- _film->log()->log ("Dropping frame without PTS");
- }
+ emit_video (*i, false, bet * av_q2d (_format_context->streams[_video_stream]->time_base));
}
}
extern "C" {
#include <libavfilter/avfiltergraph.h>
-#ifdef HAVE_BUFFERSRC_H
#include <libavfilter/buffersrc.h>
-#endif
-#if (LIBAVFILTER_VERSION_MAJOR == 2 && LIBAVFILTER_VERSION_MINOR >= 53 && LIBAVFILTER_VERSION_MINOR <= 77) || LIBAVFILTER_VERSION_MAJOR == 3
#include <libavfilter/avcodec.h>
#include <libavfilter/buffersink.h>
-#elif LIBAVFILTER_VERSION_MAJOR == 2 && LIBAVFILTER_VERSION_MINOR == 15
-#include <libavfilter/vsrc_buffer.h>
-#endif
#include <libavformat/avio.h>
}
#include "decoder.h"
#include "filter_graph.h"
-#include "ffmpeg_compatibility.h"
#include "filter.h"
#include "exceptions.h"
#include "image.h"
using boost::shared_ptr;
using libdcp::Size;
-/** Construct a FilterGraph for the settings in a film.
+/** Construct a FFmpegFilterGraph for the settings in a film.
* @param film Film.
* @param decoder Decoder that we are using.
* @param s Size of the images to process.
* @param p Pixel format of the images to process.
*/
-FilterGraph::FilterGraph (shared_ptr<Film> film, FFmpegDecoder* decoder, libdcp::Size s, AVPixelFormat p)
+FFmpegFilterGraph::FFmpegFilterGraph (shared_ptr<Film> film, FFmpegDecoder* decoder, libdcp::Size s, AVPixelFormat p)
: _buffer_src_context (0)
, _buffer_sink_context (0)
, _size (s)
, _pixel_format (p)
{
+ _frame = av_frame_alloc ();
+
string filters = Filter::ffmpeg_strings (film->filters()).first;
if (!filters.empty ()) {
filters += N_(",");
throw DecodeError (N_("could not find buffer src filter"));
}
- AVFilter* buffer_sink = get_sink ();
+ AVFilter* buffer_sink = avfilter_get_by_name(N_("buffersink"));
+ if (buffer_sink == 0) {
+ throw DecodeError (N_("Could not create buffer sink filter"));
+ }
stringstream a;
- a << _size.width << N_(":")
- << _size.height << N_(":")
- << _pixel_format << N_(":")
- << decoder->time_base_numerator() << N_(":")
- << decoder->time_base_denominator() << N_(":")
- << decoder->sample_aspect_ratio_numerator() << N_(":")
- << decoder->sample_aspect_ratio_denominator();
+ a << "video_size=" << _size.width << "x" << _size.height << ":"
+ << "pix_fmt=" << _pixel_format << ":"
+ << "time_base=" << decoder->time_base_numerator() << "/" << decoder->time_base_denominator() << ":"
+ << "pixel_aspect=" << decoder->sample_aspect_ratio_numerator() << "/" << decoder->sample_aspect_ratio_denominator();
int r;
- if ((r = avfilter_graph_create_filter (&_buffer_src_context, buffer_src, N_("in"), a.str().c_str(), 0, graph)) < 0) {
+ if ((r = avfilter_graph_create_filter (&_buffer_src_context, buffer_src, "in", a.str().c_str(), 0, graph)) < 0) {
throw DecodeError (N_("could not create buffer source"));
}
throw DecodeError (N_("could not create buffer sink."));
}
+ av_free (sink_params);
+
AVFilterInOut* outputs = avfilter_inout_alloc ();
outputs->name = av_strdup(N_("in"));
outputs->filter_ctx = _buffer_src_context;
inputs->pad_idx = 0;
inputs->next = 0;
-#if LIBAVFILTER_VERSION_MAJOR == 2 && LIBAVFILTER_VERSION_MINOR == 15
- if (avfilter_graph_parse (graph, filters.c_str(), inputs, outputs, 0) < 0) {
- throw DecodeError (N_("could not set up filter graph."));
- }
-#else
if (avfilter_graph_parse (graph, filters.c_str(), &inputs, &outputs, 0) < 0) {
throw DecodeError (N_("could not set up filter graph."));
}
-#endif
if (avfilter_graph_config (graph, 0) < 0) {
throw DecodeError (N_("could not configure filter graph."));
/* XXX: leaking `inputs' / `outputs' ? */
}
+FFmpegFilterGraph::~FFmpegFilterGraph ()
+{
+ av_frame_free (&_frame);
+}
+
/** Take an AVFrame and process it using our configured filters, returning a
- * set of Images.
+ * set of Images. Caller handles memory management of the input frame.
*/
list<shared_ptr<Image> >
-FilterGraph::process (AVFrame const * frame)
+FFmpegFilterGraph::process (AVFrame* frame)
{
list<shared_ptr<Image> > images;
-
-#if LIBAVFILTER_VERSION_MAJOR == 2 && LIBAVFILTER_VERSION_MINOR >= 53 && LIBAVFILTER_VERSION_MINOR <= 61
-
- if (av_vsrc_buffer_add_frame (_buffer_src_context, frame, 0) < 0) {
- throw DecodeError (N_("could not push buffer into filter chain."));
- }
-
-#elif LIBAVFILTER_VERSION_MAJOR == 2 && LIBAVFILTER_VERSION_MINOR == 15
-
- AVRational par;
- par.num = sample_aspect_ratio_numerator ();
- par.den = sample_aspect_ratio_denominator ();
-
- if (av_vsrc_buffer_add_frame (_buffer_src_context, frame, 0, par) < 0) {
- throw DecodeError (N_("could not push buffer into filter chain."));
- }
-
-#else
if (av_buffersrc_write_frame (_buffer_src_context, frame) < 0) {
throw DecodeError (N_("could not push buffer into filter chain."));
}
-#endif
-
-#if LIBAVFILTER_VERSION_MAJOR == 2 && LIBAVFILTER_VERSION_MINOR >= 15 && LIBAVFILTER_VERSION_MINOR <= 61
- while (avfilter_poll_frame (_buffer_sink_context->inputs[0])) {
-#else
- while (av_buffersink_read (_buffer_sink_context, 0)) {
-#endif
-
-#if LIBAVFILTER_VERSION_MAJOR == 2 && LIBAVFILTER_VERSION_MINOR >= 15
-
- int r = avfilter_request_frame (_buffer_sink_context->inputs[0]);
- if (r < 0) {
- throw DecodeError (N_("could not request filtered frame"));
- }
-
- AVFilterBufferRef* filter_buffer = _buffer_sink_context->inputs[0]->cur_buf;
-
-#else
-
- AVFilterBufferRef* filter_buffer;
- if (av_buffersink_get_buffer_ref (_buffer_sink_context, &filter_buffer, 0) < 0) {
- filter_buffer = 0;
+ while (1) {
+ if (av_buffersink_get_frame (_buffer_sink_context, _frame) < 0) {
+ break;
}
-#endif
-
- if (filter_buffer) {
- /* This takes ownership of filter_buffer */
- images.push_back (shared_ptr<Image> (new FilterBufferImage ((PixelFormat) frame->format, filter_buffer)));
- }
+ images.push_back (shared_ptr<Image> (new SimpleImage (_frame)));
}
return images;
* @return true if this chain can process images with `s' and `p', otherwise false.
*/
bool
-FilterGraph::can_process (libdcp::Size s, AVPixelFormat p) const
+FFmpegFilterGraph::can_process (libdcp::Size s, AVPixelFormat p) const
{
return (_size == s && _pixel_format == p);
}
+
+list<shared_ptr<Image> >
+EmptyFilterGraph::process (AVFrame* frame)
+{
+ list<shared_ptr<Image> > im;
+ im.push_back (shared_ptr<Image> (new SimpleImage (frame)));
+ return im;
+}
+
+shared_ptr<FilterGraph>
+filter_graph_factory (shared_ptr<Film> film, FFmpegDecoder* decoder, libdcp::Size size, AVPixelFormat pixel_format)
+{
+ if (film->filters().empty() && film->crop() == Crop()) {
+ return shared_ptr<FilterGraph> (new EmptyFilterGraph);
+ }
+
+ return shared_ptr<FilterGraph> (new FFmpegFilterGraph (film, decoder, size, pixel_format));
+}
#define DVDOMATIC_FILTER_GRAPH_H
#include "util.h"
-#include "ffmpeg_compatibility.h"
class Image;
class VideoFilter;
class FFmpegDecoder;
-/** @class FilterGraph
+class FilterGraph
+{
+public:
+ virtual bool can_process (libdcp::Size, AVPixelFormat) const = 0;
+ virtual std::list<boost::shared_ptr<Image> > process (AVFrame *) = 0;
+};
+
+class EmptyFilterGraph : public FilterGraph
+{
+public:
+ bool can_process (libdcp::Size, AVPixelFormat) const {
+ return true;
+ }
+
+ std::list<boost::shared_ptr<Image> > process (AVFrame *);
+};
+
+/** @class FFmpegFilterGraph
* @brief A graph of FFmpeg filters.
*/
-class FilterGraph
+class FFmpegFilterGraph : public FilterGraph
{
public:
- FilterGraph (boost::shared_ptr<Film> film, FFmpegDecoder* decoder, libdcp::Size s, AVPixelFormat p);
+ FFmpegFilterGraph (boost::shared_ptr<Film> film, FFmpegDecoder* decoder, libdcp::Size s, AVPixelFormat p);
+ ~FFmpegFilterGraph ();
bool can_process (libdcp::Size s, AVPixelFormat p) const;
- std::list<boost::shared_ptr<Image> > process (AVFrame const * frame);
+ std::list<boost::shared_ptr<Image> > process (AVFrame * frame);
private:
AVFilterContext* _buffer_src_context;
AVFilterContext* _buffer_sink_context;
libdcp::Size _size; ///< size of the images that this chain can process
AVPixelFormat _pixel_format; ///< pixel format of the images that this chain can process
+ AVFrame* _frame;
};
+boost::shared_ptr<FilterGraph> filter_graph_factory (boost::shared_ptr<Film>, FFmpegDecoder *, libdcp::Size, AVPixelFormat);
+
#endif
#include <libavfilter/avfiltergraph.h>
#include <libpostproc/postprocess.h>
#include <libavutil/pixfmt.h>
+#include <libavutil/pixdesc.h>
}
#include "image.h"
#include "exceptions.h"
int
Image::lines (int n) const
{
- switch (_pixel_format) {
- case PIX_FMT_YUV420P:
- if (n == 0) {
- return size().height;
- } else {
- return size().height / 2;
- }
- break;
- case PIX_FMT_RGB24:
- case PIX_FMT_RGBA:
- case PIX_FMT_YUV422P10LE:
- case PIX_FMT_YUV422P:
- case PIX_FMT_YUV444P:
- case PIX_FMT_YUV444P9BE:
- case PIX_FMT_YUV444P9LE:
- case PIX_FMT_YUV444P10BE:
- case PIX_FMT_YUV444P10LE:
- case PIX_FMT_UYVY422:
+ if (n == 0) {
return size().height;
- default:
+ }
+
+ AVPixFmtDescriptor const * d = av_pix_fmt_desc_get(_pixel_format);
+ if (!d) {
throw PixelFormatError (N_("lines()"), _pixel_format);
}
-
- return 0;
+
+ return size().height / pow(2, d->log2_chroma_h);
}
/** @return Number of components */
int
Image::components () const
{
- switch (_pixel_format) {
- case PIX_FMT_YUV420P:
- case PIX_FMT_YUV422P10LE:
- case PIX_FMT_YUV422P:
- case PIX_FMT_YUV444P:
- case PIX_FMT_YUV444P9BE:
- case PIX_FMT_YUV444P9LE:
- case PIX_FMT_YUV444P10BE:
- case PIX_FMT_YUV444P10LE:
- return 3;
- case PIX_FMT_RGB24:
- case PIX_FMT_RGBA:
- case PIX_FMT_UYVY422:
- return 1;
- default:
+ AVPixFmtDescriptor const * d = av_pix_fmt_desc_get(_pixel_format);
+ if (!d) {
throw PixelFormatError (N_("components()"), _pixel_format);
}
- return 0;
+ if ((d->flags & PIX_FMT_PLANAR) == 0) {
+ return 1;
+ }
+
+ return d->nb_components;
}
shared_ptr<Image>
{
/* U/V black value for 8-bit colour */
static uint8_t const eight_bit_uv = (1 << 7) - 1;
-
/* U/V black value for 9-bit colour */
static uint16_t const nine_bit_uv = (1 << 8) - 1;
-
/* U/V black value for 10-bit colour */
static uint16_t const ten_bit_uv = (1 << 9) - 1;
+ /* U/V black value for 16-bit colour */
+ static uint16_t const sixteen_bit_uv = (1 << 15) - 1;
switch (_pixel_format) {
case PIX_FMT_YUV420P:
case PIX_FMT_YUV444P10LE:
yuv_16_black (ten_bit_uv);
break;
+
+ case PIX_FMT_YUV422P16LE:
+ case PIX_FMT_YUV444P16LE:
+ yuv_16_black (sixteen_bit_uv);
+ break;
case PIX_FMT_YUV444P10BE:
case PIX_FMT_YUV422P10BE:
yuv_16_black (swap_16 (ten_bit_uv));
-
+ break;
+
case PIX_FMT_RGB24:
memset (data()[0], 0, lines(0) * stride()[0]);
break;
float
Image::bytes_per_pixel (int c) const
{
- if (c == 3) {
+ AVPixFmtDescriptor const * d = av_pix_fmt_desc_get(_pixel_format);
+ if (!d) {
+ throw PixelFormatError (N_("lines()"), _pixel_format);
+ }
+
+ if (c >= components()) {
return 0;
}
+
+ float bpp[4] = { 0, 0, 0, 0 };
+
+ bpp[0] = floor ((d->comp[0].depth_minus1 + 1 + 7) / 8);
+ if (d->nb_components > 1) {
+ bpp[1] = floor ((d->comp[1].depth_minus1 + 1 + 7) / 8) / pow (2, d->log2_chroma_w);
+ }
+ if (d->nb_components > 2) {
+ bpp[2] = floor ((d->comp[2].depth_minus1 + 1 + 7) / 8) / pow (2, d->log2_chroma_w);
+ }
+ if (d->nb_components > 3) {
+ bpp[3] = floor ((d->comp[3].depth_minus1 + 1 + 7) / 8) / pow (2, d->log2_chroma_w);
+ }
- switch (_pixel_format) {
- case PIX_FMT_RGB24:
- if (c == 0) {
- return 3;
- } else {
- return 0;
- }
- case PIX_FMT_RGBA:
- if (c == 0) {
- return 4;
- } else {
- return 0;
- }
- case PIX_FMT_YUV420P:
- case PIX_FMT_YUV422P:
- if (c == 0) {
- return 1;
- } else {
- return 0.5;
- }
- case PIX_FMT_YUV422P10LE:
- if (c == 0) {
- return 2;
- } else {
- return 1;
- }
- case PIX_FMT_UYVY422:
- return 2;
- case PIX_FMT_YUV444P:
- return 3;
- case PIX_FMT_YUV444P9BE:
- case PIX_FMT_YUV444P9LE:
- case PIX_FMT_YUV444P10LE:
- case PIX_FMT_YUV444P10BE:
- return 6;
- default:
- throw PixelFormatError (N_("bytes_per_pixel()"), _pixel_format);
+ if ((d->flags & PIX_FMT_PLANAR) == 0) {
+ /* Not planar; sum them up */
+ return bpp[0] + bpp[1] + bpp[2] + bpp[3];
}
- return 0;
+ return bpp[c];
}
-
/** Construct a SimpleImage of a given size and format, allocating memory
* as required.
*
SimpleImage::SimpleImage (SimpleImage const & other)
: Image (other)
+ , _size (other._size)
+ , _aligned (other._aligned)
{
- _size = other._size;
- _aligned = other._aligned;
-
allocate ();
for (int i = 0; i < components(); ++i) {
}
}
+SimpleImage::SimpleImage (AVFrame* frame)
+ : Image (static_cast<AVPixelFormat> (frame->format))
+ , _size (frame->width, frame->height)
+ , _aligned (true)
+{
+ allocate ();
+
+ for (int i = 0; i < components(); ++i) {
+ uint8_t* p = _data[i];
+ uint8_t* q = frame->data[i];
+ for (int j = 0; j < lines(i); ++j) {
+ memcpy (p, q, _line_size[i]);
+ p += stride()[i];
+ /* AVFrame's linesize is what we call `stride' */
+ q += frame->linesize[i];
+ }
+ }
+}
+
SimpleImage::SimpleImage (shared_ptr<const Image> other)
: Image (*other.get())
{
return _aligned;
}
-FilterBufferImage::FilterBufferImage (AVPixelFormat p, AVFilterBufferRef* b)
- : Image (p)
- , _buffer (b)
-{
- _line_size = (int *) av_malloc (4 * sizeof (int));
- _line_size[0] = _line_size[1] = _line_size[2] = _line_size[3] = 0;
-
- for (int i = 0; i < components(); ++i) {
- _line_size[i] = size().width * bytes_per_pixel(i);
- }
-}
-
-FilterBufferImage::~FilterBufferImage ()
-{
- avfilter_unref_buffer (_buffer);
- av_free (_line_size);
-}
-
-uint8_t **
-FilterBufferImage::data () const
-{
- return _buffer->data;
-}
-
-int *
-FilterBufferImage::line_size () const
-{
- return _line_size;
-}
-
-int *
-FilterBufferImage::stride () const
-{
- /* I've seen images where the _buffer->linesize is larger than the width
- (by a small amount), suggesting that _buffer->linesize is what we call
- stride. But I'm not sure.
- */
- return _buffer->linesize;
-}
-
-libdcp::Size
-FilterBufferImage::size () const
-{
- return libdcp::Size (_buffer->video->w, _buffer->video->h);
-}
-
-bool
-FilterBufferImage::aligned () const
-{
- /* XXX? */
- return true;
-}
-
RGBPlusAlphaImage::RGBPlusAlphaImage (shared_ptr<const Image> im)
: SimpleImage (im->pixel_format(), im->size(), false)
{
#include <libavfilter/avfilter.h>
}
#include "util.h"
-#include "ffmpeg_compatibility.h"
class Scaler;
-class RGBFrameImage;
class SimpleImage;
/** @class Image
virtual void swap (Image &);
float bytes_per_pixel (int) const;
+ friend class pixel_formats_test;
+
private:
void yuv_16_black (uint16_t);
static uint16_t swap_16 (uint16_t);
AVPixelFormat _pixel_format; ///< FFmpeg's way of describing the pixel format of this Image
};
-/** @class FilterBufferImage
- * @brief An Image that is held in an AVFilterBufferRef.
- */
-class FilterBufferImage : public Image
-{
-public:
- FilterBufferImage (AVPixelFormat, AVFilterBufferRef *);
- ~FilterBufferImage ();
-
- uint8_t ** data () const;
- int * line_size () const;
- int * stride () const;
- libdcp::Size size () const;
- bool aligned () const;
-
-private:
- /* Not allowed */
- FilterBufferImage (FilterBufferImage const &);
- FilterBufferImage& operator= (FilterBufferImage const &);
-
- AVFilterBufferRef* _buffer;
- int* _line_size;
-};
-
/** @class SimpleImage
* @brief An Image for which memory is allocated using a `simple' av_malloc().
*/
{
public:
SimpleImage (AVPixelFormat, libdcp::Size, bool);
+ SimpleImage (AVFrame *);
SimpleImage (SimpleImage const &);
SimpleImage (boost::shared_ptr<const Image>);
SimpleImage& operator= (SimpleImage const &);
_log->log(String::compose("Matcher video @ %1 [audio=%2, video=%3, pending_audio=%4]", t, _audio_frames, _video_frames, _pending_audio.size()));
- if (!_first_input) {
+ if (!_first_input || t < _first_input.get()) {
_first_input = t;
}
bool const this_is_first_video = !_had_first_video;
_had_first_video = true;
- if (this_is_first_video && _had_first_audio) {
+ if (!_had_first_audio) {
+ /* No audio yet; we must postpone these data until we have some */
+ _pending_video.push_back (VideoRecord (image, same, sub, t));
+ } else if (this_is_first_video && _had_first_audio) {
/* First video since we got audio */
- fix_start (t);
- }
-
- /* Video before audio is fine, since we can make up an arbitrary difference
- with audio samples (contrasting with video which is quantised to frames)
- */
+ _pending_video.push_back (VideoRecord (image, same, sub, t));
+ fix_start ();
+ } else {
+ /* Normal running */
- /* Difference between where this video is and where it should be */
- double const delta = t - _first_input.get() - _video_frames / _frames_per_second;
- double const one_frame = 1 / _frames_per_second;
-
- if (delta > one_frame) {
- /* Insert frames to make up the difference */
- int const extra = rint (delta / one_frame);
- for (int i = 0; i < extra; ++i) {
- repeat_last_video ();
- _log->log (String::compose ("Extra video frame inserted at %1s", _video_frames / _frames_per_second));
+ /* Difference between where this video is and where it should be */
+ double const delta = t - _first_input.get() - _video_frames / _frames_per_second;
+ double const one_frame = 1 / _frames_per_second;
+
+ if (delta > one_frame) {
+ /* Insert frames to make up the difference */
+ int const extra = rint (delta / one_frame);
+ for (int i = 0; i < extra; ++i) {
+ repeat_last_video ();
+ _log->log (String::compose ("Extra video frame inserted at %1s", _video_frames / _frames_per_second));
+ }
}
- }
- if (delta > -one_frame) {
- Video (image, same, sub);
- ++_video_frames;
- } else {
- /* We are omitting a frame to keep things right */
- _log->log (String::compose ("Frame removed at %1s", t));
+ if (delta > -one_frame) {
+ Video (image, same, sub);
+ ++_video_frames;
+ } else {
+ /* We are omitting a frame to keep things right */
+ _log->log (String::compose ("Frame removed at %1s; delta %2; first input was at %3", t, delta, _first_input.get()));
+ }
+
+ _last_image = image;
+ _last_subtitle = sub;
}
-
- _last_image = image;
- _last_subtitle = sub;
}
void
_channels = b->channels ();
_log->log (String::compose (
- "Matcher audio (%1 frames) @ %2 [video=%3, audio=%4, pending_audio=%5]",
- b->frames(), t, _video_frames, _audio_frames, _pending_audio.size()
+ "Matcher audio (%1 frames) @ %2 [video=%3, audio=%4, pending_video=%5, pending_audio=%6]",
+ b->frames(), t, _video_frames, _audio_frames, _pending_video.size(), _pending_audio.size()
)
);
- if (!_first_input) {
+ if (!_first_input || t < _first_input.get()) {
_first_input = t;
}
- bool const this_is_first_audio = _had_first_audio;
+ bool const this_is_first_audio = !_had_first_audio;
_had_first_audio = true;
if (!_had_first_video) {
/* No video yet; we must postpone these data until we have some */
_pending_audio.push_back (AudioRecord (b, t));
- } else if (this_is_first_audio && !_had_first_video) {
+ } else if (this_is_first_audio && _had_first_video) {
/* First audio since we got video */
_pending_audio.push_back (AudioRecord (b, t));
- fix_start (_first_input.get ());
+ fix_start ();
} else {
- /* Normal running. We assume audio time stamps are consecutive */
+ /* Normal running. We assume audio time stamps are consecutive, so there's no equivalent of
+ the checking / insertion of repeat frames that there is for video.
+ */
Audio (b);
_audio_frames += b->frames ();
}
}
void
-Matcher::fix_start (double first_video)
+Matcher::fix_start ()
{
+ assert (!_pending_video.empty ());
assert (!_pending_audio.empty ());
+
+ _log->log (String::compose ("Fixing start; video at %1, audio at %2", _pending_video.front().time, _pending_audio.front().time));
+
+ match (_pending_video.front().time - _pending_audio.front().time);
- _log->log (String::compose ("Fixing start; video at %1, audio at %2", first_video, _pending_audio.front().time));
+ for (list<VideoRecord>::iterator i = _pending_video.begin(); i != _pending_video.end(); ++i) {
+ process_video (i->image, i->same, i->subtitle, i->time);
+ }
- match (first_video - _pending_audio.front().time);
+ _pending_video.clear ();
for (list<AudioRecord>::iterator i = _pending_audio.begin(); i != _pending_audio.end(); ++i) {
process_audio (i->audio, i->time);
#include <boost/optional.hpp>
#include "processor.h"
-#include "ffmpeg_compatibility.h"
class Matcher : public Processor, public TimedAudioSink, public TimedVideoSink, public AudioSource, public VideoSource
{
void process_end ();
private:
- void fix_start (double);
+ void fix_start ();
void match (double);
void repeat_last_video ();
boost::optional<libdcp::Size> _size;
boost::optional<int> _channels;
+ struct VideoRecord {
+ VideoRecord (boost::shared_ptr<const Image> i, bool s, boost::shared_ptr<Subtitle> u, double t)
+ : image (i)
+ , same (s)
+ , subtitle (u)
+ , time (t)
+ {}
+
+ boost::shared_ptr<const Image> image;
+ bool same;
+ boost::shared_ptr<Subtitle> subtitle;
+ double time;
+ };
+
struct AudioRecord {
AudioRecord (boost::shared_ptr<const AudioBuffers> a, double t)
: audio (a)
double time;
};
+ std::list<VideoRecord> _pending_video;
std::list<AudioRecord> _pending_audio;
boost::optional<double> _first_input;
assert (_encoder);
shared_ptr<AudioStream> st = f->audio_stream();
- if (st && st->sample_rate()) {
+ if (st && st->sample_rate ()) {
_matcher.reset (new Matcher (f->log(), st->sample_rate(), f->source_frame_rate()));
}
_delay_line.reset (new DelayLine (f->log(), f->audio_delay() / 1000.0f));
examine_content_job.cc
exceptions.cc
filter_graph.cc
- ffmpeg_compatibility.cc
ffmpeg_decoder.cc
film.cc
filter.cc
--- /dev/null
+/*
+ Copyright (C) 2012 Carl Hetherington <cth@carlh.net>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+void
+do_remote_encode (shared_ptr<DCPVideoFrame> frame, ServerDescription* description, shared_ptr<EncodedData> locally_encoded)
+{
+ shared_ptr<EncodedData> remotely_encoded;
+ BOOST_CHECK_NO_THROW (remotely_encoded = frame->encode_remotely (description));
+ BOOST_CHECK (remotely_encoded);
+
+ BOOST_CHECK_EQUAL (locally_encoded->size(), remotely_encoded->size());
+ BOOST_CHECK (memcmp (locally_encoded->data(), remotely_encoded->data(), locally_encoded->size()) == 0);
+}
+
+BOOST_AUTO_TEST_CASE (client_server_test)
+{
+ shared_ptr<Image> image (new SimpleImage (PIX_FMT_RGB24, libdcp::Size (1998, 1080), true));
+ uint8_t* p = image->data()[0];
+
+ for (int y = 0; y < 1080; ++y) {
+ uint8_t* q = p;
+ for (int x = 0; x < 1998; ++x) {
+ *q++ = x % 256;
+ *q++ = y % 256;
+ *q++ = (x + y) % 256;
+ }
+ p += image->stride()[0];
+ }
+
+ shared_ptr<Image> sub_image (new SimpleImage (PIX_FMT_RGBA, libdcp::Size (100, 200), true));
+ p = sub_image->data()[0];
+ for (int y = 0; y < 200; ++y) {
+ uint8_t* q = p;
+ for (int x = 0; x < 100; ++x) {
+ *q++ = y % 256;
+ *q++ = x % 256;
+ *q++ = (x + y) % 256;
+ *q++ = 1;
+ }
+ p += sub_image->stride()[0];
+ }
+
+ shared_ptr<Subtitle> subtitle (new Subtitle (Position (50, 60), sub_image));
+
+ shared_ptr<FileLog> log (new FileLog ("build/test/client_server_test.log"));
+
+ shared_ptr<DCPVideoFrame> frame (
+ new DCPVideoFrame (
+ image,
+ subtitle,
+ libdcp::Size (1998, 1080),
+ 0,
+ 0,
+ 1,
+ Scaler::from_id ("bicubic"),
+ 0,
+ 24,
+ "",
+ 0,
+ 200000000,
+ log
+ )
+ );
+
+ shared_ptr<EncodedData> locally_encoded = frame->encode_locally ();
+ BOOST_ASSERT (locally_encoded);
+
+ Server* server = new Server (log);
+
+ new thread (boost::bind (&Server::run, server, 2));
+
+ /* Let the server get itself ready */
+ dvdomatic_sleep (1);
+
+ ServerDescription description ("localhost", 2);
+
+ list<thread*> threads;
+ for (int i = 0; i < 8; ++i) {
+ threads.push_back (new thread (boost::bind (do_remote_encode, frame, &description, locally_encoded)));
+ }
+
+ for (list<thread*>::iterator i = threads.begin(); i != threads.end(); ++i) {
+ (*i)->join ();
+ }
+
+ for (list<thread*>::iterator i = threads.begin(); i != threads.end(); ++i) {
+ delete *i;
+ }
+}
+
--- /dev/null
+/*
+ Copyright (C) 2012 Carl Hetherington <cth@carlh.net>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+BOOST_AUTO_TEST_CASE (make_dcp_test)
+{
+ shared_ptr<Film> film = new_test_film ("make_dcp_test");
+ film->set_name ("test_film2");
+ film->set_content ("../../../test/test.mp4");
+ film->set_format (Format::from_nickname ("Flat"));
+ film->set_dcp_content_type (DCPContentType::from_pretty_name ("Test"));
+ film->make_dcp ();
+ film->write_metadata ();
+
+ while (JobManager::instance()->work_to_do ()) {
+ dvdomatic_sleep (1);
+ }
+
+ BOOST_CHECK_EQUAL (JobManager::instance()->errors(), false);
+}
+
+/** Test Film::have_dcp(). Requires the output from make_dcp_test above */
+BOOST_AUTO_TEST_CASE (have_dcp_test)
+{
+ boost::filesystem::path p = test_film_dir ("make_dcp_test");
+ Film f (p.string ());
+ BOOST_CHECK (f.have_dcp());
+
+ p /= f.dcp_name();
+ p /= f.dcp_video_mxf_filename();
+ boost::filesystem::remove (p);
+ BOOST_CHECK (!f.have_dcp ());
+}
+
+BOOST_AUTO_TEST_CASE (make_dcp_with_range_test)
+{
+ shared_ptr<Film> film = new_test_film ("make_dcp_with_range_test");
+ film->set_name ("test_film3");
+ film->set_content ("../../../test/test.mp4");
+ film->examine_content ();
+ film->set_format (Format::from_nickname ("Flat"));
+ film->set_dcp_content_type (DCPContentType::from_pretty_name ("Test"));
+ film->set_trim_end (42);
+ film->make_dcp ();
+
+ while (JobManager::instance()->work_to_do() && !JobManager::instance()->errors()) {
+ dvdomatic_sleep (1);
+ }
+
+ BOOST_CHECK_EQUAL (JobManager::instance()->errors(), false);
+}
+
--- /dev/null
+/*
+ Copyright (C) 2013 Carl Hetherington <cth@carlh.net>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+BOOST_AUTO_TEST_CASE (film_metadata_test)
+{
+ string const test_film = "build/test/film_metadata_test";
+
+ if (boost::filesystem::exists (test_film)) {
+ boost::filesystem::remove_all (test_film);
+ }
+
+ BOOST_CHECK_THROW (new Film (test_film, true), OpenFileError);
+
+ shared_ptr<Film> f (new Film (test_film, false));
+ f->_dci_date = boost::gregorian::from_undelimited_string ("20130211");
+ BOOST_CHECK (f->format() == 0);
+ BOOST_CHECK (f->dcp_content_type() == 0);
+ BOOST_CHECK (f->filters ().empty());
+
+ f->set_name ("fred");
+ BOOST_CHECK_THROW (f->set_content ("jim"), OpenFileError);
+ f->set_dcp_content_type (DCPContentType::from_pretty_name ("Short"));
+ f->set_format (Format::from_nickname ("Flat"));
+ f->set_left_crop (1);
+ f->set_right_crop (2);
+ f->set_top_crop (3);
+ f->set_bottom_crop (4);
+ vector<Filter const *> f_filters;
+ f_filters.push_back (Filter::from_id ("pphb"));
+ f_filters.push_back (Filter::from_id ("unsharp"));
+ f->set_filters (f_filters);
+ f->set_trim_start (42);
+ f->set_trim_end (99);
+ f->set_dcp_ab (true);
+ f->write_metadata ();
+
+ stringstream s;
+ s << "diff -u test/metadata.ref " << test_film << "/metadata";
+ BOOST_CHECK_EQUAL (::system (s.str().c_str ()), 0);
+
+ shared_ptr<Film> g (new Film (test_film, true));
+
+ BOOST_CHECK_EQUAL (g->name(), "fred");
+ BOOST_CHECK_EQUAL (g->dcp_content_type(), DCPContentType::from_pretty_name ("Short"));
+ BOOST_CHECK_EQUAL (g->format(), Format::from_nickname ("Flat"));
+ BOOST_CHECK_EQUAL (g->crop().left, 1);
+ BOOST_CHECK_EQUAL (g->crop().right, 2);
+ BOOST_CHECK_EQUAL (g->crop().top, 3);
+ BOOST_CHECK_EQUAL (g->crop().bottom, 4);
+ vector<Filter const *> g_filters = g->filters ();
+ BOOST_CHECK_EQUAL (g_filters.size(), 2);
+ BOOST_CHECK_EQUAL (g_filters.front(), Filter::from_id ("pphb"));
+ BOOST_CHECK_EQUAL (g_filters.back(), Filter::from_id ("unsharp"));
+ BOOST_CHECK_EQUAL (g->trim_start(), 42);
+ BOOST_CHECK_EQUAL (g->trim_end(), 99);
+ BOOST_CHECK_EQUAL (g->dcp_ab(), true);
+
+ g->write_metadata ();
+ BOOST_CHECK_EQUAL (::system (s.str().c_str ()), 0);
+}
--- /dev/null
+/*
+ Copyright (C) 2012 Carl Hetherington <cth@carlh.net>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+BOOST_AUTO_TEST_CASE (paths_test)
+{
+ shared_ptr<Film> f = new_test_film ("paths_test");
+ f->set_directory ("build/test/a/b/c/d/e");
+
+ f->_content = "/foo/bar/baz";
+ BOOST_CHECK_EQUAL (f->content_path(), "/foo/bar/baz");
+ f->_content = "foo/bar/baz";
+ BOOST_CHECK_EQUAL (f->content_path(), "build/test/a/b/c/d/e/foo/bar/baz");
+}
+
--- /dev/null
+/*
+ Copyright (C) 2012 Carl Hetherington <cth@carlh.net>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+BOOST_AUTO_TEST_CASE (format_test)
+{
+ Format::setup_formats ();
+
+ Format const * f = Format::from_nickname ("Flat");
+ BOOST_CHECK (f);
+ BOOST_CHECK_EQUAL (f->dcp_size().width, 1998);
+ BOOST_CHECK_EQUAL (f->dcp_size().height, 1080);
+
+ f = Format::from_nickname ("Scope");
+ BOOST_CHECK (f);
+ BOOST_CHECK_EQUAL (f->dcp_size().width, 2048);
+ BOOST_CHECK_EQUAL (f->dcp_size().height, 858);
+}
+
+
+/* Test VariableFormat-based scaling of content */
+BOOST_AUTO_TEST_CASE (scaling_test)
+{
+ shared_ptr<Film> film (new Film (test_film_dir ("scaling_test").string(), false));
+
+ /* 4:3 ratio */
+ film->set_size (libdcp::Size (320, 240));
+
+ /* This format should preserve aspect ratio of the source */
+ Format const * format = Format::from_id ("var-185");
+
+ /* We should have enough padding that the result is 4:3,
+ which would be 1440 pixels.
+ */
+ BOOST_CHECK_EQUAL (format->dcp_padding (film), (1998 - 1440) / 2);
+
+ /* This crops it to 1.291666667 */
+ film->set_left_crop (5);
+ film->set_right_crop (5);
+
+ /* We should now have enough padding that the result is 1.29166667,
+ which would be 1395 pixels.
+ */
+ BOOST_CHECK_EQUAL (format->dcp_padding (film), rint ((1998 - 1395) / 2.0));
+}
+
--- /dev/null
+/*
+ Copyright (C) 2012 Carl Hetherington <cth@carlh.net>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+/* Test best_dcp_frame_rate and FrameRateConversion */
+BOOST_AUTO_TEST_CASE (best_dcp_frame_rate_test)
+{
+ /* Run some tests with a limited range of allowed rates */
+
+ std::list<int> afr;
+ afr.push_back (24);
+ afr.push_back (25);
+ afr.push_back (30);
+ Config::instance()->set_allowed_dcp_frame_rates (afr);
+
+ int best = best_dcp_frame_rate (60);
+ FrameRateConversion frc = FrameRateConversion (60, best);
+ BOOST_CHECK_EQUAL (best, 30);
+ BOOST_CHECK_EQUAL (frc.skip, true);
+ BOOST_CHECK_EQUAL (frc.repeat, false);
+ BOOST_CHECK_EQUAL (frc.change_speed, false);
+
+ best = best_dcp_frame_rate (50);
+ frc = FrameRateConversion (50, best);
+ BOOST_CHECK_EQUAL (best, 25);
+ BOOST_CHECK_EQUAL (frc.skip, true);
+ BOOST_CHECK_EQUAL (frc.repeat, false);
+ BOOST_CHECK_EQUAL (frc.change_speed, false);
+
+ best = best_dcp_frame_rate (48);
+ frc = FrameRateConversion (48, best);
+ BOOST_CHECK_EQUAL (best, 24);
+ BOOST_CHECK_EQUAL (frc.skip, true);
+ BOOST_CHECK_EQUAL (frc.repeat, false);
+ BOOST_CHECK_EQUAL (frc.change_speed, false);
+
+ best = best_dcp_frame_rate (30);
+ frc = FrameRateConversion (30, best);
+ BOOST_CHECK_EQUAL (best, 30);
+ BOOST_CHECK_EQUAL (frc.skip, false);
+ BOOST_CHECK_EQUAL (frc.repeat, false);
+ BOOST_CHECK_EQUAL (frc.change_speed, false);
+
+ best = best_dcp_frame_rate (29.97);
+ frc = FrameRateConversion (29.97, best);
+ BOOST_CHECK_EQUAL (best, 30);
+ BOOST_CHECK_EQUAL (frc.skip, false);
+ BOOST_CHECK_EQUAL (frc.repeat, false);
+ BOOST_CHECK_EQUAL (frc.change_speed, true);
+
+ best = best_dcp_frame_rate (25);
+ frc = FrameRateConversion (25, best);
+ BOOST_CHECK_EQUAL (best, 25);
+ BOOST_CHECK_EQUAL (frc.skip, false);
+ BOOST_CHECK_EQUAL (frc.repeat, false);
+ BOOST_CHECK_EQUAL (frc.change_speed, false);
+
+ best = best_dcp_frame_rate (24);
+ frc = FrameRateConversion (24, best);
+ BOOST_CHECK_EQUAL (best, 24);
+ BOOST_CHECK_EQUAL (frc.skip, false);
+ BOOST_CHECK_EQUAL (frc.repeat, false);
+ BOOST_CHECK_EQUAL (frc.change_speed, false);
+
+ best = best_dcp_frame_rate (14.5);
+ frc = FrameRateConversion (14.5, best);
+ BOOST_CHECK_EQUAL (best, 30);
+ BOOST_CHECK_EQUAL (frc.skip, false);
+ BOOST_CHECK_EQUAL (frc.repeat, true);
+ BOOST_CHECK_EQUAL (frc.change_speed, true);
+
+ best = best_dcp_frame_rate (12.6);
+ frc = FrameRateConversion (12.6, best);
+ BOOST_CHECK_EQUAL (best, 25);
+ BOOST_CHECK_EQUAL (frc.skip, false);
+ BOOST_CHECK_EQUAL (frc.repeat, true);
+ BOOST_CHECK_EQUAL (frc.change_speed, true);
+
+ best = best_dcp_frame_rate (12.4);
+ frc = FrameRateConversion (12.4, best);
+ BOOST_CHECK_EQUAL (best, 25);
+ BOOST_CHECK_EQUAL (frc.skip, false);
+ BOOST_CHECK_EQUAL (frc.repeat, true);
+ BOOST_CHECK_EQUAL (frc.change_speed, true);
+
+ best = best_dcp_frame_rate (12);
+ frc = FrameRateConversion (12, best);
+ BOOST_CHECK_EQUAL (best, 24);
+ BOOST_CHECK_EQUAL (frc.skip, false);
+ BOOST_CHECK_EQUAL (frc.repeat, true);
+ BOOST_CHECK_EQUAL (frc.change_speed, false);
+
+ /* Now add some more rates and see if it will use them
+ in preference to skip/repeat.
+ */
+
+ afr.push_back (48);
+ afr.push_back (50);
+ afr.push_back (60);
+ Config::instance()->set_allowed_dcp_frame_rates (afr);
+
+ best = best_dcp_frame_rate (60);
+ frc = FrameRateConversion (60, best);
+ BOOST_CHECK_EQUAL (best, 60);
+ BOOST_CHECK_EQUAL (frc.skip, false);
+ BOOST_CHECK_EQUAL (frc.repeat, false);
+ BOOST_CHECK_EQUAL (frc.change_speed, false);
+
+ best = best_dcp_frame_rate (50);
+ frc = FrameRateConversion (50, best);
+ BOOST_CHECK_EQUAL (best, 50);
+ BOOST_CHECK_EQUAL (frc.skip, false);
+ BOOST_CHECK_EQUAL (frc.repeat, false);
+ BOOST_CHECK_EQUAL (frc.change_speed, false);
+
+ best = best_dcp_frame_rate (48);
+ frc = FrameRateConversion (48, best);
+ BOOST_CHECK_EQUAL (best, 48);
+ BOOST_CHECK_EQUAL (frc.skip, false);
+ BOOST_CHECK_EQUAL (frc.repeat, false);
+ BOOST_CHECK_EQUAL (frc.change_speed, false);
+
+ /* Check some out-there conversions (not the best) */
+
+ frc = FrameRateConversion (14.99, 24);
+ BOOST_CHECK_EQUAL (frc.skip, false);
+ BOOST_CHECK_EQUAL (frc.repeat, true);
+ BOOST_CHECK_EQUAL (frc.change_speed, true);
+
+ /* Check some conversions with limited DCP targets */
+
+ afr.clear ();
+ afr.push_back (24);
+ Config::instance()->set_allowed_dcp_frame_rates (afr);
+
+ best = best_dcp_frame_rate (25);
+ frc = FrameRateConversion (25, best);
+ BOOST_CHECK_EQUAL (best, 24);
+ BOOST_CHECK_EQUAL (frc.skip, false);
+ BOOST_CHECK_EQUAL (frc.repeat, false);
+ BOOST_CHECK_EQUAL (frc.change_speed, true);
+}
+
+BOOST_AUTO_TEST_CASE (audio_sampling_rate_test)
+{
+ std::list<int> afr;
+ afr.push_back (24);
+ afr.push_back (25);
+ afr.push_back (30);
+ Config::instance()->set_allowed_dcp_frame_rates (afr);
+
+ shared_ptr<Film> f = new_test_film ("audio_sampling_rate_test");
+ f->set_source_frame_rate (24);
+ f->set_dcp_frame_rate (24);
+
+ f->set_content_audio_stream (shared_ptr<AudioStream> (new FFmpegAudioStream ("a", 42, 48000, 0)));
+ BOOST_CHECK_EQUAL (f->target_audio_sample_rate(), 48000);
+
+ f->set_content_audio_stream (shared_ptr<AudioStream> (new FFmpegAudioStream ("a", 42, 44100, 0)));
+ BOOST_CHECK_EQUAL (f->target_audio_sample_rate(), 48000);
+
+ f->set_content_audio_stream (shared_ptr<AudioStream> (new FFmpegAudioStream ("a", 42, 80000, 0)));
+ BOOST_CHECK_EQUAL (f->target_audio_sample_rate(), 96000);
+
+ f->set_source_frame_rate (23.976);
+ f->set_dcp_frame_rate (best_dcp_frame_rate (23.976));
+ f->set_content_audio_stream (shared_ptr<AudioStream> (new FFmpegAudioStream ("a", 42, 48000, 0)));
+ BOOST_CHECK_EQUAL (f->target_audio_sample_rate(), 47952);
+
+ f->set_source_frame_rate (29.97);
+ f->set_dcp_frame_rate (best_dcp_frame_rate (29.97));
+ BOOST_CHECK_EQUAL (f->dcp_frame_rate (), 30);
+ f->set_content_audio_stream (shared_ptr<AudioStream> (new FFmpegAudioStream ("a", 42, 48000, 0)));
+ BOOST_CHECK_EQUAL (f->target_audio_sample_rate(), 47952);
+
+ f->set_source_frame_rate (25);
+ f->set_dcp_frame_rate (24);
+ f->set_content_audio_stream (shared_ptr<AudioStream> (new FFmpegAudioStream ("a", 42, 48000, 0)));
+ BOOST_CHECK_EQUAL (f->target_audio_sample_rate(), 50000);
+
+ f->set_source_frame_rate (25);
+ f->set_dcp_frame_rate (24);
+ f->set_content_audio_stream (shared_ptr<AudioStream> (new FFmpegAudioStream ("a", 42, 44100, 0)));
+ BOOST_CHECK_EQUAL (f->target_audio_sample_rate(), 50000);
+
+ /* Check some out-there conversions (not the best) */
+
+ f->set_source_frame_rate (14.99);
+ f->set_dcp_frame_rate (25);
+ f->set_content_audio_stream (shared_ptr<AudioStream> (new FFmpegAudioStream ("a", 42, 16000, 0)));
+ /* The FrameRateConversion within target_audio_sample_rate should choose to double-up
+ the 14.99 fps video to 30 and then run it slow at 25.
+ */
+ BOOST_CHECK_EQUAL (f->target_audio_sample_rate(), rint (48000 * 2 * 14.99 / 25));
+}
+
--- /dev/null
+
+BOOST_AUTO_TEST_CASE (aligned_image_test)
+{
+ SimpleImage* s = new SimpleImage (PIX_FMT_RGB24, libdcp::Size (50, 50), true);
+ BOOST_CHECK_EQUAL (s->components(), 1);
+ /* 160 is 150 aligned to the nearest 32 bytes */
+ BOOST_CHECK_EQUAL (s->stride()[0], 160);
+ BOOST_CHECK_EQUAL (s->line_size()[0], 150);
+ BOOST_CHECK (s->data()[0]);
+ BOOST_CHECK (!s->data()[1]);
+ BOOST_CHECK (!s->data()[2]);
+ BOOST_CHECK (!s->data()[3]);
+
+ /* copy constructor */
+ SimpleImage* t = new SimpleImage (*s);
+ BOOST_CHECK_EQUAL (t->components(), 1);
+ BOOST_CHECK_EQUAL (t->stride()[0], 160);
+ BOOST_CHECK_EQUAL (t->line_size()[0], 150);
+ BOOST_CHECK (t->data()[0]);
+ BOOST_CHECK (!t->data()[1]);
+ BOOST_CHECK (!t->data()[2]);
+ BOOST_CHECK (!t->data()[3]);
+ BOOST_CHECK (t->data() != s->data());
+ BOOST_CHECK (t->data()[0] != s->data()[0]);
+ BOOST_CHECK (t->line_size() != s->line_size());
+ BOOST_CHECK (t->line_size()[0] == s->line_size()[0]);
+ BOOST_CHECK (t->stride() != s->stride());
+ BOOST_CHECK (t->stride()[0] == s->stride()[0]);
+
+ /* assignment operator */
+ SimpleImage* u = new SimpleImage (PIX_FMT_YUV422P, libdcp::Size (150, 150), false);
+ *u = *s;
+ BOOST_CHECK_EQUAL (u->components(), 1);
+ BOOST_CHECK_EQUAL (u->stride()[0], 160);
+ BOOST_CHECK_EQUAL (u->line_size()[0], 150);
+ BOOST_CHECK (u->data()[0]);
+ BOOST_CHECK (!u->data()[1]);
+ BOOST_CHECK (!u->data()[2]);
+ BOOST_CHECK (!u->data()[3]);
+ BOOST_CHECK (u->data() != s->data());
+ BOOST_CHECK (u->data()[0] != s->data()[0]);
+ BOOST_CHECK (u->line_size() != s->line_size());
+ BOOST_CHECK (u->line_size()[0] == s->line_size()[0]);
+ BOOST_CHECK (u->stride() != s->stride());
+ BOOST_CHECK (u->stride()[0] == s->stride()[0]);
+
+ delete s;
+ delete t;
+ delete u;
+}
+
+BOOST_AUTO_TEST_CASE (compact_image_test)
+{
+ SimpleImage* s = new SimpleImage (PIX_FMT_RGB24, libdcp::Size (50, 50), false);
+ BOOST_CHECK_EQUAL (s->components(), 1);
+ BOOST_CHECK_EQUAL (s->stride()[0], 50 * 3);
+ BOOST_CHECK_EQUAL (s->line_size()[0], 50 * 3);
+ BOOST_CHECK (s->data()[0]);
+ BOOST_CHECK (!s->data()[1]);
+ BOOST_CHECK (!s->data()[2]);
+ BOOST_CHECK (!s->data()[3]);
+
+ /* copy constructor */
+ SimpleImage* t = new SimpleImage (*s);
+ BOOST_CHECK_EQUAL (t->components(), 1);
+ BOOST_CHECK_EQUAL (t->stride()[0], 50 * 3);
+ BOOST_CHECK_EQUAL (t->line_size()[0], 50 * 3);
+ BOOST_CHECK (t->data()[0]);
+ BOOST_CHECK (!t->data()[1]);
+ BOOST_CHECK (!t->data()[2]);
+ BOOST_CHECK (!t->data()[3]);
+ BOOST_CHECK (t->data() != s->data());
+ BOOST_CHECK (t->data()[0] != s->data()[0]);
+ BOOST_CHECK (t->line_size() != s->line_size());
+ BOOST_CHECK (t->line_size()[0] == s->line_size()[0]);
+ BOOST_CHECK (t->stride() != s->stride());
+ BOOST_CHECK (t->stride()[0] == s->stride()[0]);
+
+ /* assignment operator */
+ SimpleImage* u = new SimpleImage (PIX_FMT_YUV422P, libdcp::Size (150, 150), true);
+ *u = *s;
+ BOOST_CHECK_EQUAL (u->components(), 1);
+ BOOST_CHECK_EQUAL (u->stride()[0], 50 * 3);
+ BOOST_CHECK_EQUAL (u->line_size()[0], 50 * 3);
+ BOOST_CHECK (u->data()[0]);
+ BOOST_CHECK (!u->data()[1]);
+ BOOST_CHECK (!u->data()[2]);
+ BOOST_CHECK (!u->data()[3]);
+ BOOST_CHECK (u->data() != s->data());
+ BOOST_CHECK (u->data()[0] != s->data()[0]);
+ BOOST_CHECK (u->line_size() != s->line_size());
+ BOOST_CHECK (u->line_size()[0] == s->line_size()[0]);
+ BOOST_CHECK (u->stride() != s->stride());
+ BOOST_CHECK (u->stride()[0] == s->stride()[0]);
+
+ delete s;
+ delete t;
+ delete u;
+}
--- /dev/null
+/*
+ Copyright (C) 2012 Carl Hetherington <cth@carlh.net>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+class TestJob : public Job
+{
+public:
+ TestJob (shared_ptr<Film> f)
+ : Job (f)
+ {
+
+ }
+
+ void set_finished_ok () {
+ set_state (FINISHED_OK);
+ }
+
+ void set_finished_error () {
+ set_state (FINISHED_ERROR);
+ }
+
+ void run ()
+ {
+ while (1) {
+ if (finished ()) {
+ return;
+ }
+ }
+ }
+
+ string name () const {
+ return "";
+ }
+};
+
+BOOST_AUTO_TEST_CASE (job_manager_test)
+{
+ shared_ptr<Film> f;
+
+ /* Single job */
+ shared_ptr<TestJob> a (new TestJob (f));
+
+ JobManager::instance()->add (a);
+ dvdomatic_sleep (1);
+ BOOST_CHECK_EQUAL (a->running (), true);
+ a->set_finished_ok ();
+ dvdomatic_sleep (2);
+ BOOST_CHECK_EQUAL (a->finished_ok(), true);
+}
--- /dev/null
+/*
+ Copyright (C) 2012 Carl Hetherington <cth@carlh.net>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+/* Check that Image::make_black works, and doesn't use values which crash
+ sws_scale().
+*/
+BOOST_AUTO_TEST_CASE (make_black_test)
+{
+ libdcp::Size in_size (512, 512);
+ libdcp::Size out_size (1024, 1024);
+
+ list<AVPixelFormat> pix_fmts;
+ pix_fmts.push_back (AV_PIX_FMT_RGB24);
+ pix_fmts.push_back (AV_PIX_FMT_YUV420P);
+ pix_fmts.push_back (AV_PIX_FMT_YUV422P10LE);
+ pix_fmts.push_back (AV_PIX_FMT_YUV422P16LE);
+ pix_fmts.push_back (AV_PIX_FMT_YUV444P9LE);
+ pix_fmts.push_back (AV_PIX_FMT_YUV444P9BE);
+ pix_fmts.push_back (AV_PIX_FMT_YUV444P10LE);
+ pix_fmts.push_back (AV_PIX_FMT_YUV444P10BE);
+ pix_fmts.push_back (AV_PIX_FMT_UYVY422);
+
+ int N = 0;
+ for (list<AVPixelFormat>::const_iterator i = pix_fmts.begin(); i != pix_fmts.end(); ++i) {
+ boost::shared_ptr<Image> foo (new SimpleImage (*i, in_size, true));
+ foo->make_black ();
+ boost::shared_ptr<Image> bar = foo->scale_and_convert_to_rgb (out_size, 0, Scaler::from_id ("bicubic"), true);
+
+ uint8_t* p = bar->data()[0];
+ for (int y = 0; y < bar->size().height; ++y) {
+ uint8_t* q = p;
+ for (int x = 0; x < bar->line_size()[0]; ++x) {
+ BOOST_CHECK_EQUAL (*q++, 0);
+ }
+ p += bar->stride()[0];
+ }
+
+ ++N;
+ }
+}
--- /dev/null
+/*
+ Copyright (C) 2013 Carl Hetherington <cth@carlh.net>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+using std::list;
+using std::cout;
+
+struct Case
+{
+ Case (AVPixelFormat f, int c, int l0, int l1, int l2, float b0, float b1, float b2)
+ : format(f)
+ , components(c)
+ {
+ lines[0] = l0;
+ lines[1] = l1;
+ lines[2] = l2;
+ bpp[0] = b0;
+ bpp[1] = b1;
+ bpp[2] = b2;
+ }
+
+ AVPixelFormat format;
+ int components;
+ int lines[3];
+ float bpp[3];
+};
+
+
+BOOST_AUTO_TEST_CASE (pixel_formats_test)
+{
+ /* This needs to happen in the first test */
+ dvdomatic_setup ();
+
+ list<Case> cases;
+ cases.push_back(Case(AV_PIX_FMT_RGB24, 1, 480, 480, 480, 3, 0, 0 ));
+ cases.push_back(Case(AV_PIX_FMT_RGBA, 1, 480, 480, 480, 4, 0, 0 ));
+ cases.push_back(Case(AV_PIX_FMT_YUV420P, 3, 480, 240, 240, 1, 0.5, 0.5));
+ cases.push_back(Case(AV_PIX_FMT_YUV422P, 3, 480, 480, 480, 1, 0.5, 0.5));
+ cases.push_back(Case(AV_PIX_FMT_YUV422P10LE, 3, 480, 480, 480, 2, 1, 1 ));
+ cases.push_back(Case(AV_PIX_FMT_YUV422P16LE, 3, 480, 480, 480, 2, 1, 1 ));
+ cases.push_back(Case(AV_PIX_FMT_UYVY422, 1, 480, 480, 480, 2, 0, 0 ));
+ cases.push_back(Case(AV_PIX_FMT_YUV444P, 3, 480, 480, 480, 1, 1, 1 ));
+ cases.push_back(Case(AV_PIX_FMT_YUV444P9BE, 3, 480, 480, 480, 2, 2, 2 ));
+ cases.push_back(Case(AV_PIX_FMT_YUV444P9LE, 3, 480, 480, 480, 2, 2, 2 ));
+ cases.push_back(Case(AV_PIX_FMT_YUV444P10BE, 3, 480, 480, 480, 2, 2, 2 ));
+ cases.push_back(Case(AV_PIX_FMT_YUV444P10LE, 3, 480, 480, 480, 2, 2, 2 ));
+
+ for (list<Case>::iterator i = cases.begin(); i != cases.end(); ++i) {
+ AVFrame* f = av_frame_alloc ();
+ f->width = 640;
+ f->height = 480;
+ f->format = static_cast<int> (i->format);
+ SimpleImage t (f);
+ BOOST_CHECK_EQUAL(t.components(), i->components);
+ BOOST_CHECK_EQUAL(t.lines(0), i->lines[0]);
+ BOOST_CHECK_EQUAL(t.lines(1), i->lines[1]);
+ BOOST_CHECK_EQUAL(t.lines(2), i->lines[2]);
+ BOOST_CHECK_EQUAL(t.bytes_per_pixel(0), i->bpp[0]);
+ BOOST_CHECK_EQUAL(t.bytes_per_pixel(1), i->bpp[1]);
+ BOOST_CHECK_EQUAL(t.bytes_per_pixel(2), i->bpp[2]);
+ }
+}
--- /dev/null
+/*
+ Copyright (C) 2013 Carl Hetherington <cth@carlh.net>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+BOOST_AUTO_TEST_CASE (stream_test)
+{
+ FFmpegAudioStream a ("ffmpeg 4 44100 1 hello there world", boost::optional<int> (1));
+ BOOST_CHECK_EQUAL (a.id(), 4);
+ BOOST_CHECK_EQUAL (a.sample_rate(), 44100);
+ BOOST_CHECK_EQUAL (a.channel_layout(), 1);
+ BOOST_CHECK_EQUAL (a.name(), "hello there world");
+ BOOST_CHECK_EQUAL (a.to_string(), "ffmpeg 4 44100 1 hello there world");
+
+ SndfileStream e ("external 44100 1", boost::optional<int> (1));
+ BOOST_CHECK_EQUAL (e.sample_rate(), 44100);
+ BOOST_CHECK_EQUAL (e.channel_layout(), 1);
+ BOOST_CHECK_EQUAL (e.to_string(), "external 44100 1");
+
+ SubtitleStream s ("5 a b c", boost::optional<int> (1));
+ BOOST_CHECK_EQUAL (s.id(), 5);
+ BOOST_CHECK_EQUAL (s.name(), "a b c");
+
+ shared_ptr<AudioStream> ff = audio_stream_factory ("ffmpeg 4 44100 1 hello there world", boost::optional<int> (1));
+ shared_ptr<FFmpegAudioStream> cff = dynamic_pointer_cast<FFmpegAudioStream> (ff);
+ BOOST_CHECK (cff);
+ BOOST_CHECK_EQUAL (cff->id(), 4);
+ BOOST_CHECK_EQUAL (cff->sample_rate(), 44100);
+ BOOST_CHECK_EQUAL (cff->channel_layout(), 1);
+ BOOST_CHECK_EQUAL (cff->name(), "hello there world");
+ BOOST_CHECK_EQUAL (cff->to_string(), "ffmpeg 4 44100 1 hello there world");
+
+ shared_ptr<AudioStream> fe = audio_stream_factory ("external 44100 1", boost::optional<int> (1));
+ BOOST_CHECK_EQUAL (fe->sample_rate(), 44100);
+ BOOST_CHECK_EQUAL (fe->channel_layout(), 1);
+ BOOST_CHECK_EQUAL (fe->to_string(), "external 44100 1");
+}
+
using boost::thread;
using boost::dynamic_pointer_cast;
-void
-setup_test_config ()
+struct TestConfig
{
- Config::instance()->set_num_local_encoding_threads (1);
- Config::instance()->set_servers (vector<ServerDescription*> ());
- Config::instance()->set_server_port (61920);
- Config::instance()->set_default_dci_metadata (DCIMetadata ());
-}
+ TestConfig()
+ {
+ dvdomatic_setup();
+
+ Config::instance()->set_num_local_encoding_threads (1);
+ Config::instance()->set_servers (vector<ServerDescription*> ());
+ Config::instance()->set_server_port (61920);
+ Config::instance()->set_default_dci_metadata (DCIMetadata ());
+ }
+};
+
+BOOST_GLOBAL_FIXTURE (TestConfig);
boost::filesystem::path
test_film_dir (string name)
return shared_ptr<Film> (new Film (p.string(), false));
}
-
-/* Check that Image::make_black works, and doesn't use values which crash
- sws_scale().
-*/
-BOOST_AUTO_TEST_CASE (make_black_test)
-{
- /* This needs to happen in the first test */
- dvdomatic_setup ();
-
- libdcp::Size in_size (512, 512);
- libdcp::Size out_size (1024, 1024);
-
- list<AVPixelFormat> pix_fmts;
- pix_fmts.push_back (AV_PIX_FMT_RGB24);
- pix_fmts.push_back (AV_PIX_FMT_YUV420P);
- pix_fmts.push_back (AV_PIX_FMT_YUV422P10LE);
- pix_fmts.push_back (AV_PIX_FMT_YUV444P9LE);
- pix_fmts.push_back (AV_PIX_FMT_YUV444P9BE);
- pix_fmts.push_back (AV_PIX_FMT_YUV444P10LE);
- pix_fmts.push_back (AV_PIX_FMT_YUV444P10BE);
- pix_fmts.push_back (AV_PIX_FMT_UYVY422);
-
- int N = 0;
- for (list<AVPixelFormat>::const_iterator i = pix_fmts.begin(); i != pix_fmts.end(); ++i) {
- boost::shared_ptr<Image> foo (new SimpleImage (*i, in_size, true));
- foo->make_black ();
- boost::shared_ptr<Image> bar = foo->scale_and_convert_to_rgb (out_size, 0, Scaler::from_id ("bicubic"), true);
-
- uint8_t* p = bar->data()[0];
- for (int y = 0; y < bar->size().height; ++y) {
- uint8_t* q = p;
- for (int x = 0; x < bar->line_size()[0]; ++x) {
- BOOST_CHECK_EQUAL (*q++, 0);
- }
- p += bar->stride()[0];
- }
-
- ++N;
- }
-}
-
-shared_ptr<const Image> trimmer_test_last_video;
-shared_ptr<const AudioBuffers> trimmer_test_last_audio;
-
-void
-trimmer_test_video_helper (shared_ptr<const Image> image, bool, shared_ptr<Subtitle>)
-{
- trimmer_test_last_video = image;
-}
-
-void
-trimmer_test_audio_helper (shared_ptr<const AudioBuffers> audio)
-{
- trimmer_test_last_audio = audio;
-}
-
-BOOST_AUTO_TEST_CASE (trimmer_passthrough_test)
-{
- Trimmer trimmer (shared_ptr<Log> (), 0, 0, 200, 48000, 25, 25);
- trimmer.Video.connect (bind (&trimmer_test_video_helper, _1, _2, _3));
- trimmer.Audio.connect (bind (&trimmer_test_audio_helper, _1));
-
- shared_ptr<SimpleImage> video (new SimpleImage (PIX_FMT_RGB24, libdcp::Size (1998, 1080), true));
- shared_ptr<AudioBuffers> audio (new AudioBuffers (6, 42 * 1920));
-
- trimmer.process_video (video, false, shared_ptr<Subtitle> ());
- trimmer.process_audio (audio);
-
- BOOST_CHECK_EQUAL (video.get(), trimmer_test_last_video.get());
- BOOST_CHECK_EQUAL (audio.get(), trimmer_test_last_audio.get());
- BOOST_CHECK_EQUAL (audio->frames(), trimmer_test_last_audio->frames());
-}
-
-
-/** Test the audio handling of the Trimmer */
-BOOST_AUTO_TEST_CASE (trimmer_audio_test)
-{
- Trimmer trimmer (shared_ptr<Log> (), 25, 75, 200, 48000, 25, 25);
-
- trimmer.Audio.connect (bind (&trimmer_test_audio_helper, _1));
-
- /* 21 video frames-worth of audio frames; should be completely stripped */
- trimmer_test_last_audio.reset ();
- shared_ptr<AudioBuffers> audio (new AudioBuffers (6, 21 * 1920));
- trimmer.process_audio (audio);
- BOOST_CHECK (trimmer_test_last_audio == 0);
-
- /* 42 more video frames-worth, 4 should be stripped from the start */
- audio.reset (new AudioBuffers (6, 42 * 1920));
- trimmer.process_audio (audio);
- BOOST_CHECK (trimmer_test_last_audio);
- BOOST_CHECK_EQUAL (trimmer_test_last_audio->frames(), 38 * 1920);
-
- /* 42 more video frames-worth, should be kept as-is */
- trimmer_test_last_audio.reset ();
- audio.reset (new AudioBuffers (6, 42 * 1920));
- trimmer.process_audio (audio);
- BOOST_CHECK (trimmer_test_last_audio);
- BOOST_CHECK_EQUAL (trimmer_test_last_audio->frames(), 42 * 1920);
-
- /* 25 more video frames-worth, 5 should be trimmed from the end */
- trimmer_test_last_audio.reset ();
- audio.reset (new AudioBuffers (6, 25 * 1920));
- trimmer.process_audio (audio);
- BOOST_CHECK (trimmer_test_last_audio);
- BOOST_CHECK_EQUAL (trimmer_test_last_audio->frames(), 20 * 1920);
-
- /* Now some more; all should be trimmed */
- trimmer_test_last_audio.reset ();
- audio.reset (new AudioBuffers (6, 100 * 1920));
- trimmer.process_audio (audio);
- BOOST_CHECK (trimmer_test_last_audio == 0);
-}
-
-
-BOOST_AUTO_TEST_CASE (film_metadata_test)
-{
- setup_test_config ();
-
- string const test_film = "build/test/film_metadata_test";
-
- if (boost::filesystem::exists (test_film)) {
- boost::filesystem::remove_all (test_film);
- }
-
- BOOST_CHECK_THROW (new Film (test_film, true), OpenFileError);
-
- shared_ptr<Film> f (new Film (test_film, false));
- f->_dci_date = boost::gregorian::from_undelimited_string ("20130211");
- BOOST_CHECK (f->format() == 0);
- BOOST_CHECK (f->dcp_content_type() == 0);
- BOOST_CHECK (f->filters ().empty());
-
- f->set_name ("fred");
- BOOST_CHECK_THROW (f->set_content ("jim"), OpenFileError);
- f->set_dcp_content_type (DCPContentType::from_pretty_name ("Short"));
- f->set_format (Format::from_nickname ("Flat"));
- f->set_left_crop (1);
- f->set_right_crop (2);
- f->set_top_crop (3);
- f->set_bottom_crop (4);
- vector<Filter const *> f_filters;
- f_filters.push_back (Filter::from_id ("pphb"));
- f_filters.push_back (Filter::from_id ("unsharp"));
- f->set_filters (f_filters);
- f->set_trim_start (42);
- f->set_trim_end (99);
- f->set_dcp_ab (true);
- f->write_metadata ();
-
- stringstream s;
- s << "diff -u test/metadata.ref " << test_film << "/metadata";
- BOOST_CHECK_EQUAL (::system (s.str().c_str ()), 0);
-
- shared_ptr<Film> g (new Film (test_film, true));
-
- BOOST_CHECK_EQUAL (g->name(), "fred");
- BOOST_CHECK_EQUAL (g->dcp_content_type(), DCPContentType::from_pretty_name ("Short"));
- BOOST_CHECK_EQUAL (g->format(), Format::from_nickname ("Flat"));
- BOOST_CHECK_EQUAL (g->crop().left, 1);
- BOOST_CHECK_EQUAL (g->crop().right, 2);
- BOOST_CHECK_EQUAL (g->crop().top, 3);
- BOOST_CHECK_EQUAL (g->crop().bottom, 4);
- vector<Filter const *> g_filters = g->filters ();
- BOOST_CHECK_EQUAL (g_filters.size(), 2);
- BOOST_CHECK_EQUAL (g_filters.front(), Filter::from_id ("pphb"));
- BOOST_CHECK_EQUAL (g_filters.back(), Filter::from_id ("unsharp"));
- BOOST_CHECK_EQUAL (g->trim_start(), 42);
- BOOST_CHECK_EQUAL (g->trim_end(), 99);
- BOOST_CHECK_EQUAL (g->dcp_ab(), true);
-
- g->write_metadata ();
- BOOST_CHECK_EQUAL (::system (s.str().c_str ()), 0);
-}
-
-BOOST_AUTO_TEST_CASE (stream_test)
-{
- FFmpegAudioStream a ("ffmpeg 4 44100 1 hello there world", boost::optional<int> (1));
- BOOST_CHECK_EQUAL (a.id(), 4);
- BOOST_CHECK_EQUAL (a.sample_rate(), 44100);
- BOOST_CHECK_EQUAL (a.channel_layout(), 1);
- BOOST_CHECK_EQUAL (a.name(), "hello there world");
- BOOST_CHECK_EQUAL (a.to_string(), "ffmpeg 4 44100 1 hello there world");
-
- SndfileStream e ("external 44100 1", boost::optional<int> (1));
- BOOST_CHECK_EQUAL (e.sample_rate(), 44100);
- BOOST_CHECK_EQUAL (e.channel_layout(), 1);
- BOOST_CHECK_EQUAL (e.to_string(), "external 44100 1");
-
- SubtitleStream s ("5 a b c", boost::optional<int> (1));
- BOOST_CHECK_EQUAL (s.id(), 5);
- BOOST_CHECK_EQUAL (s.name(), "a b c");
-
- shared_ptr<AudioStream> ff = audio_stream_factory ("ffmpeg 4 44100 1 hello there world", boost::optional<int> (1));
- shared_ptr<FFmpegAudioStream> cff = dynamic_pointer_cast<FFmpegAudioStream> (ff);
- BOOST_CHECK (cff);
- BOOST_CHECK_EQUAL (cff->id(), 4);
- BOOST_CHECK_EQUAL (cff->sample_rate(), 44100);
- BOOST_CHECK_EQUAL (cff->channel_layout(), 1);
- BOOST_CHECK_EQUAL (cff->name(), "hello there world");
- BOOST_CHECK_EQUAL (cff->to_string(), "ffmpeg 4 44100 1 hello there world");
-
- shared_ptr<AudioStream> fe = audio_stream_factory ("external 44100 1", boost::optional<int> (1));
- BOOST_CHECK_EQUAL (fe->sample_rate(), 44100);
- BOOST_CHECK_EQUAL (fe->channel_layout(), 1);
- BOOST_CHECK_EQUAL (fe->to_string(), "external 44100 1");
-}
-
-BOOST_AUTO_TEST_CASE (format_test)
-{
- Format::setup_formats ();
-
- Format const * f = Format::from_nickname ("Flat");
- BOOST_CHECK (f);
- BOOST_CHECK_EQUAL (f->dcp_size().width, 1998);
- BOOST_CHECK_EQUAL (f->dcp_size().height, 1080);
-
- f = Format::from_nickname ("Scope");
- BOOST_CHECK (f);
- BOOST_CHECK_EQUAL (f->dcp_size().width, 2048);
- BOOST_CHECK_EQUAL (f->dcp_size().height, 858);
-}
-
-/* Test VariableFormat-based scaling of content */
-BOOST_AUTO_TEST_CASE (scaling_test)
-{
- shared_ptr<Film> film (new Film (test_film_dir ("scaling_test").string(), false));
-
- /* 4:3 ratio */
- film->set_size (libdcp::Size (320, 240));
-
- /* This format should preserve aspect ratio of the source */
- Format const * format = Format::from_id ("var-185");
-
- /* We should have enough padding that the result is 4:3,
- which would be 1440 pixels.
- */
- BOOST_CHECK_EQUAL (format->dcp_padding (film), (1998 - 1440) / 2);
-
- /* This crops it to 1.291666667 */
- film->set_left_crop (5);
- film->set_right_crop (5);
-
- /* We should now have enough padding that the result is 1.29166667,
- which would be 1395 pixels.
- */
- BOOST_CHECK_EQUAL (format->dcp_padding (film), rint ((1998 - 1395) / 2.0));
-}
-
-BOOST_AUTO_TEST_CASE (util_test)
-{
- string t = "Hello this is a string \"with quotes\" and indeed without them";
- vector<string> b = split_at_spaces_considering_quotes (t);
- vector<string>::iterator i = b.begin ();
- BOOST_CHECK_EQUAL (*i++, "Hello");
- BOOST_CHECK_EQUAL (*i++, "this");
- BOOST_CHECK_EQUAL (*i++, "is");
- BOOST_CHECK_EQUAL (*i++, "a");
- BOOST_CHECK_EQUAL (*i++, "string");
- BOOST_CHECK_EQUAL (*i++, "with quotes");
- BOOST_CHECK_EQUAL (*i++, "and");
- BOOST_CHECK_EQUAL (*i++, "indeed");
- BOOST_CHECK_EQUAL (*i++, "without");
- BOOST_CHECK_EQUAL (*i++, "them");
-}
-
-class NullLog : public Log
-{
-public:
- void do_log (string) {}
-};
-
-BOOST_AUTO_TEST_CASE (md5_digest_test)
-{
- string const t = md5_digest ("test/md5.test");
- BOOST_CHECK_EQUAL (t, "15058685ba99decdc4398c7634796eb0");
-
- BOOST_CHECK_THROW (md5_digest ("foobar"), OpenFileError);
-}
-
-BOOST_AUTO_TEST_CASE (paths_test)
-{
- shared_ptr<Film> f = new_test_film ("paths_test");
- f->set_directory ("build/test/a/b/c/d/e");
-
- f->_content = "/foo/bar/baz";
- BOOST_CHECK_EQUAL (f->content_path(), "/foo/bar/baz");
- f->_content = "foo/bar/baz";
- BOOST_CHECK_EQUAL (f->content_path(), "build/test/a/b/c/d/e/foo/bar/baz");
-}
-
-void
-do_remote_encode (shared_ptr<DCPVideoFrame> frame, ServerDescription* description, shared_ptr<EncodedData> locally_encoded)
-{
- shared_ptr<EncodedData> remotely_encoded;
- BOOST_CHECK_NO_THROW (remotely_encoded = frame->encode_remotely (description));
- BOOST_CHECK (remotely_encoded);
-
- BOOST_CHECK_EQUAL (locally_encoded->size(), remotely_encoded->size());
- BOOST_CHECK (memcmp (locally_encoded->data(), remotely_encoded->data(), locally_encoded->size()) == 0);
-}
-
-BOOST_AUTO_TEST_CASE (client_server_test)
-{
- shared_ptr<Image> image (new SimpleImage (PIX_FMT_RGB24, libdcp::Size (1998, 1080), true));
- uint8_t* p = image->data()[0];
-
- for (int y = 0; y < 1080; ++y) {
- uint8_t* q = p;
- for (int x = 0; x < 1998; ++x) {
- *q++ = x % 256;
- *q++ = y % 256;
- *q++ = (x + y) % 256;
- }
- p += image->stride()[0];
- }
-
- shared_ptr<Image> sub_image (new SimpleImage (PIX_FMT_RGBA, libdcp::Size (100, 200), true));
- p = sub_image->data()[0];
- for (int y = 0; y < 200; ++y) {
- uint8_t* q = p;
- for (int x = 0; x < 100; ++x) {
- *q++ = y % 256;
- *q++ = x % 256;
- *q++ = (x + y) % 256;
- *q++ = 1;
- }
- p += sub_image->stride()[0];
- }
-
- shared_ptr<Subtitle> subtitle (new Subtitle (Position (50, 60), sub_image));
-
- shared_ptr<FileLog> log (new FileLog ("build/test/client_server_test.log"));
-
- shared_ptr<DCPVideoFrame> frame (
- new DCPVideoFrame (
- image,
- subtitle,
- libdcp::Size (1998, 1080),
- 0,
- 0,
- 1,
- Scaler::from_id ("bicubic"),
- 0,
- 24,
- "",
- 0,
- 200000000,
- log
- )
- );
-
- shared_ptr<EncodedData> locally_encoded = frame->encode_locally ();
- BOOST_ASSERT (locally_encoded);
-
- Server* server = new Server (log);
-
- new thread (boost::bind (&Server::run, server, 2));
-
- /* Let the server get itself ready */
- dvdomatic_sleep (1);
-
- ServerDescription description ("localhost", 2);
-
- list<thread*> threads;
- for (int i = 0; i < 8; ++i) {
- threads.push_back (new thread (boost::bind (do_remote_encode, frame, &description, locally_encoded)));
- }
-
- for (list<thread*>::iterator i = threads.begin(); i != threads.end(); ++i) {
- (*i)->join ();
- }
-
- for (list<thread*>::iterator i = threads.begin(); i != threads.end(); ++i) {
- delete *i;
- }
-}
-
-BOOST_AUTO_TEST_CASE (make_dcp_test)
-{
- shared_ptr<Film> film = new_test_film ("make_dcp_test");
- film->set_name ("test_film2");
- film->set_content ("../../../test/test.mp4");
- film->set_format (Format::from_nickname ("Flat"));
- film->set_dcp_content_type (DCPContentType::from_pretty_name ("Test"));
- film->make_dcp ();
- film->write_metadata ();
-
- while (JobManager::instance()->work_to_do ()) {
- dvdomatic_sleep (1);
- }
-
- BOOST_CHECK_EQUAL (JobManager::instance()->errors(), false);
-}
-
-/** Test Film::have_dcp(). Requires the output from make_dcp_test above */
-BOOST_AUTO_TEST_CASE (have_dcp_test)
-{
- boost::filesystem::path p = test_film_dir ("make_dcp_test");
- Film f (p.string ());
- BOOST_CHECK (f.have_dcp());
-
- p /= f.dcp_name();
- p /= f.dcp_video_mxf_filename();
- boost::filesystem::remove (p);
- BOOST_CHECK (!f.have_dcp ());
-}
-
-BOOST_AUTO_TEST_CASE (make_dcp_with_range_test)
-{
- shared_ptr<Film> film = new_test_film ("make_dcp_with_range_test");
- film->set_name ("test_film3");
- film->set_content ("../../../test/test.mp4");
- film->examine_content ();
- film->set_format (Format::from_nickname ("Flat"));
- film->set_dcp_content_type (DCPContentType::from_pretty_name ("Test"));
- film->set_trim_end (42);
- film->make_dcp ();
-
- while (JobManager::instance()->work_to_do() && !JobManager::instance()->errors()) {
- dvdomatic_sleep (1);
- }
-
- BOOST_CHECK_EQUAL (JobManager::instance()->errors(), false);
-}
-
-/* Test best_dcp_frame_rate and FrameRateConversion */
-BOOST_AUTO_TEST_CASE (best_dcp_frame_rate_test)
-{
- /* Run some tests with a limited range of allowed rates */
-
- std::list<int> afr;
- afr.push_back (24);
- afr.push_back (25);
- afr.push_back (30);
- Config::instance()->set_allowed_dcp_frame_rates (afr);
-
- int best = best_dcp_frame_rate (60);
- FrameRateConversion frc = FrameRateConversion (60, best);
- BOOST_CHECK_EQUAL (best, 30);
- BOOST_CHECK_EQUAL (frc.skip, true);
- BOOST_CHECK_EQUAL (frc.repeat, false);
- BOOST_CHECK_EQUAL (frc.change_speed, false);
-
- best = best_dcp_frame_rate (50);
- frc = FrameRateConversion (50, best);
- BOOST_CHECK_EQUAL (best, 25);
- BOOST_CHECK_EQUAL (frc.skip, true);
- BOOST_CHECK_EQUAL (frc.repeat, false);
- BOOST_CHECK_EQUAL (frc.change_speed, false);
-
- best = best_dcp_frame_rate (48);
- frc = FrameRateConversion (48, best);
- BOOST_CHECK_EQUAL (best, 24);
- BOOST_CHECK_EQUAL (frc.skip, true);
- BOOST_CHECK_EQUAL (frc.repeat, false);
- BOOST_CHECK_EQUAL (frc.change_speed, false);
-
- best = best_dcp_frame_rate (30);
- frc = FrameRateConversion (30, best);
- BOOST_CHECK_EQUAL (best, 30);
- BOOST_CHECK_EQUAL (frc.skip, false);
- BOOST_CHECK_EQUAL (frc.repeat, false);
- BOOST_CHECK_EQUAL (frc.change_speed, false);
-
- best = best_dcp_frame_rate (29.97);
- frc = FrameRateConversion (29.97, best);
- BOOST_CHECK_EQUAL (best, 30);
- BOOST_CHECK_EQUAL (frc.skip, false);
- BOOST_CHECK_EQUAL (frc.repeat, false);
- BOOST_CHECK_EQUAL (frc.change_speed, true);
-
- best = best_dcp_frame_rate (25);
- frc = FrameRateConversion (25, best);
- BOOST_CHECK_EQUAL (best, 25);
- BOOST_CHECK_EQUAL (frc.skip, false);
- BOOST_CHECK_EQUAL (frc.repeat, false);
- BOOST_CHECK_EQUAL (frc.change_speed, false);
-
- best = best_dcp_frame_rate (24);
- frc = FrameRateConversion (24, best);
- BOOST_CHECK_EQUAL (best, 24);
- BOOST_CHECK_EQUAL (frc.skip, false);
- BOOST_CHECK_EQUAL (frc.repeat, false);
- BOOST_CHECK_EQUAL (frc.change_speed, false);
-
- best = best_dcp_frame_rate (14.5);
- frc = FrameRateConversion (14.5, best);
- BOOST_CHECK_EQUAL (best, 30);
- BOOST_CHECK_EQUAL (frc.skip, false);
- BOOST_CHECK_EQUAL (frc.repeat, true);
- BOOST_CHECK_EQUAL (frc.change_speed, true);
-
- best = best_dcp_frame_rate (12.6);
- frc = FrameRateConversion (12.6, best);
- BOOST_CHECK_EQUAL (best, 25);
- BOOST_CHECK_EQUAL (frc.skip, false);
- BOOST_CHECK_EQUAL (frc.repeat, true);
- BOOST_CHECK_EQUAL (frc.change_speed, true);
-
- best = best_dcp_frame_rate (12.4);
- frc = FrameRateConversion (12.4, best);
- BOOST_CHECK_EQUAL (best, 25);
- BOOST_CHECK_EQUAL (frc.skip, false);
- BOOST_CHECK_EQUAL (frc.repeat, true);
- BOOST_CHECK_EQUAL (frc.change_speed, true);
-
- best = best_dcp_frame_rate (12);
- frc = FrameRateConversion (12, best);
- BOOST_CHECK_EQUAL (best, 24);
- BOOST_CHECK_EQUAL (frc.skip, false);
- BOOST_CHECK_EQUAL (frc.repeat, true);
- BOOST_CHECK_EQUAL (frc.change_speed, false);
-
- /* Now add some more rates and see if it will use them
- in preference to skip/repeat.
- */
-
- afr.push_back (48);
- afr.push_back (50);
- afr.push_back (60);
- Config::instance()->set_allowed_dcp_frame_rates (afr);
-
- best = best_dcp_frame_rate (60);
- frc = FrameRateConversion (60, best);
- BOOST_CHECK_EQUAL (best, 60);
- BOOST_CHECK_EQUAL (frc.skip, false);
- BOOST_CHECK_EQUAL (frc.repeat, false);
- BOOST_CHECK_EQUAL (frc.change_speed, false);
-
- best = best_dcp_frame_rate (50);
- frc = FrameRateConversion (50, best);
- BOOST_CHECK_EQUAL (best, 50);
- BOOST_CHECK_EQUAL (frc.skip, false);
- BOOST_CHECK_EQUAL (frc.repeat, false);
- BOOST_CHECK_EQUAL (frc.change_speed, false);
-
- best = best_dcp_frame_rate (48);
- frc = FrameRateConversion (48, best);
- BOOST_CHECK_EQUAL (best, 48);
- BOOST_CHECK_EQUAL (frc.skip, false);
- BOOST_CHECK_EQUAL (frc.repeat, false);
- BOOST_CHECK_EQUAL (frc.change_speed, false);
-
- /* Check some out-there conversions (not the best) */
-
- frc = FrameRateConversion (14.99, 24);
- BOOST_CHECK_EQUAL (frc.skip, false);
- BOOST_CHECK_EQUAL (frc.repeat, true);
- BOOST_CHECK_EQUAL (frc.change_speed, true);
-
- /* Check some conversions with limited DCP targets */
-
- afr.clear ();
- afr.push_back (24);
- Config::instance()->set_allowed_dcp_frame_rates (afr);
-
- best = best_dcp_frame_rate (25);
- frc = FrameRateConversion (25, best);
- BOOST_CHECK_EQUAL (best, 24);
- BOOST_CHECK_EQUAL (frc.skip, false);
- BOOST_CHECK_EQUAL (frc.repeat, false);
- BOOST_CHECK_EQUAL (frc.change_speed, true);
-}
-
-BOOST_AUTO_TEST_CASE (audio_sampling_rate_test)
-{
- std::list<int> afr;
- afr.push_back (24);
- afr.push_back (25);
- afr.push_back (30);
- Config::instance()->set_allowed_dcp_frame_rates (afr);
-
- shared_ptr<Film> f = new_test_film ("audio_sampling_rate_test");
- f->set_source_frame_rate (24);
- f->set_dcp_frame_rate (24);
-
- f->set_content_audio_stream (shared_ptr<AudioStream> (new FFmpegAudioStream ("a", 42, 48000, 0)));
- BOOST_CHECK_EQUAL (f->target_audio_sample_rate(), 48000);
-
- f->set_content_audio_stream (shared_ptr<AudioStream> (new FFmpegAudioStream ("a", 42, 44100, 0)));
- BOOST_CHECK_EQUAL (f->target_audio_sample_rate(), 48000);
-
- f->set_content_audio_stream (shared_ptr<AudioStream> (new FFmpegAudioStream ("a", 42, 80000, 0)));
- BOOST_CHECK_EQUAL (f->target_audio_sample_rate(), 96000);
-
- f->set_source_frame_rate (23.976);
- f->set_dcp_frame_rate (best_dcp_frame_rate (23.976));
- f->set_content_audio_stream (shared_ptr<AudioStream> (new FFmpegAudioStream ("a", 42, 48000, 0)));
- BOOST_CHECK_EQUAL (f->target_audio_sample_rate(), 47952);
-
- f->set_source_frame_rate (29.97);
- f->set_dcp_frame_rate (best_dcp_frame_rate (29.97));
- BOOST_CHECK_EQUAL (f->dcp_frame_rate (), 30);
- f->set_content_audio_stream (shared_ptr<AudioStream> (new FFmpegAudioStream ("a", 42, 48000, 0)));
- BOOST_CHECK_EQUAL (f->target_audio_sample_rate(), 47952);
-
- f->set_source_frame_rate (25);
- f->set_dcp_frame_rate (24);
- f->set_content_audio_stream (shared_ptr<AudioStream> (new FFmpegAudioStream ("a", 42, 48000, 0)));
- BOOST_CHECK_EQUAL (f->target_audio_sample_rate(), 50000);
-
- f->set_source_frame_rate (25);
- f->set_dcp_frame_rate (24);
- f->set_content_audio_stream (shared_ptr<AudioStream> (new FFmpegAudioStream ("a", 42, 44100, 0)));
- BOOST_CHECK_EQUAL (f->target_audio_sample_rate(), 50000);
-
- /* Check some out-there conversions (not the best) */
-
- f->set_source_frame_rate (14.99);
- f->set_dcp_frame_rate (25);
- f->set_content_audio_stream (shared_ptr<AudioStream> (new FFmpegAudioStream ("a", 42, 16000, 0)));
- /* The FrameRateConversion within target_audio_sample_rate should choose to double-up
- the 14.99 fps video to 30 and then run it slow at 25.
- */
- BOOST_CHECK_EQUAL (f->target_audio_sample_rate(), rint (48000 * 2 * 14.99 / 25));
-}
-
-class TestJob : public Job
-{
-public:
- TestJob (shared_ptr<Film> f)
- : Job (f)
- {
-
- }
-
- void set_finished_ok () {
- set_state (FINISHED_OK);
- }
-
- void set_finished_error () {
- set_state (FINISHED_ERROR);
- }
-
- void run ()
- {
- while (1) {
- if (finished ()) {
- return;
- }
- }
- }
-
- string name () const {
- return "";
- }
-};
-
-BOOST_AUTO_TEST_CASE (job_manager_test)
-{
- shared_ptr<Film> f;
-
- /* Single job */
- shared_ptr<TestJob> a (new TestJob (f));
-
- JobManager::instance()->add (a);
- dvdomatic_sleep (1);
- BOOST_CHECK_EQUAL (a->running (), true);
- a->set_finished_ok ();
- dvdomatic_sleep (2);
- BOOST_CHECK_EQUAL (a->finished_ok(), true);
-}
-
-BOOST_AUTO_TEST_CASE (compact_image_test)
-{
- SimpleImage* s = new SimpleImage (PIX_FMT_RGB24, libdcp::Size (50, 50), false);
- BOOST_CHECK_EQUAL (s->components(), 1);
- BOOST_CHECK_EQUAL (s->stride()[0], 50 * 3);
- BOOST_CHECK_EQUAL (s->line_size()[0], 50 * 3);
- BOOST_CHECK (s->data()[0]);
- BOOST_CHECK (!s->data()[1]);
- BOOST_CHECK (!s->data()[2]);
- BOOST_CHECK (!s->data()[3]);
-
- /* copy constructor */
- SimpleImage* t = new SimpleImage (*s);
- BOOST_CHECK_EQUAL (t->components(), 1);
- BOOST_CHECK_EQUAL (t->stride()[0], 50 * 3);
- BOOST_CHECK_EQUAL (t->line_size()[0], 50 * 3);
- BOOST_CHECK (t->data()[0]);
- BOOST_CHECK (!t->data()[1]);
- BOOST_CHECK (!t->data()[2]);
- BOOST_CHECK (!t->data()[3]);
- BOOST_CHECK (t->data() != s->data());
- BOOST_CHECK (t->data()[0] != s->data()[0]);
- BOOST_CHECK (t->line_size() != s->line_size());
- BOOST_CHECK (t->line_size()[0] == s->line_size()[0]);
- BOOST_CHECK (t->stride() != s->stride());
- BOOST_CHECK (t->stride()[0] == s->stride()[0]);
-
- /* assignment operator */
- SimpleImage* u = new SimpleImage (PIX_FMT_YUV422P, libdcp::Size (150, 150), true);
- *u = *s;
- BOOST_CHECK_EQUAL (u->components(), 1);
- BOOST_CHECK_EQUAL (u->stride()[0], 50 * 3);
- BOOST_CHECK_EQUAL (u->line_size()[0], 50 * 3);
- BOOST_CHECK (u->data()[0]);
- BOOST_CHECK (!u->data()[1]);
- BOOST_CHECK (!u->data()[2]);
- BOOST_CHECK (!u->data()[3]);
- BOOST_CHECK (u->data() != s->data());
- BOOST_CHECK (u->data()[0] != s->data()[0]);
- BOOST_CHECK (u->line_size() != s->line_size());
- BOOST_CHECK (u->line_size()[0] == s->line_size()[0]);
- BOOST_CHECK (u->stride() != s->stride());
- BOOST_CHECK (u->stride()[0] == s->stride()[0]);
-
- delete s;
- delete t;
- delete u;
-}
-
-BOOST_AUTO_TEST_CASE (aligned_image_test)
-{
- SimpleImage* s = new SimpleImage (PIX_FMT_RGB24, libdcp::Size (50, 50), true);
- BOOST_CHECK_EQUAL (s->components(), 1);
- /* 160 is 150 aligned to the nearest 32 bytes */
- BOOST_CHECK_EQUAL (s->stride()[0], 160);
- BOOST_CHECK_EQUAL (s->line_size()[0], 150);
- BOOST_CHECK (s->data()[0]);
- BOOST_CHECK (!s->data()[1]);
- BOOST_CHECK (!s->data()[2]);
- BOOST_CHECK (!s->data()[3]);
-
- /* copy constructor */
- SimpleImage* t = new SimpleImage (*s);
- BOOST_CHECK_EQUAL (t->components(), 1);
- BOOST_CHECK_EQUAL (t->stride()[0], 160);
- BOOST_CHECK_EQUAL (t->line_size()[0], 150);
- BOOST_CHECK (t->data()[0]);
- BOOST_CHECK (!t->data()[1]);
- BOOST_CHECK (!t->data()[2]);
- BOOST_CHECK (!t->data()[3]);
- BOOST_CHECK (t->data() != s->data());
- BOOST_CHECK (t->data()[0] != s->data()[0]);
- BOOST_CHECK (t->line_size() != s->line_size());
- BOOST_CHECK (t->line_size()[0] == s->line_size()[0]);
- BOOST_CHECK (t->stride() != s->stride());
- BOOST_CHECK (t->stride()[0] == s->stride()[0]);
-
- /* assignment operator */
- SimpleImage* u = new SimpleImage (PIX_FMT_YUV422P, libdcp::Size (150, 150), false);
- *u = *s;
- BOOST_CHECK_EQUAL (u->components(), 1);
- BOOST_CHECK_EQUAL (u->stride()[0], 160);
- BOOST_CHECK_EQUAL (u->line_size()[0], 150);
- BOOST_CHECK (u->data()[0]);
- BOOST_CHECK (!u->data()[1]);
- BOOST_CHECK (!u->data()[2]);
- BOOST_CHECK (!u->data()[3]);
- BOOST_CHECK (u->data() != s->data());
- BOOST_CHECK (u->data()[0] != s->data()[0]);
- BOOST_CHECK (u->line_size() != s->line_size());
- BOOST_CHECK (u->line_size()[0] == s->line_size()[0]);
- BOOST_CHECK (u->stride() != s->stride());
- BOOST_CHECK (u->stride()[0] == s->stride()[0]);
-
- delete s;
- delete t;
- delete u;
-}
-
+#include "pixel_formats_test.cc"
+#include "make_black_test.cc"
+#include "trimmer_test.cc"
+#include "film_metadata_test.cc"
+#include "stream_test.cc"
+#include "format_test.cc"
+#include "util_test.cc"
+#include "film_test.cc"
+#include "dcp_test.cc"
+#include "frame_rate_test.cc"
+#include "job_test.cc"
+#include "client_server_test.cc"
+#include "image_test.cc"
--- /dev/null
+/*
+ Copyright (C) 2012 Carl Hetherington <cth@carlh.net>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+using boost::shared_ptr;
+
+shared_ptr<const Image> trimmer_test_last_video;
+shared_ptr<const AudioBuffers> trimmer_test_last_audio;
+
+void
+trimmer_test_video_helper (shared_ptr<const Image> image, bool, shared_ptr<Subtitle>)
+{
+ trimmer_test_last_video = image;
+}
+
+void
+trimmer_test_audio_helper (shared_ptr<const AudioBuffers> audio)
+{
+ trimmer_test_last_audio = audio;
+}
+
+BOOST_AUTO_TEST_CASE (trimmer_passthrough_test)
+{
+ Trimmer trimmer (shared_ptr<Log> (), 0, 0, 200, 48000, 25, 25);
+ trimmer.Video.connect (bind (&trimmer_test_video_helper, _1, _2, _3));
+ trimmer.Audio.connect (bind (&trimmer_test_audio_helper, _1));
+
+ shared_ptr<SimpleImage> video (new SimpleImage (PIX_FMT_RGB24, libdcp::Size (1998, 1080), true));
+ shared_ptr<AudioBuffers> audio (new AudioBuffers (6, 42 * 1920));
+
+ trimmer.process_video (video, false, shared_ptr<Subtitle> ());
+ trimmer.process_audio (audio);
+
+ BOOST_CHECK_EQUAL (video.get(), trimmer_test_last_video.get());
+ BOOST_CHECK_EQUAL (audio.get(), trimmer_test_last_audio.get());
+ BOOST_CHECK_EQUAL (audio->frames(), trimmer_test_last_audio->frames());
+}
+
+
+/** Test the audio handling of the Trimmer */
+BOOST_AUTO_TEST_CASE (trimmer_audio_test)
+{
+ Trimmer trimmer (shared_ptr<Log> (), 25, 75, 200, 48000, 25, 25);
+
+ trimmer.Audio.connect (bind (&trimmer_test_audio_helper, _1));
+
+ /* 21 video frames-worth of audio frames; should be completely stripped */
+ trimmer_test_last_audio.reset ();
+ shared_ptr<AudioBuffers> audio (new AudioBuffers (6, 21 * 1920));
+ trimmer.process_audio (audio);
+ BOOST_CHECK (trimmer_test_last_audio == 0);
+
+ /* 42 more video frames-worth, 4 should be stripped from the start */
+ audio.reset (new AudioBuffers (6, 42 * 1920));
+ trimmer.process_audio (audio);
+ BOOST_CHECK (trimmer_test_last_audio);
+ BOOST_CHECK_EQUAL (trimmer_test_last_audio->frames(), 38 * 1920);
+
+ /* 42 more video frames-worth, should be kept as-is */
+ trimmer_test_last_audio.reset ();
+ audio.reset (new AudioBuffers (6, 42 * 1920));
+ trimmer.process_audio (audio);
+ BOOST_CHECK (trimmer_test_last_audio);
+ BOOST_CHECK_EQUAL (trimmer_test_last_audio->frames(), 42 * 1920);
+
+ /* 25 more video frames-worth, 5 should be trimmed from the end */
+ trimmer_test_last_audio.reset ();
+ audio.reset (new AudioBuffers (6, 25 * 1920));
+ trimmer.process_audio (audio);
+ BOOST_CHECK (trimmer_test_last_audio);
+ BOOST_CHECK_EQUAL (trimmer_test_last_audio->frames(), 20 * 1920);
+
+ /* Now some more; all should be trimmed */
+ trimmer_test_last_audio.reset ();
+ audio.reset (new AudioBuffers (6, 100 * 1920));
+ trimmer.process_audio (audio);
+ BOOST_CHECK (trimmer_test_last_audio == 0);
+}
+
+
--- /dev/null
+/*
+ Copyright (C) 2012 Carl Hetherington <cth@carlh.net>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+BOOST_AUTO_TEST_CASE (util_test)
+{
+ string t = "Hello this is a string \"with quotes\" and indeed without them";
+ vector<string> b = split_at_spaces_considering_quotes (t);
+ vector<string>::iterator i = b.begin ();
+ BOOST_CHECK_EQUAL (*i++, "Hello");
+ BOOST_CHECK_EQUAL (*i++, "this");
+ BOOST_CHECK_EQUAL (*i++, "is");
+ BOOST_CHECK_EQUAL (*i++, "a");
+ BOOST_CHECK_EQUAL (*i++, "string");
+ BOOST_CHECK_EQUAL (*i++, "with quotes");
+ BOOST_CHECK_EQUAL (*i++, "and");
+ BOOST_CHECK_EQUAL (*i++, "indeed");
+ BOOST_CHECK_EQUAL (*i++, "without");
+ BOOST_CHECK_EQUAL (*i++, "them");
+}
+
+BOOST_AUTO_TEST_CASE (md5_digest_test)
+{
+ string const t = md5_digest ("test/md5.test");
+ BOOST_CHECK_EQUAL (t, "15058685ba99decdc4398c7634796eb0");
+
+ BOOST_CHECK_THROW (md5_digest ("foobar"), OpenFileError);
+}
define_name = 'HAVE_G_FORMAT_SIZE',
mandatory = False)
- conf.check_cc(fragment = """
- extern "C" {
- #include <libavutil/avutil.h>
- }
- int main() { AVPixelFormat f; }
- """, msg = 'Checking for AVPixelFormat',
- uselib = 'AVUTIL',
- define_name = 'HAVE_AV_PIXEL_FORMAT',
- mandatory = False)
-
- conf.check_cc(fragment = """
- extern "C" {
- #include <libavcodec/avcodec.h>
- }
- int main() { AVFrame* f; av_frame_get_best_effort_timestamp(f); }
- """, msg = 'Checking for av_frame_get_best_effort_timestamp',
- uselib = 'AVCODEC',
- define_name = 'HAVE_AV_FRAME_GET_BEST_EFFORT_TIMESTAMP',
- mandatory = False)
-
- conf.check_cc(fragment = """
- extern "C" {
- #include <libavfilter/buffersrc.h>
- }
- int main() { }
- """, msg = 'Checking for buffersrc.h',
- uselib = 'AVCODEC',
- define_name = 'HAVE_BUFFERSRC_H',
- mandatory = False)
-
conf.find_program('msgfmt', var='MSGFMT')
datadir = conf.env.DATADIR