#include <iostream>
#include <iomanip>
#include <boost/filesystem.hpp>
+#include <boost/lexical_cast.hpp>
#include <openssl/sha.h>
+#include <libxml++/nodes/element.h>
+#include <libxml++/document.h>
+#include <xmlsec/xmldsig.h>
+#include <xmlsec/dl.h>
+#include <xmlsec/app.h>
+#include <xmlsec/crypto.h>
#include "KM_util.h"
#include "KM_fileio.h"
#include "AS_DCP.h"
#include "exceptions.h"
#include "types.h"
#include "argb_frame.h"
+#include "certificates.h"
#include "gamma_lut.h"
-#include "xyz_srgb_lut.h"
+#include "xyz_frame.h"
using std::string;
+using std::cout;
using std::stringstream;
using std::min;
using std::max;
+using std::list;
+using std::setw;
+using std::setfill;
using boost::shared_ptr;
+using boost::lexical_cast;
using namespace libdcp;
/** Create a UUID.
/** Create a digest for a file.
* @param filename File name.
+ * @param progress Pointer to a progress reporting function, or 0. The function will be called
+ * with a progress value between 0 and 1.
* @return Digest.
*/
string
-libdcp::make_digest (string filename)
+libdcp::make_digest (string filename, boost::function<void (float)>* progress)
{
Kumu::FileReader reader;
if (ASDCP_FAILURE (reader.OpenRead (filename.c_str ()))) {
SHA_CTX sha;
SHA1_Init (&sha);
-
- Kumu::ByteString read_buffer (65536);
- int done = 0;
+
+ int const buffer_size = 65536;
+ Kumu::ByteString read_buffer (buffer_size);
+
+ Kumu::fsize_t done = 0;
+ Kumu::fsize_t const size = reader.Size ();
while (1) {
ui32_t read = 0;
Kumu::Result_t r = reader.Read (read_buffer.Data(), read_buffer.Capacity(), &read);
}
SHA1_Update (&sha, read_buffer.Data(), read);
- done += read;
+
+ if (progress) {
+ (*progress) (float (done) / size);
+ done += read;
+ }
}
- byte_t byte_buffer[20];
+ byte_t byte_buffer[SHA_DIGEST_LENGTH];
SHA1_Final (byte_buffer, &sha);
- stringstream s;
char digest[64];
- return Kumu::base64encode (byte_buffer, 20, digest, 64);
+ return Kumu::base64encode (byte_buffer, SHA_DIGEST_LENGTH, digest, 64);
}
/** Convert a content kind to a string which can be used in a
* e.g. 0 reduces by (2^0 == 1), ie keeping the same size.
* 1 reduces by (2^1 == 2), ie halving the size of the image.
* This is useful for scaling 4K DCP images down to 2K.
- * @return openjpeg image, which the caller must call opj_image_destroy() on.
+ * @return XYZ image.
*/
-opj_image_t *
+shared_ptr<libdcp::XYZFrame>
libdcp::decompress_j2k (uint8_t* data, int64_t size, int reduce)
{
opj_dinfo_t* decoder = opj_create_decompress (CODEC_J2K);
if (!image) {
opj_destroy_decompress (decoder);
opj_cio_close (cio);
- boost::throw_exception (DCPReadError ("could not decode JPEG2000 codestream"));
+ boost::throw_exception (DCPReadError ("could not decode JPEG2000 codestream of " + lexical_cast<string> (size) + " bytes."));
}
opj_cio_close (cio);
image->x1 = rint (float(image->x1) / pow (2, reduce));
image->y1 = rint (float(image->y1) / pow (2, reduce));
- return image;
-}
-
-/** Convert an openjpeg XYZ image to RGB.
- * @param xyz_frame Frame in XYZ.
- * @return RGB image.
- */
-shared_ptr<ARGBFrame>
-libdcp::xyz_to_rgb (opj_image_t* xyz_frame, shared_ptr<const GammaLUT> lut_in, shared_ptr<const XYZsRGBLUT> lut_out)
-{
- float const dci_coefficient = 48.0 / 52.37;
-
- /* sRGB color matrix for XYZ -> RGB */
- float const colour_matrix[3][3] = {
- { 3.240454836, -1.537138850, -0.498531547},
- {-0.969266390, 1.876010929, 0.041556082},
- { 0.055643420, -0.204025854, 1.057225162}
- };
-
- int const max_colour = pow (2, lut_out->bit_depth()) - 1;
-
- struct {
- double x, y, z;
- } s;
-
- struct {
- double r, g, b;
- } d;
-
- int* xyz_x = xyz_frame->comps[0].data;
- int* xyz_y = xyz_frame->comps[1].data;
- int* xyz_z = xyz_frame->comps[2].data;
-
- shared_ptr<ARGBFrame> argb_frame (new ARGBFrame (Size (xyz_frame->x1, xyz_frame->y1)));
-
- uint8_t* argb = argb_frame->data ();
-
- for (int y = 0; y < xyz_frame->y1; ++y) {
- uint8_t* argb_line = argb;
- for (int x = 0; x < xyz_frame->x1; ++x) {
-
- assert (*xyz_x >= 0 && *xyz_y >= 0 && *xyz_z >= 0 && *xyz_x < 4096 && *xyz_x < 4096 && *xyz_z < 4096);
-
- /* In gamma LUT */
- s.x = lut_in->lut()[*xyz_x++];
- s.y = lut_in->lut()[*xyz_y++];
- s.z = lut_in->lut()[*xyz_z++];
-
-#if 0
- /* DCI companding */
- s.x /= dci_coefficient;
- s.y /= dci_coefficient;
- s.z /= dci_coefficient;
-#endif
-
- /* XYZ to RGB */
- d.r = ((s.x * colour_matrix[0][0]) + (s.y * colour_matrix[0][1]) + (s.z * colour_matrix[0][2]));
- d.g = ((s.x * colour_matrix[1][0]) + (s.y * colour_matrix[1][1]) + (s.z * colour_matrix[1][2]));
- d.b = ((s.x * colour_matrix[2][0]) + (s.y * colour_matrix[2][1]) + (s.z * colour_matrix[2][2]));
-
- d.r = min (d.r, 1.0);
- d.r = max (d.r, 0.0);
-
- d.g = min (d.g, 1.0);
- d.g = max (d.g, 0.0);
-
- d.b = min (d.b, 1.0);
- d.b = max (d.b, 0.0);
-
- /* Out gamma LUT */
- *argb_line++ = lut_out->lut()[(int) (d.b * max_colour)];
- *argb_line++ = lut_out->lut()[(int) (d.g * max_colour)];
- *argb_line++ = lut_out->lut()[(int) (d.r * max_colour)];
- *argb_line++ = 0xff;
- }
-
- argb += argb_frame->stride ();
- }
-
- return argb_frame;
+ return shared_ptr<XYZFrame> (new XYZFrame (image));
}
/** @param s A string.
return true;
}
+void
+libdcp::init ()
+{
+ if (xmlSecInit() < 0) {
+ throw MiscError ("could not initialise xmlsec");
+ }
+
+#ifdef XMLSEC_CRYPTO_DYNAMIC_LOADING
+ if (xmlSecCryptoDLLoadLibrary(BAD_CAST XMLSEC_CRYPTO) < 0) {
+ throw MiscError ("unable to load default xmlsec-crypto library");
+ }
+#endif
+
+ if (xmlSecCryptoAppInit(0) < 0) {
+ throw MiscError ("could not initialise crypto");
+ }
+
+ if (xmlSecCryptoInit() < 0) {
+ throw MiscError ("could not initialise xmlsec-crypto");
+ }
+}
+
bool libdcp::operator== (libdcp::Size const & a, libdcp::Size const & b)
{
return (a.width == b.width && a.height == b.height);
return !(a == b);
}
+/** The base64 decode routine in KM_util.cpp gives different values to both
+ * this and the command-line base64 for some inputs. Not sure why.
+ */
+int
+libdcp::base64_decode (string const & in, unsigned char* out, int out_length)
+{
+ BIO* b64 = BIO_new (BIO_f_base64 ());
+
+ /* This means the input should have no newlines */
+ BIO_set_flags (b64, BIO_FLAGS_BASE64_NO_NL);
+
+ /* Copy our input string, removing newlines */
+ char in_buffer[in.size() + 1];
+ char* p = in_buffer;
+ for (size_t i = 0; i < in.size(); ++i) {
+ if (in[i] != '\n' && in[i] != '\r') {
+ *p++ = in[i];
+ }
+ }
+
+ BIO* bmem = BIO_new_mem_buf (in_buffer, p - in_buffer);
+ bmem = BIO_push (b64, bmem);
+ int const N = BIO_read (bmem, out, out_length);
+ BIO_free_all (bmem);
+
+ return N;
+}
+
+string
+libdcp::tm_to_string (struct tm* tm)
+{
+ char buffer[64];
+ strftime (buffer, 64, "%Y-%m-%dT%I:%M:%S", tm);
+
+ int offset = 0;
+
+#ifdef LIBDCP_POSIX
+ offset = tm->tm_gmtoff / 60;
+#else
+ TIME_ZONE_INFORMATION tz;
+ GetTimeZoneInformation (&tz);
+ offset = tz.Bias;
+#endif
+
+ return string (buffer) + utc_offset_to_string (offset);
+}
+
+/** @param b Offset from UTC to local time in minutes.
+ * @return string of the form e.g. -01:00.
+ */
+string
+libdcp::utc_offset_to_string (int b)
+{
+ bool const negative = (b < 0);
+ b = negative ? -b : b;
+
+ int const hours = b / 60;
+ int const minutes = b % 60;
+
+ stringstream o;
+ if (negative) {
+ o << "-";
+ } else {
+ o << "+";
+ }
+
+ o << setw(2) << setfill('0') << hours << ":" << setw(2) << setfill('0') << minutes;
+ return o.str ();
+}
+
+string
+libdcp::ptime_to_string (boost::posix_time::ptime t)
+{
+ struct tm t_tm = boost::posix_time::to_tm (t);
+ return tm_to_string (&t_tm);
+}