summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCarl Hetherington <cth@carlh.net>2021-01-27 01:27:53 +0100
committerCarl Hetherington <cth@carlh.net>2021-02-06 23:05:15 +0100
commit73780cdb80eb3ac1f0f9d96fe6032f8a2317b01b (patch)
tree171b53d146b9eb5a0e98e90b5dbdbff6cf3a7a50
parenteac3cac07eb97a6bcf6accd1575af76cd6f59112 (diff)
WIP: analyse J2K files.j2k-check
-rw-r--r--src/verify.cc30
-rw-r--r--src/verify.h26
-rw-r--r--src/verify_j2k.cc346
-rw-r--r--src/verify_j2k.h60
-rw-r--r--src/wscript2
-rw-r--r--test/test.cc15
-rw-r--r--test/test.h1
-rw-r--r--test/verify_test.cc38
8 files changed, 516 insertions, 2 deletions
diff --git a/src/verify.cc b/src/verify.cc
index e45c381e..0082cfcd 100644
--- a/src/verify.cc
+++ b/src/verify.cc
@@ -1491,9 +1491,35 @@ dcp::note_to_string (VerificationNote note)
case VerificationNote::Code::UNSIGNED_PKL_WITH_ENCRYPTED_CONTENT:
return String::compose("The PKL %1, which has encrypted content, is not signed.", note.note().get());
case VerificationNote::Code::MISMATCHED_PKL_ANNOTATION_TEXT_WITH_CPL:
- return String::compose("The PKL %1 has only one CPL but its <AnnotationText> does not match the CPL's <ContentTitleText>", note.note().get());
+ return String::compose("The PKL %1 has only one CPL but its <AnnotationText> does not match the CPL's <ContentTitleText>.", note.note().get());
case VerificationNote::Code::PARTIALLY_ENCRYPTED:
- return "Some assets are encrypted but some are not";
+ return "Some assets are encrypted but some are not.";
+ case VerificationNote::Code::INVALID_JPEG2000_CODESTREAM:
+ return String::compose("The JPEG2000 codestream for at least one frame is invalid (%1)", note.note().get());
+ case VerificationNote::Code::INVALID_JPEG2000_GUARD_BITS_FOR_2K:
+ return String::compose("The JPEG2000 codestream uses %1 guard bits in a 2K image instead of 1.", note.note().get());
+ case VerificationNote::Code::INVALID_JPEG2000_GUARD_BITS_FOR_4K:
+ return String::compose("The JPEG2000 codestream uses %1 guard bits in a 4K image instead of 2.", note.note().get());
+ case VerificationNote::Code::INVALID_JPEG2000_TILE_SIZE:
+ return "The JPEG2000 tile size is not the same as the image size.";
+ case VerificationNote::Code::INVALID_JPEG2000_CODE_BLOCK_WIDTH:
+ return String::compose("The JPEG2000 codestream uses a code block width of %1 instead of 32.", note.note().get());
+ case VerificationNote::Code::INVALID_JPEG2000_CODE_BLOCK_HEIGHT:
+ return String::compose("The JPEG2000 codestream uses a code block height of %1 instead of 32.", note.note().get());
+ case VerificationNote::Code::INCORRECT_JPEG2000_POC_MARKER_COUNT_FOR_2K:
+ return String::compose("%1 POC markers found in 2K JPEG2000 codestream instead of 0.", note.note().get());
+ case VerificationNote::Code::INCORRECT_JPEG2000_POC_MARKER_COUNT_FOR_4K:
+ return String::compose("%1 POC markers found in 4K JPEG2000 codestream instead of 1.", note.note().get());
+ case VerificationNote::Code::INCORRECT_JPEG2000_POC_MARKER:
+ return String::compose("Incorrect POC marker content found (%1)", note.note().get());
+ case VerificationNote::Code::INVALID_JPEG2000_POC_MARKER_LOCATION:
+ return "POC marker found outside main header";
+ case VerificationNote::Code::INVALID_JPEG2000_TILE_PARTS_FOR_2K:
+ return String::compose("The JPEG2000 codestream has %1 tile parts in a 2K image instead of 3.", note.note().get());
+ case VerificationNote::Code::INVALID_JPEG2000_TILE_PARTS_FOR_4K:
+ return String::compose("The JPEG2000 codestream has %1 tile parts in a 4K image instead of 6.", note.note().get());
+ case VerificationNote::Code::MISSING_JPEG200_TLM_MARKER:
+ return "No TLM marker was found in a JPEG2000 codestream.";
}
return "";
diff --git a/src/verify.h b/src/verify.h
index 0a8b39ca..d57958e2 100644
--- a/src/verify.h
+++ b/src/verify.h
@@ -318,6 +318,32 @@ public:
MISMATCHED_PKL_ANNOTATION_TEXT_WITH_CPL,
/** If any content is encrypted, everything must be encrypted */
PARTIALLY_ENCRYPTED,
+ /** General error from our JPEG2000 codestream verification */
+ INVALID_JPEG2000_CODESTREAM,
+ /** Invalid number of guard bits in a 2K JPEG2000 stream (should be 1) Bv2.1_10.2.1 */
+ INVALID_JPEG2000_GUARD_BITS_FOR_2K,
+ /** Invalid number of guard bits in a 4K JPEG2000 stream (should be 2) Bv2.1_10.2.1 */
+ INVALID_JPEG2000_GUARD_BITS_FOR_4K,
+ /** JPEG2000 tile size is not the same as the image size Bv2.1_10.2.1 */
+ INVALID_JPEG2000_TILE_SIZE,
+ /** JPEG2000 code block width is not 32 Bv2.1_10.2.1 */
+ INVALID_JPEG2000_CODE_BLOCK_WIDTH,
+ /** JPEG2000 code block height is not 32 Bv2.1_10.2.1 */
+ INVALID_JPEG2000_CODE_BLOCK_HEIGHT,
+ /** There must be no POC markers in a 2K codestream Bv2.1_10.2.1 */
+ INCORRECT_JPEG2000_POC_MARKER_COUNT_FOR_2K,
+ /** There must be exactly one POC marker in a 4K codestream Bv2.1_10.2.1 */
+ INCORRECT_JPEG2000_POC_MARKER_COUNT_FOR_4K,
+ /** A 4K POC marker has incorrect content Bv2.1_10.2.1 */
+ INCORRECT_JPEG2000_POC_MARKER,
+ /** A POC marker was found outside the main head Bv2.1_10.2.1 */
+ INVALID_JPEG2000_POC_MARKER_LOCATION,
+ /** Invalid number of tile parts for 2K JPEG2000 stream (should be 3) Bv2.1_10.2.1 */
+ INVALID_JPEG2000_TILE_PARTS_FOR_2K,
+ /** Invalid number of tile parts for 4K JPEG2000 stream (should be 6) Bv2.1_10.2.1 */
+ INVALID_JPEG2000_TILE_PARTS_FOR_4K,
+ /** No TLM marker was found Bv2.1_10.2.1 */
+ MISSING_JPEG200_TLM_MARKER,
};
VerificationNote (Type type, Code code)
diff --git a/src/verify_j2k.cc b/src/verify_j2k.cc
new file mode 100644
index 00000000..2e1255ea
--- /dev/null
+++ b/src/verify_j2k.cc
@@ -0,0 +1,346 @@
+/*
+ Copyright (C) 2021 Carl Hetherington <cth@carlh.net>
+
+ This file is part of libdcp.
+
+ libdcp 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.
+
+ libdcp 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 libdcp. If not, see <http://www.gnu.org/licenses/>.
+
+ In addition, as a special exception, the copyright holders give
+ permission to link the code of portions of this program with the
+ OpenSSL library under certain conditions as described in each
+ individual source file, and distribute linked combinations
+ including the two.
+
+ You must obey the GNU General Public License in all respects
+ for all of the code used other than OpenSSL. If you modify
+ file(s) with this exception, you may extend this exception to your
+ version of the file(s), but you are not obligated to do so. If you
+ do not wish to do so, delete this exception statement from your
+ version. If you delete this exception statement from all source
+ files in the program, then also delete it here.
+*/
+
+
+/** @file src/verify_j2k.cc
+ * @brief Verification that JPEG2000 files meet requirements
+ */
+
+
+#include "compose.hpp"
+#include "data.h"
+#include "raw_convert.h"
+#include "verify.h"
+#include "verify_j2k.h"
+#include <memory>
+#include <vector>
+
+
+using std::shared_ptr;
+using std::map;
+using std::runtime_error;
+using std::string;
+using std::vector;
+using boost::optional;
+using dcp::raw_convert;
+
+
+class InvalidCodestream : public runtime_error
+{
+public:
+ InvalidCodestream (string note)
+ : runtime_error(note)
+ {}
+};
+
+
+void
+dcp::verify_j2k (shared_ptr<const Data> j2k, vector<VerificationNote>& notes)
+{
+ try {
+ auto ptr = j2k->data();
+ auto end = ptr + j2k->size();
+
+ map<string, uint8_t> markers = {
+ { "SOC", 0x4f },
+ { "SIZ", 0x51 },
+ { "COD", 0x52 },
+ { "COC", 0x53 },
+ { "TLM", 0x55 },
+ { "QCD", 0x5c },
+ { "QCC", 0x5d },
+ { "POC", 0x5f },
+ { "COM", 0x64 },
+ { "SOT", 0x90 },
+ { "SOD", 0x93 },
+ { "EOC", 0xd9 },
+ };
+
+ auto marker_name_from_id = [&markers](uint8_t b) -> optional<string> {
+ for (auto const& i: markers) {
+ if (i.second == b) {
+ return i.first;
+ }
+ }
+ return {};
+ };
+
+ auto require_marker = [&](string name) {
+ if (ptr == end || *ptr != 0xff) {
+ throw InvalidCodestream ("missing marker start byte");
+ }
+ ++ptr;
+ if (ptr == end || *ptr != markers[name]) {
+ throw InvalidCodestream ("missing_marker " + name);
+ }
+ ++ptr;
+ };
+
+ auto get_8 = [&]() {
+ if (ptr >= end) {
+ throw InvalidCodestream ("unexpected end of file");
+ }
+ return *ptr++;
+ };
+
+ auto get_16 = [&]() {
+ if (ptr >= (end - 1)) {
+ throw InvalidCodestream ("unexpected end of file");
+ }
+ auto const a = *ptr++;
+ auto const b = *ptr++;
+ return b | (a << 8);
+ };
+
+ auto get_32 = [&]() -> uint32_t {
+ if (ptr >= (end - 3)) {
+ throw InvalidCodestream ("unexpected end of file");
+ }
+ auto const a = *ptr++;
+ auto const b = *ptr++;
+ auto const c = *ptr++;
+ auto const d = *ptr++;
+ return d | (c << 8) | (b << 16) | (a << 24);
+ };
+
+ auto require_8 = [&](uint8_t value, string note) {
+ auto v = get_8 ();
+ if (v != value) {
+ throw InvalidCodestream (String::compose(note, v));
+ }
+ };
+
+ auto require_16 = [&](uint16_t value, string note) {
+ auto v = get_16 ();
+ if (v != value) {
+ throw InvalidCodestream (String::compose(note, v));
+ }
+ };
+
+ auto require_32 = [&](uint32_t value, string note) {
+ auto v = get_32 ();
+ if (v != value) {
+ throw InvalidCodestream (String::compose(note, v));
+ }
+ };
+
+ require_marker ("SOC");
+ require_marker ("SIZ");
+ auto L_siz = get_16();
+ if (L_siz != 47) {
+ throw InvalidCodestream("unexpected SIZ size " + raw_convert<string>(L_siz));
+ }
+
+ get_16(); // CA: codestream capabilities
+ auto const image_width = get_32();
+ auto const image_height = get_32();
+ auto const fourk = image_width > 2048;
+ require_32 (0, "invalid top-left image x coordinate %1");
+ require_32 (0, "invalid top-left image y coordinate %1");
+ auto const tile_width = get_32();
+ auto const tile_height = get_32();
+ if (tile_width != image_width || tile_height != image_height) {
+ notes.push_back ({ VerificationNote::Type::BV21_ERROR, VerificationNote::Code::INVALID_JPEG2000_TILE_SIZE });
+ }
+ require_32 (0, "invalid tile anchor x coordinate %1");
+ require_32 (0, "invalid tile anchor y coordinate %1");
+ require_16 (3, "invalid component count %1");
+ for (auto i = 0; i < 3; ++i) {
+ require_8 (12 - 1, "invalid bit depth %1");
+ require_8 (1, "invalid horizontal subsampling factor %1");
+ require_8 (1, "invalid vertical subsampling factor %1");
+ }
+
+ auto num_COD = 0;
+ auto num_QCD = 0;
+ /** number of POC markers in the main header */
+ auto num_POC_in_main = 0;
+ /** number of POC markers after the main header */
+ auto num_POC_after_main = 0;
+ bool main_header_finished = false;
+ bool tlm = false;
+
+ while (ptr < end)
+ {
+ require_8(0xff, "missing marker start byte");
+ auto marker_id = get_8();
+ auto marker_name = marker_name_from_id (marker_id);
+ if (!marker_name) {
+ char buffer[16];
+ snprintf (buffer, 16, "%2x", marker_id);
+ throw InvalidCodestream(String::compose("unknown marker %1", buffer));
+ } else if (*marker_name == "SOT") {
+ require_16(10, "invalid SOT size %1");
+ get_16(); // tile index
+ get_32(); // tile part length
+ get_8(); // tile part index
+ auto tile_parts = get_8();
+ if (!fourk && tile_parts != 3) {
+ notes.push_back ({ VerificationNote::Type::BV21_ERROR, VerificationNote::Code::INVALID_JPEG2000_TILE_PARTS_FOR_2K, raw_convert<string>(tile_parts) });
+ }
+ if (fourk && tile_parts != 6) {
+ notes.push_back ({ VerificationNote::Type::BV21_ERROR, VerificationNote::Code::INVALID_JPEG2000_TILE_PARTS_FOR_2K, raw_convert<string>(tile_parts) });
+ }
+ main_header_finished = true;
+ } else if (*marker_name == "SOD") {
+ while (ptr < (end - 1) && (ptr[0] != 0xff || ptr[1] < 0x90)) {
+ ++ptr;
+ }
+ } else if (*marker_name == "SIZ") {
+ throw InvalidCodestream ("duplicate SIZ marker");
+ } else if (*marker_name == "COD") {
+ num_COD++;
+ get_16(); // length
+ require_8(1, "invalid coding style %1");
+ require_8(4, "invalid progression order %1"); // CPRL
+ require_16(1, "invalid quality layers count %1");
+ require_8(1, "invalid multi-component transform flag %1");
+ require_8(fourk ? 6 : 5, "invalid number of transform levels %1");
+ auto log_code_block_width = get_8();
+ if (log_code_block_width != 3) {
+ notes.push_back ({ VerificationNote::Type::BV21_ERROR, VerificationNote::Code::INVALID_JPEG2000_CODE_BLOCK_WIDTH, raw_convert<string>(4 * (2 << log_code_block_width)) });
+ }
+ auto log_code_block_height = get_8();
+ if (log_code_block_height != 3) {
+ notes.push_back ({ VerificationNote::Type::BV21_ERROR, VerificationNote::Code::INVALID_JPEG2000_CODE_BLOCK_HEIGHT, raw_convert<string>(4 * (2 << log_code_block_height)) });
+ }
+ require_8(0, "invalid mode variations");
+ require_8(0, "invalid wavelet transform type %1"); // 9/7 irreversible
+ require_8(0x77, "invalid precinct size %1");
+ require_8(0x88, "invalid precinct size %1");
+ require_8(0x88, "invalid precinct size %1");
+ require_8(0x88, "invalid precinct size %1");
+ require_8(0x88, "invalid precinct size %1");
+ require_8(0x88, "invalid precinct size %1");
+ if (fourk) {
+ require_8(0x88, "invalid precinct size %1");
+ }
+ } else if (*marker_name == "QCD") {
+ num_QCD++;
+ auto const L_qcd = get_16();
+ auto quantization_style = get_8();
+ int guard_bits = (quantization_style >> 5) & 7;
+ if (fourk && guard_bits != 2) {
+ notes.push_back ({ VerificationNote::Type::BV21_ERROR, VerificationNote::Code::INVALID_JPEG2000_GUARD_BITS_FOR_4K, raw_convert<string>(guard_bits) });
+ }
+ if (!fourk && guard_bits != 1) {
+ notes.push_back ({ VerificationNote::Type::BV21_ERROR, VerificationNote::Code::INVALID_JPEG2000_GUARD_BITS_FOR_2K, raw_convert<string>(guard_bits) });
+ }
+ ptr += L_qcd - 3;
+ } else if (*marker_name == "COC") {
+ get_16(); // length
+ require_8(0, "invalid COC component number");
+ require_8(1, "invalid coding style %1");
+ require_8(5, "invalid number of transform levels %1");
+ require_8(3, "invalid code block width exponent %1");
+ require_8(3, "invalid code block height exponent %1");
+ require_8(0, "invalid mode variations");
+ require_8(0x77, "invalid precinct size %1");
+ require_8(0x88, "invalid precinct size %1");
+ require_8(0x88, "invalid precinct size %1");
+ require_8(0x88, "invalid precinct size %1");
+ require_8(0x88, "invalid precinct size %1");
+ require_8(0x88, "invalid precinct size %1");
+ } else if (*marker_name == "TLM") {
+ auto const len = get_16();
+ ptr += len - 2;
+ tlm = true;
+ } else if (*marker_name == "QCC" || *marker_name == "COM") {
+ auto const len = get_16();
+ ptr += len - 2;
+ } else if (*marker_name == "POC") {
+ if (main_header_finished) {
+ num_POC_after_main++;
+ } else {
+ num_POC_in_main++;
+ }
+
+ auto require_8_poc = [&](uint16_t value, string note) {
+ if (get_8() != value) {
+ notes.push_back ({ VerificationNote::Type::BV21_ERROR, VerificationNote::Code::INCORRECT_JPEG2000_POC_MARKER, String::compose(note, value) });
+ }
+ };
+
+ auto require_16_poc = [&](uint16_t value, string note) {
+ if (get_16() != value) {
+ notes.push_back ({ VerificationNote::Type::BV21_ERROR, VerificationNote::Code::INCORRECT_JPEG2000_POC_MARKER, String::compose(note, value) });
+ }
+ };
+
+ require_16_poc(16, "invalid length %1");
+ require_8_poc(0, "invalid RSpoc %1");
+ require_8_poc(0, "invalid CSpoc %1");
+ require_16_poc(1, "invalid LYEpoc %1");
+ require_8_poc(6, "invalid REpoc %1");
+ require_8_poc(3, "invalid CEpoc %1");
+ require_8_poc(4, "invalid Ppoc %1");
+ require_8_poc(6, "invalid RSpoc %1");
+ require_8_poc(0, "invalid CSpoc %1");
+ require_16_poc(1, "invalid LYEpoc %1");
+ require_8_poc(7, "invalid REpoc %1");
+ require_8_poc(3, "invalid CEpoc %1");
+ require_8_poc(4, "invalid Ppoc %1");
+ }
+ }
+
+ if (num_COD == 0) {
+ throw InvalidCodestream("no COD marker found");
+ }
+ if (num_COD > 1) {
+ throw InvalidCodestream("more than one COD marker found");
+ }
+ if (num_QCD == 0) {
+ throw InvalidCodestream("no QCD marker found");
+ }
+ if (num_QCD > 1) {
+ throw InvalidCodestream("more than one QCD marker found");
+ }
+ if (num_POC_in_main != 0 && !fourk) {
+ notes.push_back ({ VerificationNote::Type::BV21_ERROR, VerificationNote::Code::INCORRECT_JPEG2000_POC_MARKER_COUNT_FOR_2K, raw_convert<string>(num_POC_in_main) });
+ }
+ if (num_POC_in_main != 1 && fourk) {
+ notes.push_back ({ VerificationNote::Type::BV21_ERROR, VerificationNote::Code::INCORRECT_JPEG2000_POC_MARKER_COUNT_FOR_4K, raw_convert<string>(num_POC_in_main) });
+ }
+ if (num_POC_after_main != 0) {
+ notes.push_back ({ VerificationNote::Type::BV21_ERROR, VerificationNote::Code::INVALID_JPEG2000_POC_MARKER_LOCATION });
+ }
+ if (!tlm) {
+ notes.push_back ({ VerificationNote::Type::BV21_ERROR, VerificationNote::Code::MISSING_JPEG200_TLM_MARKER });
+ }
+ }
+ catch (InvalidCodestream const& e)
+ {
+ notes.push_back ({VerificationNote::Type::ERROR, VerificationNote::Code::INVALID_JPEG2000_CODESTREAM, string(e.what()) });
+ }
+}
+
diff --git a/src/verify_j2k.h b/src/verify_j2k.h
new file mode 100644
index 00000000..3d9093dd
--- /dev/null
+++ b/src/verify_j2k.h
@@ -0,0 +1,60 @@
+/*
+ Copyright (C) 2021 Carl Hetherington <cth@carlh.net>
+
+ This file is part of libdcp.
+
+ libdcp 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.
+
+ libdcp 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 libdcp. If not, see <http://www.gnu.org/licenses/>.
+
+ In addition, as a special exception, the copyright holders give
+ permission to link the code of portions of this program with the
+ OpenSSL library under certain conditions as described in each
+ individual source file, and distribute linked combinations
+ including the two.
+
+ You must obey the GNU General Public License in all respects
+ for all of the code used other than OpenSSL. If you modify
+ file(s) with this exception, you may extend this exception to your
+ version of the file(s), but you are not obligated to do so. If you
+ do not wish to do so, delete this exception statement from your
+ version. If you delete this exception statement from all source
+ files in the program, then also delete it here.
+*/
+
+
+/** @file src/verify_j2k.h
+ * @brief Verification that JPEG2000 files meet requirements
+ */
+
+
+#ifndef LIBDCP_VERIFY_J2K
+#define LIBDCP_VERIFY_J2K
+
+
+#include "verify.h"
+#include <vector>
+
+
+namespace dcp {
+
+
+class Data;
+
+
+void verify_j2k (std::shared_ptr<const Data> data, std::vector<VerificationNote>& notes);
+
+
+}
+
+
+#endif
diff --git a/src/wscript b/src/wscript
index 10bb90c7..c5729b93 100644
--- a/src/wscript
+++ b/src/wscript
@@ -109,6 +109,7 @@ def build(bld):
types.cc
util.cc
verify.cc
+ verify_j2k.cc
version.cc
"""
@@ -195,6 +196,7 @@ def build(bld):
types.h
util.h
verify.h
+ verify_j2k.h
version.h
"""
diff --git a/test/test.cc b/test/test.cc
index 7ee42cf8..1f12bee4 100644
--- a/test/test.cc
+++ b/test/test.cc
@@ -515,4 +515,19 @@ black_picture_asset (boost::filesystem::path dir, int frames)
}
+boost::filesystem::path
+find_file (boost::filesystem::path dir, string filename_part)
+{
+ boost::optional<boost::filesystem::path> found;
+ for (auto i: boost::filesystem::directory_iterator(dir)) {
+ if (i.path().filename().string().find(filename_part) != string::npos) {
+ BOOST_REQUIRE (!found);
+ found = i;
+ }
+ }
+ BOOST_REQUIRE (found);
+ return *found;
+}
+
+
BOOST_GLOBAL_FIXTURE (TestConfig);
diff --git a/test/test.h b/test/test.h
index 155c7898..57be5d11 100644
--- a/test/test.h
+++ b/test/test.h
@@ -54,6 +54,7 @@ extern std::shared_ptr<dcp::DCP> make_simple_with_interop_ccaps (boost::filesyst
extern std::shared_ptr<dcp::DCP> make_simple_with_smpte_ccaps (boost::filesystem::path path);
extern std::shared_ptr<dcp::OpenJPEGImage> black_image (dcp::Size size = dcp::Size(1998, 1080));
extern std::shared_ptr<dcp::ReelAsset> black_picture_asset (boost::filesystem::path dir, int frames = 24);
+extern boost::filesystem::path find_file (boost::filesystem::path dir, std::string filename_part);
/** Creating an object of this class will make asdcplib's random number generation
* (more) predictable.
diff --git a/test/verify_test.cc b/test/verify_test.cc
index f5a012a6..e3efb57f 100644
--- a/test/verify_test.cc
+++ b/test/verify_test.cc
@@ -53,6 +53,7 @@
#include "test.h"
#include "util.h"
#include "verify.h"
+#include "verify_j2k.h"
#include <boost/test/unit_test.hpp>
#include <boost/algorithm/string.hpp>
#include <cstdio>
@@ -2728,3 +2729,40 @@ BOOST_AUTO_TEST_CASE (verify_partially_encrypted)
check_verify_result ({dir}, {{dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::PARTIALLY_ENCRYPTED}});
}
+
+BOOST_AUTO_TEST_CASE (verify_jpeg2000_codestream_2k)
+{
+ vector<dcp::VerificationNote> notes;
+ dcp::MonoPictureAsset picture (find_file(private_test / "data" / "JourneyToJah_TLR-1_F_EN-DE-FR_CH_51_2K_LOK_20140225_DGL_SMPTE_OV", "j2c.mxf"));
+ auto reader = picture.start_read ();
+ auto frame = reader->get_frame (0);
+ verify_j2k (frame, notes);
+ dump_notes (notes);
+}
+
+
+BOOST_AUTO_TEST_CASE (verify_jpeg2000_codestream_4k)
+{
+ vector<dcp::VerificationNote> notes;
+ dcp::MonoPictureAsset picture (find_file(private_test / "data" / "sul", "TLR"));
+ auto reader = picture.start_read ();
+ auto frame = reader->get_frame (0);
+ verify_j2k (frame, notes);
+ dump_notes (notes);
+}
+
+
+BOOST_AUTO_TEST_CASE (verify_jpeg2000_codestream_libdcp)
+{
+ boost::filesystem::path dir = "build/test/verify_jpeg2000_codestream_libdcp";
+ prepare_directory (dir);
+ auto dcp = make_simple (dir);
+ dcp->write_xml (dcp::Standard::SMPTE);
+ vector<dcp::VerificationNote> notes;
+ dcp::MonoPictureAsset picture (find_file(dir, "video"));
+ auto reader = picture.start_read ();
+ auto frame = reader->get_frame (0);
+ verify_j2k (frame, notes);
+ dump_notes (notes);
+}
+