From 59886567974bd3e79d30a4a9425d86d50bf425f3 Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Sun, 26 Jan 2014 21:35:02 +0000 Subject: [PATCH] It builds again. --- examples/make_dcp.cc | 30 +- src/argb_frame.cc | 6 +- src/argb_frame.h | 12 +- src/asset.cc | 64 +++ src/asset.h | 42 +- src/certificates.cc | 51 ++- src/certificates.h | 26 +- src/content.cc | 59 +-- src/content.h | 66 +-- src/cpl.cc | 248 +++-------- src/cpl.h | 60 +-- src/dcp.cc | 420 +++++++++--------- src/dcp.h | 80 +--- src/exceptions.cc | 12 +- src/exceptions.h | 27 +- src/file.cc | 38 ++ src/file.h | 43 ++ src/{parse/subtitle.cc => font.cc} | 64 +-- src/{parse/subtitle.h => font.h} | 65 +-- src/gamma_lut.cc | 6 +- src/gamma_lut.h | 2 +- src/kdm.cc | 2 +- src/key.h | 1 + src/load_font.cc | 30 ++ src/load_font.h | 38 ++ src/mono_picture_mxf.cc | 12 +- src/mono_picture_mxf.h | 6 +- src/mono_picture_mxf_writer.cc | 12 +- src/mono_picture_mxf_writer.h | 2 +- src/mxf.cc | 41 +- src/mxf.h | 29 +- src/mxf_writer.cc | 46 ++ src/mxf_writer.h | 46 ++ src/object.cc | 4 + src/object.h | 6 +- src/parse/asset_map.cc | 79 ---- src/parse/asset_map.h | 81 ---- src/parse/cpl.cc | 151 ------- src/parse/cpl.h | 163 ------- src/parse/pkl.cc | 52 --- src/picture_mxf.cc | 35 +- src/picture_mxf.h | 22 +- src/picture_mxf_writer.cc | 20 +- src/picture_mxf_writer.h | 31 +- src/picture_mxf_writer_common.cc | 6 +- src/reel.cc | 70 ++- src/reel.h | 48 +- src/reel_asset.cc | 101 +++++ src/reel_asset.h | 82 ++++ src/reel_mono_picture_asset.cc | 49 ++ src/reel_mono_picture_asset.h | 40 ++ src/reel_picture_asset.cc | 88 ++++ src/reel_picture_asset.h | 52 +++ src/reel_sound_asset.cc | 44 ++ src/reel_sound_asset.h | 42 ++ src/reel_stereo_picture_asset.cc | 50 +++ src/reel_stereo_picture_asset.h | 37 ++ src/reel_subtitle_asset.cc | 44 ++ src/{parse/pkl.h => reel_subtitle_asset.h} | 42 +- src/ref.h | 91 ++++ src/signer.cc | 50 +-- src/signer.h | 18 +- src/sound_mxf.cc | 112 +---- src/sound_mxf.h | 47 +- src/sound_mxf_writer.cc | 122 +++++ src/sound_mxf_writer.h | 55 +++ src/stereo_picture_frame.cc | 2 + src/stereo_picture_mxf.cc | 96 ++-- src/stereo_picture_mxf.h | 5 +- src/stereo_picture_mxf_writer.cc | 21 +- src/stereo_picture_mxf_writer.h | 9 +- src/subtitle.cc | 60 +++ src/subtitle.h | 55 +++ src/subtitle_asset.h | 198 --------- ...{subtitle_asset.cc => subtitle_content.cc} | 171 ++----- src/subtitle_content.h | 104 +++++ src/subtitle_string.cc | 107 +++++ src/subtitle_string.h | 124 ++++++ src/text.cc | 41 ++ src/text.h | 48 ++ src/types.h | 9 +- src/util.cc | 36 +- src/util.h | 4 +- src/wscript | 20 +- test/{cpl_sar.cc => cpl_sar_test.cc} | 16 +- test/decryption_test.cc | 5 +- test/read_dcp_test.cc | 4 +- test/recovery_test.cc | 10 +- test/rewrite_subs.cc | 26 +- test/round_trip_test.cc | 30 +- test/subs_in_out.cc | 4 +- test/subtitle_tests.cc | 61 +-- test/test.cc | 14 - test/test.h | 19 - test/wscript | 2 +- tools/dcpinfo.cc | 47 +- 96 files changed, 2772 insertions(+), 2196 deletions(-) create mode 100644 src/file.cc create mode 100644 src/file.h rename src/{parse/subtitle.cc => font.cc} (60%) rename src/{parse/subtitle.h => font.h} (58%) create mode 100644 src/load_font.cc create mode 100644 src/load_font.h create mode 100644 src/mxf_writer.cc create mode 100644 src/mxf_writer.h delete mode 100644 src/parse/asset_map.cc delete mode 100644 src/parse/asset_map.h delete mode 100644 src/parse/cpl.cc delete mode 100644 src/parse/cpl.h delete mode 100644 src/parse/pkl.cc create mode 100644 src/reel_asset.cc create mode 100644 src/reel_asset.h create mode 100644 src/reel_mono_picture_asset.cc create mode 100644 src/reel_mono_picture_asset.h create mode 100644 src/reel_picture_asset.cc create mode 100644 src/reel_picture_asset.h create mode 100644 src/reel_sound_asset.cc create mode 100644 src/reel_sound_asset.h create mode 100644 src/reel_stereo_picture_asset.cc create mode 100644 src/reel_stereo_picture_asset.h create mode 100644 src/reel_subtitle_asset.cc rename src/{parse/pkl.h => reel_subtitle_asset.h} (58%) create mode 100644 src/ref.h create mode 100644 src/sound_mxf_writer.cc create mode 100644 src/sound_mxf_writer.h create mode 100644 src/subtitle.cc create mode 100644 src/subtitle.h delete mode 100644 src/subtitle_asset.h rename src/{subtitle_asset.cc => subtitle_content.cc} (63%) create mode 100644 src/subtitle_content.h create mode 100644 src/subtitle_string.cc create mode 100644 src/subtitle_string.h create mode 100644 src/text.cc create mode 100644 src/text.h rename test/{cpl_sar.cc => cpl_sar_test.cc} (82%) diff --git a/examples/make_dcp.cc b/examples/make_dcp.cc index 8f6897b8..e3e83f8d 100644 --- a/examples/make_dcp.cc +++ b/examples/make_dcp.cc @@ -36,7 +36,11 @@ #include "mono_picture_mxf.h" #include "mono_picture_mxf_writer.h" #include "sound_mxf.h" +#include "sound_mxf_writer.h" #include "reel.h" +#include "file.h" +#include "reel_mono_picture_asset.h" +#include "reel_sound_asset.h" int main () @@ -49,10 +53,10 @@ main () per second. */ - boost::shared_ptr picture_mxf (new dcp::MonoPictureMXF (24)); + boost::shared_ptr picture_mxf (new dcp::MonoPictureMXF (dcp::Fraction (24, 1))); /* Start off a write to it */ - boost::shared_ptr picture_writer = picture_mxf->start_write ("DCP/picture.mxf", false); + boost::shared_ptr picture_writer = picture_mxf->start_write ("DCP/picture.mxf", dcp::SMPTE, false); /* Write 24 frames of the same JPEG2000 file */ dcp::File picture ("examples/help.j2c"); @@ -66,8 +70,8 @@ main () /* Now create a sound MXF. As before, create an object and a writer. When creating the object we specify the sampling rate (48kHz) and the number of channels (2). */ - boost::shared_ptr sound_mxf (new dcp::SoundMXF (48000, 2)); - boost::shared_ptr sound_writer = sound_mxf->start_write ("DCP/sound.mxf", false); + boost::shared_ptr sound_mxf (new dcp::SoundMXF (dcp::Fraction (24, 1), 48000, 2)); + boost::shared_ptr sound_writer = sound_mxf->start_write ("DCP/sound.mxf", dcp::SMPTE); /* Write some sine waves */ float* audio[2]; @@ -85,24 +89,24 @@ main () sound_writer->finalize (); /* Now create a reel */ - shared_ptr reel (new dcp::Reel ()); + boost::shared_ptr reel (new dcp::Reel ()); /* Add picture and sound to it. The zeros are the `entry points', i.e. the first (video) frame from the MXFs that the reel should play. */ - reel->add (picture, 0); - reel->add (sound, 0); + reel->add (boost::shared_ptr (new dcp::ReelMonoPictureAsset (picture_mxf, 0))); + reel->add (boost::shared_ptr (new dcp::ReelSoundAsset (sound_mxf, 0))); /* Make a CPL with this reel */ - shared_ptr cpl (new dcp::CPL ("My film", dcp::FEATURE)); + boost::shared_ptr cpl (new dcp::CPL ("My film", dcp::FEATURE)); cpl->add (reel); /* Write the DCP */ - list > assets; - asset.push_back (cpl); - asset.push_back (picture); - asset.push_back (sound); - dcp::write ("DCP", assets); + dcp::DCP dcp ("DCP"); + dcp.add (cpl); + dcp.add (picture_mxf); + dcp.add (sound_mxf); + dcp.write_xml (dcp::SMPTE); return 0; } diff --git a/src/argb_frame.cc b/src/argb_frame.cc index 79637329..99d6c814 100644 --- a/src/argb_frame.cc +++ b/src/argb_frame.cc @@ -1,5 +1,5 @@ /* - Copyright (C) 2012 Carl Hetherington + Copyright (C) 2012-2014 Carl Hetherington This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -17,6 +17,10 @@ */ +/** @file src/argb_frame.cc + * @brief ARGBFrame class. + */ + #include "argb_frame.h" using namespace dcp; diff --git a/src/argb_frame.h b/src/argb_frame.h index 2ad6f69d..a1b3b427 100644 --- a/src/argb_frame.h +++ b/src/argb_frame.h @@ -1,5 +1,5 @@ /* - Copyright (C) 2012 Carl Hetherington + Copyright (C) 2012-2014 Carl Hetherington This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -18,11 +18,11 @@ */ /** @file src/argb_frame.h - * @brief Container for a single image from a picture asset. + * @brief ARGBFrame class. */ -#include #include "util.h" +#include namespace dcp { @@ -42,7 +42,7 @@ namespace dcp * * Lines are packed so that the second row directly follows the first. */ -class ARGBFrame +class ARGBFrame : boost::noncopyable { public: ARGBFrame (Size size); @@ -60,8 +60,8 @@ public: } private: - Size _size; - uint8_t* _data; + Size _size; ///< frame size in pixels + uint8_t* _data; ///< pointer to image data }; } diff --git a/src/asset.cc b/src/asset.cc index 0147eba2..74e32cf0 100644 --- a/src/asset.cc +++ b/src/asset.cc @@ -17,18 +17,82 @@ */ +/** @file src/asset.cc + * @brief Asset class. + */ + #include "asset.h" +#include "util.h" +#include +#include using std::string; +using boost::lexical_cast; +using boost::function; using namespace dcp; +/** Create an Asset with a randomly-generated ID */ Asset::Asset () { } +Asset::Asset (boost::filesystem::path file) + : _file (file) +{ + +} + +/** Create an Asset with a specified ID. + * @param id ID to use. + */ Asset::Asset (string id) : Object (id) { } + +void +Asset::write_to_pkl (xmlpp::Node* node) const +{ + xmlpp::Node* asset = node->add_child ("Asset"); + asset->add_child("Id")->add_child_text ("urn:uuid:" + _id); + asset->add_child("AnnotationText")->add_child_text (_id); + asset->add_child("Hash")->add_child_text (hash ()); + asset->add_child("Size")->add_child_text (lexical_cast (boost::filesystem::file_size (_file))); + asset->add_child("Type")->add_child_text (pkl_type ()); +} + +void +Asset::write_to_assetmap (xmlpp::Node* node) const +{ + xmlpp::Node* asset = node->add_child ("Asset"); + asset->add_child("Id")->add_child_text ("urn:uuid:" + _id); + xmlpp::Node* chunk_list = asset->add_child ("ChunkList"); + xmlpp::Node* chunk = chunk_list->add_child ("Chunk"); + chunk->add_child("Path")->add_child_text (_file.string ()); + chunk->add_child("VolumeIndex")->add_child_text ("1"); + chunk->add_child("Offset")->add_child_text ("0"); + chunk->add_child("Length")->add_child_text (lexical_cast (boost::filesystem::file_size (_file))); +} + +string +Asset::hash () const +{ + if (!_hash.empty ()) { + _hash = make_digest (_file, 0); + } + + return _hash; +} + +bool +Asset::equals (boost::shared_ptr other, EqualityOptions, function note) const +{ + if (_hash != other->_hash) { + note (ERROR, "Asset hashes differ"); + return false; + } + + return true; +} diff --git a/src/asset.h b/src/asset.h index f3546cd8..e8867ae5 100644 --- a/src/asset.h +++ b/src/asset.h @@ -17,23 +17,61 @@ */ +/** @file src/asset.h + * @brief Asset class. + */ + #ifndef LIBDCP_ASSET_H #define LIBDCP_ASSET_H #include "object.h" +#include "types.h" +#include +#include + +namespace xmlpp { + class Node; +} namespace dcp { /** @class Asset - * @brief Parent class for DCP assets, i.e. picture/sound/subtitles, CPLs and PKLs. + * @brief Parent class for DCP assets, i.e. picture/sound/subtitles and CPLs. + * + * Note that this class is not used for ReelAssets; they are just for the metadata + * that gets put into s. */ - class Asset : public Object { public: Asset (); + Asset (boost::filesystem::path file); Asset (std::string id); + virtual std::string pkl_type () const = 0; + virtual bool equals (boost::shared_ptr other, EqualityOptions opt, boost::function note) const; + + /** Write details of the asset to a PKL AssetList node. + * @param node Parent node. + */ + void write_to_pkl (xmlpp::Node* node) const; + void write_to_assetmap (xmlpp::Node* node) const; + + boost::filesystem::path file () const { + return _file; + } + + void set_file (boost::filesystem::path file) { + _file = file; + } + + std::string hash () const; + +protected: + friend class MXFWriter; + + boost::filesystem::path _file; + mutable std::string _hash; }; } diff --git a/src/certificates.cc b/src/certificates.cc index caa4c830..222352f2 100644 --- a/src/certificates.cc +++ b/src/certificates.cc @@ -1,5 +1,5 @@ /* - Copyright (C) 2012 Carl Hetherington + Copyright (C) 2012-2014 Carl Hetherington This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -17,25 +17,21 @@ */ -#include -#include -#include -#include -#include -#include -#include -#include -#include #include "KM_util.h" #include "certificates.h" #include "compose.hpp" #include "exceptions.h" #include "util.h" +#include +#include +#include +#include +#include +#include +#include using std::list; using std::string; -using std::stringstream; -using std::vector; using boost::shared_ptr; using namespace dcp; @@ -47,6 +43,9 @@ Certificate::Certificate (X509* c) } +/** Load an X509 certificate from a file. + * @param filename File to load. + */ Certificate::Certificate (boost::filesystem::path filename) : _certificate (0) , _public_key (0) @@ -61,6 +60,9 @@ Certificate::Certificate (boost::filesystem::path filename) } } +/** Load an X509 certificate from a string. + * @param cert String to read from. + */ Certificate::Certificate (string cert) : _certificate (0) , _public_key (0) @@ -68,6 +70,9 @@ Certificate::Certificate (string cert) read_string (cert); } +/** Copy constructor. + * @param other Certificate to copy. + */ Certificate::Certificate (Certificate const & other) : _certificate (0) , _public_key (0) @@ -75,6 +80,9 @@ Certificate::Certificate (Certificate const & other) read_string (other.certificate (true)); } +/** Read a certificate from a string. + * @param cert String to read. + */ void Certificate::read_string (string cert) { @@ -91,12 +99,16 @@ Certificate::read_string (string cert) BIO_free (bio); } +/** Destructor */ Certificate::~Certificate () { X509_free (_certificate); RSA_free (_public_key); } +/** operator= for Certificate. + * @param other Certificate to read from. + */ Certificate & Certificate::operator= (Certificate const & other) { @@ -114,6 +126,10 @@ Certificate::operator= (Certificate const & other) return *this; } +/** Return the certificate as a string. + * @param with_begin_end true to include the -----BEGIN CERTIFICATE--- / -----END CERTIFICATE----- markers. + * @return Certificate string. + */ string Certificate::certificate (bool with_begin_end) const { @@ -143,6 +159,10 @@ Certificate::certificate (bool with_begin_end) const return s; } +/** @return Certificate's issuer, in the form + * dnqualifier=,CN=,OU=,O=organizationName + * and with + signs escaped to \+ + */ string Certificate::issuer () const { @@ -244,6 +264,7 @@ Certificate::thumbprint () const return Kumu::base64encode (digest, 20, digest_base64, 64); } +/** @return RSA public key from this Certificate. Caller must not free the returned value. */ RSA * Certificate::public_key () const { @@ -266,6 +287,7 @@ Certificate::public_key () const return _public_key; } +/** @return Root certificate */ shared_ptr CertificateChain::root () const { @@ -273,6 +295,7 @@ CertificateChain::root () const return _certificates.front (); } +/** @return Leaf certificate */ shared_ptr CertificateChain::leaf () const { @@ -280,6 +303,7 @@ CertificateChain::leaf () const return _certificates.back (); } +/** @return Certificates in order from leaf to root */ list > CertificateChain::leaf_to_root () const { @@ -288,6 +312,9 @@ CertificateChain::leaf_to_root () const return c; } +/** Add a certificate to the end of the chain. + * @param c Certificate to add. + */ void CertificateChain::add (shared_ptr c) { diff --git a/src/certificates.h b/src/certificates.h index c7d6698b..8ec5ddd5 100644 --- a/src/certificates.h +++ b/src/certificates.h @@ -1,5 +1,5 @@ /* - Copyright (C) 2012 Carl Hetherington + Copyright (C) 2012-2014 Carl Hetherington This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -17,16 +17,20 @@ */ +/** @file src/certificates.h + * @brief Certificate and CertificateChain classes. + */ + #ifndef LIBDCP_CERTIFICATES_H #define LIBDCP_CERTIFICATES_H -#include -#include +#undef X509_NAME +#include #include #include #include -#undef X509_NAME -#include +#include +#include class certificates; @@ -36,6 +40,11 @@ namespace xmlpp { namespace dcp { +/** @class Certificate + * @brief A wrapper for an X509 certificate. + * + * This class can take a Certificate from a file, a string or an OpenSSL X509 object. + */ class Certificate { public: @@ -51,16 +60,12 @@ public: Certificate& operator= (Certificate const &); - /** @param with_begin_end true to include BEGIN CERTIFICATE / END CERTIFICATE markers - * @return the whole certificate as a string. - */ std::string certificate (bool with_begin_end = false) const; std::string issuer () const; std::string serial () const; std::string subject () const; std::string common_name () const; - /** @return RSA public key from this Certificate. Caller must not free the returned value. */ RSA* public_key () const; std::string thumbprint () const; @@ -76,6 +81,9 @@ private: mutable RSA* _public_key; }; +/** @class CertificateChain + * @brief A chain of any number of certificates, from root to leaf. + */ class CertificateChain { public: diff --git a/src/content.cc b/src/content.cc index 9a035c80..06e2806d 100644 --- a/src/content.cc +++ b/src/content.cc @@ -1,5 +1,5 @@ /* - Copyright (C) 2012 Carl Hetherington + Copyright (C) 2012-2014 Carl Hetherington This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -33,59 +33,36 @@ using namespace boost; using namespace dcp; Content::Content (boost::filesystem::path file) - : _file (file) - , _edit_rate (0) + : Asset (file) + , _edit_rate (24, 1) , _intrinsic_duration (0) { } -Content::Content (int edit_rate) +Content::Content (Fraction edit_rate) : _edit_rate (edit_rate) , _intrinsic_duration (0) { } -void -Content::write_to_pkl (xmlpp::Node* node) const -{ - xmlpp::Node* asset = node->add_child ("Asset"); - asset->add_child("Id")->add_child_text ("urn:uuid:" + _id); - asset->add_child("AnnotationText")->add_child_text (_id); -//XXX asset->add_child("Hash")->add_child_text (digest ()); - asset->add_child("Size")->add_child_text (lexical_cast (filesystem::file_size (_file))); - asset->add_child("Type")->add_child_text ("application/mxf"); -} - -void -Content::write_to_assetmap (xmlpp::Node* node) const -{ - xmlpp::Node* asset = node->add_child ("Asset"); - asset->add_child("Id")->add_child_text ("urn:uuid:" + _id); - xmlpp::Node* chunk_list = asset->add_child ("ChunkList"); - xmlpp::Node* chunk = chunk_list->add_child ("Chunk"); - chunk->add_child("Path")->add_child_text (_file.string ()); - chunk->add_child("VolumeIndex")->add_child_text ("1"); - chunk->add_child("Offset")->add_child_text ("0"); - chunk->add_child("Length")->add_child_text (lexical_cast (filesystem::file_size (_file))); -} - bool -Content::equals (shared_ptr other, EqualityOptions, boost::function note) const +Content::equals (shared_ptr other, EqualityOptions opt, boost::function note) const { - // if (_edit_rate != other->_edit_rate) { - // note (ERROR, "asset edit rates differ"); - // return false; - // } + if (!Asset::equals (other, opt, note)) { + return false; + } - // if (_intrinsic_duration != other->_intrinsic_duration) { - // note (ERROR, "asset intrinsic durations differ"); - // } - - // if (_duration != other->_duration) { - // note (ERROR, "asset durations differ"); - // } + if (_edit_rate != other->_edit_rate) { + note (ERROR, "content edit rates differ"); + return false; + } + + if (_intrinsic_duration != other->_intrinsic_duration) { + note (ERROR, "asset intrinsic durations differ"); + return false; + } - // return true; + return true; } diff --git a/src/content.h b/src/content.h index 75e39a01..57aaa14c 100644 --- a/src/content.h +++ b/src/content.h @@ -1,5 +1,5 @@ /* - Copyright (C) 2012 Carl Hetherington + Copyright (C) 2012-2014 Carl Hetherington This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -17,16 +17,20 @@ */ +/** @file src/content.h + * @brief Content class. + */ + #ifndef LIBDCP_CONTENT_H #define LIBDCP_CONTENT_H -#include -#include -#include -#include -#include #include "types.h" #include "asset.h" +#include +#include +#include +#include +#include namespace ASDCP { class WriterInfo; @@ -48,65 +52,25 @@ class Content : public Asset { public: Content (boost::filesystem::path file); - Content (int edit_rate); + Content (Fraction edit_rate); virtual ~Content () {} - /** Write details of the asset to a PKL AssetList node. - * @param p Parent node. - */ - void write_to_pkl (xmlpp::Node *) const; - - /** Write details of the asset to a ASSETMAP stream. - * @param s Stream. - */ - void write_to_assetmap (xmlpp::Node *) const; - - boost::filesystem::path file () const { - return _file; - } - - void set_file (boost::filesystem::path file) { - _file = file; - } - - int edit_rate () const { + Fraction edit_rate () const { return _edit_rate; } - void set_edit_rate (int r) { - _edit_rate = r; - } - - void set_entry_point (int64_t p) { - _entry_point = p; - } - int64_t intrinsic_duration () const { return _intrinsic_duration; } - void set_intrinsic_duration (int64_t d) { - _intrinsic_duration = d; - } - - int64_t duration () const { - return _duration; - } - - void set_duration (int64_t d) { - _duration = d; - } - virtual bool equals (boost::shared_ptr other, EqualityOptions opt, boost::function) const; protected: - boost::filesystem::path _file; - /** The edit rate; this is normally equal to the number of video frames per second */ - int _edit_rate; - int64_t _entry_point; + friend class MXFWriter; + + Fraction _edit_rate; int64_t _intrinsic_duration; - int64_t _duration; }; } diff --git a/src/cpl.cc b/src/cpl.cc index 9f7ef7b3..0cd1d6ce 100644 --- a/src/cpl.cc +++ b/src/cpl.cc @@ -17,20 +17,22 @@ */ -#include #include "cpl.h" -#include "parse/cpl.h" #include "util.h" #include "mono_picture_mxf.h" #include "stereo_picture_mxf.h" #include "sound_mxf.h" -#include "subtitle_asset.h" -#include "parse/asset_map.h" +#include "subtitle_content.h" #include "reel.h" #include "metadata.h" #include "signer.h" #include "exceptions.h" +#include "xml.h" #include "compose.hpp" +#include "reel_picture_asset.h" +#include "reel_sound_asset.h" +#include "reel_subtitle_asset.h" +#include using std::string; using std::stringstream; @@ -43,165 +45,56 @@ using boost::lexical_cast; using boost::optional; using namespace dcp; -CPL::CPL (boost::filesystem::path directory, string name, ContentKind content_kind, int length, int frames_per_second) - : _directory (directory) - , _name (name) +CPL::CPL (string annotation_text, ContentKind content_kind) + : _annotation_text (annotation_text) , _content_kind (content_kind) - , _length (length) - , _fps (frames_per_second) + , _length (0) { } -/** Construct a CPL object from a XML file. - * @param directory The directory containing this CPL's DCP. - * @param file The CPL XML filename. - * @param asset_maps AssetMaps to look for assets in. - * @param require_mxfs true to throw an exception if a required MXF file does not exist. - */ -CPL::CPL (boost::filesystem::path directory, string file, list asset_maps, bool require_mxfs) - : _directory (directory) - , _content_kind (FEATURE) +/** Construct a CPL object from a XML file */ +CPL::CPL (boost::filesystem::path file) + : _content_kind (FEATURE) , _length (0) - , _fps (0) { - /* Read the XML */ - shared_ptr cpl; - try { - cpl.reset (new parse::CPL (file)); - } catch (FileError& e) { - boost::throw_exception (FileError ("could not load CPL file", file, e.number ())); - } - - /* Now cherry-pick the required bits into our own data structure */ - - _name = cpl->annotation_text; - _content_kind = cpl->content_kind; - - /* Trim urn:uuid: off the front */ - _id = cpl->id.substr (9); - - for (list >::iterator i = cpl->reels.begin(); i != cpl->reels.end(); ++i) { - - shared_ptr 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 picture; - shared_ptr sound; - shared_ptr subtitle; - - /* Some rather twisted logic to decide if we are 3D or not; - some DCPs give a MainStereoscopicPicture to indicate 3D, others - just have a FrameRate twice the EditRate and apparently - expect you to divine the fact that they are hence 3D. - */ - - if (!(*i)->asset_list->main_stereoscopic_picture && p->edit_rate == p->frame_rate) { - - try { - pair > asset = asset_from_id (asset_maps, p->id); - -// picture.reset (new MonoPictureMXF (asset.first, asset.second->chunks.front()->path)); - -// picture->read (); -// picture->set_edit_rate (_fps); -// picture->set_entry_point (p->entry_point); -// picture->set_duration (p->duration); - if (p->key_id.length() > 9) { - /* Trim urn:uuid: */ -// picture->set_key_id (p->key_id.substr (9)); - } - } catch (MXFFileError) { - if (require_mxfs) { - throw; - } - } - - } else { - try { - pair > asset = asset_from_id (asset_maps, p->id); - -// picture.reset (new StereoPictureMXF (asset.first, asset.second->chunks.front()->path)); - -// picture->read (); -// picture->set_edit_rate (_fps); -// picture->set_entry_point (p->entry_point); -// picture->set_duration (p->duration); - if (p->key_id.length() > 9) { - /* Trim urn:uuid: */ -// picture->set_key_id (p->key_id.substr (9)); - } - - } catch (MXFFileError) { - if (require_mxfs) { - throw; - } - } - - } - - if ((*i)->asset_list->main_sound) { - - try { - pair > asset = asset_from_id (asset_maps, (*i)->asset_list->main_sound->id); - -// sound.reset (new SoundMXF (asset.first, asset.second->chunks.front()->path)); -// shared_ptr s = (*i)->asset_list->main_sound; - -// sound->read (); -// sound->set_entry_point (s->entry_point); -// sound->set_duration (s->duration); -// if (s->key_id.length() > 9) { - /* Trim urn:uuid: */ -// sound->set_key_id (s->key_id.substr (9)); -// } - } catch (MXFFileError) { - if (require_mxfs) { - throw; - } - } - } - - if ((*i)->asset_list->main_subtitle) { - -// pair > asset = asset_from_id (asset_maps, (*i)->asset_list->main_subtitle->id); - -// subtitle.reset (new SubtitleAsset (asset.first, asset.second->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 (new Reel (picture, sound, subtitle))); - } + cxml::Document f ("CompositionPlaylist"); + f.read_file (file); + + _id = f.string_child ("Id"); + _annotation_text = f.optional_string_child ("AnnotationText").get_value_or (""); + _issue_date = f.string_child ("IssueDate"); + _creator = f.optional_string_child ("Creator").get_value_or (""); + _content_title_text = f.string_child ("ContentTitleText"); + _content_kind = content_kind_from_string (f.string_child ("ContentKind")); + shared_ptr content_version = f.node_child ("ContentVersion"); + if (content_version) { + _content_version_id = content_version->optional_string_child ("Id").get_value_or (""); + _content_version_label_text = content_version->string_child ("LabelText"); + content_version->done (); + } + f.ignore_child ("RatingList"); + _reels = type_grand_children (f, "ReelList", "Reel"); + + f.ignore_child ("Issuer"); + f.ignore_child ("Signer"); + f.ignore_child ("Signature"); + + f.done (); } void -CPL::add_reel (shared_ptr reel) +CPL::add (shared_ptr reel) { _reels.push_back (reel); } void -CPL::write_xml (bool interop, XMLMetadata const & metadata, shared_ptr signer) const +CPL::write_xml (boost::filesystem::path file, Standard standard, XMLMetadata metadata, shared_ptr signer) const { - boost::filesystem::path p; - p /= _directory; - stringstream s; - s << _id << "_cpl.xml"; - p /= s.str(); - xmlpp::Document doc; xmlpp::Element* root; - if (interop) { + if (standard == INTEROP) { root = doc.create_root_node ("CompositionPlaylist", "http://www.digicine.com/PROTO-ASDCP-CPL-20040511#"); } else { root = doc.create_root_node ("CompositionPlaylist", "http://www.smpte-ra.org/schemas/429-7/2006/CPL"); @@ -212,44 +105,34 @@ CPL::write_xml (bool interop, XMLMetadata const & metadata, shared_ptradd_child("Id")->add_child_text ("urn:uuid:" + _id); - root->add_child("AnnotationText")->add_child_text (_name); + root->add_child("AnnotationText")->add_child_text (_annotation_text); root->add_child("IssueDate")->add_child_text (metadata.issue_date); root->add_child("Issuer")->add_child_text (metadata.issuer); root->add_child("Creator")->add_child_text (metadata.creator); - root->add_child("ContentTitleText")->add_child_text (_name); + root->add_child("ContentTitleText")->add_child_text (_content_version_label_text); root->add_child("ContentKind")->add_child_text (content_kind_to_string (_content_kind)); { xmlpp::Node* cv = root->add_child ("ContentVersion"); - cv->add_child ("Id")->add_child_text ("urn:uri:" + _id + "_" + metadata.issue_date); - cv->add_child ("LabelText")->add_child_text (_id + "_" + metadata.issue_date); + cv->add_child ("Id")->add_child_text (_content_version_id); + cv->add_child ("LabelText")->add_child_text (_content_version_label_text); } root->add_child("RatingList"); xmlpp::Element* reel_list = root->add_child ("ReelList"); for (list >::const_iterator i = _reels.begin(); i != _reels.end(); ++i) { - (*i)->write_to_cpl (reel_list); + (*i)->write_to_cpl (reel_list, standard); } if (signer) { - signer->sign (root, interop); + signer->sign (root, standard); } /* This must not be the _formatted version otherwise signature digests will be wrong */ - doc.write_to_file (p.string (), "UTF-8"); - - _digest = make_digest (p.string (), 0); - _length = boost::filesystem::file_size (p.string ()); -} + doc.write_to_file (file.string (), "UTF-8"); -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:" + _id); - asset->add_child("Hash")->add_child_text (_digest); - asset->add_child("Size")->add_child_text (lexical_cast (_length)); - asset->add_child("Type")->add_child_text ("text/xml"); + _digest = make_digest (file.string (), 0); + _length = boost::filesystem::file_size (file.string ()); } list > @@ -258,13 +141,13 @@ CPL::assets () const list > a; for (list >::const_iterator i = _reels.begin(); i != _reels.end(); ++i) { if ((*i)->main_picture ()) { - a.push_back ((*i)->main_picture ()); + a.push_back ((*i)->main_picture()->mxf ()); } if ((*i)->main_sound ()) { - a.push_back ((*i)->main_sound ()); + a.push_back ((*i)->main_sound()->mxf ()); } if ((*i)->main_subtitle ()) { - a.push_back ((*i)->main_subtitle ()); + a.push_back ((*i)->main_subtitle()->subtitle_content ()); } } @@ -284,14 +167,12 @@ CPL::write_to_assetmap (xmlpp::Node* node) const chunk->add_child("Length")->add_child_text(lexical_cast (_length)); } - - bool CPL::equals (CPL const & other, EqualityOptions opt, boost::function note) const { - if (_name != other._name && !opt.cpl_names_can_differ) { + if (_annotation_text != other._annotation_text && !opt.cpl_annotation_texts_can_differ) { stringstream s; - s << "names differ: " << _name << " vs " << other._name << "\n"; + s << "annotation texts differ: " << _annotation_text << " vs " << other._annotation_text << "\n"; note (ERROR, s.str ()); return false; } @@ -301,16 +182,6 @@ CPL::equals (CPL const & other, EqualityOptions opt, boost::function >::const_iterator i = _reels.begin(); i != _reels.end(); ++i) { (*i)->add_kdm (kdm); } } -pair > -CPL::asset_from_id (list asset_maps, string id) const -{ - for (list::const_iterator i = asset_maps.begin(); i != asset_maps.end(); ++i) { - shared_ptr a = i->second->asset_from_id (id); - if (a) { - return make_pair (i->first, a); - } - } - - return make_pair ("", shared_ptr ()); -} - void CPL::set_mxf_keys (Key key) { diff --git a/src/cpl.h b/src/cpl.h index abfb92f6..83fc4b9d 100644 --- a/src/cpl.h +++ b/src/cpl.h @@ -33,11 +33,6 @@ #include "asset.h" namespace dcp { - -namespace parse { - class AssetMap; - class AssetMapAsset; -} class Content; class Reel; @@ -52,16 +47,23 @@ class KDM; class CPL : public Asset { public: - CPL (boost::filesystem::path directory, std::string name, ContentKind content_kind, int length, int frames_per_second); - CPL (boost::filesystem::path, std::string file, std::list asset_maps, bool require_mxfs = true); + CPL (std::string annotation_text, ContentKind content_kind); + CPL (boost::filesystem::path file); - void add_reel (boost::shared_ptr reel); - - /** @return the length in frames */ - int length () const { - return _length; + std::string pkl_type () const { + return "text/xml"; } + void add (boost::shared_ptr reel); + + std::string annotation_text () const { + return _annotation_text; + } + + std::string content_title_text () const { + return _content_title_text; + } + /** @return the type of the content, used by media servers * to categorise things (e.g. feature, trailer, etc.) */ @@ -73,18 +75,6 @@ public: 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 > assets () const; bool encrypted () const; @@ -93,29 +83,27 @@ public: bool equals (CPL const & other, EqualityOptions options, boost::function note) const; - void write_xml (bool, XMLMetadata const &, boost::shared_ptr) const; + void write_xml (boost::filesystem::path file, Standard standard, XMLMetadata, boost::shared_ptr) const; void write_to_assetmap (xmlpp::Node *) const; - void write_to_pkl (xmlpp::Node *) const; - void add_kdm (KDM const &); + void add (KDM const &); private: - std::pair > asset_from_id (std::list, std::string id) const; - boost::filesystem::path _directory; - /** the name of the DCP */ - std::string _name; - /** the content kind of the CPL */ + std::string _annotation_text; + std::string _issue_date; + std::string _creator; + std::string _content_title_text; ContentKind _content_kind; - /** length in frames */ - mutable int _length; - /** frames per second */ - int _fps; + std::string _content_version_id; + std::string _content_version_label_text; /** reels */ std::list > _reels; /** a SHA1 digest of our XML */ mutable std::string _digest; + /** length in bytes of the XML that we last wrote to disk */ + mutable int64_t _length; }; } diff --git a/src/dcp.cc b/src/dcp.cc index a5b0c6e4..808c040e 100644 --- a/src/dcp.cc +++ b/src/dcp.cc @@ -1,5 +1,5 @@ /* - Copyright (C) 2012 Carl Hetherington + Copyright (C) 2012-2014 Carl Hetherington This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -18,33 +18,35 @@ */ /** @file src/dcp.cc - * @brief A class to create a DCP. + * @brief DCP class. */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include #include "dcp.h" #include "sound_mxf.h" #include "picture_mxf.h" -#include "subtitle_asset.h" +#include "subtitle_content.h" +#include "mono_picture_mxf.h" +#include "stereo_picture_mxf.h" #include "util.h" #include "metadata.h" #include "exceptions.h" -#include "parse/pkl.h" -#include "parse/asset_map.h" #include "reel.h" #include "cpl.h" #include "signer.h" #include "kdm.h" +#include "compose.hpp" +#include "AS_DCP.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include using std::string; using std::list; @@ -53,8 +55,10 @@ using std::ostream; using std::copy; using std::back_inserter; using std::make_pair; +using std::map; using boost::shared_ptr; using boost::lexical_cast; +using boost::dynamic_pointer_cast; using namespace dcp; DCP::DCP (boost::filesystem::path directory) @@ -64,33 +68,154 @@ DCP::DCP (boost::filesystem::path directory) } void -DCP::write_xml (bool interop, XMLMetadata const & metadata, shared_ptr signer) const +DCP::read () { - for (list >::const_iterator i = _cpls.begin(); i != _cpls.end(); ++i) { - (*i)->write_xml (interop, metadata, signer); + /* Read the ASSETMAP */ + + boost::filesystem::path asset_map_file; + if (boost::filesystem::exists (_directory / "ASSETMAP")) { + asset_map_file = _directory / "ASSETMAP"; + } else if (boost::filesystem::exists (_directory / "ASSETMAP.xml")) { + asset_map_file = _directory / "ASSETMAP.xml"; + } else { + boost::throw_exception (DCPReadError ("could not find AssetMap file")); + } + + cxml::Document asset_map ("AssetMap"); + asset_map.read_file (asset_map_file); + list > assets = asset_map.node_child("AssetList")->node_children ("Asset"); + map paths; + for (list >::const_iterator i = assets.begin(); i != assets.end(); ++i) { + if ((*i)->node_child("ChunkList")->node_children("Chunk").size() != 1) { + boost::throw_exception (XMLError ("unsupported asset chunk count")); + } + paths.insert (make_pair ( + (*i)->string_child ("Id"), + (*i)->node_child("ChunkList")->node_child("Chunk")->string_child ("Path") + )); } - string pkl_uuid = make_uuid (); - string pkl_path = write_pkl (pkl_uuid, interop, metadata, signer); + /* Read all the assets from the asset map */ - write_volindex (interop); - write_assetmap (pkl_uuid, boost::filesystem::file_size (pkl_path), interop, metadata); + for (map::const_iterator i = paths.begin(); i != paths.end(); ++i) { + if (boost::algorithm::ends_with (i->second.string(), ".xml")) { + xmlpp::DomParser* p = new xmlpp::DomParser; + try { + p->parse_file (i->second.string()); + } catch (std::exception& e) { + delete p; + continue; + } + + string const root = p->get_document()->get_root_node()->get_name (); + delete p; + + if (root == "CompositionPlaylist") { + _assets.push_back (shared_ptr (new CPL (i->second))); + } else if (root == "DCSubtitle") { + _assets.push_back (shared_ptr (new SubtitleContent (i->second))); + } + } else if (boost::algorithm::ends_with (i->second.string(), ".mxf")) { + ASDCP::EssenceType_t type; + if (ASDCP::EssenceType (i->second.string().c_str(), type) != ASDCP::RESULT_OK) { + throw DCPReadError ("Could not find essence type"); + } + switch (type) { + case ASDCP::ESS_UNKNOWN: + case ASDCP::ESS_MPEG2_VES: + throw DCPReadError ("MPEG2 video essences are not supported"); + case ASDCP::ESS_JPEG_2000: + _assets.push_back (shared_ptr (new MonoPictureMXF (i->second))); + break; + case ASDCP::ESS_PCM_24b_48k: + case ASDCP::ESS_PCM_24b_96k: + _assets.push_back (shared_ptr (new SoundMXF (i->second))); + break; + case ASDCP::ESS_JPEG_2000_S: + _assets.push_back (shared_ptr (new StereoPictureMXF (i->second))); + break; + default: + throw DCPReadError ("Unknown MXF essence type"); + } + } + } +} + +bool +DCP::equals (DCP const & other, EqualityOptions opt, boost::function note) const +{ + if (_assets.size() != other._assets.size()) { + note (ERROR, "Asset counts differ"); + return false; + } + + list >::const_iterator a = _assets.begin (); + list >::const_iterator b = other._assets.begin (); + + while (a != _assets.end ()) { + if (!(*a)->equals (*b, opt, note)) { + return false; + } + ++a; + ++b; + } + + return true; +} + +void +DCP::add (shared_ptr asset) +{ + _assets.push_back (asset); } -std::string -DCP::write_pkl (string pkl_uuid, bool interop, XMLMetadata const & metadata, shared_ptr signer) const +class AssetComparator { - assert (!_cpls.empty ()); +public: + bool operator() (shared_ptr a, shared_ptr b) { + return a->id() < b->id(); + } +}; + +bool +DCP::encrypted () const +{ + list > cpl = cpls (); + for (list >::const_iterator i = cpl.begin(); i != cpl.end(); ++i) { + if ((*i)->encrypted ()) { + return true; + } + } + + return false; +} + +void +DCP::add (KDM const & kdm) +{ + list keys = kdm.keys (); + list > cpl = cpls (); - boost::filesystem::path p; - p /= _directory; + for (list >::iterator i = cpl.begin(); i != cpl.end(); ++i) { + for (list::iterator j = keys.begin(); j != keys.end(); ++j) { + if (j->cpl_id() == (*i)->id()) { + (*i)->add (kdm); + } + } + } +} + +boost::filesystem::path +DCP::write_pkl (Standard standard, string pkl_uuid, XMLMetadata metadata, shared_ptr signer) const +{ + boost::filesystem::path p = _directory; stringstream s; s << pkl_uuid << "_pkl.xml"; p /= s.str(); xmlpp::Document doc; xmlpp::Element* pkl; - if (interop) { + if (standard == INTEROP) { pkl = doc.create_root_node("PackingList", "http://www.digicine.com/PROTO-ASDCP-PKL-20040311#"); } else { pkl = doc.create_root_node("PackingList", "http://www.smpte-ra.org/schemas/429-8/2007/PKL"); @@ -101,36 +226,49 @@ DCP::write_pkl (string pkl_uuid, bool interop, XMLMetadata const & metadata, sha } pkl->add_child("Id")->add_child_text ("urn:uuid:" + pkl_uuid); + /* XXX: this is a bit of a hack */ - pkl->add_child("AnnotationText")->add_child_text(_cpls.front()->name()); + list >::const_iterator i = _assets.begin(); + shared_ptr first_cpl; + while (i != _assets.end()) { + first_cpl = dynamic_pointer_cast (*i); + if (first_cpl) { + break; + } + } + + assert (first_cpl); + pkl->add_child("AnnotationText")->add_child_text (first_cpl->annotation_text ()); + pkl->add_child("IssueDate")->add_child_text (metadata.issue_date); pkl->add_child("Issuer")->add_child_text (metadata.issuer); pkl->add_child("Creator")->add_child_text (metadata.creator); xmlpp::Element* asset_list = pkl->add_child("AssetList"); - list > a = assets (); - for (list >::const_iterator i = a.begin(); i != a.end(); ++i) { - (*i)->write_to_pkl (asset_list); - } - - for (list >::const_iterator i = _cpls.begin(); i != _cpls.end(); ++i) { - (*i)->write_to_pkl (asset_list); + + for (list >::const_iterator i = _assets.begin(); i != _assets.end(); ++i) { + shared_ptr c = dynamic_pointer_cast (*i); + if (c) { + c->write_to_pkl (asset_list); + } } if (signer) { - signer->sign (pkl, interop); + signer->sign (pkl, standard); } doc.write_to_file (p.string (), "UTF-8"); return p.string (); } +/** Write the VOLINDEX file. + * @param standard DCP standard to use (INTEROP or SMPTE) + */ void -DCP::write_volindex (bool interop) const +DCP::write_volindex (Standard standard) const { - boost::filesystem::path p; - p /= _directory; - if (interop) { + boost::filesystem::path p = _directory; + if (standard == INTEROP) { p /= "VOLINDEX"; } else { p /= "VOLINDEX.xml"; @@ -143,11 +281,10 @@ DCP::write_volindex (bool interop) const } void -DCP::write_assetmap (string pkl_uuid, int pkl_length, bool interop, XMLMetadata const & metadata) const +DCP::write_assetmap (Standard standard, string pkl_uuid, int pkl_length, XMLMetadata metadata) const { - boost::filesystem::path p; - p /= _directory; - if (interop) { + boost::filesystem::path p = _directory; + if (standard == INTEROP) { p /= "ASSETMAP"; } else { p /= "ASSETMAP.xml"; @@ -155,7 +292,7 @@ DCP::write_assetmap (string pkl_uuid, int pkl_length, bool interop, XMLMetadata xmlpp::Document doc; xmlpp::Element* root; - if (interop) { + if (standard == INTEROP) { root = doc.create_root_node ("AssetMap", "http://www.digicine.com/PROTO-ASDCP-AM-20040311#"); } else { root = doc.create_root_node ("AssetMap", "http://www.smpte-ra.org/schemas/429-9/2007/AM"); @@ -163,7 +300,7 @@ DCP::write_assetmap (string pkl_uuid, int pkl_length, bool interop, XMLMetadata root->add_child("Id")->add_child_text ("urn:uuid:" + make_uuid()); root->add_child("AnnotationText")->add_child_text ("Created by " + metadata.creator); - if (interop) { + if (standard == INTEROP) { root->add_child("VolumeCount")->add_child_text ("1"); root->add_child("IssueDate")->add_child_text (metadata.issue_date); root->add_child("Issuer")->add_child_text (metadata.issuer); @@ -187,12 +324,7 @@ DCP::write_assetmap (string pkl_uuid, int pkl_length, bool interop, XMLMetadata chunk->add_child("Offset")->add_child_text ("0"); chunk->add_child("Length")->add_child_text (lexical_cast (pkl_length)); - for (list >::const_iterator i = _cpls.begin(); i != _cpls.end(); ++i) { - (*i)->write_to_assetmap (asset_list); - } - - list > a = assets (); - for (list >::const_iterator i = a.begin(); i != a.end(); ++i) { + for (list >::const_iterator i = _assets.begin(); i != _assets.end(); ++i) { (*i)->write_to_assetmap (asset_list); } @@ -201,173 +333,39 @@ DCP::write_assetmap (string pkl_uuid, int pkl_length, bool interop, XMLMetadata } void -DCP::read (bool require_mxfs) +DCP::write_xml ( + Standard standard, + XMLMetadata metadata, + shared_ptr signer + ) { - read_assets (); - read_cpls (require_mxfs); -} - -void -DCP::read_assets () -{ - shared_ptr asset_map; - try { - boost::filesystem::path p = _directory; - p /= "ASSETMAP"; - if (boost::filesystem::exists (p)) { - asset_map.reset (new dcp::parse::AssetMap (p.string ())); - } else { - p = _directory; - p /= "ASSETMAP.xml"; - if (boost::filesystem::exists (p)) { - asset_map.reset (new dcp::parse::AssetMap (p.string ())); - } else { - boost::throw_exception (DCPReadError ("could not find AssetMap file")); - } - } - - } catch (FileError& e) { - boost::throw_exception (FileError ("could not load AssetMap file", _files.asset_map, e.number ())); - } - - for (list >::const_iterator i = asset_map->assets.begin(); i != asset_map->assets.end(); ++i) { - if ((*i)->chunks.size() != 1) { - boost::throw_exception (XMLError ("unsupported asset chunk count")); - } - - boost::filesystem::path t = _directory; - t /= (*i)->chunks.front()->path; - - if (boost::algorithm::ends_with (t.string(), ".mxf") || boost::algorithm::ends_with (t.string(), ".ttf")) { - continue; - } - - xmlpp::DomParser* p = new xmlpp::DomParser; - try { - p->parse_file (t.string()); - } catch (std::exception& e) { - delete p; - continue; - } - - string const root = p->get_document()->get_root_node()->get_name (); - delete p; - - if (root == "CompositionPlaylist") { - _files.cpls.push_back (t.string()); - } else if (root == "PackingList") { - if (_files.pkl.empty ()) { - _files.pkl = t.string(); - } else { - boost::throw_exception (DCPReadError ("duplicate PKLs found")); - } - } - } + string const pkl_uuid = make_uuid (); + boost::filesystem::path const pkl_path = write_pkl (standard, pkl_uuid, metadata, signer); - if (_files.cpls.empty ()) { - boost::throw_exception (DCPReadError ("no CPL files found")); - } - - if (_files.pkl.empty ()) { - boost::throw_exception (DCPReadError ("no PKL file found")); - } - - shared_ptr pkl; - try { - pkl.reset (new parse::PKL (_files.pkl)); - } catch (FileError& e) { - boost::throw_exception (FileError ("could not load PKL file", _files.pkl, e.number ())); - } - - _asset_maps.push_back (make_pair (boost::filesystem::absolute (_directory).string(), asset_map)); -} - -void -DCP::read_cpls (bool require_mxfs) -{ - for (list::iterator i = _files.cpls.begin(); i != _files.cpls.end(); ++i) { - _cpls.push_back (shared_ptr (new CPL (_directory, *i, _asset_maps, require_mxfs))); - } -} - -bool -DCP::equals (DCP const & other, EqualityOptions opt, boost::function note) const -{ - if (_cpls.size() != other._cpls.size()) { - note (ERROR, "CPL counts differ"); - return false; - } - - list >::const_iterator a = _cpls.begin (); - list >::const_iterator b = other._cpls.begin (); - - while (a != _cpls.end ()) { - if (!(*a)->equals (*b->get(), opt, note)) { - return false; + write_volindex (standard); + write_assetmap (standard, pkl_uuid, boost::filesystem::file_size (pkl_path), metadata); + + for (list >::const_iterator i = _assets.begin(); i != _assets.end(); ++i) { + shared_ptr c = dynamic_pointer_cast (*i); + if (c) { + string const filename = c->id() + "_cpl.xml"; + c->write_xml (_directory / filename, standard, metadata, signer); } - ++a; - ++b; - } - - return true; -} - -void -DCP::add_cpl (shared_ptr cpl) -{ - _cpls.push_back (cpl); -} - -class AssetComparator -{ -public: - bool operator() (shared_ptr a, shared_ptr b) { - return a->id() < b->id(); - } -}; - -list > -DCP::assets () const -{ - list > a; - for (list >::const_iterator i = _cpls.begin(); i != _cpls.end(); ++i) { - list > t = (*i)->assets (); - a.merge (t); } - - a.sort (AssetComparator ()); - a.unique (); - return a; } -bool -DCP::encrypted () const +list > +DCP::cpls () const { - for (list >::const_iterator i = _cpls.begin(); i != _cpls.end(); ++i) { - if ((*i)->encrypted ()) { - return true; + list > cpls; + for (list >::const_iterator i = _assets.begin(); i != _assets.end(); ++i) { + shared_ptr c = dynamic_pointer_cast (*i); + if (c) { + cpls.push_back (c); } } - return false; + return cpls; } -void -DCP::add_kdm (KDM const & kdm) -{ - list keys = kdm.keys (); - for (list >::iterator i = _cpls.begin(); i != _cpls.end(); ++i) { - for (list::iterator j = keys.begin(); j != keys.end(); ++j) { - if (j->cpl_id() == (*i)->id()) { - (*i)->add_kdm (kdm); - } - } - } -} - -void -DCP::add_assets_from (DCP const & ov) -{ - copy (ov._asset_maps.begin(), ov._asset_maps.end(), back_inserter (_asset_maps)); -} diff --git a/src/dcp.h b/src/dcp.h index 19903d3f..9f6d60fd 100644 --- a/src/dcp.h +++ b/src/dcp.h @@ -24,12 +24,13 @@ #ifndef LIBDCP_DCP_H #define LIBDCP_DCP_H -#include -#include -#include -#include #include "types.h" #include "certificates.h" +#include "metadata.h" +#include +#include +#include +#include namespace xmlpp { class Document; @@ -41,14 +42,12 @@ namespace dcp { class Content; -class PictureAsset; -class SoundAsset; -class SubtitleAsset; class Reel; class CPL; class XMLMetadata; class Signer; class KDM; +class Asset; namespace parse { class AssetMap; @@ -69,21 +68,7 @@ public: */ DCP (boost::filesystem::path directory); - void read (bool require_mxfs = true); - - /** Read an existing DCP's assets. - * - * The DCP's XML metadata will be examined, and you can then look at the contents - * of the DCP. - */ - void read_assets (); - - void read_cpls (bool require_mxfs = true); - - /** Write the required XML files to the directory that was - * passed into the constructor. - */ - void write_xml (bool interop, XMLMetadata const &, boost::shared_ptr signer = boost::shared_ptr ()) const; + void read (); /** Compare this DCP with another, according to various options. * @param other DCP to compare this one to. @@ -92,29 +77,19 @@ public: */ bool equals (DCP const & other, EqualityOptions options, boost::function note) const; - /** Add a CPL to this DCP. - * @param cpl CPL to add. - */ - void add_cpl (boost::shared_ptr cpl); - - /** @return The list of CPLs in this DCP */ - std::list > cpls () const { - return _cpls; - } + void add (boost::shared_ptr asset); - /** Add another DCP as a source of assets for this DCP. This should be called before - * ::read() on the DCP that needs the extra assets. For example - * - * DCP original_version ("my_dcp_OV"); - * DCP supplemental ("my_dcp_VF"); - * supplemental.add_assets_from (original_version); - * supplemental.read (); - */ - void add_assets_from (DCP const &); + std::list > cpls () const; bool encrypted () const; - void add_kdm (KDM const &); + void add (KDM const &); + + void write_xml ( + Standard standard, + XMLMetadata metadata = XMLMetadata (), + boost::shared_ptr signer = boost::shared_ptr () + ); /** Emitted with a parameter between 0 and 1 to indicate progress * for long jobs. @@ -126,34 +101,19 @@ private: /** Write the PKL file. * @param pkl_uuid UUID to use. */ - std::string write_pkl (std::string pkl_uuid, bool, XMLMetadata const &, boost::shared_ptr) const; + boost::filesystem::path write_pkl (Standard standard, std::string pkl_uuid, XMLMetadata metadata, boost::shared_ptr signer) const; - /** Write the VOLINDEX file */ - void write_volindex (bool) const; + void write_volindex (Standard standard) const; /** Write the ASSETMAP file. * @param pkl_uuid UUID of our PKL. * @param pkl_length Length of our PKL in bytes. */ - void write_assetmap (std::string pkl_uuid, int pkl_length, bool, XMLMetadata const &) const; - - /** @return Assets in all the CPLs in this DCP */ - std::list > assets () const; - - struct Files { - std::list cpls; - std::string pkl; - std::string asset_map; - }; - - Files _files; + void write_assetmap (Standard standard, std::string pkl_uuid, int pkl_length, XMLMetadata metadata) const; /** the directory that we are writing to */ boost::filesystem::path _directory; - /** our CPLs */ - std::list > _cpls; - - std::list _asset_maps; + std::list > _assets; }; } diff --git a/src/exceptions.cc b/src/exceptions.cc index c3755a60..e5a46948 100644 --- a/src/exceptions.cc +++ b/src/exceptions.cc @@ -17,13 +17,17 @@ */ +/** @file src/exceptions.cc + * @brief Exceptions thrown by libdcp. + */ + #include "exceptions.h" #include "compose.hpp" using std::string; using namespace dcp; -FileError::FileError (std::string const & message, boost::filesystem::path filename, int number) +FileError::FileError (string message, boost::filesystem::path filename, int number) : _message (String::compose ("%1 (error %2) (%3)", message, filename.string(), number)) , _filename (filename) , _number (number) @@ -31,3 +35,9 @@ FileError::FileError (std::string const & message, boost::filesystem::path filen } +UnresolvedRefError::UnresolvedRefError (string id) + : _message (String::compose ("Unresolved reference to asset id %1", id)) +{ + +} + diff --git a/src/exceptions.h b/src/exceptions.h index 2a7fbdda..c1d47e51 100644 --- a/src/exceptions.h +++ b/src/exceptions.h @@ -1,5 +1,5 @@ /* - Copyright (C) 2012 Carl Hetherington + Copyright (C) 2012-2014 Carl Hetherington This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -33,7 +33,7 @@ namespace dcp class FileError : public std::exception { public: - FileError (std::string const & message, boost::filesystem::path filename, int number); + FileError (std::string message, boost::filesystem::path filename, int number); ~FileError () throw () {} /** @return error message */ @@ -63,7 +63,7 @@ private: class MXFFileError : public FileError { public: - MXFFileError (std::string const & message, boost::filesystem::path filename, int number) + MXFFileError (std::string message, boost::filesystem::path filename, int number) : FileError (message, filename, number) {} }; @@ -72,7 +72,7 @@ public: class MiscError : public std::exception { public: - MiscError (std::string const & message) : _message (message) {} + MiscError (std::string message) : _message (message) {} ~MiscError () throw () {} /** @return error message */ @@ -89,7 +89,7 @@ private: class DCPReadError : public std::exception { public: - DCPReadError (std::string const & message) : _message (message) {} + DCPReadError (std::string message) : _message (message) {} ~DCPReadError () throw () {} /** @return error message */ @@ -106,7 +106,7 @@ private: class XMLError : public std::exception { public: - XMLError (std::string const & message) : _message (message) {} + XMLError (std::string message) : _message (message) {} ~XMLError () throw () {} /** @return error message */ @@ -118,6 +118,21 @@ private: /** error message */ std::string _message; }; + +class UnresolvedRefError : public std::exception +{ +public: + UnresolvedRefError (std::string id); + ~UnresolvedRefError () throw () {} + + /** @return error message */ + char const * what () const throw () { + return _message.c_str (); + } + +private: + std::string _message; +}; } diff --git a/src/file.cc b/src/file.cc new file mode 100644 index 00000000..d39f93e0 --- /dev/null +++ b/src/file.cc @@ -0,0 +1,38 @@ +/* + Copyright (C) 2014 Carl Hetherington + + 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 "file.h" +#include "util.h" + +using namespace dcp; + +File::File (boost::filesystem::path file) +{ + _size = boost::filesystem::file_size (file); + _data = new uint8_t[_size]; + FILE* f = dcp::fopen_boost (file, "r"); + assert (f); + fread (_data, 1, _size, f); + fclose (f); +} + +File::~File () +{ + delete[] _data; +} diff --git a/src/file.h b/src/file.h new file mode 100644 index 00000000..f2b99209 --- /dev/null +++ b/src/file.h @@ -0,0 +1,43 @@ +/* + Copyright (C) 2014 Carl Hetherington + + 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 + +namespace dcp { + +class File +{ +public: + File (boost::filesystem::path file); + ~File (); + + uint8_t* data () const { + return _data; + } + + int64_t size () const { + return _size; + } + +private: + uint8_t* _data; + int64_t _size; +}; + +} diff --git a/src/parse/subtitle.cc b/src/font.cc similarity index 60% rename from src/parse/subtitle.cc rename to src/font.cc index 3f9869b4..5a568a63 100644 --- a/src/parse/subtitle.cc +++ b/src/font.cc @@ -1,5 +1,5 @@ /* - Copyright (C) 2012-2013 Carl Hetherington + Copyright (C) 2012-2014 Carl Hetherington This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -17,18 +17,16 @@ */ -#include -#include -#include "subtitle.h" -#include "../types.h" +#include "font.h" +#include "xml.h" +#include "text.h" +#include using std::string; using std::list; using boost::shared_ptr; using boost::optional; -using boost::lexical_cast; using namespace dcp; -using namespace dcp::parse; Font::Font (shared_ptr node) { @@ -81,55 +79,3 @@ Font::Font (list > const & font_nodes) } } } - -LoadFont::LoadFont (shared_ptr node) -{ - id = node->string_attribute ("Id"); - uri = node->string_attribute ("URI"); -} - - -Subtitle::Subtitle (shared_ptr node) -{ - in = Time (node->string_attribute ("TimeIn")); - out = Time (node->string_attribute ("TimeOut")); - font_nodes = type_children (node, "Font"); - text_nodes = type_children (node, "Text"); - fade_up_time = fade_time (node, "FadeUpTime"); - fade_down_time = fade_time (node, "FadeDownTime"); -} - -Time -Subtitle::fade_time (shared_ptr 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 (u)); - } - - if (t > Time (0, 0, 8, 0)) { - t = Time (0, 0, 8, 0); - } - - return t; -} - -Text::Text (shared_ptr node) - : v_align (CENTER) -{ - text = node->content (); - v_position = node->number_attribute ("VPosition"); - optional v = node->optional_string_attribute ("VAlign"); - if (v) { - v_align = string_to_valign (v.get ()); - } - - font_nodes = type_children (node, "Font"); -} - diff --git a/src/parse/subtitle.h b/src/font.h similarity index 58% rename from src/parse/subtitle.h rename to src/font.h index 50c2ebc5..1b29e570 100644 --- a/src/parse/subtitle.h +++ b/src/font.h @@ -1,5 +1,5 @@ /* - Copyright (C) 2012 Carl Hetherington + Copyright (C) 2012-2014 Carl Hetherington This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -17,51 +17,18 @@ */ -#include "../xml.h" -#include "../dcp_time.h" -#include "../types.h" +#include "types.h" +#include "subtitle.h" +#include +#include +#include -namespace dcp -{ - -namespace parse -{ - -class Font; +namespace cxml { + class Node; +} -class Text -{ -public: - Text () - : v_position (0) - , v_align (TOP) - {} +namespace dcp { - Text (boost::shared_ptr node); - - float v_position; - VAlign v_align; - std::string text; - std::list > font_nodes; -}; - -class Subtitle -{ -public: - Subtitle () {} - Subtitle (boost::shared_ptr node); - - Time in; - Time out; - Time fade_up_time; - Time fade_down_time; - std::list > font_nodes; - std::list > text_nodes; - -private: - Time fade_time (boost::shared_ptr, std::string name); -}; - class Font { public: @@ -85,16 +52,4 @@ public: std::list > text_nodes; }; -class LoadFont -{ -public: - LoadFont () {} - LoadFont (boost::shared_ptr node); - - std::string id; - std::string uri; -}; - -} - } diff --git a/src/gamma_lut.cc b/src/gamma_lut.cc index 0b8ad2fd..1a954914 100644 --- a/src/gamma_lut.cc +++ b/src/gamma_lut.cc @@ -17,18 +17,18 @@ */ -#include #include "gamma_lut.h" #include "lut_cache.h" +#include using namespace dcp; LUTCache GammaLUT::cache; -GammaLUT::GammaLUT(int bits, float gamma) +GammaLUT::GammaLUT (int bits, float gamma) : LUT (bits, gamma) { - int const bit_length = pow(2, bits); + 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 index 095ad882..31615afd 100644 --- a/src/gamma_lut.h +++ b/src/gamma_lut.h @@ -1,5 +1,5 @@ /* - Copyright (C) 2012-2013 Carl Hetherington + Copyright (C) 2012-2014 Carl Hetherington 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 diff --git a/src/kdm.cc b/src/kdm.cc index bdfa1fec..92e9a756 100644 --- a/src/kdm.cc +++ b/src/kdm.cc @@ -108,7 +108,7 @@ KDM::KDM ( apu.recipient.x509_subject_name = recipient_cert->subject (); apu.composition_playlist_id = "urn:uuid:" + cpl->id (); // apu.content_authenticator = signer->certificates().leaf()->thumbprint (); - apu.content_title_text = cpl->name (); + apu.content_title_text = cpl->content_title_text (); apu.content_keys_not_valid_before = ptime_to_string (not_valid_before); apu.content_keys_not_valid_after = ptime_to_string (not_valid_after); apu.authorized_device_info.device_list_identifier = "urn:uuid:" + make_uuid (); diff --git a/src/key.h b/src/key.h index be9f95f4..d6b2f2f2 100644 --- a/src/key.h +++ b/src/key.h @@ -25,6 +25,7 @@ #define LIBDCP_KEY_H #include +#include namespace dcp { diff --git a/src/load_font.cc b/src/load_font.cc new file mode 100644 index 00000000..a337856c --- /dev/null +++ b/src/load_font.cc @@ -0,0 +1,30 @@ +/* + Copyright (C) 2012-2014 Carl Hetherington + + 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 "load_font.h" +#include + +using boost::shared_ptr; +using namespace dcp; + +LoadFont::LoadFont (shared_ptr node) +{ + id = node->string_attribute ("Id"); + uri = node->string_attribute ("URI"); +} diff --git a/src/load_font.h b/src/load_font.h new file mode 100644 index 00000000..6319b1de --- /dev/null +++ b/src/load_font.h @@ -0,0 +1,38 @@ +/* + Copyright (C) 2012-2014 Carl Hetherington + + 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 + +namespace cxml { + class Node; +} + +namespace dcp { + +class LoadFont +{ +public: + LoadFont () {} + LoadFont (boost::shared_ptr node); + + std::string id; + std::string uri; +}; + +} diff --git a/src/mono_picture_mxf.cc b/src/mono_picture_mxf.cc index cea21c15..e33454dc 100644 --- a/src/mono_picture_mxf.cc +++ b/src/mono_picture_mxf.cc @@ -45,14 +45,10 @@ MonoPictureMXF::MonoPictureMXF (boost::filesystem::path file) boost::throw_exception (DCPReadError ("could not read video MXF information")); } - _size.width = desc.StoredWidth; - _size.height = desc.StoredHeight; - _edit_rate = desc.EditRate.Numerator; - assert (desc.EditRate.Denominator == 1); - _intrinsic_duration = desc.ContainerDuration; + read_picture_descriptor (desc); } -MonoPictureMXF::MonoPictureMXF (int edit_rate) +MonoPictureMXF::MonoPictureMXF (Fraction edit_rate) : PictureMXF (edit_rate) { @@ -121,10 +117,10 @@ MonoPictureMXF::equals (shared_ptr other, EqualityOptions opt, bo } shared_ptr -MonoPictureMXF::start_write (boost::filesystem::path file, bool overwrite) +MonoPictureMXF::start_write (boost::filesystem::path file, Standard standard, bool overwrite) { /* XXX: can't we use shared_ptr here? */ - return shared_ptr (new MonoPictureMXFWriter (this, file, overwrite)); + return shared_ptr (new MonoPictureMXFWriter (this, file, standard, overwrite)); } string diff --git a/src/mono_picture_mxf.h b/src/mono_picture_mxf.h index 8c8ee531..9f0a2eea 100644 --- a/src/mono_picture_mxf.h +++ b/src/mono_picture_mxf.h @@ -24,6 +24,8 @@ namespace dcp { +class MonoPictureMXFWriter; + /** @class MonoPictureMXF * @brief A 2D (monoscopic) picture MXF. */ @@ -38,10 +40,10 @@ public: /** Create a MonoPictureMXF with a given edit rate. * @param edit_rate Edit rate (i.e. frame rate) in frames per second. */ - MonoPictureMXF (int edit_rate); + MonoPictureMXF (Fraction edit_rate); /** Start a progressive write to a MonoPictureMXF */ - boost::shared_ptr start_write (boost::filesystem::path, bool); + boost::shared_ptr start_write (boost::filesystem::path, Standard standard, bool); boost::shared_ptr get_frame (int n) const; bool equals (boost::shared_ptr other, EqualityOptions opt, boost::function note) const; diff --git a/src/mono_picture_mxf_writer.cc b/src/mono_picture_mxf_writer.cc index 590a3f7d..619d298e 100644 --- a/src/mono_picture_mxf_writer.cc +++ b/src/mono_picture_mxf_writer.cc @@ -40,8 +40,8 @@ struct MonoPictureMXFWriter::ASDCPState : public ASDCPStateBase /** @param a Asset to write to. `a' must not be deleted while * this writer class still exists, or bad things will happen. */ -MonoPictureMXFWriter::MonoPictureMXFWriter (PictureMXF* asset, boost::filesystem::path file, bool overwrite) - : PictureMXFWriter (asset, file, overwrite) +MonoPictureMXFWriter::MonoPictureMXFWriter (PictureMXF* asset, boost::filesystem::path file, Standard standard, bool overwrite) + : PictureMXFWriter (asset, file, standard, overwrite) , _state (new MonoPictureMXFWriter::ASDCPState) { _state->encryption_context = asset->encryption_context (); @@ -50,7 +50,7 @@ MonoPictureMXFWriter::MonoPictureMXFWriter (PictureMXF* asset, boost::filesystem void MonoPictureMXFWriter::start (uint8_t* data, int size) { - dcp::start (this, _state, _mxf, data, size); + dcp::start (this, _state, _standard, _picture_mxf, data, size); } FrameInfo @@ -95,15 +95,11 @@ MonoPictureMXFWriter::fake_write (int size) void MonoPictureMXFWriter::finalize () { - assert (!_finalized); - Kumu::Result_t r = _state->mxf_writer.Finalize(); if (ASDCP_FAILURE (r)) { boost::throw_exception (MXFFileError ("error in finalizing video MXF", _mxf->file().string(), r)); } - _finalized = true; - _mxf->set_intrinsic_duration (_frames_written); - _mxf->set_duration (_frames_written); + PictureMXFWriter::finalize (); } diff --git a/src/mono_picture_mxf_writer.h b/src/mono_picture_mxf_writer.h index c4d9b6ed..7ff87a5d 100644 --- a/src/mono_picture_mxf_writer.h +++ b/src/mono_picture_mxf_writer.h @@ -45,7 +45,7 @@ public: private: friend class MonoPictureMXF; - MonoPictureMXFWriter (PictureMXF *, boost::filesystem::path file, bool); + MonoPictureMXFWriter (PictureMXF *, boost::filesystem::path file, Standard standard, bool); void start (uint8_t *, int); /* do this with an opaque pointer so we don't have to include diff --git a/src/mxf.cc b/src/mxf.cc index fa3aed8b..42d92536 100644 --- a/src/mxf.cc +++ b/src/mxf.cc @@ -21,10 +21,6 @@ * @brief Parent class for assets of DCPs made up of MXF files. */ -#include -#include -#include -#include #include "AS_DCP.h" #include "KM_prng.h" #include "KM_util.h" @@ -33,6 +29,10 @@ #include "metadata.h" #include "exceptions.h" #include "kdm.h" +#include +#include +#include +#include using std::string; using std::list; @@ -42,12 +42,20 @@ using boost::lexical_cast; using boost::dynamic_pointer_cast; using namespace dcp; +MXF::MXF (Fraction edit_rate) + : Content (edit_rate) + , _progress (0) + , _encryption_context (0) + , _decryption_context (0) +{ + +} + MXF::MXF (boost::filesystem::path file) : Content (file) , _progress (0) , _encryption_context (0) , _decryption_context (0) - , _interop (false) { } @@ -59,13 +67,13 @@ MXF::~MXF () } void -MXF::fill_writer_info (ASDCP::WriterInfo* writer_info) +MXF::fill_writer_info (ASDCP::WriterInfo* writer_info, Standard standard) { writer_info->ProductVersion = _metadata.product_version; writer_info->CompanyName = _metadata.company_name; writer_info->ProductName = _metadata.product_name.c_str(); - if (_interop) { + if (standard == INTEROP) { writer_info->LabelSetType = ASDCP::LS_MXF_INTEROP; } else { writer_info->LabelSetType = ASDCP::LS_MXF_SMPTE; @@ -107,25 +115,6 @@ MXF::equals (shared_ptr other, EqualityOptions opt, boost::functi return true; } -void -MXF::write_to_cpl (xmlpp::Element* node) const -{ - pair const attr = cpl_node_attribute (); - xmlpp::Element* a = node->add_child (cpl_node_name ()); - if (!attr.first.empty ()) { - a->set_attribute (attr.first, attr.second); - } - a->add_child ("Id")->add_child_text ("urn:uuid:" + _id); - a->add_child ("AnnotationText")->add_child_text (_file.string ()); - a->add_child ("EditRate")->add_child_text (lexical_cast (_edit_rate) + " 1"); - a->add_child ("IntrinsicDuration")->add_child_text (lexical_cast (_intrinsic_duration)); - a->add_child ("EntryPoint")->add_child_text (lexical_cast (_entry_point)); - a->add_child ("Duration")->add_child_text (lexical_cast (_duration)); - if (!_key_id.empty ()) { - a->add_child("KeyId")->add_child_text ("urn:uuid:" + _key_id); - } -} - void MXF::set_key (Key key) { diff --git a/src/mxf.h b/src/mxf.h index a83d9fff..3aaadfd7 100644 --- a/src/mxf.h +++ b/src/mxf.h @@ -20,10 +20,10 @@ #ifndef LIBDCP_MXF_ASSET_H #define LIBDCP_MXF_ASSET_H -#include #include "content.h" #include "key.h" #include "metadata.h" +#include namespace ASDCP { class AESEncContext; @@ -41,19 +41,23 @@ class MXFMetadata; class MXF : public Content { public: + MXF (Fraction edit_rate); MXF (boost::filesystem::path file); - MXF (int edit_rate); ~MXF (); virtual bool equals (boost::shared_ptr other, EqualityOptions opt, boost::function note) const; - virtual void write_to_cpl (xmlpp::Element *) const; virtual std::string key_type () const = 0; + + std::string pkl_type () const { + return "application/x-smpte-mxf"; + } /** Fill in a ADSCP::WriteInfo struct. * @param w struct to fill in. + * @param standard INTEROP or SMPTE. */ - void fill_writer_info (ASDCP::WriterInfo* w); + void fill_writer_info (ASDCP::WriterInfo* w, Standard standard); void set_progress (boost::signals2::signal* progress) { _progress = progress; @@ -89,23 +93,7 @@ public: return _metadata; } - /** Set whether or not the asset should be written in Interop mode. - * @param i true to use interop. - */ - void set_interop (bool i) { - _interop = i; - } - - bool interop () const { - return _interop; - } - protected: - virtual std::string cpl_node_name () const = 0; - virtual std::pair cpl_node_attribute () const { - return std::make_pair ("", ""); - } - /** Signal to emit to report progress, or 0 */ boost::signals2::signal* _progress; ASDCP::AESEncContext* _encryption_context; @@ -113,7 +101,6 @@ protected: std::string _key_id; boost::optional _key; MXFMetadata _metadata; - bool _interop; }; } diff --git a/src/mxf_writer.cc b/src/mxf_writer.cc new file mode 100644 index 00000000..c5fca159 --- /dev/null +++ b/src/mxf_writer.cc @@ -0,0 +1,46 @@ +/* + Copyright (C) 2012-2014 Carl Hetherington + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include "mxf_writer.h" +#include "mxf.h" + +using namespace dcp; + +MXFWriter::MXFWriter (MXF* mxf, boost::filesystem::path file) + : _mxf (mxf) + , _file (file) + , _frames_written (0) + , _finalized (false) +{ + mxf->_file = file; + mxf->_hash.clear (); +} + +MXFWriter::~MXFWriter () +{ + assert (_finalized); +} + +void +MXFWriter::finalize () +{ + assert (!_finalized); + _finalized = true; + _mxf->_intrinsic_duration = _frames_written; +} diff --git a/src/mxf_writer.h b/src/mxf_writer.h new file mode 100644 index 00000000..19632131 --- /dev/null +++ b/src/mxf_writer.h @@ -0,0 +1,46 @@ +/* + Copyright (C) 2012-2014 Carl Hetherington + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#ifndef LIBDCP_MXF_WRITER_H +#define LIBDCP_MXF_WRITER_H + +#include + +namespace dcp { + +class MXF; + +class MXFWriter : public boost::noncopyable +{ +public: + virtual ~MXFWriter (); + virtual void finalize (); + +protected: + MXFWriter (MXF* mxf, boost::filesystem::path file); + + MXF* _mxf; + boost::filesystem::path _file; + int64_t _frames_written; + bool _finalized; +}; + +} + +#endif diff --git a/src/object.cc b/src/object.cc index e9a4f62b..e3849d79 100644 --- a/src/object.cc +++ b/src/object.cc @@ -23,12 +23,16 @@ using std::string; using namespace dcp; +/** Create an Object with a random ID. */ Object::Object () : _id (make_uuid ()) { } +/** Create an Object with a given ID. + * @param ID to use. + */ Object::Object (string id) : _id (id) { diff --git a/src/object.h b/src/object.h index 636c76a0..e12888e8 100644 --- a/src/object.h +++ b/src/object.h @@ -24,7 +24,9 @@ namespace dcp { -/** Some part of a DCP that has a UUID */ +/** @class Object + * @brief Some part of a DCP that has a UUID. + */ class Object { public: @@ -32,11 +34,13 @@ public: Object (std::string id); virtual ~Object () {} + /** @return ID */ std::string id () const { return _id; } protected: + /** ID */ std::string _id; }; diff --git a/src/parse/asset_map.cc b/src/parse/asset_map.cc deleted file mode 100644 index 0b323fb3..00000000 --- a/src/parse/asset_map.cc +++ /dev/null @@ -1,79 +0,0 @@ -/* - Copyright (C) 2012 Carl Hetherington - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - -*/ - -/** @file src/asset_map.cc - * @brief Classes used to parse a AssetMap. - */ - -#include -#include "asset_map.h" -#include "../util.h" -#include "../xml.h" - -using std::string; -using std::list; -using boost::shared_ptr; -using namespace dcp::parse; - -AssetMap::AssetMap (string file) -{ - cxml::Document f ("AssetMap"); - f.read_file (file); - - id = f.string_child ("Id"); - creator = f.string_child ("Creator"); - volume_count = f.number_child ("VolumeCount"); - issue_date = f.string_child ("IssueDate"); - issuer = f.string_child ("Issuer"); - assets = type_grand_children (f, "AssetList", "Asset"); -} - -AssetMapAsset::AssetMapAsset (shared_ptr node) -{ - id = node->string_child ("Id"); - packing_list = node->optional_string_child ("PackingList").get_value_or (""); - chunks = type_grand_children (node, "ChunkList", "Chunk"); -} - -Chunk::Chunk (shared_ptr node) -{ - path = node->string_child ("Path"); - - string const prefix = "file://"; - - if (boost::algorithm::starts_with (path, prefix)) { - path = path.substr (prefix.length()); - } - - volume_index = node->optional_number_child ("VolumeIndex").get_value_or (0); - offset = node->optional_number_child ("Offset").get_value_or (0); - length = node->optional_number_child ("Length").get_value_or (0); -} - -shared_ptr -AssetMap::asset_from_id (string id) const -{ - for (list >::const_iterator i = assets.begin (); i != assets.end(); ++i) { - if ((*i)->id == id) { - return *i; - } - } - - return shared_ptr (); -} diff --git a/src/parse/asset_map.h b/src/parse/asset_map.h deleted file mode 100644 index e3035593..00000000 --- a/src/parse/asset_map.h +++ /dev/null @@ -1,81 +0,0 @@ -/* - Copyright (C) 2012 Carl Hetherington - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - -*/ - -/** @file src/asset_map.h - * @brief Classes used to parse a AssetMap. - */ - -#include -#include -#include - -namespace dcp { - -namespace parse { - -/** @class Chunk - * @brief A simple parser for and representation of a \ node within an asset map. - */ -class Chunk -{ -public: - Chunk (); - Chunk (boost::shared_ptr node); - - std::string path; - int64_t volume_index; - int64_t offset; - int64_t length; -}; - -/** @class AssetMapAsset - * @brief A simple parser for and representation of an \ node within an asset map. - */ -class AssetMapAsset -{ -public: - AssetMapAsset (); - AssetMapAsset (boost::shared_ptr node); - - std::string id; - std::string packing_list; - std::list > chunks; -}; - -/** @class AssetMap - * @brief A simple parser for and representation of an asset map file. - */ -class AssetMap -{ -public: - AssetMap (std::string file); - - boost::shared_ptr asset_from_id (std::string id) const; - - std::string id; - std::string creator; - int64_t volume_count; - std::string issue_date; - std::string issuer; - std::list > assets; -}; - -} - -} diff --git a/src/parse/cpl.cc b/src/parse/cpl.cc deleted file mode 100644 index d94fb68e..00000000 --- a/src/parse/cpl.cc +++ /dev/null @@ -1,151 +0,0 @@ -/* - Copyright (C) 2012 Carl Hetherington - - 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 -#include "cpl.h" -#include "../xml.h" -#include "../util.h" - -using std::string; -using std::bad_cast; -using boost::shared_ptr; -using namespace dcp::parse; - -CPL::CPL (string file) -{ - cxml::Document f ("CompositionPlaylist"); - f.read_file (file); - - id = f.string_child ("Id"); - annotation_text = f.optional_string_child ("AnnotationText").get_value_or (""); - issue_date = f.string_child ("IssueDate"); - creator = f.optional_string_child ("Creator").get_value_or (""); - content_title_text = f.string_child ("ContentTitleText"); - content_kind = content_kind_from_string (f.string_child ("ContentKind")); - content_version = optional_type_child (f, "ContentVersion"); - f.ignore_child ("RatingList"); - reels = type_grand_children (f, "ReelList", "Reel"); - - f.ignore_child ("Issuer"); - f.ignore_child ("Signer"); - f.ignore_child ("Signature"); - - f.done (); -} - -ContentVersion::ContentVersion (shared_ptr node) -{ - id = node->optional_string_child ("Id").get_value_or (""); - label_text = node->string_child ("LabelText"); - node->done (); -} - -Reel::Reel (shared_ptr node) -{ - id = node->string_child ("Id"); - asset_list = type_child (node, "AssetList"); - - node->ignore_child ("AnnotationText"); - node->done (); -} - -CPLAssetList::CPLAssetList (shared_ptr node) -{ - main_picture = optional_type_child (node, "MainPicture"); - main_stereoscopic_picture = optional_type_child (node, "MainStereoscopicPicture"); - main_sound = optional_type_child (node, "MainSound"); - main_subtitle = optional_type_child (node, "MainSubtitle"); - - node->done (); -} - -MainPicture::MainPicture (shared_ptr node) - : Picture (node) -{ - -} - -MainStereoscopicPicture::MainStereoscopicPicture (shared_ptr node) - : Picture (node) -{ - -} - -Picture::Picture (shared_ptr 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 ("IntrinsicDuration"); - entry_point = node->number_child ("EntryPoint"); - duration = node->number_child ("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 ("ScreenAspectRatio"); - screen_aspect_ratio = Fraction (f * 1000, 1000); - } catch (bad_cast& e) { - - } - - key_id = node->optional_string_child ("KeyId").get_value_or (""); - - node->ignore_child ("Hash"); - - node->done (); -} - -MainSound::MainSound (shared_ptr 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 ("IntrinsicDuration"); - entry_point = node->number_child ("EntryPoint"); - duration = node->number_child ("Duration"); - key_id = node->optional_string_child ("KeyId").get_value_or (""); - - node->ignore_child ("Hash"); - node->ignore_child ("Language"); - - node->done (); -} - -MainSubtitle::MainSubtitle (shared_ptr 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 ("IntrinsicDuration"); - entry_point = node->number_child ("EntryPoint"); - duration = node->number_child ("Duration"); - - node->ignore_child ("Hash"); - node->ignore_child ("Language"); - - node->done (); -} diff --git a/src/parse/cpl.h b/src/parse/cpl.h deleted file mode 100644 index 4889a1c4..00000000 --- a/src/parse/cpl.h +++ /dev/null @@ -1,163 +0,0 @@ -/* - Copyright (C) 2012 Carl Hetherington - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - -*/ - -/** @file src/parse/cpl.h - * @brief Classes used to parse a CPL. - */ - -#include -#include -#include -#include "../types.h" - -namespace dcp { - -namespace parse { - -/** @brief A simple representation of a CPL \ node */ -class Picture -{ -public: - Picture () {} - Picture (boost::shared_ptr node); - - std::string id; - std::string annotation_text; - Fraction edit_rate; - /** Duration of the whole thing */ - int64_t intrinsic_duration; - /** Start point in frames */ - int64_t entry_point; - /** Duration that will actually play */ - int64_t duration; - Fraction frame_rate; - Fraction screen_aspect_ratio; - std::string key_id; -}; - - -/** @brief A simple parser for and representation of a CPL \ node */ -class MainPicture : public Picture -{ -public: - MainPicture () {} - MainPicture (boost::shared_ptr node); -}; - -/** @brief A simple parser for and representation of a CPL \ node */ -class MainStereoscopicPicture : public Picture -{ -public: - MainStereoscopicPicture () {} - MainStereoscopicPicture (boost::shared_ptr node); -}; - -/** @brief A simple parser for and representation of a CPL \ node */ -class MainSound -{ -public: - MainSound () {} - MainSound (boost::shared_ptr node); - - std::string id; - std::string annotation_text; - Fraction edit_rate; - int64_t intrinsic_duration; - int64_t entry_point; - int64_t duration; - std::string key_id; -}; - -/** @brief A simple parser for and representation of a CPL \ node */ -class MainSubtitle -{ -public: - MainSubtitle () {} - MainSubtitle (boost::shared_ptr node); - - std::string id; - std::string annotation_text; - Fraction edit_rate; - int64_t intrinsic_duration; - int64_t entry_point; - int64_t duration; -}; - -/** @brief A simple parser for and representation of a CPL \ node */ -class CPLAssetList -{ -public: - CPLAssetList () {} - CPLAssetList (boost::shared_ptr node); - - boost::shared_ptr main_picture; - boost::shared_ptr main_stereoscopic_picture; - boost::shared_ptr main_sound; - boost::shared_ptr main_subtitle; -}; - -/** @brief A simple parser for and representation of a CPL \ node */ -class Reel -{ -public: - Reel () {} - Reel (boost::shared_ptr node); - - std::string id; - boost::shared_ptr asset_list; -}; - - -/** @brief A simple parser for and representation of a CPL \ node */ -class ContentVersion -{ -public: - ContentVersion () {} - ContentVersion (boost::shared_ptr node); - - std::string id; - std::string label_text; -}; - -/** @class CPL - * @brief Class to parse a CPL - * - * This class is used to parse XML CPL files. It is rarely necessary - * for the caller to use it outside libdcp. - */ -class CPL -{ -public: - /** Parse a CPL XML file into our member variables */ - CPL (std::string file); - - std::string id; - std::string annotation_text; - std::string issue_date; - std::string creator; - std::string content_title_text; - ContentKind content_kind; - boost::shared_ptr content_version; - std::list > reels; -}; - -} - -} - diff --git a/src/parse/pkl.cc b/src/parse/pkl.cc deleted file mode 100644 index d0fa1556..00000000 --- a/src/parse/pkl.cc +++ /dev/null @@ -1,52 +0,0 @@ -/* - Copyright (C) 2012 Carl Hetherington - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - -*/ - -/** @file src/pkl_file.cc - * @brief Classes used to parse a PKL. - */ - -#include -#include "pkl.h" - -using namespace std; -using namespace boost; -using namespace dcp::parse; - -PKL::PKL (string file) -{ - cxml::Document f ("PackingList"); - f.read_file (file); - - id = f.string_child ("Id"); - annotation_text = f.optional_string_child ("AnnotationText").get_value_or (""); - issue_date = f.string_child ("IssueDate"); - issuer = f.string_child ("Issuer"); - creator = f.string_child ("Creator"); - assets = type_grand_children (f, "AssetList", "Asset"); -} - -PKLAsset::PKLAsset (boost::shared_ptr node) -{ - id = node->string_child ("Id"); - annotation_text = node->optional_string_child ("AnnotationText").get_value_or (""); - hash = node->string_child ("Hash"); - size = node->number_child ("Size"); - type = node->string_child ("Type"); - original_file_name = node->optional_string_child ("OriginalFileName").get_value_or (""); -} diff --git a/src/picture_mxf.cc b/src/picture_mxf.cc index 8f0f85b9..7634493d 100644 --- a/src/picture_mxf.cc +++ b/src/picture_mxf.cc @@ -17,10 +17,6 @@ */ -/** @file src/picture_asset.cc - * @brief An asset made up of JPEG2000 files - */ - #include #include #include @@ -58,33 +54,21 @@ PictureMXF::PictureMXF (boost::filesystem::path file) } -PictureMXF::PictureMXF (int edit_rate) +PictureMXF::PictureMXF (Fraction edit_rate) : MXF (edit_rate) { } void -PictureMXF::write_to_cpl (xmlpp::Element* node) const +PictureMXF::read_picture_descriptor (ASDCP::JP2K::PictureDescriptor const & desc) { - MXF::write_to_cpl (node); - - xmlpp::Node::NodeList c = node->get_children (); - xmlpp::Node::NodeList::iterator i = c.begin(); - while (i != c.end() && (*i)->get_name() != cpl_node_name ()) { - ++i; - } - - assert (i != c.end ()); - - (*i)->add_child ("FrameRate")->add_child_text (lexical_cast (_edit_rate * edit_rate_factor ()) + " 1"); - if (_interop) { - stringstream s; - s << std::fixed << std::setprecision (2) << (float (_size.width) / _size.height); - (*i)->add_child ("ScreenAspectRatio")->add_child_text (s.str ()); - } else { - (*i)->add_child ("ScreenAspectRatio")->add_child_text (lexical_cast (_size.width) + " " + lexical_cast (_size.height)); - } + _size.width = desc.StoredWidth; + _size.height = desc.StoredHeight; + _edit_rate = Fraction (desc.EditRate.Numerator, desc.EditRate.Denominator); + _intrinsic_duration = desc.ContainerDuration; + _frame_rate = Fraction (desc.SampleRate.Numerator, desc.SampleRate.Denominator); + _screen_aspect_ratio = Fraction (desc.AspectRatio.Numerator, desc.AspectRatio.Denominator); } bool @@ -212,6 +196,3 @@ PictureMXF::key_type () const { return "MDIK"; } - - - diff --git a/src/picture_mxf.h b/src/picture_mxf.h index 3a587280..df75625c 100644 --- a/src/picture_mxf.h +++ b/src/picture_mxf.h @@ -47,19 +47,27 @@ class PictureMXF : public MXF { public: PictureMXF (boost::filesystem::path file); - PictureMXF (int edit_rate); + PictureMXF (Fraction edit_rate); - virtual boost::shared_ptr start_write (boost::filesystem::path file, bool overwrite) = 0; + virtual boost::shared_ptr start_write (boost::filesystem::path file, Standard standard, bool overwrite) = 0; + + void write_to_pkl (xmlpp::Node* node) const; Size size () const { return _size; } - void set_size (Size s) { - _size = s; + Fraction frame_rate () const { + return _frame_rate; } - void write_to_cpl (xmlpp::Element *) const; + Fraction screen_aspect_ratio () const { + return _screen_aspect_ratio; + } + + Fraction edit_rate () const { + return _edit_rate; + } protected: @@ -72,8 +80,12 @@ protected: ASDCP::JP2K::PictureDescriptor const & a, ASDCP::JP2K::PictureDescriptor const & b, boost::function ) const; + void read_picture_descriptor (ASDCP::JP2K::PictureDescriptor const &); + /** picture size in pixels */ Size _size; + Fraction _frame_rate; + Fraction _screen_aspect_ratio; private: std::string key_type () const; diff --git a/src/picture_mxf_writer.cc b/src/picture_mxf_writer.cc index 1a128b33..46426401 100644 --- a/src/picture_mxf_writer.cc +++ b/src/picture_mxf_writer.cc @@ -1,5 +1,5 @@ /* - Copyright (C) 2012-2013 Carl Hetherington + Copyright (C) 2012-2014 Carl Hetherington This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -17,13 +17,13 @@ */ -#include -#include -#include "AS_DCP.h" #include "KM_fileio.h" #include "picture_mxf_writer.h" #include "exceptions.h" #include "picture_mxf.h" +#include "AS_DCP.h" +#include +#include using std::istream; using std::ostream; @@ -82,14 +82,12 @@ FrameInfo::write (FILE* f) const #endif } - -PictureMXFWriter::PictureMXFWriter (PictureMXF* mxf, boost::filesystem::path file, bool overwrite) - : _mxf (mxf) - , _file (file) - , _frames_written (0) +PictureMXFWriter::PictureMXFWriter (PictureMXF* mxf, boost::filesystem::path file, Standard standard, bool overwrite) + : MXFWriter (mxf, file) + , _picture_mxf (mxf) , _started (false) - , _finalized (false) + , _standard (standard) , _overwrite (overwrite) { - + } diff --git a/src/picture_mxf_writer.h b/src/picture_mxf_writer.h index bcd8873d..f8c67ba9 100644 --- a/src/picture_mxf_writer.h +++ b/src/picture_mxf_writer.h @@ -1,5 +1,5 @@ /* - Copyright (C) 2012-2013 Carl Hetherington + Copyright (C) 2012-2014 Carl Hetherington This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -17,13 +17,14 @@ */ +#include "metadata.h" +#include "types.h" +#include "mxf_writer.h" +#include +#include #include #include #include -#include -#include -#include "metadata.h" -#include "types.h" namespace dcp { @@ -49,31 +50,21 @@ struct FrameInfo std::string hash; }; -class PictureMXFWriter : public boost::noncopyable +class PictureMXFWriter : public MXFWriter { public: - virtual ~PictureMXFWriter () {} virtual FrameInfo write (uint8_t *, int) = 0; - virtual void finalize () = 0; virtual void fake_write (int) = 0; protected: template - friend void start (PictureMXFWriter *, boost::shared_ptr

