Make it quicker to cancel an analyse subtitle job (#2486).
[dcpomatic.git] / src / lib / analyse_subtitles_job.cc
1 /*
2     Copyright (C) 2020-2021 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
22 #include "analyse_subtitles_job.h"
23 #include "bitmap_text.h"
24 #include "image.h"
25 #include "player.h"
26 #include "playlist.h"
27 #include "render_text.h"
28 #include "subtitle_analysis.h"
29 #include "text_content.h"
30 #include <iostream>
31
32 #include "i18n.h"
33
34
35 using std::make_shared;
36 using std::shared_ptr;
37 using std::string;
38 using std::weak_ptr;
39 #if BOOST_VERSION >= 106100
40 using namespace boost::placeholders;
41 #endif
42
43
44 AnalyseSubtitlesJob::AnalyseSubtitlesJob (shared_ptr<const Film> film, shared_ptr<Content> content)
45         : Job (film)
46         , _content (content)
47         , _path (_film->subtitle_analysis_path(content))
48 {
49 }
50
51
52 string
53 AnalyseSubtitlesJob::name () const
54 {
55         return _("Analysing subtitles");
56 }
57
58
59 string
60 AnalyseSubtitlesJob::json_name () const
61 {
62         return N_("analyse_subtitles");
63 }
64
65
66 void
67 AnalyseSubtitlesJob::run ()
68 {
69         auto playlist = make_shared<Playlist>();
70         auto content = _content.lock ();
71         DCPOMATIC_ASSERT (content);
72         playlist->add (_film, content);
73
74         auto player = make_shared<Player>(_film, playlist);
75         player->set_ignore_audio ();
76         player->set_fast ();
77         player->set_play_referenced ();
78         player->Text.connect (bind(&AnalyseSubtitlesJob::analyse, this, _1, _2));
79
80         set_progress_unknown ();
81
82         if (!content->text.empty()) {
83                 while (!player->pass ()) {
84                         boost::this_thread::interruption_point();
85                 }
86         }
87
88         SubtitleAnalysis analysis (_bounding_box, content->text.front()->x_offset(), content->text.front()->y_offset());
89         analysis.write (_path);
90
91         set_progress (1);
92         set_state (FINISHED_OK);
93 }
94
95
96 void
97 AnalyseSubtitlesJob::analyse(PlayerText const& text, TextType type)
98 {
99         if (type != TextType::OPEN_SUBTITLE) {
100                 return;
101         }
102
103         for (auto const& i: text.bitmap) {
104                 if (!_bounding_box) {
105                         _bounding_box = i.rectangle;
106                 } else {
107                         _bounding_box->extend (i.rectangle);
108                 }
109         }
110
111         if (text.string.empty()) {
112                 return;
113         }
114
115         /* We can provide dummy values for time and frame rate here as they are only used to calculate fades */
116         dcp::Size const frame = _film->frame_size();
117         std::vector<dcp::SubtitleStandard> override_standard;
118         if (_film->interop()) {
119                 /* Since the film is Interop there is only one way the vpositions in the subs can be interpreted
120                  * (we assume).
121                  */
122                 override_standard.push_back(dcp::SubtitleStandard::INTEROP);
123         } else {
124                 /* We're using the great new SMPTE standard, which means there are two different ways that vposition
125                  * could be interpreted; we will write SMPTE-2014 standard assets, but if the projection system uses
126                  * SMPTE 20{07,10} instead they won't be placed how we intended.  To show the user this, make the
127                  * bounding rectangle enclose both possibilities.
128                  */
129                 override_standard.push_back(dcp::SubtitleStandard::SMPTE_2007);
130                 override_standard.push_back(dcp::SubtitleStandard::SMPTE_2014);
131         }
132
133         for (auto standard: override_standard) {
134                 for (auto i: bounding_box(text.string, frame, standard)) {
135                         dcpomatic::Rect<double> rect (
136                                 double(i.x) / frame.width, double(i.y) / frame.height,
137                                 double(i.width) / frame.width, double(i.height) / frame.height
138                                 );
139                         if (!_bounding_box) {
140                                 _bounding_box = rect;
141                         } else {
142                                 _bounding_box->extend (rect);
143                         }
144                 }
145         }
146 }
147