Basics of DCP import.
authorCarl Hetherington <cth@carlh.net>
Wed, 9 Jul 2014 08:36:58 +0000 (09:36 +0100)
committerCarl Hetherington <cth@carlh.net>
Wed, 9 Jul 2014 08:36:58 +0000 (09:36 +0100)
24 files changed:
src/lib/audio_content.cc
src/lib/audio_content.h
src/lib/audio_examiner.h [new file with mode: 0644]
src/lib/dcp_content.cc [new file with mode: 0644]
src/lib/dcp_content.h [new file with mode: 0644]
src/lib/dcp_decoder.cc [new file with mode: 0644]
src/lib/dcp_decoder.h [new file with mode: 0644]
src/lib/dcp_examiner.cc [new file with mode: 0644]
src/lib/dcp_examiner.h [new file with mode: 0644]
src/lib/exceptions.h
src/lib/ffmpeg_content.cc
src/lib/player.cc
src/lib/single_stream_audio_content.cc [new file with mode: 0644]
src/lib/single_stream_audio_content.h [new file with mode: 0644]
src/lib/sndfile_content.cc
src/lib/sndfile_content.h
src/lib/sndfile_decoder.h
src/lib/subtitle_content.cc
src/lib/subtitle_content.h
src/lib/video_content.cc
src/lib/video_decoder.cc
src/lib/video_decoder.h
src/lib/wscript
src/wx/film_editor.cc

index 9f0d26573453c347873237e21e195df9b53aab36..6317aa4cb7d7390c9a47fc1523353e7c5b50a39f 100644 (file)
@@ -43,6 +43,14 @@ int const AudioContentProperty::AUDIO_GAIN = 203;
 int const AudioContentProperty::AUDIO_DELAY = 204;
 int const AudioContentProperty::AUDIO_MAPPING = 205;
 
+AudioContent::AudioContent (shared_ptr<const Film> f)
+       : Content (f)
+       , _audio_gain (0)
+       , _audio_delay (Config::instance()->default_audio_delay ())
+{
+
+}
+
 AudioContent::AudioContent (shared_ptr<const Film> f, DCPTime s)
        : Content (f, s)
        , _audio_gain (0)
index 336a98629bae47b103cdb0d3d88e15ced581cfaa..131ced61aead11dab059a600aa4f3f3b1aed024c 100644 (file)
@@ -50,6 +50,7 @@ class AudioContent : public virtual Content
 public:
        typedef int64_t Frame;
        
+       AudioContent (boost::shared_ptr<const Film>);
        AudioContent (boost::shared_ptr<const Film>, DCPTime);
        AudioContent (boost::shared_ptr<const Film>, boost::filesystem::path);
        AudioContent (boost::shared_ptr<const Film>, cxml::ConstNodePtr);
