summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCarl Hetherington <cth@carlh.net>2024-08-02 18:32:50 +0200
committerCarl Hetherington <cth@carlh.net>2024-08-02 18:32:50 +0200
commit30253cbddbe1167d1b36d4f4185681ceeb6e26ba (patch)
tree86ffb12b9e48d9d74512aa2b2c3952e25cdf9bad
parent95abcc7d18997c1391423d15506bf03ab20997e6 (diff)
WIP: hacks.shared-ptr
-rw-r--r--src/lib/j2k_image_proxy.cc246
-rw-r--r--src/lib/j2k_image_proxy.h183
-rw-r--r--src/lib/map_cli.cc21
-rw-r--r--src/lib/wscript1
4 files changed, 187 insertions, 264 deletions
diff --git a/src/lib/j2k_image_proxy.cc b/src/lib/j2k_image_proxy.cc
deleted file mode 100644
index 023723c8b..000000000
--- a/src/lib/j2k_image_proxy.cc
+++ /dev/null
@@ -1,246 +0,0 @@
-/*
- Copyright (C) 2014-2021 Carl Hetherington <cth@carlh.net>
-
- This file is part of DCP-o-matic.
-
- DCP-o-matic 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.
-
- DCP-o-matic 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 DCP-o-matic. If not, see <http://www.gnu.org/licenses/>.
-
-*/
-
-
-#include "dcpomatic_assert.h"
-#include "dcpomatic_socket.h"
-#include "image.h"
-#include "j2k_image_proxy.h"
-#include <dcp/colour_conversion.h>
-#include <dcp/j2k_transcode.h>
-#include <dcp/mono_j2k_picture_frame.h>
-#include <dcp/openjpeg_image.h>
-#include <dcp/raw_convert.h>
-#include <dcp/rgb_xyz.h>
-#include <dcp/stereo_j2k_picture_frame.h>
-#include <dcp/warnings.h>
-#include <libcxml/cxml.h>
-LIBDCP_DISABLE_WARNINGS
-#include <libxml++/libxml++.h>
-LIBDCP_ENABLE_WARNINGS
-#include <iostream>
-
-#include "i18n.h"
-
-
-using std::cout;
-using std::dynamic_pointer_cast;
-using std::make_shared;
-using std::max;
-using std::shared_ptr;
-using std::string;
-using boost::optional;
-using dcp::ArrayData;
-using dcp::raw_convert;
-
-
-/** Construct a J2KImageProxy from a JPEG2000 file */
-J2KImageProxy::J2KImageProxy (boost::filesystem::path path, dcp::Size size, AVPixelFormat pixel_format)
- : _data (new dcp::ArrayData(path))
- , _size (size)
- , _pixel_format (pixel_format)
- , _error (false)
-{
- /* ::image assumes 16bpp */
- DCPOMATIC_ASSERT (_pixel_format == AV_PIX_FMT_RGB48 || _pixel_format == AV_PIX_FMT_XYZ12LE);
-}
-
-
-J2KImageProxy::J2KImageProxy (
- shared_ptr<const dcp::MonoJ2KPictureFrame> frame,
- dcp::Size size,
- AVPixelFormat pixel_format,
- optional<int> forced_reduction
- )
- : _data (frame)
- , _size (size)
- , _pixel_format (pixel_format)
- , _forced_reduction (forced_reduction)
- , _error (false)
-{
- /* ::image assumes 16bpp */
- DCPOMATIC_ASSERT (_pixel_format == AV_PIX_FMT_RGB48 || _pixel_format == AV_PIX_FMT_XYZ12LE);
-}
-
-
-J2KImageProxy::J2KImageProxy (
- shared_ptr<const dcp::StereoJ2KPictureFrame> frame,
- dcp::Size size,
- dcp::Eye eye,
- AVPixelFormat pixel_format,
- optional<int> forced_reduction
- )
- : _data (eye == dcp::Eye::LEFT ? frame->left() : frame->right())
- , _size (size)
- , _eye (eye)
- , _pixel_format (pixel_format)
- , _forced_reduction (forced_reduction)
- , _error (false)
-{
- /* ::image assumes 16bpp */
- DCPOMATIC_ASSERT (_pixel_format == AV_PIX_FMT_RGB48 || _pixel_format == AV_PIX_FMT_XYZ12LE);
-}
-
-
-J2KImageProxy::J2KImageProxy (shared_ptr<cxml::Node> xml, shared_ptr<Socket> socket)
- : _error (false)
-{
- _size = dcp::Size (xml->number_child<int>("Width"), xml->number_child<int>("Height"));
- if (xml->optional_number_child<int>("Eye")) {
- _eye = static_cast<dcp::Eye>(xml->number_child<int>("Eye"));
- }
- auto data = make_shared<ArrayData>(xml->number_child<int>("Size"));
- /* This only matters when we are using J2KImageProxy for the preview, which
- will never use this constructor (which is only used for passing data to
- encode servers). So we can put anything in here. It's a bit of a hack.
- */
- _pixel_format = AV_PIX_FMT_XYZ12LE;
- socket->read (data->data(), data->size());
- _data = data;
-}
-
-
-int
-J2KImageProxy::prepare (Image::Alignment alignment, optional<dcp::Size> target_size) const
-{
- boost::mutex::scoped_lock lm (_mutex);
-
- if (_image && target_size == _target_size) {
- DCPOMATIC_ASSERT (_reduce);
- return *_reduce;
- }
-
- int reduce = 0;
-
- if (_forced_reduction) {
- reduce = *_forced_reduction;
- } else {
- while (target_size && (_size.width / pow(2, reduce)) > target_size->width && (_size.height / pow(2, reduce)) > target_size->height) {
- ++reduce;
- }
-
- --reduce;
- reduce = max (0, reduce);
- }
-
- try {
- /* XXX: should check that potentially trashing _data here doesn't matter */
- auto decompressed = dcp::decompress_j2k (const_cast<uint8_t*>(_data->data()), _data->size(), reduce);
- _image = make_shared<Image>(_pixel_format, decompressed->size(), alignment);
-
- int const shift = 16 - decompressed->precision (0);
-
- /* Copy data in whatever format (sRGB or XYZ) into our Image; I'm assuming
- the data is 12-bit either way.
- */
-
- int const width = decompressed->size().width;
-
- int p = 0;
- int* decomp_0 = decompressed->data (0);
- int* decomp_1 = decompressed->data (1);
- int* decomp_2 = decompressed->data (2);
- for (int y = 0; y < decompressed->size().height; ++y) {
- auto q = reinterpret_cast<uint16_t *>(_image->data()[0] + y * _image->stride()[0]);
- for (int x = 0; x < width; ++x) {
- *q++ = decomp_0[p] << shift;
- *q++ = decomp_1[p] << shift;
- *q++ = decomp_2[p] << shift;
- ++p;
- }
- }
- } catch (dcp::J2KDecompressionError& e) {
- _image = make_shared<Image>(_pixel_format, _size, alignment);
- _image->make_black ();
- _error = true;
- }
-
- _target_size = target_size;
- _reduce = reduce;
-
- return reduce;
-}
-
-
-ImageProxy::Result
-J2KImageProxy::image (Image::Alignment alignment, optional<dcp::Size> target_size) const
-{
- int const r = prepare (alignment, target_size);
-
- /* I think this is safe without a lock on mutex. _image is guaranteed to be
- set up when prepare() has happened.
- */
- return Result (_image, r, _error);
-}
-
-
-void
-J2KImageProxy::add_metadata(xmlpp::Element* element) const
-{
- cxml::add_text_child(element, "Type", N_("J2K"));
- cxml::add_text_child(element, "Width", raw_convert<string>(_size.width));
- cxml::add_text_child(element, "Height", raw_convert<string>(_size.height));
- if (_eye) {
- cxml::add_text_child(element, "Eye", raw_convert<string>(static_cast<int>(_eye.get())));
- }
- cxml::add_text_child(element, "Size", raw_convert<string>(_data->size()));
-}
-
-
-void
-J2KImageProxy::write_to_socket (shared_ptr<Socket> socket) const
-{
- socket->write (_data->data(), _data->size());
-}
-
-
-bool
-J2KImageProxy::same (shared_ptr<const ImageProxy> other) const
-{
- auto jp = dynamic_pointer_cast<const J2KImageProxy>(other);
- if (!jp) {
- return false;
- }
-
- return *_data == *jp->_data;
-}
-
-
-J2KImageProxy::J2KImageProxy (ArrayData data, dcp::Size size, AVPixelFormat pixel_format)
- : _data (new ArrayData(data))
- , _size (size)
- , _pixel_format (pixel_format)
- , _error (false)
-{
- /* ::image assumes 16bpp */
- DCPOMATIC_ASSERT (_pixel_format == AV_PIX_FMT_RGB48 || _pixel_format == AV_PIX_FMT_XYZ12LE);
-}
-
-
-size_t
-J2KImageProxy::memory_used () const
-{
- size_t m = _data->size();
- if (_image) {
- /* 3 components, 16-bits per pixel */
- m += 3 * 2 * _image->size().width * _image->size().height;
- }
- return m;
-}
diff --git a/src/lib/j2k_image_proxy.h b/src/lib/j2k_image_proxy.h
index 1d2d5cc21..0343f929c 100644
--- a/src/lib/j2k_image_proxy.h
+++ b/src/lib/j2k_image_proxy.h
@@ -19,8 +19,11 @@
*/
+#include "dcpomatic_assert.h"
#include "image_proxy.h"
#include <dcp/array_data.h>
+#include <dcp/mono_j2k_picture_frame.h>
+#include <dcp/types.h>
#include <dcp/util.h>
#include <boost/thread/mutex.hpp>
@@ -31,41 +34,181 @@ namespace dcp {
}
+template <class Frame>
class J2KImageProxy : public ImageProxy
{
public:
- J2KImageProxy (boost::filesystem::path path, dcp::Size, AVPixelFormat pixel_format);
+ J2KImageProxy(boost::filesystem::path path, dcp::Size, AVPixelFormat pixel_format)
+ : _data(path)
+ , _size (size)
+ , _pixel_format (pixel_format)
+ , _error (false)
+ {
+ /* ::image assumes 16bpp */
+ DCPOMATIC_ASSERT (_pixel_format == AV_PIX_FMT_RGB48 || _pixel_format == AV_PIX_FMT_XYZ12LE);
+ }
J2KImageProxy (
- std::shared_ptr<const dcp::MonoJ2KPictureFrame> frame,
+ dcp::MonoJ2KPictureFrame frame,
dcp::Size,
AVPixelFormat pixel_format,
boost::optional<int> forced_reduction
- );
+ )
+ : _data (frame)
+ , _size (size)
+ , _pixel_format (pixel_format)
+ , _forced_reduction (forced_reduction)
+ , _error (false)
+ {
+ /* ::image assumes 16bpp */
+ DCPOMATIC_ASSERT (_pixel_format == AV_PIX_FMT_RGB48 || _pixel_format == AV_PIX_FMT_XYZ12LE);
+ }
J2KImageProxy (
- std::shared_ptr<const dcp::StereoJ2KPictureFrame> frame,
- dcp::Size,
- dcp::Eye,
+ dcp::StereoJ2KPictureFrame frame,
+ dcp::Size size,
+ dcp::Eye eye,
AVPixelFormat pixel_format,
boost::optional<int> forced_reduction
- );
+ )
+ : _data (eye == dcp::Eye::LEFT ? frame->left() : frame->right())
+ , _size (size)
+ , _eye (eye)
+ , _pixel_format (pixel_format)
+ , _forced_reduction (forced_reduction)
+ , _error (false)
+ {
+ /* ::image assumes 16bpp */
+ DCPOMATIC_ASSERT (_pixel_format == AV_PIX_FMT_RGB48 || _pixel_format == AV_PIX_FMT_XYZ12LE);
+ }
- J2KImageProxy (std::shared_ptr<cxml::Node> xml, std::shared_ptr<Socket> socket);
+
+ J2KImageProxy (std::shared_ptr<cxml::Node> xml, std::shared_ptr<Socket> socket)
+ {
+ _size = dcp::Size (xml->number_child<int>("Width"), xml->number_child<int>("Height"));
+ if (xml->optional_number_child<int>("Eye")) {
+ _eye = static_cast<dcp::Eye>(xml->number_child<int>("Eye"));
+ }
+ auto data = make_shared<ArrayData>(xml->number_child<int>("Size"));
+ /* This only matters when we are using J2KImageProxy for the preview, which
+ will never use this constructor (which is only used for passing data to
+ encode servers). So we can put anything in here. It's a bit of a hack.
+ */
+ _pixel_format = AV_PIX_FMT_XYZ12LE;
+ socket->read(data->data(), data->size());
+ _data = data;
+ }
/* For tests */
- J2KImageProxy (dcp::ArrayData data, dcp::Size size, AVPixelFormat pixel_format);
+ J2KImageProxy(dcp::ArrayData data, dcp::Size size, AVPixelFormat pixel_format)
+ : _data(data)
+ , _size(size)
+ , _pixel_format(pixel_format)
+ , _error(false)
+ {
+ /* ::image assumes 16bpp */
+ DCPOMATIC_ASSERT (_pixel_format == AV_PIX_FMT_RGB48 || _pixel_format == AV_PIX_FMT_XYZ12LE);
+ }
Result image (
Image::Alignment alignment,
boost::optional<dcp::Size> size = boost::optional<dcp::Size> ()
- ) const override;
+ ) const override
+ {
+ int const r = prepare (alignment, target_size);
+
+ /* I think this is safe without a lock on mutex. _image is guaranteed to be
+ set up when prepare() has happened.
+ */
+ return Result(_image, r, _error);
+ }
+
+ void add_metadata(xmlpp::Element*) const override
+ {
+ cxml::add_text_child(element, "Type", N_("J2K"));
+ cxml::add_text_child(element, "Width", raw_convert<string>(_size.width));
+ cxml::add_text_child(element, "Height", raw_convert<string>(_size.height));
+ if (_eye) {
+ cxml::add_text_child(element, "Eye", raw_convert<string>(static_cast<int>(_eye.get())));
+ }
+ cxml::add_text_child(element, "Size", raw_convert<string>(_data.size()));
+ }
+
+ void write_to_socket (std::shared_ptr<Socket> override) const override
+ {
+ socket->write(_data.data(), _data.size());
+ }
- void add_metadata(xmlpp::Element*) const override;
- void write_to_socket (std::shared_ptr<Socket> override) const override;
/** @return true if our image is definitely the same as another, false if it is probably not */
- bool same (std::shared_ptr<const ImageProxy>) const override;
- int prepare (Image::Alignment alignment, boost::optional<dcp::Size> = boost::optional<dcp::Size>()) const override;
+ bool same(std::shared_ptr<const ImageProxy>) const override
+ {
+ auto jp = dynamic_pointer_cast<const J2KImageProxy>(other);
+ if (!jp) {
+ return false;
+ }
+
+ return _data == *jp->_data;
+ }
+
+ int prepare(Image::Alignment alignment, boost::optional<dcp::Size> = boost::optional<dcp::Size>()) const override
+ {
+ boost::mutex::scoped_lock lm (_mutex);
+
+ if (_image && target_size == _target_size) {
+ DCPOMATIC_ASSERT (_reduce);
+ return *_reduce;
+ }
+
+ int reduce = 0;
+
+ if (_forced_reduction) {
+ reduce = *_forced_reduction;
+ } else {
+ while (target_size && (_size.width / pow(2, reduce)) > target_size->width && (_size.height / pow(2, reduce)) > target_size->height) {
+ ++reduce;
+ }
+
+ --reduce;
+ reduce = max (0, reduce);
+ }
+
+ try {
+ /* XXX: should check that potentially trashing _data here doesn't matter */
+ auto decompressed = dcp::decompress_j2k (const_cast<uint8_t*>(_data->data()), _data->size(), reduce);
+ _image = make_shared<Image>(_pixel_format, decompressed->size(), alignment);
+
+ int const shift = 16 - decompressed->precision (0);
+
+ /* Copy data in whatever format (sRGB or XYZ) into our Image; I'm assuming
+ the data is 12-bit either way.
+ */
+
+ int const width = decompressed->size().width;
+
+ int p = 0;
+ int* decomp_0 = decompressed->data (0);
+ int* decomp_1 = decompressed->data (1);
+ int* decomp_2 = decompressed->data (2);
+ for (int y = 0; y < decompressed->size().height; ++y) {
+ auto q = reinterpret_cast<uint16_t *>(_image->data()[0] + y * _image->stride()[0]);
+ for (int x = 0; x < width; ++x) {
+ *q++ = decomp_0[p] << shift;
+ *q++ = decomp_1[p] << shift;
+ *q++ = decomp_2[p] << shift;
+ ++p;
+ }
+ }
+ } catch (dcp::J2KDecompressionError& e) {
+ _image = make_shared<Image>(_pixel_format, _size, alignment);
+ _image->make_black ();
+ _error = true;
+ }
+
+ _target_size = target_size;
+ _reduce = reduce;
+
+ return reduce;
+ }
std::shared_ptr<const dcp::Data> j2k () const {
return _data;
@@ -79,10 +222,18 @@ public:
return _eye;
}
- size_t memory_used () const override;
+ size_t memory_used () const override
+ {
+ size_t m = _data->size();
+ if (_image) {
+ /* 3 components, 16-bits per pixel */
+ m += 3 * 2 * _image->size().width * _image->size().height;
+ }
+ return m;
+ }
private:
- std::shared_ptr<const dcp::Data> _data;
+ Frame _data;
dcp::Size _size;
boost::optional<dcp::Eye> _eye;
mutable std::shared_ptr<Image> _image;
diff --git a/src/lib/map_cli.cc b/src/lib/map_cli.cc
index be3841deb..aab4fabff 100644
--- a/src/lib/map_cli.cc
+++ b/src/lib/map_cli.cc
@@ -64,6 +64,7 @@ help(std::function<void (string)> out)
out(" -s, --soft-link using soft links instead of copying");
out(" -d, --assets-dir look in this directory for assets (can be given more than once)");
out(" -r, --rename rename all files to <uuid>.<mxf|xml>");
+ out(" -k, --kdm decrypt assets using the given KDM");
out(" --config <dir> directory containing config.xml and cinemas.xml");
}
@@ -77,6 +78,7 @@ map_cli(int argc, char* argv[], std::function<void (string)> out)
bool rename = false;
vector<boost::filesystem::path> assets_dir;
optional<boost::filesystem::path> config_dir;
+ optional<boost::filesystem::path> kdm_filename;
/* This makes it possible to call getopt several times in the same executable, for tests */
optind = 0;
@@ -90,11 +92,12 @@ map_cli(int argc, char* argv[], std::function<void (string)> out)
{ "soft-link", no_argument, 0, 's' },
{ "assets-dir", required_argument, 0, 'd' },
{ "rename", no_argument, 0, 'r' },
+ { "kdm", required_argument, 0, 'k' },
{ "config", required_argument, 0, 'c' },
{ 0, 0, 0, 0 }
};
- int c = getopt_long(argc, argv, "ho:lsd:rc:", long_options, &option_index);
+ int c = getopt_long(argc, argv, "ho:lsd:rk:c:", long_options, &option_index);
if (c == -1) {
break;
@@ -121,6 +124,9 @@ map_cli(int argc, char* argv[], std::function<void (string)> out)
case 'r':
rename = true;
break;
+ case 'k':
+ kdm = optarg;
+ break;
case 'c':
config_dir = optarg;
break;
@@ -165,6 +171,19 @@ map_cli(int argc, char* argv[], std::function<void (string)> out)
return String::compose("Could not create output directory %1: %2", *output_dir, ec.message());
}
+ optional<dcp::DecryptedKDM> kdm;
+ if (kdm_filename) {
+ auto key = Config::instance()->decryption_chain()->key();
+ if (!key) {
+ return string{"Could not find private key to decrypt the KDM"};
+ }
+ try {
+ kdm = dcp::DecryptedKDM(dcp::EncryptedKDM(dcp::file_to_string(*kdm_filename)), *key);
+ } catch (std::exception& e) {
+ return String::compose("Could not decrypt KDM %1: %2", kdm_filename, e.what());
+ }
+ }
+
/* Find all the assets in the asset directories. This assumes that the asset directories are in fact
* DCPs (with AssetMaps and so on). We could search for assets ourselves here but interop fonts are
* a little tricky because they don't contain their own UUID within the DCP.
diff --git a/src/lib/wscript b/src/lib/wscript
index dfe3ce487..37ae1e19b 100644
--- a/src/lib/wscript
+++ b/src/lib/wscript
@@ -140,7 +140,6 @@ sources = """
image_proxy.cc
image_store.cc
internal_player_server.cc
- j2k_image_proxy.cc
job.cc
job_manager.cc
j2k_encoder.cc