diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/asset_factory.cc | 23 | ||||
| -rw-r--r-- | src/asset_list.h | 4 | ||||
| -rw-r--r-- | src/asset_map.cc | 40 | ||||
| -rw-r--r-- | src/asset_reader.h | 14 | ||||
| -rw-r--r-- | src/atmos_asset.cc | 4 | ||||
| -rw-r--r-- | src/behaviour.h | 52 | ||||
| -rw-r--r-- | src/certificate_chain.cc | 49 | ||||
| -rw-r--r-- | src/chromaticity.h | 4 | ||||
| -rw-r--r-- | src/colour_conversion.h | 4 | ||||
| -rw-r--r-- | src/combine.cc | 4 | ||||
| -rw-r--r-- | src/cpl.cc | 109 | ||||
| -rw-r--r-- | src/dcp.cc | 26 | ||||
| -rw-r--r-- | src/dcp_time.h | 4 | ||||
| -rw-r--r-- | src/encrypted_kdm.cc | 121 | ||||
| -rw-r--r-- | src/equality_options.h | 8 | ||||
| -rw-r--r-- | src/exceptions.cc | 4 | ||||
| -rw-r--r-- | src/exceptions.h | 31 | ||||
| -rw-r--r-- | src/ffmpeg_image.cc | 122 | ||||
| -rw-r--r-- | src/ffmpeg_image.h | 107 | ||||
| -rw-r--r-- | src/frame_info.h (renamed from src/reel_closed_caption_asset.cc) | 91 | ||||
| -rw-r--r-- | src/interop_text_asset.cc (renamed from src/interop_subtitle_asset.cc) | 77 | ||||
| -rw-r--r-- | src/interop_text_asset.h (renamed from src/interop_subtitle_asset.h) | 18 | ||||
| -rw-r--r-- | src/j2k_picture_asset.cc | 229 | ||||
| -rw-r--r-- | src/j2k_picture_asset.h | 110 | ||||
| -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.h | 2 | ||||
| -rw-r--r-- | src/locale_convert.h | 2 | ||||
| -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/mono_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.cc | 86 | ||||
| -rw-r--r-- | src/mono_mpeg2_picture_asset.h (renamed from src/reel_smpte_subtitle_asset.cc) | 47 | ||||
| -rw-r--r-- | src/mono_mpeg2_picture_asset_reader.h | 58 | ||||
| -rw-r--r-- | src/mono_mpeg2_picture_asset_writer.cc | 144 | ||||
| -rw-r--r-- | src/mono_mpeg2_picture_asset_writer.h | 68 | ||||
| -rw-r--r-- | src/mono_mpeg2_picture_frame.cc | 91 | ||||
| -rw-r--r-- | src/mono_mpeg2_picture_frame.h | 81 | ||||
| -rw-r--r-- | src/mpeg2_picture_asset.cc | 74 | ||||
| -rw-r--r-- | src/mpeg2_picture_asset.h | 90 | ||||
| -rw-r--r-- | src/mpeg2_picture_asset_writer.cc (renamed from src/reel_interop_subtitle_asset.cc) | 33 | ||||
| -rw-r--r-- | src/mpeg2_picture_asset_writer.h | 73 | ||||
| -rw-r--r-- | src/mpeg2_picture_asset_writer_common.cc | 93 | ||||
| -rw-r--r-- | src/mpeg2_transcode.cc | 216 | ||||
| -rw-r--r-- | src/mpeg2_transcode.h (renamed from src/reel_closed_caption_asset.h) | 85 | ||||
| -rw-r--r-- | src/mxf.h | 7 | ||||
| -rw-r--r-- | src/name_format.h | 4 | ||||
| -rw-r--r-- | src/picture_asset.cc | 187 | ||||
| -rw-r--r-- | src/picture_asset.h | 72 | ||||
| -rw-r--r-- | src/pkl.cc | 26 | ||||
| -rw-r--r-- | src/pkl.h | 2 | ||||
| -rw-r--r-- | src/rating.cc | 4 | ||||
| -rw-r--r-- | src/reel.cc | 178 | ||||
| -rw-r--r-- | src/reel.h | 24 | ||||
| -rw-r--r-- | src/reel_asset.cc | 20 | ||||
| -rw-r--r-- | src/reel_asset.h | 4 | ||||
| -rw-r--r-- | src/reel_atmos_asset.cc | 8 | ||||
| -rw-r--r-- | src/reel_atmos_asset.h | 4 | ||||
| -rw-r--r-- | src/reel_file_asset.cc | 10 | ||||
| -rw-r--r-- | src/reel_file_asset.h | 2 | ||||
| -rw-r--r-- | src/reel_interop_closed_caption_asset.h | 77 | ||||
| -rw-r--r-- | src/reel_interop_text_asset.cc (renamed from src/reel_smpte_closed_caption_asset.cc) | 64 | ||||
| -rw-r--r-- | src/reel_interop_text_asset.h (renamed from src/reel_interop_subtitle_asset.h) | 33 | ||||
| -rw-r--r-- | src/reel_markers_asset.cc | 14 | ||||
| -rw-r--r-- | src/reel_markers_asset.h | 6 | ||||
| -rw-r--r-- | src/reel_mono_picture_asset.cc | 6 | ||||
| -rw-r--r-- | src/reel_mono_picture_asset.h | 31 | ||||
| -rw-r--r-- | src/reel_picture_asset.cc | 18 | ||||
| -rw-r--r-- | src/reel_picture_asset.h | 36 | ||||
| -rw-r--r-- | src/reel_smpte_closed_caption_asset.h | 83 | ||||
| -rw-r--r-- | src/reel_smpte_text_asset.cc (renamed from src/reel_interop_closed_caption_asset.cc) | 60 | ||||
| -rw-r--r-- | src/reel_smpte_text_asset.h (renamed from src/reel_smpte_subtitle_asset.h) | 34 | ||||
| -rw-r--r-- | src/reel_sound_asset.cc | 2 | ||||
| -rw-r--r-- | src/reel_sound_asset.h | 2 | ||||
| -rw-r--r-- | src/reel_stereo_picture_asset.cc | 6 | ||||
| -rw-r--r-- | src/reel_stereo_picture_asset.h | 20 | ||||
| -rw-r--r-- | src/reel_text_asset.cc (renamed from src/reel_subtitle_asset.cc) | 47 | ||||
| -rw-r--r-- | src/reel_text_asset.h (renamed from src/reel_subtitle_asset.h) | 49 | ||||
| -rw-r--r-- | src/rgb_xyz.h | 2 | ||||
| -rw-r--r-- | src/smpte_text_asset.cc (renamed from src/smpte_subtitle_asset.cc) | 130 | ||||
| -rw-r--r-- | src/smpte_text_asset.h (renamed from src/smpte_subtitle_asset.h) | 30 | ||||
| -rw-r--r-- | src/sound_asset.cc | 12 | ||||
| -rw-r--r-- | src/sound_asset.h | 8 | ||||
| -rw-r--r-- | src/sound_frame.cc | 22 | ||||
| -rw-r--r-- | src/sound_frame.h | 8 | ||||
| -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/stereo_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/text.cc (renamed from src/subtitle.cc) | 30 | ||||
| -rw-r--r-- | src/text.h (renamed from src/subtitle.h) | 20 | ||||
| -rw-r--r-- | src/text_asset.cc (renamed from src/subtitle_asset.cc) | 139 | ||||
| -rw-r--r-- | src/text_asset.h (renamed from src/subtitle_asset.h) | 50 | ||||
| -rw-r--r-- | src/text_asset_internal.cc (renamed from src/subtitle_asset_internal.cc) | 26 | ||||
| -rw-r--r-- | src/text_asset_internal.h (renamed from src/subtitle_asset_internal.h) | 12 | ||||
| -rw-r--r-- | src/text_image.cc (renamed from src/subtitle_image.cc) | 52 | ||||
| -rw-r--r-- | src/text_image.h (renamed from src/subtitle_image.h) | 28 | ||||
| -rw-r--r-- | src/text_string.cc (renamed from src/subtitle_string.cc) | 50 | ||||
| -rw-r--r-- | src/text_string.h (renamed from src/subtitle_string.h) | 25 | ||||
| -rw-r--r-- | src/text_type.h | 55 | ||||
| -rw-r--r-- | src/types.cc | 8 | ||||
| -rw-r--r-- | src/types.h | 6 | ||||
| -rw-r--r-- | src/verify.cc | 999 | ||||
| -rw-r--r-- | src/verify.h | 57 | ||||
| -rw-r--r-- | src/verify_j2k.h | 4 | ||||
| -rw-r--r-- | src/verify_report.cc | 151 | ||||
| -rw-r--r-- | src/verify_report.h | 240 | ||||
| -rw-r--r-- | src/warnings.h | 17 | ||||
| -rw-r--r-- | src/wscript | 114 |
117 files changed, 4248 insertions, 2155 deletions
diff --git a/src/asset_factory.cc b/src/asset_factory.cc index be4f6b49..652e1e31 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 "smpte_subtitle_asset.h" +#include "mono_j2k_picture_asset.h" +#include "mono_mpeg2_picture_asset.h" +#include "smpte_text_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,9 +91,9 @@ 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); + return make_shared<SMPTETextAsset>(path); case ASDCP::ESS_DCDATA_DOLBY_ATMOS: return make_shared<AtmosAsset>(path); default: 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/combine.cc b/src/combine.cc index b7a625f0..74456bfb 100644 --- a/src/combine.cc +++ b/src/combine.cc @@ -45,7 +45,7 @@ #include "exceptions.h" #include "filesystem.h" #include "font_asset.h" -#include "interop_subtitle_asset.h" +#include "interop_text_asset.h" #include "raw_convert.h" #include <boost/filesystem.hpp> #include <set> @@ -139,7 +139,7 @@ dcp::combine ( continue; } - auto sub = dynamic_pointer_cast<dcp::InteropSubtitleAsset>(j); + auto sub = dynamic_pointer_cast<dcp::InteropTextAsset>(j); if (sub) { /* Interop fonts are really fiddly. The font files are assets (in the ASSETMAP) * and also linked from the font XML by filename. We have to fix both these things, @@ -48,10 +48,9 @@ #include "raw_convert.h" #include "reel.h" #include "reel_atmos_asset.h" -#include "reel_closed_caption_asset.h" #include "reel_picture_asset.h" #include "reel_sound_asset.h" -#include "reel_subtitle_asset.h" +#include "reel_text_asset.h" #include "util.h" #include "version.h" #include "warnings.h" @@ -213,15 +212,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 +232,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 +402,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 +434,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 +480,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 +498,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 +510,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 +533,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 +566,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 */ @@ -49,17 +49,18 @@ #include "exceptions.h" #include "filesystem.h" #include "font_asset.h" -#include "interop_subtitle_asset.h" +#include "interop_text_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 "reel_text_asset.h" +#include "smpte_text_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" @@ -221,7 +222,7 @@ DCP::read (vector<dcp::VerificationNote>* notes, bool ignore_incorrect_picture_m if ( pkl_type == remove_parameters(CPL::static_pkl_type(standard)) || - pkl_type == remove_parameters(InteropSubtitleAsset::static_pkl_type(standard))) { + pkl_type == remove_parameters(InteropTextAsset::static_pkl_type(standard))) { auto p = new xmlpp::DomParser; try { p->parse_file(dcp::filesystem::fix_long_path(path).string()); @@ -243,13 +244,14 @@ DCP::read (vector<dcp::VerificationNote>* notes, bool ignore_incorrect_picture_m if (standard == Standard::SMPTE && notes) { notes->push_back (VerificationNote(VerificationNote::Type::ERROR, VerificationNote::Code::MISMATCHED_STANDARD)); } - other_assets.push_back (make_shared<InteropSubtitleAsset>(path)); + other_assets.push_back (make_shared<InteropTextAsset>(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)) + *pkl_type == remove_parameters(SMPTETextAsset::static_pkl_type(standard)) ) { bool found_threed_marked_as_twod = false; @@ -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"); } @@ -542,7 +544,7 @@ DCP::assets (bool ignore_unresolved) const auto o = j->asset_ref().asset(); assets.push_back (o); /* More Interop special-casing */ - auto sub = dynamic_pointer_cast<InteropSubtitleAsset>(o); + auto sub = dynamic_pointer_cast<InteropTextAsset>(o); if (sub) { add_to_container(assets, sub->font_assets()); } 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/equality_options.h b/src/equality_options.h index 1c29be7e..1e890a09 100644 --- a/src/equality_options.h +++ b/src/equality_options.h @@ -77,10 +77,10 @@ public: bool load_font_nodes_can_differ = false; bool sound_assets_can_differ = false; bool keep_going = false; - /** true to save the last pair of different image subtitles to the current working directory */ - bool export_differing_subtitles = false; - /** The maximum allowable absolute difference between the vertical position of subtitles */ - float max_subtitle_vertical_position_error = 0; + /** true to save the last pair of different image subtitles/captions to the current working directory */ + bool export_differing_texts = false; + /** The maximum allowable absolute difference between the vertical position of texts */ + float max_text_vertical_position_error = 0; }; diff --git a/src/exceptions.cc b/src/exceptions.cc index 9c57011d..4420125c 100644 --- a/src/exceptions.cc +++ b/src/exceptions.cc @@ -121,8 +121,8 @@ ReadError::ReadError (string message, string detail) } -MissingSubtitleImageError::MissingSubtitleImageError (string id) - : runtime_error (String::compose("Could not load image for subtitle %1", id)) +MissingTextImageError::MissingTextImageError (string id) + : runtime_error (String::compose("Could not load image for subtitle/caption %1", id)) { } diff --git a/src/exceptions.h b/src/exceptions.h index 8d85f02a..145df25f 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: @@ -232,10 +259,10 @@ public: }; -class MissingSubtitleImageError : public std::runtime_error +class MissingTextImageError : public std::runtime_error { public: - MissingSubtitleImageError (std::string id); + MissingTextImageError (std::string id); }; 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/reel_closed_caption_asset.cc b/src/frame_info.h index e5649d6a..325350c4 100644 --- a/src/reel_closed_caption_asset.cc +++ b/src/frame_info.h @@ -32,59 +32,78 @@ */ -/** @file src/reel_closed_caption_asset.cc - * @brief ReelClosedCaptionAsset class - */ +#ifndef LIBDCP_FRAME_INFO_H +#define LIBDCP_FRAME_INFO_H -#include "dcp_assert.h" -#include "reel_closed_caption_asset.h" -#include "smpte_subtitle_asset.h" -#include "subtitle_asset.h" #include "warnings.h" LIBDCP_DISABLE_WARNINGS -#include <libxml++/libxml++.h> +#include <asdcp/AS_DCP.h> LIBDCP_ENABLE_WARNINGS +#include <stdint.h> +#include <string> -using std::string; -using std::shared_ptr; -using std::dynamic_pointer_cast; -using namespace dcp; +namespace dcp { -ReelClosedCaptionAsset::ReelClosedCaptionAsset (std::shared_ptr<SubtitleAsset> asset, Fraction edit_rate, int64_t intrinsic_duration, int64_t entry_point) - : ReelFileAsset ( - asset, - dynamic_pointer_cast<SMPTESubtitleAsset>(asset) ? dynamic_pointer_cast<SMPTESubtitleAsset>(asset)->key_id() : boost::none, - asset->id(), - edit_rate, - intrinsic_duration, - entry_point - ) +/** @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; +}; -ReelClosedCaptionAsset::ReelClosedCaptionAsset (std::shared_ptr<const cxml::Node> node) - : ReelFileAsset (node) +struct J2KFrameInfo : public FrameInfo { - _language = node->optional_string_child ("Language"); -} + J2KFrameInfo() = default; + J2KFrameInfo(uint64_t offset_, uint64_t size_, std::string hash_) + : FrameInfo(offset_, size_, hash_) + {} +}; -bool -ReelClosedCaptionAsset::equals(shared_ptr<const ReelClosedCaptionAsset> other, EqualityOptions const& opt, NoteHandler note) const + +struct MPEG2FrameInfo : public FrameInfo { - if (!asset_equals (other, opt, note)) { - return false; - } - if (!file_asset_equals (other, opt, note)) { - return false; - } - - return true; + 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_text_asset.cc index 253a0498..01e0f89a 100644 --- a/src/interop_subtitle_asset.cc +++ b/src/interop_text_asset.cc @@ -32,8 +32,8 @@ */ -/** @file src/interop_subtitle_asset.cc - * @brief InteropSubtitleAsset class +/** @file src/interop_text_asset.cc + * @brief InteropTextAsset class */ @@ -44,10 +44,10 @@ #include "font_asset.h" #include "file.h" #include "interop_load_font_node.h" -#include "interop_subtitle_asset.h" +#include "interop_text_asset.h" #include "raw_convert.h" -#include "subtitle_asset_internal.h" -#include "subtitle_image.h" +#include "text_asset_internal.h" +#include "text_image.h" #include "util.h" #include "warnings.h" #include "xml.h" @@ -70,8 +70,8 @@ using boost::optional; using namespace dcp; -InteropSubtitleAsset::InteropSubtitleAsset (boost::filesystem::path file) - : SubtitleAsset (file) +InteropTextAsset::InteropTextAsset(boost::filesystem::path file) + : TextAsset(file) { _raw_xml = dcp::file_to_string(file, 10 * 1024 * 1024); @@ -89,12 +89,12 @@ InteropSubtitleAsset::InteropSubtitleAsset (boost::filesystem::path file) for (auto i: xml->node()->get_children()) { auto e = dynamic_cast<xmlpp::Element const *>(i); if (e && (e->get_name() == "Font" || e->get_name() == "Subtitle")) { - parse_subtitles (e, ps, optional<int>(), Standard::INTEROP); + parse_texts (e, ps, optional<int>(), Standard::INTEROP); } } - for (auto i: _subtitles) { - auto si = dynamic_pointer_cast<SubtitleImage>(i); + for (auto i: _texts) { + auto si = dynamic_pointer_cast<TextImage>(i); if (si) { si->read_png_file (file.parent_path() / String::compose("%1.png", si->id())); } @@ -102,38 +102,38 @@ InteropSubtitleAsset::InteropSubtitleAsset (boost::filesystem::path file) } -InteropSubtitleAsset::InteropSubtitleAsset () +InteropTextAsset::InteropTextAsset() { } string -InteropSubtitleAsset::xml_as_string () const +InteropTextAsset::xml_as_string() const { xmlpp::Document doc; 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); } - subtitles_as_xml (root, 250, Standard::INTEROP); + texts_as_xml(root, 250, Standard::INTEROP); return format_xml(doc, {}); } void -InteropSubtitleAsset::add_font (string load_id, dcp::ArrayData data) +InteropTextAsset::add_font(string load_id, dcp::ArrayData data) { _fonts.push_back (Font(load_id, make_uuid(), data)); auto const uri = String::compose("font_%1.ttf", _load_font_nodes.size()); @@ -142,13 +142,13 @@ InteropSubtitleAsset::add_font (string load_id, dcp::ArrayData data) bool -InteropSubtitleAsset::equals(shared_ptr<const Asset> other_asset, EqualityOptions const& options, NoteHandler note) const +InteropTextAsset::equals(shared_ptr<const Asset> other_asset, EqualityOptions const& options, NoteHandler note) const { - if (!SubtitleAsset::equals (other_asset, options, note)) { + if (!TextAsset::equals (other_asset, options, note)) { return false; } - auto other = dynamic_pointer_cast<const InteropSubtitleAsset> (other_asset); + auto other = dynamic_pointer_cast<const InteropTextAsset> (other_asset); if (!other) { return false; } @@ -174,7 +174,7 @@ InteropSubtitleAsset::equals(shared_ptr<const Asset> other_asset, EqualityOption } if (_movie_title != other->_movie_title) { - note (NoteType::ERROR, "Subtitle movie titles differ"); + note (NoteType::ERROR, "Subtitle or caption movie titles differ"); return false; } @@ -183,7 +183,7 @@ InteropSubtitleAsset::equals(shared_ptr<const Asset> other_asset, EqualityOption vector<shared_ptr<LoadFontNode>> -InteropSubtitleAsset::load_font_nodes () const +InteropTextAsset::load_font_nodes() const { vector<shared_ptr<LoadFontNode>> lf; copy (_load_font_nodes.begin(), _load_font_nodes.end(), back_inserter (lf)); @@ -192,7 +192,7 @@ InteropSubtitleAsset::load_font_nodes () const void -InteropSubtitleAsset::write (boost::filesystem::path p) const +InteropTextAsset::write(boost::filesystem::path p) const { File f(p, "wb"); if (!f) { @@ -206,9 +206,8 @@ InteropSubtitleAsset::write (boost::filesystem::path p) const _file = p; /* Image subtitles */ - for (auto i: _subtitles) { - auto im = dynamic_pointer_cast<dcp::SubtitleImage> (i); - if (im) { + for (auto i: _texts) { + if (auto im = dynamic_pointer_cast<dcp::TextImage>(i)) { im->write_png_file(p.parent_path() / String::compose("%1.png", im->id())); } } @@ -230,7 +229,7 @@ InteropSubtitleAsset::write (boost::filesystem::path p) const * a list of font ID, load ID and data. */ void -InteropSubtitleAsset::resolve_fonts (vector<shared_ptr<Asset>> assets) +InteropTextAsset::resolve_fonts(vector<shared_ptr<Asset>> assets) { for (auto asset: assets) { auto font = dynamic_pointer_cast<FontAsset>(asset); @@ -256,7 +255,7 @@ InteropSubtitleAsset::resolve_fonts (vector<shared_ptr<Asset>> assets) vector<shared_ptr<Asset>> -InteropSubtitleAsset::font_assets() +InteropTextAsset::font_assets() { vector<shared_ptr<Asset>> assets; for (auto const& i: _fonts) { @@ -268,7 +267,7 @@ InteropSubtitleAsset::font_assets() vector<shared_ptr<const Asset>> -InteropSubtitleAsset::font_assets() const +InteropTextAsset::font_assets() const { vector<shared_ptr<const Asset>> assets; for (auto const& i: _fonts) { @@ -280,12 +279,12 @@ InteropSubtitleAsset::font_assets() const void -InteropSubtitleAsset::add_to_assetmap (AssetMap& asset_map, boost::filesystem::path root) const +InteropTextAsset::add_to_assetmap(AssetMap& asset_map, boost::filesystem::path root) const { Asset::add_to_assetmap(asset_map, root); - for (auto i: _subtitles) { - auto im = dynamic_pointer_cast<dcp::SubtitleImage>(i); + for (auto i: _texts) { + auto im = dynamic_pointer_cast<dcp::TextImage>(i); if (im) { DCP_ASSERT(im->file()); add_file_to_assetmap(asset_map, root, im->file().get(), im->id()); @@ -295,12 +294,12 @@ InteropSubtitleAsset::add_to_assetmap (AssetMap& asset_map, boost::filesystem::p void -InteropSubtitleAsset::add_to_pkl (shared_ptr<PKL> pkl, boost::filesystem::path root) const +InteropTextAsset::add_to_pkl(shared_ptr<PKL> pkl, boost::filesystem::path root) const { Asset::add_to_pkl (pkl, root); - for (auto i: _subtitles) { - auto im = dynamic_pointer_cast<dcp::SubtitleImage> (i); + for (auto i: _texts) { + auto im = dynamic_pointer_cast<dcp::TextImage>(i); if (im) { auto png_image = im->png_image (); pkl->add_asset(im->id(), optional<string>(), make_digest(png_image), png_image.size(), "image/png", root.filename().string()); @@ -310,7 +309,7 @@ InteropSubtitleAsset::add_to_pkl (shared_ptr<PKL> pkl, boost::filesystem::path r void -InteropSubtitleAsset::set_font_file (string load_id, boost::filesystem::path file) +InteropTextAsset::set_font_file(string load_id, boost::filesystem::path file) { for (auto& i: _fonts) { if (i.load_id == load_id) { @@ -327,7 +326,7 @@ InteropSubtitleAsset::set_font_file (string load_id, boost::filesystem::path fil vector<string> -InteropSubtitleAsset::unresolved_fonts() const +InteropTextAsset::unresolved_fonts() const { vector<string> unresolved; for (auto load_font_node: _load_font_nodes) { diff --git a/src/interop_subtitle_asset.h b/src/interop_text_asset.h index f63740d5..363b1f27 100644 --- a/src/interop_subtitle_asset.h +++ b/src/interop_text_asset.h @@ -32,16 +32,16 @@ */ -/** @file src/interop_subtitle_asset.h - * @brief InteropSubtitleAsset class +/** @file src/interop_text_asset.h + * @brief InteropTextAsset class */ -#ifndef DCP_INTEROP_SUBTITLE_ASSET_H -#define DCP_INTEROP_SUBTITLE_ASSET_H +#ifndef LIBDCP_INTEROP_TEXT_ASSET_H +#define LIBDCP_INTEROP_TEXT_ASSET_H -#include "subtitle_asset.h" +#include "text_asset.h" #include "subtitle_standard.h" #include <boost/filesystem.hpp> @@ -52,16 +52,16 @@ namespace dcp { class InteropLoadFontNode; -/** @class InteropSubtitleAsset +/** @class InteropTextAsset * @brief A set of subtitles to be read and/or written in the Inter-Op format * * Inter-Op subtitles are sometimes known as CineCanvas. */ -class InteropSubtitleAsset : public SubtitleAsset +class InteropTextAsset : public TextAsset { public: - InteropSubtitleAsset (); - explicit InteropSubtitleAsset (boost::filesystem::path file); + InteropTextAsset(); + explicit InteropTextAsset(boost::filesystem::path file); bool equals ( std::shared_ptr<const Asset>, 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/mono_picture_asset_reader.h b/src/mono_j2k_picture_asset_reader.h index 5bead791..8376e939 100644 --- a/src/mono_picture_asset_reader.h +++ b/src/mono_j2k_picture_asset_reader.h @@ -32,23 +32,23 @@ */ -/** @file src/mono_picture_asset_reader.h - * @brief MonoPictureAssetReader typedef +/** @file src/mono_j2k_picture_asset_reader.h + * @brief MonoJ2KPictureAssetReader typedef */ -#ifndef LIBDCP_MONO_PICTURE_ASSET_READER_H -#define LIBDCP_MONO_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 "mono_picture_frame.h" +#include "mono_j2k_picture_frame.h" namespace dcp { -typedef AssetReader<ASDCP::JP2K::MXFReader, MonoPictureFrame> MonoPictureAssetReader; +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/reel_smpte_subtitle_asset.cc b/src/mono_mpeg2_picture_asset.h index 64440547..8ef3653e 100644 --- a/src/reel_smpte_subtitle_asset.cc +++ b/src/mono_mpeg2_picture_asset.h @@ -1,5 +1,5 @@ /* - Copyright (C) 2021 Carl Hetherington <cth@carlh.net> + Copyright (C) 2023 Carl Hetherington <cth@carlh.net> This file is part of libdcp. @@ -32,35 +32,42 @@ */ -/** @file src/reel_interop_subtitle_asset.cc - * @brief ReelInteropSubtitleAsset class +#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 "reel_smpte_subtitle_asset.h" -#include "smpte_subtitle_asset.h" -#include "warnings.h" -LIBDCP_DISABLE_WARNINGS -#include <libxml++/libxml++.h> -LIBDCP_ENABLE_WARNINGS +#include "behaviour.h" +#include "mpeg2_picture_asset.h" +#include "mono_mpeg2_picture_asset_reader.h" + +namespace dcp { -using std::shared_ptr; -using std::string; -using boost::optional; -using namespace dcp; +class MonoMPEG2PictureAssetWriter; -ReelSMPTESubtitleAsset::ReelSMPTESubtitleAsset (shared_ptr<SMPTESubtitleAsset> asset, Fraction edit_rate, int64_t intrinsic_duration, int64_t entry_point) - : ReelSubtitleAsset (asset, edit_rate, intrinsic_duration, entry_point) + +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; +}; -} -ReelSMPTESubtitleAsset::ReelSMPTESubtitleAsset (shared_ptr<const cxml::Node> node) - : ReelSubtitleAsset (node) -{ - node->done (); } + +#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/reel_interop_subtitle_asset.cc b/src/mpeg2_picture_asset_writer.cc index 5f295d53..f5ead775 100644 --- a/src/reel_interop_subtitle_asset.cc +++ b/src/mpeg2_picture_asset_writer.cc @@ -1,5 +1,5 @@ /* - Copyright (C) 2021 Carl Hetherington <cth@carlh.net> + Copyright (C) 2024 Carl Hetherington <cth@carlh.net> This file is part of libdcp. @@ -32,34 +32,17 @@ */ -/** @file src/reel_interop_subtitle_asset.cc - * @brief ReelInteropSubtitleAsset class - */ +#include "mpeg2_picture_asset.h" +#include "mpeg2_picture_asset_writer.h" -#include "reel_interop_subtitle_asset.h" -#include "warnings.h" -LIBDCP_DISABLE_WARNINGS -#include <libxml++/libxml++.h> -LIBDCP_ENABLE_WARNINGS - - -using std::shared_ptr; -using std::string; -using boost::optional; using namespace dcp; -ReelInteropSubtitleAsset::ReelInteropSubtitleAsset (std::shared_ptr<SubtitleAsset> asset, Fraction edit_rate, int64_t intrinsic_duration, int64_t entry_point) - : ReelSubtitleAsset (asset, edit_rate, intrinsic_duration, entry_point) -{ - -} - - -ReelInteropSubtitleAsset::ReelInteropSubtitleAsset (std::shared_ptr<const cxml::Node> node) - : ReelSubtitleAsset (node) +MPEG2PictureAssetWriter::MPEG2PictureAssetWriter(MPEG2PictureAsset* asset, boost::filesystem::path file, bool overwrite) + : AssetWriter(asset, file) + , _picture_asset(asset) + , _overwrite(overwrite) { - node->done (); + 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/reel_closed_caption_asset.h b/src/mpeg2_transcode.h index 405de34b..1f13cfc3 100644 --- a/src/reel_closed_caption_asset.h +++ b/src/mpeg2_transcode.h @@ -1,5 +1,5 @@ /* - Copyright (C) 2012-2021 Carl Hetherington <cth@carlh.net> + Copyright (C) 2023 Carl Hetherington <cth@carlh.net> This file is part of libdcp. @@ -32,62 +32,73 @@ */ -/** @file src/reel_closed_caption_asset.h - * @brief ReelClosedCaptionAsset class - */ +#ifndef LIBDCP_MPEG2_TRANSCODE_H +#define LIBDCP_MPEG2_TRANSCODE_H -#ifndef LIBDCP_REEL_CLOSED_CAPTION_ASSET_H -#define LIBDCP_REEL_CLOSED_CAPTION_ASSET_H +#include "ffmpeg_image.h" +#include <memory> -#include "language_tag.h" -#include "reel_asset.h" -#include "reel_file_asset.h" -#include "subtitle_asset.h" +struct AVCodec; +struct AVCodecContext; +struct AVFrame; +struct AVPacket; -struct verify_invalid_language2; +namespace dcp { -namespace dcp { +class MonoMPEG2PictureFrame; -/** @class ReelClosedCaptionAsset - * @brief Part of a Reel's description which refers to a closed caption XML/MXF file - */ -class ReelClosedCaptionAsset : public ReelFileAsset +class MPEG2Codec { public: - ReelClosedCaptionAsset (std::shared_ptr<SubtitleAsset> asset, Fraction edit_rate, int64_t intrinsic_duration, int64_t entry_point); - explicit ReelClosedCaptionAsset (std::shared_ptr<const cxml::Node>); + MPEG2Codec() = default; + virtual ~MPEG2Codec(); - std::shared_ptr<const SubtitleAsset> asset () const { - return asset_of_type<const SubtitleAsset>(); - } + MPEG2Codec(MPEG2Codec const&) = delete; + MPEG2Codec& operator=(MPEG2Codec const&) = delete; - std::shared_ptr<SubtitleAsset> asset () { - return asset_of_type<SubtitleAsset>(); - } +protected: + AVCodec const* _codec; + AVCodecContext* _context; +}; - bool equals(std::shared_ptr<const ReelClosedCaptionAsset>, EqualityOptions const&, NoteHandler) const; - void set_language (dcp::LanguageTag l) { - _language = l.to_string(); - } +class MPEG2Decompressor : public MPEG2Codec +{ +public: + MPEG2Decompressor(); + ~MPEG2Decompressor(); - void unset_language () { - _language = boost::optional<std::string> (); - } + std::vector<FFmpegImage> decompress_frame(std::shared_ptr<const MonoMPEG2PictureFrame> frame); + std::vector<FFmpegImage> flush(); - boost::optional<std::string> language () const { - return _language; - } +private: + std::vector<FFmpegImage> decompress_packet(AVPacket* packet); -protected: - friend struct ::verify_invalid_language2; + 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(); - boost::optional<std::string> _language; +private: + boost::optional<IndexedFrame> send_and_receive(AVFrame const* frame); }; @@ -64,7 +64,8 @@ namespace dcp class MXFMetadata; -class PictureAssetWriter; +class J2KPictureAssetWriter; +class MPEG2PictureAssetWriter; /** @class MXF @@ -136,7 +137,9 @@ public: protected: template <class P, class Q> - friend void start (PictureAssetWriter* writer, std::shared_ptr<P> state, Q* mxf, uint8_t const * data, int size); + friend void start (J2KPictureAssetWriter* writer, std::shared_ptr<P> state, Q* mxf, uint8_t const * data, int size); + template <class P, class Q> + friend void start (MPEG2PictureAssetWriter* writer, std::shared_ptr<P> state, Q* mxf, uint8_t const * data, int size); MXF (); diff --git a/src/name_format.h b/src/name_format.h index 6401fe82..3eb0277b 100644 --- a/src/name_format.h +++ b/src/name_format.h @@ -37,8 +37,8 @@ */ -#ifndef LIBDCP_NAME_FORMAT -#define LIBDCP_NAME_FORMAT +#ifndef LIBDCP_NAME_FORMAT_H +#define LIBDCP_NAME_FORMAT_H #include <string> diff --git a/src/picture_asset.cc b/src/picture_asset.cc index c9f669a6..3b28bf4b 100644 --- a/src/picture_asset.cc +++ b/src/picture_asset.cc @@ -32,199 +32,24 @@ */ -/** @file src/picture_asset.cc - * @brief PictureAsset class - */ - - -#include "compose.hpp" -#include "dcp_assert.h" -#include "equality_options.h" -#include "exceptions.h" -#include "j2k_transcode.h" -#include "openjpeg_image.h" #include "picture_asset.h" -#include "picture_asset_writer.h" -#include "util.h" -#include <asdcp/AS_DCP.h> -#include <asdcp/KM_fileio.h> -#include <libxml++/nodes/element.h> -#include <boost/filesystem.hpp> -#include <list> -#include <stdexcept> - - -using std::string; -using std::list; -using std::vector; -using std::max; -using std::pair; -using std::make_pair; -using std::shared_ptr; -using namespace dcp; - - -PictureAsset::PictureAsset (boost::filesystem::path file) - : Asset (file) -{ - -} - -PictureAsset::PictureAsset (Fraction edit_rate, Standard standard) - : MXF (standard) - , _edit_rate (edit_rate) -{ -} +using namespace dcp; -void -PictureAsset::read_picture_descriptor (ASDCP::JP2K::PictureDescriptor const & desc) +PictureAsset::PictureAsset(boost::filesystem::path file) + : Asset(file) { - _size.width = desc.StoredWidth; - _size.height = desc.StoredHeight; - _edit_rate = Fraction (desc.EditRate.Numerator, desc.EditRate.Denominator); - _intrinsic_duration = desc.ContainerDuration; - _frame_rate = Fraction (desc.SampleRate.Numerator, desc.SampleRate.Denominator); - _screen_aspect_ratio = Fraction (desc.AspectRatio.Numerator, desc.AspectRatio.Denominator); -} - -bool -PictureAsset::descriptor_equals ( - ASDCP::JP2K::PictureDescriptor const & a, ASDCP::JP2K::PictureDescriptor const & b, NoteHandler note - ) const -{ - if ( - a.EditRate != b.EditRate || - a.SampleRate != b.SampleRate || - a.StoredWidth != b.StoredWidth || - a.StoredHeight != b.StoredHeight || - a.AspectRatio != b.AspectRatio || - a.Rsize != b.Rsize || - a.Xsize != b.Xsize || - a.Ysize != b.Ysize || - a.XOsize != b.XOsize || - a.YOsize != b.YOsize || - a.XTsize != b.XTsize || - a.YTsize != b.YTsize || - a.XTOsize != b.XTOsize || - a.YTOsize != b.YTOsize || - a.Csize != b.Csize -// a.CodingStyleDefault != b.CodingStyleDefault || -// a.QuantizationDefault != b.QuantizationDefault - ) { - - note (NoteType::ERROR, "video MXF picture descriptors differ"); - return false; - } - - if (a.ContainerDuration != b.ContainerDuration) { - note (NoteType::ERROR, "video container durations differ"); - } - -// for (unsigned int j = 0; j < ASDCP::JP2K::MaxComponents; ++j) { -// if (a.ImageComponents[j] != b.ImageComponents[j]) { -// notes.pack_start ("video MXF picture descriptors differ"); -// } -// } - - return true; } -bool -PictureAsset::frame_buffer_equals ( - int frame, EqualityOptions const& opt, NoteHandler note, - uint8_t const * data_A, unsigned int size_A, uint8_t const * data_B, unsigned int size_B - ) const +PictureAsset::PictureAsset(Fraction edit_rate, Standard standard) + : MXF(standard) + , _edit_rate(edit_rate) { - if (size_A == size_B && memcmp (data_A, data_B, size_A) == 0) { - note (NoteType::NOTE, "J2K identical"); - /* Easy result; the J2K data is identical */ - return true; - } - - /* Decompress the images to bitmaps */ - auto image_A = decompress_j2k (const_cast<uint8_t*>(data_A), size_A, 0); - auto image_B = decompress_j2k (const_cast<uint8_t*>(data_B), size_B, 0); - - /* Compare them */ - - vector<int> abs_diffs (image_A->size().width * image_A->size().height * 3); - int d = 0; - int max_diff = 0; - for (int c = 0; c < 3; ++c) { - - if (image_A->size() != image_B->size()) { - note (NoteType::ERROR, String::compose ("image sizes for frame %1 differ", frame)); - return false; - } - - int const pixels = image_A->size().width * image_A->size().height; - for (int j = 0; j < pixels; ++j) { - int const t = abs (image_A->data(c)[j] - image_B->data(c)[j]); - abs_diffs[d++] = t; - max_diff = max (max_diff, t); - } - } - - uint64_t total = 0; - for (vector<int>::iterator j = abs_diffs.begin(); j != abs_diffs.end(); ++j) { - total += *j; - } - - double const mean = double (total) / abs_diffs.size (); - - uint64_t total_squared_deviation = 0; - for (auto j: abs_diffs) { - total_squared_deviation += pow (j - mean, 2); - } - - auto const std_dev = sqrt (double (total_squared_deviation) / abs_diffs.size()); - - note (NoteType::NOTE, String::compose("mean difference %1 deviation %2", mean, std_dev)); - - if (mean > opt.max_mean_pixel_error) { - note ( - NoteType::ERROR, - String::compose ("mean %1 out of range %2 in frame %3", mean, opt.max_mean_pixel_error, frame) - ); - - return false; - } - - if (std_dev > opt.max_std_dev_pixel_error) { - note ( - NoteType::ERROR, - String::compose ("standard deviation %1 out of range %2 in frame %3", std_dev, opt.max_std_dev_pixel_error, frame) - ); - - return false; - } - - return true; } -string -PictureAsset::static_pkl_type (Standard standard) -{ - switch (standard) { - case Standard::INTEROP: - return "application/x-smpte-mxf;asdcpKind=Picture"; - case Standard::SMPTE: - return "application/mxf"; - default: - DCP_ASSERT (false); - } -} - - -string -PictureAsset::pkl_type (Standard standard) const -{ - return static_pkl_type (standard); -} diff --git a/src/picture_asset.h b/src/picture_asset.h index 9ad1eb22..36364485 100644 --- a/src/picture_asset.h +++ b/src/picture_asset.h @@ -32,56 +32,31 @@ */ -/** @file src/picture_asset.h - * @brief PictureAsset class - */ - - #ifndef LIBDCP_PICTURE_ASSET_H #define LIBDCP_PICTURE_ASSET_H +#include "asset.h" #include "mxf.h" -#include "util.h" -#include "metadata.h" - - -namespace ASDCP { - namespace JP2K { - struct PictureDescriptor; - } -} +#include "types.h" namespace dcp { -class MonoPictureFrame; -class StereoPictureFrame; -class PictureAssetWriter; - - -/** @class PictureAsset - * @brief An asset made up of JPEG2000 data - */ class PictureAsset : public Asset, public MXF { public: - /** Load a PictureAsset from a file */ - explicit PictureAsset (boost::filesystem::path file); - - /** Create a new PictureAsset with a given edit rate and standard */ + explicit PictureAsset(boost::filesystem::path file); PictureAsset(Fraction edit_rate, Standard standard); - enum class Behaviour { - OVERWRITE_EXISTING, - MAKE_NEW - }; + Fraction edit_rate () const { + return _edit_rate; + } - virtual std::shared_ptr<PictureAssetWriter> start_write ( - boost::filesystem::path file, - Behaviour behaviour - ) = 0; + int64_t intrinsic_duration () const { + return _intrinsic_duration; + } Size size () const { return _size; @@ -107,33 +82,7 @@ public: _screen_aspect_ratio = r; } - Fraction edit_rate () const { - return _edit_rate; - } - - int64_t intrinsic_duration () const { - return _intrinsic_duration; - } - - static std::string static_pkl_type (Standard standard); - protected: - friend class MonoPictureAssetWriter; - friend class StereoPictureAssetWriter; - - bool frame_buffer_equals ( - int frame, EqualityOptions const& opt, NoteHandler note, - uint8_t const * data_A, unsigned int size_A, uint8_t const * data_B, unsigned int size_B - ) const; - - bool descriptor_equals ( - ASDCP::JP2K::PictureDescriptor const & a, - ASDCP::JP2K::PictureDescriptor const & b, - NoteHandler note - ) const; - - void read_picture_descriptor (ASDCP::JP2K::PictureDescriptor const &); - Fraction _edit_rate; /** The total length of this content in video frames. The amount of * content presented may be less than this. @@ -144,8 +93,6 @@ protected: Fraction _frame_rate; Fraction _screen_aspect_ratio; -private: - std::string pkl_type (Standard standard) const override; }; @@ -153,3 +100,4 @@ private: #endif + @@ -105,26 +105,26 @@ PKL::write_xml (boost::filesystem::path file, shared_ptr<const CertificateChain> pkl = doc.create_root_node("PackingList", pkl_smpte_ns); } - pkl->add_child("Id")->add_child_text ("urn:uuid:" + _id); + cxml::add_text_child(pkl, "Id", "urn:uuid:" + _id); if (_annotation_text) { - pkl->add_child("AnnotationText")->add_child_text (*_annotation_text); + cxml::add_text_child(pkl, "AnnotationText", *_annotation_text); } - pkl->add_child("IssueDate")->add_child_text (_issue_date); - pkl->add_child("Issuer")->add_child_text (_issuer); - pkl->add_child("Creator")->add_child_text (_creator); + cxml::add_text_child(pkl, "IssueDate", _issue_date); + cxml::add_text_child(pkl, "Issuer", _issuer); + cxml::add_text_child(pkl, "Creator", _creator); - auto asset_list = pkl->add_child("AssetList"); + auto asset_list = cxml::add_child(pkl, "AssetList"); for (auto i: _assets) { - auto asset = asset_list->add_child("Asset"); - asset->add_child("Id")->add_child_text ("urn:uuid:" + i->id()); + auto asset = cxml::add_child(asset_list, "Asset"); + cxml::add_text_child(asset, "Id", "urn:uuid:" + i->id()); if (i->annotation_text()) { - asset->add_child("AnnotationText")->add_child_text (*i->annotation_text()); + cxml::add_text_child(asset, "AnnotationText", *i->annotation_text()); } - asset->add_child("Hash")->add_child_text (i->hash()); - asset->add_child("Size")->add_child_text (raw_convert<string>(i->size())); - asset->add_child("Type")->add_child_text (i->type()); + cxml::add_text_child(asset, "Hash", i->hash()); + cxml::add_text_child(asset, "Size", raw_convert<string>(i->size())); + cxml::add_text_child(asset, "Type", i->type()); if (auto filename = i->original_filename()) { - asset->add_child("OriginalFileName")->add_child_text(*filename); + cxml::add_text_child(asset, "OriginalFileName", *filename); } } @@ -32,7 +32,7 @@ */ -/** @file src/pkl.cc +/** @file src/pkl.h * @brief PKL class */ diff --git a/src/rating.cc b/src/rating.cc index 21960365..156d5ad0 100644 --- a/src/rating.cc +++ b/src/rating.cc @@ -61,8 +61,8 @@ Rating::Rating (cxml::ConstNodePtr node) void Rating::as_xml (xmlpp::Element* parent) const { - parent->add_child("Agency")->add_child_text(agency); - parent->add_child("Label")->add_child_text(label); + cxml::add_text_child(parent, "Agency", agency); + cxml::add_text_child(parent, "Label", label); } diff --git a/src/reel.cc b/src/reel.cc index a8481d59..ce654f6f 100644 --- a/src/reel.cc +++ b/src/reel.cc @@ -40,25 +40,22 @@ #include "decrypted_kdm.h" #include "decrypted_kdm_key.h" #include "equality_options.h" -#include "interop_subtitle_asset.h" -#include "mono_picture_asset.h" -#include "picture_asset.h" +#include "interop_text_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" -#include "reel_interop_closed_caption_asset.h" -#include "reel_interop_subtitle_asset.h" +#include "reel_interop_text_asset.h" #include "reel_markers_asset.h" #include "reel_mono_picture_asset.h" -#include "reel_smpte_closed_caption_asset.h" -#include "reel_smpte_subtitle_asset.h" +#include "reel_smpte_text_asset.h" #include "reel_sound_asset.h" #include "reel_stereo_picture_asset.h" -#include "reel_subtitle_asset.h" -#include "smpte_subtitle_asset.h" +#include "reel_text_asset.h" +#include "smpte_text_asset.h" #include "sound_asset.h" -#include "stereo_picture_asset.h" -#include "subtitle_asset.h" +#include "stereo_j2k_picture_asset.h" +#include "text_asset.h" #include "util.h" #include <libxml++/nodes/element.h> #include <stdint.h> @@ -79,38 +76,56 @@ Reel::Reel (std::shared_ptr<const cxml::Node> node, dcp::Standard standard) { auto asset_list = node->node_child ("AssetList"); - auto main_picture = asset_list->optional_node_child ("MainPicture"); - if (main_picture) { + if (auto main_picture = asset_list->optional_node_child("MainPicture")) { _main_picture = make_shared<ReelMonoPictureAsset>(main_picture); } - auto main_stereoscopic_picture = asset_list->optional_node_child ("MainStereoscopicPicture"); - if (main_stereoscopic_picture) { + if (auto main_stereoscopic_picture = asset_list->optional_node_child("MainStereoscopicPicture")) { _main_picture = make_shared<ReelStereoPictureAsset>(main_stereoscopic_picture); } - auto main_sound = asset_list->optional_node_child ("MainSound"); - if (main_sound) { + if (auto main_sound = asset_list->optional_node_child("MainSound")) { _main_sound = make_shared<ReelSoundAsset>(main_sound); } - auto main_subtitle = asset_list->optional_node_child ("MainSubtitle"); - if (main_subtitle) { + if (auto main_subtitle = asset_list->optional_node_child("MainSubtitle")) { switch (standard) { - case Standard::INTEROP: - _main_subtitle = make_shared<ReelInteropSubtitleAsset>(main_subtitle); - break; - case Standard::SMPTE: - _main_subtitle = make_shared<ReelSMPTESubtitleAsset>(main_subtitle); - break; + case Standard::INTEROP: + _main_subtitle = make_shared<ReelInteropTextAsset>(main_subtitle); + break; + case Standard::SMPTE: + _main_subtitle = make_shared<ReelSMPTETextAsset>(main_subtitle); + break; } } - auto main_markers = asset_list->optional_node_child ("MainMarkers"); - if (main_markers) { + if (auto main_caption = asset_list->optional_node_child("MainCaption")) { + switch (standard) { + case Standard::INTEROP: + DCP_ASSERT(false); + break; + case Standard::SMPTE: + _main_caption = make_shared<ReelSMPTETextAsset>(main_caption); + break; + } + } + + if (auto main_markers = asset_list->optional_node_child("MainMarkers")) { _main_markers = make_shared<ReelMarkersAsset>(main_markers); } + auto closed_subtitles = asset_list->node_children("ClosedSubtitle"); + for (auto i: closed_subtitles) { + switch (standard) { + case Standard::INTEROP: + DCP_ASSERT(false); + break; + case Standard::SMPTE: + _closed_subtitles.push_back(make_shared<ReelSMPTETextAsset>(i)); + break; + } + } + /* XXX: it's not ideal that we silently tolerate Interop or SMPTE nodes here */ /* XXX: not sure if Interop supports multiple closed captions */ auto closed_captions = asset_list->node_children ("MainClosedCaption"); @@ -119,17 +134,16 @@ Reel::Reel (std::shared_ptr<const cxml::Node> node, dcp::Standard standard) } for (auto i: closed_captions) { switch (standard) { - case Standard::INTEROP: - _closed_captions.push_back (make_shared<ReelInteropClosedCaptionAsset>(i)); - break; - case Standard::SMPTE: - _closed_captions.push_back (make_shared<ReelSMPTEClosedCaptionAsset>(i)); - break; + case Standard::INTEROP: + _closed_captions.push_back(make_shared<ReelInteropTextAsset>(i)); + break; + case Standard::SMPTE: + _closed_captions.push_back(make_shared<ReelSMPTETextAsset>(i)); + break; } } - auto atmos = asset_list->optional_node_child ("AuxData"); - if (atmos) { + if (auto atmos = asset_list->optional_node_child("AuxData")) { _atmos = make_shared<ReelAtmosAsset>(atmos); } @@ -141,9 +155,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); @@ -162,6 +176,14 @@ Reel::write_to_cpl (xmlpp::Element* node, Standard standard) const _main_subtitle->write_to_cpl (asset_list, standard); } + if (_main_caption) { + _main_caption->write_to_cpl(asset_list, standard); + } + + for (auto i: _closed_subtitles) { + i->write_to_cpl(asset_list, standard); + } + for (auto i: _closed_captions) { i->write_to_cpl (asset_list, standard); } @@ -208,8 +230,8 @@ Reel::equals(std::shared_ptr<const Reel> other, EqualityOptions const& opt, Note bool same_type = false; { - auto interop = dynamic_pointer_cast<ReelInteropSubtitleAsset>(_main_subtitle); - auto interop_other = dynamic_pointer_cast<ReelInteropSubtitleAsset>(other->_main_subtitle); + auto interop = dynamic_pointer_cast<ReelInteropTextAsset>(_main_subtitle); + auto interop_other = dynamic_pointer_cast<ReelInteropTextAsset>(other->_main_subtitle); if (interop && interop_other) { same_type = true; if (!interop->equals(interop_other, opt, note)) { @@ -219,8 +241,8 @@ Reel::equals(std::shared_ptr<const Reel> other, EqualityOptions const& opt, Note } { - auto smpte = dynamic_pointer_cast<ReelSMPTESubtitleAsset>(_main_subtitle); - auto smpte_other = dynamic_pointer_cast<ReelSMPTESubtitleAsset>(other->_main_subtitle); + auto smpte = dynamic_pointer_cast<ReelSMPTETextAsset>(_main_subtitle); + auto smpte_other = dynamic_pointer_cast<ReelSMPTETextAsset>(other->_main_subtitle); if (smpte && smpte_other) { same_type = true; if (!smpte->equals(smpte_other, opt, note)) { @@ -327,19 +349,19 @@ 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()); } if (_main_subtitle) { - auto smpte = dynamic_pointer_cast<ReelSMPTESubtitleAsset>(_main_subtitle); + auto smpte = dynamic_pointer_cast<ReelSMPTETextAsset>(_main_subtitle); if (smpte && i.id() == smpte->key_id() && smpte->asset_ref().resolved()) { smpte->smpte_asset()->set_key(i.key()); } } for (auto j: _closed_captions) { - auto smpte = dynamic_pointer_cast<ReelSMPTESubtitleAsset>(j); + auto smpte = dynamic_pointer_cast<ReelSMPTETextAsset>(j); if (smpte && i.id() == smpte->key_id() && smpte->asset_ref().resolved()) { smpte->smpte_asset()->set_key(i.key()); } @@ -358,12 +380,23 @@ Reel::add (shared_ptr<ReelAsset> asset) _main_picture = p; } else if (auto so = dynamic_pointer_cast<ReelSoundAsset>(asset)) { _main_sound = so; - } else if (auto su = dynamic_pointer_cast<ReelSubtitleAsset>(asset)) { - _main_subtitle = su; + } else if (auto te = dynamic_pointer_cast<ReelTextAsset>(asset)) { + switch (te->type()) { + case TextType::OPEN_SUBTITLE: + _main_subtitle = te; + break; + case TextType::OPEN_CAPTION: + _main_caption = te; + break; + case TextType::CLOSED_SUBTITLE: + _closed_subtitles.push_back(te); + break; + case TextType::CLOSED_CAPTION: + _closed_captions.push_back(te); + break; + } } else if (auto m = dynamic_pointer_cast<ReelMarkersAsset>(asset)) { _main_markers = m; - } else if (auto c = dynamic_pointer_cast<ReelClosedCaptionAsset>(asset)) { - _closed_captions.push_back (c); } else if (auto a = dynamic_pointer_cast<ReelAtmosAsset>(asset)) { _atmos = a; } else { @@ -385,6 +418,10 @@ Reel::assets () const if (_main_subtitle) { a.push_back (_main_subtitle); } + if (_main_caption) { + a.push_back(_main_caption); + } + std::copy (_closed_subtitles.begin(), _closed_subtitles.end(), back_inserter(a)); std::copy (_closed_captions.begin(), _closed_captions.end(), back_inserter(a)); if (_atmos) { a.push_back (_atmos); @@ -404,28 +441,33 @@ Reel::resolve_refs (vector<shared_ptr<Asset>> assets) _main_sound->asset_ref().resolve(assets); } - if (_main_subtitle) { - _main_subtitle->asset_ref().resolve(assets); - + auto resolve_interop_fonts = [&assets](shared_ptr<ReelTextAsset>(asset)) { /* Interop subtitle handling is all special cases */ - if (_main_subtitle->asset_ref().resolved()) { - auto iop = dynamic_pointer_cast<InteropSubtitleAsset> (_main_subtitle->asset_ref().asset()); - if (iop) { - iop->resolve_fonts (assets); + if (asset->asset_ref().resolved()) { + if (auto iop = dynamic_pointer_cast<InteropTextAsset>(asset->asset_ref().asset())) { + iop->resolve_fonts(assets); } } + + }; + + if (_main_subtitle) { + _main_subtitle->asset_ref().resolve(assets); + resolve_interop_fonts(_main_subtitle); } - for (auto i: _closed_captions) { + if (_main_caption) { + _main_caption->asset_ref().resolve(assets); + } + + for (auto i: _closed_subtitles) { i->asset_ref().resolve(assets); + resolve_interop_fonts(i); + } - /* Interop subtitle handling is all special cases */ - if (i->asset_ref().resolved()) { - auto iop = dynamic_pointer_cast<InteropSubtitleAsset> (i->asset_ref().asset()); - if (iop) { - iop->resolve_fonts (assets); - } - } + for (auto i: _closed_captions) { + i->asset_ref().resolve(assets); + resolve_interop_fonts(i); } if (_atmos) { @@ -453,9 +495,15 @@ Reel::duration () const if (_main_subtitle) { d = min (d, _main_subtitle->actual_duration()); } + if (_main_caption) { + d = min(d, _main_caption->actual_duration()); + } if (_main_markers) { d = min (d, _main_markers->actual_duration()); } + for (auto i: _closed_subtitles) { + d = min(d, i->actual_duration()); + } for (auto i: _closed_captions) { d = min (d, i->actual_duration()); } @@ -32,7 +32,7 @@ */ -/** @file src/reel.cc +/** @file src/reel.h * @brief Reel class */ @@ -68,7 +68,7 @@ class DecryptedKDM; class ReelAsset; class ReelPictureAsset; class ReelSoundAsset; -class ReelSubtitleAsset; +class ReelTextAsset; class ReelMarkersAsset; class ReelClosedCaptionAsset; class ReelAtmosAsset; @@ -84,7 +84,7 @@ public: Reel ( std::shared_ptr<ReelPictureAsset> picture, std::shared_ptr<ReelSoundAsset> sound = std::shared_ptr<ReelSoundAsset> (), - std::shared_ptr<ReelSubtitleAsset> subtitle = std::shared_ptr<ReelSubtitleAsset> (), + std::shared_ptr<ReelTextAsset> subtitle = std::shared_ptr<ReelTextAsset> (), std::shared_ptr<ReelMarkersAsset> markers = std::shared_ptr<ReelMarkersAsset> (), std::shared_ptr<ReelAtmosAsset> atmos = std::shared_ptr<ReelAtmosAsset> () ) @@ -105,15 +105,23 @@ public: return _main_sound; } - std::shared_ptr<ReelSubtitleAsset> main_subtitle () const { + std::shared_ptr<ReelTextAsset> main_subtitle () const { return _main_subtitle; } + std::shared_ptr<ReelTextAsset> main_caption() const { + return _main_caption; + } + std::shared_ptr<ReelMarkersAsset> main_markers () const { return _main_markers; } - std::vector<std::shared_ptr<ReelClosedCaptionAsset>> closed_captions () const { + std::vector<std::shared_ptr<ReelTextAsset>> closed_subtitles() const { + return _closed_subtitles; + } + + std::vector<std::shared_ptr<ReelTextAsset>> closed_captions () const { return _closed_captions; } @@ -145,9 +153,11 @@ private: std::shared_ptr<ReelPictureAsset> _main_picture; std::shared_ptr<ReelSoundAsset> _main_sound; - std::shared_ptr<ReelSubtitleAsset> _main_subtitle; + std::shared_ptr<ReelTextAsset> _main_subtitle; + std::shared_ptr<ReelTextAsset> _main_caption; std::shared_ptr<ReelMarkersAsset> _main_markers; - std::vector<std::shared_ptr<ReelClosedCaptionAsset>> _closed_captions; + std::vector<std::shared_ptr<ReelTextAsset>> _closed_subtitles; + std::vector<std::shared_ptr<ReelTextAsset>> _closed_captions; std::shared_ptr<ReelAtmosAsset> _atmos; std::vector<dcp::DecryptedKDM> _kdms; diff --git a/src/reel_asset.cc b/src/reel_asset.cc index 3a3ae731..1d3d1d1f 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()); 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; } @@ -138,7 +138,7 @@ optional_to_string (optional<T> o) bool ReelAsset::asset_equals(shared_ptr<const ReelAsset> other, EqualityOptions const& opt, NoteHandler note) const { - auto const node = cpl_node_name(Standard::SMPTE); + auto const node = cpl_node_name(); if (_annotation_text != other->_annotation_text) { string const s = String::compose("Reel %1: annotation texts differ (%2 vs %3)", node, optional_to_string(_annotation_text), optional_to_string(other->_annotation_text)); diff --git a/src/reel_asset.h b/src/reel_asset.h index e928cb18..99db60b4 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; @@ -139,7 +139,7 @@ protected: /** @return the node name that this asset uses in the CPL's <Reel> node * e.g. MainPicture, MainSound etc. */ - virtual std::string cpl_node_name (Standard) const = 0; + virtual std::string cpl_node_name() const = 0; /** @return Any attribute that should be used on the asset's node in the CPL */ virtual std::pair<std::string, std::string> cpl_node_attribute (Standard) const; diff --git a/src/reel_atmos_asset.cc b/src/reel_atmos_asset.cc index ef39a4eb..fcecb548 100644 --- a/src/reel_atmos_asset.cc +++ b/src/reel_atmos_asset.cc @@ -69,7 +69,7 @@ ReelAtmosAsset::ReelAtmosAsset (std::shared_ptr<const cxml::Node> node) string -ReelAtmosAsset::cpl_node_name (Standard) const +ReelAtmosAsset::cpl_node_name() const { return "axd:AuxData"; } @@ -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..ab18d1ab 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: @@ -76,7 +76,7 @@ private: return std::string("MDEK"); } - std::string cpl_node_name (Standard standard) const override; + std::string cpl_node_name() const override; std::pair<std::string, std::string> cpl_node_namespace () const override; }; 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.h b/src/reel_interop_closed_caption_asset.h deleted file mode 100644 index 5e8f7c1e..00000000 --- a/src/reel_interop_closed_caption_asset.h +++ /dev/null @@ -1,77 +0,0 @@ -/* - Copyright (C) 2021 Carl Hetherington <cth@carlh.net> - - This file is part of libdcp. - - libdcp is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - libdcp is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with libdcp. If not, see <http://www.gnu.org/licenses/>. - - In addition, as a special exception, the copyright holders give - permission to link the code of portions of this program with the - OpenSSL library under certain conditions as described in each - individual source file, and distribute linked combinations - including the two. - - You must obey the GNU General Public License in all respects - for all of the code used other than OpenSSL. If you modify - file(s) with this exception, you may extend this exception to your - version of the file(s), but you are not obligated to do so. If you - do not wish to do so, delete this exception statement from your - version. If you delete this exception statement from all source - files in the program, then also delete it here. -*/ - - -/** @file src/reel_interop_closed_caption_asset.h - * @brief ReelInteropClosedCaptionAsset class - */ - - -#ifndef LIBDCP_REEL_INTEROP_CLOSED_CAPTION_ASSET_H -#define LIBDCP_REEL_INTEROP_CLOSED_CAPTION_ASSET_H - - -#include "interop_subtitle_asset.h" -#include "reel_closed_caption_asset.h" - - -namespace dcp { - - -class ReelInteropClosedCaptionAsset : public ReelClosedCaptionAsset -{ -public: - ReelInteropClosedCaptionAsset (std::shared_ptr<InteropSubtitleAsset> asset, Fraction edit_rate, int64_t intrinsic_duration, int64_t entry_point); - explicit ReelInteropClosedCaptionAsset (std::shared_ptr<const cxml::Node>); - - std::shared_ptr<const InteropSubtitleAsset> interop_asset () const { - return asset_of_type<const InteropSubtitleAsset>(); - } - - std::shared_ptr<InteropSubtitleAsset> interop_asset () { - return asset_of_type<InteropSubtitleAsset>(); - } - - xmlpp::Node* write_to_cpl (xmlpp::Node* node, Standard standard) const override; - -private: - std::string cpl_node_name (Standard) const override; - std::pair<std::string, std::string> cpl_node_namespace () const override; -}; - - -}; - - -#endif - diff --git a/src/reel_smpte_closed_caption_asset.cc b/src/reel_interop_text_asset.cc index a2a68202..0f99b614 100644 --- a/src/reel_smpte_closed_caption_asset.cc +++ b/src/reel_interop_text_asset.cc @@ -32,12 +32,13 @@ */ -/** @file src/reel_smpte_closed_caption_asset.cc - * @brief ReelSMPTEClosedCaptionAsset class +/** @file src/reel_interop_text_asset.cc + * @brief ReelInteropTextAsset class */ -#include "reel_smpte_closed_caption_asset.h" +#include "dcp_assert.h" +#include "reel_interop_text_asset.h" #include "warnings.h" LIBDCP_DISABLE_WARNINGS #include <libxml++/libxml++.h> @@ -46,46 +47,69 @@ LIBDCP_ENABLE_WARNINGS using std::make_pair; using std::pair; -using std::shared_ptr; using std::string; +using boost::optional; using namespace dcp; -ReelSMPTEClosedCaptionAsset::ReelSMPTEClosedCaptionAsset (shared_ptr<SMPTESubtitleAsset> asset, Fraction edit_rate, int64_t intrinsic_duration, int64_t entry_point) - : ReelClosedCaptionAsset (asset, edit_rate, intrinsic_duration, entry_point) +ReelInteropTextAsset::ReelInteropTextAsset(TextType type, std::shared_ptr<TextAsset> asset, Fraction edit_rate, int64_t intrinsic_duration, int64_t entry_point) + : ReelTextAsset(type, asset, edit_rate, intrinsic_duration, entry_point) { } -ReelSMPTEClosedCaptionAsset::ReelSMPTEClosedCaptionAsset (shared_ptr<const cxml::Node> node) - : ReelClosedCaptionAsset (node) +ReelInteropTextAsset::ReelInteropTextAsset(std::shared_ptr<const cxml::Node> node) + : ReelTextAsset(node) { node->done (); } -xmlpp::Node * -ReelSMPTEClosedCaptionAsset::write_to_cpl (xmlpp::Node* node, Standard standard) const +string +ReelInteropTextAsset::cpl_node_name() const { - auto asset = ReelClosedCaptionAsset::write_to_cpl (node, standard); - if (_language) { - asset->add_child("Language", "tt")->add_child_text(*_language); + switch (_type) { + case TextType::OPEN_SUBTITLE: + return "MainSubtitle"; + case TextType::OPEN_CAPTION: + return "cc-cpl:MainCaption"; + case TextType::CLOSED_SUBTITLE: + return "ClosedSubtitle"; + case TextType::CLOSED_CAPTION: + return "cc-cpl:MainClosedCaption"; } - return asset; + + DCP_ASSERT(false); + return ""; } -string -ReelSMPTEClosedCaptionAsset::cpl_node_name (Standard) const +pair<string, string> +ReelInteropTextAsset::cpl_node_namespace() const { - return "tt:ClosedCaption"; + switch (_type) { + case TextType::OPEN_SUBTITLE: + case TextType::CLOSED_SUBTITLE: + return {}; + case TextType::OPEN_CAPTION: + case TextType::CLOSED_CAPTION: + return make_pair("http://www.digicine.com/PROTO-ASDCP-CC-CPL-20070926#", "cc-cpl"); + } + + DCP_ASSERT(false); + return {}; } -pair<string, string> -ReelSMPTEClosedCaptionAsset::cpl_node_namespace () const +xmlpp::Element * +ReelInteropTextAsset::write_to_cpl(xmlpp::Element* node, Standard standard) const { - return make_pair("http://www.smpte-ra.org/schemas/429-12/2008/TT", "tt"); + auto asset = ReelFileAsset::write_to_cpl (node, standard); + if (_language) { + cxml::add_text_child(asset, "Language", *_language); + } + return asset; } + diff --git a/src/reel_interop_subtitle_asset.h b/src/reel_interop_text_asset.h index 7e90e9e0..4c0648e4 100644 --- a/src/reel_interop_subtitle_asset.h +++ b/src/reel_interop_text_asset.h @@ -32,35 +32,40 @@ */ -/** @file src/reel_interop_subtitle_asset.h - * @brief ReelInteropSubtitleAsset class +/** @file src/reel_interop_text_asset.h + * @brief ReelInteropTextAsset class */ -#include "interop_subtitle_asset.h" -#include "reel_file_asset.h" -#include "reel_subtitle_asset.h" +#include "interop_text_asset.h" +#include "reel_text_asset.h" namespace dcp { -/** @class ReelInteropSubtitleAsset - * @brief Part of a Reel's description which refers to an Interop subtitle XML file +/** @class ReelInteropTextAsset + * @brief Part of a Reel's description which refers to an Interop subtitle or caption XML file */ -class ReelInteropSubtitleAsset : public ReelSubtitleAsset +class ReelInteropTextAsset : public ReelTextAsset { public: - ReelInteropSubtitleAsset (std::shared_ptr<SubtitleAsset> asset, Fraction edit_rate, int64_t intrinsic_duration, int64_t entry_point); - explicit ReelInteropSubtitleAsset (std::shared_ptr<const cxml::Node>); + ReelInteropTextAsset(TextType type, std::shared_ptr<TextAsset> asset, Fraction edit_rate, int64_t intrinsic_duration, int64_t entry_point); + explicit ReelInteropTextAsset (std::shared_ptr<const cxml::Node>); - std::shared_ptr<const InteropSubtitleAsset> interop_asset () const { - return asset_of_type<const InteropSubtitleAsset>(); + std::shared_ptr<const InteropTextAsset> interop_asset() const { + return asset_of_type<const InteropTextAsset>(); } - std::shared_ptr<InteropSubtitleAsset> interop_asset () { - return asset_of_type<InteropSubtitleAsset>(); + std::shared_ptr<InteropTextAsset> interop_asset() { + return asset_of_type<InteropTextAsset>(); } + + xmlpp::Element* write_to_cpl(xmlpp::Element* node, Standard standard) const override; + +protected: + std::string cpl_node_name() const override; + std::pair<std::string, std::string> cpl_node_namespace() const override; }; diff --git a/src/reel_markers_asset.cc b/src/reel_markers_asset.cc index d71a22ec..df920f44 100644 --- a/src/reel_markers_asset.cc +++ b/src/reel_markers_asset.cc @@ -73,7 +73,7 @@ ReelMarkersAsset::ReelMarkersAsset (cxml::ConstNodePtr node) string -ReelMarkersAsset::cpl_node_name (Standard) const +ReelMarkersAsset::cpl_node_name() const { return "MainMarkers"; } @@ -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..059c0c1f 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); @@ -62,7 +62,7 @@ public: } protected: - std::string cpl_node_name (Standard) const override; + std::string cpl_node_name() const override; private: std::map<Marker, Time> _markers; diff --git a/src/reel_mono_picture_asset.cc b/src/reel_mono_picture_asset.cc index 9c44ec20..5dbf85d2 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) { @@ -62,7 +62,7 @@ ReelMonoPictureAsset::ReelMonoPictureAsset (std::shared_ptr<const cxml::Node> no string -ReelMonoPictureAsset::cpl_node_name (Standard) const +ReelMonoPictureAsset::cpl_node_name() const { return "MainPicture"; } diff --git a/src/reel_mono_picture_asset.h b/src/reel_mono_picture_asset.h index fb2dff70..0c468e0e 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,21 +58,31 @@ 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: - std::string cpl_node_name (Standard standard) const override; + std::string cpl_node_name() const override; }; 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.h b/src/reel_smpte_closed_caption_asset.h deleted file mode 100644 index 32a79efd..00000000 --- a/src/reel_smpte_closed_caption_asset.h +++ /dev/null @@ -1,83 +0,0 @@ -/* - Copyright (C) 2021 Carl Hetherington <cth@carlh.net> - - This file is part of libdcp. - - libdcp is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - libdcp is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with libdcp. If not, see <http://www.gnu.org/licenses/>. - - In addition, as a special exception, the copyright holders give - permission to link the code of portions of this program with the - OpenSSL library under certain conditions as described in each - individual source file, and distribute linked combinations - including the two. - - You must obey the GNU General Public License in all respects - for all of the code used other than OpenSSL. If you modify - file(s) with this exception, you may extend this exception to your - version of the file(s), but you are not obligated to do so. If you - do not wish to do so, delete this exception statement from your - version. If you delete this exception statement from all source - files in the program, then also delete it here. -*/ - - -/** @file src/reel_smpte_closed_caption_asset.h - * @brief ReelSMPTEClosedCaptionAsset class - */ - - -#ifndef LIBDCP_REEL_SMPTE_CLOSED_CAPTION_ASSET_H -#define LIBDCP_REEL_SMPTE_CLOSED_CAPTION_ASSET_H - - -#include "reel_file_asset.h" -#include "reel_closed_caption_asset.h" -#include "smpte_subtitle_asset.h" - - -namespace dcp { - - -class ReelSMPTEClosedCaptionAsset : public ReelClosedCaptionAsset -{ -public: - ReelSMPTEClosedCaptionAsset (std::shared_ptr<SMPTESubtitleAsset> asset, Fraction edit_rate, int64_t intrinsic_duration, int64_t entry_point); - explicit ReelSMPTEClosedCaptionAsset (std::shared_ptr<const cxml::Node>); - - std::shared_ptr<SMPTESubtitleAsset> smpte_asset () { - return asset_of_type<SMPTESubtitleAsset>(); - } - - std::shared_ptr<const SMPTESubtitleAsset> smpte_asset () const { - return asset_of_type<const SMPTESubtitleAsset>(); - } - - xmlpp::Node* write_to_cpl (xmlpp::Node* node, Standard standard) const override; - - -private: - boost::optional<std::string> key_type () const override { - return std::string("MDSK"); - } - - std::string cpl_node_name (Standard) const override; - std::pair<std::string, std::string> cpl_node_namespace () const override; -}; - - -}; - - -#endif - diff --git a/src/reel_interop_closed_caption_asset.cc b/src/reel_smpte_text_asset.cc index be968068..b1ce34cb 100644 --- a/src/reel_interop_closed_caption_asset.cc +++ b/src/reel_smpte_text_asset.cc @@ -32,7 +32,13 @@ */ -#include "reel_interop_closed_caption_asset.h" +/** @file src/reel_interop_text_asset.cc + * @brief ReelInteropTextAsset class + */ + + +#include "reel_smpte_text_asset.h" +#include "smpte_text_asset.h" #include "warnings.h" LIBDCP_DISABLE_WARNINGS #include <libxml++/libxml++.h> @@ -43,44 +49,70 @@ using std::make_pair; using std::pair; using std::shared_ptr; using std::string; +using boost::optional; using namespace dcp; -ReelInteropClosedCaptionAsset::ReelInteropClosedCaptionAsset (shared_ptr<InteropSubtitleAsset> asset, Fraction edit_rate, int64_t intrinsic_duration, int64_t entry_point) - : ReelClosedCaptionAsset (asset, edit_rate, intrinsic_duration, entry_point) +ReelSMPTETextAsset::ReelSMPTETextAsset(TextType type, shared_ptr<SMPTETextAsset> asset, Fraction edit_rate, int64_t intrinsic_duration, int64_t entry_point) + : ReelTextAsset(type, asset, edit_rate, intrinsic_duration, entry_point) { } - -ReelInteropClosedCaptionAsset::ReelInteropClosedCaptionAsset (shared_ptr<const cxml::Node> node) - : ReelClosedCaptionAsset (node) +ReelSMPTETextAsset::ReelSMPTETextAsset(shared_ptr<const cxml::Node> node) + : ReelTextAsset(node) { node->done (); } + string -ReelInteropClosedCaptionAsset::cpl_node_name (Standard) const +ReelSMPTETextAsset::cpl_node_name() const { - return "cc-cpl:MainClosedCaption"; + switch (_type) { + case TextType::OPEN_SUBTITLE: + return "MainSubtitle"; + case TextType::OPEN_CAPTION: + return "tt:MainCaption"; + case TextType::CLOSED_SUBTITLE: + return "ClosedSubtitle"; + case TextType::CLOSED_CAPTION: + return "tt:ClosedCaption"; + } + + DCP_ASSERT(false); + return ""; } pair<string, string> -ReelInteropClosedCaptionAsset::cpl_node_namespace () const +ReelSMPTETextAsset::cpl_node_namespace() const { - return make_pair("http://www.digicine.com/PROTO-ASDCP-CC-CPL-20070926#", "cc-cpl"); + switch (_type) { + case TextType::OPEN_SUBTITLE: + case TextType::CLOSED_SUBTITLE: + return {}; + case TextType::OPEN_CAPTION: + case TextType::CLOSED_CAPTION: + return make_pair("http://www.smpte-ra.org/schemas/429-12/2008/TT", "tt"); + } + + DCP_ASSERT(false); + return {}; } -xmlpp::Node * -ReelInteropClosedCaptionAsset::write_to_cpl (xmlpp::Node* node, Standard standard) const +xmlpp::Element * +ReelSMPTETextAsset::write_to_cpl(xmlpp::Element* node, Standard standard) const { - auto asset = ReelClosedCaptionAsset::write_to_cpl (node, standard); + auto asset = ReelFileAsset::write_to_cpl (node, standard); + string const ns = _type == TextType::CLOSED_CAPTION ? "tt" : ""; if (_language) { - asset->add_child("Language")->add_child_text(*_language); + cxml::add_child(asset, "Language", ns)->add_child_text(*_language); } return asset; } + + diff --git a/src/reel_smpte_subtitle_asset.h b/src/reel_smpte_text_asset.h index 2a097309..b3c2f655 100644 --- a/src/reel_smpte_subtitle_asset.h +++ b/src/reel_smpte_text_asset.h @@ -32,38 +32,44 @@ */ -/** @file src/reel_interop_subtitle_asset.h - * @brief ReelInteropSubtitleAsset class +/** @file src/reel_smpte_text_asset.h + * @brief ReelSMPTETextAsset class */ -#include "reel_subtitle_asset.h" -#include "smpte_subtitle_asset.h" +#include "reel_text_asset.h" +#include "smpte_text_asset.h" namespace dcp { -class SMPTESubtitleAsset; +class SMPTETextAsset; -/** @class ReelSMPTESubtitleAsset - * @brief Part of a Reel's description which refers to an SMPTE subtitle MXF file +/** @class ReelSMPTETextAsset + * @brief Part of a Reel's description which refers to an SMPTE subtitle or caption MXF file */ -class ReelSMPTESubtitleAsset : public ReelSubtitleAsset +class ReelSMPTETextAsset : public ReelTextAsset { public: - ReelSMPTESubtitleAsset (std::shared_ptr<SMPTESubtitleAsset> asset, Fraction edit_rate, int64_t intrinsic_duration, int64_t entry_point); - explicit ReelSMPTESubtitleAsset (std::shared_ptr<const cxml::Node>); + ReelSMPTETextAsset(TextType type, std::shared_ptr<SMPTETextAsset> asset, Fraction edit_rate, int64_t intrinsic_duration, int64_t entry_point); + explicit ReelSMPTETextAsset(std::shared_ptr<const cxml::Node>); - std::shared_ptr<const SMPTESubtitleAsset> smpte_asset () const { - return asset_of_type<const SMPTESubtitleAsset>(); + std::shared_ptr<const SMPTETextAsset> smpte_asset() const { + return asset_of_type<const SMPTETextAsset>(); } - std::shared_ptr<SMPTESubtitleAsset> smpte_asset () { - return asset_of_type<SMPTESubtitleAsset>(); + std::shared_ptr<SMPTETextAsset> smpte_asset() { + return asset_of_type<SMPTETextAsset>(); } + xmlpp::Element* write_to_cpl(xmlpp::Element* node, Standard standard) const override; + +protected: + std::string cpl_node_name() const override; + std::pair<std::string, std::string> cpl_node_namespace() const override; + private: boost::optional<std::string> key_type () const override { return std::string("MDSK"); diff --git a/src/reel_sound_asset.cc b/src/reel_sound_asset.cc index 1f2ddcdd..dbac6cb3 100644 --- a/src/reel_sound_asset.cc +++ b/src/reel_sound_asset.cc @@ -68,7 +68,7 @@ ReelSoundAsset::ReelSoundAsset (shared_ptr<const cxml::Node> node) string -ReelSoundAsset::cpl_node_name (Standard) const +ReelSoundAsset::cpl_node_name() const { return "MainSound"; } diff --git a/src/reel_sound_asset.h b/src/reel_sound_asset.h index 16d629d1..6537ab5c 100644 --- a/src/reel_sound_asset.h +++ b/src/reel_sound_asset.h @@ -69,7 +69,7 @@ public: private: boost::optional<std::string> key_type () const override; - std::string cpl_node_name (Standard standard) const override; + std::string cpl_node_name() const override; }; diff --git a/src/reel_stereo_picture_asset.cc b/src/reel_stereo_picture_asset.cc index 5bf4756c..2ee452d9 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) { @@ -64,7 +64,7 @@ ReelStereoPictureAsset::ReelStereoPictureAsset (std::shared_ptr<const cxml::Node string -ReelStereoPictureAsset::cpl_node_name (Standard) const +ReelStereoPictureAsset::cpl_node_name() const { return "msp-cpl:MainStereoscopicPicture"; } diff --git a/src/reel_stereo_picture_asset.h b/src/reel_stereo_picture_asset.h index 7cac1c8b..0dd726a4 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,21 +57,21 @@ 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: - std::string cpl_node_name (Standard standard) const override; + std::string cpl_node_name() const override; std::pair<std::string, std::string> cpl_node_attribute (Standard standard) const override; }; diff --git a/src/reel_subtitle_asset.cc b/src/reel_text_asset.cc index d856a05e..248a2ab3 100644 --- a/src/reel_subtitle_asset.cc +++ b/src/reel_text_asset.cc @@ -32,15 +32,14 @@ */ -/** @file src/reel_subtitle_asset.cc - * @brief ReelSubtitleAsset class +/** @file src/reel_text_asset.cc + * @brief ReelTextAsset class */ #include "language_tag.h" -#include "reel_subtitle_asset.h" -#include "smpte_subtitle_asset.h" -#include "subtitle_asset.h" +#include "reel_text_asset.h" +#include "smpte_text_asset.h" #include "warnings.h" LIBDCP_DISABLE_WARNINGS #include <libxml++/libxml++.h> @@ -54,43 +53,45 @@ using boost::optional; using namespace dcp; -ReelSubtitleAsset::ReelSubtitleAsset (std::shared_ptr<SubtitleAsset> asset, Fraction edit_rate, int64_t intrinsic_duration, int64_t entry_point) +ReelTextAsset::ReelTextAsset(TextType type, std::shared_ptr<TextAsset> asset, Fraction edit_rate, int64_t intrinsic_duration, int64_t entry_point) : ReelFileAsset ( asset, - dynamic_pointer_cast<SMPTESubtitleAsset>(asset) ? dynamic_pointer_cast<SMPTESubtitleAsset>(asset)->key_id() : boost::none, + dynamic_pointer_cast<SMPTETextAsset>(asset) ? dynamic_pointer_cast<SMPTETextAsset>(asset)->key_id() : boost::none, asset->id(), edit_rate, intrinsic_duration, entry_point ) + , _type(type) { } -ReelSubtitleAsset::ReelSubtitleAsset (std::shared_ptr<const cxml::Node> node) +ReelTextAsset::ReelTextAsset(std::shared_ptr<const cxml::Node> node) : ReelFileAsset (node) { - _language = node->optional_string_child("Language"); -} - + if (node->name() == "MainSubtitle") { + _type = TextType::OPEN_SUBTITLE; + } else if (node->name() == "MainClosedCaption" || node->name() == "ClosedCaption") { + _type = TextType::CLOSED_CAPTION; + } else { + DCP_ASSERT(false); + } -string -ReelSubtitleAsset::cpl_node_name (Standard) const -{ - return "MainSubtitle"; + _language = node->optional_string_child("Language"); } void -ReelSubtitleAsset::set_language (dcp::LanguageTag language) +ReelTextAsset::set_language(dcp::LanguageTag language) { _language = language.to_string(); } bool -ReelSubtitleAsset::equals(shared_ptr<const ReelSubtitleAsset> other, EqualityOptions const& opt, NoteHandler note) const +ReelTextAsset::equals(shared_ptr<const ReelTextAsset> other, EqualityOptions const& opt, NoteHandler note) const { if (!asset_equals (other, opt, note)) { return false; @@ -102,15 +103,3 @@ ReelSubtitleAsset::equals(shared_ptr<const ReelSubtitleAsset> other, EqualityOpt return true; } - -xmlpp::Node * -ReelSubtitleAsset::write_to_cpl (xmlpp::Node* node, Standard standard) const -{ - auto asset = ReelFileAsset::write_to_cpl (node, standard); - if (_language) { - asset->add_child("Language")->add_child_text(*_language); - } - return asset; -} - - diff --git a/src/reel_subtitle_asset.h b/src/reel_text_asset.h index 8b694fd6..35efe941 100644 --- a/src/reel_subtitle_asset.h +++ b/src/reel_text_asset.h @@ -32,50 +32,50 @@ */ -/** @file src/reel_subtitle_asset.h - * @brief ReelSubtitleAsset class. +/** @file src/reel_text_asset.h + * @brief ReelTextAsset class. */ -#ifndef LIBDCP_REEL_SUBTITLE_ASSET_H -#define LIBDCP_REEL_SUBTITLE_ASSET_H +#ifndef LIBDCP_REEL_TEXT_ASSET_H +#define LIBDCP_REEL_TEXT_ASSET_H #include "language_tag.h" #include "reel_asset.h" #include "reel_file_asset.h" -#include "subtitle_asset.h" +#include "text_asset.h" +#include "text_type.h" struct verify_invalid_language1; +struct verify_invalid_language2; namespace dcp { -class SubtitleAsset; +class TextAsset; -/** @class ReelSubtitleAsset - * @brief Part of a Reel's description which refers to a subtitle XML/MXF file +/** @class ReelTextAsset + * @brief Part of a Reel's description which refers to a subtitle or caption XML/MXF file */ -class ReelSubtitleAsset : public ReelFileAsset +class ReelTextAsset : public ReelFileAsset { public: - ReelSubtitleAsset (std::shared_ptr<SubtitleAsset> asset, Fraction edit_rate, int64_t intrinsic_duration, int64_t entry_point); - explicit ReelSubtitleAsset (std::shared_ptr<const cxml::Node>); + ReelTextAsset(TextType type, std::shared_ptr<TextAsset> asset, Fraction edit_rate, int64_t intrinsic_duration, int64_t entry_point); + explicit ReelTextAsset(std::shared_ptr<const cxml::Node>); - std::shared_ptr<const SubtitleAsset> asset () const { - return asset_of_type<const SubtitleAsset>(); + std::shared_ptr<const TextAsset> asset() const { + return asset_of_type<const TextAsset>(); } - std::shared_ptr<SubtitleAsset> asset () { - return asset_of_type<SubtitleAsset>(); + std::shared_ptr<TextAsset> asset() { + return asset_of_type<TextAsset>(); } - xmlpp::Node* write_to_cpl (xmlpp::Node* node, Standard standard) const override; - - bool equals(std::shared_ptr<const ReelSubtitleAsset>, EqualityOptions const&, NoteHandler) const; + bool equals(std::shared_ptr<const ReelTextAsset>, EqualityOptions const&, NoteHandler) const; void set_language (dcp::LanguageTag language); @@ -83,17 +83,20 @@ public: return _language; } + TextType type() const { + return _type; + } + protected: + friend struct ::verify_invalid_language1; + friend struct ::verify_invalid_language2; + /** As in other places, this is stored and returned as a string so that * we can tolerate non-RFC-5646 strings, but must be set as a dcp::LanguageTag * to try to ensure that we create compliant output. */ boost::optional<std::string> _language; - -private: - friend struct ::verify_invalid_language1; - - std::string cpl_node_name (Standard standard) const override; + TextType _type; }; 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_text_asset.cc index 0ff1d7ef..f92a091a 100644 --- a/src/smpte_subtitle_asset.cc +++ b/src/smpte_text_asset.cc @@ -32,8 +32,8 @@ */ -/** @file src/smpte_subtitle_asset.cc - * @brief SMPTESubtitleAsset class +/** @file src/smpte_text_asset.cc + * @brief SMPTETextAsset class */ @@ -45,8 +45,8 @@ #include "filesystem.h" #include "raw_convert.h" #include "smpte_load_font_node.h" -#include "smpte_subtitle_asset.h" -#include "subtitle_image.h" +#include "smpte_text_asset.h" +#include "text_image.h" #include "util.h" #include "warnings.h" #include "xml.h" @@ -79,7 +79,7 @@ static string const subtitle_smpte_ns_2010 = "http://www.smpte-ra.org/schemas/42 static string const subtitle_smpte_ns_2014 = "http://www.smpte-ra.org/schemas/428-7/2014/DCST"; -SMPTESubtitleAsset::SMPTESubtitleAsset(SubtitleStandard standard) +SMPTETextAsset::SMPTETextAsset(SubtitleStandard standard) : MXF(Standard::SMPTE) , _edit_rate (24, 1) , _time_code_rate (24) @@ -90,12 +90,13 @@ SMPTESubtitleAsset::SMPTESubtitleAsset(SubtitleStandard standard) } -SMPTESubtitleAsset::SMPTESubtitleAsset (boost::filesystem::path file) - : SubtitleAsset (file) +SMPTETextAsset::SMPTETextAsset(boost::filesystem::path file) + : TextAsset (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; @@ -139,8 +140,8 @@ SMPTESubtitleAsset::SMPTESubtitleAsset (boost::filesystem::path file) /* Try to read PNG files from the same folder that the XML is in; the wisdom of this is debatable, at best... */ - for (auto i: _subtitles) { - auto im = dynamic_pointer_cast<SubtitleImage>(i); + for (auto i: _texts) { + auto im = dynamic_pointer_cast<TextImage>(i); if (im && im->png_image().size() == 0) { /* Even more dubious; allow <id>.png or urn:uuid:<id>.png */ auto p = file.parent_path() / String::compose("%1.png", im->id()); @@ -158,17 +159,17 @@ SMPTESubtitleAsset::SMPTESubtitleAsset (boost::filesystem::path file) } /* Check that all required image data have been found */ - for (auto i: _subtitles) { - auto im = dynamic_pointer_cast<SubtitleImage>(i); + for (auto i: _texts) { + auto im = dynamic_pointer_cast<TextImage>(i); if (im && im->png_image().size() == 0) { - throw MissingSubtitleImageError (im->id()); + throw MissingTextImageError (im->id()); } } } void -SMPTESubtitleAsset::parse_xml (shared_ptr<cxml::Document> xml) +SMPTETextAsset::parse_xml(shared_ptr<cxml::Document> xml) { if (xml->namespace_uri() == subtitle_smpte_ns_2007) { _subtitle_standard = SubtitleStandard::SMPTE_2007; @@ -211,17 +212,17 @@ SMPTESubtitleAsset::parse_xml (shared_ptr<cxml::Document> xml) for (auto i: xml->node()->get_children()) { auto const e = dynamic_cast<xmlpp::Element const *>(i); if (e && e->get_name() == "SubtitleList") { - parse_subtitles (e, ps, _time_code_rate, Standard::SMPTE); + parse_texts(e, ps, _time_code_rate, Standard::SMPTE); } } /* Guess intrinsic duration */ - _intrinsic_duration = latest_subtitle_out().as_editable_units_ceil(_edit_rate.numerator / _edit_rate.denominator); + _intrinsic_duration = latest_text_out().as_editable_units_ceil(_edit_rate.numerator / _edit_rate.denominator); } void -SMPTESubtitleAsset::read_mxf_resources (shared_ptr<ASDCP::TimedText::MXFReader> reader, shared_ptr<DecryptionContext> dec) +SMPTETextAsset::read_mxf_resources(shared_ptr<ASDCP::TimedText::MXFReader> reader, shared_ptr<DecryptionContext> dec) { ASDCP::TimedText::TimedTextDescriptor descriptor; reader->FillTimedTextDescriptor (descriptor); @@ -265,13 +266,13 @@ SMPTESubtitleAsset::read_mxf_resources (shared_ptr<ASDCP::TimedText::MXFReader> } case ASDCP::TimedText::MT_PNG: { - auto j = _subtitles.begin(); - while (j != _subtitles.end() && ((!dynamic_pointer_cast<SubtitleImage>(*j)) || dynamic_pointer_cast<SubtitleImage>(*j)->id() != id)) { + auto j = _texts.begin(); + while (j != _texts.end() && ((!dynamic_pointer_cast<TextImage>(*j)) || dynamic_pointer_cast<TextImage>(*j)->id() != id)) { ++j; } - if (j != _subtitles.end()) { - dynamic_pointer_cast<SubtitleImage>(*j)->set_png_image(ArrayData(buffer.RoData(), buffer.Size())); + if (j != _texts.end()) { + dynamic_pointer_cast<TextImage>(*j)->set_png_image(ArrayData(buffer.RoData(), buffer.Size())); } break; } @@ -283,7 +284,7 @@ SMPTESubtitleAsset::read_mxf_resources (shared_ptr<ASDCP::TimedText::MXFReader> void -SMPTESubtitleAsset::read_mxf_descriptor (shared_ptr<ASDCP::TimedText::MXFReader> reader) +SMPTETextAsset::read_mxf_descriptor(shared_ptr<ASDCP::TimedText::MXFReader> reader) { ASDCP::TimedText::TimedTextDescriptor descriptor; reader->FillTimedTextDescriptor (descriptor); @@ -300,7 +301,7 @@ SMPTESubtitleAsset::read_mxf_descriptor (shared_ptr<ASDCP::TimedText::MXFReader> void -SMPTESubtitleAsset::set_key (Key key) +SMPTETextAsset::set_key(Key key) { /* See if we already have a key; if we do, and we have a file, we'll already have read that file. @@ -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 ( @@ -343,7 +345,7 @@ SMPTESubtitleAsset::set_key (Key key) vector<shared_ptr<LoadFontNode>> -SMPTESubtitleAsset::load_font_nodes () const +SMPTETextAsset::load_font_nodes() const { vector<shared_ptr<LoadFontNode>> lf; copy (_load_font_nodes.begin(), _load_font_nodes.end(), back_inserter(lf)); @@ -352,9 +354,10 @@ SMPTESubtitleAsset::load_font_nodes () const bool -SMPTESubtitleAsset::valid_mxf (boost::filesystem::path file) +SMPTETextAsset::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); @@ -363,44 +366,44 @@ SMPTESubtitleAsset::valid_mxf (boost::filesystem::path file) string -SMPTESubtitleAsset::xml_as_string () const +SMPTETextAsset::xml_as_string() const { xmlpp::Document doc; 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); + texts_as_xml(cxml::add_child(root, "SubtitleList"), _time_code_rate, Standard::SMPTE); return format_xml(doc, std::make_pair(string{}, schema_namespace())); } void -SMPTESubtitleAsset::write (boost::filesystem::path p) const +SMPTETextAsset::write(boost::filesystem::path p) const { EncryptionContext enc (key(), Standard::SMPTE); @@ -430,8 +433,8 @@ SMPTESubtitleAsset::write (boost::filesystem::path p) const /* Image subtitle references */ - for (auto i: _subtitles) { - auto si = dynamic_pointer_cast<SubtitleImage>(i); + for (auto i: _texts) { + auto si = dynamic_pointer_cast<TextImage>(i); if (si) { ASDCP::TimedText::TimedTextResourceDescriptor res; unsigned int c; @@ -453,7 +456,7 @@ SMPTESubtitleAsset::write (boost::filesystem::path p) const /* This header size is a guess. Empirically it seems that each subtitle reference is 90 bytes, and we need some extra. The default size is not enough for some feature-length PNG sub projects (see DCP-o-matic #1561). */ - ASDCP::Result_t r = writer.OpenWrite(dcp::filesystem::fix_long_path(p).string().c_str(), writer_info, descriptor, _subtitles.size() * 90 + 16384); + ASDCP::Result_t r = writer.OpenWrite(dcp::filesystem::fix_long_path(p).string().c_str(), writer_info, descriptor, _texts.size() * 90 + 16384); if (ASDCP_FAILURE (r)) { boost::throw_exception (FileError ("could not open subtitle MXF for writing", p.string(), r)); } @@ -486,9 +489,8 @@ SMPTESubtitleAsset::write (boost::filesystem::path p) const /* Image subtitle payload */ - for (auto i: _subtitles) { - auto si = dynamic_pointer_cast<SubtitleImage>(i); - if (si) { + for (auto i: _texts) { + if (auto si = dynamic_pointer_cast<TextImage>(i)) { ASDCP::TimedText::FrameBuffer buffer; buffer.SetData (si->png_image().data(), si->png_image().size()); buffer.Size (si->png_image().size()); @@ -505,15 +507,15 @@ SMPTESubtitleAsset::write (boost::filesystem::path p) const } bool -SMPTESubtitleAsset::equals(shared_ptr<const Asset> other_asset, EqualityOptions const& options, NoteHandler note) const +SMPTETextAsset::equals(shared_ptr<const Asset> other_asset, EqualityOptions const& options, NoteHandler note) const { - if (!SubtitleAsset::equals (other_asset, options, note)) { + if (!TextAsset::equals (other_asset, options, note)) { return false; } - auto other = dynamic_pointer_cast<const SMPTESubtitleAsset>(other_asset); + auto other = dynamic_pointer_cast<const SMPTETextAsset>(other_asset); if (!other) { - note (NoteType::ERROR, "Subtitles are in different standards"); + note (NoteType::ERROR, "Subtitles/captions are in different standards"); return false; } @@ -536,46 +538,46 @@ SMPTESubtitleAsset::equals(shared_ptr<const Asset> other_asset, EqualityOptions } if (_content_title_text != other->_content_title_text) { - note (NoteType::ERROR, "Subtitle content title texts differ"); + note (NoteType::ERROR, "Subtitle/caption content title texts differ"); return false; } if (_language != other->_language) { - note (NoteType::ERROR, String::compose("Subtitle languages differ (`%1' vs `%2')", _language.get_value_or("[none]"), other->_language.get_value_or("[none]"))); + note (NoteType::ERROR, String::compose("Subtitle/caption languages differ (`%1' vs `%2')", _language.get_value_or("[none]"), other->_language.get_value_or("[none]"))); return false; } if (_annotation_text != other->_annotation_text) { - note (NoteType::ERROR, "Subtitle annotation texts differ"); + note (NoteType::ERROR, "Subtitle/caption annotation texts differ"); return false; } if (_issue_date != other->_issue_date) { if (options.issue_dates_can_differ) { - note (NoteType::NOTE, "Subtitle issue dates differ"); + note (NoteType::NOTE, "Subtitle/caption issue dates differ"); } else { - note (NoteType::ERROR, "Subtitle issue dates differ"); + note (NoteType::ERROR, "Subtitle/caption issue dates differ"); return false; } } if (_reel_number != other->_reel_number) { - note (NoteType::ERROR, "Subtitle reel numbers differ"); + note (NoteType::ERROR, "Subtitle/caption reel numbers differ"); return false; } if (_edit_rate != other->_edit_rate) { - note (NoteType::ERROR, "Subtitle edit rates differ"); + note (NoteType::ERROR, "Subtitle/caption edit rates differ"); return false; } if (_time_code_rate != other->_time_code_rate) { - note (NoteType::ERROR, "Subtitle time code rates differ"); + note (NoteType::ERROR, "Subtitle/caption time code rates differ"); return false; } if (_start_time != other->_start_time) { - note (NoteType::ERROR, "Subtitle start times differ"); + note (NoteType::ERROR, "Subtitle/caption start times differ"); return false; } @@ -584,7 +586,7 @@ SMPTESubtitleAsset::equals(shared_ptr<const Asset> other_asset, EqualityOptions void -SMPTESubtitleAsset::add_font (string load_id, dcp::ArrayData data) +SMPTETextAsset::add_font(string load_id, dcp::ArrayData data) { string const uuid = make_uuid (); _fonts.push_back (Font(load_id, uuid, data)); @@ -593,15 +595,15 @@ SMPTESubtitleAsset::add_font (string load_id, dcp::ArrayData data) void -SMPTESubtitleAsset::add (shared_ptr<Subtitle> s) +SMPTETextAsset::add(shared_ptr<Text> s) { - SubtitleAsset::add (s); - _intrinsic_duration = latest_subtitle_out().as_editable_units_ceil(_edit_rate.numerator / _edit_rate.denominator); + TextAsset::add(s); + _intrinsic_duration = latest_text_out().as_editable_units_ceil(_edit_rate.numerator / _edit_rate.denominator); } string -SMPTESubtitleAsset::schema_namespace() const +SMPTETextAsset::schema_namespace() const { switch (_subtitle_standard) { case SubtitleStandard::SMPTE_2007: diff --git a/src/smpte_subtitle_asset.h b/src/smpte_text_asset.h index 26144fe1..24665d3a 100644 --- a/src/smpte_subtitle_asset.h +++ b/src/smpte_text_asset.h @@ -32,12 +32,12 @@ */ -#ifndef LIBDCP_SMPTE_SUBTITLE_ASSET_H -#define LIBDCP_SMPTE_SUBTITLE_ASSET_H +#ifndef LIBDCP_SMPTE_TEXT_ASSET_H +#define LIBDCP_SMPTE_TEXT_ASSET_H -/** @file src/smpte_subtitle_asset.h - * @brief SMPTESubtitleAsset class +/** @file src/smpte_text_asset.h + * @brief SMPTETextAsset class */ @@ -45,7 +45,7 @@ #include "language_tag.h" #include "local_time.h" #include "mxf.h" -#include "subtitle_asset.h" +#include "text_asset.h" #include "subtitle_standard.h" #include <boost/filesystem.hpp> @@ -69,18 +69,18 @@ namespace dcp { class SMPTELoadFontNode; -/** @class SMPTESubtitleAsset - * @brief A set of subtitles to be read and/or written in the SMPTE format +/** @class SMPTETextAsset + * @brief A set of subtitles/captions to be read and/or written in the SMPTE format */ -class SMPTESubtitleAsset : public SubtitleAsset, public MXF +class SMPTETextAsset : public TextAsset, public MXF { public: - explicit SMPTESubtitleAsset(SubtitleStandard standard = SubtitleStandard::SMPTE_2014); + explicit SMPTETextAsset(SubtitleStandard standard = SubtitleStandard::SMPTE_2014); - /** Construct a SMPTESubtitleAsset by reading an MXF or XML file + /** Construct a SMPTETextAsset by reading an MXF or XML file * @param file Filename */ - explicit SMPTESubtitleAsset (boost::filesystem::path file); + explicit SMPTETextAsset(boost::filesystem::path file); bool equals ( std::shared_ptr<const Asset>, @@ -95,7 +95,7 @@ public: /** Write this content to a MXF file */ void write (boost::filesystem::path path) const override; - void add (std::shared_ptr<Subtitle>) override; + void add(std::shared_ptr<Text>) override; void add_font (std::string id, dcp::ArrayData data) override; void set_key (Key key) override; @@ -135,7 +135,7 @@ public: return _intrinsic_duration; } - /** @return title of the film that these subtitles are for, + /** @return title of the film that these subtitles/captions are for, * to be presented to the user */ std::string content_title_text () const { @@ -167,8 +167,8 @@ public: return _edit_rate; } - /** @return subdivision of 1 second that is used for subtitle times; - * e.g. a time_code_rate of 250 means that a subtitle time of 0:0:0:001 + /** @return subdivision of 1 second that is used for text times; + * e.g. a time_code_rate of 250 means that a text time of 0:0:0:001 * represents 4ms. */ int time_code_rate () const override { 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/stereo_picture_asset_reader.h b/src/stereo_j2k_picture_asset_reader.h index 9cb05263..e4812a8e 100644 --- a/src/stereo_picture_asset_reader.h +++ b/src/stereo_j2k_picture_asset_reader.h @@ -32,23 +32,23 @@ */ -/** @file src/stereo_picture_asset_reader.h - * @brief StereoPictureAssetReader typedef +/** @file src/stereo_j2k_picture_asset_reader.h + * @brief StereoJ2KPictureAssetReader typedef */ -#ifndef LIBDCP_STEREO_PICTURE_ASSET_READER_H -#define LIBDCP_STEREO_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 "stereo_picture_frame.h" +#include "stereo_j2k_picture_frame.h" namespace dcp { -typedef AssetReader<ASDCP::JP2K::MXFSReader, StereoPictureFrame> StereoPictureAssetReader; +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.cc b/src/text.cc index 248d0cff..158af38c 100644 --- a/src/subtitle.cc +++ b/src/text.cc @@ -32,15 +32,15 @@ */ -/** @file src/subtitle.cc - * @brief Subtitle class +/** @file src/text.cc + * @brief Text class */ #include "compose.hpp" #include "dcp_time.h" #include "equality_options.h" -#include "subtitle.h" +#include "text.h" using std::shared_ptr; @@ -48,7 +48,7 @@ using namespace dcp; /** @param v_position Vertical position as a fraction of the screen height (between 0 and 1) from v_align */ -Subtitle::Subtitle ( +Text::Text( Time in, Time out, float h_position, @@ -74,56 +74,56 @@ Subtitle::Subtitle ( bool -Subtitle::equals(shared_ptr<const Subtitle> other, EqualityOptions const& options, NoteHandler note) const +Text::equals(shared_ptr<const Text> other, EqualityOptions const& options, NoteHandler note) const { bool same = true; if (in() != other->in()) { - note(NoteType::ERROR, "subtitle in times differ"); + note(NoteType::ERROR, "text in times differ"); same = false; } if (out() != other->out()) { - note(NoteType::ERROR, "subtitle out times differ"); + note(NoteType::ERROR, "text out times differ"); same = false; } if (h_position() != other->h_position()) { - note(NoteType::ERROR, "subtitle horizontal positions differ"); + note(NoteType::ERROR, "text horizontal positions differ"); same = false; } if (h_align() != other->h_align()) { - note(NoteType::ERROR, "subtitle horizontal alignments differ"); + note(NoteType::ERROR, "text horizontal alignments differ"); same = false; } auto const vpos = std::abs(v_position() - other->v_position()); - if (vpos > options.max_subtitle_vertical_position_error) { + if (vpos > options.max_text_vertical_position_error) { note( NoteType::ERROR, - String::compose("subtitle vertical positions differ by %1 (more than the allowed difference of %2)", vpos, options.max_subtitle_vertical_position_error) + String::compose("text vertical positions differ by %1 (more than the allowed difference of %2)", vpos, options.max_text_vertical_position_error) ); same = false; } if (v_align() != other->v_align()) { - note(NoteType::ERROR, "subtitle vertical alignments differ"); + note(NoteType::ERROR, "text vertical alignments differ"); same = false; } if (z_position() != other->z_position()) { - note(NoteType::ERROR, "subtitle Z positions differ"); + note(NoteType::ERROR, "text Z positions differ"); same = false; } if (fade_up_time() != other->fade_up_time()) { - note(NoteType::ERROR, "subtitle fade-up times differ"); + note(NoteType::ERROR, "text fade-up times differ"); same = false; } if (fade_down_time() != other->fade_down_time()) { - note(NoteType::ERROR, "subtitle fade-down times differ"); + note(NoteType::ERROR, "text fade-down times differ"); same = false; } diff --git a/src/subtitle.h b/src/text.h index 1ca3f9d4..b8f15757 100644 --- a/src/subtitle.h +++ b/src/text.h @@ -32,13 +32,13 @@ */ -/** @file src/subtitle.h - * @brief Subtitle class +/** @file src/text.h + * @brief Text class */ -#ifndef LIBDCP_SUBTITLE_H -#define LIBDCP_SUBTITLE_H +#ifndef LIBDCP_TEXT_H +#define LIBDCP_TEXT_H #include "dcp_time.h" @@ -52,17 +52,17 @@ namespace dcp { class EqualityOptions; -class Subtitle +class Text { public: - virtual ~Subtitle () {} + virtual ~Text() {} - /** @return subtitle start time (relative to the start of the reel) */ + /** @return text start time (relative to the start of the reel) */ Time in () const { return _in; } - /** @return subtitle finish time (relative to the start of the reel) */ + /** @return text finish time (relative to the start of the reel) */ Time out () const { return _out; } @@ -130,11 +130,11 @@ public: _fade_down_time = t; } - virtual bool equals(std::shared_ptr<const dcp::Subtitle> other, EqualityOptions const& options, NoteHandler note) const; + virtual bool equals(std::shared_ptr<const dcp::Text> other, EqualityOptions const& options, NoteHandler note) const; protected: - Subtitle ( + Text( Time in, Time out, float h_position, diff --git a/src/subtitle_asset.cc b/src/text_asset.cc index 1cd4fc07..ff662b69 100644 --- a/src/subtitle_asset.cc +++ b/src/text_asset.cc @@ -32,8 +32,8 @@ */ -/** @file src/subtitle_asset.cc - * @brief SubtitleAsset class +/** @file src/text_asset.cc + * @brief TextAsset class */ @@ -42,10 +42,10 @@ #include "load_font_node.h" #include "raw_convert.h" #include "reel_asset.h" -#include "subtitle_asset.h" -#include "subtitle_asset_internal.h" -#include "subtitle_image.h" -#include "subtitle_string.h" +#include "text_image.h" +#include "text_string.h" +#include "text_asset.h" +#include "text_asset_internal.h" #include "util.h" #include "xml.h" #include <asdcp/AS_DCP.h> @@ -71,13 +71,13 @@ using boost::optional; using namespace dcp; -SubtitleAsset::SubtitleAsset () +TextAsset::TextAsset() { } -SubtitleAsset::SubtitleAsset (boost::filesystem::path file) +TextAsset::TextAsset(boost::filesystem::path file) : Asset (file) { @@ -133,8 +133,8 @@ optional_number_attribute (xmlpp::Element const * node, string name) } -SubtitleAsset::ParseState -SubtitleAsset::font_node_state (xmlpp::Element const * node, Standard standard) const +TextAsset::ParseState +TextAsset::font_node_state(xmlpp::Element const * node, Standard standard) const { ParseState ps; @@ -169,7 +169,7 @@ SubtitleAsset::font_node_state (xmlpp::Element const * node, Standard standard) } void -SubtitleAsset::position_align (SubtitleAsset::ParseState& ps, xmlpp::Element const * node) const +TextAsset::position_align(TextAsset::ParseState& ps, xmlpp::Element const * node) const { auto hp = optional_number_attribute<float> (node, "HPosition"); if (!hp) { @@ -210,8 +210,8 @@ SubtitleAsset::position_align (SubtitleAsset::ParseState& ps, xmlpp::Element con } -SubtitleAsset::ParseState -SubtitleAsset::text_node_state (xmlpp::Element const * node) const +TextAsset::ParseState +TextAsset::text_node_state(xmlpp::Element const * node) const { ParseState ps; @@ -228,8 +228,8 @@ SubtitleAsset::text_node_state (xmlpp::Element const * node) const } -SubtitleAsset::ParseState -SubtitleAsset::image_node_state (xmlpp::Element const * node) const +TextAsset::ParseState +TextAsset::image_node_state(xmlpp::Element const * node) const { ParseState ps; @@ -241,8 +241,8 @@ SubtitleAsset::image_node_state (xmlpp::Element const * node) const } -SubtitleAsset::ParseState -SubtitleAsset::subtitle_node_state (xmlpp::Element const * node, optional<int> tcr) const +TextAsset::ParseState +TextAsset::subtitle_node_state(xmlpp::Element const * node, optional<int> tcr) const { ParseState ps; ps.in = Time (string_attribute(node, "TimeIn"), tcr); @@ -254,7 +254,7 @@ SubtitleAsset::subtitle_node_state (xmlpp::Element const * node, optional<int> t Time -SubtitleAsset::fade_time (xmlpp::Element const * node, string name, optional<int> tcr) const +TextAsset::fade_time(xmlpp::Element const * node, string name, optional<int> tcr) const { auto const u = optional_string_attribute(node, name).get_value_or (""); Time t; @@ -276,7 +276,7 @@ SubtitleAsset::fade_time (xmlpp::Element const * node, string name, optional<int void -SubtitleAsset::parse_subtitles (xmlpp::Element const * node, vector<ParseState>& state, optional<int> tcr, Standard standard) +TextAsset::parse_texts(xmlpp::Element const * node, vector<ParseState>& state, optional<int> tcr, Standard standard) { if (node->get_name() == "Font") { state.push_back (font_node_state (node, standard)); @@ -366,7 +366,7 @@ SubtitleAsset::parse_subtitles (xmlpp::Element const * node, vector<ParseState>& /* Handle actual content e.g. text */ auto const v = dynamic_cast<xmlpp::ContentNode const *>(i); if (v) { - maybe_add_subtitle (v->get_content(), state, space_before, standard, rubies); + maybe_add_text(v->get_content(), state, space_before, standard, rubies); space_before = 0; } @@ -383,7 +383,7 @@ SubtitleAsset::parse_subtitles (xmlpp::Element const * node, vector<ParseState>& } space_before += raw_convert<float>(size); } else if (e->get_name() != "Ruby") { - parse_subtitles (e, state, tcr, standard); + parse_texts (e, state, tcr, standard); } } } @@ -393,7 +393,7 @@ SubtitleAsset::parse_subtitles (xmlpp::Element const * node, vector<ParseState>& void -SubtitleAsset::maybe_add_subtitle( +TextAsset::maybe_add_text( string text, vector<ParseState> const & parse_state, float space_before, @@ -482,8 +482,8 @@ SubtitleAsset::maybe_add_subtitle( switch (ps.type.get()) { case ParseState::Type::TEXT: - _subtitles.push_back ( - make_shared<SubtitleString>( + _texts.push_back ( + make_shared<TextString>( ps.font_id, ps.italic.get_value_or (false), ps.bold.get_value_or (false), @@ -530,9 +530,9 @@ SubtitleAsset::maybe_add_subtitle( break; } - /* Add a subtitle with no image data and we'll fill that in later */ - _subtitles.push_back ( - make_shared<SubtitleImage>( + /* Add a text with no image data and we'll fill that in later */ + _texts.push_back( + make_shared<TextImage>( ArrayData(), text, ps.in.get(), @@ -552,22 +552,22 @@ SubtitleAsset::maybe_add_subtitle( } -vector<shared_ptr<const Subtitle>> -SubtitleAsset::subtitles () const +vector<shared_ptr<const Text>> +TextAsset::texts() const { - vector<shared_ptr<const Subtitle>> s; - for (auto i: _subtitles) { + vector<shared_ptr<const Text>> s; + for (auto i: _texts) { s.push_back (i); } return s; } -vector<shared_ptr<const Subtitle>> -SubtitleAsset::subtitles_during (Time from, Time to, bool starting) const +vector<shared_ptr<const Text>> +TextAsset::texts_during(Time from, Time to, bool starting) const { - vector<shared_ptr<const Subtitle>> s; - for (auto i: _subtitles) { + vector<shared_ptr<const Text>> s; + for (auto i: _texts) { if ((starting && from <= i->in() && i->in() < to) || (!starting && i->out() >= from && i->in() <= to)) { s.push_back (i); } @@ -578,17 +578,17 @@ SubtitleAsset::subtitles_during (Time from, Time to, bool starting) const void -SubtitleAsset::add (shared_ptr<Subtitle> s) +TextAsset::add(shared_ptr<Text> s) { - _subtitles.push_back (s); + _texts.push_back(s); } Time -SubtitleAsset::latest_subtitle_out () const +TextAsset::latest_text_out() const { Time t; - for (auto i: _subtitles) { + for (auto i: _texts) { if (i->out() > t) { t = i->out (); } @@ -599,33 +599,33 @@ SubtitleAsset::latest_subtitle_out () const bool -SubtitleAsset::equals(shared_ptr<const Asset> other_asset, EqualityOptions const& options, NoteHandler note) const +TextAsset::equals(shared_ptr<const Asset> other_asset, EqualityOptions const& options, NoteHandler note) const { if (!Asset::equals (other_asset, options, note)) { return false; } - auto other = dynamic_pointer_cast<const SubtitleAsset> (other_asset); + auto other = dynamic_pointer_cast<const TextAsset> (other_asset); if (!other) { return false; } - if (_subtitles.size() != other->_subtitles.size()) { - note (NoteType::ERROR, String::compose("different number of subtitles: %1 vs %2", _subtitles.size(), other->_subtitles.size())); + if (_texts.size() != other->_texts.size()) { + note (NoteType::ERROR, String::compose("different number of texts: %1 vs %2", _texts.size(), other->_texts.size())); return false; } - auto i = _subtitles.begin(); - auto j = other->_subtitles.begin(); + auto i = _texts.begin(); + auto j = other->_texts.begin(); - while (i != _subtitles.end()) { - auto string_i = dynamic_pointer_cast<SubtitleString> (*i); - auto string_j = dynamic_pointer_cast<SubtitleString> (*j); - auto image_i = dynamic_pointer_cast<SubtitleImage> (*i); - auto image_j = dynamic_pointer_cast<SubtitleImage> (*j); + while (i != _texts.end()) { + auto string_i = dynamic_pointer_cast<TextString>(*i); + auto string_j = dynamic_pointer_cast<TextString>(*j); + auto image_i = dynamic_pointer_cast<TextImage>(*i); + auto image_j = dynamic_pointer_cast<TextImage>(*j); if ((string_i && !string_j) || (image_i && !image_j)) { - note (NoteType::ERROR, "subtitles differ: string vs. image"); + note (NoteType::ERROR, "texts differ: string vs. image"); return false; } @@ -645,9 +645,9 @@ SubtitleAsset::equals(shared_ptr<const Asset> other_asset, EqualityOptions const } -struct SubtitleSorter +struct TextSorter { - bool operator() (shared_ptr<Subtitle> a, shared_ptr<Subtitle> b) { + bool operator()(shared_ptr<Text> a, shared_ptr<Text> b) { if (a->in() != b->in()) { return a->in() < b->in(); } @@ -660,7 +660,7 @@ struct SubtitleSorter void -SubtitleAsset::pull_fonts (shared_ptr<order::Part> part) +TextAsset::pull_fonts(shared_ptr<order::Part> part) { if (part->children.empty ()) { return; @@ -724,12 +724,12 @@ SubtitleAsset::pull_fonts (shared_ptr<order::Part> part) * class because the differences between the two are fairly subtle. */ void -SubtitleAsset::subtitles_as_xml (xmlpp::Element* xml_root, int time_code_rate, Standard standard) const +TextAsset::texts_as_xml(xmlpp::Element* xml_root, int time_code_rate, Standard standard) const { - auto sorted = _subtitles; - std::stable_sort(sorted.begin(), sorted.end(), SubtitleSorter()); + auto sorted = _texts; + std::stable_sort(sorted.begin(), sorted.end(), TextSorter()); - /* Gather our subtitles into a hierarchy of Subtitle/Text/String objects, writing + /* Gather our texts into a hierarchy of Subtitle/Text/String objects, writing font information into the bottom level (String) objects. */ @@ -766,7 +766,7 @@ SubtitleAsset::subtitles_as_xml (xmlpp::Element* xml_root, int time_code_rate, S text.reset (); } - auto is = dynamic_pointer_cast<SubtitleString>(i); + auto is = dynamic_pointer_cast<TextString>(i); if (is) { if (!text || last_h_align != is->h_align() || @@ -799,8 +799,7 @@ SubtitleAsset::subtitles_as_xml (xmlpp::Element* xml_root, int time_code_rate, S text->children.push_back (make_shared<order::String>(text, order::Font (is, standard), is->text(), is->space_before())); } - auto ii = dynamic_pointer_cast<SubtitleImage>(i); - if (ii) { + if (auto ii = dynamic_pointer_cast<TextImage>(i)) { text.reset (); subtitle->children.push_back ( make_shared<order::Image>(subtitle, ii->id(), ii->png_image(), ii->h_align(), ii->h_position(), ii->v_align(), ii->v_position(), ii->z_position()) @@ -824,7 +823,7 @@ SubtitleAsset::subtitles_as_xml (xmlpp::Element* xml_root, int time_code_rate, S map<string, ArrayData> -SubtitleAsset::font_data () const +TextAsset::font_data() const { map<string, ArrayData> out; for (auto const& i: _fonts) { @@ -835,7 +834,7 @@ SubtitleAsset::font_data () const map<string, boost::filesystem::path> -SubtitleAsset::font_filenames () const +TextAsset::font_filenames() const { map<string, boost::filesystem::path> out; for (auto const& i: _fonts) { @@ -852,7 +851,7 @@ SubtitleAsset::font_filenames () const * (see DCP-o-matic bug #1689). */ void -SubtitleAsset::fix_empty_font_ids () +TextAsset::fix_empty_font_ids() { bool have_empty = false; vector<string> ids; @@ -876,8 +875,8 @@ SubtitleAsset::fix_empty_font_ids () } } - for (auto i: _subtitles) { - auto j = dynamic_pointer_cast<SubtitleString> (i); + for (auto i: _texts) { + auto j = dynamic_pointer_cast<TextString>(i); if (j && j->font() && j->font().get() == "") { j->set_font (empty_id); } @@ -957,15 +956,15 @@ format_xml_node (xmlpp::Node const* node, State& state) /** Format XML much as write_to_string_formatted() would do, except without adding any white space - * to <Text> nodes. This is an attempt to avoid changing what is actually displayed as subtitles - * while also formatting the XML in such a way as to avoid DoM bug 2205. + * to <Text> nodes. This is an attempt to avoid changing what is actually displayed while also + * formatting the XML in such a way as to avoid DoM bug 2205. * * xml_namespace is an optional namespace for the root node; it would be nicer to set this up with * set_namespace_declaration in the caller and then to extract it here but I couldn't find a way * to get all namespaces with the libxml++ API. */ string -SubtitleAsset::format_xml(xmlpp::Document const& document, optional<pair<string, string>> xml_namespace) +TextAsset::format_xml(xmlpp::Document const& document, optional<pair<string, string>> xml_namespace) { auto root = document.get_root_node(); @@ -997,7 +996,7 @@ SubtitleAsset::format_xml(xmlpp::Document const& document, optional<pair<string, void -SubtitleAsset::ensure_font(string load_id, dcp::ArrayData data) +TextAsset::ensure_font(string load_id, dcp::ArrayData data) { if (std::find_if(_fonts.begin(), _fonts.end(), [load_id](Font const& font) { return font.load_id == load_id; }) == _fonts.end()) { add_font(load_id, data); diff --git a/src/subtitle_asset.h b/src/text_asset.h index 25758c2e..4d739027 100644 --- a/src/subtitle_asset.h +++ b/src/text_asset.h @@ -32,20 +32,20 @@ */ -/** @file src/subtitle_asset.h - * @brief SubtitleAsset class +/** @file src/text_asset.h + * @brief TextAsset class */ -#ifndef LIBDCP_SUBTITLE_ASSET_H -#define LIBDCP_SUBTITLE_ASSET_H +#ifndef LIBDCP_TEXT_ASSET_H +#define LIBDCP_TEXT_ASSET_H #include "array_data.h" #include "asset.h" #include "dcp_time.h" #include "subtitle_standard.h" -#include "subtitle_string.h" +#include "text_string.h" #include <libcxml/cxml.h> #include <boost/shared_array.hpp> #include <map> @@ -70,13 +70,13 @@ struct pull_fonts_test3; namespace dcp { -class SubtitleString; -class SubtitleImage; class FontNode; -class TextNode; -class SubtitleNode; class LoadFontNode; class ReelAsset; +class SubtitleNode; +class TextImage; +class TextNode; +class TextString; namespace order { @@ -85,19 +85,19 @@ namespace order { } -/** @class SubtitleAsset - * @brief A parent for classes representing a file containing subtitles +/** @class TextAsset + * @brief A parent for classes representing a file containing subtitles or captions * - * This class holds a list of Subtitle objects which it can extract + * This class holds a list of Text objects which it can extract * from the appropriate part of either an Interop or SMPTE XML file. - * Its subclasses InteropSubtitleAsset and SMPTESubtitleAsset handle the + * Its subclasses InteropTextAsset and SMPTETextAsset handle the * differences between the two types. */ -class SubtitleAsset : public Asset +class TextAsset : public Asset { public: - SubtitleAsset (); - explicit SubtitleAsset (boost::filesystem::path file); + TextAsset(); + explicit TextAsset(boost::filesystem::path file); bool equals ( std::shared_ptr<const Asset>, @@ -105,10 +105,10 @@ public: NoteHandler note ) const override; - std::vector<std::shared_ptr<const Subtitle>> subtitles_during (Time from, Time to, bool starting) const; - std::vector<std::shared_ptr<const Subtitle>> subtitles () const; + std::vector<std::shared_ptr<const Text>> texts_during(Time from, Time to, bool starting) const; + std::vector<std::shared_ptr<const Text>> texts() const; - virtual void add (std::shared_ptr<Subtitle>); + virtual void add(std::shared_ptr<Text>); virtual void add_font (std::string id, dcp::ArrayData data) = 0; void ensure_font(std::string id, dcp::ArrayData data); std::map<std::string, ArrayData> font_data () const; @@ -117,7 +117,7 @@ public: virtual void write (boost::filesystem::path) const = 0; virtual std::string xml_as_string () const = 0; - Time latest_subtitle_out () const; + Time latest_text_out() const; void fix_empty_font_ids (); @@ -169,7 +169,7 @@ protected: float space_before = 0; }; - void parse_subtitles (xmlpp::Element const * node, std::vector<ParseState>& state, boost::optional<int> tcr, Standard standard); + void parse_texts(xmlpp::Element const * node, std::vector<ParseState>& state, boost::optional<int> tcr, Standard standard); ParseState font_node_state (xmlpp::Element const * node, Standard standard) const; ParseState text_node_state (xmlpp::Element const * node) const; ParseState image_node_state (xmlpp::Element const * node) const; @@ -177,10 +177,10 @@ protected: Time fade_time (xmlpp::Element const * node, std::string name, boost::optional<int> tcr) const; void position_align (ParseState& ps, xmlpp::Element const * node) const; - void subtitles_as_xml (xmlpp::Element* root, int time_code_rate, Standard standard) const; + void texts_as_xml(xmlpp::Element* root, int time_code_rate, Standard standard) const; - /** All our subtitles, in no particular order */ - std::vector<std::shared_ptr<Subtitle>> _subtitles; + /** All our texts, in no particular order */ + std::vector<std::shared_ptr<Text>> _texts; class Font { @@ -216,7 +216,7 @@ private: friend struct ::pull_fonts_test2; friend struct ::pull_fonts_test3; - void maybe_add_subtitle( + void maybe_add_text( std::string text, std::vector<ParseState> const & parse_state, float space_before, diff --git a/src/subtitle_asset_internal.cc b/src/text_asset_internal.cc index 99d8411b..aba26edd 100644 --- a/src/subtitle_asset_internal.cc +++ b/src/text_asset_internal.cc @@ -32,13 +32,13 @@ */ -/** @file src/subtitle_asset_internal.cc - * @brief Internal SubtitleAsset helpers +/** @file src/text_asset_internal.cc + * @brief Internal TextAsset helpers */ -#include "subtitle_asset_internal.h" -#include "subtitle_string.h" +#include "text_asset_internal.h" +#include "text_string.h" #include "compose.hpp" #include <cmath> @@ -49,7 +49,7 @@ using std::shared_ptr; using namespace dcp; -order::Font::Font (shared_ptr<SubtitleString> s, Standard standard) +order::Font::Font(shared_ptr<TextString> s, Standard standard) { if (s->font()) { if (standard == Standard::SMPTE) { @@ -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/subtitle_asset_internal.h b/src/text_asset_internal.h index 557db2e4..ddbc8833 100644 --- a/src/subtitle_asset_internal.h +++ b/src/text_asset_internal.h @@ -32,13 +32,13 @@ */ -/** @file src/subtitle_asset_internal.h - * @brief Internal SubtitleAsset helpers +/** @file src/text_asset_internal.h + * @brief Internal TextAsset helpers */ -#ifndef LIBDCP_SUBTITLE_ASSET_INTERNAL_H -#define LIBDCP_SUBTITLE_ASSET_INTERNAL_H +#ifndef LIBDCP_TEXT_ASSET_INTERNAL_H +#define LIBDCP_TEXT_ASSET_INTERNAL_H #include "array_data.h" @@ -63,7 +63,7 @@ namespace dcp { class Ruby; -class SubtitleString; +class TextString; namespace order { @@ -82,7 +82,7 @@ class Font public: Font () {} - Font (std::shared_ptr<SubtitleString> s, Standard standard); + Font (std::shared_ptr<TextString> s, Standard standard); xmlpp::Element* as_xml (xmlpp::Element* parent, Context& context) const; diff --git a/src/subtitle_image.cc b/src/text_image.cc index 9340bc54..7a6c4222 100644 --- a/src/subtitle_image.cc +++ b/src/text_image.cc @@ -32,14 +32,14 @@ */ -/** @file src/subtitle_image.cc - * @brief SubtitleImage class +/** @file src/text_image.cc + * @brief TextImage class */ #include "equality_options.h" #include "filesystem.h" -#include "subtitle_image.h" +#include "text_image.h" #include "util.h" @@ -50,7 +50,7 @@ using std::string; using namespace dcp; -SubtitleImage::SubtitleImage ( +TextImage::TextImage( ArrayData png_image, Time in, Time out, @@ -62,7 +62,7 @@ SubtitleImage::SubtitleImage ( Time fade_up_time, Time fade_down_time ) - : Subtitle(in, out, h_position, h_align, v_position, v_align, z_position, fade_up_time, fade_down_time) + : Text(in, out, h_position, h_align, v_position, v_align, z_position, fade_up_time, fade_down_time) , _png_image (png_image) , _id (make_uuid ()) { @@ -70,7 +70,7 @@ SubtitleImage::SubtitleImage ( } -SubtitleImage::SubtitleImage ( +TextImage::TextImage( ArrayData png_image, string id, Time in, @@ -83,7 +83,7 @@ SubtitleImage::SubtitleImage ( Time fade_up_time, Time fade_down_time ) - : Subtitle(in, out, h_position, h_align, v_position, v_align, z_position, fade_up_time, fade_down_time) + : Text(in, out, h_position, h_align, v_position, v_align, z_position, fade_up_time, fade_down_time) , _png_image (png_image) , _id (id) { @@ -92,7 +92,7 @@ SubtitleImage::SubtitleImage ( void -SubtitleImage::read_png_file (boost::filesystem::path file) +TextImage::read_png_file(boost::filesystem::path file) { _file = file; _png_image = ArrayData (file); @@ -100,7 +100,7 @@ SubtitleImage::read_png_file (boost::filesystem::path file) void -SubtitleImage::write_png_file (boost::filesystem::path file) const +TextImage::write_png_file(boost::filesystem::path file) const { _file = file; png_image().write (file); @@ -108,7 +108,7 @@ SubtitleImage::write_png_file (boost::filesystem::path file) const bool -dcp::operator== (SubtitleImage const & a, SubtitleImage const & b) +dcp::operator==(TextImage const & a, TextImage const & b) { return ( a.png_image() == b.png_image() && @@ -127,36 +127,36 @@ dcp::operator== (SubtitleImage const & a, SubtitleImage const & b) bool -dcp::operator!= (SubtitleImage const & a, SubtitleImage const & b) +dcp::operator!=(TextImage const & a, TextImage const & b) { return !(a == b); } bool -SubtitleImage::equals(shared_ptr<const Subtitle> other_sub, EqualityOptions const& options, NoteHandler note) const +TextImage::equals(shared_ptr<const Text> other_sub, EqualityOptions const& options, NoteHandler note) const { - if (!Subtitle::equals(other_sub, options, note)) { + if (!Text::equals(other_sub, options, note)) { return false; } - auto other = dynamic_pointer_cast<const SubtitleImage>(other_sub); + auto other = dynamic_pointer_cast<const TextImage>(other_sub); if (!other) { - note(NoteType::ERROR, "Subtitle types differ: string vs image"); + note(NoteType::ERROR, "Text types differ: string vs image"); return false; } if (png_image() != other->png_image()) { - note (NoteType::ERROR, "subtitle image PNG data differs"); - if (options.export_differing_subtitles) { - string const base = "dcpdiff_subtitle_"; + note (NoteType::ERROR, "text image PNG data differs"); + if (options.export_differing_texts) { + string const base = "dcpdiff_text_"; if (filesystem::exists(base + "A.png")) { - note (NoteType::ERROR, "could not export subtitle as " + base + "A.png already exists"); + note (NoteType::ERROR, "could not export text as " + base + "A.png already exists"); } else { png_image().write(base + "A.png"); } if (filesystem::exists(base + "B.png")) { - note (NoteType::ERROR, "could not export subtitle as " + base + "B.png already exists"); + note (NoteType::ERROR, "could not export text as " + base + "B.png already exists"); } else { other->png_image().write(base + "B.png"); } @@ -169,13 +169,13 @@ SubtitleImage::equals(shared_ptr<const Subtitle> other_sub, EqualityOptions cons ostream& -dcp::operator<< (ostream& s, SubtitleImage const & sub) +dcp::operator<<(ostream& s, TextImage const& text) { - s << "\n[IMAGE] from " << sub.in() << " to " << sub.out() << ";\n" - << "fade up " << sub.fade_up_time() << ", fade down " << sub.fade_down_time() << ";\n" - << "v pos " << sub.v_position() << ", valign " << ((int) sub.v_align()) - << ", hpos " << sub.h_position() << ", halign " << ((int) sub.h_align()) - << ", zpos " << sub.z_position() << "\n"; + s << "\n[IMAGE] from " << text.in() << " to " << text.out() << ";\n" + << "fade up " << text.fade_up_time() << ", fade down " << text.fade_down_time() << ";\n" + << "v pos " << text.v_position() << ", valign " << ((int) text.v_align()) + << ", hpos " << text.h_position() << ", halign " << ((int) text.h_align()) + << ", zpos " << text.z_position() << "\n"; return s; } diff --git a/src/subtitle_image.h b/src/text_image.h index ae733fe4..1f1e464e 100644 --- a/src/subtitle_image.h +++ b/src/text_image.h @@ -32,17 +32,17 @@ */ -/** @file src/subtitle_image.h - * @brief SubtitleImage class +/** @file src/text_image.h + * @brief TextImage class */ -#ifndef LIBDCP_SUBTITLE_IMAGE_H -#define LIBDCP_SUBTITLE_IMAGE_H +#ifndef LIBDCP_TEXT_IMAGE_H +#define LIBDCP_TEXT_IMAGE_H #include "array_data.h" -#include "subtitle.h" +#include "text.h" #include "dcp_time.h" #include <boost/optional.hpp> #include <string> @@ -51,13 +51,13 @@ namespace dcp { -/** @class SubtitleImage - * @brief A bitmap subtitle with all the associated attributes +/** @class TextImage + * @brief A bitmap subtitle or caption with all the associated attributes */ -class SubtitleImage : public Subtitle +class TextImage : public Text { public: - SubtitleImage ( + TextImage( ArrayData png_image, Time in, Time out, @@ -70,7 +70,7 @@ public: Time fade_down_time ); - SubtitleImage ( + TextImage( ArrayData png_image, std::string id, Time in, @@ -104,7 +104,7 @@ public: return _file; } - bool equals(std::shared_ptr<const dcp::Subtitle> other_sub, EqualityOptions const& options, NoteHandler note) const override; + bool equals(std::shared_ptr<const dcp::Text> other_text, EqualityOptions const& options, NoteHandler note) const override; private: ArrayData _png_image; @@ -113,9 +113,9 @@ private: }; -bool operator== (SubtitleImage const & a, SubtitleImage const & b); -bool operator!= (SubtitleImage const & a, SubtitleImage const & b); -std::ostream& operator<< (std::ostream& s, SubtitleImage const & sub); +bool operator==(TextImage const & a, TextImage const& b); +bool operator!=(TextImage const & a, TextImage const& b); +std::ostream& operator<<(std::ostream& s, TextImage const& text); } diff --git a/src/subtitle_string.cc b/src/text_string.cc index af61d928..32f9e4ed 100644 --- a/src/subtitle_string.cc +++ b/src/text_string.cc @@ -32,13 +32,13 @@ */ -/** @file src/subtitle_string.cc - * @brief SubtitleString class +/** @file src/text_string.cc + * @brief TextString class */ #include "compose.hpp" -#include "subtitle_string.h" +#include "text_string.h" #include "xml.h" #include <cmath> @@ -54,7 +54,7 @@ using boost::optional; using namespace dcp; -SubtitleString::SubtitleString ( +TextString::TextString( optional<string> font, bool italic, bool bold, @@ -78,7 +78,7 @@ SubtitleString::SubtitleString ( float space_before, vector<Ruby> rubies ) - : Subtitle(in, out, h_position, h_align, v_position, v_align, z_position, fade_up_time, fade_down_time) + : Text(in, out, h_position, h_align, v_position, v_align, z_position, fade_up_time, fade_down_time) , _font (font) , _italic (italic) , _bold (bold) @@ -98,7 +98,7 @@ SubtitleString::SubtitleString ( float -SubtitleString::size_in_pixels (int screen_height) const +TextString::size_in_pixels(int screen_height) const { /* Size in the subtitle file is given in points as if the screen height is 11 inches, so a 72pt font would be 1/11th of the screen @@ -110,7 +110,7 @@ SubtitleString::size_in_pixels (int screen_height) const bool -dcp::operator== (SubtitleString const & a, SubtitleString const & b) +dcp::operator==(TextString const & a, TextString const & b) { return ( a.font() == b.font() && @@ -140,14 +140,14 @@ dcp::operator== (SubtitleString const & a, SubtitleString const & b) bool -dcp::operator!= (SubtitleString const & a, SubtitleString const & b) +dcp::operator!=(TextString const & a, TextString const & b) { return !(a == b); } ostream& -dcp::operator<< (ostream& s, SubtitleString const & sub) +dcp::operator<<(ostream& s, TextString const & sub) { s << "\n`" << sub.text() << "' from " << sub.in() << " to " << sub.out() << ";\n" << "fade up " << sub.fade_up_time() << ", fade down " << sub.fade_down_time() << ";\n" @@ -188,77 +188,77 @@ dcp::operator<< (ostream& s, SubtitleString const & sub) bool -SubtitleString::equals(shared_ptr<const Subtitle> other_sub, EqualityOptions const& options, NoteHandler note) const +TextString::equals(shared_ptr<const Text> other_sub, EqualityOptions const& options, NoteHandler note) const { - if (!Subtitle::equals(other_sub, options, note)) { + if (!Text::equals(other_sub, options, note)) { return false; } - auto other = dynamic_pointer_cast<const SubtitleString>(other_sub); + auto other = dynamic_pointer_cast<const TextString>(other_sub); if (!other) { - note(NoteType::ERROR, "Subtitle types differ: string vs image"); + note(NoteType::ERROR, "Text types differ: string vs image"); return false; } bool same = true; if (_font != other->_font) { - note(NoteType::ERROR, String::compose("subtitle font differs: %1 vs %2", _font.get_value_or("[none]"), other->_font.get_value_or("[none]"))); + note(NoteType::ERROR, String::compose("text font differs: %1 vs %2", _font.get_value_or("[none]"), other->_font.get_value_or("[none]"))); same = false; } if (_italic != other->_italic) { - note(NoteType::ERROR, String::compose("subtitle italic flag differs: %1 vs %2", _italic ? "true" : "false", other->_italic ? "true" : "false")); + note(NoteType::ERROR, String::compose("text italic flag differs: %1 vs %2", _italic ? "true" : "false", other->_italic ? "true" : "false")); same = false; } if (_bold != other->_bold) { - note(NoteType::ERROR, String::compose("subtitle bold flag differs: %1 vs %2", _bold ? "true" : "false", other->_bold ? "true" : "false")); + note(NoteType::ERROR, String::compose("text bold flag differs: %1 vs %2", _bold ? "true" : "false", other->_bold ? "true" : "false")); same = false; } if (_underline != other->_underline) { - note(NoteType::ERROR, String::compose("subtitle underline flag differs: %1 vs %2", _underline ? "true" : "false", other->_underline ? "true" : "false")); + note(NoteType::ERROR, String::compose("text underline flag differs: %1 vs %2", _underline ? "true" : "false", other->_underline ? "true" : "false")); same = false; } if (_colour != other->_colour) { - note(NoteType::ERROR, String::compose("subtitle colour differs: %1 vs %2", _colour.to_rgb_string(), other->_colour.to_rgb_string())); + note(NoteType::ERROR, String::compose("text colour differs: %1 vs %2", _colour.to_rgb_string(), other->_colour.to_rgb_string())); same = false; } if (_size != other->_size) { - note(NoteType::ERROR, String::compose("subtitle size differs: %1 vs %2", _size, other->_size)); + note(NoteType::ERROR, String::compose("text size differs: %1 vs %2", _size, other->_size)); same = false; } if (_aspect_adjust != other->_aspect_adjust) { - note(NoteType::ERROR, String::compose("subtitle aspect_adjust differs: %1 vs %2", _aspect_adjust, other->_aspect_adjust)); + note(NoteType::ERROR, String::compose("text aspect_adjust differs: %1 vs %2", _aspect_adjust, other->_aspect_adjust)); same = false; } if (_direction != other->_direction) { - note(NoteType::ERROR, String::compose("subtitle direction differs: %1 vs %2", direction_to_string(_direction), direction_to_string(other->_direction))); + note(NoteType::ERROR, String::compose("text direction differs: %1 vs %2", direction_to_string(_direction), direction_to_string(other->_direction))); same = false; } if (_text != other->_text) { - note(NoteType::ERROR, String::compose("subtitle text differs: %1 vs %2", _text, other->_text)); + note(NoteType::ERROR, String::compose("text text differs: %1 vs %2", _text, other->_text)); same = false; } if (_effect != other->_effect) { - note(NoteType::ERROR, String::compose("subtitle effect differs: %1 vs %2", effect_to_string(_effect), effect_to_string(other->_effect))); + note(NoteType::ERROR, String::compose("text effect differs: %1 vs %2", effect_to_string(_effect), effect_to_string(other->_effect))); same = false; } if (_effect_colour != other->_effect_colour) { - note(NoteType::ERROR, String::compose("subtitle effect colour differs: %1 vs %2", _effect_colour.to_rgb_string(), other->_effect_colour.to_rgb_string())); + note(NoteType::ERROR, String::compose("text effect colour differs: %1 vs %2", _effect_colour.to_rgb_string(), other->_effect_colour.to_rgb_string())); same = false; } if (_space_before != other->_space_before) { - note(NoteType::ERROR, String::compose("subtitle space before differs: %1 vs %2", _space_before, other->_space_before)); + note(NoteType::ERROR, String::compose("text space before differs: %1 vs %2", _space_before, other->_space_before)); same = false; } diff --git a/src/subtitle_string.h b/src/text_string.h index 1ef57ff2..f532bafe 100644 --- a/src/subtitle_string.h +++ b/src/text_string.h @@ -32,18 +32,18 @@ */ -/** @file src/subtitle_string.h - * @brief SubtitleString class +/** @file src/text_string.h + * @brief TextString class */ -#ifndef LIBDCP_SUBTITLE_STRING_H -#define LIBDCP_SUBTITLE_STRING_H +#ifndef LIBDCP_TEXT_STRING_H +#define LIBDCP_TEXT_STRING_H #include "dcp_time.h" #include "ruby.h" -#include "subtitle.h" +#include "text.h" #include <boost/optional.hpp> #include <string> @@ -51,10 +51,10 @@ namespace dcp { -/** @class SubtitleString +/** @class TextString * @brief A single line of subtitle text with all the associated attributes. */ -class SubtitleString : public Subtitle +class TextString : public Text { public: /** @param font Font ID, or empty to use the default @@ -80,7 +80,7 @@ public: * @param fade_down_time Time to fade the text out * @param space_before Space to add before this string, in ems (could be negative to remove space). */ - SubtitleString ( + TextString( boost::optional<std::string> font, bool italic, bool bold, @@ -200,7 +200,7 @@ public: _rubies = std::move(rubies); } - bool equals(std::shared_ptr<const dcp::Subtitle> other_sub, EqualityOptions const& options, NoteHandler node) const override; + bool equals(std::shared_ptr<const dcp::Text> other_sub, EqualityOptions const& options, NoteHandler node) const override; private: /** font ID */ @@ -226,9 +226,10 @@ private: std::vector<Ruby> _rubies; }; -bool operator== (SubtitleString const & a, SubtitleString const & b); -bool operator!= (SubtitleString const & a, SubtitleString const & b); -std::ostream& operator<< (std::ostream& s, SubtitleString const & sub); + +bool operator==(TextString const & a, TextString const & b); +bool operator!=(TextString const & a, TextString const & b); +std::ostream& operator<<(std::ostream& s, TextString const & sub); } diff --git a/src/text_type.h b/src/text_type.h new file mode 100644 index 00000000..9a0abb48 --- /dev/null +++ b/src/text_type.h @@ -0,0 +1,55 @@ +/* + 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_TEXT_TYPE_H +#define LIBDCP_TEXT_TYPE_H + + +namespace dcp { + + +enum class TextType +{ + OPEN_SUBTITLE, + OPEN_CAPTION, + CLOSED_SUBTITLE, + CLOSED_CAPTION +}; + + +} + + +#endif + 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/types.h b/src/types.h index 65840e6d..80ee8933 100644 --- a/src/types.h +++ b/src/types.h @@ -260,19 +260,19 @@ extern bool operator!= (Colour const & a, Colour const & b); typedef boost::function<void (NoteType, std::string)> NoteHandler; -/** Maximum absolute difference between dcp::SubtitleString::aspect_adjust values that +/** Maximum absolute difference between dcp::TextString::aspect_adjust values that * are considered equal */ constexpr float ASPECT_ADJUST_EPSILON = 1e-3; -/** Maximum absolute difference between dcp::SubtitleString alignment values that +/** Maximum absolute difference between dcp::TextString alignment values that * are considered equal. */ constexpr float ALIGN_EPSILON = 1e-3; -/** Maximum absolute difference between dcp::SubtitleString space_before values that +/** Maximum absolute difference between dcp::TextString space_before values that * are considered equal. */ constexpr float SPACE_BEFORE_EPSILON = 1e-3; diff --git a/src/verify.cc b/src/verify.cc index ec8925f2..9abda0ff 100644 --- a/src/verify.cc +++ b/src/verify.cc @@ -42,21 +42,20 @@ #include "dcp.h" #include "exceptions.h" #include "filesystem.h" -#include "interop_subtitle_asset.h" -#include "mono_picture_asset.h" -#include "mono_picture_frame.h" +#include "interop_text_asset.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" -#include "reel_interop_subtitle_asset.h" +#include "reel_interop_text_asset.h" #include "reel_markers_asset.h" #include "reel_picture_asset.h" #include "reel_sound_asset.h" -#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 "reel_smpte_text_asset.h" +#include "reel_text_asset.h" +#include "smpte_text_asset.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 +88,7 @@ using std::cout; using std::dynamic_pointer_cast; +using std::function; using std::list; using std::make_shared; using std::map; @@ -98,7 +98,6 @@ using std::shared_ptr; using std::string; using std::vector; using boost::optional; -using boost::function; using namespace dcp; @@ -294,9 +293,83 @@ parse (XercesDOMParser& parser, string xml) } +class Context +{ +public: + Context( + std::vector<VerificationNote>& notes_, + boost::filesystem::path xsd_dtd_directory_, + function<void (string, optional<boost::filesystem::path>)> stage_, + function<void (float)> progress_, + VerificationOptions options_ + ) + : notes(notes_) + , xsd_dtd_directory(xsd_dtd_directory_) + , stage(stage_) + , progress(progress_) + , options(options_) + { + + } + + Context(Context const&) = delete; + Context& operator=(Context const&) = delete; + + template<typename... Args> + void ok(dcp::VerificationNote::Code code, Args... args) + { + add_note({dcp::VerificationNote::Type::OK, code, std::forward<Args>(args)...}); + } + + template<typename... Args> + void warning(dcp::VerificationNote::Code code, Args... args) + { + add_note({dcp::VerificationNote::Type::WARNING, code, std::forward<Args>(args)...}); + } + + template<typename... Args> + void bv21_error(dcp::VerificationNote::Code code, Args... args) + { + add_note({dcp::VerificationNote::Type::BV21_ERROR, code, std::forward<Args>(args)...}); + } + + template<typename... Args> + void error(dcp::VerificationNote::Code code, Args... args) + { + add_note({dcp::VerificationNote::Type::ERROR, code, std::forward<Args>(args)...}); + } + + void add_note(dcp::VerificationNote note) + { + if (cpl) { + note.set_cpl_id(cpl->id()); + } + notes.push_back(std::move(note)); + } + + void add_note_if_not_existing(dcp::VerificationNote note) + { + if (find(notes.begin(), notes.end(), note) == notes.end()) { + add_note(note); + } + } + + std::vector<VerificationNote>& notes; + std::shared_ptr<const DCP> dcp; + std::shared_ptr<const CPL> cpl; + boost::filesystem::path xsd_dtd_directory; + function<void (string, optional<boost::filesystem::path>)> stage; + function<void (float)> progress; + VerificationOptions options; + + boost::optional<string> subtitle_language; + boost::optional<int> audio_channels; +}; + + template <class T> void -validate_xml (T xml, boost::filesystem::path xsd_dtd_directory, vector<VerificationNote>& notes) +validate_xml(Context& context, T xml) { try { XMLPlatformUtils::Initialize (); @@ -349,7 +422,7 @@ validate_xml (T xml, boost::filesystem::path xsd_dtd_directory, vector<Verificat parser.setValidationSchemaFullChecking(true); parser.setErrorHandler(&error_handler); - LocalFileResolver resolver (xsd_dtd_directory); + LocalFileResolver resolver(context.xsd_dtd_directory); parser.setEntityResolver(&resolver); try { @@ -367,13 +440,12 @@ validate_xml (T xml, boost::filesystem::path xsd_dtd_directory, vector<Verificat XMLPlatformUtils::Terminate (); for (auto i: error_handler.errors()) { - notes.push_back ({ - VerificationNote::Type::ERROR, + context.error( VerificationNote::Code::INVALID_XML, i.message(), boost::trim_copy(i.public_id() + " " + i.system_id()), i.line() - }); + ); } } @@ -387,9 +459,8 @@ enum class VerifyAssetResult { static VerifyAssetResult verify_asset( - shared_ptr<const DCP> dcp, + Context& context, shared_ptr<const ReelFileAsset> reel_file_asset, - function<void (float)> progress, string* reference_hash, string* calculated_hash ) @@ -403,11 +474,11 @@ verify_asset( * call to hash(). */ reel_file_asset->asset_ref()->unset_hash(); - *calculated_hash = reel_file_asset->asset_ref()->hash([progress](int64_t done, int64_t total) { - progress(float(done) / total); + *calculated_hash = reel_file_asset->asset_ref()->hash([&context](int64_t done, int64_t total) { + context.progress(float(done) / total); }); - auto pkls = dcp->pkls(); + auto pkls = context.dcp->pkls(); /* We've read this DCP in so it must have at least one PKL */ DCP_ASSERT (!pkls.empty()); @@ -437,103 +508,106 @@ verify_asset( } -void -verify_language_tag (string tag, vector<VerificationNote>& notes) +static void +verify_language_tag(Context& context, string tag) { try { LanguageTag test (tag); } catch (LanguageTagError &) { - notes.push_back ({VerificationNote::Type::BV21_ERROR, VerificationNote::Code::INVALID_LANGUAGE, tag}); + context.bv21_error(VerificationNote::Code::INVALID_LANGUAGE, tag); } } static void -verify_picture_asset(shared_ptr<const ReelFileAsset> reel_file_asset, boost::filesystem::path file, int64_t start_frame, vector<VerificationNote>& notes, function<void (float)> progress) +verify_picture_asset( + Context& context, + shared_ptr<const ReelFileAsset> reel_file_asset, + boost::filesystem::path file, + int64_t start_frame + ) { - auto asset = dynamic_pointer_cast<PictureAsset>(reel_file_asset->asset_ref().asset()); + auto asset = dynamic_pointer_cast<J2KPictureAsset>(reel_file_asset->asset_ref().asset()); auto const duration = asset->intrinsic_duration (); - auto check_and_add = [¬es](vector<VerificationNote> const& j2k_notes) { + auto check_and_add = [&context](vector<VerificationNote> const& j2k_notes) { for (auto i: j2k_notes) { - if (find(notes.begin(), notes.end(), i) == notes.end()) { - notes.push_back (i); - } + context.add_note_if_not_existing(i); } }; int const max_frame = rint(250 * 1000000 / (8 * asset->edit_rate().as_float())); int const risky_frame = rint(230 * 1000000 / (8 * asset->edit_rate().as_float())); - auto check_frame_size = [max_frame, risky_frame, file, start_frame](int index, int size, int frame_rate, vector<VerificationNote>& notes) { + bool any_bad_frames_seen = false; + + auto check_frame_size = [max_frame, risky_frame, file, start_frame, &any_bad_frames_seen](Context& context, int index, int size, int frame_rate) { if (size > max_frame) { - notes.push_back( + context.add_note( VerificationNote( VerificationNote::Type::ERROR, VerificationNote::Code::INVALID_PICTURE_FRAME_SIZE_IN_BYTES, file ).set_frame(start_frame + index).set_frame_rate(frame_rate) ); + any_bad_frames_seen = true; } else if (size > risky_frame) { - notes.push_back( + context.add_note( VerificationNote( VerificationNote::Type::WARNING, VerificationNote::Code::NEARLY_INVALID_PICTURE_FRAME_SIZE_IN_BYTES, file ).set_frame(start_frame + index).set_frame_rate(frame_rate) ); + any_bad_frames_seen = true; } }; - if (auto mono_asset = dynamic_pointer_cast<MonoPictureAsset>(reel_file_asset->asset_ref().asset())) { + if (auto mono_asset = dynamic_pointer_cast<MonoJ2KPictureAsset>(reel_file_asset->asset_ref().asset())) { auto reader = mono_asset->start_read (); for (int64_t i = 0; i < duration; ++i) { auto frame = reader->get_frame (i); - check_frame_size(i, frame->size(), mono_asset->frame_rate().numerator, notes); + check_frame_size(context, i, frame->size(), mono_asset->frame_rate().numerator); if (!mono_asset->encrypted() || mono_asset->key()) { vector<VerificationNote> j2k_notes; verify_j2k(frame, start_frame, i, mono_asset->frame_rate().numerator, j2k_notes); check_and_add (j2k_notes); } - progress (float(i) / duration); + context.progress(float(i) / duration); } - } else if (auto stereo_asset = dynamic_pointer_cast<StereoPictureAsset>(asset)) { + } else if (auto stereo_asset = dynamic_pointer_cast<StereoJ2KPictureAsset>(asset)) { auto reader = stereo_asset->start_read (); for (int64_t i = 0; i < duration; ++i) { auto frame = reader->get_frame (i); - check_frame_size(i, frame->left()->size(), stereo_asset->frame_rate().numerator, notes); - check_frame_size(i, frame->right()->size(), stereo_asset->frame_rate().numerator, notes); + check_frame_size(context, i, frame->left()->size(), stereo_asset->frame_rate().numerator); + check_frame_size(context, i, frame->right()->size(), stereo_asset->frame_rate().numerator); if (!stereo_asset->encrypted() || stereo_asset->key()) { vector<VerificationNote> j2k_notes; verify_j2k(frame->left(), start_frame, i, stereo_asset->frame_rate().numerator, j2k_notes); verify_j2k(frame->right(), start_frame, i, stereo_asset->frame_rate().numerator, j2k_notes); check_and_add (j2k_notes); } - progress (float(i) / duration); + context.progress(float(i) / duration); } } + + if (!any_bad_frames_seen) { + context.ok(VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, file); + } } static void -verify_main_picture_asset ( - shared_ptr<const DCP> dcp, - shared_ptr<const ReelPictureAsset> reel_asset, - int64_t start_frame, - function<void (string, optional<boost::filesystem::path>)> stage, - function<void (float)> progress, - VerificationOptions options, - vector<VerificationNote>& notes - ) +verify_main_picture_asset(Context& context, shared_ptr<const ReelPictureAsset> reel_asset, int64_t start_frame) { auto asset = reel_asset->asset(); auto const file = *asset->file(); - if (options.check_asset_hashes && (!options.maximum_asset_size_for_hash_check || filesystem::file_size(file) < *options.maximum_asset_size_for_hash_check)) { - stage ("Checking picture asset hash", file); + if (context.options.check_asset_hashes && (!context.options.maximum_asset_size_for_hash_check || filesystem::file_size(file) < *context.options.maximum_asset_size_for_hash_check)) { + context.stage("Checking picture asset hash", file); string reference_hash; string calculated_hash; - auto const r = verify_asset(dcp, reel_asset, progress, &reference_hash, &calculated_hash); + auto const r = verify_asset(context, reel_asset, &reference_hash, &calculated_hash); switch (r) { case VerifyAssetResult::BAD: - notes.push_back( + context.add_note( dcp::VerificationNote( VerificationNote::Type::ERROR, VerificationNote::Code::INCORRECT_PICTURE_HASH, @@ -542,17 +616,16 @@ verify_main_picture_asset ( ); break; case VerifyAssetResult::CPL_PKL_DIFFER: - notes.push_back ({ - VerificationNote::Type::ERROR, VerificationNote::Code::MISMATCHED_PICTURE_HASHES, file - }); + context.error(VerificationNote::Code::MISMATCHED_PICTURE_HASHES, file); break; default: + context.ok(VerificationNote::Code::CORRECT_PICTURE_HASH, file); break; } } - stage ("Checking picture frame sizes", asset->file()); - verify_picture_asset(reel_asset, file, start_frame, notes, progress); + context.stage("Checking picture frame sizes", asset->file()); + verify_picture_asset(context, reel_asset, file, start_frame); /* Only flat/scope allowed by Bv2.1 */ if ( @@ -560,12 +633,7 @@ verify_main_picture_asset ( asset->size() != Size(1998, 1080) && asset->size() != Size(4096, 1716) && asset->size() != Size(3996, 2160)) { - notes.push_back({ - VerificationNote::Type::BV21_ERROR, - VerificationNote::Code::INVALID_PICTURE_SIZE_IN_PIXELS, - String::compose("%1x%2", asset->size().width, asset->size().height), - file - }); + context.bv21_error(VerificationNote::Code::INVALID_PICTURE_SIZE_IN_PIXELS, String::compose("%1x%2", asset->size().width, asset->size().height), file); } /* Only 24, 25, 48fps allowed for 2K */ @@ -573,69 +641,50 @@ verify_main_picture_asset ( (asset->size() == Size(2048, 858) || asset->size() == Size(1998, 1080)) && (asset->edit_rate() != Fraction(24, 1) && asset->edit_rate() != Fraction(25, 1) && asset->edit_rate() != Fraction(48, 1)) ) { - notes.push_back({ - VerificationNote::Type::BV21_ERROR, + context.bv21_error( VerificationNote::Code::INVALID_PICTURE_FRAME_RATE_FOR_2K, String::compose("%1/%2", asset->edit_rate().numerator, asset->edit_rate().denominator), file - }); + ); } if (asset->size() == Size(4096, 1716) || asset->size() == Size(3996, 2160)) { /* Only 24fps allowed for 4K */ if (asset->edit_rate() != Fraction(24, 1)) { - notes.push_back({ - VerificationNote::Type::BV21_ERROR, + context.bv21_error( VerificationNote::Code::INVALID_PICTURE_FRAME_RATE_FOR_4K, String::compose("%1/%2", asset->edit_rate().numerator, asset->edit_rate().denominator), file - }); + ); } /* Only 2D allowed for 4K */ - if (dynamic_pointer_cast<const StereoPictureAsset>(asset)) { - notes.push_back({ - VerificationNote::Type::BV21_ERROR, + if (dynamic_pointer_cast<const StereoJ2KPictureAsset>(asset)) { + context.bv21_error( VerificationNote::Code::INVALID_PICTURE_ASSET_RESOLUTION_FOR_3D, String::compose("%1/%2", asset->edit_rate().numerator, asset->edit_rate().denominator), file - }); + ); } } - } -struct State -{ - boost::optional<string> subtitle_language; - boost::optional<int> audio_channels; -}; - - static void -verify_main_sound_asset ( - shared_ptr<const DCP> dcp, - shared_ptr<const ReelSoundAsset> reel_asset, - function<void (string, optional<boost::filesystem::path>)> stage, - function<void (float)> progress, - VerificationOptions options, - vector<VerificationNote>& notes, - State& state - ) +verify_main_sound_asset(Context& context, shared_ptr<const ReelSoundAsset> reel_asset) { auto asset = reel_asset->asset(); auto const file = *asset->file(); - if (options.check_asset_hashes && (!options.maximum_asset_size_for_hash_check || filesystem::file_size(file) < *options.maximum_asset_size_for_hash_check)) { - stage("Checking sound asset hash", file); + if (context.options.check_asset_hashes && (!context.options.maximum_asset_size_for_hash_check || filesystem::file_size(file) < *context.options.maximum_asset_size_for_hash_check)) { + context.stage("Checking sound asset hash", file); string reference_hash; string calculated_hash; - auto const r = verify_asset(dcp, reel_asset, progress, &reference_hash, &calculated_hash); + auto const r = verify_asset(context, reel_asset, &reference_hash, &calculated_hash); switch (r) { case VerifyAssetResult::BAD: - notes.push_back( + context.add_note( dcp::VerificationNote( VerificationNote::Type::ERROR, VerificationNote::Code::INCORRECT_SOUND_HASH, @@ -644,58 +693,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 ReelTextAsset> 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 ReelTextAsset> 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 +755,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 ( - shared_ptr<const SMPTESubtitleAsset> asset, - optional<int64_t> reel_asset_duration, - vector<VerificationNote>& notes + Context& context, + shared_ptr<const SMPTETextAsset> asset, + 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 +780,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 InteropTextAsset> asset) { - if (asset->subtitles().empty()) { - notes.push_back({VerificationNote::Type::ERROR, VerificationNote::Code::MISSING_SUBTITLE, asset->id(), asset->file().get() }); + if (asset->texts().empty()) { + 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 SMPTETextAsset> 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 +829,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 +846,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,26 +854,19 @@ 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 TextAsset> asset, optional<int64_t> reel_asset_duration) { - stage ("Checking subtitle XML", asset->file()); - /* Note: we must not use SubtitleAsset::xml_as_string() here as that will mean the data on disk + context.stage("Checking subtitle XML", asset->file()); + /* Note: we must not use TextAsset::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) { + auto namespace_count = [](shared_ptr<const TextAsset> asset, string root_node) { cxml::Document doc(root_node); doc.read_string(asset->raw_xml().get()); auto root = dynamic_cast<xmlpp::Element*>(doc.node())->cobj(); @@ -840,21 +877,21 @@ verify_subtitle_asset ( return count; }; - auto interop = dynamic_pointer_cast<const InteropSubtitleAsset>(asset); + auto interop = dynamic_pointer_cast<const InteropTextAsset>(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); + auto smpte = dynamic_pointer_cast<const SMPTETextAsset>(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 +900,33 @@ verify_subtitle_asset ( /** Verify all closed caption stuff */ static void verify_closed_caption_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 + Context& context, + shared_ptr<const TextAsset> asset, + optional<int64_t> reel_asset_duration ) { - 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 + context.stage("Checking closed caption XML", asset->file()); + /* Note: we must not use TextAsset::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); + auto interop = dynamic_pointer_cast<const InteropTextAsset>(asset); if (interop) { - verify_interop_text_asset(interop, notes); + verify_interop_text_asset(context, interop); } - auto smpte = dynamic_pointer_cast<const SMPTESubtitleAsset>(asset); + auto smpte = dynamic_pointer_cast<const SMPTETextAsset>(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 +935,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 +1031,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 +1042,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 +1065,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 +1075,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 +1168,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,22 +1191,18 @@ 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); } } void dcp::verify_text_lines_and_characters( - shared_ptr<const SubtitleAsset> asset, + shared_ptr<const TextAsset> asset, int warning_length, int error_length, LinesCharactersResult* result @@ -1213,7 +1230,7 @@ dcp::verify_text_lines_and_characters( vector<shared_ptr<Event>> events; - auto position = [](shared_ptr<const SubtitleString> sub) { + auto position = [](shared_ptr<const TextString> sub) { switch (sub->v_align()) { case VAlign::TOP: return lrintf(sub->v_position() * 100); @@ -1227,8 +1244,8 @@ dcp::verify_text_lines_and_characters( }; /* Make a list of "subtitle starts" and "subtitle ends" events */ - for (auto j: asset->subtitles()) { - auto text = dynamic_pointer_cast<const SubtitleString>(j); + for (auto j: asset->texts()) { + auto text = dynamic_pointer_cast<const TextString>(j); if (text) { auto in = make_shared<Event>(text->in(), position(text), text->text().length()); events.push_back(in); @@ -1284,14 +1301,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 +1325,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 +1341,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 +1396,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 +1431,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 +1444,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 +1478,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 +1505,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 +1519,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 +1530,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 +1545,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 +1571,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 +1649,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 +1666,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 +1708,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 +1763,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 +1781,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 +1809,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 +1834,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 +1867,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 +1876,25 @@ dcp::verify ( for (auto dcp: dcps) { stage ("Checking DCP", dcp->directory()); + + context.dcp = dcp; + bool carry_on = true; try { dcp->read (¬es, true); } catch (MissingAssetmapError& e) { - notes.push_back ({VerificationNote::Type::ERROR, VerificationNote::Code::FAILED_READ, string(e.what())}); + context.error(VerificationNote::Code::FAILED_READ, string(e.what())); carry_on = false; } catch (ReadError& e) { - notes.push_back ({VerificationNote::Type::ERROR, VerificationNote::Code::FAILED_READ, string(e.what())}); + context.error(VerificationNote::Code::FAILED_READ, string(e.what())); } catch (XMLError& e) { - notes.push_back ({VerificationNote::Type::ERROR, VerificationNote::Code::FAILED_READ, string(e.what())}); + context.error(VerificationNote::Code::FAILED_READ, string(e.what())); } catch (MXFFileError& e) { - notes.push_back ({VerificationNote::Type::ERROR, VerificationNote::Code::FAILED_READ, string(e.what())}); + context.error(VerificationNote::Code::FAILED_READ, string(e.what())); } catch (BadURNUUIDError& e) { - notes.push_back({VerificationNote::Type::ERROR, VerificationNote::Code::FAILED_READ, string(e.what())}); + context.error(VerificationNote::Code::FAILED_READ, string(e.what())); } catch (cxml::Error& e) { - notes.push_back ({VerificationNote::Type::ERROR, VerificationNote::Code::FAILED_READ, string(e.what())}); + context.error(VerificationNote::Code::FAILED_READ, string(e.what())); } if (!carry_on) { @@ -1906,16 +1911,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 +1921,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 +1949,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 +2232,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..01ea0efe 100644 --- a/src/verify.h +++ b/src/verify.h @@ -58,19 +58,21 @@ namespace dcp { -class SubtitleAsset; +class DCP; +class TextAsset; 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); @@ -687,7 +728,7 @@ struct LinesCharactersResult extern void verify_text_lines_and_characters( - std::shared_ptr<const dcp::SubtitleAsset> asset, + std::shared_ptr<const dcp::TextAsset> asset, int warning_length, int error_length, dcp::LinesCharactersResult* result 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..bcbda1ff --- /dev/null +++ b/src/verify_report.cc @@ -0,0 +1,151 @@ +/* + 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_text_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()); + switch (note.type()) { + case dcp::VerificationNote::Type::OK: + formatter.list_item(note_as_string, string("ok")); + break; + case dcp::VerificationNote::Type::WARNING: + formatter.list_item(note_as_string, string("warning")); + break; + case dcp::VerificationNote::Type::ERROR: + formatter.list_item(note_as_string, string("error")); + break; + case dcp::VerificationNote::Type::BV21_ERROR: + formatter.list_item(note_as_string, string("bv21-error")); + break; + } + } + } + }; + + 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..a8ea4152 --- /dev/null +++ b/src/verify_report.h @@ -0,0 +1,240 @@ +/* + 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" + "li.bv21-error {\n" + " background-color: #ff6666;\n" + "}\n" + "ul {\n" + " list-style: none;\n" + "}\n" + ); + return html; + } + + Wrap body() override { + return wrapped("body"); + } + + Wrap unordered_list() override { + return wrapped("ul"); + } + + void list_item(std::string const& text, boost::optional<std::string> type = {}) override { + if (type) { + _file.puts(dcp::String::compose("<li class=\"%1\">%2", *type, text).c_str()); + } else { + _file.puts(dcp::String::compose("<li>%1", text).c_str()); + } + } + + std::function<std::string (std::string)> process_string() override { + return [](std::string s) { + boost::replace_all(s, "<", "<"); + boost::replace_all(s, ">", ">"); + return s; + }; + } + + std::function<std::string (std::string)> process_filename() override { + return [](std::string s) { + return String::compose("<code>%1</code>", s); + }; + } + +private: + void tagged(std::string tag, std::string content) { + _file.puts(String::compose("<%1>%2</%3>\n", tag, content, tag).c_str()); + }; + + Wrap wrapped(std::string const& tag) { + _file.puts(String::compose("<%1>", tag).c_str()); + return Wrap(this, String::compose("</%1>", tag)); + }; +}; + + +extern void verify_report(dcp::VerificationResult const& result, Formatter& formatter); + + +} + diff --git a/src/warnings.h b/src/warnings.h index a3448d9c..7a19b902 100644 --- a/src/warnings.h +++ b/src/warnings.h @@ -43,7 +43,7 @@ _Pragma("GCC diagnostic ignored \"-Woverloaded-virtual\"") \ _Pragma("GCC diagnostic ignored \"-Wtautological-overlap-compare\"") #else -#if __GNUC__ >= 9 +#if __GNUC__ >= 14 #define LIBDCP_DISABLE_WARNINGS \ _Pragma("GCC diagnostic push") \ _Pragma("GCC diagnostic ignored \"-Wdeprecated-declarations\"") \ @@ -54,6 +54,21 @@ _Pragma("GCC diagnostic ignored \"-Wdeprecated-copy\"") \ _Pragma("GCC diagnostic ignored \"-Wsuggest-override\"") \ _Pragma("GCC diagnostic ignored \"-Wunused-function\"") \ + _Pragma("GCC diagnostic ignored \"-Woverloaded-virtual\"") \ + _Pragma("GCC diagnostic ignored \"-Wignored-qualifiers\"") \ + _Pragma("GCC diagnostic ignored \"-Wtemplate-id-cdtor\"") +#elif __GNUC__ >= 9 +#define LIBDCP_DISABLE_WARNINGS \ + _Pragma("GCC diagnostic push") \ + _Pragma("GCC diagnostic ignored \"-Wdeprecated-declarations\"") \ + _Pragma("GCC diagnostic ignored \"-Wmissing-field-initializers\"") \ + _Pragma("GCC diagnostic ignored \"-Wunused-parameter\"") \ + _Pragma("GCC diagnostic ignored \"-Waddress\"") \ + _Pragma("GCC diagnostic ignored \"-Wparentheses\"") \ + _Pragma("GCC diagnostic ignored \"-Wdeprecated-copy\"") \ + _Pragma("GCC diagnostic ignored \"-Wsuggest-override\"") \ + _Pragma("GCC diagnostic ignored \"-Wunused-function\"") \ + _Pragma("GCC diagnostic ignored \"-Woverloaded-virtual\"") \ _Pragma("GCC diagnostic ignored \"-Wignored-qualifiers\"") #elif __GNUC__ >= 5 #define LIBDCP_DISABLE_WARNINGS \ diff --git a/src/wscript b/src/wscript index c2d499c8..1f0190fc 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,13 +58,16 @@ def build(bld): exceptions.cc file.cc filesystem.cc + ffmpeg_image.cc font_asset.cc fsk.cc gamma_transfer_function.cc h_align.cc identity_transfer_function.cc interop_load_font_node.cc - interop_subtitle_asset.cc + interop_text_asset.cc + j2k_picture_asset.cc + j2k_picture_asset_writer.cc j2k_transcode.cc key.cc language_tag.cc @@ -72,52 +75,54 @@ 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 reel.cc reel_asset.cc reel_atmos_asset.cc - reel_closed_caption_asset.cc reel_file_asset.cc - reel_interop_closed_caption_asset.cc - reel_interop_subtitle_asset.cc + reel_interop_text_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_smpte_text_asset.cc reel_sound_asset.cc reel_stereo_picture_asset.cc - reel_subtitle_asset.cc + reel_text_asset.cc ref.cc rgb_xyz.cc ruby.cc s_gamut3_transfer_function.cc search.cc smpte_load_font_node.cc - smpte_subtitle_asset.cc + smpte_text_asset.cc sound_asset.cc sound_asset_writer.cc sound_frame.cc - stereo_picture_asset.cc - stereo_picture_asset_writer.cc - stereo_picture_frame.cc - subtitle.cc - subtitle_asset.cc - subtitle_asset_internal.cc - subtitle_image.cc + stereo_j2k_picture_asset.cc + stereo_j2k_picture_asset_writer.cc + stereo_j2k_picture_frame.cc + text_asset.cc + text_asset_internal.cc + text.cc + text_image.cc subtitle_standard.cc - subtitle_string.cc + text_string.cc transfer_function.cc types.cc utc_offset.cc @@ -125,6 +130,7 @@ def build(bld): v_align.cc verify.cc verify_j2k.cc + verify_report.cc version.cc """ @@ -139,8 +145,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 +168,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 + interop_text_asset.h + ffmpeg_image.h + j2k_picture_asset.h + j2k_picture_asset_writer.h j2k_transcode.h key.h language_tag.h @@ -174,57 +185,61 @@ 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 - reel_closed_caption_asset.h reel_file_asset.h - reel_interop_closed_caption_asset.h - reel_interop_subtitle_asset.h + reel_interop_text_asset.h reel_markers_asset.h reel_mono_picture_asset.h reel_picture_asset.h + reel_smpte_text_asset.h reel_sound_asset.h - reel_smpte_closed_caption_asset.h - reel_smpte_subtitle_asset.h reel_stereo_picture_asset.h - reel_subtitle_asset.h + reel_text_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 + smpte_text_asset.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 - subtitle.h - subtitle_asset.h - subtitle_image.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 + text.h + text_asset.h + text_image.h subtitle_standard.h - subtitle_string.h + text_string.h + text_type.h transfer_function.h types.h utc_offset.h @@ -232,6 +247,7 @@ def build(bld): v_align.h verify.h verify_j2k.h + verify_report.h version.h warnings.h """ @@ -244,7 +260,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 +272,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'] |
