diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/argb_frame.cc | 8 | ||||
| -rw-r--r-- | src/argb_frame.h | 18 | ||||
| -rw-r--r-- | src/asset.cc | 138 | ||||
| -rw-r--r-- | src/asset.h | 149 | ||||
| -rw-r--r-- | src/certificates.cc | 57 | ||||
| -rw-r--r-- | src/certificates.h | 31 | ||||
| -rw-r--r-- | src/colour_matrix.cc | 8 | ||||
| -rw-r--r-- | src/colour_matrix.h | 4 | ||||
| -rw-r--r-- | src/content.cc | 70 | ||||
| -rw-r--r-- | src/content.h | 83 | ||||
| -rw-r--r-- | src/cpl.cc | 331 | ||||
| -rw-r--r-- | src/cpl.h | 127 | ||||
| -rw-r--r-- | src/dcp.cc | 468 | ||||
| -rw-r--r-- | src/dcp.h | 101 | ||||
| -rw-r--r-- | src/dcp_time.cc | 36 | ||||
| -rw-r--r-- | src/dcp_time.h | 8 | ||||
| -rw-r--r-- | src/exceptions.cc | 14 | ||||
| -rw-r--r-- | src/exceptions.h | 52 | ||||
| -rw-r--r-- | src/file.cc | 46 | ||||
| -rw-r--r-- | src/file.h (renamed from src/rec709_linearised_gamma_lut.cc) | 45 | ||||
| -rw-r--r-- | src/font.cc (renamed from src/parse/subtitle.cc) | 84 | ||||
| -rw-r--r-- | src/font.h (renamed from src/parse/subtitle.h) | 65 | ||||
| -rw-r--r-- | src/gamma_lut.cc | 31 | ||||
| -rw-r--r-- | src/gamma_lut.h | 36 | ||||
| -rw-r--r-- | src/image.cc | 6 | ||||
| -rw-r--r-- | src/image.h | 4 | ||||
| -rw-r--r-- | src/kdm.cc | 58 | ||||
| -rw-r--r-- | src/kdm.h | 14 | ||||
| -rw-r--r-- | src/kdm_smpte_xml.h (renamed from src/xml/kdm_smpte.h) | 22 | ||||
| -rw-r--r-- | src/key.cc | 20 | ||||
| -rw-r--r-- | src/key.h | 9 | ||||
| -rw-r--r-- | src/load_font.cc (renamed from src/rec709_linearised_gamma_lut.h) | 18 | ||||
| -rw-r--r-- | src/load_font.h (renamed from src/srgb_linearised_gamma_lut.h) | 20 | ||||
| -rw-r--r-- | src/lut_cache.h | 26 | ||||
| -rw-r--r-- | src/metadata.cc | 16 | ||||
| -rw-r--r-- | src/metadata.h | 12 | ||||
| -rw-r--r-- | src/mono_picture_asset.cc | 200 | ||||
| -rw-r--r-- | src/mono_picture_asset.h | 64 | ||||
| -rw-r--r-- | src/mono_picture_frame.cc | 26 | ||||
| -rw-r--r-- | src/mono_picture_frame.h | 20 | ||||
| -rw-r--r-- | src/mono_picture_mxf.cc | 137 | ||||
| -rw-r--r-- | src/mono_picture_mxf.h | 62 | ||||
| -rw-r--r-- | src/mono_picture_mxf_writer.cc (renamed from src/mono_picture_asset_writer.cc) | 46 | ||||
| -rw-r--r-- | src/mono_picture_mxf_writer.h (renamed from src/mono_picture_asset_writer.h) | 31 | ||||
| -rw-r--r-- | src/mxf.cc (renamed from src/mxf_asset.cc) | 91 | ||||
| -rw-r--r-- | src/mxf.h (renamed from src/mxf_asset.h) | 81 | ||||
| -rw-r--r-- | src/mxf_writer.cc | 53 | ||||
| -rw-r--r-- | src/mxf_writer.h | 62 | ||||
| -rw-r--r-- | src/object.cc | 44 | ||||
| -rw-r--r-- | src/object.h (renamed from src/srgb_linearised_gamma_lut.cc) | 44 | ||||
| -rw-r--r-- | src/parse/asset_map.cc | 79 | ||||
| -rw-r--r-- | src/parse/asset_map.h | 81 | ||||
| -rw-r--r-- | src/parse/cpl.cc | 151 | ||||
| -rw-r--r-- | src/parse/cpl.h | 163 | ||||
| -rw-r--r-- | src/parse/pkl.cc | 52 | ||||
| -rw-r--r-- | src/picture_mxf.cc (renamed from src/picture_asset.cc) | 87 | ||||
| -rw-r--r-- | src/picture_mxf.h | 114 | ||||
| -rw-r--r-- | src/picture_mxf_writer.cc (renamed from src/picture_asset_writer.cc) | 25 | ||||
| -rw-r--r-- | src/picture_mxf_writer.h (renamed from src/picture_asset_writer.h) | 52 | ||||
| -rw-r--r-- | src/picture_mxf_writer_common.cc (renamed from src/picture_asset_writer_common.cc) | 17 | ||||
| -rw-r--r-- | src/reel.cc | 104 | ||||
| -rw-r--r-- | src/reel.h | 61 | ||||
| -rw-r--r-- | src/reel_asset.cc | 104 | ||||
| -rw-r--r-- | src/reel_asset.h | 113 | ||||
| -rw-r--r-- | src/reel_mono_picture_asset.cc | 54 | ||||
| -rw-r--r-- | src/reel_mono_picture_asset.h | 49 | ||||
| -rw-r--r-- | src/reel_picture_asset.cc | 91 | ||||
| -rw-r--r-- | src/reel_picture_asset.h | 59 | ||||
| -rw-r--r-- | src/reel_sound_asset.cc | 48 | ||||
| -rw-r--r-- | src/reel_sound_asset.h | 53 | ||||
| -rw-r--r-- | src/reel_stereo_picture_asset.cc | 67 | ||||
| -rw-r--r-- | src/reel_stereo_picture_asset.h | 51 | ||||
| -rw-r--r-- | src/reel_subtitle_asset.cc | 48 | ||||
| -rw-r--r-- | src/reel_subtitle_asset.h | 52 | ||||
| -rw-r--r-- | src/ref.h | 120 | ||||
| -rw-r--r-- | src/rgb_xyz.cc | 21 | ||||
| -rw-r--r-- | src/rgb_xyz.h | 10 | ||||
| -rw-r--r-- | src/signer.cc | 53 | ||||
| -rw-r--r-- | src/signer.h | 29 | ||||
| -rw-r--r-- | src/signer_chain.cc | 49 | ||||
| -rw-r--r-- | src/signer_chain.h | 8 | ||||
| -rw-r--r-- | src/sound_asset.cc | 401 | ||||
| -rw-r--r-- | src/sound_asset.h | 134 | ||||
| -rw-r--r-- | src/sound_frame.cc | 16 | ||||
| -rw-r--r-- | src/sound_frame.h | 20 | ||||
| -rw-r--r-- | src/sound_mxf.cc | 178 | ||||
| -rw-r--r-- | src/sound_mxf.h | 78 | ||||
| -rw-r--r-- | src/sound_mxf_writer.cc | 120 | ||||
| -rw-r--r-- | src/sound_mxf_writer.h | 55 | ||||
| -rw-r--r-- | src/stereo_picture_frame.cc | 20 | ||||
| -rw-r--r-- | src/stereo_picture_frame.h | 14 | ||||
| -rw-r--r-- | src/stereo_picture_mxf.cc (renamed from src/stereo_picture_asset.cc) | 125 | ||||
| -rw-r--r-- | src/stereo_picture_mxf.h (renamed from src/stereo_picture_asset.h) | 33 | ||||
| -rw-r--r-- | src/stereo_picture_mxf_writer.cc (renamed from src/stereo_picture_asset_writer.cc) | 54 | ||||
| -rw-r--r-- | src/stereo_picture_mxf_writer.h (renamed from src/stereo_picture_asset_writer.h) | 19 | ||||
| -rw-r--r-- | src/subtitle.cc | 60 | ||||
| -rw-r--r-- | src/subtitle.h (renamed from src/lut.h) | 62 | ||||
| -rw-r--r-- | src/subtitle_asset.h | 207 | ||||
| -rw-r--r-- | src/subtitle_content.cc (renamed from src/subtitle_asset.cc) | 275 | ||||
| -rw-r--r-- | src/subtitle_content.h | 113 | ||||
| -rw-r--r-- | src/subtitle_string.cc | 107 | ||||
| -rw-r--r-- | src/subtitle_string.h | 129 | ||||
| -rw-r--r-- | src/text.cc | 49 | ||||
| -rw-r--r-- | src/text.h (renamed from src/parse/pkl.h) | 56 | ||||
| -rw-r--r-- | src/types.cc | 54 | ||||
| -rw-r--r-- | src/types.h | 42 | ||||
| -rw-r--r-- | src/util.cc | 172 | ||||
| -rw-r--r-- | src/util.h | 39 | ||||
| -rw-r--r-- | src/version.h | 2 | ||||
| -rw-r--r-- | src/wscript | 79 | ||||
| -rw-r--r-- | src/xml.h | 6 | ||||
| -rw-r--r-- | src/xyz_frame.cc | 22 | ||||
| -rw-r--r-- | src/xyz_frame.h | 24 |
113 files changed, 4493 insertions, 3533 deletions
diff --git a/src/argb_frame.cc b/src/argb_frame.cc index a48f80bb..99d6c814 100644 --- a/src/argb_frame.cc +++ b/src/argb_frame.cc @@ -1,5 +1,5 @@ /* - Copyright (C) 2012 Carl Hetherington <cth@carlh.net> + Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -17,9 +17,13 @@ */ +/** @file src/argb_frame.cc + * @brief ARGBFrame class. + */ + #include "argb_frame.h" -using namespace libdcp; +using namespace dcp; /** Construct an empty ARGBFrame of a given size and with * undefined contents. diff --git a/src/argb_frame.h b/src/argb_frame.h index f26436cd..419227bb 100644 --- a/src/argb_frame.h +++ b/src/argb_frame.h @@ -1,5 +1,5 @@ /* - Copyright (C) 2012 Carl Hetherington <cth@carlh.net> + Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -18,13 +18,13 @@ */ /** @file src/argb_frame.h - * @brief Container for a single image from a picture asset. + * @brief ARGBFrame class. */ -#include <stdint.h> #include "util.h" +#include <stdint.h> -namespace libdcp +namespace dcp { /** @class ARGBFrame @@ -42,26 +42,28 @@ namespace libdcp * * Lines are packed so that the second row directly follows the first. */ -class ARGBFrame +class ARGBFrame : boost::noncopyable { public: ARGBFrame (Size size); ~ARGBFrame (); + /** @return pointer to the image data */ uint8_t* data () const { return _data; } - /** Length of one picture row in bytes */ + /** @return length of one picture row in bytes */ int stride () const; + /** @return size of the picture in pixels */ Size size () const { return _size; } private: - Size _size; - uint8_t* _data; + Size _size; ///< frame size in pixels + uint8_t* _data; ///< pointer to image data }; } diff --git a/src/asset.cc b/src/asset.cc index 4ad29dde..aaa79dc7 100644 --- a/src/asset.cc +++ b/src/asset.cc @@ -1,5 +1,5 @@ /* - Copyright (C) 2012 Carl Hetherington <cth@carlh.net> + Copyright (C) 2014 Carl Hetherington <cth@carlh.net> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -18,112 +18,106 @@ */ /** @file src/asset.cc - * @brief Parent class for assets of DCPs. + * @brief Asset class. */ -#include <iostream> -#include <boost/filesystem.hpp> -#include <boost/lexical_cast.hpp> -#include <boost/function.hpp> -#include <libxml++/nodes/element.h> -#include "AS_DCP.h" -#include "KM_util.h" #include "asset.h" #include "util.h" -#include "metadata.h" +#include "exceptions.h" #include "compose.hpp" +#include <libxml++/libxml++.h> +#include <boost/lexical_cast.hpp> using std::string; -using boost::shared_ptr; using boost::lexical_cast; -using namespace libdcp; - -Asset::Asset (boost::filesystem::path directory, boost::filesystem::path file_name) - : _directory (directory) - , _file_name (file_name) - , _uuid (make_uuid ()) - , _edit_rate (0) - , _entry_point (0) - , _intrinsic_duration (0) - , _duration (0) +using boost::function; +using boost::optional; +using namespace dcp; + +/** Create an Asset with a randomly-generated ID */ +Asset::Asset () { - if (_file_name.empty ()) { - _file_name = _uuid + ".xml"; - } + +} + +/** Create an Asset from a given file. The ID will + * be extracted from the file. + * @param file File name. + */ +Asset::Asset (boost::filesystem::path file) + : _file (file) +{ + +} + +/** Create an Asset with a specified ID. + * @param id ID to use. + */ +Asset::Asset (string id) + : Object (id) +{ + } void -Asset::write_to_pkl (xmlpp::Node* node, bool interop) const +Asset::write_to_pkl (xmlpp::Node* node, Standard standard) const { + assert (!_file.empty ()); + xmlpp::Node* asset = node->add_child ("Asset"); - asset->add_child("Id")->add_child_text ("urn:uuid:" + _uuid); - asset->add_child("AnnotationText")->add_child_text (_file_name.string ()); - asset->add_child("Hash")->add_child_text (digest ()); - asset->add_child("Size")->add_child_text (lexical_cast<string> (boost::filesystem::file_size(path()))); - if (interop) { - asset->add_child("Type")->add_child_text (String::compose ("application/x-smpte-mxf;asdcpKind=%1", asdcp_kind ())); - } else { - asset->add_child("Type")->add_child_text ("application/mxf"); - } + asset->add_child("Id")->add_child_text ("urn:uuid:" + _id); + asset->add_child("AnnotationText")->add_child_text (_id); + asset->add_child("Hash")->add_child_text (hash ()); + asset->add_child("Size")->add_child_text (lexical_cast<string> (boost::filesystem::file_size (_file))); + asset->add_child("Type")->add_child_text (pkl_type (standard)); } void -Asset::write_to_assetmap (xmlpp::Node* node) const +Asset::write_to_assetmap (xmlpp::Node* node, boost::filesystem::path root) const { + assert (!_file.empty ()); + xmlpp::Node* asset = node->add_child ("Asset"); - asset->add_child("Id")->add_child_text ("urn:uuid:" + _uuid); + asset->add_child("Id")->add_child_text ("urn:uuid:" + _id); xmlpp::Node* chunk_list = asset->add_child ("ChunkList"); xmlpp::Node* chunk = chunk_list->add_child ("Chunk"); - chunk->add_child("Path")->add_child_text (_file_name.string ()); + optional<boost::filesystem::path> path = relative_to_root (root, _file); + if (!path) { + throw MiscError (String::compose ("Asset %1 is not within the directory %2", _file, root)); + } + chunk->add_child("Path")->add_child_text (path.get().string ()); chunk->add_child("VolumeIndex")->add_child_text ("1"); chunk->add_child("Offset")->add_child_text ("0"); - chunk->add_child("Length")->add_child_text (lexical_cast<string> (boost::filesystem::file_size(path()))); -} - -boost::filesystem::path -Asset::path () const -{ - boost::filesystem::path p; - p /= _directory; - p /= _file_name; - return p; + chunk->add_child("Length")->add_child_text (lexical_cast<string> (boost::filesystem::file_size (_file))); } string -Asset::digest () const -{ - if (_digest.empty ()) { - _digest = make_digest (path().string(), 0); - } - - return _digest; -} - -void -Asset::compute_digest (boost::function<void (float)> progress) +Asset::hash (function<void (float)> progress) const { - if (!_digest.empty ()) { - return; + assert (!_file.empty ()); + + if (_hash.empty ()) { + _hash = make_digest (_file, progress); } - _digest = make_digest (path().string(), &progress); + return _hash; } bool -Asset::equals (shared_ptr<const Asset> other, EqualityOptions, boost::function<void (NoteType, string)> note) const +Asset::equals (boost::shared_ptr<const Asset> other, EqualityOptions, function<void (NoteType, string)> note) const { - if (_edit_rate != other->_edit_rate) { - note (ERROR, "asset edit rates differ"); + if (_hash != other->_hash) { + note (ERROR, "Asset hashes differ"); return false; } - - if (_intrinsic_duration != other->_intrinsic_duration) { - note (ERROR, "asset intrinsic durations differ"); - } - - if (_duration != other->_duration) { - note (ERROR, "asset durations differ"); - } return true; } + +void +Asset::set_file (boost::filesystem::path file) const +{ + _file = boost::filesystem::absolute (file); + _hash.clear (); +} + diff --git a/src/asset.h b/src/asset.h index 6db8e5c2..bad982f1 100644 --- a/src/asset.h +++ b/src/asset.h @@ -1,5 +1,5 @@ /* - Copyright (C) 2012 Carl Hetherington <cth@carlh.net> + Copyright (C) 2014 Carl Hetherington <cth@carlh.net> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -18,141 +18,70 @@ */ /** @file src/asset.h - * @brief Parent class for assets of DCPs. - */ + * @brief Asset class. + */ #ifndef LIBDCP_ASSET_H #define LIBDCP_ASSET_H -#include <string> -#include <list> +#include "object.h" +#include "types.h" #include <boost/filesystem.hpp> #include <boost/function.hpp> -#include <libxml++/libxml++.h> -#include "types.h" - -namespace ASDCP { - struct WriterInfo; -} +#include <boost/bind.hpp> namespace xmlpp { - class Element; + class Node; } -namespace libdcp -{ +namespace dcp { -/** @brief Parent class for assets of DCPs +/** @class Asset + * @brief Parent class for DCP assets, i.e. picture/sound/subtitles and CPLs. * - * These are collections of pictures or sound. + * Note that this class is not used for ReelAssets; those are just for the metadata + * that gets put into <Reel>s. */ -class Asset +class Asset : public Object { public: - /** Construct an Asset. - * @param directory Directory where our XML or MXF file is. - * @param file_name Name of our file within directory, or empty to make one up based on UUID. + Asset (); + Asset (boost::filesystem::path file); + Asset (std::string id); + + virtual bool equals ( + boost::shared_ptr<const Asset> other, + EqualityOptions opt, + boost::function<void (NoteType, std::string)> note + ) const; + + /** Write details of the asset to a ASSETMAP. + * @param node Parent node. */ - Asset (boost::filesystem::path directory, boost::filesystem::path file_name = ""); - - virtual ~Asset() {} - - /** Write details of the asset to a CPL AssetList node. - * @param p Parent element. - */ - virtual void write_to_cpl (xmlpp::Element* p) const = 0; + void write_to_assetmap (xmlpp::Node* node, boost::filesystem::path root) const; /** Write details of the asset to a PKL AssetList node. - * @param p Parent node. - */ - void write_to_pkl (xmlpp::Node *, bool interop) const; - - /** Write details of the asset to a ASSETMAP stream. - * @param s Stream. + * @param node Parent node. + * @param standard Standard to use. */ - void write_to_assetmap (xmlpp::Node *) const; - - /** Compute the digest for this asset. Calling this is optional: if - * it is not called, the digest will be computed when required. However, - * calling this method allows the caller to see the progress of the - * computation, which can be long for large assets. - * @param Called with progress between 0 and 1. - */ - void compute_digest (boost::function<void (float)> progress); - - std::string uuid () const { - return _uuid; - } + void write_to_pkl (xmlpp::Node* node, Standard standard) const; - boost::filesystem::path path () const; - - void set_directory (boost::filesystem::path d) { - _directory = d; - } - - void set_file_name (boost::filesystem::path f) { - _file_name = f; - } - - int entry_point () const { - return _entry_point; + boost::filesystem::path file () const { + return _file; } - int duration () const { - return _duration; - } - - int intrinsic_duration () const { - return _intrinsic_duration; - } - - int edit_rate () const { - return _edit_rate; - } - - void set_entry_point (int e) { - _entry_point = e; - } - - void set_duration (int d) { - _duration = d; - } - - void set_intrinsic_duration (int d) { - _intrinsic_duration = d; - } - - void set_edit_rate (int r) { - _edit_rate = r; - } + void set_file (boost::filesystem::path file) const; - virtual bool equals (boost::shared_ptr<const Asset> other, EqualityOptions opt, boost::function<void (NoteType, std::string)>) const; + /** @return the hash of this asset's file */ + std::string hash (boost::function<void (float)> progress = 0) const; protected: + virtual std::string pkl_type (Standard standard) const = 0; - /** @return Interop PKL asdcpKind for the <Type> tag e.g. Picture, Sound etc. */ - virtual std::string asdcp_kind () const = 0; - - std::string digest () const; - - /** Directory that our MXF or XML file is in */ - boost::filesystem::path _directory; - /** Name of our MXF or XML file */ - boost::filesystem::path _file_name; - /** Our UUID */ - std::string _uuid; - /** The edit rate; this is normally equal to the number of video frames per second */ - int _edit_rate; - /** Start point to present in frames */ - int _entry_point; - /** Total length in frames */ - int _intrinsic_duration; - /** Length to present in frames */ - int _duration; - -private: - /** Digest of our MXF or XML file */ - mutable std::string _digest; + /** The disk file that represents this asset, if one exists */ + mutable boost::filesystem::path _file; + /** Hash of _file, or empty if the hash has not yet been computed */ + mutable std::string _hash; }; } diff --git a/src/certificates.cc b/src/certificates.cc index 2f5d47fd..0a0393c6 100644 --- a/src/certificates.cc +++ b/src/certificates.cc @@ -1,5 +1,5 @@ /* - Copyright (C) 2012 Carl Hetherington <cth@carlh.net> + Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -17,27 +17,27 @@ */ -#include <sstream> -#include <vector> -#include <cerrno> -#include <boost/algorithm/string.hpp> -#include <openssl/x509.h> -#include <openssl/ssl.h> -#include <openssl/asn1.h> -#include <openssl/err.h> -#include <libxml++/nodes/element.h> +/** @file src/certificates.cc + * @brief Certificate and CertificateChain classes. + */ + #include "KM_util.h" #include "certificates.h" #include "compose.hpp" #include "exceptions.h" #include "util.h" +#include <libxml++/nodes/element.h> +#include <openssl/x509.h> +#include <openssl/ssl.h> +#include <openssl/asn1.h> +#include <openssl/err.h> +#include <boost/algorithm/string.hpp> +#include <cerrno> using std::list; using std::string; -using std::stringstream; -using std::vector; using boost::shared_ptr; -using namespace libdcp; +using namespace dcp; /** @param c X509 certificate, which this object will take ownership of */ Certificate::Certificate (X509* c) @@ -47,6 +47,9 @@ Certificate::Certificate (X509* c) } +/** Load an X509 certificate from a file. + * @param filename File to load. + */ Certificate::Certificate (boost::filesystem::path filename) : _certificate (0) , _public_key (0) @@ -61,6 +64,9 @@ Certificate::Certificate (boost::filesystem::path filename) } } +/** Load an X509 certificate from a string. + * @param cert String to read from. + */ Certificate::Certificate (string cert) : _certificate (0) , _public_key (0) @@ -68,6 +74,9 @@ Certificate::Certificate (string cert) read_string (cert); } +/** Copy constructor. + * @param other Certificate to copy. + */ Certificate::Certificate (Certificate const & other) : _certificate (0) , _public_key (0) @@ -75,6 +84,9 @@ Certificate::Certificate (Certificate const & other) read_string (other.certificate (true)); } +/** Read a certificate from a string. + * @param cert String to read. + */ void Certificate::read_string (string cert) { @@ -91,12 +103,16 @@ Certificate::read_string (string cert) BIO_free (bio); } +/** Destructor */ Certificate::~Certificate () { X509_free (_certificate); RSA_free (_public_key); } +/** operator= for Certificate. + * @param other Certificate to read from. + */ Certificate & Certificate::operator= (Certificate const & other) { @@ -114,6 +130,10 @@ Certificate::operator= (Certificate const & other) return *this; } +/** Return the certificate as a string. + * @param with_begin_end true to include the -----BEGIN CERTIFICATE--- / -----END CERTIFICATE----- markers. + * @return Certificate string. + */ string Certificate::certificate (bool with_begin_end) const { @@ -143,6 +163,10 @@ Certificate::certificate (bool with_begin_end) const return s; } +/** @return Certificate's issuer, in the form + * dnqualifier=<dnQualififer>,CN=<commonName>,OU=<organizationalUnitName>,O=<organizationName> + * and with + signs escaped to \+ + */ string Certificate::issuer () const { @@ -244,6 +268,7 @@ Certificate::thumbprint () const return Kumu::base64encode (digest, 20, digest_base64, 64); } +/** @return RSA public key from this Certificate. Caller must not free the returned value. */ RSA * Certificate::public_key () const { @@ -266,6 +291,7 @@ Certificate::public_key () const return _public_key; } +/** @return Root certificate */ shared_ptr<Certificate> CertificateChain::root () const { @@ -273,6 +299,7 @@ CertificateChain::root () const return _certificates.front (); } +/** @return Leaf certificate */ shared_ptr<Certificate> CertificateChain::leaf () const { @@ -280,6 +307,7 @@ CertificateChain::leaf () const return _certificates.back (); } +/** @return Certificates in order from leaf to root */ list<shared_ptr<Certificate> > CertificateChain::leaf_to_root () const { @@ -288,6 +316,9 @@ CertificateChain::leaf_to_root () const return c; } +/** Add a certificate to the end of the chain. + * @param c Certificate to add. + */ void CertificateChain::add (shared_ptr<Certificate> c) { diff --git a/src/certificates.h b/src/certificates.h index 2bf8d0db..96a8cbb7 100644 --- a/src/certificates.h +++ b/src/certificates.h @@ -1,5 +1,5 @@ /* - Copyright (C) 2012 Carl Hetherington <cth@carlh.net> + Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -17,16 +17,19 @@ */ +/** @file src/certificates.h + * @brief Certificate and CertificateChain classes. + */ + #ifndef LIBDCP_CERTIFICATES_H #define LIBDCP_CERTIFICATES_H -#include <string> -#include <list> -#include <boost/noncopyable.hpp> -#include <boost/shared_ptr.hpp> -#include <boost/filesystem.hpp> #undef X509_NAME #include <openssl/x509.h> +#include <boost/shared_ptr.hpp> +#include <boost/filesystem.hpp> +#include <string> +#include <list> class certificates; @@ -34,8 +37,13 @@ namespace xmlpp { class Element; } -namespace libdcp { +namespace dcp { +/** @class Certificate + * @brief A wrapper for an X509 certificate. + * + * This class can take a Certificate from a file, a string or an OpenSSL X509 object. + */ class Certificate { public: @@ -51,16 +59,12 @@ public: Certificate& operator= (Certificate const &); - /** @param with_begin_end true to include BEGIN CERTIFICATE / END CERTIFICATE markers - * @return the whole certificate as a string. - */ std::string certificate (bool with_begin_end = false) const; std::string issuer () const; std::string serial () const; std::string subject () const; std::string common_name () const; - /** @return RSA public key from this Certificate. Caller must not free the returned value. */ RSA* public_key () const; std::string thumbprint () const; @@ -76,12 +80,15 @@ private: mutable RSA* _public_key; }; +/** @class CertificateChain + * @brief A chain of any number of certificates, from root to leaf. + */ class CertificateChain { public: CertificateChain () {} - void add (boost::shared_ptr<Certificate>); + void add (boost::shared_ptr<Certificate> c); boost::shared_ptr<Certificate> root () const; boost::shared_ptr<Certificate> leaf () const; diff --git a/src/colour_matrix.cc b/src/colour_matrix.cc index b633e384..72f952c3 100644 --- a/src/colour_matrix.cc +++ b/src/colour_matrix.cc @@ -1,5 +1,5 @@ /* - Copyright (C) 2013 Carl Hetherington <cth@carlh.net> + Copyright (C) 2013-2014 Carl Hetherington <cth@carlh.net> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -23,19 +23,19 @@ EasyDCP player, I think. */ -double const libdcp::colour_matrix::xyz_to_rgb[3][3] = { +double const dcp::colour_matrix::xyz_to_rgb[3][3] = { { 3.24096989631653, -1.5373831987381, -0.498610764741898 }, { -0.96924364566803, 1.87596750259399, 0.0415550582110882 }, { 0.0556300804018974, -0.203976958990097, 1.05697154998779 } }; -double const libdcp::colour_matrix::srgb_to_xyz[3][3] = { +double const dcp::colour_matrix::srgb_to_xyz[3][3] = { {0.4124564, 0.3575761, 0.1804375}, {0.2126729, 0.7151522, 0.0721750}, {0.0193339, 0.1191920, 0.9503041} }; -double const libdcp::colour_matrix::rec709_to_xyz[3][3] = { +double const dcp::colour_matrix::rec709_to_xyz[3][3] = { { 0.412390799265959, 0.357584339383878, 0.180480788401834 }, { 0.21263900587151, 0.715168678767756, 0.0721923153607337 }, { 0.0193308187155918, 0.119194779794626, 0.950532152249661 } diff --git a/src/colour_matrix.h b/src/colour_matrix.h index 615e8b38..ce15051c 100644 --- a/src/colour_matrix.h +++ b/src/colour_matrix.h @@ -1,5 +1,5 @@ /* - Copyright (C) 2013 Carl Hetherington <cth@carlh.net> + Copyright (C) 2013-2014 Carl Hetherington <cth@carlh.net> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -17,7 +17,7 @@ */ -namespace libdcp { +namespace dcp { namespace colour_matrix { diff --git a/src/content.cc b/src/content.cc new file mode 100644 index 00000000..50db005a --- /dev/null +++ b/src/content.cc @@ -0,0 +1,70 @@ +/* + Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net> + + This program 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. + + This program 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 this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include "content.h" +#include "util.h" +#include "metadata.h" +#include "AS_DCP.h" +#include "KM_util.h" +#include <libxml++/nodes/element.h> +#include <boost/filesystem.hpp> +#include <boost/function.hpp> +#include <iostream> + +/* We need this here for the #undef ERROR for Windows */ +#include "types.h" + +using namespace std; +using namespace boost; +using namespace dcp; + +Content::Content (boost::filesystem::path file) + : Asset (file) + , _edit_rate (24, 1) + , _intrinsic_duration (0) +{ + +} + +Content::Content (Fraction edit_rate) + : _edit_rate (edit_rate) + , _intrinsic_duration (0) +{ + +} + +bool +Content::equals (shared_ptr<const Content> other, EqualityOptions opt, boost::function<void (NoteType, string)> note) const +{ + if (!Asset::equals (other, opt, note)) { + return false; + } + + if (_edit_rate != other->_edit_rate) { + note (ERROR, "content edit rates differ"); + return false; + } + + if (_intrinsic_duration != other->_intrinsic_duration) { + note (ERROR, "asset intrinsic durations differ"); + return false; + } + + return true; +} diff --git a/src/content.h b/src/content.h new file mode 100644 index 00000000..2059c8f5 --- /dev/null +++ b/src/content.h @@ -0,0 +1,83 @@ +/* + Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net> + + This program 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. + + This program 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 this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +/** @file src/content.h + * @brief Content class. + */ + +#ifndef LIBDCP_CONTENT_H +#define LIBDCP_CONTENT_H + +#include "types.h" +#include "asset.h" +#include <libxml++/libxml++.h> +#include <boost/filesystem.hpp> +#include <boost/function.hpp> +#include <string> +#include <list> + +namespace ASDCP { + class WriterInfo; +} + +namespace xmlpp { + class Element; +} + +namespace dcp +{ + +/** @class Content + * @brief An asset that represents a piece of content, i.e. picture, sound or subtitle. + * + * Such a piece of content will be contained in a file (either MXF or XML) within a DCP. + */ +class Content : public Asset +{ +public: + Content (boost::filesystem::path file); + Content (Fraction edit_rate); + virtual ~Content () {} + + bool equals ( + boost::shared_ptr<const Content> other, + EqualityOptions opt, + boost::function<void (NoteType, std::string)> + ) const; + + Fraction edit_rate () const { + return _edit_rate; + } + + int64_t intrinsic_duration () const { + return _intrinsic_duration; + } + +protected: + friend class MXFWriter; + + virtual std::string asdcp_kind () const = 0; + + Fraction _edit_rate; + int64_t _intrinsic_duration; +}; + +} + +#endif @@ -1,5 +1,5 @@ /* - Copyright (C) 2012-2013 Carl Hetherington <cth@carlh.net> + Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -17,20 +17,22 @@ */ -#include <libxml/parser.h> #include "cpl.h" -#include "parse/cpl.h" #include "util.h" -#include "mono_picture_asset.h" -#include "stereo_picture_asset.h" -#include "sound_asset.h" -#include "subtitle_asset.h" -#include "parse/asset_map.h" +#include "mono_picture_mxf.h" +#include "stereo_picture_mxf.h" +#include "sound_mxf.h" +#include "subtitle_content.h" #include "reel.h" #include "metadata.h" #include "signer.h" #include "exceptions.h" +#include "xml.h" #include "compose.hpp" +#include "reel_picture_asset.h" +#include "reel_sound_asset.h" +#include "reel_subtitle_asset.h" +#include <libxml/parser.h> using std::string; using std::stringstream; @@ -41,167 +43,78 @@ using std::make_pair; using boost::shared_ptr; using boost::lexical_cast; using boost::optional; -using namespace libdcp; +using namespace dcp; -CPL::CPL (boost::filesystem::path directory, string name, ContentKind content_kind, int length, int frames_per_second) - : _directory (directory) - , _name (name) +CPL::CPL (string annotation_text, ContentKind content_kind) + : _annotation_text (annotation_text) + /* default _content_title_text to _annotation_text */ + , _content_title_text (annotation_text) , _content_kind (content_kind) - , _length (length) - , _fps (frames_per_second) + , _content_version_id ("urn:uuid:" + make_uuid ()) { - _id = make_uuid (); + /* default _content_version_id to and _content_version_label to + a random ID and the current time. + */ + time_t now = time (0); + struct tm* tm = localtime (&now); + _content_version_id = "urn:uuid:" + make_uuid() + tm_to_string (tm); + _content_version_label_text = _content_version_id; } -/** Construct a CPL object from a XML file. - * @param directory The directory containing this CPL's DCP. - * @param file The CPL XML filename. - * @param asset_maps AssetMaps to look for assets in. - * @param require_mxfs true to throw an exception if a required MXF file does not exist. - */ -CPL::CPL (boost::filesystem::path directory, string file, list<PathAssetMap> asset_maps, bool require_mxfs) - : _directory (directory) +/** Construct a CPL object from a XML file */ +CPL::CPL (boost::filesystem::path file) + : Asset (file) , _content_kind (FEATURE) - , _length (0) - , _fps (0) { - /* Read the XML */ - shared_ptr<parse::CPL> cpl; - try { - cpl.reset (new parse::CPL (file)); - } catch (FileError& e) { - boost::throw_exception (FileError ("could not load CPL file", file, e.number ())); - } - - /* Now cherry-pick the required bits into our own data structure */ - - _name = cpl->annotation_text; - _content_kind = cpl->content_kind; - - /* Trim urn:uuid: off the front */ - _id = cpl->id.substr (9); - - for (list<shared_ptr<parse::Reel> >::iterator i = cpl->reels.begin(); i != cpl->reels.end(); ++i) { - - shared_ptr<parse::Picture> p; - - if ((*i)->asset_list->main_picture) { - p = (*i)->asset_list->main_picture; - } else { - p = (*i)->asset_list->main_stereoscopic_picture; - } - - _fps = p->edit_rate.numerator; - _length += p->duration; - - shared_ptr<PictureAsset> picture; - shared_ptr<SoundAsset> sound; - shared_ptr<SubtitleAsset> subtitle; - - /* Some rather twisted logic to decide if we are 3D or not; - some DCPs give a MainStereoscopicPicture to indicate 3D, others - just have a FrameRate twice the EditRate and apparently - expect you to divine the fact that they are hence 3D. - */ - - if (!(*i)->asset_list->main_stereoscopic_picture && p->edit_rate == p->frame_rate) { - - try { - pair<string, shared_ptr<const parse::AssetMapAsset> > asset = asset_from_id (asset_maps, p->id); - - picture.reset (new MonoPictureAsset (asset.first, asset.second->chunks.front()->path)); + cxml::Document f ("CompositionPlaylist"); + f.read_file (file); - picture->read (); - picture->set_edit_rate (_fps); - picture->set_entry_point (p->entry_point); - picture->set_duration (p->duration); - if (p->key_id.length() > 9) { - /* Trim urn:uuid: */ - picture->set_key_id (p->key_id.substr (9)); - } - } catch (MXFFileError) { - if (require_mxfs) { - throw; - } - } - - } else { - try { - pair<string, shared_ptr<const parse::AssetMapAsset> > asset = asset_from_id (asset_maps, p->id); - - picture.reset (new StereoPictureAsset (asset.first, asset.second->chunks.front()->path)); - - picture->read (); - picture->set_edit_rate (_fps); - picture->set_entry_point (p->entry_point); - picture->set_duration (p->duration); - if (p->key_id.length() > 9) { - /* Trim urn:uuid: */ - picture->set_key_id (p->key_id.substr (9)); - } - - } catch (MXFFileError) { - if (require_mxfs) { - throw; - } - } - - } - - if ((*i)->asset_list->main_sound) { - - try { - pair<string, shared_ptr<const parse::AssetMapAsset> > asset = asset_from_id (asset_maps, (*i)->asset_list->main_sound->id); - - sound.reset (new SoundAsset (asset.first, asset.second->chunks.front()->path)); - shared_ptr<parse::MainSound> s = (*i)->asset_list->main_sound; - - sound->read (); - sound->set_entry_point (s->entry_point); - sound->set_duration (s->duration); - if (s->key_id.length() > 9) { - /* Trim urn:uuid: */ - sound->set_key_id (s->key_id.substr (9)); - } - } catch (MXFFileError) { - if (require_mxfs) { - throw; - } - } - } - - if ((*i)->asset_list->main_subtitle) { - - pair<string, shared_ptr<const parse::AssetMapAsset> > asset = asset_from_id (asset_maps, (*i)->asset_list->main_subtitle->id); + _id = f.string_child ("Id"); + if (_id.length() > 9) { + _id = _id.substr (9); + } + _annotation_text = f.optional_string_child ("AnnotationText").get_value_or (""); + _metadata.issuer = f.optional_string_child ("Issuer").get_value_or (""); + _metadata.creator = f.optional_string_child ("Creator").get_value_or (""); + _metadata.issue_date = f.string_child ("IssueDate"); + _content_title_text = f.string_child ("ContentTitleText"); + _content_kind = content_kind_from_string (f.string_child ("ContentKind")); + shared_ptr<cxml::Node> content_version = f.optional_node_child ("ContentVersion"); + if (content_version) { + _content_version_id = content_version->optional_string_child ("Id").get_value_or (""); + _content_version_label_text = content_version->string_child ("LabelText"); + content_version->done (); + } + f.ignore_child ("RatingList"); + _reels = type_grand_children<Reel> (f, "ReelList", "Reel"); - subtitle.reset (new SubtitleAsset (asset.first, asset.second->chunks.front()->path)); + f.ignore_child ("Issuer"); + f.ignore_child ("Signer"); + f.ignore_child ("Signature"); - subtitle->set_entry_point ((*i)->asset_list->main_subtitle->entry_point); - subtitle->set_duration ((*i)->asset_list->main_subtitle->duration); - } - - _reels.push_back (shared_ptr<Reel> (new Reel (picture, sound, subtitle))); - } + f.done (); } +/** Add a reel to this CPL. + * @param reel Reel to add. + */ void -CPL::add_reel (shared_ptr<Reel> reel) +CPL::add (boost::shared_ptr<Reel> reel) { _reels.push_back (reel); } +/** Write an CompositonPlaylist XML file. + * @param file Filename to write. + * @param standard INTEROP or SMPTE. + * @param signer Signer to sign the CPL, or 0 to add no signature. + */ void -CPL::write_xml (bool interop, XMLMetadata const & metadata, shared_ptr<const Signer> signer) const +CPL::write_xml (boost::filesystem::path file, Standard standard, shared_ptr<const Signer> signer) const { - boost::filesystem::path p; - p /= _directory; - stringstream s; - s << _id << "_cpl.xml"; - p /= s.str(); - xmlpp::Document doc; xmlpp::Element* root; - if (interop) { + if (standard == INTEROP) { root = doc.create_root_node ("CompositionPlaylist", "http://www.digicine.com/PROTO-ASDCP-CPL-20040511#"); } else { root = doc.create_root_node ("CompositionPlaylist", "http://www.smpte-ra.org/schemas/429-7/2006/CPL"); @@ -212,90 +125,61 @@ CPL::write_xml (bool interop, XMLMetadata const & metadata, shared_ptr<const Sig } root->add_child("Id")->add_child_text ("urn:uuid:" + _id); - root->add_child("AnnotationText")->add_child_text (_name); - root->add_child("IssueDate")->add_child_text (metadata.issue_date); - root->add_child("Issuer")->add_child_text (metadata.issuer); - root->add_child("Creator")->add_child_text (metadata.creator); - root->add_child("ContentTitleText")->add_child_text (_name); + root->add_child("AnnotationText")->add_child_text (_annotation_text); + root->add_child("IssueDate")->add_child_text (_metadata.issue_date); + root->add_child("Issuer")->add_child_text (_metadata.issuer); + root->add_child("Creator")->add_child_text (_metadata.creator); + root->add_child("ContentTitleText")->add_child_text (_content_title_text); root->add_child("ContentKind")->add_child_text (content_kind_to_string (_content_kind)); { xmlpp::Node* cv = root->add_child ("ContentVersion"); - cv->add_child ("Id")->add_child_text ("urn:uri:" + _id + "_" + metadata.issue_date); - cv->add_child ("LabelText")->add_child_text (_id + "_" + metadata.issue_date); + cv->add_child ("Id")->add_child_text (_content_version_id); + cv->add_child ("LabelText")->add_child_text (_content_version_label_text); } root->add_child("RatingList"); xmlpp::Element* reel_list = root->add_child ("ReelList"); for (list<shared_ptr<Reel> >::const_iterator i = _reels.begin(); i != _reels.end(); ++i) { - (*i)->write_to_cpl (reel_list); + (*i)->write_to_cpl (reel_list, standard); } if (signer) { - signer->sign (root, interop); + signer->sign (root, standard); } /* This must not be the _formatted version otherwise signature digests will be wrong */ - doc.write_to_file (p.string (), "UTF-8"); + doc.write_to_file (file.string (), "UTF-8"); - _digest = make_digest (p.string (), 0); - _length = boost::filesystem::file_size (p.string ()); + set_file (file); } -void -CPL::write_to_pkl (xmlpp::Node* node, bool interop) const +list<shared_ptr<const Content> > +CPL::content () const { - xmlpp::Node* asset = node->add_child ("Asset"); - asset->add_child("Id")->add_child_text ("urn:uuid:" + _id); - asset->add_child("Hash")->add_child_text (_digest); - asset->add_child("Size")->add_child_text (lexical_cast<string> (_length)); - if (interop) { - asset->add_child("Type")->add_child_text ("text/xml;asdcpKind=CPL"); - } else { - asset->add_child("Type")->add_child_text ("text/xml"); - } -} + list<shared_ptr<const Content> > c; -list<shared_ptr<const Asset> > -CPL::assets () const -{ - list<shared_ptr<const Asset> > a; for (list<shared_ptr<Reel> >::const_iterator i = _reels.begin(); i != _reels.end(); ++i) { if ((*i)->main_picture ()) { - a.push_back ((*i)->main_picture ()); + c.push_back ((*i)->main_picture()->mxf ()); } if ((*i)->main_sound ()) { - a.push_back ((*i)->main_sound ()); + c.push_back ((*i)->main_sound()->mxf ()); } if ((*i)->main_subtitle ()) { - a.push_back ((*i)->main_subtitle ()); + c.push_back ((*i)->main_subtitle()->subtitle_content ()); } } - return a; -} - -void -CPL::write_to_assetmap (xmlpp::Node* node) const -{ - xmlpp::Node* asset = node->add_child ("Asset"); - asset->add_child("Id")->add_child_text ("urn:uuid:" + _id); - xmlpp::Node* chunk_list = asset->add_child ("ChunkList"); - xmlpp::Node* chunk = chunk_list->add_child ("Chunk"); - chunk->add_child("Path")->add_child_text (_id + "_cpl.xml"); - chunk->add_child("VolumeIndex")->add_child_text ("1"); - chunk->add_child("Offset")->add_child_text("0"); - chunk->add_child("Length")->add_child_text(lexical_cast<string> (_length)); + return c; } - - bool CPL::equals (CPL const & other, EqualityOptions opt, boost::function<void (NoteType, string)> note) const { - if (_name != other._name && !opt.cpl_names_can_differ) { + if (_annotation_text != other._annotation_text && !opt.cpl_annotation_texts_can_differ) { stringstream s; - s << "names differ: " << _name << " vs " << other._name << "\n"; + s << "annotation texts differ: " << _annotation_text << " vs " << other._annotation_text << "\n"; note (ERROR, s.str ()); return false; } @@ -305,16 +189,6 @@ CPL::equals (CPL const & other, EqualityOptions opt, boost::function<void (NoteT return false; } - if (_fps != other._fps) { - note (ERROR, String::compose ("frames per second differ (%1 vs %2)", _fps, other._fps)); - return false; - } - - if (_length != other._length) { - stringstream s; - note (ERROR, String::compose ("lengths differ (%1 vs %2)", _length, other._length)); - } - if (_reels.size() != other._reels.size()) { note (ERROR, String::compose ("reel counts differ (%1 vs %2)", _reels.size(), other._reels.size())); return false; @@ -347,31 +221,48 @@ CPL::encrypted () const return false; } +/** Add a KDM to this CPL. If the KDM is for any of this CPLs assets it will be used + * to decrypt those assets. + * @param kdm KDM. + */ void -CPL::add_kdm (KDM const & kdm) +CPL::add (KDM const & kdm) { for (list<shared_ptr<Reel> >::const_iterator i = _reels.begin(); i != _reels.end(); ++i) { - (*i)->add_kdm (kdm); + (*i)->add (kdm); } } -pair<string, shared_ptr<const parse::AssetMapAsset> > -CPL::asset_from_id (list<PathAssetMap> asset_maps, string id) const +/** Set a private key for every MXF referenced by this CPL. This will allow the data + * to be decrypted or encrypted. + * @param key Key to use. + */ +void +CPL::set_mxf_keys (Key key) { - for (list<PathAssetMap>::const_iterator i = asset_maps.begin(); i != asset_maps.end(); ++i) { - shared_ptr<parse::AssetMapAsset> a = i->second->asset_from_id (id); - if (a) { - return make_pair (i->first, a); - } + for (list<shared_ptr<Reel> >::const_iterator i = _reels.begin(); i != _reels.end(); ++i) { + (*i)->set_mxf_keys (key); } - - return make_pair ("", shared_ptr<const parse::AssetMapAsset> ()); } void -CPL::set_mxf_keys (Key key) +CPL::resolve_refs (list<shared_ptr<Object> > objects) { for (list<shared_ptr<Reel> >::const_iterator i = _reels.begin(); i != _reels.end(); ++i) { - (*i)->set_mxf_keys (key); + (*i)->resolve_refs (objects); } } + +string +CPL::pkl_type (Standard standard) const +{ + switch (standard) { + case INTEROP: + return "text/xml;asdcpKind=CPL"; + case SMPTE: + return "text/xml"; + default: + assert (false); + } +} + @@ -1,5 +1,5 @@ /* - Copyright (C) 2012 Carl Hetherington <cth@carlh.net> + Copyright (C) 2014 Carl Hetherington <cth@carlh.net> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -20,45 +20,66 @@ #ifndef LIBDCP_CPL_H #define LIBDCP_CPL_H -#include <list> +#include "types.h" +#include "certificates.h" +#include "key.h" +#include "asset.h" +#include "metadata.h" +#include <libxml++/libxml++.h> #include <boost/shared_ptr.hpp> #include <boost/function.hpp> #include <boost/date_time/posix_time/posix_time.hpp> #include <boost/optional.hpp> #include <boost/filesystem.hpp> -#include <libxml++/libxml++.h> -#include "types.h" -#include "certificates.h" -#include "key.h" - -namespace libdcp { +#include <list> -namespace parse { - class AssetMap; - class AssetMapAsset; -} +namespace dcp { -class Asset; +class Content; class Reel; class XMLMetadata; class MXFMetadata; class Signer; class KDM; -/** @brief A CPL within a DCP */ -class CPL +/** @class CPL + * @brief A Composition Playlist. + */ +class CPL : public Asset { public: - CPL (boost::filesystem::path directory, std::string name, ContentKind content_kind, int length, int frames_per_second); - CPL (boost::filesystem::path, std::string file, std::list<PathAssetMap> asset_maps, bool require_mxfs = true); + CPL (std::string annotation_text, ContentKind content_kind); + CPL (boost::filesystem::path file); - void add_reel (boost::shared_ptr<Reel> reel); + bool equals ( + CPL const & other, + EqualityOptions options, + boost::function<void (NoteType, std::string)> note + ) const; + + void add (boost::shared_ptr<Reel> reel); + void add (KDM const &); + + /** @return contents of the <AnnotationText> node */ + std::string annotation_text () const { + return _annotation_text; + } - /** @return the length in frames */ - int length () const { - return _length; + /** @return contents of the <ContentTitleText> node */ + std::string content_title_text () const { + return _content_title_text; + } + + /** @return contents of the <Id> node within <ContentVersion> */ + void set_content_version_id (std::string id) { + _content_version_id = id; } + /** @return contents of the <LabelText> node within <ContentVersion> */ + void set_content_version_label_text (std::string text) { + _content_version_label_text = text; + } + /** @return the type of the content, used by media servers * to categorise things (e.g. feature, trailer, etc.) */ @@ -66,59 +87,43 @@ public: return _content_kind; } + /** @return the reels in this CPL */ std::list<boost::shared_ptr<Reel> > reels () const { return _reels; } - /** @return the CPL's name, as will be presented on projector - * media servers and theatre management systems. + /** @return the Content in this CPL across all its reels + * (Content is picture, sound and subtitles) */ - std::string name () const { - return _name; - } - - /** @return the number of frames per second */ - int frames_per_second () const { - return _fps; - } - - std::list<boost::shared_ptr<const Asset> > assets () const; + std::list<boost::shared_ptr<const Content> > content () const; bool encrypted () const; void set_mxf_keys (Key); - std::string id () const { - return _id; - } - - bool equals (CPL const & other, EqualityOptions options, boost::function<void (NoteType, std::string)> note) const; - - void write_xml (bool, XMLMetadata const &, boost::shared_ptr<const Signer>) const; - void write_to_assetmap (xmlpp::Node *) const; - void write_to_pkl (xmlpp::Node *, bool) const; + void write_xml ( + boost::filesystem::path file, + Standard standard, + boost::shared_ptr<const Signer> + ) const; + + void resolve_refs (std::list<boost::shared_ptr<Object> >); + +protected: + /** @return type string for PKLs for this asset */ + std::string pkl_type (Standard standard) const; - void add_kdm (KDM const &); - private: - std::pair<std::string, boost::shared_ptr<const parse::AssetMapAsset> > asset_from_id (std::list<PathAssetMap>, std::string id) const; - - boost::filesystem::path _directory; - /** the name of the DCP */ - std::string _name; - /** the content kind of the CPL */ - ContentKind _content_kind; - /** length in frames */ - mutable int _length; - /** frames per second */ - int _fps; - /** reels */ + std::string _annotation_text; ///< <AnnotationText> + /** <Issuer>, <Creator> and <IssueDate>. These are grouped + * because they occur together in a few places. + */ + XMLMetadata _metadata; + std::string _content_title_text; ///< <ContentTitleText> + ContentKind _content_kind; ///< <ContentKind> + std::string _content_version_id; ///< <Id> in <ContentVersion> + std::string _content_version_label_text; ///< <LabelText> in <ContentVersion> std::list<boost::shared_ptr<Reel> > _reels; - - /** our UUID */ - std::string _id; - /** a SHA1 digest of our XML */ - mutable std::string _digest; }; } @@ -1,5 +1,5 @@ /* - Copyright (C) 2012 Carl Hetherington <cth@carlh.net> + Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -18,80 +18,207 @@ */ /** @file src/dcp.cc - * @brief A class to create a DCP. + * @brief DCP class. */ -#include <sstream> -#include <iomanip> -#include <cassert> -#include <iostream> -#include <boost/filesystem.hpp> -#include <boost/lexical_cast.hpp> -#include <boost/algorithm/string.hpp> -#include <boost/lexical_cast.hpp> -#include <libxml++/libxml++.h> -#include <xmlsec/xmldsig.h> -#include <xmlsec/app.h> #include "dcp.h" -#include "asset.h" -#include "sound_asset.h" -#include "picture_asset.h" -#include "subtitle_asset.h" +#include "sound_mxf.h" +#include "picture_mxf.h" +#include "subtitle_content.h" +#include "mono_picture_mxf.h" +#include "stereo_picture_mxf.h" #include "util.h" #include "metadata.h" #include "exceptions.h" -#include "parse/pkl.h" -#include "parse/asset_map.h" #include "reel.h" #include "cpl.h" #include "signer.h" #include "kdm.h" +#include "compose.hpp" +#include "AS_DCP.h" +#include <xmlsec/xmldsig.h> +#include <xmlsec/app.h> +#include <libxml++/libxml++.h> +#include <boost/filesystem.hpp> +#include <boost/algorithm/string.hpp> +#include <boost/lexical_cast.hpp> +#include <sstream> +#include <iomanip> +#include <cassert> +#include <iostream> using std::string; using std::list; using std::stringstream; using std::ostream; -using std::copy; -using std::back_inserter; using std::make_pair; +using std::map; using boost::shared_ptr; using boost::lexical_cast; -using namespace libdcp; +using namespace dcp; DCP::DCP (boost::filesystem::path directory) : _directory (directory) { boost::filesystem::create_directories (directory); + _directory = boost::filesystem::canonical (_directory); } void -DCP::write_xml (bool interop, XMLMetadata const & metadata, shared_ptr<const Signer> signer) const +DCP::read () { - for (list<shared_ptr<CPL> >::const_iterator i = _cpls.begin(); i != _cpls.end(); ++i) { - (*i)->write_xml (interop, metadata, signer); + /* Read the ASSETMAP */ + + boost::filesystem::path asset_map_file; + if (boost::filesystem::exists (_directory / "ASSETMAP")) { + asset_map_file = _directory / "ASSETMAP"; + } else if (boost::filesystem::exists (_directory / "ASSETMAP.xml")) { + asset_map_file = _directory / "ASSETMAP.xml"; + } else { + boost::throw_exception (DCPReadError ("could not find AssetMap file")); } - string pkl_uuid = make_uuid (); - string pkl_path = write_pkl (pkl_uuid, interop, metadata, signer); + cxml::Document asset_map ("AssetMap"); + asset_map.read_file (asset_map_file); + list<shared_ptr<cxml::Node> > asset_nodes = asset_map.node_child("AssetList")->node_children ("Asset"); + map<string, boost::filesystem::path> paths; + for (list<shared_ptr<cxml::Node> >::const_iterator i = asset_nodes.begin(); i != asset_nodes.end(); ++i) { + if ((*i)->node_child("ChunkList")->node_children("Chunk").size() != 1) { + boost::throw_exception (XMLError ("unsupported asset chunk count")); + } + paths.insert (make_pair ( + (*i)->string_child ("Id"), + (*i)->node_child("ChunkList")->node_child("Chunk")->string_child ("Path") + )); + } + + /* Read all the assets from the asset map */ - write_volindex (interop); - write_assetmap (pkl_uuid, boost::filesystem::file_size (pkl_path), interop, metadata); + for (map<string, boost::filesystem::path>::const_iterator i = paths.begin(); i != paths.end(); ++i) { + boost::filesystem::path path = _directory / i->second; + if (boost::algorithm::ends_with (path.string(), ".xml")) { + xmlpp::DomParser* p = new xmlpp::DomParser; + try { + p->parse_file (path.string()); + } catch (std::exception& e) { + delete p; + continue; + } + + string const root = p->get_document()->get_root_node()->get_name (); + delete p; + + if (root == "CompositionPlaylist") { + _assets.push_back (shared_ptr<CPL> (new CPL (path))); + } else if (root == "DCSubtitle") { + _assets.push_back (shared_ptr<SubtitleContent> (new SubtitleContent (path))); + } + } else if (boost::algorithm::ends_with (path.string(), ".mxf")) { + ASDCP::EssenceType_t type; + if (ASDCP::EssenceType (path.string().c_str(), type) != ASDCP::RESULT_OK) { + throw DCPReadError ("Could not find essence type"); + } + switch (type) { + case ASDCP::ESS_UNKNOWN: + case ASDCP::ESS_MPEG2_VES: + throw DCPReadError ("MPEG2 video essences are not supported"); + case ASDCP::ESS_JPEG_2000: + _assets.push_back (shared_ptr<MonoPictureMXF> (new MonoPictureMXF (path))); + break; + case ASDCP::ESS_PCM_24b_48k: + case ASDCP::ESS_PCM_24b_96k: + _assets.push_back (shared_ptr<SoundMXF> (new SoundMXF (path))); + break; + case ASDCP::ESS_JPEG_2000_S: + _assets.push_back (shared_ptr<StereoPictureMXF> (new StereoPictureMXF (path))); + break; + default: + throw DCPReadError ("Unknown MXF essence type"); + } + } + } + + list<shared_ptr<CPL> > cpl = cpls (); + for (list<shared_ptr<CPL> >::const_iterator i = cpl.begin(); i != cpl.end(); ++i) { + (*i)->resolve_refs (list_of_type<Asset, Object> (assets ())); + } } -std::string -DCP::write_pkl (string pkl_uuid, bool interop, XMLMetadata const & metadata, shared_ptr<const Signer> signer) const +bool +DCP::equals (DCP const & other, EqualityOptions opt, boost::function<void (NoteType, string)> note) const { - assert (!_cpls.empty ()); + if (_assets.size() != other._assets.size()) { + note (ERROR, "Asset counts differ"); + return false; + } + + list<shared_ptr<Asset> >::const_iterator a = _assets.begin (); + list<shared_ptr<Asset> >::const_iterator b = other._assets.begin (); + + while (a != _assets.end ()) { + if (!(*a)->equals (*b, opt, note)) { + return false; + } + ++a; + ++b; + } + + return true; +} + +void +DCP::add (boost::shared_ptr<Asset> asset) +{ + _assets.push_back (asset); +} + +class AssetComparator +{ +public: + bool operator() (shared_ptr<const Content> a, shared_ptr<const Content> b) { + return a->id() < b->id(); + } +}; + +bool +DCP::encrypted () const +{ + list<shared_ptr<CPL> > cpl = cpls (); + for (list<shared_ptr<CPL> >::const_iterator i = cpl.begin(); i != cpl.end(); ++i) { + if ((*i)->encrypted ()) { + return true; + } + } + + return false; +} + +void +DCP::add (KDM const & kdm) +{ + list<KDMKey> keys = kdm.keys (); + list<shared_ptr<CPL> > cpl = cpls (); - boost::filesystem::path p; - p /= _directory; + for (list<shared_ptr<CPL> >::iterator i = cpl.begin(); i != cpl.end(); ++i) { + for (list<KDMKey>::iterator j = keys.begin(); j != keys.end(); ++j) { + if (j->cpl_id() == (*i)->id()) { + (*i)->add (kdm); + } + } + } +} + +boost::filesystem::path +DCP::write_pkl (Standard standard, string pkl_uuid, XMLMetadata metadata, shared_ptr<const Signer> signer) const +{ + boost::filesystem::path p = _directory; stringstream s; s << pkl_uuid << "_pkl.xml"; p /= s.str(); xmlpp::Document doc; xmlpp::Element* pkl; - if (interop) { + if (standard == INTEROP) { pkl = doc.create_root_node("PackingList", "http://www.digicine.com/PROTO-ASDCP-PKL-20040311#"); } else { pkl = doc.create_root_node("PackingList", "http://www.smpte-ra.org/schemas/429-8/2007/PKL"); @@ -102,83 +229,112 @@ DCP::write_pkl (string pkl_uuid, bool interop, XMLMetadata const & metadata, sha } pkl->add_child("Id")->add_child_text ("urn:uuid:" + pkl_uuid); + /* XXX: this is a bit of a hack */ - pkl->add_child("AnnotationText")->add_child_text(_cpls.front()->name()); + assert (cpls().size() > 0); + pkl->add_child("AnnotationText")->add_child_text (cpls().front()->annotation_text ()); + pkl->add_child("IssueDate")->add_child_text (metadata.issue_date); pkl->add_child("Issuer")->add_child_text (metadata.issuer); pkl->add_child("Creator")->add_child_text (metadata.creator); xmlpp::Element* asset_list = pkl->add_child("AssetList"); - list<shared_ptr<const Asset> > a = assets (); - for (list<shared_ptr<const Asset> >::const_iterator i = a.begin(); i != a.end(); ++i) { - (*i)->write_to_pkl (asset_list, interop); - } - - for (list<shared_ptr<CPL> >::const_iterator i = _cpls.begin(); i != _cpls.end(); ++i) { - (*i)->write_to_pkl (asset_list, interop); + for (list<shared_ptr<Asset> >::const_iterator i = _assets.begin(); i != _assets.end(); ++i) { + (*i)->write_to_pkl (asset_list, standard); } if (signer) { - signer->sign (pkl, interop); + signer->sign (pkl, standard); } doc.write_to_file (p.string (), "UTF-8"); return p.string (); } +/** Write the VOLINDEX file. + * @param standard DCP standard to use (INTEROP or SMPTE) + */ void -DCP::write_volindex (bool interop) const +DCP::write_volindex (Standard standard) const { - boost::filesystem::path p; - p /= _directory; - if (interop) { + boost::filesystem::path p = _directory; + switch (standard) { + case INTEROP: p /= "VOLINDEX"; - } else { + break; + case SMPTE: p /= "VOLINDEX.xml"; + break; + default: + assert (false); } xmlpp::Document doc; xmlpp::Element* root; - if (interop) { + + switch (standard) { + case INTEROP: root = doc.create_root_node ("VolumeIndex", "http://www.digicine.com/PROTO-ASDCP-AM-20040311#"); - } else { + break; + case SMPTE: root = doc.create_root_node ("VolumeIndex", "http://www.smpte-ra.org/schemas/429-9/2007/AM"); + break; + default: + assert (false); } + root->add_child("Index")->add_child_text ("1"); doc.write_to_file (p.string (), "UTF-8"); } void -DCP::write_assetmap (string pkl_uuid, int pkl_length, bool interop, XMLMetadata const & metadata) const +DCP::write_assetmap (Standard standard, string pkl_uuid, int pkl_length, XMLMetadata metadata) const { - boost::filesystem::path p; - p /= _directory; - if (interop) { + boost::filesystem::path p = _directory; + + switch (standard) { + case INTEROP: p /= "ASSETMAP"; - } else { + break; + case SMPTE: p /= "ASSETMAP.xml"; + break; + default: + assert (false); } xmlpp::Document doc; xmlpp::Element* root; - if (interop) { + + switch (standard) { + case INTEROP: root = doc.create_root_node ("AssetMap", "http://www.digicine.com/PROTO-ASDCP-AM-20040311#"); - } else { + break; + case SMPTE: root = doc.create_root_node ("AssetMap", "http://www.smpte-ra.org/schemas/429-9/2007/AM"); + break; + default: + assert (false); } root->add_child("Id")->add_child_text ("urn:uuid:" + make_uuid()); root->add_child("AnnotationText")->add_child_text ("Created by " + metadata.creator); - if (interop) { + + switch (standard) { + case INTEROP: root->add_child("VolumeCount")->add_child_text ("1"); root->add_child("IssueDate")->add_child_text (metadata.issue_date); root->add_child("Issuer")->add_child_text (metadata.issuer); root->add_child("Creator")->add_child_text (metadata.creator); - } else { + break; + case SMPTE: root->add_child("Creator")->add_child_text (metadata.creator); root->add_child("VolumeCount")->add_child_text ("1"); root->add_child("IssueDate")->add_child_text (metadata.issue_date); root->add_child("Issuer")->add_child_text (metadata.issuer); + break; + default: + assert (false); } xmlpp::Node* asset_list = root->add_child ("AssetList"); @@ -193,187 +349,41 @@ DCP::write_assetmap (string pkl_uuid, int pkl_length, bool interop, XMLMetadata chunk->add_child("Offset")->add_child_text ("0"); chunk->add_child("Length")->add_child_text (lexical_cast<string> (pkl_length)); - for (list<shared_ptr<CPL> >::const_iterator i = _cpls.begin(); i != _cpls.end(); ++i) { - (*i)->write_to_assetmap (asset_list); - } - - list<shared_ptr<const Asset> > a = assets (); - for (list<shared_ptr<const Asset> >::const_iterator i = a.begin(); i != a.end(); ++i) { - (*i)->write_to_assetmap (asset_list); + for (list<shared_ptr<Asset> >::const_iterator i = _assets.begin(); i != _assets.end(); ++i) { + (*i)->write_to_assetmap (asset_list, _directory); } /* This must not be the _formatted version otherwise signature digests will be wrong */ doc.write_to_file (p.string (), "UTF-8"); } +/** Write all the XML files for this DCP. + * @param standand INTEROP or SMPTE. + * @param metadata Metadata to use for PKL and asset map files. + * @param signer Signer to use, or 0. + */ void -DCP::read (bool require_mxfs) -{ - read_assets (); - read_cpls (require_mxfs); -} - -void -DCP::read_assets () -{ - shared_ptr<parse::AssetMap> asset_map; - try { - boost::filesystem::path p = _directory; - p /= "ASSETMAP"; - if (boost::filesystem::exists (p)) { - asset_map.reset (new libdcp::parse::AssetMap (p.string ())); - } else { - p = _directory; - p /= "ASSETMAP.xml"; - if (boost::filesystem::exists (p)) { - asset_map.reset (new libdcp::parse::AssetMap (p.string ())); - } else { - boost::throw_exception (DCPReadError ("could not find AssetMap file")); - } - } - - } catch (FileError& e) { - boost::throw_exception (FileError ("could not load AssetMap file", _files.asset_map, e.number ())); - } - - for (list<shared_ptr<libdcp::parse::AssetMapAsset> >::const_iterator i = asset_map->assets.begin(); i != asset_map->assets.end(); ++i) { - if ((*i)->chunks.size() != 1) { - boost::throw_exception (XMLError ("unsupported asset chunk count")); - } - - boost::filesystem::path t = _directory; - t /= (*i)->chunks.front()->path; - - if (boost::algorithm::ends_with (t.string(), ".mxf") || boost::algorithm::ends_with (t.string(), ".ttf")) { - continue; - } - - xmlpp::DomParser* p = new xmlpp::DomParser; - try { - p->parse_file (t.string()); - } catch (std::exception& e) { - delete p; - continue; - } - - string const root = p->get_document()->get_root_node()->get_name (); - delete p; - - if (root == "CompositionPlaylist") { - _files.cpls.push_back (t.string()); - } else if (root == "PackingList") { - if (_files.pkl.empty ()) { - _files.pkl = t.string(); - } else { - boost::throw_exception (DCPReadError ("duplicate PKLs found")); - } - } - } - - if (_files.cpls.empty ()) { - boost::throw_exception (DCPReadError ("no CPL files found")); - } - - if (_files.pkl.empty ()) { - boost::throw_exception (DCPReadError ("no PKL file found")); - } - - shared_ptr<parse::PKL> pkl; - try { - pkl.reset (new parse::PKL (_files.pkl)); - } catch (FileError& e) { - boost::throw_exception (FileError ("could not load PKL file", _files.pkl, e.number ())); - } - - _asset_maps.push_back (make_pair (boost::filesystem::absolute (_directory).string(), asset_map)); -} - -void -DCP::read_cpls (bool require_mxfs) -{ - for (list<string>::iterator i = _files.cpls.begin(); i != _files.cpls.end(); ++i) { - _cpls.push_back (shared_ptr<CPL> (new CPL (_directory, *i, _asset_maps, require_mxfs))); - } -} - -bool -DCP::equals (DCP const & other, EqualityOptions opt, boost::function<void (NoteType, string)> note) const -{ - if (_cpls.size() != other._cpls.size()) { - note (ERROR, "CPL counts differ"); - return false; - } - - list<shared_ptr<CPL> >::const_iterator a = _cpls.begin (); - list<shared_ptr<CPL> >::const_iterator b = other._cpls.begin (); - - while (a != _cpls.end ()) { - if (!(*a)->equals (*b->get(), opt, note)) { - return false; - } - ++a; - ++b; - } - - return true; -} - -void -DCP::add_cpl (shared_ptr<CPL> cpl) -{ - _cpls.push_back (cpl); -} - -class AssetComparator -{ -public: - bool operator() (shared_ptr<const Asset> a, shared_ptr<const Asset> b) { - return a->uuid() < b->uuid(); - } -}; - -list<shared_ptr<const Asset> > -DCP::assets () const -{ - list<shared_ptr<const Asset> > a; - for (list<shared_ptr<CPL> >::const_iterator i = _cpls.begin(); i != _cpls.end(); ++i) { - list<shared_ptr<const Asset> > t = (*i)->assets (); - a.merge (t); - } - - a.sort (AssetComparator ()); - a.unique (); - return a; -} - -bool -DCP::encrypted () const +DCP::write_xml ( + Standard standard, + XMLMetadata metadata, + shared_ptr<const Signer> signer + ) { - for (list<shared_ptr<CPL> >::const_iterator i = _cpls.begin(); i != _cpls.end(); ++i) { - if ((*i)->encrypted ()) { - return true; - } + list<shared_ptr<CPL> > cpl = cpls (); + for (list<shared_ptr<CPL> >::const_iterator i = cpl.begin(); i != cpl.end(); ++i) { + string const filename = (*i)->id() + "_cpl.xml"; + (*i)->write_xml (_directory / filename, standard, signer); } - return false; -} - -void -DCP::add_kdm (KDM const & kdm) -{ - list<KDMKey> keys = kdm.keys (); + string const pkl_uuid = make_uuid (); + boost::filesystem::path const pkl_path = write_pkl (standard, pkl_uuid, metadata, signer); - for (list<shared_ptr<CPL> >::iterator i = _cpls.begin(); i != _cpls.end(); ++i) { - for (list<KDMKey>::iterator j = keys.begin(); j != keys.end(); ++j) { - if (j->cpl_id() == (*i)->id()) { - (*i)->add_kdm (kdm); - } - } - } + write_volindex (standard); + write_assetmap (standard, pkl_uuid, boost::filesystem::file_size (pkl_path), metadata); } -void -DCP::add_assets_from (DCP const & ov) +list<shared_ptr<CPL> > +DCP::cpls () const { - copy (ov._asset_maps.begin(), ov._asset_maps.end(), back_inserter (_asset_maps)); + return list_of_type<Asset, CPL> (_assets); } @@ -1,5 +1,5 @@ /* - Copyright (C) 2012 Carl Hetherington <cth@carlh.net> + Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -18,18 +18,19 @@ */ /** @file src/dcp.h - * @brief A class to create or read a DCP. + * @brief DCP class. */ #ifndef LIBDCP_DCP_H #define LIBDCP_DCP_H -#include <string> -#include <vector> -#include <boost/shared_ptr.hpp> -#include <boost/signals2.hpp> #include "types.h" #include "certificates.h" +#include "metadata.h" +#include <boost/shared_ptr.hpp> +#include <boost/signals2.hpp> +#include <string> +#include <vector> namespace xmlpp { class Document; @@ -37,18 +38,16 @@ namespace xmlpp { } /** @brief Namespace for everything in libdcp */ -namespace libdcp +namespace dcp { -class Asset; -class PictureAsset; -class SoundAsset; -class SubtitleAsset; +class Content; class Reel; class CPL; class XMLMetadata; class Signer; class KDM; +class Asset; namespace parse { class AssetMap; @@ -69,91 +68,59 @@ public: */ DCP (boost::filesystem::path directory); - void read (bool require_mxfs = true); - - /** Read an existing DCP's assets. - * - * The DCP's XML metadata will be examined, and you can then look at the contents - * of the DCP. - */ - void read_assets (); - - void read_cpls (bool require_mxfs = true); - - /** Write the required XML files to the directory that was - * passed into the constructor. - */ - void write_xml (bool interop, XMLMetadata const &, boost::shared_ptr<const Signer> signer = boost::shared_ptr<const Signer> ()) const; + void read (); /** Compare this DCP with another, according to various options. * @param other DCP to compare this one to. * @param options Options to define what "equality" means. + * @param note Functor to handle notes made by the equality operation. * @return true if the DCPs are equal according to EqualityOptions, otherwise false. */ bool equals (DCP const & other, EqualityOptions options, boost::function<void (NoteType, std::string)> note) const; - /** Add a CPL to this DCP. - * @param cpl CPL to add. - */ - void add_cpl (boost::shared_ptr<CPL> cpl); + void add (boost::shared_ptr<Asset> asset); - /** @return The list of CPLs in this DCP */ - std::list<boost::shared_ptr<CPL> > cpls () const { - return _cpls; - } + std::list<boost::shared_ptr<CPL> > cpls () const; - /** Add another DCP as a source of assets for this DCP. This should be called before - * ::read() on the DCP that needs the extra assets. For example - * - * DCP original_version ("my_dcp_OV"); - * DCP supplemental ("my_dcp_VF"); - * supplemental.add_assets_from (original_version); - * supplemental.read (); - */ - void add_assets_from (libdcp::DCP const &); + /** @return All this DCP's assets (note that CPLs are assets) */ + std::list<boost::shared_ptr<Asset> > assets () const { + return _assets; + } bool encrypted () const; - void add_kdm (KDM const &); + void add (KDM const &); - /** Emitted with a parameter between 0 and 1 to indicate progress - * for long jobs. - */ - boost::signals2::signal<void (float)> Progress; + void write_xml ( + Standard standard, + XMLMetadata metadata = XMLMetadata (), + boost::shared_ptr<const Signer> signer = boost::shared_ptr<const Signer> () + ); private: /** Write the PKL file. * @param pkl_uuid UUID to use. */ - std::string write_pkl (std::string pkl_uuid, bool, XMLMetadata const &, boost::shared_ptr<const Signer>) const; + boost::filesystem::path write_pkl ( + Standard standard, + std::string pkl_uuid, + XMLMetadata metadata, + boost::shared_ptr<const Signer> signer + ) const; - /** Write the VOLINDEX file */ - void write_volindex (bool) const; + void write_volindex (Standard standard) const; /** Write the ASSETMAP file. * @param pkl_uuid UUID of our PKL. * @param pkl_length Length of our PKL in bytes. */ - void write_assetmap (std::string pkl_uuid, int pkl_length, bool, XMLMetadata const &) const; - - /** @return Assets in all the CPLs in this DCP */ - std::list<boost::shared_ptr<const Asset> > assets () const; - - struct Files { - std::list<std::string> cpls; - std::string pkl; - std::string asset_map; - }; - - Files _files; + void write_assetmap (Standard standard, std::string pkl_uuid, int pkl_length, XMLMetadata metadata) const; /** the directory that we are writing to */ boost::filesystem::path _directory; - /** our CPLs */ - std::list<boost::shared_ptr<CPL> > _cpls; - - std::list<PathAssetMap> _asset_maps; + /** the assets that make up this DCP */ + std::list<boost::shared_ptr<Asset> > _assets; }; } diff --git a/src/dcp_time.cc b/src/dcp_time.cc index 14155fdb..5376b972 100644 --- a/src/dcp_time.cc +++ b/src/dcp_time.cc @@ -1,5 +1,5 @@ /* - Copyright (C) 2012 Carl Hetherington <cth@carlh.net> + Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -18,20 +18,20 @@ */ /** @file src/dcp_time.cc - * @brief A representation of time within a DCP. + * @brief Time class. */ -#include <iostream> -#include <vector> +#include "dcp_time.h" +#include "exceptions.h" #include <boost/algorithm/string.hpp> #include <boost/lexical_cast.hpp> +#include <iostream> +#include <vector> #include <cmath> -#include "dcp_time.h" -#include "exceptions.h" using namespace std; using namespace boost; -using namespace libdcp; +using namespace dcp; Time::Time (int frame, int frames_per_second) : h (0) @@ -85,19 +85,19 @@ Time::Time (string time) } bool -libdcp::operator== (Time const & a, Time const & b) +dcp::operator== (Time const & a, Time const & b) { return (a.h == b.h && a.m == b.m && a.s == b.s && a.t == b.t); } bool -libdcp::operator!= (Time const & a, Time const & b) +dcp::operator!= (Time const & a, Time const & b) { return !(a == b); } bool -libdcp::operator<= (Time const & a, Time const & b) +dcp::operator<= (Time const & a, Time const & b) { if (a.h != b.h) { return a.h <= b.h; @@ -119,7 +119,7 @@ libdcp::operator<= (Time const & a, Time const & b) } bool -libdcp::operator< (Time const & a, Time const & b) +dcp::operator< (Time const & a, Time const & b) { if (a.h != b.h) { return a.h < b.h; @@ -141,7 +141,7 @@ libdcp::operator< (Time const & a, Time const & b) } bool -libdcp::operator> (Time const & a, Time const & b) +dcp::operator> (Time const & a, Time const & b) { if (a.h != b.h) { return a.h > b.h; @@ -163,14 +163,14 @@ libdcp::operator> (Time const & a, Time const & b) } ostream & -libdcp::operator<< (ostream& s, Time const & t) +dcp::operator<< (ostream& s, Time const & t) { s << t.h << ":" << t.m << ":" << t.s << "." << t.t; return s; } -libdcp::Time -libdcp::operator+ (Time a, Time const & b) +dcp::Time +dcp::operator+ (Time a, Time const & b) { Time r; @@ -197,8 +197,8 @@ libdcp::operator+ (Time a, Time const & b) return r; } -libdcp::Time -libdcp::operator- (Time a, Time const & b) +dcp::Time +dcp::operator- (Time a, Time const & b) { Time r; @@ -226,7 +226,7 @@ libdcp::operator- (Time a, Time const & b) } float -libdcp::operator/ (Time a, Time const & b) +dcp::operator/ (Time a, Time const & b) { int64_t const at = a.h * 3600 * 250 + a.m * 60 * 250 + a.s * 250 + a.t; int64_t const bt = b.h * 3600 * 250 + b.m * 60 * 250 + b.s * 250 + b.t; diff --git a/src/dcp_time.h b/src/dcp_time.h index 92cee9a0..697ca230 100644 --- a/src/dcp_time.h +++ b/src/dcp_time.h @@ -1,5 +1,5 @@ /* - Copyright (C) 2012 Carl Hetherington <cth@carlh.net> + Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -18,17 +18,17 @@ */ /** @file src/dcp_time.h - * @brief A representation of time within a DCP. + * @brief Time class. */ #ifndef LIBDCP_TIME_H #define LIBDCP_TIME_H +#include <stdint.h> #include <string> #include <iostream> -#include <stdint.h> -namespace libdcp { +namespace dcp { /** @class Time * @brief A representation of time within a DCP. diff --git a/src/exceptions.cc b/src/exceptions.cc index 6a32a33f..e5a46948 100644 --- a/src/exceptions.cc +++ b/src/exceptions.cc @@ -17,13 +17,17 @@ */ +/** @file src/exceptions.cc + * @brief Exceptions thrown by libdcp. + */ + #include "exceptions.h" #include "compose.hpp" using std::string; -using namespace libdcp; +using namespace dcp; -FileError::FileError (std::string const & message, boost::filesystem::path filename, int number) +FileError::FileError (string message, boost::filesystem::path filename, int number) : _message (String::compose ("%1 (error %2) (%3)", message, filename.string(), number)) , _filename (filename) , _number (number) @@ -31,3 +35,9 @@ FileError::FileError (std::string const & message, boost::filesystem::path filen } +UnresolvedRefError::UnresolvedRefError (string id) + : _message (String::compose ("Unresolved reference to asset id %1", id)) +{ + +} + diff --git a/src/exceptions.h b/src/exceptions.h index 2070dd0f..bc5e83d1 100644 --- a/src/exceptions.h +++ b/src/exceptions.h @@ -1,5 +1,5 @@ /* - Copyright (C) 2012 Carl Hetherington <cth@carlh.net> + Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -26,14 +26,16 @@ * @brief Exceptions thrown by libdcp. */ -namespace libdcp +namespace dcp { -/** @brief An exception related to a file */ +/** @class FileError + * @brief An exception related to a file + */ class FileError : public std::exception { public: - FileError (std::string const & message, boost::filesystem::path filename, int number); + FileError (std::string message, boost::filesystem::path filename, int number); ~FileError () throw () {} /** @return error message */ @@ -59,20 +61,24 @@ private: int _number; }; -/** @brief An exception related to an MXF file */ +/** @class MXFFileError + * @brief An exception related to an MXF file + */ class MXFFileError : public FileError { public: - MXFFileError (std::string const & message, boost::filesystem::path filename, int number) + MXFFileError (std::string message, boost::filesystem::path filename, int number) : FileError (message, filename, number) {} }; -/** @brief A miscellaneous exception */ +/** @class MiscError + * @brief A miscellaneous exception + */ class MiscError : public std::exception { public: - MiscError (std::string const & message) : _message (message) {} + MiscError (std::string message) : _message (message) {} ~MiscError () throw () {} /** @return error message */ @@ -85,11 +91,13 @@ private: std::string _message; }; -/** @brief A DCP read exception */ +/** @class DCPReadError + * @brief A DCP read exception + */ class DCPReadError : public std::exception { public: - DCPReadError (std::string const & message) : _message (message) {} + DCPReadError (std::string message) : _message (message) {} ~DCPReadError () throw () {} /** @return error message */ @@ -102,11 +110,13 @@ private: std::string _message; }; -/** @brief An XML error */ +/** @class XMLError + * @brief An XML error + */ class XMLError : public std::exception { public: - XMLError (std::string const & message) : _message (message) {} + XMLError (std::string message) : _message (message) {} ~XMLError () throw () {} /** @return error message */ @@ -118,6 +128,24 @@ private: /** error message */ std::string _message; }; + +/** @class UnresolvedRefError + * @brief An exception caused by a reference (by UUID) to something which is not known + */ +class UnresolvedRefError : public std::exception +{ +public: + UnresolvedRefError (std::string id); + ~UnresolvedRefError () throw () {} + + /** @return error message */ + char const * what () const throw () { + return _message.c_str (); + } + +private: + std::string _message; +}; } diff --git a/src/file.cc b/src/file.cc new file mode 100644 index 00000000..aabda166 --- /dev/null +++ b/src/file.cc @@ -0,0 +1,46 @@ +/* + Copyright (C) 2014 Carl Hetherington <cth@carlh.net> + + This program 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. + + This program 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 this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +/** @file src/file.cc + * @brief File class. + */ + +#include "file.h" +#include "util.h" + +using namespace dcp; + +/** Read a file into memory. + * @param file to read. + */ +File::File (boost::filesystem::path file) +{ + _size = boost::filesystem::file_size (file); + _data = new uint8_t[_size]; + FILE* f = dcp::fopen_boost (file, "r"); + assert (f); + fread (_data, 1, _size, f); + fclose (f); +} + +/** File destructor */ +File::~File () +{ + delete[] _data; +} diff --git a/src/rec709_linearised_gamma_lut.cc b/src/file.h index f14e4472..b06cece0 100644 --- a/src/rec709_linearised_gamma_lut.cc +++ b/src/file.h @@ -1,5 +1,5 @@ /* - Copyright (C) 2012-2013 Carl Hetherington <cth@carlh.net> + Copyright (C) 2014 Carl Hetherington <cth@carlh.net> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -17,22 +17,39 @@ */ -#include "rec709_linearised_gamma_lut.h" +/** @file src/file.h + * @brief File class. + */ -using namespace libdcp; +#ifndef LIBDCP_FILE_H +#define LIBDCP_FILE_H -LUTCache<Rec709LinearisedGammaLUT> Rec709LinearisedGammaLUT::cache; +#include <boost/filesystem.hpp> -Rec709LinearisedGammaLUT::Rec709LinearisedGammaLUT (int bits, float gamma) - : LUT (bits, gamma) +namespace dcp { + +/** @class File + * @brief Helper class which loads a file into memory. + */ +class File { - int const bit_length = pow (2, bits); - for (int i = 0; i < bit_length; ++i) { - float const p = static_cast<float> (i) / (bit_length - 1); - if (p > 0.08125) { - _lut[i] = pow ((p + 0.099) / 1.099, gamma); - } else { - _lut[i] = p / 4.5; - } +public: + File (boost::filesystem::path file); + ~File (); + + uint8_t* data () const { + return _data; + } + + int64_t size () const { + return _size; } + +private: + uint8_t* _data; ///< file's data + int64_t _size; ///< data size in bytes +}; + } + +#endif diff --git a/src/parse/subtitle.cc b/src/font.cc index 914be677..52996a73 100644 --- a/src/parse/subtitle.cc +++ b/src/font.cc @@ -1,5 +1,5 @@ /* - Copyright (C) 2012-2013 Carl Hetherington <cth@carlh.net> + Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -17,20 +17,18 @@ */ -#include <boost/shared_ptr.hpp> -#include <boost/optional.hpp> -#include "subtitle.h" -#include "../types.h" +#include "font.h" +#include "xml.h" +#include "text.h" +#include <libcxml/cxml.h> using std::string; using std::list; using boost::shared_ptr; using boost::optional; -using boost::lexical_cast; -using namespace libdcp; -using namespace libdcp::parse; +using namespace dcp; -Font::Font (shared_ptr<const cxml::Node> node) +Font::Font (boost::shared_ptr<const cxml::Node> node) { text = node->content (); @@ -54,7 +52,7 @@ Font::Font (shared_ptr<const cxml::Node> node) text_nodes = type_children<Text> (node, "Text"); } -Font::Font (list<shared_ptr<Font> > const & font_nodes) +Font::Font (std::list<boost::shared_ptr<Font> > const & font_nodes) : size (0) , italic (false) , color ("FFFFFFFF") @@ -81,69 +79,3 @@ Font::Font (list<shared_ptr<Font> > const & font_nodes) } } } - -LoadFont::LoadFont (shared_ptr<const cxml::Node> node) -{ - optional<string> x = node->optional_string_attribute ("Id"); - if (!x) { - x = node->optional_string_attribute ("ID"); - } - id = x.get_value_or (""); - - uri = node->optional_string_attribute ("URI"); -} - -Subtitle::Subtitle (shared_ptr<const cxml::Node> node) -{ - in = Time (node->string_attribute ("TimeIn")); - out = Time (node->string_attribute ("TimeOut")); - font_nodes = type_children<Font> (node, "Font"); - text_nodes = type_children<Text> (node, "Text"); - fade_up_time = fade_time (node, "FadeUpTime"); - fade_down_time = fade_time (node, "FadeDownTime"); -} - -Time -Subtitle::fade_time (shared_ptr<const cxml::Node> node, string name) -{ - string const u = node->optional_string_attribute (name).get_value_or (""); - Time t; - - if (u.empty ()) { - t = Time (0, 0, 0, 20); - } else if (u.find (":") != string::npos) { - t = Time (u); - } else { - t = Time (0, 0, 0, lexical_cast<int> (u)); - } - - if (t > Time (0, 0, 8, 0)) { - t = Time (0, 0, 8, 0); - } - - return t; -} - -Text::Text (shared_ptr<const cxml::Node> node) - : v_align (CENTER) -{ - /* Vertical position */ - text = node->content (); - optional<float> x = node->optional_number_attribute<float> ("VPosition"); - if (!x) { - x = node->number_attribute<float> ("Vposition"); - } - v_position = x.get (); - - /* Vertical alignment */ - optional<string> v = node->optional_string_attribute ("VAlign"); - if (!v) { - v = node->optional_string_attribute ("Valign"); - } - if (v) { - v_align = string_to_valign (v.get ()); - } - - font_nodes = type_children<Font> (node, "Font"); -} - diff --git a/src/parse/subtitle.h b/src/font.h index 3d99d9bc..1b29e570 100644 --- a/src/parse/subtitle.h +++ b/src/font.h @@ -1,5 +1,5 @@ /* - Copyright (C) 2012 Carl Hetherington <cth@carlh.net> + Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -17,51 +17,18 @@ */ -#include "../xml.h" -#include "../dcp_time.h" -#include "../types.h" +#include "types.h" +#include "subtitle.h" +#include <boost/shared_ptr.hpp> +#include <boost/optional.hpp> +#include <list> -namespace libdcp -{ - -namespace parse -{ - -class Font; +namespace cxml { + class Node; +} -class Text -{ -public: - Text () - : v_position (0) - , v_align (TOP) - {} +namespace dcp { - Text (boost::shared_ptr<const cxml::Node> node); - - float v_position; - VAlign v_align; - std::string text; - std::list<boost::shared_ptr<Font> > font_nodes; -}; - -class Subtitle -{ -public: - Subtitle () {} - Subtitle (boost::shared_ptr<const cxml::Node> node); - - Time in; - Time out; - Time fade_up_time; - Time fade_down_time; - std::list<boost::shared_ptr<Font> > font_nodes; - std::list<boost::shared_ptr<Text> > text_nodes; - -private: - Time fade_time (boost::shared_ptr<const cxml::Node>, std::string name); -}; - class Font { public: @@ -85,16 +52,4 @@ public: std::list<boost::shared_ptr<Text> > text_nodes; }; -class LoadFont -{ -public: - LoadFont () {} - LoadFont (boost::shared_ptr<const cxml::Node> node); - - std::string id; - boost::optional<std::string> uri; -}; - -} - } diff --git a/src/gamma_lut.cc b/src/gamma_lut.cc index 80d9b902..abe35b7c 100644 --- a/src/gamma_lut.cc +++ b/src/gamma_lut.cc @@ -1,5 +1,5 @@ /* - Copyright (C) 2012-2013 Carl Hetherington <cth@carlh.net> + Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -17,19 +17,34 @@ */ -#include <cmath> #include "gamma_lut.h" #include "lut_cache.h" +#include <cmath> -using namespace libdcp; +using namespace dcp; LUTCache<GammaLUT> GammaLUT::cache; -GammaLUT::GammaLUT(int bits, float gamma) - : LUT (bits, gamma) +GammaLUT::GammaLUT (int bit_depth, float gamma, bool linearised) + : _bit_depth (bit_depth) + , _gamma (gamma) + , _linearised (linearised) { - int const bit_length = pow(2, bits); - for (int i = 0; i < bit_length; ++i) { - _lut[i] = pow(float(i) / (bit_length - 1), gamma); + _lut = new float[int(std::pow(2.0f, _bit_depth))]; + int const bit_length = pow (2, _bit_depth); + + if (_linearised) { + for (int i = 0; i < bit_length; ++i) { + float const p = static_cast<float> (i) / (bit_length - 1); + if (p > 0.04045) { + _lut[i] = pow ((p + 0.055) / 1.055, gamma); + } else { + _lut[i] = p / 12.92; + } + } + } else { + for (int i = 0; i < bit_length; ++i) { + _lut[i] = pow(float(i) / (bit_length - 1), gamma); + } } } diff --git a/src/gamma_lut.h b/src/gamma_lut.h index 32e4af02..76a63ccf 100644 --- a/src/gamma_lut.h +++ b/src/gamma_lut.h @@ -1,5 +1,5 @@ /* - Copyright (C) 2012-2013 Carl Hetherington <cth@carlh.net> + Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -20,16 +20,42 @@ #ifndef LIBDCP_GAMMA_LUT_H #define LIBDCP_GAMMA_LUT_H -#include "lut.h" #include "lut_cache.h" -namespace libdcp { +namespace dcp { -class GammaLUT : public LUT +class GammaLUT { public: - GammaLUT (int bit_length, float gamma); + GammaLUT (int bit_depth, float gamma, bool linearised); + + ~GammaLUT () { + delete[] _lut; + } + + float const * lut () const { + return _lut; + } + + int bit_depth () const { + return _bit_depth; + } + + float gamma () const { + return _gamma; + } + + bool linearised () const { + return _linearised; + } + static LUTCache<GammaLUT> cache; + +private: + float* _lut; + int _bit_depth; + float _gamma; + bool _linearised; }; } diff --git a/src/image.cc b/src/image.cc index 24174fca..15ff24dc 100644 --- a/src/image.cc +++ b/src/image.cc @@ -1,5 +1,5 @@ /* - Copyright (C) 2012 Carl Hetherington <cth@carlh.net> + Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -20,7 +20,7 @@ #include "image.h" using boost::shared_ptr; -using namespace libdcp; +using namespace dcp; Image::Image (Size s) : _size (s) @@ -34,7 +34,7 @@ Image::Image (Image const & other) } -Image::Image (shared_ptr<const Image> other) +Image::Image (boost::shared_ptr<const Image> other) : _size (other->_size) { diff --git a/src/image.h b/src/image.h index 8b7fee09..069534ea 100644 --- a/src/image.h +++ b/src/image.h @@ -1,5 +1,5 @@ /* - Copyright (C) 2012 Carl Hetherington <cth@carlh.net> + Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -19,7 +19,7 @@ #include "util.h" -namespace libdcp { +namespace dcp { class Image { @@ -1,5 +1,5 @@ /* - Copyright (C) 2013 Carl Hetherington <cth@carlh.net> + Copyright (C) 2013-2014 Carl Hetherington <cth@carlh.net> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -17,23 +17,27 @@ */ -#include <iomanip> -#include <algorithm> -#include <boost/algorithm/string.hpp> -#include <openssl/rsa.h> -#include <openssl/pem.h> -#include <openssl/err.h> -#include <libcxml/cxml.h> -#include "AS_DCP.h" -#include "KM_util.h" +/** @file src/kdm.cc + * @brief KDM and KDMKey classes. + */ + #include "util.h" #include "kdm.h" #include "compose.hpp" #include "exceptions.h" #include "signer.h" #include "cpl.h" -#include "mxf_asset.h" -#include "xml/kdm_smpte.h" +#include "mxf.h" +#include "kdm_smpte_xml.h" +#include "AS_DCP.h" +#include "KM_util.h" +#include <libcxml/cxml.h> +#include <openssl/rsa.h> +#include <openssl/pem.h> +#include <openssl/err.h> +#include <boost/algorithm/string.hpp> +#include <iomanip> +#include <algorithm> using std::list; using std::string; @@ -43,7 +47,7 @@ using std::setw; using std::setfill; using std::cout; using boost::shared_ptr; -using namespace libdcp; +using namespace dcp; KDM::KDM (boost::filesystem::path kdm, boost::filesystem::path private_key) : _xml_kdm (new xml::DCinemaSecurityMessage (kdm)) @@ -87,7 +91,9 @@ KDM::KDM (boost::filesystem::path kdm, boost::filesystem::path private_key) } KDM::KDM ( - shared_ptr<const CPL> cpl, shared_ptr<const Signer> signer, shared_ptr<const Certificate> recipient_cert, + boost::shared_ptr<const CPL> cpl, + boost::shared_ptr<const Signer> signer, + boost::shared_ptr<const Certificate> recipient_cert, boost::posix_time::ptime not_valid_before, boost::posix_time::ptime not_valid_after, string annotation_text, string issue_date ) @@ -108,7 +114,7 @@ KDM::KDM ( apu.recipient.x509_subject_name = recipient_cert->subject (); apu.composition_playlist_id = "urn:uuid:" + cpl->id (); // apu.content_authenticator = signer->certificates().leaf()->thumbprint (); - apu.content_title_text = cpl->name (); + apu.content_title_text = cpl->content_title_text (); apu.content_keys_not_valid_before = ptime_to_string (not_valid_before); apu.content_keys_not_valid_after = ptime_to_string (not_valid_after); apu.authorized_device_info.device_list_identifier = "urn:uuid:" + make_uuid (); @@ -124,10 +130,10 @@ KDM::KDM ( */ apu.authorized_device_info.device_list.push_back ("2jmj7l5rSw0yVb/vlWAYkK/YBwk="); - list<shared_ptr<const Asset> > assets = cpl->assets (); - for (list<shared_ptr<const Asset> >::iterator i = assets.begin(); i != assets.end(); ++i) { + list<shared_ptr<const Content> > content = cpl->content (); + for (list<shared_ptr<const Content> >::iterator i = content.begin(); i != content.end(); ++i) { /* XXX: non-MXF assets? */ - shared_ptr<const MXFAsset> mxf = boost::dynamic_pointer_cast<const MXFAsset> (*i); + shared_ptr<const MXF> mxf = boost::dynamic_pointer_cast<const MXF> (*i); if (mxf) { apu.key_id_list.push_back (xml::TypedKeyId (mxf->key_type(), "urn:uuid:" + mxf->key_id())); } @@ -138,9 +144,9 @@ KDM::KDM ( /* AuthenticatedPrivate */ - for (list<shared_ptr<const Asset> >::iterator i = assets.begin(); i != assets.end(); ++i) { + for (list<shared_ptr<const Content> >::iterator i = content.begin(); i != content.end(); ++i) { /* XXX: non-MXF assets? */ - shared_ptr<const MXFAsset> mxf = boost::dynamic_pointer_cast<const MXFAsset> (*i); + shared_ptr<const MXF> mxf = boost::dynamic_pointer_cast<const MXF> (*i); if (mxf) { KDMKey kkey ( signer, cpl->id (), mxf->key_type (), mxf->key_id (), @@ -202,7 +208,13 @@ KDM::as_xml () const } KDMKey::KDMKey ( - shared_ptr<const Signer> signer, string cpl_id, string key_type, string key_id, boost::posix_time::ptime from, boost::posix_time::ptime until, Key key + boost::shared_ptr<const Signer> signer, + string cpl_id, + string key_type, + string key_id, + boost::posix_time::ptime from, + boost::posix_time::ptime until, + Key key ) : _cpl_id (cpl_id) , _key_type (key_type) @@ -275,7 +287,7 @@ KDMKey::operator= (KDMKey const & other) } string -KDMKey::encrypted_base64 (shared_ptr<const Certificate> recipient_cert) const +KDMKey::encrypted_base64 (boost::shared_ptr<const Certificate> recipient_cert) const { assert (_key_type.length() == 4); assert (_not_valid_before.length() == 25); @@ -383,7 +395,7 @@ KDMKey::put_uuid (uint8_t ** d, string id) const } bool -libdcp::operator== (libdcp::KDMKey const & a, libdcp::KDMKey const & b) +dcp::operator== (dcp::KDMKey const & a, dcp::KDMKey const & b) { if (memcmp (a._signer_thumbprint, b._signer_thumbprint, 20) != 0) { return false; @@ -1,5 +1,5 @@ /* - Copyright (C) 2013 Carl Hetherington <cth@carlh.net> + Copyright (C) 2013-2014 Carl Hetherington <cth@carlh.net> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -18,21 +18,21 @@ */ /** @file src/kdm.h - * @brief Handling of Key Delivery Messages (KDMs). + * @brief KDM and KDMKey classes. */ #ifndef LIBDCP_KDM_H #define LIBDCP_KDM_H +#include "key.h" +#include "metadata.h" #include <boost/filesystem.hpp> #include <boost/scoped_ptr.hpp> #include <boost/date_time/posix_time/posix_time.hpp> -#include "key.h" -#include "metadata.h" class kdm_key_test; -namespace libdcp { +namespace dcp { namespace xml { class DCinemaSecurityMessage; @@ -161,8 +161,8 @@ public: * @param recipient_cert Certificate of the projector that this KDM is targeted at. * @param not_valid_before Start of validity period. * @param not_valid_after End of validity period. - * @param annotation_text Text for the <AnnotationText> node. - * @param issue_date Text for the <IssueDate> node. + * @param annotation_text Text for the <AnnotationText> node. + * @param issue_date Text for the <IssueDate> node. */ KDM ( boost::shared_ptr<const CPL> cpl, boost::shared_ptr<const Signer> signer, boost::shared_ptr<const Certificate> recipient_cert, diff --git a/src/xml/kdm_smpte.h b/src/kdm_smpte_xml.h index 32a297f8..70b7847e 100644 --- a/src/xml/kdm_smpte.h +++ b/src/kdm_smpte_xml.h @@ -1,5 +1,5 @@ /* - Copyright (C) 2013 Carl Hetherington <cth@carlh.net> + Copyright (C) 2013-2014 Carl Hetherington <cth@carlh.net> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -17,7 +17,7 @@ */ -/** @file src/xml/kdm_smpte.h +/** @file src/kdm_smpte_xml.h * @brief 1:1ish C++ representations of the XML schema for a SMPTE KDM. * * This file contains classes which map pretty-much 1:1 to the elements in a SMPTE KDM @@ -29,19 +29,19 @@ * change very often. */ -#ifndef LIBDCP_XML_KDM_SMPTE_H -#define LIBDCP_XML_KDM_SMPTE_H +#ifndef LIBDCP_KDM_SMPTE_XML_H +#define LIBDCP_KDM_SMPTE_XML_H -#include <string> -#include <list> -#include <boost/optional.hpp> -#include <boost/filesystem.hpp> +#include "exceptions.h" +#include <libcxml/cxml.h> #include <libxml/parser.h> #include <libxml++/libxml++.h> -#include <libcxml/cxml.h> -#include "../exceptions.h" +#include <boost/optional.hpp> +#include <boost/filesystem.hpp> +#include <string> +#include <list> -namespace libdcp { +namespace dcp { namespace xml { class Writer @@ -1,5 +1,5 @@ /* - Copyright (C) 2013 Carl Hetherington <cth@carlh.net> + Copyright (C) 2013-2014 Carl Hetherington <cth@carlh.net> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -17,19 +17,23 @@ */ -#include <sstream> -#include <string> -#include <iomanip> +/** @file src/key.cc + * @brief Key class. + */ + +#include "key.h" #include "AS_DCP.h" #include "KM_prng.h" #include "KM_util.h" -#include "key.h" +#include <sstream> +#include <string> +#include <iomanip> using std::string; using std::stringstream; using std::setw; using std::setfill; -using namespace libdcp; +using namespace dcp; Key::Key () : _value (new uint8_t[ASDCP::KeyLen]) @@ -86,13 +90,13 @@ Key::hex () const } bool -libdcp::operator== (Key const & a, Key const & b) +dcp::operator== (Key const & a, Key const & b) { return memcmp (a.value(), b.value(), ASDCP::KeyLen) == 0; } bool -libdcp::operator!= (Key const & a, Key const & b) +dcp::operator!= (Key const & a, Key const & b) { return !(a == b); } @@ -1,5 +1,5 @@ /* - Copyright (C) 2013 Carl Hetherington <cth@carlh.net> + Copyright (C) 2013-2014 Carl Hetherington <cth@carlh.net> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -18,18 +18,19 @@ */ /** @file src/key.h - * @brief Class to hold a key for encrypting MXFs. + * @brief Key class. */ #ifndef LIBDCP_KEY_H #define LIBDCP_KEY_H #include <stdint.h> +#include <string> -namespace libdcp { +namespace dcp { /** @class Key - * @brief A key for encrypting MXFs. + * @brief A key for decrypting/encrypting MXFs. */ class Key { diff --git a/src/rec709_linearised_gamma_lut.h b/src/load_font.cc index af0158a5..707f4a0b 100644 --- a/src/rec709_linearised_gamma_lut.h +++ b/src/load_font.cc @@ -1,5 +1,5 @@ /* - Copyright (C) 2012-2013 Carl Hetherington <cth@carlh.net> + Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -17,16 +17,14 @@ */ -#include "lut.h" -#include "lut_cache.h" +#include "load_font.h" +#include <libcxml/cxml.h> -namespace libdcp { +using boost::shared_ptr; +using namespace dcp; -class Rec709LinearisedGammaLUT : public LUT +LoadFont::LoadFont (boost::shared_ptr<const cxml::Node> node) { -public: - Rec709LinearisedGammaLUT (int bit_length, float gamma); - static LUTCache<Rec709LinearisedGammaLUT> cache; -}; - + id = node->string_attribute ("Id"); + uri = node->string_attribute ("URI"); } diff --git a/src/srgb_linearised_gamma_lut.h b/src/load_font.h index 34800c66..6319b1de 100644 --- a/src/srgb_linearised_gamma_lut.h +++ b/src/load_font.h @@ -1,5 +1,5 @@ /* - Copyright (C) 2012-2013 Carl Hetherington <cth@carlh.net> + Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -17,16 +17,22 @@ */ -#include "lut.h" -#include "lut_cache.h" +#include <boost/shared_ptr.hpp> -namespace libdcp { +namespace cxml { + class Node; +} -class SRGBLinearisedGammaLUT : public LUT +namespace dcp { + +class LoadFont { public: - SRGBLinearisedGammaLUT (int bit_length, float gamma); - static LUTCache<SRGBLinearisedGammaLUT> cache; + LoadFont () {} + LoadFont (boost::shared_ptr<const cxml::Node> node); + + std::string id; + std::string uri; }; } diff --git a/src/lut_cache.h b/src/lut_cache.h index b60ee109..5c75fe6a 100644 --- a/src/lut_cache.h +++ b/src/lut_cache.h @@ -1,14 +1,34 @@ +/* + Copyright (C) 2013-2014 Carl Hetherington <cth@carlh.net> + + This program 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. + + This program 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 this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + #ifndef LIBDCP_LUT_CACHE_H #define LIBDCP_LUT_CACHE_H #include <list> #include <boost/shared_ptr.hpp> +#include <boost/noncopyable.hpp> template<class T> -class LUTCache +class LUTCache : public boost::noncopyable { public: - boost::shared_ptr<T> get (int bit_depth, float gamma) + boost::shared_ptr<T> get (int bit_depth, float gamma, bool linearised) { for (typename std::list<boost::shared_ptr<T> >::iterator i = _cache.begin(); i != _cache.end(); ++i) { if ((*i)->bit_depth() == bit_depth && (*i)->gamma() == gamma) { @@ -16,7 +36,7 @@ public: } } - boost::shared_ptr<T> lut (new T (bit_depth, gamma)); + boost::shared_ptr<T> lut (new T (bit_depth, gamma, linearised)); _cache.push_back (lut); return lut; } diff --git a/src/metadata.cc b/src/metadata.cc index acbc3b2e..e3382613 100644 --- a/src/metadata.cc +++ b/src/metadata.cc @@ -1,5 +1,5 @@ /* - Copyright (C) 2012 Carl Hetherington <cth@carlh.net> + Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -18,20 +18,20 @@ */ /** @file src/metadata.cc - * @brief Metadata for writing to the DCP. + * @brief XMLMetadata and MXFMetadata classes. */ -#include <sstream> -#include <iomanip> -#include <time.h> +#include "metadata.h" +#include "util.h" #ifdef LIBDCP_WINDOWS #include <windows.h> #endif -#include "metadata.h" -#include "util.h" +#include <sstream> +#include <iomanip> +#include <time.h> using namespace std; -using namespace libdcp; +using namespace dcp; MXFMetadata::MXFMetadata () : company_name ("libdcp") diff --git a/src/metadata.h b/src/metadata.h index b4c0896d..b1fcdd8e 100644 --- a/src/metadata.h +++ b/src/metadata.h @@ -1,5 +1,5 @@ /* - Copyright (C) 2012 Carl Hetherington <cth@carlh.net> + Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -21,16 +21,19 @@ #define LIBDCP_METADATA_H /** @file src/metadata.h - * @brief Metadata for writing to the DCP. + * @brief XMLMetadata and MXFMetadata classes. */ #include <string> class utc_offset_to_string_test; -namespace libdcp +namespace dcp { +/** @class MXFMetadata + * @brief Metadata that is written to a MXF file's header + */ class MXFMetadata { public: @@ -41,6 +44,9 @@ public: std::string product_version; }; +/** @class XMLMetadata + * @brief Common metadata that is written to a few different XML files + */ class XMLMetadata { public: diff --git a/src/mono_picture_asset.cc b/src/mono_picture_asset.cc deleted file mode 100644 index 0ac48d37..00000000 --- a/src/mono_picture_asset.cc +++ /dev/null @@ -1,200 +0,0 @@ -/* - Copyright (C) 2012-2013 Carl Hetherington <cth@carlh.net> - - This program 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. - - This program 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 this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - -*/ - -#include "mono_picture_asset.h" -#include "mono_picture_asset_writer.h" -#include "AS_DCP.h" -#include "KM_fileio.h" -#include "exceptions.h" -#include "mono_picture_frame.h" - -using std::string; -using std::vector; -using boost::shared_ptr; -using boost::dynamic_pointer_cast; -using boost::lexical_cast; -using namespace libdcp; - -MonoPictureAsset::MonoPictureAsset (boost::filesystem::path directory, boost::filesystem::path mxf_name) - : PictureAsset (directory, mxf_name) -{ - -} - -void -MonoPictureAsset::create (vector<boost::filesystem::path> const & files) -{ - create (boost::bind (&MonoPictureAsset::path_from_list, this, _1, files)); -} - -void -MonoPictureAsset::create (boost::function<boost::filesystem::path (int)> get_path) -{ - ASDCP::JP2K::CodestreamParser j2k_parser; - ASDCP::JP2K::FrameBuffer frame_buffer (4 * Kumu::Megabyte); - Kumu::Result_t r = j2k_parser.OpenReadFrame (get_path(0).string().c_str(), frame_buffer); - if (ASDCP_FAILURE (r)) { - boost::throw_exception (FileError ("could not open JPEG2000 file for reading", get_path(0), r)); - } - - ASDCP::JP2K::PictureDescriptor picture_desc; - j2k_parser.FillPictureDescriptor (picture_desc); - picture_desc.EditRate = ASDCP::Rational (_edit_rate, 1); - - ASDCP::WriterInfo writer_info; - fill_writer_info (&writer_info); - - ASDCP::JP2K::MXFWriter mxf_writer; - r = mxf_writer.OpenWrite (path().string().c_str(), writer_info, picture_desc, 16384, false); - if (ASDCP_FAILURE (r)) { - boost::throw_exception (MXFFileError ("could not open MXF file for writing", path().string(), r)); - } - - for (int i = 0; i < _intrinsic_duration; ++i) { - - boost::filesystem::path const path = get_path (i); - - Kumu::Result_t r = j2k_parser.OpenReadFrame (path.string().c_str(), frame_buffer); - if (ASDCP_FAILURE (r)) { - boost::throw_exception (FileError ("could not open JPEG2000 file for reading", path, r)); - } - - r = mxf_writer.WriteFrame (frame_buffer, _encryption_context, 0); - if (ASDCP_FAILURE (r)) { - boost::throw_exception (MXFFileError ("error in writing video MXF", this->path().string(), r)); - } - - if (_progress) { - (*_progress) (0.5 * float (i) / _intrinsic_duration); - } - } - - r = mxf_writer.Finalize(); - if (ASDCP_FAILURE (r)) { - boost::throw_exception (MXFFileError ("error in finalising video MXF", path().string(), r)); - } -} - -void -MonoPictureAsset::read () -{ - ASDCP::JP2K::MXFReader reader; - Kumu::Result_t r = reader.OpenRead (path().string().c_str()); - if (ASDCP_FAILURE (r)) { - boost::throw_exception (MXFFileError ("could not open MXF file for reading", path().string(), r)); - } - - ASDCP::JP2K::PictureDescriptor desc; - if (ASDCP_FAILURE (reader.FillPictureDescriptor (desc))) { - boost::throw_exception (DCPReadError ("could not read video MXF information")); - } - - _size.width = desc.StoredWidth; - _size.height = desc.StoredHeight; - _edit_rate = desc.EditRate.Numerator; - assert (desc.EditRate.Denominator == 1); - _intrinsic_duration = desc.ContainerDuration; -} - -boost::filesystem::path -MonoPictureAsset::path_from_list (int f, vector<boost::filesystem::path> const & files) const -{ - return files[f]; -} - -shared_ptr<const MonoPictureFrame> -MonoPictureAsset::get_frame (int n) const -{ - return shared_ptr<const MonoPictureFrame> (new MonoPictureFrame (path(), n, _decryption_context)); -} - -bool -MonoPictureAsset::equals (shared_ptr<const Asset> other, EqualityOptions opt, boost::function<void (NoteType, string)> note) const -{ - if (!MXFAsset::equals (other, opt, note)) { - return false; - } - - ASDCP::JP2K::MXFReader reader_A; - Kumu::Result_t r = reader_A.OpenRead (path().string().c_str()); - if (ASDCP_FAILURE (r)) { - boost::throw_exception (MXFFileError ("could not open MXF file for reading", path().string(), r)); - } - - ASDCP::JP2K::MXFReader reader_B; - r = reader_B.OpenRead (other->path().string().c_str()); - if (ASDCP_FAILURE (r)) { - boost::throw_exception (MXFFileError ("could not open MXF file for reading", path().string(), r)); - } - - ASDCP::JP2K::PictureDescriptor desc_A; - if (ASDCP_FAILURE (reader_A.FillPictureDescriptor (desc_A))) { - boost::throw_exception (DCPReadError ("could not read video MXF information")); - } - ASDCP::JP2K::PictureDescriptor desc_B; - if (ASDCP_FAILURE (reader_B.FillPictureDescriptor (desc_B))) { - boost::throw_exception (DCPReadError ("could not read video MXF information")); - } - - if (!descriptor_equals (desc_A, desc_B, note)) { - return false; - } - - shared_ptr<const MonoPictureAsset> other_picture = dynamic_pointer_cast<const MonoPictureAsset> (other); - assert (other_picture); - - for (int i = 0; i < _intrinsic_duration; ++i) { - if (i >= other_picture->intrinsic_duration()) { - return false; - } - - note (PROGRESS, "Comparing video frame " + lexical_cast<string> (i) + " of " + lexical_cast<string> (_intrinsic_duration)); - shared_ptr<const MonoPictureFrame> frame_A = get_frame (i); - shared_ptr<const MonoPictureFrame> frame_B = other_picture->get_frame (i); - - if (!frame_buffer_equals ( - i, opt, note, - frame_A->j2k_data(), frame_A->j2k_size(), - frame_B->j2k_data(), frame_B->j2k_size() - )) { - return false; - } - } - - return true; -} - -shared_ptr<PictureAssetWriter> -MonoPictureAsset::start_write (bool overwrite) -{ - /* XXX: can't we use shared_ptr here? */ - return shared_ptr<MonoPictureAssetWriter> (new MonoPictureAssetWriter (this, overwrite)); -} - -string -MonoPictureAsset::cpl_node_name () const -{ - return "MainPicture"; -} - -int -MonoPictureAsset::edit_rate_factor () const -{ - return 1; -} diff --git a/src/mono_picture_asset.h b/src/mono_picture_asset.h deleted file mode 100644 index ae2df4d2..00000000 --- a/src/mono_picture_asset.h +++ /dev/null @@ -1,64 +0,0 @@ -/* - Copyright (C) 2012-2013 Carl Hetherington <cth@carlh.net> - - This program 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. - - This program 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 this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - -*/ - -#ifndef LIBDCP_MONO_PICTURE_ASSET_H -#define LIBDCP_MONO_PICTURE_ASSET_H - -#include "picture_asset.h" - -namespace libdcp { - -/** A 2D (monoscopic) picture asset */ -class MonoPictureAsset : public PictureAsset -{ -public: - MonoPictureAsset (boost::filesystem::path directory, boost::filesystem::path mxf_name); - - void read (); - - /** The following parameters must be set up (if required) before calling this: - * Interop mode (set_interop) - * Edit rate (set_edit_rate) - * MXF Metadata (set_metadata) - */ - void create (std::vector<boost::filesystem::path> const & files); - - /** The following parameters must be set up (if required) before calling this: - * Interop mode (set_interop) - * Edit rate (set_edit_rate) - * MXF Metadata (set_metadata) - */ - void create (boost::function<boost::filesystem::path (int)> get_path); - - /** Start a progressive write to a MonoPictureAsset */ - boost::shared_ptr<PictureAssetWriter> start_write (bool); - - boost::shared_ptr<const MonoPictureFrame> get_frame (int n) const; - bool equals (boost::shared_ptr<const Asset> other, EqualityOptions opt, boost::function<void (NoteType, std::string)> note) const; - -private: - boost::filesystem::path path_from_list (int f, std::vector<boost::filesystem::path> const & files) const; - void construct (boost::function<boost::filesystem::path (int)>, bool, MXFMetadata const &); - std::string cpl_node_name () const; - int edit_rate_factor () const; -}; - -} - -#endif diff --git a/src/mono_picture_frame.cc b/src/mono_picture_frame.cc index 890967d1..c2f8abb7 100644 --- a/src/mono_picture_frame.cc +++ b/src/mono_picture_frame.cc @@ -1,5 +1,5 @@ /* - Copyright (C) 2012 Carl Hetherington <cth@carlh.net> + Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -17,26 +17,30 @@ */ -#include <openjpeg.h> -#include "AS_DCP.h" -#include "KM_fileio.h" +/** @file src/mono_picture_frame.cc + * @brief MonoPictureFrame class. + */ + #include "mono_picture_frame.h" #include "exceptions.h" #include "argb_frame.h" -#include "lut.h" -#include "util.h" #include "gamma_lut.h" +#include "util.h" #include "rgb_xyz.h" +#include "KM_fileio.h" +#include "AS_DCP.h" +#include <openjpeg.h> #define DCI_GAMMA 2.6 using std::string; using boost::shared_ptr; -using namespace libdcp; +using namespace dcp; /** Make a picture frame from a 2D (monoscopic) asset. * @param mxf_path Path to the asset's MXF file. * @param n Frame within the asset, not taking EntryPoint into account. + * @param c Context for decryption, or 0. */ MonoPictureFrame::MonoPictureFrame (boost::filesystem::path mxf_path, int n, ASDCP::AESDecContext* c) { @@ -54,17 +58,20 @@ MonoPictureFrame::MonoPictureFrame (boost::filesystem::path mxf_path, int n, ASD } } +/** MonoPictureFrame destructor */ MonoPictureFrame::~MonoPictureFrame () { delete _buffer; } +/** @return Pointer to JPEG2000 data */ uint8_t const * MonoPictureFrame::j2k_data () const { return _buffer->RoData (); } +/** @return Size of JPEG2000 data in bytes */ int MonoPictureFrame::j2k_size () const { @@ -74,17 +81,18 @@ MonoPictureFrame::j2k_size () const /** @param reduce a factor by which to reduce the resolution * of the image, expressed as a power of two (pass 0 for no * reduction). + * @param srgb_gamma Reciprocal of output gamma to use after + * the conversion from XYZ to RGB. * * @return An ARGB representation of this frame. This is ARGB in the * Cairo sense, so that each pixel takes up 4 bytes; the first byte * is blue, second green, third red and fourth alpha (always 255). - * */ shared_ptr<ARGBFrame> MonoPictureFrame::argb_frame (int reduce, float srgb_gamma) const { return xyz_to_rgb ( decompress_j2k (const_cast<uint8_t*> (_buffer->RoData()), _buffer->Size(), reduce), - GammaLUT::cache.get (12, DCI_GAMMA), GammaLUT::cache.get (12, 1 / srgb_gamma) + GammaLUT::cache.get (12, DCI_GAMMA, false), GammaLUT::cache.get (12, 1 / srgb_gamma, false) ); } diff --git a/src/mono_picture_frame.h b/src/mono_picture_frame.h index 0eedfb95..cc6096cd 100644 --- a/src/mono_picture_frame.h +++ b/src/mono_picture_frame.h @@ -1,5 +1,5 @@ /* - Copyright (C) 2012-2013 Carl Hetherington <cth@carlh.net> + Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -17,10 +17,16 @@ */ +/** @file src/mono_picture_frame.h + * @brief MonoPictureFrame class. + */ + +#include "types.h" +#include <boost/shared_ptr.hpp> +#include <boost/noncopyable.hpp> +#include <boost/filesystem.hpp> #include <string> #include <stdint.h> -#include <boost/shared_ptr.hpp> -#include "types.h" namespace ASDCP { namespace JP2K { @@ -29,12 +35,14 @@ namespace ASDCP { class AESDecContext; } -namespace libdcp { +namespace dcp { class ARGBFrame; -/** A single frame of a 2D (monoscopic) picture asset */ -class MonoPictureFrame +/** @class MonoPictureFrame + * @brief A single frame of a 2D (monoscopic) picture asset. + */ +class MonoPictureFrame : public boost::noncopyable { public: MonoPictureFrame (boost::filesystem::path mxf_path, int n, ASDCP::AESDecContext *); diff --git a/src/mono_picture_mxf.cc b/src/mono_picture_mxf.cc new file mode 100644 index 00000000..4af24008 --- /dev/null +++ b/src/mono_picture_mxf.cc @@ -0,0 +1,137 @@ +/* + Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net> + + This program 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. + + This program 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 this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include "mono_picture_mxf.h" +#include "mono_picture_mxf_writer.h" +#include "AS_DCP.h" +#include "KM_fileio.h" +#include "exceptions.h" +#include "mono_picture_frame.h" +#include "compose.hpp" + +using std::string; +using std::vector; +using boost::shared_ptr; +using boost::dynamic_pointer_cast; +using namespace dcp; + +MonoPictureMXF::MonoPictureMXF (boost::filesystem::path file) + : PictureMXF (file) +{ + ASDCP::JP2K::MXFReader reader; + Kumu::Result_t r = reader.OpenRead (file.string().c_str()); + if (ASDCP_FAILURE (r)) { + boost::throw_exception (MXFFileError ("could not open MXF file for reading", file.string(), r)); + } + + ASDCP::JP2K::PictureDescriptor desc; + if (ASDCP_FAILURE (reader.FillPictureDescriptor (desc))) { + boost::throw_exception (DCPReadError ("could not read video MXF information")); + } + + read_picture_descriptor (desc); + + ASDCP::WriterInfo info; + if (ASDCP_FAILURE (reader.FillWriterInfo (info))) { + boost::throw_exception (DCPReadError ("could not read video MXF information")); + } + + read_writer_info (info); +} + +MonoPictureMXF::MonoPictureMXF (Fraction edit_rate) + : PictureMXF (edit_rate) +{ + +} + +shared_ptr<const MonoPictureFrame> +MonoPictureMXF::get_frame (int n) const +{ + return shared_ptr<const MonoPictureFrame> (new MonoPictureFrame (_file, n, _decryption_context)); +} + +bool +MonoPictureMXF::equals (shared_ptr<const Content> other, EqualityOptions opt, boost::function<void (NoteType, string)> note) const +{ + if (!MXF::equals (other, opt, note)) { + return false; + } + + ASDCP::JP2K::MXFReader reader_A; + Kumu::Result_t r = reader_A.OpenRead (_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; + r = reader_B.OpenRead (other->file().string().c_str()); + if (ASDCP_FAILURE (r)) { + boost::throw_exception (MXFFileError ("could not open MXF file for reading", other->file().string(), r)); + } + + ASDCP::JP2K::PictureDescriptor desc_A; + if (ASDCP_FAILURE (reader_A.FillPictureDescriptor (desc_A))) { + boost::throw_exception (DCPReadError ("could not read video MXF information")); + } + ASDCP::JP2K::PictureDescriptor desc_B; + if (ASDCP_FAILURE (reader_B.FillPictureDescriptor (desc_B))) { + boost::throw_exception (DCPReadError ("could not read video MXF information")); + } + + if (!descriptor_equals (desc_A, desc_B, note)) { + return false; + } + + shared_ptr<const MonoPictureMXF> other_picture = dynamic_pointer_cast<const MonoPictureMXF> (other); + assert (other_picture); + + for (int i = 0; i < _intrinsic_duration; ++i) { + if (i >= other_picture->intrinsic_duration()) { + return false; + } + + note (PROGRESS, String::compose ("Comparing video frame %1 of %2", i, _intrinsic_duration)); + shared_ptr<const MonoPictureFrame> frame_A = get_frame (i); + shared_ptr<const MonoPictureFrame> frame_B = other_picture->get_frame (i); + + if (!frame_buffer_equals ( + i, opt, note, + frame_A->j2k_data(), frame_A->j2k_size(), + frame_B->j2k_data(), frame_B->j2k_size() + )) { + return false; + } + } + + return true; +} + +shared_ptr<PictureMXFWriter> +MonoPictureMXF::start_write (boost::filesystem::path file, Standard standard, bool overwrite) +{ + /* XXX: can't we use shared_ptr here? */ + return shared_ptr<MonoPictureMXFWriter> (new MonoPictureMXFWriter (this, file, standard, overwrite)); +} + +string +MonoPictureMXF::cpl_node_name () const +{ + return "MainPicture"; +} diff --git a/src/mono_picture_mxf.h b/src/mono_picture_mxf.h new file mode 100644 index 00000000..c02a63c1 --- /dev/null +++ b/src/mono_picture_mxf.h @@ -0,0 +1,62 @@ +/* + Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net> + + This program 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. + + This program 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 this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#ifndef LIBDCP_MONO_PICTURE_MXF_H +#define LIBDCP_MONO_PICTURE_MXF_H + +#include "picture_mxf.h" + +namespace dcp { + +class MonoPictureMXFWriter; + +/** @class MonoPictureMXF + * @brief A 2D (monoscopic) picture MXF. + */ +class MonoPictureMXF : public PictureMXF +{ +public: + /** Create a MonoPictureMXF by reading a file. + * @param file MXF file to read. + */ + MonoPictureMXF (boost::filesystem::path file); + + /** Create a MonoPictureMXF with a given edit rate. + * @param edit_rate Edit rate (i.e. frame rate) in frames per second. + */ + MonoPictureMXF (Fraction edit_rate); + + /** Start a progressive write to a MonoPictureMXF */ + boost::shared_ptr<PictureMXFWriter> start_write (boost::filesystem::path, Standard standard, bool); + + bool equals ( + boost::shared_ptr<const Content> other, + EqualityOptions opt, + boost::function<void (NoteType, std::string)> note + ) const; + + boost::shared_ptr<const MonoPictureFrame> get_frame (int n) const; + +private: + std::string cpl_node_name () const; +}; + +} + +#endif diff --git a/src/mono_picture_asset_writer.cc b/src/mono_picture_mxf_writer.cc index 6f0ba973..5cc424e1 100644 --- a/src/mono_picture_asset_writer.cc +++ b/src/mono_picture_mxf_writer.cc @@ -1,5 +1,5 @@ /* - Copyright (C) 2012-2013 Carl Hetherington <cth@carlh.net> + Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -17,22 +17,25 @@ */ +/** @file src/mono_picture_mxf_writer.cc + * @brief MonoPictureMXFWriter class + */ + #include "AS_DCP.h" #include "KM_fileio.h" -#include "mono_picture_asset_writer.h" +#include "mono_picture_mxf_writer.h" #include "exceptions.h" -#include "picture_asset.h" +#include "picture_mxf.h" -#include "picture_asset_writer_common.cc" +#include "picture_mxf_writer_common.cc" using std::istream; using std::ostream; using std::string; using boost::shared_ptr; -using boost::lexical_cast; -using namespace libdcp; +using namespace dcp; -struct MonoPictureAssetWriter::ASDCPState : public ASDCPStateBase +struct MonoPictureMXFWriter::ASDCPState : public ASDCPStateBase { ASDCP::JP2K::MXFWriter mxf_writer; }; @@ -40,21 +43,22 @@ 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, bool overwrite) - : PictureAssetWriter (asset, overwrite) - , _state (new MonoPictureAssetWriter::ASDCPState) +MonoPictureMXFWriter::MonoPictureMXFWriter (PictureMXF* asset, boost::filesystem::path file, Standard standard, bool overwrite) + : PictureMXFWriter (asset, file, standard, overwrite) + , _state (new MonoPictureMXFWriter::ASDCPState) { _state->encryption_context = asset->encryption_context (); } void -MonoPictureAssetWriter::start (uint8_t* data, int size) +MonoPictureMXFWriter::start (uint8_t* data, int size) { - libdcp::start (this, _state, _asset, data, size); + dcp::start (this, _state, _standard, _picture_mxf, data, size); + _picture_mxf->set_frame_rate (_picture_mxf->edit_rate()); } FrameInfo -MonoPictureAssetWriter::write (uint8_t* data, int size) +MonoPictureMXFWriter::write (uint8_t* data, int size) { assert (!_finalized); @@ -71,7 +75,7 @@ MonoPictureAssetWriter::write (uint8_t* data, int size) string hash; ASDCP::Result_t const r = _state->mxf_writer.WriteFrame (_state->frame_buffer, _state->encryption_context, 0, &hash); if (ASDCP_FAILURE (r)) { - boost::throw_exception (MXFFileError ("error in writing video MXF", _asset->path().string(), r)); + boost::throw_exception (MXFFileError ("error in writing video MXF", _file.string(), r)); } ++_frames_written; @@ -79,31 +83,27 @@ MonoPictureAssetWriter::write (uint8_t* data, int size) } void -MonoPictureAssetWriter::fake_write (int size) +MonoPictureMXFWriter::fake_write (int size) { assert (_started); assert (!_finalized); Kumu::Result_t r = _state->mxf_writer.FakeWriteFrame (size); if (ASDCP_FAILURE (r)) { - boost::throw_exception (MXFFileError ("error in writing video MXF", _asset->path().string(), r)); + boost::throw_exception (MXFFileError ("error in writing video MXF", _mxf->file().string(), r)); } ++_frames_written; } void -MonoPictureAssetWriter::finalize () +MonoPictureMXFWriter::finalize () { - assert (!_finalized); - Kumu::Result_t r = _state->mxf_writer.Finalize(); if (ASDCP_FAILURE (r)) { - boost::throw_exception (MXFFileError ("error in finalizing video MXF", _asset->path().string(), r)); + boost::throw_exception (MXFFileError ("error in finalizing video MXF", _mxf->file().string(), r)); } - _finalized = true; - _asset->set_intrinsic_duration (_frames_written); - _asset->set_duration (_frames_written); + PictureMXFWriter::finalize (); } diff --git a/src/mono_picture_asset_writer.h b/src/mono_picture_mxf_writer.h index e3922623..065171c2 100644 --- a/src/mono_picture_asset_writer.h +++ b/src/mono_picture_mxf_writer.h @@ -1,5 +1,5 @@ /* - Copyright (C) 2012-2013 Carl Hetherington <cth@carlh.net> + Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -17,26 +17,33 @@ */ +/** @file src/mono_picture_mxf_writer.h + * @brief MonoPictureMXFWriter class + */ + +#ifndef LIBDCP_MONO_PICTURE_MXF_WRITER_H +#define LIBDCP_MONO_PICTURE_MXF_WRITER_H + +#include "picture_mxf_writer.h" +#include <boost/shared_ptr.hpp> +#include <boost/utility.hpp> #include <stdint.h> #include <string> #include <fstream> -#include <boost/shared_ptr.hpp> -#include <boost/utility.hpp> -#include "picture_asset_writer.h" -namespace libdcp { +namespace dcp { -/** A helper class for writing to MonoPictureAssets progressively (i.e. writing frame-by-frame, - * rather than giving libdcp all the frames in one go). +/** @class MonoPictureMXFWriter + * @brief A helper class for writing to MonoPictureMXFs * - * Objects of this class can only be created with MonoPictureAsset::start_write(). + * Objects of this class can only be created with MonoPictureMXF::start_write(). * * Frames can be written to the MonoPictureAsset by calling write() with a JPEG2000 image * (a verbatim .j2c file). finalize() must be called after the last frame has been written. * The action of finalize() can't be done in MonoPictureAssetWriter's destructor as it may * throw an exception. */ -class MonoPictureAssetWriter : public PictureAssetWriter +class MonoPictureMXFWriter : public PictureMXFWriter { public: FrameInfo write (uint8_t *, int); @@ -44,9 +51,9 @@ public: void finalize (); private: - friend class MonoPictureAsset; + friend class MonoPictureMXF; - MonoPictureAssetWriter (PictureAsset *, bool); + MonoPictureMXFWriter (PictureMXF *, boost::filesystem::path file, Standard standard, bool); void start (uint8_t *, int); /* do this with an opaque pointer so we don't have to include @@ -58,3 +65,5 @@ private: }; } + +#endif diff --git a/src/mxf_asset.cc b/src/mxf.cc index c2428a3c..e1ed9650 100644 --- a/src/mxf_asset.cc +++ b/src/mxf.cc @@ -1,5 +1,5 @@ /* - Copyright (C) 2012 Carl Hetherington <cth@carlh.net> + Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -21,18 +21,18 @@ * @brief Parent class for assets of DCPs made up of MXF files. */ -#include <iostream> -#include <boost/filesystem.hpp> -#include <boost/lexical_cast.hpp> -#include <libxml++/nodes/element.h> #include "AS_DCP.h" #include "KM_prng.h" #include "KM_util.h" -#include "mxf_asset.h" +#include "mxf.h" #include "util.h" #include "metadata.h" #include "exceptions.h" #include "kdm.h" +#include "compose.hpp" +#include <libxml++/nodes/element.h> +#include <boost/filesystem.hpp> +#include <iostream> using std::string; using std::list; @@ -40,38 +40,44 @@ using std::pair; using boost::shared_ptr; using boost::lexical_cast; using boost::dynamic_pointer_cast; -using namespace libdcp; +using namespace dcp; + +MXF::MXF (Fraction edit_rate) + : Content (edit_rate) + , _encryption_context (0) + , _decryption_context (0) +{ -MXFAsset::MXFAsset (boost::filesystem::path directory, boost::filesystem::path file_name) - : Asset (directory, file_name) - , _progress (0) +} + +MXF::MXF (boost::filesystem::path file) + : Content (file) , _encryption_context (0) , _decryption_context (0) - , _interop (false) { } -MXFAsset::~MXFAsset () +MXF::~MXF () { delete _encryption_context; delete _decryption_context; } void -MXFAsset::fill_writer_info (ASDCP::WriterInfo* writer_info) +MXF::fill_writer_info (ASDCP::WriterInfo* writer_info, Standard standard) { writer_info->ProductVersion = _metadata.product_version; writer_info->CompanyName = _metadata.company_name; writer_info->ProductName = _metadata.product_name.c_str(); - if (_interop) { + if (standard == INTEROP) { writer_info->LabelSetType = ASDCP::LS_MXF_INTEROP; } else { writer_info->LabelSetType = ASDCP::LS_MXF_SMPTE; } unsigned int c; - Kumu::hex2bin (_uuid.c_str(), writer_info->AssetUUID, Kumu::UUID_Length, &c); + Kumu::hex2bin (_id.c_str(), writer_info->AssetUUID, Kumu::UUID_Length, &c); assert (c == Kumu::UUID_Length); if (_key) { @@ -85,19 +91,19 @@ MXFAsset::fill_writer_info (ASDCP::WriterInfo* writer_info) } bool -MXFAsset::equals (shared_ptr<const Asset> other, EqualityOptions opt, boost::function<void (NoteType, string)> note) const +MXF::equals (shared_ptr<const Content> other, EqualityOptions opt, boost::function<void (NoteType, string)> note) const { - if (!Asset::equals (other, opt, note)) { + if (!Content::equals (other, opt, note)) { return false; } - shared_ptr<const MXFAsset> other_mxf = dynamic_pointer_cast<const MXFAsset> (other); + shared_ptr<const MXF> other_mxf = dynamic_pointer_cast<const MXF> (other); if (!other_mxf) { note (ERROR, "comparing an MXF asset with a non-MXF asset"); return false; } - if (_file_name != other_mxf->_file_name) { + if (_file != other_mxf->file ()) { note (ERROR, "MXF names differ"); if (!opt.mxf_names_can_differ) { return false; @@ -107,27 +113,13 @@ MXFAsset::equals (shared_ptr<const Asset> other, EqualityOptions opt, boost::fun return true; } +/** Set the (private) key that will be used to encrypt or decrypt this MXF's content. + * This is the top-secret key that is distributed (itself encrypted) to cinemas + * via Key Delivery Messages (KDMs). + * @param key Key to use. + */ void -MXFAsset::write_to_cpl (xmlpp::Element* node) const -{ - pair<string, string> const attr = cpl_node_attribute (); - xmlpp::Element* a = node->add_child (cpl_node_name ()); - if (!attr.first.empty ()) { - a->set_attribute (attr.first, attr.second); - } - a->add_child ("Id")->add_child_text ("urn:uuid:" + _uuid); - a->add_child ("AnnotationText")->add_child_text (_file_name.string ()); - a->add_child ("EditRate")->add_child_text (lexical_cast<string> (_edit_rate) + " 1"); - a->add_child ("IntrinsicDuration")->add_child_text (lexical_cast<string> (_intrinsic_duration)); - a->add_child ("EntryPoint")->add_child_text (lexical_cast<string> (_entry_point)); - a->add_child ("Duration")->add_child_text (lexical_cast<string> (_duration)); - if (!_key_id.empty ()) { - a->add_child("KeyId")->add_child_text ("urn:uuid:" + _key_id); - } -} - -void -MXFAsset::set_key (Key key) +MXF::set_key (Key key) { _key = key; @@ -153,3 +145,24 @@ MXFAsset::set_key (Key key) throw MiscError ("could not set up CBC initialization vector"); } } + +void +MXF::read_writer_info (ASDCP::WriterInfo const & info) +{ + char buffer[64]; + Kumu::bin2UUIDhex (info.AssetUUID, 16, buffer, 64); + _id = buffer; +} + +string +MXF::pkl_type (Standard standard) const +{ + switch (standard) { + case INTEROP: + return String::compose ("application/x-smpte-mxf;asdcpKind=%1", asdcp_kind ()); + case SMPTE: + return "application/x-smpte-mxf"; + default: + assert (false); + } +} diff --git a/src/mxf_asset.h b/src/mxf.h index 4eafdcba..c5ecd1b7 100644 --- a/src/mxf_asset.h +++ b/src/mxf.h @@ -1,5 +1,5 @@ /* - Copyright (C) 2012 Carl Hetherington <cth@carlh.net> + Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -17,13 +17,13 @@ */ -#ifndef LIBDCP_MXF_ASSET_H -#define LIBDCP_MXF_ASSET_H +#ifndef LIBDCP_MXF_H +#define LIBDCP_MXF_H -#include <boost/signals2.hpp> -#include "asset.h" +#include "content.h" #include "key.h" #include "metadata.h" +#include <boost/signals2.hpp> namespace ASDCP { class AESEncContext; @@ -34,93 +34,88 @@ namespace ASDCP { #undef Key #undef set_key -namespace libdcp +namespace dcp { class MXFMetadata; -/** @brief Parent class for assets which have MXF files */ -class MXFAsset : public Asset +/** @class MXF + * @brief Parent class for classes which represent MXF files. + */ +class MXF : public Content { public: - /** Construct an MXFAsset. - * This class will not write anything to disk in this constructor, but subclasses may. - * - * @param directory Directory where MXF file is. - * @param file_name Name of MXF file. - */ - MXFAsset (boost::filesystem::path directory, boost::filesystem::path file_name); - - ~MXFAsset (); + MXF (Fraction edit_rate); + MXF (boost::filesystem::path file); + ~MXF (); - virtual bool equals (boost::shared_ptr<const Asset> other, EqualityOptions opt, boost::function<void (NoteType, std::string)> note) const; - virtual void write_to_cpl (xmlpp::Element *) const; + /** @return the 4-character key type for this MXF (MDIK, MDAK, etc.) */ virtual std::string key_type () const = 0; + bool equals ( + boost::shared_ptr<const Content> other, + EqualityOptions opt, + boost::function<void (NoteType, std::string)> note + ) const; + /** Fill in a ADSCP::WriteInfo struct. * @param w struct to fill in. + * @param standard INTEROP or SMPTE. */ - void fill_writer_info (ASDCP::WriterInfo* w); - - void set_progress (boost::signals2::signal<void (float)>* progress) { - _progress = progress; - } + void fill_writer_info (ASDCP::WriterInfo* w, Standard standard); + /** @return true if the data is encrypted */ bool encrypted () const { return !_key_id.empty (); } + /** Set the ID of the key that is used for encryption/decryption. + * @param i key ID. + */ void set_key_id (std::string i) { _key_id = i; } + /** @return the ID of the key used for encryption/decryption, or an empty string */ std::string key_id () const { return _key_id; } - + void set_key (Key); + /** @return encryption/decryption key, if one has been set */ boost::optional<Key> key () const { return _key; } + /** @return encryption context, set up with any key that has been passed to set_key() */ ASDCP::AESEncContext* encryption_context () const { return _encryption_context; } + /** Set the metadata that is written to the MXF file. + * @param m Metadata. + */ void set_metadata (MXFMetadata m) { _metadata = m; } + /** @return metadata from the MXF file */ MXFMetadata metadata () const { return _metadata; } - /** Set whether or not the asset should be written in Interop mode. - * @param i true to use interop. - */ - void set_interop (bool i) { - _interop = i; - } - - bool interop () const { - return _interop; - } - protected: - virtual std::string cpl_node_name () const = 0; - virtual std::pair<std::string, std::string> cpl_node_attribute () const { - return std::make_pair ("", ""); - } + std::string pkl_type (Standard standard) const; + void read_writer_info (ASDCP::WriterInfo const &); - /** Signal to emit to report progress, or 0 */ - boost::signals2::signal<void (float)>* _progress; ASDCP::AESEncContext* _encryption_context; ASDCP::AESDecContext* _decryption_context; + /** ID of the key used for encryption/decryption, or an empty string */ std::string _key_id; + /** Key used for encryption/decryption, if there is one */ boost::optional<Key> _key; MXFMetadata _metadata; - bool _interop; }; } diff --git a/src/mxf_writer.cc b/src/mxf_writer.cc new file mode 100644 index 00000000..374e6590 --- /dev/null +++ b/src/mxf_writer.cc @@ -0,0 +1,53 @@ +/* + Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net> + + This program 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. + + This program 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 this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +/** @file src/mxf_writer.h + * @brief MXFWriter class. + */ + +#include "mxf_writer.h" +#include "mxf.h" + +using namespace dcp; + +/** Create an MXFWriter. + * @param mxf MXF that we are writing. + * @param file File to write to. + */ +MXFWriter::MXFWriter (MXF* mxf, boost::filesystem::path file) + : _mxf (mxf) + , _file (file) + , _frames_written (0) + , _finalized (false) +{ + mxf->set_file (file); +} + +MXFWriter::~MXFWriter () +{ + assert (_finalized); +} + +void +MXFWriter::finalize () +{ + assert (!_finalized); + _finalized = true; + _mxf->_intrinsic_duration = _frames_written; +} diff --git a/src/mxf_writer.h b/src/mxf_writer.h new file mode 100644 index 00000000..b4e20f33 --- /dev/null +++ b/src/mxf_writer.h @@ -0,0 +1,62 @@ +/* + Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net> + + This program 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. + + This program 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 this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +/** @file src/mxf_writer.h + * @brief MXFWriter class. + */ + +#ifndef LIBDCP_MXF_WRITER_H +#define LIBDCP_MXF_WRITER_H + +#include <boost/filesystem.hpp> + +namespace dcp { + +class MXF; + +/** @class MXFWriter + * @brief Parent class for classes which can write MXF files. + * + * The MXFWriter lasts for the duration of the write and is then discarded. + * They can only be created by calling start_write() on an MXF object. + */ +class MXFWriter : public boost::noncopyable +{ +public: + virtual ~MXFWriter (); + virtual void finalize (); + +protected: + MXFWriter (MXF* mxf, boost::filesystem::path file); + + /** MXF that we are writing */ + MXF* _mxf; + /** File that we are writing to */ + boost::filesystem::path _file; + /** Number of `frames' written so far; the definition of a frame + * varies depending on the subclass. + */ + int64_t _frames_written; + /** true if finalize() has been called on this object */ + bool _finalized; +}; + +} + +#endif diff --git a/src/object.cc b/src/object.cc new file mode 100644 index 00000000..315e501a --- /dev/null +++ b/src/object.cc @@ -0,0 +1,44 @@ +/* + Copyright (C) 2014 Carl Hetherington <cth@carlh.net> + + This program 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. + + This program 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 this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +/** @file src/object.cc + * @brief Object class. + */ + +#include "object.h" +#include "util.h" + +using std::string; +using namespace dcp; + +/** Create an Object with a random ID. */ +Object::Object () + : _id (make_uuid ()) +{ + +} + +/** Create an Object with a given ID. + * @param id ID to use. + */ +Object::Object (string id) + : _id (id) +{ + +} diff --git a/src/srgb_linearised_gamma_lut.cc b/src/object.h index 0d3650f4..82598c58 100644 --- a/src/srgb_linearised_gamma_lut.cc +++ b/src/object.h @@ -1,5 +1,5 @@ /* - Copyright (C) 2012-2013 Carl Hetherington <cth@carlh.net> + Copyright (C) 2014 Carl Hetherington <cth@carlh.net> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -17,22 +17,38 @@ */ -#include "srgb_linearised_gamma_lut.h" +/** @file src/object.h + * @brief Object class. + */ -using namespace libdcp; +#ifndef LIBDCP_OBJECT_H +#define LIBDCP_OBJECT_H -LUTCache<SRGBLinearisedGammaLUT> SRGBLinearisedGammaLUT::cache; +#include <boost/noncopyable.hpp> +#include <string> -SRGBLinearisedGammaLUT::SRGBLinearisedGammaLUT (int bits, float gamma) - : LUT (bits, gamma) +namespace dcp { + +/** @class Object + * @brief Some part of a DCP that has a UUID. + */ +class Object : public boost::noncopyable { - int const bit_length = pow (2, bits); - for (int i = 0; i < bit_length; ++i) { - float const p = static_cast<float> (i) / (bit_length - 1); - if (p > 0.04045) { - _lut[i] = pow ((p + 0.055) / 1.055, gamma); - } else { - _lut[i] = p / 12.92; - } +public: + Object (); + Object (std::string id); + virtual ~Object () {} + + /** @return ID */ + std::string id () const { + return _id; } + +protected: + /** ID */ + std::string _id; +}; + } + +#endif diff --git a/src/parse/asset_map.cc b/src/parse/asset_map.cc deleted file mode 100644 index 484c2710..00000000 --- a/src/parse/asset_map.cc +++ /dev/null @@ -1,79 +0,0 @@ -/* - Copyright (C) 2012 Carl Hetherington <cth@carlh.net> - - This program 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. - - This program 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 this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - -*/ - -/** @file src/asset_map.cc - * @brief Classes used to parse a AssetMap. - */ - -#include <boost/algorithm/string.hpp> -#include "asset_map.h" -#include "../util.h" -#include "../xml.h" - -using std::string; -using std::list; -using boost::shared_ptr; -using namespace libdcp::parse; - -AssetMap::AssetMap (string file) -{ - cxml::Document f ("AssetMap"); - f.read_file (file); - - id = f.string_child ("Id"); - creator = f.string_child ("Creator"); - volume_count = f.number_child<int64_t> ("VolumeCount"); - issue_date = f.string_child ("IssueDate"); - issuer = f.string_child ("Issuer"); - assets = type_grand_children<AssetMapAsset> (f, "AssetList", "Asset"); -} - -AssetMapAsset::AssetMapAsset (shared_ptr<const cxml::Node> node) -{ - id = node->string_child ("Id"); - packing_list = node->optional_string_child ("PackingList").get_value_or (""); - chunks = type_grand_children<Chunk> (node, "ChunkList", "Chunk"); -} - -Chunk::Chunk (shared_ptr<const cxml::Node> node) -{ - path = node->string_child ("Path"); - - string const prefix = "file://"; - - if (boost::algorithm::starts_with (path, prefix)) { - path = path.substr (prefix.length()); - } - - volume_index = node->optional_number_child<int64_t> ("VolumeIndex").get_value_or (0); - offset = node->optional_number_child<int64_t> ("Offset").get_value_or (0); - length = node->optional_number_child<int64_t> ("Length").get_value_or (0); -} - -shared_ptr<AssetMapAsset> -AssetMap::asset_from_id (string id) const -{ - for (list<shared_ptr<AssetMapAsset> >::const_iterator i = assets.begin (); i != assets.end(); ++i) { - if ((*i)->id == id) { - return *i; - } - } - - return shared_ptr<AssetMapAsset> (); -} diff --git a/src/parse/asset_map.h b/src/parse/asset_map.h deleted file mode 100644 index af3e8918..00000000 --- a/src/parse/asset_map.h +++ /dev/null @@ -1,81 +0,0 @@ -/* - Copyright (C) 2012 Carl Hetherington <cth@carlh.net> - - This program 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. - - This program 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 this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - -*/ - -/** @file src/asset_map.h - * @brief Classes used to parse a AssetMap. - */ - -#include <stdint.h> -#include <boost/shared_ptr.hpp> -#include <libcxml/cxml.h> - -namespace libdcp { - -namespace parse { - -/** @class Chunk - * @brief A simple parser for and representation of a \<Chunk\> node within an asset map. - */ -class Chunk -{ -public: - Chunk (); - Chunk (boost::shared_ptr<const cxml::Node> node); - - std::string path; - int64_t volume_index; - int64_t offset; - int64_t length; -}; - -/** @class AssetMapAsset - * @brief A simple parser for and representation of an \<AssetMap\> node within an asset map. - */ -class AssetMapAsset -{ -public: - AssetMapAsset (); - AssetMapAsset (boost::shared_ptr<const cxml::Node> node); - - std::string id; - std::string packing_list; - std::list<boost::shared_ptr<Chunk> > chunks; -}; - -/** @class AssetMap - * @brief A simple parser for and representation of an asset map file. - */ -class AssetMap -{ -public: - AssetMap (std::string file); - - boost::shared_ptr<AssetMapAsset> asset_from_id (std::string id) const; - - std::string id; - std::string creator; - int64_t volume_count; - std::string issue_date; - std::string issuer; - std::list<boost::shared_ptr<AssetMapAsset> > assets; -}; - -} - -} diff --git a/src/parse/cpl.cc b/src/parse/cpl.cc deleted file mode 100644 index f6ce434c..00000000 --- a/src/parse/cpl.cc +++ /dev/null @@ -1,151 +0,0 @@ -/* - Copyright (C) 2012 Carl Hetherington <cth@carlh.net> - - This program 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. - - This program 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 this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - -*/ - -/** @file src/cpl_file.cc - * @brief Classes used to parse a CPL. - */ - -#include <iostream> -#include "cpl.h" -#include "../xml.h" -#include "../util.h" - -using std::string; -using std::bad_cast; -using boost::shared_ptr; -using namespace libdcp::parse; - -CPL::CPL (string file) -{ - cxml::Document f ("CompositionPlaylist"); - f.read_file (file); - - id = f.string_child ("Id"); - annotation_text = f.optional_string_child ("AnnotationText").get_value_or (""); - issue_date = f.string_child ("IssueDate"); - creator = f.optional_string_child ("Creator").get_value_or (""); - content_title_text = f.string_child ("ContentTitleText"); - content_kind = content_kind_from_string (f.string_child ("ContentKind")); - content_version = optional_type_child<ContentVersion> (f, "ContentVersion"); - f.ignore_child ("RatingList"); - reels = type_grand_children<Reel> (f, "ReelList", "Reel"); - - f.ignore_child ("Issuer"); - f.ignore_child ("Signer"); - f.ignore_child ("Signature"); - - f.done (); -} - -ContentVersion::ContentVersion (shared_ptr<const cxml::Node> node) -{ - id = node->optional_string_child ("Id").get_value_or (""); - label_text = node->string_child ("LabelText"); - node->done (); -} - -Reel::Reel (shared_ptr<const cxml::Node> node) -{ - id = node->string_child ("Id"); - asset_list = type_child<CPLAssetList> (node, "AssetList"); - - node->ignore_child ("AnnotationText"); - node->done (); -} - -CPLAssetList::CPLAssetList (shared_ptr<const cxml::Node> node) -{ - main_picture = optional_type_child<MainPicture> (node, "MainPicture"); - main_stereoscopic_picture = optional_type_child<MainStereoscopicPicture> (node, "MainStereoscopicPicture"); - main_sound = optional_type_child<MainSound> (node, "MainSound"); - main_subtitle = optional_type_child<MainSubtitle> (node, "MainSubtitle"); - - node->done (); -} - -MainPicture::MainPicture (shared_ptr<const cxml::Node> node) - : Picture (node) -{ - -} - -MainStereoscopicPicture::MainStereoscopicPicture (shared_ptr<const cxml::Node> node) - : Picture (node) -{ - -} - -Picture::Picture (shared_ptr<const cxml::Node> node) -{ - id = node->string_child ("Id"); - annotation_text = node->optional_string_child ("AnnotationText").get_value_or (""); - edit_rate = Fraction (node->string_child ("EditRate")); - intrinsic_duration = node->number_child<int64_t> ("IntrinsicDuration"); - entry_point = node->number_child<int64_t> ("EntryPoint"); - duration = node->number_child<int64_t> ("Duration"); - frame_rate = Fraction (node->string_child ("FrameRate")); - try { - screen_aspect_ratio = Fraction (node->string_child ("ScreenAspectRatio")); - } catch (XMLError& e) { - /* Maybe it's not a fraction */ - } - try { - float f = node->number_child<float> ("ScreenAspectRatio"); - screen_aspect_ratio = Fraction (f * 1000, 1000); - } catch (bad_cast& e) { - - } - - key_id = node->optional_string_child ("KeyId").get_value_or (""); - - node->ignore_child ("Hash"); - - node->done (); -} - -MainSound::MainSound (shared_ptr<const cxml::Node> node) -{ - id = node->string_child ("Id"); - annotation_text = node->optional_string_child ("AnnotationText").get_value_or (""); - edit_rate = Fraction (node->string_child ("EditRate")); - intrinsic_duration = node->number_child<int64_t> ("IntrinsicDuration"); - entry_point = node->number_child<int64_t> ("EntryPoint"); - duration = node->number_child<int64_t> ("Duration"); - key_id = node->optional_string_child ("KeyId").get_value_or (""); - - node->ignore_child ("Hash"); - node->ignore_child ("Language"); - - node->done (); -} - -MainSubtitle::MainSubtitle (shared_ptr<const cxml::Node> node) -{ - id = node->string_child ("Id"); - annotation_text = node->optional_string_child ("AnnotationText").get_value_or (""); - edit_rate = Fraction (node->string_child ("EditRate")); - intrinsic_duration = node->number_child<int64_t> ("IntrinsicDuration"); - entry_point = node->number_child<int64_t> ("EntryPoint"); - duration = node->number_child<int64_t> ("Duration"); - - node->ignore_child ("Hash"); - node->ignore_child ("Language"); - - node->done (); -} diff --git a/src/parse/cpl.h b/src/parse/cpl.h deleted file mode 100644 index 04bf9351..00000000 --- a/src/parse/cpl.h +++ /dev/null @@ -1,163 +0,0 @@ -/* - Copyright (C) 2012 Carl Hetherington <cth@carlh.net> - - This program 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. - - This program 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 this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - -*/ - -/** @file src/parse/cpl.h - * @brief Classes used to parse a CPL. - */ - -#include <stdint.h> -#include <boost/shared_ptr.hpp> -#include <libcxml/cxml.h> -#include "../types.h" - -namespace libdcp { - -namespace parse { - -/** @brief A simple representation of a CPL \<Picture\> node */ -class Picture -{ -public: - Picture () {} - Picture (boost::shared_ptr<const cxml::Node> node); - - std::string id; - std::string annotation_text; - Fraction edit_rate; - /** Duration of the whole thing */ - int64_t intrinsic_duration; - /** Start point in frames */ - int64_t entry_point; - /** Duration that will actually play */ - int64_t duration; - Fraction frame_rate; - Fraction screen_aspect_ratio; - std::string key_id; -}; - - -/** @brief A simple parser for and representation of a CPL \<MainPicture\> node */ -class MainPicture : public Picture -{ -public: - MainPicture () {} - MainPicture (boost::shared_ptr<const cxml::Node> node); -}; - -/** @brief A simple parser for and representation of a CPL \<MainStereoscopicPicture\> node */ -class MainStereoscopicPicture : public Picture -{ -public: - MainStereoscopicPicture () {} - MainStereoscopicPicture (boost::shared_ptr<const cxml::Node> node); -}; - -/** @brief A simple parser for and representation of a CPL \<MainSound\> node */ -class MainSound -{ -public: - MainSound () {} - MainSound (boost::shared_ptr<const cxml::Node> node); - - std::string id; - std::string annotation_text; - Fraction edit_rate; - int64_t intrinsic_duration; - int64_t entry_point; - int64_t duration; - std::string key_id; -}; - -/** @brief A simple parser for and representation of a CPL \<MainSubtitle\> node */ -class MainSubtitle -{ -public: - MainSubtitle () {} - MainSubtitle (boost::shared_ptr<const cxml::Node> node); - - std::string id; - std::string annotation_text; - Fraction edit_rate; - int64_t intrinsic_duration; - int64_t entry_point; - int64_t duration; -}; - -/** @brief A simple parser for and representation of a CPL \<AssetList\> node */ -class CPLAssetList -{ -public: - CPLAssetList () {} - CPLAssetList (boost::shared_ptr<const cxml::Node> node); - - boost::shared_ptr<MainPicture> main_picture; - boost::shared_ptr<MainStereoscopicPicture> main_stereoscopic_picture; - boost::shared_ptr<MainSound> main_sound; - boost::shared_ptr<MainSubtitle> main_subtitle; -}; - -/** @brief A simple parser for and representation of a CPL \<Reel\> node */ -class Reel -{ -public: - Reel () {} - Reel (boost::shared_ptr<const cxml::Node> node); - - std::string id; - boost::shared_ptr<CPLAssetList> asset_list; -}; - - -/** @brief A simple parser for and representation of a CPL \<ContentVersion\> node */ -class ContentVersion -{ -public: - ContentVersion () {} - ContentVersion (boost::shared_ptr<const cxml::Node> node); - - std::string id; - std::string label_text; -}; - -/** @class CPL - * @brief Class to parse a CPL - * - * This class is used to parse XML CPL files. It is rarely necessary - * for the caller to use it outside libdcp. - */ -class CPL -{ -public: - /** Parse a CPL XML file into our member variables */ - CPL (std::string file); - - std::string id; - std::string annotation_text; - std::string issue_date; - std::string creator; - std::string content_title_text; - ContentKind content_kind; - boost::shared_ptr<ContentVersion> content_version; - std::list<boost::shared_ptr<Reel> > reels; -}; - -} - -} - diff --git a/src/parse/pkl.cc b/src/parse/pkl.cc deleted file mode 100644 index bbf070ae..00000000 --- a/src/parse/pkl.cc +++ /dev/null @@ -1,52 +0,0 @@ -/* - Copyright (C) 2012 Carl Hetherington <cth@carlh.net> - - This program 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. - - This program 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 this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - -*/ - -/** @file src/pkl_file.cc - * @brief Classes used to parse a PKL. - */ - -#include <iostream> -#include "pkl.h" - -using namespace std; -using namespace boost; -using namespace libdcp::parse; - -PKL::PKL (string file) -{ - cxml::Document f ("PackingList"); - f.read_file (file); - - id = f.string_child ("Id"); - annotation_text = f.optional_string_child ("AnnotationText").get_value_or (""); - issue_date = f.string_child ("IssueDate"); - issuer = f.string_child ("Issuer"); - creator = f.string_child ("Creator"); - assets = type_grand_children<PKLAsset> (f, "AssetList", "Asset"); -} - -PKLAsset::PKLAsset (boost::shared_ptr<const cxml::Node> node) -{ - id = node->string_child ("Id"); - annotation_text = node->optional_string_child ("AnnotationText").get_value_or (""); - hash = node->string_child ("Hash"); - size = node->number_child<int64_t> ("Size"); - type = node->string_child ("Type"); - original_file_name = node->optional_string_child ("OriginalFileName").get_value_or (""); -} diff --git a/src/picture_asset.cc b/src/picture_mxf.cc index b2eecee5..4a8e477b 100644 --- a/src/picture_asset.cc +++ b/src/picture_mxf.cc @@ -1,5 +1,5 @@ /* - Copyright (C) 2012 Carl Hetherington <cth@carlh.net> + Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -17,25 +17,21 @@ */ -/** @file src/picture_asset.cc - * @brief An asset made up of JPEG2000 files - */ - +#include "picture_mxf.h" +#include "util.h" +#include "exceptions.h" +#include "xyz_frame.h" +#include "picture_mxf_writer.h" +#include "compose.hpp" +#include "AS_DCP.h" +#include "KM_fileio.h" +#include <libxml++/nodes/element.h> +#include <openjpeg.h> +#include <boost/filesystem.hpp> #include <list> #include <stdexcept> #include <iostream> #include <sstream> -#include <boost/filesystem.hpp> -#include <boost/lexical_cast.hpp> -#include <openjpeg.h> -#include <libxml++/nodes/element.h> -#include "AS_DCP.h" -#include "KM_fileio.h" -#include "picture_asset.h" -#include "util.h" -#include "exceptions.h" -#include "xyz_frame.h" -#include "picture_asset_writer.h" using std::string; using std::ostream; @@ -48,41 +44,33 @@ using std::make_pair; using std::istream; using std::cout; using boost::shared_ptr; -using boost::dynamic_pointer_cast; -using boost::lexical_cast; -using namespace libdcp; +using namespace dcp; -PictureAsset::PictureAsset (boost::filesystem::path directory, boost::filesystem::path mxf_name) - : MXFAsset (directory, mxf_name) +PictureMXF::PictureMXF (boost::filesystem::path file) + : MXF (file) { } -void -PictureAsset::write_to_cpl (xmlpp::Element* node) const +PictureMXF::PictureMXF (Fraction edit_rate) + : MXF (edit_rate) { - MXFAsset::write_to_cpl (node); - - xmlpp::Node::NodeList c = node->get_children (); - xmlpp::Node::NodeList::iterator i = c.begin(); - while (i != c.end() && (*i)->get_name() != cpl_node_name ()) { - ++i; - } - assert (i != c.end ()); +} - (*i)->add_child ("FrameRate")->add_child_text (lexical_cast<string> (_edit_rate * edit_rate_factor ()) + " 1"); - if (_interop) { - stringstream s; - s << std::fixed << std::setprecision (2) << (float (_size.width) / _size.height); - (*i)->add_child ("ScreenAspectRatio")->add_child_text (s.str ()); - } else { - (*i)->add_child ("ScreenAspectRatio")->add_child_text (lexical_cast<string> (_size.width) + " " + lexical_cast<string> (_size.height)); - } +void +PictureMXF::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 -PictureAsset::descriptor_equals ( +PictureMXF::descriptor_equals ( ASDCP::JP2K::PictureDescriptor const & a, ASDCP::JP2K::PictureDescriptor const & b, boost::function<void (NoteType, string)> note ) const { @@ -124,7 +112,7 @@ PictureAsset::descriptor_equals ( } bool -PictureAsset::frame_buffer_equals ( +PictureMXF::frame_buffer_equals ( int frame, EqualityOptions opt, boost::function<void (NoteType, string)> note, uint8_t const * data_A, unsigned int size_A, uint8_t const * data_B, unsigned int size_B ) const @@ -148,7 +136,7 @@ PictureAsset::frame_buffer_equals ( for (int c = 0; c < 3; ++c) { if (image_A->size() != image_B->size()) { - note (ERROR, "image sizes for frame " + lexical_cast<string>(frame) + " differ"); + note (ERROR, String::compose ("image sizes for frame %1 differ", frame)); return false; } @@ -174,14 +162,12 @@ PictureAsset::frame_buffer_equals ( double const std_dev = sqrt (double (total_squared_deviation) / abs_diffs.size()); - note (NOTE, "mean difference " + lexical_cast<string> (mean) + ", deviation " + lexical_cast<string> (std_dev)); + note (NOTE, String::compose ("mean difference %1, deviation %2", mean, std_dev)); if (mean > opt.max_mean_pixel_error) { note ( ERROR, - "mean " + lexical_cast<string>(mean) + - " out of range " + lexical_cast<string>(opt.max_mean_pixel_error) + - " in frame " + lexical_cast<string>(frame) + String::compose ("mean %1 out of range %2 in frame %3", mean, opt.max_mean_pixel_error, frame) ); return false; @@ -190,9 +176,7 @@ PictureAsset::frame_buffer_equals ( if (std_dev > opt.max_std_dev_pixel_error) { note ( ERROR, - "standard deviation " + lexical_cast<string>(std_dev) + - " out of range " + lexical_cast<string>(opt.max_std_dev_pixel_error) + - " in frame " + lexical_cast<string>(frame) + String::compose ("standard deviation %1 out of range %2 in frame %3", std_dev, opt.max_std_dev_pixel_error, frame) ); return false; @@ -202,10 +186,7 @@ PictureAsset::frame_buffer_equals ( } string -PictureAsset::key_type () const +PictureMXF::key_type () const { return "MDIK"; } - - - diff --git a/src/picture_mxf.h b/src/picture_mxf.h new file mode 100644 index 00000000..1ce2e7de --- /dev/null +++ b/src/picture_mxf.h @@ -0,0 +1,114 @@ +/* + Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net> + + This program 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. + + This program 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 this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#ifndef LIBDCP_PICTURE_MXF_H +#define LIBDCP_PICTURE_MXF_H + +/** @file src/picture_mxf.h + * @brief PictureMXF class. + */ + +#include "mxf.h" +#include "util.h" +#include "metadata.h" +#include <openjpeg.h> + +namespace ASDCP { + namespace JP2K { + class PictureDescriptor; + } +} + +namespace dcp +{ + +class MonoPictureFrame; +class StereoPictureFrame; +class PictureMXFWriter; + +/** @class PictureMXF + * @brief An asset made up of JPEG2000 data. + */ +class PictureMXF : public MXF +{ +public: + PictureMXF (boost::filesystem::path file); + PictureMXF (Fraction edit_rate); + + virtual boost::shared_ptr<PictureMXFWriter> start_write ( + boost::filesystem::path file, + Standard standard, + bool overwrite + ) = 0; + + Size size () const { + return _size; + } + + void set_size (Size s) { + _size = s; + } + + Fraction frame_rate () const { + return _frame_rate; + } + + void set_frame_rate (Fraction r) { + _frame_rate = r; + } + + Fraction screen_aspect_ratio () const { + return _screen_aspect_ratio; + } + + void set_screen_aspect_ratio (Fraction r) { + _screen_aspect_ratio = r; + } + +protected: + + bool frame_buffer_equals ( + int frame, EqualityOptions opt, boost::function<void (NoteType, std::string)> 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, + boost::function<void (NoteType, std::string)> + ) const; + + void read_picture_descriptor (ASDCP::JP2K::PictureDescriptor const &); + + /** picture size in pixels */ + Size _size; + Fraction _frame_rate; + Fraction _screen_aspect_ratio; + +private: + std::string key_type () const; + std::string asdcp_kind () const { + return "Picture"; + } +}; + + +} + +#endif diff --git a/src/picture_asset_writer.cc b/src/picture_mxf_writer.cc index 92b384db..46426401 100644 --- a/src/picture_asset_writer.cc +++ b/src/picture_mxf_writer.cc @@ -1,5 +1,5 @@ /* - Copyright (C) 2012-2013 Carl Hetherington <cth@carlh.net> + Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -17,19 +17,19 @@ */ -#include <inttypes.h> -#include <stdint.h> -#include "AS_DCP.h" #include "KM_fileio.h" -#include "picture_asset_writer.h" +#include "picture_mxf_writer.h" #include "exceptions.h" -#include "picture_asset.h" +#include "picture_mxf.h" +#include "AS_DCP.h" +#include <inttypes.h> +#include <stdint.h> using std::istream; using std::ostream; using std::string; using boost::shared_ptr; -using namespace libdcp; +using namespace dcp; FrameInfo::FrameInfo (istream& s) : offset (0) @@ -82,13 +82,12 @@ FrameInfo::write (FILE* f) const #endif } - -PictureAssetWriter::PictureAssetWriter (PictureAsset* asset, bool overwrite) - : _asset (asset) - , _frames_written (0) +PictureMXFWriter::PictureMXFWriter (PictureMXF* mxf, boost::filesystem::path file, Standard standard, bool overwrite) + : MXFWriter (mxf, file) + , _picture_mxf (mxf) , _started (false) - , _finalized (false) + , _standard (standard) , _overwrite (overwrite) { - + } diff --git a/src/picture_asset_writer.h b/src/picture_mxf_writer.h index a62b20b2..d1baa7ad 100644 --- a/src/picture_asset_writer.h +++ b/src/picture_mxf_writer.h @@ -1,5 +1,5 @@ /* - Copyright (C) 2012-2013 Carl Hetherington <cth@carlh.net> + Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -17,19 +17,29 @@ */ +/** @file src/picture_mxf_writer.h + * @brief PictureMXFWriter and FrameInfo classes. + */ + +#ifndef LIBDCP_PICTURE_MXF_WRITER_H +#define LIBDCP_PICTURE_MXF_WRITER_H + +#include "metadata.h" +#include "types.h" +#include "mxf_writer.h" +#include <boost/shared_ptr.hpp> +#include <boost/utility.hpp> #include <stdint.h> #include <string> #include <fstream> -#include <boost/shared_ptr.hpp> -#include <boost/utility.hpp> -#include "metadata.h" -#include "types.h" -namespace libdcp { +namespace dcp { -class PictureAsset; +class PictureMXF; -/** Information about a single frame (either a monoscopic frame or a left *or* right eye stereoscopic frame) */ +/** @class FrameInfo + * @brief Information about a single frame (either a monoscopic frame or a left *or* right eye stereoscopic frame) + */ struct FrameInfo { FrameInfo (uint64_t o, uint64_t s, std::string h) @@ -49,31 +59,27 @@ struct FrameInfo std::string hash; }; -class PictureAssetWriter : public boost::noncopyable +/** @class PictureMXFWriter + * @brief Parent class for classes which write picture MXF files. + */ +class PictureMXFWriter : public MXFWriter { public: - virtual ~PictureAssetWriter () {} virtual FrameInfo write (uint8_t *, int) = 0; - virtual void finalize () = 0; virtual void fake_write (int) = 0; - + protected: template <class P, class Q> - friend void start (PictureAssetWriter *, boost::shared_ptr<P>, Q *, uint8_t *, int); + friend void start (PictureMXFWriter *, boost::shared_ptr<P>, Standard, Q *, uint8_t *, int); - PictureAssetWriter (PictureAsset *, bool); + PictureMXFWriter (PictureMXF *, boost::filesystem::path, Standard standard, bool); - PictureAsset* _asset; - - /** Number of picture frames written to the asset so far. For stereo assets - * this will be incremented for each eye (i.e. there will be twice the number - * of frames as in a mono asset). - */ - int _frames_written; + PictureMXF* _picture_mxf; bool _started; - /** true if finalize() has been called */ - bool _finalized; + Standard _standard; bool _overwrite; }; } + +#endif diff --git a/src/picture_asset_writer_common.cc b/src/picture_mxf_writer_common.cc index 52848745..d8d1589b 100644 --- a/src/picture_asset_writer_common.cc +++ b/src/picture_mxf_writer_common.cc @@ -1,5 +1,5 @@ /* - Copyright (C) 2012-2013 Carl Hetherington <cth@carlh.net> + Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -33,19 +33,24 @@ struct ASDCPStateBase }; template <class P, class Q> -void libdcp::start (PictureAssetWriter* writer, shared_ptr<P> state, Q* asset, uint8_t* data, int size) +void dcp::start (PictureMXFWriter* writer, shared_ptr<P> state, Standard standard, Q* mxf, uint8_t* data, int size) { + mxf->set_file (writer->_file); + if (ASDCP_FAILURE (state->j2k_parser.OpenReadFrame (data, size, state->frame_buffer))) { boost::throw_exception (MiscError ("could not parse J2K frame")); } state->j2k_parser.FillPictureDescriptor (state->picture_descriptor); - state->picture_descriptor.EditRate = ASDCP::Rational (asset->edit_rate(), 1); + state->picture_descriptor.EditRate = ASDCP::Rational (mxf->edit_rate().numerator, mxf->edit_rate().denominator); + + mxf->set_size (Size (state->picture_descriptor.StoredWidth, state->picture_descriptor.StoredHeight)); + mxf->set_screen_aspect_ratio (Fraction (state->picture_descriptor.AspectRatio.Numerator, state->picture_descriptor.AspectRatio.Denominator)); - asset->fill_writer_info (&state->writer_info); + mxf->fill_writer_info (&state->writer_info, standard); Kumu::Result_t r = state->mxf_writer.OpenWrite ( - asset->path().string().c_str(), + mxf->file().string().c_str(), state->writer_info, state->picture_descriptor, 16384, @@ -53,7 +58,7 @@ void libdcp::start (PictureAssetWriter* writer, shared_ptr<P> state, Q* asset, u ); if (ASDCP_FAILURE (r)) { - boost::throw_exception (MXFFileError ("could not open MXF file for writing", asset->path().string(), r)); + boost::throw_exception (MXFFileError ("could not open MXF file for writing", mxf->file().string(), r)); } writer->_started = true; diff --git a/src/reel.cc b/src/reel.cc index ff51c025..4bdbcb5f 100644 --- a/src/reel.cc +++ b/src/reel.cc @@ -1,5 +1,5 @@ /* - Copyright (C) 2012 Carl Hetherington <cth@carlh.net> + Copyright (C) 2014 Carl Hetherington <cth@carlh.net> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -17,46 +17,79 @@ */ -#include <libxml++/nodes/element.h> #include "reel.h" #include "util.h" -#include "picture_asset.h" -#include "mono_picture_asset.h" -#include "stereo_picture_asset.h" -#include "sound_asset.h" -#include "subtitle_asset.h" +#include "picture_mxf.h" +#include "mono_picture_mxf.h" +#include "stereo_picture_mxf.h" +#include "sound_mxf.h" +#include "subtitle_content.h" +#include "reel_mono_picture_asset.h" +#include "reel_stereo_picture_asset.h" +#include "reel_sound_asset.h" +#include "reel_subtitle_asset.h" #include "kdm.h" +#include <libxml++/nodes/element.h> using std::string; using std::list; using std::cout; using boost::shared_ptr; using boost::dynamic_pointer_cast; -using namespace libdcp; +using namespace dcp; + +Reel::Reel (boost::shared_ptr<const cxml::Node> node) + : Object (node->string_child ("Id")) +{ + shared_ptr<cxml::Node> asset_list = node->node_child ("AssetList"); + + shared_ptr<cxml::Node> main_picture = asset_list->optional_node_child ("MainPicture"); + if (main_picture) { + _main_picture.reset (new ReelMonoPictureAsset (main_picture)); + } + + shared_ptr<cxml::Node> main_stereoscopic_picture = asset_list->optional_node_child ("MainStereoscopicPicture"); + if (main_stereoscopic_picture) { + _main_picture.reset (new ReelStereoPictureAsset (main_stereoscopic_picture)); + } + + shared_ptr<cxml::Node> main_sound = asset_list->optional_node_child ("MainSound"); + if (main_sound) { + _main_sound.reset (new ReelSoundAsset (main_sound)); + } + + shared_ptr<cxml::Node> main_subtitle = asset_list->optional_node_child ("MainSubtitle"); + if (main_subtitle) { + _main_subtitle.reset (new ReelSubtitleAsset (main_subtitle)); + } + + node->ignore_child ("AnnotationText"); + node->done (); +} void -Reel::write_to_cpl (xmlpp::Element* node) const +Reel::write_to_cpl (xmlpp::Element* node, Standard standard) const { xmlpp::Element* reel = node->add_child ("Reel"); reel->add_child("Id")->add_child_text ("urn:uuid:" + make_uuid()); xmlpp::Element* asset_list = reel->add_child ("AssetList"); - if (_main_picture && dynamic_pointer_cast<MonoPictureAsset> (_main_picture)) { + if (_main_picture && dynamic_pointer_cast<ReelMonoPictureAsset> (_main_picture)) { /* Mono pictures come before other stuff... */ - _main_picture->write_to_cpl (asset_list); + _main_picture->write_to_cpl (asset_list, standard); } if (_main_sound) { - _main_sound->write_to_cpl (asset_list); + _main_sound->write_to_cpl (asset_list, standard); } if (_main_subtitle) { - _main_subtitle->write_to_cpl (asset_list); + _main_subtitle->write_to_cpl (asset_list, standard); } - if (_main_picture && dynamic_pointer_cast<StereoPictureAsset> (_main_picture)) { + if (_main_picture && dynamic_pointer_cast<ReelStereoPictureAsset> (_main_picture)) { /* ... but stereo pictures must come after */ - _main_picture->write_to_cpl (asset_list); + _main_picture->write_to_cpl (asset_list, standard); } } @@ -100,16 +133,16 @@ Reel::encrypted () const } void -Reel::add_kdm (KDM const & kdm) +Reel::add (KDM const & kdm) { list<KDMKey> keys = kdm.keys (); for (list<KDMKey>::iterator i = keys.begin(); i != keys.end(); ++i) { if (i->key_id() == _main_picture->key_id()) { - _main_picture->set_key (i->key ()); + _main_picture->mxf()->set_key (i->key ()); } if (i->key_id() == _main_sound->key_id()) { - _main_sound->set_key (i->key ()); + _main_sound->mxf()->set_key (i->key ()); } } } @@ -117,10 +150,41 @@ Reel::add_kdm (KDM const & kdm) void Reel::set_mxf_keys (Key key) { - _main_picture->set_key (key); + _main_picture->mxf()->set_key (key); if (_main_sound) { - _main_sound->set_key (key); + _main_sound->mxf()->set_key (key); } /* XXX: subtitle asset? */ } + +void +Reel::add (shared_ptr<ReelAsset> asset) +{ + shared_ptr<ReelPictureAsset> p = dynamic_pointer_cast<ReelPictureAsset> (asset); + shared_ptr<ReelSoundAsset> so = dynamic_pointer_cast<ReelSoundAsset> (asset); + shared_ptr<ReelSubtitleAsset> su = dynamic_pointer_cast<ReelSubtitleAsset> (asset); + if (p) { + _main_picture = p; + } else if (so) { + _main_sound = so; + } else if (su) { + _main_subtitle = su; + } +} + +void +Reel::resolve_refs (list<shared_ptr<Object> > objects) +{ + if (_main_picture) { + _main_picture->content().resolve (objects); + } + + if (_main_sound) { + _main_sound->content().resolve (objects); + } + + if (_main_subtitle) { + _main_subtitle->content().resolve (objects); + } +} @@ -1,5 +1,5 @@ /* - Copyright (C) 2012 Carl Hetherington <cth@carlh.net> + Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -20,64 +20,75 @@ #ifndef LIBDCP_REEL_H #define LIBDCP_REEL_H -#include <list> -#include <boost/shared_ptr.hpp> -#include <boost/function.hpp> -#include <libxml++/libxml++.h> #include "key.h" #include "types.h" +#include "ref.h" +#include <libxml++/libxml++.h> +#include <boost/shared_ptr.hpp> +#include <boost/function.hpp> +#include <list> -namespace xmlpp { +namespace cxml { class Node; } -namespace libdcp { +namespace dcp { -class PictureAsset; -class SoundAsset; -class SubtitleAsset; -class KDM; +class KDM; +class ReelAsset; +class ReelPictureAsset; +class ReelSoundAsset; +class ReelSubtitleAsset; +class Content; -/** @brief A reel within a DCP; the part which actually contains picture, sound and subtitle data */ -class Reel +/** @brief A reel within a DCP; the part which actually refers to picture, sound and subtitle data */ +class Reel : public Object { public: + Reel () {} + Reel ( - boost::shared_ptr<PictureAsset> picture, - boost::shared_ptr<SoundAsset> sound, - boost::shared_ptr<SubtitleAsset> subtitle + boost::shared_ptr<ReelPictureAsset> picture, + boost::shared_ptr<ReelSoundAsset> sound, + boost::shared_ptr<ReelSubtitleAsset> subtitle ) : _main_picture (picture) , _main_sound (sound) , _main_subtitle (subtitle) {} + + Reel (boost::shared_ptr<const cxml::Node>); - boost::shared_ptr<const PictureAsset> main_picture () const { + boost::shared_ptr<ReelPictureAsset> main_picture () const { return _main_picture; } - boost::shared_ptr<const SoundAsset> main_sound () const { + boost::shared_ptr<ReelSoundAsset> main_sound () const { return _main_sound; } - boost::shared_ptr<const SubtitleAsset> main_subtitle () const { + boost::shared_ptr<ReelSubtitleAsset> main_subtitle () const { return _main_subtitle; } - void write_to_cpl (xmlpp::Element *) const; + void add (boost::shared_ptr<ReelAsset> asset); + + void write_to_cpl (xmlpp::Element* node, Standard standard) const; bool encrypted () const; - void set_mxf_keys (libdcp::Key); + void set_mxf_keys (dcp::Key); bool equals (boost::shared_ptr<const Reel> other, EqualityOptions opt, boost::function<void (NoteType, std::string)> notes) const; - void add_kdm (KDM const &); + void add (KDM const &); + + void resolve_refs (std::list<boost::shared_ptr<Object> >); private: - boost::shared_ptr<PictureAsset> _main_picture; - boost::shared_ptr<SoundAsset> _main_sound; - boost::shared_ptr<SubtitleAsset> _main_subtitle; + boost::shared_ptr<ReelPictureAsset> _main_picture; + boost::shared_ptr<ReelSoundAsset> _main_sound; + boost::shared_ptr<ReelSubtitleAsset> _main_subtitle; }; } diff --git a/src/reel_asset.cc b/src/reel_asset.cc new file mode 100644 index 00000000..fa0068c5 --- /dev/null +++ b/src/reel_asset.cc @@ -0,0 +1,104 @@ +/* + Copyright (C) 2014 Carl Hetherington <cth@carlh.net> + + This program 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. + + This program 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 this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include "reel_asset.h" +#include "content.h" +#include "compose.hpp" +#include <libcxml/cxml.h> + +using std::pair; +using std::string; +using std::make_pair; +using boost::shared_ptr; +using boost::lexical_cast; +using namespace dcp; + +ReelAsset::ReelAsset () + : Object (make_uuid ()) + , _content (_id) + , _edit_rate (Fraction (24, 1)) + , _intrinsic_duration (0) + , _entry_point (0) + , _duration (0) +{ + +} + +/** Construct a ReelAsset. + * @param content Content that this asset refers to. + * @param entry_point Entry point to use in that content. + */ +ReelAsset::ReelAsset (boost::shared_ptr<Content> content, int64_t entry_point) + : Object (content->id ()) + , _content (content) + , _edit_rate (content->edit_rate ()) + , _intrinsic_duration (content->intrinsic_duration ()) + , _entry_point (entry_point) + , _duration (_intrinsic_duration - _entry_point) + , _hash (make_digest (content->file (), 0)) +{ + /* default _annotation_text to the leaf name of our file */ + _annotation_text = content->file().leaf().string (); +} + +ReelAsset::ReelAsset (boost::shared_ptr<const cxml::Node> node) + : Object (node->string_child ("Id")) + , _content (_id) + , _annotation_text (node->optional_string_child ("AnnotationText").get_value_or ("")) + , _edit_rate (Fraction (node->string_child ("EditRate"))) + , _intrinsic_duration (node->number_child<int64_t> ("IntrinsicDuration")) + , _entry_point (node->number_child<int64_t> ("EntryPoint")) + , _duration (node->number_child<int64_t> ("Duration")) + , _hash (node->optional_string_child ("Hash").get_value_or ("")) + , _key_id (node->optional_string_child ("KeyId").get_value_or ("")) +{ + if (_id.length() > 9) { + _id = _id.substr (9); + _content.set_id (_id); + } + + if (_key_id.length() > 9) { + _key_id = _key_id.substr (9); + } +} + +void +ReelAsset::write_to_cpl (xmlpp::Node* node, Standard) const +{ + pair<string, string> const attr = cpl_node_attribute (); + xmlpp::Element* a = node->add_child (cpl_node_name ()); + if (!attr.first.empty ()) { + a->set_attribute (attr.first, attr.second); + } + a->add_child("Id")->add_child_text ("urn:uuid:" + _id); + a->add_child("AnnotationText")->add_child_text (_annotation_text); + a->add_child("EditRate")->add_child_text (String::compose ("%1 %2", _edit_rate.numerator, _edit_rate.denominator)); + a->add_child("IntrinsicDuration")->add_child_text (lexical_cast<string> (_intrinsic_duration)); + a->add_child("EntryPoint")->add_child_text (lexical_cast<string> (_entry_point)); + a->add_child("Duration")->add_child_text (lexical_cast<string> (_duration)); + if (!_key_id.empty ()) { + a->add_child("KeyId")->add_child_text ("urn:uuid:" + _key_id); + } +} + +pair<string, string> +ReelAsset::cpl_node_attribute () const +{ + return make_pair ("", ""); +} diff --git a/src/reel_asset.h b/src/reel_asset.h new file mode 100644 index 00000000..a71290ae --- /dev/null +++ b/src/reel_asset.h @@ -0,0 +1,113 @@ +/* + Copyright (C) 2014 Carl Hetherington <cth@carlh.net> + + This program 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. + + This program 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 this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#ifndef LIBDCP_REEL_ASSET_H +#define LIBDCP_REEL_ASSET_H + +#include "object.h" +#include "util.h" +#include "ref.h" +#include <boost/shared_ptr.hpp> + +namespace cxml { + class Node; +} + +namespace xmlpp { + class Node; +} + +namespace dcp { + +class Content; + +/** @class ReelAsset + * @brief An entry in a <Reel> which refers to a use of a piece of content. + * + * This class encapsulates the XML that exists in a <Reel> to say + * that a piece of content is used in this reel. It does not + * describe the content itself (but links to a Content object which does). + */ +class ReelAsset : public Object +{ +public: + ReelAsset (); + ReelAsset (boost::shared_ptr<Content> content, int64_t entry_point); + ReelAsset (boost::shared_ptr<const cxml::Node>); + + virtual void write_to_cpl (xmlpp::Node* node, Standard standard) const; + + virtual bool equals ( + boost::shared_ptr<const ReelAsset>, + EqualityOptions, + boost::function<void (NoteType, std::string)>) + const { + + return false; + } + + /** @return a Ref to our actual content */ + Ref<Content>& content () { + return _content; + } + + /** @return true if a KeyId is specified for this asset, implying + * that its content is encrypted. + */ + bool encrypted () const { + return !_key_id.empty (); + } + + /** @return Key ID to describe the key that encrypts this asset's; + * content. + */ + std::string key_id () const { + return _key_id; + } + +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 () 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 () const; + + /** Reference to the content (MXF or XML file) that this reel entry + * applies to. + */ + Ref<Content> _content; + +private: + + std::string _annotation_text; ///< The <AnnotationText> from the reel's entry for this asset + Fraction _edit_rate; ///< The <EditRate> from the reel's entry for this asset + int64_t _intrinsic_duration; ///< The <IntrinsicDuration> from the reel's entry for this asset + int64_t _entry_point; ///< The <EntryPoint> from the reel's entry for this asset + int64_t _duration; ///< The <Duration> from the reel's entry for this asset + std::string _hash; ///< The <Hash> from the reel's entry for this asset + std::string _key_id; ///< The <KeyId> from the reel's entry for this asset, or empty if there isn't one +}; + +} + +#endif diff --git a/src/reel_mono_picture_asset.cc b/src/reel_mono_picture_asset.cc new file mode 100644 index 00000000..b4ed6301 --- /dev/null +++ b/src/reel_mono_picture_asset.cc @@ -0,0 +1,54 @@ +/* + Copyright (C) 2014 Carl Hetherington <cth@carlh.net> + + This program 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. + + This program 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 this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +/** @file src/reel_mono_picture_asset.cc + * @brief ReelMonoPictureAsset class. + */ + +#include "reel_mono_picture_asset.h" +#include "mono_picture_mxf.h" +#include <libcxml/cxml.h> + +using std::string; +using boost::shared_ptr; +using namespace dcp; + +ReelMonoPictureAsset::ReelMonoPictureAsset () +{ + +} + +ReelMonoPictureAsset::ReelMonoPictureAsset (boost::shared_ptr<MonoPictureMXF> mxf, int64_t entry_point) + : ReelPictureAsset (mxf, entry_point) +{ + +} + +ReelMonoPictureAsset::ReelMonoPictureAsset (boost::shared_ptr<const cxml::Node> node) + : ReelPictureAsset (node) +{ + node->done (); +} + +string +ReelMonoPictureAsset::cpl_node_name () const +{ + return "MainPicture"; +} + diff --git a/src/reel_mono_picture_asset.h b/src/reel_mono_picture_asset.h new file mode 100644 index 00000000..a25550a4 --- /dev/null +++ b/src/reel_mono_picture_asset.h @@ -0,0 +1,49 @@ +/* + Copyright (C) 2014 Carl Hetherington <cth@carlh.net> + + This program 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. + + This program 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 this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +/** @file src/reel_mono_picture_asset.h + * @brief ReelMonoPictureAsset class. + */ + +#ifndef LIBDCP_REEL_MONO_PICTURE_ASSET_H +#define LIBDCP_REEL_MONO_PICTURE_ASSET_H + +#include "reel_picture_asset.h" + +namespace dcp { + +class MonoPictureMXF; + +/** @class ReelMonoPictureAsset + * @brief Part of a Reel's description which refers to a monoscopic picture MXF. + */ +class ReelMonoPictureAsset : public ReelPictureAsset +{ +public: + ReelMonoPictureAsset (); + ReelMonoPictureAsset (boost::shared_ptr<MonoPictureMXF> content, int64_t entry_point); + ReelMonoPictureAsset (boost::shared_ptr<const cxml::Node>); + +private: + std::string cpl_node_name () const; +}; + +} + +#endif diff --git a/src/reel_picture_asset.cc b/src/reel_picture_asset.cc new file mode 100644 index 00000000..d1910bb3 --- /dev/null +++ b/src/reel_picture_asset.cc @@ -0,0 +1,91 @@ +/* + Copyright (C) 2014 Carl Hetherington <cth@carlh.net> + + This program 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. + + This program 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 this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +/** @file src/reel_picture_asset.h + * @brief ReelPictureAsset class. + */ + +#include "content.h" +#include "reel_picture_asset.h" +#include "picture_mxf.h" +#include "compose.hpp" +#include <libcxml/cxml.h> + +using std::bad_cast; +using std::string; +using std::stringstream; +using boost::shared_ptr; +using namespace dcp; + +ReelPictureAsset::ReelPictureAsset () + : _frame_rate (Fraction (24, 1)) + , _screen_aspect_ratio (Fraction (1998, 1080)) +{ + +} + +ReelPictureAsset::ReelPictureAsset (boost::shared_ptr<PictureMXF> content, int64_t entry_point) + : ReelAsset (content, entry_point) + , _frame_rate (content->frame_rate ()) + , _screen_aspect_ratio (content->screen_aspect_ratio ()) +{ + +} + +ReelPictureAsset::ReelPictureAsset (boost::shared_ptr<const cxml::Node> node) + : ReelAsset (node) +{ + _frame_rate = Fraction (node->string_child ("FrameRate")); + try { + _screen_aspect_ratio = Fraction (node->string_child ("ScreenAspectRatio")); + } catch (XMLError& e) { + /* Maybe it's not a fraction */ + } + try { + float f = node->number_child<float> ("ScreenAspectRatio"); + _screen_aspect_ratio = Fraction (f * 1000, 1000); + } catch (bad_cast& e) { + + } +} + +void +ReelPictureAsset::write_to_cpl (xmlpp::Node* node, Standard standard) const +{ + ReelAsset::write_to_cpl (node, standard); + + xmlpp::Node::NodeList c = node->get_children (); + xmlpp::Node::NodeList::iterator i = c.begin(); + while (i != c.end() && (*i)->get_name() != cpl_node_name ()) { + ++i; + } + + assert (i != c.end ()); + + (*i)->add_child ("FrameRate")->add_child_text (String::compose ("%1 %2", _frame_rate.numerator, _frame_rate.denominator)); + if (standard == INTEROP) { + stringstream s; + s << std::fixed << std::setprecision (2) << (float (_screen_aspect_ratio.numerator) / _screen_aspect_ratio.denominator); + (*i)->add_child ("ScreenAspectRatio")->add_child_text (s.str ()); + } else { + (*i)->add_child ("ScreenAspectRatio")->add_child_text ( + 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 new file mode 100644 index 00000000..547f3ada --- /dev/null +++ b/src/reel_picture_asset.h @@ -0,0 +1,59 @@ +/* + Copyright (C) 2014 Carl Hetherington <cth@carlh.net> + + This program 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. + + This program 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 this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +/** @file src/reel_picture_asset.h + * @brief ReelPictureAsset class. + */ + +#ifndef LIBDCP_REEL_PICTURE_ASSET_H +#define LIBDCP_REEL_PICTURE_ASSET_H + +#include "reel_asset.h" +#include "picture_mxf.h" + +namespace dcp { + +/** @class ReelPictureAsset + * @brief Part of a Reel's description which refers to a picture MXF. + */ +class ReelPictureAsset : public ReelAsset +{ +public: + ReelPictureAsset (); + ReelPictureAsset (boost::shared_ptr<PictureMXF> content, int64_t entry_point); + ReelPictureAsset (boost::shared_ptr<const cxml::Node>); + + virtual void write_to_cpl (xmlpp::Node* node, Standard standard) const; + + boost::shared_ptr<PictureMXF> mxf () { + return boost::dynamic_pointer_cast<PictureMXF> (_content.object ()); + } + + void set_screen_aspect_ratio (Fraction a) { + _screen_aspect_ratio = a; + } + +private: + Fraction _frame_rate; + Fraction _screen_aspect_ratio; +}; + +} + +#endif diff --git a/src/reel_sound_asset.cc b/src/reel_sound_asset.cc new file mode 100644 index 00000000..984434dd --- /dev/null +++ b/src/reel_sound_asset.cc @@ -0,0 +1,48 @@ +/* + Copyright (C) 2014 Carl Hetherington <cth@carlh.net> + + This program 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. + + This program 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 this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +/** @file src/reel_sound_asset.cc + * @brief ReelSoundAsset class. + */ + +#include "reel_sound_asset.h" +#include <libcxml/cxml.h> + +using std::string; +using boost::shared_ptr; +using namespace dcp; + +ReelSoundAsset::ReelSoundAsset (boost::shared_ptr<Content> content, int64_t entry_point) + : ReelAsset (content, entry_point) +{ + +} + +ReelSoundAsset::ReelSoundAsset (boost::shared_ptr<const cxml::Node> node) + : ReelAsset (node) +{ + node->ignore_child ("Language"); + node->done (); +} + +string +ReelSoundAsset::cpl_node_name () const +{ + return "MainSound"; +} diff --git a/src/reel_sound_asset.h b/src/reel_sound_asset.h new file mode 100644 index 00000000..42835c84 --- /dev/null +++ b/src/reel_sound_asset.h @@ -0,0 +1,53 @@ +/* + Copyright (C) 2014 Carl Hetherington <cth@carlh.net> + + This program 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. + + This program 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 this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +/** @file src/reel_sound_asset.h + * @brief ReelSoundAsset class. + */ + +#include "reel_asset.h" +#include "sound_mxf.h" +#include <boost/shared_ptr.hpp> +#include <string> + +namespace dcp { + +/** @class ReelSoundAsset + * @brief Part of a Reel's description which refers to a sound MXF. + */ +class ReelSoundAsset : public ReelAsset +{ +public: + ReelSoundAsset (boost::shared_ptr<Content> content, int64_t entry_point); + ReelSoundAsset (boost::shared_ptr<const cxml::Node>); + + boost::shared_ptr<SoundMXF> mxf () { + return boost::dynamic_pointer_cast<SoundMXF> (_content.object ()); + } + + boost::shared_ptr<const SoundMXF> mxf () const { + return boost::dynamic_pointer_cast<const SoundMXF> (_content.object ()); + } + +private: + std::string cpl_node_name () const; +}; + +} + diff --git a/src/reel_stereo_picture_asset.cc b/src/reel_stereo_picture_asset.cc new file mode 100644 index 00000000..09178dc2 --- /dev/null +++ b/src/reel_stereo_picture_asset.cc @@ -0,0 +1,67 @@ +/* + Copyright (C) 2014 Carl Hetherington <cth@carlh.net> + + This program 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. + + This program 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 this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +/** @file src/reel_stereo_picture_asset.cc + * @brief ReelStereoPictureAsset class. + */ + +#include "reel_stereo_picture_asset.h" +#include "stereo_picture_mxf.h" +#include <libcxml/cxml.h> + +using std::string; +using std::pair; +using std::make_pair; +using boost::shared_ptr; +using namespace dcp; + +ReelStereoPictureAsset::ReelStereoPictureAsset () +{ + +} + +ReelStereoPictureAsset::ReelStereoPictureAsset (boost::shared_ptr<StereoPictureMXF> mxf, int64_t entry_point) + : ReelPictureAsset (mxf, entry_point) +{ + +} + +ReelStereoPictureAsset::ReelStereoPictureAsset (boost::shared_ptr<const cxml::Node> node) + : ReelPictureAsset (node) +{ + node->done (); +} + +string +ReelStereoPictureAsset::cpl_node_name () const +{ + return "msp-cpl:MainStereoscopicPicture"; +} + +pair<string, string> +ReelStereoPictureAsset::cpl_node_attribute (Standard standard) const +{ + if (standard == INTEROP) { + return make_pair ("xmlns:msp-cpl", "http://www.digicine.com/schemas/437-Y/2007/Main-Stereo-Picture-CPL"); + } else { + return make_pair ("xmlns:msp-cpl", "http://www.smpte-ra.org/schemas/429-10/2008/Main-Stereo-Picture-CPL"); + } + + return make_pair ("", ""); +} diff --git a/src/reel_stereo_picture_asset.h b/src/reel_stereo_picture_asset.h new file mode 100644 index 00000000..57cc4da1 --- /dev/null +++ b/src/reel_stereo_picture_asset.h @@ -0,0 +1,51 @@ +/* + Copyright (C) 2014 Carl Hetherington <cth@carlh.net> + + This program 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. + + This program 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 this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +/** @file src/reel_stereo_picture_asset.h + * @brief ReelStereoPictureAsset class. + */ + +#ifndef LIBDCP_REEL_STEREO_PICTURE_ASSET_H +#define LIBDCP_REEL_STEREO_PICTURE_ASSET_H + +#include "reel_picture_asset.h" + +namespace dcp { + +class StereoPictureMXF; + +/** @class ReelStereoPictureAsset + * @brief Part of a Reel's description which refers to a stereoscopic picture MXF. + */ +class ReelStereoPictureAsset : public ReelPictureAsset +{ +public: + ReelStereoPictureAsset (); + ReelStereoPictureAsset (boost::shared_ptr<StereoPictureMXF> content, int64_t entry_point); + ReelStereoPictureAsset (boost::shared_ptr<const cxml::Node>); + +private: + std::string cpl_node_name () const; + std::pair<std::string, std::string> cpl_node_attribute (Standard standard) const; +}; + +} + +#endif + diff --git a/src/reel_subtitle_asset.cc b/src/reel_subtitle_asset.cc new file mode 100644 index 00000000..1c948200 --- /dev/null +++ b/src/reel_subtitle_asset.cc @@ -0,0 +1,48 @@ +/* + Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net> + + This program 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. + + This program 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 this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +/** @file src/reel_subtitle_asset.cc + * @brief ReelSubtitleAsset class. + */ + +#include "subtitle_content.h" +#include "reel_subtitle_asset.h" + +using std::string; +using boost::shared_ptr; +using namespace dcp; + +ReelSubtitleAsset::ReelSubtitleAsset (boost::shared_ptr<SubtitleContent> content, int64_t entry_point) + : ReelAsset (content, entry_point) +{ + +} + +ReelSubtitleAsset::ReelSubtitleAsset (boost::shared_ptr<const cxml::Node> node) + : ReelAsset (node) +{ + node->ignore_child ("Language"); + node->done (); +} + +string +ReelSubtitleAsset::cpl_node_name () const +{ + return "MainSubtitle"; +} diff --git a/src/reel_subtitle_asset.h b/src/reel_subtitle_asset.h new file mode 100644 index 00000000..5cdfac13 --- /dev/null +++ b/src/reel_subtitle_asset.h @@ -0,0 +1,52 @@ +/* + Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net> + + This program 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. + + This program 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 this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +/** @file src/reel_subtitle_asset.h + * @brief ReelSubtitleAsset class. + */ + +#ifndef LIBDCP_REEL_SUBTITLE_ASSET_H +#define LIBDCP_REEL_SUBTITLE_ASSET_H + +#include "reel_asset.h" + +namespace dcp { + +class SubtitleContent; + +/** @class ReelSubtitleAsset + * @brief Part of a Reel's description which refers to a subtitle XML file. + */ +class ReelSubtitleAsset : public ReelAsset +{ +public: + ReelSubtitleAsset (boost::shared_ptr<SubtitleContent> content, int64_t entry_point); + ReelSubtitleAsset (boost::shared_ptr<const cxml::Node>); + + boost::shared_ptr<SubtitleContent> subtitle_content () const { + return boost::dynamic_pointer_cast<SubtitleContent> (_content.object ()); + } + +private: + std::string cpl_node_name () const; +}; + +} + +#endif diff --git a/src/ref.h b/src/ref.h new file mode 100644 index 00000000..c22222cc --- /dev/null +++ b/src/ref.h @@ -0,0 +1,120 @@ +/* + Copyright (C) 2014 Carl Hetherington <cth@carlh.net> + + This program 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. + + This program 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 this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +/** @file src/ref.h + * @brief Ref class. + */ + +#ifndef LIBDCP_REF_H +#define LIBDCP_REF_H + +#include "exceptions.h" +#include "object.h" +#include <boost/shared_ptr.hpp> +#include <string> + +namespace dcp { + +/** @class Ref + * @brief A reference to an object which is identified by a universally-unique identifier (UUID). + * + * This class is a `pointer' to a thing. It will always know the + * UUID of the thing, and it may have a shared_ptr to the C++ object + * which represents the thing. + * + * If the Ref does not have a shared_ptr it may be given one by + * calling resolve() with a list of objects. The shared_ptr will be + * set up using any object on the list which has a matching ID. + */ +template<class T> +class Ref +{ +public: + /** Initialise a Ref with an ID but no shared_ptr */ + Ref (std::string id) + : _id (id) + {} + + /** Initialise a Ref with a shared_ptr to an object */ + Ref (boost::shared_ptr<T> object) + : _id (object->id ()) + , _object (object) + {} + + /** Set the ID of this Ref */ + void set_id (std::string id) + { + _id = id; + } + + /** Look through a list of objects and copy a shared_ptr to any object + * which matches the ID of this one. + */ + void resolve (std::list<boost::shared_ptr<Object> > objects) + { + typename std::list<boost::shared_ptr<Object> >::iterator i = objects.begin(); + while (i != objects.end() && (*i)->id() != _id) { + ++i; + } + + if (i != objects.end ()) { + _object = boost::dynamic_pointer_cast<T> (*i); + } + } + + /** @return the ID of the thing that we are pointing to */ + std::string id () const { + return _id; + } + + /** @return a shared_ptr to the thing; an UnresolvedRefError is thrown + * if the shared_ptr is not known. + */ + boost::shared_ptr<T> object () const { + if (!_object) { + throw UnresolvedRefError (_id); + } + + return _object; + } + + /** operator-> to access the shared_ptr; an UnresolvedRefError is thrown + * if the shared_ptr is not known. + */ + T * operator->() const { + if (!_object) { + throw UnresolvedRefError (_id); + } + + return _object.get (); + } + + /** @return true if a shared_ptr is known for this Ref */ + bool resolved () const { + return _object; + } + +private: + std::string _id; ///< ID; will always be known + boost::shared_ptr<T> _object; ///< shared_ptr to the thing, may be null. +}; + +} + +#endif diff --git a/src/rgb_xyz.cc b/src/rgb_xyz.cc index dea2106c..92997441 100644 --- a/src/rgb_xyz.cc +++ b/src/rgb_xyz.cc @@ -1,5 +1,5 @@ /* - Copyright (C) 2013 Carl Hetherington <cth@carlh.net> + Copyright (C) 2013-2014 Carl Hetherington <cth@carlh.net> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -27,16 +27,22 @@ using std::min; using std::max; using boost::shared_ptr; -using namespace libdcp; +using namespace dcp; #define DCI_COEFFICIENT (48.0 / 52.37) /** Convert an openjpeg XYZ image to RGB. * @param xyz_frame Frame in XYZ. + * @param lut_in Input Gamma LUT to use. + * @param lut_out Output Gamma LUT to use. * @return RGB image. */ shared_ptr<ARGBFrame> -libdcp::xyz_to_rgb (shared_ptr<const XYZFrame> xyz_frame, shared_ptr<const LUT> lut_in, shared_ptr<const LUT> lut_out) +dcp::xyz_to_rgb ( + boost::shared_ptr<const XYZFrame> xyz_frame, + boost::shared_ptr<const GammaLUT> lut_in, + boost::shared_ptr<const GammaLUT> lut_out + ) { int const max_colour = pow (2, lut_out->bit_depth()) - 1; @@ -99,8 +105,13 @@ libdcp::xyz_to_rgb (shared_ptr<const XYZFrame> xyz_frame, shared_ptr<const LUT> return argb_frame; } -shared_ptr<libdcp::XYZFrame> -libdcp::rgb_to_xyz (shared_ptr<const Image> rgb, shared_ptr<const LUT> lut_in, shared_ptr<const LUT> lut_out, double const colour_matrix[3][3]) +shared_ptr<dcp::XYZFrame> +dcp::rgb_to_xyz ( + boost::shared_ptr<const Image> rgb, + boost::shared_ptr<const GammaLUT> lut_in, + boost::shared_ptr<const GammaLUT> lut_out, + double const colour_matrix[3][3] + ) { assert (lut_in->bit_depth() == 12); assert (lut_out->bit_depth() == 16); diff --git a/src/rgb_xyz.h b/src/rgb_xyz.h index 87959cb3..28e09230 100644 --- a/src/rgb_xyz.h +++ b/src/rgb_xyz.h @@ -1,5 +1,5 @@ /* - Copyright (C) 2013 Carl Hetherington <cth@carlh.net> + Copyright (C) 2013-2014 Carl Hetherington <cth@carlh.net> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -19,19 +19,19 @@ #include <boost/shared_ptr.hpp> -namespace libdcp { +namespace dcp { class ARGBFrame; class XYZFrame; -class LUT; +class GammaLUT; class Image; extern boost::shared_ptr<ARGBFrame> xyz_to_rgb ( - boost::shared_ptr<const XYZFrame>, boost::shared_ptr<const LUT>, boost::shared_ptr<const LUT> + boost::shared_ptr<const XYZFrame>, boost::shared_ptr<const GammaLUT>, boost::shared_ptr<const GammaLUT> ); extern boost::shared_ptr<XYZFrame> rgb_to_xyz ( - boost::shared_ptr<const Image>, boost::shared_ptr<const LUT>, boost::shared_ptr<const LUT>, double const colour_matrix[3][3] + boost::shared_ptr<const Image>, boost::shared_ptr<const GammaLUT>, boost::shared_ptr<const GammaLUT>, double const colour_matrix[3][3] ); } diff --git a/src/signer.cc b/src/signer.cc index 11cf5eb8..8b3cf31d 100644 --- a/src/signer.cc +++ b/src/signer.cc @@ -1,5 +1,5 @@ /* - Copyright (C) 2013 Carl Hetherington <cth@carlh.net> + Copyright (C) 2013-2014 Carl Hetherington <cth@carlh.net> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -17,34 +17,49 @@ */ +/** @file src/signer.cc + * @brief Signer class. + */ + +#include "signer.h" +#include "exceptions.h" +#include <libcxml/cxml.h> #include <libxml++/libxml++.h> #include <xmlsec/xmldsig.h> #include <xmlsec/dl.h> #include <xmlsec/app.h> #include <xmlsec/crypto.h> -#include <libcxml/cxml.h> -#include "signer.h" -#include "exceptions.h" -#include "compose.hpp" using std::string; using std::list; using std::cout; using boost::shared_ptr; -using namespace libdcp; +using namespace dcp; -/** @param signer_key Filename of private key to sign with */ +/** Add a <Signer> and <ds:Signature> nodes to an XML node. + * @param parent XML node to add to. + * @param standard INTEROP or SMPTE. + */ void -Signer::sign (xmlpp::Element* parent, bool interop) const +Signer::sign (xmlpp::Element* parent, Standard standard) const { - add_signer (parent, "dsig"); + /* <Signer> */ + + xmlpp::Element* signer = parent->add_child("Signer"); + xmlpp::Element* data = signer->add_child("X509Data", "dsig"); + xmlpp::Element* serial_element = data->add_child("X509IssuerSerial", "dsig"); + serial_element->add_child("X509IssuerName", "dsig")->add_child_text (_certificates.leaf()->issuer()); + serial_element->add_child("X509SerialNumber", "dsig")->add_child_text (_certificates.leaf()->serial()); + data->add_child("X509SubjectName", "dsig")->add_child_text (_certificates.leaf()->subject()); + /* <Signature> */ + xmlpp::Element* signature = parent->add_child("Signature", "dsig"); xmlpp::Element* 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"); - if (interop) { + if (standard == INTEROP) { signed_info->add_child("SignatureMethod", "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"); @@ -115,21 +130,3 @@ Signer::add_signature_value (xmlpp::Node* parent, string ns) const xmlSecDSigCtxDestroy (signature_context); } - -void -Signer::add_signer (xmlpp::Element* parent, string ns) const -{ - xmlpp::Element* signer = parent->add_child("Signer"); - - { - xmlpp::Element* data = signer->add_child("X509Data", ns); - - { - xmlpp::Element* serial_element = data->add_child("X509IssuerSerial", ns); - serial_element->add_child("X509IssuerName", ns)->add_child_text (_certificates.leaf()->issuer()); - serial_element->add_child("X509SerialNumber", ns)->add_child_text (_certificates.leaf()->serial()); - } - - data->add_child("X509SubjectName", ns)->add_child_text (_certificates.leaf()->subject()); - } -} diff --git a/src/signer.h b/src/signer.h index efb8099b..6e258f8d 100644 --- a/src/signer.h +++ b/src/signer.h @@ -1,5 +1,5 @@ /* - Copyright (C) 2013 Carl Hetherington <cth@carlh.net> + Copyright (C) 2013-2014 Carl Hetherington <cth@carlh.net> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -17,25 +17,39 @@ */ -#include <boost/filesystem.hpp> +#ifndef LIBDCP_SIGNER_H +#define LIBDCP_SIGNER_H + +/** @file src/signer.h + * @brief Signer class. + */ + #include "certificates.h" +#include "types.h" +#include <boost/filesystem.hpp> namespace xmlpp { class Element; class Node; } -namespace libdcp { +namespace dcp { -class Signer +/** @class Signer + * @brief A class which can sign XML files. + */ +class Signer : public boost::noncopyable { public: + /** @param c Certificate chain to sign with. + * @param k Key to sign with. + */ Signer (CertificateChain c, boost::filesystem::path k) : _certificates (c) , _key (k) {} - void sign (xmlpp::Element* parent, bool interop) const; + void sign (xmlpp::Element* parent, Standard standard) const; void add_signature_value (xmlpp::Node* parent, std::string ns) const; CertificateChain const & certificates () const { @@ -44,11 +58,12 @@ public: private: - void add_signer (xmlpp::Element* parent, std::string ns) const; - + /** Certificate chain to sign with */ CertificateChain _certificates; /** Filename of signer key */ boost::filesystem::path _key; }; } + +#endif diff --git a/src/signer_chain.cc b/src/signer_chain.cc index f35e4ff6..3b75b06c 100644 --- a/src/signer_chain.cc +++ b/src/signer_chain.cc @@ -1,5 +1,5 @@ /* - Copyright (C) 2013 Carl Hetherington <cth@carlh.net> + Copyright (C) 2013-2014 Carl Hetherington <cth@carlh.net> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -17,17 +17,21 @@ */ -#include <fstream> -#include <sstream> -#include <boost/filesystem.hpp> -#include <boost/algorithm/string.hpp> -#include <openssl/sha.h> -#include <openssl/bio.h> -#include <openssl/evp.h> -#include "KM_util.h" +/** @file src/signer_chain.cc + * @brief Functions to make signer chains. + */ + #include "signer_chain.h" #include "exceptions.h" #include "util.h" +#include "KM_util.h" +#include <openssl/sha.h> +#include <openssl/bio.h> +#include <openssl/evp.h> +#include <boost/filesystem.hpp> +#include <boost/algorithm/string.hpp> +#include <fstream> +#include <sstream> using std::string; using std::ofstream; @@ -35,7 +39,11 @@ using std::ifstream; using std::stringstream; using std::cout; -static void command (string cmd) +/** Run a shell command. + * @param cmd Command to run (UTF8-encoded). + */ +static void +command (string cmd) { #ifdef LIBDCP_WINDOWS /* We need to use CreateProcessW on Windows so that the UTF-8/16 mess @@ -77,16 +85,15 @@ static void command (string cmd) if (code) { stringstream s; s << "error " << code << " in " << cmd << " within " << boost::filesystem::current_path(); - throw libdcp::MiscError (s.str()); + throw dcp::MiscError (s.str()); } } /** Extract a public key from a private key and create a SHA1 digest of it. - * @param key Private key + * @param private_key Private key * @param openssl openssl binary name (or full path if openssl is not on the system path). * @return SHA1 digest of corresponding public key, with escaped / characters. */ - static string public_key_digest (boost::filesystem::path private_key, boost::filesystem::path openssl) { @@ -102,7 +109,7 @@ public_key_digest (boost::filesystem::path private_key, boost::filesystem::path string pub; ifstream f (public_name.string().c_str ()); if (!f.good ()) { - throw libdcp::MiscError ("public key not found"); + throw dcp::MiscError ("public key not found"); } bool read = false; @@ -121,22 +128,22 @@ public_key_digest (boost::filesystem::path private_key, boost::filesystem::path /* Decode the base64 of the public key */ unsigned char buffer[512]; - int const N = libdcp::base64_decode (pub, buffer, 1024); + int const N = dcp::base64_decode (pub, buffer, 1024); /* Hash it with SHA1 (without the first 24 bytes, for reasons that are not entirely clear) */ SHA_CTX context; if (!SHA1_Init (&context)) { - throw libdcp::MiscError ("could not init SHA1 context"); + throw dcp::MiscError ("could not init SHA1 context"); } if (!SHA1_Update (&context, buffer + 24, N - 24)) { - throw libdcp::MiscError ("could not update SHA1 digest"); + throw dcp::MiscError ("could not update SHA1 digest"); } unsigned char digest[SHA_DIGEST_LENGTH]; if (!SHA1_Final (digest, &context)) { - throw libdcp::MiscError ("could not finish SHA1 digest"); + throw dcp::MiscError ("could not finish SHA1 digest"); } char digest_base64[64]; @@ -149,8 +156,12 @@ public_key_digest (boost::filesystem::path private_key, boost::filesystem::path return dig; } +/** Generate a chain of root, intermediate and leaf keys by running an OpenSSL binary. + * @param directory Directory to write the files to. + * @param openssl openssl binary path. + */ void -libdcp::make_signer_chain (boost::filesystem::path directory, boost::filesystem::path openssl) +dcp::make_signer_chain (boost::filesystem::path directory, boost::filesystem::path openssl) { boost::filesystem::path const cwd = boost::filesystem::current_path (); diff --git a/src/signer_chain.h b/src/signer_chain.h index 50f56ed9..f039caf3 100644 --- a/src/signer_chain.h +++ b/src/signer_chain.h @@ -1,5 +1,5 @@ /* - Copyright (C) 2013 Carl Hetherington <cth@carlh.net> + Copyright (C) 2013-2014 Carl Hetherington <cth@carlh.net> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -17,9 +17,13 @@ */ +/** @file src/signer_chain.h + * @brief Functions to make signer chains. + */ + #include <boost/filesystem.hpp> -namespace libdcp { +namespace dcp { /** Create a chain of certificates for signing things. * @param directory Directory to write files to. diff --git a/src/sound_asset.cc b/src/sound_asset.cc deleted file mode 100644 index 95244b90..00000000 --- a/src/sound_asset.cc +++ /dev/null @@ -1,401 +0,0 @@ -/* - Copyright (C) 2012 Carl Hetherington <cth@carlh.net> - - This program 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. - - This program 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 this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - -*/ - -/** @file src/sound_asset.cc - * @brief An asset made up of WAV files - */ - -#include <iostream> -#include <stdexcept> -#include <boost/filesystem.hpp> -#include <boost/lexical_cast.hpp> -#include <libxml++/nodes/element.h> -#include "KM_fileio.h" -#include "AS_DCP.h" -#include "sound_asset.h" -#include "util.h" -#include "exceptions.h" -#include "sound_frame.h" - -using std::string; -using std::stringstream; -using std::ostream; -using std::vector; -using std::list; -using boost::shared_ptr; -using boost::lexical_cast; -using namespace libdcp; - -SoundAsset::SoundAsset (boost::filesystem::path directory, boost::filesystem::path mxf_name) - : MXFAsset (directory, mxf_name) - , _channels (0) - , _sampling_rate (0) -{ - -} - -void -SoundAsset::create (vector<boost::filesystem::path> const & files) -{ - create (boost::bind (&SoundAsset::path_from_channel, this, _1, files)); -} - -void -SoundAsset::read () -{ - ASDCP::PCM::MXFReader reader; - Kumu::Result_t r = reader.OpenRead (path().string().c_str()); - if (ASDCP_FAILURE (r)) { - boost::throw_exception (MXFFileError ("could not open MXF file for reading", path().string(), r)); - } - - ASDCP::PCM::AudioDescriptor desc; - if (ASDCP_FAILURE (reader.FillAudioDescriptor (desc))) { - boost::throw_exception (DCPReadError ("could not read audio MXF information")); - } - - _sampling_rate = desc.AudioSamplingRate.Numerator / desc.AudioSamplingRate.Denominator; - _channels = desc.ChannelCount; - _edit_rate = desc.EditRate.Numerator; - assert (desc.EditRate.Denominator == 1); - _intrinsic_duration = desc.ContainerDuration; -} - -boost::filesystem::path -SoundAsset::path_from_channel (Channel channel, vector<boost::filesystem::path> const & files) -{ - unsigned int const c = int (channel); - assert (c < files.size ()); - return files[c]; -} - -void -SoundAsset::create (boost::function<boost::filesystem::path (Channel)> get_path) -{ - ASDCP::Rational asdcp_edit_rate (_edit_rate, 1); - - assert (_channels > 0); - ASDCP::PCM::WAVParser* pcm_parser_channel[_channels]; - for (int i = 0; i < _channels; ++i) { - pcm_parser_channel[i] = new ASDCP::PCM::WAVParser (); - } - - Kumu::Result_t r = pcm_parser_channel[0]->OpenRead (get_path(LEFT).string().c_str(), asdcp_edit_rate); - if (ASDCP_FAILURE (r)) { - boost::throw_exception (FileError ("could not open WAV file for reading", get_path(LEFT), r)); - } - - ASDCP::PCM::AudioDescriptor audio_desc; - pcm_parser_channel[0]->FillAudioDescriptor (audio_desc); - audio_desc.ChannelCount = 0; - audio_desc.BlockAlign = 0; - audio_desc.EditRate = asdcp_edit_rate; - audio_desc.AvgBps = audio_desc.AvgBps * _channels; - - Channel channels[] = { - LEFT, - RIGHT, - CENTRE, - LFE, - LS, - RS, - /* XXX: not quite sure what these should be yet */ - CHANNEL_7, - CHANNEL_8 - }; - - assert (int(_channels) <= int(sizeof(channels) / sizeof(Channel))); - - ASDCP::PCM::FrameBuffer* frame_buffer_channel[_channels]; - ASDCP::PCM::AudioDescriptor* audio_desc_channel[_channels]; - for (int i = 0; i < _channels; ++i) { - frame_buffer_channel[i] = new ASDCP::PCM::FrameBuffer (); - audio_desc_channel[i] = new ASDCP::PCM::AudioDescriptor (); - } - - for (int i = 0; i < _channels; ++i) { - - boost::filesystem::path const path = get_path (channels[i]); - - Kumu::Result_t r = pcm_parser_channel[i]->OpenRead (path.string().c_str(), asdcp_edit_rate); - if (ASDCP_FAILURE (r)) { - boost::throw_exception (FileError ("could not open WAV file for reading", path, r)); - } - - pcm_parser_channel[i]->FillAudioDescriptor (*audio_desc_channel[i]); - frame_buffer_channel[i]->Capacity (ASDCP::PCM::CalcFrameBufferSize (*audio_desc_channel[i])); - - audio_desc.ChannelCount += audio_desc_channel[i]->ChannelCount; - audio_desc.BlockAlign += audio_desc_channel[i]->BlockAlign; - } - - ASDCP::PCM::FrameBuffer frame_buffer; - frame_buffer.Capacity (ASDCP::PCM::CalcFrameBufferSize (audio_desc)); - frame_buffer.Size (ASDCP::PCM::CalcFrameBufferSize (audio_desc)); - - ASDCP::WriterInfo writer_info; - MXFAsset::fill_writer_info (&writer_info); - - ASDCP::PCM::MXFWriter mxf_writer; - r = mxf_writer.OpenWrite (path().string().c_str(), writer_info, audio_desc); - if (ASDCP_FAILURE (r)) { - boost::throw_exception (FileError ("could not open audio MXF for writing", path().string(), r)); - } - - for (int i = 0; i < _intrinsic_duration; ++i) { - - for (int j = 0; j < _channels; ++j) { - memset (frame_buffer_channel[j]->Data(), 0, frame_buffer_channel[j]->Capacity()); - if (ASDCP_FAILURE (pcm_parser_channel[j]->ReadFrame (*frame_buffer_channel[j]))) { - boost::throw_exception (MiscError ("could not read audio frame")); - } - } - - byte_t *data_s = frame_buffer.Data(); - byte_t *data_e = data_s + frame_buffer.Capacity(); - byte_t sample_size = ASDCP::PCM::CalcSampleSize (*audio_desc_channel[0]); - int offset = 0; - - while (data_s < data_e) { - for (int j = 0; j < _channels; ++j) { - byte_t* frame = frame_buffer_channel[j]->Data() + offset; - memcpy (data_s, frame, sample_size); - data_s += sample_size; - } - offset += sample_size; - } - - if (ASDCP_FAILURE (mxf_writer.WriteFrame (frame_buffer, _encryption_context, 0))) { - boost::throw_exception (MiscError ("could not write audio MXF frame")); - } - - if (_progress) { - (*_progress) (0.5 * float (i) / _intrinsic_duration); - } - } - - bool const failed = ASDCP_FAILURE (mxf_writer.Finalize()); - - for (int i = 0; i < _channels; ++i) { - delete pcm_parser_channel[i]; - delete frame_buffer_channel[i]; - delete audio_desc_channel[i]; - } - - if (failed) { - boost::throw_exception (MiscError ("could not finalise audio MXF")); - } -} - -string -SoundAsset::cpl_node_name () const -{ - return "MainSound"; -} - -bool -SoundAsset::equals (shared_ptr<const Asset> other, EqualityOptions opt, boost::function<void (NoteType, string)> note) const -{ - if (!MXFAsset::equals (other, opt, note)) { - return false; - } - - ASDCP::PCM::MXFReader reader_A; - Kumu::Result_t r = reader_A.OpenRead (path().string().c_str()); - if (ASDCP_FAILURE (r)) { - boost::throw_exception (MXFFileError ("could not open MXF file for reading", path().string(), r)); - } - - ASDCP::PCM::MXFReader reader_B; - r = reader_B.OpenRead (other->path().string().c_str()); - if (ASDCP_FAILURE (r)) { - boost::throw_exception (MXFFileError ("could not open MXF file for reading", path().string(), r)); - } - - ASDCP::PCM::AudioDescriptor desc_A; - if (ASDCP_FAILURE (reader_A.FillAudioDescriptor (desc_A))) { - boost::throw_exception (DCPReadError ("could not read audio MXF information")); - } - ASDCP::PCM::AudioDescriptor desc_B; - if (ASDCP_FAILURE (reader_B.FillAudioDescriptor (desc_B))) { - boost::throw_exception (DCPReadError ("could not read audio MXF information")); - } - - if ( - desc_A.EditRate != desc_B.EditRate || - desc_A.AudioSamplingRate != desc_B.AudioSamplingRate || - desc_A.Locked != desc_B.Locked || - desc_A.ChannelCount != desc_B.ChannelCount || - desc_A.QuantizationBits != desc_B.QuantizationBits || - desc_A.BlockAlign != desc_B.BlockAlign || - desc_A.AvgBps != desc_B.AvgBps || - desc_A.LinkedTrackID != desc_B.LinkedTrackID || - desc_A.ContainerDuration != desc_B.ContainerDuration -// desc_A.ChannelFormat != desc_B.ChannelFormat || - ) { - - note (ERROR, "audio MXF picture descriptors differ"); - return false; - } - - ASDCP::PCM::FrameBuffer buffer_A (1 * Kumu::Megabyte); - ASDCP::PCM::FrameBuffer buffer_B (1 * Kumu::Megabyte); - - for (int i = 0; i < _intrinsic_duration; ++i) { - if (ASDCP_FAILURE (reader_A.ReadFrame (i, buffer_A))) { - boost::throw_exception (DCPReadError ("could not read audio frame")); - } - - if (ASDCP_FAILURE (reader_B.ReadFrame (i, buffer_B))) { - boost::throw_exception (DCPReadError ("could not read audio frame")); - } - - if (buffer_A.Size() != buffer_B.Size()) { - note (ERROR, "sizes of audio data for frame " + lexical_cast<string>(i) + " differ"); - return false; - } - - if (memcmp (buffer_A.RoData(), buffer_B.RoData(), buffer_A.Size()) != 0) { - for (uint32_t i = 0; i < buffer_A.Size(); ++i) { - int const d = abs (buffer_A.RoData()[i] - buffer_B.RoData()[i]); - if (d > opt.max_audio_sample_error) { - note (ERROR, "PCM data difference of " + lexical_cast<string> (d)); - return false; - } - } - } - } - - return true; -} - -shared_ptr<const SoundFrame> -SoundAsset::get_frame (int n) const -{ - /* XXX: should add on entry point here? */ - return shared_ptr<const SoundFrame> (new SoundFrame (path().string(), n, _decryption_context)); -} - -shared_ptr<SoundAssetWriter> -SoundAsset::start_write () -{ - /* XXX: can't we use a shared_ptr here? */ - return shared_ptr<SoundAssetWriter> (new SoundAssetWriter (this)); -} - -struct SoundAssetWriter::ASDCPState -{ - ASDCP::PCM::MXFWriter mxf_writer; - ASDCP::PCM::FrameBuffer frame_buffer; - ASDCP::WriterInfo writer_info; - ASDCP::PCM::AudioDescriptor audio_desc; - ASDCP::AESEncContext* encryption_context; -}; - -SoundAssetWriter::SoundAssetWriter (SoundAsset* a) - : _state (new SoundAssetWriter::ASDCPState) - , _asset (a) - , _finalized (false) - , _frames_written (0) - , _frame_buffer_offset (0) -{ - _state->encryption_context = a->encryption_context (); - - /* Derived from ASDCP::Wav::SimpleWaveHeader::FillADesc */ - _state->audio_desc.EditRate = ASDCP::Rational (_asset->edit_rate(), 1); - _state->audio_desc.AudioSamplingRate = ASDCP::Rational (_asset->sampling_rate(), 1); - _state->audio_desc.Locked = 0; - _state->audio_desc.ChannelCount = _asset->channels (); - _state->audio_desc.QuantizationBits = 24; - _state->audio_desc.BlockAlign = 3 * _asset->channels(); - _state->audio_desc.AvgBps = _asset->sampling_rate() * _state->audio_desc.BlockAlign; - _state->audio_desc.LinkedTrackID = 0; - _state->audio_desc.ChannelFormat = ASDCP::PCM::CF_NONE; - - _state->frame_buffer.Capacity (ASDCP::PCM::CalcFrameBufferSize (_state->audio_desc)); - _state->frame_buffer.Size (ASDCP::PCM::CalcFrameBufferSize (_state->audio_desc)); - memset (_state->frame_buffer.Data(), 0, _state->frame_buffer.Capacity()); - - _asset->fill_writer_info (&_state->writer_info); - - Kumu::Result_t r = _state->mxf_writer.OpenWrite (_asset->path().string().c_str(), _state->writer_info, _state->audio_desc); - if (ASDCP_FAILURE (r)) { - boost::throw_exception (FileError ("could not open audio MXF for writing", _asset->path().string(), r)); - } -} - -void -SoundAssetWriter::write (float const * const * data, int frames) -{ - for (int i = 0; i < frames; ++i) { - - byte_t* out = _state->frame_buffer.Data() + _frame_buffer_offset; - - /* Write one sample per channel */ - for (int j = 0; j < _asset->channels(); ++j) { - int32_t const s = data[j][i] * (1 << 23); - *out++ = (s & 0xff); - *out++ = (s & 0xff00) >> 8; - *out++ = (s & 0xff0000) >> 16; - } - _frame_buffer_offset += 3 * _asset->channels(); - - assert (_frame_buffer_offset <= int (_state->frame_buffer.Capacity())); - - /* Finish the MXF frame if required */ - if (_frame_buffer_offset == int (_state->frame_buffer.Capacity())) { - write_current_frame (); - _frame_buffer_offset = 0; - memset (_state->frame_buffer.Data(), 0, _state->frame_buffer.Capacity()); - } - } -} - -void -SoundAssetWriter::write_current_frame () -{ - ASDCP::Result_t const r = _state->mxf_writer.WriteFrame (_state->frame_buffer, _state->encryption_context, 0); - if (ASDCP_FAILURE (r)) { - boost::throw_exception (MiscError ("could not write audio MXF frame (" + lexical_cast<string> (int (r)) + ")")); - } - - ++_frames_written; -} - -void -SoundAssetWriter::finalize () -{ - if (_frame_buffer_offset > 0) { - write_current_frame (); - } - - if (ASDCP_FAILURE (_state->mxf_writer.Finalize())) { - boost::throw_exception (MiscError ("could not finalise audio MXF")); - } - - _finalized = true; - _asset->set_intrinsic_duration (_frames_written); - _asset->set_duration (_frames_written); -} - -string -SoundAsset::key_type () const -{ - return "MDAK"; -} diff --git a/src/sound_asset.h b/src/sound_asset.h deleted file mode 100644 index c52a5436..00000000 --- a/src/sound_asset.h +++ /dev/null @@ -1,134 +0,0 @@ -/* - Copyright (C) 2012 Carl Hetherington <cth@carlh.net> - - This program 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. - - This program 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 this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - -*/ - -#ifndef LIBDCP_SOUND_ASSET_H -#define LIBDCP_SOUND_ASSET_H - -/** @file src/sound_asset.h - * @brief An asset made up of PCM audio data files - */ - -#include "mxf_asset.h" -#include "types.h" -#include "metadata.h" - -namespace libdcp -{ - -class SoundFrame; -class SoundAsset; - -class SoundAssetWriter -{ -public: - void write (float const * const *, int); - void finalize (); - -private: - friend class SoundAsset; - - SoundAssetWriter (SoundAsset *); - - /* no copy construction */ - SoundAssetWriter (SoundAssetWriter const &); - SoundAssetWriter& operator= (SoundAssetWriter const &); - - void write_current_frame (); - - /* do this with an opaque pointer so we don't have to include - ASDCP headers - */ - - struct ASDCPState; - boost::shared_ptr<ASDCPState> _state; - - SoundAsset* _asset; - bool _finalized; - int _frames_written; - int _frame_buffer_offset; -}; - -/** @brief An asset made up of WAV files */ -class SoundAsset : public MXFAsset -{ -public: - SoundAsset (boost::filesystem::path directory, boost::filesystem::path mxf_name); - - void read (); - - /** The following parameters must be set up (if required) before calling this: - * Interop mode (set_interop) - * Edit rate (set_edit_rate) - * MXF Metadata (set_metadata) - * Channels (set_channels) - * Intrinsic duration (set_intrinsic_duration) - */ - void create (std::vector<boost::filesystem::path> const & files); - - /** The following parameters must be set up (if required) before calling this: - * Interop mode (set_interop) - * Edit rate (set_edit_rate) - * MXF Metadata (set_metadata) - * Channels (set_channels) - * Intrinsic duration (set_intrinsic_duration) - */ - void create (boost::function<boost::filesystem::path (Channel)> get_path); - - boost::shared_ptr<SoundAssetWriter> start_write (); - - bool equals (boost::shared_ptr<const Asset> other, EqualityOptions opt, boost::function<void (NoteType, std::string)> note) const; - - boost::shared_ptr<const SoundFrame> get_frame (int n) const; - - void set_channels (int c) { - _channels = c; - } - - int channels () const { - return _channels; - } - - void set_sampling_rate (int s) { - _sampling_rate = s; - } - - int sampling_rate () const { - return _sampling_rate; - } - -protected: - - std::string asdcp_kind () const { - return "Sound"; - } - -private: - std::string key_type () const; - void construct (boost::function<boost::filesystem::path (Channel)> get_path); - boost::filesystem::path path_from_channel (Channel channel, std::vector<boost::filesystem::path> const & files); - std::string cpl_node_name () const; - - /** Number of channels in the asset */ - int _channels; - int _sampling_rate; -}; - -} - -#endif diff --git a/src/sound_frame.cc b/src/sound_frame.cc index 6bc52c1a..c79b29b3 100644 --- a/src/sound_frame.cc +++ b/src/sound_frame.cc @@ -1,5 +1,5 @@ /* - Copyright (C) 2012 Carl Hetherington <cth@carlh.net> + Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -17,18 +17,22 @@ */ -#include "AS_DCP.h" -#include "KM_fileio.h" +/** @file src/sound_frame.cc + * @brief SoundFrame class. + */ + #include "sound_frame.h" #include "exceptions.h" +#include "AS_DCP.h" +#include "KM_fileio.h" using namespace std; -using namespace libdcp; +using namespace dcp; -SoundFrame::SoundFrame (string mxf_path, int n, ASDCP::AESDecContext* c) +SoundFrame::SoundFrame (boost::filesystem::path mxf_path, int n, ASDCP::AESDecContext* c) { ASDCP::PCM::MXFReader reader; - Kumu::Result_t r = reader.OpenRead (mxf_path.c_str()); + Kumu::Result_t r = reader.OpenRead (mxf_path.string().c_str()); if (ASDCP_FAILURE (r)) { boost::throw_exception (FileError ("could not open MXF file for reading", mxf_path, r)); } diff --git a/src/sound_frame.h b/src/sound_frame.h index 154ff84b..26f738d3 100644 --- a/src/sound_frame.h +++ b/src/sound_frame.h @@ -1,5 +1,5 @@ /* - Copyright (C) 2012 Carl Hetherington <cth@carlh.net> + Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -17,11 +17,17 @@ */ +/** @file src/sound_frame.h + * @brief SoundFrame class. + */ + #ifndef LIBDCP_SOUND_FRAME_H #define LIBDCP_SOUND_FRAME_H -#include <string> +#include <boost/noncopyable.hpp> +#include <boost/filesystem.hpp> #include <stdint.h> +#include <string> namespace ASDCP { namespace PCM { @@ -30,18 +36,22 @@ namespace ASDCP { class AESDecContext; } -namespace libdcp { +namespace dcp { -class SoundFrame +/** @class SoundFrame + * @brief One ‘frame’ of sound data from a MXF. + */ +class SoundFrame : public boost::noncopyable { public: - SoundFrame (std::string mxf_path, int n, ASDCP::AESDecContext *); + SoundFrame (boost::filesystem::path mxf_path, int n, ASDCP::AESDecContext *); ~SoundFrame (); uint8_t const * data () const; int size () const; private: + /** a buffer to hold the frame */ ASDCP::PCM::FrameBuffer* _buffer; }; diff --git a/src/sound_mxf.cc b/src/sound_mxf.cc new file mode 100644 index 00000000..fa48a6c1 --- /dev/null +++ b/src/sound_mxf.cc @@ -0,0 +1,178 @@ +/* + Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net> + + This program 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. + + This program 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 this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +/** @file src/sound_mxf.cc + * @brief SoundMXF class. + */ + +#include "sound_mxf.h" +#include "util.h" +#include "exceptions.h" +#include "sound_frame.h" +#include "sound_mxf_writer.h" +#include "compose.hpp" +#include "KM_fileio.h" +#include "AS_DCP.h" +#include <libxml++/nodes/element.h> +#include <boost/filesystem.hpp> +#include <iostream> +#include <stdexcept> + +using std::string; +using std::stringstream; +using std::ostream; +using std::vector; +using std::list; +using boost::shared_ptr; +using namespace dcp; + +SoundMXF::SoundMXF (boost::filesystem::path file) + : MXF (file) + , _channels (0) + , _sampling_rate (0) +{ + ASDCP::PCM::MXFReader reader; + Kumu::Result_t r = reader.OpenRead (file.string().c_str()); + if (ASDCP_FAILURE (r)) { + boost::throw_exception (MXFFileError ("could not open MXF file for reading", file.string(), r)); + } + + ASDCP::PCM::AudioDescriptor desc; + if (ASDCP_FAILURE (reader.FillAudioDescriptor (desc))) { + boost::throw_exception (DCPReadError ("could not read audio MXF information")); + } + + _sampling_rate = desc.AudioSamplingRate.Numerator / desc.AudioSamplingRate.Denominator; + _channels = desc.ChannelCount; + _edit_rate = Fraction (desc.EditRate.Numerator, desc.EditRate.Denominator); + + _intrinsic_duration = desc.ContainerDuration; + + ASDCP::WriterInfo info; + if (ASDCP_FAILURE (reader.FillWriterInfo (info))) { + boost::throw_exception (DCPReadError ("could not read audio MXF information")); + } + + read_writer_info (info); + +} + +SoundMXF::SoundMXF (Fraction edit_rate, int sampling_rate, int channels) + : MXF (edit_rate) + , _channels (channels) + , _sampling_rate (sampling_rate) +{ + +} + +bool +SoundMXF::equals (shared_ptr<const Content> other, EqualityOptions opt, boost::function<void (NoteType, string)> note) const +{ + if (!MXF::equals (other, opt, note)) { + return false; + } + + ASDCP::PCM::MXFReader reader_A; + Kumu::Result_t r = reader_A.OpenRead (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; + r = reader_B.OpenRead (other->file().string().c_str()); + if (ASDCP_FAILURE (r)) { + boost::throw_exception (MXFFileError ("could not open MXF file for reading", file().string(), r)); + } + + ASDCP::PCM::AudioDescriptor desc_A; + if (ASDCP_FAILURE (reader_A.FillAudioDescriptor (desc_A))) { + boost::throw_exception (DCPReadError ("could not read audio MXF information")); + } + ASDCP::PCM::AudioDescriptor desc_B; + if (ASDCP_FAILURE (reader_B.FillAudioDescriptor (desc_B))) { + boost::throw_exception (DCPReadError ("could not read audio MXF information")); + } + + if ( + desc_A.EditRate != desc_B.EditRate || + desc_A.AudioSamplingRate != desc_B.AudioSamplingRate || + desc_A.Locked != desc_B.Locked || + desc_A.ChannelCount != desc_B.ChannelCount || + desc_A.QuantizationBits != desc_B.QuantizationBits || + desc_A.BlockAlign != desc_B.BlockAlign || + desc_A.AvgBps != desc_B.AvgBps || + desc_A.LinkedTrackID != desc_B.LinkedTrackID || + desc_A.ContainerDuration != desc_B.ContainerDuration +// desc_A.ChannelFormat != desc_B.ChannelFormat || + ) { + + note (ERROR, "audio MXF picture descriptors differ"); + return false; + } + + ASDCP::PCM::FrameBuffer buffer_A (1 * Kumu::Megabyte); + ASDCP::PCM::FrameBuffer buffer_B (1 * Kumu::Megabyte); + + for (int i = 0; i < _intrinsic_duration; ++i) { + if (ASDCP_FAILURE (reader_A.ReadFrame (i, buffer_A))) { + boost::throw_exception (DCPReadError ("could not read audio frame")); + } + + if (ASDCP_FAILURE (reader_B.ReadFrame (i, buffer_B))) { + boost::throw_exception (DCPReadError ("could not read audio frame")); + } + + if (buffer_A.Size() != buffer_B.Size()) { + note (ERROR, String::compose ("sizes of audio data for frame %1 differ", i)); + return false; + } + + if (memcmp (buffer_A.RoData(), buffer_B.RoData(), buffer_A.Size()) != 0) { + for (uint32_t i = 0; i < buffer_A.Size(); ++i) { + int const d = abs (buffer_A.RoData()[i] - buffer_B.RoData()[i]); + if (d > opt.max_audio_sample_error) { + note (ERROR, String::compose ("PCM data difference of %1", d)); + return false; + } + } + } + } + + return true; +} + +shared_ptr<const SoundFrame> +SoundMXF::get_frame (int n) const +{ + /* XXX: should add on entry point here? */ + return shared_ptr<const SoundFrame> (new SoundFrame (file(), n, _decryption_context)); +} + +shared_ptr<SoundMXFWriter> +SoundMXF::start_write (boost::filesystem::path file, Standard standard) +{ + /* XXX: can't we use a shared_ptr here? */ + return shared_ptr<SoundMXFWriter> (new SoundMXFWriter (this, file, standard)); +} + +string +SoundMXF::key_type () const +{ + return "MDAK"; +} diff --git a/src/sound_mxf.h b/src/sound_mxf.h new file mode 100644 index 00000000..50d10957 --- /dev/null +++ b/src/sound_mxf.h @@ -0,0 +1,78 @@ +/* + Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net> + + This program 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. + + This program 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 this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +/** @file src/sound_mxf.h + * @brief SoundMXF class + */ + +#ifndef LIBDCP_SOUND_MXF_H +#define LIBDCP_SOUND_MXF_H + +#include "mxf.h" +#include "types.h" +#include "metadata.h" + +namespace dcp +{ + +class SoundFrame; +class SoundMXFWriter; + +/** @class SoundMXF + * @brief Representation of a MXF file containing sound + */ +class SoundMXF : public MXF +{ +public: + SoundMXF (boost::filesystem::path file); + SoundMXF (Fraction edit_rate, int sampling_rate, int channels); + + boost::shared_ptr<SoundMXFWriter> start_write (boost::filesystem::path file, Standard standard); + + bool equals ( + boost::shared_ptr<const Content> other, + EqualityOptions opt, + boost::function<void (NoteType, std::string)> note + ) const; + + boost::shared_ptr<const SoundFrame> get_frame (int n) const; + + /** @return number of channels */ + int channels () const { + return _channels; + } + + /** @return sampling rate in Hz */ + int sampling_rate () const { + return _sampling_rate; + } + +private: + std::string key_type () const; + std::string asdcp_kind () const { + return "Sound"; + } + + int _channels; ///< number of channels + int _sampling_rate; ///< sampling rate in Hz +}; + +} + +#endif diff --git a/src/sound_mxf_writer.cc b/src/sound_mxf_writer.cc new file mode 100644 index 00000000..51ba7e40 --- /dev/null +++ b/src/sound_mxf_writer.cc @@ -0,0 +1,120 @@ +/* + Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net> + + This program 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. + + This program 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 this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include "sound_mxf_writer.h" +#include "sound_mxf.h" +#include "exceptions.h" +#include "compose.hpp" +#include "AS_DCP.h" + +using namespace dcp; + +struct SoundMXFWriter::ASDCPState +{ + ASDCP::PCM::MXFWriter mxf_writer; + ASDCP::PCM::FrameBuffer frame_buffer; + ASDCP::WriterInfo writer_info; + ASDCP::PCM::AudioDescriptor audio_desc; + ASDCP::AESEncContext* encryption_context; +}; + +SoundMXFWriter::SoundMXFWriter (SoundMXF* m, boost::filesystem::path file, Standard standard) + : MXFWriter (m, file) + , _state (new SoundMXFWriter::ASDCPState) + , _sound_mxf (m) + , _frame_buffer_offset (0) +{ + _state->encryption_context = m->encryption_context (); + + /* Derived from ASDCP::Wav::SimpleWaveHeader::FillADesc */ + _state->audio_desc.EditRate = ASDCP::Rational (_sound_mxf->edit_rate().numerator, _sound_mxf->edit_rate().denominator); + _state->audio_desc.AudioSamplingRate = ASDCP::Rational (_sound_mxf->sampling_rate(), 1); + _state->audio_desc.Locked = 0; + _state->audio_desc.ChannelCount = _sound_mxf->channels (); + _state->audio_desc.QuantizationBits = 24; + _state->audio_desc.BlockAlign = 3 * _sound_mxf->channels(); + _state->audio_desc.AvgBps = _sound_mxf->sampling_rate() * _state->audio_desc.BlockAlign; + _state->audio_desc.LinkedTrackID = 0; + _state->audio_desc.ChannelFormat = ASDCP::PCM::CF_NONE; + + _state->frame_buffer.Capacity (ASDCP::PCM::CalcFrameBufferSize (_state->audio_desc)); + _state->frame_buffer.Size (ASDCP::PCM::CalcFrameBufferSize (_state->audio_desc)); + memset (_state->frame_buffer.Data(), 0, _state->frame_buffer.Capacity()); + + _sound_mxf->fill_writer_info (&_state->writer_info, standard); + + Kumu::Result_t r = _state->mxf_writer.OpenWrite (file.string().c_str(), _state->writer_info, _state->audio_desc); + if (ASDCP_FAILURE (r)) { + boost::throw_exception (FileError ("could not open audio MXF for writing", file.string(), r)); + } +} + +void +SoundMXFWriter::write (float const * const * data, int frames) +{ + assert (!_finalized); + + for (int i = 0; i < frames; ++i) { + + byte_t* out = _state->frame_buffer.Data() + _frame_buffer_offset; + + /* Write one sample per channel */ + for (int j = 0; j < _sound_mxf->channels(); ++j) { + int32_t const s = data[j][i] * (1 << 23); + *out++ = (s & 0xff); + *out++ = (s & 0xff00) >> 8; + *out++ = (s & 0xff0000) >> 16; + } + _frame_buffer_offset += 3 * _sound_mxf->channels(); + + assert (_frame_buffer_offset <= int (_state->frame_buffer.Capacity())); + + /* Finish the MXF frame if required */ + if (_frame_buffer_offset == int (_state->frame_buffer.Capacity())) { + write_current_frame (); + _frame_buffer_offset = 0; + memset (_state->frame_buffer.Data(), 0, _state->frame_buffer.Capacity()); + } + } +} + +void +SoundMXFWriter::write_current_frame () +{ + ASDCP::Result_t const r = _state->mxf_writer.WriteFrame (_state->frame_buffer, _state->encryption_context, 0); + if (ASDCP_FAILURE (r)) { + boost::throw_exception (MiscError (String::compose ("could not write audio MXF frame (%1)", int (r)))); + } + + ++_frames_written; +} + +void +SoundMXFWriter::finalize () +{ + if (_frame_buffer_offset > 0) { + write_current_frame (); + } + + if (ASDCP_FAILURE (_state->mxf_writer.Finalize())) { + boost::throw_exception (MiscError ("could not finalise audio MXF")); + } + + MXFWriter::finalize (); +} diff --git a/src/sound_mxf_writer.h b/src/sound_mxf_writer.h new file mode 100644 index 00000000..4f60074c --- /dev/null +++ b/src/sound_mxf_writer.h @@ -0,0 +1,55 @@ +/* + Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net> + + This program 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. + + This program 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 this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include "mxf_writer.h" +#include "types.h" +#include <boost/shared_ptr.hpp> +#include <boost/filesystem.hpp> + +namespace dcp { + +class SoundFrame; +class SoundMXF; + +class SoundMXFWriter : public MXFWriter +{ +public: + void write (float const * const *, int); + void finalize (); + +private: + friend class SoundMXF; + + SoundMXFWriter (SoundMXF *, boost::filesystem::path, Standard standard); + + void write_current_frame (); + + /* do this with an opaque pointer so we don't have to include + ASDCP headers + */ + + struct ASDCPState; + boost::shared_ptr<ASDCPState> _state; + + SoundMXF* _sound_mxf; + int _frame_buffer_offset; +}; + +} + diff --git a/src/stereo_picture_frame.cc b/src/stereo_picture_frame.cc index dce1f106..0b56b216 100644 --- a/src/stereo_picture_frame.cc +++ b/src/stereo_picture_frame.cc @@ -1,5 +1,5 @@ /* - Copyright (C) 2012 Carl Hetherington <cth@carlh.net> + Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -17,22 +17,21 @@ */ -#include <openjpeg.h> -#include "AS_DCP.h" -#include "KM_fileio.h" #include "stereo_picture_frame.h" #include "exceptions.h" #include "argb_frame.h" -#include "lut.h" #include "util.h" #include "gamma_lut.h" #include "rgb_xyz.h" +#include "AS_DCP.h" +#include "KM_fileio.h" +#include <openjpeg.h> #define DCI_GAMMA 2.6 using std::string; using boost::shared_ptr; -using namespace libdcp; +using namespace dcp; /** Make a picture frame from a 3D (stereoscopic) asset. * @param mxf_path Path to the asset's MXF file. @@ -59,11 +58,12 @@ StereoPictureFrame::~StereoPictureFrame () delete _buffer; } -/** @param reduce a factor by which to reduce the resolution +/** @param eye Eye to return (EYE_LEFT or EYE_RIGHT). + * @param reduce a factor by which to reduce the resolution * of the image, expressed as a power of two (pass 0 for no * reduction). - * - * @param eye Eye to return (EYE_LEFT or EYE_RIGHT). + * @param srgb_gamma Reciprocal of gamma to use when doing the + * output gamma correction (after conversion from XYZ to RGB). * * @return An ARGB representation of one of the eyes (left or right) * of this frame. This is ARGB in the Cairo sense, so that each @@ -84,7 +84,7 @@ StereoPictureFrame::argb_frame (Eye eye, int reduce, float srgb_gamma) const break; } - return xyz_to_rgb (xyz_frame, GammaLUT::cache.get (12, DCI_GAMMA), GammaLUT::cache.get (12, 1 / srgb_gamma)); + return xyz_to_rgb (xyz_frame, GammaLUT::cache.get (12, DCI_GAMMA, false), GammaLUT::cache.get (12, 1 / srgb_gamma, false)); } uint8_t const * diff --git a/src/stereo_picture_frame.h b/src/stereo_picture_frame.h index 878f6ab4..fe363da5 100644 --- a/src/stereo_picture_frame.h +++ b/src/stereo_picture_frame.h @@ -1,5 +1,5 @@ /* - Copyright (C) 2012-2013 Carl Hetherington <cth@carlh.net> + Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -17,10 +17,12 @@ */ -#include <string> -#include <stdint.h> -#include <boost/shared_ptr.hpp> #include "types.h" +#include <boost/shared_ptr.hpp> +#include <boost/noncopyable.hpp> +#include <boost/filesystem.hpp> +#include <stdint.h> +#include <string> namespace ASDCP { namespace JP2K { @@ -29,12 +31,12 @@ namespace ASDCP { class AESDecContext; } -namespace libdcp { +namespace dcp { class ARGBFrame; /** A single frame of a 3D (stereoscopic) picture asset */ -class StereoPictureFrame +class StereoPictureFrame : public boost::noncopyable { public: StereoPictureFrame (boost::filesystem::path mxf_path, int n); diff --git a/src/stereo_picture_asset.cc b/src/stereo_picture_mxf.cc index 47a41cc2..0188e4e7 100644 --- a/src/stereo_picture_asset.cc +++ b/src/stereo_picture_mxf.cc @@ -1,5 +1,5 @@ /* - Copyright (C) 2012-2013 Carl Hetherington <cth@carlh.net> + Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -18,35 +18,78 @@ */ #include "AS_DCP.h" -#include "stereo_picture_asset.h" +#include "stereo_picture_mxf.h" #include "stereo_picture_frame.h" #include "exceptions.h" -#include "stereo_picture_asset_writer.h" +#include "stereo_picture_mxf_writer.h" using std::string; using std::pair; using std::make_pair; using boost::shared_ptr; using boost::dynamic_pointer_cast; -using namespace libdcp; +using namespace dcp; + +StereoPictureMXF::StereoPictureMXF (boost::filesystem::path file) + : PictureMXF (file) +{ + ASDCP::JP2K::MXFSReader reader; + Kumu::Result_t r = reader.OpenRead (file.string().c_str()); + if (ASDCP_FAILURE (r)) { + boost::throw_exception (MXFFileError ("could not open MXF file for reading", file.string(), r)); + } + + ASDCP::JP2K::PictureDescriptor desc; + if (ASDCP_FAILURE (reader.FillPictureDescriptor (desc))) { + boost::throw_exception (DCPReadError ("could not read video MXF information")); + } + + read_picture_descriptor (desc); + + ASDCP::WriterInfo info; + if (ASDCP_FAILURE (reader.FillWriterInfo (info))) { + boost::throw_exception (DCPReadError ("could not read video MXF information")); + } + + read_writer_info (info); +} + +StereoPictureMXF::StereoPictureMXF (Fraction edit_rate) + : PictureMXF + (edit_rate) +{ + +} + +shared_ptr<const StereoPictureFrame> +StereoPictureMXF::get_frame (int n) const +{ + return shared_ptr<const StereoPictureFrame> (new StereoPictureFrame (file().string(), n)); +} + +shared_ptr<PictureMXFWriter> +StereoPictureMXF::start_write (boost::filesystem::path file, Standard standard, bool overwrite) +{ + return shared_ptr<StereoPictureMXFWriter> (new StereoPictureMXFWriter (this, file, standard, overwrite)); +} bool -StereoPictureAsset::equals (shared_ptr<const Asset> other, EqualityOptions opt, boost::function<void (NoteType, string)> note) const +StereoPictureMXF::equals (shared_ptr<const Content> other, EqualityOptions opt, boost::function<void (NoteType, string)> note) const { - if (!MXFAsset::equals (other, opt, note)) { + if (!MXF::equals (other, opt, note)) { return false; } ASDCP::JP2K::MXFSReader reader_A; - Kumu::Result_t r = reader_A.OpenRead (path().string().c_str()); + Kumu::Result_t r = reader_A.OpenRead (file().string().c_str()); if (ASDCP_FAILURE (r)) { - boost::throw_exception (MXFFileError ("could not open MXF file for reading", path().string(), r)); + boost::throw_exception (MXFFileError ("could not open MXF file for reading", file().string(), r)); } ASDCP::JP2K::MXFSReader reader_B; - r = reader_B.OpenRead (other->path().string().c_str()); + r = reader_B.OpenRead (other->file().string().c_str()); if (ASDCP_FAILURE (r)) { - boost::throw_exception (MXFFileError ("could not open MXF file for reading", path().string(), r)); + boost::throw_exception (MXFFileError ("could not open MXF file for reading", file().string(), r)); } ASDCP::JP2K::PictureDescriptor desc_A; @@ -62,7 +105,7 @@ StereoPictureAsset::equals (shared_ptr<const Asset> other, EqualityOptions opt, return false; } - shared_ptr<const StereoPictureAsset> other_picture = dynamic_pointer_cast<const StereoPictureAsset> (other); + shared_ptr<const StereoPictureMXF> other_picture = dynamic_pointer_cast<const StereoPictureMXF> (other); assert (other_picture); for (int i = 0; i < _intrinsic_duration; ++i) { @@ -88,63 +131,3 @@ StereoPictureAsset::equals (shared_ptr<const Asset> other, EqualityOptions opt, return true; } - -StereoPictureAsset::StereoPictureAsset (boost::filesystem::path directory, boost::filesystem::path mxf_name) - : PictureAsset (directory, mxf_name) -{ - -} - -void -StereoPictureAsset::read () -{ - ASDCP::JP2K::MXFSReader reader; - Kumu::Result_t r = reader.OpenRead (path().string().c_str()); - if (ASDCP_FAILURE (r)) { - boost::throw_exception (MXFFileError ("could not open MXF file for reading", path().string(), r)); - } - - ASDCP::JP2K::PictureDescriptor desc; - if (ASDCP_FAILURE (reader.FillPictureDescriptor (desc))) { - boost::throw_exception (DCPReadError ("could not read video MXF information")); - } - - _size.width = desc.StoredWidth; - _size.height = desc.StoredHeight; -} - -shared_ptr<const StereoPictureFrame> -StereoPictureAsset::get_frame (int n) const -{ - return shared_ptr<const StereoPictureFrame> (new StereoPictureFrame (path().string(), n)); -} - -shared_ptr<PictureAssetWriter> -StereoPictureAsset::start_write (bool overwrite) -{ - return shared_ptr<StereoPictureAssetWriter> (new StereoPictureAssetWriter (this, overwrite)); -} - -string -StereoPictureAsset::cpl_node_name () const -{ - return "msp-cpl:MainStereoscopicPicture"; -} - -pair<string, string> -StereoPictureAsset::cpl_node_attribute () const -{ - if (_interop) { - return make_pair ("xmlns:msp-cpl", "http://www.digicine.com/schemas/437-Y/2007/Main-Stereo-Picture-CPL"); - } else { - return make_pair ("xmlns:msp-cpl", "http://www.smpte-ra.org/schemas/429-10/2008/Main-Stereo-Picture-CPL"); - } - - return make_pair ("", ""); -} - -int -StereoPictureAsset::edit_rate_factor () const -{ - return 2; -} diff --git a/src/stereo_picture_asset.h b/src/stereo_picture_mxf.h index 64cdb6b3..dc4605c7 100644 --- a/src/stereo_picture_asset.h +++ b/src/stereo_picture_mxf.h @@ -1,5 +1,5 @@ /* - Copyright (C) 2012-2013 Carl Hetherington <cth@carlh.net> + Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -17,31 +17,30 @@ */ -#ifndef LIBDCP_STEREO_PICTURE_ASSET_H -#define LIBDCP_STEREO_PICTURE_ASSET_H +#ifndef LIBDCP_STEREO_PICTURE_MXF_H +#define LIBDCP_STEREO_PICTURE_MXF_H -#include "picture_asset.h" +#include "picture_mxf.h" -namespace libdcp { +namespace dcp { /** A 3D (stereoscopic) picture asset */ -class StereoPictureAsset : public PictureAsset +class StereoPictureMXF : public PictureMXF { public: - StereoPictureAsset (boost::filesystem::path directory, boost::filesystem::path mxf_name); + StereoPictureMXF (boost::filesystem::path file); + StereoPictureMXF (Fraction edit_rate); - void read (); - - /** Start a progressive write to a StereoPictureAsset */ - boost::shared_ptr<PictureAssetWriter> start_write (bool); + /** Start a progressive write to a StereoPictureMXF */ + boost::shared_ptr<PictureMXFWriter> start_write (boost::filesystem::path file, Standard, bool); + bool equals ( + boost::shared_ptr<const Content> other, + EqualityOptions opt, + boost::function<void (NoteType, std::string)> note + ) const; + boost::shared_ptr<const StereoPictureFrame> get_frame (int n) const; - bool equals (boost::shared_ptr<const Asset> other, EqualityOptions opt, boost::function<void (NoteType, std::string)> note) const; - -private: - std::string cpl_node_name () const; - std::pair<std::string, std::string> cpl_node_attribute () const; - int edit_rate_factor () const; }; } diff --git a/src/stereo_picture_asset_writer.cc b/src/stereo_picture_mxf_writer.cc index b4b0ad52..4f98c60e 100644 --- a/src/stereo_picture_asset_writer.cc +++ b/src/stereo_picture_mxf_writer.cc @@ -1,5 +1,5 @@ /* - Copyright (C) 2012-2013 Carl Hetherington <cth@carlh.net> + Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -17,37 +17,38 @@ */ +#include "stereo_picture_mxf_writer.h" +#include "exceptions.h" +#include "picture_mxf.h" #include "AS_DCP.h" #include "KM_fileio.h" -#include "stereo_picture_asset_writer.h" -#include "exceptions.h" -#include "picture_asset.h" -#include "picture_asset_writer_common.cc" +#include "picture_mxf_writer_common.cc" using std::istream; using std::ostream; using std::string; using boost::shared_ptr; -using namespace libdcp; +using namespace dcp; -struct StereoPictureAssetWriter::ASDCPState : public ASDCPStateBase +struct StereoPictureMXFWriter::ASDCPState : public ASDCPStateBase { ASDCP::JP2K::MXFSWriter mxf_writer; }; -StereoPictureAssetWriter::StereoPictureAssetWriter (PictureAsset* asset, bool overwrite) - : PictureAssetWriter (asset, overwrite) - , _state (new StereoPictureAssetWriter::ASDCPState) +StereoPictureMXFWriter::StereoPictureMXFWriter (PictureMXF* mxf, boost::filesystem::path file, Standard standard, bool overwrite) + : PictureMXFWriter (mxf, file, standard, overwrite) + , _state (new StereoPictureMXFWriter::ASDCPState) , _next_eye (EYE_LEFT) { - _state->encryption_context = asset->encryption_context (); + _state->encryption_context = mxf->encryption_context (); } void -StereoPictureAssetWriter::start (uint8_t* data, int size) +StereoPictureMXFWriter::start (uint8_t* data, int size) { - libdcp::start (this, _state, _asset, data, size); + dcp::start (this, _state, _standard, _picture_mxf, data, size); + _picture_mxf->set_frame_rate (Fraction (_picture_mxf->edit_rate().numerator * 2, _picture_mxf->edit_rate().denominator)); } /** Write a frame for one eye. Frames must be written left, then right, then left etc. @@ -55,7 +56,7 @@ StereoPictureAssetWriter::start (uint8_t* data, int size) * @param size Size of data. */ FrameInfo -StereoPictureAssetWriter::write (uint8_t* data, int size) +StereoPictureMXFWriter::write (uint8_t* data, int size) { assert (!_finalized); @@ -79,41 +80,42 @@ StereoPictureAssetWriter::write (uint8_t* data, int size) ); if (ASDCP_FAILURE (r)) { - boost::throw_exception (MXFFileError ("error in writing video MXF", _asset->path().string(), r)); + boost::throw_exception (MXFFileError ("error in writing video MXF", _mxf->file().string(), r)); } _next_eye = _next_eye == EYE_LEFT ? EYE_RIGHT : EYE_LEFT; - ++_frames_written; + if (_next_eye == EYE_LEFT) { + ++_frames_written; + } + return FrameInfo (before_offset, _state->mxf_writer.Tell() - before_offset, hash); } void -StereoPictureAssetWriter::fake_write (int size) +StereoPictureMXFWriter::fake_write (int size) { assert (_started); assert (!_finalized); Kumu::Result_t r = _state->mxf_writer.FakeWriteFrame (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", _asset->path().string(), r)); + boost::throw_exception (MXFFileError ("error in writing video MXF", _mxf->file().string(), r)); } _next_eye = _next_eye == EYE_LEFT ? EYE_RIGHT : EYE_LEFT; - ++_frames_written; + if (_next_eye == EYE_LEFT) { + ++_frames_written; + } } void -StereoPictureAssetWriter::finalize () +StereoPictureMXFWriter::finalize () { - assert (!_finalized); - Kumu::Result_t r = _state->mxf_writer.Finalize(); if (ASDCP_FAILURE (r)) { - boost::throw_exception (MXFFileError ("error in finalizing video MXF", _asset->path().string(), r)); + boost::throw_exception (MXFFileError ("error in finalizing video MXF", _mxf->file().string(), r)); } - _finalized = true; - _asset->set_intrinsic_duration (_frames_written / 2); - _asset->set_duration (_frames_written / 2); + PictureMXFWriter::finalize (); } diff --git a/src/stereo_picture_asset_writer.h b/src/stereo_picture_mxf_writer.h index 771524c4..ebd9dbec 100644 --- a/src/stereo_picture_asset_writer.h +++ b/src/stereo_picture_mxf_writer.h @@ -1,5 +1,5 @@ /* - Copyright (C) 2012-2013 Carl Hetherington <cth@carlh.net> + Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -17,14 +17,15 @@ */ +#include "picture_mxf_writer.h" +#include "types.h" +#include <boost/shared_ptr.hpp> +#include <boost/utility.hpp> #include <stdint.h> #include <string> #include <fstream> -#include <boost/shared_ptr.hpp> -#include <boost/utility.hpp> -#include "picture_asset_writer.h" -namespace libdcp { +namespace dcp { /** A helper class for writing to StereoPictureAssets progressively (i.e. writing frame-by-frame, * rather than giving libdcp all the frames in one go). @@ -36,7 +37,7 @@ namespace libdcp { * The action of finalize() can't be done in MonoPictureAssetWriter's destructor as it may * throw an exception. */ -class StereoPictureAssetWriter : public PictureAssetWriter +class StereoPictureMXFWriter : public PictureMXFWriter { public: FrameInfo write (uint8_t *, int); @@ -44,9 +45,9 @@ public: void finalize (); private: - friend class StereoPictureAsset; + friend class StereoPictureMXF; - StereoPictureAssetWriter (PictureAsset *, bool); + StereoPictureMXFWriter (PictureMXF *, boost::filesystem::path file, Standard, bool); void start (uint8_t *, int); /* do this with an opaque pointer so we don't have to include @@ -56,7 +57,7 @@ private: struct ASDCPState; boost::shared_ptr<ASDCPState> _state; - libdcp::Eye _next_eye; + dcp::Eye _next_eye; }; } diff --git a/src/subtitle.cc b/src/subtitle.cc new file mode 100644 index 00000000..8c40254a --- /dev/null +++ b/src/subtitle.cc @@ -0,0 +1,60 @@ +/* + Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net> + + This program 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. + + This program 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 this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include "subtitle.h" +#include "xml.h" +#include "font.h" +#include "text.h" +#include <libcxml/cxml.h> + +using std::string; +using boost::shared_ptr; +using boost::lexical_cast; +using namespace dcp; + +Subtitle::Subtitle (boost::shared_ptr<const cxml::Node> node) +{ + in = Time (node->string_attribute ("TimeIn")); + out = Time (node->string_attribute ("TimeOut")); + font_nodes = type_children<Font> (node, "Font"); + text_nodes = type_children<Text> (node, "Text"); + fade_up_time = fade_time (node, "FadeUpTime"); + fade_down_time = fade_time (node, "FadeDownTime"); +} + +Time +Subtitle::fade_time (shared_ptr<const cxml::Node> node, string name) +{ + string const u = node->optional_string_attribute (name).get_value_or (""); + Time t; + + if (u.empty ()) { + t = Time (0, 0, 0, 20); + } else if (u.find (":") != string::npos) { + t = Time (u); + } else { + t = Time (0, 0, 0, lexical_cast<int> (u)); + } + + if (t > Time (0, 0, 8, 0)) { + t = Time (0, 0, 8, 0); + } + + return t; +} diff --git a/src/lut.h b/src/subtitle.h index ad291467..073bfb0c 100644 --- a/src/lut.h +++ b/src/subtitle.h @@ -1,5 +1,5 @@ /* - Copyright (C) 2012 Carl Hetherington <cth@carlh.net> + Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -17,45 +17,37 @@ */ -#ifndef LIBDCP_LUT_H -#define LIBDCP_LUT_H +#ifndef LIBDCP_SUBTITLE_H +#define LIBDCP_SUBTITLE_H -#include <cmath> -#include <boost/utility.hpp> +#include "dcp_time.h" +#include <boost/shared_ptr.hpp> +#include <list> -namespace libdcp { +namespace cxml { + class Node; +} + +namespace dcp { + +class Font; +class Text; -class LUT : boost::noncopyable +class Subtitle { public: - LUT(int bit_depth, float gamma) - : _lut(0) - , _bit_depth (bit_depth) - , _gamma (gamma) - { - _lut = new float[int(std::pow(2.0f, _bit_depth))]; - } - - virtual ~LUT() { - delete[] _lut; - } - - float const * lut() const { - return _lut; - } - - int bit_depth () const { - return _bit_depth; - } - - float gamma () const { - return _gamma; - } - -protected: - float* _lut; - int _bit_depth; - float _gamma; + Subtitle () {} + Subtitle (boost::shared_ptr<const cxml::Node> node); + + Time in; + Time out; + Time fade_up_time; + Time fade_down_time; + std::list<boost::shared_ptr<Font> > font_nodes; + std::list<boost::shared_ptr<Text> > text_nodes; + +private: + Time fade_time (boost::shared_ptr<const cxml::Node>, std::string name); }; } diff --git a/src/subtitle_asset.h b/src/subtitle_asset.h deleted file mode 100644 index a7ec641c..00000000 --- a/src/subtitle_asset.h +++ /dev/null @@ -1,207 +0,0 @@ -/* - Copyright (C) 2012 Carl Hetherington <cth@carlh.net> - - This program 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. - - This program 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 this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - -*/ - -#ifndef LIBDCP_SUBTITLE_ASSET_H -#define LIBDCP_SUBTITLE_ASSET_H - -#include <libcxml/cxml.h> -#include "asset.h" -#include "dcp_time.h" - -namespace libdcp -{ - -namespace parse -{ - class Font; - class Text; - class Subtitle; - class LoadFont; -} - -class Subtitle -{ -public: - Subtitle ( - std::string font, - bool italic, - Color color, - int size, - Time in, - Time out, - float v_position, - VAlign v_align, - std::string text, - Effect effect, - Color effect_color, - Time fade_up_time, - Time fade_down_time - ); - - std::string font () const { - return _font; - } - - bool italic () const { - return _italic; - } - - Color color () const { - return _color; - } - - Time in () const { - return _in; - } - - Time out () const { - return _out; - } - - std::string text () const { - return _text; - } - - float v_position () const { - return _v_position; - } - - VAlign v_align () const { - return _v_align; - } - - Effect effect () const { - return _effect; - } - - Color effect_color () const { - return _effect_color; - } - - Time fade_up_time () const { - return _fade_up_time; - } - - Time fade_down_time () const { - return _fade_down_time; - } - - int size () const { - return _size; - } - - int size_in_pixels (int screen_height) const; - -private: - std::string _font; - bool _italic; - Color _color; - /** Size in points as if the screen height is 11 inches, so a 72pt font - * would be 1/11th of the screen height. - */ - int _size; - Time _in; - Time _out; - /** Vertical position as a proportion of the screen height from the top - * (between 0 and 1) - */ - float _v_position; - VAlign _v_align; - std::string _text; - Effect _effect; - Color _effect_color; - Time _fade_up_time; - Time _fade_down_time; -}; - -bool operator== (Subtitle const & a, Subtitle const & b); -std::ostream& operator<< (std::ostream& s, Subtitle const & sub); - -class SubtitleAsset : public Asset -{ -public: - SubtitleAsset (std::string directory, std::string xml_file); - SubtitleAsset (std::string directory, std::string movie_title, std::string language); - - void write_to_cpl (xmlpp::Element *) const; - virtual bool equals (boost::shared_ptr<const Asset>, EqualityOptions, boost::function<void (NoteType, std::string)> note) const { - /* XXX */ - note (ERROR, "subtitle assets not compared yet"); - return true; - } - - std::string language () const { - return _language; - } - - std::list<boost::shared_ptr<Subtitle> > subtitles_at (Time t) const; - std::list<boost::shared_ptr<Subtitle> > const & subtitles () const { - return _subtitles; - } - - void add (boost::shared_ptr<Subtitle>); - - void read_xml (std::string); - void write_xml () const; - Glib::ustring xml_as_string () const; - -protected: - - std::string asdcp_kind () const { - return "Subtitle"; - } - -private: - std::string font_id_to_name (std::string id) const; - void read_mxf (std::string); - void read_xml (boost::shared_ptr<cxml::Document>); - - struct ParseState { - std::list<boost::shared_ptr<parse::Font> > font_nodes; - std::list<boost::shared_ptr<parse::Text> > text_nodes; - std::list<boost::shared_ptr<parse::Subtitle> > subtitle_nodes; - }; - - void maybe_add_subtitle (std::string text, ParseState const & parse_state); - - void examine_font_nodes ( - boost::shared_ptr<const cxml::Node> xml, - std::list<boost::shared_ptr<parse::Font> > const & font_nodes, - ParseState& parse_state - ); - - void examine_text_nodes ( - boost::shared_ptr<const cxml::Node> xml, - std::list<boost::shared_ptr<parse::Text> > const & text_nodes, - ParseState& parse_state - ); - - boost::optional<std::string> _movie_title; - /* strangely, this is sometimes a string */ - std::string _reel_number; - std::string _language; - std::list<boost::shared_ptr<parse::LoadFont> > _load_font_nodes; - - std::list<boost::shared_ptr<Subtitle> > _subtitles; - bool _need_sort; -}; - -} - -#endif diff --git a/src/subtitle_asset.cc b/src/subtitle_content.cc index 4eb1a9cd..f338517e 100644 --- a/src/subtitle_asset.cc +++ b/src/subtitle_content.cc @@ -1,5 +1,5 @@ /* - Copyright (C) 2012 Carl Hetherington <cth@carlh.net> + Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -17,17 +17,17 @@ */ -#include <fstream> -#include <cerrno> -#include <boost/lexical_cast.hpp> -#include <boost/algorithm/string.hpp> -#include <libxml++/nodes/element.h> -#include "AS_DCP.h" -#include "KM_util.h" -#include "subtitle_asset.h" -#include "parse/subtitle.h" +#include "subtitle_content.h" #include "util.h" #include "xml.h" +#include "font.h" +#include "text.h" +#include "load_font.h" +#include "subtitle_string.h" +#include <libxml++/nodes/element.h> +#include <boost/lexical_cast.hpp> +#include <boost/algorithm/string.hpp> +#include <fstream> using std::string; using std::list; @@ -38,117 +38,56 @@ using std::cout; using boost::shared_ptr; using boost::lexical_cast; using boost::optional; -using namespace libdcp; +using namespace dcp; -SubtitleAsset::SubtitleAsset (string directory, string file) - : Asset (directory, file) - , _need_sort (false) -{ - /* Grotesque hack: we should look in the PKL to see what type this file is; - instead we'll look at the first character to decide what to do. - I think this is easily fixable (properly) in 1.0. - */ - - FILE* f = fopen_boost (path(), "r"); - if (!f) { - throw FileError ("Could not open file for reading", file, errno); - } - unsigned char test[1]; - fread (test, 1, 1, f); - fclose (f); - - if (test[0] == '<' || test[0] == 0xef) { - read_xml (path().string()); - } else { - read_mxf (path().string()); - } -} - -SubtitleAsset::SubtitleAsset (string directory, string movie_title, string language) - : Asset (directory) - , _movie_title (movie_title) - , _reel_number ("1") - , _language (language) +SubtitleContent::SubtitleContent (boost::filesystem::path file) + : Content (file) , _need_sort (false) { - -} - -void -SubtitleAsset::read_mxf (string mxf_file) -{ - ASDCP::TimedText::MXFReader reader; - Kumu::Result_t r = reader.OpenRead (mxf_file.c_str ()); - if (ASDCP_FAILURE (r)) { - boost::throw_exception (MXFFileError ("could not open MXF file for reading", mxf_file, r)); - } - - string s; - reader.ReadTimedTextResource (s, 0, 0); - shared_ptr<cxml::Document> xml (new cxml::Document ("SubtitleReel")); - stringstream t; - t << s; - xml->read_stream (t); - read_xml (xml); -} - -void -SubtitleAsset::read_xml (string xml_file) -{ shared_ptr<cxml::Document> xml (new cxml::Document ("DCSubtitle")); - xml->read_file (xml_file); - read_xml (xml); -} - -void -SubtitleAsset::read_xml (shared_ptr<cxml::Document> xml) -{ - /* XXX: hacks aplenty in here; need separate parsers for DCSubtitle and SubtitleReel */ + xml->read_file (file); - /* DCSubtitle */ - optional<string> x = xml->optional_string_child ("SubtitleID"); - if (!x) { - /* SubtitleReel */ - x = xml->optional_string_child ("Id"); - } - _uuid = x.get_value_or (""); - - _movie_title = xml->optional_string_child ("MovieTitle"); + _id = xml->string_child ("SubtitleID"); + _movie_title = xml->string_child ("MovieTitle"); _reel_number = xml->string_child ("ReelNumber"); _language = xml->string_child ("Language"); xml->ignore_child ("LoadFont"); - list<shared_ptr<libdcp::parse::Font> > font_nodes = type_children<libdcp::parse::Font> (xml, "Font"); - _load_font_nodes = type_children<libdcp::parse::LoadFont> (xml, "LoadFont"); + list<shared_ptr<dcp::Font> > font_nodes = type_children<dcp::Font> (xml, "Font"); + _load_font_nodes = type_children<dcp::LoadFont> (xml, "LoadFont"); /* Now make Subtitle objects to represent the raw XML nodes in a sane way. */ - - shared_ptr<cxml::Node> subtitle_list = xml->optional_node_child ("SubtitleList"); - if (subtitle_list) { - list<shared_ptr<libdcp::parse::Font> > font = type_children<libdcp::parse::Font> (subtitle_list, "Font"); - copy (font.begin(), font.end(), back_inserter (font_nodes)); - } ParseState parse_state; examine_font_nodes (xml, font_nodes, parse_state); } +SubtitleContent::SubtitleContent (Fraction edit_rate, string movie_title, string language) + : Content (edit_rate) + , _movie_title (movie_title) + , _reel_number ("1") + , _language (language) + , _need_sort (false) +{ + +} + void -SubtitleAsset::examine_font_nodes ( +SubtitleContent::examine_font_nodes ( shared_ptr<const cxml::Node> xml, - list<shared_ptr<libdcp::parse::Font> > const & font_nodes, + list<shared_ptr<dcp::Font> > const & font_nodes, ParseState& parse_state ) { - for (list<shared_ptr<libdcp::parse::Font> >::const_iterator i = font_nodes.begin(); i != font_nodes.end(); ++i) { + for (list<shared_ptr<dcp::Font> >::const_iterator i = font_nodes.begin(); i != font_nodes.end(); ++i) { parse_state.font_nodes.push_back (*i); maybe_add_subtitle ((*i)->text, parse_state); - for (list<shared_ptr<libdcp::parse::Subtitle> >::iterator j = (*i)->subtitle_nodes.begin(); j != (*i)->subtitle_nodes.end(); ++j) { + for (list<shared_ptr<dcp::Subtitle> >::iterator j = (*i)->subtitle_nodes.begin(); j != (*i)->subtitle_nodes.end(); ++j) { parse_state.subtitle_nodes.push_back (*j); examine_text_nodes (xml, (*j)->text_nodes, parse_state); examine_font_nodes (xml, (*j)->font_nodes, parse_state); @@ -163,13 +102,13 @@ SubtitleAsset::examine_font_nodes ( } void -SubtitleAsset::examine_text_nodes ( +SubtitleContent::examine_text_nodes ( shared_ptr<const cxml::Node> xml, - list<shared_ptr<libdcp::parse::Text> > const & text_nodes, + list<shared_ptr<dcp::Text> > const & text_nodes, ParseState& parse_state ) { - for (list<shared_ptr<libdcp::parse::Text> >::const_iterator i = text_nodes.begin(); i != text_nodes.end(); ++i) { + for (list<shared_ptr<dcp::Text> >::const_iterator i = text_nodes.begin(); i != text_nodes.end(); ++i) { parse_state.text_nodes.push_back (*i); maybe_add_subtitle ((*i)->text, parse_state); examine_font_nodes (xml, (*i)->font_nodes, parse_state); @@ -178,7 +117,7 @@ SubtitleAsset::examine_text_nodes ( } void -SubtitleAsset::maybe_add_subtitle (string text, ParseState const & parse_state) +SubtitleContent::maybe_add_subtitle (string text, ParseState const & parse_state) { if (empty_or_white_space (text)) { return; @@ -191,13 +130,13 @@ SubtitleAsset::maybe_add_subtitle (string text, ParseState const & parse_state) assert (!parse_state.text_nodes.empty ()); assert (!parse_state.subtitle_nodes.empty ()); - libdcp::parse::Font effective_font (parse_state.font_nodes); - libdcp::parse::Text effective_text (*parse_state.text_nodes.back ()); - libdcp::parse::Subtitle effective_subtitle (*parse_state.subtitle_nodes.back ()); + dcp::Font effective_font (parse_state.font_nodes); + dcp::Text effective_text (*parse_state.text_nodes.back ()); + dcp::Subtitle effective_subtitle (*parse_state.subtitle_nodes.back ()); _subtitles.push_back ( - shared_ptr<Subtitle> ( - new Subtitle ( + shared_ptr<SubtitleString> ( + new SubtitleString ( font_id_to_name (effective_font.id), effective_font.italic.get(), effective_font.color.get(), @@ -216,11 +155,11 @@ SubtitleAsset::maybe_add_subtitle (string text, ParseState const & parse_state) ); } -list<shared_ptr<Subtitle> > -SubtitleAsset::subtitles_at (Time t) const +list<shared_ptr<SubtitleString> > +SubtitleContent::subtitles_at (Time t) const { - list<shared_ptr<Subtitle> > s; - for (list<shared_ptr<Subtitle> >::const_iterator i = _subtitles.begin(); i != _subtitles.end(); ++i) { + list<shared_ptr<SubtitleString> > s; + for (list<shared_ptr<SubtitleString> >::const_iterator i = _subtitles.begin(); i != _subtitles.end(); ++i) { if ((*i)->in() <= t && t <= (*i)->out ()) { s.push_back (*i); } @@ -230,9 +169,9 @@ SubtitleAsset::subtitles_at (Time t) const } std::string -SubtitleAsset::font_id_to_name (string id) const +SubtitleContent::font_id_to_name (string id) const { - list<shared_ptr<libdcp::parse::LoadFont> >::const_iterator i = _load_font_nodes.begin(); + list<shared_ptr<dcp::LoadFont> >::const_iterator i = _load_font_nodes.begin(); while (i != _load_font_nodes.end() && (*i)->id != id) { ++i; } @@ -241,116 +180,22 @@ SubtitleAsset::font_id_to_name (string id) const return ""; } - if ((*i)->uri && (*i)->uri.get() == "arial.ttf") { + if ((*i)->uri == "arial.ttf") { return "Arial"; } return ""; } -Subtitle::Subtitle ( - string font, - bool italic, - Color color, - int size, - Time in, - Time out, - float v_position, - VAlign v_align, - string text, - Effect effect, - Color effect_color, - Time fade_up_time, - Time fade_down_time - ) - : _font (font) - , _italic (italic) - , _color (color) - , _size (size) - , _in (in) - , _out (out) - , _v_position (v_position) - , _v_align (v_align) - , _text (text) - , _effect (effect) - , _effect_color (effect_color) - , _fade_up_time (fade_up_time) - , _fade_down_time (fade_down_time) -{ - -} - -int -Subtitle::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 - height. - */ - - return _size * screen_height / (11 * 72); -} - -bool -libdcp::operator== (Subtitle const & a, Subtitle const & b) -{ - return ( - a.font() == b.font() && - a.italic() == b.italic() && - a.color() == b.color() && - a.size() == b.size() && - a.in() == b.in() && - a.out() == b.out() && - a.v_position() == b.v_position() && - a.v_align() == b.v_align() && - a.text() == b.text() && - a.effect() == b.effect() && - a.effect_color() == b.effect_color() && - a.fade_up_time() == b.fade_up_time() && - a.fade_down_time() == b.fade_down_time() - ); -} - -ostream& -libdcp::operator<< (ostream& s, Subtitle 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" - << "font " << sub.font() << ", "; - - if (sub.italic()) { - s << "italic"; - } else { - s << "non-italic"; - } - - s << ", size " << sub.size() << ", color " << sub.color() << ", vpos " << sub.v_position() << ", valign " << ((int) sub.v_align()) << ";\n" - << "effect " << ((int) sub.effect()) << ", effect color " << sub.effect_color(); - - return s; -} - void -SubtitleAsset::add (shared_ptr<Subtitle> s) +SubtitleContent::add (shared_ptr<SubtitleString> s) { _subtitles.push_back (s); _need_sort = true; } -void -SubtitleAsset::write_to_cpl (xmlpp::Element* node) const -{ - /* XXX: should EditRate, Duration and IntrinsicDuration be in here? */ - - xmlpp::Node* ms = node->add_child ("MainSubtitle"); - ms->add_child("Id")->add_child_text("urn:uuid:" + _uuid); - ms->add_child("AnnotationText")->add_child_text (_file_name.string ()); - /* XXX */ - ms->add_child("EntryPoint")->add_child_text ("0"); -} - struct SubtitleSorter { - bool operator() (shared_ptr<Subtitle> a, shared_ptr<Subtitle> b) { + bool operator() (shared_ptr<SubtitleString> a, shared_ptr<SubtitleString> b) { if (a->in() != b->in()) { return a->in() < b->in(); } @@ -359,25 +204,23 @@ struct SubtitleSorter { }; void -SubtitleAsset::write_xml () const +SubtitleContent::write_xml () const { - FILE* f = fopen_boost (path (), "r"); + FILE* f = fopen_boost (file (), "r"); Glib::ustring const s = xml_as_string (); fwrite (s.c_str(), 1, s.length(), f); fclose (f); } Glib::ustring -SubtitleAsset::xml_as_string () const +SubtitleContent::xml_as_string () const { xmlpp::Document doc; xmlpp::Element* root = doc.create_root_node ("DCSubtitle"); root->set_attribute ("Version", "1.0"); - root->add_child("SubtitleID")->add_child_text (_uuid); - if (_movie_title) { - root->add_child("MovieTitle")->add_child_text (_movie_title.get ()); - } + root->add_child("SubtitleID")->add_child_text (_id); + root->add_child("MovieTitle")->add_child_text (_movie_title); root->add_child("ReelNumber")->add_child_text (lexical_cast<string> (_reel_number)); root->add_child("Language")->add_child_text (_language); @@ -388,12 +231,10 @@ SubtitleAsset::xml_as_string () const if (!_load_font_nodes.empty ()) { xmlpp::Element* load_font = root->add_child("LoadFont"); load_font->set_attribute("Id", _load_font_nodes.front()->id); - if (_load_font_nodes.front()->uri) { - load_font->set_attribute("URI", _load_font_nodes.front()->uri.get ()); - } + load_font->set_attribute("URI", _load_font_nodes.front()->uri); } - list<shared_ptr<Subtitle> > sorted = _subtitles; + list<shared_ptr<SubtitleString> > sorted = _subtitles; if (_need_sort) { sorted.sort (SubtitleSorter ()); } @@ -415,7 +256,7 @@ SubtitleAsset::xml_as_string () const xmlpp::Element* font = 0; xmlpp::Element* subtitle = 0; - for (list<shared_ptr<Subtitle> >::iterator i = sorted.begin(); i != sorted.end(); ++i) { + for (list<shared_ptr<SubtitleString> >::iterator i = sorted.begin(); i != sorted.end(); ++i) { /* We will start a new <Font>...</Font> whenever some font property changes. I suppose we should really make an optimal hierarchy of <Font> tags, but diff --git a/src/subtitle_content.h b/src/subtitle_content.h new file mode 100644 index 00000000..410c5934 --- /dev/null +++ b/src/subtitle_content.h @@ -0,0 +1,113 @@ +/* + Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net> + + This program 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. + + This program 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 this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#ifndef LIBDCP_SUBTITLE_CONTENT_H +#define LIBDCP_SUBTITLE_CONTENT_H + +#include "content.h" +#include "dcp_time.h" +#include <libcxml/cxml.h> + +namespace dcp +{ + +class SubtitleString; +class Font; +class Text; +class Subtitle; +class LoadFont; + +/** @class SubtitleContent + * @brief A representation of an XML file containing subtitles. + */ +class SubtitleContent : public Content +{ +public: + SubtitleContent (boost::filesystem::path file); + SubtitleContent (Fraction edit_rate, std::string movie_title, std::string language); + + bool equals ( + boost::shared_ptr<const Content>, + EqualityOptions, + boost::function<void (NoteType, std::string)> note + ) const { + /* XXX */ + note (ERROR, "subtitle content not compared yet"); + return true; + } + + std::string language () const { + return _language; + } + + std::list<boost::shared_ptr<SubtitleString> > subtitles_at (Time t) const; + std::list<boost::shared_ptr<SubtitleString> > const & subtitles () const { + return _subtitles; + } + + void add (boost::shared_ptr<SubtitleString>); + + void write_xml () const; + Glib::ustring xml_as_string () const; + +protected: + std::string pkl_type (Standard) const { + return "text/xml"; + } + + std::string asdcp_kind () const { + return "Subtitle"; + } + +private: + std::string font_id_to_name (std::string id) const; + + struct ParseState { + std::list<boost::shared_ptr<Font> > font_nodes; + std::list<boost::shared_ptr<Text> > text_nodes; + std::list<boost::shared_ptr<Subtitle> > subtitle_nodes; + }; + + void maybe_add_subtitle (std::string text, ParseState const & parse_state); + + void examine_font_nodes ( + boost::shared_ptr<const cxml::Node> xml, + std::list<boost::shared_ptr<Font> > const & font_nodes, + ParseState& parse_state + ); + + void examine_text_nodes ( + boost::shared_ptr<const cxml::Node> xml, + std::list<boost::shared_ptr<Text> > const & text_nodes, + ParseState& parse_state + ); + + std::string _movie_title; + /* strangely, this is sometimes a string */ + std::string _reel_number; + std::string _language; + std::list<boost::shared_ptr<LoadFont> > _load_font_nodes; + + std::list<boost::shared_ptr<SubtitleString> > _subtitles; + bool _need_sort; +}; + +} + +#endif diff --git a/src/subtitle_string.cc b/src/subtitle_string.cc new file mode 100644 index 00000000..66869fd5 --- /dev/null +++ b/src/subtitle_string.cc @@ -0,0 +1,107 @@ +/* + Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net> + + This program 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. + + This program 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 this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include "subtitle_string.h" +#include "xml.h" + +using std::string; +using std::ostream; +using namespace dcp; + +SubtitleString::SubtitleString ( + string font, + bool italic, + Color color, + int size, + Time in, + Time out, + float v_position, + VAlign v_align, + string text, + Effect effect, + Color effect_color, + Time fade_up_time, + Time fade_down_time + ) + : _font (font) + , _italic (italic) + , _color (color) + , _size (size) + , _in (in) + , _out (out) + , _v_position (v_position) + , _v_align (v_align) + , _text (text) + , _effect (effect) + , _effect_color (effect_color) + , _fade_up_time (fade_up_time) + , _fade_down_time (fade_down_time) +{ + +} + +int +SubtitleString::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 + height. + */ + + return _size * screen_height / (11 * 72); +} + +bool +dcp::operator== (SubtitleString const & a, SubtitleString const & b) +{ + return ( + a.font() == b.font() && + a.italic() == b.italic() && + a.color() == b.color() && + a.size() == b.size() && + a.in() == b.in() && + a.out() == b.out() && + a.v_position() == b.v_position() && + a.v_align() == b.v_align() && + a.text() == b.text() && + a.effect() == b.effect() && + a.effect_color() == b.effect_color() && + a.fade_up_time() == b.fade_up_time() && + a.fade_down_time() == b.fade_down_time() + ); +} + +ostream& +dcp::operator<< (ostream& s, SubtitleString 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" + << "font " << sub.font() << ", "; + + if (sub.italic()) { + s << "italic"; + } else { + s << "non-italic"; + } + + s << ", size " << sub.size() << ", color " << sub.color() << ", vpos " << sub.v_position() << ", valign " << ((int) sub.v_align()) << ";\n" + << "effect " << ((int) sub.effect()) << ", effect color " << sub.effect_color(); + + return s; +} diff --git a/src/subtitle_string.h b/src/subtitle_string.h new file mode 100644 index 00000000..076a6ec7 --- /dev/null +++ b/src/subtitle_string.h @@ -0,0 +1,129 @@ +/* + Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net> + + This program 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. + + This program 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 this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#ifndef LIBDCP_SUBTITLE_STRING_H +#define LIBDCP_SUBTITLE_STRING_H + +#include "types.h" +#include "dcp_time.h" +#include <string> + +namespace dcp { + +class SubtitleString +{ +public: + SubtitleString ( + std::string font, + bool italic, + Color color, + int size, + Time in, + Time out, + float v_position, + VAlign v_align, + std::string text, + Effect effect, + Color effect_color, + Time fade_up_time, + Time fade_down_time + ); + + std::string font () const { + return _font; + } + + bool italic () const { + return _italic; + } + + Color color () const { + return _color; + } + + Time in () const { + return _in; + } + + Time out () const { + return _out; + } + + std::string text () const { + return _text; + } + + float v_position () const { + return _v_position; + } + + VAlign v_align () const { + return _v_align; + } + + Effect effect () const { + return _effect; + } + + Color effect_color () const { + return _effect_color; + } + + Time fade_up_time () const { + return _fade_up_time; + } + + Time fade_down_time () const { + return _fade_down_time; + } + + int size () const { + return _size; + } + + int size_in_pixels (int screen_height) const; + +private: + std::string _font; + bool _italic; + Color _color; + /** Size in points as if the screen height is 11 inches, so a 72pt font + * would be 1/11th of the screen height. + */ + int _size; + Time _in; + Time _out; + /** Vertical position as a proportion of the screen height from the top + * (between 0 and 1) + */ + float _v_position; + VAlign _v_align; + std::string _text; + Effect _effect; + Color _effect_color; + Time _fade_up_time; + Time _fade_down_time; +}; + +bool operator== (SubtitleString const & a, SubtitleString const & b); +std::ostream& operator<< (std::ostream& s, SubtitleString const & sub); + +} + +#endif diff --git a/src/text.cc b/src/text.cc new file mode 100644 index 00000000..888cab29 --- /dev/null +++ b/src/text.cc @@ -0,0 +1,49 @@ +/* + Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net> + + This program 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. + + This program 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 this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +/** @file src/text.cc + * @brief Text class for parsing subtitle XML. + */ + +#include "text.h" +#include "xml.h" +#include "font.h" +#include <libcxml/cxml.h> + +using std::string; +using boost::shared_ptr; +using boost::optional; +using namespace dcp; + +/** Read a <Text> node from a subtitle XML file, noting its contents + * in this object's member variables. + * @param node Node to read. + */ +Text::Text (boost::shared_ptr<const cxml::Node> node) + : v_align (CENTER) +{ + text = node->content (); + v_position = node->number_attribute<float> ("VPosition"); + optional<string> v = node->optional_string_attribute ("VAlign"); + if (v) { + v_align = string_to_valign (v.get ()); + } + + font_nodes = type_children<Font> (node, "Font"); +} diff --git a/src/parse/pkl.h b/src/text.h index 13d87fa1..268d146c 100644 --- a/src/parse/pkl.h +++ b/src/text.h @@ -1,5 +1,5 @@ /* - Copyright (C) 2012 Carl Hetherington <cth@carlh.net> + Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -17,44 +17,40 @@ */ -/** @file src/parse/pkl.h - * @brief Classes used to parse a PKL +/** @file src/text.h + * @brief Text class for parsing subtitle XML. */ +#include "types.h" #include <boost/shared_ptr.hpp> -#include "../xml.h" +#include <list> -namespace libdcp { +namespace cxml { + class Node; +} -namespace parse { +namespace dcp { -class PKLAsset -{ -public: - PKLAsset () {} - PKLAsset (boost::shared_ptr<const cxml::Node>); - - std::string id; - std::string annotation_text; - std::string hash; - int64_t size; - std::string type; - std::string original_file_name; -}; +class Font; -class PKL +/** @class Text + * @brief Parser for Text nodes from subtitle XML. + */ +class Text { public: - PKL (std::string file); - - std::string id; - std::string annotation_text; - std::string issue_date; - std::string issuer; - std::string creator; - std::list<boost::shared_ptr<PKLAsset> > assets; -}; + /** Construct a default text node */ + Text () + : v_position (0) + , v_align (TOP) + {} -} + Text (boost::shared_ptr<const cxml::Node> node); + + float v_position; + VAlign v_align; + std::string text; + std::list<boost::shared_ptr<Font> > font_nodes; +}; } diff --git a/src/types.cc b/src/types.cc index 693b9ab2..0ecf7a16 100644 --- a/src/types.cc +++ b/src/types.cc @@ -1,15 +1,37 @@ +/* + Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net> + + This program 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. + + This program 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 this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include "types.h" +#include "exceptions.h" +#include <boost/lexical_cast.hpp> +#include <boost/algorithm/string.hpp> #include <vector> #include <cstdio> #include <iomanip> -#include <boost/lexical_cast.hpp> -#include <boost/algorithm/string.hpp> -#include "types.h" -#include "exceptions.h" using namespace std; -using namespace libdcp; +using namespace dcp; using namespace boost; +/** Construct a Fraction from a string of the form <numerator> <denominator> + * e.g. "1 3". + */ Fraction::Fraction (string s) { vector<string> b; @@ -22,17 +44,18 @@ Fraction::Fraction (string s) } bool -libdcp::operator== (Fraction const & a, Fraction const & b) +dcp::operator== (Fraction const & a, Fraction const & b) { return (a.numerator == b.numerator && a.denominator == b.denominator); } bool -libdcp::operator!= (Fraction const & a, Fraction const & b) +dcp::operator!= (Fraction const & a, Fraction const & b) { return (a.numerator != b.numerator || a.denominator != b.denominator); } +/** Construct a Color, initialising it to black. */ Color::Color () : r (0) , g (0) @@ -41,6 +64,9 @@ Color::Color () } +/** Construct a Color from R, G and B. The values run between + * 0 and 255. + */ Color::Color (int r_, int g_, int b_) : r (r_) , g (g_) @@ -84,7 +110,7 @@ Color::to_argb_string () const * @param b Second color to compare. */ bool -libdcp::operator== (Color const & a, Color const & b) +dcp::operator== (Color const & a, Color const & b) { return (a.r == b.r && a.g == b.g && a.b == b.b); } @@ -94,20 +120,20 @@ libdcp::operator== (Color const & a, Color const & b) * @param b Second color to compare. */ bool -libdcp::operator!= (Color const & a, Color const & b) +dcp::operator!= (Color const & a, Color const & b) { return !(a == b); } ostream & -libdcp::operator<< (ostream& s, Color const & c) +dcp::operator<< (ostream& s, Color const & c) { s << "(" << c.r << ", " << c.g << ", " << c.b << ")"; return s; } string -libdcp::effect_to_string (Effect e) +dcp::effect_to_string (Effect e) { switch (e) { case NONE: @@ -122,7 +148,7 @@ libdcp::effect_to_string (Effect e) } Effect -libdcp::string_to_effect (string s) +dcp::string_to_effect (string s) { if (s == "none") { return NONE; @@ -136,7 +162,7 @@ libdcp::string_to_effect (string s) } string -libdcp::valign_to_string (VAlign v) +dcp::valign_to_string (VAlign v) { switch (v) { case TOP: @@ -151,7 +177,7 @@ libdcp::valign_to_string (VAlign v) } VAlign -libdcp::string_to_valign (string s) +dcp::string_to_valign (string s) { if (s == "top") { return TOP; diff --git a/src/types.h b/src/types.h index 013c1863..ece7d44c 100644 --- a/src/types.h +++ b/src/types.h @@ -1,5 +1,5 @@ /* - Copyright (C) 2012 Carl Hetherington <cth@carlh.net> + Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -24,10 +24,10 @@ #ifndef LIBDCP_TYPES_H #define LIBDCP_TYPES_H -#include <string> #include <boost/shared_ptr.hpp> +#include <string> -namespace libdcp +namespace dcp { namespace parse { @@ -85,12 +85,20 @@ enum Eye EYE_LEFT, EYE_RIGHT }; - + +/** @class Fraction + * @brief A fraction (i.e. a thing with an integer numerator and an integer denominator). + */ class Fraction { public: + /** Construct a fraction of 0/0 */ Fraction () : numerator (0), denominator (0) {} Fraction (std::string s); + /** Construct a fraction with a specified numerator and denominator. + * @param n Numerator. + * @param d Denominator. + */ Fraction (int n, int d) : numerator (n), denominator (d) {} int numerator; @@ -99,20 +107,33 @@ public: extern bool operator== (Fraction const & a, Fraction const & b); extern bool operator!= (Fraction const & a, Fraction const & b); - -struct EqualityOptions { + +/** @struct EqualityOptions + * @brief A class to describe what "equality" means for a particular test. + * + * When comparing things, we want to be able to ignore some differences; + * this class expresses those differences. + */ +struct EqualityOptions +{ + /** Construct an EqualityOptions where nothing at all can differ */ EqualityOptions () : max_mean_pixel_error (0) , max_std_dev_pixel_error (0) , max_audio_sample_error (0) - , cpl_names_can_differ (false) + , cpl_annotation_texts_can_differ (false) , mxf_names_can_differ (false) {} + /** The maximum allowable mean difference in pixel value between two images */ double max_mean_pixel_error; + /** The maximum standard deviation of the differences in pixel value between two images */ double max_std_dev_pixel_error; + /** The maximum difference in audio sample value between two soundtracks */ int max_audio_sample_error; - bool cpl_names_can_differ; + /** true if the <AnnotationText> nodes of CPLs are allowed to differ */ + bool cpl_annotation_texts_can_differ; + /** true if MXF filenames are allowed to differ */ bool mxf_names_can_differ; }; @@ -125,6 +146,11 @@ enum NoteType { NOTE }; +enum Standard { + INTEROP, + SMPTE +}; + /** @class Color * @brief An RGB color (aka colour). */ diff --git a/src/util.cc b/src/util.cc index a668b7fc..1e32fbc9 100644 --- a/src/util.cc +++ b/src/util.cc @@ -1,5 +1,5 @@ /* - Copyright (C) 2012 Carl Hetherington <cth@carlh.net> + Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -21,22 +21,6 @@ * @brief Utility methods. */ -#include <stdexcept> -#include <sstream> -#include <iostream> -#include <iomanip> -#include <boost/filesystem.hpp> -#include <boost/lexical_cast.hpp> -#include <openssl/sha.h> -#include <libxml++/nodes/element.h> -#include <libxml++/document.h> -#include <xmlsec/xmldsig.h> -#include <xmlsec/dl.h> -#include <xmlsec/app.h> -#include <xmlsec/crypto.h> -#include "KM_util.h" -#include "KM_fileio.h" -#include "AS_DCP.h" #include "util.h" #include "exceptions.h" #include "types.h" @@ -44,6 +28,22 @@ #include "certificates.h" #include "gamma_lut.h" #include "xyz_frame.h" +#include "compose.hpp" +#include "KM_util.h" +#include "KM_fileio.h" +#include "AS_DCP.h" +#include <xmlsec/xmldsig.h> +#include <xmlsec/dl.h> +#include <xmlsec/app.h> +#include <xmlsec/crypto.h> +#include <libxml++/nodes/element.h> +#include <libxml++/document.h> +#include <openssl/sha.h> +#include <boost/filesystem.hpp> +#include <stdexcept> +#include <sstream> +#include <iostream> +#include <iomanip> using std::string; using std::wstring; @@ -54,15 +54,17 @@ using std::max; using std::list; using std::setw; using std::setfill; +using std::ostream; using boost::shared_ptr; -using boost::lexical_cast; -using namespace libdcp; +using boost::optional; +using boost::function; +using namespace dcp; /** Create a UUID. * @return UUID. */ string -libdcp::make_uuid () +dcp::make_uuid () { char buffer[64]; Kumu::UUID id; @@ -74,15 +76,15 @@ libdcp::make_uuid () /** Create a digest for a file. * @param filename File name. - * @param progress Pointer to a progress reporting function, or 0. The function will be called + * @param progress Optional progress reporting function. The function will be called * with a progress value between 0 and 1. * @return Digest. */ string -libdcp::make_digest (string filename, boost::function<void (float)>* progress) +dcp::make_digest (boost::filesystem::path filename, function<void (float)> progress) { Kumu::FileReader reader; - Kumu::Result_t r = reader.OpenRead (filename.c_str ()); + Kumu::Result_t r = reader.OpenRead (filename.string().c_str ()); if (ASDCP_FAILURE (r)) { boost::throw_exception (FileError ("could not open file to compute digest", filename, r)); } @@ -108,7 +110,7 @@ libdcp::make_digest (string filename, boost::function<void (float)>* progress) SHA1_Update (&sha, read_buffer.Data(), read); if (progress) { - (*progress) (float (done) / size); + progress (float (done) / size); done += read; } } @@ -121,12 +123,12 @@ libdcp::make_digest (string filename, boost::function<void (float)>* progress) } /** Convert a content kind to a string which can be used in a - * <ContentKind> node. + * <ContentKind> node. * @param kind ContentKind. * @return string. */ string -libdcp::content_kind_to_string (ContentKind kind) +dcp::content_kind_to_string (ContentKind kind) { switch (kind) { case FEATURE: @@ -154,35 +156,35 @@ libdcp::content_kind_to_string (ContentKind kind) assert (false); } -/** Convert a string from a <ContentKind> node to a libdcp ContentKind. +/** Convert a string from a <ContentKind> node to a libdcp ContentKind. * Reasonably tolerant about varying case. - * @param type Content kind string. + * @param kind Content kind string. * @return libdcp ContentKind. */ -libdcp::ContentKind -libdcp::content_kind_from_string (string type) +dcp::ContentKind +dcp::content_kind_from_string (string kind) { - transform (type.begin(), type.end(), type.begin(), ::tolower); + transform (kind.begin(), kind.end(), kind.begin(), ::tolower); - if (type == "feature") { + if (kind == "feature") { return FEATURE; - } else if (type == "short") { + } else if (kind == "short") { return SHORT; - } else if (type == "trailer") { + } else if (kind == "trailer") { return TRAILER; - } else if (type == "test") { + } else if (kind == "test") { return TEST; - } else if (type == "transitional") { + } else if (kind == "transitional") { return TRANSITIONAL; - } else if (type == "rating") { + } else if (kind == "rating") { return RATING; - } else if (type == "teaser") { + } else if (kind == "teaser") { return TEASER; - } else if (type == "policy") { + } else if (kind == "policy") { return POLICY; - } else if (type == "psa") { + } else if (kind == "psa") { return PUBLIC_SERVICE_ANNOUNCEMENT; - } else if (type == "advertisement") { + } else if (kind == "advertisement") { return ADVERTISEMENT; } @@ -198,8 +200,8 @@ libdcp::content_kind_from_string (string type) * This is useful for scaling 4K DCP images down to 2K. * @return XYZ image. */ -shared_ptr<libdcp::XYZFrame> -libdcp::decompress_j2k (uint8_t* data, int64_t size, int reduce) +shared_ptr<dcp::XYZFrame> +dcp::decompress_j2k (uint8_t* data, int64_t size, int reduce) { opj_dinfo_t* decoder = opj_create_decompress (CODEC_J2K); opj_dparameters_t parameters; @@ -211,7 +213,7 @@ libdcp::decompress_j2k (uint8_t* data, int64_t size, int reduce) if (!image) { opj_destroy_decompress (decoder); opj_cio_close (cio); - boost::throw_exception (DCPReadError ("could not decode JPEG2000 codestream of " + lexical_cast<string> (size) + " bytes.")); + boost::throw_exception (DCPReadError (String::compose ("could not decode JPEG2000 codestream of %1 bytes.", size))); } opj_destroy_decompress (decoder); @@ -226,7 +228,7 @@ libdcp::decompress_j2k (uint8_t* data, int64_t size, int reduce) * @return true if the string contains only space, newline or tab characters, or is empty. */ bool -libdcp::empty_or_white_space (string s) +dcp::empty_or_white_space (string s) { for (size_t i = 0; i < s.length(); ++i) { if (s[i] != ' ' && s[i] != '\n' && s[i] != '\t') { @@ -237,8 +239,11 @@ libdcp::empty_or_white_space (string s) return true; } +/** Set up various bits that the library needs. Should be called one + * by client applications. + */ void -libdcp::init () +dcp::init () { if (xmlSecInit() < 0) { throw MiscError ("could not initialise xmlsec"); @@ -259,21 +264,33 @@ libdcp::init () } } -bool libdcp::operator== (libdcp::Size const & a, libdcp::Size const & b) +bool dcp::operator== (dcp::Size const & a, dcp::Size const & b) { return (a.width == b.width && a.height == b.height); } -bool libdcp::operator!= (libdcp::Size const & a, libdcp::Size const & b) +bool dcp::operator!= (dcp::Size const & a, dcp::Size const & b) { return !(a == b); } -/** The base64 decode routine in KM_util.cpp gives different values to both - * this and the command-line base64 for some inputs. Not sure why. +ostream& dcp::operator<< (ostream& s, dcp::Size const & a) +{ + s << a.width << "x" << a.height; + return s; +} + +/** Decode a base64 string. The base64 decode routine in KM_util.cpp + * gives different values to both this and the command-line base64 + * for some inputs. Not sure why. + * + * @param in base64-encoded string. + * @param out Output buffer. + * @param out_length Length of output buffer. + * @return Number of characters written to the output buffer. */ int -libdcp::base64_decode (string const & in, unsigned char* out, int out_length) +dcp::base64_decode (string const & in, unsigned char* out, int out_length) { BIO* b64 = BIO_new (BIO_f_base64 ()); @@ -297,8 +314,13 @@ libdcp::base64_decode (string const & in, unsigned char* out, int out_length) return N; } +/** Convert a struct tm to a string of the form + * 2014-01-26T21:39:00+01:00 + * @param tm struct tm. + * @return Time as a string. + */ string -libdcp::tm_to_string (struct tm* tm) +dcp::tm_to_string (struct tm* tm) { char buffer[64]; strftime (buffer, 64, "%Y-%m-%dT%H:%M:%S", tm); @@ -320,7 +342,7 @@ libdcp::tm_to_string (struct tm* tm) * @return string of the form e.g. -01:00. */ string -libdcp::utc_offset_to_string (int b) +dcp::utc_offset_to_string (int b) { bool const negative = (b < 0); b = negative ? -b : b; @@ -339,20 +361,29 @@ libdcp::utc_offset_to_string (int b) return o.str (); } +/** Convert a boost::posix_time::ptime to a string of the form + * 2014-01-26T21:39:00+01:00. + * @param t boost::posix_time::ptime. + * @return Time as a string. + */ string -libdcp::ptime_to_string (boost::posix_time::ptime t) +dcp::ptime_to_string (boost::posix_time::ptime t) { struct tm t_tm = boost::posix_time::to_tm (t); return tm_to_string (&t_tm); } -/* Apparently there is no way to create an ofstream using a UTF-8 - filename under Windows. We are hence reduced to using fopen - with this wrapper. -*/ +/** @param p Path to open. + * @param t mode flags, as for fopen(3). + * @return FILE pointer or 0 on error. + * + * Apparently there is no way to create an ofstream using a UTF-8 + * filename under Windows. We are hence reduced to using fopen + * with this wrapper. + */ FILE * -libdcp::fopen_boost (boost::filesystem::path p, string t) +dcp::fopen_boost (boost::filesystem::path p, string t) { #ifdef LIBDCP_WINDOWS wstring w (t.begin(), t.end()); @@ -362,3 +393,26 @@ libdcp::fopen_boost (boost::filesystem::path p, string t) return fopen (p.c_str(), t.c_str ()); #endif } + +optional<boost::filesystem::path> +dcp::relative_to_root (boost::filesystem::path root, boost::filesystem::path file) +{ + boost::filesystem::path::const_iterator i = root.begin (); + boost::filesystem::path::const_iterator j = file.begin (); + + while (i != root.end() && j != file.end() && *i == *j) { + ++i; + ++j; + } + + if (i != root.end ()) { + return optional<boost::filesystem::path> (); + } + + boost::filesystem::path rel; + while (j != file.end ()) { + rel /= *j++; + } + + return rel; +} @@ -1,5 +1,5 @@ /* - Copyright (C) 2012 Carl Hetherington <cth@carlh.net> + Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -24,27 +24,32 @@ * @brief Utility methods. */ -#include <string> -#include <stdint.h> +#include "types.h" #include <boost/shared_ptr.hpp> #include <boost/function.hpp> #include <boost/date_time/posix_time/posix_time.hpp> #include <boost/filesystem.hpp> +#include <boost/optional.hpp> #include <openjpeg.h> -#include "types.h" +#include <string> +#include <stdint.h> namespace xmlpp { class Element; } -namespace libdcp { +namespace dcp { class ARGBFrame; class CertificateChain; class GammaLUT; class XYZFrame; -struct Size { +/** @struct Size + * @brief The integer, two-dimensional size of something. + */ +struct Size +{ Size () : width (0) , height (0) @@ -65,9 +70,10 @@ struct Size { extern bool operator== (Size const & a, Size const & b); extern bool operator!= (Size const & a, Size const & b); +extern std::ostream& operator<< (std::ostream& s, Size const & a); extern std::string make_uuid (); -extern std::string make_digest (std::string filename, boost::function<void (float)> *); +extern std::string make_digest (boost::filesystem::path filename, boost::function<void (float)>); extern std::string content_kind_to_string (ContentKind kind); extern ContentKind content_kind_from_string (std::string kind); extern bool empty_or_white_space (std::string s); @@ -75,16 +81,31 @@ extern boost::shared_ptr<XYZFrame> decompress_j2k (uint8_t* data, int64_t size, extern void init (); -extern void sign (xmlpp::Element* parent, CertificateChain const & certificates, boost::filesystem::path signer_key, bool interop); +extern void sign (xmlpp::Element* parent, CertificateChain const & certificates, boost::filesystem::path signer_key, Standard standard); extern void add_signature_value (xmlpp::Element* parent, CertificateChain const & certificates, boost::filesystem::path signer_key, std::string const & ns); extern void add_signer (xmlpp::Element* parent, CertificateChain const & certificates, std::string const & ns); extern int base64_decode (std::string const & in, unsigned char* out, int out_length); - +extern boost::optional<boost::filesystem::path> relative_to_root (boost::filesystem::path root, boost::filesystem::path file); extern std::string tm_to_string (struct tm *); extern std::string utc_offset_to_string (int); extern std::string ptime_to_string (boost::posix_time::ptime); extern FILE * fopen_boost (boost::filesystem::path, std::string); + +template <class F, class T> +std::list<boost::shared_ptr<T> > +list_of_type (std::list<boost::shared_ptr<F> > const & from) +{ + std::list<boost::shared_ptr<T> > out; + for (typename std::list<boost::shared_ptr<F> >::const_iterator i = from.begin(); i != from.end(); ++i) { + boost::shared_ptr<T> check = boost::dynamic_pointer_cast<T> (*i); + if (check) { + out.push_back (check); + } + } + + return out; +} } diff --git a/src/version.h b/src/version.h index 52abd13a..9b6dc65f 100644 --- a/src/version.h +++ b/src/version.h @@ -1,5 +1,5 @@ -namespace libdcp { +namespace dcp { extern char const * version; extern char const * git_commit; diff --git a/src/wscript b/src/wscript index cdf3c062..c06ca1bd 100644 --- a/src/wscript +++ b/src/wscript @@ -6,51 +6,61 @@ def build(bld): else: obj = bld(features = 'cxx cxxshlib') - obj.name = 'libdcp' - obj.target = 'dcp' + 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' - obj.use = 'libkumu-libdcp libasdcp-libdcp' + obj.use = 'libkumu-libdcp%s libasdcp-libdcp%s' % (bld.env.API_VERSION, bld.env.API_VERSION) obj.source = """ argb_frame.cc asset.cc certificates.cc colour_matrix.cc + content.cc cpl.cc dcp.cc dcp_time.cc exceptions.cc + file.cc + font.cc gamma_lut.cc image.cc kdm.cc key.cc + load_font.cc metadata.cc - mono_picture_asset.cc - mono_picture_asset_writer.cc + mono_picture_mxf.cc + mono_picture_mxf_writer.cc mono_picture_frame.cc - mxf_asset.cc - picture_asset.cc - picture_asset_writer.cc - rec709_linearised_gamma_lut.cc + mxf.cc + mxf_writer.cc + object.cc + picture_mxf.cc + picture_mxf_writer.cc reel.cc + reel_asset.cc + reel_mono_picture_asset.cc + reel_picture_asset.cc + reel_sound_asset.cc + reel_stereo_picture_asset.cc + reel_subtitle_asset.cc rgb_xyz.cc signer.cc signer_chain.cc - sound_asset.cc + sound_mxf.cc + sound_mxf_writer.cc sound_frame.cc - srgb_linearised_gamma_lut.cc - stereo_picture_asset.cc - stereo_picture_asset_writer.cc + stereo_picture_mxf.cc + stereo_picture_mxf_writer.cc stereo_picture_frame.cc - subtitle_asset.cc + subtitle.cc + subtitle_content.cc + subtitle_string.cc + text.cc types.cc util.cc version.cc xyz_frame.cc - parse/asset_map.cc - parse/cpl.cc - parse/pkl.cc - parse/subtitle.cc """ headers = """ @@ -58,6 +68,7 @@ def build(bld): certificates.h colour_matrix.h cpl.h + content.h dcp.h dcp_time.h exceptions.h @@ -65,32 +76,40 @@ def build(bld): image.h kdm.h key.h - lut.h lut_cache.h metadata.h - mono_picture_asset.h + mono_picture_mxf.h mono_picture_frame.h - mxf_asset.h - picture_asset.h - picture_asset_writer.h + mxf.h + mxf_writer.h + object.h + picture_mxf.h + picture_mxf_writer.h rgb_xyz.h - rec709_linearised_gamma_lut.h reel.h + reel_asset.h + reel_mono_picture_asset.h + reel_picture_asset.h + reel_sound_asset.h + reel_stereo_picture_asset.h + ref.h argb_frame.h signer.h signer_chain.h - sound_asset.h sound_frame.h - srgb_linearised_gamma_lut.h - stereo_picture_asset.h + sound_mxf.h + sound_mxf_writer.h + stereo_picture_mxf.h stereo_picture_frame.h - subtitle_asset.h + subtitle.h + subtitle_content.h + subtitle_string.h types.h util.h version.h xyz_frame.h """ - bld.install_files('${PREFIX}/include/libdcp', headers) + bld.install_files('${PREFIX}/include/libdcp%s/dcp' % bld.env.API_VERSION, headers) if bld.env.STATIC: - bld.install_files('${PREFIX}/lib', 'libdcp.a') + bld.install_files('${PREFIX}/lib', 'libdcp%s.a' % bld.env.API_VERSION) @@ -1,5 +1,5 @@ /* - Copyright (C) 2013 Carl Hetherington <cth@carlh.net> + Copyright (C) 2013-2014 Carl Hetherington <cth@carlh.net> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -20,10 +20,10 @@ #ifndef LIBDCP_XML_H #define LIBDCP_XML_H -#include <libcxml/cxml.h> #include "exceptions.h" +#include <libcxml/cxml.h> -namespace libdcp +namespace dcp { template <class T> diff --git a/src/xyz_frame.cc b/src/xyz_frame.cc index f5b0ee86..cba90883 100644 --- a/src/xyz_frame.cc +++ b/src/xyz_frame.cc @@ -1,5 +1,5 @@ /* - Copyright (C) 2012-2013 Carl Hetherington <cth@carlh.net> + Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -17,11 +17,15 @@ */ +/** @file src/xyz_frame.cc + * @brief XZYFrame class. + */ + +#include "xyz_frame.h" #include <cassert> #include <stdexcept> -#include "xyz_frame.h" -using namespace libdcp; +using namespace dcp; /** Construct an XYZFrame, taking ownership of the opj_image_t */ XYZFrame::XYZFrame (opj_image_t* image) @@ -30,6 +34,9 @@ XYZFrame::XYZFrame (opj_image_t* image) assert (_opj_image->numcomps == 3); } +/** Construct a new XYZFrame with undefined contents. + * @param size Size for the frame in pixels. + */ XYZFrame::XYZFrame (Size size) { opj_image_cmptparm_t cmptparm[3]; @@ -58,11 +65,15 @@ XYZFrame::XYZFrame (Size size) _opj_image->y1 = size.height; } +/** XYZFrame destructor */ XYZFrame::~XYZFrame () { opj_image_destroy (_opj_image); } +/** @param c Component index (0, 1 or 2) + * @return Pointer to the data for component c. + */ int * XYZFrame::data (int c) const { @@ -70,9 +81,10 @@ XYZFrame::data (int c) const return _opj_image->comps[c].data; } -libdcp::Size +/** @return Size of the image in pixels */ +dcp::Size XYZFrame::size () const { /* XXX: this may not be right; x0 and y0 can presumably be non-zero */ - return libdcp::Size (_opj_image->x1, _opj_image->y1); + return dcp::Size (_opj_image->x1, _opj_image->y1); } diff --git a/src/xyz_frame.h b/src/xyz_frame.h index a4dcb2c0..8ba829b2 100644 --- a/src/xyz_frame.h +++ b/src/xyz_frame.h @@ -1,5 +1,5 @@ /* - Copyright (C) 2012-2013 Carl Hetherington <cth@carlh.net> + Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -17,12 +17,21 @@ */ -#include <openjpeg.h> +/** @file src/xyz_frame.h + * @brief XZYFrame class. + */ + #include "util.h" +#include <openjpeg.h> -namespace libdcp { +namespace dcp { -class XYZFrame +/** @class XYZFrame + * @brief An image in XYZ colour. + * + * This class is a thin wrapper of libopenjpeg's opj_image_t. + */ +class XYZFrame : public boost::noncopyable { public: XYZFrame (opj_image_t *); @@ -30,14 +39,17 @@ public: ~XYZFrame (); int* data (int) const; - libdcp::Size size () const; + dcp::Size size () const; + /** @return Pointer to opj_image_t struct. The caller + * must not delete this. + */ opj_image_t* opj_image () const { return _opj_image; } private: - opj_image_t* _opj_image; + opj_image_t* _opj_image; ///< opj_image_t that we are managing }; } |
