From 8095eaa320551b6795d0368c0ad0c227a3167caa Mon Sep 17 00:00:00 2001 From: jhurst Date: Tue, 20 Dec 2005 01:55:40 +0000 Subject: [PATCH] wheee! --- AS_DCP_UUID.cpp | 125 +++ AS_DCP_UUID.h | 53 ++ AS_DCP_system.h | 190 +++++ DirScanner.cpp | 189 +++++ DirScanner.h | 91 +++ FileIO.cpp | 555 +++++++++++++ FileIO.h | 143 ++++ FortunaRNG.cpp | 207 +++++ FortunaRNG.h | 72 ++ Identifier.h | 178 +++++ Index.h | 75 ++ MemIO.h | 236 ++++++ Mutex.h | 86 ++ README | 326 ++++++++ Timecode.cpp | 67 ++ Timecode.h | 48 ++ hex_utils.h | 83 ++ src/AS_DCP.cpp | 509 ++++++++++++ src/AS_DCP.h | 1088 +++++++++++++++++++++++++ src/AS_DCP_AES.cpp | 462 +++++++++++ src/AS_DCP_JP2K.cpp | 594 ++++++++++++++ src/AS_DCP_MPEG2.cpp | 529 +++++++++++++ src/AS_DCP_MXF.cpp | 598 ++++++++++++++ src/AS_DCP_PCM.cpp | 475 +++++++++++ src/AS_DCP_internal.h | 299 +++++++ src/Index.cpp | 203 +++++ src/JP2K.cpp | 222 ++++++ src/JP2K.h | 163 ++++ src/JP2K_Codestream_Parser.cpp | 193 +++++ src/JP2K_Sequence_Parser.cpp | 244 ++++++ src/KLV.cpp | 270 +++++++ src/KLV.h | 151 ++++ src/MDD.h | 1249 +++++++++++++++++++++++++++++ src/MPEG.cpp | 267 +++++++ src/MPEG.h | 237 ++++++ src/MPEG2_Parser.cpp | 581 ++++++++++++++ src/MXF.cpp | 1020 ++++++++++++++++++++++++ src/MXF.h | 307 +++++++ src/MXFTypes.cpp | 284 +++++++ src/MXFTypes.h | 285 +++++++ src/Metadata.cpp | 781 ++++++++++++++++++ src/Metadata.h | 389 +++++++++ src/PCMParserList.cpp | 213 +++++ src/PCMParserList.h | 86 ++ src/PCM_Parser.cpp | 214 +++++ src/Wav.cpp | 207 +++++ src/Wav.h | 95 +++ src/WavFileWriter.h | 120 +++ src/asdcp-mem-test.cpp | 152 ++++ src/asdcp-test.cpp | 1366 ++++++++++++++++++++++++++++++++ src/asdcp-version.cpp | 46 ++ src/h__Reader.cpp | 346 ++++++++ src/h__Writer.cpp | 458 +++++++++++ src/j2c-test.cpp | 74 ++ src/klvwalk.cpp | 161 ++++ 55 files changed, 17662 insertions(+) create mode 100755 AS_DCP_UUID.cpp create mode 100755 AS_DCP_UUID.h create mode 100755 AS_DCP_system.h create mode 100755 DirScanner.cpp create mode 100755 DirScanner.h create mode 100755 FileIO.cpp create mode 100755 FileIO.h create mode 100755 FortunaRNG.cpp create mode 100755 FortunaRNG.h create mode 100755 Identifier.h create mode 100755 Index.h create mode 100755 MemIO.h create mode 100755 Mutex.h create mode 100755 README create mode 100755 Timecode.cpp create mode 100755 Timecode.h create mode 100755 hex_utils.h create mode 100755 src/AS_DCP.cpp create mode 100755 src/AS_DCP.h create mode 100755 src/AS_DCP_AES.cpp create mode 100755 src/AS_DCP_JP2K.cpp create mode 100755 src/AS_DCP_MPEG2.cpp create mode 100755 src/AS_DCP_MXF.cpp create mode 100755 src/AS_DCP_PCM.cpp create mode 100755 src/AS_DCP_internal.h create mode 100755 src/Index.cpp create mode 100755 src/JP2K.cpp create mode 100755 src/JP2K.h create mode 100755 src/JP2K_Codestream_Parser.cpp create mode 100755 src/JP2K_Sequence_Parser.cpp create mode 100755 src/KLV.cpp create mode 100755 src/KLV.h create mode 100755 src/MDD.h create mode 100755 src/MPEG.cpp create mode 100755 src/MPEG.h create mode 100755 src/MPEG2_Parser.cpp create mode 100755 src/MXF.cpp create mode 100755 src/MXF.h create mode 100755 src/MXFTypes.cpp create mode 100755 src/MXFTypes.h create mode 100755 src/Metadata.cpp create mode 100755 src/Metadata.h create mode 100755 src/PCMParserList.cpp create mode 100755 src/PCMParserList.h create mode 100755 src/PCM_Parser.cpp create mode 100755 src/Wav.cpp create mode 100755 src/Wav.h create mode 100755 src/WavFileWriter.h create mode 100755 src/asdcp-mem-test.cpp create mode 100755 src/asdcp-test.cpp create mode 100755 src/asdcp-version.cpp create mode 100755 src/h__Reader.cpp create mode 100755 src/h__Writer.cpp create mode 100755 src/j2c-test.cpp create mode 100755 src/klvwalk.cpp diff --git a/AS_DCP_UUID.cpp b/AS_DCP_UUID.cpp new file mode 100755 index 0000000..39daeb0 --- /dev/null +++ b/AS_DCP_UUID.cpp @@ -0,0 +1,125 @@ +/* +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 AS_DCP_UUID.cpp + \version $Id$ + \brief Miscellaneous timecode functions +*/ + +#include +#include + + +static FortunaRNG s_RNG; + +using namespace ASDCP; + + +// given a 16 byte buffer, this subroutine uses the given RNG to generate a +// random value, and sets the necessary bits to turn the value into a proper UUID per +// sec. 4.4 of http://www.ietf.org/internet-drafts/draft-mealling-uuid-urn-03.txt +// Parts of that document are reprinted here for your convenience: +// +// 0 1 2 3 +// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | time_low | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | time_mid | time_hi_and_version | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// |clk_seq_hi_res | clk_seq_low | node (0-1) | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | node (2-5) | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// +// Msb0 Msb1 Msb2 Msb3 Version Description +// +// 0 1 0 0 4 The randomly or pseudo- +// randomly generated version +// specified in this document. +// +// o Set the two most significant bits (bits six and seven) of the +// clock_seq_hi_and_reserved to zero and one, respectively. +// o Set the four most significant bits (bits 12 through 15) of the +// time_hi_and_version field to the four-bit version number from +// Section 4.1.3. +// o Set all the other bits to randomly (or pseudo-randomly) chosen +// values. +// + +// fill buffer with random bits and set appropriate UUID flags +void +ASDCP::GenRandomUUID(FortunaRNG& RNG, byte_t* buf) +{ + assert(buf); + RNG.FillRandom(buf, UUIDlen); + buf[6] &= 0x0f; // clear bits 4-7 + buf[6] |= 0x40; // set UUID version + buf[8] &= 0x3f; // clear bits 6&7 + buf[8] |= 0x80; // set bit 7 +} + +// add UUID hyphens to 32 character hexadecimal string +char* +ASDCP::hyphenate_UUID(char* str_buf, ui32_t buf_len) +{ + ui32_t i, j, k; + + assert(str_buf); + assert (buf_len > 36); + + // shift the node id + for ( k = 19, i = 12; i > 0; i-- ) + str_buf[k+i+4] = str_buf[k+i]; + + // shift the time (mid+hi+clk) + for ( k = 15, j = 3; k > 6; k -= 4, j-- ) + { + for ( i = 4; i > 0; i-- ) + str_buf[k+i+j] = str_buf[k+i]; + } + + // add in the hyphens and trainling null + for ( i = 8; i < 24; i += 5 ) + str_buf[i] = '-'; + + str_buf[36] = 0; + return str_buf; +} + + +// +void +ASDCP::MakeUUID(byte_t* buf) +{ + GenRandomUUID(s_RNG, buf); +} + + + +// +// end AS_DCP_UUID.cpp +// diff --git a/AS_DCP_UUID.h b/AS_DCP_UUID.h new file mode 100755 index 0000000..342224e --- /dev/null +++ b/AS_DCP_UUID.h @@ -0,0 +1,53 @@ +/* +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 AS_DCP_UUID.h + \version $Id$ + \brief UUID generation +*/ + +#ifndef _AS_DCP_UUID_H_ +#define _AS_DCP_UUID_H_ + +#include +#include + +namespace ASDCP +{ + void MakeUUID(byte_t* buf); + + void GenRandomUUID(FortunaRNG& RNG, byte_t* buf); + + // add UUID hyphens to 32 character hexadecimal string + char* hyphenate_UUID(char* str_buf, ui32_t buf_len); + +} // namespace ASDCP + +#endif //_AS_DCP_UUID_H_ + +// +// end Timecode.h +// diff --git a/AS_DCP_system.h b/AS_DCP_system.h new file mode 100755 index 0000000..227a3a8 --- /dev/null +++ b/AS_DCP_system.h @@ -0,0 +1,190 @@ +/* +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_system.h + \version $Id$ + \brief AS-DCP portable types and byte swapping +*/ + + +#ifndef _AS_DCP_SYSTEM_H_ +#define _AS_DCP_SYSTEM_H_ + +#ifdef __APPLE__ +#define ASDCP_BIG_ENDIAN +#endif + +// 64 bit types are not used in the public interface +#ifdef WIN32 +#define WIN32_LEAN_AND_MEAN +#define VC_EXTRALEAN +#include +#pragma warning(disable:4786) // Ignore "identifer > 255 characters" warning + +#ifndef ASDCP_NO_BASE_TYPES +typedef unsigned __int64 ui64_t; +typedef __int64 i64_t; +#define i64_C(c) (i64_t)(c) +#define ui64_C(c) (ui64_t)(c) +#endif + +#else // WIN32 + +#ifndef ASDCP_NO_BASE_TYPES +typedef unsigned long long ui64_t; +typedef long long i64_t; +#define i64_C(c) c##LL +#define ui64_C(c) c##ULL +#endif + +#endif // WIN32 + +#include +#include + +namespace ASDCP { + + inline ui16_t Swap2(ui16_t i) + { + return ( (i << 8) | (( i & 0xff00) >> 8) ); + } + + inline ui32_t Swap4(ui32_t i) + { + return + ( (i & 0x000000ffUL) << 24 ) | + ( (i & 0xff000000UL) >> 24 ) | + ( (i & 0x0000ff00UL) << 8 ) | + ( (i & 0x00ff0000UL) >> 8 ); + } + + inline ui64_t Swap8(ui64_t i) + { + return + ( (i & ui64_C(0x00000000000000FF)) << 56 ) | + ( (i & ui64_C(0xFF00000000000000)) >> 56 ) | + ( (i & ui64_C(0x000000000000FF00)) << 40 ) | + ( (i & ui64_C(0x00FF000000000000)) >> 40 ) | + ( (i & ui64_C(0x0000000000FF0000)) << 24 ) | + ( (i & ui64_C(0x0000FF0000000000)) >> 24 ) | + ( (i & ui64_C(0x00000000FF000000)) << 8 ) | + ( (i & ui64_C(0x000000FF00000000)) >> 8 ); + } + + // + template + inline T xmin(T lhs, T rhs) + { + return (lhs < rhs) ? lhs : rhs; + } + + // + template + inline T xmax(T lhs, T rhs) + { + return (lhs > rhs) ? lhs : rhs; + } + + // read an integer from byte-structured storage + template + inline T cp2i(const byte_t* p) { return *(T*)p; } + + // write an integer to byte-structured storage + template + inline void i2p(T i, byte_t* p) { *(T*)p = i; } + +#ifdef ASDCP_BIG_ENDIAN +#define ASDCP_i16_LE(i) ASDCP::Swap2(i) +#define ASDCP_i32_LE(i) ASDCP::Swap4(i) +#define ASDCP_i64_LE(i) ASDCP::Swap8(i) +#define ASDCP_i16_BE(i) (i) +#define ASDCP_i32_BE(i) (i) +#define ASDCP_i64_BE(i) (i) +#else +#define ASDCP_i16_LE(i) (i) +#define ASDCP_i32_LE(i) (i) +#define ASDCP_i64_LE(i) (i) +#define ASDCP_i16_BE(i) ASDCP::Swap2(i) +#define ASDCP_i32_BE(i) ASDCP::Swap4(i) +#define ASDCP_i64_BE(i) ASDCP::Swap8(i) +#endif // ASDCP_BIG_ENDIAN + + +// 64 bit integer/text conversion +// + const ui32_t IntBufferLen = 32; + + inline i64_t atoi64(const char *str) { +#ifdef WIN32 + return _atoi64(str); +#else + return strtoll(str, NULL, 10); +#endif + } + + inline const char* i64sz(i64_t i, char* buf) + { + assert(buf); +#ifdef WIN32 + sprintf(buf, "%I64d", i); +#else + sprintf(buf, "%lld", i); +#endif + return buf; + } + + inline const char* ui64sz(ui64_t i, char* buf) + { + assert(buf); +#ifdef WIN32 + sprintf(buf, "%I64u", i); +#else + sprintf(buf, "%llu", i); +#endif + return buf; + } + + inline const char* i64szx(i64_t i, ui32_t digits, char* buf) + { + assert(buf); + if ( digits > 30 ) digits = 30; +#ifdef WIN32 + sprintf(buf,"%0*I64x", digits, i); +#else + sprintf(buf,"%0*llx", digits, i); +#endif + return buf; + } + +} // namespace ASDCP + + + +#endif // _AS_DCP_SYSTEM_H_ + +// +// AS_DCP_system.h +// diff --git a/DirScanner.cpp b/DirScanner.cpp new file mode 100755 index 0000000..6c3c40a --- /dev/null +++ b/DirScanner.cpp @@ -0,0 +1,189 @@ +/* +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 DirScanner.cpp + \version $Id$ + \brief Cross-platform, simple non-recursive directory scanner +*/ + +#include +#include +#include + +// Win32 directory scanner +// +#ifdef WIN32 + +// +// +ASDCP::Result_t +DirScanner::Open(const char* filename) +{ + ASDCP_TEST_NULL_STR(filename); + + // we need to append a '*' to read the entire directory + ui32_t fn_len = strlen(filename); + char* tmp_file = (char*)malloc(fn_len + 8); + + if ( tmp_file == 0 ) + return ASDCP::RESULT_ALLOC; + + strcpy(tmp_file, filename); + char* p = &tmp_file[fn_len] - 1; + + if ( *p != '/' && *p != '\\' ) + { + p++; + *p++ = '/'; + } + + *p++ = '*'; + *p = 0; + // whew... + + m_Handle = _findfirsti64(tmp_file, &m_FileInfo); + ASDCP::Result_t result = ASDCP::RESULT_OK; + + if ( m_Handle == -1 ) + result = ASDCP::RESULT_NOT_FOUND; + + return result; +} + + +// +// +ASDCP::Result_t +DirScanner::Close() +{ + if ( m_Handle == -1 ) + return ASDCP::RESULT_FILEOPEN; + + if ( _findclose((long)m_Handle) == -1 ) + return ASDCP::RESULT_FAIL; + + m_Handle = -1; + return ASDCP::RESULT_OK; +} + + +// This sets filename param to the same per-instance buffer every time, so +// the value will change on the next call +ASDCP::Result_t +DirScanner::GetNext(char* filename) +{ + ASDCP_TEST_NULL(filename); + + if ( m_Handle == -1 ) + return ASDCP::RESULT_FILEOPEN; + + if ( m_FileInfo.name[0] == '\0' ) + return ASDCP::RESULT_ENDOFFILE; + + strncpy(filename, m_FileInfo.name, ASDCP_MAX_PATH); + ASDCP::Result_t result = ASDCP::RESULT_OK; + + if ( _findnexti64((long)m_Handle, &m_FileInfo) == -1 ) + { + m_FileInfo.name[0] = '\0'; + + if ( errno != ENOENT ) + result = ASDCP::RESULT_FAIL; + } + + return result; +} + + +#else // WIN32 + +// POSIX directory scanner + +// +ASDCP::Result_t +DirScanner::Open(const char* filename) +{ + ASDCP_TEST_NULL_STR(filename); + + ASDCP::Result_t result = ASDCP::RESULT_OK; + + if ( ( m_Handle = opendir(filename) ) == NULL ) + { + if ( errno == ENOENT ) + result = ASDCP::RESULT_ENDOFFILE; + + else + result = ASDCP::RESULT_FAIL; + } + + return result; +} + + +// +ASDCP::Result_t +DirScanner::Close() +{ + if ( m_Handle == NULL ) + return ASDCP::RESULT_FILEOPEN; + + if ( closedir(m_Handle) == -1 ) + return ASDCP::RESULT_FAIL; + + m_Handle = NULL; + return ASDCP::RESULT_OK; +} + + +// +ASDCP::Result_t +DirScanner::GetNext(char* filename) +{ + ASDCP_TEST_NULL(filename); + + if ( m_Handle == NULL ) + return ASDCP::RESULT_FILEOPEN; + + struct dirent* entry; + + for (;;) + { + if ( ( entry = readdir(m_Handle)) == NULL ) + return ASDCP::RESULT_ENDOFFILE; + + break; + } + + strncpy(filename, entry->d_name, ASDCP_MAX_PATH); + return ASDCP::RESULT_OK; +} + + +#endif // WIN32 + +// +// end DirScanner.cpp +// diff --git a/DirScanner.h b/DirScanner.h new file mode 100755 index 0000000..fba681b --- /dev/null +++ b/DirScanner.h @@ -0,0 +1,91 @@ +/* +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 DirScanner.h + \version $Id$ + \brief Cross-platform, simple non-recursive directory scanner +*/ + +#ifndef _DIRSCANNER_H_ +#define _DIRSCANNER_H_ + +#include +#define ASDCP_MAX_PATH 256 + +// Win32 directory scanner +// +#ifdef WIN32 +#include + +// +// +class DirScanner +{ + public: + __int64 m_Handle; + struct _finddatai64_t m_FileInfo; + + DirScanner() {}; + ~DirScanner() { Close(); } + ASDCP::Result_t Open(const char*); + ASDCP::Result_t Close(); + ASDCP::Result_t GetNext(char*); +}; + + +#else // WIN32 + +#include + +// POSIX directory scanner + +// +// +class DirScanner +{ + public: + DIR* m_Handle; + + DirScanner() : m_Handle(NULL) + { + } + + ~DirScanner() { Close(); } + + ASDCP::Result_t Open(const char*); + ASDCP::Result_t Close(); + ASDCP::Result_t GetNext(char*); +}; + + +#endif // WIN32 + +#endif // _DIRSCANNER_H_ + + +// +// end DirScanner.h +// diff --git a/FileIO.cpp b/FileIO.cpp new file mode 100755 index 0000000..b3d434e --- /dev/null +++ b/FileIO.cpp @@ -0,0 +1,555 @@ +/* +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 FileIO.cpp + \version $Id$ + \brief Cross-platform, simple file accessors +*/ + + +#include +#include +#include + +#ifdef WIN32 +typedef struct _stati64 fstat_t; + +// AFAIK, there is no iovec equivalent in the win32 API +struct iovec { + char* iov_base; // stupid iovec uses char* + int iov_len; +}; +#else +#include +typedef struct stat fstat_t; +#endif + +// +// +static ASDCP::Result_t +do_stat(const char* path, fstat_t* stat_info) +{ + ASDCP_TEST_NULL_STR(path); + ASDCP_TEST_NULL(stat_info); + + ASDCP::Result_t result = ASDCP::RESULT_OK; + +#ifdef WIN32 + UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX); + + if ( _stati64(path, stat_info) == (__int64)-1 ) + result = ASDCP::RESULT_FILEOPEN; + + ::SetErrorMode( prev ); +#else + if ( stat(path, stat_info) == -1L ) + result = ASDCP::RESULT_FILEOPEN; + + if ( stat_info->st_mode & (S_IFREG|S_IFLNK|S_IFDIR) == 0 ) + result = ASDCP::RESULT_FILEOPEN; +#endif + + return result; +} + + + +// +bool +ASDCP::PathIsFile(const char* pathname) +{ + assert(pathname); + fstat_t info; + + if ( ASDCP_SUCCESS(do_stat(pathname, &info)) ) + { + if ( info.st_mode & S_IFREG ) + return true; + } + + return false; +} + + +// +bool +ASDCP::PathIsDirectory(const char* pathname) +{ + assert(pathname); + fstat_t info; + + if ( ASDCP_SUCCESS(do_stat(pathname, &info)) ) + { + if ( info.st_mode & S_IFDIR ) + return true; + } + + return false; +} + + +// +ASDCP::fsize_t +ASDCP::FileSize(const char* pathname) +{ + assert(pathname); + fstat_t info; + + if ( ASDCP_SUCCESS(do_stat(pathname, &info)) ) + { + if ( info.st_mode & S_IFREG ) + return(info.st_size); + } + + return 0; +} + +//------------------------------------------------------------------------------------------ +// portable aspects of the file classes + +const int IOVecMaxEntries = 32; // we never use more that 3, but that number seems somehow small... + +// +class ASDCP::FileWriter::h__iovec +{ +public: + int m_Count; + struct iovec m_iovec[IOVecMaxEntries]; + h__iovec() : m_Count(0) {} +}; + + + +// +ASDCP::fsize_t +ASDCP::FileReader::Size() const +{ + return ASDCP::FileSize(m_Filename.c_str()); +} + +// these are declared here instead of in the header file +// because we have a mem_ptr that is managing a hidden class +ASDCP::FileWriter::FileWriter() {} +ASDCP::FileWriter::~FileWriter() {} + +// +ASDCP::Result_t +ASDCP::FileWriter::Writev(const byte_t* buf, ui32_t buf_len) +{ + assert( ! m_IOVec.empty() ); + register h__iovec* iov = m_IOVec; + ASDCP_TEST_NULL(buf); + + if ( iov->m_Count >= IOVecMaxEntries ) + { + DefaultLogSink().Error("The iovec is full! Only %lu entries allowed before a flush.\n", + IOVecMaxEntries); + return RESULT_FAIL; + } + + iov->m_iovec[iov->m_Count].iov_base = (char*)buf; // stupid iovec uses char* + iov->m_iovec[iov->m_Count].iov_len = buf_len; + iov->m_Count++; + + return RESULT_OK; +} + + +#ifdef WIN32 +//------------------------------------------------------------------------------------------ +// + +ASDCP::Result_t +ASDCP::FileReader::OpenRead(const char* filename) const +{ + ASDCP_TEST_NULL_STR(filename); + const_cast(this)->m_Filename = filename; + + // suppress popup window on error + UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX); + + const_cast(this)->m_Handle = ::CreateFile(filename, + (GENERIC_READ), // open for reading + FILE_SHARE_READ, // share for reading + NULL, // no security + OPEN_EXISTING, // read + FILE_ATTRIBUTE_NORMAL, // normal file + NULL // no template file + ); + + ::SetErrorMode(prev); + + return ( m_Handle == INVALID_HANDLE_VALUE ) ? + ASDCP::RESULT_FILEOPEN : ASDCP::RESULT_OK; +} + +// +ASDCP::Result_t +ASDCP::FileReader::Close() const +{ + if ( m_Handle == INVALID_HANDLE_VALUE ) + return ASDCP::RESULT_FILEOPEN; + + // suppress popup window on error + UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX); + BOOL result = ::CloseHandle(m_Handle); + ::SetErrorMode(prev); + const_cast(this)->m_Handle = INVALID_HANDLE_VALUE; + + return ( result == 0 ) ? ASDCP::RESULT_FAIL : ASDCP::RESULT_OK; +} + +// +ASDCP::Result_t +ASDCP::FileReader::Seek(ASDCP::fpos_t position, SeekPos_t whence) const +{ + if ( m_Handle == INVALID_HANDLE_VALUE ) + return ASDCP::RESULT_STATE; + + LARGE_INTEGER in; + UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX); + in.QuadPart = position; + in.LowPart = ::SetFilePointer(m_Handle, in.LowPart, &in.HighPart, whence); + HRESULT LastError = GetLastError(); + ::SetErrorMode(prev); + + if ( (LastError != NO_ERROR + && (in.LowPart == INVALID_SET_FILE_POINTER + || in.LowPart == ERROR_NEGATIVE_SEEK )) ) + return ASDCP::RESULT_READFAIL; + + return ASDCP::RESULT_OK; +} + +// +ASDCP::Result_t +ASDCP::FileReader::Tell(ASDCP::fpos_t* pos) const +{ + ASDCP_TEST_NULL(pos); + + if ( m_Handle == (HANDLE)-1L ) + return ASDCP::RESULT_FILEOPEN; + + LARGE_INTEGER in; + UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX); + in.QuadPart = (__int64)0; + in.LowPart = ::SetFilePointer(m_Handle, in.LowPart, &in.HighPart, FILE_CURRENT); + HRESULT LastError = GetLastError(); + ::SetErrorMode(prev); + + if ( (LastError != NO_ERROR + && (in.LowPart == INVALID_SET_FILE_POINTER + || in.LowPart == ERROR_NEGATIVE_SEEK )) ) + return ASDCP::RESULT_READFAIL; + + *pos = (ASDCP::fpos_t)in.QuadPart; + return ASDCP::RESULT_OK; +} + +// +ASDCP::Result_t +ASDCP::FileReader::Read(byte_t* buf, ui32_t buf_len, ui32_t* read_count) const +{ + ASDCP_TEST_NULL(buf); + Result_t result = ASDCP::RESULT_OK; + DWORD tmp_count; + ui32_t tmp_int; + + if ( read_count == 0 ) + read_count = &tmp_int; + + *read_count = 0; + + if ( m_Handle == INVALID_HANDLE_VALUE ) + return ASDCP::RESULT_FILEOPEN; + + UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX); + if ( ::ReadFile(m_Handle, buf, buf_len, &tmp_count, NULL) == 0 ) + result = ASDCP::RESULT_READFAIL; + + ::SetErrorMode(prev); + + if ( tmp_count == 0 ) /* EOF */ + result = ASDCP::RESULT_ENDOFFILE; + + if ( ASDCP_SUCCESS(result) ) + *read_count = tmp_count; + + return result; +} + + + +//------------------------------------------------------------------------------------------ +// + +// +ASDCP::Result_t +ASDCP::FileWriter::OpenWrite(const char* filename) +{ + ASDCP_TEST_NULL_STR(filename); + m_Filename = filename; + + // suppress popup window on error + UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX); + + m_Handle = ::CreateFile(filename, + (GENERIC_WRITE|GENERIC_READ), // open for reading + FILE_SHARE_READ, // share for reading + NULL, // no security + CREATE_ALWAYS, // overwrite (beware!) + FILE_ATTRIBUTE_NORMAL, // normal file + NULL // no template file + ); + + ::SetErrorMode(prev); + + if ( m_Handle == INVALID_HANDLE_VALUE ) + return ASDCP::RESULT_FILEOPEN; + + m_IOVec = new h__iovec; + return ASDCP::RESULT_OK; +} + +// +ASDCP::Result_t +ASDCP::FileWriter::Writev(ui32_t* bytes_written) +{ + assert( ! m_IOVec.empty() ); + register h__iovec* iov = m_IOVec; + ui32_t tmp_int; + + if ( bytes_written == 0 ) + bytes_written = &tmp_int; + + if ( m_Handle == INVALID_HANDLE_VALUE ) + return ASDCP::RESULT_STATE; + + *bytes_written = 0; + UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX); + Result_t result = ASDCP::RESULT_OK; + + // AFAIK, there is no writev() equivalent in the win32 API + for ( register int i = 0; i < iov->m_Count; i++ ) + { + ui32_t tmp_count = 0; + BOOL wr_result = ::WriteFile(m_Handle, + iov->m_iovec[i].iov_base, + iov->m_iovec[i].iov_len, + (DWORD*)&tmp_count, + NULL); + + if ( wr_result == 0 ) + { + result = ASDCP::RESULT_WRITEFAIL; + break; + } + + assert(iov->m_iovec[i].iov_len == tmp_count); + *bytes_written += tmp_count; + } + + ::SetErrorMode(prev); + iov->m_Count = 0; // error nor not, all is lost + + return result; +} + +// +ASDCP::Result_t +ASDCP::FileWriter::Write(const byte_t* buf, ui32_t buf_len, ui32_t* bytes_written) +{ + ASDCP_TEST_NULL(buf); + ui32_t tmp_int; + + if ( bytes_written == 0 ) + bytes_written = &tmp_int; + + if ( m_Handle == INVALID_HANDLE_VALUE ) + return ASDCP::RESULT_STATE; + + // suppress popup window on error + UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX); + BOOL result = ::WriteFile(m_Handle, buf, buf_len, (DWORD*)bytes_written, NULL); + ::SetErrorMode(prev); + + return ( result == 0 ) ? ASDCP::RESULT_WRITEFAIL : ASDCP::RESULT_OK; +} + +#else // WIN32 +//------------------------------------------------------------------------------------------ +// POSIX + +// +ASDCP::Result_t +ASDCP::FileReader::OpenRead(const char* filename) const +{ + ASDCP_TEST_NULL_STR(filename); + const_cast(this)->m_Filename = filename; + const_cast(this)->m_Handle = open(filename, O_RDONLY, 0); + return ( m_Handle == -1L ) ? RESULT_FILEOPEN : RESULT_OK; +} + +// +ASDCP::Result_t +ASDCP::FileReader::Close() const +{ + if ( m_Handle == -1L ) + return RESULT_FILEOPEN; + + close(m_Handle); + const_cast(this)->m_Handle = -1L; + return RESULT_OK; +} + +// +ASDCP::Result_t +ASDCP::FileReader::Seek(ASDCP::fpos_t position, SeekPos_t whence) const +{ + if ( m_Handle == -1L ) + return RESULT_FILEOPEN; + + if ( lseek(m_Handle, position, whence) == -1L ) + return RESULT_BADSEEK; + + return RESULT_OK; +} + +// +ASDCP::Result_t +ASDCP::FileReader::Tell(ASDCP::fpos_t* pos) const +{ + ASDCP_TEST_NULL(pos); + + if ( m_Handle == -1L ) + return RESULT_FILEOPEN; + + ASDCP::fpos_t tmp_pos; + + if ( (tmp_pos = lseek(m_Handle, 0, SEEK_CUR)) == -1 ) + return RESULT_READFAIL; + + *pos = tmp_pos; + return RESULT_OK; +} + +// +ASDCP::Result_t +ASDCP::FileReader::Read(byte_t* buf, ui32_t buf_len, ui32_t* read_count) const +{ + ASDCP_TEST_NULL(buf); + i32_t tmp_count = 0; + ui32_t tmp_int = 0; + + if ( read_count == 0 ) + read_count = &tmp_int; + + *read_count = 0; + + if ( m_Handle == -1L ) + return RESULT_FILEOPEN; + + if ( (tmp_count = read(m_Handle, buf, buf_len)) == -1L ) + return RESULT_READFAIL; + + *read_count = tmp_count; + return (tmp_count == 0 ? RESULT_ENDOFFILE : RESULT_OK); +} + + +//------------------------------------------------------------------------------------------ +// + +// +ASDCP::Result_t +ASDCP::FileWriter::OpenWrite(const char* filename) +{ + ASDCP_TEST_NULL_STR(filename); + m_Filename = filename; + m_Handle = open(filename, O_RDWR|O_CREAT|O_TRUNC, 0644); + + if ( m_Handle == -1L ) + { + perror(filename); + return RESULT_FILEOPEN; + } + + m_IOVec = new h__iovec; + return RESULT_OK; +} + +// +ASDCP::Result_t +ASDCP::FileWriter::Writev(ui32_t* bytes_written) +{ + assert( ! m_IOVec.empty() ); + register h__iovec* iov = m_IOVec; + ui32_t tmp_int; + + if ( bytes_written == 0 ) + bytes_written = &tmp_int; + + if ( m_Handle == -1L ) + return RESULT_STATE; + + int read_size = writev(m_Handle, iov->m_iovec, iov->m_Count); + + if ( read_size == -1L ) + return RESULT_WRITEFAIL; + + iov->m_Count = 0; + *bytes_written = read_size; + return RESULT_OK; +} + +// +ASDCP::Result_t +ASDCP::FileWriter::Write(const byte_t* buf, ui32_t buf_len, ui32_t* bytes_written) +{ + ASDCP_TEST_NULL(buf); + ui32_t tmp_int; + + if ( bytes_written == 0 ) + bytes_written = &tmp_int; + + if ( m_Handle == -1L ) + return RESULT_STATE; + + int read_size = write(m_Handle, buf, buf_len); + + if ( read_size == -1L ) + return RESULT_WRITEFAIL; + + *bytes_written = read_size; + return RESULT_OK; +} + + +#endif // WIN32 + +// +// end FileIO.cpp +// diff --git a/FileIO.h b/FileIO.h new file mode 100755 index 0000000..a833803 --- /dev/null +++ b/FileIO.h @@ -0,0 +1,143 @@ +/* +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 FileIO.h + \version $Id$ + \brief Cross-platform, simple file accessors +*/ + + +#ifndef _FILEIO_H_ +#define _FILEIO_H_ + +#include +#include + +#ifdef WIN32 +#include +#else +#include +#include +#endif + +namespace ASDCP { +#ifdef WIN32 + typedef __int64 fsize_t; + typedef __int64 fpos_t; + + enum SeekPos_t { + SP_BEGIN = FILE_BEGIN, + SP_POS = FILE_CURRENT, + SP_END = FILE_END + }; +#else + typedef off_t fsize_t; + typedef off_t fpos_t; + typedef int HANDLE; + const HANDLE INVALID_HANDLE_VALUE = -1L; + + enum SeekPos_t { + SP_BEGIN = SEEK_SET, + SP_POS = SEEK_CUR, + SP_END = SEEK_END + }; +#endif + + bool PathIsFile(const char* pathname); + bool PathIsDirectory(const char* pathname); + fsize_t FileSize(const char* pathname); + + // + class FileReader + { + ASDCP_NO_COPY_CONSTRUCT(FileReader); + + protected: + std::string m_Filename; + HANDLE m_Handle; + + public: + FileReader() : m_Handle(INVALID_HANDLE_VALUE) {} + virtual ~FileReader() { Close(); } + + Result_t OpenRead(const char*) const; // open the file for reading + Result_t Close() const; // close the file + fsize_t Size() const; // returns the file's current size + Result_t Seek(ASDCP::fpos_t = 0, SeekPos_t = SP_BEGIN) const; // move the file pointer + Result_t Tell(ASDCP::fpos_t* pos) const; // report the file pointer's location + Result_t Read(byte_t*, ui32_t, ui32_t* = 0) const; // read a buffer of data + + inline ASDCP::fpos_t Tell() const // report the file pointer's location + { + ASDCP::fpos_t tmp_pos; + Tell(&tmp_pos); + return tmp_pos; + } + + inline bool IsOpen() { // returns true if the file is open + return (m_Handle != INVALID_HANDLE_VALUE); + } + }; + + + // + class FileWriter : public FileReader + { + class h__iovec; + mem_ptr m_IOVec; + ASDCP_NO_COPY_CONSTRUCT(FileWriter); + + public: + FileWriter(); + virtual ~FileWriter(); + + Result_t OpenWrite(const char*); // open a new file, overwrites existing + Result_t OpenModify(const char*); // open a file for read/write + + // this part of the interface takes advantage of the iovec structure on + // platforms that support it. For each call to Writev(const byte_t*, ui32_t, ui32_t*), + // the given buffer is added to an internal iovec struct. All items on the list + // are written to disk by a call to Writev(); + Result_t Writev(const byte_t*, ui32_t); // queue buffer for "gather" write + Result_t Writev(ui32_t* = 0); // write all queued buffers + + // if you call this while there are unwritten items on the iovec list, + // the iovec list will be written to disk before the givn buffer,as though + // you had called Writev() first. + Result_t Write(const byte_t*, ui32_t, ui32_t* = 0); // write buffer to disk + + + }; + +} // namespace ASDCP + + +#endif // _FILEREADER_H_ + + +// +// end FileReader.h +// diff --git a/FortunaRNG.cpp b/FortunaRNG.cpp new file mode 100755 index 0000000..9c444c8 --- /dev/null +++ b/FortunaRNG.cpp @@ -0,0 +1,207 @@ +/* +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 FortunaRNG.cpp + \version $Id$ + \brief Fortuna PRNG implementation +*/ + + +/* + This module partially implements the Fortuna PRNG described in Chapter + 10 of "Practical Cryptography", Ferguson & Schneier, 2003. + + The generator does not use a hash to stir the key, nor does it limit + the block size of generated blocks. It does NOT implement a cross- + platform entropy collector. + + In fact, the seed methods in use here should be regarded with great + suspicion. This module is provided as a development aid, and is not + endorsed for any use outside that narrow purpose. + */ + + +#include +#include +#include +#include + + +const ui32_t RNG_KEY_SIZE = 16; +const ui32_t RNG_KEY_SIZE_BITS = 128; +const ui32_t RNG_BLOCK_SIZE = 16; + +// internal implementation class +class FortunaRNG::h__RNG +{ +public: + AES_KEY m_Context; + byte_t m_rng_buf[RNG_BLOCK_SIZE]; + + // + void + fill_rand(byte_t* buf, ui32_t len) + { + ui32_t gen_count = 0; + + while ( gen_count + RNG_BLOCK_SIZE <= len ) + { + AES_encrypt(m_rng_buf, buf + gen_count, &m_Context); + *(ui32_t*)(m_rng_buf + 12) += 3; + gen_count += RNG_BLOCK_SIZE; + } + + if ( len == gen_count ) // partial count needed? + return; + + byte_t tmp[RNG_BLOCK_SIZE]; + AES_encrypt(m_rng_buf, tmp, &m_Context); + *(ui32_t*)(m_rng_buf + 12) += 3; + memcpy(buf + gen_count, tmp, len - gen_count); + } + + void + re_seed() + { + // re-seed the generator + byte_t rng_key[RNG_KEY_SIZE]; + fill_rand(rng_key, RNG_KEY_SIZE); + AES_set_encrypt_key(rng_key, RNG_KEY_SIZE_BITS, &m_Context); + } +}; + + +// we have separate constructor implementations depending upon platform: +// + +#ifdef WIN32 +// on win32 systems, we grab one bit at a time from the performance +// counter and assemble seed bytes from the collected bits + +#define WIN32_LEAN_AND_MEAN +#define VC_EXTRALEAN +#include + +// +static byte_t get_perf_byte() +{ + LARGE_INTEGER ticks; + byte_t retval; + + for ( int i = 0; i < 8; i++ ) + { + QueryPerformanceCounter(&ticks); + retval |= (ticks.LowPart & 0x00000001) << i; + } + + return retval; +} + +// +FortunaRNG::FortunaRNG() +{ + m_RNG = new h__RNG; + byte_t rng_key[RNG_KEY_SIZE]; + int i; + + for ( i = 0; i < RNG_KEY_SIZE; i++ ) + rng_key[i] = get_perf_byte(); + + for ( i = 0; i < RNG_BLOCK_SIZE; i++ ) + m_RNG->m_rng_buf[i] = get_perf_byte(); + + AES_set_encrypt_key(rng_key, RNG_KEY_SIZE_BITS, &m_RNG->m_Context); +} + +#else // WIN32 +// on POSIX systems, we simply read some seed from /dev/urandom + +#include + +const char* DEV_URANDOM = "/dev/urandom"; + +FortunaRNG::FortunaRNG() +{ + m_RNG = new h__RNG; + + byte_t rng_key[RNG_KEY_SIZE]; + FILE* dev_random = fopen(DEV_URANDOM, "r"); + + if ( dev_random == 0 ) + { + ASDCP::DefaultLogSink().Error("Unable to open DEV_URANDOM\n"); + perror(DEV_URANDOM); + m_RNG = 0; + return; + } + + if ( fread(rng_key, 1, RNG_KEY_SIZE, dev_random) != RNG_KEY_SIZE ) + { + fclose(dev_random); + ASDCP::DefaultLogSink().Error("Unable to read DEV_URANDOM\n"); + perror(DEV_URANDOM); + m_RNG = 0; + return; + } + + if ( fread(m_RNG->m_rng_buf, 1, RNG_BLOCK_SIZE, dev_random) != RNG_BLOCK_SIZE ) + { + fclose(dev_random); + ASDCP::DefaultLogSink().Error("Unable to read DEV_URANDOM\n"); + perror(DEV_URANDOM); + m_RNG = 0; + return; + } + + fclose(dev_random); + AES_set_encrypt_key(rng_key, RNG_KEY_SIZE_BITS, &m_RNG->m_Context); +} + +#endif // WIN32 + +FortunaRNG::~FortunaRNG() +{ +} + +// +byte_t* +FortunaRNG::FillRandom(byte_t* buf, ui32_t len) +{ + assert(buf); + + if ( ! m_RNG ) + return 0; + + m_RNG->fill_rand(buf, len); + m_RNG->re_seed(); + + return buf; +} + + +// +// end FortunaRNG.cpp +// diff --git a/FortunaRNG.h b/FortunaRNG.h new file mode 100755 index 0000000..356af8b --- /dev/null +++ b/FortunaRNG.h @@ -0,0 +1,72 @@ +/* +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 FortunaRNG.h + \version $Id$ + \brief Fortuna PRNG interface +*/ + + +/* + This module partially implements the Fortuna PRNG described in Chapter + 10 of "Practical Cryptography", Ferguson & Schneier, 2003. + + The generator does not use a hash to stir the key, nor does it limit + the block size of generated blocks. It does NOT implement a cross- + platform entropy collector. + + In fact, the seed methods in use here should be regarded with great + suspicion. This module is provided as a development aid, and is not + endorsed for any use outside that narrow purpose. + */ + + + +#ifndef __FORTUNARNG_H__ +#define __FORTUNARNG_H__ + +#include // re-using handy typedefs + + +class FortunaRNG +{ + class h__RNG; + ASDCP::mem_ptr m_RNG; + ASDCP_NO_COPY_CONSTRUCT(FortunaRNG); + + public: + FortunaRNG(); + ~FortunaRNG(); + byte_t* FillRandom(byte_t* buf, ui32_t len); +}; + + + +#endif // __FORTUNARNG_H__ + +// +// end FortunaRNG.h +// diff --git a/Identifier.h b/Identifier.h new file mode 100755 index 0000000..be93639 --- /dev/null +++ b/Identifier.h @@ -0,0 +1,178 @@ +// +// Identifier.h +// + +#ifndef _IDENTIFIER_H_ +#define _IDENTIFIER_H_ + +namespace ASDCP +{ + // the base of all identifier classes + template + class Identifier : public IArchive + { + protected: + byte_t m_Value[SIZE]; + + public: + Identifier() { + memset(m_Value, 0, SIZE); + } + + // + inline Result_t Set(const byte_t* value) { + ASDCP_TEST_NULL(value); + memcpy(m_Value, value, SIZE); + return RESULT_OK; + } + + // + inline Result_t ReadFrom(ASDCP::MemIOReader& Reader) { + Reader.ReadRaw(m_Value, SIZE); + return RESULT_OK; + } + + // + inline Result_t WriteTo(ASDCP::MemIOWriter& Writer) { + Writer.WriteRaw(m_Value, SIZE); + return RESULT_OK; + } + + inline const byte_t* Data() const { return m_Value; } + + inline ui32_t Size() const { return SIZE; } + + // + inline bool operator<(const Identifier& rhs) const + { + for ( ui32_t i = 0; i < SIZE; i++ ) + { + if ( m_Value[i] != rhs.m_Value[i] ) + return m_Value[i] < rhs.m_Value[i]; + } + + return false; + } + + // + inline bool operator==(const Identifier& rhs) const + { + if ( rhs.Size() != SIZE ) + return false; + + return ( memcmp(m_Value, rhs.m_Value, SIZE) == 0 ); + } + + // + // todo: refactor characrer insertion back to bin2hex() + const char* ToString(char* str_buf) const + { + char* p = str_buf; + + for ( ui32_t i = 0; i < SIZE; i++ ) + { + *p = (m_Value[i] >> 4) & 0x0f; + *p += *p < 10 ? 0x30 : 0x61 - 10; + p++; + + *p = m_Value[i] & 0x0f; + *p += *p < 10 ? 0x30 : 0x61 - 10; + p++; + + *p = ' '; + p++; + } + + *p = 0; + return str_buf; + } + }; + + + class UL; + class UUID; + + // UID - either a UL or a UUID + class UID : public Identifier + { + friend class ASDCP::UL; + friend class ASDCP::UUID; + + public: + UID() {} + UID(const UID& rhs) { + memcpy(m_Value, rhs.m_Value, SMPTE_UL_LENGTH); + } + }; + + // Universal Label + class UL : public Identifier + { + public: + UL() {} + UL(const UL& rhs) { + memcpy(m_Value, rhs.m_Value, SMPTE_UL_LENGTH); + } + + UL(const UID& rhs) { + memcpy(m_Value, rhs.m_Value, SMPTE_UL_LENGTH); + } + + UL(const byte_t* value) { + assert(value); + memcpy(m_Value, value, SMPTE_UL_LENGTH); + } + + bool operator==(const UL& rhs) const { + return ( memcmp(m_Value, rhs.m_Value, SMPTE_UL_LENGTH) == 0 ) ? true : false; + } + }; + + // UUID + class UUID : public Identifier + { + public: + UUID() {} + UUID(const UUID& rhs) { + memcpy(m_Value, rhs.m_Value, SMPTE_UL_LENGTH); + } +#if 0 + UUID(const UID& rhs) { + memcpy(m_Value, rhs.m_Value + 8, 8); + memcpy(m_Value + 8, rhs.m_Value, 8); + } +#endif + void GenRandomValue(); + }; + + // UMID + class UMID : public Identifier + { + public: + UMID() {} + UMID(const UMID &rhs) { + memcpy(m_Value, rhs.m_Value, SMPTE_UMID_LENGTH); + }; + + void MakeUMID(int Type); + + void MakeUMID(int Type, const UUID& ID); + + void SetMaterial(UL& aUL); + + void SetInstance(int Instance, int Method = -1); + + ui32_t GetInstance(void) const + { + assert(0); + return ( m_Value[13] << 16 ) | ( m_Value[14] << 8 ) | m_Value[15]; + } + }; + +} // namespace mxflib + +#endif // _IDENTIFIER_H_ + +// +// end Identifier.h +// diff --git a/Index.h b/Index.h new file mode 100755 index 0000000..8ac2511 --- /dev/null +++ b/Index.h @@ -0,0 +1,75 @@ +// +// +// + +#ifndef _INDEX_H_ +#define _INDEX_H_ + +#include "MXF.h" + +namespace ASDCP +{ + namespace MXF + { + // + 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 SliceOffset; + Array 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 DeltaEntryArray; + Batch 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); + }; + + } // namespace MXF +} // namespace ASDCP + + +#endif // _INDEX_H_ + +// +// end MXF.h +// diff --git a/MemIO.h b/MemIO.h new file mode 100755 index 0000000..83b676a --- /dev/null +++ b/MemIO.h @@ -0,0 +1,236 @@ +/* +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 MemIO.h + \version $Id$ + \brief Interface for reading and writing typed objects to a byte-oriented buffer +*/ + +#ifndef _MEMIO_H_ +#define _MEMIO_H_ + +#include +#include + +namespace ASDCP +{ + // + class MemIOWriter + { + ASDCP_NO_COPY_CONSTRUCT(MemIOWriter); + MemIOWriter(); + + protected: + byte_t* m_p; + ui32_t m_capacity; + ui32_t m_size; + + public: + MemIOWriter(byte_t* p, ui32_t c) : + m_p(p), m_capacity(c), m_size(0) { + assert(m_p); assert(m_capacity); + } + + ~MemIOWriter() {} + + inline byte_t* Data() { return m_p; } + inline byte_t* CurrentData() { return m_p + m_size; } + inline ui32_t Size() { return m_size; } + inline ui32_t Remainder() { return m_capacity - m_size; } + + inline Result_t AddOffset(ui32_t offset) { + if ( ( m_size + offset ) > m_capacity ) + return RESULT_FAIL; + + m_size += offset; + return RESULT_OK; + + } + + inline Result_t WriteRaw(const byte_t* p, ui32_t buf_len) { + if ( ( m_size + buf_len ) > m_capacity ) + return RESULT_FAIL; + + memcpy(m_p + m_size, p, buf_len); + m_size += buf_len; + return RESULT_OK; + } + + inline Result_t WriteBER(ui64_t i, ui32_t ber_len) { + if ( ( m_size + ber_len ) > m_capacity ) + return RESULT_FAIL; + + if ( ! write_BER(m_p + m_size, i, ber_len) ) + return RESULT_FAIL; + + m_size += ber_len; + return RESULT_OK; + } + + inline Result_t WriteUi8(ui8_t i) { + if ( ( m_size + 1 ) > m_capacity ) + return RESULT_FAIL; + + *(m_p + m_size) = i; + m_size++; + return RESULT_OK; + } + + inline Result_t WriteUi16BE(ui16_t i) { + if ( ( m_size + sizeof(ui16_t) ) > m_capacity ) + return RESULT_FAIL; + + i2p(ASDCP_i16_BE(i), m_p + m_size); + m_size += sizeof(ui16_t); + return RESULT_OK; + } + + inline Result_t WriteUi32BE(ui32_t i) { + if ( ( m_size + sizeof(ui32_t) ) > m_capacity ) + return RESULT_FAIL; + + i2p(ASDCP_i32_BE(i), m_p + m_size); + m_size += sizeof(ui32_t); + return RESULT_OK; + } + + inline Result_t WriteUi64BE(ui64_t i) { + if ( ( m_size + sizeof(ui64_t) ) > m_capacity ) + return RESULT_FAIL; + + i2p(ASDCP_i64_BE(i), m_p + m_size); + m_size += sizeof(ui64_t); + return RESULT_OK; + } + }; + + // + class MemIOReader + { + ASDCP_NO_COPY_CONSTRUCT(MemIOReader); + MemIOReader(); + + protected: + const byte_t* m_p; + ui32_t m_capacity; + ui32_t m_size; // this is sort of a misnomer, when we are reading it measures offset + + public: + MemIOReader(const byte_t* p, ui32_t c) : + m_p(p), m_capacity(c), m_size(0) { + assert(m_p); assert(m_capacity); + } + + ~MemIOReader() {} + + inline const byte_t* Data() { return m_p; } + inline const byte_t* CurrentData() { return m_p + m_size; } + inline ui32_t Offset() { return m_size; } + inline ui32_t Remainder() { return m_capacity - m_size; } + + inline Result_t SkipOffset(ui32_t offset) { + if ( ( m_size + offset ) > m_capacity ) + return RESULT_FAIL; + + m_size += offset; + return RESULT_OK; + } + + inline Result_t ReadRaw(byte_t* p, ui32_t buf_len) { + if ( ( m_size + buf_len ) > m_capacity ) + return RESULT_FAIL; + + memcpy(p, m_p + m_size, buf_len); + m_size += buf_len; + return RESULT_OK; + } + + Result_t ReadBER(ui64_t* i, ui32_t* ber_len) { + ASDCP_TEST_NULL(i); + ASDCP_TEST_NULL(ber_len); + + if ( ( *ber_len = BER_length(m_p + m_size) ) == 0 ) + return RESULT_FAIL; + + if ( ( m_size + *ber_len ) > m_capacity ) + return RESULT_FAIL; + + if ( ! read_BER(m_p + m_size, i) ) + return RESULT_FAIL; + + m_size += *ber_len; + return RESULT_OK; + } + + inline Result_t ReadUi8(ui8_t* i) { + ASDCP_TEST_NULL(i); + if ( ( m_size + 1 ) > m_capacity ) + return RESULT_FAIL; + + *i = *(m_p + m_size); + m_size++; + return RESULT_OK; + } + + inline Result_t ReadUi16BE(ui16_t* i) { + ASDCP_TEST_NULL(i); + if ( ( m_size + sizeof(ui16_t) ) > m_capacity ) + return RESULT_FAIL; + + *i = ASDCP_i16_BE(cp2i(m_p + m_size)); + m_size += sizeof(ui16_t); + return RESULT_OK; + } + + inline Result_t ReadUi32BE(ui32_t* i) { + ASDCP_TEST_NULL(i); + if ( ( m_size + sizeof(ui32_t) ) > m_capacity ) + return RESULT_FAIL; + + *i = ASDCP_i32_BE(cp2i(m_p + m_size)); + m_size += sizeof(ui32_t); + return RESULT_OK; + } + + inline Result_t ReadUi64BE(ui64_t* i) { + ASDCP_TEST_NULL(i); + if ( ( m_size + sizeof(ui64_t) ) > m_capacity ) + return RESULT_FAIL; + + *i = ASDCP_i64_BE(cp2i(m_p + m_size)); + m_size += sizeof(ui64_t); + return RESULT_OK; + } + }; + + +} // namespace ASDCP + +#endif // _MEMIO_H_ + +// +// end MemIO.h +// diff --git a/Mutex.h b/Mutex.h new file mode 100755 index 0000000..f918f71 --- /dev/null +++ b/Mutex.h @@ -0,0 +1,86 @@ +/* +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 Mutex.h + \version $Id$ + \brief Portable mutex wrapper +*/ + +#ifndef _MUTEX_H_ +#define _MUTEX_H_ + +#include "AS_DCP_system.h" + +namespace ASDCP +{ +#ifdef WIN32 + class Mutex + { + CRITICAL_SECTION m_Mutex; + ASDCP_NO_COPY_CONSTRUCT(Mutex); + + public: + inline Mutex() { ::InitializeCriticalSection(&m_Mutex); } + inline ~Mutex() { ::DeleteCriticalSection(&m_Mutex); } + inline void Lock() { ::EnterCriticalSection(&m_Mutex); } + inline void Unlock() { ::LeaveCriticalSection(&m_Mutex); } + }; +#else // WIN32 +#include + class Mutex + { + pthread_mutex_t m_Mutex; + ASDCP_NO_COPY_CONSTRUCT(Mutex); + + public: + inline Mutex() { pthread_mutex_init(&m_Mutex, 0); } + inline ~Mutex() { pthread_mutex_destroy(&m_Mutex); } + inline void Lock() { pthread_mutex_lock(&m_Mutex); } + inline void Unlock() { pthread_mutex_unlock(&m_Mutex); } + }; +#endif // WIN32 + + // automatic Mutex management within a block - + // the mutex is created by the constructor and + // released by the destructor + class AutoMutex + { + Mutex& m_Mutex; + AutoMutex(); + ASDCP_NO_COPY_CONSTRUCT(AutoMutex); + + public: + AutoMutex(Mutex& Mtx) : m_Mutex(Mtx) { m_Mutex.Lock(); } + ~AutoMutex() { m_Mutex.Unlock(); } + }; + +} // namespace ASDCP + +#endif // _MUTEX_H_ + +// +// end Mutex.h +// diff --git a/README b/README new file mode 100755 index 0000000..bae2f52 --- /dev/null +++ b/README @@ -0,0 +1,326 @@ + +$Id$ + +The asdcplib library is a set of objects that offer +simplified access to files conforming to the sound and +picture track file formats proposed by the SMPTE working +group DC-28.20. + +This work was funded by Digital Cinema Initiatives, LLC +and others. + +**The asdcplib project is housed on SourceForge. The project +home page is at http://sourceforge.net/projects/asdcplib. +There is not much there other than CVS because I don't +really have the time to master SourceForge administration. +Feel free to email with questions or to request a tar file. + +The project used to depend upon the mxflib project. Because +of the focus on covering the whole of the MXF spscifications, +mxflib is considerably larger and more complex that what we +require for this application. For this reason we have forked +mxflib and have simplified its implementation to better suit +our application. Thanks to Matt Beard and Oliver Morgan for +their great work and support. + +OpenSSL is also required if you want to read and write +encrypted AS-DCP files. See http://www.openssl.org + +Thanks also to the members of the SMPTE DC-28.20 packaging +ad-hoc group and the members of the MXF Interop Initiative +for their encouragement and support. Special thanks to +Jim Whittlesey and Howard Lukk at DCI for proposing and +supporting this project. + + +Design Notes + +This library is intended (but of course not limited) for +use by developers of commercial D-Cinema products. It is +designed to be easily integrated into a wide variety of +development environments. Commercial users are strongly +urged to use static linking (at least where you use this +library) to prevent malicious in-field replacement of +critical system modules. This recommendation should be +considered wherever Open Source or Free software is being +used for applications having non-trivial security +requirements. + +The author strives mightily to provide an API that is +completely independent of operating system and other +library dependencies, and which allows selective replacement +of some modules for local needs. Specifically, the essence +parsers and OpenSSL crypto functions can be replaced by +linking to alternative implementations of the ASDCP:: +objects which provide those services. + +AS_DCP.h contains the entire API. You do not need to read +any of the other files, except maybe asdcp-test.cpp which +contains detailed usage examples of each of the API's +services. + + +Build Instructions + +GNU make is required to build asdcplib. The makefile will +work on win32 systems that have Cygwin. Other win32 gmake +packages may or may not work depending upon the availability +of standard POSIX shell commands. + +If you are building on win32 or a unix with no OpenSSL +library support, you will have to also obtain and build +OpenSSL (I'm using 0.9.7d). Unpack it into the same +parent directory as asdcplib/, and rename the directory +as (or make a sym link named) 'openssl': + +myhost$ ls -l +total 1761 +drwxr-xr-x 1 jhurst None 0 Feb 3 16:37 asdcplib +drwxr-xr-x 56 jhurst None 0 Feb 2 16:35 openssl + +I have tested this build on win32, Linux and Darwin platforms. +Others may work as well. + +If you do not want to use or do not have OpenSSL, invoke +make with ASDCP_WITHOUT_OPENSSL defined: + + make ASDCP_WITHOUT_OPENSSL=1 + + +Build Versions + +There are two flavors of build: the default build and the +"SMPTE_LABELS" build. Unless you are specifically working on +emerging SMPTE standards for D-Cinema content, you will certainly +want the default build. If you are developing support for the MXF +Interop Initiative standards, the default build is for you! + +If you are not aiming for MXF Interop and if you are working +on developing support for the very latest versions of the SMPTE +draft standards, then you want the SMPTE_LABELS build: + + make SMPTE_LABELS=1 + +NOTE: If you choose the SMPTE_LABELS build, you will be creating +files that may not work on MXF Interop systems. In the case of +encryption, incompatibility with MXF Interop is guaranteed! You +have been warned! + + +Documentation + +Currently, the documentation is mostly in AS_DCP.h. Read +that file for a detailed description of the library's +capabilities. Read asdcp-test.cpp for library usage +examples. More detailed documentation will be written RSN. + + +Change History +2005.00.00 - The Reformation + o Removed mxflib as a dependency by forking the necessary + functions and placing them in the mxf-lite subdirectory. + Please note that the very heavy modifications done here + render all comparisson to mxflib code a substantial task. + All errors are now mine and users are warned not to bug + Oliver or Matt for help with this code. The version of + mxflib at the time of the fork was: 0.5.1.3. + + +2005.06.03 - bug fixes v0.10.18 + o Updated UL batch to include GC UL. + + +2005.05.27 - bug fixes v0.10.17 + o Un-did essence container and compression descriptor changes. + The default build reflects MXF Interop decisions as of 26 May. + o Added note about build versions to README (see above). + o Added warnings to SMPTE_LABELS builds. + o Fixed JP2K essence container label. + + +2005.05.02 - bug fixes v0.10.16 + o Reorganized internal files, added file reader object, added OS + portability header, removed and renamed some files. If you have a + patch against previous versions of the source, you should check + it thoroughly. + o Added RGBA attributes to JP2K descriptor. + o Changed interface to CodestreamParser. + o Added JP2K parser implementation. It is parsing each frame but is + not yet being used to populate the descriptor. + o Added 48fps option for `asdcp-test -p`. + o Added picture rate constants to AS_DCP.h (23.976, 24, 48). + o Added sample rate constant to AS_DCP.h (48k). + o Changed asdcp-test to encrypt picture headers by default + (plaintext offset will be 0), added -E option to allow + plaintext headers. + + +2005.04.28 - bug fixes v0.9.15 + o The XML descriptors for the crypto DMS have moved in mxflib to + the file DMS_Crypto.xml (they were in DMS_DCPENC.xml). Older + installations should update the file from mxflib. + o Added Close() and Seek() to ~MyFileWriter(), cleaned up headers + o Added UUID generator output mode (-u). + o Added -S option to extract PCM essence into stereo wav files + o Added more UL testing and conformance checking. + o Added macro SMPTE_LABELS which causes the library to be built + with SMPTE (as opposed to MXF Interop) labels. This is not + set by default, and currently only affects the PCM container + label and encrypted element label. + o Cleaned up the GNUmakefile test targets, the source files + are now named with the TEST_FILE_PREFIX macro. + o enabled 23.976-framed PCM (2002 samples per frame) + o The size of the asdcp-test frame buffer for picture essence + may now be set from the command line (-b). The default is 4MB. + o h__Reader::ReadEKLVPacket() now tests the UL (duh) and switches + on the value, allowing plaintext and ciphertext frames to + be mixed in the file. + o Fixed error in UUID generator format. + o JP2K files now use the GenericPictureDescriptor to store + ContainerDuration and SampleRate. SampleRate is mapped + to EditRate in the PictureDescriptor struct. This fixes + the second caveat from the 0.8.13 release. + o Fxed bug in PCMParserList that was miscalculating the extent + of a PCM sample. This bug did not affect API users, it was + only present in asdcp-test. + o Fixed EditRate on PCM files (was showing sample rate) + o Fixed Encrypted Essence Container UL + o Fixed BlockAlign value for PCM essence + + - The following changes were provided by Jeff Loewenguth + - Thanks Jeff! + o Moved the DMS CryptographicFramework entry from the material + package to the source package + o Fixed erroneous Source Essence Container Label value + o Fixed broken sort of JP2K frames in JP2K parser + o Added FindFrameGOPStart() method to the MPEG2 MXFReader + o Added missing length values for EKLV packets without HMAC + o -x with JP2K essence writes to files with 6 digit names + (up from 5 digits). + o The Key ID may now be specified as an argument to asdcp-test + (-j ) + + +2004.12.30 - bug fixes + wav files v0.8.14 + o Added WAV file write to asdcp-test (uses mxflib::waveheader_t). + o Three-partition files reading properly. adscplib still writes + two-partition files. + o Changes in the mxflib WAV essence parser API had broken + asdcplib's ability to read essence from a WAV file. I have + fixed this bug, but at the expense of breaking compatibility + with older versions of mxflib. Beware! + o Removed redundant (but working) bin-text-bin conversions. + + +2004.12.23 - JPEG 2000 support v0.8.13 + o Reads/writes JPEG 2000 essence in plaintext and ciphertext + with the following caveats: + - The Picture Essence Descriptor is empty. + - Because there is no essence descriptor, the reader code + in asdcp-test has no idea how many frames are in the file + and ends with an out-of-bounds frame error. This error + is being suppressed in asdcp-test for the current release. + o Still broken when reading three-partition files. + + +2004.10.22 - fixes and UL updates v0.7.11 + WARNING: COMPATIBILITY BREAKPOINT + Files created with this and future versions of this library are + not compatible with previous versions of this library. As you + might suspect, files created with previous versions of this + library are not compatible with this and future versions. + + o h__Reader will now open a three-partitition file (untested) + o Moved DMS from Material partition to File partition + o Added length fields to appropriate places in EKLV packet + + +2004.10.22 - fixes and UL updates v0.7.10 + o fixed frame buffer handling of externally allocated buffer, + created unit test (asdcp-mem-test) + o added operator==() and operator!=() to Rational type + o fixed some type-related compiler warnings + o asdcp-test -p now works on unwrap + o updated some ULs to match documentation (thanks to Arun + for the submission) + o canonicalized line endings + + +2004.07.02 - full plaintext + ciphertext read/write v0.6.9 + o HMAC, plaintext offest and raw ciphertext read supported + o back to proper CBR index + o MPEG temporal offset working + + +2004.07.01 - plaintext + ciphertext read/write v0.5.8 + o encryption of MPEG and PCM essence supported with the + following caveats: + - no HMAC support + - no plaintext offest support + - no raw ciphertext read support + o moved to reflecting the whole KLV triplet in the CBR + index (now incompatible with mxflib, still searching + for info about what's "right") + o awaitng the following fixes/features: + - retrieve Temporal Offset from index in MPEG2::Reader + - test for correct ULs when reading frame triplets + - fix header metadata items for encrypted files + - HMAC support + - plaintext offest support + - raw ciphertext read support + + +2004.06.14 - plaintext read/write w/key generator v0.4.5 + o Project now uses OpenSSL (tested with 0.9.7d on win32, Linux, Darwin) + Use `make ASDCP_WITHOUT_OPENSSL=1` to make plaintext-only version + o Accepts interior I frames when parsing MPEG2 VES + o Improved error reporting on format errors + o Added support for encryption to asdcp-test + o Added RNG for asdcp-test (non-production use only, see notes in FortunaRNG.h) + o Implemented CBC encrypt, decrypt module + o Added partial TemporalOffset retrieval from MPEG2 parser + o Fixed win32 binmode bug + + +2004.05.12 - plaintext read/write v0.3.4 + o Full read/write of plaintext MPEG-2 VES and WAV files + (does not yet support mux from or demux to 2 channel pairs) + o Builds with autoconf-based mxflib + o Added ASDCP_ prefix to macros + o Updated documentation, fixed documentation errors + o Simplified API for MXF writer setup + o Decoupled essence parsers from MXF writers + o added raw ciphertext support to FrameBuffer + o Cleaned up Get/Set naming confusion + o Added missing const qualifiers + + +2004.04.27 - preview release v0.2.1 + o hasty release for quick review + o this release may not build with mxflib using autoconf. + o writes plaintext AS-DCP MPEG2 essence files + o tested under win32 and linux. FreeBSD and Darwin do not work + + +2004.02.04 - First release (v0.1.1): + o asdcp-test is mostly complete. It should correctly provide + access to the read and info capabilities of the library. + o The MPEG2 reader is functional but not fully tested. + o Some file format integrity tests remain to be coded. + o The PCM reader is mostly complete, I am having difficulty + getting a suitable test file from mxfwrap. + o The makefile creates a static library module. The API + will cleanly support a DSO (or DLL). Let me know if DSO + support is important to you. + o Nothing is thread safe. Thread safety was not a requirement + in the initial project definition. Thread safety may be added + above the mxflib/klvlib level. Let me know if it is important + to you. + o The files xmldict.xml and types.xml must be in the current + directory when the program is run. The files are supplied + with mxflib. Runtime location of the files is not currently + a project goal. Comments on how best to handle this situation + would be appreciated. See AS_DCP.cpp at init_mxf_types() + for a more detailed discussion. + +-- diff --git a/Timecode.cpp b/Timecode.cpp new file mode 100755 index 0000000..bc4cd55 --- /dev/null +++ b/Timecode.cpp @@ -0,0 +1,67 @@ +/* +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 Timecode.cpp + \version $Id$ + \brief Miscellaneous timecode functions +*/ + +#include + +// constants for 30fps drop-frame timecode +const ui32_t DF_FRM_IN_1 = 1798UL; // number of frames in 1 min of DF code +const ui32_t DF_FRM_IN_10 = 17982UL; // number of frames in 10 mins of DF code +const ui32_t DF_FRM_HOUR = 107892UL; // number of frames in 1 hour of DF code + + +// convert hh:mm:ss:ff at fps to frame count +ui32_t +ASDCP::tc_to_frames(ui16_t fps, ui16_t hh, ui16_t mm, ui16_t ss, ui16_t ff, bool df) +{ + if ( df ) + { + if ( fps != 30 ) + { + DefaultLogSink().Error("Drop Frame Timecode is not supported at %hu fps\n", fps); + return 0; + } + + return ( (hh * DF_FRM_HOUR) + + ((mm / 10) * DF_FRM_IN_10) + + ((mm % 10) * DF_FRM_IN_1) + + (ss * fps ) + + ff ); + } + else + { + return ((((((hh * 60UL) + mm) * 60UL) + ss ) * fps ) + ff); + } +} + + +// +// end Timecode.cpp +// diff --git a/Timecode.h b/Timecode.h new file mode 100755 index 0000000..b119838 --- /dev/null +++ b/Timecode.h @@ -0,0 +1,48 @@ +/* +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 Timecode.h + \version $Id$ + \brief Misccellaneous timecode functions +*/ + +#ifndef _TIMECODE_H_ +#define _TIMECODE_H_ + +#include + +namespace ASDCP +{ + // convert hh:mm:ss:ff at fps to frame count + ui32_t tc_to_frames(ui16_t fps, ui16_t hh, ui16_t mm, ui16_t ss, ui16_t ff, bool df = false); + +} // namespace ASDCP + +#endif // _TIMECODE_H_ + +// +// end Timecode.h +// diff --git a/hex_utils.h b/hex_utils.h new file mode 100755 index 0000000..5f51876 --- /dev/null +++ b/hex_utils.h @@ -0,0 +1,83 @@ +/* +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 hex_utils.h + \version $Id$ + \brief AS-DCP library, printable hex utilities +*/ + +#ifndef _AS_DCP_HEX_UTILS_H__ +#define _AS_DCP_HEX_UTILS_H__ + +namespace ASDCP +{ + // Convert NULL-terminated UTF-8 hexadecimal string to binary, returns 0 if + // the binary buffer was large enough to hold the result. The output parameter + // 'char_count' will contain the length of the converted string. If the output + // buffer is too small or any of the pointer arguments are NULL, the subroutine + // will return -1 and set 'char_count' to the required buffer size. No data will + // be written to 'buf' if the subroutine fails. + i32_t hex2bin(const char* str, byte_t* buf, ui32_t buf_len, ui32_t* char_count); + + // Convert a binary string to NULL-terminated UTF-8 hexadecimal, returns the buffer + // if the binary buffer was large enough to hold the result. If the output buffer + // is too small or any of the pointer arguments are NULL, the subroutine will + // return 0. + // + const char* bin2hex(const byte_t* bin_buf, ui32_t bin_len, char* str_buf, ui32_t str_len); + + + // print to a stream the contents of the buffer as hexadecimal numbers in numbered + // rows of 16-bytes each. + // + void hexdump(const byte_t* buf, ui32_t dump_len, FILE* stream = 0); + + // Return the length in bytes of a BER encoded value + inline ui32_t BER_length(const byte_t* buf) + { + if ( buf == 0 || (*buf & 0xf0) != 0x80 ) + return 0; + + return (*buf & 0x0f) + 1; + } + + // read a BER value + bool read_BER(const byte_t* buf, ui64_t* val); + + // decode a ber value and compare it to a test value + bool read_test_BER(byte_t **buf, ui64_t test_value); + + // create BER encoding of integer value + bool write_BER(byte_t* buf, ui64_t val, ui32_t ber_len = 0); + +} // namespace ASDCP + +#endif // _AS_DCP_HEX_UTILS_H__ + +// +// end hex_utils.cpp +// + 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 +#include "hex_utils.h" +#include + + +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 +#include +#include +#include + +//-------------------------------------------------------------------------------- +// 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 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& 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 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 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 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 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 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 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 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 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 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 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 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 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 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 +#include + +using namespace ASDCP; +const int KEY_SIZE_BITS = 128; + + +#ifndef ASDCP_WITHOUT_OPENSSL +#include +#include +#include + +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 + + +//------------------------------------------------------------------------------------------ + +// +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(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(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 +#include +#include + +//------------------------------------------------------------------------------------------ + +// +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::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 +#include + + +// 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 +#include +#include + +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 +#include +#include +#include + +//------------------------------------------------------------------------------------------ + +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(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 +#include +#include +#include +#include +#include +#include +#include + +using namespace ASDCP; + + +//------------------------------------------------------------------------------------------ + +class FileList : public std::list +{ + 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(this)->m_Parser = new h__SequenceParser; + + Result_t result = m_Parser->OpenRead(filename); + + if ( ASDCP_FAILURE(result) ) + const_cast(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 + + +// 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 +#include + +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 + +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 + +// 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 +#include +#include + + +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 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 +#include + +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(this)->m_Parser = new h__Parser; + + Result_t result = m_Parser->OpenRead(filename); + + if ( ASDCP_FAILURE(result) ) + const_cast(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 + +//------------------------------------------------------------------------------------------ +// + +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(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 +{ +public: + void InitWithBatch(ASDCP::MXF::Batch& Batch) + { + ASDCP::MXF::Batch::iterator i = Batch.begin(); + + for ( ; i != Batch.end(); i++ ) + insert(std::map::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::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::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::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::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 m_List; + std::map 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::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::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::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::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::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::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 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 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
    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 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 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 Identifications; + UID ContentStorage; + UL OperationalPattern; + Batch
      EssenceContainers; + Batch
        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 SliceOffset; + Array 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 DeltaEntryArray; + Batch 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 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 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(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(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 +#include +#include +#include + +// 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 ItemInfo; + typedef std::map 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 Batch : public std::vector, 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::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::iterator i = this->begin(); + for ( ; i != this->end(); i++ ) + fprintf(stream, " %s\n", (*i).ToString(identbuf)); + } + }; + + // + template + class Array : public std::list, 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::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::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 + + +//------------------------------------------------------------------------------------------ +// + +// +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 Packages; + Batch 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 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 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 Locators; + Batch 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 +#include + +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 +#include + +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 + { + 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 +#include + +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(this)->m_Parser = new h__WAVParser; + + Result_t result = m_Parser->OpenRead(filename, PictureRate); + + if ( ASDCP_FAILURE(result) ) + const_cast(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 +#include + + +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 + +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 +#include +#include + +#ifndef _WAVFILEWRITER_H_ +#define _WAVFILEWRITER_H_ + +// +class WavFileWriter +{ + ASDCP::PCM::AudioDescriptor m_ADesc; + std::list 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::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 +#include + +#include +#include + +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 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 +#include + +#include +#include +#include +#include +#include +#include +#include + +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 [-p , -e, -M, -R]|-x [-m][-S]|-g|-u|-G|-V|-h]\n\ + [-k ] [-j ] [-f ] [-d ]\n\ + [-b ] [-W] [-v [-s]] [, ...]\n\ +\n", PACKAGE); + + fprintf(stream, "\ +Major modes:\n\ + -i - show file info\n\ + -c - create AS-DCP file from input(s)\n\ + -x - 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 - write key ID instead of creating a random value\n\ + -k - 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 - Size (in bytes) of the picture frame buffer, default: 2097152 (2MB)\n\ + -f - starting frame number, default 0\n\ + -d - number of frames to process, default all\n\ + -p - 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 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 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::file_info(Options); + } +#if 0 + else if ( EssenceType == ESS_PCM_24b_48k ) + { + fputs("File essence type is PCM audio.\n", stdout); + FileInfoWrapper::file_info(Options); + } + else if ( EssenceType == ESS_JPEG_2000 ) + { + fputs("File essence type is JPEG 2000 pictures.\n", stdout); + FileInfoWrapper::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 \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 +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 + +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(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(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 + +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
          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 +#include +#include +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 +#include +#include +#include +#include +#include +#include +#include +#include + +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 +// + + + + + + + + + + -- 2.30.2