Hacks.
authorCarl Hetherington <cth@carlh.net>
Wed, 26 Jun 2013 00:21:21 +0000 (01:21 +0100)
committerCarl Hetherington <cth@carlh.net>
Wed, 26 Jun 2013 00:21:21 +0000 (01:21 +0100)
54 files changed:
doc/design/timing.tex [new file with mode: 0644]
src/lib/audio_content.h
src/lib/audio_decoder.cc
src/lib/audio_decoder.h
src/lib/audio_sink.h [deleted file]
src/lib/audio_source.cc [deleted file]
src/lib/audio_source.h [deleted file]
src/lib/black_decoder.cc
src/lib/black_decoder.h
src/lib/combiner.cc [deleted file]
src/lib/combiner.h [deleted file]
src/lib/decoder.h
src/lib/encoder.cc
src/lib/encoder.h
src/lib/ffmpeg_content.cc
src/lib/ffmpeg_content.h
src/lib/ffmpeg_decoder.cc
src/lib/ffmpeg_decoder.h
src/lib/ffmpeg_examiner.cc
src/lib/ffmpeg_examiner.h
src/lib/image.cc
src/lib/imagemagick_content.cc
src/lib/imagemagick_content.h
src/lib/imagemagick_decoder.cc
src/lib/imagemagick_decoder.h
src/lib/imagemagick_examiner.h
src/lib/matcher.cc [deleted file]
src/lib/matcher.h [deleted file]
src/lib/null_content.h
src/lib/player.cc
src/lib/player.h
src/lib/playlist.h
src/lib/resampler.cc [new file with mode: 0644]
src/lib/resampler.h [new file with mode: 0644]
src/lib/silence_decoder.cc
src/lib/silence_decoder.h
src/lib/sndfile_content.cc
src/lib/sndfile_content.h
src/lib/sndfile_decoder.cc
src/lib/sndfile_decoder.h
src/lib/subtitle.cc
src/lib/transcoder.cc
src/lib/types.h
src/lib/util.cc
src/lib/util.h
src/lib/video_content.cc
src/lib/video_content.h
src/lib/video_decoder.cc
src/lib/video_decoder.h
src/lib/video_examiner.h
src/lib/video_sink.h [deleted file]
src/lib/video_source.cc [deleted file]
src/lib/video_source.h [deleted file]
src/lib/wscript

diff --git a/doc/design/timing.tex b/doc/design/timing.tex
new file mode 100644 (file)
index 0000000..567ba02
--- /dev/null
@@ -0,0 +1,42 @@
+\documentclass{article}
+\begin{document}
+
+We are trying to implement full-ish playlist based content specification.  The timing is awkward.
+
+\section{Reference timing}
+
+Frame rates of things can vary a lot; content can be in pretty much
+anything, and DCP video and audio frame rates may change on a whim
+depending on what is best for a given set of content.  This suggests
+(albeit without strong justification) the need for a frame-rate-independent unit of time.
+
+So far we've been using a time type called \texttt{Time} expressed in
+$\mathtt{TIME\_HZ}^{-1}$; e.g. \texttt{TIME\_HZ} units is 1 second.
+\texttt{TIME\_HZ} is chosen to be divisible by lots of frame and
+sample rates.
+
+We express content start time as a \texttt{Time}.
+
+
+\section{Timing at different stages of the chain}
+
+Let's try this: decoders produce sequences of (perhaps) video frames
+and (perhaps) audio frames.  There are no gaps.  They are at the
+content's native frame rates and are synchronised (meaning that if
+they are played together, at the content's frame rates, they will be
+in sync).  The decoders give timestamps for each piece of their
+output, which are \emph{simple indices} (\texttt{ContentVideoFrame}
+and \texttt{ContentAudioFrame}).  Decoders know nothing of \texttt{Time}.
+
+
+\section{Split of stuff between decoders and player}
+
+In some ways it seems nice to have decoders which produce the rawest
+possible data and make the player sort it out (e.g.\ cropping and
+scaling video, resampling audio).  The resampling is awkward, though,
+as you really need one resampler per source.  So it might make more sense
+to put stuff in the decoder.  But then, what's one map of resamplers between friends?
+
+
+
+\end{document}
index 73a00ca7d70f3b7862e93accf6168129b1fba4ea..9bf53e0ab4d044ac379c6f45decd7d00ba4f932f 100644 (file)
@@ -41,6 +41,8 @@ public:
 class AudioContent : public virtual Content
 {
 public:
+       typedef int64_t Frame;
+       
        AudioContent (boost::shared_ptr<const Film>, Time);
        AudioContent (boost::shared_ptr<const Film>, boost::filesystem::path);
        AudioContent (boost::shared_ptr<const Film>, boost::shared_ptr<const cxml::Node>);
@@ -49,7 +51,7 @@ public:
        void as_xml (xmlpp::Node *) const;
 
         virtual int audio_channels () const = 0;
-        virtual ContentAudioFrame audio_length () const = 0;
+        virtual AudioContent::Frame audio_length () const = 0;
         virtual int content_audio_frame_rate () const = 0;
        virtual int output_audio_frame_rate () const = 0;
        virtual AudioMapping audio_mapping () const = 0;
index a9e01908c4782a6095821d080800b0568ce71214..3964719104956a46e16f734fd8acaadcaa7b9df0 100644 (file)
@@ -31,57 +31,12 @@ using std::cout;
 using boost::optional;
 using boost::shared_ptr;
 
-AudioDecoder::AudioDecoder (shared_ptr<const Film> f, shared_ptr<const AudioContent> c)
+AudioDecoder::AudioDecoder (shared_ptr<const Film> f)
        : Decoder (f)
-       , _next_audio (0)
-       , _audio_content (c)
+       , _next_audio_frame (0)
 {
-       if (_audio_content->content_audio_frame_rate() != _audio_content->output_audio_frame_rate()) {
-
-               shared_ptr<const Film> film = _film.lock ();
-               assert (film);
-
-               stringstream s;
-               s << String::compose (
-                       "Will resample audio from %1 to %2",
-                       _audio_content->content_audio_frame_rate(), _audio_content->output_audio_frame_rate()
-                       );
-               
-               film->log()->log (s.str ());
-
-               /* We will be using planar float data when we call the
-                  resampler.  As far as I can see, the audio channel
-                  layout is not necessary for our purposes; it seems
-                  only to be used get the number of channels and
-                  decide if rematrixing is needed.  It won't be, since
-                  input and output layouts are the same.
-               */
-
-               _swr_context = swr_alloc_set_opts (
-                       0,
-                       av_get_default_channel_layout (_audio_content->audio_channels ()),
-                       AV_SAMPLE_FMT_FLTP,
-                       _audio_content->output_audio_frame_rate(),
-                       av_get_default_channel_layout (_audio_content->audio_channels ()),
-                       AV_SAMPLE_FMT_FLTP,
-                       _audio_content->content_audio_frame_rate(),
-                       0, 0
-                       );
-               
-               swr_init (_swr_context);
-       } else {
-               _swr_context = 0;
-       }
 }
 
-AudioDecoder::~AudioDecoder ()
-{
-       if (_swr_context) {
-               swr_free (&_swr_context);
-       }
-}
-       
-
 #if 0
 void
 AudioDecoder::process_end ()
@@ -113,54 +68,8 @@ AudioDecoder::process_end ()
 #endif
 
 void
-AudioDecoder::audio (shared_ptr<const AudioBuffers> data, Time time)
-{
-       /* Maybe resample */
-       if (_swr_context) {
-
-               /* Compute the resampled frames count and add 32 for luck */
-               int const max_resampled_frames = ceil (
-                       (int64_t) data->frames() * _audio_content->output_audio_frame_rate() / _audio_content->content_audio_frame_rate()
-                       ) + 32;
-
-               shared_ptr<AudioBuffers> resampled (new AudioBuffers (data->channels(), max_resampled_frames));
-
-               /* Resample audio */
-               int const resampled_frames = swr_convert (
-                       _swr_context, (uint8_t **) resampled->data(), max_resampled_frames, (uint8_t const **) data->data(), data->frames()
-                       );
-               
-               if (resampled_frames < 0) {
-                       throw EncodeError (_("could not run sample-rate converter"));
-               }
-
-               resampled->set_frames (resampled_frames);
-               
-               /* And point our variables at the resampled audio */
-               data = resampled;
-       }
-
-       shared_ptr<const Film> film = _film.lock ();
-       assert (film);
-       
-       /* Remap channels */
-       shared_ptr<AudioBuffers> dcp_mapped (new AudioBuffers (film->dcp_audio_channels(), data->frames()));
-       dcp_mapped->make_silent ();
-       list<pair<int, libdcp::Channel> > map = _audio_content->audio_mapping().content_to_dcp ();
-       for (list<pair<int, libdcp::Channel> >::iterator i = map.begin(); i != map.end(); ++i) {
-               dcp_mapped->accumulate_channel (data.get(), i->first, i->second);
-       }
-
-       Audio (dcp_mapped, time);
-       _next_audio = time + film->audio_frames_to_time (data->frames());
-}
-
-bool
-AudioDecoder::audio_done () const
+AudioDecoder::audio (shared_ptr<const AudioBuffers> data, AudioContent::Frame frame)
 {
-       shared_ptr<const Film> film = _film.lock ();
-       assert (film);
-       
-       return (_audio_content->length() - _next_audio) < film->audio_frames_to_time (1);
+       Audio (data, frame);
+       _next_audio_frame = frame + data->frames ();
 }
-       
index 1da8a676f37c7f59c3deeb46a166829e60ff1687..168348c2e3fdf52e0bb7c5a617dbd07718ce5e71 100644 (file)
 #ifndef DCPOMATIC_AUDIO_DECODER_H
 #define DCPOMATIC_AUDIO_DECODER_H
 
-#include "audio_source.h"
 #include "decoder.h"
-extern "C" {
-#include <libswresample/swresample.h>
-}
+#include "content.h"
 
-class AudioContent;
+class AudioBuffers;
 
 /** @class AudioDecoder.
  *  @brief Parent class for audio decoders.
  */
-class AudioDecoder : public AudioSource, public virtual Decoder
+class AudioDecoder : public virtual Decoder
 {
 public:
-       AudioDecoder (boost::shared_ptr<const Film>, boost::shared_ptr<const AudioContent>);
-       ~AudioDecoder ();
+       AudioDecoder (boost::shared_ptr<const Film>);
 
-protected:
-
-       void audio (boost::shared_ptr<const AudioBuffers>, Time);
-       bool audio_done () const;
+       /** Emitted when some audio data is ready */
+       boost::signals2::signal<void (boost::shared_ptr<const AudioBuffers>, AudioContent::Frame)> Audio;
 
-       Time _next_audio;
-       boost::shared_ptr<const AudioContent> _audio_content;
+protected:
 
-private:       
-       SwrContext* _swr_context;
+       void audio (boost::shared_ptr<const AudioBuffers>, AudioContent::Frame);
+       AudioContent::Frame _next_audio_frame;
 };
 
 #endif
diff --git a/src/lib/audio_sink.h b/src/lib/audio_sink.h
deleted file mode 100644 (file)
index 1aad5ed..0000000
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
-    Copyright (C) 2012 Carl Hetherington <cth@carlh.net>
-
-    This program is free software; you can redistribute it and/or modify
-    it under the terms of the GNU General Public License as published by
-    the Free Software Foundation; either version 2 of the License, or
-    (at your option) any later version.
-
-    This program is distributed in the hope that it will be useful,
-    but WITHOUT ANY WARRANTY; without even the implied warranty of
-    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-    GNU General Public License for more details.
-
-    You should have received a copy of the GNU General Public License
-    along with this program; if not, write to the Free Software
-    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
-*/
-
-#ifndef DCPOMATIC_AUDIO_SINK_H
-#define DCPOMATIC_AUDIO_SINK_H
-
-class AudioBuffers;
-
-class AudioSink
-{
-public:
-       /** Call with some audio data */
-       virtual void process_audio (boost::shared_ptr<const AudioBuffers>, Time) = 0;
-};
-
-#endif
diff --git a/src/lib/audio_source.cc b/src/lib/audio_source.cc
deleted file mode 100644 (file)
index e617216..0000000
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
-    Copyright (C) 2012 Carl Hetherington <cth@carlh.net>
-
-    This program is free software; you can redistribute it and/or modify
-    it under the terms of the GNU General Public License as published by
-    the Free Software Foundation; either version 2 of the License, or
-    (at your option) any later version.
-
-    This program is distributed in the hope that it will be useful,
-    but WITHOUT ANY WARRANTY; without even the implied warranty of
-    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-    GNU General Public License for more details.
-
-    You should have received a copy of the GNU General Public License
-    along with this program; if not, write to the Free Software
-    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
-*/
-
-#include "audio_source.h"
-#include "audio_sink.h"
-
-using boost::shared_ptr;
-using boost::weak_ptr;
-using boost::bind;
-
-static void
-process_audio_proxy (weak_ptr<AudioSink> sink, shared_ptr<const AudioBuffers> audio, Time time)
-{
-       shared_ptr<AudioSink> p = sink.lock ();
-       if (p) {
-               p->process_audio (audio, time);
-       }
-}
-
-void
-AudioSource::connect_audio (shared_ptr<AudioSink> s)
-{
-       Audio.connect (bind (process_audio_proxy, weak_ptr<AudioSink> (s), _1, _2));
-}
-
-
diff --git a/src/lib/audio_source.h b/src/lib/audio_source.h
deleted file mode 100644 (file)
index ef47e96..0000000
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
-    Copyright (C) 2012 Carl Hetherington <cth@carlh.net>
-
-    This program is free software; you can redistribute it and/or modify
-    it under the terms of the GNU General Public License as published by
-    the Free Software Foundation; either version 2 of the License, or
-    (at your option) any later version.
-
-    This program is distributed in the hope that it will be useful,
-    but WITHOUT ANY WARRANTY; without even the implied warranty of
-    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-    GNU General Public License for more details.
-
-    You should have received a copy of the GNU General Public License
-    along with this program; if not, write to the Free Software
-    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
-*/
-
-/** @file  src/audio_source.h
- *  @brief Parent class for classes which emit audio data.
- */
-
-#ifndef DCPOMATIC_AUDIO_SOURCE_H
-#define DCPOMATIC_AUDIO_SOURCE_H
-
-#include <boost/signals2.hpp>
-#include "types.h"
-
-class AudioBuffers;
-class AudioSink;
-
-/** A class that emits audio data */
-class AudioSource
-{
-public:
-       /** Emitted when some audio data is ready */
-       boost::signals2::signal<void (boost::shared_ptr<const AudioBuffers>, Time)> Audio;
-
-       void connect_audio (boost::shared_ptr<AudioSink>);
-};
-
-#endif
index 0b231edd355cdd4bc93d06c31bc849cb8f3f2a78..beb6bfef385c5b5f5a3c21a67e95d7aece5d845a 100644 (file)
@@ -25,7 +25,8 @@ using boost::shared_ptr;
 
 BlackDecoder::BlackDecoder (shared_ptr<const Film> f, shared_ptr<NullContent> c)
        : Decoder (f)
-       , VideoDecoder (f, c)
+       , VideoDecoder (f)
+       , _null_content (c)
 {
 
 }
@@ -36,9 +37,9 @@ BlackDecoder::pass ()
        if (!_image) {
                _image.reset (new SimpleImage (AV_PIX_FMT_RGB24, video_size(), true));
                _image->make_black ();
-               video (_image, false, _next_video);
+               video (_image, false, _next_video_frame);
        } else {
-               video (_image, true, _next_video);
+               video (_image, true, _next_video_frame);
        }
 }
 
