55b4ef9b69aeadc4e9aaf87e397d9bd50aa10cd8
[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                 set_progress (1);
92                 set_state (FINISHED_OK);
93
94                 struct timeval finish;
95                 gettimeofday (&finish, 0);
96
97                 float fps = 0;
98                 if (finish.tv_sec != start.tv_sec) {
99                         fps = _encoder->frames_done() / (finish.tv_sec - start.tv_sec);
100                 }
101
102                 LOG_GENERAL (N_("Transcode job completed successfully: %1 fps"), fps);
103
104                 if (dynamic_pointer_cast<DCPEncoder>(_encoder)) {
105                         Analytics::instance()->successful_dcp_encode();
106                 }
107
108                 /* XXX: this shouldn't be here */
109                 if (_film->upload_after_make_dcp() && dynamic_pointer_cast<DCPEncoder>(_encoder)) {
110                         shared_ptr<Job> job (new UploadJob (_film));
111                         JobManager::instance()->add (job);
112                 }
113
114                 _encoder.reset ();
115
116         } catch (...) {
117                 _encoder.reset ();
118                 throw;
119         }
120 }
121
122 string
123 TranscodeJob::status () const
124 {
125         if (!_encoder) {
126                 return Job::status ();
127         }
128
129
130         char buffer[256];
131         if (finished() || _encoder->finishing()) {
132                 strncpy (buffer, Job::status().c_str(), 255);
133                 buffer[255] = '\0';
134         } else {
135                 snprintf (
136                         buffer, sizeof(buffer), "%s; %" PRId64 "/%" PRId64 " frames",
137                         Job::status().c_str(),
138                         _encoder->frames_done(),
139                         _film->length().frames_round (_film->video_frame_rate ())
140                         );
141
142                 optional<float> const fps = _encoder->current_rate ();
143                 if (fps) {
144                         char fps_buffer[64];
145                         /// TRANSLATORS: fps here is an abbreviation for frames per second
146                         snprintf (fps_buffer, sizeof(fps_buffer), _("; %.1f fps"), *fps);
147                         strncat (buffer, fps_buffer, strlen(buffer) - 1);
148                 }
149         }
150
151         return buffer;
152 }
153
154 /** @return Approximate remaining time in seconds */
155 int
156 TranscodeJob::remaining_time () const
157 {
158         /* _encoder might be destroyed by the job-runner thread */
159         shared_ptr<Encoder> e = _encoder;
160
161         if (!e || e->finishing()) {
162                 /* We aren't doing any actual encoding so just use the job's guess */
163                 return Job::remaining_time ();
164         }
165
166         /* We're encoding so guess based on the current encoding rate */
167
168         optional<float> fps = e->current_rate ();
169
170         if (!fps) {
171                 return 0;
172         }
173
174         /* Compute approximate proposed length here, as it's only here that we need it */
175         return (_film->length().frames_round(_film->video_frame_rate()) - e->frames_done()) / *fps;
176 }