b91588491e1e08242b2a748407fbab0ee4337a8d
[libdcp.git] / src / verify_j2k.cc
1 /*
2     Copyright (C) 2021 Carl Hetherington <cth@carlh.net>
3
4     This file is part of libdcp.
5
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.
10
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.
15
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/>.
18
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
23     including the two.
24
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.
32 */
33
34
35 /** @file  src/verify_j2k.cc
36  *  @brief Verification that JPEG2000 files meet requirements
37  */
38
39
40 #include "compose.hpp"
41 #include "data.h"
42 #include "raw_convert.h"
43 #include "verify.h"
44 #include "verify_j2k.h"
45 #include <memory>
46 #include <vector>
47
48
49 using std::shared_ptr;
50 using std::map;
51 using std::runtime_error;
52 using std::string;
53 using std::vector;
54 using boost::optional;
55 using dcp::raw_convert;
56
57
58 class InvalidCodestream : public runtime_error
59 {
60 public:
61         InvalidCodestream (string note)
62                 : runtime_error(note)
63         {}
64 };
65
66
67 void
68 dcp::verify_j2k(shared_ptr<const Data> j2k, int frame_index, int frame_rate, vector<VerificationNote>& notes)
69 {
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));
72
73         try {
74                 auto ptr = j2k->data();
75                 auto end = ptr + j2k->size();
76
77                 map<string, uint8_t> markers = {
78                         { "SOC", 0x4f },
79                         { "SIZ", 0x51 },
80                         { "COD", 0x52 },
81                         { "COC", 0x53 },
82                         { "TLM", 0x55 },
83                         { "QCD", 0x5c },
84                         { "QCC", 0x5d },
85                         { "POC", 0x5f },
86                         { "COM", 0x64 },
87                         { "SOT", 0x90 },
88                         { "SOD", 0x93 },
89                         { "EOC", 0xd9 },
90                 };
91
92                 auto marker_name_from_id = [&markers](uint8_t b) -> optional<string> {
93                         for (auto const& i: markers) {
94                                 if (i.second == b) {
95                                         return i.first;
96                                 }
97                         }
98                         return {};
99                 };
100
101                 auto require_marker = [&](string name) {
102                         if (ptr == end || *ptr != 0xff) {
103                                 throw InvalidCodestream ("missing marker start byte");
104                         }
105                         ++ptr;
106                         if (ptr == end || *ptr != markers[name]) {
107                                 throw InvalidCodestream ("missing_marker " + name);
108                         }
109                         ++ptr;
110                 };
111
112                 auto get_8 = [&]() {
113                         if (ptr >= end) {
114                                 throw InvalidCodestream ("unexpected end of file");
115                         }
116                         return *ptr++;
117                 };
118
119                 auto get_16 = [&]() {
120                         if (ptr >= (end - 1)) {
121                                 throw InvalidCodestream ("unexpected end of file");
122                         }
123                         auto const a = *ptr++;
124                         auto const b = *ptr++;
125                         return b | (a << 8);
126                 };
127
128                 auto get_32 = [&]() -> uint32_t {
129                         if (ptr >= (end - 3)) {
130                                 throw InvalidCodestream ("unexpected end of file");
131                         }
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);
137                 };
138
139                 auto require_8 = [&](uint8_t value, string note) {
140                         auto v = get_8 ();
141                         if (v != value) {
142                                 throw InvalidCodestream (String::compose(note, v));
143                         }
144                 };
145
146                 auto require_16 = [&](uint16_t value, string note) {
147                         auto v = get_16 ();
148                         if (v != value) {
149                                 throw InvalidCodestream (String::compose(note, v));
150                         }
151                 };
152
153                 auto require_32 = [&](uint32_t value, string note) {
154                         auto v = get_32 ();
155                         if (v != value) {
156                                 throw InvalidCodestream (String::compose(note, v));
157                         }
158                 };
159
160                 require_marker ("SOC");
161                 require_marker ("SIZ");
162                 auto L_siz = get_16();
163                 if (L_siz != 47) {
164                         throw InvalidCodestream("unexpected SIZ size " + raw_convert<string>(L_siz));
165                 }
166
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 });
177                 }
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");
185                 }
186
187                 auto num_COD = 0;
188                 auto num_QCD = 0;
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;
194                 bool tlm = false;
195
196                 while (ptr < end)
197                 {
198                         require_8(0xff, "missing marker start byte");
199                         auto marker_id = get_8();
200                         auto marker_name = marker_name_from_id (marker_id);
201                         if (!marker_name) {
202                                 char buffer[16];
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) });
213                                 }
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) });
216                                 }
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);
223                                 }
224                                 main_header_finished = true;
225                         } else if (*marker_name == "SOD") {
226                                 while (ptr < (end - 1) && (ptr[0] != 0xff || ptr[1] < 0x90)) {
227                                         ++ptr;
228                                 }
229                         } else if (*marker_name == "SIZ") {
230                                 throw InvalidCodestream ("duplicate SIZ marker");
231                         } else if (*marker_name == "COD") {
232                                 num_COD++;
233                                 get_16(); // length
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)) });
242                                 }
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)) });
246                                 }
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");
255                                 if (fourk) {
256                                         require_8(0x88, "invalid precinct size %1");
257                                 }
258                         } else if (*marker_name == "QCD") {
259                                 num_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) });
265                                 }
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) });
268                                 }
269                                 ptr += L_qcd - 3;
270                         } else if (*marker_name == "COC") {
271                                 get_16(); // length
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.
276                                  */
277                                 if (coc_component_number > 2) {
278                                         throw InvalidCodestream(String::compose("invalid COC component number %1", coc_component_number));
279                                 }
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();
293                                 ptr += len - 2;
294                                 tlm = true;
295                         } else if (*marker_name == "QCC" || *marker_name == "COM") {
296                                 auto const len = get_16();
297                                 ptr += len - 2;
298                         } else if (*marker_name == "POC") {
299                                 if (main_header_finished) {
300                                         num_POC_after_main++;
301                                 } else {
302                                         num_POC_in_main++;
303                                 }
304
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) });
308                                         }
309                                 };
310
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) });
314                                         }
315                                 };
316
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");
330                         }
331                 }
332
333                 if (num_COD == 0) {
334                         throw InvalidCodestream("no COD marker found");
335                 }
336                 if (num_COD > 1) {
337                         throw InvalidCodestream("more than one COD marker found");
338                 }
339                 if (num_QCD == 0) {
340                         throw InvalidCodestream("no QCD marker found");
341                 }
342                 if (num_QCD > 1) {
343                         throw InvalidCodestream("more than one QCD marker found");
344                 }
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) });
347                 }
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) });
350                 }
351                 if (num_POC_after_main != 0) {
352                         notes.push_back ({ VerificationNote::Type::BV21_ERROR, VerificationNote::Code::INVALID_JPEG2000_POC_MARKER_LOCATION });
353                 }
354                 if (!tlm) {
355                         notes.push_back ({ VerificationNote::Type::BV21_ERROR, VerificationNote::Code::MISSING_JPEG200_TLM_MARKER });
356                 }
357         }
358         catch (InvalidCodestream const& e)
359         {
360                 notes.push_back ({VerificationNote::Type::ERROR, VerificationNote::Code::INVALID_JPEG2000_CODESTREAM, string(e.what()) });
361         }
362 }
363