@@ -53,50 +54,28 @@ BlackDecoder::video_frame_rate () const
        return f->dcp_video_frame_rate ();
 }
 
-ContentVideoFrame
+VideoContent::Frame
 BlackDecoder::video_length () const
 {
-       return _video_content->length() * video_frame_rate() / TIME_HZ;
-}
-
-Time
-BlackDecoder::position () const
-{
-       return _next_video;
+       return _null_content->length() * video_frame_rate() / TIME_HZ;
 }
 
 void
-BlackDecoder::seek (Time t)
+BlackDecoder::seek (VideoContent::Frame frame)
 {
-       _next_video = t;
+       _next_video_frame = frame;
 }
 
 void
 BlackDecoder::seek_back ()
 {
-       boost::shared_ptr<const Film> f = _film.lock ();
-       if (!f) {
-               return;
-       }
-
-       _next_video -= f->video_frames_to_time (2);
-}
-
-void
-BlackDecoder::seek_forward ()
-{
-       boost::shared_ptr<const Film> f = _film.lock ();
-       if (!f) {
-               return;
+       if (_next_video_frame > 0) {
+               --_next_video_frame;
        }
-
-       _next_video += f->video_frames_to_time (1);
 }
-
+       
 bool
 BlackDecoder::done () const
 {
-       return video_done ();
+       return _next_video_frame >= _null_content->video_length ();
 }
-
-       
index 4591881a16159cbdecda8b868c171e738771f68f..40aa73d726248e7a852a30e749f1e20c62065a3b 100644 (file)
@@ -29,20 +29,19 @@ public:
        /* Decoder */
        
        void pass ();
-       void seek (Time);
-       void seek_back ();
-       void seek_forward ();
-       Time position () const;
        bool done () const;
 
        /* VideoDecoder */
 
+       void seek (VideoContent::Frame);
+       void seek_back ();
        float video_frame_rate () const;
        libdcp::Size video_size () const {
                return libdcp::Size (256, 256);
        }
-       ContentVideoFrame video_length () const;
+       VideoContent::Frame video_length () const;
 
 private:
+       boost::shared_ptr<NullContent> _null_content;
        boost::shared_ptr<Image> _image;
 };
diff --git a/src/lib/combiner.cc b/src/lib/combiner.cc
deleted file mode 100644 (file)
index 44971d1..0000000
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
-    Copyright (C) 2012 Carl Hetherington <cth@carlh.net>
-
-    This program is free software; you can redistribute it and/or modify
-    it under the terms of the GNU General Public License as published by
-    the Free Software Foundation; either version 2 of the License, or
-    (at your option) any later version.
-
-    This program is distributed in the hope that it will be useful,
-    but WITHOUT ANY WARRANTY; without even the implied warranty of
-    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-    GNU General Public License for more details.
-
-    You should have received a copy of the GNU General Public License
-    along with this program; if not, write to the Free Software
-    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
-*/
-
-#include "combiner.h"
-#include "image.h"
-
-using boost::shared_ptr;
-
-Combiner::Combiner ()
-{
-
-}
-
-/** Process video for the left half of the frame.
- *  Subtitle parameter will be ignored.
- *  @param image Frame image.
- */
-void
-Combiner::process_video (shared_ptr<const Image> image, bool, Time)
-{
-       _image.reset (new SimpleImage (image, true));
-}
-
-/** Process video for the right half of the frame.
- *  @param image Frame image.
- *  @param sub Subtitle (which will be put onto the whole frame)
- */
-void
-Combiner::process_video_b (shared_ptr<const Image> image, bool, Time t)
-{
-       /* Copy the right half of this image into our _image */
-       /* XXX: this should probably be in the Image class */
-       for (int i = 0; i < image->components(); ++i) {
-               int const line_size = image->line_size()[i];
-               int const half_line_size = line_size / 2;
-
-               uint8_t* p = _image->data()[i];
-               uint8_t* q = image->data()[i];
-                       
-               for (int j = 0; j < image->lines (i); ++j) {
-                       memcpy (p + half_line_size, q + half_line_size, half_line_size);
-                       p += _image->stride()[i];
-                       q += image->stride()[i];
-               }
-       }
-
-       Video (_image, false, t);
-       _image.reset ();
-}
diff --git a/src/lib/combiner.h b/src/lib/combiner.h
deleted file mode 100644 (file)
index 46c90b4..0000000
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
-    Copyright (C) 2012 Carl Hetherington <cth@carlh.net>
-
-    This program is free software; you can redistribute it and/or modify
-    it under the terms of the GNU General Public License as published by
-    the Free Software Foundation; either version 2 of the License, or
-    (at your option) any later version.
-
-    This program is distributed in the hope that it will be useful,
-    but WITHOUT ANY WARRANTY; without even the implied warranty of
-    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-    GNU General Public License for more details.
-
-    You should have received a copy of the GNU General Public License
-    along with this program; if not, write to the Free Software
-    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
-*/
-
-/** @file src/lib/combiner.h
- *  @brief Class for combining two video streams.
- */
-
-#include "video_source.h"
-#include "video_sink.h"
-
-/** @class Combiner
- *  @brief A class which can combine two video streams into one, with
- *  one image used for the left half of the screen and the other for
- *  the right.
- */
-class Combiner : public VideoSource, public VideoSink
-{
-public:
-       Combiner ();
-
-       void process_video (boost::shared_ptr<const Image> i, bool, Time);
-       void process_video_b (boost::shared_ptr<const Image> i, bool, Time);
-
-private:
-       /** The image that we are currently working on */
-       boost::shared_ptr<Image> _image;
-};
index 391b9d19aa17380289db9f310f1174e9d2eaee81..cfca6867f652d3fc9d2d5be7320d75128060cffe 100644 (file)
@@ -29,8 +29,6 @@
 #include <stdint.h>
 #include <boost/shared_ptr.hpp>
 #include <boost/signals2.hpp>
-#include "video_source.h"
-#include "audio_source.h"
 #include "film.h"
 
 class Image;
@@ -54,24 +52,6 @@ public:
         */
        virtual void pass () = 0;
 
-       /** Seek this decoder to as close as possible to some time,
-        *  expressed relative to our source's start.
-        *  @param t Time.
-        *  @param a true to try hard to be accurate, otherwise false.
-        */
-       virtual void seek (Time) = 0;
-
-       /** Seek back one video frame */
-       virtual void seek_back () = 0;
-
-       /** Seek forward one video frame */
-       virtual void seek_forward () = 0;
-
-       /** @return Approximate time of the next content that we will emit,
-        *  expressed relative to the start of our source.
-        */
-       virtual Time position () const = 0;
-
        virtual bool done () const = 0;
 
 protected:
index 8b2db0eb3113767594264b6ca8367e2022bbbe8b..c3865d2c16ebf44852cede103a97d94f93c3c022 100644 (file)
@@ -169,7 +169,7 @@ Encoder::frame_done ()
 }
 
 void
