summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorCarl Hetherington <cth@carlh.net>2013-06-18 15:07:41 +0100
committerCarl Hetherington <cth@carlh.net>2013-06-18 15:07:41 +0100
commitc2bac22380bea453665a24c6f39200a977771daf (patch)
treea464d94724318b81484eb3011fa0a946205550c3 /src
parent564f68cb2c258e61c1e70950c9d036859ea8619a (diff)
parent59617eb2230e47b59882c4f9ca6092ce05f53cf1 (diff)
Merge master.
Diffstat (limited to 'src')
-rw-r--r--src/argb_frame.cc14
-rw-r--r--src/argb_frame.h14
-rw-r--r--src/asset.cc58
-rw-r--r--src/asset.h61
-rw-r--r--src/cpl.cc460
-rw-r--r--src/cpl.h111
-rw-r--r--src/cpl_file.cc148
-rw-r--r--src/dcp.cc558
-rw-r--r--src/dcp.h102
-rw-r--r--src/dcp_time.cc2
-rw-r--r--src/gamma_lut.cc16
-rw-r--r--src/gamma_lut.h13
-rw-r--r--src/lut.h (renamed from src/test_mode.h)46
-rw-r--r--src/lut_cache.h28
-rw-r--r--src/metadata.cc30
-rw-r--r--src/metadata.h29
-rw-r--r--src/mxf_asset.cc54
-rw-r--r--src/mxf_asset.h47
-rw-r--r--src/parse/asset_map.cc (renamed from src/asset_map.cc)40
-rw-r--r--src/parse/asset_map.h (renamed from src/asset_map.h)16
-rw-r--r--src/parse/cpl.cc147
-rw-r--r--src/parse/cpl.h (renamed from src/cpl_file.h)52
-rw-r--r--src/parse/pkl.cc (renamed from src/pkl_file.cc)36
-rw-r--r--src/parse/pkl.h (renamed from src/pkl_file.h)16
-rw-r--r--src/parse/subtitle.cc135
-rw-r--r--src/parse/subtitle.h93
-rw-r--r--src/picture_asset.cc323
-rw-r--r--src/picture_asset.h182
-rw-r--r--src/picture_frame.cc55
-rw-r--r--src/picture_frame.h16
-rw-r--r--src/reel.cc24
-rw-r--r--src/reel.h4
-rw-r--r--src/sound_asset.cc223
-rw-r--r--src/sound_asset.h75
-rw-r--r--src/sound_frame.cc4
-rw-r--r--src/subtitle_asset.cc251
-rw-r--r--src/subtitle_asset.h91
-rw-r--r--src/test_mode.cc45
-rw-r--r--src/types.cc12
-rw-r--r--src/types.h15
-rw-r--r--src/util.cc64
-rw-r--r--src/util.h28
-rw-r--r--src/wscript24
-rw-r--r--src/xml.cc263
-rw-r--r--src/xml.h156
45 files changed, 2316 insertions, 1865 deletions
diff --git a/src/argb_frame.cc b/src/argb_frame.cc
index 8e54e3b4..a48f80bb 100644
--- a/src/argb_frame.cc
+++ b/src/argb_frame.cc
@@ -21,16 +21,14 @@
using namespace libdcp;
-/** Construct an empty ARGBFrame with a given width and height and with
+/** Construct an empty ARGBFrame of a given size and with
* undefined contents.
- * @param width Width in pixels.
- * @param height Height in pixels.
+ * @param size Size in pixels.
*/
-ARGBFrame::ARGBFrame (int width, int height)
- : _width (width)
- , _height (height)
+ARGBFrame::ARGBFrame (Size size)
+ : _size (size)
{
- _data = new uint8_t[width * height * 4];
+ _data = new uint8_t[_size.width * _size.height * 4];
}
@@ -43,5 +41,5 @@ ARGBFrame::~ARGBFrame ()
int
ARGBFrame::stride () const
{
- return _width * 4;
+ return _size.width * 4;
}
diff --git a/src/argb_frame.h b/src/argb_frame.h
index c5c35768..a9946bb0 100644
--- a/src/argb_frame.h
+++ b/src/argb_frame.h
@@ -22,6 +22,7 @@
*/
#include <stdint.h>
+#include "util.h"
namespace libdcp
{
@@ -44,7 +45,7 @@ namespace libdcp
class ARGBFrame
{
public:
- ARGBFrame (int width, int height);
+ ARGBFrame (Size size);
~ARGBFrame ();
uint8_t* data () const {
@@ -54,17 +55,12 @@ public:
/** Length of one picture row in bytes */
int stride () const;
- int width () const {
- return _width;
- }
-
- int height () const {
- return _height;
+ Size size () const {
+ return _size;
}
private:
- int _width;
- int _height;
+ Size _size;
uint8_t* _data;
};
diff --git a/src/asset.cc b/src/asset.cc
index fa6947d1..c566a1e5 100644
--- a/src/asset.cc
+++ b/src/asset.cc
@@ -25,6 +25,7 @@
#include <fstream>
#include <boost/filesystem.hpp>
#include <boost/lexical_cast.hpp>
+#include <boost/function.hpp>
#include <libxml++/nodes/element.h>
#include "AS_DCP.h"
#include "KM_util.h"
@@ -36,10 +37,14 @@ using namespace std;
using namespace boost;
using namespace libdcp;
-Asset::Asset (string directory, string file_name)
+Asset::Asset (string directory, string file_name, int edit_rate, int intrinsic_duration)
: _directory (directory)
, _file_name (file_name)
, _uuid (make_uuid ())
+ , _edit_rate (edit_rate)
+ , _entry_point (0)
+ , _intrinsic_duration (intrinsic_duration)
+ , _duration (intrinsic_duration)
{
if (_file_name.empty ()) {
_file_name = _uuid + ".xml";
@@ -47,30 +52,27 @@ Asset::Asset (string directory, string file_name)
}
void
-Asset::write_to_pkl (xmlpp::Element* p) const
+Asset::write_to_pkl (xmlpp::Node* node) const
{
- xmlpp::Element* asset = p->add_child("Asset");
- asset->add_child("Id")->add_child_text("urn:uuid:" + _uuid);
+ 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);
- asset->add_child("Hash")->add_child_text (digest());
- asset->add_child("Size")->add_child_text (boost::lexical_cast<string> (filesystem::file_size(path())));
+ asset->add_child("Hash")->add_child_text (digest ());
+ asset->add_child("Size")->add_child_text (lexical_cast<string> (filesystem::file_size(path())));
asset->add_child("Type")->add_child_text ("application/mxf");
}
void
-Asset::write_to_assetmap (ostream& s) const
+Asset::write_to_assetmap (xmlpp::Node* node) const
{
- s << " <Asset>\n"
- << " <Id>urn:uuid:" << _uuid << "</Id>\n"
- << " <ChunkList>\n"
- << " <Chunk>\n"
- << " <Path>" << _file_name << "</Path>\n"
- << " <VolumeIndex>1</VolumeIndex>\n"
- << " <Offset>0</Offset>\n"
- << " <Length>" << filesystem::file_size(path()) << "</Length>\n"
- << " </Chunk>\n"
- << " </ChunkList>\n"
- << " </Asset>\n";
+ 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 (_file_name);
+ 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> (filesystem::file_size(path())));
}
filesystem::path
@@ -92,3 +94,23 @@ Asset::digest () const
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;
+}
diff --git a/src/asset.h b/src/asset.h
index 5436e05c..3dffa9ab 100644
--- a/src/asset.h
+++ b/src/asset.h
@@ -27,6 +27,8 @@
#include <string>
#include <list>
#include <boost/filesystem.hpp>
+#include <boost/function.hpp>
+#include <libxml++/libxml++.h>
#include "types.h"
namespace ASDCP {
@@ -51,37 +53,72 @@ public:
* @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 (std::string directory, std::string file_name = "");
+ Asset (std::string directory, std::string file_name = "", int edit_rate = 0, int intrinsic_duration = 0);
virtual ~Asset() {}
/** 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 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.
*/
- void write_to_assetmap (std::ostream& s) const;
+ void write_to_assetmap (xmlpp::Node *) const;
std::string uuid () const {
return _uuid;
}
- virtual bool equals (boost::shared_ptr<const Asset> other, EqualityOptions opt, std::list<std::string>& notes) const = 0;
+ boost::filesystem::path path () const;
+
+ void set_directory (std::string d) {
+ _directory = d;
+ }
+
+ void set_file_name (std::string f) {
+ _file_name = f;
+ }
+
+ int entry_point () const {
+ return _entry_point;
+ }
+
+ 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;
+ }
+
+ virtual bool equals (boost::shared_ptr<const Asset> other, EqualityOptions opt, boost::function<void (NoteType, std::string)>) const;
protected:
- friend class PictureAsset;
- friend class SoundAsset;
std::string digest () const;
- boost::filesystem::path path () const;
/** Directory that our MXF or XML file is in */
std::string _directory;
@@ -89,6 +126,14 @@ protected:
std::string _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 */
diff --git a/src/cpl.cc b/src/cpl.cc
new file mode 100644
index 00000000..1ca64f88
--- /dev/null
+++ b/src/cpl.cc
@@ -0,0 +1,460 @@
+/*
+ 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;
+}
diff --git a/src/cpl.h b/src/cpl.h
new file mode 100644
index 00000000..f9814337
--- /dev/null
+++ b/src/cpl.h
@@ -0,0 +1,111 @@
+/*
+ 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;
+ 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;
+};
+
+}
diff --git a/src/cpl_file.cc b/src/cpl_file.cc
deleted file mode 100644
index 6a17d721..00000000
--- a/src/cpl_file.cc
+++ /dev/null
@@ -1,148 +0,0 @@
-/*
- Copyright (C) 2012 Carl Hetherington <cth@carlh.net>
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
-*/
-
-/** @file src/cpl_file.cc
- * @brief Classes used to parse a CPL.
- */
-
-#include <iostream>
-#include "cpl_file.h"
-
-using namespace std;
-using namespace libdcp;
-
-CPLFile::CPLFile (string file)
- : XMLFile (file, "CompositionPlaylist")
-{
- id = string_child ("Id");
- annotation_text = optional_string_child ("AnnotationText");
- issue_date = string_child ("IssueDate");
- creator = optional_string_child ("Creator");
- content_title_text = string_child ("ContentTitleText");
- content_kind = kind_child ("ContentKind");
- content_version = optional_type_child<ContentVersion> ("ContentVersion");
- ignore_child ("RatingList");
- reels = type_grand_children<CPLReel> ("ReelList", "Reel");
-
- ignore_child ("Issuer");
- ignore_child ("Signer");
- ignore_child ("Signature");
-
- done ();
-}
-
-ContentVersion::ContentVersion (xmlpp::Node const * node)
- : XMLNode (node)
-{
- id = optional_string_child ("Id");
- label_text = string_child ("LabelText");
- done ();
-}
-
-CPLReel::CPLReel (xmlpp::Node const * node)
- : XMLNode (node)
-{
- id = string_child ("Id");
- asset_list = type_child<CPLAssetList> ("AssetList");
-
- ignore_child ("AnnotationText");
- done ();
-}
-
-CPLAssetList::CPLAssetList (xmlpp::Node const * node)
- : XMLNode (node)
-{
- main_picture = optional_type_child<MainPicture> ("MainPicture");
- main_stereoscopic_picture = optional_type_child<MainStereoscopicPicture> ("MainStereoscopicPicture");
- main_sound = optional_type_child<MainSound> ("MainSound");
- main_subtitle = optional_type_child<MainSubtitle> ("MainSubtitle");
-
- done ();
-}
-
-MainPicture::MainPicture (xmlpp::Node const * node)
- : Picture (node)
-{
-
-}
-
-MainStereoscopicPicture::MainStereoscopicPicture (xmlpp::Node const * node)
- : Picture (node)
-{
-
-}
-
-Picture::Picture (xmlpp::Node const * node)
- : XMLNode (node)
-{
- id = string_child ("Id");
- annotation_text = optional_string_child ("AnnotationText");
- edit_rate = fraction_child ("EditRate");
- intrinsic_duration = int64_child ("IntrinsicDuration");
- entry_point = int64_child ("EntryPoint");
- duration = int64_child ("Duration");
- frame_rate = fraction_child ("FrameRate");
- try {
- screen_aspect_ratio = fraction_child ("ScreenAspectRatio");
- } catch (XMLError& e) {
- /* Maybe it's not a fraction */
- }
- try {
- float f = float_child ("ScreenAspectRatio");
- screen_aspect_ratio = Fraction (f * 1000, 1000);
- } catch (bad_cast& e) {
-
- }
-
- ignore_child ("Hash");
-
- done ();
-}
-
-MainSound::MainSound (xmlpp::Node const * node)
- : XMLNode (node)
-{
- id = string_child ("Id");
- annotation_text = optional_string_child ("AnnotationText");
- edit_rate = fraction_child ("EditRate");
- intrinsic_duration = int64_child ("IntrinsicDuration");
- entry_point = int64_child ("EntryPoint");
- duration = int64_child ("Duration");
-
- ignore_child ("Hash");
- ignore_child ("Language");
-
- done ();
-}
-
-MainSubtitle::MainSubtitle (xmlpp::Node const * node)
- : XMLNode (node)
-{
- id = string_child ("Id");
- annotation_text = optional_string_child ("AnnotationText");
- edit_rate = fraction_child ("EditRate");
- intrinsic_duration = int64_child ("IntrinsicDuration");
- entry_point = int64_child ("EntryPoint");
- duration = int64_child ("Duration");
-
- ignore_child ("Hash");
- ignore_child ("Language");
-
- done ();
-}
diff --git a/src/dcp.cc b/src/dcp.cc
index 73c8a2cd..fdd7a1f5 100644
--- a/src/dcp.cc
+++ b/src/dcp.cc
@@ -29,6 +29,7 @@
#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>
@@ -40,10 +41,11 @@
#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;
@@ -51,6 +53,7 @@ using std::stringstream;
using std::ofstream;
using std::ostream;
using boost::shared_ptr;
+using boost::lexical_cast;
using namespace libdcp;
DCP::DCP (string directory)
@@ -60,21 +63,21 @@ DCP::DCP (string directory)
}
void
-DCP::write_xml (shared_ptr<Encryption> crypt) 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, crypt);
}
string pkl_uuid = make_uuid ();
- string pkl_path = write_pkl (pkl_uuid, crypt);
+ 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, shared_ptr<Encryption> crypt) const
{
assert (!_cpls.empty ());
@@ -93,28 +96,25 @@ DCP::write_pkl (string pkl_uuid, shared_ptr<Encryption> crypt) const
pkl->add_child("Id")->add_child_text ("urn:uuid:" + pkl_uuid);
/* XXX: this is a bit of a hack */
pkl->add_child("AnnotationText")->add_child_text(_cpls.front()->name());
- 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);
}
if (crypt) {
sign (pkl, crypt->certificates, crypt->signer_key);
}
- doc.write_to_file_formatted (p.string(), "UTF-8");
-
+ doc.write_to_file_formatted (p.string (), "UTF-8");
return p.string ();
}
@@ -124,55 +124,50 @@ DCP::write_volindex () const
boost::filesystem::path p;
p /= _directory;
p /= "VOLINDEX.xml";
- ofstream vi (p.string().c_str());
- vi << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
- << "<VolumeIndex xmlns=\"http://www.smpte-ra.org/schemas/429-9/2007/AM\">\n"
- << " <Index>1</Index>\n"
- << "</VolumeIndex>\n";
+ xmlpp::Document doc;
+ xmlpp::Element* root = doc.create_root_node ("VolumeIndex", "http://www.smpte-ra.org/schemas/429-9/2007/AM");
+ root->add_child("Index")->add_child_text ("1");
+ doc.write_to_file_formatted (p.string (), "UTF-8");
}
void
-DCP::write_assetmap (string pkl_uuid, int pkl_length) const
+DCP::write_assetmap (string pkl_uuid, int pkl_length, XMLMetadata const & metadata) const
{
boost::filesystem::path p;
p /= _directory;
p /= "ASSETMAP.xml";
- ofstream am (p.string().c_str());
-
- am << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
- << "<AssetMap xmlns=\"http://www.smpte-ra.org/schemas/429-9/2007/AM\">\n"
- << " <Id>urn:uuid:" << make_uuid() << "</Id>\n"
- << " <Creator>" << Metadata::instance()->creator << "</Creator>\n"
- << " <VolumeCount>1</VolumeCount>\n"
- << " <IssueDate>" << Metadata::instance()->issue_date << "</IssueDate>\n"
- << " <Issuer>" << Metadata::instance()->issuer << "</Issuer>\n"
- << " <AssetList>\n";
-
- am << " <Asset>\n"
- << " <Id>urn:uuid:" << pkl_uuid << "</Id>\n"
- << " <PackingList>true</PackingList>\n"
- << " <ChunkList>\n"
- << " <Chunk>\n"
- << " <Path>" << pkl_uuid << "_pkl.xml</Path>\n"
- << " <VolumeIndex>1</VolumeIndex>\n"
- << " <Offset>0</Offset>\n"
- << " <Length>" << pkl_length << "</Length>\n"
- << " </Chunk>\n"
- << " </ChunkList>\n"
- << " </Asset>\n";
+
+ xmlpp::Document doc;
+ xmlpp::Element* root = doc.create_root_node ("AssetMap", "http://www.smpte-ra.org/schemas/429-9/2007/AM");
+
+ root->add_child("Id")->add_child_text ("urn:uuid:" + make_uuid());
+ 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);
+ xmlpp::Node* asset_list = root->add_child ("AssetList");
+
+ xmlpp::Node* asset = asset_list->add_child ("Asset");
+ asset->add_child("Id")->add_child_text ("urn:uuid:" + pkl_uuid);
+ asset->add_child("PackingList")->add_child_text ("true");
+ xmlpp::Node* chunk_list = asset->add_child ("ChunkList");
+ xmlpp::Node* chunk = chunk_list->add_child ("Chunk");
+ chunk->add_child("Path")->add_child_text (pkl_uuid + "_pkl.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> (pkl_length));
for (list<shared_ptr<const CPL> >::const_iterator i = _cpls.begin(); i != _cpls.end(); ++i) {
- (*i)->write_to_assetmap (am);
+ (*i)->write_to_assetmap (asset_list);
}
list<shared_ptr<const Asset> > a = assets ();
for (list<shared_ptr<const Asset> >::const_iterator i = a.begin(); i != a.end(); ++i) {
- (*i)->write_to_assetmap (am);
+ (*i)->write_to_assetmap (asset_list);
}
- am << " </AssetList>\n"
- << "</AssetMap>\n";
+ doc.write_to_file_formatted (p.string (), "UTF-8");
}
@@ -181,29 +176,29 @@ DCP::read (bool require_mxfs)
{
Files files;
- shared_ptr<AssetMap> asset_map;
+ shared_ptr<parse::AssetMap> asset_map;
try {
boost::filesystem::path p = _directory;
p /= "ASSETMAP";
if (boost::filesystem::exists (p)) {
- asset_map.reset (new AssetMap (p.string ()));
+ asset_map.reset (new libdcp::parse::AssetMap (p.string ()));
} else {
p = _directory;
p /= "ASSETMAP.xml";
if (boost::filesystem::exists (p)) {
- asset_map.reset (new AssetMap (p.string ()));
+ asset_map.reset (new libdcp::parse::AssetMap (p.string ()));
} else {
- throw DCPReadError ("could not find AssetMap file");
+ boost::throw_exception (DCPReadError ("could not find AssetMap file"));
}
}
} catch (FileError& e) {
- throw FileError ("could not load AssetMap file", files.asset_map);
+ boost::throw_exception (FileError ("could not load AssetMap file", files.asset_map));
}
- for (list<shared_ptr<AssetMapAsset> >::const_iterator i = asset_map->assets.begin(); i != asset_map->assets.end(); ++i) {
+ for (list<shared_ptr<libdcp::parse::AssetMapAsset> >::const_iterator i = asset_map->assets.begin(); i != asset_map->assets.end(); ++i) {
if ((*i)->chunks.size() != 1) {
- throw XMLError ("unsupported asset chunk count");
+ boost::throw_exception (XMLError ("unsupported asset chunk count"));
}
boost::filesystem::path t = _directory;
@@ -230,24 +225,24 @@ DCP::read (bool require_mxfs)
if (files.pkl.empty ()) {
files.pkl = t.string();
} else {
- throw DCPReadError ("duplicate PKLs found");
+ boost::throw_exception (DCPReadError ("duplicate PKLs found"));
}
}
}
if (files.cpls.empty ()) {
- throw FileError ("no CPL files found", "");
+ boost::throw_exception (FileError ("no CPL files found", ""));
}
if (files.pkl.empty ()) {
- throw FileError ("no PKL file found", "");
+ boost::throw_exception (FileError ("no PKL file found", ""));
}
- shared_ptr<PKLFile> pkl;
+ shared_ptr<parse::PKL> pkl;
try {
- pkl.reset (new PKLFile (files.pkl));
+ pkl.reset (new parse::PKL (files.pkl));
} catch (FileError& e) {
- throw FileError ("could not load PKL file", files.pkl);
+ boost::throw_exception (FileError ("could not load PKL file", files.pkl));
}
/* Cross-check */
@@ -259,10 +254,10 @@ DCP::read (bool require_mxfs)
}
bool
-DCP::equals (DCP const & other, EqualityOptions opt, list<string>& notes) const
+DCP::equals (DCP const & other, EqualityOptions opt, boost::function<void (NoteType, string)> note) const
{
if (_cpls.size() != other._cpls.size()) {
- notes.push_back ("CPL counts differ");
+ note (ERROR, "CPL counts differ");
return false;
}
@@ -270,7 +265,7 @@ DCP::equals (DCP const & other, EqualityOptions opt, list<string>& notes) const
list<shared_ptr<const CPL> >::const_iterator b = other._cpls.begin ();
while (a != _cpls.end ()) {
- if (!(*a)->equals (*b->get(), opt, notes)) {
+ if (!(*a)->equals (*b->get(), opt, note)) {
return false;
}
++a;
@@ -280,7 +275,6 @@ DCP::equals (DCP const & other, EqualityOptions opt, list<string>& notes) const
return true;
}
-
void
DCP::add_cpl (shared_ptr<CPL> cpl)
{
@@ -308,421 +302,3 @@ DCP::assets () const
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;
-}
diff --git a/src/dcp.h b/src/dcp.h
index 5a915019..42c48387 100644
--- a/src/dcp.h
+++ b/src/dcp.h
@@ -28,7 +28,6 @@
#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"
@@ -46,93 +45,11 @@ class PictureAsset;
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.
*/
@@ -160,15 +77,14 @@ public:
/** 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 &, 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.
- * @param options Options to define just what "equality" means.
- * @param notes Filled in with notes about differences.
+ * @param options Options to define what "equality" means.
* @return true if the DCPs are equal according to EqualityOptions, otherwise false.
*/
- bool equals (DCP const & other, EqualityOptions options, std::list<std::string>& notes) const;
+ bool equals (DCP const & other, EqualityOptions options, boost::function<void (NoteType, std::string)> note) const;
/** Add a CPL to this DCP.
* @param cpl CPL to add.
@@ -190,7 +106,7 @@ private:
/** 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 &, boost::shared_ptr<Encryption>) const;
/** Write the VOLINDEX file */
void write_volindex () const;
@@ -199,7 +115,7 @@ private:
* @param pkl_uuid UUID of our PKL.
* @param pkl_length Length of our PKL in bytes.
*/
- void write_assetmap (std::string pkl_uuid, int pkl_length) const;
+ void write_assetmap (std::string pkl_uuid, int pkl_length, XMLMetadata const &) const;
/** @return Assets in all this CPLs in this DCP */
std::list<boost::shared_ptr<const Asset> > assets () const;
diff --git a/src/dcp_time.cc b/src/dcp_time.cc
index 7c7c5298..7d3111d2 100644
--- a/src/dcp_time.cc
+++ b/src/dcp_time.cc
@@ -64,7 +64,7 @@ Time::Time (string time)
vector<string> b;
split (b, time, is_any_of (":"));
if (b.size() != 4) {
- throw DCPReadError ("unrecognised time specification");
+ boost::throw_exception (DCPReadError ("unrecognised time specification"));
}
h = lexical_cast<int> (b[0]);
diff --git a/src/gamma_lut.cc b/src/gamma_lut.cc
new file mode 100644
index 00000000..acc80af0
--- /dev/null
+++ b/src/gamma_lut.cc
@@ -0,0 +1,16 @@
+#include <cmath>
+#include "gamma_lut.h"
+#include "lut_cache.h"
+
+using namespace libdcp;
+
+LUTCache<GammaLUT> GammaLUT::cache;
+
+GammaLUT::GammaLUT(int bits, float gamma)
+ : LUT<float> (bits, gamma)
+{
+ int const bit_length = pow(2, bits);
+ for (int i = 0; i < bit_length; ++i) {
+ _lut[i] = pow(float(i) / (bit_length - 1), gamma);
+ }
+}
diff --git a/src/gamma_lut.h b/src/gamma_lut.h
new file mode 100644
index 00000000..e41cd21f
--- /dev/null
+++ b/src/gamma_lut.h
@@ -0,0 +1,13 @@
+#include "lut.h"
+#include "lut_cache.h"
+
+namespace libdcp {
+
+class GammaLUT : public LUT<float>
+{
+public:
+ GammaLUT (int bit_length, float gamma);
+ static LUTCache<GammaLUT> cache;
+};
+
+}
diff --git a/src/test_mode.h b/src/lut.h
index b2e671d5..bdb5f37f 100644
--- a/src/test_mode.h
+++ b/src/lut.h
@@ -17,13 +17,47 @@
*/
-/** @file src/test_mode.h
- * @brief A method to enable test mode for libdcp.
- */
+#ifndef LIBDCP_LUT_H
+#define LIBDCP_LUT_H
-namespace libdcp
-{
+#include <cmath>
+
+namespace libdcp {
-extern void enable_test_mode ();
+template<typename T>
+class LUT
+{
+public:
+ LUT(int bit_depth, float gamma)
+ : _lut(0)
+ , _bit_depth (bit_depth)
+ , _gamma (gamma)
+ {
+ _lut = new T[int(std::pow(2.0f, _bit_depth))];
+ }
+
+ virtual ~LUT() {
+ delete[] _lut;
+ }
+
+ T const * lut() const {
+ return _lut;
+ }
+
+ int bit_depth () const {
+ return _bit_depth;
+ }
+
+ float gamma () const {
+ return _gamma;
+ }
+
+protected:
+ T* _lut;
+ int _bit_depth;
+ float _gamma;
+};
}
+
+#endif
diff --git a/src/lut_cache.h b/src/lut_cache.h
new file mode 100644
index 00000000..b60ee109
--- /dev/null
+++ b/src/lut_cache.h
@@ -0,0 +1,28 @@
+#ifndef LIBDCP_LUT_CACHE_H
+#define LIBDCP_LUT_CACHE_H
+
+#include <list>
+#include <boost/shared_ptr.hpp>
+
+template<class T>
+class LUTCache
+{
+public:
+ boost::shared_ptr<T> get (int bit_depth, float gamma)
+ {
+ for (typename std::list<boost::shared_ptr<T> >::iterator i = _cache.begin(); i != _cache.end(); ++i) {
+ if ((*i)->bit_depth() == bit_depth && (*i)->gamma() == gamma) {
+ return *i;
+ }
+ }
+
+ boost::shared_ptr<T> lut (new T (bit_depth, gamma));
+ _cache.push_back (lut);
+ return lut;
+ }
+
+private:
+ std::list<boost::shared_ptr<T> > _cache;
+};
+
+#endif
diff --git a/src/metadata.cc b/src/metadata.cc
index 7e900e50..2967ac1d 100644
--- a/src/metadata.cc
+++ b/src/metadata.cc
@@ -27,16 +27,25 @@
using namespace std;
using namespace libdcp;
-Metadata* Metadata::_instance = 0;
-
-/** Construct a Metadata object with some default values */
-Metadata::Metadata ()
+MXFMetadata::MXFMetadata ()
: company_name ("libdcp")
, product_name ("libdcp")
, product_version (LIBDCP_VERSION)
- , issuer ("libdcp" LIBDCP_VERSION)
+{
+
+}
+
+
+XMLMetadata::XMLMetadata ()
+ : issuer ("libdcp" LIBDCP_VERSION)
, creator ("libdcp" LIBDCP_VERSION)
{
+ set_issue_date_now ();
+}
+
+void
+XMLMetadata::set_issue_date_now ()
+{
char buffer[64];
time_t now;
time (&now);
@@ -44,15 +53,4 @@ Metadata::Metadata ()
strftime (buffer, 64, "%Y-%m-%dT%I:%M:%S+00:00", tm);
issue_date = string (buffer);
}
-
-/** @return Singleton Metadata instance */
-Metadata *
-Metadata::instance ()
-{
- if (_instance == 0) {
- _instance = new Metadata;
- }
-
- return _instance;
-}
diff --git a/src/metadata.h b/src/metadata.h
index 1610491e..7336766d 100644
--- a/src/metadata.h
+++ b/src/metadata.h
@@ -17,6 +17,9 @@
*/
+#ifndef LIBDCP_METADATA_H
+#define LIBDCP_METADATA_H
+
/** @file src/metadata.h
* @brief Metadata for writing to the DCP.
*/
@@ -26,28 +29,28 @@
namespace libdcp
{
-/** @brief A class to hold various metadata that will be written
- * to the DCP.
- *
- * The values are initialised, and can be modified if desired.
- */
-class Metadata
+class MXFMetadata
{
public:
- static Metadata* instance ();
+ MXFMetadata ();
std::string company_name;
std::string product_name;
std::string product_version;
+};
+
+class XMLMetadata
+{
+public:
+ XMLMetadata ();
+
+ void set_issue_date_now ();
+
std::string issuer;
std::string creator;
std::string issue_date;
-
-private:
- Metadata ();
-
- /** Singleton instance of Metadata */
- static Metadata* _instance;
};
}
+
+#endif
diff --git a/src/mxf_asset.cc b/src/mxf_asset.cc
index 6ba42d75..d52ab2cc 100644
--- a/src/mxf_asset.cc
+++ b/src/mxf_asset.cc
@@ -39,12 +39,18 @@ using boost::shared_ptr;
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)
+ , _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)
{
@@ -76,15 +82,15 @@ MXFAsset::~MXFAsset ()
}
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) {
@@ -98,38 +104,28 @@ MXFAsset::fill_writer_info (ASDCP::WriterInfo* writer_info) const
}
bool
-MXFAsset::equals (shared_ptr<const Asset> other, EqualityOptions, list<string>& notes) const
+MXFAsset::equals (shared_ptr<const Asset> other, EqualityOptions opt, boost::function<void (NoteType, string)> note) const
{
+ if (!Asset::equals (other, opt, note)) {
+ return false;
+ }
+
shared_ptr<const MXFAsset> other_mxf = dynamic_pointer_cast<const MXFAsset> (other);
if (!other_mxf) {
- notes.push_back ("comparing an MXF asset with a non-MXF asset");
+ note (ERROR, "comparing an MXF asset with a non-MXF asset");
return false;
}
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
{
diff --git a/src/mxf_asset.h b/src/mxf_asset.h
index 93fa9013..a23a052d 100644
--- a/src/mxf_asset.h
+++ b/src/mxf_asset.h
@@ -30,45 +30,48 @@ namespace ASDCP {
namespace libdcp
{
+class MXFMetadata;
+
/** @brief Parent class for assets which have MXF files */
class MXFAsset : public Asset
{
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, 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;
+ 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;
diff --git a/src/asset_map.cc b/src/parse/asset_map.cc
index 8fcc515f..aedc931e 100644
--- a/src/asset_map.cc
+++ b/src/parse/asset_map.cc
@@ -23,36 +23,36 @@
#include <boost/algorithm/string.hpp>
#include "asset_map.h"
-#include "util.h"
+#include "../util.h"
+#include "../xml.h"
using std::string;
using std::list;
using boost::shared_ptr;
-using namespace libdcp;
+using namespace libdcp::parse;
AssetMap::AssetMap (string file)
- : XMLFile (file, "AssetMap")
{
- id = string_child ("Id");
- creator = string_child ("Creator");
- volume_count = int64_child ("VolumeCount");
- issue_date = string_child ("IssueDate");
- issuer = string_child ("Issuer");
- assets = type_grand_children<AssetMapAsset> ("AssetList", "Asset");
+ cxml::File f (file, "AssetMap");
+
+ id = f.string_child ("Id");
+ creator = f.string_child ("Creator");
+ volume_count = f.number_child<int64_t> ("VolumeCount");
+ issue_date = f.string_child ("IssueDate");
+ issuer = f.string_child ("Issuer");
+ assets = type_grand_children<AssetMapAsset> (f, "AssetList", "Asset");
}
-AssetMapAsset::AssetMapAsset (xmlpp::Node const * node)
- : XMLNode (node)
+AssetMapAsset::AssetMapAsset (shared_ptr<const cxml::Node> node)
{
- id = string_child ("Id");
- packing_list = optional_string_child ("PackingList");
- chunks = type_grand_children<Chunk> ("ChunkList", "Chunk");
+ id = node->string_child ("Id");
+ packing_list = node->optional_string_child ("PackingList").get_value_or ("");
+ chunks = type_grand_children<Chunk> (node, "ChunkList", "Chunk");
}
-Chunk::Chunk (xmlpp::Node const * node)
- : XMLNode (node)
+Chunk::Chunk (shared_ptr<const cxml::Node> node)
{
- path = string_child ("Path");
+ path = node->string_child ("Path");
string const prefix = "file://";
@@ -60,9 +60,9 @@ Chunk::Chunk (xmlpp::Node const * node)
path = path.substr (prefix.length());
}
- volume_index = optional_int64_child ("VolumeIndex");
- offset = optional_int64_child ("Offset");
- length = optional_int64_child ("Length");
+ volume_index = node->optional_number_child<int64_t> ("VolumeIndex").get_value_or (0);
+ offset = node->optional_number_child<int64_t> ("Offset").get_value_or (0);
+ length = node->optional_number_child<int64_t> ("Length").get_value_or (0);
}
shared_ptr<AssetMapAsset>
diff --git a/src/asset_map.h b/src/parse/asset_map.h
index 8cf89b4b..af3e8918 100644
--- a/src/asset_map.h
+++ b/src/parse/asset_map.h
@@ -23,18 +23,20 @@
#include <stdint.h>
#include <boost/shared_ptr.hpp>
-#include "xml.h"
+#include <libcxml/cxml.h>
namespace libdcp {
+namespace parse {
+
/** @class Chunk
* @brief A simple parser for and representation of a \<Chunk\> node within an asset map.
*/
-class Chunk : public XMLNode
+class Chunk
{
public:
Chunk ();
- Chunk (xmlpp::Node const * node);
+ Chunk (boost::shared_ptr<const cxml::Node> node);
std::string path;
int64_t volume_index;
@@ -45,11 +47,11 @@ public:
/** @class AssetMapAsset
* @brief A simple parser for and representation of an \<AssetMap\> node within an asset map.
*/
-class AssetMapAsset : public XMLNode
+class AssetMapAsset
{
public:
AssetMapAsset ();
- AssetMapAsset (xmlpp::Node const * node);
+ AssetMapAsset (boost::shared_ptr<const cxml::Node> node);
std::string id;
std::string packing_list;
@@ -59,7 +61,7 @@ public:
/** @class AssetMap
* @brief A simple parser for and representation of an asset map file.
*/
-class AssetMap : public XMLFile
+class AssetMap
{
public:
AssetMap (std::string file);
@@ -75,3 +77,5 @@ public:
};
}
+
+}
diff --git a/src/parse/cpl.cc b/src/parse/cpl.cc
new file mode 100644
index 00000000..c4cf4374
--- /dev/null
+++ b/src/parse/cpl.cc
@@ -0,0 +1,147 @@
+/*
+ Copyright (C) 2012 Carl Hetherington <cth@carlh.net>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+/** @file src/cpl_file.cc
+ * @brief Classes used to parse a CPL.
+ */
+
+#include <iostream>
+#include "cpl.h"
+#include "../xml.h"
+#include "../util.h"
+
+using std::string;
+using std::bad_cast;
+using boost::shared_ptr;
+using namespace libdcp::parse;
+
+CPL::CPL (string file)
+{
+ cxml::File f (file, "CompositionPlaylist");
+
+ id = f.string_child ("Id");
+ annotation_text = f.optional_string_child ("AnnotationText").get_value_or ("");
+ issue_date = f.string_child ("IssueDate");
+ creator = f.optional_string_child ("Creator").get_value_or ("");
+ content_title_text = f.string_child ("ContentTitleText");
+ content_kind = content_kind_from_string (f.string_child ("ContentKind"));
+ content_version = optional_type_child<ContentVersion> (f, "ContentVersion");
+ f.ignore_child ("RatingList");
+ reels = type_grand_children<Reel> (f, "ReelList", "Reel");
+
+ f.ignore_child ("Issuer");
+ f.ignore_child ("Signer");
+ f.ignore_child ("Signature");
+
+ f.done ();
+}
+
+ContentVersion::ContentVersion (shared_ptr<const cxml::Node> node)
+{
+ id = node->optional_string_child ("Id").get_value_or ("");
+ label_text = node->string_child ("LabelText");
+ node->done ();
+}
+
+Reel::Reel (shared_ptr<const cxml::Node> node)
+{
+ id = node->string_child ("Id");
+ asset_list = type_child<CPLAssetList> (node, "AssetList");
+
+ node->ignore_child ("AnnotationText");
+ node->done ();
+}
+
+CPLAssetList::CPLAssetList (shared_ptr<const cxml::Node> node)
+{
+ main_picture = optional_type_child<MainPicture> (node, "MainPicture");
+ main_stereoscopic_picture = optional_type_child<MainStereoscopicPicture> (node, "MainStereoscopicPicture");
+ main_sound = optional_type_child<MainSound> (node, "MainSound");
+ main_subtitle = optional_type_child<MainSubtitle> (node, "MainSubtitle");
+
+ node->done ();
+}
+
+MainPicture::MainPicture (shared_ptr<const cxml::Node> node)
+ : Picture (node)
+{
+
+}
+
+MainStereoscopicPicture::MainStereoscopicPicture (shared_ptr<const cxml::Node> node)
+ : Picture (node)
+{
+
+}
+
+Picture::Picture (shared_ptr<const cxml::Node> node)
+{
+ id = node->string_child ("Id");
+ annotation_text = node->optional_string_child ("AnnotationText").get_value_or ("");
+ edit_rate = Fraction (node->string_child ("EditRate"));
+ intrinsic_duration = node->number_child<int64_t> ("IntrinsicDuration");
+ entry_point = node->number_child<int64_t> ("EntryPoint");
+ duration = node->number_child<int64_t> ("Duration");
+ frame_rate = Fraction (node->string_child ("FrameRate"));
+ try {
+ screen_aspect_ratio = Fraction (node->string_child ("ScreenAspectRatio"));
+ } catch (XMLError& e) {
+ /* Maybe it's not a fraction */
+ }
+ try {
+ float f = node->number_child<float> ("ScreenAspectRatio");
+ screen_aspect_ratio = Fraction (f * 1000, 1000);
+ } catch (bad_cast& e) {
+
+ }
+
+ node->ignore_child ("Hash");
+
+ node->done ();
+}
+
+MainSound::MainSound (shared_ptr<const cxml::Node> node)
+{
+ id = node->string_child ("Id");
+ annotation_text = node->optional_string_child ("AnnotationText").get_value_or ("");
+ edit_rate = Fraction (node->string_child ("EditRate"));
+ intrinsic_duration = node->number_child<int64_t> ("IntrinsicDuration");
+ entry_point = node->number_child<int64_t> ("EntryPoint");
+ duration = node->number_child<int64_t> ("Duration");
+
+ node->ignore_child ("Hash");
+ node->ignore_child ("Language");
+
+ node->done ();
+}
+
+MainSubtitle::MainSubtitle (shared_ptr<const cxml::Node> node)
+{
+ id = node->string_child ("Id");
+ annotation_text = node->optional_string_child ("AnnotationText").get_value_or ("");
+ edit_rate = Fraction (node->string_child ("EditRate"));
+ intrinsic_duration = node->number_child<int64_t> ("IntrinsicDuration");
+ entry_point = node->number_child<int64_t> ("EntryPoint");
+ duration = node->number_child<int64_t> ("Duration");
+
+ node->ignore_child ("Hash");
+ node->ignore_child ("Language");
+
+ node->done ();
+}
diff --git a/src/cpl_file.h b/src/parse/cpl.h
index 67b38a0d..434a244b 100644
--- a/src/cpl_file.h
+++ b/src/parse/cpl.h
@@ -17,28 +17,34 @@
*/
-/** @file src/cpl_file.h
+/** @file src/parse/cpl.h
* @brief Classes used to parse a CPL.
*/
#include <stdint.h>
#include <boost/shared_ptr.hpp>
-#include "xml.h"
+#include <libcxml/cxml.h>
+#include "../types.h"
namespace libdcp {
-/** @brief A simple parser for and representation of a CPL \<Picture\> node */
-class Picture : public XMLNode
+namespace parse {
+
+/** @brief A simple representation of a CPL \<Picture\> node */
+class Picture
{
public:
Picture () {}
- Picture (xmlpp::Node const * node);
+ Picture (boost::shared_ptr<const cxml::Node> node);
std::string id;
std::string annotation_text;
Fraction edit_rate;
+ /** Duration of the whole thing */
int64_t intrinsic_duration;
+ /** Start point in frames */
int64_t entry_point;
+ /** Duration that will actually play */
int64_t duration;
Fraction frame_rate;
Fraction screen_aspect_ratio;
@@ -50,7 +56,7 @@ class MainPicture : public Picture
{
public:
MainPicture () {}
- MainPicture (xmlpp::Node const * node);
+ MainPicture (boost::shared_ptr<const cxml::Node> node);
};
/** @brief A simple parser for and representation of a CPL \<MainStereoscopicPicture\> node */
@@ -58,15 +64,15 @@ class MainStereoscopicPicture : public Picture
{
public:
MainStereoscopicPicture () {}
- MainStereoscopicPicture (xmlpp::Node const * node);
+ MainStereoscopicPicture (boost::shared_ptr<const cxml::Node> node);
};
/** @brief A simple parser for and representation of a CPL \<MainSound\> node */
-class MainSound : public XMLNode
+class MainSound
{
public:
MainSound () {}
- MainSound (xmlpp::Node const * node);
+ MainSound (boost::shared_ptr<const cxml::Node> node);
std::string id;
std::string annotation_text;
@@ -77,11 +83,11 @@ public:
};
/** @brief A simple parser for and representation of a CPL \<MainSubtitle\> node */
-class MainSubtitle : public XMLNode
+class MainSubtitle
{
public:
MainSubtitle () {}
- MainSubtitle (xmlpp::Node const * node);
+ MainSubtitle (boost::shared_ptr<const cxml::Node> node);
std::string id;
std::string annotation_text;
@@ -92,11 +98,11 @@ public:
};
/** @brief A simple parser for and representation of a CPL \<AssetList\> node */
-class CPLAssetList : public XMLNode
+class CPLAssetList
{
public:
CPLAssetList () {}
- CPLAssetList (xmlpp::Node const * node);
+ CPLAssetList (boost::shared_ptr<const cxml::Node> node);
boost::shared_ptr<MainPicture> main_picture;
boost::shared_ptr<MainStereoscopicPicture> main_stereoscopic_picture;
@@ -105,11 +111,11 @@ public:
};
/** @brief A simple parser for and representation of a CPL \<Reel\> node */
-class CPLReel : public XMLNode
+class Reel
{
public:
- CPLReel () {}
- CPLReel (xmlpp::Node const * node);
+ Reel () {}
+ Reel (boost::shared_ptr<const cxml::Node> node);
std::string id;
boost::shared_ptr<CPLAssetList> asset_list;
@@ -117,27 +123,27 @@ public:
/** @brief A simple parser for and representation of a CPL \<ContentVersion\> node */
-class ContentVersion : public XMLNode
+class ContentVersion
{
public:
ContentVersion () {}
- ContentVersion (xmlpp::Node const * node);
+ ContentVersion (boost::shared_ptr<const cxml::Node> node);
std::string id;
std::string label_text;
};
-/** @class CPLFile
+/** @class CPL
* @brief Class to parse a CPL
*
* This class is used to parse XML CPL files. It is rarely necessary
* for the caller to use it outside libdcp.
*/
-class CPLFile : public XMLFile
+class CPL
{
public:
/** Parse a CPL XML file into our member variables */
- CPLFile (std::string file);
+ CPL (std::string file);
std::string id;
std::string annotation_text;
@@ -146,8 +152,10 @@ public:
std::string content_title_text;
ContentKind content_kind;
boost::shared_ptr<ContentVersion> content_version;
- std::list<boost::shared_ptr<CPLReel> > reels;
+ std::list<boost::shared_ptr<Reel> > reels;
};
}
+}
+
diff --git a/src/pkl_file.cc b/src/parse/pkl.cc
index 21763f27..d790cfe4 100644
--- a/src/pkl_file.cc
+++ b/src/parse/pkl.cc
@@ -22,30 +22,30 @@
*/
#include <iostream>
-#include "pkl_file.h"
+#include "pkl.h"
using namespace std;
using namespace boost;
-using namespace libdcp;
+using namespace libdcp::parse;
-PKLFile::PKLFile (string file)
- : XMLFile (file, "PackingList")
+PKL::PKL (string file)
{
- id = string_child ("Id");
- annotation_text = string_child ("AnnotationText");
- issue_date = string_child ("IssueDate");
- issuer = string_child ("Issuer");
- creator = string_child ("Creator");
- assets = type_grand_children<PKLAsset> ("AssetList", "Asset");
+ cxml::File f (file, "PackingList");
+
+ id = f.string_child ("Id");
+ annotation_text = f.optional_string_child ("AnnotationText").get_value_or ("");
+ issue_date = f.string_child ("IssueDate");
+ issuer = f.string_child ("Issuer");
+ creator = f.string_child ("Creator");
+ assets = type_grand_children<PKLAsset> (f, "AssetList", "Asset");
}
-PKLAsset::PKLAsset (xmlpp::Node const * node)
- : XMLNode (node)
+PKLAsset::PKLAsset (boost::shared_ptr<const cxml::Node> node)
{
- id = string_child ("Id");
- annotation_text = optional_string_child ("AnnotationText");
- hash = string_child ("Hash");
- size = int64_child ("Size");
- type = string_child ("Type");
- original_file_name = optional_string_child ("OriginalFileName");
+ id = node->string_child ("Id");
+ annotation_text = node->optional_string_child ("AnnotationText").get_value_or ("");
+ hash = node->string_child ("Hash");
+ size = node->number_child<int64_t> ("Size");
+ type = node->string_child ("Type");
+ original_file_name = node->optional_string_child ("OriginalFileName").get_value_or ("");
}
diff --git a/src/pkl_file.h b/src/parse/pkl.h
index b64da5da..13d87fa1 100644
--- a/src/pkl_file.h
+++ b/src/parse/pkl.h
@@ -17,20 +17,22 @@
*/
-/** @file src/pkl_file.h
+/** @file src/parse/pkl.h
* @brief Classes used to parse a PKL
*/
#include <boost/shared_ptr.hpp>
-#include "xml.h"
+#include "../xml.h"
namespace libdcp {
-class PKLAsset : public XMLNode
+namespace parse {
+
+class PKLAsset
{
public:
PKLAsset () {}
- PKLAsset (xmlpp::Node const * node);
+ PKLAsset (boost::shared_ptr<const cxml::Node>);
std::string id;
std::string annotation_text;
@@ -40,10 +42,10 @@ public:
std::string original_file_name;
};
-class PKLFile : public XMLFile
+class PKL
{
public:
- PKLFile (std::string file);
+ PKL (std::string file);
std::string id;
std::string annotation_text;
@@ -54,3 +56,5 @@ public:
};
}
+
+}
diff --git a/src/parse/subtitle.cc b/src/parse/subtitle.cc
new file mode 100644
index 00000000..612af716
--- /dev/null
+++ b/src/parse/subtitle.cc
@@ -0,0 +1,135 @@
+/*
+ Copyright (C) 2012-2013 Carl Hetherington <cth@carlh.net>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include <boost/shared_ptr.hpp>
+#include <boost/optional.hpp>
+#include "subtitle.h"
+#include "../types.h"
+
+using std::string;
+using std::list;
+using boost::shared_ptr;
+using boost::optional;
+using boost::lexical_cast;
+using namespace libdcp;
+using namespace libdcp::parse;
+
+Font::Font (shared_ptr<const cxml::Node> node)
+{
+ text = node->content ();
+
+ id = node->optional_string_attribute ("Id").get_value_or ("");
+ size = node->optional_number_attribute<int64_t> ("Size").get_value_or (0);
+ italic = node->optional_bool_attribute ("Italic");
+ optional<string> c = node->optional_string_attribute ("Color");
+ if (c) {
+ color = Color (c.get ());
+ }
+ optional<string> const e = node->optional_string_attribute ("Effect");
+ if (e) {
+ effect = string_to_effect (e.get ());
+ }
+ c = node->optional_string_attribute ( "EffectColor");
+ if (c) {
+ effect_color = Color (c.get ());
+ }
+ subtitle_nodes = type_children<Subtitle> (node, "Subtitle");
+ font_nodes = type_children<Font> (node, "Font");
+ text_nodes = type_children<Text> (node, "Text");
+}
+
+Font::Font (list<shared_ptr<Font> > const & font_nodes)
+ : size (0)
+ , italic (false)
+ , color ("FFFFFFFF")
+ , effect_color ("FFFFFFFF")
+{
+ for (list<shared_ptr<Font> >::const_iterator i = font_nodes.begin(); i != font_nodes.end(); ++i) {
+ if (!(*i)->id.empty ()) {
+ id = (*i)->id;
+ }
+ if ((*i)->size != 0) {
+ size = (*i)->size;
+ }
+ if ((*i)->italic) {
+ italic = (*i)->italic.get ();
+ }
+ if ((*i)->color) {
+ color = (*i)->color.get ();
+ }
+ if ((*i)->effect) {
+ effect = (*i)->effect.get ();
+ }
+ if ((*i)->effect_color) {
+ effect_color = (*i)->effect_color.get ();
+ }
+ }
+}
+
+LoadFont::LoadFont (shared_ptr<const cxml::Node> node)
+{
+ id = node->string_attribute ("Id");
+ uri = node->string_attribute ("URI");
+}
+
+
+Subtitle::Subtitle (shared_ptr<const cxml::Node> node)
+{
+ in = Time (node->string_attribute ("TimeIn"));
+ out = Time (node->string_attribute ("TimeOut"));
+ font_nodes = type_children<Font> (node, "Font");
+ text_nodes = type_children<Text> (node, "Text");
+ fade_up_time = fade_time (node, "FadeUpTime");
+ fade_down_time = fade_time (node, "FadeDownTime");
+}
+
+Time
+Subtitle::fade_time (shared_ptr<const cxml::Node> node, string name)
+{
+ string const u = node->optional_string_attribute (name).get_value_or ("");
+ Time t;
+
+ if (u.empty ()) {
+ t = Time (0, 0, 0, 20);
+ } else if (u.find (":") != string::npos) {
+ t = Time (u);
+ } else {
+ t = Time (0, 0, 0, lexical_cast<int> (u));
+ }
+
+ if (t > Time (0, 0, 8, 0)) {
+ t = Time (0, 0, 8, 0);
+ }
+
+ return t;
+}
+
+Text::Text (shared_ptr<const cxml::Node> node)
+ : v_align (CENTER)
+{
+ text = node->content ();
+ v_position = node->number_attribute<float> ("VPosition");
+ optional<string> v = node->optional_string_attribute ("VAlign");
+ if (v) {
+ v_align = string_to_valign (v.get ());
+ }
+
+ font_nodes = type_children<Font> (node, "Font");
+}
+
diff --git a/src/parse/subtitle.h b/src/parse/subtitle.h
new file mode 100644
index 00000000..34321545
--- /dev/null
+++ b/src/parse/subtitle.h
@@ -0,0 +1,93 @@
+/*
+ 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 "../xml.h"
+#include "../dcp_time.h"
+#include "../types.h"
+
+namespace libdcp
+{
+
+namespace parse
+{
+
+class Font;
+
+class Text
+{
+public:
+ Text () {}
+ Text (boost::shared_ptr<const cxml::Node> node);
+
+ float v_position;
+ VAlign v_align;
+ std::string text;
+ std::list<boost::shared_ptr<Font> > font_nodes;
+};
+
+class Subtitle
+{
+public:
+ Subtitle () {}
+ Subtitle (boost::shared_ptr<const cxml::Node> node);
+
+ Time in;
+ Time out;
+ Time fade_up_time;
+ Time fade_down_time;
+ std::list<boost::shared_ptr<Font> > font_nodes;
+ std::list<boost::shared_ptr<Text> > text_nodes;
+
+private:
+ Time fade_time (boost::shared_ptr<const cxml::Node>, std::string name);
+};
+
+class Font
+{
+public:
+ Font () {}
+ Font (boost::shared_ptr<const cxml::Node> node);
+ Font (std::list<boost::shared_ptr<Font> > const & font_nodes);
+
+ std::string text;
+ std::string id;
+ int size;
+ boost::optional<bool> italic;
+ boost::optional<Color> color;
+ boost::optional<Effect> effect;
+ boost::optional<Color> effect_color;
+
+ std::list<boost::shared_ptr<Subtitle> > subtitle_nodes;
+ std::list<boost::shared_ptr<Font> > font_nodes;
+ std::list<boost::shared_ptr<Text> > text_nodes;
+};
+
+class LoadFont
+{
+public:
+ LoadFont () {}
+ LoadFont (boost::shared_ptr<const cxml::Node> node);
+
+ std::string id;
+ std::string uri;
+};
+
+}
+
+}
diff --git a/src/picture_asset.cc b/src/picture_asset.cc
index f783bb39..c6a95c74 100644
--- a/src/picture_asset.cc
+++ b/src/picture_asset.cc
@@ -43,62 +43,69 @@ 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, 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
-PictureAsset::equals (shared_ptr<const Asset> other, EqualityOptions opt, list<string>& notes) const
+PictureAsset::equals (shared_ptr<const Asset> other, EqualityOptions opt, boost::function<void (NoteType, string)> note) const
{
- if (!MXFAsset::equals (other, opt, notes)) {
+ if (!MXFAsset::equals (other, opt, note)) {
return false;
}
ASDCP::JP2K::MXFReader reader_A;
if (ASDCP_FAILURE (reader_A.OpenRead (path().string().c_str()))) {
- throw MXFFileError ("could not open MXF file for reading", path().string());
+ boost::throw_exception (MXFFileError ("could not open MXF file for reading", path().string()));
}
ASDCP::JP2K::MXFReader reader_B;
if (ASDCP_FAILURE (reader_B.OpenRead (other->path().string().c_str()))) {
- throw MXFFileError ("could not open MXF file for reading", path().string());
+ boost::throw_exception (MXFFileError ("could not open MXF file for reading", path().string()));
}
ASDCP::JP2K::PictureDescriptor desc_A;
if (ASDCP_FAILURE (reader_A.FillPictureDescriptor (desc_A))) {
- throw DCPReadError ("could not read video MXF information");
+ boost::throw_exception (DCPReadError ("could not read video MXF information"));
}
ASDCP::JP2K::PictureDescriptor desc_B;
if (ASDCP_FAILURE (reader_B.FillPictureDescriptor (desc_B))) {
- throw DCPReadError ("could not read video MXF information");
+ boost::throw_exception (DCPReadError ("could not read video MXF information"));
}
if (
@@ -122,7 +129,7 @@ PictureAsset::equals (shared_ptr<const Asset> other, EqualityOptions opt, list<s
// desc_A.QuantizationDefault != desc_B.QuantizationDefault
) {
- notes.push_back ("video MXF picture descriptors differ");
+ note (ERROR, "video MXF picture descriptors differ");
return false;
}
@@ -142,15 +149,14 @@ 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, encrypted, size)
{
- _width = width;
- _height = height;
- construct (get_path);
+ construct (get_path, metadata);
}
MonoPictureAsset::MonoPictureAsset (
@@ -159,74 +165,82 @@ 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, 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, 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()))) {
- throw MXFFileError ("could not open MXF file for reading", path().string());
+ boost::throw_exception (MXFFileError ("could not open MXF file for reading", path().string()));
}
ASDCP::JP2K::PictureDescriptor desc;
if (ASDCP_FAILURE (reader.FillPictureDescriptor (desc))) {
- throw DCPReadError ("could not read video MXF information");
+ boost::throw_exception (DCPReadError ("could not read video MXF information"));
}
- _width = desc.StoredWidth;
- _height = desc.StoredHeight;
+ _size.width = desc.StoredWidth;
+ _size.height = desc.StoredHeight;
+ _edit_rate = desc.EditRate.Numerator;
+ assert (desc.EditRate.Denominator == 1);
+ _intrinsic_duration = desc.ContainerDuration;
}
void
-MonoPictureAsset::construct (boost::function<string (int)> get_path)
+MonoPictureAsset::construct (boost::function<string (int)> get_path, MXFMetadata const & metadata)
{
ASDCP::JP2K::CodestreamParser j2k_parser;
ASDCP::JP2K::FrameBuffer frame_buffer (4 * Kumu::Megabyte);
if (ASDCP_FAILURE (j2k_parser.OpenReadFrame (get_path(0).c_str(), frame_buffer))) {
- throw FileError ("could not open JPEG2000 file for reading", get_path (0));
+ boost::throw_exception (FileError ("could not open JPEG2000 file for reading", get_path (0)));
}
ASDCP::JP2K::PictureDescriptor picture_desc;
j2k_parser.FillPictureDescriptor (picture_desc);
- picture_desc.EditRate = ASDCP::Rational (_fps, 1);
+ picture_desc.EditRate = ASDCP::Rational (_edit_rate, 1);
ASDCP::WriterInfo writer_info;
- fill_writer_info (&writer_info);
-
+ fill_writer_info (&writer_info, _uuid, metadata);
+
ASDCP::JP2K::MXFWriter mxf_writer;
- if (ASDCP_FAILURE (mxf_writer.OpenWrite (path().string().c_str(), writer_info, picture_desc))) {
- throw MXFFileError ("could not open MXF file for writing", path().string());
+ if (ASDCP_FAILURE (mxf_writer.OpenWrite (path().string().c_str(), writer_info, picture_desc, 16384, false))) {
+ boost::throw_exception (MXFFileError ("could not open MXF file for writing", path().string()));
}
- for (int i = 0; i < _length; ++i) {
+ for (int i = 0; i < _intrinsic_duration; ++i) {
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, _encryption_context, 0))) {
- throw MiscError ("error in writing video MXF");
+ boost::throw_exception (MXFFileError ("error in writing video MXF", this->path().string()));
}
if (_progress) {
- (*_progress) (0.5 * float (i) / _length);
+ (*_progress) (0.5 * float (i) / _intrinsic_duration);
}
}
if (ASDCP_FAILURE (mxf_writer.Finalize())) {
- throw MiscError ("error in finalising video MXF");
+ boost::throw_exception (MXFFileError ("error in finalising video MXF", path().string()));
}
}
@@ -239,28 +253,29 @@ MonoPictureAsset::path_from_list (int f, vector<string> const & files) const
shared_ptr<const MonoPictureFrame>
MonoPictureAsset::get_frame (int n) const
{
- return shared_ptr<const MonoPictureFrame> (new MonoPictureFrame (path().string(), n + _entry_point));
+ return shared_ptr<const MonoPictureFrame> (new MonoPictureFrame (path().string(), n));
}
bool
-MonoPictureAsset::equals (shared_ptr<const Asset> other, EqualityOptions opt, list<string>& notes) const
+MonoPictureAsset::equals (shared_ptr<const Asset> other, EqualityOptions opt, boost::function<void (NoteType, string)> note) const
{
- if (!PictureAsset::equals (other, opt, notes)) {
+ if (!PictureAsset::equals (other, opt, note)) {
return false;
}
shared_ptr<const MonoPictureAsset> other_picture = dynamic_pointer_cast<const MonoPictureAsset> (other);
assert (other_picture);
- for (int i = 0; i < _length; ++i) {
+ for (int i = 0; i < _intrinsic_duration; ++i) {
+ note (PROGRESS, "Comparing video frame " + lexical_cast<string> (i) + " of " + lexical_cast<string> (_intrinsic_duration));
shared_ptr<const MonoPictureFrame> frame_A = get_frame (i);
shared_ptr<const MonoPictureFrame> frame_B = other_picture->get_frame (i);
if (!frame_buffer_equals (
- i, opt, notes,
- frame_A->j2k_frame()->RoData(), frame_A->j2k_frame()->Size(),
- frame_B->j2k_frame()->RoData(), frame_B->j2k_frame()->Size()
+ i, opt, note,
+ frame_A->j2k_data(), frame_A->j2k_size(),
+ frame_B->j2k_data(), frame_B->j2k_size()
)) {
return false;
}
@@ -270,31 +285,31 @@ MonoPictureAsset::equals (shared_ptr<const Asset> other, EqualityOptions opt, li
}
bool
-StereoPictureAsset::equals (shared_ptr<const Asset> other, EqualityOptions opt, list<string>& notes) const
+StereoPictureAsset::equals (shared_ptr<const Asset> other, EqualityOptions opt, boost::function<void (NoteType, string)> note) const
{
- if (!PictureAsset::equals (other, opt, notes)) {
+ if (!PictureAsset::equals (other, opt, note)) {
return false;
}
shared_ptr<const StereoPictureAsset> other_picture = dynamic_pointer_cast<const StereoPictureAsset> (other);
assert (other_picture);
- for (int i = 0; i < _length; ++i) {
+ for (int i = 0; i < _intrinsic_duration; ++i) {
shared_ptr<const StereoPictureFrame> frame_A = get_frame (i);
shared_ptr<const StereoPictureFrame> frame_B = other_picture->get_frame (i);
if (!frame_buffer_equals (
- i, opt, notes,
- frame_A->j2k_frame()->Left.RoData(), frame_A->j2k_frame()->Left.Size(),
- frame_B->j2k_frame()->Left.RoData(), frame_B->j2k_frame()->Left.Size()
+ i, opt, note,
+ frame_A->left_j2k_data(), frame_A->left_j2k_size(),
+ frame_B->left_j2k_data(), frame_B->left_j2k_size()
)) {
return false;
}
if (!frame_buffer_equals (
- i, opt, notes,
- frame_A->j2k_frame()->Right.RoData(), frame_A->j2k_frame()->Right.Size(),
- frame_B->j2k_frame()->Right.RoData(), frame_B->j2k_frame()->Right.Size()
+ i, opt, note,
+ frame_A->right_j2k_data(), frame_A->right_j2k_size(),
+ frame_B->right_j2k_data(), frame_B->right_j2k_size()
)) {
return false;
}
@@ -305,10 +320,12 @@ StereoPictureAsset::equals (shared_ptr<const Asset> other, EqualityOptions opt,
bool
PictureAsset::frame_buffer_equals (
- int frame, EqualityOptions opt, list<string>& notes, uint8_t const * data_A, unsigned int size_A, uint8_t const * data_B, unsigned int size_B
+ int frame, EqualityOptions opt, boost::function<void (NoteType, string)> note,
+ uint8_t const * data_A, unsigned int size_A, uint8_t const * data_B, unsigned int size_B
) const
{
if (size_A == size_B && memcmp (data_A, data_B, size_A) == 0) {
+ note (NOTE, "J2K identical");
/* Easy result; the J2K data is identical */
return true;
}
@@ -320,7 +337,7 @@ PictureAsset::frame_buffer_equals (
/* Compare them */
if (image_A->numcomps != image_B->numcomps) {
- notes.push_back ("image component counts for frame " + lexical_cast<string>(frame) + " differ");
+ note (ERROR, "image component counts for frame " + lexical_cast<string>(frame) + " differ");
return false;
}
@@ -331,7 +348,7 @@ PictureAsset::frame_buffer_equals (
for (int c = 0; c < image_A->numcomps; ++c) {
if (image_A->comps[c].w != image_B->comps[c].w || image_A->comps[c].h != image_B->comps[c].h) {
- notes.push_back ("image sizes for frame " + lexical_cast<string>(frame) + " differ");
+ note (ERROR, "image sizes for frame " + lexical_cast<string>(frame) + " differ");
return false;
}
@@ -357,11 +374,18 @@ PictureAsset::frame_buffer_equals (
double const std_dev = sqrt (double (total_squared_deviation) / abs_diffs.size());
- if (mean > opt.max_mean_pixel_error || std_dev > opt.max_std_dev_pixel_error) {
- notes.push_back ("mean or standard deviation out of range for " + lexical_cast<string>(frame));
+ note (NOTE, "mean difference " + lexical_cast<string> (mean) + ", deviation " + lexical_cast<string> (std_dev));
+
+ if (mean > opt.max_mean_pixel_error) {
+ note (ERROR, "mean " + lexical_cast<string>(mean) + " out of range " + lexical_cast<string>(opt.max_mean_pixel_error) + " in frame " + lexical_cast<string>(frame));
return false;
}
-
+
+ if (std_dev > opt.max_std_dev_pixel_error) {
+ note (ERROR, "standard deviation " + lexical_cast<string>(std_dev) + " out of range " + lexical_cast<string>(opt.max_std_dev_pixel_error) + " in frame " + lexical_cast<string>(frame));
+ return false;
+ }
+
opj_image_destroy (image_A);
opj_image_destroy (image_B);
@@ -369,27 +393,152 @@ PictureAsset::frame_buffer_equals (
}
-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, false, Size (0, 0))
{
ASDCP::JP2K::MXFSReader reader;
if (ASDCP_FAILURE (reader.OpenRead (path().string().c_str()))) {
- throw MXFFileError ("could not open MXF file for reading", path().string());
+ boost::throw_exception (MXFFileError ("could not open MXF file for reading", path().string()));
}
ASDCP::JP2K::PictureDescriptor desc;
if (ASDCP_FAILURE (reader.FillPictureDescriptor (desc))) {
- throw DCPReadError ("could not read video MXF information");
+ boost::throw_exception (DCPReadError ("could not read video MXF information"));
}
- _width = desc.StoredWidth;
- _height = desc.StoredHeight;
+ _size.width = desc.StoredWidth;
+ _size.height = desc.StoredHeight;
}
shared_ptr<const StereoPictureFrame>
StereoPictureAsset::get_frame (int n) const
{
- return shared_ptr<const StereoPictureFrame> (new StereoPictureFrame (path().string(), n + _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);
+
+ _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
diff --git a/src/picture_asset.h b/src/picture_asset.h
index 96bf5659..08f892ed 100644
--- a/src/picture_asset.h
+++ b/src/picture_asset.h
@@ -17,12 +17,17 @@
*/
+#ifndef LIBDCP_PICTURE_ASSET_H
+#define LIBDCP_PICTURE_ASSET_H
+
/** @file src/picture_asset.h
* @brief An asset made up of JPEG2000 files
*/
#include <openjpeg.h>
#include "mxf_asset.h"
+#include "util.h"
+#include "metadata.h"
namespace libdcp
{
@@ -34,55 +39,127 @@ class StereoPictureFrame;
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, 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 */
class MonoPictureAsset : public PictureAsset
{
public:
- /** 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 files Pathnames of JPEG2000 files, in frame order.
* @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 (
@@ -91,22 +168,22 @@ public:
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 (
@@ -115,31 +192,50 @@ public:
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;
- void construct (boost::function<std::string (int)>);
+ void construct (boost::function<std::string (int)>, MXFMetadata const &);
};
/** A 3D (stereoscopic) picture asset */
class StereoPictureAsset : public PictureAsset
{
public:
- StereoPictureAsset (std::string directory, std::string mxf_name, int fps, int entry_point, int length);
+ StereoPictureAsset (std::string directory, std::string mxf_name, int fps, int intrinsic_duration);
boost::shared_ptr<const StereoPictureFrame> 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;
};
}
+
+#endif
diff --git a/src/picture_frame.cc b/src/picture_frame.cc
index a2b90a0b..7e6bc1f8 100644
--- a/src/picture_frame.cc
+++ b/src/picture_frame.cc
@@ -25,6 +25,9 @@
#include "argb_frame.h"
#include "lut.h"
#include "util.h"
+#include "gamma_lut.h"
+
+#define DCI_GAMMA 2.6
using std::string;
using boost::shared_ptr;
@@ -38,14 +41,14 @@ MonoPictureFrame::MonoPictureFrame (string mxf_path, int n)
{
ASDCP::JP2K::MXFReader reader;
if (ASDCP_FAILURE (reader.OpenRead (mxf_path.c_str()))) {
- throw FileError ("could not open MXF file for reading", mxf_path);
+ boost::throw_exception (FileError ("could not open MXF file for reading", mxf_path));
}
/* XXX: unfortunate guesswork on this buffer size */
_buffer = new ASDCP::JP2K::FrameBuffer (4 * Kumu::Megabyte);
if (ASDCP_FAILURE (reader.ReadFrame (n, *_buffer))) {
- throw DCPReadError ("could not read video frame");
+ boost::throw_exception (DCPReadError ("could not read video frame"));
}
}
@@ -54,6 +57,18 @@ MonoPictureFrame::~MonoPictureFrame ()
delete _buffer;
}
+uint8_t const *
+MonoPictureFrame::j2k_data () const
+{
+ return _buffer->RoData ();
+}
+
+int
+MonoPictureFrame::j2k_size () const
+{
+ return _buffer->Size ();
+}
+
/** @param reduce a factor by which to reduce the resolution
* of the image, expressed as a power of two (pass 0 for no
* reduction).
@@ -64,11 +79,11 @@ MonoPictureFrame::~MonoPictureFrame ()
*
*/
shared_ptr<ARGBFrame>
-MonoPictureFrame::argb_frame (int reduce) const
+MonoPictureFrame::argb_frame (int reduce, float srgb_gamma) const
{
opj_image_t* xyz_frame = decompress_j2k (const_cast<uint8_t*> (_buffer->RoData()), _buffer->Size(), reduce);
assert (xyz_frame->numcomps == 3);
- shared_ptr<ARGBFrame> f = xyz_to_rgb (xyz_frame);
+ shared_ptr<ARGBFrame> f = xyz_to_rgb (xyz_frame, GammaLUT::cache.get (12, DCI_GAMMA), GammaLUT::cache.get (12, 1 / srgb_gamma));
opj_image_destroy (xyz_frame);
return f;
}
@@ -81,14 +96,14 @@ StereoPictureFrame::StereoPictureFrame (string mxf_path, int n)
{
ASDCP::JP2K::MXFSReader reader;
if (ASDCP_FAILURE (reader.OpenRead (mxf_path.c_str()))) {
- throw FileError ("could not open MXF file for reading", mxf_path);
+ boost::throw_exception (FileError ("could not open MXF file for reading", mxf_path));
}
/* XXX: unfortunate guesswork on this buffer size */
_buffer = new ASDCP::JP2K::SFrameBuffer (4 * Kumu::Megabyte);
if (ASDCP_FAILURE (reader.ReadFrame (n, *_buffer))) {
- throw DCPReadError ("could not read video frame");
+ boost::throw_exception (DCPReadError ("could not read video frame"));
}
}
@@ -110,7 +125,7 @@ StereoPictureFrame::~StereoPictureFrame ()
*
*/
shared_ptr<ARGBFrame>
-StereoPictureFrame::argb_frame (Eye eye, int reduce) const
+StereoPictureFrame::argb_frame (Eye eye, int reduce, float srgb_gamma) const
{
opj_image_t* xyz_frame = 0;
switch (eye) {
@@ -123,7 +138,31 @@ StereoPictureFrame::argb_frame (Eye eye, int reduce) const
}
assert (xyz_frame->numcomps == 3);
- shared_ptr<ARGBFrame> f = xyz_to_rgb (xyz_frame);
+ shared_ptr<ARGBFrame> f = xyz_to_rgb (xyz_frame, GammaLUT::cache.get (12, DCI_GAMMA), GammaLUT::cache.get (12, 1 / srgb_gamma));
opj_image_destroy (xyz_frame);
return f;
}
+
+uint8_t const *
+StereoPictureFrame::left_j2k_data () const
+{
+ return _buffer->Left.RoData ();
+}
+
+int
+StereoPictureFrame::left_j2k_size () const
+{
+ return _buffer->Left.Size ();
+}
+
+uint8_t const *
+StereoPictureFrame::right_j2k_data () const
+{
+ return _buffer->Right.RoData ();
+}
+
+int
+StereoPictureFrame::right_j2k_size () const
+{
+ return _buffer->Right.Size ();
+}
diff --git a/src/picture_frame.h b/src/picture_frame.h
index ad51abed..42c5d629 100644
--- a/src/picture_frame.h
+++ b/src/picture_frame.h
@@ -40,10 +40,9 @@ public:
MonoPictureFrame (std::string mxf_path, int n);
~MonoPictureFrame ();
- boost::shared_ptr<ARGBFrame> argb_frame (int reduce = 0) const;
- ASDCP::JP2K::FrameBuffer* j2k_frame () const {
- return _buffer;
- }
+ boost::shared_ptr<ARGBFrame> argb_frame (int reduce = 0, float srgb_gamma = 2.4) const;
+ uint8_t const * j2k_data () const;
+ int j2k_size () const;
private:
ASDCP::JP2K::FrameBuffer* _buffer;
@@ -56,10 +55,11 @@ public:
StereoPictureFrame (std::string mxf_path, int n);
~StereoPictureFrame ();
- boost::shared_ptr<ARGBFrame> argb_frame (Eye eye, int reduce = 0) const;
- ASDCP::JP2K::SFrameBuffer* j2k_frame () const {
- return _buffer;
- }
+ boost::shared_ptr<ARGBFrame> argb_frame (Eye eye, int reduce = 0, float srgb_gamma = 2.4) const;
+ uint8_t const * left_j2k_data () const;
+ int left_j2k_size () const;
+ uint8_t const * right_j2k_data () const;
+ int right_j2k_size () const;
private:
ASDCP::JP2K::SFrameBuffer* _buffer;
diff --git a/src/reel.cc b/src/reel.cc
index d8703dd0..3f077269 100644
--- a/src/reel.cc
+++ b/src/reel.cc
@@ -28,12 +28,12 @@ using namespace std;
using namespace libdcp;
void
-Reel::write_to_cpl (xmlpp::Node* parent) const
+Reel::write_to_cpl (xmlpp::Node* node) const
{
- xmlpp::Element* reel = parent->add_child("Reel");
- reel->add_child("Id")->add_child_text("urn:uuid:" + make_uuid());
- xmlpp::Element* asset_list = reel->add_child("AssetList");
-
+ xmlpp::Node* reel = node->add_child ("Reel");
+ reel->add_child("Id")->add_child_text ("urn:uuid:" + make_uuid());
+ xmlpp::Node* asset_list = reel->add_child ("AssetList");
+
if (_main_picture) {
_main_picture->write_to_cpl (asset_list);
}
@@ -48,32 +48,32 @@ Reel::write_to_cpl (xmlpp::Node* parent) const
}
bool
-Reel::equals (boost::shared_ptr<const Reel> other, EqualityOptions opt, list<string>& notes) const
+Reel::equals (boost::shared_ptr<const Reel> other, EqualityOptions opt, boost::function<void (NoteType, string)> note) const
{
if ((_main_picture && !other->_main_picture) || (!_main_picture && other->_main_picture)) {
- notes.push_back ("reel has different assets");
+ note (ERROR, "reel has different assets");
return false;
}
- if (_main_picture && !_main_picture->equals (other->_main_picture, opt, notes)) {
+ if (_main_picture && !_main_picture->equals (other->_main_picture, opt, note)) {
return false;
}
if ((_main_sound && !other->_main_sound) || (!_main_sound && other->_main_sound)) {
- notes.push_back ("reel has different assets");
+ note (ERROR, "reel has different assets");
return false;
}
- if (_main_sound && !_main_sound->equals (other->_main_sound, opt, notes)) {
+ if (_main_sound && !_main_sound->equals (other->_main_sound, opt, note)) {
return false;
}
if ((_main_subtitle && !other->_main_subtitle) || (!_main_subtitle && other->_main_subtitle)) {
- notes.push_back ("reel has different assets");
+ note (ERROR, "reel has different assets");
return false;
}
- if (_main_subtitle && !_main_subtitle->equals (other->_main_subtitle, opt, notes)) {
+ if (_main_subtitle && !_main_subtitle->equals (other->_main_subtitle, opt, note)) {
return false;
}
diff --git a/src/reel.h b/src/reel.h
index 0ad5ace2..00278a14 100644
--- a/src/reel.h
+++ b/src/reel.h
@@ -19,6 +19,8 @@
#include <list>
#include <boost/shared_ptr.hpp>
+#include <boost/function.hpp>
+#include <libxml++/libxml++.h>
#include "types.h"
namespace xmlpp {
@@ -59,7 +61,7 @@ public:
void write_to_cpl (xmlpp::Node *) const;
- bool equals (boost::shared_ptr<const Reel> other, EqualityOptions opt, std::list<std::string>& notes) const;
+ bool equals (boost::shared_ptr<const Reel> other, EqualityOptions opt, boost::function<void (NoteType, std::string)> notes) const;
private:
boost::shared_ptr<const PictureAsset> _main_picture;
diff --git a/src/sound_asset.cc b/src/sound_asset.cc
index d964a46d..45a65646 100644
--- a/src/sound_asset.cc
+++ b/src/sound_asset.cc
@@ -48,18 +48,17 @@ SoundAsset::SoundAsset (
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
)
- : MXFAsset (directory, mxf_name, progress, fps, 0, length, encrypted)
+ : MXFAsset (directory, mxf_name, progress, fps, intrinsic_duration, encrypted)
, _channels (files.size ())
, _sampling_rate (0)
- , _start_frame (start_frame)
{
assert (_channels);
- construct (boost::bind (&SoundAsset::path_from_channel, this, _1, files));
+ construct (boost::bind (&SoundAsset::path_from_channel, this, _1, files), metadata);
}
SoundAsset::SoundAsset (
@@ -68,39 +67,47 @@ SoundAsset::SoundAsset (
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
)
- : MXFAsset (directory, mxf_name, progress, fps, 0, length, encrypted)
+ : MXFAsset (directory, mxf_name, progress, fps, intrinsic_duration, encrypted)
, _channels (channels)
, _sampling_rate (0)
- , _start_frame (start_frame)
{
assert (_channels);
- construct (get_path);
+ construct (get_path, metadata);
}
-SoundAsset::SoundAsset (string directory, string mxf_name, int fps, int entry_point, int length)
- : MXFAsset (directory, mxf_name, 0, fps, entry_point, length, false)
+SoundAsset::SoundAsset (string directory, string mxf_name)
+ : MXFAsset (directory, mxf_name)
, _channels (0)
- , _start_frame (0)
{
ASDCP::PCM::MXFReader reader;
if (ASDCP_FAILURE (reader.OpenRead (path().string().c_str()))) {
- throw MXFFileError ("could not open MXF file for reading", path().string());
+ boost::throw_exception (MXFFileError ("could not open MXF file for reading", path().string()));
}
-
ASDCP::PCM::AudioDescriptor desc;
if (ASDCP_FAILURE (reader.FillAudioDescriptor (desc))) {
- throw DCPReadError ("could not read audio MXF information");
+ boost::throw_exception (DCPReadError ("could not read audio MXF information"));
}
_sampling_rate = desc.AudioSamplingRate.Numerator / desc.AudioSamplingRate.Denominator;
_channels = desc.ChannelCount;
+ _edit_rate = desc.EditRate.Numerator;
+ assert (desc.EditRate.Denominator == 1);
+ _intrinsic_duration = desc.ContainerDuration;
+}
+
+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
@@ -112,20 +119,20 @@ SoundAsset::path_from_channel (Channel channel, vector<string> const & files)
}
void
-SoundAsset::construct (boost::function<string (Channel)> get_path)
+SoundAsset::construct (boost::function<string (Channel)> get_path, MXFMetadata const & metadata)
{
- ASDCP::Rational asdcp_fps (_fps, 1);
+ ASDCP::Rational asdcp_edit_rate (_edit_rate, 1);
ASDCP::PCM::WAVParser pcm_parser_channel[_channels];
- if (pcm_parser_channel[0].OpenRead (get_path(LEFT).c_str(), asdcp_fps)) {
- throw FileError ("could not open WAV file for reading", get_path(LEFT));
+ if (pcm_parser_channel[0].OpenRead (get_path(LEFT).c_str(), asdcp_edit_rate)) {
+ boost::throw_exception (FileError ("could not open WAV file for reading", get_path(LEFT)));
}
ASDCP::PCM::AudioDescriptor audio_desc;
pcm_parser_channel[0].FillAudioDescriptor (audio_desc);
audio_desc.ChannelCount = 0;
audio_desc.BlockAlign = 0;
- audio_desc.EditRate = asdcp_fps;
+ audio_desc.EditRate = asdcp_edit_rate;
audio_desc.AvgBps = audio_desc.AvgBps * _channels;
Channel channels[] = {
@@ -149,8 +156,8 @@ SoundAsset::construct (boost::function<string (Channel)> get_path)
string const path = get_path (channels[i]);
- if (ASDCP_FAILURE (pcm_parser_channel[i].OpenRead (path.c_str(), asdcp_fps))) {
- throw FileError ("could not open WAV file for reading", path);
+ if (ASDCP_FAILURE (pcm_parser_channel[i].OpenRead (path.c_str(), asdcp_edit_rate))) {
+ boost::throw_exception (FileError ("could not open WAV file for reading", path));
}
pcm_parser_channel[i].FillAudioDescriptor (audio_desc_channel[i]);
@@ -165,28 +172,19 @@ SoundAsset::construct (boost::function<string (Channel)> get_path)
frame_buffer.Size (ASDCP::PCM::CalcFrameBufferSize (audio_desc));
ASDCP::WriterInfo writer_info;
- fill_writer_info (&writer_info);
+ MXFAsset::fill_writer_info (&writer_info, _uuid, metadata);
ASDCP::PCM::MXFWriter mxf_writer;
if (ASDCP_FAILURE (mxf_writer.OpenWrite (path().string().c_str(), writer_info, audio_desc))) {
- throw FileError ("could not open audio MXF for writing", path().string());
- }
-
- /* Skip through up to our _start_frame; this is pretty inefficient... */
- for (int i = 0; i < _start_frame; ++i) {
- for (int j = 0; j < _channels; ++j) {
- if (ASDCP_FAILURE (pcm_parser_channel[j].ReadFrame (frame_buffer_channel[j]))) {
- throw MiscError ("could not read audio frame");
- }
- }
+ boost::throw_exception (FileError ("could not open audio MXF for writing", path().string()));
}
- for (int i = 0; i < _length; ++i) {
+ for (int i = 0; i < _intrinsic_duration; ++i) {
for (int j = 0; j < _channels; ++j) {
memset (frame_buffer_channel[j].Data(), 0, frame_buffer_channel[j].Capacity());
if (ASDCP_FAILURE (pcm_parser_channel[j].ReadFrame (frame_buffer_channel[j]))) {
- throw MiscError ("could not read audio frame");
+ boost::throw_exception (MiscError ("could not read audio frame"));
}
}
@@ -205,58 +203,58 @@ SoundAsset::construct (boost::function<string (Channel)> get_path)
}
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) {
- (*_progress) (0.5 * float (i) / _length);
+ (*_progress) (0.5 * float (i) / _intrinsic_duration);
}
}
if (ASDCP_FAILURE (mxf_writer.Finalize())) {
- throw MiscError ("could not finalise audio MXF");
+ boost::throw_exception (MiscError ("could not finalise audio MXF"));
}
}
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
-SoundAsset::equals (shared_ptr<const Asset> other, EqualityOptions opt, list<string>& notes) const
+SoundAsset::equals (shared_ptr<const Asset> other, EqualityOptions opt, boost::function<void (NoteType, string)> note) const
{
- if (!MXFAsset::equals (other, opt, notes)) {
+ if (!MXFAsset::equals (other, opt, note)) {
return false;
}
ASDCP::PCM::MXFReader reader_A;
if (ASDCP_FAILURE (reader_A.OpenRead (path().string().c_str()))) {
- throw MXFFileError ("could not open MXF file for reading", path().string());
+ boost::throw_exception (MXFFileError ("could not open MXF file for reading", path().string()));
}
ASDCP::PCM::MXFReader reader_B;
if (ASDCP_FAILURE (reader_B.OpenRead (other->path().string().c_str()))) {
- throw MXFFileError ("could not open MXF file for reading", path().string());
+ boost::throw_exception (MXFFileError ("could not open MXF file for reading", path().string()));
}
ASDCP::PCM::AudioDescriptor desc_A;
if (ASDCP_FAILURE (reader_A.FillAudioDescriptor (desc_A))) {
- throw DCPReadError ("could not read audio MXF information");
+ boost::throw_exception (DCPReadError ("could not read audio MXF information"));
}
ASDCP::PCM::AudioDescriptor desc_B;
if (ASDCP_FAILURE (reader_B.FillAudioDescriptor (desc_B))) {
- throw DCPReadError ("could not read audio MXF information");
+ boost::throw_exception (DCPReadError ("could not read audio MXF information"));
}
if (
@@ -272,24 +270,24 @@ SoundAsset::equals (shared_ptr<const Asset> other, EqualityOptions opt, list<str
// desc_A.ChannelFormat != desc_B.ChannelFormat ||
) {
- notes.push_back ("audio MXF picture descriptors differ");
+ note (ERROR, "audio MXF picture descriptors differ");
return false;
}
ASDCP::PCM::FrameBuffer buffer_A (1 * Kumu::Megabyte);
ASDCP::PCM::FrameBuffer buffer_B (1 * Kumu::Megabyte);
- for (int i = 0; i < _length; ++i) {
+ for (int i = 0; i < _intrinsic_duration; ++i) {
if (ASDCP_FAILURE (reader_A.ReadFrame (i, buffer_A))) {
- throw DCPReadError ("could not read audio frame");
+ boost::throw_exception (DCPReadError ("could not read audio frame"));
}
if (ASDCP_FAILURE (reader_B.ReadFrame (i, buffer_B))) {
- throw DCPReadError ("could not read audio frame");
+ boost::throw_exception (DCPReadError ("could not read audio frame"));
}
if (buffer_A.Size() != buffer_B.Size()) {
- notes.push_back ("sizes of audio data for frame " + lexical_cast<string>(i) + " differ");
+ note (ERROR, "sizes of audio data for frame " + lexical_cast<string>(i) + " differ");
return false;
}
@@ -297,7 +295,7 @@ SoundAsset::equals (shared_ptr<const Asset> other, EqualityOptions opt, list<str
for (uint32_t i = 0; i < buffer_A.Size(); ++i) {
int const d = abs (buffer_A.RoData()[i] - buffer_B.RoData()[i]);
if (d > opt.max_audio_sample_error) {
- notes.push_back ("PCM data difference of " + lexical_cast<string> (d));
+ note (ERROR, "PCM data difference of " + lexical_cast<string> (d));
return false;
}
}
@@ -310,7 +308,106 @@ SoundAsset::equals (shared_ptr<const Asset> other, EqualityOptions opt, list<str
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());
+
+ _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
diff --git a/src/sound_asset.h b/src/sound_asset.h
index 80122038..a587b551 100644
--- a/src/sound_asset.h
+++ b/src/sound_asset.h
@@ -26,11 +26,44 @@
#include "mxf_asset.h"
#include "types.h"
+#include "metadata.h"
namespace libdcp
{
-class SoundFrame;
+class SoundFrame;
+class SoundAsset;
+
+class SoundAssetWriter
+{
+public:
+ void write (float const * const *, int);
+ void finalize ();
+
+private:
+ friend class SoundAsset;
+
+ SoundAssetWriter (SoundAsset *, MXFMetadata const &);
+
+ /* no copy construction */
+ SoundAssetWriter (SoundAssetWriter const &);
+ SoundAssetWriter& operator= (SoundAssetWriter const &);
+
+ void write_current_frame ();
+
+ /* do this with an opaque pointer so we don't have to include
+ ASDCP headers
+ */
+
+ struct ASDCPState;
+ boost::shared_ptr<ASDCPState> _state;
+
+ SoundAsset* _asset;
+ bool _finalized;
+ int _frames_written;
+ int _frame_buffer_offset;
+ MXFMetadata _metadata;
+};
/** @brief An asset made up of WAV files */
class SoundAsset : public MXFAsset
@@ -45,7 +78,9 @@ public:
* @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,
@@ -53,9 +88,9 @@ public:
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.
@@ -65,8 +100,7 @@ public:
* @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.
*/
@@ -76,26 +110,33 @@ public:
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 (
std::string directory,
std::string mxf_name,
int fps,
- int entry_point,
- int length
+ int channels,
+ int sampling_rate
);
+
+ boost::shared_ptr<SoundAssetWriter> start_write (MXFMetadata const & metadata = MXFMetadata ());
- /** 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;
boost::shared_ptr<const SoundFrame> get_frame (int n) const;
@@ -109,14 +150,12 @@ public:
private:
std::string key_type () const;
-
- void construct (boost::function<std::string (Channel)> get_path);
+ 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 */
int _channels;
int _sampling_rate;
- int _start_frame;
};
}
diff --git a/src/sound_frame.cc b/src/sound_frame.cc
index ed626f5e..c2a10564 100644
--- a/src/sound_frame.cc
+++ b/src/sound_frame.cc
@@ -29,14 +29,14 @@ SoundFrame::SoundFrame (string mxf_path, int n)
{
ASDCP::PCM::MXFReader reader;
if (ASDCP_FAILURE (reader.OpenRead (mxf_path.c_str()))) {
- throw FileError ("could not open MXF file for reading", mxf_path);
+ boost::throw_exception (FileError ("could not open MXF file for reading", mxf_path));
}
/* XXX: unfortunate guesswork on this buffer size */
_buffer = new ASDCP::PCM::FrameBuffer (1 * Kumu::Megabyte);
if (ASDCP_FAILURE (reader.ReadFrame (n, *_buffer))) {
- throw DCPReadError ("could not read audio frame");
+ boost::throw_exception (DCPReadError ("could not read audio frame"));
}
}
diff --git a/src/subtitle_asset.cc b/src/subtitle_asset.cc
index 998abc07..80cc1ea6 100644
--- a/src/subtitle_asset.cc
+++ b/src/subtitle_asset.cc
@@ -23,6 +23,7 @@
#include <libxml++/nodes/element.h>
#include "subtitle_asset.h"
#include "util.h"
+#include "xml.h"
using std::string;
using std::list;
@@ -31,6 +32,7 @@ using std::ofstream;
using std::stringstream;
using boost::shared_ptr;
using boost::lexical_cast;
+using boost::optional;
using namespace libdcp;
SubtitleAsset::SubtitleAsset (string directory, string xml_file)
@@ -53,7 +55,7 @@ SubtitleAsset::SubtitleAsset (string directory, string movie_title, string langu
void
SubtitleAsset::read_xml (string xml_file)
{
- shared_ptr<XMLFile> xml (new XMLFile (xml_file, "DCSubtitle"));
+ shared_ptr<cxml::File> xml (new cxml::File (xml_file, "DCSubtitle"));
_uuid = xml->string_child ("SubtitleID");
_movie_title = xml->string_child ("MovieTitle");
@@ -62,8 +64,8 @@ SubtitleAsset::read_xml (string xml_file)
xml->ignore_child ("LoadFont");
- list<shared_ptr<FontNode> > font_nodes = xml->type_children<FontNode> ("Font");
- _load_font_nodes = xml->type_children<LoadFontNode> ("LoadFont");
+ list<shared_ptr<libdcp::parse::Font> > font_nodes = type_children<libdcp::parse::Font> (xml, "Font");
+ _load_font_nodes = type_children<libdcp::parse::LoadFont> (xml, "LoadFont");
/* Now make Subtitle objects to represent the raw XML nodes
in a sane way.
@@ -75,17 +77,17 @@ SubtitleAsset::read_xml (string xml_file)
void
SubtitleAsset::examine_font_nodes (
- shared_ptr<XMLFile> xml,
- list<shared_ptr<FontNode> > const & font_nodes,
+ shared_ptr<const cxml::Node> xml,
+ list<shared_ptr<libdcp::parse::Font> > const & font_nodes,
ParseState& parse_state
)
{
- for (list<shared_ptr<FontNode> >::const_iterator i = font_nodes.begin(); i != font_nodes.end(); ++i) {
+ for (list<shared_ptr<libdcp::parse::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<SubtitleNode> >::iterator j = (*i)->subtitle_nodes.begin(); j != (*i)->subtitle_nodes.end(); ++j) {
+ for (list<shared_ptr<libdcp::parse::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);
@@ -101,12 +103,12 @@ SubtitleAsset::examine_font_nodes (
void
SubtitleAsset::examine_text_nodes (
- shared_ptr<XMLFile> xml,
- list<shared_ptr<TextNode> > const & text_nodes,
+ shared_ptr<const cxml::Node> xml,
+ list<shared_ptr<libdcp::parse::Text> > const & text_nodes,
ParseState& parse_state
)
{
- for (list<shared_ptr<TextNode> >::const_iterator i = text_nodes.begin(); i != text_nodes.end(); ++i) {
+ for (list<shared_ptr<libdcp::parse::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);
@@ -128,9 +130,9 @@ SubtitleAsset::maybe_add_subtitle (string text, ParseState const & parse_state)
assert (!parse_state.text_nodes.empty ());
assert (!parse_state.subtitle_nodes.empty ());
- FontNode effective_font (parse_state.font_nodes);
- TextNode effective_text (*parse_state.text_nodes.back ());
- SubtitleNode effective_subtitle (*parse_state.subtitle_nodes.back ());
+ libdcp::parse::Font effective_font (parse_state.font_nodes);
+ libdcp::parse::Text effective_text (*parse_state.text_nodes.back ());
+ libdcp::parse::Subtitle effective_subtitle (*parse_state.subtitle_nodes.back ());
_subtitles.push_back (
shared_ptr<Subtitle> (
@@ -153,107 +155,6 @@ SubtitleAsset::maybe_add_subtitle (string text, ParseState const & parse_state)
);
}
-FontNode::FontNode (xmlpp::Node const * node)
- : XMLNode (node)
-{
- text = content ();
-
- id = optional_string_attribute ("Id");
- size = optional_int64_attribute ("Size");
- italic = optional_bool_attribute ("Italic");
- color = optional_color_attribute ("Color");
- string const e = optional_string_attribute ("Effect");
- if (!e.empty ()) {
- effect = string_to_effect (e);
- }
- effect_color = optional_color_attribute ("EffectColor");
- subtitle_nodes = type_children<SubtitleNode> ("Subtitle");
- font_nodes = type_children<FontNode> ("Font");
- text_nodes = type_children<TextNode> ("Text");
-}
-
-FontNode::FontNode (list<shared_ptr<FontNode> > const & font_nodes)
- : size (0)
- , italic (false)
- , color ("FFFFFFFF")
- , effect_color ("FFFFFFFF")
-{
- for (list<shared_ptr<FontNode> >::const_iterator i = font_nodes.begin(); i != font_nodes.end(); ++i) {
- if (!(*i)->id.empty ()) {
- id = (*i)->id;
- }
- if ((*i)->size != 0) {
- size = (*i)->size;
- }
- if ((*i)->italic) {
- italic = (*i)->italic.get ();
- }
- if ((*i)->color) {
- color = (*i)->color.get ();
- }
- if ((*i)->effect) {
- effect = (*i)->effect.get ();
- }
- if ((*i)->effect_color) {
- effect_color = (*i)->effect_color.get ();
- }
- }
-}
-
-LoadFontNode::LoadFontNode (xmlpp::Node const * node)
- : XMLNode (node)
-{
- id = string_attribute ("Id");
- uri = string_attribute ("URI");
-}
-
-
-SubtitleNode::SubtitleNode (xmlpp::Node const * node)
- : XMLNode (node)
-{
- in = time_attribute ("TimeIn");
- out = time_attribute ("TimeOut");
- font_nodes = type_children<FontNode> ("Font");
- text_nodes = type_children<TextNode> ("Text");
- fade_up_time = fade_time ("FadeUpTime");
- fade_down_time = fade_time ("FadeDownTime");
-}
-
-Time
-SubtitleNode::fade_time (string name)
-{
- string const u = optional_string_attribute (name);
- Time t;
-
- if (u.empty ()) {
- t = Time (0, 0, 0, 20);
- } else if (u.find (":") != string::npos) {
- t = Time (u);
- } else {
- t = Time (0, 0, 0, lexical_cast<int> (u));
- }
-
- if (t > Time (0, 0, 8, 0)) {
- t = Time (0, 0, 8, 0);
- }
-
- return t;
-}
-
-TextNode::TextNode (xmlpp::Node const * node)
- : XMLNode (node)
- , v_align (CENTER)
-{
- text = content ();
- v_position = float_attribute ("VPosition");
- string const v = optional_string_attribute ("VAlign");
- if (!v.empty ()) {
- v_align = string_to_valign (v);
- }
-
- font_nodes = type_children<FontNode> ("Font");
-}
-
list<shared_ptr<Subtitle> >
SubtitleAsset::subtitles_at (Time t) const
{
@@ -270,7 +171,7 @@ SubtitleAsset::subtitles_at (Time t) const
std::string
SubtitleAsset::font_id_to_name (string id) const
{
- list<shared_ptr<LoadFontNode> >::const_iterator i = _load_font_nodes.begin();
+ list<shared_ptr<libdcp::parse::LoadFont> >::const_iterator i = _load_font_nodes.begin();
while (i != _load_font_nodes.end() && (*i)->id != id) {
++i;
}
@@ -376,14 +277,15 @@ SubtitleAsset::add (shared_ptr<Subtitle> s)
}
void
-SubtitleAsset::write_to_cpl (xmlpp::Element* parent) const
+SubtitleAsset::write_to_cpl (xmlpp::Node* node) const
{
/* XXX: should EditRate, Duration and IntrinsicDuration be in here? */
- xmlpp::Element* main_subtitle = parent->add_child("MainSubtitle");
- main_subtitle->add_child("Id")->add_child_text("urn:uuid:" + _uuid);
- main_subtitle->add_child("AnnotationText")->add_child_text(_file_name);
- main_subtitle->add_child("EntryPoint")->add_child_text("0");
+ xmlpp::Node* ms = node->add_child ("MainSubtitle");
+ ms->add_child("Id")->add_child_text("urn:uuid:" + _uuid);
+ ms->add_child("AnnotationText")->add_child_text (_file_name);
+ /* XXX */
+ ms->add_child("EntryPoint")->add_child_text ("0");
}
struct SubtitleSorter {
@@ -398,26 +300,30 @@ struct SubtitleSorter {
void
SubtitleAsset::write_xml () const
{
- ofstream f (path().string().c_str());
- write_xml (f);
+ ofstream s (path().string().c_str());
+ write_xml (s);
}
void
SubtitleAsset::write_xml (ostream& s) const
{
- s << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
- << "<DCSubtitle Version=\"1.0\">\n"
- << " <SubtitleID>" << _uuid << "</SubtitleID>\n"
- << " <MovieTitle>" << _movie_title << "</MovieTitle>\n"
- << " <ReelNumber>" << _reel_number << "</ReelNumber>\n"
- << " <Language>" << _language << "</Language>\n";
+ xmlpp::Document doc;
+ xmlpp::Element* root = doc.create_root_node ("DCSubtitle");
+ root->set_attribute ("Version", "1.0");
+
+ root->add_child("SubtitleID")->add_child_text (_uuid);
+ 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) {
- throw MiscError ("multiple LoadFont nodes not supported");
+ boost::throw_exception (MiscError ("multiple LoadFont nodes not supported"));
}
if (!_load_font_nodes.empty ()) {
- s << " <LoadFont Id=\"" << _load_font_nodes.front()->id << "\" URI=\"" << _load_font_nodes.front()->uri << "\"/>\n";
+ 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<Subtitle> > sorted = _subtitles;
@@ -428,7 +334,6 @@ SubtitleAsset::write_xml (ostream& s) const
/* XXX: multiple fonts not supported */
/* XXX: script, underlined, weight not supported */
- bool first = true;
bool italic = false;
Color color;
int size = 0;
@@ -440,66 +345,61 @@ SubtitleAsset::write_xml (ostream& s) const
Time last_fade_up_time;
Time last_fade_down_time;
+ xmlpp::Element* font = 0;
+ xmlpp::Element* subtitle = 0;
+
for (list<shared_ptr<Subtitle> >::iterator i = sorted.begin(); i != sorted.end(); ++i) {
/* We will start a new <Font>...</Font> whenever some font property changes.
- I suppose should really make an optimal hierarchy of <Font> tags, but
+ I suppose we should really make an optimal hierarchy of <Font> tags, but
that seems hard.
*/
- bool const font_changed = first ||
+ bool const font_changed =
italic != (*i)->italic() ||
color != (*i)->color() ||
size != (*i)->size() ||
effect != (*i)->effect() ||
effect_color != (*i)->effect_color();
- stringstream a;
if (font_changed) {
italic = (*i)->italic ();
- a << "Italic=\"" << (italic ? "yes" : "no") << "\" ";
color = (*i)->color ();
- a << "Color=\"" << color.to_argb_string() << "\" ";
size = (*i)->size ();
- a << "Size=\"" << size << "\" ";
effect = (*i)->effect ();
- a << "Effect=\"" << effect_to_string(effect) << "\" ";
effect_color = (*i)->effect_color ();
- a << "EffectColor=\"" << effect_color.to_argb_string() << "\" ";
- a << "Script=\"normal\" Underlined=\"no\" Weight=\"normal\"";
}
- if (first || font_changed ||
+ 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()
)) {
- if (!first) {
- s << " </Subtitle>\n";
- }
-
- if (font_changed) {
- if (!first) {
- s << " </Font>\n";
- }
-
- string id = "theFontId";
- if (!_load_font_nodes.empty()) {
- id = _load_font_nodes.front()->id;
- }
-
- s << " <Font Id=\"" << id << "\" " << a.str() << ">\n";
- }
-
- s << " <Subtitle "
- << "SpotNumber=\"" << spot_number++ << "\" "
- << "TimeIn=\"" << (*i)->in().to_string() << "\" "
- << "TimeOut=\"" << (*i)->out().to_string() << "\" "
- << "FadeUpTime=\"" << (*i)->fade_up_time().to_ticks() << "\" "
- << "FadeDownTime=\"" << (*i)->fade_down_time().to_ticks() << "\""
- << ">\n";
+ 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 ();
@@ -507,23 +407,12 @@ SubtitleAsset::write_xml (ostream& s) const
last_fade_down_time = (*i)->fade_down_time ();
}
- s << " <Text "
- << "VAlign=\"" << valign_to_string ((*i)->v_align()) << "\" "
- << "VPosition=\"" << (*i)->v_position() << "\""
- << ">" << escape ((*i)->text()) << "</Text>\n";
-
- first = false;
+ 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());
}
- s << " </Subtitle>\n";
- s << " </Font>\n";
- s << "</DCSubtitle>\n";
+ doc.write_to_stream_formatted (s, "UTF-8");
}
-/** XXX: Another reason why we should be writing with libxml++ */
-string
-SubtitleAsset::escape (string s) const
-{
- boost::replace_all (s, "&", "&amp;");
- return s;
-}
diff --git a/src/subtitle_asset.h b/src/subtitle_asset.h
index f4a57151..2da1ce7b 100644
--- a/src/subtitle_asset.h
+++ b/src/subtitle_asset.h
@@ -20,71 +20,11 @@
#include "asset.h"
#include "xml.h"
#include "dcp_time.h"
+#include "parse/subtitle.h"
namespace libdcp
{
-class FontNode;
-
-class TextNode : public XMLNode
-{
-public:
- TextNode () {}
- TextNode (xmlpp::Node const * node);
-
- float v_position;
- VAlign v_align;
- std::string text;
- std::list<boost::shared_ptr<FontNode> > font_nodes;
-};
-
-class SubtitleNode : public XMLNode
-{
-public:
- SubtitleNode () {}
- SubtitleNode (xmlpp::Node const * node);
-
- Time in;
- Time out;
- Time fade_up_time;
- Time fade_down_time;
- std::list<boost::shared_ptr<FontNode> > font_nodes;
- std::list<boost::shared_ptr<TextNode> > text_nodes;
-
-private:
- Time fade_time (std::string name);
-};
-
-class FontNode : public XMLNode
-{
-public:
- FontNode () {}
- FontNode (xmlpp::Node const * node);
- FontNode (std::list<boost::shared_ptr<FontNode> > const & font_nodes);
-
- std::string text;
- std::string id;
- int size;
- boost::optional<bool> italic;
- boost::optional<Color> color;
- boost::optional<Effect> effect;
- boost::optional<Color> effect_color;
-
- std::list<boost::shared_ptr<SubtitleNode> > subtitle_nodes;
- std::list<boost::shared_ptr<FontNode> > font_nodes;
- std::list<boost::shared_ptr<TextNode> > text_nodes;
-};
-
-class LoadFontNode : public XMLNode
-{
-public:
- LoadFontNode () {}
- LoadFontNode (xmlpp::Node const * node);
-
- std::string id;
- std::string uri;
-};
-
class Subtitle
{
public:
@@ -183,14 +123,10 @@ public:
SubtitleAsset (std::string directory, std::string xml_file);
SubtitleAsset (std::string directory, std::string movie_title, std::string language);
- /** Write details of the asset to a CPL AssetList node.
- * @param p Parent node.
- */
- void write_to_cpl (xmlpp::Element* p) const;
-
- virtual bool equals (boost::shared_ptr<const Asset>, EqualityOptions, std::list<std::string>& notes) const {
+ void write_to_cpl (xmlpp::Node *) const;
+ virtual bool equals (boost::shared_ptr<const Asset>, EqualityOptions, boost::function<void (NoteType, std::string)> note) const {
/* XXX */
- notes.push_back ("subtitle assets not compared yet");
+ note (ERROR, "subtitle assets not compared yet");
return true;
}
@@ -207,29 +143,28 @@ public:
void read_xml (std::string);
void write_xml () const;
- void write_xml (std::ostream& s) const;
+ void write_xml (std::ostream &) const;
private:
std::string font_id_to_name (std::string id) const;
- std::string escape (std::string) const;
struct ParseState {
- std::list<boost::shared_ptr<FontNode> > font_nodes;
- std::list<boost::shared_ptr<TextNode> > text_nodes;
- std::list<boost::shared_ptr<SubtitleNode> > subtitle_nodes;
+ std::list<boost::shared_ptr<parse::Font> > font_nodes;
+ std::list<boost::shared_ptr<parse::Text> > text_nodes;
+ std::list<boost::shared_ptr<parse::Subtitle> > subtitle_nodes;
};
void maybe_add_subtitle (std::string text, ParseState const & parse_state);
void examine_font_nodes (
- boost::shared_ptr<XMLFile> xml,
- std::list<boost::shared_ptr<FontNode> > const & font_nodes,
+ boost::shared_ptr<const cxml::Node> xml,
+ std::list<boost::shared_ptr<parse::Font> > const & font_nodes,
ParseState& parse_state
);
void examine_text_nodes (
- boost::shared_ptr<XMLFile> xml,
- std::list<boost::shared_ptr<TextNode> > const & text_nodes,
+ boost::shared_ptr<const cxml::Node> xml,
+ std::list<boost::shared_ptr<parse::Text> > const & text_nodes,
ParseState& parse_state
);
@@ -237,7 +172,7 @@ private:
/* strangely, this is sometimes a string */
std::string _reel_number;
std::string _language;
- std::list<boost::shared_ptr<LoadFontNode> > _load_font_nodes;
+ std::list<boost::shared_ptr<parse::LoadFont> > _load_font_nodes;
std::list<boost::shared_ptr<Subtitle> > _subtitles;
bool _need_sort;
diff --git a/src/test_mode.cc b/src/test_mode.cc
deleted file mode 100644
index bfe10fee..00000000
--- a/src/test_mode.cc
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- Copyright (C) 2012 Carl Hetherington <cth@carlh.net>
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
-*/
-
-/** @file src/test_mode.cc
- * @brief A method to enable test mode for libdcp.
- */
-
-#include "KM_prng.h"
-#include "test_mode.h"
-#include "metadata.h"
-
-/** Calling this will seed the random number generator used to
- * generate UUIDs with a known value, and set the DCP issue
- * date to 1st January 2012 at midnight. This means that
- * two runs of libdcp with the same inputs will produce
- * the same output.
- */
-
-void
-libdcp::enable_test_mode ()
-{
- Kumu::libdcp_test = true;
- Metadata::instance()->issue_date = "2012-01-01T00:00:00+00:00";
-
- /* Remove version strings */
- Metadata::instance()->issuer = "libdcp-test";
- Metadata::instance()->creator = "libdcp-test";
- Metadata::instance()->product_version = "test";
-}
diff --git a/src/types.cc b/src/types.cc
index ac01ae45..693b9ab2 100644
--- a/src/types.cc
+++ b/src/types.cc
@@ -15,7 +15,7 @@ Fraction::Fraction (string s)
vector<string> b;
split (b, s, is_any_of (" "));
if (b.size() != 2) {
- throw XMLError ("malformed fraction " + s + " in XML node");
+ boost::throw_exception (XMLError ("malformed fraction " + s + " in XML node"));
}
numerator = lexical_cast<int> (b[0]);
denominator = lexical_cast<int> (b[1]);
@@ -57,7 +57,7 @@ Color::Color (string argb_hex)
{
int alpha;
if (sscanf (argb_hex.c_str(), "%2x%2x%2x%2x", &alpha, &r, &g, &b) < 4) {
- throw XMLError ("could not parse colour string");
+ boost::throw_exception (XMLError ("could not parse colour string"));
}
}
@@ -118,7 +118,7 @@ libdcp::effect_to_string (Effect e)
return "shadow";
}
- throw MiscError ("unknown effect type");
+ boost::throw_exception (MiscError ("unknown effect type"));
}
Effect
@@ -132,7 +132,7 @@ libdcp::string_to_effect (string s)
return SHADOW;
}
- throw DCPReadError ("unknown subtitle effect type");
+ boost::throw_exception (DCPReadError ("unknown subtitle effect type"));
}
string
@@ -147,7 +147,7 @@ libdcp::valign_to_string (VAlign v)
return "bottom";
}
- throw MiscError ("unknown valign type");
+ boost::throw_exception (MiscError ("unknown valign type"));
}
VAlign
@@ -161,7 +161,7 @@ libdcp::string_to_valign (string s)
return BOTTOM;
}
- throw DCPReadError ("unknown subtitle valign type");
+ boost::throw_exception (DCPReadError ("unknown subtitle valign type"));
}
diff --git a/src/types.h b/src/types.h
index f1b5f640..edabb9e2 100644
--- a/src/types.h
+++ b/src/types.h
@@ -24,6 +24,8 @@
#ifndef LIBDCP_TYPES_H
#define LIBDCP_TYPES_H
+#include <string>
+
namespace libdcp
{
@@ -98,11 +100,24 @@ struct EqualityOptions {
: max_mean_pixel_error (0)
, max_std_dev_pixel_error (0)
, max_audio_sample_error (0)
+ , cpl_names_can_differ (false)
+ , mxf_names_can_differ (false)
{}
double max_mean_pixel_error;
double max_std_dev_pixel_error;
int max_audio_sample_error;
+ bool cpl_names_can_differ;
+ bool mxf_names_can_differ;
+};
+
+/* Win32 defines this */
+#undef ERROR
+
+enum NoteType {
+ PROGRESS,
+ ERROR,
+ NOTE
};
/** @class Color
diff --git a/src/util.cc b/src/util.cc
index 8277b2bf..0c63c305 100644
--- a/src/util.cc
+++ b/src/util.cc
@@ -26,6 +26,7 @@
#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>
@@ -39,8 +40,8 @@
#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;
@@ -49,6 +50,7 @@ using std::min;
using std::max;
using std::list;
using boost::shared_ptr;
+using boost::lexical_cast;
using namespace libdcp;
/** Create a UUID.
@@ -74,7 +76,7 @@ libdcp::make_digest (string filename)
{
Kumu::FileReader reader;
if (ASDCP_FAILURE (reader.OpenRead (filename.c_str ()))) {
- throw FileError ("could not open file to compute digest", filename);
+ boost::throw_exception (FileError ("could not open file to compute digest", filename));
}
SHA_CTX sha;
@@ -89,7 +91,7 @@ libdcp::make_digest (string filename)
if (r == Kumu::RESULT_ENDOFFILE) {
break;
} else if (ASDCP_FAILURE (r)) {
- throw FileError ("could not read file to compute digest", filename);
+ boost::throw_exception (FileError ("could not read file to compute digest", filename));
}
SHA1_Update (&sha, read_buffer.Data(), read);
@@ -194,7 +196,7 @@ libdcp::decompress_j2k (uint8_t* data, int64_t size, int reduce)
if (!image) {
opj_destroy_decompress (decoder);
opj_cio_close (cio);
- throw DCPReadError ("could not decode JPEG2000 codestream");
+ boost::throw_exception (DCPReadError ("could not decode JPEG2000 codestream of " + lexical_cast<string> (size) + " bytes."));
}
opj_cio_close (cio);
@@ -209,8 +211,22 @@ libdcp::decompress_j2k (uint8_t* data, int64_t size, int reduce)
* @return RGB image.
*/
shared_ptr<ARGBFrame>
-libdcp::xyz_to_rgb (opj_image_t* xyz_frame)
+libdcp::xyz_to_rgb (opj_image_t* xyz_frame, shared_ptr<const GammaLUT> lut_in, shared_ptr<const GammaLUT> lut_out)
{
+ float const dci_coefficient = 48.0 / 52.37;
+
+ /* sRGB color matrix for XYZ -> RGB. This is the same as the one used by the Fraunhofer
+ EasyDCP player, I think.
+ */
+
+ float const colour_matrix[3][3] = {
+ { 3.24096989631653, -1.5373831987381, -0.498610764741898 },
+ { -0.96924364566803, 1.87596750259399, 0.0415550582110882 },
+ { 0.0556300804018974, -0.203976958990097, 1.05697154998779 }
+ };
+
+ int const max_colour = pow (2, lut_out->bit_depth()) - 1;
+
struct {
double x, y, z;
} s;
@@ -223,7 +239,7 @@ libdcp::xyz_to_rgb (opj_image_t* xyz_frame)
int* xyz_y = xyz_frame->comps[1].data;
int* xyz_z = xyz_frame->comps[2].data;
- shared_ptr<ARGBFrame> argb_frame (new ARGBFrame (xyz_frame->x1, xyz_frame->y1));
+ shared_ptr<ARGBFrame> argb_frame (new ARGBFrame (Size (xyz_frame->x1, xyz_frame->y1)));
uint8_t* argb = argb_frame->data ();
@@ -234,19 +250,19 @@ libdcp::xyz_to_rgb (opj_image_t* xyz_frame)
assert (*xyz_x >= 0 && *xyz_y >= 0 && *xyz_z >= 0 && *xyz_x < 4096 && *xyz_x < 4096 && *xyz_z < 4096);
/* In gamma LUT */
- s.x = lut_in[*xyz_x++];
- s.y = lut_in[*xyz_y++];
- s.z = lut_in[*xyz_z++];
-
+ s.x = lut_in->lut()[*xyz_x++];
+ s.y = lut_in->lut()[*xyz_y++];
+ s.z = lut_in->lut()[*xyz_z++];
+
/* DCI companding */
- s.x /= DCI_COEFFICIENT;
- s.y /= DCI_COEFFICIENT;
- s.z /= DCI_COEFFICIENT;
+ s.x /= dci_coefficient;
+ s.y /= dci_coefficient;
+ s.z /= dci_coefficient;
/* XYZ to RGB */
- d.r = ((s.x * color_matrix[0][0]) + (s.y * color_matrix[0][1]) + (s.z * color_matrix[0][2]));
- d.g = ((s.x * color_matrix[1][0]) + (s.y * color_matrix[1][1]) + (s.z * color_matrix[1][2]));
- d.b = ((s.x * color_matrix[2][0]) + (s.y * color_matrix[2][1]) + (s.z * color_matrix[2][2]));
+ d.r = ((s.x * colour_matrix[0][0]) + (s.y * colour_matrix[0][1]) + (s.z * colour_matrix[0][2]));
+ d.g = ((s.x * colour_matrix[1][0]) + (s.y * colour_matrix[1][1]) + (s.z * colour_matrix[1][2]));
+ d.b = ((s.x * colour_matrix[2][0]) + (s.y * colour_matrix[2][1]) + (s.z * colour_matrix[2][2]));
d.r = min (d.r, 1.0);
d.r = max (d.r, 0.0);
@@ -258,9 +274,9 @@ libdcp::xyz_to_rgb (opj_image_t* xyz_frame)
d.b = max (d.b, 0.0);
/* Out gamma LUT */
- *argb_line++ = lut_out[(int) (d.b * COLOR_DEPTH)];
- *argb_line++ = lut_out[(int) (d.g * COLOR_DEPTH)];
- *argb_line++ = lut_out[(int) (d.r * COLOR_DEPTH)];
+ *argb_line++ = lut_out->lut()[(int) (d.b * max_colour)] * 0xff;
+ *argb_line++ = lut_out->lut()[(int) (d.g * max_colour)] * 0xff;
+ *argb_line++ = lut_out->lut()[(int) (d.r * max_colour)] * 0xff;
*argb_line++ = 0xff;
}
@@ -412,3 +428,13 @@ libdcp::sign (xmlpp::Element* parent, CertificateChain const & certificates, str
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);
+}
+
diff --git a/src/util.h b/src/util.h
index ddc5a322..8b5f76bb 100644
--- a/src/util.h
+++ b/src/util.h
@@ -17,12 +17,16 @@
*/
+#ifndef LIBDCP_UTIL_H
+#define LIBDCP_UTIL_H
+
/** @file src/util.h
* @brief Utility methods.
*/
#include <string>
#include <stdint.h>
+#include <boost/shared_ptr.hpp>
#include <openjpeg.h>
#include "types.h"
@@ -34,14 +38,34 @@ 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 ();
@@ -50,3 +74,5 @@ extern void add_signature_value (xmlpp::Element* parent, CertificateChain const
extern void add_signer (xmlpp::Element* parent, CertificateChain const & certificates, std::string const & ns);
}
+
+#endif
diff --git a/src/wscript b/src/wscript
index 0922cb56..93d6d5c1 100644
--- a/src/wscript
+++ b/src/wscript
@@ -1,5 +1,5 @@
def build(bld):
- if bld.env.STATIC_LIBDCP:
+ if bld.env.STATIC:
obj = bld(features = 'cxx cxxstlib')
else:
obj = bld(features = 'cxx cxxshlib')
@@ -7,37 +7,38 @@ def build(bld):
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 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
picture_frame.cc
- pkl_file.cc
reel.cc
argb_frame.cc
sound_asset.cc
sound_frame.cc
subtitle_asset.cc
- test_mode.cc
types.cc
util.cc
version.cc
- xml.cc
+ parse/asset_map.cc
+ parse/cpl.cc
+ parse/pkl.cc
+ parse/subtitle.cc
"""
headers = """
asset.h
certificates.h
+ cpl.h
crypt_chain.h
dcp.h
dcp_time.h
@@ -51,13 +52,12 @@ def build(bld):
sound_asset.h
sound_frame.h
subtitle_asset.h
- test_mode.h
types.h
util.h
version.h
- xml.h
"""
bld.install_files('${PREFIX}/include/libdcp', headers)
- if bld.env.STATIC_LIBDCP:
+ if bld.env.STATIC:
bld.install_files('${PREFIX}/lib', 'libdcp.a')
+
diff --git a/src/xml.cc b/src/xml.cc
deleted file mode 100644
index 22e91ac0..00000000
--- a/src/xml.cc
+++ /dev/null
@@ -1,263 +0,0 @@
-#include <sstream>
-#include <iostream>
-#include <boost/lexical_cast.hpp>
-#include <boost/filesystem.hpp>
-#include <boost/algorithm/string.hpp>
-#include <libxml++/libxml++.h>
-#include "xml.h"
-#include "exceptions.h"
-#include "util.h"
-
-using namespace std;
-using namespace boost;
-using namespace libdcp;
-
-XMLNode::XMLNode ()
- : _node (0)
-{
-
-}
-
-XMLNode::XMLNode (xmlpp::Node const * node)
- : _node (node)
-{
-
-}
-
-xmlpp::Node *
-XMLNode::node_child (string name)
-{
- list<xmlpp::Node*> n = node_children (name);
- if (n.size() > 1) {
- throw XMLError ("duplicate XML tag " + name);
- } else if (n.empty ()) {
- throw XMLError ("missing XML tag " + name + " in " + _node->get_name());
- }
-
- return n.front ();
-}
-
-list<xmlpp::Node*>
-XMLNode::node_children (string name)
-{
- /* XXX: using find / get_path should work here, but I can't follow
- how get_path works.
- */
-
- xmlpp::Node::NodeList c = _node->get_children ();
-
- list<xmlpp::Node*> n;
- for (xmlpp::Node::NodeList::iterator i = c.begin (); i != c.end(); ++i) {
- if ((*i)->get_name() == name) {
- n.push_back (*i);
- }
- }
-
- _taken.push_back (name);
- return n;
-}
-
-string
-XMLNode::string_child (string name)
-{
- return XMLNode (node_child (name)).content ();
-}
-
-string
-XMLNode::optional_string_child (string name)
-{
- list<xmlpp::Node*> nodes = node_children (name);
- if (nodes.size() > 2) {
- throw XMLError ("duplicate XML tag " + name);
- }
-
- if (nodes.empty ()) {
- return "";
- }
-
- return string_child (name);
-}
-
-ContentKind
-XMLNode::kind_child (string name)
-{
- return content_kind_from_string (string_child (name));
-}
-
-Fraction
-XMLNode::fraction_child (string name)
-{
- return Fraction (string_child (name));
-}
-
-int64_t
-XMLNode::int64_child (string name)
-{
- string s = string_child (name);
- erase_all (s, " ");
- return lexical_cast<int64_t> (s);
-}
-
-int64_t
-XMLNode::optional_int64_child (string name)
-{
- list<xmlpp::Node*> nodes = node_children (name);
- if (nodes.size() > 2) {
- throw XMLError ("duplicate XML tag " + name);
- }
-
- if (nodes.empty ()) {
- return 0;
- }
-
- return int64_child (name);
-}
-
-float
-XMLNode::float_child (string name)
-{
- return lexical_cast<float> (string_child (name));
-}
-
-void
-XMLNode::ignore_child (string name)
-{
- _taken.push_back (name);
-}
-
-Time
-XMLNode::time_attribute (string name)
-{
- return Time (string_attribute (name));
-}
-
-string
-XMLNode::string_attribute (string name)
-{
- xmlpp::Element const * e = dynamic_cast<const xmlpp::Element *> (_node);
- if (!e) {
- throw XMLError ("missing attribute");
- }
-
- xmlpp::Attribute* a = e->get_attribute (name);
- if (!a) {
- throw XMLError ("missing attribute");
- }
-
- return a->get_value ();
-}
-
-string
-XMLNode::optional_string_attribute (string name)
-{
- xmlpp::Element const * e = dynamic_cast<const xmlpp::Element *> (_node);
- if (!e) {
- return "";
- }
-
- xmlpp::Attribute* a = e->get_attribute (name);
- if (!a) {
- return "";
- }
-
- return a->get_value ();
-}
-
-float
-XMLNode::float_attribute (string name)
-{
- return lexical_cast<float> (string_attribute (name));
-}
-
-int64_t
-XMLNode::int64_attribute (string name)
-{
- return lexical_cast<int64_t> (string_attribute (name));
-}
-
-int64_t
-XMLNode::optional_int64_attribute (string name)
-{
- string const s = optional_string_attribute (name);
- if (s.empty ()) {
- return 0;
- }
-
- return lexical_cast<int64_t> (s);
-}
-
-optional<bool>
-XMLNode::optional_bool_attribute (string name)
-{
- string const s = optional_string_attribute (name);
- if (s.empty ()) {
- return optional<bool> ();
- }
-
- if (s == "1" || s == "yes") {
- return optional<bool> (true);
- }
-
- return optional<bool> (false);
-}
-
-optional<Color>
-XMLNode::optional_color_attribute (string name)
-{
- string const s = optional_string_attribute (name);
- if (s.empty ()) {
- return optional<Color> ();
- }
-
- return optional<Color> (Color (s));
-}
-
-void
-XMLNode::done ()
-{
- xmlpp::Node::NodeList c = _node->get_children ();
- for (xmlpp::Node::NodeList::iterator i = c.begin(); i != c.end(); ++i) {
- if (dynamic_cast<xmlpp::Element *> (*i) && find (_taken.begin(), _taken.end(), (*i)->get_name()) == _taken.end ()) {
- throw XMLError ("unexpected XML node " + (*i)->get_name());
- }
- }
-}
-
-string
-XMLNode::content ()
-{
- string content;
-
- xmlpp::Node::NodeList c = _node->get_children ();
- for (xmlpp::Node::NodeList::const_iterator i = c.begin(); i != c.end(); ++i) {
- xmlpp::ContentNode const * v = dynamic_cast<xmlpp::ContentNode const *> (*i);
- if (v) {
- content += v->get_content ();
- }
- }
-
- return content;
-}
-
-XMLFile::XMLFile (string file, string root_name)
-{
- if (!filesystem::exists (file)) {
- throw FileError ("XML file does not exist", file);
- }
-
- _parser = new xmlpp::DomParser;
- _parser->parse_file (file);
- if (!_parser) {
- throw XMLError ("could not parse XML");
- }
-
- _node = _parser->get_document()->get_root_node ();
- if (_node->get_name() != root_name) {
- throw XMLError ("unrecognised root node");
- }
-}
-
-XMLFile::~XMLFile ()
-{
- delete _parser;
-}
diff --git a/src/xml.h b/src/xml.h
index 6fc0d0fa..5978ff7e 100644
--- a/src/xml.h
+++ b/src/xml.h
@@ -1,103 +1,91 @@
-#ifndef LIBDCP_XML_H
-#define LIBDCP_XML_H
+/*
+ Copyright (C) 2013 Carl Hetherington <cth@carlh.net>
-#include <string>
-#include <list>
-#include <stdint.h>
-#include <glibmm.h>
-#include <boost/shared_ptr.hpp>
-#include <boost/optional.hpp>
-#include "types.h"
-#include "exceptions.h"
-#include "dcp_time.h"
+ 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.
-namespace xmlpp {
- class Node;
- class DomParser;
-}
+ 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.
-namespace libdcp {
+ 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.
-/** @brief A helper class for XML nodes */
-class XMLNode
-{
-public:
- XMLNode ();
- XMLNode (xmlpp::Node const * node);
+*/
- std::string string_child (std::string);
- std::string optional_string_child (std::string);
- ContentKind kind_child (std::string);
- Fraction fraction_child (std::string);
- int64_t int64_child (std::string);
- int64_t optional_int64_child (std::string);
- float float_child (std::string);
- void ignore_child (std::string);
- void done ();
+#ifndef LIBDCP_XML_H
+#define LIBDCP_XML_H
- Time time_attribute (std::string);
- float float_attribute (std::string);
- std::string string_attribute (std::string);
- std::string optional_string_attribute (std::string);
- int64_t int64_attribute (std::string);
- int64_t optional_int64_attribute (std::string);
- boost::optional<bool> optional_bool_attribute (std::string);
- boost::optional<Color> optional_color_attribute (std::string);
+#include <libcxml/cxml.h>
+#include "exceptions.h"
- std::string content ();
+namespace libdcp
+{
- template <class T>
- boost::shared_ptr<T> type_child (std::string name) {
- return boost::shared_ptr<T> (new T (node_child (name)));
+template <class T>
+boost::shared_ptr<T>
+optional_type_child (cxml::Node const & node, std::string name)
+{
+ std::list<boost::shared_ptr<cxml::Node> > n = node.node_children (name);
+ if (n.size() > 1) {
+ throw XMLError ("duplicate XML tag");
+ } else if (n.empty ()) {
+ return boost::shared_ptr<T> ();
}
- template <class T>
- boost::shared_ptr<T> optional_type_child (std::string name) {
- std::list<xmlpp::Node*> n = node_children (name);
- if (n.size() > 1) {
- throw XMLError ("duplicate XML tag");
- } else if (n.empty ()) {
- return boost::shared_ptr<T> ();
- }
-
- return boost::shared_ptr<T> (new T (n.front ()));
- }
+ return boost::shared_ptr<T> (new T (n.front ()));
+}
+
+template <class T>
+boost::shared_ptr<T> type_child (boost::shared_ptr<const cxml::Node> node, std::string name) {
+ return boost::shared_ptr<T> (new T (node->node_child (name)));
+}
- template <class T>
- std::list<boost::shared_ptr<T> > type_children (std::string name) {
- std::list<xmlpp::Node*> n = node_children (name);
- std::list<boost::shared_ptr<T> > r;
- for (typename std::list<xmlpp::Node*>::iterator i = n.begin(); i != n.end(); ++i) {
- r.push_back (boost::shared_ptr<T> (new T (*i)));
- }
- return r;
- }
+template <class T>
+boost::shared_ptr<T>
+optional_type_child (boost::shared_ptr<const cxml::Node> node, std::string name)
+{
+ return optional_type_child<T> (*node.get(), name);
+}
- template <class T>
- std::list<boost::shared_ptr<T> > type_grand_children (std::string name, std::string sub) {
- XMLNode p (node_child (name));
- return p.type_children<T> (sub);
+template <class T>
+std::list<boost::shared_ptr<T> >
+type_children (cxml::Node const & node, std::string name)
+{
+ std::list<boost::shared_ptr<cxml::Node> > n = node.node_children (name);
+ std::list<boost::shared_ptr<T> > r;
+ for (typename std::list<boost::shared_ptr<cxml::Node> >::iterator i = n.begin(); i != n.end(); ++i) {
+ r.push_back (boost::shared_ptr<T> (new T (*i)));
}
+ return r;
+}
- xmlpp::Node const * _node;
-
-private:
- xmlpp::Node* node_child (std::string);
- std::list<xmlpp::Node*> node_children (std::string);
- std::list<Glib::ustring> _taken;
-};
-
-/** @brief A helper class for XML files */
-class XMLFile : public XMLNode
+template <class T>
+std::list<boost::shared_ptr<T> >
+type_children (boost::shared_ptr<const cxml::Node> node, std::string name)
{
-public:
- XMLFile (std::string file, std::string root_name);
- virtual ~XMLFile ();
-
-private:
- xmlpp::DomParser* _parser;
-};
+ return type_children<T> (*node.get(), name);
+}
+
+template <class T>
+std::list<boost::shared_ptr<T> >
+type_grand_children (cxml::Node const & node, std::string name, std::string sub)
+{
+ boost::shared_ptr<const cxml::Node> p = node.node_child (name);
+ return type_children<T> (p, sub);
+}
+template <class T>
+std::list<boost::shared_ptr<T> >
+type_grand_children (boost::shared_ptr<const cxml::Node> node, std::string name, std::string sub)
+{
+ return type_grand_children<T> (*node.get(), name, sub);
+}
+
}
#endif