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, int frame_index, int frame_rate, vector<VerificationNote>& notes)
70 /* See ITU-T T800 (visible on https://github.com/Ymagis/ClairMeta/issues/130) */
71 unsigned int const max_tile_part_size = std::floor(200e6 / (8 * frame_rate));
74 auto ptr = j2k->data();
75 auto end = ptr + j2k->size();
77 map<string, uint8_t> markers = {
92 auto marker_name_from_id = [&markers](uint8_t b) -> optional<string> {
93 for (auto const& i: markers) {
101 auto require_marker = [&](string name) {
102 if (ptr == end || *ptr != 0xff) {
103 throw InvalidCodestream ("missing marker start byte");
106 if (ptr == end || *ptr != markers[name]) {
107 throw InvalidCodestream ("missing_marker " + name);
114 throw InvalidCodestream ("unexpected end of file");
119 auto get_16 = [&]() {
120 if (ptr >= (end - 1)) {
121 throw InvalidCodestream ("unexpected end of file");
123 auto const a = *ptr++;
124 auto const b = *ptr++;
128 auto get_32 = [&]() -> uint32_t {
129 if (ptr >= (end - 3)) {
130 throw InvalidCodestream ("unexpected end of file");
132 auto const a = *ptr++;
133 auto const b = *ptr++;
134 auto const c = *ptr++;
135 auto const d = *ptr++;
136 return d | (c << 8) | (b << 16) | (a << 24);
139 auto require_8 = [&](uint8_t value, string note) {
142 throw InvalidCodestream (String::compose(note, v));
146 auto require_16 = [&](uint16_t value, string note) {
149 throw InvalidCodestream (String::compose(note, v));
153 auto require_32 = [&](uint32_t value, string note) {
156 throw InvalidCodestream (String::compose(note, v));
160 require_marker ("SOC");
161 require_marker ("SIZ");
162 auto L_siz = get_16();
164 throw InvalidCodestream("unexpected SIZ size " + raw_convert<string>(L_siz));
167 get_16(); // CA: codestream capabilities
168 auto const image_width = get_32();
169 auto const image_height = get_32();
170 auto const fourk = image_width > 2048;
171 require_32 (0, "invalid top-left image x coordinate %1");
172 require_32 (0, "invalid top-left image y coordinate %1");
173 auto const tile_width = get_32();
174 auto const tile_height = get_32();
175 if (tile_width != image_width || tile_height != image_height) {
176 notes.push_back ({ VerificationNote::Type::BV21_ERROR, VerificationNote::Code::INVALID_JPEG2000_TILE_SIZE });
178 require_32 (0, "invalid tile anchor x coordinate %1");
179 require_32 (0, "invalid tile anchor y coordinate %1");
180 require_16 (3, "invalid component count %1");
181 for (auto i = 0; i < 3; ++i) {
182 require_8 (12 - 1, "invalid bit depth %1");
183 require_8 (1, "invalid horizontal subsampling factor %1");
184 require_8 (1, "invalid vertical subsampling factor %1");
189 /** number of POC markers in the main header */
190 auto num_POC_in_main = 0;
191 /** number of POC markers after the main header */
192 auto num_POC_after_main = 0;
193 bool main_header_finished = false;
198 require_8(0xff, "missing marker start byte");
199 auto marker_id = get_8();
200 auto marker_name = marker_name_from_id (marker_id);
203 snprintf (buffer, 16, "%2x", marker_id);
204 throw InvalidCodestream(String::compose("unknown marker %1", buffer));
205 } else if (*marker_name == "SOT") {
206 require_16(10, "invalid SOT size %1");
207 get_16(); // tile index
208 auto const tile_part_length = get_32();
209 auto const tile_part_index = get_8();
210 auto tile_parts = get_8();
211 if (!fourk && tile_parts != 3) {
212 notes.push_back ({ VerificationNote::Type::BV21_ERROR, VerificationNote::Code::INVALID_JPEG2000_TILE_PARTS_FOR_2K, raw_convert<string>(tile_parts) });
214 if (fourk && tile_parts != 6) {
215 notes.push_back ({ VerificationNote::Type::BV21_ERROR, VerificationNote::Code::INVALID_JPEG2000_TILE_PARTS_FOR_4K, raw_convert<string>(tile_parts) });
217 if (tile_part_length > max_tile_part_size) {
218 VerificationNote note{VerificationNote::Type::ERROR, VerificationNote::Code::INVALID_JPEG2000_TILE_PART_SIZE};
219 note.set_frame(frame_index);
220 note.set_component(tile_part_index);
221 note.set_size(tile_part_length);
222 notes.push_back(note);
224 main_header_finished = true;
225 } else if (*marker_name == "SOD") {
226 while (ptr < (end - 1) && (ptr[0] != 0xff || ptr[1] < 0x90)) {
229 } else if (*marker_name == "SIZ") {
230 throw InvalidCodestream ("duplicate SIZ marker");
231 } else if (*marker_name == "COD") {
234 require_8(1, "invalid coding style %1");
235 require_8(4, "invalid progression order %1"); // CPRL
236 require_16(1, "invalid quality layers count %1");
237 require_8(1, "invalid multi-component transform flag %1");
238 require_8(fourk ? 6 : 5, "invalid number of transform levels %1");
239 auto log_code_block_width = get_8();
240 if (log_code_block_width != 3) {
241 notes.push_back ({ VerificationNote::Type::BV21_ERROR, VerificationNote::Code::INVALID_JPEG2000_CODE_BLOCK_WIDTH, raw_convert<string>(4 * (2 << log_code_block_width)) });
243 auto log_code_block_height = get_8();
244 if (log_code_block_height != 3) {
245 notes.push_back ({ VerificationNote::Type::BV21_ERROR, VerificationNote::Code::INVALID_JPEG2000_CODE_BLOCK_HEIGHT, raw_convert<string>(4 * (2 << log_code_block_height)) });
247 require_8(0, "invalid mode variations");
248 require_8(0, "invalid wavelet transform type %1"); // 9/7 irreversible
249 require_8(0x77, "invalid precinct size %1");
250 require_8(0x88, "invalid precinct size %1");
251 require_8(0x88, "invalid precinct size %1");
252 require_8(0x88, "invalid precinct size %1");
253 require_8(0x88, "invalid precinct size %1");
254 require_8(0x88, "invalid precinct size %1");
256 require_8(0x88, "invalid precinct size %1");
258 } else if (*marker_name == "QCD") {
260 auto const L_qcd = get_16();
261 auto quantization_style = get_8();
262 int guard_bits = (quantization_style >> 5) & 7;
263 if (fourk && guard_bits != 2) {
264 notes.push_back ({ VerificationNote::Type::BV21_ERROR, VerificationNote::Code::INVALID_JPEG2000_GUARD_BITS_FOR_4K, raw_convert<string>(guard_bits) });
266 if (!fourk && guard_bits != 1) {
267 notes.push_back ({ VerificationNote::Type::BV21_ERROR, VerificationNote::Code::INVALID_JPEG2000_GUARD_BITS_FOR_2K, raw_convert<string>(guard_bits) });
270 } else if (*marker_name == "COC") {
272 auto const coc_component_number = get_8();
273 /* I don't know if this is really a requirement, but it seems to make sense that there should only
274 * be components 0, 1 and 2. DoM bug #2395 is about a DCP with COC component number 1 which seems
275 * like it should be OK.
277 if (coc_component_number > 2) {
278 throw InvalidCodestream(String::compose("invalid COC component number %1", coc_component_number));
280 require_8(1, "invalid coding style %1");
281 require_8(5, "invalid number of transform levels %1");
282 require_8(3, "invalid code block width exponent %1");
283 require_8(3, "invalid code block height exponent %1");
284 require_8(0, "invalid mode variations");
285 require_8(0x77, "invalid precinct size %1");
286 require_8(0x88, "invalid precinct size %1");
287 require_8(0x88, "invalid precinct size %1");
288 require_8(0x88, "invalid precinct size %1");
289 require_8(0x88, "invalid precinct size %1");
290 require_8(0x88, "invalid precinct size %1");
291 } else if (*marker_name == "TLM") {
292 auto const len = get_16();
295 } else if (*marker_name == "QCC" || *marker_name == "COM") {
296 auto const len = get_16();
298 } else if (*marker_name == "POC") {
299 if (main_header_finished) {
300 num_POC_after_main++;
305 auto require_8_poc = [&](uint16_t value, string note) {
306 if (get_8() != value) {
307 notes.push_back ({ VerificationNote::Type::BV21_ERROR, VerificationNote::Code::INCORRECT_JPEG2000_POC_MARKER, String::compose(note, value) });
311 auto require_16_poc = [&](uint16_t value, string note) {
312 if (get_16() != value) {
313 notes.push_back ({ VerificationNote::Type::BV21_ERROR, VerificationNote::Code::INCORRECT_JPEG2000_POC_MARKER, String::compose(note, value) });
317 require_16_poc(16, "invalid length %1");
318 require_8_poc(0, "invalid RSpoc %1");
319 require_8_poc(0, "invalid CSpoc %1");
320 require_16_poc(1, "invalid LYEpoc %1");
321 require_8_poc(6, "invalid REpoc %1");
322 require_8_poc(3, "invalid CEpoc %1");
323 require_8_poc(4, "invalid Ppoc %1");
324 require_8_poc(6, "invalid RSpoc %1");
325 require_8_poc(0, "invalid CSpoc %1");
326 require_16_poc(1, "invalid LYEpoc %1");
327 require_8_poc(7, "invalid REpoc %1");
328 require_8_poc(3, "invalid CEpoc %1");
329 require_8_poc(4, "invalid Ppoc %1");
334 throw InvalidCodestream("no COD marker found");
337 throw InvalidCodestream("more than one COD marker found");
340 throw InvalidCodestream("no QCD marker found");
343 throw InvalidCodestream("more than one QCD marker found");
345 if (num_POC_in_main != 0 && !fourk) {
346 notes.push_back ({ VerificationNote::Type::BV21_ERROR, VerificationNote::Code::INCORRECT_JPEG2000_POC_MARKER_COUNT_FOR_2K, raw_convert<string>(num_POC_in_main) });
348 if (num_POC_in_main != 1 && fourk) {
349 notes.push_back ({ VerificationNote::Type::BV21_ERROR, VerificationNote::Code::INCORRECT_JPEG2000_POC_MARKER_COUNT_FOR_4K, raw_convert<string>(num_POC_in_main) });
351 if (num_POC_after_main != 0) {
352 notes.push_back ({ VerificationNote::Type::BV21_ERROR, VerificationNote::Code::INVALID_JPEG2000_POC_MARKER_LOCATION });
355 notes.push_back ({ VerificationNote::Type::BV21_ERROR, VerificationNote::Code::MISSING_JPEG200_TLM_MARKER });
358 catch (InvalidCodestream const& e)
360 notes.push_back ({VerificationNote::Type::ERROR, VerificationNote::Code::INVALID_JPEG2000_CODESTREAM, string(e.what()) });