using std::list;
using std::string;
using std::cout;
+using std::exception;
using std::map;
using boost::shared_ptr;
using boost::optional;
, _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
_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 (
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) {
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);
+ }
}
}
/*
- 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.
#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;
/** 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;
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 */
/*
- 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.
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);
/* Remove warning */
return boost::filesystem::path("/");
}
+
+void
+make_random_file (boost::filesystem::path path, size_t size)
+{
+ size_t const chunk = 128 * 1024;
+ uint8_t* buffer = static_cast<uint8_t*> (malloc(chunk));
+ BOOST_REQUIRE (buffer);
+ FILE* r = fopen("/dev/urandom", "rb");
+ BOOST_REQUIRE (r);
+ FILE* t = fopen_boost(path, "wb");
+ BOOST_REQUIRE (t);
+ while (size) {
+ size_t this_time = min (size, chunk);
+ size_t N = fread (buffer, 1, this_time, r);
+ BOOST_REQUIRE (N == this_time);
+ N = fwrite (buffer, 1, this_time, t);
+ BOOST_REQUIRE (N == this_time);
+ size -= this_time;
+ }
+ fclose (t);
+ fclose (r);
+ free (buffer);
+}
boost::filesystem::path dcp_file (boost::shared_ptr<const Film> film, std::string prefix);
void check_one_frame (boost::filesystem::path dcp, int64_t index, boost::filesystem::path ref);
extern boost::filesystem::path subtitle_file (boost::shared_ptr<Film> film);
+extern void make_random_file (boost::filesystem::path path, size_t size);
/*
- 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.
#include "lib/util.h"
#include "lib/cross.h"
#include "lib/exceptions.h"
+#include "test.h"
#include <dcp/certificate_chain.h>
#include <boost/test/unit_test.hpp>
+#include <boost/bind.hpp>
using std::string;
using std::vector;
+using std::list;
using boost::shared_ptr;
using namespace dcpomatic;
BOOST_CHECK_EQUAL ("hello_world", careful_string_filter("héllo_wörld"));
BOOST_CHECK_EQUAL ("hello_world_a", careful_string_filter("héllo_wörld_à"));
}
+
+static list<float> progress_values;
+
+static void
+progress (float p)
+{
+ progress_values.push_back (p);
+}
+
+BOOST_AUTO_TEST_CASE (copy_in_bits_test)
+{
+ for (int i = 0; i < 32; ++i) {
+ make_random_file ("build/test/random.dat", rand() % (256 * 1024 * 1024));
+
+ progress_values.clear ();
+ copy_in_bits ("build/test/random.dat", "build/test/random.dat2", boost::bind(&progress, _1));
+ BOOST_CHECK (!progress_values.empty());
+
+ check_file ("build/test/random.dat", "build/test/random.dat2");
+ }
+}