#include "exceptions.h"
#include "examine_content_job.h"
#include "config.h"
-#include "ui_signaller.h"
#include "playlist.h"
#include "player.h"
#include "dcp_content_type.h"
#include "safe_stringstream.h"
#include "environment_info.h"
#include "raw_convert.h"
+#include "audio_processor.h"
+#include "md5_digester.h"
#include <libcxml/cxml.h>
#include <dcp/cpl.h>
#include <dcp/signer.h>
, _sequence_video (true)
, _interop (false)
, _burn_subtitles (false)
+ , _audio_processor (0)
, _state_version (current_state_version)
, _dirty (false)
{
return video_identifier() + ".mxf";
}
-boost::filesystem::path
-Film::video_mxf_filename () const
-{
- return filename_safe_name() + "_video.mxf";
-}
-
-boost::filesystem::path
-Film::audio_mxf_filename () const
-{
- return filename_safe_name() + "_audio.mxf";
-}
-
-boost::filesystem::path
-Film::subtitle_xml_filename () const
-{
- return filename_safe_name() + "_subtitle.xml";
-}
-
string
Film::filename_safe_name () const
{
}
boost::filesystem::path
-Film::audio_analysis_dir () const
+Film::audio_analysis_path (shared_ptr<const Playlist> playlist) const
{
- return dir ("analysis");
+ boost::filesystem::path p = dir ("analysis");
+
+ MD5Digester digester;
+ BOOST_FOREACH (shared_ptr<Content> i, playlist->content ()) {
+ shared_ptr<AudioContent> ac = dynamic_pointer_cast<AudioContent> (i);
+ if (!ac) {
+ continue;
+ }
+
+ digester.add (ac->digest ());
+ digester.add (ac->audio_mapping().digest ());
+ digester.add (ac->audio_gain ());
+ }
+
+ if (audio_processor ()) {
+ digester.add (audio_processor()->id ());
+ }
+
+ p /= digester.get ();
+ return p;
}
/** Add suitable Jobs to the JobManager to create a DCP for this Film */
root->add_child("Signed")->add_child_text (_signed ? "1" : "0");
root->add_child("Encrypted")->add_child_text (_encrypted ? "1" : "0");
root->add_child("Key")->add_child_text (_key.hex ());
+ if (_audio_processor) {
+ root->add_child("AudioProcessor")->add_child_text (_audio_processor->id ());
+ }
_playlist->as_xml (root->add_child ("Playlist"));
return doc;
}
_key = dcp::Key (f.string_child ("Key"));
+ if (f.optional_string_child ("AudioProcessor")) {
+ _audio_processor = AudioProcessor::from_id (f.string_child ("AudioProcessor"));
+ } else {
+ _audio_processor = 0;
+ }
+
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);
/* Find all mapped channels */
- list<dcp::Channel> mapped;
+ list<int> mapped;
for (ContentList::const_iterator i = cl.begin(); i != cl.end(); ++i) {
shared_ptr<const AudioContent> ac = dynamic_pointer_cast<const AudioContent> (*i);
if (ac) {
- list<dcp::Channel> c = ac->audio_mapping().mapped_dcp_channels ();
+ list<int> c = ac->audio_mapping().mapped_output_channels ();
copy (c.begin(), c.end(), back_inserter (mapped));
}
}
int non_lfe = 0;
int lfe = 0;
- for (list<dcp::Channel>::const_iterator i = mapped.begin(); i != mapped.end(); ++i) {
- if (static_cast<int> (*i) >= audio_channels()) {
+ for (list<int>::const_iterator i = mapped.begin(); i != mapped.end(); ++i) {
+ if (*i >= audio_channels()) {
/* This channel is mapped but is not included in the DCP */
continue;
}
- if ((*i) == dcp::LFE) {
+ if (static_cast<dcp::Channel> (*i) == dcp::LFE) {
++lfe;
} else {
++non_lfe;
string
Film::dcp_name (bool if_created_now) const
{
+ string unfiltered;
if (use_isdcf_name()) {
- return isdcf_name (if_created_now);
+ unfiltered = isdcf_name (if_created_now);
+ } else {
+ unfiltered = name ();
}
- return name();
+ /* Filter out `bad' characters which cause problems with some systems.
+ There's no apparent list of what really is allowed, so this is a guess.
+ */
+
+ string filtered;
+ string const allowed = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-_";
+ for (size_t i = 0; i < unfiltered.size(); ++i) {
+ if (allowed.find (unfiltered[i]) != string::npos) {
+ filtered += unfiltered[i];
+ }
+ }
+
+ return filtered;
}
void
signal_changed (BURN_SUBTITLES);
}
+void
+Film::set_audio_processor (AudioProcessor const * processor)
+{
+ _audio_processor = processor;
+ signal_changed (AUDIO_PROCESSOR);
+}
+
void
Film::signal_changed (Property p)
{
break;
}
- if (ui_signaller) {
- ui_signaller->emit (boost::bind (boost::ref (Changed), p));
- }
+ emit (boost::bind (boost::ref (Changed), p));
}
void
{
if (p == VideoContentProperty::VIDEO_FRAME_RATE) {
set_video_frame_rate (_playlist->best_dcp_frame_rate ());
- } else if (
- p == AudioContentProperty::AUDIO_MAPPING ||
- p == AudioContentProperty::AUDIO_CHANNELS) {
+ } else if (p == AudioContentProperty::AUDIO_STREAMS) {
signal_changed (NAME);
}
- if (ui_signaller) {
- ui_signaller->emit (boost::bind (boost::ref (ContentChanged), c, p));
- }
+ emit (boost::bind (boost::ref (ContentChanged), c, p));
}
void
dcp::Size
Film::frame_size () const
{
- return fit_ratio_within (container()->ratio(), full_frame (), 1);
+ return fit_ratio_within (container()->ratio(), full_frame ());
}
dcp::EncryptedKDM
* Note: the decision made by this method isn't, of course, 100% reliable.
*/
bool
-Film::should_be_enough_disk_space (double& required, double& available) const
-{
+Film::should_be_enough_disk_space (double& required, double& available, bool& can_hard_link) const
+{
+ /* Create a test file and see if we can hard-link it */
+ boost::filesystem::path test = internal_video_mxf_dir() / "test";
+ boost::filesystem::path test2 = internal_video_mxf_dir() / "test2";
+ can_hard_link = true;
+ FILE* f = fopen_boost (test, "w");
+ if (f) {
+ fclose (f);
+ boost::system::error_code ec;
+ boost::filesystem::create_hard_link (test, test2, ec);
+ if (ec) {
+ can_hard_link = false;
+ }
+ boost::filesystem::remove (test);
+ boost::filesystem::remove (test2);
+ }
+
boost::filesystem::space_info s = boost::filesystem::space (internal_video_mxf_dir ());
required = double (required_disk_space ()) / 1073741824.0f;
+ if (!can_hard_link) {
+ required *= 2;
+ }
available = double (s.available) / 1073741824.0f;
return (available - required) > 1;
}
return all;
}
+
+/** Change the gains of the supplied AudioMapping to make it a default
+ * for this film. The defaults are guessed based on what processor (if any)
+ * is in use and the number of input channels.
+ */
+void
+Film::make_audio_mapping_default (AudioMapping& mapping) const
+{
+ if (audio_processor ()) {
+ audio_processor()->make_audio_mapping_default (mapping);
+ } else {
+ mapping.make_zero ();
+ if (mapping.input_channels() == 1) {
+ /* Mono -> Centre */
+ mapping.set (0, static_cast<int> (dcp::CENTRE), 1);
+ } else {
+ /* 1:1 mapping */
+ for (int i = 0; i < min (mapping.input_channels(), mapping.output_channels()); ++i) {
+ mapping.set (i, i, 1);
+ }
+ }
+ }
+}
+
+/** @return The names of the channels that audio contents' outputs are passed into;
+ * this is either the DCP or a AudioProcessor.
+ */
+vector<string>
+Film::audio_output_names () const
+{
+ if (audio_processor ()) {
+ return audio_processor()->input_names ();
+ }
+
+ vector<string> n;
+ n.push_back (_("L"));
+ n.push_back (_("R"));
+ n.push_back (_("C"));
+ n.push_back (_("Lfe"));
+ n.push_back (_("Ls"));
+ n.push_back (_("Rs"));
+ n.push_back (_("HI"));
+ n.push_back (_("VI"));
+ n.push_back (_("Lc"));
+ n.push_back (_("Rc"));
+ n.push_back (_("BsL"));
+ n.push_back (_("BsR"));
+
+ return vector<string> (n.begin(), n.begin() + audio_channels ());
+}