Split examiner parts off decoder.
authorCarl Hetherington <cth@carlh.net>
Fri, 21 Jun 2013 16:38:50 +0000 (17:38 +0100)
committerCarl Hetherington <cth@carlh.net>
Fri, 21 Jun 2013 16:38:50 +0000 (17:38 +0100)
20 files changed:
src/lib/ffmpeg.cc [new file with mode: 0644]
src/lib/ffmpeg.h [new file with mode: 0644]
src/lib/ffmpeg_content.cc
src/lib/ffmpeg_decoder.cc
src/lib/ffmpeg_decoder.h
src/lib/ffmpeg_examiner.cc [new file with mode: 0644]
src/lib/ffmpeg_examiner.h [new file with mode: 0644]
src/lib/filter_graph.cc
src/lib/imagemagick.h [new file with mode: 0644]
src/lib/imagemagick_content.cc
src/lib/imagemagick_decoder.cc
src/lib/imagemagick_decoder.h
src/lib/imagemagick_examiner.cc [new file with mode: 0644]
src/lib/imagemagick_examiner.h [new file with mode: 0644]
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 [new file with mode: 0644]
src/lib/wscript

diff --git a/src/lib/ffmpeg.cc b/src/lib/ffmpeg.cc
new file mode 100644 (file)
index 0000000..0d897ab
--- /dev/null
@@ -0,0 +1,138 @@
+/*
+    Copyright (C) 2013 Carl Hetherington <cth@carlh.net>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+extern "C" {
+#include <libavcodec/avcodec.h>
+#include <libavformat/avformat.h>
+#include <libswscale/swscale.h>
+#include <libpostproc/postprocess.h>
+}
+#include "ffmpeg.h"
+#include "ffmpeg_content.h"
+#include "exceptions.h"
+
+#include "i18n.h"
+
+using std::string;
+using std::stringstream;
+using boost::shared_ptr;
+
+boost::mutex FFmpeg::_mutex;
+
+FFmpeg::FFmpeg (boost::shared_ptr<const FFmpegContent> c)
+       : _ffmpeg_content (c)
+       , _format_context (0)
+       , _frame (0)
+       , _video_stream (-1)
+       , _video_codec_context (0)
+       , _video_codec (0)
+       , _audio_codec_context (0)
+       , _audio_codec (0)
+{
+       setup_general ();
+       setup_video ();
+       setup_audio ();
+}
+
+FFmpeg::~FFmpeg ()
+{
+       boost::mutex::scoped_lock lm (_mutex);
+       
+       if (_audio_codec_context) {
+               avcodec_close (_audio_codec_context);
+       }
+
+       if (_video_codec_context) {
+               avcodec_close (_video_codec_context);
+       }
+
+       av_free (_frame);
+       
+       avformat_close_input (&_format_context);
+}
+
+void
+FFmpeg::setup_general ()
+{
+       av_register_all ();
+
+       if (avformat_open_input (&_format_context, _ffmpeg_content->file().string().c_str(), 0, 0) < 0) {
+               throw OpenFileError (_ffmpeg_content->file().string ());
+       }
+
+       if (avformat_find_stream_info (_format_context, 0) < 0) {
+               throw DecodeError (_("could not find stream information"));
+       }
+
+       /* Find video stream */
+
+       for (uint32_t i = 0; i < _format_context->nb_streams; ++i) {
+               AVStream* s = _format_context->streams[i];
+               if (s->codec->codec_type == AVMEDIA_TYPE_VIDEO) {
+                       _video_stream = i;
+               }
+       }
+
+       if (_video_stream < 0) {
+               throw DecodeError (N_("could not find video stream"));
+       }
+
+       _frame = avcodec_alloc_frame ();
+       if (_frame == 0) {
+               throw DecodeError (N_("could not allocate frame"));
+       }
+}
+
+void
+FFmpeg::setup_video ()
+{
+       boost::mutex::scoped_lock lm (_mutex);
+       
+       _video_codec_context = _format_context->streams[_video_stream]->codec;
+       _video_codec = avcodec_find_decoder (_video_codec_context->codec_id);
+
+       if (_video_codec == 0) {
+               throw DecodeError (_("could not find video decoder"));
+       }
+
+       if (avcodec_open2 (_video_codec_context, _video_codec, 0) < 0) {
+               throw DecodeError (N_("could not open video decoder"));
+       }
+}
+
+void
+FFmpeg::setup_audio ()
+{
+       boost::mutex::scoped_lock lm (_mutex);
+       
+       if (!_ffmpeg_content->audio_stream ()) {
+               return;
+       }
+
+       _audio_codec_context = _format_context->streams[_ffmpeg_content->audio_stream()->id]->codec;
+       _audio_codec = avcodec_find_decoder (_audio_codec_context->codec_id);
+
+       if (_audio_codec == 0) {
+               throw DecodeError (_("could not find audio decoder"));
+       }
+
+       if (avcodec_open2 (_audio_codec_context, _audio_codec, 0) < 0) {
+               throw DecodeError (N_("could not open audio decoder"));
+       }
+}
diff --git a/src/lib/ffmpeg.h b/src/lib/ffmpeg.h
new file mode 100644 (file)
index 0000000..dcafe17
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+    Copyright (C) 2013 Carl Hetherington <cth@carlh.net>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include <vector>
+#include <boost/shared_ptr.hpp>
+#include <boost/thread/mutex.hpp>
+extern "C" {
+#include <libavcodec/avcodec.h>
+}
+
+struct AVFilterGraph;
+struct AVCodecContext;
+struct AVFilterContext;
+struct AVFormatContext;
+struct AVFrame;
+struct AVBufferContext;
+struct AVCodec;
+struct AVStream;
+
+class FFmpegContent;
+
+class FFmpeg
+{
+public:
+       FFmpeg (boost::shared_ptr<const FFmpegContent>);
+       virtual ~FFmpeg ();
+
+       boost::shared_ptr<const FFmpegContent> ffmpeg_content () const {
+               return _ffmpeg_content;
+       }
+
+protected:
+       boost::shared_ptr<const FFmpegContent> _ffmpeg_content;
+       AVFormatContext* _format_context;
+       AVPacket _packet;
+       AVFrame* _frame;
+       int _video_stream;
+
+       AVCodecContext* _video_codec_context;
+       AVCodec* _video_codec;
+       AVCodecContext* _audio_codec_context;    ///< may be 0 if there is no audio
+       AVCodec* _audio_codec;                   ///< may be 0 if there is no audio
+
+       /* It would appear (though not completely verified) that one must have
+          a mutex around calls to avcodec_open* and avcodec_close... and here
+          it is.
+       */
+       static boost::mutex _mutex;
+
+private:
+       void setup_general ();
+       void setup_video ();
+       void setup_audio ();
+};
index b030fe397bc1cc3b188a4e9567e770893ee14ef7..43e88c428948109d20d1189f208b9571a9924dae 100644 (file)
 
 #include <libcxml/cxml.h>
 #include "ffmpeg_content.h"
