diff options
| author | Carl Hetherington <cth@carlh.net> | 2019-10-21 17:25:04 +0200 |
|---|---|---|
| committer | Carl Hetherington <cth@carlh.net> | 2019-10-21 17:25:04 +0200 |
| commit | 48b82de5b6e8e07330a2f72dbddd8d9830fe047e (patch) | |
| tree | 9eab5b143bd389cfa7cd02028a16964abf0baaf9 /src/lib | |
| parent | 6dc179f7d08477ecc7bac1257b47dda048a1b878 (diff) | |
Copy with progress updates when we might copy long files (#1574).
Diffstat (limited to 'src/lib')
| -rw-r--r-- | src/lib/reel_writer.cc | 37 | ||||
| -rw-r--r-- | src/lib/reel_writer.h | 4 | ||||
| -rw-r--r-- | src/lib/util.cc | 52 | ||||
| -rw-r--r-- | src/lib/util.h | 3 |
4 files changed, 86 insertions, 10 deletions
diff --git a/src/lib/reel_writer.cc b/src/lib/reel_writer.cc index f6313773e..eeea48e42 100644 --- a/src/lib/reel_writer.cc +++ b/src/lib/reel_writer.cc @@ -54,6 +54,7 @@ using std::list; using std::string; using std::cout; +using std::exception; using std::map; using boost::shared_ptr; using boost::optional; @@ -75,6 +76,7 @@ ReelWriter::ReelWriter ( , _reel_index (reel_index) , _reel_count (reel_count) , _content_summary (content_summary) + , _job (job) { /* Create our picture asset in a subdirectory, named according to those film's parameters which affect the video output. We will hard-link @@ -100,9 +102,6 @@ ReelWriter::ReelWriter ( _film->internal_video_asset_dir() / _film->internal_video_asset_filename(_period) ); - if (job) { - job->sub (_("Checking existing image data")); - } _first_nonexistant_frame = check_existing_picture_asset (); _picture_asset_writer = _picture_asset->start_write ( @@ -180,16 +179,27 @@ ReelWriter::check_existing_picture_asset () DCPOMATIC_ASSERT (_picture_asset->file()); boost::filesystem::path asset = _picture_asset->file().get(); + shared_ptr<Job> job = _job.lock (); + /* If there is an existing asset, break any hard links to it as we are about to change its contents (if only by changing the IDs); see #1126. */ if (boost::filesystem::exists(asset) && boost::filesystem::hard_link_count(asset) > 1) { - boost::filesystem::copy_file (asset, asset.string() + ".tmp"); + if (job) { + job->sub (_("Copying old video file")); + copy_in_bits (asset, asset.string() + ".tmp", bind(&Job::set_progress, job.get(), _1, false)); + } else { + boost::filesystem::copy_file (asset, asset.string() + ".tmp"); + } boost::filesystem::remove (asset); boost::filesystem::rename (asset.string() + ".tmp", asset); } + if (job) { + job->sub (_("Checking existing image data")); + } + /* Try to open the existing asset */ FILE* asset_file = fopen_boost (asset, "rb"); if (!asset_file) { @@ -295,10 +305,21 @@ ReelWriter::finish () boost::filesystem::create_hard_link (video_from, video_to, ec); if (ec) { LOG_WARNING_NC ("Hard-link failed; copying instead"); - boost::filesystem::copy_file (video_from, video_to, ec); - if (ec) { - LOG_ERROR ("Failed to copy video file from %1 to %2 (%3)", video_from.string(), video_to.string(), ec.message ()); - throw FileError (ec.message(), video_from); + shared_ptr<Job> job = _job.lock (); + if (job) { + job->sub (_("Copying video file into DCP")); + try { + copy_in_bits (video_from, video_to, bind(&Job::set_progress, job.get(), _1, false)); + } catch (exception& e) { + LOG_ERROR ("Failed to copy video file from %1 to %2 (%3)", video_from.string(), video_to.string(), e.what()); + throw FileError (e.what(), video_from); + } + } else { + boost::filesystem::copy_file (video_from, video_to, ec); + if (ec) { + LOG_ERROR ("Failed to copy video file from %1 to %2 (%3)", video_from.string(), video_to.string(), ec.message ()); + throw FileError (ec.message(), video_from); + } } } diff --git a/src/lib/reel_writer.h b/src/lib/reel_writer.h index 8649ea37f..46f477616 100644 --- a/src/lib/reel_writer.h +++ b/src/lib/reel_writer.h @@ -1,5 +1,5 @@ /* - Copyright (C) 2012-2018 Carl Hetherington <cth@carlh.net> + Copyright (C) 2012-2019 Carl Hetherington <cth@carlh.net> This file is part of DCP-o-matic. @@ -25,6 +25,7 @@ #include "dcp_text_track.h" #include <dcp/picture_asset_writer.h> #include <boost/shared_ptr.hpp> +#include <boost/weak_ptr.hpp> namespace dcpomatic { class Font; @@ -116,6 +117,7 @@ private: /** number of reels in the DCP */ int _reel_count; boost::optional<std::string> _content_summary; + boost::weak_ptr<Job> _job; boost::shared_ptr<dcp::PictureAsset> _picture_asset; boost::shared_ptr<dcp::PictureAssetWriter> _picture_asset_writer; diff --git a/src/lib/util.cc b/src/lib/util.cc index cd2d5a368..234c22246 100644 --- a/src/lib/util.cc +++ b/src/lib/util.cc @@ -1015,6 +1015,58 @@ show_jobs_on_console (bool progress) return error; } +/** XXX: could use mmap? */ +void +copy_in_bits (boost::filesystem::path from, boost::filesystem::path to, boost::function<void (float)> progress) +{ + FILE* f = fopen_boost (from, "rb"); + if (!f) { + throw OpenFileError (from, errno, OpenFileError::READ); + } + FILE* t = fopen_boost (to, "wb"); + if (!t) { + fclose (f); + throw OpenFileError (to, errno, OpenFileError::WRITE); + } + + /* on the order of a second's worth of copying */ + boost::uintmax_t const chunk = 20 * 1024 * 1024; + + uint8_t* buffer = static_cast<uint8_t*> (malloc(chunk)); + if (!buffer) { + throw std::bad_alloc (); + } + + boost::uintmax_t const total = boost::filesystem::file_size (from); + boost::uintmax_t remaining = total; + + while (remaining) { + boost::uintmax_t this_time = min (chunk, remaining); + size_t N = fread (buffer, 1, chunk, f); + if (N < this_time) { + fclose (f); + fclose (t); + free (buffer); + throw ReadFileError (from, errno); + } + + N = fwrite (buffer, 1, this_time, t); + if (N < this_time) { + fclose (f); + fclose (t); + free (buffer); + throw WriteFileError (to, errno); + } + + progress (1 - float(remaining) / total); + remaining -= this_time; + } + + fclose (f); + fclose (t); + free (buffer); +} + #ifdef DCPOMATIC_VARIANT_SWAROOP /* Make up a key from the machine UUID */ diff --git a/src/lib/util.h b/src/lib/util.h index d90053cdc..c8dcb29d6 100644 --- a/src/lib/util.h +++ b/src/lib/util.h @@ -1,5 +1,5 @@ /* - Copyright (C) 2012-2016 Carl Hetherington <cth@carlh.net> + Copyright (C) 2012-2019 Carl Hetherington <cth@carlh.net> This file is part of DCP-o-matic. @@ -111,6 +111,7 @@ extern size_t utf8_strlen (std::string s); extern std::string day_of_week_to_string (boost::gregorian::greg_weekday d); extern void emit_subtitle_image (dcpomatic::ContentTimePeriod period, dcp::SubtitleImage sub, dcp::Size size, boost::shared_ptr<TextDecoder> decoder); extern bool show_jobs_on_console (bool progress); +extern void copy_in_bits (boost::filesystem::path from, boost::filesystem::path to, boost::function<void (float)>); #ifdef DCPOMATIC_VARIANT_SWAROOP extern boost::shared_ptr<dcp::CertificateChain> read_swaroop_chain (boost::filesystem::path path); extern void write_swaroop_chain (boost::shared_ptr<const dcp::CertificateChain> chain, boost::filesystem::path output); |
