/*
- Copyright (C) 2012-2013 Carl Hetherington <cth@carlh.net>
+ Copyright (C) 2012-2014 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
#include <boost/filesystem.hpp>
#include <boost/algorithm/string.hpp>
#include <boost/lexical_cast.hpp>
-#include <boost/date_time.hpp>
#include <libxml++/libxml++.h>
#include <libcxml/cxml.h>
-#include <libdcp/signer_chain.h>
-#include <libdcp/cpl.h>
-#include <libdcp/signer.h>
-#include <libdcp/util.h>
-#include <libdcp/kdm.h>
+#include <dcp/signer_chain.h>
+#include <dcp/cpl.h>
+#include <dcp/signer.h>
+#include <dcp/util.h>
+#include <dcp/local_time.h>
+#include <dcp/raw_convert.h>
#include "film.h"
#include "job.h"
#include "util.h"
using std::list;
using boost::shared_ptr;
using boost::weak_ptr;
-using boost::lexical_cast;
using boost::dynamic_pointer_cast;
using boost::to_upper_copy;
using boost::ends_with;
using boost::starts_with;
using boost::optional;
-using libdcp::Size;
-using libdcp::Signer;
+using dcp::Size;
+using dcp::Signer;
+using dcp::raw_convert;
/* 5 -> 6
* AudioMapping XML changed.
* 6 -> 7
* Subtitle offset changed to subtitle y offset, and subtitle x offset added.
+ * 7 -> 8
+ * Use <Scale> tag in <VideoContent> rather than <Ratio>.
*/
-int const Film::state_version = 7;
+int const Film::current_state_version = 8;
/** Construct a Film object in a given directory.
*
* @param dir Film directory.
*/
-Film::Film (boost::filesystem::path dir)
+Film::Film (boost::filesystem::path dir, bool log)
: _playlist (new Playlist)
, _use_dci_name (true)
, _dcp_content_type (Config::instance()->default_dcp_content_type ())
, _three_d (false)
, _sequence_video (true)
, _interop (false)
+ , _state_version (current_state_version)
, _dirty (false)
{
set_dci_date_today ();
}
set_directory (result);
- _log.reset (new FileLog (file ("log")));
+ if (log) {
+ _log.reset (new FileLog (file ("log")));
+ } else {
+ _log.reset (new NullLog);
+ }
_playlist->set_sequence_video (_sequence_video);
}
Film::video_identifier () const
{
assert (container ());
- LocaleGuard lg;
stringstream s;
+ s.imbue (std::locale::classic ());
+
s << container()->id()
<< "_" << resolution_to_string (_resolution)
<< "_" << _playlist->video_identifier()
}
boost::filesystem::path
-Film::audio_analysis_path (shared_ptr<const AudioContent> c) const
+Film::audio_analysis_dir () const
{
- boost::filesystem::path p = dir ("analysis");
- p /= c->digest();
- return p;
+ return dir ("analysis");
}
/** Add suitable Jobs to the JobManager to create a DCP for this Film */
if (dcp_name().find ("/") != string::npos) {
throw BadSettingError (_("name"), _("cannot contain slashes"));
}
-
+
+ /* It seems to make sense to auto-save metadata here, since the make DCP may last
+ a long time, and crashes/power failures are moderately likely.
+ */
+ write_metadata ();
+
log()->log (String::compose ("DCP-o-matic %1 git %2 using %3", dcpomatic_version, dcpomatic_git_commit, dependency_version_summary()));
{
return N;
}
-/** Write state to our `metadata' file */
-void
-Film::write_metadata () const
+shared_ptr<xmlpp::Document>
+Film::metadata () const
{
- if (!boost::filesystem::exists (directory ())) {
- boost::filesystem::create_directory (directory ());
- }
-
- LocaleGuard lg;
+ shared_ptr<xmlpp::Document> doc (new xmlpp::Document);
+ xmlpp::Element* root = doc->create_root_node ("Metadata");
- boost::filesystem::create_directories (directory ());
-
- xmlpp::Document doc;
- xmlpp::Element* root = doc.create_root_node ("Metadata");
-
- root->add_child("Version")->add_child_text (lexical_cast<string> (state_version));
+ root->add_child("Version")->add_child_text (raw_convert<string> (current_state_version));
root->add_child("Name")->add_child_text (_name);
root->add_child("UseDCIName")->add_child_text (_use_dci_name ? "1" : "0");
root->add_child("Resolution")->add_child_text (resolution_to_string (_resolution));
root->add_child("Scaler")->add_child_text (_scaler->id ());
root->add_child("WithSubtitles")->add_child_text (_with_subtitles ? "1" : "0");
- root->add_child("J2KBandwidth")->add_child_text (lexical_cast<string> (_j2k_bandwidth));
+ root->add_child("J2KBandwidth")->add_child_text (raw_convert<string> (_j2k_bandwidth));
_dci_metadata.as_xml (root->add_child ("DCIMetadata"));
- root->add_child("VideoFrameRate")->add_child_text (lexical_cast<string> (_video_frame_rate));
+ root->add_child("VideoFrameRate")->add_child_text (raw_convert<string> (_video_frame_rate));
root->add_child("DCIDate")->add_child_text (boost::gregorian::to_iso_string (_dci_date));
- root->add_child("AudioChannels")->add_child_text (lexical_cast<string> (_audio_channels));
+ root->add_child("AudioChannels")->add_child_text (raw_convert<string> (_audio_channels));
root->add_child("ThreeD")->add_child_text (_three_d ? "1" : "0");
root->add_child("SequenceVideo")->add_child_text (_sequence_video ? "1" : "0");
root->add_child("Interop")->add_child_text (_interop ? "1" : "0");
root->add_child("Key")->add_child_text (_key.hex ());
_playlist->as_xml (root->add_child ("Playlist"));
- doc.write_to_file_formatted (file("metadata.xml").string ());
-
- _dirty = false;
+ return doc;
}
-/** Read state from our metadata file */
+/** Write state to our `metadata' file */
void
-Film::read_metadata ()
+Film::write_metadata () const
{
- LocaleGuard lg;
+ boost::filesystem::create_directories (directory ());
+ shared_ptr<xmlpp::Document> doc = metadata ();
+ doc->write_to_file_formatted (file("metadata.xml").string ());
+ _dirty = false;
+}
+/** Read state from our metadata file.
+ * @return Notes about things that the user should know about, or empty.
+ */
+list<string>
+Film::read_metadata ()
+{
if (boost::filesystem::exists (file ("metadata")) && !boost::filesystem::exists (file ("metadata.xml"))) {
throw StringError (_("This film was created with an older version of DCP-o-matic, and unfortunately it cannot be loaded into this version. You will need to create a new Film, re-add your content and set it up again. Sorry!"));
}
cxml::Document f ("Metadata");
f.read_file (file ("metadata.xml"));
- int const version = f.number_child<int> ("Version");
+ _state_version = f.number_child<int> ("Version");
+ if (_state_version > current_state_version) {
+ throw StringError (_("This film was created with a newer version of DCP-o-matic, and it cannot be loaded into this version. Sorry!"));
+ }
_name = f.string_child ("Name");
_use_dci_name = f.bool_child ("UseDCIName");
_sequence_video = f.bool_child ("SequenceVideo");
_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"), version);
+ _key = dcp::Key (f.string_child ("Key"));
+
+ list<string> notes;
+ /* This method is the only one that can return notes (so far) */
+ _playlist->set_from_xml (shared_from_this(), f.node_child ("Playlist"), _state_version, notes);
_dirty = false;
+ return notes;
}
/** Given a directory name, return its full path within the Film's directory.
return file (p);
}
-/** @return List of subdirectories (not full paths) containing DCPs that can be successfully libdcp::DCP::read() */
+/** @return List of subdirectories (not full paths) containing DCPs that can be successfully dcp::DCP::read() */
list<boost::filesystem::path>
Film::dcps () const
{
) {
try {
- libdcp::DCP dcp (*i);
+ dcp::DCP dcp (*i);
dcp.read ();
out.push_back (i->path().leaf ());
} catch (...) {
void
Film::examine_and_add_content (shared_ptr<Content> c)
{
+ if (dynamic_pointer_cast<FFmpegContent> (c)) {
+ run_ffprobe (c->path(0), file ("ffprobe.log"), _log);
+ }
+
shared_ptr<Job> j (new ExamineContentJob (shared_from_this(), c));
j->Finished.connect (bind (&Film::maybe_add_content, this, boost::weak_ptr<Job> (j), boost::weak_ptr<Content> (c)));
JobManager::instance()->add (j);
_playlist->move_later (c);
}
-Time
+DCPTime
Film::length () const
{
return _playlist->length ();
return _playlist->has_subtitles ();
}
-OutputVideoFrame
+int
Film::best_video_frame_rate () const
{
return _playlist->best_dcp_frame_rate ();
}
+FrameRateChange
+Film::active_frame_rate_change (DCPTime t) const
+{
+ return _playlist->active_frame_rate_change (t, video_frame_rate ());
+}
+
void
Film::playlist_content_changed (boost::weak_ptr<Content> c, int p)
{
signal_changed (CONTENT);
}
-OutputAudioFrame
-Film::time_to_audio_frames (Time t) const
-{
- return t * audio_frame_rate () / TIME_HZ;
-}
-
-OutputVideoFrame
-Film::time_to_video_frames (Time t) const
-{
- return t * video_frame_rate () / TIME_HZ;
-}
-
-Time
-Film::audio_frames_to_time (OutputAudioFrame f) const
-{
- return f * TIME_HZ / audio_frame_rate ();
-}
-
-Time
-Film::video_frames_to_time (OutputVideoFrame f) const
-{
- return f * TIME_HZ / video_frame_rate ();
-}
-
-OutputAudioFrame
+int
Film::audio_frame_rate () const
{
/* XXX */
signal_changed (SEQUENCE_VIDEO);
}
-libdcp::Size
+/** @return Size of the largest possible image in whatever resolution we are using */
+dcp::Size
Film::full_frame () const
{
switch (_resolution) {
case RESOLUTION_2K:
- return libdcp::Size (2048, 1080);
+ return dcp::Size (2048, 1080);
case RESOLUTION_4K:
- return libdcp::Size (4096, 2160);
+ return dcp::Size (4096, 2160);
}
assert (false);
- return libdcp::Size ();
+ return dcp::Size ();
+}
+
+/** @return Size of the frame */
+dcp::Size
+Film::frame_size () const
+{
+ return fit_ratio_within (container()->ratio(), full_frame ());
}
-libdcp::KDM
+dcp::EncryptedKDM
Film::make_kdm (
- shared_ptr<libdcp::Certificate> target,
+ shared_ptr<dcp::Certificate> target,
boost::filesystem::path dcp_dir,
- boost::posix_time::ptime from,
- boost::posix_time::ptime until
+ dcp::LocalTime from,
+ dcp::LocalTime until
) const
{
shared_ptr<const Signer> signer = make_signer ();
- libdcp::DCP dcp (dir (dcp_dir.string ()));
+ dcp::DCP dcp (dir (dcp_dir.string ()));
try {
dcp.read ();
throw KDMError (_("Could not read DCP to make KDM for"));
}
- time_t now = time (0);
- struct tm* tm = localtime (&now);
- string const issue_date = libdcp::tm_to_string (tm);
-
dcp.cpls().front()->set_mxf_keys (key ());
- return libdcp::KDM (dcp.cpls().front(), signer, target, from, until, "DCP-o-matic", issue_date);
+ return dcp::DecryptedKDM (
+ dcp.cpls().front(), from, until, "DCP-o-matic", dcp.cpls().front()->content_title_text(), dcp::LocalTime().as_string()
+ ).encrypt (signer, target);
}
-list<libdcp::KDM>
+list<dcp::EncryptedKDM>
Film::make_kdms (
list<shared_ptr<Screen> > screens,
boost::filesystem::path dcp,
- boost::posix_time::ptime from,
- boost::posix_time::ptime until
+ dcp::LocalTime from,
+ dcp::LocalTime until
) const
{
- list<libdcp::KDM> kdms;
+ list<dcp::EncryptedKDM> kdms;
for (list<shared_ptr<Screen> >::iterator i = screens.begin(); i != screens.end(); ++i) {
kdms.push_back (make_kdm ((*i)->certificate, dcp, from, until));
uint64_t
Film::required_disk_space () const
{
- return uint64_t (j2k_bandwidth() / 8) * length() / TIME_HZ;
+ return uint64_t (j2k_bandwidth() / 8) * length().seconds();
}
/** This method checks the disk that the Film is on and tries to decide whether or not