diff options
| author | Carl Hetherington <cth@carlh.net> | 2013-11-23 20:24:51 +0000 |
|---|---|---|
| committer | Carl Hetherington <cth@carlh.net> | 2013-11-23 20:24:51 +0000 |
| commit | c3da7c64f01420447dbab7f5c2ea42ff1b911cc5 (patch) | |
| tree | 77832e3b515987132ce8fe64ee82eaa2e87b8bd0 /src/lib | |
| parent | bab2a1cf99c58dcb598fed383015b1937d3ea07f (diff) | |
Basics of joining.
Diffstat (limited to 'src/lib')
| -rw-r--r-- | src/lib/audio_content.cc | 26 | ||||
| -rw-r--r-- | src/lib/audio_content.h | 1 | ||||
| -rw-r--r-- | src/lib/content.cc | 51 | ||||
| -rw-r--r-- | src/lib/content.h | 1 | ||||
| -rw-r--r-- | src/lib/exceptions.h | 9 | ||||
| -rw-r--r-- | src/lib/ffmpeg_content.cc | 41 | ||||
| -rw-r--r-- | src/lib/ffmpeg_content.h | 5 | ||||
| -rw-r--r-- | src/lib/ffmpeg_examiner.cc | 40 | ||||
| -rw-r--r-- | src/lib/ffmpeg_examiner.h | 2 | ||||
| -rw-r--r-- | src/lib/subtitle_content.cc | 27 | ||||
| -rw-r--r-- | src/lib/subtitle_content.h | 1 | ||||
| -rw-r--r-- | src/lib/util.cc | 57 | ||||
| -rw-r--r-- | src/lib/util.h | 3 | ||||
| -rw-r--r-- | src/lib/video_content.cc | 45 | ||||
| -rw-r--r-- | src/lib/video_content.h | 1 |
15 files changed, 235 insertions, 75 deletions
diff --git a/src/lib/audio_content.cc b/src/lib/audio_content.cc index e0eaacb91..04823d1e6 100644 --- a/src/lib/audio_content.cc +++ b/src/lib/audio_content.cc @@ -22,8 +22,12 @@ #include "analyse_audio_job.h" #include "job_manager.h" #include "film.h" +#include "exceptions.h" + +#include "i18n.h" using std::string; +using std::vector; using boost::shared_ptr; using boost::lexical_cast; using boost::dynamic_pointer_cast; @@ -60,6 +64,28 @@ AudioContent::AudioContent (shared_ptr<const Film> f, shared_ptr<const cxml::Nod _audio_delay = node->number_child<int> ("AudioDelay"); } +AudioContent::AudioContent (shared_ptr<const Film> f, vector<shared_ptr<Content> > c) + : Content (f, c) +{ + shared_ptr<AudioContent> ref = dynamic_pointer_cast<AudioContent> (c[0]); + assert (ref); + + for (size_t i = 0; i < c.size(); ++i) { + shared_ptr<AudioContent> ac = dynamic_pointer_cast<AudioContent> (c[i]); + + if (ac->audio_gain() != ref->audio_gain()) { + throw JoinError (_("Content to be joined must have the same audio gain.")); + } + + if (ac->audio_delay() != ref->audio_delay()) { + throw JoinError (_("Content to be joined must have the same audio delay.")); + } + } + + _audio_gain = ref->audio_gain (); + _audio_delay = ref->audio_delay (); +} + void AudioContent::as_xml (xmlpp::Node* node) const { diff --git a/src/lib/audio_content.h b/src/lib/audio_content.h index 73919105d..b100d7aba 100644 --- a/src/lib/audio_content.h +++ b/src/lib/audio_content.h @@ -46,6 +46,7 @@ public: AudioContent (boost::shared_ptr<const Film>, Time); AudioContent (boost::shared_ptr<const Film>, boost::filesystem::path); AudioContent (boost::shared_ptr<const Film>, boost::shared_ptr<const cxml::Node>); + AudioContent (boost::shared_ptr<const Film>, std::vector<boost::shared_ptr<Content> >); void as_xml (xmlpp::Node *) const; std::string technical_summary () const; diff --git a/src/lib/content.cc b/src/lib/content.cc index 16069b7a7..eaa55790b 100644 --- a/src/lib/content.cc +++ b/src/lib/content.cc @@ -24,11 +24,16 @@ #include "util.h" #include "content_factory.h" #include "ui_signaller.h" +#include "exceptions.h" + +#include "i18n.h" using std::string; using std::stringstream; using std::set; +using std::list; using std::cout; +using std::vector; using boost::shared_ptr; using boost::lexical_cast; @@ -72,19 +77,42 @@ Content::Content (shared_ptr<const Film> f, shared_ptr<const cxml::Node> node) : _film (f) , _change_signals_frequent (false) { - _paths.push_back (node->string_child ("Path")); + list<cxml::NodePtr> path_children = node->node_children ("Path"); + for (list<cxml::NodePtr>::const_iterator i = path_children.begin(); i != path_children.end(); ++i) { + _paths.push_back ((*i)->content ()); + } _digest = node->string_child ("Digest"); _position = node->number_child<Time> ("Position"); _trim_start = node->number_child<Time> ("TrimStart"); _trim_end = node->number_child<Time> ("TrimEnd"); } +Content::Content (shared_ptr<const Film> f, vector<shared_ptr<Content> > c) + : _film (f) + , _position (c.front()->position ()) + , _trim_start (c.front()->trim_start ()) + , _trim_end (c.back()->trim_end ()) + , _change_signals_frequent (false) +{ + for (size_t i = 0; i < c.size(); ++i) { + if (i > 0 && c[i]->trim_start ()) { + throw JoinError (_("Only the first piece of content to be joined can have a start trim.")); + } + + if (i < (c.size() - 1) && c[i]->trim_end ()) { + throw JoinError (_("Only the last piece of content to be joined can have an end trim.")); + } + } +} + void Content::as_xml (xmlpp::Node* node) const { boost::mutex::scoped_lock lm (_mutex); - - node->add_child("Path")->add_child_text (_paths.front().string()); + + for (vector<boost::filesystem::path>::const_iterator i = _paths.begin(); i != _paths.end(); ++i) { + node->add_child("Path")->add_child_text (i->string ()); + } node->add_child("Digest")->add_child_text (_digest); node->add_child("Position")->add_child_text (lexical_cast<string> (_position)); node->add_child("TrimStart")->add_child_text (lexical_cast<string> (_trim_start)); @@ -95,15 +123,10 @@ void Content::examine (shared_ptr<Job> job) { boost::mutex::scoped_lock lm (_mutex); - boost::filesystem::path p = _paths.front (); + vector<boost::filesystem::path> p = _paths; lm.unlock (); - string d; - if (boost::filesystem::is_regular_file (p)) { - d = md5_digest (p); - } else { - d = md5_digest_directory (p, job); - } + string const d = md5_digest (p, job); lm.lock (); _digest = d; @@ -206,7 +229,13 @@ Content::identifier () const bool Content::path_valid () const { - return boost::filesystem::exists (_paths.front ()); + for (vector<boost::filesystem::path>::const_iterator i = _paths.begin(); i != _paths.end(); ++i) { + if (!boost::filesystem::exists (*i)) { + return false; + } + } + + return true; } void diff --git a/src/lib/content.h b/src/lib/content.h index b4d90b22f..9cf6d866a 100644 --- a/src/lib/content.h +++ b/src/lib/content.h @@ -52,6 +52,7 @@ public: Content (boost::shared_ptr<const Film>, Time); Content (boost::shared_ptr<const Film>, boost::filesystem::path); Content (boost::shared_ptr<const Film>, boost::shared_ptr<const cxml::Node>); + Content (boost::shared_ptr<const Film>, std::vector<boost::shared_ptr<Content> >); virtual ~Content () {} virtual void examine (boost::shared_ptr<Job>); diff --git a/src/lib/exceptions.h b/src/lib/exceptions.h index b04d973dc..f4631c09b 100644 --- a/src/lib/exceptions.h +++ b/src/lib/exceptions.h @@ -104,7 +104,14 @@ private: /** name of the file that this exception concerns */ boost::filesystem::path _file; }; - + +class JoinError : public StringError +{ +public: + JoinError (std::string s) + : StringError (s) + {} +}; /** @class OpenFileError. * @brief Indicates that some error occurred when trying to open a file. diff --git a/src/lib/ffmpeg_content.cc b/src/lib/ffmpeg_content.cc index 4ca2ddc29..e843e1e16 100644 --- a/src/lib/ffmpeg_content.cc +++ b/src/lib/ffmpeg_content.cc @@ -26,6 +26,7 @@ #include "filter.h" #include "film.h" #include "log.h" +#include "exceptions.h" #include "i18n.h" @@ -37,6 +38,7 @@ using std::cout; using std::pair; using boost::shared_ptr; using boost::lexical_cast; +using boost::dynamic_pointer_cast; int const FFmpegContentProperty::SUBTITLE_STREAMS = 100; int const FFmpegContentProperty::SUBTITLE_STREAM = 101; @@ -83,6 +85,33 @@ FFmpegContent::FFmpegContent (shared_ptr<const Film> f, shared_ptr<const cxml::N _first_video = node->optional_number_child<double> ("FirstVideo"); } +FFmpegContent::FFmpegContent (shared_ptr<const Film> f, vector<boost::shared_ptr<Content> > c) + : Content (f, c) + , VideoContent (f, c) + , AudioContent (f, c) + , SubtitleContent (f, c) +{ + shared_ptr<FFmpegContent> ref = dynamic_pointer_cast<FFmpegContent> (c[0]); + assert (ref); + + for (size_t i = 0; i < c.size(); ++i) { + shared_ptr<FFmpegContent> fc = dynamic_pointer_cast<FFmpegContent> (c[i]); + if (*(fc->_subtitle_stream.get()) != *(ref->_subtitle_stream.get())) { + throw JoinError (_("Content to be joined must use the same subtitle stream.")); + } + + if (*(fc->_audio_stream.get()) != *(ref->_audio_stream.get())) { + throw JoinError (_("Content to be joined must use the same audio stream.")); + } + } + + _subtitle_streams = ref->subtitle_streams (); + _subtitle_stream = ref->subtitle_stream (); + _audio_streams = ref->audio_streams (); + _audio_stream = ref->audio_stream (); + _first_video = ref->_first_video; +} + void FFmpegContent::as_xml (xmlpp::Node* node) const { @@ -300,11 +329,23 @@ operator== (FFmpegSubtitleStream const & a, FFmpegSubtitleStream const & b) } bool +operator!= (FFmpegSubtitleStream const & a, FFmpegSubtitleStream const & b) +{ + return a.id != b.id; +} + +bool operator== (FFmpegAudioStream const & a, FFmpegAudioStream const & b) { return a.id == b.id; } +bool +operator!= (FFmpegAudioStream const & a, FFmpegAudioStream const & b) +{ + return a.id != b.id; +} + FFmpegAudioStream::FFmpegAudioStream (shared_ptr<const cxml::Node> node) : mapping (node->node_child ("Mapping")) { diff --git a/src/lib/ffmpeg_content.h b/src/lib/ffmpeg_content.h index 775cb9220..4576aaf45 100644 --- a/src/lib/ffmpeg_content.h +++ b/src/lib/ffmpeg_content.h @@ -63,6 +63,7 @@ private: }; extern bool operator== (FFmpegAudioStream const & a, FFmpegAudioStream const & b); +extern bool operator!= (FFmpegAudioStream const & a, FFmpegAudioStream const & b); class FFmpegSubtitleStream { @@ -81,6 +82,7 @@ public: }; extern bool operator== (FFmpegSubtitleStream const & a, FFmpegSubtitleStream const & b); +extern bool operator!= (FFmpegSubtitleStream const & a, FFmpegSubtitleStream const & b); class FFmpegContentProperty : public VideoContentProperty { @@ -97,6 +99,7 @@ class FFmpegContent : public VideoContent, public AudioContent, public SubtitleC public: FFmpegContent (boost::shared_ptr<const Film>, boost::filesystem::path); FFmpegContent (boost::shared_ptr<const Film>, boost::shared_ptr<const cxml::Node>); + FFmpegContent (boost::shared_ptr<const Film>, std::vector<boost::shared_ptr<Content> >); boost::shared_ptr<FFmpegContent> shared_from_this () { return boost::dynamic_pointer_cast<FFmpegContent> (Content::shared_from_this ()); @@ -153,7 +156,7 @@ public: boost::mutex::scoped_lock lm (_mutex); return _first_video; } - + private: friend class ffmpeg_pts_offset_test; diff --git a/src/lib/ffmpeg_examiner.cc b/src/lib/ffmpeg_examiner.cc index 4d10aabbb..8e4f24720 100644 --- a/src/lib/ffmpeg_examiner.cc +++ b/src/lib/ffmpeg_examiner.cc @@ -50,12 +50,12 @@ FFmpegExaminer::FFmpegExaminer (shared_ptr<const FFmpegContent> c) _audio_streams.push_back ( shared_ptr<FFmpegAudioStream> ( - new FFmpegAudioStream (stream_name (s), i, s->codec->sample_rate, s->codec->channels) + new FFmpegAudioStream (audio_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))); + _subtitle_streams.push_back (shared_ptr<FFmpegSubtitleStream> (new FFmpegSubtitleStream (subtitle_stream_name (s), i))); } } @@ -141,6 +141,36 @@ FFmpegExaminer::video_length () const } string +FFmpegExaminer::audio_stream_name (AVStream* s) const +{ + stringstream n; + + n << stream_name (s); + + if (!n.str().empty()) { + n << "; "; + } + + n << s->codec->channels << " channels"; + + return n.str (); +} + +string +FFmpegExaminer::subtitle_stream_name (AVStream* s) const +{ + stringstream n; + + n << stream_name (s); + + if (n.str().empty()) { + n << _("unknown"); + } + + return n.str (); +} + +string FFmpegExaminer::stream_name (AVStream* s) const { stringstream n; @@ -160,11 +190,5 @@ FFmpegExaminer::stream_name (AVStream* s) const } } - if (!n.str().empty()) { - n << "; "; - } - - n << s->codec->channels << " channels"; - return n.str (); } diff --git a/src/lib/ffmpeg_examiner.h b/src/lib/ffmpeg_examiner.h index 4912d899a..4de475d2a 100644 --- a/src/lib/ffmpeg_examiner.h +++ b/src/lib/ffmpeg_examiner.h @@ -47,6 +47,8 @@ public: private: std::string stream_name (AVStream* s) const; + std::string audio_stream_name (AVStream* s) const; + std::string subtitle_stream_name (AVStream* s) const; boost::optional<double> frame_time (int) const; std::vector<boost::shared_ptr<FFmpegSubtitleStream> > _subtitle_streams; diff --git a/src/lib/subtitle_content.cc b/src/lib/subtitle_content.cc index 1194e2dc9..6c0d3af86 100644 --- a/src/lib/subtitle_content.cc +++ b/src/lib/subtitle_content.cc @@ -20,10 +20,15 @@ #include <libcxml/cxml.h> #include "subtitle_content.h" #include "util.h" +#include "exceptions.h" + +#include "i18n.h" using std::string; +using std::vector; using boost::shared_ptr; using boost::lexical_cast; +using boost::dynamic_pointer_cast; int const SubtitleContentProperty::SUBTITLE_OFFSET = 500; int const SubtitleContentProperty::SUBTITLE_SCALE = 501; @@ -47,6 +52,28 @@ SubtitleContent::SubtitleContent (shared_ptr<const Film> f, shared_ptr<const cxm _subtitle_scale = node->number_child<float> ("SubtitleScale"); } +SubtitleContent::SubtitleContent (shared_ptr<const Film> f, vector<shared_ptr<Content> > c) + : Content (f, c) +{ + shared_ptr<SubtitleContent> ref = dynamic_pointer_cast<SubtitleContent> (c[0]); + assert (ref); + + for (size_t i = 0; i < c.size(); ++i) { + shared_ptr<SubtitleContent> sc = dynamic_pointer_cast<SubtitleContent> (c[i]); + + if (sc->subtitle_offset() != ref->subtitle_offset()) { + throw JoinError (_("Content to be joined must have the same subtitle offset.")); + } + + if (sc->subtitle_scale() != ref->subtitle_scale()) { + throw JoinError (_("Content to be joined must have the same subtitle scale.")); + } + } + + _subtitle_offset = ref->subtitle_offset (); + _subtitle_scale = ref->subtitle_scale (); +} + void SubtitleContent::as_xml (xmlpp::Node* root) const { diff --git a/src/lib/subtitle_content.h b/src/lib/subtitle_content.h index c29485fee..854647d18 100644 --- a/src/lib/subtitle_content.h +++ b/src/lib/subtitle_content.h @@ -34,6 +34,7 @@ class SubtitleContent : public virtual Content public: SubtitleContent (boost::shared_ptr<const Film>, boost::filesystem::path); SubtitleContent (boost::shared_ptr<const Film>, boost::shared_ptr<const cxml::Node>); + SubtitleContent (boost::shared_ptr<const Film>, std::vector<boost::shared_ptr<Content> >); void as_xml (xmlpp::Node *) const; diff --git a/src/lib/util.cc b/src/lib/util.cc index 484c4fb9b..7ae4f8b16 100644 --- a/src/lib/util.cc +++ b/src/lib/util.cc @@ -386,47 +386,9 @@ md5_digest (void const * data, int size) return s.str (); } -/** @param file File name. - * @return MD5 digest of file's contents. - */ -string -md5_digest (boost::filesystem::path file) -{ - ifstream f (file.string().c_str(), std::ios::binary); - if (!f.good ()) { - throw OpenFileError (file.string()); - } - - f.seekg (0, std::ios::end); - int bytes = f.tellg (); - f.seekg (0, std::ios::beg); - - int const buffer_size = 64 * 1024; - char buffer[buffer_size]; - - MD5_CTX md5_context; - MD5_Init (&md5_context); - while (bytes > 0) { - int const t = min (bytes, buffer_size); - f.read (buffer, t); - MD5_Update (&md5_context, buffer, t); - bytes -= t; - } - - unsigned char digest[MD5_DIGEST_LENGTH]; - MD5_Final (digest, &md5_context); - - stringstream s; - for (int i = 0; i < MD5_DIGEST_LENGTH; ++i) { - s << std::hex << std::setfill('0') << std::setw(2) << ((int) digest[i]); - } - - return s.str (); -} - /** @param job Optional job for which to report progress */ string -md5_digest_directory (boost::filesystem::path directory, shared_ptr<Job> job) +md5_digest (vector<boost::filesystem::path> files, shared_ptr<Job> job) { int const buffer_size = 64 * 1024; char buffer[buffer_size]; @@ -434,18 +396,10 @@ md5_digest_directory (boost::filesystem::path directory, shared_ptr<Job> job) MD5_CTX md5_context; MD5_Init (&md5_context); - int files = 0; - if (job) { - for (boost::filesystem::directory_iterator i(directory); i != boost::filesystem::directory_iterator(); ++i) { - ++files; - } - } - - int j = 0; - for (boost::filesystem::directory_iterator i(directory); i != boost::filesystem::directory_iterator(); ++i) { - ifstream f (i->path().string().c_str(), std::ios::binary); + for (size_t i = 0; i < files.size(); ++i) { + ifstream f (files[i].string().c_str(), std::ios::binary); if (!f.good ()) { - throw OpenFileError (i->path().string()); + throw OpenFileError (files[i].string()); } f.seekg (0, std::ios::end); @@ -460,8 +414,7 @@ md5_digest_directory (boost::filesystem::path directory, shared_ptr<Job> job) } if (job) { - job->set_progress (float (j) / files); - ++j; + job->set_progress (float (i) / files.size ()); } } diff --git a/src/lib/util.h b/src/lib/util.h index 5e568cc27..7dcd920b7 100644 --- a/src/lib/util.h +++ b/src/lib/util.h @@ -67,8 +67,7 @@ extern double seconds (struct timeval); extern void dcpomatic_setup (); extern void dcpomatic_setup_gettext_i18n (std::string); extern std::vector<std::string> split_at_spaces_considering_quotes (std::string); -extern std::string md5_digest (boost::filesystem::path); -extern std::string md5_digest_directory (boost::filesystem::path, boost::shared_ptr<Job>); +extern std::string md5_digest (std::vector<boost::filesystem::path>, boost::shared_ptr<Job>); extern std::string md5_digest (void const *, int); extern void ensure_ui_thread (); extern std::string audio_channel_name (int); diff --git a/src/lib/video_content.cc b/src/lib/video_content.cc index 743e6eb18..ca4ed8a9f 100644 --- a/src/lib/video_content.cc +++ b/src/lib/video_content.cc @@ -28,6 +28,7 @@ #include "colour_conversion.h" #include "util.h" #include "film.h" +#include "exceptions.h" #include "i18n.h" @@ -42,9 +43,11 @@ using std::string; using std::stringstream; using std::setprecision; using std::cout; +using std::vector; using boost::shared_ptr; using boost::lexical_cast; using boost::optional; +using boost::dynamic_pointer_cast; VideoContent::VideoContent (shared_ptr<const Film> f) : Content (f) @@ -96,6 +99,48 @@ VideoContent::VideoContent (shared_ptr<const Film> f, shared_ptr<const cxml::Nod _colour_conversion = ColourConversion (node->node_child ("ColourConversion")); } +VideoContent::VideoContent (shared_ptr<const Film> f, vector<shared_ptr<Content> > c) + : Content (f, c) +{ + shared_ptr<VideoContent> ref = dynamic_pointer_cast<VideoContent> (c[0]); + assert (ref); + + for (size_t i = 0; i < c.size(); ++i) { + shared_ptr<VideoContent> vc = dynamic_pointer_cast<VideoContent> (c[i]); + + if (vc->video_size() != ref->video_size()) { + throw JoinError (_("Content to be joined must have the same picture size.")); + } + + if (vc->video_frame_rate() != ref->video_frame_rate()) { + throw JoinError (_("Content to be joined must have the same video frame rate.")); + } + + if (vc->video_frame_type() != ref->video_frame_type()) { + throw JoinError (_("Content to be joined must have the same video frame type.")); + } + + if (vc->crop() != ref->crop()) { + throw JoinError (_("Content to be joined must have the same crop.")); + } + + if (vc->ratio() != ref->ratio()) { + throw JoinError (_("Content to be joined must have the same ratio.")); + } + + if (vc->colour_conversion() != ref->colour_conversion()) { + throw JoinError (_("Content to be joined must have the same colour conversion.")); + } + } + + _video_size = ref->video_size (); + _video_frame_rate = ref->video_frame_rate (); + _video_frame_type = ref->video_frame_type (); + _crop = ref->crop (); + _ratio = ref->ratio (); + _colour_conversion = ref->colour_conversion (); +} + void VideoContent::as_xml (xmlpp::Node* node) const { diff --git a/src/lib/video_content.h b/src/lib/video_content.h index 0fc6d4e7a..effca5c61 100644 --- a/src/lib/video_content.h +++ b/src/lib/video_content.h @@ -46,6 +46,7 @@ public: VideoContent (boost::shared_ptr<const Film>, Time, VideoContent::Frame); VideoContent (boost::shared_ptr<const Film>, boost::filesystem::path); VideoContent (boost::shared_ptr<const Film>, boost::shared_ptr<const cxml::Node>); + VideoContent (boost::shared_ptr<const Film>, std::vector<boost::shared_ptr<Content> >); void as_xml (xmlpp::Node *) const; std::string technical_summary () const; |