-#include "ffmpeg_decoder.h"
+#include "ffmpeg_examiner.h"
 #include "compose.hpp"
 #include "job.h"
 #include "util.h"
 #include "filter.h"
+#include "film.h"
 #include "log.h"
 
 #include "i18n.h"
@@ -130,29 +131,29 @@ FFmpegContent::examine (shared_ptr<Job> job)
        shared_ptr<const Film> film = _film.lock ();
        assert (film);
 
-       shared_ptr<FFmpegDecoder> decoder (new FFmpegDecoder (film, shared_from_this (), true, false));
+       shared_ptr<FFmpegExaminer> examiner (new FFmpegExaminer (shared_from_this ()));
 
        ContentVideoFrame video_length = 0;
-       video_length = decoder->video_length ();
-       film->log()->log (String::compose ("Video length obtained from header as %1 frames", decoder->video_length ()));
+       video_length = examiner->video_length ();
+       film->log()->log (String::compose ("Video length obtained from header as %1 frames", examiner->video_length ()));
 
         {
                 boost::mutex::scoped_lock lm (_mutex);
 
                 _video_length = video_length;
 
-                _subtitle_streams = decoder->subtitle_streams ();
+                _subtitle_streams = examiner->subtitle_streams ();
                 if (!_subtitle_streams.empty ()) {
                         _subtitle_stream = _subtitle_streams.front ();
                 }
                 
-                _audio_streams = decoder->audio_streams ();
+                _audio_streams = examiner->audio_streams ();
                 if (!_audio_streams.empty ()) {
                         _audio_stream = _audio_streams.front ();
                 }
         }
 
-        take_from_video_decoder (decoder);
+        take_from_video_examiner (examiner);
 
         signal_changed (ContentProperty::LENGTH);
         signal_changed (FFmpegContentProperty::SUBTITLE_STREAMS);
index 2e586b8f7fcdfc486abec542c052297ac1bb8bea..b1f8aa3d0ae6112733998ee0c7308ee62428fd04 100644 (file)
 #include <iostream>
 #include <stdint.h>
 #include <boost/lexical_cast.hpp>
+#include <sndfile.h>
 extern "C" {
 #include <libavcodec/avcodec.h>
 #include <libavformat/avformat.h>
-#include <libswscale/swscale.h>
-#include <libpostproc/postprocess.h>
 }
-#include <sndfile.h>
 #include "film.h"
 #include "filter.h"
 #include "exceptions.h"
@@ -59,162 +57,26 @@ using boost::optional;
 using boost::dynamic_pointer_cast;
 using libdcp::Size;
 
-boost::mutex FFmpegDecoder::_mutex;
-
 FFmpegDecoder::FFmpegDecoder (shared_ptr<const Film> f, shared_ptr<const FFmpegContent> c, bool video, bool audio)
        : Decoder (f)
        , VideoDecoder (f, c)
        , AudioDecoder (f, c)