-Encoder::process_video (shared_ptr<const Image> image, bool same, Time)
+Encoder::process_video (shared_ptr<const Image> image, bool same)
 {
        boost::mutex::scoped_lock lock (_mutex);
 
@@ -215,7 +215,7 @@ Encoder::process_video (shared_ptr<const Image> image, bool same, Time)
 }
 
 void
-Encoder::process_audio (shared_ptr<const AudioBuffers> data, Time)
+Encoder::process_audio (shared_ptr<const AudioBuffers> data)
 {
        _writer->write (data);
 }
index 3fe707b511e717b38b001788db4a35d601ae8dfd..b5a641f50d40492caa008115cf294ea4ec084de5 100644 (file)
@@ -36,8 +36,6 @@ extern "C" {
 #include <libswresample/swresample.h>
 }
 #include "util.h"
-#include "video_sink.h"
-#include "audio_sink.h"
 
 class Image;
 class AudioBuffers;
@@ -55,7 +53,7 @@ class Job;
  *  is supplied as uncompressed PCM in blocks of various sizes.
  */
 
-class Encoder : public VideoSink, public AudioSink
+class Encoder
 {
 public:
        Encoder (boost::shared_ptr<const Film> f, boost::shared_ptr<Job>);
@@ -68,10 +66,10 @@ public:
         *  @param i Video frame image.
         *  @param same true if i is the same as the last time we were called.
         */
-       void process_video (boost::shared_ptr<const Image> i, bool same, Time);
+       void process_video (boost::shared_ptr<const Image> i, bool same);
 
        /** Call with some audio data */
-       void process_audio (boost::shared_ptr<const AudioBuffers>, Time);
+       void process_audio (boost::shared_ptr<const AudioBuffers>);
 
        /** Called when a processing run has finished */
        void process_end ();
index 68132c5abf94bea8aedd52196b8520712b0c1e8c..378bd98cbb80519e608dcdd16acc20879b71f531 100644 (file)
@@ -139,7 +139,7 @@ FFmpegContent::examine (shared_ptr<Job> job)
 
        shared_ptr<FFmpegExaminer> examiner (new FFmpegExaminer (shared_from_this ()));
 
-       ContentVideoFrame video_length = 0;
+       VideoContent::Frame video_length = 0;
        video_length = examiner->video_length ();
        film->log()->log (String::compose ("Video length obtained from header as %1 frames", video_length));
 
@@ -214,12 +214,12 @@ FFmpegContent::set_audio_stream (shared_ptr<FFmpegAudioStream> s)
         signal_changed (FFmpegContentProperty::AUDIO_STREAM);
 }
 
-ContentAudioFrame
+AudioContent::Frame
 FFmpegContent::audio_length () const
 {
        int const cafr = content_audio_frame_rate ();
        int const vfr  = video_frame_rate ();
-       ContentVideoFrame const vl = video_length ();
+       VideoContent::Frame const vl = video_length ();
 
        boost::mutex::scoped_lock lm (_mutex);
         if (!_audio_stream) {
index 36c24c2b3353629614ada8999c0c1cb970b56674..fc45267ee553a26d0e3141bdc2abad02e7388c7d 100644 (file)
@@ -99,7 +99,7 @@ public:
 
         /* AudioContent */
         int audio_channels () const;
-        ContentAudioFrame audio_length () const;
+        AudioContent::Frame audio_length () const;
         int content_audio_frame_rate () const;
         int output_audio_frame_rate () const;
        AudioMapping audio_mapping () const;
index f1d984ee15967aa1576426127769fc0bddb23da3..d897aef9d8c93750799893f6ea87d0e9aaa34e76 100644 (file)
@@ -59,8 +59,8 @@ using libdcp::Size;
 
 FFmpegDecoder::FFmpegDecoder (shared_ptr<const Film> f, shared_ptr<const FFmpegContent> c, bool video, bool audio)
        : Decoder (f)
-       , VideoDecoder (f, c)
-       , AudioDecoder (f, c)
+       , VideoDecoder (f)
+       , AudioDecoder (f)
        , FFmpeg (c)
        , _subtitle_codec_context (0)
        , _subtitle_codec (0)
@@ -108,7 +108,8 @@ FFmpegDecoder::pass ()
                }
 
                /* Stop us being asked for any more data */
-               _next_video = _next_audio = _ffmpeg_content->length ();
+               _next_video_frame = _ffmpeg_content->video_length ();
+               _next_audio_frame = _ffmpeg_content->audio_length ();
                return;
        }
 
@@ -119,6 +120,7 @@ FFmpegDecoder::pass ()
        } else if (_ffmpeg_content->audio_stream() && _packet.stream_index == _ffmpeg_content->audio_stream()->id && _decode_audio) {
                decode_audio_packet ();
        } else if (_ffmpeg_content->subtitle_stream() && _packet.stream_index == _ffmpeg_content->subtitle_stream()->id) {
+#if 0          
 
                int got_subtitle;
                AVSubtitle sub;
@@ -138,6 +140,7 @@ FFmpegDecoder::pass ()
                        }
                        avsubtitle_free (&sub);
                }
+#endif         
        }
 
        av_free_packet (&_packet);
@@ -256,38 +259,26 @@ FFmpegDecoder::bytes_per_audio_sample () const
 }
 
 void
-FFmpegDecoder::seek (Time t)
+FFmpegDecoder::seek (VideoContent::Frame frame)
 {
-       do_seek (t, false, false);
-       VideoDecoder::seek (t);
+       do_seek (frame, false, false);
 }
 
 void
 FFmpegDecoder::seek_back ()
 {
-       if (position() < (2.5 * TIME_HZ / _ffmpeg_content->video_frame_rate())) {
+       if (_next_video_frame == 0) {
                return;
        }
        
-       do_seek (position() - 2.5 * TIME_HZ / _ffmpeg_content->video_frame_rate(), true, true);
+       do_seek (_next_video_frame - 1, true, true);
        VideoDecoder::seek_back ();
 }
 
 void
-FFmpegDecoder::seek_forward ()
+FFmpegDecoder::do_seek (VideoContent::Frame frame, bool backwards, bool accurate)
 {
-       if (position() >= (_ffmpeg_content->length() - 0.5 * TIME_HZ / _ffmpeg_content->video_frame_rate())) {
-               return;
-       }
-       
-       do_seek (position() - 0.5 * TIME_HZ / _ffmpeg_content->video_frame_rate(), true, true);
-       VideoDecoder::seek_forward ();
-}
-
-void
-FFmpegDecoder::do_seek (Time t, bool backwards, bool accurate)
-{
-       int64_t const vt = t / (av_q2d (_format_context->streams[_video_stream]->time_base) * TIME_HZ);
+       int64_t const vt = frame * _ffmpeg_content->video_frame_rate() / av_q2d (_format_context->streams[_video_stream]->time_base);
        av_seek_frame (_format_context, _video_stream, vt, backwards ? AVSEEK_FLAG_BACKWARD : 0);
 
        avcodec_flush_buffers (video_codec_context());
@@ -347,7 +338,7 @@ FFmpegDecoder::decode_audio_packet ()
                                        );
                                
                                assert (audio_codec_context()->channels == _ffmpeg_content->audio_channels());
-                               audio (deinterleave_audio (_frame->data, data_size), source_pts_seconds * TIME_HZ);
+                               Audio (deinterleave_audio (_frame->data, data_size), source_pts_seconds * _ffmpeg_content->content_audio_frame_rate());
                        }
                        
                        copy_packet.data += decode_result;
@@ -398,11 +389,33 @@ FFmpegDecoder::decode_video_packet ()
                
                int64_t const bet = av_frame_get_best_effort_timestamp (_frame);
                if (bet != AV_NOPTS_VALUE) {
-                       /* XXX: may need to insert extra frames / remove frames here ...
-                          (as per old Matcher)
-                       */
-                       Time const t = bet * av_q2d (_format_context->streams[_video_stream]->time_base) * TIME_HZ;
-                       video (image, false, t);
+
+                       double const pts = bet * av_q2d (_format_context->streams[_video_stream]->time_base);
+                       double const next = _next_video_frame / _ffmpeg_content->video_frame_rate();
+                       double const one_frame = 1 / _ffmpeg_content->video_frame_rate ();
+                       double delta = pts - next;
+
+                       while (delta > one_frame) {
+                               /* This PTS is more than one frame forward in time of where we think we should be; emit
+                                  a black frame.
+                               */
+                               boost::shared_ptr<Image> black (
+                                       new SimpleImage (
+                                               static_cast<AVPixelFormat> (_frame->format),
+                                               libdcp::Size (video_codec_context()->width, video_codec_context()->height),
+                                               true
+                                               )
+                                       );
+                               
+                               black->make_black ();
+                               video (image, false, _next_video_frame);
+                               delta -= one_frame;
+                       }
+
+                       if (delta > -one_frame) {
+                               /* This PTS is within a frame of being right; emit this (otherwise it will be dropped) */
+                               video (image, false, _next_video_frame);
+                       }
                } else {
                        shared_ptr<const Film> film = _film.lock ();
                        assert (film);
@@ -413,27 +426,6 @@ FFmpegDecoder::decode_video_packet ()
        return true;
 }
 
-Time
-FFmpegDecoder::position () const
-{
-       if (_decode_video && _decode_audio && _ffmpeg_content->audio_stream()) {
-               return min (_next_video, _next_audio);
-       }
-
-       if (_decode_audio && _ffmpeg_content->audio_stream()) {
-               return _next_audio;
-       }
-
-       return _next_video;
-}
-
-bool
-FFmpegDecoder::done () const
-{
-       bool const ad = !_decode_audio || !_ffmpeg_content->audio_stream() || audio_done();
-       bool const vd = !_decode_video || video_done();
-       return ad && vd;
-}
        
 void
 FFmpegDecoder::setup_subtitle ()
@@ -455,3 +447,12 @@ FFmpegDecoder::setup_subtitle ()
                throw DecodeError (N_("could not open subtitle decoder"));
        }
 }
+
+bool
+FFmpegDecoder::done () const
+{
+       bool const vd = !_decode_video || (_next_video_frame >= _ffmpeg_content->video_length());
+       bool const ad = !_decode_audio || !_ffmpeg_content->audio_stream() || (_next_audio_frame >= _ffmpeg_content->audio_length());
+       return vd && ad;
+}
+       
index 331d9be70ffb5cfb55e247583c4055ef65bb2268..a8eabb9725c533870d38887436f68ae793232716 100644 (file)
@@ -49,10 +49,8 @@ public:
        ~FFmpegDecoder ();
 
        void pass ();
-       void seek (Time);
+       void seek (VideoContent::Frame);
        void seek_back ();
-       void seek_forward ();
-       Time position () const;
        bool done () const;
 
 private:
@@ -65,7 +63,7 @@ private:
 
        AVSampleFormat audio_sample_format () const;
        int bytes_per_audio_sample () const;
-       void do_seek (Time, bool, bool);
+       void do_seek (VideoContent::Frame, bool, bool);
 
        bool decode_video_packet ();
        void decode_audio_packet ();
