1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
|
/*
Copyright (C) 2018-2021 Carl Hetherington <cth@carlh.net>
This file is part of DCP-o-matic.
DCP-o-matic is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
DCP-o-matic is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with DCP-o-matic. If not, see <http://www.gnu.org/licenses/>.
*/
#include "verify_dcp_dialog.h"
#include "wx_util.h"
#include "lib/verify_dcp_job.h"
#include "lib/warnings.h"
#include <dcp/verify.h>
#include <dcp/raw_convert.h>
DCPOMATIC_DISABLE_WARNINGS
#include <wx/richtext/richtextctrl.h>
#include <wx/notebook.h>
DCPOMATIC_ENABLE_WARNINGS
using std::list;
using std::map;
using std::shared_ptr;
using std::string;
VerifyDCPDialog::VerifyDCPDialog (wxWindow* parent, shared_ptr<VerifyDCPJob> job)
: wxDialog (parent, wxID_ANY, _("DCP verification"), wxDefaultPosition, {600, 400})
{
auto sizer = new wxBoxSizer (wxVERTICAL);
auto notebook = new wxNotebook (this, wxID_ANY);
sizer->Add (notebook, 1, wxEXPAND | wxALL, DCPOMATIC_DIALOG_BORDER);
map<dcp::VerificationNote::Type, wxRichTextCtrl*> pages;
pages[dcp::VerificationNote::Type::ERROR] = new wxRichTextCtrl (notebook, wxID_ANY, wxEmptyString, wxDefaultPosition, {400, 300}, wxRE_READONLY);
notebook->AddPage (pages[dcp::VerificationNote::Type::ERROR], _("Errors"));
pages[dcp::VerificationNote::Type::BV21_ERROR] = new wxRichTextCtrl (notebook, wxID_ANY, wxEmptyString, wxDefaultPosition, {400, 300}, wxRE_READONLY);
notebook->AddPage (pages[dcp::VerificationNote::Type::BV21_ERROR], _("SMPTE Bv2.1 errors"));
pages[dcp::VerificationNote::Type::WARNING] = new wxRichTextCtrl (notebook, wxID_ANY, wxEmptyString, wxDefaultPosition, {400, 300}, wxRE_READONLY);
notebook->AddPage (pages[dcp::VerificationNote::Type::WARNING], _("Warnings"));
auto summary = new wxStaticText (this, wxID_ANY, wxT(""));
sizer->Add (summary, 0, wxALL, DCPOMATIC_DIALOG_BORDER);
auto buttons = CreateStdDialogButtonSizer (0);
sizer->Add (CreateSeparatedSizer(buttons), wxSizerFlags().Expand().DoubleBorder());
buttons->SetAffirmativeButton (new wxButton (this, wxID_OK));
buttons->Realize ();
SetSizer (sizer);
sizer->Layout ();
sizer->SetSizeHints (this);
for (auto const& i: pages) {
i.second->GetCaret()->Hide();
}
if (job->finished_ok() && job->notes().empty()) {
summary->SetLabel (_("DCP validates OK."));
return;
}
map<dcp::VerificationNote::Type, int> counts;
counts[dcp::VerificationNote::Type::WARNING] = 0;
counts[dcp::VerificationNote::Type::BV21_ERROR] = 0;
counts[dcp::VerificationNote::Type::ERROR] = 0;
auto add_bullet = [&pages](dcp::VerificationNote::Type type, wxString message) {
pages[type]->BeginStandardBullet(N_("standard/diamond"), 1, 50);
pages[type]->WriteText (message);
pages[type]->Newline ();
pages[type]->EndStandardBullet ();
};
auto add = [&counts, &add_bullet](dcp::VerificationNote note, wxString message) {
if (note.note()) {
message.Replace("%n", std_to_wx(note.note().get()));
}
if (note.file()) {
message.Replace("%f", std_to_wx(note.file()->filename().string()));
}
if (note.line()) {
message.Replace("%l", std_to_wx(dcp::raw_convert<string>(note.line().get())));
}
add_bullet (note.type(), message);
counts[note.type()]++;
};
if (job->finished_in_error() && job->error_summary() != "") {
/* We have an error that did not come from dcp::verify */
add_bullet (dcp::VerificationNote::Type::ERROR, std_to_wx(job->error_summary()));
}
for (auto i: job->notes()) {
switch (i.code()) {
case dcp::VerificationNote::Code::FAILED_READ:
add (i, std_to_wx(*i.note()));
break;
case dcp::VerificationNote::Code::MISMATCHED_CPL_HASHES:
add(i, _("The hash of the CPL %n in the PKL does not agree with the CPL file. This probably means that the CPL file is corrupt."));
break;
case dcp::VerificationNote::Code::INVALID_PICTURE_FRAME_RATE:
add(i, _("The picture in a reel has a frame rate of %n, which is not valid."));
break;
case dcp::VerificationNote::Code::INCORRECT_PICTURE_HASH:
add(i, _("The hash of the picture asset %f does not agree with the PKL file. This probably means that the asset file is corrupt."));
break;
case dcp::VerificationNote::Code::MISMATCHED_PICTURE_HASHES:
add(i, _("The PKL and CPL hashes disagree for picture asset %f."));
break;
case dcp::VerificationNote::Code::INCORRECT_SOUND_HASH:
add(i, _("The hash of the sound asset %f does not agree with the PKL file. This probably means that the asset file is corrupt."));
break;
case dcp::VerificationNote::Code::MISMATCHED_SOUND_HASHES:
add(i, _("The PKL and CPL hashes disagree for sound asset %f."));
break;
case dcp::VerificationNote::Code::EMPTY_ASSET_PATH:
add(i, _("An asset has an empty path in the ASSETMAP."));
break;
case dcp::VerificationNote::Code::MISSING_ASSET:
add(i, _("The asset %f is missing."));
break;
case dcp::VerificationNote::Code::MISMATCHED_STANDARD:
add(i, _("Parts of the DCP are written according to the Interop standard and parts according to SMPTE."));
break;
case dcp::VerificationNote::Code::INVALID_XML:
if (i.line()) {
add(i, _("The XML in %f is malformed on line %l (%n)."));
} else {
add(i, _("The XML in %f is malformed (%n)."));
}
break;
case dcp::VerificationNote::Code::MISSING_ASSETMAP:
add(i, _("No ASSETMAP or ASSETMAP.xml file was found."));
break;
case dcp::VerificationNote::Code::INVALID_INTRINSIC_DURATION:
add(i, _("The asset %n has an instrinsic duration of less than 1 second, which is invalid."));
break;
case dcp::VerificationNote::Code::INVALID_DURATION:
add(i, _("The asset %n has a duration of less than 1 second, which is invalid."));
break;
case dcp::VerificationNote::Code::INVALID_PICTURE_FRAME_SIZE_IN_BYTES:
add(i, _("At least one frame of the video asset %f is over the limit of 250Mbit/s."));
break;
case dcp::VerificationNote::Code::NEARLY_INVALID_PICTURE_FRAME_SIZE_IN_BYTES:
add(i, _("At least one frame of the video asset %f is close to the limit of 250MBit/s."));
break;
case dcp::VerificationNote::Code::EXTERNAL_ASSET:
add(i, _("This DCP refers to at the asset %n in another DCP (and perhaps others), so it is a \"version file\" (VF)"));
break;
case dcp::VerificationNote::Code::INVALID_STANDARD:
add(i, _("This DCP uses the Interop standard, but it should be made with SMPTE."));
break;
case dcp::VerificationNote::Code::INVALID_LANGUAGE:
add(i, _("The invalid language tag %n is used."));
break;
case dcp::VerificationNote::Code::INVALID_PICTURE_SIZE_IN_PIXELS:
add(i, _("The video asset %f uses the invalid image size %n."));
break;
case dcp::VerificationNote::Code::INVALID_PICTURE_FRAME_RATE_FOR_2K:
add(i, _("The video asset %f uses the invalid frame rate %n."));
break;
case dcp::VerificationNote::Code::INVALID_PICTURE_FRAME_RATE_FOR_4K:
add(i, _("The video asset %f uses the frame rate %n which is invalid for 4K video."));
break;
case dcp::VerificationNote::Code::INVALID_PICTURE_ASSET_RESOLUTION_FOR_3D:
add(i, _("The video asset %f uses the frame rate %n which is invalid for 3D video."));
break;
case dcp::VerificationNote::Code::INVALID_CLOSED_CAPTION_XML_SIZE_IN_BYTES:
add(i, _("The XML in the closed caption asset %f takes up %n bytes which is over the 256KB limit."));
break;
case dcp::VerificationNote::Code::INVALID_TIMED_TEXT_SIZE_IN_BYTES:
add(i, _("The timed text asset %f takes up %n bytes which is over the 115MB limit."));
break;
case dcp::VerificationNote::Code::INVALID_TIMED_TEXT_FONT_SIZE_IN_BYTES:
add(i, _("The fonts in the timed text asset %f take up %n bytes which is over the 10MB limit."));
break;
case dcp::VerificationNote::Code::MISSING_SUBTITLE_LANGUAGE:
add(i, _("The subtitle asset %f contains no <Language> tag."));
break;
case dcp::VerificationNote::Code::MISMATCHED_SUBTITLE_LANGUAGES:
add(i, _("Not all subtitle assets specify the same <Language> tag."));
break;
case dcp::VerificationNote::Code::MISSING_SUBTITLE_START_TIME:
add(i, _("The subtitle asset %f contains no <StartTime> tag."));
break;
case dcp::VerificationNote::Code::INVALID_SUBTITLE_START_TIME:
add(i, _("The subtitle asset %f has a <StartTime> which is not zero."));
break;
case dcp::VerificationNote::Code::INVALID_SUBTITLE_FIRST_TEXT_TIME:
add(i, _("The first subtitle or closed caption happens before 4s into the first reel."));
break;
case dcp::VerificationNote::Code::INVALID_SUBTITLE_DURATION:
add(i, _("At least one subtitle lasts less than 15 frames."));
break;
case dcp::VerificationNote::Code::INVALID_SUBTITLE_SPACING:
add(i, _("At least one pair of subtitles is separated by less than 2 frames."));
break;
case dcp::VerificationNote::Code::INVALID_SUBTITLE_LINE_COUNT:
add(i, _("There are more than 3 subtitle lines in at least one place."));
break;
case dcp::VerificationNote::Code::NEARLY_INVALID_SUBTITLE_LINE_LENGTH:
add(i, _("There are more than 52 characters in at least one subtitle line."));
break;
case dcp::VerificationNote::Code::INVALID_SUBTITLE_LINE_LENGTH:
add(i, _("There are more than 79 characters in at least one subtitle line."));
break;
case dcp::VerificationNote::Code::INVALID_CLOSED_CAPTION_LINE_COUNT:
add(i, _("There are more than 3 closed caption lines in at least one place."));
break;
case dcp::VerificationNote::Code::INVALID_CLOSED_CAPTION_LINE_LENGTH:
add(i, _("There are more than 32 characters in at least one closed caption line."));
break;
case dcp::VerificationNote::Code::INVALID_SOUND_FRAME_RATE:
add(i, _("The sound asset %f has an invalid frame rate of %n."));
break;
case dcp::VerificationNote::Code::MISSING_CPL_ANNOTATION_TEXT:
add(i, _("The CPL %n has no <AnnotationText> tag."));
break;
case dcp::VerificationNote::Code::MISMATCHED_CPL_ANNOTATION_TEXT:
add(i, _("The CPL %n has an <AnnotationText> which is not the same as its <ContentTitleText>."));
break;
case dcp::VerificationNote::Code::MISMATCHED_ASSET_DURATION:
add(i, _("At least one asset in a reel does not have the same duration as the others."));
break;
case dcp::VerificationNote::Code::MISSING_MAIN_SUBTITLE_FROM_SOME_REELS:
add(i, _("The DCP has subtitles but at least one reel has no subtitle asset."));
break;
case dcp::VerificationNote::Code::MISMATCHED_CLOSED_CAPTION_ASSET_COUNTS:
add(i, _("The DCP has closed captions but not every reel has the same number of closed caption assets."));
break;
case dcp::VerificationNote::Code::MISSING_SUBTITLE_ENTRY_POINT:
add(i, _("The subtitle asset %n has no <EntryPoint> tag."));
break;
case dcp::VerificationNote::Code::INCORRECT_SUBTITLE_ENTRY_POINT:
add(i, _("Subtitle asset %n has a non-zero <EntryPoint>."));
break;
case dcp::VerificationNote::Code::MISSING_CLOSED_CAPTION_ENTRY_POINT:
add(i, _("The closed caption asset %n has no <EntryPoint> tag."));
break;
case dcp::VerificationNote::Code::INCORRECT_CLOSED_CAPTION_ENTRY_POINT:
add(i, _("Closed caption asset %n has a non-zero <EntryPoint>."));
break;
case dcp::VerificationNote::Code::MISSING_HASH:
add(i, _("The asset %n has no <Hash> in the CPL."));
break;
case dcp::VerificationNote::Code::MISSING_FFEC_IN_FEATURE:
add(i, _("The DCP is a feature but has no FFEC (first frame of end credits) marker."));
break;
case dcp::VerificationNote::Code::MISSING_FFMC_IN_FEATURE:
add(i, _("The DCP is a feature but has no FFMC (first frame of moving credits) marker."));
break;
case dcp::VerificationNote::Code::MISSING_FFOC:
add(i, _("The DCP has no FFOC (first frame of content) marker."));
break;
case dcp::VerificationNote::Code::MISSING_LFOC:
add(i, _("The DCP has no LFOC (last frame of content) marker."));
break;
case dcp::VerificationNote::Code::INCORRECT_FFOC:
add(i, _("The DCP has a FFOC of %n instead of 1."));
break;
case dcp::VerificationNote::Code::INCORRECT_LFOC:
add(i, _("The DCP has a LFOC of %n instead of the reel duration minus one."));
break;
case dcp::VerificationNote::Code::MISSING_CPL_METADATA:
add(i, _("The CPL %n has no CPL metadata tag."));
break;
case dcp::VerificationNote::Code::MISSING_CPL_METADATA_VERSION_NUMBER:
add(i, _("The CPL %n has no CPL metadata version number tag."));
break;
case dcp::VerificationNote::Code::MISSING_EXTENSION_METADATA:
add(i, _("The CPL %n has no CPL extension metadata tag."));
break;
case dcp::VerificationNote::Code::INVALID_EXTENSION_METADATA:
add(i, _("The CPL %f has an invalid CPL extension metadata tag (%n)"));
break;
case dcp::VerificationNote::Code::UNSIGNED_CPL_WITH_ENCRYPTED_CONTENT:
add(i, _("The CPL %n has encrypted content but is not signed."));
break;
case dcp::VerificationNote::Code::UNSIGNED_PKL_WITH_ENCRYPTED_CONTENT:
add(i, _("The PKL %n has encrypted content but is not signed."));
break;
case dcp::VerificationNote::Code::MISMATCHED_PKL_ANNOTATION_TEXT_WITH_CPL:
add(i, _("The PKL %n has an <AnnotationText> which does not match its CPL's <ContentTitleText>."));
break;
case dcp::VerificationNote::Code::PARTIALLY_ENCRYPTED:
add(i, _("The DCP has encrypted content, but not all its assets are encrypted."));
break;
}
}
wxString summary_text;
if (counts[dcp::VerificationNote::Type::ERROR] == 1) {
/// TRANSLATORS: this will be used at the start of a string like "1 error, 2 Bv2.1 errors and 3 warnings."
summary_text = _("1 error, ");
} else {
/// TRANSLATORS: this will be used at the start of a string like "1 error, 2 Bv2.1 errors and 3 warnings."
summary_text = wxString::Format("%d errors, ", counts[dcp::VerificationNote::Type::ERROR]);
}
if (counts[dcp::VerificationNote::Type::BV21_ERROR] == 1) {
/// TRANSLATORS: this will be used in the middle of a string like "1 error, 2 Bv2.1 errors and 3 warnings."
summary_text += _("1 Bv2.1 error, ");
} else {
/// TRANSLATORS: this will be used in the middle of a string like "1 error, 2 Bv2.1 errors and 3 warnings."
summary_text += wxString::Format("%d Bv2.1 errors, ", counts[dcp::VerificationNote::Type::BV21_ERROR]);
}
if (counts[dcp::VerificationNote::Type::WARNING] == 1) {
/// TRANSLATORS: this will be used at the end of a string like "1 error, 2 Bv2.1 errors and 3 warnings."
summary_text += _("and 1 warning.");
} else {
/// TRANSLATORS: this will be used at the end of a string like "1 error, 2 Bv2.1 errors and 3 warnings."
summary_text += wxString::Format("and %d warnings.", counts[dcp::VerificationNote::Type::WARNING]);
}
summary->SetLabel(summary_text);
if (counts[dcp::VerificationNote::Type::ERROR] == 0) {
add_bullet (dcp::VerificationNote::Type::ERROR, _("No errors found."));
}
if (counts[dcp::VerificationNote::Type::BV21_ERROR] == 0) {
add_bullet (dcp::VerificationNote::Type::BV21_ERROR, _("No SMPTE Bv2.1 errors found."));
}
if (counts[dcp::VerificationNote::Type::WARNING] == 0) {
add_bullet (dcp::VerificationNote::Type::WARNING, _("No warnings found."));
}
}
|