-       , _ffmpeg_content (c)
-       , _format_context (0)
-       , _video_stream (-1)
-       , _frame (0)
-       , _video_codec_context (0)
-       , _video_codec (0)
-       , _audio_codec_context (0)
-       , _audio_codec (0)
+       , FFmpeg (c)
        , _subtitle_codec_context (0)
        , _subtitle_codec (0)
        , _decode_video (video)
        , _decode_audio (audio)
 {
-       setup_general ();
-       setup_video ();
-       setup_audio ();
        setup_subtitle ();
 }
 
 FFmpegDecoder::~FFmpegDecoder ()
 {
-       boost::mutex::scoped_lock lm (_mutex);
-       
-       if (_audio_codec_context) {
-               avcodec_close (_audio_codec_context);
-       }
-
-       if (_video_codec_context) {
-               avcodec_close (_video_codec_context);
-       }
-
        if (_subtitle_codec_context) {
                avcodec_close (_subtitle_codec_context);
        }
-
-       av_free (_frame);
-       
-       avformat_close_input (&_format_context);
 }      
 
-void
-FFmpegDecoder::setup_general ()
-{
-       av_register_all ();
-
-       if (avformat_open_input (&_format_context, _ffmpeg_content->file().string().c_str(), 0, 0) < 0) {
-               throw OpenFileError (_ffmpeg_content->file().string ());
-       }
-
-       if (avformat_find_stream_info (_format_context, 0) < 0) {
-               throw DecodeError (_("could not find stream information"));
-       }
-
-       /* Find video, audio and subtitle streams */
-
-       for (uint32_t i = 0; i < _format_context->nb_streams; ++i) {
-               AVStream* s = _format_context->streams[i];
-               if (s->codec->codec_type == AVMEDIA_TYPE_VIDEO) {
-                       _video_stream = i;
-               } else if (s->codec->codec_type == AVMEDIA_TYPE_AUDIO) {
-
-                       /* This is a hack; sometimes it seems that _audio_codec_context->channel_layout isn't set up,
-                          so bodge it here.  No idea why we should have to do this.
-                       */
-
-                       if (s->codec->channel_layout == 0) {
-                               s->codec->channel_layout = av_get_default_channel_layout (s->codec->channels);
-                       }
-                       
-                       _audio_streams.push_back (
-                               shared_ptr<FFmpegAudioStream> (
-                                       new FFmpegAudioStream (stream_name (s), i, s->codec->sample_rate, s->codec->channels)
-                                       )
-                               );
-
-               } else if (s->codec->codec_type == AVMEDIA_TYPE_SUBTITLE) {
-                       _subtitle_streams.push_back (shared_ptr<FFmpegSubtitleStream> (new FFmpegSubtitleStream (stream_name (s), i)));
-               }
-       }
-
-       if (_video_stream < 0) {
-               throw DecodeError (N_("could not find video stream"));
-       }
-
-       _frame = avcodec_alloc_frame ();
-       if (_frame == 0) {
-               throw DecodeError (N_("could not allocate frame"));
-       }
-}
-
-void
-FFmpegDecoder::setup_video ()
-{
-       boost::mutex::scoped_lock lm (_mutex);
-       
-       _video_codec_context = _format_context->streams[_video_stream]->codec;
-       _video_codec = avcodec_find_decoder (_video_codec_context->codec_id);
-
-       if (_video_codec == 0) {
-               throw DecodeError (_("could not find video decoder"));
-       }
-
-       if (avcodec_open2 (_video_codec_context, _video_codec, 0) < 0) {
-               throw DecodeError (N_("could not open video decoder"));
-       }
-}
-
-void
-FFmpegDecoder::setup_audio ()
-{
-       boost::mutex::scoped_lock lm (_mutex);
-       
-       if (!_ffmpeg_content->audio_stream ()) {
-               return;
-       }
-
-       _audio_codec_context = _format_context->streams[_ffmpeg_content->audio_stream()->id]->codec;
-       _audio_codec = avcodec_find_decoder (_audio_codec_context->codec_id);
-
-       if (_audio_codec == 0) {
-               throw DecodeError (_("could not find audio decoder"));
-       }
-
-       if (avcodec_open2 (_audio_codec_context, _audio_codec, 0) < 0) {
-               throw DecodeError (N_("could not open audio decoder"));
-       }
-}
-
-void
-FFmpegDecoder::setup_subtitle ()
-{
-       boost::mutex::scoped_lock lm (_mutex);
-       
-       if (!_ffmpeg_content->subtitle_stream() || _ffmpeg_content->subtitle_stream()->id >= int (_format_context->nb_streams)) {
-               return;
-       }
-
-       _subtitle_codec_context = _format_context->streams[_ffmpeg_content->subtitle_stream()->id]->codec;
-       _subtitle_codec = avcodec_find_decoder (_subtitle_codec_context->codec_id);
-
-       if (_subtitle_codec == 0) {
-               throw DecodeError (_("could not find subtitle decoder"));
-       }
-       
-       if (avcodec_open2 (_subtitle_codec_context, _subtitle_codec, 0) < 0) {
-               throw DecodeError (N_("could not open subtitle decoder"));
-       }
-}
-
-
 void
 FFmpegDecoder::pass ()
 {
@@ -377,18 +239,6 @@ FFmpegDecoder::deinterleave_audio (uint8_t** data, int size)
        return audio;
 }
 
