diff options
| author | Carl Hetherington <cth@carlh.net> | 2016-07-01 12:22:38 +0100 |
|---|---|---|
| committer | Carl Hetherington <cth@carlh.net> | 2019-12-03 17:01:32 +0100 |
| commit | dfb6aa39a8000fc071dfa0680584dae173535c9a (patch) | |
| tree | ccf64f8dbc81ab13d36f3319475fc0e59f95e2ec /src/asdcp | |
| parent | 8204f14304dd463a42f8540413cf95cf49e1f829 (diff) | |
Move public headers into src/asdcp and install them in a asdcp subdirectory.
Diffstat (limited to 'src/asdcp')
| -rwxr-xr-x | src/asdcp/AS_DCP.h | 1944 | ||||
| -rwxr-xr-x | src/asdcp/KM_error.h | 194 | ||||
| -rwxr-xr-x | src/asdcp/KM_fileio.h | 388 | ||||
| -rwxr-xr-x | src/asdcp/KM_memio.h | 254 | ||||
| -rw-r--r-- | src/asdcp/KM_platform.h | 247 | ||||
| -rwxr-xr-x | src/asdcp/KM_prng.h | 63 | ||||
| -rw-r--r-- | src/asdcp/KM_tai.h | 107 | ||||
| -rwxr-xr-x | src/asdcp/KM_util.h | 556 |
8 files changed, 3753 insertions, 0 deletions
diff --git a/src/asdcp/AS_DCP.h b/src/asdcp/AS_DCP.h new file mode 100755 index 0000000..72b847e --- /dev/null +++ b/src/asdcp/AS_DCP.h @@ -0,0 +1,1944 @@ +/* +Copyright (c) 2003-2014, John Hurst +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. +3. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/*! \file AS_DCP.h + \version $Id$ + \brief AS-DCP library, public interface + +The asdcplib library is a set of file access objects that offer simplified +access to files conforming to the standards published by the SMPTE +D-Cinema Technology Committee 21DC. The file format, labeled AS-DCP, +is described in a series of documents which includes but may not +be be limited to: + + o SMPTE ST 429-2:2011 DCP Operational Constraints + o SMPTE ST 429-3:2006 Sound and Picture Track File + o SMPTE ST 429-4:2006 MXF JPEG 2000 Application + o SMPTE ST 429-5:2009 Timed Text Track File + o SMPTE ST 429-6:2006 MXF Track File Essence Encryption + o SMPTE ST 429-10:2008 Stereoscopic Picture Track File + o SMPTE ST 429-14:2008 Aux Data Track File + o SMPTE ST 330:2004 - UMID + o SMPTE ST 336:2001 - KLV + o SMPTE ST 377:2004 - MXF (old version, required) + o SMPTE ST 377-1:2011 - MXF + o SMPTE ST 377-4:2012 - MXF Multichannel Audio Labeling Framework + o SMPTE ST 390:2011 - MXF OP-Atom + o SMPTE ST 379-1:2009 - MXF Generic Container (GC) + o SMPTE ST 381-1:2005 - MPEG2 picture in GC + o SMPTE ST 422:2006 - JPEG 2000 picture in GC + o SMPTE ST 382:2007 - WAV/PCM sound in GC + o IETF RFC 2104 - HMAC/SHA1 + o NIST FIPS 197 - AES (Rijndael) (via OpenSSL) + + o MXF Interop Track File Specification + o MXF Interop Track File Essence Encryption Specification + o MXF Interop Operational Constraints Specification + - Note: the MXF Interop documents are not formally published. + Contact the asdcplib support address to get copies. + +The following use cases are supported by the library: + + o Write essence to a plaintext or ciphertext AS-DCP file: + o Read essence from a plaintext or ciphertext AS-DCP file: + MPEG2 Video Elementary Stream + JPEG 2000 codestreams + JPEG 2000 stereoscopic codestream pairs + PCM audio streams + SMPTE 429-7 Timed Text XML with font and image resources + Aux Data (frame-wrapped synchronous blob) + Proposed Dolby (TM) Atmos track file + + o Read header metadata from an AS-DCP file + +This project depends upon the following libraries: + - OpenSSL http://www.openssl.org/ + - Expat http://expat.sourceforge.net/ or + Xerces-C http://xerces.apache.org/xerces-c/ + An XML library is not needed if you don't need support for SMPTE ST 429-5:2009. +*/ + +#ifndef _AS_DCP_H_ +#define _AS_DCP_H_ + +#include <asdcp/KM_error.h> +#include <asdcp/KM_fileio.h> +#include <stdio.h> +#include <stdarg.h> +#include <math.h> +#include <iosfwd> +#include <string> +#include <cstring> +#include <list> + +//-------------------------------------------------------------------------------- +// common integer types +// supply your own by defining ASDCP_NO_BASE_TYPES + +#ifndef ASDCP_NO_BASE_TYPES +typedef unsigned char byte_t; +typedef char i8_t; +typedef unsigned char ui8_t; +typedef short i16_t; +typedef unsigned short ui16_t; +typedef int i32_t; +typedef unsigned int ui32_t; +#endif + + +//-------------------------------------------------------------------------------- +// convenience macros + +// Convenience macros for managing return values in predicates +#define ASDCP_SUCCESS(v) (((v) < 0) ? 0 : 1) +#define ASDCP_FAILURE(v) (((v) < 0) ? 1 : 0) + + +// Returns RESULT_PTR if the given argument is NULL. +// See Result_t below for an explanation of RESULT_* symbols. +#define ASDCP_TEST_NULL(p) \ + if ( (p) == 0 ) { \ + return ASDCP::RESULT_PTR; \ + } + +// Returns RESULT_PTR if the given argument is NULL. See Result_t +// below for an explanation of RESULT_* symbols. It then assumes +// that the argument is a pointer to a string and returns +// RESULT_NULL_STR if the first character is '\0'. +// +#define ASDCP_TEST_NULL_STR(p) \ + ASDCP_TEST_NULL(p); \ + if ( (p)[0] == '\0' ) { \ + return ASDCP::RESULT_NULL_STR; \ + } + +// Produces copy constructor boilerplate. Allows convenient private +// declatarion of copy constructors to prevent the compiler from +// silently manufacturing default methods. +#define ASDCP_NO_COPY_CONSTRUCT(T) \ + T(const T&); \ + T& operator=(const T&) + +//-------------------------------------------------------------------------------- +// All library components are defined in the namespace ASDCP +// +namespace ASDCP { + // + // The version number declaration and explanation have moved to ../configure.ac + const char* Version(); + + // UUIDs are passed around as strings of UUIDlen bytes + const ui32_t UUIDlen = 16; + + // Encryption keys are passed around as strings of KeyLen bytes + const ui32_t KeyLen = 16; + + //--------------------------------------------------------------------------------- + // return values + + using Kumu::Result_t; + + using Kumu::RESULT_FALSE; + using Kumu::RESULT_OK; + using Kumu::RESULT_FAIL; + using Kumu::RESULT_PTR; + using Kumu::RESULT_NULL_STR; + using Kumu::RESULT_ALLOC; + using Kumu::RESULT_PARAM; + using Kumu::RESULT_SMALLBUF; + using Kumu::RESULT_INIT; + using Kumu::RESULT_NOT_FOUND; + using Kumu::RESULT_NO_PERM; + using Kumu::RESULT_FILEOPEN; + using Kumu::RESULT_BADSEEK; + using Kumu::RESULT_READFAIL; + using Kumu::RESULT_WRITEFAIL; + using Kumu::RESULT_STATE; + using Kumu::RESULT_ENDOFFILE; + using Kumu::RESULT_CONFIG; + + KM_DECLARE_RESULT(FORMAT, -101, "The file format is not proper OP-Atom/AS-DCP."); + KM_DECLARE_RESULT(RAW_ESS, -102, "Unknown raw essence file type."); + KM_DECLARE_RESULT(RAW_FORMAT, -103, "Raw essence format invalid."); + KM_DECLARE_RESULT(RANGE, -104, "Frame number out of range."); + KM_DECLARE_RESULT(CRYPT_CTX, -105, "AESEncContext required when writing to encrypted file."); + KM_DECLARE_RESULT(LARGE_PTO, -106, "Plaintext offset exceeds frame buffer size."); + KM_DECLARE_RESULT(CAPEXTMEM, -107, "Cannot resize externally allocated memory."); + KM_DECLARE_RESULT(CHECKFAIL, -108, "The check value did not decrypt correctly."); + KM_DECLARE_RESULT(HMACFAIL, -109, "HMAC authentication failure."); + KM_DECLARE_RESULT(HMAC_CTX, -110, "HMAC context required."); + KM_DECLARE_RESULT(CRYPT_INIT, -111, "Error initializing block cipher context."); + KM_DECLARE_RESULT(EMPTY_FB, -112, "Empty frame buffer."); + KM_DECLARE_RESULT(KLV_CODING, -113, "KLV coding error."); + KM_DECLARE_RESULT(SPHASE, -114, "Stereoscopic phase mismatch."); + KM_DECLARE_RESULT(SFORMAT, -115, "Rate mismatch, file may contain stereoscopic essence."); + + //--------------------------------------------------------------------------------- + // file identification + + // The file accessors in this library implement a bounded set of essence types. + // This list will be expanded when support for new types is added to the library. + enum EssenceType_t { + ESS_UNKNOWN, // the file is not a supported AS-DCP of AS-02 essence container + + // + ESS_MPEG2_VES, // the file contains an MPEG-2 video elementary stream + + // d-cinema essence types + ESS_JPEG_2000, // the file contains one or more JPEG 2000 codestreams + ESS_PCM_24b_48k, // the file contains one or more PCM audio pairs + ESS_PCM_24b_96k, // the file contains one or more PCM audio pairs + ESS_TIMED_TEXT, // the file contains an XML timed text document and one or more resources + ESS_JPEG_2000_S, // the file contains one or more JPEG 2000 codestream pairs (stereoscopic) + ESS_DCDATA_UNKNOWN, // the file contains one or more D-Cinema Data bytestreams + ESS_DCDATA_DOLBY_ATMOS, // the file contains one or more DolbyATMOS bytestreams + + // IMF essence types + ESS_AS02_JPEG_2000, // the file contains one or more JPEG 2000 codestreams + ESS_AS02_PCM_24b_48k, // the file contains one or more PCM audio pairs, clip wrapped + ESS_AS02_PCM_24b_96k, // the file contains one or more PCM audio pairs, clip wrapped + ESS_AS02_TIMED_TEXT, // the file contains a TTML document and zero or more resources + + ESS_MAX + }; + + // Determine the type of essence contained in the given MXF file. RESULT_OK + // is returned if the file is successfully opened and contains a valid MXF + // stream. If there is an error, the result code will indicate the reason. + Result_t EssenceType(const std::string& filename, EssenceType_t& type); + + // Determine the type of essence contained in the given raw file. RESULT_OK + // is returned if the file is successfully opened and contains a known + // stream type. If there is an error, the result code will indicate the reason. + Result_t RawEssenceType(const std::string& filename, EssenceType_t& type); + + + //--------------------------------------------------------------------------------- + // base types + + // A simple container for rational numbers. + class Rational + { + public: + i32_t Numerator; + i32_t Denominator; + + Rational() : Numerator(0), Denominator(0) {} + Rational(i32_t n, i32_t d) : Numerator(n), Denominator(d) {} + + inline double Quotient() const { + return (double)Numerator / (double)Denominator; + } + + inline bool operator==(const Rational& rhs) const { + return ( rhs.Numerator == Numerator && rhs.Denominator == Denominator ); + } + + inline bool operator!=(const Rational& rhs) const { + return ( rhs.Numerator != Numerator || rhs.Denominator != Denominator ); + } + + inline bool operator<(const Rational& rhs) { + if ( Numerator < rhs.Numerator ) return true; + if ( Numerator == rhs.Numerator && Denominator < rhs.Denominator ) return true; + return false; + } + + inline bool operator>(const Rational& rhs) { + if ( Numerator > rhs.Numerator ) return true; + if ( Numerator == rhs.Numerator && Denominator > rhs.Denominator ) return true; + return false; + } + }; + + // Encodes a rational number as a string having a single delimiter character between + // numerator and denominator. Retuns the buffer pointer to allow convenient in-line use. + const char* EncodeRational(const Rational&, char* str_buf, ui32_t buf_len, char delimiter = ' '); + + // Decodes a rational number havng a single non-digit delimiter character between + // the numerator and denominator. Returns false if the string does not contain + // the expected syntax. + bool DecodeRational(const char* str_rat, Rational&); + + + // common edit rates, use these instead of hard coded constants + const Rational EditRate_24 = Rational(24,1); + const Rational EditRate_23_98 = Rational(24000,1001); // Not a DCI-compliant value! + const Rational EditRate_48 = Rational(48,1); + const Rational SampleRate_48k = Rational(48000,1); + const Rational SampleRate_96k = Rational(96000,1); + + // Additional frame rates, see ST 428-11, ST 429-13 + // These rates are new and not supported by all systems. Do not assume that + // a package made using one of these rates will work just anywhere! + const Rational EditRate_25 = Rational(25,1); + const Rational EditRate_30 = Rational(30,1); + const Rational EditRate_50 = Rational(50,1); + const Rational EditRate_60 = Rational(60,1); + const Rational EditRate_96 = Rational(96,1); + const Rational EditRate_100 = Rational(100,1); + const Rational EditRate_120 = Rational(120,1); + + // Archival frame rates, see ST 428-21 + // These rates are new and not supported by all systems. Do not assume that + // a package made using one of these rates will work just anywhere! + const Rational EditRate_16 = Rational(16,1); + const Rational EditRate_18 = Rational(200,11); // 18.182 + const Rational EditRate_20 = Rational(20,1); + const Rational EditRate_22 = Rational(240,11); // 21.818 + + + // Non-reference counting container for internal member objects. + // Please do not use this class for any other purpose. + template <class T> + class mem_ptr + { + T* m_p; // the thing we point to + mem_ptr(T&); + + public: + mem_ptr() : m_p(0) {} + mem_ptr(T* p) : m_p(p) {} + ~mem_ptr() { delete m_p; } + + inline T& operator*() const { return *m_p; } + inline T* operator->() const { return m_p; } + inline operator T*()const { return m_p; } + inline const mem_ptr<T>& operator=(T* p) { set(p); return *this; } + inline T* set(T* p) { delete m_p; m_p = p; return m_p; } + inline T* get() const { return m_p; } + inline void release() { m_p = 0; } + inline bool empty() const { return m_p == 0; } + }; + + + //--------------------------------------------------------------------------------- + // WriterInfo class - encapsulates writer identification details used for + // OpenWrite() calls. Replace these values at runtime to identify your product. + // + // MXF files use SMPTE Universal Labels to identify data items. The set of Labels + // in a file is determined by the MXF Operational Pattern and any constraining + // documentation. There are currently two flavors of AS-DCP file in use: MXF Interop + // (AKA JPEG Interop) and SMPTE. The two differ in the values of three labels: + // + // OPAtom + // Interop : 06 0e 2b 34 04 01 01 01 0d 01 02 01 10 00 00 00 + // SMPTE : 06 0e 2b 34 04 01 01 02 0d 01 02 01 10 00 00 00 + // + // EKLV Packet: + // Interop : 06 0e 2b 34 02 04 01 07 0d 01 03 01 02 7e 01 00 + // SMPTE : 06 0e 2b 34 02 04 01 01 0d 01 03 01 02 7e 01 00 + // + // GenericDescriptor/SubDescriptors: + // Interop : 06 0e 2b 34 01 01 01 02 06 01 01 04 06 10 00 00 + // SMPTE : 06 0e 2b 34 01 01 01 09 06 01 01 04 06 10 00 00 + // + // asdcplib will read any (otherwise valid) file which has any combination of the + // above values. When writing files, MXF Interop labels are used by default. To + // write a file containing SMPTE labels, replace the default label set value in + // the WriterInfo before calling OpenWrite() + // + // + enum LabelSet_t + { + LS_MXF_UNKNOWN, + LS_MXF_INTEROP, + LS_MXF_SMPTE, + LS_MAX + }; + + // + struct WriterInfo + { + byte_t ProductUUID[UUIDlen]; + byte_t AssetUUID[UUIDlen]; + byte_t ContextID[UUIDlen]; + byte_t CryptographicKeyID[UUIDlen]; + bool EncryptedEssence; // true if essence data is (or is going to be) encrypted + bool UsesHMAC; // true if HMAC exists or is to be calculated + std::string ProductVersion; + std::string CompanyName; + std::string ProductName; + LabelSet_t LabelSetType; + + WriterInfo() : EncryptedEssence(false), UsesHMAC(false), LabelSetType(LS_MXF_INTEROP) + { + static byte_t default_ProductUUID_Data[UUIDlen] = { + 0x43, 0x05, 0x9a, 0x1d, 0x04, 0x32, 0x41, 0x01, + 0xb8, 0x3f, 0x73, 0x68, 0x15, 0xac, 0xf3, 0x1d }; + + memcpy(ProductUUID, default_ProductUUID_Data, UUIDlen); + memset(AssetUUID, 0, UUIDlen); + memset(ContextID, 0, UUIDlen); + memset(CryptographicKeyID, 0, UUIDlen); + + ProductVersion = "Unreleased "; + ProductVersion += Version(); + CompanyName = "DCI"; + ProductName = "asdcplib"; + } + }; + + // Print WriterInfo to std::ostream + std::ostream& operator << (std::ostream& strm, const WriterInfo& winfo); + // Print WriterInfo to stream, stderr by default. + void WriterInfoDump(const WriterInfo&, FILE* = 0); + + //--------------------------------------------------------------------------------- + // cryptographic support + + // The following classes define interfaces to Rijndael contexts having the following properties: + // o 16 byte key + // o CBC mode with 16 byte block size + const ui32_t CBC_KEY_SIZE = 16; + const ui32_t CBC_BLOCK_SIZE = 16; + const ui32_t HMAC_SIZE = 20; + + // + class AESEncContext + { + class h__AESContext; + mem_ptr<h__AESContext> m_Context; + ASDCP_NO_COPY_CONSTRUCT(AESEncContext); + + public: + AESEncContext(); + ~AESEncContext(); + + // Initializes Rijndael CBC encryption context. + // Returns error if the key argument is NULL. + Result_t InitKey(const byte_t* key); + + // Initializes 16 byte CBC Initialization Vector. This operation may be performed + // any number of times for a given key. + // Returns error if the i_vec argument is NULL. + Result_t SetIVec(const byte_t* i_vec); + Result_t GetIVec(byte_t* i_vec) const; + + // Encrypt a block of data. The block size must be a multiple of CBC_BLOCK_SIZE. + // Returns error if either argument is NULL. + Result_t EncryptBlock(const byte_t* pt_buf, byte_t* ct_buf, ui32_t block_size); + }; + + // + class AESDecContext + { + class h__AESContext; + mem_ptr<h__AESContext> m_Context; + ASDCP_NO_COPY_CONSTRUCT(AESDecContext); + + public: + AESDecContext(); + ~AESDecContext(); + + // Initializes Rijndael CBC decryption context. + // Returns error if the key argument is NULL. + Result_t InitKey(const byte_t* key); + + // Initializes 16 byte CBC Initialization Vector. This operation may be performed + // any number of times for a given key. + // Returns error if the i_vec argument is NULL. + Result_t SetIVec(const byte_t* i_vec); + + // Decrypt a block of data. The block size must be a multiple of CBC_BLOCK_SIZE. + // Returns error if either argument is NULL. + Result_t DecryptBlock(const byte_t* ct_buf, byte_t* pt_buf, ui32_t block_size); + }; + + // + class HMACContext + { + class h__HMACContext; + mem_ptr<h__HMACContext> m_Context; + ASDCP_NO_COPY_CONSTRUCT(HMACContext); + + public: + HMACContext(); + ~HMACContext(); + + // Initializes HMAC context. The key argument must point to a binary + // key that is CBC_KEY_SIZE bytes in length. Returns error if the key + // argument is NULL. + Result_t InitKey(const byte_t* key, LabelSet_t); + + // Reset internal state, allows repeated cycles of Update -> Finalize + void Reset(); + + // Add data to the digest. Returns error if the key argument is NULL or + // if the digest has been finalized. + Result_t Update(const byte_t* buf, ui32_t buf_len); + + // Finalize digest. Returns error if the digest has already been finalized. + Result_t Finalize(); + + // Writes HMAC value to given buffer. buf must point to a writable area of + // memory that is at least HMAC_SIZE bytes in length. Returns error if the + // buf argument is NULL or if the digest has not been finalized. + Result_t GetHMACValue(byte_t* buf) const; + + // Tests the given value against the finalized value in the object. buf must + // point to a readable area of memory that is at least HMAC_SIZE bytes in length. + // Returns error if the buf argument is NULL or if the values do ot match. + Result_t TestHMACValue(const byte_t* buf) const; + }; + + //--------------------------------------------------------------------------------- + // frame buffer base class + // + // The supported essence types are stored using per-frame KLV packetization. The + // following class implements essence-neutral functionality for managing a buffer + // containing a frame of essence. + + class FrameBuffer + { + ASDCP_NO_COPY_CONSTRUCT(FrameBuffer); + + protected: + byte_t* m_Data; // pointer to memory area containing frame data + ui32_t m_Capacity; // size of memory area pointed to by m_Data + bool m_OwnMem; // if false, m_Data points to externally allocated memory + ui32_t m_Size; // size of frame data in memory area pointed to by m_Data + ui32_t m_FrameNumber; // delivery-order frame number + + // It is possible to read raw ciphertext from an encrypted AS-DCP file. + // After reading an encrypted AS-DCP file in raw mode, the frame buffer will + // contain the encrypted source value portion of the Encrypted Triplet, followed + // by the integrity pack, if it exists. + // The buffer will begin with the IV and CheckValue, followed by encrypted essence + // and optional integrity pack + // The SourceLength and PlaintextOffset values from the packet will be held in the + // following variables: + ui32_t m_SourceLength; // plaintext length (delivered plaintext+decrypted ciphertext) + ui32_t m_PlaintextOffset; // offset to first byte of ciphertext + + public: + FrameBuffer(); + virtual ~FrameBuffer(); + + // Instructs the object to use an externally allocated buffer. The external + // buffer will not be cleaned up by the frame buffer when it exits. + // Call with (0,0) to revert to internally allocated buffer. + // Returns error if the buf_addr argument is NULL and buf_size is non-zero. + Result_t SetData(byte_t* buf_addr, ui32_t buf_size); + + // Sets the size of the internally allocate buffer. Returns RESULT_CAPEXTMEM + // if the object is using an externally allocated buffer via SetData(); + // Resets content size to zero. + Result_t Capacity(ui32_t cap); + + // returns the size of the buffer + inline ui32_t Capacity() const { return m_Capacity; } + + // returns a const pointer to the essence data + inline const byte_t* RoData() const { return m_Data; } + + // returns a non-const pointer to the essence data + inline byte_t* Data() { return m_Data; } + + // set the size of the buffer's contents + inline ui32_t Size(ui32_t size) { return m_Size = size; } + + // returns the size of the buffer's contents + inline ui32_t Size() const { return m_Size; } + + // Sets the absolute frame number of this frame in the file in delivery order. + inline void FrameNumber(ui32_t num) { m_FrameNumber = num; } + + // Returns the absolute frame number of this frame in the file in delivery order. + inline ui32_t FrameNumber() const { return m_FrameNumber; } + + // Sets the length of the plaintext essence data + inline void SourceLength(ui32_t len) { m_SourceLength = len; } + + // When this value is 0 (zero), the buffer contains only plaintext. When it is + // non-zero, the buffer contains raw ciphertext and the return value is the length + // of the original plaintext. + inline ui32_t SourceLength() const { return m_SourceLength; } + + // Sets the offset into the buffer at which encrypted data begins + inline void PlaintextOffset(ui32_t ofst) { m_PlaintextOffset = ofst; } + + // Returns offset into buffer of first byte of ciphertext. + inline ui32_t PlaintextOffset() const { return m_PlaintextOffset; } + }; + + //--------------------------------------------------------------------------------- + // Accessors in the MXFReader and MXFWriter classes below return these types to + // provide direct access to MXF metadata structures declared in MXF.h and Metadata.h + + namespace MXF { + // #include<Metadata.h> to use these + class OP1aHeader; + class OPAtomIndexFooter; + class RIP; + }; + + //--------------------------------------------------------------------------------- + // MPEG2 video elementary stream support + + // + namespace MPEG2 + { + // MPEG picture coding type + enum FrameType_t { + FRAME_U = 0x00, // Unknown + FRAME_I = 0x01, // I-Frame + FRAME_P = 0x02, // P-Frame + FRAME_B = 0x03 // B-Frame + }; + + // convert FrameType_t to char + inline char FrameTypeChar(FrameType_t type) + { + switch ( type ) + { + case FRAME_I: return 'I'; + case FRAME_B: return 'B'; + case FRAME_P: return 'P'; + default: return 'U'; + } + } + + // Structure represents the metadata elements in the file header's + // MPEG2VideoDescriptor object. + struct VideoDescriptor + { + Rational EditRate; // + ui32_t FrameRate; // + Rational SampleRate; // + ui8_t FrameLayout; // + ui32_t StoredWidth; // + ui32_t StoredHeight; // + Rational AspectRatio; // + ui32_t ComponentDepth; // + ui32_t HorizontalSubsampling; // + ui32_t VerticalSubsampling; // + ui8_t ColorSiting; // + ui8_t CodedContentType; // + bool LowDelay; // + ui32_t BitRate; // + ui8_t ProfileAndLevel; // + ui32_t ContainerDuration; // + }; + + // Print VideoDescriptor to std::ostream + std::ostream& operator << (std::ostream& strm, const VideoDescriptor& vdesc); + // Print VideoDescriptor to stream, stderr by default. + void VideoDescriptorDump(const VideoDescriptor&, FILE* = 0); + + // A container for MPEG frame data. + class FrameBuffer : public ASDCP::FrameBuffer + { + ASDCP_NO_COPY_CONSTRUCT(FrameBuffer); // TODO: should have copy construct + + protected: + FrameType_t m_FrameType; + ui8_t m_TemporalOffset; + bool m_ClosedGOP; + bool m_GOPStart; + + public: + FrameBuffer() : + m_FrameType(FRAME_U), m_TemporalOffset(0), + m_ClosedGOP(false), m_GOPStart(false) {} + + FrameBuffer(ui32_t size) : + m_FrameType(FRAME_U), m_TemporalOffset(0), + m_ClosedGOP(false), m_GOPStart(false) + { + Capacity(size); + } + + virtual ~FrameBuffer() {} + + // Sets the MPEG frame type of the picture data in the frame buffer. + inline void FrameType(FrameType_t type) { m_FrameType = type; } + + // Returns the MPEG frame type of the picture data in the frame buffer. + inline FrameType_t FrameType() const { return m_FrameType; } + + // Sets the MPEG temporal offset of the picture data in the frame buffer. + inline void TemporalOffset(ui8_t offset) { m_TemporalOffset = offset; } + + // Returns the MPEG temporal offset of the picture data in the frame buffer. + inline ui8_t TemporalOffset() const { return m_TemporalOffset; } + + // Sets the MPEG GOP 'start' attribute for the frame buffer. + inline void GOPStart(bool start) { m_GOPStart = start; } + + // True if the frame in the buffer is the first in the GOP (in transport order) + inline bool GOPStart() const { return m_GOPStart; } + + // Sets the MPEG GOP 'closed' attribute for the frame buffer. + inline void ClosedGOP(bool closed) { m_ClosedGOP = closed; } + + // Returns true if the frame in the buffer is from a closed GOP, false if + // the frame is from an open GOP. Always returns false unless GOPStart() + // returns true. + inline bool ClosedGOP() const { return m_ClosedGOP; } + + // Print object state to stream, include n bytes of frame data if indicated. + // Default stream is stderr. + void Dump(FILE* = 0, ui32_t dump_len = 0) const; + }; + + + // An object which opens and reads an MPEG2 Video Elementary Stream file. The call to + // OpenRead() reads metadata from the file and populates an internal VideoDescriptor object. + // Each subsequent call to ReadFrame() reads exactly one frame from the stream into the + // given FrameBuffer object. + class Parser + { + class h__Parser; + mem_ptr<h__Parser> m_Parser; + ASDCP_NO_COPY_CONSTRUCT(Parser); + + public: + Parser(); + virtual ~Parser(); + + // Opens the stream for reading, parses enough data to provide a complete + // set of stream metadata for the MXFWriter below. + Result_t OpenRead(const std::string& filename) const; + + // Fill a VideoDescriptor struct with the values from the file's header. + // Returns RESULT_INIT if the file is not open. + Result_t FillVideoDescriptor(VideoDescriptor&) const; + + // Rewind the stream to the beginning. + Result_t Reset() const; + + // Reads the next sequential frame in the input file and places it in the + // frame buffer. Fails if the buffer is too small or the stream is empty. + // The frame buffer's PlaintextOffset parameter will be set to the first + // data byte of the first slice. Set this value to zero if you want + // encrypted headers. + Result_t ReadFrame(FrameBuffer&) const; + }; + + // A class which creates and writes MPEG frame data to an AS-DCP format MXF file. + // Not yet implemented + class MXFWriter + { + class h__Writer; + mem_ptr<h__Writer> m_Writer; + ASDCP_NO_COPY_CONSTRUCT(MXFWriter); + + public: + MXFWriter(); + virtual ~MXFWriter(); + + // Warning: direct manipulation of MXF structures can interfere + // with the normal operation of the wrapper. Caveat emptor! + virtual MXF::OP1aHeader& OP1aHeader(); + virtual MXF::OPAtomIndexFooter& OPAtomIndexFooter(); + virtual MXF::RIP& RIP(); + + // Open the file for writing. The file must not exist. Returns error if + // the operation cannot be completed or if nonsensical data is discovered + // in the essence descriptor. + Result_t OpenWrite(const std::string& filename, const WriterInfo&, + const VideoDescriptor&, ui32_t HeaderSize = 16384); + + // Writes a frame of essence to the MXF file. If the optional AESEncContext + // argument is present, the essence is encrypted prior to writing. + // Fails if the file is not open, is finalized, or an operating system + // error occurs. + Result_t WriteFrame(const FrameBuffer&, AESEncContext* = 0, HMACContext* = 0); + + // Closes the MXF file, writing the index and revised header. + Result_t Finalize(); + }; + + // A class which reads MPEG frame data from an AS-DCP format MXF file. + class MXFReader + { + class h__Reader; + mem_ptr<h__Reader> m_Reader; + ASDCP_NO_COPY_CONSTRUCT(MXFReader); + + public: + MXFReader(); + virtual ~MXFReader(); + + // Warning: direct manipulation of MXF structures can interfere + // with the normal operation of the wrapper. Caveat emptor! + virtual MXF::OP1aHeader& OP1aHeader(); + virtual MXF::OPAtomIndexFooter& OPAtomIndexFooter(); + virtual MXF::RIP& RIP(); + + // Open the file for reading. The file must exist. Returns error if the + // operation cannot be completed. + Result_t OpenRead(const std::string& filename) const; + + // Returns RESULT_INIT if the file is not open. + Result_t Close() const; + + // Fill a VideoDescriptor struct with the values from the file's header. + // Returns RESULT_INIT if the file is not open. + Result_t FillVideoDescriptor(VideoDescriptor&) const; + + // Fill a WriterInfo struct with the values from the file's header. + // Returns RESULT_INIT if the file is not open. + Result_t FillWriterInfo(WriterInfo&) const; + + // Reads a frame of essence from the MXF file. If the optional AESEncContext + // argument is present, the essence is decrypted after reading. If the MXF + // file is encrypted and the AESDecContext argument is NULL, the frame buffer + // will contain the ciphertext frame data. If the HMACContext argument is + // not NULL, the HMAC will be calculated (if the file supports it). + // Returns RESULT_INIT if the file is not open, failure if the frame number is + // out of range, or if optional decrypt or HAMC operations fail. + Result_t ReadFrame(ui32_t frame_number, FrameBuffer&, AESDecContext* = 0, HMACContext* = 0) const; + + // Using the index table read from the footer partition, lookup the frame number + // and return the offset into the file at which to read that frame of essence. + // Returns RESULT_INIT if the file is not open, and RESULT_RANGE if the frame number is + // out of range. + Result_t LocateFrame(ui32_t FrameNum, Kumu::fpos_t& streamOffset, i8_t& temporalOffset, i8_t& keyFrameOffset) const; + + // Calculates the first frame in transport order of the GOP in which the requested + // frame is located. Calls ReadFrame() to fetch the frame at the calculated position. + // Returns RESULT_INIT if the file is not open. + Result_t ReadFrameGOPStart(ui32_t frame_number, FrameBuffer&, AESDecContext* = 0, HMACContext* = 0) const; + + // Calculates the first frame in transport order of the GOP in which the requested + // frame is located. Sets key_frame_number to the number of the frame at the calculated position. + // Returns RESULT_INIT if the file is not open. + Result_t FindFrameGOPStart(ui32_t frame_number, ui32_t& key_frame_number) const; + + // Returns the type of the frame at the given position. + // Returns RESULT_INIT if the file is not open or RESULT_RANGE if the index is out of range. + Result_t FrameType(ui32_t frame_number, FrameType_t&) const; + + // Print debugging information to stream + void DumpHeaderMetadata(FILE* = 0) const; + void DumpIndex(FILE* = 0) const; + }; + } // namespace MPEG2 + + //--------------------------------------------------------------------------------- + // + + + + namespace PCM + { + // The default value of the ChannelFormat element of the AudioDescriptor struct + // is CF_NONE. If the value is set to one of the other ChannelFormat_t enum + // values, the file's Wave Audio Descriptor will contain a Channel Assignment + // element. + // + // The channel format should be one of (CF_CFG_1, CF_CFG_2, or CF_CFG_3) for + // SMPTE 429-2 files. It should normally be CF_NONE for JPEG Interop files, but + // the 429-2 may also be used. + // + enum ChannelFormat_t { + CF_NONE, + CF_CFG_1, // 5.1 with optional HI/VI + CF_CFG_2, // 6.1 (5.1 + center surround) with optional HI/VI + CF_CFG_3, // 7.1 (SDDS) with optional HI/VI + CF_CFG_4, // Wild Track Format + CF_CFG_5, // 7.1 DS with optional HI/VI + CF_CFG_6, // ST 377-4 (MCA) labels (see also ASDCP::MXF::decode_mca_string) + CF_MAXIMUM + }; + + struct AudioDescriptor + { + Rational EditRate; // rate of frame wrapping + Rational AudioSamplingRate; // rate of audio sample + ui32_t Locked; // + ui32_t ChannelCount; // number of channels + ui32_t QuantizationBits; // number of bits per single-channel sample + ui32_t BlockAlign; // number of bytes ber sample, all channels + ui32_t AvgBps; // + ui32_t LinkedTrackID; // + ui32_t ContainerDuration; // number of frames + ChannelFormat_t ChannelFormat; // audio channel arrangement + }; + + // Print AudioDescriptor to std::ostream + std::ostream& operator << (std::ostream& strm, const AudioDescriptor& adesc); + // Print debugging information to stream (stderr default) + void AudioDescriptorDump(const AudioDescriptor&, FILE* = 0); + + // Returns size in bytes of a single sample of data described by ADesc + inline ui32_t CalcSampleSize(const AudioDescriptor& ADesc) + { + return (ADesc.QuantizationBits / 8) * ADesc.ChannelCount; + } + + // Returns number of samples per frame of data described by ADesc + inline ui32_t CalcSamplesPerFrame(const AudioDescriptor& ADesc) + { + double tmpd = ADesc.AudioSamplingRate.Quotient() / ADesc.EditRate.Quotient(); + return (ui32_t)ceil(tmpd); + } + + // Returns the size in bytes of a frame of data described by ADesc + inline ui32_t CalcFrameBufferSize(const AudioDescriptor& ADesc) + { + return CalcSampleSize(ADesc) * CalcSamplesPerFrame(ADesc); + } + + // + class FrameBuffer : public ASDCP::FrameBuffer + { + public: + FrameBuffer() {} + FrameBuffer(ui32_t size) { Capacity(size); } + virtual ~FrameBuffer() {} + + // Print debugging information to stream (stderr default) + void Dump(FILE* = 0, ui32_t dump_bytes = 0) const; + }; + + // An object which opens and reads a WAV file. The call to OpenRead() reads metadata from + // the file and populates an internal AudioDescriptor object. Each subsequent call to + // ReadFrame() reads exactly one frame from the stream into the given FrameBuffer object. + // A "frame" is either 2000 or 2002 samples, depending upon the value of PictureRate. + class WAVParser + { + class h__WAVParser; + mem_ptr<h__WAVParser> m_Parser; + ASDCP_NO_COPY_CONSTRUCT(WAVParser); + + public: + WAVParser(); + virtual ~WAVParser(); + + // Opens the stream for reading, parses enough data to provide a complete + // set of stream metadata for the MXFWriter below. PictureRate controls + // ther frame rate for the MXF frame wrapping option. + Result_t OpenRead(const std::string& filename, const Rational& PictureRate) const; + + // Fill an AudioDescriptor struct with the values from the file's header. + // Returns RESULT_INIT if the file is not open. + Result_t FillAudioDescriptor(AudioDescriptor&) const; + + // Rewind the stream to the beginning. + Result_t Reset() const; + + // Reads the next sequential frame in the input file and places it in the + // frame buffer. Fails if the buffer is too small or the stream is empty. + Result_t ReadFrame(FrameBuffer&) const; + }; + + + // + class MXFWriter + { + class h__Writer; + mem_ptr<h__Writer> m_Writer; + ASDCP_NO_COPY_CONSTRUCT(MXFWriter); + + public: + MXFWriter(); + virtual ~MXFWriter(); + + // Warning: direct manipulation of MXF structures can interfere + // with the normal operation of the wrapper. Caveat emptor! + virtual MXF::OP1aHeader& OP1aHeader(); + virtual MXF::OPAtomIndexFooter& OPAtomIndexFooter(); + virtual MXF::RIP& RIP(); + + // Open the file for writing. The file must not exist. Returns error if + // the operation cannot be completed or if nonsensical data is discovered + // in the essence descriptor. + Result_t OpenWrite(const std::string& filename, const WriterInfo&, + const AudioDescriptor&, ui32_t HeaderSize = 16384); + + // Writes a frame of essence to the MXF file. If the optional AESEncContext + // argument is present, the essence is encrypted prior to writing. + // Fails if the file is not open, is finalized, or an operating system + // error occurs. + Result_t WriteFrame(const FrameBuffer&, AESEncContext* = 0, HMACContext* = 0); + + // Closes the MXF file, writing the index and revised header. + Result_t Finalize(); + }; + + // + class MXFReader + { + class h__Reader; + mem_ptr<h__Reader> m_Reader; + ASDCP_NO_COPY_CONSTRUCT(MXFReader); + + public: + MXFReader(); + virtual ~MXFReader(); + + // Warning: direct manipulation of MXF structures can interfere + // with the normal operation of the wrapper. Caveat emptor! + virtual MXF::OP1aHeader& OP1aHeader(); + virtual MXF::OPAtomIndexFooter& OPAtomIndexFooter(); + virtual MXF::RIP& RIP(); + + // Open the file for reading. The file must exist. Returns error if the + // operation cannot be completed. + Result_t OpenRead(const std::string& filename) const; + + // Returns RESULT_INIT if the file is not open. + Result_t Close() const; + + // Fill an AudioDescriptor struct with the values from the file's header. + // Returns RESULT_INIT if the file is not open. + Result_t FillAudioDescriptor(AudioDescriptor&) const; + + // Fill a WriterInfo struct with the values from the file's header. + // Returns RESULT_INIT if the file is not open. + Result_t FillWriterInfo(WriterInfo&) const; + + // Reads a frame of essence from the MXF file. If the optional AESEncContext + // argument is present, the essence is decrypted after reading. If the MXF + // file is encrypted and the AESDecContext argument is NULL, the frame buffer + // will contain the ciphertext frame data. If the HMACContext argument is + // not NULL, the HMAC will be calculated (if the file supports it). + // Returns RESULT_INIT if the file is not open, failure if the frame number is + // out of range, or if optional decrypt or HAMC operations fail. + Result_t ReadFrame(ui32_t frame_number, FrameBuffer&, AESDecContext* = 0, HMACContext* = 0) const; + + // Using the index table read from the footer partition, lookup the frame number + // and return the offset into the file at which to read that frame of essence. + // Returns RESULT_INIT if the file is not open, and RESULT_RANGE if the frame number is + // out of range. + Result_t LocateFrame(ui32_t FrameNum, Kumu::fpos_t& streamOffset, i8_t& temporalOffset, i8_t& keyFrameOffset) const; + + // Print debugging information to stream + void DumpHeaderMetadata(FILE* = 0) const; + void DumpIndex(FILE* = 0) const; + }; + } // namespace PCM + + //--------------------------------------------------------------------------------- + // + namespace JP2K + { + const ui32_t MaxComponents = 3; + const ui32_t MaxPrecincts = 32; // ISO 15444-1 Annex A.6.1 + const ui32_t MaxDefaults = 256; // made up + +#pragma pack(1) + struct ImageComponent_t // ISO 15444-1 Annex A.5.1 + { + ui8_t Ssize; + ui8_t XRsize; + ui8_t YRsize; + }; + + struct CodingStyleDefault_t // ISO 15444-1 Annex A.6.1 + { + ui8_t Scod; + + struct + { + ui8_t ProgressionOrder; + ui8_t NumberOfLayers[sizeof(ui16_t)]; + ui8_t MultiCompTransform; + } SGcod; + + struct + { + ui8_t DecompositionLevels; + ui8_t CodeblockWidth; + ui8_t CodeblockHeight; + ui8_t CodeblockStyle; + ui8_t Transformation; + ui8_t PrecinctSize[MaxPrecincts]; + } SPcod; + }; + + struct QuantizationDefault_t // ISO 15444-1 Annex A.6.4 + { + ui8_t Sqcd; + ui8_t SPqcd[MaxDefaults]; + ui8_t SPqcdLength; + }; +#pragma pack() + + struct PictureDescriptor + { + Rational EditRate; + ui32_t ContainerDuration; + Rational SampleRate; + ui32_t StoredWidth; + ui32_t StoredHeight; + Rational AspectRatio; + ui16_t Rsize; + ui32_t Xsize; + ui32_t Ysize; + ui32_t XOsize; + ui32_t YOsize; + ui32_t XTsize; + ui32_t YTsize; + ui32_t XTOsize; + ui32_t YTOsize; + ui16_t Csize; + ImageComponent_t ImageComponents[MaxComponents]; + CodingStyleDefault_t CodingStyleDefault; + QuantizationDefault_t QuantizationDefault; + }; + + // Print debugging information to std::ostream + std::ostream& operator << (std::ostream& strm, const PictureDescriptor& pdesc); + // Print debugging information to stream (stderr default) + void PictureDescriptorDump(const PictureDescriptor&, FILE* = 0); + + // + class FrameBuffer : public ASDCP::FrameBuffer + { + public: + FrameBuffer() {} + FrameBuffer(ui32_t size) { Capacity(size); } + virtual ~FrameBuffer() {} + + // Print debugging information to stream (stderr default) + void Dump(FILE* = 0, ui32_t dump_bytes = 0) const; + }; + + + // An object which opens and reads a JPEG 2000 codestream file. The file is expected + // to contain exactly one complete frame of picture essence as an unwrapped (raw) + // ISO/IEC 15444 codestream. + class CodestreamParser + { + class h__CodestreamParser; + mem_ptr<h__CodestreamParser> m_Parser; + ASDCP_NO_COPY_CONSTRUCT(CodestreamParser); + + public: + CodestreamParser(); + virtual ~CodestreamParser(); + + // Opens a file for reading, parses enough data to provide a complete + // set of stream metadata for the MXFWriter below. + // The frame buffer's PlaintextOffset parameter will be set to the first + // byte of the data segment. Set this value to zero if you want + // encrypted headers. + Result_t OpenReadFrame(const std::string& filename, FrameBuffer&) const; + + Result_t OpenReadFrame(const unsigned char * data, unsigned int size, FrameBuffer&) const; + + // Fill a PictureDescriptor struct with the values from the file's codestream. + // Returns RESULT_INIT if the file is not open. + Result_t FillPictureDescriptor(PictureDescriptor&) const; + }; + + // Parses the data in the frame buffer to fill in the picture descriptor. Copies + // the offset of the image data into start_of_data. Returns error if the parser fails. + Result_t ParseMetadataIntoDesc(const FrameBuffer&, PictureDescriptor&, byte_t* start_of_data = 0); + + // An object which reads a sequence of files containing JPEG 2000 pictures. + class SequenceParser + { + class h__SequenceParser; + mem_ptr<h__SequenceParser> m_Parser; + ASDCP_NO_COPY_CONSTRUCT(SequenceParser); + + public: + SequenceParser(); + virtual ~SequenceParser(); + + // Opens a directory for reading. The directory is expected to contain one or + // more files, each containing the codestream for exactly one picture. The + // files must be named such that the frames are in temporal order when sorted + // alphabetically by filename. The parser will automatically parse enough data + // from the first file to provide a complete set of stream metadata for the + // MXFWriter below. If the "pedantic" parameter is given and is true, the + // parser will check the metadata for each codestream and fail if a + // mismatch is detected. + Result_t OpenRead(const std::string& filename, bool pedantic = false) const; + + // Opens a file sequence for reading. The sequence is expected to contain one or + // more filenames, each naming a file containing the codestream for exactly one + // picture. The parser will automatically parse enough data + // from the first file to provide a complete set of stream metadata for the + // MXFWriter below. If the "pedantic" parameter is given and is true, the + // parser will check the metadata for each codestream and fail if a + // mismatch is detected. + Result_t OpenRead(const std::list<std::string>& file_list, bool pedantic = false) const; + + // Fill a PictureDescriptor struct with the values from the first file's codestream. + // Returns RESULT_INIT if the directory is not open. + Result_t FillPictureDescriptor(PictureDescriptor&) const; + + // Rewind the directory to the beginning. + Result_t Reset() const; + + // Reads the next sequential frame in the directory and places it in the + // frame buffer. Fails if the buffer is too small or the direcdtory + // contains no more files. + // The frame buffer's PlaintextOffset parameter will be set to the first + // byte of the data segment. Set this value to zero if you want + // encrypted headers. + Result_t ReadFrame(FrameBuffer&) const; + }; + + + // + class MXFWriter + { + class h__Writer; + mem_ptr<h__Writer> m_Writer; + ASDCP_NO_COPY_CONSTRUCT(MXFWriter); + + public: + MXFWriter(); + virtual ~MXFWriter(); + + // Warning: direct manipulation of MXF structures can interfere + // with the normal operation of the wrapper. Caveat emptor! + virtual MXF::OP1aHeader& OP1aHeader(); + virtual MXF::OPAtomIndexFooter& OPAtomIndexFooter(); + virtual MXF::RIP& RIP(); + + // Open the file for writing. The file must not exist unless overwrite is true. Returns error if + // the operation cannot be completed or if nonsensical data is discovered + // in the essence descriptor. + Result_t OpenWrite(const std::string& filename, const WriterInfo&, + const PictureDescriptor&, ui32_t HeaderSize = 16384, bool overwrite = false); + + // Writes a frame of essence to the MXF file. If the optional AESEncContext + // argument is present, the essence is encrypted prior to writing. + // A MD5 hash of the data that we write is written to hash if it is not 0 + // Fails if the file is not open, is finalized, or an operating system + // error occurs. + Result_t WriteFrame(const FrameBuffer&, AESEncContext* = 0, HMACContext* = 0, std::string* hash = 0); + + Result_t FakeWriteFrame(int size); + + // Closes the MXF file, writing the index and revised header. + Result_t Finalize(); + + // Return the current file offset in the MXF file that we are writing + ui64_t Tell() const; + }; + + // + class MXFReader + { + class h__Reader; + mem_ptr<h__Reader> m_Reader; + ASDCP_NO_COPY_CONSTRUCT(MXFReader); + + public: + MXFReader(); + virtual ~MXFReader(); + + // Warning: direct manipulation of MXF structures can interfere + // with the normal operation of the wrapper. Caveat emptor! + virtual MXF::OP1aHeader& OP1aHeader(); + virtual MXF::OPAtomIndexFooter& OPAtomIndexFooter(); + virtual MXF::RIP& RIP(); + + // Open the file for reading. The file must exist. Returns error if the + // operation cannot be completed. + Result_t OpenRead(const std::string& filename) const; + + // Returns RESULT_INIT if the file is not open. + Result_t Close() const; + + // Fill an AudioDescriptor struct with the values from the file's header. + // Returns RESULT_INIT if the file is not open. + Result_t FillPictureDescriptor(PictureDescriptor&) const; + + // Fill a WriterInfo struct with the values from the file's header. + // Returns RESULT_INIT if the file is not open. + Result_t FillWriterInfo(WriterInfo&) const; + + // Reads a frame of essence from the MXF file. If the optional AESEncContext + // argument is present, the essence is decrypted after reading. If the MXF + // file is encrypted and the AESDecContext argument is NULL, the frame buffer + // will contain the ciphertext frame data. If the HMACContext argument is + // not NULL, the HMAC will be calculated (if the file supports it). + // Returns RESULT_INIT if the file is not open, failure if the frame number is + // out of range, or if optional decrypt or HAMC operations fail. + Result_t ReadFrame(ui32_t frame_number, FrameBuffer&, AESDecContext* = 0, HMACContext* = 0) const; + + // Using the index table read from the footer partition, lookup the frame number + // and return the offset into the file at which to read that frame of essence. + // Returns RESULT_INIT if the file is not open, and RESULT_FRAME if the frame number is + // out of range. + Result_t LocateFrame(ui32_t FrameNum, Kumu::fpos_t& streamOffset, i8_t& temporalOffset, i8_t& keyFrameOffset) const; + + // Print debugging information to stream + void DumpHeaderMetadata(FILE* = 0) const; + void DumpIndex(FILE* = 0) const; + }; + + + // Stereoscopic Image support + // + + enum StereoscopicPhase_t + { + SP_LEFT, + SP_RIGHT + }; + + struct SFrameBuffer + { + JP2K::FrameBuffer Left; + JP2K::FrameBuffer Right; + + SFrameBuffer(ui32_t size) { + Left.Capacity(size); + Right.Capacity(size); + } + }; + + class MXFSWriter + { + class h__SWriter; + mem_ptr<h__SWriter> m_Writer; + ASDCP_NO_COPY_CONSTRUCT(MXFSWriter); + + public: + MXFSWriter(); + virtual ~MXFSWriter(); + + // Warning: direct manipulation of MXF structures can interfere + // with the normal operation of the wrapper. Caveat emptor! + virtual MXF::OP1aHeader& OP1aHeader(); + virtual MXF::OPAtomIndexFooter& OPAtomIndexFooter(); + virtual MXF::RIP& RIP(); + + // Open the file for writing. The file must not exist unless overwrite is true. Returns error if + // the operation cannot be completed or if nonsensical data is discovered + // in the essence descriptor. + Result_t OpenWrite(const std::string& filename, const WriterInfo&, + const PictureDescriptor&, ui32_t HeaderSize = 16384, bool overwrite = false); + + // Writes a pair of frames of essence to the MXF file. If the optional AESEncContext + // argument is present, the essence is encrypted prior to writing. + // Fails if the file is not open, is finalized, or an operating system + // error occurs. + Result_t WriteFrame(const SFrameBuffer&, AESEncContext* = 0, HMACContext* = 0); + + // Writes a frame of essence to the MXF file. If the optional AESEncContext + // argument is present, the essence is encrypted prior to writing. + // Fails if the file is not open, is finalized, or an operating system + // error occurs. Frames must be written in the proper phase (L-R-L-R), + // RESULT_SPHASE will be returned if phase is reversed. The first frame + // written must be left eye. + Result_t WriteFrame(const FrameBuffer&, StereoscopicPhase_t phase, + AESEncContext* = 0, HMACContext* = 0, std::string* hash = 0); + + Result_t FakeWriteFrame(int size, StereoscopicPhase_t phase); + + // Closes the MXF file, writing the index and revised header. Returns + // RESULT_SPHASE if WriteFrame was called an odd number of times. + Result_t Finalize(); + + // Return the current file offset in the MXF file that we are writing + ui64_t Tell() const; + }; + + // + class MXFSReader + { + class h__SReader; + mem_ptr<h__SReader> m_Reader; + ASDCP_NO_COPY_CONSTRUCT(MXFSReader); + + public: + MXFSReader(); + virtual ~MXFSReader(); + + // Warning: direct manipulation of MXF structures can interfere + // with the normal operation of the wrapper. Caveat emptor! + virtual MXF::OP1aHeader& OP1aHeader(); + virtual MXF::OPAtomIndexFooter& OPAtomIndexFooter(); + virtual MXF::RIP& RIP(); + + // Open the file for reading. The file must exist. Returns error if the + // operation cannot be completed. + Result_t OpenRead(const std::string& filename) const; + + // Returns RESULT_INIT if the file is not open. + Result_t Close() const; + + // Fill an AudioDescriptor struct with the values from the file's header. + // Returns RESULT_INIT if the file is not open. + Result_t FillPictureDescriptor(PictureDescriptor&) const; + + // Fill a WriterInfo struct with the values from the file's header. + // Returns RESULT_INIT if the file is not open. + Result_t FillWriterInfo(WriterInfo&) const; + + // Reads a pair of frames of essence from the MXF file. If the optional AESEncContext + // argument is present, the essence is decrypted after reading. If the MXF + // file is encrypted and the AESDecContext argument is NULL, the frame buffer + // will contain the ciphertext frame data. If the HMACContext argument is + // not NULL, the HMAC will be calculated (if the file supports it). + // Returns RESULT_INIT if the file is not open, failure if the frame number is + // out of range, or if optional decrypt or HAMC operations fail. + Result_t ReadFrame(ui32_t frame_number, SFrameBuffer&, AESDecContext* = 0, HMACContext* = 0) const; + + // Reads a frame of essence from the MXF file. If the optional AESEncContext + // argument is present, the essence is decrypted after reading. If the MXF + // file is encrypted and the AESDecContext argument is NULL, the frame buffer + // will contain the ciphertext frame data. If the HMACContext argument is + // not NULL, the HMAC will be calculated (if the file supports it). + // Returns RESULT_INIT if the file is not open, failure if the frame number is + // out of range, or if optional decrypt or HAMC operations fail. + Result_t ReadFrame(ui32_t frame_number, StereoscopicPhase_t phase, + FrameBuffer&, AESDecContext* = 0, HMACContext* = 0) const; + + // Using the index table read from the footer partition, lookup the frame number + // and return the offset into the file at which to read that frame of essence. + // Returns RESULT_INIT if the file is not open, and RESULT_FRAME if the frame number is + // out of range. + Result_t LocateFrame(ui32_t FrameNum, Kumu::fpos_t& streamOffset, i8_t& temporalOffset, i8_t& keyFrameOffset) const; + + // Print debugging information to stream + void DumpHeaderMetadata(FILE* = 0) const; + void DumpIndex(FILE* = 0) const; + }; + } // namespace JP2K + + //--------------------------------------------------------------------------------- + // + namespace TimedText + { + enum MIMEType_t { MT_BIN, MT_PNG, MT_OPENTYPE }; + + struct TimedTextResourceDescriptor + { + byte_t ResourceID[UUIDlen]; + MIMEType_t Type; + + TimedTextResourceDescriptor() : Type(MT_BIN) {} + }; + + typedef std::list<TimedTextResourceDescriptor> ResourceList_t; + + struct TimedTextDescriptor + { + Rational EditRate; // + ui32_t ContainerDuration; + byte_t AssetID[UUIDlen]; + std::string NamespaceName; + std::string EncodingName; + ResourceList_t ResourceList; + + TimedTextDescriptor() : ContainerDuration(0), EncodingName("UTF-8") {} // D-Cinema format is always UTF-8 + }; + + // Print debugging information to std::ostream + std::ostream& operator << (std::ostream& strm, const TimedTextDescriptor& tinfo); + // Print debugging information to stream (stderr default) + void DescriptorDump(const TimedTextDescriptor&, FILE* = 0); + + // + class FrameBuffer : public ASDCP::FrameBuffer + { + ASDCP_NO_COPY_CONSTRUCT(FrameBuffer); // TODO: should have copy construct + + protected: + byte_t m_AssetID[UUIDlen]; + std::string m_MIMEType; + + public: + FrameBuffer() { memset(m_AssetID, 0, UUIDlen); } + FrameBuffer(ui32_t size) { Capacity(size); memset(m_AssetID, 0, UUIDlen); } + virtual ~FrameBuffer() {} + + inline const byte_t* AssetID() const { return m_AssetID; } + inline void AssetID(const byte_t* buf) { memcpy(m_AssetID, buf, UUIDlen); } + inline const char* MIMEType() const { return m_MIMEType.c_str(); } + inline void MIMEType(const std::string& s) { m_MIMEType = s; } + + // Print debugging information to stream (stderr default) + void Dump(FILE* = 0, ui32_t dump_bytes = 0) const; + }; + + // An abstract base for a lookup service that returns the resource data + // identified by the given ancillary resource id. + // + class IResourceResolver + { + public: + virtual ~IResourceResolver() {} + virtual Result_t ResolveRID(const byte_t* uuid, FrameBuffer&) const = 0; // return data for RID + }; + + // Resolves resource references by testing the named directory for file names containing + // the respective UUID. + // + class LocalFilenameResolver : public ASDCP::TimedText::IResourceResolver + { + std::string m_Dirname; + ASDCP_NO_COPY_CONSTRUCT(LocalFilenameResolver); + + public: + LocalFilenameResolver(); + virtual ~LocalFilenameResolver(); + Result_t OpenRead(const std::string& dirname); + Result_t ResolveRID(const byte_t* uuid, FrameBuffer& FrameBuf) const; + }; + + // + class DCSubtitleParser + { + class h__SubtitleParser; + mem_ptr<h__SubtitleParser> m_Parser; + ASDCP_NO_COPY_CONSTRUCT(DCSubtitleParser); + + public: + DCSubtitleParser(); + virtual ~DCSubtitleParser(); + + // Opens an XML file for reading, parses data to provide a complete + // set of stream metadata for the MXFWriter below. + Result_t OpenRead(const std::string& filename) const; + + // Parses an XML document to provide a complete set of stream metadata + // for the MXFWriter below. The optional filename argument is used to + // initialize the default resource resolver (see ReadAncillaryResource). + Result_t OpenRead(const std::string& xml_doc, const std::string& filename) const; + + // Fill a TimedTextDescriptor struct with the values from the file's contents. + // Returns RESULT_INIT if the file is not open. + Result_t FillTimedTextDescriptor(TimedTextDescriptor&) const; + + // Reads the complete Timed Text Resource into the given string. + Result_t ReadTimedTextResource(std::string&) const; + + // Reads the Ancillary Resource having the given ID. Fails if the buffer + // is too small or the resource does not exist. The optional Resolver + // argument can be provided which will be used to retrieve the resource + // having a particulat UUID. If a Resolver is not supplied, the default + // internal resolver will return the contents of the file having the UUID + // as the filename. The filename must exist in the same directory as the + // XML file opened with OpenRead(). + Result_t ReadAncillaryResource(const byte_t* uuid, FrameBuffer&, + const IResourceResolver* Resolver = 0) const; + }; + + // + class MXFWriter + { + class h__Writer; + mem_ptr<h__Writer> m_Writer; + ASDCP_NO_COPY_CONSTRUCT(MXFWriter); + + public: + MXFWriter(); + virtual ~MXFWriter(); + + // Warning: direct manipulation of MXF structures can interfere + // with the normal operation of the wrapper. Caveat emptor! + virtual MXF::OP1aHeader& OP1aHeader(); + virtual MXF::OPAtomIndexFooter& OPAtomIndexFooter(); + virtual MXF::RIP& RIP(); + + // Open the file for writing. The file must not exist. Returns error if + // the operation cannot be completed or if nonsensical data is discovered + // in the essence descriptor. + Result_t OpenWrite(const std::string& filename, const WriterInfo&, + const TimedTextDescriptor&, ui32_t HeaderSize = 16384); + + // Writes the Timed-Text Resource to the MXF file. The file must be UTF-8 + // encoded. If the optional AESEncContext argument is present, the essence + // is encrypted prior to writing. Fails if the file is not open, is finalized, + // or an operating system error occurs. + // This method may only be called once, and it must be called before any + // call to WriteAncillaryResource(). RESULT_STATE will be returned if these + // conditions are not met. + Result_t WriteTimedTextResource(const std::string& XMLDoc, AESEncContext* = 0, HMACContext* = 0); + + // Writes an Ancillary Resource to the MXF file. If the optional AESEncContext + // argument is present, the essence is encrypted prior to writing. + // Fails if the file is not open, is finalized, or an operating system + // error occurs. RESULT_STATE will be returned if the method is called before + // WriteTimedTextResource() + Result_t WriteAncillaryResource(const FrameBuffer&, AESEncContext* = 0, HMACContext* = 0); + + // Closes the MXF file, writing the index and revised header. + Result_t Finalize(); + }; + + // + class MXFReader + { + class h__Reader; + mem_ptr<h__Reader> m_Reader; + ASDCP_NO_COPY_CONSTRUCT(MXFReader); + + public: + MXFReader(); + virtual ~MXFReader(); + + // Warning: direct manipulation of MXF structures can interfere + // with the normal operation of the wrapper. Caveat emptor! + virtual MXF::OP1aHeader& OP1aHeader(); + virtual MXF::OPAtomIndexFooter& OPAtomIndexFooter(); + virtual MXF::RIP& RIP(); + + // Open the file for reading. The file must exist. Returns error if the + // operation cannot be completed. + Result_t OpenRead(const std::string& filename) const; + + // Returns RESULT_INIT if the file is not open. + Result_t Close() const; + + // Fill a TimedTextDescriptor struct with the values from the file's header. + // Returns RESULT_INIT if the file is not open. + Result_t FillTimedTextDescriptor(TimedTextDescriptor&) const; + + // Fill a WriterInfo struct with the values from the file's header. + // Returns RESULT_INIT if the file is not open. + Result_t FillWriterInfo(WriterInfo&) const; + + // Reads the complete Timed Text Resource into the given string. Fails if the resource + // is encrypted and AESDecContext is NULL (use the following method to retrieve the + // raw ciphertet block). + Result_t ReadTimedTextResource(std::string&, AESDecContext* = 0, HMACContext* = 0) const; + + // Reads the complete Timed Text Resource from the MXF file. If the optional AESEncContext + // argument is present, the resource is decrypted after reading. If the MXF + // file is encrypted and the AESDecContext argument is NULL, the frame buffer + // will contain the ciphertext frame data. If the HMACContext argument is + // not NULL, the HMAC will be calculated (if the file supports it). + // Returns RESULT_INIT if the file is not open, failure if the frame number is + // out of range, or if optional decrypt or HAMC operations fail. + Result_t ReadTimedTextResource(FrameBuffer&, AESDecContext* = 0, HMACContext* = 0) const; + + // Reads the timed-text resource having the given UUID from the MXF file. If the + // optional AESEncContext argument is present, the resource is decrypted after + // reading. If the MXF file is encrypted and the AESDecContext argument is NULL, + // the frame buffer will contain the ciphertext frame data. If the HMACContext + // argument is not NULL, the HMAC will be calculated (if the file supports it). + // Returns RESULT_INIT if the file is not open, failure if the frame number is + // out of range, or if optional decrypt or HAMC operations fail. + Result_t ReadAncillaryResource(const byte_t* uuid, FrameBuffer&, AESDecContext* = 0, HMACContext* = 0) const; + + // Print debugging information to stream + void DumpHeaderMetadata(FILE* = 0) const; + void DumpIndex(FILE* = 0) const; + }; + } // namespace TimedText + + //--------------------------------------------------------------------------------- + // + namespace DCData + { + struct DCDataDescriptor + { + Rational EditRate; // Sample rate + ui32_t ContainerDuration; // number of frames + byte_t AssetID[UUIDlen]; // The UUID for the DCData track + byte_t DataEssenceCoding[UUIDlen]; // The coding for the data carried + }; + + // Print DCDataDescriptor to std::ostream + std::ostream& operator << (std::ostream& strm, const DCDataDescriptor& ddesc); + // Print debugging information to stream (stderr default) + void DCDataDescriptorDump(const DCDataDescriptor&, FILE* = 0); + + // + class FrameBuffer : public ASDCP::FrameBuffer + { + public: + FrameBuffer() {} + FrameBuffer(ui32_t size) { Capacity(size); } + virtual ~FrameBuffer() {} + + // Print debugging information to stream (stderr default) + void Dump(FILE* = 0, ui32_t dump_bytes = 0) const; + }; + + // An object which opens and reads a DC Data file. The file is expected + // to contain exactly one complete frame of DC data essence as an unwrapped (raw) + // byte stream. + class BytestreamParser + { + class h__BytestreamParser; + mem_ptr<h__BytestreamParser> m_Parser; + ASDCP_NO_COPY_CONSTRUCT(BytestreamParser); + + public: + BytestreamParser(); + virtual ~BytestreamParser(); + + // Opens a file for reading, parses enough data to provide a complete + // set of stream metadata for the MXFWriter below. + // The frame buffer's PlaintextOffset parameter will be set to the first + // byte of the data segment. Set this value to zero if you want + // encrypted headers. + Result_t OpenReadFrame(const std::string& filename, FrameBuffer&) const; + + // Fill a DCDataDescriptor struct with the values from the file's bytestream. + // Returns RESULT_INIT if the file is not open. + Result_t FillDCDataDescriptor(DCDataDescriptor&) const; + }; + + // An object which reads a sequence of files containing DC Data. + class SequenceParser + { + class h__SequenceParser; + mem_ptr<h__SequenceParser> m_Parser; + ASDCP_NO_COPY_CONSTRUCT(SequenceParser); + + public: + SequenceParser(); + virtual ~SequenceParser(); + + // Opens a directory for reading. The directory is expected to contain one or + // more files, each containing the bytestream for exactly one frame. The files + // must be named such that the frames are in temporal order when sorted + // alphabetically by filename. + Result_t OpenRead(const std::string& filename) const; + + // Opens a file sequence for reading. The sequence is expected to contain one or + // more filenames, each naming a file containing the bytestream for exactly one + // frame. + Result_t OpenRead(const std::list<std::string>& file_list) const; + + // Fill a DCDataDescriptor struct with default values. + // Returns RESULT_INIT if the directory is not open. + Result_t FillDCDataDescriptor(DCDataDescriptor&) const; + + // Rewind the directory to the beginning. + Result_t Reset() const; + + // Reads the next sequential frame in the directory and places it in the + // frame buffer. Fails if the buffer is too small or the direcdtory + // contains no more files. + // The frame buffer's PlaintextOffset parameter will be set to the first + // byte of the data segment. Set this value to zero if you want + // encrypted headers. + Result_t ReadFrame(FrameBuffer&) const; + }; + + // + class MXFWriter + { + class h__Writer; + mem_ptr<h__Writer> m_Writer; + ASDCP_NO_COPY_CONSTRUCT(MXFWriter); + + public: + MXFWriter(); + virtual ~MXFWriter(); + + // Warning: direct manipulation of MXF structures can interfere + // with the normal operation of the wrapper. Caveat emptor! + virtual MXF::OP1aHeader& OP1aHeader(); + virtual MXF::OPAtomIndexFooter& OPAtomIndexFooter(); + virtual MXF::RIP& RIP(); + + // Open the file for writing. The file must not exist. Returns error if + // the operation cannot be completed or if nonsensical data is discovered + // in the essence descriptor. + Result_t OpenWrite(const std::string& filename, const WriterInfo&, + const DCDataDescriptor&, ui32_t HeaderSize = 16384); + + // Writes a frame of essence to the MXF file. If the optional AESEncContext + // argument is present, the essence is encrypted prior to writing. + // Fails if the file is not open, is finalized, or an operating system + // error occurs. + Result_t WriteFrame(const FrameBuffer&, AESEncContext* = 0, HMACContext* = 0); + + // Closes the MXF file, writing the index and revised header. + Result_t Finalize(); + }; + + // + class MXFReader + { + class h__Reader; + mem_ptr<h__Reader> m_Reader; + ASDCP_NO_COPY_CONSTRUCT(MXFReader); + + public: + MXFReader(); + virtual ~MXFReader(); + + // Warning: direct manipulation of MXF structures can interfere + // with the normal operation of the wrapper. Caveat emptor! + virtual MXF::OP1aHeader& OP1aHeader(); + virtual MXF::OPAtomIndexFooter& OPAtomIndexFooter(); + virtual MXF::RIP& RIP(); + + // Open the file for reading. The file must exist. Returns error if the + // operation cannot be completed. + Result_t OpenRead(const std::string& filename) const; + + // Returns RESULT_INIT if the file is not open. + Result_t Close() const; + + // Fill a DCDataDescriptor struct with the values from the file's header. + // Returns RESULT_INIT if the file is not open. + Result_t FillDCDataDescriptor(DCDataDescriptor&) const; + + // Fill a WriterInfo struct with the values from the file's header. + // Returns RESULT_INIT if the file is not open. + Result_t FillWriterInfo(WriterInfo&) const; + + // Reads a frame of essence from the MXF file. If the optional AESEncContext + // argument is present, the essence is decrypted after reading. If the MXF + // file is encrypted and the AESDecContext argument is NULL, the frame buffer + // will contain the ciphertext frame data. If the HMACContext argument is + // not NULL, the HMAC will be calculated (if the file supports it). + // Returns RESULT_INIT if the file is not open, failure if the frame number is + // out of range, or if optional decrypt or HAMC operations fail. + Result_t ReadFrame(ui32_t frame_number, FrameBuffer&, AESDecContext* = 0, HMACContext* = 0) const; + + // Using the index table read from the footer partition, lookup the frame number + // and return the offset into the file at which to read that frame of essence. + // Returns RESULT_INIT if the file is not open, and RESULT_RANGE if the frame number is + // out of range. + Result_t LocateFrame(ui32_t FrameNum, Kumu::fpos_t& streamOffset, i8_t& temporalOffset, i8_t& keyFrameOffset) const; + + // Print debugging information to stream + void DumpHeaderMetadata(FILE* = 0) const; + void DumpIndex(FILE* = 0) const; + }; + + } // namespace DCData + + //--------------------------------------------------------------------------------- + // + namespace ATMOS + { + struct AtmosDescriptor : public DCData::DCDataDescriptor + { + ui32_t FirstFrame; // Frame number of the frame to align with the FFOA of the picture track + ui16_t MaxChannelCount; // Max number of channels in bitstream + ui16_t MaxObjectCount; // Max number of objects in bitstream + byte_t AtmosID[UUIDlen]; // UUID of Atmos Project + ui8_t AtmosVersion; // ATMOS Coder Version used to create bitstream + }; + + // Print AtmosDescriptor to std::ostream + std::ostream& operator << (std::ostream& strm, const AtmosDescriptor& adesc); + // Print debugging information to stream (stderr default) + void AtmosDescriptorDump(const AtmosDescriptor&, FILE* = 0); + // Determine if a file is a raw atmos file + bool IsDolbyAtmos(const std::string& filename); + + // + class MXFWriter + { + + class h__Writer; + mem_ptr<h__Writer> m_Writer; + ASDCP_NO_COPY_CONSTRUCT(MXFWriter); + + public: + MXFWriter(); + virtual ~MXFWriter(); + + // Warning: direct manipulation of MXF structures can interfere + // with the normal operation of the wrapper. Caveat emptor! + virtual MXF::OP1aHeader& OP1aHeader(); + virtual MXF::OPAtomIndexFooter& OPAtomIndexFooter(); + virtual MXF::RIP& RIP(); + + // Open the file for writing. The file must not exist. Returns error if + // the operation cannot be completed or if nonsensical data is discovered + // in the essence descriptor. + Result_t OpenWrite(const std::string& filename, const WriterInfo&, + const AtmosDescriptor&, ui32_t HeaderSize = 16384); + + // Writes a frame of essence to the MXF file. If the optional AESEncContext + // argument is present, the essence is encrypted prior to writing. + // Fails if the file is not open, is finalized, or an operating system + // error occurs. + Result_t WriteFrame(const DCData::FrameBuffer&, AESEncContext* = 0, HMACContext* = 0); + + // Closes the MXF file, writing the index and revised header. + Result_t Finalize(); + }; + + // + class MXFReader + { + class h__Reader; + mem_ptr<h__Reader> m_Reader; + ASDCP_NO_COPY_CONSTRUCT(MXFReader); + + public: + MXFReader(); + virtual ~MXFReader(); + + // Warning: direct manipulation of MXF structures can interfere + // with the normal operation of the wrapper. Caveat emptor! + virtual MXF::OP1aHeader& OP1aHeader(); + virtual MXF::OPAtomIndexFooter& OPAtomIndexFooter(); + virtual MXF::RIP& RIP(); + + // Open the file for reading. The file must exist. Returns error if the + // operation cannot be completed. + Result_t OpenRead(const std::string& filename) const; + + // Returns RESULT_INIT if the file is not open. + Result_t Close() const; + + // Fill an AtmosDescriptor struct with the values from the file's header. + // Returns RESULT_INIT if the file is not open. + Result_t FillAtmosDescriptor(AtmosDescriptor&) const; + + // Fill a WriterInfo struct with the values from the file's header. + // Returns RESULT_INIT if the file is not open. + Result_t FillWriterInfo(WriterInfo&) const; + + // Reads a frame of essence from the MXF file. If the optional AESEncContext + // argument is present, the essence is decrypted after reading. If the MXF + // file is encrypted and the AESDecContext argument is NULL, the frame buffer + // will contain the ciphertext frame data. If the HMACContext argument is + // not NULL, the HMAC will be calculated (if the file supports it). + // Returns RESULT_INIT if the file is not open, failure if the frame number is + // out of range, or if optional decrypt or HAMC operations fail. + Result_t ReadFrame(ui32_t frame_number, DCData::FrameBuffer&, AESDecContext* = 0, HMACContext* = 0) const; + + // Using the index table read from the footer partition, lookup the frame number + // and return the offset into the file at which to read that frame of essence. + // Returns RESULT_INIT if the file is not open, and RESULT_RANGE if the frame number is + // out of range. + Result_t LocateFrame(ui32_t FrameNum, Kumu::fpos_t& streamOffset, i8_t& temporalOffset, i8_t& keyFrameOffset) const; + + // Print debugging information to stream + void DumpHeaderMetadata(FILE* = 0) const; + void DumpIndex(FILE* = 0) const; + }; + + } // namespace ATMOS + + + +} // namespace ASDCP + + +#endif // _AS_DCP_H_ + +// +// end AS_DCP.h +// diff --git a/src/asdcp/KM_error.h b/src/asdcp/KM_error.h new file mode 100755 index 0000000..8270cc0 --- /dev/null +++ b/src/asdcp/KM_error.h @@ -0,0 +1,194 @@ +/* +Copyright (c) 2004-2015, John Hurst +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. +3. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + /*! \file KM_error.h + \version $Id$ + \brief error reporting support + */ + + + +#ifndef _KM_ERROR_H_ +#define _KM_ERROR_H_ + +#include <string> + +#define KM_DECLARE_RESULT(sym, i, l) const Result_t RESULT_##sym = Result_t(i, #sym, l); + +namespace Kumu +{ + // Result code container. Both a signed integer and a text string are stored in the object. + // When defining your own codes your choice of integer values is mostly unconstrained, but pay + // attention to the numbering in the other libraries that use Kumu. Values between -99 and 99 + // are reserved for Kumu. + + class Result_t + { + int value; + std::string label, symbol, message; + Result_t(); + + public: + // Return registered Result_t for the given "value" code. + static const Result_t& Find(int value); + + // Unregister the Result_t matching the given "value" code. Returns + // RESULT_FALSE if "value" does not match a registered Result_t. + // Returns RESULT_FAIL if ( value < -99 || value > 99 ) (Kumu core + // codes may not be deleted). + static Result_t Delete(int value); + + // Iteration through registered result codes, not thread safe. + // Get accepts contiguous values from 0 to End() - 1. + static unsigned int End(); + static const Result_t& Get(unsigned int); + + Result_t(int v, const std::string& s, const std::string& l); + Result_t(const Result_t& rhs); + const Result_t& operator=(const Result_t& rhs); + ~Result_t(); + + const Result_t operator()(const std::string& message) const; + const Result_t operator()(const int& line, const char* filename) const; + const Result_t operator()(const std::string& message, const int& line, const char* filename) const; + + inline bool operator==(const Result_t& rhs) const { return value == rhs.value; } + inline bool operator!=(const Result_t& rhs) const { return value != rhs.value; } + inline bool Success() const { return ! ( value < 0 ); } + inline bool Failure() const { return ( value < 0 ); } + + inline int Value() const { return value; } + inline operator int() const { return value; } + inline const char* Label() const { return label.c_str(); } + inline operator const char*() const { return label.c_str(); } + inline const char* Symbol() const { return symbol.c_str(); } + inline const char* Message() const { return message.c_str(); } + }; + + KM_DECLARE_RESULT(FALSE, 1, "Successful but not true."); + KM_DECLARE_RESULT(OK, 0, "Success."); + KM_DECLARE_RESULT(FAIL, -1, "An undefined error was detected."); + KM_DECLARE_RESULT(PTR, -2, "An unexpected NULL pointer was given."); + KM_DECLARE_RESULT(NULL_STR, -3, "An unexpected empty string was given."); + KM_DECLARE_RESULT(ALLOC, -4, "Error allocating memory."); + KM_DECLARE_RESULT(PARAM, -5, "Invalid parameter."); + KM_DECLARE_RESULT(NOTIMPL, -6, "Unimplemented Feature."); + KM_DECLARE_RESULT(SMALLBUF, -7, "The given buffer is too small."); + KM_DECLARE_RESULT(INIT, -8, "The object is not yet initialized."); + KM_DECLARE_RESULT(NOT_FOUND, -9, "The requested file does not exist on the system."); + KM_DECLARE_RESULT(NO_PERM, -10, "Insufficient privilege exists to perform the operation."); + KM_DECLARE_RESULT(STATE, -11, "Object state error."); + KM_DECLARE_RESULT(CONFIG, -12, "Invalid configuration option detected."); + KM_DECLARE_RESULT(FILEOPEN, -13, "File open failure."); + KM_DECLARE_RESULT(BADSEEK, -14, "An invalid file location was requested."); + KM_DECLARE_RESULT(READFAIL, -15, "File read error."); + KM_DECLARE_RESULT(WRITEFAIL, -16, "File write error."); + KM_DECLARE_RESULT(ENDOFFILE, -17, "Attempt to read past end of file."); + KM_DECLARE_RESULT(FILEEXISTS, -18, "Filename already exists."); + KM_DECLARE_RESULT(NOTAFILE, -19, "Filename not found."); + KM_DECLARE_RESULT(UNKNOWN, -20, "Unknown result code."); + KM_DECLARE_RESULT(DIR_CREATE, -21, "Unable to create directory."); + KM_DECLARE_RESULT(NOT_EMPTY, -22, "Unable to delete non-empty directory."); + // 23-100 are reserved + +} // namespace Kumu + +//-------------------------------------------------------------------------------- +// convenience macros + +// Convenience macros for managing return values in predicates +# define KM_SUCCESS(v) (((v) < 0) ? 0 : 1) +# define KM_FAILURE(v) (((v) < 0) ? 1 : 0) + + +// Returns RESULT_PTR if the given argument is NULL. +// See Result_t above for an explanation of RESULT_* symbols. +# define KM_TEST_NULL(p) \ + if ( (p) == 0 ) { \ + return Kumu::RESULT_PTR(__LINE__, __FILE__); \ + } + +// Returns RESULT_PTR if the given argument is NULL. See Result_t +// in WaimeaCore for an explanation of RESULT_* symbols. It then assumes +// that the argument is a pointer to a string and returns +// RESULT_NULL_STR if the first character is '\0'. +// +# define KM_TEST_NULL_STR(p) \ + KM_TEST_NULL(p); \ + if ( (p)[0] == '\0' ) { \ + return Kumu::RESULT_NULL_STR(__LINE__, __FILE__); \ + } + +// RESULT_STATE is ambiguous. Use these everywhere it is assigned to provide some context +#define KM_RESULT_STATE_TEST_IMPLICIT() \ + if ( result == Kumu::RESULT_STATE ) { \ + Kumu::DefaultLogSink().Error("RESULT_STATE RETURNED at %s (%d)\n", __FILE__, __LINE__); \ + } + +#define KM_RESULT_STATE_TEST_THIS(_this__r_) \ + if ( _this__r_ == Kumu::RESULT_STATE ) { \ + Kumu::DefaultLogSink().Error("RESULT_STATE RETURNED at %s (%d)\n", __FILE__, __LINE__); \ + } + +#define KM_RESULT_STATE_HERE() \ + Kumu::DefaultLogSink().Error("RESULT_STATE RETURNED at %s (%d)\n", __FILE__, __LINE__); + + + +namespace Kumu +{ + // simple tracing mechanism + class DTrace_t + { + DTrace_t(); + + protected: + const char* m_Label; + Result_t* m_Watch; + int m_Line; + const char* m_File; + int m_Sequence; + + public: + DTrace_t(const char* Label, Result_t* Watch, int Line, const char* File); + ~DTrace_t(); + }; +} + +#ifdef KM_TRACE +#define WDTRACE(l) DTrace_t __wl__Trace__((l), 0, __LINE__, __FILE__) +#define WDTRACER(l,r) DTrace_t __wl__Trace__((l), &(r), __LINE__, __FILE__) +#else +#define WDTRACE(l) +#define WDTRACER(l,r) +#endif + + +#endif // _KM_ERROR_H_ + +// +// end KM_error.h +// diff --git a/src/asdcp/KM_fileio.h b/src/asdcp/KM_fileio.h new file mode 100755 index 0000000..1fab636 --- /dev/null +++ b/src/asdcp/KM_fileio.h @@ -0,0 +1,388 @@ +/* +Copyright (c) 2004-2014, John Hurst +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. +3. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + /*! \file KM_fileio.h + \version $Id$ + \brief portable file i/o + */ + +#ifndef _KM_FILEIO_H_ +#define _KM_FILEIO_H_ + +#include <asdcp/KM_util.h> +#include <string> +#include <boost/filesystem.hpp> +#include <openssl/md5.h> + +#ifdef KM_WIN32 +# include <io.h> +#else +# include <dirent.h> +# include <unistd.h> +# include <time.h> +# include <sys/types.h> +#include <regex.h> +#endif + +#include <sys/stat.h> + + + +namespace Kumu +{ + class DirScanner + { + public: + DirScanner(); + Result_t Open(const std::string&); + Result_t GetNext(char*); + Result_t Close(); + private: + boost::filesystem::directory_iterator _iterator; + }; + + + // + enum DirectoryEntryType_t { + DET_FILE, + DET_DIR, + DET_DEV, + DET_LINK + }; + + // + class DirScannerEx + { + std::string m_Dirname; +#ifdef KM_WIN32 + __int64 m_Handle; + struct _finddatai64_t m_FileInfo; +#else + DIR* m_Handle; +#endif + + KM_NO_COPY_CONSTRUCT(DirScannerEx); + + public: + + DirScannerEx(); + ~DirScannerEx() { Close(); } + + Result_t Open(const std::string& dirname); + Result_t Close(); + + + inline Result_t GetNext(std::string& next_item_name) { + DirectoryEntryType_t ft; + return GetNext(next_item_name, ft); + } + + Result_t GetNext(std::string& next_item_name, DirectoryEntryType_t& next_item_type); + }; + +#ifdef KM_WIN32 + typedef __int64 fsize_t; + typedef __int64 fpos_t; + typedef HANDLE FileHandle; + + enum SeekPos_t { + SP_BEGIN = FILE_BEGIN, + SP_POS = FILE_CURRENT, + SP_END = FILE_END + }; +#else + typedef off_t fsize_t; + typedef off_t fpos_t; + typedef int FileHandle; + const FileHandle INVALID_HANDLE_VALUE = -1L; + + enum SeekPos_t { + SP_BEGIN = SEEK_SET, + SP_POS = SEEK_CUR, + SP_END = SEEK_END + }; +#endif + + // +#ifndef KM_SMALL_FILES_OK + template <bool sizecheck> void compile_time_size_checker(); + template <> inline void compile_time_size_checker<false>() {} + // + // READ THIS if your compiler is complaining about a previously declared implementation of + // compile_time_size_checker(). For example, GCC 4.0.1 looks like this: + // + // error: 'void Kumu::compile_time_size_checker() [with bool sizecheck = false]' previously declared here + // + // This is happening because the equality being tested below is false. The reason for this + // will depend on your OS, but on Linux it is probably because you have not used -D_FILE_OFFSET_BITS=64 + // Adding this magic macro to your CFLAGS will get you going again. If you are on a system that + // does not support 64-bit files, you can disable this check by using -DKM_SMALL_FILES_OK. You + // will then of course be limited to file sizes < 4GB. + // + template <> inline void compile_time_size_checker<sizeof(Kumu::fsize_t)==sizeof(ui64_t)>() {} +#endif + // + + const ui32_t Kilobyte = 1024; + const ui32_t Megabyte = Kilobyte * Kilobyte; + const ui32_t Gigabyte = Megabyte * Kilobyte; + + const ui32_t MaxFilePath = Kilobyte; + + + //------------------------------------------------------------------------------------------ + // Path Manglers + //------------------------------------------------------------------------------------------ + + // types + typedef std::list<std::string> PathCompList_t; // a list of path components + typedef std::list<std::string> PathList_t; // a list of paths + + // tests + bool PathExists(const std::string& Path); // true if the path exists in the filesystem + bool PathIsFile(const std::string& Path); // true if the path exists in the filesystem and is a file + bool PathIsDirectory(const std::string& Path); // true if the path exists in the filesystem and is a directory + fsize_t FileSize(const std::string& Path); // returns the size of a regular file, 0 for a directory or device + std::string PathCwd(); + bool PathsAreEquivalent(const std::string& lhs, const std::string& rhs); // true if paths point to the same filesystem entry + + // Returns free space and total space available for the given path + Result_t FreeSpaceForPath(const std::string& path, Kumu::fsize_t& free_space, Kumu::fsize_t& total_space); + + // split and reassemble paths as lists of path components + PathCompList_t& PathToComponents(const std::string& Path, PathCompList_t& CList, char separator = '/'); // removes '//' + std::string ComponentsToPath(const PathCompList_t& CList, char separator = '/'); + std::string ComponentsToAbsolutePath(const PathCompList_t& CList, char separator = '/'); // add separator to the front + bool PathHasComponents(const std::string& Path, char separator = '/'); // true if paths starts with separator + + bool PathIsAbsolute(const std::string& Path, char separator = '/'); // true if path begins with separator + std::string PathMakeAbsolute(const std::string& Path, char separator = '/'); // compute position of relative path using getcwd() + std::string PathMakeLocal(const std::string& Path, const std::string& Parent); // remove Parent from front of Path, if it exists + std::string PathMakeCanonical(const std::string& Path, char separator = '/'); // remove '.' and '..' + bool PathResolveLinks(const std::string& link_path, std::string& resolved_path, char separator = '/'); + + // common operations + std::string PathBasename(const std::string& Path, char separator = '/'); // returns right-most path element (list back()) + std::string PathDirname(const std::string& Path, char separator = '/'); // returns everything but the right-most element + std::string PathGetExtension(const std::string& Path); // returns everything in the right-most element following the right-most '.' + std::string PathSetExtension(const std::string& Path, const std::string& Extension); // empty extension removes '.' as well + + std::string PathJoin(const std::string& Path1, const std::string& Path2, char separator = '/'); + std::string PathJoin(const std::string& Path1, const std::string& Path2, const std::string& Path3, char separator = '/'); + std::string PathJoin(const std::string& Path1, const std::string& Path2, + const std::string& Path3, const std::string& Path4, char separator = '/'); + + + //------------------------------------------------------------------------------------------ + // Path Search + //------------------------------------------------------------------------------------------ + + // An interface for a path matching function, used by FindInPath() and FindInPaths() below + // + class IPathMatch + { + public: + virtual ~IPathMatch() {} + virtual bool Match(const std::string& s) const = 0; + }; + + // matches any pathname + class PathMatchAny : public IPathMatch + { + public: + virtual ~PathMatchAny() {} + inline bool Match(const std::string&) const { return true; } + }; + +#ifndef KM_WIN32 + // matches pathnames using a regular expression + class PathMatchRegex : public IPathMatch + { + regex_t m_regex; + PathMatchRegex(); + const PathMatchRegex& operator=(const PathMatchRegex&); + + public: + PathMatchRegex(const std::string& Pattern); + PathMatchRegex(const PathMatchRegex&); + virtual ~PathMatchRegex(); + bool Match(const std::string& s) const; + }; + + // matches pathnames using a Bourne shell glob expression + class PathMatchGlob : public IPathMatch + { + regex_t m_regex; + PathMatchGlob(); + const PathMatchGlob& operator=(const PathMatchGlob&); + + public: + PathMatchGlob(const std::string& Pattern); + PathMatchGlob(const PathMatchGlob&); + virtual ~PathMatchGlob(); + bool Match(const std::string& s) const; + }; +#endif /* !KM_WIN32 */ + + // Search all paths in SearchPaths for filenames matching Pattern (no directories are returned). + // Put results in FoundPaths. Returns after first find if one_shot is true. + PathList_t& FindInPath(const IPathMatch& Pattern, const std::string& SearchDir, + PathList_t& FoundPaths, bool one_shot = false, char separator = '/'); + + PathList_t& FindInPaths(const IPathMatch& Pattern, const PathList_t& SearchPaths, + PathList_t& FoundPaths, bool one_shot = false, char separator = '/'); + + std::string GetExecutablePath(const std::string& default_path); + + //------------------------------------------------------------------------------------------ + // Directory Manipulation + //------------------------------------------------------------------------------------------ + + // Create a directory, creates intermediate directories as necessary + Result_t CreateDirectoriesInPath(const std::string& Path); + + // Delete a file (fails if the path points to a directory) + Result_t DeleteFile(const std::string& filename); + + // Recursively remove a file or directory + Result_t DeletePath(const std::string& pathname); + + // Remove the path only if it is a directory that is empty. + Result_t DeleteDirectoryIfEmpty(const std::string& path); + + //------------------------------------------------------------------------------------------ + // File I/O Wrappers + //------------------------------------------------------------------------------------------ + + // Instant IO for strings + // + // Reads an entire file into a string. + Result_t ReadFileIntoString(const std::string& filename, std::string& outString, ui32_t max_size = 8 * Megabyte); + + // Writes a string to a file, overwrites the existing file if present. + Result_t WriteStringIntoFile(const std::string& filename, const std::string& inString); + + // Instant IO for archivable objects + // + // Unarchives a file into an object + Result_t ReadFileIntoObject(const std::string& Filename, IArchive& Object, ui32_t max_size = 8 * Kumu::Megabyte); + + // Archives an object into a file + Result_t WriteObjectIntoFile(const IArchive& Object, const std::string& Filename); + + // Instant IO for memory buffers + // + // Unarchives a file into a buffer + Result_t ReadFileIntoBuffer(const std::string& Filename, Kumu::ByteString& Buffer, + ui32_t max_size = 8 * Kumu::Megabyte); + + // Archives a buffer into a file + Result_t WriteBufferIntoFile(const Kumu::ByteString& Buffer, const std::string& Filename); + + + //------------------------------------------------------------------------------------------ + // File I/O + //------------------------------------------------------------------------------------------ + + // + class FileReader + { + KM_NO_COPY_CONSTRUCT(FileReader); + + protected: + std::string m_Filename; + FileHandle m_Handle; + + public: + FileReader() : m_Handle(INVALID_HANDLE_VALUE) {} + virtual ~FileReader() { Close(); } + + Result_t OpenRead(const std::string&) const; // open the file for reading + Result_t Close() const; // close the file + fsize_t Size() const; // returns the file's current size + Result_t Seek(Kumu::fpos_t = 0, SeekPos_t = SP_BEGIN) const; // move the file pointer + Result_t Tell(Kumu::fpos_t* pos) const; // report the file pointer's location + Result_t Read(byte_t*, ui32_t, ui32_t* = 0) const; // read a buffer of data + + inline Kumu::fpos_t Tell() const // report the file pointer's location + { + Kumu::fpos_t tmp_pos; + Tell(&tmp_pos); + return tmp_pos; + } + + inline bool IsOpen() { // returns true if the file is open + return (m_Handle != INVALID_HANDLE_VALUE); + } + }; + + // + class FileWriter : public FileReader + { + class h__iovec; + mem_ptr<h__iovec> m_IOVec; + KM_NO_COPY_CONSTRUCT(FileWriter); + bool m_Hashing; + MD5_CTX m_MD5Context; + + public: + FileWriter(); + virtual ~FileWriter(); + + Result_t OpenWrite(const std::string&); // open a new file, overwrites existing + Result_t OpenModify(const std::string&); // open a file for read/write + + // this part of the interface takes advantage of the iovec structure on + // platforms that support it. For each call to Writev(const byte_t*, ui32_t, ui32_t*), + // the given buffer is added to an internal iovec struct. All items on the list + // are written to disk by a call to Writev(); + Result_t Writev(const byte_t*, ui32_t); // queue buffer for "gather" write + Result_t Writev(ui32_t* = 0); // write all queued buffers + + // if you call this while there are unwritten items on the iovec list, + // the iovec list will be written to disk before the given buffer,as though + // you had called Writev() first. + Result_t Write(const byte_t*, ui32_t, ui32_t* = 0); // write buffer to disk + + void StartHashing(); + void MaybeHash(void const *, int); + std::string StopHashing(); + }; + + Result_t CreateDirectoriesInPath(const std::string& Path); + Result_t FreeSpaceForPath(const std::string& path, Kumu::fsize_t& free_space, Kumu::fsize_t& total_space); + Result_t DeleteFile(const std::string& filename); + Result_t DeletePath(const std::string& pathname); + +} // namespace Kumu + + +#endif // _KM_FILEIO_H_ + + +// +// end KM_fileio.h +// diff --git a/src/asdcp/KM_memio.h b/src/asdcp/KM_memio.h new file mode 100755 index 0000000..557b868 --- /dev/null +++ b/src/asdcp/KM_memio.h @@ -0,0 +1,254 @@ +/* +Copyright (c) 2006-2011, John Hurst +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. +3. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + /*! \file KM_memio.h + \version $Id$ + \brief abstraction for byte-oriented conversion of integers and objects + */ + +#ifndef _KM_MEMIO_H_ +#define _KM_MEMIO_H_ + +#include <asdcp/KM_platform.h> +#include <string> +#include <cstring> + +namespace Kumu +{ + class ByteString; + + // + class MemIOWriter + { + KM_NO_COPY_CONSTRUCT(MemIOWriter); + MemIOWriter(); + + protected: + byte_t* m_p; + ui32_t m_capacity; + ui32_t m_size; + + public: + MemIOWriter(byte_t* p, ui32_t c) : m_p(p), m_capacity(c), m_size(0) { + assert(m_p); assert(m_capacity); + } + + MemIOWriter(ByteString* Buf); + ~MemIOWriter() {} + + inline void Reset() { m_size = 0; } + inline byte_t* Data() { return m_p; } + inline const byte_t* RoData() const { return m_p; } + inline byte_t* CurrentData() { return m_p + m_size; } + inline ui32_t Length() const { return m_size; } + inline ui32_t Remainder() const { return m_capacity - m_size; } + + inline bool AddOffset(ui32_t offset) { + if ( ( m_size + offset ) > m_capacity ) + return false; + + m_size += offset; + return true; + + } + + inline bool WriteRaw(const byte_t* p, ui32_t buf_len) { + if ( ( m_size + buf_len ) > m_capacity ) + return false; + + memcpy(m_p + m_size, p, buf_len); + m_size += buf_len; + return true; + } + + bool WriteBER(ui64_t i, ui32_t ber_len); + + inline bool WriteUi8(ui8_t i) { + if ( ( m_size + 1 ) > m_capacity ) + return false; + + *(m_p + m_size) = i; + m_size++; + return true; + } + + inline bool WriteUi16BE(ui16_t i) { + if ( ( m_size + sizeof(ui16_t) ) > m_capacity ) + return false; + + i2p<ui16_t>(KM_i16_BE(i), m_p + m_size); + m_size += sizeof(ui16_t); + return true; + } + + inline bool WriteUi32BE(ui32_t i) { + if ( ( m_size + sizeof(ui32_t) ) > m_capacity ) + return false; + + i2p<ui32_t>(KM_i32_BE(i), m_p + m_size); + m_size += sizeof(ui32_t); + return true; + } + + inline bool WriteUi64BE(ui64_t i) { + if ( ( m_size + sizeof(ui64_t) ) > m_capacity ) + return false; + + i2p<ui64_t>(KM_i64_BE(i), m_p + m_size); + m_size += sizeof(ui64_t); + return true; + } + + inline bool WriteString(const std::string& str) { + ui32_t len = static_cast<ui32_t>(str.length()); + if ( ! WriteUi32BE(len) ) return false; + if ( ! WriteRaw((const byte_t*)str.c_str(), len) ) return false; + return true; + } + }; + + // + class MemIOReader + { + KM_NO_COPY_CONSTRUCT(MemIOReader); + MemIOReader(); + + protected: + const byte_t* m_p; + ui32_t m_capacity; + ui32_t m_size; // this is sort of a misnomer, when we are reading it measures offset + + public: + MemIOReader(const byte_t* p, ui32_t c) : + m_p(p), m_capacity(c), m_size(0) { + assert(m_p); assert(m_capacity); + } + + MemIOReader(const ByteString* Buf); + ~MemIOReader() {} + + inline void Reset() { m_size = 0; } + inline const byte_t* Data() const { return m_p; } + inline const byte_t* CurrentData() const { return m_p + m_size; } + inline ui32_t Offset() const { return m_size; } + inline ui32_t Remainder() const { return m_capacity - m_size; } + + inline bool SkipOffset(ui32_t offset) { + if ( ( m_size + offset ) > m_capacity ) + return false; + + m_size += offset; + return true; + } + + inline bool ReadRaw(byte_t* p, ui32_t buf_len) { + if ( ( m_size + buf_len ) > m_capacity ) + return false; + + memcpy(p, m_p + m_size, buf_len); + m_size += buf_len; + return true; + } + + bool ReadBER(ui64_t* i, ui32_t* ber_len); + + inline bool ReadUi8(ui8_t* i) { + assert(i); + if ( ( m_size + 1 ) > m_capacity ) + return false; + + *i = *(m_p + m_size); + m_size++; + return true; + } + + inline bool ReadUi16BE(ui16_t* i) { + assert(i); + if ( ( m_size + sizeof(ui16_t) ) > m_capacity ) + return false; + + *i = KM_i16_BE(cp2i<ui16_t>(m_p + m_size)); + m_size += sizeof(ui16_t); + return true; + } + + inline bool ReadUi32BE(ui32_t* i) { + assert(i); + if ( ( m_size + sizeof(ui32_t) ) > m_capacity ) + return false; + + *i = KM_i32_BE(cp2i<ui32_t>(m_p + m_size)); + m_size += sizeof(ui32_t); + return true; + } + + inline bool ReadUi64BE(ui64_t* i) { + assert(i); + if ( ( m_size + sizeof(ui64_t) ) > m_capacity ) + return false; + + *i = KM_i64_BE(cp2i<ui64_t>(m_p + m_size)); + m_size += sizeof(ui64_t); + return true; + } + + inline bool ReadString(std::string& str) + { + ui32_t str_length = 0; + if ( ! ReadUi32BE(&str_length) ) return false; + + if ( str_length > 0 ) + { + if ( ( m_size + str_length ) > m_capacity ) return false; + str.assign((const char*)CurrentData(), str_length); + if ( ! SkipOffset(str_length) ) return false; + } + + return true; + } + }; + + // + inline bool + UnarchiveString(MemIOReader& Reader, std::string& str) { + return Reader.ReadString(str); + } + + // + inline bool + ArchiveString(MemIOWriter& Writer, const std::string& str) + { + return Writer.WriteString(str); + } + + +} // namespace Kumu + +#endif // _KM_MEMIO_H_ + +// +// end KM_memio.h +// diff --git a/src/asdcp/KM_platform.h b/src/asdcp/KM_platform.h new file mode 100644 index 0000000..defcd8a --- /dev/null +++ b/src/asdcp/KM_platform.h @@ -0,0 +1,247 @@ +/* +Copyright (c) 2004-2015, John Hurst +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. +3. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + /*! \file KM_platform.h + \version $Id$ + \brief platform portability + */ + +#ifndef _KM_PLATFORM_H_ +# define _KM_PLATFORM_H_ + +#if defined(__APPLE__) && defined(__MACH__) +# define KM_MACOSX +# ifdef __BIG_ENDIAN__ +# define KM_BIG_ENDIAN +# endif +# endif + +# ifdef KM_WIN32 +# define WIN32_LEAN_AND_MEAN +# define VC_EXTRALEAN +# include <windows.h> +# include <stdlib.h> +# include <stdio.h> +# include <stdarg.h> +# pragma warning(disable:4786) // Ignore "identifer > 255 characters" warning + +typedef unsigned __int64 ui64_t; +typedef __int64 i64_t; +# define i64_C(c) (i64_t)(c) +# define ui64_C(c) (ui64_t)(c) +# define snprintf _snprintf +# define vsnprintf _vsnprintf + +# else // KM_WIN32 +typedef unsigned long long ui64_t; +typedef long long i64_t; +# define i64_C(c) c##LL +# define ui64_C(c) c##ULL + +# endif // KM_WIN32 + +# include <stdio.h> +# include <assert.h> +# include <stdlib.h> +# include <limits.h> + +typedef unsigned char byte_t; +typedef char i8_t; +typedef unsigned char ui8_t; +typedef short i16_t; +typedef unsigned short ui16_t; +typedef int i32_t; +typedef unsigned int ui32_t; + + +namespace Kumu +{ + inline ui16_t Swap2(ui16_t i) + { + return ( (i << 8) | (( i & 0xff00) >> 8) ); + } + + inline ui32_t Swap4(ui32_t i) + { + return + ( (i & 0x000000ffUL) << 24 ) | + ( (i & 0xff000000UL) >> 24 ) | + ( (i & 0x0000ff00UL) << 8 ) | + ( (i & 0x00ff0000UL) >> 8 ); + } + + inline ui64_t Swap8(ui64_t i) + { + return + ( (i & ui64_C(0x00000000000000FF)) << 56 ) | + ( (i & ui64_C(0xFF00000000000000)) >> 56 ) | + ( (i & ui64_C(0x000000000000FF00)) << 40 ) | + ( (i & ui64_C(0x00FF000000000000)) >> 40 ) | + ( (i & ui64_C(0x0000000000FF0000)) << 24 ) | + ( (i & ui64_C(0x0000FF0000000000)) >> 24 ) | + ( (i & ui64_C(0x00000000FF000000)) << 8 ) | + ( (i & ui64_C(0x000000FF00000000)) >> 8 ); + } + + // + template<class T> + inline T xmin(T lhs, T rhs) { + return (lhs < rhs) ? lhs : rhs; + } + + // + template<class T> + inline T xmax(T lhs, T rhs) { + return (lhs > rhs) ? lhs : rhs; + } + + // + template<class T> + inline T xclamp(T v, T l, T h) { + if ( v < l ) { return l; } + if ( v > h ) { return h; } + return v; + } + + // + template<class T> + inline T xabs(T n) { + if ( n < 0 ) { return -n; } + return n; + } + + // read an integer from byte-structured storage + template<class T> + inline T cp2i(const byte_t* p) { return *(T*)p; } + + // write an integer to byte-structured storage + template<class T> + inline void i2p(T i, byte_t* p) { *(T*)p = i; } + + +# ifdef KM_BIG_ENDIAN +# define KM_i16_LE(i) Kumu::Swap2(i) +# define KM_i32_LE(i) Kumu::Swap4(i) +# define KM_i64_LE(i) Kumu::Swap8(i) +# define KM_i16_BE(i) (i) +# define KM_i32_BE(i) (i) +# define KM_i64_BE(i) (i) +# else +# define KM_i16_LE(i) (i) +# define KM_i32_LE(i) (i) +# define KM_i64_LE(i) (i) +# define KM_i16_BE(i) Kumu::Swap2(i) +# define KM_i32_BE(i) Kumu::Swap4(i) +# define KM_i64_BE(i) Kumu::Swap8(i) +# endif // KM_BIG_ENDIAN + + // A non-reference counting, auto-delete container for internal + // member object pointers. + template <class T> + class mem_ptr + { + mem_ptr(T&); + + protected: + T* m_p; // the thing we point to + + public: + mem_ptr() : m_p(0) {} + mem_ptr(T* p) : m_p(p) {} + ~mem_ptr() { delete m_p; } + + inline T& operator*() const { return *m_p; } + inline T* operator->() const { assert(m_p!=0); return m_p; } + inline operator T*()const { return m_p; } + inline const mem_ptr<T>& operator=(T* p) { this->set(p); return *this; } + inline T* set(T* p) { delete m_p; m_p = p; return m_p; } + inline T* get() const { return m_p; } + inline void release() { m_p = 0; } + inline bool empty() const { return m_p == 0; } + }; + +} // namespace Kumu + +// Produces copy constructor boilerplate. Allows convenient private +// declatarion of copy constructors to prevent the compiler from +// silently manufacturing default methods. +# define KM_NO_COPY_CONSTRUCT(T) \ + T(const T&); \ + T& operator=(const T&) + +/* +// Example + class foo + { + KM_NO_COPY_CONSTRUCT(foo); // accessing private mthods will cause compile time error + public: + // ... + }; +*/ + +// Produces copy constructor boilerplate. Implements +// copy and assignment, see example below +# define KM_EXPLICIT_COPY_CONSTRUCT(T) \ + T(const T&); \ + const T& operator=(const T&) + +# define KM_EXPLICIT_COPY_CONSTRUCT_IMPL_START(N, T) \ + void T##_copy_impl(N::T& lhs, const N::T& rhs) \ + { + +#define KM_COPY_ITEM(I) lhs.I = rhs.I; + +# define KM_EXPLICIT_COPY_CONSTRUCT_IMPL_END(N, T) \ + } \ + N::T::T(const N::T& rhs) { T##_copy_impl(*this, rhs); } \ + const N::T& N::T::operator=(const N::T& rhs) { T##_copy_impl(*this, rhs); return *this; } + +/* +// Example +namespace bar { + class foo + { + public: + std::string param_a; + int param_b; + + KM_EXPLICIT_COPY_CONSTRUCT(foo); + // ... + }; +} + +// +KM_EXPLICIT_COPY_CONSTRUCT_IMPL_START(bar, foo) +KM_COPY_ITEM(param_a) +KM_COPY_ITEM(param_b) +KM_EXPLICIT_COPY_CONSTRUCT_IMPL_END(bar, foo) +*/ + +#endif // _KM_PLATFORM_H_ + +// +// KM_platform.h +// diff --git a/src/asdcp/KM_prng.h b/src/asdcp/KM_prng.h new file mode 100755 index 0000000..e2bc3f1 --- /dev/null +++ b/src/asdcp/KM_prng.h @@ -0,0 +1,63 @@ +/* +Copyright (c) 2006-2009, John Hurst +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. +3. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + /*! \file KM_prng.h + \version $Id$ + \brief Fortuna pseudo-random number generator + */ + +#ifndef _KM_PRNG_H_ +#define _KM_PRNG_H_ + +#include <asdcp/KM_util.h> + +namespace Kumu +{ + class FortunaRNG + { + KM_NO_COPY_CONSTRUCT(FortunaRNG); + + public: + FortunaRNG(); + ~FortunaRNG(); + const byte_t* FillRandom(byte_t* buf, ui32_t len); + const byte_t* FillRandom(ByteString&); + void Reset(); + }; + + + // key_len must be <= 64 (larger values will be truncated) + void Gen_FIPS_186_Value(const byte_t* key_in, ui32_t key_len, byte_t* buf, ui32_t buf_len); + +} // namespace Kumu + + + +#endif // _KM_PRNG_H_ + +// +// end KM_prng.h +// diff --git a/src/asdcp/KM_tai.h b/src/asdcp/KM_tai.h new file mode 100644 index 0000000..a126760 --- /dev/null +++ b/src/asdcp/KM_tai.h @@ -0,0 +1,107 @@ +/* + +THIS IS A SUBSET OF THE FULL LIBTAI. CHANGES HAVE BEEN MADE TO SUIT +LIBKUMU STYLE AND TYPE CONVENTIONS. ALL BUGS BELONG TO JOHN HURST. +THE FOLLOWING IS FOR ATTRIBUTION, THANK YOU MR. BERNSTEIN FOR WRITING +AND DISTRIBUTING SUCH GREAT SOFTWARE: + +libtai 0.60, alpha. +19981013 +Copyright 1998 +D. J. Bernstein, djb@pobox.com +http://pobox.com/~djb/libtai.html + + +libtai is a library for storing and manipulating dates and times. + +libtai supports two time scales: (1) TAI64, covering a few hundred +billion years with 1-second precision; (2) TAI64NA, covering the same +period with 1-attosecond precision. Both scales are defined in terms of +TAI, the current international real time standard. + +libtai provides an internal format for TAI64, struct tai, designed for +fast time manipulations. The tai_pack() and tai_unpack() routines +convert between struct tai and a portable 8-byte TAI64 storage format. +libtai provides similar internal and external formats for TAI64NA. + +libtai provides struct caldate to store dates in year-month-day form. It +can convert struct caldate, under the Gregorian calendar, to a modified +Julian day number for easy date arithmetic. + +libtai provides struct caltime to store calendar dates and times along +with UTC offsets. It can convert from struct tai to struct caltime in +UTC, accounting for leap seconds, for accurate date and time display. It +can also convert back from struct caltime to struct tai for user input. +Its overall UTC-to-TAI conversion speed is 100x better than the usual +UNIX mktime() implementation. + +This version of libtai requires a UNIX system with gettimeofday(). It +will be easy to port to other operating systems with compilers +supporting 64-bit arithmetic. + +The libtai source code is in the public domain. + +*/ + + /*! \file KM_tai.h + \version $Id$ + \brief portable time functions + */ + +#ifndef _KUMU_TAI_H_ +#define _KUMU_TAI_H_ + +#include <asdcp/KM_platform.h> + +// +namespace Kumu +{ + namespace TAI + { + class caltime; + + // + struct tai + { + ui64_t x; + inline void add_seconds(i32_t s) { x += s; } + inline void add_minutes(i32_t m) { x += m * 60; } + inline void add_hours(i32_t h) { x += h * 3600; } + inline void add_days(i32_t d) { x += d * 86400; } + void now(); + + const tai& operator=(const caltime& rhs); + }; + + // + struct caldate + { + i32_t year; + i32_t month; + i32_t day; + }; + + // + class caltime + { + public: + caldate date; + i32_t hour; + i32_t minute; + i32_t second; + i32_t offset; + + const caltime& operator=(const tai& rhs); + }; + + + } // namespace TAI + +} // namespace Kumu + + +#endif // _KUMU_TAI_H_ + +// +// end KM_tai.h +// diff --git a/src/asdcp/KM_util.h b/src/asdcp/KM_util.h new file mode 100755 index 0000000..409d66e --- /dev/null +++ b/src/asdcp/KM_util.h @@ -0,0 +1,556 @@ +/* +Copyright (c) 2005-2015, John Hurst +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. +3. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + /*! \file KM_util.h + \version $Id$ + \brief Utility functions + */ + +#ifndef _KM_UTIL_H_ +#define _KM_UTIL_H_ + +#include <asdcp/KM_memio.h> +#include <asdcp/KM_error.h> +#include <asdcp/KM_tai.h> +#include <string.h> +#include <list> + +namespace Kumu +{ + extern bool cth_test; + + // The version number declaration and explanation are in ../configure.ac + const char* Version(); + + // a class that represents the string form of a value + template <class T, int SIZE = 16> + class IntPrinter : public std::string + { + KM_NO_COPY_CONSTRUCT(IntPrinter); + IntPrinter(); + + protected: + const char* m_format; + char m_strbuf[SIZE]; + + public: + IntPrinter(const char* format, T value) { + assert(format); + m_format = format; + snprintf(m_strbuf, SIZE, m_format, value); + } + + inline operator const char*() { return m_strbuf; } + inline const char* c_str() { return m_strbuf; } + inline const char* set_value(T value) { + snprintf(m_strbuf, SIZE, m_format, value); + return m_strbuf; + } + }; + + struct i8Printer : public IntPrinter<i8_t> { + i8Printer(i8_t value) : IntPrinter<i8_t>("%hd", value) {} + }; + + struct ui8Printer : public IntPrinter<ui8_t> { + ui8Printer(ui8_t value) : IntPrinter<ui8_t>("%hu", value) {} + }; + + struct i16Printer : public IntPrinter<i16_t> { + i16Printer(i16_t value) : IntPrinter<i16_t>("%hd", value) {} + }; + + struct ui16Printer : public IntPrinter<ui16_t> { + ui16Printer(ui16_t value) : IntPrinter<ui16_t>("%hu", value) {} + }; + + struct i32Printer : public IntPrinter<i32_t> { + i32Printer(i32_t value) : IntPrinter<i32_t>("%d", value) {} + }; + + struct ui32Printer : public IntPrinter<ui32_t> { + ui32Printer(ui32_t value) : IntPrinter<ui32_t>("%u", value) {} + }; + +#ifdef KM_WIN32 + struct i64Printer : public IntPrinter<i64_t, 32> { + i64Printer(i64_t value) : IntPrinter<i64_t, 32>("%I64d", value) {} + }; + + struct ui64Printer : public IntPrinter<ui64_t, 32> { + ui64Printer(ui64_t value) : IntPrinter<ui64_t, 32>("%I64u", value) {} + }; +#else + struct i64Printer : public IntPrinter<i64_t, 32> { + i64Printer(i64_t value) : IntPrinter<i64_t, 32>("%qd", value) {} + }; + + struct ui64Printer : public IntPrinter<ui64_t, 32> { + ui64Printer(ui64_t value) : IntPrinter<ui64_t, 32>("%qu", value) {} + }; +#endif + + // Convert NULL-terminated UTF-8 hexadecimal string to binary, returns 0 if + // the binary buffer was large enough to hold the result. The output parameter + // 'char_count' will contain the length of the converted string. If the output + // buffer is too small or any of the pointer arguments are NULL, the subroutine + // will return -1 and set 'char_count' to the required buffer size. No data will + // be written to 'buf' if the subroutine fails. + i32_t hex2bin(const char* str, byte_t* buf, ui32_t buf_len, ui32_t* char_count); + + // Convert a binary string to NULL-terminated UTF-8 hexadecimal, returns the buffer + // if the output buffer was large enough to hold the result. If the output buffer + // is too small or any of the pointer arguments are NULL, the subroutine will + // return 0. + // + const char* bin2hex(const byte_t* bin_buf, ui32_t bin_len, char* str_buf, ui32_t str_len); + + const char* bin2UUIDhex(const byte_t* bin_buf, ui32_t bin_len, char* str_buf, ui32_t str_len); + + // same as above for base64 text + i32_t base64decode(const char* str, byte_t* buf, ui32_t buf_len, ui32_t* char_count); + const char* base64encode(const byte_t* bin_buf, ui32_t bin_len, char* str_buf, ui32_t str_len); + + // returns the length of a Base64 encoding of a buffer of the given length + inline ui32_t base64_encode_length(ui32_t length) { + while ( ( length % 3 ) != 0 ) + length++; + + return ( length / 3 ) * 4; + } + + // print buffer contents to a stream as hexadecimal values in numbered + // rows of 16-bytes each. + // + void hexdump(const byte_t* buf, ui32_t dump_len, FILE* stream = 0); + + // Return the length in bytes of a BER encoded value + inline ui32_t BER_length(const byte_t* buf) + { + if ( buf == 0 || (*buf & 0xf0) != 0x80 ) + return 0; + + return (*buf & 0x0f) + 1; + } + + // Return the BER length required to encode value. A return value of zero + // indicates a value too large for this library. + ui32_t get_BER_length_for_value(ui64_t valuse); + + // read a BER value + bool read_BER(const byte_t* buf, ui64_t* val); + + // decode a ber value and compare it to a test value + bool read_test_BER(byte_t **buf, ui64_t test_value); + + // create BER encoding of integer value + bool write_BER(byte_t* buf, ui64_t val, ui32_t ber_len = 0); + + //---------------------------------------------------------------- + // + + // an abstract base class that objects implement to serialize state + // to and from a binary stream. + class IArchive + { + public: + virtual ~IArchive(){} + virtual bool HasValue() const = 0; + virtual ui32_t ArchiveLength() const = 0; + virtual bool Archive(MemIOWriter* Writer) const = 0; + virtual bool Unarchive(MemIOReader* Reader) = 0; + }; + + // + template <class T> + class ArchivableList : public std::list<T>, public IArchive + { + public: + ArchivableList() {} + virtual ~ArchivableList() {} + + bool HasValue() const { return ! this->empty(); } + + ui32_t ArchiveLength() const + { + ui32_t arch_size = sizeof(ui32_t); + + typename ArchivableList<T>::const_iterator i = this->begin(); + for ( ; i != this->end(); i++ ) + arch_size += i->ArchiveLength(); + + return arch_size; + } + + bool Unarchive(Kumu::MemIOReader* Reader) + { + if ( Reader == 0 ) return false; + ui32_t read_size = 0; + if ( ! Reader->ReadUi32BE(&read_size) ) return false; + for ( ui32_t i = 0; i < read_size; i++ ) + { + T TmpTP; + if ( ! TmpTP.Unarchive(Reader) ) return false; + this->push_back(TmpTP); + } + + return true; + } + + bool Archive(Kumu::MemIOWriter* Writer) const + { + if ( Writer == 0 ) return false; + if ( ! Writer->WriteUi32BE(static_cast<ui32_t>(this->size())) ) return false; + typename ArchivableList<T>::const_iterator i = this->begin(); + for ( ; i != this->end(); i++ ) + if ( ! i->Archive(Writer) ) return false; + + return true; + } + }; + + // archivable version of std::string + + // + class ArchivableString : public std::string, public Kumu::IArchive + { + + public: + ArchivableString() {} + ArchivableString(const char* sz) : std::string(sz) {} + ArchivableString(const std::string& s) : std::string(s) {} + virtual ~ArchivableString() {} + + bool HasValue() const { return ! this->empty(); } + ui32_t ArchiveLength() const { return sizeof(ui32_t) + static_cast<ui32_t>(this->size()); } + + bool Archive(MemIOWriter* Writer) const { + if ( Writer == 0 ) return false; + return Writer->WriteString(*this); + } + + bool Unarchive(MemIOReader* Reader) { + if ( Reader == 0 ) return false; + return Reader->ReadString(*this); + } + }; + + // + typedef Kumu::ArchivableList<ArchivableString> StringList; + + // + // the base of all identifier classes, Identifier is not usually used directly + // see UUID and SymmetricKey below for more detail. + // + template <ui32_t SIZE> + class Identifier : public IArchive + { + protected: + bool m_HasValue; + byte_t m_Value[SIZE]; + + public: + Identifier() : m_HasValue(false) { memset(m_Value, 0, SIZE); } + Identifier(const byte_t* value) : m_HasValue(true) { memcpy(m_Value, value, SIZE); } + Identifier(const Identifier& rhs) : IArchive() { + m_HasValue = rhs.m_HasValue; + memcpy(m_Value, rhs.m_Value, SIZE); + } + + virtual ~Identifier() {} + + const Identifier& operator=(const Identifier& rhs) { + m_HasValue = rhs.m_HasValue; + memcpy(m_Value, rhs.m_Value, SIZE); + return *this; + } + + inline void Set(const byte_t* value) { m_HasValue = true; memcpy(m_Value, value, SIZE); } + inline void Reset() { m_HasValue = false; memset(m_Value, 0, SIZE); } + inline const byte_t* Value() const { return m_Value; } + inline ui32_t Size() const { return SIZE; } + + inline bool operator<(const Identifier& rhs) const { + ui32_t test_size = xmin(rhs.Size(), SIZE); + + for ( ui32_t i = 0; i < test_size; i++ ) + { + if ( m_Value[i] != rhs.m_Value[i] ) + return m_Value[i] < rhs.m_Value[i]; + } + + return false; + } + + inline bool operator==(const Identifier& rhs) const { + if ( rhs.Size() != SIZE ) return false; + return ( memcmp(m_Value, rhs.m_Value, SIZE) == 0 ); + } + + inline bool operator!=(const Identifier& rhs) const { + if ( rhs.Size() != SIZE ) return true; + return ( memcmp(m_Value, rhs.m_Value, SIZE) != 0 ); + } + + inline bool DecodeHex(const char* str) { + ui32_t char_count; + m_HasValue = ( hex2bin(str, m_Value, SIZE, &char_count) == 0 ); + if ( m_HasValue && char_count != SIZE ) + m_HasValue = false; + return m_HasValue; + } + + inline const char* EncodeHex(char* buf, ui32_t buf_len) const { + return bin2hex(m_Value, SIZE, buf, buf_len); + } + + inline const char* EncodeString(char* str_buf, ui32_t buf_len) const { + return EncodeHex(str_buf, buf_len); + } + + inline bool DecodeBase64(const char* str) { + ui32_t char_count; + m_HasValue = ( base64decode(str, m_Value, SIZE, &char_count) == 0 ); + if ( m_HasValue && char_count != SIZE ) + m_HasValue = false; + return m_HasValue; + } + + inline const char* EncodeBase64(char* buf, ui32_t buf_len) const { + return base64encode(m_Value, SIZE, buf, buf_len); + } + + inline bool HasValue() const { return m_HasValue; } + + inline ui32_t ArchiveLength() const { return SIZE; } + + inline bool Unarchive(Kumu::MemIOReader* Reader) { + m_HasValue = Reader->ReadRaw(m_Value, SIZE); + return m_HasValue; + } + + inline bool Archive(Kumu::MemIOWriter* Writer) const { + return Writer->WriteRaw(m_Value, SIZE); + } + }; + + + // UUID + // + const ui32_t UUID_Length = 16; + class UUID : public Identifier<UUID_Length> + { + public: + UUID() {} + UUID(const byte_t* value) : Identifier<UUID_Length>(value) {} + UUID(const UUID& rhs) : Identifier<UUID_Length>(rhs) {} + virtual ~UUID() {} + + inline const char* EncodeString(char* buf, ui32_t buf_len) const { + return bin2UUIDhex(m_Value, Size(), buf, buf_len); + } + + inline const char* EncodeHex(char* buf, ui32_t buf_len) const { + return bin2UUIDhex(m_Value, Size(), buf, buf_len); + } + }; + + void GenRandomUUID(byte_t* buf); // buf must be UUID_Length or longer + void GenRandomValue(UUID&); + void ResetTestRNG(); + + typedef ArchivableList<UUID> UUIDList; + + // a self-wiping key container + // + const ui32_t SymmetricKey_Length = 16; + const byte_t NilKey[SymmetricKey_Length] = { + 0xfa, 0xce, 0xfa, 0xce, 0xfa, 0xce, 0xfa, 0xce, + 0xfa, 0xce, 0xfa, 0xce, 0xfa, 0xce, 0xfa, 0xce + }; + + class SymmetricKey : public Identifier<SymmetricKey_Length> + { + public: + SymmetricKey() {} + SymmetricKey(const byte_t* value) : Identifier<SymmetricKey_Length>(value) {} + SymmetricKey(const UUID& rhs) : Identifier<SymmetricKey_Length>(rhs) {} + virtual ~SymmetricKey() { memcpy(m_Value, NilKey, 16); m_HasValue = false; } + }; + + void GenRandomValue(SymmetricKey&); + + // + // 2004-05-01T13:20:00+00:00 + const ui32_t DateTimeLen = 25; // the number of chars in the xs:dateTime format (sans milliseconds) + + // UTC time+date representation + class Timestamp : public IArchive + { + TAI::tai m_Timestamp; // always UTC + i32_t m_TZOffsetMinutes; + + public: + Timestamp(); + Timestamp(const Timestamp& rhs); + Timestamp(const char* datestr); + Timestamp(const ui16_t& Year, const ui8_t& Month, const ui8_t& Day); + Timestamp(const ui16_t& Year, const ui8_t& Month, const ui8_t& Day, + const ui8_t& Hour, const ui8_t& Minute, const ui8_t& Second); + virtual ~Timestamp(); + + const Timestamp& operator=(const Timestamp& rhs); + bool operator<(const Timestamp& rhs) const; + bool operator>(const Timestamp& rhs) const; + bool operator==(const Timestamp& rhs) const; + bool operator!=(const Timestamp& rhs) const; + + // always UTC + void GetComponents(ui16_t& Year, ui8_t& Month, ui8_t& Day, + ui8_t& Hour, ui8_t& Minute, ui8_t& Second) const; + void SetComponents(const ui16_t& Year, const ui8_t& Month, const ui8_t& Day, + const ui8_t& Hour, const ui8_t& Minute, const ui8_t& Second); + + // Write the timestamp value to the given buffer in the form 2004-05-01T13:20:00+00:00 + // returns 0 if the buffer is smaller than DateTimeLen + const char* EncodeString(char* str_buf, ui32_t buf_len) const; + + // decode and set value from string formatted by EncodeString + bool DecodeString(const char* datestr); + + // Add the given number of days, hours, minutes, or seconds to the timestamp value. + // Values less than zero will cause the timestamp to decrease + inline void AddDays(const i32_t& d) { m_Timestamp.add_days(d); } + inline void AddHours(const i32_t& h) { m_Timestamp.add_hours(h); } + inline void AddMinutes(const i32_t& m) { m_Timestamp.add_minutes(m); } + inline void AddSeconds(const i32_t& s) { m_Timestamp.add_seconds(s); } + + // returns false if the requested adjustment is out of range + bool SetTZOffsetMinutes(const i32_t& minutes); + inline i32_t GetTZOffsetMinutes() const { return m_TZOffsetMinutes; } + + // Return the number of seconds since the Unix epoch UTC (1970-01-01T00:00:00+00:00) + ui64_t GetCTime() const; + + // Set internal time to the number of seconds since the Unix epoch UTC + void SetCTime(const ui64_t& ctime); + + // Read and write the timestamp (always UTC) value as a byte string having + // the following format: + // | 16 bits int, big-endian | 8 bits | 8 bits | 8 bits | 8 bits | 8 bits | + // | Year A.D | Month(1-12) | Day(1-31) | Hour(0-23) | Minute(0-59) | Second(0-59) | + // + virtual bool HasValue() const; + virtual ui32_t ArchiveLength() const { return 8L; } + virtual bool Archive(MemIOWriter* Writer) const; + virtual bool Unarchive(MemIOReader* Reader); + }; + + // + class ByteString : public IArchive + { + KM_NO_COPY_CONSTRUCT(ByteString); + + protected: + byte_t* m_Data; // pointer to memory area containing frame data + ui32_t m_Capacity; // size of memory area pointed to by m_Data + ui32_t m_Length; // length of byte string in memory area pointed to by m_Data + + public: + ByteString(); + ByteString(ui32_t cap); + virtual ~ByteString(); + + // Sets or resets the size of the internally allocated buffer. + Result_t Capacity(ui32_t cap); + + Result_t Append(const ByteString&); + Result_t Append(const byte_t* buf, ui32_t buf_len); + + // returns the size of the buffer + inline ui32_t Capacity() const { return m_Capacity; } + + // returns a const pointer to the essence data + inline const byte_t* RoData() const { assert(m_Data); return m_Data; } + + // returns a non-const pointer to the essence data + inline byte_t* Data() { assert(m_Data); return m_Data; } + + // set the length of the buffer's contents + inline ui32_t Length(ui32_t l) { return m_Length = l; } + + // returns the length of the buffer's contents + inline ui32_t Length() const { return m_Length; } + + // copy the given data into the ByteString, set Length value. + // Returns error if the ByteString is too small. + Result_t Set(const byte_t* buf, ui32_t buf_len); + Result_t Set(const ByteString& Buf); + + inline virtual bool HasValue() const { return m_Length > 0; } + + inline virtual ui32_t ArchiveLength() const { return sizeof(ui32_t) + m_Length; } + + inline virtual bool Archive(MemIOWriter* Writer) const { + assert(Writer); + if ( ! Writer->WriteUi32BE(m_Length) ) return false; + if ( ! Writer->WriteRaw(m_Data, m_Length) ) return false; + return true; + } + + inline virtual bool Unarchive(MemIOReader* Reader) { + assert(Reader); + ui32_t tmp_len; + if ( ! Reader->ReadUi32BE(&tmp_len) ) return false; + if ( KM_FAILURE(Capacity(tmp_len)) ) return false; + if ( ! Reader->ReadRaw(m_Data, tmp_len) ) return false; + m_Length = tmp_len; + return true; + } + }; + + inline void hexdump(const ByteString& buf, FILE* stream = 0) { + hexdump(buf.RoData(), buf.Length(), stream); + } + + // Locates the first occurrence of the null-terminated string s2 in the string s1, where not more + // than n characters are searched. Characters that appear after a `\0' character are not searched. + // Reproduced here from BSD for portability. + const char *km_strnstr(const char *s1, const char *s2, size_t n); + + // Split the input string into tokens using the given separator. If the separator is not found the + // entire string will be returned as a single-item list. Empty items will be recorded for + // adjacent instances of the separator. E.g., "/foo//bar/" will return ["", "foo", "", "bar", ""]. + std::list<std::string> km_token_split(const std::string& str, const std::string& separator); + +} // namespace Kumu + + +#endif // _KM_UTIL_H_ + +// +// end KM_util.h +// |
