for 2K projectors.
*/
boost::shared_ptr<libdcp::MonoPictureAsset> picture_asset (
- new libdcp::MonoPictureAsset (video_frame, "My Film DCP", "video.mxf", 0, 24, 48, 1998, 1080, false)
- new libdcp::MonoPictureAsset (video_frame, "My Film DCP", "video.mxf", 0, 24, 48, libdcp::Size (1998, 1080))
++ new libdcp::MonoPictureAsset (video_frame, "My Film DCP", "video.mxf", 0, 24, 48, libdcp::Size (1998, 1080), false)
);
/* Now we will create a `sound asset', which is made up of a WAV file for each channel of audio. Here we're using
/* Now we can create the sound asset using these files */
boost::shared_ptr<libdcp::SoundAsset> sound_asset (
- new libdcp::SoundAsset (sound_files, "My Film DCP", "audio.mxf", 0, 24, 48, 0, false)
- new libdcp::SoundAsset (sound_files, "My Film DCP", "audio.mxf", 0, 24, 48)
++ new libdcp::SoundAsset (sound_files, "My Film DCP", "audio.mxf", 0, 24, 48, false)
);
/* Now that we have the assets, we can create a Reel to put them in and add it to the CPL */
#include <iostream>
#include <fstream>
#include <boost/filesystem.hpp>
-#include <boost/function.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"
return _digest;
}
-
+ bool
+ Asset::equals (shared_ptr<const Asset> other, EqualityOptions, boost::function<void (NoteType, string)> note) const
+ {
+ if (_edit_rate != other->_edit_rate) {
+ note (ERROR, "MXF edit rates differ");
+ return false;
+ }
+
+ if (_intrinsic_duration != other->_intrinsic_duration) {
+ note (ERROR, "MXF intrinsic durations differ");
+ return false;
+ }
+
+ if (_duration != other->_duration) {
+ note (ERROR, "MXF durations differ");
+ return false;
+ }
+
+ return true;
+ }
virtual ~Asset() {}
- /** Write details of the asset to a CPL stream.
- * @param s Stream.
+ /** Write details of the asset to a CPL AssetList node.
+ * @param p Parent node.
*/
- virtual void write_to_cpl (xmlpp::Element* p) const = 0;
+ virtual void write_to_cpl (xmlpp::Node *) const = 0;
- /** Write details of the asset to a PKL stream.
- * @param s Stream.
+ /** Write details of the asset to a PKL AssetList node.
+ * @param p Parent node.
*/
- void write_to_pkl (xmlpp::Element* p) const;
+ void write_to_pkl (xmlpp::Node *) const;
/** Write details of the asset to a ASSETMAP stream.
* @param s Stream.
--- /dev/null
-CPL::write_xml (XMLMetadata const & metadata) const
+ /*
+ 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.
+
+ */
+
+ #include <fstream>
+ #include "cpl.h"
+ #include "parse/cpl.h"
+ #include "util.h"
+ #include "picture_asset.h"
+ #include "sound_asset.h"
+ #include "subtitle_asset.h"
+ #include "parse/asset_map.h"
+ #include "reel.h"
+ #include "metadata.h"
++#include "encryption.h"
+
+ using std::string;
+ using std::stringstream;
+ using std::ofstream;
+ using std::ostream;
+ using std::list;
+ using boost::shared_ptr;
+ using boost::lexical_cast;
+ using namespace libdcp;
+
+ CPL::CPL (string directory, string name, ContentKind content_kind, int length, int frames_per_second)
+ : _directory (directory)
+ , _name (name)
+ , _content_kind (content_kind)
+ , _length (length)
+ , _fps (frames_per_second)
+ {
+ _uuid = make_uuid ();
+ }
+
+ /** 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_map The corresponding asset map.
+ * @param require_mxfs true to throw an exception if a required MXF file does not exist.
+ */
+ CPL::CPL (string directory, string file, shared_ptr<const libdcp::parse::AssetMap> asset_map, bool require_mxfs)
+ : _directory (directory)
+ , _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));
+ }
+
+ /* Now cherry-pick the required bits into our own data structure */
+
+ _name = cpl->annotation_text;
+ _content_kind = cpl->content_kind;
+
+ for (list<shared_ptr<libdcp::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 {
+ picture.reset (new MonoPictureAsset (
+ _directory,
+ asset_map->asset_from_id (p->id)->chunks.front()->path
+ )
+ );
+
+ picture->set_entry_point (p->entry_point);
+ picture->set_duration (p->duration);
+ } catch (MXFFileError) {
+ if (require_mxfs) {
+ throw;
+ }
+ }
+
+ } else {
+ try {
+ picture.reset (new StereoPictureAsset (
+ _directory,
+ asset_map->asset_from_id (p->id)->chunks.front()->path,
+ _fps,
+ p->duration
+ )
+ );
+
+ picture->set_entry_point (p->entry_point);
+ picture->set_duration (p->duration);
+
+ } catch (MXFFileError) {
+ if (require_mxfs) {
+ throw;
+ }
+ }
+
+ }
+
+ if ((*i)->asset_list->main_sound) {
+
+ try {
+ sound.reset (new SoundAsset (
+ _directory,
+ asset_map->asset_from_id ((*i)->asset_list->main_sound->id)->chunks.front()->path
+ )
+ );
+
+ sound->set_entry_point ((*i)->asset_list->main_sound->entry_point);
+ sound->set_duration ((*i)->asset_list->main_sound->duration);
+ } catch (MXFFileError) {
+ if (require_mxfs) {
+ throw;
+ }
+ }
+ }
+
+ if ((*i)->asset_list->main_subtitle) {
+
+ subtitle.reset (new SubtitleAsset (
+ _directory,
+ asset_map->asset_from_id ((*i)->asset_list->main_subtitle->id)->chunks.front()->path
+ )
+ );
+
+ 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)));
+ }
+ }
+
+ void
+ CPL::add_reel (shared_ptr<const Reel> reel)
+ {
+ _reels.push_back (reel);
+ }
+
+ void
++CPL::write_xml (shared_ptr<Encryption> crypt, XMLMetadata const & metadata) const
+ {
+ boost::filesystem::path p;
+ p /= _directory;
+ stringstream s;
+ s << _uuid << "_cpl.xml";
+ p /= s.str();
+
+ xmlpp::Document doc;
+ xmlpp::Element* root = doc.create_root_node ("CompositionPlaylist", "http://www.smpte-ra.org/schemas/429-7/2006/CPL");
++
++ if (crypt) {
++ root->set_namespace_declaration ("http://www.w3.org/2000/09/xmldsig#", "dsig");
++ }
++
+ root->add_child("Id")->add_child_text ("urn:uuid:" + _uuid);
+ root->add_child("AnnotationText")->add_child_text (_name);
+ root->add_child("IssueDate")->add_child_text (metadata.issue_date);
+ root->add_child("Creator")->add_child_text (metadata.creator);
+ root->add_child("ContentTitleText")->add_child_text (_name);
+ 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:" + _uuid + "_" + metadata.issue_date);
+ cv->add_child ("LabelText")->add_child_text (_uuid + "_" + metadata.issue_date);
+ }
+ root->add_child("RatingList");
+
+ xmlpp::Node* reel_list = root->add_child ("ReelList");
+
+ for (list<shared_ptr<const Reel> >::const_iterator i = _reels.begin(); i != _reels.end(); ++i) {
+ (*i)->write_to_cpl (reel_list);
+ }
+
++ if (crypt) {
++ sign (root, crypt->certificates, crypt->signer_key);
++ }
++
+ doc.write_to_file_formatted (p.string (), "UTF-8");
+
+ _digest = make_digest (p.string ());
+ _length = boost::filesystem::file_size (p.string ());
+ }
+
+ void
+ CPL::write_to_pkl (xmlpp::Node* node) const
+ {
+ xmlpp::Node* asset = node->add_child ("Asset");
+ asset->add_child("Id")->add_child_text ("urn:uuid:" + _uuid);
+ asset->add_child("Hash")->add_child_text (_digest);
+ asset->add_child("Size")->add_child_text (lexical_cast<string> (_length));
+ asset->add_child("Type")->add_child_text ("text/xml");
+ }
+
+ list<shared_ptr<const Asset> >
+ CPL::assets () const
+ {
+ list<shared_ptr<const Asset> > a;
+ for (list<shared_ptr<const Reel> >::const_iterator i = _reels.begin(); i != _reels.end(); ++i) {
+ if ((*i)->main_picture ()) {
+ a.push_back ((*i)->main_picture ());
+ }
+ if ((*i)->main_sound ()) {
+ a.push_back ((*i)->main_sound ());
+ }
+ if ((*i)->main_subtitle ()) {
+ a.push_back ((*i)->main_subtitle ());
+ }
+ }
+
+ 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:" + _uuid);
+ xmlpp::Node* chunk_list = asset->add_child ("ChunkList");
+ xmlpp::Node* chunk = chunk_list->add_child ("Chunk");
+ chunk->add_child("Path")->add_child_text (_uuid + "_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));
+ }
+
+
+
+ bool
+ CPL::equals (CPL const & other, EqualityOptions opt, boost::function<void (NoteType, string)> note) const
+ {
+ if (_name != other._name && !opt.cpl_names_can_differ) {
+ stringstream s;
+ s << "names differ: " << _name << " vs " << other._name << "\n";
+ note (ERROR, s.str ());
+ return false;
+ }
+
+ if (_content_kind != other._content_kind) {
+ note (ERROR, "content kinds differ");
+ return false;
+ }
+
+ if (_fps != other._fps) {
+ note (ERROR, "frames per second differ");
+ return false;
+ }
+
+ if (_length != other._length) {
+ note (ERROR, "lengths differ");
+ return false;
+ }
+
+ if (_reels.size() != other._reels.size()) {
+ note (ERROR, "reel counts differ");
+ return false;
+ }
+
+ list<shared_ptr<const Reel> >::const_iterator a = _reels.begin ();
+ list<shared_ptr<const Reel> >::const_iterator b = other._reels.begin ();
+
+ while (a != _reels.end ()) {
+ if (!(*a)->equals (*b, opt, note)) {
+ return false;
+ }
+ ++a;
+ ++b;
+ }
+
+ return true;
+ }
++
++shared_ptr<xmlpp::Document>
++CPL::make_kdm (
++ CertificateChain const & certificates,
++ string const & signer_key,
++ shared_ptr<const Certificate> recipient_cert,
++ boost::posix_time::ptime from,
++ boost::posix_time::ptime until
++ ) const
++{
++ assert (recipient_cert);
++
++ shared_ptr<xmlpp::Document> doc (new xmlpp::Document);
++ xmlpp::Element* root = doc->create_root_node ("DCinemaSecurityMessage");
++ root->set_namespace_declaration ("http://www.smpte-ra.org/schemas/430-3/2006/ETM", "");
++ root->set_namespace_declaration ("http://www.w3.org/2000/09/xmldsig#", "ds");
++ root->set_namespace_declaration ("http://www.w3.org/2001/04/xmlenc#", "enc");
++
++ {
++ xmlpp::Element* authenticated_public = root->add_child("AuthenticatedPublic");
++ authenticated_public->set_attribute("Id", "ID_AuthenticatedPublic");
++ xmlAddID (0, doc->cobj(), (const xmlChar *) "ID_AuthenticatedPublic", authenticated_public->get_attribute("Id")->cobj());
++
++ authenticated_public->add_child("MessageId")->add_child_text("urn:uuid:" + make_uuid());
++ authenticated_public->add_child("MessageType")->add_child_text("http://www.smpte-ra.org/430-1/2006/KDM#kdm-key-type");
++ authenticated_public->add_child("AnnotationText")->add_child_text(Metadata::instance()->product_name);
++ authenticated_public->add_child("IssueDate")->add_child_text(Metadata::instance()->issue_date);
++
++ {
++ xmlpp::Element* signer = authenticated_public->add_child("Signer");
++ signer->add_child("X509IssuerName", "ds")->add_child_text (
++ Certificate::name_for_xml (recipient_cert->issuer())
++ );
++ signer->add_child("X509SerialNumber", "ds")->add_child_text (
++ recipient_cert->serial()
++ );
++ }
++
++ {
++ xmlpp::Element* required_extensions = authenticated_public->add_child("RequiredExtensions");
++
++ {
++ xmlpp::Element* kdm_required_extensions = required_extensions->add_child("KDMRequiredExtensions");
++ kdm_required_extensions->set_namespace_declaration ("http://www.smpte-ra.org/schemas/430-1/2006/KDM");
++ {
++ xmlpp::Element* recipient = kdm_required_extensions->add_child("Recipient");
++ {
++ xmlpp::Element* serial_element = recipient->add_child("X509IssuerSerial");
++ serial_element->add_child("X509IssuerName", "ds")->add_child_text (
++ Certificate::name_for_xml (recipient_cert->issuer())
++ );
++ serial_element->add_child("X509SerialNumber", "ds")->add_child_text (
++ recipient_cert->serial()
++ );
++ }
++
++ recipient->add_child("X509SubjectName")->add_child_text (Certificate::name_for_xml (recipient_cert->subject()));
++ }
++
++ kdm_required_extensions->add_child("CompositionPlaylistId")->add_child_text("urn:uuid:" + _uuid);
++ kdm_required_extensions->add_child("ContentTitleText")->add_child_text(_name);
++ kdm_required_extensions->add_child("ContentAuthenticator")->add_child_text(certificates.leaf()->thumbprint());
++ kdm_required_extensions->add_child("ContentKeysNotValidBefore")->add_child_text("XXX");
++ kdm_required_extensions->add_child("ContentKeysNotValidAfter")->add_child_text("XXX");
++
++ {
++ xmlpp::Element* authorized_device_info = kdm_required_extensions->add_child("AuthorizedDeviceInfo");
++ authorized_device_info->add_child("DeviceListIdentifier")->add_child_text("urn:uuid:" + make_uuid());
++ authorized_device_info->add_child("DeviceListDescription")->add_child_text(recipient_cert->subject());
++ {
++ xmlpp::Element* device_list = authorized_device_info->add_child("DeviceList");
++ device_list->add_child("CertificateThumbprint")->add_child_text(recipient_cert->thumbprint());
++ }
++ }
++
++ {
++ xmlpp::Element* key_id_list = kdm_required_extensions->add_child("KeyIdList");
++ list<shared_ptr<const Asset> > a = assets();
++ for (list<shared_ptr<const Asset> >::iterator i = a.begin(); i != a.end(); ++i) {
++ /* XXX: non-MXF assets? */
++ shared_ptr<const MXFAsset> mxf = boost::dynamic_pointer_cast<const MXFAsset> (*i);
++ if (mxf) {
++ mxf->add_typed_key_id (key_id_list);
++ }
++ }
++ }
++
++ {
++ xmlpp::Element* forensic_mark_flag_list = kdm_required_extensions->add_child("ForensicMarkFlagList");
++ forensic_mark_flag_list->add_child("ForensicMarkFlag")->add_child_text (
++ "http://www.smpte-ra.org/430-1/2006/KDM#mrkflg-picture-disable"
++ );
++ forensic_mark_flag_list->add_child("ForensicMarkFlag")->add_child_text (
++ "http://www.smpte-ra.org/430-1/2006/KDM#mrkflg-audio-disable"
++ );
++ }
++ }
++ }
++
++ authenticated_public->add_child("NonCriticalExtensions");
++ }
++
++ {
++ xmlpp::Element* authenticated_private = root->add_child("AuthenticatedPrivate");
++ authenticated_private->set_attribute ("Id", "ID_AuthenticatedPrivate");
++ xmlAddID (0, doc->cobj(), (const xmlChar *) "ID_AuthenticatedPrivate", authenticated_private->get_attribute("Id")->cobj());
++ {
++ xmlpp::Element* encrypted_key = authenticated_private->add_child ("EncryptedKey", "enc");
++ {
++ xmlpp::Element* encryption_method = encrypted_key->add_child ("EncryptionMethod", "enc");
++ encryption_method->set_attribute ("Algorithm", "http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p");
++ encryption_method->add_child("DigestMethod", "ds")->set_attribute("Algorithm", "http://www.w3.org/2000/09/xmldsig#sha1");
++ }
++
++ xmlpp::Element* cipher_data = authenticated_private->add_child ("CipherData", "enc");
++ cipher_data->add_child("CipherValue", "enc")->add_child_text("XXX");
++ }
++ }
++
++ /* XXX: x2 one for each mxf? */
++
++ {
++ xmlpp::Element* signature = root->add_child("Signature", "ds");
++
++ {
++ xmlpp::Element* signed_info = signature->add_child("SignedInfo", "ds");
++ signed_info->add_child("CanonicalizationMethod", "ds")->set_attribute(
++ "Algorithm", "http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments"
++ );
++ signed_info->add_child("SignatureMethod", "ds")->set_attribute(
++ "Algorithm", "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"
++ );
++ {
++ xmlpp::Element* reference = signed_info->add_child("Reference", "ds");
++ reference->set_attribute("URI", "#ID_AuthenticatedPublic");
++ reference->add_child("DigestMethod", "ds")->set_attribute("Algorithm", "http://www.w3.org/2001/04/xmlenc#sha256");
++ reference->add_child("DigestValue", "ds");
++ }
++
++ {
++ xmlpp::Element* reference = signed_info->add_child("Reference", "ds");
++ reference->set_attribute("URI", "#ID_AuthenticatedPrivate");
++ reference->add_child("DigestMethod", "ds")->set_attribute("Algorithm", "http://www.w3.org/2001/04/xmlenc#sha256");
++ reference->add_child("DigestValue", "ds");
++ }
++ }
++
++ add_signature_value (signature, certificates, signer_key, "ds");
++ }
++
++ return doc;
++}
--- /dev/null
-
+ /*
+ 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.
+
+ */
+
+ #include <list>
+ #include <boost/shared_ptr.hpp>
+ #include <boost/function.hpp>
++#include <boost/date_time/posix_time/posix_time.hpp>
+ #include <libxml++/libxml++.h>
+ #include "types.h"
++#include "certificates.h"
+
+ namespace libdcp {
+
+ namespace parse {
+ class AssetMap;
+ }
+
+ class Asset;
+ class Reel;
+ class XMLMetadata;
- void write_xml (XMLMetadata const &) const;
++ class Encryption;
++
+ /** @brief A CPL within a DCP */
+ class CPL
+ {
+ public:
+ CPL (std::string directory, std::string name, ContentKind content_kind, int length, int frames_per_second);
+ CPL (std::string directory, std::string file, boost::shared_ptr<const parse::AssetMap> asset_map, bool require_mxfs = true);
+
+ void add_reel (boost::shared_ptr<const Reel> reel);
+
+ /** @return the length in frames */
+ int length () const {
+ return _length;
+ }
+
+ /** @return the type of the content, used by media servers
+ * to categorise things (e.g. feature, trailer, etc.)
+ */
+ ContentKind content_kind () const {
+ return _content_kind;
+ }
+
+ std::list<boost::shared_ptr<const Reel> > reels () const {
+ return _reels;
+ }
+
+ /** @return the CPL's name, as will be presented on projector
+ * media servers and theatre management systems.
+ */
+ 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;
+
+ bool equals (CPL const & other, EqualityOptions options, boost::function<void (NoteType, std::string)> note) const;
+
++ void write_xml (XMLMetadata const &, boost::shared_ptr<Encryption>) const;
+ void write_to_assetmap (xmlpp::Node *) const;
+ void write_to_pkl (xmlpp::Node *) const;
++
++ boost::shared_ptr<xmlpp::Document> make_kdm (
++ CertificateChain const &,
++ std::string const &,
++ boost::shared_ptr<const Certificate>,
++ boost::posix_time::ptime from,
++ boost::posix_time::ptime until
++ ) const;
+
+ private:
+ std::string _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::list<boost::shared_ptr<const Reel> > _reels;
+
+ /** our UUID */
+ std::string _uuid;
+ /** a SHA1 digest of our XML */
+ mutable std::string _digest;
+ };
+
+ }
#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 "util.h"
#include "metadata.h"
#include "exceptions.h"
- #include "cpl_file.h"
- #include "pkl_file.h"
- #include "asset_map.h"
+ #include "parse/pkl.h"
+ #include "parse/asset_map.h"
#include "reel.h"
+ #include "cpl.h"
++#include "encryption.h"
using std::string;
using std::list;
}
void
- DCP::write_xml (shared_ptr<Encryption> crypt) const
-DCP::write_xml (XMLMetadata const & metadata) const
++DCP::write_xml (XMLMetadata const & metadata, shared_ptr<Encryption> crypt) const
{
for (list<shared_ptr<const CPL> >::const_iterator i = _cpls.begin(); i != _cpls.end(); ++i) {
- (*i)->write_xml (crypt);
- (*i)->write_xml (metadata);
++ (*i)->write_xml (metadata, crypt);
}
string pkl_uuid = make_uuid ();
- string pkl_path = write_pkl (pkl_uuid, crypt);
- string pkl_path = write_pkl (pkl_uuid, metadata);
++ string pkl_path = write_pkl (pkl_uuid, metadata, crypt);
write_volindex ();
- write_assetmap (pkl_uuid, boost::filesystem::file_size (pkl_path));
+ write_assetmap (pkl_uuid, boost::filesystem::file_size (pkl_path), metadata);
}
std::string
- DCP::write_pkl (string pkl_uuid, shared_ptr<Encryption> crypt) const
-DCP::write_pkl (string pkl_uuid, XMLMetadata const & metadata) const
++DCP::write_pkl (string pkl_uuid, XMLMetadata const & metadata, shared_ptr<Encryption> crypt) const
{
assert (!_cpls.empty ());
p /= s.str();
xmlpp::Document doc;
- xmlpp::Element* root = doc.create_root_node ("PackingList", "http://www.smpte-ra.org/schemas/429-8/2007/PKL");
+ xmlpp::Element* pkl = doc.create_root_node("PackingList", "http://www.smpte-ra.org/schemas/429-8/2007/PKL");
+ if (crypt) {
+ pkl->set_namespace_declaration ("http://www.w3.org/2000/09/xmldsig#", "dsig");
+ }
- root->add_child("Id")->add_child_text ("urn:uuid:" + pkl_uuid);
+ pkl->add_child("Id")->add_child_text ("urn:uuid:" + pkl_uuid);
/* XXX: this is a bit of a hack */
- root->add_child("AnnotationText")->add_child_text (_cpls.front()->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);
-
- xmlpp::Node* asset_list = root->add_child ("AssetList");
+ pkl->add_child("AnnotationText")->add_child_text(_cpls.front()->name());
- pkl->add_child("IssueDate")->add_child_text (Metadata::instance()->issue_date);
- pkl->add_child("Issuer")->add_child_text (Metadata::instance()->issuer);
- pkl->add_child("Creator")->add_child_text (Metadata::instance()->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);
- }
++ 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);
- for (list<shared_ptr<const CPL> >::const_iterator i = _cpls.begin(); i != _cpls.end(); ++i) {
- (*i)->write_to_pkl (asset_list);
- }
++ 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);
+ }
-
++
+ for (list<shared_ptr<const CPL> >::const_iterator i = _cpls.begin(); i != _cpls.end(); ++i) {
+ (*i)->write_to_pkl (asset_list);
}
- doc.write_to_file_formatted (p.string(), "UTF-8");
-
+ if (crypt) {
+ sign (pkl, crypt->certificates, crypt->signer_key);
+ }
+
+ doc.write_to_file_formatted (p.string (), "UTF-8");
return p.string ();
}
return true;
}
--
void
DCP::add_cpl (shared_ptr<CPL> cpl)
{
a.unique ();
return a;
}
-
- CPL::CPL (string directory, string name, ContentKind content_kind, int length, int frames_per_second)
- : _directory (directory)
- , _name (name)
- , _content_kind (content_kind)
- , _length (length)
- , _fps (frames_per_second)
- {
- _uuid = make_uuid ();
- }
-
- /** 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_map The corresponding asset map.
- * @param require_mxfs true to throw an exception if a required MXF file does not exist.
- */
- CPL::CPL (string directory, string file, shared_ptr<const AssetMap> asset_map, bool require_mxfs)
- : _directory (directory)
- , _content_kind (FEATURE)
- , _length (0)
- , _fps (0)
- {
- /* Read the XML */
- shared_ptr<CPLFile> cpl;
- try {
- cpl.reset (new CPLFile (file));
- } catch (FileError& e) {
- throw FileError ("could not load CPL file", file);
- }
-
- /* Now cherry-pick the required bits into our own data structure */
-
- _name = cpl->annotation_text;
- _content_kind = cpl->content_kind;
-
- for (list<shared_ptr<CPLReel> >::iterator i = cpl->reels.begin(); i != cpl->reels.end(); ++i) {
-
- shared_ptr<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 {
- picture.reset (new MonoPictureAsset (
- _directory,
- asset_map->asset_from_id (p->id)->chunks.front()->path,
- _fps,
- (*i)->asset_list->main_picture->entry_point,
- (*i)->asset_list->main_picture->duration
- )
- );
- } catch (MXFFileError) {
- if (require_mxfs) {
- throw;
- }
- }
-
- } else {
-
- try {
- picture.reset (new StereoPictureAsset (
- _directory,
- asset_map->asset_from_id (p->id)->chunks.front()->path,
- _fps,
- p->entry_point,
- p->duration
- )
- );
- } catch (MXFFileError) {
- if (require_mxfs) {
- throw;
- }
- }
-
- }
-
- if ((*i)->asset_list->main_sound) {
-
- try {
- sound.reset (new SoundAsset (
- _directory,
- asset_map->asset_from_id ((*i)->asset_list->main_sound->id)->chunks.front()->path,
- _fps,
- (*i)->asset_list->main_sound->entry_point,
- (*i)->asset_list->main_sound->duration
- )
- );
- } catch (MXFFileError) {
- if (require_mxfs) {
- throw;
- }
- }
- }
-
- if ((*i)->asset_list->main_subtitle) {
-
- subtitle.reset (new SubtitleAsset (
- _directory,
- asset_map->asset_from_id ((*i)->asset_list->main_subtitle->id)->chunks.front()->path
- )
- );
- }
-
- _reels.push_back (shared_ptr<Reel> (new Reel (picture, sound, subtitle)));
- }
- }
-
- void
- CPL::add_reel (shared_ptr<const Reel> reel)
- {
- _reels.push_back (reel);
- }
-
- void
- CPL::write_xml (shared_ptr<Encryption> crypt) const
- {
- boost::filesystem::path p;
- p /= _directory;
- stringstream s;
- s << _uuid << "_cpl.xml";
- p /= s.str();
-
- xmlpp::Document doc;
- xmlpp::Element* cpl = doc.create_root_node("CompositionPlaylist", "http://www.smpte-ra.org/schemas/429-7/2006/CPL");
-
- if (crypt) {
- cpl->set_namespace_declaration ("http://www.w3.org/2000/09/xmldsig#", "dsig");
- }
-
- cpl->add_child("Id")->add_child_text ("urn:uuid:" + _uuid);
- cpl->add_child("AnnotationText")->add_child_text (_name);
- cpl->add_child("IssueDate")->add_child_text (Metadata::instance()->issue_date);
- cpl->add_child("Creator")->add_child_text (Metadata::instance()->creator);
- cpl->add_child("ContentTitleText")->add_child_text (_name);
- cpl->add_child("ContentKind")->add_child_text (content_kind_to_string (_content_kind));
-
- {
- xmlpp::Element* cv = cpl->add_child ("ContentVersion");
- cv->add_child("Id")->add_child_text ("urn:uri:" + _uuid + "_" + Metadata::instance()->issue_date);
- cv->add_child("LabelText")->add_child_text (_uuid + "_" + Metadata::instance()->issue_date);
- }
-
- cpl->add_child("RatingList");
-
- xmlpp::Element* reel_list = cpl->add_child("ReelList");
- for (list<shared_ptr<const Reel> >::const_iterator i = _reels.begin(); i != _reels.end(); ++i) {
- (*i)->write_to_cpl (reel_list);
- }
-
- if (crypt) {
- sign (cpl, crypt->certificates, crypt->signer_key);
- }
-
- doc.write_to_file_formatted (p.string(), "UTF-8");
-
- _digest = make_digest (p.string ());
- _length = boost::filesystem::file_size (p.string ());
- }
-
- void
- CPL::write_to_pkl (xmlpp::Element* p) const
- {
- xmlpp::Element* asset = p->add_child("Asset");
- asset->add_child("Id")->add_child_text("urn:uuid:" + _uuid);
- asset->add_child("Hash")->add_child_text(_digest);
- asset->add_child("Size")->add_child_text(boost::lexical_cast<string> (_length));
- asset->add_child("Type")->add_child_text("text/xml");
- }
-
- list<shared_ptr<const Asset> >
- CPL::assets () const
- {
- list<shared_ptr<const Asset> > a;
- for (list<shared_ptr<const Reel> >::const_iterator i = _reels.begin(); i != _reels.end(); ++i) {
- if ((*i)->main_picture ()) {
- a.push_back ((*i)->main_picture ());
- }
- if ((*i)->main_sound ()) {
- a.push_back ((*i)->main_sound ());
- }
- if ((*i)->main_subtitle ()) {
- a.push_back ((*i)->main_subtitle ());
- }
- }
-
- return a;
- }
-
- void
- CPL::write_to_assetmap (ostream& s) const
- {
- s << " <Asset>\n"
- << " <Id>urn:uuid:" << _uuid << "</Id>\n"
- << " <ChunkList>\n"
- << " <Chunk>\n"
- << " <Path>" << _uuid << "_cpl.xml</Path>\n"
- << " <VolumeIndex>1</VolumeIndex>\n"
- << " <Offset>0</Offset>\n"
- << " <Length>" << _length << "</Length>\n"
- << " </Chunk>\n"
- << " </ChunkList>\n"
- << " </Asset>\n";
- }
-
-
-
- bool
- CPL::equals (CPL const & other, EqualityOptions opt, list<string>& notes) const
- {
- if (_name != other._name) {
- notes.push_back ("names differ");
- return false;
- }
-
- if (_content_kind != other._content_kind) {
- notes.push_back ("content kinds differ");
- return false;
- }
-
- if (_fps != other._fps) {
- notes.push_back ("frames per second differ");
- return false;
- }
-
- if (_length != other._length) {
- notes.push_back ("lengths differ");
- return false;
- }
-
- if (_reels.size() != other._reels.size()) {
- notes.push_back ("reel counts differ");
- return false;
- }
-
- list<shared_ptr<const Reel> >::const_iterator a = _reels.begin ();
- list<shared_ptr<const Reel> >::const_iterator b = other._reels.begin ();
-
- while (a != _reels.end ()) {
- if (!(*a)->equals (*b, opt, notes)) {
- return false;
- }
- ++a;
- ++b;
- }
-
- return true;
- }
-
- shared_ptr<xmlpp::Document>
- CPL::make_kdm (
- CertificateChain const & certificates,
- string const & signer_key,
- shared_ptr<const Certificate> recipient_cert,
- boost::posix_time::ptime from,
- boost::posix_time::ptime until
- ) const
- {
- assert (recipient_cert);
-
- shared_ptr<xmlpp::Document> doc (new xmlpp::Document);
- xmlpp::Element* root = doc->create_root_node ("DCinemaSecurityMessage");
- root->set_namespace_declaration ("http://www.smpte-ra.org/schemas/430-3/2006/ETM", "");
- root->set_namespace_declaration ("http://www.w3.org/2000/09/xmldsig#", "ds");
- root->set_namespace_declaration ("http://www.w3.org/2001/04/xmlenc#", "enc");
-
- {
- xmlpp::Element* authenticated_public = root->add_child("AuthenticatedPublic");
- authenticated_public->set_attribute("Id", "ID_AuthenticatedPublic");
- xmlAddID (0, doc->cobj(), (const xmlChar *) "ID_AuthenticatedPublic", authenticated_public->get_attribute("Id")->cobj());
-
- authenticated_public->add_child("MessageId")->add_child_text("urn:uuid:" + make_uuid());
- authenticated_public->add_child("MessageType")->add_child_text("http://www.smpte-ra.org/430-1/2006/KDM#kdm-key-type");
- authenticated_public->add_child("AnnotationText")->add_child_text(Metadata::instance()->product_name);
- authenticated_public->add_child("IssueDate")->add_child_text(Metadata::instance()->issue_date);
-
- {
- xmlpp::Element* signer = authenticated_public->add_child("Signer");
- signer->add_child("X509IssuerName", "ds")->add_child_text (
- Certificate::name_for_xml (recipient_cert->issuer())
- );
- signer->add_child("X509SerialNumber", "ds")->add_child_text (
- recipient_cert->serial()
- );
- }
-
- {
- xmlpp::Element* required_extensions = authenticated_public->add_child("RequiredExtensions");
-
- {
- xmlpp::Element* kdm_required_extensions = required_extensions->add_child("KDMRequiredExtensions");
- kdm_required_extensions->set_namespace_declaration ("http://www.smpte-ra.org/schemas/430-1/2006/KDM");
- {
- xmlpp::Element* recipient = kdm_required_extensions->add_child("Recipient");
- {
- xmlpp::Element* serial_element = recipient->add_child("X509IssuerSerial");
- serial_element->add_child("X509IssuerName", "ds")->add_child_text (
- Certificate::name_for_xml (recipient_cert->issuer())
- );
- serial_element->add_child("X509SerialNumber", "ds")->add_child_text (
- recipient_cert->serial()
- );
- }
-
- recipient->add_child("X509SubjectName")->add_child_text (Certificate::name_for_xml (recipient_cert->subject()));
- }
-
- kdm_required_extensions->add_child("CompositionPlaylistId")->add_child_text("urn:uuid:" + _uuid);
- kdm_required_extensions->add_child("ContentTitleText")->add_child_text(_name);
- kdm_required_extensions->add_child("ContentAuthenticator")->add_child_text(certificates.leaf()->thumbprint());
- kdm_required_extensions->add_child("ContentKeysNotValidBefore")->add_child_text("XXX");
- kdm_required_extensions->add_child("ContentKeysNotValidAfter")->add_child_text("XXX");
-
- {
- xmlpp::Element* authorized_device_info = kdm_required_extensions->add_child("AuthorizedDeviceInfo");
- authorized_device_info->add_child("DeviceListIdentifier")->add_child_text("urn:uuid:" + make_uuid());
- authorized_device_info->add_child("DeviceListDescription")->add_child_text(recipient_cert->subject());
- {
- xmlpp::Element* device_list = authorized_device_info->add_child("DeviceList");
- device_list->add_child("CertificateThumbprint")->add_child_text(recipient_cert->thumbprint());
- }
- }
-
- {
- xmlpp::Element* key_id_list = kdm_required_extensions->add_child("KeyIdList");
- list<shared_ptr<const Asset> > a = assets();
- for (list<shared_ptr<const Asset> >::iterator i = a.begin(); i != a.end(); ++i) {
- /* XXX: non-MXF assets? */
- shared_ptr<const MXFAsset> mxf = boost::dynamic_pointer_cast<const MXFAsset> (*i);
- if (mxf) {
- mxf->add_typed_key_id (key_id_list);
- }
- }
- }
-
- {
- xmlpp::Element* forensic_mark_flag_list = kdm_required_extensions->add_child("ForensicMarkFlagList");
- forensic_mark_flag_list->add_child("ForensicMarkFlag")->add_child_text (
- "http://www.smpte-ra.org/430-1/2006/KDM#mrkflg-picture-disable"
- );
- forensic_mark_flag_list->add_child("ForensicMarkFlag")->add_child_text (
- "http://www.smpte-ra.org/430-1/2006/KDM#mrkflg-audio-disable"
- );
- }
- }
- }
-
- authenticated_public->add_child("NonCriticalExtensions");
- }
-
- {
- xmlpp::Element* authenticated_private = root->add_child("AuthenticatedPrivate");
- authenticated_private->set_attribute ("Id", "ID_AuthenticatedPrivate");
- xmlAddID (0, doc->cobj(), (const xmlChar *) "ID_AuthenticatedPrivate", authenticated_private->get_attribute("Id")->cobj());
- {
- xmlpp::Element* encrypted_key = authenticated_private->add_child ("EncryptedKey", "enc");
- {
- xmlpp::Element* encryption_method = encrypted_key->add_child ("EncryptionMethod", "enc");
- encryption_method->set_attribute ("Algorithm", "http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p");
- encryption_method->add_child("DigestMethod", "ds")->set_attribute("Algorithm", "http://www.w3.org/2000/09/xmldsig#sha1");
- }
-
- xmlpp::Element* cipher_data = authenticated_private->add_child ("CipherData", "enc");
- cipher_data->add_child("CipherValue", "enc")->add_child_text("XXX");
- }
- }
-
- /* XXX: x2 one for each mxf? */
-
- {
- xmlpp::Element* signature = root->add_child("Signature", "ds");
-
- {
- xmlpp::Element* signed_info = signature->add_child("SignedInfo", "ds");
- signed_info->add_child("CanonicalizationMethod", "ds")->set_attribute(
- "Algorithm", "http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments"
- );
- signed_info->add_child("SignatureMethod", "ds")->set_attribute(
- "Algorithm", "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"
- );
- {
- xmlpp::Element* reference = signed_info->add_child("Reference", "ds");
- reference->set_attribute("URI", "#ID_AuthenticatedPublic");
- reference->add_child("DigestMethod", "ds")->set_attribute("Algorithm", "http://www.w3.org/2001/04/xmlenc#sha256");
- reference->add_child("DigestValue", "ds");
- }
-
- {
- xmlpp::Element* reference = signed_info->add_child("Reference", "ds");
- reference->set_attribute("URI", "#ID_AuthenticatedPrivate");
- reference->add_child("DigestMethod", "ds")->set_attribute("Algorithm", "http://www.w3.org/2001/04/xmlenc#sha256");
- reference->add_child("DigestValue", "ds");
- }
- }
-
- add_signature_value (signature, certificates, signer_key, "ds");
- }
--
- return doc;
- }
#include <vector>
#include <boost/shared_ptr.hpp>
#include <boost/signals2.hpp>
- #include <boost/date_time/posix_time/posix_time.hpp>
#include "types.h"
+#include "certificates.h"
namespace xmlpp {
- class Node;
+ class Document;
+ class Element;
}
/** @brief Namespace for everything in libdcp */
class SoundAsset;
class SubtitleAsset;
class Reel;
- class AssetMap;
+ class CPL;
+ class XMLMetadata;
++class Encryption;
- class Encryption
- {
- public:
- Encryption (CertificateChain c, std::string const & k)
- : certificates (c)
- , signer_key (k)
- {}
-
- CertificateChain certificates;
- std::string signer_key;
- };
-
- /** @brief A CPL within a DCP */
- class CPL
- {
- public:
- CPL (std::string directory, std::string name, ContentKind content_kind, int length, int frames_per_second);
- CPL (std::string directory, std::string file, boost::shared_ptr<const AssetMap> asset_map, bool require_mxfs = true);
-
- void add_reel (boost::shared_ptr<const Reel> reel);
-
- /** @return the length in frames */
- int length () const {
- return _length;
- }
-
- /** @return the type of the content, used by media servers
- * to categorise things (e.g. feature, trailer, etc.)
- */
- ContentKind content_kind () const {
- return _content_kind;
- }
-
- std::list<boost::shared_ptr<const Reel> > reels () const {
- return _reels;
- }
-
- /** @return the CPL's name, as will be presented on projector
- * media servers and theatre management systems.
- */
- 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;
-
- bool equals (CPL const & other, EqualityOptions options, std::list<std::string>& notes) const;
-
- void write_xml (boost::shared_ptr<Encryption>) const;
- void write_to_assetmap (std::ostream& s) const;
- void write_to_pkl (xmlpp::Element* p) const;
-
- boost::shared_ptr<xmlpp::Document> make_kdm (
- CertificateChain const &,
- std::string const &,
- boost::shared_ptr<const Certificate>,
- boost::posix_time::ptime from,
- boost::posix_time::ptime until
- ) const;
-
- private:
- std::string _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::list<boost::shared_ptr<const Reel> > _reels;
-
- /** our UUID */
- std::string _uuid;
- /** a SHA1 digest of our XML */
- mutable std::string _digest;
- };
-
- /** @class DCP dcp.h libdcp/dcp.h
+ /** @class DCP
* @brief A class to create or read a DCP.
*/
/** Write the required XML files to the directory that was
* passed into the constructor.
*/
- void write_xml (boost::shared_ptr<Encryption> crypt = boost::shared_ptr<Encryption> ()) const;
- void write_xml (XMLMetadata const &) const;
++ void write_xml (XMLMetadata const &, boost::shared_ptr<Encryption> crypt = boost::shared_ptr<Encryption> ()) const;
/** Compare this DCP with another, according to various options.
* @param other DCP to compare this one to.
/** Write the PKL file.
* @param pkl_uuid UUID to use.
*/
- std::string write_pkl (std::string pkl_uuid, boost::shared_ptr<Encryption>) const;
- std::string write_pkl (std::string pkl_uuid, XMLMetadata const &) const;
++ std::string write_pkl (std::string pkl_uuid, XMLMetadata const &, boost::shared_ptr<Encryption>) const;
/** Write the VOLINDEX file */
void write_volindex () const;
using boost::dynamic_pointer_cast;
using namespace libdcp;
- MXFAsset::MXFAsset (string directory, string file_name, boost::signals2::signal<void (float)>* progress, int fps, int entry_point, int length, bool encrypted)
+ MXFAsset::MXFAsset (string directory, string file_name)
: Asset (directory, file_name)
-MXFAsset::MXFAsset (string directory, string file_name, boost::signals2::signal<void (float)>* progress, int edit_rate, int intrinsic_duration)
+ , _progress (0)
++ , _encrypted (false)
++ , _encryption_context (0)
+ {
+
+ }
+
++MXFAsset::MXFAsset (string directory, string file_name, boost::signals2::signal<void (float)>* progress, int edit_rate, int intrinsic_duration, bool encrypted)
+ : Asset (directory, file_name, edit_rate, intrinsic_duration)
, _progress (progress)
- , _fps (fps)
- , _entry_point (entry_point)
- , _length (length)
+ , _encrypted (encrypted)
+ , _encryption_context (0)
{
-
+ if (_encrypted) {
+ _key_id = make_uuid ();
+ uint8_t key_buffer[ASDCP::KeyLen];
+ Kumu::FortunaRNG rng;
+ rng.FillRandom (key_buffer, ASDCP::KeyLen);
+ char key_string[ASDCP::KeyLen * 4];
+ Kumu::bin2hex (key_buffer, ASDCP::KeyLen, key_string, ASDCP::KeyLen * 4);
+ _key_value = key_string;
+
+ _encryption_context = new ASDCP::AESEncContext;
+ if (ASDCP_FAILURE (_encryption_context->InitKey (key_buffer))) {
+ throw MiscError ("could not set up encryption context");
+ }
+
+ uint8_t cbc_buffer[ASDCP::CBC_BLOCK_SIZE];
+
+ if (ASDCP_FAILURE (_encryption_context->SetIVec (rng.FillRandom (cbc_buffer, ASDCP::CBC_BLOCK_SIZE)))) {
+ throw MiscError ("could not set up CBC initialization vector");
+ }
+ }
+}
+
+MXFAsset::~MXFAsset ()
+{
+ delete _encryption_context;
}
+
void
- MXFAsset::fill_writer_info (ASDCP::WriterInfo* writer_info) const
+ MXFAsset::fill_writer_info (ASDCP::WriterInfo* writer_info, string uuid, MXFMetadata const & metadata)
{
- writer_info->ProductVersion = Metadata::instance()->product_version;
- writer_info->CompanyName = Metadata::instance()->company_name;
- writer_info->ProductName = Metadata::instance()->product_name.c_str();
+ writer_info->ProductVersion = metadata.product_version;
+ writer_info->CompanyName = metadata.company_name;
+ writer_info->ProductName = metadata.product_name.c_str();
writer_info->LabelSetType = ASDCP::LS_MXF_SMPTE;
unsigned int c;
- Kumu::hex2bin (_uuid.c_str(), writer_info->AssetUUID, Kumu::UUID_Length, &c);
+ Kumu::hex2bin (uuid.c_str(), writer_info->AssetUUID, Kumu::UUID_Length, &c);
assert (c == Kumu::UUID_Length);
+
+ if (_encrypted) {
+ Kumu::GenRandomUUID (writer_info->ContextID);
+ writer_info->EncryptedEssence = true;
+
+ unsigned int c;
+ Kumu::hex2bin (_key_id.c_str(), writer_info->CryptographicKeyID, Kumu::UUID_Length, &c);
+ assert (c == Kumu::UUID_Length);
+ }
}
bool
}
if (_file_name != other_mxf->_file_name) {
- notes.push_back ("MXF names differ");
- return false;
- }
-
- if (_fps != other_mxf->_fps) {
- notes.push_back ("MXF frames per second differ");
- return false;
+ note (ERROR, "MXF names differ");
+ if (!opt.mxf_names_can_differ) {
+ return false;
+ }
}
- if (_length != other_mxf->_length) {
- notes.push_back ("MXF lengths differ");
- return false;
- }
-
return true;
}
- int
- MXFAsset::length () const
- {
- return _length;
- }
-
+
+void
+MXFAsset::add_typed_key_id (xmlpp::Element* parent) const
+{
+ xmlpp::Element* typed_key_id = parent->add_child("TypedKeyId");
+ typed_key_id->add_child("KeyType")->add_child_text(key_type ());
+ typed_key_id->add_child("KeyId")->add_child_text("urn:uuid:" + _key_id);
+}
{
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 (std::string directory, std::string file_name);
+
+ /** 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.
- * @param progress Signal to inform of progress.
- * @param fps Frames per second.
- * @param entry_point The entry point of this MXF; ie the first frame that should be used.
- * @param length Length in frames.
- * @param encrypted true if the MXF should be encrypted.
+ * @param progress Signal to use to inform of progress, or 0.
+ * @param edit_rate Edit rate in frames per second (usually equal to the video frame rate).
+ * @param intrinsic_duration Duration of the whole asset in frames.
*/
- MXFAsset (
- std::string directory, std::string file_name, boost::signals2::signal<void (float)>* progress, int fps, int entry_point, int length, bool encrypted
- );
- MXFAsset (std::string directory, std::string file_name, boost::signals2::signal<void (float)>* progress, int edit_rate, int intrinsic_duration);
++ MXFAsset (std::string directory, std::string file_name, boost::signals2::signal<void (float)>* progress, int edit_rate, int intrinsic_duration, bool encrypted);
+
+ ~MXFAsset ();
- virtual bool equals (boost::shared_ptr<const Asset> other, EqualityOptions opt, std::list<std::string>& notes) const;
-
- int length () const;
- void add_typed_key_id (xmlpp::Element *) const;
+ virtual bool equals (boost::shared_ptr<const Asset> other, EqualityOptions opt, boost::function<void (NoteType, std::string)> note) const;
- protected:
- virtual std::string key_type () const = 0;
-
/** Fill in a ADSCP::WriteInfo struct.
* @param w struct to fill in.
+ * @param uuid uuid to use.
*/
- void fill_writer_info (ASDCP::WriterInfo* w) const;
- static void fill_writer_info (ASDCP::WriterInfo* w, std::string uuid, MXFMetadata const & metadata);
++ void fill_writer_info (ASDCP::WriterInfo* w, std::string uuid, MXFMetadata const & metadata);
- /** Signal to emit to report progress */
++ void add_typed_key_id (xmlpp::Element *) const;
++
+ protected:
++ virtual std::string key_type () const = 0;
+
+ /** Signal to emit to report progress, or 0 */
boost::signals2::signal<void (float)>* _progress;
- /** Frames per second */
- int _fps;
- int _entry_point;
- /** Length in frames */
- int _length;
+ bool _encrypted;
+ ASDCP::AESEncContext* _encryption_context;
+ std::string _key_value;
+ std::string _key_id;
};
}
using std::list;
using std::vector;
using std::max;
+using std::stringstream;
+ using std::pair;
+ 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;
- PictureAsset::PictureAsset (string directory, string mxf_name, boost::signals2::signal<void (float)>* progress, int fps, int entry_point, int length, bool encrypted)
- : MXFAsset (directory, mxf_name, progress, fps, entry_point, length, encrypted)
- , _width (0)
- , _height (0)
-PictureAsset::PictureAsset (string directory, string mxf_name, boost::signals2::signal<void (float)>* progress, int fps, int intrinsic_duration, Size size)
- : MXFAsset (directory, mxf_name, progress, fps, intrinsic_duration)
++PictureAsset::PictureAsset (string directory, string mxf_name, boost::signals2::signal<void (float)>* progress, int fps, int intrinsic_duration, bool encrypted, Size size)
++ : MXFAsset (directory, mxf_name, progress, fps, intrinsic_duration, encrypted)
+ , _size (size)
+ {
+
+ }
+
+ PictureAsset::PictureAsset (string directory, string mxf_name)
+ : MXFAsset (directory, mxf_name)
{
}
void
- PictureAsset::write_to_cpl (xmlpp::Element* parent) const
+ PictureAsset::write_to_cpl (xmlpp::Node* node) const
{
- xmlpp::Element* main_picture = parent->add_child("MainPicture");
- main_picture->add_child("Id")->add_child_text("urn:uuid:" + _uuid);
- main_picture->add_child("AnnotationText")->add_child_text(_file_name);
- main_picture->add_child("EditRate")->add_child_text(boost::lexical_cast<string> (_fps) + " 1");
- main_picture->add_child("IntrinsicDuration")->add_child_text(boost::lexical_cast<string> (_length));
- main_picture->add_child("EntryPoint")->add_child_text("0");
- main_picture->add_child("Duration")->add_child_text(boost::lexical_cast<string> (_length));
+ xmlpp::Node* mp = node->add_child ("MainPicture");
+ mp->add_child ("Id")->add_child_text ("urn:uuid:" + _uuid);
+ mp->add_child ("AnnotationText")->add_child_text (_file_name);
+ mp->add_child ("EditRate")->add_child_text (lexical_cast<string> (_edit_rate) + " 1");
+ mp->add_child ("IntrinsicDuration")->add_child_text (lexical_cast<string> (_intrinsic_duration));
+ mp->add_child ("EntryPoint")->add_child_text (lexical_cast<string> (_entry_point));
+ mp->add_child ("Duration")->add_child_text (lexical_cast<string> (_duration));
+ if (_encrypted) {
- main_picture->add_child("KeyId")->add_child_text("urn:uuid:" + _key_id);
++ mp->add_child("KeyId")->add_child_text("urn:uuid:" + _key_id);
+ }
- main_picture->add_child("FrameRate")->add_child_text(boost::lexical_cast<string> (_fps) + " 1");
- stringstream sar;
- sar << _width << " " << _height;
- main_picture->add_child("ScreenAspectRatio")->add_child_text(sar.str());
+ mp->add_child ("FrameRate")->add_child_text (lexical_cast<string> (_edit_rate) + " 1");
+ mp->add_child ("ScreenAspectRatio")->add_child_text (lexical_cast<string> (_size.width) + " " + lexical_cast<string> (_size.height));
}
bool
string mxf_name,
boost::signals2::signal<void (float)>* progress,
int fps,
- int length,
- int width,
- int height,
- bool encrypted)
- : PictureAsset (directory, mxf_name, progress, fps, 0, length, encrypted)
+ int intrinsic_duration,
++ bool encrypted,
+ Size size,
+ MXFMetadata const & metadata
+ )
- : PictureAsset (directory, mxf_name, progress, fps, intrinsic_duration, size)
++ : PictureAsset (directory, mxf_name, progress, fps, intrinsic_duration, encrypted, size)
{
- _width = width;
- _height = height;
- construct (get_path);
+ construct (get_path, metadata);
}
MonoPictureAsset::MonoPictureAsset (
string mxf_name,
boost::signals2::signal<void (float)>* progress,
int fps,
- int length,
- int width,
- int height,
- bool encrypted)
- : PictureAsset (directory, mxf_name, progress, fps, 0, length, encrypted)
+ int intrinsic_duration,
++ bool encrypted,
+ Size size,
+ MXFMetadata const & metadata
+ )
- : PictureAsset (directory, mxf_name, progress, fps, intrinsic_duration, size)
++ : PictureAsset (directory, mxf_name, progress, fps, intrinsic_duration, encrypted, size)
+ {
+ construct (boost::bind (&MonoPictureAsset::path_from_list, this, _1, files), metadata);
+ }
+
+ MonoPictureAsset::MonoPictureAsset (string directory, string mxf_name, int fps, Size size)
- : PictureAsset (directory, mxf_name, 0, fps, 0, size)
++ : PictureAsset (directory, mxf_name, 0, fps, 0, false, size)
{
- _width = width;
- _height = height;
- construct (boost::bind (&MonoPictureAsset::path_from_list, this, _1, files));
+
}
- MonoPictureAsset::MonoPictureAsset (string directory, string mxf_name, int fps, int entry_point, int length)
- : PictureAsset (directory, mxf_name, 0, fps, entry_point, length, false)
+ MonoPictureAsset::MonoPictureAsset (string directory, string mxf_name)
+ : PictureAsset (directory, mxf_name)
{
ASDCP::JP2K::MXFReader reader;
if (ASDCP_FAILURE (reader.OpenRead (path().string().c_str()))) {
string const path = get_path (i);
if (ASDCP_FAILURE (j2k_parser.OpenReadFrame (path.c_str(), frame_buffer))) {
- throw FileError ("could not open JPEG2000 file for reading", path);
+ boost::throw_exception (FileError ("could not open JPEG2000 file for reading", path));
}
- if (ASDCP_FAILURE (mxf_writer.WriteFrame (frame_buffer, 0, 0))) {
+ if (ASDCP_FAILURE (mxf_writer.WriteFrame (frame_buffer, _encryption_context, 0))) {
- throw MiscError ("error in writing video MXF");
+ boost::throw_exception (MXFFileError ("error in writing video MXF", this->path().string()));
}
if (_progress) {
}
- StereoPictureAsset::StereoPictureAsset (string directory, string mxf_name, int fps, int entry_point, int length)
- : PictureAsset (directory, mxf_name, 0, fps, entry_point, length, false)
+ StereoPictureAsset::StereoPictureAsset (string directory, string mxf_name, int fps, int intrinsic_duration)
- : PictureAsset (directory, mxf_name, 0, fps, intrinsic_duration, Size (0, 0))
++ : PictureAsset (directory, mxf_name, 0, fps, intrinsic_duration, false, Size (0, 0))
{
ASDCP::JP2K::MXFSReader reader;
if (ASDCP_FAILURE (reader.OpenRead (path().string().c_str()))) {
shared_ptr<const StereoPictureFrame>
StereoPictureAsset::get_frame (int n) const
{
- return shared_ptr<const StereoPictureFrame> (new StereoPictureFrame (path().string(), n + _entry_point));
+ return shared_ptr<const StereoPictureFrame> (new StereoPictureFrame (path().string(), n));
+ }
+
+ shared_ptr<MonoPictureAssetWriter>
+ MonoPictureAsset::start_write (bool overwrite, MXFMetadata const & metadata)
+ {
+ /* XXX: can't we use shared_ptr here? */
+ return shared_ptr<MonoPictureAssetWriter> (new MonoPictureAssetWriter (this, overwrite, metadata));
+ }
+
+ FrameInfo::FrameInfo (istream& s)
+ {
+ s >> offset >> size >> hash;
+ }
+
+ void
+ FrameInfo::write (ostream& s)
+ {
+ s << offset << " " << size << " " << hash;
+ }
+
+ struct MonoPictureAssetWriter::ASDCPState
+ {
+ ASDCPState()
+ : frame_buffer (4 * Kumu::Megabyte)
+ {}
+
+ ASDCP::JP2K::CodestreamParser j2k_parser;
+ ASDCP::JP2K::FrameBuffer frame_buffer;
+ ASDCP::JP2K::MXFWriter mxf_writer;
+ ASDCP::WriterInfo writer_info;
+ ASDCP::JP2K::PictureDescriptor picture_descriptor;
+ };
+
+
+ /** @param a Asset to write to. `a' must not be deleted while
+ * this writer class still exists, or bad things will happen.
+ */
+ MonoPictureAssetWriter::MonoPictureAssetWriter (MonoPictureAsset* a, bool overwrite, MXFMetadata const & m)
+ : _state (new MonoPictureAssetWriter::ASDCPState)
+ , _asset (a)
+ , _frames_written (0)
+ , _started (false)
+ , _finalized (false)
+ , _overwrite (overwrite)
+ , _metadata (m)
+ {
+
+ }
+
+
+ void
+ MonoPictureAssetWriter::start (uint8_t* data, int size)
+ {
+ 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);
+
- MXFAsset::fill_writer_info (&_state->writer_info, _asset->uuid(), _metadata);
++ _asset->fill_writer_info (&_state->writer_info, _asset->uuid(), _metadata);
+
+ if (ASDCP_FAILURE (_state->mxf_writer.OpenWrite (
+ _asset->path().string().c_str(),
+ _state->writer_info,
+ _state->picture_descriptor,
+ 16384,
+ _overwrite)
+ )) {
+
+ boost::throw_exception (MXFFileError ("could not open MXF file for writing", _asset->path().string()));
+ }
+
+ _started = true;
+ }
+
+ FrameInfo
+ MonoPictureAssetWriter::write (uint8_t* data, int size)
+ {
+ assert (!_finalized);
+
+ if (!_started) {
+ start (data, size);
+ }
+
+ if (ASDCP_FAILURE (_state->j2k_parser.OpenReadFrame (data, size, _state->frame_buffer))) {
+ boost::throw_exception (MiscError ("could not parse J2K frame"));
+ }
+
+ uint64_t const before_offset = _state->mxf_writer.Tell ();
+
+ string hash;
+ if (ASDCP_FAILURE (_state->mxf_writer.WriteFrame (_state->frame_buffer, 0, 0, &hash))) {
+ boost::throw_exception (MXFFileError ("error in writing video MXF", _asset->path().string()));
+ }
+
+ ++_frames_written;
+ return FrameInfo (before_offset, _state->mxf_writer.Tell() - before_offset, hash);
+ }
+
+ void
+ MonoPictureAssetWriter::fake_write (int size)
+ {
+ assert (_started);
+ assert (!_finalized);
+
+ if (ASDCP_FAILURE (_state->mxf_writer.FakeWriteFrame (size))) {
+ boost::throw_exception (MXFFileError ("error in writing video MXF", _asset->path().string()));
+ }
+
+ ++_frames_written;
+ }
+
+ void
+ MonoPictureAssetWriter::finalize ()
+ {
+ assert (!_finalized);
+
+ if (ASDCP_FAILURE (_state->mxf_writer.Finalize())) {
+ boost::throw_exception (MXFFileError ("error in finalizing video MXF", _asset->path().string()));
+ }
+
+ _finalized = true;
+ _asset->set_intrinsic_duration (_frames_written);
+ _asset->set_duration (_frames_written);
}
+
+string
+PictureAsset::key_type () const
+{
+ return "MDIK";
+}
class PictureAsset : public MXFAsset
{
public:
- PictureAsset (
- std::string directory, std::string mxf_name, boost::signals2::signal<void (float)>* progress, int fps, int entry_point, int length, bool encrypted
- );
+ /** Construct a PictureAsset.
+ * This class will not write anything to disk in this constructor, but subclasses may.
+ *
+ * @param directory Directory where MXF file is.
+ * @param mxf_name Name of MXF file.
+ */
+ PictureAsset (std::string directory, std::string mxf_name);
+
+ /** Construct a PictureAsset.
+ * This class will not write anything to disk in this constructor, but subclasses may.
+ *
+ * @param directory Directory where MXF file is.
+ * @param mxf_name Name of MXF file.
+ * @param progress Signal to use to inform of progres, or 0.
+ * @param fps Video Frames per second.
+ * @param intrinsic_duration Duration of all the frames in the asset.
+ * @param size Size of video frame images in pixels.
+ */
- PictureAsset (std::string directory, std::string mxf_name, boost::signals2::signal<void (float)>* progress, int fps, int intrinsic_duration, Size size);
++ PictureAsset (std::string directory, std::string mxf_name, boost::signals2::signal<void (float)>* progress, int fps, int intrinsic_duration, bool encrypted, Size);
- /** Write details of the asset to a CPL AssetList node.
- * @param p Parent node.
+ /** Write details of this asset to a CPL XML node.
+ * @param node Node.
*/
- void write_to_cpl (xmlpp::Element* p) const;
+ void write_to_cpl (xmlpp::Node* node) const;
- bool equals (boost::shared_ptr<const Asset> other, EqualityOptions opt, std::list<std::string>& notes) const;
+ bool equals (boost::shared_ptr<const Asset> other, EqualityOptions opt, boost::function<void (NoteType, std::string)> note) const;
- int width () const {
- return _width;
- }
-
- int height () const {
- return _height;
+ Size size () const {
+ return _size;
}
protected:
++ std::string key_type () const;
++
bool frame_buffer_equals (
- int frame, EqualityOptions opt, std::list<std::string>& notes,
+ 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;
+
+ /** picture size in pixels */
+ Size _size;
+ };
+
+ class MonoPictureAsset;
+
+ struct FrameInfo
+ {
+ FrameInfo (uint64_t o, uint64_t s, std::string h)
+ : offset (o)
+ , size (s)
+ , hash (h)
+ {}
+
+ FrameInfo (std::istream& s);
+
+ void write (std::ostream& s);
- /** picture width in pixels */
- int _width;
- /** picture height in pixels */
- int _height;
+ uint64_t offset;
+ uint64_t size;
+ std::string hash;
+ };
+
+ /** A helper class for writing to MonoPictureAssets progressively (i.e. writing frame-by-frame,
+ * rather than giving libdcp all the frames in one go).
+ *
+ * Objects of this class can only be created with MonoPictureAsset::start_write().
+ *
+ * Frames can be written to the MonoPictureAsset by calling write() with a JPEG2000 image
+ * (a verbatim .j2 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:
+ FrameInfo write (uint8_t* data, int size);
+ void fake_write (int size);
+ void finalize ();
private:
- std::string key_type () const;
+ friend class MonoPictureAsset;
+
+ MonoPictureAssetWriter (MonoPictureAsset *, bool, MXFMetadata const &);
+ void start (uint8_t *, int);
+
+ /* no copy construction */
+ MonoPictureAssetWriter (MonoPictureAssetWriter const &);
+ MonoPictureAssetWriter& operator= (MonoPictureAssetWriter const &);
+
+ /* do this with an opaque pointer so we don't have to include
+ ASDCP headers
+ */
+
+ struct ASDCPState;
+ boost::shared_ptr<ASDCPState> _state;
+
+ MonoPictureAsset* _asset;
+ /** Number of picture frames written to the asset so far */
+ int _frames_written;
+ bool _started;
+ /** true if finalize() has been called */
+ bool _finalized;
+ bool _overwrite;
+ MXFMetadata _metadata;
};
/** A 2D (monoscopic) picture asset */
* @param directory Directory in which to create MXF file.
* @param mxf_name Name of MXF file to create.
* @param progress Signal to inform of progress.
- * @param fps Frames per second.
- * @param length Length in frames.
- * @param width Width of images in pixels.
- * @param height Height of images in pixels.
+ * @param fps Video frames per second.
+ * @param intrinsic_duration Length of the whole asset in frames.
+ * @param size Size of images in pixels.
+ * @param encrypted true if asset should be encrypted.
*/
MonoPictureAsset (
std::vector<std::string> const & files,
std::string mxf_name,
boost::signals2::signal<void (float)>* progress,
int fps,
- int length,
- int width,
- int height,
- bool encrypted
+ int intrinsic_duration,
++ bool encrypted,
+ Size size,
+ MXFMetadata const & metadata = MXFMetadata ()
);
- /** Construct a PictureAsset, generating the MXF from the JPEG2000 files.
+ /** Construct a MonoPictureAsset, generating the MXF from the JPEG2000 files.
* This may take some time; progress is indicated by emission of the Progress signal.
+ *
* @param get_path Functor which returns a JPEG2000 file path for a given frame (frames counted from 0).
* @param directory Directory in which to create MXF file.
* @param mxf_name Name of MXF file to create.
* @param progress Signal to inform of progress.
- * @param fps Frames per second.
- * @param length Length in frames.
- * @param width Width of images in pixels.
- * @param height Height of images in pixels.
+ * @param fps Video frames per second.
+ * @param intrinsic_duration Length of the whole asset in frames.
+ * @param size Size of images in pixels.
+ * @param encrypted true if asset should be encrypted.
*/
MonoPictureAsset (
boost::function<std::string (int)> get_path,
std::string mxf_name,
boost::signals2::signal<void (float)>* progress,
int fps,
- int length,
- int width,
- int height,
- bool encrypted
+ int intrinsic_duration,
++ bool encrypted,
+ Size size,
+ MXFMetadata const & metadata = MXFMetadata ()
);
- MonoPictureAsset (std::string directory, std::string mxf_name, int fps, int entry_point, int length);
-
+ /** Construct a MonoPictureAsset, reading the MXF from disk.
+ * @param directory Directory that the MXF is in.
+ * @param mxf_name The filename of the MXF within `directory'.
+ */
+ MonoPictureAsset (std::string directory, std::string mxf_name);
+
+ /** Construct a MonoPictureAsset for progressive writing using
+ * start_write() and a MonoPictureAssetWriter.
+ *
+ * @param directory Directory to put the MXF in.
+ * @param mxf_name Filename of the MXF within this directory.
+ * @param fps Video frames per second.
+ * @param size Size in pixels that the picture frames will be.
+ */
+ MonoPictureAsset (std::string directory, std::string mxf_name, int fps, Size size);
+
+ /** Start a progressive write to a MonoPictureAsset */
+ boost::shared_ptr<MonoPictureAssetWriter> start_write (bool, MXFMetadata const & metadata = MXFMetadata ());
+
boost::shared_ptr<const MonoPictureFrame> get_frame (int n) const;
- bool equals (boost::shared_ptr<const Asset> other, EqualityOptions opt, std::list<std::string>& notes) const;
+ bool equals (boost::shared_ptr<const Asset> other, EqualityOptions opt, boost::function<void (NoteType, std::string)> note) const;
private:
std::string path_from_list (int f, std::vector<std::string> const & files) const;
#include <list>
#include <boost/shared_ptr.hpp>
+ #include <boost/function.hpp>
+ #include <libxml++/libxml++.h>
#include "types.h"
+namespace xmlpp {
+ class Node;
+}
+
namespace libdcp {
class PictureAsset;
string directory,
string mxf_name,
boost::signals2::signal<void (float)>* progress,
- int fps, int intrinsic_duration,
+ int fps,
- int length,
- int start_frame,
- bool encrypted
++ int intrinsic_duration,
++ bool encrypted,
+ MXFMetadata const & metadata
)
- : MXFAsset (directory, mxf_name, progress, fps, 0, length, encrypted)
- : MXFAsset (directory, mxf_name, progress, fps, intrinsic_duration)
++ : MXFAsset (directory, mxf_name, progress, fps, intrinsic_duration, encrypted)
, _channels (files.size ())
, _sampling_rate (0)
- , _start_frame (start_frame)
{
assert (_channels);
string directory,
string mxf_name,
boost::signals2::signal<void (float)>* progress,
- int fps, int intrinsic_duration, int channels,
+ int fps,
- int length,
- int start_frame,
++ int intrinsic_duration,
+ int channels,
- bool encrypted
++ bool encrypted,
+ MXFMetadata const & metadata
)
- : MXFAsset (directory, mxf_name, progress, fps, 0, length, encrypted)
- : MXFAsset (directory, mxf_name, progress, fps, intrinsic_duration)
++ : MXFAsset (directory, mxf_name, progress, fps, intrinsic_duration, encrypted)
, _channels (channels)
, _sampling_rate (0)
- , _start_frame (start_frame)
{
assert (_channels);
_sampling_rate = desc.AudioSamplingRate.Numerator / desc.AudioSamplingRate.Denominator;
_channels = desc.ChannelCount;
- : MXFAsset (directory, mxf_name, 0, fps, 0)
+ _edit_rate = desc.EditRate.Numerator;
+ assert (desc.EditRate.Denominator == 1);
+ _intrinsic_duration = desc.ContainerDuration;
+ }
+
+ SoundAsset::SoundAsset (string directory, string mxf_name, int fps, int channels, int sampling_rate)
++ : MXFAsset (directory, mxf_name, 0, fps, 0, false)
+ , _channels (channels)
+ , _sampling_rate (sampling_rate)
+ {
+
}
string
offset += sample_size;
}
- if (ASDCP_FAILURE (mxf_writer.WriteFrame (frame_buffer, 0, 0))) {
+ if (ASDCP_FAILURE (mxf_writer.WriteFrame (frame_buffer, _encryption_context, 0))) {
- throw MiscError ("could not write audio MXF frame");
+ boost::throw_exception (MiscError ("could not write audio MXF frame"));
}
if (_progress) {
}
void
- SoundAsset::write_to_cpl (xmlpp::Element* parent) const
+ SoundAsset::write_to_cpl (xmlpp::Node* node) const
{
- xmlpp::Element* main_sound = parent->add_child("MainSound");
- main_sound->add_child("Id")->add_child_text("urn:uuid:" + _uuid);
- main_sound->add_child("AnnotationText")->add_child_text(_file_name);
- main_sound->add_child("EditRate")->add_child_text(boost::lexical_cast<string> (_fps) + " 1");
- main_sound->add_child("IntrinsicDuration")->add_child_text(boost::lexical_cast<string> (_length));
- main_sound->add_child("EntryPoint")->add_child_text("0");
- main_sound->add_child("Duration")->add_child_text(boost::lexical_cast<string> (_length));
+ xmlpp::Node* ms = node->add_child ("MainSound");
+ ms->add_child ("Id")->add_child_text ("urn:uuid:" + _uuid);
+ ms->add_child ("AnnotationText")->add_child_text (_file_name);
+ ms->add_child ("EditRate")->add_child_text (lexical_cast<string> (_edit_rate) + " 1");
+ ms->add_child ("IntrinsicDuration")->add_child_text (lexical_cast<string> (_intrinsic_duration));
+ ms->add_child ("EntryPoint")->add_child_text (lexical_cast<string> (_entry_point));
+ ms->add_child ("Duration")->add_child_text (lexical_cast<string> (_duration));
+ if (_encrypted) {
- main_sound->add_child("KeyId")->add_child_text("urn:uuid:" + _key_id);
++ ms->add_child("KeyId")->add_child_text("urn:uuid:" + _key_id);
+ }
}
bool
shared_ptr<const SoundFrame>
SoundAsset::get_frame (int n) const
{
- return shared_ptr<const SoundFrame> (new SoundFrame (path().string(), n + _entry_point));
+ /* XXX: should add on entry point here? */
+ return shared_ptr<const SoundFrame> (new SoundFrame (path().string(), n));
+ }
+
+ shared_ptr<SoundAssetWriter>
+ SoundAsset::start_write (MXFMetadata const & metadata)
+ {
+ /* XXX: can't we use a shared_ptr here? */
+ return shared_ptr<SoundAssetWriter> (new SoundAssetWriter (this, metadata));
+ }
+
+ struct SoundAssetWriter::ASDCPState
+ {
+ ASDCP::PCM::MXFWriter mxf_writer;
+ ASDCP::PCM::FrameBuffer frame_buffer;
+ ASDCP::WriterInfo writer_info;
+ ASDCP::PCM::AudioDescriptor audio_desc;
+ };
+
+ SoundAssetWriter::SoundAssetWriter (SoundAsset* a, MXFMetadata const & m)
+ : _state (new SoundAssetWriter::ASDCPState)
+ , _asset (a)
+ , _finalized (false)
+ , _frames_written (0)
+ , _frame_buffer_offset (0)
+ , _metadata (m)
+ {
+ /* 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());
+
- MXFAsset::fill_writer_info (&_state->writer_info, _asset->uuid (), _metadata);
++ _asset->fill_writer_info (&_state->writer_info, _asset->uuid (), _metadata);
+
+ if (ASDCP_FAILURE (_state->mxf_writer.OpenWrite (_asset->path().string().c_str(), _state->writer_info, _state->audio_desc))) {
+ boost::throw_exception (FileError ("could not open audio MXF for writing", _asset->path().string()));
+ }
+ }
+
+ 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 ()
+ {
+ if (ASDCP_FAILURE (_state->mxf_writer.WriteFrame (_state->frame_buffer, 0, 0))) {
+ boost::throw_exception (MiscError ("could not write audio MXF frame"));
+ }
+
+ ++_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";
+}
* @param mxf_name Name of MXF file to create.
* @param progress Signal to inform of progress.
* @param fps Frames per second.
+ * @param length Length in frames.
+ * @param start_frame Frame in the source to start writing from.
+ * @param intrinsic_duration Length of the whole asset in frames.
+ * @param encrypted true if asset should be encrypted.
+ * Note that this is different to entry_point in that the asset will contain no data before start_frame.
*/
SoundAsset (
std::vector<std::string> const & files,
std::string mxf_name,
boost::signals2::signal<void (float)>* progress,
int fps,
- int length,
- int start_frame,
- bool encrypted
+ int intrinsic_duration,
++ bool encrypted,
+ MXFMetadata const & metadata = MXFMetadata ()
);
/** Construct a SoundAsset, generating the MXF from some WAV files.
* @param mxf_name Name of MXF file to create.
* @param progress Signal to inform of progress.
* @param fps Frames per second.
- * @param length Length in frames.
- * @param start_frame Frame in the source to start writing from.
+ * @param intrinsic_duration Length of the whole asset in frames.
* @param channels Number of audio channels.
+ * @param encrypted true if asset should be encrypted.
*/
SoundAsset (
boost::function<std::string (Channel)> get_path,
std::string mxf_name,
boost::signals2::signal<void (float)>* progress,
int fps,
- int length,
- int start_frame,
+ int intrinsic_duration,
int channels,
- bool encrypted
++ bool encrypted,
+ MXFMetadata const & metadata = MXFMetadata ()
+ );
+
+ SoundAsset (
+ std::string directory,
+ std::string mxf_name
);
SoundAsset (
}
private:
-
- void construct (boost::function<std::string (Channel)> get_path);
+ std::string key_type () const;
+ void construct (boost::function<std::string (Channel)> get_path, MXFMetadata const &);
std::string path_from_channel (Channel channel, std::vector<std::string> const & files);
/** Number of channels in the asset */
#include <fstream>
#include <boost/lexical_cast.hpp>
#include <boost/algorithm/string.hpp>
+#include <libxml++/nodes/element.h>
#include "subtitle_asset.h"
#include "util.h"
+ #include "xml.h"
using std::string;
using std::list;
#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 "KM_util.h"
#include "KM_fileio.h"
#include "AS_DCP.h"
#include "exceptions.h"
#include "types.h"
#include "argb_frame.h"
- #include "lut.h"
+#include "certificates.h"
+ #include "gamma_lut.h"
using std::string;
+using std::cout;
using std::stringstream;
using std::min;
using std::max;
+using std::list;
using boost::shared_ptr;
+ using boost::lexical_cast;
using namespace libdcp;
/** Create a UUID.
return true;
}
+void
+libdcp::init ()
+{
+ if (xmlSecInit() < 0) {
+ throw MiscError ("could not initialise xmlsec");
+ }
+
+#ifdef XMLSEC_CRYPTO_DYNAMIC_LOADING
+ if (xmlSecCryptoDLLoadLibrary (BAD_CAST XMLSEC_CRYPTO) < 0) {
+ throw MiscError ("unable to load default xmlsec-crypto library");
+ }
+#endif
+
+ if (xmlSecCryptoAppInit (0) < 0) {
+ throw MiscError ("could not initialise crypto library");
+ }
+
+ if (xmlSecCryptoInit() < 0) {
+ throw MiscError ("could not initialise xmlsec-crypto");
+ }
+}
+
+void
+libdcp::add_signature_value (xmlpp::Element* parent, CertificateChain const & certificates, string const & signer_key, string const & ns)
+{
+ parent->add_child("SignatureValue", ns);
+
+ xmlpp::Element* key_info = parent->add_child("KeyInfo", ns);
+ list<shared_ptr<Certificate> > c = certificates.leaf_to_root ();
+ for (list<shared_ptr<Certificate> >::iterator i = c.begin(); i != c.end(); ++i) {
+ xmlpp::Element* data = key_info->add_child("X509Data", ns);
+
+ {
+ xmlpp::Element* serial = data->add_child("X509IssuerSerial", ns);
+ serial->add_child("X509IssuerName", ns)->add_child_text(
+ Certificate::name_for_xml ((*i)->issuer())
+ );
+ serial->add_child("X509SerialNumber", ns)->add_child_text((*i)->serial());
+ }
+
+ data->add_child("X509Certificate", ns)->add_child_text((*i)->certificate());
+ }
+
+ xmlSecKeysMngrPtr keys_manager = xmlSecKeysMngrCreate();
+ if (!keys_manager) {
+ throw MiscError ("could not create keys manager");
+ }
+ if (xmlSecCryptoAppDefaultKeysMngrInit (keys_manager) < 0) {
+ throw MiscError ("could not initialise keys manager");
+ }
+
+ xmlSecKeyPtr const key = xmlSecCryptoAppKeyLoad (signer_key.c_str(), xmlSecKeyDataFormatPem, 0, 0, 0);
+ if (key == 0) {
+ throw MiscError ("could not load signer key");
+ }
+
+ if (xmlSecCryptoAppDefaultKeysMngrAdoptKey (keys_manager, key) < 0) {
+ xmlSecKeyDestroy (key);
+ throw MiscError ("could not use signer key");
+ }
+
+ xmlSecDSigCtx signature_context;
+
+ if (xmlSecDSigCtxInitialize (&signature_context, keys_manager) < 0) {
+ throw MiscError ("could not initialise XMLSEC context");
+ }
+
+ if (xmlSecDSigCtxSign (&signature_context, parent->cobj()) < 0) {
+ throw MiscError ("could not sign");
+ }
+
+ xmlSecDSigCtxFinalize (&signature_context);
+ xmlSecKeysMngrDestroy (keys_manager);
+}
+
+
+void
+libdcp::add_signer (xmlpp::Element* parent, CertificateChain const & certificates, string const & ns)
+{
+ 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 (
+ Certificate::name_for_xml (certificates.leaf()->issuer())
+ );
+ serial_element->add_child("X509SerialNumber", ns)->add_child_text (
+ certificates.leaf()->serial()
+ );
+ }
+
+ data->add_child("X509SubjectName", ns)->add_child_text (Certificate::name_for_xml (certificates.leaf()->subject()));
+ }
+}
+
+void
+libdcp::sign (xmlpp::Element* parent, CertificateChain const & certificates, string const & signer_key)
+{
+ add_signer (parent, certificates, "dsig");
+
+ 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");
+ signed_info->add_child("SignatureMethod", "dsig")->set_attribute("Algorithm", "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256");
+ {
+ xmlpp::Element* reference = signed_info->add_child("Reference", "dsig");
+ reference->set_attribute ("URI", "");
+ {
+ xmlpp::Element* transforms = reference->add_child("Transforms", "dsig");
+ transforms->add_child("Transform", "dsig")->set_attribute (
+ "Algorithm", "http://www.w3.org/2000/09/xmldsig#enveloped-signature"
+ );
+ }
+ reference->add_child("DigestMethod", "dsig")->set_attribute("Algorithm", "http://www.w3.org/2000/09/xmldsig#sha1");
+ /* This will be filled in by the signing later */
+ reference->add_child("DigestValue", "dsig");
+ }
+ }
+
+ add_signature_value (signature, certificates, signer_key, "dsig");
+}
+
+ bool libdcp::operator== (libdcp::Size const & a, libdcp::Size const & b)
+ {
+ return (a.width == b.width && a.height == b.height);
+ }
+
+ bool libdcp::operator!= (libdcp::Size const & a, libdcp::Size const & b)
+ {
+ return !(a == b);
+ }
+
namespace libdcp {
class ARGBFrame;
+class CertificateChain;
+ class GammaLUT;
+ class XYZsRGBLUT;
+
+ struct Size {
+ Size ()
+ : width (0)
+ , height (0)
+ {}
+
+ Size (int w, int h)
+ : width (w)
+ , height (h)
+ {}
+ int width;
+ int height;
+ };
+
+ extern bool operator== (Size const & a, Size const & b);
+ extern bool operator!= (Size const & a, Size const & b);
+
extern std::string make_uuid ();
extern std::string make_digest (std::string filename);
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);
extern opj_image_t* decompress_j2k (uint8_t* data, int64_t size, int reduce);
- extern boost::shared_ptr<ARGBFrame> xyz_to_rgb (opj_image_t* xyz_frame);
+ extern boost::shared_ptr<ARGBFrame> xyz_to_rgb (opj_image_t* xyz_frame, boost::shared_ptr<const GammaLUT>, boost::shared_ptr<const GammaLUT>);
+extern void init ();
+
+extern void sign (xmlpp::Element* parent, CertificateChain const & certificates, std::string const & signer_key);
+extern void add_signature_value (xmlpp::Element* parent, CertificateChain const & certificates, std::string const & signer_key, std::string const & ns);
+extern void add_signer (xmlpp::Element* parent, CertificateChain const & certificates, std::string const & ns);
+
}
+
+ #endif
obj.name = 'libdcp'
obj.target = 'dcp'
obj.export_includes = ['.']
- obj.uselib = 'BOOST_FILESYSTEM BOOST_SIGNALS2 BOOST_DATETIME OPENSSL SIGC++ LIBXML++ OPENJPEG XMLSEC1'
- obj.uselib = 'BOOST_FILESYSTEM BOOST_SIGNALS2 OPENSSL SIGC++ LIBXML++ OPENJPEG CXML'
++ obj.uselib = 'BOOST_FILESYSTEM BOOST_SIGNALS2 BOOST_DATETIME OPENSSL SIGC++ LIBXML++ OPENJPEG CXML XMLSEC1'
obj.use = 'libkumu-libdcp libasdcp-libdcp'
obj.source = """
asset.cc
- asset_map.cc
+ dcp.cc
+ certificates.cc
- cpl_file.cc
+ crypt_chain.cc
- dcp.cc
+ cpl.cc
dcp_time.cc
- lut.cc
+ gamma_lut.cc
metadata.cc
mxf_asset.cc
picture_asset.cc
headers = """
asset.h
+ certificates.h
+ cpl.h
+ crypt_chain.h
dcp.h
dcp_time.h
exceptions.h
#include "picture_asset.h"
#include "sound_asset.h"
#include "reel.h"
+#include "certificates.h"
+#include "crypt_chain.h"
+ #include "gamma_lut.h"
+ #include "cpl.h"
#define BOOST_TEST_DYN_LINK
#define BOOST_TEST_MODULE libdcp_test
BOOST_AUTO_TEST_CASE (dcp_test)
{
+ libdcp::init ();
+
Kumu::libdcp_test = true;
- libdcp::Metadata* t = libdcp::Metadata::instance ();
- t->issuer = "OpenDCP 0.0.25";
- t->creator = "OpenDCP 0.0.25";
- t->company_name = "OpenDCP";
- t->product_name = "OpenDCP";
- t->product_version = "0.0.25";
- t->issue_date = "2012-07-17T04:45:18+00:00";
+ libdcp::XMLMetadata xml_meta;
+ xml_meta.issuer = "OpenDCP 0.0.25";
+ xml_meta.creator = "OpenDCP 0.0.25";
+ xml_meta.issue_date = "2012-07-17T04:45:18+00:00";
+ libdcp::MXFMetadata mxf_meta;
+ mxf_meta.company_name = "OpenDCP";
+ mxf_meta.product_name = "OpenDCP";
+ mxf_meta.product_version = "0.0.25";
boost::filesystem::remove_all ("build/test/foo");
boost::filesystem::create_directories ("build/test/foo");
libdcp::DCP d ("build/test/foo");
&d.Progress,
24,
24,
- 32,
- 32,
- false
+ libdcp::Size (32, 32),
++ false,
+ mxf_meta
));
shared_ptr<libdcp::SoundAsset> ms (new libdcp::SoundAsset (
&(d.Progress),
24,
24,
- false
+ 0,
+ 2,
+ 2,
++ false,
+ mxf_meta
));
cpl->add_reel (shared_ptr<libdcp::Reel> (new libdcp::Reel (mp, ms, shared_ptr<libdcp::SubtitleAsset> ())));
vector<string> p;
p.push_back ("frobozz");
- BOOST_CHECK_THROW (new libdcp::MonoPictureAsset (p, "build/test/bar", "video.mxf", &d.Progress, 24, 24, 32, 32, false), libdcp::FileError);
- BOOST_CHECK_THROW (new libdcp::SoundAsset (p, "build/test/bar", "audio.mxf", &d.Progress, 24, 24, 0, false), libdcp::FileError);
- BOOST_CHECK_THROW (new libdcp::MonoPictureAsset (p, "build/test/bar", "video.mxf", &d.Progress, 24, 24, libdcp::Size (32, 32)), libdcp::FileError);
- BOOST_CHECK_THROW (new libdcp::SoundAsset (p, "build/test/bar", "audio.mxf", &d.Progress, 24, 24), libdcp::FileError);
++ BOOST_CHECK_THROW (new libdcp::MonoPictureAsset (p, "build/test/bar", "video.mxf", &d.Progress, 24, 24, false, libdcp::Size (32, 32)), libdcp::FileError);
++ BOOST_CHECK_THROW (new libdcp::SoundAsset (p, "build/test/bar", "audio.mxf", &d.Progress, 24, 24, false), libdcp::FileError);
}
BOOST_AUTO_TEST_CASE (read_dcp)
}
+BOOST_AUTO_TEST_CASE (encryption)
+{
+ Kumu::libdcp_test = true;
+
+ libdcp::Metadata* t = libdcp::Metadata::instance ();
+ t->issuer = "OpenDCP 0.0.25";
+ t->creator = "OpenDCP 0.0.25";
+ t->company_name = "OpenDCP";
+ t->product_name = "OpenDCP";
+ t->product_version = "0.0.25";
+ t->issue_date = "2012-07-17T04:45:18+00:00";
+ boost::filesystem::remove_all ("build/test/bar");
+ boost::filesystem::create_directories ("build/test/bar");
+ libdcp::DCP d ("build/test/bar");
+
+ libdcp::CertificateChain chain;
+ chain.add (shared_ptr<libdcp::Certificate> (new libdcp::Certificate ("test/data/ca.self-signed.pem")));
+ chain.add (shared_ptr<libdcp::Certificate> (new libdcp::Certificate ("test/data/intermediate.signed.pem")));
+ chain.add (shared_ptr<libdcp::Certificate> (new libdcp::Certificate ("test/data/leaf.signed.pem")));
+
+ shared_ptr<libdcp::Encryption> crypt (
+ new libdcp::Encryption (
+ chain,
+ "test/data/signer.key"
+ )
+ );
+
+ shared_ptr<libdcp::CPL> cpl (new libdcp::CPL ("build/test/bar", "A Test DCP", libdcp::FEATURE, 24, 24));
+
+ shared_ptr<libdcp::MonoPictureAsset> mp (new libdcp::MonoPictureAsset (
+ j2c,
+ "build/test/bar",
+ "video.mxf",
+ &d.Progress,
+ 24,
+ 24,
+ 32,
+ 32,
+ true
+ ));
+
+ shared_ptr<libdcp::SoundAsset> ms (new libdcp::SoundAsset (
+ wav,
+ "build/test/bar",
+ "audio.mxf",
+ &(d.Progress),
+ 24,
+ 24,
+ 0,
+ 2,
+ true
+ ));
+
+ cpl->add_reel (shared_ptr<libdcp::Reel> (new libdcp::Reel (mp, ms, shared_ptr<libdcp::SubtitleAsset> ())));
+ d.add_cpl (cpl);
+
+ d.write_xml (crypt);
+
+ shared_ptr<xmlpp::Document> kdm = cpl->make_kdm (
+ crypt->certificates,
+ crypt->signer_key,
+ crypt->certificates.leaf(),
+ boost::posix_time::time_from_string ("2013-01-01 00:00:00"),
+ boost::posix_time::time_from_string ("2013-01-08 00:00:00")
+ );
+
+ kdm->write_to_file_formatted ("build/test/bar.kdm.xml", "UTF-8");
+}
+
+BOOST_AUTO_TEST_CASE (certificates)
+{
+ libdcp::CertificateChain c;
+
+ c.add (shared_ptr<libdcp::Certificate> (new libdcp::Certificate ("test/data/ca.self-signed.pem")));
+ c.add (shared_ptr<libdcp::Certificate> (new libdcp::Certificate ("test/data/intermediate.signed.pem")));
+ c.add (shared_ptr<libdcp::Certificate> (new libdcp::Certificate ("test/data/leaf.signed.pem")));
+
+ BOOST_CHECK_EQUAL (
+ c.root()->issuer(),
+ "/O=example.org/OU=example.org/CN=.smpte-430-2.ROOT.NOT_FOR_PRODUCTION/dnQualifier=rTeK7x+nopFkyphflooz6p2ZM7A="
+ );
+
+ BOOST_CHECK_EQUAL (
+ libdcp::Certificate::name_for_xml (c.root()->issuer()),
+ "dnQualifier=rTeK7x\\+nopFkyphflooz6p2ZM7A=,CN=.smpte-430-2.ROOT.NOT_FOR_PRODUCTION,OU=example.org,O=example.org"
+ );
+
+ BOOST_CHECK_EQUAL (c.root()->serial(), "5");
+
+ BOOST_CHECK_EQUAL (
+ libdcp::Certificate::name_for_xml (c.root()->subject()),
+ "dnQualifier=rTeK7x\\+nopFkyphflooz6p2ZM7A=,CN=.smpte-430-2.ROOT.NOT_FOR_PRODUCTION,OU=example.org,O=example.org"
+ );
+}
+
+BOOST_AUTO_TEST_CASE (crypt_chain)
+{
+ boost::filesystem::remove_all ("build/test/crypt");
+ boost::filesystem::create_directory ("build/test/crypt");
+ libdcp::make_crypt_chain ("build/test/crypt");
+}
++
+ BOOST_AUTO_TEST_CASE (recovery)
+ {
+ Kumu::libdcp_test = true;
+
+ string const picture = "test/data/32x32_red_square.j2c";
+ int const size = boost::filesystem::file_size (picture);
+ uint8_t* data = new uint8_t[size];
+ {
+ FILE* f = fopen (picture.c_str(), "rb");
+ BOOST_CHECK (f);
+ fread (data, 1, size, f);
+ fclose (f);
+ }
+
+ #ifdef LIBDCP_POSIX
+ /* XXX: fix this posix-only stuff */
+ Kumu::ResetTestRNG ();
+ #endif
+
+ boost::filesystem::remove_all ("build/test/baz");
+ boost::filesystem::create_directories ("build/test/baz");
+ shared_ptr<libdcp::MonoPictureAsset> mp (new libdcp::MonoPictureAsset ("build/test/baz", "video1.mxf", 24, libdcp::Size (32, 32)));
+ shared_ptr<libdcp::MonoPictureAssetWriter> writer = mp->start_write (false);
+
+ int written_size = 0;
+ for (int i = 0; i < 24; ++i) {
+ libdcp::FrameInfo info = writer->write (data, size);
+ written_size = info.size;
+ }
+
+ writer->finalize ();
+ writer.reset ();
+
+ boost::filesystem::copy_file ("build/test/baz/video1.mxf", "build/test/baz/video2.mxf");
+ boost::filesystem::resize_file ("build/test/baz/video2.mxf", 16384 + 353 * 11);
+
+ {
+ FILE* f = fopen ("build/test/baz/video2.mxf", "r+");
+ rewind (f);
+ char zeros[256];
+ memset (zeros, 0, 256);
+ fwrite (zeros, 1, 256, f);
+ fclose (f);
+ }
+
+ #ifdef LIBDCP_POSIX
+ Kumu::ResetTestRNG ();
+ #endif
+
+ mp.reset (new libdcp::MonoPictureAsset ("build/test/baz", "video2.mxf", 24, libdcp::Size (32, 32)));
+ writer = mp->start_write (true);
+
+ writer->write (data, size);
+
+ for (int i = 1; i < 4; ++i) {
+ writer->fake_write (written_size);
+ }
+
+ for (int i = 4; i < 24; ++i) {
+ writer->write (data, size);
+ }
+
+ writer->finalize ();
+ }
else:
conf.env.append_value('CXXFLAGS', '-DLIBDCP_POSIX')
+ if not conf.options.osx:
+ conf.env.append_value('CXXFLAGS', ['-Wno-unused-result'])
+
conf.check_cfg(package = 'openssl', args = '--cflags --libs', uselib_store = 'OPENSSL', mandatory = True)
conf.check_cfg(package = 'libxml++-2.6', args = '--cflags --libs', uselib_store = 'LIBXML++', mandatory = True)
- openjpeg_fragment = """
- #include <stdio.h>\n
- #include <openjpeg.h>\n
- int main () {\n
- void* p = (void *) opj_image_create;\n
- return 0;\n
- }
- """
- if conf.options.static_openjpeg:
- conf.check_cc(fragment = openjpeg_fragment, msg = 'Checking for library openjpeg', stlib = 'openjpeg', uselib_store = 'OPENJPEG')
+ conf.check_cfg(package = 'xmlsec1', args = '--cflags --libs', uselib_store = 'XMLSEC1', mandatory = True)
+ # Remove erroneous escaping of quotes from xmlsec1 defines
+ conf.env.DEFINES_XMLSEC1 = [f.replace('\\', '') for f in conf.env.DEFINES_XMLSEC1]
+
+ if conf.options.static:
+ conf.check_cc(fragment = """
+ #include <stdio.h>\n
+ #include <openjpeg.h>\n
+ int main () {\n
+ void* p = (void *) opj_image_create;\n
+ return 0;\n
+ }
+ """,
+ msg = 'Checking for library openjpeg', stlib = 'openjpeg', uselib_store = 'OPENJPEG', mandatory = True)
+
+ conf.env.HAVE_CXML = 1
+ conf.env.STLIB_CXML = ['cxml']
else:
- conf.check_cc(fragment = openjpeg_fragment, msg = 'Checking for library openjpeg', lib = 'openjpeg', uselib_store = 'OPENJPEG')
+ conf.check_cfg(package = 'libopenjpeg', args = '--cflags --libs', uselib_store = 'OPENJPEG', mandatory = True)
+ conf.check_cfg(package = 'libcxml', args = '--cflags --libs', uselib_store = 'CXML', mandatory = True)
if conf.options.target_windows:
boost_lib_suffix = '-mt'
msg = 'Checking for boost signals2 library',
uselib_store = 'BOOST_SIGNALS2')
- lut.make_luts()
-
+ conf.check_cxx(fragment = """
+ #include <boost/date_time.hpp>\n
+ int main() { boost::gregorian::day_clock::local_day(); }\n
+ """,
+ msg = 'Checking for boost datetime library',
+ libpath = '/usr/local/lib',
+ lib = ['boost_date_time%s' % boost_lib_suffix, 'boost_system%s' % boost_lib_suffix],
+ uselib_store = 'BOOST_DATETIME')
+
conf.recurse('test')
conf.recurse('asdcplib')