-float
-FFmpegDecoder::video_frame_rate () const
-{
-       AVStream* s = _format_context->streams[_video_stream];
-
-       if (s->avg_frame_rate.num && s->avg_frame_rate.den) {
-               return av_q2d (s->avg_frame_rate);
-       }
-
-       return av_q2d (s->r_frame_rate);
-}
-
 AVSampleFormat
 FFmpegDecoder::audio_sample_format () const
 {
@@ -399,39 +249,6 @@ FFmpegDecoder::audio_sample_format () const
        return _audio_codec_context->sample_fmt;
 }
 
-libdcp::Size
-FFmpegDecoder::video_size () const
-{
-       return libdcp::Size (_video_codec_context->width, _video_codec_context->height);
-}
-
-string
-FFmpegDecoder::stream_name (AVStream* s) const
-{
-       stringstream n;
-
-       if (s->metadata) {
-               AVDictionaryEntry const * lang = av_dict_get (s->metadata, N_("language"), 0, 0);
-               if (lang) {
-                       n << lang->value;
-               }
-               
-               AVDictionaryEntry const * title = av_dict_get (s->metadata, N_("title"), 0, 0);
-               if (title) {
-                       if (!n.str().empty()) {
-                               n << N_(" ");
-                       }
-                       n << title->value;
-               }
-       }
-
-       if (n.str().empty()) {
-               n << N_("unknown");
-       }
-
-       return n.str ();
-}
-
 int
 FFmpegDecoder::bytes_per_audio_sample () const
 {
@@ -448,22 +265,22 @@ FFmpegDecoder::seek (Time t)
 void
 FFmpegDecoder::seek_back ()
 {
-       if (position() < (2.5 * TIME_HZ / video_frame_rate())) {
+       if (position() < (2.5 * TIME_HZ / _ffmpeg_content->video_frame_rate())) {
                return;
        }
        
-       do_seek (position() - 2.5 * TIME_HZ / video_frame_rate(), true, true);
+       do_seek (position() - 2.5 * TIME_HZ / _ffmpeg_content->video_frame_rate(), true, true);
        VideoDecoder::seek_back ();
 }
 
 void
 FFmpegDecoder::seek_forward ()
 {
-       if (position() >= (_ffmpeg_content->length() - 0.5 * TIME_HZ / video_frame_rate())) {
+       if (position() >= (_ffmpeg_content->length() - 0.5 * TIME_HZ / _ffmpeg_content->video_frame_rate())) {
                return;
        }
        
-       do_seek (position() - 0.5 * TIME_HZ / video_frame_rate(), true, true);
+       do_seek (position() - 0.5 * TIME_HZ / _ffmpeg_content->video_frame_rate(), true, true);
        VideoDecoder::seek_forward ();
 }
 
@@ -505,13 +322,6 @@ FFmpegDecoder::do_seek (Time t, bool backwards, bool accurate)
        return;
 }
 
-/** @return Length (in video frames) according to our content's header */
-ContentVideoFrame
-FFmpegDecoder::video_length () const
-{
-       return (double(_format_context->duration) / AV_TIME_BASE) * video_frame_rate();
-}
-
 void
 FFmpegDecoder::decode_audio_packet ()
 {
@@ -623,3 +433,23 @@ FFmpegDecoder::done () const
        return (!_decode_audio || !_audio_codec_context || audio_done()) && (!_decode_video || video_done());
 }
        
+void
+FFmpegDecoder::setup_subtitle ()
+{
+       boost::mutex::scoped_lock lm (_mutex);
+       
+       if (!_ffmpeg_content->subtitle_stream() || _ffmpeg_content->subtitle_stream()->id >= int (_format_context->nb_streams)) {
+               return;
+       }
+
+       _subtitle_codec_context = _format_context->streams[_ffmpeg_content->subtitle_stream()->id]->codec;
+       _subtitle_codec = avcodec_find_decoder (_subtitle_codec_context->codec_id);
+
+       if (_subtitle_codec == 0) {
+               throw DecodeError (_("could not find subtitle decoder"));
+       }
+       
+       if (avcodec_open2 (_subtitle_codec_context, _subtitle_codec, 0) < 0) {
+               throw DecodeError (N_("could not open subtitle decoder"));
+       }
+}
index be178dbfbfc1d57232c89223e2f2ca62f0a90e36..331d9be70ffb5cfb55e247583c4055ef65bb2268 100644 (file)
@@ -35,33 +35,19 @@ extern "C" {
 #include "decoder.h"
 #include "video_decoder.h"
 #include "audio_decoder.h"
+#include "ffmpeg.h"
 
-struct AVFilterGraph;
-struct AVCodecContext;
-struct AVFilterContext;
-struct AVFormatContext;
-struct AVFrame;
-struct AVBufferContext;
-struct AVCodec;
-struct AVStream;
-class Job;
-class Options;
-class Image;
-class Log;
-class FFmpegContent;
 class Film;
 
 /** @class FFmpegDecoder
  *  @brief A decoder using FFmpeg to decode content.
  */
-class FFmpegDecoder : public VideoDecoder, public AudioDecoder
+class FFmpegDecoder : public VideoDecoder, public AudioDecoder, public FFmpeg
 {
 public:
        FFmpegDecoder (boost::shared_ptr<const Film>, boost::shared_ptr<const FFmpegContent>, bool video, bool audio);
        ~FFmpegDecoder ();
 
-       /* Decoder */
-
        void pass ();
        void seek (Time);
        void seek_back ();
@@ -69,78 +55,30 @@ public:
        Time position () const;
        bool done () const;
 
-       /* VideoDecoder */
-
-       float video_frame_rate () const;
-       libdcp::Size video_size () const;
-       ContentVideoFrame video_length () const;
-
-       /* FFmpegDecoder */
-
-       std::vector<boost::shared_ptr<FFmpegSubtitleStream> > subtitle_streams () const {
-               return _subtitle_streams;
-       }
-       
-       std::vector<boost::shared_ptr<FFmpegAudioStream> > audio_streams () const {
-               return _audio_streams;
-       }
-
-       boost::shared_ptr<const FFmpegContent> ffmpeg_content () const {
-               return _ffmpeg_content;
-       }
-
 private:
 
        /* No copy construction */
        FFmpegDecoder (FFmpegDecoder const &);
        FFmpegDecoder& operator= (FFmpegDecoder const &);
 
-       PixelFormat pixel_format () const;
+       void setup_subtitle ();
+
        AVSampleFormat audio_sample_format () const;
        int bytes_per_audio_sample () const;
        void do_seek (Time, bool, bool);
 
-       void setup_general ();
-       void setup_video ();
-       void setup_audio ();
-       void setup_subtitle ();
-
        bool decode_video_packet ();
        void decode_audio_packet ();
 
        void maybe_add_subtitle ();
        boost::shared_ptr<AudioBuffers> deinterleave_audio (uint8_t** data, int size);
 
-       std::string stream_name (AVStream* s) const;
-
-       boost::shared_ptr<const FFmpegContent> _ffmpeg_content;
-
-       AVFormatContext* _format_context;
-       int _video_stream;
-       
-       AVFrame* _frame;
-
-       AVCodecContext* _video_codec_context;
-       AVCodec* _video_codec;
-       AVCodecContext* _audio_codec_context;    ///< may be 0 if there is no audio
-       AVCodec* _audio_codec;                   ///< may be 0 if there is no audio
        AVCodecContext* _subtitle_codec_context; ///< may be 0 if there is no subtitle
        AVCodec* _subtitle_codec;                ///< may be 0 if there is no subtitle
-
-       AVPacket _packet;
-
+       
        std::list<boost::shared_ptr<FilterGraph> > _filter_graphs;
        boost::mutex _filter_graphs_mutex;
 
-        std::vector<boost::shared_ptr<FFmpegSubtitleStream> > _subtitle_streams;
-        std::vector<boost::shared_ptr<FFmpegAudioStream> > _audio_streams;
-
        bool _decode_video;
        bool _decode_audio;
-
-       /* It would appear (though not completely verified) that one must have
-          a mutex around calls to avcodec_open* and avcodec_close... and here
-          it is.
-       */
-       static boost::mutex _mutex;
 };
diff --git a/src/lib/ffmpeg_examiner.cc b/src/lib/ffmpeg_examiner.cc
new file mode 100644 (file)
index 0000000..e5d356a
--- /dev/null
@@ -0,0 +1,111 @@
+/*
+    Copyright (C) 2013 Carl Hetherington <cth@carlh.net>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+extern "C" {
+#include <libavcodec/avcodec.h>
+#include <libavformat/avformat.h>
+}
+#include "ffmpeg_examiner.h"
+#include "ffmpeg_content.h"
+
+using std::string;
+using std::stringstream;
+using boost::shared_ptr;
+
+FFmpegExaminer::FFmpegExaminer (shared_ptr<const FFmpegContent> c)
+       : FFmpeg (c)
+{
+       /* Find audio and subtitle streams */
+
+       for (uint32_t i = 0; i < _format_context->nb_streams; ++i) {
+               AVStream* s = _format_context->streams[i];
+               if (s->codec->codec_type == AVMEDIA_TYPE_AUDIO) {
+
+                       /* This is a hack; sometimes it seems that _audio_codec_context->channel_layout isn't set up,
+                          so bodge it here.  No idea why we should have to do this.
+                       */
+
+                       if (s->codec->channel_layout == 0) {
+                               s->codec->channel_layout = av_get_default_channel_layout (s->codec->channels);
+                       }
+                       
+                       _audio_streams.push_back (
+                               shared_ptr<FFmpegAudioStream> (
+                                       new FFmpegAudioStream (stream_name (s), i, s->codec->sample_rate, s->codec->channels)
+                                       )
+                               );
+
+               } else if (s->codec->codec_type == AVMEDIA_TYPE_SUBTITLE) {
+                       _subtitle_streams.push_back (shared_ptr<FFmpegSubtitleStream> (new FFmpegSubtitleStream (stream_name (s), i)));
+               }
+       }
+
+}
+
+float
+FFmpegExaminer::video_frame_rate () const
+{
+       AVStream* s = _format_context->streams[_video_stream];
+
+       if (s->avg_frame_rate.num && s->avg_frame_rate.den) {
+               return av_q2d (s->avg_frame_rate);
+       }
+
+       return av_q2d (s->r_frame_rate);
+}
+
+libdcp::Size
+FFmpegExaminer::video_size () const
+{
+       return libdcp::Size (_video_codec_context->width, _video_codec_context->height);
+}
+
+/** @return Length (in video frames) according to our content's header */
+ContentVideoFrame
+FFmpegExaminer::video_length () const
+{
+       return (double (_format_context->duration) / AV_TIME_BASE) * video_frame_rate();
+}
+
+string
+FFmpegExaminer::stream_name (AVStream* s) const
+{
+       stringstream n;
+
+       if (s->metadata) {
+               AVDictionaryEntry const * lang = av_dict_get (s->metadata, "language", 0, 0);
+               if (lang) {
+                       n << lang->value;
+               }
+               
+               AVDictionaryEntry const * title = av_dict_get (s->metadata, "title", 0, 0);
+               if (title) {
+                       if (!n.str().empty()) {
+                               n << " ";
+                       }
+                       n << title->value;
+               }
+       }
+
+       if (n.str().empty()) {
+               n << "unknown";
+       }
+
+       return n.str ();
+}
diff --git a/src/lib/ffmpeg_examiner.h b/src/lib/ffmpeg_examiner.h
new file mode 100644 (file)
index 0000000..8754515
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+    Copyright (C) 2013 Carl Hetherington <cth@carlh.net>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include "ffmpeg.h"
+#include "video_examiner.h"
+
+class FFmpegAudioStream;
+class FFmpegSubtitleStream;
+
+class FFmpegExaminer : public FFmpeg, public VideoExaminer
+{
+public:
+       FFmpegExaminer (boost::shared_ptr<const FFmpegContent>);
+       
+       float video_frame_rate () const;
+       libdcp::Size video_size () const;
+       ContentVideoFrame video_length () const;
+
+       std::vector<boost::shared_ptr<FFmpegSubtitleStream> > subtitle_streams () const {
+               return _subtitle_streams;
+       }
+       
+       std::vector<boost::shared_ptr<FFmpegAudioStream> > audio_streams () const {
+               return _audio_streams;
+       }
+
+private:
+       std::string stream_name (AVStream* s) const;
+       
+        std::vector<boost::shared_ptr<FFmpegSubtitleStream> > _subtitle_streams;
+        std::vector<boost::shared_ptr<FFmpegAudioStream> > _audio_streams;
+};
index b13b232a9a0a5c8405fb9b8d1835120b91a654bb..275c469096afb33507f78a885d70172643f64dc0 100644 (file)
@@ -33,7 +33,6 @@ extern "C" {
 #include "filter.h"
 #include "exceptions.h"
 #include "image.h"
-#include "ffmpeg_decoder.h"
 
 #include "i18n.h"
 
diff --git a/src/lib/imagemagick.h b/src/lib/imagemagick.h
new file mode 100644 (file)
index 0000000..5a1712a
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+    Copyright (C) 2012 Carl Hetherington <cth@carlh.net>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+class ImageMagickContent;
+
+class ImageMagick
+{
+public:
+       ImageMagick (boost::shared_ptr<const ImageMagickContent> c)
+               : _imagemagick_content (c)
+       {}
+
+       boost::shared_ptr<const ImageMagickContent> content () const {
+               return _imagemagick_content;
+       }
+
+protected:
+       boost::shared_ptr<const ImageMagickContent> _imagemagick_content;
+};
index 2b7449a6e99870acc3471bbfdfe40644dc0221bb..f9ee8cc84029d0cceb01ce845d272ceda8a62484 100644 (file)
 
 #include <libcxml/cxml.h>
 #include "imagemagick_content.h"
-#include "imagemagick_decoder.h"
+#include "imagemagick_examiner.h"
 #include "config.h"
 #include "compose.hpp"
+#include "film.h"
 
 #include "i18n.h"
 
@@ -73,10 +74,10 @@ ImageMagickContent::examine (shared_ptr<Job> job)
        shared_ptr<const Film> film = _film.lock ();
        assert (film);
        
-       shared_ptr<ImageMagickDecoder> decoder (new ImageMagickDecoder (film, shared_from_this()));
+       shared_ptr<ImageMagickExaminer> examiner (new ImageMagickExaminer (film, shared_from_this()));
 
        set_video_length (Config::instance()->default_still_length() * 24);
-       take_from_video_decoder (decoder);
+       take_from_video_examiner (examiner);
 }
 
 shared_ptr<Content>
index 077d0174dfe951a953fae0ac5519c393fbb74fea..c9123c77c288404e31783f41d6c26e51895a2674 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2012 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2012-2013 Carl Hetherington <cth@carlh.net>
 
     This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
@@ -35,41 +35,11 @@ using libdcp::Size;
 ImageMagickDecoder::ImageMagickDecoder (shared_ptr<const Film> f, shared_ptr<const ImageMagickContent> c)
        : Decoder (f)
        , VideoDecoder (f, c)
-       , _imagemagick_content (c)
+       , ImageMagick (c)
 {
 
 }
 
-libdcp::Size
-ImageMagickDecoder::video_size () const
-{
-       if (!_video_size) {
-               using namespace MagickCore;
-               Magick::Image* image = new Magick::Image (_imagemagick_content->file().string());
-               _video_size = libdcp::Size (image->columns(), image->rows());
-               delete image;
-       }
-
-       return _video_size.get ();
-}
-
-int
-ImageMagickDecoder::video_length () const
-{
-       return _imagemagick_content->video_length ();
-}
-
-float
-ImageMagickDecoder::video_frame_rate () const
-{
-       boost::shared_ptr<const Film> f = _film.lock ();
-       if (!f) {
-               return 24;
-       }
-
-       return f->dcp_video_frame_rate ();
-}
-
 void
 ImageMagickDecoder::pass ()
 {
index 73858b58a4c8165952d3cd85e984cc31022a5fe3..e169f9bc384a9a3b79a59657ab356c1e8b94438e 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2012 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2012-2013 Carl Hetherington <cth@carlh.net>
 
     This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
@@ -18,6 +18,7 @@
 */
 
 #include "video_decoder.h"
+#include "imagemagick.h"
 
 namespace Magick {
        class Image;
@@ -25,7 +26,7 @@ namespace Magick {
 
 class ImageMagickContent;
 
-class ImageMagickDecoder : public VideoDecoder
+class ImageMagickDecoder : public VideoDecoder, public ImageMagick
 {
 public:
        ImageMagickDecoder (boost::shared_ptr<const Film>, boost::shared_ptr<const ImageMagickContent>);
@@ -39,20 +40,7 @@ public:
        Time position () const;
        bool done () const;
 
-       /* VideoDecoder */
-
-       float video_frame_rate () const;
-       libdcp::Size video_size () const;
-       ContentVideoFrame video_length () const;
-
-       /* ImageMagickDecoder */
-
-       boost::shared_ptr<const ImageMagickContent> content () const {
-               return _imagemagick_content;
-       }
-
 private:
-       boost::shared_ptr<const ImageMagickContent> _imagemagick_content;
        boost::shared_ptr<Image> _image;
        mutable boost::optional<libdcp::Size> _video_size;
 };
diff --git a/src/lib/imagemagick_examiner.cc b/src/lib/imagemagick_examiner.cc
new file mode 100644 (file)
index 0000000..0eee0d4
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+    Copyright (C) 2013 Carl Hetherington <cth@carlh.net>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include <iostream>
+#include <Magick++.h>
+#include "imagemagick_content.h"
+#include "imagemagick_examiner.h"
+#include "film.h"
+
+#include "i18n.h"
+
+using std::cout;
+using boost::shared_ptr;
+
+ImageMagickExaminer::ImageMagickExaminer (shared_ptr<const Film> f, shared_ptr<const ImageMagickContent> c)
+       : ImageMagick (c)
+       , _film (f)
+{
+       using namespace MagickCore;
+       Magick::Image* image = new Magick::Image (_imagemagick_content->file().string());
+       _video_size = libdcp::Size (image->columns(), image->rows());
+       delete image;
+}
+
+libdcp::Size
+ImageMagickExaminer::video_size () const
+{
+       return _video_size;
+}
+
+int
+ImageMagickExaminer::video_length () const
+{
+       return _imagemagick_content->video_length ();
+}
+
+float
+ImageMagickExaminer::video_frame_rate () const
+{
+       boost::shared_ptr<const Film> f = _film.lock ();
+       if (!f) {
+               return 24;
+       }
+
+       return f->dcp_video_frame_rate ();
+}
+
diff --git a/src/lib/imagemagick_examiner.h b/src/lib/imagemagick_examiner.h
new file mode 100644 (file)
index 0000000..827dad6
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+    Copyright (C) 2013 Carl Hetherington <cth@carlh.net>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include "imagemagick.h"
+#include "video_examiner.h"
+
+namespace Magick {
+       class Image;
+}
+
+class ImageMagickContent;
+
+class ImageMagickExaminer : public ImageMagick, public VideoExaminer
+{
+public:
+       ImageMagickExaminer (boost::shared_ptr<const Film>, boost::shared_ptr<const ImageMagickContent>);
+
+       float video_frame_rate () const;
+       libdcp::Size video_size () const;
+       ContentVideoFrame video_length () const;
+
+private:
+       boost::weak_ptr<const Film> _film;
+       libdcp::Size _video_size;
+};
index 99471edc1a84f0fdf222e9151314223664716fed..f9de6aa2146b0f0ff5a91de4b214ddc37b83442a 100644 (file)
 
 */
 
+#include <iomanip>
 #include <libcxml/cxml.h>
 #include "video_content.h"
-#include "video_decoder.h"
+#include "video_examiner.h"
 #include "ratio.h"
 
 #include "i18n.h"
@@ -99,9 +100,9 @@ VideoContent::as_xml (xmlpp::Node* node) const
 }
 
 void
-VideoContent::take_from_video_decoder (shared_ptr<VideoDecoder> d)
+VideoContent::take_from_video_examiner (shared_ptr<VideoExaminer> d)
 {
-       /* These decoder calls could call other content methods which take a lock on the mutex */
+       /* These examiner calls could call other content methods which take a lock on the mutex */
        libdcp::Size const vs = d->video_size ();
        float const vfr = d->video_frame_rate ();
        
index 44f1c28472c08cc1885a4c021a723b9a6a31bc2b..23bcaa89bad7b945a041b4c039c54bdc36dd3f69 100644 (file)
@@ -23,7 +23,7 @@
 #include "content.h"
 #include "util.h"
 
-class VideoDecoder;
+class VideoExaminer;
 class Ratio;
 
 class VideoContentProperty
@@ -80,7 +80,7 @@ public:
        }
 
 protected:
-       void take_from_video_decoder (boost::shared_ptr<VideoDecoder>);
+       void take_from_video_examiner (boost::shared_ptr<VideoExaminer>);
 
        ContentVideoFrame _video_length;
 
index 54d14e2c68ab2562b6d6e8945fd1723661047065..fcc0ccf41eeafcc5491e2fd6f7569bd3250d7131 100644 (file)
@@ -69,8 +69,8 @@ VideoDecoder::video (shared_ptr<Image> image, bool same, Time t)
                
                if (sub) {
                        dcpomatic::Rect const tx = subtitle_transformed_area (
-                               float (image_size.width) / video_size().width,
-                               float (image_size.height) / video_size().height,
+                               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()
                                );
                        
index f0d140d803149b314202d462797f7360f4dfa00c..8de76c10f7a2653b2d79702b9a10301bfce5b221 100644 (file)
@@ -35,15 +35,6 @@ public:
        virtual void seek_back ();
        virtual void seek_forward ();
        
-       /* Calls for VideoContent to find out about itself */
-
-       /** @return video frame rate second, or 0 if unknown */
-       virtual float video_frame_rate () const = 0;
-       /** @return video size in pixels */
-       virtual libdcp::Size video_size () const = 0;
-       /** @return length according to our content's header */
-       virtual ContentVideoFrame video_length () const = 0;
-
        void set_video_container_size (libdcp::Size);
 
 protected:
diff --git a/src/lib/video_examiner.h b/src/lib/video_examiner.h
new file mode 100644 (file)
index 0000000..2f71329
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+    Copyright (C) 2013 Carl Hetherington <cth@carlh.net>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include <libdcp/types.h>
+#include "types.h"
+
+class VideoExaminer
+{
+public:
+       virtual float video_frame_rate () const = 0;
+       virtual libdcp::Size video_size () const = 0;
+       virtual ContentVideoFrame video_length () const = 0;
+};
index 9945fc753c4258fb54d32f54b68b3520d015545b..d0f10299821d95778631e943c8d4b2e57ef0a6e1 100644 (file)
@@ -23,13 +23,16 @@ sources = """
           examine_content_job.cc
           exceptions.cc
           filter_graph.cc
+          ffmpeg.cc
           ffmpeg_content.cc
           ffmpeg_decoder.cc
+          ffmpeg_examiner.cc
           film.cc
           filter.cc
           image.cc
           imagemagick_content.cc
           imagemagick_decoder.cc
+          imagemagick_examiner.cc
           job.cc
           job_manager.cc
           log.cc