diff --git a/src/lib/audio_examiner.h b/src/lib/audio_examiner.h
new file mode 100644 (file)
index 0000000..64a98ba
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+    Copyright (C) 2014 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 AudioExaminer
+{
+public:
+       virtual ~AudioExaminer () {}
+
+       virtual int audio_channels () const = 0;
+       virtual ContentTime audio_length () const = 0;
+       virtual int audio_frame_rate () const = 0;
+};
diff --git a/src/lib/dcp_content.cc b/src/lib/dcp_content.cc
new file mode 100644 (file)
index 0000000..663846e
--- /dev/null
@@ -0,0 +1,92 @@
+/*
+    Copyright (C) 2014 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 <dcp/dcp.h>
+#include "dcp_content.h"
+#include "dcp_examiner.h"
+#include "job.h"
+#include "film.h"
+#include "compose.hpp"
+
+#include "i18n.h"
+
+using std::string;
+using boost::shared_ptr;
+
+DCPContent::DCPContent (shared_ptr<const Film> f, boost::filesystem::path p)
+       : Content (f)
+       , VideoContent (f)
+       , SingleStreamAudioContent (f)
+       , SubtitleContent (f)
+       , _directory (p)
+{
+       read_directory (p);
+}
+
+void
+DCPContent::read_directory (boost::filesystem::path p)
+{
+       for (boost::filesystem::directory_iterator i(p); i != boost::filesystem::directory_iterator(); ++i) {
+               if (boost::filesystem::is_regular_file (i->path ())) {
+                       _paths.push_back (i->path ());
+               } else if (boost::filesystem::is_directory (i->path ())) {
+                       read_directory (i->path ());
+               }
+       }
+}
+
+void
+DCPContent::examine (shared_ptr<Job> job)
+{
+       job->set_progress_unknown ();
+       Content::examine (job);
+       shared_ptr<VideoExaminer> examiner (new DCPExaminer (shared_from_this ()));
+       take_from_video_examiner (examiner);
+}
+
+string
+DCPContent::summary () const
+{
+       return String::compose (_("%1 [DCP]"), path_summary ());
+}
+
+string
+DCPContent::technical_summary () const
+{
+       return Content::technical_summary() + " - "
+               + VideoContent::technical_summary() + " - "
+               + AudioContent::technical_summary() + " - ";
+}
+
+void
+DCPContent::as_xml (xmlpp::Node* node) const
+{
+       node->add_child("Type")->add_child_text ("DCP");
+       Content::as_xml (node);
+       VideoContent::as_xml (node);
+       SingleStreamAudioContent::as_xml (node);
+}
+
+DCPTime
+DCPContent::full_length () const
+{
+       shared_ptr<const Film> film = _film.lock ();
+       assert (film);
+       return DCPTime (video_length (), FrameRateChange (video_frame_rate (), film->video_frame_rate ()));
+}
diff --git a/src/lib/dcp_content.h b/src/lib/dcp_content.h
new file mode 100644 (file)
index 0000000..7f3ac95
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+    Copyright (C) 2014 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_content.h"
+#include "single_stream_audio_content.h"
+#include "subtitle_content.h"
+
+class DCPContent : public VideoContent, public SingleStreamAudioContent, public SubtitleContent
+{
+public:
+       DCPContent (boost::shared_ptr<const Film> f, boost::filesystem::path p);
+
+       boost::shared_ptr<DCPContent> shared_from_this () {
+               return boost::dynamic_pointer_cast<DCPContent> (Content::shared_from_this ());
+       }
+
+       DCPTime full_length () const;
+       
+       void examine (boost::shared_ptr<Job>);
+       std::string summary () const;
+       std::string technical_summary () const;
+       void as_xml (xmlpp::Node *) const;
+
+       boost::filesystem::path directory () const {
+               boost::mutex::scoped_lock lm (_mutex);
+               return _directory;
+       }
+
+private:
+       void read_directory (boost::filesystem::path);
+
+       boost::filesystem::path _directory;
+};
diff --git a/src/lib/dcp_decoder.cc b/src/lib/dcp_decoder.cc
new file mode 100644 (file)
index 0000000..14672a2
--- /dev/null
@@ -0,0 +1,119 @@
+/*
+    Copyright (C) 2014 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 <dcp/dcp.h>
+#include <dcp/cpl.h>
+#include <dcp/reel.h>
+#include <dcp/mono_picture_mxf.h>
+#include <dcp/stereo_picture_mxf.h>
+#include <dcp/reel_picture_asset.h>
+#include <dcp/mono_picture_frame.h>
+#include <dcp/stereo_picture_frame.h>
+#include "dcp_decoder.h"
+#include "dcp_content.h"
+#include "image_proxy.h"
+#include "image.h"
+
+using std::list;
+using std::cout;
+using boost::shared_ptr;
+using boost::dynamic_pointer_cast;
+
+DCPDecoder::DCPDecoder (shared_ptr<const DCPContent> c, shared_ptr<Log> log)
+       : VideoDecoder (c)
+       , AudioDecoder (c)
+       , SubtitleDecoder (c)
+       , _log (log)
+       , _dcp_content (c)
+{
+       dcp::DCP dcp (c->directory ());
+       dcp.read ();
+       assert (dcp.cpls().size() == 1);
+       _reels = dcp.cpls().front()->reels ();
+       _reel = _reels.begin ();
+}
+
+bool
+DCPDecoder::pass ()
+{
+       if (_reel == _reels.end ()) {
+               return true;
+       }
+
+       float const vfr = _dcp_content->video_frame_rate ();
+       
+       if ((*_reel)->main_picture ()) {
+               shared_ptr<dcp::PictureMXF> mxf = (*_reel)->main_picture()->mxf ();
+               shared_ptr<dcp::MonoPictureMXF> mono = dynamic_pointer_cast<dcp::MonoPictureMXF> (mxf);
+               shared_ptr<dcp::StereoPictureMXF> stereo = dynamic_pointer_cast<dcp::StereoPictureMXF> (mxf);
+               if (mono) {
+                       shared_ptr<Image> image (new Image (PIX_FMT_RGB24, mxf->size(), false));
+                       mono->get_frame (_next.frames (vfr))->rgb_frame (image->data()[0]);
+                       shared_ptr<Image> aligned (new Image (image, true));
+                       video (shared_ptr<ImageProxy> (new RawImageProxy (aligned, _log)), _next.frames (vfr));
+               } else {
+
+                       shared_ptr<Image> left (new Image (PIX_FMT_RGB24, mxf->size(), false));
+                       stereo->get_frame (_next.frames (vfr))->rgb_frame (dcp::EYE_LEFT, left->data()[0]);
+                       shared_ptr<Image> aligned_left (new Image (left, true));
+                       video (shared_ptr<ImageProxy> (new RawImageProxy (aligned_left, _log)), _next.frames (vfr));
+
+                       shared_ptr<Image> right (new Image (PIX_FMT_RGB24, mxf->size(), false));
+                       stereo->get_frame (_next.frames (vfr))->rgb_frame (dcp::EYE_RIGHT, right->data()[0]);
+                       shared_ptr<Image> aligned_right (new Image (right, true));
+                       video (shared_ptr<ImageProxy> (new RawImageProxy (aligned_right, _log)), _next.frames (vfr));
+               }
+       }
+
+       /* XXX: sound */
+       /* XXX: subtitle */
+
+       _next += ContentTime::from_frames (1, vfr);
+
+       if ((*_reel)->main_picture ()) {
+               if ((*_reel)->main_picture()->duration() >= _next.frames (vfr)) {
+                       ++_reel;
+               }
+       }
+       
+       return false;
+}
+
+void
+DCPDecoder::seek (ContentTime t, bool accurate)
+{
+       VideoDecoder::seek (t, accurate);
+       AudioDecoder::seek (t, accurate);
+       SubtitleDecoder::seek (t, accurate);
+
+       _reel = _reels.begin ();
+       while (_reel != _reels.end() && t >= ContentTime::from_frames ((*_reel)->main_picture()->duration(), _dcp_content->video_frame_rate ())) {
+               t -= ContentTime::from_frames ((*_reel)->main_picture()->duration(), _dcp_content->video_frame_rate ());
+               ++_reel;
+       }
+
+       _next = t;
+}
+
+
+list<ContentTimePeriod>
+DCPDecoder::subtitles_during (ContentTimePeriod, bool starting) const
+{
+       return list<ContentTimePeriod> ();
+}
diff --git a/src/lib/dcp_decoder.h b/src/lib/dcp_decoder.h
new file mode 100644 (file)
index 0000000..d81b20b
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+    Copyright (C) 2014 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_decoder.h"
+#include "audio_decoder.h"
+#include "subtitle_decoder.h"
+
+namespace dcp {
+       class Reel;
+}
+
+class DCPContent;
+class Log;
+
+class DCPDecoder : public VideoDecoder, public AudioDecoder, public SubtitleDecoder
+{
+public:
+       DCPDecoder (boost::shared_ptr<const DCPContent>, boost::shared_ptr<Log>);
+
+private:
+       void seek (ContentTime t, bool accurate);
+       bool pass ();
+       std::list<ContentTimePeriod> subtitles_during (ContentTimePeriod, bool starting) const;
+
+       ContentTime _next;
+       std::list<boost::shared_ptr<dcp::Reel> > _reels;
+       std::list<boost::shared_ptr<dcp::Reel> >::iterator _reel;
+       boost::shared_ptr<Log> _log;
+       boost::shared_ptr<const DCPContent> _dcp_content;
+};
diff --git a/src/lib/dcp_examiner.cc b/src/lib/dcp_examiner.cc
new file mode 100644 (file)
index 0000000..7ce18c9
--- /dev/null
@@ -0,0 +1,84 @@
+/*
+    Copyright (C) 2014 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 <dcp/dcp.h>
+#include <dcp/cpl.h>
+#include <dcp/reel.h>
+#include <dcp/reel_picture_asset.h>
+#include <dcp/reel_sound_asset.h>
+#include <dcp/sound_mxf.h>
+#include "dcp_examiner.h"
+#include "dcp_content.h"
+#include "exceptions.h"
+
+#include "i18n.h"
+
+using std::list;
+using boost::shared_ptr;
+
+DCPExaminer::DCPExaminer (shared_ptr<const DCPContent> content)
+{
+       dcp::DCP dcp (content->directory ());
+       dcp.read ();
+
+       if (dcp.cpls().size() == 0) {
+               throw DCPError ("No CPLs found in DCP");
+       } else if (dcp.cpls().size() > 1) {
+               throw DCPError ("Multiple CPLs found in DCP");
+       }
+
+       list<shared_ptr<dcp::Reel> > reels = dcp.cpls().front()->reels ();
+       for (list<shared_ptr<dcp::Reel> >::const_iterator i = reels.begin(); i != reels.end(); ++i) {
+
+               if ((*i)->main_picture ()) {
+                       dcp::Fraction const frac = (*i)->main_picture()->frame_rate ();
+                       float const fr = float(frac.numerator) / frac.denominator;
+                       if (!_video_frame_rate) {
+                               _video_frame_rate = fr;
+                       } else if (_video_frame_rate.get() != fr) {
+                               throw DCPError (_("Mismatched frame rates in DCP"));
+                       }
+
+                       shared_ptr<dcp::PictureMXF> mxf = (*i)->main_picture()->mxf ();
+                       if (!_video_size) {
+                               _video_size = mxf->size ();
+                       } else if (_video_size.get() != mxf->size ()) {
+                               throw DCPError (_("Mismatched video sizes in DCP"));
+                       }
+
+                       _video_length += ContentTime::from_frames ((*i)->main_picture()->duration(), _video_frame_rate.get ());
+               }
+                       
+               if ((*i)->main_sound ()) {
+                       shared_ptr<dcp::SoundMXF> mxf = (*i)->main_sound()->mxf ();
+
+                       if (!_audio_channels) {
+                               _audio_channels = mxf->channels ();
+                       } else if (_audio_channels.get() != mxf->channels ()) {
+                               throw DCPError (_("Mismatched audio channel counts in DCP"));
+                       }
+
+                       if (!_audio_frame_rate) {
+                               _audio_frame_rate = mxf->sampling_rate ();
+                       } else if (_audio_frame_rate.get() != mxf->sampling_rate ()) {
+                               throw DCPError (_("Mismatched audio frame rates in DCP"));
+                       }
+               }
+       }
+}
diff --git a/src/lib/dcp_examiner.h b/src/lib/dcp_examiner.h
new file mode 100644 (file)
index 0000000..c8b0ef7
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+    Copyright (C) 2014 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_examiner.h"
+
+class DCPContent;
+
+class DCPExaminer : public VideoExaminer
+{
+public:
+       DCPExaminer (boost::shared_ptr<const DCPContent>);
+       
+       float video_frame_rate () const {
+               return _video_frame_rate.get_value_or (24);
+       }
+       
+       dcp::Size video_size () const {
+               return _video_size.get_value_or (dcp::Size (1998, 1080));
+       }
+       
+       ContentTime video_length () const {
+               return _video_length;
+       }
+
+private:
+       boost::optional<float> _video_frame_rate;
+       boost::optional<dcp::Size> _video_size;
+       ContentTime _video_length;
+       boost::optional<int> _audio_channels;
+       boost::optional<int> _audio_frame_rate;
+};
index 38452ffc02b7c0867596cb55787a77c29eeafc01..a8969d7024c6aa60c0bd3460d66ca9034b7d3456 100644 (file)
@@ -245,10 +245,18 @@ public:
        SubRipError (std::string, std::string, boost::filesystem::path);
 };
 
+class DCPError : public StringError
+{
+public:
+       DCPError (std::string s)
+               : StringError (s)
+       {}
+};
+
 /** @class ExceptionStore
  *  @brief A parent class for classes which have a need to catch and
  *  re-throw exceptions.
-
+ *
  *  This is intended for classes which run their own thread; they should do
  *  something like
  *
index da4acb5f86f54cf5cd1e2d6b6339eae0031d243d..d2bb329db346bc3ca34988f95a8425a3727dcded 100644 (file)
@@ -174,7 +174,6 @@ FFmpegContent::examine (shared_ptr<Job> job)
 
        shared_ptr<const Film> film = _film.lock ();
        assert (film);
-       LOG_GENERAL ("Video length obtained from header as %1 frames", video_length.frames (video_frame_rate ()));
 
        {
                boost::mutex::scoped_lock lm (_mutex);
index ebec19b2dc40f2d86da3c51865e05eb1d9105a6e..9f9f8db2e3374b369f7c206e480310b9d278dd01 100644 (file)
@@ -43,6 +43,8 @@
 #include "content_video.h"
 #include "player_video.h"
 #include "frame_rate_change.h"
+#include "dcp_content.h"
+#include "dcp_decoder.h"
 
 #define LOG_GENERAL(...) _film->log()->log (String::compose (__VA_ARGS__), Log::TYPE_GENERAL);
 
@@ -120,6 +122,12 @@ Player::setup_pieces ()
                        frc = FrameRateChange (fc->video_frame_rate(), _film->video_frame_rate());
                }
 
+               shared_ptr<const DCPContent> dc = dynamic_pointer_cast<const DCPContent> (*i);
+               if (dc) {
+                       decoder.reset (new DCPDecoder (dc, _film->log ()));
+                       frc = FrameRateChange (dc->video_frame_rate(), _film->video_frame_rate());
+               }
+
                /* ImageContent */
                shared_ptr<const ImageContent> ic = dynamic_pointer_cast<const ImageContent> (*i);
                if (ic) {
diff --git a/src/lib/single_stream_audio_content.cc b/src/lib/single_stream_audio_content.cc
new file mode 100644 (file)
index 0000000..ac4da25
--- /dev/null
@@ -0,0 +1,103 @@
+/*
+    Copyright (C) 2014 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 <dcp/raw_convert.h>
+#include "single_stream_audio_content.h"
+#include "audio_examiner.h"
+#include "film.h"
+
+using std::string;
+using boost::shared_ptr;
+using dcp::raw_convert;
+
+SingleStreamAudioContent::SingleStreamAudioContent (shared_ptr<const Film> f)
+       : Content (f)
+       , AudioContent (f)
+       , _audio_channels (0)
+       , _audio_length (0)
+       , _audio_frame_rate (0)
+{
+
+}
+
+SingleStreamAudioContent::SingleStreamAudioContent (shared_ptr<const Film> f, boost::filesystem::path p)
+       : Content (f, p)
+       , AudioContent (f, p)
+       , _audio_channels (0)
+       , _audio_length (0)
+       , _audio_frame_rate (0)
+{
+
+}
+
+SingleStreamAudioContent::SingleStreamAudioContent (shared_ptr<const Film> f, cxml::ConstNodePtr node, int version)
+       : Content (f, node)
+       , AudioContent (f, node)
+       , _audio_mapping (node->node_child ("AudioMapping"), version)
+{
+       _audio_channels = node->number_child<int> ("AudioChannels");
+       _audio_length = ContentTime (node->number_child<ContentTime::Type> ("AudioLength"));
+       _audio_frame_rate = node->number_child<int> ("AudioFrameRate");
+}
+
+void
+SingleStreamAudioContent::set_audio_mapping (AudioMapping m)
+{
+       {
+               boost::mutex::scoped_lock lm (_mutex);
+               _audio_mapping = m;
+       }
+
+       AudioContent::set_audio_mapping (m);
+}
+
+
+void
+SingleStreamAudioContent::as_xml (xmlpp::Node* node) const
+{
+       AudioContent::as_xml (node);
+       node->add_child("AudioChannels")->add_child_text (raw_convert<string> (audio_channels ()));
+       node->add_child("AudioLength")->add_child_text (raw_convert<string> (audio_length().get ()));
+       node->add_child("AudioFrameRate")->add_child_text (raw_convert<string> (audio_frame_rate ()));
+       _audio_mapping.as_xml (node->add_child("AudioMapping"));
+}
+
+void
+SingleStreamAudioContent::take_from_audio_examiner (shared_ptr<AudioExaminer> examiner)
+{
+       {
+               boost::mutex::scoped_lock lm (_mutex);
+               _audio_channels = examiner->audio_channels ();
+               _audio_length = examiner->audio_length ();
+               _audio_frame_rate = examiner->audio_frame_rate ();
+       }
+
+       signal_changed (AudioContentProperty::AUDIO_CHANNELS);
+       signal_changed (AudioContentProperty::AUDIO_LENGTH);
+       signal_changed (AudioContentProperty::AUDIO_FRAME_RATE);
+
+       {
+               boost::mutex::scoped_lock lm (_mutex);
+               /* XXX: do this in signal_changed...? */
+               _audio_mapping = AudioMapping (_audio_channels);
+               _audio_mapping.make_default ();
+       }
+       
+       signal_changed (AudioContentProperty::AUDIO_MAPPING);
+}
diff --git a/src/lib/single_stream_audio_content.h b/src/lib/single_stream_audio_content.h
new file mode 100644 (file)
index 0000000..588c250
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+    Copyright (C) 2014 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_SINGLE_STREAM_AUDIO_CONTENT_H
+#define DCPOMATIC_SINGLE_STREAM_AUDIO_CONTENT_H
+
+#include "audio_content.h"
+
+class AudioExaminer;
+
+class SingleStreamAudioContent : public AudioContent
+{
+public:
+       SingleStreamAudioContent (boost::shared_ptr<const Film>);
+       SingleStreamAudioContent (boost::shared_ptr<const Film>, boost::filesystem::path);
+       SingleStreamAudioContent (boost::shared_ptr<const Film> f, cxml::ConstNodePtr node, int version);
+
+       void as_xml (xmlpp::Node* node) const;
+
+       /* AudioContent */
+       int audio_channels () const {
+               boost::mutex::scoped_lock lm (_mutex);
+               return _audio_channels;
+       }
+       
+       ContentTime audio_length () const {
+               boost::mutex::scoped_lock lm (_mutex);
+               return _audio_length;
+       }
+       
+       int audio_frame_rate () const {
+               boost::mutex::scoped_lock lm (_mutex);
+               return _audio_frame_rate;
+       }
+
+       AudioMapping audio_mapping () const {
+               boost::mutex::scoped_lock lm (_mutex);
+               return _audio_mapping;
+       }
+
+       void set_audio_mapping (AudioMapping);
+
+       void take_from_audio_examiner (boost::shared_ptr<AudioExaminer>);
+
+protected:
+       int _audio_channels;
+       ContentTime _audio_length;
+       int _audio_frame_rate;
+       AudioMapping _audio_mapping;
+};
+
+#endif
index aab69f3063c5cf9c229dd7e97f79f6f0e0a5fd53..a573d43c407f8ac8a825190a1784600ba37ac873 100644 (file)
@@ -36,24 +36,27 @@ using dcp::raw_convert;
 
 SndfileContent::SndfileContent (shared_ptr<const Film> f, boost::filesystem::path p)
        : Content (f, p)
