#include "content_factory.h"
#include "ui_signaller.h"
#include "exceptions.h"
+#include "film.h"
#include "i18n.h"
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));
+ }
}
}
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
{
/* XXX: should handle multiple paths more gracefully */
+ assert (number_of_paths ());
+
string s = path(0).filename().string ();
if (number_of_paths() > 1) {
s += " ...";
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));
+ content.reset (new FFmpegContent (film, node, version));
} else if (type == "Image") {
- content.reset (new ImageContent (film, node));
+ 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;
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);
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)
, _frame (0)
, _video_stream (-1)
{
- setup_general (long_probe);
+ setup_general ();
setup_video ();
setup_audio ();
}
}
void
-FFmpeg::setup_general (bool long_probe)
+FFmpeg::setup_general ()
{
av_register_all ();
_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, 0, 0, &options) < 0) {
throw OpenFileError (_ffmpeg_content->path(0).string ());
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
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 {
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"
}
-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 ();
}
{
shared_ptr<FFmpegContent> ref = dynamic_pointer_cast<FFmpegContent> (c[0]);
assert (ref);
-
+
for (size_t i = 0; i < c.size(); ++i) {
shared_ptr<FFmpegContent> fc = dynamic_pointer_cast<FFmpegContent> (c[i]);
- if (*(fc->_subtitle_stream.get()) != *(ref->_subtitle_stream.get())) {
+ if (f->with_subtitles() && *(fc->_subtitle_stream.get()) != *(ref->_subtitle_stream.get())) {
throw JoinError (_("Content to be joined must use the same subtitle stream."));
}
return a.id != b.id;
}
-FFmpegAudioStream::FFmpegAudioStream (shared_ptr<const cxml::Node> node)
- : mapping (node->node_child ("Mapping"))
+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);
{
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 () {
, 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 (audio_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 (subtitle_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));
}
}
}
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;
}
}
-ImageContent::ImageContent (shared_ptr<const Film> f, shared_ptr<const cxml::Node> node)
+ImageContent::ImageContent (shared_ptr<const Film> f, shared_ptr<const cxml::Node> node, int)
: Content (f, node)
, VideoContent (f, node)
{
{
public:
ImageContent (boost::shared_ptr<const Film>, boost::filesystem::path);
- ImageContent (boost::shared_ptr<const Film>, boost::shared_ptr<const cxml::Node>);
+ 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 ());
/** @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"))
{
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 ());
using std::pair;
using std::ofstream;
using std::cout;
+using std::streampos;
using boost::shared_ptr;
using boost::thread;
using boost::lexical_cast;
MD5_CTX md5_context;
MD5_Init (&md5_context);
+ vector<int64_t> sizes;
+ for (size_t i = 0; i < files.size(); ++i) {
+ sizes.push_back (boost::filesystem::file_size (files[i]));
+ }
+
for (size_t i = 0; i < files.size(); ++i) {
ifstream f (files[i].string().c_str(), std::ios::binary);
if (!f.good ()) {
}
f.seekg (0, std::ios::end);
- int bytes = f.tellg ();
+ streampos const bytes = f.tellg ();
f.seekg (0, std::ios::beg);
- while (bytes > 0) {
- int const t = min (bytes, buffer_size);
+ streampos remaining = bytes;
+ while (remaining > 0) {
+ int const t = min (remaining, streampos (buffer_size));
f.read (buffer, t);
MD5_Update (&md5_context, buffer, t);
- bytes -= t;
- }
+ remaining -= t;
- if (job) {
- job->set_progress (float (i) / files.size ());
+ if (job) {
+ job->set_progress ((float (i) + 1 - float(remaining) / bytes) / files.size ());
+ }
}
}
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->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 ();
Frame (wxString const & title)
: wxFrame (NULL, -1, title)
, _servers_list_dialog (0)
+ , _hints_dialog (0)
{
wxMenuBar* bar = new wxMenuBar;
setup_menu (bar);
break;
}
- sel.push_back (_film->content()[s]);
+ if (s < int (_film->content().size ())) {
+ sel.push_back (_film->content()[s]);
+ }
}
return sel;
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 ();
}
#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);