}
void
- Asset::write_to_pkl (xmlpp::Node* node) const
-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> (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 ());
++ asset->add_child("Type")->add_child_text (pkl_type (standard));
}
void
#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 {
- class 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_pkl (xmlpp::Node* node) const;
- 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 () const = 0;
++ 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;
};
}
--- /dev/null
+/*
+ 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
}
/* 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 ());
}
}
}
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);
++ }
++}
++
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 () const {
- return "text/xml";
- }
++ 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;
};
}
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);
++ (*i)->write_to_pkl (asset_list, standard);
}
if (signer) {
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;
- if (standard == INTEROP) {
++ switch (standard) {
++ case INTEROP:
p /= "VOLINDEX";
-- } else {
++ break;
++ case SMPTE:
p /= "VOLINDEX.xml";
++ break;
++ default:
++ assert (false);
}
xmlpp::Document doc;
- xmlpp::Element* root = doc.create_root_node ("VolumeIndex", "http://www.smpte-ra.org/schemas/429-9/2007/AM");
+ 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;
- if (standard == INTEROP) {
++
++ switch (standard) {
++ case INTEROP:
p /= "ASSETMAP";
-- } else {
++ break;
++ case SMPTE:
p /= "ASSETMAP.xml";
++ break;
++ default:
++ assert (false);
}
xmlpp::Document doc;
xmlpp::Element* root;
- if (standard == INTEROP) {
- 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 (standard == INTEROP) {
- 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");
--- /dev/null
+/*
+ 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/asset.cc
+ * @brief Parent class for assets of DCPs made up of MXF files.
+ */
+
+#include "AS_DCP.h"
+#include "KM_prng.h"
+#include "KM_util.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;
+using std::pair;
+using boost::shared_ptr;
+using boost::lexical_cast;
+using boost::dynamic_pointer_cast;
+using namespace dcp;
+
+MXF::MXF (Fraction edit_rate)
+ : Content (edit_rate)
+ , _encryption_context (0)
+ , _decryption_context (0)
+{
+
+}
+
+MXF::MXF (boost::filesystem::path file)
+ : Content (file)
+ , _encryption_context (0)
+ , _decryption_context (0)
+{
+
+}
+
+MXF::~MXF ()
+{
+ delete _encryption_context;
+ delete _decryption_context;
+}
+
+void
+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 (standard == INTEROP) {
+ writer_info->LabelSetType = ASDCP::LS_MXF_INTEROP;
+ } else {
+ writer_info->LabelSetType = ASDCP::LS_MXF_SMPTE;
+ }
+ unsigned int c;
+ Kumu::hex2bin (_id.c_str(), writer_info->AssetUUID, Kumu::UUID_Length, &c);
+ assert (c == Kumu::UUID_Length);
+
+ if (_key) {
+ 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
+MXF::equals (shared_ptr<const Content> other, EqualityOptions opt, boost::function<void (NoteType, string)> note) const
+{
+ if (!Content::equals (other, opt, note)) {
+ return false;
+ }
+
+ 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 != other_mxf->file ()) {
+ note (ERROR, "MXF names differ");
+ if (!opt.mxf_names_can_differ) {
+ return false;
+ }
+ }
+
+ 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
+MXF::set_key (Key key)
+{
+ _key = key;
+
+ if (_key_id.empty ()) {
+ /* No key ID so far; we now need one */
+ _key_id = make_uuid ();
+ }
+
+ _decryption_context = new ASDCP::AESDecContext;
+ if (ASDCP_FAILURE (_decryption_context->InitKey (_key->value ()))) {
+ throw MiscError ("could not set up decryption context");
+ }
+
+ _encryption_context = new ASDCP::AESEncContext;
+ if (ASDCP_FAILURE (_encryption_context->InitKey (_key->value ()))) {
+ throw MiscError ("could not set up encryption context");
+ }
+
+ uint8_t cbc_buffer[ASDCP::CBC_BLOCK_SIZE];
+
+ Kumu::FortunaRNG rng;
+ if (ASDCP_FAILURE (_encryption_context->SetIVec (rng.FillRandom (cbc_buffer, ASDCP::CBC_BLOCK_SIZE)))) {
+ 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);
++ }
++}
--- /dev/null
- std::string pkl_type () const {
- return "application/x-smpte-mxf";
- }
-
+/*
+ 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_MXF_H
+#define LIBDCP_MXF_H
+
+#include "content.h"
+#include "key.h"
+#include "metadata.h"
+#include <boost/signals2.hpp>
+
+namespace ASDCP {
+ class AESEncContext;
+ class AESDecContext;
+}
+
+namespace dcp
+{
+
+class MXFMetadata;
+
+/** @class MXF
+ * @brief Parent class for classes which represent MXF files.
+ */
+class MXF : public Content
+{
+public:
+ MXF (Fraction edit_rate);
+ MXF (boost::filesystem::path file);
+ ~MXF ();
+
+ /** @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, 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;
+ }
+
+protected:
++ std::string pkl_type (Standard standard) const;
+ void read_writer_info (ASDCP::WriterInfo const &);
+
+ 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;
+};
+
+}
+
+#endif
--- /dev/null
+/*
+ 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
--- /dev/null
+/*
+ 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
--- /dev/null
-
+/*
+ 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_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;
+using std::ostream;
+using std::ofstream;
+using std::stringstream;
++using std::cout;
+using boost::shared_ptr;
+using boost::lexical_cast;
+using boost::optional;
+using namespace dcp;
+
+SubtitleContent::SubtitleContent (boost::filesystem::path file)
+ : Content (file)
+ , _need_sort (false)
+{
+ shared_ptr<cxml::Document> xml (new cxml::Document ("DCSubtitle"));
+ xml->read_file (file);
+
+ _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<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.
+ */
- load_font->set_attribute("URI", _load_font_nodes.front()->uri);
++
+ 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
+SubtitleContent::examine_font_nodes (
+ shared_ptr<const cxml::Node> xml,
+ list<shared_ptr<dcp::Font> > const & font_nodes,
+ ParseState& parse_state
+ )
+{
+ 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<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);
+ parse_state.subtitle_nodes.pop_back ();
+ }
+
+ examine_font_nodes (xml, (*i)->font_nodes, parse_state);
+ examine_text_nodes (xml, (*i)->text_nodes, parse_state);
+
+ parse_state.font_nodes.pop_back ();
+ }
+}
+
+void
+SubtitleContent::examine_text_nodes (
+ shared_ptr<const cxml::Node> xml,
+ list<shared_ptr<dcp::Text> > const & text_nodes,
+ ParseState& parse_state
+ )
+{
+ 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);
+ parse_state.text_nodes.pop_back ();
+ }
+}
+
+void
+SubtitleContent::maybe_add_subtitle (string text, ParseState const & parse_state)
+{
+ if (empty_or_white_space (text)) {
+ return;
+ }
+
+ if (parse_state.text_nodes.empty() || parse_state.subtitle_nodes.empty ()) {
+ return;
+ }
+
+ assert (!parse_state.text_nodes.empty ());
+ assert (!parse_state.subtitle_nodes.empty ());
+
+ 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<SubtitleString> (
+ new SubtitleString (
+ font_id_to_name (effective_font.id),
+ effective_font.italic.get(),
+ effective_font.color.get(),
+ effective_font.size,
+ effective_subtitle.in,
+ effective_subtitle.out,
+ effective_text.v_position,
+ effective_text.v_align,
+ text,
+ effective_font.effect ? effective_font.effect.get() : NONE,
+ effective_font.effect_color.get(),
+ effective_subtitle.fade_up_time,
+ effective_subtitle.fade_down_time
+ )
+ )
+ );
+}
+
+list<shared_ptr<SubtitleString> >
+SubtitleContent::subtitles_at (Time t) const
+{
+ 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);
+ }
+ }
+
+ return s;
+}
+
+std::string
+SubtitleContent::font_id_to_name (string id) const
+{
+ list<shared_ptr<dcp::LoadFont> >::const_iterator i = _load_font_nodes.begin();
+ while (i != _load_font_nodes.end() && (*i)->id != id) {
+ ++i;
+ }
+
+ if (i == _load_font_nodes.end ()) {
+ return "";
+ }
+
+ if ((*i)->uri == "arial.ttf") {
+ return "Arial";
+ }
+
+ return "";
+}
+
+void
+SubtitleContent::add (shared_ptr<SubtitleString> s)
+{
+ _subtitles.push_back (s);
+ _need_sort = true;
+}
+
+struct SubtitleSorter {
+ bool operator() (shared_ptr<SubtitleString> a, shared_ptr<SubtitleString> b) {
+ if (a->in() != b->in()) {
+ return a->in() < b->in();
+ }
+ return a->v_position() < b->v_position();
+ }
+};
+
+void
+SubtitleContent::write_xml () const
+{
+ 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
+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 (_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);
+
+ if (_load_font_nodes.size() > 1) {
+ boost::throw_exception (MiscError ("multiple LoadFont nodes not supported"));
+ }
+
+ if (!_load_font_nodes.empty ()) {
+ xmlpp::Element* load_font = root->add_child("LoadFont");
+ load_font->set_attribute("Id", _load_font_nodes.front()->id);
++ load_font->set_attribute("URI", _load_font_nodes.front()->uri);
+ }
+
+ list<shared_ptr<SubtitleString> > sorted = _subtitles;
+ if (_need_sort) {
+ sorted.sort (SubtitleSorter ());
+ }
+
+ /* XXX: multiple fonts not supported */
+ /* XXX: script, underlined, weight not supported */
+
+ bool italic = false;
+ Color color;
+ int size = 0;
+ Effect effect = NONE;
+ Color effect_color;
+ int spot_number = 1;
+ Time last_in;
+ Time last_out;
+ Time last_fade_up_time;
+ Time last_fade_down_time;
+
+ xmlpp::Element* font = 0;
+ xmlpp::Element* subtitle = 0;
+
+ 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
+ that seems hard.
+ */
+
+ bool const font_changed =
+ italic != (*i)->italic() ||
+ color != (*i)->color() ||
+ size != (*i)->size() ||
+ effect != (*i)->effect() ||
+ effect_color != (*i)->effect_color();
+
+ if (font_changed) {
+ italic = (*i)->italic ();
+ color = (*i)->color ();
+ size = (*i)->size ();
+ effect = (*i)->effect ();
+ effect_color = (*i)->effect_color ();
+ }
+
+ if (!font || font_changed) {
+ font = root->add_child ("Font");
+ string id = "theFontId";
+ if (!_load_font_nodes.empty()) {
+ id = _load_font_nodes.front()->id;
+ }
+ font->set_attribute ("Id", id);
+ font->set_attribute ("Italic", italic ? "yes" : "no");
+ font->set_attribute ("Color", color.to_argb_string());
+ font->set_attribute ("Size", lexical_cast<string> (size));
+ font->set_attribute ("Effect", effect_to_string (effect));
+ font->set_attribute ("EffectColor", effect_color.to_argb_string());
+ font->set_attribute ("Script", "normal");
+ font->set_attribute ("Underlined", "no");
+ font->set_attribute ("Weight", "normal");
+ }
+
+ if (!subtitle || font_changed ||
+ (last_in != (*i)->in() ||
+ last_out != (*i)->out() ||
+ last_fade_up_time != (*i)->fade_up_time() ||
+ last_fade_down_time != (*i)->fade_down_time()
+ )) {
+
+ subtitle = font->add_child ("Subtitle");
+ subtitle->set_attribute ("SpotNumber", lexical_cast<string> (spot_number++));
+ subtitle->set_attribute ("TimeIn", (*i)->in().to_string());
+ subtitle->set_attribute ("TimeOut", (*i)->out().to_string());
+ subtitle->set_attribute ("FadeUpTime", lexical_cast<string> ((*i)->fade_up_time().to_ticks()));
+ subtitle->set_attribute ("FadeDownTime", lexical_cast<string> ((*i)->fade_down_time().to_ticks()));
+
+ last_in = (*i)->in ();
+ last_out = (*i)->out ();
+ last_fade_up_time = (*i)->fade_up_time ();
+ last_fade_down_time = (*i)->fade_down_time ();
+ }
+
+ xmlpp::Element* text = subtitle->add_child ("Text");
+ text->set_attribute ("VAlign", valign_to_string ((*i)->v_align()));
+ text->set_attribute ("VPosition", lexical_cast<string> ((*i)->v_position()));
+ text->add_child_text ((*i)->text());
+ }
+
+ return doc.write_to_string_formatted ("UTF-8");
+}
+
--- /dev/null
- std::string pkl_type () const {
+/*
+ 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
mxf_meta.product_name = "OpenDCP";
mxf_meta.product_version = "0.0.25";
- /* We're making build/test/DCP/foo */
+ /* We're making build/test/foo */
- boost::filesystem::remove_all ("build/test/foo");
- boost::filesystem::create_directories ("build/test/foo");
- dcp::DCP d ("build/test/foo");
+ boost::filesystem::remove_all ("build/test/DCP/foo");
+ boost::filesystem::create_directories ("build/test/DCP/foo");
- libdcp::DCP d ("build/test/DCP/foo");
- shared_ptr<libdcp::CPL> cpl (new libdcp::CPL ("build/test/DCP/foo", "A Test DCP", libdcp::FEATURE, 24, 24));
++ dcp::DCP d ("build/test/DCP/foo");
+ shared_ptr<dcp::CPL> cpl (new dcp::CPL ("A Test DCP", dcp::FEATURE));
+ cpl->set_content_version_id ("urn:uri:81fb54df-e1bf-4647-8788-ea7ba154375b_2012-07-17T04:45:18+00:00");
+ cpl->set_content_version_label_text ("81fb54df-e1bf-4647-8788-ea7ba154375b_2012-07-17T04:45:18+00:00");
- shared_ptr<libdcp::MonoPictureAsset> mp (new libdcp::MonoPictureAsset ("build/test/DCP/foo", "video.mxf"));
- mp->set_progress (&d.Progress);
- mp->set_edit_rate (24);
- mp->set_intrinsic_duration (24);
- mp->set_duration (24);
- mp->set_size (libdcp::Size (32, 32));
+ shared_ptr<dcp::MonoPictureMXF> mp (new dcp::MonoPictureMXF (dcp::Fraction (24, 1)));
mp->set_metadata (mxf_meta);
- shared_ptr<dcp::PictureMXFWriter> picture_writer = mp->start_write ("build/test/foo/video.mxf", dcp::SMPTE, false);
- mp->create (j2c);
++ shared_ptr<dcp::PictureMXFWriter> picture_writer = mp->start_write ("build/test/DCP/foo/video.mxf", dcp::SMPTE, false);
+ dcp::File j2c ("test/data/32x32_red_square.j2c");
+ for (int i = 0; i < 24; ++i) {
+ picture_writer->write (j2c.data (), j2c.size ());
+ }
+ picture_writer->finalize ();
- shared_ptr<libdcp::SoundAsset> ms (new libdcp::SoundAsset ("build/test/DCP/foo", "audio.mxf"));
- ms->set_progress (&d.Progress);
- ms->set_edit_rate (24);
- ms->set_intrinsic_duration (24);
- ms->set_duration (24);
- ms->set_channels (2);
+ shared_ptr<dcp::SoundMXF> ms (new dcp::SoundMXF (dcp::Fraction (24, 1), 48000, 1));
ms->set_metadata (mxf_meta);
- shared_ptr<dcp::SoundMXFWriter> sound_writer = ms->start_write ("build/test/foo/audio.mxf", dcp::SMPTE);
- ms->create (wav);
++ shared_ptr<dcp::SoundMXFWriter> sound_writer = ms->start_write ("build/test/DCP/foo/audio.mxf", dcp::SMPTE);
+
+ SF_INFO info;
+ info.format = 0;
+ SNDFILE* sndfile = sf_open ("test/data/1s_24-bit_48k_silence.wav", SFM_READ, &info);
+ BOOST_CHECK (sndfile);
+ float buffer[4096*6];
+ float* channels[1];
+ channels[0] = buffer;
+ while (1) {
+ sf_count_t N = sf_readf_float (sndfile, buffer, 4096);
+ sound_writer->write (channels, N);
+ if (N < 4096) {
+ break;
+ }
+ }
+
+ sound_writer->finalize ();
- cpl->add_reel (shared_ptr<libdcp::Reel> (new libdcp::Reel (mp, ms, shared_ptr<libdcp::SubtitleAsset> ())));
- d.add_cpl (cpl);
+ cpl->add (shared_ptr<dcp::Reel> (
+ new dcp::Reel (
+ shared_ptr<dcp::ReelMonoPictureAsset> (new dcp::ReelMonoPictureAsset (mp, 0)),
+ shared_ptr<dcp::ReelSoundAsset> (new dcp::ReelSoundAsset (ms, 0)),
+ shared_ptr<dcp::ReelSubtitleAsset> ()
+ )
+ ));
+
+ d.add (cpl);
+ d.add (mp);
+ d.add (ms);
- d.write_xml (false, xml_meta);
+ d.write_xml (dcp::SMPTE, xml_meta);
- /* build/test/foo is checked against test/ref/DCP/foo by run-tests.sh */
+ /* build/test/DCP/foo is checked against test/ref/DCP/foo by run-tests.sh */
}
/** Decrypt an encrypted test DCP and check that its first frame is the same as the unencrypted version */
BOOST_AUTO_TEST_CASE (decryption_test)
{
- boost::filesystem::path plaintext_path = test_corpus;
+ boost::filesystem::path plaintext_path = private_test;
plaintext_path /= "TONEPLATES-SMPTE-PLAINTEXT_TST_F_XX-XX_ITL-TD_51-XX_2K_WOE_20111001_WOE_OV";
- libdcp::DCP plaintext (plaintext_path.string ());
+ dcp::DCP plaintext (plaintext_path.string ());
plaintext.read ();
BOOST_CHECK_EQUAL (plaintext.encrypted (), false);
- boost::filesystem::path encrypted_path = test_corpus;
+ boost::filesystem::path encrypted_path = private_test;
encrypted_path /= "TONEPLATES-SMPTE-ENCRYPTED_TST_F_XX-XX_ITL-TD_51-XX_2K_WOE_20111001_WOE_OV";
- libdcp::DCP encrypted (encrypted_path.string ());
+ dcp::DCP encrypted (encrypted_path.string ());
encrypted.read ();
BOOST_CHECK_EQUAL (encrypted.encrypted (), true);
boost::filesystem::remove_all ("build/test/DCP/bar");
boost::filesystem::create_directories ("build/test/DCP/bar");
- libdcp::DCP d ("build/test/DCP/bar");
+ dcp::DCP d ("build/test/DCP/bar");
- libdcp::CertificateChain chain;
- chain.add (shared_ptr<libdcp::Certificate> (new libdcp::Certificate (boost::filesystem::path ("test/ref/crypt/ca.self-signed.pem"))));
- chain.add (shared_ptr<libdcp::Certificate> (new libdcp::Certificate (boost::filesystem::path ("test/ref/crypt/intermediate.signed.pem"))));
- chain.add (shared_ptr<libdcp::Certificate> (new libdcp::Certificate (boost::filesystem::path ("test/ref/crypt/leaf.signed.pem"))));
+ /* Use test/ref/crypt so this test is repeatable */
- chain.add (shared_ptr<dcp::Certificate> (new dcp::Certificate (boost::filesystem::path ("build/test/signer/ca.self-signed.pem"))));
- chain.add (shared_ptr<dcp::Certificate> (new dcp::Certificate (boost::filesystem::path ("build/test/signer/intermediate.signed.pem"))));
- chain.add (shared_ptr<dcp::Certificate> (new dcp::Certificate (boost::filesystem::path ("build/test/signer/leaf.signed.pem"))));
+ dcp::CertificateChain chain;
++ chain.add (shared_ptr<dcp::Certificate> (new dcp::Certificate (boost::filesystem::path ("test/ref/crypt/ca.self-signed.pem"))));
++ chain.add (shared_ptr<dcp::Certificate> (new dcp::Certificate (boost::filesystem::path ("test/ref/crypt/intermediate.signed.pem"))));
++ chain.add (shared_ptr<dcp::Certificate> (new dcp::Certificate (boost::filesystem::path ("test/ref/crypt/leaf.signed.pem"))));
- shared_ptr<libdcp::Signer> signer (
- new libdcp::Signer (
+ shared_ptr<dcp::Signer> signer (
+ new dcp::Signer (
chain,
- "build/test/signer/leaf.key"
+ "test/ref/crypt/leaf.key"
)
);
int
main (int argc, char* argv[])
- try
{
- if (argc < 2) {
- cerr << "Syntax: " << argv[0] << " <dcp>\n";
- exit (EXIT_FAILURE);
- }
-
- DCP* dcp = new DCP (argv[1]);
- dcp->read ();
-
- list<shared_ptr<CPL> > cpls = dcp->cpls ();
- for (list<boost::shared_ptr<CPL> >::iterator i = cpls.begin(); i != cpls.end(); ++i) {
-
- list<shared_ptr<Reel> > reels = (*i)->reels ();
- for (list<shared_ptr<Reel> >::iterator j = reels.begin(); j != reels.end(); ++j) {
-
- if ((*j)->main_subtitle()) {
- (*j)->main_subtitle()->subtitle_content()->write_xml ();
+ try {
+ if (argc < 2) {
+ cerr << "Syntax: " << argv[0] << " <dcp>\n";
+ exit (EXIT_FAILURE);
+ }
+
+ DCP* dcp = new DCP (argv[1]);
- dcp->read (false);
++ dcp->read ();
+
+ list<shared_ptr<CPL> > cpls = dcp->cpls ();
+ for (list<boost::shared_ptr<CPL> >::iterator i = cpls.begin(); i != cpls.end(); ++i) {
+
+ list<shared_ptr<Reel> > reels = (*i)->reels ();
+ for (list<shared_ptr<Reel> >::iterator j = reels.begin(); j != reels.end(); ++j) {
+
+ if ((*j)->main_subtitle()) {
- (*j)->main_subtitle()->write_xml ();
++ (*j)->main_subtitle()->subtitle_content()->write_xml ();
+ }
}
}
}
*/
- extern std::string test_corpus;
-extern boost::filesystem::path j2c (int);
-extern boost::filesystem::path wav (libdcp::Channel);
+ extern std::string private_test;
else:
boost_lib_suffix = ''
- bld(source = 'libdcp.pc.in',
- version = VERSION,
- includedir = '%s/include' % bld.env.PREFIX,
- libs = "-L${libdir} -ldcp -lasdcp-libdcp -lkumu-libdcp -lcxml -lboost_system%s" % boost_lib_suffix,
- install_path = '${LIBDIR}/pkgconfig')
+ bld(source='libdcp%s.pc.in' % bld.env.API_VERSION,
+ version=VERSION,
+ includedir='%s/include/libdcp%s' % (bld.env.PREFIX, bld.env.API_VERSION),
- libs="-L${libdir} -ldcp%s -lasdcp-libdcp%s -lkumu-libdcp%s -lboost_system%s" % (API_VERSION, API_VERSION, API_VERSION, boost_lib_suffix),
++ libs="-L${libdir} -ldcp%s -lasdcp-libdcp%s -lkumu-libdcp%s -lcxml -lboost_system%s" % (API_VERSION, API_VERSION, API_VERSION, boost_lib_suffix),
+ install_path='${LIBDIR}/pkgconfig')
bld.recurse('src')
bld.recurse('tools')