index c09395e76609086f33579b63b18c008cf4cc3744..6f1524f50e2ccf95be3c33c1ef89c9157158fe55 100644 (file)
@@ -134,7 +134,7 @@ FFmpegExaminer::video_size () const
 }
 
 /** @return Length (in video frames) according to our content's header */
-ContentVideoFrame
+VideoContent::Frame
 FFmpegExaminer::video_length () const
 {
        return (double (_format_context->duration) / AV_TIME_BASE) * video_frame_rate();
index 57b7775d4b84942c8aa065e891e8da230dcd5179..5cf9c2d0a5b8454c2918b196d8227cdcbc071619 100644 (file)
@@ -31,7 +31,7 @@ public:
        
        float video_frame_rate () const;
        libdcp::Size video_size () const;
-       ContentVideoFrame video_length () const;
+       VideoContent::Frame video_length () const;
 
        std::vector<boost::shared_ptr<FFmpegSubtitleStream> > subtitle_streams () const {
                return _subtitle_streams;
index 0bff1a7ccfca18d9912b33e44dffa28e532c5f3c..722ff5d3ceff24509edeb49bb21ca9c575148abb 100644 (file)
@@ -43,8 +43,9 @@ extern "C" {
 
 #include "i18n.h"
 
-using namespace std;
-using namespace boost;
+using std::string;
+using std::min;
+using boost::shared_ptr;
 using libdcp::Size;
 
 void
index f9ee8cc84029d0cceb01ce845d272ceda8a62484..2fd65ffa0d403d607fd7b2a835d9b71710516746 100644 (file)
@@ -87,7 +87,7 @@ ImageMagickContent::clone () const
 }
 
 void
-ImageMagickContent::set_video_length (ContentVideoFrame len)
+ImageMagickContent::set_video_length (VideoContent::Frame len)
 {
        {
                boost::mutex::scoped_lock lm (_mutex);
index d7673d870730ec3dd411e3b875a386f454652c6f..04425af080111aebb54cb0a81bb6b8a906d63ddc 100644 (file)
@@ -43,7 +43,7 @@ public:
        boost::shared_ptr<Content> clone () const;
        Time length () const;
 
-       void set_video_length (ContentVideoFrame);
+       void set_video_length (VideoContent::Frame);
 
        static bool valid_file (boost::filesystem::path);
 };
index c9123c77c288404e31783f41d6c26e51895a2674..acc34421cc28c1df84c1249fa0332af55d962da9 100644 (file)
@@ -34,7 +34,7 @@ using libdcp::Size;
 
 ImageMagickDecoder::ImageMagickDecoder (shared_ptr<const Film> f, shared_ptr<const ImageMagickContent> c)
        : Decoder (f)
-       , VideoDecoder (f, c)
+       , VideoDecoder (f)
        , ImageMagick (c)
 {
 
@@ -43,12 +43,12 @@ ImageMagickDecoder::ImageMagickDecoder (shared_ptr<const Film> f, shared_ptr<con
 void
 ImageMagickDecoder::pass ()
 {
-       if (_next_video >= _imagemagick_content->length ()) {
+       if (_next_video_frame >= _imagemagick_content->video_length ()) {
                return;
        }
 
        if (_image) {
-               video (_image, true, _next_video);
+               video (_image, true, _next_video_frame);
                return;
        }
 
@@ -71,48 +71,25 @@ ImageMagickDecoder::pass ()
 
        delete magick_image;
 
-       _image = _image->crop (_imagemagick_content->crop(), true);
-       video (_image, false, _next_video);
+       video (_image, false, _next_video_frame);
 }
 
 void
-ImageMagickDecoder::seek (Time t)
+ImageMagickDecoder::seek (VideoContent::Frame frame)
 {
-       _next_video = t;
+       _next_video_frame = frame;
 }
 
 void
 ImageMagickDecoder::seek_back ()
 {
-       boost::shared_ptr<const Film> f = _film.lock ();
-       if (!f) {
-               return;
-       }
-       
-       _next_video -= f->video_frames_to_time (2);
-}
-
-void
-ImageMagickDecoder::seek_forward ()
-{
-       boost::shared_ptr<const Film> f = _film.lock ();
-       if (!f) {
-               return;
+       if (_next_video_frame > 0) {
+               _next_video_frame--;
        }
-       
-       _next_video += f->video_frames_to_time (1);
 }
 
-Time
-ImageMagickDecoder::position () const
-{
-       return _next_video;
-}
-
-
 bool
 ImageMagickDecoder::done () const
 {
-       return video_done ();
+       return _next_video_frame > _imagemagick_content->video_length ();
 }
-       
index e169f9bc384a9a3b79a59657ab356c1e8b94438e..286f473378396f9bbd44f39f28f08c7eeefaf0dd 100644 (file)
@@ -34,10 +34,8 @@ public:
        /* Decoder */
 
        void pass ();
-       void seek (Time);
+       void seek (VideoContent::Frame);
        void seek_back ();
-       void seek_forward ();
-       Time position () const;
        bool done () const;
 
 private:
index 827dad67e5c9dd85bbdfc92c8b8de68d46c7093a..801ede44248348ec26f6287542c54f66a672d96d 100644 (file)
@@ -33,7 +33,7 @@ public:
 
        float video_frame_rate () const;
        libdcp::Size video_size () const;
-       ContentVideoFrame video_length () const;
+       VideoContent::Frame video_length () const;
 
 private:
        boost::weak_ptr<const Film> _film;
diff --git a/src/lib/matcher.cc b/src/lib/matcher.cc
deleted file mode 100644 (file)
index 4acb82a..0000000
+++ /dev/null
@@ -1,224 +0,0 @@
-/*
-    Copyright (C) 2012 Carl Hetherington <cth@carlh.net>
-
-    This program is free software; you can redistribute it and/or modify
-    it under the terms of the GNU General Public License as published by
-    the Free Software Foundation; either version 2 of the License, or
-    (at your option) any later version.
-
-    This program is distributed in the hope that it will be useful,
-    but WITHOUT ANY WARRANTY; without even the implied warranty of
-    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-    GNU General Public License for more details.
-
-    You should have received a copy of the GNU General Public License
-    along with this program; if not, write to the Free Software
-    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
-*/
-
-#include "matcher.h"
-#include "image.h"
-#include "log.h"
-
-#include "i18n.h"
-
-using std::min;
-using std::cout;
-using std::list;
-using boost::shared_ptr;
-
-Matcher::Matcher (shared_ptr<Log> log, int sample_rate, float frames_per_second)
-       : Processor (log)
-       , _sample_rate (sample_rate)
-       , _frames_per_second (frames_per_second)
-       , _video_frames (0)
-       , _audio_frames (0)
-       , _had_first_video (false)
-       , _had_first_audio (false)
-{
-
-}
-
-void
-Matcher::process_video (boost::shared_ptr<const Image> image, bool same, boost::shared_ptr<Subtitle> sub, double t)
-{
-       _pixel_format = image->pixel_format ();
-       _size = image->size ();
-
-       _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 || t < _first_input.get()) {
-               _first_input = t;
-       }
-
-       bool const this_is_first_video = !_had_first_video;
-       _had_first_video = true;
-
-       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 */
-               _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));
-                       }
-               }
-               
-               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;
-       }
-}
-
-void
-Matcher::process_audio (boost::shared_ptr<const AudioBuffers> b, double t)
-{
-       _channels = b->channels ();
-
-       _log->log (String::compose (
-                          "Matcher audio (%1 frames) @ %2 [video=%3, audio=%4, pending_video=%5, pending_audio=%6]",
-                          b->frames(), t, _video_frames, _audio_frames, _pending_video.size(), _pending_audio.size()
-                          )
-               );
-
-       if (!_first_input || t < _first_input.get()) {
-               _first_input = t;
-       }
-
-       bool const this_is_first_audio = !_had_first_audio;
-       _had_first_audio = true;
-       
-       if (!_had_first_video) {
-               /* No video yet; we must postpone these data until we have some */
-               _pending_audio.push_back (AudioRecord (b, t));
-       } else if (this_is_first_audio && _had_first_video) {
-               /* First audio since we got video */
-               _pending_audio.push_back (AudioRecord (b, t));
-               fix_start ();
-       } else {
-               /* Normal running.  We assume audio time stamps are consecutive, so there's no equivalent of
-                  the checking / insertion of repeat frames that there is for video.
-               */
-               Audio (b);
-               _audio_frames += b->frames ();
-       }
-}
-
-void
-Matcher::process_end ()
-{
-       if (_audio_frames == 0 || !_pixel_format || !_size || !_channels) {
-               /* We won't do anything */
-               return;
-       }
-
-       _log->log (String::compose ("Matcher has seen %1 video frames (which equals %2 audio frames) and %3 audio frames",
-                                   _video_frames, video_frames_to_audio_frames (_video_frames, _sample_rate, _frames_per_second), _audio_frames));
-       
-       match ((double (_audio_frames) / _sample_rate) - (double (_video_frames) / _frames_per_second));
-}
-
-void
-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);
-
-       for (list<VideoRecord>::iterator i = _pending_video.begin(); i != _pending_video.end(); ++i) {
-               process_video (i->image, i->same, i->subtitle, i->time);
-       }
-
-       _pending_video.clear ();
-
-       for (list<AudioRecord>::iterator i = _pending_audio.begin(); i != _pending_audio.end(); ++i) {
-               process_audio (i->audio, i->time);
-       }
-       
-       _pending_audio.clear ();
-}
-
-void
-Matcher::match (double extra_video_needed)
-{
-       _log->log (String::compose ("Match %1", extra_video_needed));
-       
-       if (extra_video_needed > 0) {
-
-               /* Emit black video frames */
-               
-               int const black_video_frames = ceil (extra_video_needed * _frames_per_second);
-
-               _log->log (String::compose (N_("Emitting %1 frames of black video"), black_video_frames));
-
-               shared_ptr<Image> black (new SimpleImage (_pixel_format.get(), _size.get(), true));
-               black->make_black ();
-               for (int i = 0; i < black_video_frames; ++i) {
-                       Video (black, i != 0, shared_ptr<Subtitle>());
-                       ++_video_frames;
-               }
-
-               extra_video_needed -= black_video_frames / _frames_per_second;
-       }
-
-       if (extra_video_needed < 0) {
-               
-               /* Emit silence */
-               
-               int64_t to_do = -extra_video_needed * _sample_rate;
-               _log->log (String::compose (N_("Emitting %1 frames of silence"), to_do));
-
-               /* Do things in half second blocks as I think there may be limits
-                  to what FFmpeg (and in particular the resampler) can cope with.
-               */
-               int64_t const block = _sample_rate / 2;
-               shared_ptr<AudioBuffers> b (new AudioBuffers (_channels.get(), block));
-               b->make_silent ();
-               
-               while (to_do > 0) {
-                       int64_t const this_time = min (to_do, block);
-                       b->set_frames (this_time);
-                       Audio (b);
-                       _audio_frames += b->frames ();
-                       to_do -= this_time;
-               }
-       }
-}
-
-void
-Matcher::repeat_last_video ()
-{
-       if (!_last_image) {
-               shared_ptr<Image> im (new SimpleImage (_pixel_format.get(), _size.get(), true));
-               im->make_black ();
-               _last_image = im;
-       }
-
-       Video (_last_image, true, _last_subtitle);
-       ++_video_frames;
-}
-
diff --git a/src/lib/matcher.h b/src/lib/matcher.h
deleted file mode 100644 (file)
index 61fd814..0000000
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
-    Copyright (C) 2012 Carl Hetherington <cth@carlh.net>
-
-    This program is free software; you can redistribute it and/or modify
-    it under the terms of the GNU General Public License as published by
-    the Free Software Foundation; either version 2 of the License, or
-    (at your option) any later version.
-
-    This program is distributed in the hope that it will be useful,
-    but WITHOUT ANY WARRANTY; without even the implied warranty of
-    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-    GNU General Public License for more details.
-
-    You should have received a copy of the GNU General Public License
-    along with this program; if not, write to the Free Software
-    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
-*/
-
-#include <boost/optional.hpp>
-#include "processor.h"
-
-class Matcher : public Processor, public TimedAudioSink, public TimedVideoSink, public AudioSource, public VideoSource 
-{
-public:
-       Matcher (boost::shared_ptr<Log> log, int sample_rate, float frames_per_second);
-       void process_video (boost::shared_ptr<const Image> i, bool, boost::shared_ptr<Subtitle> s, double);
-       void process_audio (boost::shared_ptr<const AudioBuffers>, double);
-       void process_end ();
-
-private:
-       void fix_start ();
-       void match (double);
-       void repeat_last_video ();
-       
-       int _sample_rate;
-       float _frames_per_second;
-       int _video_frames;
-       int64_t _audio_frames;
-       boost::optional<AVPixelFormat> _pixel_format;
-       boost::optional<libdcp::Size> _size;
-       boost::optional<int> _channels;
-
-       struct VideoRecord {
-               VideoRecord (boost::shared_ptr<const Image> i, bool s, boost::shared_ptr<Subtitle> u, double t)
-                       : image (i)
-                       , same (s)
-                       , subtitle (u)
-                       , time (t)
-               {}
-
-               boost::shared_ptr<const Image> image;
-               bool same;
-               boost::shared_ptr<Subtitle> subtitle;
-               double time;
-       };
-
-       struct AudioRecord {
-               AudioRecord (boost::shared_ptr<const AudioBuffers> a, double t)
-                       : audio (a)
-                       , time (t)
-               {}
-               
-               boost::shared_ptr<const AudioBuffers> audio;
-               double time;
-       };
-
-       std::list<VideoRecord> _pending_video;
-       std::list<AudioRecord> _pending_audio;
-
-       boost::optional<double> _first_input;
-       boost::shared_ptr<const Image> _last_image;
-       boost::shared_ptr<Subtitle> _last_subtitle;
-
-       bool _had_first_video;
-       bool _had_first_audio;
-};
index 889ff7a0d326990a59e2d90af62138ad4c24231f..44bfffa49d6729a5a7f85ce86337b54c840cc1a7 100644 (file)
@@ -43,7 +43,7 @@ public:
 
         int audio_channels () const;
        
