Accessor for ClosedCaptionsDialog.
[dcpomatic.git] / src / lib / transcode_job.cc
1 /*
2     Copyright (C) 2012-2019 Carl Hetherington <cth@carlh.net>
3
4     This file is part of DCP-o-matic.
5
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.
10
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.
15
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/>.
18
19 */
20
21 /** @file src/transcode_job.cc
22  *  @brief A job which transcodes from one format to another.
23  */
24
25 #include "transcode_job.h"
26 #include "dcp_encoder.h"
27 #include "upload_job.h"
28 #include "job_manager.h"
29 #include "film.h"
30 #include "encoder.h"
31 #include "log.h"
32 #include "dcpomatic_log.h"
33 #include "compose.hpp"
34 #include "analytics.h"
35 #include <iostream>
36 #include <iomanip>
37
38 #include "i18n.h"
39
40 using std::string;
41 using std::fixed;
42 using std::setprecision;
43 using std::cout;
44 using boost::shared_ptr;
45 using boost::optional;
46 using boost::dynamic_pointer_cast;
47
48 /** @param film Film to use */
49 TranscodeJob::TranscodeJob (shared_ptr<const Film> film)
50         : Job (film)
51 {
52
53 }
54
55 TranscodeJob::~TranscodeJob ()
56 {
57         /* We have to stop the job thread here as we're about to start tearing down
58            the Encoder, which is bad news if the job thread is still feeding it data.
59         */
60         stop_thread ();
61 }
62
63 string
64 TranscodeJob::name () const
65 {
66         return String::compose (_("Transcoding %1"), _film->name());
67 }
68
69 string
70 TranscodeJob::json_name () const
71 {
72         return N_("transcode");
73 }
74
75 void
76 TranscodeJob::set_encoder (shared_ptr<Encoder> e)
77 {
78         _encoder = e;
79 }
80
81 void
82 TranscodeJob::run ()
83 {
84         try {
85                 struct timeval start;
86                 gettimeofday (&start, 0);
87                 LOG_GENERAL_NC (N_("Transcode job starting"));
88
89                 DCPOMATIC_ASSERT (_encoder);
90                 _encoder->go ();
91
92                 struct timeval finish;
93                 gettimeofday (&finish, 0);
94
95                 float fps = 0;
96                 if (finish.tv_sec != start.tv_sec) {
97                         fps = _encoder->frames_done() / (finish.tv_sec - start.tv_sec);
98                 }
99
100                 LOG_GENERAL (N_("Transcode job completed successfully: %1 fps"), fps);
101
102                 if (dynamic_pointer_cast<DCPEncoder>(_encoder)) {
103                         Analytics::instance()->successful_dcp_encode();
104                 }
105
106                 /* XXX: this shouldn't be here */
107                 if (_film->upload_after_make_dcp() && dynamic_pointer_cast<DCPEncoder>(_encoder)) {
108                         shared_ptr<Job> job (new UploadJob (_film));
109                         JobManager::instance()->add (job);
110                 }
111
112                 _encoder.reset ();
113
114                 set_progress (1);
115                 set_state (FINISHED_OK);
116
117         } catch (...) {
118                 _encoder.reset ();
119                 throw;
120         }
121 }
122
123 string
124 TranscodeJob::status () const
125 {
126         if (!_encoder) {
127                 return Job::status ();
128         }
129
130
131         char buffer[256];
132         if (finished() || _encoder->finishing()) {
133                 strncpy (buffer, Job::status().c_str(), 255);
134                 buffer[255] = '\0';
135         } else {
136                 snprintf (
137                         buffer, sizeof(buffer), "%s; %" PRId64 "/%" PRId64 " frames",
138                         Job::status().c_str(),
139                         _encoder->frames_done(),
140                         _film->length().frames_round (_film->video_frame_rate ())
141                         );
142
143                 optional<float> const fps = _encoder->current_rate ();
144                 if (fps) {
145                         char fps_buffer[64];
146                         /// TRANSLATORS: fps here is an abbreviation for frames per second
147                         snprintf (fps_buffer, sizeof(fps_buffer), _("; %.1f fps"), *fps);
148                         strncat (buffer, fps_buffer, strlen(buffer) - 1);
149                 }
150         }
151
152         return buffer;
153 }
154
155 /** @return Approximate remaining time in seconds */
156 int
157 TranscodeJob::remaining_time () const
158 {
159         /* _encoder might be destroyed by the job-runner thread */
160         shared_ptr<Encoder> e = _encoder;
161
162         if (!e || e->finishing()) {
163                 /* We aren't doing any actual encoding so just use the job's guess */
164                 return Job::remaining_time ();
165         }
166
167         /* We're encoding so guess based on the current encoding rate */
168
169         optional<float> fps = e->current_rate ();
170
171         if (!fps) {
172                 return 0;
173         }
174
175         /* Compute approximate proposed length here, as it's only here that we need it */
176         return (_film->length().frames_round(_film->video_frame_rate()) - e->frames_done()) / *fps;
177 }