diff options
| author | mikey <mikey@cinecert.com> | 2013-04-12 23:39:31 +0000 |
|---|---|---|
| committer | mikey <> | 2013-04-12 23:39:31 +0000 |
| commit | 252740d6f7d8924c6af30e55d2da487356a0acdc (patch) | |
| tree | ba01aa630cf6cd11e7c286a60380aa35be916616 /src | |
| parent | c9e20228d6c328a0b446c1417e0082389ff045ff (diff) | |
Added atmos support and new ULs per SMPTE 429-2:2013 - see README for deets.
Diffstat (limited to 'src')
41 files changed, 5110 insertions, 203 deletions
diff --git a/src/AS_DCP.h b/src/AS_DCP.h index c0556ba..b9aba83 100755 --- a/src/AS_DCP.h +++ b/src/AS_DCP.h @@ -1,5 +1,5 @@ /* -Copyright (c) 2003-2012, John Hurst +Copyright (c) 2003-2013, John Hurst All rights reserved. Redistribution and use in source and binary forms, with or without @@ -25,7 +25,7 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /*! \file AS_DCP.h - \version $Id$ + \version $Id$ \brief AS-DCP library, public interface The asdcplib library is a set of file access objects that offer simplified @@ -87,6 +87,7 @@ This project depends upon the following libraries: #define _AS_DCP_H_ #include <KM_error.h> +#include <KM_fileio.h> #include <stdio.h> #include <stdarg.h> #include <math.h> @@ -203,13 +204,15 @@ namespace ASDCP { // 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 - ESS_PCM_24b_96k, // the file contains one or more PCM audio pairs - ESS_TIMED_TEXT, // the file contains an XML timed text document and one or more resources - ESS_JPEG_2000_S, // the file contains one or more JPEG 2000 codestream pairs (stereoscopic) + ESS_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 + ESS_PCM_24b_96k, // the file contains one or more PCM audio pairs + ESS_TIMED_TEXT, // the file contains an XML timed text document and one or more resources + ESS_JPEG_2000_S, // the file contains one or more JPEG 2000 codestream pairs (stereoscopic) + ESS_DCDATA_UNKNOWN, // the file contains one or more D-Cinema Data bytestreams + ESS_DCDATA_DOLBY_ATMOS, // the file contains one or more DolbyATMOS bytestreams }; // Determine the type of essence contained in the given MXF file. RESULT_OK @@ -253,7 +256,7 @@ namespace ASDCP { if ( Numerator == rhs.Numerator && Denominator < rhs.Denominator ) return true; return false; } - + inline bool operator>(const Rational& rhs) { if ( Numerator > rhs.Numerator ) return true; if ( Numerator == rhs.Numerator && Denominator > rhs.Denominator ) return true; @@ -365,7 +368,7 @@ namespace ASDCP { 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); @@ -407,7 +410,7 @@ namespace ASDCP { // 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. @@ -601,22 +604,22 @@ namespace ASDCP { // 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; // + Rational EditRate; // + ui32_t FrameRate; // + Rational SampleRate; // + ui8_t FrameLayout; // + ui32_t StoredWidth; // + ui32_t StoredHeight; // + Rational AspectRatio; // + ui32_t ComponentDepth; // + ui32_t HorizontalSubsampling; // + ui32_t VerticalSubsampling; // + ui8_t ColorSiting; // + ui8_t CodedContentType; // + bool LowDelay; // + ui32_t BitRate; // + ui8_t ProfileAndLevel; // + ui32_t ContainerDuration; // }; // Print VideoDescriptor to std::ostream @@ -646,7 +649,7 @@ namespace ASDCP { { Capacity(size); } - + virtual ~FrameBuffer() {} // Sets the MPEG frame type of the picture data in the frame buffer. @@ -787,6 +790,12 @@ namespace ASDCP { // out of range, or if optional decrypt or HAMC operations fail. Result_t ReadFrame(ui32_t frame_number, FrameBuffer&, AESDecContext* = 0, HMACContext* = 0) const; + // Using the index table read from the footer partition, lookup the frame number + // and return the offset into the file at which to read that frame of essence. + // Returns RESULT_INIT if the file is not open, and RESULT_RANGE if the frame number is + // out of range. + Result_t LocateFrame(ui32_t FrameNum, Kumu::fpos_t& streamOffset, i8_t& temporalOffset, i8_t& keyFrameOffset) const; + // Calculates the first frame in transport order of the GOP in which the requested // frame is located. Calls ReadFrame() to fetch the frame at the calculated position. // Returns RESULT_INIT if the file is not open. @@ -836,12 +845,12 @@ namespace ASDCP { { Rational EditRate; // rate of frame wrapping Rational AudioSamplingRate; // rate of audio sample - ui32_t Locked; // + 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 AvgBps; // + ui32_t LinkedTrackID; // ui32_t ContainerDuration; // number of frames ChannelFormat_t ChannelFormat; // audio channel arrangement }; @@ -877,7 +886,7 @@ namespace ASDCP { 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; }; @@ -986,6 +995,12 @@ namespace ASDCP { // out of range, or if optional decrypt or HAMC operations fail. Result_t ReadFrame(ui32_t frame_number, FrameBuffer&, AESDecContext* = 0, HMACContext* = 0) const; + // Using the index table read from the footer partition, lookup the frame number + // and return the offset into the file at which to read that frame of essence. + // Returns RESULT_INIT if the file is not open, and RESULT_RANGE if the frame number is + // out of range. + Result_t LocateFrame(ui32_t FrameNum, Kumu::fpos_t& streamOffset, i8_t& temporalOffset, i8_t& keyFrameOffset) const; + // Print debugging information to stream void DumpHeaderMetadata(FILE* = 0) const; void DumpIndex(FILE* = 0) const; @@ -1018,7 +1033,7 @@ namespace ASDCP { ui8_t NumberOfLayers[sizeof(ui16_t)]; ui8_t MultiCompTransform; } SGcod; - + struct { ui8_t DecompositionLevels; @@ -1073,7 +1088,7 @@ namespace ASDCP { 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; }; @@ -1125,7 +1140,7 @@ namespace ASDCP { // 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 + // 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; @@ -1134,7 +1149,7 @@ namespace ASDCP { // picture. The parser will automatically parse enough data // from the first file to provide a complete set of stream metadata for the // MXFWriter below. If the "pedantic" parameter is given and is true, the - // parser will check the metadata for each codestream and fail if a + // parser will check the metadata for each codestream and fail if a // mismatch is detected. Result_t OpenRead(const std::list<std::string>& file_list, bool pedantic = false) const; @@ -1227,6 +1242,12 @@ namespace ASDCP { // out of range, or if optional decrypt or HAMC operations fail. Result_t ReadFrame(ui32_t frame_number, FrameBuffer&, AESDecContext* = 0, HMACContext* = 0) const; + // Using the index table read from the footer partition, lookup the frame number + // and return the offset into the file at which to read that frame of essence. + // Returns RESULT_INIT if the file is not open, and RESULT_FRAME if the frame number is + // out of range. + Result_t LocateFrame(ui32_t FrameNum, Kumu::fpos_t& streamOffset, i8_t& temporalOffset, i8_t& keyFrameOffset) const; + // Print debugging information to stream void DumpHeaderMetadata(FILE* = 0) const; void DumpIndex(FILE* = 0) const; @@ -1241,7 +1262,7 @@ namespace ASDCP { SP_LEFT, SP_RIGHT }; - + struct SFrameBuffer { JP2K::FrameBuffer Left; @@ -1344,6 +1365,12 @@ namespace ASDCP { Result_t ReadFrame(ui32_t frame_number, StereoscopicPhase_t phase, FrameBuffer&, AESDecContext* = 0, HMACContext* = 0) const; + // Using the index table read from the footer partition, lookup the frame number + // and return the offset into the file at which to read that frame of essence. + // Returns RESULT_INIT if the file is not open, and RESULT_FRAME if the frame number is + // out of range. + Result_t LocateFrame(ui32_t FrameNum, Kumu::fpos_t& streamOffset, i8_t& temporalOffset, i8_t& keyFrameOffset) const; + // Print debugging information to stream void DumpHeaderMetadata(FILE* = 0) const; void DumpIndex(FILE* = 0) const; @@ -1359,7 +1386,7 @@ namespace ASDCP { struct TimedTextResourceDescriptor { byte_t ResourceID[UUIDlen]; - MIMEType_t Type; + MIMEType_t Type; TimedTextResourceDescriptor() : Type(MT_BIN) {} }; @@ -1368,7 +1395,7 @@ namespace ASDCP { struct TimedTextDescriptor { - Rational EditRate; // + Rational EditRate; // ui32_t ContainerDuration; byte_t AssetID[UUIDlen]; std::string NamespaceName; @@ -1396,7 +1423,7 @@ namespace ASDCP { FrameBuffer() { memset(m_AssetID, 0, UUIDlen); } FrameBuffer(ui32_t size) { Capacity(size); memset(m_AssetID, 0, UUIDlen); } virtual ~FrameBuffer() {} - + inline const byte_t* AssetID() const { return m_AssetID; } inline void AssetID(const byte_t* buf) { memcpy(m_AssetID, buf, UUIDlen); } inline const char* MIMEType() const { return m_MIMEType.c_str(); } @@ -1554,6 +1581,290 @@ namespace ASDCP { }; } // namespace TimedText + //--------------------------------------------------------------------------------- + // + namespace DCData + { + struct DCDataDescriptor + { + Rational EditRate; // Sample rate + ui32_t ContainerDuration; // number of frames + byte_t AssetID[UUIDlen]; // The UUID for the DCData track + byte_t DataEssenceCoding[UUIDlen]; // The coding for the data carried + }; + + // Print DCDataDescriptor to std::ostream + std::ostream& operator << (std::ostream& strm, const DCDataDescriptor& ddesc); + // Print debugging information to stream (stderr default) + void DCDataDescriptorDump(const DCDataDescriptor&, FILE* = 0); + + // + class FrameBuffer : public ASDCP::FrameBuffer + { + public: + FrameBuffer() {} + FrameBuffer(ui32_t size) { Capacity(size); } + virtual ~FrameBuffer() {} + + // Print debugging information to stream (stderr default) + void Dump(FILE* = 0, ui32_t dump_bytes = 0) const; + }; + + // An object which opens and reads a DC Data file. The file is expected + // to contain exactly one complete frame of DC data essence as an unwrapped (raw) + // byte stream. + class BytestreamParser + { + class h__BytestreamParser; + mem_ptr<h__BytestreamParser> m_Parser; + ASDCP_NO_COPY_CONSTRUCT(BytestreamParser); + + public: + BytestreamParser(); + virtual ~BytestreamParser(); + + // Opens a file for reading, parses enough data to provide a complete + // set of stream metadata for the MXFWriter below. + // The frame buffer's PlaintextOffset parameter will be set to the first + // byte of the data segment. Set this value to zero if you want + // encrypted headers. + Result_t OpenReadFrame(const char* filename, FrameBuffer&) const; + + // Fill a DCDataDescriptor struct with the values from the file's bytestream. + // Returns RESULT_INIT if the file is not open. + Result_t FillDCDataDescriptor(DCDataDescriptor&) const; + }; + + // An object which reads a sequence of files containing DC Data. + class SequenceParser + { + class h__SequenceParser; + mem_ptr<h__SequenceParser> m_Parser; + ASDCP_NO_COPY_CONSTRUCT(SequenceParser); + + public: + SequenceParser(); + virtual ~SequenceParser(); + + // Opens a directory for reading. The directory is expected to contain one or + // more files, each containing the bytestream for exactly one frame. The files + // must be named such that the frames are in temporal order when sorted + // alphabetically by filename. + Result_t OpenRead(const char* filename) const; + + // Opens a file sequence for reading. The sequence is expected to contain one or + // more filenames, each naming a file containing the bytestream for exactly one + // frame. + Result_t OpenRead(const std::list<std::string>& file_list) const; + + // Fill a DCDataDescriptor struct with default values. + // Returns RESULT_INIT if the directory is not open. + Result_t FillDCDataDescriptor(DCDataDescriptor&) const; + + // Rewind the directory to the beginning. + Result_t Reset() const; + + // Reads the next sequential frame in the directory and places it in the + // frame buffer. Fails if the buffer is too small or the direcdtory + // contains no more files. + // The frame buffer's PlaintextOffset parameter will be set to the first + // byte of the data segment. Set this value to zero if you want + // encrypted headers. + Result_t ReadFrame(FrameBuffer&) const; + }; + + // + class MXFWriter + { + class h__Writer; + mem_ptr<h__Writer> m_Writer; + ASDCP_NO_COPY_CONSTRUCT(MXFWriter); + + public: + MXFWriter(); + virtual ~MXFWriter(); + + // Warning: direct manipulation of MXF structures can interfere + // with the normal operation of the wrapper. Caveat emptor! + virtual MXF::OPAtomHeader& OPAtomHeader(); + virtual MXF::OPAtomIndexFooter& OPAtomIndexFooter(); + + // 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 DCDataDescriptor&, ui32_t HeaderSize = 16384); + + // Writes a frame of essence to the MXF file. If the optional AESEncContext + // argument is present, the essence is encrypted prior to writing. + // Fails if the file is not open, is finalized, or an operating system + // error occurs. + Result_t WriteFrame(const FrameBuffer&, AESEncContext* = 0, HMACContext* = 0); + + // Closes the MXF file, writing the index and revised header. + Result_t Finalize(); + }; + + // + class MXFReader + { + class h__Reader; + mem_ptr<h__Reader> m_Reader; + ASDCP_NO_COPY_CONSTRUCT(MXFReader); + + public: + MXFReader(); + virtual ~MXFReader(); + + // Warning: direct manipulation of MXF structures can interfere + // with the normal operation of the wrapper. Caveat emptor! + virtual MXF::OPAtomHeader& OPAtomHeader(); + virtual MXF::OPAtomIndexFooter& OPAtomIndexFooter(); + + // 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 DCDataDescriptor struct with the values from the file's header. + // Returns RESULT_INIT if the file is not open. + Result_t FillDCDataDescriptor(DCDataDescriptor&) const; + + // Fill a WriterInfo struct with the values from the file's header. + // Returns RESULT_INIT if the file is not open. + Result_t FillWriterInfo(WriterInfo&) const; + + // Reads a frame of essence from the MXF file. If the optional AESEncContext + // argument is present, the essence is decrypted after reading. If the MXF + // file is encrypted and the AESDecContext argument is NULL, the frame buffer + // will contain the ciphertext frame data. If the HMACContext argument is + // not NULL, the HMAC will be calculated (if the file supports it). + // Returns RESULT_INIT if the file is not open, failure if the frame number is + // out of range, or if optional decrypt or HAMC operations fail. + Result_t ReadFrame(ui32_t frame_number, FrameBuffer&, AESDecContext* = 0, HMACContext* = 0) const; + + // Using the index table read from the footer partition, lookup the frame number + // and return the offset into the file at which to read that frame of essence. + // Returns RESULT_INIT if the file is not open, and RESULT_RANGE if the frame number is + // out of range. + Result_t LocateFrame(ui32_t FrameNum, Kumu::fpos_t& streamOffset, i8_t& temporalOffset, i8_t& keyFrameOffset) const; + + // Print debugging information to stream + void DumpHeaderMetadata(FILE* = 0) const; + void DumpIndex(FILE* = 0) const; + }; + + } // namespace DCData + + //--------------------------------------------------------------------------------- + // + namespace ATMOS + { + struct AtmosDescriptor : public DCData::DCDataDescriptor + { + ui32_t FirstFrame; // Frame number of the frame to align with the FFOA of the picture track + ui16_t MaxChannelCount; // Max number of channels in bitstream + ui16_t MaxObjectCount; // Max number of objects in bitstream + byte_t AtmosID[UUIDlen]; // UUID of Atmos Project + ui8_t AtmosVersion; // ATMOS Coder Version used to create bitstream + }; + + // Print AtmosDescriptor to std::ostream + std::ostream& operator << (std::ostream& strm, const AtmosDescriptor& adesc); + // Print debugging information to stream (stderr default) + void AtmosDescriptorDump(const AtmosDescriptor&, FILE* = 0); + // Determine if a file is a raw atmos file + bool IsDolbyAtmos(const char* filename); + + // + class MXFWriter + { + + class h__Writer; + mem_ptr<h__Writer> m_Writer; + ASDCP_NO_COPY_CONSTRUCT(MXFWriter); + + public: + MXFWriter(); + virtual ~MXFWriter(); + + // Warning: direct manipulation of MXF structures can interfere + // with the normal operation of the wrapper. Caveat emptor! + virtual MXF::OPAtomHeader& OPAtomHeader(); + virtual MXF::OPAtomIndexFooter& OPAtomIndexFooter(); + + // 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 AtmosDescriptor&, ui32_t HeaderSize = 16384); + + // Writes a frame of essence to the MXF file. If the optional AESEncContext + // argument is present, the essence is encrypted prior to writing. + // Fails if the file is not open, is finalized, or an operating system + // error occurs. + Result_t WriteFrame(const DCData::FrameBuffer&, AESEncContext* = 0, HMACContext* = 0); + + // Closes the MXF file, writing the index and revised header. + Result_t Finalize(); + }; + + // + class MXFReader + { + class h__Reader; + mem_ptr<h__Reader> m_Reader; + ASDCP_NO_COPY_CONSTRUCT(MXFReader); + + public: + MXFReader(); + virtual ~MXFReader(); + + // Warning: direct manipulation of MXF structures can interfere + // with the normal operation of the wrapper. Caveat emptor! + virtual MXF::OPAtomHeader& OPAtomHeader(); + virtual MXF::OPAtomIndexFooter& OPAtomIndexFooter(); + + // 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 AtmosDescriptor struct with the values from the file's header. + // Returns RESULT_INIT if the file is not open. + Result_t FillAtmosDescriptor(AtmosDescriptor&) const; + + // Fill a WriterInfo struct with the values from the file's header. + // Returns RESULT_INIT if the file is not open. + Result_t FillWriterInfo(WriterInfo&) const; + + // Reads a frame of essence from the MXF file. If the optional AESEncContext + // argument is present, the essence is decrypted after reading. If the MXF + // file is encrypted and the AESDecContext argument is NULL, the frame buffer + // will contain the ciphertext frame data. If the HMACContext argument is + // not NULL, the HMAC will be calculated (if the file supports it). + // Returns RESULT_INIT if the file is not open, failure if the frame number is + // out of range, or if optional decrypt or HAMC operations fail. + Result_t ReadFrame(ui32_t frame_number, DCData::FrameBuffer&, AESDecContext* = 0, HMACContext* = 0) const; + + // Using the index table read from the footer partition, lookup the frame number + // and return the offset into the file at which to read that frame of essence. + // Returns RESULT_INIT if the file is not open, and RESULT_RANGE if the frame number is + // out of range. + Result_t LocateFrame(ui32_t FrameNum, Kumu::fpos_t& streamOffset, i8_t& temporalOffset, i8_t& keyFrameOffset) const; + + // Print debugging information to stream + void DumpHeaderMetadata(FILE* = 0) const; + void DumpIndex(FILE* = 0) const; + }; + + } // namespace ATMOS + + } // namespace ASDCP diff --git a/src/AS_DCP_ATMOS.cpp b/src/AS_DCP_ATMOS.cpp new file mode 100644 index 0000000..da23a0a --- /dev/null +++ b/src/AS_DCP_ATMOS.cpp @@ -0,0 +1,455 @@ +/* +Copyright (c) 2004-2013, 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_ATMOS.cpp + \version $Id$ + \brief AS-DCP library, Dolby Atmos essence reader and writer implementation +*/ + + +#include <iostream> + +#include "AS_DCP.h" +#include "AS_DCP_DCData_internal.h" +#include "AS_DCP_internal.h" + +namespace ASDCP +{ +namespace ATMOS +{ + static std::string ATMOS_PACKAGE_LABEL = "File Package: SMPTE 382M frame wrapping of Dolby ATMOS data"; + static std::string ATMOS_DEF_LABEL = "Dolby ATMOS Data Track"; + static byte_t ATMOS_ESSENCE_CODING[SMPTE_UL_LENGTH] = { 0x06, 0x0e, 0x2b, 0x34, 0x04, 0x01, 0x01, 0x05, + 0x0e, 0x09, 0x06, 0x04, 0x00, 0x00, 0x00, 0x00 }; +} // namespace ATMOS +} // namespace ASDCP + +// +std::ostream& +ASDCP::ATMOS::operator << (std::ostream& strm, const AtmosDescriptor& ADesc) +{ + char str_buf[40]; + strm << " EditRate: " << ADesc.EditRate.Numerator << "/" << ADesc.EditRate.Denominator << std::endl; + strm << " ContainerDuration: " << (unsigned) ADesc.ContainerDuration << std::endl; + strm << " DataEssenceCoding: " << UL(ADesc.DataEssenceCoding).EncodeString(str_buf, 40) << std::endl; + strm << " AtmosVersion: " << (unsigned) ADesc.AtmosVersion << std::endl; + strm << " MaxChannelCount: " << (unsigned) ADesc.MaxChannelCount << std::endl; + strm << " MaxObjectCount: " << (unsigned) ADesc.MaxObjectCount << std::endl; + strm << " AtmosID: " << UUID(ADesc.AtmosID).EncodeString(str_buf, 40) << std::endl; + strm << " FirstFrame: " << (unsigned) ADesc.FirstFrame << std::endl; + return strm; +} + +// +void +ASDCP::ATMOS::AtmosDescriptorDump(const AtmosDescriptor& ADesc, FILE* stream) +{ + char str_buf[40]; + char atmosID_buf[40]; + if ( stream == 0 ) + stream = stderr; + + fprintf(stream, "\ + EditRate: %d/%d\n\ + ContainerDuration: %u\n\ + DataEssenceCoding: %s\n\ + AtmosVersion: %u\n\ + MaxChannelCount: %u\n\ + MaxObjectCount: %u\n\ + AtmosID: %s\n\ + FirsFrame: %u\n", + ADesc.EditRate.Numerator, ADesc.EditRate.Denominator, + ADesc.ContainerDuration, + UL(ADesc.DataEssenceCoding).EncodeString(str_buf, 40), + ADesc.AtmosVersion, + ADesc.MaxChannelCount, + ADesc.MaxObjectCount, + UUID(ADesc.AtmosID).EncodeString(atmosID_buf, 40), + ADesc.FirstFrame); +} + +// +bool +ASDCP::ATMOS::IsDolbyAtmos(const char* filename) +{ + // TODO + // For now use an atmos extension + bool result = (0 == (std::string("atmos").compare(Kumu::PathGetExtension(std::string(filename))))); + return result; +} + + +//------------------------------------------------------------------------------------------ + + +class ASDCP::ATMOS::MXFReader::h__Reader : public ASDCP::DCData::h__Reader +{ + MXF::DolbyAtmosSubDescriptor* m_EssenceSubDescriptor; + + ASDCP_NO_COPY_CONSTRUCT(h__Reader); + h__Reader(); + + public: + AtmosDescriptor m_ADesc; + + h__Reader(const Dictionary& d) : DCData::h__Reader(d), m_EssenceSubDescriptor(NULL), + m_ADesc() {} + ~h__Reader() {} + Result_t OpenRead(const char*); + Result_t MD_to_Atmos_ADesc(ATMOS::AtmosDescriptor& ADesc); +}; + +ASDCP::Result_t +ASDCP::ATMOS::MXFReader::h__Reader::MD_to_Atmos_ADesc(ATMOS::AtmosDescriptor& ADesc) +{ + ASDCP_TEST_NULL(m_EssenceSubDescriptor); + Result_t result = MD_to_DCData_DDesc(ADesc); + if( ASDCP_SUCCESS(result) ) + { + MXF::DolbyAtmosSubDescriptor* ADescObj = m_EssenceSubDescriptor; + ADesc.MaxChannelCount = ADescObj->MaxChannelCount; + ADesc.MaxObjectCount = ADescObj->MaxObjectCount; + ::memcpy(ADesc.AtmosID, ADescObj->AtmosID.Value(), UUIDlen); + ADesc.AtmosVersion = ADescObj->AtmosVersion; + ADesc.FirstFrame = ADescObj->FirstFrame; + } + return result; +} + +// +// +ASDCP::Result_t +ASDCP::ATMOS::MXFReader::h__Reader::OpenRead(const char* filename) +{ + Result_t result = DCData::h__Reader::OpenRead(filename); + + if( ASDCP_SUCCESS(result) ) + { + + if (NULL == m_EssenceSubDescriptor) + { + InterchangeObject* iObj = NULL; + result = m_HeaderPart.GetMDObjectByType(OBJ_TYPE_ARGS(DolbyAtmosSubDescriptor), &iObj); + m_EssenceSubDescriptor = static_cast<MXF::DolbyAtmosSubDescriptor*>(iObj); + } + + if ( ASDCP_SUCCESS(result) ) + { + result = MD_to_Atmos_ADesc(m_ADesc); + } + } + + return result; +} + + +//------------------------------------------------------------------------------------------ + +ASDCP::ATMOS::MXFReader::MXFReader() +{ + m_Reader = new h__Reader(DefaultSMPTEDict()); +} + + +ASDCP::ATMOS::MXFReader::~MXFReader() +{ + if ( m_Reader && m_Reader->m_File.IsOpen() ) + m_Reader->Close(); +} + +// Warning: direct manipulation of MXF structures can interfere +// with the normal operation of the wrapper. Caveat emptor! +// +ASDCP::MXF::OPAtomHeader& +ASDCP::ATMOS::MXFReader::OPAtomHeader() +{ + if ( m_Reader.empty() ) + { + assert(g_OPAtomHeader); + return *g_OPAtomHeader; + } + + return m_Reader->m_HeaderPart; +} + +// Warning: direct manipulation of MXF structures can interfere +// with the normal operation of the wrapper. Caveat emptor! +// +ASDCP::MXF::OPAtomIndexFooter& +ASDCP::ATMOS::MXFReader::OPAtomIndexFooter() +{ + if ( m_Reader.empty() ) + { + assert(g_OPAtomIndexFooter); + return *g_OPAtomIndexFooter; + } + + return m_Reader->m_FooterPart; +} + +// Open the file for reading. The file must exist. Returns error if the +// operation cannot be completed. +ASDCP::Result_t +ASDCP::ATMOS::MXFReader::OpenRead(const char* filename) const +{ + return m_Reader->OpenRead(filename); +} + +// +ASDCP::Result_t +ASDCP::ATMOS::MXFReader::ReadFrame(ui32_t FrameNum, DCData::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::ATMOS::MXFReader::LocateFrame(ui32_t FrameNum, Kumu::fpos_t& streamOffset, i8_t& temporalOffset, i8_t& keyFrameOffset) const +{ + return m_Reader->LocateFrame(FrameNum, streamOffset, temporalOffset, keyFrameOffset); +} + + +// Fill the struct with the values from the file's header. +// Returns RESULT_INIT if the file is not open. +ASDCP::Result_t +ASDCP::ATMOS::MXFReader::FillAtmosDescriptor(AtmosDescriptor& 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::ATMOS::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::ATMOS::MXFReader::DumpHeaderMetadata(FILE* stream) const +{ + if ( m_Reader->m_File.IsOpen() ) + m_Reader->m_HeaderPart.Dump(stream); +} + +// +void +ASDCP::ATMOS::MXFReader::DumpIndex(FILE* stream) const +{ + if ( m_Reader->m_File.IsOpen() ) + m_Reader->m_FooterPart.Dump(stream); +} + +// +ASDCP::Result_t +ASDCP::ATMOS::MXFReader::Close() const +{ + if ( m_Reader && m_Reader->m_File.IsOpen() ) + { + m_Reader->Close(); + return RESULT_OK; + } + + return RESULT_INIT; +} + + +//------------------------------------------------------------------------------------------ + +// +class ASDCP::ATMOS::MXFWriter::h__Writer : public DCData::h__Writer +{ + MXF::DolbyAtmosSubDescriptor* m_EssenceSubDescriptor; + + ASDCP_NO_COPY_CONSTRUCT(h__Writer); + h__Writer(); + + public: + AtmosDescriptor m_ADesc; + + h__Writer(const Dictionary& d) : DCData::h__Writer(d), + m_EssenceSubDescriptor(NULL), m_ADesc() {} + + ~h__Writer(){} + + Result_t OpenWrite(const char*, ui32_t HeaderSize, const AtmosDescriptor& ADesc); + Result_t Atmos_ADesc_to_MD(const AtmosDescriptor& ADesc); +}; + +// +ASDCP::Result_t +ASDCP::ATMOS::MXFWriter::h__Writer::Atmos_ADesc_to_MD(const AtmosDescriptor& ADesc) +{ + ASDCP_TEST_NULL(m_EssenceDescriptor); + ASDCP_TEST_NULL(m_EssenceSubDescriptor); + MXF::DolbyAtmosSubDescriptor * ADescObj = m_EssenceSubDescriptor; + ADescObj->MaxChannelCount = ADesc.MaxChannelCount; + ADescObj->MaxObjectCount = ADesc.MaxObjectCount; + ADescObj->AtmosID.Set(ADesc.AtmosID); + ADescObj->AtmosVersion = ADesc.AtmosVersion; + ADescObj->FirstFrame = ADesc.FirstFrame; + return RESULT_OK; +} + +// +ASDCP::Result_t +ASDCP::ATMOS::MXFWriter::h__Writer::OpenWrite(const char* filename, ui32_t HeaderSize, const AtmosDescriptor& ADesc) +{ + + m_EssenceSubDescriptor = new DolbyAtmosSubDescriptor(m_Dict); + DCData::SubDescriptorList_t subDescriptors; + subDescriptors.push_back(m_EssenceSubDescriptor); + Result_t result = DCData::h__Writer::OpenWrite(filename, HeaderSize, subDescriptors); + if ( ASDCP_FAILURE(result) ) + delete m_EssenceSubDescriptor; + + if ( ASDCP_SUCCESS(result) ) + { + m_ADesc = ADesc; + memcpy(m_ADesc.DataEssenceCoding, ATMOS_ESSENCE_CODING, SMPTE_UL_LENGTH); + result = Atmos_ADesc_to_MD(m_ADesc); + } + + return result; +} + + + + +//------------------------------------------------------------------------------------------ + +ASDCP::ATMOS::MXFWriter::MXFWriter() +{ +} + +ASDCP::ATMOS::MXFWriter::~MXFWriter() +{ +} + +// Warning: direct manipulation of MXF structures can interfere +// with the normal operation of the wrapper. Caveat emptor! +// +ASDCP::MXF::OPAtomHeader& +ASDCP::ATMOS::MXFWriter::OPAtomHeader() +{ + if ( m_Writer.empty() ) + { + assert(g_OPAtomHeader); + return *g_OPAtomHeader; + } + + return m_Writer->m_HeaderPart; +} + +// Warning: direct manipulation of MXF structures can interfere +// with the normal operation of the wrapper. Caveat emptor! +// +ASDCP::MXF::OPAtomIndexFooter& +ASDCP::ATMOS::MXFWriter::OPAtomIndexFooter() +{ + if ( m_Writer.empty() ) + { + assert(g_OPAtomIndexFooter); + return *g_OPAtomIndexFooter; + } + + return m_Writer->m_FooterPart; +} + +// Open the file for writing. The file must not exist. Returns error if +// the operation cannot be completed. +ASDCP::Result_t +ASDCP::ATMOS::MXFWriter::OpenWrite(const char* filename, const WriterInfo& Info, + const AtmosDescriptor& ADesc, ui32_t HeaderSize) +{ + if ( Info.LabelSetType != LS_MXF_SMPTE ) + { + DefaultLogSink().Error("Atmos support requires LS_MXF_SMPTE\n"); + return RESULT_FORMAT; + } + + m_Writer = new h__Writer(DefaultSMPTEDict()); + m_Writer->m_Info = Info; + + Result_t result = m_Writer->OpenWrite(filename, HeaderSize, ADesc); + + if ( ASDCP_SUCCESS(result) ) + result = m_Writer->SetSourceStream(ADesc, ATMOS_ESSENCE_CODING, ATMOS_PACKAGE_LABEL, + ATMOS_DEF_LABEL); + + 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::ATMOS::MXFWriter::WriteFrame(const DCData::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::ATMOS::MXFWriter::Finalize() +{ + if ( m_Writer.empty() ) + return RESULT_INIT; + + return m_Writer->Finalize(); +} + + +// +// end AS_DCP_ATMOS.cpp +// + + diff --git a/src/AS_DCP_DCData.cpp b/src/AS_DCP_DCData.cpp new file mode 100644 index 0000000..53e4497 --- /dev/null +++ b/src/AS_DCP_DCData.cpp @@ -0,0 +1,554 @@ +/* +Copyright (c) 2004-2013, 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_DCData.cpp + \version $Id$ + \brief AS-DCP library, Dcinema generic data essence reader and writer implementation +*/ + +#include <iostream> + +#include "AS_DCP.h" +#include "AS_DCP_DCData_internal.h" +#include "AS_DCP_internal.h" + +namespace ASDCP +{ +namespace DCData +{ + static std::string DC_DATA_PACKAGE_LABEL = "File Package: SMPTE 382M frame wrapping of D-Cinema Generic data"; + static std::string DC_DATA_DEF_LABEL = "D-Cinema Generic Data Track"; +} // namespace DCData +} // namespace ASDCP + +// +std::ostream& +ASDCP::DCData::operator << (std::ostream& strm, const DCDataDescriptor& DDesc) +{ + char str_buf[40]; + strm << " EditRate: " << DDesc.EditRate.Numerator << "/" << DDesc.EditRate.Denominator << std::endl; + strm << " ContainerDuration: " << (unsigned) DDesc.ContainerDuration << std::endl; + strm << " DataEssenceCoding: " << UL(DDesc.DataEssenceCoding).EncodeString(str_buf, 40) << std::endl; + return strm; +} + +// +void +ASDCP::DCData::DCDataDescriptorDump(const DCDataDescriptor& DDesc, FILE* stream) +{ + char str_buf[40]; + if ( stream == 0 ) + stream = stderr; + + fprintf(stream, "\ + EditRate: %d/%d\n\ + ContainerDuration: %u\n\ + DataEssenceCoding: %s\n", + DDesc.EditRate.Numerator, DDesc.EditRate.Denominator, + DDesc.ContainerDuration, + UL(DDesc.DataEssenceCoding).EncodeString(str_buf, 40)); +} + + +//------------------------------------------------------------------------------------------ + + +ASDCP::Result_t +ASDCP::DCData::h__Reader::MD_to_DCData_DDesc(DCData::DCDataDescriptor& DDesc) +{ + ASDCP_TEST_NULL(m_EssenceDescriptor); + MXF::DCDataDescriptor* DDescObj = m_EssenceDescriptor; + DDesc.EditRate = DDescObj->SampleRate; + assert(DDescObj->ContainerDuration <= 0xFFFFFFFFL); + DDesc.ContainerDuration = static_cast<ui32_t>(DDescObj->ContainerDuration); + memcpy(DDesc.DataEssenceCoding, DDescObj->DataEssenceCoding.Value(), SMPTE_UL_LENGTH); + return RESULT_OK; +} + +// +// +ASDCP::Result_t +ASDCP::DCData::h__Reader::OpenRead(const char* filename) +{ + Result_t result = OpenMXFRead(filename); + + if( ASDCP_SUCCESS(result) ) + { + if (NULL == m_EssenceDescriptor) + { + InterchangeObject* iObj = NULL; + result = m_HeaderPart.GetMDObjectByType(OBJ_TYPE_ARGS(DCDataDescriptor), &iObj); + m_EssenceDescriptor = static_cast<MXF::DCDataDescriptor*>(iObj); + } + + if ( ASDCP_SUCCESS(result) ) + { + result = MD_to_DCData_DDesc(m_DDesc); + } + } + + // check for sample/frame rate sanity + if ( ASDCP_SUCCESS(result) + && m_DDesc.EditRate != EditRate_24 + && m_DDesc.EditRate != EditRate_25 + && m_DDesc.EditRate != EditRate_30 + && m_DDesc.EditRate != EditRate_48 + && m_DDesc.EditRate != EditRate_50 + && m_DDesc.EditRate != EditRate_60 + && m_DDesc.EditRate != EditRate_96 + && m_DDesc.EditRate != EditRate_100 + && m_DDesc.EditRate != EditRate_120 ) + { + DefaultLogSink().Error("DC Data file EditRate is not a supported value: %d/%d\n", // lu + m_DDesc.EditRate.Numerator, m_DDesc.EditRate.Denominator); + + return RESULT_FORMAT; + } + + if( ASDCP_SUCCESS(result) ) + result = InitMXFIndex(); + + if( ASDCP_SUCCESS(result) ) + result = InitInfo(); + + return result; +} + +// +// +ASDCP::Result_t +ASDCP::DCData::h__Reader::ReadFrame(ui32_t FrameNum, FrameBuffer& FrameBuf, + AESDecContext* Ctx, HMACContext* HMAC) +{ + if ( ! m_File.IsOpen() ) + return RESULT_INIT; + + assert(m_Dict); + return ReadEKLVFrame(FrameNum, FrameBuf, m_Dict->ul(MDD_DCDataEssence), Ctx, HMAC); +} + + +// +//------------------------------------------------------------------------------------------ + +class ASDCP::DCData::MXFReader::h__Reader : public DCData::h__Reader +{ + ASDCP_NO_COPY_CONSTRUCT(h__Reader); + h__Reader(); + + public: + h__Reader(const Dictionary& d) : DCData::h__Reader(d) {} +}; + + +//------------------------------------------------------------------------------------------ + + +// +void +ASDCP::DCData::FrameBuffer::Dump(FILE* stream, ui32_t dump_len) const +{ + if ( stream == 0 ) + stream = stderr; + + fprintf(stream, "Frame: %06u, %7u bytes\n", m_FrameNumber, m_Size); + + if ( dump_len > 0 ) + Kumu::hexdump(m_Data, dump_len, stream); +} + + +//------------------------------------------------------------------------------------------ + +ASDCP::DCData::MXFReader::MXFReader() +{ + m_Reader = new h__Reader(DefaultSMPTEDict()); +} + + +ASDCP::DCData::MXFReader::~MXFReader() +{ + if ( m_Reader && m_Reader->m_File.IsOpen() ) + m_Reader->Close(); +} + +// Warning: direct manipulation of MXF structures can interfere +// with the normal operation of the wrapper. Caveat emptor! +// +ASDCP::MXF::OPAtomHeader& +ASDCP::DCData::MXFReader::OPAtomHeader() +{ + if ( m_Reader.empty() ) + { + assert(g_OPAtomHeader); + return *g_OPAtomHeader; + } + + return m_Reader->m_HeaderPart; +} + +// Warning: direct manipulation of MXF structures can interfere +// with the normal operation of the wrapper. Caveat emptor! +// +ASDCP::MXF::OPAtomIndexFooter& +ASDCP::DCData::MXFReader::OPAtomIndexFooter() +{ + if ( m_Reader.empty() ) + { + assert(g_OPAtomIndexFooter); + return *g_OPAtomIndexFooter; + } + + return m_Reader->m_FooterPart; +} + +// Open the file for reading. The file must exist. Returns error if the +// operation cannot be completed. +ASDCP::Result_t +ASDCP::DCData::MXFReader::OpenRead(const char* filename) const +{ + return m_Reader->OpenRead(filename); +} + +// +ASDCP::Result_t +ASDCP::DCData::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::DCData::MXFReader::LocateFrame(ui32_t FrameNum, Kumu::fpos_t& streamOffset, i8_t& temporalOffset, i8_t& keyFrameOffset) const +{ + return m_Reader->LocateFrame(FrameNum, streamOffset, temporalOffset, keyFrameOffset); +} + + +// Fill the struct with the values from the file's header. +// Returns RESULT_INIT if the file is not open. +ASDCP::Result_t +ASDCP::DCData::MXFReader::FillDCDataDescriptor(DCDataDescriptor& DDesc) const +{ + if ( m_Reader && m_Reader->m_File.IsOpen() ) + { + DDesc = m_Reader->m_DDesc; + 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::DCData::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::DCData::MXFReader::DumpHeaderMetadata(FILE* stream) const +{ + if ( m_Reader->m_File.IsOpen() ) + m_Reader->m_HeaderPart.Dump(stream); +} + + +// +void +ASDCP::DCData::MXFReader::DumpIndex(FILE* stream) const +{ + if ( m_Reader->m_File.IsOpen() ) + m_Reader->m_FooterPart.Dump(stream); +} + +// +ASDCP::Result_t +ASDCP::DCData::MXFReader::Close() const +{ + if ( m_Reader && m_Reader->m_File.IsOpen() ) + { + m_Reader->Close(); + return RESULT_OK; + } + + return RESULT_INIT; +} + + +//------------------------------------------------------------------------------------------ + + +// +ASDCP::Result_t +ASDCP::DCData::h__Writer::DCData_DDesc_to_MD(DCData::DCDataDescriptor& DDesc) +{ + ASDCP_TEST_NULL(m_EssenceDescriptor); + MXF::DCDataDescriptor* DDescObj = static_cast<MXF::DCDataDescriptor *>(m_EssenceDescriptor); + + DDescObj->SampleRate = DDesc.EditRate; + DDescObj->ContainerDuration = DDesc.ContainerDuration; + DDescObj->DataEssenceCoding.Set(DDesc.DataEssenceCoding); + + return RESULT_OK; +} + +// +ASDCP::Result_t +ASDCP::DCData::h__Writer::OpenWrite(char const* filename, ui32_t HeaderSize, + const SubDescriptorList_t& subDescriptors) +{ + if ( ! m_State.Test_BEGIN() ) + return RESULT_STATE; + + Result_t result = m_File.OpenWrite(filename); + + if ( ASDCP_SUCCESS(result) ) + { + m_HeaderSize = HeaderSize; + m_EssenceDescriptor = new MXF::DCDataDescriptor(m_Dict); + SubDescriptorList_t::const_iterator sDObj; + SubDescriptorList_t::const_iterator lastDescriptor = subDescriptors.end(); + for (sDObj = subDescriptors.begin(); sDObj != lastDescriptor; ++sDObj) + { + m_EssenceSubDescriptorList.push_back(*sDObj); + GenRandomValue((*sDObj)->InstanceUID); + m_EssenceDescriptor->SubDescriptors.push_back((*sDObj)->InstanceUID); + } + result = m_State.Goto_INIT(); + } + + return result; +} + +// +ASDCP::Result_t +ASDCP::DCData::h__Writer::SetSourceStream(DCDataDescriptor const& DDesc, + const byte_t * essenceCoding, + const std::string& packageLabel, + const std::string& defLabel) +{ + if ( ! m_State.Test_INIT() ) + return RESULT_STATE; + + if ( DDesc.EditRate != EditRate_24 + && DDesc.EditRate != EditRate_25 + && DDesc.EditRate != EditRate_30 + && DDesc.EditRate != EditRate_48 + && DDesc.EditRate != EditRate_50 + && DDesc.EditRate != EditRate_60 + && DDesc.EditRate != EditRate_96 + && DDesc.EditRate != EditRate_100 + && DDesc.EditRate != EditRate_120 ) + { + DefaultLogSink().Error("DCDataDescriptor.EditRate is not a supported value: %d/%d\n", + DDesc.EditRate.Numerator, DDesc.EditRate.Denominator); + return RESULT_RAW_FORMAT; + } + + assert(m_Dict); + m_DDesc = DDesc; + if (NULL != essenceCoding) + memcpy(m_DDesc.DataEssenceCoding, essenceCoding, SMPTE_UL_LENGTH); + Result_t result = DCData_DDesc_to_MD(m_DDesc); + + if ( ASDCP_SUCCESS(result) ) + { + memcpy(m_EssenceUL, m_Dict->ul(MDD_DCDataEssence), SMPTE_UL_LENGTH); + m_EssenceUL[SMPTE_UL_LENGTH-1] = 1; // first (and only) essence container + result = m_State.Goto_READY(); + } + + if ( ASDCP_SUCCESS(result) ) + { + ui32_t TCFrameRate = m_DDesc.EditRate.Numerator; + + result = WriteMXFHeader(packageLabel, UL(m_Dict->ul(MDD_DCDataWrapping)), + defLabel, UL(m_EssenceUL), UL(m_Dict->ul(MDD_DataDataDef)), + m_DDesc.EditRate, TCFrameRate); + } + + return result; +} + +// +ASDCP::Result_t +ASDCP::DCData::h__Writer::WriteFrame(const FrameBuffer& FrameBuf, + ASDCP::AESEncContext* Ctx, ASDCP::HMACContext* HMAC) +{ + Result_t result = RESULT_OK; + + if ( m_State.Test_READY() ) + result = m_State.Goto_RUNNING(); // first time through + + ui64_t StreamOffset = m_StreamOffset; + + if ( ASDCP_SUCCESS(result) ) + result = WriteEKLVPacket(FrameBuf, m_EssenceUL, Ctx, HMAC); + + if ( ASDCP_SUCCESS(result) ) + { + IndexTableSegment::IndexEntry Entry; + Entry.StreamOffset = StreamOffset; + m_FooterPart.PushIndexEntry(Entry); + m_FramesWritten++; + } + return result; +} + +// Closes the MXF file, writing the index and other closing information. +// +ASDCP::Result_t +ASDCP::DCData::h__Writer::Finalize() +{ + if ( ! m_State.Test_RUNNING() ) + return RESULT_STATE; + + m_State.Goto_FINAL(); + + return WriteMXFFooter(); +} + + +// +//------------------------------------------------------------------------------------------ + + +class ASDCP::DCData::MXFWriter::h__Writer : public DCData::h__Writer +{ + ASDCP_NO_COPY_CONSTRUCT(h__Writer); + h__Writer(); + + public: + h__Writer(const Dictionary& d) : DCData::h__Writer(d) {} +}; + + +//------------------------------------------------------------------------------------------ + +ASDCP::DCData::MXFWriter::MXFWriter() +{ +} + +ASDCP::DCData::MXFWriter::~MXFWriter() +{ +} + +// Warning: direct manipulation of MXF structures can interfere +// with the normal operation of the wrapper. Caveat emptor! +// +ASDCP::MXF::OPAtomHeader& +ASDCP::DCData::MXFWriter::OPAtomHeader() +{ + if ( m_Writer.empty() ) + { + assert(g_OPAtomHeader); + return *g_OPAtomHeader; + } + + return m_Writer->m_HeaderPart; +} + +// Warning: direct manipulation of MXF structures can interfere +// with the normal operation of the wrapper. Caveat emptor! +// +ASDCP::MXF::OPAtomIndexFooter& +ASDCP::DCData::MXFWriter::OPAtomIndexFooter() +{ + if ( m_Writer.empty() ) + { + assert(g_OPAtomIndexFooter); + return *g_OPAtomIndexFooter; + } + + return m_Writer->m_FooterPart; +} + +// Open the file for writing. The file must not exist. Returns error if +// the operation cannot be completed. +ASDCP::Result_t +ASDCP::DCData::MXFWriter::OpenWrite(const char* filename, const WriterInfo& Info, + const DCDataDescriptor& DDesc, ui32_t HeaderSize) +{ + if ( Info.LabelSetType != LS_MXF_SMPTE ) + { + DefaultLogSink().Error("DC Data support requires LS_MXF_SMPTE\n"); + return RESULT_FORMAT; + } + + m_Writer = new h__Writer(DefaultSMPTEDict()); + m_Writer->m_Info = Info; + + Result_t result = m_Writer->OpenWrite(filename, HeaderSize, SubDescriptorList_t()); + + if ( ASDCP_SUCCESS(result) ) + result = m_Writer->SetSourceStream(DDesc, NULL, DC_DATA_PACKAGE_LABEL, DC_DATA_DEF_LABEL); + + 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::DCData::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::DCData::MXFWriter::Finalize() +{ + if ( m_Writer.empty() ) + return RESULT_INIT; + + return m_Writer->Finalize(); +} + + +// +// end AS_DCP_DCData.cpp +// diff --git a/src/AS_DCP_DCData_internal.h b/src/AS_DCP_DCData_internal.h new file mode 100644 index 0000000..65d6c41 --- /dev/null +++ b/src/AS_DCP_DCData_internal.h @@ -0,0 +1,94 @@ +/* +Copyright (c) 2004-2012, 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_DCData_internal.h + \version $Id$ + \brief AS-DCP library, non-public common DCData reader and writer implementation +*/ + +#ifndef _AS_DCP_DCDATA_INTERNAL_H_ +#define _AS_DCP_DCDATA_INTERNAL_H_ + +#include <list> + +#include "AS_DCP_internal.h" + +namespace ASDCP +{ + +namespace MXF +{ + class InterchangeObject; +} + +namespace DCData +{ + typedef std::list<MXF::InterchangeObject*> SubDescriptorList_t; + + class h__Reader : public ASDCP::h__ASDCPReader + { + MXF::DCDataDescriptor* m_EssenceDescriptor; + ASDCP_NO_COPY_CONSTRUCT(h__Reader); + h__Reader(); + + public: + DCDataDescriptor m_DDesc; + + h__Reader(const Dictionary& d) : ASDCP::h__ASDCPReader(d), m_EssenceDescriptor(0), + m_DDesc() {} + ~h__Reader() {} + Result_t OpenRead(const char*); + Result_t ReadFrame(ui32_t, FrameBuffer&, AESDecContext*, HMACContext*); + Result_t MD_to_DCData_DDesc(DCData::DCDataDescriptor& DDesc); + }; + + class h__Writer : public ASDCP::h__Writer + { + ASDCP_NO_COPY_CONSTRUCT(h__Writer); + h__Writer(); + + public: + DCDataDescriptor m_DDesc; + byte_t m_EssenceUL[SMPTE_UL_LENGTH]; + + h__Writer(const Dictionary& d) : ASDCP::h__Writer(d) { + memset(m_EssenceUL, 0, SMPTE_UL_LENGTH); + } + + ~h__Writer(){} + + Result_t OpenWrite(const char*, ui32_t HeaderSize, const SubDescriptorList_t& subDescriptors); + Result_t SetSourceStream(const DCDataDescriptor&, const byte_t*, const std::string&, const std::string&); + Result_t WriteFrame(const FrameBuffer&, AESEncContext* = 0, HMACContext* = 0); + Result_t Finalize(); + Result_t DCData_DDesc_to_MD(DCData::DCDataDescriptor& DDesc); +}; + + +} // namespace DCData +} // namespace ASDCP + +#endif // _AS_DCP_DCDATA_INTERNAL_H_ diff --git a/src/AS_DCP_JP2K.cpp b/src/AS_DCP_JP2K.cpp index 33d5b93..300a6a2 100755 --- a/src/AS_DCP_JP2K.cpp +++ b/src/AS_DCP_JP2K.cpp @@ -280,6 +280,22 @@ lh__Reader::MD_to_JP2K_PDesc(JP2K::PictureDescriptor& PDesc) return RESULT_OK; } +// Compares the actual floating point value of the rates. +// This allows, for example, {300000,1001} and {2997,100) to be considered equivalent. +// to 29.97. +bool +epsilon_compare(const ASDCP::Rational& left, const ASDCP::Rational& right, double epsilon = 0.001) +{ + bool result = false; + double difference = left.Quotient() - right.Quotient(); + + if (fabs(difference) < epsilon) + result = true; + + return result; +} +// end DOLBY + // // ASDCP::Result_t @@ -459,6 +475,8 @@ ASDCP::JP2K::MXFReader::MXFReader() ASDCP::JP2K::MXFReader::~MXFReader() { + if ( m_Reader && m_Reader->m_File.IsOpen() ) + m_Reader->Close(); } // Warning: direct manipulation of MXF structures can interfere @@ -510,6 +528,12 @@ ASDCP::JP2K::MXFReader::ReadFrame(ui32_t FrameNum, FrameBuffer& FrameBuf, return RESULT_INIT; } +ASDCP::Result_t +ASDCP::JP2K::MXFReader::LocateFrame(ui32_t FrameNum, Kumu::fpos_t& streamOffset, i8_t& temporalOffset, i8_t& keyFrameOffset) const +{ + return m_Reader->LocateFrame(FrameNum, streamOffset, temporalOffset, keyFrameOffset); +} + // Fill the struct with the values from the file's header. // Returns RESULT_INIT if the file is not open. @@ -663,6 +687,8 @@ ASDCP::JP2K::MXFSReader::MXFSReader() ASDCP::JP2K::MXFSReader::~MXFSReader() { + if ( m_Reader && m_Reader->m_File.IsOpen() ) + m_Reader->Close(); } // Warning: direct manipulation of MXF structures can interfere @@ -731,6 +757,12 @@ ASDCP::JP2K::MXFSReader::ReadFrame(ui32_t FrameNum, StereoscopicPhase_t phase, F return RESULT_INIT; } +ASDCP::Result_t +ASDCP::JP2K::MXFSReader::LocateFrame(ui32_t FrameNum, Kumu::fpos_t& streamOffset, i8_t& temporalOffset, i8_t& keyFrameOffset) const +{ + return m_Reader->LocateFrame(FrameNum, streamOffset, temporalOffset, keyFrameOffset); +} + // Fill the struct with the values from the file's header. // Returns RESULT_INIT if the file is not open. ASDCP::Result_t diff --git a/src/AS_DCP_MPEG2.cpp b/src/AS_DCP_MPEG2.cpp index 2081630..61ab7b7 100755 --- a/src/AS_DCP_MPEG2.cpp +++ b/src/AS_DCP_MPEG2.cpp @@ -333,6 +333,8 @@ ASDCP::MPEG2::MXFReader::MXFReader() ASDCP::MPEG2::MXFReader::~MXFReader() { + if ( m_Reader && m_Reader->m_File.IsOpen() ) + m_Reader->Close(); } // Warning: direct manipulation of MXF structures can interfere @@ -387,6 +389,13 @@ ASDCP::MPEG2::MXFReader::ReadFrame(ui32_t FrameNum, FrameBuffer& FrameBuf, // ASDCP::Result_t +ASDCP::MPEG2::MXFReader::LocateFrame(ui32_t FrameNum, Kumu::fpos_t& streamOffset, i8_t& temporalOffset, i8_t& keyFrameOffset) const +{ + return m_Reader->LocateFrame(FrameNum, streamOffset, temporalOffset, keyFrameOffset); +} + +// +ASDCP::Result_t ASDCP::MPEG2::MXFReader::ReadFrameGOPStart(ui32_t FrameNum, FrameBuffer& FrameBuf, AESDecContext* Ctx, HMACContext* HMAC) const { diff --git a/src/AS_DCP_MXF.cpp b/src/AS_DCP_MXF.cpp index 96fc959..4d039f0 100755 --- a/src/AS_DCP_MXF.cpp +++ b/src/AS_DCP_MXF.cpp @@ -191,6 +191,13 @@ ASDCP::EssenceType(const char* filename, EssenceType_t& type) type = ESS_MPEG2_VES; else if ( ASDCP_SUCCESS(TestHeader.GetMDObjectByType(OBJ_TYPE_ARGS(TimedTextDescriptor))) ) type = ESS_TIMED_TEXT; + else if ( ASDCP_SUCCESS(TestHeader.GetMDObjectByType(OBJ_TYPE_ARGS(DCDataDescriptor))) ) + { + if ( ASDCP_SUCCESS(TestHeader.GetMDObjectByType(OBJ_TYPE_ARGS(DolbyAtmosSubDescriptor))) ) + type = ESS_DCDATA_DOLBY_ATMOS; + else + type = ESS_DCDATA_UNKNOWN; + } } return result; @@ -205,6 +212,7 @@ ASDCP::RawEssenceType(const char* filename, EssenceType_t& type) ASDCP::FrameBuffer FB; Kumu::FileReader Reader; ASDCP::Wav::SimpleWaveHeader WavHeader; + ASDCP::RF64::SimpleRF64Header RF64Header; ASDCP::AIFF::SimpleAIFFHeader AIFFHeader; Kumu::XMLElement TmpElement("Tmp"); @@ -248,6 +256,16 @@ ASDCP::RawEssenceType(const char* filename, EssenceType_t& type) return RESULT_FORMAT; } } + else if ( ASDCP_SUCCESS(RF64Header.ReadFromBuffer(FB.RoData(), read_count, &data_offset)) ) + { + switch ( RF64Header.samplespersec ) + { + case 48000: type = ESS_PCM_24b_48k; break; + case 96000: type = ESS_PCM_24b_96k; break; + default: + return RESULT_FORMAT; + } + } else if ( ASDCP_SUCCESS(AIFFHeader.ReadFromBuffer(FB.RoData(), read_count, &data_offset)) ) { type = ESS_PCM_24b_48k; @@ -256,6 +274,10 @@ ASDCP::RawEssenceType(const char* filename, EssenceType_t& type) { type = ESS_TIMED_TEXT; } + else if ( ASDCP::ATMOS::IsDolbyAtmos(filename) ) + { + type = ESS_DCDATA_DOLBY_ATMOS; + } } } else if ( Kumu::PathIsDirectory(filename) ) @@ -298,6 +320,21 @@ ASDCP::RawEssenceType(const char* filename, EssenceType_t& type) return RESULT_FORMAT; } } + else if ( ASDCP_SUCCESS(RF64Header.ReadFromBuffer(FB.RoData(), read_count, &data_offset)) ) + { + switch ( RF64Header.samplespersec ) + { + case 48000: type = ESS_PCM_24b_48k; break; + case 96000: type = ESS_PCM_24b_96k; break; + default: + return RESULT_FORMAT; + } + } + else if ( ASDCP::ATMOS::IsDolbyAtmos(Str.c_str()) ) + { + type = ESS_DCDATA_DOLBY_ATMOS; + } + } break; diff --git a/src/AS_DCP_PCM.cpp b/src/AS_DCP_PCM.cpp index 606a6f5..e52b27f 100755 --- a/src/AS_DCP_PCM.cpp +++ b/src/AS_DCP_PCM.cpp @@ -134,6 +134,35 @@ ASDCP::PCM::operator << (std::ostream& strm, const AudioDescriptor& ADesc) strm << " AvgBps: " << (unsigned) ADesc.AvgBps << std::endl; strm << " LinkedTrackID: " << (unsigned) ADesc.LinkedTrackID << std::endl; strm << " ContainerDuration: " << (unsigned) ADesc.ContainerDuration << std::endl; + strm << " ChannelFormat: "; + switch (ADesc.ChannelFormat) + { + case CF_NONE: + default: + strm << "No Channel Format"; + break; + + case CF_CFG_1: + strm << "Config 1 (5.1 with optional HI/VI)"; + break; + + case CF_CFG_2: + strm << "Config 2 (5.1 + center surround with optional HI/VI)"; + break; + + case CF_CFG_3: + strm << "Config 3 (7.1 with optional HI/VI)"; + break; + + case CF_CFG_4: + strm << "Config 4"; + break; + + case CF_CFG_5: + strm << "Config 5 (7.1 DS with optional HI/VI)"; + break; + } + strm << std::endl; return strm; } @@ -154,7 +183,8 @@ ASDCP::PCM::AudioDescriptorDump(const AudioDescriptor& ADesc, FILE* stream) BlockAlign: %u\n\ AvgBps: %u\n\ LinkedTrackID: %u\n\ - ContainerDuration: %u\n", + ContainerDuration: %u\n\ + ChannelFormat: %u\n", ADesc.EditRate.Numerator, ADesc.EditRate.Denominator, ADesc.AudioSamplingRate.Numerator, ADesc.AudioSamplingRate.Denominator, ADesc.Locked, @@ -163,7 +193,8 @@ ASDCP::PCM::AudioDescriptorDump(const AudioDescriptor& ADesc, FILE* stream) ADesc.BlockAlign, ADesc.AvgBps, ADesc.LinkedTrackID, - ADesc.ContainerDuration + ADesc.ContainerDuration, + ADesc.ChannelFormat ); } @@ -256,6 +287,7 @@ ASDCP::PCM::MXFReader::h__Reader::OpenRead(const char* filename) } else { + DefaultLogSink().Error("PCM EditRate not in expected value range.\n"); // or we just drop the hammer return RESULT_FORMAT; } @@ -370,6 +402,12 @@ ASDCP::PCM::MXFReader::ReadFrame(ui32_t FrameNum, FrameBuffer& FrameBuf, } +ASDCP::Result_t +ASDCP::PCM::MXFReader::LocateFrame(ui32_t FrameNum, Kumu::fpos_t& streamOffset, i8_t& temporalOffset, i8_t& keyFrameOffset) const +{ + return m_Reader->LocateFrame(FrameNum, streamOffset, temporalOffset, keyFrameOffset); +} + // Fill the struct with the values from the file's header. // Returns RESULT_INIT if the file is not open. ASDCP::Result_t diff --git a/src/AS_DCP_TimedText.cpp b/src/AS_DCP_TimedText.cpp index 971fe08..694676d 100644 --- a/src/AS_DCP_TimedText.cpp +++ b/src/AS_DCP_TimedText.cpp @@ -92,7 +92,7 @@ ASDCP::TimedText::DescriptorDump(ASDCP::TimedText::TimedTextDescriptor const& TD fprintf(stream, "ContainerDuration: %u\n", TDesc.ContainerDuration); fprintf(stream, " AssetID: %s\n", TmpID.EncodeHex(buf, 64)); fprintf(stream, " NamespaceName: %s\n", TDesc.NamespaceName.c_str()); - fprintf(stream, " ResourceCount: %d\n", TDesc.ResourceList.size()); + fprintf(stream, " ResourceCount: %zu\n", TDesc.ResourceList.size()); TimedText::ResourceList_t::const_iterator ri; for ( ri = TDesc.ResourceList.begin() ; ri != TDesc.ResourceList.end(); ri++ ) @@ -567,7 +567,7 @@ ASDCP::TimedText::MXFWriter::h__Writer::SetSourceStream(ASDCP::TimedText::TimedT { InitHeader(); AddDMSegment(m_TDesc.EditRate, 24, TIMED_TEXT_DEF_LABEL, - UL(m_Dict->ul(MDD_PictureDataDef)), TIMED_TEXT_PACKAGE_LABEL); + UL(m_Dict->ul(MDD_DataDataDef)), TIMED_TEXT_PACKAGE_LABEL); AddEssenceDescriptor(UL(m_Dict->ul(MDD_TimedTextWrapping))); diff --git a/src/AS_DCP_internal.h b/src/AS_DCP_internal.h index af6b553..5310100 100755 --- a/src/AS_DCP_internal.h +++ b/src/AS_DCP_internal.h @@ -25,7 +25,7 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /*! \file AS_DCP_internal.h - \version $Id$ + \version $Id$ \brief AS-DCP library, non-public common elements */ @@ -62,7 +62,7 @@ namespace ASDCP std::vector<int> result; const char* pstr = str; const char* r = strchr(pstr, '.'); - + while ( r != 0 ) { assert(r >= pstr); @@ -132,7 +132,7 @@ namespace ASDCP const ASDCP::WriterInfo& Info, Kumu::fpos_t& LastPosition, ASDCP::FrameBuffer& CtFrameBuf, ui32_t FrameNum, ui32_t SequenceNum, ASDCP::FrameBuffer& FrameBuf, const byte_t* EssenceUL, AESDecContext* Ctx, HMACContext* HMAC); - + // class KLReader : public ASDCP::KLVPacket { @@ -146,7 +146,7 @@ namespace ASDCP inline const byte_t* Key() { return m_KeyBuf; } inline const ui64_t Length() { return m_ValueLength; } inline const ui64_t KLLength() { return m_KLLength; } - + Result_t ReadKLFromFile(Kumu::FileReader& Reader); }; @@ -207,7 +207,7 @@ namespace ASDCP if ( KM_SUCCESS(result) ) { Result_t cr_result = m_HeaderPart.GetMDObjectByType(OBJ_TYPE_ARGS(CryptographicContext), &Object); - + if ( KM_SUCCESS(cr_result) ) MD_to_CryptoInfo((CryptographicContext*)Object, m_Info, *m_Dict); } @@ -223,7 +223,9 @@ namespace ASDCP if ( KM_SUCCESS(result) ) result = m_HeaderPart.InitFromFile(m_File); - + else + DefaultLogSink().Error("ASDCP::h__Reader::OpenMXFRead, OpenRead failed\n"); + return result; } @@ -266,6 +268,28 @@ namespace ASDCP FrameNum, SequenceNum, FrameBuf, EssenceUL, Ctx, HMAC); } + // Get the position of a frame from a track file + Result_t LocateFrame(const ASDCP::MXF::Partition& CurrentPartition, + ui32_t FrameNum, Kumu::fpos_t& streamOffset, + i8_t& temporalOffset, i8_t& keyFrameOffset) + { + // look up frame index node + IndexTableSegment::IndexEntry TmpEntry; + + if ( KM_FAILURE(m_FooterPart.Lookup(FrameNum, TmpEntry)) ) + { + DefaultLogSink().Error("Frame value out of range: %u\n", FrameNum); + return RESULT_RANGE; + } + + // get frame position, temporal offset, and key frame ofset + streamOffset = CurrentPartition.BodyOffset + TmpEntry.StreamOffset; + temporalOffset = TmpEntry.TemporalOffset; + keyFrameOffset = TmpEntry.KeyFrameOffset; + + return RESULT_OK; + } + // void Close() { m_File.Close(); @@ -291,6 +315,9 @@ namespace ASDCP Result_t InitMXFIndex(); Result_t ReadEKLVFrame(ui32_t FrameNum, ASDCP::FrameBuffer& FrameBuf, const byte_t* EssenceUL, AESDecContext* Ctx, HMACContext* HMAC); + Result_t LocateFrame(ui32_t FrameNum, Kumu::fpos_t& streamOffset, + i8_t& temporalOffset, i8_t& keyFrameOffset); + }; @@ -392,13 +419,13 @@ namespace ASDCP { public: byte_t Data[klv_intpack_size]; - + IntegrityPack() { memset(Data, 0, klv_intpack_size); } ~IntegrityPack() {} - + Result_t CalcValues(const ASDCP::FrameBuffer&, const byte_t* AssetID, ui32_t sequence, HMACContext* HMAC); Result_t TestValues(const ASDCP::FrameBuffer&, const byte_t* AssetID, ui32_t sequence, HMACContext* HMAC); }; diff --git a/src/AtmosSyncChannel_Generator.cpp b/src/AtmosSyncChannel_Generator.cpp new file mode 100644 index 0000000..b9b8c44 --- /dev/null +++ b/src/AtmosSyncChannel_Generator.cpp @@ -0,0 +1,149 @@ +/* +Copyright (c) 2013-2013, 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 AtmosSyncChannel_Generator.cpp + \version $Id$ + \brief Dolby Atmos sync channel generator implementation +*/ + +#include <AtmosSyncChannel_Generator.h> + +#include <AS_DCP.h> + +using namespace ASDCP; + +// +ASDCP::PCM::AtmosSyncChannelGenerator::AtmosSyncChannelGenerator(ui16_t bitsPerSample, ui32_t sampleRate, + const ASDCP::Rational& editRate, const byte_t* uuid) + : m_syncEncoder(), + m_audioTrackUUID(), + m_ADesc(), + m_syncSignalBuffer(NULL), + m_numSamplesPerFrame(0), + m_currentFrameNumber(0), + m_numBytesPerFrame(0), + m_isSyncEncoderInitialized(false) +{ + + m_ADesc.EditRate = editRate; + m_ADesc.ChannelCount = 1; + m_ADesc.QuantizationBits = bitsPerSample; + m_ADesc.AudioSamplingRate = Rational(sampleRate, 1); + m_ADesc.BlockAlign = ((bitsPerSample + 7) / 8); + m_ADesc.AvgBps = (sampleRate * m_ADesc.BlockAlign); + + memcpy(&m_audioTrackUUID.abyUUIDBytes[0], uuid, UUIDlen); + m_numSamplesPerFrame = (editRate.Denominator * sampleRate) / editRate.Numerator; + m_numBytesPerFrame = m_numSamplesPerFrame * m_ADesc.BlockAlign; + + if (bitsPerSample == 24) + { + ui32_t frameRate = editRate.Numerator/editRate.Denominator; // intentionally allowing for imprecise cast to int + m_isSyncEncoderInitialized = (SyncEncoderInit(&m_syncEncoder, sampleRate, frameRate, &m_audioTrackUUID) == SYNC_ENCODER_ERROR_NONE); + m_syncSignalBuffer = new float[m_numSamplesPerFrame]; + } + else + { + m_isSyncEncoderInitialized = false; + } +} + +ASDCP::PCM::AtmosSyncChannelGenerator::~AtmosSyncChannelGenerator() +{ + delete [] m_syncSignalBuffer; +} + +ASDCP::Result_t +ASDCP::PCM::AtmosSyncChannelGenerator::ReadFrame(FrameBuffer& OutFB) +{ + if (OutFB.Capacity() < m_numBytesPerFrame) + { + return RESULT_SMALLBUF; + } + + /** + * Update frame number and size. + */ + OutFB.FrameNumber(m_currentFrameNumber); + OutFB.Size(m_numBytesPerFrame); + + /** + * Get pointer to frame essence. + */ + byte_t* frameEssence = OutFB.Data(); + + if (m_isSyncEncoderInitialized) + { + /** + * Generate sync signal frame. + */ + int ret = EncodeSync(&m_syncEncoder, m_numSamplesPerFrame, m_syncSignalBuffer, m_currentFrameNumber); + if (ret == SYNC_ENCODER_ERROR_NONE) + { + for (unsigned int i = 0; i < m_numSamplesPerFrame; ++i) + { + /** + * Convert each encoded float sample to a signed 24 bit integer and + * copy into the essence buffer. + */ + i32_t sample = convertSampleFloatToInt24(m_syncSignalBuffer[i]); + memcpy(frameEssence, ((byte_t*)(&sample))+1, NUM_BYTES_PER_INT24); + frameEssence += NUM_BYTES_PER_INT24; + } + } + else + { + /** + * Encoding error, zero out the frame. + */ + memset(frameEssence, 0, m_numBytesPerFrame); + } + } + else + { + /** + * Sync encoder not initialize, zero out the frame. + */ + memset(frameEssence, 0, m_numBytesPerFrame); + } + ++m_currentFrameNumber; + return RESULT_OK; +} + +ASDCP::Result_t +ASDCP::PCM::AtmosSyncChannelGenerator::Reset() +{ + m_currentFrameNumber = 0; + return RESULT_OK; +} + +Result_t +ASDCP::PCM::AtmosSyncChannelGenerator::FillAudioDescriptor(AudioDescriptor& ADesc) const +{ + ADesc = m_ADesc; + return RESULT_OK; +} + diff --git a/src/AtmosSyncChannel_Generator.h b/src/AtmosSyncChannel_Generator.h new file mode 100644 index 0000000..d7f4219 --- /dev/null +++ b/src/AtmosSyncChannel_Generator.h @@ -0,0 +1,151 @@ +/* +Copyright (c) 2013-2013, 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 AtmosSyncChannel_Generator.h + \version $Id$ + \brief Dolby Atmos sync channel generator +*/ + +#ifndef _ATMOSSYNCCHANNEL_GENERATOR_H_ +#define _ATMOSSYNCCHANNEL_GENERATOR_H_ + +#include <AS_DCP.h> +#include "SyncEncoder.h" +#include "UUIDInformation.h" + +#define INT24_MAX 8388607.0 +#define INT24_MIN -8388608.0 + +namespace ASDCP +{ + namespace ATMOS + { + static const ui32_t SYNC_CHANNEL = 14; + } + + namespace PCM + { + + static const ui16_t NUM_BYTES_PER_INT24 = 3; + + class AtmosSyncChannelGenerator + { + SYNCENCODER m_syncEncoder; + UUIDINFORMATION m_audioTrackUUID; + AudioDescriptor m_ADesc; + float *m_syncSignalBuffer; + ui32_t m_numSamplesPerFrame; + ui32_t m_currentFrameNumber; + ui32_t m_numBytesPerFrame; + bool m_isSyncEncoderInitialized; + + ASDCP_NO_COPY_CONSTRUCT(AtmosSyncChannelGenerator); + + public: + /** + * Constructor + * + * @param bitsPerSample the number of bits in each sample of pcm data + * @param sampleRate the sampling rate + * @param editRate the edit rate of the associated picture track. + * @param atmosUUID the UUID of the associated ATMOS track file. + * + */ + AtmosSyncChannelGenerator(ui16_t bitsPerSample, ui32_t sampleRate, + const ASDCP::Rational& editRate, const byte_t* uuid); + ~AtmosSyncChannelGenerator(); + + /** + * Set the frame number when seeking + * Use override the default starting frame number for a new track or + * to set the frame number when doing random access. + * + * @param frameNumber + * + */ + void setFrameNumber(ui32_t frameNumber) { m_currentFrameNumber = frameNumber; }; + + /** + * Get the number of bytes per frame. + * + * @return Number of bytes per frame + * + */ + ui32_t getBytesPerFrame() { return m_numBytesPerFrame; } + + /** + * Generates the next frame of sync data. + * Generates the next frame of sync data and places it + * the frame buffer. Fails if the buffer is too small. + * **Automatically increments the frame number.** + * + * @param buf the buffer that the generated frame data will be written to. + * + * @return Kumu::RESULT_OK if the buffer is succesfully filled with sync + * data for the next frame. + */ + Result_t ReadFrame(FrameBuffer& buf); + + /** + * Reset the frame count. + * + * @return Kumu::RESULT_OK + */ + Result_t Reset(); + + /** + * Fill the AudioDescriptor with the relevant information. + * + * @return Kumu::RESULT_OK + */ + Result_t FillAudioDescriptor(PCM::AudioDescriptor& ADesc) const; + + /** + * Converts a sample float into + * 24-bit PCM data. + * + */ + static inline i32_t convertSampleFloatToInt24(float sample) + { + if (sample >= 0.0) + { + return (static_cast<i32_t>(sample * INT24_MAX) << 8); + } + else + { + return (static_cast<i32_t>(-sample * INT24_MIN) << 8); + } + } + }; + + } // namespace PCM +} // namespace ASDCP + +#endif // _ATMOSSYNCCHANNEL_GENERATOR_H_ + +// +// end AtmosSyncChannel_Generator.h +// diff --git a/src/AtmosSyncChannel_Mixer.cpp b/src/AtmosSyncChannel_Mixer.cpp new file mode 100644 index 0000000..70cfbfd --- /dev/null +++ b/src/AtmosSyncChannel_Mixer.cpp @@ -0,0 +1,326 @@ +/* +Copyright (c) 2013-2013, 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 AtmosSyncChannel_Mixer.cpp + \version $Id$ + \brief Read WAV files(s), multiplex multiple PCM frame buffers including Atmos Sync into one +*/ + +#include <AtmosSyncChannel_Mixer.h> + +#include <algorithm> + +#include <AS_DCP.h> +#include <KM_log.h> +#include <PCMDataProviders.h> + +using namespace ASDCP; +using namespace Kumu; + +// +ASDCP::AtmosSyncChannelMixer::AtmosSyncChannelMixer(const byte_t * trackUUID) + : m_inputs(), m_outputs(), m_trackUUID(), m_ADesc(), m_ChannelCount(0), m_FramesRead(0) +{ + ::memcpy(m_trackUUID, trackUUID, UUIDlen); +} + +ASDCP::AtmosSyncChannelMixer::~AtmosSyncChannelMixer() +{ + clear(); +} + +void +ASDCP::AtmosSyncChannelMixer::clear() +{ + m_outputs.clear(); + std::for_each(m_inputs.begin(), m_inputs.end(), delete_input()); + m_inputs.clear(); +} + +// +Result_t +ASDCP::AtmosSyncChannelMixer::OpenRead(ui32_t argc, const char** argv, const Rational& PictureRate) +{ + ASDCP_TEST_NULL_STR(argv); + PathList_t TmpFileList; + + for ( ui32_t i = 0; i < argc; ++i ) + TmpFileList.push_back(argv[i]); + + return OpenRead(TmpFileList, PictureRate); +} + +// +Result_t +ASDCP::AtmosSyncChannelMixer::OpenRead(const Kumu::PathList_t& argv, const Rational& PictureRate) +{ + Result_t result = RESULT_OK; + PathList_t::iterator fi; + Kumu::PathList_t file_list; + PCM::AudioDescriptor tmpDesc; + + if ( argv.size() == 1 && PathIsDirectory(argv.front()) ) + { + DirScanner Dir; + char name_buf[MaxFilePath]; + result = Dir.Open(argv.front().c_str()); + + if ( KM_SUCCESS(result) ) + result = Dir.GetNext(name_buf); + + while ( KM_SUCCESS(result) ) + { + if ( name_buf[0] != '.' ) // no hidden files + { + std::string tmp_path = argv.front() + "/" + name_buf; + file_list.push_back(tmp_path); + } + + result = Dir.GetNext(name_buf); + } + + if ( result == RESULT_ENDOFFILE ) + { + result = RESULT_OK; + file_list.sort(); + } + } + else + { + file_list = argv; + } + + for ( fi = file_list.begin(); KM_SUCCESS(result) && fi != file_list.end(); ++fi ) + { + result = OpenRead(*fi, PictureRate); + } + + if ( ASDCP_SUCCESS(result) && (m_ChannelCount < ATMOS::SYNC_CHANNEL)) + { + // atmos sync channel has not been added + result = MixInSilenceChannels(); + if ( ASDCP_SUCCESS(result) ) + result = MixInAtmosSyncChannel(); + } + + if ( ASDCP_SUCCESS(result) ) + { + m_ADesc.ChannelCount = m_ChannelCount; + m_ADesc.AvgBps = (ui32_t)(ceil(m_ADesc.AudioSamplingRate.Quotient()) * m_ADesc.BlockAlign); + } + else + { + clear(); + } + + return result; +} + +// +Result_t +ASDCP::AtmosSyncChannelMixer::OpenRead(const std::string& file, const Rational& PictureRate) +{ + Result_t result = RESULT_OK; + PCM::AudioDescriptor tmpDesc; + ui32_t numChannels = 0; + mem_ptr<WAVDataProvider> I = new WAVDataProvider; + result = I->OpenRead(file.c_str(), PictureRate); + + if ( ASDCP_SUCCESS(result)) + { + result = I->FillAudioDescriptor(tmpDesc); + } + + if ( ASDCP_SUCCESS(result) ) + { + + if ( m_ChannelCount == 0 ) + { + m_ADesc = tmpDesc; + } + else + { + + if ( tmpDesc.AudioSamplingRate != m_ADesc.AudioSamplingRate ) + { + DefaultLogSink().Error("AudioSamplingRate mismatch in PCM parser list."); + return RESULT_FORMAT; + } + + if ( tmpDesc.QuantizationBits != m_ADesc.QuantizationBits ) + { + DefaultLogSink().Error("QuantizationBits mismatch in PCM parser list."); + return RESULT_FORMAT; + } + + if ( tmpDesc.ContainerDuration < m_ADesc.ContainerDuration ) + m_ADesc.ContainerDuration = tmpDesc.ContainerDuration; + + m_ADesc.BlockAlign += tmpDesc.BlockAlign; + } + } + + + if ( ASDCP_SUCCESS(result) ) + { + numChannels = tmpDesc.ChannelCount; // default to all channels + if ((m_ChannelCount < ATMOS::SYNC_CHANNEL) && (m_ChannelCount + numChannels) > (ATMOS::SYNC_CHANNEL - 1)) + { + // need to insert an atmos channel between the channels of this file. + numChannels = ATMOS::SYNC_CHANNEL - m_ChannelCount - 1; + m_outputs.push_back(std::make_pair(numChannels, I.get())); + m_ChannelCount += numChannels; + MixInAtmosSyncChannel(); + numChannels = tmpDesc.ChannelCount - numChannels; + } + m_outputs.push_back(std::make_pair(numChannels, I.get())); + m_inputs.push_back(I); + I.release(); + m_ChannelCount += numChannels; + } + return result; +} + +Result_t +ASDCP::AtmosSyncChannelMixer::MixInSilenceChannels() +{ + Result_t result = RESULT_OK; + PCM::AudioDescriptor tmpDesc; + ui32_t numSilenceChannels = ATMOS::SYNC_CHANNEL - m_ChannelCount - 1; + if (numSilenceChannels > 0) + { + mem_ptr<SilenceDataProvider> I = new SilenceDataProvider(numSilenceChannels, + m_ADesc.QuantizationBits, + m_ADesc.AudioSamplingRate.Numerator, + m_ADesc.EditRate); + result = I->FillAudioDescriptor(tmpDesc); + if ( ASDCP_SUCCESS(result) ) + { + m_ADesc.BlockAlign += tmpDesc.BlockAlign; + m_ChannelCount += tmpDesc.ChannelCount; + m_outputs.push_back(std::make_pair(numSilenceChannels, I.get())); + m_inputs.push_back(I); + I.release(); + assert(m_ChannelCount == (ATMOS::SYNC_CHANNEL - 1)); + } + } + return result; +} + +// +Result_t +ASDCP::AtmosSyncChannelMixer::MixInAtmosSyncChannel() +{ + Result_t result = RESULT_OK; + PCM::AudioDescriptor tmpDesc; + mem_ptr<AtmosSyncDataProvider> I = new AtmosSyncDataProvider(m_ADesc.QuantizationBits, + m_ADesc.AudioSamplingRate.Numerator, + m_ADesc.EditRate, m_trackUUID); + result = I->FillAudioDescriptor(tmpDesc); + if ( ASDCP_SUCCESS(result) ) + { + m_ADesc.BlockAlign += tmpDesc.BlockAlign; + m_ChannelCount += tmpDesc.ChannelCount; + m_outputs.push_back(std::make_pair(tmpDesc.ChannelCount, I.get())); + m_inputs.push_back(I); + I.release(); + assert(m_ChannelCount == ATMOS::SYNC_CHANNEL); + } + return result; +} + +// +Result_t +ASDCP::AtmosSyncChannelMixer::FillAudioDescriptor(PCM::AudioDescriptor& ADesc) const +{ + ADesc = m_ADesc; + return RESULT_OK; +} + +// +Result_t +ASDCP::AtmosSyncChannelMixer::Reset() +{ + Result_t result = RESULT_OK; + SourceList::iterator it; + SourceList::iterator lastInput = m_inputs.end(); + + for ( it = m_inputs.begin(); it != lastInput && ASDCP_SUCCESS(result) ; ++it ) + result = (*it)->Reset(); + + return result; +} + + +//2 +Result_t +ASDCP::AtmosSyncChannelMixer::ReadFrame(PCM::FrameBuffer& OutFB) +{ + + + Result_t result = RESULT_OK; + SourceList::iterator iter; + SourceList::iterator lastInput = m_inputs.end(); + ui32_t bufSize = PCM::CalcFrameBufferSize(m_ADesc); + assert( bufSize <= OutFB.Capacity()); + + for ( iter = m_inputs.begin(); iter != lastInput && ASDCP_SUCCESS(result) ; ++iter ) + result = (*iter)->ReadFrame(); + + if ( ASDCP_SUCCESS(result) ) + { + OutFB.Size(bufSize); + byte_t* Out_p = OutFB.Data(); + byte_t* End_p = Out_p + OutFB.Size(); + ui32_t bytesWritten = 0; + OutputList::iterator iter; + OutputList::iterator lastOutput = m_outputs.end(); + + while ( Out_p < End_p && ASDCP_SUCCESS(result) ) + { + iter = m_outputs.begin(); + while ( iter != lastOutput && ASDCP_SUCCESS(result) ) + { + result = ((*iter).second)->PutSample((*iter).first, Out_p, &bytesWritten); + Out_p += bytesWritten; + ++iter; + } + } + + if ( ASDCP_SUCCESS(result) ) + { + assert(Out_p == End_p); + OutFB.FrameNumber(m_FramesRead++); + } + } + + return result; +} + + +// +// end AtmosSyncChannel_Mixer.cpp +// diff --git a/src/AtmosSyncChannel_Mixer.h b/src/AtmosSyncChannel_Mixer.h new file mode 100644 index 0000000..c6a27a3 --- /dev/null +++ b/src/AtmosSyncChannel_Mixer.h @@ -0,0 +1,91 @@ +/* +Copyright (c) 2013-2013, 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 AtmosSyncChannel_Mixer.h + \version $Id$ + \brief Read WAV files(s), multiplex multiple PCM frame buffers including Atmos Sync into one +*/ + +#ifndef _ATMOSSYNCCHANNEL_MIXER_H_ +#define _ATMOSSYNCCHANNEL_MIXER_H_ + +#include <AS_DCP.h> +#include <KM_error.h> +#include <PCMDataProviders.h> +#include <vector> + +namespace ASDCP +{ + + // + class AtmosSyncChannelMixer + { + typedef std::pair<ui32_t, PCMDataProviderInterface*> InputBus; + typedef std::vector<InputBus> OutputList; + typedef std::vector<PCMDataProviderInterface*> SourceList; + + SourceList m_inputs; + OutputList m_outputs; + byte_t m_trackUUID[ASDCP::UUIDlen]; + + Result_t OpenRead(const std::string& file, const Rational& PictureRate); + Result_t MixInSilenceChannels(); + Result_t MixInAtmosSyncChannel(); + void clear(); + + // functor for deleting + struct delete_input + { + void operator()(PCMDataProviderInterface* i) + { + delete i; + } + }; + + ASDCP_NO_COPY_CONSTRUCT(AtmosSyncChannelMixer); + + protected: + PCM::AudioDescriptor m_ADesc; + ui32_t m_ChannelCount; + ui32_t m_FramesRead; + + public: + AtmosSyncChannelMixer(const byte_t * trackUUID); + virtual ~AtmosSyncChannelMixer(); + + Result_t OpenRead(ui32_t argc, const char** argv, const Rational& PictureRate); + Result_t OpenRead(const Kumu::PathList_t& argv, const Rational& PictureRate); + Result_t FillAudioDescriptor(PCM::AudioDescriptor& ADesc) const; + Result_t Reset(); + Result_t ReadFrame(PCM::FrameBuffer& OutFB); + }; +} // namespace ASDCP + +#endif // _ATMOSSYNCCHANNEL_MIXER_H_ + +// +// end AtmosSyncChannel_Mixer.h +// diff --git a/src/CRC16.c b/src/CRC16.c new file mode 100644 index 0000000..3e2f09e --- /dev/null +++ b/src/CRC16.c @@ -0,0 +1,86 @@ +/* +Copyright (c) 2013-2013, 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 CRC16.c + \version $Id$ + \brief Implementation of a CRC function +*/ + +#include "CRC16.h" + +static const unsigned short g_aushCRC16tab[256]= { + 0x0000,0x1021,0x2042,0x3063,0x4084,0x50a5,0x60c6,0x70e7, + 0x8108,0x9129,0xa14a,0xb16b,0xc18c,0xd1ad,0xe1ce,0xf1ef, + 0x1231,0x0210,0x3273,0x2252,0x52b5,0x4294,0x72f7,0x62d6, + 0x9339,0x8318,0xb37b,0xa35a,0xd3bd,0xc39c,0xf3ff,0xe3de, + 0x2462,0x3443,0x0420,0x1401,0x64e6,0x74c7,0x44a4,0x5485, + 0xa56a,0xb54b,0x8528,0x9509,0xe5ee,0xf5cf,0xc5ac,0xd58d, + 0x3653,0x2672,0x1611,0x0630,0x76d7,0x66f6,0x5695,0x46b4, + 0xb75b,0xa77a,0x9719,0x8738,0xf7df,0xe7fe,0xd79d,0xc7bc, + 0x48c4,0x58e5,0x6886,0x78a7,0x0840,0x1861,0x2802,0x3823, + 0xc9cc,0xd9ed,0xe98e,0xf9af,0x8948,0x9969,0xa90a,0xb92b, + 0x5af5,0x4ad4,0x7ab7,0x6a96,0x1a71,0x0a50,0x3a33,0x2a12, + 0xdbfd,0xcbdc,0xfbbf,0xeb9e,0x9b79,0x8b58,0xbb3b,0xab1a, + 0x6ca6,0x7c87,0x4ce4,0x5cc5,0x2c22,0x3c03,0x0c60,0x1c41, + 0xedae,0xfd8f,0xcdec,0xddcd,0xad2a,0xbd0b,0x8d68,0x9d49, + 0x7e97,0x6eb6,0x5ed5,0x4ef4,0x3e13,0x2e32,0x1e51,0x0e70, + 0xff9f,0xefbe,0xdfdd,0xcffc,0xbf1b,0xaf3a,0x9f59,0x8f78, + 0x9188,0x81a9,0xb1ca,0xa1eb,0xd10c,0xc12d,0xf14e,0xe16f, + 0x1080,0x00a1,0x30c2,0x20e3,0x5004,0x4025,0x7046,0x6067, + 0x83b9,0x9398,0xa3fb,0xb3da,0xc33d,0xd31c,0xe37f,0xf35e, + 0x02b1,0x1290,0x22f3,0x32d2,0x4235,0x5214,0x6277,0x7256, + 0xb5ea,0xa5cb,0x95a8,0x8589,0xf56e,0xe54f,0xd52c,0xc50d, + 0x34e2,0x24c3,0x14a0,0x0481,0x7466,0x6447,0x5424,0x4405, + 0xa7db,0xb7fa,0x8799,0x97b8,0xe75f,0xf77e,0xc71d,0xd73c, + 0x26d3,0x36f2,0x0691,0x16b0,0x6657,0x7676,0x4615,0x5634, + 0xd94c,0xc96d,0xf90e,0xe92f,0x99c8,0x89e9,0xb98a,0xa9ab, + 0x5844,0x4865,0x7806,0x6827,0x18c0,0x08e1,0x3882,0x28a3, + 0xcb7d,0xdb5c,0xeb3f,0xfb1e,0x8bf9,0x9bd8,0xabbb,0xbb9a, + 0x4a75,0x5a54,0x6a37,0x7a16,0x0af1,0x1ad0,0x2ab3,0x3a92, + 0xfd2e,0xed0f,0xdd6c,0xcd4d,0xbdaa,0xad8b,0x9de8,0x8dc9, + 0x7c26,0x6c07,0x5c64,0x4c45,0x3ca2,0x2c83,0x1ce0,0x0cc1, + 0xef1f,0xff3e,0xcf5d,0xdf7c,0xaf9b,0xbfba,0x8fd9,0x9ff8, + 0x6e17,0x7e36,0x4e55,0x5e74,0x2e93,0x3eb2,0x0ed1,0x1ef0 +}; + +unsigned short CRC16(const void *pData, int ilength) +{ + int n; + unsigned short ushCRC; + unsigned char *puchData; + + + puchData = (unsigned char*)pData; + + ushCRC = 0; + for(n = 0; n < ilength; n ++){ + + ushCRC = (ushCRC << 8) ^ g_aushCRC16tab[((ushCRC>>8) ^ *puchData) & 0x00FF]; + puchData ++; + } + + return ushCRC; +} diff --git a/src/CRC16.h b/src/CRC16.h new file mode 100644 index 0000000..6d3beb3 --- /dev/null +++ b/src/CRC16.h @@ -0,0 +1,45 @@ +/* +Copyright (c) 2013-2013, 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 CRC16.h + \version $Id$ + \brief Declaration of a CRC function +*/ + +#ifndef _CRC_16_H_ +#define _CRC_16_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +unsigned short CRC16(const void *pData, int ilength); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif diff --git a/src/DCData_ByteStream_Parser.cpp b/src/DCData_ByteStream_Parser.cpp new file mode 100644 index 0000000..73565d4 --- /dev/null +++ b/src/DCData_ByteStream_Parser.cpp @@ -0,0 +1,121 @@ +/* +Copyright (c) 2013-2013, 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 AtmosSyncChannel_Mixer.h + \version $Id$ + \brief AS-DCP library, Digital Cinema Data bytestream essence reader +*/ + +#include "AS_DCP.h" +#include "KM_fileio.h" +#include "KM_log.h" +using Kumu::DefaultLogSink; + +//------------------------------------------------------------------------------------------ + +class ASDCP::DCData::BytestreamParser::h__BytestreamParser +{ + ASDCP_NO_COPY_CONSTRUCT(h__BytestreamParser); + +public: + DCDataDescriptor m_DDesc; + Kumu::FileReader m_File; + + h__BytestreamParser() + { + memset(&m_DDesc, 0, sizeof(m_DDesc)); + m_DDesc.EditRate = Rational(24,1); + } + + ~h__BytestreamParser() {} + + 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) ) + { + Kumu::fsize_t file_size = m_File.Size(); + + if ( FB.Capacity() < file_size ) + { + DefaultLogSink().Error("FrameBuf.Capacity: %u frame length: %u\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); + + return result; + } +}; + + +//------------------------------------------------------------------------------------------ + +ASDCP::DCData::BytestreamParser::BytestreamParser() +{ +} + +ASDCP::DCData::BytestreamParser::~BytestreamParser() +{ +} + +// Opens the stream for reading, parses enough data to provide a complete +// set of stream metadata for the MXFWriter below. +ASDCP::Result_t +ASDCP::DCData::BytestreamParser::OpenReadFrame(const char* filename, FrameBuffer& FB) const +{ + const_cast<ASDCP::DCData::BytestreamParser*>(this)->m_Parser = new h__BytestreamParser; + return m_Parser->OpenReadFrame(filename, FB); +} + +// +ASDCP::Result_t +ASDCP::DCData::BytestreamParser::FillDCDataDescriptor(DCDataDescriptor& DDesc) const +{ + if ( m_Parser.empty() ) + return RESULT_INIT; + + DDesc = m_Parser->m_DDesc; + return RESULT_OK; +} + + +// +// end DCData_Bytestream_Parser.cpp +// + + + diff --git a/src/DCData_Sequence_Parser.cpp b/src/DCData_Sequence_Parser.cpp new file mode 100644 index 0000000..45594c5 --- /dev/null +++ b/src/DCData_Sequence_Parser.cpp @@ -0,0 +1,294 @@ +/* +Copyright (c) 2013-2013, 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 AtmosSyncChannel_Mixer.h + \version $Id$ + \brief AS-DCP library, DCinema data seqence reader implementation +*/ + +#include "AS_DCP.h" + +#include <algorithm> +#include <list> +#include <string> + +#include "KM_fileio.h" +#include "KM_log.h" + +using ASDCP::Result_t; + +//------------------------------------------------------------------------------------------ +namespace ASDCP +{ +namespace DCData +{ +class FileList; +} // namespace DCData +} // namespace ASDCP + + +class ASDCP::DCData::FileList : public std::list<std::string> +{ + std::string m_DirName; + + public: + FileList() {} + ~FileList() {} + + const FileList& operator=(const std::list<std::string>& pathlist) { + std::list<std::string>::const_iterator i; + for ( i = pathlist.begin(); i != pathlist.end(); i++ ) + push_back(*i); + return *this; + } + + // + Result_t InitFromDirectory(const char* path) + { + char next_file[Kumu::MaxFilePath]; + Kumu::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; + + if ( ! Kumu::PathIsDirectory(Str) ) + push_back(Str); + } + + sort(); + } + + return result; + } +}; + +//------------------------------------------------------------------------------------------ + +class ASDCP::DCData::SequenceParser::h__SequenceParser +{ + ui32_t m_FramesRead; + Rational m_PictureRate; + FileList m_FileList; + FileList::iterator m_CurrentFile; + BytestreamParser m_Parser; + + Result_t OpenRead(); + + ASDCP_NO_COPY_CONSTRUCT(h__SequenceParser); + + public: + DCDataDescriptor m_DDesc; + + h__SequenceParser() : m_FramesRead(0) + { + memset(&m_DDesc, 0, sizeof(m_DDesc)); + m_DDesc.EditRate = Rational(24,1); + } + + ~h__SequenceParser() + { + Close(); + } + + Result_t OpenRead(const char* filename); + Result_t OpenRead(const std::list<std::string>& file_list); + void Close() {} + + Result_t Reset() + { + m_FramesRead = 0; + m_CurrentFile = m_FileList.begin(); + return RESULT_OK; + } + + Result_t ReadFrame(FrameBuffer&); +}; + + +// +ASDCP::Result_t +ASDCP::DCData::SequenceParser::h__SequenceParser::OpenRead() +{ + if ( m_FileList.empty() ) + return RESULT_ENDOFFILE; + + m_CurrentFile = m_FileList.begin(); + BytestreamParser Parser; + FrameBuffer TmpBuffer; + + Kumu::fsize_t file_size = Kumu::FileSize((*m_CurrentFile).c_str()); + + if ( file_size == 0 ) + return RESULT_NOT_FOUND; + + assert(file_size <= 0xFFFFFFFFL); + Result_t result = TmpBuffer.Capacity((ui32_t) file_size); + + if ( ASDCP_SUCCESS(result) ) + result = Parser.OpenReadFrame((*m_CurrentFile).c_str(), TmpBuffer); + + if ( ASDCP_SUCCESS(result) ) + result = Parser.FillDCDataDescriptor(m_DDesc); + + // how big is it? + if ( ASDCP_SUCCESS(result) ) + m_DDesc.ContainerDuration = m_FileList.size(); + + return result; +} + +// +ASDCP::Result_t +ASDCP::DCData::SequenceParser::h__SequenceParser::OpenRead(const char* filename) +{ + ASDCP_TEST_NULL_STR(filename); + + Result_t result = m_FileList.InitFromDirectory(filename); + + if ( ASDCP_SUCCESS(result) ) + result = OpenRead(); + + return result; +} + + +// +ASDCP::Result_t +ASDCP::DCData::SequenceParser::h__SequenceParser::OpenRead(const std::list<std::string>& file_list) +{ + m_FileList = file_list; + return OpenRead(); +} + +// +ASDCP::Result_t +ASDCP::DCData::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::DCData::SequenceParser::SequenceParser() +{ +} + +ASDCP::DCData::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::DCData::SequenceParser::OpenRead(const char* filename) const +{ + const_cast<ASDCP::DCData::SequenceParser*>(this)->m_Parser = new h__SequenceParser; + + Result_t result = m_Parser->OpenRead(filename); + + if ( ASDCP_FAILURE(result) ) + const_cast<ASDCP::DCData::SequenceParser*>(this)->m_Parser.release(); + + return result; +} + +// +Result_t +ASDCP::DCData::SequenceParser::OpenRead(const std::list<std::string>& file_list) const +{ + const_cast<ASDCP::DCData::SequenceParser*>(this)->m_Parser = new h__SequenceParser; + + Result_t result = m_Parser->OpenRead(file_list); + + if ( ASDCP_FAILURE(result) ) + const_cast<ASDCP::DCData::SequenceParser*>(this)->m_Parser.release(); + + return result; +} + + +// Rewinds the stream to the beginning. +ASDCP::Result_t +ASDCP::DCData::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::DCData::SequenceParser::ReadFrame(FrameBuffer& FB) const +{ + if ( m_Parser.empty() ) + return RESULT_INIT; + + return m_Parser->ReadFrame(FB); +} + +// +ASDCP::Result_t +ASDCP::DCData::SequenceParser::FillDCDataDescriptor(DCDataDescriptor& DDesc) const +{ + if ( m_Parser.empty() ) + return RESULT_INIT; + + DDesc = m_Parser->m_DDesc; + return RESULT_OK; +} + + +// +// end DCData_Sequence_Parser.cpp +// + diff --git a/src/Index.cpp b/src/Index.cpp index 449d5bf..f377585 100755 --- a/src/Index.cpp +++ b/src/Index.cpp @@ -143,7 +143,7 @@ ASDCP::MXF::IndexTableSegment::Dump(FILE* stream) } else { - fprintf(stream, " IndexEntryArray: %du entries\n", IndexEntryArray.size()); + fprintf(stream, " IndexEntryArray: %zu entries\n", IndexEntryArray.size()); } } diff --git a/src/MDD.cpp b/src/MDD.cpp index 5627624..5afb75e 100644 --- a/src/MDD.cpp +++ b/src/MDD.cpp @@ -1,5 +1,5 @@ /* -Copyright (c) 2006-2012, John Hurst +Copyright (c) 2006-2013, John Hurst All rights reserved. Redistribution and use in source and binary forms, with or without @@ -917,6 +917,85 @@ static const ASDCP::MDDEntry s_MDD_Table[] = { { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x0d, // 293 0x01, 0x03, 0x07, 0x01, 0x04, 0x00, 0x00, 0x00 }, {0}, false, "SoundfieldGroupLabelSubDescriptor_GroupOfSoundfieldGroupsLinkID" }, + { { 0x06, 0x0e, 0x2b, 0x34, 0x04, 0x01, 0x01, 0x05, // 294 + 0x0e, 0x09, 0x06, 0x05, 0x00, 0x00, 0x00, 0x00 }, + {0}, false, "DCDataWrapping" }, + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x02, 0x01, 0x05, // 295 + 0x0e, 0x09, 0x06, 0x01, 0x00, 0x00, 0x00, 0x00 }, + {0}, false, "DCDataEssence" }, + { { 0x06, 0x0e, 0x2b, 0x34, 0x02, 0x53, 0x01, 0x05, // 296 + 0x0e, 0x09, 0x06, 0x03, 0x00, 0x00, 0x00, 0x00 }, + {0}, false, "DCDataDescriptor" }, + { { 0x06, 0x0e, 0x2b, 0x34, 0x02, 0x53, 0x01, 0x05, // 297 + 0x0e, 0x09, 0x06, 0x06, 0x00, 0x00, 0x00, 0x00 }, + {0}, false, "DolbyAtmosSubDescriptor" }, + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x05, // 298 + 0x0e, 0x09, 0x05, 0x06, 0x00, 0x00, 0x00, 0x00 }, + {0}, true, "DolbyAtmosSubDescriptor_AtmosVersion" }, + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x05, // 299 + 0x0e, 0x09, 0x05, 0x07, 0x00, 0x00, 0x00, 0x00 }, + {0}, true, "DolbyAtmosSubDescriptor_MaxChannelCount" }, + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x05, // 300 + 0x0e, 0x09, 0x05, 0x08, 0x00, 0x00, 0x00, 0x00 }, + {0}, true, "DolbyAtmosSubDescriptor_MaxObjectCount" }, + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x05, // 301 + 0x0e, 0x09, 0x05, 0x09, 0x00, 0x00, 0x00, 0x00 }, + {0}, true, "DolbyAtmosSubDescriptor_AtmosID" }, + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x05, // 302 + 0x0e, 0x09, 0x05, 0x0A, 0x00, 0x00, 0x00, 0x00 }, + {0}, true, "DolbyAtmosSubDescriptor_FirstFrame" }, + { { 0x06, 0x0e, 0x2b, 0x34, 0x04, 0x01, 0x01, 0x01, // 303 + 0x01, 0x03, 0x02, 0x02, 0x03, 0x00, 0x00, 0x00 }, + {0}, false, "DataDataDef" }, + { { 0x06, 0x0e, 0x2b, 0x34, 0x04, 0x01, 0x01, 0x0d, // 304 + 0x04, 0x02, 0x02, 0x10, 0x03, 0x02, 0x00, 0x00 }, + {0}, false, "DCAudioChannelCfg_MCA" }, + { { 0x06, 0x0e, 0x2b, 0x34, 0x04, 0x01, 0x01, 0x0d, // 305 + 0x03, 0x02, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00 }, + {0}, false, "DCAudioChannel_L" }, + { { 0x06, 0x0e, 0x2b, 0x34, 0x04, 0x01, 0x01, 0x0d, // 306 + 0x03, 0x02, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00 }, + {0}, false, "DCAudioChannel_R" }, + { { 0x06, 0x0e, 0x2b, 0x34, 0x04, 0x01, 0x01, 0x0d, // 307 + 0x03, 0x02, 0x01, 0x03, 0x00, 0x00, 0x00, 0x00 }, + {0}, false, "DCAudioChannel_C" }, + { { 0x06, 0x0e, 0x2b, 0x34, 0x04, 0x01, 0x01, 0x0d, // 308 + 0x03, 0x02, 0x01, 0x04, 0x00, 0x00, 0x00, 0x00 }, + {0}, false, "DCAudioChannel_LFE" }, + { { 0x06, 0x0e, 0x2b, 0x34, 0x04, 0x01, 0x01, 0x0d, // 309 + 0x03, 0x02, 0x01, 0x05, 0x00, 0x00, 0x00, 0x00 }, + {0}, false, "DCAudioChannel_Ls" }, + { { 0x06, 0x0e, 0x2b, 0x34, 0x04, 0x01, 0x01, 0x0d, // 310 + 0x03, 0x02, 0x01, 0x06, 0x00, 0x00, 0x00, 0x00 }, + {0}, false, "DCAudioChannel_Rs" }, + { { 0x06, 0x0e, 0x2b, 0x34, 0x04, 0x01, 0x01, 0x0d, // 311 + 0x03, 0x02, 0x01, 0x07, 0x00, 0x00, 0x00, 0x00 }, + {0}, false, "DCAudioChannel_Lss" }, + { { 0x06, 0x0e, 0x2b, 0x34, 0x04, 0x01, 0x01, 0x0d, // 312 + 0x03, 0x02, 0x01, 0x08, 0x00, 0x00, 0x00, 0x00 }, + {0}, false, "DCAudioChannel_Rss" }, + { { 0x06, 0x0e, 0x2b, 0x34, 0x04, 0x01, 0x01, 0x0d, // 313 + 0x03, 0x02, 0x01, 0x09, 0x00, 0x00, 0x00, 0x00 }, + {0}, false, "DCAudioChannel_Lrs" }, + { { 0x06, 0x0e, 0x2b, 0x34, 0x04, 0x01, 0x01, 0x0d, // 314 + 0x03, 0x02, 0x01, 0x0a, 0x00, 0x00, 0x00, 0x00 }, + {0}, false, "DCAudioChannel_Rrs" }, + { { 0x06, 0x0e, 0x2b, 0x34, 0x04, 0x01, 0x01, 0x0d, // 315 + 0x03, 0x02, 0x01, 0x0b, 0x00, 0x00, 0x00, 0x00 }, + {0}, false, "DCAudioChannel_Lc" }, + { { 0x06, 0x0e, 0x2b, 0x34, 0x04, 0x01, 0x01, 0x0d, // 316 + 0x03, 0x02, 0x01, 0x0c, 0x00, 0x00, 0x00, 0x00 }, + {0}, false, "DCAudioChannel_Rc" }, + { { 0x06, 0x0e, 0x2b, 0x34, 0x04, 0x01, 0x01, 0x0d, // 317 + 0x03, 0x02, 0x01, 0x0d, 0x00, 0x00, 0x00, 0x00 }, + {0}, false, "DCAudioChannel_Cs" }, + { { 0x06, 0x0e, 0x2b, 0x34, 0x04, 0x01, 0x01, 0x0d, // 318 + 0x03, 0x02, 0x01, 0x0e, 0x00, 0x00, 0x00, 0x00 }, + {0}, false, "DCAudioChannel_HI" }, + { { 0x06, 0x0e, 0x2b, 0x34, 0x04, 0x01, 0x01, 0x0d, // 319 + 0x03, 0x02, 0x01, 0x0f, 0x00, 0x00, 0x00, 0x00 }, + {0}, false, "DCAudioChannel_VIN" }, + { {0}, {0}, false, 0 } }; @@ -1,5 +1,5 @@ /* -Copyright (c) 2006-2012, John Hurst +Copyright (c) 2006-2013, John Hurst All rights reserved. Redistribution and use in source and binary forms, with or without @@ -290,46 +290,72 @@ namespace ASDCP { MDD_CryptographicContext_CipherAlgorithm, // 252 MDD_CryptographicContext_MICAlgorithm, // 253 MDD_CryptographicContext_CryptographicKeyID, // 254 - MDD_TimedTextWrapping, // 255 - MDD_TimedTextEssence, // 256 - MDD_TimedTextDescriptor, // 257 - MDD_TimedTextDescriptor_ResourceID, // 258 - MDD_TimedTextDescriptor_UCSEncoding, // 259 - MDD_TimedTextDescriptor_NamespaceURI, // 260 - MDD_TimedTextResourceSubDescriptor, // 261 - MDD_TimedTextResourceSubDescriptor_AncillaryResourceID, // 262 - MDD_TimedTextResourceSubDescriptor_MIMEMediaType, // 263 - MDD_TimedTextResourceSubDescriptor_EssenceStreamID_DEPRECATED, // 264 - MDD_GenericStreamPartition, // 265 - MDD_DMSegment_DataDefinition_DEPRECATED, // 266 - MDD_DMSegment_Duration_DEPRECATED, // 267 - MDD_DMSegment_TrackIDList, // 268 - MDD_StereoscopicPictureSubDescriptor, // 269 + MDD_TimedTextWrapping, // 255 + MDD_TimedTextEssence, // 256 + MDD_TimedTextDescriptor, // 257 + MDD_TimedTextDescriptor_ResourceID, // 258 + MDD_TimedTextDescriptor_UCSEncoding, // 259 + MDD_TimedTextDescriptor_NamespaceURI, // 260 + MDD_TimedTextResourceSubDescriptor, // 261 + MDD_TimedTextResourceSubDescriptor_AncillaryResourceID, // 262 + MDD_TimedTextResourceSubDescriptor_MIMEMediaType, // 263 + MDD_TimedTextResourceSubDescriptor_EssenceStreamID_DEPRECATED, // 264 + MDD_GenericStreamPartition, // 265 + MDD_DMSegment_DataDefinition_DEPRECATED, // 266 + MDD_DMSegment_Duration_DEPRECATED, // 267 + MDD_DMSegment_TrackIDList, // 268 + MDD_StereoscopicPictureSubDescriptor, // 269 MDD_WaveAudioDescriptor_ChannelAssignment, // 270 - MDD_GenericStream_DataElement, // 271 + MDD_GenericStream_DataElement, // 271 MDD_MXFInterop_GenericDescriptor_SubDescriptors, // 272 - MDD_Core_BodySID, // 273 - MDD_Core_IndexSID, // 274 - MDD_Core_OperationalPattern, // 275 - MDD_Core_EssenceContainers, // 276 - MDD_DCAudioChannelCfg_1_5p1, // 277 - MDD_DCAudioChannelCfg_2_6p1, // 278 - MDD_DCAudioChannelCfg_3_7p1, // 279 - MDD_DCAudioChannelCfg_4_WTF, // 280 - MDD_DCAudioChannelCfg_5_7p1_DS, // 281 - MDD_MCALabelSubDescriptor, // 282 - MDD_AudioChannelLabelSubDescriptor, // 283 - MDD_SoundfieldGroupLabelSubDescriptor, // 284 - MDD_GroupOfSoundfieldGroupsLabelSubDescriptor, // 285 - MDD_MCALabelSubDescriptor_MCALabelDictionaryID, // 286 - MDD_MCALabelSubDescriptor_MCALinkID, // 287 - MDD_MCALabelSubDescriptor_MCATagSymbol, // 288 - MDD_MCALabelSubDescriptor_MCATagName, // 289 - MDD_MCALabelSubDescriptor_MCAChannelID, // 290 - MDD_MCALabelSubDescriptor_RFC5646SpokenLanguage, // 291 - MDD_AudioChannelLabelSubDescriptor_SoundfieldGroupLinkID, // 292 - MDD_SoundfieldGroupLabelSubDescriptor_GroupOfSoundfieldGroupsLinkID, // 293 - MDD_Max + MDD_Core_BodySID, // 273 + MDD_Core_IndexSID, // 274 + MDD_Core_OperationalPattern, // 275 + MDD_Core_EssenceContainers, // 276 + MDD_DCAudioChannelCfg_1_5p1, // 277 + MDD_DCAudioChannelCfg_2_6p1, // 278 + MDD_DCAudioChannelCfg_3_7p1, // 279 + MDD_DCAudioChannelCfg_4_WTF, // 280 + MDD_DCAudioChannelCfg_5_7p1_DS, // 281 + MDD_MCALabelSubDescriptor, // 282 + MDD_AudioChannelLabelSubDescriptor, // 283 + MDD_SoundfieldGroupLabelSubDescriptor, // 284 + MDD_GroupOfSoundfieldGroupsLabelSubDescriptor, // 285 + MDD_MCALabelSubDescriptor_MCALabelDictionaryID, // 286 + MDD_MCALabelSubDescriptor_MCALinkID, // 287 + MDD_MCALabelSubDescriptor_MCATagSymbol, // 288 + MDD_MCALabelSubDescriptor_MCATagName, // 289 + MDD_MCALabelSubDescriptor_MCAChannelID, // 290 + MDD_MCALabelSubDescriptor_RFC5646SpokenLanguage, // 291 + MDD_AudioChannelLabelSubDescriptor_SoundfieldGroupLinkID, // 292 + MDD_SoundfieldGroupLabelSubDescriptor_GroupOfSoundfieldGroupsLinkID, // 293 + MDD_DCDataWrapping, // 294 + MDD_DCDataEssence, // 295 + MDD_DCDataDescriptor, // 296 + MDD_DolbyAtmosSubDescriptor, // 297 + MDD_DolbyAtmosSubDescriptor_AtmosVersion, // 298 + MDD_DolbyAtmosSubDescriptor_MaxChannelCount, // 299 + MDD_DolbyAtmosSubDescriptor_MaxObjectCount, // 300 + MDD_DolbyAtmosSubDescriptor_AtmosID, // 301 + MDD_DolbyAtmosSubDescriptor_FirstFrame, // 302 + MDD_DataDataDef, // 303 + MDD_DCAudioChannelCfg_MCA, // 304 + MDD_DCAudioChannel_L, // 305 + MDD_DCAudioChannel_R, // 306 + MDD_DCAudioChannel_C, // 307 + MDD_DCAudioChannel_LFE, // 308 + MDD_DCAudioChannel_Ls, // 309 + MDD_DCAudioChannel_Rs, // 310 + MDD_DCAudioChannel_Lss, // 311 + MDD_DCAudioChannel_Rss, // 312 + MDD_DCAudioChannel_Lrs, // 313 + MDD_DCAudioChannel_Rrs, // 314 + MDD_DCAudioChannel_Lc, // 315 + MDD_DCAudioChannel_Rc, // 316 + MDD_DCAudioChannel_Cs, // 317 + MDD_DCAudioChannel_HI, // 318 + MDD_DCAudioChannel_VIN, // 319 + MDD_Max }; // enum MDD_t @@ -342,7 +368,7 @@ namespace ASDCP { const MDD_t MDD_Preface_EssenceContainers = MDD_Core_EssenceContainers; const MDD_t MDD_Preface_OperationalPattern = MDD_Core_OperationalPattern; const MDD_t MDD_TimedTextResourceSubDescriptor_EssenceStreamID = MDD_Core_BodySID; - + } // namespaceASDCP diff --git a/src/MXF.cpp b/src/MXF.cpp index 7f042ee..1b9e558 100755 --- a/src/MXF.cpp +++ b/src/MXF.cpp @@ -725,12 +725,20 @@ ASDCP::MXF::OPAtomHeader::InitFromFile(const Kumu::FileReader& Reader) } } } + else + { + DefaultLogSink().Error("OPAtomHeader::InitFromFile, SeekToRIP failed\n"); + } if ( ASDCP_SUCCESS(result) ) result = Reader.Seek(0); + else + DefaultLogSink().Error("OPAtomHeader::InitFromFile, Seek failed\n"); if ( ASDCP_SUCCESS(result) ) result = Partition::InitFromFile(Reader); // test UL and OP + else + DefaultLogSink().Error("OPAtomHeader::InitFromFile, Partition::InitFromFile failed\n"); if ( ASDCP_FAILURE(result) ) return result; @@ -774,7 +782,10 @@ ASDCP::MXF::OPAtomHeader::InitFromFile(const Kumu::FileReader& Reader) result = Reader.Read(m_Buffer.Data(), m_Buffer.Capacity(), &read_count); if ( ASDCP_FAILURE(result) ) - return result; + { + DefaultLogSink().Error("OPAtomHeader::InitFromFile, Read failed\n"); + return result; + } if ( read_count != m_Buffer.Capacity() ) { @@ -1038,17 +1049,21 @@ ASDCP::MXF::OPAtomIndexFooter::InitFromFile(const Kumu::FileReader& Reader) { Result_t result = Partition::InitFromFile(Reader); // test UL and OP - // slurp up the remainder of the footer - ui32_t read_count; + // slurp up the remainder of the footer + ui32_t read_count = 0; - if ( ASDCP_SUCCESS(result) ) + if ( ASDCP_SUCCESS(result) ) { - assert (IndexByteCount <= 0xFFFFFFFFL); - result = m_Buffer.Capacity((ui32_t) IndexByteCount); + assert (IndexByteCount <= 0xFFFFFFFFL); + // At this point, m_Buffer may not have been initialized + // so it's capacity is zero and data pointer is NULL + // However, if IndexByteCount is zero then the capacity + // doesn't change and the data pointer is not set. + result = m_Buffer.Capacity((ui32_t) IndexByteCount); } - if ( ASDCP_SUCCESS(result) ) - result = Reader.Read(m_Buffer.Data(), m_Buffer.Capacity(), &read_count); + if ( ASDCP_SUCCESS(result) && m_Buffer.Data() ) + result = Reader.Read(m_Buffer.Data(), m_Buffer.Capacity(), &read_count); if ( ASDCP_SUCCESS(result) && read_count != m_Buffer.Capacity() ) { @@ -1056,6 +1071,12 @@ ASDCP::MXF::OPAtomIndexFooter::InitFromFile(const Kumu::FileReader& Reader) read_count, m_Buffer.Capacity()); return RESULT_FAIL; } + else if( ASDCP_SUCCESS(result) && !m_Buffer.Data() ) + { + DefaultLogSink().Error( "Buffer for footer partition not created: IndexByteCount = %u\n", + IndexByteCount ); + return RESULT_FAIL; + } if ( ASDCP_SUCCESS(result) ) result = InitFromBuffer(m_Buffer.RoData(), m_Buffer.Capacity()); diff --git a/src/Makefile.am b/src/Makefile.am index 6cd2da3..fdbf3c4 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -61,7 +61,8 @@ include_HEADERS += \ MXFTypes.h \ MXF.h \ Wav.h \ - PCMParserList.h + PCMParserList.h \ + AtmosSyncChannel_Generator.h nodist_include_HEADERS = TimedText_Transform.h endif @@ -89,7 +90,16 @@ libasdcp_la_SOURCES = MPEG2_Parser.cpp MPEG.cpp JP2K_Codestream_Parser.cpp \ AS_DCP_PCM.cpp AS_DCP_TimedText.cpp PCMParserList.cpp \ Wav.h WavFileWriter.h MXF.h Metadata.h \ JP2K.h AS_DCP.h AS_DCP_internal.h KLV.h MPEG.h MXFTypes.h MDD.h \ - PCMParserList.h S12MTimecode.h MDD.cpp + PCMParserList.h S12MTimecode.h MDD.cpp \ + AS_DCP_ATMOS.cpp AS_DCP_DCData.cpp AS_DCP_DCData_internal.h \ + DCData_ByteStream_Parser.cpp DCData_Sequence_Parser.cpp \ + AtmosSyncChannel_Generator.cpp AtmosSyncChannel_Generator.h \ + AtmosSyncChannel_Mixer.cpp AtmosSyncChannel_Mixer.h \ + PCMDataProviders.cpp PCMDataProviders.h \ + SyncEncoder.c SyncEncoder.h SyncCommon.h CRC16.c CRC16.h \ + UUIDInformation.c UUIDInformation.h + + if DEV_HEADERS nodist_libasdcp_la_SOURCES += TimedText_Transform.h TimedText_Transform.cpp endif diff --git a/src/Metadata.cpp b/src/Metadata.cpp index ac1db76..3507ba0 100755 --- a/src/Metadata.cpp +++ b/src/Metadata.cpp @@ -73,6 +73,8 @@ static InterchangeObject* MCALabelSubDescriptor_Factory(const Dictionary*& Dict) static InterchangeObject* AudioChannelLabelSubDescriptor_Factory(const Dictionary*& Dict) { return new AudioChannelLabelSubDescriptor(Dict); } static InterchangeObject* SoundfieldGroupLabelSubDescriptor_Factory(const Dictionary*& Dict) { return new SoundfieldGroupLabelSubDescriptor(Dict); } static InterchangeObject* GroupOfSoundfieldGroupsLabelSubDescriptor_Factory(const Dictionary*& Dict) { return new GroupOfSoundfieldGroupsLabelSubDescriptor(Dict); } +static InterchangeObject* DCDataDescriptor_Factory(const Dictionary*& Dict) { return new DCDataDescriptor(Dict); } +static InterchangeObject* DolbyAtmosSubDescriptor_Factory(const Dictionary*& Dict) { return new DolbyAtmosSubDescriptor(Dict); } void @@ -112,6 +114,8 @@ ASDCP::MXF::Metadata_InitTypes(const Dictionary*& Dict) SetObjectFactory(Dict->ul(MDD_AudioChannelLabelSubDescriptor), AudioChannelLabelSubDescriptor_Factory); SetObjectFactory(Dict->ul(MDD_SoundfieldGroupLabelSubDescriptor), SoundfieldGroupLabelSubDescriptor_Factory); SetObjectFactory(Dict->ul(MDD_GroupOfSoundfieldGroupsLabelSubDescriptor), GroupOfSoundfieldGroupsLabelSubDescriptor_Factory); + SetObjectFactory(Dict->ul(MDD_DCDataDescriptor), DCDataDescriptor_Factory); + SetObjectFactory(Dict->ul(MDD_DolbyAtmosSubDescriptor), DolbyAtmosSubDescriptor_Factory); } //------------------------------------------------------------------------------------------ @@ -2888,6 +2892,170 @@ GroupOfSoundfieldGroupsLabelSubDescriptor::WriteToBuffer(ASDCP::FrameBuffer& Buf return InterchangeObject::WriteToBuffer(Buffer); } +//------------------------------------------------------------------------------------------ +// DCDataDescriptor + +// + +DCDataDescriptor::DCDataDescriptor(const Dictionary*& d) : GenericDataEssenceDescriptor(d), m_Dict(d) +{ + assert(m_Dict); + m_UL = m_Dict->ul(MDD_DCDataDescriptor); +} + +DCDataDescriptor::DCDataDescriptor(const DCDataDescriptor& rhs) : GenericDataEssenceDescriptor(rhs.m_Dict), m_Dict(rhs.m_Dict) +{ + assert(m_Dict); + m_UL = m_Dict->ul(MDD_DCDataDescriptor); + Copy(rhs); +} + +// +ASDCP::Result_t +DCDataDescriptor::InitFromTLVSet(TLVReader& TLVSet) +{ + // NOTE (this function can be removed if no attributes are defined for this descriptor) + assert(m_Dict); + Result_t result = GenericDataEssenceDescriptor::InitFromTLVSet(TLVSet); + return result; +} + +// +ASDCP::Result_t +DCDataDescriptor::WriteToTLVSet(TLVWriter& TLVSet) +{ + // NOTE (this function can be removed if no attributes are defined for this descriptor) + assert(m_Dict); + Result_t result = GenericDataEssenceDescriptor::WriteToTLVSet(TLVSet); + return result; +} + +// +void +DCDataDescriptor::Copy(const DCDataDescriptor& rhs) +{ + GenericDataEssenceDescriptor::Copy(rhs); +} + +// +void +DCDataDescriptor::Dump(FILE* stream) +{ + char identbuf[IdentBufferLen]; + *identbuf = 0; + + if ( stream == 0 ) + stream = stderr; + + GenericDataEssenceDescriptor::Dump(stream); +} + +// +ASDCP::Result_t +DCDataDescriptor::InitFromBuffer(const byte_t* p, ui32_t l) +{ + return InterchangeObject::InitFromBuffer(p, l); +} + +// +ASDCP::Result_t +DCDataDescriptor::WriteToBuffer(ASDCP::FrameBuffer& Buffer) +{ + return InterchangeObject::WriteToBuffer(Buffer); +} + +//------------------------------------------------------------------------------------------ +// DolbyAtmosSubDescriptor + +// + +DolbyAtmosSubDescriptor::DolbyAtmosSubDescriptor(const Dictionary*& d) : InterchangeObject(d), m_Dict(d), FirstFrame(0), MaxChannelCount(0), MaxObjectCount(0), AtmosVersion(0) +{ + assert(m_Dict); + m_UL = m_Dict->ul(MDD_DolbyAtmosSubDescriptor); +} + +DolbyAtmosSubDescriptor::DolbyAtmosSubDescriptor(const DolbyAtmosSubDescriptor& rhs) : InterchangeObject(rhs.m_Dict), m_Dict(rhs.m_Dict) +{ + assert(m_Dict); + m_UL = m_Dict->ul(MDD_DolbyAtmosSubDescriptor); + Copy(rhs); +} + + +// +ASDCP::Result_t +DolbyAtmosSubDescriptor::InitFromTLVSet(TLVReader& TLVSet) +{ + assert(m_Dict); + Result_t result = InterchangeObject::InitFromTLVSet(TLVSet); + if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadObject(OBJ_READ_ARGS(DolbyAtmosSubDescriptor, AtmosID)); + if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadUi32(OBJ_READ_ARGS(DolbyAtmosSubDescriptor, FirstFrame)); + if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadUi16(OBJ_READ_ARGS(DolbyAtmosSubDescriptor, MaxChannelCount)); + if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadUi16(OBJ_READ_ARGS(DolbyAtmosSubDescriptor, MaxObjectCount)); + if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadUi8(OBJ_READ_ARGS(DolbyAtmosSubDescriptor, AtmosVersion)); + return result; +} + +// +ASDCP::Result_t +DolbyAtmosSubDescriptor::WriteToTLVSet(TLVWriter& TLVSet) +{ + assert(m_Dict); + Result_t result = InterchangeObject::WriteToTLVSet(TLVSet); + if ( ASDCP_SUCCESS(result) ) result = TLVSet.WriteObject(OBJ_WRITE_ARGS(DolbyAtmosSubDescriptor, AtmosID)); + if ( ASDCP_SUCCESS(result) ) result = TLVSet.WriteUi32(OBJ_WRITE_ARGS(DolbyAtmosSubDescriptor, FirstFrame)); + if ( ASDCP_SUCCESS(result) ) result = TLVSet.WriteUi16(OBJ_WRITE_ARGS(DolbyAtmosSubDescriptor, MaxChannelCount)); + if ( ASDCP_SUCCESS(result) ) result = TLVSet.WriteUi16(OBJ_WRITE_ARGS(DolbyAtmosSubDescriptor, MaxObjectCount)); + if ( ASDCP_SUCCESS(result) ) result = TLVSet.WriteUi8(OBJ_WRITE_ARGS(DolbyAtmosSubDescriptor, AtmosVersion)); + return result; +} + +// +void +DolbyAtmosSubDescriptor::Copy(const DolbyAtmosSubDescriptor& rhs) +{ + InterchangeObject::Copy(rhs); + AtmosID = rhs.AtmosID; + FirstFrame = rhs.FirstFrame; + MaxChannelCount = rhs.MaxChannelCount; + MaxObjectCount = rhs.MaxObjectCount; + AtmosVersion = rhs.AtmosVersion; +} + +// +void +DolbyAtmosSubDescriptor::Dump(FILE* stream) +{ + char identbuf[IdentBufferLen]; + *identbuf = 0; + + if ( stream == 0 ) + stream = stderr; + + InterchangeObject::Dump(stream); + fprintf(stream, " %22s = %s\n", "AtmosID", AtmosID.EncodeString(identbuf, IdentBufferLen)); + fprintf(stream, " %22s = %d\n", "FirstFrame", FirstFrame); + fprintf(stream, " %22s = %d\n", "MaxChannelCount", MaxChannelCount); + fprintf(stream, " %22s = %d\n", "MaxObjectCount", MaxObjectCount); + fprintf(stream, " %22s = %d\n", "AtmosVersion", AtmosVersion); +} + +// +ASDCP::Result_t +DolbyAtmosSubDescriptor::InitFromBuffer(const byte_t* p, ui32_t l) +{ + return InterchangeObject::InitFromBuffer(p, l); +} + +// +ASDCP::Result_t +DolbyAtmosSubDescriptor::WriteToBuffer(ASDCP::FrameBuffer& Buffer) +{ + return InterchangeObject::WriteToBuffer(Buffer); +} + + // // end Metadata.cpp // diff --git a/src/Metadata.h b/src/Metadata.h index 507a151..6b9a50c 100755 --- a/src/Metadata.h +++ b/src/Metadata.h @@ -889,6 +889,55 @@ namespace ASDCP virtual Result_t WriteToBuffer(ASDCP::FrameBuffer&); }; + // + class DCDataDescriptor : public GenericDataEssenceDescriptor + { + DCDataDescriptor(); + + public: + const Dictionary*& m_Dict; + + DCDataDescriptor(const Dictionary*& d); + DCDataDescriptor(const DCDataDescriptor& rhs); + virtual ~DCDataDescriptor() {} + + const DCDataDescriptor& operator=(const DCDataDescriptor& rhs) { Copy(rhs); return *this; } + virtual void Copy(const DCDataDescriptor& rhs); + virtual const char* HasName() { return "DCDataDescriptor"; } + virtual Result_t InitFromTLVSet(TLVReader& TLVSet); + virtual Result_t WriteToTLVSet(TLVWriter& TLVSet); + virtual void Dump(FILE* = 0); + virtual Result_t InitFromBuffer(const byte_t* p, ui32_t l); + virtual Result_t WriteToBuffer(ASDCP::FrameBuffer&); + }; + + // + class DolbyAtmosSubDescriptor : public InterchangeObject + { + DolbyAtmosSubDescriptor(); + + public: + const Dictionary*& m_Dict; + UUID AtmosID; + ui32_t FirstFrame; + ui16_t MaxChannelCount; + ui16_t MaxObjectCount; + ui8_t AtmosVersion; + + DolbyAtmosSubDescriptor(const Dictionary*& d); + DolbyAtmosSubDescriptor(const DolbyAtmosSubDescriptor& rhs); + virtual ~DolbyAtmosSubDescriptor() {} + + const DolbyAtmosSubDescriptor& operator=(const DolbyAtmosSubDescriptor& rhs) { Copy(rhs); return *this; } + virtual void Copy(const DolbyAtmosSubDescriptor& rhs); + virtual const char* HasName() { return "DolbyAtmosSubDescriptor"; } + virtual Result_t InitFromTLVSet(TLVReader& TLVSet); + virtual Result_t WriteToTLVSet(TLVWriter& TLVSet); + virtual void Dump(FILE* = 0); + virtual Result_t InitFromBuffer(const byte_t* p, ui32_t l); + virtual Result_t WriteToBuffer(ASDCP::FrameBuffer&); + }; + } // namespace MXF } // namespace ASDCP diff --git a/src/PCMDataProviders.cpp b/src/PCMDataProviders.cpp new file mode 100644 index 0000000..7d2c152 --- /dev/null +++ b/src/PCMDataProviders.cpp @@ -0,0 +1,210 @@ +/* +Copyright (c) 20013-2013, 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 PCMDataProviders.cpp + \version $Id$ + \brief Implementation of PCM sample data providers for WAV, AtmosSync and Silence. +*/ + +#include <PCMDataProviders.h> + +#include <KM_log.h> + +using namespace ASDCP; +using namespace Kumu; + +ASDCP::PCMDataProviderInterface::~PCMDataProviderInterface() {} + +// +ASDCP::WAVDataProvider::WAVDataProvider() + : m_Parser(), m_FB(), m_ADesc(), m_SampleSize(0), m_ptr(NULL) +{} + +ASDCP::WAVDataProvider::~WAVDataProvider() +{} + +Result_t +ASDCP::WAVDataProvider::PutSample(const ui32_t numChannels, byte_t* buf, ui32_t* bytesWritten) +{ + ASDCP_TEST_NULL(buf); + ASDCP_TEST_NULL(m_ptr); + if ( numChannels > m_ADesc.ChannelCount) + { + DefaultLogSink().Error("Requested %u channels from a wav file with %u channel.", numChannels, + m_ADesc.ChannelCount); + return RESULT_FAIL; + } + *bytesWritten = m_SampleSize * numChannels; + ::memcpy(buf, m_ptr, *bytesWritten); + m_ptr += *bytesWritten; + return RESULT_OK; +} + +Result_t +ASDCP::WAVDataProvider::ReadFrame() +{ + Result_t result = m_Parser.ReadFrame(m_FB); + m_ptr = ASDCP_SUCCESS(result) ? m_FB.RoData() : NULL; + return result; +} + +Result_t +ASDCP::WAVDataProvider::FillAudioDescriptor(PCM::AudioDescriptor& ADesc) const +{ + ADesc = m_ADesc; + return RESULT_OK; +} + +Result_t +ASDCP::WAVDataProvider::Reset() +{ + return m_Parser.Reset(); +} + +Result_t +ASDCP::WAVDataProvider::OpenRead(const char* filename, const Rational& PictureRate) +{ + ASDCP_TEST_NULL_STR(filename); + + Result_t result = m_Parser.OpenRead(filename, PictureRate); + + if ( ASDCP_SUCCESS(result) ) + result = m_Parser.FillAudioDescriptor(m_ADesc); + + if ( ASDCP_SUCCESS(result) ) + { + m_ADesc.EditRate = PictureRate; + m_SampleSize = ((m_ADesc.QuantizationBits + 7) / 8); + result = m_FB.Capacity(PCM::CalcFrameBufferSize(m_ADesc)); + } + + return result; +} + +// +ASDCP::AtmosSyncDataProvider::AtmosSyncDataProvider(const ui16_t bitsPerSample, const ui32_t sampleRate, + const ASDCP::Rational& editRate, const byte_t* uuid) + : m_Generator(bitsPerSample, sampleRate, editRate, uuid), m_FB(), m_ADesc(), m_SampleSize() +{ + m_Generator.FillAudioDescriptor(m_ADesc); + m_SampleSize = PCM::CalcSampleSize(m_ADesc); + m_FB.Capacity(PCM::CalcFrameBufferSize(m_ADesc)); +} + +ASDCP::AtmosSyncDataProvider::~AtmosSyncDataProvider() +{} + +Result_t +ASDCP::AtmosSyncDataProvider::PutSample(const ui32_t numChannels, byte_t* buf, ui32_t* bytesWritten) +{ + ASDCP_TEST_NULL(buf); + ASDCP_TEST_NULL(m_ptr); + if ( numChannels > m_ADesc.ChannelCount) + { + DefaultLogSink().Error("Requested %u channels from a wav file with %u channel.", numChannels, + m_ADesc.ChannelCount); + return RESULT_FAIL; + } + + (*bytesWritten) = m_SampleSize; + ::memcpy(buf, m_ptr, m_SampleSize); + m_ptr += m_SampleSize; + return RESULT_OK; +} + +Result_t +ASDCP::AtmosSyncDataProvider::ReadFrame() +{ + Result_t result = m_Generator.ReadFrame(m_FB); + m_ptr = ASDCP_SUCCESS(result) ? m_FB.RoData() : NULL; + return result; +} + +Result_t +ASDCP::AtmosSyncDataProvider::FillAudioDescriptor(PCM::AudioDescriptor& ADesc) const +{ + ADesc = m_ADesc; + return RESULT_OK; +} + +Result_t +ASDCP::AtmosSyncDataProvider::Reset() +{ + return m_Generator.Reset(); +} + +// +ASDCP::SilenceDataProvider::SilenceDataProvider(const ui16_t numChannels, const ui16_t bitsPerSample, + const ui32_t sampleRate, const ASDCP::Rational& editRate) + : m_ADesc(), m_SampleSize(0) +{ + m_SampleSize = ((bitsPerSample + 7) / 8); + m_ADesc.EditRate = editRate; + m_ADesc.AudioSamplingRate = Rational(sampleRate, 1); + m_ADesc.ChannelCount = numChannels; + m_ADesc.QuantizationBits = bitsPerSample; + m_ADesc.BlockAlign = numChannels * m_SampleSize; + m_ADesc.AvgBps = sampleRate * m_ADesc.BlockAlign; +} + +ASDCP::SilenceDataProvider::~SilenceDataProvider() +{} + +Result_t +ASDCP::SilenceDataProvider::PutSample(const ui32_t numChannels, byte_t* buf, ui32_t* bytesWritten) +{ + ASDCP_TEST_NULL(buf); + if ( numChannels > m_ADesc.ChannelCount) + { + DefaultLogSink().Error("Requested %u channels from a wav file with %u channel.", numChannels, + m_ADesc.ChannelCount); + return RESULT_FAIL; + } + (*bytesWritten) = m_SampleSize * numChannels; + ::memset(buf, 0, (*bytesWritten)); + return RESULT_OK; +} + +Result_t +ASDCP::SilenceDataProvider::ReadFrame() +{ + // no op + return RESULT_OK; +} + +Result_t +ASDCP::SilenceDataProvider::FillAudioDescriptor(PCM::AudioDescriptor& ADesc) const +{ + ADesc = m_ADesc; + return RESULT_OK; +} + +Result_t +ASDCP::SilenceDataProvider::Reset() +{ + //no op + return RESULT_OK; +} diff --git a/src/PCMDataProviders.h b/src/PCMDataProviders.h new file mode 100644 index 0000000..9241fd3 --- /dev/null +++ b/src/PCMDataProviders.h @@ -0,0 +1,119 @@ +/* +Copyright (c) 2013-2013, 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 PCMDataProviders.h + \version $Id$ + \brief PCM sample data providers for WAV, AtmosSync and Silence. +*/ + +#ifndef _PCMDATAPROVIDERS_H_ +#define _PCMDATAPROVIDERS_H_ + +#include <AS_DCP.h> +#include <AtmosSyncChannel_Generator.h> + +namespace ASDCP +{ + + // PCM Data Provider Interface + class PCMDataProviderInterface + { + ASDCP_NO_COPY_CONSTRUCT(PCMDataProviderInterface); + + public: + PCMDataProviderInterface() {}; + virtual ~PCMDataProviderInterface() = 0; + virtual Result_t PutSample(const ui32_t numChannels, byte_t* buf, ui32_t* bytesWritten) = 0; + virtual Result_t ReadFrame() = 0; + virtual Result_t FillAudioDescriptor(PCM::AudioDescriptor& ADesc) const = 0; + virtual Result_t Reset() = 0; + }; + + // WAV file implementation of the PCM Data Provider Interface + class WAVDataProvider : public PCMDataProviderInterface + { + ASDCP_NO_COPY_CONSTRUCT(WAVDataProvider); + PCM::WAVParser m_Parser; + PCM::FrameBuffer m_FB; + PCM::AudioDescriptor m_ADesc; + const byte_t* m_ptr; + ui32_t m_SampleSize; + + public: + WAVDataProvider(); + virtual ~WAVDataProvider(); + virtual Result_t PutSample(const ui32_t numChannels, byte_t* buf, ui32_t* bytesWritten); + virtual Result_t ReadFrame(); + virtual Result_t FillAudioDescriptor(PCM::AudioDescriptor& ADesc) const; + virtual Result_t Reset(); + Result_t OpenRead(const char* filename, const Rational& PictureRate); + + }; + + // Atmos Sync Channel implementation of the PCM Data Provider Interface + class AtmosSyncDataProvider : public PCMDataProviderInterface + { + ASDCP_NO_COPY_CONSTRUCT(AtmosSyncDataProvider); + PCM::AtmosSyncChannelGenerator m_Generator; + PCM::FrameBuffer m_FB; + PCM::AudioDescriptor m_ADesc; + const byte_t* m_ptr; + ui32_t m_SampleSize; + + public: + AtmosSyncDataProvider(const ui16_t bitsPerSample, const ui32_t sampleRate, + const ASDCP::Rational& PictureRate, const byte_t* uuid); + virtual ~AtmosSyncDataProvider(); + virtual Result_t PutSample(const ui32_t numChannels, byte_t* buf, ui32_t* bytesWritten); + virtual Result_t ReadFrame(); + virtual Result_t FillAudioDescriptor(PCM::AudioDescriptor& ADesc) const; + virtual Result_t Reset(); + }; + + // Silence Channel(s) implementation of the PCM Data Provider Interface + class SilenceDataProvider : public PCMDataProviderInterface + { + ASDCP_NO_COPY_CONSTRUCT(SilenceDataProvider); + PCM::AudioDescriptor m_ADesc; + ui32_t m_SampleSize; + + public: + SilenceDataProvider(const ui16_t numChannels, const ui16_t bitsPerSample, + const ui32_t sampleRate, const ASDCP::Rational& editRate); + virtual ~SilenceDataProvider(); + virtual Result_t PutSample(const ui32_t numChannels, byte_t* buf, ui32_t* bytesWritten); + virtual Result_t ReadFrame(); + virtual Result_t FillAudioDescriptor(PCM::AudioDescriptor& ADesc) const; + virtual Result_t Reset(); + }; + +} // namespace ASDCP + +#endif // _PCMDATAPROVIDERS_H_ + +// +// end PCMDataProviders.h +// diff --git a/src/PCM_Parser.cpp b/src/PCM_Parser.cpp index 3fa6d7d..e3b693d 100755 --- a/src/PCM_Parser.cpp +++ b/src/PCM_Parser.cpp @@ -37,6 +37,7 @@ using Kumu::DefaultLogSink; using namespace ASDCP; using namespace ASDCP::PCM; using namespace ASDCP::Wav; +using namespace ASDCP::RF64; //------------------------------------------------------------------------------------------ @@ -47,8 +48,8 @@ class ASDCP::PCM::WAVParser::h__WAVParser Kumu::FileReader m_FileReader; bool m_EOF; ui32_t m_DataStart; - ui32_t m_DataLength; - ui32_t m_ReadCount; + ui64_t m_DataLength; + ui64_t m_ReadCount; ui32_t m_FrameBufferSize; ui32_t m_FramesRead; Rational m_PictureRate; @@ -129,6 +130,22 @@ ASDCP::PCM::WAVParser::h__WAVParser::OpenRead(const char* filename, const Ration m_ADesc.ChannelFormat = PCM::CF_NONE; Reset(); } + else + { + SimpleRF64Header RF64Header; + m_FileReader.Seek(0); + result = RF64Header.ReadFromFile(m_FileReader, &m_DataStart); + + if ( ASDCP_SUCCESS(result) ) + { + RF64Header.FillADesc(m_ADesc, PictureRate); + m_FrameBufferSize = ASDCP::PCM::CalcFrameBufferSize(m_ADesc); + m_DataLength = RF64Header.data_len; + m_ADesc.ContainerDuration = m_DataLength / m_FrameBufferSize; + m_ADesc.ChannelFormat = PCM::CF_NONE; + Reset(); + } + } } } diff --git a/src/SyncCommon.h b/src/SyncCommon.h new file mode 100644 index 0000000..bc5d498 --- /dev/null +++ b/src/SyncCommon.h @@ -0,0 +1,80 @@ +/* +Copyright (c) 2013-2013, 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 SyncCommon.h + \version $Id$ + \brief Common elements for ATMOS Sync Channel generation +*/ + +#ifndef _SYNC_COMMON_H_ +#define _SYNC_COMMON_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef BYTE +typedef unsigned char BYTE; +#endif + +#ifndef USHORT +typedef unsigned short USHORT; +#endif + +#ifndef INT +typedef int INT; +#endif + +#ifndef FLOAT +typedef float FLOAT; +#endif + +#define SYMBOL_RATE (12000) + +#define SYMBOL_LENGTH_48 (4) +#define SYMBOL_LENGTH_96 (8) + +#define SYNC_HEADER (0x4D56) +#define SYNC_HEADER1 (0x4D) +#define SYNC_HEADER2 (0x56) + +#define SYNC_HEADER_BITS (16) +#define FRAME_RATE_BITS (4) +#define RESERVE_BITS (2) +#define UUID_SUB_INDEX_BITS (2) +#define UUID_SUB_BITS (32) +#define FRAME_INDEX_BITS (24) +#define CRC_BITS (16) +#define MESSAGE_TOTAL_BITS (96) +#define MESSAGE_TOTAL_BYTES (12) + +#define MAX_PACKET (32) + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif diff --git a/src/SyncEncoder.c b/src/SyncEncoder.c new file mode 100644 index 0000000..c553509 --- /dev/null +++ b/src/SyncEncoder.c @@ -0,0 +1,346 @@ +/* +Copyright (c) 2013-2013, 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 SyncEncoder.c + \version $Id$ + \brief Implementation of Atmos Sync Frame Encoder +*/ + +#include "SyncEncoder.h" +#include "CRC16.h" + +#include <memory.h> + +void ConstructFrame(LPSYNCENCODER pSyncEncoder, + INT iFrameIndex); + +FLOAT SEWriteBits( INT iSampleRate, /* In: Sample rate of signal */ + FLOAT *pfAudioBuffer, /* Out: Audio buffer containing signal */ + INT iBits, /* In: Number of bits to write */ + BYTE *pbyData, /* In: Data to write */ + FLOAT fSymbolPhase); /* In: Symbol phase */ + + + +INT SyncEncoderInit(LPSYNCENCODER pSyncEncoder, /* Out: SYNCENCODER structure to be initialized */ + INT iSampleRate, /* In: Signal sample rate */ + INT iFrameRate, /* In: frame rate */ + LPUUIDINFORMATION pUUID) /* In: UUID */ +{ + pSyncEncoder->iError = SYNC_ENCODER_ERROR_NONE; + + /* Check and set sample rate */ + pSyncEncoder->iSymbolLength = 1; + switch(iSampleRate){ + case 48000: + pSyncEncoder->iSampleRate = iSampleRate; + pSyncEncoder->iSymbolLength = SYMBOL_LENGTH_48; + break; + case 96000: + pSyncEncoder->iSampleRate = iSampleRate; + pSyncEncoder->iSymbolLength = SYMBOL_LENGTH_96; + break; + default: + pSyncEncoder->iError = SYNC_ENCODER_ERROR_INVALID_SR; + }; + + if(pSyncEncoder->iError != SYNC_ENCODER_ERROR_NONE){ + return pSyncEncoder->iError; + } + + /* check and set frame rate */ + switch(iFrameRate){ + case 24: + pSyncEncoder->iFrameRate = iFrameRate; + pSyncEncoder->iFrameRateCode = 0; + pSyncEncoder->iPacketsPerFrame = 4; + break; + case 25: + pSyncEncoder->iFrameRate = iFrameRate; + pSyncEncoder->iFrameRateCode = 1; + pSyncEncoder->iPacketsPerFrame = 4; + break; + case 30: + pSyncEncoder->iFrameRate = iFrameRate; + pSyncEncoder->iFrameRateCode = 2; + pSyncEncoder->iPacketsPerFrame = 4; + break; + case 48: + pSyncEncoder->iFrameRate = iFrameRate; + pSyncEncoder->iFrameRateCode = 3; + pSyncEncoder->iPacketsPerFrame = 2; + break; + case 50: + pSyncEncoder->iFrameRate = iFrameRate; + pSyncEncoder->iFrameRateCode = 4; + pSyncEncoder->iPacketsPerFrame = 2; + break; + case 60: + pSyncEncoder->iFrameRate = iFrameRate; + pSyncEncoder->iFrameRateCode = 5; + pSyncEncoder->iPacketsPerFrame = 2; + break; + case 96: + pSyncEncoder->iFrameRate = iFrameRate; + pSyncEncoder->iFrameRateCode = 6; + pSyncEncoder->iPacketsPerFrame = 1; + break; + case 100: + pSyncEncoder->iFrameRate = iFrameRate; + pSyncEncoder->iFrameRateCode = 7; + pSyncEncoder->iPacketsPerFrame = 1; + break; + case 120: + pSyncEncoder->iFrameRate = iFrameRate; + pSyncEncoder->iFrameRateCode = 8; + pSyncEncoder->iPacketsPerFrame = 1; + break; + default: + pSyncEncoder->iError = SYNC_ENCODER_ERROR_INVALID_FR; + }; + + if(pSyncEncoder->iError != SYNC_ENCODER_ERROR_NONE){ + return pSyncEncoder->iError; + } + + /* calculate required buffer length */ + pSyncEncoder->iAudioBufferLength = pSyncEncoder->iSampleRate / pSyncEncoder->iFrameRate; + + /* Calculate total packet bits including wash bits */ + pSyncEncoder->iPacketBits = pSyncEncoder->iAudioBufferLength / (pSyncEncoder->iSymbolLength * pSyncEncoder->iPacketsPerFrame); + + /* Initialize symbol phase */ + pSyncEncoder->fSymbolPhase = 1.0f; + + /* Initialize UUD information */ + pSyncEncoder->iUUIDSubIndex = 0; + memcpy(&pSyncEncoder->UUID,pUUID,sizeof(UUIDINFORMATION)); + + return pSyncEncoder->iError; +} + +INT GetSyncEncoderAudioBufferLength(LPSYNCENCODER pSyncEncoder) /* In: Sync encoder structure */ +{ + if(pSyncEncoder->iError != SYNC_ENCODER_ERROR_NONE){ + return pSyncEncoder->iError; + } + + return pSyncEncoder->iAudioBufferLength; +} + + + +INT EncodeSync( LPSYNCENCODER pSyncEncoder, /* In: Sync encoder structure */ + INT iBufferLength, /* In: Length of audio buffer */ + FLOAT *pfAudioBuffer, /* Out: Audio buffer with signal */ + INT iFrameIndex) /* In: Frame Index */ +{ + INT n; + INT iBufferIndex; + + + if(pSyncEncoder->iError != SYNC_ENCODER_ERROR_NONE){ + return pSyncEncoder->iError; + } + if(iBufferLength != pSyncEncoder->iAudioBufferLength){ + return SYNC_ENCODER_ERROR_INVALID_BL; + } + + iBufferIndex = 0; + for(n = 0; n < pSyncEncoder->iPacketsPerFrame; n ++){ + /* Construct message */ + ConstructFrame(pSyncEncoder,iFrameIndex); + + /* Write Message */ + pSyncEncoder->fSymbolPhase = SEWriteBits(pSyncEncoder->iSampleRate, + &pfAudioBuffer[iBufferIndex], + pSyncEncoder->iPacketBits, + pSyncEncoder->abyPacket, + pSyncEncoder->fSymbolPhase); + + iBufferIndex += (pSyncEncoder->iPacketBits * pSyncEncoder->iSymbolLength); + + } + + return pSyncEncoder->iError; +} + +void ConstructFrame(LPSYNCENCODER pSyncEncoder, + INT iFrameIndex) +{ + USHORT ushCRC; + BYTE byByte; + INT iUUIDIndex; + + /* Flush the packet buffer */ + memset(pSyncEncoder->abyPacket,0,MAX_PACKET); + + /* Sync Header */ + pSyncEncoder->abyPacket[0] = SYNC_HEADER1; + pSyncEncoder->abyPacket[1] = SYNC_HEADER2; + + /* Frame Rate code */ + byByte = 0; + byByte = (unsigned char)(pSyncEncoder->iFrameRateCode << 4); + + /* UUID sub index */ + byByte |= (unsigned char)(pSyncEncoder->iUUIDSubIndex & 0x3); + + pSyncEncoder->abyPacket[2] = byByte; + + /* UUID Sub */ + iUUIDIndex = pSyncEncoder->iUUIDSubIndex << 2; + pSyncEncoder->abyPacket[3] = pSyncEncoder->UUID.abyUUIDBytes[iUUIDIndex]; + pSyncEncoder->abyPacket[4] = pSyncEncoder->UUID.abyUUIDBytes[iUUIDIndex + 1]; + pSyncEncoder->abyPacket[5] = pSyncEncoder->UUID.abyUUIDBytes[iUUIDIndex + 2]; + pSyncEncoder->abyPacket[6] = pSyncEncoder->UUID.abyUUIDBytes[iUUIDIndex + 3]; + + /* Update UUID sub index */ + pSyncEncoder->iUUIDSubIndex ++; + pSyncEncoder->iUUIDSubIndex &= 0x3; + + /* Frame Index */ + byByte = (unsigned char)((iFrameIndex >> 16) & 0XFF); + pSyncEncoder->abyPacket[7] = byByte; + byByte = (unsigned char)((iFrameIndex >> 8) & 0XFF); + pSyncEncoder->abyPacket[8] = byByte; + byByte = (unsigned char)(iFrameIndex & 0XFF); + pSyncEncoder->abyPacket[9] = byByte; + + /* calculate CRC */ + ushCRC = CRC16(&pSyncEncoder->abyPacket[2],MESSAGE_TOTAL_BYTES - 4); + + /* Insert CRC */ + byByte = (unsigned char)((ushCRC >> 8) & 0XFF); + pSyncEncoder->abyPacket[10] = byByte; + byByte = (unsigned char)(ushCRC & 0XFF); + pSyncEncoder->abyPacket[11] = byByte; + +} + +static FLOAT g_afSymbol0_48[SYMBOL_LENGTH_48] = { + 0.3827f, + 0.9239f, + 0.9239f, + 0.3827f, +}; + +static FLOAT g_afSymbol1_48[SYMBOL_LENGTH_48] = { + 0.7071f, + 0.7071f, + -0.7071f, + -0.7071f, +}; + +static FLOAT g_afSymbol0_96[SYMBOL_LENGTH_96] = { + 0.1951f, + 0.5556f, + 0.8315f, + 0.9808f, + 0.9808f, + 0.8315f, + 0.5556f, + 0.1951f, +}; + +static FLOAT g_afSymbol1_96[SYMBOL_LENGTH_96] = { + 0.3827f, + 0.9239f, + 0.9239f, + 0.3827f, + -0.3827f, + -0.9239f, + -0.9239f, + -0.3827f, +}; + +/* Symbol gain */ +static FLOAT g_fGain = 0.1f; + +FLOAT SEWriteBits( INT iSampleRate, /* In: Sample rate of signal */ + FLOAT *pfAudioBuffer, /* Out: Audio buffer containing signal */ + INT iBits, /* In: Number of bits to write */ + BYTE *pbyData, /* In: Data to write */ + FLOAT fSymbolPhase) /* In: Symbol phase */ +{ + INT n; + INT i; + INT iSymbolLength; + FLOAT *pfSymbol0; + FLOAT *pfSymbol1; + BYTE byByte; + + /* Select the correct symbol length and symbol signal based on sample rate */ + switch (iSampleRate){ + case 96000: + iSymbolLength = SYMBOL_LENGTH_96; + pfSymbol0 = g_afSymbol0_96; + pfSymbol1 = g_afSymbol1_96; + break; + case 48000: + iSymbolLength = SYMBOL_LENGTH_48; + pfSymbol0 = g_afSymbol0_48; + pfSymbol1 = g_afSymbol1_48; + break; + default: + iSymbolLength = 0; + pfSymbol0 = g_afSymbol0_96; + pfSymbol1 = g_afSymbol1_96; + }; + + /* Write bits */ + n = 0; + i = 0; + while(n < iBits){ + INT k; + FLOAT *pfSymbol; + + /* Grab next byte of data */ + if(i == 0){ + byByte = *pbyData; + pbyData ++; + } + + pfSymbol = (byByte & 0x80) ? pfSymbol1 : pfSymbol0; + + for(k = 0; k < iSymbolLength; k ++){ + *pfAudioBuffer = *pfSymbol * fSymbolPhase * g_fGain; + pfAudioBuffer ++; + pfSymbol ++; + } + + fSymbolPhase *= (byByte & 0x80) ? 1.0f : -1.0f; + + byByte <<= 1; + + n ++; + + i ++; + i &= 0x7; + } + + return fSymbolPhase; +} diff --git a/src/SyncEncoder.h b/src/SyncEncoder.h new file mode 100644 index 0000000..4b97f5a --- /dev/null +++ b/src/SyncEncoder.h @@ -0,0 +1,86 @@ +/* +Copyright (c) 2013-2013, 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 SyncEncoder.h + \version $Id$ + \brief Declaration of Atmos Sync Frame Encoder +*/ + +#ifndef _SYNC_ENCODER_H_ +#define _SYNC_ENCODER_H_ + +#include "SyncCommon.h" +#include "UUIDInformation.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct SyncEncoder{ + INT iSampleRate; /* Signal sample rate */ + INT iSymbolLength; /* Symbol Length */ + INT iFrameRate; /* Frame rate */ + INT iFrameRateCode; /* Frame rate code */ + INT iAudioBufferLength; /* Length of audio buffer */ + INT iPacketBits; /* Bits in each packet includes wash bits */ + INT iPacketsPerFrame; /* Number of packets per frame */ + FLOAT fSymbolPhase; /* Symbol phase */ + + INT iUUIDSubIndex; /* UUID transmission sub index */ + UUIDINFORMATION UUID; /* UUID */ + + BYTE abyPacket[MAX_PACKET]; + + INT iError; /* Error state */ +}SYNCENCODER,*LPSYNCENCODER; + +enum{ + SYNC_ENCODER_ERROR_NONE = 0, /* No error */ + SYNC_ENCODER_ERROR_INVALID_SR = -1, /* Invalid sample rate */ + SYNC_ENCODER_ERROR_INVALID_FR = -2, /* Invalid frame rate */ + SYNC_ENCODER_ERROR_INVALID_BL = -10, /* Buffer length is incorrect */ + SYNC_ENCODER_ERROR_UNKNOWN = -100, /* Unknown */ +}; + + +INT SyncEncoderInit(LPSYNCENCODER pSyncEncoder, /* Out: SYNCENCODER structure to be initialized */ + INT iSampleRate, /* In: Signal sample rate */ + INT iFrameRate, /* In: frame rate */ + LPUUIDINFORMATION pUUID); /* In: UUID */ + +INT GetSyncEncoderAudioBufferLength(LPSYNCENCODER pSyncEncoder); + +INT EncodeSync( LPSYNCENCODER pSyncEncoder, /* In: Sync encoder structure */ + INT iBufferLength, /* In: Length of audio buffer */ + FLOAT *pfAudioBuffer, /* Out: Audio buffer with signal */ + INT iFrameIndex); /* In: Frame Index */ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif + diff --git a/src/UUIDInformation.c b/src/UUIDInformation.c new file mode 100644 index 0000000..4c5eec9 --- /dev/null +++ b/src/UUIDInformation.c @@ -0,0 +1,119 @@ +/* +Copyright (c) 2013-2013, 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 SyncEncoder.h + \version $Id$ + \brief Implementation of Atmos Sync UUID +*/ + +#include "UUIDInformation.h" +#include <stdlib.h> + + +void UUIDSynthesize(LPUUIDINFORMATION pUUID) +{ + INT n; + + for(n = 0; n < 16; n ++){ + pUUID->abyUUIDBytes[n] = (BYTE)(rand() & 0xFF); + } + + pUUID->abyUUIDBytes[6] &= 0x0F; + pUUID->abyUUIDBytes[6] |= 0x40; + + pUUID->abyUUIDBytes[8] &= 0x0F; + pUUID->abyUUIDBytes[8] |= 0xA0; +} + +void UUIDPrint( FILE *pFilePtr, + LPUUIDINFORMATION pUUID) +{ + if(pFilePtr != NULL){ + INT n; + + for(n = 0; n < 16; n ++){ + fprintf(pFilePtr,"%02x",pUUID->abyUUIDBytes[n]); + } + } + else{ + INT n; + + for(n = 0; n < 16; n ++){ + fprintf(stdout,"%02x",pUUID->abyUUIDBytes[n]); + } + } +} + +void UUIDPrintFormated( FILE *pFilePtr, + LPUUIDINFORMATION pUUID) +{ + if(pFilePtr != NULL){ + INT n; + + for(n = 0; n < 4; n ++){ + fprintf(pFilePtr,"%02x",pUUID->abyUUIDBytes[n]); + } + fprintf(pFilePtr,"-"); + for(n = 4; n < 6; n ++){ + fprintf(pFilePtr,"%02x",pUUID->abyUUIDBytes[n]); + } + fprintf(pFilePtr,"-"); + for(n = 6; n < 8; n ++){ + fprintf(pFilePtr,"%02x",pUUID->abyUUIDBytes[n]); + } + fprintf(pFilePtr,"-"); + for(n = 8; n < 10; n ++){ + fprintf(pFilePtr,"%02x",pUUID->abyUUIDBytes[n]); + } + fprintf(pFilePtr,"-"); + for(n = 10; n < 16; n ++){ + fprintf(pFilePtr,"%02x",pUUID->abyUUIDBytes[n]); + } + } + else{ + INT n; + + for(n = 0; n < 4; n ++){ + fprintf(stdout,"%02x",pUUID->abyUUIDBytes[n]); + } + fprintf(stdout,"-"); + for(n = 4; n < 6; n ++){ + fprintf(stdout,"%02x",pUUID->abyUUIDBytes[n]); + } + fprintf(stdout,"-"); + for(n = 6; n < 8; n ++){ + fprintf(stdout,"%02x",pUUID->abyUUIDBytes[n]); + } + fprintf(stdout,"-"); + for(n = 8; n < 10; n ++){ + fprintf(stdout,"%02x",pUUID->abyUUIDBytes[n]); + } + fprintf(stdout,"-"); + for(n = 10; n < 16; n ++){ + fprintf(stdout,"%02x",pUUID->abyUUIDBytes[n]); + } + } +} diff --git a/src/UUIDInformation.h b/src/UUIDInformation.h new file mode 100644 index 0000000..4bd0ff4 --- /dev/null +++ b/src/UUIDInformation.h @@ -0,0 +1,58 @@ +/* +Copyright (c) 2013-2013, 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 SyncEncoder.h + \version $Id$ + \brief Declaration of Atmos Sync UUID +*/ + +#ifndef _UUID_INFORMATION_H_ +#define _UUID_INFORMATION_H_ + +#include "SyncCommon.h" +#include <stdio.h> + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct UUIInformation{ + BYTE abyUUIDBytes[16]; +} UUIDINFORMATION,*LPUUIDINFORMATION; + +void UUIDSynthesize(LPUUIDINFORMATION pUUID); + +void UUIDPrint( FILE *pFilePtr, + LPUUIDINFORMATION pUUID); + +void UUIDPrintFormated( FILE *pFilePtr, + LPUUIDINFORMATION pUUID); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif diff --git a/src/Wav.cpp b/src/Wav.cpp index 7275ab9..0f14850 100755 --- a/src/Wav.cpp +++ b/src/Wav.cpp @@ -44,7 +44,7 @@ ASDCP::Wav::SimpleWaveHeader::SimpleWaveHeader(ASDCP::PCM::AudioDescriptor& ADes nchannels = ADesc.ChannelCount; bitspersample = ADesc.QuantizationBits; samplespersec = (ui32_t)ceil(ADesc.AudioSamplingRate.Quotient()); - blockalign = nchannels * (bitspersample / 8); + blockalign = nchannels * ((bitspersample + 7) / 8); avgbps = samplespersec * blockalign; cbsize = 0; data_len = ASDCP::PCM::CalcFrameBufferSize(ADesc) * ADesc.ContainerDuration; @@ -363,7 +363,230 @@ ASDCP::AIFF::SimpleAIFFHeader::ReadFromBuffer(const byte_t* buf, ui32_t buf_len, return RESULT_OK; } +ASDCP::RF64::SimpleRF64Header::SimpleRF64Header(ASDCP::PCM::AudioDescriptor& ADesc) +{ + format = 1; + nchannels = ADesc.ChannelCount; + bitspersample = ADesc.QuantizationBits; + samplespersec = (ui32_t)ceil(ADesc.AudioSamplingRate.Quotient()); + blockalign = nchannels * ((bitspersample + 7) / 8); + avgbps = samplespersec * blockalign; + data_len = ASDCP::PCM::CalcFrameBufferSize(ADesc) * ADesc.ContainerDuration; +} + +// +void +ASDCP::RF64::SimpleRF64Header::FillADesc(ASDCP::PCM::AudioDescriptor& ADesc, ASDCP::Rational PictureRate) const +{ + ADesc.EditRate = 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; + ADesc.ChannelFormat = PCM::CF_NONE; +} + +// +ASDCP::Result_t +ASDCP::RF64::SimpleRF64Header::WriteToFile(Kumu::FileWriter& OutFile) const +{ + static ui32_t fmt_len = + sizeof(format) + + sizeof(nchannels) + + sizeof(samplespersec) + + sizeof(avgbps) + + sizeof(blockalign) + + sizeof(bitspersample); + + ui32_t write_count = 0; + ui64_t RIFF_len = data_len + SimpleWavHeaderLength - 8; + DefaultLogSink().Debug("RIFF_len is %llu.\n", RIFF_len); + byte_t* tmp_header = NULL; + ui32_t header_len = 0; + + if (RIFF_len > MAX_RIFF_LEN) + { + DefaultLogSink().Debug("Will write out an RF64 wave file.\n"); + ui32_t data32_len = ((data_len < MAX_RIFF_LEN) ? data_len : MAX_RIFF_LEN); + ui64_t data64_len = ((data_len < MAX_RIFF_LEN) ? 0 : data_len); + static ui32_t ds64_len = + sizeof(RIFF_len) + + sizeof(data64_len) + + sizeof(SAMPLE_COUNT) + + sizeof(TABLE_LEN); + + header_len = SIMPLE_RF64_HEADER_LEN; + tmp_header = new byte_t[header_len]; + byte_t* p = tmp_header; + memcpy(p, &FCC_RF64, sizeof(fourcc)); p += 4; + *((ui32_t*)p) = KM_i32_LE(MAX_RIFF_LEN); p += 4; + memcpy(p, &Wav::FCC_WAVE, sizeof(fourcc)); p += 4; + memcpy(p, &FCC_ds64, sizeof(fourcc)); p += 4; + *((ui32_t*)p) = KM_i32_LE(ds64_len); p += 4; + *((ui64_t*)p) = KM_i64_LE(RIFF_len); p += 8; + *((ui64_t*)p) = KM_i64_LE(data64_len); p += 8; + *((ui64_t*)p) = KM_i64_LE(SAMPLE_COUNT); p += 8; + *((ui32_t*)p) = KM_i32_LE(TABLE_LEN); p += 4; + memcpy(p, &Wav::FCC_fmt_, sizeof(fourcc)); p += 4; + *((ui32_t*)p) = KM_i32_LE(fmt_len); p += 4; + *((ui16_t*)p) = KM_i16_LE(format); p += 2; + *((ui16_t*)p) = KM_i16_LE(nchannels); p += 2; + *((ui32_t*)p) = KM_i32_LE(samplespersec); p += 4; + *((ui32_t*)p) = KM_i32_LE(avgbps); p += 4; + *((ui16_t*)p) = KM_i16_LE(blockalign); p += 2; + *((ui16_t*)p) = KM_i16_LE(bitspersample); p += 2; + memcpy(p, &Wav::FCC_data, sizeof(fourcc)); p += 4; + *((ui32_t*)p) = KM_i32_LE(data32_len); p += 4; + write_count = (p - tmp_header); + } + else + { + DefaultLogSink().Debug("Will write out a regular wave file.\n"); + header_len = SimpleWavHeaderLength; + tmp_header = new byte_t[header_len]; + byte_t* p = tmp_header; + memcpy(p, &Wav::FCC_RIFF, sizeof(fourcc)); p += 4; + *((ui32_t*)p) = KM_i32_LE(RIFF_len); p += 4; + memcpy(p, &Wav::FCC_WAVE, sizeof(fourcc)); p += 4; + memcpy(p, &Wav::FCC_fmt_, sizeof(fourcc)); p += 4; + *((ui32_t*)p) = KM_i32_LE(fmt_len); p += 4; + *((ui16_t*)p) = KM_i16_LE(format); p += 2; + *((ui16_t*)p) = KM_i16_LE(nchannels); p += 2; + *((ui32_t*)p) = KM_i32_LE(samplespersec); p += 4; + *((ui32_t*)p) = KM_i32_LE(avgbps); p += 4; + *((ui16_t*)p) = KM_i16_LE(blockalign); p += 2; + *((ui16_t*)p) = KM_i16_LE(bitspersample); p += 2; + memcpy(p, &Wav::FCC_data, sizeof(fourcc)); p += 4; + *((ui32_t*)p) = KM_i32_LE(data_len); p += 4; + write_count = (p - tmp_header); + } + if (header_len != write_count) + { + DefaultLogSink().Warn("Expected to write %u bytes but wrote %u bytes for header.\n", + header_len, write_count); + } + write_count = 0; + ASDCP::Result_t r = OutFile.Write(tmp_header, header_len, &write_count); + delete [] tmp_header; + return r; +} + +// +ASDCP::Result_t +ASDCP::RF64::SimpleRF64Header::ReadFromFile(const Kumu::FileReader& InFile, ui32_t* data_start) +{ + ui32_t read_count = 0; + ui32_t local_data_start = 0; + ASDCP::PCM::FrameBuffer TmpBuffer(Wav::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); + else + DefaultLogSink().Error("Failed to read %d bytes from file\n", Wav::MaxWavHeader); + + return result; +} + +ASDCP::Result_t +ASDCP::RF64::SimpleRF64Header::ReadFromBuffer(const byte_t* buf, ui32_t buf_len, ui32_t* data_start) +{ + if ( buf_len < SIMPLE_RF64_HEADER_LEN ) + return RESULT_SMALLBUF; + + *data_start = 0; + const byte_t* p = buf; + const byte_t* end_p = p + buf_len; + + fourcc test_RF64(p); p += 4; + if ( test_RF64 != FCC_RF64 ) + { + DefaultLogSink().Debug("File does not begin with RF64 header\n"); + return RESULT_RAW_FORMAT; + } + + ui32_t tmp_len = KM_i32_LE(*(ui32_t*)p); p += 4; + + fourcc test_WAVE(p); p += 4; + if ( test_WAVE != Wav::FCC_WAVE ) + { + DefaultLogSink().Debug("File does not contain a WAVE header\n"); + return RESULT_RAW_FORMAT; + } + + fourcc test_ds64(p); p += 4; + if ( test_ds64 != FCC_ds64 ) + { + DefaultLogSink().Debug("File does not contain a ds64 chunk\n"); + return RESULT_RAW_FORMAT; + } + ui32_t ds64_len = KM_i32_LE(*(ui32_t*)p); p += 4; + ui64_t RIFF_len = ((tmp_len == MAX_RIFF_LEN) ? KM_i64_LE(*(ui64_t*)p) : tmp_len); p += 8; + data_len = KM_i64_LE(*(ui64_t*)p); p += 8; + p += (ds64_len - 16); // skip rest of ds64 chunk + + fourcc test_fcc; + + while ( p < end_p ) + { + test_fcc = fourcc(p); p += 4; + ui32_t chunk_size = KM_i32_LE(*(ui32_t*)p); p += 4; + + if ( test_fcc == Wav::FCC_data ) + { + if ( chunk_size > RIFF_len ) + { + DefaultLogSink().Error("Chunk size %u larger than file: %u\n", chunk_size, RIFF_len); + return RESULT_RAW_FORMAT; + } + + if (chunk_size != MAX_RIFF_LEN) + data_len = chunk_size; + *data_start = p - buf; + break; + } + + if ( test_fcc == Wav::FCC_fmt_ ) + { + ui16_t format = KM_i16_LE(*(ui16_t*)p); p += 2; + + if ( format != Wav::WAVE_FORMAT_PCM && format != Wav::WAVE_FORMAT_EXTENSIBLE ) + { + DefaultLogSink().Error("Expecting uncompressed PCM data, got format type %hd\n", format); + return RESULT_RAW_FORMAT; + } + + nchannels = KM_i16_LE(*(ui16_t*)p); p += 2; + samplespersec = KM_i32_LE(*(ui32_t*)p); p += 4; + avgbps = KM_i32_LE(*(ui32_t*)p); p += 4; + blockalign = KM_i16_LE(*(ui16_t*)p); p += 2; + bitspersample = KM_i16_LE(*(ui16_t*)p); p += 2; + p += chunk_size - 16; // 16 is the number of bytes read in this block + } + 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 @@ -118,6 +118,45 @@ namespace ASDCP }; } // namespace Wav + + namespace RF64 + { + const fourcc FCC_RF64("RF64"); + const fourcc FCC_ds64("ds64"); + + + static const ui32_t MAX_RIFF_LEN = 0xFFFFFFFF; + static const ui32_t DS64_HEADER_LEN = 28; + static const ui32_t SIMPLE_RF64_HEADER_LEN = 80; + // + class SimpleRF64Header + { + public: + ui16_t format; + ui16_t nchannels; + ui32_t samplespersec; + ui32_t avgbps; + ui16_t blockalign; + ui16_t bitspersample; + ui64_t data_len; + + SimpleRF64Header() : + format(0), nchannels(0), samplespersec(0), avgbps(0), + blockalign(0), bitspersample(0), data_len(0) {} + + SimpleRF64Header(ASDCP::PCM::AudioDescriptor& ADesc); + + Result_t ReadFromBuffer(const byte_t* buf, ui32_t buf_len, ui32_t* data_start); + Result_t ReadFromFile(const Kumu::FileReader& InFile, ui32_t* data_start); + Result_t WriteToFile(Kumu::FileWriter& OutFile) const; + void FillADesc(ASDCP::PCM::AudioDescriptor& ADesc, Rational PictureRate) const; + + private: + static const ui64_t SAMPLE_COUNT = 0; + static const ui32_t TABLE_LEN = 0; + }; + + } // namespace RF64 } // namespace ASDCP #endif // _WAV_H_ diff --git a/src/WavFileWriter.h b/src/WavFileWriter.h index 3febc23..9611adc 100755 --- a/src/WavFileWriter.h +++ b/src/WavFileWriter.h @@ -136,22 +136,36 @@ class WavFileWriter assert(file_count && m_ChannelCount); ui32_t element_size = ASDCP::PCM::CalcFrameBufferSize(m_ADesc) / file_count; - - for ( ui32_t i = 0; i < file_count && ASDCP_SUCCESS(result); i++ ) - { - snprintf(filename, Kumu::MaxFilePath, "%s_%u.wav", file_root, (i + 1)); - m_OutFile.push_back(new WavFileElement(element_size)); - result = m_OutFile.back()->OpenWrite(filename); - - if ( ASDCP_SUCCESS(result) ) - { - ASDCP::PCM::AudioDescriptor tmpDesc = m_ADesc; - tmpDesc.ChannelCount = m_ChannelCount; - ASDCP::Wav::SimpleWaveHeader Wav(tmpDesc); - result = Wav.WriteToFile(*(m_OutFile.back())); - } - } - + if (split == ST_NONE) + { + snprintf(filename, Kumu::MaxFilePath, "%s", file_root); + m_OutFile.push_back(new WavFileElement(element_size)); + result = m_OutFile.back()->OpenWrite(filename); + if ( ASDCP_SUCCESS(result) ) + { + ASDCP::PCM::AudioDescriptor tmpDesc = m_ADesc; + tmpDesc.ChannelCount = m_ChannelCount; + ASDCP::RF64::SimpleRF64Header Wav(tmpDesc); + result = Wav.WriteToFile(*(m_OutFile.back())); + } + } + else + { + for ( ui32_t i = 0; i < file_count && ASDCP_SUCCESS(result); i++ ) + { + snprintf(filename, Kumu::MaxFilePath, "%s_%u.wav", file_root, (i + 1)); + m_OutFile.push_back(new WavFileElement(element_size)); + result = m_OutFile.back()->OpenWrite(filename); + + if ( ASDCP_SUCCESS(result) ) + { + ASDCP::PCM::AudioDescriptor tmpDesc = m_ADesc; + tmpDesc.ChannelCount = m_ChannelCount; + ASDCP::RF64::SimpleRF64Header Wav(tmpDesc); + result = Wav.WriteToFile(*(m_OutFile.back())); + } + } + } return result; } diff --git a/src/asdcp-info.cpp b/src/asdcp-info.cpp index 05e2c79..cbc4381 100755 --- a/src/asdcp-info.cpp +++ b/src/asdcp-info.cpp @@ -25,7 +25,7 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /*! \file asdcp-info.cpp - \version $Id$ + \version $Id$ \brief AS-DCP file metadata utility This program provides metadata information about an AS-DCP file. @@ -125,7 +125,7 @@ public: bool stereo_image_flag; // if true, expect stereoscopic JP2K input (left eye first) bool showid_flag; // if true, show file identity info (the WriterInfo struct) bool showdescriptor_flag; // if true, show the essence descriptor - bool showcoding_flag; // if true, show the coding UL + bool showcoding_flag; // if true, show the coding UL bool showrate_flag; // if true and is image file, show bit rate bool max_bitrate_flag; // true if -t option given double max_bitrate; // if true and is image file, max bit rate for rate test @@ -145,7 +145,7 @@ public: help_flag = true; continue; } - + if ( argv[i][0] == '-' && ( isalpha(argv[i][1]) || isdigit(argv[i][1]) ) && argv[i][2] == 0 ) @@ -191,7 +191,7 @@ public: if ( help_flag || version_flag ) return; - + if ( filenames.empty() ) { fputs("At least one filename argument is required.\n", stderr); @@ -270,6 +270,30 @@ class MyTextDescriptor : public TimedText::TimedTextDescriptor } }; +class MyDCDataDescriptor : public DCData::DCDataDescriptor +{ + public: + void FillDescriptor(DCData::MXFReader& Reader) { + Reader.FillDCDataDescriptor(*this); + } + + void Dump(FILE* stream) { + DCData::DCDataDescriptorDump(*this, stream); + } +}; + +class MyAtmosDescriptor : public ATMOS::AtmosDescriptor +{ + public: + void FillDescriptor(ATMOS::MXFReader& Reader) { + Reader.FillAtmosDescriptor(*this); + } + + void Dump(FILE* stream) { + ATMOS::AtmosDescriptorDump(*this, stream); + } +}; + // // template<class ReaderT, class DescriptorT> @@ -333,7 +357,7 @@ public: Result_t result = m_Reader.OPAtomHeader().GetMDObjectByType(DefaultCompositeDict().ul(MDD_RGBAEssenceDescriptor), reinterpret_cast<MXF::InterchangeObject**>(&descriptor)); - + if ( KM_SUCCESS(result) ) m_PictureEssenceCoding = descriptor->PictureEssenceCoding; } @@ -489,7 +513,7 @@ public: Result_t result = m_Reader.OPAtomHeader().GetMDObjectByType(DefaultCompositeDict().ul(MDD_WaveAudioDescriptor), reinterpret_cast<MXF::InterchangeObject**>(&descriptor)); - + if ( KM_SUCCESS(result) ) { char buf[64]; @@ -572,7 +596,7 @@ show_file_info(CommandOptions& Options) { FileInfoWrapper<ASDCP::JP2K::MXFSReader, MyStereoPictureDescriptor>wrapper; result = wrapper.file_info(Options, "JPEG 2000 stereoscopic pictures"); - + if ( KM_SUCCESS(result) ) { wrapper.get_PictureEssenceCoding(); @@ -592,6 +616,16 @@ show_file_info(CommandOptions& Options) FileInfoWrapper<ASDCP::TimedText::MXFReader, MyTextDescriptor>wrapper; result = wrapper.file_info(Options, "Timed Text"); } + else if ( EssenceType == ESS_DCDATA_UNKNOWN ) + { + FileInfoWrapper<ASDCP::DCData::MXFReader, MyDCDataDescriptor> wrapper; + result = wrapper.file_info(Options, "D-Cinema Generic Data"); + } + else if ( EssenceType == ESS_DCDATA_DOLBY_ATMOS ) + { + FileInfoWrapper<ASDCP::ATMOS::MXFReader, MyAtmosDescriptor> wrapper; + result = wrapper.file_info(Options, "Dolby ATMOS"); + } else { fprintf(stderr, "File is not AS-DCP: %s\n", Options.filenames.front().c_str()); diff --git a/src/asdcp-unwrap.cpp b/src/asdcp-unwrap.cpp index e9b9fee..7b8654b 100755 --- a/src/asdcp-unwrap.cpp +++ b/src/asdcp-unwrap.cpp @@ -25,7 +25,7 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /*! \file asdcp-unwrap.cpp - \version $Id$ + \version $Id$ \brief AS-DCP file manipulation utility This program extracts picture, sound and text essence from AS-DCP files. @@ -92,7 +92,8 @@ Options:\n\ -b <buffer-size> - Specify size in bytes of picture frame buffer\n\ Defaults to 4,194,304 (4MB)\n\ -d <duration> - Number of frames to process, default all\n\ - -f <start-frame> - Starting frame number, default 0\n\ + -e <extension> - Extension to use for Unknown D-Cinema Data files. default dcdata\n\ + -f <start-frame> - Starting frame number, default 0\n \ -G - Perform GOP start lookup test on MXF+Interop MPEG file\n\ -h | -help - Show help\n\ -k <key-string> - Use key for ciphertext operations\n\ @@ -153,6 +154,7 @@ public: PCM::ChannelFormat_t channel_fmt; // audio channel arrangement const char* input_filename; std::string prefix_buffer; + const char* extension; // file extension to use for unknown D-Cinema Data track files. // Rational PictureRate() @@ -181,7 +183,7 @@ public: version_flag(false), help_flag(false), stereo_image_flag(false), number_width(6), start_frame(0), duration(0xffffffff), duration_flag(false), j2c_pedantic(true), picture_rate(24), fb_size(FRAME_BUFFER_SIZE), file_prefix(0), - channel_fmt(PCM::CF_NONE), input_filename(0) + channel_fmt(PCM::CF_NONE), input_filename(0), extension("dcdata") { memset(key_value, 0, KeyLen); memset(key_id_value, 0, UUIDlen); @@ -194,7 +196,7 @@ public: help_flag = true; continue; } - + if ( argv[i][0] == '-' && ( isalpha(argv[i][1]) || isdigit(argv[i][1]) ) && argv[i][2] == 0 ) @@ -216,6 +218,11 @@ public: duration = abs(atoi(argv[i])); break; + case 'e': + TEST_EXTRA_ARG(i, 'e'); + extension = argv[i]; + break; + case 'f': TEST_EXTRA_ARG(i, 'f'); start_frame = abs(atoi(argv[i])); @@ -224,6 +231,20 @@ public: case 'G': mode = MMT_GOP_START; break; case 'h': help_flag = true; break; + case 'k': key_flag = true; + TEST_EXTRA_ARG(i, 'k'); + { + ui32_t length; + Kumu::hex2bin(argv[i], key_value, KeyLen, &length); + + if ( length != KeyLen ) + { + fprintf(stderr, "Unexpected key length: %u, expecting %u characters.\n", length, KeyLen); + return; + } + } + break; + case 'm': read_hmac = true; break; case 'p': @@ -276,7 +297,7 @@ public: if ( help_flag || version_flag ) return; - + if ( ( mode == MMT_EXTRACT || mode == MMT_GOP_START ) && input_filename == 0 ) { fputs("Option requires at least one filename argument.\n", stderr); @@ -369,11 +390,11 @@ read_MPEG2_file(CommandOptions& Options) if ( ! Options.no_write_flag ) { - ui32_t write_count = 0; - result = OutFile.Write(FrameBuffer.Data(), FrameBuffer.Size(), &write_count); - } + ui32_t write_count = 0; + result = OutFile.Write(FrameBuffer.Data(), FrameBuffer.Size(), &write_count); } } + } return result; } @@ -499,13 +520,13 @@ read_JP2K_S_file(CommandOptions& Options) { if ( ! Options.no_write_flag ) { - Kumu::FileWriter OutFile; - ui32_t write_count; - snprintf(filename, filename_max, left_format, Options.file_prefix, i); - result = OutFile.OpenWrite(filename); + Kumu::FileWriter OutFile; + ui32_t write_count; + snprintf(filename, filename_max, left_format, Options.file_prefix, i); + result = OutFile.OpenWrite(filename); - if ( ASDCP_SUCCESS(result) ) - result = OutFile.Write(FrameBuffer.Data(), FrameBuffer.Size(), &write_count); + if ( ASDCP_SUCCESS(result) ) + result = OutFile.Write(FrameBuffer.Data(), FrameBuffer.Size(), &write_count); } if ( Options.verbose_flag ) @@ -519,18 +540,18 @@ read_JP2K_S_file(CommandOptions& Options) { if ( ! Options.no_write_flag ) { - Kumu::FileWriter OutFile; - ui32_t write_count; - snprintf(filename, filename_max, right_format, Options.file_prefix, i); - result = OutFile.OpenWrite(filename); + Kumu::FileWriter OutFile; + ui32_t write_count; + snprintf(filename, filename_max, right_format, Options.file_prefix, i); + result = OutFile.OpenWrite(filename); - if ( ASDCP_SUCCESS(result) ) - result = OutFile.Write(FrameBuffer.Data(), FrameBuffer.Size(), &write_count); - } + 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; @@ -602,14 +623,14 @@ read_JP2K_file(CommandOptions& Options) { if ( ! Options.no_write_flag ) { - Kumu::FileWriter OutFile; - char filename[256]; - ui32_t write_count; - snprintf(filename, 256, name_format, Options.file_prefix, i); - result = OutFile.OpenWrite(filename); - - if ( ASDCP_SUCCESS(result) ) - result = OutFile.Write(FrameBuffer.Data(), FrameBuffer.Size(), &write_count); + Kumu::FileWriter OutFile; + char filename[256]; + ui32_t write_count; + snprintf(filename, 256, name_format, Options.file_prefix, i); + result = OutFile.OpenWrite(filename); + + if ( ASDCP_SUCCESS(result) ) + result = OutFile.Write(FrameBuffer.Data(), FrameBuffer.Size(), &write_count); } if ( Options.verbose_flag ) @@ -659,14 +680,14 @@ read_PCM_file(CommandOptions& Options) } else { - FrameBuffer.Capacity(PCM::CalcFrameBufferSize(ADesc)); + FrameBuffer.Capacity(PCM::CalcFrameBufferSize(ADesc)); } if ( Options.verbose_flag ) { fprintf(stderr, "Frame Buffer size: %u\n", Options.fb_size); - PCM::AudioDescriptorDump(ADesc); - } + PCM::AudioDescriptorDump(ADesc); + } } if ( ASDCP_SUCCESS(result) ) @@ -691,10 +712,10 @@ read_PCM_file(CommandOptions& Options) if ( ! Options.no_write_flag ) { - OutWave.OpenWrite(ADesc, Options.file_prefix, - ( Options.split_wav ? WavFileWriter::ST_STEREO : - ( Options.mono_wav ? WavFileWriter::ST_MONO : WavFileWriter::ST_NONE ) )); - } + OutWave.OpenWrite(ADesc, Options.file_prefix, + ( Options.split_wav ? WavFileWriter::ST_STEREO : + ( Options.mono_wav ? WavFileWriter::ST_MONO : WavFileWriter::ST_NONE ) )); + } } if ( ASDCP_SUCCESS(result) && Options.key_flag ) @@ -730,10 +751,10 @@ read_PCM_file(CommandOptions& Options) if ( ! Options.no_write_flag ) { - result = OutWave.WriteFrame(FrameBuffer); - } + result = OutWave.WriteFrame(FrameBuffer); } } + } return result; } @@ -820,6 +841,90 @@ read_timed_text_file(CommandOptions& Options) if ( ASDCP_SUCCESS(result) ) result = Writer.Write(FrameBuffer.RoData(), FrameBuffer.Size(), &write_count); + if ( Options.verbose_flag ) + FrameBuffer.Dump(stderr, Options.fb_dump_size); + } + } + + return result; +} + +// Read one or more plaintext DCData bytestreams from a plaintext ASDCP file +// Read one or more plaintext DCData bytestreams from a ciphertext ASDCP file +// Read one or more ciphertext DCData byestreams from a ciphertext ASDCP file +// +Result_t +read_DCData_file(CommandOptions& Options) +{ + AESDecContext* Context = 0; + HMACContext* HMAC = 0; + DCData::MXFReader Reader; + DCData::FrameBuffer FrameBuffer(Options.fb_size); + ui32_t frame_count = 0; + + Result_t result = Reader.OpenRead(Options.input_filename); + + if ( ASDCP_SUCCESS(result) ) + { + DCData::DCDataDescriptor DDesc; + Reader.FillDCDataDescriptor(DDesc); + + frame_count = DDesc.ContainerDuration; + + if ( Options.verbose_flag ) + { + fprintf(stderr, "Frame Buffer size: %u\n", Options.fb_size); + DCData::DCDataDescriptorDump(DDesc); + } + } + + 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, Info.LabelSetType); + } + 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; + + char name_format[64]; + snprintf(name_format, 64, "%%s%%0%du.%s", Options.number_width, Options.extension); + + 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.no_write_flag ) + { + Kumu::FileWriter OutFile; + char filename[256]; + ui32_t write_count; + snprintf(filename, 256, name_format, Options.file_prefix, 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); } @@ -888,6 +993,15 @@ main(int argc, const char** argv) result = read_timed_text_file(Options); break; + case ESS_DCDATA_UNKNOWN: + result = read_DCData_file(Options); + break; + + case ESS_DCDATA_DOLBY_ATMOS: + Options.extension = "atmos"; + result = read_DCData_file(Options); + break; + default: fprintf(stderr, "%s: Unknown file type, not ASDCP essence.\n", Options.input_filename); return 5; diff --git a/src/asdcp-wrap.cpp b/src/asdcp-wrap.cpp index bbff82a..12435ce 100755 --- a/src/asdcp-wrap.cpp +++ b/src/asdcp-wrap.cpp @@ -25,7 +25,7 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /*! \file asdcp-wrap.cpp - \version $Id$ + \version $Id$ \brief AS-DCP file manipulation utility This program wraps d-cinema essence (picture, sound or text) into an AS-DCP @@ -48,6 +48,7 @@ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include <KM_fileio.h> #include <KM_prng.h> +#include <AtmosSyncChannel_Mixer.h> #include <AS_DCP.h> #include <PCMParserList.h> #include <Metadata.h> @@ -76,7 +77,7 @@ public: 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-wrap"; @@ -117,7 +118,7 @@ USAGE: %s [-h|-help] [-V]\n\ \n\ %s [-3] [-a <uuid>] [-b <buffer-size>] [-C <UL>] [-d <duration>]\n\ [-e|-E] [-f <start-frame>] [-j <key-id-string>] [-k <key-string>]\n\ - [-l <label>] [-L] [-M] [-p <frame-rate>] [-s <num>] [-v] [-W]\n\ + [-l <label>] [-L] [-M] [-p <frame-rate>] [-s] [-v] [-W]\n\ [-z|-Z] <input-file>+ <output-file>\n\n", PROGRAM_NAME, PROGRAM_NAME); @@ -147,6 +148,10 @@ Options:\n\ -P <UL> - Set PictureEssenceCoding UL value in a JP2K file\n\ -p <rate> - fps of picture when wrapping PCM or JP2K:\n\ Use one of [23|24|25|30|48|50|60], 24 is default\n\ + -s - Insert a Dolby Atmos synchronization channel when\n\ + wrapping PCM. This implies a -L option(SMPTE ULs) and \n\ + will overide -C and -l options with Configuration 4 \n\ + Channel Assigment and no format label respectively. \n\ -v - Verbose, prints informative messages to stderr\n\ -W - Read input file only, do not write source file\n\ -z - Fail if j2c inputs have unequal parameters (default)\n\ @@ -168,7 +173,7 @@ decode_channel_fmt(const std::string& label_name) else if ( label_name == "6.1" ) return PCM::CF_CFG_2; - + else if ( label_name == "7.1" ) return PCM::CF_CFG_3; @@ -217,6 +222,11 @@ public: Kumu::PathList_t filenames; // list of filenames to be processed UL channel_assignment; UL picture_coding; + bool dolby_atmos_sync_flag; // if true, insert a Dolby Atmos Synchronization channel. + ui32_t ffoa; /// first frame of action for atmos wrapping + ui32_t max_channel_count; /// max channel count for atmos wrapping + ui32_t max_object_count; /// max object count for atmos wrapping + // Rational PictureRate() @@ -268,6 +278,8 @@ public: duration(0xffffffff), use_smpte_labels(false), j2c_pedantic(true), fb_size(FRAME_BUFFER_SIZE), channel_fmt(PCM::CF_NONE), + ffoa(0), max_channel_count(10), max_object_count(118), // hard-coded sample atmos properties + dolby_atmos_sync_flag(false), show_ul_values(false) { memset(key_value, 0, KeyLen); @@ -281,7 +293,7 @@ public: help_flag = true; continue; } - + if ( argv[i][0] == '-' && ( isalpha(argv[i][1]) || isdigit(argv[i][1]) ) && argv[i][2] == 0 ) @@ -387,7 +399,7 @@ public: TEST_EXTRA_ARG(i, 'p'); picture_rate = abs(atoi(argv[i])); break; - + case 's': dolby_atmos_sync_flag = true; break; case 'V': version_flag = true; break; case 'v': verbose_flag = true; break; case 'W': no_write_flag = true; break; @@ -416,7 +428,7 @@ public: if ( help_flag || version_flag ) return; - + if ( filenames.size() < 2 ) { fputs("Option requires at least two filename arguments: <input-file> <output-file>\n", stderr); @@ -515,19 +527,16 @@ write_MPEG2_file(CommandOptions& Options) while ( ASDCP_SUCCESS(result) && duration++ < Options.duration ) { - if ( 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 ) { @@ -578,7 +587,7 @@ check_phfr_params(CommandOptions& Options, JP2K::PictureDescriptor& PDesc) // do not set the label if the user has already done so if ( ! Options.picture_coding.HasValue() ) Options.picture_coding = UL(P_HFR_UL_2K); - + return true; } @@ -673,7 +682,7 @@ write_JP2K_S_file(CommandOptions& Options) if ( ASDCP_SUCCESS(result) ) result = Writer.OpenWrite(Options.out_file.c_str(), Info, PDesc); - + if ( ASDCP_SUCCESS(result) && Options.picture_coding.HasValue() ) { MXF::RGBAEssenceDescriptor *descriptor = 0; @@ -697,7 +706,7 @@ write_JP2K_S_file(CommandOptions& Options) { if ( Options.verbose_flag ) FrameBuffer.Dump(stderr, Options.fb_dump_size); - + if ( Options.encrypt_header_flag ) FrameBuffer.PlaintextOffset(0); } @@ -712,7 +721,7 @@ write_JP2K_S_file(CommandOptions& Options) { if ( Options.verbose_flag ) FrameBuffer.Dump(stderr, Options.fb_dump_size); - + if ( Options.encrypt_header_flag ) FrameBuffer.PlaintextOffset(0); } @@ -825,19 +834,16 @@ write_JP2K_file(CommandOptions& Options) while ( ASDCP_SUCCESS(result) && duration++ < Options.duration ) { - if ( 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 ) { @@ -1007,6 +1013,131 @@ write_PCM_file(CommandOptions& Options) return result; } +// Mix one or more plaintext PCM audio streams with a Dolby Atmos Synchronization channel and write them to a plaintext ASDCP file +// Mix one or more plaintext PCM audio streams with a Dolby Atmos Synchronization channel and write them to a ciphertext ASDCP file +// +Result_t +write_PCM_with_ATMOS_sync_file(CommandOptions& Options) +{ + AESEncContext* Context = 0; + HMACContext* HMAC = 0; + PCM::MXFWriter Writer; + PCM::FrameBuffer FrameBuffer; + PCM::AudioDescriptor ADesc; + Rational PictureRate = Options.PictureRate(); + byte_t IV_buf[CBC_BLOCK_SIZE]; + Kumu::FortunaRNG RNG; + + WriterInfo Info = s_MyInfo; // fill in your favorite identifiers here + if ( Options.asset_id_flag ) + memcpy(Info.AssetUUID, Options.asset_id_value, UUIDlen); + else + Kumu::GenRandomUUID(Info.AssetUUID); + AtmosSyncChannelMixer Mixer(Info.AssetUUID); + + // set up essence parser + Result_t result = Mixer.OpenRead(Options.filenames, PictureRate); + + // set up MXF writer + if ( ASDCP_SUCCESS(result) ) + { + Mixer.FillAudioDescriptor(ADesc); + + ADesc.EditRate = PictureRate; + FrameBuffer.Capacity(PCM::CalcFrameBufferSize(ADesc)); + ADesc.ChannelFormat = PCM::CF_CFG_4; + + if ( Options.verbose_flag ) + { + fprintf(stderr, "%.1fkHz PCM Audio, %s fps (%u spf)\n", + ADesc.AudioSamplingRate.Quotient() / 1000.0, + Options.szPictureRate(), + PCM::CalcSamplesPerFrame(ADesc)); + fputs("AudioDescriptor:\n", stderr); + PCM::AudioDescriptorDump(ADesc); + } + } + + if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag ) + { + Info.LabelSetType = LS_MXF_SMPTE; + fprintf(stderr, "ATTENTION! Writing SMPTE Universal Labels\n"); + + // configure encryption + if( Options.key_flag ) + { + Kumu::GenRandomUUID(Info.ContextID); + Info.EncryptedEssence = true; + + if ( Options.key_id_flag ) + memcpy(Info.CryptographicKeyID, Options.key_id_value, UUIDlen); + else + RNG.FillRandom(Info.CryptographicKeyID, UUIDlen); + + 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, Info.LabelSetType); + } + } + + if ( ASDCP_SUCCESS(result) ) + result = Writer.OpenWrite(Options.out_file.c_str(), Info, ADesc); + } + + if ( ASDCP_SUCCESS(result) ) + { + result = Mixer.Reset(); + ui32_t duration = 0; + + while ( ASDCP_SUCCESS(result) && duration++ < Options.duration ) + { + result = Mixer.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 %u bytes, got %u.\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); + + // 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)); + } + } + } + + if ( result == RESULT_ENDOFFILE ) + result = RESULT_OK; + } + + if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag ) + result = Writer.Finalize(); + + return result; +} + //------------------------------------------------------------------------------------------ // TimedText essence @@ -1109,7 +1240,7 @@ write_timed_text_file(CommandOptions& Options) if ( ! Options.no_write_flag ) { result = Writer.WriteAncillaryResource(FrameBuffer, Context, HMAC); - + // 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 @@ -1129,6 +1260,124 @@ write_timed_text_file(CommandOptions& Options) return result; } +// Write one or more plaintext Dolby ATMOS bytestreams to a plaintext ASDCP file +// Write one or more plaintext Dolby ATMOS bytestreams to a ciphertext ASDCP file +// +Result_t +write_dolby_atmos_file(CommandOptions& Options) +{ + AESEncContext* Context = 0; + HMACContext* HMAC = 0; + ATMOS::MXFWriter Writer; + DCData::FrameBuffer FrameBuffer(Options.fb_size); + ATMOS::AtmosDescriptor ADesc; + DCData::SequenceParser Parser; + byte_t IV_buf[CBC_BLOCK_SIZE]; + Kumu::FortunaRNG RNG; + + // set up essence parser + Result_t result = Parser.OpenRead(Options.filenames.front().c_str()); + + // set up MXF writer + if ( ASDCP_SUCCESS(result) ) + { + Parser.FillDCDataDescriptor(ADesc); + ADesc.EditRate = Options.PictureRate(); + // TODO: fill AtmosDescriptor + ADesc.FirstFrame = Options.ffoa; + ADesc.MaxChannelCount = Options.max_channel_count; + ADesc.MaxObjectCount = Options.max_object_count; + Kumu::GenRandomUUID(ADesc.AtmosID); + ADesc.AtmosVersion = 1; + if ( Options.verbose_flag ) + { + fprintf(stderr, "Dolby ATMOS Data\n"); + fputs("AtmosDescriptor:\n", stderr); + fprintf(stderr, "Frame Buffer size: %u\n", Options.fb_size); + ATMOS::AtmosDescriptorDump(ADesc); + } + } + + if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag ) + { + WriterInfo Info = s_MyInfo; // fill in your favorite identifiers here + if ( Options.asset_id_flag ) + memcpy(Info.AssetUUID, Options.asset_id_value, UUIDlen); + else + Kumu::GenRandomUUID(Info.AssetUUID); + + Info.LabelSetType = LS_MXF_SMPTE; + + // configure encryption + if( Options.key_flag ) + { + Kumu::GenRandomUUID(Info.ContextID); + Info.EncryptedEssence = true; + + if ( Options.key_id_flag ) + memcpy(Info.CryptographicKeyID, Options.key_id_value, UUIDlen); + else + RNG.FillRandom(Info.CryptographicKeyID, UUIDlen); + + 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, Info.LabelSetType); + } + } + + if ( ASDCP_SUCCESS(result) ) + result = Writer.OpenWrite(Options.out_file.c_str(), Info, ADesc); + } + + if ( ASDCP_SUCCESS(result) ) + { + ui32_t duration = 0; + result = Parser.Reset(); + + while ( ASDCP_SUCCESS(result) && duration++ < Options.duration ) + { + 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); + + // 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)); + } + } + + if ( result == RESULT_ENDOFFILE ) + result = RESULT_OK; + } + + if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag ) + result = Writer.Finalize(); + + return result; +} + // int main(int argc, const char** argv) @@ -1184,13 +1433,24 @@ main(int argc, const char** argv) case ESS_PCM_24b_48k: case ESS_PCM_24b_96k: - result = write_PCM_file(Options); + if ( Options.dolby_atmos_sync_flag ) + { + result = write_PCM_with_ATMOS_sync_file(Options); + } + else + { + result = write_PCM_file(Options); + } break; case ESS_TIMED_TEXT: result = write_timed_text_file(Options); break; + case ESS_DCDATA_DOLBY_ATMOS: + result = write_dolby_atmos_file(Options); + break; + default: fprintf(stderr, "%s: Unknown file type, not ASDCP-compatible essence.\n", Options.filenames.front().c_str()); diff --git a/src/blackwave.cpp b/src/blackwave.cpp index 35e5e77..36d3235 100644 --- a/src/blackwave.cpp +++ b/src/blackwave.cpp @@ -187,8 +187,8 @@ make_black_wav_file(CommandOptions& Options) if ( ASDCP_SUCCESS(result) ) { - Wav::SimpleWaveHeader WavHeader(ADesc); - result = WavHeader.WriteToFile(OutFile); + RF64::SimpleRF64Header WavHeader(ADesc); + result = WavHeader.WriteToFile(OutFile); } if ( ASDCP_SUCCESS(result) ) diff --git a/src/h__Reader.cpp b/src/h__Reader.cpp index da2aca2..77d732f 100755 --- a/src/h__Reader.cpp +++ b/src/h__Reader.cpp @@ -75,19 +75,26 @@ ASDCP::h__ASDCPReader::OpenMXFRead(const char* filename) if ( KM_SUCCESS(result) ) { - // if this is a three partition file, go to the body - // partition and read the partition pack - if ( m_HeaderPart.m_RIP.PairArray.size() > 2 ) - { - Array<RIP::Pair>::iterator r_i = m_HeaderPart.m_RIP.PairArray.begin(); - r_i++; - m_File.Seek((*r_i).ByteOffset); - result = m_BodyPart.InitFromFile(m_File); - } + // if this is a three partition file, go to the body + // partition and read the partition pack + if ( m_HeaderPart.m_RIP.PairArray.size() > 2 ) + { + Array<RIP::Pair>::iterator r_i = m_HeaderPart.m_RIP.PairArray.begin(); + r_i++; + m_File.Seek((*r_i).ByteOffset); + result = m_BodyPart.InitFromFile(m_File); + if( !ASDCP_SUCCESS(result) ) + { + DefaultLogSink().Error("ASDCP::h__Reader::OpenMXFRead, m_BodyPart.InitFromFile failed\n"); + } + } } + else + DefaultLogSink().Error("ASDCP::h__Reader::OpenMXFRead, TrackFileReader::OpenMXFRead failed\n"); + if ( KM_SUCCESS(result) ) - m_HeaderPart.BodyOffset = m_File.Tell(); + m_HeaderPart.BodyOffset = m_File.Tell(); return result; } @@ -148,6 +155,14 @@ ASDCP::h__ASDCPReader::ReadEKLVFrame(ui32_t FrameNum, ASDCP::FrameBuffer& FrameB EssenceUL, Ctx, HMAC); } +Result_t +ASDCP::h__ASDCPReader::LocateFrame(ui32_t FrameNum, Kumu::fpos_t& streamOffset, + i8_t& temporalOffset, i8_t& keyFrameOffset) +{ + return ASDCP::MXF::TrackFileReader<OPAtomHeader, OPAtomIndexFooter>::LocateFrame(m_HeaderPart, FrameNum, + streamOffset, temporalOffset, keyFrameOffset); +} + //------------------------------------------------------------------------------------------ // |