-       , AudioContent (f, p)
-       , _audio_channels (0)
-       , _audio_length (0)
-       , _audio_frame_rate (0)
+       , SingleStreamAudioContent (f, p)
 {
 
 }
 
 SndfileContent::SndfileContent (shared_ptr<const Film> f, cxml::ConstNodePtr node, int version)
        : Content (f, node)
-       , AudioContent (f, node)
-       , _audio_mapping (node->node_child ("AudioMapping"), version)
+       , SingleStreamAudioContent (f, node, version)
 {
-       _audio_channels = node->number_child<int> ("AudioChannels");
-       _audio_length = ContentTime (node->number_child<ContentTime::Type> ("AudioLength"));
-       _audio_frame_rate = node->number_child<int> ("AudioFrameRate");
+       
+}
+
+void
+SndfileContent::as_xml (xmlpp::Node* node) const
+{
+       node->add_child("Type")->add_child_text ("Sndfile");
+       Content::as_xml (node);
+       SingleStreamAudioContent::as_xml (node);
 }
 
+
 string
 SndfileContent::summary () const
 {
@@ -102,41 +105,8 @@ SndfileContent::examine (shared_ptr<Job> job)
 {
        job->set_progress_unknown ();
        Content::examine (job);
-
-       SndfileDecoder dec (shared_from_this());
-
-       {
-               boost::mutex::scoped_lock lm (_mutex);
-               _audio_channels = dec.audio_channels ();
-               _audio_length = dec.audio_length ();
-               _audio_frame_rate = dec.audio_frame_rate ();
-       }
-
-       signal_changed (AudioContentProperty::AUDIO_CHANNELS);
-       signal_changed (AudioContentProperty::AUDIO_LENGTH);
-       signal_changed (AudioContentProperty::AUDIO_FRAME_RATE);
-
-       {
-               boost::mutex::scoped_lock lm (_mutex);
-               /* XXX: do this in signal_changed...? */
-               _audio_mapping = AudioMapping (_audio_channels);
-               _audio_mapping.make_default ();
-       }
-       
-       signal_changed (AudioContentProperty::AUDIO_MAPPING);
-}
-
-void
-SndfileContent::as_xml (xmlpp::Node* node) const
-{
-       node->add_child("Type")->add_child_text ("Sndfile");
-       Content::as_xml (node);
-       AudioContent::as_xml (node);
-
-       node->add_child("AudioChannels")->add_child_text (raw_convert<string> (audio_channels ()));
-       node->add_child("AudioLength")->add_child_text (raw_convert<string> (audio_length().get ()));
-       node->add_child("AudioFrameRate")->add_child_text (raw_convert<string> (audio_frame_rate ()));
-       _audio_mapping.as_xml (node->add_child("AudioMapping"));
+       shared_ptr<AudioExaminer> dec (new SndfileDecoder (shared_from_this()));
+       take_from_audio_examiner (dec);
 }
 
 DCPTime
@@ -147,13 +117,3 @@ SndfileContent::full_length () const
        return DCPTime (audio_length(), film->active_frame_rate_change (position ()));
 }
 
