2 Copyright (C) 2021 Carl Hetherington <cth@carlh.net>
4 This file is part of libdcp.
6 libdcp 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.
11 libdcp 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.
16 You should have received a copy of the GNU General Public License
17 along with libdcp. If not, see <http://www.gnu.org/licenses/>.
19 In addition, as a special exception, the copyright holders give
20 permission to link the code of portions of this program with the
21 OpenSSL library under certain conditions as described in each
22 individual source file, and distribute linked combinations
25 You must obey the GNU General Public License in all respects
26 for all of the code used other than OpenSSL. If you modify
27 file(s) with this exception, you may extend this exception to your
28 version of the file(s), but you are not obligated to do so. If you
29 do not wish to do so, delete this exception statement from your
30 version. If you delete this exception statement from all source
31 files in the program, then also delete it here.
35 /** @file src/verify_j2k.cc
36 * @brief Verification that JPEG2000 files meet requirements
40 #include "compose.hpp"
42 #include "raw_convert.h"
44 #include "verify_j2k.h"
49 using std::shared_ptr;
51 using std::runtime_error;
54 using boost::optional;
55 using dcp::raw_convert;
58 class InvalidCodestream : public runtime_error
61 InvalidCodestream (string note)
68 dcp::verify_j2k (shared_ptr<const Data> j2k, vector<VerificationNote>& notes)
71 auto ptr = j2k->data();
72 auto end = ptr + j2k->size();
74 map<string, uint8_t> markers = {
89 auto marker_name_from_id = [&markers](uint8_t b) -> optional<string> {
90 for (auto const& i: markers) {
98 auto require_marker = [&](string name) {
99 if (ptr == end || *ptr != 0xff) {
100 throw InvalidCodestream ("missing marker start byte");
103 if (ptr == end || *ptr != markers[name]) {
104 throw InvalidCodestream ("missing_marker " + name);
111 throw InvalidCodestream ("unexpected end of file");
116 auto get_16 = [&]() {
117 if (ptr >= (end - 1)) {
118 throw InvalidCodestream ("unexpected end of file");
120 auto const a = *ptr++;
121 auto const b = *ptr++;
125 auto get_32 = [&]() -> uint32_t {
126 if (ptr >= (end - 3)) {
127 throw InvalidCodestream ("unexpected end of file");
129 auto const a = *ptr++;
130 auto const b = *ptr++;
131 auto const c = *ptr++;
132 auto const d = *ptr++;
133 return d | (c << 8) | (b << 16) | (a << 24);
136 auto require_8 = [&](uint8_t value, string note) {
139 throw InvalidCodestream (String::compose(note, v));
143 auto require_16 = [&](uint16_t value, string note) {
146 throw InvalidCodestream (String::compose(note, v));
150 auto require_32 = [&](uint32_t value, string note) {
153 throw InvalidCodestream (String::compose(note, v));
157 require_marker ("SOC");
158 require_marker ("SIZ");
159 auto L_siz = get_16();
161 throw InvalidCodestream("unexpected SIZ size " + raw_convert<string>(L_siz));
164 get_16(); // CA: codestream capabilities
165 auto const image_width = get_32();
166 auto const image_height = get_32();
167 auto const fourk = image_width > 2048;
168 require_32 (0, "invalid top-left image x coordinate %1");
169 require_32 (0, "invalid top-left image y coordinate %1");
170 auto const tile_width = get_32();
171 auto const tile_height = get_32();
172 if (tile_width != image_width || tile_height != image_height) {
173 notes.push_back ({ VerificationNote::Type::BV21_ERROR, VerificationNote::Code::INVALID_JPEG2000_TILE_SIZE });
175 require_32 (0, "invalid tile anchor x coordinate %1");
176 require_32 (0, "invalid tile anchor y coordinate %1");
177 require_16 (3, "invalid component count %1");
178 for (auto i = 0; i < 3; ++i) {
179 require_8 (12 - 1, "invalid bit depth %1");
180 require_8 (1, "invalid horizontal subsampling factor %1");
181 require_8 (1, "invalid vertical subsampling factor %1");
186 /** number of POC markers in the main header */
187 auto num_POC_in_main = 0;
188 /** number of POC markers after the main header */
189 auto num_POC_after_main = 0;
190 bool main_header_finished = false;
195 require_8(0xff, "missing marker start byte");
196 auto marker_id = get_8();
197 auto marker_name = marker_name_from_id (marker_id);
200 snprintf (buffer, 16, "%2x", marker_id);
201 throw InvalidCodestream(String::compose("unknown marker %1", buffer));
202 } else if (*marker_name == "SOT") {
203 require_16(10, "invalid SOT size %1");
204 get_16(); // tile index
205 get_32(); // tile part length
206 get_8(); // tile part index
207 auto tile_parts = get_8();
208 if (!fourk && tile_parts != 3) {
209 notes.push_back ({ VerificationNote::Type::BV21_ERROR, VerificationNote::Code::INVALID_JPEG2000_TILE_PARTS_FOR_2K, raw_convert<string>(tile_parts) });
211 if (fourk && tile_parts != 6) {
212 notes.push_back ({ VerificationNote::Type::BV21_ERROR, VerificationNote::Code::INVALID_JPEG2000_TILE_PARTS_FOR_4K, raw_convert<string>(tile_parts) });
214 main_header_finished = true;
215 } else if (*marker_name == "SOD") {
216 while (ptr < (end - 1) && (ptr[0] != 0xff || ptr[1] < 0x90)) {
219 } else if (*marker_name == "SIZ") {
220 throw InvalidCodestream ("duplicate SIZ marker");
221 } else if (*marker_name == "COD") {
224 require_8(1, "invalid coding style %1");
225 require_8(4, "invalid progression order %1"); // CPRL
226 require_16(1, "invalid quality layers count %1");
227 require_8(1, "invalid multi-component transform flag %1");
228 require_8(fourk ? 6 : 5, "invalid number of transform levels %1");
229 auto log_code_block_width = get_8();
230 if (log_code_block_width != 3) {
231 notes.push_back ({ VerificationNote::Type::BV21_ERROR, VerificationNote::Code::INVALID_JPEG2000_CODE_BLOCK_WIDTH, raw_convert<string>(4 * (2 << log_code_block_width)) });
233 auto log_code_block_height = get_8();
234 if (log_code_block_height != 3) {
235 notes.push_back ({ VerificationNote::Type::BV21_ERROR, VerificationNote::Code::INVALID_JPEG2000_CODE_BLOCK_HEIGHT, raw_convert<string>(4 * (2 << log_code_block_height)) });
237 require_8(0, "invalid mode variations");
238 require_8(0, "invalid wavelet transform type %1"); // 9/7 irreversible
239 require_8(0x77, "invalid precinct size %1");
240 require_8(0x88, "invalid precinct size %1");
241 require_8(0x88, "invalid precinct size %1");
242 require_8(0x88, "invalid precinct size %1");
243 require_8(0x88, "invalid precinct size %1");
244 require_8(0x88, "invalid precinct size %1");
246 require_8(0x88, "invalid precinct size %1");
248 } else if (*marker_name == "QCD") {
250 auto const L_qcd = get_16();
251 auto quantization_style = get_8();
252 int guard_bits = (quantization_style >> 5) & 7;
253 if (fourk && guard_bits != 2) {
254 notes.push_back ({ VerificationNote::Type::BV21_ERROR, VerificationNote::Code::INVALID_JPEG2000_GUARD_BITS_FOR_4K, raw_convert<string>(guard_bits) });
256 if (!fourk && guard_bits != 1) {
257 notes.push_back ({ VerificationNote::Type::BV21_ERROR, VerificationNote::Code::INVALID_JPEG2000_GUARD_BITS_FOR_2K, raw_convert<string>(guard_bits) });
260 } else if (*marker_name == "COC") {
262 auto const coc_component_number = get_8();
263 /* I don't know if this is really a requirement, but it seems to make sense that there should only
264 * be components 0, 1 and 2. DoM bug #2395 is about a DCP with COC component number 1 which seems
265 * like it should be OK.
267 if (coc_component_number > 2) {
268 throw InvalidCodestream(String::compose("invalid COC component number %1", coc_component_number));
270 require_8(1, "invalid coding style %1");
271 require_8(5, "invalid number of transform levels %1");
272 require_8(3, "invalid code block width exponent %1");
273 require_8(3, "invalid code block height exponent %1");
274 require_8(0, "invalid mode variations");
275 require_8(0x77, "invalid precinct size %1");
276 require_8(0x88, "invalid precinct size %1");
277 require_8(0x88, "invalid precinct size %1");
278 require_8(0x88, "invalid precinct size %1");
279 require_8(0x88, "invalid precinct size %1");
280 require_8(0x88, "invalid precinct size %1");
281 } else if (*marker_name == "TLM") {
282 auto const len = get_16();
285 } else if (*marker_name == "QCC" || *marker_name == "COM") {
286 auto const len = get_16();
288 } else if (*marker_name == "POC") {
289 if (main_header_finished) {
290 num_POC_after_main++;
295 auto require_8_poc = [&](uint16_t value, string note) {
296 if (get_8() != value) {
297 notes.push_back ({ VerificationNote::Type::BV21_ERROR, VerificationNote::Code::INCORRECT_JPEG2000_POC_MARKER, String::compose(note, value) });
301 auto require_16_poc = [&](uint16_t value, string note) {
302 if (get_16() != value) {
303 notes.push_back ({ VerificationNote::Type::BV21_ERROR, VerificationNote::Code::INCORRECT_JPEG2000_POC_MARKER, String::compose(note, value) });
307 require_16_poc(16, "invalid length %1");
308 require_8_poc(0, "invalid RSpoc %1");
309 require_8_poc(0, "invalid CSpoc %1");
310 require_16_poc(1, "invalid LYEpoc %1");
311 require_8_poc(6, "invalid REpoc %1");
312 require_8_poc(3, "invalid CEpoc %1");
313 require_8_poc(4, "invalid Ppoc %1");
314 require_8_poc(6, "invalid RSpoc %1");
315 require_8_poc(0, "invalid CSpoc %1");
316 require_16_poc(1, "invalid LYEpoc %1");
317 require_8_poc(7, "invalid REpoc %1");
318 require_8_poc(3, "invalid CEpoc %1");
319 require_8_poc(4, "invalid Ppoc %1");
324 throw InvalidCodestream("no COD marker found");
327 throw InvalidCodestream("more than one COD marker found");
330 throw InvalidCodestream("no QCD marker found");
333 throw InvalidCodestream("more than one QCD marker found");
335 if (num_POC_in_main != 0 && !fourk) {
336 notes.push_back ({ VerificationNote::Type::BV21_ERROR, VerificationNote::Code::INCORRECT_JPEG2000_POC_MARKER_COUNT_FOR_2K, raw_convert<string>(num_POC_in_main) });
338 if (num_POC_in_main != 1 && fourk) {
339 notes.push_back ({ VerificationNote::Type::BV21_ERROR, VerificationNote::Code::INCORRECT_JPEG2000_POC_MARKER_COUNT_FOR_4K, raw_convert<string>(num_POC_in_main) });
341 if (num_POC_after_main != 0) {
342 notes.push_back ({ VerificationNote::Type::BV21_ERROR, VerificationNote::Code::INVALID_JPEG2000_POC_MARKER_LOCATION });
345 notes.push_back ({ VerificationNote::Type::BV21_ERROR, VerificationNote::Code::MISSING_JPEG200_TLM_MARKER });
348 catch (InvalidCodestream const& e)
350 notes.push_back ({VerificationNote::Type::ERROR, VerificationNote::Code::INVALID_JPEG2000_CODESTREAM, string(e.what()) });