+/** @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();
+
+ 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));
+ }
+
+ 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<Options> o (new Options (j2k_dir(), ".j2c", dir ("wavs")));
+ o->out_size = format()->dcp_size ();
+ o->padding = format()->dcp_padding (shared_from_this ());
+ o->ratio = format()->ratio_as_float (shared_from_this ());
+ o->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(), o, shared_ptr<Job> ())));
+ } else {
+ r = JobManager::instance()->add (shared_ptr<Job> (new TranscodeJob (shared_from_this(), o, shared_ptr<Job> ())));
+ }
+ }
+
+ r = JobManager::instance()->add (shared_ptr<Job> (new CheckHashesJob (shared_from_this(), o, r)));
+ JobManager::instance()->add (shared_ptr<Job> (new MakeDCPJob (shared_from_this(), o, r)));
+}
+
+/** Start a job to examine our content file */
+void
+Film::examine_content ()
+{
+ if (_examine_content_job) {
+ return;
+ }
+
+ set_thumbs (vector<int> ());
+ boost::filesystem::remove_all (dir ("thumbs"));
+
+ /* This call will recreate the directory */
+ dir ("thumbs");
+
+ _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 ();
+}
+
+/** @return full paths to any audio files that this Film has */
+vector<string>
+Film::audio_files () const
+{
+ vector<string> f;
+ for (boost::filesystem::directory_iterator i = boost::filesystem::directory_iterator (dir("wavs")); i != boost::filesystem::directory_iterator(); ++i) {
+ f.push_back (i->path().string ());
+ }
+
+ return f;
+}
+
+/** 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);
+}
+
+void
+Film::copy_from_dvd ()
+{
+ shared_ptr<Job> j (new CopyFromDVDJob (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.
+ */
+int
+Film::encoded_frames () const
+{
+ if (format() == 0) {
+ return 0;
+ }
+
+ int N = 0;
+ for (boost::filesystem::directory_iterator i = boost::filesystem::directory_iterator (j2k_dir ()); i != boost::filesystem::directory_iterator(); ++i) {
+ ++N;
+ boost::this_thread::interruption_point ();
+ }
+
+ return N;
+}
+
+/** Return the filename of a subtitle image if one exists for a given thumb index.
+ * @param Thumbnail index.
+ * @return Position of the image within the source frame, and the image filename, if one exists.
+ * Otherwise the filename will be empty.