From 326bf1a5409b2520464ff5df17051d4377955495 Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Tue, 30 Apr 2013 15:47:41 +0100 Subject: Remove old ffmpeg compatibility stuff. --- src/lib/ffmpeg_compatibility.cc | 119 ---------------------------------------- src/lib/ffmpeg_compatibility.h | 31 ----------- src/lib/filter_graph.cc | 55 ++----------------- src/lib/filter_graph.h | 1 - src/lib/image.h | 1 - src/lib/matcher.h | 1 - src/lib/wscript | 1 - 7 files changed, 4 insertions(+), 205 deletions(-) delete mode 100644 src/lib/ffmpeg_compatibility.cc delete mode 100644 src/lib/ffmpeg_compatibility.h (limited to 'src/lib') diff --git a/src/lib/ffmpeg_compatibility.cc b/src/lib/ffmpeg_compatibility.cc deleted file mode 100644 index 361fa7423..000000000 --- a/src/lib/ffmpeg_compatibility.cc +++ /dev/null @@ -1,119 +0,0 @@ -/* - Copyright (C) 2012 Carl Hetherington - - 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 -} -#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 (&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 (&buffer_sink->inputs[1])->name = 0; - buffer_sink->outputs = new AVFilterPad[1]; - const_cast (&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 index 772d22c33..000000000 --- a/src/lib/ffmpeg_compatibility.h +++ /dev/null @@ -1,31 +0,0 @@ -/* - Copyright (C) 2012 Carl Hetherington - - 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 diff --git a/src/lib/filter_graph.cc b/src/lib/filter_graph.cc index 045cbaa6a..c57d26e8d 100644 --- a/src/lib/filter_graph.cc +++ b/src/lib/filter_graph.cc @@ -23,20 +23,13 @@ extern "C" { #include -#ifdef HAVE_BUFFERSRC_H #include -#endif -#if (LIBAVFILTER_VERSION_MAJOR == 2 && LIBAVFILTER_VERSION_MINOR >= 53 && LIBAVFILTER_VERSION_MINOR <= 77) || LIBAVFILTER_VERSION_MAJOR == 3 #include #include -#elif LIBAVFILTER_VERSION_MAJOR == 2 && LIBAVFILTER_VERSION_MINOR == 15 -#include -#endif #include } #include "decoder.h" #include "filter_graph.h" -#include "ffmpeg_compatibility.h" #include "filter.h" #include "exceptions.h" #include "image.h" @@ -80,7 +73,10 @@ FilterGraph::FilterGraph (shared_ptr 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_(":") @@ -119,15 +115,9 @@ FilterGraph::FilterGraph (shared_ptr 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.")); @@ -144,54 +134,17 @@ FilterGraph::process (AVFrame const * frame) { list > 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; } -#endif - if (filter_buffer) { /* This takes ownership of filter_buffer */ images.push_back (shared_ptr (new FilterBufferImage ((PixelFormat) frame->format, filter_buffer))); diff --git a/src/lib/filter_graph.h b/src/lib/filter_graph.h index 7e4e8422b..ffd6855de 100644 --- a/src/lib/filter_graph.h +++ b/src/lib/filter_graph.h @@ -25,7 +25,6 @@ #define DVDOMATIC_FILTER_GRAPH_H #include "util.h" -#include "ffmpeg_compatibility.h" class Image; class VideoFilter; diff --git a/src/lib/image.h b/src/lib/image.h index 62961a92e..ad2aa79bd 100644 --- a/src/lib/image.h +++ b/src/lib/image.h @@ -32,7 +32,6 @@ extern "C" { #include } #include "util.h" -#include "ffmpeg_compatibility.h" class Scaler; class RGBFrameImage; diff --git a/src/lib/matcher.h b/src/lib/matcher.h index 41aa373a4..21e42f53d 100644 --- a/src/lib/matcher.h +++ b/src/lib/matcher.h @@ -19,7 +19,6 @@ #include #include "processor.h" -#include "ffmpeg_compatibility.h" class Matcher : public Processor, public TimedAudioSink, public TimedVideoSink, public AudioSource, public VideoSource { diff --git a/src/lib/wscript b/src/lib/wscript index 51b103afd..a4b68801f 100644 --- a/src/lib/wscript +++ b/src/lib/wscript @@ -22,7 +22,6 @@ sources = """ examine_content_job.cc exceptions.cc filter_graph.cc - ffmpeg_compatibility.cc ffmpeg_decoder.cc film.cc filter.cc -- cgit v1.2.3 From 8889cf7126810fb9b754643a45dcc94ad578125f Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Tue, 30 Apr 2013 15:59:26 +0100 Subject: Update filter graph to new API. --- src/lib/filter_graph.cc | 20 +++++++++----------- src/lib/filter_graph.h | 2 +- src/lib/image.cc | 32 ++++++++++++++------------------ src/lib/image.h | 16 ++++++++-------- 4 files changed, 32 insertions(+), 38 deletions(-) (limited to 'src/lib') diff --git a/src/lib/filter_graph.cc b/src/lib/filter_graph.cc index c57d26e8d..2352b3e8a 100644 --- a/src/lib/filter_graph.cc +++ b/src/lib/filter_graph.cc @@ -130,25 +130,23 @@ FilterGraph::FilterGraph (shared_ptr film, FFmpegDecoder* decoder, libdcp: * set of Images. */ list > -FilterGraph::process (AVFrame const * frame) +FilterGraph::process (AVFrame* frame) { list > images; - - if (av_buffersrc_write_frame (_buffer_src_context, frame) < 0) { + if (av_buffersrc_add_frame (_buffer_src_context, frame) < 0) { throw DecodeError (N_("could not push buffer into filter chain.")); } - while (av_buffersink_read (_buffer_sink_context, 0)) { - AVFilterBufferRef* filter_buffer; - if (av_buffersink_get_buffer_ref (_buffer_sink_context, &filter_buffer, 0) < 0) { - filter_buffer = 0; + while (1) { + AVFrame* frame = av_frame_alloc (); + if (av_buffersink_get_frame (_buffer_sink_context, 0) < 0) { + av_frame_free (&frame); + break; } - if (filter_buffer) { - /* This takes ownership of filter_buffer */ - images.push_back (shared_ptr (new FilterBufferImage ((PixelFormat) frame->format, filter_buffer))); - } + /* This takes ownership of the AVFrame */ + images.push_back (shared_ptr (new FrameImage (frame))); } return images; diff --git a/src/lib/filter_graph.h b/src/lib/filter_graph.h index ffd6855de..249b89851 100644 --- a/src/lib/filter_graph.h +++ b/src/lib/filter_graph.h @@ -39,7 +39,7 @@ public: FilterGraph (boost::shared_ptr film, FFmpegDecoder* decoder, libdcp::Size s, AVPixelFormat p); bool can_process (libdcp::Size s, AVPixelFormat p) const; - std::list > process (AVFrame const * frame); + std::list > process (AVFrame * frame); private: AVFilterContext* _buffer_src_context; diff --git a/src/lib/image.cc b/src/lib/image.cc index 1be41fecf..2d4bc0af0 100644 --- a/src/lib/image.cc +++ b/src/lib/image.cc @@ -609,9 +609,9 @@ SimpleImage::aligned () const return _aligned; } -FilterBufferImage::FilterBufferImage (AVPixelFormat p, AVFilterBufferRef* b) - : Image (p) - , _buffer (b) +FrameImage::FrameImage (AVFrame* frame) + : Image (static_cast (frame->format)) + , _frame (frame) { _line_size = (int *) av_malloc (4 * sizeof (int)); _line_size[0] = _line_size[1] = _line_size[2] = _line_size[3] = 0; @@ -621,44 +621,40 @@ FilterBufferImage::FilterBufferImage (AVPixelFormat p, AVFilterBufferRef* b) } } -FilterBufferImage::~FilterBufferImage () +FrameImage::~FrameImage () { - avfilter_unref_buffer (_buffer); + av_frame_free (&_frame); av_free (_line_size); } uint8_t ** -FilterBufferImage::data () const +FrameImage::data () const { - return _buffer->data; + return _frame->data; } int * -FilterBufferImage::line_size () const +FrameImage::line_size () const { return _line_size; } int * -FilterBufferImage::stride () const +FrameImage::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; + /* AVFrame's `linesize' is what we call `stride' */ + return _frame->linesize; } libdcp::Size -FilterBufferImage::size () const +FrameImage::size () const { - return libdcp::Size (_buffer->video->w, _buffer->video->h); + return libdcp::Size (_frame->width, _frame->height); } bool -FilterBufferImage::aligned () const +FrameImage::aligned () const { - /* XXX? */ return true; } diff --git a/src/lib/image.h b/src/lib/image.h index ad2aa79bd..00768ee02 100644 --- a/src/lib/image.h +++ b/src/lib/image.h @@ -98,14 +98,14 @@ 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 FrameImage + * @brief An Image that is held in an AVFrame. */ -class FilterBufferImage : public Image +class FrameImage : public Image { public: - FilterBufferImage (AVPixelFormat, AVFilterBufferRef *); - ~FilterBufferImage (); + FrameImage (AVFrame *); + ~FrameImage (); uint8_t ** data () const; int * line_size () const; @@ -115,10 +115,10 @@ public: private: /* Not allowed */ - FilterBufferImage (FilterBufferImage const &); - FilterBufferImage& operator= (FilterBufferImage const &); + FrameImage (FrameImage const &); + FrameImage& operator= (FrameImage const &); - AVFilterBufferRef* _buffer; + AVFrame* _frame; int* _line_size; }; -- cgit v1.2.3 From 32b21d67ae5b87c5840dd0f642840aa2e245a6ed Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Tue, 30 Apr 2013 16:28:33 +0100 Subject: Typo. --- src/lib/filter_graph.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/lib') diff --git a/src/lib/filter_graph.cc b/src/lib/filter_graph.cc index 2352b3e8a..47f99da14 100644 --- a/src/lib/filter_graph.cc +++ b/src/lib/filter_graph.cc @@ -140,7 +140,7 @@ FilterGraph::process (AVFrame* frame) while (1) { AVFrame* frame = av_frame_alloc (); - if (av_buffersink_get_frame (_buffer_sink_context, 0) < 0) { + if (av_buffersink_get_frame (_buffer_sink_context, frame) < 0) { av_frame_free (&frame); break; } -- cgit v1.2.3 From 65cd9738e069023ae00d84b46d8eac74b016895c Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Tue, 30 Apr 2013 16:46:21 +0100 Subject: Fix reffing of the AVFrame output by the decoder. --- src/lib/filter_graph.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/lib') diff --git a/src/lib/filter_graph.cc b/src/lib/filter_graph.cc index 47f99da14..f0c49b37c 100644 --- a/src/lib/filter_graph.cc +++ b/src/lib/filter_graph.cc @@ -134,7 +134,7 @@ FilterGraph::process (AVFrame* frame) { list > images; - if (av_buffersrc_add_frame (_buffer_src_context, frame) < 0) { + if (av_buffersrc_write_frame (_buffer_src_context, frame) < 0) { throw DecodeError (N_("could not push buffer into filter chain.")); } -- cgit v1.2.3 From 254664b5a482cb3551d0a98ececfbdd8296e6c71 Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Tue, 30 Apr 2013 19:13:09 +0100 Subject: Couple of missing formats from Image::components(). --- src/lib/image.cc | 3 +++ 1 file changed, 3 insertions(+) (limited to 'src/lib') diff --git a/src/lib/image.cc b/src/lib/image.cc index 2d4bc0af0..d2842e095 100644 --- a/src/lib/image.cc +++ b/src/lib/image.cc @@ -90,6 +90,9 @@ Image::components () const { switch (_pixel_format) { case PIX_FMT_YUV420P: + case PIX_FMT_YUV422P9BE + case PIX_FMT_YUV422P9LE: + case PIX_FMT_YUV422P10BE: case PIX_FMT_YUV422P10LE: case PIX_FMT_YUV422P: case PIX_FMT_YUV444P: -- cgit v1.2.3 From d6a5bde6923078f8914f263ee76c6122b588197d Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Wed, 1 May 2013 08:07:39 +0100 Subject: Typo. --- src/lib/image.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/lib') diff --git a/src/lib/image.cc b/src/lib/image.cc index d2842e095..5ae9ba1d3 100644 --- a/src/lib/image.cc +++ b/src/lib/image.cc @@ -90,7 +90,7 @@ Image::components () const { switch (_pixel_format) { case PIX_FMT_YUV420P: - case PIX_FMT_YUV422P9BE + case PIX_FMT_YUV422P9BE: case PIX_FMT_YUV422P9LE: case PIX_FMT_YUV422P10BE: case PIX_FMT_YUV422P10LE: -- cgit v1.2.3 From 502672fd38b12b41746949dd6d66cfe203b657df Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Wed, 1 May 2013 13:09:27 +0100 Subject: More pixel formats. --- src/lib/image.cc | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'src/lib') diff --git a/src/lib/image.cc b/src/lib/image.cc index 5ae9ba1d3..6464d2368 100644 --- a/src/lib/image.cc +++ b/src/lib/image.cc @@ -68,8 +68,10 @@ Image::lines (int n) const break; case PIX_FMT_RGB24: case PIX_FMT_RGBA: - case PIX_FMT_YUV422P10LE: case PIX_FMT_YUV422P: + case PIX_FMT_YUV422P10LE: + case PIX_FMT_YUV422P16LE: + case PIX_FMT_YUV422P16BE: case PIX_FMT_YUV444P: case PIX_FMT_YUV444P9BE: case PIX_FMT_YUV444P9LE: @@ -94,12 +96,16 @@ Image::components () const case PIX_FMT_YUV422P9LE: case PIX_FMT_YUV422P10BE: case PIX_FMT_YUV422P10LE: + case PIX_FMT_YUV422P16LE: + case PIX_FMT_YUV422P16BE: 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_YUV444P16LE: + case PIX_FMT_YUV444P16BE: return 3; case PIX_FMT_RGB24: case PIX_FMT_RGBA: @@ -448,6 +454,7 @@ Image::bytes_per_pixel (int c) const return 0.5; } case PIX_FMT_YUV422P10LE: + case PIX_FMT_YUV422P16LE: if (c == 0) { return 2; } else { -- cgit v1.2.3 From 85f70dbf690a9f77189067e655b02283f910a36f Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Wed, 1 May 2013 14:16:38 +0100 Subject: Add pixel formats tests. --- src/lib/image.h | 2 ++ test/test.cc | 53 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+) (limited to 'src/lib') diff --git a/src/lib/image.h b/src/lib/image.h index 00768ee02..39d84fcd4 100644 --- a/src/lib/image.h +++ b/src/lib/image.h @@ -91,6 +91,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); diff --git a/test/test.cc b/test/test.cc index 496c91519..11ca56031 100644 --- a/test/test.cc +++ b/test/test.cc @@ -123,6 +123,59 @@ BOOST_AUTO_TEST_CASE (make_black_test) } } + +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) +{ + list 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, 2, 2 )); + cases.push_back(Case(AV_PIX_FMT_YUV444P, 3, 480, 480, 480, 3, 3, 3 )); + cases.push_back(Case(AV_PIX_FMT_YUV444P9BE, 3, 480, 480, 480, 6, 6, 6 )); + cases.push_back(Case(AV_PIX_FMT_YUV444P9LE, 3, 480, 480, 480, 6, 6, 6 )); + cases.push_back(Case(AV_PIX_FMT_YUV444P10BE, 3, 480, 480, 480, 6, 6, 6 )); + cases.push_back(Case(AV_PIX_FMT_YUV444P10LE, 3, 480, 480, 480, 6, 6, 6 )); + + for (list::iterator i = cases.begin(); i != cases.end(); ++i) { + AVFrame* f = av_frame_alloc (); + f->width = 640; + f->height = 480; + f->format = static_cast (i->format); + FrameImage 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]); + } +} + shared_ptr trimmer_test_last_video; shared_ptr trimmer_test_last_audio; -- cgit v1.2.3 From c45fd99b0b4a193edcebccc927793d48431a5a13 Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Wed, 1 May 2013 15:12:38 +0100 Subject: Use ffmpeg calls for pixel parameters; add (and alter, hmm) tests to suit. --- src/lib/image.cc | 125 ++----- test/client_server_test.cc | 106 ++++++ test/dcp_test.cc | 67 ++++ test/film_metadata_test.cc | 76 ++++ test/film_test.cc | 30 ++ test/format_test.cc | 61 ++++ test/frame_rate_test.cc | 211 +++++++++++ test/image_test.cc | 99 ++++++ test/job_test.cc | 64 ++++ test/make_black_test.cc | 55 +++ test/pixel_formats_test.cc | 77 ++++ test/stream_test.cc | 52 +++ test/test.cc | 849 ++------------------------------------------- test/trimmer_test.cc | 95 +++++ test/util_test.cc | 43 +++ 15 files changed, 1100 insertions(+), 910 deletions(-) create mode 100644 test/client_server_test.cc create mode 100644 test/dcp_test.cc create mode 100644 test/film_metadata_test.cc create mode 100644 test/film_test.cc create mode 100644 test/format_test.cc create mode 100644 test/frame_rate_test.cc create mode 100644 test/image_test.cc create mode 100644 test/job_test.cc create mode 100644 test/make_black_test.cc create mode 100644 test/pixel_formats_test.cc create mode 100644 test/stream_test.cc create mode 100644 test/trimmer_test.cc create mode 100644 test/util_test.cc (limited to 'src/lib') diff --git a/src/lib/image.cc b/src/lib/image.cc index 6464d2368..0b887ea62 100644 --- a/src/lib/image.cc +++ b/src/lib/image.cc @@ -35,6 +35,7 @@ extern "C" { #include #include #include +#include } #include "image.h" #include "exceptions.h" @@ -58,64 +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_YUV422P: - case PIX_FMT_YUV422P10LE: - case PIX_FMT_YUV422P16LE: - case PIX_FMT_YUV422P16BE: - 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_YUV422P9BE: - case PIX_FMT_YUV422P9LE: - case PIX_FMT_YUV422P10BE: - case PIX_FMT_YUV422P10LE: - case PIX_FMT_YUV422P16LE: - case PIX_FMT_YUV422P16BE: - 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_YUV444P16LE: - case PIX_FMT_YUV444P16BE: - 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 @@ -429,54 +398,36 @@ Image::write_to_socket (shared_ptr 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: - case PIX_FMT_YUV422P16LE: - 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. * diff --git a/test/client_server_test.cc b/test/client_server_test.cc new file mode 100644 index 000000000..e5229b5ff --- /dev/null +++ b/test/client_server_test.cc @@ -0,0 +1,106 @@ +/* + Copyright (C) 2012 Carl Hetherington + + 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 frame, ServerDescription* description, shared_ptr locally_encoded) +{ + shared_ptr 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 (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 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 (new Subtitle (Position (50, 60), sub_image)); + + shared_ptr log (new FileLog ("build/test/client_server_test.log")); + + shared_ptr frame ( + new DCPVideoFrame ( + image, + subtitle, + libdcp::Size (1998, 1080), + 0, + 0, + 1, + Scaler::from_id ("bicubic"), + 0, + 24, + "", + 0, + 200000000, + log + ) + ); + + shared_ptr 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 threads; + for (int i = 0; i < 8; ++i) { + threads.push_back (new thread (boost::bind (do_remote_encode, frame, &description, locally_encoded))); + } + + for (list::iterator i = threads.begin(); i != threads.end(); ++i) { + (*i)->join (); + } + + for (list::iterator i = threads.begin(); i != threads.end(); ++i) { + delete *i; + } +} + diff --git a/test/dcp_test.cc b/test/dcp_test.cc new file mode 100644 index 000000000..9312a2067 --- /dev/null +++ b/test/dcp_test.cc @@ -0,0 +1,67 @@ +/* + Copyright (C) 2012 Carl Hetherington + + 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 = 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 = new_test_film ("make_dcp_with_range_test"); + film->set_name ("test_film3"); + film->set_content ("../../../test/test.mp4"); + film->examine_content (); + film->set_format (Format::from_nickname ("Flat")); + film->set_dcp_content_type (DCPContentType::from_pretty_name ("Test")); + film->set_trim_end (42); + film->make_dcp (); + + while (JobManager::instance()->work_to_do() && !JobManager::instance()->errors()) { + dvdomatic_sleep (1); + } + + BOOST_CHECK_EQUAL (JobManager::instance()->errors(), false); +} + diff --git a/test/film_metadata_test.cc b/test/film_metadata_test.cc new file mode 100644 index 000000000..0b4495b48 --- /dev/null +++ b/test/film_metadata_test.cc @@ -0,0 +1,76 @@ +/* + Copyright (C) 2013 Carl Hetherington + + 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 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 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 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 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 index 000000000..02dfa0553 --- /dev/null +++ b/test/film_test.cc @@ -0,0 +1,30 @@ +/* + Copyright (C) 2012 Carl Hetherington + + 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 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 index 000000000..b150738a4 --- /dev/null +++ b/test/format_test.cc @@ -0,0 +1,61 @@ +/* + Copyright (C) 2012 Carl Hetherington + + 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 (new Film (test_film_dir ("scaling_test").string(), false)); + + /* 4:3 ratio */ + film->set_size (libdcp::Size (320, 240)); + + /* This format should preserve aspect ratio of the source */ + Format const * format = Format::from_id ("var-185"); + + /* We should have enough padding that the result is 4:3, + which would be 1440 pixels. + */ + BOOST_CHECK_EQUAL (format->dcp_padding (film), (1998 - 1440) / 2); + + /* This crops it to 1.291666667 */ + film->set_left_crop (5); + film->set_right_crop (5); + + /* We should now have enough padding that the result is 1.29166667, + which would be 1395 pixels. + */ + BOOST_CHECK_EQUAL (format->dcp_padding (film), rint ((1998 - 1395) / 2.0)); +} + diff --git a/test/frame_rate_test.cc b/test/frame_rate_test.cc new file mode 100644 index 000000000..00700656e --- /dev/null +++ b/test/frame_rate_test.cc @@ -0,0 +1,211 @@ +/* + Copyright (C) 2012 Carl Hetherington + + 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 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 afr; + afr.push_back (24); + afr.push_back (25); + afr.push_back (30); + Config::instance()->set_allowed_dcp_frame_rates (afr); + + shared_ptr 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 (new FFmpegAudioStream ("a", 42, 48000, 0))); + BOOST_CHECK_EQUAL (f->target_audio_sample_rate(), 48000); + + f->set_content_audio_stream (shared_ptr (new FFmpegAudioStream ("a", 42, 44100, 0))); + BOOST_CHECK_EQUAL (f->target_audio_sample_rate(), 48000); + + f->set_content_audio_stream (shared_ptr (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 (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 (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 (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 (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 (new FFmpegAudioStream ("a", 42, 16000, 0))); + /* The FrameRateConversion within target_audio_sample_rate should choose to double-up + the 14.99 fps video to 30 and then run it slow at 25. + */ + BOOST_CHECK_EQUAL (f->target_audio_sample_rate(), rint (48000 * 2 * 14.99 / 25)); +} + diff --git a/test/image_test.cc b/test/image_test.cc new file mode 100644 index 000000000..a9ec3042a --- /dev/null +++ b/test/image_test.cc @@ -0,0 +1,99 @@ + +BOOST_AUTO_TEST_CASE (aligned_image_test) +{ + SimpleImage* s = new SimpleImage (PIX_FMT_RGB24, libdcp::Size (50, 50), true); + BOOST_CHECK_EQUAL (s->components(), 1); + /* 160 is 150 aligned to the nearest 32 bytes */ + BOOST_CHECK_EQUAL (s->stride()[0], 160); + BOOST_CHECK_EQUAL (s->line_size()[0], 150); + BOOST_CHECK (s->data()[0]); + BOOST_CHECK (!s->data()[1]); + BOOST_CHECK (!s->data()[2]); + BOOST_CHECK (!s->data()[3]); + + /* copy constructor */ + SimpleImage* t = new SimpleImage (*s); + BOOST_CHECK_EQUAL (t->components(), 1); + BOOST_CHECK_EQUAL (t->stride()[0], 160); + BOOST_CHECK_EQUAL (t->line_size()[0], 150); + BOOST_CHECK (t->data()[0]); + BOOST_CHECK (!t->data()[1]); + BOOST_CHECK (!t->data()[2]); + BOOST_CHECK (!t->data()[3]); + BOOST_CHECK (t->data() != s->data()); + BOOST_CHECK (t->data()[0] != s->data()[0]); + BOOST_CHECK (t->line_size() != s->line_size()); + BOOST_CHECK (t->line_size()[0] == s->line_size()[0]); + BOOST_CHECK (t->stride() != s->stride()); + BOOST_CHECK (t->stride()[0] == s->stride()[0]); + + /* assignment operator */ + SimpleImage* u = new SimpleImage (PIX_FMT_YUV422P, libdcp::Size (150, 150), false); + *u = *s; + BOOST_CHECK_EQUAL (u->components(), 1); + BOOST_CHECK_EQUAL (u->stride()[0], 160); + BOOST_CHECK_EQUAL (u->line_size()[0], 150); + BOOST_CHECK (u->data()[0]); + BOOST_CHECK (!u->data()[1]); + BOOST_CHECK (!u->data()[2]); + BOOST_CHECK (!u->data()[3]); + BOOST_CHECK (u->data() != s->data()); + BOOST_CHECK (u->data()[0] != s->data()[0]); + BOOST_CHECK (u->line_size() != s->line_size()); + BOOST_CHECK (u->line_size()[0] == s->line_size()[0]); + BOOST_CHECK (u->stride() != s->stride()); + BOOST_CHECK (u->stride()[0] == s->stride()[0]); + + delete s; + delete t; + delete u; +} + +BOOST_AUTO_TEST_CASE (compact_image_test) +{ + SimpleImage* s = new SimpleImage (PIX_FMT_RGB24, libdcp::Size (50, 50), false); + BOOST_CHECK_EQUAL (s->components(), 1); + BOOST_CHECK_EQUAL (s->stride()[0], 50 * 3); + BOOST_CHECK_EQUAL (s->line_size()[0], 50 * 3); + BOOST_CHECK (s->data()[0]); + BOOST_CHECK (!s->data()[1]); + BOOST_CHECK (!s->data()[2]); + BOOST_CHECK (!s->data()[3]); + + /* copy constructor */ + SimpleImage* t = new SimpleImage (*s); + BOOST_CHECK_EQUAL (t->components(), 1); + BOOST_CHECK_EQUAL (t->stride()[0], 50 * 3); + BOOST_CHECK_EQUAL (t->line_size()[0], 50 * 3); + BOOST_CHECK (t->data()[0]); + BOOST_CHECK (!t->data()[1]); + BOOST_CHECK (!t->data()[2]); + BOOST_CHECK (!t->data()[3]); + BOOST_CHECK (t->data() != s->data()); + BOOST_CHECK (t->data()[0] != s->data()[0]); + BOOST_CHECK (t->line_size() != s->line_size()); + BOOST_CHECK (t->line_size()[0] == s->line_size()[0]); + BOOST_CHECK (t->stride() != s->stride()); + BOOST_CHECK (t->stride()[0] == s->stride()[0]); + + /* assignment operator */ + SimpleImage* u = new SimpleImage (PIX_FMT_YUV422P, libdcp::Size (150, 150), true); + *u = *s; + BOOST_CHECK_EQUAL (u->components(), 1); + BOOST_CHECK_EQUAL (u->stride()[0], 50 * 3); + BOOST_CHECK_EQUAL (u->line_size()[0], 50 * 3); + BOOST_CHECK (u->data()[0]); + BOOST_CHECK (!u->data()[1]); + BOOST_CHECK (!u->data()[2]); + BOOST_CHECK (!u->data()[3]); + BOOST_CHECK (u->data() != s->data()); + BOOST_CHECK (u->data()[0] != s->data()[0]); + BOOST_CHECK (u->line_size() != s->line_size()); + BOOST_CHECK (u->line_size()[0] == s->line_size()[0]); + BOOST_CHECK (u->stride() != s->stride()); + BOOST_CHECK (u->stride()[0] == s->stride()[0]); + + delete s; + delete t; + delete u; +} diff --git a/test/job_test.cc b/test/job_test.cc new file mode 100644 index 000000000..247d4f756 --- /dev/null +++ b/test/job_test.cc @@ -0,0 +1,64 @@ +/* + Copyright (C) 2012 Carl Hetherington + + 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 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 f; + + /* Single job */ + shared_ptr a (new TestJob (f)); + + JobManager::instance()->add (a); + dvdomatic_sleep (1); + BOOST_CHECK_EQUAL (a->running (), true); + a->set_finished_ok (); + dvdomatic_sleep (2); + BOOST_CHECK_EQUAL (a->finished_ok(), true); +} diff --git a/test/make_black_test.cc b/test/make_black_test.cc new file mode 100644 index 000000000..76f38d676 --- /dev/null +++ b/test/make_black_test.cc @@ -0,0 +1,55 @@ +/* + Copyright (C) 2012 Carl Hetherington + + 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 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::const_iterator i = pix_fmts.begin(); i != pix_fmts.end(); ++i) { + boost::shared_ptr foo (new SimpleImage (*i, in_size, true)); + foo->make_black (); + boost::shared_ptr bar = foo->scale_and_convert_to_rgb (out_size, 0, Scaler::from_id ("bicubic"), true); + + uint8_t* p = bar->data()[0]; + for (int y = 0; y < bar->size().height; ++y) { + uint8_t* q = p; + for (int x = 0; x < bar->line_size()[0]; ++x) { + BOOST_CHECK_EQUAL (*q++, 0); + } + p += bar->stride()[0]; + } + + ++N; + } +} diff --git a/test/pixel_formats_test.cc b/test/pixel_formats_test.cc new file mode 100644 index 000000000..e0f6c4373 --- /dev/null +++ b/test/pixel_formats_test.cc @@ -0,0 +1,77 @@ +/* + Copyright (C) 2013 Carl Hetherington + + 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 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::iterator i = cases.begin(); i != cases.end(); ++i) { + AVFrame* f = av_frame_alloc (); + f->width = 640; + f->height = 480; + f->format = static_cast (i->format); + FrameImage t (f); + BOOST_CHECK_EQUAL(t.components(), i->components); + BOOST_CHECK_EQUAL(t.lines(0), i->lines[0]); + BOOST_CHECK_EQUAL(t.lines(1), i->lines[1]); + BOOST_CHECK_EQUAL(t.lines(2), i->lines[2]); + BOOST_CHECK_EQUAL(t.bytes_per_pixel(0), i->bpp[0]); + BOOST_CHECK_EQUAL(t.bytes_per_pixel(1), i->bpp[1]); + BOOST_CHECK_EQUAL(t.bytes_per_pixel(2), i->bpp[2]); + } +} diff --git a/test/stream_test.cc b/test/stream_test.cc new file mode 100644 index 000000000..b467dc9a2 --- /dev/null +++ b/test/stream_test.cc @@ -0,0 +1,52 @@ +/* + Copyright (C) 2013 Carl Hetherington + + 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 (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 (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 (1)); + BOOST_CHECK_EQUAL (s.id(), 5); + BOOST_CHECK_EQUAL (s.name(), "a b c"); + + shared_ptr ff = audio_stream_factory ("ffmpeg 4 44100 1 hello there world", boost::optional (1)); + shared_ptr cff = dynamic_pointer_cast (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 fe = audio_stream_factory ("external 44100 1", boost::optional (1)); + BOOST_CHECK_EQUAL (fe->sample_rate(), 44100); + BOOST_CHECK_EQUAL (fe->channel_layout(), 1); + BOOST_CHECK_EQUAL (fe->to_string(), "external 44100 1"); +} + diff --git a/test/test.cc b/test/test.cc index 11ca56031..91c876412 100644 --- a/test/test.cc +++ b/test/test.cc @@ -52,14 +52,20 @@ using boost::shared_ptr; using boost::thread; using boost::dynamic_pointer_cast; -void -setup_test_config () +struct TestConfig { - Config::instance()->set_num_local_encoding_threads (1); - Config::instance()->set_servers (vector ()); - 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 ()); + 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,819 +88,16 @@ new_test_film (string name) return shared_ptr (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 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::const_iterator i = pix_fmts.begin(); i != pix_fmts.end(); ++i) { - boost::shared_ptr foo (new SimpleImage (*i, in_size, true)); - foo->make_black (); - boost::shared_ptr 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; - } -} - - -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) -{ - list 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, 2, 2 )); - cases.push_back(Case(AV_PIX_FMT_YUV444P, 3, 480, 480, 480, 3, 3, 3 )); - cases.push_back(Case(AV_PIX_FMT_YUV444P9BE, 3, 480, 480, 480, 6, 6, 6 )); - cases.push_back(Case(AV_PIX_FMT_YUV444P9LE, 3, 480, 480, 480, 6, 6, 6 )); - cases.push_back(Case(AV_PIX_FMT_YUV444P10BE, 3, 480, 480, 480, 6, 6, 6 )); - cases.push_back(Case(AV_PIX_FMT_YUV444P10LE, 3, 480, 480, 480, 6, 6, 6 )); - - for (list::iterator i = cases.begin(); i != cases.end(); ++i) { - AVFrame* f = av_frame_alloc (); - f->width = 640; - f->height = 480; - f->format = static_cast (i->format); - FrameImage 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]); - } -} - -shared_ptr trimmer_test_last_video; -shared_ptr trimmer_test_last_audio; - -void -trimmer_test_video_helper (shared_ptr image, bool, shared_ptr) -{ - trimmer_test_last_video = image; -} - -void -trimmer_test_audio_helper (shared_ptr audio) -{ - trimmer_test_last_audio = audio; -} - -BOOST_AUTO_TEST_CASE (trimmer_passthrough_test) -{ - Trimmer trimmer (shared_ptr (), 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 video (new SimpleImage (PIX_FMT_RGB24, libdcp::Size (1998, 1080), true)); - shared_ptr audio (new AudioBuffers (6, 42 * 1920)); - - trimmer.process_video (video, false, shared_ptr ()); - 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 (), 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 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 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 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 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 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 (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 (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 (1)); - BOOST_CHECK_EQUAL (s.id(), 5); - BOOST_CHECK_EQUAL (s.name(), "a b c"); - - shared_ptr ff = audio_stream_factory ("ffmpeg 4 44100 1 hello there world", boost::optional (1)); - shared_ptr cff = dynamic_pointer_cast (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 fe = audio_stream_factory ("external 44100 1", boost::optional (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 (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 b = split_at_spaces_considering_quotes (t); - vector::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 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 frame, ServerDescription* description, shared_ptr locally_encoded) -{ - shared_ptr 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 (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 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 (new Subtitle (Position (50, 60), sub_image)); - - shared_ptr log (new FileLog ("build/test/client_server_test.log")); - - shared_ptr frame ( - new DCPVideoFrame ( - image, - subtitle, - libdcp::Size (1998, 1080), - 0, - 0, - 1, - Scaler::from_id ("bicubic"), - 0, - 24, - "", - 0, - 200000000, - log - ) - ); - - shared_ptr 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 threads; - for (int i = 0; i < 8; ++i) { - threads.push_back (new thread (boost::bind (do_remote_encode, frame, &description, locally_encoded))); - } - - for (list::iterator i = threads.begin(); i != threads.end(); ++i) { - (*i)->join (); - } - - for (list::iterator i = threads.begin(); i != threads.end(); ++i) { - delete *i; - } -} - -BOOST_AUTO_TEST_CASE (make_dcp_test) -{ - shared_ptr 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 = 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 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 afr; - afr.push_back (24); - afr.push_back (25); - afr.push_back (30); - Config::instance()->set_allowed_dcp_frame_rates (afr); - - shared_ptr 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 (new FFmpegAudioStream ("a", 42, 48000, 0))); - BOOST_CHECK_EQUAL (f->target_audio_sample_rate(), 48000); - - f->set_content_audio_stream (shared_ptr (new FFmpegAudioStream ("a", 42, 44100, 0))); - BOOST_CHECK_EQUAL (f->target_audio_sample_rate(), 48000); - - f->set_content_audio_stream (shared_ptr (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 (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 (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 (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 (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 (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 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 f; - - /* Single job */ - shared_ptr a (new TestJob (f)); - - JobManager::instance()->add (a); - dvdomatic_sleep (1); - BOOST_CHECK_EQUAL (a->running (), true); - a->set_finished_ok (); - dvdomatic_sleep (2); - BOOST_CHECK_EQUAL (a->finished_ok(), true); -} - -BOOST_AUTO_TEST_CASE (compact_image_test) -{ - SimpleImage* s = new SimpleImage (PIX_FMT_RGB24, libdcp::Size (50, 50), false); - BOOST_CHECK_EQUAL (s->components(), 1); - BOOST_CHECK_EQUAL (s->stride()[0], 50 * 3); - BOOST_CHECK_EQUAL (s->line_size()[0], 50 * 3); - BOOST_CHECK (s->data()[0]); - BOOST_CHECK (!s->data()[1]); - BOOST_CHECK (!s->data()[2]); - BOOST_CHECK (!s->data()[3]); - - /* copy constructor */ - SimpleImage* t = new SimpleImage (*s); - BOOST_CHECK_EQUAL (t->components(), 1); - BOOST_CHECK_EQUAL (t->stride()[0], 50 * 3); - BOOST_CHECK_EQUAL (t->line_size()[0], 50 * 3); - BOOST_CHECK (t->data()[0]); - BOOST_CHECK (!t->data()[1]); - BOOST_CHECK (!t->data()[2]); - BOOST_CHECK (!t->data()[3]); - BOOST_CHECK (t->data() != s->data()); - BOOST_CHECK (t->data()[0] != s->data()[0]); - BOOST_CHECK (t->line_size() != s->line_size()); - BOOST_CHECK (t->line_size()[0] == s->line_size()[0]); - BOOST_CHECK (t->stride() != s->stride()); - BOOST_CHECK (t->stride()[0] == s->stride()[0]); - - /* assignment operator */ - SimpleImage* u = new SimpleImage (PIX_FMT_YUV422P, libdcp::Size (150, 150), true); - *u = *s; - BOOST_CHECK_EQUAL (u->components(), 1); - BOOST_CHECK_EQUAL (u->stride()[0], 50 * 3); - BOOST_CHECK_EQUAL (u->line_size()[0], 50 * 3); - BOOST_CHECK (u->data()[0]); - BOOST_CHECK (!u->data()[1]); - BOOST_CHECK (!u->data()[2]); - BOOST_CHECK (!u->data()[3]); - BOOST_CHECK (u->data() != s->data()); - BOOST_CHECK (u->data()[0] != s->data()[0]); - BOOST_CHECK (u->line_size() != s->line_size()); - BOOST_CHECK (u->line_size()[0] == s->line_size()[0]); - BOOST_CHECK (u->stride() != s->stride()); - BOOST_CHECK (u->stride()[0] == s->stride()[0]); - - delete s; - delete t; - delete u; -} - -BOOST_AUTO_TEST_CASE (aligned_image_test) -{ - SimpleImage* s = new SimpleImage (PIX_FMT_RGB24, libdcp::Size (50, 50), true); - BOOST_CHECK_EQUAL (s->components(), 1); - /* 160 is 150 aligned to the nearest 32 bytes */ - BOOST_CHECK_EQUAL (s->stride()[0], 160); - BOOST_CHECK_EQUAL (s->line_size()[0], 150); - BOOST_CHECK (s->data()[0]); - BOOST_CHECK (!s->data()[1]); - BOOST_CHECK (!s->data()[2]); - BOOST_CHECK (!s->data()[3]); - - /* copy constructor */ - SimpleImage* t = new SimpleImage (*s); - BOOST_CHECK_EQUAL (t->components(), 1); - BOOST_CHECK_EQUAL (t->stride()[0], 160); - BOOST_CHECK_EQUAL (t->line_size()[0], 150); - BOOST_CHECK (t->data()[0]); - BOOST_CHECK (!t->data()[1]); - BOOST_CHECK (!t->data()[2]); - BOOST_CHECK (!t->data()[3]); - BOOST_CHECK (t->data() != s->data()); - BOOST_CHECK (t->data()[0] != s->data()[0]); - BOOST_CHECK (t->line_size() != s->line_size()); - BOOST_CHECK (t->line_size()[0] == s->line_size()[0]); - BOOST_CHECK (t->stride() != s->stride()); - BOOST_CHECK (t->stride()[0] == s->stride()[0]); - - /* assignment operator */ - SimpleImage* u = new SimpleImage (PIX_FMT_YUV422P, libdcp::Size (150, 150), false); - *u = *s; - BOOST_CHECK_EQUAL (u->components(), 1); - BOOST_CHECK_EQUAL (u->stride()[0], 160); - BOOST_CHECK_EQUAL (u->line_size()[0], 150); - BOOST_CHECK (u->data()[0]); - BOOST_CHECK (!u->data()[1]); - BOOST_CHECK (!u->data()[2]); - BOOST_CHECK (!u->data()[3]); - BOOST_CHECK (u->data() != s->data()); - BOOST_CHECK (u->data()[0] != s->data()[0]); - BOOST_CHECK (u->line_size() != s->line_size()); - BOOST_CHECK (u->line_size()[0] == s->line_size()[0]); - BOOST_CHECK (u->stride() != s->stride()); - BOOST_CHECK (u->stride()[0] == s->stride()[0]); - - delete s; - delete t; - delete u; -} - +#include "pixel_formats_test.cc" +#include "make_black_test.cc" +#include "trimmer_test.cc" +#include "film_metadata_test.cc" +#include "stream_test.cc" +#include "format_test.cc" +#include "util_test.cc" +#include "film_test.cc" +#include "dcp_test.cc" +#include "frame_rate_test.cc" +#include "job_test.cc" +#include "client_server_test.cc" +#include "image_test.cc" diff --git a/test/trimmer_test.cc b/test/trimmer_test.cc new file mode 100644 index 000000000..605f7d1b2 --- /dev/null +++ b/test/trimmer_test.cc @@ -0,0 +1,95 @@ +/* + Copyright (C) 2012 Carl Hetherington + + 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 trimmer_test_last_video; +shared_ptr trimmer_test_last_audio; + +void +trimmer_test_video_helper (shared_ptr image, bool, shared_ptr) +{ + trimmer_test_last_video = image; +} + +void +trimmer_test_audio_helper (shared_ptr audio) +{ + trimmer_test_last_audio = audio; +} + +BOOST_AUTO_TEST_CASE (trimmer_passthrough_test) +{ + Trimmer trimmer (shared_ptr (), 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 video (new SimpleImage (PIX_FMT_RGB24, libdcp::Size (1998, 1080), true)); + shared_ptr audio (new AudioBuffers (6, 42 * 1920)); + + trimmer.process_video (video, false, shared_ptr ()); + 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 (), 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 audio (new AudioBuffers (6, 21 * 1920)); + trimmer.process_audio (audio); + BOOST_CHECK (trimmer_test_last_audio == 0); + + /* 42 more video frames-worth, 4 should be stripped from the start */ + audio.reset (new AudioBuffers (6, 42 * 1920)); + trimmer.process_audio (audio); + BOOST_CHECK (trimmer_test_last_audio); + BOOST_CHECK_EQUAL (trimmer_test_last_audio->frames(), 38 * 1920); + + /* 42 more video frames-worth, should be kept as-is */ + trimmer_test_last_audio.reset (); + audio.reset (new AudioBuffers (6, 42 * 1920)); + trimmer.process_audio (audio); + BOOST_CHECK (trimmer_test_last_audio); + BOOST_CHECK_EQUAL (trimmer_test_last_audio->frames(), 42 * 1920); + + /* 25 more video frames-worth, 5 should be trimmed from the end */ + trimmer_test_last_audio.reset (); + audio.reset (new AudioBuffers (6, 25 * 1920)); + trimmer.process_audio (audio); + BOOST_CHECK (trimmer_test_last_audio); + BOOST_CHECK_EQUAL (trimmer_test_last_audio->frames(), 20 * 1920); + + /* Now some more; all should be trimmed */ + trimmer_test_last_audio.reset (); + audio.reset (new AudioBuffers (6, 100 * 1920)); + trimmer.process_audio (audio); + BOOST_CHECK (trimmer_test_last_audio == 0); +} + + diff --git a/test/util_test.cc b/test/util_test.cc new file mode 100644 index 000000000..18c24ac3a --- /dev/null +++ b/test/util_test.cc @@ -0,0 +1,43 @@ +/* + Copyright (C) 2012 Carl Hetherington + + 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 b = split_at_spaces_considering_quotes (t); + vector::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); +} -- cgit v1.2.3 From 36fec15b78e6d5017c9f631bd2e828aa9ca91aa1 Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Wed, 1 May 2013 22:24:08 +0100 Subject: Missing format for make_black. --- src/lib/image.cc | 12 +++++++++--- test/make_black_test.cc | 1 + 2 files changed, 10 insertions(+), 3 deletions(-) (limited to 'src/lib') diff --git a/src/lib/image.cc b/src/lib/image.cc index 0b887ea62..b97291585 100644 --- a/src/lib/image.cc +++ b/src/lib/image.cc @@ -274,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: @@ -304,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; diff --git a/test/make_black_test.cc b/test/make_black_test.cc index 76f38d676..3c0b979ff 100644 --- a/test/make_black_test.cc +++ b/test/make_black_test.cc @@ -29,6 +29,7 @@ BOOST_AUTO_TEST_CASE (make_black_test) 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); -- cgit v1.2.3 From fdd63a4c9925f0339089dce3a52f0d6ed0d97880 Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Thu, 2 May 2013 22:15:32 +0100 Subject: Use newer format to specify filter graphs; don't filter unless necessary; fix tiny memory leak. --- src/lib/ffmpeg_decoder.cc | 45 +++++++++++++++++++++++++++------------------ src/lib/filter_graph.cc | 19 +++++++++---------- src/lib/image.cc | 8 ++++++-- src/lib/image.h | 3 ++- test/pixel_formats_test.cc | 2 +- 5 files changed, 45 insertions(+), 32 deletions(-) (limited to 'src/lib') diff --git a/src/lib/ffmpeg_decoder.cc b/src/lib/ffmpeg_decoder.cc index 8e09810cb..cd68e5294 100644 --- a/src/lib/ffmpeg_decoder.cc +++ b/src/lib/ffmpeg_decoder.cc @@ -500,32 +500,41 @@ FFmpegDecoder::set_subtitle_stream (shared_ptr 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 graph; - - list >::iterator i = _filter_graphs.begin(); - while (i != _filter_graphs.end() && !(*i)->can_process (libdcp::Size (_frame->width, _frame->height), (AVPixelFormat) _frame->format)) { - ++i; + if (_film->crop() == Crop() && _film->filters().empty()) { + /* No filter graph needed; just emit */ + emit_video (shared_ptr (new FrameImage (_frame, false)), false, bet * av_q2d (_format_context->streams[_video_stream]->time_base)); + return; } + + shared_ptr graph; - 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 >::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; + } } list > images = graph->process (_frame); for (list >::iterator i = images.begin(); i != images.end(); ++i) { - int64_t const bet = av_frame_get_best_effort_timestamp (_frame); - if (bet != AV_NOPTS_VALUE) { - emit_video (*i, false, bet * av_q2d (_format_context->streams[_video_stream]->time_base)); - } else { - _film->log()->log ("Dropping frame without PTS"); - } + emit_video (*i, false, bet * av_q2d (_format_context->streams[_video_stream]->time_base)); } } diff --git a/src/lib/filter_graph.cc b/src/lib/filter_graph.cc index f0c49b37c..2624bc4d7 100644 --- a/src/lib/filter_graph.cc +++ b/src/lib/filter_graph.cc @@ -79,17 +79,14 @@ FilterGraph::FilterGraph (shared_ptr film, FFmpegDecoder* decoder, libdcp: } 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")); } @@ -103,6 +100,8 @@ FilterGraph::FilterGraph (shared_ptr 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; @@ -133,7 +132,7 @@ list > FilterGraph::process (AVFrame* frame) { list > images; - + if (av_buffersrc_write_frame (_buffer_src_context, frame) < 0) { throw DecodeError (N_("could not push buffer into filter chain.")); } @@ -146,7 +145,7 @@ FilterGraph::process (AVFrame* frame) } /* This takes ownership of the AVFrame */ - images.push_back (shared_ptr (new FrameImage (frame))); + images.push_back (shared_ptr (new FrameImage (frame, true))); } return images; diff --git a/src/lib/image.cc b/src/lib/image.cc index b97291585..1768be924 100644 --- a/src/lib/image.cc +++ b/src/lib/image.cc @@ -576,9 +576,10 @@ SimpleImage::aligned () const return _aligned; } -FrameImage::FrameImage (AVFrame* frame) +FrameImage::FrameImage (AVFrame* frame, bool own) : Image (static_cast (frame->format)) , _frame (frame) + , _own (own) { _line_size = (int *) av_malloc (4 * sizeof (int)); _line_size[0] = _line_size[1] = _line_size[2] = _line_size[3] = 0; @@ -590,7 +591,10 @@ FrameImage::FrameImage (AVFrame* frame) FrameImage::~FrameImage () { - av_frame_free (&_frame); + if (_own) { + av_frame_free (&_frame); + } + av_free (_line_size); } diff --git a/src/lib/image.h b/src/lib/image.h index 39d84fcd4..16fbd28c2 100644 --- a/src/lib/image.h +++ b/src/lib/image.h @@ -106,7 +106,7 @@ private: class FrameImage : public Image { public: - FrameImage (AVFrame *); + FrameImage (AVFrame *, bool); ~FrameImage (); uint8_t ** data () const; @@ -121,6 +121,7 @@ private: FrameImage& operator= (FrameImage const &); AVFrame* _frame; + bool _own; int* _line_size; }; diff --git a/test/pixel_formats_test.cc b/test/pixel_formats_test.cc index e0f6c4373..e8ad725ff 100644 --- a/test/pixel_formats_test.cc +++ b/test/pixel_formats_test.cc @@ -65,7 +65,7 @@ BOOST_AUTO_TEST_CASE (pixel_formats_test) f->width = 640; f->height = 480; f->format = static_cast (i->format); - FrameImage t (f); + FrameImage t (f, true); 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]); -- cgit v1.2.3 From 3fc3aad8735903ced3dae65f764eb33e3f5b3f11 Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Fri, 3 May 2013 10:01:36 +0100 Subject: Try to fix the filter / AVFrame ownership. --- src/lib/ffmpeg_decoder.cc | 8 +---- src/lib/filter_graph.cc | 42 +++++++++++++++++++------ src/lib/filter_graph.h | 27 ++++++++++++++-- src/lib/image.cc | 77 +++++++++++++--------------------------------- src/lib/image.h | 27 +--------------- test/pixel_formats_test.cc | 2 +- 6 files changed, 80 insertions(+), 103 deletions(-) (limited to 'src/lib') diff --git a/src/lib/ffmpeg_decoder.cc b/src/lib/ffmpeg_decoder.cc index cd68e5294..982139515 100644 --- a/src/lib/ffmpeg_decoder.cc +++ b/src/lib/ffmpeg_decoder.cc @@ -506,12 +506,6 @@ FFmpegDecoder::filter_and_emit_video () return; } - if (_film->crop() == Crop() && _film->filters().empty()) { - /* No filter graph needed; just emit */ - emit_video (shared_ptr (new FrameImage (_frame, false)), false, bet * av_q2d (_format_context->streams[_video_stream]->time_base)); - return; - } - shared_ptr graph; { @@ -523,7 +517,7 @@ FFmpegDecoder::filter_and_emit_video () } if (i == _filter_graphs.end ()) { - graph.reset (new FilterGraph (_film, this, libdcp::Size (_frame->width, _frame->height), (AVPixelFormat) _frame->format)); + 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 { diff --git a/src/lib/filter_graph.cc b/src/lib/filter_graph.cc index 2624bc4d7..b0427a23d 100644 --- a/src/lib/filter_graph.cc +++ b/src/lib/filter_graph.cc @@ -44,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, FFmpegDecoder* decoder, libdcp::Size s, AVPixelFormat p) +FFmpegFilterGraph::FFmpegFilterGraph (shared_ptr 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_(","); @@ -125,11 +127,16 @@ FilterGraph::FilterGraph (shared_ptr 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 > -FilterGraph::process (AVFrame* frame) +FFmpegFilterGraph::process (AVFrame* frame) { list > images; @@ -138,14 +145,11 @@ FilterGraph::process (AVFrame* frame) } while (1) { - AVFrame* frame = av_frame_alloc (); - if (av_buffersink_get_frame (_buffer_sink_context, frame) < 0) { - av_frame_free (&frame); + if (av_buffersink_get_frame (_buffer_sink_context, _frame) < 0) { break; } - /* This takes ownership of the AVFrame */ - images.push_back (shared_ptr (new FrameImage (frame, true))); + images.push_back (shared_ptr (new SimpleImage (_frame))); } return images; @@ -156,7 +160,25 @@ FilterGraph::process (AVFrame* 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 > +EmptyFilterGraph::process (AVFrame* frame) +{ + list > im; + im.push_back (shared_ptr (new SimpleImage (frame))); + return im; +} + +shared_ptr +filter_graph_factory (shared_ptr film, FFmpegDecoder* decoder, libdcp::Size size, AVPixelFormat pixel_format) +{ + if (film->filters().empty() && film->crop() == Crop()) { + return shared_ptr (new EmptyFilterGraph); + } + + return shared_ptr (new FFmpegFilterGraph (film, decoder, size, pixel_format)); +} diff --git a/src/lib/filter_graph.h b/src/lib/filter_graph.h index 249b89851..2138943e4 100644 --- a/src/lib/filter_graph.h +++ b/src/lib/filter_graph.h @@ -30,13 +30,31 @@ class Image; class VideoFilter; class FFmpegDecoder; -/** @class FilterGraph +class FilterGraph +{ +public: + virtual bool can_process (libdcp::Size, AVPixelFormat) const = 0; + virtual std::list > process (AVFrame *) = 0; +}; + +class EmptyFilterGraph : public FilterGraph +{ +public: + bool can_process (libdcp::Size, AVPixelFormat) const { + return true; + } + + std::list > process (AVFrame *); +}; + +/** @class FFmpegFilterGraph * @brief A graph of FFmpeg filters. */ -class FilterGraph +class FFmpegFilterGraph : public FilterGraph { public: - FilterGraph (boost::shared_ptr film, FFmpegDecoder* decoder, libdcp::Size s, AVPixelFormat p); + FFmpegFilterGraph (boost::shared_ptr film, FFmpegDecoder* decoder, libdcp::Size s, AVPixelFormat p); + ~FFmpegFilterGraph (); bool can_process (libdcp::Size s, AVPixelFormat p) const; std::list > process (AVFrame * frame); @@ -46,6 +64,9 @@ private: 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 filter_graph_factory (boost::shared_ptr, FFmpegDecoder *, libdcp::Size, AVPixelFormat); + #endif diff --git a/src/lib/image.cc b/src/lib/image.cc index 1768be924..b166dfac6 100644 --- a/src/lib/image.cc +++ b/src/lib/image.cc @@ -469,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) { @@ -486,6 +485,25 @@ SimpleImage::SimpleImage (SimpleImage const & other) } } +SimpleImage::SimpleImage (AVFrame* frame) + : Image (static_cast (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 other) : Image (*other.get()) { @@ -576,59 +594,6 @@ SimpleImage::aligned () const return _aligned; } -FrameImage::FrameImage (AVFrame* frame, bool own) - : Image (static_cast (frame->format)) - , _frame (frame) - , _own (own) -{ - _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); - } -} - -FrameImage::~FrameImage () -{ - if (_own) { - av_frame_free (&_frame); - } - - av_free (_line_size); -} - -uint8_t ** -FrameImage::data () const -{ - return _frame->data; -} - -int * -FrameImage::line_size () const -{ - return _line_size; -} - -int * -FrameImage::stride () const -{ - /* AVFrame's `linesize' is what we call `stride' */ - return _frame->linesize; -} - -libdcp::Size -FrameImage::size () const -{ - return libdcp::Size (_frame->width, _frame->height); -} - -bool -FrameImage::aligned () const -{ - return true; -} - RGBPlusAlphaImage::RGBPlusAlphaImage (shared_ptr im) : SimpleImage (im->pixel_format(), im->size(), false) { diff --git a/src/lib/image.h b/src/lib/image.h index 16fbd28c2..70dacfaee 100644 --- a/src/lib/image.h +++ b/src/lib/image.h @@ -34,7 +34,6 @@ extern "C" { #include "util.h" class Scaler; -class RGBFrameImage; class SimpleImage; /** @class Image @@ -100,31 +99,6 @@ private: AVPixelFormat _pixel_format; ///< FFmpeg's way of describing the pixel format of this Image }; -/** @class FrameImage - * @brief An Image that is held in an AVFrame. - */ -class FrameImage : public Image -{ -public: - FrameImage (AVFrame *, bool); - ~FrameImage (); - - uint8_t ** data () const; - int * line_size () const; - int * stride () const; - libdcp::Size size () const; - bool aligned () const; - -private: - /* Not allowed */ - FrameImage (FrameImage const &); - FrameImage& operator= (FrameImage const &); - - AVFrame* _frame; - bool _own; - int* _line_size; -}; - /** @class SimpleImage * @brief An Image for which memory is allocated using a `simple' av_malloc(). */ @@ -132,6 +106,7 @@ class SimpleImage : public Image { public: SimpleImage (AVPixelFormat, libdcp::Size, bool); + SimpleImage (AVFrame *); SimpleImage (SimpleImage const &); SimpleImage (boost::shared_ptr); SimpleImage& operator= (SimpleImage const &); diff --git a/test/pixel_formats_test.cc b/test/pixel_formats_test.cc index e8ad725ff..84f2a33ce 100644 --- a/test/pixel_formats_test.cc +++ b/test/pixel_formats_test.cc @@ -65,7 +65,7 @@ BOOST_AUTO_TEST_CASE (pixel_formats_test) f->width = 640; f->height = 480; f->format = static_cast (i->format); - FrameImage t (f, true); + 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]); -- cgit v1.2.3 From af671aa256ae0d2fa38a6740cea7fc0f6b45b5b8 Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Tue, 7 May 2013 09:48:47 +0100 Subject: Fix typo preventing audio sync fixes when audio follows video. --- src/lib/matcher.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/lib') diff --git a/src/lib/matcher.cc b/src/lib/matcher.cc index 9924c003a..2723f2a2d 100644 --- a/src/lib/matcher.cc +++ b/src/lib/matcher.cc @@ -110,7 +110,7 @@ Matcher::process_audio (boost::shared_ptr b, double t) 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 ()); -- cgit v1.2.3 From 4ff5c21a9c3c2ea8ab3a253ef5658e3ee91e0bea Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Tue, 7 May 2013 10:18:33 +0100 Subject: Fix another bit of incorrect logic (this_is_first_audio was inverted). Also try to add _audio_starts_with_video mode to Matcher. --- src/lib/matcher.cc | 28 ++++++++++++++++++++++------ src/lib/matcher.h | 6 ++++++ 2 files changed, 28 insertions(+), 6 deletions(-) (limited to 'src/lib') diff --git a/src/lib/matcher.cc b/src/lib/matcher.cc index 2723f2a2d..3776d4fbc 100644 --- a/src/lib/matcher.cc +++ b/src/lib/matcher.cc @@ -34,7 +34,6 @@ Matcher::Matcher (shared_ptr log, int sample_rate, float frames_per_second) , _frames_per_second (frames_per_second) , _video_frames (0) , _audio_frames (0) - , _had_first_video (false) , _had_first_audio (false) { @@ -52,8 +51,11 @@ Matcher::process_video (boost::shared_ptr image, bool same, boost:: _first_input = t; } - bool const this_is_first_video = !_had_first_video; - _had_first_video = true; + bool const this_is_first_video = !_first_video; + + if (!_first_video) { + _first_video = t; + } if (this_is_first_video && _had_first_audio) { /* First video since we got audio */ @@ -104,13 +106,13 @@ Matcher::process_audio (boost::shared_ptr b, double t) _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) { + if (!_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 && _first_video) { /* First audio since we got video */ _pending_audio.push_back (AudioRecord (b, t)); fix_start (_first_input.get ()); @@ -145,6 +147,11 @@ Matcher::fix_start (double first_video) match (first_video - _pending_audio.front().time); for (list::iterator i = _pending_audio.begin(); i != _pending_audio.end(); ++i) { + if (_audio_starts_with_video) { + assert (_first_video); + assert (_first_audio); + i->time += _first_video.get() - _first_audio.get(); + } process_audio (i->audio, i->time); } @@ -211,3 +218,12 @@ Matcher::repeat_last_video () ++_video_frames; } +/** Set `audio starts with video' behaviour. If this is enabled, + * audio time stamps are adjusted so that the first audio is synced + * with the first video. + */ +void +Matcher::set_audio_starts_with_video (bool a) +{ + _audio_starts_with_video = a; +} diff --git a/src/lib/matcher.h b/src/lib/matcher.h index 41aa373a4..aab6adad9 100644 --- a/src/lib/matcher.h +++ b/src/lib/matcher.h @@ -25,6 +25,9 @@ class Matcher : public Processor, public TimedAudioSink, public TimedVideoSink, { public: Matcher (boost::shared_ptr log, int sample_rate, float frames_per_second); + + void set_audio_starts_with_video (bool); + void process_video (boost::shared_ptr i, bool, boost::shared_ptr s, double); void process_audio (boost::shared_ptr, double); void process_end (); @@ -55,9 +58,12 @@ private: std::list _pending_audio; boost::optional _first_input; + boost::optional _first_video; boost::shared_ptr _last_image; boost::shared_ptr _last_subtitle; bool _had_first_video; bool _had_first_audio; + + bool _audio_starts_with_video; }; -- cgit v1.2.3 From 213f1b6c74585a16ea48bdeb550dbc6d11bb0f74 Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Tue, 7 May 2013 10:20:51 +0100 Subject: Revert "Fix another bit of incorrect logic (this_is_first_audio was inverted). Also try to add _audio_starts_with_video mode to Matcher." This reverts commit 4ff5c21a9c3c2ea8ab3a253ef5658e3ee91e0bea. --- src/lib/matcher.cc | 28 ++++++---------------------- src/lib/matcher.h | 6 ------ 2 files changed, 6 insertions(+), 28 deletions(-) (limited to 'src/lib') diff --git a/src/lib/matcher.cc b/src/lib/matcher.cc index 3776d4fbc..2723f2a2d 100644 --- a/src/lib/matcher.cc +++ b/src/lib/matcher.cc @@ -34,6 +34,7 @@ Matcher::Matcher (shared_ptr log, int sample_rate, float frames_per_second) , _frames_per_second (frames_per_second) , _video_frames (0) , _audio_frames (0) + , _had_first_video (false) , _had_first_audio (false) { @@ -51,11 +52,8 @@ Matcher::process_video (boost::shared_ptr image, bool same, boost:: _first_input = t; } - bool const this_is_first_video = !_first_video; - - if (!_first_video) { - _first_video = t; - } + bool const this_is_first_video = !_had_first_video; + _had_first_video = true; if (this_is_first_video && _had_first_audio) { /* First video since we got audio */ @@ -106,13 +104,13 @@ Matcher::process_audio (boost::shared_ptr b, double t) _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 (!_first_video) { + 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 && _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 ()); @@ -147,11 +145,6 @@ Matcher::fix_start (double first_video) match (first_video - _pending_audio.front().time); for (list::iterator i = _pending_audio.begin(); i != _pending_audio.end(); ++i) { - if (_audio_starts_with_video) { - assert (_first_video); - assert (_first_audio); - i->time += _first_video.get() - _first_audio.get(); - } process_audio (i->audio, i->time); } @@ -218,12 +211,3 @@ Matcher::repeat_last_video () ++_video_frames; } -/** Set `audio starts with video' behaviour. If this is enabled, - * audio time stamps are adjusted so that the first audio is synced - * with the first video. - */ -void -Matcher::set_audio_starts_with_video (bool a) -{ - _audio_starts_with_video = a; -} diff --git a/src/lib/matcher.h b/src/lib/matcher.h index aab6adad9..41aa373a4 100644 --- a/src/lib/matcher.h +++ b/src/lib/matcher.h @@ -25,9 +25,6 @@ class Matcher : public Processor, public TimedAudioSink, public TimedVideoSink, { public: Matcher (boost::shared_ptr log, int sample_rate, float frames_per_second); - - void set_audio_starts_with_video (bool); - void process_video (boost::shared_ptr i, bool, boost::shared_ptr s, double); void process_audio (boost::shared_ptr, double); void process_end (); @@ -58,12 +55,9 @@ private: std::list _pending_audio; boost::optional _first_input; - boost::optional _first_video; boost::shared_ptr _last_image; boost::shared_ptr _last_subtitle; bool _had_first_video; bool _had_first_audio; - - bool _audio_starts_with_video; }; -- cgit v1.2.3 From 188a4c094775a0fa8b69722bba7c18ae3e2bb5d3 Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Tue, 7 May 2013 10:21:12 +0100 Subject: Fix more incorrect logic. --- src/lib/matcher.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/lib') diff --git a/src/lib/matcher.cc b/src/lib/matcher.cc index 2723f2a2d..f8d5c5fcd 100644 --- a/src/lib/matcher.cc +++ b/src/lib/matcher.cc @@ -104,7 +104,7 @@ Matcher::process_audio (boost::shared_ptr b, double t) _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) { -- cgit v1.2.3 From 1f4a21c0da3a917a5ff7743a577cc0f210fa86d8 Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Tue, 7 May 2013 21:02:13 +0100 Subject: Stop SndfileDecoder with no audio emitting lots of silence. --- src/lib/sndfile_decoder.cc | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'src/lib') diff --git a/src/lib/sndfile_decoder.cc b/src/lib/sndfile_decoder.cc index fdaf2eeaa..d70478a1b 100644 --- a/src/lib/sndfile_decoder.cc +++ b/src/lib/sndfile_decoder.cc @@ -102,14 +102,20 @@ SndfileDecoder::pass () sf_count_t const block = _audio_stream->sample_rate() / 2; shared_ptr audio (new AudioBuffers (_audio_stream->channels(), block)); sf_count_t const this_time = min (block, _frames - _done); + bool have_sound = false; for (size_t i = 0; i < _sndfiles.size(); ++i) { if (!_sndfiles[i]) { audio->make_silent (i); } else { sf_read_float (_sndfiles[i], audio->data(i), this_time); + have_sound = true; } } + if (!have_sound) { + return true; + } + audio->set_frames (this_time); Audio (audio, double(_done) / _audio_stream->sample_rate()); _done += this_time; -- cgit v1.2.3 From b66a082df05202f0119b16853f04034cf85ec80b Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Tue, 7 May 2013 23:03:06 +0100 Subject: Another attempt to fix matching and confusions about the delay line. --- src/lib/delay_line.cc | 2 +- src/lib/matcher.cc | 83 +++++++++++++++++++++++++++++---------------------- src/lib/matcher.h | 17 ++++++++++- 3 files changed, 64 insertions(+), 38 deletions(-) (limited to 'src/lib') diff --git a/src/lib/delay_line.cc b/src/lib/delay_line.cc index b0180800a..f6af6f9b1 100644 --- a/src/lib/delay_line.cc +++ b/src/lib/delay_line.cc @@ -50,7 +50,7 @@ void DelayLine::process_video (shared_ptr image, bool same, boost::shared_ptr sub, double t) { if (_seconds < 0) { - t += _seconds; + t -= _seconds; } Video (image, same, sub, t); diff --git a/src/lib/matcher.cc b/src/lib/matcher.cc index f8d5c5fcd..4acb82afa 100644 --- a/src/lib/matcher.cc +++ b/src/lib/matcher.cc @@ -48,45 +48,47 @@ Matcher::process_video (boost::shared_ptr 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,12 +97,12 @@ Matcher::process_audio (boost::shared_ptr 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; } @@ -113,9 +115,11 @@ Matcher::process_audio (boost::shared_ptr b, double t) } 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::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::iterator i = _pending_audio.begin(); i != _pending_audio.end(); ++i) { process_audio (i->audio, i->time); diff --git a/src/lib/matcher.h b/src/lib/matcher.h index 41aa373a4..d6778da11 100644 --- a/src/lib/matcher.h +++ b/src/lib/matcher.h @@ -30,7 +30,7 @@ public: void process_end (); private: - void fix_start (double); + void fix_start (); void match (double); void repeat_last_video (); @@ -42,6 +42,20 @@ private: boost::optional _size; boost::optional _channels; + struct VideoRecord { + VideoRecord (boost::shared_ptr i, bool s, boost::shared_ptr u, double t) + : image (i) + , same (s) + , subtitle (u) + , time (t) + {} + + boost::shared_ptr image; + bool same; + boost::shared_ptr subtitle; + double time; + }; + struct AudioRecord { AudioRecord (boost::shared_ptr a, double t) : audio (a) @@ -52,6 +66,7 @@ private: double time; }; + std::list _pending_video; std::list _pending_audio; boost::optional _first_input; -- cgit v1.2.3 From 83742c7e6edcf958e0820abc77c029f4ada4880f Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Wed, 8 May 2013 16:58:19 +0100 Subject: Better fix for still (no sound) DCP crash. --- src/lib/encoder.cc | 4 ++++ src/lib/sndfile_decoder.cc | 6 ------ src/lib/transcoder.cc | 2 +- 3 files changed, 5 insertions(+), 7 deletions(-) (limited to 'src/lib') diff --git a/src/lib/encoder.cc b/src/lib/encoder.cc index cff9899ac..8549962ff 100644 --- a/src/lib/encoder.cc +++ b/src/lib/encoder.cc @@ -296,6 +296,10 @@ Encoder::process_video (shared_ptr image, bool same, boost::shared_ void Encoder::process_audio (shared_ptr data) { + if (!data->frames ()) { + return; + } + #if HAVE_SWRESAMPLE /* Maybe sample-rate convert */ if (_swr_context) { diff --git a/src/lib/sndfile_decoder.cc b/src/lib/sndfile_decoder.cc index d70478a1b..fdaf2eeaa 100644 --- a/src/lib/sndfile_decoder.cc +++ b/src/lib/sndfile_decoder.cc @@ -102,20 +102,14 @@ SndfileDecoder::pass () sf_count_t const block = _audio_stream->sample_rate() / 2; shared_ptr audio (new AudioBuffers (_audio_stream->channels(), block)); sf_count_t const this_time = min (block, _frames - _done); - bool have_sound = false; for (size_t i = 0; i < _sndfiles.size(); ++i) { if (!_sndfiles[i]) { audio->make_silent (i); } else { sf_read_float (_sndfiles[i], audio->data(i), this_time); - have_sound = true; } } - if (!have_sound) { - return true; - } - audio->set_frames (this_time); Audio (audio, double(_done) / _audio_stream->sample_rate()); _done += this_time; diff --git a/src/lib/transcoder.cc b/src/lib/transcoder.cc index faafcaf8b..48cf402d7 100644 --- a/src/lib/transcoder.cc +++ b/src/lib/transcoder.cc @@ -56,7 +56,7 @@ Transcoder::Transcoder (shared_ptr f, DecodeOptions o, Job* j, shared_ptr< assert (_encoder); shared_ptr st = f->audio_stream(); - if (st) { + 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)); -- cgit v1.2.3 From 4fbec16cc17d689cbd1ac17d3f031765c04a53c1 Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Mon, 20 May 2013 12:33:37 +0100 Subject: Fix another crash on stills with no audio. --- src/lib/sndfile_decoder.cc | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'src/lib') diff --git a/src/lib/sndfile_decoder.cc b/src/lib/sndfile_decoder.cc index fdaf2eeaa..7e9e67d0f 100644 --- a/src/lib/sndfile_decoder.cc +++ b/src/lib/sndfile_decoder.cc @@ -96,6 +96,10 @@ SndfileDecoder::SndfileDecoder (shared_ptr f, DecodeOptions o) bool SndfileDecoder::pass () { + if (_audio_streams.empty ()) { + return true; + } + /* Do things in half second blocks as I think there may be limits to what FFmpeg (and in particular the resampler) can cope with. */ -- cgit v1.2.3 From 7d9cf8f7370dea78681b6ceddd869fea5c4980f8 Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Fri, 24 May 2013 13:29:18 +0100 Subject: i18n tweak from Lilian. --- src/lib/po/fr_FR.po | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/lib') diff --git a/src/lib/po/fr_FR.po b/src/lib/po/fr_FR.po index a6a72598f..81f61d8b8 100644 --- a/src/lib/po/fr_FR.po +++ b/src/lib/po/fr_FR.po @@ -8,7 +8,7 @@ msgstr "" "Project-Id-Version: DVD-o-matic FRENCH\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2013-05-09 09:51+0100\n" -"PO-Revision-Date: 2013-05-10 14:33+0100\n" +"PO-Revision-Date: 2013-05-21 10:30+0100\n" "Last-Translator: \n" "Language-Team: \n" "Language: \n" @@ -489,7 +489,7 @@ msgstr "lecture du fichier impossible" #: src/lib/exceptions.cc:44 msgid "could not read from file %1 (%2)" -msgstr "création du dossier distant %1 impossible (%2)" +msgstr "lecture du fichier impossible %1 (%2)" #: src/lib/encoder.cc:137 #: src/lib/encoder.cc:314 -- cgit v1.2.3