Remove unnecessary include.
[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, vector<VerificationNote>& notes)
69 {
70         try {
71                 auto ptr = j2k->data();
72                 auto end = ptr + j2k->size();
73
74                 map<string, uint8_t> markers = {
75                         { "SOC", 0x4f },
76                         { "SIZ", 0x51 },
77                         { "COD", 0x52 },
78                         { "COC", 0x53 },
79                         { "TLM", 0x55 },
80                         { "QCD", 0x5c },
81                         { "QCC", 0x5d },
82                         { "POC", 0x5f },
83                         { "COM", 0x64 },
84                         { "SOT", 0x90 },
85                         { "SOD", 0x93 },
86                         { "EOC", 0xd9 },
87                 };
88
89                 auto marker_name_from_id = [&markers](uint8_t b) -> optional<string> {
90                         for (auto const& i: markers) {
91                                 if (i.second == b) {
92                                         return i.first;
93                                 }
94                         }
95                         return {};
96                 };
97
98                 auto require_marker = [&](string name) {
99                         if (ptr == end || *ptr != 0xff) {
100                                 throw InvalidCodestream ("missing marker start byte");
101                         }
102                         ++ptr;
103                         if (ptr == end || *ptr != markers[name]) {
104                                 throw InvalidCodestream ("missing_marker " + name);
105                         }
106                         ++ptr;
107                 };
108
109                 auto get_8 = [&]() {
110                         if (ptr >= end) {
111                                 throw InvalidCodestream ("unexpected end of file");
112                         }
113                         return *ptr++;
114                 };
115
116                 auto get_16 = [&]() {
117                         if (ptr >= (end - 1)) {
118                                 throw InvalidCodestream ("unexpected end of file");
119                         }
120                         auto const a = *ptr++;
121                         auto const b = *ptr++;
122                         return b | (a << 8);
123                 };
124
125                 auto get_32 = [&]() -> uint32_t {
126                         if (ptr >= (end - 3)) {
127                                 throw InvalidCodestream ("unexpected end of file");
128                         }
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);
134                 };
135
136                 auto require_8 = [&](uint8_t value, string note) {
137                         auto v = get_8 ();
138                         if (v != value) {
139                                 throw InvalidCodestream (String::compose(note, v));
140                         }
141                 };
142
143                 auto require_16 = [&](uint16_t value, string note) {
144                         auto v = get_16 ();
145                         if (v != value) {
146                                 throw InvalidCodestream (String::compose(note, v));
147                         }
148                 };
149
150                 auto require_32 = [&](uint32_t value, string note) {
151                         auto v = get_32 ();
152                         if (v != value) {
153                                 throw InvalidCodestream (String::compose(note, v));
154                         }
155                 };
156
157                 require_marker ("SOC");
158                 require_marker ("SIZ");
159                 auto L_siz = get_16();
160                 if (L_siz != 47) {
161                         throw InvalidCodestream("unexpected SIZ size " + raw_convert<string>(L_siz));
162                 }
163
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 });
174                 }
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");
182                 }
183
184                 auto num_COD = 0;
185                 auto num_QCD = 0;
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;
191                 bool tlm = false;
192
193                 while (ptr < end)
194                 {
195                         require_8(0xff, "missing marker start byte");
196                         auto marker_id = get_8();
197                         auto marker_name = marker_name_from_id (marker_id);
198                         if (!marker_name) {
199                                 char buffer[16];
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) });
210                                 }
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) });
213                                 }
214                                 main_header_finished = true;
215                         } else if (*marker_name == "SOD") {
216                                 while (ptr < (end - 1) && (ptr[0] != 0xff || ptr[1] < 0x90)) {
217                                         ++ptr;
218                                 }
219                         } else if (*marker_name == "SIZ") {
220                                 throw InvalidCodestream ("duplicate SIZ marker");
221                         } else if (*marker_name == "COD") {
222                                 num_COD++;
223                                 get_16(); // length
224                                 /* XXX: I can't find any evidence for this: must the coding style really always be 1? */
225                                 require_8(1, "invalid COD coding style %1");
226                                 require_8(4, "invalid progression order %1"); // CPRL
227                                 require_16(1, "invalid quality layers count %1");
228                                 require_8(1, "invalid multi-component transform flag %1");
229                                 require_8(fourk ? 6 : 5, "invalid number of transform levels %1");
230                                 auto log_code_block_width = get_8();
231                                 if (log_code_block_width != 3) {
232                                         notes.push_back ({ VerificationNote::Type::BV21_ERROR, VerificationNote::Code::INVALID_JPEG2000_CODE_BLOCK_WIDTH, raw_convert<string>(4 * (2 << log_code_block_width)) });
233                                 }
234                                 auto log_code_block_height = get_8();
235                                 if (log_code_block_height != 3) {
236                                         notes.push_back ({ VerificationNote::Type::BV21_ERROR, VerificationNote::Code::INVALID_JPEG2000_CODE_BLOCK_HEIGHT, raw_convert<string>(4 * (2 << log_code_block_height)) });
237                                 }
238                                 require_8(0, "invalid mode variations");
239                                 require_8(0, "invalid wavelet transform type %1"); // 9/7 irreversible
240                                 require_8(0x77, "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");
245                                 require_8(0x88, "invalid precinct size %1");
246                                 if (fourk) {
247                                         require_8(0x88, "invalid precinct size %1");
248                                 }
249                         } else if (*marker_name == "QCD") {
250                                 num_QCD++;
251                                 auto const L_qcd = get_16();
252                                 auto quantization_style = get_8();
253                                 int guard_bits = (quantization_style >> 5) & 7;
254                                 if (fourk && guard_bits != 2) {
255                                         notes.push_back ({ VerificationNote::Type::BV21_ERROR, VerificationNote::Code::INVALID_JPEG2000_GUARD_BITS_FOR_4K, raw_convert<string>(guard_bits) });
256                                 }
257                                 if (!fourk && guard_bits != 1) {
258                                         notes.push_back ({ VerificationNote::Type::BV21_ERROR, VerificationNote::Code::INVALID_JPEG2000_GUARD_BITS_FOR_2K, raw_convert<string>(guard_bits) });
259                                 }
260                                 ptr += L_qcd - 3;
261                         } else if (*marker_name == "COC") {
262                                 get_16(); // length
263                                 require_8(0, "invalid COC component number");
264                                 /* XXX: I can't find any evidence for this: must the coding style really always be 1? */
265                                 require_8(1, "invalid COC coding style %1");
266                                 require_8(5, "invalid number of transform levels %1");
267                                 require_8(3, "invalid code block width exponent %1");
268                                 require_8(3, "invalid code block height exponent %1");
269                                 require_8(0, "invalid mode variations");
270                                 require_8(0x77, "invalid precinct size %1");
271                                 require_8(0x88, "invalid precinct size %1");
272                                 require_8(0x88, "invalid precinct size %1");
273                                 require_8(0x88, "invalid precinct size %1");
274                                 require_8(0x88, "invalid precinct size %1");
275                                 require_8(0x88, "invalid precinct size %1");
276                         } else if (*marker_name == "TLM") {
277                                 auto const len = get_16();
278                                 ptr += len - 2;
279                                 tlm = true;
280                         } else if (*marker_name == "QCC" || *marker_name == "COM") {
281                                 auto const len = get_16();
282                                 ptr += len - 2;
283                         } else if (*marker_name == "POC") {
284                                 if (main_header_finished) {
285                                         num_POC_after_main++;
286                                 } else {
287                                         num_POC_in_main++;
288                                 }
289
290                                 auto require_8_poc = [&](uint16_t value, string note) {
291                                         if (get_8() != value) {
292                                                 notes.push_back ({ VerificationNote::Type::BV21_ERROR, VerificationNote::Code::INCORRECT_JPEG2000_POC_MARKER, String::compose(note, value) });
293                                         }
294                                 };
295
296                                 auto require_16_poc = [&](uint16_t value, string note) {
297                                         if (get_16() != value) {
298                                                 notes.push_back ({ VerificationNote::Type::BV21_ERROR, VerificationNote::Code::INCORRECT_JPEG2000_POC_MARKER, String::compose(note, value) });
299                                         }
300                                 };
301
302                                 require_16_poc(16, "invalid length %1");
303                                 require_8_poc(0, "invalid RSpoc %1");
304                                 require_8_poc(0, "invalid CSpoc %1");
305                                 require_16_poc(1, "invalid LYEpoc %1");
306                                 require_8_poc(6, "invalid REpoc %1");
307                                 require_8_poc(3, "invalid CEpoc %1");
308                                 require_8_poc(4, "invalid Ppoc %1");
309                                 require_8_poc(6, "invalid RSpoc %1");
310                                 require_8_poc(0, "invalid CSpoc %1");
311                                 require_16_poc(1, "invalid LYEpoc %1");
312                                 require_8_poc(7, "invalid REpoc %1");
313                                 require_8_poc(3, "invalid CEpoc %1");
314                                 require_8_poc(4, "invalid Ppoc %1");
315                         }
316                 }
317
318                 if (num_COD == 0) {
319                         throw InvalidCodestream("no COD marker found");
320                 }
321                 if (num_COD > 1) {
322                         throw InvalidCodestream("more than one COD marker found");
323                 }
324                 if (num_QCD == 0) {
325                         throw InvalidCodestream("no QCD marker found");
326                 }
327                 if (num_QCD > 1) {
328                         throw InvalidCodestream("more than one QCD marker found");
329                 }
330                 if (num_POC_in_main != 0 && !fourk) {
331                         notes.push_back ({ VerificationNote::Type::BV21_ERROR, VerificationNote::Code::INCORRECT_JPEG2000_POC_MARKER_COUNT_FOR_2K, raw_convert<string>(num_POC_in_main) });
332                 }
333                 if (num_POC_in_main != 1 && fourk) {
334                         notes.push_back ({ VerificationNote::Type::BV21_ERROR, VerificationNote::Code::INCORRECT_JPEG2000_POC_MARKER_COUNT_FOR_4K, raw_convert<string>(num_POC_in_main) });
335                 }
336                 if (num_POC_after_main != 0) {
337                         notes.push_back ({ VerificationNote::Type::BV21_ERROR, VerificationNote::Code::INVALID_JPEG2000_POC_MARKER_LOCATION });
338                 }
339                 if (!tlm) {
340                         notes.push_back ({ VerificationNote::Type::BV21_ERROR, VerificationNote::Code::MISSING_JPEG200_TLM_MARKER });
341                 }
342         }
343         catch (InvalidCodestream const& e)
344         {
345                 notes.push_back ({VerificationNote::Type::ERROR, VerificationNote::Code::INVALID_JPEG2000_CODESTREAM, string(e.what()) });
346         }
347 }
348