-void
-SndfileContent::set_audio_mapping (AudioMapping m)
-{
-       {
-               boost::mutex::scoped_lock lm (_mutex);
-               _audio_mapping = m;
-       }
-
-       AudioContent::set_audio_mapping (m);
-}
index dcd6cb55d0e1735bb977ca6ce947d8b9399c4939..75c723518ea61ebf48ed6e06163b2894e71fe53b 100644 (file)
 extern "C" {
 #include <libavutil/audioconvert.h>
 }
-#include "audio_content.h"
+#include "single_stream_audio_content.h"
 
 namespace cxml {
        class Node;
 }
 
-class SndfileContent : public AudioContent
+class SndfileContent : public SingleStreamAudioContent
 {
 public:
        SndfileContent (boost::shared_ptr<const Film>, boost::filesystem::path);
@@ -39,43 +39,15 @@ public:
                return boost::dynamic_pointer_cast<SndfileContent> (Content::shared_from_this ());
        }
        
+       DCPTime full_length () const;
+       
        void examine (boost::shared_ptr<Job>);
        std::string summary () const;
        std::string technical_summary () const;
        std::string information () const;
        void as_xml (xmlpp::Node *) const;
-       DCPTime full_length () const;
-
-       /* AudioContent */
-       int audio_channels () const {
-               boost::mutex::scoped_lock lm (_mutex);
-               return _audio_channels;
-       }
-       
-       ContentTime audio_length () const {
-               boost::mutex::scoped_lock lm (_mutex);
-               return _audio_length;
-       }
-       
-       int audio_frame_rate () const {
-               boost::mutex::scoped_lock lm (_mutex);
-               return _audio_frame_rate;
-       }
-
-       AudioMapping audio_mapping () const {
-               boost::mutex::scoped_lock lm (_mutex);
-               return _audio_mapping;
-       }
-
-       void set_audio_mapping (AudioMapping);
        
        static bool valid_file (boost::filesystem::path);
