Merge branch 'master' of ssh://houllier/home/carl/git/dvdomatic
authorCarl Hetherington <cth@carlh.net>
Fri, 24 May 2013 21:29:34 +0000 (22:29 +0100)
committerCarl Hetherington <cth@carlh.net>
Fri, 24 May 2013 21:29:34 +0000 (22:29 +0100)
29 files changed:
cscript
src/lib/delay_line.cc
src/lib/encoder.cc
src/lib/ffmpeg_compatibility.cc [deleted file]
src/lib/ffmpeg_compatibility.h [deleted file]
src/lib/ffmpeg_decoder.cc
src/lib/filter_graph.cc
src/lib/filter_graph.h
src/lib/image.cc
src/lib/image.h
src/lib/matcher.cc
src/lib/matcher.h
src/lib/transcoder.cc
src/lib/wscript
test/client_server_test.cc [new file with mode: 0644]
test/dcp_test.cc [new file with mode: 0644]
test/film_metadata_test.cc [new file with mode: 0644]
test/film_test.cc [new file with mode: 0644]
test/format_test.cc [new file with mode: 0644]
test/frame_rate_test.cc [new file with mode: 0644]
test/image_test.cc [new file with mode: 0644]
test/job_test.cc [new file with mode: 0644]
test/make_black_test.cc [new file with mode: 0644]
test/pixel_formats_test.cc [new file with mode: 0644]
test/stream_test.cc [new file with mode: 0644]
test/test.cc
test/trimmer_test.cc [new file with mode: 0644]
test/util_test.cc [new file with mode: 0644]
wscript

diff --git a/cscript b/cscript
index 4c54b03774fa8f8a9930aa45bef3269395b887ba..521bc54f9c20691ab214c0ba5fed3741a6a1eead 100644 (file)
--- a/cscript
+++ b/cscript
@@ -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):
index b0180800a4625795823697c2dd5c9d1bfd2fcfff..f6af6f9b12a2b51e1bdad65ffe9b5590f1ed101d 100644 (file)
@@ -50,7 +50,7 @@ void
 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);
index cff9899acb6151cc19990f372c0a091c7d63968f..8549962ff01807505683266e778d16c4c8eaef43 100644 (file)
@@ -296,6 +296,10 @@ Encoder::process_video (shared_ptr<const Image> image, bool same, boost::shared_
 void
 Encoder::process_audio (shared_ptr<const AudioBuffers> data)
 {
+       if (!data->frames ()) {
+               return;
+       }
+       
 #if HAVE_SWRESAMPLE
        /* Maybe sample-rate convert */
        if (_swr_context) {
diff --git a/src/lib/ffmpeg_compatibility.cc b/src/lib/ffmpeg_compatibility.cc
deleted file mode 100644 (file)
index 361fa74..0000000
+++ /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_compatibility.h b/src/lib/ffmpeg_compatibility.h
deleted file mode 100644 (file)
index 772d22c..0000000
+++ /dev/null
@@ -1,31 +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.
-
-*/
-
-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
index 8e09810cb2cc75c3a6109139e0a0db8427f4eb8e..982139515b0af588eeb9c8180e5accd74cef444f 100644 (file)
@@ -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));
        }
 }
 
index 045cbaa6a0ba504c6f4acda30adbaba711884e8e..b0427a23d0566166921fd6e95eceb5b86c879505 100644 (file)
 
 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));
+}
index 7e4e8422b07d36236b2185c588f2a23311eab18e..2138943e42b6e3e6ef11ba4967f0c85f0755d618 100644 (file)
 #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
index 1be41fecf880dffaaa5f8ddbfafd20ec5296e175..b166dfac6a0c72054531dc19cce0b9a7fd445971 100644 (file)
@@ -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)
 {
index 62961a92e12d56cd5d9ac658bb12d1c47e27658d..70dacfaee4bd9e4bcd24bdf8ba4241916b1941e4 100644 (file)
@@ -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 &);
index 9924c003ae5b81298ef27653e5ba015d7d312307..4acb82afa51924852631c4af40ec75659c06296a 100644 (file)
@@ -48,45 +48,47 @@ Matcher::process_video (boost::shared_ptr<const Image> image, bool same, boost::
 
        _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
@@ -95,27 +97,29 @@ Matcher::process_audio (boost::shared_ptr<const AudioBuffers> b, double t)
        _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 ();
        }
@@ -136,13 +140,20 @@ Matcher::process_end ()
 }
 
 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);
index 41aa373a412cd51808dc5d7967e3c04640cc7a80..61fd814362b41b2f0b61758752c6bd3fe28f834c 100644 (file)
@@ -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 
 {
@@ -30,7 +29,7 @@ public:
        void process_end ();
 
 private:
-       void fix_start (double);
+       void fix_start ();
        void match (double);
        void repeat_last_video ();
        
@@ -42,6 +41,20 @@ private:
        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)
@@ -52,6 +65,7 @@ private:
                double time;
        };
 
+       std::list<VideoRecord> _pending_video;
        std::list<AudioRecord> _pending_audio;
 
        boost::optional<double> _first_input;
index fcfbb164887c028fd3785055b3d91e863fb4542b..48cf402d77bac4f36ababbac36adff4fb880d5e9 100644 (file)
@@ -56,7 +56,7 @@ Transcoder::Transcoder (shared_ptr<Film> f, DecodeOptions o, Job* j, shared_ptr<
        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));
index 253a496d1ae1caa07fb0ea02b613385b7fec23c8..66207b1e4468f700729e69b412fecb28650483a9 100644 (file)
@@ -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 (file)
index 0000000..e5229b5
--- /dev/null
@@ -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 (file)
index 0000000..9312a20
--- /dev/null
@@ -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 (file)
index 0000000..0b4495b
--- /dev/null
@@ -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/test/film_test.cc b/test/film_test.cc
new file mode 100644 (file)
index 0000000..02dfa05
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+    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");
+}
+
diff --git a/test/format_test.cc b/test/format_test.cc
new file mode 100644 (file)
index 0000000..b150738
--- /dev/null
@@ -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 (file)
index 0000000..0070065
--- /dev/null
@@ -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 (file)
index 0000000..a9ec304
--- /dev/null
@@ -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 (file)
index 0000000..247d4f7
--- /dev/null
@@ -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 (file)
index 0000000..3c0b979
--- /dev/null
@@ -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 (file)
index 0000000..84f2a33
--- /dev/null
@@ -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 (file)
index 0000000..b467dc9
--- /dev/null
@@ -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");
+}
+
index 496c915198818753036d2864656cd26aa75693e8..91c876412f74b15af3f6f37eea5fc932cb7d1293 100644 (file)
@@ -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 (file)
index 0000000..605f7d1
--- /dev/null
@@ -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 (file)
index 0000000..18c24ac
--- /dev/null
@@ -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);
+}
diff --git a/wscript b/wscript
index 0682e2674c5d53702c8d75b83f5a45060406566f..b5676ba5bdb30988d6db07e6d0f29f6a95e9ff39 100644 (file)
--- a/wscript
+++ b/wscript
@@ -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