diff options
| author | jhurst <jhurst@cinecert.com> | 2005-12-20 01:55:40 +0000 |
|---|---|---|
| committer | jhurst <> | 2005-12-20 01:55:40 +0000 |
| commit | 8095eaa320551b6795d0368c0ad0c227a3167caa (patch) | |
| tree | e522d5137671fffbc8fcc084831b5d8806ef44f2 /src | |
wheee!
Diffstat (limited to 'src')
38 files changed, 14938 insertions, 0 deletions
diff --git a/src/AS_DCP.cpp b/src/AS_DCP.cpp new file mode 100755 index 0000000..4b5acd6 --- /dev/null +++ b/src/AS_DCP.cpp @@ -0,0 +1,509 @@ +/* +Copyright (c) 2004-2005, 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.cpp + \version $Id$ + \brief AS-DCP library, misc classes and subroutines +*/ + +#include <AS_DCP_system.h> +#include "hex_utils.h" +#include <assert.h> + + +static const ui32_t s_MessageCount = 27; +static const char* s_ErrorMessages[] = +{ + "An undefined error was detected.", + "An unexpected NULL pointer was given.", + "An unexpected empty string was given.", + "The given frame buffer is too small.", + "The object is not yet initialized.", + + "The requested file does not exist on the system.", + "Insufficient privilege exists to perform the operation.", + "File open error.", + "The file contains errors or is not OP-Atom/AS-DCP.", + "An invalid file location was requested.", + + "File read error.", + "File write error.", + "Unknown raw essence file type.", + "Raw essence format invalid.", + "Object state error.", + + "Attempt to read past end of file.", + "Invalid configuration option detected.", + "Frame number out of range.", + "AESEncContext required when writing to encrypted file", + "Plaintext offset exceeds frame buffer size", + + "Error allocating memory", + "Cannot resize externally allocated memory", + "The check value did not decrypt correctly", + "HMAC authentication failure", + "HMAC context required", + + "Error initializing block cipher context", + "Attempted to write an empty frame buffer" +}; + + +//------------------------------------------------------------------------------------------ + +// +class StderrLogSink : public ASDCP::ILogSink +{ +public: + bool show_info; + bool show_debug; + + StderrLogSink() : show_info(false), show_debug(false) {} + ~StderrLogSink() {} + + void Error(const char* fmt, ...) { + va_list args; + va_start(args, fmt); + vLogf(LOG_ERROR, fmt, &args); + va_end(args); + } + + void Warn(const char* fmt, ...) { + va_list args; + va_start(args, fmt); + vLogf(LOG_WARN, fmt, &args); + va_end(args); + } + + void Info(const char* fmt, ...) { + va_list args; + va_start(args, fmt); + vLogf(LOG_INFO, fmt, &args); + va_end(args); + } + + void Debug(const char* fmt, ...) { + va_list args; + va_start(args, fmt); + vLogf(LOG_DEBUG, fmt, &args); + va_end(args); + } + + void Logf(ASDCP::ILogSink::LogType_t type, const char* fmt, ...) { + va_list args; + va_start(args, fmt); + vLogf(type, fmt, &args); + va_end(args); + } + + void vLogf(ASDCP::ILogSink::LogType_t type, const char* fmt, va_list* list) { + FILE* stream = stderr; + + switch ( type ) + { + case LOG_ERROR: fputs("Error: ", stream); break; + case LOG_WARN: fputs("Warning: ", stream); break; + case LOG_INFO: + if ( ! show_info ) return; + fputs("Info: ", stream); + break; + case LOG_DEBUG: + if ( ! show_debug ) return; + fputs("Debug: ", stream); + break; + } + + vfprintf(stream, fmt, *list); + } + +} s_StderrLogSink; + +// +static ASDCP::ILogSink* s_DefaultLogSink = 0; + +// +void +ASDCP::SetDefaultLogSink(ILogSink* Sink) +{ + s_DefaultLogSink = Sink; +} + +// bootleg entry for debug enthusiasts +void +set_debug_mode(bool info_mode, bool debug_mode) +{ + s_StderrLogSink.show_info = info_mode; + s_StderrLogSink.show_debug = debug_mode; +} + +// Returns the internal default sink. +ASDCP::ILogSink& +ASDCP::DefaultLogSink() +{ + if ( s_DefaultLogSink == 0 ) + s_DefaultLogSink = &s_StderrLogSink; + + return *s_DefaultLogSink; +} + +const char* +ASDCP::Version() +{ + static char ver[16]; + sprintf(ver, "%lu.%lu.%lu", VERSION_MAJOR, VERSION_APIMINOR, VERSION_IMPMINOR); + return ver; +} + + +// Returns a pointer to an English language string describing the given result code. +// If the result code is not a valid member of the Result_t enum, the string +// "**UNKNOWN**" will be returned. +const char* +ASDCP::GetResultString(Result_t result) +{ + if ( result >= 0 ) + return "No error."; + + ui32_t idx = (- result); + + if ( idx > s_MessageCount ) + return "**UNKNOWN**"; + + return s_ErrorMessages[--idx]; +} + + +// convert utf-8 hext string to bin +i32_t +ASDCP::hex2bin(const char* str, byte_t* buf, ui32_t buf_len, ui32_t* conv_size) +{ + ASDCP_TEST_NULL(str); + ASDCP_TEST_NULL(buf); + ASDCP_TEST_NULL(conv_size); + + *conv_size = 0; + + if ( str[0] == 0 ) // nothing to convert + return 0; + + for ( int j = 0; str[j]; j++ ) + { + if ( isxdigit(str[j]) ) + (*conv_size)++; + } + + if ( *conv_size & 0x01 ) (*conv_size)++; + *conv_size /= 2; + + if ( *conv_size > buf_len )// maximum possible data size + return -1; + + *conv_size = 0; + + int phase = 0; // track high/low nybble + + // for each character, fill in the high nybble then the low + for ( int i = 0; str[i]; i++ ) + { + if ( ! isxdigit(str[i]) ) + continue; + + byte_t val = str[i] - ( isdigit(str[i]) ? 0x30 : ( isupper(str[i]) ? 0x37 : 0x57 ) ); + + if ( phase == 0 ) + { + buf[*conv_size] = val << 4; + phase++; + } + else + { + buf[*conv_size] |= val; + phase = 0; + (*conv_size)++; + } + } + + return 0; +} + + +// convert a memory region to a NULL-terminated hexadecimal string +// +const char* +ASDCP::bin2hex(const byte_t* bin_buf, ui32_t bin_len, char* str_buf, ui32_t str_len) +{ + if ( bin_buf == 0 + || str_buf == 0 + || ((bin_len * 2) + 1) > str_len ) + return 0; + + char* p = str_buf; + + for ( ui32_t i = 0; i < bin_len; i++ ) + { + *p = (bin_buf[i] >> 4) & 0x0f; + *p += *p < 10 ? 0x30 : 0x61 - 10; + p++; + + *p = bin_buf[i] & 0x0f; + *p += *p < 10 ? 0x30 : 0x61 - 10; + p++; + } + + *p = '\0'; + return str_buf; +} + + +// spew a range of bin data as hex +void +ASDCP::hexdump(const byte_t* buf, ui32_t dump_len, FILE* stream) +{ + if ( buf == 0 ) + return; + + if ( stream == 0 ) + stream = stderr; + + static ui32_t row_len = 16; + const byte_t* p = buf; + const byte_t* end_p = p + dump_len; + + for ( ui32_t line = 0; p < end_p; line++ ) + { + fprintf(stream, " %06x: ", line); + ui32_t i; + const byte_t* pp; + + for ( pp = p, i = 0; i < row_len && pp < end_p; i++, pp++ ) + fprintf(stream, "%02x ", *pp); + + while ( i++ < row_len ) + fputs(" ", stream); + + for ( pp = p, i = 0; i < row_len && pp < end_p; i++, pp++ ) + fputc((isprint(*pp) ? *pp : '.'), stream); + + fputc('\n', stream); + p += row_len; + } +} + +//------------------------------------------------------------------------------------------ + +// read a ber value from the buffer and compare with test value. +// Advances buffer to first character after BER value. +// +bool +ASDCP::read_test_BER(byte_t **buf, ui64_t test_value) +{ + if ( buf == 0 ) + return false; + + if ( ( **buf & 0x80 ) == 0 ) + return false; + + ui64_t val = 0; + ui8_t ber_size = ( **buf & 0x0f ) + 1; + + if ( ber_size > 9 ) + return false; + + for ( ui8_t i = 1; i < ber_size; i++ ) + { + if ( (*buf)[i] > 0 ) + val |= (ui64_t)((*buf)[i]) << ( ( ( ber_size - 1 ) - i ) * 8 ); + } + + *buf += ber_size; + return ( val == test_value ); +} + + +// +bool +ASDCP::read_BER(const byte_t* buf, ui64_t* val) +{ + ui8_t ber_size, i; + + if ( buf == 0 || val == 0 ) + return false; + + if ( ( *buf & 0x80 ) == 0 ) + return false; + + *val = 0; + ber_size = ( *buf & 0x0f ) + 1; + + if ( ber_size > 9 ) + return false; + + for ( i = 1; i < ber_size; i++ ) + { + if ( buf[i] > 0 ) + *val |= (ui64_t)buf[i] << ( ( ( ber_size - 1 ) - i ) * 8 ); + } + + return true; +} + + +static const ui64_t ber_masks[9] = + { ui64_C(0xffffffffffffffff), ui64_C(0xffffffffffffff00), + ui64_C(0xffffffffffff0000), ui64_C(0xffffffffff000000), + ui64_C(0xffffffff00000000), ui64_C(0xffffff0000000000), + ui64_C(0xffff000000000000), ui64_C(0xff00000000000000), + 0 + }; + + +// +bool +ASDCP::write_BER(byte_t* buf, ui64_t val, ui32_t ber_len) +{ + if ( buf == 0 ) + return false; + + if ( ber_len == 0 ) + { // calculate default length + if ( val < 0x01000000L ) + ber_len = 4; + else if ( val < ui64_C(0x0100000000000000) ) + ber_len = 8; + else + ber_len = 9; + } + else + { // sanity check BER length + if ( ber_len > 9 ) + { + DefaultLogSink().Error("BER size %lu exceeds maximum size of 9\n", ber_len); + return false; + } + + if ( val & ber_masks[ber_len - 1] ) + { + char intbuf[IntBufferLen]; + DefaultLogSink().Error("BER size %lu too small for value %s\n", + ber_len, ui64sz(val, intbuf)); + return false; + } + } + + buf[0] = 0x80 + ( ber_len - 1 ); + + for ( ui32_t i = ber_len - 1; i > 0; i-- ) + { + buf[i] = (ui8_t)(val & 0xff); + val >>= 8; + } + + return true; +} + +//------------------------------------------------------------------------------------------ +// +// frame buffer base class implementation + +ASDCP::FrameBuffer::FrameBuffer() : + m_Data(0), m_Capacity(0), m_OwnMem(false), m_Size(0), + m_FrameNumber(0), m_SourceLength(0), m_PlaintextOffset(0) +{ +} + +ASDCP::FrameBuffer::~FrameBuffer() +{ + if ( m_OwnMem && m_Data != 0 ) + free(m_Data); +} + +// Instructs the object to use an externally allocated buffer. The external +// buffer will not be cleaned up by the frame buffer when it is destroyed. +// Call with (0,0) to revert to internally allocated buffer. +// Returns error if the buf_addr argument is NULL and either buf_size is +// non-zero or internally allocated memory is in use. +ASDCP::Result_t +ASDCP::FrameBuffer::SetData(byte_t* buf_addr, ui32_t buf_size) +{ + // if buf_addr is null and we have an external memory reference, + // drop the reference and place the object in the initialized- + // but-no-buffer-allocated state + if ( buf_addr == 0 ) + { + if ( buf_size > 0 || m_OwnMem ) + return RESULT_PTR; + + m_OwnMem = false; + m_Capacity = m_Size = 0; + m_Data = 0; + return RESULT_OK; + } + + if ( m_OwnMem && m_Data != 0 ) + free(m_Data); + + m_OwnMem = false; + m_Capacity = buf_size; + m_Data = buf_addr; + m_Size = 0; + + return RESULT_OK; +} + +// 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. +ASDCP::Result_t +ASDCP::FrameBuffer::Capacity(ui32_t cap_size) +{ + if ( ! m_OwnMem && m_Data != 0 ) + return RESULT_CAPEXTMEM; // cannot resize external memory + + if ( m_Capacity < cap_size ) + { + if ( m_Data != 0 ) + { + assert(m_OwnMem); + free(m_Data); + } + + m_Data = (byte_t*)malloc(cap_size); + + if ( m_Data == 0 ) + return RESULT_ALLOC; + + m_Capacity = cap_size; + m_OwnMem = true; + m_Size = 0; + } + + return RESULT_OK; +} + + +// +// end AS_DCP.cpp +// diff --git a/src/AS_DCP.h b/src/AS_DCP.h new file mode 100755 index 0000000..06ff8b9 --- /dev/null +++ b/src/AS_DCP.h @@ -0,0 +1,1088 @@ +/* +Copyright (c) 2003-2005, 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 wrapper objects that offer simplified +access to files conforming to the file formats proposed by the SMPTE +D-Cinema packaging working group DC28.20. The file format, labeled +AS-DCP, is described in series of separate documents which include but +may not be limited to: + + o AS-DCP Track File Specification + o AS-DCP Track File Essence Encryption Specification + o AS-DCP Operational Constraints Specification + o SMPTE 330M - UMID + o SMPTE 336M - KLV + o SMPTE 377M - MXF + o SMPTE 390M - OP-Atom + o SMPTE 379M - Generic Container + o SMPTE 381M - MPEG2 picture + o SMPTE XXXM - JPEG 2000 picture + o SMPTE 382M - WAV/PCM sound + o IETF RFC 2104 - HMAC/SHA1 + o NIST FIPS 197 - AES (Rijndael) + +The following use cases are supported by the library: + + o Write a plaintext MPEG2 Video Elementary Stream to a plaintext ASDCP file + o Write a plaintext MPEG2 Video Elementary Stream to a ciphertext ASDCP file + o Read a plaintext MPEG2 Video Elementary Stream from a plaintext ASDCP file + o Read a plaintext MPEG2 Video Elementary Stream from a ciphertext ASDCP file + o Read a ciphertext MPEG2 Video Elementary Stream from a ciphertext ASDCP file + o Write one or more plaintext JPEG 2000 codestreams to a plaintext ASDCP file + o Write one or more plaintext JPEG 2000 codestreams to a ciphertext ASDCP file + o Read one or more plaintext JPEG 2000 codestreams from a plaintext ASDCP file + o Read one or more plaintext JPEG 2000 codestreams from a ciphertext ASDCP file + o Read one or more ciphertext JPEG 2000 codestreams from a ciphertext ASDCP file + o Write one or more plaintext PCM audio streams to a plaintext ASDCP file + o Write one or more plaintext PCM audio streams to a ciphertext ASDCP file + o Read one or more plaintext PCM audio streams from a plaintext ASDCP file + o Read one or more plaintext PCM audio streams from a ciphertext ASDCP file + o Read one or more ciphertext PCM audio streams from a ciphertext ASDCP file + o Read header metadata from an ASDCP file + +This project depends upon the following library: + - OpenSSL http://www.openssl.org/ + +*/ + +#ifndef _AS_DCP_H__ +#define _AS_DCP_H__ + +#include <stdio.h> +#include <stdarg.h> +#include <iostream> +#include <math.h> + +//-------------------------------------------------------------------------------- +// 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 consists of three segments: major, API minor, and + // implementation minor. Whenever a change is made to AS_DCP.h, the API minor + // version will increment. Changes made to the internal implementation will + // result in the incrementing of the implementation minor version. + + // For example, if asdcplib version 1.0.0 were modified to accomodate changes + // in file format, and if no changes were made to AS_DCP.h, the new version would be + // 1.0.1. If changes were also required in AS_DCP.h, the new version would be 1.1.1. + const ui32_t VERSION_MAJOR = 1; + const ui32_t VERSION_APIMINOR = 0; + const ui32_t VERSION_IMPMINOR = 3; + 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; + + // Encryption key IDs are passed around as strings of KeyIDlen bytes + const ui32_t KeyIDlen = 16; + + + //--------------------------------------------------------------------------------- + // message logging + + // Error and debug messages will be delivered to an object having this interface. + // The default implementation sends only LOG_ERROR and LOG_WARN messages to stderr. + // To receive LOG_INFO or LOG_DEBUG messages, or to send messages somewhere other + // than stderr, implement this interface and register an instance of your new class + // by calling SetDefaultLogSink(). + class ILogSink + { + public: + enum LogType_t { LOG_ERROR, LOG_WARN, LOG_INFO, LOG_DEBUG }; + + virtual ~ILogSink() {} + virtual void Error(const char*, ...) = 0; // receives error messges + virtual void Warn(const char*, ...) = 0; // receives warning messges + virtual void Info(const char*, ...) = 0; // receives info messages + virtual void Debug(const char*, ...) = 0; // receives debug messages + virtual void Logf(LogType_t, const char*, ...) = 0; // log a formatted string with positional parameters + virtual void vLogf(LogType_t, const char*, va_list*) = 0; // log a formatted string with a va_list struct + }; + + // Sets the internal default sink to the given receiver. If the given value + // is zero, sets the default sink to the internally allocated stderr sink. + void SetDefaultLogSink(ILogSink* = 0); + + // Returns the internal default sink. + ILogSink& DefaultLogSink(); + + //--------------------------------------------------------------------------------- + // return values + + // Each method or subroutine in this library that is not void or does not directly + // return a value will instead return a result code from this enumeration. + enum Result_t { + RESULT_FALSE = 1, // successful but negative + RESULT_OK = 0, // No errors detected + RESULT_FAIL = -1, // An undefined error was detected + RESULT_PTR = -2, // An unexpected NULL pointer was given + RESULT_NULL_STR = -3, // An unexpected empty string was given + RESULT_SMALLBUF = -4, // The given frame buffer is too small + RESULT_INIT = -5, // The object is not yet initialized + RESULT_NOT_FOUND = -6, // The requested file does not exist on the system + RESULT_NO_PERM = -7, // Insufficient privilege exists to perform the operation + RESULT_FILEOPEN = -8, // Failure opening file + RESULT_FORMAT = -9, // The file format is not proper OP-Atom/AS-DCP + RESULT_BADSEEK = -10, // An invalid file location was requested + RESULT_READFAIL = -11, // File read error + RESULT_WRITEFAIL = -12, // File write error + RESULT_RAW_ESS = -13, // Unknown raw essence file type + RESULT_RAW_FORMAT = -14, // Raw essence format invalid + RESULT_STATE = -15, // Object state error + RESULT_ENDOFFILE = -16, // Attempt to read past end of file + RESULT_CONFIG = -17, // Invalid configuration option detected + RESULT_RANGE = -18, // Frame number out of range + RESULT_CRYPT_CTX = -19, // AESEncContext required when writing to encrypted file + RESULT_LARGE_PTO = -20, // Plaintext offset exceeds frame buffer size + RESULT_ALLOC = -21, // Error allocating memory + RESULT_CAPEXTMEM = -22, // Cannot resize externally allocated memory + RESULT_CHECKFAIL = -23, // The check value did not decrypt correctly + RESULT_HMACFAIL = -24, // HMAC authentication failure + RESULT_HMAC_CTX = -25, // HMAC context required + RESULT_CRYPT_INIT = -26, // Error initializing block cipher context + RESULT_EMPTY_FB = -27, // Attempted to write an empty frame buffer + }; + + // Returns a pointer to an English language string describing the given result code. + // If the result code is not a valid member of the Result_t enum, the string + // "**UNKNOWN**" will be returned. + const char* GetResultString(Result_t); + + //--------------------------------------------------------------------------------- + // 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 essence container + ESS_MPEG2_VES, // the file contains an MPEG video elementary stream + 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 + }; + + + // 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 char* 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 char* filename, EssenceType_t& type); + + // Locate the named object in the file header and dump it to the given stream. + // The default dump stream is stderr. + Result_t FindObject(const char* filename, const char* objname, FILE* = 0); + + + // A simple container for rational numbers. + struct Rational + { + 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 ); + } + }; + + // common edit rates, use these instead of hard coded constants + const Rational EditRate_24(24,1); + const Rational EditRate_23_98(24000,1001); + const Rational EditRate_48(48,1); + const Rational SampleRate_48k(48000,1); + + // 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; } + }; + + //--------------------------------------------------------------------------------- + // 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); + + // 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; + }; + + //--------------------------------------------------------------------------------- + // WriterInfo class - encapsulates writer identification details used for + // OpenWrite() calls. Replace these values at runtime to identify your product. + // + struct WriterInfo + { + byte_t ProductUUID[UUIDlen]; + byte_t AssetUUID[UUIDlen]; + byte_t ContextID[UUIDlen]; + byte_t CryptographicKeyID[KeyIDlen]; + bool EncryptedEssence; // true if essence data is (or is to be) encrypted + bool UsesHMAC; // true if HMAC exists or is to be calculated + std::string ProductVersion; + std::string CompanyName; + std::string ProductName; + + WriterInfo() : EncryptedEssence(false), UsesHMAC(false) { + 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, KeyIDlen); + + ProductVersion = "Unreleased "; + ProductVersion += Version(); + CompanyName = "DCI"; + ProductName = "asdcplib"; + } + }; + + // Print WriterInfo to stream, stderr by default. + void WriterInfoDump(const WriterInfo&, FILE* = 0); + + //--------------------------------------------------------------------------------- + // 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; } + }; + + + //--------------------------------------------------------------------------------- + // 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 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 char* 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(); + + // 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 char* 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(); + + // Open the file for reading. The file must exist. Returns error if the + // operation cannot be completed. + Result_t OpenRead(const char* 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; + + // 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 + { + struct AudioDescriptor + { + Rational SampleRate; // 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 + }; + + // 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.SampleRate.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 char* 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(); + + // 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 char* 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(); + + // Open the file for reading. The file must exist. Returns error if the + // operation cannot be completed. + Result_t OpenRead(const char* 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; + + // 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 DefaultCodingDataLength = 64; + + struct ImageComponent + { + byte_t Ssize; + byte_t XRsize; + byte_t YRsize; + }; + + 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 ImageComponents[MaxComponents]; + byte_t CodingStyle[DefaultCodingDataLength]; + ui32_t CodingStyleLength; + byte_t QuantDefault[DefaultCodingDataLength]; + ui32_t QuantDefaultLength; + }; + + // 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 char* filename, 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; + }; + + // 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 char* filename, 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(); + + // 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 char* filename, const WriterInfo&, + const PictureDescriptor&, 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(); + + // Open the file for reading. The file must exist. Returns error if the + // operation cannot be completed. + Result_t OpenRead(const char* 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; + + // Print debugging information to stream + void DumpHeaderMetadata(FILE* = 0) const; + void DumpIndex(FILE* = 0) const; + }; + } // namespace JP2K +} // namespace ASDCP + + +#endif // _AS_DCP_H__ + +// +// end AS_DCP.h +// diff --git a/src/AS_DCP_AES.cpp b/src/AS_DCP_AES.cpp new file mode 100755 index 0000000..3d36566 --- /dev/null +++ b/src/AS_DCP_AES.cpp @@ -0,0 +1,462 @@ +/* +Copyright (c) 2004, 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_AES.h + \version $Id$ + \brief AS-DCP library, AES wrapper +*/ + + +#include <assert.h> +#include <AS_DCP.h> + +using namespace ASDCP; +const int KEY_SIZE_BITS = 128; + + +#ifndef ASDCP_WITHOUT_OPENSSL +#include <openssl/aes.h> +#include <openssl/sha.h> +#include <openssl/err.h> + +void +print_ssl_error() +{ + char err_buf[256]; + unsigned long errval = ERR_get_error(); + DefaultLogSink().Error("OpenSSL: %s\n", ERR_error_string(errval, err_buf)); +} + +#endif + + +//------------------------------------------------------------------------------------------ + +#ifdef ASDCP_WITHOUT_OPENSSL +class ASDCP::AESEncContext::h__AESContext +#else +class ASDCP::AESEncContext::h__AESContext : public AES_KEY +#endif +{ +public: + byte_t m_IVec[CBC_BLOCK_SIZE]; +}; + + +ASDCP::AESEncContext::AESEncContext() {} +ASDCP::AESEncContext::~AESEncContext() {} + +// Initializes Rijndael CBC encryption context. +// Returns error if the key argument is NULL. +ASDCP::Result_t +ASDCP::AESEncContext::InitKey(const byte_t* key) +{ + ASDCP_TEST_NULL(key); + + if ( m_Context ) + return RESULT_INIT; + +#ifndef ASDCP_WITHOUT_OPENSSL + m_Context = new h__AESContext; + + if ( AES_set_encrypt_key(key, KEY_SIZE_BITS, m_Context) ) + { + print_ssl_error(); + return RESULT_CRYPT_INIT; + } + + return RESULT_OK; +#else // ASDCP_WITHOUT_OPENSSL + return RESULT_FAIL; +#endif // ASDCP_WITHOUT_OPENSSL +} + + +// Set the value of the 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. +ASDCP::Result_t +ASDCP::AESEncContext::SetIVec(const byte_t* i_vec) +{ + ASDCP_TEST_NULL(i_vec); + + if ( ! m_Context ) + return RESULT_INIT; + +#ifndef ASDCP_WITHOUT_OPENSSL + memcpy(m_Context->m_IVec, i_vec, CBC_BLOCK_SIZE); + return RESULT_OK; +#else // ASDCP_WITHOUT_OPENSSL + return RESULT_FAIL; +#endif // ASDCP_WITHOUT_OPENSSL +} + + +// Retrieve the value of the 16 byte CBC Initialization Vector. +// Returns error if the i_vec argument is NULL. +ASDCP::Result_t +ASDCP::AESEncContext::GetIVec(byte_t* i_vec) const +{ + ASDCP_TEST_NULL(i_vec); + + if ( ! m_Context ) + return RESULT_INIT; + +#ifndef ASDCP_WITHOUT_OPENSSL + memcpy(i_vec, m_Context->m_IVec, CBC_BLOCK_SIZE); + return RESULT_OK; +#else // ASDCP_WITHOUT_OPENSSL + return RESULT_FAIL; +#endif // ASDCP_WITHOUT_OPENSSL +} + + +// Encrypt a 16 byte block of data. +// Returns error if either argument is NULL. +ASDCP::Result_t +ASDCP::AESEncContext::EncryptBlock(const byte_t* pt_buf, byte_t* ct_buf, ui32_t block_size) +{ + ASDCP_TEST_NULL(pt_buf); + ASDCP_TEST_NULL(ct_buf); + assert(block_size > 0); + assert( block_size % CBC_BLOCK_SIZE == 0 ); + + if ( m_Context.empty() ) + return RESULT_INIT; + +#ifndef ASDCP_WITHOUT_OPENSSL + h__AESContext* Ctx = m_Context; + byte_t tmp_buf[CBC_BLOCK_SIZE]; + const byte_t* in_p = pt_buf; + byte_t* out_p = ct_buf; + + while ( block_size ) + { + // xor with the previous block + for ( ui32_t i = 0; i < CBC_BLOCK_SIZE; i++ ) + tmp_buf[i] = in_p[i] ^ Ctx->m_IVec[i]; + + AES_encrypt(tmp_buf, Ctx->m_IVec, Ctx); + memcpy(out_p, Ctx->m_IVec, CBC_BLOCK_SIZE); + + in_p += CBC_BLOCK_SIZE; + out_p += CBC_BLOCK_SIZE; + block_size -= CBC_BLOCK_SIZE; + } + + return RESULT_OK; +#else // ASDCP_WITHOUT_OPENSSL + return RESULT_FAIL; +#endif // ASDCP_WITHOUT_OPENSSL +} + + +//------------------------------------------------------------------------------------------ + +#ifdef ASDCP_WITHOUT_OPENSSL +class ASDCP::AESDecContext::h__AESContext +#else +class ASDCP::AESDecContext::h__AESContext : public AES_KEY +#endif +{ +public: + byte_t m_IVec[CBC_BLOCK_SIZE]; +}; + +ASDCP::AESDecContext::AESDecContext() {} +ASDCP::AESDecContext::~AESDecContext() {} + + +// Initializes Rijndael CBC decryption context. +// Returns error if the key argument is NULL. +ASDCP::Result_t +ASDCP::AESDecContext::InitKey(const byte_t* key) +{ + ASDCP_TEST_NULL(key); + + if ( m_Context ) + return RESULT_INIT; + +#ifndef ASDCP_WITHOUT_OPENSSL + m_Context = new h__AESContext; + + if ( AES_set_decrypt_key(key, KEY_SIZE_BITS, m_Context) ) + { + print_ssl_error(); + return RESULT_CRYPT_INIT; + } + + return RESULT_OK; +#else // ASDCP_WITHOUT_OPENSSL + return RESULT_FAIL; +#endif // ASDCP_WITHOUT_OPENSSL +} + +// 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. +ASDCP::Result_t +ASDCP::AESDecContext::SetIVec(const byte_t* i_vec) +{ + ASDCP_TEST_NULL(i_vec); + + if ( ! m_Context ) + return RESULT_INIT; + +#ifndef ASDCP_WITHOUT_OPENSSL + memcpy(m_Context->m_IVec, i_vec, CBC_BLOCK_SIZE); + return RESULT_OK; +#else // ASDCP_WITHOUT_OPENSSL + return RESULT_FAIL; +#endif // ASDCP_WITHOUT_OPENSSL +} + +// Decrypt a 16 byte block of data. +// Returns error if either argument is NULL. +ASDCP::Result_t +ASDCP::AESDecContext::DecryptBlock(const byte_t* ct_buf, byte_t* pt_buf, ui32_t block_size) +{ + ASDCP_TEST_NULL(ct_buf); + ASDCP_TEST_NULL(pt_buf); + assert(block_size > 0); + assert( block_size % CBC_BLOCK_SIZE == 0 ); + + if ( m_Context.empty() ) + return RESULT_INIT; + +#ifndef ASDCP_WITHOUT_OPENSSL + register h__AESContext* Ctx = m_Context; + + const byte_t* in_p = ct_buf; + byte_t* out_p = pt_buf; + + while ( block_size ) + { + AES_decrypt(in_p, out_p, Ctx); + + for ( ui32_t i = 0; i < CBC_BLOCK_SIZE; i++ ) + out_p[i] ^= Ctx->m_IVec[i]; + + memcpy(Ctx->m_IVec, in_p, CBC_BLOCK_SIZE); + + in_p += CBC_BLOCK_SIZE; + out_p += CBC_BLOCK_SIZE; + block_size -= CBC_BLOCK_SIZE; + } + + return RESULT_OK; +#else // ASDCP_WITHOUT_OPENSSL + return RESULT_FAIL; +#endif // ASDCP_WITHOUT_OPENSSL +} + +//------------------------------------------------------------------------------------------ + + +static byte_t ipad[KeyLen] = { 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36 }; + +static byte_t opad[KeyLen] = { 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, + 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c }; + +class HMACContext::h__HMACContext +{ +#ifndef ASDCP_WITHOUT_OPENSSL + SHA_CTX m_SHA; +#endif // ASDCP_WITHOUT_OPENSSL + byte_t m_key[KeyLen]; + ASDCP_NO_COPY_CONSTRUCT(h__HMACContext); + +public: + byte_t sha_value[HMAC_SIZE]; + bool m_Final; + + h__HMACContext() : m_Final(false) {} + ~h__HMACContext() {} + + // + void SetKey(const byte_t* key) + { + static byte_t key_nonce[KeyLen] = { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, + 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff }; +#ifndef ASDCP_WITHOUT_OPENSSL + byte_t sha_buf[SHA_DIGEST_LENGTH]; + + // 7.10: MICKey = trunc( SHA1 ( key, key_nonce ) ) + SHA_CTX SHA; + SHA1_Init(&SHA); + SHA1_Update(&SHA, key, KeyLen); + SHA1_Update(&SHA, key_nonce, KeyLen); + SHA1_Final(sha_buf, &SHA); + memcpy(m_key, sha_buf, KeyLen); + + Reset(); +#endif // ASDCP_WITHOUT_OPENSSL + } + + void + Reset() + { +#ifndef ASDCP_WITHOUT_OPENSSL + byte_t xor_buf[KeyLen]; + memset(sha_value, 0, HMAC_SIZE); + m_Final = false; + SHA1_Init(&m_SHA); + + // H(K XOR opad, H(K XOR ipad, text)) + // ^^^^^^^^^^ + for ( ui32_t i = 0; i < KeyLen; i++ ) + xor_buf[i] = m_key[i] ^ ipad[i]; + + SHA1_Update(&m_SHA, xor_buf, KeyLen); +#endif // ASDCP_WITHOUT_OPENSSL + } + + // + void + Update(const byte_t* buf, ui32_t buf_len) + { +#ifndef ASDCP_WITHOUT_OPENSSL + // H(K XOR opad, H(K XOR ipad, text)) + // ^^^^ + SHA1_Update(&m_SHA, buf, buf_len); +#endif // ASDCP_WITHOUT_OPENSSL + } + + // + void + Finalize() + { +#ifndef ASDCP_WITHOUT_OPENSSL + // H(K XOR opad, H(K XOR ipad, text)) + // ^^^^^^^^^^^^^^^ + SHA1_Final(sha_value, &m_SHA); + + SHA_CTX SHA; + SHA1_Init(&SHA); + + byte_t xor_buf[KeyLen]; + + for ( ui32_t i = 0; i < KeyLen; i++ ) + xor_buf[i] = m_key[i] ^ opad[i]; + + SHA1_Update(&SHA, xor_buf, KeyLen); + SHA1_Update(&SHA, sha_value, HMAC_SIZE); + + SHA1_Final(sha_value, &SHA); + m_Final = true; +#endif // ASDCP_WITHOUT_OPENSSL + } +}; + + +HMACContext::HMACContext() +{ +} + +HMACContext::~HMACContext() +{ +} + + +// +Result_t +HMACContext::InitKey(const byte_t* key) +{ + ASDCP_TEST_NULL(key); + + m_Context = new h__HMACContext; + m_Context->SetKey(key); + return RESULT_OK; +} + + +// +void +HMACContext::Reset() +{ + if ( ! m_Context.empty() ) + m_Context->Reset(); +} + + +// +Result_t +HMACContext::Update(const byte_t* buf, ui32_t buf_len) +{ + ASDCP_TEST_NULL(buf); + + if ( m_Context.empty() || m_Context->m_Final ) + return RESULT_INIT; + + m_Context->Update(buf, buf_len); + return RESULT_OK; +} + + +// +Result_t +HMACContext::Finalize() +{ + if ( m_Context.empty() || m_Context->m_Final ) + return RESULT_INIT; + + m_Context->Finalize(); + return RESULT_OK; +} + + +// +Result_t +HMACContext::GetHMACValue(byte_t* buf) const +{ + ASDCP_TEST_NULL(buf); + + if ( m_Context.empty() || ! m_Context->m_Final ) + return RESULT_INIT; + + memcpy(buf, m_Context->sha_value, HMAC_SIZE); + return RESULT_OK; +} + + +// +Result_t +HMACContext::TestHMACValue(const byte_t* buf) const +{ + ASDCP_TEST_NULL(buf); + + if ( m_Context.empty() || ! m_Context->m_Final ) + return RESULT_INIT; + + return ( memcmp(buf, m_Context->sha_value, HMAC_SIZE) == 0 ) ? RESULT_OK : RESULT_HMACFAIL; +} + + + +// +// end AS_DCP_AES.cpp +// diff --git a/src/AS_DCP_JP2K.cpp b/src/AS_DCP_JP2K.cpp new file mode 100755 index 0000000..4262edf --- /dev/null +++ b/src/AS_DCP_JP2K.cpp @@ -0,0 +1,594 @@ +/* +Copyright (c) 2004-2006, 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_JP2k.cpp + \version $Id$ + \brief AS-DCP library, JPEG 2000 essence reader and writer implementation +*/ + +#include "AS_DCP_internal.h" +#include "MDD.h" +#include <assert.h> + + +//------------------------------------------------------------------------------------------ + +// +const byte_t JP2KEssenceCompressionLabel[klv_key_size] = +{ + 0x06, 0x0e, 0x2b, 0x34, 0x04, 0x01, 0x01, 0x09, + 0x04, 0x01, 0x02, 0x02, 0x03, 0x01, 0x01, 0x01 }; +#if 0 +// +ASDCP::Result_t +ASDCP::JP2K_PDesc_to_MD(JP2K::PictureDescriptor& PDesc, MDObject& PDescObj) +{ + char text_buf[32]; + + PDescObj.SetValue("Codec", DataChunk(klv_key_size, JP2KEssenceCompressionLabel)); + + sprintf(text_buf, "%ld/%ld", PDesc.EditRate.Numerator, PDesc.EditRate.Denominator); + PDescObj.SetString("SampleRate", text_buf); + + sprintf(text_buf, "%ld/%ld", PDesc.AspectRatio.Numerator, PDesc.AspectRatio.Denominator); + PDescObj.SetString("AspectRatio", text_buf); + + PDescObj.SetUint("FrameLayout", 0); + PDescObj.SetUint("StoredWidth", PDesc.StoredWidth); + PDescObj.SetUint("StoredHeight", PDesc.StoredHeight); + PDescObj.SetUint("ContainerDuration", PDesc.ContainerDuration); + + MDObject* PSubDescObj = GetMDObjectByType(PDescObj, "JPEG2000PictureSubDescriptor"); + + if ( PSubDescObj == 0 ) + { + DefaultLogSink().Error("Unable to locate JPEG2000PictureSubDescriptor"); + return RESULT_FALSE; + } + + PSubDescObj->SetUint("Rsize", PDesc.Rsize); + PSubDescObj->SetUint("Xsize", PDesc.Xsize); + PSubDescObj->SetUint("Ysize", PDesc.Ysize); + PSubDescObj->SetUint("XOsize", PDesc.XOsize); + PSubDescObj->SetUint("YOsize", PDesc.YOsize); + PSubDescObj->SetUint("XTsize", PDesc.XTsize); + PSubDescObj->SetUint("YTsize", PDesc.YTsize); + PSubDescObj->SetUint("XTOsize", PDesc.XTOsize); + PSubDescObj->SetUint("YTOsize", PDesc.YTOsize); + PSubDescObj->SetUint("Csize", PDesc.Csize); + + const ui32_t tmp_buffer_len = 64; + byte_t tmp_buffer[tmp_buffer_len]; + + *(ui32_t*)tmp_buffer = ASDCP_i32_BE(3L); // three components + *(ui32_t*)(tmp_buffer+4) = ASDCP_i32_BE(3L); + memcpy(tmp_buffer + 8, &PDesc.ImageComponents, sizeof(ASDCP::JP2K::ImageComponent) * 3L); + + PSubDescObj->SetValue("PictureComponentSizing", DataChunk(17, tmp_buffer)); + PSubDescObj->SetValue("CodingStyleDefault", DataChunk(PDesc.CodingStyleLength, PDesc.CodingStyle)); + PSubDescObj->SetValue("QuantizationDefault", DataChunk(PDesc.QuantDefaultLength, PDesc.QuantDefault)); + + return RESULT_OK; +} +#endif + +// +ASDCP::Result_t +ASDCP::MD_to_JP2K_PDesc(MXF::RGBAEssenceDescriptor* PDescObj, JP2K::PictureDescriptor& PDesc) +{ + ASDCP_TEST_NULL(PDescObj); + PDesc.CodingStyleLength = PDesc.QuantDefaultLength = 0; + +#if 0 + PDesc.StoredWidth = PDescObj.GetUint("StoredWidth"); + PDesc.StoredHeight = PDescObj.GetUint("StoredHeight"); + PDesc.ContainerDuration = PDescObj.GetUint("ContainerDuration"); + + // + MDObject* Ptr = PDescObj["SampleRate"]; // should be EditRate + + if ( Ptr ) + { + PDesc.EditRate.Numerator = Ptr->GetInt("Numerator"); + PDesc.EditRate.Denominator = Ptr->GetInt("Denominator"); + } + + // + Ptr = PDescObj["AspectRatio"]; + + if ( Ptr ) + { + PDesc.AspectRatio.Numerator = Ptr->GetInt("Numerator"); + PDesc.AspectRatio.Denominator = Ptr->GetInt("Denominator"); + } + + MDObject* PSubDescObj = GetMDObjectByType(PDescObj, "JPEG2000PictureSubDescriptor"); + + if ( PSubDescObj == 0 ) + { + DefaultLogSink().Error("Unable to locate JPEG2000PictureSubDescriptor"); + return RESULT_FALSE; + } + + PDesc.Rsize = PSubDescObj->GetUint("Rsize"); + PDesc.Xsize = PSubDescObj->GetUint("Xsize"); + PDesc.Ysize = PSubDescObj->GetUint("Ysize"); + PDesc.XOsize = PSubDescObj->GetUint("XOsize"); + PDesc.YOsize = PSubDescObj->GetUint("YOsize"); + PDesc.XTsize = PSubDescObj->GetUint("XTsize"); + PDesc.YTsize = PSubDescObj->GetUint("YTsize"); + PDesc.XTOsize = PSubDescObj->GetUint("XTOsize"); + PDesc.YTOsize = PSubDescObj->GetUint("YTOsize"); + PDesc.Csize = PSubDescObj->GetUint("Csize"); + + // + Ptr = (*PSubDescObj)["PictureComponentSizing"]; + + if ( Ptr ) + { + DataChunk DC3 = Ptr->GetData(); + + if ( DC3.Size == 17 ) // ( 2* sizeof(ui32_t) ) + 3 components * 3 byte each + { + memcpy(&PDesc.ImageComponents, DC3.Data + 8, DC3.Size - 8); + } + else + { + DefaultLogSink().Error("Unexpected PictureComponentSizing size: %lu, should be 17\n", DC3.Size); + } + } + + // + Ptr = (*PSubDescObj)["CodingStyleDefault"]; + + if ( Ptr ) + { + DataChunk DC1 = Ptr->GetData(); + PDesc.CodingStyleLength = DC1.Size; + memcpy(PDesc.CodingStyle, DC1.Data, DC1.Size); + } + + // + Ptr = (*PSubDescObj)["QuantizationDefault"]; + + if ( Ptr ) + { + DataChunk DC2 = Ptr->GetData(); + PDesc.QuantDefaultLength = DC2.Size; + memcpy(PDesc.QuantDefault, DC2.Data, DC2.Size); + } +#endif + return RESULT_OK; +} + + +void +ASDCP::JP2K::PictureDescriptorDump(const PictureDescriptor& PDesc, FILE* stream) +{ + if ( stream == 0 ) + stream = stderr; + + fprintf(stream, "\ + AspectRatio: %lu/%lu\n\ + EditRate: %lu/%lu\n\ + StoredWidth: %lu\n\ + StoredHeight: %lu\n\ + Rsize: %lu\n\ + Xsize: %lu\n\ + Ysize: %lu\n\ + XOsize: %lu\n\ + YOsize: %lu\n\ + XTsize: %lu\n\ + YTsize: %lu\n\ + XTOsize: %lu\n\ + YTOsize: %lu\n\ +ContainerDuration: %lu\n", + PDesc.AspectRatio.Numerator ,PDesc.AspectRatio.Denominator, + PDesc.EditRate.Numerator ,PDesc.EditRate.Denominator, + PDesc.StoredWidth, + PDesc.StoredHeight, + PDesc.Rsize, + PDesc.Xsize, + PDesc.Ysize, + PDesc.XOsize, + PDesc.YOsize, + PDesc.XTsize, + PDesc.YTsize, + PDesc.XTOsize, + PDesc.YTOsize, + PDesc.ContainerDuration + ); + + fprintf(stream, "Color Components:\n"); + + for ( ui32_t i = 0; i < PDesc.Csize; i++ ) + { + fprintf(stream, " %lu.%lu.%lu\n", + PDesc.ImageComponents[i].Ssize, + PDesc.ImageComponents[i].XRsize, + PDesc.ImageComponents[i].YRsize + ); + } + + const ui32_t tmp_buf_len = 256; + char tmp_buf[tmp_buf_len]; + + if ( PDesc.CodingStyleLength ) + fprintf(stream, "Default Coding (%lu): %s\n", + PDesc.CodingStyleLength, + bin2hex(PDesc.CodingStyle, PDesc.CodingStyleLength, + tmp_buf, tmp_buf_len) + ); + + if ( PDesc.QuantDefaultLength ) + fprintf(stream, "Default Coding (%lu): %s\n", + PDesc.QuantDefaultLength, + bin2hex(PDesc.QuantDefault, PDesc.QuantDefaultLength, + tmp_buf, tmp_buf_len) + ); +} + +//------------------------------------------------------------------------------------------ +// +// hidden, internal implementation of JPEG 2000 reader + +class ASDCP::JP2K::MXFReader::h__Reader : public ASDCP::h__Reader +{ + ASDCP_NO_COPY_CONSTRUCT(h__Reader); + +public: + PictureDescriptor m_PDesc; // codestream parameter list + + h__Reader() {} + Result_t OpenRead(const char*); + Result_t ReadFrame(ui32_t, FrameBuffer&, AESDecContext*, HMACContext*); + Result_t ReadFrameGOPStart(ui32_t, FrameBuffer&, AESDecContext*, HMACContext*); +}; + + +// +// +ASDCP::Result_t +ASDCP::JP2K::MXFReader::h__Reader::OpenRead(const char* filename) +{ + Result_t result = OpenMXFRead(filename); + + if( ASDCP_SUCCESS(result) ) + { + InterchangeObject* Object; + if ( ASDCP_SUCCESS(m_HeaderPart.GetMDObjectByType(OBJ_TYPE_ARGS(RGBAEssenceDescriptor), &Object)) ) + { + assert(Object); + result = MD_to_JP2K_PDesc((MXF::RGBAEssenceDescriptor*)Object, m_PDesc); + } + } + + if( ASDCP_SUCCESS(result) ) + result = InitMXFIndex(); + + if( ASDCP_SUCCESS(result) ) + result = InitInfo(m_Info); + + return result; +} + +// +// +ASDCP::Result_t +ASDCP::JP2K::MXFReader::h__Reader::ReadFrame(ui32_t FrameNum, FrameBuffer& FrameBuf, + AESDecContext* Ctx, HMACContext* HMAC) +{ + if ( ! m_File.IsOpen() ) + return RESULT_INIT; + + return ReadEKLVPacket(FrameNum, FrameBuf, JP2KEssenceUL_Data, Ctx, HMAC); +} + +//------------------------------------------------------------------------------------------ + + +// +void +ASDCP::JP2K::FrameBuffer::Dump(FILE* stream, ui32_t dump_len) const +{ + if ( stream == 0 ) + stream = stderr; + + fprintf(stream, "Frame: %06lu, %7lu bytes", m_FrameNumber, m_Size); + + fputc('\n', stream); + + if ( dump_len > 0 ) + hexdump(m_Data, dump_len, stream); +} + + +//------------------------------------------------------------------------------------------ + +ASDCP::JP2K::MXFReader::MXFReader() +{ + m_Reader = new h__Reader; +} + + +ASDCP::JP2K::MXFReader::~MXFReader() +{ +} + +// Open the file for reading. The file must exist. Returns error if the +// operation cannot be completed. +ASDCP::Result_t +ASDCP::JP2K::MXFReader::OpenRead(const char* filename) const +{ + return m_Reader->OpenRead(filename); +} + +// +ASDCP::Result_t +ASDCP::JP2K::MXFReader::ReadFrame(ui32_t FrameNum, FrameBuffer& FrameBuf, + AESDecContext* Ctx, HMACContext* HMAC) const +{ + if ( m_Reader && m_Reader->m_File.IsOpen() ) + return m_Reader->ReadFrame(FrameNum, FrameBuf, Ctx, HMAC); + + return RESULT_INIT; +} + + +// Fill the struct with the values from the file's header. +// Returns RESULT_INIT if the file is not open. +ASDCP::Result_t +ASDCP::JP2K::MXFReader::FillPictureDescriptor(PictureDescriptor& PDesc) const +{ + if ( m_Reader && m_Reader->m_File.IsOpen() ) + { + PDesc = m_Reader->m_PDesc; + return RESULT_OK; + } + + return RESULT_INIT; +} + + +// Fill the struct with the values from the file's header. +// Returns RESULT_INIT if the file is not open. +ASDCP::Result_t +ASDCP::JP2K::MXFReader::FillWriterInfo(WriterInfo& Info) const +{ + if ( m_Reader && m_Reader->m_File.IsOpen() ) + { + Info = m_Reader->m_Info; + return RESULT_OK; + } + + return RESULT_INIT; +} + +// +void +ASDCP::JP2K::MXFReader::DumpHeaderMetadata(FILE* stream) const +{ + if ( m_Reader->m_File.IsOpen() ) + m_Reader->m_HeaderPart.Dump(stream); +} + + +// +void +ASDCP::JP2K::MXFReader::DumpIndex(FILE* stream) const +{ + if ( m_Reader->m_File.IsOpen() ) + m_Reader->m_FooterPart.Dump(stream); +} + + +//------------------------------------------------------------------------------------------ + + +#if 0 +// +class ASDCP::JP2K::MXFWriter::h__Writer : public ASDCP::h__Writer +{ +public: + PictureDescriptor m_PDesc; + ui32_t m_GOPOffset; + + ASDCP_NO_COPY_CONSTRUCT(h__Writer); + + h__Writer() : m_GOPOffset(0) {} + ~h__Writer(){} + + Result_t OpenWrite(const char*, ui32_t HeaderSize); + Result_t SetSourceStream(const PictureDescriptor&); + Result_t WriteFrame(const FrameBuffer&, AESEncContext* = 0, HMACContext* = 0); + Result_t Finalize(); +}; + + +// Open the file for writing. The file must not exist. Returns error if +// the operation cannot be completed. +ASDCP::Result_t +ASDCP::JP2K::MXFWriter::h__Writer::OpenWrite(const char* filename, ui32_t HeaderSize) +{ + if ( ! m_State.Test_BEGIN() ) + return RESULT_STATE; + + m_File = new MXFFile; + + Result_t result = m_File.OpenWrite(filename); + + if ( ASDCP_SUCCESS(result) ) + { + m_EssenceDescriptor = new MDObject("RGBAEssenceDescriptor"); + MDObject* jp2kDesc = new MDObject("JPEG2000PictureSubDescriptor"); + MDObject* md01 = m_EssenceDescriptor->AddChild("SubDescriptors"); + + if ( md01 == 0 ) + { + DefaultLogSink().Error("Unable to locate JPEG2000PictureSubDescriptor, incomplete dictionary?\n"); + return RESULT_FAIL; + } + + MDObject* md02 = md01->AddChild("SubDescriptor"); + assert(md02); + md02->MakeLink(*jp2kDesc); + + result = m_State.Goto_INIT(); + } + + return result; +} + +// Automatically sets the MXF file's metadata from the first jpeg codestream stream. +ASDCP::Result_t +ASDCP::JP2K::MXFWriter::h__Writer::SetSourceStream(const PictureDescriptor& PDesc) +{ + if ( ! m_State.Test_INIT() ) + return RESULT_STATE; + + m_PDesc = PDesc; + Result_t result = JP2K_PDesc_to_MD(m_PDesc, *m_EssenceDescriptor); + + if ( ASDCP_SUCCESS(result) ) + result = WriteMXFHeader(ESS_JPEG_2000, m_PDesc.EditRate, 24 /* TCFrameRate */); + + if ( ASDCP_SUCCESS(result) ) + result = m_State.Goto_READY(); + + return result; +} + +// 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. +// +ASDCP::Result_t +ASDCP::JP2K::MXFWriter::h__Writer::WriteFrame(const FrameBuffer& FrameBuf, AESEncContext* Ctx, + HMACContext* HMAC) +{ + Result_t result = RESULT_OK; + + if ( m_State.Test_READY() ) + result = m_State.Goto_RUNNING(); // first time through + + ui64_t ThisOffset = m_StreamOffset; + + if ( ASDCP_SUCCESS(result) ) + result = WriteEKLVPacket(FrameBuf, JP2KEssenceUL_Data, Ctx, HMAC); + + if ( ASDCP_SUCCESS(result) ) + { + m_IndexMan->OfferEditUnit(0, m_FramesWritten, 0, 1); + m_IndexMan->OfferOffset(0, m_FramesWritten, ThisOffset); + m_FramesWritten++; + } + + return result; +} + + +// Closes the MXF file, writing the index and other closing information. +// +ASDCP::Result_t +ASDCP::JP2K::MXFWriter::h__Writer::Finalize() +{ + if ( ! m_State.Test_RUNNING() ) + return RESULT_STATE; + + if ( ! m_File ) + return RESULT_INIT; + + m_State.Goto_FINAL(); + + return WriteMXFFooter(ESS_JPEG_2000); +} + + +//------------------------------------------------------------------------------------------ + + + +ASDCP::JP2K::MXFWriter::MXFWriter() +{ +} + +ASDCP::JP2K::MXFWriter::~MXFWriter() +{ +} + + +// Open the file for writing. The file must not exist. Returns error if +// the operation cannot be completed. +ASDCP::Result_t +ASDCP::JP2K::MXFWriter::OpenWrite(const char* filename, const WriterInfo& Info, + const PictureDescriptor& PDesc, ui32_t HeaderSize) +{ + m_Writer = new h__Writer; + + Result_t result = m_Writer->OpenWrite(filename, HeaderSize); + + if ( ASDCP_SUCCESS(result) ) + { + m_Writer->m_Info = Info; + result = m_Writer->SetSourceStream(PDesc); + } + + if ( ASDCP_FAILURE(result) ) + m_Writer.release(); + + return result; +} + + +// 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. +ASDCP::Result_t +ASDCP::JP2K::MXFWriter::WriteFrame(const FrameBuffer& FrameBuf, AESEncContext* Ctx, HMACContext* HMAC) +{ + if ( m_Writer.empty() ) + return RESULT_INIT; + + return m_Writer->WriteFrame(FrameBuf, Ctx, HMAC); +} + +// Closes the MXF file, writing the index and other closing information. +ASDCP::Result_t +ASDCP::JP2K::MXFWriter::Finalize() +{ + if ( m_Writer.empty() ) + return RESULT_INIT; + + return m_Writer->Finalize(); +} + +#endif + +// +// end AS_DCP_JP2K.cpp +// diff --git a/src/AS_DCP_MPEG2.cpp b/src/AS_DCP_MPEG2.cpp new file mode 100755 index 0000000..a126583 --- /dev/null +++ b/src/AS_DCP_MPEG2.cpp @@ -0,0 +1,529 @@ +/* +Copyright (c) 2004-2006, 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_MPEG2.cpp + \version $Id$ + \brief AS-DCP library, MPEG2 essence reader and writer implementation +*/ + +#include "AS_DCP_internal.h" +#include "MDD.h" + + +//------------------------------------------------------------------------------------------ + +// +ASDCP::Result_t +ASDCP::MD_to_MPEG2_VDesc(MXF::MPEG2VideoDescriptor* VDescObj, MPEG2::VideoDescriptor& VDesc) +{ + ASDCP_TEST_NULL(VDescObj); + VDesc = *((MPEG2::VideoDescriptor*)VDescObj); + VDesc.FrameRate = 0; + return RESULT_OK; +} + + +// +void +ASDCP::MPEG2::VideoDescriptorDump(const VideoDescriptor& VDesc, FILE* stream) +{ + if ( stream == 0 ) + stream = stderr; + + fprintf(stream, "\ + SampleRate: %lu/%lu\n\ + FrameLayout: %lu\n\ + StoredWidth: %lu\n\ + StoredHeight: %lu\n\ + AspectRatio: %lu/%lu\n\ + ComponentDepth: %lu\n\ +HorizontalSubsampling: %lu\n\ + VerticalSubsampling: %lu\n\ + ColorSiting: %lu\n\ + CodedContentType: %lu\n\ + LowDelay: %lu\n\ + BitRate: %lu\n\ + ProfileAndLevel: %lu\n\ + ContainerDuration: %lu\n", + VDesc.SampleRate.Numerator ,VDesc.SampleRate.Denominator, + VDesc.FrameLayout, + VDesc.StoredWidth, + VDesc.StoredHeight, + VDesc.AspectRatio.Numerator ,VDesc.AspectRatio.Denominator, + VDesc.ComponentDepth, + VDesc.HorizontalSubsampling, + VDesc.VerticalSubsampling, + VDesc.ColorSiting, + VDesc.CodedContentType, + VDesc.LowDelay, + VDesc.BitRate, + VDesc.ProfileAndLevel, + VDesc.ContainerDuration + ); +} + +//------------------------------------------------------------------------------------------ +// +// hidden, internal implementation of MPEG2 reader + +class ASDCP::MPEG2::MXFReader::h__Reader : public ASDCP::h__Reader +{ + ASDCP_NO_COPY_CONSTRUCT(h__Reader); + +public: + VideoDescriptor m_VDesc; // video parameter list + + h__Reader() {} + ~h__Reader() {} + Result_t OpenRead(const char*); + Result_t ReadFrame(ui32_t, FrameBuffer&, AESDecContext*, HMACContext*); + Result_t ReadFrameGOPStart(ui32_t, FrameBuffer&, AESDecContext*, HMACContext*); + Result_t FindFrameGOPStart(ui32_t, ui32_t&); +}; + + +// +// +ASDCP::Result_t +ASDCP::MPEG2::MXFReader::h__Reader::OpenRead(const char* filename) +{ + Result_t result = OpenMXFRead(filename); + + if( ASDCP_SUCCESS(result) ) + { + InterchangeObject* Object; + if ( ASDCP_SUCCESS(m_HeaderPart.GetMDObjectByType(OBJ_TYPE_ARGS(MPEG2VideoDescriptor), &Object)) ) + { + assert(Object); + result = MD_to_MPEG2_VDesc((MXF::MPEG2VideoDescriptor*)Object, m_VDesc); + } + } + + if( ASDCP_SUCCESS(result) ) + result = InitMXFIndex(); + + if( ASDCP_SUCCESS(result) ) + result = InitInfo(m_Info); + + return result; +} + + +// +// +ASDCP::Result_t +ASDCP::MPEG2::MXFReader::h__Reader::ReadFrameGOPStart(ui32_t FrameNum, FrameBuffer& FrameBuf, + AESDecContext* Ctx, HMACContext* HMAC) +{ + ui32_t KeyFrameNum; + + Result_t result = FindFrameGOPStart(FrameNum, KeyFrameNum); + + if ( ASDCP_SUCCESS(result) ) + result = ReadFrame(KeyFrameNum, FrameBuf, Ctx, HMAC); + + return result; +} + + +// +// +ASDCP::Result_t +ASDCP::MPEG2::MXFReader::h__Reader::FindFrameGOPStart(ui32_t FrameNum, ui32_t& KeyFrameNum) +{ + KeyFrameNum = 0; + + if ( ! m_File.IsOpen() ) + return RESULT_INIT; + + // look up frame index node + IndexTableSegment::IndexEntry TmpEntry; + + if ( ASDCP_FAILURE(m_FooterPart.Lookup(FrameNum, TmpEntry)) ) + { + DefaultLogSink().Error("Frame value out of range: %lu\n", FrameNum); + return RESULT_RANGE; + } + + KeyFrameNum = FrameNum - TmpEntry.KeyFrameOffset; + + return RESULT_OK; +} + + +// +// +ASDCP::Result_t +ASDCP::MPEG2::MXFReader::h__Reader::ReadFrame(ui32_t FrameNum, FrameBuffer& FrameBuf, + AESDecContext* Ctx, HMACContext* HMAC) +{ + if ( ! m_File.IsOpen() ) + return RESULT_INIT; + + Result_t result = ReadEKLVPacket(FrameNum, FrameBuf, MPEGEssenceUL_Data, Ctx, HMAC); + + if ( ASDCP_FAILURE(result) ) + return result; + + IndexTableSegment::IndexEntry TmpEntry; + m_FooterPart.Lookup(FrameNum, TmpEntry); + + switch ( ( TmpEntry.Flags >> 4 ) & 0x03 ) + { + case 0: FrameBuf.FrameType(FRAME_I); break; + case 2: FrameBuf.FrameType(FRAME_P); break; + case 3: FrameBuf.FrameType(FRAME_B); break; + default: FrameBuf.FrameType(FRAME_U); + } + + FrameBuf.TemporalOffset(TmpEntry.TemporalOffset); + FrameBuf.GOPStart(TmpEntry.Flags & 0x40 ? true : false); + FrameBuf.ClosedGOP(TmpEntry.Flags & 0x80 ? true : false); + + return RESULT_OK; +} + +//------------------------------------------------------------------------------------------ + + +// +void +ASDCP::MPEG2::FrameBuffer::Dump(FILE* stream, ui32_t dump_len) const +{ + if ( stream == 0 ) + stream = stderr; + + fprintf(stream, "Frame: %06lu, %c%-2hu, %7lu bytes", + m_FrameNumber, FrameTypeChar(m_FrameType), m_TemporalOffset, m_Size); + + if ( m_GOPStart ) + fprintf(stream, " (start %s GOP)", ( m_ClosedGOP ? "closed" : "open")); + + fputc('\n', stream); + + if ( dump_len > 0 ) + hexdump(m_Data, dump_len, stream); +} + + +//------------------------------------------------------------------------------------------ + +ASDCP::MPEG2::MXFReader::MXFReader() +{ + m_Reader = new h__Reader; +} + + +ASDCP::MPEG2::MXFReader::~MXFReader() +{ +} + +// Open the file for reading. The file must exist. Returns error if the +// operation cannot be completed. +ASDCP::Result_t +ASDCP::MPEG2::MXFReader::OpenRead(const char* filename) const +{ + return m_Reader->OpenRead(filename); +} + +// +ASDCP::Result_t +ASDCP::MPEG2::MXFReader::ReadFrame(ui32_t FrameNum, FrameBuffer& FrameBuf, + AESDecContext* Ctx, HMACContext* HMAC) const +{ + if ( m_Reader && m_Reader->m_File.IsOpen() ) + return m_Reader->ReadFrame(FrameNum, FrameBuf, Ctx, HMAC); + + return RESULT_INIT; +} + + +// +ASDCP::Result_t +ASDCP::MPEG2::MXFReader::ReadFrameGOPStart(ui32_t FrameNum, FrameBuffer& FrameBuf, + AESDecContext* Ctx, HMACContext* HMAC) const +{ + if ( m_Reader && m_Reader->m_File.IsOpen() ) + return m_Reader->ReadFrameGOPStart(FrameNum, FrameBuf, Ctx, HMAC); + + return RESULT_INIT; +} + + +// +ASDCP::Result_t +ASDCP::MPEG2::MXFReader::FindFrameGOPStart(ui32_t FrameNum, ui32_t& KeyFrameNum) const +{ + if ( m_Reader && m_Reader->m_File.IsOpen() ) + return m_Reader->FindFrameGOPStart(FrameNum, KeyFrameNum); + + return RESULT_INIT; +} + + +// Fill the struct with the values from the file's header. +// Returns RESULT_INIT if the file is not open. +ASDCP::Result_t +ASDCP::MPEG2::MXFReader::FillVideoDescriptor(VideoDescriptor& VDesc) const +{ + if ( m_Reader && m_Reader->m_File.IsOpen() ) + { + VDesc = m_Reader->m_VDesc; + return RESULT_OK; + } + + return RESULT_INIT; +} + + +// Fill the struct with the values from the file's header. +// Returns RESULT_INIT if the file is not open. +ASDCP::Result_t +ASDCP::MPEG2::MXFReader::FillWriterInfo(WriterInfo& Info) const +{ + if ( m_Reader && m_Reader->m_File.IsOpen() ) + { + Info = m_Reader->m_Info; + return RESULT_OK; + } + + return RESULT_INIT; +} + +// +void +ASDCP::MPEG2::MXFReader::DumpHeaderMetadata(FILE* stream) const +{ + if ( m_Reader->m_File.IsOpen() ) + m_Reader->m_HeaderPart.Dump(stream); +} + + +// +void +ASDCP::MPEG2::MXFReader::DumpIndex(FILE* stream) const +{ + if ( m_Reader->m_File.IsOpen() ) + m_Reader->m_FooterPart.Dump(stream); +} + + +//------------------------------------------------------------------------------------------ +#if 0 + + +// +class ASDCP::MPEG2::MXFWriter::h__Writer : public ASDCP::h__Writer +{ +public: + VideoDescriptor m_VDesc; + ui32_t m_GOPOffset; + + ASDCP_NO_COPY_CONSTRUCT(h__Writer); + + h__Writer() : m_GOPOffset(0) {} + ~h__Writer(){} + + Result_t OpenWrite(const char*, ui32_t HeaderSize); + Result_t SetSourceStream(const VideoDescriptor&); + Result_t WriteFrame(const FrameBuffer&, AESEncContext* = 0, HMACContext* = 0); + Result_t Finalize(); +}; + + +// Open the file for writing. The file must not exist. Returns error if +// the operation cannot be completed. +ASDCP::Result_t +ASDCP::MPEG2::MXFWriter::h__Writer::OpenWrite(const char* filename, ui32_t HeaderSize) +{ + if ( ! m_State.Test_BEGIN() ) + return RESULT_STATE; + + m_File = new MXFFile; + + Result_t result = m_File->OpenWrite(filename); + + if ( ASDCP_SUCCESS(result) ) + { + m_EssenceDescriptor = new MDObject("MPEG2VideoDescriptor"); + result = m_State.Goto_INIT(); + } + + return result; +} + +// Automatically sets the MXF file's metadata from the MPEG stream. +ASDCP::Result_t +ASDCP::MPEG2::MXFWriter::h__Writer::SetSourceStream(const VideoDescriptor& VDesc) +{ + if ( ! m_State.Test_INIT() ) + return RESULT_STATE; + + m_VDesc = VDesc; + Result_t result = MPEG2_VDesc_to_MD(m_VDesc, *m_EssenceDescriptor); + + if ( ASDCP_SUCCESS(result) ) + result = WriteMXFHeader(ESS_MPEG2_VES, m_VDesc.EditRate, 24 /* TCFrameRate */); + + if ( ASDCP_SUCCESS(result) ) + result = m_State.Goto_READY(); + + return result; +} + +// 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. +// +ASDCP::Result_t +ASDCP::MPEG2::MXFWriter::h__Writer::WriteFrame(const FrameBuffer& FrameBuf, AESEncContext* Ctx, + HMACContext* HMAC) +{ + Result_t result = RESULT_OK; + + if ( m_State.Test_READY() ) + result = m_State.Goto_RUNNING(); // first time through, get the body location + + ui64_t ThisOffset = m_StreamOffset; + + if ( ASDCP_SUCCESS(result) ) + result = WriteEKLVPacket(FrameBuf, MPEGEssenceUL_Data, Ctx, HMAC); + + if ( ASDCP_FAILURE(result) ) + return result; + + // create mxflib flags + int Flags = 0; + + switch ( FrameBuf.FrameType() ) + { + case FRAME_I: Flags = 0x00; break; + case FRAME_P: Flags = 0x22; break; + case FRAME_B: Flags = 0x33; break; + } + + if ( FrameBuf.GOPStart() ) + { + m_GOPOffset = 0; + Flags |= 0x40; + + if ( FrameBuf.ClosedGOP() ) + Flags |= 0x80; + } + + // update the index manager + m_IndexMan->OfferEditUnit(0, m_FramesWritten, m_GOPOffset, Flags); + m_IndexMan->OfferTemporalOffset(m_FramesWritten, m_GOPOffset - FrameBuf.TemporalOffset()); + m_IndexMan->OfferOffset(0, m_FramesWritten, ThisOffset); + + m_FramesWritten++; + m_GOPOffset++; + + return RESULT_OK; +} + + +// Closes the MXF file, writing the index and other closing information. +// +ASDCP::Result_t +ASDCP::MPEG2::MXFWriter::h__Writer::Finalize() +{ + if ( ! m_State.Test_RUNNING() ) + return RESULT_STATE; + + if ( ! m_File ) + return RESULT_INIT; + + m_State.Goto_FINAL(); + + return WriteMXFFooter(ESS_MPEG2_VES); +} + + +//------------------------------------------------------------------------------------------ + + + +ASDCP::MPEG2::MXFWriter::MXFWriter() +{ +} + +ASDCP::MPEG2::MXFWriter::~MXFWriter() +{ +} + + +// Open the file for writing. The file must not exist. Returns error if +// the operation cannot be completed. +ASDCP::Result_t +ASDCP::MPEG2::MXFWriter::OpenWrite(const char* filename, const WriterInfo& Info, + const VideoDescriptor& VDesc, ui32_t HeaderSize) +{ + m_Writer = new h__Writer; + + Result_t result = m_Writer->OpenWrite(filename, HeaderSize); + + if ( ASDCP_SUCCESS(result) ) + { + m_Writer->m_Info = Info; + result = m_Writer->SetSourceStream(VDesc); + } + + if ( ASDCP_FAILURE(result) ) + m_Writer.release(); + + return result; +} + + +// 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. +ASDCP::Result_t +ASDCP::MPEG2::MXFWriter::WriteFrame(const FrameBuffer& FrameBuf, AESEncContext* Ctx, HMACContext* HMAC) +{ + if ( m_Writer.empty() ) + return RESULT_INIT; + + return m_Writer->WriteFrame(FrameBuf, Ctx, HMAC); +} + +// Closes the MXF file, writing the index and other closing information. +ASDCP::Result_t +ASDCP::MPEG2::MXFWriter::Finalize() +{ + if ( m_Writer.empty() ) + return RESULT_INIT; + + return m_Writer->Finalize(); +} +#endif + +// +// end AS_DCP_MPEG2.cpp +// diff --git a/src/AS_DCP_MXF.cpp b/src/AS_DCP_MXF.cpp new file mode 100755 index 0000000..a7cb1e3 --- /dev/null +++ b/src/AS_DCP_MXF.cpp @@ -0,0 +1,598 @@ +/* +Copyright (c) 2004-2005, 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_MXF.cpp + \version $Id$ + \brief AS-DCP library, misc classes and subroutines +*/ + +#include "AS_DCP_internal.h" +#include "FileIO.h" +#include "DirScanner.h" +#include "JP2K.h" +#include "Wav.h" +#include "MDD.h" + + +//------------------------------------------------------------------------------------------ +// misc subroutines + + +// helper class for XXX below +// +class PathSplitter +{ +public: + char* Root; + char* SubPath; + + PathSplitter(const std::string Str) + { + Root = strdup(Str.c_str()); + assert(Root); + + // sub-path exists? + SubPath = strchr(Root, OBJECT_PATH_SEPARATOR); + + if ( SubPath ) + { + while ( SubPath[1] == OBJECT_PATH_SEPARATOR ) + SubPath++; + + *SubPath++ = 0; + + if ( *SubPath == 0 ) + SubPath = 0; + } + } + + ~PathSplitter() { + free(Root); + } +}; + +// +void +ASDCP::WriterInfoDump(const WriterInfo& Info, FILE* stream) +{ + if ( stream == 0 ) + stream = stderr; + + char str_buf[40]; + + fprintf(stream," ProductUUID: %s\n", bin2hex(Info.ProductUUID, 16, str_buf, 40)); + fprintf(stream,"\ + ProductVersion: %s\n\ + CompanyName: %s\n\ + ProductName: %s\n\ + EncryptedEssence: %s\n", + Info.ProductVersion.c_str(), + Info.CompanyName.c_str(), + Info.ProductName.c_str(), + ( Info.EncryptedEssence ? "Yes" : "No" ) + ); + + if ( Info.EncryptedEssence ) + { + fprintf(stream, " HMAC: %s\n", ( Info.UsesHMAC ? "Yes" : "No")); + fprintf(stream, " ContextID: %s\n", bin2hex(Info.ContextID, 16, str_buf, 40)); + fprintf(stream, "CryptographicKeyID: %s\n", bin2hex(Info.CryptographicKeyID, 16, str_buf, 40)); + } + + fprintf(stream," AssetUUID: %s\n", bin2hex(Info.AssetUUID, 16, str_buf, 40)); +} + +// +Result_t +ASDCP::MD_to_WriterInfo(Identification* InfoObj, WriterInfo& Info) +{ + ASDCP_TEST_NULL(InfoObj); + char tmp_str[IdentBufferLen]; + + Info.ProductName = "Unknown Product"; + Info.ProductVersion = "Unknown Version"; + Info.CompanyName = "Unknown Company"; + memset(Info.ProductUUID, 0, UUIDlen); + + InfoObj->ProductName.ToString(tmp_str); + if ( *tmp_str ) Info.ProductName = tmp_str; + + InfoObj->VersionString.ToString(tmp_str); + if ( *tmp_str ) Info.ProductVersion = tmp_str; + + InfoObj->CompanyName.ToString(tmp_str); + if ( *tmp_str ) Info.CompanyName = tmp_str; + + memcpy(Info.ProductUUID, InfoObj->ProductUID.Data(), UUIDlen); + + return RESULT_OK; +} + + +// +Result_t +ASDCP::MD_to_CryptoInfo(CryptographicContext* InfoObj, WriterInfo& Info) +{ + ASDCP_TEST_NULL(InfoObj); + + Info.EncryptedEssence = true; + memcpy(Info.ContextID, InfoObj->ContextID.Data(), UUIDlen); + memcpy(Info.CryptographicKeyID, InfoObj->CryptographicKeyID.Data(), UUIDlen); + + UL MIC_SHA1(MICAlgorithm_HMAC_SHA1); + UL MIC_NONE(MICAlgorithm_NONE); + + if ( InfoObj->MICAlgorithm == MIC_SHA1 ) + Info.UsesHMAC = true; + + else if ( InfoObj->MICAlgorithm == MIC_NONE ) + Info.UsesHMAC = false; + + else + { + DefaultLogSink().Error("Unexpected MICAlgorithm UL.\n"); + return RESULT_FORMAT; + } + + return RESULT_OK; +} + +#if 0 + + +// +// add DMS CryptographicFramework entry to source package +void +ASDCP::AddDMScrypt(PackagePtr SourcePackage, WriterInfo& Descr, const byte_t* SourceEssenceContainerLabel) +{ + assert(SourceEssenceContainerLabel); + + TrackPtr MPDMTrack = SourcePackage->AddDMTrack(); // zero parameters = static + DMSegmentPtr MPDMSegment = MPDMTrack->AddDMSegment(); + + MDObject* Crypto_DMS_Ptr = new MDObject("CryptographicFramework"); + MPDMSegment->AddChild("DMFramework")->MakeLink(*Crypto_DMS_Ptr); + + MDObject* Crypto_DMS_BasicPtr = new MDObject("CryptographicContext"); + Crypto_DMS_Ptr->AddChild("ContextSR")->MakeLink(*Crypto_DMS_BasicPtr); + + UUID ContextID(Descr.ContextID); + Crypto_DMS_BasicPtr->SetValue("ContextID", DataChunk(UUIDlen, ContextID.GetValue())); // UUID + Crypto_DMS_BasicPtr->SetValue("SourceEssenceContainer", + DataChunk(klv_key_size, SourceEssenceContainerLabel)); // Label + Crypto_DMS_BasicPtr->SetValue("CipherAlgorithm", DataChunk(klv_key_size, CipherAlgorithm_AES)); // UL Key + + Crypto_DMS_BasicPtr->SetValue("MICAlgorithm", + DataChunk(KeyLen, + (Descr.UsesHMAC ? + MICAlgorithm_HMAC_SHA1 + : MICAlgorithm_NONE))); // UL Key + + UUID CryptographicKeyID(Descr.CryptographicKeyID); + + Crypto_DMS_BasicPtr->SetValue("CryptographicKeyID", DataChunk(UUIDlen, CryptographicKeyID.GetValue())); // UUID +} + + +// +// +ASDCP::Result_t +ASDCP::FindObject(const char* filename, const char* objname, FILE* stream) +{ + ASDCP_TEST_NULL_STR(filename); + ASDCP_TEST_NULL_STR(objname); + + if ( stream == 0 ) + stream = stderr; + + ASDCP::h__Reader Reader; + Result_t result = Reader.OpenMXFRead(filename); + + if ( ASDCP_FAILURE(result) ) + return result; + + MDObject* DescObj = Reader.GetMDObjectByType(objname); + + if ( DescObj ) + { + DumpMDObject(*DescObj, " ", stream); + return RESULT_OK; + } + + return RESULT_FAIL; +} +#endif + +// +// +ASDCP::Result_t +ASDCP::EssenceType(const char* filename, EssenceType_t& type) +{ + ASDCP_TEST_NULL_STR(filename); + FileReader Reader; + OPAtomHeader TestHeader; + + Result_t result = Reader.OpenRead(filename); + + if ( ASDCP_SUCCESS(result) ) + result = TestHeader.InitFromFile(Reader); // test UL and OP + + if ( ASDCP_SUCCESS(result) ) + { + type = ESS_UNKNOWN; + if ( ASDCP_SUCCESS(TestHeader.GetMDObjectByType(OBJ_TYPE_ARGS(JPEG2000PictureSubDescriptor))) ) + type = ESS_JPEG_2000; + else + { + if ( ASDCP_SUCCESS(TestHeader.GetMDObjectByType(OBJ_TYPE_ARGS(WaveAudioDescriptor))) ) + type = ESS_PCM_24b_48k; + else + { + if ( ASDCP_SUCCESS(TestHeader.GetMDObjectByType(OBJ_TYPE_ARGS(MPEG2VideoDescriptor))) ) + type = ESS_MPEG2_VES; + } + } + } + + return result; +} + + +// +ASDCP::Result_t +ASDCP::RawEssenceType(const char* filename, EssenceType_t& type) +{ + ASDCP_TEST_NULL_STR(filename); + type = ESS_UNKNOWN; + ASDCP::FrameBuffer FB; + FileReader Reader; + ui32_t read_count; + Result_t result = FB.Capacity(Wav::MaxWavHeader); // using Wav max because everything else is much smaller + + if ( ASDCP::PathIsFile(filename) ) + { + result = Reader.OpenRead(filename); + + if ( ASDCP_SUCCESS(result) ) + { + result = Reader.Read(FB.Data(), FB.Capacity(), &read_count); + Reader.Close(); + } + + if ( ASDCP_SUCCESS(result) ) + { + ASDCP::Wav::SimpleWaveHeader WavHeader; + ui32_t data_offset; + const byte_t* p = FB.RoData(); + + if ( p[0] == 0 && p[1] == 0 && p[2] == 1 && (p[3] == 0xb3 || p[3] == 0) ) + type = ESS_MPEG2_VES; + + else if ( ASDCP_SUCCESS(WavHeader.ReadFromBuffer(p, read_count, &data_offset)) ) + type = ESS_PCM_24b_48k; + } + } + else if ( ASDCP::PathIsDirectory(filename) ) + { + char next_file[ASDCP_MAX_PATH]; + DirScanner Scanner; + Result_t result = Scanner.Open(filename); + + if ( ASDCP_SUCCESS(result) ) + { + while ( ASDCP_SUCCESS(Scanner.GetNext(next_file)) ) + { + if ( next_file[0] == '.' ) // no hidden files or internal links + continue; + + std::string Str(filename); + Str += "/"; + Str += next_file; + result = Reader.OpenRead(Str.c_str()); + + if ( ASDCP_SUCCESS(result) ) + { + result = Reader.Read(FB.Data(), FB.Capacity(), &read_count); + Reader.Close(); + } + + if ( ASDCP_SUCCESS(result) + && ( memcmp(FB.RoData(), ASDCP::JP2K::Magic, sizeof(ASDCP::JP2K::Magic)) == 0 ) ) + type = ESS_JPEG_2000; + + break; + } + } + } + + return result; +} + +// +Result_t +ASDCP::EncryptFrameBuffer(const ASDCP::FrameBuffer& FBin, ASDCP::FrameBuffer& FBout, AESEncContext* Ctx) +{ + ASDCP_TEST_NULL(Ctx); + FBout.Size(0); + + // size the buffer + Result_t result = FBout.Capacity(calc_esv_length(FBin.Size(), FBin.PlaintextOffset())); + + // write the IV + byte_t* p = FBout.Data(); + + // write the IV to the frame buffer + Ctx->GetIVec(p); + p += CBC_BLOCK_SIZE; + + + // encrypt the check value to the frame buffer + if ( ASDCP_SUCCESS(result) ) + { + result = Ctx->EncryptBlock(ESV_CheckValue, p, CBC_BLOCK_SIZE); + p += CBC_BLOCK_SIZE; + } + + // write optional plaintext region + if ( FBin.PlaintextOffset() > 0 ) + { + assert(FBin.PlaintextOffset() <= FBin.Size()); + memcpy(p, FBin.RoData(), FBin.PlaintextOffset()); + p += FBin.PlaintextOffset(); + } + + ui32_t ct_size = FBin.Size() - FBin.PlaintextOffset(); + ui32_t diff = ct_size % CBC_BLOCK_SIZE; + ui32_t block_size = ct_size - diff; + assert((block_size % CBC_BLOCK_SIZE) == 0); + + // encrypt the ciphertext region essence data + if ( ASDCP_SUCCESS(result) ) + { + result = Ctx->EncryptBlock(FBin.RoData() + FBin.PlaintextOffset(), p, block_size); + p += block_size; + } + + // construct and encrypt the padding + if ( ASDCP_SUCCESS(result) ) + { + byte_t the_last_block[CBC_BLOCK_SIZE]; + + if ( diff > 0 ) + memcpy(the_last_block, FBin.RoData() + FBin.PlaintextOffset() + block_size, diff); + + for (ui32_t i = 0; diff < CBC_BLOCK_SIZE; diff++, i++ ) + the_last_block[diff] = i; + + result = Ctx->EncryptBlock(the_last_block, p, CBC_BLOCK_SIZE); + } + + if ( ASDCP_SUCCESS(result) ) + FBout.Size(calc_esv_length(FBin.Size(), FBin.PlaintextOffset())); + + return result; +} + +// +Result_t +ASDCP::DecryptFrameBuffer(const ASDCP::FrameBuffer& FBin, ASDCP::FrameBuffer& FBout, AESDecContext* Ctx) +{ + ASDCP_TEST_NULL(Ctx); + assert(FBout.Capacity() >= FBin.SourceLength()); + + ui32_t ct_size = FBin.SourceLength() - FBin.PlaintextOffset(); + ui32_t diff = ct_size % CBC_BLOCK_SIZE; + ui32_t block_size = ct_size - diff; + assert(block_size); + assert((block_size % CBC_BLOCK_SIZE) == 0); + + const byte_t* buf = FBin.RoData(); + + // get ivec + Ctx->SetIVec(buf); + buf += CBC_BLOCK_SIZE; + + // decrypt and test check value + byte_t CheckValue[CBC_BLOCK_SIZE]; + Result_t result = Ctx->DecryptBlock(buf, CheckValue, CBC_BLOCK_SIZE); + buf += CBC_BLOCK_SIZE; + + if ( memcmp(CheckValue, ESV_CheckValue, CBC_BLOCK_SIZE) != 0 ) + return RESULT_CHECKFAIL; + + // copy plaintext region + if ( FBin.PlaintextOffset() > 0 ) + { + memcpy(FBout.Data(), buf, FBin.PlaintextOffset()); + buf += FBin.PlaintextOffset(); + } + + // decrypt all but last block + if ( ASDCP_SUCCESS(result) ) + { + result = Ctx->DecryptBlock(buf, FBout.Data() + FBin.PlaintextOffset(), block_size); + buf += block_size; + } + + // decrypt last block + if ( ASDCP_SUCCESS(result) ) + { + byte_t the_last_block[CBC_BLOCK_SIZE]; + result = Ctx->DecryptBlock(buf, the_last_block, CBC_BLOCK_SIZE); + + if ( the_last_block[diff] != 0 ) + { + DefaultLogSink().Error("Unexpected non-zero padding value.\n"); + return RESULT_FORMAT; + } + + if ( diff > 0 ) + memcpy(FBout.Data() + FBin.PlaintextOffset() + block_size, the_last_block, diff); + } + + if ( ASDCP_SUCCESS(result) ) + FBout.Size(FBin.SourceLength()); + + return result; +} + + +// +Result_t +ASDCP::IntegrityPack::CalcValues(const ASDCP::FrameBuffer& FB, byte_t* AssetID, + ui32_t sequence, HMACContext* HMAC) +{ + ASDCP_TEST_NULL(AssetID); + ASDCP_TEST_NULL(HMAC); + byte_t* p = Data; + HMAC->Reset(); + + static byte_t ber_4[klv_length_size] = {0x83, 0}; + + // update HMAC with essence data + HMAC->Update(FB.RoData(), FB.Size()); + + // track file ID length + memcpy(p, ber_4, klv_length_size); + *(p+3) = UUIDlen;; + p += klv_length_size; + + // track file ID + memcpy(p, AssetID, UUIDlen); + p += UUIDlen; + + // sequence length + memcpy(p, ber_4, klv_length_size); + *(p+3) = sizeof(ui64_t); + p += klv_length_size; + + // sequence number + i2p<ui64_t>(ASDCP_i64_BE(sequence), p); + p += sizeof(ui64_t); + + // HMAC length + memcpy(p, ber_4, klv_length_size); + *(p+3) = HMAC_SIZE; + p += klv_length_size; + + // update HMAC with intpack values + HMAC->Update(Data, klv_intpack_size - HMAC_SIZE); + + // finish & write HMAC + HMAC->Finalize(); + HMAC->GetHMACValue(p); + + assert(p + HMAC_SIZE == Data + klv_intpack_size); + + return RESULT_OK; +} + + +Result_t +ASDCP::IntegrityPack::TestValues(const ASDCP::FrameBuffer& FB, byte_t* AssetID, + ui32_t sequence, HMACContext* HMAC) +{ + ASDCP_TEST_NULL(AssetID); + ASDCP_TEST_NULL(HMAC); + + // find the start of the intpack + byte_t* p = (byte_t*)FB.RoData() + ( FB.Size() - klv_intpack_size ); + + // test the AssetID length + if ( ! read_test_BER(&p, UUIDlen) ) + return RESULT_HMACFAIL; + + // test the AssetID + if ( memcmp(p, AssetID, UUIDlen) != 0 ) + { + DefaultLogSink().Error("IntegrityPack failure: AssetID mismatch.\n"); + return RESULT_HMACFAIL; + } + p += UUIDlen; + + // test the sequence length + if ( ! read_test_BER(&p, sizeof(ui64_t)) ) + return RESULT_HMACFAIL; + + ui32_t test_sequence = (ui32_t)ASDCP_i64_BE(cp2i<ui64_t>(p)); + + // test the sequence value + if ( test_sequence != sequence ) + { + DefaultLogSink().Error("IntegrityPack failure: sequence is %lu, expecting %lu.\n", test_sequence, sequence); + return RESULT_HMACFAIL; + } + + p += sizeof(ui64_t); + + // test the HMAC length + if ( ! read_test_BER(&p, HMAC_SIZE) ) + return RESULT_HMACFAIL; + + // test the HMAC + HMAC->Reset(); + HMAC->Update(FB.RoData(), FB.Size() - HMAC_SIZE); + HMAC->Finalize(); + + return HMAC->TestHMACValue(p); +} + +//------------------------------------------------------------------------------------------ +// + + +// +ASDCP::Result_t +ASDCP::KLVReader::ReadKLFromFile(ASDCP::FileReader& Reader) +{ + ui32_t read_count; + m_HeaderLength = klv_key_size + klv_length_size; + Result_t result = Reader.Read(m_Key, m_HeaderLength, &read_count); + assert(read_count == m_HeaderLength); + + if ( ASDCP_SUCCESS(result) ) + { + m_BERLength = BER_length(m_Key + klv_key_size); + + if ( m_BERLength != klv_length_size ) + { + ASDCP::DefaultLogSink().Error("Found packet with BER length %lu; being less efficient...\n", + m_BERLength); + // TODO: recover the correct BER value + // and reposition the file pointer + assert(0); + } + + if ( ! read_BER(m_Key + klv_key_size, &m_Length) ) + return RESULT_FAIL; + } + + return result; +} + +// +// end AS_DCP_MXF.cpp +// diff --git a/src/AS_DCP_PCM.cpp b/src/AS_DCP_PCM.cpp new file mode 100755 index 0000000..3f019c1 --- /dev/null +++ b/src/AS_DCP_PCM.cpp @@ -0,0 +1,475 @@ +/* +Copyright (c) 2004-2005, 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_PCM.cpp + \version $Id$ + \brief AS-DCP library, PCM essence reader and writer implementation +*/ + +#include "AS_DCP_internal.h" +#include "MDD.h" +#include <stdio.h> +#include <stdarg.h> +#include <map> + +//------------------------------------------------------------------------------------------ + +// +ASDCP::Result_t +ASDCP::MD_to_PCM_ADesc(MXF::WaveAudioDescriptor* ADescObj, PCM::AudioDescriptor& ADesc) +{ + ASDCP_TEST_NULL(ADescObj); + ADesc.SampleRate = ADescObj->SampleRate; + ADesc.AudioSamplingRate = ADescObj->AudioSamplingRate; + ADesc.Locked = ADescObj->Locked; + ADesc.ChannelCount = ADescObj->ChannelCount; + ADesc.QuantizationBits = ADescObj->QuantizationBits; + ADesc.BlockAlign = ADescObj->BlockAlign; + ADesc.AvgBps = ADescObj->AvgBps; + ADesc.LinkedTrackID = ADescObj->LinkedTrackID; + ADesc.ContainerDuration = ADescObj->ContainerDuration; + return RESULT_OK; +} + +void +ASDCP::PCM::AudioDescriptorDump(const AudioDescriptor& ADesc, FILE* stream) +{ + if ( stream == 0 ) + stream = stderr; + + fprintf(stream, "\ + SampleRate: %lu/%lu\n\ + AudioSamplingRate: %lu/%lu\n\ + Locked: %lu\n\ + ChannelCount: %lu\n\ + QuantizationBits: %lu\n\ + BlockAlign: %lu\n\ + AvgBps: %lu\n\ + LinkedTrackID: %lu\n\ + ContainerDuration: %lu\n", + ADesc.SampleRate.Numerator ,ADesc.SampleRate.Denominator, + ADesc.AudioSamplingRate.Numerator ,ADesc.AudioSamplingRate.Denominator, + ADesc.Locked, + ADesc.ChannelCount, + ADesc.QuantizationBits, + ADesc.BlockAlign, + ADesc.AvgBps, + ADesc.LinkedTrackID, + ADesc.ContainerDuration + ); +} + + +// +// +static ui32_t +calc_CBR_frame_size(ASDCP::WriterInfo& Info, const ASDCP::PCM::AudioDescriptor& ADesc) +{ + ui32_t CBR_frame_size = 0; + + if ( Info.EncryptedEssence ) + { + CBR_frame_size = + klv_key_size + + klv_length_size + + klv_cryptinfo_size + + calc_esv_length(ASDCP::PCM::CalcFrameBufferSize(ADesc), 0) + + ( Info.UsesHMAC ? klv_intpack_size : (klv_length_size * 3) ); + } + else + { + CBR_frame_size = ASDCP::PCM::CalcFrameBufferSize(ADesc) + klv_key_size + klv_length_size; + } + + return CBR_frame_size; +} + + +//------------------------------------------------------------------------------------------ + + +class ASDCP::PCM::MXFReader::h__Reader : public ASDCP::h__Reader +{ + ASDCP_NO_COPY_CONSTRUCT(h__Reader); + +public: + AudioDescriptor m_ADesc; + + h__Reader() {} + ~h__Reader() {} + Result_t OpenRead(const char*); + Result_t ReadFrame(ui32_t, FrameBuffer&, AESDecContext*, HMACContext*); +}; + + +// +// +ASDCP::Result_t +ASDCP::PCM::MXFReader::h__Reader::OpenRead(const char* filename) +{ + Result_t result = OpenMXFRead(filename); + + if( ASDCP_SUCCESS(result) ) + { + InterchangeObject* Object; + if ( ASDCP_SUCCESS(m_HeaderPart.GetMDObjectByType(OBJ_TYPE_ARGS(WaveAudioDescriptor), &Object)) ) + { + assert(Object); + result = MD_to_PCM_ADesc((MXF::WaveAudioDescriptor*)Object, m_ADesc); + } + } + + // check for sample/frame rate sanity + if ( m_ADesc.SampleRate != EditRate_24 + && m_ADesc.SampleRate != EditRate_48 + && m_ADesc.SampleRate != EditRate_23_98 ) + { + DefaultLogSink().Error("PCM file SampleRate is not 24/1, 48/1 or 24000/1001: %08x/%08x\n", // lu + m_ADesc.SampleRate.Numerator, m_ADesc.SampleRate.Denominator); + + // oh, they gave us the audio sampling rate instead, assume 24/1 + if ( m_ADesc.SampleRate == SampleRate_48k ) + { + DefaultLogSink().Warn("adjusting SampleRate to 24/1\n"); + m_ADesc.SampleRate.Numerator = 24; + m_ADesc.SampleRate.Denominator = 1; + } + else + { + // or we just drop the hammer + return RESULT_FORMAT; + } + } + + if( ASDCP_SUCCESS(result) ) + result = InitMXFIndex(); + + if( ASDCP_SUCCESS(result) ) + result = InitInfo(m_Info); + + // TODO: test file for sane CBR index BytesPerEditUnit + + return result; +} + + +// +// +ASDCP::Result_t +ASDCP::PCM::MXFReader::h__Reader::ReadFrame(ui32_t FrameNum, FrameBuffer& FrameBuf, + AESDecContext* Ctx, HMACContext* HMAC) +{ + if ( ! m_File.IsOpen() ) + return RESULT_INIT; + + return ReadEKLVPacket(FrameNum, FrameBuf, WAVEssenceUL_Data, Ctx, HMAC); +} + +//------------------------------------------------------------------------------------------ + + +// +void +ASDCP::PCM::FrameBuffer::Dump(FILE* stream, ui32_t dump_len) const +{ + if ( stream == 0 ) + stream = stderr; + + fprintf(stream, "Frame: %06lu, %7lu bytes\n", + m_FrameNumber, m_Size); + + if ( dump_len ) + hexdump(m_Data, dump_len, stream); +} + +//------------------------------------------------------------------------------------------ + +ASDCP::PCM::MXFReader::MXFReader() +{ + m_Reader = new h__Reader; +} + + +ASDCP::PCM::MXFReader::~MXFReader() +{ + if ( m_Reader && m_Reader->m_File.IsOpen() ) + m_Reader->Close(); +} + +// Open the file for reading. The file must exist. Returns error if the +// operation cannot be completed. +ASDCP::Result_t +ASDCP::PCM::MXFReader::OpenRead(const char* filename) const +{ + return m_Reader->OpenRead(filename); +} + +// 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. +ASDCP::Result_t +ASDCP::PCM::MXFReader::ReadFrame(ui32_t FrameNum, FrameBuffer& FrameBuf, + AESDecContext* Ctx, HMACContext* HMAC) const +{ + if ( m_Reader && m_Reader->m_File.IsOpen() ) + return m_Reader->ReadFrame(FrameNum, FrameBuf, Ctx, HMAC); + + return RESULT_INIT; +} + + +// Fill the struct with the values from the file's header. +// Returns RESULT_INIT if the file is not open. +ASDCP::Result_t +ASDCP::PCM::MXFReader::FillAudioDescriptor(AudioDescriptor& ADesc) const +{ + if ( m_Reader && m_Reader->m_File.IsOpen() ) + { + ADesc = m_Reader->m_ADesc; + return RESULT_OK; + } + + return RESULT_INIT; +} + +// Fill the struct with the values from the file's header. +// Returns RESULT_INIT if the file is not open. +ASDCP::Result_t +ASDCP::PCM::MXFReader::FillWriterInfo(WriterInfo& Info) const +{ + if ( m_Reader && m_Reader->m_File.IsOpen() ) + { + Info = m_Reader->m_Info; + return RESULT_OK; + } + + return RESULT_INIT; +} + +// +void +ASDCP::PCM::MXFReader::DumpHeaderMetadata(FILE* stream) const +{ + if ( m_Reader && m_Reader->m_File.IsOpen() ) + m_Reader->m_HeaderPart.Dump(stream); +} + + +// +void +ASDCP::PCM::MXFReader::DumpIndex(FILE* stream) const +{ + if ( m_Reader->m_File.IsOpen() ) + m_Reader->m_FooterPart.Dump(stream); +} + + +//------------------------------------------------------------------------------------------ +#if 0 + +// +class ASDCP::PCM::MXFWriter::h__Writer : public ASDCP::h__Writer +{ +public: + AudioDescriptor m_ADesc; + + ASDCP_NO_COPY_CONSTRUCT(h__Writer); + + h__Writer(){} + ~h__Writer(){} + + Result_t OpenWrite(const char*, ui32_t HeaderSize); + Result_t SetSourceStream(const AudioDescriptor&); + Result_t WriteFrame(const FrameBuffer&, AESEncContext* = 0, HMACContext* = 0); + Result_t Finalize(); +}; + + + +// Open the file for writing. The file must not exist. Returns error if +// the operation cannot be completed. +ASDCP::Result_t +ASDCP::PCM::MXFWriter::h__Writer::OpenWrite(const char* filename, ui32_t HeaderSize) +{ + if ( ! m_State.Test_BEGIN() ) + return RESULT_STATE; + + m_File = new MXFFile; + + Result_t result = m_File.OpenWrite(filename); + + if ( ASDCP_SUCCESS(result) ) + { + m_EssenceDescriptor = new MDObject("WaveAudioDescriptor"); + result = m_State.Goto_INIT(); + } + + return result; +} + + +// Automatically sets the MXF file's metadata from the WAV parser info. +ASDCP::Result_t +ASDCP::PCM::MXFWriter::h__Writer::SetSourceStream(const AudioDescriptor& ADesc) +{ + if ( ! m_State.Test_INIT() ) + return RESULT_STATE; + + if ( ADesc.SampleRate != EditRate_24 + && ADesc.SampleRate != EditRate_48 + && ADesc.SampleRate != EditRate_23_98 ) + { + DefaultLogSink().Error("AudioDescriptor.SampleRate is not 24/1, 48/1 or 24000/1001: %lu/%lu\n", + ADesc.SampleRate.Numerator, ADesc.SampleRate.Denominator); + return RESULT_RAW_FORMAT; + } + + if ( ADesc.AudioSamplingRate != SampleRate_48k ) + { + DefaultLogSink().Error("AudioDescriptor.AudioSamplingRate is not 48000/1: %lu/%lu\n", + ADesc.AudioSamplingRate.Numerator, ADesc.AudioSamplingRate.Denominator); + return RESULT_RAW_FORMAT; + } + + m_ADesc = ADesc; + Result_t result = PCM_ADesc_to_MD(m_ADesc, *m_EssenceDescriptor); + + if ( ASDCP_SUCCESS(result) ) + { + result = WriteMXFHeader(ESS_PCM_24b_48k, m_ADesc.SampleRate, + 24 /* TCFrameRate */, calc_CBR_frame_size(m_Info, m_ADesc)); + } + + if ( ASDCP_SUCCESS(result) ) + result = m_State.Goto_READY(); + + return result; +} + + +// +// +ASDCP::Result_t +ASDCP::PCM::MXFWriter::h__Writer::WriteFrame(const FrameBuffer& FrameBuf, AESEncContext* Ctx, + HMACContext* HMAC) +{ + Result_t result = RESULT_OK; + + if ( m_State.Test_READY() ) + result = m_State.Goto_RUNNING(); // first time through + + if ( ASDCP_SUCCESS(result) ) + result = WriteEKLVPacket(FrameBuf, WAVEssenceUL_Data, Ctx, HMAC); + + if ( ASDCP_SUCCESS(result) ) + m_FramesWritten++; + + return result; +} + +// Closes the MXF file, writing the index and other closing information. +// +ASDCP::Result_t +ASDCP::PCM::MXFWriter::h__Writer::Finalize() +{ + if ( ! m_State.Test_RUNNING() ) + return RESULT_STATE; + + if ( ! m_File ) + return RESULT_INIT; + + m_State.Goto_FINAL(); + + return WriteMXFFooter(ESS_PCM_24b_48k); +} + + +//------------------------------------------------------------------------------------------ +// + + + +ASDCP::PCM::MXFWriter::MXFWriter() +{ +} + +ASDCP::PCM::MXFWriter::~MXFWriter() +{ +} + + +// Open the file for writing. The file must not exist. Returns error if +// the operation cannot be completed. +ASDCP::Result_t +ASDCP::PCM::MXFWriter::OpenWrite(const char* filename, const WriterInfo& Info, + const AudioDescriptor& ADesc, ui32_t HeaderSize) +{ + m_Writer = new h__Writer; + + Result_t result = m_Writer->OpenWrite(filename, HeaderSize); + + if ( ASDCP_SUCCESS(result) ) + { + m_Writer->m_Info = Info; + result = m_Writer->SetSourceStream(ADesc); + } + + if ( ASDCP_FAILURE(result) ) + m_Writer.release(); + + return result; +} + +// 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. +ASDCP::Result_t +ASDCP::PCM::MXFWriter::WriteFrame(const FrameBuffer& FrameBuf, AESEncContext* Ctx, HMACContext* HMAC) +{ + if ( m_Writer.empty() ) + return RESULT_INIT; + + return m_Writer->WriteFrame(FrameBuf, Ctx, HMAC); +} + +// Closes the MXF file, writing the index and other closing information. +ASDCP::Result_t +ASDCP::PCM::MXFWriter::Finalize() +{ + if ( m_Writer.empty() ) + return RESULT_INIT; + + return m_Writer->Finalize(); +} + +#endif + +// +// end AS_DCP_PCM.cpp +// + diff --git a/src/AS_DCP_internal.h b/src/AS_DCP_internal.h new file mode 100755 index 0000000..473a4ab --- /dev/null +++ b/src/AS_DCP_internal.h @@ -0,0 +1,299 @@ +/* +Copyright (c) 2004-2005, 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_internal.h + \version $Id$ + \brief AS-DCP library, non-public common elements +*/ + +#ifndef _AS_DCP_INTERNAL_H__ +#define _AS_DCP_INTERNAL_H__ + +#include "AS_DCP_system.h" +#include "Metadata.h" +#include "hex_utils.h" + +using namespace std; +using namespace ASDCP; +using namespace ASDCP::MXF; + + + +namespace ASDCP +{ + // constant values used to calculate KLV and EKLV packet sizes + static const ui32_t klv_key_size = 16; + static const ui32_t klv_length_size = 4; + + static const ui32_t klv_cryptinfo_size = + klv_length_size + + UUIDlen /* ContextID */ + + klv_length_size + + sizeof(ui64_t) /* PlaintextOffset */ + + klv_length_size + + klv_key_size /* SourceKey */ + + klv_length_size + + sizeof(ui64_t) /* SourceLength */ + + klv_length_size /* ESV length */ ; + + static const ui32_t klv_intpack_size = + klv_length_size + + UUIDlen /* TrackFileID */ + + klv_length_size + + sizeof(ui64_t) /* SequenceNumber */ + + klv_length_size + + 20; /* HMAC length*/ + + // why this value? i dunno. it was peeled from mxflib. + static const ui32_t HeaderPadding = 16384; + + const byte_t GCMulti_Data[16] = + { 0x06, 0x0E, 0x2B, 0x34, 0x04, 0x01, 0x01, 0x03, + 0x0d, 0x01, 0x03, 0x01, 0x02, 0x7F, 0x01, 0x00 }; + + static const byte_t CipherAlgorithm_AES[klv_key_size] = + { 0x06, 0x0e, 0x2b, 0x34, 0x04, 0x01, 0x01, 0x07, + 0x02, 0x09, 0x02, 0x01, 0x01, 0x00, 0x00, 0x00 }; + + static const byte_t MICAlgorithm_NONE[klv_key_size] = {0}; + static const byte_t MICAlgorithm_HMAC_SHA1[klv_key_size] = + { 0x06, 0x0e, 0x2b, 0x34, 0x04, 0x01, 0x01, 0x07, + 0x02, 0x09, 0x02, 0x02, 0x01, 0x00, 0x00, 0x00 }; + +#ifdef SMPTE_LABELS + static byte_t OPAtom_Data[klv_key_size] = + { 0x06, 0x0e, 0x2b, 0x34, 0x04, 0x01, 0x01, 0x02, + 0x0d, 0x01, 0x02, 0x01, 0x10, 0x00, 0x00, 0x00 }; + static UL OPAtomUL(OPAtom_Data); +#else + static byte_t OPAtom_Data[klv_key_size] = + { 0x06, 0x0e, 0x2b, 0x34, 0x04, 0x01, 0x01, 0x01, + 0x0d, 0x01, 0x02, 0x01, 0x10, 0x00, 0x00, 0x00 }; + static UL OPAtomUL(OPAtom_Data); +#endif + + static const byte_t OP1a_Data[klv_key_size] = + { 0x06, 0x0e, 0x2b, 0x34, 0x04, 0x01, 0x01, 0x01, + 0x0d, 0x01, 0x02, 0x01, 0x01, 0x01, 0x01, 0x00 }; + static UL OP1aUL(OP1a_Data); + + // Essence element labels + static const byte_t WAVEssenceUL_Data[klv_key_size] = + { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x02, 0x01, 0x01, + 0x0d, 0x01, 0x03, 0x01, 0x16, 0x01, 0x01, 0x00 }; + + static const byte_t MPEGEssenceUL_Data[klv_key_size] = + { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x02, 0x01, 0x01, + 0x0d, 0x01, 0x03, 0x01, 0x15, 0x01, 0x05, 0x00 }; + + static const byte_t JP2KEssenceUL_Data[klv_key_size] = + { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x02, 0x01, 0x01, + 0x0d, 0x01, 0x03, 0x01, 0x15, 0x01, 0x08, 0x01 }; + +#ifdef SMPTE_LABELS + static const byte_t CryptEssenceUL_Data[klv_key_size] = + { 0x06, 0x0e, 0x2b, 0x34, 0x02, 0x04, 0x01, 0x01, + 0x0d, 0x01, 0x03, 0x01, 0x02, 0x7e, 0x01, 0x00 }; +#else + static const byte_t CryptEssenceUL_Data[klv_key_size] = + { 0x06, 0x0e, 0x2b, 0x34, 0x02, 0x04, 0x01, 0x07, + 0x0d, 0x01, 0x03, 0x01, 0x02, 0x7e, 0x01, 0x00 }; +#endif + + // Essence Container Labels + static const byte_t WrappingUL_Data_PCM_24b_48k[klv_key_size] = + { 0x06, 0x0e, 0x2b, 0x34, 0x04, 0x01, 0x01, 0x01, + 0x0d, 0x01, 0x03, 0x01, 0x02, 0x06, 0x01, 0x00 }; + + static const byte_t WrappingUL_Data_MPEG2_VES[klv_key_size] = + { 0x06, 0x0e, 0x2b, 0x34, 0x04, 0x01, 0x01, 0x02, + 0x0d, 0x01, 0x03, 0x01, 0x02, 0x04, 0x60, 0x01 }; + + static const byte_t WrappingUL_Data_JPEG_2000[klv_key_size] = + { 0x06, 0x0e, 0x2b, 0x34, 0x04, 0x01, 0x01, 0x07, + 0x0d, 0x01, 0x03, 0x01, 0x02, 0x0c, 0x01, 0x00 }; + + static const byte_t WrappingUL_Data_Crypt[klv_key_size] = + { 0x06, 0x0e, 0x2b, 0x34, 0x04, 0x01, 0x01, 0x07, + 0x0d, 0x01, 0x03, 0x01, 0x02, 0x0b, 0x01, 0x00 }; + + + // the label for the Cryptographic Framework DM scheme + static const byte_t CryptoFrameworkUL_Data[klv_key_size] = + { 0x06, 0x0e, 0x2b, 0x34, 0x04, 0x01, 0x01, 0x07, + 0x0d, 0x01, 0x04, 0x01, 0x02, 0x01, 0x01, 0x00 }; + + // the check value for EKLV packets + // CHUKCHUKCHUKCHUK + static const byte_t ESV_CheckValue[CBC_BLOCK_SIZE] = + { 0x43, 0x48, 0x55, 0x4b, 0x43, 0x48, 0x55, 0x4b, + 0x43, 0x48, 0x55, 0x4b, 0x43, 0x48, 0x55, 0x4b }; + + // labels used for FilePackages + static std::string MPEG_PACKAGE_LABEL = "File Package: SMPTE 381M frame wrapping of MPEG2 video elementary stream"; + static std::string JP2K_PACKAGE_LABEL = "File Package: SMPTE XXXM frame wrapping of JPEG 2000 codestreams"; + static std::string PCM_PACKAGE_LABEL = "File Package: SMPTE 382M frame wrapping of wave audio"; + + // GetMDObjectByPath() allows searching for metadata object by pathname + // This character separates the path elements. + static const char OBJECT_PATH_SEPARATOR = '.'; + + //------------------------------------------------------------------------------------------ + // + + // MDObject* GetMDObjectByPath(MDObject&, const std::string); + // MDObject* GetMDObjectByType(MDObject&, const std::string); + // void DumpMDObject(MDObject&, std::string = " ", FILE* = 0); + // void DumpHeader(Partition&, FILE* = 0); + // void DumpIndexTable(IndexTablePtr, ui64_t = 0, FILE* = 0); + // Result_t init_mxf_types(); + Result_t MD_to_MPEG2_VDesc(MXF::MPEG2VideoDescriptor*, MPEG2::VideoDescriptor&); + Result_t MD_to_JP2K_PDesc(MXF::RGBAEssenceDescriptor*, JP2K::PictureDescriptor&); + Result_t MD_to_PCM_ADesc(MXF::WaveAudioDescriptor*, PCM::AudioDescriptor&); + Result_t MD_to_WriterInfo(MXF::Identification*, WriterInfo&); + Result_t MD_to_CryptoInfo(MXF::CryptographicContext*, WriterInfo&); + +#if 0 + Result_t MPEG2_VDesc_to_MD(MPEG2::VideoDescriptor&, MDObject&); + Result_t JP2K_PDesc_to_MD(JP2K::PictureDescriptor&, MDObject&); + Result_t PCM_ADesc_to_MD(PCM::AudioDescriptor&, MDObject&); + void AddDMScrypt(PackagePtr, WriterInfo&, const byte_t*); +#endif + + Result_t EncryptFrameBuffer(const ASDCP::FrameBuffer&, ASDCP::FrameBuffer&, AESEncContext*); + Result_t DecryptFrameBuffer(const ASDCP::FrameBuffer&, ASDCP::FrameBuffer&, AESDecContext*); + + // calculate size of encrypted essence with IV, CheckValue, and padding + inline ui32_t + calc_esv_length(ui32_t source_length, ui32_t plaintext_offset) + { + ui32_t ct_size = source_length - plaintext_offset; + ui32_t diff = ct_size % CBC_BLOCK_SIZE; + ui32_t block_size = ct_size - diff; + return plaintext_offset + block_size + (CBC_BLOCK_SIZE * 3); + } + + // + class h__Reader + { + ASDCP_NO_COPY_CONSTRUCT(h__Reader); + + public: + FileReader m_File; + OPAtomHeader m_HeaderPart; + Partition m_BodyPart; + OPAtomIndexFooter m_FooterPart; + ui64_t m_EssenceStart; + WriterInfo m_Info; + ASDCP::FrameBuffer m_CtFrameBuf; + + h__Reader(); + virtual ~h__Reader(); + // + // MDObject* GetMDObjectByType(const std::string ObjName); + // MDObject* GetMDObjectByPath(const std::string ObjPath); + Result_t InitInfo(WriterInfo& Info); + Result_t OpenMXFRead(const char* filename); + Result_t InitMXFIndex(); + Result_t ReadEKLVPacket(ui32_t FrameNum, ASDCP::FrameBuffer& FrameBuf, + const byte_t* EssenceUL, AESDecContext* Ctx, HMACContext* HMAC); + void Close(); + }; + + // helper class for calculating Integrity Packs, used by WriteEKLVPacket() below. + // + class IntegrityPack + { + public: + byte_t Data[klv_intpack_size]; + + IntegrityPack() { + memset(Data, 0, klv_intpack_size); + } + + ~IntegrityPack() {} + + Result_t CalcValues(const ASDCP::FrameBuffer&, byte_t* AssetID, ui32_t sequence, HMACContext* HMAC); + Result_t TestValues(const ASDCP::FrameBuffer&, byte_t* AssetID, ui32_t sequence, HMACContext* HMAC); + }; + + // + class KLVReader + { + byte_t m_Key[32]; + ui64_t m_Length; + ui32_t m_BERLength; + ui32_t m_HeaderLength; + + ASDCP_NO_COPY_CONSTRUCT(KLVReader); + + public: + KLVReader() : m_Length(0), m_BERLength(0), m_HeaderLength(0) {} + ~KLVReader() {} + + inline const byte_t* Key() { return m_Key; } + inline const ui64_t Length() { return m_Length; } + inline const ui64_t KLLength() { return m_BERLength + klv_key_size; } + Result_t ReadKLFromFile(ASDCP::FileReader& Reader); +#if 0 + // + { + ui32_t read_count; + m_HeaderLength = klv_key_size + klv_length_size; + Result_t result = Reader.Read(m_Key, m_HeaderLength, &read_count); + assert(read_count == m_HeaderLength); + + if ( ASDCP_SUCCESS(result) ) + { + m_BERLength = BER_length(m_Key + klv_key_size); + + if ( m_BERLength != klv_length_size ) + { + ASDCP::DefaultLogSink().Error("Found packet with BER length %lu; being less efficient...\n", + m_BERLength); + // TODO: recover the correct BER value + // and reposition the file pointer + assert(0); + } + + if ( ! read_BER(m_Key + klv_key_size, &m_Length) ) + return RESULT_FAIL; + } + + return result; + } +#endif + + }; + +} // namespace ASDCP + +#endif // _AS_DCP_INTERNAL_H__ + + +// +// end AS_DCP_internal.h +// diff --git a/src/Index.cpp b/src/Index.cpp new file mode 100755 index 0000000..9d1791a --- /dev/null +++ b/src/Index.cpp @@ -0,0 +1,203 @@ +// +// Index.cpp +// + +#include "MDD.h" +#include "MXF.h" + +// +ASDCP::MXF::IndexTableSegment::IndexTableSegment() : + IndexStartPosition(0), IndexDuration(0), EditUnitByteCount(0), + IndexSID(0), BodySID(0), SliceCount(0), PosTableCount(0) +{ +} + +// +ASDCP::MXF::IndexTableSegment::~IndexTableSegment() +{ +} + +// +ASDCP::Result_t +ASDCP::MXF::IndexTableSegment::InitFromBuffer(const byte_t* p, ui32_t l) +{ + ASDCP_TEST_NULL(p); + + Result_t result = KLVPacket::InitFromBuffer(p, l, s_MDD_Table[MDDindex_IndexTableSegment].ul); + + if ( ASDCP_SUCCESS(result) ) + { + TLVReader MemRDR(m_ValueStart, m_ValueLength, m_Lookup); + + result = MemRDR.ReadObject(OBJ_READ_ARGS(InterchangeObject, InstanceUID)); + if ( ASDCP_SUCCESS(result) ) result = MemRDR.ReadObject(OBJ_READ_ARGS(IndexTableSegmentBase, IndexEditRate)); + if ( ASDCP_SUCCESS(result) ) result = MemRDR.ReadUi64(OBJ_READ_ARGS(IndexTableSegmentBase, IndexStartPosition)); + if ( ASDCP_SUCCESS(result) ) result = MemRDR.ReadUi64(OBJ_READ_ARGS(IndexTableSegmentBase, IndexDuration)); + if ( ASDCP_SUCCESS(result) ) result = MemRDR.ReadUi32(OBJ_READ_ARGS(IndexTableSegmentBase, EditUnitByteCount)); + if ( ASDCP_SUCCESS(result) ) result = MemRDR.ReadUi32(OBJ_READ_ARGS(IndexTableSegmentBase, IndexSID)); + if ( ASDCP_SUCCESS(result) ) result = MemRDR.ReadUi32(OBJ_READ_ARGS(IndexTableSegmentBase, BodySID)); + if ( ASDCP_SUCCESS(result) ) result = MemRDR.ReadUi8(OBJ_READ_ARGS(IndexTableSegmentBase, SliceCount)); + if ( ASDCP_SUCCESS(result) ) result = MemRDR.ReadUi8(OBJ_READ_ARGS(IndexTableSegmentBase, PosTableCount)); + if ( ASDCP_SUCCESS(result) ) result = MemRDR.ReadObject(OBJ_READ_ARGS(IndexTableSegment, DeltaEntryArray)); + if ( ASDCP_SUCCESS(result) ) result = MemRDR.ReadObject(OBJ_READ_ARGS(IndexTableSegment, IndexEntryArray)); + } + + if ( ASDCP_SUCCESS(result) ) + { + Batch<IndexEntry>::iterator i; + ui32_t offset = 0; + for ( i = IndexEntryArray.begin(); i != IndexEntryArray.end(); i++ ) + { + if ( (*i).Flags == 0x40 ) + offset = 0; + + (*i).KeyFrameOffset = offset++; + } + } + + return result; +} + +// +ASDCP::Result_t +ASDCP::MXF::IndexTableSegment::WriteToBuffer(ASDCP::FrameBuffer& Buffer) +{ + return WriteKLToBuffer(Buffer, s_MDD_Table[MDDindex_IndexTableSegment].ul, 0); +#if 0 + Result_t result = KLVPacket::InitFromBuffer(p, l, s_MDD_Table[MDDindex_IndexTableSegment].ul); + + if ( ASDCP_SUCCESS(result) ) + { + TLVWriter MemRDR(m_ValueStart, m_ValueLength, m_Lookup); + + result = MemRDR.ReadObject(OBJ_READ_ARGS(InterchangeObject, InstanceUID)); + if ( ASDCP_SUCCESS(result) ) result = MemRDR.ReadObject(OBJ_READ_ARGS(IndexTableSegmentBase, IndexEditRate)); + if ( ASDCP_SUCCESS(result) ) result = MemRDR.ReadUi64(OBJ_READ_ARGS(IndexTableSegmentBase, IndexStartPosition)); + if ( ASDCP_SUCCESS(result) ) result = MemRDR.ReadUi64(OBJ_READ_ARGS(IndexTableSegmentBase, IndexDuration)); + if ( ASDCP_SUCCESS(result) ) result = MemRDR.ReadUi32(OBJ_READ_ARGS(IndexTableSegmentBase, EditUnitByteCount)); + if ( ASDCP_SUCCESS(result) ) result = MemRDR.ReadUi32(OBJ_READ_ARGS(IndexTableSegmentBase, IndexSID)); + if ( ASDCP_SUCCESS(result) ) result = MemRDR.ReadUi32(OBJ_READ_ARGS(IndexTableSegmentBase, BodySID)); + if ( ASDCP_SUCCESS(result) ) result = MemRDR.ReadUi8(OBJ_READ_ARGS(IndexTableSegmentBase, SliceCount)); + if ( ASDCP_SUCCESS(result) ) result = MemRDR.ReadUi8(OBJ_READ_ARGS(IndexTableSegmentBase, PosTableCount)); + if ( ASDCP_SUCCESS(result) ) result = MemRDR.ReadObject(OBJ_READ_ARGS(IndexTableSegment, DeltaEntryArray)); + if ( ASDCP_SUCCESS(result) ) result = MemRDR.ReadObject(OBJ_READ_ARGS(IndexTableSegment, IndexEntryArray)); + } + + return result; +#endif +} + +// +void +ASDCP::MXF::IndexTableSegment::Dump(FILE* stream) +{ + char identbuf[IdentBufferLen]; + + if ( stream == 0 ) + stream = stderr; + + KLVPacket::Dump(stream, false); + fprintf(stream, " InstanceUID = %s\n", InstanceUID.ToString(identbuf)); + fprintf(stream, " IndexEditRate = %s\n", IndexEditRate.ToString(identbuf)); + fprintf(stream, " IndexStartPosition = %s\n", i64sz(IndexStartPosition, identbuf)); + fprintf(stream, " IndexDuration = %s\n", i64sz(IndexDuration, identbuf)); + fprintf(stream, " EditUnitByteCount = %lu\n", EditUnitByteCount); + fprintf(stream, " IndexSID = %lu\n", IndexSID); + fprintf(stream, " BodySID = %lu\n", BodySID); + fprintf(stream, " SliceCount = %hu\n", SliceCount); + fprintf(stream, " PosTableCount = %hu\n", PosTableCount); + + fprintf(stream, " DeltaEntryArray:\n"); DeltaEntryArray.Dump(stream); + fprintf(stream, " IndexEntryArray:\n"); IndexEntryArray.Dump(stream); + + fputs("==========================================================================\n", stream); +} + +// +const char* +ASDCP::MXF::IndexTableSegment::DeltaEntry::ToString(char* str_buf) const +{ + sprintf(str_buf, "%3i %-3hu %-3lu", PosTableIndex, Slice, ElementData); + return str_buf; +} + +// +ASDCP::Result_t +ASDCP::MXF::IndexTableSegment::DeltaEntry::ReadFrom(ASDCP::MemIOReader& Reader) +{ + Result_t result = Reader.ReadUi8((ui8_t*)&PosTableIndex); + if ( ASDCP_SUCCESS(result) ) result = Reader.ReadUi8(&Slice); + if ( ASDCP_SUCCESS(result) ) result = Reader.ReadUi32BE(&ElementData); + return result; +} + +// +ASDCP::Result_t +ASDCP::MXF::IndexTableSegment::DeltaEntry::WriteTo(ASDCP::MemIOWriter& Writer) +{ + Result_t result = Writer.WriteUi8((ui8_t)PosTableIndex); + if ( ASDCP_SUCCESS(result) ) result = Writer.WriteUi8(Slice); + if ( ASDCP_SUCCESS(result) ) result = Writer.WriteUi32BE(ElementData); + return result; +} + +// Flags: +// Bit 7: Random Access +// Bit 6: Sequence Header +// Bit 5: forward prediction flag +// Bit 4: backward prediction flag +// e.g. +// 00== I frame (no prediction) +// 10== P frame(forward prediction from previous frame) +// 01== B frame (backward prediction from future frame) +// 11== B frame (forward & backward prediction) +// Bits 0-3: reserved [RP210 Flags to indicate coding of elements in this edit unit] + +// +const char* +ASDCP::MXF::IndexTableSegment::IndexEntry::ToString(char* str_buf) const +{ + char intbuf[IntBufferLen]; + char txt_flags[6]; + txt_flags[5] = 0; + + txt_flags[0] = ( (Flags & 0x80) != 0 ) ? 'r' : ' '; + txt_flags[1] = ( (Flags & 0x40) != 0 ) ? 's' : ' '; + txt_flags[2] = ( (Flags & 0x20) != 0 ) ? 'f' : ' '; + txt_flags[3] = ( (Flags & 0x10) != 0 ) ? 'b' : ' '; + txt_flags[4] = ( (Flags & 0x0f) == 3 ) ? 'B' : ( (Flags & 0x0f) == 2 ) ? 'P' : 'I'; + + sprintf(str_buf, "%3i %-3hu %s %s", + TemporalOffset, KeyFrameOffset, txt_flags, + i64sz(StreamOffset, intbuf)); + + return str_buf; +} + +// +ASDCP::Result_t +ASDCP::MXF::IndexTableSegment::IndexEntry::ReadFrom(ASDCP::MemIOReader& Reader) +{ + Result_t result = Reader.ReadUi8((ui8_t*)&TemporalOffset); + if ( ASDCP_SUCCESS(result) ) result = Reader.ReadUi8((ui8_t*)&KeyFrameOffset); + if ( ASDCP_SUCCESS(result) ) result = Reader.ReadUi8(&Flags); + if ( ASDCP_SUCCESS(result) ) result = Reader.ReadUi64BE(&StreamOffset); + return result; +} + +// +ASDCP::Result_t +ASDCP::MXF::IndexTableSegment::IndexEntry::WriteTo(ASDCP::MemIOWriter& Writer) +{ + Result_t result = Writer.WriteUi8((ui8_t)TemporalOffset); + if ( ASDCP_SUCCESS(result) ) result = Writer.WriteUi8((ui8_t)KeyFrameOffset); + if ( ASDCP_SUCCESS(result) ) result = Writer.WriteUi8(Flags); + if ( ASDCP_SUCCESS(result) ) result = Writer.WriteUi64BE(StreamOffset); + return result; +} + + +// +// end Index.cpp +// + diff --git a/src/JP2K.cpp b/src/JP2K.cpp new file mode 100755 index 0000000..20ec891 --- /dev/null +++ b/src/JP2K.cpp @@ -0,0 +1,222 @@ +/* +Copyright (c) 2005, 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 JP2K.cpp + \version $Id$ + \brief JPEG 2000 parser implementation + + This is not a complete implementation of all things JP2K. There is just enough here to + support parsing picture metadata from a codestream header. +*/ + +#include <JP2K.h> +#include <hex_utils.h> + + +// when indexed with the second byte of a marker code, this table will procuce one of +// two values: +// 0 - the marker is a standalone marker +// 1 - the marker designates a marker segment +// +const byte_t MarkerSegmentMap[] = + { + /* 0 1 2 3 4 5 6 7 8 9 a b c d e f */ + /* 0 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0 + /* 1 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1 + /* 2 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2 + /* 3 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 3 + /* 4 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 4 + /* 5 */ 0, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, // 5 + /* 6 */ 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 6 + /* 7 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 7 + /* 8 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8 + /* 9 */ 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9 + /* a */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // a + /* b */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // b + /* c */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // c + /* d */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // d + /* e */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // e + /* f */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // f + /* 0 1 2 3 4 5 6 7 8 9 a b c d e f */ + }; + + +// +ASDCP::Result_t +ASDCP::JP2K::GetNextMarker(const byte_t** buf, JP2K::Marker& Marker) +{ + assert((buf != 0) && (*buf != 0 )); + + if ( **buf != 0xff ) + return ASDCP::RESULT_FAIL; + + Marker.m_IsSegment = (MarkerSegmentMap[*(++(*buf))] == 1); + Marker.m_Type = (Marker_t)(0xff00 | *(*buf)++); + + if ( Marker.m_IsSegment ) + { + Marker.m_DataSize = *(*buf)++ << 8; + Marker.m_DataSize |= *(*buf)++; + Marker.m_DataSize -= 2; + Marker.m_Data = *buf; + *buf += Marker.m_DataSize; + } + + + if ( Marker.m_DataSize != 0 && Marker.m_DataSize < 3 ) + { + DefaultLogSink().Error("Illegal data size: %lu\n", Marker.m_DataSize); + return ASDCP::RESULT_FAIL; + } + + return ASDCP::RESULT_OK; +} + + +//------------------------------------------------------------------------------------------------------- +// + +// +void +ASDCP::JP2K::Accessor::SIZ::ReadComponent(ui32_t index, ASDCP::JP2K::ImageComponent& IC) +{ + assert ( index < Csize() ); + const byte_t* p = m_MarkerData + 36 + (index * 3); + IC.Ssize = *p++; + IC.XRsize = *p++; + IC.YRsize = *p; +} + +// +void +ASDCP::JP2K::Accessor::SIZ::Dump(FILE* stream) +{ + if ( stream == 0 ) + stream = stderr; + + fprintf(stream, "SIZ: \n"); + fprintf(stream, " Rsize: %hu\n", Rsize()); + fprintf(stream, " Xsize: %lu\n", Xsize()); + fprintf(stream, " Ysize: %lu\n", Xsize()); + fprintf(stream, " XOsize: %lu\n", XOsize()); + fprintf(stream, " YOsize: %lu\n", XOsize()); + fprintf(stream, " XTsize: %lu\n", XTsize()); + fprintf(stream, " YTsize: %lu\n", XTsize()); + fprintf(stream, "XTOsize: %lu\n", XTOsize()); + fprintf(stream, "YTOsize: %lu\n", YTOsize()); + fprintf(stream, " Csize: %lu\n", Csize()); + + if ( Csize() > 0 ) + { + fprintf(stream, "Components\n"); + + for ( ui32_t i = 0; i < Csize(); i++ ) + { + ImageComponent TmpComp; + ReadComponent(i, TmpComp); + fprintf(stream, "%lu: ", i); + fprintf(stream, "%lu, %lu, %lu\n", TmpComp.Ssize, TmpComp.XRsize, TmpComp.YRsize); + } + } +} + +// +void +ASDCP::JP2K::Accessor::COM::Dump(FILE* stream) +{ + if ( stream == 0 ) + stream = stderr; + + if ( IsText() ) + { + char* t_str = (char*)malloc(CommentSize() + 1); + assert( t_str != 0 ); + ui32_t cs = CommentSize(); + memcpy(t_str, CommentData(), cs); + t_str[cs] = 0; + fprintf(stream, "COM:%s\n", t_str); + } + else + { + hexdump(CommentData(), CommentSize(), stream); + } +} + + +//------------------------------------------------------------------------------------------------------- +// + + +// +void +ASDCP::JP2K::Marker::Dump(FILE* stream) const +{ + if ( stream == 0 ) + stream = stderr; + + fprintf(stream, "Marker%s 0x%04x: %s", (m_IsSegment ? " segment" : ""), m_Type, GetMarkerString(m_Type)); + + if ( m_IsSegment ) + fprintf(stream, ", 0x%0x bytes", m_DataSize); + + fputc('\n', stream); +} + +// +const char* +ASDCP::JP2K::GetMarkerString(Marker_t m) +{ + switch ( m ) + { + case MRK_NIL: return "NIL"; break; + case MRK_SOC: return "SOC: Start of codestream"; break; + case MRK_SOT: return "SOT: Start of tile-part"; break; + case MRK_SOD: return "SOD: Start of data"; break; + case MRK_EOC: return "EOC: End of codestream"; break; + case MRK_SIZ: return "SIZ: Image and tile size"; break; + case MRK_COD: return "COD: Coding style default"; break; + case MRK_COC: return "COC: Coding style component"; break; + case MRK_RGN: return "RGN: Region of interest"; break; + case MRK_QCD: return "QCD: Quantization default"; break; + case MRK_QCC: return "QCC: Quantization component"; break; + case MRK_POC: return "POC: Progression order change"; break; + case MRK_TLM: return "TLM: Tile-part lengths"; break; + case MRK_PLM: return "PLM: Packet length, main header"; break; + case MRK_PLT: return "PLT: Packet length, tile-part header"; break; + case MRK_PPM: return "PPM: Packed packet headers, main header"; break; + case MRK_PPT: return "PPT: Packed packet headers, tile-part header"; break; + case MRK_SOP: return "SOP: Start of packet"; break; + case MRK_EPH: return "EPH: End of packet header"; break; + case MRK_CRG: return "CRG: Component registration"; break; + case MRK_COM: return "COM: Comment"; break; + } + + return "Unknown marker code"; +} + +// +// end +// diff --git a/src/JP2K.h b/src/JP2K.h new file mode 100755 index 0000000..cff77b2 --- /dev/null +++ b/src/JP2K.h @@ -0,0 +1,163 @@ +/* +Copyright (c) 2005, 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 JP2K.h + \version $Id$ + \brief JPEG 2000 constants and data structures + + This is not a complete enumeration of all things JP2K. There is just enough here to + support parsing picture metadata from a codestream header. +*/ + +#ifndef _JP2K_H_ +#define _JP2K_H_ + +// AS_DCP.h is included only for it's base type definitions. +#include <AS_DCP_system.h> +#include <hex_utils.h> +#include <assert.h> + +namespace ASDCP +{ +namespace JP2K +{ + const byte_t Magic[] = {0xff, 0x4f, 0xff}; + + enum Marker_t + { + MRK_NIL = 0, + MRK_SOC = 0xff4f, // Start of codestream + MRK_SOT = 0xff90, // Start of tile-part + MRK_SOD = 0xff93, // Start of data + MRK_EOC = 0xffd9, // End of codestream + MRK_SIZ = 0xff51, // Image and tile size + MRK_COD = 0xff52, // Coding style default + MRK_COC = 0xff53, // Coding style component + MRK_RGN = 0xff5e, // Region of interest + MRK_QCD = 0xff5c, // Quantization default + MRK_QCC = 0xff5d, // Quantization component + MRK_POC = 0xff5f, // Progression order change + MRK_TLM = 0xff55, // Tile-part lengths + MRK_PLM = 0xff57, // Packet length, main header + MRK_PLT = 0xff58, // Packet length, tile-part header + MRK_PPM = 0xff60, // Packed packet headers, main header + MRK_PPT = 0xff61, // Packed packet headers, tile-part header + MRK_SOP = 0xff91, // Start of packet + MRK_EPH = 0xff92, // End of packet header + MRK_CRG = 0xff63, // Component registration + MRK_COM = 0xff64, // Comment + }; + + const char* GetMarkerString(Marker_t m); + + // + class Marker + { + ASDCP_NO_COPY_CONSTRUCT(Marker); + + public: + Marker_t m_Type; + bool m_IsSegment; + ui32_t m_DataSize; + const byte_t* m_Data; + + Marker() : m_Type(MRK_NIL), m_IsSegment(false), m_DataSize(0), m_Data(0) {} + ~Marker() {} + + void Dump(FILE* stream = 0) const; + }; + + // + ASDCP::Result_t GetNextMarker(const byte_t**, Marker&); + + // accessor objects for marker segments + namespace Accessor + { + // image size + class SIZ + { + const byte_t* m_MarkerData; + ASDCP_NO_COPY_CONSTRUCT(SIZ); + SIZ(); + + public: + SIZ(const Marker& M) + { + assert(M.m_Type == MRK_SIZ); + m_MarkerData = M.m_Data; + } + + ~SIZ() {} + + inline ui16_t Rsize() { return ASDCP_i16_BE(*(ui16_t*)m_MarkerData); } + inline ui32_t Xsize() { return ASDCP_i32_BE(*(ui32_t*)(m_MarkerData + 2)); } + inline ui32_t Ysize() { return ASDCP_i32_BE(*(ui32_t*)(m_MarkerData + 6)); } + inline ui32_t XOsize() { return ASDCP_i32_BE(*(ui32_t*)(m_MarkerData + 10)); } + inline ui32_t YOsize() { return ASDCP_i32_BE(*(ui32_t*)(m_MarkerData + 14)); } + inline ui32_t XTsize() { return ASDCP_i32_BE(*(ui32_t*)(m_MarkerData + 18)); } + inline ui32_t YTsize() { return ASDCP_i32_BE(*(ui32_t*)(m_MarkerData + 22)); } + inline ui32_t XTOsize() { return ASDCP_i32_BE(*(ui32_t*)(m_MarkerData + 26)); } + inline ui32_t YTOsize() { return ASDCP_i32_BE(*(ui32_t*)(m_MarkerData + 30)); } + inline ui16_t Csize() { return ASDCP_i16_BE(*(ui16_t*)(m_MarkerData + 34)); } + void ReadComponent(ui32_t index, ImageComponent& IC); + void Dump(FILE* stream = 0); + }; + + // a comment + class COM + { + bool m_IsText; + const byte_t* m_MarkerData; + ui32_t m_DataSize; + + ASDCP_NO_COPY_CONSTRUCT(COM); + COM(); + + public: + COM(const Marker& M) + { + assert(M.m_Type == MRK_COM); + m_IsText = M.m_Data[1] == 1; + m_MarkerData = M.m_Data + 2; + m_DataSize = M.m_DataSize - 2; + } + + ~COM() {} + + inline bool IsText() { return m_IsText; } + inline const byte_t* CommentData() { return m_MarkerData; } + inline ui32_t CommentSize() { return m_DataSize; } + void Dump(FILE* stream = 0); + }; + } // namespace Accessor +} // namespace JP2K +} // namespace ASDCP + +#endif // _JP2K_H_ + +// +// end JP2K.h +// diff --git a/src/JP2K_Codestream_Parser.cpp b/src/JP2K_Codestream_Parser.cpp new file mode 100755 index 0000000..216827b --- /dev/null +++ b/src/JP2K_Codestream_Parser.cpp @@ -0,0 +1,193 @@ +/* +Copyright (c) 2004, 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 Codestream_Parser.cpp + \version $Id$ + \brief AS-DCP library, JPEG 2000 codestream essence reader implementation +*/ + +#include <AS_DCP.h> +#include <FileIO.h> +#include <JP2K.h> +#include <assert.h> + +//------------------------------------------------------------------------------------------ + +class ASDCP::JP2K::CodestreamParser::h__CodestreamParser +{ + ASDCP_NO_COPY_CONSTRUCT(h__CodestreamParser); + +public: + PictureDescriptor m_PDesc; + FileReader m_File; + + h__CodestreamParser() + { + memset(&m_PDesc, 0, sizeof(m_PDesc)); + m_PDesc.EditRate = Rational(24,1); + m_PDesc.SampleRate = m_PDesc.EditRate; + } + + ~h__CodestreamParser() {} + + Result_t OpenReadFrame(const char* filename, FrameBuffer& FB) + { + ASDCP_TEST_NULL_STR(filename); + m_File.Close(); + Result_t result = m_File.OpenRead(filename); + + if ( ASDCP_SUCCESS(result) ) + { + fsize_t file_size = m_File.Size(); + + if ( FB.Capacity() < file_size ) + { + DefaultLogSink().Error("FrameBuf.Capacity: %lu frame length: %lu\n", FB.Capacity(), (ui32_t)file_size); + return RESULT_SMALLBUF; + } + } + + ui32_t read_count; + + if ( ASDCP_SUCCESS(result) ) + result = m_File.Read(FB.Data(), FB.Capacity(), &read_count); + + if ( ASDCP_SUCCESS(result) ) + FB.Size(read_count); + + if ( ASDCP_SUCCESS(result) ) + { + Marker NextMarker; + ui32_t i; + const byte_t* p = FB.RoData(); + const byte_t* end_p = p + FB.Size(); + + while ( p < end_p && ASDCP_SUCCESS(result) ) + { + result = GetNextMarker(&p, NextMarker); +#if 0 + fprintf(stderr, "%s Length: %lu\n", + GetMarkerString(NextMarker.m_Type), NextMarker.m_DataSize); +#endif + + switch ( NextMarker.m_Type ) + { + case MRK_SOD: + FB.PlaintextOffset(p - FB.RoData()); + p = end_p; + break; + + case MRK_SIZ: + { + Accessor::SIZ SIZ_(NextMarker); + m_PDesc.StoredWidth = SIZ_.Xsize(); + m_PDesc.StoredHeight = SIZ_.Ysize(); + m_PDesc.AspectRatio = Rational(SIZ_.Xsize(), SIZ_.Ysize()); + m_PDesc.Rsize = SIZ_.Rsize(); + m_PDesc.Xsize = SIZ_.Xsize(); + m_PDesc.Ysize = SIZ_.Ysize(); + m_PDesc.XOsize = SIZ_.XOsize(); + m_PDesc.YOsize = SIZ_.YOsize(); + m_PDesc.XTsize = SIZ_.XTsize(); + m_PDesc.YTsize = SIZ_.YTsize(); + m_PDesc.XTOsize = SIZ_.XTOsize(); + m_PDesc.YTOsize = SIZ_.YTOsize(); + m_PDesc.Csize = SIZ_.Csize(); + + if ( m_PDesc.Csize != 3 ) + { + DefaultLogSink().Error("Unexpected number of components: %lu\n", m_PDesc.Csize); + return RESULT_RAW_FORMAT; + } + + for ( i = 0; i < m_PDesc.Csize; i++ ) + SIZ_.ReadComponent(i, m_PDesc.ImageComponents[i]); + } + break; + + case MRK_COD: + if ( NextMarker.m_DataSize > DefaultCodingDataLength ) + { + DefaultLogSink().Error("Unexpectedly large CodingStyle data: %lu\n", NextMarker.m_DataSize); + return RESULT_RAW_FORMAT; + } + + m_PDesc.CodingStyleLength = NextMarker.m_DataSize; + memcpy(m_PDesc.CodingStyle, NextMarker.m_Data, m_PDesc.CodingStyleLength); + break; + + case MRK_QCD: + if ( NextMarker.m_DataSize > DefaultCodingDataLength ) + { + DefaultLogSink().Error("Unexpectedly large QuantDefault data: %lu\n", NextMarker.m_DataSize); + return RESULT_RAW_FORMAT; + } + + m_PDesc.QuantDefaultLength = NextMarker.m_DataSize; + memcpy(m_PDesc.QuantDefault, NextMarker.m_Data, m_PDesc.QuantDefaultLength); + break; + } + } + } + + return result; + } +}; + +//------------------------------------------------------------------------------------------ + +ASDCP::JP2K::CodestreamParser::CodestreamParser() +{ +} + +ASDCP::JP2K::CodestreamParser::~CodestreamParser() +{ +} + +// Opens the stream for reading, parses enough data to provide a complete +// set of stream metadata for the MXFWriter below. +ASDCP::Result_t +ASDCP::JP2K::CodestreamParser::OpenReadFrame(const char* filename, FrameBuffer& FB) const +{ + const_cast<ASDCP::JP2K::CodestreamParser*>(this)->m_Parser = new h__CodestreamParser; + return m_Parser->OpenReadFrame(filename, FB); +} + +// +ASDCP::Result_t +ASDCP::JP2K::CodestreamParser::FillPictureDescriptor(PictureDescriptor& PDesc) const +{ + if ( m_Parser.empty() ) + return RESULT_INIT; + + PDesc = m_Parser->m_PDesc; + return RESULT_OK; +} + + +// +// end Codestream_Parser.cpp +// diff --git a/src/JP2K_Sequence_Parser.cpp b/src/JP2K_Sequence_Parser.cpp new file mode 100755 index 0000000..8d1b63c --- /dev/null +++ b/src/JP2K_Sequence_Parser.cpp @@ -0,0 +1,244 @@ +/* +Copyright (c) 2004-2005, 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 JP2K_Sequence_Parser.cpp + \version $Id$ + \brief AS-DCP library, JPEG 2000 codestream essence reader implementation +*/ + +#include <AS_DCP.h> +#include <FileIO.h> +#include <DirScanner.h> +#include <list> +#include <string> +#include <algorithm> +#include <string.h> +#include <assert.h> + +using namespace ASDCP; + + +//------------------------------------------------------------------------------------------ + +class FileList : public std::list<std::string> +{ + std::string m_DirName; + +public: + FileList() {} + ~FileList() {} + + // + Result_t InitFromDirectory(const char* path) + { + char next_file[ASDCP_MAX_PATH]; + DirScanner Scanner; + + Result_t result = Scanner.Open(path); + + if ( ASDCP_SUCCESS(result) ) + { + m_DirName = path; + + while ( ASDCP_SUCCESS(Scanner.GetNext(next_file)) ) + { + if ( next_file[0] == '.' ) // no hidden files or internal links + continue; + + std::string Str(m_DirName); + Str += "/"; + Str += next_file; + push_back(Str); + } + + sort(); + } + + return result; + } +}; + +//------------------------------------------------------------------------------------------ + +class ASDCP::JP2K::SequenceParser::h__SequenceParser +{ + ui32_t m_FramesRead; + Rational m_PictureRate; + FileList m_FileList; + FileList::iterator m_CurrentFile; + CodestreamParser m_Parser; + + ASDCP_NO_COPY_CONSTRUCT(h__SequenceParser); + +public: + PictureDescriptor m_PDesc; + + h__SequenceParser() : m_FramesRead(0) + { + memset(&m_PDesc, 0, sizeof(m_PDesc)); + m_PDesc.EditRate = Rational(24,1); + } + + ~h__SequenceParser() + { + Close(); + } + + Result_t OpenRead(const char* filename); + void Close() {} + + Result_t Reset() + { + m_FramesRead = 0; + m_CurrentFile = m_FileList.begin(); + return RESULT_OK; + } + + Result_t ReadFrame(FrameBuffer&); +}; + + +// +ASDCP::Result_t +ASDCP::JP2K::SequenceParser::h__SequenceParser::OpenRead(const char* filename) +{ + ASDCP_TEST_NULL_STR(filename); + + Result_t result = m_FileList.InitFromDirectory(filename); + + if ( m_FileList.empty() ) + return RESULT_ENDOFFILE; + + if ( ASDCP_SUCCESS(result) ) + { + m_CurrentFile = m_FileList.begin(); + + CodestreamParser Parser; + FrameBuffer TmpBuffer; + + fsize_t file_size = FileSize((*m_CurrentFile).c_str()); + + if ( file_size == 0 ) + result = RESULT_NOT_FOUND; + + if ( ASDCP_SUCCESS(result) ) + result = TmpBuffer.Capacity(file_size); + + if ( ASDCP_SUCCESS(result) ) + result = Parser.OpenReadFrame((*m_CurrentFile).c_str(), TmpBuffer); + + if ( ASDCP_SUCCESS(result) ) + result = Parser.FillPictureDescriptor(m_PDesc); + + // how big is it? + if ( ASDCP_SUCCESS(result) ) + m_PDesc.ContainerDuration = m_FileList.size(); + } + + return result; +} + +// +// +ASDCP::Result_t +ASDCP::JP2K::SequenceParser::h__SequenceParser::ReadFrame(FrameBuffer& FB) +{ + if ( m_CurrentFile == m_FileList.end() ) + return RESULT_ENDOFFILE; + + // open the file + Result_t result = m_Parser.OpenReadFrame((*m_CurrentFile).c_str(), FB); + + if ( ASDCP_SUCCESS(result) ) + { + FB.FrameNumber(m_FramesRead++); + m_CurrentFile++; + } + + return result; +} + + +//------------------------------------------------------------------------------------------ + +ASDCP::JP2K::SequenceParser::SequenceParser() +{ +} + +ASDCP::JP2K::SequenceParser::~SequenceParser() +{ +} + +// Opens the stream for reading, parses enough data to provide a complete +// set of stream metadata for the MXFWriter below. +ASDCP::Result_t +ASDCP::JP2K::SequenceParser::OpenRead(const char* filename, bool pedantic) const +{ + const_cast<ASDCP::JP2K::SequenceParser*>(this)->m_Parser = new h__SequenceParser; + + Result_t result = m_Parser->OpenRead(filename); + + if ( ASDCP_FAILURE(result) ) + const_cast<ASDCP::JP2K::SequenceParser*>(this)->m_Parser.release(); + + return result; +} + +// Rewinds the stream to the beginning. +ASDCP::Result_t +ASDCP::JP2K::SequenceParser::Reset() const +{ + if ( m_Parser.empty() ) + return RESULT_INIT; + + return m_Parser->Reset(); +} + +// Places a frame of data in the frame buffer. Fails if the buffer is too small +// or the stream is empty. +ASDCP::Result_t +ASDCP::JP2K::SequenceParser::ReadFrame(FrameBuffer& FB) const +{ + if ( m_Parser.empty() ) + return RESULT_INIT; + + return m_Parser->ReadFrame(FB); +} + +ASDCP::Result_t +ASDCP::JP2K::SequenceParser::FillPictureDescriptor(PictureDescriptor& PDesc) const +{ + if ( m_Parser.empty() ) + return RESULT_INIT; + + PDesc = m_Parser->m_PDesc; + return RESULT_OK; +} + + +// +// end JP2K_Sequence_Parser.cpp +// diff --git a/src/KLV.cpp b/src/KLV.cpp new file mode 100755 index 0000000..b57e805 --- /dev/null +++ b/src/KLV.cpp @@ -0,0 +1,270 @@ +/* +Copyright (c) 2005, 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. +*/ + +// +// KLV.cpp +// + +#include "KLV.h" +#include <hex_utils.h> + + +// This is how much we read when we're reading from a file and we don't know +// how long the packet is. This gives us the K (16 bytes) and L (4-9 bytes) +// and the remaining bytes for an even read (tmp_read_size % 16 == 0) +const ui32_t kl_length = ASDCP::SMPTE_UL_LENGTH + ASDCP::MXF_BER_LENGTH; +const ui32_t tmp_read_size = 32; + +//------------------------------------------------------------------------------------------ +// + +// +ASDCP::Result_t +ASDCP::KLVPacket::InitFromBuffer(const byte_t* buf, ui32_t buf_len, const byte_t* label) +{ + Result_t result = KLVPacket::InitFromBuffer(buf, buf_len); + + if ( ASDCP_SUCCESS(result) ) + result = ( memcmp(m_KeyStart, label, SMPTE_UL_LENGTH) == 0 ) ? + RESULT_OK : RESULT_FAIL; + + return result; +} + +// +ASDCP::Result_t +ASDCP::KLVPacket::InitFromBuffer(const byte_t* buf, ui32_t buf_len) +{ + m_KeyStart = m_ValueStart = 0; + m_KLLength = m_ValueLength = 0; + + if ( memcmp(buf, SMPTE_UL_START, 4) != 0 ) + { + DefaultLogSink().Error("Unexpected UL preamble: %02x.%02x.%02x.%02x\n", + buf[0], buf[1], buf[2], buf[3]); + return RESULT_FAIL; + } + + ui64_t tmp_size; + if ( ! read_BER(buf + SMPTE_UL_LENGTH, &tmp_size) ) + return RESULT_FAIL; + + m_ValueLength = tmp_size; + m_KLLength = SMPTE_UL_LENGTH + BER_length(buf + SMPTE_UL_LENGTH); + m_KeyStart = buf; + m_ValueStart = buf + m_KLLength; + return RESULT_OK; +} + +// +bool +ASDCP::KLVPacket::HasUL(const byte_t* ul) +{ + if ( m_KeyStart == 0 ) + return false; + + return ( memcmp(ul, m_KeyStart, SMPTE_UL_LENGTH) == 0 ) ? true : false; +} + +// +ASDCP::Result_t +ASDCP::KLVPacket::WriteKLToBuffer(ASDCP::FrameBuffer& Buffer, const byte_t* label, ui32_t length) +{ + if ( Buffer.Size() + kl_length > Buffer.Capacity() ) + { + DefaultLogSink().Error("Small write buffer\n"); + return RESULT_FAIL; + } + + memcpy(Buffer.Data() + Buffer.Size(), label, SMPTE_UL_LENGTH); + + if ( ! write_BER(Buffer.Data() + Buffer.Size() + SMPTE_UL_LENGTH, length, MXF_BER_LENGTH) ) + return RESULT_FAIL; + + Buffer.Size(Buffer.Size() + kl_length); + return RESULT_OK; +} + +// +void +ASDCP::KLVPacket::Dump(FILE* stream, bool show_hex) +{ + if ( stream == 0 ) + stream = stderr; + + if ( m_KeyStart != 0 ) + { + assert(m_ValueStart); + + for ( ui32_t i = 0; i < SMPTE_UL_LENGTH; i++ ) + fprintf(stream, "%02x.", m_KeyStart[i]); + + const MDDEntry* Entry = GetMDDEntry(m_KeyStart); + fprintf(stream, "\b len: %7lu (%s)\n", m_ValueLength, (Entry ? Entry->name : "Unknown")); + + if ( show_hex && m_ValueLength < 1000 ) + hexdump(m_ValueStart, ASDCP::xmin(m_ValueLength, (ui32_t)64), stream); + } + else + { + fprintf(stream, "*** Malformed packet ***\n"); + } +} + +// +ASDCP::Result_t +ASDCP::KLVFilePacket::InitFromFile(const FileReader& Reader, const byte_t* label) +{ + Result_t result = KLVFilePacket::InitFromFile(Reader); + + if ( ASDCP_SUCCESS(result) ) + result = ( memcmp(m_KeyStart, label, SMPTE_UL_LENGTH) == 0 ) ? + RESULT_OK : RESULT_FAIL; + + return result; +} + +// +ASDCP::Result_t +ASDCP::KLVFilePacket::InitFromFile(const FileReader& Reader) +{ + ui32_t read_count; + byte_t tmp_data[tmp_read_size]; + ui64_t tmp_size; + m_KeyStart = m_ValueStart = 0; + m_KLLength = m_ValueLength = 0; + m_Buffer.Size(0); + + Result_t result = Reader.Read(tmp_data, tmp_read_size, &read_count); + + if ( ASDCP_FAILURE(result) ) + return result; + + if ( read_count < (SMPTE_UL_LENGTH + 1) ) + { + DefaultLogSink().Error("Short read of Key and Length got %lu\n", read_count); + return RESULT_READFAIL; + } + + if ( memcmp(tmp_data, SMPTE_UL_START, 4) != 0 ) + { + DefaultLogSink().Error("Unexpected UL preamble: %02x.%02x.%02x.%02x\n", + tmp_data[0], tmp_data[1], tmp_data[2], tmp_data[3]); + return RESULT_FAIL; + } + + if ( ! read_BER(tmp_data + SMPTE_UL_LENGTH, &tmp_size) ) + { + DefaultLogSink().Error("BER Length decoding error\n"); + return RESULT_FAIL; + } + + if ( tmp_size > MAX_KLV_PACKET_LENGTH ) + { + char intbuf[IntBufferLen]; + DefaultLogSink().Error("Packet length %s exceeds internal limit\n", + ui64sz(tmp_size, intbuf)); + return RESULT_FAIL; + } + + ui32_t remainder = 0; + ui32_t ber_len = BER_length(tmp_data + SMPTE_UL_LENGTH); + m_KLLength = SMPTE_UL_LENGTH + ber_len; + m_ValueLength = tmp_size; + ui32_t packet_length = m_ValueLength + m_KLLength; + + result = m_Buffer.Capacity(packet_length); + + if ( ASDCP_FAILURE(result) ) + return result; + + m_KeyStart = m_Buffer.Data(); + m_ValueStart = m_Buffer.Data() + m_KLLength; + m_Buffer.Size(packet_length); + + // is the whole packet in the tmp buf? + if ( packet_length <= tmp_read_size ) + { + assert(packet_length <= read_count); + memcpy(m_Buffer.Data(), tmp_data, packet_length); + + if ( (remainder = read_count - packet_length) != 0 ) + { + DefaultLogSink().Warn("Repositioning pointer for short packet\n"); + ASDCP::fpos_t pos = Reader.Tell(); + assert(pos > remainder); + result = Reader.Seek(pos - remainder); + } + } + else + { + if ( read_count < tmp_read_size ) + { + DefaultLogSink().Error("Short read of packet body, expecting %lu, got %lu\n", + m_Buffer.Size(), read_count); + return RESULT_READFAIL; + } + + memcpy(m_Buffer.Data(), tmp_data, tmp_read_size); + remainder = m_Buffer.Size() - tmp_read_size; + + if ( remainder > 0 ) + { + result = Reader.Read(m_Buffer.Data() + tmp_read_size, remainder, &read_count); + + if ( read_count != remainder ) + { + DefaultLogSink().Error("Short read of packet body, expecting %lu, got %lu\n", + remainder+tmp_read_size, read_count+tmp_read_size); + result = RESULT_READFAIL; + } + } + } + + return result; +} + +// +ASDCP::Result_t +ASDCP::KLVFilePacket::WriteKLToFile(FileWriter& Writer, const byte_t* label, ui32_t length) +{ + byte_t buffer[kl_length]; + memcpy(buffer, label, SMPTE_UL_LENGTH); + + if ( ! write_BER(buffer+SMPTE_UL_LENGTH, length, MXF_BER_LENGTH) ) + return RESULT_FAIL; + + ui32_t write_count; + Writer.Write(buffer, kl_length, &write_count); + assert(write_count == kl_length); + return RESULT_OK; +} + + +// +// end KLV.cpp +// diff --git a/src/KLV.h b/src/KLV.h new file mode 100755 index 0000000..587bbe8 --- /dev/null +++ b/src/KLV.h @@ -0,0 +1,151 @@ +/* +Copyright (c) 2005, 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. +*/ + + +#ifndef _KLV_H_ +#define _KLV_H_ + +#include <FileIO.h> +#include <MemIO.h> + +namespace ASDCP +{ + const ui32_t MXF_BER_LENGTH = 4; + const ui32_t MXF_TAG_LENGTH = 2; + const ui32_t SMPTE_UL_LENGTH = 16; + const ui32_t SMPTE_UMID_LENGTH = 32; + const byte_t SMPTE_UL_START[4] = { 0x06, 0x0e, 0x2b, 0x34 }; + const ui32_t MAX_KLV_PACKET_LENGTH = 1024*1024*64; + + const ui32_t IdentBufferLen = 128; + + struct TagValue + { + byte_t a; + byte_t b; + + inline bool operator<(const TagValue& rhs) const { + if ( a < rhs.a ) return true; + if ( a == rhs.a && b < rhs.b ) return true; + return false; + } + + inline bool operator==(const TagValue& rhs) const { + if ( a != rhs.a ) return false; + if ( b != rhs.b ) return false; + return true; + } + }; + + // + class IArchive + { + public: + virtual ~IArchive() {} + virtual Result_t ReadFrom(ASDCP::MemIOReader& Reader) = 0; + virtual Result_t WriteTo(ASDCP::MemIOWriter& Writer) = 0; + }; +} // namespace ASDCP + +#include "Identifier.h" + +namespace ASDCP +{ + // + class IPrimerLookup + { + public: + virtual ~IPrimerLookup() {} + virtual void ClearTagList() = 0; + virtual Result_t InsertTag(const ASDCP::UL& Key, ASDCP::TagValue& Tag) = 0; + virtual Result_t TagForKey(const ASDCP::UL& Key, ASDCP::TagValue& Tag) = 0; + }; + + // + struct MDDEntry + { + byte_t ul[SMPTE_UL_LENGTH]; + TagValue tag; + bool optional; + const char* name; + const char* detail; + }; + + // + const MDDEntry* GetMDDEntry(const byte_t*); + + // + class KLVPacket + { + ASDCP_NO_COPY_CONSTRUCT(KLVPacket); + + protected: + const byte_t* m_KeyStart; + ui32_t m_KLLength; + const byte_t* m_ValueStart; + ui32_t m_ValueLength; + + public: + KLVPacket() : m_KeyStart(0), m_KLLength(0), m_ValueStart(0), m_ValueLength(0) {} + virtual ~KLVPacket() {} + + ui32_t PacketLength() { + return m_KLLength + m_ValueLength; + } + + virtual bool HasUL(const byte_t*); + virtual Result_t InitFromBuffer(const byte_t*, ui32_t); + virtual Result_t InitFromBuffer(const byte_t*, ui32_t, const byte_t* label); + virtual Result_t WriteKLToBuffer(ASDCP::FrameBuffer&, const byte_t* label, ui32_t length); + virtual void Dump(FILE*, bool); + }; + + // + class KLVFilePacket : public KLVPacket + { + ASDCP_NO_COPY_CONSTRUCT(KLVFilePacket); + + protected: + ASDCP::FrameBuffer m_Buffer; + + public: + KLVFilePacket() {} + virtual ~KLVFilePacket() {} + + virtual Result_t InitFromFile(const FileReader&); + virtual Result_t InitFromFile(const FileReader&, const byte_t* label); + virtual Result_t WriteKLToFile(FileWriter& Writer, const byte_t* label, ui32_t length); + }; + +} // namespace ASDCP + +#endif // _KLV_H_ + + +// +// end KLV.h +// diff --git a/src/MDD.h b/src/MDD.h new file mode 100755 index 0000000..493e30d --- /dev/null +++ b/src/MDD.h @@ -0,0 +1,1249 @@ +// +// MDD.h +// + +#ifndef _MDD_H_ +#define _MDD_H_ +#include <KLV.h> + +const ASDCP::MDDEntry s_MDD_Table[] = { + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x02, 0x01, 0x01, + 0x0d, 0x01, 0x03, 0x01, 0x15, 0x01, 0x08, 0x01 }, {0}, true, "JPEG2000Essence", // 0 + "JPEG 2000 Compressed Picture Essence Descriptor" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x02, 0x01, 0x01, + 0x0d, 0x01, 0x03, 0x01, 0x15, 0x01, 0x05, 0x00 }, {0}, true, "MPEG2Essence", // 1 + "MPEG-2 Compressed Picture Essence Descriptor" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x02, 0x01, 0x01, + 0x0d, 0x01, 0x03, 0x01, 0x16, 0x01, 0x01, 0x00 }, {0}, true, "WAVEssence", // 2 + "PCM Audio Essence Descriptor" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x02, 0x04, 0x01, 0x07, + 0x0d, 0x01, 0x03, 0x01, 0x02, 0x7e, 0x01, 0x00 }, {0}, true, "EKLVPacket", // 3 + "Encrypted Essence Container Descriptor" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x01, + 0x03, 0x01, 0x02, 0x10, 0x01, 0x00, 0x00, 0x00 }, {0}, false, "KLVFill", // 4 + "KLV Filler packet" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x04, + 0x03, 0x01, 0x02, 0x01, 0x06, 0x00, 0x00, 0x00 }, {0}, false, "PartitionMetadata_MajorVersion", // 5 + "Major Version number of MXF byte-level format (non-backwards compatible version number)" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x04, + 0x03, 0x01, 0x02, 0x01, 0x07, 0x00, 0x00, 0x00 }, {0}, false, "PartitionMetadata_MinorVersion", // 6 + "Minor Version number of MXF byte-level format (backwards compatible version number)" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x05, + 0x03, 0x01, 0x02, 0x01, 0x09, 0x00, 0x00, 0x00 }, {0}, false, "PartitionMetadata_KAGSize", // 7 + "Size of the KLV Alignment Grid (KAG) for this partition, in bytes" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x04, + 0x06, 0x10, 0x10, 0x03, 0x01, 0x00, 0x00, 0x00 }, {0}, false, "PartitionMetadata_ThisPartition", // 8 + "Byte offset of the start of This Partition, relative to the start of the Header Partition" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x04, + 0x06, 0x10, 0x10, 0x02, 0x01, 0x00, 0x00, 0x00 }, {0}, false, "PartitionMetadata_PreviousPartition", // 9 + "Byte offset of the start of the Previous Partition, relative to the start of the Header Partition" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x04, + 0x06, 0x10, 0x10, 0x05, 0x01, 0x00, 0x00, 0x00 }, {0}, false, "PartitionMetadata_FooterPartition", // 10 + "Byte offset of the start of the Footer Partition, relative to the start of the Header Partition" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x04, + 0x04, 0x06, 0x09, 0x01, 0x00, 0x00, 0x00, 0x00 }, {0}, false, "PartitionMetadata_HeaderByteCount", // 11 + "Count of Bytes used for Header Metadata. This starts at the byte following the Partition pack and includes any trailing filler which is part of the Header Metadata." }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x04, + 0x04, 0x06, 0x09, 0x02, 0x00, 0x00, 0x00, 0x00 }, {0}, false, "PartitionMetadata_IndexByteCount", // 12 + "Count of Bytes used for Index Table Segments. This starts at the byte following the Header Metadata and includes any trailing filler which is part of the Index Table." }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x04, + 0x01, 0x03, 0x04, 0x05, 0x00, 0x00, 0x00, 0x00 }, {0}, false, "PartitionMetadata_IndexSID", // 13 + "Index Table Segment Identifier in this partition. The value 0 defines that there are no Index Table segments in this partition." }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x04, + 0x06, 0x08, 0x01, 0x02, 0x01, 0x03, 0x00, 0x00 }, {0}, false, "PartitionMetadata_BodyOffset", // 14 + "Byte offset of the first byte in the following Essence Container data relative to the start of the Essence Container identified by this BodySID" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x04, + 0x01, 0x03, 0x04, 0x04, 0x00, 0x00, 0x00, 0x00 }, {0}, false, "PartitionMetadata_BodySID", // 15 + "Identifier of the Essence Container data found in this partition. The value 0 indicates there is no Essence Container data in this partition." }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x05, + 0x01, 0x02, 0x02, 0x03, 0x00, 0x00, 0x00, 0x00 }, {0}, false, "PartitionMetadata_OperationalPattern", // 16 + "Universal Label of the Operational Pattern to which this file complies" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x05, + 0x01, 0x02, 0x02, 0x10, 0x02, 0x01, 0x00, 0x00 }, {0}, false, "PartitionMetadata_EssenceContainers", // 17 + "The unordered batch of Universal Labels of Essence Containers used in or referenced by this file" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x02, 0x05, 0x01, 0x01, + 0x0d, 0x01, 0x02, 0x01, 0x01, 0x02, 0x01, 0x00 }, {0}, false, "OpenHeader", // 18 + "Open Header Partition Pack" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x02, 0x05, 0x01, 0x01, + 0x0d, 0x01, 0x02, 0x01, 0x01, 0x02, 0x03, 0x00 }, {0}, false, "OpenCompleteHeader", // 19 + "Open Complete Header Partition Pack" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x02, 0x05, 0x01, 0x01, + 0x0d, 0x01, 0x02, 0x01, 0x01, 0x02, 0x02, 0x00 }, {0}, false, "ClosedHeader", // 20 + "Closed Header Partition Pack" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x02, 0x05, 0x01, 0x01, + 0x0d, 0x01, 0x02, 0x01, 0x01, 0x02, 0x04, 0x00 }, {0}, false, "ClosedCompleteHeader", // 21 + "Closed Complete Header Partition Pack" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x02, 0x05, 0x01, 0x01, + 0x0d, 0x01, 0x02, 0x01, 0x01, 0x03, 0x01, 0x00 }, {0}, false, "OpenBodyPartition", // 22 + "Open Body Partition Pack" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x02, 0x05, 0x01, 0x01, + 0x0d, 0x01, 0x02, 0x01, 0x01, 0x03, 0x03, 0x00 }, {0}, false, "OpenCompleteBodyPartition", // 23 + "Open Complete Body Partition Pack" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x02, 0x05, 0x01, 0x01, + 0x0d, 0x01, 0x02, 0x01, 0x01, 0x03, 0x02, 0x00 }, {0}, false, "ClosedBodyPartition", // 24 + "Closed Body Partition Pack" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x02, 0x05, 0x01, 0x01, + 0x0d, 0x01, 0x02, 0x01, 0x01, 0x03, 0x04, 0x00 }, {0}, false, "ClosedCompleteBodyPartition", // 25 + "Closed Complete Body Partition Pack" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x02, 0x05, 0x01, 0x01, + 0x0d, 0x01, 0x02, 0x01, 0x01, 0x04, 0x02, 0x00 }, {0}, false, "Footer", // 26 + "Footer Partition Pack" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x02, 0x05, 0x01, 0x01, + 0x0d, 0x01, 0x02, 0x01, 0x01, 0x04, 0x04, 0x00 }, {0}, false, "CompleteFooter", // 27 + "Complete Footer Partition Pack" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x02, 0x05, 0x01, 0x01, + 0x0d, 0x01, 0x02, 0x01, 0x01, 0x05, 0x01, 0x00 }, {0}, false, "Primer", // 28 + "Primer Pack" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x05, + 0x06, 0x01, 0x01, 0x07, 0x15, 0x00, 0x00, 0x00 }, {0}, false, "Primer_LocalTagEntryBatch", // 29 + "Local Tag Entry Batch" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x05, + 0x01, 0x03, 0x06, 0x02, 0x00, 0x00, 0x00, 0x00 }, {0}, false, "LocalTagEntryBatch_Primer_LocalTag", // 30 + "The value of the Local Tag" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x05, + 0x01, 0x03, 0x06, 0x03, 0x00, 0x00, 0x00, 0x00 }, {0}, false, "LocalTagEntryBatch_Primer_UID", // 31 + "The UID of which the local tag is an alias" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x15, 0x02, 0x00, 0x00, 0x00, 0x00 }, {0x3c, 0x0a}, false, "InterchangeObject_InstanceUID", // 32 + "Unique ID of this instance" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x02, + 0x05, 0x20, 0x07, 0x01, 0x08, 0x00, 0x00, 0x00 }, {0x01, 0x02}, true, "GenerationInterchangeObject_GenerationUID", // 33 + "Generation Instance" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x02, 0x53, 0x01, 0x01, + 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, {0}, false, "DefaultObject", // 34 + "" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x05, + 0x05, 0x30, 0x04, 0x06, 0x00, 0x00, 0x00, 0x00 }, {0x3f, 0x0b}, false, "IndexTableSegmentBase_IndexEditRate", // 35 + "Edit Rate copied from the tracks of the Essence Container" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x05, + 0x07, 0x02, 0x01, 0x03, 0x01, 0x0a, 0x00, 0x00 }, {0x3f, 0x0c}, false, "IndexTableSegmentBase_IndexStartPosition", // 36 + "The first editable unit indexed by this Index Table segment measured in File Package Edit Units" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x05, + 0x07, 0x02, 0x02, 0x01, 0x01, 0x02, 0x00, 0x00 }, {0x3f, 0x0d}, false, "IndexTableSegmentBase_IndexDuration", // 37 + "Time duration of this table segment measured in Edit Unitsof the referenceg package" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x04, + 0x04, 0x06, 0x02, 0x01, 0x00, 0x00, 0x00, 0x00 }, {0x3f, 0x05}, false, "IndexTableSegmentBase_EditUnitByteCount", // 38 + "Byte count of each and every Edit Unit. A value of 0 defines the byte count of Edit Units is only given in the Index Entry Array" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x04, + 0x01, 0x03, 0x04, 0x05, 0x00, 0x00, 0x00, 0x00 }, {0x3f, 0x06}, false, "IndexTableSegmentBase_IndexSID", // 39 + "Stream Identifier (SID) of Index Stream" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x04, + 0x01, 0x03, 0x04, 0x04, 0x00, 0x00, 0x00, 0x00 }, {0x3f, 0x07}, false, "IndexTableSegmentBase_BodySID", // 40 + "Stream Identifier (SID) of Essence Container Stream" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x04, + 0x04, 0x04, 0x04, 0x01, 0x01, 0x00, 0x00, 0x00 }, {0x3f, 0x08}, false, "IndexTableSegmentBase_SliceCount", // 41 + "Number of slices minus 1 (NSL)" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x05, + 0x04, 0x04, 0x04, 0x01, 0x07, 0x00, 0x00, 0x00 }, {0x3f, 0x0e}, true, "IndexTableSegmentBase_PosTableCount", // 42 + "Number of PosTable Entries minus 1 (NPE)" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x02, 0x53, 0x01, 0x01, + 0x0d, 0x01, 0x02, 0x01, 0x01, 0x10, 0x01, 0x00 }, {0}, false, "IndexTableSegment", // 43 + "A segment of an Index Table" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x05, + 0x04, 0x04, 0x04, 0x01, 0x06, 0x00, 0x00, 0x00 }, {0x3f, 0x09}, true, "IndexTableSegment_DeltaEntryArray", // 44 + "Map Elements onto Slices" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x04, + 0x04, 0x04, 0x04, 0x01, 0x04, 0x00, 0x00, 0x00 }, {0}, false, "DeltaEntryArray_IndexTableSegment_PosTableIndex", // 45 + "Index into PosTable (or Apply Temporta Reordering if -1)" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x04, + 0x04, 0x04, 0x04, 0x01, 0x02, 0x00, 0x00, 0x00 }, {0}, false, "DeltaEntryArray_IndexTableSegment_Slice", // 46 + "Slice number in IndexEntry" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x04, + 0x04, 0x04, 0x04, 0x01, 0x03, 0x00, 0x00, 0x00 }, {0}, false, "DeltaEntryArray_IndexTableSegment_ElementDelta", // 47 + "Delta from start of slice to this Element" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x05, + 0x04, 0x04, 0x04, 0x02, 0x05, 0x00, 0x00, 0x00 }, {0x3f, 0x0a}, false, "IndexTableSegment_IndexEntryArray", // 48 + "Index from Edit Unit number to stream offset" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x04, + 0x04, 0x04, 0x04, 0x02, 0x03, 0x00, 0x00, 0x00 }, {0}, false, "IndexEntryArray_IndexTableSegment_TemporalOffset", // 49 + "Offset in edit units from Display Order to Coded Order" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x04, + 0x04, 0x04, 0x04, 0x02, 0x04, 0x00, 0x00, 0x00 }, {0}, false, "IndexEntryArray_IndexTableSegment_AnchorOffset", // 50 + "Offset in edit units to previous Anchor Frame. The value is zero if this is an anchor frame." }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x04, + 0x04, 0x04, 0x04, 0x02, 0x02, 0x00, 0x00, 0x00 }, {0}, false, "IndexEntryArray_IndexTableSegment_Flags", // 51 + "Flags for this Edit Unit" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x04, + 0x04, 0x04, 0x04, 0x02, 0x01, 0x00, 0x00, 0x00 }, {0}, false, "IndexEntryArray_IndexTableSegment_StreamOffset", // 52 + "Offset in bytes from the first KLV element in this Edit Unit within the Essence Container" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x04, + 0x04, 0x04, 0x04, 0x01, 0x05, 0x00, 0x00, 0x00 }, {0}, false, "IndexEntryArray_IndexTableSegment_SliceOffsetArray", // 53 + "Array of offsets in bytes from the Stream Offset to the start of each slice." }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x04, + 0x04, 0x04, 0x04, 0x01, 0x08, 0x00, 0x00, 0x00 }, {0}, false, "IndexEntryArray_IndexTableSegment_PosTableArray", // 54 + "Array of fractional position offsets from the start of the content package to the synchronized sample in the Content Package" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x02, 0x05, 0x01, 0x01, + 0x0d, 0x01, 0x02, 0x01, 0x01, 0x11, 0x01, 0x00 }, {0}, false, "RandomIndexMetadata", // 55 + "Random Index Pack" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x04, + 0x01, 0x03, 0x04, 0x04, 0x00, 0x00, 0x00, 0x00 }, {0}, false, "PartitionArray_RandomIndexMetadata_BodySID", // 56 + "Stream ID of the Body in this partition" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x04, + 0x06, 0x09, 0x02, 0x01, 0x01, 0x00, 0x00, 0x00 }, {0}, false, "PartitionArray_RandomIndexMetadata_ByteOffset", // 57 + "Byte offset from file start (1st byte of the file which is numbered 0) to the 1st byte of the Partition Pack Key" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x04, + 0x04, 0x06, 0x10, 0x01, 0x00, 0x00, 0x00, 0x00 }, {0}, false, "RandomIndexMetadata_Length", // 58 + "Overall Length of this Pack including the Set Key and BER Length fields" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x02, 0x53, 0x01, 0x01, + 0x0d, 0x01, 0x01, 0x01, 0x01, 0x01, 0x2f, 0x00 }, {0}, false, "Preface", // 59 + "Preface Set" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x02, + 0x07, 0x02, 0x01, 0x10, 0x02, 0x04, 0x00, 0x00 }, {0x3b, 0x02}, false, "Preface_LastModifiedDate", // 60 + "Date & time of the last modification of the file" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x02, + 0x03, 0x01, 0x02, 0x01, 0x05, 0x00, 0x00, 0x00 }, {0x3b, 0x05}, false, "Preface_Version", // 61 + "The value shall be 258 (i.e. v1.2)" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x02, + 0x03, 0x01, 0x02, 0x01, 0x04, 0x00, 0x00, 0x00 }, {0x3b, 0x07}, true, "Preface_ObjectModelVersion", // 62 + "Simple integer version number of Object Model" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x04, + 0x06, 0x01, 0x01, 0x04, 0x01, 0x08, 0x00, 0x00 }, {0x3b, 0x08}, true, "Preface_PrimaryPackage", // 63 + "The primary Package in this file" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x02, + 0x06, 0x01, 0x01, 0x04, 0x06, 0x04, 0x00, 0x00 }, {0x3b, 0x06}, false, "Preface_Identifications", // 64 + "Ordered array of strong references to Identification sets recording all modifications to the file" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x02, + 0x06, 0x01, 0x01, 0x04, 0x02, 0x01, 0x00, 0x00 }, {0x3b, 0x03}, false, "Preface_ContentStorage", // 65 + "Strong reference to Content Storage object" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x05, + 0x01, 0x02, 0x02, 0x03, 0x00, 0x00, 0x00, 0x00 }, {0x3b, 0x09}, false, "Preface_OperationalPattern", // 66 + "Universal Label of the Operational Pattern which this file complies to (repeat of Partition Pack value)" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x05, + 0x01, 0x02, 0x02, 0x10, 0x02, 0x01, 0x00, 0x00 }, {0x3b, 0x0a}, false, "Preface_EssenceContainers", // 67 + "Unordered batch of ULs of Essence Containers used in or referenced by this file (repeat of Partition Pack value)" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x05, + 0x01, 0x02, 0x02, 0x10, 0x02, 0x02, 0x00, 0x00 }, {0x3b, 0x0b}, false, "Preface_DMSchemes", // 68 + "An unordered batch of Universal Labels of all the Descriptive Metadata schemes used in this file" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x02, 0x53, 0x01, 0x01, + 0x0d, 0x01, 0x01, 0x01, 0x01, 0x01, 0x30, 0x00 }, {0}, false, "Identification", // 69 + "Identification set" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x02, + 0x05, 0x20, 0x07, 0x01, 0x01, 0x00, 0x00, 0x00 }, {0x3c, 0x09}, false, "Identification_ThisGenerationUID", // 70 + "This Generation Identifier" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x02, + 0x05, 0x20, 0x07, 0x01, 0x02, 0x01, 0x00, 0x00 }, {0x3c, 0x01}, false, "Identification_CompanyName", // 71 + "Manufacturer of the equipment or application that created or modified the file" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x02, + 0x05, 0x20, 0x07, 0x01, 0x03, 0x01, 0x00, 0x00 }, {0x3c, 0x02}, false, "Identification_ProductName", // 72 + "Name of the application which created or modified this file" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x02, + 0x05, 0x20, 0x07, 0x01, 0x04, 0x00, 0x00, 0x00 }, {0x3c, 0x03}, true, "Identification_ProductVersion", // 73 + "Maj.min.tweak.build.rel version number of this application" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x02, + 0x05, 0x20, 0x07, 0x01, 0x05, 0x01, 0x00, 0x00 }, {0x3c, 0x04}, false, "Identification_VersionString", // 74 + "Human readable name of the application version" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x02, + 0x05, 0x20, 0x07, 0x01, 0x07, 0x00, 0x00, 0x00 }, {0x3c, 0x05}, false, "Identification_ProductUID", // 75 + "A unique identification for the product which created this file (defined by the manufacturer)" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x02, + 0x07, 0x02, 0x01, 0x10, 0x02, 0x03, 0x00, 0x00 }, {0x3c, 0x06}, false, "Identification_ModificationDate", // 76 + "Time & date an application created or modified this file and created this Identification set" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x02, + 0x05, 0x20, 0x07, 0x01, 0x0a, 0x00, 0x00, 0x00 }, {0x3c, 0x07}, true, "Identification_ToolkitVersion", // 77 + "Maj.min.tweak.build.rel version of software or hardware codec used" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x02, + 0x05, 0x20, 0x07, 0x01, 0x06, 0x01, 0x00, 0x00 }, {0x3c, 0x08}, true, "Identification_Platform", // 78 + "Human readable name of the operating system used." }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x02, 0x53, 0x01, 0x01, + 0x0d, 0x01, 0x01, 0x01, 0x01, 0x01, 0x18, 0x00 }, {0}, false, "ContentStorage", // 79 + "Content Storage set" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x02, + 0x06, 0x01, 0x01, 0x04, 0x05, 0x01, 0x00, 0x00 }, {0x19, 0x01}, false, "ContentStorage_Packages", // 80 + "Unordered batch of all packages used in this file" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x02, + 0x06, 0x01, 0x01, 0x04, 0x05, 0x02, 0x00, 0x00 }, {0x19, 0x02}, true, "ContentStorage_EssenceContainerData", // 81 + "Unordered batch of strong references to Essence Container Data sets used in this file" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x02, 0x53, 0x01, 0x01, + 0x0d, 0x01, 0x01, 0x01, 0x01, 0x01, 0x23, 0x00 }, {0}, false, "EssenceContainerData", // 82 + "Essence Container Data set" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x02, + 0x06, 0x01, 0x01, 0x06, 0x01, 0x00, 0x00, 0x00 }, {0x27, 0x01}, false, "EssenceContainerData_LinkedPackageUID", // 83 + "Identifier of the Package to which this set is linked as a UMID" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x04, + 0x01, 0x03, 0x04, 0x05, 0x00, 0x00, 0x00, 0x00 }, {0x3f, 0x06}, true, "EssenceContainerData_IndexSID", // 84 + "ID of the Index Table for the Essence Container to which this set is linked" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x04, + 0x01, 0x03, 0x04, 0x04, 0x00, 0x00, 0x00, 0x00 }, {0x3f, 0x07}, false, "EssenceContainerData_BodySID", // 85 + "ID of the Essence Container to which this set is linked" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x15, 0x10, 0x00, 0x00, 0x00, 0x00 }, {0x44, 0x01}, false, "GenericPackage_PackageUID", // 86 + "Unique Package Identifier as a UMID" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x03, 0x03, 0x02, 0x01, 0x00, 0x00, 0x00 }, {0x44, 0x02}, true, "GenericPackage_Name", // 87 + "Human readable package name" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x02, + 0x07, 0x02, 0x01, 0x10, 0x01, 0x03, 0x00, 0x00 }, {0x44, 0x05}, false, "GenericPackage_PackageCreationDate", // 88 + "The date & time of creation of this package" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x02, + 0x07, 0x02, 0x01, 0x10, 0x02, 0x05, 0x00, 0x00 }, {0x44, 0x04}, false, "GenericPackage_PackageModifiedDate", // 89 + "The date & time of last modification of this package" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x02, + 0x06, 0x01, 0x01, 0x04, 0x06, 0x05, 0x00, 0x00 }, {0x44, 0x03}, false, "GenericPackage_Tracks", // 90 + "Array of Unique IDs of Tracks" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x02, 0x53, 0x01, 0x01, + 0x0d, 0x01, 0x01, 0x01, 0x01, 0x01, 0x32, 0x00 }, {0}, false, "NetworkLocator", // 91 + "Network Locator set for location with a URL" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00 }, {0x40, 0x01}, false, "NetworkLocator_URLString", // 92 + "A URL indicating where the essence may be found." }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x02, 0x53, 0x01, 0x01, + 0x0d, 0x01, 0x01, 0x01, 0x01, 0x01, 0x33, 0x00 }, {0}, false, "TextLocator", // 93 + "Text Locator set for location with a human-readable text string" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x02, + 0x01, 0x04, 0x01, 0x02, 0x01, 0x00, 0x00, 0x00 }, {0x41, 0x01}, false, "TextLocator_LocatorName", // 94 + "Value of a human-readable locator text string for manual location of essence" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x02, + 0x01, 0x07, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00 }, {0x48, 0x01}, false, "GenericTrack_TrackID", // 95 + "ID of the track in this package (for linking to a SourceTrackID in a segment)" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x02, + 0x01, 0x04, 0x01, 0x03, 0x00, 0x00, 0x00, 0x00 }, {0x48, 0x04}, false, "GenericTrack_TrackNumber", // 96 + "Number used to link to the track in the Essence Container if it exists" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x02, + 0x01, 0x07, 0x01, 0x02, 0x01, 0x00, 0x00, 0x00 }, {0x48, 0x02}, true, "GenericTrack_TrackName", // 97 + "Human readable name of the track type" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x02, + 0x06, 0x01, 0x01, 0x04, 0x02, 0x04, 0x00, 0x00 }, {0x48, 0x03}, false, "GenericTrack_Sequence", // 98 + "Strong Reference to Sequence Set" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x02, 0x53, 0x01, 0x01, + 0x0d, 0x01, 0x01, 0x01, 0x01, 0x01, 0x3a, 0x00 }, {0}, false, "StaticTrack", // 99 + "" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x02, 0x53, 0x01, 0x01, + 0x0d, 0x01, 0x01, 0x01, 0x01, 0x01, 0x3b, 0x00 }, {0}, false, "Track", // 100 + "Track" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x02, + 0x05, 0x30, 0x04, 0x05, 0x00, 0x00, 0x00, 0x00 }, {0x4b, 0x01}, false, "Track_EditRate", // 101 + "Edit Rate of Track" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x02, + 0x07, 0x02, 0x01, 0x03, 0x01, 0x03, 0x00, 0x00 }, {0x4b, 0x02}, false, "Track_Origin", // 102 + "An Offset used to resolved timeline references to this track. The start of the track has this timeline value measured in Edit Units." }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x02, 0x53, 0x01, 0x01, + 0x0d, 0x01, 0x01, 0x01, 0x01, 0x01, 0x39, 0x00 }, {0}, false, "EventTrack", // 103 + "Event Track" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x02, + 0x05, 0x30, 0x04, 0x02, 0x00, 0x00, 0x00, 0x00 }, {0x49, 0x01}, false, "EventTrack_EventEditRate", // 104 + "Edit Rate of Event Track" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x05, + 0x07, 0x02, 0x01, 0x03, 0x01, 0x0b, 0x00, 0x00 }, {0x49, 0x02}, true, "EventTrack_EventOrigin", // 105 + "An Offset used to resolved timeline references to this event track. The start of the event track has this timeline value measured in Edit Units." }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x02, + 0x04, 0x07, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00 }, {0x02, 0x01}, false, "StructuralComponent_DataDefinition", // 106 + "Data Definition - kind of data or metadata this structure refers to" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x02, + 0x07, 0x02, 0x02, 0x01, 0x01, 0x03, 0x00, 0x00 }, {0x02, 0x02}, false, "StructuralComponent_Duration", // 107 + "Duration (in units of edit rate)" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x02, 0x53, 0x01, 0x01, + 0x0d, 0x01, 0x01, 0x01, 0x01, 0x01, 0x0f, 0x00 }, {0}, false, "Sequence", // 108 + "Sequence" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x02, + 0x06, 0x01, 0x01, 0x04, 0x06, 0x09, 0x00, 0x00 }, {0x10, 0x01}, false, "Sequence_StructuralComponents", // 109 + "Ordered array of strong references to Structural Components" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x02, 0x53, 0x01, 0x01, + 0x0d, 0x01, 0x01, 0x01, 0x01, 0x01, 0x14, 0x00 }, {0}, false, "TimecodeComponent", // 110 + "Timecode Component" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x02, + 0x04, 0x04, 0x01, 0x01, 0x02, 0x06, 0x00, 0x00 }, {0x15, 0x02}, false, "TimecodeComponent_RoundedTimecodeBase", // 111 + "Integer frames per second" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x02, + 0x07, 0x02, 0x01, 0x03, 0x01, 0x05, 0x00, 0x00 }, {0x15, 0x01}, false, "TimecodeComponent_StartTimecode", // 112 + "Starting timecode" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x01, + 0x04, 0x04, 0x01, 0x01, 0x05, 0x00, 0x00, 0x00 }, {0x15, 0x03}, false, "TimecodeComponent_DropFrame", // 113 + "Drop frame flag" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x02, 0x53, 0x01, 0x01, + 0x0d, 0x01, 0x01, 0x01, 0x01, 0x01, 0x11, 0x00 }, {0}, false, "SourceClip", // 114 + "Source Clip" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x02, + 0x07, 0x02, 0x01, 0x03, 0x01, 0x04, 0x00, 0x00 }, {0x12, 0x01}, false, "SourceClip_StartPosition", // 115 + "Offset into Essence measured in edit units of the track containing this segment" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x02, + 0x06, 0x01, 0x01, 0x03, 0x01, 0x00, 0x00, 0x00 }, {0x11, 0x01}, false, "SourceClip_SourcePackageID", // 116 + "ID of referenced Package as a UMID" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x02, + 0x06, 0x01, 0x01, 0x03, 0x02, 0x00, 0x00, 0x00 }, {0x11, 0x02}, false, "SourceClip_SourceTrackID", // 117 + "Track ID of the referenced Track within the referenced Package" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x02, 0x53, 0x01, 0x01, + 0x0d, 0x01, 0x01, 0x01, 0x01, 0x01, 0x41, 0x00 }, {0}, false, "DMSegment", // 118 + "Descriptive Metadata Segment" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x02, + 0x07, 0x02, 0x01, 0x03, 0x03, 0x03, 0x00, 0x00 }, {0x06, 0x01}, false, "DMSegment_EventStartPosition", // 119 + "Offset into the descriptive metadata track in edit units" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x02, + 0x05, 0x30, 0x04, 0x04, 0x01, 0x00, 0x00, 0x00 }, {0x06, 0x02}, true, "DMSegment_EventComment", // 120 + "Description of the Descriptive Metadata Framework" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x04, + 0x01, 0x07, 0x01, 0x05, 0x00, 0x00, 0x00, 0x00 }, {0x61, 0x02}, false, "DMSegment_TrackIDs", // 121 + "An unordered list of track ID values that identify the tracks in this Package to which this DM Framework refers (if omitted, refers to all essence tracks)" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x05, + 0x06, 0x01, 0x01, 0x04, 0x02, 0x0c, 0x00, 0x00 }, {0x61, 0x01}, false, "DMSegment_DMFramework", // 122 + "Strong Reference to the Descriptive Metadata Framework" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x02, 0x53, 0x01, 0x01, + 0x0d, 0x01, 0x01, 0x01, 0x01, 0x01, 0x45, 0x00 }, {0}, false, "DMSourceClip", // 123 + "Descriptive Metadata SourceClip" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x05, + 0x01, 0x07, 0x01, 0x06, 0x00, 0x00, 0x00, 0x00 }, {0x61, 0x03}, true, "DMSourceClip_DMSourceClipTrackIDs", // 124 + "An unordered list of track ID values that identify the tracks in this Package to which the referenced Descriptive Metadata refers (if omitted, refers to all essence tracks)" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x02, 0x53, 0x01, 0x01, + 0x0d, 0x01, 0x01, 0x01, 0x01, 0x01, 0x36, 0x00 }, {0}, false, "MaterialPackage", // 125 + "Material Package set" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x02, 0x53, 0x01, 0x01, + 0x0d, 0x01, 0x01, 0x01, 0x01, 0x01, 0x37, 0x00 }, {0}, false, "SourcePackage", // 126 + "File Package set" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x02, + 0x06, 0x01, 0x01, 0x04, 0x02, 0x03, 0x00, 0x00 }, {0x47, 0x01}, false, "SourcePackage_Descriptor", // 127 + "Strong Reference to the Descriptor" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x02, + 0x06, 0x01, 0x01, 0x04, 0x06, 0x03, 0x00, 0x00 }, {0x2f, 0x01}, true, "GenericDescriptor_Locators", // 128 + "Ordered array of strong references to Locator sets" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x02, + 0x06, 0x01, 0x01, 0x04, 0x06, 0x10, 0x00, 0x00 }, {0}, true, "GenericDescriptor_SubDescriptors", // 129 + "Ordered array of strong references to sub descriptor sets" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x02, 0x53, 0x01, 0x01, + 0x0d, 0x01, 0x01, 0x01, 0x01, 0x01, 0x25, 0x00 }, {0}, false, "FileDescriptor", // 130 + "File Descriptor" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x05, + 0x06, 0x01, 0x01, 0x03, 0x05, 0x00, 0x00, 0x00 }, {0x30, 0x06}, true, "FileDescriptor_LinkedTrackID", // 131 + "Link to (i.e. value of) the Track ID of the Track in this Package to which the Descriptor applies" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x01, + 0x04, 0x06, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00 }, {0x30, 0x01}, false, "FileDescriptor_SampleRate", // 132 + "The field or frame rate of the Essence Container (not the essence sampling clock rate)" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x01, + 0x04, 0x06, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00 }, {0x30, 0x02}, true, "FileDescriptor_ContainerDuration", // 133 + "Duration of Essence Container (measured in Edit Units)" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x02, + 0x06, 0x01, 0x01, 0x04, 0x01, 0x02, 0x00, 0x00 }, {0x30, 0x04}, false, "FileDescriptor_EssenceContainer", // 134 + "The UL identifying the Essence Container described by this Descriptor" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x02, + 0x06, 0x01, 0x01, 0x04, 0x01, 0x03, 0x00, 0x00 }, {0x30, 0x05}, true, "FileDescriptor_Codec", // 135 + "UL to identify a codec compatible with this Essence Container" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x02, 0x53, 0x01, 0x01, + 0x0d, 0x01, 0x01, 0x01, 0x01, 0x01, 0x27, 0x00 }, {0}, false, "GenericPictureEssenceDescriptor", // 136 + "Defines the Picture Essence Descriptor set" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x05, + 0x04, 0x05, 0x01, 0x13, 0x00, 0x00, 0x00, 0x00 }, {0x32, 0x15}, true, "GenericPictureEssenceDescriptor_SignalStandard", // 137 + "Underlying signal standard" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x01, + 0x04, 0x01, 0x03, 0x01, 0x04, 0x00, 0x00, 0x00 }, {0x32, 0x0c}, false, "GenericPictureEssenceDescriptor_FrameLayout", // 138 + "Interlace or Progressive layout" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x01, + 0x04, 0x01, 0x05, 0x02, 0x02, 0x00, 0x00, 0x00 }, {0x32, 0x03}, false, "GenericPictureEssenceDescriptor_StoredWidth", // 139 + "Horizontal Size of active picture" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x01, + 0x04, 0x01, 0x05, 0x02, 0x01, 0x00, 0x00, 0x00 }, {0x32, 0x02}, false, "GenericPictureEssenceDescriptor_StoredHeight", // 140 + "Vertical Field Size of active picture" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x05, + 0x04, 0x01, 0x03, 0x02, 0x08, 0x00, 0x00, 0x00 }, {0x32, 0x16}, true, "GenericPictureEssenceDescriptor_StoredF2Offset", // 141 + "Topness Adjustment for stored picture" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x01, + 0x04, 0x01, 0x05, 0x01, 0x08, 0x00, 0x00, 0x00 }, {0x32, 0x05}, true, "GenericPictureEssenceDescriptor_SampledWidth", // 142 + "Sampled width supplied to codec" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x01, + 0x04, 0x01, 0x05, 0x01, 0x07, 0x00, 0x00, 0x00 }, {0x32, 0x04}, true, "GenericPictureEssenceDescriptor_SampledHeight", // 143 + "Sampled height supplied to codec" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x01, + 0x04, 0x01, 0x05, 0x01, 0x09, 0x00, 0x00, 0x00 }, {0x32, 0x06}, true, "GenericPictureEssenceDescriptor_SampledXOffset", // 144 + "Offset from sampled to stored width" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x01, + 0x04, 0x01, 0x05, 0x01, 0x0a, 0x00, 0x00, 0x00 }, {0x32, 0x07}, true, "GenericPictureEssenceDescriptor_SampledYOffset", // 145 + "Offset from sampled to stored" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x01, + 0x04, 0x01, 0x05, 0x01, 0x0b, 0x00, 0x00, 0x00 }, {0x32, 0x08}, true, "GenericPictureEssenceDescriptor_DisplayHeight", // 146 + "Displayed Height placed in Production Aperture" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x01, + 0x04, 0x01, 0x05, 0x01, 0x0c, 0x00, 0x00, 0x00 }, {0x32, 0x09}, true, "GenericPictureEssenceDescriptor_DisplayWidth", // 147 + "Displayed Width placed in Production Aperture" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x01, + 0x04, 0x01, 0x05, 0x01, 0x0d, 0x00, 0x00, 0x00 }, {0x32, 0x0a}, true, "GenericPictureEssenceDescriptor_DisplayXOffset", // 148 + "The horizontal offset from the (in pixels) of the picture as displayed" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x01, + 0x04, 0x01, 0x05, 0x01, 0x0e, 0x00, 0x00, 0x00 }, {0x32, 0x0b}, true, "GenericPictureEssenceDescriptor_DisplayYOffset", // 149 + "The vertical offset (in pixels) of the picture as displayed" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x05, + 0x04, 0x01, 0x03, 0x02, 0x07, 0x00, 0x00, 0x00 }, {0x32, 0x17}, true, "GenericPictureEssenceDescriptor_DisplayF2Offset", // 150 + "Topness Adjustment for Displayed Picture" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x01, + 0x04, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00 }, {0x32, 0x0e}, false, "GenericPictureEssenceDescriptor_AspectRatio", // 151 + "Specifies the horizontal to vertical aspect ratio of the whole image as it is to be presented to avoid geometric distortion (and hence including any black edges)" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x05, + 0x04, 0x01, 0x03, 0x02, 0x09, 0x00, 0x00, 0x00 }, {0x32, 0x18}, true, "GenericPictureEssenceDescriptor_ActiveFormatDescriptor", // 152 + "Specifies the intended framing of the content within the displayed image" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x02, + 0x04, 0x01, 0x03, 0x02, 0x05, 0x00, 0x00, 0x00 }, {0x32, 0x0d}, false, "GenericPictureEssenceDescriptor_VideoLineMap", // 153 + "First active line in each field" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x02, + 0x05, 0x20, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00 }, {0x32, 0x0f}, true, "GenericPictureEssenceDescriptor_AlphaTransparency", // 154 + "Is Alpha Inverted" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x02, + 0x04, 0x01, 0x02, 0x01, 0x01, 0x01, 0x02, 0x00 }, {0x32, 0x10}, true, "GenericPictureEssenceDescriptor_Gamma", // 155 + "Registered UL of known Gamma" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x02, + 0x04, 0x18, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00 }, {0x32, 0x11}, true, "GenericPictureEssenceDescriptor_ImageAlignmentOffset", // 156 + "Byte Boundary alignment required for Low Level Essence Storage" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x02, + 0x04, 0x18, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00 }, {0x32, 0x13}, true, "GenericPictureEssenceDescriptor_ImageStartOffset", // 157 + "Unused bytes before start of stored data" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x02, + 0x04, 0x18, 0x01, 0x03, 0x00, 0x00, 0x00, 0x00 }, {0x32, 0x14}, true, "GenericPictureEssenceDescriptor_ImageEndOffset", // 158 + "Unused bytes before start of stored data" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x02, + 0x04, 0x01, 0x03, 0x01, 0x06, 0x00, 0x00, 0x00 }, {0x32, 0x12}, true, "GenericPictureEssenceDescriptor_FieldDominance", // 159 + "The number of the field which is considered temporally to come first" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x02, + 0x04, 0x01, 0x06, 0x01, 0x00, 0x00, 0x00, 0x00 }, {0x32, 0x01}, false, "GenericPictureEssenceDescriptor_PictureEssenceCoding", // 160 + "UL identifying the Picture Compression Scheme" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x02, 0x53, 0x01, 0x01, + 0x0d, 0x01, 0x01, 0x01, 0x01, 0x01, 0x28, 0x00 }, {0}, false, "CDCIEssenceDescriptor", // 161 + "Defines the CDCI Picture Essence Descriptor set" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x02, + 0x04, 0x01, 0x05, 0x03, 0x0a, 0x00, 0x00, 0x00 }, {0x33, 0x01}, false, "CDCIEssenceDescriptor_ComponentDepth", // 162 + "Number of active bits per sample (e.g. 8, 10, 16)" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x01, + 0x04, 0x01, 0x05, 0x01, 0x05, 0x00, 0x00, 0x00 }, {0x33, 0x02}, false, "CDCIEssenceDescriptor_HorizontalSubsampling", // 163 + "Specifies the H colour subsampling" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x02, + 0x04, 0x01, 0x05, 0x01, 0x10, 0x00, 0x00, 0x00 }, {0x33, 0x08}, true, "CDCIEssenceDescriptor_VerticalSubsampling", // 164 + "Specifies the V colour subsampling" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x01, + 0x04, 0x01, 0x05, 0x01, 0x06, 0x00, 0x00, 0x00 }, {0x33, 0x03}, true, "CDCIEssenceDescriptor_ColorSiting", // 165 + "Enumerated value describing the color siting" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x05, + 0x03, 0x01, 0x02, 0x01, 0x0a, 0x00, 0x00, 0x00 }, {0x33, 0x0b}, true, "CDCIEssenceDescriptor_ReversedByteOrder", // 166 + "a FALSE value denotes Chroma followed by Luma pexels according to ITU Rec. 601" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x02, + 0x04, 0x18, 0x01, 0x04, 0x00, 0x00, 0x00, 0x00 }, {0x33, 0x07}, true, "CDCIEssenceDescriptor_PaddingBits", // 167 + "Bits to round up each pixel to stored size" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x02, + 0x04, 0x01, 0x05, 0x03, 0x07, 0x00, 0x00, 0x00 }, {0x33, 0x09}, true, "CDCIEssenceDescriptor_AlphaSampleDepth", // 168 + "Number of bits per alpha sample" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x01, + 0x04, 0x01, 0x05, 0x03, 0x03, 0x00, 0x00, 0x00 }, {0x33, 0x04}, true, "CDCIEssenceDescriptor_BlackRefLevel", // 169 + "Black refernece level e.g. 16 or 64 (8 or 10-bits)" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x01, + 0x04, 0x01, 0x05, 0x03, 0x04, 0x00, 0x00, 0x00 }, {0x33, 0x05}, true, "CDCIEssenceDescriptor_WhiteReflevel", // 170 + "White reference level e.g. 235 or 943 (8 or 10 bits)" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x02, + 0x04, 0x01, 0x05, 0x03, 0x05, 0x00, 0x00, 0x00 }, {0x33, 0x06}, true, "CDCIEssenceDescriptor_ColorRange", // 171 + "Color range e.g. 225 or 897 (8 or 10 bits)" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x02, 0x53, 0x01, 0x01, + 0x0d, 0x01, 0x01, 0x01, 0x01, 0x01, 0x29, 0x00 }, {0}, false, "RGBAEssenceDescriptor", // 172 + "Defines the RGBA Picture Essence Descriptor set" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x05, + 0x04, 0x01, 0x05, 0x03, 0x0b, 0x00, 0x00, 0x00 }, {0x34, 0x06}, true, "RGBAEssenceDescriptor_ComponentMaxRef", // 173 + "Maximum value for RGB components, e.g. 235 or 940 (8 or 10 bits)" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x05, + 0x04, 0x01, 0x05, 0x03, 0x0c, 0x00, 0x00, 0x00 }, {0x34, 0x07}, true, "RGBAEssenceDescriptor_ComponentMinRef", // 174 + "Minimum value for RGB components, e.g. 16 or 64 (8 or 10 bits)" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x05, + 0x04, 0x01, 0x05, 0x03, 0x0d, 0x00, 0x00, 0x00 }, {0x34, 0x08}, true, "RGBAEssenceDescriptor_AlphaMaxRef", // 175 + "Maximum value for alpha component, e.g. 235 or 940 (8 or 10 bits)" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x05, + 0x04, 0x01, 0x05, 0x03, 0x0e, 0x00, 0x00, 0x00 }, {0x34, 0x09}, true, "RGBAEssenceDescriptor_AlphaMinRef", // 176 + "Minimum value for alpha component, e.g. 16 or 64 (8 or 10 bits)" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x05, + 0x04, 0x01, 0x04, 0x04, 0x01, 0x00, 0x00, 0x00 }, {0x34, 0x05}, true, "RGBAEssenceDescriptor_ScanningDirection", // 177 + "Enumerated Scanning Direction" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x02, + 0x04, 0x01, 0x05, 0x03, 0x06, 0x00, 0x00, 0x00 }, {0x34, 0x01}, false, "RGBAEssenceDescriptor_PixelLayout", // 178 + "Pixel Layout" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x02, + 0x04, 0x01, 0x05, 0x03, 0x08, 0x00, 0x00, 0x00 }, {0x34, 0x03}, true, "RGBAEssenceDescriptor_Palette", // 179 + "Palette" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x02, + 0x04, 0x01, 0x05, 0x03, 0x09, 0x00, 0x00, 0x00 }, {0x34, 0x04}, true, "RGBAEssenceDescriptor_PaletteLayout", // 180 + "Palette Layout" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x02, 0x53, 0x01, 0x01, + 0x0d, 0x01, 0x01, 0x01, 0x01, 0x01, 0x42, 0x00 }, {0}, false, "GenericSoundEssenceDescriptor", // 181 + "Defines the Sound Essence Descriptor set" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x05, + 0x04, 0x02, 0x03, 0x01, 0x01, 0x01, 0x00, 0x00 }, {0x3d, 0x03}, false, "GenericSoundEssenceDescriptor_AudioSamplingRate", // 182 + "Sampling rate of the audio essence" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x04, + 0x04, 0x02, 0x03, 0x01, 0x04, 0x00, 0x00, 0x00 }, {0x3d, 0x02}, false, "GenericSoundEssenceDescriptor_Locked", // 183 + "Boolean indicating that the Number of samples per frame is locked or unlocked (non-0 = locked)" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x01, + 0x04, 0x02, 0x01, 0x01, 0x03, 0x00, 0x00, 0x00 }, {0x3d, 0x04}, true, "GenericSoundEssenceDescriptor_AudioRefLevel", // 184 + "Audio reference level which gives the number of dBm for 0VU" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x01, + 0x04, 0x02, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00 }, {0x3d, 0x05}, true, "GenericSoundEssenceDescriptor_ElectroSpatialFormulation", // 185 + "E.g. mono, dual mono, stereo, A,B etc (enum)" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x05, + 0x04, 0x02, 0x01, 0x01, 0x04, 0x00, 0x00, 0x00 }, {0x3d, 0x07}, false, "GenericSoundEssenceDescriptor_ChannelCount", // 186 + "Number of Sound Channels" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x04, + 0x04, 0x02, 0x03, 0x03, 0x04, 0x00, 0x00, 0x00 }, {0x3d, 0x01}, false, "GenericSoundEssenceDescriptor_QuantizationBits", // 187 + "Number of quantization bits" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x05, + 0x04, 0x02, 0x07, 0x01, 0x00, 0x00, 0x00, 0x00 }, {0x3d, 0x0c}, true, "GenericSoundEssenceDescriptor_DialNorm", // 188 + "Gain to be applied to normalise perceived loudness of the clip, defined by ATSC A/53 (1dB per step)" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x02, + 0x04, 0x02, 0x04, 0x02, 0x00, 0x00, 0x00, 0x00 }, {0x3d, 0x06}, false, "GenericSoundEssenceDescriptor_SoundEssenceCompression", // 189 + "UL identifying the Sound Compression Scheme" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x02, 0x53, 0x01, 0x01, + 0x0d, 0x01, 0x01, 0x01, 0x01, 0x01, 0x43, 0x00 }, {0}, false, "GenericDataEssenceDescriptor", // 190 + "Defines the Data Essence Descriptor set" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x05, + 0x04, 0x03, 0x03, 0x02, 0x00, 0x00, 0x00, 0x00 }, {0x3e, 0x01}, false, "GenericDataEssenceDescriptor_DataEssenceCoding", // 191 + "Specifies the data essence coding type" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x02, 0x53, 0x01, 0x01, + 0x0d, 0x01, 0x01, 0x01, 0x01, 0x01, 0x44, 0x00 }, {0}, false, "MultipleDescriptor", // 192 + "Defines the Multiple Descriptor set" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x04, + 0x06, 0x01, 0x01, 0x04, 0x06, 0x0b, 0x00, 0x00 }, {0x3f, 0x01}, false, "MultipleDescriptor_SubDescriptorUIDs", // 193 + "Unordered array of strong references to File Descriptor sets (1 per interleaved item within the Essence Container)" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x02, 0x53, 0x01, 0x01, + 0x0d, 0x01, 0x01, 0x01, 0x01, 0x01, 0x51, 0x00 }, {0}, false, "MPEG2VideoDescriptor", // 194 + "Defines the MPEG2 Picture Essence Descriptor set" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x05, + 0x04, 0x01, 0x06, 0x02, 0x01, 0x02, 0x00, 0x00 }, {0}, true, "MPEG2VideoDescriptor_SingleSequence", // 195 + "TRUE if the essence consists of a single MPEG sequence. False if there are a number of sequences. This flag implies that the sequence header information is not varying in the essence stream." }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x05, + 0x04, 0x01, 0x06, 0x02, 0x01, 0x03, 0x00, 0x00 }, {0}, true, "MPEG2VideoDescriptor_ConstantBFrames", // 196 + "TRUE if the number of B frames is always constant" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x05, + 0x04, 0x01, 0x06, 0x02, 0x01, 0x04, 0x00, 0x00 }, {0}, true, "MPEG2VideoDescriptor_CodedContentType", // 197 + "0= "Unknown",1= "Progressive", 2= "Interlaced", 3= "Mixed": an enumerated value which tells if the underlying content which was MPEG coded was of a known type" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x05, + 0x04, 0x01, 0x06, 0x02, 0x01, 0x05, 0x00, 0x00 }, {0}, true, "MPEG2VideoDescriptor_LowDelay", // 198 + "TRUE if low delay mode was used in the sequence" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x05, + 0x04, 0x01, 0x06, 0x02, 0x01, 0x06, 0x00, 0x00 }, {0}, true, "MPEG2VideoDescriptor_ClosedGOP", // 199 + "TRUE if closed_gop is set in all GOP Headers, per 13818-1 IBP descriptor" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x05, + 0x04, 0x01, 0x06, 0x02, 0x01, 0x07, 0x00, 0x00 }, {0}, true, "MPEG2VideoDescriptor_IdenticalGOP", // 200 + "TRUE if every GOP in the sequence is constructed the same, per 13818-1 IBP descriptor" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x05, + 0x04, 0x01, 0x06, 0x02, 0x01, 0x08, 0x00, 0x00 }, {0}, true, "MPEG2VideoDescriptor_MaxGOP", // 201 + "Specifies the maximum occurring spacing between I frames, per 13818-1 IBP descriptor. A value of 0 or the absence of this property implies no limit to the maximum GOP" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x05, + 0x04, 0x01, 0x06, 0x02, 0x01, 0x09, 0x00, 0x00 }, {0}, true, "MPEG2VideoDescriptor_BPictureCount", // 202 + "Specifies the maximum number of B pictures between P or I frames, equivalent to 13818-2 annex D (M-1)" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x05, + 0x04, 0x01, 0x06, 0x02, 0x01, 0x0b, 0x00, 0x00 }, {0}, true, "MPEG2VideoDescriptor_BitRate", // 203 + "Maximum bit rate of MPEG video elementary stream in bit/s as defined in ISO-13818-2 bit_rate property" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x05, + 0x04, 0x01, 0x06, 0x02, 0x01, 0x0a, 0x00, 0x00 }, {0}, true, "MPEG2VideoDescriptor_ProfileAndLevel", // 204 + "Specifies the MPEG-2 video profile and level. The value is taken directly from the profile_and_level_indication in the MPEG-2 sequence header extension. For main profile @ main level, the value is 0x48. For 4:2:2 profile @ main level, the value is 0x85" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x02, 0x53, 0x01, 0x01, + 0x0d, 0x01, 0x01, 0x01, 0x01, 0x01, 0x48, 0x00 }, {0}, false, "WaveAudioDescriptor", // 205 + "Defines the Wave Audio Essence Descriptor Set" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x05, + 0x04, 0x02, 0x03, 0x02, 0x01, 0x00, 0x00, 0x00 }, {0x3d, 0x0a}, false, "WaveAudioDescriptor_BlockAlign", // 206 + "Sample Block alignment" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x05, + 0x04, 0x02, 0x03, 0x02, 0x02, 0x00, 0x00, 0x00 }, {0x3d, 0x0b}, true, "WaveAudioDescriptor_SequenceOffset", // 207 + "Zero-based ordinal frame number of first essence data within five-frame sequence" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x05, + 0x04, 0x02, 0x03, 0x03, 0x05, 0x00, 0x00, 0x00 }, {0x3d, 0x09}, false, "WaveAudioDescriptor_AvgBps", // 208 + "Average Bytes per second" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x05, + 0x04, 0x02, 0x03, 0x01, 0x0e, 0x00, 0x00, 0x00 }, {0x3d, 0x0e}, true, "WaveAudioDescriptor_PeakEnvelope", // 209 + "Peak Envelope from <LEVL> Chunk" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x02, 0x53, 0x01, 0x01, + 0x0d, 0x01, 0x01, 0x01, 0x01, 0x01, 0x5a, 0x00 }, {0}, false, "JPEG2000PictureSubDescriptor", // 210 + "JPEG 2000 Picture Sub Descriptor" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x0a, + 0x04, 0x01, 0x06, 0x03, 0x01, 0x00, 0x00, 0x00 }, {0}, false, "JPEG2000PictureSubDescriptor_Rsize", // 211 + "An enumerated value that defines the decoder capabilities" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x0a, + 0x04, 0x01, 0x06, 0x03, 0x02, 0x00, 0x00, 0x00 }, {0}, false, "JPEG2000PictureSubDescriptor_Xsize", // 212 + "Width of the reference grid" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x0a, + 0x04, 0x01, 0x06, 0x03, 0x03, 0x00, 0x00, 0x00 }, {0}, false, "JPEG2000PictureSubDescriptor_Ysize", // 213 + "Height of the reference grid" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x0a, + 0x04, 0x01, 0x06, 0x03, 0x04, 0x00, 0x00, 0x00 }, {0}, false, "JPEG2000PictureSubDescriptor_XOsize", // 214 + "Horizontal offset from the origin of the reference grid to the left side of the image area" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x0a, + 0x04, 0x01, 0x06, 0x03, 0x05, 0x00, 0x00, 0x00 }, {0}, false, "JPEG2000PictureSubDescriptor_YOsize", // 215 + "Vertical offset from the origin of the reference grid to the top side of the image area" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x0a, + 0x04, 0x01, 0x06, 0x03, 0x06, 0x00, 0x00, 0x00 }, {0}, false, "JPEG2000PictureSubDescriptor_XTsize", // 216 + "Width of one reference tile with respect to the reference grid" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x0a, + 0x04, 0x01, 0x06, 0x03, 0x07, 0x00, 0x00, 0x00 }, {0}, false, "JPEG2000PictureSubDescriptor_YTsize", // 217 + "Height of one reference tile with respect to the reference grid" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x0a, + 0x04, 0x01, 0x06, 0x03, 0x08, 0x00, 0x00, 0x00 }, {0}, false, "JPEG2000PictureSubDescriptor_XTOsize", // 218 + "Horizontal offset from the origin of the reference grid to the left side of the first tile" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x0a, + 0x04, 0x01, 0x06, 0x03, 0x09, 0x00, 0x00, 0x00 }, {0}, false, "JPEG2000PictureSubDescriptor_YTOsize", // 219 + "Vertical offset from the origin of the reference grid to the top side of the first tile" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x0a, + 0x04, 0x01, 0x06, 0x03, 0x0a, 0x00, 0x00, 0x00 }, {0}, false, "JPEG2000PictureSubDescriptor_Csize", // 220 + "The number of components in the picture" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x0a, + 0x04, 0x01, 0x06, 0x03, 0x0b, 0x00, 0x00, 0x00 }, {0}, false, "JPEG2000PictureSubDescriptor_PictureComponentSizing", // 221 + "Array of picture components" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x0a, + 0x04, 0x01, 0x06, 0x03, 0x0c, 0x00, 0x00, 0x00 }, {0}, true, "JPEG2000PictureSubDescriptor_CodingStyleDefault", // 222 + "Default coding style for all components. Use this value only if static for all pictures in the Essence Container" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x0a, + 0x04, 0x01, 0x06, 0x03, 0x0d, 0x00, 0x00, 0x00 }, {0}, true, "JPEG2000PictureSubDescriptor_QuantizationDefault", // 223 + "Default quantization style for all components. Use this value only if static for all pictures in the Essence Container" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x02, 0x53, 0x01, 0x01, + 0x0d, 0x01, 0x04, 0x01, 0x00, 0x00, 0x00, 0x00 }, {0}, false, "DM_Framework", // 224 + "Superclass for all concrete DM Frameworks" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x02, 0x53, 0x01, 0x01, + 0x0d, 0x01, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00 }, {0}, false, "DM_Set", // 225 + "Superclass for all concrete DM Frameworks" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x04, 0x01, 0x01, 0x07, + 0x0d, 0x01, 0x03, 0x01, 0x02, 0x0b, 0x01, 0x00 }, {0}, false, "EncryptedContainerLabel", // 226 + "DCP-Crypto Encrypted Essence Container, frame-wrapped" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x04, 0x01, 0x01, 0x07, + 0x0d, 0x01, 0x04, 0x01, 0x02, 0x01, 0x01, 0x00 }, {0}, false, "CryptographicFrameworkLabel", // 227 + "DCP-Crypto Framework" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x02, 0x53, 0x01, 0x01, + 0x0d, 0x01, 0x04, 0x01, 0x02, 0x01, 0x00, 0x00 }, {0}, false, "CryptographicFramework", // 228 + "DCP-Encryption Cryptographic Framework" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x09, + 0x06, 0x01, 0x01, 0x04, 0x02, 0x0d, 0x00, 0x00 }, {0}, false, "CryptographicFramework_ContextSR", // 229 + "Strong Reference to the associated Cryptographic Context" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x02, 0x53, 0x01, 0x01, + 0x0d, 0x01, 0x04, 0x01, 0x02, 0x02, 0x00, 0x00 }, {0}, false, "CryptographicContext", // 230 + "cryptographic information that applies to encrypted essence tracks as a whole" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x09, + 0x01, 0x01, 0x15, 0x11, 0x00, 0x00, 0x00, 0x00 }, {0}, false, "CryptographicContext_ContextID", // 231 + "Persistent Unique identifier for the context" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x09, + 0x06, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x00 }, {0}, false, "CryptographicContext_SourceEssenceContainer", // 232 + "Essence Container Label for the source essence, prior to encryption" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x09, + 0x02, 0x09, 0x03, 0x01, 0x01, 0x00, 0x00, 0x00 }, {0}, false, "CryptographicContext_CipherAlgorithm", // 233 + "Algorithm used for Triplet encryption, if any" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x09, + 0x02, 0x09, 0x03, 0x02, 0x01, 0x00, 0x00, 0x00 }, {0}, false, "CryptographicContext_MICAlgorithm", // 234 + "Algorithm used for Triplet integrity, if any" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x09, + 0x02, 0x09, 0x03, 0x01, 0x02, 0x00, 0x00, 0x00 }, {0}, false, "CryptographicContext_CryptographicKeyID", // 235 + "Unique identifier for the cryptographic key" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x02, 0x04, 0x01, 0x07, + 0x0d, 0x01, 0x03, 0x01, 0x02, 0x7e, 0x01, 0x00 }, {0}, false, "EncryptedTriplet", // 236 + "encrypted data and cryptographic information specific to the Triplet" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x09, + 0x06, 0x01, 0x01, 0x06, 0x03, 0x00, 0x00, 0x00 }, {0}, false, "EncryptedTriplet_ContextIDLink", // 237 + "Persistent Unique identifier for the context.associated with this Triplet" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x09, + 0x06, 0x09, 0x02, 0x01, 0x03, 0x00, 0x00, 0x00 }, {0}, false, "EncryptedTriplet_PlaintextOffset", // 238 + "Offset within the source at which encryption starts" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x09, + 0x06, 0x01, 0x01, 0x02, 0x03, 0x00, 0x00, 0x00 }, {0}, false, "EncryptedTriplet_SourceKey", // 239 + "Key of the source Triplet" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x09, + 0x04, 0x06, 0x10, 0x02, 0x00, 0x00, 0x00, 0x00 }, {0}, false, "EncryptedTriplet_SourceLength", // 240 + "Length of the value of the source Triplet" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x09, + 0x02, 0x09, 0x03, 0x01, 0x03, 0x00, 0x00, 0x00 }, {0}, false, "EncryptedTriplet_EncryptedSourceValue", // 241 + "Encrypted Source value starting at Plaintext Offset" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x09, + 0x06, 0x01, 0x01, 0x06, 0x02, 0x00, 0x00, 0x00 }, {0}, true, "EncryptedTriplet_TrackFileID", // 242 + "The identifier of the AS-DCP Track File containing this Triplet" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x09, + 0x06, 0x10, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00 }, {0}, true, "EncryptedTriplet_SequenceNumber", // 243 + "Sequence number of this Triplet within the Track File" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x09, + 0x02, 0x09, 0x03, 0x02, 0x02, 0x00, 0x00, 0x00 }, {0}, true, "EncryptedTriplet_MIC", // 244 + "Keyed HMAC" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x04, 0x01, 0x01, 0x07, + 0x02, 0x09, 0x02, 0x01, 0x01, 0x00, 0x00, 0x00 }, {0}, false, "CipherAlgorithmAES128CBC", // 245 + "Identifes the use of AES128 CBC mode cipher algorithm" }, + + { { 0x06, 0x0e, 0x2b, 0x34, 0x04, 0x01, 0x01, 0x07, + 0x02, 0x09, 0x02, 0x02, 0x01, 0x00, 0x00, 0x00 }, {0}, false, "HMACAlgorithmSHA1128", // 246 + "Identifes the use of SHA1 128 bit HMAC algorithm" }, + + { 0, 0, 0 } +}; + +const ui32_t MDDindex_KLVFill = 4; +const ui32_t MDDindex_PartitionMetadata_MajorVersion = 5; +const ui32_t MDDindex_PartitionMetadata_MinorVersion = 6; +const ui32_t MDDindex_PartitionMetadata_KAGSize = 7; +const ui32_t MDDindex_PartitionMetadata_ThisPartition = 8; +const ui32_t MDDindex_PartitionMetadata_PreviousPartition = 9; +const ui32_t MDDindex_PartitionMetadata_FooterPartition = 10; +const ui32_t MDDindex_PartitionMetadata_HeaderByteCount = 11; +const ui32_t MDDindex_PartitionMetadata_IndexByteCount = 12; +const ui32_t MDDindex_PartitionMetadata_IndexSID = 13; +const ui32_t MDDindex_PartitionMetadata_BodyOffset = 14; +const ui32_t MDDindex_PartitionMetadata_BodySID = 15; +const ui32_t MDDindex_PartitionMetadata_OperationalPattern = 16; +const ui32_t MDDindex_PartitionMetadata_EssenceContainers = 17; +const ui32_t MDDindex_OpenHeader = 18; +const ui32_t MDDindex_OpenCompleteHeader = 19; +const ui32_t MDDindex_ClosedHeader = 20; +const ui32_t MDDindex_ClosedCompleteHeader = 21; +const ui32_t MDDindex_OpenBodyPartition = 22; +const ui32_t MDDindex_OpenCompleteBodyPartition = 23; +const ui32_t MDDindex_ClosedBodyPartition = 24; +const ui32_t MDDindex_ClosedCompleteBodyPartition = 25; +const ui32_t MDDindex_Footer = 26; +const ui32_t MDDindex_CompleteFooter = 27; +const ui32_t MDDindex_Primer = 28; +const ui32_t MDDindex_Primer_LocalTagEntryBatch = 29; +const ui32_t MDDindex_LocalTagEntryBatch_Primer_LocalTag = 30; +const ui32_t MDDindex_LocalTagEntryBatch_Primer_UID = 31; +const ui32_t MDDindex_InterchangeObject_InstanceUID = 32; +const ui32_t MDDindex_GenerationInterchangeObject_GenerationUID = 33; +const ui32_t MDDindex_DefaultObject = 34; +const ui32_t MDDindex_IndexTableSegmentBase_IndexEditRate = 35; +const ui32_t MDDindex_IndexTableSegmentBase_IndexStartPosition = 36; +const ui32_t MDDindex_IndexTableSegmentBase_IndexDuration = 37; +const ui32_t MDDindex_IndexTableSegmentBase_EditUnitByteCount = 38; +const ui32_t MDDindex_IndexTableSegmentBase_IndexSID = 39; +const ui32_t MDDindex_IndexTableSegmentBase_BodySID = 40; +const ui32_t MDDindex_IndexTableSegmentBase_SliceCount = 41; +const ui32_t MDDindex_IndexTableSegmentBase_PosTableCount = 42; +const ui32_t MDDindex_IndexTableSegment = 43; +const ui32_t MDDindex_IndexTableSegment_DeltaEntryArray = 44; +const ui32_t MDDindex_DeltaEntryArray_IndexTableSegment_PosTableIndex = 45; +const ui32_t MDDindex_DeltaEntryArray_IndexTableSegment_Slice = 46; +const ui32_t MDDindex_DeltaEntryArray_IndexTableSegment_ElementDelta = 47; +const ui32_t MDDindex_IndexTableSegment_IndexEntryArray = 48; +const ui32_t MDDindex_IndexEntryArray_IndexTableSegment_TemporalOffset = 49; +const ui32_t MDDindex_IndexEntryArray_IndexTableSegment_AnchorOffset = 50; +const ui32_t MDDindex_IndexEntryArray_IndexTableSegment_Flags = 51; +const ui32_t MDDindex_IndexEntryArray_IndexTableSegment_StreamOffset = 52; +const ui32_t MDDindex_IndexEntryArray_IndexTableSegment_SliceOffsetArray = 53; +const ui32_t MDDindex_IndexEntryArray_IndexTableSegment_PosTableArray = 54; +const ui32_t MDDindex_RandomIndexMetadata = 55; +const ui32_t MDDindex_PartitionArray_RandomIndexMetadata_BodySID = 56; +const ui32_t MDDindex_PartitionArray_RandomIndexMetadata_ByteOffset = 57; +const ui32_t MDDindex_RandomIndexMetadata_Length = 58; +const ui32_t MDDindex_Preface = 59; +const ui32_t MDDindex_Preface_LastModifiedDate = 60; +const ui32_t MDDindex_Preface_Version = 61; +const ui32_t MDDindex_Preface_ObjectModelVersion = 62; +const ui32_t MDDindex_Preface_PrimaryPackage = 63; +const ui32_t MDDindex_Preface_Identifications = 64; +const ui32_t MDDindex_Preface_ContentStorage = 65; +const ui32_t MDDindex_Preface_OperationalPattern = 66; +const ui32_t MDDindex_Preface_EssenceContainers = 67; +const ui32_t MDDindex_Preface_DMSchemes = 68; +const ui32_t MDDindex_Identification = 69; +const ui32_t MDDindex_Identification_ThisGenerationUID = 70; +const ui32_t MDDindex_Identification_CompanyName = 71; +const ui32_t MDDindex_Identification_ProductName = 72; +const ui32_t MDDindex_Identification_ProductVersion = 73; +const ui32_t MDDindex_Identification_VersionString = 74; +const ui32_t MDDindex_Identification_ProductUID = 75; +const ui32_t MDDindex_Identification_ModificationDate = 76; +const ui32_t MDDindex_Identification_ToolkitVersion = 77; +const ui32_t MDDindex_Identification_Platform = 78; +const ui32_t MDDindex_ContentStorage = 79; +const ui32_t MDDindex_ContentStorage_Packages = 80; +const ui32_t MDDindex_ContentStorage_EssenceContainerData = 81; +const ui32_t MDDindex_EssenceContainerData = 82; +const ui32_t MDDindex_EssenceContainerData_LinkedPackageUID = 83; +const ui32_t MDDindex_EssenceContainerData_IndexSID = 84; +const ui32_t MDDindex_EssenceContainerData_BodySID = 85; +const ui32_t MDDindex_GenericPackage_PackageUID = 86; +const ui32_t MDDindex_GenericPackage_Name = 87; +const ui32_t MDDindex_GenericPackage_PackageCreationDate = 88; +const ui32_t MDDindex_GenericPackage_PackageModifiedDate = 89; +const ui32_t MDDindex_GenericPackage_Tracks = 90; +const ui32_t MDDindex_NetworkLocator = 91; +const ui32_t MDDindex_NetworkLocator_URLString = 92; +const ui32_t MDDindex_TextLocator = 93; +const ui32_t MDDindex_TextLocator_LocatorName = 94; +const ui32_t MDDindex_GenericTrack_TrackID = 95; +const ui32_t MDDindex_GenericTrack_TrackNumber = 96; +const ui32_t MDDindex_GenericTrack_TrackName = 97; +const ui32_t MDDindex_GenericTrack_Sequence = 98; +const ui32_t MDDindex_StaticTrack = 99; +const ui32_t MDDindex_Track = 100; +const ui32_t MDDindex_Track_EditRate = 101; +const ui32_t MDDindex_Track_Origin = 102; +const ui32_t MDDindex_EventTrack = 103; +const ui32_t MDDindex_EventTrack_EventEditRate = 104; +const ui32_t MDDindex_EventTrack_EventOrigin = 105; +const ui32_t MDDindex_StructuralComponent_DataDefinition = 106; +const ui32_t MDDindex_StructuralComponent_Duration = 107; +const ui32_t MDDindex_Sequence = 108; +const ui32_t MDDindex_Sequence_StructuralComponents = 109; +const ui32_t MDDindex_TimecodeComponent = 110; +const ui32_t MDDindex_TimecodeComponent_RoundedTimecodeBase = 111; +const ui32_t MDDindex_TimecodeComponent_StartTimecode = 112; +const ui32_t MDDindex_TimecodeComponent_DropFrame = 113; +const ui32_t MDDindex_SourceClip = 114; +const ui32_t MDDindex_SourceClip_StartPosition = 115; +const ui32_t MDDindex_SourceClip_SourcePackageID = 116; +const ui32_t MDDindex_SourceClip_SourceTrackID = 117; +const ui32_t MDDindex_DMSegment = 118; +const ui32_t MDDindex_DMSegment_EventStartPosition = 119; +const ui32_t MDDindex_DMSegment_EventComment = 120; +const ui32_t MDDindex_DMSegment_TrackIDs = 121; +const ui32_t MDDindex_DMSegment_DMFramework = 122; +const ui32_t MDDindex_DMSourceClip = 123; +const ui32_t MDDindex_DMSourceClip_DMSourceClipTrackIDs = 124; +const ui32_t MDDindex_MaterialPackage = 125; +const ui32_t MDDindex_SourcePackage = 126; +const ui32_t MDDindex_SourcePackage_Descriptor = 127; +const ui32_t MDDindex_GenericDescriptor_Locators = 128; +const ui32_t MDDindex_GenericDescriptor_SubDescriptors = 129; +const ui32_t MDDindex_FileDescriptor = 130; +const ui32_t MDDindex_FileDescriptor_LinkedTrackID = 131; +const ui32_t MDDindex_FileDescriptor_SampleRate = 132; +const ui32_t MDDindex_FileDescriptor_ContainerDuration = 133; +const ui32_t MDDindex_FileDescriptor_EssenceContainer = 134; +const ui32_t MDDindex_FileDescriptor_Codec = 135; +const ui32_t MDDindex_GenericPictureEssenceDescriptor = 136; +const ui32_t MDDindex_GenericPictureEssenceDescriptor_SignalStandard = 137; +const ui32_t MDDindex_GenericPictureEssenceDescriptor_FrameLayout = 138; +const ui32_t MDDindex_GenericPictureEssenceDescriptor_StoredWidth = 139; +const ui32_t MDDindex_GenericPictureEssenceDescriptor_StoredHeight = 140; +const ui32_t MDDindex_GenericPictureEssenceDescriptor_StoredF2Offset = 141; +const ui32_t MDDindex_GenericPictureEssenceDescriptor_SampledWidth = 142; +const ui32_t MDDindex_GenericPictureEssenceDescriptor_SampledHeight = 143; +const ui32_t MDDindex_GenericPictureEssenceDescriptor_SampledXOffset = 144; +const ui32_t MDDindex_GenericPictureEssenceDescriptor_SampledYOffset = 145; +const ui32_t MDDindex_GenericPictureEssenceDescriptor_DisplayHeight = 146; +const ui32_t MDDindex_GenericPictureEssenceDescriptor_DisplayWidth = 147; +const ui32_t MDDindex_GenericPictureEssenceDescriptor_DisplayXOffset = 148; +const ui32_t MDDindex_GenericPictureEssenceDescriptor_DisplayYOffset = 149; +const ui32_t MDDindex_GenericPictureEssenceDescriptor_DisplayF2Offset = 150; +const ui32_t MDDindex_GenericPictureEssenceDescriptor_AspectRatio = 151; +const ui32_t MDDindex_GenericPictureEssenceDescriptor_ActiveFormatDescriptor = 152; +const ui32_t MDDindex_GenericPictureEssenceDescriptor_VideoLineMap = 153; +const ui32_t MDDindex_GenericPictureEssenceDescriptor_AlphaTransparency = 154; +const ui32_t MDDindex_GenericPictureEssenceDescriptor_Gamma = 155; +const ui32_t MDDindex_GenericPictureEssenceDescriptor_ImageAlignmentOffset = 156; +const ui32_t MDDindex_GenericPictureEssenceDescriptor_ImageStartOffset = 157; +const ui32_t MDDindex_GenericPictureEssenceDescriptor_ImageEndOffset = 158; +const ui32_t MDDindex_GenericPictureEssenceDescriptor_FieldDominance = 159; +const ui32_t MDDindex_GenericPictureEssenceDescriptor_PictureEssenceCoding = 160; +const ui32_t MDDindex_CDCIEssenceDescriptor = 161; +const ui32_t MDDindex_CDCIEssenceDescriptor_ComponentDepth = 162; +const ui32_t MDDindex_CDCIEssenceDescriptor_HorizontalSubsampling = 163; +const ui32_t MDDindex_CDCIEssenceDescriptor_VerticalSubsampling = 164; +const ui32_t MDDindex_CDCIEssenceDescriptor_ColorSiting = 165; +const ui32_t MDDindex_CDCIEssenceDescriptor_ReversedByteOrder = 166; +const ui32_t MDDindex_CDCIEssenceDescriptor_PaddingBits = 167; +const ui32_t MDDindex_CDCIEssenceDescriptor_AlphaSampleDepth = 168; +const ui32_t MDDindex_CDCIEssenceDescriptor_BlackRefLevel = 169; +const ui32_t MDDindex_CDCIEssenceDescriptor_WhiteReflevel = 170; +const ui32_t MDDindex_CDCIEssenceDescriptor_ColorRange = 171; +const ui32_t MDDindex_RGBAEssenceDescriptor = 172; +const ui32_t MDDindex_RGBAEssenceDescriptor_ComponentMaxRef = 173; +const ui32_t MDDindex_RGBAEssenceDescriptor_ComponentMinRef = 174; +const ui32_t MDDindex_RGBAEssenceDescriptor_AlphaMaxRef = 175; +const ui32_t MDDindex_RGBAEssenceDescriptor_AlphaMinRef = 176; +const ui32_t MDDindex_RGBAEssenceDescriptor_ScanningDirection = 177; +const ui32_t MDDindex_RGBAEssenceDescriptor_PixelLayout = 178; +const ui32_t MDDindex_RGBAEssenceDescriptor_Palette = 179; +const ui32_t MDDindex_RGBAEssenceDescriptor_PaletteLayout = 180; +const ui32_t MDDindex_GenericSoundEssenceDescriptor = 181; +const ui32_t MDDindex_GenericSoundEssenceDescriptor_AudioSamplingRate = 182; +const ui32_t MDDindex_GenericSoundEssenceDescriptor_Locked = 183; +const ui32_t MDDindex_GenericSoundEssenceDescriptor_AudioRefLevel = 184; +const ui32_t MDDindex_GenericSoundEssenceDescriptor_ElectroSpatialFormulation = 185; +const ui32_t MDDindex_GenericSoundEssenceDescriptor_ChannelCount = 186; +const ui32_t MDDindex_GenericSoundEssenceDescriptor_QuantizationBits = 187; +const ui32_t MDDindex_GenericSoundEssenceDescriptor_DialNorm = 188; +const ui32_t MDDindex_GenericSoundEssenceDescriptor_SoundEssenceCompression = 189; +const ui32_t MDDindex_GenericDataEssenceDescriptor = 190; +const ui32_t MDDindex_GenericDataEssenceDescriptor_DataEssenceCoding = 191; +const ui32_t MDDindex_MultipleDescriptor = 192; +const ui32_t MDDindex_MultipleDescriptor_SubDescriptorUIDs = 193; +const ui32_t MDDindex_MPEG2VideoDescriptor = 194; +const ui32_t MDDindex_MPEG2VideoDescriptor_SingleSequence = 195; +const ui32_t MDDindex_MPEG2VideoDescriptor_ConstantBFrames = 196; +const ui32_t MDDindex_MPEG2VideoDescriptor_CodedContentType = 197; +const ui32_t MDDindex_MPEG2VideoDescriptor_LowDelay = 198; +const ui32_t MDDindex_MPEG2VideoDescriptor_ClosedGOP = 199; +const ui32_t MDDindex_MPEG2VideoDescriptor_IdenticalGOP = 200; +const ui32_t MDDindex_MPEG2VideoDescriptor_MaxGOP = 201; +const ui32_t MDDindex_MPEG2VideoDescriptor_BPictureCount = 202; +const ui32_t MDDindex_MPEG2VideoDescriptor_BitRate = 203; +const ui32_t MDDindex_MPEG2VideoDescriptor_ProfileAndLevel = 204; +const ui32_t MDDindex_WaveAudioDescriptor = 205; +const ui32_t MDDindex_WaveAudioDescriptor_BlockAlign = 206; +const ui32_t MDDindex_WaveAudioDescriptor_SequenceOffset = 207; +const ui32_t MDDindex_WaveAudioDescriptor_AvgBps = 208; +const ui32_t MDDindex_WaveAudioDescriptor_PeakEnvelope = 209; +const ui32_t MDDindex_JPEG2000PictureSubDescriptor = 210; +const ui32_t MDDindex_JPEG2000PictureSubDescriptor_Rsize = 211; +const ui32_t MDDindex_JPEG2000PictureSubDescriptor_Xsize = 212; +const ui32_t MDDindex_JPEG2000PictureSubDescriptor_Ysize = 213; +const ui32_t MDDindex_JPEG2000PictureSubDescriptor_XOsize = 214; +const ui32_t MDDindex_JPEG2000PictureSubDescriptor_YOsize = 215; +const ui32_t MDDindex_JPEG2000PictureSubDescriptor_XTsize = 216; +const ui32_t MDDindex_JPEG2000PictureSubDescriptor_YTsize = 217; +const ui32_t MDDindex_JPEG2000PictureSubDescriptor_XTOsize = 218; +const ui32_t MDDindex_JPEG2000PictureSubDescriptor_YTOsize = 219; +const ui32_t MDDindex_JPEG2000PictureSubDescriptor_Csize = 220; +const ui32_t MDDindex_JPEG2000PictureSubDescriptor_PictureComponentSizing = 221; +const ui32_t MDDindex_JPEG2000PictureSubDescriptor_CodingStyleDefault = 222; +const ui32_t MDDindex_JPEG2000PictureSubDescriptor_QuantizationDefault = 223; +const ui32_t MDDindex_DM_Framework = 224; +const ui32_t MDDindex_DM_Set = 225; +const ui32_t MDDindex_EncryptedContainerLabel = 226; +const ui32_t MDDindex_CryptographicFrameworkLabel = 227; +const ui32_t MDDindex_CryptographicFramework = 228; +const ui32_t MDDindex_CryptographicFramework_ContextSR = 229; +const ui32_t MDDindex_CryptographicContext = 230; +const ui32_t MDDindex_CryptographicContext_ContextID = 231; +const ui32_t MDDindex_CryptographicContext_SourceEssenceContainer = 232; +const ui32_t MDDindex_CryptographicContext_CipherAlgorithm = 233; +const ui32_t MDDindex_CryptographicContext_MICAlgorithm = 234; +const ui32_t MDDindex_CryptographicContext_CryptographicKeyID = 235; +const ui32_t MDDindex_EncryptedTriplet = 236; +const ui32_t MDDindex_EncryptedTriplet_ContextIDLink = 237; +const ui32_t MDDindex_EncryptedTriplet_PlaintextOffset = 238; +const ui32_t MDDindex_EncryptedTriplet_SourceKey = 239; +const ui32_t MDDindex_EncryptedTriplet_SourceLength = 240; +const ui32_t MDDindex_EncryptedTriplet_EncryptedSourceValue = 241; +const ui32_t MDDindex_EncryptedTriplet_TrackFileID = 242; +const ui32_t MDDindex_EncryptedTriplet_SequenceNumber = 243; +const ui32_t MDDindex_EncryptedTriplet_MIC = 244; +const ui32_t MDDindex_CipherAlgorithmAES128CBC = 245; +const ui32_t MDDindex_HMACAlgorithmSHA1128 = 246; + +#endif // _MDD_H_ + +// +// end MDD.h +// diff --git a/src/MPEG.cpp b/src/MPEG.cpp new file mode 100755 index 0000000..0f60a8f --- /dev/null +++ b/src/MPEG.cpp @@ -0,0 +1,267 @@ +/* +Copyright (c) 2005, 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 MPEG.cpp + \version $Id$ + \brief MPEG2 VES parser +*/ + +#include <MPEG.h> + +// walk a buffer stopping at the end of the buffer or the end of a VES +// start code '00 00 01'. If successful, returns address of first byte +// of start code +ASDCP::Result_t +ASDCP::MPEG2::FindVESStartCode(const byte_t* buf, ui32_t buf_len, StartCode_t* sc, const byte_t** new_pos) +{ + ASDCP_TEST_NULL(buf); + ASDCP_TEST_NULL(new_pos); + + ui32_t zero_i = 0; + const byte_t* p = buf; + const byte_t* end_p = buf + buf_len; + + for ( ; p < end_p; p++ ) + { + if ( *p == 0 ) + zero_i++; + + else if ( *p == 1 && zero_i > 1 ) + { + // 2 or more 0 bytes followed by a 1, start code is next + if ( ++p == end_p ) + return RESULT_FAIL; + + *new_pos = p - 3; + *sc = (StartCode_t)*p; + return RESULT_OK; + } + else + zero_i = 0; + } + + *new_pos = buf + buf_len; + return RESULT_FAIL; +} + + +//------------------------------------------------------------------------------------------ + +// +ASDCP::Rational +ASDCP::MPEG2::Accessor::Sequence::AspectRatio() +{ + switch ( m_p[3] & 0xf0 ) + { + case 0x10: return Rational(1,1); + case 0x20: return Rational(4,3); + case 0x30: return Rational(16,9); + case 0x40: return Rational(221,100); + } + + DefaultLogSink().Error("Unknown AspectRatio value: %02x\n", m_p[3]); + return Rational(0,0); +} + +//------------------------------------------------------------------------------------------ + +enum State_t { + ST_IDLE, + ST_START_HEADER, + ST_IN_HEADER, +}; + + +// +class ASDCP::MPEG2::VESParser::h__StreamState +{ +public: + State_t m_State; + h__StreamState() : m_State(ST_IDLE) {} + ~h__StreamState() {} + + void Goto_START_HEADER() { m_State = ST_START_HEADER; } + void Goto_IN_HEADER() { m_State = ST_IN_HEADER; } + void Goto_IDLE() { m_State = ST_IDLE; } + bool Test_IDLE() { return m_State == ST_IDLE; } + bool Test_START_HEADER() { return m_State == ST_START_HEADER; } + bool Test_IN_HEADER() { return m_State == ST_IN_HEADER; } +}; + +//------------------------------------------------------------------------------------------ + + +ASDCP::MPEG2::VESParser::VESParser() : + m_Delegate(0), m_HBufLen(0), m_ZeroCount(0), m_Partial(false) +{ + m_State = new h__StreamState; +} + +ASDCP::MPEG2::VESParser::~VESParser() +{ +} + + +// +void +ASDCP::MPEG2::VESParser::SetDelegate(VESParserDelegate* Delegate) +{ + m_Delegate = Delegate; +} + +// +void +ASDCP::MPEG2::VESParser::Reset() +{ + m_State->Goto_IDLE(); + m_HBufLen = 0; + m_ZeroCount = 0; + m_Partial = false; +} + +// +ASDCP::Result_t +ASDCP::MPEG2::VESParser::Parse(const byte_t* buf, ui32_t buf_len) +{ + ASDCP_TEST_NULL(buf); + ASDCP_TEST_NULL(m_Delegate); + + Result_t result; + const byte_t* end_p = buf + buf_len; + const byte_t* run_pos = buf; // track runs of uninteresting data as a position and count + ui32_t run_len = 0; + + // search for MPEG2 headers + // copy interesting data to a buffer and pass to delegate for processing + for ( const byte_t* p = buf; p < end_p; p++ ) + { + if ( m_State->Test_IN_HEADER() ) + { + m_HBuf[m_HBufLen++] = *p; + assert(m_HBufLen < VESHeaderBufSize); + } + else + { + run_len++; + } + + if ( m_State->Test_START_HEADER() ) // *p is a start code + { + // Do we already have a header? We need to flush it... + if ( m_HBufLen > 0) + { + m_HBufLen -= 3; // remove the current partial start code + + // let the delegate handle the header + switch( m_HBuf[3] ) + { + case PIC_START: result = m_Delegate->Picture(this, m_HBuf, m_HBufLen); break; + case EXT_START: result = m_Delegate->Extension(this, m_HBuf, m_HBufLen); break; + case SEQ_START: result = m_Delegate->Sequence(this, m_HBuf, m_HBufLen); break; + case GOP_START: result = m_Delegate->GOP(this, m_HBuf, m_HBufLen); break; + + default: + DefaultLogSink().Error("Unexpected start code: %02x at byte %lu\n", + m_HBuf[3], (ui32_t)(p - buf)); + result = RESULT_RAW_FORMAT; + } + + // the next run starts with the start code that got us here + run_len = 4; + run_pos = p-3; + m_HBufLen = 0; + + // Parser handlers return RESULT_FALSE to teriminate without error + if ( result != RESULT_OK ) + { + m_State->Goto_IDLE(); + return result; + } + } + + // all headers start with this same start code: 00 00 01 xx + m_HBuf[0] = m_HBuf[1] = 0; m_HBuf[2] = 1; m_HBuf[3] = *p; + + // is this a header we want? + if ( *p == PIC_START || *p == SEQ_START || *p == EXT_START || *p == GOP_START ) + { + // we're starting a new header, flush the current run + if ( run_len > 4 ) + { + m_Delegate->Data(this, run_pos, run_len - 4); + run_len = 0; + } + + m_Partial = false; + m_HBufLen = 4; + m_State->Goto_IN_HEADER(); + } + else + { + if ( *p == FIRST_SLICE ) + result = m_Delegate->Slice(this); + + if ( m_Partial ) + { + m_Partial = false; + m_Delegate->Data(this, m_HBuf, 3); + } + + m_State->Goto_IDLE(); + } + } + else if ( *p == 0 ) + { + m_ZeroCount++; + } + else + { + if ( *p == 1 && m_ZeroCount > 1 ) + m_State->Goto_START_HEADER(); + + m_ZeroCount = 0; + } + } + + if ( run_len > 0 ) + { + if ( m_State->Test_START_HEADER() ) + { + m_Partial = true; // 'partial' means we have a partial header in progress + run_len -= 3; + } + + // flush the current run + m_Delegate->Data(this, run_pos, run_len); + } + + return RESULT_OK; +} + + +// +// end MPEG.cpp +// diff --git a/src/MPEG.h b/src/MPEG.h new file mode 100755 index 0000000..c4de827 --- /dev/null +++ b/src/MPEG.h @@ -0,0 +1,237 @@ +/* +Copyright (c) 2005, 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 MPEG.h + \version $Id$ + \brief MPEG2 VES parser interface +*/ + +#ifndef _ASDCP_MPEG_H_ +#define _ASDCP_MPEG_H_ + +#include <AS_DCP_system.h> +#include <stdio.h> +#include <assert.h> + + +namespace ASDCP +{ + namespace MPEG2 + { + // + enum StartCode_t { + PIC_START = 0x00, + SEQ_START = 0xb3, + EXT_START = 0xb5, + GOP_START = 0xb8, + FIRST_SLICE = 0x01, + INVALID = 0xff + }; + + // + enum RateCode_t { + RATE_23_976 = 0x01, + RATE_24 = 0x02, + RATE_25 = 0x03, + RATE_29_97 = 0x04, + RATE_30 = 0x05 + }; + + // + enum ExtCode_t { + EXT_SEQ = 0x01 + }; + + //------------------------------------------------------------------------------------------ + // VES Parser + + // find the location in the buffer of the next VES packet, returns RESULT_FAIL + // if no start code is found + Result_t FindVESStartCode(const byte_t* buf, ui32_t buf_len, StartCode_t* sc, const byte_t** new_pos); + + // return the extension code of an extension header + inline ExtCode_t ParseExtensionCode(const byte_t* buf) + { + assert(buf); + return (ExtCode_t)(buf[4] >> 4); + } + + // + class VESParserDelegate; // the delegate is declared later + const ui32_t VESHeaderBufSize = 1024; // should be larger than any expected header + + // MPEG VES parser class - call Parse() as many times as you want with buffers + // of any size. State is maintained between calls. When complete headers are + // available for examination, the respective delegate method will be called. + // All other data is given to the delegate's Data() method. + class VESParser + { + class h__StreamState; + mem_ptr<h__StreamState> m_State; + VESParserDelegate* m_Delegate; + + ui32_t m_HBufLen; // temp space for partial header contents + byte_t m_HBuf[VESHeaderBufSize]; + ui32_t m_ZeroCount; + bool m_Partial; + + ASDCP_NO_COPY_CONSTRUCT(VESParser); + + public: + VESParser(); + ~VESParser(); + + void SetDelegate(VESParserDelegate*); // you must call this before Parse() + Result_t Parse(const byte_t*, ui32_t); // call repeatedly + void Reset(); // resets the internal state machine and counters + }; + + // Parser Event Delegate Interface + // + // Create a concrete subclass and give it to the parser by calling SetDelegate(). + // The respective method will be called when a header of the named type is found. + // Handler methods should return RESULT_OK to continue processing or RESULT_FALSE + // to stop without error. + // + class VESParserDelegate + { + public: + virtual ~VESParserDelegate() {} + + // header handlers + virtual Result_t Picture(VESParser*, const byte_t*, ui32_t) = 0; + virtual Result_t Extension(VESParser*, const byte_t*, ui32_t) = 0; + virtual Result_t Sequence(VESParser*, const byte_t*, ui32_t) = 0; + virtual Result_t GOP(VESParser*, const byte_t*, ui32_t) = 0; + + // this is not a header handler, it is a signal that actual picture data + // has started. All Slice data is reported via the Data() method. + virtual Result_t Slice(VESParser*) = 0; + + // Any data not given to the header handlers above is reported here + virtual Result_t Data(VESParser*, const byte_t*, ui32_t) = 0; + }; + + + //------------------------------------------------------------------------------------------ + // VES header accessor objects + // + // For use within parser delegate methods. The constructor expects a pointer to a buffer + // containing two zero bytes, a one byte, a start code and some number of header bytes. + // They are not documented further as they should be self-explanatory. + namespace Accessor + { + // decoding tables + static i16_t FrameRateLUT[] = { 0, 24, 24, 25, 30, 30, 50, 60, 60}; + static bool PulldownLUT[] = { false, true, false, false, true, false, false, true, false}; + + // + class Sequence + { + const byte_t* m_p; + ASDCP_NO_COPY_CONSTRUCT(Sequence); + + public: + Sequence(const byte_t* p) { assert(p); m_p = p + 4; } + inline ui16_t HorizontalSize() { return (ui16_t)( ( m_p[0] << 4 ) | ( m_p[1] >> 4 ) ); } + inline ui16_t VerticalSize() { return (ui16_t)( ( ( m_p[1] & 0x0f ) << 8 ) | m_p[2] ); } + inline RateCode_t RateCode() { return (RateCode_t)( m_p[3] & 0x0f ); } + inline ui16_t FrameRate() { return FrameRateLUT[RateCode()]; } + inline bool Pulldown() { return PulldownLUT[RateCode()] != 0; } + inline i32_t BitRate() { + return ( ( (i32_t)m_p[4] << 10 ) + ( (i32_t)m_p[5] << 2 ) + ( m_p[6] >> 6 ) ) * 400; + } + + Rational AspectRatio(); + }; + + // + class SequenceEx + { + const byte_t* m_p; + ASDCP_NO_COPY_CONSTRUCT(SequenceEx); + + public: + SequenceEx(const byte_t* p) + { + assert(p); + assert(ParseExtensionCode(p) == EXT_SEQ); + m_p = p + 4; + } + + inline ui16_t ProfileAndLevel() { return ( m_p[0] << 4) | ( m_p[1] >> 4 ); } + inline ui8_t ChromaFormat() { return ( m_p[1] >> 1 ) & 0x03; } + inline bool Progressive() { return ( ( m_p[1] >> 3 ) & 0x01 ) > 0; } + inline ui32_t HorizontalSizeExt() { + return ( ( m_p[1] & 0x01 ) << 13 ) | ( ( m_p[2] & 0x80 ) << 5 ); + } + inline ui32_t VerticalSizeExt() { return ( m_p[2] & 0x60 ) << 7; } + inline ui32_t BitRateExt() { + return ( ( m_p[2] & 0x1f ) << 25 ) | ( ( m_p[3] & 0xfe ) << 17 ); + } + + inline bool LowDelay() { return ( m_p[5] & 0x80 ) > 0; } + }; + + // + class GOP + { + const byte_t* m_p; + ASDCP_NO_COPY_CONSTRUCT(GOP); + + public: + GOP(const byte_t* p) { assert(p); m_p = p + 4; } + inline bool Closed() { return ( ( m_p[3] ) >> 6 ) & 0x01; } + }; + + // + class Picture + { + const byte_t* m_p; + ASDCP_NO_COPY_CONSTRUCT(Picture); + + public: + Picture(const byte_t* p) { assert(p); m_p = p + 4; } + inline i16_t TemporalRef() { + return ( (i16_t)( m_p[0] << 2 ) ) | ( ( (i16_t)m_p[1] & 0x00c0 ) >> 6 ); + } + + inline FrameType_t FrameType() { + return (FrameType_t)( ( m_p[1] & 0x38 ) >> 3 ); + } + }; + + } // namespace Accessor + + } // namespace MPEG2 + +} // namespace ASDCP + +#endif // ASDCP_MPEG_H_ + +// +// end MPEG.h +// diff --git a/src/MPEG2_Parser.cpp b/src/MPEG2_Parser.cpp new file mode 100755 index 0000000..9e72b0f --- /dev/null +++ b/src/MPEG2_Parser.cpp @@ -0,0 +1,581 @@ +/* +Copyright (c) 2004, 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 MPEG2_Parser.cpp + \version $Id$ + \brief AS-DCP library, MPEG2 raw essence reader implementation +*/ + +#include <FileIO.h> +#include <MPEG.h> + +using namespace ASDCP; +using namespace ASDCP::MPEG2; + +// data will be read from a VES file in chunks of this size +const ui32_t VESReadSize = 4096; + + +//------------------------------------------------------------------------------------------ + +// +enum ParserState_t { + ST_INIT, + ST_SEQ, + ST_PIC, + ST_GOP, + ST_EXT, + ST_SLICE, +}; + + +// +class h__ParserState +{ + ParserState_t m_State; + ASDCP_NO_COPY_CONSTRUCT(h__ParserState); + + public: + h__ParserState() : m_State(::ST_INIT) {} + ~h__ParserState() {} + + bool Test_SLICE() { return m_State == ST_SLICE; } + void Reset() { m_State = ST_INIT; } + + // + inline Result_t Goto_SEQ() + { + switch ( m_State ) + { + case ST_INIT: + m_State = ST_SEQ; + return RESULT_OK; + } + + return RESULT_STATE; + } + + + // + inline Result_t Goto_SLICE() + { + switch ( m_State ) + { + case ST_PIC: + case ST_EXT: + m_State = ST_SLICE; + return RESULT_OK; + } + + return RESULT_STATE; + } + + + // + inline Result_t Goto_PIC() + { + switch ( m_State ) + { + case ST_INIT: + case ST_SEQ: + case ST_GOP: + case ST_EXT: + m_State = ST_PIC; + return RESULT_OK; + } + + return RESULT_STATE; + } + + + // + inline Result_t Goto_GOP() + { + switch ( m_State ) + { + case ST_EXT: + case ST_SEQ: + m_State = ST_GOP; + return RESULT_OK; + } + + return RESULT_STATE; + } + + // + inline Result_t Goto_EXT() + { + switch ( m_State ) + { + case ST_PIC: + case ST_EXT: + case ST_SEQ: + case ST_GOP: + m_State = ST_EXT; + return RESULT_OK; + } + + return RESULT_STATE; + } +}; + +//------------------------------------------------------------------------------------------ + +// This is a parser delagate that reads the stream params from a +// sequence header and sequence extension header. The parser is +// commanded to return after processing the sequence extension +// header. +class StreamParams : public VESParserDelegate +{ + h__ParserState m_State; + + ASDCP_NO_COPY_CONSTRUCT(StreamParams); + +public: + VideoDescriptor m_VDesc; + + StreamParams() + { + m_VDesc.ContainerDuration = 0; + m_VDesc.ComponentDepth = 8; + } + + ~StreamParams() {} + + // + Result_t Sequence(VESParser*, const byte_t* b, ui32_t s) + { + Result_t result = m_State.Goto_SEQ(); + + if ( ASDCP_FAILURE(result) ) + return result; + + Accessor::Sequence SEQ(b); + m_VDesc.AspectRatio = SEQ.AspectRatio(); + m_VDesc.FrameRate = SEQ.FrameRate(); + m_VDesc.StoredWidth = SEQ.HorizontalSize(); + m_VDesc.StoredHeight = SEQ.VerticalSize(); + m_VDesc.BitRate = SEQ.BitRate(); + m_VDesc.EditRate = SEQ.Pulldown() ? Rational(SEQ.FrameRate() * 1000, 1001) : Rational(SEQ.FrameRate(), 1); + m_VDesc.SampleRate = m_VDesc.EditRate; + return RESULT_OK; + } + + // + Result_t Extension(VESParser*, const byte_t* b, ui32_t s) + { + Result_t result = m_State.Goto_EXT(); + + if ( ASDCP_FAILURE(result) ) + return result; + + Accessor::SequenceEx SEQX(b); + m_VDesc.ProfileAndLevel = SEQX.ProfileAndLevel(); + m_VDesc.FrameLayout = SEQX.Progressive() ? 0 : 1; + m_VDesc.CodedContentType = SEQX.Progressive() ? 1 : 2; + m_VDesc.LowDelay = SEQX.LowDelay(); + m_VDesc.HorizontalSubsampling = SEQX.ChromaFormat() == 3 ? 1 : 2; + m_VDesc.VerticalSubsampling = SEQX.ChromaFormat() >= 3 ? 1 : 2; + + if ( ( m_VDesc.HorizontalSubsampling == 2 ) && ( m_VDesc.VerticalSubsampling == 2 ) ) + m_VDesc.ColorSiting = 3; // 4:2:0 + + else if ( ( m_VDesc.HorizontalSubsampling == 2 ) && ( m_VDesc.VerticalSubsampling == 1 ) ) + m_VDesc.ColorSiting = 4; // 4:2:2 + + else if ( ( m_VDesc.HorizontalSubsampling == 1 ) && ( m_VDesc.VerticalSubsampling == 1 ) ) + m_VDesc.ColorSiting = 0; // 4:4:4 + + // TODO: get H&V size and bit rate extensions + + return RESULT_FALSE; + } + + Result_t GOP(VESParser*, const byte_t*, ui32_t) { return RESULT_FALSE; } + Result_t Picture(VESParser*, const byte_t*, ui32_t) { return RESULT_FALSE; } + Result_t Slice(VESParser*) { return RESULT_FALSE; } + Result_t Data(VESParser*, const byte_t*, ui32_t) { return RESULT_OK; } +}; + + +//------------------------------------------------------------------------------------------ + +// This is a parser delagate that reads a VES stream and sets public +// instance variables to indicate state. It is used below to read an +// entire frame into a buffer. The delegate retains metadata about the +// frame for later access. +// +class FrameParser : public VESParserDelegate +{ + h__ParserState m_State; + ASDCP_NO_COPY_CONSTRUCT(FrameParser); + +public: + ui32_t m_FrameSize; + bool m_CompletePicture; + bool m_HasGOP; + bool m_ClosedGOP; + ui8_t m_TemporalRef; + ui32_t m_PlaintextOffset; + FrameType_t m_FrameType; + + FrameParser() + { + Reset(); + } + + ~FrameParser() {} + + void Reset() + { + m_FrameSize = 0; + m_HasGOP = m_ClosedGOP = false; + m_CompletePicture = false; + m_TemporalRef = 0; + m_PlaintextOffset = 0; + m_FrameType = FRAME_U; + m_State.Reset(); + } + + Result_t Sequence(VESParser*, const byte_t* b, ui32_t s) + { + if ( m_State.Test_SLICE() ) + { + m_CompletePicture = true; + return RESULT_FALSE; + } + + m_FrameSize += s; + return m_State.Goto_SEQ(); + } + + Result_t Picture(VESParser*, const byte_t* b, ui32_t s) + { + if ( m_State.Test_SLICE() ) + { + m_CompletePicture = true; + return RESULT_FALSE; + } + + Accessor::Picture PIC(b); + m_TemporalRef = PIC.TemporalRef(); + m_FrameType = PIC.FrameType(); + m_FrameSize += s; + return m_State.Goto_PIC(); + } + + Result_t Slice(VESParser*) + { + m_PlaintextOffset = m_FrameSize; + return m_State.Goto_SLICE(); + } + + Result_t Extension(VESParser*, const byte_t* b, ui32_t s) + { + m_FrameSize += s; + return m_State.Goto_EXT(); + } + + Result_t GOP(VESParser*, const byte_t* b, ui32_t s) + { + Accessor::GOP GOP(b); + m_ClosedGOP = GOP.Closed(); + m_HasGOP = true; + m_FrameSize += s; + return m_State.Goto_GOP(); + } + + Result_t Data(VESParser*, const byte_t* b, ui32_t s) + { + m_FrameSize += s; + return RESULT_OK; + } +}; + +//------------------------------------------------------------------------------------------ + +// The following code assumes the following things: +// - each frame will begin with a picture header or a sequence header +// - any frame that begins with a sequence header is an I frame and is +// assumed to contain a GOP header, a picture header and complete picture data +// - any frame that begins with a picture header is either an I, B or P frame +// and is assumed to contain a complete picture header and picture data + + +class ASDCP::MPEG2::Parser::h__Parser +{ + StreamParams m_ParamsDelegate; + FrameParser m_ParserDelegate; + VESParser m_Parser; + FileReader m_FileReader; + ui32_t m_FrameNumber; + bool m_EOF; + ASDCP::MPEG2::FrameBuffer m_TmpBuffer; + + ASDCP_NO_COPY_CONSTRUCT(h__Parser); + +public: + h__Parser() : m_TmpBuffer(VESReadSize*2) {} + ~h__Parser() { Close(); } + + Result_t OpenRead(const char* filename); + void Close(); + Result_t Reset(); + Result_t ReadFrame(FrameBuffer&); + Result_t FillVideoDescriptor(VideoDescriptor&); +}; + + +// +Result_t +ASDCP::MPEG2::Parser::h__Parser::Reset() +{ + m_FrameNumber = 0; + m_EOF = false; + m_FileReader.Seek(0); + m_ParserDelegate.Reset(); + return RESULT_OK; +} + +// +void +ASDCP::MPEG2::Parser::h__Parser::Close() +{ + m_FileReader.Close(); +} + +// +ASDCP::Result_t +ASDCP::MPEG2::Parser::h__Parser::OpenRead(const char* filename) +{ + ASDCP_TEST_NULL_STR(filename) + ui32_t read_count = 0; + + Result_t result = m_FileReader.OpenRead(filename); + + if ( ASDCP_SUCCESS(result) ) + result = m_FileReader.Read(m_TmpBuffer.Data(), m_TmpBuffer.Capacity(), &read_count); + + if ( ASDCP_SUCCESS(result) ) + { + const byte_t* p = m_TmpBuffer.RoData(); + + // the mxflib parser demanded the file start with a sequence header. + // Since no one complained and that's the easiest thing to implement, + // I have left it that way. Let me know if you want to be able to + // locate the first GOP in the stream. + if ( p[0] != 0 || p[1] != 0 || p[2] != 1 ) + { + DefaultLogSink().Error("Frame buffer does not begin with a start code.\n"); + return RESULT_RAW_FORMAT; + } + + if ( ASDCP_SUCCESS(result) ) + { + m_Parser.SetDelegate(&m_ParamsDelegate); + result = m_Parser.Parse(p, read_count); + } + } + + if ( ASDCP_SUCCESS(result) ) + { + m_Parser.SetDelegate(&m_ParserDelegate); + m_FileReader.Seek(0); + } + + if ( ASDCP_FAILURE(result) ) + { + DefaultLogSink().Error("Unable to identify a wrapping mode for the essence in file \"%s\"\n", filename); + m_FileReader.Close(); + } + + return result; +} + +// +// +ASDCP::Result_t +ASDCP::MPEG2::Parser::h__Parser::ReadFrame(FrameBuffer& FB) +{ + Result_t result = RESULT_OK; + ui32_t write_offset = 0; + ui32_t read_count = 0; + + FB.Size(0); + + if ( m_EOF ) + return RESULT_ENDOFFILE; + + // Data is read in VESReadSize chunks. Each chunk is parsed, and the + // process is stopped when a Sequence or Picture header is found or when + // the input file is exhausted. The partial next frame is cached for the + // next call. + m_ParserDelegate.Reset(); + + if ( m_TmpBuffer.Size() > 0 ) + { + memcpy(FB.Data(), m_TmpBuffer.RoData(), m_TmpBuffer.Size()); + result = m_Parser.Parse(FB.RoData(), m_TmpBuffer.Size()); + write_offset = m_TmpBuffer.Size(); + m_TmpBuffer.Size(0); + } + + while ( ! m_ParserDelegate.m_CompletePicture && ASDCP_SUCCESS(result) ) + { + if ( FB.Capacity() < ( write_offset + VESReadSize ) ) + { + DefaultLogSink().Error("FrameBuf.Capacity: %lu FrameLength: %lu\n", + FB.Capacity(), ( write_offset + VESReadSize )); + return RESULT_SMALLBUF; + } + + result = m_FileReader.Read(FB.Data() + write_offset, VESReadSize, &read_count); + + if ( result == RESULT_ENDOFFILE ) + { + m_EOF = true; + + if ( write_offset > 0 ) + result = RESULT_OK; + } + + if ( ASDCP_SUCCESS(result) ) + { + result = m_Parser.Parse(FB.RoData() + write_offset, read_count); + write_offset += read_count; + } + + if ( m_EOF ) + break; + } + + if ( ASDCP_SUCCESS(result) + && m_ParserDelegate.m_FrameSize < write_offset ) + { + assert(m_TmpBuffer.Size() == 0); + ui32_t diff = write_offset - m_ParserDelegate.m_FrameSize; + assert(diff <= m_TmpBuffer.Capacity()); + memcpy(m_TmpBuffer.Data(), FB.RoData() + m_ParserDelegate.m_FrameSize, diff); + m_TmpBuffer.Size(diff); + } + +#ifndef NDEBUG + if ( ASDCP_SUCCESS(result) ) + { + const byte_t* p = FB.RoData(); + if ( p[0] != 0 || p[1] != 0 || p[2] != 1 ) + { + DefaultLogSink().Error("Parsed frame buffer does not begin with a start code.\n"); + return RESULT_RAW_FORMAT; + } + } +#endif + + if ( ASDCP_SUCCESS(result) ) + { + FB.Size(m_ParserDelegate.m_FrameSize); + FB.TemporalOffset(m_ParserDelegate.m_TemporalRef); + FB.FrameType(m_ParserDelegate.m_FrameType); + FB.PlaintextOffset(m_ParserDelegate.m_PlaintextOffset); + FB.FrameNumber(m_FrameNumber++); + + if ( m_ParserDelegate.m_HasGOP ) + { + FB.GOPStart(m_ParserDelegate.m_HasGOP); + FB.ClosedGOP(m_ParserDelegate.m_ClosedGOP); + } + } + + return result; +} + + +// Fill a VideoDescriptor struct with the values from the file's header. +ASDCP::Result_t +ASDCP::MPEG2::Parser::h__Parser::FillVideoDescriptor(VideoDescriptor& VDesc) +{ + VDesc = m_ParamsDelegate.m_VDesc; + return RESULT_OK; +} + +//------------------------------------------------------------------------------------------ + +ASDCP::MPEG2::Parser::Parser() +{ +} + +ASDCP::MPEG2::Parser::~Parser() +{ +} + +// Opens the stream for reading, parses enough data to provide a complete +// set of stream metadata for the MXFWriter below. +ASDCP::Result_t +ASDCP::MPEG2::Parser::OpenRead(const char* filename) const +{ + const_cast<ASDCP::MPEG2::Parser*>(this)->m_Parser = new h__Parser; + + Result_t result = m_Parser->OpenRead(filename); + + if ( ASDCP_FAILURE(result) ) + const_cast<ASDCP::MPEG2::Parser*>(this)->m_Parser.release(); + + return result; +} + +// Rewinds the stream to the beginning. +ASDCP::Result_t +ASDCP::MPEG2::Parser::Reset() const +{ + if ( m_Parser.empty() ) + return RESULT_INIT; + + return m_Parser->Reset(); +} + +// Places a frame of data in the frame buffer. Fails if the buffer is too small +// or the stream is empty. +ASDCP::Result_t +ASDCP::MPEG2::Parser::ReadFrame(FrameBuffer& FB) const +{ + if ( m_Parser.empty() ) + return RESULT_INIT; + + return m_Parser->ReadFrame(FB); +} + +ASDCP::Result_t +ASDCP::MPEG2::Parser::FillVideoDescriptor(VideoDescriptor& VDesc) const +{ + if ( m_Parser.empty() ) + return RESULT_INIT; + + return m_Parser->FillVideoDescriptor(VDesc); +} + +// +// end AS_DCP_MPEG2_Parser.cpp +// diff --git a/src/MXF.cpp b/src/MXF.cpp new file mode 100755 index 0000000..bacf069 --- /dev/null +++ b/src/MXF.cpp @@ -0,0 +1,1020 @@ +// +// +// + + +#define ASDCP_DECLARE_MDD +#include "MDD.h" + +#include "MXF.h" +#include "Metadata.h" +#include <hex_utils.h> + +//------------------------------------------------------------------------------------------ +// + +const ui32_t kl_length = ASDCP::SMPTE_UL_LENGTH + ASDCP::MXF_BER_LENGTH; + +const byte_t mdd_key[] = { 0x06, 0x0e, 0x2b, 0x34 }; + +// +const ASDCP::MDDEntry* +ASDCP::GetMDDEntry(const byte_t* ul_buf) +{ + ui32_t t_idx = 0; + ui32_t k_idx = 8; + + // must be a pointer to a SMPTE UL + if ( ul_buf == 0 || memcmp(mdd_key, ul_buf, 4) != 0 ) + return 0; + + // advance to first matching element + // TODO: optimize using binary search + while ( s_MDD_Table[t_idx].ul != 0 + && s_MDD_Table[t_idx].ul[k_idx] != ul_buf[k_idx] ) + t_idx++; + + if ( s_MDD_Table[t_idx].ul == 0 ) + return 0; + + // match successive elements + while ( s_MDD_Table[t_idx].ul != 0 + && k_idx < SMPTE_UL_LENGTH - 1 + && s_MDD_Table[t_idx].ul[k_idx] == ul_buf[k_idx] ) + { + if ( s_MDD_Table[t_idx].ul[k_idx+1] == ul_buf[k_idx+1] ) + { + k_idx++; + } + else + { + while ( s_MDD_Table[t_idx].ul != 0 + && s_MDD_Table[t_idx].ul[k_idx] == ul_buf[k_idx] + && s_MDD_Table[t_idx].ul[k_idx+1] != ul_buf[k_idx+1] ) + t_idx++; + + while ( s_MDD_Table[t_idx].ul[k_idx] != ul_buf[k_idx] ) + k_idx--; + } + } + + return (s_MDD_Table[t_idx].ul == 0 ? 0 : &s_MDD_Table[t_idx]); +} + +//------------------------------------------------------------------------------------------ +// + +// +ASDCP::Result_t +ASDCP::MXF::SeekToRIP(const ASDCP::FileReader& Reader) +{ + ASDCP::fpos_t end_pos; + + // go to the end - 4 bytes + Result_t result = Reader.Seek(0, ASDCP::SP_END); + + if ( ASDCP_SUCCESS(result) ) + result = Reader.Tell(&end_pos); + + if ( ASDCP_SUCCESS(result) + && end_pos < (SMPTE_UL_LENGTH+MXF_BER_LENGTH) ) + result = RESULT_FAIL; // File is smaller than an empty packet! + + if ( ASDCP_SUCCESS(result) ) + result = Reader.Seek(end_pos - 4); + + // get the ui32_t RIP length + ui32_t read_count; + byte_t intbuf[MXF_BER_LENGTH]; + ui32_t rip_size = 0; + + if ( ASDCP_SUCCESS(result) ) + { + result = Reader.Read(intbuf, MXF_BER_LENGTH, &read_count); + + if ( ASDCP_SUCCESS(result) && read_count != 4 ) + result = RESULT_FAIL; + } + + if ( ASDCP_SUCCESS(result) ) + { + rip_size = ASDCP_i32_BE(cp2i<ui32_t>(intbuf)); + + if ( rip_size > end_pos ) // RIP can't be bigger than the file + return RESULT_FAIL; + } + + // reposition to start of RIP + if ( ASDCP_SUCCESS(result) ) + result = Reader.Seek(end_pos - rip_size); + + return result; +} + +// +ASDCP::Result_t +ASDCP::MXF::RIP::InitFromFile(const ASDCP::FileReader& Reader) +{ + Result_t result = KLVFilePacket::InitFromFile(Reader, s_MDD_Table[MDDindex_RandomIndexMetadata].ul); + + if ( ASDCP_SUCCESS(result) ) + { + MemIOReader MemRDR(m_ValueStart, m_ValueLength - 4); + result = PairArray.ReadFrom(MemRDR); + } + + if ( ASDCP_FAILURE(result) ) + DefaultLogSink().Error("Failed to initialize RIP\n"); + + return result; +} + +// +ASDCP::Result_t +ASDCP::MXF::RIP::WriteToFile(ASDCP::FileWriter& Writer) +{ + Result_t result = WriteKLToFile(Writer, s_MDD_Table[MDDindex_RandomIndexMetadata].ul, 0); + return result; +} + +// +void +ASDCP::MXF::RIP::Dump(FILE* stream) +{ + if ( stream == 0 ) + stream = stderr; + + KLVFilePacket::Dump(stream, false); + PairArray.Dump(stream, false); + + fputs("==========================================================================\n", stream); +} + +//------------------------------------------------------------------------------------------ +// + +// +ASDCP::Result_t +ASDCP::MXF::Partition::InitFromFile(const ASDCP::FileReader& Reader) +{ + Result_t result = KLVFilePacket::InitFromFile(Reader); + // test the UL + // could be one of several values + + if ( ASDCP_SUCCESS(result) ) + { + MemIOReader MemRDR(m_ValueStart, m_ValueLength); + result = MemRDR.ReadUi16BE(&MajorVersion); + if ( ASDCP_SUCCESS(result) ) result = MemRDR.ReadUi16BE(&MinorVersion); + if ( ASDCP_SUCCESS(result) ) result = MemRDR.ReadUi32BE(&KAGSize); + if ( ASDCP_SUCCESS(result) ) result = MemRDR.ReadUi64BE(&ThisPartition); + if ( ASDCP_SUCCESS(result) ) result = MemRDR.ReadUi64BE(&PreviousPartition); + if ( ASDCP_SUCCESS(result) ) result = MemRDR.ReadUi64BE(&FooterPartition); + if ( ASDCP_SUCCESS(result) ) result = MemRDR.ReadUi64BE(&HeaderByteCount); + if ( ASDCP_SUCCESS(result) ) result = MemRDR.ReadUi64BE(&IndexByteCount); + if ( ASDCP_SUCCESS(result) ) result = MemRDR.ReadUi32BE(&IndexSID); + if ( ASDCP_SUCCESS(result) ) result = MemRDR.ReadUi64BE(&BodyOffset); + if ( ASDCP_SUCCESS(result) ) result = MemRDR.ReadUi32BE(&BodySID); + if ( ASDCP_SUCCESS(result) ) result = OperationalPattern.ReadFrom(MemRDR); + if ( ASDCP_SUCCESS(result) ) result = EssenceContainers.ReadFrom(MemRDR); + } + + if ( ASDCP_FAILURE(result) ) + DefaultLogSink().Error("Failed to initialize Partition\n"); + + return result; +} + +// +ASDCP::Result_t +ASDCP::MXF::Partition::WriteToFile(ASDCP::FileWriter& Writer) +{ + Result_t result = m_Buffer.Capacity(1024); + + if ( ASDCP_SUCCESS(result) ) + { + MemIOWriter MemWRT(m_Buffer.Data(), m_Buffer.Capacity()); + result = MemWRT.WriteUi16BE(MajorVersion); + if ( ASDCP_SUCCESS(result) ) result = MemWRT.WriteUi16BE(MinorVersion); + if ( ASDCP_SUCCESS(result) ) result = MemWRT.WriteUi32BE(KAGSize); + if ( ASDCP_SUCCESS(result) ) result = MemWRT.WriteUi64BE(ThisPartition); + if ( ASDCP_SUCCESS(result) ) result = MemWRT.WriteUi64BE(PreviousPartition); + if ( ASDCP_SUCCESS(result) ) result = MemWRT.WriteUi64BE(FooterPartition); + if ( ASDCP_SUCCESS(result) ) result = MemWRT.WriteUi64BE(HeaderByteCount); + if ( ASDCP_SUCCESS(result) ) result = MemWRT.WriteUi64BE(IndexByteCount); + if ( ASDCP_SUCCESS(result) ) result = MemWRT.WriteUi32BE(IndexSID); + if ( ASDCP_SUCCESS(result) ) result = MemWRT.WriteUi64BE(BodyOffset); + if ( ASDCP_SUCCESS(result) ) result = MemWRT.WriteUi32BE(BodySID); + if ( ASDCP_SUCCESS(result) ) result = OperationalPattern.WriteTo(MemWRT); + if ( ASDCP_SUCCESS(result) ) result = EssenceContainers.WriteTo(MemWRT); + if ( ASDCP_SUCCESS(result) ) m_Buffer.Size(MemWRT.Size()); + } + + if ( ASDCP_SUCCESS(result) ) + { + ui32_t write_count; // this is subclassed, so the UL is only right some of the time + result = WriteKLToFile(Writer, s_MDD_Table[MDDindex_ClosedCompleteHeader].ul, m_Buffer.Size()); + + if ( ASDCP_SUCCESS(result) ) + result = Writer.Write(m_Buffer.RoData(), m_Buffer.Size(), &write_count); + } + + return result; +} + +// +void +ASDCP::MXF::Partition::Dump(FILE* stream) +{ + char identbuf[IdentBufferLen]; + char intbuf[IntBufferLen]; + + if ( stream == 0 ) + stream = stderr; + + KLVFilePacket::Dump(stream, false); + fprintf(stream, " MajorVersion = %hu\n", MajorVersion); + fprintf(stream, " MinorVersion = %hu\n", MinorVersion); + fprintf(stream, " KAGSize = %lu\n", KAGSize); + fprintf(stream, " ThisPartition = %s\n", ui64sz(ThisPartition, intbuf)); + fprintf(stream, " PreviousPartition = %s\n", ui64sz(PreviousPartition, intbuf)); + fprintf(stream, " FooterPartition = %s\n", ui64sz(FooterPartition, intbuf)); + fprintf(stream, " HeaderByteCount = %s\n", ui64sz(HeaderByteCount, intbuf)); + fprintf(stream, " IndexByteCount = %s\n", ui64sz(IndexByteCount, intbuf)); + fprintf(stream, " IndexSID = %lu\n", IndexSID); + fprintf(stream, " BodyOffset = %s\n", ui64sz(BodyOffset, intbuf)); + fprintf(stream, " BodySID = %lu\n", BodySID); + fprintf(stream, " OperationalPattern = %s\n", OperationalPattern.ToString(identbuf)); + fputs("Essence Containers:\n", stream); EssenceContainers.Dump(stream, false); + + fputs("==========================================================================\n", stream); +} + + +//------------------------------------------------------------------------------------------ +// + +class ASDCP::MXF::Primer::h__PrimerLookup : public std::map<UL, TagValue> +{ +public: + void InitWithBatch(ASDCP::MXF::Batch<ASDCP::MXF::Primer::LocalTagEntry>& Batch) + { + ASDCP::MXF::Batch<ASDCP::MXF::Primer::LocalTagEntry>::iterator i = Batch.begin(); + + for ( ; i != Batch.end(); i++ ) + insert(std::map<UL, TagValue>::value_type((*i).UL, (*i).Tag)); + } +}; + + +// +ASDCP::MXF::Primer::Primer() {} + +// +ASDCP::MXF::Primer::~Primer() {} + +// +void +ASDCP::MXF::Primer::ClearTagList() +{ + LocalTagEntryBatch.clear(); + m_Lookup = new h__PrimerLookup; +} + +// +ASDCP::Result_t +ASDCP::MXF::Primer::InitFromBuffer(const byte_t* p, ui32_t l) +{ + Result_t result = KLVPacket::InitFromBuffer(p, l, s_MDD_Table[MDDindex_Primer].ul); + + if ( ASDCP_SUCCESS(result) ) + { + MemIOReader MemRDR(m_ValueStart, m_ValueLength); + result = LocalTagEntryBatch.ReadFrom(MemRDR); + } + + if ( ASDCP_SUCCESS(result) ) + { + m_Lookup = new h__PrimerLookup; + m_Lookup->InitWithBatch(LocalTagEntryBatch); + } + + if ( ASDCP_FAILURE(result) ) + DefaultLogSink().Error("Failed to initialize Primer\n"); + + return result; +} + +// +ASDCP::Result_t +ASDCP::MXF::Primer::WriteToBuffer(ASDCP::FrameBuffer& Buffer) +{ + MemIOWriter MemWRT(Buffer.Data(), Buffer.Capacity()); + Result_t result = LocalTagEntryBatch.WriteTo(MemWRT); + Buffer.Size(MemWRT.Size()); +#if 0 + if ( ASDCP_SUCCESS(result) ) + { + ui32_t write_count; + result = WriteKLToFile(Writer, s_MDD_Table[MDDindex_Primer].ul, Buffer.Size()); + + if ( ASDCP_SUCCESS(result) ) + result = Writer.Write(Buffer.RoData(), Buffer.Size(), &write_count); + } +#endif + + return result; +} + +// +ASDCP::Result_t +ASDCP::MXF::Primer::InsertTag(const ASDCP::UL& Key, ASDCP::TagValue& Tag) +{ + assert(m_Lookup); + + std::map<UL, TagValue>::iterator i = m_Lookup->find(Key); + + if ( i == m_Lookup->end() ) + { + const MDDEntry* mdde = GetMDDEntry(Key.Data()); + assert(mdde); + + LocalTagEntry TmpEntry; + TmpEntry.UL = Key; + TmpEntry.Tag = mdde->tag; + + LocalTagEntryBatch.push_back(TmpEntry); + m_Lookup->insert(std::map<UL, TagValue>::value_type(TmpEntry.UL, TmpEntry.Tag)); + } + + return RESULT_OK; +} + +// +ASDCP::Result_t +ASDCP::MXF::Primer::TagForKey(const ASDCP::UL& Key, ASDCP::TagValue& Tag) +{ + assert(m_Lookup); + if ( m_Lookup.empty() ) + { + DefaultLogSink().Error("Primer lookup is empty\n"); + return RESULT_FAIL; + } + + std::map<UL, TagValue>::iterator i = m_Lookup->find(Key); + + if ( i == m_Lookup->end() ) + return RESULT_FALSE; + + Tag = (*i).second; + return RESULT_OK; +} + +// +void +ASDCP::MXF::Primer::Dump(FILE* stream) +{ + char identbuf[IdentBufferLen]; + + if ( stream == 0 ) + stream = stderr; + + KLVPacket::Dump(stream, false); + fprintf(stream, "Primer: %lu %s\n", + LocalTagEntryBatch.ItemCount, + ( LocalTagEntryBatch.ItemCount == 1 ? "entry" : "entries" )); + + Batch<LocalTagEntry>::iterator i = LocalTagEntryBatch.begin(); + for ( ; i != LocalTagEntryBatch.end(); i++ ) + { + const MDDEntry* Entry = GetMDDEntry((*i).UL.Data()); + fprintf(stream, " %s %s\n", (*i).ToString(identbuf), (Entry ? Entry->name : "Unknown")); + } + + fputs("==========================================================================\n", stream); +} + + +//------------------------------------------------------------------------------------------ +// + +// +ASDCP::Result_t +ASDCP::MXF::Preface::InitFromBuffer(const byte_t* p, ui32_t l) +{ + ASDCP_TEST_NULL(p); + + Result_t result = KLVPacket::InitFromBuffer(p, l, s_MDD_Table[MDDindex_Preface].ul); + + if ( ASDCP_SUCCESS(result) ) + { + TLVReader MemRDR(m_ValueStart, m_ValueLength, m_Lookup); + + result = MemRDR.ReadObject(OBJ_READ_ARGS(InterchangeObject, InstanceUID)); + if ( ASDCP_SUCCESS(result) ) result = MemRDR.ReadObject(OBJ_READ_ARGS(GenerationInterchangeObject, GenerationUID)); + if ( ASDCP_SUCCESS(result) ) result = MemRDR.ReadObject(OBJ_READ_ARGS(Preface, LastModifiedDate)); + if ( ASDCP_SUCCESS(result) ) result = MemRDR.ReadUi16(OBJ_READ_ARGS(Preface, Version)); + if ( ASDCP_SUCCESS(result) ) result = MemRDR.ReadUi32(OBJ_READ_ARGS(Preface, ObjectModelVersion)); + if ( ASDCP_SUCCESS(result) ) result = MemRDR.ReadObject(OBJ_READ_ARGS(Preface, PrimaryPackage)); + if ( ASDCP_SUCCESS(result) ) result = MemRDR.ReadObject(OBJ_READ_ARGS(Preface, Identifications)); + if ( ASDCP_SUCCESS(result) ) result = MemRDR.ReadObject(OBJ_READ_ARGS(Preface, ContentStorage)); + if ( ASDCP_SUCCESS(result) ) result = MemRDR.ReadObject(OBJ_READ_ARGS(Preface, OperationalPattern)); + if ( ASDCP_SUCCESS(result) ) result = MemRDR.ReadObject(OBJ_READ_ARGS(Preface, EssenceContainers)); + if ( ASDCP_SUCCESS(result) ) result = MemRDR.ReadObject(OBJ_READ_ARGS(Preface, DMSchemes)); + } + + if ( ASDCP_FAILURE(result) ) + DefaultLogSink().Error("Failed to initialize Preface\n"); + + return result; +} + +// +ASDCP::Result_t +ASDCP::MXF::Preface::WriteToBuffer(ASDCP::FrameBuffer& Buffer) +{ + TLVWriter MemWRT(Buffer.Data() + kl_length, Buffer.Capacity() - kl_length, m_Lookup); + Result_t result = MemWRT.WriteObject(OBJ_WRITE_ARGS(InterchangeObject, InstanceUID)); + if ( ASDCP_SUCCESS(result) ) result = MemWRT.WriteObject(OBJ_WRITE_ARGS(GenerationInterchangeObject, GenerationUID)); + if ( ASDCP_SUCCESS(result) ) result = MemWRT.WriteObject(OBJ_WRITE_ARGS(Preface, LastModifiedDate)); + if ( ASDCP_SUCCESS(result) ) result = MemWRT.WriteUi16(OBJ_WRITE_ARGS(Preface, Version)); + if ( ASDCP_SUCCESS(result) ) result = MemWRT.WriteUi32(OBJ_WRITE_ARGS(Preface, ObjectModelVersion)); + if ( ASDCP_SUCCESS(result) ) result = MemWRT.WriteObject(OBJ_WRITE_ARGS(Preface, PrimaryPackage)); + if ( ASDCP_SUCCESS(result) ) result = MemWRT.WriteObject(OBJ_WRITE_ARGS(Preface, Identifications)); + if ( ASDCP_SUCCESS(result) ) result = MemWRT.WriteObject(OBJ_WRITE_ARGS(Preface, ContentStorage)); + if ( ASDCP_SUCCESS(result) ) result = MemWRT.WriteObject(OBJ_WRITE_ARGS(Preface, OperationalPattern)); + if ( ASDCP_SUCCESS(result) ) result = MemWRT.WriteObject(OBJ_WRITE_ARGS(Preface, EssenceContainers)); + if ( ASDCP_SUCCESS(result) ) result = MemWRT.WriteObject(OBJ_WRITE_ARGS(Preface, DMSchemes)); + + if ( ASDCP_SUCCESS(result) ) + { + ui32_t packet_length = MemWRT.Size(); + result = WriteKLToBuffer(Buffer, s_MDD_Table[MDDindex_Preface].ul, packet_length); + + if ( ASDCP_SUCCESS(result) ) + Buffer.Size(Buffer.Size() + packet_length); + } + + return result; +} + +// +void +ASDCP::MXF::Preface::Dump(FILE* stream) +{ + char identbuf[IdentBufferLen]; + + if ( stream == 0 ) + stream = stderr; + + KLVPacket::Dump(stream, false); + fprintf(stream, " InstanceUID = %s\n", InstanceUID.ToString(identbuf)); + fprintf(stream, " GenerationUID = %s\n", GenerationUID.ToString(identbuf)); + fprintf(stream, " LastModifiedDate = %s\n", LastModifiedDate.ToString(identbuf)); + fprintf(stream, " Version = %hu\n", Version); + fprintf(stream, " ObjectModelVersion = %lu\n", ObjectModelVersion); + fprintf(stream, " PrimaryPackage = %s\n", PrimaryPackage.ToString(identbuf)); + fprintf(stream, " Identifications:\n"); Identifications.Dump(stream); + fprintf(stream, " ContentStorage = %s\n", ContentStorage.ToString(identbuf)); + fprintf(stream, " OperationalPattern = %s\n", OperationalPattern.ToString(identbuf)); + fprintf(stream, " EssenceContainers:\n"); EssenceContainers.Dump(stream); + fprintf(stream, " DMSchemes:\n"); DMSchemes.Dump(stream); + + fputs("==========================================================================\n", stream); +} + +//------------------------------------------------------------------------------------------ +// + +// +class ASDCP::MXF::h__PacketList +{ +public: + std::list<InterchangeObject*> m_List; + std::map<UL, InterchangeObject*> m_Map; + + ~h__PacketList() { + while ( ! m_List.empty() ) + { + delete m_List.back(); + m_List.pop_back(); + } + } + + // + void AddPacket(InterchangeObject* ThePacket) + { + assert(ThePacket); + m_Map.insert(std::map<UID, InterchangeObject*>::value_type(ThePacket->InstanceUID, ThePacket)); + m_List.push_back(ThePacket); + } + + // + Result_t GetMDObjectByType(const byte_t* ObjectID, InterchangeObject** Object) + { + ASDCP_TEST_NULL(ObjectID); + ASDCP_TEST_NULL(Object); + std::list<InterchangeObject*>::iterator li; + *Object = 0; + + for ( li = m_List.begin(); li != m_List.end(); li++ ) + { + if ( (*li)->HasUL(ObjectID) ) + { + *Object = *li; + return RESULT_OK; + } + } + + return RESULT_FAIL; + } +}; + +//------------------------------------------------------------------------------------------ +// + +ASDCP::MXF::OPAtomHeader::OPAtomHeader() : m_Preface(0), m_HasRIP(false) +{ + m_PacketList = new h__PacketList; +} + + +ASDCP::MXF::OPAtomHeader::~OPAtomHeader() +{ +} + + +ASDCP::Result_t +ASDCP::MXF::OPAtomHeader::InitFromFile(const ASDCP::FileReader& Reader) +{ + m_HasRIP = false; + Result_t result = SeekToRIP(Reader); + + if ( ASDCP_SUCCESS(result) ) + { + result = m_RIP.InitFromFile(Reader); + + if ( ASDCP_FAILURE(result) ) + { + DefaultLogSink().Error("File contains no RIP\n"); + result = RESULT_OK; + } + else + { + m_HasRIP = true; + } + } + + if ( ASDCP_SUCCESS(result) ) + result = Reader.Seek(0); + + if ( ASDCP_SUCCESS(result) ) + result = Partition::InitFromFile(Reader); // test UL and OP + + // slurp up the remainder of the header + ui32_t read_count; + + if ( ASDCP_SUCCESS(result) ) + { + ui64_t here = Reader.Tell(); + ui32_t buf_len = HeaderByteCount; + result = m_Buffer.Capacity(buf_len); + } + + if ( ASDCP_SUCCESS(result) ) + result = Reader.Read(m_Buffer.Data(), m_Buffer.Capacity(), &read_count); + + if ( ASDCP_SUCCESS(result) && read_count != m_Buffer.Capacity() ) + { + DefaultLogSink().Error("Short read of OP-Atom header metadata; wanted %lu, got %lu\n", + m_Buffer.Capacity(), read_count); + return RESULT_FAIL; + } + + const byte_t* p = m_Buffer.RoData(); + const byte_t* end_p = p + m_Buffer.Capacity(); + + while ( ASDCP_SUCCESS(result) && p < end_p ) + { + // parse the packets and index them by uid, discard KLVFill items + InterchangeObject* object = CreateObject(p); + assert(object); + + object->m_Lookup = &m_Primer; + result = object->InitFromBuffer(p, end_p - p); + const byte_t* redo_p = p; + p += object->PacketLength(); + // hexdump(p, object->PacketLength()); + + if ( ASDCP_SUCCESS(result) ) + { + if ( object->IsA(s_MDD_Table[MDDindex_KLVFill].ul) ) + { + delete object; + } + else if ( object->IsA(s_MDD_Table[MDDindex_Primer].ul) ) + { + delete object; + result = m_Primer.InitFromBuffer(redo_p, end_p - redo_p); + } + else if ( object->IsA(s_MDD_Table[MDDindex_Preface].ul) ) + { + m_Preface = object; + } + else + { + m_PacketList->AddPacket(object); + } + } + else + { + DefaultLogSink().Error("Error initializing packet\n"); + delete object; + } + } + + return result; +} + +// +ASDCP::Result_t +ASDCP::MXF::OPAtomHeader::GetMDObjectByType(const byte_t* ObjectID, InterchangeObject** Object) +{ + InterchangeObject* TmpObject; + + if ( Object == 0 ) + Object = &TmpObject; + + return m_PacketList->GetMDObjectByType(ObjectID, Object); +} + +ASDCP::MXF::Identification* +ASDCP::MXF::OPAtomHeader::GetIdentification() +{ + InterchangeObject* Object; + + if ( ASDCP_SUCCESS(GetMDObjectByType(OBJ_TYPE_ARGS(Identification), &Object)) ) + return (Identification*)Object; + + return 0; +} + +// +ASDCP::Result_t +ASDCP::MXF::OPAtomHeader::WriteToFile(ASDCP::FileWriter& Writer, ui32_t HeaderSize) +{ + if ( HeaderSize < 4096 ) + { + DefaultLogSink().Error("HeaderSize %lu is too small. Must be >= 4096\n"); + return RESULT_FAIL; + } + + ASDCP::FrameBuffer HeaderBuffer; + + Result_t result = HeaderBuffer.Capacity(HeaderSize); + HeaderByteCount = HeaderSize; + + if ( ASDCP_SUCCESS(result) ) + { + assert(m_Preface); + m_Preface->m_Lookup = &m_Primer; + result = m_Preface->WriteToBuffer(HeaderBuffer); + } +#if 0 + std::list<InterchangeObject*>::iterator pl_i = m_PacketList->m_List.begin(); + for ( ; pl_i != m_PacketList->m_List.end() && ASDCP_SUCCESS(result); pl_i++ ) + { + InterchangeObject* object = *pl_i; + object->m_Lookup = &m_Primer; + result = object->WriteToBuffer(HeaderBuffer); + } +#endif + if ( ASDCP_SUCCESS(result) ) + result = Partition::WriteToFile(Writer); + + // if ( ASDCP_SUCCESS(result) ) + // result = m_Primer.WriteToFile(Writer); + + if ( ASDCP_SUCCESS(result) ) + { + ui32_t write_count; + Writer.Write(HeaderBuffer.RoData(), HeaderBuffer.Size(), &write_count); + assert(write_count == HeaderBuffer.Size()); + } + + // KLV Fill + if ( ASDCP_SUCCESS(result) ) + { + ASDCP::fpos_t pos = Writer.Tell(); + + if ( pos > HeaderSize ) + { + char intbuf[IntBufferLen]; + DefaultLogSink().Error("Header size %s exceeds specified value %lu\n", + ui64sz(pos, intbuf), + HeaderSize); + return RESULT_FAIL; + } + + ASDCP::FrameBuffer NilBuf; + ui32_t klv_fill_length = HeaderSize - (ui32_t)pos; + + if ( klv_fill_length < kl_length ) + { + DefaultLogSink().Error("Remaining region too small for KLV Fill header\n"); + return RESULT_FAIL; + } + + klv_fill_length -= kl_length; + result = WriteKLToFile(Writer, s_MDD_Table[MDDindex_KLVFill].ul, klv_fill_length); + + if ( ASDCP_SUCCESS(result) ) + result = NilBuf.Capacity(klv_fill_length); + + if ( ASDCP_SUCCESS(result) ) + { + memset(NilBuf.Data(), 0, klv_fill_length); + ui32_t write_count; + Writer.Write(NilBuf.RoData(), klv_fill_length, &write_count); + assert(write_count == klv_fill_length); + } + } + + return result; +} + +// +void +ASDCP::MXF::OPAtomHeader::Dump(FILE* stream) +{ + if ( stream == 0 ) + stream = stderr; + + if ( m_HasRIP ) + m_RIP.Dump(stream); + + Partition::Dump(stream); + m_Primer.Dump(stream); + assert(m_Preface); + m_Preface->Dump(stream); + + std::list<InterchangeObject*>::iterator i = m_PacketList->m_List.begin(); + for ( ; i != m_PacketList->m_List.end(); i++ ) + (*i)->Dump(stream); +} + +//------------------------------------------------------------------------------------------ +// + +ASDCP::MXF::OPAtomIndexFooter::OPAtomIndexFooter() : m_Lookup(0) +{ + m_PacketList = new h__PacketList; +} + + +ASDCP::MXF::OPAtomIndexFooter::~OPAtomIndexFooter() +{ +} + + +ASDCP::Result_t +ASDCP::MXF::OPAtomIndexFooter::InitFromFile(const ASDCP::FileReader& Reader) +{ + Result_t result = Partition::InitFromFile(Reader); // test UL and OP + + // slurp up the remainder of the footer + ui32_t read_count; + + if ( ASDCP_SUCCESS(result) ) + result = m_Buffer.Capacity(IndexByteCount); + + if ( ASDCP_SUCCESS(result) ) + result = Reader.Read(m_Buffer.Data(), m_Buffer.Capacity(), &read_count); + + if ( ASDCP_SUCCESS(result) && read_count != m_Buffer.Capacity() ) + { + DefaultLogSink().Error("Short read of footer partition: got %lu, expecting %lu\n", + read_count, m_Buffer.Capacity()); + return RESULT_FAIL; + } + + const byte_t* p = m_Buffer.RoData(); + const byte_t* end_p = p + m_Buffer.Capacity(); + + while ( ASDCP_SUCCESS(result) && p < end_p ) + { + // parse the packets and index them by uid, discard KLVFill items + InterchangeObject* object = CreateObject(p); + assert(object); + + object->m_Lookup = m_Lookup; + result = object->InitFromBuffer(p, end_p - p); + p += object->PacketLength(); + + if ( ASDCP_SUCCESS(result) ) + { + m_PacketList->AddPacket(object); + } + else + { + DefaultLogSink().Error("Error initializing packet\n"); + delete object; + } + } + + if ( ASDCP_FAILURE(result) ) + DefaultLogSink().Error("Failed to initialize OPAtomIndexFooter\n"); + + return result; +} + +// +ASDCP::Result_t +ASDCP::MXF::OPAtomIndexFooter::WriteToFile(ASDCP::FileWriter& Writer) +{ + Result_t result = WriteKLToFile(Writer, s_MDD_Table[MDDindex_CompleteFooter].ul, 0); + return result; +} + +// +void +ASDCP::MXF::OPAtomIndexFooter::Dump(FILE* stream) +{ + if ( stream == 0 ) + stream = stderr; + + Partition::Dump(stream); + + std::list<InterchangeObject*>::iterator i = m_PacketList->m_List.begin(); + for ( ; i != m_PacketList->m_List.end(); i++ ) + (*i)->Dump(stream); +} + +// +ASDCP::Result_t +ASDCP::MXF::OPAtomIndexFooter::Lookup(ui32_t frame_num, IndexTableSegment::IndexEntry& Entry) +{ + std::list<InterchangeObject*>::iterator li; + for ( li = m_PacketList->m_List.begin(); li != m_PacketList->m_List.end(); li++ ) + { + if ( (*li)->IsA(OBJ_TYPE_ARGS(IndexTableSegment)) ) + { + IndexTableSegment* Segment = (IndexTableSegment*)(*li); + ui64_t start_pos = Segment->IndexStartPosition; + + if ( Segment->EditUnitByteCount > 0 ) + { + if ( m_PacketList->m_List.size() > 1 ) + DefaultLogSink().Error("Unexpected multiple IndexTableSegment in CBR file\n"); + + if ( ! Segment->IndexEntryArray.empty() ) + DefaultLogSink().Error("Unexpected IndexEntryArray contents in CBR file\n"); + + Entry.StreamOffset = (ui64_t)frame_num * Segment->EditUnitByteCount; + return RESULT_OK; + } + else if ( (ui64_t)frame_num >= start_pos + && (ui64_t)frame_num < (start_pos + Segment->IndexDuration) ) + { + Entry = Segment->IndexEntryArray[frame_num]; + return RESULT_OK; + } + } + } + + return RESULT_FAIL; +} + + +//------------------------------------------------------------------------------------------ +// + +// +ASDCP::Result_t +ASDCP::MXF::InterchangeObject::WriteToBuffer(ASDCP::FrameBuffer& Buffer) +{ + if ( Buffer.Capacity() < (Buffer.Size() + m_KLLength + m_ValueLength) ) + { + DefaultLogSink().Error("InterchangeObject::WriteToBuffer: Buffer too small\n"); + Dump(); + return RESULT_READFAIL; + } + + Result_t result = WriteKLToBuffer(Buffer, m_KeyStart, m_ValueLength); + + if ( ASDCP_SUCCESS(result) ) + { + memcpy(Buffer.Data() + Buffer.Size(), m_ValueStart, m_ValueLength); + Buffer.Size(Buffer.Size() + m_ValueLength); + } + + return result; +} + +// +bool +ASDCP::MXF::InterchangeObject::IsA(const byte_t* label) +{ + if ( m_KLLength == 0 ) + return false; + + return ( memcmp(label, m_KeyStart, SMPTE_UL_LENGTH) == 0 ); +} + + +//------------------------------------------------------------------------------------------ +// + +// +enum FLT_t + { + FLT_Preface, + FLT_Identification, + FLT_ContentStorage, + FLT_MaterialPackage, + FLT_SourcePackage, + FLT_Track, + FLT_Sequence, + FLT_SourceClip, + FLT_TimecodeComponent, + FLT_FileDescriptor, + FLT_WaveAudioDescriptor, + FLT_GenericPictureEssenceDescriptor, + FLT_MPEG2VideoDescriptor, + FLT_RGBAEssenceDescriptor, + FLT_JPEG2000PictureSubDescriptor, + FLT_IndexTableSegment, + FLT_CryptographicFramework, + FLT_CryptographicContext + }; + +// +typedef std::map<ASDCP::UL, FLT_t> FactoryList; +#define SETUP_IDX(t) const ui32_t FLT_##t = v; + +static FactoryList s_FactoryList; +#define SETUP_FACTORY(t) s_FactoryList.insert(FactoryList::value_type(s_MDD_Table[MDDindex_##t].ul, FLT_##t)); +#define CASE_FACTORY(t) case FLT_##t: return new t + +// +ASDCP::MXF::InterchangeObject* +ASDCP::MXF::CreateObject(const byte_t* label) +{ + if ( label == 0 ) + return 0; + + if ( s_FactoryList.empty() ) + { + SETUP_FACTORY(Preface); + SETUP_FACTORY(Identification); + SETUP_FACTORY(ContentStorage); + SETUP_FACTORY(MaterialPackage); + SETUP_FACTORY(SourcePackage); + SETUP_FACTORY(Track); + SETUP_FACTORY(Sequence); + SETUP_FACTORY(SourceClip); + SETUP_FACTORY(TimecodeComponent); + SETUP_FACTORY(FileDescriptor); + SETUP_FACTORY(WaveAudioDescriptor); + SETUP_FACTORY(GenericPictureEssenceDescriptor); + SETUP_FACTORY(MPEG2VideoDescriptor); + SETUP_FACTORY(RGBAEssenceDescriptor); + SETUP_FACTORY(JPEG2000PictureSubDescriptor); + SETUP_FACTORY(IndexTableSegment); + SETUP_FACTORY(CryptographicFramework); + SETUP_FACTORY(CryptographicContext); + } + + FactoryList::iterator i = s_FactoryList.find(label); + + if ( i == s_FactoryList.end() ) + return new InterchangeObject; + + switch ( i->second ) + { + CASE_FACTORY(Preface); + CASE_FACTORY(Identification); + CASE_FACTORY(ContentStorage); + CASE_FACTORY(MaterialPackage); + CASE_FACTORY(SourcePackage); + CASE_FACTORY(Track); + CASE_FACTORY(Sequence); + CASE_FACTORY(SourceClip); + CASE_FACTORY(TimecodeComponent); + CASE_FACTORY(FileDescriptor); + CASE_FACTORY(WaveAudioDescriptor); + CASE_FACTORY(GenericPictureEssenceDescriptor); + CASE_FACTORY(MPEG2VideoDescriptor); + CASE_FACTORY(RGBAEssenceDescriptor); + CASE_FACTORY(JPEG2000PictureSubDescriptor); + CASE_FACTORY(IndexTableSegment); + CASE_FACTORY(CryptographicFramework); + CASE_FACTORY(CryptographicContext); + } + + return new InterchangeObject; +} + +// +// end MXF.cpp +// diff --git a/src/MXF.h b/src/MXF.h new file mode 100755 index 0000000..fc0d996 --- /dev/null +++ b/src/MXF.h @@ -0,0 +1,307 @@ +// +// +// + +#ifndef _MXF_H_ +#define _MXF_H_ + +#include "MXFTypes.h" + +namespace ASDCP +{ + namespace MXF + { + // seek an open file handle to the start of the RIP KLV packet + Result_t SeekToRIP(const FileReader&); + + // + class RIP : public ASDCP::KLVFilePacket + { + ASDCP_NO_COPY_CONSTRUCT(RIP); + + public: + // + class Pair { + public: + ui32_t BodySID; + ui64_t ByteOffset; + + ui32_t Size() { return sizeof(ui32_t) + sizeof(ui64_t); } + + inline const char* ToString(char* str_buf) const { + char intbuf[IntBufferLen]; + sprintf(str_buf, "%-6lu: %s", BodySID, ui64sz(ByteOffset, intbuf)); + return str_buf; + } + + inline Result_t ReadFrom(ASDCP::MemIOReader& Reader) { + Result_t result = Reader.ReadUi32BE(&BodySID); + + if ( ASDCP_SUCCESS(result) ) + result = Reader.ReadUi64BE(&ByteOffset); + + return result; + } + + inline Result_t WriteTo(ASDCP::MemIOWriter& Writer) { + Result_t result = Writer.WriteUi32BE(BodySID); + + if ( ASDCP_SUCCESS(result) ) + result = Writer.WriteUi64BE(ByteOffset); + + return result; + } + }; + + Array<Pair> PairArray; + + RIP() {} + virtual ~RIP() {} + virtual Result_t InitFromFile(const ASDCP::FileReader& Reader); + virtual Result_t WriteToFile(ASDCP::FileWriter& Writer); + virtual void Dump(FILE* = 0); + }; + + + // + class Partition : public ASDCP::KLVFilePacket + { + ASDCP_NO_COPY_CONSTRUCT(Partition); + + public: + ui16_t MajorVersion; + ui16_t MinorVersion; + ui32_t KAGSize; + ui64_t ThisPartition; + ui64_t PreviousPartition; + ui64_t FooterPartition; + ui64_t HeaderByteCount; + ui64_t IndexByteCount; + ui32_t IndexSID; + ui64_t BodyOffset; + ui32_t BodySID; + UL OperationalPattern; + Batch<UL> EssenceContainers; + + Partition() {} + virtual ~Partition() {} + virtual Result_t InitFromFile(const ASDCP::FileReader& Reader); + virtual Result_t WriteToFile(ASDCP::FileWriter& Writer); + virtual void Dump(FILE* = 0); + }; + + + // + class Primer : public ASDCP::KLVPacket, public ASDCP::IPrimerLookup + { + class h__PrimerLookup; + mem_ptr<h__PrimerLookup> m_Lookup; + ASDCP_NO_COPY_CONSTRUCT(Primer); + + public: + // + class LocalTagEntry + { + public: + TagValue Tag; + ASDCP::UL UL; + + inline const char* ToString(char* str_buf) const { + sprintf(str_buf, "%02x %02x: ", Tag.a, Tag.b); + UL.ToString(str_buf + strlen(str_buf)); + return str_buf; + } + + inline Result_t ReadFrom(ASDCP::MemIOReader& Reader) { + Result_t result = Reader.ReadUi8(&Tag.a); + + if ( ASDCP_SUCCESS(result) ) + result = Reader.ReadUi8(&Tag.b); + + if ( ASDCP_SUCCESS(result) ) + result = UL.ReadFrom(Reader); + + return result; + } + + inline Result_t WriteTo(ASDCP::MemIOWriter& Writer) { + Result_t result = Writer.WriteUi8(Tag.a); + + if ( ASDCP_SUCCESS(result) ) + result = Writer.WriteUi8(Tag.b); + + if ( ASDCP_SUCCESS(result) ) + result = UL.WriteTo(Writer); + + return result; + } + }; + + Batch<LocalTagEntry> LocalTagEntryBatch; + + Primer(); + virtual ~Primer(); + + virtual void ClearTagList(); + virtual Result_t InsertTag(const ASDCP::UL& Key, ASDCP::TagValue& Tag); + virtual Result_t TagForKey(const ASDCP::UL& Key, ASDCP::TagValue& Tag); + + virtual Result_t InitFromBuffer(const byte_t* p, ui32_t l); + virtual Result_t WriteToBuffer(ASDCP::FrameBuffer&); + virtual void Dump(FILE* = 0); + }; + + + // + class InterchangeObject : public ASDCP::KLVPacket + { + public: + IPrimerLookup* m_Lookup; + UID InstanceUID; + + InterchangeObject() : m_Lookup(0) {} + virtual ~InterchangeObject() {} + virtual Result_t WriteToBuffer(ASDCP::FrameBuffer&); + virtual bool IsA(const byte_t* label); + + virtual void Dump(FILE* stream = 0) { + KLVPacket::Dump(stream, true); + } + }; + + // + InterchangeObject* CreateObject(const byte_t* label); + + + // + class Preface : public InterchangeObject + { + ASDCP_NO_COPY_CONSTRUCT(Preface); + + public: + UUID GenerationUID; + Timestamp LastModifiedDate; + ui16_t Version; + ui32_t ObjectModelVersion; + UID PrimaryPackage; + Batch<UID> Identifications; + UID ContentStorage; + UL OperationalPattern; + Batch<UL> EssenceContainers; + Batch<UL> DMSchemes; + + Preface() {} + virtual ~Preface() {} + virtual Result_t InitFromBuffer(const byte_t* p, ui32_t l); + virtual Result_t WriteToBuffer(ASDCP::FrameBuffer& Buffer); + virtual void Dump(FILE* = 0); + }; + + // + class IndexTableSegment : public InterchangeObject + { + ASDCP_NO_COPY_CONSTRUCT(IndexTableSegment); + + public: + // + class DeltaEntry + { + public: + i8_t PosTableIndex; + ui8_t Slice; + ui32_t ElementData; + + Result_t ReadFrom(ASDCP::MemIOReader& Reader); + Result_t WriteTo(ASDCP::MemIOWriter& Writer); + inline const char* ToString(char* str_buf) const; + }; + + // + class IndexEntry + { + public: + i8_t TemporalOffset; + i8_t KeyFrameOffset; + ui8_t Flags; + ui64_t StreamOffset; + std::list<ui32_t> SliceOffset; + Array<Rational> PosTable; + + Result_t ReadFrom(ASDCP::MemIOReader& Reader); + Result_t WriteTo(ASDCP::MemIOWriter& Writer); + inline const char* ToString(char* str_buf) const; + }; + + Rational IndexEditRate; + ui64_t IndexStartPosition; + ui64_t IndexDuration; + ui32_t EditUnitByteCount; + ui32_t IndexSID; + ui32_t BodySID; + ui8_t SliceCount; + ui8_t PosTableCount; + Batch<DeltaEntry> DeltaEntryArray; + Batch<IndexEntry> IndexEntryArray; + + IndexTableSegment(); + virtual ~IndexTableSegment(); + virtual Result_t InitFromBuffer(const byte_t* p, ui32_t l); + virtual Result_t WriteToBuffer(ASDCP::FrameBuffer& Buffer); + virtual void Dump(FILE* = 0); + }; + + //--------------------------------------------------------------------------------- + // + class h__PacketList; // See MXF.cpp + class Identification; + + // + class OPAtomHeader : public Partition + { + mem_ptr<h__PacketList> m_PacketList; + ASDCP_NO_COPY_CONSTRUCT(OPAtomHeader); + + public: + ASDCP::MXF::RIP m_RIP; + ASDCP::MXF::Primer m_Primer; + InterchangeObject* m_Preface; + ASDCP::FrameBuffer m_Buffer; + bool m_HasRIP; + + OPAtomHeader(); + virtual ~OPAtomHeader(); + virtual Result_t InitFromFile(const ASDCP::FileReader& Reader); + virtual Result_t WriteToFile(ASDCP::FileWriter& Writer, ui32_t HeaderLength = 16384); + virtual void Dump(FILE* = 0); + virtual Result_t GetMDObjectByType(const byte_t*, InterchangeObject** = 0); + Identification* GetIdentification(); + }; + + // + class OPAtomIndexFooter : public Partition + { + mem_ptr<h__PacketList> m_PacketList; + ASDCP::FrameBuffer m_Buffer; + ASDCP_NO_COPY_CONSTRUCT(OPAtomIndexFooter); + + public: + IPrimerLookup* m_Lookup; + + OPAtomIndexFooter(); + virtual ~OPAtomIndexFooter(); + virtual Result_t InitFromFile(const ASDCP::FileReader& Reader); + virtual Result_t WriteToFile(ASDCP::FileWriter& Writer); + virtual void Dump(FILE* = 0); + + virtual Result_t Lookup(ui32_t frame_num, IndexTableSegment::IndexEntry&); + }; + + } // namespace MXF +} // namespace ASDCP + + +#endif // _MXF_H_ + +// +// end MXF.h +// diff --git a/src/MXFTypes.cpp b/src/MXFTypes.cpp new file mode 100755 index 0000000..ca65bdf --- /dev/null +++ b/src/MXFTypes.cpp @@ -0,0 +1,284 @@ + +#include "MXFTypes.h" + +//------------------------------------------------------------------------------------------ +// + +// +ASDCP::Result_t +ASDCP::MXF::UTF16String::ReadFrom(ASDCP::MemIOReader& Reader) +{ + const byte_t* p = Reader.Data() + Reader.Offset(); + /// cheating - for all use cases, we know the previous two bytes are the length + m_length = ASDCP_i16_BE(cp2i<ui16_t>(p-2)); + assert(m_length % 2 == 0); + m_length /= 2; + assert(IdentBufferLen >= m_length); + ui32_t i = 0; + + for ( i = 0; i < m_length; i++ ) + m_buffer[i] = p[(i*2)+1]; + + m_buffer[i] = 0; + + Reader.SkipOffset(m_length*2); + return RESULT_OK; +} + +// +ASDCP::Result_t +ASDCP::MXF::UTF16String::WriteTo(ASDCP::MemIOWriter& Writer) +{ + byte_t* p = Writer.Data() + Writer.Size(); + ui32_t i = 0; + m_length = strlen(m_buffer); + memset(p, 0, m_length*2); + + for ( i = 0; i < m_length; i++ ) + p[(i*2)+1] = m_buffer[i]; + + m_buffer[i] = 0; + + Writer.AddOffset(m_length*2); + return RESULT_OK; +} + + +//------------------------------------------------------------------------------------------ +// + +ASDCP::MXF::TLVReader::TLVReader(const byte_t* p, ui32_t c, IPrimerLookup* PrimerLookup) : + MemIOReader(p, c), m_Lookup(PrimerLookup) +{ + Result_t result = RESULT_OK; + + while ( Remainder() > 0 && ASDCP_SUCCESS(result) ) + { + TagValue Tag; + ui16_t pkt_len = 0; + + result = MemIOReader::ReadUi8(&Tag.a); + + if ( ASDCP_SUCCESS(result) ) + result = MemIOReader::ReadUi8(&Tag.b); + + if ( ASDCP_SUCCESS(result) ) + result = MemIOReader::ReadUi16BE(&pkt_len); + + if ( ASDCP_SUCCESS(result) ) + { + m_ElementMap.insert(TagMap::value_type(Tag, ItemInfo(m_size, pkt_len))); + result = SkipOffset(pkt_len); + } + + if ( ASDCP_FAILURE(result) ) + { + DefaultLogSink().Error("Malformed Set\n"); + m_ElementMap.clear(); + break; + } + } +} + +// +bool +ASDCP::MXF::TLVReader::FindTL(const MDDEntry& Entry) +{ + if ( m_Lookup == 0 ) + { + fprintf(stderr, "No Lookup service\n"); + return false; + } + + TagValue TmpTag; + + if ( m_Lookup->TagForKey(Entry.ul, TmpTag) != RESULT_OK ) + { + if ( Entry.tag.a == 0 ) + { + DefaultLogSink().Error("No such UL in this TL list: %s (%02x %02x)\n", + Entry.name, Entry.tag.a, Entry.tag.b); + return false; + } + + TmpTag = Entry.tag; + } + + TagMap::iterator e_i = m_ElementMap.find(TmpTag); + + if ( e_i != m_ElementMap.end() ) + { + m_size = (*e_i).second.first; + m_capacity = m_size + (*e_i).second.second; + return true; + } + + // DefaultLogSink().Warn("Not Found (%02x %02x): %s\n", TmpTag.a, TmpTag.b, Entry.name); + return false; +} + +// +ASDCP::Result_t +ASDCP::MXF::TLVReader::ReadObject(const MDDEntry& Entry, IArchive* Object) +{ + ASDCP_TEST_NULL(Object); + + if ( FindTL(Entry) ) + return Object->ReadFrom(*this); + + return RESULT_FALSE; +} + +// +ASDCP::Result_t +ASDCP::MXF::TLVReader::ReadUi8(const MDDEntry& Entry, ui8_t* value) +{ + ASDCP_TEST_NULL(value); + + if ( FindTL(Entry) ) + return MemIOReader::ReadUi8(value); + + return RESULT_FALSE; +} + +// +ASDCP::Result_t +ASDCP::MXF::TLVReader::ReadUi16(const MDDEntry& Entry, ui16_t* value) +{ + ASDCP_TEST_NULL(value); + + if ( FindTL(Entry) ) + return MemIOReader::ReadUi16BE(value); + + return RESULT_FALSE; +} + +// +ASDCP::Result_t +ASDCP::MXF::TLVReader::ReadUi32(const MDDEntry& Entry, ui32_t* value) +{ + ASDCP_TEST_NULL(value); + + if ( FindTL(Entry) ) + return MemIOReader::ReadUi32BE(value); + + return RESULT_FALSE; +} + +// +ASDCP::Result_t +ASDCP::MXF::TLVReader::ReadUi64(const MDDEntry& Entry, ui64_t* value) +{ + ASDCP_TEST_NULL(value); + + if ( FindTL(Entry) ) + return MemIOReader::ReadUi64BE(value); + + return RESULT_FALSE; +} + +//------------------------------------------------------------------------------------------ +// + +ASDCP::MXF::TLVWriter::TLVWriter(byte_t* p, ui32_t c, IPrimerLookup* PrimerLookup) : + MemIOWriter(p, c), m_Lookup(PrimerLookup) +{ + assert(c > 3); +} + +// +ASDCP::Result_t +ASDCP::MXF::TLVWriter::WriteTag(const MDDEntry& Entry) +{ + TagValue TmpTag; + + if ( m_Lookup == 0 ) + { + DefaultLogSink().Error("No Primer object available\n"); + return RESULT_FAIL; + } + + if ( m_Lookup->InsertTag(Entry.ul, TmpTag) != RESULT_OK ) + { + DefaultLogSink().Error("No tag for entry %s\n", Entry.name); + return RESULT_FAIL; + } + + Result_t result = MemIOWriter::WriteUi8(TmpTag.a); + if ( ASDCP_SUCCESS(result) ) MemIOWriter::WriteUi8(TmpTag.b); + + return result; +} + +// +ASDCP::Result_t +ASDCP::MXF::TLVWriter::WriteObject(const MDDEntry& Entry, IArchive* Object) +{ + ASDCP_TEST_NULL(Object); + Result_t result = WriteTag(Entry); + + // write a temp length + byte_t* l_p = CurrentData(); + + if ( ASDCP_SUCCESS(result) ) + MemIOWriter::WriteUi16BE(0); + + if ( ASDCP_SUCCESS(result) ) + { + ui32_t before = Size(); + result = Object->WriteTo(*this); + + if ( ASDCP_SUCCESS(result) ) + i2p<ui16_t>(ASDCP_i16_BE( Size() - before), l_p); + } + + return result; +} + +// +ASDCP::Result_t +ASDCP::MXF::TLVWriter::WriteUi8(const MDDEntry& Entry, ui8_t* value) +{ + ASDCP_TEST_NULL(value); + Result_t result = WriteTag(Entry); + if ( ASDCP_SUCCESS(result) ) MemIOWriter::WriteUi16BE(sizeof(ui8_t)); + if ( ASDCP_SUCCESS(result) ) MemIOWriter::WriteUi8(*value); + return result; +} + +// +ASDCP::Result_t +ASDCP::MXF::TLVWriter::WriteUi16(const MDDEntry& Entry, ui16_t* value) +{ + ASDCP_TEST_NULL(value); + Result_t result = WriteTag(Entry); + if ( ASDCP_SUCCESS(result) ) MemIOWriter::WriteUi16BE(sizeof(ui16_t)); + if ( ASDCP_SUCCESS(result) ) MemIOWriter::WriteUi8(*value); + return result; +} + +// +ASDCP::Result_t +ASDCP::MXF::TLVWriter::WriteUi32(const MDDEntry& Entry, ui32_t* value) +{ + ASDCP_TEST_NULL(value); + Result_t result = WriteTag(Entry); + if ( ASDCP_SUCCESS(result) ) MemIOWriter::WriteUi16BE(sizeof(ui32_t)); + if ( ASDCP_SUCCESS(result) ) MemIOWriter::WriteUi8(*value); + return result; +} + +// +ASDCP::Result_t +ASDCP::MXF::TLVWriter::WriteUi64(const MDDEntry& Entry, ui64_t* value) +{ + ASDCP_TEST_NULL(value); + Result_t result = WriteTag(Entry); + if ( ASDCP_SUCCESS(result) ) MemIOWriter::WriteUi16BE(sizeof(ui64_t)); + if ( ASDCP_SUCCESS(result) ) MemIOWriter::WriteUi8(*value); + return result; +} + +// +// end +// diff --git a/src/MXFTypes.h b/src/MXFTypes.h new file mode 100755 index 0000000..02efbb1 --- /dev/null +++ b/src/MXFTypes.h @@ -0,0 +1,285 @@ +// +// MXFTypes.h +// + +#ifndef _MXFTYPES_H_ +#define _MXFTYPES_H_ + + + +#endif //_MXFTYPES_H_ + +#include "KLV.h" +#include <list> +#include <vector> +#include <map> +#include <wchar.h> + +// used with TLVReader::Read* +// +// these are used below to manufacture arguments +#define OBJ_READ_ARGS(s,l) s_MDD_Table[MDDindex_##s##_##l], &l +#define OBJ_READ_ARGS_R(s,l,r) s_MDD_Table[MDDindex_##s##_##l], &r + +#define OBJ_WRITE_ARGS(s,l) s_MDD_Table[MDDindex_##s##_##l], &l + +#define OBJ_TYPE_ARGS(t) s_MDD_Table[MDDindex_##t].ul + + +namespace ASDCP +{ + namespace MXF + { + typedef std::pair<ui32_t, ui32_t> ItemInfo; + typedef std::map<TagValue, ItemInfo> TagMap; + + // + class TLVReader : public ASDCP::MemIOReader + { + + TagMap m_ElementMap; + IPrimerLookup* m_Lookup; + + TLVReader(); + ASDCP_NO_COPY_CONSTRUCT(TLVReader); + bool FindTL(const MDDEntry&); + + public: + TLVReader(const byte_t* p, ui32_t c, IPrimerLookup* = 0); + Result_t ReadObject(const MDDEntry&, IArchive*); + Result_t ReadUi8(const MDDEntry&, ui8_t*); + Result_t ReadUi16(const MDDEntry&, ui16_t*); + Result_t ReadUi32(const MDDEntry&, ui32_t*); + Result_t ReadUi64(const MDDEntry&, ui64_t*); + }; + + // + class TLVWriter : public ASDCP::MemIOWriter + { + + TagMap m_ElementMap; + IPrimerLookup* m_Lookup; + + TLVWriter(); + ASDCP_NO_COPY_CONSTRUCT(TLVWriter); + Result_t WriteTag(const MDDEntry&); + + public: + TLVWriter(byte_t* p, ui32_t c, IPrimerLookup* = 0); + Result_t WriteObject(const MDDEntry&, IArchive*); + Result_t WriteUi8(const MDDEntry&, ui8_t*); + Result_t WriteUi16(const MDDEntry&, ui16_t*); + Result_t WriteUi32(const MDDEntry&, ui32_t*); + Result_t WriteUi64(const MDDEntry&, ui64_t*); + }; + + // + template <class T> + class Batch : public std::vector<T>, public IArchive + { + public: + ui32_t ItemCount; + ui32_t ItemSize; + + Batch() : ItemCount(0), ItemSize(0) { ItemSize = sizeof(T); } + ~Batch() {} + + // + Result_t ReadFrom(ASDCP::MemIOReader& Reader) { + Result_t result = Reader.ReadUi32BE(&ItemCount); + + if ( ASDCP_SUCCESS(result) ) + result = Reader.ReadUi32BE(&ItemSize); + + if ( ( ItemCount > 65536 ) || ( ItemSize > 1024 ) ) + return RESULT_FAIL; + + for ( ui32_t i = 0; i < ItemCount && ASDCP_SUCCESS(result); i++ ) + { + T Tmp; + result = Tmp.ReadFrom(Reader); + + if ( ASDCP_SUCCESS(result) ) + push_back(Tmp); + } + + return result; + } + + // + Result_t WriteTo(ASDCP::MemIOWriter& Writer) { + Result_t result = Writer.WriteUi32BE(size()); + + if ( ASDCP_SUCCESS(result) ) + result = Writer.WriteUi32BE(ItemSize); + + typename std::vector<T>::iterator l_i = begin(); + for ( ; l_i != end() && ASDCP_SUCCESS(result); l_i++ ) + result = (*l_i).WriteTo(Writer); + + return result; + } + + // + void Dump(FILE* stream = 0, ui32_t depth = 0) + { + char identbuf[IdentBufferLen]; + + if ( stream == 0 ) + stream = stderr; + + typename std::vector<T>::iterator i = this->begin(); + for ( ; i != this->end(); i++ ) + fprintf(stream, " %s\n", (*i).ToString(identbuf)); + } + }; + + // + template <class T> + class Array : public std::list<T>, public IArchive + { + public: + Array() {} + ~Array() {} + + // + Result_t ReadFrom(ASDCP::MemIOReader& Reader) + { + while ( Reader.Remainder() > 0 ) + { + T Tmp; + Tmp.ReadFrom(Reader); + push_back(Tmp); + } + + return RESULT_OK; + } + + // + Result_t WriteTo(ASDCP::MemIOWriter& Writer) { + Result_t result = RESULT_OK; + typename std::list<T>::iterator l_i = begin(); + + for ( ; l_i != end() && ASDCP_SUCCESS(result); l_i++ ) + result = (*l_i).WriteTo(Writer); + + return result; + } + + // + void Dump(FILE* stream = 0, ui32_t depth = 0) + { + char identbuf[IdentBufferLen]; + + if ( stream == 0 ) + stream = stderr; + + typename std::list<T>::iterator i = this->begin(); + for ( ; i != this->end(); i++ ) + fprintf(stream, " %s\n", (*i).ToString(identbuf)); + } + }; + + // + class Timestamp : public IArchive + { + public: + ui16_t Year; + ui8_t Month; + ui8_t Day; + ui8_t Hour; + ui8_t Minute; + ui8_t Second; + ui8_t mSec_4; + + Timestamp() : + Year(0), Month(0), Day(0), + Hour(0), Minute(0), Second(0), mSec_4(0) {} + + // + inline const char* ToString(char* str_buf) const { + sprintf(str_buf, "%04hu-%02hu-%02hu %02hu:%02hu:%02hu.%03hu", + Year, Month, Day, Hour, Minute, Second, mSec_4); + return str_buf; + } + + // + inline Result_t ReadFrom(ASDCP::MemIOReader& Reader) { + Result_t result = Reader.ReadUi16BE(&Year); + + if ( ASDCP_SUCCESS(result) ) + result = Reader.ReadRaw(&Month, 6); + + return result; + } + + // + inline Result_t WriteTo(ASDCP::MemIOWriter& Writer) { + Result_t result = Writer.WriteUi16BE(Year); + + if ( ASDCP_SUCCESS(result) ) + result = Writer.WriteRaw(&Month, 6); + + return result; + } + }; + + // + class UTF16String : public IArchive + { + ui16_t m_length; + char m_buffer[IdentBufferLen]; + + public: + UTF16String() : m_length(0) { *m_buffer = 0; } + ~UTF16String() {} + + // + const char* ToString(char* str_buf) const { + strncpy(str_buf, m_buffer, m_length+1); + return str_buf; + } + + Result_t ReadFrom(ASDCP::MemIOReader& Reader); + Result_t WriteTo(ASDCP::MemIOWriter& Writer); + }; + + // + class Rational : public ASDCP::Rational, public IArchive + { + public: + Rational() {} + ~Rational() {} + + // + const char* ToString(char* str_buf) const { + sprintf(str_buf, "%lu/%lu", Numerator, Denominator); + return str_buf; + } + + Result_t ReadFrom(ASDCP::MemIOReader& Reader) { + Result_t result = Reader.ReadUi32BE((ui32_t*)&Numerator); + + if ( ASDCP_SUCCESS(result) ) + result = Reader.ReadUi32BE((ui32_t*)&Denominator); + + return result; + } + + Result_t WriteTo(ASDCP::MemIOWriter& Writer) { + Result_t result = Writer.WriteUi32BE((ui32_t)Numerator); + + if ( ASDCP_SUCCESS(result) ) + result = Writer.WriteUi32BE((ui32_t)Denominator); + + return result; + } + }; + + } // namespace MXF +} // namespace ASDCP + + +// +// end MXFTypes.h +// diff --git a/src/Metadata.cpp b/src/Metadata.cpp new file mode 100755 index 0000000..1335803 --- /dev/null +++ b/src/Metadata.cpp @@ -0,0 +1,781 @@ +/* +Copyright (c) 2005-2006, 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 Metadata.cpp + \version $Id$ + \brief AS-DCP library, MXF Metadata Sets implementation +*/ + + +#include "Metadata.h" +#include "MDD.h" +#include <hex_utils.h> + + +//------------------------------------------------------------------------------------------ +// + +// +ASDCP::Result_t +ASDCP::MXF::Identification::InitFromBuffer(const byte_t* p, ui32_t l) +{ + ASDCP_TEST_NULL(p); + + Result_t result = KLVPacket::InitFromBuffer(p, l, s_MDD_Table[MDDindex_Identification].ul); + + if ( ASDCP_SUCCESS(result) ) + { + TLVReader MemRDR(m_ValueStart, m_ValueLength, m_Lookup); + + result = MemRDR.ReadObject(OBJ_READ_ARGS(InterchangeObject, InstanceUID)); + if ( ASDCP_SUCCESS(result) ) result = MemRDR.ReadObject(OBJ_READ_ARGS(Identification, ThisGenerationUID)); + if ( ASDCP_SUCCESS(result) ) result = MemRDR.ReadObject(OBJ_READ_ARGS(Identification, CompanyName)); + if ( ASDCP_SUCCESS(result) ) result = MemRDR.ReadObject(OBJ_READ_ARGS(Identification, ProductName)); + if ( ASDCP_SUCCESS(result) ) result = MemRDR.ReadUi16(OBJ_READ_ARGS(Identification, ProductVersion)); + if ( ASDCP_SUCCESS(result) ) result = MemRDR.ReadObject(OBJ_READ_ARGS(Identification, VersionString)); + if ( ASDCP_SUCCESS(result) ) result = MemRDR.ReadObject(OBJ_READ_ARGS(Identification, ProductUID)); + if ( ASDCP_SUCCESS(result) ) result = MemRDR.ReadObject(OBJ_READ_ARGS(Identification, ModificationDate)); + if ( ASDCP_SUCCESS(result) ) result = MemRDR.ReadUi16(OBJ_READ_ARGS(Identification, ToolkitVersion)); + if ( ASDCP_SUCCESS(result) ) result = MemRDR.ReadObject(OBJ_READ_ARGS(Identification, Platform)); + } + + return result; +} + +// +ASDCP::Result_t +ASDCP::MXF::Identification::WriteToBuffer(ASDCP::FrameBuffer& Buffer) +{ + return WriteKLToBuffer(Buffer, s_MDD_Table[MDDindex_Identification].ul, 0); +} + + +// +void +ASDCP::MXF::Identification::Dump(FILE* stream) +{ + char identbuf[IdentBufferLen]; + + if ( stream == 0 ) + stream = stderr; + + KLVPacket::Dump(stream, false); + fprintf(stream, " InstanceUID = %s\n", InstanceUID.ToString(identbuf)); + fprintf(stream, " ThisGenerationUID = %s\n", ThisGenerationUID.ToString(identbuf)); + fprintf(stream, " CompanyName = %s\n", CompanyName.ToString(identbuf)); + fprintf(stream, " ProductName = %s\n", ProductName.ToString(identbuf)); + fprintf(stream, " ProductVersion = %hu\n", ProductVersion); + fprintf(stream, " VersionString = %s\n", VersionString.ToString(identbuf)); + fprintf(stream, " ProductUID = %s\n", ProductUID.ToString(identbuf)); + fprintf(stream, " ModificationDate = %s\n", ModificationDate.ToString(identbuf)); + fprintf(stream, " ToolkitVersion = %hu\n", ToolkitVersion); + fprintf(stream, " Platform = %s\n", Platform.ToString(identbuf)); + + fputs("==========================================================================\n", stream); +} + +//------------------------------------------------------------------------------------------ +// + +// +ASDCP::Result_t +ASDCP::MXF::ContentStorage::InitFromBuffer(const byte_t* p, ui32_t l) +{ + Result_t result = KLVPacket::InitFromBuffer(p, l, s_MDD_Table[MDDindex_ContentStorage].ul); + + if ( ASDCP_SUCCESS(result) ) + { + TLVReader MemRDR(m_ValueStart, m_ValueLength, m_Lookup); + + result = MemRDR.ReadObject(OBJ_READ_ARGS(InterchangeObject, InstanceUID)); + if ( ASDCP_SUCCESS(result) ) result = MemRDR.ReadObject(OBJ_READ_ARGS(GenerationInterchangeObject, GenerationUID)); + if ( ASDCP_SUCCESS(result) ) result = MemRDR.ReadObject(OBJ_READ_ARGS(ContentStorage, Packages)); + if ( ASDCP_SUCCESS(result) ) result = MemRDR.ReadObject(OBJ_READ_ARGS(ContentStorage, EssenceContainerData)); + } + + return result; +} + +// +ASDCP::Result_t +ASDCP::MXF::ContentStorage::WriteToBuffer(ASDCP::FrameBuffer& Buffer) +{ + return WriteKLToBuffer(Buffer, s_MDD_Table[MDDindex_ContentStorage].ul, 0); +} + +// +void +ASDCP::MXF::ContentStorage::Dump(FILE* stream) +{ + char identbuf[IdentBufferLen]; + + if ( stream == 0 ) + stream = stderr; + + KLVPacket::Dump(stream, false); + fprintf(stream, " InstanceUID = %s\n", InstanceUID.ToString(identbuf)); + fprintf(stream, " GenerationUID = %s\n", GenerationUID.ToString(identbuf)); + fprintf(stream, " Packages:\n"); Packages.Dump(stream); + fprintf(stream, " EssenceContainerData:\n"); EssenceContainerData.Dump(stream); + + fputs("==========================================================================\n", stream); +} + +//------------------------------------------------------------------------------------------ +// + +// +ASDCP::Result_t +ASDCP::MXF::GenericPackage::InitFromBuffer(const byte_t* p, ui32_t l) +{ + TLVReader MemRDR(m_ValueStart, m_ValueLength, m_Lookup); + + Result_t result = MemRDR.ReadObject(OBJ_READ_ARGS(InterchangeObject, InstanceUID)); + // result = MemRDR.ReadObject(OBJ_READ_ARGS(InterchangeObject, InstanceUID)); + if ( ASDCP_SUCCESS(result) ) result = MemRDR.ReadObject(OBJ_READ_ARGS(GenerationInterchangeObject, GenerationUID)); + if ( ASDCP_SUCCESS(result) ) result = MemRDR.ReadObject(OBJ_READ_ARGS(GenericPackage, PackageUID)); + if ( ASDCP_SUCCESS(result) ) result = MemRDR.ReadObject(OBJ_READ_ARGS(GenericPackage, Name)); + if ( ASDCP_SUCCESS(result) ) result = MemRDR.ReadObject(OBJ_READ_ARGS(GenericPackage, PackageCreationDate)); + if ( ASDCP_SUCCESS(result) ) result = MemRDR.ReadObject(OBJ_READ_ARGS(GenericPackage, PackageModifiedDate)); + if ( ASDCP_SUCCESS(result) ) result = MemRDR.ReadObject(OBJ_READ_ARGS(GenericPackage, Tracks)); + + return result; +} + +// +ASDCP::Result_t +ASDCP::MXF::GenericPackage::WriteToBuffer(ASDCP::FrameBuffer& Buffer) +{ + return WriteKLToBuffer(Buffer, s_MDD_Table[MDDindex_DefaultObject].ul, 0); +} + +// +void +ASDCP::MXF::GenericPackage::Dump(FILE* stream) +{ + char identbuf[IdentBufferLen]; + + if ( stream == 0 ) + stream = stderr; + + KLVPacket::Dump(stream, false); + fprintf(stream, " InstanceUID = %s\n", InstanceUID.ToString(identbuf)); + fprintf(stream, " GenerationUID = %s\n", GenerationUID.ToString(identbuf)); + fprintf(stream, " PackageUID = %s\n", PackageUID.ToString(identbuf)); + fprintf(stream, " Name = %s\n", Name.ToString(identbuf)); + fprintf(stream, " PackageCreationDate= %s\n", PackageCreationDate.ToString(identbuf)); + fprintf(stream, " PackageModifiedDate= %s\n", PackageModifiedDate.ToString(identbuf)); + fprintf(stream, " Tracks:\n"); Tracks.Dump(stream); + + fputs("==========================================================================\n", stream); +} + + +//------------------------------------------------------------------------------------------ +// + +// +ASDCP::Result_t +ASDCP::MXF::MaterialPackage::InitFromBuffer(const byte_t* p, ui32_t l) +{ + Result_t result = KLVPacket::InitFromBuffer(p, l, s_MDD_Table[MDDindex_MaterialPackage].ul); + + if ( ASDCP_SUCCESS(result) ) + { + return GenericPackage::InitFromBuffer(p, l); + } + + return result; +} + +// +ASDCP::Result_t +ASDCP::MXF::MaterialPackage::WriteToBuffer(ASDCP::FrameBuffer& Buffer) +{ + return WriteKLToBuffer(Buffer, s_MDD_Table[MDDindex_MaterialPackage].ul, 0); +} + +// +void +ASDCP::MXF::MaterialPackage::Dump(FILE* stream) +{ + GenericPackage::Dump(stream); +} + + +//------------------------------------------------------------------------------------------ +// + +// +ASDCP::Result_t +ASDCP::MXF::SourcePackage::InitFromBuffer(const byte_t* p, ui32_t l) +{ + Result_t result = KLVPacket::InitFromBuffer(p, l, s_MDD_Table[MDDindex_SourcePackage].ul); + + if ( ASDCP_SUCCESS(result) ) + { + return GenericPackage::InitFromBuffer(p, l); + } + + return result; +} + +// +ASDCP::Result_t +ASDCP::MXF::SourcePackage::WriteToBuffer(ASDCP::FrameBuffer& Buffer) +{ + return WriteKLToBuffer(Buffer, s_MDD_Table[MDDindex_SourcePackage].ul, 0); +} + +// +void +ASDCP::MXF::SourcePackage::Dump(FILE* stream) +{ + GenericPackage::Dump(stream); +} + +//------------------------------------------------------------------------------------------ +// + +// +ASDCP::Result_t +ASDCP::MXF::Track::InitFromBuffer(const byte_t* p, ui32_t l) +{ + ASDCP_TEST_NULL(p); + + Result_t result = KLVPacket::InitFromBuffer(p, l, s_MDD_Table[MDDindex_Track].ul); + + if ( ASDCP_SUCCESS(result) ) + { + TLVReader MemRDR(m_ValueStart, m_ValueLength, m_Lookup); + + result = MemRDR.ReadObject(OBJ_READ_ARGS(InterchangeObject, InstanceUID)); + if ( ASDCP_SUCCESS(result) ) result = MemRDR.ReadObject(OBJ_READ_ARGS(GenerationInterchangeObject, GenerationUID)); + if ( ASDCP_SUCCESS(result) ) result = MemRDR.ReadUi32(OBJ_READ_ARGS(GenericTrack, TrackID)); + if ( ASDCP_SUCCESS(result) ) result = MemRDR.ReadUi32(OBJ_READ_ARGS(GenericTrack, TrackNumber)); + if ( ASDCP_SUCCESS(result) ) result = MemRDR.ReadObject(OBJ_READ_ARGS(GenericTrack, TrackName)); + if ( ASDCP_SUCCESS(result) ) result = MemRDR.ReadObject(OBJ_READ_ARGS(GenericTrack, Sequence)); + if ( ASDCP_SUCCESS(result) ) result = MemRDR.ReadObject(OBJ_READ_ARGS(Track, EditRate)); + if ( ASDCP_SUCCESS(result) ) result = MemRDR.ReadUi64(OBJ_READ_ARGS(Track, Origin)); + } + + return result; +} + +// +ASDCP::Result_t +ASDCP::MXF::Track::WriteToBuffer(ASDCP::FrameBuffer& Buffer) +{ + return WriteKLToBuffer(Buffer, s_MDD_Table[MDDindex_Track].ul, 0); +} + +// +void +ASDCP::MXF::Track::Dump(FILE* stream) +{ + char identbuf[IdentBufferLen]; + + if ( stream == 0 ) + stream = stderr; + + KLVPacket::Dump(stream, false); + fprintf(stream, " InstanceUID = %s\n", InstanceUID.ToString(identbuf)); + fprintf(stream, " GenerationUID = %s\n", GenerationUID.ToString(identbuf)); + fprintf(stream, " TrackID = %lu\n", TrackID); + fprintf(stream, " TrackNumber = %lu\n", TrackNumber); + fprintf(stream, " TrackName = %s\n", TrackName.ToString(identbuf)); + fprintf(stream, " Sequence = %s\n", Sequence.ToString(identbuf)); + fprintf(stream, " EditRate = %s\n", EditRate.ToString(identbuf)); + fprintf(stream, " Origin = %s\n", i64sz(Origin, identbuf)); + + fputs("==========================================================================\n", stream); +} + +//------------------------------------------------------------------------------------------ +// + +// +ASDCP::Result_t +ASDCP::MXF::Sequence::InitFromBuffer(const byte_t* p, ui32_t l) +{ + ASDCP_TEST_NULL(p); + + Result_t result = KLVPacket::InitFromBuffer(p, l, s_MDD_Table[MDDindex_Sequence].ul); + + if ( ASDCP_SUCCESS(result) ) + { + TLVReader MemRDR(m_ValueStart, m_ValueLength, m_Lookup); + + result = MemRDR.ReadObject(OBJ_READ_ARGS(InterchangeObject, InstanceUID)); + if ( ASDCP_SUCCESS(result) ) result = MemRDR.ReadObject(OBJ_READ_ARGS(GenerationInterchangeObject, GenerationUID)); + if ( ASDCP_SUCCESS(result) ) result = MemRDR.ReadObject(OBJ_READ_ARGS(StructuralComponent, DataDefinition)); + if ( ASDCP_SUCCESS(result) ) result = MemRDR.ReadUi64(OBJ_READ_ARGS(StructuralComponent, Duration)); + if ( ASDCP_SUCCESS(result) ) result = MemRDR.ReadObject(OBJ_READ_ARGS(Sequence, StructuralComponents)); + } + + return result; +} + +// +ASDCP::Result_t +ASDCP::MXF::Sequence::WriteToBuffer(ASDCP::FrameBuffer& Buffer) +{ + return WriteKLToBuffer(Buffer, s_MDD_Table[MDDindex_Sequence].ul, 0); +} + +// +void +ASDCP::MXF::Sequence::Dump(FILE* stream) +{ + char identbuf[IdentBufferLen]; + + if ( stream == 0 ) + stream = stderr; + + const MDDEntry* Entry = GetMDDEntry(DataDefinition.Data()); + + KLVPacket::Dump(stream, false); + fprintf(stream, " InstanceUID = %s\n", InstanceUID.ToString(identbuf)); + fprintf(stream, " DataDefinition = %s (%s)\n", DataDefinition.ToString(identbuf), (Entry ? Entry->name : "Unknown")); + fprintf(stream, " Duration = %s\n", ui64sz(Duration, identbuf)); + fprintf(stream, " StructuralComponents:\n"); StructuralComponents.Dump(stream); + + fputs("==========================================================================\n", stream); +} + + + +//------------------------------------------------------------------------------------------ +// + +// +ASDCP::Result_t +ASDCP::MXF::SourceClip::InitFromBuffer(const byte_t* p, ui32_t l) +{ + ASDCP_TEST_NULL(p); + + Result_t result = KLVPacket::InitFromBuffer(p, l, s_MDD_Table[MDDindex_SourceClip].ul); + + if ( ASDCP_SUCCESS(result) ) + { + TLVReader MemRDR(m_ValueStart, m_ValueLength, m_Lookup); + + result = MemRDR.ReadObject(OBJ_READ_ARGS(InterchangeObject, InstanceUID)); + if ( ASDCP_SUCCESS(result) ) result = MemRDR.ReadObject(OBJ_READ_ARGS(GenerationInterchangeObject, GenerationUID)); + if ( ASDCP_SUCCESS(result) ) result = MemRDR.ReadObject(OBJ_READ_ARGS(StructuralComponent, DataDefinition)); + if ( ASDCP_SUCCESS(result) ) result = MemRDR.ReadUi64(OBJ_READ_ARGS(SourceClip, StartPosition)); + if ( ASDCP_SUCCESS(result) ) result = MemRDR.ReadUi64(OBJ_READ_ARGS(StructuralComponent, Duration)); + if ( ASDCP_SUCCESS(result) ) result = MemRDR.ReadObject(OBJ_READ_ARGS(SourceClip, SourcePackageID)); + if ( ASDCP_SUCCESS(result) ) result = MemRDR.ReadUi32(OBJ_READ_ARGS(SourceClip, SourceTrackID)); + } + + return result; +} + +// +ASDCP::Result_t +ASDCP::MXF::SourceClip::WriteToBuffer(ASDCP::FrameBuffer& Buffer) +{ + return WriteKLToBuffer(Buffer, s_MDD_Table[MDDindex_SourceClip].ul, 0); +} + +// +void +ASDCP::MXF::SourceClip::Dump(FILE* stream) +{ + char identbuf[IdentBufferLen]; + + if ( stream == 0 ) + stream = stderr; + + KLVPacket::Dump(stream, false); + fprintf(stream, " InstanceUID = %s\n", InstanceUID.ToString(identbuf)); + fprintf(stream, " DataDefinition = %s\n", DataDefinition.ToString(identbuf)); + fprintf(stream, " StartPosition = %s\n", ui64sz(StartPosition, identbuf)); + fprintf(stream, " SourcePackageID = %s\n", SourcePackageID.ToString(identbuf)); + fprintf(stream, " SourcePackageID = %u\n", SourceTrackID); + + fputs("==========================================================================\n", stream); +} + + +//------------------------------------------------------------------------------------------ +// + +// +ASDCP::Result_t +ASDCP::MXF::TimecodeComponent::InitFromBuffer(const byte_t* p, ui32_t l) +{ + ASDCP_TEST_NULL(p); + + Result_t result = KLVPacket::InitFromBuffer(p, l, s_MDD_Table[MDDindex_TimecodeComponent].ul); + + if ( ASDCP_SUCCESS(result) ) + { + TLVReader MemRDR(m_ValueStart, m_ValueLength, m_Lookup); + + result = MemRDR.ReadObject(OBJ_READ_ARGS(InterchangeObject, InstanceUID)); + if ( ASDCP_SUCCESS(result) ) result = MemRDR.ReadObject(OBJ_READ_ARGS(GenerationInterchangeObject, GenerationUID)); + if ( ASDCP_SUCCESS(result) ) result = MemRDR.ReadObject(OBJ_READ_ARGS(StructuralComponent, DataDefinition)); + if ( ASDCP_SUCCESS(result) ) result = MemRDR.ReadUi64(OBJ_READ_ARGS(StructuralComponent, Duration)); + if ( ASDCP_SUCCESS(result) ) result = MemRDR.ReadUi16(OBJ_READ_ARGS(TimecodeComponent, RoundedTimecodeBase)); + if ( ASDCP_SUCCESS(result) ) result = MemRDR.ReadUi64(OBJ_READ_ARGS(TimecodeComponent, StartTimecode)); + if ( ASDCP_SUCCESS(result) ) result = MemRDR.ReadUi8(OBJ_READ_ARGS(TimecodeComponent, DropFrame)); + } + + return result; +} + +// +ASDCP::Result_t +ASDCP::MXF::TimecodeComponent::WriteToBuffer(ASDCP::FrameBuffer& Buffer) +{ + return WriteKLToBuffer(Buffer, s_MDD_Table[MDDindex_TimecodeComponent].ul, 0); +} + +// +void +ASDCP::MXF::TimecodeComponent::Dump(FILE* stream) +{ + char identbuf[IdentBufferLen]; + + if ( stream == 0 ) + stream = stderr; + + KLVPacket::Dump(stream, false); + fprintf(stream, " InstanceUID = %s\n", InstanceUID.ToString(identbuf)); + fprintf(stream, " DataDefinition = %s\n", DataDefinition.ToString(identbuf)); + fprintf(stream, " Duration = %s\n", ui64sz(Duration, identbuf)); + fprintf(stream, " RoundedTimecodeBase= %u\n", RoundedTimecodeBase); + fprintf(stream, " StartTimecode = %s\n", ui64sz(StartTimecode, identbuf)); + fprintf(stream, " DropFrame = %d\n", DropFrame); + + fputs("==========================================================================\n", stream); +} + +//------------------------------------------------------------------------------------------ +// + +// +ASDCP::Result_t +ASDCP::MXF::WaveAudioDescriptor::InitFromBuffer(const byte_t* p, ui32_t l) +{ + ASDCP_TEST_NULL(p); + + Result_t result = KLVPacket::InitFromBuffer(p, l, s_MDD_Table[MDDindex_WaveAudioDescriptor].ul); + + if ( ASDCP_SUCCESS(result) ) + { + TLVReader MemRDR(m_ValueStart, m_ValueLength, m_Lookup); + MXF::Rational TmpRat; + + //InterchangeObject_InstanceUID + result = MemRDR.ReadObject(OBJ_READ_ARGS(InterchangeObject, InstanceUID)); + if ( ASDCP_SUCCESS(result) ) result = MemRDR.ReadObject(OBJ_READ_ARGS(GenerationInterchangeObject, GenerationUID)); + if ( ASDCP_SUCCESS(result) ) result = MemRDR.ReadObject(OBJ_READ_ARGS(FileDescriptor, SampleRate)); + if ( ASDCP_SUCCESS(result) ) result = MemRDR.ReadUi64(OBJ_READ_ARGS(FileDescriptor, ContainerDuration)); + if ( ASDCP_SUCCESS(result) ) result = MemRDR.ReadUi32(OBJ_READ_ARGS(FileDescriptor, LinkedTrackID)); + if ( ASDCP_SUCCESS(result) ) result = MemRDR.ReadObject(OBJ_READ_ARGS(FileDescriptor, EssenceContainer)); + if ( ASDCP_SUCCESS(result) ) result = MemRDR.ReadObject(OBJ_READ_ARGS(GenericSoundEssenceDescriptor, AudioSamplingRate)); + if ( ASDCP_SUCCESS(result) ) result = MemRDR.ReadUi8(OBJ_READ_ARGS(GenericSoundEssenceDescriptor, Locked)); + if ( ASDCP_SUCCESS(result) ) result = MemRDR.ReadUi32(OBJ_READ_ARGS(GenericSoundEssenceDescriptor, ChannelCount)); + if ( ASDCP_SUCCESS(result) ) result = MemRDR.ReadUi32(OBJ_READ_ARGS(GenericSoundEssenceDescriptor, QuantizationBits)); + if ( ASDCP_SUCCESS(result) ) result = MemRDR.ReadUi16(OBJ_READ_ARGS(WaveAudioDescriptor, BlockAlign)); + if ( ASDCP_SUCCESS(result) ) result = MemRDR.ReadUi32(OBJ_READ_ARGS(WaveAudioDescriptor, AvgBps)); + } + + return result; +} + +// +ASDCP::Result_t +ASDCP::MXF::WaveAudioDescriptor::WriteToBuffer(ASDCP::FrameBuffer& Buffer) +{ + return WriteKLToBuffer(Buffer, s_MDD_Table[MDDindex_WaveAudioDescriptor].ul, 0); +} + +// +void +ASDCP::MXF::WaveAudioDescriptor::Dump(FILE* stream) +{ + char identbuf[IdentBufferLen]; + + if ( stream == 0 ) + stream = stderr; + + KLVPacket::Dump(stream, false); + fprintf(stream, " InstanceUID: %s\n", InstanceUID.ToString(identbuf)); + fprintf(stream, " LinkedTrackID: %lu\n", LinkedTrackID); + fprintf(stream, " EssenceContainer: %s\n", EssenceContainer.ToString(identbuf)); + fprintf(stream, "...\n"); + + fputs("==========================================================================\n", stream); +} + + +//------------------------------------------------------------------------------------------ +// + +// +ASDCP::Result_t +ASDCP::MXF::MPEG2VideoDescriptor::InitFromBuffer(const byte_t* p, ui32_t l) +{ + ASDCP_TEST_NULL(p); + + Result_t result = KLVPacket::InitFromBuffer(p, l, s_MDD_Table[MDDindex_MPEG2VideoDescriptor].ul); + + if ( ASDCP_SUCCESS(result) ) + { + TLVReader MemRDR(m_ValueStart, m_ValueLength, m_Lookup); + MXF::Rational TmpRat; + ui8_t tmp_delay; + + //InterchangeObject_InstanceUID + result = MemRDR.ReadObject(OBJ_READ_ARGS(InterchangeObject, InstanceUID)); + if ( ASDCP_SUCCESS(result) ) result = MemRDR.ReadObject(OBJ_READ_ARGS_R(FileDescriptor, SampleRate, TmpRat)); + SampleRate = TmpRat; + ui64_t tmpDuration = 0; + if ( ASDCP_SUCCESS(result) ) result = MemRDR.ReadUi64(OBJ_READ_ARGS_R(FileDescriptor, ContainerDuration, tmpDuration)); + ContainerDuration = tmpDuration; + if ( ASDCP_SUCCESS(result) ) result = MemRDR.ReadUi32(OBJ_READ_ARGS(FileDescriptor, LinkedTrackID)); + if ( ASDCP_SUCCESS(result) ) result = MemRDR.ReadObject(OBJ_READ_ARGS(FileDescriptor, EssenceContainer)); + if ( ASDCP_SUCCESS(result) ) result = MemRDR.ReadObject(OBJ_READ_ARGS_R(GenericPictureEssenceDescriptor, AspectRatio, TmpRat)); + AspectRatio = TmpRat; + if ( ASDCP_SUCCESS(result) ) result = MemRDR.ReadUi8(OBJ_READ_ARGS(GenericPictureEssenceDescriptor, FrameLayout)); + if ( ASDCP_SUCCESS(result) ) result = MemRDR.ReadUi32(OBJ_READ_ARGS(GenericPictureEssenceDescriptor, StoredWidth)); + if ( ASDCP_SUCCESS(result) ) result = MemRDR.ReadUi32(OBJ_READ_ARGS(GenericPictureEssenceDescriptor, StoredHeight)); + if ( ASDCP_SUCCESS(result) ) result = MemRDR.ReadUi32(OBJ_READ_ARGS(CDCIEssenceDescriptor, ComponentDepth)); + if ( ASDCP_SUCCESS(result) ) result = MemRDR.ReadUi32(OBJ_READ_ARGS(CDCIEssenceDescriptor, HorizontalSubsampling)); + if ( ASDCP_SUCCESS(result) ) result = MemRDR.ReadUi32(OBJ_READ_ARGS(CDCIEssenceDescriptor, VerticalSubsampling)); + if ( ASDCP_SUCCESS(result) ) result = MemRDR.ReadUi8(OBJ_READ_ARGS(CDCIEssenceDescriptor, ColorSiting)); + if ( ASDCP_SUCCESS(result) ) result = MemRDR.ReadUi8(OBJ_READ_ARGS(MPEG2VideoDescriptor, CodedContentType)); + if ( ASDCP_SUCCESS(result) ) result = MemRDR.ReadUi32(OBJ_READ_ARGS(MPEG2VideoDescriptor, BitRate)); + if ( ASDCP_SUCCESS(result) ) result = MemRDR.ReadUi8(OBJ_READ_ARGS(MPEG2VideoDescriptor, ProfileAndLevel)); + if ( ASDCP_SUCCESS(result) ) result = MemRDR.ReadUi8(OBJ_READ_ARGS_R(MPEG2VideoDescriptor, LowDelay, tmp_delay)); + LowDelay = (tmp_delay > 0); + } + + return result; +} + +// +ASDCP::Result_t +ASDCP::MXF::MPEG2VideoDescriptor::WriteToBuffer(ASDCP::FrameBuffer& Buffer) +{ + return WriteKLToBuffer(Buffer, s_MDD_Table[MDDindex_MPEG2VideoDescriptor].ul, 0); +} + +// +void +ASDCP::MXF::MPEG2VideoDescriptor::Dump(FILE* stream) +{ + char identbuf[IdentBufferLen]; + + if ( stream == 0 ) + stream = stderr; + + KLVPacket::Dump(stream, false); + fprintf(stream, " InstanceUID: %s\n", InstanceUID.ToString(identbuf)); + fprintf(stream, " LinkedTrackID: %lu\n", LinkedTrackID); + fprintf(stream, " EssenceContainer: %s\n", EssenceContainer.ToString(identbuf)); + ASDCP::MPEG2::VideoDescriptorDump(*this, stream); + + fputs("==========================================================================\n", stream); +} + +//------------------------------------------------------------------------------------------ +// + +// +ASDCP::Result_t +ASDCP::MXF::FileDescriptor::InitFromBuffer(const byte_t* p, ui32_t l) +{ + ASDCP_TEST_NULL(p); + + Result_t result = KLVPacket::InitFromBuffer(p, l); // any of a variety of ULs, really + + if ( ASDCP_SUCCESS(result) ) + { + TLVReader MemRDR(m_ValueStart, m_ValueLength, m_Lookup); + + result = MemRDR.ReadObject(OBJ_READ_ARGS(InterchangeObject, InstanceUID)); + if ( ASDCP_SUCCESS(result) ) result = MemRDR.ReadObject(OBJ_READ_ARGS(GenerationInterchangeObject, GenerationUID)); + if ( ASDCP_SUCCESS(result) ) result = MemRDR.ReadObject(OBJ_READ_ARGS(GenericDescriptor, Locators)); + if ( ASDCP_SUCCESS(result) ) result = MemRDR.ReadObject(OBJ_READ_ARGS(GenericDescriptor, SubDescriptors)); + if ( ASDCP_SUCCESS(result) ) result = MemRDR.ReadUi32(OBJ_READ_ARGS(FileDescriptor, LinkedTrackID)); + if ( ASDCP_SUCCESS(result) ) result = MemRDR.ReadObject(OBJ_READ_ARGS(FileDescriptor, SampleRate)); + if ( ASDCP_SUCCESS(result) ) result = MemRDR.ReadUi64(OBJ_READ_ARGS(FileDescriptor, ContainerDuration)); + if ( ASDCP_SUCCESS(result) ) result = MemRDR.ReadObject(OBJ_READ_ARGS(FileDescriptor, EssenceContainer)); + if ( ASDCP_SUCCESS(result) ) result = MemRDR.ReadObject(OBJ_READ_ARGS(FileDescriptor, Codec)); + } + + return result; +} + +// +ASDCP::Result_t +ASDCP::MXF::FileDescriptor::WriteToBuffer(ASDCP::FrameBuffer& Buffer) +{ + return WriteKLToBuffer(Buffer, s_MDD_Table[MDDindex_FileDescriptor].ul, 0); +} + +// +void +ASDCP::MXF::FileDescriptor::Dump(FILE* stream) +{ + char identbuf[IdentBufferLen]; + + if ( stream == 0 ) + stream = stderr; + + KLVPacket::Dump(stream, false); + fprintf(stream, " InstanceUID: %s\n", InstanceUID.ToString(identbuf)); + fprintf(stream, " GenerationUID: %lu\n", GenerationUID.ToString(identbuf)); + fprintf(stream, " LinkedTrackID: %lu\n", LinkedTrackID); + fprintf(stream, " EssenceContainer: %s\n", EssenceContainer.ToString(identbuf)); + fprintf(stream, "...\n"); + + fputs("==========================================================================\n", stream); +} + +//------------------------------------------------------------------------------------------ +// + +// +ASDCP::Result_t +ASDCP::MXF::GenericPictureEssenceDescriptor::WriteToBuffer(ASDCP::FrameBuffer& Buffer) +{ + return WriteKLToBuffer(Buffer, s_MDD_Table[MDDindex_GenericPictureEssenceDescriptor].ul, 0); +} + +//------------------------------------------------------------------------------------------ +// + +// +ASDCP::Result_t +ASDCP::MXF::RGBAEssenceDescriptor::WriteToBuffer(ASDCP::FrameBuffer& Buffer) +{ + return WriteKLToBuffer(Buffer, s_MDD_Table[MDDindex_RGBAEssenceDescriptor].ul, 0); +} + +//------------------------------------------------------------------------------------------ +// + +// +ASDCP::Result_t +ASDCP::MXF::JPEG2000PictureSubDescriptor::WriteToBuffer(ASDCP::FrameBuffer& Buffer) +{ + return WriteKLToBuffer(Buffer, s_MDD_Table[MDDindex_JPEG2000PictureSubDescriptor].ul, 0); +} + +//------------------------------------------------------------------------------------------ +// + +// +ASDCP::Result_t +ASDCP::MXF::CryptographicFramework::InitFromBuffer(const byte_t* p, ui32_t l) +{ + ASDCP_TEST_NULL(p); + + Result_t result = KLVPacket::InitFromBuffer(p, l, s_MDD_Table[MDDindex_CryptographicFramework].ul); + + if ( ASDCP_SUCCESS(result) ) + { + TLVReader MemRDR(m_ValueStart, m_ValueLength, m_Lookup); + + result = MemRDR.ReadObject(OBJ_READ_ARGS(InterchangeObject, InstanceUID)); + if ( ASDCP_SUCCESS(result) ) result = MemRDR.ReadObject(OBJ_READ_ARGS(CryptographicFramework, ContextSR)); + } + + return result; +} + +// +void +ASDCP::MXF::CryptographicFramework::Dump(FILE* stream) +{ + char identbuf[IdentBufferLen]; + + if ( stream == 0 ) + stream = stderr; + + KLVPacket::Dump(stream, false); + fprintf(stream, " InstanceUID = %s\n", InstanceUID.ToString(identbuf)); + fprintf(stream, " ContextSR = %s\n", ContextSR.ToString(identbuf)); + + fputs("==========================================================================\n", stream); +} + + +//------------------------------------------------------------------------------------------ +// + +// +ASDCP::Result_t +ASDCP::MXF::CryptographicContext::InitFromBuffer(const byte_t* p, ui32_t l) +{ + ASDCP_TEST_NULL(p); + + Result_t result = KLVPacket::InitFromBuffer(p, l, s_MDD_Table[MDDindex_CryptographicContext].ul); + + if ( ASDCP_SUCCESS(result) ) + { + TLVReader MemRDR(m_ValueStart, m_ValueLength, m_Lookup); + + result = MemRDR.ReadObject(OBJ_READ_ARGS(InterchangeObject, InstanceUID)); + if ( ASDCP_SUCCESS(result) ) result = MemRDR.ReadObject(OBJ_READ_ARGS(CryptographicContext, ContextID)); + if ( ASDCP_SUCCESS(result) ) result = MemRDR.ReadObject(OBJ_READ_ARGS(CryptographicContext, SourceEssenceContainer)); + if ( ASDCP_SUCCESS(result) ) result = MemRDR.ReadObject(OBJ_READ_ARGS(CryptographicContext, CipherAlgorithm)); + if ( ASDCP_SUCCESS(result) ) result = MemRDR.ReadObject(OBJ_READ_ARGS(CryptographicContext, MICAlgorithm)); + if ( ASDCP_SUCCESS(result) ) result = MemRDR.ReadObject(OBJ_READ_ARGS(CryptographicContext, CryptographicKeyID)); + } + + return result; +} + +// +void +ASDCP::MXF::CryptographicContext::Dump(FILE* stream) +{ + char identbuf[IdentBufferLen]; + + if ( stream == 0 ) + stream = stderr; + + KLVPacket::Dump(stream, false); + fprintf(stream, " InstanceUID = %s\n", InstanceUID.ToString(identbuf)); + fprintf(stream, " ContextID = %s\n", ContextID.ToString(identbuf)); + fprintf(stream, " SourceEssenceCnt = %s\n", SourceEssenceContainer.ToString(identbuf)); + fprintf(stream, " CipherAlgorithm = %s\n", CipherAlgorithm.ToString(identbuf)); + fprintf(stream, " MICAlgorithm = %s\n", MICAlgorithm.ToString(identbuf)); + fprintf(stream, " CryptographicKeyID = %s\n", CryptographicKeyID.ToString(identbuf)); + + fputs("==========================================================================\n", stream); +} + +// +// end MXF.cpp +// diff --git a/src/Metadata.h b/src/Metadata.h new file mode 100755 index 0000000..0a04790 --- /dev/null +++ b/src/Metadata.h @@ -0,0 +1,389 @@ +// +// +// TODO: constructor initializers for all member variables +// + +#ifndef _METADATA_H_ +#define _METADATA_H_ + +#include "MXF.h" + +namespace ASDCP +{ + namespace MXF + { + // + class Identification : public InterchangeObject + { + ASDCP_NO_COPY_CONSTRUCT(Identification); + + public: + UUID ThisGenerationUID; + UTF16String CompanyName; + UTF16String ProductName; + ui16_t ProductVersion; + UTF16String VersionString; + UUID ProductUID; + Timestamp ModificationDate; + ui16_t ToolkitVersion; + UTF16String Platform; + + Identification() {} + virtual ~Identification() {} + virtual Result_t InitFromBuffer(const byte_t* p, ui32_t l); + virtual Result_t WriteToBuffer(ASDCP::FrameBuffer&); + virtual void Dump(FILE* = 0); + }; + + + // + class ContentStorage : public InterchangeObject + { + ASDCP_NO_COPY_CONSTRUCT(ContentStorage); + + public: + UUID GenerationUID; + Batch<UUID> Packages; + Batch<UUID> EssenceContainerData; + + ContentStorage() {} + virtual ~ContentStorage() {} + virtual Result_t InitFromBuffer(const byte_t* p, ui32_t l); + virtual Result_t WriteToBuffer(ASDCP::FrameBuffer&); + virtual void Dump(FILE* = 0); + }; + + // + class GenericPackage : public InterchangeObject + { + ASDCP_NO_COPY_CONSTRUCT(GenericPackage); + + public: + UMID PackageUID; + UUID GenerationUID; + UTF16String Name; + Timestamp PackageCreationDate; + Timestamp PackageModifiedDate; + Batch<UID> Tracks; + + GenericPackage() {} + virtual ~GenericPackage() {} + virtual Result_t InitFromBuffer(const byte_t* p, ui32_t l) = 0; + virtual Result_t WriteToBuffer(ASDCP::FrameBuffer&); + virtual void Dump(FILE* = 0) = 0; + }; + + + // + class MaterialPackage : public GenericPackage + { + ASDCP_NO_COPY_CONSTRUCT(MaterialPackage); + + public: + MaterialPackage() {} + virtual ~MaterialPackage() {} + virtual Result_t InitFromBuffer(const byte_t* p, ui32_t l); + virtual Result_t WriteToBuffer(ASDCP::FrameBuffer&); + virtual void Dump(FILE* = 0); + }; + + + // + class SourcePackage : public GenericPackage + { + ASDCP_NO_COPY_CONSTRUCT(SourcePackage); + + public: + SourcePackage() {} + virtual ~SourcePackage() {} + virtual Result_t InitFromBuffer(const byte_t* p, ui32_t l); + virtual Result_t WriteToBuffer(ASDCP::FrameBuffer&); + virtual void Dump(FILE* = 0); + }; + + // + class Track : public InterchangeObject + { + ASDCP_NO_COPY_CONSTRUCT(Track); + + public: + UUID GenerationUID; + ui32_t TrackID; + ui32_t TrackNumber; + UTF16String TrackName; + UUID Sequence; + Rational EditRate; + ui64_t Origin; + + Track() {} + virtual ~Track() {} + virtual Result_t InitFromBuffer(const byte_t* p, ui32_t l); + virtual Result_t WriteToBuffer(ASDCP::FrameBuffer&); + virtual void Dump(FILE* = 0); + }; + + // + class Sequence : public InterchangeObject + { + ASDCP_NO_COPY_CONSTRUCT(Sequence); + + public: + UUID GenerationUID; + UL DataDefinition; + ui64_t Duration; + Batch<UID> StructuralComponents; + + Sequence() {} + virtual ~Sequence() {} + virtual Result_t InitFromBuffer(const byte_t* p, ui32_t l); + virtual Result_t WriteToBuffer(ASDCP::FrameBuffer&); + virtual void Dump(FILE* = 0); + }; + + // + class SourceClip : public InterchangeObject + { + ASDCP_NO_COPY_CONSTRUCT(SourceClip); + + public: + UUID GenerationUID; + UL DataDefinition; + ui64_t StartPosition; + ui64_t Duration; + UMID SourcePackageID; + ui32_t SourceTrackID; + + SourceClip() {} + virtual ~SourceClip() {} + virtual Result_t InitFromBuffer(const byte_t* p, ui32_t l); + virtual Result_t WriteToBuffer(ASDCP::FrameBuffer&); + virtual void Dump(FILE* = 0); + }; + + // + class TimecodeComponent : public InterchangeObject + { + ASDCP_NO_COPY_CONSTRUCT(TimecodeComponent); + + public: + UUID GenerationUID; + UL DataDefinition; + ui64_t Duration; + ui16_t RoundedTimecodeBase; + ui64_t StartTimecode; + ui8_t DropFrame; + + TimecodeComponent() {} + virtual ~TimecodeComponent() {} + virtual Result_t InitFromBuffer(const byte_t* p, ui32_t l); + virtual Result_t WriteToBuffer(ASDCP::FrameBuffer&); + virtual void Dump(FILE* = 0); + }; + + // + class MPEG2VideoDescriptor : public ASDCP::MPEG2::VideoDescriptor, public InterchangeObject + { + ASDCP_NO_COPY_CONSTRUCT(MPEG2VideoDescriptor); + + public: + ui32_t LinkedTrackID; + UL EssenceContainer; + + MPEG2VideoDescriptor() : LinkedTrackID(0) {} + ~MPEG2VideoDescriptor() {} + virtual Result_t InitFromBuffer(const byte_t* p, ui32_t l); + virtual Result_t WriteToBuffer(ASDCP::FrameBuffer&); + virtual void Dump(FILE* = 0); + }; + + // + class FileDescriptor : public InterchangeObject + { + ASDCP_NO_COPY_CONSTRUCT(FileDescriptor); + + public: + UUID GenerationUID; + Batch<UUID> Locators; + Batch<UUID> SubDescriptors; + ui32_t LinkedTrackID; + Rational SampleRate; + ui64_t ContainerDuration; + UL EssenceContainer; + UL Codec; + + FileDescriptor() : LinkedTrackID(0), ContainerDuration(0) {} + ~FileDescriptor() {} + virtual Result_t InitFromBuffer(const byte_t* p, ui32_t l); + virtual Result_t WriteToBuffer(ASDCP::FrameBuffer&); + virtual void Dump(FILE* = 0); + }; + + // + class WaveAudioDescriptor : public FileDescriptor + { + ASDCP_NO_COPY_CONSTRUCT(WaveAudioDescriptor); + + public: + Rational AudioSamplingRate; + ui8_t Locked; + i8_t AudioRefLevel; + ui8_t ElectroSpatialFormulation; + ui32_t ChannelCount; + ui32_t QuantizationBits; + i8_t DialNorm; + UL SoundEssenceCompression; + ui16_t BlockAlign; + ui8_t SequenceOffset; + ui32_t AvgBps; + // Stream PeakEnvelope; + + WaveAudioDescriptor() : + Locked(false), AudioRefLevel(0), ElectroSpatialFormulation(0), + ChannelCount(0), QuantizationBits(0), DialNorm(0), BlockAlign(0), + SequenceOffset(0), AvgBps(0) {} + + ~WaveAudioDescriptor() {} + virtual Result_t InitFromBuffer(const byte_t* p, ui32_t l); + virtual Result_t WriteToBuffer(ASDCP::FrameBuffer&); + virtual void Dump(FILE* = 0); + }; + + // + class GenericPictureEssenceDescriptor : public FileDescriptor + { + ASDCP_NO_COPY_CONSTRUCT(GenericPictureEssenceDescriptor); + + public: + ui8_t FrameLayout; + ui32_t StoredWidth; + ui32_t StoredHeight; + ui32_t DisplayWidth; + ui32_t DisplayHeight; + Rational AspectRatio; + ui32_t ComponentMaxRef; + ui32_t ComponentMinRef; + UL Gamma; + UL PictureEssenceCoding; + + GenericPictureEssenceDescriptor() : + FrameLayout(0), StoredWidth(0), StoredHeight(0), DisplayWidth(0), + DisplayHeight(0), ComponentMaxRef(0), ComponentMinRef(0) {} + + ~GenericPictureEssenceDescriptor() {} + // virtual Result_t InitFromBuffer(const byte_t* p, ui32_t l); + virtual Result_t WriteToBuffer(ASDCP::FrameBuffer&); + // virtual void Dump(FILE* = 0); + }; + + // + class RGBAEssenceDescriptor : public GenericPictureEssenceDescriptor + { + ASDCP_NO_COPY_CONSTRUCT(RGBAEssenceDescriptor); + + public: + class RGBLayout + { + public: + struct element { + ui8_t Code; + ui8_t Depth; + } PictureElement[8]; + RGBLayout() { memset(PictureElement, 0, sizeof(PictureElement)); } + }; + + RGBLayout PixelLayout; + + RGBAEssenceDescriptor() {} + ~RGBAEssenceDescriptor() {} + // virtual Result_t InitFromBuffer(const byte_t* p, ui32_t l); + virtual Result_t WriteToBuffer(ASDCP::FrameBuffer&); + // virtual void Dump(FILE* = 0); + }; + + class Raw + { + ASDCP_NO_COPY_CONSTRUCT(Raw); + + public: + byte_t* data; + Raw() {} + ~Raw() {} + + }; + + // + class JPEG2000PictureSubDescriptor : public InterchangeObject + { + ASDCP_NO_COPY_CONSTRUCT(JPEG2000PictureSubDescriptor); + + public: + 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; + Raw PictureComponentSizing; + Raw CodingStyleDefault; + Raw QuantizationDefault; + + JPEG2000PictureSubDescriptor() : + Rsize(0), Xsize(0), Ysize(0), + XOsize(0), YOsize(0), XTsize(0), + YTsize(0), XTOsize(0), YTOsize(0), + Csize(0) {} + + ~JPEG2000PictureSubDescriptor() {} + // virtual Result_t InitFromBuffer(const byte_t* p, ui32_t l); + virtual Result_t WriteToBuffer(ASDCP::FrameBuffer&); + // virtual void Dump(FILE* = 0); + }; + + + // + class CryptographicFramework : public InterchangeObject + { + ASDCP_NO_COPY_CONSTRUCT(CryptographicFramework); + + public: + UUID ContextSR; + + CryptographicFramework() {} + ~CryptographicFramework() {} + virtual Result_t InitFromBuffer(const byte_t* p, ui32_t l); + // virtual Result_t WriteToBuffer(ASDCP::FrameBuffer&); + virtual void Dump(FILE* = 0); + }; + + // + class CryptographicContext : public InterchangeObject + { + ASDCP_NO_COPY_CONSTRUCT(CryptographicContext); + + public: + UUID ContextID; + UL SourceEssenceContainer; + UL CipherAlgorithm; + UL MICAlgorithm; + UUID CryptographicKeyID; + + CryptographicContext() {} + ~CryptographicContext() {} + virtual Result_t InitFromBuffer(const byte_t* p, ui32_t l); + // virtual Result_t WriteToBuffer(ASDCP::FrameBuffer&); + virtual void Dump(FILE* = 0); + }; + + } // namespace MXF +} // namespace ASDCP + + +#endif // _METADATA_H_ + +// +// end Metadata.h +// diff --git a/src/PCMParserList.cpp b/src/PCMParserList.cpp new file mode 100755 index 0000000..2679162 --- /dev/null +++ b/src/PCMParserList.cpp @@ -0,0 +1,213 @@ +/* +Copyright (c) 2004, 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 PCMParserList.cpp + \version $Id$ + \brief Read WAV file(s), multiplex multiple PCM frame buffers into one +*/ + +#include <PCMParserList.h> +#include <assert.h> + +using namespace ASDCP; + + +ASDCP::ParserInstance::ParserInstance() : m_p(0), m_SampleSize(0) +{ +} + +ASDCP::ParserInstance::~ParserInstance() +{ +} + +// PCM::CalcSampleSize(ADesc); +Result_t +ASDCP::ParserInstance::OpenRead(const char* filename, Rational& PictureRate) +{ + ASDCP_TEST_NULL_STR(filename); + + Result_t result = Parser.OpenRead(filename, PictureRate); + + if ( ASDCP_SUCCESS(result) ) + result = Parser.FillAudioDescriptor(ADesc); + + if ( ASDCP_SUCCESS(result) ) + { + ADesc.SampleRate = PictureRate; + m_SampleSize = PCM::CalcSampleSize(ADesc); + result = FB.Capacity(PCM::CalcFrameBufferSize(ADesc)); + } + + return result; +} + + +// deposit the next available sample into the given framebuffer +Result_t +ASDCP::ParserInstance::PutSample(byte_t* p) +{ + ASDCP_TEST_NULL(p); + + memcpy(p, m_p, m_SampleSize); + m_p += m_SampleSize; + return RESULT_OK; +} + + +// +Result_t +ASDCP::ParserInstance::ReadFrame() +{ + Result_t result = Parser.ReadFrame(FB); + m_p = ASDCP_SUCCESS(result) ? FB.RoData() : 0; + return result; +} + + +//------------------------------------------------------------------------------------------ +// + + +// +ASDCP::PCMParserList::PCMParserList() : m_ChannelCount(0) +{ +} + +ASDCP::PCMParserList::~PCMParserList() +{ + while ( ! empty() ) + { + delete back(); + pop_back(); + } +} + + +// +Result_t +ASDCP::PCMParserList::OpenRead(ui32_t argc, const char** argv, Rational& PictureRate) +{ + ASDCP_TEST_NULL_STR(argv); + Result_t result = RESULT_OK; + + for ( ui32_t i = 0; i < argc && ASDCP_SUCCESS(result); i++ ) + { + ParserInstance* I = new ParserInstance; + result = I->OpenRead(argv[i], PictureRate); + + if ( ASDCP_SUCCESS(result) ) + { + if ( i == 0 ) + m_ADesc = I->ADesc; + + else + m_ADesc.BlockAlign += I->ADesc.BlockAlign; + // result = I->CmpADesc(m_ADesc); + + m_ChannelCount += I->ADesc.ChannelCount; + } + + if ( ASDCP_SUCCESS(result) ) + result = I->FB.Capacity(PCM::CalcFrameBufferSize(m_ADesc)); + + if ( ASDCP_SUCCESS(result) ) + push_back(I); + } + + m_ADesc.ChannelCount = m_ChannelCount; + + if ( ASDCP_FAILURE(result) ) + clear(); + + return result; +} + +// +Result_t +ASDCP::PCMParserList::FillAudioDescriptor(PCM::AudioDescriptor& ADesc) const +{ + ADesc = m_ADesc; + return RESULT_OK; +} + +// +Result_t +ASDCP::PCMParserList::Reset() +{ + Result_t result = RESULT_OK; + PCMParserList::iterator self_i; + + for ( self_i = begin(); self_i != end() && ASDCP_SUCCESS(result) ; self_i++ ) + result = (*self_i)->Parser.Reset(); + + return result; +} + + +// +Result_t +ASDCP::PCMParserList::ReadFrame(PCM::FrameBuffer& OutFB) +{ + Result_t result = RESULT_OK; + + if ( size() == 1 ) + return front()->Parser.ReadFrame(OutFB); + + PCMParserList::iterator self_i; + assert(PCM::CalcFrameBufferSize(m_ADesc) <= OutFB.Capacity()); + + for ( self_i = begin(); self_i != end() && ASDCP_SUCCESS(result) ; self_i++ ) + result = (*self_i)->ReadFrame(); + + if ( ASDCP_SUCCESS(result) ) + { + OutFB.Size(PCM::CalcFrameBufferSize(m_ADesc)); + + // ui32_t sample_size = (PCM::CalcSampleSize(m_ADesc)); + byte_t* Out_p = OutFB.Data(); + byte_t* End_p = Out_p + OutFB.Size(); + + while ( Out_p < End_p && ASDCP_SUCCESS(result) ) + { + self_i = begin(); + + while ( self_i != end() && ASDCP_SUCCESS(result) ) + { + result = (*self_i)->PutSample(Out_p); + Out_p += (*self_i)->SampleSize(); + self_i++; + } + } + + assert(Out_p == End_p); + } + + return result; +} + +// +// end PCMParserList.cpp +// diff --git a/src/PCMParserList.h b/src/PCMParserList.h new file mode 100755 index 0000000..e6bf20b --- /dev/null +++ b/src/PCMParserList.h @@ -0,0 +1,86 @@ +/* +Copyright (c) 2004, 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 PCMParserList.h + \version $Id$ + \brief Read WAV file(s), multiplex multiple PCM frame buffers into one +*/ + +#ifndef _PCMPARSERLIST_H_ +#define _PCMPARSERLIST_H_ + +#include <AS_DCP.h> +#include <vector> + +namespace ASDCP +{ + // + class ParserInstance + { + const byte_t* m_p; + ui32_t m_SampleSize; + + ASDCP_NO_COPY_CONSTRUCT(ParserInstance); + + public: + PCM::WAVParser Parser; + PCM::FrameBuffer FB; + PCM::AudioDescriptor ADesc; + + ParserInstance(); + virtual ~ParserInstance(); + + Result_t OpenRead(const char* filename, Rational& PictureRate); + Result_t PutSample(byte_t* p); + Result_t ReadFrame(); + inline ui32_t SampleSize() { return m_SampleSize; } + }; + + // + class PCMParserList : public std::vector<ParserInstance*> + { + PCM::AudioDescriptor m_ADesc; + ui32_t m_ChannelCount; + + ASDCP_NO_COPY_CONSTRUCT(PCMParserList); + + public: + PCMParserList(); + virtual ~PCMParserList(); + + Result_t OpenRead(ui32_t argc, const char** argv, Rational& PictureRate); + Result_t FillAudioDescriptor(PCM::AudioDescriptor& ADesc) const; + Result_t Reset(); + Result_t ReadFrame(PCM::FrameBuffer& OutFB); + }; +} + + +#endif // _PCMPARSERLIST_H_ + +// +// end PCMParserList.h +// diff --git a/src/PCM_Parser.cpp b/src/PCM_Parser.cpp new file mode 100755 index 0000000..70cc277 --- /dev/null +++ b/src/PCM_Parser.cpp @@ -0,0 +1,214 @@ +/* +Copyright (c) 2004, 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 PCM_Parser.cpp + \version $Id$ + \brief AS-DCP library, PCM raw essence reader implementation +*/ + +#include <Wav.h> +#include <assert.h> + +using namespace ASDCP; +using namespace ASDCP::PCM; +using namespace ASDCP::Wav; + + +//------------------------------------------------------------------------------------------ + +// +class ASDCP::PCM::WAVParser::h__WAVParser +{ + FileReader m_FileReader; + bool m_EOF; + ui32_t m_DataStart; + ui32_t m_DataLength; + ui32_t m_ReadCount; + ui32_t m_FrameBufferSize; + ui32_t m_FramesRead; + Rational m_PictureRate; + + ASDCP_NO_COPY_CONSTRUCT(h__WAVParser); + +public: + AudioDescriptor m_ADesc; + + + h__WAVParser() : + m_EOF(false), m_DataStart(0), m_DataLength(0), m_ReadCount(0), + m_FrameBufferSize(0), m_FramesRead(0) {} + + ~h__WAVParser() + { + Close(); + } + + Result_t OpenRead(const char* filename, const Rational& PictureRate); + void Close(); + void Reset(); + Result_t ReadFrame(FrameBuffer&); +}; + + +// +void +ASDCP::PCM::WAVParser::h__WAVParser::Close() +{ + m_FileReader.Close(); +} + +// +void +ASDCP::PCM::WAVParser::h__WAVParser::Reset() +{ + m_FileReader.Seek(m_DataStart); + m_FramesRead = 0; + m_ReadCount = 0; +} + +// +ASDCP::Result_t +ASDCP::PCM::WAVParser::h__WAVParser::OpenRead(const char* filename, const Rational& PictureRate) +{ + ASDCP_TEST_NULL_STR(filename); + SimpleWaveHeader WavHeader; + + Result_t result = m_FileReader.OpenRead(filename); + + if ( ASDCP_SUCCESS(result) ) + result = WavHeader.ReadFromFile(m_FileReader, &m_DataStart); + + if ( ASDCP_SUCCESS(result) ) + { + WavHeader.FillADesc(m_ADesc, PictureRate); + m_FrameBufferSize = ASDCP::PCM::CalcFrameBufferSize(m_ADesc); + m_DataLength = WavHeader.data_len; + m_ADesc.ContainerDuration = m_DataLength / m_FrameBufferSize; + Reset(); + } + + return result; +} + +// +ASDCP::Result_t +ASDCP::PCM::WAVParser::h__WAVParser::ReadFrame(FrameBuffer& FB) +{ + FB.Size(0); + + if ( m_EOF || m_ReadCount >= m_DataLength ) + return RESULT_ENDOFFILE; + + if ( FB.Capacity() < m_FrameBufferSize ) + { + DefaultLogSink().Error("FrameBuf.Capacity: %lu FrameLength: %lu\n", + FB.Capacity(), m_FrameBufferSize); + return RESULT_SMALLBUF; + } + + ui32_t read_count = 0; + Result_t result = m_FileReader.Read(FB.Data(), m_FrameBufferSize, &read_count); + + if ( result == RESULT_ENDOFFILE ) + { + m_EOF = true; + + if ( read_count > 0 ) + result = RESULT_OK; + } + + if ( ASDCP_SUCCESS(result) ) + { + m_ReadCount += read_count; + FB.Size(read_count); + FB.FrameNumber(m_FramesRead++); + } + + return result; +} + + +//------------------------------------------------------------------------------------------ + +ASDCP::PCM::WAVParser::WAVParser() +{ +} + +ASDCP::PCM::WAVParser::~WAVParser() +{ +} + +// Opens the stream for reading, parses enough data to provide a complete +// set of stream metadata for the MXFWriter below. +ASDCP::Result_t +ASDCP::PCM::WAVParser::OpenRead(const char* filename, const Rational& PictureRate) const +{ + const_cast<ASDCP::PCM::WAVParser*>(this)->m_Parser = new h__WAVParser; + + Result_t result = m_Parser->OpenRead(filename, PictureRate); + + if ( ASDCP_FAILURE(result) ) + const_cast<ASDCP::PCM::WAVParser*>(this)->m_Parser.release(); + + return result; +} + +// Rewinds the stream to the beginning. +ASDCP::Result_t +ASDCP::PCM::WAVParser::Reset() const +{ + if ( m_Parser.empty() ) + return RESULT_INIT; + + m_Parser->Reset(); + return RESULT_OK; +} + +// Places a frame of data in the frame buffer. Fails if the buffer is too small +// or the stream is empty. +ASDCP::Result_t +ASDCP::PCM::WAVParser::ReadFrame(FrameBuffer& FB) const +{ + if ( m_Parser.empty() ) + return RESULT_INIT; + + return m_Parser->ReadFrame(FB); +} + +ASDCP::Result_t +ASDCP::PCM::WAVParser::FillAudioDescriptor(AudioDescriptor& ADesc) const +{ + if ( m_Parser.empty() ) + return RESULT_INIT; + + ADesc = m_Parser->m_ADesc; + return RESULT_OK; +} + + +// +// end PCM_Parser.cpp +// diff --git a/src/Wav.cpp b/src/Wav.cpp new file mode 100755 index 0000000..42e0d21 --- /dev/null +++ b/src/Wav.cpp @@ -0,0 +1,207 @@ +/* +Copyright (c) 2005, 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 Wav.cpp + \version $Id$ + \brief Wave file common elements +*/ + +#include <Wav.h> +#include <assert.h> + + +const ui32_t SimpleHeaderLength = 46; + +// +ASDCP::Wav::SimpleWaveHeader::SimpleWaveHeader(ASDCP::PCM::AudioDescriptor& ADesc) +{ + format = 1; + nchannels = ADesc.ChannelCount; + bitspersample = ADesc.QuantizationBits; + samplespersec = (ui32_t)ceil(ADesc.AudioSamplingRate.Quotient()); + avgbps = samplespersec * nchannels * ((bitspersample + 7) / 8); + blockalign = nchannels * ((bitspersample + 7) / 8); + cbsize = 0; + data_len = ASDCP::PCM::CalcFrameBufferSize(ADesc) * ADesc.ContainerDuration; +} + +// +void +ASDCP::Wav::SimpleWaveHeader::FillADesc(ASDCP::PCM::AudioDescriptor& ADesc, ASDCP::Rational PictureRate) const +{ + ADesc.SampleRate = PictureRate; + + ADesc.LinkedTrackID = 0; + ADesc.Locked = 0; + ADesc.ChannelCount = nchannels; + ADesc.AudioSamplingRate = Rational(samplespersec, 1); + ADesc.AvgBps = avgbps; + ADesc.BlockAlign = blockalign; + ADesc.QuantizationBits = bitspersample; + ui32_t FrameBufferSize = ASDCP::PCM::CalcFrameBufferSize(ADesc); + ADesc.ContainerDuration = data_len / FrameBufferSize; +} + + +// +ASDCP::Result_t +ASDCP::Wav::SimpleWaveHeader::WriteToFile(ASDCP::FileWriter& OutFile) const +{ + ui32_t write_count; + byte_t tmp_header[SimpleHeaderLength]; + byte_t* p = tmp_header; + + static ui32_t fmt_len = + sizeof(format) + + sizeof(nchannels) + + sizeof(samplespersec) + + sizeof(avgbps) + + sizeof(blockalign) + + sizeof(bitspersample) + + sizeof(cbsize); + + ui32_t RIFF_len = data_len + SimpleHeaderLength - 8; + + memcpy(p, &FCC_RIFF, sizeof(fourcc)); p += 4; + *((ui32_t*)p) = ASDCP_i32_LE(RIFF_len); p += 4; + memcpy(p, &FCC_WAVE, sizeof(fourcc)); p += 4; + memcpy(p, &FCC_fmt_, sizeof(fourcc)); p += 4; + *((ui32_t*)p) = ASDCP_i32_LE(fmt_len); p += 4; + *((ui16_t*)p) = ASDCP_i16_LE(format); p += 2; + *((ui16_t*)p) = ASDCP_i16_LE(nchannels); p += 2; + *((ui32_t*)p) = ASDCP_i32_LE(samplespersec); p += 4; + *((ui32_t*)p) = ASDCP_i32_LE(avgbps); p += 4; + *((ui16_t*)p) = ASDCP_i16_LE(blockalign); p += 2; + *((ui16_t*)p) = ASDCP_i16_LE(bitspersample); p += 2; + *((ui16_t*)p) = ASDCP_i16_LE(cbsize); p += 2; + memcpy(p, &FCC_data, sizeof(fourcc)); p += 4; + *((ui32_t*)p) = ASDCP_i32_LE(data_len); p += 4; + + return OutFile.Write(tmp_header, SimpleHeaderLength, &write_count); +} + +// +ASDCP::Result_t +ASDCP::Wav::SimpleWaveHeader::ReadFromFile(const ASDCP::FileReader& InFile, ui32_t* data_start) +{ + ui32_t read_count = 0; + ui32_t local_data_start = 0; + ASDCP::PCM::FrameBuffer TmpBuffer(MaxWavHeader); + + if ( data_start == 0 ) + data_start = &local_data_start; + + Result_t result = InFile.Read(TmpBuffer.Data(), TmpBuffer.Capacity(), &read_count); + + if ( ASDCP_SUCCESS(result) ) + result = ReadFromBuffer(TmpBuffer.RoData(), read_count, data_start); + + return result; +} + +ASDCP::Result_t +ASDCP::Wav::SimpleWaveHeader::ReadFromBuffer(const byte_t* buf, ui32_t buf_len, ui32_t* data_start) +{ + if ( buf_len < SimpleHeaderLength ) + return RESULT_SMALLBUF; + + *data_start = 0; + const byte_t* p = buf; + const byte_t* end_p = p + buf_len; + + fourcc test_RIFF(p); p += 4; + if ( test_RIFF != FCC_RIFF ) + { + DefaultLogSink().Error("Files does not begin with RIFF header\n"); + return RESULT_RAW_FORMAT; + } + + ui32_t RIFF_len = ASDCP_i32_LE(*(ui32_t*)p); p += 4; + + fourcc test_WAVE(p); p += 4; + if ( test_WAVE != FCC_WAVE ) + { + DefaultLogSink().Error("Files does not begin with WAVE header\n"); + return RESULT_RAW_FORMAT; + } + + fourcc test_fcc; + + while ( p < end_p ) + { + test_fcc = fourcc(p); p += 4; + ui32_t chunk_size = ASDCP_i32_LE(*(ui32_t*)p); p += 4; + + if ( test_fcc == FCC_data ) + { + if ( chunk_size > RIFF_len ) + { + DefaultLogSink().Error("Chunk size %lu larger than file: %lu\n", chunk_size, RIFF_len); + return RESULT_RAW_FORMAT; + } + + data_len = chunk_size; + *data_start = p - buf; + break; + } + + if ( test_fcc == FCC_fmt_ ) + { + ui16_t format = ASDCP_i16_LE(*(ui16_t*)p); p += 2; + + if ( format != 1 ) + { + DefaultLogSink().Error("Expecting uncompressed essence, got format type %hu\n", format); + return RESULT_RAW_FORMAT; + } + + nchannels = ASDCP_i16_LE(*(ui16_t*)p); p += 2; + samplespersec = ASDCP_i32_LE(*(ui32_t*)p); p += 4; + avgbps = ASDCP_i32_LE(*(ui32_t*)p); p += 4; + blockalign = ASDCP_i16_LE(*(ui16_t*)p); p += 2; + bitspersample = ASDCP_i16_LE(*(ui16_t*)p); p += 2; + p += chunk_size - 16; + } + else + { + p += chunk_size; + } + } + + if ( *data_start == 0 ) // can't have no data! + { + DefaultLogSink().Error("No data chunk found, file contains no essence\n"); + return RESULT_RAW_FORMAT; + } + + return RESULT_OK; +} + + + +// +// end Wav.cpp +// diff --git a/src/Wav.h b/src/Wav.h new file mode 100755 index 0000000..a04c92f --- /dev/null +++ b/src/Wav.h @@ -0,0 +1,95 @@ +/* +Copyright (c) 2005, 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 Wav.h + \version $Id$ + \brief Wave file common elements +*/ + +#ifndef _WAV_H_ +#define _WAV_H_ + +#include <FileIO.h> + +namespace ASDCP +{ +namespace Wav +{ + const ui32_t MaxWavHeader = 1024*32; // must find "data" within this space or no happy + + // + class fourcc + { + private: + byte_t data[4]; + + public: + inline fourcc() { memset( data, 0, 4 ); } + inline fourcc( const char* v ) { memcpy( this->data, v, 4 ); } + inline fourcc( const byte_t* v ) { memcpy( this->data, v, 4 ); } + inline fourcc& operator=(const fourcc & s) { memcpy( this->data, s.data, 4); return *this; } + inline bool operator==(const fourcc &rhs) { return memcmp(data, rhs.data, 4) == 0 ? true : false; } + inline bool operator!=(const fourcc &rhs) { return memcmp(data, rhs.data, 4) != 0 ? true : false; } + }; + + const fourcc FCC_RIFF("RIFF"); + const fourcc FCC_WAVE("WAVE"); + const fourcc FCC_fmt_("fmt "); + const fourcc FCC_data("data"); + + // + class SimpleWaveHeader + { + public: + ui16_t format; + ui16_t nchannels; + ui32_t samplespersec; + ui32_t avgbps; + ui16_t blockalign; + ui16_t bitspersample; + ui16_t cbsize; + ui32_t data_len; + + SimpleWaveHeader() : + format(0), nchannels(0), samplespersec(0), avgbps(0), + blockalign(0), bitspersample(0), cbsize(0), data_len(0) {} + + SimpleWaveHeader(ASDCP::PCM::AudioDescriptor& ADesc); + + Result_t ReadFromBuffer(const byte_t* buf, ui32_t buf_len, ui32_t* data_start); + Result_t ReadFromFile(const ASDCP::FileReader& InFile, ui32_t* data_start); + Result_t WriteToFile(ASDCP::FileWriter& OutFile) const; + void FillADesc(ASDCP::PCM::AudioDescriptor& ADesc, Rational PictureRate) const; + }; + +} // namespace Wav +} // namespace ASDCP + +#endif // _WAV_H_ + +// +// end Wav.h +// diff --git a/src/WavFileWriter.h b/src/WavFileWriter.h new file mode 100755 index 0000000..250cc20 --- /dev/null +++ b/src/WavFileWriter.h @@ -0,0 +1,120 @@ +/* +Copyright (c) 2005, 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 WavFileWriter.h + \version $Id$ + \brief demux and write PCM data to WAV file(s) +*/ + +#include <FileIO.h> +#include <Wav.h> +#include <list> + +#ifndef _WAVFILEWRITER_H_ +#define _WAVFILEWRITER_H_ + +// +class WavFileWriter +{ + ASDCP::PCM::AudioDescriptor m_ADesc; + std::list<ASDCP::FileWriter*> m_OutFile; + ASDCP_NO_COPY_CONSTRUCT(WavFileWriter); + + public: + WavFileWriter() {} + ~WavFileWriter() + { + while ( ! m_OutFile.empty() ) + { + delete m_OutFile.back(); + m_OutFile.pop_back(); + } + } + + ASDCP::Result_t + OpenWrite(ASDCP::PCM::AudioDescriptor &ADesc, const char* file_root, bool split) + { + ASDCP_TEST_NULL_STR(file_root); + char filename[256]; + ui32_t file_count = 1; + ASDCP::Result_t result = ASDCP::RESULT_OK; + m_ADesc = ADesc; + + if ( split ) + { + assert ( m_ADesc.ChannelCount % 2 == 0 ); // no support yet for stuffing odd files + file_count = m_ADesc.ChannelCount / 2; + m_ADesc.ChannelCount = 2; + } + + for ( ui32_t i = 0; i < file_count && ASDCP_SUCCESS(result); i++ ) + { + sprintf(filename, "%s_%lu.wav", file_root, (i + 1)); + m_OutFile.push_back(new ASDCP::FileWriter); + result = m_OutFile.back()->OpenWrite(filename); + + if ( ASDCP_SUCCESS(result) ) + { + ASDCP::Wav::SimpleWaveHeader Wav(m_ADesc); + result = Wav.WriteToFile(*(m_OutFile.back())); + } + } + + return result; + } + + ASDCP::Result_t + WriteFrame(ASDCP::PCM::FrameBuffer& FB) + { + ui32_t write_count; + ASDCP::Result_t result = ASDCP::RESULT_OK; + std::list<ASDCP::FileWriter*>::iterator fi; + assert(! m_OutFile.empty()); + + ui32_t sample_size = ASDCP::PCM::CalcSampleSize(m_ADesc); + const byte_t* p = FB.RoData(); + const byte_t* end_p = p + FB.Size(); + + while ( p < end_p ) + { + for ( fi = m_OutFile.begin(); fi != m_OutFile.end() && ASDCP_SUCCESS(result); fi++ ) + { + result = (*fi)->Write(p, sample_size, &write_count); + assert(write_count == sample_size); + p += sample_size; + } + } + + return result; + } +}; + + +#endif // _WAVFILEWRITER_H_ + +// +// end WavFileWriter.h +// diff --git a/src/asdcp-mem-test.cpp b/src/asdcp-mem-test.cpp new file mode 100755 index 0000000..b199abe --- /dev/null +++ b/src/asdcp-mem-test.cpp @@ -0,0 +1,152 @@ +/* +Copyright (c) 2004, 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 asdcp-mem-test.cpp + \version $Id$ + \brief AS-DCP frame buffer allocation test +*/ + + +#include <AS_DCP_internal.h> +#include <FortunaRNG.h> + +#include <iostream> +#include <assert.h> + +using namespace ASDCP; + +const ui32_t buf_size = 1024; +FortunaRNG RNG; + +// +int a() +{ + FrameBuffer FB; + FB.Capacity(buf_size); + assert(FB.Capacity() == buf_size); + RNG.FillRandom(FB.Data(), FB.Capacity()); + + return 0; +} + +// +int b() +{ + byte_t* buf = (byte_t*)malloc(buf_size); + assert(buf); + RNG.FillRandom(buf, buf_size); + + { + FrameBuffer FB; + FB.SetData(buf, buf_size); + assert(FB.Data() == buf); + assert(FB.Capacity() == buf_size); + // ~FB() is called... + } + + free(buf); + return 0; +} + +// +int c() +{ + byte_t* buf = (byte_t*)malloc(buf_size); + assert(buf); + RNG.FillRandom(buf, buf_size); + + { + FrameBuffer FB; + FB.SetData(buf, buf_size); + assert(FB.Data() == buf); + assert(FB.Capacity() == buf_size); + + FB.SetData(0,0); + assert(FB.Data() == 0); + assert(FB.Capacity() == 0); + + FB.Capacity(buf_size); + assert(FB.Capacity() == buf_size); + RNG.FillRandom(FB.Data(), FB.Capacity()); + // ~FB() is called... + } + + free(buf); + return 0; +} + +// +int d() +{ + mem_ptr<DataChunk> Chunk(new DataChunk(1024)); + + +#if 0 + + // MPEG2::Parser mPFile; + MPEG2::MXFReader mRFile; + Result_t result = mRFile.OpenRead("../test/write_test_mpeg.mxf"); + assert(ASDCP_SUCCESS(result)); + + // MPEG2::MXFWriter mWFile; + JP2K::CodestreamParser jPCFile; + JP2K::SequenceParser jPSFile; + JP2K::MXFReader jRFile; + JP2K::MXFWriter jWFile; + + PCM::WAVParser pPFile; + PCM::MXFReader pRFile; + PCM::MXFWriter pWFile; +#endif + return 0; +} + +// +int +main( int argc, char **argv ) +{ + ui32_t i = 0x00010000; + fputs("Watch your process monitor, memory usage should not change after startup.\n", stderr); + + while ( i-- ) + { + a(); + b(); + c(); + d(); + + if ( i && ( i % 1000 ) == 0 ) + fputc('.', stderr); + } + + fputc('\n', stderr); + return 0; +} + + +// +// end asdcp-mem-test.cpp +// diff --git a/src/asdcp-test.cpp b/src/asdcp-test.cpp new file mode 100755 index 0000000..c034626 --- /dev/null +++ b/src/asdcp-test.cpp @@ -0,0 +1,1366 @@ +/* +Copyright (c) 2003-2005, 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 asdcp-test.cpp + \version $Id$ + \brief AS-DCP file manipulation utility + + This program provides command line access to the major features of the asdcplib + library, and serves as a library unit test which provides the functionality of + the supported use cases. + + For more information about asdcplib, please refer to the header file AS_DCP.h + + WARNING: While the asdcplib library attempts to provide a complete and secure + implementation of the cryptographic features of the AS-DCP file formats, this + unit test program is NOT secure and is therefore NOT SUITABLE FOR USE in a + production environment without some modification. + + In particular, this program uses weak IV generation and externally generated + plaintext keys. These shortcomings exist because cryptographic-quality + random number generation and key management are outside the scope of the + asdcplib library. Developers using asdcplib for commercial implementations + claiming SMPTE conformance are expected to provide proper implementations of + these features. +*/ + +#include <iostream> +#include <assert.h> + +#include <FileIO.h> +#include <PCMParserList.h> +#include <WavFileWriter.h> +#include <hex_utils.h> +#include <AS_DCP_UUID.h> +#include <MXF.h> +#include <Metadata.h> + +using namespace ASDCP; + +const ui32_t FRAME_BUFFER_SIZE = 4*1024*1024; + +//------------------------------------------------------------------------------------------ +// +// command line option parser class + +static const char* PACKAGE = "asdcp-test"; // program name for messages +const ui32_t MAX_IN_FILES = 16; // maximum number of input files handled by + // the command option parser + +// local program identification info written to file headers +class MyInfo : public WriterInfo +{ +public: + MyInfo() + { + static byte_t default_ProductUUID_Data[UUIDlen] = + { 0x7d, 0x83, 0x6e, 0x16, 0x37, 0xc7, 0x4c, 0x22, + 0xb2, 0xe0, 0x46, 0xa7, 0x17, 0xe8, 0x4f, 0x42 }; + + memcpy(ProductUUID, default_ProductUUID_Data, UUIDlen); + CompanyName = "WidgetCo"; + ProductName = "asdcp-test"; + + char s_buf[128]; + sprintf(s_buf, "%lu.%lu.%lu", VERSION_MAJOR, VERSION_APIMINOR, VERSION_IMPMINOR); + ProductVersion = s_buf; + } +} s_MyInfo; + + +// Macros used to test command option data state. + +// True if a major mode has already been selected. +#define TEST_MAJOR_MODE() ( info_flag||create_flag||extract_flag||genkey_flag||genid_flag||gop_start_flag ) + +// Causes the caller to return if a major mode has already been selected, +// otherwise sets the given flag. +#define TEST_SET_MAJOR_MODE(f) if ( TEST_MAJOR_MODE() ) \ + { \ + fputs("Conflicting major mode, choose one of -(gcixG)).\n", stderr); \ + return; \ + } \ + (f) = true; + +// Increment the iterator, test for an additional non-option command line argument. +// Causes the caller to return if there are no remaining arguments or if the next +// argument begins with '-'. +#define TEST_EXTRA_ARG(i,c) if ( ++i >= argc || argv[(i)][0] == '-' ) \ + { \ + fprintf(stderr, "Argument not found for option %c.\n", (c)); \ + return; \ + } +// +void +banner(FILE* stream = stderr) +{ + fprintf(stream, "\n\ +%s (asdcplib %s)\n\n\ +Copyright (c) 2003-2005 John Hurst\n\n\ +asdcplib may be copied only under the terms of the license found at\n\ +the top of every file in the asdcplib distribution kit.\n\n\ +Specify the -h (help) option for further information about %s\n\n", + PACKAGE, ASDCP::Version(), PACKAGE); +} + +// +void +usage(FILE* stream = stderr) +{ + fprintf(stream, "\ +USAGE: %s [-i [-H, -n]|-c <filename> [-p <rate>, -e, -M, -R]|-x <root-name> [-m][-S]|-g|-u|-G|-V|-h]\n\ + [-k <key-string>] [-j <key-id-string>] [-f <start-frame-num>] [-d <duration>]\n\ + [-b <buf-size>] [-W] [-v [-s]] [<filename>, ...]\n\ +\n", PACKAGE); + + fprintf(stream, "\ +Major modes:\n\ + -i - show file info\n\ + -c <filename> - create AS-DCP file from input(s)\n\ + -x <root-name> - extract essence from AS-DCP file to named file(s)\n\ + -g - generate a random 16 byte value to stdout\n\ + -u - generate a random UUID value to stdout\n\ + -G - Perform GOP start lookup test on MPEG file\n\ + -V - show version\n\ + -h - show help\n\ +\n"); + + fprintf(stream, "\ +Security Options:\n\ + -j <key-id-str> - write key ID instead of creating a random value\n\ + -k <key-string> - use key for ciphertext operations\n\ + -e - encrypt MPEG or JP2K headers (default)\n\ + -E - do not encrypt MPEG or JP2K headers\n\ + -M - do not create HMAC values when writing\n\ + -m - verify HMAC values when reading\n\ +\n"); + + fprintf(stream, "\ +Read/Write Options:\n\ + -b <buf-size> - Size (in bytes) of the picture frame buffer, default: 2097152 (2MB)\n\ + -f <frame-num> - starting frame number, default 0\n\ + -d <duration> - number of frames to process, default all\n\ + -p <rate> - fps of picture when wrapping PCM or JP2K:, use one of [23|24|48], 24 is default\n\ + -R - Repeat the first frame over the entire file (picture essence only, requires -c, -d)\n\ + -S - Split Wave essence to stereo WAV files during extract (default = multichannel WAV)\n\ + -W - read input file only, do not write source file\n\ +\n"); + + fprintf(stream, "\ +Info Options:\n\ + -H - show MXF header metadata, used with option -i\n\ + -n - show index, used with option -i\n\ +\n\ +Other Options:\n\ + -s <number> - number of bytes of frame buffer to be dumped as hex to stderr (use with -v)\n\ + -v - verbose, show extra detail during run\n\ +\n\ + NOTES: o There is no option grouping, all options must be distinct arguments.\n\ + o All option arguments must be separated from the option by whitespace.\n\ + o An argument of \"23\" to the -p option will be interpreted as 23000/1001 fps.\n\ +\n"); +} + +// +// +class CommandOptions +{ + CommandOptions(); + +public: + bool error_flag; // true if the given options are in error or not complete + bool info_flag; // true if the file info mode was selected + bool create_flag; // true if the file create mode was selected + bool extract_flag; // true if the file extract mode was selected + bool genkey_flag; // true if we are to generate a new key value + bool genid_flag; // true if we are to generate a new UUID value + bool gop_start_flag; // true if we are to perform a GOP start lookup test + bool key_flag; // true if an encryption key was given + bool key_id_flag; // true if a key ID was given + bool encrypt_header_flag; // true if mpeg headers are to be encrypted + bool write_hmac; // true if HMAC values are to be generated and written + bool read_hmac; // true if HMAC values are to be validated + bool split_wav; // true if PCM is to be extracted to stereo WAV files + bool verbose_flag; // true if the verbose option was selected + ui32_t fb_dump_size; // number of bytes of frame buffer to dump + bool showindex_flag; // true if index is to be displayed + bool showheader_flag; // true if MXF file header is to be displayed + bool no_write_flag; // true if no output files are to be written + bool version_flag; // true if the version display option was selected + bool help_flag; // true if the help display option was selected + ui32_t start_frame; // frame number to begin processing + ui32_t duration; // number of frames to be processed + bool duration_flag; // true if duration argument given + bool do_repeat; // if true and -c -d, repeat first input frame + ui32_t picture_rate; // fps of picture when wrapping PCM + ui32_t fb_size; // size of picture frame buffer + ui32_t file_count; // number of elements in filenames[] + const char* file_root; // filename pre for files written by the extract mode + const char* out_file; // name of mxf file created by create mode + byte_t key_value[KeyLen]; // value of given encryption key (when key_flag is true) + byte_t key_id_value[KeyIDlen];// value of given key ID (when key_id_flag is true) + const char* filenames[MAX_IN_FILES]; // list of filenames to be processed + + // + Rational PictureRate() + { + if ( picture_rate == 23 ) return EditRate_23_98; + if ( picture_rate == 48 ) return EditRate_48; + return EditRate_24; + } + + // + const char* szPictureRate() + { + if ( picture_rate == 23 ) return "23.976"; + if ( picture_rate == 48 ) return "48"; + return "24"; + } + + // + CommandOptions(int argc, const char** argv) : + error_flag(true), info_flag(false), create_flag(false), + extract_flag(false), genkey_flag(false), genid_flag(false), gop_start_flag(false), + key_flag(false), encrypt_header_flag(true), write_hmac(true), read_hmac(false), split_wav(false), + verbose_flag(false), fb_dump_size(0), showindex_flag(false), showheader_flag(false), + no_write_flag(false), version_flag(false), help_flag(false), start_frame(0), + duration(0xffffffff), duration_flag(false), do_repeat(false), picture_rate(24), + fb_size(FRAME_BUFFER_SIZE), file_count(0), file_root(0), out_file(0) + { + memset(key_value, 0, KeyLen); + memset(key_id_value, 0, KeyIDlen); + + for ( int i = 1; i < argc; i++ ) + { + if ( argv[i][0] == '-' && isalpha(argv[i][1]) && argv[i][2] == 0 ) + { + switch ( argv[i][1] ) + { + case 'i': TEST_SET_MAJOR_MODE(info_flag); break; + case 'G': TEST_SET_MAJOR_MODE(gop_start_flag); break; + case 'W': no_write_flag = true; break; + case 'n': showindex_flag = true; break; + case 'H': showheader_flag = true; break; + case 'R': do_repeat = true; break; + case 'S': split_wav = true; break; + case 'V': version_flag = true; break; + case 'h': help_flag = true; break; + case 'v': verbose_flag = true; break; + case 'g': +#ifdef ASDCP_WITHOUT_OPENSSL + fputs("Program compiled without encryption support.\n", stderr); + return; +#else + genkey_flag = true; +#endif + break; + + case 'u': +#ifdef ASDCP_WITHOUT_OPENSSL + fputs("Program compiled without encryption support.\n", stderr); + return; +#else + genid_flag = true; +#endif + break; + + case 'e': encrypt_header_flag = true; break; + case 'E': encrypt_header_flag = false; break; + case 'M': write_hmac = false; break; + case 'm': read_hmac = true; break; + + case 'c': + TEST_SET_MAJOR_MODE(create_flag); + TEST_EXTRA_ARG(i, 'c'); + out_file = argv[i]; + break; + + case 'x': + TEST_SET_MAJOR_MODE(extract_flag); + TEST_EXTRA_ARG(i, 'x'); + file_root = argv[i]; + break; + + case 'k': key_flag = true; +#ifdef ASDCP_WITHOUT_OPENSSL + fputs("Program compiled without encryption support.\n", stderr); + return; +#else + TEST_EXTRA_ARG(i, 'k'); + { + ui32_t length; + hex2bin(argv[i], key_value, KeyLen, &length); + + if ( length != KeyLen ) + { + fprintf(stderr, "Unexpected key length: %lu, expecting %lu characters.\n", KeyLen, length); + return; + } + } +#endif + break; + + case 'j': key_id_flag = true; +#ifdef ASDCP_WITHOUT_OPENSSL + fputs("Program compiled without encryption support.\n", stderr); + return; +#else + TEST_EXTRA_ARG(i, 'j'); + { + ui32_t length; + hex2bin(argv[i], key_id_value, KeyIDlen, &length); + + if ( length != KeyIDlen ) + { + fprintf(stderr, "Unexpected key ID length: %lu, expecting %lu characters.\n", KeyIDlen, length); + return; + } + } +#endif + break; + + case 'f': + TEST_EXTRA_ARG(i, 'f'); + start_frame = atoi(argv[i]); // TODO: test for negative value, should use strtol() + break; + + case 'd': + TEST_EXTRA_ARG(i, 'd'); + duration_flag = true; + duration = atoi(argv[i]); // TODO: test for negative value, should use strtol() + break; + + case 'p': + TEST_EXTRA_ARG(i, 'p'); + picture_rate = atoi(argv[i]); + break; + + case 's': + TEST_EXTRA_ARG(i, 's'); + fb_dump_size = atoi(argv[i]); + break; + + case 'b': + TEST_EXTRA_ARG(i, 'b'); + fb_size = atoi(argv[i]); + + if ( verbose_flag ) + fprintf(stderr, "Frame Buffer size: %lu bytes.\n", fb_size); + + break; + + default: + fprintf(stderr, "Unrecognized option: %c\n", argv[i][1]); + return; + } + } + else + { + filenames[file_count++] = argv[i]; + + if ( file_count >= MAX_IN_FILES ) + { + fprintf(stderr, "Filename lists exceeds maximum list size: %lu\n", MAX_IN_FILES); + return; + } + } + } + + if ( TEST_MAJOR_MODE() ) + { + if ( ! genkey_flag && ! genid_flag && file_count == 0 ) + { + fputs("Option requires at least one filename argument.\n", stderr); + return; + } + } + + if ( ! TEST_MAJOR_MODE() && ! help_flag && ! version_flag ) + { + fputs("No operation selected (use one of -(gcixG) or -h for help).\n", stderr); + return; + } + + error_flag = false; + } +}; + +//------------------------------------------------------------------------------------------ +// MPEG2 essence +#if 0 +// Write a plaintext MPEG2 Video Elementary Stream to a plaintext ASDCP file +// Write a plaintext MPEG2 Video Elementary Stream to a ciphertext ASDCP file +// +Result_t +write_MPEG2_file(CommandOptions& Options) +{ + AESEncContext* Context = 0; + HMACContext* HMAC = 0; + MPEG2::FrameBuffer FrameBuffer(Options.fb_size); + MPEG2::Parser Parser; + MPEG2::MXFWriter Writer; + MPEG2::VideoDescriptor VDesc; + +#ifndef ASDCP_WITHOUT_OPENSSL + byte_t IV_buf[CBC_BLOCK_SIZE]; + FortunaRNG RNG; +#endif + + // set up essence parser + Result_t result = Parser.OpenRead(Options.filenames[0]); + + // set up MXF writer + if ( ASDCP_SUCCESS(result) ) + { + Parser.FillVideoDescriptor(VDesc); + + if ( Options.verbose_flag ) + { + fputs("MPEG-2 Pictures\n", stderr); + fputs("VideoDescriptor:\n", stderr); + fprintf(stderr, "Frame Buffer size: %lu\n", Options.fb_size); + MPEG2::VideoDescriptorDump(VDesc); + } + } + + if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag ) + { + WriterInfo Info = s_MyInfo; // fill in your favorite identifiers here +#ifndef ASDCP_WITHOUT_OPENSSL + GenRandomUUID(RNG, Info.AssetUUID); + + // configure encryption + if( Options.key_flag ) + { + GenRandomUUID(RNG, Info.ContextID); + Info.EncryptedEssence = true; + + if ( Options.key_id_flag ) + memcpy(Info.CryptographicKeyID, Options.key_id_value, KeyIDlen); + else + RNG.FillRandom(Info.CryptographicKeyID, KeyIDlen); + + Context = new AESEncContext; + result = Context->InitKey(Options.key_value); + + if ( ASDCP_SUCCESS(result) ) + result = Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE)); + + if ( ASDCP_SUCCESS(result) && Options.write_hmac ) + { + Info.UsesHMAC = true; + HMAC = new HMACContext; + result = HMAC->InitKey(Options.key_value); + } + } +#endif // ASDCP_WITHOUT_OPENSSL + + if ( ASDCP_SUCCESS(result) ) + result = Writer.OpenWrite(Options.out_file, Info, VDesc); + } + + if ( ASDCP_SUCCESS(result) ) + // loop through the frames + { + result = Parser.Reset(); + ui32_t duration = 0; + + while ( ASDCP_SUCCESS(result) && duration++ < Options.duration ) + { + if ( ! Options.do_repeat || duration == 1 ) + { + result = Parser.ReadFrame(FrameBuffer); + + if ( ASDCP_SUCCESS(result) ) + { + if ( Options.verbose_flag ) + FrameBuffer.Dump(stderr, Options.fb_dump_size); + + if ( Options.encrypt_header_flag ) + FrameBuffer.PlaintextOffset(0); + } + } + + if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag ) + { + result = Writer.WriteFrame(FrameBuffer, Context, HMAC); + +#ifndef ASDCP_WITHOUT_OPENSSL + // The Writer class will forward the last block of ciphertext + // to the encryption context for use as the IV for the next + // frame. If you want to use non-sequitur IV values, un-comment + // the following line of code. + // if ( ASDCP_SUCCESS(result) && Options.key_flag ) + // Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE)); +#endif + } + } + + if ( result == RESULT_ENDOFFILE ) + result = RESULT_OK; + } + + if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag ) + result = Writer.Finalize(); + + return result; +} +#endif + +// Read a plaintext MPEG2 Video Elementary Stream from a plaintext ASDCP file +// Read a plaintext MPEG2 Video Elementary Stream from a ciphertext ASDCP file +// Read a ciphertext MPEG2 Video Elementary Stream from a ciphertext ASDCP file +// +Result_t +read_MPEG2_file(CommandOptions& Options) +{ + AESDecContext* Context = 0; + HMACContext* HMAC = 0; + MPEG2::MXFReader Reader; + MPEG2::FrameBuffer FrameBuffer(Options.fb_size); + FileWriter OutFile; + ui32_t frame_count = 0; + + Result_t result = Reader.OpenRead(Options.filenames[0]); + + if ( ASDCP_SUCCESS(result) ) + { + MPEG2::VideoDescriptor VDesc; + Reader.FillVideoDescriptor(VDesc); + frame_count = VDesc.ContainerDuration; + + if ( Options.verbose_flag ) + { + fprintf(stderr, "Frame Buffer size: %lu\n", Options.fb_size); + MPEG2::VideoDescriptorDump(VDesc); + } + } + + if ( ASDCP_SUCCESS(result) ) + { + char filename[256]; + sprintf(filename, "%s.ves", Options.file_root); + result = OutFile.OpenWrite(filename); + } + + if ( ASDCP_SUCCESS(result) && Options.key_flag ) + { + Context = new AESDecContext; + result = Context->InitKey(Options.key_value); + + if ( ASDCP_SUCCESS(result) && Options.read_hmac ) + { + WriterInfo Info; + Reader.FillWriterInfo(Info); + + if ( Info.UsesHMAC ) + { + HMAC = new HMACContext; + result = HMAC->InitKey(Options.key_value); + } + else + { + fputs("File does not contain HMAC values, ignoring -m option.\n", stderr); + } + } + } + + ui32_t last_frame = Options.start_frame + ( Options.duration ? Options.duration : frame_count); + if ( last_frame > frame_count ) + last_frame = frame_count; + + for ( ui32_t i = Options.start_frame; ASDCP_SUCCESS(result) && i < last_frame; i++ ) + { + result = Reader.ReadFrame(i, FrameBuffer, Context, HMAC); + + if ( ASDCP_SUCCESS(result) ) + { + if ( Options.verbose_flag ) + FrameBuffer.Dump(stderr, Options.fb_dump_size); + + ui32_t write_count = 0; + result = OutFile.Write(FrameBuffer.Data(), FrameBuffer.Size(), &write_count); + } + } + + return result; +} + + +// +Result_t +gop_start_test(CommandOptions& Options) +{ + using namespace ASDCP::MPEG2; + + MXFReader Reader; + MPEG2::FrameBuffer FrameBuffer(Options.fb_size); + ui32_t frame_count = 0; + + Result_t result = Reader.OpenRead(Options.filenames[0]); + + if ( ASDCP_SUCCESS(result) ) + { + MPEG2::VideoDescriptor VDesc; + Reader.FillVideoDescriptor(VDesc); + frame_count = VDesc.ContainerDuration; + + if ( Options.verbose_flag ) + { + fprintf(stderr, "Frame Buffer size: %lu\n", Options.fb_size); + MPEG2::VideoDescriptorDump(VDesc); + } + } + + ui32_t last_frame = Options.start_frame + ( Options.duration ? Options.duration : frame_count); + if ( last_frame > frame_count ) + last_frame = frame_count; + + for ( ui32_t i = Options.start_frame; ASDCP_SUCCESS(result) && i < last_frame; i++ ) + { + result = Reader.ReadFrameGOPStart(i, FrameBuffer); + + if ( ASDCP_SUCCESS(result) ) + { + if ( Options.verbose_flag ) + FrameBuffer.Dump(stderr, Options.fb_dump_size); + + if ( FrameBuffer.FrameType() != FRAME_I ) + fprintf(stderr, "Expecting an I frame, got %c\n", FrameTypeChar(FrameBuffer.FrameType())); + + fprintf(stderr, "Requested frame %lu, got %lu\n", i, FrameBuffer.FrameNumber()); + } + } + + return result; +} + +#if 0 +//------------------------------------------------------------------------------------------ +// JPEG 2000 essence + +// Write one or more plaintext JPEG 2000 codestreams to a plaintext ASDCP file +// Write one or more plaintext JPEG 2000 codestreams to a ciphertext ASDCP file +// +Result_t +write_JP2K_file(CommandOptions& Options) +{ + AESEncContext* Context = 0; + HMACContext* HMAC = 0; + JP2K::MXFWriter Writer; + JP2K::FrameBuffer FrameBuffer(Options.fb_size); + JP2K::PictureDescriptor PDesc; + JP2K::SequenceParser Parser; + +#ifndef ASDCP_WITHOUT_OPENSSL + byte_t IV_buf[CBC_BLOCK_SIZE]; + FortunaRNG RNG; +#endif + + // set up essence parser + Result_t result = Parser.OpenRead(Options.filenames[0]); + + // set up MXF writer + if ( ASDCP_SUCCESS(result) ) + { + Parser.FillPictureDescriptor(PDesc); + PDesc.EditRate = Options.PictureRate(); + + if ( Options.verbose_flag ) + { + fprintf(stderr, "JPEG 2000 pictures\n"); + fputs("PictureDescriptor:\n", stderr); + fprintf(stderr, "Frame Buffer size: %lu\n", Options.fb_size); + JP2K::PictureDescriptorDump(PDesc); + } + } + + if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag ) + { + WriterInfo Info = s_MyInfo; // fill in your favorite identifiers here +#ifndef ASDCP_WITHOUT_OPENSSL + GenRandomUUID(RNG, Info.AssetUUID); + + // configure encryption + if( Options.key_flag ) + { + GenRandomUUID(RNG, Info.ContextID); + Info.EncryptedEssence = true; + + if ( Options.key_id_flag ) + memcpy(Info.CryptographicKeyID, Options.key_id_value, KeyIDlen); + else + RNG.FillRandom(Info.CryptographicKeyID, KeyIDlen); + + Context = new AESEncContext; + result = Context->InitKey(Options.key_value); + + if ( ASDCP_SUCCESS(result) ) + result = Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE)); + + if ( ASDCP_SUCCESS(result) && Options.write_hmac ) + { + Info.UsesHMAC = true; + HMAC = new HMACContext; + result = HMAC->InitKey(Options.key_value); + } + } +#endif // ASDCP_WITHOUT_OPENSSL + + if ( ASDCP_SUCCESS(result) ) + result = Writer.OpenWrite(Options.out_file, Info, PDesc); + } + + if ( ASDCP_SUCCESS(result) ) + { + ui32_t duration = 0; + result = Parser.Reset(); + + while ( ASDCP_SUCCESS(result) && duration++ < Options.duration ) + { + if ( ! Options.do_repeat || duration == 1 ) + { + result = Parser.ReadFrame(FrameBuffer); + + if ( ASDCP_SUCCESS(result) ) + { + if ( Options.verbose_flag ) + FrameBuffer.Dump(stderr, Options.fb_dump_size); + + if ( Options.encrypt_header_flag ) + FrameBuffer.PlaintextOffset(0); + } + } + + if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag ) + { + result = Writer.WriteFrame(FrameBuffer, Context, HMAC); +#ifndef ASDCP_WITHOUT_OPENSSL + // The Writer class will forward the last block of ciphertext + // to the encryption context for use as the IV for the next + // frame. If you want to use non-sequitur IV values, un-comment + // the following line of code. + // if ( ASDCP_SUCCESS(result) && Options.key_flag ) + // Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE)); +#endif + } + } + + if ( result == RESULT_ENDOFFILE ) + result = RESULT_OK; + } + + if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag ) + result = Writer.Finalize(); + + return result; +} +#endif + +// Read one or more plaintext JPEG 2000 codestreams from a plaintext ASDCP file +// Read one or more plaintext JPEG 2000 codestreams from a ciphertext ASDCP file +// Read one or more ciphertext JPEG 2000 codestreams from a ciphertext ASDCP file +// +Result_t +read_JP2K_file(CommandOptions& Options) +{ + AESDecContext* Context = 0; + HMACContext* HMAC = 0; + JP2K::MXFReader Reader; + JP2K::FrameBuffer FrameBuffer(Options.fb_size); + ui32_t frame_count = 0; + + Result_t result = Reader.OpenRead(Options.filenames[0]); + + if ( ASDCP_SUCCESS(result) ) + { + JP2K::PictureDescriptor PDesc; + Reader.FillPictureDescriptor(PDesc); + + frame_count = PDesc.ContainerDuration; + + if ( Options.verbose_flag ) + { + fprintf(stderr, "Frame Buffer size: %lu\n", Options.fb_size); + JP2K::PictureDescriptorDump(PDesc); + } + } + + if ( ASDCP_SUCCESS(result) && Options.key_flag ) + { + Context = new AESDecContext; + result = Context->InitKey(Options.key_value); + + if ( ASDCP_SUCCESS(result) && Options.read_hmac ) + { + WriterInfo Info; + Reader.FillWriterInfo(Info); + + if ( Info.UsesHMAC ) + { + HMAC = new HMACContext; + result = HMAC->InitKey(Options.key_value); + } + else + { + fputs("File does not contain HMAC values, ignoring -m option.\n", stderr); + } + } + } + + ui32_t last_frame = Options.start_frame + ( Options.duration ? Options.duration : frame_count); + if ( last_frame > frame_count ) + last_frame = frame_count; + + for ( ui32_t i = Options.start_frame; ASDCP_SUCCESS(result) && i < last_frame; i++ ) + { + result = Reader.ReadFrame(i, FrameBuffer, Context, HMAC); + + if ( ASDCP_SUCCESS(result) ) + { + FileWriter OutFile; + char filename[256]; + ui32_t write_count; + sprintf(filename, "%s%06lu.j2c", Options.file_root, i); + result = OutFile.OpenWrite(filename); + + if ( ASDCP_SUCCESS(result) ) + result = OutFile.Write(FrameBuffer.Data(), FrameBuffer.Size(), &write_count); + + if ( Options.verbose_flag ) + FrameBuffer.Dump(stderr, Options.fb_dump_size); + } + } + + return result; +} + +#if 0 +//------------------------------------------------------------------------------------------ +// PCM essence + + +// Write one or more plaintext PCM audio streams to a plaintext ASDCP file +// Write one or more plaintext PCM audio streams to a ciphertext ASDCP file +// +Result_t +write_PCM_file(CommandOptions& Options) +{ + AESEncContext* Context = 0; + HMACContext* HMAC = 0; + PCMParserList Parser; + PCM::MXFWriter Writer; + PCM::FrameBuffer FrameBuffer; + PCM::AudioDescriptor ADesc; + Rational PictureRate = Options.PictureRate(); + +#ifndef ASDCP_WITHOUT_OPENSSL + byte_t IV_buf[CBC_BLOCK_SIZE]; + FortunaRNG RNG; +#endif + + // set up essence parser + Result_t result = Parser.OpenRead(Options.file_count, Options.filenames, PictureRate); + + // set up MXF writer + if ( ASDCP_SUCCESS(result) ) + { + Parser.FillAudioDescriptor(ADesc); + + ADesc.SampleRate = PictureRate; + FrameBuffer.Capacity(PCM::CalcFrameBufferSize(ADesc)); + + if ( Options.verbose_flag ) + { + fprintf(stderr, "48Khz PCM Audio, %s fps (%lu spf)\n", + Options.szPictureRate(), + PCM::CalcSamplesPerFrame(ADesc)); + fputs("AudioDescriptor:\n", stderr); + PCM::AudioDescriptorDump(ADesc); + } + } + + if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag ) + { + WriterInfo Info = s_MyInfo; // fill in your favorite identifiers here +#ifndef ASDCP_WITHOUT_OPENSSL + GenRandomUUID(RNG, Info.AssetUUID); + + // configure encryption + if( Options.key_flag ) + { + GenRandomUUID(RNG, Info.ContextID); + Info.EncryptedEssence = true; + + if ( Options.key_id_flag ) + memcpy(Info.CryptographicKeyID, Options.key_id_value, KeyIDlen); + else + RNG.FillRandom(Info.CryptographicKeyID, KeyIDlen); + + Context = new AESEncContext; + result = Context->InitKey(Options.key_value); + + if ( ASDCP_SUCCESS(result) ) + result = Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE)); + + if ( ASDCP_SUCCESS(result) && Options.write_hmac ) + { + Info.UsesHMAC = true; + HMAC = new HMACContext; + result = HMAC->InitKey(Options.key_value); + } + } +#endif // ASDCP_WITHOUT_OPENSSL + + if ( ASDCP_SUCCESS(result) ) + result = Writer.OpenWrite(Options.out_file, Info, ADesc); + } + + if ( ASDCP_SUCCESS(result) ) + { + result = Parser.Reset(); + ui32_t duration = 0; + + while ( ASDCP_SUCCESS(result) && duration++ < Options.duration ) + { + result = Parser.ReadFrame(FrameBuffer); + + if ( ASDCP_SUCCESS(result) ) + { + if ( FrameBuffer.Size() != FrameBuffer.Capacity() ) + { + fprintf(stderr, "WARNING: Last frame read was short, PCM input is possibly not frame aligned.\n"); + fprintf(stderr, "Expecting %lu bytes, got %lu.\n", FrameBuffer.Capacity(), FrameBuffer.Size()); + result = RESULT_ENDOFFILE; + continue; + } + + if ( Options.verbose_flag ) + FrameBuffer.Dump(stderr, Options.fb_dump_size); + + if ( ! Options.no_write_flag ) + { + result = Writer.WriteFrame(FrameBuffer, Context, HMAC); + +#ifndef ASDCP_WITHOUT_OPENSSL + // The Writer class will forward the last block of ciphertext + // to the encryption context for use as the IV for the next + // frame. If you want to use non-sequitur IV values, un-comment + // the following line of code. + // if ( ASDCP_SUCCESS(result) && Options.key_flag ) + // Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE)); +#endif + } + } + } + + if ( result == RESULT_ENDOFFILE ) + result = RESULT_OK; + } + + if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag ) + result = Writer.Finalize(); + + return result; +} +#endif + +// Read one or more plaintext PCM audio streams from a plaintext ASDCP file +// Read one or more plaintext PCM audio streams from a ciphertext ASDCP file +// Read one or more ciphertext PCM audio streams from a ciphertext ASDCP file +// +Result_t +read_PCM_file(CommandOptions& Options) +{ + AESDecContext* Context = 0; + HMACContext* HMAC = 0; + PCM::MXFReader Reader; + PCM::FrameBuffer FrameBuffer; + WavFileWriter OutWave; + PCM::AudioDescriptor ADesc; + ui32_t last_frame = 0; + + Result_t result = Reader.OpenRead(Options.filenames[0]); + + if ( ASDCP_SUCCESS(result) ) + { + Reader.FillAudioDescriptor(ADesc); + + if ( ADesc.SampleRate != EditRate_23_98 + && ADesc.SampleRate != EditRate_24 + && ADesc.SampleRate != EditRate_48 ) + ADesc.SampleRate = Options.PictureRate(); + + FrameBuffer.Capacity(PCM::CalcFrameBufferSize(ADesc)); + + if ( Options.verbose_flag ) + PCM::AudioDescriptorDump(ADesc); + } + + if ( ASDCP_SUCCESS(result) ) + { + last_frame = ADesc.ContainerDuration; + + if ( Options.duration > 0 && Options.duration < last_frame ) + last_frame = Options.duration; + + if ( Options.start_frame > 0 ) + { + if ( Options.start_frame > ADesc.ContainerDuration ) + { + fprintf(stderr, "Start value greater than file duration.\n"); + return RESULT_FAIL; + } + + last_frame = xmin(Options.start_frame + last_frame, ADesc.ContainerDuration); + } + + ADesc.ContainerDuration = last_frame - Options.start_frame; + OutWave.OpenWrite(ADesc, Options.file_root, Options.split_wav); + } + + if ( ASDCP_SUCCESS(result) && Options.key_flag ) + { + Context = new AESDecContext; + result = Context->InitKey(Options.key_value); + + if ( ASDCP_SUCCESS(result) && Options.read_hmac ) + { + WriterInfo Info; + Reader.FillWriterInfo(Info); + + if ( Info.UsesHMAC ) + { + HMAC = new HMACContext; + result = HMAC->InitKey(Options.key_value); + } + else + { + fputs("File does not contain HMAC values, ignoring -m option.\n", stderr); + } + } + } + + for ( ui32_t i = Options.start_frame; ASDCP_SUCCESS(result) && i < last_frame; i++ ) + { + result = Reader.ReadFrame(i, FrameBuffer, Context, HMAC); + + if ( ASDCP_SUCCESS(result) ) + { + if ( Options.verbose_flag ) + FrameBuffer.Dump(stderr, Options.fb_dump_size); + + result = OutWave.WriteFrame(FrameBuffer); + } + } + + return result; +} + + +//------------------------------------------------------------------------------------------ +// + +// +// These classes wrap the irregular names in the asdcplib API +// so that I can use a template to simplify the implementation +// of show_file_info() + +class MyVideoDescriptor : public MPEG2::VideoDescriptor +{ + public: + void FillDescriptor(MPEG2::MXFReader& Reader) { + Reader.FillVideoDescriptor(*this); + } + + void Dump(FILE* stream) { + MPEG2::VideoDescriptorDump(*this, stream); + } +}; +#if 0 +class MyPictureDescriptor : public JP2K::PictureDescriptor +{ + public: + void FillDescriptor(JP2K::MXFReader& Reader) { + Reader.FillPictureDescriptor(*this); + } + + void Dump(FILE* stream) { + JP2K::PictureDescriptorDump(*this, stream); + } +}; + +class MyAudioDescriptor : public PCM::AudioDescriptor +{ + public: + void FillDescriptor(PCM::MXFReader& Reader) { + Reader.FillAudioDescriptor(*this); + } + + void Dump(FILE* stream) { + PCM::AudioDescriptorDump(*this, stream); + } +}; +#endif + +// MSVC didn't like the function template, so now it's a static class method +template<class ReaderT, class DescriptorT> +class FileInfoWrapper +{ +public: + static void file_info(CommandOptions& Options, FILE* stream = 0) + { + if ( stream == 0 ) + stream = stdout; + + if ( Options.verbose_flag || Options.showheader_flag ) + { + ReaderT Reader; + Result_t result = Reader.OpenRead(Options.filenames[0]); + + if ( ASDCP_SUCCESS(result) ) + { + if ( Options.showheader_flag ) + Reader.DumpHeaderMetadata(stream); + + WriterInfo WI; + Reader.FillWriterInfo(WI); + WriterInfoDump(WI, stream); + + DescriptorT Desc; + Desc.FillDescriptor(Reader); + Desc.Dump(stream); + + if ( Options.showindex_flag ) + Reader.DumpIndex(stream); + } + else if ( result == RESULT_FORMAT && Options.showheader_flag ) + { + Reader.DumpHeaderMetadata(stream); + } + } + } +}; + +// Read header metadata from an ASDCP file +// +Result_t +show_file_info(CommandOptions& Options) +{ + EssenceType_t EssenceType; + Result_t result = ASDCP::EssenceType(Options.filenames[0], EssenceType); + + if ( ASDCP_FAILURE(result) ) + return result; + + if ( EssenceType == ESS_MPEG2_VES ) + { + fputs("File essence type is MPEG2 video.\n", stdout); + FileInfoWrapper<ASDCP::MPEG2::MXFReader, MyVideoDescriptor>::file_info(Options); + } +#if 0 + else if ( EssenceType == ESS_PCM_24b_48k ) + { + fputs("File essence type is PCM audio.\n", stdout); + FileInfoWrapper<ASDCP::PCM::MXFReader, MyAudioDescriptor>::file_info(Options); + } + else if ( EssenceType == ESS_JPEG_2000 ) + { + fputs("File essence type is JPEG 2000 pictures.\n", stdout); + FileInfoWrapper<ASDCP::JP2K::MXFReader, MyPictureDescriptor>::file_info(Options); + } +#endif + else + { + fprintf(stderr, "File is not AS-DCP: %s\n", Options.filenames[0]); + FileReader Reader; + MXF::OPAtomHeader TestHeader; + + result = Reader.OpenRead(Options.filenames[0]); + + if ( ASDCP_SUCCESS(result) ) + result = TestHeader.InitFromFile(Reader); // test UL and OP + + if ( ASDCP_SUCCESS(result) ) + { + if ( MXF::Identification* ID = TestHeader.GetIdentification() ) + { + TestHeader.Dump(); + ID->Dump(); + // show OP + // show + } + else + { + fputs("File contains no Identification object.\n", stdout); + } + } + else + { + fputs("File is not MXF.\n", stdout); + } + } + + return result; +} + + +// +int +main(int argc, const char** argv) +{ + Result_t result = RESULT_OK; + CommandOptions Options(argc, argv); + + if ( Options.help_flag ) + { + usage(); + return 0; + } + + if ( Options.error_flag ) + return 3; + + if ( Options.version_flag ) + banner(); + + if ( Options.info_flag ) + { + result = show_file_info(Options); + } + else if ( Options.gop_start_flag ) + { + result = gop_start_test(Options); + } +#ifndef ASDCP_WITHOUT_OPENSSL + else if ( Options.genkey_flag ) + { + FortunaRNG RNG; + byte_t bin_buf[KeyLen]; + char str_buf[40]; + + RNG.FillRandom(bin_buf, KeyLen); + printf("%s\n", bin2hex(bin_buf, KeyLen, str_buf, 40)); + } + else if ( Options.genid_flag ) + { + FortunaRNG RNG; + byte_t bin_buf[KeyLen]; + char str_buf[40]; + + GenRandomUUID(RNG, bin_buf); + bin2hex(bin_buf, KeyLen, str_buf, 40); + printf("%s\n", hyphenate_UUID(str_buf, 40)); + } +#endif // ASDCP_WITHOUT_OPENSSL + else if ( Options.extract_flag ) + { + EssenceType_t EssenceType; + result = ASDCP::EssenceType(Options.filenames[0], EssenceType); +#ifdef SMPTE_LABELS + fprintf(stderr, "ATTENTION! Expecting SMPTE Universal Labels\n"); +#endif + if ( ASDCP_SUCCESS(result) ) + { + switch ( EssenceType ) + { + case ESS_MPEG2_VES: + result = read_MPEG2_file(Options); + break; + + case ESS_JPEG_2000: + result = read_JP2K_file(Options); + break; + + case ESS_PCM_24b_48k: + result = read_PCM_file(Options); + break; + + default: + fprintf(stderr, "%s: Unknown file type, not ASDCP essence.\n", Options.filenames[0]); + return 5; + } + } + } + else if ( Options.create_flag ) + { + if ( Options.do_repeat && ! Options.duration_flag ) + { + fputs("Option -R requires -d <duration>\n", stderr); + return RESULT_FAIL; + } + + EssenceType_t EssenceType; + result = ASDCP::RawEssenceType(Options.filenames[0], EssenceType); +#ifdef SMPTE_LABELS + fprintf(stderr, "ATTENTION! Writing SMPTE Universal Labels\n"); +#endif +#if 0 + if ( ASDCP_SUCCESS(result) ) + { + switch ( EssenceType ) + { + case ESS_MPEG2_VES: + result = write_MPEG2_file(Options); + break; + + case ESS_JPEG_2000: + result = write_JP2K_file(Options); + break; + + case ESS_PCM_24b_48k: + result = write_PCM_file(Options); + break; + + default: + fprintf(stderr, "%s: Unknown file type, not ASDCP-compatible essence.\n", + Options.filenames[0]); + return 5; + } + } +#endif + } + + if ( result != RESULT_OK ) + { + fputs("Program stopped on error.\n", stderr); + + if ( result != RESULT_FAIL ) + { + fputs(GetResultString(result), stderr); + fputc('\n', stderr); + } + + return 1; + } + + return 0; +} + + +// +// end asdcp-test.cpp +// diff --git a/src/asdcp-version.cpp b/src/asdcp-version.cpp new file mode 100755 index 0000000..148f69d --- /dev/null +++ b/src/asdcp-version.cpp @@ -0,0 +1,46 @@ +/* +Copyright (c) 2004, 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 asdcp-version.cpp + \version $Id$ + \brief AS-DCP library version annunciator +*/ + +#include <AS_DCP.h> +using namespace ASDCP; + +// +int +main() +{ + printf("%lu.%lu.%lu", VERSION_MAJOR, VERSION_APIMINOR, VERSION_IMPMINOR); + return 0; +} + + +// +// end asdcp-version.cpp +// diff --git a/src/h__Reader.cpp b/src/h__Reader.cpp new file mode 100755 index 0000000..1503102 --- /dev/null +++ b/src/h__Reader.cpp @@ -0,0 +1,346 @@ +/* +Copyright (c) 2004-2005, 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 h__Reader.cpp + \version $Id$ + \brief MXF file reader base class +*/ + +#include "AS_DCP_internal.h" +#include "KLV.h" +#include "MDD.h" +#include <assert.h> + +using namespace ASDCP; +using namespace ASDCP::MXF; + + +// GetMDObjectByPath() path to file's Identification metadata object +static const char* INFO_OBJECT_PATH = "Preface.Identifications.Identification.Identification"; +static const char* ASSET_ID_OBJECT_PATH = "Preface.ContentStorage.ContentStorage.Packages.Package.SourcePackage.PackageUID"; + + +ASDCP::h__Reader::h__Reader() : m_EssenceStart(0) +{ +} + +ASDCP::h__Reader::~h__Reader() +{ + Close(); +} + +void +ASDCP::h__Reader::Close() +{ + m_File.Close(); +} + +//------------------------------------------------------------------------------------------ +// + +// +Result_t +ASDCP::h__Reader::InitInfo(WriterInfo& Info) +{ + InterchangeObject* Object; + + // Identification + Result_t result = m_HeaderPart.GetMDObjectByType(OBJ_TYPE_ARGS(Identification), &Object); + + if( ASDCP_SUCCESS(result) ) + MD_to_WriterInfo((Identification*)Object, m_Info); + + // SourcePackage + if( ASDCP_SUCCESS(result) ) + result = m_HeaderPart.GetMDObjectByType(OBJ_TYPE_ARGS(SourcePackage), &Object); + + if( ASDCP_SUCCESS(result) ) + { + SourcePackage* SP = (SourcePackage*)Object; + memcpy(Info.AssetUUID, SP->PackageUID.Data() + 16, UUIDlen); + } + + // optional CryptographicContext + if( ASDCP_SUCCESS(result) ) + { + Result_t cr_result = m_HeaderPart.GetMDObjectByType(OBJ_TYPE_ARGS(CryptographicContext), &Object); + + if( ASDCP_SUCCESS(cr_result) ) + MD_to_CryptoInfo((CryptographicContext*)Object, m_Info); + } + + return result; +} + + +// standard method of opening an MXF file for read +Result_t +ASDCP::h__Reader::OpenMXFRead(const char* filename) +{ + Result_t result = m_File.OpenRead(filename); + + if ( ASDCP_SUCCESS(result) ) + result = m_HeaderPart.InitFromFile(m_File); + + // OP-Atom states that there will be either two or three partitions, + // one closed header and one closed footer with an optional body + ui32_t test_s = m_HeaderPart.m_RIP.PairArray.size(); + + if ( test_s < 2 || test_s > 3 ) + { + DefaultLogSink().Error("RIP count is not 2 or 3: %lu\n", test_s); + return RESULT_FORMAT; + } + + // it really OP-Atom? + // MDObject* OpPattern = GetMDObjectByType("OperationalPattern"); + // TODO: check the label + + // if this is a three partition file, go to the body + // partition and read off the partition pack + if ( test_s == 3 ) + { + DefaultLogSink().Error("RIP count is 3: must write code...\n"); + return RESULT_FORMAT; + } + // TODO: check the partition pack to make sure it is + // really a body with a single essence container + + m_EssenceStart = m_File.Tell(); + + return RESULT_OK; +} + + +// standard method of populating the in-memory index +Result_t +ASDCP::h__Reader::InitMXFIndex() +{ + if ( ! m_File.IsOpen() ) + return RESULT_INIT; + + Result_t result = m_File.Seek(m_HeaderPart.FooterPartition); + + if ( ASDCP_SUCCESS(result) ) + { + m_FooterPart.m_Lookup = &m_HeaderPart.m_Primer; + result = m_FooterPart.InitFromFile(m_File); + } + + return result; +} + + +// standard method of reading a plaintext or encrypted frame +Result_t +ASDCP::h__Reader::ReadEKLVPacket(ui32_t FrameNum, ASDCP::FrameBuffer& FrameBuf, + const byte_t* EssenceUL, AESDecContext* Ctx, HMACContext* HMAC) +{ + // look up frame index node + IndexTableSegment::IndexEntry TmpEntry; + + if ( ASDCP_FAILURE(m_FooterPart.Lookup(FrameNum, TmpEntry)) ) + { + DefaultLogSink().Error("Frame value out of range: %lu\n", FrameNum); + return RESULT_RANGE; + } + + // get frame position and go read the frame's key and length + ASDCP::KLVReader Reader; + ASDCP::fpos_t FilePosition = m_EssenceStart + TmpEntry.StreamOffset; + + Result_t result = m_File.Seek(FilePosition); + + if ( ASDCP_SUCCESS(result) ) + result = Reader.ReadKLFromFile(m_File); + + if ( ASDCP_FAILURE(result) ) + return result; + + UL Key(Reader.Key()); + UL InteropRef(CryptEssenceUL_Data); + UL SMPTERef(CryptEssenceUL_Data); + ui64_t PacketLength = Reader.Length(); + + if ( Key == InteropRef || Key == SMPTERef ) + { + if ( ! m_Info.EncryptedEssence ) + { + DefaultLogSink().Error("EKLV packet found, no Cryptographic Context in header.\n"); + return RESULT_FORMAT; + } + + // read encrypted triplet value into internal buffer + m_CtFrameBuf.Capacity(PacketLength); + ui32_t read_count; + result = m_File.Read(m_CtFrameBuf.Data(), PacketLength, &read_count); + + if ( ASDCP_FAILURE(result) ) + return result; + + if ( read_count != PacketLength ) + { + DefaultLogSink().Error("read length is smaller than EKLV packet length.\n"); + return RESULT_FORMAT; + } + + m_CtFrameBuf.Size(PacketLength); + + // should be const but mxflib::ReadBER is not + byte_t* ess_p = m_CtFrameBuf.Data(); + + // read context ID length + if ( ! read_test_BER(&ess_p, UUIDlen) ) + return RESULT_FORMAT; + + // test the context ID + if ( memcmp(ess_p, m_Info.ContextID, UUIDlen) != 0 ) + { + DefaultLogSink().Error("Packet's Cryptographic Context ID does not match the header.\n"); + return RESULT_FORMAT; + } + ess_p += UUIDlen; + + // read PlaintextOffset length + if ( ! read_test_BER(&ess_p, sizeof(ui64_t)) ) + return RESULT_FORMAT; + + ui32_t PlaintextOffset = (ui32_t)ASDCP_i64_BE(cp2i<ui64_t>(ess_p)); + ess_p += sizeof(ui64_t); + + // read essence UL length + if ( ! read_test_BER(&ess_p, klv_key_size) ) + return RESULT_FORMAT; + + // TODO: test essence UL + ess_p += klv_key_size; + + // read SourceLength length + if ( ! read_test_BER(&ess_p, sizeof(ui64_t)) ) + return RESULT_FORMAT; + + ui32_t SourceLength = (ui32_t)ASDCP_i64_BE(cp2i<ui64_t>(ess_p)); + ess_p += sizeof(ui64_t); + assert(SourceLength); + + if ( FrameBuf.Capacity() < SourceLength ) + { + DefaultLogSink().Error("FrameBuf.Capacity: %lu SourceLength: %lu\n", FrameBuf.Capacity(), SourceLength); + return RESULT_SMALLBUF; + } + + ui32_t esv_length = calc_esv_length(SourceLength, PlaintextOffset); + + // read ESV length + if ( ! read_test_BER(&ess_p, esv_length) ) + { + DefaultLogSink().Error("read_test_BER did not return %lu\n", esv_length); + return RESULT_FORMAT; + } + + ui32_t tmp_len = esv_length + (m_Info.UsesHMAC ? klv_intpack_size : 0); + + if ( PacketLength < tmp_len ) + { + DefaultLogSink().Error("Frame length is larger than EKLV packet length.\n"); + return RESULT_FORMAT; + } + + if ( Ctx ) + { + // wrap the pointer and length as a FrameBuffer for use by + // DecryptFrameBuffer() and TestValues() + FrameBuffer TmpWrapper; + TmpWrapper.SetData(ess_p, tmp_len); + TmpWrapper.Size(tmp_len); + TmpWrapper.SourceLength(SourceLength); + TmpWrapper.PlaintextOffset(PlaintextOffset); + + result = DecryptFrameBuffer(TmpWrapper, FrameBuf, Ctx); + FrameBuf.FrameNumber(FrameNum); + + // detect and test integrity pack + if ( ASDCP_SUCCESS(result) && m_Info.UsesHMAC && HMAC ) + { + IntegrityPack IntPack; + result = IntPack.TestValues(TmpWrapper, m_Info.AssetUUID, FrameNum + 1, HMAC); + } + } + else // return ciphertext to caller + { + if ( FrameBuf.Capacity() < tmp_len ) + return RESULT_SMALLBUF; + + memcpy(FrameBuf.Data(), ess_p, tmp_len); + FrameBuf.Size(tmp_len); + FrameBuf.SourceLength(SourceLength); + FrameBuf.PlaintextOffset(PlaintextOffset); + } + } + else if ( Key == EssenceUL ) + { // read plaintext frame + if ( FrameBuf.Capacity() < PacketLength ) + { + char intbuf[IntBufferLen]; + DefaultLogSink().Error("FrameBuf.Capacity: %lu FrameLength: %s\n", + FrameBuf.Capacity(), ui64sz(PacketLength, intbuf)); + return RESULT_SMALLBUF; + } + + // read the data into the supplied buffer + ui32_t read_count; + result = m_File.Read(FrameBuf.Data(), PacketLength, &read_count); + + if ( ASDCP_FAILURE(result) ) + return result; + + if ( read_count != PacketLength ) + { + char intbuf1[IntBufferLen]; + char intbuf2[IntBufferLen]; + DefaultLogSink().Error("read_count: %s != FrameLength: %s\n", + ui64sz(read_count, intbuf1), + ui64sz(PacketLength, intbuf2) ); + + return RESULT_READFAIL; + } + + FrameBuf.FrameNumber(FrameNum); + FrameBuf.Size(read_count); + } + else + { + DefaultLogSink().Error("Unexpected UL found.\n"); + return RESULT_FORMAT; + } + + return result; +} + + +// +// end h__Reader.cpp +// diff --git a/src/h__Writer.cpp b/src/h__Writer.cpp new file mode 100755 index 0000000..411f63e --- /dev/null +++ b/src/h__Writer.cpp @@ -0,0 +1,458 @@ +/* +Copyright (c) 2004-2005, 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 h__Writer.cpp + \version $Id$ + \brief MXF file writer base class +*/ + +#include "AS_DCP_internal.h" +#include "MemIO.h" +#include "Timecode.h" +#include <assert.h> + +using namespace mxflib; +using namespace ASDCP; + + +ASDCP::h__Writer::h__Writer() : m_FramesWritten(0), m_StreamOffset(0) +{ + init_mxf_types(); +} + +ASDCP::h__Writer::~h__Writer() +{ +} + +// standard method of writing the header of a new MXF file +Result_t +ASDCP::h__Writer::WriteMXFHeader(EssenceType_t EssenceType, ASDCP::Rational& EditRate, + ui32_t TCFrameRate, ui32_t BytesPerEditUnit) +{ + // write the stream metadata + m_Metadata = new Metadata(); + assert(m_Metadata); + assert(m_Metadata->m_Object); + + if ( m_Info.EncryptedEssence ) + { + UL DMSUL(CryptoFrameworkUL_Data); + m_Metadata->AddDMScheme(DMSUL); + } + + // Set the OP label + // If we are writing OP-Atom we write the header as OP1a initially as another process + // may try to read the file before it is complete and then it will NOT be a valid OP-Atom file + m_Metadata->SetOP(OP1aUL); + + // Build the Material Package + // DRAGONS: We should really try and determine the UMID type rather than cop-out! + UMID PackageUMID; + PackageUMID.MakeUMID(0x0d); // mixed type + +#if ASDCP_USE_MXFLIB + mxflib::Rational EditRate_; + EditRate_.Numerator = EditRate.Numerator; + EditRate_.Denominator = EditRate.Denominator; +#else +#define EditRate_ EditRate +#endif + + m_MaterialPackage = m_Metadata->AddMaterialPackage("AS-DCP Material Package", PackageUMID); + m_Metadata->SetPrimaryPackage(m_MaterialPackage); // This will be overwritten for OP-Atom + + TrackPtr MPTimecodeTrack = m_MaterialPackage->AddTimecodeTrack(EditRate_); + m_MPTimecode = MPTimecodeTrack->AddTimecodeComponent(TCFrameRate, 0, 0); + + TrackPtr FPTimecodeTrack = 0; + mxflib::UUID assetUUID(m_Info.AssetUUID); + + UMID EssenceUMID; + + switch ( EssenceType ) + { + case ESS_MPEG2_VES: + PackageUMID.MakeUMID(0x0f, assetUUID); + m_FilePackage = m_Metadata->AddFilePackage(1, MPEG_PACKAGE_LABEL, PackageUMID); + m_MPTrack = m_MaterialPackage->AddPictureTrack(EditRate_); + m_FPTrack = m_FilePackage->AddPictureTrack(0, EditRate_); + break; + + case ESS_JPEG_2000: + PackageUMID.MakeUMID(0x0f, assetUUID); + m_FilePackage = m_Metadata->AddFilePackage(1, JP2K_PACKAGE_LABEL, PackageUMID); + m_MPTrack = m_MaterialPackage->AddPictureTrack(EditRate_); + m_FPTrack = m_FilePackage->AddPictureTrack(0, EditRate_); + break; + + case ESS_PCM_24b_48k: + PackageUMID.MakeUMID(0x0f, assetUUID); + m_FilePackage = m_Metadata->AddFilePackage(1, PCM_PACKAGE_LABEL, PackageUMID); + m_MPTrack = m_MaterialPackage->AddSoundTrack(EditRate_); + m_FPTrack = m_FilePackage->AddSoundTrack(0, EditRate_); + break; + + default: return RESULT_RAW_ESS; + } + + // Add an essence element + FPTimecodeTrack = m_FilePackage->AddTimecodeTrack(EditRate_); + m_FPTimecode = FPTimecodeTrack->AddTimecodeComponent(TCFrameRate, 0/* NDF */, + tc_to_frames(TCFrameRate, 1, 0, 0, 0) ); + + // Add a single Component to this Track of the Material Package + m_MPClip = m_MPTrack->AddSourceClip(); + + // Add a single Component to this Track of the File Package + m_FPClip = m_FPTrack->AddSourceClip(); + const byte_t* SourceEssenceContainerLabel = 0; + + // Frame wrapping + if ( m_Info.EncryptedEssence ) + { + switch ( EssenceType ) + { + case ESS_MPEG2_VES: + SourceEssenceContainerLabel = WrappingUL_Data_MPEG2_VES; + break; + + case ESS_JPEG_2000: + SourceEssenceContainerLabel = WrappingUL_Data_JPEG_2000; + break; + + case ESS_PCM_24b_48k: + SourceEssenceContainerLabel = WrappingUL_Data_PCM_24b_48k; + break; + + default: + return RESULT_RAW_ESS; + } + } + + mem_ptr<UL> WrappingUL; + switch ( EssenceType ) + { + case ESS_MPEG2_VES: + WrappingUL = new UL(WrappingUL_Data_MPEG2_VES); // memchk TESTED + break; + + case ESS_JPEG_2000: + WrappingUL = new UL(WrappingUL_Data_JPEG_2000); // memchk TESTED + break; + + case ESS_PCM_24b_48k: + WrappingUL = new UL(WrappingUL_Data_PCM_24b_48k); // memchk TESTED + break; + + default: + return RESULT_RAW_ESS; + } + assert(!WrappingUL.empty()); + m_EssenceDescriptor->SetValue("EssenceContainer", DataChunk(klv_key_size, WrappingUL->GetValue())); + + // Write a File Descriptor only on the internally ref'ed Track + m_EssenceDescriptor->SetUint("LinkedTrackID", m_FPTrack->GetUint("TrackID")); + m_FilePackage->AddChild("Descriptor")->MakeLink(*m_EssenceDescriptor); + + UL CryptEssenceUL(WrappingUL_Data_Crypt); + + if ( m_Info.EncryptedEssence ) + { + m_Metadata->AddEssenceType(CryptEssenceUL); + } + else + { + UL GCUL(GCMulti_Data); + m_Metadata->AddEssenceType(GCUL); + m_Metadata->AddEssenceType(*WrappingUL); + } + + // Link the MP to the FP + m_MPClip->MakeLink(m_FPTrack, 0); + + // + // ** Write out the header ** + // + + m_HeaderPart = new Partition("OpenHeader"); + assert(m_HeaderPart); + m_HeaderPart->SetKAG(1); // Everything else can stay at default + m_HeaderPart->SetUint("BodySID", 1); + + m_HeaderPart->AddMetadata(m_Metadata); + + // Build an Ident set describing us and link into the metadata + MDObject* Ident = new MDObject("Identification"); + assert(Ident); + Ident->SetString("CompanyName", m_Info.CompanyName); + Ident->SetString("ProductName", m_Info.ProductName); + Ident->SetString("VersionString", m_Info.ProductVersion); + UUID ProductUID(m_Info.ProductUUID); + Ident->SetValue("ProductUID", DataChunk(UUIDlen, ProductUID.GetValue())); + + // TODO: get Oliver to show me how this works + // Ident->SetString("ToolkitVersion", ?); + + m_Metadata->UpdateGenerations(*Ident); + + if ( m_Info.EncryptedEssence ) + AddDMScrypt(m_FilePackage, m_Info, SourceEssenceContainerLabel); + + // Write the header partition + m_File->WritePartition(*m_HeaderPart, HeaderPadding); + + // set up the index + switch ( EssenceType ) + { + case ESS_MPEG2_VES: + case ESS_JPEG_2000: + m_IndexMan = new IndexManager(0, 0); + m_IndexMan->SetPosTableIndex(0, -1); + break; + + case ESS_PCM_24b_48k: + m_IndexMan = new IndexManager(0, BytesPerEditUnit); + break; + + case ESS_UNKNOWN: + return RESULT_INIT; + } + + m_IndexMan->SetBodySID(1); + m_IndexMan->SetIndexSID(129); + m_IndexMan->SetEditRate(EditRate_); + + return RESULT_OK; +} + +// standard method of writing a plaintext or encrypted frame +Result_t +ASDCP::h__Writer::WriteEKLVPacket(const ASDCP::FrameBuffer& FrameBuf, const byte_t* EssenceUL, + AESEncContext* Ctx, HMACContext* HMAC) +{ + Result_t result; + IntegrityPack IntPack; + + byte_t overhead[128]; + MemIOWriter Overhead(overhead, 128); + + if ( FrameBuf.Size() == 0 ) + { + DefaultLogSink().Error("Cannot write empty frame buffer\n"); + return RESULT_EMPTY_FB; + } + + if ( m_Info.EncryptedEssence ) + { + if ( ! Ctx ) + return RESULT_CRYPT_CTX; + + if ( m_Info.UsesHMAC && ! HMAC ) + return RESULT_HMAC_CTX; + + if ( FrameBuf.PlaintextOffset() > FrameBuf.Size() ) + return RESULT_LARGE_PTO; + + // encrypt the essence data (create encrypted source value) + result = EncryptFrameBuffer(FrameBuf, m_CtFrameBuf, Ctx); + + // create HMAC + if ( ASDCP_SUCCESS(result) && m_Info.UsesHMAC ) + result = IntPack.CalcValues(m_CtFrameBuf, m_Info.AssetUUID, m_FramesWritten + 1, HMAC); + + if ( ASDCP_SUCCESS(result) ) + { // write UL + Overhead.WriteRaw((byte_t*)CryptEssenceUL_Data, klv_key_size); + + // construct encrypted triplet header + ui32_t ETLength = klv_cryptinfo_size + m_CtFrameBuf.Size(); + + if ( m_Info.UsesHMAC ) + ETLength += klv_intpack_size; + else + ETLength += (klv_length_size * 3); // for empty intpack + + Overhead.WriteBER(ETLength, klv_length_size); // write encrypted triplet length + Overhead.WriteBER(UUIDlen, klv_length_size); // write ContextID length + Overhead.WriteRaw(m_Info.ContextID, UUIDlen); // write ContextID + Overhead.WriteBER(sizeof(ui64_t), klv_length_size); // write PlaintextOffset length + Overhead.WriteUi64BE(FrameBuf.PlaintextOffset()); // write PlaintextOffset + Overhead.WriteBER(klv_key_size, klv_length_size); // write essence UL length + Overhead.WriteRaw((byte_t*)EssenceUL, klv_key_size); // write the essence UL + Overhead.WriteBER(sizeof(ui64_t), klv_length_size); // write SourceLength length + Overhead.WriteUi64BE(FrameBuf.Size()); // write SourceLength + Overhead.WriteBER(m_CtFrameBuf.Size(), klv_length_size); // write ESV length + + result = m_File->Writev(Overhead.Data(), Overhead.Size()); + } + + if ( ASDCP_SUCCESS(result) ) + { + m_StreamOffset += Overhead.Size(); + // write encrypted source value + result = m_File->Writev((byte_t*)m_CtFrameBuf.RoData(), m_CtFrameBuf.Size()); + } + + if ( ASDCP_SUCCESS(result) ) + { + m_StreamOffset += m_CtFrameBuf.Size(); + + byte_t hmoverhead[512]; + MemIOWriter HMACOverhead(hmoverhead, 512); + + // write the HMAC + if ( m_Info.UsesHMAC ) + { + HMACOverhead.WriteRaw(IntPack.Data, klv_intpack_size); + } + else + { // we still need the var-pack length values if the intpack is empty + for ( ui32_t i = 0; i < 3 ; i++ ) + HMACOverhead.WriteBER(0, klv_length_size); + } + + // write HMAC + result = m_File->Writev(HMACOverhead.Data(), HMACOverhead.Size()); + m_StreamOffset += HMACOverhead.Size(); + } + } + else + { + Overhead.WriteRaw((byte_t*)EssenceUL, klv_key_size); + Overhead.WriteBER(FrameBuf.Size(), klv_length_size); + result = m_File->Writev(Overhead.Data(), Overhead.Size()); + + if ( ASDCP_SUCCESS(result) ) + result = m_File->Writev((byte_t*)FrameBuf.RoData(), FrameBuf.Size()); + + if ( ASDCP_SUCCESS(result) ) + m_StreamOffset += Overhead.Size() + FrameBuf.Size(); + } + + if ( ASDCP_SUCCESS(result) ) + result = m_File->Writev(); + + return result; +} + + +// standard method of writing the header and footer of a completed MXF file +// +Result_t +ASDCP::h__Writer::WriteMXFFooter(EssenceType_t EssenceType) +{ + // write the index + DataChunk IndexChunk; + ui32_t IndexSID = 0; + + // Find all essence container data sets so we can update "IndexSID" + MDObjectListPtr ECDataSets = 0; + MDObject* Ptr = (*m_Metadata)["ContentStorage"]; + if ( Ptr ) + Ptr = Ptr->GetLink(); + if ( Ptr ) + Ptr = (*Ptr)["EssenceContainerData"]; + if ( Ptr ) + ECDataSets = Ptr->ChildList("EssenceContainer"); + + // ** Handle full index tables next ** + // *********************************** + + // Make an index table containing all available entries + IndexTablePtr Index = m_IndexMan->MakeIndex(); + m_IndexMan->AddEntriesToIndex(Index); + + // Write the index table + Index->WriteIndex(IndexChunk); + + // Record the IndexSID for when the index is written + IndexSID = Index->IndexSID; + + // Update IndexSID in essence container data set + MDObjectList::iterator ECD_it = ECDataSets->begin(); + while(ECD_it != ECDataSets->end()) + { + if((*ECD_it)->GetLink()) + { + if((*ECD_it)->GetLink()->GetUint("BodySID") == m_IndexMan->GetBodySID()) + { + (*ECD_it)->GetLink()->SetUint("IndexSID", m_IndexMan->GetIndexSID()); + break; + } + } + + ECD_it++; + } + + // If we are writing OP-Atom this is the first place we can claim it + m_Metadata->SetOP(OPAtomUL); + + // Set top-level file package correctly for OP-Atom + m_Metadata->SetPrimaryPackage(m_FilePackage); + + m_Metadata->SetTime(); + m_MPTimecode->SetDuration(m_FramesWritten); + + m_MPClip->SetDuration(m_FramesWritten); + m_FPTimecode->SetDuration(m_FramesWritten); + m_FPClip->SetDuration(m_FramesWritten); + m_EssenceDescriptor->SetInt64("ContainerDuration", m_FramesWritten); + + // Turn the header or body partition into a footer + m_HeaderPart->ChangeType("CompleteFooter"); + m_HeaderPart->SetUint("IndexSID", IndexSID); + + // Make sure any new sets are linked in + m_HeaderPart->UpdateMetadata(m_Metadata); + + // Actually write the footer + m_File->WritePartitionWithIndex(*m_HeaderPart, &IndexChunk, false); + + // Add a RIP + m_File->WriteRIP(); + + // + // ** Update the header ** + // + // For generalized OPs update the value of "FooterPartition" in the header pack + // For OP-Atom re-write the entire header + // + ASDCP::fpos_t FooterPos = m_HeaderPart->GetUint64("FooterPartition"); + m_File->Seek(0); + + m_HeaderPart->ChangeType("ClosedCompleteHeader"); + m_HeaderPart->SetUint64("FooterPartition", FooterPos); + m_HeaderPart->SetUint64("BodySID", 1); + + m_File->ReWritePartition(*m_HeaderPart); + m_File->Close(); + + return RESULT_OK; +} + +// +// end h__Writer.cpp +// diff --git a/src/j2c-test.cpp b/src/j2c-test.cpp new file mode 100755 index 0000000..98c7cf4 --- /dev/null +++ b/src/j2c-test.cpp @@ -0,0 +1,74 @@ + +#include <AS_DCP.h> +#include <FileIO.h> +#include <JP2K.h> +using namespace ASDCP; +using namespace ASDCP::JP2K; + +// +int +main(int argc, const char** argv) +{ + ASDCP::JP2K::FrameBuffer FB; + Marker MyMarker; + + if ( argc < 2 ) + return 1; + + FB.Capacity(1024*1024*2); + CodestreamParser Parser; + + Result_t result = Parser.OpenReadFrame(argv[1], FB); + + if ( result != RESULT_OK ) + { + fputs("Program stopped on error.\n", stderr); + + if ( result != RESULT_FAIL ) + { + fputs(GetResultString(result), stderr); + fputc('\n', stderr); + } + + return 1; + } + + const byte_t* p = FB.RoData(); + const byte_t* end_p = p + FB.Size(); + + hexdump(p, 256, stderr); + + while ( p < end_p && ASDCP_SUCCESS(GetNextMarker(&p, MyMarker)) ) + { + MyMarker.Dump(); + + switch ( MyMarker.m_Type ) + { + case MRK_SOD: + p = end_p; + break; + + case MRK_SIZ: + { + Accessor::SIZ SIZ_(MyMarker); + hexdump(MyMarker.m_Data - 2, MyMarker.m_DataSize + 2, stderr); + SIZ_.Dump(); + } + break; + + case MRK_COM: + { + Accessor::COM COM_(MyMarker); + COM_.Dump(); + } + break; + } + } + + return 0; +} + + +// +// end jp2k-test.cpp +// diff --git a/src/klvwalk.cpp b/src/klvwalk.cpp new file mode 100755 index 0000000..3704f10 --- /dev/null +++ b/src/klvwalk.cpp @@ -0,0 +1,161 @@ +// +// klvwalk.cpp +// + +#include <AS_DCP.h> +#include <MXF.h> +#include <hex_utils.h> +#include <stdlib.h> +#include <string.h> +#include <ctype.h> +#include <assert.h> +#include <sys/types.h> +#include <sys/stat.h> + +using namespace ASDCP; + + +//------------------------------------------------------------------------------------------ +// + +// There is no header file thet defines this function. +// You just have to know it's there... +void set_debug_mode(bool info_mode, bool debug_mode); + + +int +main(int argc, char** argv) +{ + Result_t result = RESULT_OK; + bool read_mxf = false; + bool rewrite_mxf = false; + int arg_i = 1; + set_debug_mode(true, true); + + if ( strcmp(argv[1], "-r") == 0 ) + { + read_mxf = true; + arg_i++; + } + else if ( strcmp(argv[1], "-w") == 0 ) + { + rewrite_mxf = true; + arg_i++; + assert(argc - arg_i == 2); + } + + fprintf(stderr, "Opening file %s\n", argv[arg_i]); + + if ( read_mxf ) + { + ASDCP::FileReader Reader; + ASDCP::MXF::OPAtomHeader Header; + + result = Reader.OpenRead(argv[arg_i]); + + if ( ASDCP_SUCCESS(result) ) + result = Header.InitFromFile(Reader); + + // if ( ASDCP_SUCCESS(result) ) + Header.Dump(); + + if ( ASDCP_SUCCESS(result) ) + { + ASDCP::MXF::OPAtomIndexFooter Index; + result = Reader.Seek(Header.FooterPartition); + + if ( ASDCP_SUCCESS(result) ) + { + Index.m_Lookup = &Header.m_Primer; + result = Index.InitFromFile(Reader); + } + + if ( ASDCP_SUCCESS(result) ) + Index.Dump(); + } + } + else if ( rewrite_mxf ) + { + ASDCP::FileReader Reader; + ASDCP::FileWriter Writer; + ASDCP::MXF::OPAtomHeader Header; + ASDCP::MXF::OPAtomIndexFooter Index; + + result = Reader.OpenRead(argv[arg_i++]); + + if ( ASDCP_SUCCESS(result) ) + result = Header.InitFromFile(Reader); + + if ( ASDCP_SUCCESS(result) ) + result = Reader.Seek(Header.FooterPartition); + + if ( ASDCP_SUCCESS(result) ) + result = Index.InitFromFile(Reader); + + Header.m_Primer.ClearTagList(); + + if ( ASDCP_SUCCESS(result) ) + result = Writer.OpenWrite(argv[arg_i]); + + if ( ASDCP_SUCCESS(result) ) + result = Header.WriteToFile(Writer); + +// if ( ASDCP_SUCCESS(result) ) +// result = Index.WriteToFile(Writer); + + // essence packets + + // index + + // RIP + } + else // dump klv + { + ASDCP::FileReader Reader; + KLVFilePacket KP; + + result = Reader.OpenRead(argv[arg_i]); + + if ( ASDCP_SUCCESS(result) ) + result = KP.InitFromFile(Reader); + + while ( ASDCP_SUCCESS(result) ) + { + KP.Dump(stderr, true); + result = KP.InitFromFile(Reader); + } + + if( result == RESULT_ENDOFFILE ) + result = RESULT_OK; + } + + if ( result != RESULT_OK ) + { + fputs("Program stopped on error.\n", stderr); + + if ( result != RESULT_FAIL ) + { + fputs(GetResultString(result), stderr); + fputc('\n', stderr); + } + + return 1; + } + + return 0; +} + + +// +// end klvwalk.cpp +// + + + + + + + + + + |