-        ContentAudioFrame audio_length () const {
+        AudioContent::Frame audio_length () const {
                return _audio_length;
        }
        
@@ -62,6 +62,6 @@ public:
        }
 
 private:
-       ContentAudioFrame _audio_length;
+       AudioContent::Frame _audio_length;
        Time _length;
 };
index cd1c54d5bd466e78834d36d87c10476e180e6410..79f1c3b97a91496b979e50d8af2d9844e4bc1e4e 100644 (file)
 #include "null_content.h"
 #include "black_decoder.h"
 #include "silence_decoder.h"
+#include "ratio.h"
+#include "resampler.h"
 
 using std::list;
 using std::cout;
 using std::min;
 using std::max;
 using std::vector;
+using std::pair;
+using std::map;
 using boost::shared_ptr;
 using boost::weak_ptr;
 using boost::dynamic_pointer_cast;
@@ -49,10 +53,12 @@ struct Piece
        Piece (shared_ptr<Content> c, shared_ptr<Decoder> d)
                : content (c)
                , decoder (d)
+               , last_emission (0)
        {}
        
        shared_ptr<Content> content;
        shared_ptr<Decoder> decoder;
+       Time last_emission;
 };
        
 
@@ -125,13 +131,8 @@ Player::pass ()
                        continue;
                }
 
-               if (!_audio && dynamic_pointer_cast<AudioDecoder> ((*i)->decoder) && !dynamic_pointer_cast<VideoDecoder> ((*i)->decoder)) {
-                       continue;
-               }
-               
-               Time const t = (*i)->content->start() + (*i)->decoder->position();
-               if (t < earliest_t) {
-                       earliest_t = t;
+               if ((*i)->last_emission < earliest_t) {
+                       earliest_t = (*i)->last_emission;
                        earliest = *i;
                }
        }
@@ -142,39 +143,106 @@ Player::pass ()
        }
 
        earliest->decoder->pass ();
-       _position = earliest->content->start() + earliest->decoder->position ();
+       _position = earliest->last_emission;
 
         return false;
 }
 
 void
