Destory TranscodeJob earlier, hopefully to fix #1570.
[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::dynamic_pointer_cast;
46
47 /** @param film Film to use */
48 TranscodeJob::TranscodeJob (shared_ptr<const Film> film)
49         : Job (film)
50 {
51
52 }
53
54 TranscodeJob::~TranscodeJob ()
55 {
56         /* We have to stop the job thread here as we're about to start tearing down
57            the Encoder, which is bad news if the job thread is still feeding it data.
58         */
59         stop_thread ();
60 }
61
62 string
63 TranscodeJob::name () const
64 {
65         return String::compose (_("Transcoding %1"), _film->name());
66 }
67
68 string
69 TranscodeJob::json_name () const
70 {
71         return N_("transcode");
72 }
73
74 void
75 TranscodeJob::set_encoder (shared_ptr<Encoder> e)
76 {
77         _encoder = e;
78 }
79
80 void
81 TranscodeJob::run ()
82 {
83         try {
84                 struct timeval start;
85                 gettimeofday (&start, 0);
86                 LOG_GENERAL_NC (N_("Transcode job starting"));
87
88                 DCPOMATIC_ASSERT (_encoder);
89                 _encoder->go ();
90                 set_progress (1);
91                 set_state (FINISHED_OK);
92
93                 struct timeval finish;
94                 gettimeofday (&finish, 0);
95
96                 float fps = 0;
97                 if (finish.tv_sec != start.tv_sec) {
98                         fps = _encoder->frames_done() / (finish.tv_sec - start.tv_sec);
99                 }
100
101                 LOG_GENERAL (N_("Transcode job completed successfully: %1 fps"), fps);
102
103                 if (dynamic_pointer_cast<DCPEncoder>(_encoder)) {
104                         Analytics::instance()->successful_dcp_encode();
105                 }
106
107                 /* XXX: this shouldn't be here */
108                 if (_film->upload_after_make_dcp() && dynamic_pointer_cast<DCPEncoder>(_encoder)) {
109                         shared_ptr<Job> job (new UploadJob (_film));
110                         JobManager::instance()->add (job);
111                 }
112
113                 _encoder.reset ();
114
115         } catch (...) {
116                 _encoder.reset ();
117                 throw;
118         }
119 }
120
121 string
122 TranscodeJob::status () const
123 {
124         if (!_encoder) {
125                 return Job::status ();
126         }
127
128
129         char buffer[256];
130         if (finished() || _encoder->finishing()) {
131                 strncpy (buffer, Job::status().c_str(), 255);
132                 buffer[255] = '\0';
133         } else {
134                 snprintf (
135                         buffer, sizeof(buffer), "%s; %" PRId64 "/%" PRId64 " frames",
136                         Job::status().c_str(),
137                         _encoder->frames_done(),
138                         _film->length().frames_round (_film->video_frame_rate ())
139                         );
140
141                 float const fps = _encoder->current_rate ();
142                 if (fps) {
143                         char fps_buffer[64];
144                         /// TRANSLATORS: fps here is an abbreviation for frames per second
145                         snprintf (fps_buffer, sizeof(fps_buffer), _("; %.1f fps"), fps);
146                         strncat (buffer, fps_buffer, strlen(buffer) - 1);
147                 }
148         }
149
150         return buffer;
151 }
152
153 /** @return Approximate remaining time in seconds */
154 int
155 TranscodeJob::remaining_time () const
156 {
157         /* _encoder might be destroyed by the job-runner thread */
158         shared_ptr<Encoder> e = _encoder;
159
160         if (!e || e->finishing()) {
161                 /* We aren't doing any actual encoding so just use the job's guess */
162                 return Job::remaining_time ();
163         }
164
165         /* We're encoding so guess based on the current encoding rate */
166
167         float fps = e->current_rate ();
168
169         if (fps == 0) {
170                 return 0;
171         }
172
173         /* Compute approximate proposed length here, as it's only here that we need it */
174         return (_film->length().frames_round (_film->video_frame_rate ()) - e->frames_done()) / fps;
175 }