diff options
153 files changed, 6449 insertions, 1972 deletions
diff --git a/benchmark/wscript b/benchmark/wscript index 7097a762..7305bdaa 100644 --- a/benchmark/wscript +++ b/benchmark/wscript @@ -35,7 +35,7 @@ def build(bld): for p in ['rgb_to_xyz', 'j2k_transcode']: obj = bld(features='cxx cxxprogram') obj.name = p - obj.uselib = 'BOOST_FILESYSTEM ASDCPLIB_CTH CXML' + obj.uselib = 'BOOST_FILESYSTEM ASDCPLIB_DCPOMATIC CXML AVCODEC AVUTIL' obj.cppflags = ['-g', '-O2'] obj.use = 'libdcp%s' % bld.env.API_VERSION obj.source = "%s.cc" % p @@ -35,7 +35,22 @@ import os import shutil def dependencies(target, options): - return (('libcxml', 'v0.17.6'), ('openjpeg', '925ca5192bb16d4f58a6fddc8b1623eced7f0203'), ('asdcplib', '8a4a2f25cac0c58aac1d4267facab20e5ec3b57f')) + deps = [ + ('libcxml', 'v0.17.9', options), + ('openjpeg', 'ad8edaacd54a862940d0a77c41ecda5858b54d6e'), + ('asdcplib', 'v1.0.1') + ] + + if target.platform == 'linux': + ffmpeg_options = { 'shared': False } + else: + ffmpeg_options = {} + + if target.platform != 'linux' or target.distro != 'arch': + # Use distro-provided FFmpeg on Arch, otherwise our own + deps.append(('ffmpeg', '0b73d2f5e70a04a67aa902902c42e3025ef3bb77', ffmpeg_options)) + + return deps def build(target, options): cmd = './waf configure --disable-examples --disable-dumpimage --disable-benchmarks --prefix=%s' % target.directory @@ -47,6 +62,9 @@ def build(target, options): elif target.platform == 'windows': cmd += f' --target-windows-{target.bits}' + if 'c++17' in options and options['c++17']: + cmd += ' --c++17' + if target.debug: cmd += ' --enable-debug' diff --git a/examples/make_dcp.cc b/examples/make_dcp.cc index 6e0961a5..80e5f52b 100644 --- a/examples/make_dcp.cc +++ b/examples/make_dcp.cc @@ -33,8 +33,8 @@ #include "dcp.h" #include "cpl.h" -#include "mono_picture_asset.h" -#include "mono_picture_asset_writer.h" +#include "mono_j2k_picture_asset.h" +#include "mono_j2k_picture_asset_writer.h" #include "sound_asset.h" #include "sound_asset_writer.h" #include "reel.h" @@ -56,10 +56,10 @@ main () per second. */ - auto picture_asset = std::make_shared<dcp::MonoPictureAsset>(dcp::Fraction(24, 1), dcp::Standard::SMPTE); + auto picture_asset = std::make_shared<dcp::MonoJ2KPictureAsset>(dcp::Fraction(24, 1), dcp::Standard::SMPTE); /* Start off a write to it */ - auto picture_writer = picture_asset->start_write("DCP/picture.mxf", dcp::PictureAsset::Behaviour::MAKE_NEW); + auto picture_writer = picture_asset->start_write("DCP/picture.mxf", dcp::Behaviour::MAKE_NEW); /* Write 24 frames of the same JPEG2000 file */ dcp::ArrayData picture("examples/help.j2c"); diff --git a/examples/read_dcp.cc b/examples/read_dcp.cc index f099409f..fff02288 100644 --- a/examples/read_dcp.cc +++ b/examples/read_dcp.cc @@ -27,10 +27,10 @@ #include "cpl.h" #include "reel.h" #include "reel_picture_asset.h" -#include "mono_picture_frame.h" -#include "mono_picture_asset.h" -#include "mono_picture_asset_reader.h" -#include "stereo_picture_asset.h" +#include "mono_j2k_picture_frame.h" +#include "mono_j2k_picture_asset.h" +#include "mono_j2k_picture_asset_reader.h" +#include "stereo_j2k_picture_asset.h" #include "sound_asset.h" #include "subtitle_asset.h" #include "openjpeg_image.h" @@ -71,9 +71,9 @@ main () auto assets = dcp.assets(); std::cout << "DCP has " << assets.size() << " assets.\n"; for (auto i: assets) { - if (std::dynamic_pointer_cast<dcp::MonoPictureAsset>(i)) { + if (std::dynamic_pointer_cast<dcp::MonoJ2KPictureAsset>(i)) { std::cout << "2D picture\n"; - } else if (std::dynamic_pointer_cast<dcp::StereoPictureAsset>(i)) { + } else if (std::dynamic_pointer_cast<dcp::StereoJ2KPictureAsset>(i)) { std::cout << "3D picture\n"; } else if (std::dynamic_pointer_cast<dcp::SoundAsset>(i)) { std::cout << "Sound\n"; @@ -89,7 +89,7 @@ main () auto cpl = dcp.cpls().front(); /* Get the picture asset in the first reel */ - auto picture_asset = std::dynamic_pointer_cast<dcp::MonoPictureAsset>( + auto picture_asset = std::dynamic_pointer_cast<dcp::MonoJ2KPictureAsset>( cpl->reels()[0]->main_picture()->asset() ); diff --git a/examples/wscript b/examples/wscript index d062af1b..68cd79ee 100644 --- a/examples/wscript +++ b/examples/wscript @@ -21,7 +21,7 @@ def build(bld): obj = bld(features='cxx cxxprogram') obj.name = example obj.use = 'libdcp%s' % bld.env.API_VERSION - obj.uselib = 'OPENJPEG CXML OPENMP ASDCPLIB_CTH BOOST_FILESYSTEM OPENSSL XMLSEC1 MAGICK' + obj.uselib = 'OPENJPEG CXML OPENMP ASDCPLIB_DCPOMATIC BOOST_FILESYSTEM OPENSSL XMLSEC1 MAGICK AVCODEC AVUTIL' obj.source = example + '.cc' obj.target = example obj.install_path = '' diff --git a/libdcp-1.0.pc.in b/libdcp-1.0.pc.in index 04b326ca..a8d1007f 100644 --- a/libdcp-1.0.pc.in +++ b/libdcp-1.0.pc.in @@ -5,6 +5,6 @@ includedir=@includedir@ Name: libdcp Description: DCP reading and writing library Version: @version@ -Requires: openssl libxml++-2.6 xmlsec1 libasdcp-carl xerces-c +Requires: openssl libxml++-@xmlpp_api@ xmlsec1 libasdcp-dcpomatic xerces-c libavutil libavcodec Libs: @libs@ Cflags: -I${includedir} diff --git a/run/test/subs_in_out b/run/test/subs_in_out index 513294d1..2bf4fea4 100755 --- a/run/test/subs_in_out +++ b/run/test/subs_in_out @@ -2,7 +2,7 @@ export LD_LIBRARY_PATH=build/src:build/asdcplib/src:$LD_LIBRARY_PATH # SIP stops this being passed in from the caller's environment -export DYLD_LIBRARY_PATH=/Users/ci/osx-environment/x86_64/10.10/lib +export DYLD_LIBRARY_PATH=/Users/ci/osx-environment/x86_64/10.10/lib:/Users/ci/workspace/lib if [ "$1" == "--debug" ]; then shift gdb --args build/test/subs_in_out "$@" diff --git a/run/tools/dcpmap b/run/tools/dcpmap new file mode 100755 index 00000000..ab236329 --- /dev/null +++ b/run/tools/dcpmap @@ -0,0 +1,16 @@ +#!/bin/bash + +DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +top=$DIR/../.. +export LIBDCP_RESOURCES=$top + +export LD_LIBRARY_PATH=$top/build/src:build/asdcplib/src:$LD_LIBRARY_PATH +if [ "$1" == "--debug" ]; then + shift + gdb --args $top/build/tools/dcpmap "$@" +elif [ "$1" == "--valgrind" ]; then + shift + valgrind --tool="memcheck" --leak-check=full --show-reachable=yes $top/build/tools/dcpmap "$@" +else + $top/build/tools/dcpmap "$@" +fi diff --git a/scripts/check-boilerplate b/scripts/check-boilerplate new file mode 100755 index 00000000..311f95b8 --- /dev/null +++ b/scripts/check-boilerplate @@ -0,0 +1,23 @@ +#!/usr/bin/python + +import glob +import os +import sys + +for file in glob.glob('src/*.h'): + for line in open(file).readlines(): + if line.find('@file') != -1: + filename = line.strip().split()[2] + if filename != file: + print(f'AWOOGA: {file} {filename}') + sys.exit(1) + elif line.find('ifndef') != -1: + guard = line.strip().split()[1] + if not guard.startswith('LIBDCP'): + print(f'AWOOGA: {file} {guard}') + sys.exit(1) + correct_guard = 'LIBDCP_' + os.path.basename(file).upper().replace('.', '_') + if guard != correct_guard: + print(f'AWOOGA: {file} {guard} {correct_guard}') + sys.exit(1) + diff --git a/src/asset_factory.cc b/src/asset_factory.cc index be4f6b49..26811366 100644 --- a/src/asset_factory.cc +++ b/src/asset_factory.cc @@ -40,11 +40,12 @@ #include "asset_factory.h" #include "atmos_asset.h" #include "compose.hpp" -#include "mono_picture_asset.h" +#include "mono_j2k_picture_asset.h" +#include "mono_mpeg2_picture_asset.h" #include "smpte_subtitle_asset.h" #include "sound_asset.h" -#include "stereo_picture_asset.h" -#include "stereo_picture_asset.h" +#include "stereo_j2k_picture_asset.h" +#include "stereo_j2k_picture_asset.h" #include <memory> @@ -61,21 +62,23 @@ dcp::asset_factory (boost::filesystem::path path, bool ignore_incorrect_picture_ */ ASDCP::EssenceType_t type; - auto const result = ASDCP::EssenceType(dcp::filesystem::fix_long_path(path).string().c_str(), type); + Kumu::FileReaderFactory factory; + auto const result = ASDCP::EssenceType(dcp::filesystem::fix_long_path(path).string().c_str(), type, factory); if (!ASDCP_SUCCESS(result)) { throw ReadError(String::compose("Could not find essence type (%1)", result.Message()), path.string()); } switch (type) { case ASDCP::ESS_UNKNOWN: + throw ReadError("Unknown asset type"); case ASDCP::ESS_MPEG2_VES: - throw ReadError ("MPEG2 video essences are not supported"); + return make_shared<MonoMPEG2PictureAsset>(path); case ASDCP::ESS_JPEG_2000: try { - return make_shared<MonoPictureAsset>(path); + return make_shared<MonoJ2KPictureAsset>(path); } catch (dcp::MXFFileError& e) { if (ignore_incorrect_picture_mxf_type && e.number() == ASDCP::RESULT_SFORMAT) { /* Tried to load it as mono but the error says it's stereo; try that instead */ - auto stereo = make_shared<StereoPictureAsset>(path); + auto stereo = make_shared<StereoJ2KPictureAsset>(path); if (stereo && found_threed_marked_as_twod) { *found_threed_marked_as_twod = true; } @@ -88,7 +91,7 @@ dcp::asset_factory (boost::filesystem::path path, bool ignore_incorrect_picture_ case ASDCP::ESS_PCM_24b_96k: return make_shared<SoundAsset>(path); case ASDCP::ESS_JPEG_2000_S: - return make_shared<StereoPictureAsset>(path); + return make_shared<StereoJ2KPictureAsset>(path); case ASDCP::ESS_TIMED_TEXT: return make_shared<SMPTESubtitleAsset>(path); case ASDCP::ESS_DCDATA_DOLBY_ATMOS: diff --git a/src/asset_list.h b/src/asset_list.h index 5c50495d..29563a8a 100644 --- a/src/asset_list.h +++ b/src/asset_list.h @@ -32,8 +32,8 @@ */ -#ifndef DCP_ASSET_LIST_H -#define DCP_ASSET_LIST_H +#ifndef LIBDCP_ASSET_LIST_H +#define LIBDCP_ASSET_LIST_H #include "types.h" diff --git a/src/asset_map.cc b/src/asset_map.cc index 281f27c3..0ee4b486 100644 --- a/src/asset_map.cc +++ b/src/asset_map.cc @@ -165,29 +165,29 @@ AssetMap::write_xml(boost::filesystem::path file) const DCP_ASSERT (false); } - root->add_child("Id")->add_child_text("urn:uuid:" + _id); + cxml::add_text_child(root, "Id", "urn:uuid:" + _id); if (_annotation_text) { - root->add_child("AnnotationText")->add_child_text(*_annotation_text); + cxml::add_text_child(root, "AnnotationText", *_annotation_text); } switch (_standard) { case Standard::INTEROP: - root->add_child("VolumeCount")->add_child_text("1"); - root->add_child("IssueDate")->add_child_text(_issue_date); - root->add_child("Issuer")->add_child_text(_issuer); - root->add_child("Creator")->add_child_text(_creator); + cxml::add_text_child(root, "VolumeCount", "1"); + cxml::add_text_child(root, "IssueDate", _issue_date); + cxml::add_text_child(root, "Issuer", _issuer); + cxml::add_text_child(root, "Creator", _creator); break; case Standard::SMPTE: - root->add_child("Creator")->add_child_text(_creator); - root->add_child("VolumeCount")->add_child_text("1"); - root->add_child("IssueDate")->add_child_text(_issue_date); - root->add_child("Issuer")->add_child_text(_issuer); + cxml::add_text_child(root, "Creator", _creator); + cxml::add_text_child(root, "VolumeCount", "1"); + cxml::add_text_child(root, "IssueDate", _issue_date); + cxml::add_text_child(root, "Issuer", _issuer); break; default: DCP_ASSERT (false); } - auto asset_list = root->add_child("AssetList"); + auto asset_list = cxml::add_child(root, "AssetList"); for (auto const& asset: _assets) { asset.write_xml(asset_list, file.parent_path()); } @@ -200,20 +200,20 @@ AssetMap::write_xml(boost::filesystem::path file) const void AssetMap::Asset::write_xml(xmlpp::Element* asset_list, boost::filesystem::path dcp_root_directory) const { - auto node = asset_list->add_child("Asset"); - node->add_child("Id")->add_child_text("urn:uuid:" + _id); + auto node = cxml::add_child(asset_list, "Asset"); + cxml::add_text_child(node, "Id", "urn:uuid:" + _id); if (_pkl) { - node->add_child("PackingList")->add_child_text("true"); + cxml::add_text_child(node, "PackingList", "true"); } - auto chunk_list = node->add_child("ChunkList"); - auto chunk = chunk_list->add_child("Chunk"); + auto chunk_list = cxml::add_child(node, "ChunkList"); + auto chunk = cxml::add_child(chunk_list, "Chunk"); auto relative_path = relative_to_root(filesystem::canonical(dcp_root_directory), filesystem::canonical(_path)); DCP_ASSERT(relative_path); - chunk->add_child("Path")->add_child_text(relative_path->generic_string()); - chunk->add_child("VolumeIndex")->add_child_text("1"); - chunk->add_child("Offset")->add_child_text("0"); - chunk->add_child("Length")->add_child_text(raw_convert<string>(filesystem::file_size(_path))); + cxml::add_text_child(chunk, "Path", relative_path->generic_string()); + cxml::add_text_child(chunk, "VolumeIndex", "1"); + cxml::add_text_child(chunk, "Offset", "0"); + cxml::add_text_child(chunk, "Length", raw_convert<string>(filesystem::file_size(_path))); } diff --git a/src/asset_reader.h b/src/asset_reader.h index c8953e09..091ac915 100644 --- a/src/asset_reader.h +++ b/src/asset_reader.h @@ -53,9 +53,9 @@ namespace dcp { class AtmosAsset; -class MonoPictureAsset; +class MonoJ2KPictureAsset; class SoundAsset; -class StereoPictureAsset; +class StereoJ2KPictureAsset; template <class R, class F> @@ -90,14 +90,16 @@ protected: private: friend class AtmosAsset; - friend class MonoPictureAsset; + friend class MonoJ2KPictureAsset; + friend class MonoMPEG2PictureAsset; friend class SoundAsset; - friend class StereoPictureAsset; + friend class StereoJ2KPictureAsset; - explicit AssetReader (Asset const * asset, boost::optional<Key> key, Standard standard) + AssetReader(Asset const * asset, boost::optional<Key> key, Standard standard) : _crypto_context (new DecryptionContext(key, standard)) { - _reader = new R (); + Kumu::FileReaderFactory factory; + _reader = new R(factory); DCP_ASSERT (asset->file()); auto const r = _reader->OpenRead(dcp::filesystem::fix_long_path(*asset->file()).string().c_str()); if (ASDCP_FAILURE(r)) { diff --git a/src/atmos_asset.cc b/src/atmos_asset.cc index 42a0774e..09a22c1e 100644 --- a/src/atmos_asset.cc +++ b/src/atmos_asset.cc @@ -42,6 +42,7 @@ #include "atmos_asset_writer.h" #include "exceptions.h" #include <asdcp/AS_DCP.h> +#include <asdcp/KM_fileio.h> using std::string; @@ -67,7 +68,8 @@ AtmosAsset::AtmosAsset (boost::filesystem::path file) : Asset (file) , MXF (Standard::SMPTE) { - ASDCP::ATMOS::MXFReader reader; + Kumu::FileReaderFactory factory; + ASDCP::ATMOS::MXFReader reader(factory); auto r = reader.OpenRead(dcp::filesystem::fix_long_path(file).string().c_str()); if (ASDCP_FAILURE (r)) { boost::throw_exception (MXFFileError("could not open MXF file for reading", file.string(), r)); diff --git a/src/behaviour.h b/src/behaviour.h new file mode 100644 index 00000000..1600966c --- /dev/null +++ b/src/behaviour.h @@ -0,0 +1,52 @@ +/* + Copyright (C) 2024 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. +*/ + + +#ifndef LIBDCP_BEHAVIOUR_H +#define LIBDCP_BEHAVIOUR_H + + +namespace dcp { + + +enum class Behaviour { + OVERWRITE_EXISTING, + MAKE_NEW +}; + + +} + + +#endif + diff --git a/src/certificate_chain.cc b/src/certificate_chain.cc index c4e3a9b0..2bbddc7f 100644 --- a/src/certificate_chain.cc +++ b/src/certificate_chain.cc @@ -606,47 +606,47 @@ CertificateChain::sign (xmlpp::Element* parent, Standard standard) const /* <Signer> */ parent->add_child_text(" "); - auto signer = parent->add_child("Signer"); + auto signer = cxml::add_child(parent, "Signer"); signer->set_namespace_declaration ("http://www.w3.org/2000/09/xmldsig#", "dsig"); - auto data = signer->add_child("X509Data", "dsig"); - auto serial_element = data->add_child("X509IssuerSerial", "dsig"); - serial_element->add_child("X509IssuerName", "dsig")->add_child_text (leaf().issuer()); - serial_element->add_child("X509SerialNumber", "dsig")->add_child_text (leaf().serial()); - data->add_child("X509SubjectName", "dsig")->add_child_text (leaf().subject()); + auto data = cxml::add_child(signer, "X509Data", string("dsig")); + auto serial_element = cxml::add_child(data, "X509IssuerSerial", string("dsig")); + cxml::add_child(serial_element, "X509IssuerName", string("dsig"))->add_child_text(leaf().issuer()); + cxml::add_child(serial_element, "X509SerialNumber", string("dsig"))->add_child_text(leaf().serial()); + cxml::add_child(data, "X509SubjectName", string("dsig"))->add_child_text(leaf().subject()); indent (signer, 2); /* <Signature> */ parent->add_child_text("\n "); - auto signature = parent->add_child("Signature"); + auto signature = cxml::add_child(parent, "Signature"); signature->set_namespace_declaration ("http://www.w3.org/2000/09/xmldsig#", "dsig"); signature->set_namespace ("dsig"); parent->add_child_text("\n"); - auto signed_info = signature->add_child ("SignedInfo", "dsig"); - signed_info->add_child("CanonicalizationMethod", "dsig")->set_attribute ("Algorithm", "http://www.w3.org/TR/2001/REC-xml-c14n-20010315"); + auto signed_info = cxml::add_child(signature, "SignedInfo", string("dsig")); + cxml::add_child(signed_info, "CanonicalizationMethod", string("dsig"))->set_attribute("Algorithm", "http://www.w3.org/TR/2001/REC-xml-c14n-20010315"); if (standard == Standard::INTEROP) { - signed_info->add_child("SignatureMethod", "dsig")->set_attribute("Algorithm", "http://www.w3.org/2000/09/xmldsig#rsa-sha1"); + cxml::add_child(signed_info, "SignatureMethod", string("dsig"))->set_attribute("Algorithm", "http://www.w3.org/2000/09/xmldsig#rsa-sha1"); } else { - signed_info->add_child("SignatureMethod", "dsig")->set_attribute("Algorithm", "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"); + cxml::add_child(signed_info, "SignatureMethod", string("dsig"))->set_attribute("Algorithm", "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"); } - auto reference = signed_info->add_child("Reference", "dsig"); + auto reference = cxml::add_child(signed_info, "Reference", string("dsig")); reference->set_attribute ("URI", ""); - auto transforms = reference->add_child("Transforms", "dsig"); - transforms->add_child("Transform", "dsig")->set_attribute ( + auto transforms = cxml::add_child(reference, "Transforms", string("dsig")); + cxml::add_child(transforms, "Transform", string("dsig"))->set_attribute( "Algorithm", "http://www.w3.org/2000/09/xmldsig#enveloped-signature" ); - reference->add_child("DigestMethod", "dsig")->set_attribute("Algorithm", "http://www.w3.org/2000/09/xmldsig#sha1"); + cxml::add_child(reference, "DigestMethod", string("dsig"))->set_attribute("Algorithm", "http://www.w3.org/2000/09/xmldsig#sha1"); /* This will be filled in by the signing later */ - reference->add_child("DigestValue", "dsig"); + cxml::add_child(reference, "DigestValue", string("dsig")); - signature->add_child("SignatureValue", "dsig"); - signature->add_child("KeyInfo", "dsig"); + cxml::add_child(signature, "SignatureValue", string("dsig")); + cxml::add_child(signature, "KeyInfo", string("dsig")); add_signature_value (signature, "dsig", true); } @@ -655,19 +655,20 @@ void CertificateChain::add_signature_value (xmlpp::Element* parent, string ns, bool add_indentation) const { cxml::Node cp (parent); - auto key_info = cp.node_child("KeyInfo")->node(); + auto key_info = dynamic_cast<xmlpp::Element*>(cp.node_child("KeyInfo")->node()); + DCP_ASSERT(key_info); /* Add the certificate chain to the KeyInfo child node of parent */ for (auto const& i: leaf_to_root()) { - auto data = key_info->add_child("X509Data", ns); + auto data = cxml::add_child(key_info, "X509Data", ns); { - auto serial = data->add_child("X509IssuerSerial", ns); - serial->add_child("X509IssuerName", ns)->add_child_text (i.issuer ()); - serial->add_child("X509SerialNumber", ns)->add_child_text (i.serial ()); + auto serial = cxml::add_child(data, "X509IssuerSerial", ns); + cxml::add_child(serial, "X509IssuerName", ns)->add_child_text(i.issuer()); + cxml::add_child(serial, "X509SerialNumber", ns)->add_child_text(i.serial()); } - data->add_child("X509Certificate", ns)->add_child_text (i.certificate()); + cxml::add_child(data, "X509Certificate", ns)->add_child_text(i.certificate()); } auto signature_context = xmlSecDSigCtxCreate (0); diff --git a/src/chromaticity.h b/src/chromaticity.h index 41bb8fda..52edd7bf 100644 --- a/src/chromaticity.h +++ b/src/chromaticity.h @@ -37,8 +37,8 @@ */ -#ifndef DCP_CHROMATICITY_H -#define DCP_CHROMATICITY_H +#ifndef LIBDCP_CHROMATICITY_H +#define LIBDCP_CHROMATICITY_H #include <cmath> diff --git a/src/colour_conversion.h b/src/colour_conversion.h index 8501699a..29140541 100644 --- a/src/colour_conversion.h +++ b/src/colour_conversion.h @@ -37,8 +37,8 @@ */ -#ifndef DCP_COLOUR_CONVERSION_H -#define DCP_COLOUR_CONVERSION_H +#ifndef LIBDCP_COLOUR_CONVERSION_H +#define LIBDCP_COLOUR_CONVERSION_H #include "chromaticity.h" @@ -213,15 +213,15 @@ CPL::write_xml(boost::filesystem::path file, shared_ptr<const CertificateChain> root = doc.create_root_node ("CompositionPlaylist", cpl_smpte_ns); } - root->add_child("Id")->add_child_text ("urn:uuid:" + _id); + cxml::add_text_child(root, "Id", "urn:uuid:" + _id); if (_annotation_text) { - root->add_child("AnnotationText")->add_child_text (*_annotation_text); + cxml::add_text_child(root, "AnnotationText", *_annotation_text); } - root->add_child("IssueDate")->add_child_text (_issue_date); - root->add_child("Issuer")->add_child_text (_issuer); - root->add_child("Creator")->add_child_text (_creator); - root->add_child("ContentTitleText")->add_child_text (_content_title_text); - auto content_kind = root->add_child("ContentKind"); + cxml::add_text_child(root, "IssueDate", _issue_date); + cxml::add_text_child(root, "Issuer", _issuer); + cxml::add_text_child(root, "Creator", _creator); + cxml::add_text_child(root, "ContentTitleText", _content_title_text); + auto content_kind = cxml::add_child(root, "ContentKind"); content_kind->add_child_text(_content_kind.name()); if (_content_kind.scope()) { content_kind->set_attribute("scope", *_content_kind.scope()); @@ -233,12 +233,12 @@ CPL::write_xml(boost::filesystem::path file, shared_ptr<const CertificateChain> _content_versions[0].as_xml (root); } - auto rating_list = root->add_child("RatingList"); + auto rating_list = cxml::add_child(root, "RatingList"); for (auto i: _ratings) { - i.as_xml (rating_list->add_child("Rating")); + i.as_xml(cxml::add_child(rating_list, "Rating")); } - auto reel_list = root->add_child ("ReelList"); + auto reel_list = cxml::add_child(root, "ReelList"); if (_reels.empty()) { throw NoReelsError (); @@ -403,27 +403,27 @@ CPL::write_mca_subdescriptors(xmlpp::Element* parent, shared_ptr<const SoundAsse reinterpret_cast<ASDCP::MXF::InterchangeObject**>(&soundfield) ); if (KM_SUCCESS(r)) { - auto mca_subs = parent->add_child("mca:MCASubDescriptors"); + auto mca_subs = cxml::add_child(parent, "mca:MCASubDescriptors"); mca_subs->set_namespace_declaration (mca_sub_descriptors_ns, "mca"); mca_subs->set_namespace_declaration (smpte_395_ns, "r0"); mca_subs->set_namespace_declaration (smpte_335_ns, "r1"); - auto sf = mca_subs->add_child("SoundfieldGroupLabelSubDescriptor", "r0"); + auto sf = cxml::add_child(mca_subs, "SoundfieldGroupLabelSubDescriptor", string("r0")); char buffer[64]; soundfield->InstanceUID.EncodeString(buffer, sizeof(buffer)); - sf->add_child("InstanceID", "r1")->add_child_text("urn:uuid:" + string(buffer)); + cxml::add_child(sf, "InstanceID", string("r1"))->add_child_text("urn:uuid:" + string(buffer)); soundfield->MCALabelDictionaryID.EncodeString(buffer, sizeof(buffer)); - sf->add_child("MCALabelDictionaryID", "r1")->add_child_text("urn:smpte:ul:" + string(buffer)); + cxml::add_child(sf, "MCALabelDictionaryID", string("r1"))->add_child_text("urn:smpte:ul:" + string(buffer)); soundfield->MCALinkID.EncodeString(buffer, sizeof(buffer)); - sf->add_child("MCALinkID", "r1")->add_child_text("urn:uuid:" + string(buffer)); + cxml::add_child(sf, "MCALinkID", string("r1"))->add_child_text("urn:uuid:" + string(buffer)); soundfield->MCATagSymbol.EncodeString(buffer, sizeof(buffer)); - sf->add_child("MCATagSymbol", "r1")->add_child_text(buffer); + cxml::add_child(sf, "MCATagSymbol", string("r1"))->add_child_text(buffer); if (!soundfield->MCATagName.empty()) { soundfield->MCATagName.get().EncodeString(buffer, sizeof(buffer)); - sf->add_child("MCATagName", "r1")->add_child_text(buffer); + cxml::add_child(sf, "MCATagName", string("r1"))->add_child_text(buffer); } if (!soundfield->RFC5646SpokenLanguage.empty()) { soundfield->RFC5646SpokenLanguage.get().EncodeString(buffer, sizeof(buffer)); - sf->add_child("RFC5646SpokenLanguage", "r1")->add_child_text(buffer); + cxml::add_child(sf, "RFC5646SpokenLanguage", string("r1"))->add_child_text(buffer); } /* Find the MCA subdescriptors in the MXF so that we can also write them here */ @@ -435,29 +435,29 @@ CPL::write_mca_subdescriptors(xmlpp::Element* parent, shared_ptr<const SoundAsse for (auto i: channels) { auto channel = reinterpret_cast<ASDCP::MXF::AudioChannelLabelSubDescriptor*>(i); - auto ch = mca_subs->add_child("AudioChannelLabelSubDescriptor", "r0"); + auto ch = cxml::add_child(mca_subs, "AudioChannelLabelSubDescriptor", string("r0")); channel->InstanceUID.EncodeString(buffer, sizeof(buffer)); - ch->add_child("InstanceID", "r1")->add_child_text("urn:uuid:" + string(buffer)); + cxml::add_child(ch, "InstanceID", string("r1"))->add_child_text("urn:uuid:" + string(buffer)); channel->MCALabelDictionaryID.EncodeString(buffer, sizeof(buffer)); - ch->add_child("MCALabelDictionaryID", "r1")->add_child_text("urn:smpte:ul:" + string(buffer)); + cxml::add_child(ch, "MCALabelDictionaryID", string("r1"))->add_child_text("urn:smpte:ul:" + string(buffer)); channel->MCALinkID.EncodeString(buffer, sizeof(buffer)); - ch->add_child("MCALinkID", "r1")->add_child_text("urn:uuid:" + string(buffer)); + cxml::add_child(ch, "MCALinkID", string("r1"))->add_child_text("urn:uuid:" + string(buffer)); channel->MCATagSymbol.EncodeString(buffer, sizeof(buffer)); - ch->add_child("MCATagSymbol", "r1")->add_child_text(buffer); + cxml::add_child(ch, "MCATagSymbol", string("r1"))->add_child_text(buffer); if (!channel->MCATagName.empty()) { channel->MCATagName.get().EncodeString(buffer, sizeof(buffer)); - ch->add_child("MCATagName", "r1")->add_child_text(buffer); + cxml::add_child(ch, "MCATagName", string("r1"))->add_child_text(buffer); } if (!channel->MCAChannelID.empty()) { - ch->add_child("MCAChannelID", "r1")->add_child_text(raw_convert<string>(channel->MCAChannelID.get())); + cxml::add_child(ch, "MCAChannelID", string("r1"))->add_child_text(raw_convert<string>(channel->MCAChannelID.get())); } if (!channel->RFC5646SpokenLanguage.empty()) { channel->RFC5646SpokenLanguage.get().EncodeString(buffer, sizeof(buffer)); - ch->add_child("RFC5646SpokenLanguage", "r1")->add_child_text(buffer); + cxml::add_child(ch, "RFC5646SpokenLanguage", string("r1"))->add_child_text(buffer); } if (!channel->SoundfieldGroupLinkID.empty()) { channel->SoundfieldGroupLinkID.get().EncodeString(buffer, sizeof(buffer)); - ch->add_child("SoundfieldGroupLinkID", "r1")->add_child_text("urn:uuid:" + string(buffer)); + cxml::add_child(ch, "SoundfieldGroupLinkID", string("r1"))->add_child_text("urn:uuid:" + string(buffer)); } } } @@ -481,16 +481,16 @@ CPL::maybe_write_composition_metadata_asset(xmlpp::Element* node, bool include_m return; } - auto meta = node->add_child("meta:CompositionMetadataAsset"); + auto meta = cxml::add_child(node, "meta:CompositionMetadataAsset"); meta->set_namespace_declaration (cpl_metadata_ns, "meta"); - meta->add_child("Id")->add_child_text("urn:uuid:" + _cpl_metadata_id); + cxml::add_text_child(meta, "Id", "urn:uuid:" + _cpl_metadata_id); auto mp = _reels.front()->main_picture(); - meta->add_child("EditRate")->add_child_text(mp->edit_rate().as_string()); - meta->add_child("IntrinsicDuration")->add_child_text(raw_convert<string>(mp->intrinsic_duration())); + cxml::add_text_child(meta, "EditRate", mp->edit_rate().as_string()); + cxml::add_text_child(meta, "IntrinsicDuration", raw_convert<string>(mp->intrinsic_duration())); - auto fctt = meta->add_child("FullContentTitleText", "meta"); + auto fctt = cxml::add_child(meta, "FullContentTitleText", string("meta")); if (_full_content_title_text && !_full_content_title_text->empty()) { fctt->add_child_text (*_full_content_title_text); } @@ -499,11 +499,11 @@ CPL::maybe_write_composition_metadata_asset(xmlpp::Element* node, bool include_m } if (_release_territory) { - meta->add_child("ReleaseTerritory", "meta")->add_child_text(*_release_territory); + cxml::add_child(meta, "ReleaseTerritory", string("meta"))->add_child_text(*_release_territory); } if (_version_number) { - xmlpp::Element* vn = meta->add_child("VersionNumber", "meta"); + auto vn = cxml::add_child(meta, "VersionNumber", string("meta")); vn->add_child_text(raw_convert<string>(*_version_number)); if (_status) { vn->set_attribute("status", status_to_string(*_status)); @@ -511,19 +511,19 @@ CPL::maybe_write_composition_metadata_asset(xmlpp::Element* node, bool include_m } if (_chain) { - meta->add_child("Chain", "meta")->add_child_text(*_chain); + cxml::add_child(meta, "Chain", string("meta"))->add_child_text(*_chain); } if (_distributor) { - meta->add_child("Distributor", "meta")->add_child_text(*_distributor); + cxml::add_child(meta, "Distributor", string("meta"))->add_child_text(*_distributor); } if (_facility) { - meta->add_child("Facility", "meta")->add_child_text(*_facility); + cxml::add_child(meta, "Facility", string("meta"))->add_child_text(*_facility); } if (_content_versions.size() > 1) { - xmlpp::Element* vc = meta->add_child("AlternateContentVersionList", "meta"); + auto vc = cxml::add_child(meta, "AlternateContentVersionList", string("meta")); for (size_t i = 1; i < _content_versions.size(); ++i) { _content_versions[i].as_xml (vc); } @@ -534,17 +534,17 @@ CPL::maybe_write_composition_metadata_asset(xmlpp::Element* node, bool include_m } if (_main_sound_configuration) { - meta->add_child("MainSoundConfiguration", "meta")->add_child_text(_main_sound_configuration->to_string()); + cxml::add_child(meta, "MainSoundConfiguration", string("meta"))->add_child_text(_main_sound_configuration->to_string()); } - meta->add_child("MainSoundSampleRate", "meta")->add_child_text(raw_convert<string>(*_main_sound_sample_rate) + " 1"); + cxml::add_child(meta, "MainSoundSampleRate", string("meta"))->add_child_text(raw_convert<string>(*_main_sound_sample_rate) + " 1"); - auto stored = meta->add_child("MainPictureStoredArea", "meta"); - stored->add_child("Width", "meta")->add_child_text(raw_convert<string>(_main_picture_stored_area->width)); - stored->add_child("Height", "meta")->add_child_text(raw_convert<string>(_main_picture_stored_area->height)); + auto stored = cxml::add_child(meta, "MainPictureStoredArea", string("meta")); + cxml::add_child(stored, "Width", string("meta"))->add_child_text(raw_convert<string>(_main_picture_stored_area->width)); + cxml::add_child(stored, "Height", string("meta"))->add_child_text(raw_convert<string>(_main_picture_stored_area->height)); - auto active = meta->add_child("MainPictureActiveArea", "meta"); - active->add_child("Width", "meta")->add_child_text(raw_convert<string>(_main_picture_active_area->width)); - active->add_child("Height", "meta")->add_child_text(raw_convert<string>(_main_picture_active_area->height)); + auto active = cxml::add_child(meta, "MainPictureActiveArea", string("meta")); + cxml::add_child(active, "Width", string("meta"))->add_child_text(raw_convert<string>(_main_picture_active_area->width)); + cxml::add_child(active, "Height", string("meta"))->add_child_text(raw_convert<string>(_main_picture_active_area->height)); optional<string> first_subtitle_language; for (auto i: _reels) { @@ -567,18 +567,18 @@ CPL::maybe_write_composition_metadata_asset(xmlpp::Element* node, bool include_m } lang += i; } - meta->add_child("MainSubtitleLanguageList", "meta")->add_child_text(lang); + cxml::add_child(meta, "MainSubtitleLanguageList", string("meta"))->add_child_text(lang); } - auto metadata_list = meta->add_child("ExtensionMetadataList", "meta"); + auto metadata_list = cxml::add_child(meta, "ExtensionMetadataList", string("meta")); auto add_extension_metadata = [metadata_list](string scope, string name, string property_name, string property_value) { - auto extension = metadata_list->add_child("ExtensionMetadata", "meta"); + auto extension = cxml::add_child(metadata_list, "ExtensionMetadata", string("meta")); extension->set_attribute("scope", scope); - extension->add_child("Name", "meta")->add_child_text(name); - auto property = extension->add_child("PropertyList", "meta")->add_child("Property", "meta"); - property->add_child("Name", "meta")->add_child_text(property_name); - property->add_child("Value", "meta")->add_child_text(property_value); + cxml::add_child(extension, "Name", string("meta"))->add_child_text(name); + auto property = cxml::add_child(cxml::add_child(extension, "PropertyList", string("meta")), "Property", string("meta")); + cxml::add_child(property, "Name", string("meta"))->add_child_text(property_name); + cxml::add_child(property, "Value", string("meta"))->add_child_text(property_value); }; /* SMPTE Bv2.1 8.6.3 */ @@ -51,15 +51,16 @@ #include "font_asset.h" #include "interop_subtitle_asset.h" #include "metadata.h" -#include "mono_picture_asset.h" -#include "picture_asset.h" +#include "mono_j2k_picture_asset.h" +#include "mono_mpeg2_picture_asset.h" +#include "j2k_picture_asset.h" #include "pkl.h" #include "raw_convert.h" #include "reel_asset.h" #include "reel_subtitle_asset.h" #include "smpte_subtitle_asset.h" #include "sound_asset.h" -#include "stereo_picture_asset.h" +#include "stereo_j2k_picture_asset.h" #include "util.h" #include "verify.h" #include "warnings.h" @@ -246,7 +247,8 @@ DCP::read (vector<dcp::VerificationNote>* notes, bool ignore_incorrect_picture_m other_assets.push_back (make_shared<InteropSubtitleAsset>(path)); } } else if ( - *pkl_type == remove_parameters(PictureAsset::static_pkl_type(standard)) || + *pkl_type == remove_parameters(J2KPictureAsset::static_pkl_type(standard)) || + *pkl_type == remove_parameters(MPEG2PictureAsset::static_pkl_type(standard)) || *pkl_type == remove_parameters(SoundAsset::static_pkl_type(standard)) || *pkl_type == remove_parameters(AtmosAsset::static_pkl_type(standard)) || *pkl_type == remove_parameters(SMPTESubtitleAsset::static_pkl_type(standard)) @@ -441,7 +443,7 @@ DCP::write_volindex (Standard standard) const DCP_ASSERT (false); } - root->add_child("Index")->add_child_text ("1"); + cxml::add_text_child(root, "Index", "1"); doc.write_to_file_formatted(dcp::filesystem::fix_long_path(p).string(), "UTF-8"); } diff --git a/src/dcp_time.h b/src/dcp_time.h index 506dafda..21b59921 100644 --- a/src/dcp_time.h +++ b/src/dcp_time.h @@ -37,8 +37,8 @@ */ -#ifndef LIBDCP_TIME_H -#define LIBDCP_TIME_H +#ifndef LIBDCP_DCP_TIME_H +#define LIBDCP_DCP_TIME_H #include "types.h" diff --git a/src/encrypted_kdm.cc b/src/encrypted_kdm.cc index 465a657d..d1089c0b 100644 --- a/src/encrypted_kdm.cc +++ b/src/encrypted_kdm.cc @@ -44,6 +44,7 @@ #include "file.h" #include "util.h" #include <libcxml/cxml.h> +#include <libxml++/attributenode.h> #include <libxml++/document.h> #include <libxml++/nodes/element.h> #include <libxml/parser.h> @@ -85,8 +86,8 @@ public: void as_xml (xmlpp::Element* node) const { - node->add_child("X509IssuerName", "ds")->add_child_text (x509_issuer_name); - node->add_child("X509SerialNumber", "ds")->add_child_text (x509_serial_number); + cxml::add_child(node, "X509IssuerName", string("ds"))->add_child_text(x509_issuer_name); + cxml::add_child(node, "X509SerialNumber", string("ds"))->add_child_text(x509_serial_number); } string x509_issuer_name; @@ -108,8 +109,8 @@ public: void as_xml (xmlpp::Element* node) const { - x509_issuer_serial.as_xml (node->add_child ("X509IssuerSerial", "ds")); - node->add_child("X509Certificate", "ds")->add_child_text (x509_certificate); + x509_issuer_serial.as_xml(cxml::add_child(node, "X509IssuerSerial", string("ds"))); + cxml::add_child(node, "X509Certificate", string("ds"))->add_child_text(x509_certificate); } Signer x509_issuer_serial; @@ -136,8 +137,8 @@ public: void as_xml (xmlpp::Element* node) const { node->set_attribute ("URI", uri); - node->add_child("DigestMethod", "ds")->set_attribute ("Algorithm", "http://www.w3.org/2001/04/xmlenc#sha256"); - node->add_child("DigestValue", "ds")->add_child_text (digest_value); + cxml::add_child(node, "DigestMethod", string("ds"))->set_attribute("Algorithm", "http://www.w3.org/2001/04/xmlenc#sha256"); + cxml::add_child(node, "DigestValue", string("ds"))->add_child_text(digest_value); } string uri; @@ -168,16 +169,16 @@ public: void as_xml (xmlpp::Element* node) const { - node->add_child ("CanonicalizationMethod", "ds")->set_attribute ( + cxml::add_child(node, "CanonicalizationMethod", string("ds"))->set_attribute( "Algorithm", "http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments" ); - node->add_child ("SignatureMethod", "ds")->set_attribute ( + cxml::add_child(node, "SignatureMethod", string("ds"))->set_attribute( "Algorithm", "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256" ); - authenticated_public.as_xml (node->add_child ("Reference", "ds")); - authenticated_private.as_xml (node->add_child ("Reference", "ds")); + authenticated_public.as_xml(cxml::add_child(node, "Reference", string("ds"))); + authenticated_private.as_xml(cxml::add_child(node, "Reference", string("ds"))); } private: @@ -200,14 +201,14 @@ public: } } - void as_xml (xmlpp::Node* node) const + void as_xml(xmlpp::Element* element) const { - signed_info.as_xml (node->add_child ("SignedInfo", "ds")); - node->add_child("SignatureValue", "ds")->add_child_text (signature_value); + signed_info.as_xml(cxml::add_child(element, "SignedInfo", string("ds"))); + cxml::add_child(element, "SignatureValue", string("ds"))->add_child_text(signature_value); - auto key_info_node = node->add_child("KeyInfo", "ds"); + auto key_info_node = cxml::add_child(element, "KeyInfo", string("ds")); for (auto i: x509_data) { - i.as_xml (key_info_node->add_child("X509Data", "ds")); + i.as_xml(cxml::add_child(key_info_node, "X509Data", string("ds"))); } } @@ -229,22 +230,22 @@ public: } } - void as_xml (xmlpp::Element* node, map<string, xmlpp::Attribute *>& references) const + void as_xml (xmlpp::Element* node, map<string, xmlpp::AttributeNode*>& references) const { - references["ID_AuthenticatedPrivate"] = node->set_attribute ("Id", "ID_AuthenticatedPrivate"); + references["ID_AuthenticatedPrivate"] = dynamic_cast<xmlpp::AttributeNode*>(node->set_attribute("Id", "ID_AuthenticatedPrivate")); for (auto i: encrypted_key) { - auto encrypted_key = node->add_child ("EncryptedKey", "enc"); + auto encrypted_key = cxml::add_child(node, "EncryptedKey", string("enc")); /* XXX: hack for testing with Dolby */ encrypted_key->set_namespace_declaration ("http://www.w3.org/2001/04/xmlenc#", "enc"); - auto encryption_method = encrypted_key->add_child("EncryptionMethod", "enc"); + auto encryption_method = cxml::add_child(encrypted_key, "EncryptionMethod", string("enc")); encryption_method->set_attribute ("Algorithm", "http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p"); - auto digest_method = encryption_method->add_child ("DigestMethod", "ds"); + auto digest_method = cxml::add_child(encryption_method, "DigestMethod", string("ds")); /* XXX: hack for testing with Dolby */ digest_method->set_namespace_declaration ("http://www.w3.org/2000/09/xmldsig#", "ds"); digest_method->set_attribute ("Algorithm", "http://www.w3.org/2000/09/xmldsig#sha1"); - auto cipher_data = encrypted_key->add_child("CipherData", "enc"); - cipher_data->add_child("CipherValue", "enc")->add_child_text (i); + auto cipher_data = cxml::add_child(encrypted_key, "CipherData", string("enc")); + cxml::add_child(cipher_data, "CipherValue", string("enc"))->add_child_text(i); } } @@ -271,9 +272,9 @@ public: void as_xml (xmlpp::Element* node) const { - auto type = node->add_child("KeyType"); + auto type = cxml::add_child(node, "KeyType"); type->add_child_text (key_type); - node->add_child("KeyId")->add_child_text ("urn:uuid:" + key_id); + cxml::add_text_child(node, "KeyId", "urn:uuid:" + key_id); /* XXX: this feels like a bit of a hack */ if (key_type == "MDEK") { type->set_attribute ("scope", "http://www.dolby.com/cp850/2012/KDM#kdm-key-type"); @@ -302,7 +303,7 @@ public: void as_xml (xmlpp::Element* node) const { for (auto const& i: typed_key_id) { - i.as_xml (node->add_child("TypedKeyId")); + i.as_xml(cxml::add_child(node, ("TypedKeyId"))); } } @@ -326,13 +327,13 @@ public: void as_xml (xmlpp::Element* node) const { - node->add_child ("DeviceListIdentifier")->add_child_text ("urn:uuid:" + device_list_identifier); + cxml::add_text_child(node, "DeviceListIdentifier", "urn:uuid:" + device_list_identifier); if (device_list_description) { - node->add_child ("DeviceListDescription")->add_child_text (device_list_description.get()); + cxml::add_text_child(node, "DeviceListDescription", device_list_description.get()); } - auto device_list = node->add_child ("DeviceList"); + auto device_list = cxml::add_child(node, "DeviceList"); for (auto i: certificate_thumbprints) { - device_list->add_child("CertificateThumbprint")->add_child_text (i); + cxml::add_text_child(device_list, "CertificateThumbprint", i); } } @@ -357,8 +358,8 @@ public: void as_xml (xmlpp::Element* node) const { - node->add_child("X509IssuerName", "ds")->add_child_text (x509_issuer_name); - node->add_child("X509SerialNumber", "ds")->add_child_text (x509_serial_number); + cxml::add_child(node, "X509IssuerName", string("ds"))->add_child_text(x509_issuer_name); + cxml::add_child(node, "X509SerialNumber", string("ds"))->add_child_text(x509_serial_number); } string x509_issuer_name; @@ -380,8 +381,8 @@ public: void as_xml (xmlpp::Element* node) const { - x509_issuer_serial.as_xml (node->add_child ("X509IssuerSerial")); - node->add_child("X509SubjectName")->add_child_text (x509_subject_name); + x509_issuer_serial.as_xml(cxml::add_child(node, "X509IssuerSerial")); + cxml::add_text_child(node, "X509SubjectName", x509_subject_name); } X509IssuerSerial x509_issuer_serial; @@ -428,30 +429,30 @@ public: { node->set_attribute ("xmlns", "http://www.smpte-ra.org/schemas/430-1/2006/KDM"); - recipient.as_xml (node->add_child ("Recipient")); - node->add_child("CompositionPlaylistId")->add_child_text ("urn:uuid:" + composition_playlist_id); - node->add_child("ContentTitleText")->add_child_text (content_title_text); + recipient.as_xml(cxml::add_child(node, "Recipient")); + cxml::add_text_child(node, "CompositionPlaylistId", "urn:uuid:" + composition_playlist_id); + cxml::add_text_child(node, "ContentTitleText", content_title_text); if (content_authenticator) { - node->add_child("ContentAuthenticator")->add_child_text (content_authenticator.get ()); + cxml::add_text_child(node, "ContentAuthenticator", content_authenticator.get()); } - node->add_child("ContentKeysNotValidBefore")->add_child_text (not_valid_before.as_string ()); - node->add_child("ContentKeysNotValidAfter")->add_child_text (not_valid_after.as_string ()); + cxml::add_text_child(node, "ContentKeysNotValidBefore", not_valid_before.as_string()); + cxml::add_text_child(node, "ContentKeysNotValidAfter", not_valid_after.as_string()); if (authorized_device_info) { - authorized_device_info->as_xml (node->add_child ("AuthorizedDeviceInfo")); + authorized_device_info->as_xml(cxml::add_child(node, "AuthorizedDeviceInfo")); } - key_id_list.as_xml (node->add_child ("KeyIdList")); + key_id_list.as_xml(cxml::add_child(node, "KeyIdList")); if (disable_forensic_marking_picture || disable_forensic_marking_audio) { - auto forensic_mark_flag_list = node->add_child ("ForensicMarkFlagList"); + auto forensic_mark_flag_list = cxml::add_child(node, "ForensicMarkFlagList"); if (disable_forensic_marking_picture) { - forensic_mark_flag_list->add_child("ForensicMarkFlag")->add_child_text(picture_disable); + cxml::add_text_child(forensic_mark_flag_list, "ForensicMarkFlag", picture_disable); } if (disable_forensic_marking_audio) { auto mrkflg = audio_disable; if (*disable_forensic_marking_audio > 0) { mrkflg += String::compose ("-above-channel-%1", *disable_forensic_marking_audio); } - forensic_mark_flag_list->add_child("ForensicMarkFlag")->add_child_text (mrkflg); + cxml::add_text_child(forensic_mark_flag_list, "ForensicMarkFlag", mrkflg); } } } @@ -490,7 +491,7 @@ public: void as_xml (xmlpp::Element* node) const { - kdm_required_extensions.as_xml (node->add_child ("KDMRequiredExtensions")); + kdm_required_extensions.as_xml(cxml::add_child(node, "KDMRequiredExtensions")); } KDMRequiredExtensions kdm_required_extensions; @@ -517,21 +518,21 @@ public: } - void as_xml (xmlpp::Element* node, map<string, xmlpp::Attribute *>& references) const + void as_xml (xmlpp::Element* node, map<string, xmlpp::AttributeNode*>& references) const { - references["ID_AuthenticatedPublic"] = node->set_attribute ("Id", "ID_AuthenticatedPublic"); + references["ID_AuthenticatedPublic"] = dynamic_cast<xmlpp::AttributeNode*>(node->set_attribute("Id", "ID_AuthenticatedPublic")); - node->add_child("MessageId")->add_child_text ("urn:uuid:" + message_id); - node->add_child("MessageType")->add_child_text ("http://www.smpte-ra.org/430-1/2006/KDM#kdm-key-type"); + cxml::add_text_child(node, "MessageId", "urn:uuid:" + message_id); + cxml::add_text_child(node, "MessageType", "http://www.smpte-ra.org/430-1/2006/KDM#kdm-key-type"); if (annotation_text) { - node->add_child("AnnotationText")->add_child_text (annotation_text.get ()); + cxml::add_text_child(node, "AnnotationText", annotation_text.get()); } - node->add_child("IssueDate")->add_child_text (issue_date); + cxml::add_text_child(node, "IssueDate", issue_date); - signer.as_xml (node->add_child ("Signer")); - required_extensions.as_xml (node->add_child ("RequiredExtensions")); + signer.as_xml(cxml::add_child(node, "Signer")); + required_extensions.as_xml(cxml::add_child(node, "RequiredExtensions")); - node->add_child ("NonCriticalExtensions"); + cxml::add_child(node, "NonCriticalExtensions"); } string message_id; @@ -563,14 +564,14 @@ public: shared_ptr<xmlpp::Document> as_xml () const { - shared_ptr<xmlpp::Document> document (new xmlpp::Document ()); - xmlpp::Element* root = document->create_root_node ("DCinemaSecurityMessage", "http://www.smpte-ra.org/schemas/430-3/2006/ETM"); + auto document = make_shared<xmlpp::Document>(); + auto root = document->create_root_node("DCinemaSecurityMessage", "http://www.smpte-ra.org/schemas/430-3/2006/ETM"); root->set_namespace_declaration ("http://www.w3.org/2000/09/xmldsig#", "ds"); root->set_namespace_declaration ("http://www.w3.org/2001/04/xmlenc#", "enc"); - map<string, xmlpp::Attribute *> references; - authenticated_public.as_xml (root->add_child ("AuthenticatedPublic"), references); - authenticated_private.as_xml (root->add_child ("AuthenticatedPrivate"), references); - signature.as_xml (root->add_child ("Signature", "ds")); + map<string, xmlpp::AttributeNode*> references; + authenticated_public.as_xml(cxml::add_child(root, "AuthenticatedPublic"), references); + authenticated_private.as_xml(cxml::add_child(root, "AuthenticatedPrivate"), references); + signature.as_xml(cxml::add_child(root, "Signature", string("ds"))); for (auto i: references) { xmlAddID (0, document->cobj(), (const xmlChar *) i.first.c_str(), i.second->cobj()); diff --git a/src/exceptions.h b/src/exceptions.h index 8d85f02a..3858b763 100644 --- a/src/exceptions.h +++ b/src/exceptions.h @@ -140,6 +140,33 @@ public: }; +class MPEG2CodecError : public MiscError +{ +public: + explicit MPEG2CodecError(std::string message) + : MiscError(message) + {} +}; + + +class MPEG2DecompressionError : public ReadError +{ +public: + explicit MPEG2DecompressionError(std::string message) + : ReadError(message) + {} +}; + + +class MPEG2CompressionError : public MiscError +{ +public: + explicit MPEG2CompressionError(std::string message) + : MiscError(message) + {} +}; + + class BadContentKindError : public ReadError { public: diff --git a/src/ffmpeg_image.cc b/src/ffmpeg_image.cc new file mode 100644 index 00000000..deebe1b6 --- /dev/null +++ b/src/ffmpeg_image.cc @@ -0,0 +1,122 @@ +/* + Copyright (C) 2024 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. +*/ + + +#include "ffmpeg_image.h" +#include "types.h" +extern "C" { +#include <libavutil/pixfmt.h> +} + + +using namespace dcp; + + +FFmpegImage::FFmpegImage(int64_t pts) +{ + auto const width = size().width; + auto const height = size().height; + + _frame = av_frame_alloc(); + if (!_frame) { + throw std::bad_alloc(); + } + + _frame->buf[0] = av_buffer_alloc(width * height); + _frame->buf[1] = av_buffer_alloc(width * height / 4); + _frame->buf[2] = av_buffer_alloc(width * height / 4); + + _frame->linesize[0] = width; + _frame->linesize[1] = width / 2; + _frame->linesize[2] = width / 2; + + for (auto i = 0; i < 3; ++i) { + _frame->data[i] = _frame->buf[i]->data; + } + + _frame->width = width; + _frame->height = height; + _frame->format = AV_PIX_FMT_YUV420P; + _frame->pts = pts; +} + + +void +FFmpegImage::set_pts(int64_t pts) +{ + _frame->pts = pts; +} + + +uint8_t* +FFmpegImage::y() +{ + return _frame->data[0]; +} + + +int +FFmpegImage::y_stride() const +{ + return _frame->linesize[0]; +} + + +uint8_t* +FFmpegImage::u() +{ + return _frame->data[1]; +} + + +int +FFmpegImage::u_stride() const +{ + return _frame->linesize[1]; +} + + +uint8_t* +FFmpegImage::v() +{ + return _frame->data[2]; +} + + +int +FFmpegImage::v_stride() const +{ + return _frame->linesize[2]; +} + + diff --git a/src/ffmpeg_image.h b/src/ffmpeg_image.h new file mode 100644 index 00000000..44eb0db7 --- /dev/null +++ b/src/ffmpeg_image.h @@ -0,0 +1,107 @@ +/* + Copyright (C) 2023 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. +*/ + + +#ifndef LIBDCP_FFMPEG_IMAGE_H +#define LIBDCP_FFMPEG_IMAGE_H + + +#include "types.h" +#include "warnings.h" +LIBDCP_DISABLE_WARNINGS +extern "C" { +#include <libavutil/frame.h> +} +LIBDCP_ENABLE_WARNINGS +#include <algorithm> +#include <vector> + + +namespace dcp { + + +class FFmpegImage +{ +public: + explicit FFmpegImage(int64_t pts); + + explicit FFmpegImage(AVFrame* frame) + : _frame(frame) + {} + + FFmpegImage(FFmpegImage const& other) = delete; + FFmpegImage& operator=(FFmpegImage const& other) = delete; + + FFmpegImage(FFmpegImage&& other) { + std::swap(_frame, other._frame); + } + + FFmpegImage& operator=(FFmpegImage&& other) { + std::swap(_frame, other._frame); + return *this; + } + + ~FFmpegImage() + { + av_frame_free(&_frame); + } + + AVFrame const * frame() const { + return _frame; + } + + uint8_t* y(); + int y_stride() const; + + uint8_t* u(); + int u_stride() const; + + uint8_t* v(); + int v_stride() const; + + Size size() const { + return { 1920, 1080 }; + } + + void set_pts(int64_t pts); + +private: + AVFrame* _frame = nullptr; +}; + + +} + + +#endif + diff --git a/src/frame_info.h b/src/frame_info.h new file mode 100644 index 00000000..325350c4 --- /dev/null +++ b/src/frame_info.h @@ -0,0 +1,109 @@ +/* + Copyright (C) 2012-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. +*/ + + +#ifndef LIBDCP_FRAME_INFO_H +#define LIBDCP_FRAME_INFO_H + + +#include "warnings.h" +LIBDCP_DISABLE_WARNINGS +#include <asdcp/AS_DCP.h> +LIBDCP_ENABLE_WARNINGS +#include <stdint.h> +#include <string> + + +namespace dcp { + + +/** @class FrameInfo + * @brief Information about a single frame (either a monoscopic frame or a left *or* right eye stereoscopic frame) + */ +struct FrameInfo +{ + FrameInfo () = default; + + FrameInfo(uint64_t o, uint64_t s, std::string h) + : offset(o) + , size(s) + , hash(h) + {} + + uint64_t offset = 0; + uint64_t size = 0; + std::string hash; +}; + + +struct J2KFrameInfo : public FrameInfo +{ + J2KFrameInfo() = default; + + J2KFrameInfo(uint64_t offset_, uint64_t size_, std::string hash_) + : FrameInfo(offset_, size_, hash_) + {} +}; + + +struct MPEG2FrameInfo : public FrameInfo +{ + MPEG2FrameInfo() = default; + + MPEG2FrameInfo( + uint64_t offset_, + uint64_t size_, + std::string hash_, + ASDCP::MPEG2::FrameType_t type_, + bool gop_start_, + bool closed_gop_, + uint8_t temporal_offset_ + ) + : FrameInfo(offset_, size_, hash_) + , type(type_) + , gop_start(gop_start_) + , closed_gop(closed_gop_) + , temporal_offset(temporal_offset_) + {} + + ASDCP::MPEG2::FrameType_t type; + bool gop_start; + bool closed_gop; + uint8_t temporal_offset; +}; + + +} + + +#endif diff --git a/src/interop_subtitle_asset.cc b/src/interop_subtitle_asset.cc index 32c3f66a..a4594207 100644 --- a/src/interop_subtitle_asset.cc +++ b/src/interop_subtitle_asset.cc @@ -115,13 +115,13 @@ InteropSubtitleAsset::xml_as_string () const auto root = doc.create_root_node ("DCSubtitle"); root->set_attribute ("Version", "1.0"); - root->add_child("SubtitleID")->add_child_text (_id); - root->add_child("MovieTitle")->add_child_text (_movie_title); - root->add_child("ReelNumber")->add_child_text (raw_convert<string> (_reel_number)); - root->add_child("Language")->add_child_text (_language); + cxml::add_text_child(root, "SubtitleID", _id); + cxml::add_text_child(root, "MovieTitle", _movie_title); + cxml::add_text_child(root, "ReelNumber", raw_convert<string> (_reel_number)); + cxml::add_text_child(root, "Language", _language); for (auto i: _load_font_nodes) { - auto load_font = root->add_child("LoadFont"); + auto load_font = cxml::add_child(root, "LoadFont"); load_font->set_attribute ("Id", i->id); load_font->set_attribute ("URI", i->uri); } diff --git a/src/interop_subtitle_asset.h b/src/interop_subtitle_asset.h index f63740d5..ad1c68a7 100644 --- a/src/interop_subtitle_asset.h +++ b/src/interop_subtitle_asset.h @@ -37,8 +37,8 @@ */ -#ifndef DCP_INTEROP_SUBTITLE_ASSET_H -#define DCP_INTEROP_SUBTITLE_ASSET_H +#ifndef LIBDCP_INTEROP_SUBTITLE_ASSET_H +#define LIBDCP_INTEROP_SUBTITLE_ASSET_H #include "subtitle_asset.h" diff --git a/src/j2k_picture_asset.cc b/src/j2k_picture_asset.cc new file mode 100644 index 00000000..98792253 --- /dev/null +++ b/src/j2k_picture_asset.cc @@ -0,0 +1,229 @@ +/* + Copyright (C) 2012-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/picture_asset.cc + * @brief J2KPictureAsset class + */ + + +#include "compose.hpp" +#include "dcp_assert.h" +#include "equality_options.h" +#include "exceptions.h" +#include "j2k_transcode.h" +#include "openjpeg_image.h" +#include "j2k_picture_asset.h" +#include "j2k_picture_asset_writer.h" +#include "util.h" +#include <asdcp/AS_DCP.h> +#include <asdcp/KM_fileio.h> +#include <libxml++/nodes/element.h> +#include <boost/filesystem.hpp> +#include <list> +#include <stdexcept> + + +using std::string; +using std::list; +using std::vector; +using std::max; +using std::pair; +using std::make_pair; +using std::shared_ptr; +using namespace dcp; + + +J2KPictureAsset::J2KPictureAsset(boost::filesystem::path file) + : PictureAsset(file) +{ + +} + + +J2KPictureAsset::J2KPictureAsset(Fraction edit_rate, Standard standard) + : PictureAsset(edit_rate, standard) +{ + +} + + +void +J2KPictureAsset::read_picture_descriptor (ASDCP::JP2K::PictureDescriptor const & desc) +{ + _size.width = desc.StoredWidth; + _size.height = desc.StoredHeight; + _edit_rate = Fraction (desc.EditRate.Numerator, desc.EditRate.Denominator); + _intrinsic_duration = desc.ContainerDuration; + _frame_rate = Fraction (desc.SampleRate.Numerator, desc.SampleRate.Denominator); + _screen_aspect_ratio = Fraction (desc.AspectRatio.Numerator, desc.AspectRatio.Denominator); +} + + +bool +J2KPictureAsset::descriptor_equals ( + ASDCP::JP2K::PictureDescriptor const & a, ASDCP::JP2K::PictureDescriptor const & b, NoteHandler note + ) const +{ + if ( + a.EditRate != b.EditRate || + a.SampleRate != b.SampleRate || + a.StoredWidth != b.StoredWidth || + a.StoredHeight != b.StoredHeight || + a.AspectRatio != b.AspectRatio || + a.Rsize != b.Rsize || + a.Xsize != b.Xsize || + a.Ysize != b.Ysize || + a.XOsize != b.XOsize || + a.YOsize != b.YOsize || + a.XTsize != b.XTsize || + a.YTsize != b.YTsize || + a.XTOsize != b.XTOsize || + a.YTOsize != b.YTOsize || + a.Csize != b.Csize +// a.CodingStyleDefault != b.CodingStyleDefault || +// a.QuantizationDefault != b.QuantizationDefault + ) { + + note (NoteType::ERROR, "video MXF picture descriptors differ"); + return false; + } + + if (a.ContainerDuration != b.ContainerDuration) { + note (NoteType::ERROR, "video container durations differ"); + } + +// for (unsigned int j = 0; j < ASDCP::JP2K::MaxComponents; ++j) { +// if (a.ImageComponents[j] != b.ImageComponents[j]) { +// notes.pack_start ("video MXF picture descriptors differ"); +// } +// } + + return true; +} + + +bool +J2KPictureAsset::frame_buffer_equals ( + int frame, EqualityOptions const& opt, NoteHandler note, + uint8_t const * data_A, unsigned int size_A, uint8_t const * data_B, unsigned int size_B + ) const +{ + if (size_A == size_B && memcmp (data_A, data_B, size_A) == 0) { + note (NoteType::NOTE, "J2K identical"); + /* Easy result; the J2K data is identical */ + return true; + } + + /* Decompress the images to bitmaps */ + auto image_A = decompress_j2k (const_cast<uint8_t*>(data_A), size_A, 0); + auto image_B = decompress_j2k (const_cast<uint8_t*>(data_B), size_B, 0); + + /* Compare them */ + + vector<int> abs_diffs (image_A->size().width * image_A->size().height * 3); + int d = 0; + int max_diff = 0; + + for (int c = 0; c < 3; ++c) { + + if (image_A->size() != image_B->size()) { + note (NoteType::ERROR, String::compose ("image sizes for frame %1 differ", frame)); + return false; + } + + int const pixels = image_A->size().width * image_A->size().height; + for (int j = 0; j < pixels; ++j) { + int const t = abs (image_A->data(c)[j] - image_B->data(c)[j]); + abs_diffs[d++] = t; + max_diff = max (max_diff, t); + } + } + + uint64_t total = 0; + for (vector<int>::iterator j = abs_diffs.begin(); j != abs_diffs.end(); ++j) { + total += *j; + } + + double const mean = double (total) / abs_diffs.size (); + + uint64_t total_squared_deviation = 0; + for (auto j: abs_diffs) { + total_squared_deviation += pow (j - mean, 2); + } + + auto const std_dev = sqrt (double (total_squared_deviation) / abs_diffs.size()); + + note (NoteType::NOTE, String::compose("mean difference %1 deviation %2", mean, std_dev)); + + if (mean > opt.max_mean_pixel_error) { + note ( + NoteType::ERROR, + String::compose ("mean %1 out of range %2 in frame %3", mean, opt.max_mean_pixel_error, frame) + ); + + return false; + } + + if (std_dev > opt.max_std_dev_pixel_error) { + note ( + NoteType::ERROR, + String::compose ("standard deviation %1 out of range %2 in frame %3", std_dev, opt.max_std_dev_pixel_error, frame) + ); + + return false; + } + + return true; +} + + +string +J2KPictureAsset::static_pkl_type (Standard standard) +{ + switch (standard) { + case Standard::INTEROP: + return "application/x-smpte-mxf;asdcpKind=Picture"; + case Standard::SMPTE: + return "application/mxf"; + default: + DCP_ASSERT (false); + } +} + + +string +J2KPictureAsset::pkl_type (Standard standard) const +{ + return static_pkl_type (standard); +} diff --git a/src/j2k_picture_asset.h b/src/j2k_picture_asset.h new file mode 100644 index 00000000..972de43e --- /dev/null +++ b/src/j2k_picture_asset.h @@ -0,0 +1,110 @@ +/* + Copyright (C) 2012-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/j2k_picture_asset.h + * @brief J2KPictureAsset class + */ + + +#ifndef LIBDCP_J2K_PICTURE_ASSET_H +#define LIBDCP_J2K_PICTURE_ASSET_H + + +#include "behaviour.h" +#include "mxf.h" +#include "metadata.h" +#include "picture_asset.h" +#include "util.h" + + +namespace ASDCP { + namespace JP2K { + struct PictureDescriptor; + } +} + + +namespace dcp { + + +class MonoJ2KPictureFrame; +class StereoJ2KPictureFrame; +class J2KPictureAssetWriter; + + +/** @class J2KPictureAsset + * @brief An asset made up of JPEG2000 data + */ +class J2KPictureAsset : public PictureAsset +{ +public: + /** Load a J2KPictureAsset from a file */ + explicit J2KPictureAsset (boost::filesystem::path file); + + /** Create a new J2KPictureAsset with a given edit rate and standard */ + J2KPictureAsset (Fraction edit_rate, Standard standard); + + virtual std::shared_ptr<J2KPictureAssetWriter> start_write ( + boost::filesystem::path file, + Behaviour behaviour + ) = 0; + + static std::string static_pkl_type (Standard standard); + +protected: + friend class MonoJ2KPictureAssetWriter; + friend class StereoJ2KPictureAssetWriter; + + bool frame_buffer_equals ( + int frame, EqualityOptions const& opt, NoteHandler note, + uint8_t const * data_A, unsigned int size_A, uint8_t const * data_B, unsigned int size_B + ) const; + + bool descriptor_equals ( + ASDCP::JP2K::PictureDescriptor const & a, + ASDCP::JP2K::PictureDescriptor const & b, + NoteHandler note + ) const; + + void read_picture_descriptor (ASDCP::JP2K::PictureDescriptor const &); + +private: + std::string pkl_type (Standard standard) const override; +}; + + +} + + +#endif diff --git a/src/picture_asset_writer.cc b/src/j2k_picture_asset_writer.cc index c30be1bc..75be51ba 100644 --- a/src/picture_asset_writer.cc +++ b/src/j2k_picture_asset_writer.cc @@ -33,13 +33,13 @@ /** @file src/picture_asset_writer.cc - * @brief PictureAssetWriter and FrameInfo classes + * @brief J2KPictureAssetWriter and FrameInfo classes */ -#include "picture_asset_writer.h" +#include "j2k_picture_asset_writer.h" #include "exceptions.h" -#include "picture_asset.h" +#include "j2k_picture_asset.h" #include <asdcp/KM_fileio.h> #include <asdcp/AS_DCP.h> #include <inttypes.h> @@ -51,7 +51,7 @@ using std::shared_ptr; using namespace dcp; -PictureAssetWriter::PictureAssetWriter (PictureAsset* asset, boost::filesystem::path file, bool overwrite) +J2KPictureAssetWriter::J2KPictureAssetWriter (J2KPictureAsset* asset, boost::filesystem::path file, bool overwrite) : AssetWriter (asset, file) , _picture_asset (asset) , _overwrite (overwrite) @@ -60,8 +60,8 @@ PictureAssetWriter::PictureAssetWriter (PictureAsset* asset, boost::filesystem:: } -FrameInfo -PictureAssetWriter::write (Data const& data) +J2KFrameInfo +J2KPictureAssetWriter::write (Data const& data) { return write (data.data(), data.size()); } diff --git a/src/picture_asset_writer.h b/src/j2k_picture_asset_writer.h index 0caa8815..0077b3e0 100644 --- a/src/picture_asset_writer.h +++ b/src/j2k_picture_asset_writer.h @@ -32,16 +32,17 @@ */ -/** @file src/picture_asset_writer.h - * @brief PictureAssetWriter and FrameInfo classes. +/** @file src/j2k_picture_asset_writer.h + * @brief J2KPictureAssetWriter and FrameInfo classes. */ -#ifndef LIBDCP_PICTURE_ASSET_WRITER_H -#define LIBDCP_PICTURE_ASSET_WRITER_H +#ifndef LIBDCP_J2K_PICTURE_ASSET_WRITER_H +#define LIBDCP_J2K_PICTURE_ASSET_WRITER_H #include "asset_writer.h" +#include "frame_info.h" #include "metadata.h" #include <boost/utility.hpp> #include <memory> @@ -53,46 +54,27 @@ namespace dcp { class Data; -class PictureAsset; +class J2KPictureAsset; -/** @class FrameInfo - * @brief Information about a single frame (either a monoscopic frame or a left *or* right eye stereoscopic frame) - */ -struct FrameInfo -{ - FrameInfo () {} - - FrameInfo (uint64_t o, uint64_t s, std::string h) - : offset (o) - , size (s) - , hash (h) - {} - - uint64_t offset = 0; - uint64_t size = 0; - std::string hash; -}; - - -/** @class PictureAssetWriter +/** @class J2KPictureAssetWriter * @brief Parent class for classes which write picture assets. */ -class PictureAssetWriter : public AssetWriter +class J2KPictureAssetWriter : public AssetWriter { public: - virtual FrameInfo write (uint8_t const *, int) = 0; - virtual void fake_write (int) = 0; + virtual J2KFrameInfo write(uint8_t const *, int) = 0; + virtual void fake_write(J2KFrameInfo const& info) = 0; - FrameInfo write (Data const& data); + J2KFrameInfo write(Data const& data); protected: template <class P, class Q> - friend void start (PictureAssetWriter *, std::shared_ptr<P>, Q *, uint8_t const *, int); + friend void start (J2KPictureAssetWriter *, std::shared_ptr<P>, Q *, uint8_t const *, int); - PictureAssetWriter (PictureAsset *, boost::filesystem::path, bool); + J2KPictureAssetWriter (J2KPictureAsset *, boost::filesystem::path, bool); - PictureAsset* _picture_asset = nullptr; + J2KPictureAsset* _picture_asset = nullptr; bool _overwrite = false; }; diff --git a/src/picture_asset_writer_common.cc b/src/j2k_picture_asset_writer_common.cc index 82866aac..a86da194 100644 --- a/src/picture_asset_writer_common.cc +++ b/src/j2k_picture_asset_writer_common.cc @@ -32,8 +32,8 @@ */ -/** @file src/picture_asset_writer_common.cc - * @brief Common parts of PictureAssetWriter +/** @file src/j2k_picture_asset_writer_common.cc + * @brief Common parts of J2KPictureAssetWriter */ @@ -46,9 +46,9 @@ using std::shared_ptr; namespace dcp { -struct ASDCPStateBase +struct ASDCPJ2KStateBase { - ASDCPStateBase () + ASDCPJ2KStateBase() : frame_buffer (4 * Kumu::Megabyte) {} @@ -63,7 +63,7 @@ struct ASDCPStateBase template <class P, class Q> -void dcp::start (PictureAssetWriter* writer, shared_ptr<P> state, Q* asset, uint8_t const * data, int size) +void dcp::start (J2KPictureAssetWriter* writer, shared_ptr<P> state, Q* asset, uint8_t const * data, int size) { asset->set_file (writer->_file); diff --git a/src/language_tag.h b/src/language_tag.h index 6f46c16b..3232dda9 100644 --- a/src/language_tag.h +++ b/src/language_tag.h @@ -32,7 +32,7 @@ */ -/** @file src/language_tag.cc +/** @file src/language_tag.h * @brief LanguageTag class */ diff --git a/src/locale_convert.h b/src/locale_convert.h index 37510a96..1323e704 100644 --- a/src/locale_convert.h +++ b/src/locale_convert.h @@ -32,7 +32,7 @@ */ -/** @file src/locale_convert.cc +/** @file src/locale_convert.h * @brief Methods to convert to/from string using the current locale */ diff --git a/src/mono_picture_asset.cc b/src/mono_j2k_picture_asset.cc index a72fd7d4..f718525d 100644 --- a/src/mono_picture_asset.cc +++ b/src/mono_j2k_picture_asset.cc @@ -33,7 +33,7 @@ /** @file src/mono_picture_asset.cc - * @brief MonoPictureAsset class + * @brief MonoJ2KPictureAsset class */ @@ -42,10 +42,10 @@ #include "equality_options.h" #include "exceptions.h" #include "filesystem.h" -#include "mono_picture_asset.h" -#include "mono_picture_asset_reader.h" -#include "mono_picture_asset_writer.h" -#include "mono_picture_frame.h" +#include "mono_j2k_picture_asset.h" +#include "mono_j2k_picture_asset_reader.h" +#include "mono_j2k_picture_asset_writer.h" +#include "mono_j2k_picture_frame.h" #include <asdcp/AS_DCP.h> #include <asdcp/KM_fileio.h> @@ -63,10 +63,11 @@ using namespace boost::placeholders; using namespace dcp; -MonoPictureAsset::MonoPictureAsset (boost::filesystem::path file) - : PictureAsset (file) +MonoJ2KPictureAsset::MonoJ2KPictureAsset (boost::filesystem::path file) + : J2KPictureAsset (file) { - ASDCP::JP2K::MXFReader reader; + Kumu::FileReaderFactory factory; + ASDCP::JP2K::MXFReader reader(factory); auto r = reader.OpenRead(dcp::filesystem::fix_long_path(file).string().c_str()); if (ASDCP_FAILURE(r)) { boost::throw_exception (MXFFileError("could not open MXF file for reading", file.string(), r)); @@ -88,8 +89,8 @@ MonoPictureAsset::MonoPictureAsset (boost::filesystem::path file) } -MonoPictureAsset::MonoPictureAsset (Fraction edit_rate, Standard standard) - : PictureAsset (edit_rate, standard) +MonoJ2KPictureAsset::MonoJ2KPictureAsset (Fraction edit_rate, Standard standard) + : J2KPictureAsset (edit_rate, standard) { } @@ -103,20 +104,21 @@ storing_note_handler (list<pair<NoteType, string>>& notes, NoteType t, string s) bool -MonoPictureAsset::equals(shared_ptr<const Asset> other, EqualityOptions const& opt, NoteHandler note) const +MonoJ2KPictureAsset::equals(shared_ptr<const Asset> other, EqualityOptions const& opt, NoteHandler note) const { - if (!dynamic_pointer_cast<const MonoPictureAsset>(other)) { + if (!dynamic_pointer_cast<const MonoJ2KPictureAsset>(other)) { return false; } - ASDCP::JP2K::MXFReader reader_A; + Kumu::FileReaderFactory factory; + ASDCP::JP2K::MXFReader reader_A(factory); DCP_ASSERT (_file); auto r = reader_A.OpenRead(dcp::filesystem::fix_long_path(*_file).string().c_str()); if (ASDCP_FAILURE(r)) { boost::throw_exception (MXFFileError("could not open MXF file for reading", _file->string(), r)); } - ASDCP::JP2K::MXFReader reader_B; + ASDCP::JP2K::MXFReader reader_B(factory); DCP_ASSERT (other->file ()); r = reader_B.OpenRead(dcp::filesystem::fix_long_path(*other->file()).string().c_str()); if (ASDCP_FAILURE (r)) { @@ -136,7 +138,7 @@ MonoPictureAsset::equals(shared_ptr<const Asset> other, EqualityOptions const& o return false; } - auto other_picture = dynamic_pointer_cast<const MonoPictureAsset> (other); + auto other_picture = dynamic_pointer_cast<const MonoJ2KPictureAsset> (other); DCP_ASSERT (other_picture); bool result = true; @@ -184,23 +186,23 @@ MonoPictureAsset::equals(shared_ptr<const Asset> other, EqualityOptions const& o } -shared_ptr<PictureAssetWriter> -MonoPictureAsset::start_write(boost::filesystem::path file, Behaviour behaviour) +shared_ptr<J2KPictureAssetWriter> +MonoJ2KPictureAsset::start_write(boost::filesystem::path file, Behaviour behaviour) { - /* Can't use make_shared here as the MonoPictureAssetWriter constructor is private */ - return shared_ptr<MonoPictureAssetWriter>(new MonoPictureAssetWriter(this, file, behaviour == Behaviour::OVERWRITE_EXISTING)); + /* Can't use make_shared here as the MonoJ2KPictureAssetWriter constructor is private */ + return shared_ptr<MonoJ2KPictureAssetWriter>(new MonoJ2KPictureAssetWriter(this, file, behaviour == Behaviour::OVERWRITE_EXISTING)); } -shared_ptr<MonoPictureAssetReader> -MonoPictureAsset::start_read () const +shared_ptr<MonoJ2KPictureAssetReader> +MonoJ2KPictureAsset::start_read () const { - /* Can't use make_shared here as the MonoPictureAssetReader constructor is private */ - return shared_ptr<MonoPictureAssetReader>(new MonoPictureAssetReader(this, key(), standard())); + /* Can't use make_shared here as the MonoJ2KPictureAssetReader constructor is private */ + return shared_ptr<MonoJ2KPictureAssetReader>(new MonoJ2KPictureAssetReader(this, key(), standard())); } string -MonoPictureAsset::cpl_node_name () const +MonoJ2KPictureAsset::cpl_node_name () const { return "MainPicture"; } diff --git a/src/mono_picture_asset.h b/src/mono_j2k_picture_asset.h index 9658dcf6..d716b8ff 100644 --- a/src/mono_picture_asset.h +++ b/src/mono_j2k_picture_asset.h @@ -32,50 +32,50 @@ */ -/** @file src/mono_picture_asset.cc - * @brief MonoPictureAsset class +/** @file src/mono_j2k_picture_asset.h + * @brief MonoJ2KPictureAsset class */ -#ifndef LIBDCP_MONO_PICTURE_ASSET_H -#define LIBDCP_MONO_PICTURE_ASSET_H +#ifndef LIBDCP_MONO_J2K_PICTURE_ASSET_H +#define LIBDCP_MONO_J2K_PICTURE_ASSET_H -#include "picture_asset.h" -#include "mono_picture_asset_reader.h" +#include "j2k_picture_asset.h" +#include "mono_j2k_picture_asset_reader.h" namespace dcp { -class MonoPictureAssetWriter; +class MonoJ2KPictureAssetWriter; -/** @class MonoPictureAsset +/** @class MonoJ2KPictureAsset * @brief A 2D (monoscopic) picture asset */ -class MonoPictureAsset : public PictureAsset +class MonoJ2KPictureAsset : public J2KPictureAsset { public: - /** Create a MonoPictureAsset by reading a file. + /** Create a MonoJ2KPictureAsset by reading a file. * @param file Asset file to read. */ - explicit MonoPictureAsset (boost::filesystem::path file); + explicit MonoJ2KPictureAsset (boost::filesystem::path file); - /** Create a MonoPictureAsset with a given edit rate. + /** Create a MonoJ2KPictureAsset with a given edit rate. * @param edit_rate Edit rate (i.e. frame rate) in frames per second. * @param standard DCP standard (INTEROP or SMPTE). */ - MonoPictureAsset(Fraction edit_rate, Standard standard); + MonoJ2KPictureAsset(Fraction edit_rate, Standard standard); - /** Start a progressive write to a MonoPictureAsset. + /** Start a progressive write to a MonoJ2KPictureAsset. * @path file File to write to. * @path behaviour OVERWRITE_EXISTING to overwrite and potentially add to an existing file * (after a write previously failed), MAKE_NEW to create a new file. * If in doubt, use MAKE_NEW here. */ - std::shared_ptr<PictureAssetWriter> start_write(boost::filesystem::path file, Behaviour behaviour) override; - std::shared_ptr<MonoPictureAssetReader> start_read () const; + std::shared_ptr<J2KPictureAssetWriter> start_write(boost::filesystem::path file, Behaviour behaviour) override; + std::shared_ptr<MonoJ2KPictureAssetReader> start_read () const; bool equals ( std::shared_ptr<const Asset> other, diff --git a/src/stereo_picture_asset_reader.h b/src/mono_j2k_picture_asset_reader.h index 9cb05263..8376e939 100644 --- a/src/stereo_picture_asset_reader.h +++ b/src/mono_j2k_picture_asset_reader.h @@ -32,23 +32,23 @@ */ -/** @file src/stereo_picture_asset_reader.h - * @brief StereoPictureAssetReader typedef +/** @file src/mono_j2k_picture_asset_reader.h + * @brief MonoJ2KPictureAssetReader typedef */ -#ifndef LIBDCP_STEREO_PICTURE_ASSET_READER_H -#define LIBDCP_STEREO_PICTURE_ASSET_READER_H +#ifndef LIBDCP_MONO_J2K_PICTURE_ASSET_READER_H +#define LIBDCP_MONO_J2K_PICTURE_ASSET_READER_H #include "asset_reader.h" -#include "stereo_picture_frame.h" +#include "mono_j2k_picture_frame.h" namespace dcp { -typedef AssetReader<ASDCP::JP2K::MXFSReader, StereoPictureFrame> StereoPictureAssetReader; +typedef AssetReader<ASDCP::JP2K::MXFReader, MonoJ2KPictureFrame> MonoJ2KPictureAssetReader; } diff --git a/src/mono_picture_asset_writer.cc b/src/mono_j2k_picture_asset_writer.cc index 7fd58114..1188701e 100644 --- a/src/mono_picture_asset_writer.cc +++ b/src/mono_j2k_picture_asset_writer.cc @@ -33,15 +33,15 @@ /** @file src/mono_picture_asset_writer.cc - * @brief MonoPictureAssetWriter class + * @brief MonoJ2KPictureAssetWriter class */ #include "crypto_context.h" #include "dcp_assert.h" #include "exceptions.h" -#include "mono_picture_asset_writer.h" -#include "picture_asset.h" +#include "mono_j2k_picture_asset_writer.h" +#include "j2k_picture_asset.h" #include "warnings.h" LIBDCP_DISABLE_WARNINGS #include <asdcp/AS_DCP.h> @@ -49,7 +49,7 @@ LIBDCP_DISABLE_WARNINGS LIBDCP_ENABLE_WARNINGS -#include "picture_asset_writer_common.cc" +#include "j2k_picture_asset_writer_common.cc" using std::string; @@ -57,7 +57,7 @@ using std::shared_ptr; using namespace dcp; -struct MonoPictureAssetWriter::ASDCPState : public ASDCPStateBase +struct MonoJ2KPictureAssetWriter::ASDCPState : public ASDCPJ2KStateBase { ASDCP::JP2K::MXFWriter mxf_writer; }; @@ -66,15 +66,15 @@ struct MonoPictureAssetWriter::ASDCPState : public ASDCPStateBase /** @param a Asset to write to. `a' must not be deleted while * this writer class still exists, or bad things will happen. */ -MonoPictureAssetWriter::MonoPictureAssetWriter (PictureAsset* asset, boost::filesystem::path file, bool overwrite) - : PictureAssetWriter (asset, file, overwrite) - , _state (new MonoPictureAssetWriter::ASDCPState) +MonoJ2KPictureAssetWriter::MonoJ2KPictureAssetWriter (J2KPictureAsset* asset, boost::filesystem::path file, bool overwrite) + : J2KPictureAssetWriter (asset, file, overwrite) + , _state (new MonoJ2KPictureAssetWriter::ASDCPState) { } -MonoPictureAssetWriter::~MonoPictureAssetWriter() +MonoJ2KPictureAssetWriter::~MonoJ2KPictureAssetWriter() { try { /* Last-resort finalization to close the file, at least */ @@ -86,15 +86,15 @@ MonoPictureAssetWriter::~MonoPictureAssetWriter() void -MonoPictureAssetWriter::start (uint8_t const * data, int size) +MonoJ2KPictureAssetWriter::start (uint8_t const * data, int size) { dcp::start (this, _state, _picture_asset, data, size); _picture_asset->set_frame_rate (_picture_asset->edit_rate()); } -FrameInfo -MonoPictureAssetWriter::write (uint8_t const * data, int size) +J2KFrameInfo +MonoJ2KPictureAssetWriter::write (uint8_t const * data, int size) { DCP_ASSERT (!_finalized); @@ -117,17 +117,17 @@ MonoPictureAssetWriter::write (uint8_t const * data, int size) } ++_frames_written; - return FrameInfo (before_offset, _state->mxf_writer.Tell() - before_offset, hash); + return J2KFrameInfo(before_offset, _state->mxf_writer.Tell() - before_offset, hash); } void -MonoPictureAssetWriter::fake_write (int size) +MonoJ2KPictureAssetWriter::fake_write(J2KFrameInfo const& info) { DCP_ASSERT (_started); DCP_ASSERT (!_finalized); - auto r = _state->mxf_writer.FakeWriteFrame (size); + auto r = _state->mxf_writer.FakeWriteFrame(info.size); if (ASDCP_FAILURE(r)) { boost::throw_exception (MXFFileError("error in writing video MXF", _file.string(), r)); } @@ -137,7 +137,7 @@ MonoPictureAssetWriter::fake_write (int size) bool -MonoPictureAssetWriter::finalize () +MonoJ2KPictureAssetWriter::finalize () { if (_started) { auto r = _state->mxf_writer.Finalize(); @@ -147,5 +147,5 @@ MonoPictureAssetWriter::finalize () } _picture_asset->_intrinsic_duration = _frames_written; - return PictureAssetWriter::finalize (); + return J2KPictureAssetWriter::finalize (); } diff --git a/src/mono_picture_asset_writer.h b/src/mono_j2k_picture_asset_writer.h index 551d8c80..b3f57191 100644 --- a/src/mono_picture_asset_writer.h +++ b/src/mono_j2k_picture_asset_writer.h @@ -32,16 +32,16 @@ */ -/** @file src/mono_picture_asset_writer.h - * @brief MonoPictureAssetWriter class +/** @file src/mono_j2k_picture_asset_writer.h + * @brief MonoJ2KPictureAssetWriter class */ -#ifndef LIBDCP_MONO_PICTURE_ASSET_WRITER_H -#define LIBDCP_MONO_PICTURE_ASSET_WRITER_H +#ifndef LIBDCP_MONO_J2K_PICTURE_ASSET_WRITER_H +#define LIBDCP_MONO_J2K_PICTURE_ASSET_WRITER_H -#include "picture_asset_writer.h" +#include "j2k_picture_asset_writer.h" #include <memory> #include <boost/utility.hpp> #include <stdint.h> @@ -51,29 +51,29 @@ namespace dcp { -/** @class MonoPictureAssetWriter - * @brief A helper class for writing to MonoPictureAssets +/** @class MonoJ2KPictureAssetWriter + * @brief A helper class for writing to MonoJ2KPictureAssets * - * Objects of this class can only be created with MonoPictureAsset::start_write(). + * Objects of this class can only be created with MonoJ2KPictureAsset::start_write(). * - * Frames can be written to the MonoPictureAsset by calling write() with a JPEG2000 image + * Frames can be written to the MonoJ2KPictureAsset by calling write() with a JPEG2000 image * (a verbatim .j2c file). finalize() should be called after the last frame has been written, * but if it is not, it will be called by the destructor (though in that case any error * during finalization will be ignored). */ -class MonoPictureAssetWriter : public PictureAssetWriter +class MonoJ2KPictureAssetWriter : public J2KPictureAssetWriter { public: - ~MonoPictureAssetWriter(); + ~MonoJ2KPictureAssetWriter(); - FrameInfo write (uint8_t const *, int) override; - void fake_write (int size) override; + J2KFrameInfo write(uint8_t const *, int) override; + void fake_write(J2KFrameInfo const& info) override; bool finalize () override; private: - friend class MonoPictureAsset; + friend class MonoJ2KPictureAsset; - MonoPictureAssetWriter (PictureAsset* a, boost::filesystem::path file, bool); + MonoJ2KPictureAssetWriter (J2KPictureAsset* a, boost::filesystem::path file, bool); void start (uint8_t const *, int); diff --git a/src/mono_picture_frame.cc b/src/mono_j2k_picture_frame.cc index 2abd57e4..69ba9a59 100644 --- a/src/mono_picture_frame.cc +++ b/src/mono_j2k_picture_frame.cc @@ -33,7 +33,7 @@ /** @file src/mono_picture_frame.cc - * @brief MonoPictureFrame class + * @brief MonoJ2KPictureFrame class */ @@ -44,7 +44,7 @@ #include "file.h" #include "filesystem.h" #include "j2k_transcode.h" -#include "mono_picture_frame.h" +#include "mono_j2k_picture_frame.h" #include "rgb_xyz.h" #include "util.h" #include <asdcp/KM_fileio.h> @@ -58,7 +58,7 @@ using boost::optional; using namespace dcp; -MonoPictureFrame::MonoPictureFrame (boost::filesystem::path path) +MonoJ2KPictureFrame::MonoJ2KPictureFrame (boost::filesystem::path path) { auto const size = filesystem::file_size(path); _buffer.reset(new ASDCP::JP2K::FrameBuffer(size)); @@ -81,7 +81,7 @@ MonoPictureFrame::MonoPictureFrame (boost::filesystem::path path) * @param c Context for decryption, or 0. * @param check_hmac true to check the HMAC and give an error if it is not as expected. */ -MonoPictureFrame::MonoPictureFrame (ASDCP::JP2K::MXFReader* reader, int n, shared_ptr<DecryptionContext> c, bool check_hmac) +MonoJ2KPictureFrame::MonoJ2KPictureFrame (ASDCP::JP2K::MXFReader* reader, int n, shared_ptr<DecryptionContext> c, bool check_hmac) { /* XXX: unfortunate guesswork on this buffer size */ _buffer = make_shared<ASDCP::JP2K::FrameBuffer>(4 * Kumu::Megabyte); @@ -94,7 +94,7 @@ MonoPictureFrame::MonoPictureFrame (ASDCP::JP2K::MXFReader* reader, int n, share } -MonoPictureFrame::MonoPictureFrame (uint8_t const * data, int size) +MonoJ2KPictureFrame::MonoJ2KPictureFrame (uint8_t const * data, int size) { _buffer = make_shared<ASDCP::JP2K::FrameBuffer>(size); _buffer->Size (size); @@ -103,28 +103,28 @@ MonoPictureFrame::MonoPictureFrame (uint8_t const * data, int size) uint8_t const * -MonoPictureFrame::data () const +MonoJ2KPictureFrame::data () const { return _buffer->RoData (); } uint8_t * -MonoPictureFrame::data () +MonoJ2KPictureFrame::data () { return _buffer->Data (); } int -MonoPictureFrame::size () const +MonoJ2KPictureFrame::size () const { return _buffer->Size (); } shared_ptr<OpenJPEGImage> -MonoPictureFrame::xyz_image (int reduce) const +MonoJ2KPictureFrame::xyz_image (int reduce) const { return decompress_j2k (const_cast<uint8_t*>(_buffer->RoData()), _buffer->Size(), reduce); } diff --git a/src/mono_picture_frame.h b/src/mono_j2k_picture_frame.h index 43575864..f08e7343 100644 --- a/src/mono_picture_frame.h +++ b/src/mono_j2k_picture_frame.h @@ -32,13 +32,13 @@ */ -/** @file src/mono_picture_frame.h - * @brief MonoPictureFrame class +/** @file src/mono_j2k_picture_frame.h + * @brief MonoJ2KPictureFrame class */ -#ifndef LIBDCP_MONO_PICTURE_FRAME_H -#define LIBDCP_MONO_PICTURE_FRAME_H +#ifndef LIBDCP_MONO_J2K_PICTURE_FRAME_H +#define LIBDCP_MONO_J2K_PICTURE_FRAME_H #include "asset_reader.h" @@ -64,20 +64,20 @@ namespace dcp { class OpenJPEGImage; -/** @class MonoPictureFrame +/** @class MonoJ2KPictureFrame * @brief A single frame of a 2D (monoscopic) picture asset */ -class MonoPictureFrame : public Data +class MonoJ2KPictureFrame : public Data { public: /** Make a picture frame from a JPEG2000 file. * @param path Path to JPEG2000 file. */ - explicit MonoPictureFrame (boost::filesystem::path path); - MonoPictureFrame (uint8_t const * data, int size); + explicit MonoJ2KPictureFrame (boost::filesystem::path path); + MonoJ2KPictureFrame (uint8_t const * data, int size); - MonoPictureFrame (MonoPictureFrame const&) = delete; - MonoPictureFrame& operator= (MonoPictureFrame const&) = delete; + MonoJ2KPictureFrame (MonoJ2KPictureFrame const&) = delete; + MonoJ2KPictureFrame& operator= (MonoJ2KPictureFrame const&) = delete; /** @param reduce a factor by which to reduce the resolution * of the image, expressed as a power of two (pass 0 for no @@ -95,12 +95,12 @@ public: int size () const override; private: - /* XXX: this is a bit of a shame, but I tried friend MonoPictureAssetReader and it's + /* XXX: this is a bit of a shame, but I tried friend MonoJ2KPictureAssetReader and it's rejected by some (seemingly older) GCCs. */ - friend class AssetReader<ASDCP::JP2K::MXFReader, MonoPictureFrame>; + friend class AssetReader<ASDCP::JP2K::MXFReader, MonoJ2KPictureFrame>; - MonoPictureFrame (ASDCP::JP2K::MXFReader* reader, int n, std::shared_ptr<DecryptionContext>, bool check_hmac); + MonoJ2KPictureFrame (ASDCP::JP2K::MXFReader* reader, int n, std::shared_ptr<DecryptionContext>, bool check_hmac); std::shared_ptr<ASDCP::JP2K::FrameBuffer> _buffer; }; diff --git a/src/mono_mpeg2_picture_asset.cc b/src/mono_mpeg2_picture_asset.cc new file mode 100644 index 00000000..380da0fe --- /dev/null +++ b/src/mono_mpeg2_picture_asset.cc @@ -0,0 +1,86 @@ +/* + Copyright (C) 2023 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. +*/ + + +#include "filesystem.h" +#include "mono_mpeg2_picture_asset.h" +#include "mono_mpeg2_picture_asset_reader.h" +#include "mono_mpeg2_picture_asset_writer.h" +#include <asdcp/AS_DCP.h> + + +using std::shared_ptr; +using namespace dcp; + + +MonoMPEG2PictureAsset::MonoMPEG2PictureAsset(boost::filesystem::path file) + : MPEG2PictureAsset(file) +{ + Kumu::FileReaderFactory factory; + ASDCP::MPEG2::MXFReader reader(factory); + auto const result = reader.OpenRead(dcp::filesystem::fix_long_path(file).string().c_str()); + if (ASDCP_FAILURE(result)) { + boost::throw_exception(MXFFileError("could not open MXF file for reading", file.string(), result)); + } + + ASDCP::MPEG2::VideoDescriptor desc; + if (ASDCP_FAILURE(reader.FillVideoDescriptor(desc))) { + boost::throw_exception(ReadError("could not read video MXF information")); + } + + read_video_descriptor(desc); + + ASDCP::WriterInfo info; + if (ASDCP_FAILURE(reader.FillWriterInfo(info))) { + boost::throw_exception(ReadError("could not read video MXF information")); + } + + _id = read_writer_info(info); +} + + +shared_ptr<MonoMPEG2PictureAssetReader> +MonoMPEG2PictureAsset::start_read () const +{ + /* Can't use make_shared here as the MonoMPEG2PictureAssetReader constructor is private */ + return shared_ptr<MonoMPEG2PictureAssetReader>(new MonoMPEG2PictureAssetReader(this, key(), standard())); + +} + + +shared_ptr<MPEG2PictureAssetWriter> +MonoMPEG2PictureAsset::start_write(boost::filesystem::path file, Behaviour behaviour) +{ + /* Can't use make_shared here as the MonoJ2KPictureAssetWriter constructor is private */ + return shared_ptr<MonoMPEG2PictureAssetWriter>(new MonoMPEG2PictureAssetWriter(this, file, behaviour == Behaviour::OVERWRITE_EXISTING)); +} diff --git a/src/mono_mpeg2_picture_asset.h b/src/mono_mpeg2_picture_asset.h new file mode 100644 index 00000000..8ef3653e --- /dev/null +++ b/src/mono_mpeg2_picture_asset.h @@ -0,0 +1,73 @@ +/* + Copyright (C) 2023 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. +*/ + + +#ifndef LIBDCP_MONO_MPEG2_PICTURE_ASSET_H +#define LIBDCP_MONO_MPEG2_PICTURE_ASSET_H + + +/** @file src/mono_mpeg2_picture_asset.h + * @brief MonoMPEG2PictureAsset class + */ + + +#include "behaviour.h" +#include "mpeg2_picture_asset.h" +#include "mono_mpeg2_picture_asset_reader.h" + + +namespace dcp { + + +class MonoMPEG2PictureAssetWriter; + + +class MonoMPEG2PictureAsset : public MPEG2PictureAsset +{ +public: + MonoMPEG2PictureAsset(Fraction edit_rate) + : MPEG2PictureAsset(edit_rate) + {} + + explicit MonoMPEG2PictureAsset(boost::filesystem::path file); + + std::shared_ptr<MPEG2PictureAssetWriter> start_write(boost::filesystem::path file, Behaviour behaviour) override; + std::shared_ptr<MonoMPEG2PictureAssetReader> start_read() const; +}; + + + +} + + +#endif diff --git a/src/mono_mpeg2_picture_asset_reader.h b/src/mono_mpeg2_picture_asset_reader.h new file mode 100644 index 00000000..75155f40 --- /dev/null +++ b/src/mono_mpeg2_picture_asset_reader.h @@ -0,0 +1,58 @@ +/* + Copyright (C) 2023 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/mono_mpeg2_picture_asset_reader.h + * @brief MonoJ2KPictureAssetReader typedef + */ + + +#ifndef LIBDCP_MONO_MPEG2_PICTURE_ASSET_READER_H +#define LIBDCP_MONO_MPEG2_PICTURE_ASSET_READER_H + + +#include "asset_reader.h" +#include "mono_mpeg2_picture_frame.h" + + +namespace dcp { + + +typedef AssetReader<ASDCP::MPEG2::MXFReader, MonoMPEG2PictureFrame> MonoMPEG2PictureAssetReader; + + +} + + +#endif + diff --git a/src/mono_mpeg2_picture_asset_writer.cc b/src/mono_mpeg2_picture_asset_writer.cc new file mode 100644 index 00000000..f8101c54 --- /dev/null +++ b/src/mono_mpeg2_picture_asset_writer.cc @@ -0,0 +1,144 @@ +/* + Copyright (C) 2024 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. +*/ + + +#include "mono_mpeg2_picture_asset_writer.h" +#include "mpeg2_picture_asset.h" +#include "mpeg2_picture_asset_writer.h" + + +using std::string; +using namespace dcp; + + +struct MonoMPEG2PictureAssetWriter::ASDCPState : public ASDCPMPEG2StateBase +{ + ASDCP::MPEG2::MXFWriter mxf_writer; +}; + + + + +MonoMPEG2PictureAssetWriter::MonoMPEG2PictureAssetWriter(MPEG2PictureAsset* asset, boost::filesystem::path file, bool overwrite) + : MPEG2PictureAssetWriter(asset, file, overwrite) + , _state(new MonoMPEG2PictureAssetWriter::ASDCPState) +{ + asset->set_file(file); +} + + +MonoMPEG2PictureAssetWriter::~MonoMPEG2PictureAssetWriter() +{ + try { + /* Last-resort finalization to close the file, at least */ + if (!_finalized) { + _state->mxf_writer.Finalize(); + } + } catch (...) {} +} + + +void +MonoMPEG2PictureAssetWriter::start(uint8_t const * data, int size) +{ + dcp::start(this, _state, _picture_asset, data, size); + _picture_asset->set_frame_rate (_picture_asset->edit_rate()); +} + + +MPEG2FrameInfo +MonoMPEG2PictureAssetWriter::write(uint8_t const * data, int size) +{ + DCP_ASSERT(!_finalized); + + if (!_started) { + start(data, size); + } + + ASDCP::MPEG2::FrameBuffer buffer; + buffer.SetData(const_cast<uint8_t*>(data), size); + buffer.Size(size); + buffer.PlaintextOffset(0); + + auto const before_offset = _state->mxf_writer.Tell(); + + string hash; + auto const r = _state->mxf_writer.WriteFrame(buffer, _crypto_context->context(), _crypto_context->hmac(), &hash); + if (ASDCP_FAILURE(r)) { + boost::throw_exception(MXFFileError("error in writing video MXF", _file.string(), r)); + } + + ++_frames_written; + return MPEG2FrameInfo( + before_offset, + _state->mxf_writer.Tell() - before_offset, + hash, + buffer.FrameType(), + buffer.GOPStart(), + buffer.ClosedGOP(), + buffer.TemporalOffset() + ); +} + + +void +MonoMPEG2PictureAssetWriter::fake_write(MPEG2FrameInfo const& info) +{ + DCP_ASSERT(_started); + DCP_ASSERT(!_finalized); + + DCP_ASSERT(false); + + auto r = _state->mxf_writer.FakeWriteFrame(info.size, info.type, info.gop_start, info.closed_gop, info.temporal_offset); + if (ASDCP_FAILURE(r)) { + boost::throw_exception(MXFFileError("error in writing video MXF", _file.string(), r)); + } + + ++_frames_written; +} + + +bool +MonoMPEG2PictureAssetWriter::finalize() +{ + if (_started) { + auto r = _state->mxf_writer.Finalize(); + if (ASDCP_FAILURE(r)) { + boost::throw_exception(MXFFileError("error in finalizing video MXF", _file.string(), r)); + } + } + + _picture_asset->_intrinsic_duration = _frames_written; + return MPEG2PictureAssetWriter::finalize(); +} + diff --git a/src/mono_mpeg2_picture_asset_writer.h b/src/mono_mpeg2_picture_asset_writer.h new file mode 100644 index 00000000..bf7a6cc9 --- /dev/null +++ b/src/mono_mpeg2_picture_asset_writer.h @@ -0,0 +1,68 @@ +/* + Copyright (C) 2024 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. +*/ + + +#include "mpeg2_picture_asset_writer.h" + + +#include "mpeg2_picture_asset_writer_common.cc" + + +namespace dcp { + + +class MonoMPEG2PictureAsset; + + +class MonoMPEG2PictureAssetWriter : public MPEG2PictureAssetWriter +{ +public: + ~MonoMPEG2PictureAssetWriter(); + + MPEG2FrameInfo write(uint8_t const *, int) override; + void fake_write(MPEG2FrameInfo const& info) override; + bool finalize() override; + +private: + friend class MonoMPEG2PictureAsset; + + MonoMPEG2PictureAssetWriter(MPEG2PictureAsset* asset, boost::filesystem::path file, bool overwrite); + + void start(uint8_t const *, int); + + struct ASDCPState; + std::shared_ptr<ASDCPState> _state; +}; + + +} diff --git a/src/mono_mpeg2_picture_frame.cc b/src/mono_mpeg2_picture_frame.cc new file mode 100644 index 00000000..3c79a94c --- /dev/null +++ b/src/mono_mpeg2_picture_frame.cc @@ -0,0 +1,91 @@ +/* + Copyright (C) 2023 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. +*/ + + +#include "compose.hpp" +#include "mono_mpeg2_picture_frame.h" + + +using std::make_shared; +using std::shared_ptr; +using namespace dcp; + + + +MonoMPEG2PictureFrame::MonoMPEG2PictureFrame(uint8_t const* data, int size) +{ + _buffer = make_shared<ASDCP::MPEG2::FrameBuffer>(size); + memcpy(_buffer->Data(), data, size); + _buffer->Size(size); +} + + +/** Make a picture frame from a 2D (monoscopic) asset. + * @param reader Reader for the asset's MXF file. + * @param n Frame within the asset, not taking EntryPoint into account. + * @param c Context for decryption, or 0. + * @param check_hmac true to check the HMAC and give an error if it is not as expected. + */ +MonoMPEG2PictureFrame::MonoMPEG2PictureFrame(ASDCP::MPEG2::MXFReader* reader, int n, shared_ptr<DecryptionContext> context, bool check_hmac) +{ + /* XXX: unfortunate guesswork on this buffer size */ + _buffer = make_shared<ASDCP::MPEG2::FrameBuffer>(4 * Kumu::Megabyte); + + auto const r = reader->ReadFrame(n, *_buffer, context->context(), check_hmac ? context->hmac() : nullptr); + + if (ASDCP_FAILURE(r)) { + boost::throw_exception(ReadError(String::compose("could not read video frame %1 (%2)", n, static_cast<int>(r)))); + } +} + + +uint8_t const * +MonoMPEG2PictureFrame::data() const +{ + return _buffer->RoData(); +} + + +uint8_t * +MonoMPEG2PictureFrame::data() +{ + return _buffer->Data(); +} + + +int +MonoMPEG2PictureFrame::size() const +{ + return _buffer->Size(); +} + diff --git a/src/mono_mpeg2_picture_frame.h b/src/mono_mpeg2_picture_frame.h new file mode 100644 index 00000000..6a7669f7 --- /dev/null +++ b/src/mono_mpeg2_picture_frame.h @@ -0,0 +1,81 @@ +/* + Copyright (C) 2012-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. +*/ + + +#ifndef LIBDCP_MONO_MPEG2_PICTURE_FRAME_H +#define LIBDCP_MONO_MPEG2_PICTURE_FRAME_H + + +#include "asset_reader.h" +#include "data.h" + + +namespace dcp { + + +class MonoMPEG2PictureFrame : public Data +{ +public: + MonoMPEG2PictureFrame(uint8_t const * data, int size); + + MonoMPEG2PictureFrame(MonoMPEG2PictureFrame const&) = delete; + MonoMPEG2PictureFrame& operator=(MonoMPEG2PictureFrame const&) = delete; + + /* XXX: couldn't we just return the frame buffer */ + + /** @return Pointer to MPEG2 data */ + uint8_t const * data() const override; + + /** @return Pointer to MPEG2 data */ + uint8_t* data () override; + + /** @return Size of MPEG2 data in bytes */ + int size() const override; + +private: + /* XXX: this is a bit of a shame, but I tried friend MonoMPEG2PictureAssetReader and it's + rejected by some (seemingly older) GCCs. + */ + friend class AssetReader<ASDCP::MPEG2::MXFReader, MonoMPEG2PictureFrame>; + + MonoMPEG2PictureFrame(ASDCP::MPEG2::MXFReader* reader, int n, std::shared_ptr<DecryptionContext>, bool check_hmac); + + /* XXX why is this a shared_ptr? */ + std::shared_ptr<ASDCP::MPEG2::FrameBuffer> _buffer; +}; + + +} + + +#endif diff --git a/src/mpeg2_picture_asset.cc b/src/mpeg2_picture_asset.cc new file mode 100644 index 00000000..6cd0b428 --- /dev/null +++ b/src/mpeg2_picture_asset.cc @@ -0,0 +1,74 @@ +/* + Copyright (C) 2023 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. +*/ + + +#include "mpeg2_picture_asset.h" + + +using std::string; +using namespace dcp; + + +MPEG2PictureAsset::MPEG2PictureAsset(boost::filesystem::path file) + : PictureAsset(file) +{ + +} + + +void +MPEG2PictureAsset::read_video_descriptor(ASDCP::MPEG2::VideoDescriptor const& descriptor) +{ + _size.width = descriptor.StoredWidth; + _size.height = descriptor.StoredHeight; + _edit_rate = Fraction(descriptor.EditRate.Numerator, descriptor.EditRate.Denominator); + _intrinsic_duration = descriptor.ContainerDuration; + _frame_rate = Fraction(descriptor.SampleRate.Numerator, descriptor.SampleRate.Denominator); + _screen_aspect_ratio = Fraction(descriptor.AspectRatio.Numerator, descriptor.AspectRatio.Denominator); +} + + +string +MPEG2PictureAsset::pkl_type (Standard standard) const +{ + DCP_ASSERT(standard == Standard::INTEROP); + return "application/x-smpte-mxf;asdcpKind=Picture"; +} + + +string +MPEG2PictureAsset::static_pkl_type(Standard standard) +{ + DCP_ASSERT(standard == Standard::INTEROP); + return "application/x-smpte-mxf;asdcpKind=Picture"; +} diff --git a/src/mpeg2_picture_asset.h b/src/mpeg2_picture_asset.h new file mode 100644 index 00000000..38b8e0a0 --- /dev/null +++ b/src/mpeg2_picture_asset.h @@ -0,0 +1,90 @@ +/* + Copyright (C) 2023 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. +*/ + + +#ifndef LIBDCP_MPEG2_PICTURE_ASSET_H +#define LIBDCP_MPEG2_PICTURE_ASSET_H + + +/** @file src/mpeg2_picture_asset.h + * @brief MPEG2PictureAsset class + */ + + +#include "behaviour.h" +#include "mpeg2_picture_asset_writer.h" +#include "picture_asset.h" +#include <boost/filesystem/path.hpp> + + +namespace ASDCP { + namespace MPEG2 { + struct VideoDescriptor; + } +} + + +namespace dcp { + + +class MPEG2PictureAsset : public PictureAsset +{ +public: + MPEG2PictureAsset(Fraction edit_rate) + : PictureAsset(edit_rate, Standard::INTEROP) + {} + + explicit MPEG2PictureAsset(boost::filesystem::path file); + + virtual std::shared_ptr<MPEG2PictureAssetWriter> start_write( + boost::filesystem::path file, + Behaviour behaviour + ) = 0; + + static std::string static_pkl_type(Standard standard); + +protected: + friend class MonoMPEG2PictureAssetWriter; + + void read_video_descriptor(ASDCP::MPEG2::VideoDescriptor const& descriptor); + +private: + std::string pkl_type(Standard standard) const override; +}; + + +} + + +#endif + diff --git a/src/mpeg2_picture_asset_writer.cc b/src/mpeg2_picture_asset_writer.cc new file mode 100644 index 00000000..f5ead775 --- /dev/null +++ b/src/mpeg2_picture_asset_writer.cc @@ -0,0 +1,48 @@ +/* + Copyright (C) 2024 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. +*/ + + +#include "mpeg2_picture_asset.h" +#include "mpeg2_picture_asset_writer.h" + + +using namespace dcp; + + +MPEG2PictureAssetWriter::MPEG2PictureAssetWriter(MPEG2PictureAsset* asset, boost::filesystem::path file, bool overwrite) + : AssetWriter(asset, file) + , _picture_asset(asset) + , _overwrite(overwrite) +{ + asset->set_file(file); +} diff --git a/src/mpeg2_picture_asset_writer.h b/src/mpeg2_picture_asset_writer.h new file mode 100644 index 00000000..a370ef9b --- /dev/null +++ b/src/mpeg2_picture_asset_writer.h @@ -0,0 +1,73 @@ +/* + Copyright (C) 2024 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. +*/ + + +#ifndef LIBDCP_MPEG2_PICTURE_ASSET_WRITER_H +#define LIBDCP_MPEG2_PICTURE_ASSET_WRITER_H + + +#include "asset_writer.h" +#include "frame_info.h" + + +namespace dcp { + + +class Data; +class MPEG2PictureAsset; + + +class MPEG2PictureAssetWriter : public AssetWriter +{ +public: + virtual MPEG2FrameInfo write(uint8_t const* data, int size) = 0; + virtual void fake_write(MPEG2FrameInfo const& info) = 0; + + MPEG2FrameInfo write(Data const& data); + +protected: + template <class P, class Q> + friend void start(MPEG2PictureAssetWriter *, std::shared_ptr<P>, Q *, uint8_t const *, int); + + MPEG2PictureAssetWriter(MPEG2PictureAsset* asset, boost::filesystem::path file, bool overwrite); + + MPEG2PictureAsset* _picture_asset = nullptr; + bool _overwrite = false; +}; + + +} + + +#endif + diff --git a/src/mpeg2_picture_asset_writer_common.cc b/src/mpeg2_picture_asset_writer_common.cc new file mode 100644 index 00000000..d46057f7 --- /dev/null +++ b/src/mpeg2_picture_asset_writer_common.cc @@ -0,0 +1,93 @@ +/* + Copyright (C) 2012-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/mpeg2_picture_asset_writer_common.cc + * @brief Common parts of MPEG2PictureAssetWriter + */ + + +#include "filesystem.h" + + +using std::shared_ptr; + + +namespace dcp { + + +class MPEG2PictureAssetWriter; + + +struct ASDCPMPEG2StateBase +{ + ASDCP::MPEG2::Parser mpeg2_parser; + ASDCP::WriterInfo writer_info; + ASDCP::MPEG2::VideoDescriptor video_descriptor; +}; + + +} + + +template <class P, class Q> +void dcp::start(MPEG2PictureAssetWriter* writer, shared_ptr<P> state, Q* asset, uint8_t const * data, int size) +{ + asset->set_file (writer->_file); + + if (ASDCP_FAILURE(state->mpeg2_parser.OpenRead(data, size))) { + boost::throw_exception(MiscError("could not parse MPEG2 frame")); + } + + state->mpeg2_parser.FillVideoDescriptor(state->video_descriptor); + state->video_descriptor.EditRate = ASDCP::Rational(asset->edit_rate().numerator, asset->edit_rate().denominator); + + asset->set_size(Size(state->video_descriptor.StoredWidth, state->video_descriptor.StoredHeight)); + asset->set_screen_aspect_ratio(Fraction(state->video_descriptor.AspectRatio.Numerator, state->video_descriptor.AspectRatio.Denominator)); + + asset->fill_writer_info(&state->writer_info, asset->id()); + + auto r = state->mxf_writer.OpenWrite( + dcp::filesystem::fix_long_path(*asset->file()).string().c_str(), + state->writer_info, + state->video_descriptor, + 16384, + writer->_overwrite + ); + + if (ASDCP_FAILURE(r)) { + boost::throw_exception(MXFFileError("could not open MXF file for writing", asset->file()->string(), r)); + } + + writer->_started = true; +} diff --git a/src/mpeg2_transcode.cc b/src/mpeg2_transcode.cc new file mode 100644 index 00000000..0ac2c1af --- /dev/null +++ b/src/mpeg2_transcode.cc @@ -0,0 +1,216 @@ +/* + Copyright (C) 2023 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. +*/ + + +#include "compose.hpp" +#include "exceptions.h" +#include "mono_mpeg2_picture_frame.h" +#include "mpeg2_transcode.h" +#include "scope_guard.h" +extern "C" { +#include <libavcodec/avcodec.h> +} + + +using std::make_shared; +using std::shared_ptr; +using std::vector; +using boost::optional; +using namespace dcp; + + +MPEG2Codec::~MPEG2Codec() +{ + avcodec_free_context(&_context); +} + + + +MPEG2Decompressor::MPEG2Decompressor() +{ + _codec = avcodec_find_decoder_by_name("mpeg2video"); + if (!_codec) { + throw MPEG2CodecError("could not find codec"); + } + + _context = avcodec_alloc_context3(_codec); + if (!_context) { + throw MPEG2CodecError("could not allocate codec context"); + } + + int const r = avcodec_open2(_context, _codec, nullptr); + if (r < 0) { + avcodec_free_context(&_context); + throw MPEG2CodecError("could not open codec"); + } + + _decompressed_frame = av_frame_alloc(); + if (!_decompressed_frame) { + throw std::bad_alloc(); + } +} + + +MPEG2Decompressor::~MPEG2Decompressor() +{ + av_frame_free(&_decompressed_frame); +} + + +vector<FFmpegImage> +MPEG2Decompressor::decompress_frame(shared_ptr<const MonoMPEG2PictureFrame> frame) +{ + /* XXX: can we avoid this? */ + auto copy = av_malloc(frame->size() + AV_INPUT_BUFFER_PADDING_SIZE); + if (!copy) { + throw std::bad_alloc(); + } + memcpy(copy, frame->data(), frame->size()); + + AVPacket packet; + av_init_packet(&packet); + av_packet_from_data(&packet, reinterpret_cast<uint8_t*>(copy), frame->size()); + + auto images = decompress_packet(&packet); + + av_packet_unref(&packet); + + return images; +} + + +vector<FFmpegImage> +MPEG2Decompressor::flush() +{ + return decompress_packet(nullptr); +} + + +vector<FFmpegImage> +MPEG2Decompressor::decompress_packet(AVPacket* packet) +{ + int const r = avcodec_send_packet(_context, packet); + if (r < 0) { + throw MPEG2DecompressionError(String::compose("avcodec_send_packet failed (%1)", r)); + } + + vector<FFmpegImage> images; + while (true) { + int const r = avcodec_receive_frame(_context, _decompressed_frame); + if (r == AVERROR(EAGAIN) || r == AVERROR_EOF) { + break; + } else if (r < 0) { + throw MPEG2DecompressionError("avcodec_receive_frame failed"); + } + + auto clone = av_frame_clone(_decompressed_frame); + if (!clone) { + throw std::bad_alloc(); + } + + images.push_back(FFmpegImage(clone)); + } + + return images; +} + + +MPEG2Compressor::MPEG2Compressor(dcp::Size size, int video_frame_rate, int64_t bit_rate) +{ + _codec = avcodec_find_encoder_by_name("mpeg2video"); + if (!_codec) { + throw MPEG2CodecError("could not find codec"); + } + + _context = avcodec_alloc_context3(_codec); + if (!_context) { + throw MPEG2CodecError("could not allocate codec context"); + } + + _context->width = size.width; + _context->height = size.height; + _context->time_base = AVRational{1, video_frame_rate}; + _context->pix_fmt = AV_PIX_FMT_YUV420P; + _context->bit_rate = bit_rate; + + int const r = avcodec_open2(_context, _codec, nullptr); + if (r < 0) { + avcodec_free_context(&_context); + throw MPEG2CodecError("could not open codec"); + } +} + + +optional<MPEG2Compressor::IndexedFrame> +MPEG2Compressor::send_and_receive(AVFrame const* frame) +{ + int r = avcodec_send_frame(_context, frame); + if (r < 0) { + throw MPEG2CompressionError(String::compose("avcodec_send_frame failed (%1", r)); + } + + auto packet = av_packet_alloc(); + if (!packet) { + throw MPEG2CompressionError("could not allocate packet"); + } + + r = avcodec_receive_packet(_context, packet); + if (r < 0 && r != AVERROR(EAGAIN)) { + throw MPEG2CompressionError(String::compose("avcodec_receive_packet failed (%1)", r)); + } + + ScopeGuard sg = [&packet]() { + av_packet_free(&packet); + }; + + if (packet->size == 0) { + return {}; + } + + DCP_ASSERT(_context->time_base.num == 1); + return IndexedFrame{make_shared<MonoMPEG2PictureFrame>(packet->data, packet->size), std::round(static_cast<double>(packet->pts) / _context->time_base.den)}; +} + + +optional<MPEG2Compressor::IndexedFrame> +MPEG2Compressor::compress_frame(FFmpegImage const& image) +{ + return send_and_receive(image.frame()); +} + + +optional<MPEG2Compressor::IndexedFrame> +MPEG2Compressor::flush() +{ + return send_and_receive(nullptr); +} diff --git a/src/mpeg2_transcode.h b/src/mpeg2_transcode.h new file mode 100644 index 00000000..1f13cfc3 --- /dev/null +++ b/src/mpeg2_transcode.h @@ -0,0 +1,108 @@ +/* + Copyright (C) 2023 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. +*/ + + +#ifndef LIBDCP_MPEG2_TRANSCODE_H +#define LIBDCP_MPEG2_TRANSCODE_H + + +#include "ffmpeg_image.h" +#include <memory> + + +struct AVCodec; +struct AVCodecContext; +struct AVFrame; +struct AVPacket; + + +namespace dcp { + + +class MonoMPEG2PictureFrame; + + +class MPEG2Codec +{ +public: + MPEG2Codec() = default; + virtual ~MPEG2Codec(); + + MPEG2Codec(MPEG2Codec const&) = delete; + MPEG2Codec& operator=(MPEG2Codec const&) = delete; + +protected: + AVCodec const* _codec; + AVCodecContext* _context; +}; + + +class MPEG2Decompressor : public MPEG2Codec +{ +public: + MPEG2Decompressor(); + ~MPEG2Decompressor(); + + std::vector<FFmpegImage> decompress_frame(std::shared_ptr<const MonoMPEG2PictureFrame> frame); + std::vector<FFmpegImage> flush(); + +private: + std::vector<FFmpegImage> decompress_packet(AVPacket* packet); + + AVFrame* _decompressed_frame; +}; + + +class MPEG2Compressor : public MPEG2Codec +{ +public: + MPEG2Compressor(dcp::Size size, int video_frame_rate, int64_t bit_rate); + + MPEG2Compressor(MPEG2Compressor const&) = delete; + MPEG2Compressor& operator=(MPEG2Compressor const&) = delete; + + /** Frame data with frame index within the asset */ + typedef std::pair<std::shared_ptr<MonoMPEG2PictureFrame>, int64_t> IndexedFrame; + + boost::optional<IndexedFrame> compress_frame(FFmpegImage const& image); + boost::optional<IndexedFrame> flush(); + +private: + boost::optional<IndexedFrame> send_and_receive(AVFrame const* frame); +}; + + +} + + +#endif @@ -64,7 +64,8 @@ namespace dcp class MXFMetadata; -class PictureAssetWriter; +class J2KPictureAssetWriter; +class MPEG2PictureAssetWriter; /** @class MXF @@ -136,7 +137,9 @@ public: protected: template <class P, class Q> - friend void start (PictureAssetWriter* writer, std::shared_ptr<P> state, Q* mxf, uint8_t const * data, int size); + friend void start (J2KPictureAssetWriter* writer, std::shared_ptr<P> state, Q* mxf, uint8_t const * data, int size); + template <class P, class Q> + friend void start (MPEG2PictureAssetWriter* writer, std::shared_ptr<P> state, Q* mxf, uint8_t const * data, int size); MXF (); diff --git a/src/name_format.h b/src/name_format.h index 6401fe82..3eb0277b 100644 --- a/src/name_format.h +++ b/src/name_format.h @@ -37,8 +37,8 @@ */ -#ifndef LIBDCP_NAME_FORMAT -#define LIBDCP_NAME_FORMAT +#ifndef LIBDCP_NAME_FORMAT_H +#define LIBDCP_NAME_FORMAT_H #include <string> diff --git a/src/picture_asset.cc b/src/picture_asset.cc index c9f669a6..3b28bf4b 100644 --- a/src/picture_asset.cc +++ b/src/picture_asset.cc @@ -32,199 +32,24 @@ */ -/** @file src/picture_asset.cc - * @brief PictureAsset class - */ - - -#include "compose.hpp" -#include "dcp_assert.h" -#include "equality_options.h" -#include "exceptions.h" -#include "j2k_transcode.h" -#include "openjpeg_image.h" #include "picture_asset.h" -#include "picture_asset_writer.h" -#include "util.h" -#include <asdcp/AS_DCP.h> -#include <asdcp/KM_fileio.h> -#include <libxml++/nodes/element.h> -#include <boost/filesystem.hpp> -#include <list> -#include <stdexcept> - - -using std::string; -using std::list; -using std::vector; -using std::max; -using std::pair; -using std::make_pair; -using std::shared_ptr; -using namespace dcp; - - -PictureAsset::PictureAsset (boost::filesystem::path file) - : Asset (file) -{ - -} - -PictureAsset::PictureAsset (Fraction edit_rate, Standard standard) - : MXF (standard) - , _edit_rate (edit_rate) -{ -} +using namespace dcp; -void -PictureAsset::read_picture_descriptor (ASDCP::JP2K::PictureDescriptor const & desc) +PictureAsset::PictureAsset(boost::filesystem::path file) + : Asset(file) { - _size.width = desc.StoredWidth; - _size.height = desc.StoredHeight; - _edit_rate = Fraction (desc.EditRate.Numerator, desc.EditRate.Denominator); - _intrinsic_duration = desc.ContainerDuration; - _frame_rate = Fraction (desc.SampleRate.Numerator, desc.SampleRate.Denominator); - _screen_aspect_ratio = Fraction (desc.AspectRatio.Numerator, desc.AspectRatio.Denominator); -} - -bool -PictureAsset::descriptor_equals ( - ASDCP::JP2K::PictureDescriptor const & a, ASDCP::JP2K::PictureDescriptor const & b, NoteHandler note - ) const -{ - if ( - a.EditRate != b.EditRate || - a.SampleRate != b.SampleRate || - a.StoredWidth != b.StoredWidth || - a.StoredHeight != b.StoredHeight || - a.AspectRatio != b.AspectRatio || - a.Rsize != b.Rsize || - a.Xsize != b.Xsize || - a.Ysize != b.Ysize || - a.XOsize != b.XOsize || - a.YOsize != b.YOsize || - a.XTsize != b.XTsize || - a.YTsize != b.YTsize || - a.XTOsize != b.XTOsize || - a.YTOsize != b.YTOsize || - a.Csize != b.Csize -// a.CodingStyleDefault != b.CodingStyleDefault || -// a.QuantizationDefault != b.QuantizationDefault - ) { - - note (NoteType::ERROR, "video MXF picture descriptors differ"); - return false; - } - - if (a.ContainerDuration != b.ContainerDuration) { - note (NoteType::ERROR, "video container durations differ"); - } - -// for (unsigned int j = 0; j < ASDCP::JP2K::MaxComponents; ++j) { -// if (a.ImageComponents[j] != b.ImageComponents[j]) { -// notes.pack_start ("video MXF picture descriptors differ"); -// } -// } - - return true; } -bool -PictureAsset::frame_buffer_equals ( - int frame, EqualityOptions const& opt, NoteHandler note, - uint8_t const * data_A, unsigned int size_A, uint8_t const * data_B, unsigned int size_B - ) const +PictureAsset::PictureAsset(Fraction edit_rate, Standard standard) + : MXF(standard) + , _edit_rate(edit_rate) { - if (size_A == size_B && memcmp (data_A, data_B, size_A) == 0) { - note (NoteType::NOTE, "J2K identical"); - /* Easy result; the J2K data is identical */ - return true; - } - - /* Decompress the images to bitmaps */ - auto image_A = decompress_j2k (const_cast<uint8_t*>(data_A), size_A, 0); - auto image_B = decompress_j2k (const_cast<uint8_t*>(data_B), size_B, 0); - - /* Compare them */ - - vector<int> abs_diffs (image_A->size().width * image_A->size().height * 3); - int d = 0; - int max_diff = 0; - for (int c = 0; c < 3; ++c) { - - if (image_A->size() != image_B->size()) { - note (NoteType::ERROR, String::compose ("image sizes for frame %1 differ", frame)); - return false; - } - - int const pixels = image_A->size().width * image_A->size().height; - for (int j = 0; j < pixels; ++j) { - int const t = abs (image_A->data(c)[j] - image_B->data(c)[j]); - abs_diffs[d++] = t; - max_diff = max (max_diff, t); - } - } - - uint64_t total = 0; - for (vector<int>::iterator j = abs_diffs.begin(); j != abs_diffs.end(); ++j) { - total += *j; - } - - double const mean = double (total) / abs_diffs.size (); - - uint64_t total_squared_deviation = 0; - for (auto j: abs_diffs) { - total_squared_deviation += pow (j - mean, 2); - } - - auto const std_dev = sqrt (double (total_squared_deviation) / abs_diffs.size()); - - note (NoteType::NOTE, String::compose("mean difference %1 deviation %2", mean, std_dev)); - - if (mean > opt.max_mean_pixel_error) { - note ( - NoteType::ERROR, - String::compose ("mean %1 out of range %2 in frame %3", mean, opt.max_mean_pixel_error, frame) - ); - - return false; - } - - if (std_dev > opt.max_std_dev_pixel_error) { - note ( - NoteType::ERROR, - String::compose ("standard deviation %1 out of range %2 in frame %3", std_dev, opt.max_std_dev_pixel_error, frame) - ); - - return false; - } - - return true; } -string -PictureAsset::static_pkl_type (Standard standard) -{ - switch (standard) { - case Standard::INTEROP: - return "application/x-smpte-mxf;asdcpKind=Picture"; - case Standard::SMPTE: - return "application/mxf"; - default: - DCP_ASSERT (false); - } -} - - -string -PictureAsset::pkl_type (Standard standard) const -{ - return static_pkl_type (standard); -} diff --git a/src/picture_asset.h b/src/picture_asset.h index 9ad1eb22..36364485 100644 --- a/src/picture_asset.h +++ b/src/picture_asset.h @@ -32,56 +32,31 @@ */ -/** @file src/picture_asset.h - * @brief PictureAsset class - */ - - #ifndef LIBDCP_PICTURE_ASSET_H #define LIBDCP_PICTURE_ASSET_H +#include "asset.h" #include "mxf.h" -#include "util.h" -#include "metadata.h" - - -namespace ASDCP { - namespace JP2K { - struct PictureDescriptor; - } -} +#include "types.h" namespace dcp { -class MonoPictureFrame; -class StereoPictureFrame; -class PictureAssetWriter; - - -/** @class PictureAsset - * @brief An asset made up of JPEG2000 data - */ class PictureAsset : public Asset, public MXF { public: - /** Load a PictureAsset from a file */ - explicit PictureAsset (boost::filesystem::path file); - - /** Create a new PictureAsset with a given edit rate and standard */ + explicit PictureAsset(boost::filesystem::path file); PictureAsset(Fraction edit_rate, Standard standard); - enum class Behaviour { - OVERWRITE_EXISTING, - MAKE_NEW - }; + Fraction edit_rate () const { + return _edit_rate; + } - virtual std::shared_ptr<PictureAssetWriter> start_write ( - boost::filesystem::path file, - Behaviour behaviour - ) = 0; + int64_t intrinsic_duration () const { + return _intrinsic_duration; + } Size size () const { return _size; @@ -107,33 +82,7 @@ public: _screen_aspect_ratio = r; } - Fraction edit_rate () const { - return _edit_rate; - } - - int64_t intrinsic_duration () const { - return _intrinsic_duration; - } - - static std::string static_pkl_type (Standard standard); - protected: - friend class MonoPictureAssetWriter; - friend class StereoPictureAssetWriter; - - bool frame_buffer_equals ( - int frame, EqualityOptions const& opt, NoteHandler note, - uint8_t const * data_A, unsigned int size_A, uint8_t const * data_B, unsigned int size_B - ) const; - - bool descriptor_equals ( - ASDCP::JP2K::PictureDescriptor const & a, - ASDCP::JP2K::PictureDescriptor const & b, - NoteHandler note - ) const; - - void read_picture_descriptor (ASDCP::JP2K::PictureDescriptor const &); - Fraction _edit_rate; /** The total length of this content in video frames. The amount of * content presented may be less than this. @@ -144,8 +93,6 @@ protected: Fraction _frame_rate; Fraction _screen_aspect_ratio; -private: - std::string pkl_type (Standard standard) const override; }; @@ -153,3 +100,4 @@ private: #endif + @@ -105,26 +105,26 @@ PKL::write_xml (boost::filesystem::path file, shared_ptr<const CertificateChain> pkl = doc.create_root_node("PackingList", pkl_smpte_ns); } - pkl->add_child("Id")->add_child_text ("urn:uuid:" + _id); + cxml::add_text_child(pkl, "Id", "urn:uuid:" + _id); if (_annotation_text) { - pkl->add_child("AnnotationText")->add_child_text (*_annotation_text); + cxml::add_text_child(pkl, "AnnotationText", *_annotation_text); } - pkl->add_child("IssueDate")->add_child_text (_issue_date); - pkl->add_child("Issuer")->add_child_text (_issuer); - pkl->add_child("Creator")->add_child_text (_creator); + cxml::add_text_child(pkl, "IssueDate", _issue_date); + cxml::add_text_child(pkl, "Issuer", _issuer); + cxml::add_text_child(pkl, "Creator", _creator); - auto asset_list = pkl->add_child("AssetList"); + auto asset_list = cxml::add_child(pkl, "AssetList"); for (auto i: _assets) { - auto asset = asset_list->add_child("Asset"); - asset->add_child("Id")->add_child_text ("urn:uuid:" + i->id()); + auto asset = cxml::add_child(asset_list, "Asset"); + cxml::add_text_child(asset, "Id", "urn:uuid:" + i->id()); if (i->annotation_text()) { - asset->add_child("AnnotationText")->add_child_text (*i->annotation_text()); + cxml::add_text_child(asset, "AnnotationText", *i->annotation_text()); } - asset->add_child("Hash")->add_child_text (i->hash()); - asset->add_child("Size")->add_child_text (raw_convert<string>(i->size())); - asset->add_child("Type")->add_child_text (i->type()); + cxml::add_text_child(asset, "Hash", i->hash()); + cxml::add_text_child(asset, "Size", raw_convert<string>(i->size())); + cxml::add_text_child(asset, "Type", i->type()); if (auto filename = i->original_filename()) { - asset->add_child("OriginalFileName")->add_child_text(*filename); + cxml::add_text_child(asset, "OriginalFileName", *filename); } } @@ -32,7 +32,7 @@ */ -/** @file src/pkl.cc +/** @file src/pkl.h * @brief PKL class */ diff --git a/src/rating.cc b/src/rating.cc index 21960365..156d5ad0 100644 --- a/src/rating.cc +++ b/src/rating.cc @@ -61,8 +61,8 @@ Rating::Rating (cxml::ConstNodePtr node) void Rating::as_xml (xmlpp::Element* parent) const { - parent->add_child("Agency")->add_child_text(agency); - parent->add_child("Label")->add_child_text(label); + cxml::add_text_child(parent, "Agency", agency); + cxml::add_text_child(parent, "Label", label); } diff --git a/src/reel.cc b/src/reel.cc index a8481d59..acd7c7fc 100644 --- a/src/reel.cc +++ b/src/reel.cc @@ -41,8 +41,8 @@ #include "decrypted_kdm_key.h" #include "equality_options.h" #include "interop_subtitle_asset.h" -#include "mono_picture_asset.h" -#include "picture_asset.h" +#include "mono_j2k_picture_asset.h" +#include "j2k_picture_asset.h" #include "reel.h" #include "reel_atmos_asset.h" #include "reel_closed_caption_asset.h" @@ -57,7 +57,7 @@ #include "reel_subtitle_asset.h" #include "smpte_subtitle_asset.h" #include "sound_asset.h" -#include "stereo_picture_asset.h" +#include "stereo_j2k_picture_asset.h" #include "subtitle_asset.h" #include "util.h" #include <libxml++/nodes/element.h> @@ -141,9 +141,9 @@ Reel::Reel (std::shared_ptr<const cxml::Node> node, dcp::Standard standard) xmlpp::Element * Reel::write_to_cpl (xmlpp::Element* node, Standard standard) const { - auto reel = node->add_child ("Reel"); - reel->add_child("Id")->add_child_text("urn:uuid:" + _id); - xmlpp::Element* asset_list = reel->add_child ("AssetList"); + auto reel = cxml::add_child(node, "Reel"); + cxml::add_text_child(reel, "Id", "urn:uuid:" + _id); + auto asset_list = cxml::add_child(reel, "AssetList"); if (_main_markers) { _main_markers->write_to_cpl (asset_list, standard); @@ -327,7 +327,7 @@ Reel::give_kdm_to_assets (DecryptedKDM const & kdm) { for (auto const& i: kdm.keys()) { if (_main_picture && i.id() == _main_picture->key_id() && _main_picture->asset_ref().resolved()) { - _main_picture->asset()->set_key (i.key()); + _main_picture->j2k_asset()->set_key(i.key()); } if (_main_sound && i.id() == _main_sound->key_id() && _main_sound->asset_ref().resolved()) { _main_sound->asset()->set_key (i.key()); @@ -32,7 +32,7 @@ */ -/** @file src/reel.cc +/** @file src/reel.h * @brief Reel class */ diff --git a/src/reel_asset.cc b/src/reel_asset.cc index 3a3ae731..c782cf2b 100644 --- a/src/reel_asset.cc +++ b/src/reel_asset.cc @@ -84,10 +84,10 @@ ReelAsset::ReelAsset (shared_ptr<const cxml::Node> node) } -xmlpp::Node* -ReelAsset::write_to_cpl (xmlpp::Node* node, Standard standard) const +xmlpp::Element* +ReelAsset::write_to_cpl(xmlpp::Element* node, Standard standard) const { - auto a = node->add_child (cpl_node_name (standard)); + auto a = cxml::add_child(node, cpl_node_name(standard)); auto const attr = cpl_node_attribute (standard); if (!attr.first.empty ()) { a->set_attribute (attr.first, attr.second); @@ -96,18 +96,18 @@ ReelAsset::write_to_cpl (xmlpp::Node* node, Standard standard) const if (!ns.first.empty()) { a->set_namespace_declaration (ns.first, ns.second); } - a->add_child("Id")->add_child_text ("urn:uuid:" + _id); + cxml::add_text_child(a, "Id", "urn:uuid:" + _id); /* Empty <AnnotationText> tags cause refusal to play on some Sony SRX320 / LMT3000 systems (DoM bug #2124) */ if (_annotation_text && !_annotation_text->empty()) { - a->add_child("AnnotationText")->add_child_text(*_annotation_text); + cxml::add_text_child(a, "AnnotationText", *_annotation_text); } - a->add_child("EditRate")->add_child_text (_edit_rate.as_string()); - a->add_child("IntrinsicDuration")->add_child_text (raw_convert<string> (_intrinsic_duration)); + cxml::add_text_child(a, "EditRate", _edit_rate.as_string()); + cxml::add_text_child(a, "IntrinsicDuration", raw_convert<string>(_intrinsic_duration)); if (_entry_point) { - a->add_child("EntryPoint")->add_child_text(raw_convert<string>(*_entry_point)); + cxml::add_text_child(a, "EntryPoint", raw_convert<string>(*_entry_point)); } if (_duration) { - a->add_child("Duration")->add_child_text(raw_convert<string>(*_duration)); + cxml::add_text_child(a, "Duration", raw_convert<string>(*_duration)); } return a; } diff --git a/src/reel_asset.h b/src/reel_asset.h index e928cb18..8dad739e 100644 --- a/src/reel_asset.h +++ b/src/reel_asset.h @@ -83,7 +83,7 @@ public: explicit ReelAsset (std::shared_ptr<const cxml::Node>); - virtual xmlpp::Node* write_to_cpl (xmlpp::Node* node, Standard standard) const; + virtual xmlpp::Element* write_to_cpl(xmlpp::Element* node, Standard standard) const; virtual bool encryptable () const { return false; diff --git a/src/reel_atmos_asset.cc b/src/reel_atmos_asset.cc index ef39a4eb..c2cdb7f3 100644 --- a/src/reel_atmos_asset.cc +++ b/src/reel_atmos_asset.cc @@ -82,11 +82,11 @@ ReelAtmosAsset::cpl_node_namespace () const } -xmlpp::Node * -ReelAtmosAsset::write_to_cpl (xmlpp::Node* node, Standard standard) const +xmlpp::Element* +ReelAtmosAsset::write_to_cpl(xmlpp::Element* node, Standard standard) const { auto asset = ReelFileAsset::write_to_cpl (node, standard); - asset->add_child("axd:DataType")->add_child_text("urn:smpte:ul:060e2b34.04010105.0e090604.00000000"); + cxml::add_text_child(asset, "axd:DataType", "urn:smpte:ul:060e2b34.04010105.0e090604.00000000"); return asset; } diff --git a/src/reel_atmos_asset.h b/src/reel_atmos_asset.h index 298cbbfd..c3de76a8 100644 --- a/src/reel_atmos_asset.h +++ b/src/reel_atmos_asset.h @@ -68,7 +68,7 @@ public: return asset_of_type<AtmosAsset>(); } - xmlpp::Node* write_to_cpl (xmlpp::Node* node, Standard standard) const override; + xmlpp::Element* write_to_cpl(xmlpp::Element* node, Standard standard) const override; bool equals(std::shared_ptr<const ReelAtmosAsset>, EqualityOptions const&, NoteHandler) const; private: diff --git a/src/reel_file_asset.cc b/src/reel_file_asset.cc index 5fefda27..8fed8012 100644 --- a/src/reel_file_asset.cc +++ b/src/reel_file_asset.cc @@ -94,15 +94,15 @@ ReelFileAsset::file_asset_equals(shared_ptr<const ReelFileAsset> other, Equality } -xmlpp::Node * -ReelFileAsset::write_to_cpl (xmlpp::Node* node, Standard standard) const +xmlpp::Element* +ReelFileAsset::write_to_cpl(xmlpp::Element* node, Standard standard) const { - auto asset = ReelAsset::write_to_cpl (node, standard); + auto asset = ReelAsset::write_to_cpl(node, standard); if (_key_id) { - asset->add_child("KeyId")->add_child_text("urn:uuid:" + *_key_id); + cxml::add_text_child(asset, "KeyId", "urn:uuid:" + *_key_id); } if (_hash) { - asset->add_child("Hash")->add_child_text(*_hash); + cxml::add_text_child(asset, "Hash", *_hash); } return asset; } diff --git a/src/reel_file_asset.h b/src/reel_file_asset.h index 687e0d3e..48fdf215 100644 --- a/src/reel_file_asset.h +++ b/src/reel_file_asset.h @@ -56,7 +56,7 @@ public: ReelFileAsset (std::shared_ptr<Asset> asset, boost::optional<std::string> key_id, std::string id, Fraction edit_rate, int64_t intrinsic_duration, int64_t entry_point); explicit ReelFileAsset (std::shared_ptr<const cxml::Node> node); - virtual xmlpp::Node* write_to_cpl (xmlpp::Node* node, Standard standard) const override; + virtual xmlpp::Element* write_to_cpl(xmlpp::Element* node, Standard standard) const override; /** @return a Ref to our actual asset */ Ref const & asset_ref () const { diff --git a/src/reel_interop_closed_caption_asset.cc b/src/reel_interop_closed_caption_asset.cc index be968068..c4539fd7 100644 --- a/src/reel_interop_closed_caption_asset.cc +++ b/src/reel_interop_closed_caption_asset.cc @@ -75,12 +75,12 @@ ReelInteropClosedCaptionAsset::cpl_node_namespace () const } -xmlpp::Node * -ReelInteropClosedCaptionAsset::write_to_cpl (xmlpp::Node* node, Standard standard) const +xmlpp::Element* +ReelInteropClosedCaptionAsset::write_to_cpl(xmlpp::Element* node, Standard standard) const { auto asset = ReelClosedCaptionAsset::write_to_cpl (node, standard); if (_language) { - asset->add_child("Language")->add_child_text(*_language); + cxml::add_text_child(asset, "Language", *_language); } return asset; } diff --git a/src/reel_interop_closed_caption_asset.h b/src/reel_interop_closed_caption_asset.h index 5e8f7c1e..5a074357 100644 --- a/src/reel_interop_closed_caption_asset.h +++ b/src/reel_interop_closed_caption_asset.h @@ -62,7 +62,7 @@ public: return asset_of_type<InteropSubtitleAsset>(); } - xmlpp::Node* write_to_cpl (xmlpp::Node* node, Standard standard) const override; + xmlpp::Element* write_to_cpl(xmlpp::Element* node, Standard standard) const override; private: std::string cpl_node_name (Standard) const override; diff --git a/src/reel_markers_asset.cc b/src/reel_markers_asset.cc index d71a22ec..4b1b3472 100644 --- a/src/reel_markers_asset.cc +++ b/src/reel_markers_asset.cc @@ -104,16 +104,16 @@ ReelMarkersAsset::get (Marker m) const } -xmlpp::Node* -ReelMarkersAsset::write_to_cpl (xmlpp::Node* node, Standard standard) const +xmlpp::Element* +ReelMarkersAsset::write_to_cpl(xmlpp::Element* node, Standard standard) const { int const tcr = edit_rate().numerator / edit_rate().denominator; auto asset = ReelAsset::write_to_cpl (node, standard); - auto ml = asset->add_child("MarkerList"); + auto ml = cxml::add_child(asset, "MarkerList"); for (auto const& i: _markers) { - auto m = ml->add_child("Marker"); - m->add_child("Label")->add_child_text(marker_to_string(i.first)); - m->add_child("Offset")->add_child_text(raw_convert<string>(i.second.as_editable_units_ceil(tcr))); + auto m = cxml::add_child(ml, "Marker"); + cxml::add_text_child(m, "Label", marker_to_string(i.first)); + cxml::add_text_child(m, "Offset", raw_convert<string>(i.second.as_editable_units_ceil(tcr))); } return asset; diff --git a/src/reel_markers_asset.h b/src/reel_markers_asset.h index 2a1480a2..e2b65585 100644 --- a/src/reel_markers_asset.h +++ b/src/reel_markers_asset.h @@ -32,7 +32,7 @@ */ -/** @file src/reel_markers_asset.cc +/** @file src/reel_markers_asset.h * @brief ReelMarkersAsset class */ @@ -51,7 +51,7 @@ public: ReelMarkersAsset (Fraction edit_rate, int64_t intrinsic_duration); explicit ReelMarkersAsset (std::shared_ptr<const cxml::Node>); - xmlpp::Node* write_to_cpl (xmlpp::Node* node, Standard standard) const override; + xmlpp::Element* write_to_cpl(xmlpp::Element* node, Standard standard) const override; bool equals(std::shared_ptr<const ReelMarkersAsset>, EqualityOptions const&, NoteHandler) const; void set (Marker, Time); diff --git a/src/reel_mono_picture_asset.cc b/src/reel_mono_picture_asset.cc index 9c44ec20..81eb4df9 100644 --- a/src/reel_mono_picture_asset.cc +++ b/src/reel_mono_picture_asset.cc @@ -38,7 +38,7 @@ #include "reel_mono_picture_asset.h" -#include "mono_picture_asset.h" +#include "mono_j2k_picture_asset.h" #include <libcxml/cxml.h> @@ -47,7 +47,7 @@ using std::shared_ptr; using namespace dcp; -ReelMonoPictureAsset::ReelMonoPictureAsset (std::shared_ptr<MonoPictureAsset> asset, int64_t entry_point) +ReelMonoPictureAsset::ReelMonoPictureAsset(std::shared_ptr<PictureAsset> asset, int64_t entry_point) : ReelPictureAsset (asset, entry_point) { diff --git a/src/reel_mono_picture_asset.h b/src/reel_mono_picture_asset.h index fb2dff70..13c6545e 100644 --- a/src/reel_mono_picture_asset.h +++ b/src/reel_mono_picture_asset.h @@ -42,13 +42,14 @@ #include "reel_picture_asset.h" -#include "mono_picture_asset.h" +#include "mono_j2k_picture_asset.h" +#include "mono_mpeg2_picture_asset.h" namespace dcp { -class MonoPictureAsset; +class MonoJ2KPictureAsset; /** @class ReelMonoPictureAsset @@ -57,17 +58,27 @@ class MonoPictureAsset; class ReelMonoPictureAsset : public ReelPictureAsset { public: - ReelMonoPictureAsset (std::shared_ptr<MonoPictureAsset> asset, int64_t entry_point); + ReelMonoPictureAsset(std::shared_ptr<PictureAsset> asset, int64_t entry_point); explicit ReelMonoPictureAsset (std::shared_ptr<const cxml::Node>); - /** @return the MonoPictureAsset that this object refers to */ - std::shared_ptr<const MonoPictureAsset> mono_asset () const { - return asset_of_type<const MonoPictureAsset>(); + /** @return the MonoJ2KPictureAsset that this object refers to, if applicable */ + std::shared_ptr<const MonoJ2KPictureAsset> mono_j2k_asset() const { + return asset_of_type<const MonoJ2KPictureAsset>(); } - /** @return the MonoPictureAsset that this object refers to */ - std::shared_ptr<MonoPictureAsset> mono_asset () { - return asset_of_type<MonoPictureAsset>(); + /** @return the MonoJ2KPictureAsset that this object refers to */ + std::shared_ptr<MonoJ2KPictureAsset> mono_j2k_asset() { + return asset_of_type<MonoJ2KPictureAsset>(); + } + + /** @return the MonoMPEG2PictureAsset that this object refers to, if applicable */ + std::shared_ptr<const MonoMPEG2PictureAsset> mono_mpeg2_asset() const { + return asset_of_type<const MonoMPEG2PictureAsset>(); + } + + /** @return the MonoMPEG2PictureAsset that this object refers to */ + std::shared_ptr<MonoMPEG2PictureAsset> mono_mpeg2_asset() { + return asset_of_type<MonoMPEG2PictureAsset>(); } private: diff --git a/src/reel_picture_asset.cc b/src/reel_picture_asset.cc index eb87d039..37a6bfcc 100644 --- a/src/reel_picture_asset.cc +++ b/src/reel_picture_asset.cc @@ -39,7 +39,7 @@ #include "compose.hpp" #include "dcp_assert.h" -#include "picture_asset.h" +#include "j2k_picture_asset.h" #include "raw_convert.h" #include "reel_picture_asset.h" #include "warnings.h" @@ -59,7 +59,7 @@ using boost::optional; using namespace dcp; -ReelPictureAsset::ReelPictureAsset (shared_ptr<PictureAsset> asset, int64_t entry_point) +ReelPictureAsset::ReelPictureAsset(shared_ptr<PictureAsset> asset, int64_t entry_point) : ReelFileAsset (asset, asset->key_id(), asset->id(), asset->edit_rate(), asset->intrinsic_duration(), entry_point) , _frame_rate (asset->frame_rate ()) , _screen_aspect_ratio (asset->screen_aspect_ratio ()) @@ -86,12 +86,12 @@ ReelPictureAsset::ReelPictureAsset (shared_ptr<const cxml::Node> node) } -xmlpp::Node* -ReelPictureAsset::write_to_cpl (xmlpp::Node* node, Standard standard) const +xmlpp::Element* +ReelPictureAsset::write_to_cpl(xmlpp::Element* node, Standard standard) const { auto asset = ReelFileAsset::write_to_cpl (node, standard); - asset->add_child("FrameRate")->add_child_text(String::compose("%1 %2", _frame_rate.numerator, _frame_rate.denominator)); + cxml::add_text_child(asset, "FrameRate", String::compose("%1 %2", _frame_rate.numerator, _frame_rate.denominator)); if (standard == Standard::INTEROP) { @@ -113,10 +113,12 @@ ReelPictureAsset::write_to_cpl (xmlpp::Node* node, Standard standard) const } } - asset->add_child("ScreenAspectRatio")->add_child_text(raw_convert<string>(closest.get(), 2, true)); + cxml::add_text_child(asset, "ScreenAspectRatio", raw_convert<string>(closest.get(), 2, true)); } else { - asset->add_child("ScreenAspectRatio")->add_child_text( - String::compose ("%1 %2", _screen_aspect_ratio.numerator, _screen_aspect_ratio.denominator) + cxml::add_text_child( + asset, + "ScreenAspectRatio", + String::compose("%1 %2", _screen_aspect_ratio.numerator, _screen_aspect_ratio.denominator) ); } diff --git a/src/reel_picture_asset.h b/src/reel_picture_asset.h index bf7d40aa..9f42a5b6 100644 --- a/src/reel_picture_asset.h +++ b/src/reel_picture_asset.h @@ -42,7 +42,8 @@ #include "reel_file_asset.h" -#include "picture_asset.h" +#include "j2k_picture_asset.h" +#include "mpeg2_picture_asset.h" namespace dcp { @@ -54,20 +55,41 @@ namespace dcp { class ReelPictureAsset : public ReelFileAsset { public: - ReelPictureAsset (std::shared_ptr<PictureAsset> asset, int64_t entry_point); + ReelPictureAsset(std::shared_ptr<PictureAsset> asset, int64_t entry_point); explicit ReelPictureAsset (std::shared_ptr<const cxml::Node>); - /** @return the PictureAsset that this object refers to */ - std::shared_ptr<const PictureAsset> asset () const { + /** @return the PictureAsset that this object refers to, if applicable */ + std::shared_ptr<const PictureAsset> asset() const { return asset_of_type<const PictureAsset>(); } - /** @return the PictureAsset that this object refers to */ - std::shared_ptr<PictureAsset> asset () { + /** @return the PictureAsset that this object refers to, if applicable */ + std::shared_ptr<PictureAsset> asset() { return asset_of_type<PictureAsset>(); } - virtual xmlpp::Node* write_to_cpl (xmlpp::Node* node, Standard standard) const override; + /** @return the J2KPictureAsset that this object refers to, if applicable */ + std::shared_ptr<const J2KPictureAsset> j2k_asset() const { + return asset_of_type<const J2KPictureAsset>(); + } + + /** @return the J2KPictureAsset that this object refers to, if applicable */ + std::shared_ptr<J2KPictureAsset> j2k_asset() { + return asset_of_type<J2KPictureAsset>(); + } + + /** @return the MPEG2PictureAsset that this object refers to, if applicable */ + std::shared_ptr<const MPEG2PictureAsset> mpeg2_asset() const { + return asset_of_type<const MPEG2PictureAsset>(); + } + + /** @return the MPEG2PictureAsset that this object refers to, if applicable */ + std::shared_ptr<MPEG2PictureAsset> mpeg2_asset() { + return asset_of_type<MPEG2PictureAsset>(); + } + + virtual xmlpp::Element* write_to_cpl(xmlpp::Element* node, Standard standard) const override; + bool equals(std::shared_ptr<const ReelPictureAsset>, EqualityOptions const&, NoteHandler) const; /** @return picture frame rate */ diff --git a/src/reel_smpte_closed_caption_asset.cc b/src/reel_smpte_closed_caption_asset.cc index a2a68202..70e5eb36 100644 --- a/src/reel_smpte_closed_caption_asset.cc +++ b/src/reel_smpte_closed_caption_asset.cc @@ -65,12 +65,12 @@ ReelSMPTEClosedCaptionAsset::ReelSMPTEClosedCaptionAsset (shared_ptr<const cxml: } -xmlpp::Node * -ReelSMPTEClosedCaptionAsset::write_to_cpl (xmlpp::Node* node, Standard standard) const +xmlpp::Element* +ReelSMPTEClosedCaptionAsset::write_to_cpl(xmlpp::Element* node, Standard standard) const { auto asset = ReelClosedCaptionAsset::write_to_cpl (node, standard); if (_language) { - asset->add_child("Language", "tt")->add_child_text(*_language); + cxml::add_child(asset, "Language", string("tt"))->add_child_text(*_language); } return asset; } diff --git a/src/reel_smpte_closed_caption_asset.h b/src/reel_smpte_closed_caption_asset.h index 32a79efd..e7a26f65 100644 --- a/src/reel_smpte_closed_caption_asset.h +++ b/src/reel_smpte_closed_caption_asset.h @@ -63,7 +63,7 @@ public: return asset_of_type<const SMPTESubtitleAsset>(); } - xmlpp::Node* write_to_cpl (xmlpp::Node* node, Standard standard) const override; + xmlpp::Element* write_to_cpl(xmlpp::Element* node, Standard standard) const override; private: diff --git a/src/reel_smpte_subtitle_asset.h b/src/reel_smpte_subtitle_asset.h index 2a097309..49b6000b 100644 --- a/src/reel_smpte_subtitle_asset.h +++ b/src/reel_smpte_subtitle_asset.h @@ -32,8 +32,8 @@ */ -/** @file src/reel_interop_subtitle_asset.h - * @brief ReelInteropSubtitleAsset class +/** @file src/reel_smpte_subtitle_asset.h + * @brief ReelSMPTESubtitleAsset class */ diff --git a/src/reel_stereo_picture_asset.cc b/src/reel_stereo_picture_asset.cc index 5bf4756c..13bfec8e 100644 --- a/src/reel_stereo_picture_asset.cc +++ b/src/reel_stereo_picture_asset.cc @@ -38,7 +38,7 @@ #include "reel_stereo_picture_asset.h" -#include "stereo_picture_asset.h" +#include "stereo_j2k_picture_asset.h" #include <libcxml/cxml.h> @@ -49,7 +49,7 @@ using std::shared_ptr; using namespace dcp; -ReelStereoPictureAsset::ReelStereoPictureAsset (std::shared_ptr<StereoPictureAsset> mxf, int64_t entry_point) +ReelStereoPictureAsset::ReelStereoPictureAsset (std::shared_ptr<StereoJ2KPictureAsset> mxf, int64_t entry_point) : ReelPictureAsset (mxf, entry_point) { diff --git a/src/reel_stereo_picture_asset.h b/src/reel_stereo_picture_asset.h index 7cac1c8b..09170ddd 100644 --- a/src/reel_stereo_picture_asset.h +++ b/src/reel_stereo_picture_asset.h @@ -42,13 +42,13 @@ #include "reel_picture_asset.h" -#include "stereo_picture_asset.h" +#include "stereo_j2k_picture_asset.h" namespace dcp { -class StereoPictureAsset; +class StereoJ2KPictureAsset; /** @class ReelStereoPictureAsset @@ -57,17 +57,17 @@ class StereoPictureAsset; class ReelStereoPictureAsset : public ReelPictureAsset { public: - ReelStereoPictureAsset (std::shared_ptr<StereoPictureAsset> content, int64_t entry_point); + ReelStereoPictureAsset (std::shared_ptr<StereoJ2KPictureAsset> content, int64_t entry_point); explicit ReelStereoPictureAsset (std::shared_ptr<const cxml::Node>); - /** @return the StereoPictureAsset that this object refers to */ - std::shared_ptr<const StereoPictureAsset> stereo_asset () const { - return asset_of_type<const StereoPictureAsset>(); + /** @return the StereoJ2KPictureAsset that this object refers to */ + std::shared_ptr<const StereoJ2KPictureAsset> stereo_asset () const { + return asset_of_type<const StereoJ2KPictureAsset>(); } - /** @return the StereoPictureAsset that this object refers to */ - std::shared_ptr<StereoPictureAsset> stereo_asset () { - return asset_of_type<StereoPictureAsset>(); + /** @return the StereoJ2KPictureAsset that this object refers to */ + std::shared_ptr<StereoJ2KPictureAsset> stereo_asset () { + return asset_of_type<StereoJ2KPictureAsset>(); } private: diff --git a/src/reel_subtitle_asset.cc b/src/reel_subtitle_asset.cc index d856a05e..436aa69f 100644 --- a/src/reel_subtitle_asset.cc +++ b/src/reel_subtitle_asset.cc @@ -103,12 +103,12 @@ ReelSubtitleAsset::equals(shared_ptr<const ReelSubtitleAsset> other, EqualityOpt } -xmlpp::Node * -ReelSubtitleAsset::write_to_cpl (xmlpp::Node* node, Standard standard) const +xmlpp::Element * +ReelSubtitleAsset::write_to_cpl(xmlpp::Element* node, Standard standard) const { auto asset = ReelFileAsset::write_to_cpl (node, standard); if (_language) { - asset->add_child("Language")->add_child_text(*_language); + cxml::add_text_child(asset, "Language", *_language); } return asset; } diff --git a/src/reel_subtitle_asset.h b/src/reel_subtitle_asset.h index 8b694fd6..c619c752 100644 --- a/src/reel_subtitle_asset.h +++ b/src/reel_subtitle_asset.h @@ -73,7 +73,7 @@ public: return asset_of_type<SubtitleAsset>(); } - xmlpp::Node* write_to_cpl (xmlpp::Node* node, Standard standard) const override; + xmlpp::Element* write_to_cpl(xmlpp::Element* node, Standard standard) const override; bool equals(std::shared_ptr<const ReelSubtitleAsset>, EqualityOptions const&, NoteHandler) const; diff --git a/src/rgb_xyz.h b/src/rgb_xyz.h index c41fdee0..315295ea 100644 --- a/src/rgb_xyz.h +++ b/src/rgb_xyz.h @@ -32,7 +32,7 @@ */ -/** @file rgb_xyz.h +/** @file src/rgb_xyz.h * @brief Conversion between RGB and XYZ */ diff --git a/src/smpte_subtitle_asset.cc b/src/smpte_subtitle_asset.cc index 0ff1d7ef..4f611583 100644 --- a/src/smpte_subtitle_asset.cc +++ b/src/smpte_subtitle_asset.cc @@ -95,7 +95,8 @@ SMPTESubtitleAsset::SMPTESubtitleAsset (boost::filesystem::path file) { auto xml = make_shared<cxml::Document>("SubtitleReel"); - auto reader = make_shared<ASDCP::TimedText::MXFReader>(); + Kumu::FileReaderFactory factory; + auto reader = make_shared<ASDCP::TimedText::MXFReader>(factory); auto r = Kumu::RESULT_OK; { ASDCPErrorSuspender sus; @@ -320,7 +321,8 @@ SMPTESubtitleAsset::set_key (Key key) /* Our data was encrypted; now we can decrypt it */ - auto reader = make_shared<ASDCP::TimedText::MXFReader>(); + Kumu::FileReaderFactory factory; + auto reader = make_shared<ASDCP::TimedText::MXFReader>(factory); auto r = reader->OpenRead(dcp::filesystem::fix_long_path(*_file).string().c_str()); if (ASDCP_FAILURE (r)) { boost::throw_exception ( @@ -354,7 +356,8 @@ SMPTESubtitleAsset::load_font_nodes () const bool SMPTESubtitleAsset::valid_mxf (boost::filesystem::path file) { - ASDCP::TimedText::MXFReader reader; + Kumu::FileReaderFactory factory; + ASDCP::TimedText::MXFReader reader(factory); Kumu::DefaultLogSink().UnsetFilterFlag(Kumu::LOG_ALLOW_ALL); auto r = reader.OpenRead(dcp::filesystem::fix_long_path(file).string().c_str()); Kumu::DefaultLogSink().SetFilterFlag(Kumu::LOG_ALLOW_ALL); @@ -369,31 +372,31 @@ SMPTESubtitleAsset::xml_as_string () const auto root = doc.create_root_node ("SubtitleReel"); DCP_ASSERT (_xml_id); - root->add_child("Id")->add_child_text("urn:uuid:" + *_xml_id); - root->add_child("ContentTitleText")->add_child_text(_content_title_text); + cxml::add_text_child(root, "Id", "urn:uuid:" + *_xml_id); + cxml::add_text_child(root, "ContentTitleText", _content_title_text); if (_annotation_text) { - root->add_child("AnnotationText")->add_child_text(_annotation_text.get()); + cxml::add_text_child(root, "AnnotationText", _annotation_text.get()); } - root->add_child("IssueDate")->add_child_text(_issue_date.as_string(false, false)); + cxml::add_text_child(root, "IssueDate", _issue_date.as_string(false, false)); if (_reel_number) { - root->add_child("ReelNumber")->add_child_text(raw_convert<string>(_reel_number.get())); + cxml::add_text_child(root, "ReelNumber", raw_convert<string>(_reel_number.get())); } if (_language) { - root->add_child("Language")->add_child_text(_language.get()); + cxml::add_text_child(root, "Language", _language.get()); } - root->add_child("EditRate")->add_child_text(_edit_rate.as_string()); - root->add_child("TimeCodeRate")->add_child_text(raw_convert<string>(_time_code_rate)); + cxml::add_text_child(root, "EditRate", _edit_rate.as_string()); + cxml::add_text_child(root, "TimeCodeRate", raw_convert<string>(_time_code_rate)); if (_start_time) { - root->add_child("StartTime")->add_child_text(_start_time.get().as_string(Standard::SMPTE)); + cxml::add_text_child(root, "StartTime", _start_time.get().as_string(Standard::SMPTE)); } for (auto i: _load_font_nodes) { - auto load_font = root->add_child("LoadFont"); + auto load_font = cxml::add_child(root, "LoadFont"); load_font->add_child_text ("urn:uuid:" + i->urn); load_font->set_attribute ("ID", i->id); } - subtitles_as_xml (root->add_child("SubtitleList"), _time_code_rate, Standard::SMPTE); + subtitles_as_xml(cxml::add_child(root, "SubtitleList"), _time_code_rate, Standard::SMPTE); return format_xml(doc, std::make_pair(string{}, schema_namespace())); } diff --git a/src/sound_asset.cc b/src/sound_asset.cc index 0ceba53d..90075278 100644 --- a/src/sound_asset.cc +++ b/src/sound_asset.cc @@ -70,7 +70,8 @@ using namespace dcp; SoundAsset::SoundAsset (boost::filesystem::path file) : Asset (file) { - ASDCP::PCM::MXFReader reader; + Kumu::FileReaderFactory factory; + ASDCP::PCM::MXFReader reader(factory); auto r = reader.OpenRead(dcp::filesystem::fix_long_path(file).string().c_str()); if (ASDCP_FAILURE(r)) { boost::throw_exception (MXFFileError("could not open MXF file for reading", file.string(), r)); @@ -138,14 +139,15 @@ SoundAsset::equals(shared_ptr<const Asset> other, EqualityOptions const& opt, No return true; } - ASDCP::PCM::MXFReader reader_A; + Kumu::FileReaderFactory factory; + ASDCP::PCM::MXFReader reader_A(factory); DCP_ASSERT (file()); auto r = reader_A.OpenRead(dcp::filesystem::fix_long_path(*file()).string().c_str()); if (ASDCP_FAILURE(r)) { boost::throw_exception (MXFFileError("could not open MXF file for reading", file()->string(), r)); } - ASDCP::PCM::MXFReader reader_B; + ASDCP::PCM::MXFReader reader_B(factory); r = reader_B.OpenRead(dcp::filesystem::fix_long_path(*other->file()).string().c_str()); if (ASDCP_FAILURE (r)) { boost::throw_exception (MXFFileError("could not open MXF file for reading", other->file()->string(), r)); @@ -278,7 +280,8 @@ SoundAsset::static_pkl_type (Standard standard) bool SoundAsset::valid_mxf (boost::filesystem::path file) { - ASDCP::PCM::MXFReader reader; + Kumu::FileReaderFactory factory; + ASDCP::PCM::MXFReader reader(factory); Kumu::Result_t r = reader.OpenRead(dcp::filesystem::fix_long_path(file).string().c_str()); return !ASDCP_FAILURE (r); } diff --git a/src/stereo_picture_asset.cc b/src/stereo_j2k_picture_asset.cc index 2ce3cdc9..6a5e7d79 100644 --- a/src/stereo_picture_asset.cc +++ b/src/stereo_j2k_picture_asset.cc @@ -33,7 +33,7 @@ /** @file src/stereo_picture_asset.cc - * @brief StereoPictureAsset class + * @brief StereoJ2KPictureAsset class */ @@ -41,10 +41,10 @@ #include "equality_options.h" #include "exceptions.h" #include "filesystem.h" -#include "stereo_picture_asset.h" -#include "stereo_picture_asset_reader.h" -#include "stereo_picture_asset_writer.h" -#include "stereo_picture_frame.h" +#include "stereo_j2k_picture_asset.h" +#include "stereo_j2k_picture_asset_reader.h" +#include "stereo_j2k_picture_asset_writer.h" +#include "stereo_j2k_picture_frame.h" #include <asdcp/AS_DCP.h> @@ -56,10 +56,11 @@ using std::dynamic_pointer_cast; using namespace dcp; -StereoPictureAsset::StereoPictureAsset (boost::filesystem::path file) - : PictureAsset (file) +StereoJ2KPictureAsset::StereoJ2KPictureAsset (boost::filesystem::path file) + : J2KPictureAsset (file) { - ASDCP::JP2K::MXFSReader reader; + Kumu::FileReaderFactory factory; + ASDCP::JP2K::MXFSReader reader(factory); auto r = reader.OpenRead(dcp::filesystem::fix_long_path(file).string().c_str()); if (ASDCP_FAILURE(r)) { boost::throw_exception (MXFFileError("could not open MXF file for reading", file.string(), r)); @@ -81,38 +82,39 @@ StereoPictureAsset::StereoPictureAsset (boost::filesystem::path file) } -StereoPictureAsset::StereoPictureAsset (Fraction edit_rate, Standard standard) - : PictureAsset (edit_rate, standard) +StereoJ2KPictureAsset::StereoJ2KPictureAsset (Fraction edit_rate, Standard standard) + : J2KPictureAsset (edit_rate, standard) { } -shared_ptr<PictureAssetWriter> -StereoPictureAsset::start_write(boost::filesystem::path file, Behaviour behaviour) +shared_ptr<J2KPictureAssetWriter> +StereoJ2KPictureAsset::start_write(boost::filesystem::path file, Behaviour behaviour) { - return shared_ptr<StereoPictureAssetWriter>(new StereoPictureAssetWriter(this, file, behaviour == Behaviour::OVERWRITE_EXISTING)); + return shared_ptr<StereoJ2KPictureAssetWriter>(new StereoJ2KPictureAssetWriter(this, file, behaviour == Behaviour::OVERWRITE_EXISTING)); } -shared_ptr<StereoPictureAssetReader> -StereoPictureAsset::start_read () const +shared_ptr<StereoJ2KPictureAssetReader> +StereoJ2KPictureAsset::start_read () const { - return shared_ptr<StereoPictureAssetReader> (new StereoPictureAssetReader(this, key(), standard())); + return shared_ptr<StereoJ2KPictureAssetReader> (new StereoJ2KPictureAssetReader(this, key(), standard())); } bool -StereoPictureAsset::equals(shared_ptr<const Asset> other, EqualityOptions const& opt, NoteHandler note) const +StereoJ2KPictureAsset::equals(shared_ptr<const Asset> other, EqualityOptions const& opt, NoteHandler note) const { - ASDCP::JP2K::MXFSReader reader_A; + Kumu::FileReaderFactory factory; + ASDCP::JP2K::MXFSReader reader_A(factory); DCP_ASSERT (file()); auto r = reader_A.OpenRead(dcp::filesystem::fix_long_path(*file()).string().c_str()); if (ASDCP_FAILURE (r)) { boost::throw_exception (MXFFileError ("could not open MXF file for reading", file()->string(), r)); } - ASDCP::JP2K::MXFSReader reader_B; + ASDCP::JP2K::MXFSReader reader_B(factory); DCP_ASSERT (other->file()); r = reader_B.OpenRead(dcp::filesystem::fix_long_path(*other->file()).string().c_str()); if (ASDCP_FAILURE (r)) { @@ -132,7 +134,7 @@ StereoPictureAsset::equals(shared_ptr<const Asset> other, EqualityOptions const& return false; } - auto other_picture = dynamic_pointer_cast<const StereoPictureAsset> (other); + auto other_picture = dynamic_pointer_cast<const StereoJ2KPictureAsset> (other); DCP_ASSERT (other_picture); auto reader = start_read (); @@ -141,8 +143,8 @@ StereoPictureAsset::equals(shared_ptr<const Asset> other, EqualityOptions const& bool result = true; for (int i = 0; i < _intrinsic_duration; ++i) { - shared_ptr<const StereoPictureFrame> frame_A; - shared_ptr<const StereoPictureFrame> frame_B; + shared_ptr<const StereoJ2KPictureFrame> frame_A; + shared_ptr<const StereoJ2KPictureFrame> frame_B; try { frame_A = reader->get_frame (i); frame_B = other_reader->get_frame (i); diff --git a/src/stereo_picture_asset.h b/src/stereo_j2k_picture_asset.h index 6ee1d423..7ef86ec5 100644 --- a/src/stereo_picture_asset.h +++ b/src/stereo_j2k_picture_asset.h @@ -32,34 +32,34 @@ */ -/** @file src/stereo_picture_asset.h - * @brief StereoPictureAsset class +/** @file src/stereo_j2k_picture_asset.h + * @brief StereoJ2KPictureAsset class */ -#ifndef LIBDCP_STEREO_PICTURE_ASSET_H -#define LIBDCP_STEREO_PICTURE_ASSET_H +#ifndef LIBDCP_STEREO_J2K_PICTURE_ASSET_H +#define LIBDCP_STEREO_J2K_PICTURE_ASSET_H -#include "picture_asset.h" -#include "stereo_picture_asset_reader.h" +#include "j2k_picture_asset.h" +#include "stereo_j2k_picture_asset_reader.h" namespace dcp { -/** @class StereoPictureAsset +/** @class StereoJ2KPictureAsset * @brief A 3D (stereoscopic) picture asset */ -class StereoPictureAsset : public PictureAsset +class StereoJ2KPictureAsset : public J2KPictureAsset { public: - explicit StereoPictureAsset (boost::filesystem::path file); - explicit StereoPictureAsset (Fraction edit_rate, Standard standard); + explicit StereoJ2KPictureAsset (boost::filesystem::path file); + explicit StereoJ2KPictureAsset (Fraction edit_rate, Standard standard); - /** Start a progressive write to a StereoPictureAsset */ - std::shared_ptr<PictureAssetWriter> start_write(boost::filesystem::path file, Behaviour behaviour) override; - std::shared_ptr<StereoPictureAssetReader> start_read () const; + /** Start a progressive write to a StereoJ2KPictureAsset */ + std::shared_ptr<J2KPictureAssetWriter> start_write(boost::filesystem::path file, Behaviour behaviour) override; + std::shared_ptr<StereoJ2KPictureAssetReader> start_read () const; bool equals ( std::shared_ptr<const Asset> other, diff --git a/src/mono_picture_asset_reader.h b/src/stereo_j2k_picture_asset_reader.h index 5bead791..e4812a8e 100644 --- a/src/mono_picture_asset_reader.h +++ b/src/stereo_j2k_picture_asset_reader.h @@ -32,23 +32,23 @@ */ -/** @file src/mono_picture_asset_reader.h - * @brief MonoPictureAssetReader typedef +/** @file src/stereo_j2k_picture_asset_reader.h + * @brief StereoJ2KPictureAssetReader typedef */ -#ifndef LIBDCP_MONO_PICTURE_ASSET_READER_H -#define LIBDCP_MONO_PICTURE_ASSET_READER_H +#ifndef LIBDCP_STEREO_J2K_PICTURE_ASSET_READER_H +#define LIBDCP_STEREO_J2K_PICTURE_ASSET_READER_H #include "asset_reader.h" -#include "mono_picture_frame.h" +#include "stereo_j2k_picture_frame.h" namespace dcp { -typedef AssetReader<ASDCP::JP2K::MXFReader, MonoPictureFrame> MonoPictureAssetReader; +typedef AssetReader<ASDCP::JP2K::MXFSReader, StereoJ2KPictureFrame> StereoJ2KPictureAssetReader; } diff --git a/src/stereo_picture_asset_writer.cc b/src/stereo_j2k_picture_asset_writer.cc index 6ee271bc..e59de02f 100644 --- a/src/stereo_picture_asset_writer.cc +++ b/src/stereo_j2k_picture_asset_writer.cc @@ -33,20 +33,20 @@ /** @file src/stereo_picture_asset_writer.cc - * @brief StereoPictureAssetWriter class + * @brief StereoJ2KPictureAssetWriter class */ -#include "stereo_picture_asset_writer.h" +#include "stereo_j2k_picture_asset_writer.h" #include "exceptions.h" #include "dcp_assert.h" -#include "picture_asset.h" +#include "j2k_picture_asset.h" #include "crypto_context.h" #include <asdcp/AS_DCP.h> #include <asdcp/KM_fileio.h> -#include "picture_asset_writer_common.cc" +#include "j2k_picture_asset_writer_common.cc" using std::string; @@ -54,21 +54,21 @@ using std::shared_ptr; using namespace dcp; -struct StereoPictureAssetWriter::ASDCPState : public ASDCPStateBase +struct StereoJ2KPictureAssetWriter::ASDCPState : public ASDCPJ2KStateBase { ASDCP::JP2K::MXFSWriter mxf_writer; }; -StereoPictureAssetWriter::StereoPictureAssetWriter (PictureAsset* mxf, boost::filesystem::path file, bool overwrite) - : PictureAssetWriter (mxf, file, overwrite) - , _state (new StereoPictureAssetWriter::ASDCPState) +StereoJ2KPictureAssetWriter::StereoJ2KPictureAssetWriter (J2KPictureAsset* mxf, boost::filesystem::path file, bool overwrite) + : J2KPictureAssetWriter (mxf, file, overwrite) + , _state (new StereoJ2KPictureAssetWriter::ASDCPState) { } -StereoPictureAssetWriter::~StereoPictureAssetWriter() +StereoJ2KPictureAssetWriter::~StereoJ2KPictureAssetWriter() { try { /* Last-resort finalization to close the file, at least */ @@ -80,15 +80,15 @@ StereoPictureAssetWriter::~StereoPictureAssetWriter() void -StereoPictureAssetWriter::start (uint8_t const * data, int size) +StereoJ2KPictureAssetWriter::start (uint8_t const * data, int size) { dcp::start (this, _state, _picture_asset, data, size); _picture_asset->set_frame_rate (Fraction (_picture_asset->edit_rate().numerator * 2, _picture_asset->edit_rate().denominator)); } -FrameInfo -StereoPictureAssetWriter::write (uint8_t const * data, int size) +J2KFrameInfo +StereoJ2KPictureAssetWriter::write (uint8_t const * data, int size) { DCP_ASSERT (!_finalized); @@ -123,17 +123,17 @@ StereoPictureAssetWriter::write (uint8_t const * data, int size) ++_frames_written; } - return FrameInfo (before_offset, _state->mxf_writer.Tell() - before_offset, hash); + return J2KFrameInfo(before_offset, _state->mxf_writer.Tell() - before_offset, hash); } void -StereoPictureAssetWriter::fake_write (int size) +StereoJ2KPictureAssetWriter::fake_write(J2KFrameInfo const& info) { DCP_ASSERT (_started); DCP_ASSERT (!_finalized); - auto r = _state->mxf_writer.FakeWriteFrame (size, _next_eye == Eye::LEFT ? ASDCP::JP2K::SP_LEFT : ASDCP::JP2K::SP_RIGHT); + auto r = _state->mxf_writer.FakeWriteFrame(info.size, _next_eye == Eye::LEFT ? ASDCP::JP2K::SP_LEFT : ASDCP::JP2K::SP_RIGHT); if (ASDCP_FAILURE(r)) { boost::throw_exception (MXFFileError("error in writing video MXF", _file.string(), r)); } @@ -146,7 +146,7 @@ StereoPictureAssetWriter::fake_write (int size) bool -StereoPictureAssetWriter::finalize () +StereoJ2KPictureAssetWriter::finalize () { if (_started) { auto r = _state->mxf_writer.Finalize(); @@ -156,5 +156,5 @@ StereoPictureAssetWriter::finalize () } _picture_asset->_intrinsic_duration = _frames_written; - return PictureAssetWriter::finalize (); + return J2KPictureAssetWriter::finalize (); } diff --git a/src/stereo_picture_asset_writer.h b/src/stereo_j2k_picture_asset_writer.h index 1cee1202..e3f39a0b 100644 --- a/src/stereo_picture_asset_writer.h +++ b/src/stereo_j2k_picture_asset_writer.h @@ -32,12 +32,12 @@ */ -/** @file src/stereo_picture_asset_writer.h - * @brief StereoPictureAssetWriter class +/** @file src/stereo_j2k_picture_asset_writer.h + * @brief StereoJ2KPictureAssetWriter class */ -#include "picture_asset_writer.h" +#include "j2k_picture_asset_writer.h" #include <memory> #include <stdint.h> #include <string> @@ -46,33 +46,33 @@ namespace dcp { -/** @class StereoPictureAssetWriter - * @brief A helper class for writing to StereoPictureAssets. +/** @class StereoJ2KPictureAssetWriter + * @brief A helper class for writing to StereoJ2KPictureAssets. * - * Objects of this class can only be created with StereoPictureAsset::start_write(). + * Objects of this class can only be created with StereoJ2KPictureAsset::start_write(). * - * Frames can be written to the StereoPictureAsset by calling write() with a JPEG2000 image + * Frames can be written to the StereoJ2KPictureAsset by calling write() with a JPEG2000 image * (a verbatim .j2c file). finalize() should be called after the last frame has been written, * but if it is not, it will be called by the destructor (though in that case any error * during finalization will be ignored). */ -class StereoPictureAssetWriter : public PictureAssetWriter +class StereoJ2KPictureAssetWriter : public J2KPictureAssetWriter { public: - ~StereoPictureAssetWriter(); + ~StereoJ2KPictureAssetWriter(); /** Write a frame for one eye. Frames must be written left, then right, then left etc. * @param data JPEG2000 data. * @param size Size of data. */ - FrameInfo write (uint8_t const * data, int size) override; - void fake_write (int size) override; + J2KFrameInfo write(uint8_t const * data, int size) override; + void fake_write(J2KFrameInfo const& info) override; bool finalize () override; private: - friend class StereoPictureAsset; + friend class StereoJ2KPictureAsset; - StereoPictureAssetWriter (PictureAsset *, boost::filesystem::path file, bool); + StereoJ2KPictureAssetWriter (J2KPictureAsset *, boost::filesystem::path file, bool); void start (uint8_t const *, int); /* do this with an opaque pointer so we don't have to include diff --git a/src/stereo_picture_frame.cc b/src/stereo_j2k_picture_frame.cc index 8d3a2757..9ef91c5c 100644 --- a/src/stereo_picture_frame.cc +++ b/src/stereo_j2k_picture_frame.cc @@ -33,7 +33,7 @@ /** @file src/stereo_picture_frame.cc - * @brief StereoPictureFrame class + * @brief StereoJ2KPictureFrame class */ @@ -43,7 +43,7 @@ #include "exceptions.h" #include "j2k_transcode.h" #include "rgb_xyz.h" -#include "stereo_picture_frame.h" +#include "stereo_j2k_picture_frame.h" #include "util.h" #include <asdcp/AS_DCP.h> #include <asdcp/KM_fileio.h> @@ -55,7 +55,7 @@ using std::make_shared; using namespace dcp; -StereoPictureFrame::Part::Part (shared_ptr<ASDCP::JP2K::SFrameBuffer> buffer, Eye eye) +StereoJ2KPictureFrame::Part::Part (shared_ptr<ASDCP::JP2K::SFrameBuffer> buffer, Eye eye) : _buffer (buffer) , _eye (eye) { @@ -64,28 +64,28 @@ StereoPictureFrame::Part::Part (shared_ptr<ASDCP::JP2K::SFrameBuffer> buffer, Ey ASDCP::JP2K::FrameBuffer & -StereoPictureFrame::Part::mono () const +StereoJ2KPictureFrame::Part::mono () const { return _eye == Eye::LEFT ? _buffer->Left : _buffer->Right; } uint8_t const * -StereoPictureFrame::Part::data () const +StereoJ2KPictureFrame::Part::data () const { return mono().RoData(); } uint8_t * -StereoPictureFrame::Part::data () +StereoJ2KPictureFrame::Part::data () { return mono().Data(); } int -StereoPictureFrame::Part::size () const +StereoJ2KPictureFrame::Part::size () const { return mono().Size(); } @@ -96,7 +96,7 @@ StereoPictureFrame::Part::size () const * @param n Frame within the asset, not taking EntryPoint into account. * @param check_hmac true to check the HMAC and give an error if it is not as expected. */ -StereoPictureFrame::StereoPictureFrame (ASDCP::JP2K::MXFSReader* reader, int n, shared_ptr<DecryptionContext> c, bool check_hmac) +StereoJ2KPictureFrame::StereoJ2KPictureFrame (ASDCP::JP2K::MXFSReader* reader, int n, shared_ptr<DecryptionContext> c, bool check_hmac) { /* XXX: unfortunate guesswork on this buffer size */ _buffer = make_shared<ASDCP::JP2K::SFrameBuffer>(4 * Kumu::Megabyte); @@ -107,7 +107,7 @@ StereoPictureFrame::StereoPictureFrame (ASDCP::JP2K::MXFSReader* reader, int n, } -StereoPictureFrame::StereoPictureFrame () +StereoJ2KPictureFrame::StereoJ2KPictureFrame () { _buffer = make_shared<ASDCP::JP2K::SFrameBuffer>(4 * Kumu::Megabyte); } @@ -119,7 +119,7 @@ StereoPictureFrame::StereoPictureFrame () * reduction). */ shared_ptr<OpenJPEGImage> -StereoPictureFrame::xyz_image (Eye eye, int reduce) const +StereoJ2KPictureFrame::xyz_image (Eye eye, int reduce) const { switch (eye) { case Eye::LEFT: @@ -132,15 +132,15 @@ StereoPictureFrame::xyz_image (Eye eye, int reduce) const } -shared_ptr<StereoPictureFrame::Part> -StereoPictureFrame::right () const +shared_ptr<StereoJ2KPictureFrame::Part> +StereoJ2KPictureFrame::right () const { return make_shared<Part>(_buffer, Eye::RIGHT); } -shared_ptr<StereoPictureFrame::Part> -StereoPictureFrame::left () const +shared_ptr<StereoJ2KPictureFrame::Part> +StereoJ2KPictureFrame::left () const { return make_shared<Part>(_buffer, Eye::LEFT); } diff --git a/src/stereo_picture_frame.h b/src/stereo_j2k_picture_frame.h index b0c0f0c8..193960f3 100644 --- a/src/stereo_picture_frame.h +++ b/src/stereo_j2k_picture_frame.h @@ -32,13 +32,13 @@ */ -/** @file src/stereo_picture_frame.h - * @brief StereoPictureFrame class +/** @file src/stereo_j2k_picture_frame.h + * @brief StereoJ2KPictureFrame class */ -#ifndef LIBDCP_STEREO_PICTURE_FRAME_H -#define LIBDCP_STEREO_PICTURE_FRAME_H +#ifndef LIBDCP_STEREO_J2K_PICTURE_FRAME_H +#define LIBDCP_STEREO_J2K_PICTURE_FRAME_H #include "asset_reader.h" @@ -61,19 +61,19 @@ namespace dcp { class OpenJPEGImage; -class StereoPictureFrame; +class StereoJ2KPictureFrame; -/** @class StereoPictureFrame +/** @class StereoJ2KPictureFrame * @brief A single frame of a 3D (stereoscopic) picture asset */ -class StereoPictureFrame +class StereoJ2KPictureFrame { public: - StereoPictureFrame (); + StereoJ2KPictureFrame (); - StereoPictureFrame (StereoPictureFrame const &) = delete; - StereoPictureFrame& operator= (StereoPictureFrame const &) = delete; + StereoJ2KPictureFrame (StereoJ2KPictureFrame const &) = delete; + StereoJ2KPictureFrame& operator= (StereoJ2KPictureFrame const &) = delete; std::shared_ptr<OpenJPEGImage> xyz_image (Eye eye, int reduce = 0) const; @@ -87,7 +87,7 @@ public: int size () const override; private: - friend class StereoPictureFrame; + friend class StereoJ2KPictureFrame; ASDCP::JP2K::FrameBuffer& mono () const; @@ -99,12 +99,12 @@ public: std::shared_ptr<Part> right () const; private: - /* XXX: this is a bit of a shame, but I tried friend StereoPictureAssetReader and it's + /* XXX: this is a bit of a shame, but I tried friend StereoJ2KPictureAssetReader and it's rejected by some (seemingly older) GCCs. */ - friend class AssetReader<ASDCP::JP2K::MXFSReader, StereoPictureFrame>; + friend class AssetReader<ASDCP::JP2K::MXFSReader, StereoJ2KPictureFrame>; - StereoPictureFrame (ASDCP::JP2K::MXFSReader* reader, int n, std::shared_ptr<DecryptionContext>, bool check_hmac); + StereoJ2KPictureFrame (ASDCP::JP2K::MXFSReader* reader, int n, std::shared_ptr<DecryptionContext>, bool check_hmac); std::shared_ptr<ASDCP::JP2K::SFrameBuffer> _buffer; }; diff --git a/src/subtitle_asset_internal.cc b/src/subtitle_asset_internal.cc index 99d8411b..39f68624 100644 --- a/src/subtitle_asset_internal.cc +++ b/src/subtitle_asset_internal.cc @@ -77,7 +77,7 @@ order::Font::Font (shared_ptr<SubtitleString> s, Standard standard) xmlpp::Element* order::Font::as_xml (xmlpp::Element* parent, Context&) const { - auto e = parent->add_child("Font"); + auto e = cxml::add_child(parent, "Font"); for (const auto& i: _values) { e->set_attribute (i.first, i.second); } @@ -137,7 +137,7 @@ xmlpp::Element* order::String::as_xml (xmlpp::Element* parent, Context& context) const { if (fabs(_space_before) > SPACE_BEFORE_EPSILON) { - auto space = parent->add_child("Space"); + auto space = cxml::add_child(parent, "Space"); auto size = raw_convert<string>(_space_before, 2); if (context.standard == Standard::INTEROP) { size += "em"; @@ -212,7 +212,7 @@ position_align (xmlpp::Element* e, order::Context& context, HAlign h_align, floa xmlpp::Element* order::Text::as_xml (xmlpp::Element* parent, Context& context) const { - auto e = parent->add_child ("Text"); + auto e = cxml::add_child(parent, "Text"); position_align(e, context, _h_align, _h_position, _v_align, _v_position, _z_position); @@ -224,9 +224,9 @@ order::Text::as_xml (xmlpp::Element* parent, Context& context) const } for (auto const& ruby: _rubies) { - auto xml = e->add_child("Ruby"); - xml->add_child("Rb")->add_child_text(ruby.base); - auto rt = xml->add_child("Rt"); + auto xml = cxml::add_child(e, "Ruby"); + cxml::add_child(xml, "Rb")->add_child_text(ruby.base); + auto rt = cxml::add_child(xml, "Rt"); rt->add_child_text(ruby.annotation); rt->set_attribute("Size", dcp::raw_convert<string>(ruby.size, 6)); rt->set_attribute("Position", ruby.position == RubyPosition::BEFORE ? "before" : "after"); @@ -242,7 +242,7 @@ order::Text::as_xml (xmlpp::Element* parent, Context& context) const xmlpp::Element* order::Subtitle::as_xml (xmlpp::Element* parent, Context& context) const { - auto e = parent->add_child ("Subtitle"); + auto e = cxml::add_child(parent, "Subtitle"); e->set_attribute ("SpotNumber", raw_convert<string> (context.spot_number++)); e->set_attribute ("TimeIn", _in.rebase(context.time_code_rate).as_string(context.standard)); e->set_attribute ("TimeOut", _out.rebase(context.time_code_rate).as_string(context.standard)); @@ -274,7 +274,7 @@ order::Font::clear () xmlpp::Element * order::Image::as_xml (xmlpp::Element* parent, Context& context) const { - auto e = parent->add_child ("Image"); + auto e = cxml::add_child(parent, "Image"); position_align(e, context, _h_align, _h_position, _v_align, _v_position, _z_position); if (context.standard == Standard::SMPTE) { diff --git a/src/types.cc b/src/types.cc index e2165548..d58256a5 100644 --- a/src/types.cc +++ b/src/types.cc @@ -310,9 +310,9 @@ ContentVersion::ContentVersion (string label_text_) void ContentVersion::as_xml (xmlpp::Element* parent) const { - auto cv = parent->add_child("ContentVersion"); - cv->add_child("Id")->add_child_text(id); - cv->add_child("LabelText")->add_child_text(label_text); + auto cv = cxml::add_child(parent, "ContentVersion"); + cxml::add_text_child(cv, "Id", id); + cxml::add_text_child(cv, "LabelText", label_text); } @@ -345,7 +345,7 @@ Luminance::set_value (float v) void Luminance::as_xml (xmlpp::Element* parent, string ns) const { - auto lum = parent->add_child("Luminance", ns); + auto lum = cxml::add_child(parent, "Luminance", ns); lum->set_attribute("units", unit_to_string(_unit)); lum->add_child_text(raw_convert<string>(_value, 3)); } diff --git a/src/verify.cc b/src/verify.cc index 112a5bb5..56e6b5b8 100644 --- a/src/verify.cc +++ b/src/verify.cc @@ -43,8 +43,8 @@ #include "exceptions.h" #include "filesystem.h" #include "interop_subtitle_asset.h" -#include "mono_picture_asset.h" -#include "mono_picture_frame.h" +#include "mono_j2k_picture_asset.h" +#include "mono_j2k_picture_frame.h" #include "raw_convert.h" #include "reel.h" #include "reel_closed_caption_asset.h" @@ -55,8 +55,8 @@ #include "reel_smpte_subtitle_asset.h" #include "reel_subtitle_asset.h" #include "smpte_subtitle_asset.h" -#include "stereo_picture_asset.h" -#include "stereo_picture_frame.h" +#include "stereo_j2k_picture_asset.h" +#include "stereo_j2k_picture_frame.h" #include "verify.h" #include "verify_j2k.h" #include <libxml/parserInternals.h> @@ -88,6 +88,7 @@ using std::cout; using std::dynamic_pointer_cast; +using std::function; using std::list; using std::make_shared; using std::map; @@ -97,7 +98,6 @@ using std::shared_ptr; using std::string; using std::vector; using boost::optional; -using boost::function; using namespace dcp; @@ -293,9 +293,83 @@ parse (XercesDOMParser& parser, string xml) } +class Context +{ +public: + Context( + std::vector<VerificationNote>& notes_, + boost::filesystem::path xsd_dtd_directory_, + function<void (string, optional<boost::filesystem::path>)> stage_, + function<void (float)> progress_, + VerificationOptions options_ + ) + : notes(notes_) + , xsd_dtd_directory(xsd_dtd_directory_) + , stage(stage_) + , progress(progress_) + , options(options_) + { + + } + + Context(Context const&) = delete; + Context& operator=(Context const&) = delete; + + template<typename... Args> + void ok(dcp::VerificationNote::Code code, Args... args) + { + add_note({dcp::VerificationNote::Type::OK, code, std::forward<Args>(args)...}); + } + + template<typename... Args> + void warning(dcp::VerificationNote::Code code, Args... args) + { + add_note({dcp::VerificationNote::Type::WARNING, code, std::forward<Args>(args)...}); + } + + template<typename... Args> + void bv21_error(dcp::VerificationNote::Code code, Args... args) + { + add_note({dcp::VerificationNote::Type::BV21_ERROR, code, std::forward<Args>(args)...}); + } + + template<typename... Args> + void error(dcp::VerificationNote::Code code, Args... args) + { + add_note({dcp::VerificationNote::Type::ERROR, code, std::forward<Args>(args)...}); + } + + void add_note(dcp::VerificationNote note) + { + if (cpl) { + note.set_cpl_id(cpl->id()); + } + notes.push_back(std::move(note)); + } + + void add_note_if_not_existing(dcp::VerificationNote note) + { + if (find(notes.begin(), notes.end(), note) == notes.end()) { + add_note(note); + } + } + + std::vector<VerificationNote>& notes; + std::shared_ptr<const DCP> dcp; + std::shared_ptr<const CPL> cpl; + boost::filesystem::path xsd_dtd_directory; + function<void (string, optional<boost::filesystem::path>)> stage; + function<void (float)> progress; + VerificationOptions options; + + boost::optional<string> subtitle_language; + boost::optional<int> audio_channels; +}; + + template <class T> void -validate_xml (T xml, boost::filesystem::path xsd_dtd_directory, vector<VerificationNote>& notes) +validate_xml(Context& context, T xml) { try { XMLPlatformUtils::Initialize (); @@ -348,7 +422,7 @@ validate_xml (T xml, boost::filesystem::path xsd_dtd_directory, vector<Verificat parser.setValidationSchemaFullChecking(true); parser.setErrorHandler(&error_handler); - LocalFileResolver resolver (xsd_dtd_directory); + LocalFileResolver resolver(context.xsd_dtd_directory); parser.setEntityResolver(&resolver); try { @@ -366,13 +440,12 @@ validate_xml (T xml, boost::filesystem::path xsd_dtd_directory, vector<Verificat XMLPlatformUtils::Terminate (); for (auto i: error_handler.errors()) { - notes.push_back ({ - VerificationNote::Type::ERROR, + context.error( VerificationNote::Code::INVALID_XML, i.message(), boost::trim_copy(i.public_id() + " " + i.system_id()), i.line() - }); + ); } } @@ -386,9 +459,8 @@ enum class VerifyAssetResult { static VerifyAssetResult verify_asset( - shared_ptr<const DCP> dcp, + Context& context, shared_ptr<const ReelFileAsset> reel_file_asset, - function<void (float)> progress, string* reference_hash, string* calculated_hash ) @@ -402,11 +474,11 @@ verify_asset( * call to hash(). */ reel_file_asset->asset_ref()->unset_hash(); - *calculated_hash = reel_file_asset->asset_ref()->hash([progress](int64_t done, int64_t total) { - progress(float(done) / total); + *calculated_hash = reel_file_asset->asset_ref()->hash([&context](int64_t done, int64_t total) { + context.progress(float(done) / total); }); - auto pkls = dcp->pkls(); + auto pkls = context.dcp->pkls(); /* We've read this DCP in so it must have at least one PKL */ DCP_ASSERT (!pkls.empty()); @@ -436,103 +508,106 @@ verify_asset( } -void -verify_language_tag (string tag, vector<VerificationNote>& notes) +static void +verify_language_tag(Context& context, string tag) { try { LanguageTag test (tag); } catch (LanguageTagError &) { - notes.push_back ({VerificationNote::Type::BV21_ERROR, VerificationNote::Code::INVALID_LANGUAGE, tag}); + context.bv21_error(VerificationNote::Code::INVALID_LANGUAGE, tag); } } static void -verify_picture_asset(shared_ptr<const ReelFileAsset> reel_file_asset, boost::filesystem::path file, int64_t start_frame, vector<VerificationNote>& notes, function<void (float)> progress) +verify_picture_asset( + Context& context, + shared_ptr<const ReelFileAsset> reel_file_asset, + boost::filesystem::path file, + int64_t start_frame + ) { - auto asset = dynamic_pointer_cast<PictureAsset>(reel_file_asset->asset_ref().asset()); + auto asset = dynamic_pointer_cast<J2KPictureAsset>(reel_file_asset->asset_ref().asset()); auto const duration = asset->intrinsic_duration (); - auto check_and_add = [¬es](vector<VerificationNote> const& j2k_notes) { + auto check_and_add = [&context](vector<VerificationNote> const& j2k_notes) { for (auto i: j2k_notes) { - if (find(notes.begin(), notes.end(), i) == notes.end()) { - notes.push_back (i); - } + context.add_note_if_not_existing(i); } }; int const max_frame = rint(250 * 1000000 / (8 * asset->edit_rate().as_float())); int const risky_frame = rint(230 * 1000000 / (8 * asset->edit_rate().as_float())); - auto check_frame_size = [max_frame, risky_frame, file, start_frame](int index, int size, int frame_rate, vector<VerificationNote>& notes) { + bool any_bad_frames_seen = false; + + auto check_frame_size = [max_frame, risky_frame, file, start_frame, &any_bad_frames_seen](Context& context, int index, int size, int frame_rate) { if (size > max_frame) { - notes.push_back( + context.add_note( VerificationNote( VerificationNote::Type::ERROR, VerificationNote::Code::INVALID_PICTURE_FRAME_SIZE_IN_BYTES, file ).set_frame(start_frame + index).set_frame_rate(frame_rate) ); + any_bad_frames_seen = true; } else if (size > risky_frame) { - notes.push_back( + context.add_note( VerificationNote( VerificationNote::Type::WARNING, VerificationNote::Code::NEARLY_INVALID_PICTURE_FRAME_SIZE_IN_BYTES, file ).set_frame(start_frame + index).set_frame_rate(frame_rate) ); + any_bad_frames_seen = true; } }; - if (auto mono_asset = dynamic_pointer_cast<MonoPictureAsset>(reel_file_asset->asset_ref().asset())) { + if (auto mono_asset = dynamic_pointer_cast<MonoJ2KPictureAsset>(reel_file_asset->asset_ref().asset())) { auto reader = mono_asset->start_read (); for (int64_t i = 0; i < duration; ++i) { auto frame = reader->get_frame (i); - check_frame_size(i, frame->size(), mono_asset->frame_rate().numerator, notes); + check_frame_size(context, i, frame->size(), mono_asset->frame_rate().numerator); if (!mono_asset->encrypted() || mono_asset->key()) { vector<VerificationNote> j2k_notes; verify_j2k(frame, start_frame, i, mono_asset->frame_rate().numerator, j2k_notes); check_and_add (j2k_notes); } - progress (float(i) / duration); + context.progress(float(i) / duration); } - } else if (auto stereo_asset = dynamic_pointer_cast<StereoPictureAsset>(asset)) { + } else if (auto stereo_asset = dynamic_pointer_cast<StereoJ2KPictureAsset>(asset)) { auto reader = stereo_asset->start_read (); for (int64_t i = 0; i < duration; ++i) { auto frame = reader->get_frame (i); - check_frame_size(i, frame->left()->size(), stereo_asset->frame_rate().numerator, notes); - check_frame_size(i, frame->right()->size(), stereo_asset->frame_rate().numerator, notes); + check_frame_size(context, i, frame->left()->size(), stereo_asset->frame_rate().numerator); + check_frame_size(context, i, frame->right()->size(), stereo_asset->frame_rate().numerator); if (!stereo_asset->encrypted() || stereo_asset->key()) { vector<VerificationNote> j2k_notes; verify_j2k(frame->left(), start_frame, i, stereo_asset->frame_rate().numerator, j2k_notes); verify_j2k(frame->right(), start_frame, i, stereo_asset->frame_rate().numerator, j2k_notes); check_and_add (j2k_notes); } - progress (float(i) / duration); + context.progress(float(i) / duration); } } + + if (!any_bad_frames_seen) { + context.ok(VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, file); + } } static void -verify_main_picture_asset ( - shared_ptr<const DCP> dcp, - shared_ptr<const ReelPictureAsset> reel_asset, - int64_t start_frame, - function<void (string, optional<boost::filesystem::path>)> stage, - function<void (float)> progress, - VerificationOptions options, - vector<VerificationNote>& notes - ) +verify_main_picture_asset(Context& context, shared_ptr<const ReelPictureAsset> reel_asset, int64_t start_frame) { auto asset = reel_asset->asset(); auto const file = *asset->file(); - if (options.check_asset_hashes && (!options.maximum_asset_size_for_hash_check || filesystem::file_size(file) < *options.maximum_asset_size_for_hash_check)) { - stage ("Checking picture asset hash", file); + if (context.options.check_asset_hashes && (!context.options.maximum_asset_size_for_hash_check || filesystem::file_size(file) < *context.options.maximum_asset_size_for_hash_check)) { + context.stage("Checking picture asset hash", file); string reference_hash; string calculated_hash; - auto const r = verify_asset(dcp, reel_asset, progress, &reference_hash, &calculated_hash); + auto const r = verify_asset(context, reel_asset, &reference_hash, &calculated_hash); switch (r) { case VerifyAssetResult::BAD: - notes.push_back( + context.add_note( dcp::VerificationNote( VerificationNote::Type::ERROR, VerificationNote::Code::INCORRECT_PICTURE_HASH, @@ -541,17 +616,16 @@ verify_main_picture_asset ( ); break; case VerifyAssetResult::CPL_PKL_DIFFER: - notes.push_back ({ - VerificationNote::Type::ERROR, VerificationNote::Code::MISMATCHED_PICTURE_HASHES, file - }); + context.error(VerificationNote::Code::MISMATCHED_PICTURE_HASHES, file); break; default: + context.ok(VerificationNote::Code::CORRECT_PICTURE_HASH, file); break; } } - stage ("Checking picture frame sizes", asset->file()); - verify_picture_asset(reel_asset, file, start_frame, notes, progress); + context.stage("Checking picture frame sizes", asset->file()); + verify_picture_asset(context, reel_asset, file, start_frame); /* Only flat/scope allowed by Bv2.1 */ if ( @@ -559,12 +633,7 @@ verify_main_picture_asset ( asset->size() != Size(1998, 1080) && asset->size() != Size(4096, 1716) && asset->size() != Size(3996, 2160)) { - notes.push_back({ - VerificationNote::Type::BV21_ERROR, - VerificationNote::Code::INVALID_PICTURE_SIZE_IN_PIXELS, - String::compose("%1x%2", asset->size().width, asset->size().height), - file - }); + context.bv21_error(VerificationNote::Code::INVALID_PICTURE_SIZE_IN_PIXELS, String::compose("%1x%2", asset->size().width, asset->size().height), file); } /* Only 24, 25, 48fps allowed for 2K */ @@ -572,69 +641,50 @@ verify_main_picture_asset ( (asset->size() == Size(2048, 858) || asset->size() == Size(1998, 1080)) && (asset->edit_rate() != Fraction(24, 1) && asset->edit_rate() != Fraction(25, 1) && asset->edit_rate() != Fraction(48, 1)) ) { - notes.push_back({ - VerificationNote::Type::BV21_ERROR, + context.bv21_error( VerificationNote::Code::INVALID_PICTURE_FRAME_RATE_FOR_2K, String::compose("%1/%2", asset->edit_rate().numerator, asset->edit_rate().denominator), file - }); + ); } if (asset->size() == Size(4096, 1716) || asset->size() == Size(3996, 2160)) { /* Only 24fps allowed for 4K */ if (asset->edit_rate() != Fraction(24, 1)) { - notes.push_back({ - VerificationNote::Type::BV21_ERROR, + context.bv21_error( VerificationNote::Code::INVALID_PICTURE_FRAME_RATE_FOR_4K, String::compose("%1/%2", asset->edit_rate().numerator, asset->edit_rate().denominator), file - }); + ); } /* Only 2D allowed for 4K */ - if (dynamic_pointer_cast<const StereoPictureAsset>(asset)) { - notes.push_back({ - VerificationNote::Type::BV21_ERROR, + if (dynamic_pointer_cast<const StereoJ2KPictureAsset>(asset)) { + context.bv21_error( VerificationNote::Code::INVALID_PICTURE_ASSET_RESOLUTION_FOR_3D, String::compose("%1/%2", asset->edit_rate().numerator, asset->edit_rate().denominator), file - }); + ); } } - } -struct State -{ - boost::optional<string> subtitle_language; - boost::optional<int> audio_channels; -}; - - static void -verify_main_sound_asset ( - shared_ptr<const DCP> dcp, - shared_ptr<const ReelSoundAsset> reel_asset, - function<void (string, optional<boost::filesystem::path>)> stage, - function<void (float)> progress, - VerificationOptions options, - vector<VerificationNote>& notes, - State& state - ) +verify_main_sound_asset(Context& context, shared_ptr<const ReelSoundAsset> reel_asset) { auto asset = reel_asset->asset(); auto const file = *asset->file(); - if (options.check_asset_hashes && (!options.maximum_asset_size_for_hash_check || filesystem::file_size(file) < *options.maximum_asset_size_for_hash_check)) { - stage("Checking sound asset hash", file); + if (context.options.check_asset_hashes && (!context.options.maximum_asset_size_for_hash_check || filesystem::file_size(file) < *context.options.maximum_asset_size_for_hash_check)) { + context.stage("Checking sound asset hash", file); string reference_hash; string calculated_hash; - auto const r = verify_asset(dcp, reel_asset, progress, &reference_hash, &calculated_hash); + auto const r = verify_asset(context, reel_asset, &reference_hash, &calculated_hash); switch (r) { case VerifyAssetResult::BAD: - notes.push_back( + context.add_note( dcp::VerificationNote( VerificationNote::Type::ERROR, VerificationNote::Code::INCORRECT_SOUND_HASH, @@ -643,58 +693,58 @@ verify_main_sound_asset ( ); break; case VerifyAssetResult::CPL_PKL_DIFFER: - notes.push_back({VerificationNote::Type::ERROR, VerificationNote::Code::MISMATCHED_SOUND_HASHES, file}); + context.error(VerificationNote::Code::MISMATCHED_SOUND_HASHES, file); break; default: break; } } - if (!state.audio_channels) { - state.audio_channels = asset->channels(); - } else if (*state.audio_channels != asset->channels()) { - notes.push_back({ VerificationNote::Type::ERROR, VerificationNote::Code::MISMATCHED_SOUND_CHANNEL_COUNTS, file }); + if (!context.audio_channels) { + context.audio_channels = asset->channels(); + } else if (*context.audio_channels != asset->channels()) { + context.error(VerificationNote::Code::MISMATCHED_SOUND_CHANNEL_COUNTS, file); } - stage ("Checking sound asset metadata", file); + context.stage("Checking sound asset metadata", file); if (auto lang = asset->language()) { - verify_language_tag (*lang, notes); + verify_language_tag(context, *lang); } if (asset->sampling_rate() != 48000) { - notes.push_back({VerificationNote::Type::BV21_ERROR, VerificationNote::Code::INVALID_SOUND_FRAME_RATE, raw_convert<string>(asset->sampling_rate()), file}); + context.bv21_error(VerificationNote::Code::INVALID_SOUND_FRAME_RATE, raw_convert<string>(asset->sampling_rate()), file); } } static void -verify_main_subtitle_reel (shared_ptr<const ReelSubtitleAsset> reel_asset, vector<VerificationNote>& notes) +verify_main_subtitle_reel(Context& context, shared_ptr<const ReelSubtitleAsset> reel_asset) { /* XXX: is Language compulsory? */ if (reel_asset->language()) { - verify_language_tag (*reel_asset->language(), notes); + verify_language_tag(context, *reel_asset->language()); } if (!reel_asset->entry_point()) { - notes.push_back ({VerificationNote::Type::BV21_ERROR, VerificationNote::Code::MISSING_SUBTITLE_ENTRY_POINT, reel_asset->id() }); + context.bv21_error(VerificationNote::Code::MISSING_SUBTITLE_ENTRY_POINT, reel_asset->id()); } else if (reel_asset->entry_point().get()) { - notes.push_back ({VerificationNote::Type::BV21_ERROR, VerificationNote::Code::INCORRECT_SUBTITLE_ENTRY_POINT, reel_asset->id() }); + context.bv21_error(VerificationNote::Code::INCORRECT_SUBTITLE_ENTRY_POINT, reel_asset->id()); } } static void -verify_closed_caption_reel (shared_ptr<const ReelClosedCaptionAsset> reel_asset, vector<VerificationNote>& notes) +verify_closed_caption_reel(Context& context, shared_ptr<const ReelClosedCaptionAsset> reel_asset) { /* XXX: is Language compulsory? */ if (reel_asset->language()) { - verify_language_tag (*reel_asset->language(), notes); + verify_language_tag(context, *reel_asset->language()); } if (!reel_asset->entry_point()) { - notes.push_back ({VerificationNote::Type::BV21_ERROR, VerificationNote::Code::MISSING_CLOSED_CAPTION_ENTRY_POINT, reel_asset->id() }); + context.bv21_error(VerificationNote::Code::MISSING_CLOSED_CAPTION_ENTRY_POINT, reel_asset->id()); } else if (reel_asset->entry_point().get()) { - notes.push_back ({VerificationNote::Type::BV21_ERROR, VerificationNote::Code::INCORRECT_CLOSED_CAPTION_ENTRY_POINT, reel_asset->id() }); + context.bv21_error(VerificationNote::Code::INCORRECT_CLOSED_CAPTION_ENTRY_POINT, reel_asset->id()); } } @@ -702,22 +752,20 @@ verify_closed_caption_reel (shared_ptr<const ReelClosedCaptionAsset> reel_asset, /** Verify stuff that is common to both subtitles and closed captions */ void verify_smpte_timed_text_asset ( + Context& context, shared_ptr<const SMPTESubtitleAsset> asset, - optional<int64_t> reel_asset_duration, - vector<VerificationNote>& notes + optional<int64_t> reel_asset_duration ) { if (asset->language()) { - verify_language_tag (*asset->language(), notes); + verify_language_tag(context, *asset->language()); } else { - notes.push_back ({ VerificationNote::Type::BV21_ERROR, VerificationNote::Code::MISSING_SUBTITLE_LANGUAGE, *asset->file() }); + context.bv21_error(VerificationNote::Code::MISSING_SUBTITLE_LANGUAGE, *asset->file()); } auto const size = filesystem::file_size(asset->file().get()); if (size > 115 * 1024 * 1024) { - notes.push_back ( - { VerificationNote::Type::BV21_ERROR, VerificationNote::Code::INVALID_TIMED_TEXT_SIZE_IN_BYTES, raw_convert<string>(size), *asset->file() } - ); + context.bv21_error(VerificationNote::Code::INVALID_TIMED_TEXT_SIZE_IN_BYTES, raw_convert<string>(size), *asset->file()); } /* XXX: I'm not sure what Bv2.1_7.2.1 means when it says "the font resource shall not be larger than 10MB" @@ -729,54 +777,48 @@ verify_smpte_timed_text_asset ( total_size += i.second.size(); } if (total_size > 10 * 1024 * 1024) { - notes.push_back ({ VerificationNote::Type::BV21_ERROR, VerificationNote::Code::INVALID_TIMED_TEXT_FONT_SIZE_IN_BYTES, raw_convert<string>(total_size), asset->file().get() }); + context.bv21_error(VerificationNote::Code::INVALID_TIMED_TEXT_FONT_SIZE_IN_BYTES, raw_convert<string>(total_size), asset->file().get()); } if (!asset->start_time()) { - notes.push_back ({ VerificationNote::Type::BV21_ERROR, VerificationNote::Code::MISSING_SUBTITLE_START_TIME, asset->file().get() }); + context.bv21_error(VerificationNote::Code::MISSING_SUBTITLE_START_TIME, asset->file().get()); } else if (asset->start_time() != Time()) { - notes.push_back ({ VerificationNote::Type::BV21_ERROR, VerificationNote::Code::INVALID_SUBTITLE_START_TIME, asset->file().get() }); + context.bv21_error(VerificationNote::Code::INVALID_SUBTITLE_START_TIME, asset->file().get()); } if (reel_asset_duration && *reel_asset_duration != asset->intrinsic_duration()) { - notes.push_back ( - { - VerificationNote::Type::BV21_ERROR, - VerificationNote::Code::MISMATCHED_TIMED_TEXT_DURATION, - String::compose("%1 %2", *reel_asset_duration, asset->intrinsic_duration()), - asset->file().get() - }); + context.bv21_error( + VerificationNote::Code::MISMATCHED_TIMED_TEXT_DURATION, + String::compose("%1 %2", *reel_asset_duration, asset->intrinsic_duration()), + asset->file().get() + ); } } /** Verify Interop subtitle / CCAP stuff */ void -verify_interop_text_asset(shared_ptr<const InteropSubtitleAsset> asset, vector<VerificationNote>& notes) +verify_interop_text_asset(Context& context, shared_ptr<const InteropSubtitleAsset> asset) { if (asset->subtitles().empty()) { - notes.push_back({VerificationNote::Type::ERROR, VerificationNote::Code::MISSING_SUBTITLE, asset->id(), asset->file().get() }); + context.error(VerificationNote::Code::MISSING_SUBTITLE, asset->id(), asset->file().get()); } auto const unresolved = asset->unresolved_fonts(); if (!unresolved.empty()) { - notes.push_back({VerificationNote::Type::ERROR, VerificationNote::Code::MISSING_FONT, unresolved.front() }); + context.error(VerificationNote::Code::MISSING_FONT, unresolved.front()); } } /** Verify SMPTE subtitle-only stuff */ void -verify_smpte_subtitle_asset ( - shared_ptr<const SMPTESubtitleAsset> asset, - vector<VerificationNote>& notes, - State& state - ) +verify_smpte_subtitle_asset(Context& context, shared_ptr<const SMPTESubtitleAsset> asset) { if (asset->language()) { - if (!state.subtitle_language) { - state.subtitle_language = *asset->language(); - } else if (state.subtitle_language != *asset->language()) { - notes.push_back ({ VerificationNote::Type::BV21_ERROR, VerificationNote::Code::MISMATCHED_SUBTITLE_LANGUAGES }); + if (!context.subtitle_language) { + context.subtitle_language = *asset->language(); + } else if (context.subtitle_language != *asset->language()) { + context.bv21_error(VerificationNote::Code::MISMATCHED_SUBTITLE_LANGUAGES); } } @@ -784,14 +826,14 @@ verify_smpte_subtitle_asset ( auto xml_id = asset->xml_id(); if (xml_id) { if (asset->resource_id().get() != xml_id) { - notes.push_back ({ VerificationNote::Type::BV21_ERROR, VerificationNote::Code::MISMATCHED_TIMED_TEXT_RESOURCE_ID }); + context.bv21_error(VerificationNote::Code::MISMATCHED_TIMED_TEXT_RESOURCE_ID); } if (asset->id() == asset->resource_id().get() || asset->id() == xml_id) { - notes.push_back ({ VerificationNote::Type::BV21_ERROR, VerificationNote::Code::INCORRECT_TIMED_TEXT_ASSET_ID }); + context.bv21_error(VerificationNote::Code::INCORRECT_TIMED_TEXT_ASSET_ID); } } else { - notes.push_back ({VerificationNote::Type::WARNING, VerificationNote::Code::MISSED_CHECK_OF_ENCRYPTED}); + context.warning(VerificationNote::Code::MISSED_CHECK_OF_ENCRYPTED); } if (asset->raw_xml()) { @@ -801,7 +843,7 @@ verify_smpte_subtitle_asset ( auto issue_date = doc.string_child("IssueDate"); std::regex reg("^\\d\\d\\d\\d-\\d\\d-\\d\\dT\\d\\d:\\d\\d:\\d\\d$"); if (!std::regex_match(issue_date, reg)) { - notes.push_back({VerificationNote::Type::WARNING, VerificationNote::Code::INVALID_SUBTITLE_ISSUE_DATE, issue_date}); + context.warning(VerificationNote::Code::INVALID_SUBTITLE_ISSUE_DATE, issue_date); } } } @@ -809,23 +851,16 @@ verify_smpte_subtitle_asset ( /** Verify all subtitle stuff */ static void -verify_subtitle_asset ( - shared_ptr<const SubtitleAsset> asset, - optional<int64_t> reel_asset_duration, - function<void (string, optional<boost::filesystem::path>)> stage, - boost::filesystem::path xsd_dtd_directory, - vector<VerificationNote>& notes, - State& state - ) +verify_subtitle_asset(Context& context, shared_ptr<const SubtitleAsset> asset, optional<int64_t> reel_asset_duration) { - stage ("Checking subtitle XML", asset->file()); + context.stage("Checking subtitle XML", asset->file()); /* Note: we must not use SubtitleAsset::xml_as_string() here as that will mean the data on disk * gets passed through libdcp which may clean up and therefore hide errors. */ if (asset->raw_xml()) { - validate_xml (asset->raw_xml().get(), xsd_dtd_directory, notes); + validate_xml(context, asset->raw_xml().get()); } else { - notes.push_back ({VerificationNote::Type::WARNING, VerificationNote::Code::MISSED_CHECK_OF_ENCRYPTED}); + context.warning(VerificationNote::Code::MISSED_CHECK_OF_ENCRYPTED); } auto namespace_count = [](shared_ptr<const SubtitleAsset> asset, string root_node) { @@ -841,19 +876,19 @@ verify_subtitle_asset ( auto interop = dynamic_pointer_cast<const InteropSubtitleAsset>(asset); if (interop) { - verify_interop_text_asset(interop, notes); + verify_interop_text_asset(context, interop); if (namespace_count(asset, "DCSubtitle") > 1) { - notes.push_back({ VerificationNote::Type::WARNING, VerificationNote::Code::INCORRECT_SUBTITLE_NAMESPACE_COUNT, asset->id() }); + context.warning(VerificationNote::Code::INCORRECT_SUBTITLE_NAMESPACE_COUNT, asset->id()); } } auto smpte = dynamic_pointer_cast<const SMPTESubtitleAsset>(asset); if (smpte) { - verify_smpte_timed_text_asset (smpte, reel_asset_duration, notes); - verify_smpte_subtitle_asset (smpte, notes, state); + verify_smpte_timed_text_asset(context, smpte, reel_asset_duration); + verify_smpte_subtitle_asset(context, smpte); /* This asset may be encrypted and in that case we'll have no raw_xml() */ if (asset->raw_xml() && namespace_count(asset, "SubtitleReel") > 1) { - notes.push_back({ VerificationNote::Type::WARNING, VerificationNote::Code::INCORRECT_SUBTITLE_NAMESPACE_COUNT, asset->id()}); + context.warning(VerificationNote::Code::INCORRECT_SUBTITLE_NAMESPACE_COUNT, asset->id()); } } } @@ -862,35 +897,33 @@ verify_subtitle_asset ( /** Verify all closed caption stuff */ static void verify_closed_caption_asset ( + Context& context, shared_ptr<const SubtitleAsset> asset, - optional<int64_t> reel_asset_duration, - function<void (string, optional<boost::filesystem::path>)> stage, - boost::filesystem::path xsd_dtd_directory, - vector<VerificationNote>& notes + optional<int64_t> reel_asset_duration ) { - stage ("Checking closed caption XML", asset->file()); + context.stage("Checking closed caption XML", asset->file()); /* Note: we must not use SubtitleAsset::xml_as_string() here as that will mean the data on disk * gets passed through libdcp which may clean up and therefore hide errors. */ auto raw_xml = asset->raw_xml(); if (raw_xml) { - validate_xml (*raw_xml, xsd_dtd_directory, notes); + validate_xml(context, *raw_xml); if (raw_xml->size() > 256 * 1024) { - notes.push_back ({VerificationNote::Type::BV21_ERROR, VerificationNote::Code::INVALID_CLOSED_CAPTION_XML_SIZE_IN_BYTES, raw_convert<string>(raw_xml->size()), *asset->file()}); + context.bv21_error(VerificationNote::Code::INVALID_CLOSED_CAPTION_XML_SIZE_IN_BYTES, raw_convert<string>(raw_xml->size()), *asset->file()); } } else { - notes.push_back ({VerificationNote::Type::WARNING, VerificationNote::Code::MISSED_CHECK_OF_ENCRYPTED}); + context.warning(VerificationNote::Code::MISSED_CHECK_OF_ENCRYPTED); } auto interop = dynamic_pointer_cast<const InteropSubtitleAsset>(asset); if (interop) { - verify_interop_text_asset(interop, notes); + verify_interop_text_asset(context, interop); } auto smpte = dynamic_pointer_cast<const SMPTESubtitleAsset>(asset); if (smpte) { - verify_smpte_timed_text_asset (smpte, reel_asset_duration, notes); + verify_smpte_timed_text_asset(context, smpte, reel_asset_duration); } } @@ -899,10 +932,9 @@ verify_closed_caption_asset ( static void verify_text_details ( - dcp::Standard standard, + Context& context, vector<shared_ptr<Reel>> reels, int edit_rate, - vector<VerificationNote>& notes, std::function<bool (shared_ptr<Reel>)> check, std::function<optional<string> (shared_ptr<Reel>)> xml, std::function<int64_t (shared_ptr<Reel>)> duration, @@ -996,7 +1028,7 @@ verify_text_details ( auto reel_xml = xml(reels[i]); if (!reel_xml) { - notes.push_back ({VerificationNote::Type::WARNING, VerificationNote::Code::MISSED_CHECK_OF_ENCRYPTED}); + context.warning(VerificationNote::Code::MISSED_CHECK_OF_ENCRYPTED); continue; } @@ -1007,7 +1039,7 @@ verify_text_details ( shared_ptr<cxml::Document> doc; optional<int> tcr; optional<Time> start_time; - switch (standard) { + switch (context.dcp->standard().get_value_or(dcp::Standard::SMPTE)) { case dcp::Standard::INTEROP: doc = make_shared<cxml::Document>("DCSubtitle"); doc->read_string (*reel_xml); @@ -1030,8 +1062,8 @@ verify_text_details ( } reel_offset = end; - if (standard == dcp::Standard::SMPTE && has_text && font_ids.empty()) { - notes.push_back(dcp::VerificationNote(dcp::VerificationNote::Type::ERROR, VerificationNote::Code::MISSING_LOAD_FONT).set_id(id(reels[i]))); + if (context.dcp->standard() && *context.dcp->standard() == dcp::Standard::SMPTE && has_text && font_ids.empty()) { + context.add_note(dcp::VerificationNote(dcp::VerificationNote::Type::ERROR, VerificationNote::Code::MISSING_LOAD_FONT).set_id(id(reels[i]))); } } @@ -1040,47 +1072,34 @@ verify_text_details ( } if (too_early) { - notes.push_back({ - VerificationNote::Type::WARNING, VerificationNote::Code::INVALID_SUBTITLE_FIRST_TEXT_TIME - }); + context.warning(VerificationNote::Code::INVALID_SUBTITLE_FIRST_TEXT_TIME); } if (too_short) { - notes.push_back ({ - VerificationNote::Type::WARNING, VerificationNote::Code::INVALID_SUBTITLE_DURATION - }); + context.warning(VerificationNote::Code::INVALID_SUBTITLE_DURATION); } if (too_close) { - notes.push_back ({ - VerificationNote::Type::WARNING, VerificationNote::Code::INVALID_SUBTITLE_SPACING - }); + context.warning(VerificationNote::Code::INVALID_SUBTITLE_SPACING); } if (reel_overlap) { - notes.push_back ({ - VerificationNote::Type::ERROR, VerificationNote::Code::SUBTITLE_OVERLAPS_REEL_BOUNDARY - }); + context.error(VerificationNote::Code::SUBTITLE_OVERLAPS_REEL_BOUNDARY); } if (empty_text) { - notes.push_back ({ - VerificationNote::Type::WARNING, VerificationNote::Code::EMPTY_TEXT - }); + context.warning(VerificationNote::Code::EMPTY_TEXT); } if (missing_load_font_id) { - notes.push_back(dcp::VerificationNote(VerificationNote::Type::ERROR, VerificationNote::Code::MISSING_LOAD_FONT_FOR_FONT).set_id(*missing_load_font_id)); + context.add_note(dcp::VerificationNote(VerificationNote::Type::ERROR, VerificationNote::Code::MISSING_LOAD_FONT_FOR_FONT).set_id(*missing_load_font_id)); } } static void -verify_closed_caption_details ( - vector<shared_ptr<Reel>> reels, - vector<VerificationNote>& notes - ) +verify_closed_caption_details(Context& context, vector<shared_ptr<Reel>> reels) { std::function<void (cxml::ConstNodePtr node, std::vector<cxml::ConstNodePtr>& text_or_image)> find_text_or_image; find_text_or_image = [&find_text_or_image](cxml::ConstNodePtr node, std::vector<cxml::ConstNodePtr>& text_or_image) { @@ -1146,7 +1165,7 @@ verify_closed_caption_details ( for (auto ccap: reel->closed_captions()) { auto reel_xml = ccap->asset()->raw_xml(); if (!reel_xml) { - notes.push_back ({VerificationNote::Type::WARNING, VerificationNote::Code::MISSED_CHECK_OF_ENCRYPTED}); + context.warning(VerificationNote::Code::MISSED_CHECK_OF_ENCRYPTED); continue; } @@ -1169,15 +1188,11 @@ verify_closed_caption_details ( } if (mismatched_valign) { - notes.push_back ({ - VerificationNote::Type::ERROR, VerificationNote::Code::MISMATCHED_CLOSED_CAPTION_VALIGN, - }); + context.error(VerificationNote::Code::MISMATCHED_CLOSED_CAPTION_VALIGN); } if (incorrect_order) { - notes.push_back ({ - VerificationNote::Type::ERROR, VerificationNote::Code::INCORRECT_CLOSED_CAPTION_ORDERING, - }); + context.error(VerificationNote::Code::INCORRECT_CLOSED_CAPTION_ORDERING); } } @@ -1283,14 +1298,14 @@ verify_text_lines_and_characters ( static void -verify_text_details(dcp::Standard standard, vector<shared_ptr<Reel>> reels, vector<VerificationNote>& notes) +verify_text_details(Context& context, vector<shared_ptr<Reel>> reels) { if (reels.empty()) { return; } if (reels[0]->main_subtitle() && reels[0]->main_subtitle()->asset_ref().resolved()) { - verify_text_details(standard, reels, reels[0]->main_subtitle()->edit_rate().numerator, notes, + verify_text_details(context, reels, reels[0]->main_subtitle()->edit_rate().numerator, [](shared_ptr<Reel> reel) { return static_cast<bool>(reel->main_subtitle()); }, @@ -1307,7 +1322,7 @@ verify_text_details(dcp::Standard standard, vector<shared_ptr<Reel>> reels, vect } for (auto i = 0U; i < reels[0]->closed_captions().size(); ++i) { - verify_text_details(standard, reels, reels[0]->closed_captions()[i]->edit_rate().numerator, notes, + verify_text_details(context, reels, reels[0]->closed_captions()[i]->edit_rate().numerator, [i](shared_ptr<Reel> reel) { return i < reel->closed_captions().size(); }, @@ -1323,12 +1338,12 @@ verify_text_details(dcp::Standard standard, vector<shared_ptr<Reel>> reels, vect ); } - verify_closed_caption_details (reels, notes); + verify_closed_caption_details(context, reels); } void -verify_extension_metadata(shared_ptr<const CPL> cpl, vector<VerificationNote>& notes) +verify_extension_metadata(Context& context, shared_ptr<const CPL> cpl) { DCP_ASSERT (cpl->file()); cxml::Document doc ("CompositionPlaylist"); @@ -1378,9 +1393,9 @@ verify_extension_metadata(shared_ptr<const CPL> cpl, vector<VerificationNote>& n } if (missing) { - notes.push_back ({VerificationNote::Type::BV21_ERROR, VerificationNote::Code::MISSING_EXTENSION_METADATA, cpl->id(), cpl->file().get()}); + context.bv21_error(VerificationNote::Code::MISSING_EXTENSION_METADATA, cpl->file().get()); } else if (!malformed.empty()) { - notes.push_back ({VerificationNote::Type::BV21_ERROR, VerificationNote::Code::INVALID_EXTENSION_METADATA, malformed, cpl->file().get()}); + context.bv21_error(VerificationNote::Code::INVALID_EXTENSION_METADATA, malformed, cpl->file().get()); } } @@ -1413,17 +1428,10 @@ pkl_has_encrypted_assets(shared_ptr<const DCP> dcp, shared_ptr<const PKL> pkl) static void verify_reel( - shared_ptr<const DCP> dcp, - shared_ptr<const CPL> cpl, + Context& context, shared_ptr<const Reel> reel, int64_t start_frame, optional<dcp::Size> main_picture_active_area, - function<void (string, optional<boost::filesystem::path>)> stage, - boost::filesystem::path xsd_dtd_directory, - function<void (float)> progress, - VerificationOptions options, - vector<VerificationNote>& notes, - State& state, bool* have_main_subtitle, bool* have_no_main_subtitle, size_t* most_closed_captions, @@ -1433,24 +1441,24 @@ verify_reel( { for (auto i: reel->assets()) { if (i->duration() && (i->duration().get() * i->edit_rate().denominator / i->edit_rate().numerator) < 1) { - notes.push_back({VerificationNote::Type::ERROR, VerificationNote::Code::INVALID_DURATION, i->id()}); + context.error(VerificationNote::Code::INVALID_DURATION, i->id()); } if ((i->intrinsic_duration() * i->edit_rate().denominator / i->edit_rate().numerator) < 1) { - notes.push_back({VerificationNote::Type::ERROR, VerificationNote::Code::INVALID_INTRINSIC_DURATION, i->id()}); + context.error(VerificationNote::Code::INVALID_INTRINSIC_DURATION, i->id()); } auto file_asset = dynamic_pointer_cast<ReelFileAsset>(i); if (i->encryptable() && !file_asset->hash()) { - notes.push_back({VerificationNote::Type::BV21_ERROR, VerificationNote::Code::MISSING_HASH, i->id()}); + context.bv21_error(VerificationNote::Code::MISSING_HASH, i->id()); } } - if (dcp->standard() == Standard::SMPTE) { + if (context.dcp->standard() == Standard::SMPTE) { boost::optional<int64_t> duration; for (auto i: reel->assets()) { if (!duration) { duration = i->actual_duration(); } else if (*duration != i->actual_duration()) { - notes.push_back({VerificationNote::Type::BV21_ERROR, VerificationNote::Code::MISMATCHED_ASSET_DURATION}); + context.bv21_error(VerificationNote::Code::MISMATCHED_ASSET_DURATION); break; } } @@ -1467,32 +1475,26 @@ verify_reel( frame_rate.numerator != 50 && frame_rate.numerator != 60 && frame_rate.numerator != 96)) { - notes.push_back({ - VerificationNote::Type::ERROR, - VerificationNote::Code::INVALID_PICTURE_FRAME_RATE, - String::compose("%1/%2", frame_rate.numerator, frame_rate.denominator) - }); + context.error(VerificationNote::Code::INVALID_PICTURE_FRAME_RATE, String::compose("%1/%2", frame_rate.numerator, frame_rate.denominator)); } /* Check asset */ if (reel->main_picture()->asset_ref().resolved()) { - verify_main_picture_asset(dcp, reel->main_picture(), start_frame, stage, progress, options, notes); + verify_main_picture_asset(context, reel->main_picture(), start_frame); auto const asset_size = reel->main_picture()->asset()->size(); if (main_picture_active_area) { if (main_picture_active_area->width > asset_size.width) { - notes.push_back({ - VerificationNote::Type::ERROR, - VerificationNote::Code::INVALID_MAIN_PICTURE_ACTIVE_AREA, - String::compose("width %1 is bigger than the asset width %2", main_picture_active_area->width, asset_size.width), - cpl->file().get() - }); + context.error( + VerificationNote::Code::INVALID_MAIN_PICTURE_ACTIVE_AREA, + String::compose("width %1 is bigger than the asset width %2", main_picture_active_area->width, asset_size.width), + context.cpl->file().get() + ); } if (main_picture_active_area->height > asset_size.height) { - notes.push_back({ - VerificationNote::Type::ERROR, - VerificationNote::Code::INVALID_MAIN_PICTURE_ACTIVE_AREA, - String::compose("height %1 is bigger than the asset height %2", main_picture_active_area->height, asset_size.height), - cpl->file().get() - }); + context.error( + VerificationNote::Code::INVALID_MAIN_PICTURE_ACTIVE_AREA, + String::compose("height %1 is bigger than the asset height %2", main_picture_active_area->height, asset_size.height), + context.cpl->file().get() + ); } } } @@ -1500,13 +1502,13 @@ verify_reel( } if (reel->main_sound() && reel->main_sound()->asset_ref().resolved()) { - verify_main_sound_asset(dcp, reel->main_sound(), stage, progress, options, notes, state); + verify_main_sound_asset(context, reel->main_sound()); } if (reel->main_subtitle()) { - verify_main_subtitle_reel(reel->main_subtitle(), notes); + verify_main_subtitle_reel(context, reel->main_subtitle()); if (reel->main_subtitle()->asset_ref().resolved()) { - verify_subtitle_asset(reel->main_subtitle()->asset(), reel->main_subtitle()->duration(), stage, xsd_dtd_directory, notes, state); + verify_subtitle_asset(context, reel->main_subtitle()->asset(), reel->main_subtitle()->duration()); } *have_main_subtitle = true; } else { @@ -1514,9 +1516,9 @@ verify_reel( } for (auto i: reel->closed_captions()) { - verify_closed_caption_reel(i, notes); + verify_closed_caption_reel(context, i); if (i->asset_ref().resolved()) { - verify_closed_caption_asset(i->asset(), i->duration(), stage, xsd_dtd_directory, notes); + verify_closed_caption_asset(context, i->asset(), i->duration()); } } @@ -1525,10 +1527,10 @@ verify_reel( markers_seen->insert(i); } if (reel->main_markers()->entry_point()) { - notes.push_back({VerificationNote::Type::ERROR, VerificationNote::Code::UNEXPECTED_ENTRY_POINT}); + context.error(VerificationNote::Code::UNEXPECTED_ENTRY_POINT); } if (reel->main_markers()->duration()) { - notes.push_back({VerificationNote::Type::ERROR, VerificationNote::Code::UNEXPECTED_DURATION}); + context.error(VerificationNote::Code::UNEXPECTED_DURATION); } } @@ -1540,26 +1542,21 @@ verify_reel( static void -verify_cpl( - shared_ptr<const DCP> dcp, - shared_ptr<const CPL> cpl, - function<void (string, optional<boost::filesystem::path>)> stage, - boost::filesystem::path xsd_dtd_directory, - function<void (float)> progress, - VerificationOptions options, - vector<VerificationNote>& notes, - State& state - ) +verify_cpl(Context& context, shared_ptr<const CPL> cpl) { - stage("Checking CPL", cpl->file()); - validate_xml(cpl->file().get(), xsd_dtd_directory, notes); + context.stage("Checking CPL", cpl->file()); + validate_xml(context, cpl->file().get()); if (cpl->any_encrypted() && !cpl->all_encrypted()) { - notes.push_back({VerificationNote::Type::BV21_ERROR, VerificationNote::Code::PARTIALLY_ENCRYPTED}); + context.bv21_error(VerificationNote::Code::PARTIALLY_ENCRYPTED); + } else if (cpl->all_encrypted()) { + context.ok(VerificationNote::Code::ALL_ENCRYPTED); + } else if (!cpl->all_encrypted()) { + context.ok(VerificationNote::Code::NONE_ENCRYPTED); } for (auto const& i: cpl->additional_subtitle_languages()) { - verify_language_tag(i, notes); + verify_language_tag(context, i); } if (!cpl->content_kind().scope() || *cpl->content_kind().scope() == "http://www.smpte-ra.org/schemas/429-7/2006/CPL#standard-content") { @@ -1571,61 +1568,71 @@ verify_cpl( transform(name.begin(), name.end(), name.begin(), ::tolower); auto iter = std::find_if(all.begin(), all.end(), [name](ContentKind const& k) { return !k.scope() && k.name() == name; }); if (iter == all.end()) { - notes.push_back({VerificationNote::Type::ERROR, VerificationNote::Code::INVALID_CONTENT_KIND, cpl->content_kind().name()}); + context.error(VerificationNote::Code::INVALID_CONTENT_KIND, cpl->content_kind().name()); + } else { + context.ok(VerificationNote::Code::VALID_CONTENT_KIND, cpl->content_kind().name()); } } if (cpl->release_territory()) { if (!cpl->release_territory_scope() || cpl->release_territory_scope().get() != "http://www.smpte-ra.org/schemas/429-16/2014/CPL-Metadata#scope/release-territory/UNM49") { auto terr = cpl->release_territory().get(); + bool valid = true; /* Must be a valid region tag, or "001" */ try { LanguageTag::RegionSubtag test(terr); } catch (...) { if (terr != "001") { - notes.push_back({VerificationNote::Type::BV21_ERROR, VerificationNote::Code::INVALID_LANGUAGE, terr}); + context.bv21_error(VerificationNote::Code::INVALID_LANGUAGE, terr); + valid = false; } } + if (valid) { + context.ok(VerificationNote::Code::VALID_RELEASE_TERRITORY, terr); + } } } for (auto version: cpl->content_versions()) { if (version.label_text.empty()) { - notes.push_back( - dcp::VerificationNote(VerificationNote::Type::WARNING, VerificationNote::Code::EMPTY_CONTENT_VERSION_LABEL_TEXT, cpl->file().get()).set_id(cpl->id()) - ); + context.warning(VerificationNote::Code::EMPTY_CONTENT_VERSION_LABEL_TEXT, cpl->file().get()); break; + } else { + context.ok(VerificationNote::Code::VALID_CONTENT_VERSION_LABEL_TEXT, version.label_text); } } - if (dcp->standard() == Standard::SMPTE) { + if (context.dcp->standard() == Standard::SMPTE) { if (!cpl->annotation_text()) { - notes.push_back({VerificationNote::Type::BV21_ERROR, VerificationNote::Code::MISSING_CPL_ANNOTATION_TEXT, cpl->id(), cpl->file().get()}); + context.bv21_error(VerificationNote::Code::MISSING_CPL_ANNOTATION_TEXT, cpl->file().get()); } else if (cpl->annotation_text().get() != cpl->content_title_text()) { - notes.push_back({VerificationNote::Type::WARNING, VerificationNote::Code::MISMATCHED_CPL_ANNOTATION_TEXT, cpl->id(), cpl->file().get()}); + context.warning(VerificationNote::Code::MISMATCHED_CPL_ANNOTATION_TEXT, cpl->file().get()); + } else { + context.ok(VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, cpl->annotation_text().get()); } } - for (auto i: dcp->pkls()) { + for (auto i: context.dcp->pkls()) { /* Check that the CPL's hash corresponds to the PKL */ optional<string> h = i->hash(cpl->id()); auto calculated_cpl_hash = make_digest(ArrayData(*cpl->file())); if (h && calculated_cpl_hash != *h) { - notes.push_back( + context.add_note( dcp::VerificationNote( VerificationNote::Type::ERROR, VerificationNote::Code::MISMATCHED_CPL_HASHES, - cpl->id(), cpl->file().get() ).set_calculated_hash(calculated_cpl_hash).set_reference_hash(*h) ); + } else { + context.ok(VerificationNote::Code::MATCHING_CPL_HASHES); } /* Check that any PKL with a single CPL has its AnnotationText the same as the CPL's ContentTitleText */ optional<string> required_annotation_text; for (auto j: i->assets()) { /* See if this is a CPL */ - for (auto k: dcp->cpls()) { + for (auto k: context.dcp->cpls()) { if (j->id() == k->id()) { if (!required_annotation_text) { /* First CPL we have found; this is the required AnnotationText unless we find another */ @@ -1639,7 +1646,9 @@ verify_cpl( } if (required_annotation_text && i->annotation_text() != required_annotation_text) { - notes.push_back({VerificationNote::Type::BV21_ERROR, VerificationNote::Code::MISMATCHED_PKL_ANNOTATION_TEXT_WITH_CPL, i->id(), i->file().get()}); + context.bv21_error(VerificationNote::Code::MISMATCHED_PKL_ANNOTATION_TEXT_WITH_CPL, i->id(), i->file().get()); + } else { + context.ok(VerificationNote::Code::MATCHING_PKL_ANNOTATION_TEXT_WITH_CPL); } } @@ -1654,38 +1663,39 @@ verify_cpl( map<Marker, Time> markers_seen; auto const main_picture_active_area = cpl->main_picture_active_area(); + bool active_area_ok = true; if (main_picture_active_area && (main_picture_active_area->width % 2)) { - notes.push_back({ - VerificationNote::Type::ERROR, - VerificationNote::Code::INVALID_MAIN_PICTURE_ACTIVE_AREA, - String::compose("width %1 is not a multiple of 2", main_picture_active_area->width), - cpl->file().get() - }); + context.error( + VerificationNote::Code::INVALID_MAIN_PICTURE_ACTIVE_AREA, + String::compose("width %1 is not a multiple of 2", main_picture_active_area->width), + cpl->file().get() + ); + active_area_ok = false; } if (main_picture_active_area && (main_picture_active_area->height % 2)) { - notes.push_back({ - VerificationNote::Type::ERROR, - VerificationNote::Code::INVALID_MAIN_PICTURE_ACTIVE_AREA, - String::compose("height %1 is not a multiple of 2", main_picture_active_area->height), - cpl->file().get() - }); + context.error( + VerificationNote::Code::INVALID_MAIN_PICTURE_ACTIVE_AREA, + String::compose("height %1 is not a multiple of 2", main_picture_active_area->height), + cpl->file().get() + ); + active_area_ok = false; + } + + if (main_picture_active_area && active_area_ok) { + context.ok( + VerificationNote::Code::VALID_MAIN_PICTURE_ACTIVE_AREA, String::compose("%1x%2", main_picture_active_area->width, main_picture_active_area->height), + cpl->file().get() + ); } int64_t frame = 0; for (auto reel: cpl->reels()) { - stage("Checking reel", optional<boost::filesystem::path>()); + context.stage("Checking reel", optional<boost::filesystem::path>()); verify_reel( - dcp, - cpl, + context, reel, frame, main_picture_active_area, - stage, - xsd_dtd_directory, - progress, - options, - notes, - state, &have_main_subtitle, &have_no_main_subtitle, &most_closed_captions, @@ -1695,51 +1705,50 @@ verify_cpl( frame += reel->duration(); } - verify_text_details(dcp->standard().get_value_or(dcp::Standard::SMPTE), cpl->reels(), notes); + verify_text_details(context, cpl->reels()); - if (dcp->standard() == Standard::SMPTE) { + if (context.dcp->standard() == Standard::SMPTE) { if (auto msc = cpl->main_sound_configuration()) { - if (state.audio_channels && msc->channels() != *state.audio_channels) { - notes.push_back({ - VerificationNote::Type::ERROR, - VerificationNote::Code::INVALID_MAIN_SOUND_CONFIGURATION, - String::compose("MainSoundConfiguration has %1 channels but sound assets have %2", msc->channels(), *state.audio_channels), - cpl->file().get() - }); + if (context.audio_channels && msc->channels() != *context.audio_channels) { + context.error( + VerificationNote::Code::INVALID_MAIN_SOUND_CONFIGURATION, + String::compose("MainSoundConfiguration has %1 channels but sound assets have %2", msc->channels(), *context.audio_channels), + cpl->file().get() + ); } } if (have_main_subtitle && have_no_main_subtitle) { - notes.push_back({VerificationNote::Type::BV21_ERROR, VerificationNote::Code::MISSING_MAIN_SUBTITLE_FROM_SOME_REELS}); + context.bv21_error(VerificationNote::Code::MISSING_MAIN_SUBTITLE_FROM_SOME_REELS); } if (fewest_closed_captions != most_closed_captions) { - notes.push_back({VerificationNote::Type::BV21_ERROR, VerificationNote::Code::MISMATCHED_CLOSED_CAPTION_ASSET_COUNTS}); + context.bv21_error(VerificationNote::Code::MISMATCHED_CLOSED_CAPTION_ASSET_COUNTS); } if (cpl->content_kind() == ContentKind::FEATURE) { if (markers_seen.find(Marker::FFEC) == markers_seen.end()) { - notes.push_back({VerificationNote::Type::BV21_ERROR, VerificationNote::Code::MISSING_FFEC_IN_FEATURE}); + context.bv21_error(VerificationNote::Code::MISSING_FFEC_IN_FEATURE); } if (markers_seen.find(Marker::FFMC) == markers_seen.end()) { - notes.push_back({VerificationNote::Type::BV21_ERROR, VerificationNote::Code::MISSING_FFMC_IN_FEATURE}); + context.bv21_error(VerificationNote::Code::MISSING_FFMC_IN_FEATURE); } } auto ffoc = markers_seen.find(Marker::FFOC); if (ffoc == markers_seen.end()) { - notes.push_back({VerificationNote::Type::WARNING, VerificationNote::Code::MISSING_FFOC}); + context.warning(VerificationNote::Code::MISSING_FFOC); } else if (ffoc->second.e != 1) { - notes.push_back({VerificationNote::Type::WARNING, VerificationNote::Code::INCORRECT_FFOC, raw_convert<string>(ffoc->second.e)}); + context.warning(VerificationNote::Code::INCORRECT_FFOC, raw_convert<string>(ffoc->second.e)); } auto lfoc = markers_seen.find(Marker::LFOC); if (lfoc == markers_seen.end()) { - notes.push_back({VerificationNote::Type::WARNING, VerificationNote::Code::MISSING_LFOC}); + context.warning(VerificationNote::Code::MISSING_LFOC); } else { auto lfoc_time = lfoc->second.as_editable_units_ceil(lfoc->second.tcr); if (lfoc_time != (cpl->reels().back()->duration() - 1)) { - notes.push_back({VerificationNote::Type::WARNING, VerificationNote::Code::INCORRECT_LFOC, raw_convert<string>(lfoc_time)}); + context.warning(VerificationNote::Code::INCORRECT_LFOC, raw_convert<string>(lfoc_time)); } } @@ -1751,12 +1760,12 @@ verify_cpl( } if (result.line_count_exceeded) { - notes.push_back({VerificationNote::Type::WARNING, VerificationNote::Code::INVALID_SUBTITLE_LINE_COUNT}); + context.warning(VerificationNote::Code::INVALID_SUBTITLE_LINE_COUNT); } if (result.error_length_exceeded) { - notes.push_back({VerificationNote::Type::WARNING, VerificationNote::Code::INVALID_SUBTITLE_LINE_LENGTH}); + context.warning(VerificationNote::Code::INVALID_SUBTITLE_LINE_LENGTH); } else if (result.warning_length_exceeded) { - notes.push_back({VerificationNote::Type::WARNING, VerificationNote::Code::NEARLY_INVALID_SUBTITLE_LINE_LENGTH}); + context.warning(VerificationNote::Code::NEARLY_INVALID_SUBTITLE_LINE_LENGTH); } result = LinesCharactersResult(); @@ -1769,26 +1778,26 @@ verify_cpl( } if (result.line_count_exceeded) { - notes.push_back({VerificationNote::Type::BV21_ERROR, VerificationNote::Code::INVALID_CLOSED_CAPTION_LINE_COUNT}); + context.bv21_error(VerificationNote::Code::INVALID_CLOSED_CAPTION_LINE_COUNT); } if (result.error_length_exceeded) { - notes.push_back({VerificationNote::Type::BV21_ERROR, VerificationNote::Code::INVALID_CLOSED_CAPTION_LINE_LENGTH}); + context.bv21_error(VerificationNote::Code::INVALID_CLOSED_CAPTION_LINE_LENGTH); } if (!cpl->read_composition_metadata()) { - notes.push_back({VerificationNote::Type::BV21_ERROR, VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get()}); + context.bv21_error(VerificationNote::Code::MISSING_CPL_METADATA, cpl->file().get()); } else if (!cpl->version_number()) { - notes.push_back({VerificationNote::Type::BV21_ERROR, VerificationNote::Code::MISSING_CPL_METADATA_VERSION_NUMBER, cpl->id(), cpl->file().get()}); + context.bv21_error(VerificationNote::Code::MISSING_CPL_METADATA_VERSION_NUMBER, cpl->file().get()); } - verify_extension_metadata(cpl, notes); + verify_extension_metadata(context, cpl); if (cpl->any_encrypted()) { cxml::Document doc("CompositionPlaylist"); DCP_ASSERT(cpl->file()); doc.read_file(dcp::filesystem::fix_long_path(cpl->file().get())); if (!doc.optional_node_child("Signature")) { - notes.push_back({VerificationNote::Type::BV21_ERROR, VerificationNote::Code::UNSIGNED_CPL_WITH_ENCRYPTED_CONTENT, cpl->id(), cpl->file().get()}); + context.bv21_error(VerificationNote::Code::UNSIGNED_CPL_WITH_ENCRYPTED_CONTENT, cpl->file().get()); } } } @@ -1797,27 +1806,22 @@ verify_cpl( static void -verify_pkl( - shared_ptr<const DCP> dcp, - shared_ptr<const PKL> pkl, - boost::filesystem::path xsd_dtd_directory, - vector<VerificationNote>& notes - ) +verify_pkl(Context& context, shared_ptr<const PKL> pkl) { - validate_xml(pkl->file().get(), xsd_dtd_directory, notes); + validate_xml(context, pkl->file().get()); - if (pkl_has_encrypted_assets(dcp, pkl)) { + if (pkl_has_encrypted_assets(context.dcp, pkl)) { cxml::Document doc("PackingList"); doc.read_file(dcp::filesystem::fix_long_path(pkl->file().get())); if (!doc.optional_node_child("Signature")) { - notes.push_back({VerificationNote::Type::BV21_ERROR, VerificationNote::Code::UNSIGNED_PKL_WITH_ENCRYPTED_CONTENT, pkl->id(), pkl->file().get()}); + context.bv21_error(VerificationNote::Code::UNSIGNED_PKL_WITH_ENCRYPTED_CONTENT, pkl->id(), pkl->file().get()); } } set<string> uuid_set; for (auto asset: pkl->assets()) { if (!uuid_set.insert(asset->id()).second) { - notes.push_back({VerificationNote::Type::ERROR, VerificationNote::Code::DUPLICATE_ASSET_ID_IN_PKL, pkl->id(), pkl->file().get()}); + context.error(VerificationNote::Code::DUPLICATE_ASSET_ID_IN_PKL, pkl->id(), pkl->file().get()); break; } } @@ -1827,28 +1831,24 @@ verify_pkl( static void -verify_assetmap( - shared_ptr<const DCP> dcp, - boost::filesystem::path xsd_dtd_directory, - vector<VerificationNote>& notes - ) +verify_assetmap(Context& context, shared_ptr<const DCP> dcp) { auto asset_map = dcp->asset_map(); DCP_ASSERT(asset_map); - validate_xml(asset_map->file().get(), xsd_dtd_directory, notes); + validate_xml(context, asset_map->file().get()); set<string> uuid_set; for (auto const& asset: asset_map->assets()) { if (!uuid_set.insert(asset.id()).second) { - notes.push_back({VerificationNote::Type::ERROR, VerificationNote::Code::DUPLICATE_ASSET_ID_IN_ASSETMAP, asset_map->id(), asset_map->file().get()}); + context.error(VerificationNote::Code::DUPLICATE_ASSET_ID_IN_ASSETMAP, asset_map->id(), asset_map->file().get()); break; } } } -vector<VerificationNote> +dcp::VerificationResult dcp::verify ( vector<boost::filesystem::path> directories, vector<dcp::DecryptedKDM> kdms, @@ -1864,7 +1864,7 @@ dcp::verify ( *xsd_dtd_directory = filesystem::canonical(*xsd_dtd_directory); vector<VerificationNote> notes; - State state{}; + Context context(notes, *xsd_dtd_directory, stage, progress, options); vector<shared_ptr<DCP>> dcps; for (auto i: directories) { @@ -1873,22 +1873,25 @@ dcp::verify ( for (auto dcp: dcps) { stage ("Checking DCP", dcp->directory()); + + context.dcp = dcp; + bool carry_on = true; try { dcp->read (¬es, true); } catch (MissingAssetmapError& e) { - notes.push_back ({VerificationNote::Type::ERROR, VerificationNote::Code::FAILED_READ, string(e.what())}); + context.error(VerificationNote::Code::FAILED_READ, string(e.what())); carry_on = false; } catch (ReadError& e) { - notes.push_back ({VerificationNote::Type::ERROR, VerificationNote::Code::FAILED_READ, string(e.what())}); + context.error(VerificationNote::Code::FAILED_READ, string(e.what())); } catch (XMLError& e) { - notes.push_back ({VerificationNote::Type::ERROR, VerificationNote::Code::FAILED_READ, string(e.what())}); + context.error(VerificationNote::Code::FAILED_READ, string(e.what())); } catch (MXFFileError& e) { - notes.push_back ({VerificationNote::Type::ERROR, VerificationNote::Code::FAILED_READ, string(e.what())}); + context.error(VerificationNote::Code::FAILED_READ, string(e.what())); } catch (BadURNUUIDError& e) { - notes.push_back({VerificationNote::Type::ERROR, VerificationNote::Code::FAILED_READ, string(e.what())}); + context.error(VerificationNote::Code::FAILED_READ, string(e.what())); } catch (cxml::Error& e) { - notes.push_back ({VerificationNote::Type::ERROR, VerificationNote::Code::FAILED_READ, string(e.what())}); + context.error(VerificationNote::Code::FAILED_READ, string(e.what())); } if (!carry_on) { @@ -1905,16 +1908,9 @@ dcp::verify ( for (auto cpl: dcp->cpls()) { try { - verify_cpl( - dcp, - cpl, - stage, - *xsd_dtd_directory, - progress, - options, - notes, - state - ); + context.cpl = cpl; + verify_cpl(context, cpl); + context.cpl.reset(); } catch (ReadError& e) { notes.push_back({VerificationNote::Type::ERROR, VerificationNote::Code::FAILED_READ, string(e.what())}); } @@ -1922,23 +1918,23 @@ dcp::verify ( for (auto pkl: dcp->pkls()) { stage("Checking PKL", pkl->file()); - verify_pkl(dcp, pkl, *xsd_dtd_directory, notes); + verify_pkl(context, pkl); } if (dcp->asset_map_file()) { stage("Checking ASSETMAP", dcp->asset_map_file().get()); - verify_assetmap(dcp, *xsd_dtd_directory, notes); + verify_assetmap(context, dcp); } else { - notes.push_back ({VerificationNote::Type::ERROR, VerificationNote::Code::MISSING_ASSETMAP}); + context.error(VerificationNote::Code::MISSING_ASSETMAP); } } - return notes; + return { notes, dcps }; } string -dcp::note_to_string (VerificationNote note) +dcp::note_to_string(VerificationNote note, function<string (string)> process_string, function<string (string)> process_filename) { /** These strings should say what is wrong, incorporating any extra details (ID, filenames etc.). * @@ -1950,238 +1946,267 @@ dcp::note_to_string (VerificationNote note) * End messages with a full stop. * Messages should not mention whether or not their errors are a part of Bv2.1. */ + + auto filename = [note, process_filename]() { + return process_filename(note.file()->filename().string()); + }; + +#define compose(format, ...) String::compose(process_string(format), __VA_ARGS__) + switch (note.code()) { case VerificationNote::Code::FAILED_READ: - return *note.note(); + return process_string(*note.note()); + case VerificationNote::Code::MATCHING_CPL_HASHES: + return process_string("The hash of the CPL in the PKL matches the CPL file."); case VerificationNote::Code::MISMATCHED_CPL_HASHES: - return String::compose("The hash (%1) of the CPL (%2) in the PKL does not agree with the CPL file (%3).", note.reference_hash().get(), note.note().get(), note.calculated_hash().get()); + return compose("The hash (%1) of the CPL (%2) in the PKL does not agree with the CPL file (%3).", note.reference_hash().get(), note.cpl_id().get(), note.calculated_hash().get()); case VerificationNote::Code::INVALID_PICTURE_FRAME_RATE: - return String::compose("The picture in a reel has an invalid frame rate %1.", note.note().get()); + return compose("The picture in a reel has an invalid frame rate %1.", note.note().get()); case VerificationNote::Code::INCORRECT_PICTURE_HASH: - return String::compose("The hash (%1) of the picture asset %2 does not agree with the PKL file (%3).", note.calculated_hash().get(), note.file()->filename(), note.reference_hash().get()); + return compose("The hash (%1) of the picture asset %2 does not agree with the PKL file (%3).", note.calculated_hash().get(), filename(), note.reference_hash().get()); + case VerificationNote::Code::CORRECT_PICTURE_HASH: + return compose("The picture asset %1 has the expected hashes in the CPL and PKL.", filename()); case VerificationNote::Code::MISMATCHED_PICTURE_HASHES: - return String::compose("The PKL and CPL hashes differ for the picture asset %1.", note.file()->filename()); + return compose("The PKL and CPL hashes differ for the picture asset %1.", filename()); case VerificationNote::Code::INCORRECT_SOUND_HASH: - return String::compose("The hash (%1) of the sound asset %2 does not agree with the PKL file (%3).", note.calculated_hash().get(), note.file()->filename(), note.reference_hash().get()); + return compose("The hash (%1) of the sound asset %2 does not agree with the PKL file (%3).", note.calculated_hash().get(), filename(), note.reference_hash().get()); case VerificationNote::Code::MISMATCHED_SOUND_HASHES: - return String::compose("The PKL and CPL hashes differ for the sound asset %1.", note.file()->filename()); + return compose("The PKL and CPL hashes differ for the sound asset %1.", filename()); case VerificationNote::Code::EMPTY_ASSET_PATH: - return "The asset map contains an empty asset path."; + return process_string("The asset map contains an empty asset path."); case VerificationNote::Code::MISSING_ASSET: - return String::compose("The file %1 for an asset in the asset map cannot be found.", note.file()->filename()); + return compose("The file %1 for an asset in the asset map cannot be found.", filename()); case VerificationNote::Code::MISMATCHED_STANDARD: - return "The DCP contains both SMPTE and Interop parts."; + return process_string("The DCP contains both SMPTE and Interop parts."); case VerificationNote::Code::INVALID_XML: - return String::compose("An XML file is badly formed: %1 (%2:%3)", note.note().get(), note.file()->filename(), note.line().get()); + return compose("An XML file is badly formed: %1 (%2:%3)", note.note().get(), filename(), note.line().get()); case VerificationNote::Code::MISSING_ASSETMAP: - return "No valid ASSETMAP or ASSETMAP.xml was found."; + return process_string("No valid ASSETMAP or ASSETMAP.xml was found."); case VerificationNote::Code::INVALID_INTRINSIC_DURATION: - return String::compose("The intrinsic duration of the asset %1 is less than 1 second.", note.note().get()); + return compose("The intrinsic duration of the asset %1 is less than 1 second.", note.note().get()); case VerificationNote::Code::INVALID_DURATION: - return String::compose("The duration of the asset %1 is less than 1 second.", note.note().get()); + return compose("The duration of the asset %1 is less than 1 second.", note.note().get()); + case VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES: + return compose("Each frame of the picture asset %1 has a bit rate safely under the limit of 250Mbit/s.", filename()); case VerificationNote::Code::INVALID_PICTURE_FRAME_SIZE_IN_BYTES: - return String::compose( + return compose( "Frame %1 (timecode %2) in asset %3 has an instantaneous bit rate that is larger than the limit of 250Mbit/s.", note.frame().get(), dcp::Time(note.frame().get(), note.frame_rate().get(), note.frame_rate().get()).as_string(dcp::Standard::SMPTE), - note.file()->filename() + filename() ); case VerificationNote::Code::NEARLY_INVALID_PICTURE_FRAME_SIZE_IN_BYTES: - return String::compose( + return compose( "Frame %1 (timecode %2) in asset %3 has an instantaneous bit rate that is close to the limit of 250Mbit/s.", note.frame().get(), dcp::Time(note.frame().get(), note.frame_rate().get(), note.frame_rate().get()).as_string(dcp::Standard::SMPTE), - note.file()->filename() + filename() ); case VerificationNote::Code::EXTERNAL_ASSET: - return String::compose("The asset %1 that this DCP refers to is not included in the DCP. It may be a VF.", note.note().get()); + return compose("The asset %1 that this DCP refers to is not included in the DCP. It may be a VF.", note.note().get()); case VerificationNote::Code::THREED_ASSET_MARKED_AS_TWOD: - return String::compose("The asset %1 is 3D but its MXF is marked as 2D.", note.file()->filename()); + return compose("The asset %1 is 3D but its MXF is marked as 2D.", filename()); case VerificationNote::Code::INVALID_STANDARD: return "This DCP does not use the SMPTE standard."; case VerificationNote::Code::INVALID_LANGUAGE: - return String::compose("The DCP specifies a language '%1' which does not conform to the RFC 5646 standard.", note.note().get()); + return compose("The DCP specifies a language '%1' which does not conform to the RFC 5646 standard.", note.note().get()); + case VerificationNote::Code::VALID_RELEASE_TERRITORY: + return compose("Valid release territory %1.", note.note().get()); case VerificationNote::Code::INVALID_PICTURE_SIZE_IN_PIXELS: - return String::compose("The size %1 of picture asset %2 is not allowed.", note.note().get(), note.file()->filename()); + return compose("The size %1 of picture asset %2 is not allowed.", note.note().get(), filename()); case VerificationNote::Code::INVALID_PICTURE_FRAME_RATE_FOR_2K: - return String::compose("The frame rate %1 of picture asset %2 is not allowed for 2K DCPs.", note.note().get(), note.file()->filename()); + return compose("The frame rate %1 of picture asset %2 is not allowed for 2K DCPs.", note.note().get(), filename()); case VerificationNote::Code::INVALID_PICTURE_FRAME_RATE_FOR_4K: - return String::compose("The frame rate %1 of picture asset %2 is not allowed for 4K DCPs.", note.note().get(), note.file()->filename()); + return compose("The frame rate %1 of picture asset %2 is not allowed for 4K DCPs.", note.note().get(), filename()); case VerificationNote::Code::INVALID_PICTURE_ASSET_RESOLUTION_FOR_3D: - return "3D 4K DCPs are not allowed."; + return process_string("3D 4K DCPs are not allowed."); case VerificationNote::Code::INVALID_CLOSED_CAPTION_XML_SIZE_IN_BYTES: - return String::compose("The size %1 of the closed caption asset %2 is larger than the 256KB maximum.", note.note().get(), note.file()->filename()); + return compose("The size %1 of the closed caption asset %2 is larger than the 256KB maximum.", note.note().get(), filename()); case VerificationNote::Code::INVALID_TIMED_TEXT_SIZE_IN_BYTES: - return String::compose("The size %1 of the timed text asset %2 is larger than the 115MB maximum.", note.note().get(), note.file()->filename()); + return compose("The size %1 of the timed text asset %2 is larger than the 115MB maximum.", note.note().get(), filename()); case VerificationNote::Code::INVALID_TIMED_TEXT_FONT_SIZE_IN_BYTES: - return String::compose("The size %1 of the fonts in timed text asset %2 is larger than the 10MB maximum.", note.note().get(), note.file()->filename()); + return compose("The size %1 of the fonts in timed text asset %2 is larger than the 10MB maximum.", note.note().get(), filename()); case VerificationNote::Code::MISSING_SUBTITLE_LANGUAGE: - return String::compose("The XML for the SMPTE subtitle asset %1 has no <Language> tag.", note.file()->filename()); + return compose("The XML for the SMPTE subtitle asset %1 has no <Language> tag.", filename()); case VerificationNote::Code::MISMATCHED_SUBTITLE_LANGUAGES: - return "Some subtitle assets have different <Language> tags than others"; + return process_string("Some subtitle assets have different <Language> tags than others"); case VerificationNote::Code::MISSING_SUBTITLE_START_TIME: - return String::compose("The XML for the SMPTE subtitle asset %1 has no <StartTime> tag.", note.file()->filename()); + return compose("The XML for the SMPTE subtitle asset %1 has no <StartTime> tag.", filename()); case VerificationNote::Code::INVALID_SUBTITLE_START_TIME: - return String::compose("The XML for a SMPTE subtitle asset %1 has a non-zero <StartTime> tag.", note.file()->filename()); + return compose("The XML for a SMPTE subtitle asset %1 has a non-zero <StartTime> tag.", filename()); case VerificationNote::Code::INVALID_SUBTITLE_FIRST_TEXT_TIME: - return "The first subtitle or closed caption is less than 4 seconds from the start of the DCP."; + return process_string("The first subtitle or closed caption is less than 4 seconds from the start of the DCP."); case VerificationNote::Code::INVALID_SUBTITLE_DURATION: - return "At least one subtitle lasts less than 15 frames."; + return process_string("At least one subtitle lasts less than 15 frames."); case VerificationNote::Code::INVALID_SUBTITLE_SPACING: - return "At least one pair of subtitles is separated by less than 2 frames."; + return process_string("At least one pair of subtitles is separated by less than 2 frames."); case VerificationNote::Code::SUBTITLE_OVERLAPS_REEL_BOUNDARY: - return "At least one subtitle extends outside of its reel."; + return process_string("At least one subtitle extends outside of its reel."); case VerificationNote::Code::INVALID_SUBTITLE_LINE_COUNT: - return "There are more than 3 subtitle lines in at least one place in the DCP."; + return process_string("There are more than 3 subtitle lines in at least one place in the DCP."); case VerificationNote::Code::NEARLY_INVALID_SUBTITLE_LINE_LENGTH: - return "There are more than 52 characters in at least one subtitle line."; + return process_string("There are more than 52 characters in at least one subtitle line."); case VerificationNote::Code::INVALID_SUBTITLE_LINE_LENGTH: - return "There are more than 79 characters in at least one subtitle line."; + return process_string("There are more than 79 characters in at least one subtitle line."); case VerificationNote::Code::INVALID_CLOSED_CAPTION_LINE_COUNT: - return "There are more than 3 closed caption lines in at least one place."; + return process_string("There are more than 3 closed caption lines in at least one place."); case VerificationNote::Code::INVALID_CLOSED_CAPTION_LINE_LENGTH: - return "There are more than 32 characters in at least one closed caption line."; + return process_string("There are more than 32 characters in at least one closed caption line."); case VerificationNote::Code::INVALID_SOUND_FRAME_RATE: - return String::compose("The sound asset %1 has a sampling rate of %2", note.file()->filename(), note.note().get()); + return compose("The sound asset %1 has a sampling rate of %2", filename(), note.note().get()); case VerificationNote::Code::MISSING_CPL_ANNOTATION_TEXT: - return String::compose("The CPL %1 has no <AnnotationText> tag.", note.note().get()); + return compose("The CPL %1 has no <AnnotationText> tag.", note.cpl_id().get()); case VerificationNote::Code::MISMATCHED_CPL_ANNOTATION_TEXT: - return String::compose("The CPL %1 has an <AnnotationText> which differs from its <ContentTitleText>.", note.note().get()); + return compose("The CPL %1 has an <AnnotationText> which differs from its <ContentTitleText>.", note.cpl_id().get()); + case VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT: + return compose("Valid CPL annotation text %1", note.note().get()); case VerificationNote::Code::MISMATCHED_ASSET_DURATION: - return "All assets in a reel do not have the same duration."; + return process_string("All assets in a reel do not have the same duration."); case VerificationNote::Code::MISSING_MAIN_SUBTITLE_FROM_SOME_REELS: - return "At least one reel contains a subtitle asset, but some reel(s) do not."; + return process_string("At least one reel contains a subtitle asset, but some reel(s) do not."); case VerificationNote::Code::MISMATCHED_CLOSED_CAPTION_ASSET_COUNTS: - return "At least one reel has closed captions, but reels have different numbers of closed caption assets."; + return process_string("At least one reel has closed captions, but reels have different numbers of closed caption assets."); case VerificationNote::Code::MISSING_SUBTITLE_ENTRY_POINT: - return String::compose("The subtitle asset %1 has no <EntryPoint> tag.", note.note().get()); + return compose("The subtitle asset %1 has no <EntryPoint> tag.", note.note().get()); case VerificationNote::Code::INCORRECT_SUBTITLE_ENTRY_POINT: - return String::compose("The subtitle asset %1 has an <EntryPoint> other than 0.", note.note().get()); + return compose("The subtitle asset %1 has an <EntryPoint> other than 0.", note.note().get()); case VerificationNote::Code::MISSING_CLOSED_CAPTION_ENTRY_POINT: - return String::compose("The closed caption asset %1 has no <EntryPoint> tag.", note.note().get()); + return compose("The closed caption asset %1 has no <EntryPoint> tag.", note.note().get()); case VerificationNote::Code::INCORRECT_CLOSED_CAPTION_ENTRY_POINT: - return String::compose("The closed caption asset %1 has an <EntryPoint> other than 0.", note.note().get()); + return compose("The closed caption asset %1 has an <EntryPoint> other than 0.", note.note().get()); case VerificationNote::Code::MISSING_HASH: - return String::compose("The asset %1 has no <Hash> tag in the CPL.", note.note().get()); + return compose("The asset %1 has no <Hash> tag in the CPL.", note.note().get()); case VerificationNote::Code::MISSING_FFEC_IN_FEATURE: - return "The DCP is marked as a Feature but there is no FFEC (first frame of end credits) marker."; + return process_string("The DCP is marked as a Feature but there is no FFEC (first frame of end credits) marker."); case VerificationNote::Code::MISSING_FFMC_IN_FEATURE: - return "The DCP is marked as a Feature but there is no FFMC (first frame of moving credits) marker."; + return process_string("The DCP is marked as a Feature but there is no FFMC (first frame of moving credits) marker."); case VerificationNote::Code::MISSING_FFOC: - return "There should be a FFOC (first frame of content) marker."; + return process_string("There should be a FFOC (first frame of content) marker."); case VerificationNote::Code::MISSING_LFOC: - return "There should be a LFOC (last frame of content) marker."; + return process_string("There should be a LFOC (last frame of content) marker."); case VerificationNote::Code::INCORRECT_FFOC: - return String::compose("The FFOC marker is %1 instead of 1", note.note().get()); + return compose("The FFOC marker is %1 instead of 1", note.note().get()); case VerificationNote::Code::INCORRECT_LFOC: - return String::compose("The LFOC marker is %1 instead of 1 less than the duration of the last reel.", note.note().get()); + return compose("The LFOC marker is %1 instead of 1 less than the duration of the last reel.", note.note().get()); case VerificationNote::Code::MISSING_CPL_METADATA: - return String::compose("The CPL %1 has no <CompositionMetadataAsset> tag.", note.note().get()); + return compose("The CPL %1 has no <CompositionMetadataAsset> tag.", note.cpl_id().get()); case VerificationNote::Code::MISSING_CPL_METADATA_VERSION_NUMBER: - return String::compose("The CPL %1 has no <VersionNumber> in its <CompositionMetadataAsset>.", note.note().get()); + return compose("The CPL %1 has no <VersionNumber> in its <CompositionMetadataAsset>.", note.cpl_id().get()); case VerificationNote::Code::MISSING_EXTENSION_METADATA: - return String::compose("The CPL %1 has no <ExtensionMetadata> in its <CompositionMetadataAsset>.", note.note().get()); + return compose("The CPL %1 has no <ExtensionMetadata> in its <CompositionMetadataAsset>.", note.cpl_id().get()); case VerificationNote::Code::INVALID_EXTENSION_METADATA: - return String::compose("The CPL %1 has a malformed <ExtensionMetadata> (%2).", note.file()->filename(), note.note().get()); + return compose("The CPL %1 has a malformed <ExtensionMetadata> (%2).", filename(), note.note().get()); case VerificationNote::Code::UNSIGNED_CPL_WITH_ENCRYPTED_CONTENT: - return String::compose("The CPL %1, which has encrypted content, is not signed.", note.note().get()); + return compose("The CPL %1, which has encrypted content, is not signed.", note.cpl_id().get()); case VerificationNote::Code::UNSIGNED_PKL_WITH_ENCRYPTED_CONTENT: - return String::compose("The PKL %1, which has encrypted content, is not signed.", note.note().get()); + return 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 compose("The PKL %1 has only one CPL but its <AnnotationText> does not match the CPL's <ContentTitleText>.", note.note().get()); + case VerificationNote::Code::MATCHING_PKL_ANNOTATION_TEXT_WITH_CPL: + return process_string("The PKL and CPL annotation texts match."); + case VerificationNote::Code::ALL_ENCRYPTED: + return process_string("All the assets are encrypted."); + case VerificationNote::Code::NONE_ENCRYPTED: + return process_string("All the assets are unencrypted."); case VerificationNote::Code::PARTIALLY_ENCRYPTED: - return "Some assets are encrypted but some are not."; + return process_string("Some assets are encrypted but some are not."); case VerificationNote::Code::INVALID_JPEG2000_CODESTREAM: - return String::compose( + return compose( "Frame %1 (timecode %2) has an invalid JPEG2000 codestream (%3).", note.frame().get(), dcp::Time(note.frame().get(), note.frame_rate().get(), note.frame_rate().get()).as_string(dcp::Standard::SMPTE), 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()); + return 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()); + return 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."; + return process_string("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()); + return 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()); + return 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()); + return 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()); + return 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()); + return 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."; + return process_string("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()); + return 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()); + return 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 process_string("No TLM marker was found in a JPEG2000 codestream."); case VerificationNote::Code::MISMATCHED_TIMED_TEXT_RESOURCE_ID: - return "The Resource ID in a timed text MXF did not match the ID of the contained XML."; + return process_string("The Resource ID in a timed text MXF did not match the ID of the contained XML."); case VerificationNote::Code::INCORRECT_TIMED_TEXT_ASSET_ID: - return "The Asset ID in a timed text MXF is the same as the Resource ID or that of the contained XML."; + return process_string("The Asset ID in a timed text MXF is the same as the Resource ID or that of the contained XML."); case VerificationNote::Code::MISMATCHED_TIMED_TEXT_DURATION: { vector<string> parts; boost::split (parts, note.note().get(), boost::is_any_of(" ")); DCP_ASSERT (parts.size() == 2); - return String::compose("The reel duration of some timed text (%1) is not the same as the ContainerDuration of its MXF (%2).", parts[0], parts[1]); + return compose("The reel duration of some timed text (%1) is not the same as the ContainerDuration of its MXF (%2).", parts[0], parts[1]); } case VerificationNote::Code::MISSED_CHECK_OF_ENCRYPTED: - return "Some aspect of this DCP could not be checked because it is encrypted."; + return process_string("Some aspect of this DCP could not be checked because it is encrypted."); case VerificationNote::Code::EMPTY_TEXT: - return "There is an empty <Text> node in a subtitle or closed caption."; + return process_string("There is an empty <Text> node in a subtitle or closed caption."); case VerificationNote::Code::MISMATCHED_CLOSED_CAPTION_VALIGN: - return "Some closed <Text> or <Image> nodes have different vertical alignments within a <Subtitle>."; + return process_string("Some closed <Text> or <Image> nodes have different vertical alignments within a <Subtitle>."); case VerificationNote::Code::INCORRECT_CLOSED_CAPTION_ORDERING: - return "Some closed captions are not listed in the order of their vertical position."; + return process_string("Some closed captions are not listed in the order of their vertical position."); case VerificationNote::Code::UNEXPECTED_ENTRY_POINT: - return "There is an <EntryPoint> node inside a <MainMarkers>."; + return process_string("There is an <EntryPoint> node inside a <MainMarkers>."); case VerificationNote::Code::UNEXPECTED_DURATION: - return "There is an <Duration> node inside a <MainMarkers>."; + return process_string("There is an <Duration> node inside a <MainMarkers>."); case VerificationNote::Code::INVALID_CONTENT_KIND: - return String::compose("<ContentKind> has an invalid value %1.", note.note().get()); + return compose("<ContentKind> has an invalid value %1.", note.note().get()); + case VerificationNote::Code::VALID_CONTENT_KIND: + return compose("Valid <ContentKind> %1.", note.note().get()); case VerificationNote::Code::INVALID_MAIN_PICTURE_ACTIVE_AREA: - return String::compose("<MainPictureActiveaArea> has an invalid value: %1", note.note().get()); + return compose("<MainPictureActiveaArea> has an invalid value: %1", note.note().get()); + case VerificationNote::Code::VALID_MAIN_PICTURE_ACTIVE_AREA: + return compose("<MainPictureActiveaArea> %1 is valid", note.note().get()); case VerificationNote::Code::DUPLICATE_ASSET_ID_IN_PKL: - return String::compose("The PKL %1 has more than one asset with the same ID.", note.note().get()); + return compose("The PKL %1 has more than one asset with the same ID.", note.note().get()); case VerificationNote::Code::DUPLICATE_ASSET_ID_IN_ASSETMAP: - return String::compose("The ASSETMAP %1 has more than one asset with the same ID.", note.note().get()); + return compose("The ASSETMAP %1 has more than one asset with the same ID.", note.note().get()); case VerificationNote::Code::MISSING_SUBTITLE: - return String::compose("The subtitle asset %1 has no subtitles.", note.note().get()); + return compose("The subtitle asset %1 has no subtitles.", note.note().get()); case VerificationNote::Code::INVALID_SUBTITLE_ISSUE_DATE: - return String::compose("<IssueDate> has an invalid value: %1", note.note().get()); + return compose("<IssueDate> has an invalid value: %1", note.note().get()); case VerificationNote::Code::MISMATCHED_SOUND_CHANNEL_COUNTS: - return String::compose("The sound assets do not all have the same channel count; the first to differ is %1", note.file()->filename()); + return compose("The sound assets do not all have the same channel count; the first to differ is %1", filename()); case VerificationNote::Code::INVALID_MAIN_SOUND_CONFIGURATION: - return String::compose("<MainSoundConfiguration> has an invalid value: %1", note.note().get()); + return compose("<MainSoundConfiguration> has an invalid value: %1", note.note().get()); case VerificationNote::Code::MISSING_FONT: - return String::compose("The font file for font ID \"%1\" was not found, or was not referred to in the ASSETMAP.", note.note().get()); + return compose("The font file for font ID \"%1\" was not found, or was not referred to in the ASSETMAP.", note.note().get()); case VerificationNote::Code::INVALID_JPEG2000_TILE_PART_SIZE: - return String::compose( + return compose( "Frame %1 has an image component that is too large (component %2 is %3 bytes in size).", note.frame().get(), note.component().get(), note.size().get() ); case VerificationNote::Code::INCORRECT_SUBTITLE_NAMESPACE_COUNT: - return String::compose("The XML in the subtitle asset %1 has more than one namespace declaration.", note.note().get()); + return compose("The XML in the subtitle asset %1 has more than one namespace declaration.", note.note().get()); case VerificationNote::Code::MISSING_LOAD_FONT_FOR_FONT: - return String::compose("A subtitle or closed caption refers to a font with ID %1 that does not have a corresponding <LoadFont> node", note.id().get()); + return compose("A subtitle or closed caption refers to a font with ID %1 that does not have a corresponding <LoadFont> node", note.id().get()); case VerificationNote::Code::MISSING_LOAD_FONT: - return String::compose("The SMPTE subtitle asset %1 has <Text> nodes but no <LoadFont> node", note.id().get()); + return compose("The SMPTE subtitle asset %1 has <Text> nodes but no <LoadFont> node", note.id().get()); case VerificationNote::Code::MISMATCHED_ASSET_MAP_ID: - return String::compose("The asset with ID %1 in the asset map actually has an id of %2", note.id().get(), note.other_id().get()); + return compose("The asset with ID %1 in the asset map actually has an id of %2", note.id().get(), note.other_id().get()); case VerificationNote::Code::EMPTY_CONTENT_VERSION_LABEL_TEXT: - return String::compose("The <LabelText> in a <ContentVersion> in CPL %1 is empty", note.id().get()); + return compose("The <LabelText> in a <ContentVersion> in CPL %1 is empty", note.cpl_id().get()); + case VerificationNote::Code::VALID_CONTENT_VERSION_LABEL_TEXT: + return compose("CPL has valid <ContentVersion> %1", note.note().get()); case VerificationNote::Code::INVALID_CPL_NAMESPACE: - return String::compose("The namespace %1 in CPL %2 is invalid", note.note().get(), note.file()->filename()); + return compose("The namespace %1 in CPL %2 is invalid", note.note().get(), note.cpl_id().get()); case VerificationNote::Code::MISSING_CPL_CONTENT_VERSION: - return String::compose("The CPL %1 has no <ContentVersion> tag", note.note().get()); + return compose("The CPL %1 has no <ContentVersion> tag", note.cpl_id().get()); } return ""; @@ -2202,12 +2227,20 @@ dcp::operator== (dcp::VerificationNote const& a, dcp::VerificationNote const& b) a.id() == b.id() && a.other_id() == b.other_id() && a.frame_rate() == b.frame_rate() && + a.cpl_id() == b.cpl_id() && a.reference_hash() == b.reference_hash() && a.calculated_hash() == b.calculated_hash(); } bool +dcp::operator!=(dcp::VerificationNote const& a, dcp::VerificationNote const& b) +{ + return !(a == b); +} + + +bool dcp::operator< (dcp::VerificationNote const& a, dcp::VerificationNote const& b) { if (a.type() != b.type()) { diff --git a/src/verify.h b/src/verify.h index b5d913bd..b9e3623f 100644 --- a/src/verify.h +++ b/src/verify.h @@ -58,16 +58,20 @@ namespace dcp { +class DCP; + + class VerificationNote { public: enum class Type { + OK, ERROR, BV21_ERROR, ///< may not always be considered an error, but violates a "shall" requirement of Bv2.1 WARNING }; - /** Codes for errors or warnings from verifying DCPs. + /** Codes for successful checks, errors or warnings from verifying DCPs. * * The names should (in general) answer the question "what is wrong?" with an answer that begins "There is a ..." * e.g. "There is a INCORRECT_CPL_HASH" @@ -98,6 +102,7 @@ public: * note contains (probably technical) details */ FAILED_READ, + MATCHING_CPL_HASHES, /** The hash of the CPL in the PKL does not agree with the CPL file * note contains CPL ID * file contains CPL filename @@ -109,6 +114,7 @@ public: * note contains the invalid frame rate as "<numerator>/<denominator>" */ INVALID_PICTURE_FRAME_RATE, + CORRECT_PICTURE_HASH, /** The hash of a main picture asset does not agree with the PKL file * file contains the picture asset filename * calculated_hash contains the current hash of the picture MXF @@ -153,6 +159,7 @@ public: * note contains asset ID */ INVALID_DURATION, + VALID_PICTURE_FRAME_SIZES_IN_BYTES, /** The JPEG2000 data in at least one picture frame is larger than the equivalent of 250Mbit/s * file contains the picture asset filename */ @@ -175,6 +182,7 @@ public: * note contains the invalid language */ INVALID_LANGUAGE, + VALID_RELEASE_TERRITORY, /** A picture asset does not have one of the required Bv2.1 sizes (in pixels) [Bv2.1_7.1] * note contains the incorrect size as "<width>x<height>" * file contains the asset filename @@ -257,6 +265,7 @@ public: * file contains the CPL filename */ MISMATCHED_CPL_ANNOTATION_TEXT, + VALID_CPL_ANNOTATION_TEXT, /** At least one asset in a reel does not have the same duration as the others */ MISMATCHED_ASSET_DURATION, /** If one reel has a _MainSubtitle_, all must have them */ @@ -334,6 +343,11 @@ public: * file contains the PKL filename */ MISMATCHED_PKL_ANNOTATION_TEXT_WITH_CPL, + MATCHING_PKL_ANNOTATION_TEXT_WITH_CPL, + /** All content is encrypted */ + ALL_ENCRYPTED, + /** No content is encrypted */ + NONE_ENCRYPTED, /** Some, but not all content, is encrypted */ PARTIALLY_ENCRYPTED, /** General error during JPEG2000 codestream verification @@ -406,11 +420,13 @@ public: UNEXPECTED_DURATION, /** A <ContentKind> has been specified with either no scope or the SMPTE 429-7 scope, but which is not one of those allowed */ INVALID_CONTENT_KIND, + VALID_CONTENT_KIND, /** Either the width or height of a <MainPictureActiveArea> in a CPL is either not an even number, or bigger than the corresponding asset dimension. * note contains details of what is wrong * file contains the CPL filename */ INVALID_MAIN_PICTURE_ACTIVE_AREA, + VALID_MAIN_PICTURE_ACTIVE_AREA, /** A PKL has more than one asset with the same ID * note contains the PKL ID * file contains the PKL filename @@ -475,13 +491,13 @@ public: * file contains the CPL filename */ EMPTY_CONTENT_VERSION_LABEL_TEXT, + VALID_CONTENT_VERSION_LABEL_TEXT, /** The CPL namespace is not valid. * note contains the invalid namespace * file contains the CPL filename */ INVALID_CPL_NAMESPACE, /** A SMPTE CPL does not contain a _<ContentVersion>_ tag - * note contains the CPL ID * file contains the CPL filename */ MISSING_CPL_CONTENT_VERSION @@ -542,6 +558,7 @@ private: ID, OTHER_ID, FRAME_RATE, + CPL_ID, CALCULATED_HASH, REFERENCE_HASH }; @@ -641,6 +658,15 @@ public: return data<std::string>(Data::REFERENCE_HASH); } + VerificationNote& set_cpl_id(std::string id) { + _data[Data::CPL_ID] = id; + return *this; + } + + boost::optional<std::string> cpl_id() const { + return data<std::string>(Data::CPL_ID); + } + private: Type _type; Code _code; @@ -658,18 +684,30 @@ struct VerificationOptions }; -std::vector<VerificationNote> verify ( +struct VerificationResult +{ + std::vector<VerificationNote> notes; + std::vector<std::shared_ptr<dcp::DCP>> dcps; +}; + + +VerificationResult verify( std::vector<boost::filesystem::path> directories, std::vector<dcp::DecryptedKDM> kdms, - boost::function<void (std::string, boost::optional<boost::filesystem::path>)> stage, - boost::function<void (float)> progress, + std::function<void (std::string, boost::optional<boost::filesystem::path>)> stage, + std::function<void (float)> progress, VerificationOptions options = {}, boost::optional<boost::filesystem::path> xsd_dtd_directory = boost::optional<boost::filesystem::path>() ); -std::string note_to_string (dcp::VerificationNote note); +std::string note_to_string( + dcp::VerificationNote note, + std::function<std::string (std::string)> process_string = [](std::string s) { return s; }, + std::function<std::string (std::string)> process_filename = [](std::string s) { return s; } + ); bool operator== (dcp::VerificationNote const& a, dcp::VerificationNote const& b); +bool operator!=(dcp::VerificationNote const& a, dcp::VerificationNote const& b); bool operator< (dcp::VerificationNote const& a, dcp::VerificationNote const& b); std::ostream& operator<<(std::ostream& s, dcp::VerificationNote const& note); diff --git a/src/verify_j2k.h b/src/verify_j2k.h index 58c8f4b7..dbfc488b 100644 --- a/src/verify_j2k.h +++ b/src/verify_j2k.h @@ -37,8 +37,8 @@ */ -#ifndef LIBDCP_VERIFY_J2K -#define LIBDCP_VERIFY_J2K +#ifndef LIBDCP_VERIFY_J2K_H +#define LIBDCP_VERIFY_J2K_H #include "verify.h" diff --git a/src/verify_report.cc b/src/verify_report.cc new file mode 100644 index 00000000..2201b8fa --- /dev/null +++ b/src/verify_report.cc @@ -0,0 +1,144 @@ +/* + Copyright (C) 2018-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. +*/ + + +#include "compose.hpp" +#include "cpl.h" +#include "dcp.h" +#include "file.h" +#include "reel.h" +#include "reel_picture_asset.h" +#include "reel_sound_asset.h" +#include "reel_subtitle_asset.h" +#include "verify.h" +#include "verify_report.h" + + +using std::shared_ptr; +using std::string; +using std::vector; +using boost::optional; +using namespace dcp; + + +void write_line(File& file, string format) +{ + file.puts(string(format + "\n").c_str()); +} + + +template <typename... Args> +void write_line(File& file, string format, Args... args) +{ + file.puts(String::compose(format + "\n", std::forward<Args>(args)...).c_str()); +} + + +void +dcp::verify_report(dcp::VerificationResult const& result, Formatter& formatter) +{ + auto document = formatter.document(); + auto body = formatter.body(); + + formatter.heading("DCP verification report"); + + if (result.dcps.size() > 1) { + formatter.subheading("DCPs"); + } else { + formatter.subheading("DCP"); + } + + auto reel_asset_details = [&formatter](shared_ptr<dcp::ReelAsset> asset) { + formatter.list_item(String::compose("UUID: %1", asset->id())); + formatter.list_item(String::compose("Intrinsic duration: %1", asset->intrinsic_duration())); + formatter.list_item(String::compose("Entry point: %1", asset->entry_point().get_value_or(0))); + formatter.list_item(String::compose("Duration: %1", asset->duration().get_value_or(0))); + if (asset->annotation_text()) { + formatter.list_item(String::compose("Annotation text: %1", *asset->annotation_text())); + } + }; + + auto write_notes = [&formatter](dcp::VerificationResult const& result, optional<string> cpl_id) { + for (auto note: result.notes) { + if (note.cpl_id() == cpl_id) { + auto const note_as_string = dcp::note_to_string(note, formatter.process_string(), formatter.process_filename()); + if (note.type() == dcp::VerificationNote::Type::OK) { + formatter.list_item(note_as_string, string("ok")); + } else if (note.type() == dcp::VerificationNote::Type::WARNING) { + formatter.list_item(note_as_string, string("warning")); + } else if (note.type() == dcp::VerificationNote::Type::ERROR) { + formatter.list_item(note_as_string, string("error")); + } + } + } + }; + + for (auto dcp: result.dcps) { + auto ul = formatter.unordered_list(); + for (auto cpl: dcp->cpls()) { + formatter.list_item(String::compose("CPL ID: %1", cpl->id())); + int reel_index = 1; + for (auto reel: cpl->reels()) { + formatter.list_item(String::compose("Reel: %1", reel_index++)); + auto ul2 = formatter.unordered_list(); + if (auto pic = reel->main_picture()) { + formatter.list_item("Main picture"); + auto ul3 = formatter.unordered_list(); + reel_asset_details(pic); + formatter.list_item(String::compose("Frame rate: %1", pic->frame_rate().numerator)); + formatter.list_item(String::compose("Screen aspect ratio: %1x%2", pic->screen_aspect_ratio().numerator, pic->screen_aspect_ratio().denominator)); + } + if (auto sound = reel->main_sound()) { + formatter.list_item("Main sound"); + auto ul3 = formatter.unordered_list(); + reel_asset_details(sound); + } + if (auto sub = reel->main_subtitle()) { + formatter.list_item("Main subtitle"); + auto ul3 = formatter.unordered_list(); + reel_asset_details(sub); + if (sub->language()) { + formatter.list_item(String::compose("Language: %1", *sub->language())); + } + } + } + write_notes(result, cpl->id()); + } + } + + if (std::count_if(result.notes.begin(), result.notes.end(), [](VerificationNote const& note) { return !note.cpl_id(); }) > 0) { + formatter.subheading("Report"); + write_notes(result, {}); + } +} + diff --git a/src/verify_report.h b/src/verify_report.h new file mode 100644 index 00000000..b8ca5516 --- /dev/null +++ b/src/verify_report.h @@ -0,0 +1,237 @@ +/* + Copyright (C) 2022 Carl Hetherington <cth@carlh.net> + + This file is part of DCP-o-matic. + + DCP-o-matic 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. + + DCP-o-matic 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 DCP-o-matic. If not, see <http://www.gnu.org/licenses/>. + +*/ + + +#include "compose.hpp" +#include "file.h" +#include "verify.h" +#include <boost/filesystem.hpp> +#include <vector> + + +namespace dcp { + + +class Formatter +{ +public: + Formatter(boost::filesystem::path file) + : _file(file, "w") + {} + + class Wrap + { + public: + Wrap() = default; + + Wrap(Formatter* formatter, std::string const& close) + : _formatter(formatter) + , _close(close) + {} + + Wrap(Formatter* formatter, std::string const& close, std::function<void ()> closer) + : _formatter(formatter) + , _close(close) + , _closer(closer) + {} + + Wrap(Wrap&& other) + { + std::swap(_formatter, other._formatter); + std::swap(_close, other._close); + std::swap(_closer, other._closer); + } + + ~Wrap() + { + if (_formatter) { + _formatter->file().puts(_close.c_str()); + } + if (_closer) { + _closer(); + } + } + + private: + Formatter* _formatter = nullptr; + std::string _close; + std::function<void ()> _closer = nullptr; + }; + + virtual Wrap document() { return {}; } + + virtual void heading(std::string const& text) = 0; + virtual void subheading(std::string const& text) = 0; + virtual Wrap body() { return {}; } + + virtual Wrap unordered_list() = 0; + virtual void list_item(std::string const& text, boost::optional<std::string> type = {}) = 0; + + virtual std::function<std::string (std::string)> process_string() = 0; + virtual std::function<std::string (std::string)> process_filename() = 0; + + dcp::File& file() { + return _file; + } + +protected: + dcp::File _file; +}; + + +class TextFormatter : public Formatter +{ +public: + TextFormatter(boost::filesystem::path file) + : Formatter(file) + {} + + void heading(std::string const& text) override { + print(text); + } + + void subheading(std::string const& text) override { + print(""); + print(text); + } + + Wrap unordered_list() override { + _indent++; + return Wrap(this, "", [this]() { _indent--; }); + } + + void list_item(std::string const& text, boost::optional<std::string> type = {}) override { + LIBDCP_UNUSED(type); + for (int i = 0; i < _indent * 2; ++i) { + _file.puts(" "); + } + _file.puts("* "); + print(text); + } + + std::function<std::string (std::string)> process_string() override { + return [](std::string s) { + return s; + }; + } + + std::function<std::string (std::string)> process_filename() override { + return [](std::string s) { + return s; + }; + } + +private: + void print(std::string const& text) { + _file.puts(text.c_str()); + _file.puts("\n"); + } + + int _indent = 0; +}; + + +class HTMLFormatter : public Formatter +{ +public: + HTMLFormatter(boost::filesystem::path file) + : Formatter(file) + {} + + void heading(std::string const& text) override { + tagged("h1", text); + } + + void subheading(std::string const& text) override { + tagged("h2", text); + } + + Wrap document() override { + auto html = wrapped("html"); + auto head = wrapped("head"); + auto style = wrapped("style"); + _file.puts("li {\n" + " margin: 2px;\n" + " padding: 2px 2px 2px 1em;\n" + "}\n" + ); + _file.puts("li.ok {\n" + " background-color: #00ff00;\n" + "}\n" + "li.warning {\n" + " background-color: #ffa500;\n" + "}\n" + "li.error {\n" + " background-color: #ff0000;\n" + "}\n" + "ul {\n" + " list-style: none;\n" + "}\n" + ); + return html; + } + + Wrap body() override { + return wrapped("body"); + } + + Wrap unordered_list() override { + return wrapped("ul"); + } + + void list_item(std::string const& text, boost::optional<std::string> type = {}) override { + if (type) { + _file.puts(dcp::String::compose("<li class=\"%1\">%2", *type, text).c_str()); + } else { + _file.puts(dcp::String::compose("<li>%1", text).c_str()); + } + } + + std::function<std::string (std::string)> process_string() override { + return [](std::string s) { + boost::replace_all(s, "<", "<"); + boost::replace_all(s, ">", ">"); + return s; + }; + } + + std::function<std::string (std::string)> process_filename() override { + return [](std::string s) { + return String::compose("<code>%1</code>", s); + }; + } + +private: + void tagged(std::string tag, std::string content) { + _file.puts(String::compose("<%1>%2</%3>\n", tag, content, tag).c_str()); + }; + + Wrap wrapped(std::string const& tag) { + _file.puts(String::compose("<%1>", tag).c_str()); + return Wrap(this, String::compose("</%1>", tag)); + }; +}; + + +extern void verify_report(dcp::VerificationResult const& result, Formatter& formatter); + + +} + diff --git a/src/wscript b/src/wscript index c2d499c8..1793aa72 100644 --- a/src/wscript +++ b/src/wscript @@ -36,14 +36,14 @@ def build(bld): source = """ array_data.cc asset.cc - asset_map.cc asset_factory.cc + asset_map.cc asset_writer.cc atmos_asset.cc atmos_asset_writer.cc bitstream.cc - certificate_chain.cc certificate.cc + certificate_chain.cc chromaticity.cc colour_conversion.cc combine.cc @@ -58,6 +58,7 @@ def build(bld): exceptions.cc file.cc filesystem.cc + ffmpeg_image.cc font_asset.cc fsk.cc gamma_transfer_function.cc @@ -65,6 +66,8 @@ def build(bld): identity_transfer_function.cc interop_load_font_node.cc interop_subtitle_asset.cc + j2k_picture_asset.cc + j2k_picture_asset_writer.cc j2k_transcode.cc key.cc language_tag.cc @@ -72,15 +75,20 @@ def build(bld): locale_convert.cc metadata.cc modified_gamma_transfer_function.cc - mono_picture_asset.cc - mono_picture_asset_writer.cc - mono_picture_frame.cc + mono_j2k_picture_asset.cc + mono_j2k_picture_asset_writer.cc + mono_j2k_picture_frame.cc + mono_mpeg2_picture_asset.cc + mono_mpeg2_picture_asset_writer.cc + mono_mpeg2_picture_frame.cc + mpeg2_picture_asset.cc + mpeg2_picture_asset_writer.cc + mpeg2_transcode.cc mxf.cc name_format.cc object.cc openjpeg_image.cc picture_asset.cc - picture_asset_writer.cc pkl.cc rating.cc raw_convert.cc @@ -91,9 +99,9 @@ def build(bld): reel_file_asset.cc reel_interop_closed_caption_asset.cc reel_interop_subtitle_asset.cc + reel_markers_asset.cc reel_mono_picture_asset.cc reel_picture_asset.cc - reel_markers_asset.cc reel_smpte_closed_caption_asset.cc reel_smpte_subtitle_asset.cc reel_sound_asset.cc @@ -109,9 +117,9 @@ def build(bld): sound_asset.cc sound_asset_writer.cc sound_frame.cc - stereo_picture_asset.cc - stereo_picture_asset_writer.cc - stereo_picture_frame.cc + stereo_j2k_picture_asset.cc + stereo_j2k_picture_asset_writer.cc + stereo_j2k_picture_frame.cc subtitle.cc subtitle_asset.cc subtitle_asset_internal.cc @@ -125,6 +133,7 @@ def build(bld): v_align.cc verify.cc verify_j2k.cc + verify_report.cc version.cc """ @@ -139,8 +148,9 @@ def build(bld): atmos_asset_reader.h atmos_asset_writer.h atmos_frame.h - certificate_chain.h + behaviour.h certificate.h + certificate_chain.h chromaticity.h colour_conversion.h combine.h @@ -161,12 +171,16 @@ def build(bld): filesystem.h font_asset.h frame.h + frame_info.h fsk.h gamma_transfer_function.h h_align.h identity_transfer_function.h interop_load_font_node.h interop_subtitle_asset.h + ffmpeg_image.h + j2k_picture_asset.h + j2k_picture_asset_writer.h j2k_transcode.h key.h language_tag.h @@ -174,22 +188,27 @@ def build(bld): local_time.h locale_convert.h metadata.h - mono_picture_asset.h - mono_picture_asset_reader.h - mono_picture_asset_writer.h - mono_picture_frame.h + mpeg2_picture_asset_writer.h modified_gamma_transfer_function.h + mono_j2k_picture_asset.h + mono_j2k_picture_asset_reader.h + mono_j2k_picture_asset_writer.h + mono_j2k_picture_frame.h + mono_mpeg2_picture_asset.h + mono_mpeg2_picture_asset_reader.h + mono_mpeg2_picture_asset_writer.h + mono_mpeg2_picture_frame.h + mpeg2_picture_asset.h + mpeg2_transcode.h mxf.h name_format.h object.h openjpeg_image.h picture_asset.h - picture_asset_writer.h piecewise_lut.h pkl.h rating.h raw_convert.h - rgb_xyz.h reel.h reel_asset.h reel_atmos_asset.h @@ -200,26 +219,27 @@ def build(bld): reel_markers_asset.h reel_mono_picture_asset.h reel_picture_asset.h - reel_sound_asset.h reel_smpte_closed_caption_asset.h reel_smpte_subtitle_asset.h + reel_sound_asset.h reel_stereo_picture_asset.h reel_subtitle_asset.h ref.h + rgb_xyz.h ruby.h s_gamut3_transfer_function.h scope_guard.h search.h smpte_load_font_node.h smpte_subtitle_asset.h - sound_frame.h sound_asset.h sound_asset_reader.h sound_asset_writer.h - stereo_picture_asset.h - stereo_picture_asset_reader.h - stereo_picture_asset_writer.h - stereo_picture_frame.h + sound_frame.h + stereo_j2k_picture_asset.h + stereo_j2k_picture_asset_reader.h + stereo_j2k_picture_asset_writer.h + stereo_j2k_picture_frame.h subtitle.h subtitle_asset.h subtitle_image.h @@ -232,6 +252,7 @@ def build(bld): v_align.h verify.h verify_j2k.h + verify_report.h version.h warnings.h """ @@ -244,7 +265,7 @@ def build(bld): obj.name = 'libdcp%s' % bld.env.API_VERSION obj.target = 'dcp%s' % bld.env.API_VERSION obj.export_includes = ['.'] - obj.uselib = 'BOOST_FILESYSTEM BOOST_SIGNALS2 BOOST_DATETIME OPENSSL SIGC++ LIBXML++ OPENJPEG CXML XMLSEC1 ASDCPLIB_CTH XERCES' + obj.uselib = 'BOOST_FILESYSTEM BOOST_SIGNALS2 BOOST_DATETIME OPENSSL SIGC++ LIBXML++ OPENJPEG CXML XMLSEC1 ASDCPLIB_DCPOMATIC XERCES AVCODEC AVUTIL' obj.source = source # Library for gcov @@ -256,7 +277,7 @@ def build(bld): obj.name = 'libdcp%s_gcov' % bld.env.API_VERSION obj.target = 'dcp%s_gcov' % bld.env.API_VERSION obj.export_includes = ['.'] - obj.uselib = 'BOOST_FILESYSTEM BOOST_SIGNALS2 BOOST_DATETIME OPENSSL SIGC++ LIBXML++ OPENJPEG CXML XMLSEC1 ASDCPLIB_CTH XERCES' + obj.uselib = 'BOOST_FILESYSTEM BOOST_SIGNALS2 BOOST_DATETIME OPENSSL SIGC++ LIBXML++ OPENJPEG CXML XMLSEC1 ASDCPLIB_DCPOMATIC XERCES AVCODEC AVUTIL' obj.use = 'libkumu-libdcp%s libasdcp-libdcp%s' % (bld.env.API_VERSION, bld.env.API_VERSION) obj.source = source obj.cppflags = ['-fprofile-arcs', '-ftest-coverage', '-fno-inline', '-fno-default-inline', '-fno-elide-constructors', '-g', '-O0'] diff --git a/test/combine_test.cc b/test/combine_test.cc index 12cbb6e2..a5f6a57f 100644 --- a/test/combine_test.cc +++ b/test/combine_test.cc @@ -87,10 +87,12 @@ check_no_errors (boost::filesystem::path path) { vector<boost::filesystem::path> directories; directories.push_back (path); - auto notes = dcp::verify(directories, {}, &stage, &progress, {}, xsd_test); + auto notes = dcp::verify(directories, {}, &stage, &progress, {}, xsd_test).notes; vector<dcp::VerificationNote> filtered_notes; std::copy_if (notes.begin(), notes.end(), std::back_inserter(filtered_notes), [](dcp::VerificationNote const& i) { - return i.code() != dcp::VerificationNote::Code::INVALID_STANDARD && i.code() != dcp::VerificationNote::Code::INVALID_SUBTITLE_DURATION; + return i.type() != dcp::VerificationNote::Type::OK && + i.code() != dcp::VerificationNote::Code::INVALID_STANDARD && + i.code() != dcp::VerificationNote::Code::INVALID_SUBTITLE_DURATION; }); dump_notes (filtered_notes); BOOST_CHECK (filtered_notes.empty()); @@ -475,10 +477,12 @@ BOOST_AUTO_TEST_CASE(combine_multi_reel_subtitles) check_combined({in}, out); - auto notes = dcp::verify({out}, {}, &stage, &progress, {}, xsd_test); + auto notes = dcp::verify({out}, {}, &stage, &progress, {}, xsd_test).notes; vector<dcp::VerificationNote> filtered_notes; std::copy_if(notes.begin(), notes.end(), std::back_inserter(filtered_notes), [](dcp::VerificationNote const& i) { - return i.code() != dcp::VerificationNote::Code::INVALID_STANDARD && i.code() != dcp::VerificationNote::Code::MISMATCHED_PKL_ANNOTATION_TEXT_WITH_CPL; + return i.type() != dcp::VerificationNote::Type::OK && + i.code() != dcp::VerificationNote::Code::INVALID_STANDARD && + i.code() != dcp::VerificationNote::Code::MISMATCHED_PKL_ANNOTATION_TEXT_WITH_CPL; }); dump_notes(filtered_notes); BOOST_CHECK(filtered_notes.empty()); diff --git a/test/cpl_sar_test.cc b/test/cpl_sar_test.cc index 0ecc87e0..ab1ef182 100644 --- a/test/cpl_sar_test.cc +++ b/test/cpl_sar_test.cc @@ -33,7 +33,7 @@ #include "cpl.h" -#include "mono_picture_asset.h" +#include "mono_j2k_picture_asset.h" #include "reel_mono_picture_asset.h" #include "warnings.h" #include <libcxml/cxml.h> @@ -68,7 +68,7 @@ check (shared_ptr<dcp::ReelMonoPictureAsset> pa, dcp::Fraction frac, string sar) BOOST_AUTO_TEST_CASE (cpl_sar) { auto pa = make_shared<dcp::ReelMonoPictureAsset>( - make_shared<dcp::MonoPictureAsset>("test/ref/DCP/dcp_test1/video.mxf"), 0 + make_shared<dcp::MonoJ2KPictureAsset>("test/ref/DCP/dcp_test1/video.mxf"), 0 ); /* Easy ones */ diff --git a/test/data/text_formatter.txt b/test/data/text_formatter.txt new file mode 100644 index 00000000..8861db99 --- /dev/null +++ b/test/data/text_formatter.txt @@ -0,0 +1,8 @@ +Heading + +Subheading + * Foo + * Bar + * Fred + * Jim + * Sheila diff --git a/test/data/text_formatter_windows.txt b/test/data/text_formatter_windows.txt new file mode 100644 index 00000000..16d78d84 --- /dev/null +++ b/test/data/text_formatter_windows.txt @@ -0,0 +1,8 @@ +Heading
+
+Subheading
+ * Foo
+ * Bar
+ * Fred
+ * Jim
+ * Sheila
diff --git a/test/dcp_test.cc b/test/dcp_test.cc index 5456092c..63de990f 100644 --- a/test/dcp_test.cc +++ b/test/dcp_test.cc @@ -38,8 +38,8 @@ #include "dcp.h" #include "equality_options.h" #include "metadata.h" -#include "mono_picture_asset.h" -#include "picture_asset_writer.h" +#include "mono_j2k_picture_asset.h" +#include "j2k_picture_asset_writer.h" #include "reel.h" #include "reel_atmos_asset.h" #include "reel_markers_asset.h" @@ -49,7 +49,7 @@ #include "reel_stereo_picture_asset.h" #include "sound_asset.h" #include "sound_asset_writer.h" -#include "stereo_picture_asset.h" +#include "stereo_j2k_picture_asset.h" #include "test.h" #include <asdcp/KM_util.h> #include <sndfile.h> @@ -105,9 +105,9 @@ BOOST_AUTO_TEST_CASE (dcp_test2) cpl->set_issue_date ("2012-07-17T04:45:18+00:00"); cpl->set_annotation_text ("A Test DCP"); - auto mp = make_shared<dcp::StereoPictureAsset>(dcp::Fraction (24, 1), dcp::Standard::SMPTE); + auto mp = make_shared<dcp::StereoJ2KPictureAsset>(dcp::Fraction (24, 1), dcp::Standard::SMPTE); mp->set_metadata (mxf_meta); - auto picture_writer = mp->start_write("build/test/DCP/dcp_test2/video.mxf", dcp::PictureAsset::Behaviour::MAKE_NEW); + auto picture_writer = mp->start_write("build/test/DCP/dcp_test2/video.mxf", dcp::Behaviour::MAKE_NEW); dcp::ArrayData j2c ("test/data/flat_red.j2c"); for (int i = 0; i < 24; ++i) { /* Left */ @@ -203,10 +203,10 @@ test_rewriting_sound(string name, bool modify) dcp::DCP B ("build/test/" + name); auto reel = make_shared<dcp::Reel>(); - BOOST_REQUIRE (A_picture->mono_asset()); - BOOST_REQUIRE (A_picture->mono_asset()->file()); - copy_file (A_picture->mono_asset()->file().get(), path("build") / "test" / name / picture); - reel->add(make_shared<dcp::ReelMonoPictureAsset>(make_shared<dcp::MonoPictureAsset>(path("build") / "test" / name / picture), 0)); + BOOST_REQUIRE(A_picture->mono_j2k_asset()); + BOOST_REQUIRE(A_picture->mono_j2k_asset()->file()); + copy_file(A_picture->mono_j2k_asset()->file().get(), path("build") / "test" / name / picture); + reel->add(make_shared<dcp::ReelMonoPictureAsset>(make_shared<dcp::MonoJ2KPictureAsset>(path("build") / "test" / name / picture), 0)); auto reader = A_sound->asset()->start_read(); auto sound = make_shared<dcp::SoundAsset>(A_sound->asset()->edit_rate(), A_sound->asset()->sampling_rate(), A_sound->asset()->channels(), dcp::LanguageTag("en-US"), dcp::Standard::SMPTE); @@ -290,9 +290,9 @@ BOOST_AUTO_TEST_CASE (dcp_test5) cpl->set_issue_date ("2012-07-17T04:45:18+00:00"); cpl->set_annotation_text ("A Test DCP"); - auto mp = make_shared<dcp::MonoPictureAsset>(dcp::Fraction (24, 1), dcp::Standard::SMPTE); + auto mp = make_shared<dcp::MonoJ2KPictureAsset>(dcp::Fraction (24, 1), dcp::Standard::SMPTE); mp->set_metadata (mxf_meta); - auto picture_writer = mp->start_write("build/test/DCP/dcp_test5/video.mxf", dcp::PictureAsset::Behaviour::MAKE_NEW); + auto picture_writer = mp->start_write("build/test/DCP/dcp_test5/video.mxf", dcp::Behaviour::MAKE_NEW); dcp::ArrayData j2c ("test/data/flat_red.j2c"); for (int i = 0; i < 24; ++i) { picture_writer->write (j2c.data (), j2c.size ()); @@ -510,8 +510,8 @@ BOOST_AUTO_TEST_CASE(hashes_preserved_when_loading_corrupted_dcp) auto dcp = make_simple(dir / "1"); dcp->write_xml(); - auto asset_1_id = dcp::MonoPictureAsset(dir / "1" / "video.mxf").id(); - auto asset_1_hash = dcp::MonoPictureAsset(dir / "1" / "video.mxf").hash(); + auto asset_1_id = dcp::MonoJ2KPictureAsset(dir / "1" / "video.mxf").id(); + auto asset_1_hash = dcp::MonoJ2KPictureAsset(dir / "1" / "video.mxf").hash(); /* Replace the hash in the CPL (the one that corresponds to the actual file) * with an incorrect one new_hash. diff --git a/test/decryption_test.cc b/test/decryption_test.cc index 8f3dbff7..1aadc461 100644 --- a/test/decryption_test.cc +++ b/test/decryption_test.cc @@ -36,11 +36,11 @@ #include "dcp.h" #include "decrypted_kdm.h" #include "encrypted_kdm.h" -#include "mono_picture_asset.h" -#include "mono_picture_asset_reader.h" -#include "mono_picture_frame.h" +#include "mono_j2k_picture_asset.h" +#include "mono_j2k_picture_asset_reader.h" +#include "mono_j2k_picture_frame.h" #include "openjpeg_image.h" -#include "picture_asset_writer.h" +#include "j2k_picture_asset_writer.h" #include "reel.h" #include "reel_file_asset.h" #include "reel_mono_picture_asset.h" @@ -80,7 +80,7 @@ get_frame (dcp::DCP const & dcp) auto picture = reel->main_picture()->asset(); BOOST_CHECK (picture); - auto mono_picture = dynamic_pointer_cast<const dcp::MonoPictureAsset>(picture); + auto mono_picture = dynamic_pointer_cast<const dcp::MonoJ2KPictureAsset>(picture); auto reader = mono_picture->start_read(); auto j2k_frame = reader->get_frame(0); auto xyz = j2k_frame->xyz_image(); @@ -152,10 +152,10 @@ BOOST_AUTO_TEST_CASE (decryption_test2) auto context_id = dcp::make_uuid(); dcp::Key key; - auto picture_asset = std::make_shared<dcp::MonoPictureAsset>(dcp::Fraction(24, 1), dcp::Standard::SMPTE); + auto picture_asset = std::make_shared<dcp::MonoJ2KPictureAsset>(dcp::Fraction(24, 1), dcp::Standard::SMPTE); picture_asset->set_key (key); picture_asset->set_context_id (context_id); - auto picture_writer = picture_asset->start_write(dir / "picture.mxf", dcp::PictureAsset::Behaviour::MAKE_NEW); + auto picture_writer = picture_asset->start_write(dir / "picture.mxf", dcp::Behaviour::MAKE_NEW); dcp::ArrayData picture("test/data/flat_red.j2c"); for (int i = 0; i < 24; ++i) { picture_writer->write(picture); diff --git a/test/encryption_test.cc b/test/encryption_test.cc index 358a3fd9..a927a5df 100644 --- a/test/encryption_test.cc +++ b/test/encryption_test.cc @@ -37,8 +37,8 @@ #include "certificate_chain.h" #include "cpl.h" #include "filesystem.h" -#include "mono_picture_asset.h" -#include "picture_asset_writer.h" +#include "mono_j2k_picture_asset.h" +#include "j2k_picture_asset_writer.h" #include "sound_asset_writer.h" #include "sound_asset.h" #include "reel.h" @@ -90,11 +90,11 @@ BOOST_AUTO_TEST_CASE (encryption_test) dcp::Key key; - auto mp = make_shared<dcp::MonoPictureAsset>(dcp::Fraction (24, 1), dcp::Standard::SMPTE); + auto mp = make_shared<dcp::MonoJ2KPictureAsset>(dcp::Fraction (24, 1), dcp::Standard::SMPTE); mp->set_metadata (mxf_metadata); mp->set_key (key); - auto writer = mp->start_write("build/test/DCP/encryption_test/video.mxf", dcp::PictureAsset::Behaviour::MAKE_NEW); + auto writer = mp->start_write("build/test/DCP/encryption_test/video.mxf", dcp::Behaviour::MAKE_NEW); dcp::ArrayData j2c ("test/data/flat_red.j2c"); for (int i = 0; i < 24; ++i) { writer->write (j2c.data (), j2c.size ()); diff --git a/test/frame_info_hash_test.cc b/test/frame_info_hash_test.cc index c19172c6..331eabf1 100644 --- a/test/frame_info_hash_test.cc +++ b/test/frame_info_hash_test.cc @@ -33,8 +33,8 @@ #include "j2k_transcode.h" -#include "mono_picture_asset.h" -#include "mono_picture_asset_writer.h" +#include "mono_j2k_picture_asset.h" +#include "mono_j2k_picture_asset_writer.h" #include "openjpeg_image.h" #include <boost/random.hpp> #include <boost/test/unit_test.hpp> @@ -47,7 +47,7 @@ using std::string; static void -check (shared_ptr<dcp::PictureAssetWriter> writer, boost::random::uniform_int_distribution<>& dist, boost::random::mt19937& rng, string hash) +check (shared_ptr<dcp::J2KPictureAssetWriter> writer, boost::random::uniform_int_distribution<>& dist, boost::random::mt19937& rng, string hash) { auto xyz = make_shared<dcp::OpenJPEGImage>(dcp::Size(1998, 1080)); for (int c = 0; c < 3; ++c) { @@ -66,14 +66,14 @@ check (shared_ptr<dcp::PictureAssetWriter> writer, boost::random::uniform_int_di /** Test the hashing of data written to JPEG2000 MXFs with some random inputs */ BOOST_AUTO_TEST_CASE (frame_info_hash_test) { - auto mp = make_shared<dcp::MonoPictureAsset>(dcp::Fraction (24, 1), dcp::Standard::SMPTE); - auto writer = mp->start_write("build/test/frame_info_hash_test.mxf", dcp::PictureAsset::Behaviour::MAKE_NEW); + auto mp = make_shared<dcp::MonoJ2KPictureAsset>(dcp::Fraction (24, 1), dcp::Standard::SMPTE); + auto writer = mp->start_write("build/test/frame_info_hash_test.mxf", dcp::Behaviour::MAKE_NEW); boost::random::mt19937 rng(1); boost::random::uniform_int_distribution<> dist(0, 4095); /* Check a few random frames */ - check(writer, dist, rng, "a9e772602a2fd3135d940cfd727ab8ff"); - check(writer, dist, rng, "b075369922e42b23e1852a586ec43224"); - check(writer, dist, rng, "402395e76152db05b03c8f24ddfd7732"); + check(writer, dist, rng, "8f3dc7321d6dff3d5691011de31fc713"); + check(writer, dist, rng, "a305b83a40367fda1b5cf0efa096fd18"); + check(writer, dist, rng, "1abc71e011ced46d9928a4b2d22e20f6"); } diff --git a/test/kdm_test.cc b/test/kdm_test.cc index 6f06b4c9..75a6019e 100644 --- a/test/kdm_test.cc +++ b/test/kdm_test.cc @@ -36,8 +36,8 @@ #include "cpl.h" #include "decrypted_kdm.h" #include "encrypted_kdm.h" -#include "mono_picture_asset.h" -#include "picture_asset_writer.h" +#include "mono_j2k_picture_asset.h" +#include "j2k_picture_asset_writer.h" #include "reel.h" #include "reel_mono_picture_asset.h" #include "reel_sound_asset.h" @@ -256,9 +256,9 @@ BOOST_AUTO_TEST_CASE (validity_period_test1) auto signer = make_shared<dcp::CertificateChain>(dcp::file_to_string("test/data/certificate_chain")); signer->set_key(dcp::file_to_string("test/data/private.key")); - auto asset = make_shared<dcp::MonoPictureAsset>(dcp::Fraction(24, 1), dcp::Standard::SMPTE); + auto asset = make_shared<dcp::MonoJ2KPictureAsset>(dcp::Fraction(24, 1), dcp::Standard::SMPTE); asset->set_key (dcp::Key()); - auto writer = asset->start_write("build/test/validity_period_test1.mxf", dcp::PictureAsset::Behaviour::MAKE_NEW); + auto writer = asset->start_write("build/test/validity_period_test1.mxf", dcp::Behaviour::MAKE_NEW); dcp::ArrayData frame ("test/data/flat_red.j2c"); writer->write (frame.data(), frame.size()); writer->finalize (); @@ -320,7 +320,7 @@ BOOST_AUTO_TEST_CASE (vf_kdm_test) ov->write_xml (); auto ov_reel = ov->cpls()[0]->reels()[0]; - auto ov_reel_picture = make_shared<dcp::ReelMonoPictureAsset>(dynamic_pointer_cast<dcp::ReelMonoPictureAsset>(ov_reel->main_picture())->mono_asset(), 0); + auto ov_reel_picture = make_shared<dcp::ReelMonoPictureAsset>(dynamic_pointer_cast<dcp::ReelMonoPictureAsset>(ov_reel->main_picture())->mono_j2k_asset(), 0); auto ov_reel_sound = make_shared<dcp::ReelSoundAsset>(ov_reel->main_sound()->asset(), 0); /* Make VF */ @@ -376,7 +376,7 @@ BOOST_AUTO_TEST_CASE (vf_kdm_test) BOOST_REQUIRE_EQUAL(reload_vf.cpls()[0]->reels().size(), 1U); BOOST_REQUIRE(reload_vf.cpls()[0]->reels()[0]->main_picture()); BOOST_REQUIRE(reload_vf.cpls()[0]->reels()[0]->main_picture()->asset()); - auto mono_asset = dynamic_pointer_cast<dcp::MonoPictureAsset>(reload_vf.cpls()[0]->reels()[0]->main_picture()->asset()); + auto mono_asset = dynamic_pointer_cast<dcp::MonoJ2KPictureAsset>(reload_vf.cpls()[0]->reels()[0]->main_picture()->asset()); BOOST_REQUIRE(mono_asset); auto reader = mono_asset->start_read(); reader->set_check_hmac(false); diff --git a/test/mca_test.cc b/test/mca_test.cc index bdfc9484..1cd95732 100644 --- a/test/mca_test.cc +++ b/test/mca_test.cc @@ -168,7 +168,8 @@ check_mca_descriptors(int suffix, vector<dcp::Channel> extra_active_channels, ve /* Check MXF */ - auto reader = new ASDCP::PCM::MXFReader(); + Kumu::FileReaderFactory factory; + auto reader = new ASDCP::PCM::MXFReader(factory); reader->OpenRead(boost::filesystem::path(dir / "mxf.mxf").string()); list<ASDCP::MXF::InterchangeObject*> channels; diff --git a/test/mono_mpeg2_picture_read_test.cc b/test/mono_mpeg2_picture_read_test.cc new file mode 100644 index 00000000..f0d4aac0 --- /dev/null +++ b/test/mono_mpeg2_picture_read_test.cc @@ -0,0 +1,62 @@ +/* + Copyright (C) 2023 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. +*/ + + +#include "mono_mpeg2_picture_asset.h" +#include "mpeg2_transcode.h" +#include "test.h" +#include <boost/test/unit_test.hpp> + +extern "C" { +#include <libavcodec/avcodec.h> +} + + +BOOST_AUTO_TEST_CASE(mpeg_mono_picture_read_test) +{ + dcp::MonoMPEG2PictureAsset asset(private_test / "data" / "mas" / "r2.mxf" ); + std::cout << "frame rate " << asset.frame_rate().numerator << "\n"; + std::cout << "duration " << asset.intrinsic_duration() << "\n"; + + auto reader = asset.start_read(); + + dcp::MPEG2Decompressor decompressor; + for (auto i = 0; i < asset.intrinsic_duration(); ++i) { + auto images = decompressor.decompress_frame(reader->get_frame(i)); + BOOST_CHECK_EQUAL(images.size(), i == 0 ? 0U : 1U); + } + + auto images = decompressor.flush(); + BOOST_CHECK_EQUAL(images.size(), 1U); +} + diff --git a/test/mono_mpeg2_picture_write_test.cc b/test/mono_mpeg2_picture_write_test.cc new file mode 100644 index 00000000..44ce5ae4 --- /dev/null +++ b/test/mono_mpeg2_picture_write_test.cc @@ -0,0 +1,101 @@ +/* + Copyright (C) 2023 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. +*/ + + +#include "mono_mpeg2_picture_asset.h" +#include "mpeg2_transcode.h" +#include "test.h" +#include <boost/test/unit_test.hpp> + +extern "C" { +#include <libavcodec/avcodec.h> +} + + +BOOST_AUTO_TEST_CASE(mpeg_mono_picture_write_test) +{ + boost::filesystem::path dir = "build/test/mpeg2_mono_picture_write_test"; + + boost::system::error_code ec; + boost::filesystem::remove_all(dir); + boost::filesystem::create_directories(dir); + + dcp::MonoMPEG2PictureAsset asset(dcp::Fraction{24, 1}); + auto writer = asset.start_write(dir / "test.mxf", dcp::Behaviour::MAKE_NEW); + + dcp::MPEG2Compressor compressor({1920, 1080}, 24, 50000000); + dcp::FFmpegImage image(int64_t(0)); + for (auto y = 0; y < 1080; ++y) { + uint8_t* py = image.y() + y * image.y_stride(); + for (auto x = 0; x < 1920; ++x) { + if (x < 640) { + *py++ = 76; + } else if (x < 1280) { + *py++ = 149; + } else { + *py++ = 29; + } + } + } + + for (auto y = 0; y < 540; ++y) { + uint8_t* pu = image.u() + y * image.u_stride(); + uint8_t* pv = image.v() + y * image.v_stride(); + for (auto x = 0; x < 960; ++x) { + if (x < 320) { + *pu++ = 84; + *pv++ = 255; + } else if (x < 640) { + *pu++ = 43; + *pv++ = 21; + } else { + *pu++ = 255; + *pv++ = 107; + } + } + } + + for (auto i = 0; i < 24; ++i) { + image.set_pts(int64_t(i)); + if (auto compressed = compressor.compress_frame(image)) { + writer->write(compressed->first->data(), compressed->first->size()); + } + } + + if (auto compressed = compressor.flush()) { + writer->write(compressed->first->data(), compressed->first->size()); + } + + writer->finalize(); +} + diff --git a/test/recovery_test.cc b/test/recovery_test.cc index eaf34f81..327c248a 100644 --- a/test/recovery_test.cc +++ b/test/recovery_test.cc @@ -32,8 +32,8 @@ */ -#include "mono_picture_asset_writer.h" -#include "mono_picture_asset.h" +#include "mono_j2k_picture_asset_writer.h" +#include "mono_j2k_picture_asset.h" #include "test.h" #include <asdcp/KM_util.h> #include <boost/filesystem.hpp> @@ -55,10 +55,10 @@ BOOST_AUTO_TEST_CASE (recovery) boost::filesystem::remove_all ("build/test/baz"); boost::filesystem::create_directories ("build/test/baz"); - auto mp = make_shared<dcp::MonoPictureAsset>(dcp::Fraction (24, 1), dcp::Standard::SMPTE); - auto writer = mp->start_write("build/test/baz/video1.mxf", dcp::PictureAsset::Behaviour::MAKE_NEW); + auto mp = make_shared<dcp::MonoJ2KPictureAsset>(dcp::Fraction (24, 1), dcp::Standard::SMPTE); + auto writer = mp->start_write("build/test/baz/video1.mxf", dcp::Behaviour::MAKE_NEW); - int written_size = 0; + uint64_t written_size = 0; for (int i = 0; i < 24; ++i) { auto info = writer->write (data.data(), data.size()); BOOST_CHECK_EQUAL (info.hash, "c3c9a3adec09baf2b0bcb65806fbeac8"); @@ -82,13 +82,13 @@ BOOST_AUTO_TEST_CASE (recovery) Kumu::ResetTestRNG (); - mp = make_shared<dcp::MonoPictureAsset>(dcp::Fraction (24, 1), dcp::Standard::SMPTE); - writer = mp->start_write("build/test/baz/video2.mxf", dcp::PictureAsset::Behaviour::OVERWRITE_EXISTING); + mp = make_shared<dcp::MonoJ2KPictureAsset>(dcp::Fraction (24, 1), dcp::Standard::SMPTE); + writer = mp->start_write("build/test/baz/video2.mxf", dcp::Behaviour::OVERWRITE_EXISTING); writer->write (data.data(), data.size()); for (int i = 1; i < 4; ++i) { - writer->fake_write (written_size); + writer->fake_write(dcp::J2KFrameInfo{0, written_size, "xxx"}); } for (int i = 4; i < 24; ++i) { diff --git a/test/ref/DCP/dcp_test1/ASSETMAP.xml b/test/ref/DCP/dcp_test1/ASSETMAP.xml index 4e0d1e5a..dc811f79 100644 --- a/test/ref/DCP/dcp_test1/ASSETMAP.xml +++ b/test/ref/DCP/dcp_test1/ASSETMAP.xml @@ -37,7 +37,7 @@ <Path>video.mxf</Path> <VolumeIndex>1</VolumeIndex> <Offset>0</Offset> - <Length>49240</Length> + <Length>31648</Length> </Chunk> </ChunkList> </Asset> diff --git a/test/ref/DCP/dcp_test1/audio.mxf b/test/ref/DCP/dcp_test1/audio.mxf Binary files differindex 5e08f74f..1f5f7987 100644 --- a/test/ref/DCP/dcp_test1/audio.mxf +++ b/test/ref/DCP/dcp_test1/audio.mxf diff --git a/test/ref/DCP/dcp_test1/cpl_6affb8ee-0020-4dff-a53c-17652f6358ab.xml b/test/ref/DCP/dcp_test1/cpl_6affb8ee-0020-4dff-a53c-17652f6358ab.xml index 15cda9a1..26d41070 100644 --- a/test/ref/DCP/dcp_test1/cpl_6affb8ee-0020-4dff-a53c-17652f6358ab.xml +++ b/test/ref/DCP/dcp_test1/cpl_6affb8ee-0020-4dff-a53c-17652f6358ab.xml @@ -37,7 +37,7 @@ <IntrinsicDuration>24</IntrinsicDuration> <EntryPoint>0</EntryPoint> <Duration>24</Duration> - <Hash>wUmt8G+cFFKMGt0ueS9+F1S4uhc=</Hash> + <Hash>vsVjRV9vhTBPUWfE/TT1o2vdQsI=</Hash> <FrameRate>24 1</FrameRate> <ScreenAspectRatio>1998 1080</ScreenAspectRatio> </MainPicture> @@ -47,7 +47,7 @@ <IntrinsicDuration>24</IntrinsicDuration> <EntryPoint>0</EntryPoint> <Duration>24</Duration> - <Hash>KcJb7S2K5cNm8RG4kfQD5FTeS0A=</Hash> + <Hash>3M7YTgvFKXXMEGLkIbV4miC90FE=</Hash> </MainSound> <meta:CompositionMetadataAsset xmlns:meta="http://www.smpte-ra.org/schemas/429-16/2014/CPL-Metadata"> <Id>urn:uuid:dd015243-ab77-435c-a13d-690566885121</Id> diff --git a/test/ref/DCP/dcp_test1/pkl_d199d58b-5ef8-4d49-b270-07e590ccb280.xml b/test/ref/DCP/dcp_test1/pkl_d199d58b-5ef8-4d49-b270-07e590ccb280.xml index cedbb15b..6a1392f3 100644 --- a/test/ref/DCP/dcp_test1/pkl_d199d58b-5ef8-4d49-b270-07e590ccb280.xml +++ b/test/ref/DCP/dcp_test1/pkl_d199d58b-5ef8-4d49-b270-07e590ccb280.xml @@ -9,7 +9,7 @@ <Asset> <Id>urn:uuid:6affb8ee-0020-4dff-a53c-17652f6358ab</Id> <AnnotationText>6affb8ee-0020-4dff-a53c-17652f6358ab</AnnotationText> - <Hash>tfX1mVIKJCVr1m7Y32Nzxf0+Rpw=</Hash> + <Hash>skI+5b/9LA/y6h0mcyxysJYanxI=</Hash> <Size>8559</Size> <Type>text/xml</Type> <OriginalFileName>cpl_6affb8ee-0020-4dff-a53c-17652f6358ab.xml</OriginalFileName> @@ -17,15 +17,15 @@ <Asset> <Id>urn:uuid:5407b210-4441-4e97-8b16-8bdc7c12da54</Id> <AnnotationText>5407b210-4441-4e97-8b16-8bdc7c12da54</AnnotationText> - <Hash>wUmt8G+cFFKMGt0ueS9+F1S4uhc=</Hash> - <Size>49240</Size> + <Hash>vsVjRV9vhTBPUWfE/TT1o2vdQsI=</Hash> + <Size>31648</Size> <Type>application/mxf</Type> <OriginalFileName>video.mxf</OriginalFileName> </Asset> <Asset> <Id>urn:uuid:97f0f352-5b77-48ee-a558-9df37717f4fa</Id> <AnnotationText>97f0f352-5b77-48ee-a558-9df37717f4fa</AnnotationText> - <Hash>KcJb7S2K5cNm8RG4kfQD5FTeS0A=</Hash> + <Hash>3M7YTgvFKXXMEGLkIbV4miC90FE=</Hash> <Size>881326</Size> <Type>application/mxf</Type> <OriginalFileName>audio.mxf</OriginalFileName> diff --git a/test/ref/DCP/dcp_test1/video.mxf b/test/ref/DCP/dcp_test1/video.mxf Binary files differindex 27ba41cf..cd0e43af 100644 --- a/test/ref/DCP/dcp_test1/video.mxf +++ b/test/ref/DCP/dcp_test1/video.mxf diff --git a/test/ref/DCP/dcp_test2/audio.mxf b/test/ref/DCP/dcp_test2/audio.mxf Binary files differindex e8bd446f..6a95baf3 100644 --- a/test/ref/DCP/dcp_test2/audio.mxf +++ b/test/ref/DCP/dcp_test2/audio.mxf diff --git a/test/ref/DCP/dcp_test2/cpl_6affb8ee-0020-4dff-a53c-17652f6358ab.xml b/test/ref/DCP/dcp_test2/cpl_6affb8ee-0020-4dff-a53c-17652f6358ab.xml index 52a7779a..0e233325 100644 --- a/test/ref/DCP/dcp_test2/cpl_6affb8ee-0020-4dff-a53c-17652f6358ab.xml +++ b/test/ref/DCP/dcp_test2/cpl_6affb8ee-0020-4dff-a53c-17652f6358ab.xml @@ -22,7 +22,7 @@ <IntrinsicDuration>24</IntrinsicDuration> <EntryPoint>0</EntryPoint> <Duration>24</Duration> - <Hash>NzWhEbTccUIh1sSXIbU+8POVui0=</Hash> + <Hash>X+4lOvviLTQeP28hyzhCOGjraiE=</Hash> </MainSound> <msp-cpl:MainStereoscopicPicture xmlns:msp-cpl="http://www.smpte-ra.org/schemas/429-10/2008/Main-Stereo-Picture-CPL"> <Id>urn:uuid:5407b210-4441-4e97-8b16-8bdc7c12da54</Id> @@ -30,7 +30,7 @@ <IntrinsicDuration>24</IntrinsicDuration> <EntryPoint>0</EntryPoint> <Duration>24</Duration> - <Hash>BUpeFS6X9j+X0z2TZWSsLMT4pE0=</Hash> + <Hash>dU+tcR6bZ5BQ/TXKtBY03ejjUmc=</Hash> <FrameRate>48 1</FrameRate> <ScreenAspectRatio>1998 1080</ScreenAspectRatio> </msp-cpl:MainStereoscopicPicture> diff --git a/test/ref/DCP/dcp_test2/pkl_8577c7c0-be29-4eb5-a449-1e3870a42bbd.xml b/test/ref/DCP/dcp_test2/pkl_8577c7c0-be29-4eb5-a449-1e3870a42bbd.xml index 061e8a6a..794cfb85 100644 --- a/test/ref/DCP/dcp_test2/pkl_8577c7c0-be29-4eb5-a449-1e3870a42bbd.xml +++ b/test/ref/DCP/dcp_test2/pkl_8577c7c0-be29-4eb5-a449-1e3870a42bbd.xml @@ -9,7 +9,7 @@ <Asset> <Id>urn:uuid:6affb8ee-0020-4dff-a53c-17652f6358ab</Id> <AnnotationText>6affb8ee-0020-4dff-a53c-17652f6358ab</AnnotationText> - <Hash>zaAcrAcv0oOzOa3etTP6xDfzQko=</Hash> + <Hash>2jp5+JDLOh5CqzQ8goZTzBEWsPo=</Hash> <Size>1686</Size> <Type>text/xml</Type> <OriginalFileName>cpl_6affb8ee-0020-4dff-a53c-17652f6358ab.xml</OriginalFileName> @@ -17,7 +17,7 @@ <Asset> <Id>urn:uuid:5407b210-4441-4e97-8b16-8bdc7c12da54</Id> <AnnotationText>5407b210-4441-4e97-8b16-8bdc7c12da54</AnnotationText> - <Hash>BUpeFS6X9j+X0z2TZWSsLMT4pE0=</Hash> + <Hash>dU+tcR6bZ5BQ/TXKtBY03ejjUmc=</Hash> <Size>63160</Size> <Type>application/mxf</Type> <OriginalFileName>video.mxf</OriginalFileName> @@ -25,7 +25,7 @@ <Asset> <Id>urn:uuid:fc843acc-1ad9-4808-b9ed-33f5319e047d</Id> <AnnotationText>fc843acc-1ad9-4808-b9ed-33f5319e047d</AnnotationText> - <Hash>NzWhEbTccUIh1sSXIbU+8POVui0=</Hash> + <Hash>X+4lOvviLTQeP28hyzhCOGjraiE=</Hash> <Size>161326</Size> <Type>application/mxf</Type> <OriginalFileName>audio.mxf</OriginalFileName> diff --git a/test/ref/DCP/dcp_test2/video.mxf b/test/ref/DCP/dcp_test2/video.mxf Binary files differindex 3b670708..6200297c 100644 --- a/test/ref/DCP/dcp_test2/video.mxf +++ b/test/ref/DCP/dcp_test2/video.mxf diff --git a/test/ref/DCP/dcp_test5/audio.mxf b/test/ref/DCP/dcp_test5/audio.mxf Binary files differindex 947865e1..fc55b975 100644 --- a/test/ref/DCP/dcp_test5/audio.mxf +++ b/test/ref/DCP/dcp_test5/audio.mxf diff --git a/test/ref/DCP/dcp_test5/cpl_6affb8ee-0020-4dff-a53c-17652f6358ab.xml b/test/ref/DCP/dcp_test5/cpl_6affb8ee-0020-4dff-a53c-17652f6358ab.xml index 5fa3643b..0dbd0198 100644 --- a/test/ref/DCP/dcp_test5/cpl_6affb8ee-0020-4dff-a53c-17652f6358ab.xml +++ b/test/ref/DCP/dcp_test5/cpl_6affb8ee-0020-4dff-a53c-17652f6358ab.xml @@ -22,7 +22,7 @@ <IntrinsicDuration>24</IntrinsicDuration> <EntryPoint>0</EntryPoint> <Duration>24</Duration> - <Hash>o3VmpdFsNUgF11oadcaGJ/IfO0M=</Hash> + <Hash>w7xIWU3Q+QnEQMq7BcOhsWO+OU0=</Hash> <FrameRate>24 1</FrameRate> <ScreenAspectRatio>1998 1080</ScreenAspectRatio> </MainPicture> @@ -32,7 +32,7 @@ <IntrinsicDuration>24</IntrinsicDuration> <EntryPoint>0</EntryPoint> <Duration>24</Duration> - <Hash>l+XLgxe2fMZDgY+0QYzDfGhvTQM=</Hash> + <Hash>5ANfi1KoyGiZ8hQIhDHm5f/OvQY=</Hash> </MainSound> <axd:AuxData xmlns:axd="http://www.dolby.com/schemas/2012/AD"> <Id>urn:uuid:b68febcc-5ddf-489a-84a7-924f29fa2afd</Id> diff --git a/test/ref/DCP/dcp_test5/pkl_d76fdaaf-8316-42dc-a87e-1719ad6ca3ca.xml b/test/ref/DCP/dcp_test5/pkl_d76fdaaf-8316-42dc-a87e-1719ad6ca3ca.xml index 49fd579b..e9b1e802 100644 --- a/test/ref/DCP/dcp_test5/pkl_d76fdaaf-8316-42dc-a87e-1719ad6ca3ca.xml +++ b/test/ref/DCP/dcp_test5/pkl_d76fdaaf-8316-42dc-a87e-1719ad6ca3ca.xml @@ -9,7 +9,7 @@ <Asset> <Id>urn:uuid:6affb8ee-0020-4dff-a53c-17652f6358ab</Id> <AnnotationText>6affb8ee-0020-4dff-a53c-17652f6358ab</AnnotationText> - <Hash>eqEIVxSAbJL+SROmqrDHbr9sb+A=</Hash> + <Hash>4zHV3/AhGYo5WjNmyuxeRSPOVwk=</Hash> <Size>2024</Size> <Type>text/xml</Type> <OriginalFileName>cpl_6affb8ee-0020-4dff-a53c-17652f6358ab.xml</OriginalFileName> @@ -17,7 +17,7 @@ <Asset> <Id>urn:uuid:5407b210-4441-4e97-8b16-8bdc7c12da54</Id> <AnnotationText>5407b210-4441-4e97-8b16-8bdc7c12da54</AnnotationText> - <Hash>o3VmpdFsNUgF11oadcaGJ/IfO0M=</Hash> + <Hash>w7xIWU3Q+QnEQMq7BcOhsWO+OU0=</Hash> <Size>40144</Size> <Type>application/mxf</Type> <OriginalFileName>video.mxf</OriginalFileName> @@ -25,7 +25,7 @@ <Asset> <Id>urn:uuid:97f0f352-5b77-48ee-a558-9df37717f4fa</Id> <AnnotationText>97f0f352-5b77-48ee-a558-9df37717f4fa</AnnotationText> - <Hash>l+XLgxe2fMZDgY+0QYzDfGhvTQM=</Hash> + <Hash>5ANfi1KoyGiZ8hQIhDHm5f/OvQY=</Hash> <Size>161326</Size> <Type>application/mxf</Type> <OriginalFileName>audio.mxf</OriginalFileName> diff --git a/test/ref/DCP/dcp_test5/video.mxf b/test/ref/DCP/dcp_test5/video.mxf Binary files differindex f3e7588b..3ec65a51 100644 --- a/test/ref/DCP/dcp_test5/video.mxf +++ b/test/ref/DCP/dcp_test5/video.mxf diff --git a/test/ref/DCP/dcp_test7/ASSETMAP b/test/ref/DCP/dcp_test7/ASSETMAP index 86a6ff6a..e52b4b11 100644 --- a/test/ref/DCP/dcp_test7/ASSETMAP +++ b/test/ref/DCP/dcp_test7/ASSETMAP @@ -37,7 +37,7 @@ <Path>video.mxf</Path> <VolumeIndex>1</VolumeIndex> <Offset>0</Offset> - <Length>49240</Length> + <Length>31648</Length> </Chunk> </ChunkList> </Asset> diff --git a/test/ref/DCP/dcp_test7/audio.mxf b/test/ref/DCP/dcp_test7/audio.mxf Binary files differindex 5e08f74f..1f5f7987 100644 --- a/test/ref/DCP/dcp_test7/audio.mxf +++ b/test/ref/DCP/dcp_test7/audio.mxf diff --git a/test/ref/DCP/dcp_test7/cpl_6affb8ee-0020-4dff-a53c-17652f6358ab.xml b/test/ref/DCP/dcp_test7/cpl_6affb8ee-0020-4dff-a53c-17652f6358ab.xml index b73ef84c..13aecd8a 100644 --- a/test/ref/DCP/dcp_test7/cpl_6affb8ee-0020-4dff-a53c-17652f6358ab.xml +++ b/test/ref/DCP/dcp_test7/cpl_6affb8ee-0020-4dff-a53c-17652f6358ab.xml @@ -37,7 +37,7 @@ <IntrinsicDuration>24</IntrinsicDuration> <EntryPoint>0</EntryPoint> <Duration>24</Duration> - <Hash>wUmt8G+cFFKMGt0ueS9+F1S4uhc=</Hash> + <Hash>vsVjRV9vhTBPUWfE/TT1o2vdQsI=</Hash> <FrameRate>24 1</FrameRate> <ScreenAspectRatio>1.85</ScreenAspectRatio> </MainPicture> @@ -47,7 +47,7 @@ <IntrinsicDuration>24</IntrinsicDuration> <EntryPoint>0</EntryPoint> <Duration>24</Duration> - <Hash>KcJb7S2K5cNm8RG4kfQD5FTeS0A=</Hash> + <Hash>3M7YTgvFKXXMEGLkIbV4miC90FE=</Hash> </MainSound> </AssetList> </Reel> diff --git a/test/ref/DCP/dcp_test7/pkl_d199d58b-5ef8-4d49-b270-07e590ccb280.xml b/test/ref/DCP/dcp_test7/pkl_d199d58b-5ef8-4d49-b270-07e590ccb280.xml index 4180eb8d..9d251109 100644 --- a/test/ref/DCP/dcp_test7/pkl_d199d58b-5ef8-4d49-b270-07e590ccb280.xml +++ b/test/ref/DCP/dcp_test7/pkl_d199d58b-5ef8-4d49-b270-07e590ccb280.xml @@ -9,7 +9,7 @@ <Asset> <Id>urn:uuid:6affb8ee-0020-4dff-a53c-17652f6358ab</Id> <AnnotationText>6affb8ee-0020-4dff-a53c-17652f6358ab</AnnotationText> - <Hash>Vsre14v3AhK6X2gUzeYF9G8GKo0=</Hash> + <Hash>SgCEvehTaji0MCWOJTVioOcNndY=</Hash> <Size>1965</Size> <Type>text/xml;asdcpKind=CPL</Type> <OriginalFileName>cpl_6affb8ee-0020-4dff-a53c-17652f6358ab.xml</OriginalFileName> @@ -17,15 +17,15 @@ <Asset> <Id>urn:uuid:5407b210-4441-4e97-8b16-8bdc7c12da54</Id> <AnnotationText>5407b210-4441-4e97-8b16-8bdc7c12da54</AnnotationText> - <Hash>wUmt8G+cFFKMGt0ueS9+F1S4uhc=</Hash> - <Size>49240</Size> + <Hash>vsVjRV9vhTBPUWfE/TT1o2vdQsI=</Hash> + <Size>31648</Size> <Type>application/x-smpte-mxf;asdcpKind=Picture</Type> <OriginalFileName>video.mxf</OriginalFileName> </Asset> <Asset> <Id>urn:uuid:97f0f352-5b77-48ee-a558-9df37717f4fa</Id> <AnnotationText>97f0f352-5b77-48ee-a558-9df37717f4fa</AnnotationText> - <Hash>KcJb7S2K5cNm8RG4kfQD5FTeS0A=</Hash> + <Hash>3M7YTgvFKXXMEGLkIbV4miC90FE=</Hash> <Size>881326</Size> <Type>application/x-smpte-mxf;asdcpKind=Sound</Type> <OriginalFileName>audio.mxf</OriginalFileName> diff --git a/test/ref/DCP/dcp_test7/video.mxf b/test/ref/DCP/dcp_test7/video.mxf Binary files differindex 27ba41cf..cd0e43af 100644 --- a/test/ref/DCP/dcp_test7/video.mxf +++ b/test/ref/DCP/dcp_test7/video.mxf diff --git a/test/ref/DCP/encryption_test/audio.mxf b/test/ref/DCP/encryption_test/audio.mxf Binary files differindex a4f89ae9..505b7f13 100644 --- a/test/ref/DCP/encryption_test/audio.mxf +++ b/test/ref/DCP/encryption_test/audio.mxf diff --git a/test/ref/DCP/encryption_test/cpl_6affb8ee-0020-4dff-a53c-17652f6358ab.xml b/test/ref/DCP/encryption_test/cpl_6affb8ee-0020-4dff-a53c-17652f6358ab.xml index e8e24467..6203c884 100644 --- a/test/ref/DCP/encryption_test/cpl_6affb8ee-0020-4dff-a53c-17652f6358ab.xml +++ b/test/ref/DCP/encryption_test/cpl_6affb8ee-0020-4dff-a53c-17652f6358ab.xml @@ -23,7 +23,7 @@ <EntryPoint>0</EntryPoint> <Duration>24</Duration> <KeyId>urn:uuid:ac8d301c-e5d9-4343-b6f3-ba2668adbe56</KeyId> - <Hash>AVtX8wz76uG2Uw0Qbc9+DKnHUfw=</Hash> + <Hash>Q0GMntXvEqVQsiv+YkfhMrXdw0w=</Hash> <FrameRate>24 1</FrameRate> <ScreenAspectRatio>1998 1080</ScreenAspectRatio> </MainPicture> @@ -34,7 +34,7 @@ <EntryPoint>0</EntryPoint> <Duration>24</Duration> <KeyId>urn:uuid:7ab6c77b-6648-44b9-8549-a5290ada6238</KeyId> - <Hash>3o02UpYqrl6w0NQbFvrB2tKxaIk=</Hash> + <Hash>v0SCdh2xnKNzGC7Sk5pS2q5Q/wQ=</Hash> </MainSound> </AssetList> </Reel> @@ -57,15 +57,15 @@ <dsig:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/> </dsig:Transforms> <dsig:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/> - <dsig:DigestValue>X3YkODMLyEJsAz5v3S/uT+xDD7c=</dsig:DigestValue> + <dsig:DigestValue>DBBENxFb5Qv377fb86n0KgUQ7go=</dsig:DigestValue> </dsig:Reference> </dsig:SignedInfo> - <dsig:SignatureValue>jH02vGxlTTi9T94KCIfMkafmdO0wluP9cvh+u5HyYoZo/D7O1Ki202t5uHRp0wcu -OfBFuu1Hw3Ooy/VEa2l4l/UDfE7lI+D79e0VfA8HwFB+c21GF7Q2FRV5ddu9ODgz -rk6kC5fZSLxc7cpK6jPNnT285O3nHfjk6MQ0fgGfNbvMefgeEWVKj8qcyFgN6H4e -wr1omcjbdw/HVQaOW84//pBRSRdMJGe1u2iUE2RwxBLzP4BepzkMv9Asm6uDo1YG -X1d60g2aCUdxAg9fjyTaOss1nRp1YdPlb3SHUhKXgTT/eAPkmmr7bs3jJtp2BN9U -4Izz2GR/3Uyf0NP20mZgNQ==</dsig:SignatureValue> + <dsig:SignatureValue>rhF1QacGUhxm1jfcgyBjAo2BmD4QxBl7c6Bzt8rjXXhR+NVT4ITeyxi39+94WwBx +Yp8iQhF46bE2QGZAr+HnZZJGVG3ajeaQ4uvPp2KRC8sHpBlZ2rGGtyjXAflYjepw +c1cLvRoAKbxOMW+ESxjyYOKgT0Bke0GhbzZ8+w+hGZLHxK9oTokIG9YDnRNUKN5W +n6avbxuaT8wtZgVwD4qfKzMeJlYW3yDFo/2apQA5K+oCj/gfrS1habYqBoNo9kO1 +fWkQ7LIeRrYuzNlr5K4e/LyIm7SHwb9EGcA7K8WbDrHKvaJmTs/T01QhU9f+huk2 +ZGJMMCGHhiwcbVtqVhgJSQ==</dsig:SignatureValue> <dsig:KeyInfo> <dsig:X509Data> <dsig:X509IssuerSerial> diff --git a/test/ref/DCP/encryption_test/pkl_5203f3d4-9d62-4062-8bf1-7a114eff99df.xml b/test/ref/DCP/encryption_test/pkl_5203f3d4-9d62-4062-8bf1-7a114eff99df.xml index 4d49b259..fb0313ab 100644 --- a/test/ref/DCP/encryption_test/pkl_5203f3d4-9d62-4062-8bf1-7a114eff99df.xml +++ b/test/ref/DCP/encryption_test/pkl_5203f3d4-9d62-4062-8bf1-7a114eff99df.xml @@ -9,7 +9,7 @@ <Asset> <Id>urn:uuid:6affb8ee-0020-4dff-a53c-17652f6358ab</Id> <AnnotationText>6affb8ee-0020-4dff-a53c-17652f6358ab</AnnotationText> - <Hash>Zd9zaFAGx/4W2B3vSZkAqTzcDZ0=</Hash> + <Hash>sLWF9OcgdkvBUH1VcNiwseLA3sA=</Hash> <Size>9226</Size> <Type>text/xml</Type> <OriginalFileName>cpl_6affb8ee-0020-4dff-a53c-17652f6358ab.xml</OriginalFileName> @@ -17,7 +17,7 @@ <Asset> <Id>urn:uuid:9a7fbb03-4078-4944-90b1-0d8a21c9d793</Id> <AnnotationText>9a7fbb03-4078-4944-90b1-0d8a21c9d793</AnnotationText> - <Hash>AVtX8wz76uG2Uw0Qbc9+DKnHUfw=</Hash> + <Hash>Q0GMntXvEqVQsiv+YkfhMrXdw0w=</Hash> <Size>44008</Size> <Type>application/mxf</Type> <OriginalFileName>video.mxf</OriginalFileName> @@ -25,7 +25,7 @@ <Asset> <Id>urn:uuid:ce300880-a425-40a6-adac-eb1e3f5643fc</Id> <AnnotationText>ce300880-a425-40a6-adac-eb1e3f5643fc</AnnotationText> - <Hash>3o02UpYqrl6w0NQbFvrB2tKxaIk=</Hash> + <Hash>v0SCdh2xnKNzGC7Sk5pS2q5Q/wQ=</Hash> <Size>165454</Size> <Type>application/mxf</Type> <OriginalFileName>audio.mxf</OriginalFileName> @@ -49,15 +49,15 @@ <dsig:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/> </dsig:Transforms> <dsig:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/> - <dsig:DigestValue>0F1ZXfoRDPIQHlRG0MC1uXkahG8=</dsig:DigestValue> + <dsig:DigestValue>OU1UpJxBmpxK8SE5yfe8aJsqoIw=</dsig:DigestValue> </dsig:Reference> </dsig:SignedInfo> - <dsig:SignatureValue>u98MLscN/Fn3YxGFMw6kP7fIWUj05FPxQvIYvsIpP68UP3FX9rdP6nh3Olyv194d -A1fQJu4ZamTcYN2O2E5vdGjxxHlPNXfFPckWc9Sy1i3V8u+EFdOggjMY4snvPot6 -kMyzAICuKzTJs4l+O/BzJAJSFSxrEtBCYEFxrtyKntDxSUE7ePSqPwcim6+5BzDO -QOsIp3wjJHlp7u4QqU3knIiaM7SibIXqtIBF7TOrChWZjFImKePNib9/DXvnE2WL -Y+5wJjGhImQR+2U2DDpSCLkl0kVgDrYfKcNg4lXUDZGEKo0Yqz0+1Rszz/DqsC+J -6mYxqMMvRXeh9pViSpgKaA==</dsig:SignatureValue> + <dsig:SignatureValue>ypg6tt5iJi7YEUJQfEQmZeigXzcm2xn/J59tqqYBqwmAch1ia//Khyo6m7/HpQJl +2Y5swUI8MtmzdV3gj3urd8WuQsDWFLflIHHiIanYE/XCdFZ8lV/nvyCkAaf/AH2V +W7bWD8/fYXJD2G9a2tRiWTjzfgpsTk+ox+C12Y/SebWAQ97sSLIMwwmQIYK2kTnh +nbKSuhk+v+V2ObpYl48zXu3za3Yt77rCdcGgPwYr/NUI47ascpnueNw48clDtjp+ +775jeA5ZG5lBR3VGTLwvoOXT3CH/FgG0DPSLmYbRnOzEShVUt7xFgbxMgNTYOGQJ +3ipS6qmHgWHjX3/fo6isgg==</dsig:SignatureValue> <dsig:KeyInfo> <dsig:X509Data> <dsig:X509IssuerSerial> diff --git a/test/ref/DCP/encryption_test/video.mxf b/test/ref/DCP/encryption_test/video.mxf Binary files differindex 2fbfacdd..e6ddbadb 100644 --- a/test/ref/DCP/encryption_test/video.mxf +++ b/test/ref/DCP/encryption_test/video.mxf diff --git a/test/round_trip_test.cc b/test/round_trip_test.cc index 028accbe..ffba5cbd 100644 --- a/test/round_trip_test.cc +++ b/test/round_trip_test.cc @@ -35,15 +35,15 @@ #include "decrypted_kdm.h" #include "encrypted_kdm.h" #include "certificate_chain.h" -#include "mono_picture_asset.h" +#include "mono_j2k_picture_asset.h" #include "sound_asset.h" #include "reel.h" #include "test.h" #include "cpl.h" -#include "mono_picture_frame.h" +#include "mono_j2k_picture_frame.h" #include "certificate_chain.h" -#include "mono_picture_asset_writer.h" -#include "mono_picture_asset_reader.h" +#include "mono_j2k_picture_asset_writer.h" +#include "mono_j2k_picture_asset_reader.h" #include "reel_picture_asset.h" #include "reel_mono_picture_asset.h" #include "openjpeg_image.h" @@ -70,8 +70,8 @@ BOOST_AUTO_TEST_CASE (round_trip_test) boost::filesystem::path work_dir = "build/test/round_trip_test"; boost::filesystem::create_directory (work_dir); - auto asset_A = make_shared<dcp::MonoPictureAsset>(dcp::Fraction (24, 1), dcp::Standard::SMPTE); - auto writer = asset_A->start_write(work_dir / "video.mxf", dcp::PictureAsset::Behaviour::MAKE_NEW); + auto asset_A = make_shared<dcp::MonoJ2KPictureAsset>(dcp::Fraction (24, 1), dcp::Standard::SMPTE); + auto writer = asset_A->start_write(work_dir / "video.mxf", dcp::Behaviour::MAKE_NEW); dcp::ArrayData j2c ("test/data/flat_red.j2c"); for (int i = 0; i < 24; ++i) { writer->write (j2c.data (), j2c.size ()); @@ -123,7 +123,7 @@ BOOST_AUTO_TEST_CASE (round_trip_test) } /* Reload the picture asset */ - auto asset_B = make_shared<dcp::MonoPictureAsset>(work_dir / "video.mxf"); + auto asset_B = make_shared<dcp::MonoJ2KPictureAsset>(work_dir / "video.mxf"); BOOST_CHECK (!kdm_B.keys().empty ()); asset_B->set_key (kdm_B.keys().front().key()); diff --git a/test/shared_subtitle_test.cc b/test/shared_subtitle_test.cc index 7ac20e10..22ee7177 100644 --- a/test/shared_subtitle_test.cc +++ b/test/shared_subtitle_test.cc @@ -175,13 +175,13 @@ BOOST_AUTO_TEST_CASE (format_xml_test1) { xmlpp::Document doc; auto root = doc.create_root_node("Foo"); - root->add_child("Empty"); - root->add_child("Text")->add_child_text("Hello world"); - root->add_child("Font")->add_child("Text")->add_child_text("Say what"); - auto fred = root->add_child("Text")->add_child("Font"); + cxml::add_child(root, "Empty"); + cxml::add_text_child(root, "Text", "Hello world"); + cxml::add_text_child(cxml::add_child(root, "Font"), "Text", "Say what"); + auto fred = cxml::add_child(cxml::add_child(root, "Text"), "Font"); fred->set_attribute("bob", "job"); fred->add_child_text("Fred"); - fred->add_child("Text")->add_child_text("Jim"); + cxml::add_text_child(fred, "Text", "Jim"); fred->add_child_text("Sheila"); BOOST_REQUIRE_EQUAL (dcp::SubtitleAsset::format_xml(doc, make_pair(string{}, string{"fred"})), "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" @@ -210,7 +210,7 @@ BOOST_AUTO_TEST_CASE (format_xml_entities_test) { xmlpp::Document doc; auto root = doc.create_root_node("Foo"); - root->add_child("Bar")->add_child_text("Don't panic & xml \"is\" 'great' & < > —"); + cxml::add_text_child(root, "Bar", "Don't panic & xml \"is\" 'great' & < > —"); BOOST_REQUIRE_EQUAL(dcp::SubtitleAsset::format_xml(doc, {}), "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" "<Foo>\n" diff --git a/test/stream_operators.cc b/test/stream_operators.cc index 0ed0d05f..348abd2f 100644 --- a/test/stream_operators.cc +++ b/test/stream_operators.cc @@ -214,6 +214,9 @@ ostream& dcp::operator<< (ostream& s, VerificationNote::Type t) { switch (t) { + case VerificationNote::Type::OK: + s << "check"; + break; case VerificationNote::Type::ERROR: s << "error"; break; diff --git a/test/test.cc b/test/test.cc index d384bdfa..51a631d2 100644 --- a/test/test.cc +++ b/test/test.cc @@ -39,11 +39,11 @@ #include "interop_subtitle_asset.h" #include "file.h" #include "j2k_transcode.h" -#include "mono_picture_asset.h" -#include "mono_picture_asset.h" +#include "mono_j2k_picture_asset.h" +#include "mono_j2k_picture_asset.h" #include "openjpeg_image.h" -#include "picture_asset_writer.h" -#include "picture_asset_writer.h" +#include "j2k_picture_asset_writer.h" +#include "j2k_picture_asset_writer.h" #include "reel.h" #include "reel_asset.h" #include "reel_interop_closed_caption_asset.h" @@ -122,7 +122,7 @@ check_xml (xmlpp::Element* ref, xmlpp::Element* test, vector<string> ignore_tags BOOST_CHECK_EQUAL (ref->get_name (), test->get_name ()); BOOST_CHECK_EQUAL (ref->get_namespace_prefix (), test->get_namespace_prefix ()); - if (find(ignore_tags.begin(), ignore_tags.end(), ref->get_name()) != ignore_tags.end()) { + if (find(ignore_tags.begin(), ignore_tags.end(), std::string(ref->get_name())) != ignore_tags.end()) { return; } @@ -260,18 +260,18 @@ check_file (boost::filesystem::path ref, boost::filesystem::path check) RNGFixer::RNGFixer () { - Kumu::cth_test = true; + Kumu::dcpomatic_test = true; Kumu::FortunaRNG().Reset(); } RNGFixer::~RNGFixer () { - Kumu::cth_test = false; + Kumu::dcpomatic_test = false; } -shared_ptr<dcp::MonoPictureAsset> +shared_ptr<dcp::MonoJ2KPictureAsset> simple_picture (boost::filesystem::path path, string suffix, int frames, optional<dcp::Key> key) { dcp::MXFMetadata mxf_meta; @@ -279,12 +279,12 @@ simple_picture (boost::filesystem::path path, string suffix, int frames, optiona mxf_meta.product_name = "OpenDCP"; mxf_meta.product_version = "0.0.25"; - auto mp = make_shared<dcp::MonoPictureAsset>(dcp::Fraction (24, 1), dcp::Standard::SMPTE); + auto mp = make_shared<dcp::MonoJ2KPictureAsset>(dcp::Fraction (24, 1), dcp::Standard::SMPTE); mp->set_metadata (mxf_meta); if (key) { mp->set_key (*key); } - auto picture_writer = mp->start_write(path / dcp::String::compose("video%1.mxf", suffix), dcp::PictureAsset::Behaviour::MAKE_NEW); + auto picture_writer = mp->start_write(path / dcp::String::compose("video%1.mxf", suffix), dcp::Behaviour::MAKE_NEW); dcp::Size const size (1998, 1080); auto image = make_shared<dcp::OpenJPEGImage>(size); @@ -530,10 +530,10 @@ black_picture_asset (boost::filesystem::path dir, int frames) auto frame = dcp::compress_j2k (image, 100000000, 24, false, false); BOOST_REQUIRE (frame.size() < 230000000 / (24 * 8)); - auto asset = make_shared<dcp::MonoPictureAsset>(dcp::Fraction(24, 1), dcp::Standard::SMPTE); + auto asset = make_shared<dcp::MonoJ2KPictureAsset>(dcp::Fraction(24, 1), dcp::Standard::SMPTE); asset->set_metadata (dcp::MXFMetadata("libdcp", "libdcp", "1.6.4devel")); boost::filesystem::create_directories (dir); - auto writer = asset->start_write(dir / "pic.mxf", dcp::PictureAsset::Behaviour::MAKE_NEW); + auto writer = asset->start_write(dir / "pic.mxf", dcp::Behaviour::MAKE_NEW); for (int i = 0; i < frames; ++i) { writer->write (frame.data(), frame.size()); } @@ -567,7 +567,11 @@ Editor::Editor(boost::filesystem::path path) Editor::~Editor() { - auto f = fopen(_path.string().c_str(), "w"); + /* Open this binary so that text files are re-written with Unix endings even on Windows, + * so that if we subsequently get hashes of edited files the hashes are the same on + * all platforms. + */ + auto f = fopen(_path.string().c_str(), "wb"); BOOST_REQUIRE (f); fwrite (_content.c_str(), _content.length(), 1, f); fclose (f); diff --git a/test/test.h b/test/test.h index bacb9311..d3fded87 100644 --- a/test/test.h +++ b/test/test.h @@ -34,7 +34,7 @@ namespace xmlpp { namespace dcp { class DCP; - class MonoPictureAsset; + class MonoJ2KPictureAsset; class SoundAsset; } @@ -44,7 +44,7 @@ extern boost::filesystem::path xsd_test; extern void check_xml (xmlpp::Element* ref, xmlpp::Element* test, std::vector<std::string> ignore_tags, bool ignore_whitespace = false); extern void check_xml (std::string ref, std::string test, std::vector<std::string> ignore, bool ignore_whitespace = false); extern void check_file (boost::filesystem::path ref, boost::filesystem::path check); -extern std::shared_ptr<dcp::MonoPictureAsset> simple_picture ( +extern std::shared_ptr<dcp::MonoJ2KPictureAsset> simple_picture ( boost::filesystem::path path, std::string suffix, int frames = 24, diff --git a/test/verify_report_test.cc b/test/verify_report_test.cc new file mode 100644 index 00000000..6b288891 --- /dev/null +++ b/test/verify_report_test.cc @@ -0,0 +1,67 @@ +/* + Copyright (C) 2022 Carl Hetherington <cth@carlh.net> + + This file is part of DCP-o-matic. + + DCP-o-matic 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. + + DCP-o-matic 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 DCP-o-matic. If not, see <http://www.gnu.org/licenses/>. + +*/ + + +#include "verify.h" +#include "verify_report.h" +#include <boost/test/unit_test.hpp> +#include "test.h" + + +BOOST_AUTO_TEST_CASE(verify_report_basically_ok) +{ + dcp::HTMLFormatter formatter("build/test/verify_report_basically_ok.html"); + dcp::verify_report( + dcp::verify( + { private_test / "TONEPLATES-SMPTE-PLAINTEXT_TST_F_XX-XX_ITL-TD_51-XX_2K_WOE_20111001_WOE_OV" }, + {}, + [](std::string, boost::optional<boost::filesystem::path>) {}, + [](float) {}, + {}, + xsd_test + ), + formatter + ); +} + + +BOOST_AUTO_TEST_CASE(text_formatter) +{ + { + dcp::TextFormatter fmt("build/test/text_formatter.txt"); + + fmt.heading("Heading"); + fmt.subheading("Subheading"); + auto A = fmt.unordered_list(); + fmt.list_item("Foo"); + fmt.list_item("Bar"); + auto B = fmt.unordered_list(); + fmt.list_item("Fred"); + fmt.list_item("Jim"); + fmt.list_item("Sheila"); + } + +#ifdef LIBDCP_WINDOWS + check_file("test/data/text_formatter_windows.txt", "build/test/text_formatter.txt"); +#else + check_file("test/data/text_formatter.txt", "build/test/text_formatter.txt"); +#endif +} + diff --git a/test/verify_test.cc b/test/verify_test.cc index 5a9489fe..80d0490e 100644 --- a/test/verify_test.cc +++ b/test/verify_test.cc @@ -38,8 +38,8 @@ #include "file.h" #include "interop_subtitle_asset.h" #include "j2k_transcode.h" -#include "mono_picture_asset.h" -#include "mono_picture_asset_writer.h" +#include "mono_j2k_picture_asset.h" +#include "mono_j2k_picture_asset_writer.h" #include "openjpeg_image.h" #include "raw_convert.h" #include "reel.h" @@ -52,7 +52,7 @@ #include "reel_smpte_closed_caption_asset.h" #include "reel_smpte_subtitle_asset.h" #include "smpte_subtitle_asset.h" -#include "stereo_picture_asset.h" +#include "stereo_j2k_picture_asset.h" #include "stream_operators.h" #include "test.h" #include "util.h" @@ -63,6 +63,7 @@ #include <boost/test/unit_test.hpp> #include <cstdio> #include <iostream> +#include <tuple> using std::list; @@ -148,6 +149,43 @@ prepare_directory (path path) } +static +path +find_prefix(path dir, string prefix) +{ + auto iter = std::find_if(directory_iterator(dir), directory_iterator(), [prefix](path const& p) { + return boost::starts_with(p.filename().string(), prefix); + }); + + BOOST_REQUIRE(iter != directory_iterator()); + return iter->path(); +} + + +static +path +find_cpl(path dir) +{ + return find_prefix(dir, "cpl_"); +} + + +static +path +find_pkl(path dir) +{ + return find_prefix(dir, "pkl_"); +} + + +static +path +find_asset_map(path dir) +{ + return find_prefix(dir, "ASSETMAP"); +} + + /** Copy dcp_test{reference_number} to build/test/verify_test{verify_test_suffix} * to make a new sacrificial test DCP. */ @@ -196,53 +234,85 @@ LIBDCP_ENABLE_WARNINGS static +string +to_string(dcp::VerificationNote const& note) +{ + string s = note_to_string(note) + dcp::String::compose( + "\n [%1 %2 %3 %4 %5 %6 ", + static_cast<int>(note.type()), + static_cast<int>(note.code()), + note.note().get_value_or("<none>"), + note.file().get_value_or("<none>"), + note.line().get_value_or(0), + note.frame().get_value_or(0) + ); + + s += dcp::String::compose( + "%1 %2 %3 %4 %5]\n", + note.id().get_value_or("<none>"), + note.other_id().get_value_or("<none>"), + note.cpl_id().get_value_or("<none>"), + note.reference_hash().get_value_or("<none>"), + note.calculated_hash().get_value_or("<none>") + ); + + return s; +} + + +static void -check_verify_result(vector<path> dir, vector<dcp::DecryptedKDM> kdm, vector<dcp::VerificationNote> test_notes) +check_verify_result(vector<dcp::VerificationNote> notes, vector<dcp::VerificationNote> test_notes) { - auto notes = dcp::verify({dir}, kdm, &stage, &progress, {}, xsd_test); - std::sort (notes.begin(), notes.end()); - std::sort (test_notes.begin(), test_notes.end()); + std::sort(notes.begin(), notes.end()); + std::sort(test_notes.begin(), test_notes.end()); - string message = "\nVerification notes from test:\n"; - for (auto i: notes) { - message += " " + note_to_string(i) + "\n"; - message += dcp::String::compose( - " [%1 %2 %3 %4 %5 %6 %7]\n", - static_cast<int>(i.type()), - static_cast<int>(i.code()), - i.note().get_value_or("<none>"), - i.file().get_value_or("<none>"), - i.line().get_value_or(0), - i.reference_hash().get_value_or("<none>"), - i.calculated_hash().get_value_or("<none>") - ); + string message = "\n"; + + vector<dcp::VerificationNote> not_expected; + for (auto note: notes) { + auto iter = std::find_if(test_notes.begin(), test_notes.end(), [note](dcp::VerificationNote const& n) { return note.type() == n.type() && note.code() == n.code(); }); + if (iter != test_notes.end() && *iter != note) { + message += "Wrong details:\n --seen " + to_string(note) + " --expected " + to_string(*iter) + "\n"; + } else if (iter == test_notes.end()) { + not_expected.push_back(note); + } } - message += "Expected:\n"; - for (auto i: test_notes) { - message += " " + note_to_string(i) + "\n"; - message += dcp::String::compose( - " [%1 %2 %3 %4 %5 %6 %7]\n", - static_cast<int>(i.type()), - static_cast<int>(i.code()), - i.note().get_value_or("<none>"), - i.file().get_value_or("<none>"), - i.line().get_value_or(0), - i.reference_hash().get_value_or("<none>"), - i.calculated_hash().get_value_or("<none>") - ); + + vector<dcp::VerificationNote> not_seen; + for (auto note: test_notes) { + auto iter = std::find_if(notes.begin(), notes.end(), [note](dcp::VerificationNote const& n) { return note.type() == n.type() && note.code() == n.code(); }); + if (iter == notes.end()) { + not_seen.push_back(note); + } + } + + for (auto note: not_expected) { + message += "Not expected:\n" + to_string(note) + "\n"; } - BOOST_REQUIRE_MESSAGE (notes == test_notes, message); + for (auto note: not_seen) { + message += "Not seen:\n" + to_string(note) + "\n"; + } + + BOOST_REQUIRE_MESSAGE(notes == test_notes, message); +} + + +static +void +check_verify_result(vector<path> dir, vector<dcp::DecryptedKDM> kdm, vector<dcp::VerificationNote> test_notes) +{ + check_verify_result(dcp::verify({dir}, kdm, &stage, &progress, {}, xsd_test).notes, test_notes); } /* Copy dcp_test1 to build/test/verify_test{suffix} then edit a file found by the functor 'file', - * replacing from with to. Verify the resulting DCP and check that the results match the given - * list of codes. + * replacing from with to. */ static void -check_verify_result_after_replace (string suffix, boost::function<path (string)> file, string from, string to, vector<dcp::VerificationNote::Code> codes) +replace(string suffix, boost::function<path (string)> file, string from, string to) { auto dir = setup (1, suffix); @@ -250,17 +320,6 @@ check_verify_result_after_replace (string suffix, boost::function<path (string)> Editor e (file(suffix)); e.replace (from, to); } - - auto notes = dcp::verify({dir}, {}, &stage, &progress, {}, xsd_test); - - BOOST_REQUIRE_EQUAL (notes.size(), codes.size()); - auto i = notes.begin(); - auto j = codes.begin(); - while (i != notes.end()) { - BOOST_CHECK_EQUAL (i->code(), *j); - ++i; - ++j; - } } @@ -295,11 +354,44 @@ private: }; +static +dcp::VerificationNote +ok(dcp::VerificationNote::Code code, shared_ptr<const dcp::CPL> cpl) +{ + return dcp::VerificationNote(dcp::VerificationNote::Type::OK, code).set_cpl_id(cpl->id()); +} + + +static +dcp::VerificationNote +ok(dcp::VerificationNote::Code code, string note, shared_ptr<const dcp::CPL> cpl) +{ + return dcp::VerificationNote(dcp::VerificationNote::Type::OK, code, note).set_cpl_id(cpl->id()); +} + + +static +dcp::VerificationNote +ok(dcp::VerificationNote::Code code, boost::filesystem::path path, shared_ptr<const dcp::CPL> cpl) +{ + return dcp::VerificationNote(dcp::VerificationNote::Type::OK, code, path).set_cpl_id(cpl->id()); +} + + +void +add(vector<dcp::VerificationNote>& notes, vector<dcp::VerificationNote> const& add) +{ + for (auto i: add) { + notes.push_back(i); + } +} + + BOOST_AUTO_TEST_CASE (verify_no_error) { stages.clear (); auto dir = setup (1, "no_error"); - auto notes = dcp::verify({dir}, {}, &stage, &progress, {}, xsd_test); + auto notes = dcp::verify({dir}, {}, &stage, &progress, {}, xsd_test).notes; path const cpl_file = dir / dcp_test1_cpl(); path const pkl_file = dir / dcp_test1_pkl(); @@ -342,7 +434,9 @@ BOOST_AUTO_TEST_CASE (verify_no_error) ++st; BOOST_REQUIRE (st == stages.end()); - BOOST_CHECK_EQUAL (notes.size(), 0U); + for (auto note: notes) { + BOOST_CHECK(note.type() == dcp::VerificationNote::Type::OK); + } } @@ -351,21 +445,22 @@ BOOST_AUTO_TEST_CASE (verify_incorrect_picture_sound_hash) using namespace boost::filesystem; auto dir = setup (1, "incorrect_picture_sound_hash"); + auto cpl = make_shared<dcp::CPL>(find_cpl(dir)); auto video_path = path(dir / "video.mxf"); HashCalculator video_calc(video_path); auto mod = fopen(video_path.string().c_str(), "r+b"); BOOST_REQUIRE (mod); - fseek (mod, 4096, SEEK_SET); + BOOST_REQUIRE_EQUAL(fseek(mod, -16, SEEK_END), 0); int x = 42; - fwrite (&x, sizeof(x), 1, mod); + BOOST_REQUIRE(fwrite(&x, sizeof(x), 1, mod) == 1); fclose (mod); auto audio_path = path(dir / "audio.mxf"); HashCalculator audio_calc(audio_path); mod = fopen(audio_path.string().c_str(), "r+b"); BOOST_REQUIRE (mod); - BOOST_REQUIRE_EQUAL (fseek(mod, -64, SEEK_END), 0); + BOOST_REQUIRE_EQUAL(fseek(mod, 0, SEEK_END), 0); BOOST_REQUIRE (fwrite (&x, sizeof(x), 1, mod) == 1); fclose (mod); @@ -374,12 +469,25 @@ BOOST_AUTO_TEST_CASE (verify_incorrect_picture_sound_hash) { dir }, {}, { + ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl), + ok(dcp::VerificationNote::Code::MATCHING_PKL_ANNOTATION_TEXT_WITH_CPL, cpl), + ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl), + dcp::VerificationNote( + dcp::VerificationNote::Type::OK, + dcp::VerificationNote::Code::VALID_MAIN_PICTURE_ACTIVE_AREA, + string{"1998x1080"}, + canonical(cpl->file().get()) + ).set_cpl_id(dcp_test1_cpl_id()), + ok(dcp::VerificationNote::Code::VALID_CONTENT_KIND, string{"trailer"}, cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_VERSION_LABEL_TEXT, cpl->content_version()->label_text, cpl), + ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"A Test DCP"}, cpl), + ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "video.mxf"), cpl), dcp::VerificationNote( dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INCORRECT_PICTURE_HASH, canonical(video_path) - ).set_reference_hash(video_calc.old_hash()).set_calculated_hash(video_calc.new_hash()), + ).set_cpl_id(dcp_test1_cpl_id()).set_reference_hash(video_calc.old_hash()).set_calculated_hash(video_calc.new_hash()), dcp::VerificationNote( dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INCORRECT_SOUND_HASH, canonical(audio_path) - ).set_reference_hash(audio_calc.old_hash()).set_calculated_hash(audio_calc.new_hash()), + ).set_cpl_id(dcp_test1_cpl_id()).set_reference_hash(audio_calc.old_hash()).set_calculated_hash(audio_calc.new_hash()), }); } @@ -389,6 +497,7 @@ BOOST_AUTO_TEST_CASE (verify_mismatched_picture_sound_hashes) using namespace boost::filesystem; auto dir = setup (1, "mismatched_picture_sound_hashes"); + auto cpl = make_shared<dcp::CPL>(find_cpl(dir)); HashCalculator calc(dir / dcp_test1_cpl()); @@ -401,14 +510,30 @@ BOOST_AUTO_TEST_CASE (verify_mismatched_picture_sound_hashes) { dir }, {}, { + ok(dcp::VerificationNote::Code::MATCHING_PKL_ANNOTATION_TEXT_WITH_CPL, cpl), + ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl), + dcp::VerificationNote( + dcp::VerificationNote::Type::OK, + dcp::VerificationNote::Code::VALID_MAIN_PICTURE_ACTIVE_AREA, + string{"1998x1080"}, + canonical(cpl->file().get()) + ).set_cpl_id(cpl->id()), + ok(dcp::VerificationNote::Code::VALID_CONTENT_KIND, string{"trailer"}, cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_VERSION_LABEL_TEXT, cpl->content_version()->label_text, cpl), + ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"A Test DCP"}, cpl), + ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "video.mxf"), cpl), + dcp::VerificationNote( + dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_CPL_HASHES, canonical(dir / dcp_test1_cpl()) + ).set_cpl_id(dcp_test1_cpl_id()).set_reference_hash("x" + calc.old_hash()).set_calculated_hash(calc.old_hash()), + dcp::VerificationNote( + dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_PICTURE_HASHES, canonical(dir / "video.mxf") + ).set_cpl_id(dcp_test1_cpl_id()), dcp::VerificationNote( - dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_CPL_HASHES, dcp_test1_cpl_id(), canonical(dir / dcp_test1_cpl()) - ).set_reference_hash("x" + calc.old_hash()).set_calculated_hash(calc.old_hash()), - { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_PICTURE_HASHES, canonical(dir / "video.mxf") }, - { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_SOUND_HASHES, canonical(dir / "audio.mxf") }, - { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_XML, "value 'xKcJb7S2K5cNm8RG4kfQD5FTeS0A=' is invalid Base64-encoded binary", canonical(dir / dcp_test1_pkl()), 28 }, - { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_XML, "value 'xtfX1mVIKJCVr1m7Y32Nzxf0+Rpw=' is invalid Base64-encoded binary", canonical(dir / dcp_test1_pkl()), 12 }, - { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_XML, "value 'xwUmt8G+cFFKMGt0ueS9+F1S4uhc=' is invalid Base64-encoded binary", canonical(dir / dcp_test1_pkl()), 20 }, + dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_SOUND_HASHES, canonical(dir / "audio.mxf") + ).set_cpl_id(dcp_test1_cpl_id()), + { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_XML, "value 'x3M7YTgvFKXXMEGLkIbV4miC90FE=' is invalid Base64-encoded binary", canonical(dir / dcp_test1_pkl()), 28 }, + { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_XML, "value 'xskI+5b/9LA/y6h0mcyxysJYanxI=' is invalid Base64-encoded binary", canonical(dir / dcp_test1_pkl()), 12 }, + { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_XML, "value 'xvsVjRV9vhTBPUWfE/TT1o2vdQsI=' is invalid Base64-encoded binary", canonical(dir / dcp_test1_pkl()), 20 }, }); } @@ -424,21 +549,37 @@ BOOST_AUTO_TEST_CASE (verify_failed_read_content_kind) e.replace ("<ContentKind>", "<ContentKind>x"); } + auto cpl = std::make_shared<dcp::CPL>(find_cpl(dir)); + check_verify_result ( { dir }, {}, { + ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "video.mxf"), cpl), + ok(dcp::VerificationNote::Code::MATCHING_PKL_ANNOTATION_TEXT_WITH_CPL, cpl), + ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl), + dcp::VerificationNote( + dcp::VerificationNote::Type::OK, + dcp::VerificationNote::Code::VALID_MAIN_PICTURE_ACTIVE_AREA, + string{"1998x1080"}, + canonical(cpl->file().get()) + ).set_cpl_id(cpl->id()), + ok(dcp::VerificationNote::Code::VALID_CONTENT_VERSION_LABEL_TEXT, cpl->content_version()->label_text, cpl), + ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"A Test DCP"}, cpl), + ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "video.mxf"), cpl), + dcp::VerificationNote( + dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_CPL_HASHES, canonical(dir / dcp_test1_cpl()) + ).set_cpl_id(dcp_test1_cpl_id()).set_reference_hash(calc.old_hash()).set_calculated_hash(calc.new_hash()), dcp::VerificationNote( - dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_CPL_HASHES, dcp_test1_cpl_id(), canonical(dir / dcp_test1_cpl()) - ).set_reference_hash(calc.old_hash()).set_calculated_hash(calc.new_hash()), - { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_CONTENT_KIND, string("xtrailer") } + dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_CONTENT_KIND, string("xtrailer") + ).set_cpl_id(dcp_test1_cpl_id()) }); } static path -cpl (string suffix) +dcp_test1_cpl_path(string suffix) { return dcp::String::compose("build/test/verify_test%1/%2", suffix, dcp_test1_cpl()); } @@ -446,7 +587,7 @@ cpl (string suffix) static path -pkl (string suffix) +dcp_test1_pkl_path(string suffix) { return dcp::String::compose("build/test/verify_test%1/%2", suffix, dcp_test1_pkl()); } @@ -462,22 +603,64 @@ asset_map (string suffix) BOOST_AUTO_TEST_CASE (verify_invalid_picture_frame_rate) { - check_verify_result_after_replace ( - "invalid_picture_frame_rate", &cpl, - "<FrameRate>24 1", "<FrameRate>99 1", - { dcp::VerificationNote::Code::MISMATCHED_CPL_HASHES, - dcp::VerificationNote::Code::INVALID_PICTURE_FRAME_RATE } - ); + auto const suffix = "invalid_picture_frame_rate"; + + replace(suffix, &dcp_test1_cpl_path, "<FrameRate>24 1", "<FrameRate>99 1"); + + auto const dir = dcp::String::compose("build/test/verify_test%1", suffix); + auto const cpl_path = find_cpl(dir); + auto cpl = std::make_shared<dcp::CPL>(cpl_path); + + std::vector<dcp::VerificationNote> expected = + { + ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "video.mxf"), cpl), + ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl), + dcp::VerificationNote( + dcp::VerificationNote::Type::OK, + dcp::VerificationNote::Code::VALID_MAIN_PICTURE_ACTIVE_AREA, + string{"1998x1080"}, + canonical(cpl->file().get()) + ).set_cpl_id(cpl->id()), + ok(dcp::VerificationNote::Code::MATCHING_PKL_ANNOTATION_TEXT_WITH_CPL, cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_KIND, string{"trailer"}, cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_VERSION_LABEL_TEXT, cpl->content_version()->label_text, cpl), + ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"A Test DCP"}, cpl), + ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "video.mxf"), cpl), + dcp::VerificationNote( + dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_CPL_HASHES, canonical(cpl_path) + ).set_cpl_id(cpl->id()).set_calculated_hash("7n7GQ2TbxQbmHYuAR8ml7XDOep8=").set_reference_hash("skI+5b/9LA/y6h0mcyxysJYanxI="), + dcp::VerificationNote( + dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_PICTURE_FRAME_RATE, string{"99/1"} + ).set_cpl_id(cpl->id()) + }; + + check_verify_result(dcp::verify({dir}, {}, &stage, &progress, {}, xsd_test).notes, expected); } + BOOST_AUTO_TEST_CASE (verify_missing_asset) { auto dir = setup (1, "missing_asset"); remove (dir / "video.mxf"); + + auto cpl = std::make_shared<dcp::CPL>(find_cpl(dir)); + check_verify_result ( { dir }, {}, { + ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl), + ok(dcp::VerificationNote::Code::MATCHING_PKL_ANNOTATION_TEXT_WITH_CPL, cpl), + ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl), + dcp::VerificationNote( + dcp::VerificationNote::Type::OK, + dcp::VerificationNote::Code::VALID_MAIN_PICTURE_ACTIVE_AREA, + string{"1998x1080"}, + canonical(cpl->file().get()) + ).set_cpl_id(cpl->id()), + ok(dcp::VerificationNote::Code::VALID_CONTENT_KIND, string{"trailer"}, cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_VERSION_LABEL_TEXT, cpl->content_version()->label_text, cpl), + ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"A Test DCP"}, cpl), { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISSING_ASSET, canonical(dir) / "video.mxf" } }); } @@ -485,71 +668,236 @@ BOOST_AUTO_TEST_CASE (verify_missing_asset) BOOST_AUTO_TEST_CASE (verify_empty_asset_path) { - check_verify_result_after_replace ( - "empty_asset_path", &asset_map, - "<Path>video.mxf</Path>", "<Path></Path>", - { dcp::VerificationNote::Code::EMPTY_ASSET_PATH } - ); + auto const suffix = "empty_asset_path"; + + replace("empty_asset_path", &asset_map, "<Path>video.mxf</Path>", "<Path></Path>"); + + auto const dir = dcp::String::compose("build/test/verify_test%1", suffix); + auto const cpl_path = find_cpl(dir); + auto cpl = std::make_shared<dcp::CPL>(cpl_path); + + std::vector<dcp::VerificationNote> expected = { + ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl), + ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl), + dcp::VerificationNote( + dcp::VerificationNote::Type::OK, + dcp::VerificationNote::Code::VALID_MAIN_PICTURE_ACTIVE_AREA, + string{"1998x1080"}, + canonical(cpl->file().get()) + ).set_cpl_id(cpl->id()), + ok(dcp::VerificationNote::Code::MATCHING_PKL_ANNOTATION_TEXT_WITH_CPL, cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_KIND, string{"trailer"}, cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_VERSION_LABEL_TEXT, cpl->content_version()->label_text, cpl), + ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"A Test DCP"}, cpl), + { dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::EMPTY_ASSET_PATH } + }; + + check_verify_result(dcp::verify({dir}, {}, &stage, &progress, {}, xsd_test).notes, expected); } BOOST_AUTO_TEST_CASE (verify_mismatched_standard) { - check_verify_result_after_replace ( - "mismatched_standard", &cpl, - "http://www.smpte-ra.org/schemas/429-7/2006/CPL", "http://www.digicine.com/PROTO-ASDCP-CPL-20040511#", - { dcp::VerificationNote::Code::MISMATCHED_STANDARD, - dcp::VerificationNote::Code::INVALID_XML, - dcp::VerificationNote::Code::INVALID_XML, - dcp::VerificationNote::Code::INVALID_XML, - dcp::VerificationNote::Code::INVALID_XML, - dcp::VerificationNote::Code::INVALID_XML, - dcp::VerificationNote::Code::MISMATCHED_CPL_HASHES } - ); + auto const suffix = "mismatched_standard"; + + replace(suffix, &dcp_test1_cpl_path, "http://www.smpte-ra.org/schemas/429-7/2006/CPL", "http://www.digicine.com/PROTO-ASDCP-CPL-20040511#"); + + auto const dir = dcp::String::compose("build/test/verify_test%1", suffix); + auto const cpl_path = find_cpl(dir); + auto cpl = std::make_shared<dcp::CPL>(cpl_path); + + std::vector<dcp::VerificationNote> expected = { + ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "video.mxf"), cpl), + ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "video.mxf"), cpl), + ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl), + dcp::VerificationNote( + dcp::VerificationNote::Type::OK, + dcp::VerificationNote::Code::VALID_MAIN_PICTURE_ACTIVE_AREA, + string{"1998x1080"}, + canonical(cpl->file().get()) + ).set_cpl_id(cpl->id()), + ok(dcp::VerificationNote::Code::MATCHING_PKL_ANNOTATION_TEXT_WITH_CPL, cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_KIND, string{"trailer"}, cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_VERSION_LABEL_TEXT, cpl->content_version()->label_text, cpl), + ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"A Test DCP"}, cpl), + { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_STANDARD }, + dcp::VerificationNote( + dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_XML, "invalid character encountered", canonical(cpl_path), 42 + ).set_cpl_id(cpl->id()), + dcp::VerificationNote( + dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_XML, "no declaration found for element 'Id'", canonical(cpl_path), 53 + ).set_cpl_id(cpl->id()), + dcp::VerificationNote( + dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_XML, "no declaration found for element 'EditRate'", canonical(cpl_path), 54 + ).set_cpl_id(cpl->id()), + dcp::VerificationNote( + dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_XML, "no declaration found for element 'IntrinsicDuration'", canonical(cpl_path), 55 + ).set_cpl_id(cpl->id()), + dcp::VerificationNote( + dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_XML, + "element 'Id' is not allowed for content model '(Id,AnnotationText?,EditRate,IntrinsicDuration," + "EntryPoint?,Duration?,FullContentTitleText,ReleaseTerritory?,VersionNumber?,Chain?,Distributor?," + "Facility?,AlternateContentVersionList?,Luminance?,MainSoundConfiguration,MainSoundSampleRate," + "MainPictureStoredArea,MainPictureActiveArea,MainSubtitleLanguageList?,ExtensionMetadataList?,)'", + canonical(cpl_path), 149 + ).set_cpl_id(cpl->id()), + dcp::VerificationNote( + dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_CPL_HASHES, canonical(cpl_path) + ).set_cpl_id(cpl->id()).set_reference_hash("skI+5b/9LA/y6h0mcyxysJYanxI=").set_calculated_hash("FZ9E7L/pOuJ6aZfbiaANTv8BFOo=") + }; + + check_verify_result(dcp::verify({dir}, {}, &stage, &progress, {}, xsd_test).notes, expected); } BOOST_AUTO_TEST_CASE (verify_invalid_xml_cpl_id) { + auto const suffix = "invalid_xml_cpl_id"; + /* There's no MISMATCHED_CPL_HASHES error here because it can't find the correct hash by ID (since the ID is wrong) */ - check_verify_result_after_replace ( - "invalid_xml_cpl_id", &cpl, - "<Id>urn:uuid:6affb8ee-0020-4dff-a53c-17652f6358ab", "<Id>urn:uuid:6affb8ee-0020-4dff-a53c-17652f6358a", - { dcp::VerificationNote::Code::INVALID_XML } - ); + replace("invalid_xml_cpl_id", &dcp_test1_cpl_path, "<Id>urn:uuid:6affb8ee-0020-4dff-a53c-17652f6358ab", "<Id>urn:uuid:6affb8ee-0020-4dff-a53c-17652f6358a"); + + auto const dir = dcp::String::compose("build/test/verify_test%1", suffix); + auto const cpl_path = find_cpl(dir); + auto cpl = std::make_shared<dcp::CPL>(cpl_path); + + std::vector<dcp::VerificationNote> expected = { + ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "video.mxf"), cpl), + ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "video.mxf"), cpl), + ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl), + dcp::VerificationNote( + dcp::VerificationNote::Type::OK, + dcp::VerificationNote::Code::VALID_MAIN_PICTURE_ACTIVE_AREA, + string{"1998x1080"}, + canonical(cpl->file().get()) + ).set_cpl_id(cpl->id()), + ok(dcp::VerificationNote::Code::MATCHING_PKL_ANNOTATION_TEXT_WITH_CPL, cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_KIND, string{"trailer"}, cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_VERSION_LABEL_TEXT, cpl->content_version()->label_text, cpl), + ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"A Test DCP"}, cpl), + ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl), + dcp::VerificationNote( + dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_XML, + "value 'urn:uuid:6affb8ee-0020-4dff-a53c-17652f6358a' does not match regular expression " + "facet 'urn:uuid:[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}'", canonical(cpl_path), 3 + ).set_cpl_id(cpl->id()) + }; + + check_verify_result(dcp::verify({dir}, {}, &stage, &progress, {}, xsd_test).notes, expected); } BOOST_AUTO_TEST_CASE (verify_invalid_xml_issue_date) { - check_verify_result_after_replace ( - "invalid_xml_issue_date", &cpl, - "<IssueDate>", "<IssueDate>x", - { dcp::VerificationNote::Code::INVALID_XML, - dcp::VerificationNote::Code::MISMATCHED_CPL_HASHES } - ); + auto const suffix = "invalid_xml_issue_date"; + + replace("invalid_xml_issue_date", &dcp_test1_cpl_path, "<IssueDate>", "<IssueDate>x"); + + auto const dir = dcp::String::compose("build/test/verify_test%1", suffix); + auto const cpl_path = find_cpl(dir); + auto cpl = std::make_shared<dcp::CPL>(cpl_path); + + std::vector<dcp::VerificationNote> expected = { + ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "video.mxf"), cpl), + ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "video.mxf"), cpl), + ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl), + dcp::VerificationNote( + dcp::VerificationNote::Type::OK, + dcp::VerificationNote::Code::VALID_MAIN_PICTURE_ACTIVE_AREA, + string{"1998x1080"}, + canonical(cpl->file().get()) + ).set_cpl_id(cpl->id()), + ok(dcp::VerificationNote::Code::MATCHING_PKL_ANNOTATION_TEXT_WITH_CPL, cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_KIND, string{"trailer"}, cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_VERSION_LABEL_TEXT, cpl->content_version()->label_text, cpl), + ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"A Test DCP"}, cpl), + dcp::VerificationNote( + dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_CPL_HASHES, canonical(cpl_path) + ).set_cpl_id(cpl->id()).set_reference_hash("skI+5b/9LA/y6h0mcyxysJYanxI=").set_calculated_hash("sz3BeIugJ567q3HMnA62JeRw4TE="), + dcp::VerificationNote( + dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_XML, + "invalid character encountered", + canonical(cpl_path), 5 + ).set_cpl_id(cpl->id()), + }; + + check_verify_result(dcp::verify({dir}, {}, &stage, &progress, {}, xsd_test).notes, expected); } BOOST_AUTO_TEST_CASE (verify_invalid_xml_pkl_id) { - check_verify_result_after_replace ( - "invalid_xml_pkl_id", &pkl, - "<Id>urn:uuid:" + dcp_test1_pkl_id().substr(0, 3), - "<Id>urn:uuid:x" + dcp_test1_pkl_id().substr(1, 2), - { dcp::VerificationNote::Code::INVALID_XML } - ); + auto const suffix = "invalid_xml_pkl_id"; + + replace("invalid_xml_pkl_id", &dcp_test1_pkl_path, "<Id>urn:uuid:" + dcp_test1_pkl_id().substr(0, 3), "<Id>urn:uuid:x" + dcp_test1_pkl_id().substr(1, 2)); + + auto const dir = dcp::String::compose("build/test/verify_test%1", suffix); + auto const pkl_path = find_pkl(dir); + auto const cpl_path = find_cpl(dir); + auto cpl = std::make_shared<dcp::CPL>(cpl_path); + + std::vector<dcp::VerificationNote> expected = { + ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "video.mxf"), cpl), + ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "video.mxf"), cpl), + ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl), + dcp::VerificationNote( + dcp::VerificationNote::Type::OK, + dcp::VerificationNote::Code::VALID_MAIN_PICTURE_ACTIVE_AREA, + string{"1998x1080"}, + canonical(cpl->file().get()) + ).set_cpl_id(cpl->id()), + ok(dcp::VerificationNote::Code::MATCHING_PKL_ANNOTATION_TEXT_WITH_CPL, cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_KIND, string{"trailer"}, cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_VERSION_LABEL_TEXT, cpl->content_version()->label_text, cpl), + ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"A Test DCP"}, cpl), + ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl), + dcp::VerificationNote( + dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_XML, + "value 'urn:uuid:x199d58b-5ef8-4d49-b270-07e590ccb280' does not match regular " + "expression facet 'urn:uuid:[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}'", + canonical(pkl_path), 3 + ), + }; + + check_verify_result(dcp::verify({dir}, {}, &stage, &progress, {}, xsd_test).notes, expected); } BOOST_AUTO_TEST_CASE (verify_invalid_xml_asset_map_id) { - check_verify_result_after_replace ( - "invalid_xml_asset_map_id", &asset_map, - "<Id>urn:uuid:" + dcp_test1_asset_map_id.substr(0, 3), - "<Id>urn:uuid:x" + dcp_test1_asset_map_id.substr(1, 2), - { dcp::VerificationNote::Code::INVALID_XML } - ); + auto const suffix = "invalid_xml_asset_map_id"; + + replace("invalid_xml_asset_map_id", &asset_map, "<Id>urn:uuid:" + dcp_test1_asset_map_id.substr(0, 3), "<Id>urn:uuid:x" + dcp_test1_asset_map_id.substr(1, 2)); + + auto const dir = dcp::String::compose("build/test/verify_test%1", suffix); + auto const cpl_path = find_cpl(dir); + auto const asset_map_path = find_asset_map(dir); + auto cpl = std::make_shared<dcp::CPL>(cpl_path); + + std::vector<dcp::VerificationNote> expected = { + ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "video.mxf"), cpl), + ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "video.mxf"), cpl), + ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl), + dcp::VerificationNote( + dcp::VerificationNote::Type::OK, + dcp::VerificationNote::Code::VALID_MAIN_PICTURE_ACTIVE_AREA, + string{"1998x1080"}, + canonical(cpl->file().get()) + ).set_cpl_id(cpl->id()), + ok(dcp::VerificationNote::Code::MATCHING_PKL_ANNOTATION_TEXT_WITH_CPL, cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_KIND, string{"trailer"}, cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_VERSION_LABEL_TEXT, cpl->content_version()->label_text, cpl), + ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"A Test DCP"}, cpl), + ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl), + dcp::VerificationNote( + dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_XML, + "value 'urn:uuid:x17b3de4-6dda-408d-b19b-6711354b0bc3' does not match regular " + "expression facet 'urn:uuid:[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}'", + canonical(asset_map_path), 3 + ), + }; + + check_verify_result(dcp::verify({dir}, {}, &stage, &progress, {}, xsd_test).notes, expected); } @@ -557,11 +905,12 @@ BOOST_AUTO_TEST_CASE (verify_invalid_standard) { stages.clear (); auto dir = setup (3, "verify_invalid_standard"); - auto notes = dcp::verify({dir}, {}, &stage, &progress, {}, xsd_test); + auto notes = dcp::verify({dir}, {}, &stage, &progress, {}, xsd_test).notes; path const cpl_file = dir / "cpl_cbfd2bc0-21cf-4a8f-95d8-9cddcbe51296.xml"; path const pkl_file = dir / "pkl_d87a950c-bd6f-41f6-90cc-56ccd673e131.xml"; path const assetmap_file = dir / "ASSETMAP"; + auto cpl = std::make_shared<dcp::CPL>(cpl_file); auto st = stages.begin(); BOOST_CHECK_EQUAL (st->first, "Checking DCP"); @@ -601,13 +950,26 @@ BOOST_AUTO_TEST_CASE (verify_invalid_standard) ++st; BOOST_REQUIRE (st == stages.end()); - BOOST_REQUIRE_EQUAL (notes.size(), 2U); - auto i = notes.begin (); - BOOST_CHECK_EQUAL (i->type(), dcp::VerificationNote::Type::BV21_ERROR); - BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::Code::INVALID_STANDARD); - ++i; - BOOST_CHECK_EQUAL (i->type(), dcp::VerificationNote::Type::BV21_ERROR); - BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::Code::INVALID_JPEG2000_GUARD_BITS_FOR_2K); + vector<dcp::VerificationNote> expected = { + ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl), + { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_STANDARD }, + ok(dcp::VerificationNote::Code::VALID_CONTENT_KIND, string{"feature"}, cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_VERSION_LABEL_TEXT, cpl->content_version()->label_text, cpl), + ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl), + ok(dcp::VerificationNote::Code::MATCHING_PKL_ANNOTATION_TEXT_WITH_CPL, cpl), + ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "j2c_c6035f97-b07d-4e1c-944d-603fc2ddc242.mxf"), cpl), + ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "j2c_c6035f97-b07d-4e1c-944d-603fc2ddc242.mxf"), cpl) + }; + + for (int j = 0; j < 24; ++j) { + expected.push_back( + dcp::VerificationNote( + dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_JPEG2000_GUARD_BITS_FOR_2K, string("2") + ).set_cpl_id(cpl->id()) + ); + } + + check_verify_result(notes, expected); } /* DCP with a short asset */ @@ -620,22 +982,42 @@ BOOST_AUTO_TEST_CASE (verify_invalid_duration) BOOST_REQUIRE(dcp.cpls().size() == 1); auto cpl = dcp.cpls()[0]; - check_verify_result ( - { dir }, - {}, - { - { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_STANDARD }, - { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_DURATION, string("d7576dcb-a361-4139-96b8-267f5f8d7f91") }, - { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_INTRINSIC_DURATION, string("d7576dcb-a361-4139-96b8-267f5f8d7f91") }, - { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_DURATION, string("a2a87f5d-b749-4a7e-8d0c-9d48a4abf626") }, - { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_INTRINSIC_DURATION, string("a2a87f5d-b749-4a7e-8d0c-9d48a4abf626") }, - { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_JPEG2000_GUARD_BITS_FOR_2K, string("2") }, + vector<dcp::VerificationNote> expected = { + ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl), + ok(dcp::VerificationNote::Code::MATCHING_PKL_ANNOTATION_TEXT_WITH_CPL, cpl), + ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_KIND, string{"feature"}, cpl), + ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "j2c_d7576dcb-a361-4139-96b8-267f5f8d7f91.mxf"), cpl), + ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "j2c_d7576dcb-a361-4139-96b8-267f5f8d7f91.mxf"), cpl), + { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_STANDARD }, + dcp::VerificationNote( + dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_DURATION, string("d7576dcb-a361-4139-96b8-267f5f8d7f91") + ).set_cpl_id(cpl->id()), + dcp::VerificationNote( + dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_INTRINSIC_DURATION, string("d7576dcb-a361-4139-96b8-267f5f8d7f91") + ).set_cpl_id(cpl->id()), + dcp::VerificationNote( + dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_DURATION, string("a2a87f5d-b749-4a7e-8d0c-9d48a4abf626") + ).set_cpl_id(cpl->id()), + dcp::VerificationNote( + dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_INTRINSIC_DURATION, string("a2a87f5d-b749-4a7e-8d0c-9d48a4abf626") + ).set_cpl_id(cpl->id()), + dcp::VerificationNote( + dcp::VerificationNote::Type::WARNING, + dcp::VerificationNote::Code::EMPTY_CONTENT_VERSION_LABEL_TEXT, + cpl->file().get() + ).set_cpl_id(cpl->id()) + }; + + for (int i = 0; i < 23; ++i) { + expected.push_back( dcp::VerificationNote( - dcp::VerificationNote::Type::WARNING, - dcp::VerificationNote::Code::EMPTY_CONTENT_VERSION_LABEL_TEXT, - cpl->file().get() - ).set_id("d74fda30-d5f4-4c5f-870f-ebc089d97eb7") - }); + dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_JPEG2000_GUARD_BITS_FOR_2K, string("2") + ).set_cpl_id(cpl->id()) + ); + } + + check_verify_result({ dir }, {}, expected); } @@ -643,9 +1025,9 @@ static shared_ptr<dcp::CPL> dcp_from_frame (dcp::ArrayData const& frame, path dir) { - auto asset = make_shared<dcp::MonoPictureAsset>(dcp::Fraction(24, 1), dcp::Standard::SMPTE); + auto asset = make_shared<dcp::MonoJ2KPictureAsset>(dcp::Fraction(24, 1), dcp::Standard::SMPTE); create_directories (dir); - auto writer = asset->start_write(dir / "pic.mxf", dcp::PictureAsset::Behaviour::MAKE_NEW); + auto writer = asset->start_write(dir / "pic.mxf", dcp::Behaviour::MAKE_NEW); for (int i = 0; i < 24; ++i) { writer->write (frame.data(), frame.size()); } @@ -674,12 +1056,21 @@ BOOST_AUTO_TEST_CASE (verify_invalid_picture_frame_size_in_bytes) prepare_directory (dir); auto cpl = dcp_from_frame (oversized_frame, dir); - vector<dcp::VerificationNote> expected; + vector<dcp::VerificationNote> expected = { + ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl), + ok(dcp::VerificationNote::Code::MATCHING_PKL_ANNOTATION_TEXT_WITH_CPL, cpl), + ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_KIND, string{"trailer"}, cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_VERSION_LABEL_TEXT, cpl->content_version()->label_text, cpl), + ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"hello"}, cpl), + ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "pic.mxf"), cpl), + }; + for (auto i = 0; i < 24; ++i) { expected.push_back( dcp::VerificationNote( dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_JPEG2000_CODESTREAM, string("missing marker start byte") - ).set_frame(i).set_frame_rate(24) + ).set_frame(i).set_frame_rate(24).set_cpl_id(cpl->id()) ); } @@ -687,13 +1078,15 @@ BOOST_AUTO_TEST_CASE (verify_invalid_picture_frame_size_in_bytes) expected.push_back( dcp::VerificationNote( dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_PICTURE_FRAME_SIZE_IN_BYTES, canonical(dir / "pic.mxf") - ).set_frame(i).set_frame_rate(24) + ).set_frame(i).set_frame_rate(24).set_cpl_id(cpl->id()) ); } expected.push_back( - { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() } - ); + dcp::VerificationNote( + dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->file().get() + ).set_cpl_id(cpl->id()) + ); check_verify_result({ dir }, {}, expected); } @@ -717,13 +1110,21 @@ BOOST_AUTO_TEST_CASE (verify_nearly_invalid_picture_frame_size_in_bytes) prepare_directory (dir); auto cpl = dcp_from_frame (oversized_frame, dir); - vector<dcp::VerificationNote> expected; + vector<dcp::VerificationNote> expected = { + ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "pic.mxf"), cpl), + ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl), + ok(dcp::VerificationNote::Code::MATCHING_PKL_ANNOTATION_TEXT_WITH_CPL, cpl), + ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_KIND, string{"trailer"}, cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_VERSION_LABEL_TEXT, cpl->content_version()->label_text, cpl), + ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"hello"}, cpl), + }; for (auto i = 0; i < 24; ++i) { expected.push_back( dcp::VerificationNote( dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_JPEG2000_CODESTREAM, string("missing marker start byte") - ).set_frame(i).set_frame_rate(24) + ).set_frame(i).set_frame_rate(24).set_cpl_id(cpl->id()) ); } @@ -731,13 +1132,15 @@ BOOST_AUTO_TEST_CASE (verify_nearly_invalid_picture_frame_size_in_bytes) expected.push_back( dcp::VerificationNote( dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::NEARLY_INVALID_PICTURE_FRAME_SIZE_IN_BYTES, canonical(dir / "pic.mxf") - ).set_frame(i).set_frame_rate(24) + ).set_frame(i).set_frame_rate(24).set_cpl_id(cpl->id()) ); } expected.push_back( - { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() } - ); + dcp::VerificationNote( + dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->file().get() + ).set_cpl_id(cpl->id()) + ); check_verify_result ({ dir }, {}, expected); } @@ -754,7 +1157,20 @@ BOOST_AUTO_TEST_CASE (verify_valid_picture_frame_size_in_bytes) prepare_directory (dir); auto cpl = dcp_from_frame (frame, dir); - check_verify_result({ dir }, {}, {{ dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() }}); + check_verify_result( + { dir }, + {}, + { + ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "pic.mxf"), cpl), + ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl), + ok(dcp::VerificationNote::Code::MATCHING_PKL_ANNOTATION_TEXT_WITH_CPL, cpl), + ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_KIND, string{"trailer"}, cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_VERSION_LABEL_TEXT, cpl->content_version()->label_text, cpl), + ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"hello"}, cpl), + ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "pic.mxf"), cpl), + dcp::VerificationNote(dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->file().get()).set_cpl_id(cpl->id()) + }); } @@ -765,14 +1181,21 @@ BOOST_AUTO_TEST_CASE (verify_valid_interop_subtitles) copy_file ("test/data/subs1.xml", dir / "subs.xml"); auto asset = make_shared<dcp::InteropSubtitleAsset>(dir / "subs.xml"); auto reel_asset = make_shared<dcp::ReelInteropSubtitleAsset>(asset, dcp::Fraction(24, 1), 16 * 24, 0); - write_dcp_with_single_asset (dir, reel_asset, dcp::Standard::INTEROP); + auto cpl = write_dcp_with_single_asset(dir, reel_asset, dcp::Standard::INTEROP); check_verify_result ( {dir}, {}, { + ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl), + ok(dcp::VerificationNote::Code::MATCHING_PKL_ANNOTATION_TEXT_WITH_CPL, cpl), + ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_KIND, string{"trailer"}, cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_VERSION_LABEL_TEXT, cpl->content_version()->label_text, cpl), { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_STANDARD }, - { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISSING_FONT, string{"theFontId"} } + dcp::VerificationNote( + dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISSING_FONT, string{"theFontId"} + ).set_cpl_id(cpl->id()) }); } @@ -784,14 +1207,21 @@ BOOST_AUTO_TEST_CASE(verify_catch_missing_font_file_with_interop_ccap) copy_file("test/data/subs1.xml", dir / "ccap.xml"); auto asset = make_shared<dcp::InteropSubtitleAsset>(dir / "ccap.xml"); auto reel_asset = make_shared<dcp::ReelInteropClosedCaptionAsset>(asset, dcp::Fraction(24, 1), 16 * 24, 0); - write_dcp_with_single_asset(dir, reel_asset, dcp::Standard::INTEROP); + auto cpl = write_dcp_with_single_asset(dir, reel_asset, dcp::Standard::INTEROP); check_verify_result ( {dir}, {}, { + ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl), + ok(dcp::VerificationNote::Code::MATCHING_PKL_ANNOTATION_TEXT_WITH_CPL, cpl), + ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_KIND, string{"trailer"}, cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_VERSION_LABEL_TEXT, cpl->content_version()->label_text, cpl), { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_STANDARD }, - { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISSING_FONT, string{"theFontId"} } + dcp::VerificationNote( + dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISSING_FONT, string{"theFontId"} + ).set_cpl_id(cpl->id()) }); } @@ -805,7 +1235,7 @@ BOOST_AUTO_TEST_CASE (verify_invalid_interop_subtitles) copy_file ("test/data/subs1.xml", dir / "subs.xml"); auto asset = make_shared<dcp::InteropSubtitleAsset>(dir / "subs.xml"); auto reel_asset = make_shared<dcp::ReelInteropSubtitleAsset>(asset, dcp::Fraction(24, 1), 16 * 24, 0); - write_dcp_with_single_asset (dir, reel_asset, dcp::Standard::INTEROP); + auto cpl = write_dcp_with_single_asset(dir, reel_asset, dcp::Standard::INTEROP); { Editor e (dir / "subs.xml"); @@ -816,16 +1246,25 @@ BOOST_AUTO_TEST_CASE (verify_invalid_interop_subtitles) { dir }, {}, { + ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl), + ok(dcp::VerificationNote::Code::MATCHING_PKL_ANNOTATION_TEXT_WITH_CPL, cpl), + ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_KIND, string{"trailer"}, cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_VERSION_LABEL_TEXT, cpl->content_version()->label_text, cpl), { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_STANDARD }, - { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_XML, string("no declaration found for element 'Foo'"), path(), 5 }, - { + dcp::VerificationNote( + dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_XML, string("no declaration found for element 'Foo'"), path(), 5 + ).set_cpl_id(cpl->id()), + dcp::VerificationNote( dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_XML, string("element 'Foo' is not allowed for content model '(SubtitleID,MovieTitle,ReelNumber,Language,LoadFont*,Font*,Subtitle*)'"), path(), 29 - }, - { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISSING_FONT, string{"theFontId"} } + ).set_cpl_id(cpl->id()), + dcp::VerificationNote( + dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISSING_FONT, string{"theFontId"} + ).set_cpl_id(cpl->id()) }); } @@ -837,15 +1276,24 @@ BOOST_AUTO_TEST_CASE(verify_interop_subtitle_asset_with_no_subtitles) copy_file("test/data/subs4.xml", dir / "subs.xml"); auto asset = make_shared<dcp::InteropSubtitleAsset>(dir / "subs.xml"); auto reel_asset = make_shared<dcp::ReelInteropSubtitleAsset>(asset, dcp::Fraction(24, 1), 16 * 24, 0); - write_dcp_with_single_asset(dir, reel_asset, dcp::Standard::INTEROP); + auto cpl = write_dcp_with_single_asset(dir, reel_asset, dcp::Standard::INTEROP); check_verify_result ( { dir }, {}, { + ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl), + ok(dcp::VerificationNote::Code::MATCHING_PKL_ANNOTATION_TEXT_WITH_CPL, cpl), + ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_KIND, string{"trailer"}, cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_VERSION_LABEL_TEXT, cpl->content_version()->label_text, cpl), { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_STANDARD }, - { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISSING_SUBTITLE, asset->id(), boost::filesystem::canonical(asset->file().get()) }, - { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISSING_FONT, string{"theFontId"} } + dcp::VerificationNote( + dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISSING_SUBTITLE, asset->id(), boost::filesystem::canonical(asset->file().get()) + ).set_cpl_id(cpl->id()), + dcp::VerificationNote( + dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISSING_FONT, string{"theFontId"} + ).set_cpl_id(cpl->id()) }); } @@ -858,14 +1306,21 @@ BOOST_AUTO_TEST_CASE(verify_interop_subtitle_asset_with_single_space_subtitle) copy_file("test/data/subs5.xml", dir / "subs.xml"); auto asset = make_shared<dcp::InteropSubtitleAsset>(dir / "subs.xml"); auto reel_asset = make_shared<dcp::ReelInteropSubtitleAsset>(asset, dcp::Fraction(24, 1), 16 * 24, 0); - write_dcp_with_single_asset(dir, reel_asset, dcp::Standard::INTEROP); + auto cpl = write_dcp_with_single_asset(dir, reel_asset, dcp::Standard::INTEROP); check_verify_result ( { dir }, {}, { + ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl), + ok(dcp::VerificationNote::Code::MATCHING_PKL_ANNOTATION_TEXT_WITH_CPL, cpl), + ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_KIND, string{"trailer"}, cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_VERSION_LABEL_TEXT, cpl->content_version()->label_text, cpl), { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_STANDARD }, - { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISSING_FONT, string{"Arial"} } + dcp::VerificationNote( + dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISSING_FONT, string{"Arial"} + ).set_cpl_id(cpl->id()) }); } @@ -884,9 +1339,21 @@ BOOST_AUTO_TEST_CASE (verify_valid_smpte_subtitles) {dir}, {}, { - { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() }, - { dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::INVALID_SUBTITLE_ISSUE_DATE, string{"2021-04-14T13:19:14.000+02:00"} }, - { dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::INCORRECT_SUBTITLE_NAMESPACE_COUNT, asset->id() } + ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl), + ok(dcp::VerificationNote::Code::MATCHING_PKL_ANNOTATION_TEXT_WITH_CPL, cpl), + ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_KIND, string{"trailer"}, cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_VERSION_LABEL_TEXT, cpl->content_version()->label_text, cpl), + ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"hello"}, cpl), + dcp::VerificationNote( + dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->file().get() + ).set_cpl_id(cpl->id()), + dcp::VerificationNote( + dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::INVALID_SUBTITLE_ISSUE_DATE, string{"2021-04-14T13:19:14.000+02:00"} + ).set_cpl_id(cpl->id()), + dcp::VerificationNote( + dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::INCORRECT_SUBTITLE_NAMESPACE_COUNT, asset->id() + ).set_cpl_id(cpl->id()), }); } @@ -907,18 +1374,34 @@ BOOST_AUTO_TEST_CASE (verify_invalid_smpte_subtitles) { dir }, {}, { - { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_XML, string("no declaration found for element 'Foo'"), path(), 2 }, - { + ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl), + ok(dcp::VerificationNote::Code::MATCHING_PKL_ANNOTATION_TEXT_WITH_CPL, cpl), + ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_KIND, string{"trailer"}, cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_VERSION_LABEL_TEXT, cpl->content_version()->label_text, cpl), + ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"hello"}, cpl), + dcp::VerificationNote( + dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_XML, string("no declaration found for element 'Foo'"), path(), 2 + ).set_cpl_id(cpl->id()), + dcp::VerificationNote( dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_XML, string("element 'Foo' is not allowed for content model '(Id,ContentTitleText,AnnotationText?,IssueDate,ReelNumber?,Language?,EditRate,TimeCodeRate,StartTime?,DisplayType?,LoadFont*,SubtitleList)'"), path(), 2 - }, - { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_SUBTITLE_START_TIME, canonical(dir / "subs.mxf") }, - { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() }, - { dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::INVALID_SUBTITLE_ISSUE_DATE, string{"2020-05-09T00:29:21.000+02:00"} }, - { dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::INCORRECT_SUBTITLE_NAMESPACE_COUNT, asset->id() } + ).set_cpl_id(cpl->id()), + dcp::VerificationNote( + dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_SUBTITLE_START_TIME, canonical(dir / "subs.mxf") + ).set_cpl_id(cpl->id()), + dcp::VerificationNote( + dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->file().get() + ).set_cpl_id(cpl->id()), + dcp::VerificationNote( + dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::INVALID_SUBTITLE_ISSUE_DATE, string{"2020-05-09T00:29:21.000+02:00"} + ).set_cpl_id(cpl->id()), + dcp::VerificationNote( + dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::INCORRECT_SUBTITLE_NAMESPACE_COUNT, asset->id() + ).set_cpl_id(cpl->id()), }); } @@ -936,12 +1419,30 @@ BOOST_AUTO_TEST_CASE (verify_empty_text_node_in_subtitles) { dir }, {}, { - { dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::EMPTY_TEXT }, - { dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::INVALID_SUBTITLE_FIRST_TEXT_TIME }, - { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_SUBTITLE_LANGUAGE, canonical(dir / "subs.mxf") }, - { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() }, - { dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::INVALID_SUBTITLE_ISSUE_DATE, string{"2021-08-09T18:34:46.000+02:00"} }, - { dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::INCORRECT_SUBTITLE_NAMESPACE_COUNT, asset->id() } + ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl), + ok(dcp::VerificationNote::Code::MATCHING_PKL_ANNOTATION_TEXT_WITH_CPL, cpl), + ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_KIND, string{"trailer"}, cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_VERSION_LABEL_TEXT, cpl->content_version()->label_text, cpl), + ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"hello"}, cpl), + dcp::VerificationNote( + dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::EMPTY_TEXT + ).set_cpl_id(cpl->id()), + dcp::VerificationNote( + dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::INVALID_SUBTITLE_FIRST_TEXT_TIME + ).set_cpl_id(cpl->id()), + dcp::VerificationNote( + dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_SUBTITLE_LANGUAGE, canonical(dir / "subs.mxf") + ).set_cpl_id(cpl->id()), + dcp::VerificationNote( + dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->file().get() + ).set_cpl_id(cpl->id()), + dcp::VerificationNote( + dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::INVALID_SUBTITLE_ISSUE_DATE, string{"2021-08-09T18:34:46.000+02:00"} + ).set_cpl_id(cpl->id()), + dcp::VerificationNote( + dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::INCORRECT_SUBTITLE_NAMESPACE_COUNT, asset->id() + ).set_cpl_id(cpl->id()) }); } @@ -960,8 +1461,15 @@ BOOST_AUTO_TEST_CASE (verify_empty_text_node_in_subtitles_with_child_nodes) { dir }, {}, { + ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl), + ok(dcp::VerificationNote::Code::MATCHING_PKL_ANNOTATION_TEXT_WITH_CPL, cpl), + ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_KIND, string{"trailer"}, cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_VERSION_LABEL_TEXT, cpl->content_version()->label_text, cpl), { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_STANDARD }, - { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISSING_FONT, string{"font0"} } + dcp::VerificationNote( + dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISSING_FONT, string{"font0"} + ).set_cpl_id(cpl->id()) }); } @@ -980,10 +1488,21 @@ BOOST_AUTO_TEST_CASE (verify_empty_text_node_in_subtitles_with_empty_child_nodes { dir }, {}, { - { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISSING_SUBTITLE, asset->id(), boost::filesystem::canonical(asset->file().get()) }, + ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl), + ok(dcp::VerificationNote::Code::MATCHING_PKL_ANNOTATION_TEXT_WITH_CPL, cpl), + ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_KIND, string{"trailer"}, cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_VERSION_LABEL_TEXT, cpl->content_version()->label_text, cpl), + dcp::VerificationNote( + dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISSING_SUBTITLE, asset->id(), boost::filesystem::canonical(asset->file().get()) + ).set_cpl_id(cpl->id()), { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_STANDARD }, - { dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::EMPTY_TEXT }, - { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISSING_FONT, string{"font0"} }, + dcp::VerificationNote( + dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::EMPTY_TEXT + ).set_cpl_id(cpl->id()), + dcp::VerificationNote( + dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISSING_FONT, string{"font0"} + ).set_cpl_id(cpl->id()) }); } @@ -1011,8 +1530,16 @@ BOOST_AUTO_TEST_CASE (verify_external_asset) { vf_dir }, {}, { + ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl), + ok(dcp::VerificationNote::Code::MATCHING_PKL_ANNOTATION_TEXT_WITH_CPL, cpl), + ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_KIND, string{"trailer"}, cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_VERSION_LABEL_TEXT, cpl->content_version()->label_text, cpl), + ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"hello"}, cpl), { dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::EXTERNAL_ASSET, picture->asset()->id() }, - { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() } + dcp::VerificationNote( + dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->file().get() + ).set_cpl_id(cpl->id()) }); } @@ -1047,38 +1574,6 @@ BOOST_AUTO_TEST_CASE (verify_valid_cpl_metadata) } -path -find_prefix(path dir, string prefix) -{ - auto iter = std::find_if(directory_iterator(dir), directory_iterator(), [prefix](path const& p) { - return boost::starts_with(p.filename().string(), prefix); - }); - - BOOST_REQUIRE(iter != directory_iterator()); - return iter->path(); -} - - -path find_cpl (path dir) -{ - return find_prefix(dir, "cpl_"); -} - - -path -find_pkl(path dir) -{ - return find_prefix(dir, "pkl_"); -} - - -path -find_asset_map(path dir) -{ - return find_prefix(dir, "ASSETMAP"); -} - - /* DCP with invalid CompositionMetadataAsset */ BOOST_AUTO_TEST_CASE (verify_invalid_cpl_metadata_bad_tag) { @@ -1115,9 +1610,26 @@ BOOST_AUTO_TEST_CASE (verify_invalid_cpl_metadata_bad_tag) { dir }, {}, { - { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_XML, string("no declaration found for element 'meta:MainSoundXConfiguration'"), canonical(cpl->file().get()), 50 }, - { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_XML, string("no declaration found for element 'meta:MainSoundXSampleRate'"), canonical(cpl->file().get()), 51 }, - { + ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "pic.mxf"), cpl), + ok(dcp::VerificationNote::Code::MATCHING_PKL_ANNOTATION_TEXT_WITH_CPL, cpl), + ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl), + dcp::VerificationNote( + dcp::VerificationNote::Type::OK, + dcp::VerificationNote::Code::VALID_MAIN_PICTURE_ACTIVE_AREA, + string{"1440x1080"}, + cpl->file().get() + ).set_cpl_id(cpl->id()), + ok(dcp::VerificationNote::Code::VALID_CONTENT_KIND, string{"trailer"}, cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_VERSION_LABEL_TEXT, cpl->content_version()->label_text, cpl), + ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"hello"}, cpl), + ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "pic.mxf"), cpl), + dcp::VerificationNote( + dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_XML, string("no declaration found for element 'meta:MainSoundXConfiguration'"), canonical(cpl->file().get()), 50 + ).set_cpl_id(cpl->id()), + dcp::VerificationNote( + dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_XML, string("no declaration found for element 'meta:MainSoundXSampleRate'"), canonical(cpl->file().get()), 51 + ).set_cpl_id(cpl->id()), + dcp::VerificationNote( dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_XML, string("element 'meta:MainSoundXConfiguration' is not allowed for content model " @@ -1127,11 +1639,10 @@ BOOST_AUTO_TEST_CASE (verify_invalid_cpl_metadata_bad_tag) "MainSoundSampleRate,MainPictureStoredArea,MainPictureActiveArea,MainSubtitleLanguageList?," "ExtensionMetadataList?,)'"), canonical(cpl->file().get()), - 71 - }, + 71).set_cpl_id(cpl->id()), dcp::VerificationNote( - dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_CPL_HASHES, cpl->id(), canonical(cpl->file().get()) - ).set_reference_hash(calc.old_hash()).set_calculated_hash(calc.new_hash()) + dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_CPL_HASHES, canonical(cpl->file().get()) + ).set_cpl_id(cpl->id()).set_reference_hash(calc.old_hash()).set_calculated_hash(calc.new_hash()) }); } @@ -1185,9 +1696,21 @@ BOOST_AUTO_TEST_CASE (verify_invalid_language1) { dir }, {}, { - { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_LANGUAGE, string("badlang") }, - { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_LANGUAGE, string("wrong-andbad") }, - { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() }, + ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl), + ok(dcp::VerificationNote::Code::MATCHING_PKL_ANNOTATION_TEXT_WITH_CPL, cpl), + ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_KIND, string{"trailer"}, cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_VERSION_LABEL_TEXT, cpl->content_version()->label_text, cpl), + ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"hello"}, cpl), + dcp::VerificationNote( + dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_LANGUAGE, string("badlang") + ).set_cpl_id(cpl->id()), + dcp::VerificationNote( + dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_LANGUAGE, string("wrong-andbad") + ).set_cpl_id(cpl->id()), + dcp::VerificationNote( + dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->file().get() + ).set_cpl_id(cpl->id()) }); } @@ -1209,9 +1732,21 @@ BOOST_AUTO_TEST_CASE (verify_invalid_language2) {dir}, {}, { - { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_LANGUAGE, string("badlang") }, - { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_LANGUAGE, string("wrong-andbad") }, - { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() } + ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl), + ok(dcp::VerificationNote::Code::MATCHING_PKL_ANNOTATION_TEXT_WITH_CPL, cpl), + ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_KIND, string{"trailer"}, cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_VERSION_LABEL_TEXT, cpl->content_version()->label_text, cpl), + ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"hello"}, cpl), + dcp::VerificationNote( + dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_LANGUAGE, string("badlang") + ).set_cpl_id(cpl->id()), + dcp::VerificationNote( + dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_LANGUAGE, string("wrong-andbad") + ).set_cpl_id(cpl->id()), + dcp::VerificationNote( + dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->file().get() + ).set_cpl_id(cpl->id()) }); } @@ -1252,16 +1787,38 @@ BOOST_AUTO_TEST_CASE (verify_invalid_language3) { dir }, {}, { - { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_LANGUAGE, string("this-is-wrong") }, - { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_LANGUAGE, string("andso-is-this") }, - { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_LANGUAGE, string("fred-jim") }, - { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_LANGUAGE, string("frobozz") }, + ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "videofoo.mxf"), cpl), + ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl), + ok(dcp::VerificationNote::Code::MATCHING_PKL_ANNOTATION_TEXT_WITH_CPL, cpl), + ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl), + dcp::VerificationNote( + dcp::VerificationNote::Type::OK, + dcp::VerificationNote::Code::VALID_MAIN_PICTURE_ACTIVE_AREA, + string{"1440x1080"}, + cpl->file().get() + ).set_cpl_id(cpl->id()), + ok(dcp::VerificationNote::Code::VALID_CONTENT_KIND, string{"trailer"}, cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_VERSION_LABEL_TEXT, cpl->content_version()->label_text, cpl), + ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"hello"}, cpl), + ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "videofoo.mxf"), cpl), + dcp::VerificationNote( + dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_LANGUAGE, string("this-is-wrong") + ).set_cpl_id(cpl->id()), + dcp::VerificationNote( + dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_LANGUAGE, string("andso-is-this") + ).set_cpl_id(cpl->id()), + dcp::VerificationNote( + dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_LANGUAGE, string("fred-jim") + ).set_cpl_id(cpl->id()), + dcp::VerificationNote( + dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_LANGUAGE, string("frobozz") + ).set_cpl_id(cpl->id()), }); } static -vector<dcp::VerificationNote> +std::tuple<vector<dcp::VerificationNote>, shared_ptr<dcp::CPL>, boost::filesystem::path> check_picture_size (int width, int height, int frame_rate, bool three_d) { using namespace boost::filesystem; @@ -1269,13 +1826,13 @@ check_picture_size (int width, int height, int frame_rate, bool three_d) path dcp_path = "build/test/verify_picture_test"; prepare_directory (dcp_path); - shared_ptr<dcp::PictureAsset> mp; + shared_ptr<dcp::J2KPictureAsset> mp; if (three_d) { - mp = make_shared<dcp::StereoPictureAsset>(dcp::Fraction(frame_rate, 1), dcp::Standard::SMPTE); + mp = make_shared<dcp::StereoJ2KPictureAsset>(dcp::Fraction(frame_rate, 1), dcp::Standard::SMPTE); } else { - mp = make_shared<dcp::MonoPictureAsset>(dcp::Fraction(frame_rate, 1), dcp::Standard::SMPTE); + mp = make_shared<dcp::MonoJ2KPictureAsset>(dcp::Fraction(frame_rate, 1), dcp::Standard::SMPTE); } - auto picture_writer = mp->start_write(dcp_path / "video.mxf", dcp::PictureAsset::Behaviour::MAKE_NEW); + auto picture_writer = mp->start_write(dcp_path / "video.mxf", dcp::Behaviour::MAKE_NEW); auto image = black_image (dcp::Size(width, height)); auto j2c = dcp::compress_j2k (image, 100000000, frame_rate, three_d, width > 2048); @@ -1298,9 +1855,9 @@ check_picture_size (int width, int height, int frame_rate, bool three_d) auto reel = make_shared<dcp::Reel>(); if (three_d) { - reel->add (make_shared<dcp::ReelStereoPictureAsset>(std::dynamic_pointer_cast<dcp::StereoPictureAsset>(mp), 0)); + reel->add (make_shared<dcp::ReelStereoPictureAsset>(std::dynamic_pointer_cast<dcp::StereoJ2KPictureAsset>(mp), 0)); } else { - reel->add (make_shared<dcp::ReelMonoPictureAsset>(std::dynamic_pointer_cast<dcp::MonoPictureAsset>(mp), 0)); + reel->add (make_shared<dcp::ReelMonoPictureAsset>(std::dynamic_pointer_cast<dcp::MonoJ2KPictureAsset>(mp), 0)); } reel->add (simple_markers(frame_rate)); @@ -1311,7 +1868,8 @@ check_picture_size (int width, int height, int frame_rate, bool three_d) d->set_annotation_text("A Test DCP"); d->write_xml(); - return dcp::verify({dcp_path}, {}, &stage, &progress, {}, xsd_test); + /* It seems that for the Ubuntu 16.04 compiler we can't use an initializer list here */ + return std::tuple<vector<dcp::VerificationNote>, shared_ptr<dcp::CPL>, boost::filesystem::path>{ dcp::verify({dcp_path}, {}, &stage, &progress, {}, xsd_test).notes, cpl, dcp_path }; } @@ -1319,8 +1877,28 @@ static void check_picture_size_ok (int width, int height, int frame_rate, bool three_d) { - auto notes = check_picture_size(width, height, frame_rate, three_d); - BOOST_CHECK_EQUAL (notes.size(), 0U); + vector<dcp::VerificationNote> notes; + shared_ptr<dcp::CPL> cpl; + boost::filesystem::path dir; + std::tie(notes, cpl, dir) = check_picture_size(width, height, frame_rate, three_d); + + std::vector<dcp::VerificationNote> expected = { + ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_VERSION_LABEL_TEXT, cpl->content_version()->label_text, cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_KIND, string{"trailer"}, cpl), + ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"A Test DCP"}, cpl), + ok(dcp::VerificationNote::Code::MATCHING_PKL_ANNOTATION_TEXT_WITH_CPL, cpl), + ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl), + dcp::VerificationNote( + dcp::VerificationNote::Type::OK, + dcp::VerificationNote::Code::VALID_MAIN_PICTURE_ACTIVE_AREA, + dcp::String::compose("%1x%2", width, height), + cpl->file().get() + ).set_cpl_id(cpl->id()), + ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "video.mxf"), cpl), + ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "video.mxf"), cpl) + }; + check_verify_result(notes, expected); } @@ -1328,10 +1906,31 @@ static void check_picture_size_bad_frame_size (int width, int height, int frame_rate, bool three_d) { - auto notes = check_picture_size(width, height, frame_rate, three_d); - BOOST_REQUIRE_EQUAL (notes.size(), 1U); - BOOST_CHECK_EQUAL (notes.front().type(), dcp::VerificationNote::Type::BV21_ERROR); - BOOST_CHECK_EQUAL (notes.front().code(), dcp::VerificationNote::Code::INVALID_PICTURE_SIZE_IN_PIXELS); + vector<dcp::VerificationNote> notes; + shared_ptr<dcp::CPL> cpl; + boost::filesystem::path dir; + std::tie(notes, cpl, dir) = check_picture_size(width, height, frame_rate, three_d); + + std::vector<dcp::VerificationNote> expected = { + ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_VERSION_LABEL_TEXT, cpl->content_version()->label_text, cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_KIND, string{"trailer"}, cpl), + ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"A Test DCP"}, cpl), + ok(dcp::VerificationNote::Code::MATCHING_PKL_ANNOTATION_TEXT_WITH_CPL, cpl), + ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl), + dcp::VerificationNote( + dcp::VerificationNote::Type::OK, + dcp::VerificationNote::Code::VALID_MAIN_PICTURE_ACTIVE_AREA, + dcp::String::compose("%1x%2", width, height), + cpl->file().get() + ).set_cpl_id(cpl->id()), + ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "video.mxf"), cpl), + ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "video.mxf"), cpl), + dcp::VerificationNote( + dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_PICTURE_SIZE_IN_PIXELS, dcp::String::compose("%1x%2", width, height), canonical(dir / "video.mxf") + ).set_cpl_id(cpl->id()) + }; + check_verify_result(notes, expected); } @@ -1339,10 +1938,35 @@ static void check_picture_size_bad_2k_frame_rate (int width, int height, int frame_rate, bool three_d) { - auto notes = check_picture_size(width, height, frame_rate, three_d); - BOOST_REQUIRE_EQUAL (notes.size(), 2U); - BOOST_CHECK_EQUAL (notes.back().type(), dcp::VerificationNote::Type::BV21_ERROR); - BOOST_CHECK_EQUAL (notes.back().code(), dcp::VerificationNote::Code::INVALID_PICTURE_FRAME_RATE_FOR_2K); + vector<dcp::VerificationNote> notes; + shared_ptr<dcp::CPL> cpl; + boost::filesystem::path dir; + std::tie(notes, cpl, dir) = check_picture_size(width, height, frame_rate, three_d); + + std::vector<dcp::VerificationNote> expected = { + ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_VERSION_LABEL_TEXT, cpl->content_version()->label_text, cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_KIND, string{"trailer"}, cpl), + ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"A Test DCP"}, cpl), + ok(dcp::VerificationNote::Code::MATCHING_PKL_ANNOTATION_TEXT_WITH_CPL, cpl), + ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl), + dcp::VerificationNote( + dcp::VerificationNote::Type::OK, + dcp::VerificationNote::Code::VALID_MAIN_PICTURE_ACTIVE_AREA, + dcp::String::compose("%1x%2", width, height), + cpl->file().get() + ).set_cpl_id(cpl->id()), + ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "video.mxf"), cpl), + ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "video.mxf"), cpl), + dcp::VerificationNote( + dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_PICTURE_FRAME_RATE, dcp::String::compose("%1/1", frame_rate * (three_d ? 2 : 1)) + ).set_cpl_id(cpl->id()), + dcp::VerificationNote( + dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_PICTURE_FRAME_RATE_FOR_2K, dcp::String::compose("%1/1", frame_rate), canonical(dir / "video.mxf") + ).set_cpl_id(cpl->id()) + }; + + check_verify_result(notes, expected); } @@ -1350,10 +1974,32 @@ static void check_picture_size_bad_4k_frame_rate (int width, int height, int frame_rate, bool three_d) { - auto notes = check_picture_size(width, height, frame_rate, three_d); - BOOST_REQUIRE_EQUAL (notes.size(), 1U); - BOOST_CHECK_EQUAL (notes.front().type(), dcp::VerificationNote::Type::BV21_ERROR); - BOOST_CHECK_EQUAL (notes.front().code(), dcp::VerificationNote::Code::INVALID_PICTURE_FRAME_RATE_FOR_4K); + vector<dcp::VerificationNote> notes; + shared_ptr<dcp::CPL> cpl; + boost::filesystem::path dir; + std::tie(notes, cpl, dir) = check_picture_size(width, height, frame_rate, three_d); + + std::vector<dcp::VerificationNote> expected = { + ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_VERSION_LABEL_TEXT, cpl->content_version()->label_text, cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_KIND, string{"trailer"}, cpl), + ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"A Test DCP"}, cpl), + ok(dcp::VerificationNote::Code::MATCHING_PKL_ANNOTATION_TEXT_WITH_CPL, cpl), + ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl), + dcp::VerificationNote( + dcp::VerificationNote::Type::OK, + dcp::VerificationNote::Code::VALID_MAIN_PICTURE_ACTIVE_AREA, + dcp::String::compose("%1x%2", width, height), + cpl->file().get() + ).set_cpl_id(cpl->id()), + ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "video.mxf"), cpl), + ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "video.mxf"), cpl), + dcp::VerificationNote( + dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_PICTURE_FRAME_RATE_FOR_4K, dcp::String::compose("%1/1", frame_rate), canonical(dir / "video.mxf") + ).set_cpl_id(cpl->id()) + }; + + check_verify_result(notes, expected); } @@ -1399,10 +2045,28 @@ BOOST_AUTO_TEST_CASE (verify_picture_size) check_picture_size_bad_4k_frame_rate (3996, 2160, 48, false); /* No 4K 3D */ - auto notes = check_picture_size(3996, 2160, 24, true); - BOOST_REQUIRE_EQUAL (notes.size(), 1U); - BOOST_CHECK_EQUAL (notes.front().type(), dcp::VerificationNote::Type::BV21_ERROR); - BOOST_CHECK_EQUAL (notes.front().code(), dcp::VerificationNote::Code::INVALID_PICTURE_ASSET_RESOLUTION_FOR_3D); + vector<dcp::VerificationNote> notes; + shared_ptr<dcp::CPL> cpl; + boost::filesystem::path dir; + std::tie(notes, cpl, dir) = check_picture_size(3996, 2160, 24, true); + + std::vector<dcp::VerificationNote> expected = { + ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_VERSION_LABEL_TEXT, cpl->content_version()->label_text, cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_KIND, string{"trailer"}, cpl), + ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"A Test DCP"}, cpl), + ok(dcp::VerificationNote::Code::MATCHING_PKL_ANNOTATION_TEXT_WITH_CPL, cpl), + ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl), + dcp::VerificationNote( + dcp::VerificationNote::Type::OK, + dcp::VerificationNote::Code::VALID_MAIN_PICTURE_ACTIVE_AREA, + string{"3996x2160"}, + cpl->file().get() + ).set_cpl_id(cpl->id()), + ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "video.mxf"), cpl), + ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "video.mxf"), cpl), + { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_PICTURE_ASSET_RESOLUTION_FOR_3D }, + }; } @@ -1458,15 +2122,27 @@ BOOST_AUTO_TEST_CASE (verify_invalid_closed_caption_xml_size_in_bytes) { dir }, {}, { - { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_SUBTITLE_START_TIME, canonical(dir / "subs.mxf") }, - { + ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl), + ok(dcp::VerificationNote::Code::MATCHING_PKL_ANNOTATION_TEXT_WITH_CPL, cpl), + ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_KIND, string{"trailer"}, cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_VERSION_LABEL_TEXT, cpl->content_version()->label_text, cpl), + ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"hello"}, cpl), + dcp::VerificationNote( + dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_SUBTITLE_START_TIME, canonical(dir / "subs.mxf") + ).set_cpl_id(cpl->id()), + dcp::VerificationNote( dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_CLOSED_CAPTION_XML_SIZE_IN_BYTES, string("419371"), canonical(dir / "subs.mxf") - }, - { dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::INVALID_SUBTITLE_FIRST_TEXT_TIME }, - { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() }, + ).set_cpl_id(cpl->id()), + dcp::VerificationNote( + dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::INVALID_SUBTITLE_FIRST_TEXT_TIME + ).set_cpl_id(cpl->id()), + dcp::VerificationNote( + dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->file().get() + ).set_cpl_id(cpl->id()) }); } @@ -1503,11 +2179,30 @@ verify_timed_text_asset_too_large (string name) { dir }, {}, { - { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_TIMED_TEXT_SIZE_IN_BYTES, string("121695488"), canonical(dir / "subs.mxf") }, - { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_TIMED_TEXT_FONT_SIZE_IN_BYTES, string("121634816"), canonical(dir / "subs.mxf") }, - { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_SUBTITLE_START_TIME, canonical(dir / "subs.mxf") }, - { dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::INVALID_SUBTITLE_FIRST_TEXT_TIME }, - { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() }, + ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl), + ok(dcp::VerificationNote::Code::MATCHING_PKL_ANNOTATION_TEXT_WITH_CPL, cpl), + ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_KIND, string{"trailer"}, cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_VERSION_LABEL_TEXT, cpl->content_version()->label_text, cpl), + ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"hello"}, cpl), + dcp::VerificationNote( + dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_TIMED_TEXT_SIZE_IN_BYTES, string("121698284"), canonical(dir / "subs.mxf") + ).set_cpl_id(cpl->id()), + dcp::VerificationNote( + dcp::VerificationNote::Type::BV21_ERROR, + dcp::VerificationNote::Code::INVALID_TIMED_TEXT_FONT_SIZE_IN_BYTES, + dcp::raw_convert<string>(121634816), + canonical(dir / "subs.mxf") + ).set_cpl_id(cpl->id()), + dcp::VerificationNote( + dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_SUBTITLE_START_TIME, canonical(dir / "subs.mxf") + ).set_cpl_id(cpl->id()), + dcp::VerificationNote( + dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::INVALID_SUBTITLE_FIRST_TEXT_TIME + ).set_cpl_id(cpl->id()), + dcp::VerificationNote( + dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->file().get() + ).set_cpl_id(cpl->id()) }); } @@ -1554,15 +2249,34 @@ BOOST_AUTO_TEST_CASE (verify_missing_subtitle_language) subs->write (dir / "subs.mxf"); auto reel_subs = make_shared<dcp::ReelSMPTESubtitleAsset>(subs, dcp::Fraction(24, 1), 106, 0); - dcp->cpls()[0]->reels()[0]->add(reel_subs); + auto cpl = dcp->cpls()[0]; + cpl->reels()[0]->add(reel_subs); dcp->write_xml(); check_verify_result ( { dir }, {}, { - { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_SUBTITLE_LANGUAGE, canonical(dir / "subs.mxf") }, - { dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::INVALID_SUBTITLE_FIRST_TEXT_TIME } + ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl), + ok(dcp::VerificationNote::Code::MATCHING_PKL_ANNOTATION_TEXT_WITH_CPL, cpl), + ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl), + dcp::VerificationNote( + dcp::VerificationNote::Type::OK, + dcp::VerificationNote::Code::VALID_MAIN_PICTURE_ACTIVE_AREA, + string{"1998x1080"}, + cpl->file().get() + ).set_cpl_id(cpl->id()), + ok(dcp::VerificationNote::Code::VALID_CONTENT_KIND, string{"trailer"}, cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_VERSION_LABEL_TEXT, cpl->content_version()->label_text, cpl), + ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"A Test DCP"}, cpl), + ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "video.mxf"), cpl), + ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "video.mxf"), cpl), + dcp::VerificationNote( + dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_SUBTITLE_LANGUAGE, canonical(dir / "subs.mxf") + ).set_cpl_id(cpl->id()), + dcp::VerificationNote( + dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::INVALID_SUBTITLE_FIRST_TEXT_TIME + ).set_cpl_id(cpl->id()) }); } @@ -1600,9 +2314,31 @@ BOOST_AUTO_TEST_CASE (verify_mismatched_subtitle_languages) { path }, {}, { - { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_SUBTITLE_START_TIME, canonical(path / "subs1.mxf") }, - { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_SUBTITLE_START_TIME, canonical(path / "subs2.mxf") }, - { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISMATCHED_SUBTITLE_LANGUAGES } + ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(path / "video0.mxf"), cpl), + ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(path / "video1.mxf"), cpl), + ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl), + ok(dcp::VerificationNote::Code::MATCHING_PKL_ANNOTATION_TEXT_WITH_CPL, cpl), + ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl), + dcp::VerificationNote( + dcp::VerificationNote::Type::OK, + dcp::VerificationNote::Code::VALID_MAIN_PICTURE_ACTIVE_AREA, + string{"1998x1080"}, + cpl->file().get() + ).set_cpl_id(cpl->id()), + ok(dcp::VerificationNote::Code::VALID_CONTENT_KIND, string{"trailer"}, cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_VERSION_LABEL_TEXT, cpl->content_version()->label_text, cpl), + ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"A Test DCP"}, cpl), + ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(path / "video0.mxf"), cpl), + ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(path / "video1.mxf"), cpl), + dcp::VerificationNote( + dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_SUBTITLE_START_TIME, canonical(path / "subs1.mxf") + ).set_cpl_id(cpl->id()), + dcp::VerificationNote( + dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_SUBTITLE_START_TIME, canonical(path / "subs2.mxf") + ).set_cpl_id(cpl->id()), + dcp::VerificationNote( + dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISMATCHED_SUBTITLE_LANGUAGES + ).set_cpl_id(cpl->id()), }); } @@ -1640,8 +2376,28 @@ BOOST_AUTO_TEST_CASE (verify_multiple_closed_caption_languages_allowed) { path }, {}, { - { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_SUBTITLE_START_TIME, canonical(path / "subs1.mxf") }, - { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_SUBTITLE_START_TIME, canonical(path / "subs2.mxf") } + ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl), + ok(dcp::VerificationNote::Code::MATCHING_PKL_ANNOTATION_TEXT_WITH_CPL, cpl), + ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl), + dcp::VerificationNote( + dcp::VerificationNote::Type::OK, + dcp::VerificationNote::Code::VALID_MAIN_PICTURE_ACTIVE_AREA, + string{"1998x1080"}, + cpl->file().get() + ).set_cpl_id(cpl->id()), + ok(dcp::VerificationNote::Code::VALID_CONTENT_KIND, string{"trailer"}, cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_VERSION_LABEL_TEXT, cpl->content_version()->label_text, cpl), + ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"A Test DCP"}, cpl), + ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(path / "video0.mxf"), cpl), + ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(path / "video1.mxf"), cpl), + ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(path / "video0.mxf"), cpl), + ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(path / "video1.mxf"), cpl), + dcp::VerificationNote( + dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_SUBTITLE_START_TIME, canonical(path / "subs1.mxf") + ).set_cpl_id(cpl->id()), + dcp::VerificationNote( + dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_SUBTITLE_START_TIME, canonical(path / "subs2.mxf") + ).set_cpl_id(cpl->id()) }); } @@ -1681,15 +2437,34 @@ BOOST_AUTO_TEST_CASE (verify_missing_subtitle_start_time) subs->write (dir / "subs.mxf"); auto reel_subs = make_shared<dcp::ReelSMPTESubtitleAsset>(subs, dcp::Fraction(24, 1), 106, 0); - dcp->cpls()[0]->reels()[0]->add(reel_subs); + auto cpl = dcp->cpls()[0]; + cpl->reels()[0]->add(reel_subs); dcp->write_xml(); check_verify_result ( { dir }, {}, { - { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_SUBTITLE_START_TIME, canonical(dir / "subs.mxf") }, - { dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::INVALID_SUBTITLE_FIRST_TEXT_TIME } + ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "video.mxf"), cpl), + ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl), + ok(dcp::VerificationNote::Code::MATCHING_PKL_ANNOTATION_TEXT_WITH_CPL, cpl), + ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl), + dcp::VerificationNote( + dcp::VerificationNote::Type::OK, + dcp::VerificationNote::Code::VALID_MAIN_PICTURE_ACTIVE_AREA, + string{"1998x1080"}, + cpl->file().get() + ).set_cpl_id(cpl->id()), + ok(dcp::VerificationNote::Code::VALID_CONTENT_KIND, string{"trailer"}, cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_VERSION_LABEL_TEXT, cpl->content_version()->label_text, cpl), + ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"A Test DCP"}, cpl), + ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "video.mxf"), cpl), + dcp::VerificationNote( + dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_SUBTITLE_START_TIME, canonical(dir / "subs.mxf") + ).set_cpl_id(cpl->id()), + dcp::VerificationNote( + dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::INVALID_SUBTITLE_FIRST_TEXT_TIME + ).set_cpl_id(cpl->id()) }); } @@ -1730,15 +2505,34 @@ BOOST_AUTO_TEST_CASE (verify_invalid_subtitle_start_time) subs->write (dir / "subs.mxf"); auto reel_subs = make_shared<dcp::ReelSMPTESubtitleAsset>(subs, dcp::Fraction(24, 1), 106, 0); - dcp->cpls().front()->reels().front()->add(reel_subs); + auto cpl = dcp->cpls()[0]; + cpl->reels().front()->add(reel_subs); dcp->write_xml(); check_verify_result ( { dir }, {}, { - { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_SUBTITLE_START_TIME, canonical(dir / "subs.mxf") }, - { dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::INVALID_SUBTITLE_FIRST_TEXT_TIME } + ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl), + ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl), + ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"A Test DCP"}, cpl), + ok(dcp::VerificationNote::Code::MATCHING_PKL_ANNOTATION_TEXT_WITH_CPL, cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_KIND, string{"trailer"}, cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_VERSION_LABEL_TEXT, cpl->content_version()->label_text, cpl), + ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "video.mxf"), cpl), + dcp::VerificationNote( + dcp::VerificationNote::Type::OK, + dcp::VerificationNote::Code::VALID_MAIN_PICTURE_ACTIVE_AREA, + string{"1998x1080"}, + cpl->file().get() + ).set_cpl_id(cpl->id()), + ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "video.mxf"), cpl), + dcp::VerificationNote( + dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_SUBTITLE_START_TIME, canonical(dir / "subs.mxf") + ).set_cpl_id(cpl->id()), + dcp::VerificationNote( + dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::INVALID_SUBTITLE_FIRST_TEXT_TIME + ).set_cpl_id(cpl->id()) }); } @@ -1831,8 +2625,18 @@ BOOST_AUTO_TEST_CASE (verify_invalid_subtitle_first_text_time) { dir }, {}, { - { dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::INVALID_SUBTITLE_FIRST_TEXT_TIME }, - { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() } + ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl), + ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl), + ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"hello"}, cpl), + ok(dcp::VerificationNote::Code::MATCHING_PKL_ANNOTATION_TEXT_WITH_CPL, cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_KIND, string{"trailer"}, cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_VERSION_LABEL_TEXT, cpl->content_version()->label_text, cpl), + dcp::VerificationNote( + dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::INVALID_SUBTITLE_FIRST_TEXT_TIME + ).set_cpl_id(cpl->id()), + dcp::VerificationNote( + dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->file().get() + ).set_cpl_id(cpl->id()) }); } @@ -1843,7 +2647,20 @@ BOOST_AUTO_TEST_CASE (verify_valid_subtitle_first_text_time) auto const dir = path("build/test/verify_valid_subtitle_first_text_time"); /* Just late enough */ auto cpl = dcp_with_text<dcp::ReelSMPTESubtitleAsset> (dir, {{ 4 * 24, 5 * 24 }}); - check_verify_result({dir}, {}, {{ dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() }}); + check_verify_result( + {dir}, + {}, + { + ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl), + ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl), + ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"hello"}, cpl), + ok(dcp::VerificationNote::Code::MATCHING_PKL_ANNOTATION_TEXT_WITH_CPL, cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_KIND, string{"trailer"}, cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_VERSION_LABEL_TEXT, cpl->content_version()->label_text, cpl), + dcp::VerificationNote( + dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->file().get() + ).set_cpl_id(cpl->id()) + }); } @@ -1888,7 +2705,20 @@ BOOST_AUTO_TEST_CASE (verify_valid_subtitle_first_text_time_on_second_reel) dcp->set_annotation_text("hello"); dcp->write_xml(); - check_verify_result({dir}, {}, {{ dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() }}); + check_verify_result( + {dir}, + {}, + { + ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl), + ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl), + ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"hello"}, cpl), + ok(dcp::VerificationNote::Code::MATCHING_PKL_ANNOTATION_TEXT_WITH_CPL, cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_KIND, string{"trailer"}, cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_VERSION_LABEL_TEXT, cpl->content_version()->label_text, cpl), + dcp::VerificationNote( + dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->file().get() + ).set_cpl_id(cpl->id()) + }); } @@ -1905,8 +2735,18 @@ BOOST_AUTO_TEST_CASE (verify_invalid_subtitle_spacing) {dir}, {}, { - { dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::INVALID_SUBTITLE_SPACING }, - { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() } + ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl), + ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl), + ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"hello"}, cpl), + ok(dcp::VerificationNote::Code::MATCHING_PKL_ANNOTATION_TEXT_WITH_CPL, cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_KIND, string{"trailer"}, cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_VERSION_LABEL_TEXT, cpl->content_version()->label_text, cpl), + dcp::VerificationNote( + dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::INVALID_SUBTITLE_SPACING + ).set_cpl_id(cpl->id()), + dcp::VerificationNote( + dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->file().get() + ).set_cpl_id(cpl->id()) }); } @@ -1920,7 +2760,21 @@ BOOST_AUTO_TEST_CASE (verify_valid_subtitle_spacing) { 4 * 24, 5 * 24 }, { 5 * 24 + 16, 8 * 24 }, }); - check_verify_result({dir}, {}, {{ dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() }}); + + check_verify_result( + {dir}, + {}, + { + ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl), + ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl), + ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"hello"}, cpl), + ok(dcp::VerificationNote::Code::MATCHING_PKL_ANNOTATION_TEXT_WITH_CPL, cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_KIND, string{"trailer"}, cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_VERSION_LABEL_TEXT, cpl->content_version()->label_text, cpl), + dcp::VerificationNote( + dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->file().get() + ).set_cpl_id(cpl->id()) + }); } @@ -1932,8 +2786,18 @@ BOOST_AUTO_TEST_CASE (verify_invalid_subtitle_duration) {dir}, {}, { - { dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::INVALID_SUBTITLE_DURATION }, - { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() } + ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl), + ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl), + ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"hello"}, cpl), + ok(dcp::VerificationNote::Code::MATCHING_PKL_ANNOTATION_TEXT_WITH_CPL, cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_KIND, string{"trailer"}, cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_VERSION_LABEL_TEXT, cpl->content_version()->label_text, cpl), + dcp::VerificationNote( + dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::INVALID_SUBTITLE_DURATION + ).set_cpl_id(cpl->id()), + dcp::VerificationNote( + dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->file().get() + ).set_cpl_id(cpl->id()) }); } @@ -1942,7 +2806,21 @@ BOOST_AUTO_TEST_CASE (verify_valid_subtitle_duration) { auto const dir = path("build/test/verify_valid_subtitle_duration"); auto cpl = dcp_with_text<dcp::ReelSMPTESubtitleAsset> (dir, {{ 4 * 24, 4 * 24 + 17 }}); - check_verify_result({dir}, {}, {{ dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() }}); + + check_verify_result( + {dir}, + {}, + { + ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl), + ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl), + ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"hello"}, cpl), + ok(dcp::VerificationNote::Code::MATCHING_PKL_ANNOTATION_TEXT_WITH_CPL, cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_KIND, string{"trailer"}, cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_VERSION_LABEL_TEXT, cpl->content_version()->label_text, cpl), + dcp::VerificationNote( + dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->file().get() + ).set_cpl_id(cpl->id()) + }); } @@ -1963,10 +2841,24 @@ BOOST_AUTO_TEST_CASE (verify_subtitle_overlapping_reel_boundary) {dir}, {}, { - { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISMATCHED_TIMED_TEXT_DURATION , "72 96", boost::filesystem::canonical(asset->file().get()) }, - { dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::INVALID_SUBTITLE_FIRST_TEXT_TIME }, - { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::SUBTITLE_OVERLAPS_REEL_BOUNDARY }, - { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() } + ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl), + ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl), + ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"hello"}, cpl), + ok(dcp::VerificationNote::Code::MATCHING_PKL_ANNOTATION_TEXT_WITH_CPL, cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_KIND, string{"trailer"}, cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_VERSION_LABEL_TEXT, cpl->content_version()->label_text, cpl), + dcp::VerificationNote( + dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISMATCHED_TIMED_TEXT_DURATION , "72 96", boost::filesystem::canonical(asset->file().get()) + ).set_cpl_id(cpl->id()), + dcp::VerificationNote( + dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::INVALID_SUBTITLE_FIRST_TEXT_TIME + ).set_cpl_id(cpl->id()), + dcp::VerificationNote( + dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::SUBTITLE_OVERLAPS_REEL_BOUNDARY + ).set_cpl_id(cpl->id()), + dcp::VerificationNote( + dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->file().get() + ).set_cpl_id(cpl->id()) }); } @@ -1987,8 +2879,18 @@ BOOST_AUTO_TEST_CASE (verify_invalid_subtitle_line_count1) {dir}, {}, { - { dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::INVALID_SUBTITLE_LINE_COUNT }, - { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() } + ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl), + ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl), + ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"hello"}, cpl), + ok(dcp::VerificationNote::Code::MATCHING_PKL_ANNOTATION_TEXT_WITH_CPL, cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_KIND, string{"trailer"}, cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_VERSION_LABEL_TEXT, cpl->content_version()->label_text, cpl), + dcp::VerificationNote( + dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::INVALID_SUBTITLE_LINE_COUNT + ).set_cpl_id(cpl->id()), + dcp::VerificationNote( + dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->file().get() + ).set_cpl_id(cpl->id()) }); } @@ -2003,7 +2905,21 @@ BOOST_AUTO_TEST_CASE (verify_valid_subtitle_line_count1) { 96, 200, 0.1, dcp::VAlign::CENTER, "have" }, { 96, 200, 0.2, dcp::VAlign::CENTER, "four" }, }); - check_verify_result({dir}, {}, {{ dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() }}); + + check_verify_result( + {dir}, + {}, + { + ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl), + ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl), + ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"hello"}, cpl), + ok(dcp::VerificationNote::Code::MATCHING_PKL_ANNOTATION_TEXT_WITH_CPL, cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_KIND, string{"trailer"}, cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_VERSION_LABEL_TEXT, cpl->content_version()->label_text, cpl), + dcp::VerificationNote( + dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->file().get() + ).set_cpl_id(cpl->id()) + }); } @@ -2022,8 +2938,18 @@ BOOST_AUTO_TEST_CASE (verify_invalid_subtitle_line_count2) {dir}, {}, { - { dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::INVALID_SUBTITLE_LINE_COUNT }, - { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() } + ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl), + ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl), + ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"hello"}, cpl), + ok(dcp::VerificationNote::Code::MATCHING_PKL_ANNOTATION_TEXT_WITH_CPL, cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_KIND, string{"trailer"}, cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_VERSION_LABEL_TEXT, cpl->content_version()->label_text, cpl), + dcp::VerificationNote( + dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::INVALID_SUBTITLE_LINE_COUNT + ).set_cpl_id(cpl->id()), + dcp::VerificationNote( + dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->file().get() + ).set_cpl_id(cpl->id()) }); } @@ -2039,7 +2965,21 @@ BOOST_AUTO_TEST_CASE (verify_valid_subtitle_line_count2) { 150, 180, 0.2, dcp::VAlign::CENTER, "four" }, { 190, 250, 0.3, dcp::VAlign::CENTER, "lines" } }); - check_verify_result({dir}, {}, {{ dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() }}); + + check_verify_result( + {dir}, + {}, + { + ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl), + ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl), + ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"hello"}, cpl), + ok(dcp::VerificationNote::Code::MATCHING_PKL_ANNOTATION_TEXT_WITH_CPL, cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_KIND, string{"trailer"}, cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_VERSION_LABEL_TEXT, cpl->content_version()->label_text, cpl), + dcp::VerificationNote( + dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->file().get() + ).set_cpl_id(cpl->id()) + }); } @@ -2055,8 +2995,18 @@ BOOST_AUTO_TEST_CASE (verify_invalid_subtitle_line_length1) {dir}, {}, { - { dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::NEARLY_INVALID_SUBTITLE_LINE_LENGTH }, - { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() } + ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl), + ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl), + ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"hello"}, cpl), + ok(dcp::VerificationNote::Code::MATCHING_PKL_ANNOTATION_TEXT_WITH_CPL, cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_KIND, string{"trailer"}, cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_VERSION_LABEL_TEXT, cpl->content_version()->label_text, cpl), + dcp::VerificationNote( + dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::NEARLY_INVALID_SUBTITLE_LINE_LENGTH + ).set_cpl_id(cpl->id()), + dcp::VerificationNote( + dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->file().get() + ).set_cpl_id(cpl->id()) }); } @@ -2073,8 +3023,18 @@ BOOST_AUTO_TEST_CASE (verify_invalid_subtitle_line_length2) {dir}, {}, { - { dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::INVALID_SUBTITLE_LINE_LENGTH }, - { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() } + ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl), + ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl), + ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"hello"}, cpl), + ok(dcp::VerificationNote::Code::MATCHING_PKL_ANNOTATION_TEXT_WITH_CPL, cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_KIND, string{"trailer"}, cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_VERSION_LABEL_TEXT, cpl->content_version()->label_text, cpl), + dcp::VerificationNote( + dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::INVALID_SUBTITLE_LINE_LENGTH + ).set_cpl_id(cpl->id()), + dcp::VerificationNote( + dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->file().get() + ).set_cpl_id(cpl->id()) }); } @@ -2094,8 +3054,18 @@ BOOST_AUTO_TEST_CASE (verify_valid_closed_caption_line_count1) {dir}, {}, { - { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_CLOSED_CAPTION_LINE_COUNT}, - { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() } + ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl), + ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl), + ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"hello"}, cpl), + ok(dcp::VerificationNote::Code::MATCHING_PKL_ANNOTATION_TEXT_WITH_CPL, cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_KIND, string{"trailer"}, cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_VERSION_LABEL_TEXT, cpl->content_version()->label_text, cpl), + dcp::VerificationNote( + dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_CLOSED_CAPTION_LINE_COUNT + ).set_cpl_id(cpl->id()), + dcp::VerificationNote( + dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->file().get() + ).set_cpl_id(cpl->id()) }); } @@ -2110,7 +3080,21 @@ BOOST_AUTO_TEST_CASE (verify_valid_closed_caption_line_count2) { 96, 200, 0.1, dcp::VAlign::CENTER, "have" }, { 96, 200, 0.2, dcp::VAlign::CENTER, "four" }, }); - check_verify_result({dir}, {}, {{ dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() }}); + + check_verify_result( + {dir}, + {}, + { + ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl), + ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl), + ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"hello"}, cpl), + ok(dcp::VerificationNote::Code::MATCHING_PKL_ANNOTATION_TEXT_WITH_CPL, cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_KIND, string{"trailer"}, cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_VERSION_LABEL_TEXT, cpl->content_version()->label_text, cpl), + dcp::VerificationNote( + dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->file().get() + ).set_cpl_id(cpl->id()) + }); } @@ -2129,8 +3113,18 @@ BOOST_AUTO_TEST_CASE (verify_invalid_closed_caption_line_count3) {dir}, {}, { - { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_CLOSED_CAPTION_LINE_COUNT}, - { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() } + ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl), + ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl), + ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"hello"}, cpl), + ok(dcp::VerificationNote::Code::MATCHING_PKL_ANNOTATION_TEXT_WITH_CPL, cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_KIND, string{"trailer"}, cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_VERSION_LABEL_TEXT, cpl->content_version()->label_text, cpl), + dcp::VerificationNote( + dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_CLOSED_CAPTION_LINE_COUNT + ).set_cpl_id(cpl->id()), + dcp::VerificationNote( + dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->file().get() + ).set_cpl_id(cpl->id()) }); } @@ -2146,7 +3140,21 @@ BOOST_AUTO_TEST_CASE (verify_valid_closed_caption_line_count4) { 150, 180, 0.2, dcp::VAlign::CENTER, "four" }, { 190, 250, 0.3, dcp::VAlign::CENTER, "lines" } }); - check_verify_result({dir}, {}, {{ dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() }}); + + check_verify_result( + {dir}, + {}, + { + ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl), + ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl), + ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"hello"}, cpl), + ok(dcp::VerificationNote::Code::MATCHING_PKL_ANNOTATION_TEXT_WITH_CPL, cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_KIND, string{"trailer"}, cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_VERSION_LABEL_TEXT, cpl->content_version()->label_text, cpl), + dcp::VerificationNote( + dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->file().get() + ).set_cpl_id(cpl->id()) + }); } @@ -2158,11 +3166,20 @@ BOOST_AUTO_TEST_CASE (verify_valid_closed_caption_line_length) { { 96, 300, 0.0, dcp::VAlign::CENTER, "01234567890123456789012345678901" } }); + check_verify_result ( {dir}, {}, { - { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() } + ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl), + ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl), + ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"hello"}, cpl), + ok(dcp::VerificationNote::Code::MATCHING_PKL_ANNOTATION_TEXT_WITH_CPL, cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_KIND, string{"trailer"}, cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_VERSION_LABEL_TEXT, cpl->content_version()->label_text, cpl), + dcp::VerificationNote( + dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->file().get() + ).set_cpl_id(cpl->id()) }); } @@ -2179,8 +3196,18 @@ BOOST_AUTO_TEST_CASE (verify_invalid_closed_caption_line_length) {dir}, {}, { - { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_CLOSED_CAPTION_LINE_LENGTH }, - { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() } + ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl), + ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl), + ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"hello"}, cpl), + ok(dcp::VerificationNote::Code::MATCHING_PKL_ANNOTATION_TEXT_WITH_CPL, cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_KIND, string{"trailer"}, cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_VERSION_LABEL_TEXT, cpl->content_version()->label_text, cpl), + dcp::VerificationNote( + dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_CLOSED_CAPTION_LINE_LENGTH + ).set_cpl_id(cpl->id()), + dcp::VerificationNote( + dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->file().get() + ).set_cpl_id(cpl->id()) }); } @@ -2199,7 +3226,15 @@ BOOST_AUTO_TEST_CASE (verify_mismatched_closed_caption_valign1) {dir}, {}, { - { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() } + ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl), + ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl), + ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"hello"}, cpl), + ok(dcp::VerificationNote::Code::MATCHING_PKL_ANNOTATION_TEXT_WITH_CPL, cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_KIND, string{"trailer"}, cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_VERSION_LABEL_TEXT, cpl->content_version()->label_text, cpl), + dcp::VerificationNote( + dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->file().get() + ).set_cpl_id(cpl->id()) }); } @@ -2218,8 +3253,18 @@ BOOST_AUTO_TEST_CASE (verify_mismatched_closed_caption_valign2) {dir}, {}, { - { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_CLOSED_CAPTION_VALIGN }, - { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() } + ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl), + ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl), + ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"hello"}, cpl), + ok(dcp::VerificationNote::Code::MATCHING_PKL_ANNOTATION_TEXT_WITH_CPL, cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_KIND, string{"trailer"}, cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_VERSION_LABEL_TEXT, cpl->content_version()->label_text, cpl), + dcp::VerificationNote( + dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_CLOSED_CAPTION_VALIGN + ).set_cpl_id(cpl->id()), + dcp::VerificationNote( + dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->file().get() + ).set_cpl_id(cpl->id()) }); } @@ -2234,11 +3279,20 @@ BOOST_AUTO_TEST_CASE (verify_incorrect_closed_caption_ordering1) { 96, 300, 0.1, dcp::VAlign::TOP, "is" }, { 96, 300, 0.2, dcp::VAlign::TOP, "fine" }, }); - check_verify_result ( + + check_verify_result( {dir}, {}, { - { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() } + ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl), + ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl), + ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"hello"}, cpl), + ok(dcp::VerificationNote::Code::MATCHING_PKL_ANNOTATION_TEXT_WITH_CPL, cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_KIND, string{"trailer"}, cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_VERSION_LABEL_TEXT, cpl->content_version()->label_text, cpl), + dcp::VerificationNote( + dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->file().get() + ).set_cpl_id(cpl->id()) }); } @@ -2253,11 +3307,20 @@ BOOST_AUTO_TEST_CASE (verify_incorrect_closed_caption_ordering2) { 96, 300, 0.1, dcp::VAlign::BOTTOM, "is" }, { 96, 300, 0.0, dcp::VAlign::BOTTOM, "also fine" }, }); - check_verify_result ( + + check_verify_result( {dir}, {}, { - { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() } + ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl), + ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl), + ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"hello"}, cpl), + ok(dcp::VerificationNote::Code::MATCHING_PKL_ANNOTATION_TEXT_WITH_CPL, cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_KIND, string{"trailer"}, cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_VERSION_LABEL_TEXT, cpl->content_version()->label_text, cpl), + dcp::VerificationNote( + dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->file().get() + ).set_cpl_id(cpl->id()) }); } @@ -2270,8 +3333,18 @@ BOOST_AUTO_TEST_CASE (verify_incorrect_closed_caption_ordering3) {dir}, {}, { - { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INCORRECT_CLOSED_CAPTION_ORDERING }, - { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() } + ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl), + ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl), + ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"hello"}, cpl), + ok(dcp::VerificationNote::Code::MATCHING_PKL_ANNOTATION_TEXT_WITH_CPL, cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_KIND, string{"trailer"}, cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_VERSION_LABEL_TEXT, cpl->content_version()->label_text, cpl), + dcp::VerificationNote( + dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INCORRECT_CLOSED_CAPTION_ORDERING + ).set_cpl_id(cpl->id()), + dcp::VerificationNote( + dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->file().get() + ).set_cpl_id(cpl->id()) }); } @@ -2280,11 +3353,20 @@ BOOST_AUTO_TEST_CASE (verify_incorrect_closed_caption_ordering4) { auto const dir = path ("build/test/verify_incorrect_closed_caption_ordering4"); auto cpl = dcp_with_text_from_file<dcp::ReelSMPTEClosedCaptionAsset> (dir, "test/data/verify_incorrect_closed_caption_ordering4.xml"); - check_verify_result ( + + check_verify_result( {dir}, {}, { - { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() } + ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl), + ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl), + ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"hello"}, cpl), + ok(dcp::VerificationNote::Code::MATCHING_PKL_ANNOTATION_TEXT_WITH_CPL, cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_KIND, string{"trailer"}, cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_VERSION_LABEL_TEXT, cpl->content_version()->label_text, cpl), + dcp::VerificationNote( + dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->file().get() + ).set_cpl_id(cpl->id()) }); } @@ -2314,8 +3396,20 @@ BOOST_AUTO_TEST_CASE (verify_invalid_sound_frame_rate) {dir}, {}, { - { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_SOUND_FRAME_RATE, string("96000"), canonical(dir / "audiofoo.mxf") }, - { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() }, + ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl), + ok(dcp::VerificationNote::Code::MATCHING_PKL_ANNOTATION_TEXT_WITH_CPL, cpl), + ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"hello"}, cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_VERSION_LABEL_TEXT, cpl->content_version()->label_text, cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_KIND, string{"trailer"}, cpl), + ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "videofoo.mxf"), cpl), + ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl), + ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "videofoo.mxf"), cpl), + dcp::VerificationNote( + dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_SOUND_FRAME_RATE, string("96000"), canonical(dir / "audiofoo.mxf") + ).set_cpl_id(cpl->id()), + dcp::VerificationNote( + dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->file().get() + ).set_cpl_id(cpl->id()) }); } @@ -2342,10 +3436,24 @@ BOOST_AUTO_TEST_CASE (verify_missing_cpl_annotation_text) {dir}, {}, { - { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_ANNOTATION_TEXT, cpl->id(), canonical(cpl->file().get()) }, + ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl), + ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "video.mxf"), cpl), + dcp::VerificationNote( + dcp::VerificationNote::Type::OK, + dcp::VerificationNote::Code::VALID_MAIN_PICTURE_ACTIVE_AREA, + string{"1998x1080"}, + cpl->file().get() + ).set_cpl_id(cpl->id()), + ok(dcp::VerificationNote::Code::VALID_CONTENT_VERSION_LABEL_TEXT, cpl->content_version()->label_text, cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_KIND, string{"trailer"}, cpl), + ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "video.mxf"), cpl), + ok(dcp::VerificationNote::Code::MATCHING_PKL_ANNOTATION_TEXT_WITH_CPL, cpl), + dcp::VerificationNote( + dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_ANNOTATION_TEXT, canonical(cpl->file().get()) + ).set_cpl_id(cpl->id()), dcp::VerificationNote( - dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_CPL_HASHES, cpl->id(), canonical(cpl->file().get()) - ).set_reference_hash(calc.old_hash()).set_calculated_hash(calc.new_hash()) + dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_CPL_HASHES, canonical(cpl->file().get()) + ).set_cpl_id(cpl->id()).set_reference_hash(calc.old_hash()).set_calculated_hash(calc.new_hash()) }); } @@ -2371,10 +3479,24 @@ BOOST_AUTO_TEST_CASE (verify_mismatched_cpl_annotation_text) {dir}, {}, { - { dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::MISMATCHED_CPL_ANNOTATION_TEXT, cpl->id(), canonical(cpl->file().get()) }, + ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl), + ok(dcp::VerificationNote::Code::MATCHING_PKL_ANNOTATION_TEXT_WITH_CPL, cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_KIND, string{"trailer"}, cpl), + ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "video.mxf"), cpl), + ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "video.mxf"), cpl), dcp::VerificationNote( - dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_CPL_HASHES, cpl->id(), canonical(cpl->file().get()) - ).set_reference_hash(calc.old_hash()).set_calculated_hash(calc.new_hash()) + dcp::VerificationNote::Type::OK, + dcp::VerificationNote::Code::VALID_MAIN_PICTURE_ACTIVE_AREA, + string{"1998x1080"}, + cpl->file().get() + ).set_cpl_id(cpl->id()), + ok(dcp::VerificationNote::Code::VALID_CONTENT_VERSION_LABEL_TEXT, cpl->content_version()->label_text, cpl), + dcp::VerificationNote( + dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::MISMATCHED_CPL_ANNOTATION_TEXT, canonical(cpl->file().get()) + ).set_cpl_id(cpl->id()), + dcp::VerificationNote( + dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_CPL_HASHES, canonical(cpl->file().get()) + ).set_reference_hash(calc.old_hash()).set_calculated_hash(calc.new_hash()).set_cpl_id(cpl->id()) }); } @@ -2386,7 +3508,7 @@ BOOST_AUTO_TEST_CASE (verify_mismatched_asset_duration) shared_ptr<dcp::DCP> dcp (new dcp::DCP(dir)); auto cpl = make_shared<dcp::CPL>("A Test DCP", dcp::ContentKind::TRAILER, dcp::Standard::SMPTE); - shared_ptr<dcp::MonoPictureAsset> mp = simple_picture (dir, "", 24); + shared_ptr<dcp::MonoJ2KPictureAsset> mp = simple_picture (dir, "", 24); shared_ptr<dcp::SoundAsset> ms = simple_sound (dir, "", dcp::MXFMetadata(), "en-US", 25); auto reel = make_shared<dcp::Reel>( @@ -2405,8 +3527,20 @@ BOOST_AUTO_TEST_CASE (verify_mismatched_asset_duration) {dir}, {}, { - { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISMATCHED_ASSET_DURATION }, - { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), canonical(cpl->file().get()) } + ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl), + ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"A Test DCP"}, cpl), + ok(dcp::VerificationNote::Code::MATCHING_PKL_ANNOTATION_TEXT_WITH_CPL, cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_KIND, string{"trailer"}, cpl), + ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "video.mxf"), cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_VERSION_LABEL_TEXT, cpl->content_version()->label_text, cpl), + ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl), + ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "video.mxf"), cpl), + dcp::VerificationNote( + dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISMATCHED_ASSET_DURATION + ).set_cpl_id(cpl->id()), + dcp::VerificationNote( + dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, canonical(cpl->file().get()) + ).set_cpl_id(cpl->id()) }); } @@ -2477,8 +3611,22 @@ BOOST_AUTO_TEST_CASE (verify_missing_main_subtitle_from_some_reels) { dir }, {}, { - { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_MAIN_SUBTITLE_FROM_SOME_REELS }, - { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() }, + ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl), + ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"A Test DCP"}, cpl), + ok(dcp::VerificationNote::Code::MATCHING_PKL_ANNOTATION_TEXT_WITH_CPL, cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_KIND, string{"trailer"}, cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_VERSION_LABEL_TEXT, cpl->content_version()->label_text, cpl), + ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "video1.mxf"), cpl), + ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "video2.mxf"), cpl), + ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl), + ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "video1.mxf"), cpl), + ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "video2.mxf"), cpl), + dcp::VerificationNote( + dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_MAIN_SUBTITLE_FROM_SOME_REELS + ).set_cpl_id(cpl->id()), + dcp::VerificationNote( + dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->file().get() + ).set_cpl_id(cpl->id()) }); } @@ -2486,13 +3634,47 @@ BOOST_AUTO_TEST_CASE (verify_missing_main_subtitle_from_some_reels) { path dir ("build/test/verify_subtitles_must_be_in_all_reels2"); auto cpl = verify_subtitles_must_be_in_all_reels_check (dir, true, true); - check_verify_result({dir}, {}, {{ dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() }}); + check_verify_result( + {dir}, + {}, + { + ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl), + ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"A Test DCP"}, cpl), + ok(dcp::VerificationNote::Code::MATCHING_PKL_ANNOTATION_TEXT_WITH_CPL, cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_KIND, string{"trailer"}, cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_VERSION_LABEL_TEXT, cpl->content_version()->label_text, cpl), + ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "video1.mxf"), cpl), + ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "video2.mxf"), cpl), + ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl), + ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "video1.mxf"), cpl), + ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "video2.mxf"), cpl), + dcp::VerificationNote( + dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->file().get() + ).set_cpl_id(cpl->id()) + }); } { path dir ("build/test/verify_subtitles_must_be_in_all_reels1"); auto cpl = verify_subtitles_must_be_in_all_reels_check (dir, false, false); - check_verify_result({dir}, {}, {{ dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() }}); + check_verify_result( + {dir}, + {}, + { + ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl), + ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"A Test DCP"}, cpl), + ok(dcp::VerificationNote::Code::MATCHING_PKL_ANNOTATION_TEXT_WITH_CPL, cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_KIND, string{"trailer"}, cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_VERSION_LABEL_TEXT, cpl->content_version()->label_text, cpl), + ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "video1.mxf"), cpl), + ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "video2.mxf"), cpl), + ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl), + ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "video1.mxf"), cpl), + ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "video2.mxf"), cpl), + dcp::VerificationNote( + dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->file().get() + ).set_cpl_id(cpl->id()) + }); } } @@ -2561,21 +3743,69 @@ BOOST_AUTO_TEST_CASE (verify_mismatched_closed_caption_asset_counts) {dir}, {}, { - { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISMATCHED_CLOSED_CAPTION_ASSET_COUNTS }, - { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() } + ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl), + ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "video1.mxf"), cpl), + ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "video2.mxf"), cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_KIND, string{"trailer"}, cpl), + ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl), + ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "video1.mxf"), cpl), + ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "video2.mxf"), cpl), + ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"A Test DCP"}, cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_VERSION_LABEL_TEXT, cpl->content_version()->label_text, cpl), + ok(dcp::VerificationNote::Code::MATCHING_PKL_ANNOTATION_TEXT_WITH_CPL, cpl), + dcp::VerificationNote( + dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISMATCHED_CLOSED_CAPTION_ASSET_COUNTS + ).set_cpl_id(cpl->id()), + dcp::VerificationNote( + dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->file().get() + ).set_cpl_id(cpl->id()) }); } { path dir ("build/test/verify_closed_captions_must_be_in_all_reels2"); auto cpl = verify_closed_captions_must_be_in_all_reels_check (dir, 4, 4); - check_verify_result({dir}, {}, {{ dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() }}); + check_verify_result( + {dir}, + {}, + { + ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl), + ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "video1.mxf"), cpl), + ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "video2.mxf"), cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_KIND, string{"trailer"}, cpl), + ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl), + ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"A Test DCP"}, cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_VERSION_LABEL_TEXT, cpl->content_version()->label_text, cpl), + ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "video1.mxf"), cpl), + ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "video2.mxf"), cpl), + ok(dcp::VerificationNote::Code::MATCHING_PKL_ANNOTATION_TEXT_WITH_CPL, cpl), + dcp::VerificationNote( + dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->file().get() + ).set_cpl_id(cpl->id()) + }); } { path dir ("build/test/verify_closed_captions_must_be_in_all_reels3"); auto cpl = verify_closed_captions_must_be_in_all_reels_check (dir, 0, 0); - check_verify_result({dir}, {}, {{ dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() }}); + check_verify_result( + {dir}, + {}, + { + ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl), + ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "video1.mxf"), cpl), + ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "video2.mxf"), cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_KIND, string{"trailer"}, cpl), + ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl), + ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"A Test DCP"}, cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_VERSION_LABEL_TEXT, cpl->content_version()->label_text, cpl), + ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "video1.mxf"), cpl), + ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "video2.mxf"), cpl), + ok(dcp::VerificationNote::Code::MATCHING_PKL_ANNOTATION_TEXT_WITH_CPL, cpl), + dcp::VerificationNote( + dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->file().get() + ).set_cpl_id(cpl->id()) + }); } } @@ -2618,8 +3848,20 @@ verify_text_entry_point_check (path dir, dcp::VerificationNote::Code code, boost {dir}, {}, { - { dcp::VerificationNote::Type::BV21_ERROR, code, subs->id() }, - { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() }, + ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl), + ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"A Test DCP"}, cpl), + ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "video.mxf"), cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_VERSION_LABEL_TEXT, cpl->content_version()->label_text, cpl), + ok(dcp::VerificationNote::Code::MATCHING_PKL_ANNOTATION_TEXT_WITH_CPL, cpl), + ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "video.mxf"), cpl), + ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_KIND, string{"trailer"}, cpl), + dcp::VerificationNote( + dcp::VerificationNote::Type::BV21_ERROR, code, subs->id() + ).set_cpl_id(cpl->id()), + dcp::VerificationNote( + dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->file().get() + ).set_cpl_id(cpl->id()) }); } @@ -2686,10 +3928,25 @@ BOOST_AUTO_TEST_CASE (verify_missing_hash) {dir}, {}, { + ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl), dcp::VerificationNote( - dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_CPL_HASHES, cpl->id(), cpl->file().get() - ).set_reference_hash(calc.old_hash()).set_calculated_hash(calc.new_hash()), - { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_HASH, asset_id } + dcp::VerificationNote::Type::OK, + dcp::VerificationNote::Code::VALID_MAIN_PICTURE_ACTIVE_AREA, + string{"1998x1080"}, + cpl->file().get() + ).set_cpl_id(cpl->id()), + ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"A Test DCP"}, cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_KIND, string{"trailer"}, cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_VERSION_LABEL_TEXT, cpl->content_version()->label_text, cpl), + ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "video.mxf"), cpl), + ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "video.mxf"), cpl), + ok(dcp::VerificationNote::Code::MATCHING_PKL_ANNOTATION_TEXT_WITH_CPL, cpl), + dcp::VerificationNote( + dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_CPL_HASHES, cpl->file().get() + ).set_cpl_id(cpl->id()).set_reference_hash(calc.old_hash()).set_calculated_hash(calc.new_hash()), + dcp::VerificationNote( + dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_HASH, asset_id + ).set_cpl_id(cpl->id()) }); } @@ -2703,14 +3960,36 @@ verify_markers_test ( ) { auto dcp = make_simple (dir); - dcp->cpls()[0]->set_content_kind (dcp::ContentKind::FEATURE); + auto cpl = dcp->cpls()[0]; + cpl->set_content_kind(dcp::ContentKind::FEATURE); auto markers_asset = make_shared<dcp::ReelMarkersAsset>(dcp::Fraction(24, 1), 24); for (auto const& i: markers) { markers_asset->set (i.first, i.second); } - dcp->cpls()[0]->reels()[0]->add(markers_asset); + cpl->reels()[0]->add(markers_asset); dcp->write_xml(); + for (auto& note: test_notes) { + note.set_cpl_id(cpl->id()); + } + + test_notes.push_back(ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "video.mxf"), cpl)); + test_notes.push_back(ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl)); + test_notes.push_back(ok(dcp::VerificationNote::Code::MATCHING_PKL_ANNOTATION_TEXT_WITH_CPL, cpl)); + test_notes.push_back(ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl)); + test_notes.push_back( + dcp::VerificationNote( + dcp::VerificationNote::Type::OK, + dcp::VerificationNote::Code::VALID_MAIN_PICTURE_ACTIVE_AREA, + string{"1998x1080"}, + cpl->file().get() + ).set_cpl_id(cpl->id()) + ); + test_notes.push_back(ok(dcp::VerificationNote::Code::VALID_CONTENT_KIND, string{"feature"}, cpl)); + test_notes.push_back(ok(dcp::VerificationNote::Code::VALID_CONTENT_VERSION_LABEL_TEXT, cpl->content_version()->label_text, cpl)); + test_notes.push_back(ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"A Test DCP"}, cpl)); + test_notes.push_back(ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "video.mxf"), cpl)); + check_verify_result({dir}, {}, test_notes); } @@ -2807,7 +4086,28 @@ BOOST_AUTO_TEST_CASE (verify_missing_cpl_metadata_version_number) cpl->unset_version_number(); dcp->write_xml(); - check_verify_result({dir}, {}, {{ dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA_VERSION_NUMBER, cpl->id(), cpl->file().get() }}); + check_verify_result( + {dir}, + {}, + { + ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "video.mxf"), cpl), + ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl), + ok(dcp::VerificationNote::Code::MATCHING_PKL_ANNOTATION_TEXT_WITH_CPL, cpl), + ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl), + dcp::VerificationNote( + dcp::VerificationNote::Type::OK, + dcp::VerificationNote::Code::VALID_MAIN_PICTURE_ACTIVE_AREA, + string{"1998x1080"}, + cpl->file().get() + ).set_cpl_id(cpl->id()), + ok(dcp::VerificationNote::Code::VALID_CONTENT_KIND, string{"trailer"}, cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_VERSION_LABEL_TEXT, cpl->content_version()->label_text, cpl), + ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"A Test DCP"}, cpl), + ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "video.mxf"), cpl), + dcp::VerificationNote( + dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA_VERSION_NUMBER, cpl->file().get() + ).set_cpl_id(cpl->id()) + }); } @@ -2831,10 +4131,25 @@ BOOST_AUTO_TEST_CASE (verify_missing_extension_metadata1) {dir}, {}, { + ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_VERSION_LABEL_TEXT, cpl->content_version()->label_text, cpl), + ok(dcp::VerificationNote::Code::MATCHING_PKL_ANNOTATION_TEXT_WITH_CPL, cpl), + ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"A Test DCP"}, cpl), + ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "video.mxf"), cpl), + ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "video.mxf"), cpl), + dcp::VerificationNote( + dcp::VerificationNote::Type::OK, + dcp::VerificationNote::Code::VALID_MAIN_PICTURE_ACTIVE_AREA, + string{"1998x1080"}, + cpl->file().get() + ).set_cpl_id(cpl->id()), + ok(dcp::VerificationNote::Code::VALID_CONTENT_KIND, string{"trailer"}, cpl), + dcp::VerificationNote( + dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_CPL_HASHES, cpl->file().get() + ).set_cpl_id(cpl->id()).set_reference_hash(calc.old_hash()).set_calculated_hash(calc.new_hash()), dcp::VerificationNote( - dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_CPL_HASHES, cpl->id(), cpl->file().get() - ).set_reference_hash(calc.old_hash()).set_calculated_hash(calc.new_hash()), - { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_EXTENSION_METADATA, cpl->id(), cpl->file().get() } + dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_EXTENSION_METADATA, cpl->file().get() + ).set_cpl_id(cpl->id()) }); } @@ -2858,10 +4173,25 @@ BOOST_AUTO_TEST_CASE (verify_missing_extension_metadata2) {dir}, {}, { + ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_VERSION_LABEL_TEXT, cpl->content_version()->label_text, cpl), + ok(dcp::VerificationNote::Code::MATCHING_PKL_ANNOTATION_TEXT_WITH_CPL, cpl), + ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"A Test DCP"}, cpl), + ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "video.mxf"), cpl), + ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "video.mxf"), cpl), dcp::VerificationNote( - dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_CPL_HASHES, cpl->id(), cpl->file().get() - ).set_reference_hash(calc.old_hash()).set_calculated_hash(calc.new_hash()), - { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_EXTENSION_METADATA, cpl->id(), cpl->file().get() } + dcp::VerificationNote::Type::OK, + dcp::VerificationNote::Code::VALID_MAIN_PICTURE_ACTIVE_AREA, + string{"1998x1080"}, + cpl->file().get() + ).set_cpl_id(cpl->id()), + ok(dcp::VerificationNote::Code::VALID_CONTENT_KIND, string{"trailer"}, cpl), + dcp::VerificationNote( + dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_CPL_HASHES, cpl->file().get() + ).set_cpl_id(cpl->id()).set_reference_hash(calc.old_hash()).set_calculated_hash(calc.new_hash()), + dcp::VerificationNote( + dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_EXTENSION_METADATA, cpl->file().get() + ).set_cpl_id(cpl->id()) }); } @@ -2886,11 +4216,27 @@ BOOST_AUTO_TEST_CASE (verify_invalid_xml_cpl_extension_metadata3) {dir}, {}, { - { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_XML, string("no declaration found for element 'meta:NameX'"), cpl->file().get(), 70 }, - { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_XML, string("element 'meta:NameX' is not allowed for content model '(Name,PropertyList?,)'"), cpl->file().get(), 77 }, + ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_VERSION_LABEL_TEXT, cpl->content_version()->label_text, cpl), + ok(dcp::VerificationNote::Code::MATCHING_PKL_ANNOTATION_TEXT_WITH_CPL, cpl), + ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"A Test DCP"}, cpl), + ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "video.mxf"), cpl), + ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "video.mxf"), cpl), + dcp::VerificationNote( + dcp::VerificationNote::Type::OK, + dcp::VerificationNote::Code::VALID_MAIN_PICTURE_ACTIVE_AREA, + string{"1998x1080"}, + cpl->file().get() + ).set_cpl_id(cpl->id()), + ok(dcp::VerificationNote::Code::VALID_CONTENT_KIND, string{"trailer"}, cpl), dcp::VerificationNote( - dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_CPL_HASHES, cpl->id(), cpl->file().get() - ).set_reference_hash(calc.old_hash()).set_calculated_hash(calc.new_hash()), + dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_XML, string("no declaration found for element 'meta:NameX'"), cpl->file().get(), 70 + ).set_cpl_id(cpl->id()), + dcp::VerificationNote( + dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_XML, string("element 'meta:NameX' is not allowed for content model '(Name,PropertyList?,)'"), cpl->file().get(), 77).set_cpl_id(cpl->id()), + dcp::VerificationNote( + dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_CPL_HASHES, cpl->file().get() + ).set_cpl_id(cpl->id()).set_reference_hash(calc.old_hash()).set_calculated_hash(calc.new_hash()), }); } @@ -2914,10 +4260,25 @@ BOOST_AUTO_TEST_CASE (verify_invalid_extension_metadata1) {dir}, {}, { + ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_VERSION_LABEL_TEXT, cpl->content_version()->label_text, cpl), + ok(dcp::VerificationNote::Code::MATCHING_PKL_ANNOTATION_TEXT_WITH_CPL, cpl), + ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"A Test DCP"}, cpl), + ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "video.mxf"), cpl), + ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "video.mxf"), cpl), + dcp::VerificationNote( + dcp::VerificationNote::Type::OK, + dcp::VerificationNote::Code::VALID_MAIN_PICTURE_ACTIVE_AREA, + string{"1998x1080"}, + cpl->file().get() + ).set_cpl_id(cpl->id()), + ok(dcp::VerificationNote::Code::VALID_CONTENT_KIND, string{"trailer"}, cpl), dcp::VerificationNote( - dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_CPL_HASHES, cpl->id(), cpl->file().get() - ).set_reference_hash(calc.old_hash()).set_calculated_hash(calc.new_hash()), - { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_EXTENSION_METADATA, string("<Name> should be 'Application'"), cpl->file().get() }, + dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_CPL_HASHES, cpl->file().get() + ).set_cpl_id(cpl->id()).set_reference_hash(calc.old_hash()).set_calculated_hash(calc.new_hash()), + dcp::VerificationNote( + dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_EXTENSION_METADATA, string("<Name> should be 'Application'"), cpl->file().get() + ).set_cpl_id(cpl->id()) }); } @@ -2941,10 +4302,25 @@ BOOST_AUTO_TEST_CASE (verify_invalid_extension_metadata2) {dir}, {}, { + ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_VERSION_LABEL_TEXT, cpl->content_version()->label_text, cpl), + ok(dcp::VerificationNote::Code::MATCHING_PKL_ANNOTATION_TEXT_WITH_CPL, cpl), + ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"A Test DCP"}, cpl), + ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "video.mxf"), cpl), + ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "video.mxf"), cpl), + dcp::VerificationNote( + dcp::VerificationNote::Type::OK, + dcp::VerificationNote::Code::VALID_MAIN_PICTURE_ACTIVE_AREA, + string{"1998x1080"}, + cpl->file().get() + ).set_cpl_id(cpl->id()), + ok(dcp::VerificationNote::Code::VALID_CONTENT_KIND, string{"trailer"}, cpl), dcp::VerificationNote( - dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_CPL_HASHES, cpl->id(), cpl->file().get() - ).set_reference_hash(calc.old_hash()).set_calculated_hash(calc.new_hash()), - { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_EXTENSION_METADATA, string("<Name> property should be 'DCP Constraints Profile'"), cpl->file().get() }, + dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_CPL_HASHES, cpl->file().get() + ).set_cpl_id(cpl->id()).set_reference_hash(calc.old_hash()).set_calculated_hash(calc.new_hash()), + dcp::VerificationNote( + dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_EXTENSION_METADATA, string("<Name> property should be 'DCP Constraints Profile'"), cpl->file().get() + ).set_cpl_id(cpl->id()) }); } @@ -2969,11 +4345,28 @@ BOOST_AUTO_TEST_CASE (verify_invalid_xml_cpl_extension_metadata6) {dir}, {}, { - { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_XML, string("no declaration found for element 'meta:ValueX'"), cpl->file().get(), 74 }, - { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_XML, string("element 'meta:ValueX' is not allowed for content model '(Name,Value)'"), cpl->file().get(), 75 }, + ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_VERSION_LABEL_TEXT, cpl->content_version()->label_text, cpl), + ok(dcp::VerificationNote::Code::MATCHING_PKL_ANNOTATION_TEXT_WITH_CPL, cpl), + ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"A Test DCP"}, cpl), + ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "video.mxf"), cpl), + ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "video.mxf"), cpl), + dcp::VerificationNote( + dcp::VerificationNote::Type::OK, + dcp::VerificationNote::Code::VALID_MAIN_PICTURE_ACTIVE_AREA, + string{"1998x1080"}, + cpl->file().get() + ).set_cpl_id(cpl->id()), + ok(dcp::VerificationNote::Code::VALID_CONTENT_KIND, string{"trailer"}, cpl), + dcp::VerificationNote( + dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_XML, string("no declaration found for element 'meta:ValueX'"), cpl->file().get(), 74 + ).set_cpl_id(cpl->id()), + dcp::VerificationNote( + dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_XML, string("element 'meta:ValueX' is not allowed for content model '(Name,Value)'"), cpl->file().get(), 75 + ).set_cpl_id(cpl->id()), dcp::VerificationNote( - dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_CPL_HASHES, cpl->id(), cpl->file().get() - ).set_reference_hash(calc.old_hash()).set_calculated_hash(calc.new_hash()), + dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_CPL_HASHES, cpl->file().get() + ).set_cpl_id(cpl->id()).set_reference_hash(calc.old_hash()).set_calculated_hash(calc.new_hash()) }); } @@ -2997,10 +4390,25 @@ BOOST_AUTO_TEST_CASE (verify_invalid_xml_cpl_extension_metadata7) {dir}, {}, { + ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_VERSION_LABEL_TEXT, cpl->content_version()->label_text, cpl), + ok(dcp::VerificationNote::Code::MATCHING_PKL_ANNOTATION_TEXT_WITH_CPL, cpl), + ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"A Test DCP"}, cpl), + ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "video.mxf"), cpl), + ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "video.mxf"), cpl), + dcp::VerificationNote( + dcp::VerificationNote::Type::OK, + dcp::VerificationNote::Code::VALID_MAIN_PICTURE_ACTIVE_AREA, + string{"1998x1080"}, + cpl->file().get() + ).set_cpl_id(cpl->id()), + ok(dcp::VerificationNote::Code::VALID_CONTENT_KIND, string{"trailer"}, cpl), dcp::VerificationNote( - dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_CPL_HASHES, cpl->id(), cpl->file().get() - ).set_reference_hash(calc.old_hash()).set_calculated_hash(calc.new_hash()), - { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_EXTENSION_METADATA, string("<Value> property should be 'SMPTE-RDD-52:2020-Bv2.1'"), cpl->file().get() }, + dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_CPL_HASHES, cpl->file().get() + ).set_cpl_id(cpl->id()).set_reference_hash(calc.old_hash()).set_calculated_hash(calc.new_hash()), + dcp::VerificationNote( + dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_EXTENSION_METADATA, string("<Value> property should be 'SMPTE-RDD-52:2020-Bv2.1'"), cpl->file().get() + ).set_cpl_id(cpl->id()) }); } @@ -3025,11 +4433,27 @@ BOOST_AUTO_TEST_CASE (verify_invalid_xml_cpl_extension_metadata8) {dir}, {}, { - { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_XML, string("no declaration found for element 'meta:PropertyX'"), cpl->file().get(), 72 }, - { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_XML, string("element 'meta:PropertyX' is not allowed for content model '(Property+)'"), cpl->file().get(), 76 }, + ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_VERSION_LABEL_TEXT, cpl->content_version()->label_text, cpl), + ok(dcp::VerificationNote::Code::MATCHING_PKL_ANNOTATION_TEXT_WITH_CPL, cpl), + ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"A Test DCP"}, cpl), + ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "video.mxf"), cpl), + ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "video.mxf"), cpl), + dcp::VerificationNote( + dcp::VerificationNote::Type::OK, + dcp::VerificationNote::Code::VALID_MAIN_PICTURE_ACTIVE_AREA, + string{"1998x1080"}, + cpl->file().get() + ).set_cpl_id(cpl->id()), + ok(dcp::VerificationNote::Code::VALID_CONTENT_KIND, string{"trailer"}, cpl), + dcp::VerificationNote( + dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_XML, string("no declaration found for element 'meta:PropertyX'"), cpl->file().get(), 72 + ).set_cpl_id(cpl->id()), + dcp::VerificationNote( + dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_XML, string("element 'meta:PropertyX' is not allowed for content model '(Property+)'"), cpl->file().get(), 76).set_cpl_id(cpl->id()), dcp::VerificationNote( - dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_CPL_HASHES, cpl->id(), cpl->file().get() - ).set_reference_hash(calc.old_hash()).set_calculated_hash(calc.new_hash()), + dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_CPL_HASHES, cpl->file().get() + ).set_cpl_id(cpl->id()).set_reference_hash(calc.old_hash()).set_calculated_hash(calc.new_hash()), }); } @@ -3054,11 +4478,28 @@ BOOST_AUTO_TEST_CASE (verify_invalid_xml_cpl_extension_metadata9) {dir}, {}, { - { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_XML, string("no declaration found for element 'meta:PropertyListX'"), cpl->file().get(), 71 }, - { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_XML, string("element 'meta:PropertyListX' is not allowed for content model '(Name,PropertyList?,)'"), cpl->file().get(), 77 }, + ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_VERSION_LABEL_TEXT, cpl->content_version()->label_text, cpl), + ok(dcp::VerificationNote::Code::MATCHING_PKL_ANNOTATION_TEXT_WITH_CPL, cpl), + ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"A Test DCP"}, cpl), + ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "video.mxf"), cpl), + ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "video.mxf"), cpl), + dcp::VerificationNote( + dcp::VerificationNote::Type::OK, + dcp::VerificationNote::Code::VALID_MAIN_PICTURE_ACTIVE_AREA, + string{"1998x1080"}, + cpl->file().get() + ).set_cpl_id(cpl->id()), + ok(dcp::VerificationNote::Code::VALID_CONTENT_KIND, string{"trailer"}, cpl), + dcp::VerificationNote( + dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_XML, string("no declaration found for element 'meta:PropertyListX'"), cpl->file().get(), 71 + ).set_cpl_id(cpl->id()), + dcp::VerificationNote( + dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_XML, string("element 'meta:PropertyListX' is not allowed for content model '(Name,PropertyList?,)'"), cpl->file().get(), 77 + ).set_cpl_id(cpl->id()), dcp::VerificationNote( - dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_CPL_HASHES, cpl->id(), cpl->file().get() - ).set_reference_hash(calc.old_hash()).set_calculated_hash(calc.new_hash()), + dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_CPL_HASHES, cpl->file().get() + ).set_cpl_id(cpl->id()).set_reference_hash(calc.old_hash()).set_calculated_hash(calc.new_hash()), }); } @@ -3066,36 +4507,58 @@ BOOST_AUTO_TEST_CASE (verify_invalid_xml_cpl_extension_metadata9) BOOST_AUTO_TEST_CASE (verify_unsigned_cpl_with_encrypted_content) { - path dir = "build/test/verify_unsigned_cpl_with_encrypted_content"; + path const dir = "build/test/verify_unsigned_cpl_with_encrypted_content"; prepare_directory (dir); for (auto i: directory_iterator("test/ref/DCP/encryption_test")) { copy_file (i.path(), dir / i.path().filename()); } - path const pkl = dir / ( "pkl_" + encryption_test_pkl_id() + ".xml" ); - path const cpl = dir / ( "cpl_" + encryption_test_cpl_id() + ".xml"); + path const pkl = dir / ( "pkl_" + encryption_test_pkl_id() + ".xml"); + path const cpl_path = dir / ( "cpl_" + encryption_test_cpl_id() + ".xml"); - HashCalculator calc(cpl); + HashCalculator calc(cpl_path); { - Editor e (cpl); + Editor e(cpl_path); e.delete_lines ("<dsig:Signature", "</dsig:Signature>"); } + auto cpl = std::make_shared<dcp::CPL>(cpl_path); + check_verify_result ( {dir}, {}, { + ok(dcp::VerificationNote::Code::ALL_ENCRYPTED, cpl), + ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "video.mxf"), cpl), + ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"A Test DCP"}, cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_KIND, string{"feature"}, cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_VERSION_LABEL_TEXT, cpl->content_version()->label_text, cpl), + ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "video.mxf"), cpl), + dcp::VerificationNote( + dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_CPL_HASHES, canonical(cpl_path) + ).set_cpl_id(cpl->id()).set_reference_hash(calc.old_hash()).set_calculated_hash(calc.new_hash()), + dcp::VerificationNote( + dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISMATCHED_PKL_ANNOTATION_TEXT_WITH_CPL, encryption_test_pkl_id(), canonical(pkl) + ).set_cpl_id(cpl->id()), + dcp::VerificationNote( + dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_FFEC_IN_FEATURE + ).set_cpl_id(cpl->id()), + dcp::VerificationNote( + dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_FFMC_IN_FEATURE + ).set_cpl_id(cpl->id()), dcp::VerificationNote( - dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_CPL_HASHES, encryption_test_cpl_id(), canonical(cpl) - ).set_reference_hash(calc.old_hash()).set_calculated_hash(calc.new_hash()), - { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISMATCHED_PKL_ANNOTATION_TEXT_WITH_CPL, encryption_test_pkl_id(), canonical(pkl), }, - { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_FFEC_IN_FEATURE }, - { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_FFMC_IN_FEATURE }, - { dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::MISSING_FFOC }, - { dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::MISSING_LFOC }, - { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, encryption_test_cpl_id(), canonical(cpl) }, - { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::UNSIGNED_CPL_WITH_ENCRYPTED_CONTENT, encryption_test_cpl_id(), canonical(cpl) } + dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::MISSING_FFOC + ).set_cpl_id(cpl->id()), + dcp::VerificationNote( + dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::MISSING_LFOC + ).set_cpl_id(cpl->id()), + dcp::VerificationNote( + dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, canonical(cpl_path) + ).set_cpl_id(cpl->id()), + dcp::VerificationNote( + dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::UNSIGNED_CPL_WITH_ENCRYPTED_CONTENT, canonical(cpl_path) + ).set_cpl_id(cpl->id()) }); } @@ -3108,24 +4571,47 @@ BOOST_AUTO_TEST_CASE (verify_unsigned_pkl_with_encrypted_content) copy_file (i.path(), dir / i.path().filename()); } - path const cpl = dir / ("cpl_" + encryption_test_cpl_id() + ".xml"); + path const cpl_path = dir / ("cpl_" + encryption_test_cpl_id() + ".xml"); path const pkl = dir / ("pkl_" + encryption_test_pkl_id() + ".xml"); { Editor e (pkl); e.delete_lines ("<dsig:Signature", "</dsig:Signature>"); } + auto cpl = std::make_shared<dcp::CPL>(cpl_path); + check_verify_result ( {dir}, {}, { - { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISMATCHED_PKL_ANNOTATION_TEXT_WITH_CPL, encryption_test_pkl_id(), canonical(pkl) }, - { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_FFEC_IN_FEATURE }, - { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_FFMC_IN_FEATURE }, - { dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::MISSING_FFOC }, - { dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::MISSING_LFOC }, - { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, encryption_test_cpl_id(), canonical(cpl) }, - { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::UNSIGNED_PKL_WITH_ENCRYPTED_CONTENT, encryption_test_pkl_id(), canonical(pkl) }, + ok(dcp::VerificationNote::Code::ALL_ENCRYPTED, cpl), + ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "video.mxf"), cpl), + ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"A Test DCP"}, cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_KIND, string{"feature"}, cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_VERSION_LABEL_TEXT, cpl->content_version()->label_text, cpl), + ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "video.mxf"), cpl), + ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl), + dcp::VerificationNote( + dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISMATCHED_PKL_ANNOTATION_TEXT_WITH_CPL, encryption_test_pkl_id(), canonical(pkl) + ).set_cpl_id(cpl->id()), + dcp::VerificationNote( + dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_FFEC_IN_FEATURE + ).set_cpl_id(cpl->id()), + dcp::VerificationNote( + dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_FFMC_IN_FEATURE + ).set_cpl_id(cpl->id()), + dcp::VerificationNote( + dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::MISSING_FFOC + ).set_cpl_id(cpl->id()), + dcp::VerificationNote( + dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::MISSING_LFOC + ).set_cpl_id(cpl->id()), + dcp::VerificationNote( + dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, canonical(cpl_path) + ).set_cpl_id(cpl->id()), + dcp::VerificationNote( + dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::UNSIGNED_PKL_WITH_ENCRYPTED_CONTENT, encryption_test_pkl_id(), canonical(pkl) + ) }); } @@ -3143,7 +4629,27 @@ BOOST_AUTO_TEST_CASE (verify_unsigned_pkl_with_unencrypted_content) e.delete_lines ("<dsig:Signature", "</dsig:Signature>"); } - check_verify_result({dir}, {}, {}); + auto cpl = make_shared<dcp::CPL>(find_cpl(dir)); + + check_verify_result( + {dir}, + {}, + { + ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "video.mxf"), cpl), + ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl), + ok(dcp::VerificationNote::Code::MATCHING_PKL_ANNOTATION_TEXT_WITH_CPL, cpl), + ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl), + dcp::VerificationNote( + dcp::VerificationNote::Type::OK, + dcp::VerificationNote::Code::VALID_MAIN_PICTURE_ACTIVE_AREA, + string{"1998x1080"}, + canonical(cpl->file().get()) + ).set_cpl_id(cpl->id()), + ok(dcp::VerificationNote::Code::VALID_CONTENT_KIND, string{"trailer"}, cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_VERSION_LABEL_TEXT, cpl->content_version()->label_text, cpl), + ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"A Test DCP"}, cpl), + ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "video.mxf"), cpl), + }); } @@ -3164,10 +4670,10 @@ BOOST_AUTO_TEST_CASE (verify_partially_encrypted) dcp::Key key; - auto mp = make_shared<dcp::MonoPictureAsset>(dcp::Fraction (24, 1), dcp::Standard::SMPTE); + auto mp = make_shared<dcp::MonoJ2KPictureAsset>(dcp::Fraction (24, 1), dcp::Standard::SMPTE); mp->set_key (key); - auto writer = mp->start_write(dir / "video.mxf", dcp::PictureAsset::Behaviour::MAKE_NEW); + auto writer = mp->start_write(dir / "video.mxf", dcp::Behaviour::MAKE_NEW); dcp::ArrayData j2c ("test/data/flat_red.j2c"); for (int i = 0; i < 24; ++i) { writer->write (j2c.data(), j2c.size()); @@ -3210,7 +4716,22 @@ BOOST_AUTO_TEST_CASE (verify_partially_encrypted) {dir}, {}, { - {dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::PARTIALLY_ENCRYPTED}, + dcp::VerificationNote( + dcp::VerificationNote::Type::OK, + dcp::VerificationNote::Code::VALID_MAIN_PICTURE_ACTIVE_AREA, + string{"1440x1080"}, + cpl->file().get() + ).set_cpl_id(cpl->id()), + ok(dcp::VerificationNote::Code::MATCHING_PKL_ANNOTATION_TEXT_WITH_CPL, cpl), + ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"A Test DCP"}, cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_VERSION_LABEL_TEXT, cpl->content_version()->label_text, cpl), + ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_KIND, string{"trailer"}, cpl), + ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "video.mxf"), cpl), + ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "video.mxf"), cpl), + dcp::VerificationNote( + dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::PARTIALLY_ENCRYPTED + ).set_cpl_id(cpl->id()) }); } @@ -3218,22 +4739,22 @@ BOOST_AUTO_TEST_CASE (verify_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")); + dcp::MonoJ2KPictureAsset 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, 0, 0, 24, notes); - BOOST_REQUIRE_EQUAL (notes.size(), 0U); + BOOST_CHECK(notes.empty()); } BOOST_AUTO_TEST_CASE (verify_jpeg2000_codestream_4k) { vector<dcp::VerificationNote> notes; - dcp::MonoPictureAsset picture (find_file(private_test / "data" / "sul", "TLR")); + dcp::MonoJ2KPictureAsset picture (find_file(private_test / "data" / "sul", "TLR")); auto reader = picture.start_read (); auto frame = reader->get_frame (0); verify_j2k(frame, 0, 0, 24, notes); - BOOST_REQUIRE_EQUAL (notes.size(), 0U); + BOOST_CHECK(notes.empty()); } @@ -3244,11 +4765,11 @@ BOOST_AUTO_TEST_CASE (verify_jpeg2000_codestream_libdcp) auto dcp = make_simple (dir); dcp->write_xml (); vector<dcp::VerificationNote> notes; - dcp::MonoPictureAsset picture (find_file(dir, "video")); + dcp::MonoJ2KPictureAsset picture (find_file(dir, "video")); auto reader = picture.start_read (); auto frame = reader->get_frame (0); verify_j2k(frame, 0, 0, 24, notes); - BOOST_REQUIRE_EQUAL (notes.size(), 0U); + BOOST_CHECK(notes.empty()); } @@ -3310,10 +4831,24 @@ BOOST_AUTO_TEST_CASE (verify_mismatched_subtitle_resource_id) { dir }, {}, { - { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISMATCHED_TIMED_TEXT_DURATION , "240 0", boost::filesystem::canonical(subs_mxf) }, - { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISMATCHED_TIMED_TEXT_RESOURCE_ID }, - { dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::INVALID_SUBTITLE_FIRST_TEXT_TIME }, - { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() } + ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl), + ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"hello"}, cpl), + ok(dcp::VerificationNote::Code::MATCHING_PKL_ANNOTATION_TEXT_WITH_CPL, cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_VERSION_LABEL_TEXT, cpl->content_version()->label_text, cpl), + ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_KIND, string{"trailer"}, cpl), + dcp::VerificationNote( + dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISMATCHED_TIMED_TEXT_DURATION , "240 0", boost::filesystem::canonical(subs_mxf) + ).set_cpl_id(cpl->id()), + dcp::VerificationNote( + dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISMATCHED_TIMED_TEXT_RESOURCE_ID + ).set_cpl_id(cpl->id()), + dcp::VerificationNote( + dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::INVALID_SUBTITLE_FIRST_TEXT_TIME + ).set_cpl_id(cpl->id()), + dcp::VerificationNote( + dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->file().get() + ).set_cpl_id(cpl->id()) }); } @@ -3376,11 +4911,27 @@ BOOST_AUTO_TEST_CASE (verify_incorrect_timed_text_id) { dir }, {}, { - { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISMATCHED_TIMED_TEXT_DURATION , "240 0", boost::filesystem::canonical(subs_mxf) }, - { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INCORRECT_TIMED_TEXT_ASSET_ID }, - { dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::INVALID_SUBTITLE_FIRST_TEXT_TIME }, - { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() }, - { dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::INVALID_SUBTITLE_ISSUE_DATE, string{"2018-10-02T12:25:14+02:00"} } + ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl), + ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"hello"}, cpl), + ok(dcp::VerificationNote::Code::MATCHING_PKL_ANNOTATION_TEXT_WITH_CPL, cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_VERSION_LABEL_TEXT, cpl->content_version()->label_text, cpl), + ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_KIND, string{"trailer"}, cpl), + dcp::VerificationNote( + dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISMATCHED_TIMED_TEXT_DURATION , "240 0", boost::filesystem::canonical(subs_mxf) + ).set_cpl_id(cpl->id()), + dcp::VerificationNote( + dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INCORRECT_TIMED_TEXT_ASSET_ID + ).set_cpl_id(cpl->id()), + dcp::VerificationNote( + dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::INVALID_SUBTITLE_FIRST_TEXT_TIME + ).set_cpl_id(cpl->id()), + dcp::VerificationNote( + dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->file().get() + ).set_cpl_id(cpl->id()), + dcp::VerificationNote( + dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::INVALID_SUBTITLE_ISSUE_DATE, string{"2018-10-02T12:25:14+02:00"} + ).set_cpl_id(cpl->id()) }); } @@ -3388,18 +4939,29 @@ BOOST_AUTO_TEST_CASE (verify_incorrect_timed_text_id) /** Check a DCP with a 3D asset marked as 2D */ BOOST_AUTO_TEST_CASE (verify_threed_marked_as_twod) { + auto const path = private_test / "data" / "xm"; + + auto cpl = std::make_shared<dcp::CPL>(find_prefix(path, "CPL_")); + BOOST_REQUIRE(cpl); + check_verify_result ( - { private_test / "data" / "xm" }, + { path }, {}, { - { + ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl), + ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(path / "0d6f57e6-adac-4e1d-bfbe-d162bf13e2cd_j2c.mxf"), cpl), + ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(path / "0d6f57e6-adac-4e1d-bfbe-d162bf13e2cd_j2c.mxf"), cpl), + ok(dcp::VerificationNote::Code::MATCHING_PKL_ANNOTATION_TEXT_WITH_CPL, cpl), + ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_KIND, string{"trailer"}, cpl), + dcp::VerificationNote( dcp::VerificationNote::Type::WARNING, - dcp::VerificationNote::Code::THREED_ASSET_MARKED_AS_TWOD, boost::filesystem::canonical(find_file(private_test / "data" / "xm", "j2c")) - }, - { + dcp::VerificationNote::Code::THREED_ASSET_MARKED_AS_TWOD, boost::filesystem::canonical(find_file(path, "j2c")) + ), + dcp::VerificationNote( dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_STANDARD - }, + ) }); } @@ -3422,17 +4984,34 @@ BOOST_AUTO_TEST_CASE (verify_unexpected_things_in_main_markers) ); } - dcp::CPL cpl (find_cpl(dir)); + auto cpl = make_shared<dcp::CPL>(find_cpl(dir)); check_verify_result ( { dir }, {}, { + ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_KIND, string{"trailer"}, cpl), + ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "video.mxf"), cpl), + ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "video.mxf"), cpl), dcp::VerificationNote( - dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_CPL_HASHES, cpl.id(), canonical(find_cpl(dir)) - ).set_reference_hash(calc.old_hash()).set_calculated_hash(calc.new_hash()), - { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::UNEXPECTED_ENTRY_POINT }, - { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::UNEXPECTED_DURATION }, + dcp::VerificationNote::Type::OK, + dcp::VerificationNote::Code::VALID_MAIN_PICTURE_ACTIVE_AREA, + string{"1998x1080"}, + canonical(cpl->file().get()) + ).set_cpl_id(cpl->id()), + ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"A Test DCP"}, cpl), + ok(dcp::VerificationNote::Code::MATCHING_PKL_ANNOTATION_TEXT_WITH_CPL, cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_VERSION_LABEL_TEXT, cpl->content_version()->label_text, cpl), + dcp::VerificationNote( + dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_CPL_HASHES, canonical(find_cpl(dir)) + ).set_cpl_id(cpl->id()).set_reference_hash(calc.old_hash()).set_calculated_hash(calc.new_hash()), + dcp::VerificationNote( + dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::UNEXPECTED_ENTRY_POINT + ).set_cpl_id(cpl->id()), + dcp::VerificationNote( + dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::UNEXPECTED_DURATION + ).set_cpl_id(cpl->id()) }); } @@ -3451,16 +5030,30 @@ BOOST_AUTO_TEST_CASE(verify_invalid_content_kind) e.replace("trailer", "trip"); } - dcp::CPL cpl (find_cpl(dir)); + auto cpl = std::make_shared<dcp::CPL>(find_cpl(dir)); check_verify_result ( { dir }, {}, { + ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl), + dcp::VerificationNote( + dcp::VerificationNote::Type::OK, + dcp::VerificationNote::Code::VALID_MAIN_PICTURE_ACTIVE_AREA, + string{"1998x1080"}, + canonical(cpl->file().get()) + ).set_cpl_id(cpl->id()), + ok(dcp::VerificationNote::Code::MATCHING_PKL_ANNOTATION_TEXT_WITH_CPL, cpl), + ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"A Test DCP"}, cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_VERSION_LABEL_TEXT, cpl->content_version()->label_text, cpl), + ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "video.mxf"), cpl), + ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "video.mxf"), cpl), dcp::VerificationNote( - dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_CPL_HASHES, cpl.id(), canonical(find_cpl(dir)) - ).set_reference_hash(calc.old_hash()).set_calculated_hash(calc.new_hash()), - { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_CONTENT_KIND, string("trip") } + dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_CPL_HASHES, canonical(find_cpl(dir)) + ).set_cpl_id(cpl->id()).set_reference_hash(calc.old_hash()).set_calculated_hash(calc.new_hash()), + dcp::VerificationNote( + dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_CONTENT_KIND, string("trip") + ).set_cpl_id(cpl->id()), }); } @@ -3480,17 +5073,28 @@ BOOST_AUTO_TEST_CASE(verify_valid_content_kind) e.replace("<ContentKind>trailer</ContentKind>", "<ContentKind scope=\"http://bobs.contents/\">trip</ContentKind>"); } - dcp::CPL cpl (find_cpl(dir)); + auto cpl = std::make_shared<dcp::CPL>(find_cpl(dir)); check_verify_result ( { dir }, {}, { + ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_VERSION_LABEL_TEXT, cpl->content_version()->label_text, cpl), + ok(dcp::VerificationNote::Code::MATCHING_PKL_ANNOTATION_TEXT_WITH_CPL, cpl), + ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"A Test DCP"}, cpl), + ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "video.mxf"), cpl), + ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "video.mxf"), cpl), + dcp::VerificationNote( + dcp::VerificationNote::Type::OK, + dcp::VerificationNote::Code::VALID_MAIN_PICTURE_ACTIVE_AREA, + string{"1998x1080"}, + canonical(cpl->file().get()) + ).set_cpl_id(cpl->id()), dcp::VerificationNote( - dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_CPL_HASHES, cpl.id(), canonical(find_cpl(dir)) - ).set_reference_hash(calc.old_hash()).set_calculated_hash(calc.new_hash()), + dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_CPL_HASHES, canonical(find_cpl(dir)) + ).set_cpl_id(cpl->id()).set_reference_hash(calc.old_hash()).set_calculated_hash(calc.new_hash()), }); - } @@ -3513,17 +5117,28 @@ BOOST_AUTO_TEST_CASE(verify_invalid_main_picture_active_area_1) } dcp::PKL pkl(find_pkl(dir)); - dcp::CPL cpl(find_cpl(dir)); + auto cpl = std::make_shared<dcp::CPL>(find_cpl(dir)); check_verify_result( { dir }, {}, { + ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_KIND, string{"trailer"}, cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_VERSION_LABEL_TEXT, cpl->content_version()->label_text, cpl), + ok(dcp::VerificationNote::Code::MATCHING_PKL_ANNOTATION_TEXT_WITH_CPL, cpl), + ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"A Test DCP"}, cpl), + ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "video.mxf"), cpl), + ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "video.mxf"), cpl), + dcp::VerificationNote( + dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_CPL_HASHES, canonical(find_cpl(dir)) + ).set_cpl_id(cpl->id()).set_reference_hash(calc.old_hash()).set_calculated_hash(calc.new_hash()), + dcp::VerificationNote( + dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_MAIN_PICTURE_ACTIVE_AREA, "width 1997 is not a multiple of 2", canonical(find_cpl(dir)) + ).set_cpl_id(cpl->id()), dcp::VerificationNote( - dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_CPL_HASHES, cpl.id(), canonical(find_cpl(dir)) - ).set_reference_hash(calc.old_hash()).set_calculated_hash(calc.new_hash()), - { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_MAIN_PICTURE_ACTIVE_AREA, "width 1997 is not a multiple of 2", canonical(find_cpl(dir)) }, - { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_MAIN_PICTURE_ACTIVE_AREA, "height 4080 is bigger than the asset height 1080", canonical(find_cpl(dir)) }, + dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_MAIN_PICTURE_ACTIVE_AREA, "height 4080 is bigger than the asset height 1080", canonical(find_cpl(dir)) + ).set_cpl_id(cpl->id()), }); } @@ -3547,18 +5162,31 @@ BOOST_AUTO_TEST_CASE(verify_invalid_main_picture_active_area_2) } dcp::PKL pkl(find_pkl(dir)); - dcp::CPL cpl(find_cpl(dir)); + auto cpl = std::make_shared<dcp::CPL>(find_cpl(dir)); check_verify_result( { dir }, {}, { + ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_KIND, string{"trailer"}, cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_VERSION_LABEL_TEXT, cpl->content_version()->label_text, cpl), + ok(dcp::VerificationNote::Code::MATCHING_PKL_ANNOTATION_TEXT_WITH_CPL, cpl), + ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"A Test DCP"}, cpl), + ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "video.mxf"), cpl), + ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "video.mxf"), cpl), dcp::VerificationNote( - dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_CPL_HASHES, cpl.id(), canonical(find_cpl(dir)) - ).set_reference_hash(calc.old_hash()).set_calculated_hash(calc.new_hash()), - { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_MAIN_PICTURE_ACTIVE_AREA, "height 5125 is not a multiple of 2", canonical(find_cpl(dir)) }, - { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_MAIN_PICTURE_ACTIVE_AREA, "width 9900 is bigger than the asset width 1998", canonical(find_cpl(dir)) }, - { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_MAIN_PICTURE_ACTIVE_AREA, "height 5125 is bigger than the asset height 1080", canonical(find_cpl(dir)) }, + dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_CPL_HASHES, canonical(find_cpl(dir)) + ).set_cpl_id(cpl->id()).set_reference_hash(calc.old_hash()).set_calculated_hash(calc.new_hash()), + dcp::VerificationNote( + dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_MAIN_PICTURE_ACTIVE_AREA, "height 5125 is not a multiple of 2", canonical(find_cpl(dir)) + ).set_cpl_id(cpl->id()), + dcp::VerificationNote( + dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_MAIN_PICTURE_ACTIVE_AREA, "width 9900 is bigger than the asset width 1998", canonical(find_cpl(dir)) + ).set_cpl_id(cpl->id()), + dcp::VerificationNote( + dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_MAIN_PICTURE_ACTIVE_AREA, "height 5125 is bigger than the asset height 1080", canonical(find_cpl(dir)) + ).set_cpl_id(cpl->id()) }); } @@ -3578,11 +5206,24 @@ BOOST_AUTO_TEST_CASE(verify_duplicate_pkl_asset_ids) } dcp::PKL pkl(find_pkl(dir)); + auto cpl = std::make_shared<dcp::CPL>(find_cpl(dir)); check_verify_result( { dir }, {}, { + ok(dcp::VerificationNote::Code::MATCHING_PKL_ANNOTATION_TEXT_WITH_CPL, cpl), + ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl), + dcp::VerificationNote( + dcp::VerificationNote::Type::OK, + dcp::VerificationNote::Code::VALID_MAIN_PICTURE_ACTIVE_AREA, + string{"1998x1080"}, + canonical(cpl->file().get()) + ).set_cpl_id(cpl->id()), + ok(dcp::VerificationNote::Code::VALID_CONTENT_KIND, string{"trailer"}, cpl), + ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"A Test DCP"}, cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_VERSION_LABEL_TEXT, cpl->content_version()->label_text, cpl), + ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl), { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::DUPLICATE_ASSET_ID_IN_PKL, pkl.id(), canonical(find_pkl(dir)) }, }); } @@ -3604,13 +5245,30 @@ BOOST_AUTO_TEST_CASE(verify_duplicate_assetmap_asset_ids) dcp::PKL pkl(find_pkl(dir)); dcp::AssetMap asset_map(find_asset_map(dir)); + auto cpl = make_shared<dcp::CPL>(find_cpl(dir)); check_verify_result( { dir }, {}, { - { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::DUPLICATE_ASSET_ID_IN_ASSETMAP, asset_map.id(), canonical(find_asset_map(dir)) }, - { dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::EXTERNAL_ASSET, string("5407b210-4441-4e97-8b16-8bdc7c12da54") }, + ok(dcp::VerificationNote::Code::MATCHING_PKL_ANNOTATION_TEXT_WITH_CPL, cpl), + ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl), + dcp::VerificationNote( + dcp::VerificationNote::Type::OK, + dcp::VerificationNote::Code::VALID_MAIN_PICTURE_ACTIVE_AREA, + string{"1998x1080"}, + canonical(cpl->file().get()) + ).set_cpl_id(cpl->id()), + ok(dcp::VerificationNote::Code::VALID_CONTENT_KIND, string{"trailer"}, cpl), + ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"A Test DCP"}, cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_VERSION_LABEL_TEXT, cpl->content_version()->label_text, cpl), + ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl), + dcp::VerificationNote( + dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::DUPLICATE_ASSET_ID_IN_ASSETMAP, asset_map.id(), canonical(find_asset_map(dir)) + ), + dcp::VerificationNote( + dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::EXTERNAL_ASSET, string("5407b210-4441-4e97-8b16-8bdc7c12da54") + ) }); } @@ -3683,7 +5341,25 @@ BOOST_AUTO_TEST_CASE(verify_mismatched_sound_channel_counts) { path }, {}, { - { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_SOUND_CHANNEL_COUNTS, canonical(find_file(path, "audio2")) }, + ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl), + ok(dcp::VerificationNote::Code::MATCHING_PKL_ANNOTATION_TEXT_WITH_CPL, cpl), + ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl), + dcp::VerificationNote( + dcp::VerificationNote::Type::OK, + dcp::VerificationNote::Code::VALID_MAIN_PICTURE_ACTIVE_AREA, + string{"1998x1080"}, + cpl->file().get() + ).set_cpl_id(cpl->id()), + ok(dcp::VerificationNote::Code::VALID_CONTENT_KIND, string{"trailer"}, cpl), + ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"hello"}, cpl), + ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(path / "video1.mxf"), cpl), + ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(path / "video1.mxf"), cpl), + ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(path / "video2.mxf"), cpl), + ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(path / "video2.mxf"), cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_VERSION_LABEL_TEXT, cpl->content_version()->label_text, cpl), + dcp::VerificationNote( + dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_SOUND_CHANNEL_COUNTS, canonical(find_file(path, "audio2")) + ).set_cpl_id(cpl->id()) }); } @@ -3734,7 +5410,23 @@ BOOST_AUTO_TEST_CASE(verify_invalid_main_sound_configuration) { path }, {}, { - { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_MAIN_SOUND_CONFIGURATION, std::string{"MainSoundConfiguration has 6 channels but sound assets have 2"}, canonical(find_cpl(path)) }, + ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl), + ok(dcp::VerificationNote::Code::MATCHING_PKL_ANNOTATION_TEXT_WITH_CPL, cpl), + ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_KIND, string{"trailer"}, cpl), + ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"hello"}, cpl), + ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(path / "video1.mxf"), cpl), + ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(path / "video1.mxf"), cpl), + dcp::VerificationNote( + dcp::VerificationNote::Type::OK, + dcp::VerificationNote::Code::VALID_MAIN_PICTURE_ACTIVE_AREA, + string{"1998x1080"}, + cpl->file().get() + ).set_cpl_id(cpl->id()), + ok(dcp::VerificationNote::Code::VALID_CONTENT_VERSION_LABEL_TEXT, cpl->content_version()->label_text, cpl), + dcp::VerificationNote( + dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_MAIN_SOUND_CONFIGURATION, std::string{"MainSoundConfiguration has 6 channels but sound assets have 2"}, canonical(find_cpl(path)) + ).set_cpl_id(cpl->id()) }); } @@ -3748,8 +5440,8 @@ BOOST_AUTO_TEST_CASE(verify_invalid_tile_part_size) boost::filesystem::remove_all(path); boost::filesystem::create_directories(path); - auto mp = make_shared<dcp::MonoPictureAsset>(dcp::Fraction(24, 1), dcp::Standard::SMPTE); - auto picture_writer = mp->start_write(path / "video.mxf", dcp::PictureAsset::Behaviour::MAKE_NEW); + auto mp = make_shared<dcp::MonoJ2KPictureAsset>(dcp::Fraction(24, 1), dcp::Standard::SMPTE); + auto picture_writer = mp->start_write(path / "video.mxf", dcp::Behaviour::MAKE_NEW); dcp::Size const size(1998, 1080); auto image = make_shared<dcp::OpenJPEGImage>(size); @@ -3789,20 +5481,40 @@ BOOST_AUTO_TEST_CASE(verify_invalid_tile_part_size) dcp->set_annotation_text("A Test DCP"); dcp->write_xml(); - vector<dcp::VerificationNote> expected; + vector<dcp::VerificationNote> expected = { + ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl), + dcp::VerificationNote( + dcp::VerificationNote::Type::OK, + dcp::VerificationNote::Code::VALID_MAIN_PICTURE_ACTIVE_AREA, + string{"1998x1080"}, + cpl->file().get() + ).set_cpl_id(cpl->id()), + ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(path / "video.mxf"), cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_VERSION_LABEL_TEXT, cpl->content_version()->label_text, cpl), + ok(dcp::VerificationNote::Code::MATCHING_PKL_ANNOTATION_TEXT_WITH_CPL, cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_KIND, string{"trailer"}, cpl), + ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"A Test DCP"}, cpl), + ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl), + dcp::VerificationNote( + dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::MISSING_FFOC + ).set_cpl_id(cpl->id()), + dcp::VerificationNote( + dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::MISSING_LFOC + ).set_cpl_id(cpl->id()) + }; for (auto frame = 0; frame < 24; frame++) { expected.push_back( dcp::VerificationNote( dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_PICTURE_FRAME_SIZE_IN_BYTES, canonical(path / "video.mxf") - ).set_frame(frame).set_frame_rate(24) + ).set_frame(frame).set_frame_rate(24).set_cpl_id(cpl->id()) ); } int component_sizes[] = { - 1321721, - 1294364, - 1289952, + 1321816, + 1294414, + 1289881, }; for (auto frame = 0; frame < 24; frame++) { @@ -3810,19 +5522,11 @@ BOOST_AUTO_TEST_CASE(verify_invalid_tile_part_size) expected.push_back( dcp::VerificationNote( dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_JPEG2000_TILE_PART_SIZE - ).set_frame(frame).set_component(component).set_size(component_sizes[component]) + ).set_frame(frame).set_component(component).set_size(component_sizes[component]).set_cpl_id(cpl->id()) ); } } - expected.push_back( - { dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::MISSING_FFOC } - ); - - expected.push_back( - { dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::MISSING_LFOC } - ); - check_verify_result({ path }, {}, expected); } @@ -3830,16 +5534,41 @@ BOOST_AUTO_TEST_CASE(verify_invalid_tile_part_size) BOOST_AUTO_TEST_CASE(verify_too_many_subtitle_namespaces) { boost::filesystem::path const dir = "test/ref/DCP/subtitle_namespace_test"; + dcp::DCP dcp(dir); + dcp.read(); + BOOST_REQUIRE(!dcp.cpls().empty()); + auto cpl = dcp.cpls()[0]; + check_verify_result( { dir }, {}, { - { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_FFEC_IN_FEATURE }, - { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_FFMC_IN_FEATURE }, - { dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::INVALID_SUBTITLE_FIRST_TEXT_TIME }, - { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_SUBTITLE_LANGUAGE, canonical(find_file(dir, "sub_")) }, - { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, "fc815694-7977-4a27-a8b3-32b9d4075e4c", canonical(find_file(dir, "cpl_")) }, - { dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::INCORRECT_SUBTITLE_NAMESPACE_COUNT, std::string{"315de731-1173-484c-9a35-bdacf5a9d99d"} } + ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl), + ok(dcp::VerificationNote::Code::MATCHING_PKL_ANNOTATION_TEXT_WITH_CPL, cpl), + ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_KIND, string{"feature"}, cpl), + ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"Dcp_FTR-1_F_XX-XX_MOS_2K_20230407_SMPTE_OV"}, cpl), + ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "j2c_42b34dcd-caa5-4c7b-aa0f-66a590947ba1.mxf"), cpl), + ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "j2c_42b34dcd-caa5-4c7b-aa0f-66a590947ba1.mxf"), cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_VERSION_LABEL_TEXT, cpl->content_version()->label_text, cpl), + dcp::VerificationNote( + dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_FFEC_IN_FEATURE + ).set_cpl_id(cpl->id()), + dcp::VerificationNote( + dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_FFMC_IN_FEATURE + ).set_cpl_id(cpl->id()), + dcp::VerificationNote( + dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::INVALID_SUBTITLE_FIRST_TEXT_TIME + ).set_cpl_id(cpl->id()), + dcp::VerificationNote( + dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_SUBTITLE_LANGUAGE, canonical(find_file(dir, "sub_")) + ).set_cpl_id(cpl->id()), + dcp::VerificationNote( + dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, canonical(find_file(dir, "cpl_")) + ).set_cpl_id(cpl->id()), + dcp::VerificationNote( + dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::INCORRECT_SUBTITLE_NAMESPACE_COUNT, std::string{"315de731-1173-484c-9a35-bdacf5a9d99d"} + ).set_cpl_id(cpl->id()), }); } @@ -3855,14 +5584,19 @@ BOOST_AUTO_TEST_CASE(verify_missing_load_font_for_font) } auto asset = make_shared<dcp::InteropSubtitleAsset>(dir / "subs.xml"); auto reel_asset = make_shared<dcp::ReelInteropSubtitleAsset>(asset, dcp::Fraction(24, 1), 16 * 24, 0); - write_dcp_with_single_asset (dir, reel_asset, dcp::Standard::INTEROP); + auto cpl = write_dcp_with_single_asset(dir, reel_asset, dcp::Standard::INTEROP); check_verify_result ( {dir}, {}, { + ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl), + ok(dcp::VerificationNote::Code::MATCHING_PKL_ANNOTATION_TEXT_WITH_CPL, cpl), + ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_KIND, string{"trailer"}, cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_VERSION_LABEL_TEXT, cpl->content_version()->label_text, cpl), { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_STANDARD }, - dcp::VerificationNote(dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISSING_LOAD_FONT_FOR_FONT).set_id("theFontId") + dcp::VerificationNote(dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISSING_LOAD_FONT_FOR_FONT).set_id("theFontId").set_cpl_id(cpl->id()) }); } @@ -3903,14 +5637,29 @@ BOOST_AUTO_TEST_CASE(verify_missing_load_font) subs->write(dir / "subs.mxf"); auto reel_subs = make_shared<dcp::ReelSMPTESubtitleAsset>(subs, dcp::Fraction(24, 1), 202, 0); - dcp->cpls()[0]->reels()[0]->add(reel_subs); + auto cpl = dcp->cpls()[0]; + cpl->reels()[0]->add(reel_subs); dcp->write_xml(); check_verify_result ( { dir }, {}, { - dcp::VerificationNote(dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISSING_LOAD_FONT).set_id(reel_subs->id()) + ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl), + ok(dcp::VerificationNote::Code::MATCHING_PKL_ANNOTATION_TEXT_WITH_CPL, cpl), + ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl), + dcp::VerificationNote( + dcp::VerificationNote::Type::OK, + dcp::VerificationNote::Code::VALID_MAIN_PICTURE_ACTIVE_AREA, + string{"1998x1080"}, + cpl->file().get() + ).set_cpl_id(cpl->id()), + ok(dcp::VerificationNote::Code::VALID_CONTENT_KIND, string{"trailer"}, cpl), + ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"A Test DCP"}, cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_VERSION_LABEL_TEXT, cpl->content_version()->label_text, cpl), + ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "video.mxf"), cpl), + ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "video.mxf"), cpl), + dcp::VerificationNote(dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISSING_LOAD_FONT).set_id(reel_subs->id()).set_cpl_id(cpl->id()) }); } @@ -3922,12 +5671,13 @@ BOOST_AUTO_TEST_CASE(verify_spots_wrong_asset) auto dcp1 = make_simple(dir / "1"); dcp1->write_xml(); + auto cpl = dcp1->cpls()[0]; - auto const asset_1 = dcp::MonoPictureAsset(dir / "1" / "video.mxf").id(); + auto const asset_1 = dcp::MonoJ2KPictureAsset(dir / "1" / "video.mxf").id(); auto dcp2 = make_simple(dir / "2"); dcp2->write_xml(); - auto const asset_2 = dcp::MonoPictureAsset(dir / "2" / "video.mxf").id(); + auto const asset_2 = dcp::MonoJ2KPictureAsset(dir / "2" / "video.mxf").id(); boost::filesystem::remove(dir / "1" / "video.mxf"); boost::filesystem::copy_file(dir / "2" / "video.mxf", dir / "1" / "video.mxf"); @@ -3936,6 +5686,18 @@ BOOST_AUTO_TEST_CASE(verify_spots_wrong_asset) {dir / "1"}, {}, { + ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl), + ok(dcp::VerificationNote::Code::MATCHING_PKL_ANNOTATION_TEXT_WITH_CPL, cpl), + ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl), + dcp::VerificationNote( + dcp::VerificationNote::Type::OK, + dcp::VerificationNote::Code::VALID_MAIN_PICTURE_ACTIVE_AREA, + string{"1998x1080"}, + cpl->file().get() + ).set_cpl_id(cpl->id()), + ok(dcp::VerificationNote::Code::VALID_CONTENT_KIND, string{"trailer"}, cpl), + ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"A Test DCP"}, cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_VERSION_LABEL_TEXT, cpl->content_version()->label_text, cpl), dcp::VerificationNote(dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_ASSET_MAP_ID).set_id(asset_1).set_other_id(asset_2) }); } @@ -3956,7 +5718,20 @@ BOOST_AUTO_TEST_CASE(verify_cpl_content_version_label_text_empty) {dir}, {}, { - dcp::VerificationNote(dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::EMPTY_CONTENT_VERSION_LABEL_TEXT, cpl->file().get()).set_id(cpl->id()) + dcp::VerificationNote( + dcp::VerificationNote::Type::OK, + dcp::VerificationNote::Code::VALID_MAIN_PICTURE_ACTIVE_AREA, + string{"1998x1080"}, + cpl->file().get() + ).set_cpl_id(cpl->id()), + ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl), + ok(dcp::VerificationNote::Code::MATCHING_PKL_ANNOTATION_TEXT_WITH_CPL, cpl), + ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_KIND, string{"trailer"}, cpl), + ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"A Test DCP"}, cpl), + ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "video.mxf"), cpl), + ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "video.mxf"), cpl), + dcp::VerificationNote(dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::EMPTY_CONTENT_VERSION_LABEL_TEXT, cpl->file().get()).set_cpl_id(cpl->id()) }); } @@ -3979,9 +5754,21 @@ BOOST_AUTO_TEST_CASE(verify_encrypted_smpte_dcp) { dir }, { kdm }, { - { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), canonical(cpl_file) }, - { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::UNSIGNED_CPL_WITH_ENCRYPTED_CONTENT, cpl->id(), canonical(cpl_file) }, - { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::UNSIGNED_PKL_WITH_ENCRYPTED_CONTENT, filename_to_id(pkl_file.filename()), canonical(pkl_file) } + ok(dcp::VerificationNote::Code::MATCHING_PKL_ANNOTATION_TEXT_WITH_CPL, cpl), + ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_KIND, string{"trailer"}, cpl), + ok(dcp::VerificationNote::Code::VALID_CONTENT_VERSION_LABEL_TEXT, cpl->content_version()->label_text, cpl), + ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"hello"}, cpl), + ok(dcp::VerificationNote::Code::ALL_ENCRYPTED, cpl), + dcp::VerificationNote( + dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, canonical(cpl_file) + ).set_cpl_id(cpl->id()), + dcp::VerificationNote( + dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::UNSIGNED_CPL_WITH_ENCRYPTED_CONTENT, canonical(cpl_file) + ).set_cpl_id(cpl->id()), + dcp::VerificationNote( + dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::UNSIGNED_PKL_WITH_ENCRYPTED_CONTENT, filename_to_id(pkl_file.filename()), canonical(pkl_file) + ) }); } diff --git a/test/wscript b/test/wscript index d2cac0b3..64b3ed59 100644 --- a/test/wscript +++ b/test/wscript @@ -53,7 +53,7 @@ def configure(conf): def build(bld): obj = bld(features='cxx cxxprogram') obj.name = 'tests' - obj.uselib = 'BOOST_TEST BOOST_FILESYSTEM BOOST_DATETIME OPENJPEG CXML XMLSEC1 SNDFILE OPENMP ASDCPLIB_CTH LIBXML++ OPENSSL XERCES DL' + obj.uselib = 'BOOST_TEST BOOST_FILESYSTEM BOOST_DATETIME OPENJPEG CXML XMLSEC1 SNDFILE OPENMP ASDCPLIB_DCPOMATIC LIBXML++ OPENSSL XERCES DL AVCODEC AVUTIL' obj.cppflags = ['-fno-inline', '-fno-elide-constructors', '-g', '-O0'] if bld.env['CXX_NAME'] == 'gcc': obj.cppflags.append('-fno-default-inline') @@ -94,6 +94,8 @@ def build(bld): make_digest_test.cc markers_test.cc mca_test.cc + mono_mpeg2_picture_read_test.cc + mono_mpeg2_picture_write_test.cc kdm_test.cc key_test.cc language_tag_test.cc @@ -117,13 +119,14 @@ def build(bld): utf8_test.cc v_align_test.cc verify_test.cc + verify_report_test.cc """ obj.target = 'tests' obj.install_path = '' obj = bld(features='cxx cxxprogram') obj.name = 'subs_in_out' - obj.uselib = 'BOOST_TEST BOOST_FILESYSTEM OPENJPEG CXML OPENMP ASDCPLIB_CTH XMLSEC1 OPENSSL DL LIBXML++' + obj.uselib = 'BOOST_TEST BOOST_FILESYSTEM OPENJPEG CXML OPENMP ASDCPLIB_DCPOMATIC XMLSEC1 OPENSSL DL LIBXML++ AVCODEC AVUTIL' obj.cppflags = ['-fno-inline', '-fno-elide-constructors', '-g', '-O0'] if bld.env['CXX_NAME'] == 'gcc': obj.cppflags.append('-fno-default-inline') @@ -140,7 +143,7 @@ def build(bld): obj = bld(features='cxx cxxprogram') obj.name = 'rewrite_subs' - obj.uselib = 'BOOST_TEST BOOST_FILESYSTEM OPENJPEG CXML OPENMP ASDCPLIB_CTH XMLSEC1 OPENSSL DL LIBXML++' + obj.uselib = 'BOOST_TEST BOOST_FILESYSTEM OPENJPEG CXML OPENMP ASDCPLIB_DCPOMATIC XMLSEC1 OPENSSL DL LIBXML++ AVCODEC AVUTIL' obj.cppflags = ['-fno-inline', '-fno-elide-constructors', '-g', '-O0'] if bld.env['CXX_NAME'] == 'gcc': obj.cppflags.append('-fno-default-inline') diff --git a/tools/dcpdecryptmxf.cc b/tools/dcpdecryptmxf.cc index 451a4d34..1145d77d 100644 --- a/tools/dcpdecryptmxf.cc +++ b/tools/dcpdecryptmxf.cc @@ -41,8 +41,8 @@ #include "encrypted_kdm.h" #include "exceptions.h" #include "key.h" -#include "mono_picture_asset.h" -#include "mono_picture_asset_writer.h" +#include "mono_j2k_picture_asset.h" +#include "mono_j2k_picture_asset_writer.h" #include "sound_asset.h" #include "sound_asset_writer.h" #include "util.h" @@ -232,10 +232,10 @@ main (int argc, char* argv[]) } case Type::PICTURE: { - dcp::MonoPictureAsset in (input_file); + dcp::MonoJ2KPictureAsset in (input_file); add_key (in, decrypted_kdm); - dcp::MonoPictureAsset out (in.edit_rate(), dcp::Standard::SMPTE); - auto writer = out.start_write(output_file.get(), dcp::PictureAsset::Behaviour::MAKE_NEW); + dcp::MonoJ2KPictureAsset out (in.edit_rate(), dcp::Standard::SMPTE); + auto writer = out.start_write(output_file.get(), dcp::Behaviour::MAKE_NEW); copy (in, writer, ignore_hmac); break; } diff --git a/tools/dcpdumpimage.cc b/tools/dcpdumpimage.cc index 728e42c7..0ddf2d62 100644 --- a/tools/dcpdumpimage.cc +++ b/tools/dcpdumpimage.cc @@ -35,7 +35,7 @@ #include "colour_conversion.h" #include "cpl.h" #include "dcp.h" -#include "mono_picture_asset.h" +#include "mono_j2k_picture_asset.h" #include "openjpeg_image.h" #include "reel_picture_asset.h" #include "raw_convert.h" @@ -152,7 +152,7 @@ main(int argc, char* argv[]) if (frame_index >= duration) { frame_index -= duration; } else { - auto reader = dynamic_pointer_cast<dcp::MonoPictureAsset>(reel->main_picture()->asset())->start_read(); + auto reader = dynamic_pointer_cast<dcp::MonoJ2KPictureAsset>(reel->main_picture()->asset())->start_read(); auto frame = reader->get_frame(frame_index); auto xyz = frame->xyz_image(); std::vector<uint8_t> rgba(xyz->size().width * xyz->size().height * 4); diff --git a/tools/dcpinfo.cc b/tools/dcpinfo.cc index e812afe4..f68fac24 100644 --- a/tools/dcpinfo.cc +++ b/tools/dcpinfo.cc @@ -41,8 +41,8 @@ #include "exceptions.h" #include "filesystem.h" #include "interop_subtitle_asset.h" -#include "mono_picture_asset.h" -#include "picture_asset.h" +#include "mono_j2k_picture_asset.h" +#include "j2k_picture_asset.h" #include "reel.h" #include "reel_picture_asset.h" #include "reel_sound_asset.h" @@ -158,12 +158,12 @@ main_picture (vector<string> const& only, shared_ptr<Reel> reel, bool analyse, b OUTPUT_PICTURE("\n Picture: %1x%2\n", mp->asset()->size().width, mp->asset()->size().height); } - shared_ptr<MonoPictureAsset> ma = dynamic_pointer_cast<MonoPictureAsset>(mp->asset()); + shared_ptr<MonoJ2KPictureAsset> ma = dynamic_pointer_cast<MonoJ2KPictureAsset>(mp->asset()); if (analyse && ma) { - shared_ptr<MonoPictureAssetReader> reader = ma->start_read (); + shared_ptr<MonoJ2KPictureAssetReader> reader = ma->start_read (); pair<int, int> j2k_size_range (INT_MAX, 0); for (int64_t i = 0; i < ma->intrinsic_duration(); ++i) { - shared_ptr<const MonoPictureFrame> frame = reader->get_frame (i); + shared_ptr<const MonoJ2KPictureFrame> frame = reader->get_frame (i); if (SHOULD_PICTURE) { printf("Frame %" PRId64 " J2K size %7d", i, frame->size()); } diff --git a/tools/dcpverify.cc b/tools/dcpverify.cc index 0ef256eb..88f0d705 100644 --- a/tools/dcpverify.cc +++ b/tools/dcpverify.cc @@ -37,6 +37,7 @@ #include "filesystem.h" #include "raw_convert.h" #include "verify.h" +#include "verify_report.h" #include "version.h" #include <boost/bind/bind.hpp> #include <boost/filesystem.hpp> @@ -68,6 +69,7 @@ help (string n) << " --ignore-bv21-smpte don't give the SMPTE Bv2.1 error about a DCP not being SMPTE\n" << " --no-asset-hash-check don't check asset hashes\n" << " --asset-hash-check-maximum-size <size-in-MB> only check hashes for assets smaller than this size (in MB)\n" + << " -o <filename> write HTML report to filename\n" << " -q, --quiet don't report progress\n"; } @@ -80,6 +82,7 @@ main (int argc, char* argv[]) bool ignore_missing_assets = false; bool ignore_bv21_smpte = false; bool quiet = false; + boost::optional<boost::filesystem::path> report_filename; dcp::VerificationOptions verification_options; @@ -96,7 +99,7 @@ main (int argc, char* argv[]) { 0, 0, 0, 0 } }; - int c = getopt_long (argc, argv, "VhABCD:q", long_options, &option_index); + int c = getopt_long (argc, argv, "VhABCD:qo:", long_options, &option_index); if (c == -1) { break; @@ -126,6 +129,9 @@ main (int argc, char* argv[]) case 'q': quiet = true; break; + case 'o': + report_filename = optarg; + break; } } @@ -173,8 +179,8 @@ main (int argc, char* argv[]) vector<boost::filesystem::path> directories; directories.push_back (argv[optind]); - auto notes = dcp::verify(directories, {}, stage, progress, verification_options); - dcp::filter_notes (notes, ignore_missing_assets); + auto result = dcp::verify(directories, {}, stage, progress, verification_options); + dcp::filter_notes(result.notes, ignore_missing_assets); if (!quiet) { cout << "\n"; @@ -183,11 +189,13 @@ main (int argc, char* argv[]) bool failed = false; bool bv21_failed = false; bool warned = false; - for (auto i: notes) { + for (auto i: result.notes) { if (ignore_bv21_smpte && i.code() == dcp::VerificationNote::Code::INVALID_STANDARD) { continue; } switch (i.type()) { + case dcp::VerificationNote::Type::OK: + break; case dcp::VerificationNote::Type::ERROR: cout << "Error: " << note_to_string(i) << "\n"; failed = true; @@ -215,5 +223,10 @@ main (int argc, char* argv[]) } } + if (report_filename) { + dcp::HTMLFormatter formatter(*report_filename); + dcp::verify_report(result, formatter); + } + exit (failed ? EXIT_FAILURE : EXIT_SUCCESS); } diff --git a/tools/wscript b/tools/wscript index 3ee04e94..d282c88c 100644 --- a/tools/wscript +++ b/tools/wscript @@ -32,7 +32,7 @@ # def build(bld): - uselib = 'OPENJPEG CXML OPENMP ASDCPLIB_CTH BOOST_FILESYSTEM LIBXML++ XMLSEC1 OPENSSL XERCES DL MAGICK' + uselib = 'OPENJPEG CXML OPENMP ASDCPLIB_DCPOMATIC BOOST_FILESYSTEM LIBXML++ XMLSEC1 OPENSSL XERCES DL MAGICK AVCODEC AVUTIL' for f in ['diff', 'info', 'verify']: obj = bld(features='cxx cxxprogram') @@ -59,7 +59,7 @@ def options(opt): opt.add_option('--target-windows-64', action='store_true', default=False, help='set up to do a cross-compile to Windows 64-bit') opt.add_option('--target-windows-32', action='store_true', default=False, help='set up to do a cross-compile to Windows 32-bit') opt.add_option('--enable-debug', action='store_true', default=False, help='build with debugging information and without optimisation') - opt.add_option('--static', action='store_true', default=False, help='build libdcp statically, and link statically to openjpeg, cxml, asdcplib-carl') + opt.add_option('--static', action='store_true', default=False, help='build libdcp statically, and link statically to openjpeg, cxml, asdcplib-dcpomatic') opt.add_option('--disable-tests', action='store_true', default=False, help='disable building of tests') opt.add_option('--disable-benchmarks', action='store_true', default=False, help='disable building of benchmarks') opt.add_option('--enable-gcov', action='store_true', default=False, help='use gcov in tests') @@ -67,11 +67,20 @@ def options(opt): opt.add_option('--disable-dumpimage', action='store_true', default=False, help='disable building of dcpdumpimage') opt.add_option('--enable-openmp', action='store_true', default=False, help='enable use of OpenMP') opt.add_option('--openmp', default='gomp', help='specify OpenMP Library to use: omp, gomp (default), iomp') + opt.add_option('--c++17', action='store_true', default=False, help='build with C++17 and libxml++-4.0') def configure(conf): conf.load('compiler_cxx') conf.load('clang_compilation_database', tooldir=['waf-tools']) - conf.env.append_value('CXXFLAGS', ['-Wall', '-Wextra', '-D_FILE_OFFSET_BITS=64', '-D__STDC_FORMAT_MACROS', '-std=c++11']) + + if vars(conf.options)['c++17']: + cpp_std = '17' + conf.env.XMLPP_API = '4.0' + else: + cpp_std = '11' + conf.env.XMLPP_API = '2.6' + + conf.env.append_value('CXXFLAGS', ['-Wall', '-Wextra', '-D_FILE_OFFSET_BITS=64', '-D__STDC_FORMAT_MACROS', '-std=c++' + cpp_std]) gcc = conf.env['CC_VERSION'] if int(gcc[0]) >= 4 and int(gcc[1]) > 1: conf.env.append_value('CXXFLAGS', ['-Wno-maybe-uninitialized']) @@ -118,7 +127,7 @@ def configure(conf): conf.check(lib='dl', uselib_store='DL', msg='Checking for library dl') conf.check_cfg(package='openssl', args='--cflags --libs', uselib_store='OPENSSL', mandatory=True) - conf.check_cfg(package='libxml++-2.6', args='--cflags --libs', uselib_store='LIBXML++', mandatory=True) + conf.check_cfg(package='libxml++-' + conf.env.XMLPP_API, args='--cflags --libs', uselib_store='LIBXML++', mandatory=True) conf.check_cfg(package='xmlsec1', args='--cflags --libs', uselib_store='XMLSEC1', mandatory=True) # Remove erroneous escaping of quotes from xmlsec1 defines conf.env.DEFINES_XMLSEC1 = [f.replace('\\', '') for f in conf.env.DEFINES_XMLSEC1] @@ -153,20 +162,20 @@ def configure(conf): if not patched_openjpeg: # We don't have our patched version so we need 2.5.0 to get the GUARD_BITS option - conf.check_cfg(package='libopenjp2', args='libopenjp2 >= 2.5.0', uselib_store='OPENJPEG', mandatory=True, msg='Checking for libopenjp2 >= 2.5.0') + conf.check_cfg(package='libopenjp2', args='libopenjp2 >= 2.5.0 --cflags --libs', uselib_store='OPENJPEG', mandatory=True, msg='Checking for libopenjp2 >= 2.5.0') if conf.options.static: conf.env.STLIB_OPENJPEG = ['openjp2'] - conf.check_cfg(package='libasdcp-carl', args='libasdcp-carl >= 0.1.3 --cflags', uselib_store='ASDCPLIB_CTH', mandatory=True) - conf.env.HAVE_ASDCPLIB_CTH = 1 - conf.env.STLIB_ASDCPLIB_CTH = ['asdcp-carl', 'kumu-carl'] + conf.check_cfg(package='libasdcp-dcpomatic', args='libasdcp-dcpomatic >= 0.1.3 --cflags', uselib_store='ASDCPLIB_DCPOMATIC', mandatory=True) + conf.env.HAVE_ASDCPLIB_DCPOMATIC = 1 + conf.env.STLIB_ASDCPLIB_DCPOMATIC = ['asdcp-dcpomatic', 'kumu-dcpomatic'] conf.env.HAVE_CXML = 1 - conf.env.LIB_CXML = ['xml++-2.6', 'glibmm-2.4'] + conf.env.LIB_CXML = ['xml++-' + conf.env.XMLPP_API, 'glibmm-2.4'] conf.env.STLIB_CXML = ['cxml'] conf.check_cfg(package='xerces-c', args='--cflags', uselib_store='XERCES', mandatory=True) conf.env.LIB_XERCES = ['xerces-c', 'icuuc', 'curl'] else: - conf.check_cfg(package='libasdcp-carl', args='libasdcp-carl >= 0.1.3 --cflags --libs', uselib_store='ASDCPLIB_CTH', mandatory=True) + conf.check_cfg(package='libasdcp-dcpomatic', args='libasdcp-dcpomatic >= 0.1.3 --cflags --libs', uselib_store='ASDCPLIB_DCPOMATIC', mandatory=True) conf.check_cfg(package='libcxml', args='libcxml >= 0.17.0 --cflags --libs', uselib_store='CXML', mandatory=True) conf.check_cfg(package='xerces-c', args='--cflags --libs', uselib_store='XERCES', mandatory=True) @@ -239,6 +248,9 @@ def configure(conf): lib=['boost_date_time%s' % boost_lib_suffix, 'boost_system%s' % boost_lib_suffix], uselib_store='BOOST_DATETIME') + conf.check_cfg(package='libavcodec', args='--cflags --libs', uselib_store='AVCODEC', mandatory=True) + conf.check_cfg(package='libavutil', args='--cflags --libs', uselib_store='AVUTIL', mandatory=True) + if not conf.env.DISABLE_TESTS: conf.recurse('test') if conf.options.enable_gcov: @@ -263,7 +275,8 @@ def build(bld): version=VERSION, includedir='%s/include/libdcp%s' % (bld.env.PREFIX, bld.env.API_VERSION), libs=libs, - install_path='${LIBDIR}/pkgconfig') + install_path='${LIBDIR}/pkgconfig', + xmlpp_api=bld.env.XMLPP_ABI) bld.recurse('src') bld.recurse('tools') |
