diff options
| -rw-r--r-- | cscript | 2 | ||||
| -rw-r--r-- | src/lib/ffmpeg_compatibility.cc | 119 | ||||
| -rw-r--r-- | src/lib/ffmpeg_decoder.cc | 39 | ||||
| -rw-r--r-- | src/lib/filter_graph.cc | 118 | ||||
| -rw-r--r-- | src/lib/filter_graph.h | 30 | ||||
| -rw-r--r-- | src/lib/image.cc | 204 | ||||
| -rw-r--r-- | src/lib/image.h | 29 | ||||
| -rw-r--r-- | src/lib/matcher.h | 1 | ||||
| -rw-r--r-- | src/lib/wscript | 1 | ||||
| -rw-r--r-- | test/client_server_test.cc | 106 | ||||
| -rw-r--r-- | test/dcp_test.cc | 67 | ||||
| -rw-r--r-- | test/film_metadata_test.cc | 76 | ||||
| -rw-r--r-- | test/film_test.cc (renamed from src/lib/ffmpeg_compatibility.h) | 19 | ||||
| -rw-r--r-- | test/format_test.cc | 61 | ||||
| -rw-r--r-- | test/frame_rate_test.cc | 211 | ||||
| -rw-r--r-- | test/image_test.cc | 99 | ||||
| -rw-r--r-- | test/job_test.cc | 64 | ||||
| -rw-r--r-- | test/make_black_test.cc | 56 | ||||
| -rw-r--r-- | test/pixel_formats_test.cc | 77 | ||||
| -rw-r--r-- | test/stream_test.cc | 52 | ||||
| -rw-r--r-- | test/test.cc | 796 | ||||
| -rw-r--r-- | test/trimmer_test.cc | 95 | ||||
| -rw-r--r-- | test/util_test.cc | 43 | ||||
| -rw-r--r-- | wscript | 30 |
24 files changed, 1205 insertions, 1190 deletions
@@ -7,7 +7,7 @@ def dependencies(target): return () else: return (('openjpeg-cdist', None), - ('ffmpeg-cdist', '488d5d4496af5e3a3b9d31d6b221e8eeada6b77e'), + ('ffmpeg-cdist', '7a23ec9c771184ab563cfe24ad9b427f38368961'), ('libdcp', 'v0.49')) def build(env, target): diff --git a/src/lib/ffmpeg_compatibility.cc b/src/lib/ffmpeg_compatibility.cc deleted file mode 100644 index 361fa7423..000000000 --- a/src/lib/ffmpeg_compatibility.cc +++ /dev/null @@ -1,119 +0,0 @@ -/* - 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 diff --git a/src/lib/ffmpeg_decoder.cc b/src/lib/ffmpeg_decoder.cc index 8e09810cb..982139515 100644 --- a/src/lib/ffmpeg_decoder.cc +++ b/src/lib/ffmpeg_decoder.cc @@ -500,32 +500,35 @@ FFmpegDecoder::set_subtitle_stream (shared_ptr<SubtitleStream> s) 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)); } } diff --git a/src/lib/filter_graph.cc b/src/lib/filter_graph.cc index 045cbaa6a..b0427a23d 100644 --- a/src/lib/filter_graph.cc +++ b/src/lib/filter_graph.cc @@ -23,20 +23,13 @@ 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" @@ -51,18 +44,20 @@ using std::list; 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_(","); @@ -80,20 +75,20 @@ FilterGraph::FilterGraph (shared_ptr<Film> film, FFmpegDecoder* decoder, libdcp: 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")); } @@ -107,6 +102,8 @@ FilterGraph::FilterGraph (shared_ptr<Film> film, FFmpegDecoder* decoder, libdcp: 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; @@ -119,15 +116,9 @@ FilterGraph::FilterGraph (shared_ptr<Film> film, FFmpegDecoder* decoder, libdcp: 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.")); @@ -136,66 +127,29 @@ FilterGraph::FilterGraph (shared_ptr<Film> film, FFmpegDecoder* decoder, libdcp: /* 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; @@ -206,7 +160,25 @@ FilterGraph::process (AVFrame const * frame) * @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)); +} diff --git a/src/lib/filter_graph.h b/src/lib/filter_graph.h index 7e4e8422b..2138943e4 100644 --- a/src/lib/filter_graph.h +++ b/src/lib/filter_graph.h @@ -25,28 +25,48 @@ #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 diff --git a/src/lib/image.cc b/src/lib/image.cc index 1be41fecf..b166dfac6 100644 --- a/src/lib/image.cc +++ b/src/lib/image.cc @@ -35,6 +35,7 @@ extern "C" { #include <libavfilter/avfiltergraph.h> #include <libpostproc/postprocess.h> #include <libavutil/pixfmt.h> +#include <libavutil/pixdesc.h> } #include "image.h" #include "exceptions.h" @@ -58,55 +59,32 @@ Image::swap (Image& other) 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> @@ -296,12 +274,12 @@ Image::make_black () { /* 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: @@ -326,11 +304,17 @@ Image::make_black () 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; @@ -420,53 +404,36 @@ Image::write_to_socket (shared_ptr<Socket> socket) const 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. * @@ -502,10 +469,9 @@ SimpleImage::allocate () 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) { @@ -519,6 +485,25 @@ SimpleImage::SimpleImage (SimpleImage const & other) } } +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()) { @@ -609,59 +594,6 @@ SimpleImage::aligned () const 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) { diff --git a/src/lib/image.h b/src/lib/image.h index 62961a92e..70dacfaee 100644 --- a/src/lib/image.h +++ b/src/lib/image.h @@ -32,10 +32,8 @@ extern "C" { #include <libavfilter/avfilter.h> } #include "util.h" -#include "ffmpeg_compatibility.h" class Scaler; -class RGBFrameImage; class SimpleImage; /** @class Image @@ -92,6 +90,8 @@ protected: 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); @@ -99,30 +99,6 @@ private: 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(). */ @@ -130,6 +106,7 @@ class SimpleImage : public Image { public: SimpleImage (AVPixelFormat, libdcp::Size, bool); + SimpleImage (AVFrame *); SimpleImage (SimpleImage const &); SimpleImage (boost::shared_ptr<const Image>); SimpleImage& operator= (SimpleImage const &); diff --git a/src/lib/matcher.h b/src/lib/matcher.h index 41aa373a4..21e42f53d 100644 --- a/src/lib/matcher.h +++ b/src/lib/matcher.h @@ -19,7 +19,6 @@ #include <boost/optional.hpp> #include "processor.h" -#include "ffmpeg_compatibility.h" class Matcher : public Processor, public TimedAudioSink, public TimedVideoSink, public AudioSource, public VideoSource { diff --git a/src/lib/wscript b/src/lib/wscript index 253a496d1..66207b1e4 100644 --- a/src/lib/wscript +++ b/src/lib/wscript @@ -22,7 +22,6 @@ sources = """ examine_content_job.cc exceptions.cc filter_graph.cc - ffmpeg_compatibility.cc ffmpeg_decoder.cc film.cc filter.cc diff --git a/test/client_server_test.cc b/test/client_server_test.cc new file mode 100644 index 000000000..e5229b5ff --- /dev/null +++ b/test/client_server_test.cc @@ -0,0 +1,106 @@ +/* + 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; + } +} + diff --git a/test/dcp_test.cc b/test/dcp_test.cc new file mode 100644 index 000000000..9312a2067 --- /dev/null +++ b/test/dcp_test.cc @@ -0,0 +1,67 @@ +/* + 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); +} + diff --git a/test/film_metadata_test.cc b/test/film_metadata_test.cc new file mode 100644 index 000000000..0b4495b48 --- /dev/null +++ b/test/film_metadata_test.cc @@ -0,0 +1,76 @@ +/* + 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); +} diff --git a/src/lib/ffmpeg_compatibility.h b/test/film_test.cc index 772d22c33..02dfa0553 100644 --- a/src/lib/ffmpeg_compatibility.h +++ b/test/film_test.cc @@ -17,15 +17,14 @@ */ -struct AVFilterInOut; +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"); -extern AVFilter* get_sink (); -extern AVFilterInOut* avfilter_inout_alloc (); + 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"); +} -#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 diff --git a/test/format_test.cc b/test/format_test.cc new file mode 100644 index 000000000..b150738a4 --- /dev/null +++ b/test/format_test.cc @@ -0,0 +1,61 @@ +/* + 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)); +} + diff --git a/test/frame_rate_test.cc b/test/frame_rate_test.cc new file mode 100644 index 000000000..00700656e --- /dev/null +++ b/test/frame_rate_test.cc @@ -0,0 +1,211 @@ +/* + 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)); +} + diff --git a/test/image_test.cc b/test/image_test.cc new file mode 100644 index 000000000..a9ec3042a --- /dev/null +++ b/test/image_test.cc @@ -0,0 +1,99 @@ + +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; +} diff --git a/test/job_test.cc b/test/job_test.cc new file mode 100644 index 000000000..247d4f756 --- /dev/null +++ b/test/job_test.cc @@ -0,0 +1,64 @@ +/* + 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); +} diff --git a/test/make_black_test.cc b/test/make_black_test.cc new file mode 100644 index 000000000..3c0b979ff --- /dev/null +++ b/test/make_black_test.cc @@ -0,0 +1,56 @@ +/* + 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; + } +} diff --git a/test/pixel_formats_test.cc b/test/pixel_formats_test.cc new file mode 100644 index 000000000..84f2a33ce --- /dev/null +++ b/test/pixel_formats_test.cc @@ -0,0 +1,77 @@ +/* + 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]); + } +} diff --git a/test/stream_test.cc b/test/stream_test.cc new file mode 100644 index 000000000..b467dc9a2 --- /dev/null +++ b/test/stream_test.cc @@ -0,0 +1,52 @@ +/* + 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"); +} + diff --git a/test/test.cc b/test/test.cc index 496c91519..91c876412 100644 --- a/test/test.cc +++ b/test/test.cc @@ -52,14 +52,20 @@ using boost::shared_ptr; 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) @@ -82,766 +88,16 @@ new_test_film (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" diff --git a/test/trimmer_test.cc b/test/trimmer_test.cc new file mode 100644 index 000000000..605f7d1b2 --- /dev/null +++ b/test/trimmer_test.cc @@ -0,0 +1,95 @@ +/* + 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); +} + + diff --git a/test/util_test.cc b/test/util_test.cc new file mode 100644 index 000000000..18c24ac3a --- /dev/null +++ b/test/util_test.cc @@ -0,0 +1,43 @@ +/* + 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); +} @@ -174,36 +174,6 @@ def configure(conf): 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 |
