From: Carl Hetherington Date: Wed, 9 Jul 2014 08:36:58 +0000 (+0100) Subject: Basics of DCP import. X-Git-Tag: v2.0.48~749 X-Git-Url: https://git.carlh.net/gitweb/?a=commitdiff_plain;ds=sidebyside;h=2cf3da72a017eebf741dfb9a5ec158df94a4e7b7;p=dcpomatic.git Basics of DCP import. --- diff --git a/src/lib/audio_content.cc b/src/lib/audio_content.cc index 9f0d26573..6317aa4cb 100644 --- a/src/lib/audio_content.cc +++ b/src/lib/audio_content.cc @@ -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 f) + : Content (f) + , _audio_gain (0) + , _audio_delay (Config::instance()->default_audio_delay ()) +{ + +} + AudioContent::AudioContent (shared_ptr f, DCPTime s) : Content (f, s) , _audio_gain (0) diff --git a/src/lib/audio_content.h b/src/lib/audio_content.h index 336a98629..131ced61a 100644 --- a/src/lib/audio_content.h +++ b/src/lib/audio_content.h @@ -50,6 +50,7 @@ class AudioContent : public virtual Content public: typedef int64_t Frame; + AudioContent (boost::shared_ptr); AudioContent (boost::shared_ptr, DCPTime); AudioContent (boost::shared_ptr, boost::filesystem::path); AudioContent (boost::shared_ptr, cxml::ConstNodePtr); diff --git a/src/lib/audio_examiner.h b/src/lib/audio_examiner.h new file mode 100644 index 000000000..64a98ba17 --- /dev/null +++ b/src/lib/audio_examiner.h @@ -0,0 +1,28 @@ +/* + Copyright (C) 2014 Carl Hetherington + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +class 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 index 000000000..663846e26 --- /dev/null +++ b/src/lib/dcp_content.cc @@ -0,0 +1,92 @@ +/* + Copyright (C) 2014 Carl Hetherington + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include +#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 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->set_progress_unknown (); + Content::examine (job); + shared_ptr 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 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 index 000000000..7f3ac956d --- /dev/null +++ b/src/lib/dcp_content.h @@ -0,0 +1,49 @@ +/* + Copyright (C) 2014 Carl Hetherington + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#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 f, boost::filesystem::path p); + + boost::shared_ptr shared_from_this () { + return boost::dynamic_pointer_cast (Content::shared_from_this ()); + } + + DCPTime full_length () const; + + void examine (boost::shared_ptr); + 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 index 000000000..14672a23b --- /dev/null +++ b/src/lib/dcp_decoder.cc @@ -0,0 +1,119 @@ +/* + Copyright (C) 2014 Carl Hetherington + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#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 c, shared_ptr 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 mxf = (*_reel)->main_picture()->mxf (); + shared_ptr mono = dynamic_pointer_cast (mxf); + shared_ptr stereo = dynamic_pointer_cast (mxf); + if (mono) { + shared_ptr image (new Image (PIX_FMT_RGB24, mxf->size(), false)); + mono->get_frame (_next.frames (vfr))->rgb_frame (image->data()[0]); + shared_ptr aligned (new Image (image, true)); + video (shared_ptr (new RawImageProxy (aligned, _log)), _next.frames (vfr)); + } else { + + shared_ptr 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 aligned_left (new Image (left, true)); + video (shared_ptr (new RawImageProxy (aligned_left, _log)), _next.frames (vfr)); + + shared_ptr 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 aligned_right (new Image (right, true)); + video (shared_ptr (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 +DCPDecoder::subtitles_during (ContentTimePeriod, bool starting) const +{ + return list (); +} diff --git a/src/lib/dcp_decoder.h b/src/lib/dcp_decoder.h new file mode 100644 index 000000000..d81b20b5c --- /dev/null +++ b/src/lib/dcp_decoder.h @@ -0,0 +1,46 @@ +/* + Copyright (C) 2014 Carl Hetherington + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#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, boost::shared_ptr); + +private: + void seek (ContentTime t, bool accurate); + bool pass (); + std::list subtitles_during (ContentTimePeriod, bool starting) const; + + ContentTime _next; + std::list > _reels; + std::list >::iterator _reel; + boost::shared_ptr _log; + boost::shared_ptr _dcp_content; +}; diff --git a/src/lib/dcp_examiner.cc b/src/lib/dcp_examiner.cc new file mode 100644 index 000000000..7ce18c9f7 --- /dev/null +++ b/src/lib/dcp_examiner.cc @@ -0,0 +1,84 @@ +/* + Copyright (C) 2014 Carl Hetherington + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include +#include +#include +#include +#include +#include +#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 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 > reels = dcp.cpls().front()->reels (); + for (list >::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 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 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 index 000000000..c8b0ef7da --- /dev/null +++ b/src/lib/dcp_examiner.h @@ -0,0 +1,47 @@ +/* + Copyright (C) 2014 Carl Hetherington + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include "video_examiner.h" + +class DCPContent; + +class DCPExaminer : public VideoExaminer +{ +public: + DCPExaminer (boost::shared_ptr); + + 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 _video_frame_rate; + boost::optional _video_size; + ContentTime _video_length; + boost::optional _audio_channels; + boost::optional _audio_frame_rate; +}; diff --git a/src/lib/exceptions.h b/src/lib/exceptions.h index 38452ffc0..a8969d702 100644 --- a/src/lib/exceptions.h +++ b/src/lib/exceptions.h @@ -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 * diff --git a/src/lib/ffmpeg_content.cc b/src/lib/ffmpeg_content.cc index da4acb5f8..d2bb329db 100644 --- a/src/lib/ffmpeg_content.cc +++ b/src/lib/ffmpeg_content.cc @@ -174,7 +174,6 @@ FFmpegContent::examine (shared_ptr job) shared_ptr 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); diff --git a/src/lib/player.cc b/src/lib/player.cc index ebec19b2d..9f9f8db2e 100644 --- a/src/lib/player.cc +++ b/src/lib/player.cc @@ -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 dc = dynamic_pointer_cast (*i); + if (dc) { + decoder.reset (new DCPDecoder (dc, _film->log ())); + frc = FrameRateChange (dc->video_frame_rate(), _film->video_frame_rate()); + } + /* ImageContent */ shared_ptr ic = dynamic_pointer_cast (*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 index 000000000..ac4da25ee --- /dev/null +++ b/src/lib/single_stream_audio_content.cc @@ -0,0 +1,103 @@ +/* + Copyright (C) 2014 Carl Hetherington + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include +#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 f) + : Content (f) + , AudioContent (f) + , _audio_channels (0) + , _audio_length (0) + , _audio_frame_rate (0) +{ + +} + +SingleStreamAudioContent::SingleStreamAudioContent (shared_ptr 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 f, cxml::ConstNodePtr node, int version) + : Content (f, node) + , AudioContent (f, node) + , _audio_mapping (node->node_child ("AudioMapping"), version) +{ + _audio_channels = node->number_child ("AudioChannels"); + _audio_length = ContentTime (node->number_child ("AudioLength")); + _audio_frame_rate = node->number_child ("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 (audio_channels ())); + node->add_child("AudioLength")->add_child_text (raw_convert (audio_length().get ())); + node->add_child("AudioFrameRate")->add_child_text (raw_convert (audio_frame_rate ())); + _audio_mapping.as_xml (node->add_child("AudioMapping")); +} + +void +SingleStreamAudioContent::take_from_audio_examiner (shared_ptr 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 index 000000000..588c250fe --- /dev/null +++ b/src/lib/single_stream_audio_content.h @@ -0,0 +1,68 @@ +/* + Copyright (C) 2014 Carl Hetherington + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#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); + SingleStreamAudioContent (boost::shared_ptr, boost::filesystem::path); + SingleStreamAudioContent (boost::shared_ptr 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); + +protected: + int _audio_channels; + ContentTime _audio_length; + int _audio_frame_rate; + AudioMapping _audio_mapping; +}; + +#endif diff --git a/src/lib/sndfile_content.cc b/src/lib/sndfile_content.cc index aab69f306..a573d43c4 100644 --- a/src/lib/sndfile_content.cc +++ b/src/lib/sndfile_content.cc @@ -36,24 +36,27 @@ using dcp::raw_convert; SndfileContent::SndfileContent (shared_ptr 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 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 ("AudioChannels"); - _audio_length = ContentTime (node->number_child ("AudioLength")); - _audio_frame_rate = node->number_child ("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->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 (audio_channels ())); - node->add_child("AudioLength")->add_child_text (raw_convert (audio_length().get ())); - node->add_child("AudioFrameRate")->add_child_text (raw_convert (audio_frame_rate ())); - _audio_mapping.as_xml (node->add_child("AudioMapping")); + shared_ptr 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); -} diff --git a/src/lib/sndfile_content.h b/src/lib/sndfile_content.h index dcd6cb55d..75c723518 100644 --- a/src/lib/sndfile_content.h +++ b/src/lib/sndfile_content.h @@ -23,13 +23,13 @@ extern "C" { #include } -#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, boost::filesystem::path); @@ -39,43 +39,15 @@ public: return boost::dynamic_pointer_cast (Content::shared_from_this ()); } + DCPTime full_length () const; + void examine (boost::shared_ptr); 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 diff --git a/src/lib/sndfile_decoder.h b/src/lib/sndfile_decoder.h index 52590ef24..41d5faf08 100644 --- a/src/lib/sndfile_decoder.h +++ b/src/lib/sndfile_decoder.h @@ -20,10 +20,11 @@ #include #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 c); diff --git a/src/lib/subtitle_content.cc b/src/lib/subtitle_content.cc index f839a56d0..88e48b420 100644 --- a/src/lib/subtitle_content.cc +++ b/src/lib/subtitle_content.cc @@ -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 f) + : Content (f) + , _subtitle_use (false) + , _subtitle_x_offset (0) + , _subtitle_y_offset (0) + , _subtitle_scale (1) +{ + +} + SubtitleContent::SubtitleContent (shared_ptr f, boost::filesystem::path p) : Content (f, p) , _subtitle_use (false) diff --git a/src/lib/subtitle_content.h b/src/lib/subtitle_content.h index b73119bdb..f46a87c15 100644 --- a/src/lib/subtitle_content.h +++ b/src/lib/subtitle_content.h @@ -34,6 +34,7 @@ public: class SubtitleContent : public virtual Content { public: + SubtitleContent (boost::shared_ptr); SubtitleContent (boost::shared_ptr, boost::filesystem::path); SubtitleContent (boost::shared_ptr, cxml::ConstNodePtr, int version); SubtitleContent (boost::shared_ptr, std::vector >); diff --git a/src/lib/video_content.cc b/src/lib/video_content.cc index 5fd71076b..a2a4e6c6b 100644 --- a/src/lib/video_content.cc +++ b/src/lib/video_content.cc @@ -31,9 +31,12 @@ #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 d) _video_frame_rate = vfr; _video_length = vl; } + + shared_ptr 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); diff --git a/src/lib/video_decoder.cc b/src/lib/video_decoder.cc index bfd7a7e3e..5dd078553 100644 --- a/src/lib/video_decoder.cc +++ b/src/lib/video_decoder.cc @@ -35,6 +35,7 @@ VideoDecoder::VideoDecoder (shared_ptr c) #else : _video_content (c) #endif + , _same (false) { } @@ -123,8 +124,10 @@ VideoDecoder::get_video (VideoFrame frame, bool accurate) void VideoDecoder::video (shared_ptr 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 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)); diff --git a/src/lib/video_decoder.h b/src/lib/video_decoder.h index 2c0028fd1..f5c3cd743 100644 --- a/src/lib/video_decoder.h +++ b/src/lib/video_decoder.h @@ -60,6 +60,7 @@ protected: boost::shared_ptr _video_content; std::list _decoded_video; + bool _same; }; #endif diff --git a/src/lib/wscript b/src/lib/wscript index 9937133ec..21bc187d9 100644 --- a/src/lib/wscript +++ b/src/lib/wscript @@ -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 diff --git a/src/wx/film_editor.cc b/src/wx/film_editor.cc index 5b4f6e8fe..ad7451cb9 100644 --- a/src/wx/film_editor.cc +++ b/src/wx/film_editor.cc @@ -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 ic; + shared_ptr 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