#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;
_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
{
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;
#include "util.h"
#include "content_factory.h"
#include "ui_signaller.h"
+#include "exceptions.h"
+#include "film.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;
int const ContentProperty::TRIM_START = 403;
int const ContentProperty::TRIM_END = 404;
+Content::Content (shared_ptr<const Film> f)
+ : _film (f)
+ , _position (0)
+ , _trim_start (0)
+ , _trim_end (0)
+ , _change_signals_frequent (false)
+{
+
+}
+
Content::Content (shared_ptr<const Film> f, Time p)
: _film (f)
, _position (p)
Content::Content (shared_ptr<const Film> f, boost::filesystem::path p)
: _film (f)
- , _path (p)
, _position (0)
, _trim_start (0)
, _trim_end (0)
, _change_signals_frequent (false)
{
-
+ _paths.push_back (p);
}
Content::Content (shared_ptr<const Film> f, shared_ptr<const cxml::Node> node)
: _film (f)
, _change_signals_frequent (false)
{
- _path = 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."));
+ }
+
+ for (size_t j = 0; j < c[i]->number_of_paths(); ++j) {
+ _paths.push_back (c[i]->path (j));
+ }
+ }
+}
+
void
Content::as_xml (xmlpp::Node* node) const
{
boost::mutex::scoped_lock lm (_mutex);
-
- node->add_child("Path")->add_child_text (_path.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));
Content::examine (shared_ptr<Job> job)
{
boost::mutex::scoped_lock lm (_mutex);
- boost::filesystem::path p = _path;
+ 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;
xmlpp::Document doc;
xmlpp::Node* node = doc.create_root_node ("Content");
as_xml (node);
- return content_factory (film, cxml::NodePtr(new cxml::Node (node)));
+ return content_factory (film, cxml::NodePtr (new cxml::Node (node)), Film::state_version);
}
string
Content::technical_summary () const
{
- return String::compose ("%1 %2 %3", path(), digest(), position());
+ return String::compose ("%1 %2 %3", path_summary(), digest(), position());
}
Time
bool
Content::path_valid () const
{
- return boost::filesystem::exists (_path);
+ for (vector<boost::filesystem::path>::const_iterator i = _paths.begin(); i != _paths.end(); ++i) {
+ if (!boost::filesystem::exists (*i)) {
+ return false;
+ }
+ }
+
+ return true;
}
void
Content::set_path (boost::filesystem::path path)
{
- _path = path;
+ _paths.clear ();
+ _paths.push_back (path);
signal_changed (ContentProperty::PATH);
}
-
+string
+Content::path_summary () const
+{
+ /* XXX: should handle multiple paths more gracefully */
+
+ assert (number_of_paths ());
+
+ string s = path(0).filename().string ();
+ if (number_of_paths() > 1) {
+ s += " ...";
+ }
+
+ return s;
+}
class Content : public boost::enable_shared_from_this<Content>, public boost::noncopyable
{
public:
+ Content (boost::shared_ptr<const Film>);
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>);
boost::shared_ptr<Content> clone () const;
void set_path (boost::filesystem::path);
-
- boost::filesystem::path path () const {
+
+ std::string path_summary () const;
+
+ std::vector<boost::filesystem::path> paths () const {
+ boost::mutex::scoped_lock lm (_mutex);
+ return _paths;
+ }
+
+ size_t number_of_paths () const {
boost::mutex::scoped_lock lm (_mutex);
- return _path;
+ return _paths.size ();
}
+ boost::filesystem::path path (size_t i) const {
+ boost::mutex::scoped_lock lm (_mutex);
+ return _paths[i];
+ }
+
bool path_valid () const;
/** @return MD5 digest of the content's file(s) */
*/
mutable boost::mutex _mutex;
+ /** Paths of our data files */
+ std::vector<boost::filesystem::path> _paths;
+
private:
- /** Path of a file or a directory containing files */
- boost::filesystem::path _path;
std::string _digest;
Time _position;
Time _trim_start;
#include <libcxml/cxml.h>
#include "ffmpeg_content.h"
-#include "still_image_content.h"
-#include "moving_image_content.h"
+#include "image_content.h"
#include "sndfile_content.h"
#include "util.h"
using boost::shared_ptr;
shared_ptr<Content>
-content_factory (shared_ptr<const Film> film, cxml::NodePtr node)
+content_factory (shared_ptr<const Film> film, cxml::NodePtr node, int version)
{
string const type = node->string_child ("Type");
boost::shared_ptr<Content> content;
if (type == "FFmpeg") {
- content.reset (new FFmpegContent (film, node));
- } else if (type == "StillImage") {
- content.reset (new StillImageContent (film, node));
- } else if (type == "MovingImage") {
- content.reset (new MovingImageContent (film, node));
+ content.reset (new FFmpegContent (film, node, version));
+ } else if (type == "Image") {
+ content.reset (new ImageContent (film, node, version));
} else if (type == "Sndfile") {
- content.reset (new SndfileContent (film, node));
+ content.reset (new SndfileContent (film, node, version));
}
return content;
shared_ptr<Content> content;
if (valid_image_file (path)) {
- content.reset (new StillImageContent (film, path));
+ content.reset (new ImageContent (film, path));
} else if (SndfileContent::valid_file (path)) {
content.reset (new SndfileContent (film, path));
} else {
class Film;
-extern boost::shared_ptr<Content> content_factory (boost::shared_ptr<const Film>, cxml::NodePtr);
+extern boost::shared_ptr<Content> content_factory (boost::shared_ptr<const Film>, cxml::NodePtr, int);
extern boost::shared_ptr<Content> content_factory (boost::shared_ptr<const Film>, boost::filesystem::path);
/** 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.
boost::mutex FFmpeg::_mutex;
-/** @param long_probe true to do a long probe of the file looking for streams */
-FFmpeg::FFmpeg (boost::shared_ptr<const FFmpegContent> c, bool long_probe)
+FFmpeg::FFmpeg (boost::shared_ptr<const FFmpegContent> c)
: _ffmpeg_content (c)
+ , _avio_buffer (0)
+ , _avio_buffer_size (4096)
+ , _avio_context (0)
, _format_context (0)
, _frame (0)
, _video_stream (-1)
{
- setup_general (long_probe);
+ setup_general ();
setup_video ();
setup_audio ();
}
avformat_close_input (&_format_context);
}
+static int
+avio_read_wrapper (void* data, uint8_t* buffer, int amount)
+{
+ return reinterpret_cast<FFmpeg*>(data)->avio_read (buffer, amount);
+}
+
+static int64_t
+avio_seek_wrapper (void* data, int64_t offset, int whence)
+{
+ return reinterpret_cast<FFmpeg*>(data)->avio_seek (offset, whence);
+}
+
void
-FFmpeg::setup_general (bool long_probe)
+FFmpeg::setup_general ()
{
av_register_all ();
+ _file_group.set_paths (_ffmpeg_content->paths ());
+ _avio_buffer = static_cast<uint8_t*> (av_malloc (_avio_buffer_size));
+ _avio_context = avio_alloc_context (_avio_buffer, _avio_buffer_size, 0, this, avio_read_wrapper, 0, avio_seek_wrapper);
+ _format_context = avformat_alloc_context ();
+ _format_context->pb = _avio_context;
+
AVDictionary* options = 0;
- if (long_probe) {
- /* These durations are in microseconds, and represent how far into the content file
- we will look for streams.
- */
- av_dict_set (&options, "analyzeduration", lexical_cast<string> (5 * 60 * 1e6).c_str(), 0);
- av_dict_set (&options, "probesize", lexical_cast<string> (5 * 60 * 1e6).c_str(), 0);
- }
+ /* These durations are in microseconds, and represent how far into the content file
+ we will look for streams.
+ */
+ av_dict_set (&options, "analyzeduration", lexical_cast<string> (5 * 60 * 1e6).c_str(), 0);
+ av_dict_set (&options, "probesize", lexical_cast<string> (5 * 60 * 1e6).c_str(), 0);
- if (avformat_open_input (&_format_context, _ffmpeg_content->path().string().c_str(), 0, &options) < 0) {
- throw OpenFileError (_ffmpeg_content->path().string ());
+ if (avformat_open_input (&_format_context, 0, 0, &options) < 0) {
+ throw OpenFileError (_ffmpeg_content->path(0).string ());
}
if (avformat_find_stream_info (_format_context, 0) < 0) {
AVCodecContext *
FFmpeg::audio_codec_context () const
{
- return _format_context->streams[_ffmpeg_content->audio_stream()->id]->codec;
+ return _ffmpeg_content->audio_stream()->stream(_format_context)->codec;
+}
+
+int
+FFmpeg::avio_read (uint8_t* buffer, int const amount)
+{
+ return _file_group.read (buffer, amount);
+}
+
+int64_t
+FFmpeg::avio_seek (int64_t const pos, int whence)
+{
+ if (whence == AVSEEK_SIZE) {
+ return _file_group.length ();
+ }
+
+ return _file_group.seek (pos, whence);
}
extern "C" {
#include <libavcodec/avcodec.h>
}
+#include "file_group.h"
struct AVFilterGraph;
struct AVCodecContext;
struct AVBufferContext;
struct AVCodec;
struct AVStream;
+struct AVIOContext;
class FFmpegContent;
class FFmpeg
{
public:
- FFmpeg (boost::shared_ptr<const FFmpegContent>, bool);
+ FFmpeg (boost::shared_ptr<const FFmpegContent>);
virtual ~FFmpeg ();
boost::shared_ptr<const FFmpegContent> ffmpeg_content () const {
return _ffmpeg_content;
}
+ int avio_read (uint8_t *, int);
+ int64_t avio_seek (int64_t, int);
+
protected:
AVCodecContext* video_codec_context () const;
AVCodecContext* audio_codec_context () const;
boost::shared_ptr<const FFmpegContent> _ffmpeg_content;
+ uint8_t* _avio_buffer;
+ int _avio_buffer_size;
+ AVIOContext* _avio_context;
+ FileGroup _file_group;
+
AVFormatContext* _format_context;
AVPacket _packet;
AVFrame* _frame;
+ /** Index of video stream within AVFormatContext */
int _video_stream;
/* It would appear (though not completely verified) that one must have
static boost::mutex _mutex;
private:
- void setup_general (bool);
+ void setup_general ();
void setup_video ();
void setup_audio ();
};
*/
+extern "C" {
+#include <libavformat/avformat.h>
+}
#include <libcxml/cxml.h>
#include "ffmpeg_content.h"
#include "ffmpeg_examiner.h"
#include "filter.h"
#include "film.h"
#include "log.h"
+#include "exceptions.h"
#include "i18n.h"
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;
}
-FFmpegContent::FFmpegContent (shared_ptr<const Film> f, shared_ptr<const cxml::Node> node)
+FFmpegContent::FFmpegContent (shared_ptr<const Film> f, shared_ptr<const cxml::Node> node, int version)
: Content (f, node)
, VideoContent (f, node)
, AudioContent (f, node)
{
list<cxml::NodePtr> c = node->node_children ("SubtitleStream");
for (list<cxml::NodePtr>::const_iterator i = c.begin(); i != c.end(); ++i) {
- _subtitle_streams.push_back (shared_ptr<FFmpegSubtitleStream> (new FFmpegSubtitleStream (*i)));
+ _subtitle_streams.push_back (shared_ptr<FFmpegSubtitleStream> (new FFmpegSubtitleStream (*i, version)));
if ((*i)->optional_number_child<int> ("Selected")) {
_subtitle_stream = _subtitle_streams.back ();
}
c = node->node_children ("AudioStream");
for (list<cxml::NodePtr>::const_iterator i = c.begin(); i != c.end(); ++i) {
- _audio_streams.push_back (shared_ptr<FFmpegAudioStream> (new FFmpegAudioStream (*i)));
+ _audio_streams.push_back (shared_ptr<FFmpegAudioStream> (new FFmpegAudioStream (*i, version)));
if ((*i)->optional_number_child<int> ("Selected")) {
_audio_stream = _audio_streams.back ();
}
_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 (f->with_subtitles() && *(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
{
FFmpegContent::summary () const
{
/* Get the string() here so that the name does not have quotes around it */
- return String::compose (_("%1 [movie]"), path().filename().string());
+ return String::compose (_("%1 [movie]"), path_summary ());
}
string
return a.id == b.id;
}
+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;
}
-FFmpegAudioStream::FFmpegAudioStream (shared_ptr<const cxml::Node> node)
- : mapping (node->node_child ("Mapping"))
+bool
+operator!= (FFmpegAudioStream const & a, FFmpegAudioStream const & b)
+{
+ return a.id != b.id;
+}
+
+FFmpegStream::FFmpegStream (shared_ptr<const cxml::Node> node, int version)
+ : _legacy_id (false)
{
name = node->string_child ("Name");
id = node->number_child<int> ("Id");
+ if (version == 4 || node->optional_bool_child ("LegacyId")) {
+ _legacy_id = true;
+ }
+}
+
+void
+FFmpegStream::as_xml (xmlpp::Node* root) const
+{
+ root->add_child("Name")->add_child_text (name);
+ root->add_child("Id")->add_child_text (lexical_cast<string> (id));
+ if (_legacy_id) {
+ /* Write this so that version > 4 files are read in correctly
+ if the Id came originally from a version <= 4 file.
+ */
+ root->add_child("LegacyId")->add_child_text ("1");
+ }
+}
+
+FFmpegAudioStream::FFmpegAudioStream (shared_ptr<const cxml::Node> node, int version)
+ : FFmpegStream (node, version)
+ , mapping (node->node_child ("Mapping"))
+{
frame_rate = node->number_child<int> ("FrameRate");
channels = node->number_child<int64_t> ("Channels");
first_audio = node->optional_number_child<double> ("FirstAudio");
void
FFmpegAudioStream::as_xml (xmlpp::Node* root) const
{
- root->add_child("Name")->add_child_text (name);
- root->add_child("Id")->add_child_text (lexical_cast<string> (id));
+ FFmpegStream::as_xml (root);
root->add_child("FrameRate")->add_child_text (lexical_cast<string> (frame_rate));
root->add_child("Channels")->add_child_text (lexical_cast<string> (channels));
if (first_audio) {
mapping.as_xml (root->add_child("Mapping"));
}
+int
+FFmpegStream::index (AVFormatContext const * fc) const
+{
+ if (_legacy_id) {
+ return id;
+ }
+
+ size_t i = 0;
+ while (i < fc->nb_streams) {
+ if (fc->streams[i]->id == id) {
+ return i;
+ }
+ ++i;
+ }
+
+ assert (false);
+}
+
+AVStream *
+FFmpegStream::stream (AVFormatContext const * fc) const
+{
+ if (_legacy_id) {
+ return fc->streams[id];
+ }
+
+ size_t i = 0;
+ while (i < fc->nb_streams) {
+ if (fc->streams[i]->id == id) {
+ return fc->streams[i];
+ }
+ ++i;
+ }
+
+ assert (false);
+ return 0;
+}
+
/** Construct a SubtitleStream from a value returned from to_string().
* @param t String returned from to_string().
* @param v State file version.
*/
-FFmpegSubtitleStream::FFmpegSubtitleStream (shared_ptr<const cxml::Node> node)
+FFmpegSubtitleStream::FFmpegSubtitleStream (shared_ptr<const cxml::Node> node, int version)
+ : FFmpegStream (node, version)
{
- name = node->string_child ("Name");
- id = node->number_child<int> ("Id");
+
}
void
FFmpegSubtitleStream::as_xml (xmlpp::Node* root) const
{
- root->add_child("Name")->add_child_text (name);
- root->add_child("Id")->add_child_text (lexical_cast<string> (id));
+ FFmpegStream::as_xml (root);
}
Time
#include "subtitle_content.h"
#include "audio_mapping.h"
+struct AVFormatContext;
+struct AVStream;
+
class Filter;
class ffmpeg_pts_offset_test;
-class FFmpegAudioStream
+class FFmpegStream
{
public:
- FFmpegAudioStream (std::string n, int i, int f, int c)
+ FFmpegStream (std::string n, int i)
: name (n)
, id (i)
+ , _legacy_id (false)
+ {}
+
+ FFmpegStream (boost::shared_ptr<const cxml::Node>, int);
+
+ void as_xml (xmlpp::Node *) const;
+
+ /** @param c An AVFormatContext.
+ * @return Stream index within the AVFormatContext.
+ */
+ int index (AVFormatContext const * c) const;
+ AVStream* stream (AVFormatContext const * c) const;
+
+ std::string name;
+ int id;
+
+private:
+ /** If this is true, id is in fact the index */
+ bool _legacy_id;
+};
+
+class FFmpegAudioStream : public FFmpegStream
+{
+public:
+ FFmpegAudioStream (std::string n, int i, int f, int c)
+ : FFmpegStream (n, i)
, frame_rate (f)
, channels (c)
, mapping (c)
mapping.make_default ();
}
- FFmpegAudioStream (boost::shared_ptr<const cxml::Node>);
+ FFmpegAudioStream (boost::shared_ptr<const cxml::Node>, int);
void as_xml (xmlpp::Node *) const;
-
- std::string name;
- int id;
+
int frame_rate;
int channels;
AudioMapping mapping;
/* Constructor for tests */
FFmpegAudioStream ()
- : mapping (1)
+ : FFmpegStream ("", 0)
+ , mapping (1)
{}
};
extern bool operator== (FFmpegAudioStream const & a, FFmpegAudioStream const & b);
+extern bool operator!= (FFmpegAudioStream const & a, FFmpegAudioStream const & b);
-class FFmpegSubtitleStream
+class FFmpegSubtitleStream : public FFmpegStream
{
public:
FFmpegSubtitleStream (std::string n, int i)
- : name (n)
- , id (i)
+ : FFmpegStream (n, i)
{}
- FFmpegSubtitleStream (boost::shared_ptr<const cxml::Node>);
+ FFmpegSubtitleStream (boost::shared_ptr<const cxml::Node>, int);
void as_xml (xmlpp::Node *) const;
-
- std::string name;
- int id;
};
extern bool operator== (FFmpegSubtitleStream const & a, FFmpegSubtitleStream const & b);
+extern bool operator!= (FFmpegSubtitleStream const & a, FFmpegSubtitleStream const & b);
class FFmpegContentProperty : public VideoContentProperty
{
{
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>, boost::shared_ptr<const cxml::Node>, int version);
+ 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 ());
boost::mutex::scoped_lock lm (_mutex);
return _first_video;
}
-
+
private:
friend class ffmpeg_pts_offset_test;
, VideoDecoder (f, c)
, AudioDecoder (f, c)
, SubtitleDecoder (f)
- , FFmpeg (c, false)
+ , FFmpeg (c)
, _subtitle_codec_context (0)
, _subtitle_codec (0)
, _decode_video (video)
shared_ptr<const Film> film = _film.lock ();
assert (film);
+
+ int const si = _packet.stream_index;
- if (_packet.stream_index == _video_stream && _decode_video) {
+ if (si == _video_stream && _decode_video) {
decode_video_packet ();
- } else if (_ffmpeg_content->audio_stream() && _packet.stream_index == _ffmpeg_content->audio_stream()->id && _decode_audio) {
+ } else if (_ffmpeg_content->audio_stream() && si == _ffmpeg_content->audio_stream()->index (_format_context) && _decode_audio) {
decode_audio_packet ();
- } else if (_ffmpeg_content->subtitle_stream() && _packet.stream_index == _ffmpeg_content->subtitle_stream()->id && film->with_subtitles ()) {
+ } else if (_ffmpeg_content->subtitle_stream() && si == _ffmpeg_content->subtitle_stream()->index (_format_context) && film->with_subtitles ()) {
decode_subtitle_packet ();
}
{
boost::mutex::scoped_lock lm (_mutex);
- if (!_ffmpeg_content->subtitle_stream() || _ffmpeg_content->subtitle_stream()->id >= int (_format_context->nb_streams)) {
+ if (!_ffmpeg_content->subtitle_stream() || _ffmpeg_content->subtitle_stream()->index (_format_context) >= int (_format_context->nb_streams)) {
return;
}
- _subtitle_codec_context = _format_context->streams[_ffmpeg_content->subtitle_stream()->id]->codec;
+ _subtitle_codec_context = _ffmpeg_content->subtitle_stream()->stream(_format_context)->codec;
_subtitle_codec = avcodec_find_decoder (_subtitle_codec_context->codec_id);
if (_subtitle_codec == 0) {
#include "ffmpeg_examiner.h"
#include "ffmpeg_content.h"
+#include "i18n.h"
+
using std::string;
using std::cout;
using std::max;
using boost::optional;
FFmpegExaminer::FFmpegExaminer (shared_ptr<const FFmpegContent> c)
- : FFmpeg (c, true)
+ : FFmpeg (c)
{
/* Find audio and subtitle streams */
_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), s->id, 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), s->id)));
}
}
}
} else {
for (size_t i = 0; i < _audio_streams.size(); ++i) {
- if (_packet.stream_index == _audio_streams[i]->id && !_audio_streams[i]->first_audio) {
+ if (_packet.stream_index == _audio_streams[i]->index (_format_context) && !_audio_streams[i]->first_audio) {
if (avcodec_decode_audio4 (context, _frame, &frame_finished, &_packet) >= 0 && frame_finished) {
- _audio_streams[i]->first_audio = frame_time (_audio_streams[i]->id);
+ _audio_streams[i]->first_audio = frame_time (_audio_streams[i]->index (_format_context));
}
}
}
return max (1, length);
}
+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
{
}
}
- if (!n.str().empty()) {
- n << "; ";
- }
-
- n << s->codec->channels << " channels";
-
return n.str ();
}
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;
--- /dev/null
+/*
+ Copyright (C) 2013 Carl Hetherington <cth@carlh.net>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include <cstdio>
+#include <sndfile.h>
+#include "file_group.h"
+#include "exceptions.h"
+
+using std::vector;
+using std::cout;
+
+FileGroup::FileGroup ()
+ : _current_path (0)
+ , _current_file (0)
+{
+
+}
+
+FileGroup::FileGroup (boost::filesystem::path p)
+ : _current_path (0)
+ , _current_file (0)
+{
+ _paths.push_back (p);
+ seek (0, SEEK_SET);
+}
+
+FileGroup::FileGroup (vector<boost::filesystem::path> const & p)
+ : _paths (p)
+ , _current_path (0)
+ , _current_file (0)
+{
+ ensure_open_path (0);
+ seek (0, SEEK_SET);
+}
+
+FileGroup::~FileGroup ()
+{
+ if (_current_file) {
+ fclose (_current_file);
+ }
+}
+
+void
+FileGroup::set_paths (vector<boost::filesystem::path> const & p)
+{
+ _paths = p;
+ ensure_open_path (0);
+ seek (0, SEEK_SET);
+}
+
+/** Ensure that the given path index in the content is the _current_file */
+void
+FileGroup::ensure_open_path (size_t p) const
+{
+ if (_current_file && _current_path == p) {
+ /* Already open */
+ return;
+ }
+
+ if (_current_file) {
+ fclose (_current_file);
+ }
+
+ _current_path = p;
+ _current_file = fopen (_paths[_current_path].string().c_str(), "rb");
+ if (_current_file == 0) {
+ throw OpenFileError (_paths[_current_path]);
+ }
+}
+
+int64_t
+FileGroup::seek (int64_t pos, int whence) const
+{
+ /* Convert pos to `full_pos', which is an offset from the start
+ of all the files.
+ */
+ int64_t full_pos = 0;
+ switch (whence) {
+ case SEEK_SET:
+ full_pos = pos;
+ break;
+ case SEEK_CUR:
+ for (size_t i = 0; i < _current_path; ++i) {
+ full_pos += boost::filesystem::file_size (_paths[i]);
+ }
+ full_pos += ftell (_current_file);
+ full_pos += pos;
+ break;
+ case SEEK_END:
+ full_pos = length() - pos;
+ break;
+ }
+
+ /* Seek to full_pos */
+ size_t i = 0;
+ int64_t sub_pos = full_pos;
+ while (i < _paths.size ()) {
+ boost::uintmax_t len = boost::filesystem::file_size (_paths[i]);
+ if (sub_pos < int64_t (len)) {
+ break;
+ }
+ sub_pos -= len;
+ ++i;
+ }
+
+ if (i == _paths.size ()) {
+ return -1;
+ }
+
+ ensure_open_path (i);
+ fseek (_current_file, sub_pos, SEEK_SET);
+ return full_pos;
+}
+
+/** Try to read some data from the current position into a buffer.
+ * @param buffer Buffer to write data into.
+ * @param amount Number of bytes to read.
+ * @return Number of bytes read, or -1 in the case of error.
+ */
+int
+FileGroup::read (uint8_t* buffer, int amount) const
+{
+ int read = 0;
+ while (1) {
+ int const this_time = fread (buffer + read, 1, amount - read, _current_file);
+ read += this_time;
+ if (read == amount) {
+ /* Done */
+ break;
+ }
+
+ /* See if there is another file to use */
+ if ((_current_path + 1) >= _paths.size()) {
+ break;
+ }
+ ensure_open_path (_current_path + 1);
+ }
+
+ return read;
+}
+
+int64_t
+FileGroup::length () const
+{
+ int64_t len = 0;
+ for (size_t i = 0; i < _paths.size(); ++i) {
+ len += boost::filesystem::file_size (_paths[i]);
+ }
+
+ return len;
+}
--- /dev/null
+/*
+ Copyright (C) 2013 Carl Hetherington <cth@carlh.net>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#ifndef DCPOMATIC_FILE_GROUP_H
+#define DCPOMATIC_FILE_GROUP_H
+
+#include <vector>
+#include <boost/filesystem.hpp>
+
+class FileGroup
+{
+public:
+ FileGroup ();
+ FileGroup (boost::filesystem::path);
+ FileGroup (std::vector<boost::filesystem::path> const &);
+ ~FileGroup ();
+
+ void set_paths (std::vector<boost::filesystem::path> const &);
+
+ int64_t seek (int64_t, int) const;
+ int read (uint8_t*, int) const;
+ int64_t length () const;
+
+private:
+ void ensure_open_path (size_t) const;
+
+ std::vector<boost::filesystem::path> _paths;
+ /** Index of path that we are currently reading from */
+ mutable size_t _current_path;
+ mutable FILE* _current_file;
+};
+
+#endif
using libdcp::Size;
using libdcp::Signer;
-int const Film::state_version = 4;
+int const Film::state_version = 5;
/** Construct a Film object in a given directory.
*
cxml::Document f ("Metadata");
f.read_file (file ("metadata.xml"));
+
+ int const version = f.number_child<int> ("Version");
_name = f.string_child ("Name");
_use_dci_name = f.bool_child ("UseDCIName");
_three_d = f.bool_child ("ThreeD");
_interop = f.bool_child ("Interop");
_key = libdcp::Key (f.string_child ("Key"));
- _playlist->set_from_xml (shared_from_this(), f.node_child ("Playlist"));
+ _playlist->set_from_xml (shared_from_this(), f.node_child ("Playlist"), version);
_dirty = false;
}
--- /dev/null
+/*
+ Copyright (C) 2013 Carl Hetherington <cth@carlh.net>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include <libcxml/cxml.h>
+#include "image_content.h"
+#include "image_examiner.h"
+#include "config.h"
+#include "compose.hpp"
+#include "film.h"
+#include "job.h"
+
+#include "i18n.h"
+
+using std::string;
+using std::cout;
+using std::stringstream;
+using boost::shared_ptr;
+
+ImageContent::ImageContent (shared_ptr<const Film> f, boost::filesystem::path p)
+ : Content (f)
+ , VideoContent (f)
+{
+ if (boost::filesystem::is_regular_file (p)) {
+ _paths.push_back (p);
+ } else {
+ for (boost::filesystem::directory_iterator i(p); i != boost::filesystem::directory_iterator(); ++i) {
+ if (boost::filesystem::is_regular_file (i->path()) && valid_image_file (i->path())) {
+ _paths.push_back (i->path ());
+ }
+ }
+
+ sort (_paths.begin(), _paths.end());
+ }
+}
+
+
+ImageContent::ImageContent (shared_ptr<const Film> f, shared_ptr<const cxml::Node> node, int)
+ : Content (f, node)
+ , VideoContent (f, node)
+{
+
+}
+
+string
+ImageContent::summary () const
+{
+ string s = path_summary () + " ";
+ /* Get the string() here so that the name does not have quotes around it */
+ if (still ()) {
+ s += _("[still]");
+ } else {
+ s += _("[moving images]");
+ }
+
+ return s;
+}
+
+string
+ImageContent::technical_summary () const
+{
+ string s = Content::technical_summary() + " - "
+ + VideoContent::technical_summary() + " - ";
+
+ if (still ()) {
+ s += _("still");
+ } else {
+ s += _("moving");
+ }
+
+ return s;
+}
+
+void
+ImageContent::as_xml (xmlpp::Node* node) const
+{
+ node->add_child("Type")->add_child_text ("Image");
+ Content::as_xml (node);
+ VideoContent::as_xml (node);
+}
+
+void
+ImageContent::examine (shared_ptr<Job> job)
+{
+ job->sub (_("Computing digest"));
+ Content::examine (job);
+
+ shared_ptr<const Film> film = _film.lock ();
+ assert (film);
+
+ shared_ptr<ImageExaminer> examiner (new ImageExaminer (film, shared_from_this(), job));
+
+ take_from_video_examiner (examiner);
+ set_video_length (examiner->video_length ());
+}
+
+void
+ImageContent::set_video_length (VideoContent::Frame len)
+{
+ {
+ boost::mutex::scoped_lock lm (_mutex);
+ _video_length = len;
+ }
+
+ signal_changed (ContentProperty::LENGTH);
+}
+
+Time
+ImageContent::full_length () const
+{
+ shared_ptr<const Film> film = _film.lock ();
+ assert (film);
+
+ FrameRateConversion frc (video_frame_rate(), film->video_frame_rate ());
+ return video_length() * frc.factor() * TIME_HZ / video_frame_rate();
+}
+
+string
+ImageContent::identifier () const
+{
+ stringstream s;
+ s << VideoContent::identifier ();
+ s << "_" << video_length();
+ return s.str ();
+}
+
+bool
+ImageContent::still () const
+{
+ return number_of_paths() == 1;
+}
--- /dev/null
+/*
+ Copyright (C) 2013 Carl Hetherington <cth@carlh.net>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#ifndef DCPOMATIC_IMAGE_CONTENT_H
+#define DCPOMATIC_IMAGE_CONTENT_H
+
+#include <boost/enable_shared_from_this.hpp>
+#include "video_content.h"
+
+namespace cxml {
+ class Node;
+}
+
+class ImageContent : public VideoContent
+{
+public:
+ ImageContent (boost::shared_ptr<const Film>, boost::filesystem::path);
+ ImageContent (boost::shared_ptr<const Film>, boost::shared_ptr<const cxml::Node>, int);
+
+ boost::shared_ptr<ImageContent> shared_from_this () {
+ return boost::dynamic_pointer_cast<ImageContent> (Content::shared_from_this ());
+ };
+
+ void examine (boost::shared_ptr<Job>);
+ std::string summary () const;
+ std::string technical_summary () const;
+ void as_xml (xmlpp::Node *) const;
+ Time full_length () const;
+
+ std::string identifier () const;
+
+ void set_video_length (VideoContent::Frame);
+ bool still () const;
+};
+
+#endif
--- /dev/null
+/*
+ Copyright (C) 2012-2013 Carl Hetherington <cth@carlh.net>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include <iostream>
+#include <boost/filesystem.hpp>
+#include <Magick++.h>
+#include "image_content.h"
+#include "image_decoder.h"
+#include "image.h"
+#include "film.h"
+#include "exceptions.h"
+
+#include "i18n.h"
+
+using std::cout;
+using boost::shared_ptr;
+using libdcp::Size;
+
+ImageDecoder::ImageDecoder (shared_ptr<const Film> f, shared_ptr<const ImageContent> c)
+ : Decoder (f)
+ , VideoDecoder (f, c)
+ , _image_content (c)
+{
+
+}
+
+void
+ImageDecoder::pass ()
+{
+ if (_video_position >= _image_content->video_length ()) {
+ return;
+ }
+
+ if (_image && _image_content->still ()) {
+ video (_image, true, _video_position);
+ return;
+ }
+
+ Magick::Image* magick_image = new Magick::Image (_image_content->path(_video_position).string ());
+ libdcp::Size size (magick_image->columns(), magick_image->rows());
+
+ _image.reset (new Image (PIX_FMT_RGB24, size, true));
+
+ using namespace MagickCore;
+
+ uint8_t* p = _image->data()[0];
+ for (int y = 0; y < size.height; ++y) {
+ uint8_t* q = p;
+ for (int x = 0; x < size.width; ++x) {
+ Magick::Color c = magick_image->pixelColor (x, y);
+ *q++ = c.redQuantum() * 255 / QuantumRange;
+ *q++ = c.greenQuantum() * 255 / QuantumRange;
+ *q++ = c.blueQuantum() * 255 / QuantumRange;
+ }
+ p += _image->stride()[0];
+ }
+
+ delete magick_image;
+
+ video (_image, false, _video_position);
+}
+
+void
+ImageDecoder::seek (VideoContent::Frame frame, bool)
+{
+ _video_position = frame;
+}
+
+bool
+ImageDecoder::done () const
+{
+ return _video_position >= _image_content->video_length ();
+}
--- /dev/null
+/*
+ Copyright (C) 2012-2013 Carl Hetherington <cth@carlh.net>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ 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"
+
+namespace Magick {
+ class Image;
+}
+
+class ImageContent;
+
+class ImageDecoder : public VideoDecoder
+{
+public:
+ ImageDecoder (boost::shared_ptr<const Film>, boost::shared_ptr<const ImageContent>);
+
+ boost::shared_ptr<const ImageContent> content () {
+ return _image_content;
+ }
+
+ /* Decoder */
+
+ void pass ();
+ void seek (VideoContent::Frame, bool);
+ bool done () const;
+
+private:
+ boost::shared_ptr<const ImageContent> _image_content;
+ boost::shared_ptr<Image> _image;
+};
+
--- /dev/null
+/*
+ Copyright (C) 2013 Carl Hetherington <cth@carlh.net>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include <iostream>
+#include <boost/lexical_cast.hpp>
+#include <Magick++.h>
+#include "image_content.h"
+#include "image_examiner.h"
+#include "film.h"
+#include "job.h"
+#include "exceptions.h"
+#include "config.h"
+
+#include "i18n.h"
+
+using std::cout;
+using std::list;
+using std::sort;
+using boost::shared_ptr;
+using boost::lexical_cast;
+using boost::bad_lexical_cast;
+
+ImageExaminer::ImageExaminer (shared_ptr<const Film> film, shared_ptr<const ImageContent> content, shared_ptr<Job> job)
+ : _film (film)
+ , _image_content (content)
+ , _video_length (0)
+{
+ list<unsigned int> frames;
+ size_t const N = content->number_of_paths ();
+
+ for (size_t i = 0; i < N; ++i) {
+ boost::filesystem::path const p = content->path (i);
+ try {
+ frames.push_back (lexical_cast<int> (p.stem().string()));
+ } catch (bad_lexical_cast &) {
+ /* We couldn't turn that filename into a number; never mind */
+ }
+
+ if (!_video_size) {
+ using namespace MagickCore;
+ Magick::Image* image = new Magick::Image (p.string());
+ _video_size = libdcp::Size (image->columns(), image->rows());
+ delete image;
+ }
+
+ job->set_progress (float (i) / N);
+ }
+
+ frames.sort ();
+
+ if (N > 1 && frames.front() != 0 && frames.front() != 1) {
+ throw StringError (String::compose (_("first frame in moving image directory is number %1"), frames.front ()));
+ }
+
+ if (N > 1 && frames.back() != frames.size() && frames.back() != (frames.size() - 1)) {
+ throw StringError (String::compose (_("there are %1 images in the directory but the last one is number %2"), frames.size(), frames.back ()));
+ }
+
+ if (content->still ()) {
+ _video_length = Config::instance()->default_still_length() * video_frame_rate();
+ } else {
+ _video_length = _image_content->number_of_paths ();
+ }
+}
+
+libdcp::Size
+ImageExaminer::video_size () const
+{
+ return _video_size.get ();
+}
+
+float
+ImageExaminer::video_frame_rate () const
+{
+ boost::shared_ptr<const Film> f = _film.lock ();
+ if (!f) {
+ return 24;
+ }
+
+ return f->video_frame_rate ();
+}
--- /dev/null
+/*
+ Copyright (C) 2013 Carl Hetherington <cth@carlh.net>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include "video_examiner.h"
+
+namespace Magick {
+ class Image;
+}
+
+class ImageContent;
+
+class ImageExaminer : public VideoExaminer
+{
+public:
+ ImageExaminer (boost::shared_ptr<const Film>, boost::shared_ptr<const ImageContent>, boost::shared_ptr<Job>);
+
+ float video_frame_rate () const;
+ libdcp::Size video_size () const;
+ VideoContent::Frame video_length () const {
+ return _video_length;
+ }
+
+private:
+ boost::weak_ptr<const Film> _film;
+ boost::shared_ptr<const ImageContent> _image_content;
+ boost::optional<libdcp::Size> _video_size;
+ VideoContent::Frame _video_length;
+};
+++ /dev/null
-/*
- Copyright (C) 2012 Carl Hetherington <cth@carlh.net>
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
-*/
-
-class MovingImageContent;
-
-class MovingImage
-{
-public:
- MovingImage (boost::shared_ptr<const MovingImageContent> c)
- : _moving_image_content (c)
- {}
-
- boost::shared_ptr<const MovingImageContent> content () const {
- return _moving_image_content;
- }
-
-protected:
- boost::shared_ptr<const MovingImageContent> _moving_image_content;
-};
+++ /dev/null
-/*
- Copyright (C) 2013 Carl Hetherington <cth@carlh.net>
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
-*/
-
-#include <libcxml/cxml.h>
-#include "moving_image_content.h"
-#include "moving_image_examiner.h"
-#include "config.h"
-#include "compose.hpp"
-#include "film.h"
-#include "job.h"
-
-#include "i18n.h"
-
-using std::string;
-using std::cout;
-using std::list;
-using std::stringstream;
-using std::vector;
-using boost::shared_ptr;
-
-MovingImageContent::MovingImageContent (shared_ptr<const Film> f, boost::filesystem::path p)
- : Content (f, p)
- , VideoContent (f, p)
-{
-
-}
-
-MovingImageContent::MovingImageContent (shared_ptr<const Film> f, shared_ptr<const cxml::Node> node)
- : Content (f, node)
- , VideoContent (f, node)
-{
- list<cxml::NodePtr> c = node->node_children ("File");
- for (list<cxml::NodePtr>::const_iterator i = c.begin(); i != c.end(); ++i) {
- _files.push_back ((*i)->content ());
- }
-}
-
-string
-MovingImageContent::summary () const
-{
- /* Get the string() here so that the name does not have quotes around it */
- return String::compose (_("%1 [moving images]"), path().filename().string());
-}
-
-string
-MovingImageContent::technical_summary () const
-{
- return Content::technical_summary() + " - "
- + VideoContent::technical_summary() + " - "
- + "moving";
-}
-
-void
-MovingImageContent::as_xml (xmlpp::Node* node) const
-{
- node->add_child("Type")->add_child_text ("MovingImage");
- Content::as_xml (node);
- VideoContent::as_xml (node);
-
- for (vector<boost::filesystem::path>::const_iterator i = _files.begin(); i != _files.end(); ++i) {
- node->add_child("File")->add_child_text (i->filename().string());
- }
-}
-
-void
-MovingImageContent::examine (shared_ptr<Job> job)
-{
- job->sub (_("Computing digest"));
- Content::examine (job);
-
- shared_ptr<const Film> film = _film.lock ();
- assert (film);
-
- job->sub (_("Examining content"));
- shared_ptr<MovingImageExaminer> examiner (new MovingImageExaminer (film, shared_from_this(), job));
-
- take_from_video_examiner (examiner);
-
- _video_length = examiner->files().size ();
- _files = examiner->files ();
-}
-
-Time
-MovingImageContent::full_length () const
-{
- shared_ptr<const Film> film = _film.lock ();
- assert (film);
-
- FrameRateConversion frc (video_frame_rate(), film->video_frame_rate ());
- return video_length() * frc.factor() * TIME_HZ / video_frame_rate();
-}
-
-string
-MovingImageContent::identifier () const
-{
- stringstream s;
- s << VideoContent::identifier ();
- s << "_" << video_length();
- return s.str ();
-}
+++ /dev/null
-/*
- Copyright (C) 2013 Carl Hetherington <cth@carlh.net>
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
-*/
-
-#ifndef DCPOMATIC_MOVING_IMAGE_CONTENT_H
-#define DCPOMATIC_MOVING_IMAGE_CONTENT_H
-
-#include <boost/enable_shared_from_this.hpp>
-#include "video_content.h"
-
-namespace cxml {
- class Node;
-}
-
-/** A directory of image files which are to be presented as a movie */
-class MovingImageContent : public VideoContent
-{
-public:
- MovingImageContent (boost::shared_ptr<const Film>, boost::filesystem::path);
- MovingImageContent (boost::shared_ptr<const Film>, boost::shared_ptr<const cxml::Node>);
-
- boost::shared_ptr<MovingImageContent> shared_from_this () {
- return boost::dynamic_pointer_cast<MovingImageContent> (Content::shared_from_this ());
- };
-
- void examine (boost::shared_ptr<Job>);
- std::string summary () const;
- std::string technical_summary () const;
- void as_xml (xmlpp::Node *) const;
- Time full_length () const;
-
- std::string identifier () const;
-
- std::vector<boost::filesystem::path> const & files () const {
- return _files;
- }
-
-private:
- std::vector<boost::filesystem::path> _files;
-};
-
-#endif
+++ /dev/null
-/*
- Copyright (C) 2012-2013 Carl Hetherington <cth@carlh.net>
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
-*/
-
-#include <iostream>
-#include <boost/filesystem.hpp>
-#include <Magick++.h>
-#include "moving_image_content.h"
-#include "moving_image_decoder.h"
-#include "image.h"
-#include "film.h"
-#include "exceptions.h"
-
-#include "i18n.h"
-
-using std::cout;
-using boost::shared_ptr;
-using libdcp::Size;
-
-MovingImageDecoder::MovingImageDecoder (shared_ptr<const Film> f, shared_ptr<const MovingImageContent> c)
- : Decoder (f)
- , VideoDecoder (f, c)
- , MovingImage (c)
-{
-
-}
-
-void
-MovingImageDecoder::pass ()
-{
- if (_video_position >= _moving_image_content->video_length ()) {
- return;
- }
-
- boost::filesystem::path path = _moving_image_content->path ();
- path /= _moving_image_content->files()[_video_position];
-
- Magick::Image* magick_image = new Magick::Image (path.string());
- libdcp::Size size (magick_image->columns(), magick_image->rows());
-
- shared_ptr<Image> image (new Image (PIX_FMT_RGB24, size, false));
-
- using namespace MagickCore;
-
- uint8_t* p = image->data()[0];
- for (int y = 0; y < size.height; ++y) {
- for (int x = 0; x < size.width; ++x) {
- Magick::Color c = magick_image->pixelColor (x, y);
- *p++ = c.redQuantum() * 255 / QuantumRange;
- *p++ = c.greenQuantum() * 255 / QuantumRange;
- *p++ = c.blueQuantum() * 255 / QuantumRange;
- }
- }
-
- delete magick_image;
-
- video (image, false, _video_position);
-}
-
-void
-MovingImageDecoder::seek (VideoContent::Frame frame, bool)
-{
- _video_position = frame;
-}
-
-bool
-MovingImageDecoder::done () const
-{
- return _video_position >= _moving_image_content->video_length ();
-}
+++ /dev/null
-/*
- Copyright (C) 2012-2013 Carl Hetherington <cth@carlh.net>
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- 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 "moving_image.h"
-
-namespace Magick {
- class Image;
-}
-
-class MovingImageContent;
-
-class MovingImageDecoder : public VideoDecoder, public MovingImage
-{
-public:
- MovingImageDecoder (boost::shared_ptr<const Film>, boost::shared_ptr<const MovingImageContent>);
-
- /* Decoder */
-
- void pass ();
- void seek (VideoContent::Frame, bool);
- bool done () const;
-};
-
+++ /dev/null
-/*
- Copyright (C) 2013 Carl Hetherington <cth@carlh.net>
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
-*/
-
-#include <iostream>
-#include <boost/lexical_cast.hpp>
-#include <Magick++.h>
-#include "moving_image_content.h"
-#include "moving_image_examiner.h"
-#include "film.h"
-#include "job.h"
-#include "exceptions.h"
-
-#include "i18n.h"
-
-using std::cout;
-using std::list;
-using std::sort;
-using boost::shared_ptr;
-using boost::lexical_cast;
-
-MovingImageExaminer::MovingImageExaminer (shared_ptr<const Film> film, shared_ptr<const MovingImageContent> content, shared_ptr<Job> job)
- : MovingImage (content)
- , _film (film)
- , _video_length (0)
-{
- list<unsigned int> frames;
- unsigned int files = 0;
-
- for (boost::filesystem::directory_iterator i(content->path()); i != boost::filesystem::directory_iterator(); ++i) {
- if (boost::filesystem::is_regular_file (i->path ())) {
- ++files;
- }
- }
-
- int j = 0;
- for (boost::filesystem::directory_iterator i(content->path()); i != boost::filesystem::directory_iterator(); ++i) {
- if (!boost::filesystem::is_regular_file (i->path ())) {
- continue;
- }
-
- if (valid_image_file (i->path ())) {
- int n = lexical_cast<int> (i->path().stem().string());
- frames.push_back (n);
- _files.push_back (i->path().filename ());
-
- if (!_video_size) {
- using namespace MagickCore;
- Magick::Image* image = new Magick::Image (i->path().string());
- _video_size = libdcp::Size (image->columns(), image->rows());
- delete image;
- }
- }
-
- job->set_progress (float (j) / files);
- ++j;
- }
-
- frames.sort ();
- sort (_files.begin(), _files.end ());
-
- if (frames.size() < 2) {
- throw StringError (String::compose (_("only %1 file(s) found in moving image directory"), frames.size ()));
- }
-
- if (frames.front() != 0 && frames.front() != 1) {
- throw StringError (String::compose (_("first frame in moving image directory is number %1"), frames.front ()));
- }
-
- if (frames.back() != frames.size() && frames.back() != (frames.size() - 1)) {
- throw StringError (String::compose (_("there are %1 images in the directory but the last one is number %2"), frames.size(), frames.back ()));
- }
-
- _video_length = frames.size ();
-}
-
-libdcp::Size
-MovingImageExaminer::video_size () const
-{
- return _video_size.get ();
-}
-
-int
-MovingImageExaminer::video_length () const
-{
- return _video_length;
-}
-
-float
-MovingImageExaminer::video_frame_rate () const
-{
- return 24;
-}
-
+++ /dev/null
-/*
- Copyright (C) 2013 Carl Hetherington <cth@carlh.net>
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
-*/
-
-#include "moving_image.h"
-#include "video_examiner.h"
-
-namespace Magick {
- class Image;
-}
-
-class MovingImageContent;
-
-class MovingImageExaminer : public MovingImage, public VideoExaminer
-{
-public:
- MovingImageExaminer (boost::shared_ptr<const Film>, boost::shared_ptr<const MovingImageContent>, boost::shared_ptr<Job>);
-
- float video_frame_rate () const;
- libdcp::Size video_size () const;
- VideoContent::Frame video_length () const;
-
- std::vector<boost::filesystem::path> const & files () const {
- return _files;
- }
-
-private:
- boost::weak_ptr<const Film> _film;
- boost::optional<libdcp::Size> _video_size;
- VideoContent::Frame _video_length;
- std::vector<boost::filesystem::path> _files;
-};
#include "film.h"
#include "ffmpeg_decoder.h"
#include "ffmpeg_content.h"
-#include "still_image_decoder.h"
-#include "still_image_content.h"
-#include "moving_image_decoder.h"
-#include "moving_image_content.h"
+#include "image_decoder.h"
+#include "image_content.h"
#include "sndfile_decoder.h"
#include "sndfile_content.h"
#include "subtitle_content.h"
piece->decoder = fd;
}
- shared_ptr<const StillImageContent> ic = dynamic_pointer_cast<const StillImageContent> (*i);
+ shared_ptr<const ImageContent> ic = dynamic_pointer_cast<const ImageContent> (*i);
if (ic) {
- shared_ptr<StillImageDecoder> id;
+ bool reusing = false;
- /* See if we can re-use an old StillImageDecoder */
+ /* See if we can re-use an old ImageDecoder */
for (list<shared_ptr<Piece> >::const_iterator j = old_pieces.begin(); j != old_pieces.end(); ++j) {
- shared_ptr<StillImageDecoder> imd = dynamic_pointer_cast<StillImageDecoder> ((*j)->decoder);
+ shared_ptr<ImageDecoder> imd = dynamic_pointer_cast<ImageDecoder> ((*j)->decoder);
if (imd && imd->content() == ic) {
- id = imd;
+ piece = *j;
+ reusing = true;
}
}
- if (!id) {
- id.reset (new StillImageDecoder (_film, ic));
+ if (!reusing) {
+ shared_ptr<ImageDecoder> id (new ImageDecoder (_film, ic));
id->Video.connect (bind (&Player::process_video, this, weak_ptr<Piece> (piece), _1, _2, _3, _4, 0));
+ piece->decoder = id;
}
-
- piece->decoder = id;
- }
-
- shared_ptr<const MovingImageContent> mc = dynamic_pointer_cast<const MovingImageContent> (*i);
- if (mc) {
- shared_ptr<MovingImageDecoder> md;
-
- if (!md) {
- md.reset (new MovingImageDecoder (_film, mc));
- md->Video.connect (bind (&Player::process_video, this, weak_ptr<Piece> (piece), _1, _2, _3, _4, 0));
- }
-
- piece->decoder = md;
}
shared_ptr<const SndfileContent> sc = dynamic_pointer_cast<const SndfileContent> (*i);
#include "video_content.h"
#include "ffmpeg_decoder.h"
#include "ffmpeg_content.h"
-#include "still_image_decoder.h"
-#include "still_image_content.h"
+#include "image_decoder.h"
#include "content_factory.h"
#include "job.h"
#include "config.h"
/** @param node <Playlist> node */
void
-Playlist::set_from_xml (shared_ptr<const Film> film, shared_ptr<const cxml::Node> node)
+Playlist::set_from_xml (shared_ptr<const Film> film, shared_ptr<const cxml::Node> node, int version)
{
list<cxml::NodePtr> c = node->node_children ("Content");
for (list<cxml::NodePtr>::iterator i = c.begin(); i != c.end(); ++i) {
- _content.push_back (content_factory (film, *i));
+ _content.push_back (content_factory (film, *i, version));
}
sort (_content.begin(), _content.end(), ContentSorter ());
~Playlist ();
void as_xml (xmlpp::Node *);
- void set_from_xml (boost::shared_ptr<const Film>, boost::shared_ptr<const cxml::Node>);
+ void set_from_xml (boost::shared_ptr<const Film>, boost::shared_ptr<const cxml::Node>, int);
void add (boost::shared_ptr<Content>);
void remove (boost::shared_ptr<Content>);
}
-SndfileContent::SndfileContent (shared_ptr<const Film> f, shared_ptr<const cxml::Node> node)
+SndfileContent::SndfileContent (shared_ptr<const Film> f, shared_ptr<const cxml::Node> node, int)
: Content (f, node)
, AudioContent (f, node)
, _audio_mapping (node->node_child ("AudioMapping"))
SndfileContent::summary () const
{
/* Get the string() here so that the name does not have quotes around it */
- return String::compose (_("%1 [audio]"), path().filename().string());
+ return String::compose (_("%1 [audio]"), path_summary ());
}
string
{
public:
SndfileContent (boost::shared_ptr<const Film>, boost::filesystem::path);
- SndfileContent (boost::shared_ptr<const Film>, boost::shared_ptr<const cxml::Node>);
+ SndfileContent (boost::shared_ptr<const Film>, boost::shared_ptr<const cxml::Node>, int);
boost::shared_ptr<SndfileContent> shared_from_this () {
return boost::dynamic_pointer_cast<SndfileContent> (Content::shared_from_this ());
, _deinterleave_buffer (0)
{
_info.format = 0;
- _sndfile = sf_open (_sndfile_content->path().string().c_str(), SFM_READ, &_info);
+ _sndfile = sf_open (_sndfile_content->path(0).string().c_str(), SFM_READ, &_info);
if (!_sndfile) {
throw DecodeError (_("could not open audio file for reading"));
}
+++ /dev/null
-/*
- Copyright (C) 2012 Carl Hetherington <cth@carlh.net>
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
-*/
-
-#ifndef DCPOMATIC_STILL_IMAGE_H
-#define DCPOMATIC_STILL_IMAGE_H
-
-class StillImageContent;
-
-class StillImage
-{
-public:
- StillImage (boost::shared_ptr<const StillImageContent> c)
- : _still_image_content (c)
- {}
-
- boost::shared_ptr<const StillImageContent> content () const {
- return _still_image_content;
- }
-
-protected:
- boost::shared_ptr<const StillImageContent> _still_image_content;
-};
-
-#endif
+++ /dev/null
-/*
- Copyright (C) 2013 Carl Hetherington <cth@carlh.net>
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
-*/
-
-#include <libcxml/cxml.h>
-#include "still_image_content.h"
-#include "still_image_examiner.h"
-#include "config.h"
-#include "compose.hpp"
-#include "film.h"
-
-#include "i18n.h"
-
-using std::string;
-using std::cout;
-using std::stringstream;
-using boost::shared_ptr;
-
-StillImageContent::StillImageContent (shared_ptr<const Film> f, boost::filesystem::path p)
- : Content (f, p)
- , VideoContent (f, p)
-{
-
-}
-
-StillImageContent::StillImageContent (shared_ptr<const Film> f, shared_ptr<const cxml::Node> node)
- : Content (f, node)
- , VideoContent (f, node)
-{
-
-}
-
-string
-StillImageContent::summary () const
-{
- /* Get the string() here so that the name does not have quotes around it */
- return String::compose (_("%1 [still]"), path().filename().string());
-}
-
-string
-StillImageContent::technical_summary () const
-{
- return Content::technical_summary() + " - "
- + VideoContent::technical_summary() + " - "
- + "still";
-}
-
-void
-StillImageContent::as_xml (xmlpp::Node* node) const
-{
- node->add_child("Type")->add_child_text ("StillImage");
- Content::as_xml (node);
- VideoContent::as_xml (node);
-}
-
-void
-StillImageContent::examine (shared_ptr<Job> job)
-{
- Content::examine (job);
-
- shared_ptr<const Film> film = _film.lock ();
- assert (film);
-
- shared_ptr<StillImageExaminer> examiner (new StillImageExaminer (film, shared_from_this()));
-
- take_from_video_examiner (examiner);
- set_video_length (Config::instance()->default_still_length() * video_frame_rate());
-}
-
-void
-StillImageContent::set_video_length (VideoContent::Frame len)
-{
- {
- boost::mutex::scoped_lock lm (_mutex);
- _video_length = len;
- }
-
- signal_changed (ContentProperty::LENGTH);
-}
-
-Time
-StillImageContent::full_length () const
-{
- shared_ptr<const Film> film = _film.lock ();
- assert (film);
-
- FrameRateConversion frc (video_frame_rate(), film->video_frame_rate ());
- return video_length() * frc.factor() * TIME_HZ / video_frame_rate();
-}
-
-string
-StillImageContent::identifier () const
-{
- stringstream s;
- s << VideoContent::identifier ();
- s << "_" << video_length();
- return s.str ();
-}
+++ /dev/null
-/*
- Copyright (C) 2013 Carl Hetherington <cth@carlh.net>
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
-*/
-
-#ifndef DCPOMATIC_STILL_IMAGE_CONTENT_H
-#define DCPOMATIC_STILL_IMAGE_CONTENT_H
-
-#include <boost/enable_shared_from_this.hpp>
-#include "video_content.h"
-
-namespace cxml {
- class Node;
-}
-
-/** A single image which is to be held on screen for some time (i.e. a slide) */
-class StillImageContent : public VideoContent
-{
-public:
- StillImageContent (boost::shared_ptr<const Film>, boost::filesystem::path);
- StillImageContent (boost::shared_ptr<const Film>, boost::shared_ptr<const cxml::Node>);
-
- boost::shared_ptr<StillImageContent> shared_from_this () {
- return boost::dynamic_pointer_cast<StillImageContent> (Content::shared_from_this ());
- };
-
- void examine (boost::shared_ptr<Job>);
- std::string summary () const;
- std::string technical_summary () const;
- void as_xml (xmlpp::Node *) const;
- Time full_length () const;
-
- std::string identifier () const;
-
- void set_video_length (VideoContent::Frame);
-};
-
-#endif
+++ /dev/null
-/*
- Copyright (C) 2012-2013 Carl Hetherington <cth@carlh.net>
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
-*/
-
-#include <iostream>
-#include <boost/filesystem.hpp>
-#include <Magick++.h>
-#include "still_image_content.h"
-#include "still_image_decoder.h"
-#include "image.h"
-#include "film.h"
-#include "exceptions.h"
-
-#include "i18n.h"
-
-using std::cout;
-using boost::shared_ptr;
-using libdcp::Size;
-
-StillImageDecoder::StillImageDecoder (shared_ptr<const Film> f, shared_ptr<const StillImageContent> c)
- : Decoder (f)
- , VideoDecoder (f, c)
- , StillImage (c)
-{
-
-}
-
-void
-StillImageDecoder::pass ()
-{
- if (_video_position >= _still_image_content->video_length ()) {
- return;
- }
-
- if (_image) {
- video (_image, true, _video_position);
- return;
- }
-
- Magick::Image* magick_image = new Magick::Image (_still_image_content->path().string ());
- _video_size = libdcp::Size (magick_image->columns(), magick_image->rows());
-
- _image.reset (new Image (PIX_FMT_RGB24, _video_size.get(), true));
-
- using namespace MagickCore;
-
- uint8_t* p = _image->data()[0];
- for (int y = 0; y < _video_size->height; ++y) {
- uint8_t* q = p;
- for (int x = 0; x < _video_size->width; ++x) {
- Magick::Color c = magick_image->pixelColor (x, y);
- *q++ = c.redQuantum() * 255 / QuantumRange;
- *q++ = c.greenQuantum() * 255 / QuantumRange;
- *q++ = c.blueQuantum() * 255 / QuantumRange;
- }
- p += _image->stride()[0];
- }
-
- delete magick_image;
-
- video (_image, false, _video_position);
-}
-
-void
-StillImageDecoder::seek (VideoContent::Frame frame, bool)
-{
- _video_position = frame;
-}
-
-bool
-StillImageDecoder::done () const
-{
- return _video_position >= _still_image_content->video_length ();
-}
+++ /dev/null
-/*
- Copyright (C) 2012-2013 Carl Hetherington <cth@carlh.net>
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- 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 "still_image.h"
-
-namespace Magick {
- class Image;
-}
-
-class StillImageContent;
-
-class StillImageDecoder : public VideoDecoder, public StillImage
-{
-public:
- StillImageDecoder (boost::shared_ptr<const Film>, boost::shared_ptr<const StillImageContent>);
-
- /* Decoder */
-
- void pass ();
- void seek (VideoContent::Frame, bool);
- bool done () const;
-
-private:
- boost::shared_ptr<Image> _image;
- mutable boost::optional<libdcp::Size> _video_size;
-};
-
+++ /dev/null
-/*
- Copyright (C) 2013 Carl Hetherington <cth@carlh.net>
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
-*/
-
-#include <iostream>
-#include <Magick++.h>
-#include "still_image_content.h"
-#include "still_image_examiner.h"
-#include "film.h"
-
-#include "i18n.h"
-
-using std::cout;
-using std::string;
-using boost::shared_ptr;
-
-StillImageExaminer::StillImageExaminer (shared_ptr<const Film> f, shared_ptr<const StillImageContent> c)
- : StillImage (c)
- , _film (f)
-{
- using namespace MagickCore;
- Magick::Image* image = new Magick::Image (_still_image_content->path().string ());
- _video_size = libdcp::Size (image->columns(), image->rows());
- delete image;
-}
-
-libdcp::Size
-StillImageExaminer::video_size () const
-{
- return _video_size;
-}
-
-int
-StillImageExaminer::video_length () const
-{
- return _still_image_content->video_length ();
-}
-
-float
-StillImageExaminer::video_frame_rate () const
-{
- boost::shared_ptr<const Film> f = _film.lock ();
- if (!f) {
- return 24;
- }
-
- return f->video_frame_rate ();
-}
-
+++ /dev/null
-/*
- Copyright (C) 2013 Carl Hetherington <cth@carlh.net>
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
-*/
-
-#include "still_image.h"
-#include "video_examiner.h"
-
-namespace Magick {
- class Image;
-}
-
-class StillImageContent;
-
-class StillImageExaminer : public StillImage, public VideoExaminer
-{
-public:
- StillImageExaminer (boost::shared_ptr<const Film>, boost::shared_ptr<const StillImageContent>);
-
- float video_frame_rate () const;
- libdcp::Size video_size () const;
- VideoContent::Frame video_length () const;
-
-private:
- boost::weak_ptr<const Film> _film;
- libdcp::Size _video_size;
-};
#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;
_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
{
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;
using std::numeric_limits;
using std::pair;
using std::cout;
+using std::streampos;
using boost::shared_ptr;
using boost::thread;
using boost::lexical_cast;
return s.str ();
}
-/** @param file File name.
- * @return MD5 digest of file's contents.
- */
-string
-md5_digest (boost::filesystem::path file)
-{
- FILE* f = fopen_boost (file, "rb");
- if (!f) {
- throw OpenFileError (file.string());
- }
-
- boost::uintmax_t bytes = boost::filesystem::file_size (file);
-
- boost::uintmax_t 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);
- fread (buffer, 1, t, f);
- MD5_Update (&md5_context, buffer, t);
- bytes -= t;
- }
-
- unsigned char digest[MD5_DIGEST_LENGTH];
- MD5_Final (digest, &md5_context);
- fclose (f);
-
- 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)
{
boost::uintmax_t const buffer_size = 64 * 1024;
char buffer[buffer_size];
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;
- }
+ vector<int64_t> sizes;
+ for (size_t i = 0; i < files.size(); ++i) {
+ sizes.push_back (boost::filesystem::file_size (files[i]));
}
- int j = 0;
- for (boost::filesystem::directory_iterator i(directory); i != boost::filesystem::directory_iterator(); ++i) {
- FILE* f = fopen_boost (i->path(), "rb");
+ for (size_t i = 0; i < files.size(); ++i) {
+ FILE* f = fopen_boost (files[i], "rb");
if (!f) {
- throw OpenFileError (i->path().string());
+ throw OpenFileError (files[i].string());
}
- boost::uintmax_t bytes = boost::filesystem::file_size (i->path ());
+ boost::uintmax_t const bytes = boost::filesystem::file_size (files[i]);
+ boost::uintmax_t remaining = bytes;
- while (bytes > 0) {
- int const t = min (bytes, buffer_size);
+ while (remaining > 0) {
+ int const t = min (remaining, buffer_size);
fread (buffer, 1, t, f);
MD5_Update (&md5_context, buffer, t);
- bytes -= t;
- }
+ remaining -= t;
- if (job) {
- job->set_progress (float (j) / files);
- ++j;
+ if (job) {
+ job->set_progress ((float (i) + 1 - float(remaining) / bytes) / files.size ());
+ }
}
fclose (f);
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);
#include "colour_conversion.h"
#include "util.h"
#include "film.h"
+#include "exceptions.h"
#include "i18n.h"
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)
+ , _video_length (0)
+ , _video_frame_rate (0)
+ , _video_frame_type (VIDEO_FRAME_TYPE_2D)
+ , _ratio (Ratio::from_id ("185"))
+{
+ setup_default_colour_conversion ();
+}
VideoContent::VideoContent (shared_ptr<const Film> f, Time s, VideoContent::Frame len)
: Content (f, s)
_colour_conversion = ColourConversion (node->node_child ("ColourConversion"));
}
+VideoContent::VideoContent (shared_ptr<const Film> f, vector<shared_ptr<Content> > c)
+ : Content (f, c)
+ , _video_length (0)
+{
+ 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_length += vc->video_length ();
+ }
+
+ _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
{
public:
typedef int Frame;
+ VideoContent (boost::shared_ptr<const Film>);
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;
encoder.cc
examine_content_job.cc
exceptions.cc
+ file_group.cc
filter_graph.cc
ffmpeg.cc
ffmpeg_content.cc
film.cc
filter.cc
image.cc
+ image_content.cc
+ image_decoder.cc
+ image_examiner.cc
job.cc
job_manager.cc
kdm.cc
log.cc
- moving_image_content.cc
- moving_image_decoder.cc
- moving_image_examiner.cc
player.cc
playlist.cc
ratio.cc
sndfile_content.cc
sndfile_decoder.cc
sound_processor.cc
- still_image_content.cc
- still_image_decoder.cc
- still_image_examiner.cc
subtitle_content.cc
subtitle_decoder.cc
timer.cc
Frame (wxString const & title)
: wxFrame (NULL, -1, title)
, _servers_list_dialog (0)
+ , _hints_dialog (0)
{
#ifdef DCPOMATIC_WINDOWS_CONSOLE
AllocConsole();
_content_changed_connection = _content->Changed.connect (bind (&AudioDialog::content_changed, this, _2));
- SetTitle (wxString::Format (_("DCP-o-matic audio - %s"), std_to_wx(_content->path().filename().string()).data()));
+ SetTitle (wxString::Format (_("DCP-o-matic audio - %s"), std_to_wx(_content->path_summary()).data()));
}
void
#include <wx/dirdlg.h>
#include "lib/playlist.h"
#include "lib/film.h"
-#include "lib/moving_image_content.h"
+#include "lib/image_content.h"
#include "lib/content_factory.h"
#include "lib/examine_content_job.h"
#include "lib/job_manager.h"
+#include "lib/exceptions.h"
#include "content_menu.h"
#include "repeat_dialog.h"
#include "wx_util.h"
using std::cout;
+using std::vector;
using boost::shared_ptr;
using boost::weak_ptr;
using boost::dynamic_pointer_cast;
enum {
ID_repeat = 1,
+ ID_join,
ID_find_missing,
ID_remove
};
, _parent (p)
{
_repeat = _menu->Append (ID_repeat, _("Repeat..."));
+ _join = _menu->Append (ID_join, _("Join"));
_find_missing = _menu->Append (ID_find_missing, _("Find missing..."));
_menu->AppendSeparator ();
_remove = _menu->Append (ID_remove, _("Remove"));
_parent->Bind (wxEVT_COMMAND_MENU_SELECTED, boost::bind (&ContentMenu::repeat, this), ID_repeat);
+ _parent->Bind (wxEVT_COMMAND_MENU_SELECTED, boost::bind (&ContentMenu::join, this), ID_join);
_parent->Bind (wxEVT_COMMAND_MENU_SELECTED, boost::bind (&ContentMenu::find_missing, this), ID_find_missing);
_parent->Bind (wxEVT_COMMAND_MENU_SELECTED, boost::bind (&ContentMenu::remove, this), ID_remove);
}
{
_content = c;
_repeat->Enable (!_content.empty ());
+
+ int n = 0;
+ for (ContentList::const_iterator i = _content.begin(); i != _content.end(); ++i) {
+ if (dynamic_pointer_cast<FFmpegContent> (*i)) {
+ ++n;
+ }
+ }
+
+ _join->Enable (n > 1);
+
_find_missing->Enable (_content.size() == 1 && !_content.front()->path_valid ());
_remove->Enable (!_content.empty ());
_parent->PopupMenu (_menu, p);
_content.clear ();
}
+void
+ContentMenu::join ()
+{
+ vector<shared_ptr<Content> > fc;
+ for (ContentList::const_iterator i = _content.begin(); i != _content.end(); ++i) {
+ shared_ptr<FFmpegContent> f = dynamic_pointer_cast<FFmpegContent> (*i);
+ if (f) {
+ fc.push_back (f);
+ }
+ }
+
+ assert (fc.size() > 1);
+
+ shared_ptr<Film> film = _film.lock ();
+ if (!film) {
+ return;
+ }
+
+ try {
+ shared_ptr<FFmpegContent> joined (new FFmpegContent (film, fc));
+ for (ContentList::const_iterator i = _content.begin(); i != _content.end(); ++i) {
+ film->remove_content (*i);
+ }
+ film->add_content (joined);
+ } catch (JoinError& e) {
+ error_dialog (_parent, std_to_wx (e.what ()));
+ }
+}
+
void
ContentMenu::remove ()
{
shared_ptr<Content> content;
/* XXX: a bit nasty */
- if (dynamic_pointer_cast<MovingImageContent> (_content.front ())) {
+ shared_ptr<ImageContent> ic = dynamic_pointer_cast<ImageContent> (_content.front ());
+ if (ic && !ic->still ()) {
wxDirDialog* d = new wxDirDialog (0, _("Choose a folder"), wxT (""), wxDD_DIR_MUST_EXIST);
int const r = d->ShowModal ();
if (r == wxID_OK) {
- content.reset (new MovingImageContent (film, boost::filesystem::path (wx_to_std (d->GetPath ()))));
+ content.reset (new ImageContent (film, boost::filesystem::path (wx_to_std (d->GetPath ()))));
}
d->Destroy ();
} else {
return;
}
- old_content->set_path (new_content->path ());
+ old_content->set_path (new_content->path (0));
}
private:
void repeat ();
+ void join ();
void find_missing ();
void remove ();
void maybe_found_missing (boost::weak_ptr<Job>, boost::weak_ptr<Content>, boost::weak_ptr<Content>);
wxWindow* _parent;
ContentList _content;
wxMenuItem* _repeat;
+ wxMenuItem* _join;
wxMenuItem* _find_missing;
wxMenuItem* _remove;
};
#include "lib/filter.h"
#include "lib/ratio.h"
#include "lib/config.h"
-#include "lib/still_image_content.h"
-#include "lib/moving_image_content.h"
+#include "lib/image_content.h"
#include "lib/ffmpeg_content.h"
#include "lib/sndfile_content.h"
#include "lib/dcp_content_type.h"
}
_film->examine_and_add_content (
- shared_ptr<MovingImageContent> (
- new MovingImageContent (_film, boost::filesystem::path (wx_to_std (d->GetPath ())))
+ shared_ptr<ImageContent> (
+ new ImageContent (_film, boost::filesystem::path (wx_to_std (d->GetPath ())))
)
);
}
break;
}
- sel.push_back (_film->content()[s]);
+ if (s < int (_film->content().size ())) {
+ sel.push_back (_film->content()[s]);
+ }
}
return sel;
#include "lib/filter.h"
#include "lib/player.h"
#include "lib/video_content.h"
-#include "lib/ffmpeg_content.h"
-#include "lib/still_image_content.h"
#include "lib/video_decoder.h"
#include "film_viewer.h"
#include "wx_util.h"
ContentList content = film->content ();
int vob = 0;
for (ContentList::const_iterator i = content.begin(); i != content.end(); ++i) {
- if (boost::algorithm::starts_with ((*i)->path().filename().string(), "VTS_")) {
+ if (boost::algorithm::starts_with ((*i)->path(0).filename().string(), "VTS_")) {
++vob;
}
}
if (vob > 1) {
hint = true;
- _text->WriteText (wxString::Format (_("You have %d files that look like they are VOB files from DVD. You should coalesce them to ensure smooth joins between the files."), vob));
+ _text->WriteText (wxString::Format (_("You have %d files that look like they are VOB files from DVD. You should join them to ensure smooth joins between the files."), vob));
_text->Newline ();
}
gc->StrokePath (path);
gc->FillPath (path);
- wxString name = wxString::Format (wxT ("%s [%s]"), std_to_wx (cont->path().filename().string()).data(), type().data());
+ wxString name = wxString::Format (wxT ("%s [%s]"), std_to_wx (cont->path_summary()).data(), type().data());
wxDouble name_width;
wxDouble name_height;
wxDouble name_descent;
*/
#include "lib/content.h"
-#include "lib/still_image_content.h"
+#include "lib/image_content.h"
#include "timing_panel.h"
#include "wx_util.h"
#include "timecode.h"
}
}
- _length->set_editable (dynamic_pointer_cast<StillImageContent> (content));
+ shared_ptr<ImageContent> ic = dynamic_pointer_cast<ImageContent> (content);
+ _length->set_editable (ic && ic->still ());
}
void
{
ContentList c = _editor->selected_content ();
if (c.size() == 1) {
- shared_ptr<StillImageContent> ic = dynamic_pointer_cast<StillImageContent> (c.front ());
- if (ic) {
+ shared_ptr<ImageContent> ic = dynamic_pointer_cast<ImageContent> (c.front ());
+ if (ic && ic->still ()) {
ic->set_video_length (rint (_length->get (_editor->film()->video_frame_rate()) * ic->video_frame_rate() / TIME_HZ));
}
}
*/
#include <boost/test/unit_test.hpp>
-#include "lib/still_image_content.h"
+#include "lib/image_content.h"
#include "lib/dcp_content_type.h"
#include "lib/film.h"
#include "lib/ratio.h"
film->set_name ("black_fill_test");
film->set_container (Ratio::from_id ("185"));
film->set_sequence_video (false);
- shared_ptr<StillImageContent> contentA (new StillImageContent (film, "test/data/simple_testcard_640x480.png"));
+ shared_ptr<ImageContent> contentA (new ImageContent (film, "test/data/simple_testcard_640x480.png"));
contentA->set_ratio (Ratio::from_id ("185"));
- shared_ptr<StillImageContent> contentB (new StillImageContent (film, "test/data/simple_testcard_640x480.png"));
+ shared_ptr<ImageContent> contentB (new ImageContent (film, "test/data/simple_testcard_640x480.png"));
contentB->set_ratio (Ratio::from_id ("185"));
film->examine_and_add_content (contentA);
--- /dev/null
+/*
+ Copyright (C) 2013 Carl Hetherington <cth@carlh.net>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include <stdint.h>
+#include <cstdio>
+#include <boost/test/unit_test.hpp>
+#include <boost/filesystem.hpp>
+#include "lib/file_group.h"
+
+using std::vector;
+
+BOOST_AUTO_TEST_CASE (file_group_test)
+{
+ /* Random data; must be big enough for all the files */
+ uint8_t data[65536];
+ for (int i = 0; i < 65536; ++i) {
+ data[i] = rand() & 0xff;
+ }
+
+ int const num_files = 4;
+
+ int length[] = {
+ 99,
+ 18941,
+ 33110,
+ 42
+ };
+
+ int total_length = 0;
+ for (int i = 0; i < num_files; ++i) {
+ total_length += length[i];
+ }
+
+ vector<boost::filesystem::path> name;
+ boost::filesystem::create_directories ("build/test/file_group_test");
+ name.push_back ("build/test/file_group_test/A");
+ name.push_back ("build/test/file_group_test/B");
+ name.push_back ("build/test/file_group_test/C");
+ name.push_back ("build/test/file_group_test/D");
+
+ int base = 0;
+ for (int i = 0; i < num_files; ++i) {
+ FILE* f = fopen (name[i].string().c_str(), "wb");
+ fwrite (data + base, 1, length[i], f);
+ fclose (f);
+ base += length[i];
+ }
+
+ FileGroup fg (name);
+ uint8_t test[65536];
+
+ int pos = 0;
+
+ /* Basic read from 0 */
+ BOOST_CHECK_EQUAL (fg.read (test, 64), 64);
+ BOOST_CHECK_EQUAL (memcmp (data, test, 64), 0);
+ pos += 64;
+
+ /* Another read following the previous */
+ BOOST_CHECK_EQUAL (fg.read (test, 4), 4);
+ BOOST_CHECK_EQUAL (memcmp (data + pos, test, 4), 0);
+ pos += 4;
+
+ /* Read overlapping A and B */
+ BOOST_CHECK_EQUAL (fg.read (test, 128), 128);
+ BOOST_CHECK_EQUAL (memcmp (data + pos, test, 128), 0);
+ pos += 128;
+
+ /* Read overlapping B/C/D and over-reading */
+ BOOST_CHECK_EQUAL (fg.read (test, total_length * 3), total_length - pos);
+ BOOST_CHECK_EQUAL (memcmp (data + pos, test, total_length - pos), 0);
+
+ /* Bad seek */
+ BOOST_CHECK_EQUAL (fg.seek (total_length * 2, SEEK_SET), -1);
+
+ /* SEEK_SET */
+ BOOST_CHECK_EQUAL (fg.seek (999, SEEK_SET), 999);
+ BOOST_CHECK_EQUAL (fg.read (test, 64), 64);
+ BOOST_CHECK_EQUAL (memcmp (data + 999, test, 64), 0);
+
+ /* SEEK_CUR */
+ BOOST_CHECK_EQUAL (fg.seek (42, SEEK_CUR), 999 + 64 + 42);
+ BOOST_CHECK_EQUAL (fg.read (test, 64), 64);
+ BOOST_CHECK_EQUAL (memcmp (data + 999 + 64 + 42, test, 64), 0);
+
+ /* SEEK_END */
+ BOOST_CHECK_EQUAL (fg.seek (1077, SEEK_END), total_length - 1077);
+ BOOST_CHECK_EQUAL (fg.read (test, 256), 256);
+ BOOST_CHECK_EQUAL (memcmp (data + total_length - 1077, test, 256), 0);
+}
*/
#include <boost/test/unit_test.hpp>
-#include "lib/still_image_content.h"
+#include "lib/image_content.h"
#include "lib/ratio.h"
#include "lib/film.h"
#include "lib/dcp_content_type.h"
shared_ptr<Film> film = new_test_film ("scaling_test");
film->set_dcp_content_type (DCPContentType::from_dci_name ("FTR"));
film->set_name ("scaling_test");
- shared_ptr<StillImageContent> imc (new StillImageContent (film, "test/data/simple_testcard_640x480.png"));
+ shared_ptr<ImageContent> imc (new ImageContent (film, "test/data/simple_testcard_640x480.png"));
film->examine_and_add_content (imc);
#include <libxml++/libxml++.h>
#include <libcxml/cxml.h>
#include "lib/ffmpeg_content.h"
+#include "lib/film.h"
using std::pair;
using std::list;
map->add_child("DCP")->add_child_text ("2");
}
- FFmpegAudioStream a (shared_ptr<cxml::Node> (new cxml::Node (root)));
+ FFmpegAudioStream a (shared_ptr<cxml::Node> (new cxml::Node (root)), Film::state_version);
BOOST_CHECK_EQUAL (a.id, 4);
BOOST_CHECK_EQUAL (a.frame_rate, 44100);
using std::string;
using std::vector;
+using boost::shared_ptr;
BOOST_AUTO_TEST_CASE (util_test)
{
BOOST_AUTO_TEST_CASE (md5_digest_test)
{
- string const t = md5_digest ("test/data/md5.test");
+ vector<boost::filesystem::path> p;
+ p.push_back ("test/data/md5.test");
+ string const t = md5_digest (p, shared_ptr<Job> ());
BOOST_CHECK_EQUAL (t, "15058685ba99decdc4398c7634796eb0");
- BOOST_CHECK_THROW (md5_digest ("foobar"), OpenFileError);
+ p.clear ();
+ p.push_back ("foobar");
+ BOOST_CHECK_THROW (md5_digest (p, shared_ptr<Job> ()), std::runtime_error);
}
obj.uselib = 'BOOST_TEST DCP OPENJPEG AVFORMAT AVFILTER AVCODEC AVUTIL SWSCALE POSTPROC CXML'
obj.use = 'libdcpomatic'
obj.source = """
- test.cc
+ 4k_test.cc
audio_analysis_test.cc
- scaling_test.cc
- film_metadata_test.cc
- frame_rate_test.cc
- colour_conversion_test.cc
audio_delay_test.cc
- silence_padding_test.cc
audio_merger_test.cc
- resampler_test.cc
+ black_fill_test.cc
+ client_server_test.cc
+ colour_conversion_test.cc
ffmpeg_audio_test.cc
- threed_test.cc
- play_test.cc
- ffmpeg_pts_offset.cc
+ ffmpeg_dcp_test.cc
ffmpeg_examiner_test.cc
- black_fill_test.cc
- ratio_test.cc
- pixel_formats_test.cc
+ ffmpeg_pts_offset.cc
+ file_group_test.cc
+ film_metadata_test.cc
+ frame_rate_test.cc
+ image_test.cc
+ job_test.cc
make_black_test.cc
+ pixel_formats_test.cc
+ play_test.cc
+ ratio_test.cc
+ resampler_test.cc
+ scaling_test.cc
+ silence_padding_test.cc
stream_test.cc
+ test.cc
+ threed_test.cc
util_test.cc
- ffmpeg_dcp_test.cc
- job_test.cc
- client_server_test.cc
- image_test.cc
- 4k_test.cc
"""
obj.target = 'unit-tests'