-Player::process_video (weak_ptr<Content> weak_content, shared_ptr<const Image> image, bool same, Time time)
+Player::process_video (weak_ptr<Piece> weak_piece, shared_ptr<const Image> image, bool same, VideoContent::Frame frame)
 {
-       shared_ptr<Content> content = weak_content.lock ();
-       if (!content) {
+       shared_ptr<Piece> piece = weak_piece.lock ();
+       if (!piece) {
+               return;
+       }
+
+       shared_ptr<VideoContent> content = dynamic_pointer_cast<VideoContent> (piece->content);
+       assert (content);
+
+       FrameRateConversion frc (content->video_frame_rate(), _film->dcp_video_frame_rate());
+       if (frc.skip && (frame % 2) == 1) {
                return;
        }
+
+       image = image->crop (content->crop(), true);
+
+       libdcp::Size const container_size = _video_container_size.get_value_or (_film->container()->size (_film->full_frame ()));
+       libdcp::Size const image_size = content->ratio()->size (container_size);
        
-       time += content->start ();
+       image = image->scale_and_convert_to_rgb (image_size, _film->scaler(), true);
+
+#if 0  
+       if (film->with_subtitles ()) {
+               shared_ptr<Subtitle> sub;
+               if (_timed_subtitle && _timed_subtitle->displayed_at (t)) {
+                       sub = _timed_subtitle->subtitle ();
+               }
+               
+               if (sub) {
+                       dcpomatic::Rect const tx = subtitle_transformed_area (
+                               float (image_size.width) / content->video_size().width,
+                               float (image_size.height) / content->video_size().height,
+                               sub->area(), film->subtitle_offset(), film->subtitle_scale()
+                               );
+                       
+                       shared_ptr<Image> im = sub->image()->scale (tx.size(), film->scaler(), true);
+                       image->alpha_blend (im, tx.position());
+               }
+       }
+#endif 
+
+       if (image_size != container_size) {
+               assert (image_size.width <= container_size.width);
+               assert (image_size.height <= container_size.height);
+               shared_ptr<Image> im (new SimpleImage (PIX_FMT_RGB24, container_size, true));
+               im->make_black ();
+               im->copy (image, Position ((container_size.width - image_size.width) / 2, (container_size.height - image_size.height) / 2));
+               image = im;
+       }
+
+       Time time = content->start() + (frame * frc.factor() * TIME_HZ / _film->dcp_video_frame_rate());
        
         Video (image, same, time);
+
+       if (frc.repeat) {
+               time += TIME_HZ / _film->dcp_video_frame_rate();
+               Video (image, true, time);
+       }
+
+       piece->last_emission = min (piece->last_emission, time);
 }
 
 void
-Player::process_audio (weak_ptr<Content> weak_content, shared_ptr<const AudioBuffers> audio, Time time)
+Player::process_audio (weak_ptr<Piece> weak_piece, shared_ptr<const AudioBuffers> audio, AudioContent::Frame frame)
 {
-       shared_ptr<Content> content = weak_content.lock ();
-       if (!content) {
+       shared_ptr<Piece> piece = weak_piece.lock ();
+       if (!piece) {
                return;
        }
-       
+
+       shared_ptr<AudioContent> content = dynamic_pointer_cast<AudioContent> (piece->content);
+       assert (content);
+
+       if (content->content_audio_frame_rate() != content->output_audio_frame_rate()) {
+               audio = resampler(content)->run (audio);
+       }
+
+       /* Remap channels */
+       shared_ptr<AudioBuffers> dcp_mapped (new AudioBuffers (_film->dcp_audio_channels(), audio->frames()));
+       dcp_mapped->make_silent ();
+       list<pair<int, libdcp::Channel> > map = content->audio_mapping().content_to_dcp ();
+       for (list<pair<int, libdcp::Channel> >::iterator i = map.begin(); i != map.end(); ++i) {
+               dcp_mapped->accumulate_channel (audio.get(), i->first, i->second);
+       }
+
         /* The time of this audio may indicate that some of our buffered audio is not going to
            be added to any more, so it can be emitted.
         */
 
-       time += content->start ();
+       Time const time = content->start() + (frame * TIME_HZ / _film->dcp_audio_frame_rate());
+       piece->last_emission = min (piece->last_emission, time);
 
-       cout << "Player gets " << audio->frames() << " @ " << time << " cf " << _next_audio << "\n";
+       cout << "Player gets " << dcp_mapped->frames() << " @ " << time << " cf " << _next_audio << "\n";
 
         if (time > _next_audio) {
                 /* We can emit some audio from our buffers */
@@ -224,10 +292,18 @@ Player::seek (Time t)
        }
 
        for (list<shared_ptr<Piece> >::iterator i = _pieces.begin(); i != _pieces.end(); ++i) {
-               Time s = t - (*i)->content->start ();
+               shared_ptr<VideoContent> vc = dynamic_pointer_cast<VideoContent> ((*i)->content);
+               if (!vc) {
+                       continue;
+               }
+               
+               Time s = t - vc->start ();
                s = max (static_cast<Time> (0), s);
-               s = min ((*i)->content->length(), s);
-               (*i)->decoder->seek (s);
+               s = min (vc->length(), s);
+
+               FrameRateConversion frc (vc->video_frame_rate(), _film->dcp_video_frame_rate());
+               VideoContent::Frame f = s * _film->dcp_video_frame_rate() / (frc.factor() * TIME_HZ);
+               dynamic_pointer_cast<VideoDecoder>((*i)->decoder)->seek (f);
        }
 
        /* XXX: don't seek audio because we don't need to... */
@@ -240,20 +316,15 @@ Player::seek_back ()
 
 }
 
-void
-Player::seek_forward ()
-{
-
-}
-
 void
 Player::add_black_piece (Time s, Time len)
 {
        shared_ptr<NullContent> nc (new NullContent (_film, s, len));
        nc->set_ratio (_film->container ());
        shared_ptr<BlackDecoder> bd (new BlackDecoder (_film, nc));
-       bd->Video.connect (bind (&Player::process_video, this, nc, _1, _2, _3));
-       _pieces.push_back (shared_ptr<Piece> (new Piece (nc, bd)));
+       shared_ptr<Piece> p (new Piece (nc, bd));
+       _pieces.push_back (p);
+       bd->Video.connect (bind (&Player::process_video, this, p, _1, _2, _3));
 }
 
 void
@@ -261,8 +332,9 @@ Player::add_silent_piece (Time s, Time len)
 {
        shared_ptr<NullContent> nc (new NullContent (_film, s, len));
        shared_ptr<SilenceDecoder> sd (new SilenceDecoder (_film, nc));
-       sd->Audio.connect (bind (&Player::process_audio, this, nc, _1, _2));
-       _pieces.push_back (shared_ptr<Piece> (new Piece (nc, sd)));
+       shared_ptr<Piece> p (new Piece (nc, sd));
+       _pieces.push_back (p);
+       sd->Audio.connect (bind (&Player::process_audio, this, p, _1, _2));
 }
 
 
@@ -279,7 +351,7 @@ Player::setup_pieces ()
        for (Playlist::ContentList::iterator i = content.begin(); i != content.end(); ++i) {
 
                shared_ptr<Decoder> decoder;
-               
+
                 /* XXX: into content? */
 
                shared_ptr<const FFmpegContent> fc = dynamic_pointer_cast<const FFmpegContent> (*i);
@@ -288,9 +360,6 @@ Player::setup_pieces ()
                        
                        fd->Video.connect (bind (&Player::process_video, this, *i, _1, _2, _3));
                        fd->Audio.connect (bind (&Player::process_audio, this, *i, _1, _2));
-                       if (_video_container_size) {
-                               fd->set_video_container_size (_video_container_size.get ());
-                       }
 
                        decoder = fd;
                }
@@ -310,9 +379,6 @@ Player::setup_pieces ()
                        if (!id) {
                                id.reset (new ImageMagickDecoder (_film, ic));
                                id->Video.connect (bind (&Player::process_video, this, *i, _1, _2, _3));
-                               if (_video_container_size) {
-                                       id->set_video_container_size (_video_container_size.get ());
-                               }
                        }
 
                        decoder = id;
@@ -390,10 +456,17 @@ void
 Player::set_video_container_size (libdcp::Size s)
 {
        _video_container_size = s;
-       for (list<shared_ptr<Piece> >::iterator i = _pieces.begin(); i != _pieces.end(); ++i) {
-               shared_ptr<VideoDecoder> vd = dynamic_pointer_cast<VideoDecoder> ((*i)->decoder);
-               if (vd) {
-                       vd->set_video_container_size (s);
-               }
+}
+
+shared_ptr<Resampler>
+Player::resampler (shared_ptr<AudioContent> c)
+{
+       map<shared_ptr<AudioContent>, shared_ptr<Resampler> >::iterator i = _resamplers.find (c);
+       if (i != _resamplers.end ()) {
+               return i->second;
        }
+       
+       shared_ptr<Resampler> r (new Resampler (c->content_audio_frame_rate(), c->output_audio_frame_rate(), c->audio_channels()));
+       _resamplers[c] = r;
+       return r;
 }
index e4fb832209025117a3f5c02414a3d429bf62bc5a..903d011d0aad99a64bc4650786f20c174e8b65a8 100644 (file)
 #include <list>
 #include <boost/shared_ptr.hpp>
 #include <boost/enable_shared_from_this.hpp>
-#include "video_source.h"
-#include "audio_source.h"
-#include "video_sink.h"
-#include "audio_sink.h"
 #include "playlist.h"
 #include "audio_buffers.h"
+#include "content.h"
 
 class Job;
 class Film;
 class Playlist;
 class AudioContent;
 class Piece;
+class Image;
+class Resampler;
 
 /** @class Player
  *  @brief A class which can `play' a Playlist; emitting its audio and video.
  */
  
-class Player : public VideoSource, public AudioSource, public boost::enable_shared_from_this<Player>
+class Player : public boost::enable_shared_from_this<Player>
 {
 public:
        Player (boost::shared_ptr<const Film>, boost::shared_ptr<const Playlist>);
@@ -51,7 +50,6 @@ public:
        bool pass ();
        void seek (Time);
        void seek_back ();
-       void seek_forward ();
 
        /** @return position that we are at; ie the time of the next thing we will emit on pass() */
        Time position () const {
@@ -60,10 +58,20 @@ public:
 
        void set_video_container_size (libdcp::Size);
 
+       /** Emitted when a video frame is ready.
+        *  First parameter is the video image.
+        *  Second parameter is true if the image is the same as the last one that was emitted.
+        *  Third parameter is the time.
+        */
+       boost::signals2::signal<void (boost::shared_ptr<const Image>, bool, Time)> Video;
+       
+       /** Emitted when some audio data is ready */
+       boost::signals2::signal<void (boost::shared_ptr<const AudioBuffers>, Time)> Audio;
+
 private:
 
-       void process_video (boost::weak_ptr<Content>, boost::shared_ptr<const Image>, bool, Time);
-       void process_audio (boost::weak_ptr<Content>, boost::shared_ptr<const AudioBuffers>, Time);
+       void process_video (boost::weak_ptr<Piece>, boost::shared_ptr<const Image>, bool, VideoContent::Frame);
+       void process_audio (boost::weak_ptr<Piece>, boost::shared_ptr<const AudioBuffers>, AudioContent::Frame);
        void setup_pieces ();
        void playlist_changed ();
        void content_changed (boost::weak_ptr<Content>, int);
@@ -71,6 +79,7 @@ private:
        void add_black_piece (Time, Time);
        void add_silent_piece (Time, Time);
        void flush ();
+       boost::shared_ptr<Resampler> resampler (boost::shared_ptr<AudioContent>);
 
        boost::shared_ptr<const Film> _film;
        boost::shared_ptr<const Playlist> _playlist;
@@ -85,6 +94,7 @@ private:
        AudioBuffers _audio_buffers;
        Time _next_audio;
        boost::optional<libdcp::Size> _video_container_size;
+       std::map<boost::shared_ptr<AudioContent>, boost::shared_ptr<Resampler> > _resamplers;
 };
 
 #endif
index e4f4c79f00ee5c8fcaa46e0a7ad17ca014105e81..2d243fe8f0e84060b0ffa20b93a96d08fbf7af31 100644 (file)
 #include <list>
 #include <boost/shared_ptr.hpp>
 #include <boost/enable_shared_from_this.hpp>
-#include "video_source.h"
-#include "audio_source.h"
-#include "video_sink.h"
-#include "audio_sink.h"
 #include "ffmpeg_content.h"
 #include "audio_mapping.h"
 
diff --git a/src/lib/resampler.cc b/src/lib/resampler.cc
new file mode 100644 (file)
index 0000000..1235b90
--- /dev/null
@@ -0,0 +1,61 @@
+extern "C" {
+#include "libavutil/channel_layout.h"
+}      
+#include "resampler.h"
+#include "audio_buffers.h"
+#include "exceptions.h"
+
+#include "i18n.h"
+
+using boost::shared_ptr;
+
+Resampler::Resampler (int in, int out, int channels)
+       : _in_rate (in)
+       , _out_rate (out)
+       , _channels (channels)
+{
+       /* We will be using planar float data when we call the
+          resampler.  As far as I can see, the audio channel
+          layout is not necessary for our purposes; it seems
+          only to be used get the number of channels and
+          decide if rematrixing is needed.  It won't be, since
+          input and output layouts are the same.
+       */
+
+       _swr_context = swr_alloc_set_opts (
+               0,
+               av_get_default_channel_layout (_channels),
+               AV_SAMPLE_FMT_FLTP,
+               _out_rate,
+               av_get_default_channel_layout (_channels),
+               AV_SAMPLE_FMT_FLTP,
+               _in_rate,
+               0, 0
+               );
+       
+       swr_init (_swr_context);
+}
+
+Resampler::~Resampler ()
+{
+       swr_free (&_swr_context);
+}
+
+shared_ptr<const AudioBuffers>
+Resampler::run (shared_ptr<const AudioBuffers> in)
+{
+       /* Compute the resampled frames count and add 32 for luck */
+       int const max_resampled_frames = ceil ((double) in->frames() * _out_rate / _in_rate) + 32;
+       shared_ptr<AudioBuffers> resampled (new AudioBuffers (_channels, max_resampled_frames));
+
+       int const resampled_frames = swr_convert (
+               _swr_context, (uint8_t **) resampled->data(), max_resampled_frames, (uint8_t const **) in->data(), in->frames()
+               );
+       
+       if (resampled_frames < 0) {
+               throw EncodeError (_("could not run sample-rate converter"));
+       }
+       
+       resampled->set_frames (resampled_frames);
+       return resampled;
+}      
diff --git a/src/lib/resampler.h b/src/lib/resampler.h
new file mode 100644 (file)
index 0000000..cda7189
--- /dev/null
@@ -0,0 +1,21 @@
+#include <boost/shared_ptr.hpp>
+extern "C" {
+#include <libswresample/swresample.h>
+}
+
+class AudioBuffers;
+
+class Resampler
+{
+public:
+       Resampler (int, int, int);
+       ~Resampler ();
+
+       boost::shared_ptr<const AudioBuffers> run (boost::shared_ptr<const AudioBuffers>);
+
+private:       
+       SwrContext* _swr_context;
+       int _in_rate;
+       int _out_rate;
+       int _channels;
+};
index 0380117ce2efb7ec0eb06da07dcb379590009187..8cff80ed2a950ae6a9c36eab2c1039bc8657d624 100644 (file)
@@ -27,7 +27,8 @@ using boost::shared_ptr;
 
 SilenceDecoder::SilenceDecoder (shared_ptr<const Film> f, shared_ptr<NullContent> c)
        : Decoder (f)
-       , AudioDecoder (f, c)
+       , AudioDecoder (f)
+       , _null_content (c)
 {
        
 }
@@ -38,50 +39,14 @@ SilenceDecoder::pass ()
        shared_ptr<const Film> film = _film.lock ();
        assert (film);
 
-       Time const this_time = min (_audio_content->length() - _next_audio, TIME_HZ / 2);
-       cout << "silence emit " << this_time << " from " << _audio_content->length() << "\n";
-       shared_ptr<AudioBuffers> data (new AudioBuffers (film->dcp_audio_channels(), film->time_to_audio_frames (this_time)));
+       AudioContent::Frame const this_time = min (_null_content->audio_length() - _next_audio_frame, int64_t (_null_content->output_audio_frame_rate() / 2));
+       shared_ptr<AudioBuffers> data (new AudioBuffers (film->dcp_audio_channels(), this_time));
        data->make_silent ();
-       audio (data, _next_audio);
-}
-
-void
-SilenceDecoder::seek (Time t)
-{
-       _next_audio = t;
-}
-
-void
-SilenceDecoder::seek_back ()
-{
-       boost::shared_ptr<const Film> f = _film.lock ();
-       if (!f) {
-               return;
-       }
-
-       _next_audio -= f->video_frames_to_time (2);
-}
-
-void
-SilenceDecoder::seek_forward ()
-{
-       boost::shared_ptr<const Film> f = _film.lock ();
-       if (!f) {
-               return;
-       }
-
-       _next_audio += f->video_frames_to_time (1);
-}
-
-Time
-SilenceDecoder::position () const
-{
-       return _next_audio;
+       audio (data, _next_audio_frame);
 }
 
 bool
 SilenceDecoder::done () const
 {
-       return audio_done ();
+       return _next_audio_frame > _null_content->audio_length ();
 }
-
index a8a335d39fd78d8b8d37c25f81ef72d815b14218..10c18c69f5ebc48d87912507287f7b23a54f5e86 100644 (file)
@@ -29,9 +29,8 @@ public:
        SilenceDecoder (boost::shared_ptr<const Film>, boost::shared_ptr<NullContent>);
 
        void pass ();
-       void seek (Time);
-       void seek_back ();
-       void seek_forward ();
-       Time position () const;
        bool done () const;
+
+private:
+       boost::shared_ptr<NullContent> _null_content;
 };
index 8eede89f4365241821ccf38a6db5b9c16acba08d..beee7cd9d0aa33c37dfa31d789b4ef076d3eacfa 100644 (file)
@@ -46,7 +46,7 @@ SndfileContent::SndfileContent (shared_ptr<const Film> f, shared_ptr<const cxml:
        , AudioContent (f, node)
 {
        _audio_channels = node->number_child<int> ("AudioChannels");
-       _audio_length = node->number_child<ContentAudioFrame> ("AudioLength");
+       _audio_length = node->number_child<AudioContent::Frame> ("AudioLength");
        _audio_frame_rate = node->number_child<int> ("AudioFrameRate");
        _audio_mapping = AudioMapping (node->node_child ("AudioMapping"));
 }
index 30eb23a4ed38e89bea2883274306647e9568d9a5..876d66088c6f7a5a84f4b289de622d43256d1d97 100644 (file)
@@ -49,7 +49,7 @@ public:
                return _audio_channels;
        }
        
-        ContentAudioFrame audio_length () const {
+        AudioContent::Frame audio_length () const {
                boost::mutex::scoped_lock lm (_mutex);
                return _audio_length;
        }
@@ -72,7 +72,7 @@ public:
 
 private:
        int _audio_channels;
-       ContentAudioFrame _audio_length;
+       AudioContent::Frame _audio_length;
        int _audio_frame_rate;
        AudioMapping _audio_mapping;
 };
index ff56f106260330f5b7d7c9401ed5cf6c4658e7a9..9030021e7c672d551eb309c788c4b4d9f7a7299a 100644 (file)
@@ -35,7 +35,7 @@ using boost::shared_ptr;
 
 SndfileDecoder::SndfileDecoder (shared_ptr<const Film> f, shared_ptr<const SndfileContent> c)
        : Decoder (f)
-       , AudioDecoder (f, c)
+       , AudioDecoder (f)
        , _sndfile_content (c)
        , _deinterleave_buffer (0)
 {
@@ -100,7 +100,7 @@ SndfileDecoder::audio_channels () const
        return _info.channels;
 }
 
-ContentAudioFrame
+AudioContent::Frame
 SndfileDecoder::audio_length () const
 {
        return _info.frames;
@@ -112,14 +112,8 @@ SndfileDecoder::audio_frame_rate () const
        return _info.samplerate;
 }
 
-Time
-SndfileDecoder::position () const
-{
-       return _next_audio;
-}
-
 bool
 SndfileDecoder::done () const
 {
-       return audio_done ();
+       return _next_audio_frame > _sndfile_content->audio_length ();
 }
index e904340b320fb612de841d6a0cc4e19690973770..77fa6d17734da4096757dae504a759c9925e0e8b 100644 (file)
@@ -30,21 +30,17 @@ public:
        ~SndfileDecoder ();
 
        void pass ();
-       void seek (Time) {}
-       void seek_back () {}
-       void seek_forward () {}
-       Time position () const;
        bool done () const;
 
        int audio_channels () const;
-       ContentAudioFrame audio_length () const;
+       AudioContent::Frame audio_length () const;
        int audio_frame_rate () const;
 
 private:
        boost::shared_ptr<const SndfileContent> _sndfile_content;
        SNDFILE* _sndfile;
        SF_INFO _info;
-       ContentAudioFrame _done;
-       ContentAudioFrame _remaining;
+       AudioContent::Frame _done;
+       AudioContent::Frame _remaining;
        float* _deinterleave_buffer;
 };
index 5e719f9776774237de7b8d982fc002fb20b2d07e..7013f1d7d9004a635ac0732a8f77c638f92f6550 100644 (file)
@@ -27,8 +27,7 @@
 
 #include "i18n.h"
 
-using namespace std;
-using namespace boost;
+using boost::shared_ptr;
 using libdcp::Size;
 
 /** Construct a TimedSubtitle.  This is a subtitle image, position,
index f4637a05cf1c0f983d9a26bff6edca680b6f5d39..f4a52639a3cc5a887f3ae7c79991171e41527ac4 100644 (file)
 
 using std::string;
 using boost::shared_ptr;
+using boost::weak_ptr;
 using boost::dynamic_pointer_cast;
 
+static void
+video_proxy (weak_ptr<Encoder> encoder, shared_ptr<const Image> image, bool same)
+{
+       shared_ptr<Encoder> e = encoder.lock ();
+       if (e) {
+               e->process_video (image, same);
+       }
+}
+
+static void
+audio_proxy (weak_ptr<Encoder> encoder, shared_ptr<const AudioBuffers> audio)
+{
+       shared_ptr<Encoder> e = encoder.lock ();
+       if (e) {
+               e->process_audio (audio);
+       }
+}
+
 /** Construct a transcoder using a Decoder that we create and a supplied Encoder.
  *  @param f Film that we are transcoding.
  *  @param j Job that we are running under, or 0.
@@ -48,8 +67,8 @@ Transcoder::Transcoder (shared_ptr<const Film> f, shared_ptr<Job> j)
        , _player (f->player ())
        , _encoder (new Encoder (f, j))
 {
-       _player->connect_video (_encoder);
-       _player->connect_audio (_encoder);
+       _player->Video.connect (bind (video_proxy, _encoder, _1, _2));
+       _player->Audio.connect (bind (audio_proxy, _encoder, _1));
 }
 
 void
index 70262afb0a7c670abf0fd9bc3f23eda9f072b17b..33f8239d88cbee89ace3e07b51acd94d67b27fd9 100644 (file)
@@ -27,8 +27,6 @@
 
 class Content;
 
-typedef int64_t ContentAudioFrame;
-typedef int     ContentVideoFrame;
 typedef int64_t Time;
 #define TIME_MAX INT64_MAX
 #define TIME_HZ  ((Time) 96000)
index eda0d0236767300f349c146660d29a1f24a24595..d425fc8fefe009c3f0ee44ed94eab18c1d969285 100644 (file)
@@ -710,7 +710,7 @@ ensure_ui_thread ()
  *  @return Equivalent number of audio frames for `v'.
  */
 int64_t
-video_frames_to_audio_frames (ContentVideoFrame v, float audio_sample_rate, float frames_per_second)
+video_frames_to_audio_frames (VideoContent::Frame v, float audio_sample_rate, float frames_per_second)
 {
        return ((int64_t) v * audio_sample_rate / frames_per_second);
 }
index 42514a12c92f9f863944ab0018d16b5b8e573533..c68bb4f1619614b29e91b9ceb7f45c88898547b1 100644 (file)
@@ -38,6 +38,7 @@ extern "C" {
 }
 #include "compose.hpp"
 #include "types.h"
+#include "video_content.h"
 
 #ifdef DCPOMATIC_DEBUG
 #define TIMING(...) _film->log()->microsecond_log (String::compose (__VA_ARGS__), Log::TIMING);
@@ -152,7 +153,7 @@ private:
        int _timeout;
 };
 
