diff options
| author | jelkins <jelkins@cinecert.com> | 2020-10-08 09:49:48 -0700 |
|---|---|---|
| committer | jelkins <jelkins@cinecert.com> | 2020-10-08 09:49:48 -0700 |
| commit | ee40718aa383cb40beb67be317b3565c218d32c1 (patch) | |
| tree | d2bd150d132a7dbf8c6bbe0dd6ca0c001292158c /src | |
| parent | 6b98e08246b5b6a191152e3a3a4a69b459d1ae35 (diff) | |
| parent | d417531ed59434ecaee487adfdf54646408479bf (diff) | |
Merge branch 'master' of https://github.com/Jason-elkins/asdcplib into check_optional_values_arent_empty
Diffstat (limited to 'src')
| -rw-r--r-- | src/AS_02_ACES.cpp | 2 | ||||
| -rw-r--r-- | src/AS_02_IAB.cpp | 629 | ||||
| -rw-r--r-- | src/AS_02_IAB.h | 239 | ||||
| -rw-r--r-- | src/AS_02_ISXD.cpp | 2 | ||||
| -rw-r--r-- | src/AS_02_JP2K.cpp | 2 | ||||
| -rw-r--r-- | src/AS_02_PCM.cpp | 2 | ||||
| -rw-r--r-- | src/AS_02_PHDR.cpp | 10 | ||||
| -rw-r--r-- | src/AS_02_TimedText.cpp | 2 | ||||
| -rw-r--r-- | src/AS_02_internal.h | 6 | ||||
| -rwxr-xr-x | src/AS_DCP.h | 10 | ||||
| -rwxr-xr-x | src/AS_DCP_AES.cpp | 21 | ||||
| -rwxr-xr-x | src/AS_DCP_JP2K.cpp | 2 | ||||
| -rwxr-xr-x | src/AS_DCP_MXF.cpp | 4 | ||||
| -rwxr-xr-x | src/AS_DCP_internal.h | 61 | ||||
| -rw-r--r-- | src/CMakeLists.txt | 6 | ||||
| -rw-r--r-- | src/KM_fileio.cpp | 4 | ||||
| -rw-r--r-- | src/MDD.cpp | 21 | ||||
| -rwxr-xr-x | src/MDD.h | 7 | ||||
| -rwxr-xr-x | src/MXF.cpp | 6 | ||||
| -rw-r--r-- | src/Makefile.am | 2 | ||||
| -rwxr-xr-x | src/Metadata.cpp | 146 | ||||
| -rwxr-xr-x | src/Metadata.h | 44 | ||||
| -rw-r--r-- | src/h__02_Writer.cpp | 12 | ||||
| -rwxr-xr-x | src/h__Writer.cpp | 12 |
24 files changed, 1209 insertions, 43 deletions
diff --git a/src/AS_02_ACES.cpp b/src/AS_02_ACES.cpp index 9a847cc..876af18 100644 --- a/src/AS_02_ACES.cpp +++ b/src/AS_02_ACES.cpp @@ -603,7 +603,7 @@ AS_02::Result_t AS_02::ACES::MXFWriter::h__Writer::SetSourceStream(const std::st { result = WriteAS02Header(label, UL(m_Dict->ul(MDD_MXFGCFrameWrappedACESPictures)), //Essence Container Label per 2065-5 section 8.1 (frame wrapping) PICT_DEF_LABEL, UL(m_EssenceUL), UL(m_Dict->ul(MDD_PictureDataDef)), - edit_rate, derive_timecode_rate_from_edit_rate(edit_rate)); + edit_rate); if(KM_SUCCESS(result)) { diff --git a/src/AS_02_IAB.cpp b/src/AS_02_IAB.cpp new file mode 100644 index 0000000..e7c1fc6 --- /dev/null +++ b/src/AS_02_IAB.cpp @@ -0,0 +1,629 @@ +/* +Copyright (c) 2011-2020, Robert Scheler, Heiko Sparenberg Fraunhofer IIS, +John Hurst, Pierre-Anthony Lemieux + +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. +*/ + +#include "AS_02_internal.h" +#include "AS_02_IAB.h" + +#include <iostream> +#include <iomanip> +#include <stdexcept> + +namespace Kumu { + class RuntimeError : public std::runtime_error { + Kumu::Result_t m_Result; + RuntimeError(); + public: + RuntimeError(const Kumu::Result_t& result) : std::runtime_error(result.Message()), m_Result(result) {} + Kumu::Result_t GetResult() { return this->m_Result; } + ~RuntimeError() throw() {} + }; +} + +//------------------------------------------------------------------------------------------ + +/* Size of the BER Length of the clip */ +static const int CLIP_BER_LENGTH_SIZE = 8; + +/* Combined size of the Key and Length of the clip */ +static const int RESERVED_KL_SIZE = ASDCP::SMPTE_UL_LENGTH + CLIP_BER_LENGTH_SIZE; + + +AS_02::IAB::MXFWriter::MXFWriter() : m_ClipStart(0), m_State(ST_BEGIN) { +} + +AS_02::IAB::MXFWriter::~MXFWriter() {} + +const ASDCP::MXF::OP1aHeader& +AS_02::IAB::MXFWriter::OP1aHeader() const { + if (this->m_State == ST_BEGIN) { + throw Kumu::RuntimeError(Kumu::RESULT_INIT); + } + + return this->m_Writer->m_HeaderPart; +} + +const ASDCP::MXF::RIP& +AS_02::IAB::MXFWriter::RIP() const { + if (this->m_State == ST_BEGIN) { + throw Kumu::RuntimeError(Kumu::RESULT_INIT); + } + + return this->m_Writer->m_RIP; +} + +Kumu::Result_t +AS_02::IAB::MXFWriter::OpenWrite( + const std::string& filename, + const ASDCP::WriterInfo& Info, + const ASDCP::MXF::IABSoundfieldLabelSubDescriptor& sub, + const std::vector<ASDCP::UL>& conformsToSpecs, + const ASDCP::Rational& edit_rate, + const ASDCP::Rational& sample_rate) { + + /* are we already running */ + + if (this->m_State != ST_BEGIN) { + return Kumu::RESULT_STATE; + } + + Result_t result = Kumu::RESULT_OK; + + /* initialize the writer */ + + this->m_Writer = new AS_02::IAB::MXFWriter::h__Writer(DefaultSMPTEDict()); + + this->m_Writer->m_Info = Info; + + this->m_Writer->m_HeaderSize = 16 * 1024; + + try { + + /* open the file */ + + result = this->m_Writer->m_File.OpenWrite(filename.c_str()); + + if (result.Failure()) { + throw Kumu::RuntimeError(result); + } + + /* initialize IAB descriptor */ + + ASDCP::MXF::IABEssenceDescriptor* desc = new ASDCP::MXF::IABEssenceDescriptor(this->m_Writer->m_Dict); + + GenRandomValue(desc->InstanceUID); /* TODO: remove */ + desc->SampleRate = edit_rate; + desc->AudioSamplingRate = sample_rate; + desc->ChannelCount = 0; + desc->SoundEssenceCoding = this->m_Writer->m_Dict->ul(MDD_ImmersiveAudioCoding); + desc->QuantizationBits = 24; + + this->m_Writer->m_EssenceDescriptor = desc; + + /* copy and add the IAB subdescriptor */ + + ASDCP::MXF::IABSoundfieldLabelSubDescriptor* subdesc = new ASDCP::MXF::IABSoundfieldLabelSubDescriptor(sub); + + GenRandomValue(subdesc->InstanceUID); /* TODO: remove */ + subdesc->MCATagName = "IAB"; + subdesc->MCATagSymbol = "IAB"; + subdesc->MCALabelDictionaryID = this->m_Writer->m_Dict->ul(MDD_IABSoundfield); + GenRandomValue(subdesc->MCALinkID); + + this->m_Writer->m_EssenceSubDescriptorList.push_back(subdesc); + this->m_Writer->m_EssenceDescriptor->SubDescriptors.push_back(subdesc->InstanceUID); + + /* initialize the index write */ + + this->m_Writer->m_IndexWriter.SetEditRate(edit_rate); + + /* Essence Element UL */ + + byte_t element_ul_bytes[ASDCP::SMPTE_UL_LENGTH]; + + const ASDCP::MDDEntry& element_ul_entry = this->m_Writer->m_Dict->Type(MDD_IMF_IABEssenceClipWrappedElement); + + std::copy(element_ul_entry.ul, element_ul_entry.ul + ASDCP::SMPTE_UL_LENGTH, element_ul_bytes); + + /* only a single track */ + + element_ul_bytes[15] = 1; + + /* only a single element */ + + element_ul_bytes[13] = 1; + + /* write the file header*/ + /* WriteAS02Header() takes ownership of desc and subdesc */ + + result = this->m_Writer->WriteAS02Header( + "Clip wrapping of IA bitstreams as specified in SMPTE ST 2067-201", + UL(this->m_Writer->m_Dict->ul(MDD_IMF_IABEssenceClipWrappedContainer)), + "IA Bitstream", + UL(element_ul_bytes), + UL(this->m_Writer->m_Dict->ul(MDD_SoundDataDef)), + edit_rate, + &conformsToSpecs + ); + + if (result.Failure()) { + throw Kumu::RuntimeError(result); + } + + /* start the clip */ + + this->m_ClipStart = this->m_Writer->m_File.Tell(); + + /* reserve space for the KL of the KLV, which will be written later during finalization */ + + byte_t clip_buffer[RESERVED_KL_SIZE] = { 0 }; + + memcpy(clip_buffer, element_ul_bytes, ASDCP::SMPTE_UL_LENGTH); + + if (!Kumu::write_BER(clip_buffer + ASDCP::SMPTE_UL_LENGTH, 0, CLIP_BER_LENGTH_SIZE)) { + throw Kumu::RuntimeError(Kumu::RESULT_FAIL); + } + + result = this->m_Writer->m_File.Write(clip_buffer, RESERVED_KL_SIZE); + + if (result.Failure()) { + throw Kumu::RuntimeError(result); + } + + this->m_Writer->m_StreamOffset = RESERVED_KL_SIZE; + + this->m_State = ST_READY; + + } catch (Kumu::RuntimeError e) { + + this->Reset(); + + return e.GetResult(); + + } + + return result; + +} + +Result_t +AS_02::IAB::MXFWriter::WriteFrame(const ui8_t* frame, ui32_t sz) { + + /* are we running */ + + if (this->m_State == ST_BEGIN) { + return Kumu::RESULT_INIT; + } + + Result_t result = Kumu::RESULT_OK; + + /* update the index */ + + IndexTableSegment::IndexEntry Entry; + + Entry.StreamOffset = this->m_Writer->m_StreamOffset; + + this->m_Writer->m_IndexWriter.PushIndexEntry(Entry); + + try { + + /* write the frame */ + + result = this->m_Writer->m_File.Write(frame, sz); + + if (result.Failure()) { + throw Kumu::RuntimeError(result); + } + + /* increment the frame counter */ + + this->m_Writer->m_FramesWritten++; + + /* increment stream offset */ + + this->m_Writer->m_StreamOffset += sz; + + /* we are running now */ + + this->m_State = ST_RUNNING; + + } catch (Kumu::RuntimeError e) { + + this->Reset(); + + return e.GetResult(); + + } + + return result; +} + +Result_t +AS_02::IAB::MXFWriter::Finalize() { + + /* are we running */ + + if (this->m_State == ST_BEGIN) { + return Kumu::RESULT_INIT; + } + + Result_t result = RESULT_OK; + + try { + + /* write clip length */ + + ui64_t current_position = this->m_Writer->m_File.Tell(); + + result = this->m_Writer->m_File.Seek(m_ClipStart + ASDCP::SMPTE_UL_LENGTH); + + byte_t clip_buffer[CLIP_BER_LENGTH_SIZE] = { 0 }; + + ui64_t size = static_cast<ui64_t>(this->m_Writer->m_StreamOffset) /* total size of the KLV */ - RESERVED_KL_SIZE; + + bool check = Kumu::write_BER(clip_buffer, size, CLIP_BER_LENGTH_SIZE); + + if (!check) { + throw Kumu::RuntimeError(Kumu::RESULT_FAIL); + } + + result = this->m_Writer->m_File.Write(clip_buffer, CLIP_BER_LENGTH_SIZE); + + if (result.Failure()) { + throw Kumu::RuntimeError(result); + } + + result = this->m_Writer->m_File.Seek(current_position); + + if (result.Failure()) { + throw Kumu::RuntimeError(result); + } + + /* write footer */ + + result = this->m_Writer->WriteAS02Footer(); + + if (result.Failure()) { + throw Kumu::RuntimeError(result); + } + + } catch (Kumu::RuntimeError e) { + + /* nothing to do since we are about to reset */ + + result = e.GetResult(); + + } + + /* we are ready to start again */ + + this->Reset(); + + return result; +} + +void +AS_02::IAB::MXFWriter::Reset() { + this->m_Writer.set(NULL); + this->m_State = ST_BEGIN; +} + + +//------------------------------------------------------------------------------------------ + + +AS_02::IAB::MXFReader::MXFReader() : m_State(ST_READER_BEGIN) {} + +AS_02::IAB::MXFReader::~MXFReader() {} + +ASDCP::MXF::OP1aHeader& +AS_02::IAB::MXFReader::OP1aHeader() const { + if (this->m_State == ST_READER_BEGIN) { + throw Kumu::RuntimeError(Kumu::RESULT_INIT); + } + + return this->m_Reader->m_HeaderPart; +} + +const ASDCP::MXF::RIP& +AS_02::IAB::MXFReader::RIP() const { + if (this->m_State == ST_READER_BEGIN) { + throw Kumu::RuntimeError(Kumu::RESULT_INIT); + } + + return this->m_Reader->m_RIP; +} + +Result_t +AS_02::IAB::MXFReader::OpenRead(const std::string& filename) { + + /* are we already running */ + + if (this->m_State != ST_READER_BEGIN) { + return Kumu::RESULT_STATE; + } + + Result_t result = Kumu::RESULT_OK; + + /* initialize the writer */ + + this->m_Reader = new h__Reader(DefaultCompositeDict()); + + try { + + result = this->m_Reader->OpenMXFRead(filename); + + if (result.Failure()) { + throw Kumu::RuntimeError(result); + } + + InterchangeObject* tmp_iobj = 0; + + this->m_Reader->m_HeaderPart.GetMDObjectByType( + this->m_Reader->m_Dict->Type(MDD_IABEssenceDescriptor).ul, + &tmp_iobj + ); + + if (!tmp_iobj) { + throw Kumu::RuntimeError(Kumu::RESULT_FAIL); + } + + this->m_Reader->m_HeaderPart.GetMDObjectByType( + this->m_Reader->m_Dict->Type(MDD_IABSoundfieldLabelSubDescriptor).ul, + &tmp_iobj + ); + + if (!tmp_iobj) { + throw Kumu::RuntimeError(Kumu::RESULT_FAIL); + } + + std::list<InterchangeObject*> ObjectList; + + this->m_Reader->m_HeaderPart.GetMDObjectsByType( + this->m_Reader->m_Dict->Type(MDD_Track).ul, + ObjectList + ); + + if (ObjectList.empty()) { + throw Kumu::RuntimeError(Kumu::RESULT_FAIL); + } + + /* invalidate current frame */ + + this->m_CurrentFrameIndex = -1; + + /* we are ready */ + + this->m_State = ST_READER_READY; + + } catch (Kumu::RuntimeError e) { + + this->Reset(); + + return e.GetResult(); + } + + return RESULT_OK; +} + + +Result_t +AS_02::IAB::MXFReader::Close() { + + /* are we already running */ + + if (this->m_State == ST_READER_BEGIN) { + return Kumu::RESULT_INIT; + } + + this->Reset(); + + return Kumu::RESULT_OK; +} + + +Result_t AS_02::IAB::MXFReader::GetFrameCount(ui32_t& frameCount) const { + + /* are we already running */ + + if (this->m_State == ST_READER_BEGIN) { + return Kumu::RESULT_INIT; + } + + frameCount = m_Reader->m_IndexAccess.GetDuration(); + + return Kumu::RESULT_OK; +} + +Result_t +AS_02::IAB::MXFReader::ReadFrame(ui32_t frame_number, AS_02::IAB::MXFReader::Frame& frame) { + + /* are we already running */ + + if (this->m_State == ST_READER_BEGIN) { + return Kumu::RESULT_INIT; + } + + Result_t result = RESULT_OK; + + /* have we already read the frame */ + + if (frame_number != this->m_CurrentFrameIndex) { + + try { + + // look up frame index node + IndexTableSegment::IndexEntry index_entry; + + result = this->m_Reader->m_IndexAccess.Lookup(frame_number, index_entry); + + if (result.Failure()) { + DefaultLogSink().Error("Frame value out of range: %u\n", frame_number); + throw Kumu::RuntimeError(result); + } + + result = this->m_Reader->m_File.Seek(index_entry.StreamOffset); + + if (result.Failure()) { + DefaultLogSink().Error("Cannot seek to stream offset: %u\n", index_entry.StreamOffset); + throw Kumu::RuntimeError(result); + } + + /* read the preamble info */ + + const int preambleTLLen = 5; + const int frameTLLen = 5; + + ui32_t buffer_offset = 0; + + this->m_CurrentFrameBuffer.resize(preambleTLLen); + + result = this->m_Reader->m_File.Read(&this->m_CurrentFrameBuffer[buffer_offset], preambleTLLen); + + if (result.Failure()) { + DefaultLogSink().Error("Error reading IA Frame preamble\n", index_entry.StreamOffset); + throw Kumu::RuntimeError(result); + } + + ui32_t preambleLen = ((ui32_t)this->m_CurrentFrameBuffer[1 + buffer_offset] << 24) + + ((ui32_t)this->m_CurrentFrameBuffer[2 + buffer_offset] << 16) + + ((ui32_t)this->m_CurrentFrameBuffer[3 + buffer_offset] << 8) + + (ui32_t)this->m_CurrentFrameBuffer[4 + buffer_offset]; + + buffer_offset += preambleTLLen; + + /* read the preamble*/ + + if (preambleLen > 0) { + + this->m_CurrentFrameBuffer.resize(preambleTLLen + preambleLen); + + result = this->m_Reader->m_File.Read(&this->m_CurrentFrameBuffer[buffer_offset], preambleLen); + + if (result.Failure()) { + DefaultLogSink().Error("Error reading IA Frame preamble\n", index_entry.StreamOffset); + throw Kumu::RuntimeError(result); + } + + buffer_offset += preambleLen; + + } + + /* read the IA Frame info */ + + this->m_CurrentFrameBuffer.resize(preambleTLLen + preambleLen + frameTLLen); + + result = this->m_Reader->m_File.Read(&this->m_CurrentFrameBuffer[buffer_offset], frameTLLen); + + if (result.Failure()) { + DefaultLogSink().Error("Error reading IA Frame data\n", index_entry.StreamOffset); + throw Kumu::RuntimeError(result); + } + + ui32_t frameLen = ((ui32_t)this->m_CurrentFrameBuffer[buffer_offset + 1] << 24) + + ((ui32_t)this->m_CurrentFrameBuffer[buffer_offset + 2] << 16) + + ((ui32_t)this->m_CurrentFrameBuffer[buffer_offset + 3] << 8) + + (ui32_t)this->m_CurrentFrameBuffer[buffer_offset + 4]; + + buffer_offset += frameTLLen; + + /* read the IA Frame */ + + if (frameLen > 0) { + + this->m_CurrentFrameBuffer.resize(preambleTLLen + preambleLen + frameTLLen + frameLen); + + result = this->m_Reader->m_File.Read(&this->m_CurrentFrameBuffer[buffer_offset], frameLen); + + if (result.Failure()) { + DefaultLogSink().Error("Error reading IA Frame data\n", index_entry.StreamOffset); + throw Kumu::RuntimeError(result); + } + + buffer_offset += frameLen; + + } + + /* update current frame */ + + this->m_CurrentFrameIndex = frame_number; + + } catch (Kumu::RuntimeError e) { + + this->Reset(); + + return e.GetResult(); + + } + + } + + frame = std::pair<size_t, const ui8_t*>(this->m_CurrentFrameBuffer.size(), &this->m_CurrentFrameBuffer[0]); + + this->m_State = ST_READER_RUNNING; + + return result; +} + +Result_t +AS_02::IAB::MXFReader::FillWriterInfo(WriterInfo& Info) const { + /* are we already running */ + + if (this->m_State == ST_READER_BEGIN) { + return Kumu::RESULT_FAIL; + } + + Info = m_Reader->m_Info; + + return Kumu::RESULT_OK; +} + +void +AS_02::IAB::MXFReader::DumpHeaderMetadata(FILE* stream) const { + if (this->m_State != ST_READER_BEGIN) { + this->m_Reader->m_HeaderPart.Dump(stream); + } +} + + +void +AS_02::IAB::MXFReader::DumpIndex(FILE* stream) const { + if (this->m_State != ST_READER_BEGIN) { + this->m_Reader->m_IndexAccess.Dump(stream); + } +} + +void +AS_02::IAB::MXFReader::Reset() { + this->m_Reader.set(NULL); + this->m_State = ST_READER_BEGIN; +} + +// +// end AS_02_IAB.cpp +// diff --git a/src/AS_02_IAB.h b/src/AS_02_IAB.h new file mode 100644 index 0000000..57fb77e --- /dev/null +++ b/src/AS_02_IAB.h @@ -0,0 +1,239 @@ +/* +Copyright (c), Bjoern Stresing, Patrick Bichiou, Wolfgang Ruppel, +John Hurst, Pierre-Anthony Lemieux + +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. +*/ + +/** + * Reader and writer classes for IAB Track Files, as defined in SMPTE ST 2067-201 + * + * @file + */ + +#ifndef AS_02_IAB_h__ +#define AS_02_IAB_h__ + +#include "AS_02.h" +#include "AS_02_internal.h" +#include "Metadata.h" + +namespace AS_02 { + + namespace IAB { + + /** + * Writes IAB Track Files as specified in ST SMPTE 2067-201 + * + */ + class MXFWriter { + + typedef h__AS02Writer<AS_02::MXF::AS02IndexWriterVBR> h__Writer; + + ASDCP::mem_ptr<h__Writer> m_Writer; + ui64_t m_ClipStart; + WriterState_t m_State; + + void Reset(); + + ASDCP_NO_COPY_CONSTRUCT(MXFWriter); + + public: + MXFWriter(); + virtual ~MXFWriter(); + + /** + * Must be preceded by a succesful OpenWrite() call followed by zero or more WriteFrame() calls + * + * Warning: direct manipulation of MXF structures can interfere + * with the normal operation of the wrapper. Caveat emptor! + * + * @return Header of the Track File + * + * @throws std::runtime_error if the Track File is not open + */ + virtual const ASDCP::MXF::OP1aHeader& OP1aHeader() const; + + /** + * Must be preceded by a succesful OpenWrite() call followed by zero or more WriteFrame() calls + * + * Warning: direct manipulation of MXF structures can interfere + * with the normal operation of the wrapper. Caveat emptor! + * + * @return RIP of the Track File + * + * @throws std::runtime_error if the Track File is not open + */ + virtual const ASDCP::MXF::RIP& RIP() const; + + /** + * Creates and prepares an IAB Track File for writing. + * + * Must be called following instantiation or after Finalize() call + * + * @param filename Path to the file. The file must no exist. + * @param Info MXF file metadata to be written. + * @param sub IAB Soundfield Subdescritor items to be written. MCATagName, MCATagSymbol, MCALabelDictionaryID, MCALinkID are ignored. + * @param conformsToSpecs Value of the ConformsToSpecifications preface item + * @param edit_rate Frame rate of the IA Bitstream + * @param sampling_rate Sampling rate of the audio essence within the IA Bitstream + * + * @return RESULT_OK indicates that frames are ready to be written, + * otherwise the reader is reset and the file is left is an undermined state. + */ + Result_t OpenWrite( + const std::string& filename, + const ASDCP::WriterInfo& Info, + const ASDCP::MXF::IABSoundfieldLabelSubDescriptor& sub, + const std::vector<ASDCP::UL>& conformsToSpecs, + const ASDCP::Rational& edit_rate, + const ASDCP::Rational& sampling_rate = ASDCP::SampleRate_48k + ); + + /** + * Writes a single frame. + * + * Must be preceded by a succesful OpenWrite() call followed by zero or more WriteFrame() calls + * + * @param frame Pointer to a complete IA Frame + * @param sz Size in bytes of the IA Frame + * @return RESULT_OK indicates that the frame is written and additional frames can be written, + * otherwise the reader is reset and the file is left is an undermined state. + */ + Result_t WriteFrame(const ui8_t* frame, ui32_t sz); + + /** + * Writes the Track File footer and closes the file. + * + * Must be preceded by a succesful OpenWrite() call followed by zero or more WriteFrame() calls + * + * @return RESULT_OK indicates that the frame is written and additional frames can be written, + * otherwise the reader is reset and the file is left is an undermined state. + */ + Result_t Finalize(); + }; + + /** + * Reads IAB Track Files as specified in ST SMPTE 2067-201 + * + */ + class MXFReader { + + typedef h__AS02Reader h__Reader; + + ASDCP::mem_ptr<h__Reader> m_Reader; + + i64_t m_CurrentFrameIndex; + std::vector<ui8_t> m_CurrentFrameBuffer; + ReaderState_t m_State; + + void Reset(); + + ASDCP_NO_COPY_CONSTRUCT(MXFReader); + + public: + + /* typedefs*/ + + typedef std::pair<size_t, const ui8_t*> Frame; + + /* methods */ + + MXFReader(); + virtual ~MXFReader(); + + /** + * Warning: direct manipulation of MXF structures can interfere + * with the normal operation of the wrapper. Caveat emptor! + * + * @return Header of the Track File + * + * @throws std::runtime_error if the Track File is not open + */ + virtual ASDCP::MXF::OP1aHeader& OP1aHeader() const; + + /** + * Warning: direct manipulation of MXF structures can interfere + * with the normal operation of the wrapper. Caveat emptor! + * + * @return RIP of the Track File + * + * @throws std::runtime_error if the Track File is not open + */ + virtual const ASDCP::MXF::RIP& RIP() const; + + /** + * Creates and prepares an IAB Track File for reading. + * + * @param filename Path to the file. The file must no exist. + * + * @return RESULT_OK indicates that frames are ready to be read, + * otherwise the reader is reset + */ + Result_t OpenRead(const std::string& filename); + + /** + * Closes the IAB Track File. + * + * @return RESULT_OK indicates that the Track File was successfully closed. + */ + Result_t Close(); + + /** + * Fill a WriterInfo struct with the values from the Track File's header. + * + * @param writer_info Struct to be filled + * @return RESULT_OK indicates that writer_info was successfully filled. + */ + Result_t FillWriterInfo(ASDCP::WriterInfo& writer_info) const; + + /** + * Reads an IA Frame. + * + * @param frame_number Index of the frame to be read. Must be in the range [0, GetFrameCount()). + * @param frame Frame data. Must not be modified. Remains valid until the next call to ReadFrame(). + * @return RESULT_OK indicates that more frames are ready to be read, + * otherwise the file is closed and the reader reset + */ + Result_t ReadFrame(ui32_t frame_number, Frame& frame); + + /** + * Returns the number of IA Frame in the Track File. + * + * @param frameCount Number of IA Frames + * @return RESULT_OK unless the file is not open + */ + Result_t GetFrameCount(ui32_t& frameCount) const; + + // Print debugging information to stream + void DumpHeaderMetadata(FILE* = 0) const; + void DumpIndex(FILE* = 0) const; + }; + + } //namespace IAB + +} // namespace AS_02 + +#endif // AS_02_IAB_h__ diff --git a/src/AS_02_ISXD.cpp b/src/AS_02_ISXD.cpp index 6230c58..a374664 100644 --- a/src/AS_02_ISXD.cpp +++ b/src/AS_02_ISXD.cpp @@ -338,7 +338,7 @@ AS_02::ISXD::MXFWriter::h__Writer::SetSourceStream(const std::string& label, con { result = WriteAS02Header(label, UL(m_Dict->ul(MDD_FrameWrappedISXDContainer)), PICT_DEF_LABEL, UL(m_EssenceUL), UL(m_Dict->ul(MDD_DataDataDef)), - edit_rate, derive_timecode_rate_from_edit_rate(edit_rate)); + edit_rate); if ( KM_SUCCESS(result) ) { diff --git a/src/AS_02_JP2K.cpp b/src/AS_02_JP2K.cpp index 321abfc..ac951e4 100644 --- a/src/AS_02_JP2K.cpp +++ b/src/AS_02_JP2K.cpp @@ -363,7 +363,7 @@ AS_02::JP2K::MXFWriter::h__Writer::SetSourceStream(const std::string& label, con result = WriteAS02Header(label, wrapping_label, PICT_DEF_LABEL, UL(m_EssenceUL), UL(m_Dict->ul(MDD_PictureDataDef)), - edit_rate, derive_timecode_rate_from_edit_rate(edit_rate)); + edit_rate); if ( KM_SUCCESS(result) ) { diff --git a/src/AS_02_PCM.cpp b/src/AS_02_PCM.cpp index 8532887..aa7b2a2 100644 --- a/src/AS_02_PCM.cpp +++ b/src/AS_02_PCM.cpp @@ -423,7 +423,7 @@ AS_02::PCM::MXFWriter::h__Writer::SetSourceStream(const ASDCP::Rational& edit_ra m_BytesPerSample = AS_02::MXF::CalcSampleSize(*m_WaveAudioDescriptor); result = WriteAS02Header(PCM_PACKAGE_LABEL, UL(m_Dict->ul(MDD_WAVWrappingClip)), SOUND_DEF_LABEL, UL(m_EssenceUL), UL(m_Dict->ul(MDD_SoundDataDef)), - m_EssenceDescriptor->SampleRate, derive_timecode_rate_from_edit_rate(edit_rate)); + m_EssenceDescriptor->SampleRate); if ( KM_SUCCESS(result) ) { diff --git a/src/AS_02_PHDR.cpp b/src/AS_02_PHDR.cpp index c08efd0..471d226 100644 --- a/src/AS_02_PHDR.cpp +++ b/src/AS_02_PHDR.cpp @@ -349,8 +349,7 @@ class AS_02::PHDR::MXFWriter::h__Writer : public AS_02::h__AS02WriterFrame Result_t WritePHDRHeader(const std::string& PackageLabel, const ASDCP::UL& WrappingUL, const std::string& TrackName, const ASDCP::UL& EssenceUL, - const ASDCP::UL& DataDefinition, const ASDCP::Rational& EditRate, - const ui32_t& TCFrameRate); + const ASDCP::UL& DataDefinition, const ASDCP::Rational& EditRate); public: byte_t m_EssenceUL[SMPTE_UL_LENGTH]; @@ -436,8 +435,7 @@ AS_02::PHDR::MXFWriter::h__Writer::OpenWrite(const std::string& filename, Result_t AS_02::PHDR::MXFWriter::h__Writer::WritePHDRHeader(const std::string& PackageLabel, const ASDCP::UL& WrappingUL, const std::string& TrackName, const ASDCP::UL& EssenceUL, - const ASDCP::UL& DataDefinition, const ASDCP::Rational& EditRate, - const ui32_t& TCFrameRate) + const ASDCP::UL& DataDefinition, const ASDCP::Rational& EditRate) { if ( EditRate.Numerator == 0 || EditRate.Denominator == 0 ) { @@ -447,7 +445,7 @@ AS_02::PHDR::MXFWriter::h__Writer::WritePHDRHeader(const std::string& PackageLab InitHeader(MXFVersion_2011); - AddSourceClip(EditRate, EditRate/*TODO: for a moment*/, TCFrameRate, TrackName, EssenceUL, DataDefinition, PackageLabel); + AddSourceClip(EditRate, EditRate/*TODO: for a moment*/, 0 /*no timecode track*/, TrackName, EssenceUL, DataDefinition, PackageLabel); // add metadata track TrackSet<SourceClip> metdata_track = @@ -534,7 +532,7 @@ AS_02::PHDR::MXFWriter::h__Writer::SetSourceStream(const std::string& label, con { result = WritePHDRHeader(label, UL(m_Dict->ul(MDD_MXFGCFUFrameWrappedPictureElement)), PICT_DEF_LABEL, UL(m_EssenceUL), UL(m_Dict->ul(MDD_PictureDataDef)), - edit_rate, derive_timecode_rate_from_edit_rate(edit_rate)); + edit_rate); if ( KM_SUCCESS(result) ) { diff --git a/src/AS_02_TimedText.cpp b/src/AS_02_TimedText.cpp index 6868395..09f7103 100644 --- a/src/AS_02_TimedText.cpp +++ b/src/AS_02_TimedText.cpp @@ -496,7 +496,7 @@ AS_02::TimedText::MXFWriter::h__Writer::SetSourceStream(ASDCP::TimedText::TimedT m_EditRate = TDesc.EditRate; result = WriteAS02Header(TIMED_TEXT_PACKAGE_LABEL, UL(m_Dict->ul(MDD_TimedTextWrappingClip)), "Data Track", UL(m_EssenceUL), UL(m_Dict->ul(MDD_DataDataDef)), - TDesc.EditRate, derive_timecode_rate_from_edit_rate(TDesc.EditRate)); + TDesc.EditRate); } return result; diff --git a/src/AS_02_internal.h b/src/AS_02_internal.h index 4be764d..b5d8282 100644 --- a/src/AS_02_internal.h +++ b/src/AS_02_internal.h @@ -165,7 +165,7 @@ namespace AS_02 Result_t WriteAS02Header(const std::string& PackageLabel, const ASDCP::UL& WrappingUL, const std::string& TrackName, const ASDCP::UL& EssenceUL, const ASDCP::UL& DataDefinition, const ASDCP::Rational& EditRate, - const ui32_t& TCFrameRate) + const std::vector<ASDCP::UL>* conformsToSpecifications = NULL) { if ( EditRate.Numerator == 0 || EditRate.Denominator == 0 ) { @@ -173,9 +173,9 @@ namespace AS_02 return RESULT_PARAM; } - InitHeader(MXFVersion_2011); + InitHeader(MXFVersion_2011, conformsToSpecifications); - AddSourceClip(EditRate, EditRate/*TODO: for a moment*/, TCFrameRate, TrackName, EssenceUL, DataDefinition, PackageLabel); + AddSourceClip(EditRate, EditRate/*TODO: for a moment*/, 0 /*no timecode track*/, TrackName, EssenceUL, DataDefinition, PackageLabel); AddEssenceDescriptor(WrappingUL); this->m_IndexWriter.SetPrimerLookup(&this->m_HeaderPart.m_Primer); diff --git a/src/AS_DCP.h b/src/AS_DCP.h index 74991f6..fa8b00a 100755 --- a/src/AS_DCP.h +++ b/src/AS_DCP.h @@ -223,6 +223,7 @@ namespace ASDCP { ESS_AS02_TIMED_TEXT, // the file contains a TTML document and zero or more resources ESS_AS02_ISXD, // the file contains an ISXD document stream (per SMPTE RDD 47) ESS_AS02_ACES, // the file contains two or more ACES codestreams (per SMPTE ST 2067-50) + ESS_AS02_IAB, // the file contains an IAB stream (per SMPTE ST 2067-201) ESS_MAX }; @@ -507,6 +508,11 @@ namespace ASDCP { // point to a readable area of memory that is at least HMAC_SIZE bytes in length. // Returns error if the buf argument is NULL or if the values do ot match. Result_t TestHMACValue(const byte_t* buf) const; + + // Writes MIC key to given buffer. buf must point to a writable area of + // memory that is at least KeyLen bytes in length. Returns error if the + // buf argument is NULL. + Result_t GetMICKey(byte_t* buf) const; }; //--------------------------------------------------------------------------------- @@ -1455,7 +1461,7 @@ namespace ASDCP { byte_t ResourceID[UUIDlen]; MIMEType_t Type; - TimedTextResourceDescriptor() : Type(MT_BIN) {} + TimedTextResourceDescriptor() : Type(MT_BIN) { memset(ResourceID, 0, UUIDlen); } }; typedef std::list<TimedTextResourceDescriptor> ResourceList_t; @@ -1469,7 +1475,7 @@ namespace ASDCP { std::string EncodingName; ResourceList_t ResourceList; - TimedTextDescriptor() : ContainerDuration(0), EncodingName("UTF-8") {} // D-Cinema format is always UTF-8 + TimedTextDescriptor() : ContainerDuration(0), EncodingName("UTF-8") { memset(AssetID, 0, UUIDlen); } // D-Cinema format is always UTF-8 }; // Print debugging information to std::ostream diff --git a/src/AS_DCP_AES.cpp b/src/AS_DCP_AES.cpp index 379e8ab..6409f5b 100755 --- a/src/AS_DCP_AES.cpp +++ b/src/AS_DCP_AES.cpp @@ -349,6 +349,13 @@ public: SHA1_Final(m_SHAValue, &SHA); m_Final = true; } + + // + void + GetMICKey(byte_t* buf) const + { + memcpy(buf, m_key, KeyLen); + } }; @@ -444,6 +451,20 @@ HMACContext::TestHMACValue(const byte_t* buf) const } +// +Result_t +HMACContext::GetMICKey(byte_t* buf) const +{ + KM_TEST_NULL_L(buf); + + if ( m_Context.empty() ) + return RESULT_INIT; + + m_Context->GetMICKey(buf); + return RESULT_OK; +} + + // // end AS_DCP_AES.cpp diff --git a/src/AS_DCP_JP2K.cpp b/src/AS_DCP_JP2K.cpp index 129f53d..d5fddaa 100755 --- a/src/AS_DCP_JP2K.cpp +++ b/src/AS_DCP_JP2K.cpp @@ -319,7 +319,7 @@ ASDCP::JP2K_PDesc_to_MD(const JP2K::PictureDescriptor& PDesc, EssenceSubDescriptor.PictureComponentSizing.set_has_value(); ui32_t precinct_set_size = 0; - for ( ui32_t i = 0; PDesc.CodingStyleDefault.SPcod.PrecinctSize[i] != 0 && i < MaxPrecincts; ++i ) + for ( ui32_t i = 0; i < MaxPrecincts && PDesc.CodingStyleDefault.SPcod.PrecinctSize[i] != 0; ++i ) precinct_set_size++; ui32_t csd_size = sizeof(CodingStyleDefault_t) - MaxPrecincts + precinct_set_size; diff --git a/src/AS_DCP_MXF.cpp b/src/AS_DCP_MXF.cpp index d3606fe..c478f2f 100755 --- a/src/AS_DCP_MXF.cpp +++ b/src/AS_DCP_MXF.cpp @@ -277,6 +277,10 @@ ASDCP::EssenceType(const std::string& filename, EssenceType_t& type) { type = ESS_AS02_ACES; } + else if ( ASDCP_SUCCESS(TestHeader.GetMDObjectByType(OBJ_TYPE_ARGS(IABEssenceDescriptor))) ) + { + type = ESS_AS02_IAB; + } } } else diff --git a/src/AS_DCP_internal.h b/src/AS_DCP_internal.h index 57ed006..7162368 100755 --- a/src/AS_DCP_internal.h +++ b/src/AS_DCP_internal.h @@ -624,7 +624,7 @@ namespace ASDCP const MXF::RIP& GetRIP() const { return m_RIP; } - void InitHeader(const MXFVersion& mxf_ver) + void InitHeader(const MXFVersion& mxf_ver, const std::vector<ASDCP::UL>* conformsToSpecifications = NULL) { assert(m_Dict); assert(m_EssenceDescriptor); @@ -633,6 +633,18 @@ namespace ASDCP m_HeaderPart.m_Preface = new Preface(m_Dict); m_HeaderPart.AddChildObject(m_HeaderPart.m_Preface); + // add conformsToSpecifications, if it exists + + if (conformsToSpecifications && conformsToSpecifications->size() > 0) { + + m_HeaderPart.m_Preface->ConformsToSpecifications.set_has_value(); + + m_HeaderPart.m_Preface->ConformsToSpecifications.get().insert( + conformsToSpecifications->begin(), + conformsToSpecifications->end() + ); + } + // Set the Operational Pattern label -- we're just starting and have no RIP or index, // so we tell the world by using OP1a m_HeaderPart.m_Preface->OperationalPattern = UL(m_Dict->ul(MDD_OP1a)); @@ -675,7 +687,7 @@ namespace ASDCP // void AddSourceClip(const MXF::Rational& clip_edit_rate, - const MXF::Rational& tc_edit_rate, ui32_t TCFrameRate, + const MXF::Rational& tc_edit_rate, ui32_t tc_frame_rate, const std::string& TrackName, const UL& EssenceUL, const UL& DataDefinition, const std::string& PackageLabel) { @@ -706,14 +718,17 @@ namespace ASDCP m_HeaderPart.AddChildObject(m_MaterialPackage); m_ContentStorage->Packages.push_back(m_MaterialPackage->InstanceUID); - TrackSet<TimecodeComponent> MPTCTrack = - CreateTimecodeTrack<MaterialPackage>(m_HeaderPart, *m_MaterialPackage, - tc_edit_rate, TCFrameRate, 0, m_Dict); - - MPTCTrack.Sequence->Duration.set_has_value(); - m_DurationUpdateList.push_back(&(MPTCTrack.Sequence->Duration.get())); - MPTCTrack.Clip->Duration.set_has_value(); - m_DurationUpdateList.push_back(&(MPTCTrack.Clip->Duration.get())); + if ( tc_frame_rate ) + { + TrackSet<TimecodeComponent> MPTCTrack = + CreateTimecodeTrack<MaterialPackage>(m_HeaderPart, *m_MaterialPackage, + tc_edit_rate, tc_frame_rate, 0, m_Dict); + + MPTCTrack.Sequence->Duration.set_has_value(); + m_DurationUpdateList.push_back(&(MPTCTrack.Sequence->Duration.get())); + MPTCTrack.Clip->Duration.set_has_value(); + m_DurationUpdateList.push_back(&(MPTCTrack.Clip->Duration.get())); + } TrackSet<SourceClip> MPTrack = CreateTrackAndSequence<MaterialPackage, SourceClip>(m_HeaderPart, *m_MaterialPackage, @@ -744,14 +759,17 @@ namespace ASDCP m_HeaderPart.AddChildObject(m_FilePackage); m_ContentStorage->Packages.push_back(m_FilePackage->InstanceUID); - TrackSet<TimecodeComponent> FPTCTrack = - CreateTimecodeTrack<SourcePackage>(m_HeaderPart, *m_FilePackage, - tc_edit_rate, TCFrameRate, 0, m_Dict); - - FPTCTrack.Sequence->Duration.set_has_value(); - m_DurationUpdateList.push_back(&(FPTCTrack.Sequence->Duration.get())); - FPTCTrack.Clip->Duration.set_has_value(); - m_DurationUpdateList.push_back(&(FPTCTrack.Clip->Duration.get())); + if ( tc_frame_rate ) + { + TrackSet<TimecodeComponent> FPTCTrack = + CreateTimecodeTrack<SourcePackage>(m_HeaderPart, *m_FilePackage, + tc_edit_rate, tc_frame_rate, 0, m_Dict); + + FPTCTrack.Sequence->Duration.set_has_value(); + m_DurationUpdateList.push_back(&(FPTCTrack.Sequence->Duration.get())); + FPTCTrack.Clip->Duration.set_has_value(); + m_DurationUpdateList.push_back(&(FPTCTrack.Clip->Duration.get())); + } TrackSet<SourceClip> FPTrack = CreateTrackAndSequence<SourcePackage, SourceClip>(m_HeaderPart, *m_FilePackage, @@ -875,6 +893,13 @@ namespace ASDCP //------------------------------------------------------------------------------------------ // +// state machine for mxf reader + enum ReaderState_t { + ST_READER_BEGIN, // waiting for Open() + ST_READER_READY, // ready to read frames + ST_READER_RUNNING, // one or more frames read + }; + // class h__ASDCPReader : public MXF::TrackFileReader<OP1aHeader, OPAtomIndexFooter> { diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 842f44a..2787e46 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -37,13 +37,13 @@ set(asdcp_src ${asdcp_src} Wav.h WavFileWriter.h MXF.h Metadata.h JP2K.h AS_DCP. # ----------as02----------
# source
-set(as02_src h__02_Reader.cpp h__02_Writer.cpp AS_02_ISXD.cpp AS_02_JP2K.cpp AS_02_PCM.cpp ST2052_TextParser.cpp AS_02_TimedText.cpp AS_02_ACES.cpp ACES_Codestream_Parser.cpp ACES_Sequence_Parser.cpp ACES.cpp)
+set(as02_src h__02_Reader.cpp h__02_Writer.cpp AS_02_ISXD.cpp AS_02_JP2K.cpp AS_02_PCM.cpp ST2052_TextParser.cpp AS_02_TimedText.cpp AS_02_ACES.cpp ACES_Codestream_Parser.cpp ACES_Sequence_Parser.cpp ACES.cpp AS_02_IAB.cpp)
# header for deployment (install target)
-set(as02_deploy_header AS_02.h Metadata.h MXF.h MXFTypes.h KLV.h MDD.h AS_02_ACES.h ACES.h)
+set(as02_deploy_header AS_02.h Metadata.h MXF.h MXFTypes.h KLV.h MDD.h AS_02_ACES.h ACES.h AS_02_IAB.h AS_02_internal.h)
# header
-set(as02_src ${as02_src} AS_02.h AS_02_internal.h AS_02_ACES.h ACES.h)
+set(as02_src ${as02_src} AS_02.h AS_02_internal.h AS_02_ACES.h ACES.h AS_02_IAB.h)
include_directories("${PROJECT_SOURCE_DIR}/src" "${OpenSSLLib_include_DIR}" "${XercescppLib_include_DIR}")
diff --git a/src/KM_fileio.cpp b/src/KM_fileio.cpp index 27a5bfa..4ee73c2 100644 --- a/src/KM_fileio.cpp +++ b/src/KM_fileio.cpp @@ -719,7 +719,9 @@ Kumu::fsize_t Kumu::FileReader::Size() const { #ifdef KM_WIN32 - return FileSize(m_Filename.c_str()); + LARGE_INTEGER size; + GetFileSizeEx(m_Handle, &size); + return size.QuadPart; #else fstat_t info; diff --git a/src/MDD.cpp b/src/MDD.cpp index 6db1bec..772b96d 100644 --- a/src/MDD.cpp +++ b/src/MDD.cpp @@ -1639,6 +1639,27 @@ static const ASDCP::MDDEntry s_MDD_Table[] = { { { 0x06, 0x0e, 0x2b, 0x34, 01, 0x01, 0x01, 0x0e, 06, 0x01, 0x01, 0x02, 06, 0x00, 0x00, 0x00 }, {0}, false, "TimedTextDescriptor_ZPositionInUse" }, // 528 + { { 0x06, 0x0e, 0x2b, 0x34, 0x02, 0x53, 0x01, 0x01, // 529 + 0x0d, 0x01, 0x01, 0x01, 0x01, 0x01, 0x7b, 0x00 }, + {0}, false, "IABEssenceDescriptor" }, + { { 0x06, 0x0e, 0x2b, 0x34, 0x02, 0x53, 0x01, 0x01, // 530 + 0x0d, 0x01, 0x01, 0x01, 0x01, 0x01, 0x7c, 0x00 }, + {0}, false, "IABSoundfieldLabelSubDescriptor" }, + { { 0x06, 0x0e, 0x2b, 0x34, 0x04, 0x01, 0x01, 0x0d, // 531 + 0x01, 0x01, 0x02, 0x01, 0x02, 0x00, 0x00, 0x00 }, + {0}, false, "IMF_IABTrackFileLevel0" }, + { { 0x06, 0x0e, 0x2b, 0x34, 0x04, 0x01, 0x01, 0x0d, // 532 + 0x0d, 0x01, 0x03, 0x01, 0x02, 0x1d, 0x01, 0x01 }, + {0}, false, "IMF_IABEssenceClipWrappedContainer" }, + { { 0x06, 0x0e, 0x2b, 0x34, 0x04, 0x01, 0x01, 0x0d, // 533 + 0x03, 0x02, 0x02, 0x21, 0x00, 0x00, 0x00, 0x00 }, + {0}, false, "IABSoundfield" }, + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x02, 0x01, 0x01, // 534 + 0x0d, 0x01, 0x03, 0x01, 0x16, 0x7f, 0x0d, 0x7f }, + {0}, false, "IMF_IABEssenceClipWrappedElement" }, + { { 0x06, 0x0e, 0x2b, 0x34, 0x04, 0x01, 0x01, 0x05, // 535 + 0x0e, 0x09, 0x06, 0x04, 0x00, 0x00, 0x00, 0x00 }, + {0}, false, "ImmersiveAudioCoding" }, { {0}, {0}, false, 0 }, }; @@ -564,6 +564,13 @@ namespace ASDCP { MDD_TimedTextDescriptor_DisplayType, // 526 MDD_TimedTextDescriptor_IntrinsicPictureResolution, // 527 MDD_TimedTextDescriptor_ZPositionInUse, // 528 + MDD_IABEssenceDescriptor, // 529 + MDD_IABSoundfieldLabelSubDescriptor, // 530 + MDD_IMF_IABTrackFileLevel0, // 531 + MDD_IMF_IABEssenceClipWrappedContainer, // 532 + MDD_IABSoundfield, // 533 + MDD_IMF_IABEssenceClipWrappedElement, // 534 + MDD_ImmersiveAudioCoding, // 535 MDD_Max }; // enum MDD_t diff --git a/src/MXF.cpp b/src/MXF.cpp index 6d7cdd8..c5cc07e 100755 --- a/src/MXF.cpp +++ b/src/MXF.cpp @@ -1742,6 +1742,12 @@ ASDCP::MXF::decode_mca_string(const std::string& s, const mca_label_map_t& label return false; } + if ( ! ul_is_an_mca_channel(i->second.ul) ) + { + DefaultLogSink().Error("Not a channel symbol: '%s'\n", symbol_buf.c_str()); + return false; + } + ASDCP::MXF::AudioChannelLabelSubDescriptor *channel_descr = new ASDCP::MXF::AudioChannelLabelSubDescriptor(dict); GenRandomValue(channel_descr->MCALinkID); diff --git a/src/Makefile.am b/src/Makefile.am index 74c0b11..7768b39 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -143,6 +143,7 @@ libas02_la_SOURCES = \ AS_02_internal.h \ ACES.h \ AS_02_ACES.h \ + AS_02_IAB.h \ h__02_Reader.cpp \ h__02_Writer.cpp \ AS_02_JP2K.cpp \ @@ -151,6 +152,7 @@ libas02_la_SOURCES = \ ST2052_TextParser.cpp \ AS_02_TimedText.cpp \ ACES.cpp \ + AS_02_IAB.cpp \ ACES_Codestream_Parser.cpp \ ACES_Sequence_Parser.cpp \ AS_02_ACES.cpp diff --git a/src/Metadata.cpp b/src/Metadata.cpp index ef6e17e..fd4c115 100755 --- a/src/Metadata.cpp +++ b/src/Metadata.cpp @@ -87,6 +87,8 @@ static InterchangeObject* GenericStreamTextBasedSet_Factory(const Dictionary*& D static InterchangeObject* ISXDDataEssenceDescriptor_Factory(const Dictionary*& Dict) { return new ISXDDataEssenceDescriptor(Dict); } static InterchangeObject* PHDRMetadataTrackSubDescriptor_Factory(const Dictionary*& Dict) { return new PHDRMetadataTrackSubDescriptor(Dict); } static InterchangeObject* PIMFDynamicMetadataDescriptor_Factory(const Dictionary*& Dict) { return new PIMFDynamicMetadataDescriptor(Dict); } +static InterchangeObject* IABEssenceDescriptor_Factory(const Dictionary*& Dict) { return new IABEssenceDescriptor(Dict); } +static InterchangeObject* IABSoundfieldLabelSubDescriptor_Factory(const Dictionary*& Dict) { return new IABSoundfieldLabelSubDescriptor(Dict); } void @@ -140,6 +142,8 @@ ASDCP::MXF::Metadata_InitTypes(const Dictionary*& Dict) SetObjectFactory(Dict->ul(MDD_ISXDDataEssenceDescriptor), ISXDDataEssenceDescriptor_Factory); SetObjectFactory(Dict->ul(MDD_PHDRMetadataTrackSubDescriptor), PHDRMetadataTrackSubDescriptor_Factory); SetObjectFactory(Dict->ul(MDD_PIMFDynamicMetadataDescriptor), PIMFDynamicMetadataDescriptor_Factory); + SetObjectFactory(Dict->ul(MDD_IABEssenceDescriptor), IABEssenceDescriptor_Factory); + SetObjectFactory(Dict->ul(MDD_IABSoundfieldLabelSubDescriptor), IABSoundfieldLabelSubDescriptor_Factory); } //------------------------------------------------------------------------------------------ @@ -4851,6 +4855,148 @@ PIMFDynamicMetadataDescriptor::WriteToBuffer(ASDCP::FrameBuffer& Buffer) return InterchangeObject::WriteToBuffer(Buffer); } +//------------------------------------------------------------------------------------------ +// IABEssenceDescriptor + +// + +IABEssenceDescriptor::IABEssenceDescriptor(const Dictionary*& d) : GenericSoundEssenceDescriptor(d), m_Dict(d) +{ + assert(m_Dict); + m_UL = m_Dict->ul(MDD_IABEssenceDescriptor); +} + +IABEssenceDescriptor::IABEssenceDescriptor(const IABEssenceDescriptor& rhs) : GenericSoundEssenceDescriptor(rhs.m_Dict), m_Dict(rhs.m_Dict) +{ + assert(m_Dict); + m_UL = m_Dict->ul(MDD_IABEssenceDescriptor); + Copy(rhs); +} + + +// +ASDCP::Result_t +IABEssenceDescriptor::InitFromTLVSet(TLVReader& TLVSet) +{ + assert(m_Dict); + Result_t result = GenericSoundEssenceDescriptor::InitFromTLVSet(TLVSet); + return result; +} + +// +ASDCP::Result_t +IABEssenceDescriptor::WriteToTLVSet(TLVWriter& TLVSet) +{ + assert(m_Dict); + Result_t result = GenericSoundEssenceDescriptor::WriteToTLVSet(TLVSet); + return result; +} + +// +void +IABEssenceDescriptor::Copy(const IABEssenceDescriptor& rhs) +{ + GenericSoundEssenceDescriptor::Copy(rhs); +} + +// +void +IABEssenceDescriptor::Dump(FILE* stream) +{ + char identbuf[IdentBufferLen]; + *identbuf = 0; + + if ( stream == 0 ) + stream = stderr; + + GenericSoundEssenceDescriptor::Dump(stream); +} + +// +ASDCP::Result_t +IABEssenceDescriptor::InitFromBuffer(const byte_t* p, ui32_t l) +{ + return InterchangeObject::InitFromBuffer(p, l); +} + +// +ASDCP::Result_t +IABEssenceDescriptor::WriteToBuffer(ASDCP::FrameBuffer& Buffer) +{ + return InterchangeObject::WriteToBuffer(Buffer); +} + +//------------------------------------------------------------------------------------------ +// IABSoundfieldLabelSubDescriptor + +// + +IABSoundfieldLabelSubDescriptor::IABSoundfieldLabelSubDescriptor(const Dictionary*& d) : MCALabelSubDescriptor(d), m_Dict(d) +{ + assert(m_Dict); + m_UL = m_Dict->ul(MDD_IABSoundfieldLabelSubDescriptor); +} + +IABSoundfieldLabelSubDescriptor::IABSoundfieldLabelSubDescriptor(const IABSoundfieldLabelSubDescriptor& rhs) : MCALabelSubDescriptor(rhs.m_Dict), m_Dict(rhs.m_Dict) +{ + assert(m_Dict); + m_UL = m_Dict->ul(MDD_IABSoundfieldLabelSubDescriptor); + Copy(rhs); +} + + +// +ASDCP::Result_t +IABSoundfieldLabelSubDescriptor::InitFromTLVSet(TLVReader& TLVSet) +{ + assert(m_Dict); + Result_t result = MCALabelSubDescriptor::InitFromTLVSet(TLVSet); + return result; +} + +// +ASDCP::Result_t +IABSoundfieldLabelSubDescriptor::WriteToTLVSet(TLVWriter& TLVSet) +{ + assert(m_Dict); + Result_t result = MCALabelSubDescriptor::WriteToTLVSet(TLVSet); + return result; +} + +// +void +IABSoundfieldLabelSubDescriptor::Copy(const IABSoundfieldLabelSubDescriptor& rhs) +{ + MCALabelSubDescriptor::Copy(rhs); +} + +// +void +IABSoundfieldLabelSubDescriptor::Dump(FILE* stream) +{ + char identbuf[IdentBufferLen]; + *identbuf = 0; + + if ( stream == 0 ) + stream = stderr; + + MCALabelSubDescriptor::Dump(stream); +} + +// +ASDCP::Result_t +IABSoundfieldLabelSubDescriptor::InitFromBuffer(const byte_t* p, ui32_t l) +{ + return InterchangeObject::InitFromBuffer(p, l); +} + +// +ASDCP::Result_t +IABSoundfieldLabelSubDescriptor::WriteToBuffer(ASDCP::FrameBuffer& Buffer) +{ + return InterchangeObject::WriteToBuffer(Buffer); +} + // // end Metadata.cpp // diff --git a/src/Metadata.h b/src/Metadata.h index c624784..ab11034 100755 --- a/src/Metadata.h +++ b/src/Metadata.h @@ -1297,6 +1297,50 @@ namespace ASDCP virtual Result_t WriteToBuffer(ASDCP::FrameBuffer&); }; + // + class IABEssenceDescriptor : public GenericSoundEssenceDescriptor + { + IABEssenceDescriptor(); + + public: + const Dictionary*& m_Dict; + + IABEssenceDescriptor(const Dictionary*& d); + IABEssenceDescriptor(const IABEssenceDescriptor& rhs); + virtual ~IABEssenceDescriptor() {} + + const IABEssenceDescriptor& operator=(const IABEssenceDescriptor& rhs) { Copy(rhs); return *this; } + virtual void Copy(const IABEssenceDescriptor& rhs); + virtual const char* HasName() { return "IABEssenceDescriptor"; } + 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 IABSoundfieldLabelSubDescriptor : public MCALabelSubDescriptor + { + IABSoundfieldLabelSubDescriptor(); + + public: + const Dictionary*& m_Dict; + + IABSoundfieldLabelSubDescriptor(const Dictionary*& d); + IABSoundfieldLabelSubDescriptor(const IABSoundfieldLabelSubDescriptor& rhs); + virtual ~IABSoundfieldLabelSubDescriptor() {} + + const IABSoundfieldLabelSubDescriptor& operator=(const IABSoundfieldLabelSubDescriptor& rhs) { Copy(rhs); return *this; } + virtual void Copy(const IABSoundfieldLabelSubDescriptor& rhs); + virtual const char* HasName() { return "IABSoundfieldLabelSubDescriptor"; } + 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/h__02_Writer.cpp b/src/h__02_Writer.cpp index 9b1c6c6..d7cfaa3 100644 --- a/src/h__02_Writer.cpp +++ b/src/h__02_Writer.cpp @@ -161,7 +161,17 @@ AS_02::MXF::AS02IndexWriterVBR::PushIndexEntry(const IndexTableSegment::IndexEnt m_CurrentSegment->DeltaEntryArray.push_back(IndexTableSegment::DeltaEntry()); m_CurrentSegment->IndexEditRate = m_EditRate; m_CurrentSegment->IndexStartPosition = 0; - } + } else if (m_CurrentSegment->IndexEntryArray.size() >= CBRIndexEntriesPerSegment) { // no, this one is full, start another + m_CurrentSegment->IndexDuration = m_CurrentSegment->IndexEntryArray.size(); + ui64_t StartPosition = m_CurrentSegment->IndexStartPosition + m_CurrentSegment->IndexDuration; + + m_CurrentSegment = new IndexTableSegment(m_Dict); + assert(m_CurrentSegment); + AddChildObject(m_CurrentSegment); + m_CurrentSegment->DeltaEntryArray.push_back(IndexTableSegment::DeltaEntry()); + m_CurrentSegment->IndexEditRate = m_EditRate; + m_CurrentSegment->IndexStartPosition = StartPosition; + } m_CurrentSegment->IndexEntryArray.push_back(Entry); } diff --git a/src/h__Writer.cpp b/src/h__Writer.cpp index fc7f060..0c56b4c 100755 --- a/src/h__Writer.cpp +++ b/src/h__Writer.cpp @@ -374,6 +374,15 @@ ASDCP::Write_EKLV_Packet(Kumu::FileWriter& File, const ASDCP::Dictionary& Dict, byte_t overhead[128]; Kumu::MemIOWriter Overhead(overhead, 128); + // We declare HMACOverhead and its buffer in the outer scope, even though it is not used on + // unencrypted content: the reason is that File.Writev(const byte_t* buf, ui32_t buf_len) doesn't + // write data right away but saves a pointer on the buffer. And we write all the buffers at the end + // when calling File.writev(). + // Declaring the buffer variable in an inner scope means the buffer will go out of scope + // before the data it contains has been actually written, which means its content could be + // overwritten/get corrupted. + byte_t hmoverhead[512]; + Kumu::MemIOWriter HMACOverhead(hmoverhead, 512); if ( FrameBuf.Size() == 0 ) { @@ -455,9 +464,6 @@ ASDCP::Write_EKLV_Packet(Kumu::FileWriter& File, const ASDCP::Dictionary& Dict, { StreamOffset += CtFrameBuf.Size(); - byte_t hmoverhead[512]; - Kumu::MemIOWriter HMACOverhead(hmoverhead, 512); - // write the HMAC if ( Info.UsesHMAC ) { |
