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