-
-private:
-       int _audio_channels;
-       ContentTime _audio_length;
-       int _audio_frame_rate;
-       AudioMapping _audio_mapping;
 };
 
 #endif
index 52590ef24a3b99bc841edff4687b2a97ced69a9c..41d5faf082ec29c75b2c2e1059f528c59a26e0ff 100644 (file)
 #include <sndfile.h>
 #include "decoder.h"
 #include "audio_decoder.h"
+#include "audio_examiner.h"
 
 class SndfileContent;
 
-class SndfileDecoder : public AudioDecoder
+class SndfileDecoder : public AudioDecoder, public AudioExaminer
 {
 public:
        SndfileDecoder (boost::shared_ptr<const SndfileContent> c);
index f839a56d0f6b60b008090b7d1d6756c1b06f34d5..88e48b420150192118204d6469b296d5c49aec2d 100644 (file)
@@ -37,6 +37,16 @@ int const SubtitleContentProperty::SUBTITLE_Y_OFFSET = 501;
 int const SubtitleContentProperty::SUBTITLE_SCALE = 502;
 int const SubtitleContentProperty::SUBTITLE_USE = 503;
 
+SubtitleContent::SubtitleContent (shared_ptr<const Film> f)
+       : Content (f)
+       , _subtitle_use (false)
+       , _subtitle_x_offset (0)
+       , _subtitle_y_offset (0)
+       , _subtitle_scale (1)
+{
+
+}
+
 SubtitleContent::SubtitleContent (shared_ptr<const Film> f, boost::filesystem::path p)
        : Content (f, p)
        , _subtitle_use (false)
index b73119bdb9dccef91e87e516f3b1c475da3fdfda..f46a87c156898c4ab6cf1c5ff05d76e31f091c07 100644 (file)
@@ -34,6 +34,7 @@ public:
 class SubtitleContent : public virtual Content
 {
 public:
+       SubtitleContent (boost::shared_ptr<const Film>);
        SubtitleContent (boost::shared_ptr<const Film>, boost::filesystem::path);
        SubtitleContent (boost::shared_ptr<const Film>, cxml::ConstNodePtr, int version);
        SubtitleContent (boost::shared_ptr<const Film>, std::vector<boost::shared_ptr<Content> >);
index 5fd71076bee5b08c626e749afd6909ac359e1dd0..a2a4e6c6b3b46c452290975d5eccb2952fbaab89 100644 (file)
 #include "film.h"
 #include "exceptions.h"
 #include "frame_rate_change.h"
+#include "log.h"
 
 #include "i18n.h"
 
+#define LOG_GENERAL(...) film->log()->log (String::compose (__VA_ARGS__), Log::TYPE_GENERAL);
+
 int const VideoContentProperty::VIDEO_SIZE       = 0;
 int const VideoContentProperty::VIDEO_FRAME_RATE  = 1;
 int const VideoContentProperty::VIDEO_FRAME_TYPE  = 2;
@@ -196,6 +199,10 @@ VideoContent::take_from_video_examiner (shared_ptr<VideoExaminer> d)
                _video_frame_rate = vfr;
                _video_length = vl;
        }
+
+       shared_ptr<const Film> film = _film.lock ();
+       assert (film);
+       LOG_GENERAL ("Video length obtained from header as %1 frames", _video_length.frames (_video_frame_rate));
        
        signal_changed (VideoContentProperty::VIDEO_SIZE);
        signal_changed (VideoContentProperty::VIDEO_FRAME_RATE);
index bfd7a7e3ef6abeb9a2d3457aaa76894aa060e753..5dd078553659b21d3c09bcf8a030ea77e73ad783 100644 (file)
@@ -35,6 +35,7 @@ VideoDecoder::VideoDecoder (shared_ptr<const VideoContent> c)
 #else
        : _video_content (c)
 #endif
+       , _same (false)
 {
 
 }
@@ -123,8 +124,10 @@ VideoDecoder::get_video (VideoFrame frame, bool accurate)
 void
 VideoDecoder::video (shared_ptr<const ImageProxy> image, VideoFrame frame)
 {
-       /* We should not receive the same thing twice */
-       assert (_decoded_video.empty() || frame != _decoded_video.back().frame);
+       /* We may receive the same frame index twice for 3D, and we need to know
+          when that happens.
+       */
+       _same = (!_decoded_video.empty() && frame == _decoded_video.back().frame);
 
        /* Fill in gaps */
        /* XXX: 3D */
@@ -148,7 +151,7 @@ VideoDecoder::video (shared_ptr<const ImageProxy> image, VideoFrame frame)
                _decoded_video.push_back (ContentVideo (image, EYES_BOTH, PART_WHOLE, frame));
                break;
        case VIDEO_FRAME_TYPE_3D_ALTERNATE:
-               _decoded_video.push_back (ContentVideo (image, (frame % 2) ? EYES_RIGHT : EYES_LEFT, PART_WHOLE, frame));
+               _decoded_video.push_back (ContentVideo (image, _same ? EYES_RIGHT : EYES_LEFT, PART_WHOLE, frame));
                break;
        case VIDEO_FRAME_TYPE_3D_LEFT_RIGHT:
                _decoded_video.push_back (ContentVideo (image, EYES_LEFT, PART_LEFT_HALF, frame));
index 2c0028fd1bcfa4ce6c6a8c831452608b89467041..f5c3cd743ba43bf3df96d4a1706507a33a0e3a95 100644 (file)
@@ -60,6 +60,7 @@ protected:
 
        boost::shared_ptr<const VideoContent> _video_content;
        std::list<ContentVideo> _decoded_video;
+       bool _same;
 };
 
 #endif
index 9937133ecdd4b6f9eb5765e6e4f305827f4420c4..21bc187d9e32b23d7d1ae82be7259dc7510a7f6b 100644 (file)
@@ -15,7 +15,10 @@ sources = """
           content_factory.cc
           content_subtitle.cc
           cross.cc
+          dcp_content.cc
           dcp_content_type.cc
+          dcp_decoder.cc
+          dcp_examiner.cc
           dcp_video.cc
           dcpomatic_time.cc
           dolby_cp750.cc
@@ -59,6 +62,7 @@ sources = """
           send_kdm_email_job.cc
           server.cc
           server_finder.cc
+          single_stream_audio_content.cc
           sndfile_content.cc
           sndfile_decoder.cc
           sound_processor.cc
index 5b4f6e8fe75c38a86c1dbc2b514629d1136f40f1..ad7451cb93097a2dda9166e58a2c65e01152958d 100644 (file)
@@ -45,6 +45,7 @@
 #include "lib/playlist.h"
 #include "lib/content.h"
 #include "lib/content_factory.h"
+#include "lib/dcp_content.h"
 #include "timecode.h"
 #include "wx_util.h"
 #include "film_editor.h"
@@ -836,16 +837,22 @@ FilmEditor::content_add_folder_clicked ()
                return;
        }
 
-       shared_ptr<ImageContent> ic;
+       shared_ptr<Content> content;
        
        try {
-               ic.reset (new ImageContent (_film, boost::filesystem::path (wx_to_std (d->GetPath ()))));
-       } catch (FileError& e) {
-               error_dialog (this, std_to_wx (e.what ()));
-               return;
+               content.reset (new ImageContent (_film, boost::filesystem::path (wx_to_std (d->GetPath ()))));
+       } catch (...) {
+               try {
+                       content.reset (new DCPContent (_film, boost::filesystem::path (wx_to_std (d->GetPath ()))));
+               } catch (...) {
+                       error_dialog (this, _("Could not find any images nor a DCP in that folder"));
+                       return;
+               }
        }
 
-       _film->examine_and_add_content (ic);
+       if (content) {
+               _film->examine_and_add_content (content);
+       }
 }
 
 void