-extern int64_t video_frames_to_audio_frames (ContentVideoFrame v, float audio_sample_rate, float frames_per_second);
+extern int64_t video_frames_to_audio_frames (VideoContent::Frame v, float audio_sample_rate, float frames_per_second);
 
 class LocaleGuard
 {
index f9de6aa2146b0f0ff5a91de4b214ddc37b83442a..3818fa7920fb4b86d6cbf990858a192473428a1b 100644 (file)
@@ -22,6 +22,7 @@
 #include "video_content.h"
 #include "video_examiner.h"
 #include "ratio.h"
+#include "compose.hpp"
 
 #include "i18n.h"
 
@@ -37,7 +38,7 @@ using boost::shared_ptr;
 using boost::lexical_cast;
 using boost::optional;
 
-VideoContent::VideoContent (shared_ptr<const Film> f, Time s, ContentVideoFrame len)
+VideoContent::VideoContent (shared_ptr<const Film> f, Time s, VideoContent::Frame len)
        : Content (f, s)
        , _video_length (len)
        , _video_frame_rate (0)
@@ -58,7 +59,7 @@ VideoContent::VideoContent (shared_ptr<const Film> f, boost::filesystem::path p)
 VideoContent::VideoContent (shared_ptr<const Film> f, shared_ptr<const cxml::Node> node)
        : Content (f, node)
 {
-       _video_length = node->number_child<ContentVideoFrame> ("VideoLength");
+       _video_length = node->number_child<VideoContent::Frame> ("VideoLength");
        _video_size.width = node->number_child<int> ("VideoWidth");
        _video_size.height = node->number_child<int> ("VideoHeight");
        _video_frame_rate = node->number_child<float> ("VideoFrameRate");
index 23bcaa89bad7b945a041b4c039c54bdc36dd3f69..372cab3bd3c48ab5874188800cf7b41e541ebea0 100644 (file)
@@ -21,7 +21,6 @@
 #define DCPOMATIC_VIDEO_CONTENT_H
 
 #include "content.h"
-#include "util.h"
 
 class VideoExaminer;
 class Ratio;
@@ -38,7 +37,9 @@ public:
 class VideoContent : public virtual Content
 {
 public:
-       VideoContent (boost::shared_ptr<const Film>, Time, ContentVideoFrame);
+       typedef int Frame;
+
+       VideoContent (boost::shared_ptr<const Film>, Time, VideoContent::Frame);
        VideoContent (boost::shared_ptr<const Film>, boost::filesystem::path);
        VideoContent (boost::shared_ptr<const Film>, boost::shared_ptr<const cxml::Node>);
        VideoContent (VideoContent const &);
@@ -46,7 +47,7 @@ public:
        void as_xml (xmlpp::Node *) const;
        virtual std::string information () const;
 
-       ContentVideoFrame video_length () const {
+       VideoContent::Frame video_length () const {
                boost::mutex::scoped_lock lm (_mutex);
                return _video_length;
        }
@@ -82,7 +83,7 @@ public:
 protected:
        void take_from_video_examiner (boost::shared_ptr<VideoExaminer>);
 
-       ContentVideoFrame _video_length;
+       VideoContent::Frame _video_length;
 
 private:
        libdcp::Size _video_size;
index fcc0ccf41eeafcc5491e2fd6f7569bd3250d7131..b5cc7d15887717e9a506141d74ab0066f265c3be 100644 (file)
 using std::cout;
 using boost::shared_ptr;
 
-VideoDecoder::VideoDecoder (shared_ptr<const Film> f, shared_ptr<const VideoContent> c)
+VideoDecoder::VideoDecoder (shared_ptr<const Film> f)
        : Decoder (f)
-       , _next_video (0)
-       , _video_content (c)
-       , _frame_rate_conversion (c->video_frame_rate(), f->dcp_video_frame_rate())
-       , _odd (false)
+       , _next_video_frame (0)
 {
 
 }
 
-/** Called by subclasses when some video is ready.
- *  @param image frame to emit.
- *  @param same true if this frame is the same as the last one passed to this call.
- *  @param t Time of the frame within the source.
- */
 void
-VideoDecoder::video (shared_ptr<Image> image, bool same, Time t)
+VideoDecoder::video (shared_ptr<const Image> image, bool same, VideoContent::Frame frame)
 {
-       if (_frame_rate_conversion.skip && _odd) {
-               _odd = !_odd;
-               return;
-       }
-
-       image = image->crop (_video_content->crop(), true);
-
-       shared_ptr<const Film> film = _film.lock ();
-       assert (film);
-       
-       libdcp::Size const container_size = _video_container_size.get_value_or (film->container()->size (film->full_frame ()));
-       libdcp::Size const image_size = _video_content->ratio()->size (container_size);
-       
-       shared_ptr<Image> out = image->scale_and_convert_to_rgb (image_size, film->scaler(), true);
-
-       if (film->with_subtitles ()) {
-               shared_ptr<Subtitle> sub;
-               if (_timed_subtitle && _timed_subtitle->displayed_at (t)) {
-                       sub = _timed_subtitle->subtitle ();
-               }
-               
-               if (sub) {
-                       dcpomatic::Rect const tx = subtitle_transformed_area (
-                               float (image_size.width) / _video_content->video_size().width,
-                               float (image_size.height) / _video_content->video_size().height,
-                               sub->area(), film->subtitle_offset(), film->subtitle_scale()
-                               );
-                       
-                       shared_ptr<Image> im = sub->image()->scale (tx.size(), film->scaler(), true);
-                       out->alpha_blend (im, tx.position());
-               }
-       }
-
-       if (image_size != container_size) {
-               assert (image_size.width <= container_size.width);
-               assert (image_size.height <= container_size.height);
-               shared_ptr<Image> im (new SimpleImage (PIX_FMT_RGB24, container_size, true));
-               im->make_black ();
-               im->copy (out, Position ((container_size.width - image_size.width) / 2, (container_size.height - image_size.height) / 2));
-               out = im;
-       }
-               
-       Video (out, same, t);
-
-       if (_frame_rate_conversion.repeat) {
-               Video (image, true, t + film->video_frames_to_time (1));
-               _next_video = t + film->video_frames_to_time (2);
-       } else {
-               _next_video = t + film->video_frames_to_time (1);
-       }
-
-       _odd = !_odd;
+        Video (image, same, frame);
+       _next_video_frame = frame + 1;
 }
 
+#if 0
+
 /** Called by subclasses when a subtitle is ready.
  *  s may be 0 to say that there is no current subtitle.
  *  @param s New current subtitle, or 0.
@@ -114,40 +58,5 @@ VideoDecoder::subtitle (shared_ptr<TimedSubtitle> s)
                _timed_subtitle->subtitle()->set_position (Position (p.x - _video_content->crop().left, p.y - _video_content->crop().top));
        }
 }
+#endif
 
-bool
-VideoDecoder::video_done () const
-{
-       shared_ptr<const Film> film = _film.lock ();
-       assert (film);
-       
-       return (_video_content->length() - _next_video) < film->video_frames_to_time (1);
-}
-
-void
-VideoDecoder::seek (Time t)
-{
-       _next_video = t;
-}
-
-void
-VideoDecoder::seek_back ()
-{
-       shared_ptr<const Film> film = _film.lock ();
-       assert (film);
-       _next_video -= film->video_frames_to_time (1);
-}
-
-void
-VideoDecoder::seek_forward ()
-{
-       shared_ptr<const Film> film = _film.lock ();
-       assert (film);
-       _next_video += film->video_frames_to_time (1);
-}
-
-void
-VideoDecoder::set_video_container_size (libdcp::Size s)
-{
-       _video_container_size = s;
-}
index 8de76c10f7a2653b2d79702b9a10301bfce5b221..c86248417bdd8b4f8147fd149fb6fbe3279e2775 100644 (file)
 #ifndef DCPOMATIC_VIDEO_DECODER_H
 #define DCPOMATIC_VIDEO_DECODER_H
 
-#include "video_source.h"
 #include "decoder.h"
 #include "util.h"
 
 class VideoContent;
 
-class VideoDecoder : public VideoSource, public virtual Decoder
+class VideoDecoder : public virtual Decoder
 {
 public:
-       VideoDecoder (boost::shared_ptr<const Film>, boost::shared_ptr<const VideoContent>);
+       VideoDecoder (boost::shared_ptr<const Film>);
 
-       virtual void seek (Time);
-       virtual void seek_back ();
-       virtual void seek_forward ();
-       
-       void set_video_container_size (libdcp::Size);
+       virtual void seek (VideoContent::Frame) = 0;
+       virtual void seek_back () = 0;
 
-protected:
+       /** Emitted when a video frame is ready.
+        *  First parameter is the video image.
+        *  Second parameter is true if the image is the same as the last one that was emitted.
+        *  Third parameter is the frame within our source.
+        */
+       boost::signals2::signal<void (boost::shared_ptr<const Image>, bool, VideoContent::Frame)> Video;
        
-       void video (boost::shared_ptr<Image>, bool, Time);
-       void subtitle (boost::shared_ptr<TimedSubtitle>);
-       bool video_done () const;
-
-       Time _next_video;
-       boost::shared_ptr<const VideoContent> _video_content;
-
-private:
-       boost::shared_ptr<TimedSubtitle> _timed_subtitle;
-       FrameRateConversion _frame_rate_conversion;
-       bool _odd;
-       boost::optional<libdcp::Size> _video_container_size;
+protected:
+
+       void video (boost::shared_ptr<const Image>, bool, VideoContent::Frame);
+       VideoContent::Frame _next_video_frame;
 };
 
 #endif
index 2f713291b5a5165d42976196607ab396dd7fe50f..72f6ccc12cab575f2a20e297ecdfed5bbb967344 100644 (file)
 
 #include <libdcp/types.h>
 #include "types.h"
+#include "video_content.h"
 
 class VideoExaminer
 {
 public:
        virtual float video_frame_rate () const = 0;
        virtual libdcp::Size video_size () const = 0;
-       virtual ContentVideoFrame video_length () const = 0;
+       virtual VideoContent::Frame video_length () const = 0;
 };
diff --git a/src/lib/video_sink.h b/src/lib/video_sink.h
deleted file mode 100644 (file)
index 957aeb4..0000000
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
-    Copyright (C) 2012 Carl Hetherington <cth@carlh.net>
-
-    This program is free software; you can redistribute it and/or modify
-    it under the terms of the GNU General Public License as published by
-    the Free Software Foundation; either version 2 of the License, or
-    (at your option) any later version.
-
-    This program is distributed in the hope that it will be useful,
-    but WITHOUT ANY WARRANTY; without even the implied warranty of
-    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-    GNU General Public License for more details.
-
-    You should have received a copy of the GNU General Public License
-    along with this program; if not, write to the Free Software
-    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
-*/
-
-#ifndef DCPOMATIC_VIDEO_SINK_H
-#define DCPOMATIC_VIDEO_SINK_H
-
-#include <boost/shared_ptr.hpp>
-#include "util.h"
-
-class Subtitle;
-class Image;
-
-class VideoSink
-{
-public:
-       /** Call with a frame of video.
-        *  @param i Video frame image.
-        *  @param same true if i is the same as last time we were called.
-        */
-       virtual void process_video (boost::shared_ptr<const Image> i, bool same, Time) = 0;
-};
-
-#endif
diff --git a/src/lib/video_source.cc b/src/lib/video_source.cc
deleted file mode 100644 (file)
index 824587b..0000000
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
-    Copyright (C) 2012 Carl Hetherington <cth@carlh.net>
-
-    This program is free software; you can redistribute it and/or modify
-    it under the terms of the GNU General Public License as published by
-    the Free Software Foundation; either version 2 of the License, or
-    (at your option) any later version.
-
-    This program is distributed in the hope that it will be useful,
-    but WITHOUT ANY WARRANTY; without even the implied warranty of
-    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-    GNU General Public License for more details.
-
-    You should have received a copy of the GNU General Public License
-    along with this program; if not, write to the Free Software
-    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
-*/
-
-#include "video_source.h"
-#include "video_sink.h"
-
-using boost::shared_ptr;
-using boost::weak_ptr;
-using boost::bind;
-
-static void
-process_video_proxy (weak_ptr<VideoSink> sink, shared_ptr<const Image> image, bool same, Time time)
-{
-       shared_ptr<VideoSink> p = sink.lock ();
-       if (p) {
-               p->process_video (image, same, time);
-       }
-}
-
-void
-VideoSource::connect_video (shared_ptr<VideoSink> s)
-{
-       /* If we bind, say, a Player (as the VideoSink) to a Decoder (which is owned
-          by the Player) we create a cycle.  Use a weak_ptr to break it.
-       */
-       Video.connect (bind (process_video_proxy, weak_ptr<VideoSink> (s), _1, _2, _3));
-}
-
diff --git a/src/lib/video_source.h b/src/lib/video_source.h
deleted file mode 100644 (file)
index 9242af4..0000000
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
-    Copyright (C) 2012 Carl Hetherington <cth@carlh.net>
-
-    This program is free software; you can redistribute it and/or modify
-    it under the terms of the GNU General Public License as published by
-    the Free Software Foundation; either version 2 of the License, or
-    (at your option) any later version.
-
-    This program is distributed in the hope that it will be useful,
-    but WITHOUT ANY WARRANTY; without even the implied warranty of
-    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-    GNU General Public License for more details.
-
-    You should have received a copy of the GNU General Public License
-    along with this program; if not, write to the Free Software
-    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
-*/
-
-/** @file  src/video_source.h
- *  @brief Parent class for classes which emit video data.
- */
-
-#ifndef DCPOMATIC_VIDEO_SOURCE_H
-#define DCPOMATIC_VIDEO_SOURCE_H
-
-#include <boost/shared_ptr.hpp>
-#include <boost/signals2.hpp>
-#include "util.h"
-
-class VideoSink;
-class Subtitle;
-class Image;
-
-/** @class VideoSource
- *  @param A class that emits video data.
- */
-class VideoSource
-{
-public:
-
-       /** Emitted when a video frame is ready.
-        *  First parameter is the video image.
-        *  Second parameter is true if the image is the same as the last one that was emitted.
-        *  Third parameter is the time relative to the start of this source's content.
-        */
-       boost::signals2::signal<void (boost::shared_ptr<const Image>, bool, Time)> Video;
-
-       void connect_video (boost::shared_ptr<VideoSink>);
-};
-
-#endif
index d0f10299821d95778631e943c8d4b2e57ef0a6e1..bffc2d5e6b43ce367a14a315ad8393b03127d72f 100644 (file)
@@ -8,10 +8,8 @@ sources = """
           audio_content.cc
           audio_decoder.cc
           audio_mapping.cc
-          audio_source.cc
           black_decoder.cc
           config.cc
-          combiner.cc
           content.cc
           cross.cc
           dci_metadata.cc
@@ -41,6 +39,7 @@ sources = """
           player.cc
           playlist.cc
           ratio.cc
+          resampler.cc
           scp_dcp_job.cc
           scaler.cc
           server.cc
@@ -57,7 +56,6 @@ sources = """
           util.cc
           video_content.cc
           video_decoder.cc
-          video_source.cc
           writer.cc
           """