summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorCarl Hetherington <cth@carlh.net>2016-08-14 23:59:16 +0100
committerCarl Hetherington <cth@carlh.net>2016-08-14 23:59:16 +0100
commit9486066c29b91a8d9ac25be1c596cad62387208f (patch)
tree181a41967df7a2ccd41ab8ed14d6c99171850bdc /src
parent563dbf02e9c7ab0066bcebbcde6c662df845695a (diff)
Support reading of encrypted subtitles.
Diffstat (limited to 'src')
-rw-r--r--src/asset_reader.cc13
-rw-r--r--src/asset_reader.h10
-rw-r--r--src/mono_picture_frame.cc5
-rw-r--r--src/mono_picture_frame.h3
-rw-r--r--src/mxf.h2
-rw-r--r--src/reel.cc9
-rw-r--r--src/reel_subtitle_asset.cc26
-rw-r--r--src/reel_subtitle_asset.h8
-rw-r--r--src/smpte_subtitle_asset.cc117
-rw-r--r--src/smpte_subtitle_asset.h4
-rw-r--r--src/sound_frame.cc7
-rw-r--r--src/sound_frame.h4
-rw-r--r--src/stereo_picture_frame.cc5
-rw-r--r--src/stereo_picture_frame.h3
-rw-r--r--src/wscript1
15 files changed, 151 insertions, 66 deletions
diff --git a/src/asset_reader.cc b/src/asset_reader.cc
index 9ada75f0..73c5d010 100644
--- a/src/asset_reader.cc
+++ b/src/asset_reader.cc
@@ -34,22 +34,13 @@
#include "asset_reader.h"
#include "mxf.h"
#include "exceptions.h"
+#include "decryption_context.h"
#include <asdcp/AS_DCP.h>
using namespace dcp;
AssetReader::AssetReader (MXF const * mxf)
- : _decryption_context (0)
+ : _decryption_context (new DecryptionContext (mxf->key ()))
{
- if (mxf->key()) {
- _decryption_context = new ASDCP::AESDecContext;
- if (ASDCP_FAILURE (_decryption_context->InitKey (mxf->key()->value ()))) {
- throw MiscError ("could not set up decryption context");
- }
- }
-}
-AssetReader::~AssetReader ()
-{
- delete _decryption_context;
}
diff --git a/src/asset_reader.h b/src/asset_reader.h
index 716e854d..09169a8e 100644
--- a/src/asset_reader.h
+++ b/src/asset_reader.h
@@ -35,23 +35,21 @@
#define LIBDCP_ASSET_READER_H
#include <boost/noncopyable.hpp>
-
-namespace ASDCP {
- class AESDecContext;
-}
+#include <boost/shared_ptr.hpp>
namespace dcp {
class MXF;
+class DecryptionContext;
class AssetReader : public boost::noncopyable
{
public:
explicit AssetReader (MXF const * mxf);
- virtual ~AssetReader ();
+ virtual ~AssetReader () {}
protected:
- ASDCP::AESDecContext* _decryption_context;
+ boost::shared_ptr<DecryptionContext> _decryption_context;
};
}
diff --git a/src/mono_picture_frame.cc b/src/mono_picture_frame.cc
index fd095a1f..79527280 100644
--- a/src/mono_picture_frame.cc
+++ b/src/mono_picture_frame.cc
@@ -42,6 +42,7 @@
#include "colour_conversion.h"
#include "compose.hpp"
#include "j2k.h"
+#include "decryption_context.h"
#include <asdcp/KM_fileio.h>
#include <asdcp/AS_DCP.h>
@@ -73,12 +74,12 @@ MonoPictureFrame::MonoPictureFrame (boost::filesystem::path path)
* @param n Frame within the asset, not taking EntryPoint into account.
* @param c Context for decryption, or 0.
*/
-MonoPictureFrame::MonoPictureFrame (ASDCP::JP2K::MXFReader* reader, int n, ASDCP::AESDecContext* c)
+MonoPictureFrame::MonoPictureFrame (ASDCP::JP2K::MXFReader* reader, int n, shared_ptr<DecryptionContext> c)
{
/* XXX: unfortunate guesswork on this buffer size */
_buffer = new ASDCP::JP2K::FrameBuffer (4 * Kumu::Megabyte);
- if (ASDCP_FAILURE (reader->ReadFrame (n, *_buffer, c))) {
+ if (ASDCP_FAILURE (reader->ReadFrame (n, *_buffer, c->decryption()))) {
boost::throw_exception (DCPReadError (String::compose ("could not read video frame %1", n)));
}
}
diff --git a/src/mono_picture_frame.h b/src/mono_picture_frame.h
index ea15f6a3..cb4afd6f 100644
--- a/src/mono_picture_frame.h
+++ b/src/mono_picture_frame.h
@@ -54,6 +54,7 @@ namespace ASDCP {
namespace dcp {
class OpenJPEGImage;
+class DecryptionContext;
/** @class MonoPictureFrame
* @brief A single frame of a 2D (monoscopic) picture asset.
@@ -74,7 +75,7 @@ public:
private:
friend class MonoPictureAssetReader;
- MonoPictureFrame (ASDCP::JP2K::MXFReader* reader, int n, ASDCP::AESDecContext *);
+ MonoPictureFrame (ASDCP::JP2K::MXFReader* reader, int n, boost::shared_ptr<DecryptionContext>);
ASDCP::JP2K::FrameBuffer* _buffer;
};
diff --git a/src/mxf.h b/src/mxf.h
index 89e4f635..73ada433 100644
--- a/src/mxf.h
+++ b/src/mxf.h
@@ -80,7 +80,7 @@ public:
return _key_id;
}
- void set_key (Key);
+ virtual void set_key (Key);
/** @return encryption/decryption key, if one has been set */
boost::optional<Key> key () const {
diff --git a/src/reel.cc b/src/reel.cc
index 78be2293..50c780f4 100644
--- a/src/reel.cc
+++ b/src/reel.cc
@@ -1,5 +1,5 @@
/*
- Copyright (C) 2014-2015 Carl Hetherington <cth@carlh.net>
+ Copyright (C) 2014-2016 Carl Hetherington <cth@carlh.net>
This file is part of libdcp.
@@ -45,6 +45,7 @@
#include "decrypted_kdm_key.h"
#include "decrypted_kdm.h"
#include "interop_subtitle_asset.h"
+#include "smpte_subtitle_asset.h"
#include "reel_atmos_asset.h"
#include <libxml++/nodes/element.h>
@@ -184,6 +185,12 @@ Reel::add (DecryptedKDM const & kdm)
if (_main_sound && i->id() == _main_sound->key_id()) {
_main_sound->asset()->set_key (i->key ());
}
+ if (_main_subtitle && i->id() == _main_subtitle->key_id()) {
+ shared_ptr<SMPTESubtitleAsset> s = dynamic_pointer_cast<SMPTESubtitleAsset> (_main_subtitle->asset());
+ if (s) {
+ s->set_key (i->key ());
+ }
+ }
if (_atmos && i->id() == _atmos->key_id()) {
_atmos->asset()->set_key (i->key ());
}
diff --git a/src/reel_subtitle_asset.cc b/src/reel_subtitle_asset.cc
index 0553f4ba..c7e40420 100644
--- a/src/reel_subtitle_asset.cc
+++ b/src/reel_subtitle_asset.cc
@@ -37,19 +37,25 @@
#include "subtitle_asset.h"
#include "reel_subtitle_asset.h"
+#include "smpte_subtitle_asset.h"
+#include <libxml++/libxml++.h>
using std::string;
using boost::shared_ptr;
+using boost::dynamic_pointer_cast;
+using boost::optional;
using namespace dcp;
ReelSubtitleAsset::ReelSubtitleAsset (boost::shared_ptr<SubtitleAsset> asset, Fraction edit_rate, int64_t intrinsic_duration, int64_t entry_point)
: ReelAsset (asset, edit_rate, intrinsic_duration, entry_point)
+ , ReelMXF (dynamic_pointer_cast<SMPTESubtitleAsset>(asset) ? dynamic_pointer_cast<SMPTESubtitleAsset>(asset)->key_id() : optional<string>())
{
}
ReelSubtitleAsset::ReelSubtitleAsset (boost::shared_ptr<const cxml::Node> node)
: ReelAsset (node)
+ , ReelMXF (node)
{
node->ignore_child ("Language");
node->done ();
@@ -60,3 +66,23 @@ ReelSubtitleAsset::cpl_node_name () const
{
return "MainSubtitle";
}
+
+string
+ReelSubtitleAsset::key_type () const
+{
+ return "MDSK";
+}
+
+void
+ReelSubtitleAsset::write_to_cpl (xmlpp::Node* node, Standard standard) const
+{
+ ReelAsset::write_to_cpl (node, standard);
+
+ if (key_id ()) {
+ /* Find <MainSubtitle> */
+ xmlpp::Node* ms = find_child (node, cpl_node_name ());
+ /* Find <Hash> */
+ xmlpp::Node* hash = find_child (ms, "Hash");
+ ms->add_child_before (hash, "KeyId")->add_child_text ("urn:uuid:" + key_id().get ());
+ }
+}
diff --git a/src/reel_subtitle_asset.h b/src/reel_subtitle_asset.h
index 91dcd53b..1d76ba9e 100644
--- a/src/reel_subtitle_asset.h
+++ b/src/reel_subtitle_asset.h
@@ -39,6 +39,7 @@
#define LIBDCP_REEL_SUBTITLE_ASSET_H
#include "reel_asset.h"
+#include "reel_mxf.h"
#include "subtitle_asset.h"
namespace dcp {
@@ -46,19 +47,22 @@ namespace dcp {
class SubtitleAsset;
/** @class ReelSubtitleAsset
- * @brief Part of a Reel's description which refers to a subtitle XML file.
+ * @brief Part of a Reel's description which refers to a subtitle XML/MXF file.
*/
-class ReelSubtitleAsset : public ReelAsset
+class ReelSubtitleAsset : public ReelAsset, public ReelMXF
{
public:
ReelSubtitleAsset (boost::shared_ptr<SubtitleAsset> asset, Fraction edit_rate, int64_t instrinsic_duration, int64_t entry_point);
explicit ReelSubtitleAsset (boost::shared_ptr<const cxml::Node>);
+ void write_to_cpl (xmlpp::Node* node, Standard standard) const;
+
boost::shared_ptr<SubtitleAsset> asset () const {
return asset_of_type<SubtitleAsset> ();
}
private:
+ std::string key_type () const;
std::string cpl_node_name () const;
};
diff --git a/src/smpte_subtitle_asset.cc b/src/smpte_subtitle_asset.cc
index e0934ab5..239fff74 100644
--- a/src/smpte_subtitle_asset.cc
+++ b/src/smpte_subtitle_asset.cc
@@ -1,5 +1,5 @@
/*
- Copyright (C) 2012-2015 Carl Hetherington <cth@carlh.net>
+ Copyright (C) 2012-2016 Carl Hetherington <cth@carlh.net>
This file is part of libdcp.
@@ -45,6 +45,7 @@
#include "util.h"
#include "compose.hpp"
#include "encryption_context.h"
+#include "decryption_context.h"
#include <asdcp/AS_DCP.h>
#include <asdcp/KM_util.h>
#include <libxml++/libxml++.h>
@@ -79,19 +80,26 @@ SMPTESubtitleAsset::SMPTESubtitleAsset (boost::filesystem::path file)
shared_ptr<cxml::Document> xml (new cxml::Document ("SubtitleReel"));
shared_ptr<ASDCP::TimedText::MXFReader> reader (new ASDCP::TimedText::MXFReader ());
- Kumu::Result_t r = reader->OpenRead (file.string().c_str ());
-
+ Kumu::Result_t r = reader->OpenRead (_file.string().c_str ());
if (!ASDCP_FAILURE (r)) {
- string s;
- reader->ReadTimedTextResource (s, 0, 0);
- xml->read_string (s);
+ /* MXF-wrapped */
ASDCP::WriterInfo info;
reader->FillWriterInfo (info);
_id = read_writer_info (info);
+ if (!_key_id) {
+ /* Not encrypted; read it in now */
+ string s;
+ reader->ReadTimedTextResource (s);
+ xml->read_string (s);
+ parse_xml (xml);
+ read_mxf_descriptor (reader, shared_ptr<DecryptionContext> (new DecryptionContext ()));
+ }
} else {
- reader.reset ();
+ /* Plain XML */
try {
+ xml.reset (new cxml::Document ("SubtitleReel"));
xml->read_file (file);
+ parse_xml (xml);
_id = remove_urn_uuid (xml->string_child ("Id"));
} catch (cxml::Error& e) {
boost::throw_exception (
@@ -101,7 +109,11 @@ SMPTESubtitleAsset::SMPTESubtitleAsset (boost::filesystem::path file)
);
}
}
+}
+void
+SMPTESubtitleAsset::parse_xml (shared_ptr<cxml::Document> xml)
+{
_load_font_nodes = type_children<dcp::SMPTELoadFontNode> (xml, "LoadFont");
_content_title_text = xml->string_child ("ContentTitleText");
@@ -141,45 +153,80 @@ SMPTESubtitleAsset::SMPTESubtitleAsset (boost::filesystem::path file)
parse_subtitles (xml, font_nodes, subtitle_nodes);
- if (reader) {
- ASDCP::TimedText::TimedTextDescriptor descriptor;
- reader->FillTimedTextDescriptor (descriptor);
+ /* Guess intrinsic duration */
+ _intrinsic_duration = latest_subtitle_out().as_editable_units (_edit_rate.numerator / _edit_rate.denominator);
+}
+
+void
+SMPTESubtitleAsset::read_mxf_descriptor (shared_ptr<ASDCP::TimedText::MXFReader> reader, shared_ptr<DecryptionContext> dec)
+{
+ ASDCP::TimedText::TimedTextDescriptor descriptor;
+ reader->FillTimedTextDescriptor (descriptor);
- /* Load fonts */
+ /* Load fonts */
- for (
- ASDCP::TimedText::ResourceList_t::const_iterator i = descriptor.ResourceList.begin();
- i != descriptor.ResourceList.end();
- ++i) {
+ for (
+ ASDCP::TimedText::ResourceList_t::const_iterator i = descriptor.ResourceList.begin();
+ i != descriptor.ResourceList.end();
+ ++i) {
- if (i->Type == ASDCP::TimedText::MT_OPENTYPE) {
- ASDCP::TimedText::FrameBuffer buffer;
- buffer.Capacity (10 * 1024 * 1024);
- reader->ReadAncillaryResource (i->ResourceID, buffer);
+ if (i->Type == ASDCP::TimedText::MT_OPENTYPE) {
+ ASDCP::TimedText::FrameBuffer buffer;
+ buffer.Capacity (10 * 1024 * 1024);
+ reader->ReadAncillaryResource (i->ResourceID, buffer, dec->decryption());
- char id[64];
- Kumu::bin2UUIDhex (i->ResourceID, ASDCP::UUIDlen, id, sizeof (id));
+ char id[64];
+ Kumu::bin2UUIDhex (i->ResourceID, ASDCP::UUIDlen, id, sizeof (id));
- shared_array<uint8_t> data (new uint8_t[buffer.Size()]);
- memcpy (data.get(), buffer.RoData(), buffer.Size());
+ shared_array<uint8_t> data (new uint8_t[buffer.Size()]);
+ memcpy (data.get(), buffer.RoData(), buffer.Size());
- list<shared_ptr<SMPTELoadFontNode> >::const_iterator j = _load_font_nodes.begin ();
- while (j != _load_font_nodes.end() && (*j)->urn != id) {
- ++j;
- }
+ list<shared_ptr<SMPTELoadFontNode> >::const_iterator j = _load_font_nodes.begin ();
+ while (j != _load_font_nodes.end() && (*j)->urn != id) {
+ ++j;
+ }
- if (j != _load_font_nodes.end ()) {
- _fonts.push_back (Font ((*j)->id, (*j)->urn, Data (data, buffer.Size ())));
- }
+ if (j != _load_font_nodes.end ()) {
+ _fonts.push_back (Font ((*j)->id, (*j)->urn, Data (data, buffer.Size ())));
}
}
+ }
- /* Get intrinsic duration */
- _intrinsic_duration = descriptor.ContainerDuration;
- } else {
- /* Guess intrinsic duration */
- _intrinsic_duration = latest_subtitle_out().as_editable_units (_edit_rate.numerator / _edit_rate.denominator);
+ /* Get intrinsic duration */
+ _intrinsic_duration = descriptor.ContainerDuration;
+}
+
+void
+SMPTESubtitleAsset::set_key (Key key)
+{
+ MXF::set_key (key);
+
+ if (!_key_id || _file.empty()) {
+ /* Either we don't have any data to read, or it wasn't
+ encrypted, so we don't need to do anything else.
+ */
+ return;
}
+
+ /* Our data was encrypted; now we can decrypt it */
+
+ shared_ptr<ASDCP::TimedText::MXFReader> reader (new ASDCP::TimedText::MXFReader ());
+ Kumu::Result_t r = reader->OpenRead (_file.string().c_str ());
+ if (ASDCP_FAILURE (r)) {
+ boost::throw_exception (
+ DCPReadError (
+ String::compose ("Could not read encrypted subtitle MXF (%1)", _file, static_cast<int> (r))
+ )
+ );
+ }
+
+ string s;
+ shared_ptr<DecryptionContext> dec (new DecryptionContext (key));
+ reader->ReadTimedTextResource (s, dec->decryption());
+ shared_ptr<cxml::Document> xml (new cxml::Document ("SubtitleReel"));
+ xml->read_string (s);
+ parse_xml (xml);
+ read_mxf_descriptor (reader, dec);
}
list<shared_ptr<LoadFontNode> >
diff --git a/src/smpte_subtitle_asset.h b/src/smpte_subtitle_asset.h
index ffe5b6d9..004531a2 100644
--- a/src/smpte_subtitle_asset.h
+++ b/src/smpte_subtitle_asset.h
@@ -49,6 +49,7 @@ namespace ASDCP {
namespace dcp {
class SMPTELoadFontNode;
+class DecryptionContext;
/** @class SMPTESubtitleAsset
* @brief A set of subtitles to be read and/or written in the SMPTE format.
@@ -74,6 +75,7 @@ public:
void write (boost::filesystem::path path) const;
void add (SubtitleString);
void add_font (std::string id, boost::filesystem::path file);
+ void set_key (Key key);
void set_content_title_text (std::string t) {
_content_title_text = t;
@@ -159,6 +161,8 @@ protected:
private:
void read_fonts (boost::shared_ptr<ASDCP::TimedText::MXFReader>);
+ void parse_xml (boost::shared_ptr<cxml::Document> xml);
+ void read_mxf_descriptor (boost::shared_ptr<ASDCP::TimedText::MXFReader> reader, boost::shared_ptr<DecryptionContext> dec);
/** The total length of this content in video frames. The amount of
* content presented may be less than this.
diff --git a/src/sound_frame.cc b/src/sound_frame.cc
index 83b053b2..132d25bd 100644
--- a/src/sound_frame.cc
+++ b/src/sound_frame.cc
@@ -37,18 +37,19 @@
#include "sound_frame.h"
#include "exceptions.h"
+#include "decryption_context.h"
#include <asdcp/AS_DCP.h>
#include <asdcp/KM_fileio.h>
-using namespace std;
using namespace dcp;
+using boost::shared_ptr;
-SoundFrame::SoundFrame (ASDCP::PCM::MXFReader* reader, int n, ASDCP::AESDecContext* c)
+SoundFrame::SoundFrame (ASDCP::PCM::MXFReader* reader, int n, shared_ptr<DecryptionContext> c)
{
/* XXX: unfortunate guesswork on this buffer size */
_buffer = new ASDCP::PCM::FrameBuffer (1 * Kumu::Megabyte);
- if (ASDCP_FAILURE (reader->ReadFrame (n, *_buffer, c))) {
+ if (ASDCP_FAILURE (reader->ReadFrame (n, *_buffer, c->decryption()))) {
boost::throw_exception (DCPReadError ("could not read audio frame"));
}
}
diff --git a/src/sound_frame.h b/src/sound_frame.h
index 4c061fdc..3cd3246b 100644
--- a/src/sound_frame.h
+++ b/src/sound_frame.h
@@ -53,6 +53,8 @@ namespace ASDCP {
namespace dcp {
+class DecryptionContext;
+
/** @class SoundFrame
* @brief One &lsquo;frame&rsquo; of sound data from a SoundAsset.
*/
@@ -67,7 +69,7 @@ public:
private:
friend class SoundAssetReader;
- SoundFrame (ASDCP::PCM::MXFReader* reader, int n, ASDCP::AESDecContext *);
+ SoundFrame (ASDCP::PCM::MXFReader* reader, int n, boost::shared_ptr<DecryptionContext>);
/** a buffer to hold the frame */
ASDCP::PCM::FrameBuffer* _buffer;
diff --git a/src/stereo_picture_frame.cc b/src/stereo_picture_frame.cc
index 82f84f3f..e645c7df 100644
--- a/src/stereo_picture_frame.cc
+++ b/src/stereo_picture_frame.cc
@@ -38,6 +38,7 @@
#include "colour_conversion.h"
#include "compose.hpp"
#include "j2k.h"
+#include "decryption_context.h"
#include <asdcp/AS_DCP.h>
#include <asdcp/KM_fileio.h>
@@ -49,12 +50,12 @@ using namespace dcp;
* @param reader Reader for the MXF file.
* @param n Frame within the asset, not taking EntryPoint into account.
*/
-StereoPictureFrame::StereoPictureFrame (ASDCP::JP2K::MXFSReader* reader, int n, ASDCP::AESDecContext* c)
+StereoPictureFrame::StereoPictureFrame (ASDCP::JP2K::MXFSReader* reader, int n, shared_ptr<DecryptionContext> c)
{
/* XXX: unfortunate guesswork on this buffer size */
_buffer = new ASDCP::JP2K::SFrameBuffer (4 * Kumu::Megabyte);
- if (ASDCP_FAILURE (reader->ReadFrame (n, *_buffer, c))) {
+ if (ASDCP_FAILURE (reader->ReadFrame (n, *_buffer, c->decryption()))) {
boost::throw_exception (DCPReadError (String::compose ("could not read video frame %1 of %2", n)));
}
}
diff --git a/src/stereo_picture_frame.h b/src/stereo_picture_frame.h
index fd0e70fd..65e1cea3 100644
--- a/src/stereo_picture_frame.h
+++ b/src/stereo_picture_frame.h
@@ -49,6 +49,7 @@ namespace ASDCP {
namespace dcp {
class OpenJPEGImage;
+class DecryptionContext;
/** A single frame of a 3D (stereoscopic) picture asset */
class StereoPictureFrame : public boost::noncopyable
@@ -70,7 +71,7 @@ public:
private:
friend class StereoPictureAssetReader;
- StereoPictureFrame (ASDCP::JP2K::MXFSReader* reader, int n, ASDCP::AESDecContext *);
+ StereoPictureFrame (ASDCP::JP2K::MXFSReader* reader, int n, boost::shared_ptr<DecryptionContext>);
ASDCP::JP2K::SFrameBuffer* _buffer;
};
diff --git a/src/wscript b/src/wscript
index d76b1a50..be7d4621 100644
--- a/src/wscript
+++ b/src/wscript
@@ -47,6 +47,7 @@ def build(bld):
dcp.cc
dcp_time.cc
decrypted_kdm.cc
+ decryption_context.cc
decrypted_kdm_key.cc
encrypted_kdm.cc
encryption_context.cc