summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/asset_factory.cc19
-rw-r--r--src/asset_list.h4
-rw-r--r--src/asset_map.cc40
-rw-r--r--src/asset_reader.h14
-rw-r--r--src/atmos_asset.cc4
-rw-r--r--src/behaviour.h52
-rw-r--r--src/certificate_chain.cc49
-rw-r--r--src/chromaticity.h4
-rw-r--r--src/colour_conversion.h4
-rw-r--r--src/cpl.cc106
-rw-r--r--src/dcp.cc12
-rw-r--r--src/dcp_time.h4
-rw-r--r--src/encrypted_kdm.cc121
-rw-r--r--src/exceptions.h27
-rw-r--r--src/ffmpeg_image.cc122
-rw-r--r--src/ffmpeg_image.h107
-rw-r--r--src/frame_info.h109
-rw-r--r--src/interop_subtitle_asset.cc10
-rw-r--r--src/interop_subtitle_asset.h4
-rw-r--r--src/j2k_picture_asset.cc229
-rw-r--r--src/j2k_picture_asset.h110
-rw-r--r--src/j2k_picture_asset_writer.cc (renamed from src/picture_asset_writer.cc)12
-rw-r--r--src/j2k_picture_asset_writer.h (renamed from src/picture_asset_writer.h)46
-rw-r--r--src/j2k_picture_asset_writer_common.cc (renamed from src/picture_asset_writer_common.cc)10
-rw-r--r--src/language_tag.h2
-rw-r--r--src/locale_convert.h2
-rw-r--r--src/mono_j2k_picture_asset.cc (renamed from src/mono_picture_asset.cc)50
-rw-r--r--src/mono_j2k_picture_asset.h (renamed from src/mono_picture_asset.h)32
-rw-r--r--src/mono_j2k_picture_asset_reader.h (renamed from src/stereo_picture_asset_reader.h)12
-rw-r--r--src/mono_j2k_picture_asset_writer.cc (renamed from src/mono_picture_asset_writer.cc)34
-rw-r--r--src/mono_j2k_picture_asset_writer.h (renamed from src/mono_picture_asset_writer.h)30
-rw-r--r--src/mono_j2k_picture_frame.cc (renamed from src/mono_picture_frame.cc)18
-rw-r--r--src/mono_j2k_picture_frame.h (renamed from src/mono_picture_frame.h)26
-rw-r--r--src/mono_mpeg2_picture_asset.cc86
-rw-r--r--src/mono_mpeg2_picture_asset.h73
-rw-r--r--src/mono_mpeg2_picture_asset_reader.h58
-rw-r--r--src/mono_mpeg2_picture_asset_writer.cc144
-rw-r--r--src/mono_mpeg2_picture_asset_writer.h68
-rw-r--r--src/mono_mpeg2_picture_frame.cc91
-rw-r--r--src/mono_mpeg2_picture_frame.h81
-rw-r--r--src/mpeg2_picture_asset.cc74
-rw-r--r--src/mpeg2_picture_asset.h90
-rw-r--r--src/mpeg2_picture_asset_writer.cc48
-rw-r--r--src/mpeg2_picture_asset_writer.h73
-rw-r--r--src/mpeg2_picture_asset_writer_common.cc93
-rw-r--r--src/mpeg2_transcode.cc216
-rw-r--r--src/mpeg2_transcode.h108
-rw-r--r--src/mxf.h7
-rw-r--r--src/name_format.h4
-rw-r--r--src/picture_asset.cc187
-rw-r--r--src/picture_asset.h72
-rw-r--r--src/pkl.cc26
-rw-r--r--src/pkl.h2
-rw-r--r--src/rating.cc4
-rw-r--r--src/reel.cc14
-rw-r--r--src/reel.h2
-rw-r--r--src/reel_asset.cc18
-rw-r--r--src/reel_asset.h2
-rw-r--r--src/reel_atmos_asset.cc6
-rw-r--r--src/reel_atmos_asset.h2
-rw-r--r--src/reel_file_asset.cc10
-rw-r--r--src/reel_file_asset.h2
-rw-r--r--src/reel_interop_closed_caption_asset.cc6
-rw-r--r--src/reel_interop_closed_caption_asset.h2
-rw-r--r--src/reel_markers_asset.cc12
-rw-r--r--src/reel_markers_asset.h4
-rw-r--r--src/reel_mono_picture_asset.cc4
-rw-r--r--src/reel_mono_picture_asset.h29
-rw-r--r--src/reel_picture_asset.cc18
-rw-r--r--src/reel_picture_asset.h36
-rw-r--r--src/reel_smpte_closed_caption_asset.cc6
-rw-r--r--src/reel_smpte_closed_caption_asset.h2
-rw-r--r--src/reel_smpte_subtitle_asset.h4
-rw-r--r--src/reel_stereo_picture_asset.cc4
-rw-r--r--src/reel_stereo_picture_asset.h18
-rw-r--r--src/reel_subtitle_asset.cc6
-rw-r--r--src/reel_subtitle_asset.h2
-rw-r--r--src/rgb_xyz.h2
-rw-r--r--src/smpte_subtitle_asset.cc31
-rw-r--r--src/sound_asset.cc12
-rw-r--r--src/sound_asset.h8
-rw-r--r--src/sound_frame.cc22
-rw-r--r--src/sound_frame.h8
-rw-r--r--src/stereo_j2k_picture_asset.cc (renamed from src/stereo_picture_asset.cc)46
-rw-r--r--src/stereo_j2k_picture_asset.h (renamed from src/stereo_picture_asset.h)26
-rw-r--r--src/stereo_j2k_picture_asset_reader.h (renamed from src/mono_picture_asset_reader.h)12
-rw-r--r--src/stereo_j2k_picture_asset_writer.cc (renamed from src/stereo_picture_asset_writer.cc)34
-rw-r--r--src/stereo_j2k_picture_asset_writer.h (renamed from src/stereo_picture_asset_writer.h)26
-rw-r--r--src/stereo_j2k_picture_frame.cc (renamed from src/stereo_picture_frame.cc)28
-rw-r--r--src/stereo_j2k_picture_frame.h (renamed from src/stereo_picture_frame.h)28
-rw-r--r--src/subtitle_asset_internal.cc16
-rw-r--r--src/types.cc8
-rw-r--r--src/verify.cc960
-rw-r--r--src/verify.h53
-rw-r--r--src/verify_j2k.h4
-rw-r--r--src/verify_report.cc144
-rw-r--r--src/verify_report.h237
-rw-r--r--src/wscript71
98 files changed, 3699 insertions, 1316 deletions
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"
diff --git a/src/cpl.cc b/src/cpl.cc
index 6a25863a..e8cec95c 100644
--- a/src/cpl.cc
+++ b/src/cpl.cc
@@ -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 */
diff --git a/src/dcp.cc b/src/dcp.cc
index eb21b47d..f0379202 100644
--- a/src/dcp.cc
+++ b/src/dcp.cc
@@ -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
diff --git a/src/mxf.h b/src/mxf.h
index 19d4a956..1692fbd0 100644
--- a/src/mxf.h
+++ b/src/mxf.h
@@ -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
+
diff --git a/src/pkl.cc b/src/pkl.cc
index 57eda9da..463d046a 100644
--- a/src/pkl.cc
+++ b/src/pkl.cc
@@ -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);
}
}
diff --git a/src/pkl.h b/src/pkl.h
index d0a11188..88443057 100644
--- a/src/pkl.h
+++ b/src/pkl.h
@@ -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());
diff --git a/src/reel.h b/src/reel.h
index 52f41bfa..df6d12a9 100644
--- a/src/reel.h
+++ b/src/reel.h
@@ -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..c73255b8 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));
@@ -82,6 +83,7 @@ SoundAsset::SoundAsset (boost::filesystem::path file)
}
_sampling_rate = desc.AudioSamplingRate.Denominator ? (desc.AudioSamplingRate.Numerator / desc.AudioSamplingRate.Denominator) : 0;
+ _bit_depth = desc.QuantizationBits;
_channels = desc.ChannelCount;
_edit_rate = Fraction (desc.EditRate.Numerator, desc.EditRate.Denominator);
@@ -138,14 +140,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 +281,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/sound_asset.h b/src/sound_asset.h
index e69c3988..e5acb119 100644
--- a/src/sound_asset.h
+++ b/src/sound_asset.h
@@ -115,6 +115,13 @@ public:
return _sampling_rate;
}
+ /** @return Bit depth of samples. This should always be 24, but we return it
+ * so the verification code can check
+ */
+ int bit_depth() const {
+ return _bit_depth;
+ }
+
Fraction edit_rate () const {
return _edit_rate;
}
@@ -148,6 +155,7 @@ private:
int _channels = 0; ///< number of channels in the MXF
boost::optional<int> _active_channels; ///< estimate of the number of active channels
int _sampling_rate = 0; ///< sampling rate in Hz
+ int _bit_depth = 24;
boost::optional<std::string> _language;
};
diff --git a/src/sound_frame.cc b/src/sound_frame.cc
index 25845d88..4d90c9c2 100644
--- a/src/sound_frame.cc
+++ b/src/sound_frame.cc
@@ -37,6 +37,7 @@
*/
+#include "dcp_assert.h"
#include "sound_frame.h"
#include <asdcp/AS_DCP.h>
#include <iostream>
@@ -52,15 +53,28 @@ SoundFrame::SoundFrame (ASDCP::PCM::MXFReader* reader, int n, std::shared_ptr<co
ASDCP::PCM::AudioDescriptor desc;
reader->FillAudioDescriptor (desc);
_channels = desc.ChannelCount;
+ _bits = desc.QuantizationBits;
}
int32_t
SoundFrame::get (int channel, int frame) const
{
- uint8_t const * d = data() + (frame * _channels * 3) + (channel * 3);
- /* This is slightly dubious I think */
- return (d[0] << 8 | (d[1] << 16) | (d[2] << 24)) >> 8;
+ switch (_bits) {
+ case 24:
+ {
+ uint8_t const * d = data() + (frame * _channels * 3) + (channel * 3);
+ /* This is slightly dubious I think */
+ return (d[0] << 8 | (d[1] << 16) | (d[2] << 24)) >> 8;
+ }
+ case 16:
+ {
+ uint8_t const * d = data() + (frame * _channels * 2) + (channel * 2);
+ return d[0] | (d[1] << 8);
+ }
+ default:
+ DCP_ASSERT(false);
+ }
}
@@ -74,5 +88,5 @@ SoundFrame::channels () const
int
SoundFrame::samples () const
{
- return size() / (_channels * 3);
+ return size() / (_channels * _bits / 8);
}
diff --git a/src/sound_frame.h b/src/sound_frame.h
index 0f5021e2..72ab5925 100644
--- a/src/sound_frame.h
+++ b/src/sound_frame.h
@@ -54,10 +54,16 @@ public:
SoundFrame (ASDCP::PCM::MXFReader* reader, int n, std::shared_ptr<const DecryptionContext> c, bool check_hmac);
int channels () const;
int samples () const;
+
+ int bits() const {
+ return _bits;
+ }
+
int32_t get (int channel, int sample) const;
private:
- int _channels = 0;
+ int _channels;
+ int _bits;
};
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 ec8925f2..1e27514a 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>
@@ -89,6 +89,7 @@
using std::cout;
using std::dynamic_pointer_cast;
+using std::function;
using std::list;
using std::make_shared;
using std::map;
@@ -98,7 +99,6 @@ using std::shared_ptr;
using std::string;
using std::vector;
using boost::optional;
-using boost::function;
using namespace dcp;
@@ -294,9 +294,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 ();
@@ -349,7 +423,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 {
@@ -367,13 +441,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()
- });
+ );
}
}
@@ -387,9 +460,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
)
@@ -403,11 +475,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());
@@ -437,103 +509,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 = [&notes](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,
@@ -542,17 +617,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 (
@@ -560,12 +634,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 */
@@ -573,69 +642,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,
@@ -644,58 +694,61 @@ 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);
+ }
+ if (asset->bit_depth() != 24) {
+ context.error(VerificationNote::Code::INVALID_SOUND_BIT_DEPTH, raw_convert<string>(asset->bit_depth()), 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());
}
}
@@ -703,22 +756,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"
@@ -730,54 +781,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);
}
}
@@ -785,14 +830,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()) {
@@ -802,7 +847,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);
}
}
}
@@ -810,23 +855,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) {
@@ -842,19 +880,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());
}
}
}
@@ -863,35 +901,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);
}
}
@@ -900,10 +936,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,
@@ -997,7 +1032,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;
}
@@ -1008,7 +1043,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);
@@ -1031,8 +1066,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])));
}
}
@@ -1041,47 +1076,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) {
@@ -1147,7 +1169,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;
}
@@ -1170,15 +1192,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);
}
}
@@ -1284,14 +1302,14 @@ dcp::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());
},
@@ -1308,7 +1326,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();
},
@@ -1324,12 +1342,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");
@@ -1379,9 +1397,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());
}
}
@@ -1414,17 +1432,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,
@@ -1434,24 +1445,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;
}
}
@@ -1468,32 +1479,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()
+ );
}
}
}
@@ -1501,13 +1506,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 {
@@ -1515,9 +1520,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());
}
}
@@ -1526,10 +1531,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);
}
}
@@ -1541,26 +1546,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") {
@@ -1572,61 +1572,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 */
@@ -1640,7 +1650,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);
}
}
@@ -1655,38 +1667,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,
@@ -1696,51 +1709,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));
}
}
@@ -1752,12 +1764,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();
@@ -1770,26 +1782,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());
}
}
}
@@ -1798,27 +1810,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;
}
}
@@ -1828,28 +1835,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,
@@ -1865,7 +1868,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) {
@@ -1874,22 +1877,25 @@ dcp::verify (
for (auto dcp: dcps) {
stage ("Checking DCP", dcp->directory());
+
+ context.dcp = dcp;
+
bool carry_on = true;
try {
dcp->read (&notes, 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) {
@@ -1906,16 +1912,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())});
}
@@ -1923,23 +1922,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.).
*
@@ -1951,238 +1950,269 @@ 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::INVALID_SOUND_BIT_DEPTH:
+ return compose("The sound asset %1 has a bit depth 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 "";
@@ -2203,12 +2233,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 204d83b0..e9a67f83 100644
--- a/src/verify.h
+++ b/src/verify.h
@@ -58,6 +58,7 @@
namespace dcp {
+class DCP;
class SubtitleAsset;
@@ -65,12 +66,13 @@ 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"
@@ -101,6 +103,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
@@ -112,6 +115,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
@@ -156,6 +160,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
*/
@@ -178,6 +183,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
@@ -250,6 +256,11 @@ public:
* file contains the asset filename
*/
INVALID_SOUND_FRAME_RATE,
+ /** The audio bit depth must be 24
+ * note contains the invalid bit depth
+ * file contains the asset filename
+ */
+ INVALID_SOUND_BIT_DEPTH,
/** The CPL has no _<AnnotationText>_ tag [Bv2.1_8.1]
* note contains the CPL ID
* file contains the CPL filename
@@ -260,6 +271,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 */
@@ -337,6 +349,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
@@ -409,11 +426,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
@@ -478,13 +497,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
@@ -545,6 +564,7 @@ private:
ID,
OTHER_ID,
FRAME_RATE,
+ CPL_ID,
CALCULATED_HASH,
REFERENCE_HASH
};
@@ -644,6 +664,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;
@@ -661,18 +690,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, "<", "&lt;");
+ boost::replace_all(s, ">", "&gt;");
+ 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']