+/** @return The path to the directory to write JPEG2000 files to */
+string
+Film::j2k_dir () const
+{
+ assert (format());
+
+ boost::filesystem::path p;
+
+ /* Start with j2c */
+ p /= "j2c";
+
+ pair<string, string> f = Filter::ffmpeg_strings (filters());
+
+ /* Write stuff to specify the filter / post-processing settings that are in use,
+ so that we don't get confused about J2K files generated using different
+ settings.
+ */
+ stringstream s;
+ s << format()->id()
+ << "_" << content_digest()
+ << "_" << crop().left << "_" << crop().right << "_" << crop().top << "_" << crop().bottom
+ << "_" << f.first << "_" << f.second
+ << "_" << scaler()->id()
+ << "_" << j2k_bandwidth()
+ << "_" << boost::lexical_cast<int> (colour_lut());
+
+ p /= s.str ();
+
+ /* Similarly for the A/B case */
+ if (dcp_ab()) {
+ stringstream s;
+ pair<string, string> fa = Filter::ffmpeg_strings (Config::instance()->reference_filters());
+ s << "ab_" << Config::instance()->reference_scaler()->id() << "_" << fa.first << "_" << fa.second;
+ p /= s.str ();
+ }
+
+ return dir (p.string());
+}
+
+/** Add suitable Jobs to the JobManager to create a DCP for this Film.
+ * @param true to transcode, false to use the WAV and J2K files that are already there.
+ */
+void
+Film::make_dcp (bool transcode)
+{
+ set_dci_date_today ();
+
+ if (dcp_name().find ("/") != string::npos) {
+ throw BadSettingError ("name", "cannot contain slashes");
+ }
+
+ log()->log (String::compose ("DVD-o-matic %1 git %2 using %3", dvdomatic_version, dvdomatic_git_commit, dependency_version_summary()));
+
+ {
+ char buffer[128];
+ gethostname (buffer, sizeof (buffer));
+ log()->log (String::compose ("Starting to make DCP on %1", buffer));
+ }
+
+ log()->log (String::compose ("Content is %1; type %2", content_path(), (content_type() == STILL ? "still" : "video")));
+ log()->log (String::compose ("Content length %1", length().get()));
+ log()->log (String::compose ("Content digest %1", content_digest()));
+ log()->log (String::compose ("%1 threads", Config::instance()->num_local_encoding_threads()));
+ log()->log (String::compose ("J2K bandwidth %1", j2k_bandwidth()));
+#ifdef DVDOMATIC_DEBUG
+ log()->log ("DVD-o-matic built in debug mode.");
+#else
+ log()->log ("DVD-o-matic built in optimised mode.");
+#endif
+#ifdef LIBDCP_DEBUG
+ log()->log ("libdcp built in debug mode.");
+#else
+ log()->log ("libdcp built in optimised mode.");
+#endif
+ pair<string, int> const c = cpu_info ();
+ log()->log (String::compose ("CPU: %1, %2 processors", c.first, c.second));
+
+ if (format() == 0) {
+ throw MissingSettingError ("format");
+ }
+
+ if (content().empty ()) {
+ throw MissingSettingError ("content");
+ }
+
+ if (dcp_content_type() == 0) {
+ throw MissingSettingError ("content type");
+ }
+
+ if (name().empty()) {
+ throw MissingSettingError ("name");
+ }
+
+ shared_ptr<EncodeOptions> oe (new EncodeOptions (j2k_dir(), ".j2c", dir ("wavs")));
+ oe->out_size = format()->dcp_size ();
+ oe->padding = format()->dcp_padding (shared_from_this ());
+ if (dcp_length ()) {
+ oe->video_range = make_pair (dcp_trim_start(), dcp_trim_start() + dcp_length().get());
+ if (audio_stream()) {
+ oe->audio_range = make_pair (
+
+ video_frames_to_audio_frames (
+ oe->video_range.get().first,
+ dcp_audio_sample_rate (audio_stream()->sample_rate()),
+ dcp_frame_rate (frames_per_second()).frames_per_second
+ ),
+
+ video_frames_to_audio_frames (
+ oe->video_range.get().second,
+ dcp_audio_sample_rate (audio_stream()->sample_rate()),
+ dcp_frame_rate (frames_per_second()).frames_per_second
+ )
+ );
+ }
+
+ }
+
+ oe->video_skip = dcp_frame_rate (frames_per_second()).skip;
+
+ shared_ptr<DecodeOptions> od (new DecodeOptions);
+ od->decode_subtitles = with_subtitles ();
+
+ shared_ptr<Job> r;
+
+ if (transcode) {
+ if (dcp_ab()) {
+ r = JobManager::instance()->add (shared_ptr<Job> (new ABTranscodeJob (shared_from_this(), od, oe, shared_ptr<Job> ())));
+ } else {
+ r = JobManager::instance()->add (shared_ptr<Job> (new TranscodeJob (shared_from_this(), od, oe, shared_ptr<Job> ())));
+ }
+ }
+
+ r = JobManager::instance()->add (shared_ptr<Job> (new CheckHashesJob (shared_from_this(), od, oe, r)));
+ JobManager::instance()->add (shared_ptr<Job> (new MakeDCPJob (shared_from_this(), oe, r)));
+}
+
+/** Start a job to examine our content file */
+void
+Film::examine_content ()
+{
+ if (_examine_content_job) {
+ return;
+ }
+
+ _examine_content_job.reset (new ExamineContentJob (shared_from_this(), shared_ptr<Job> ()));
+ _examine_content_job->Finished.connect (bind (&Film::examine_content_finished, this));
+ JobManager::instance()->add (_examine_content_job);
+}
+
+void
+Film::examine_content_finished ()
+{
+ _examine_content_job.reset ();
+}
+
+/** Start a job to send our DCP to the configured TMS */
+void
+Film::send_dcp_to_tms ()
+{
+ shared_ptr<Job> j (new SCPDCPJob (shared_from_this(), shared_ptr<Job> ()));
+ JobManager::instance()->add (j);
+}
+
+/** Count the number of frames that have been encoded for this film.
+ * @return frame count.