Confirm overwrite of existing DCP when creating a new one.
[dcpomatic.git] / src / lib / writer.cc
index 4a99b079306f6c226ce3cc1b886852ded75e7e95..595915883228021dd49f86ce920ea9aa4f9492c0 100644 (file)
@@ -30,7 +30,6 @@
 #include "job.h"
 #include "cross.h"
 #include "audio_buffers.h"
-#include "md5_digester.h"
 #include "version.h"
 #include "font.h"
 #include "util.h"
@@ -40,6 +39,7 @@
 #include <fstream>
 #include <cerrno>
 #include <iostream>
+#include <cfloat>
 
 #include "i18n.h"
 
@@ -59,6 +59,9 @@ using std::pair;
 using std::string;
 using std::list;
 using std::cout;
+using std::map;
+using std::min;
+using std::max;
 using boost::shared_ptr;
 using boost::weak_ptr;
 using boost::dynamic_pointer_cast;
@@ -76,14 +79,13 @@ Writer::Writer (shared_ptr<const Film> film, weak_ptr<Job> j)
        , _repeat_written (0)
        , _pushed_to_disk (0)
 {
-       /* Remove any old DCP */
-       boost::filesystem::remove_all (_film->dir (_film->dcp_name ()));
-
        shared_ptr<Job> job = _job.lock ();
        DCPOMATIC_ASSERT (job);
 
-       BOOST_FOREACH (DCPTimePeriod p, _film->reels ()) {
-               _reels.push_back (ReelWriter (film, p, job));
+       int reel_index = 0;
+       list<DCPTimePeriod> const reels = _film->reels ();
+       BOOST_FOREACH (DCPTimePeriod p, reels) {
+               _reels.push_back (ReelWriter (film, p, job, reel_index++, reels.size(), _film->content_summary(p)));
        }
 
        /* We can keep track of the current audio and subtitle reels easily because audio
@@ -315,9 +317,9 @@ try
                                LOG_WARNING (N_("Finishing writer with a left-over queue of %1:"), _queue.size());
                                for (list<QueueItem>::const_iterator i = _queue.begin(); i != _queue.end(); ++i) {
                                        if (i->type == QueueItem::FULL) {
-                                               LOG_WARNING (N_("- type FULL, frame %1, eyes %2"), i->frame, i->eyes);
+                                               LOG_WARNING (N_("- type FULL, frame %1, eyes %2"), i->frame, (int) i->eyes);
                                        } else {
-                                               LOG_WARNING (N_("- type FAKE, size %1, frame %2, eyes %3"), i->size, i->frame, i->eyes);
+                                               LOG_WARNING (N_("- type FAKE, size %1, frame %2, eyes %3"), i->size, i->frame, (int) i->eyes);
                                        }
                                }
                        }
@@ -338,7 +340,7 @@ try
 
                        switch (qi.type) {
                        case QueueItem::FULL:
-                               LOG_DEBUG_ENCODE (N_("Writer FULL-writes %1 (%2)"), qi.frame, qi.eyes);
+                               LOG_DEBUG_ENCODE (N_("Writer FULL-writes %1 (%2)"), qi.frame, (int) qi.eyes);
                                if (!qi.encoded) {
                                        qi.encoded = Data (_film->j2c_path (qi.reel, qi.frame, qi.eyes, false));
                                }
@@ -374,6 +376,8 @@ try
 
                        DCPOMATIC_ASSERT (i != _queue.rend());
                        ++_pushed_to_disk;
+                       /* For the log message below */
+                       int const awaiting = _reels[_queue.front().reel].last_written_video_frame();
                        lock.unlock ();
 
                        /* i is valid here, even though we don't hold a lock on the mutex,
@@ -381,7 +385,7 @@ try
                           thread could erase the last item in the list.
                        */
 
-                       LOG_GENERAL ("Writer full; pushes %1 to disk", i->frame);
+                       LOG_GENERAL ("Writer full; pushes %1 to disk while awaiting %2", i->frame, awaiting);
 
                        i->encoded->write_via_temp (
                                _film->j2c_path (i->reel, i->frame, i->eyes, true),
@@ -457,16 +461,39 @@ Writer::finish ()
 
        dcp.add (cpl);
 
+       /* Calculate digests for each reel in parallel */
+
+       shared_ptr<Job> job = _job.lock ();
+       job->sub (_("Computing digests"));
+
+       boost::asio::io_service service;
+       boost::thread_group pool;
+
+       shared_ptr<boost::asio::io_service::work> work (new boost::asio::io_service::work (service));
+
+       int const threads = max (1, Config::instance()->num_local_encoding_threads ());
+
+       for (int i = 0; i < threads; ++i) {
+               pool.create_thread (boost::bind (&boost::asio::io_service::run, &service));
+       }
+
        BOOST_FOREACH (ReelWriter& i, _reels) {
+               boost::function<void (float)> set_progress = boost::bind (&Writer::set_digest_progress, this, job.get(), _1);
+               service.post (boost::bind (&ReelWriter::calculate_digests, &i, set_progress));
+       }
 
-               shared_ptr<Job> job = _job.lock ();
-               DCPOMATIC_ASSERT (job);
-               i.calculate_digests (job);
+       work.reset ();
+       pool.join_all ();
+       service.stop ();
 
+       /* Add reels to CPL */
+
+       BOOST_FOREACH (ReelWriter& i, _reels) {
                cpl->add (i.create_reel (_reel_assets, _fonts));
        }
 
        dcp::XMLMetadata meta;
+       meta.annotation_text = cpl->annotation_text ();
        meta.creator = Config::instance()->dcp_creator ();
        if (meta.creator.empty ()) {
                meta.creator = String::compose ("DCP-o-matic %1 %2", dcpomatic_version, dcpomatic_git_commit);
@@ -488,7 +515,7 @@ Writer::finish ()
                }
        }
 
-       dcp.write_xml (_film->interop () ? dcp::INTEROP : dcp::SMPTE, meta, signer);
+       dcp.write_xml (_film->interop () ? dcp::INTEROP : dcp::SMPTE, meta, signer, Config::instance()->dcp_metadata_filename_format());
 
        LOG_GENERAL (
                N_("Wrote %1 FULL, %2 FAKE, %3 REPEAT, %4 pushed to disk"), _full_written, _fake_written, _repeat_written, _pushed_to_disk
@@ -598,3 +625,18 @@ Writer::video_reel (int frame) const
        DCPOMATIC_ASSERT (i < _reels.size ());
        return i;
 }
+
+void
+Writer::set_digest_progress (Job* job, float progress)
+{
+       /* I believe this is thread-safe */
+       _digest_progresses[boost::this_thread::get_id()] = progress;
+
+       boost::mutex::scoped_lock lm (_digest_progresses_mutex);
+       float min_progress = FLT_MAX;
+       for (map<boost::thread::id, float>::const_iterator i = _digest_progresses.begin(); i != _digest_progresses.end(); ++i) {
+               min_progress = min (min_progress, i->second);
+       }
+
+       job->set_progress (min_progress);
+}