summaryrefslogtreecommitdiff
path: root/src/lib
diff options
context:
space:
mode:
authorCarl Hetherington <cth@carlh.net>2013-11-23 20:24:51 +0000
committerCarl Hetherington <cth@carlh.net>2013-11-23 20:24:51 +0000
commitc3da7c64f01420447dbab7f5c2ea42ff1b911cc5 (patch)
tree77832e3b515987132ce8fe64ee82eaa2e87b8bd0 /src/lib
parentbab2a1cf99c58dcb598fed383015b1937d3ea07f (diff)
Basics of joining.
Diffstat (limited to 'src/lib')
-rw-r--r--src/lib/audio_content.cc26
-rw-r--r--src/lib/audio_content.h1
-rw-r--r--src/lib/content.cc51
-rw-r--r--src/lib/content.h1
-rw-r--r--src/lib/exceptions.h9
-rw-r--r--src/lib/ffmpeg_content.cc41
-rw-r--r--src/lib/ffmpeg_content.h5
-rw-r--r--src/lib/ffmpeg_examiner.cc40
-rw-r--r--src/lib/ffmpeg_examiner.h2
-rw-r--r--src/lib/subtitle_content.cc27
-rw-r--r--src/lib/subtitle_content.h1
-rw-r--r--src/lib/util.cc57
-rw-r--r--src/lib/util.h3
-rw-r--r--src/lib/video_content.cc45
-rw-r--r--src/lib/video_content.h1
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;