/*
- Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net>
+ Copyright (C) 2012-2020 Carl Hetherington <cth@carlh.net>
This file is part of libdcp.
*/
#include "util.h"
+#include "language_tag.h"
#include "exceptions.h"
#include "types.h"
#include "certificate.h"
#include "openjpeg_image.h"
#include "dcp_assert.h"
#include "compose.hpp"
+#include <openjpeg.h>
#include <asdcp/KM_util.h>
#include <asdcp/KM_fileio.h>
#include <asdcp/AS_DCP.h>
#include <openssl/sha.h>
#include <boost/filesystem.hpp>
#include <boost/algorithm/string.hpp>
+#include <boost/foreach.hpp>
#include <stdexcept>
#include <iostream>
#include <iomanip>
using std::setw;
using std::setfill;
using std::ostream;
-using boost::shared_ptr;
+using std::shared_ptr;
using boost::shared_array;
using boost::optional;
using boost::function;
using boost::algorithm::trim;
using namespace dcp;
+
+/* Some ASDCP objects store this as a *&, for reasons which are not
+ * at all clear, so we have to keep this around forever.
+ */
+ASDCP::Dictionary const* dcp::asdcp_smpte_dict = 0;
+
+
/** Create a UUID.
* @return UUID.
*/
return string (buffer);
}
+string
+dcp::make_digest (ArrayData data)
+{
+ SHA_CTX sha;
+ SHA1_Init (&sha);
+ SHA1_Update (&sha, data.data(), data.size());
+ byte_t byte_buffer[SHA_DIGEST_LENGTH];
+ SHA1_Final (byte_buffer, &sha);
+ char digest[64];
+ return Kumu::base64encode (byte_buffer, SHA_DIGEST_LENGTH, digest, 64);
+}
/** Create a digest for a file.
* @param filename File name.
return Kumu::base64encode (byte_buffer, SHA_DIGEST_LENGTH, digest, 64);
}
-/** Convert a content kind to a string which can be used in a
- * <ContentKind> node.
- * @param kind ContentKind.
- * @return string.
- */
-string
-dcp::content_kind_to_string (ContentKind kind)
-{
- switch (kind) {
- case FEATURE:
- return "feature";
- case SHORT:
- return "short";
- case TRAILER:
- return "trailer";
- case TEST:
- return "test";
- case TRANSITIONAL:
- return "transitional";
- case RATING:
- return "rating";
- case TEASER:
- return "teaser";
- case POLICY:
- return "policy";
- case PUBLIC_SERVICE_ANNOUNCEMENT:
- return "psa";
- case ADVERTISEMENT:
- return "advertisement";
- }
-
- DCP_ASSERT (false);
-}
-
-/** Convert a string from a <ContentKind> node to a libdcp ContentKind.
- * Reasonably tolerant about varying case.
- * @param kind Content kind string.
- * @return libdcp ContentKind.
- */
-dcp::ContentKind
-dcp::content_kind_from_string (string kind)
-{
- transform (kind.begin(), kind.end(), kind.begin(), ::tolower);
-
- if (kind == "feature") {
- return FEATURE;
- } else if (kind == "short") {
- return SHORT;
- } else if (kind == "trailer") {
- return TRAILER;
- } else if (kind == "test") {
- return TEST;
- } else if (kind == "transitional") {
- return TRANSITIONAL;
- } else if (kind == "rating") {
- return RATING;
- } else if (kind == "teaser") {
- return TEASER;
- } else if (kind == "policy") {
- return POLICY;
- } else if (kind == "psa") {
- return PUBLIC_SERVICE_ANNOUNCEMENT;
- } else if (kind == "advertisement") {
- return ADVERTISEMENT;
- }
-
- DCP_ASSERT (false);
-}
-
/** @param s A string.
* @return true if the string contains only space, newline or tab characters, or is empty.
*/
return true;
}
-/** Set up various bits that the library needs. Should be called one
+/** Set up various bits that the library needs. Should be called once
* by client applications.
+ *
+ * @param tags_directory Path to a copy of the tags directory from the source code;
+ * if none is specified libdcp will look for a tags directory inside the environment
+ * variable LIBDCP_SHARE_PREFIX or the LIBDCP_SHARE_PREFIX #defined during the build.
*/
void
-dcp::init ()
+dcp::init (optional<boost::filesystem::path> tags_directory)
{
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");
+ if (xmlSecCryptoDLLoadLibrary(BAD_CAST "openssl") < 0) {
+ throw MiscError ("unable to load openssl xmlsec-crypto library");
}
#endif
}
OpenSSL_add_all_algorithms();
+
+ asdcp_smpte_dict = &ASDCP::DefaultSMPTEDict();
+
+ if (!tags_directory) {
+ char* prefix = getenv("LIBDCP_SHARE_PREFIX");
+ if (prefix) {
+ tags_directory = boost::filesystem::path(prefix) / "tags";
+ } else {
+ tags_directory = LIBDCP_SHARE_PREFIX "/tags";
+ }
+ }
+
+ load_language_tag_lists (*tags_directory);
}
/** Decode a base64 string. The base64 decode routine in KM_util.cpp
DCP_ASSERT (raw.substr(0, 9) == "urn:uuid:");
return raw.substr (9);
}
+
+string
+dcp::openjpeg_version ()
+{
+ return opj_version ();
+}
+
+string
+dcp::spaces (int n)
+{
+ string s = "";
+ for (int i = 0; i < n; ++i) {
+ s += " ";
+ }
+ return s;
+}
+
+void
+dcp::indent (xmlpp::Element* element, int initial)
+{
+ xmlpp::Node* last = 0;
+ BOOST_FOREACH (xmlpp::Node * n, element->get_children()) {
+ xmlpp::Element* e = dynamic_cast<xmlpp::Element*>(n);
+ if (e) {
+ element->add_child_text_before (e, "\n" + spaces(initial + 2));
+ indent (e, initial + 2);
+ last = n;
+ }
+ }
+ if (last) {
+ element->add_child_text (last, "\n" + spaces(initial));
+ }
+}
+
+/** @return true if the day represented by \ref a is less than or
+ * equal to the one represented by \ref b, ignoring the time parts.
+ */
+bool
+dcp::day_less_than_or_equal (LocalTime a, LocalTime b)
+{
+ if (a.year() != b.year()) {
+ return a.year() < b.year();
+ }
+
+ if (a.month() != b.month()) {
+ return a.month() < b.month();
+ }
+
+ return a.day() <= b.day();
+}
+
+/** @return true if the day represented by \ref a is greater than or
+ * equal to the one represented by \ref b, ignoring the time parts.
+ */
+bool
+dcp::day_greater_than_or_equal (LocalTime a, LocalTime b)
+{
+ if (a.year() != b.year()) {
+ return a.year() > b.year();
+ }
+
+ if (a.month() != b.month()) {
+ return a.month() > b.month();
+ }
+
+ return a.day() >= b.day();
+}
+
+/** Try quite hard to find a string which starts with \ref base and is
+ * not in \ref existing.
+ */
+string
+dcp::unique_string (list<string> existing, string base)
+{
+ int const max_tries = existing.size() + 1;
+ for (int i = 0; i < max_tries; ++i) {
+ string trial = String::compose("%1%2", base, i);
+ if (find(existing.begin(), existing.end(), trial) == existing.end()) {
+ return trial;
+ }
+ }
+
+ DCP_ASSERT (false);
+}
+
+
+ASDCPErrorSuspender::ASDCPErrorSuspender ()
+ : _old (Kumu::DefaultLogSink())
+{
+ _sink = new Kumu::EntryListLogSink(_log);
+ Kumu::SetDefaultLogSink (_sink);
+}
+
+
+ASDCPErrorSuspender::~ASDCPErrorSuspender ()
+{
+ Kumu::SetDefaultLogSink (&_old);
+ delete _sink;
+}
+