, Q *, uint8_t *, int); - - PictureMXFWriter (PictureMXF *, boost::filesystem::path, bool); + friend void start (PictureMXFWriter *, boost::shared_ptr

, Standard, Q *, uint8_t *, int); - PictureMXF* _mxf; + PictureMXFWriter (PictureMXF *, boost::filesystem::path, Standard standard, bool); - boost::filesystem::path _file; - /** Number of picture frames written to the asset so far. For stereo assets - * this will be incremented for each eye (i.e. there will be twice the number - * of frames as in a mono asset). - */ - int _frames_written; + PictureMXF* _picture_mxf; bool _started; - /** true if finalize() has been called */ - bool _finalized; + Standard _standard; bool _overwrite; }; diff --git a/src/picture_mxf_writer_common.cc b/src/picture_mxf_writer_common.cc index 6ac8ae35..9836f03e 100644 --- a/src/picture_mxf_writer_common.cc +++ b/src/picture_mxf_writer_common.cc @@ -33,7 +33,7 @@ struct ASDCPStateBase }; template -void dcp::start (PictureMXFWriter* writer, shared_ptr

state, Q* mxf, uint8_t* data, int size) +void dcp::start (PictureMXFWriter* writer, shared_ptr

state, Standard standard, Q* mxf, uint8_t* data, int size) { mxf->set_file (writer->_file); @@ -42,9 +42,9 @@ void dcp::start (PictureMXFWriter* writer, shared_ptr

state, Q* mxf, uint8_t* } state->j2k_parser.FillPictureDescriptor (state->picture_descriptor); - state->picture_descriptor.EditRate = ASDCP::Rational (mxf->edit_rate(), 1); + state->picture_descriptor.EditRate = ASDCP::Rational (mxf->edit_rate().numerator, mxf->edit_rate().denominator); - mxf->fill_writer_info (&state->writer_info); + mxf->fill_writer_info (&state->writer_info, standard); Kumu::Result_t r = state->mxf_writer.OpenWrite ( mxf->file().string().c_str(), diff --git a/src/reel.cc b/src/reel.cc index 9f514d88..27fc0329 100644 --- a/src/reel.cc +++ b/src/reel.cc @@ -17,15 +17,19 @@ */ -#include #include "reel.h" #include "util.h" #include "picture_mxf.h" #include "mono_picture_mxf.h" #include "stereo_picture_mxf.h" #include "sound_mxf.h" -#include "subtitle_asset.h" +#include "subtitle_content.h" +#include "reel_mono_picture_asset.h" +#include "reel_stereo_picture_asset.h" +#include "reel_sound_asset.h" +#include "reel_subtitle_asset.h" #include "kdm.h" +#include using std::string; using std::list; @@ -34,8 +38,37 @@ using boost::shared_ptr; using boost::dynamic_pointer_cast; using namespace dcp; +Reel::Reel (shared_ptr node) + : Object (node->string_child ("Id")) +{ + shared_ptr asset_list = node->node_child ("AssetList"); + + shared_ptr main_picture = asset_list->optional_node_child ("MainPicture"); + if (main_picture) { + _main_picture.reset (new ReelMonoPictureAsset (main_picture)); + } + + shared_ptr main_stereoscopic_picture = asset_list->optional_node_child ("MainStereoscopicPicture"); + if (main_stereoscopic_picture) { + _main_picture.reset (new ReelStereoPictureAsset (main_stereoscopic_picture)); + } + + shared_ptr main_sound = asset_list->optional_node_child ("MainSound"); + if (main_sound) { + _main_sound.reset (new ReelSoundAsset (main_sound)); + } + + shared_ptr main_subtitle = asset_list->optional_node_child ("MainSubtitle"); + if (main_subtitle) { + _main_subtitle.reset (new ReelSubtitleAsset (main_subtitle)); + } + + node->ignore_child ("AnnotationText"); + node->done (); +} + void -Reel::write_to_cpl (xmlpp::Element* node) const +Reel::write_to_cpl (xmlpp::Element* node, Standard standard) const { xmlpp::Element* reel = node->add_child ("Reel"); reel->add_child("Id")->add_child_text ("urn:uuid:" + make_uuid()); @@ -43,20 +76,20 @@ Reel::write_to_cpl (xmlpp::Element* node) const if (_main_picture && dynamic_pointer_cast (_main_picture)) { /* Mono pictures come before other stuff... */ - _main_picture->write_to_cpl (asset_list); + _main_picture->write_to_cpl (asset_list, standard); } if (_main_sound) { - _main_sound->write_to_cpl (asset_list); + _main_sound->write_to_cpl (asset_list, standard); } if (_main_subtitle) { - _main_subtitle->write_to_cpl (asset_list); + _main_subtitle->write_to_cpl (asset_list, standard); } if (_main_picture && dynamic_pointer_cast (_main_picture)) { /* ... but stereo pictures must come after */ - _main_picture->write_to_cpl (asset_list); + _main_picture->write_to_cpl (asset_list, standard); } } @@ -106,10 +139,10 @@ Reel::add_kdm (KDM const & kdm) for (list::iterator i = keys.begin(); i != keys.end(); ++i) { if (i->key_id() == _main_picture->key_id()) { - _main_picture->set_key (i->key ()); + _main_picture->mxf()->set_key (i->key ()); } if (i->key_id() == _main_sound->key_id()) { - _main_sound->set_key (i->key ()); + _main_sound->mxf()->set_key (i->key ()); } } } @@ -117,10 +150,25 @@ Reel::add_kdm (KDM const & kdm) void Reel::set_mxf_keys (Key key) { - _main_picture->set_key (key); + _main_picture->mxf()->set_key (key); if (_main_sound) { - _main_sound->set_key (key); + _main_sound->mxf()->set_key (key); } /* XXX: subtitle asset? */ } + +void +Reel::add (shared_ptr asset) +{ + shared_ptr p = dynamic_pointer_cast (asset); + shared_ptr so = dynamic_pointer_cast (asset); + shared_ptr su = dynamic_pointer_cast (asset); + if (p) { + _main_picture = p; + } else if (so) { + _main_sound = so; + } else if (su) { + _main_subtitle = su; + } +} diff --git a/src/reel.h b/src/reel.h index 41e811e4..8aaebf51 100644 --- a/src/reel.h +++ b/src/reel.h @@ -20,51 +20,59 @@ #ifndef LIBDCP_REEL_H #define LIBDCP_REEL_H -#include -#include -#include -#include #include "key.h" #include "types.h" +#include "ref.h" +#include +#include +#include +#include -namespace xmlpp { +namespace cxml { class Node; } namespace dcp { -class PictureMXF; -class SoundMXF; -class SubtitleAsset; -class KDM; +class KDM; +class ReelAsset; +class ReelPictureAsset; +class ReelSoundAsset; +class ReelSubtitleAsset; /** @brief A reel within a DCP; the part which actually contains picture, sound and subtitle data */ -class Reel +class Reel : public Object { public: + Reel () {} + Reel ( - boost::shared_ptr picture, - boost::shared_ptr sound, - boost::shared_ptr subtitle + boost::shared_ptr picture, + boost::shared_ptr sound, + boost::shared_ptr subtitle ) : _main_picture (picture) , _main_sound (sound) , _main_subtitle (subtitle) {} + + Reel (boost::shared_ptr); - boost::shared_ptr main_picture () const { + boost::shared_ptr main_picture () const { return _main_picture; } - boost::shared_ptr main_sound () const { + boost::shared_ptr main_sound () const { return _main_sound; } - boost::shared_ptr main_subtitle () const { + boost::shared_ptr main_subtitle () const { return _main_subtitle; } - void write_to_cpl (xmlpp::Element *) const; + void add (boost::shared_ptr asset); + + void write_to_cpl (xmlpp::Element* node, Standard standard) const; bool encrypted () const; @@ -75,9 +83,9 @@ public: void add_kdm (KDM const &); private: - boost::shared_ptr _main_picture; - boost::shared_ptr _main_sound; - boost::shared_ptr _main_subtitle; + boost::shared_ptr _main_picture; + boost::shared_ptr _main_sound; + boost::shared_ptr _main_subtitle; }; } diff --git a/src/reel_asset.cc b/src/reel_asset.cc new file mode 100644 index 00000000..8e03b5c4 --- /dev/null +++ b/src/reel_asset.cc @@ -0,0 +1,101 @@ +/* + Copyright (C) 2014 Carl Hetherington + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include "reel_asset.h" +#include "content.h" +#include "compose.hpp" +#include + +using std::pair; +using std::string; +using std::make_pair; +using boost::shared_ptr; +using boost::lexical_cast; +using namespace dcp; + +ReelAsset::ReelAsset () + : Object (make_uuid ()) + , _content (_id) + , _edit_rate (Fraction (24, 1)) + , _intrinsic_duration (0) + , _entry_point (0) + , _duration (0) +{ + +} + +ReelAsset::ReelAsset (shared_ptr content, int64_t entry_point) + : Object (content->id ()) + , _content (content) + , _edit_rate (content->edit_rate ()) + , _intrinsic_duration (content->intrinsic_duration ()) + , _entry_point (entry_point) + , _duration (_intrinsic_duration - _entry_point) + , _hash (make_digest (content->file (), 0)) +{ + +} + +ReelAsset::ReelAsset (shared_ptr node) + : Object (node->string_child ("Id")) + , _content (_id) + , _annotation_text (node->optional_string_child ("AnnotationText").get_value_or ("")) + , _edit_rate (Fraction (node->string_child ("EditRate"))) + , _intrinsic_duration (node->number_child ("IntrinsicDuration")) + , _entry_point (node->number_child ("EntryPoint")) + , _duration (node->number_child ("Duration")) + , _hash (node->string_child ("Hash")) + , _key_id (node->optional_string_child ("KeyId").get_value_or ("")) +{ + if (_id.length() > 9) { + _id = _id.substr (9); + _content.set_id (_id); + } + + if (_key_id.length() > 9) { + _key_id = _key_id.substr (9); + } + + node->done (); +} + +void +ReelAsset::write_to_cpl (xmlpp::Node* node, Standard) const +{ + pair const attr = cpl_node_attribute (); + xmlpp::Element* a = node->add_child (cpl_node_name ()); + if (!attr.first.empty ()) { + a->set_attribute (attr.first, attr.second); + } + a->add_child("Id")->add_child_text ("urn:uuid:" + _id); + a->add_child("AnnotationText")->add_child_text (_annotation_text); + a->add_child("EditRate")->add_child_text (String::compose ("%1 %2", _edit_rate.numerator, _edit_rate.denominator)); + a->add_child("IntrinsicDuration")->add_child_text (lexical_cast (_intrinsic_duration)); + a->add_child("EntryPoint")->add_child_text (lexical_cast (_entry_point)); + a->add_child("Duration")->add_child_text (lexical_cast (_duration)); + if (!_key_id.empty ()) { + a->add_child("KeyId")->add_child_text ("urn:uuid:" + _key_id); + } +} + +pair +ReelAsset::cpl_node_attribute () const +{ + return make_pair ("", ""); +} diff --git a/src/reel_asset.h b/src/reel_asset.h new file mode 100644 index 00000000..5207166a --- /dev/null +++ b/src/reel_asset.h @@ -0,0 +1,82 @@ +/* + Copyright (C) 2014 Carl Hetherington + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#ifndef LIBDCP_REEL_ASSET_H +#define LIBDCP_REEL_ASSET_H + +#include "object.h" +#include "util.h" +#include "ref.h" +#include + +namespace cxml { + class Node; +} + +namespace xmlpp { + class Node; +} + +namespace dcp { + +class Content; + +class ReelAsset : public Object +{ +public: + ReelAsset (); + ReelAsset (boost::shared_ptr content, int64_t entry_point); + ReelAsset (boost::shared_ptr); + + Ref content () const { + return _content; + } + + bool encrypted () const { + return !_key_id.empty (); + } + + std::string key_id () const { + return _key_id; + } + + virtual void write_to_cpl (xmlpp::Node* node, Standard standard) const; + virtual bool equals (boost::shared_ptr, EqualityOptions, boost::function) const { + return false; + } + +protected: + virtual std::string cpl_node_name () const = 0; + virtual std::pair cpl_node_attribute () const; + + Ref _content; + +private: + std::string _annotation_text; + Fraction _edit_rate; + int64_t _intrinsic_duration; + int64_t _entry_point; + int64_t _duration; + std::string _hash; + std::string _key_id; +}; + +} + +#endif diff --git a/src/reel_mono_picture_asset.cc b/src/reel_mono_picture_asset.cc new file mode 100644 index 00000000..892c9f4e --- /dev/null +++ b/src/reel_mono_picture_asset.cc @@ -0,0 +1,49 @@ +/* + Copyright (C) 2014 Carl Hetherington + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include "reel_mono_picture_asset.h" +#include "mono_picture_mxf.h" + +using std::string; +using boost::shared_ptr; +using namespace dcp; + +ReelMonoPictureAsset::ReelMonoPictureAsset () +{ + +} + +ReelMonoPictureAsset::ReelMonoPictureAsset (shared_ptr mxf, int64_t entry_point) + : ReelPictureAsset (mxf, entry_point) +{ + +} + +ReelMonoPictureAsset::ReelMonoPictureAsset (shared_ptr node) + : ReelPictureAsset (node) +{ + +} + +string +ReelMonoPictureAsset::cpl_node_name () const +{ + return "MainPicture"; +} + diff --git a/src/reel_mono_picture_asset.h b/src/reel_mono_picture_asset.h new file mode 100644 index 00000000..881bbec0 --- /dev/null +++ b/src/reel_mono_picture_asset.h @@ -0,0 +1,40 @@ +/* + Copyright (C) 2014 Carl Hetherington + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include "reel_picture_asset.h" + +namespace dcp { + +class MonoPictureMXF; + +class ReelMonoPictureAsset : public ReelPictureAsset +{ +public: + ReelMonoPictureAsset (); + ReelMonoPictureAsset (boost::shared_ptr content, int64_t entry_point); + ReelMonoPictureAsset (boost::shared_ptr); + +private: + std::string cpl_node_name () const; +}; + +} + + + diff --git a/src/reel_picture_asset.cc b/src/reel_picture_asset.cc new file mode 100644 index 00000000..9fdc3b9e --- /dev/null +++ b/src/reel_picture_asset.cc @@ -0,0 +1,88 @@ +/* + Copyright (C) 2014 Carl Hetherington + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include "content.h" +#include "reel_picture_asset.h" +#include "picture_mxf.h" +#include "compose.hpp" +#include + +using std::bad_cast; +using std::string; +using std::stringstream; +using boost::shared_ptr; +using boost::lexical_cast; +using namespace dcp; + +ReelPictureAsset::ReelPictureAsset () + : _frame_rate (Fraction (24, 1)) + , _screen_aspect_ratio (Fraction (1998, 1080)) +{ + +} + +ReelPictureAsset::ReelPictureAsset (shared_ptr content, int64_t entry_point) + : ReelAsset (content, entry_point) + , _frame_rate (content->frame_rate ()) + , _screen_aspect_ratio (content->screen_aspect_ratio ()) +{ + +} + +ReelPictureAsset::ReelPictureAsset (shared_ptr node) + : ReelAsset (node) +{ + _frame_rate = Fraction (node->string_child ("FrameRate")); + try { + _screen_aspect_ratio = Fraction (node->string_child ("ScreenAspectRatio")); + } catch (XMLError& e) { + /* Maybe it's not a fraction */ + } + try { + float f = node->number_child ("ScreenAspectRatio"); + _screen_aspect_ratio = Fraction (f * 1000, 1000); + } catch (bad_cast& e) { + + } +} + +void +ReelPictureAsset::write_to_cpl (xmlpp::Node* node, Standard standard) const +{ + ReelAsset::write_to_cpl (node, standard); + + xmlpp::Node::NodeList c = node->get_children (); + xmlpp::Node::NodeList::iterator i = c.begin(); + while (i != c.end() && (*i)->get_name() != cpl_node_name ()) { + ++i; + } + + assert (i != c.end ()); + + (*i)->add_child ("FrameRate")->add_child_text (String::compose ("%1 %2", _frame_rate.numerator, _frame_rate.denominator)); + if (standard == INTEROP) { + stringstream s; + s << std::fixed << std::setprecision (2) << (float (_screen_aspect_ratio.numerator) / _screen_aspect_ratio.denominator); + (*i)->add_child ("ScreenAspectRatio")->add_child_text (s.str ()); + } else { + (*i)->add_child ("ScreenAspectRatio")->add_child_text ( + String::compose ("%1 %2", _screen_aspect_ratio.numerator, _screen_aspect_ratio.denominator) + ); + } +} diff --git a/src/reel_picture_asset.h b/src/reel_picture_asset.h new file mode 100644 index 00000000..c6eb2c73 --- /dev/null +++ b/src/reel_picture_asset.h @@ -0,0 +1,52 @@ +/* + Copyright (C) 2014 Carl Hetherington + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#ifndef LIBDCP_REEL_PICTURE_ASSET_H +#define LIBDCP_REEL_PICTURE_ASSET_H + +#include "reel_asset.h" +#include "picture_mxf.h" + +namespace dcp { + +class ReelPictureAsset : public ReelAsset +{ +public: + ReelPictureAsset (); + ReelPictureAsset (boost::shared_ptr content, int64_t entry_point); + ReelPictureAsset (boost::shared_ptr); + + boost::shared_ptr mxf () { + return boost::dynamic_pointer_cast (_content.object ()); + } + + void set_screen_aspect_ratio (Fraction a) { + _screen_aspect_ratio = a; + } + + virtual void write_to_cpl (xmlpp::Node* node, Standard standard) const; + +private: + Fraction _frame_rate; + Fraction _screen_aspect_ratio; +}; + +} + +#endif diff --git a/src/reel_sound_asset.cc b/src/reel_sound_asset.cc new file mode 100644 index 00000000..6ef473fe --- /dev/null +++ b/src/reel_sound_asset.cc @@ -0,0 +1,44 @@ +/* + Copyright (C) 2014 Carl Hetherington + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include "reel_sound_asset.h" +#include + +using std::string; +using boost::shared_ptr; +using namespace dcp; + +ReelSoundAsset::ReelSoundAsset (shared_ptr content, int64_t entry_point) + : ReelAsset (content, entry_point) +{ + +} + +ReelSoundAsset::ReelSoundAsset (shared_ptr node) + : ReelAsset (node) +{ + node->ignore_child ("Language"); + node->done (); +} + +string +ReelSoundAsset::cpl_node_name () const +{ + return "MainSound"; +} diff --git a/src/reel_sound_asset.h b/src/reel_sound_asset.h new file mode 100644 index 00000000..27d2499f --- /dev/null +++ b/src/reel_sound_asset.h @@ -0,0 +1,42 @@ +/* + Copyright (C) 2014 Carl Hetherington + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include "reel_asset.h" +#include "sound_mxf.h" +#include +#include + +namespace dcp { + +class ReelSoundAsset : public ReelAsset +{ +public: + ReelSoundAsset (boost::shared_ptr content, int64_t entry_point); + ReelSoundAsset (boost::shared_ptr); + + boost::shared_ptr mxf () { + return boost::dynamic_pointer_cast (_content.object ()); + } + +private: + std::string cpl_node_name () const; +}; + +} + diff --git a/src/reel_stereo_picture_asset.cc b/src/reel_stereo_picture_asset.cc new file mode 100644 index 00000000..2715eb34 --- /dev/null +++ b/src/reel_stereo_picture_asset.cc @@ -0,0 +1,50 @@ +/* + Copyright (C) 2014 Carl Hetherington + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include "reel_stereo_picture_asset.h" + +using std::string; +using std::pair; +using std::make_pair; +using boost::shared_ptr; +using namespace dcp; + +ReelStereoPictureAsset::ReelStereoPictureAsset (shared_ptr node) + : ReelPictureAsset (node) +{ + +} + +string +ReelStereoPictureAsset::cpl_node_name () const +{ + return "msp-cpl:MainStereoscopicPicture"; +} + +pair +ReelStereoPictureAsset::cpl_node_attribute (Standard standard) const +{ + if (standard == INTEROP) { + return make_pair ("xmlns:msp-cpl", "http://www.digicine.com/schemas/437-Y/2007/Main-Stereo-Picture-CPL"); + } else { + return make_pair ("xmlns:msp-cpl", "http://www.smpte-ra.org/schemas/429-10/2008/Main-Stereo-Picture-CPL"); + } + + return make_pair ("", ""); +} diff --git a/src/reel_stereo_picture_asset.h b/src/reel_stereo_picture_asset.h new file mode 100644 index 00000000..6ae67217 --- /dev/null +++ b/src/reel_stereo_picture_asset.h @@ -0,0 +1,37 @@ +/* + Copyright (C) 2014 Carl Hetherington + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include "reel_picture_asset.h" + +namespace dcp { + +class ReelStereoPictureAsset : public ReelPictureAsset +{ +public: + ReelStereoPictureAsset (boost::shared_ptr); + +private: + std::string cpl_node_name () const; + std::pair cpl_node_attribute (Standard standard) const; +}; + +} + + + diff --git a/src/reel_subtitle_asset.cc b/src/reel_subtitle_asset.cc new file mode 100644 index 00000000..326e343c --- /dev/null +++ b/src/reel_subtitle_asset.cc @@ -0,0 +1,44 @@ +/* + Copyright (C) 2012-2014 Carl Hetherington + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include "subtitle_content.h" +#include "reel_subtitle_asset.h" + +using std::string; +using boost::shared_ptr; +using namespace dcp; + +ReelSubtitleAsset::ReelSubtitleAsset (shared_ptr content, int64_t entry_point) + : ReelAsset (content, entry_point) +{ + +} + +ReelSubtitleAsset::ReelSubtitleAsset (shared_ptr node) + : ReelAsset (node) +{ + node->ignore_child ("Language"); + node->done (); +} + +string +ReelSubtitleAsset::cpl_node_name () const +{ + return "MainSubtitle"; +} diff --git a/src/parse/pkl.h b/src/reel_subtitle_asset.h similarity index 58% rename from src/parse/pkl.h rename to src/reel_subtitle_asset.h index f31eef7d..7b17b8a9 100644 --- a/src/parse/pkl.h +++ b/src/reel_subtitle_asset.h @@ -17,44 +17,24 @@ */ -/** @file src/parse/pkl.h - * @brief Classes used to parse a PKL - */ - -#include -#include "../xml.h" +#include "reel_asset.h" namespace dcp { -namespace parse { +class SubtitleContent; -class PKLAsset +class ReelSubtitleAsset : public ReelAsset { public: - PKLAsset () {} - PKLAsset (boost::shared_ptr); - - std::string id; - std::string annotation_text; - std::string hash; - int64_t size; - std::string type; - std::string original_file_name; -}; + ReelSubtitleAsset (boost::shared_ptr content, int64_t entry_point); + ReelSubtitleAsset (boost::shared_ptr); -class PKL -{ -public: - PKL (std::string file); - - std::string id; - std::string annotation_text; - std::string issue_date; - std::string issuer; - std::string creator; - std::list > assets; + boost::shared_ptr subtitle_content () const { + return boost::dynamic_pointer_cast (_content.object ()); + } + +private: + std::string cpl_node_name () const; }; - -} } diff --git a/src/ref.h b/src/ref.h new file mode 100644 index 00000000..db39bfb6 --- /dev/null +++ b/src/ref.h @@ -0,0 +1,91 @@ +/* + Copyright (C) 2014 Carl Hetherington + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#ifndef LIBDCP_REF_H +#define LIBDCP_REF_H + +#include "exceptions.h" +#include "object.h" +#include +#include + +namespace dcp { + +template +class Ref +{ +public: + Ref (std::string id) + : _id (id) + {} + + Ref (boost::shared_ptr object) + : _id (object->id ()) + , _object (object) + {} + + void set_id (std::string id) + { + _id = id; + } + + void resolve (std::list > objects) + { + typename std::list >::iterator i = objects.begin(); + while (i != objects.end() && (*i)->id() != _id) { + ++i; + } + + if (i != objects.end ()) { + _object = boost::dynamic_pointer_cast (*i); + } + } + + std::string id () const { + return _id; + } + + boost::shared_ptr object () const { + if (!_object) { + throw UnresolvedRefError (_id); + } + + return _object; + } + + T * operator->() const { + if (!_object) { + throw UnresolvedRefError (_id); + } + + return _object.get (); + } + + bool resolved () const { + return _object; + } + +private: + std::string _id; + boost::shared_ptr _object; +}; + +} + +#endif diff --git a/src/signer.cc b/src/signer.cc index ea13563f..4c5d199a 100644 --- a/src/signer.cc +++ b/src/signer.cc @@ -1,5 +1,5 @@ /* - Copyright (C) 2013 Carl Hetherington + Copyright (C) 2013-2014 Carl Hetherington This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -17,14 +17,18 @@ */ +/** @file src/signer.cc + * @brief Signer class. + */ + +#include "signer.h" +#include "exceptions.h" +#include #include #include #include #include #include -#include -#include "signer.h" -#include "exceptions.h" using std::string; using std::list; @@ -32,18 +36,30 @@ using std::cout; using boost::shared_ptr; using namespace dcp; -/** @param signer_key Filename of private key to sign with */ +/** Add a <Signer> and <ds:Signature> nodes to an XML node. + * @param parent XML node to add to. + * @param interop true to use Interop standards, false for SMPTE. + */ void -Signer::sign (xmlpp::Element* parent, bool interop) const +Signer::sign (xmlpp::Element* parent, Standard standard) const { - add_signer (parent, "dsig"); + /* */ + + xmlpp::Element* signer = parent->add_child("Signer"); + xmlpp::Element* data = signer->add_child("X509Data", "dsig"); + xmlpp::Element* serial_element = data->add_child("X509IssuerSerial", "dsig"); + serial_element->add_child("X509IssuerName", "dsig")->add_child_text (_certificates.leaf()->issuer()); + serial_element->add_child("X509SerialNumber", "dsig")->add_child_text (_certificates.leaf()->serial()); + data->add_child("X509SubjectName", "dsig")->add_child_text (_certificates.leaf()->subject()); + /* */ + xmlpp::Element* signature = parent->add_child("Signature", "dsig"); xmlpp::Element* signed_info = signature->add_child ("SignedInfo", "dsig"); signed_info->add_child("CanonicalizationMethod", "dsig")->set_attribute ("Algorithm", "http://www.w3.org/TR/2001/REC-xml-c14n-20010315"); - if (interop) { + if (standard == INTEROP) { signed_info->add_child("SignatureMethod", "dsig")->set_attribute("Algorithm", "http://www.w3.org/2000/09/xmldsig#rsa-sha1"); } else { signed_info->add_child("SignatureMethod", "dsig")->set_attribute("Algorithm", "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"); @@ -113,21 +129,3 @@ Signer::add_signature_value (xmlpp::Node* parent, string ns) const xmlSecDSigCtxDestroy (signature_context); } - -void -Signer::add_signer (xmlpp::Element* parent, string ns) const -{ - xmlpp::Element* signer = parent->add_child("Signer"); - - { - xmlpp::Element* data = signer->add_child("X509Data", ns); - - { - xmlpp::Element* serial_element = data->add_child("X509IssuerSerial", ns); - serial_element->add_child("X509IssuerName", ns)->add_child_text (_certificates.leaf()->issuer()); - serial_element->add_child("X509SerialNumber", ns)->add_child_text (_certificates.leaf()->serial()); - } - - data->add_child("X509SubjectName", ns)->add_child_text (_certificates.leaf()->subject()); - } -} diff --git a/src/signer.h b/src/signer.h index 7c2864fd..c57c73cf 100644 --- a/src/signer.h +++ b/src/signer.h @@ -17,8 +17,13 @@ */ -#include +/** @file src/signer.h + * @brief Signer class. + */ + #include "certificates.h" +#include "types.h" +#include namespace xmlpp { class Element; @@ -27,15 +32,21 @@ namespace xmlpp { namespace dcp { +/** @class Signer + * @brief A class which can sign XML files. + */ class Signer { public: + /** @param c Certificate chain to sign with. + * @param k Key to sign with. + */ Signer (CertificateChain c, boost::filesystem::path k) : _certificates (c) , _key (k) {} - void sign (xmlpp::Element* parent, bool interop) const; + void sign (xmlpp::Element* parent, Standard standard) const; void add_signature_value (xmlpp::Node* parent, std::string ns) const; CertificateChain const & certificates () const { @@ -44,8 +55,7 @@ public: private: - void add_signer (xmlpp::Element* parent, std::string ns) const; - + /** Certificate chain to sign with */ CertificateChain _certificates; /** Filename of signer key */ boost::filesystem::path _key; diff --git a/src/sound_mxf.cc b/src/sound_mxf.cc index 63f9ba05..82ab0823 100644 --- a/src/sound_mxf.cc +++ b/src/sound_mxf.cc @@ -32,6 +32,7 @@ #include "util.h" #include "exceptions.h" #include "sound_frame.h" +#include "sound_mxf_writer.h" using std::string; using std::stringstream; @@ -60,15 +61,17 @@ SoundMXF::SoundMXF (boost::filesystem::path file) _sampling_rate = desc.AudioSamplingRate.Numerator / desc.AudioSamplingRate.Denominator; _channels = desc.ChannelCount; - _edit_rate = desc.EditRate.Numerator; - assert (desc.EditRate.Denominator == 1); + _edit_rate = Fraction (desc.EditRate.Numerator, desc.EditRate.Denominator); + _intrinsic_duration = desc.ContainerDuration; } -string -SoundMXF::cpl_node_name () const +SoundMXF::SoundMXF (Fraction edit_rate, int sampling_rate, int channels) + : MXF (edit_rate) + , _channels (channels) + , _sampling_rate (sampling_rate) { - return "MainSound"; + } bool @@ -155,105 +158,10 @@ SoundMXF::get_frame (int n) const } shared_ptr -SoundMXF::start_write () +SoundMXF::start_write (boost::filesystem::path file, Standard standard) { /* XXX: can't we use a shared_ptr here? */ - return shared_ptr (new SoundMXFWriter (this)); -} - -struct SoundMXFWriter::ASDCPState -{ - ASDCP::PCM::MXFWriter mxf_writer; - ASDCP::PCM::FrameBuffer frame_buffer; - ASDCP::WriterInfo writer_info; - ASDCP::PCM::AudioDescriptor audio_desc; - ASDCP::AESEncContext* encryption_context; -}; - -SoundMXFWriter::SoundMXFWriter (SoundMXF* a) - : _state (new SoundMXFWriter::ASDCPState) - , _asset (a) - , _finalized (false) - , _frames_written (0) - , _frame_buffer_offset (0) -{ - _state->encryption_context = a->encryption_context (); - - /* Derived from ASDCP::Wav::SimpleWaveHeader::FillADesc */ - _state->audio_desc.EditRate = ASDCP::Rational (_asset->edit_rate(), 1); - _state->audio_desc.AudioSamplingRate = ASDCP::Rational (_asset->sampling_rate(), 1); - _state->audio_desc.Locked = 0; - _state->audio_desc.ChannelCount = _asset->channels (); - _state->audio_desc.QuantizationBits = 24; - _state->audio_desc.BlockAlign = 3 * _asset->channels(); - _state->audio_desc.AvgBps = _asset->sampling_rate() * _state->audio_desc.BlockAlign; - _state->audio_desc.LinkedTrackID = 0; - _state->audio_desc.ChannelFormat = ASDCP::PCM::CF_NONE; - - _state->frame_buffer.Capacity (ASDCP::PCM::CalcFrameBufferSize (_state->audio_desc)); - _state->frame_buffer.Size (ASDCP::PCM::CalcFrameBufferSize (_state->audio_desc)); - memset (_state->frame_buffer.Data(), 0, _state->frame_buffer.Capacity()); - - _asset->fill_writer_info (&_state->writer_info); - - Kumu::Result_t r = _state->mxf_writer.OpenWrite (_asset->file().string().c_str(), _state->writer_info, _state->audio_desc); - if (ASDCP_FAILURE (r)) { - boost::throw_exception (FileError ("could not open audio MXF for writing", _asset->file().string(), r)); - } -} - -void -SoundMXFWriter::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 -SoundMXFWriter::write_current_frame () -{ - ASDCP::Result_t const r = _state->mxf_writer.WriteFrame (_state->frame_buffer, _state->encryption_context, 0); - if (ASDCP_FAILURE (r)) { - boost::throw_exception (MiscError ("could not write audio MXF frame (" + lexical_cast (int (r)) + ")")); - } - - ++_frames_written; -} - -void -SoundMXFWriter::finalize () -{ - if (_frame_buffer_offset > 0) { - write_current_frame (); - } - - if (ASDCP_FAILURE (_state->mxf_writer.Finalize())) { - boost::throw_exception (MiscError ("could not finalise audio MXF")); - } - - _finalized = true; - _asset->set_intrinsic_duration (_frames_written); - _asset->set_duration (_frames_written); + return shared_ptr (new SoundMXFWriter (this, file, standard)); } string diff --git a/src/sound_mxf.h b/src/sound_mxf.h index 4216accb..098680ac 100644 --- a/src/sound_mxf.h +++ b/src/sound_mxf.h @@ -1,5 +1,5 @@ /* - Copyright (C) 2012 Carl Hetherington + Copyright (C) 2012-2014 Carl Hetherington This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -17,12 +17,8 @@ */ -#ifndef LIBDCP_SOUND_ASSET_H -#define LIBDCP_SOUND_ASSET_H - -/** @file src/sound_asset.h - * @brief An asset made up of PCM audio data files - */ +#ifndef LIBDCP_SOUND_MXF_H +#define LIBDCP_SOUND_MXF_H #include "mxf.h" #include "types.h" @@ -32,45 +28,15 @@ namespace dcp { class SoundFrame; -class SoundMXF; - -class SoundMXFWriter -{ -public: - void write (float const * const *, int); - void finalize (); - -private: - friend class SoundMXF; - - SoundMXFWriter (SoundMXF *); - - /* no copy construction */ - SoundMXFWriter (SoundMXFWriter const &); - SoundMXFWriter& operator= (SoundMXFWriter 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 _state; - - SoundMXF* _asset; - bool _finalized; - int _frames_written; - int _frame_buffer_offset; -}; +class SoundMXFWriter; -/** @brief An asset made up of WAV files */ class SoundMXF : public MXF { public: SoundMXF (boost::filesystem::path file); + SoundMXF (Fraction edit_rate, int sampling_rate, int channels); - boost::shared_ptr start_write (); + boost::shared_ptr start_write (boost::filesystem::path file, Standard standard); bool equals (boost::shared_ptr other, EqualityOptions opt, boost::function note) const; @@ -94,7 +60,6 @@ public: private: std::string key_type () const; - std::string cpl_node_name () const; /** Number of channels in the asset */ int _channels; diff --git a/src/sound_mxf_writer.cc b/src/sound_mxf_writer.cc new file mode 100644 index 00000000..5dcd6791 --- /dev/null +++ b/src/sound_mxf_writer.cc @@ -0,0 +1,122 @@ +/* + Copyright (C) 2012-2014 Carl Hetherington + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include "sound_mxf_writer.h" +#include "sound_mxf.h" +#include "exceptions.h" +#include "compose.hpp" +#include "AS_DCP.h" +#include + +using boost::lexical_cast; +using namespace dcp; + +struct SoundMXFWriter::ASDCPState +{ + ASDCP::PCM::MXFWriter mxf_writer; + ASDCP::PCM::FrameBuffer frame_buffer; + ASDCP::WriterInfo writer_info; + ASDCP::PCM::AudioDescriptor audio_desc; + ASDCP::AESEncContext* encryption_context; +}; + +SoundMXFWriter::SoundMXFWriter (SoundMXF* m, boost::filesystem::path file, Standard standard) + : MXFWriter (m, file) + , _state (new SoundMXFWriter::ASDCPState) + , _sound_mxf (m) + , _frame_buffer_offset (0) +{ + _state->encryption_context = m->encryption_context (); + + /* Derived from ASDCP::Wav::SimpleWaveHeader::FillADesc */ + _state->audio_desc.EditRate = ASDCP::Rational (_sound_mxf->edit_rate().numerator, _sound_mxf->edit_rate().denominator); + _state->audio_desc.AudioSamplingRate = ASDCP::Rational (_sound_mxf->sampling_rate(), 1); + _state->audio_desc.Locked = 0; + _state->audio_desc.ChannelCount = _sound_mxf->channels (); + _state->audio_desc.QuantizationBits = 24; + _state->audio_desc.BlockAlign = 3 * _sound_mxf->channels(); + _state->audio_desc.AvgBps = _sound_mxf->sampling_rate() * _state->audio_desc.BlockAlign; + _state->audio_desc.LinkedTrackID = 0; + _state->audio_desc.ChannelFormat = ASDCP::PCM::CF_NONE; + + _state->frame_buffer.Capacity (ASDCP::PCM::CalcFrameBufferSize (_state->audio_desc)); + _state->frame_buffer.Size (ASDCP::PCM::CalcFrameBufferSize (_state->audio_desc)); + memset (_state->frame_buffer.Data(), 0, _state->frame_buffer.Capacity()); + + _sound_mxf->fill_writer_info (&_state->writer_info, standard); + + Kumu::Result_t r = _state->mxf_writer.OpenWrite (file.string().c_str(), _state->writer_info, _state->audio_desc); + if (ASDCP_FAILURE (r)) { + boost::throw_exception (FileError ("could not open audio MXF for writing", file.string(), r)); + } +} + +void +SoundMXFWriter::write (float const * const * data, int frames) +{ + assert (!_finalized); + + for (int i = 0; i < frames; ++i) { + + byte_t* out = _state->frame_buffer.Data() + _frame_buffer_offset; + + /* Write one sample per channel */ + for (int j = 0; j < _sound_mxf->channels(); ++j) { + int32_t const s = data[j][i] * (1 << 23); + *out++ = (s & 0xff); + *out++ = (s & 0xff00) >> 8; + *out++ = (s & 0xff0000) >> 16; + } + _frame_buffer_offset += 3 * _sound_mxf->channels(); + + assert (_frame_buffer_offset <= int (_state->frame_buffer.Capacity())); + + /* Finish the MXF frame if required */ + if (_frame_buffer_offset == int (_state->frame_buffer.Capacity())) { + write_current_frame (); + _frame_buffer_offset = 0; + memset (_state->frame_buffer.Data(), 0, _state->frame_buffer.Capacity()); + } + } +} + +void +SoundMXFWriter::write_current_frame () +{ + ASDCP::Result_t const r = _state->mxf_writer.WriteFrame (_state->frame_buffer, _state->encryption_context, 0); + if (ASDCP_FAILURE (r)) { + boost::throw_exception (MiscError (String::compose ("could not write audio MXF frame (%1)", int (r)))); + } + + ++_frames_written; +} + +void +SoundMXFWriter::finalize () +{ + if (_frame_buffer_offset > 0) { + write_current_frame (); + } + + if (ASDCP_FAILURE (_state->mxf_writer.Finalize())) { + boost::throw_exception (MiscError ("could not finalise audio MXF")); + } + + MXFWriter::finalize (); +} diff --git a/src/sound_mxf_writer.h b/src/sound_mxf_writer.h new file mode 100644 index 00000000..4f60074c --- /dev/null +++ b/src/sound_mxf_writer.h @@ -0,0 +1,55 @@ +/* + Copyright (C) 2012-2014 Carl Hetherington + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include "mxf_writer.h" +#include "types.h" +#include +#include + +namespace dcp { + +class SoundFrame; +class SoundMXF; + +class SoundMXFWriter : public MXFWriter +{ +public: + void write (float const * const *, int); + void finalize (); + +private: + friend class SoundMXF; + + SoundMXFWriter (SoundMXF *, boost::filesystem::path, Standard standard); + + void write_current_frame (); + + /* do this with an opaque pointer so we don't have to include + ASDCP headers + */ + + struct ASDCPState; + boost::shared_ptr _state; + + SoundMXF* _sound_mxf; + int _frame_buffer_offset; +}; + +} + diff --git a/src/stereo_picture_frame.cc b/src/stereo_picture_frame.cc index 893a9f72..790fcc1a 100644 --- a/src/stereo_picture_frame.cc +++ b/src/stereo_picture_frame.cc @@ -63,6 +63,8 @@ StereoPictureFrame::~StereoPictureFrame () * @param reduce a factor by which to reduce the resolution * of the image, expressed as a power of two (pass 0 for no * reduction). + * @param srgb_gamma Reciprocal of gamma to use when doing the + * output gamma correction (after conversion from XYZ to RGB). * * @return An ARGB representation of one of the eyes (left or right) * of this frame. This is ARGB in the Cairo sense, so that each diff --git a/src/stereo_picture_mxf.cc b/src/stereo_picture_mxf.cc index 7e112687..980c6ad5 100644 --- a/src/stereo_picture_mxf.cc +++ b/src/stereo_picture_mxf.cc @@ -30,6 +30,48 @@ using boost::shared_ptr; using boost::dynamic_pointer_cast; using namespace dcp; +StereoPictureMXF::StereoPictureMXF (boost::filesystem::path file) + : PictureMXF (file) +{ + ASDCP::JP2K::MXFSReader reader; + Kumu::Result_t r = reader.OpenRead (file.string().c_str()); + if (ASDCP_FAILURE (r)) { + boost::throw_exception (MXFFileError ("could not open MXF file for reading", file.string(), r)); + } + + ASDCP::JP2K::PictureDescriptor desc; + if (ASDCP_FAILURE (reader.FillPictureDescriptor (desc))) { + boost::throw_exception (DCPReadError ("could not read video MXF information")); + } + + read_picture_descriptor (desc); +} + +StereoPictureMXF::StereoPictureMXF (Fraction edit_rate) + : PictureMXF + (edit_rate) +{ + +} + +shared_ptr +StereoPictureMXF::get_frame (int n) const +{ + return shared_ptr (new StereoPictureFrame (file().string(), n)); +} + +shared_ptr +StereoPictureMXF::start_write (boost::filesystem::path file, Standard standard, bool overwrite) +{ + return shared_ptr (new StereoPictureMXFWriter (this, file, standard, overwrite)); +} + +int +StereoPictureMXF::edit_rate_factor () const +{ + return 2; +} + bool StereoPictureMXF::equals (shared_ptr other, EqualityOptions opt, boost::function note) const { @@ -88,57 +130,3 @@ StereoPictureMXF::equals (shared_ptr other, EqualityOptions opt, return true; } - -StereoPictureMXF::StereoPictureMXF (boost::filesystem::path file) - : PictureMXF (file) -{ - ASDCP::JP2K::MXFSReader reader; - Kumu::Result_t r = reader.OpenRead (file.string().c_str()); - if (ASDCP_FAILURE (r)) { - boost::throw_exception (MXFFileError ("could not open MXF file for reading", file.string(), r)); - } - - ASDCP::JP2K::PictureDescriptor desc; - if (ASDCP_FAILURE (reader.FillPictureDescriptor (desc))) { - boost::throw_exception (DCPReadError ("could not read video MXF information")); - } - - _size.width = desc.StoredWidth; - _size.height = desc.StoredHeight; -} - -shared_ptr -StereoPictureMXF::get_frame (int n) const -{ - return shared_ptr (new StereoPictureFrame (file().string(), n)); -} - -shared_ptr -StereoPictureMXF::start_write (boost::filesystem::path file, bool overwrite) -{ - return shared_ptr (new StereoPictureMXFWriter (this, file, overwrite)); -} - -string -StereoPictureMXF::cpl_node_name () const -{ - return "msp-cpl:MainStereoscopicPicture"; -} - -pair -StereoPictureMXF::cpl_node_attribute () const -{ - if (_interop) { - return make_pair ("xmlns:msp-cpl", "http://www.digicine.com/schemas/437-Y/2007/Main-Stereo-Picture-CPL"); - } else { - return make_pair ("xmlns:msp-cpl", "http://www.smpte-ra.org/schemas/429-10/2008/Main-Stereo-Picture-CPL"); - } - - return make_pair ("", ""); -} - -int -StereoPictureMXF::edit_rate_factor () const -{ - return 2; -} diff --git a/src/stereo_picture_mxf.h b/src/stereo_picture_mxf.h index 4af8a327..eb527a1e 100644 --- a/src/stereo_picture_mxf.h +++ b/src/stereo_picture_mxf.h @@ -29,16 +29,15 @@ class StereoPictureMXF : public PictureMXF { public: StereoPictureMXF (boost::filesystem::path file); + StereoPictureMXF (Fraction edit_rate); /** Start a progressive write to a StereoPictureMXF */ - boost::shared_ptr start_write (boost::filesystem::path file, bool); + boost::shared_ptr start_write (boost::filesystem::path file, Standard, bool); boost::shared_ptr get_frame (int n) const; bool equals (boost::shared_ptr other, EqualityOptions opt, boost::function note) const; private: - std::string cpl_node_name () const; - std::pair cpl_node_attribute () const; int edit_rate_factor () const; }; diff --git a/src/stereo_picture_mxf_writer.cc b/src/stereo_picture_mxf_writer.cc index ad029b57..4ea65e05 100644 --- a/src/stereo_picture_mxf_writer.cc +++ b/src/stereo_picture_mxf_writer.cc @@ -36,8 +36,8 @@ struct StereoPictureMXFWriter::ASDCPState : public ASDCPStateBase ASDCP::JP2K::MXFSWriter mxf_writer; }; -StereoPictureMXFWriter::StereoPictureMXFWriter (PictureMXF* mxf, boost::filesystem::path file, bool overwrite) - : PictureMXFWriter (mxf, file, overwrite) +StereoPictureMXFWriter::StereoPictureMXFWriter (PictureMXF* mxf, boost::filesystem::path file, Standard standard, bool overwrite) + : PictureMXFWriter (mxf, file, standard, overwrite) , _state (new StereoPictureMXFWriter::ASDCPState) , _next_eye (EYE_LEFT) { @@ -47,7 +47,7 @@ StereoPictureMXFWriter::StereoPictureMXFWriter (PictureMXF* mxf, boost::filesyst void StereoPictureMXFWriter::start (uint8_t* data, int size) { - dcp::start (this, _state, _mxf, data, size); + dcp::start (this, _state, _standard, _picture_mxf, data, size); } /** Write a frame for one eye. Frames must be written left, then right, then left etc. @@ -84,7 +84,10 @@ StereoPictureMXFWriter::write (uint8_t* data, int size) _next_eye = _next_eye == EYE_LEFT ? EYE_RIGHT : EYE_LEFT; - ++_frames_written; + if (_next_eye == EYE_LEFT) { + ++_frames_written; + } + return FrameInfo (before_offset, _state->mxf_writer.Tell() - before_offset, hash); } @@ -100,20 +103,18 @@ StereoPictureMXFWriter::fake_write (int size) } _next_eye = _next_eye == EYE_LEFT ? EYE_RIGHT : EYE_LEFT; - ++_frames_written; + if (_next_eye == EYE_LEFT) { + ++_frames_written; + } } void StereoPictureMXFWriter::finalize () { - assert (!_finalized); - Kumu::Result_t r = _state->mxf_writer.Finalize(); if (ASDCP_FAILURE (r)) { boost::throw_exception (MXFFileError ("error in finalizing video MXF", _mxf->file().string(), r)); } - _finalized = true; - _mxf->set_intrinsic_duration (_frames_written / 2); - _mxf->set_duration (_frames_written / 2); + PictureMXFWriter::finalize (); } diff --git a/src/stereo_picture_mxf_writer.h b/src/stereo_picture_mxf_writer.h index a34af069..8773eccd 100644 --- a/src/stereo_picture_mxf_writer.h +++ b/src/stereo_picture_mxf_writer.h @@ -17,12 +17,13 @@ */ +#include "picture_mxf_writer.h" +#include "types.h" +#include +#include #include #include #include -#include -#include -#include "picture_mxf_writer.h" namespace dcp { @@ -46,7 +47,7 @@ public: private: friend class StereoPictureMXF; - StereoPictureMXFWriter (PictureMXF *, boost::filesystem::path file, bool); + StereoPictureMXFWriter (PictureMXF *, boost::filesystem::path file, Standard, bool); void start (uint8_t *, int); /* do this with an opaque pointer so we don't have to include diff --git a/src/subtitle.cc b/src/subtitle.cc new file mode 100644 index 00000000..dc7a6b22 --- /dev/null +++ b/src/subtitle.cc @@ -0,0 +1,60 @@ +/* + Copyright (C) 2012-2014 Carl Hetherington + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include "subtitle.h" +#include "xml.h" +#include "font.h" +#include "text.h" +#include + +using std::string; +using boost::shared_ptr; +using boost::lexical_cast; +using namespace dcp; + +Subtitle::Subtitle (shared_ptr node) +{ + in = Time (node->string_attribute ("TimeIn")); + out = Time (node->string_attribute ("TimeOut")); + font_nodes = type_children (node, "Font"); + text_nodes = type_children (node, "Text"); + fade_up_time = fade_time (node, "FadeUpTime"); + fade_down_time = fade_time (node, "FadeDownTime"); +} + +Time +Subtitle::fade_time (shared_ptr 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 (u)); + } + + if (t > Time (0, 0, 8, 0)) { + t = Time (0, 0, 8, 0); + } + + return t; +} diff --git a/src/subtitle.h b/src/subtitle.h new file mode 100644 index 00000000..073bfb0c --- /dev/null +++ b/src/subtitle.h @@ -0,0 +1,55 @@ +/* + Copyright (C) 2012-2014 Carl Hetherington + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#ifndef LIBDCP_SUBTITLE_H +#define LIBDCP_SUBTITLE_H + +#include "dcp_time.h" +#include +#include + +namespace cxml { + class Node; +} + +namespace dcp { + +class Font; +class Text; + +class Subtitle +{ +public: + Subtitle () {} + Subtitle (boost::shared_ptr node); + + Time in; + Time out; + Time fade_up_time; + Time fade_down_time; + std::list > font_nodes; + std::list > text_nodes; + +private: + Time fade_time (boost::shared_ptr, std::string name); +}; + +} + +#endif diff --git a/src/subtitle_asset.h b/src/subtitle_asset.h deleted file mode 100644 index cbd3ad58..00000000 --- a/src/subtitle_asset.h +++ /dev/null @@ -1,198 +0,0 @@ -/* - Copyright (C) 2012 Carl Hetherington - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - -*/ - -#ifndef LIBDCP_SUBTITLE_ASSET_H -#define LIBDCP_SUBTITLE_ASSET_H - -#include -#include "content.h" -#include "dcp_time.h" - -namespace dcp -{ - -namespace parse -{ - class Font; - class Text; - class Subtitle; - class LoadFont; -} - -class Subtitle -{ -public: - Subtitle ( - std::string font, - bool italic, - Color color, - int size, - Time in, - Time out, - float v_position, - VAlign v_align, - std::string text, - Effect effect, - Color effect_color, - Time fade_up_time, - Time fade_down_time - ); - - std::string font () const { - return _font; - } - - bool italic () const { - return _italic; - } - - Color color () const { - return _color; - } - - Time in () const { - return _in; - } - - Time out () const { - return _out; - } - - std::string text () const { - return _text; - } - - float v_position () const { - return _v_position; - } - - VAlign v_align () const { - return _v_align; - } - - Effect effect () const { - return _effect; - } - - Color effect_color () const { - return _effect_color; - } - - Time fade_up_time () const { - return _fade_up_time; - } - - Time fade_down_time () const { - return _fade_down_time; - } - - int size () const { - return _size; - } - - int size_in_pixels (int screen_height) const; - -private: - std::string _font; - bool _italic; - Color _color; - /** Size in points as if the screen height is 11 inches, so a 72pt font - * would be 1/11th of the screen height. - */ - int _size; - Time _in; - Time _out; - /** Vertical position as a proportion of the screen height from the top - * (between 0 and 1) - */ - float _v_position; - VAlign _v_align; - std::string _text; - Effect _effect; - Color _effect_color; - Time _fade_up_time; - Time _fade_down_time; -}; - -bool operator== (Subtitle const & a, Subtitle const & b); -std::ostream& operator<< (std::ostream& s, Subtitle const & sub); - -class SubtitleAsset : public Content -{ -public: - SubtitleAsset (boost::filesystem::path file); - SubtitleAsset (std::string directory, std::string movie_title, std::string language); - - void write_to_cpl (xmlpp::Element *) const; - virtual bool equals (boost::shared_ptr, EqualityOptions, boost::function note) const { - /* XXX */ - note (ERROR, "subtitle assets not compared yet"); - return true; - } - - std::string language () const { - return _language; - } - - std::list > subtitles_at (Time t) const; - std::list > const & subtitles () const { - return _subtitles; - } - - void add (boost::shared_ptr); - - void write_xml () const; - Glib::ustring xml_as_string () const; - -private: - std::string font_id_to_name (std::string id) const; - - struct ParseState { - std::list > font_nodes; - std::list > text_nodes; - std::list > subtitle_nodes; - }; - - void maybe_add_subtitle (std::string text, ParseState const & parse_state); - - void examine_font_nodes ( - boost::shared_ptr xml, - std::list > const & font_nodes, - ParseState& parse_state - ); - - void examine_text_nodes ( - boost::shared_ptr xml, - std::list > const & text_nodes, - ParseState& parse_state - ); - - std::string _movie_title; - /* strangely, this is sometimes a string */ - std::string _reel_number; - std::string _language; - std::list > _load_font_nodes; - - std::list > _subtitles; - bool _need_sort; -}; - -} - -#endif diff --git a/src/subtitle_asset.cc b/src/subtitle_content.cc similarity index 63% rename from src/subtitle_asset.cc rename to src/subtitle_content.cc index 1c36fdfc..e32118c4 100644 --- a/src/subtitle_asset.cc +++ b/src/subtitle_content.cc @@ -1,5 +1,5 @@ /* - Copyright (C) 2012 Carl Hetherington + Copyright (C) 2012-2014 Carl Hetherington This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -17,14 +17,17 @@ */ -#include -#include -#include -#include -#include "subtitle_asset.h" -#include "parse/subtitle.h" +#include "subtitle_content.h" #include "util.h" #include "xml.h" +#include "font.h" +#include "text.h" +#include "load_font.h" +#include "subtitle_string.h" +#include +#include +#include +#include using std::string; using std::list; @@ -36,7 +39,7 @@ using boost::lexical_cast; using boost::optional; using namespace dcp; -SubtitleAsset::SubtitleAsset (boost::filesystem::path file) +SubtitleContent::SubtitleContent (boost::filesystem::path file) : Content (file) , _need_sort (false) { @@ -50,8 +53,8 @@ SubtitleAsset::SubtitleAsset (boost::filesystem::path file) xml->ignore_child ("LoadFont"); - list > font_nodes = type_children (xml, "Font"); - _load_font_nodes = type_children (xml, "LoadFont"); + list > font_nodes = type_children (xml, "Font"); + _load_font_nodes = type_children (xml, "LoadFont"); /* Now make Subtitle objects to represent the raw XML nodes in a sane way. @@ -61,8 +64,8 @@ SubtitleAsset::SubtitleAsset (boost::filesystem::path file) examine_font_nodes (xml, font_nodes, parse_state); } -SubtitleAsset::SubtitleAsset (string directory, string movie_title, string language) - : Content (directory) +SubtitleContent::SubtitleContent (Fraction edit_rate, string movie_title, string language) + : Content (edit_rate) , _movie_title (movie_title) , _reel_number ("1") , _language (language) @@ -72,18 +75,18 @@ SubtitleAsset::SubtitleAsset (string directory, string movie_title, string langu } void -SubtitleAsset::examine_font_nodes ( +SubtitleContent::examine_font_nodes ( shared_ptr xml, - list > const & font_nodes, + list > const & font_nodes, ParseState& parse_state ) { - for (list >::const_iterator i = font_nodes.begin(); i != font_nodes.end(); ++i) { + for (list >::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 >::iterator j = (*i)->subtitle_nodes.begin(); j != (*i)->subtitle_nodes.end(); ++j) { + for (list >::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); @@ -98,13 +101,13 @@ SubtitleAsset::examine_font_nodes ( } void -SubtitleAsset::examine_text_nodes ( +SubtitleContent::examine_text_nodes ( shared_ptr xml, - list > const & text_nodes, + list > const & text_nodes, ParseState& parse_state ) { - for (list >::const_iterator i = text_nodes.begin(); i != text_nodes.end(); ++i) { + for (list >::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); @@ -113,7 +116,7 @@ SubtitleAsset::examine_text_nodes ( } void -SubtitleAsset::maybe_add_subtitle (string text, ParseState const & parse_state) +SubtitleContent::maybe_add_subtitle (string text, ParseState const & parse_state) { if (empty_or_white_space (text)) { return; @@ -126,13 +129,13 @@ SubtitleAsset::maybe_add_subtitle (string text, ParseState const & parse_state) assert (!parse_state.text_nodes.empty ()); assert (!parse_state.subtitle_nodes.empty ()); - dcp::parse::Font effective_font (parse_state.font_nodes); - dcp::parse::Text effective_text (*parse_state.text_nodes.back ()); - dcp::parse::Subtitle effective_subtitle (*parse_state.subtitle_nodes.back ()); + dcp::Font effective_font (parse_state.font_nodes); + dcp::Text effective_text (*parse_state.text_nodes.back ()); + dcp::Subtitle effective_subtitle (*parse_state.subtitle_nodes.back ()); _subtitles.push_back ( - shared_ptr ( - new Subtitle ( + shared_ptr ( + new SubtitleString ( font_id_to_name (effective_font.id), effective_font.italic.get(), effective_font.color.get(), @@ -151,11 +154,11 @@ SubtitleAsset::maybe_add_subtitle (string text, ParseState const & parse_state) ); } -list > -SubtitleAsset::subtitles_at (Time t) const +list > +SubtitleContent::subtitles_at (Time t) const { - list > s; - for (list >::const_iterator i = _subtitles.begin(); i != _subtitles.end(); ++i) { + list > s; + for (list >::const_iterator i = _subtitles.begin(); i != _subtitles.end(); ++i) { if ((*i)->in() <= t && t <= (*i)->out ()) { s.push_back (*i); } @@ -165,9 +168,9 @@ SubtitleAsset::subtitles_at (Time t) const } std::string -SubtitleAsset::font_id_to_name (string id) const +SubtitleContent::font_id_to_name (string id) const { - list >::const_iterator i = _load_font_nodes.begin(); + list >::const_iterator i = _load_font_nodes.begin(); while (i != _load_font_nodes.end() && (*i)->id != id) { ++i; } @@ -183,109 +186,15 @@ SubtitleAsset::font_id_to_name (string id) const return ""; } -Subtitle::Subtitle ( - string font, - bool italic, - Color color, - int size, - Time in, - Time out, - float v_position, - VAlign v_align, - string text, - Effect effect, - Color effect_color, - Time fade_up_time, - Time fade_down_time - ) - : _font (font) - , _italic (italic) - , _color (color) - , _size (size) - , _in (in) - , _out (out) - , _v_position (v_position) - , _v_align (v_align) - , _text (text) - , _effect (effect) - , _effect_color (effect_color) - , _fade_up_time (fade_up_time) - , _fade_down_time (fade_down_time) -{ - -} - -int -Subtitle::size_in_pixels (int screen_height) const -{ - /* Size in the subtitle file is given in points as if the screen - height is 11 inches, so a 72pt font would be 1/11th of the screen - height. - */ - - return _size * screen_height / (11 * 72); -} - -bool -dcp::operator== (Subtitle const & a, Subtitle const & b) -{ - return ( - a.font() == b.font() && - a.italic() == b.italic() && - a.color() == b.color() && - a.size() == b.size() && - a.in() == b.in() && - a.out() == b.out() && - a.v_position() == b.v_position() && - a.v_align() == b.v_align() && - a.text() == b.text() && - a.effect() == b.effect() && - a.effect_color() == b.effect_color() && - a.fade_up_time() == b.fade_up_time() && - a.fade_down_time() == b.fade_down_time() - ); -} - -ostream& -dcp::operator<< (ostream& s, Subtitle const & sub) -{ - s << "\n`" << sub.text() << "' from " << sub.in() << " to " << sub.out() << ";\n" - << "fade up " << sub.fade_up_time() << ", fade down " << sub.fade_down_time() << ";\n" - << "font " << sub.font() << ", "; - - if (sub.italic()) { - s << "italic"; - } else { - s << "non-italic"; - } - - s << ", size " << sub.size() << ", color " << sub.color() << ", vpos " << sub.v_position() << ", valign " << ((int) sub.v_align()) << ";\n" - << "effect " << ((int) sub.effect()) << ", effect color " << sub.effect_color(); - - return s; -} - void -SubtitleAsset::add (shared_ptr s) +SubtitleContent::add (shared_ptr s) { _subtitles.push_back (s); _need_sort = true; } -void -SubtitleAsset::write_to_cpl (xmlpp::Element* node) const -{ - /* XXX: should EditRate, Duration and IntrinsicDuration be in here? */ - - xmlpp::Node* ms = node->add_child ("MainSubtitle"); - ms->add_child("Id")->add_child_text("urn:uuid:" + _id); - ms->add_child("AnnotationText")->add_child_text (_file.string ()); - /* XXX */ - ms->add_child("EntryPoint")->add_child_text ("0"); -} - struct SubtitleSorter { - bool operator() (shared_ptr a, shared_ptr b) { + bool operator() (shared_ptr a, shared_ptr b) { if (a->in() != b->in()) { return a->in() < b->in(); } @@ -294,7 +203,7 @@ struct SubtitleSorter { }; void -SubtitleAsset::write_xml () const +SubtitleContent::write_xml () const { FILE* f = fopen_boost (file (), "r"); Glib::ustring const s = xml_as_string (); @@ -303,7 +212,7 @@ SubtitleAsset::write_xml () const } Glib::ustring -SubtitleAsset::xml_as_string () const +SubtitleContent::xml_as_string () const { xmlpp::Document doc; xmlpp::Element* root = doc.create_root_node ("DCSubtitle"); @@ -324,7 +233,7 @@ SubtitleAsset::xml_as_string () const load_font->set_attribute("URI", _load_font_nodes.front()->uri); } - list > sorted = _subtitles; + list > sorted = _subtitles; if (_need_sort) { sorted.sort (SubtitleSorter ()); } @@ -346,7 +255,7 @@ SubtitleAsset::xml_as_string () const xmlpp::Element* font = 0; xmlpp::Element* subtitle = 0; - for (list >::iterator i = sorted.begin(); i != sorted.end(); ++i) { + for (list >::iterator i = sorted.begin(); i != sorted.end(); ++i) { /* We will start a new ... whenever some font property changes. I suppose we should really make an optimal hierarchy of tags, but diff --git a/src/subtitle_content.h b/src/subtitle_content.h new file mode 100644 index 00000000..b8db0c2c --- /dev/null +++ b/src/subtitle_content.h @@ -0,0 +1,104 @@ +/* + Copyright (C) 2012-2014 Carl Hetherington + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#ifndef LIBDCP_SUBTITLE_CONTENT_H +#define LIBDCP_SUBTITLE_CONTENT_H + +#include "content.h" +#include "dcp_time.h" +#include + +namespace dcp +{ + +class SubtitleString; +class Font; +class Text; +class Subtitle; +class LoadFont; + +/** @class SubtitleContent + * @brief A representation of an XML file containing subtitles. + */ +class SubtitleContent : public Content +{ +public: + SubtitleContent (boost::filesystem::path file); + SubtitleContent (Fraction edit_rate, std::string movie_title, std::string language); + + std::string pkl_type () const { + return "text/xml"; + } + + virtual bool equals (boost::shared_ptr, EqualityOptions, boost::function note) const { + /* XXX */ + note (ERROR, "subtitle content not compared yet"); + return true; + } + + std::string language () const { + return _language; + } + + std::list > subtitles_at (Time t) const; + std::list > const & subtitles () const { + return _subtitles; + } + + void add (boost::shared_ptr); + + void write_xml () const; + Glib::ustring xml_as_string () const; + +private: + std::string font_id_to_name (std::string id) const; + + struct ParseState { + std::list > font_nodes; + std::list > text_nodes; + std::list > subtitle_nodes; + }; + + void maybe_add_subtitle (std::string text, ParseState const & parse_state); + + void examine_font_nodes ( + boost::shared_ptr xml, + std::list > const & font_nodes, + ParseState& parse_state + ); + + void examine_text_nodes ( + boost::shared_ptr xml, + std::list > const & text_nodes, + ParseState& parse_state + ); + + std::string _movie_title; + /* strangely, this is sometimes a string */ + std::string _reel_number; + std::string _language; + std::list > _load_font_nodes; + + std::list > _subtitles; + bool _need_sort; +}; + +} + +#endif diff --git a/src/subtitle_string.cc b/src/subtitle_string.cc new file mode 100644 index 00000000..66869fd5 --- /dev/null +++ b/src/subtitle_string.cc @@ -0,0 +1,107 @@ +/* + Copyright (C) 2012-2014 Carl Hetherington + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include "subtitle_string.h" +#include "xml.h" + +using std::string; +using std::ostream; +using namespace dcp; + +SubtitleString::SubtitleString ( + string font, + bool italic, + Color color, + int size, + Time in, + Time out, + float v_position, + VAlign v_align, + string text, + Effect effect, + Color effect_color, + Time fade_up_time, + Time fade_down_time + ) + : _font (font) + , _italic (italic) + , _color (color) + , _size (size) + , _in (in) + , _out (out) + , _v_position (v_position) + , _v_align (v_align) + , _text (text) + , _effect (effect) + , _effect_color (effect_color) + , _fade_up_time (fade_up_time) + , _fade_down_time (fade_down_time) +{ + +} + +int +SubtitleString::size_in_pixels (int screen_height) const +{ + /* Size in the subtitle file is given in points as if the screen + height is 11 inches, so a 72pt font would be 1/11th of the screen + height. + */ + + return _size * screen_height / (11 * 72); +} + +bool +dcp::operator== (SubtitleString const & a, SubtitleString const & b) +{ + return ( + a.font() == b.font() && + a.italic() == b.italic() && + a.color() == b.color() && + a.size() == b.size() && + a.in() == b.in() && + a.out() == b.out() && + a.v_position() == b.v_position() && + a.v_align() == b.v_align() && + a.text() == b.text() && + a.effect() == b.effect() && + a.effect_color() == b.effect_color() && + a.fade_up_time() == b.fade_up_time() && + a.fade_down_time() == b.fade_down_time() + ); +} + +ostream& +dcp::operator<< (ostream& s, SubtitleString const & sub) +{ + s << "\n`" << sub.text() << "' from " << sub.in() << " to " << sub.out() << ";\n" + << "fade up " << sub.fade_up_time() << ", fade down " << sub.fade_down_time() << ";\n" + << "font " << sub.font() << ", "; + + if (sub.italic()) { + s << "italic"; + } else { + s << "non-italic"; + } + + s << ", size " << sub.size() << ", color " << sub.color() << ", vpos " << sub.v_position() << ", valign " << ((int) sub.v_align()) << ";\n" + << "effect " << ((int) sub.effect()) << ", effect color " << sub.effect_color(); + + return s; +} diff --git a/src/subtitle_string.h b/src/subtitle_string.h new file mode 100644 index 00000000..59131f07 --- /dev/null +++ b/src/subtitle_string.h @@ -0,0 +1,124 @@ +/* + Copyright (C) 2012-2014 Carl Hetherington + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include "types.h" +#include "dcp_time.h" +#include + +namespace dcp { + +class SubtitleString +{ +public: + SubtitleString ( + std::string font, + bool italic, + Color color, + int size, + Time in, + Time out, + float v_position, + VAlign v_align, + std::string text, + Effect effect, + Color effect_color, + Time fade_up_time, + Time fade_down_time + ); + + std::string font () const { + return _font; + } + + bool italic () const { + return _italic; + } + + Color color () const { + return _color; + } + + Time in () const { + return _in; + } + + Time out () const { + return _out; + } + + std::string text () const { + return _text; + } + + float v_position () const { + return _v_position; + } + + VAlign v_align () const { + return _v_align; + } + + Effect effect () const { + return _effect; + } + + Color effect_color () const { + return _effect_color; + } + + Time fade_up_time () const { + return _fade_up_time; + } + + Time fade_down_time () const { + return _fade_down_time; + } + + int size () const { + return _size; + } + + int size_in_pixels (int screen_height) const; + +private: + std::string _font; + bool _italic; + Color _color; + /** Size in points as if the screen height is 11 inches, so a 72pt font + * would be 1/11th of the screen height. + */ + int _size; + Time _in; + Time _out; + /** Vertical position as a proportion of the screen height from the top + * (between 0 and 1) + */ + float _v_position; + VAlign _v_align; + std::string _text; + Effect _effect; + Color _effect_color; + Time _fade_up_time; + Time _fade_down_time; +}; + +bool operator== (SubtitleString const & a, SubtitleString const & b); +std::ostream& operator<< (std::ostream& s, SubtitleString const & sub); + +} diff --git a/src/text.cc b/src/text.cc new file mode 100644 index 00000000..5d2a2d1b --- /dev/null +++ b/src/text.cc @@ -0,0 +1,41 @@ +/* + Copyright (C) 2012-2014 Carl Hetherington + + 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 "text.h" +#include "xml.h" +#include "font.h" +#include + +using std::string; +using boost::shared_ptr; +using boost::optional; +using namespace dcp; + +Text::Text (shared_ptr node) + : v_align (CENTER) +{ + text = node->content (); + v_position = node->number_attribute ("VPosition"); + optional v = node->optional_string_attribute ("VAlign"); + if (v) { + v_align = string_to_valign (v.get ()); + } + + font_nodes = type_children (node, "Font"); +} diff --git a/src/text.h b/src/text.h new file mode 100644 index 00000000..81992d6f --- /dev/null +++ b/src/text.h @@ -0,0 +1,48 @@ +/* + Copyright (C) 2012-2014 Carl Hetherington + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include "types.h" +#include +#include + +namespace cxml { + class Node; +} + +namespace dcp { + +class Font; + +class Text +{ +public: + Text () + : v_position (0) + , v_align (TOP) + {} + + Text (boost::shared_ptr node); + + float v_position; + VAlign v_align; + std::string text; + std::list > font_nodes; +}; + +} diff --git a/src/types.h b/src/types.h index 021d1ecf..85dc8362 100644 --- a/src/types.h +++ b/src/types.h @@ -105,14 +105,14 @@ struct EqualityOptions { : max_mean_pixel_error (0) , max_std_dev_pixel_error (0) , max_audio_sample_error (0) - , cpl_names_can_differ (false) + , cpl_annotation_texts_can_differ (false) , mxf_names_can_differ (false) {} double max_mean_pixel_error; double max_std_dev_pixel_error; int max_audio_sample_error; - bool cpl_names_can_differ; + bool cpl_annotation_texts_can_differ; bool mxf_names_can_differ; }; @@ -125,6 +125,11 @@ enum NoteType { NOTE }; +enum Standard { + INTEROP, + SMPTE +}; + /** @class Color * @brief An RGB color (aka colour). */ diff --git a/src/util.cc b/src/util.cc index 3807b4cc..fe89ca72 100644 --- a/src/util.cc +++ b/src/util.cc @@ -21,22 +21,6 @@ * @brief Utility methods. */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "KM_util.h" -#include "KM_fileio.h" -#include "AS_DCP.h" #include "util.h" #include "exceptions.h" #include "types.h" @@ -44,6 +28,22 @@ #include "certificates.h" #include "gamma_lut.h" #include "xyz_frame.h" +#include "KM_util.h" +#include "KM_fileio.h" +#include "AS_DCP.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include using std::string; using std::wstring; @@ -79,10 +79,10 @@ dcp::make_uuid () * @return Digest. */ string -dcp::make_digest (string filename, boost::function* progress) +dcp::make_digest (boost::filesystem::path filename, boost::function* progress) { Kumu::FileReader reader; - Kumu::Result_t r = reader.OpenRead (filename.c_str ()); + Kumu::Result_t r = reader.OpenRead (filename.string().c_str ()); if (ASDCP_FAILURE (r)) { boost::throw_exception (FileError ("could not open file to compute digest", filename, r)); } diff --git a/src/util.h b/src/util.h index f12f0ff0..5d7a9c4f 100644 --- a/src/util.h +++ b/src/util.h @@ -67,7 +67,7 @@ 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, boost::function *); +extern std::string make_digest (boost::filesystem::path filename, boost::function *); extern std::string content_kind_to_string (ContentKind kind); extern ContentKind content_kind_from_string (std::string kind); extern bool empty_or_white_space (std::string s); @@ -75,7 +75,7 @@ extern boost::shared_ptr decompress_j2k (uint8_t* data, int64_t size, extern void init (); -extern void sign (xmlpp::Element* parent, CertificateChain const & certificates, boost::filesystem::path signer_key, bool interop); +extern void sign (xmlpp::Element* parent, CertificateChain const & certificates, boost::filesystem::path signer_key, Standard standard); extern void add_signature_value (xmlpp::Element* parent, CertificateChain const & certificates, boost::filesystem::path signer_key, std::string const & ns); extern void add_signer (xmlpp::Element* parent, CertificateChain const & certificates, std::string const & ns); diff --git a/src/wscript b/src/wscript index 3e92900a..60d442fe 100644 --- a/src/wscript +++ b/src/wscript @@ -21,38 +21,48 @@ def build(bld): dcp.cc dcp_time.cc exceptions.cc + file.cc + font.cc gamma_lut.cc image.cc kdm.cc key.cc + load_font.cc metadata.cc mono_picture_mxf.cc mono_picture_mxf_writer.cc mono_picture_frame.cc mxf.cc + mxf_writer.cc object.cc picture_mxf.cc picture_mxf_writer.cc rec709_linearised_gamma_lut.cc reel.cc + reel_asset.cc + reel_mono_picture_asset.cc + reel_picture_asset.cc + reel_sound_asset.cc + reel_stereo_picture_asset.cc + reel_subtitle_asset.cc rgb_xyz.cc signer.cc signer_chain.cc sound_mxf.cc + sound_mxf_writer.cc sound_frame.cc srgb_linearised_gamma_lut.cc stereo_picture_mxf.cc stereo_picture_mxf_writer.cc stereo_picture_frame.cc - subtitle_asset.cc + subtitle.cc + subtitle_content.cc + subtitle_string.cc + text.cc types.cc util.cc version.cc xyz_frame.cc - parse/asset_map.cc - parse/cpl.cc - parse/pkl.cc - parse/subtitle.cc """ headers = """ diff --git a/test/cpl_sar.cc b/test/cpl_sar_test.cc similarity index 82% rename from test/cpl_sar.cc rename to test/cpl_sar_test.cc index 3ab1ec26..0c62ea8b 100644 --- a/test/cpl_sar.cc +++ b/test/cpl_sar_test.cc @@ -17,10 +17,11 @@ */ -#include -#include #include "cpl.h" +#include "reel_mono_picture_asset.h" #include "mono_picture_mxf.h" +#include +#include using boost::shared_ptr; @@ -29,24 +30,23 @@ using boost::shared_ptr; */ BOOST_AUTO_TEST_CASE (cpl_sar) { - shared_ptr mp (new dcp::MonoPictureMXF ("build/test/foo/video.mxf")); - mp->set_interop (true); + shared_ptr pa (new dcp::ReelMonoPictureAsset ()); { - mp->set_size (dcp::Size (1998, 1080)); + pa->set_screen_aspect_ratio (dcp::Fraction (1998, 1080)); xmlpp::Document doc; xmlpp::Element* el = doc.create_root_node ("Test"); - mp->write_to_cpl (el); + pa->write_to_cpl (el, dcp::INTEROP); cxml::Node node (el); BOOST_CHECK_EQUAL (node.node_child("MainPicture")->string_child ("ScreenAspectRatio"), "1.85"); } { - mp->set_size (dcp::Size (2048, 858)); + pa->set_screen_aspect_ratio (dcp::Fraction (2048, 858)); xmlpp::Document doc; xmlpp::Element* el = doc.create_root_node ("Test"); - mp->write_to_cpl (el); + pa->write_to_cpl (el, dcp::INTEROP); cxml::Node node (el); BOOST_CHECK_EQUAL (node.node_child("MainPicture")->string_child ("ScreenAspectRatio"), "2.39"); diff --git a/test/decryption_test.cc b/test/decryption_test.cc index b1cb5265..030f0b58 100644 --- a/test/decryption_test.cc +++ b/test/decryption_test.cc @@ -24,6 +24,7 @@ #include "cpl.h" #include "argb_frame.h" #include "mono_picture_mxf.h" +#include "reel_picture_asset.h" #include "reel.h" #include "test.h" @@ -34,7 +35,7 @@ static shared_ptr get_frame (dcp::DCP const & dcp) { shared_ptr reel = dcp.cpls().front()->reels().front (); - shared_ptr picture = reel->main_picture (); + shared_ptr picture = reel->main_picture()->mxf (); BOOST_CHECK (picture); shared_ptr mono_picture = dynamic_pointer_cast (picture); @@ -62,7 +63,7 @@ BOOST_AUTO_TEST_CASE (decryption_test) "test/data/private.key" ); - encrypted.add_kdm (kdm); + encrypted.add (kdm); shared_ptr plaintext_frame = get_frame (plaintext); shared_ptr encrypted_frame = get_frame (encrypted); diff --git a/test/read_dcp_test.cc b/test/read_dcp_test.cc index 06df7058..a17b0a01 100644 --- a/test/read_dcp_test.cc +++ b/test/read_dcp_test.cc @@ -33,8 +33,6 @@ BOOST_AUTO_TEST_CASE (read_dcp) list > cpls = d.cpls (); BOOST_CHECK_EQUAL (cpls.size(), 1); - BOOST_CHECK_EQUAL (cpls.front()->name(), "A Test DCP"); + BOOST_CHECK_EQUAL (cpls.front()->annotation_text(), "A Test DCP"); BOOST_CHECK_EQUAL (cpls.front()->content_kind(), dcp::FEATURE); - BOOST_CHECK_EQUAL (cpls.front()->frames_per_second(), 24); - BOOST_CHECK_EQUAL (cpls.front()->length(), 24); } diff --git a/test/recovery_test.cc b/test/recovery_test.cc index b67c54f7..1d51a711 100644 --- a/test/recovery_test.cc +++ b/test/recovery_test.cc @@ -48,9 +48,8 @@ BOOST_AUTO_TEST_CASE (recovery) boost::filesystem::remove_all ("build/test/baz"); boost::filesystem::create_directories ("build/test/baz"); - shared_ptr mp (new dcp::MonoPictureMXF (24)); - mp->set_size (dcp::Size (32, 32)); - shared_ptr writer = mp->start_write ("build/test/baz/video1.mxf", false); + shared_ptr mp (new dcp::MonoPictureMXF (dcp::Fraction (24, 1))); + shared_ptr writer = mp->start_write ("build/test/baz/video1.mxf", dcp::SMPTE, false); int written_size = 0; for (int i = 0; i < 24; ++i) { @@ -77,9 +76,8 @@ BOOST_AUTO_TEST_CASE (recovery) Kumu::ResetTestRNG (); #endif - mp.reset (new dcp::MonoPictureMXF (24)); - mp->set_size (dcp::Size (32, 32)); - writer = mp->start_write ("build/test/baz/video2.mxf", true); + mp.reset (new dcp::MonoPictureMXF (dcp::Fraction (24, 1))); + writer = mp->start_write ("build/test/baz/video2.mxf", dcp::SMPTE, true); writer->write (data, size); diff --git a/test/rewrite_subs.cc b/test/rewrite_subs.cc index 5628ec70..fdb41a2c 100644 --- a/test/rewrite_subs.cc +++ b/test/rewrite_subs.cc @@ -1,8 +1,28 @@ +/* + Copyright (C) 2012-2014 Carl Hetherington + + 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 #include "dcp.h" #include "cpl.h" #include "reel.h" -#include "subtitle_asset.h" +#include "subtitle_content.h" +#include "reel_subtitle_asset.h" #include "exceptions.h" using std::cout; @@ -21,7 +41,7 @@ try } DCP* dcp = new DCP (argv[1]); - dcp->read (false); + dcp->read (); list > cpls = dcp->cpls (); for (list >::iterator i = cpls.begin(); i != cpls.end(); ++i) { @@ -30,7 +50,7 @@ try for (list >::iterator j = reels.begin(); j != reels.end(); ++j) { if ((*j)->main_subtitle()) { - (*j)->main_subtitle()->write_xml (); + (*j)->main_subtitle()->subtitle_content()->write_xml (); } } } diff --git a/test/round_trip_test.cc b/test/round_trip_test.cc index 69bf63f9..8b1efbd7 100644 --- a/test/round_trip_test.cc +++ b/test/round_trip_test.cc @@ -30,6 +30,9 @@ #include "mono_picture_frame.h" #include "argb_frame.h" #include "signer_chain.h" +#include "mono_picture_mxf_writer.h" +#include "reel_picture_asset.h" +#include "file.h" using std::list; using boost::shared_ptr; @@ -56,12 +59,9 @@ BOOST_AUTO_TEST_CASE (round_trip_test) boost::filesystem::path work_dir = "build/test/round_trip_test"; boost::filesystem::create_directory (work_dir); - shared_ptr asset_A (new dcp::MonoPictureMXF (work_dir, "video.mxf")); - asset_A->set_edit_rate (24); - shared_ptr writer; - boost::filesystem::path mxf = work_dir + "video.mxf"; - writer->start_write (mxf, false); - TestFile j2c ("test/data/32x32_red_square.j2c"); + shared_ptr mxf_A (new dcp::MonoPictureMXF (dcp::Fraction (24, 1))); + shared_ptr writer = mxf_A->start_write (work_dir / "video.mxf", dcp::SMPTE, false); + dcp::File j2c ("test/data/32x32_red_square.j2c"); for (int i = 0; i < 24; ++i) { writer->write (j2c.data (), j2c.size ()); } @@ -69,10 +69,12 @@ BOOST_AUTO_TEST_CASE (round_trip_test) dcp::Key key; - asset_A->set_key (key); + mxf_A->set_key (key); - shared_ptr cpl (new dcp::CPL (work_dir, "A Test DCP", dcp::FEATURE, 24, 24)); - cpl->add_reel (shared_ptr (new dcp::Reel (asset_A, shared_ptr (), shared_ptr ()))); + shared_ptr cpl (new dcp::CPL ("A Test DCP", dcp::FEATURE)); + shared_ptr reel (new dcp::Reel ()); + reel->add (shared_ptr (mxf_A, 0)); + cpl->add (reel); /* A KDM using our certificate chain's leaf key pair */ dcp::KDM kdm_A ( @@ -105,14 +107,14 @@ BOOST_AUTO_TEST_CASE (round_trip_test) } /* Reload the picture MXF */ - shared_ptr asset_B ( - new dcp::MonoPictureMXF (work_dir, "video.mxf") + shared_ptr mxf_B ( + new dcp::MonoPictureMXF (work_dir / "video.mxf") ); - asset_B->set_key (kdm_B.keys().front().key()); + mxf_B->set_key (kdm_B.keys().front().key()); - shared_ptr frame_A = asset_A->get_frame(0)->argb_frame (); - shared_ptr frame_B = asset_B->get_frame(0)->argb_frame (); + shared_ptr frame_A = mxf_A->get_frame(0)->argb_frame (); + shared_ptr frame_B = mxf_B->get_frame(0)->argb_frame (); BOOST_CHECK_EQUAL (frame_A->size().width, frame_B->size().width); BOOST_CHECK_EQUAL (frame_A->size().height, frame_B->size().height); BOOST_CHECK_EQUAL (memcmp (frame_A->data(), frame_B->data(), frame_A->size().width * frame_A->size().height), 0); diff --git a/test/subs_in_out.cc b/test/subs_in_out.cc index 40e5476c..9b2c2e9b 100644 --- a/test/subs_in_out.cc +++ b/test/subs_in_out.cc @@ -1,5 +1,5 @@ #include -#include "subtitle_asset.h" +#include "subtitle_content.h" using namespace std; @@ -10,7 +10,7 @@ int main (int argc, char* argv[]) exit (EXIT_FAILURE); } - dcp::SubtitleAsset s (argv[1]); + dcp::SubtitleContent s (argv[1]); cout << s.xml_as_string (); return 0; } diff --git a/test/subtitle_tests.cc b/test/subtitle_tests.cc index b2b2d363..77dd29d7 100644 --- a/test/subtitle_tests.cc +++ b/test/subtitle_tests.cc @@ -1,5 +1,5 @@ /* - Copyright (C) 2012-2013 Carl Hetherington + Copyright (C) 2012-2014 Carl Hetherington This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -17,22 +17,23 @@ */ +#include "subtitle_content.h" +#include "subtitle_string.h" #include -#include "subtitle_asset.h" using std::list; using boost::shared_ptr; -/* Load a subtitle asset from XML and check that it is read correctly */ +/* Load some subtitle content from XML and check that it is read correctly */ BOOST_AUTO_TEST_CASE (subtitles1) { - dcp::SubtitleAsset subs ("test/data/subs1.xml"); + dcp::SubtitleContent subs ("test/data/subs1.xml"); BOOST_CHECK_EQUAL (subs.language(), "French"); - list > s = subs.subtitles_at (dcp::Time (0, 0, 6, 1)); + list > s = subs.subtitles_at (dcp::Time (0, 0, 6, 1)); BOOST_CHECK_EQUAL (s.size(), 1); - BOOST_CHECK_EQUAL (*(s.front().get()), dcp::Subtitle ( + BOOST_CHECK_EQUAL (*(s.front().get()), dcp::SubtitleString ( "Arial", false, dcp::Color (255, 255, 255), @@ -50,7 +51,7 @@ BOOST_AUTO_TEST_CASE (subtitles1) s = subs.subtitles_at (dcp::Time (0, 0, 7, 190)); BOOST_CHECK_EQUAL (s.size(), 2); - BOOST_CHECK_EQUAL (*(s.front().get()), dcp::Subtitle ( + BOOST_CHECK_EQUAL (*(s.front().get()), dcp::SubtitleString ( "Arial", true, dcp::Color (255, 255, 255), @@ -65,7 +66,7 @@ BOOST_AUTO_TEST_CASE (subtitles1) dcp::Time (0, 0, 0, 1), dcp::Time (0, 0, 0, 1) )); - BOOST_CHECK_EQUAL (*(s.back().get()), dcp::Subtitle ( + BOOST_CHECK_EQUAL (*(s.back().get()), dcp::SubtitleString ( "Arial", false, dcp::Color (255, 255, 255), @@ -83,7 +84,7 @@ BOOST_AUTO_TEST_CASE (subtitles1) s = subs.subtitles_at (dcp::Time (0, 0, 11, 95)); BOOST_CHECK_EQUAL (s.size(), 1); - BOOST_CHECK_EQUAL (*(s.back().get()), dcp::Subtitle ( + BOOST_CHECK_EQUAL (*(s.back().get()), dcp::SubtitleString ( "Arial", false, dcp::Color (255, 255, 255), @@ -101,7 +102,7 @@ BOOST_AUTO_TEST_CASE (subtitles1) s = subs.subtitles_at (dcp::Time (0, 0, 14, 42)); BOOST_CHECK_EQUAL (s.size(), 1); - BOOST_CHECK_EQUAL (*(s.back().get()), dcp::Subtitle ( + BOOST_CHECK_EQUAL (*(s.back().get()), dcp::SubtitleString ( "Arial", false, dcp::Color (255, 255, 255), @@ -121,11 +122,11 @@ BOOST_AUTO_TEST_CASE (subtitles1) /** And similarly for another one */ BOOST_AUTO_TEST_CASE (subtitles2) { - dcp::SubtitleAsset subs ("test/data/subs2.xml"); + dcp::SubtitleContent subs ("test/data/subs2.xml"); - list > s = subs.subtitles_at (dcp::Time (0, 0, 42, 100)); + list > s = subs.subtitles_at (dcp::Time (0, 0, 42, 100)); BOOST_CHECK_EQUAL (s.size(), 2); - BOOST_CHECK_EQUAL (*(s.front().get()), dcp::Subtitle ( + BOOST_CHECK_EQUAL (*(s.front().get()), dcp::SubtitleString ( "Arial", true, dcp::Color (255, 255, 255), @@ -140,7 +141,7 @@ BOOST_AUTO_TEST_CASE (subtitles2) dcp::Time (0, 0, 0, 0), dcp::Time (0, 0, 0, 0) )); - BOOST_CHECK_EQUAL (*(s.back().get()), dcp::Subtitle ( + BOOST_CHECK_EQUAL (*(s.back().get()), dcp::SubtitleString ( "Arial", true, dcp::Color (255, 255, 255), @@ -158,7 +159,7 @@ BOOST_AUTO_TEST_CASE (subtitles2) s = subs.subtitles_at (dcp::Time (0, 0, 50, 50)); BOOST_CHECK_EQUAL (s.size(), 2); - BOOST_CHECK_EQUAL (*(s.front().get()), dcp::Subtitle ( + BOOST_CHECK_EQUAL (*(s.front().get()), dcp::SubtitleString ( "Arial", true, dcp::Color (255, 255, 255), @@ -173,7 +174,7 @@ BOOST_AUTO_TEST_CASE (subtitles2) dcp::Time (0, 0, 0, 0), dcp::Time (0, 0, 0, 0) )); - BOOST_CHECK_EQUAL (*(s.back().get()), dcp::Subtitle ( + BOOST_CHECK_EQUAL (*(s.back().get()), dcp::SubtitleString ( "Arial", true, dcp::Color (255, 255, 255), @@ -191,7 +192,7 @@ BOOST_AUTO_TEST_CASE (subtitles2) s = subs.subtitles_at (dcp::Time (0, 1, 2, 300)); BOOST_CHECK_EQUAL (s.size(), 2); - BOOST_CHECK_EQUAL (*(s.front().get()), dcp::Subtitle ( + BOOST_CHECK_EQUAL (*(s.front().get()), dcp::SubtitleString ( "Arial", true, dcp::Color (255, 255, 255), @@ -206,7 +207,7 @@ BOOST_AUTO_TEST_CASE (subtitles2) dcp::Time (0, 0, 0, 0), dcp::Time (0, 0, 0, 0) )); - BOOST_CHECK_EQUAL (*(s.back().get()), dcp::Subtitle ( + BOOST_CHECK_EQUAL (*(s.back().get()), dcp::SubtitleString ( "Arial", true, dcp::Color (255, 255, 255), @@ -224,7 +225,7 @@ BOOST_AUTO_TEST_CASE (subtitles2) s = subs.subtitles_at (dcp::Time (0, 1, 15, 50)); BOOST_CHECK_EQUAL (s.size(), 2); - BOOST_CHECK_EQUAL (*(s.front().get()), dcp::Subtitle ( + BOOST_CHECK_EQUAL (*(s.front().get()), dcp::SubtitleString ( "Arial", true, dcp::Color (255, 255, 255), @@ -239,7 +240,7 @@ BOOST_AUTO_TEST_CASE (subtitles2) dcp::Time (0, 0, 0, 0), dcp::Time (0, 0, 0, 0) )); - BOOST_CHECK_EQUAL (*(s.back().get()), dcp::Subtitle ( + BOOST_CHECK_EQUAL (*(s.back().get()), dcp::SubtitleString ( "Arial", true, dcp::Color (255, 255, 255), @@ -257,7 +258,7 @@ BOOST_AUTO_TEST_CASE (subtitles2) s = subs.subtitles_at (dcp::Time (0, 1, 27, 200)); BOOST_CHECK_EQUAL (s.size(), 2); - BOOST_CHECK_EQUAL (*(s.front().get()), dcp::Subtitle ( + BOOST_CHECK_EQUAL (*(s.front().get()), dcp::SubtitleString ( "Arial", true, dcp::Color (255, 255, 255), @@ -272,7 +273,7 @@ BOOST_AUTO_TEST_CASE (subtitles2) dcp::Time (0, 0, 0, 0), dcp::Time (0, 0, 0, 0) )); - BOOST_CHECK_EQUAL (*(s.back().get()), dcp::Subtitle ( + BOOST_CHECK_EQUAL (*(s.back().get()), dcp::SubtitleString ( "Arial", true, dcp::Color (255, 255, 255), @@ -290,7 +291,7 @@ BOOST_AUTO_TEST_CASE (subtitles2) s = subs.subtitles_at (dcp::Time (0, 1, 42, 300)); BOOST_CHECK_EQUAL (s.size(), 2); - BOOST_CHECK_EQUAL (*(s.front().get()), dcp::Subtitle ( + BOOST_CHECK_EQUAL (*(s.front().get()), dcp::SubtitleString ( "Arial", false, dcp::Color (255, 255, 255), @@ -305,7 +306,7 @@ BOOST_AUTO_TEST_CASE (subtitles2) dcp::Time (0, 0, 0, 0), dcp::Time (0, 0, 0, 0) )); - BOOST_CHECK_EQUAL (*(s.back().get()), dcp::Subtitle ( + BOOST_CHECK_EQUAL (*(s.back().get()), dcp::SubtitleString ( "Arial", false, dcp::Color (255, 255, 255), @@ -323,7 +324,7 @@ BOOST_AUTO_TEST_CASE (subtitles2) s = subs.subtitles_at (dcp::Time (0, 1, 45, 200)); BOOST_CHECK_EQUAL (s.size(), 2); - BOOST_CHECK_EQUAL (*(s.front().get()), dcp::Subtitle ( + BOOST_CHECK_EQUAL (*(s.front().get()), dcp::SubtitleString ( "Arial", false, dcp::Color (255, 255, 255), @@ -338,7 +339,7 @@ BOOST_AUTO_TEST_CASE (subtitles2) dcp::Time (0, 0, 0, 0), dcp::Time (0, 0, 0, 0) )); - BOOST_CHECK_EQUAL (*(s.back().get()), dcp::Subtitle ( + BOOST_CHECK_EQUAL (*(s.back().get()), dcp::SubtitleString ( "Arial", false, dcp::Color (255, 255, 255), @@ -356,7 +357,7 @@ BOOST_AUTO_TEST_CASE (subtitles2) s = subs.subtitles_at (dcp::Time (0, 1, 47, 249)); BOOST_CHECK_EQUAL (s.size(), 2); - BOOST_CHECK_EQUAL (*(s.front().get()), dcp::Subtitle ( + BOOST_CHECK_EQUAL (*(s.front().get()), dcp::SubtitleString ( "Arial", false, dcp::Color (255, 255, 255), @@ -371,7 +372,7 @@ BOOST_AUTO_TEST_CASE (subtitles2) dcp::Time (0, 0, 0, 0), dcp::Time (0, 0, 0, 0) )); - BOOST_CHECK_EQUAL (*(s.back().get()), dcp::Subtitle ( + BOOST_CHECK_EQUAL (*(s.back().get()), dcp::SubtitleString ( "Arial", false, dcp::Color (255, 255, 255), @@ -389,7 +390,7 @@ BOOST_AUTO_TEST_CASE (subtitles2) s = subs.subtitles_at (dcp::Time (0, 2, 6, 210)); BOOST_CHECK_EQUAL (s.size(), 2); - BOOST_CHECK_EQUAL (*(s.front().get()), dcp::Subtitle ( + BOOST_CHECK_EQUAL (*(s.front().get()), dcp::SubtitleString ( "Arial", true, dcp::Color (255, 255, 255), @@ -404,7 +405,7 @@ BOOST_AUTO_TEST_CASE (subtitles2) dcp::Time (0, 0, 0, 0), dcp::Time (0, 0, 0, 0) )); - BOOST_CHECK_EQUAL (*(s.back().get()), dcp::Subtitle ( + BOOST_CHECK_EQUAL (*(s.back().get()), dcp::SubtitleString ( "Arial", true, dcp::Color (255, 255, 255), diff --git a/test/test.cc b/test/test.cc index ff018cd5..f30a9db8 100644 --- a/test/test.cc +++ b/test/test.cc @@ -49,17 +49,3 @@ wav (dcp::Channel) string test_corpus = "../libdcp-test"; -TestFile::TestFile (boost::filesystem::path file) -{ - _size = boost::filesystem::file_size (file); - _data = new uint8_t[_size]; - FILE* f = dcp::fopen_boost (file, "r"); - assert (f); - fread (_data, 1, _size, f); - fclose (f); -} - -TestFile::~TestFile () -{ - delete[] _data; -} diff --git a/test/test.h b/test/test.h index 31b4bf0a..bc6baccd 100644 --- a/test/test.h +++ b/test/test.h @@ -18,22 +18,3 @@ */ extern std::string test_corpus; - -class TestFile -{ -public: - TestFile (boost::filesystem::path file); - ~TestFile (); - - uint8_t* data () const { - return _data; - } - - int64_t size () const { - return _size; - } - -private: - uint8_t* _data; - int64_t _size; -}; diff --git a/test/wscript b/test/wscript index b2b1b038..0cc0e202 100644 --- a/test/wscript +++ b/test/wscript @@ -25,7 +25,7 @@ def build(bld): obj.source = """ certificates_test.cc color_test.cc - cpl_sar.cc + cpl_sar_test.cc dcp_time_test.cc decryption_test.cc frame_info_test.cc diff --git a/tools/dcpinfo.cc b/tools/dcpinfo.cc index 70c94d8e..b76993bf 100644 --- a/tools/dcpinfo.cc +++ b/tools/dcpinfo.cc @@ -1,3 +1,22 @@ +/* + Copyright (C) 2012-2014 Carl Hetherington + + 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 #include #include @@ -7,7 +26,11 @@ #include "reel.h" #include "sound_mxf.h" #include "picture_mxf.h" -#include "subtitle_asset.h" +#include "subtitle_content.h" +#include "reel_picture_asset.h" +#include "reel_sound_asset.h" +#include "reel_subtitle_asset.h" +#include "subtitle_string.h" #include "cpl.h" using std::string; @@ -70,7 +93,7 @@ main (int argc, char* argv[]) DCP* dcp = 0; try { dcp = new DCP (argv[optind]); - dcp->read (false); + dcp->read (); } catch (FileError& e) { cerr << "Could not read DCP " << argv[optind] << "; " << e.what() << " " << e.filename() << "\n"; exit (EXIT_FAILURE); @@ -81,9 +104,7 @@ main (int argc, char* argv[]) list > cpls = dcp->cpls (); for (list >::iterator i = cpls.begin(); i != cpls.end(); ++i) { - cout << " CPL: " << (*i)->name() << "\n" - << " Length: " << (*i)->length() << "\n" - << " Frames per second: " << (*i)->frames_per_second() << "\n"; + cout << " CPL: " << (*i)->annotation_text() << "\n"; list > reels = (*i)->reels (); @@ -92,16 +113,22 @@ main (int argc, char* argv[]) cout << " Reel " << R << "\n"; if ((*j)->main_picture()) { - cout << " Picture: " << (*j)->main_picture()->size().width << "x" << (*j)->main_picture()->size().height << "\n"; + cout << " Picture: " + << (*j)->main_picture()->mxf()->size().width + << "x" + << (*j)->main_picture()->mxf()->size().height << "\n"; } if ((*j)->main_sound()) { - cout << " Sound: " << (*j)->main_sound()->channels() << " channels at " << (*j)->main_sound()->sampling_rate() << "Hz\n"; + cout << " Sound: " + << (*j)->main_sound()->mxf()->channels() + << " channels at " + << (*j)->main_sound()->mxf()->sampling_rate() << "Hz\n"; } if ((*j)->main_subtitle()) { - list > subs = (*j)->main_subtitle()->subtitles (); - cout << " Subtitle: " << subs.size() << " subtitles in " << (*j)->main_subtitle()->language() << "\n"; + list > subs = (*j)->main_subtitle()->subtitle_content()->subtitles (); + cout << " Subtitle: " << subs.size() << " subtitles in " << (*j)->main_subtitle()->subtitle_content()->language() << "\n"; if (subtitles) { - for (list >::const_iterator k = subs.begin(); k != subs.end(); ++k) { + for (list >::const_iterator k = subs.begin(); k != subs.end(); ++k) { cout << " " << (*k)->text() << "\n"; cout << " " << "font:" << (*k)->font() << "; " -- 2.30.2