using std::back_inserter;
using std::map;
using std::exception;
+using std::find;
using boost::shared_ptr;
using boost::weak_ptr;
using boost::dynamic_pointer_cast;
using boost::optional;
using boost::is_any_of;
using dcp::raw_convert;
+using namespace dcpomatic;
string const Film::metadata_file = "metadata.xml";
, _user_explicit_video_frame_rate (false)
, _state_version (current_state_version)
, _dirty (false)
+ , _tolerant (false)
{
set_isdcf_date_today ();
_playlist_change_connection = _playlist->Change.connect (bind (&Film::playlist_change, this, _1));
- _playlist_order_changed_connection = _playlist->OrderChanged.connect (bind (&Film::playlist_order_changed, this));
+ _playlist_order_changed_connection = _playlist->OrderChange.connect (bind (&Film::playlist_order_changed, this));
_playlist_content_change_connection = _playlist->ContentChange.connect (bind (&Film::playlist_content_change, this, _1, _2, _3, _4));
+ _playlist_length_change_connection = _playlist->LengthChange.connect (bind(&Film::playlist_length_change, this));
if (dir) {
/* Make state.directory a complete path without ..s (where possible)
boost::filesystem::path result;
for (boost::filesystem::path::iterator i = p.begin(); i != p.end(); ++i) {
if (*i == "..") {
- if (boost::filesystem::is_symlink (result) || result.filename() == "..") {
+ boost::system::error_code ec;
+ if (boost::filesystem::is_symlink(result, ec) || result.filename() == "..") {
result /= *i;
} else {
result = result.parent_path ();
+ "_" + raw_convert<string>(j2k_bandwidth());
if (encrypted ()) {
- s += "_E";
+ /* This is insecure but hey, the key is in plaintext in metadata.xml */
+ s += "_E" + _key.hex();
} else {
s += "_P";
}
return p;
}
-/** Add suitable Jobs to the JobManager to create a DCP for this Film */
+/** Add suitable Jobs to the JobManager to create a DCP for this Film.
+ * @param gui true if this is being called from a GUI tool.
+ * @param check true to check the content in the project for changes before making the DCP.
+ */
void
-Film::make_dcp ()
+Film::make_dcp (bool gui, bool check)
{
if (dcp_name().find ("/") != string::npos) {
throw BadSettingError (_("name"), _("Cannot contain slashes"));
throw runtime_error (_("You must add some content to the DCP before creating it"));
}
+ if (length() == DCPTime()) {
+ throw runtime_error (_("The DCP is empty, perhaps because all the content has zero length."));
+ }
+
if (dcp_content_type() == 0) {
throw MissingSettingError (_("content type"));
}
shared_ptr<TranscodeJob> tj (new TranscodeJob (shared_from_this()));
tj->set_encoder (shared_ptr<Encoder> (new DCPEncoder (shared_from_this(), tj)));
- shared_ptr<CheckContentChangeJob> cc (new CheckContentChangeJob (shared_from_this(), tj));
- JobManager::instance()->add (cc);
+ if (check) {
+ shared_ptr<CheckContentChangeJob> cc (new CheckContentChangeJob(shared_from_this(), tj, gui));
+ JobManager::instance()->add (cc);
+ } else {
+ JobManager::instance()->add (tj);
+ }
}
/** Start a job to send our DCP to the configured TMS */
m->set_attribute("Type", dcp::marker_to_string(i->first));
m->add_child_text(raw_convert<string>(i->second.get()));
}
+ BOOST_FOREACH (dcp::Rating i, _ratings) {
+ i.as_xml (root->add_child("Rating"));
+ }
+ root->add_child("ContentVersion")->add_child_text(_content_version);
_playlist->as_xml (root->add_child ("Playlist"), with_content_paths);
return doc;
_audio_processor = 0;
}
+ if (_audio_processor && !Config::instance()->show_experimental_audio_processors()) {
+ list<AudioProcessor const *> ap = AudioProcessor::visible();
+ if (find(ap.begin(), ap.end(), _audio_processor) == ap.end()) {
+ Config::instance()->set_show_experimental_audio_processors(true);
+ }
+ }
+
_reel_type = static_cast<ReelType> (f.optional_number_child<int>("ReelType").get_value_or (static_cast<int>(REELTYPE_SINGLE)));
_reel_length = f.optional_number_child<int64_t>("ReelLength").get_value_or (2000000000);
_upload_after_make_dcp = f.optional_bool_child("UploadAfterMakeDCP").get_value_or (false);
_markers[dcp::marker_from_string(i->string_attribute("Type"))] = DCPTime(dcp::raw_convert<DCPTime::Type>(i->content()));
}
+ BOOST_FOREACH (cxml::ConstNodePtr i, f.node_children("Rating")) {
+ _ratings.push_back (dcp::Rating(i));
+ }
+
+ _content_version = f.optional_string_child("ContentVersion").get_value_or("");
+
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);
/* Write backtraces to this film's directory, until another film is loaded */
/* Count mapped audio channels */
- pair<int, int> ch = audio_channel_types (mapped_audio_channels(), audio_channels());
+ list<int> mapped = mapped_audio_channels ();
+
+ pair<int, int> ch = audio_channel_types (mapped, audio_channels());
if (!ch.first && !ch.second) {
d += "_MOS";
} else if (ch.first) {
d += String::compose("_%1%2", ch.first, ch.second);
}
- /* XXX: HI/VI */
+ if (audio_channels() > static_cast<int>(dcp::HI) && find(mapped.begin(), mapped.end(), dcp::HI) != mapped.end()) {
+ d += "-HI";
+ }
+ if (audio_channels() > static_cast<int>(dcp::VI) && find(mapped.begin(), mapped.end(), dcp::VI) != mapped.end()) {
+ d += "-VI";
+ }
d += "_" + resolution_to_string (_resolution);
if (type == CHANGE_TYPE_DONE) {
emit (boost::bind (boost::ref (ContentChange), type, c, p, frequent));
+ if (!frequent) {
+ check_settings_consistency ();
+ }
} else {
ContentChange (type, c, p, frequent);
}
}
+void
+Film::playlist_length_change ()
+{
+ LengthChange ();
+}
+
void
Film::playlist_change (ChangeType type)
{
signal_change (type, NAME);
if (type == CHANGE_TYPE_DONE) {
- /* Check that this change hasn't made our settings inconsistent */
- bool change_made = false;
- BOOST_FOREACH (shared_ptr<Content> i, content()) {
- shared_ptr<DCPContent> d = dynamic_pointer_cast<DCPContent>(i);
- if (!d) {
- continue;
- }
+ check_settings_consistency ();
+ }
+}
- string why_not;
- if (d->reference_video() && !d->can_reference_video(shared_from_this(), why_not)) {
- d->set_reference_video(false);
- change_made = true;
- }
- if (d->reference_audio() && !d->can_reference_audio(shared_from_this(), why_not)) {
- d->set_reference_audio(false);
- change_made = true;
- }
+/** Check for (and if necessary fix) impossible settings combinations, like
+ * video set to being referenced when it can't be.
+ */
+void
+Film::check_settings_consistency ()
+{
+ bool change_made = false;
+ BOOST_FOREACH (shared_ptr<Content> i, content()) {
+ shared_ptr<DCPContent> d = dynamic_pointer_cast<DCPContent>(i);
+ if (!d) {
+ continue;
}
- if (change_made) {
- Message (_("DCP-o-matic had to change your settings for referring to DCPs as OV. Please review those settings to make sure they are what you want."));
+ string why_not;
+ if (d->reference_video() && !d->can_reference_video(shared_from_this(), why_not)) {
+ d->set_reference_video(false);
+ change_made = true;
+ }
+ if (d->reference_audio() && !d->can_reference_audio(shared_from_this(), why_not)) {
+ d->set_reference_audio(false);
+ change_made = true;
}
}
+
+ if (change_made) {
+ Message (_("DCP-o-matic had to change your settings for referring to DCPs as OV. Please review those settings to make sure they are what you want."));
+ }
}
void
map<shared_ptr<const dcp::ReelMXF>, dcp::Key> keys;
- BOOST_FOREACH(shared_ptr<const dcp::ReelAsset> i, cpl->reel_assets ()) {
- shared_ptr<const dcp::ReelMXF> mxf = boost::dynamic_pointer_cast<const dcp::ReelMXF> (i);
- if (!mxf || !mxf->key_id()) {
+ BOOST_FOREACH(shared_ptr<const dcp::ReelMXF> i, cpl->reel_mxfs()) {
+ if (!i->key_id()) {
continue;
}
/* Get any imported key for this ID */
bool done = false;
BOOST_FOREACH (dcp::DecryptedKDMKey j, imported_keys) {
- if (j.id() == mxf->key_id().get()) {
- LOG_GENERAL ("Using imported key for %1", mxf->key_id().get());
- keys[mxf] = j.key();
+ if (j.id() == i->key_id().get()) {
+ LOG_GENERAL ("Using imported key for %1", i->key_id().get());
+ keys[i] = j.key();
done = true;
}
}
if (!done) {
/* No imported key; it must be an asset that we encrypted */
- LOG_GENERAL ("Using our own key for %1", mxf->key_id().get());
- keys[mxf] = key();
+ LOG_GENERAL ("Using our own key for %1", i->key_id().get());
+ keys[i] = key();
}
}
* @param disable_forensic_marking_audio if not set, don't disable forensic marking of audio. If set to 0,
* disable all forensic marking; if set above 0, disable forensic marking above that channel.
*/
-list<ScreenKDM>
+list<shared_ptr<ScreenKDM> >
Film::make_kdms (
list<shared_ptr<Screen> > screens,
boost::filesystem::path cpl_file,
optional<int> disable_forensic_marking_audio
) const
{
- list<ScreenKDM> kdms;
+ list<shared_ptr<ScreenKDM> > kdms;
BOOST_FOREACH (shared_ptr<Screen> i, screens) {
if (i->recipient) {
disable_forensic_marking_audio
);
- kdms.push_back (ScreenKDM (i, kdm));
+ kdms.push_back (shared_ptr<ScreenKDM>(new DCPScreenKDM(i, kdm)));
}
}
break;
case REELTYPE_BY_VIDEO_CONTENT:
{
- optional<DCPTime> last_split;
- shared_ptr<Content> last_video;
- BOOST_FOREACH (shared_ptr<Content> c, content ()) {
+ /* Collect all reel boundaries */
+ list<DCPTime> split_points;
+ split_points.push_back (DCPTime());
+ split_points.push_back (len);
+ BOOST_FOREACH (shared_ptr<Content> c, content()) {
if (c->video) {
BOOST_FOREACH (DCPTime t, c->reel_split_points(shared_from_this())) {
- if (last_split) {
- p.push_back (DCPTimePeriod (last_split.get(), t));
- }
- last_split = t;
+ split_points.push_back (t);
}
- last_video = c;
+ split_points.push_back (c->end(shared_from_this()));
}
}
- DCPTime video_end = last_video ? last_video->end(shared_from_this()) : DCPTime(0);
- if (last_split) {
- /* Definitely go from the last split to the end of the video content */
- p.push_back (DCPTimePeriod (last_split.get(), video_end));
- }
+ split_points.sort ();
+ split_points.unique ();
- if (video_end < len) {
- /* And maybe go after that as well if there is any non-video hanging over the end */
- p.push_back (DCPTimePeriod (video_end, len));
+ /* Make them into periods */
+ optional<DCPTime> last;
+ BOOST_FOREACH (DCPTime t, split_points) {
+ if (last) {
+ p.push_back (DCPTimePeriod(*last, t));
+ }
+ last = t;
}
break;
}
_markers.erase (type);
}
+void
+Film::set_ratings (vector<dcp::Rating> r)
+{
+ ChangeSignaller<Film> ch (this, RATINGS);
+ _ratings = r;
+}
+
+void
+Film::set_content_version (string v)
+{
+ ChangeSignaller<Film> ch (this, CONTENT_VERSION);
+ _content_version = v;
+}
+
optional<DCPTime>
Film::marker (dcp::Marker type) const
{
}
return i->second;
}
+
+shared_ptr<InfoFileHandle>
+Film::info_file_handle (DCPTimePeriod period, bool read) const
+{
+ return shared_ptr<InfoFileHandle> (new InfoFileHandle(_info_file_mutex, info_file(period), read));
+}
+
+InfoFileHandle::InfoFileHandle (boost::mutex& mutex, boost::filesystem::path file, bool read)
+ : _lock (mutex)
+ , _file (file)
+{
+ if (read) {
+ _handle = fopen_boost (file, "rb");
+ if (!_handle) {
+ throw OpenFileError (file, errno, OpenFileError::READ);
+ }
+ } else {
+ bool const exists = boost::filesystem::exists (file);
+ if (exists) {
+ _handle = fopen_boost (file, "r+b");
+ } else {
+ _handle = fopen_boost (file, "wb");
+ }
+
+ if (!_handle) {
+ throw OpenFileError (file, errno, exists ? OpenFileError::READ_WRITE : OpenFileError::WRITE);
+ }
+ }
+}
+
+InfoFileHandle::~InfoFileHandle ()
+{
+ fclose (_handle);
+}