2 Copyright (C) 2012-2021 Carl Hetherington <cth@carlh.net>
4 This file is part of DCP-o-matic.
6 DCP-o-matic is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
11 DCP-o-matic is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with DCP-o-matic. If not, see <http://www.gnu.org/licenses/>.
22 /** @file src/transcode_job.cc
23 * @brief A job which transcodes from one format to another.
27 #include "analytics.h"
28 #include "compose.hpp"
31 #include "dcp_encoder.h"
32 #include "dcpomatic_log.h"
34 #include "examine_content_job.h"
36 #include "job_manager.h"
38 #include "transcode_job.h"
39 #include "upload_job.h"
49 using std::make_shared;
50 using std::setprecision;
51 using std::shared_ptr;
53 using boost::optional;
54 using std::dynamic_pointer_cast;
57 /** @param film Film to use */
58 TranscodeJob::TranscodeJob (shared_ptr<const Film> film, ChangedBehaviour changed)
66 TranscodeJob::~TranscodeJob ()
73 TranscodeJob::name () const
75 return String::compose (_("Transcoding %1"), _film->name());
80 TranscodeJob::json_name () const
82 return N_("transcode");
87 TranscodeJob::set_encoder (shared_ptr<Encoder> e)
97 auto content = _film->content();
98 std::vector<shared_ptr<Content>> changed;
99 std::copy_if (content.begin(), content.end(), std::back_inserter(changed), [](shared_ptr<Content> c) { return c->changed(); });
101 if (!changed.empty()) {
103 case ChangedBehaviour::EXAMINE_THEN_STOP:
104 for (auto i: changed) {
105 JobManager::instance()->add(make_shared<ExamineContentJob>(_film, i));
108 set_message (_("Some files have been changed since they were added to the project.\n\nThese files will now be re-examined, so you may need to check their settings before trying again."));
109 set_error (_("Files have changed since they were added to the project."), _("Check their new settings, then try again."));
110 set_state (FINISHED_ERROR);
112 case ChangedBehaviour::STOP:
115 _("Files have changed since they were added to the project."),
116 variant::insert_dcpomatic(_("Open the project in %1, check the settings, then save it before trying again."))
118 set_state (FINISHED_ERROR);
121 LOG_GENERAL_NC (_("Some files have been changed since they were added to the project."));
126 LOG_GENERAL_NC (N_("Transcode job starting"));
128 DCPOMATIC_ASSERT (_encoder);
132 set_state (FINISHED_OK);
134 LOG_GENERAL(N_("Transcode job completed successfully: %1 fps"), dcp::locale_convert<string>(frames_per_second(), 2, true));
136 if (dynamic_pointer_cast<DCPEncoder>(_encoder)) {
138 Analytics::instance()->successful_dcp_encode();
139 } catch (FileError& e) {
140 LOG_WARNING (N_("Failed to write analytics (%1)"), e.what());
156 TranscodeJob::pause()
162 void TranscodeJob::resume()
170 TranscodeJob::status () const
173 return Job::status ();
176 if (finished() || _encoder->finishing()) {
177 return Job::status();
180 auto status = String::compose(_("%1; %2/%3 frames"), Job::status(), _encoder->frames_done(), _film->length().frames_round(_film->video_frame_rate()));
181 if (auto const fps = _encoder->current_rate()) {
182 /// TRANSLATORS: fps here is an abbreviation for frames per second
183 status += String::compose(_("; %1 fps"), dcp::locale_convert<string>(*fps, 1, true));
190 /** @return Approximate remaining time in seconds */
192 TranscodeJob::remaining_time () const
194 /* _encoder might be destroyed by the job-runner thread */
197 if (!e || e->finishing()) {
198 /* We aren't doing any actual encoding so just use the job's guess */
199 return Job::remaining_time ();
202 /* We're encoding so guess based on the current encoding rate */
204 auto fps = e->current_rate ();
210 /* Compute approximate proposed length here, as it's only here that we need it */
211 return (_film->length().frames_round(_film->video_frame_rate()) - e->frames_done()) / *fps;
216 TranscodeJob::frames_per_second() const
218 if (_finish_time != _start_time) {
219 return _encoder->frames_done() / (_finish_time - _start_time);