From dd323ce3297bb74dae1cd044206b8733b4757efa Mon Sep 17 00:00:00 2001 From: jhurst Date: Thu, 9 Apr 2020 12:28:12 -0700 Subject: Added IAB in IMF ULs and Sets --- src/Metadata.cpp | 146 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 146 insertions(+) (limited to 'src/Metadata.cpp') diff --git a/src/Metadata.cpp b/src/Metadata.cpp index adaac11..4ab2044 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) : FileDescriptor(d), m_Dict(d) +{ + assert(m_Dict); + m_UL = m_Dict->ul(MDD_IABEssenceDescriptor); +} + +IABEssenceDescriptor::IABEssenceDescriptor(const IABEssenceDescriptor& rhs) : FileDescriptor(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 = FileDescriptor::InitFromTLVSet(TLVSet); + return result; +} + +// +ASDCP::Result_t +IABEssenceDescriptor::WriteToTLVSet(TLVWriter& TLVSet) +{ + assert(m_Dict); + Result_t result = FileDescriptor::WriteToTLVSet(TLVSet); + return result; +} + +// +void +IABEssenceDescriptor::Copy(const IABEssenceDescriptor& rhs) +{ + FileDescriptor::Copy(rhs); +} + +// +void +IABEssenceDescriptor::Dump(FILE* stream) +{ + char identbuf[IdentBufferLen]; + *identbuf = 0; + + if ( stream == 0 ) + stream = stderr; + + FileDescriptor::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 // -- cgit v1.2.3 From b15f21a0c760bc8b8b28cd2a04491f4963ac0549 Mon Sep 17 00:00:00 2001 From: Pierre-Anthony Lemieux Date: Sun, 12 Apr 2020 13:28:38 -0700 Subject: Add support for IAB Track File reading and writing --- src/AS_02_IAB.cpp | 617 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/AS_02_IAB.h | 239 +++++++++++++++++++ src/AS_02_internal.h | 5 +- src/AS_DCP_internal.h | 19 +- src/CMakeLists.txt | 6 +- src/MDD.cpp | 2 +- src/Makefile.am | 2 + src/Metadata.cpp | 12 +- 8 files changed, 889 insertions(+), 13 deletions(-) create mode 100644 src/AS_02_IAB.cpp create mode 100644 src/AS_02_IAB.h (limited to 'src/Metadata.cpp') diff --git a/src/AS_02_IAB.cpp b/src/AS_02_IAB.cpp new file mode 100644 index 0000000..5fc2ea1 --- /dev/null +++ b/src/AS_02_IAB.cpp @@ -0,0 +1,617 @@ +/* +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 +#include +#include + +namespace Kumu { + class RuntimeError : public std::runtime_error { + Kumu::Result_t m_Result; + RuntimeError(); /* deleted constructor */ + public: + RuntimeError(const Kumu::Result_t& result) : std::runtime_error(result.Message()), m_Result(result) {} + Kumu::Result_t GetResult() { return this->m_Result; } + }; +} + +//------------------------------------------------------------------------------------------ + + +AS_02::IAB::MXFWriter::MXFWriter() : m_ClipStart(0), m_State(WriterState_t::ST_BEGIN) { +} + +AS_02::IAB::MXFWriter::~MXFWriter() {} + +const ASDCP::MXF::OP1aHeader& +AS_02::IAB::MXFWriter::OP1aHeader() const { + if (this->m_State == WriterState_t::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 == WriterState_t::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& conformsToSpecs, + const ASDCP::Rational& edit_rate, + const ASDCP::Rational& sample_rate) { + + /* are we already running */ + + if (this->m_State != WriterState_t::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 */ + + std::array element_ul_bytes; + + const ASDCP::MDDEntry& element_ul_entry = this->m_Writer->m_Dict->Type(MDD_IMF_IABEssenceClipWrappedElement); + + std::copy(std::begin(element_ul_entry.ul), std::end(element_ul_entry.ul), std::begin(element_ul_bytes)); + + /* only a single track */ + + element_ul_bytes[15] = 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.data()), + UL(this->m_Writer->m_Dict->ul(MDD_SoundDataDef)), + edit_rate, + derive_timecode_rate_from_edit_rate(edit_rate), + &conformsToSpecs + ); + + if (result.Failure()) { + throw Kumu::RuntimeError(result); + } + + /* start the clip */ + + this->m_ClipStart = this->m_Writer->m_File.Tell(); + + byte_t clip_buffer[24] = { 0 }; + + memcpy(clip_buffer, this->m_Writer->m_Dict->ul(MDD_IMF_IABEssenceClipWrappedElement), 16); + + if (!Kumu::write_BER(clip_buffer + 16, 0, 8)) { + throw Kumu::RuntimeError(Kumu::RESULT_FAIL); + } + + this->m_Writer->m_StreamOffset = 24; + + result = this->m_Writer->m_File.Write(clip_buffer, 24); + + if (result.Failure()) { + throw Kumu::RuntimeError(result); + } + + this->m_State = WriterState_t::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 == WriterState_t::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 = WriterState_t::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 == WriterState_t::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 + 16); + + byte_t clip_buffer[8] = { 0 }; + + ui64_t size = static_cast(this->m_Writer->m_StreamOffset); + + bool check = Kumu::write_BER(clip_buffer, size, 8); + + if (!check) { + throw Kumu::RuntimeError(Kumu::RESULT_FAIL); + } + + result = this->m_Writer->m_File.Write(clip_buffer, 8); + + 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 = WriterState_t::ST_BEGIN; +} + + +//------------------------------------------------------------------------------------------ + + +AS_02::IAB::MXFReader::MXFReader() : m_State(ReaderState_t::ST_BEGIN) {} + +AS_02::IAB::MXFReader::~MXFReader() {} + +const ASDCP::MXF::OP1aHeader& +AS_02::IAB::MXFReader::OP1aHeader() const { + if (this->m_State == ReaderState_t::ST_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 == ReaderState_t::ST_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 != ReaderState_t::ST_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 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 = ReaderState_t::ST_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 == ReaderState_t::ST_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 == ReaderState_t::ST_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 == ReaderState_t::ST_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(this->m_CurrentFrameBuffer.size(), &this->m_CurrentFrameBuffer[0]); + + this->m_State = ReaderState_t::ST_RUNNING; + + return result; +} + +Result_t +AS_02::IAB::MXFReader::FillWriterInfo(WriterInfo& Info) const { + /* are we already running */ + + if (this->m_State == ReaderState_t::ST_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 != ReaderState_t::ST_BEGIN) { + this->m_Reader->m_HeaderPart.Dump(stream); + } +} + + +void +AS_02::IAB::MXFReader::DumpIndex(FILE* stream) const { + if (this->m_State != ReaderState_t::ST_BEGIN) { + this->m_Reader->m_IndexAccess.Dump(stream); + } +} + +void +AS_02::IAB::MXFReader::Reset() { + this->m_Reader.set(NULL); + this->m_State = ReaderState_t::ST_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..aade55d --- /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 h__Writer; + + ASDCP::mem_ptr 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& 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 m_Reader; + + i64_t m_CurrentFrameIndex; + std::vector m_CurrentFrameBuffer; + ReaderState_t m_State; + + void Reset(); + + ASDCP_NO_COPY_CONSTRUCT(MXFReader); + + public: + + /* typedefs*/ + + typedef std::pair 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 const 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_internal.h b/src/AS_02_internal.h index 4be764d..cab98af 100644 --- a/src/AS_02_internal.h +++ b/src/AS_02_internal.h @@ -165,7 +165,8 @@ 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 ui32_t& TCFrameRate, + const std::vector* conformsToSpecifications = NULL) { if ( EditRate.Numerator == 0 || EditRate.Denominator == 0 ) { @@ -173,7 +174,7 @@ 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); AddEssenceDescriptor(WrappingUL); diff --git a/src/AS_DCP_internal.h b/src/AS_DCP_internal.h index 57ed006..b7712b1 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* conformsToSpecifications = NULL) { assert(m_Dict); assert(m_EssenceDescriptor); @@ -633,6 +633,16 @@ 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.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)); @@ -875,6 +885,13 @@ namespace ASDCP //------------------------------------------------------------------------------------------ // +// state machine for mxf reader + enum ReaderState_t { + ST_BEGIN, // waiting for Open() + ST_READY, // ready to read frames + ST_RUNNING, // one or more frames read + }; + // class h__ASDCPReader : public MXF::TrackFileReader { diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 842f44a..f501d1c 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) # 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/MDD.cpp b/src/MDD.cpp index 708e5f7..772b96d 100644 --- a/src/MDD.cpp +++ b/src/MDD.cpp @@ -1642,7 +1642,7 @@ static const ASDCP::MDDEntry s_MDD_Table[] = { { { 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, 0x06, 0x01, 0x01, // 530 + { { 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 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 4ab2044..9ca3d66 100755 --- a/src/Metadata.cpp +++ b/src/Metadata.cpp @@ -4860,13 +4860,13 @@ PIMFDynamicMetadataDescriptor::WriteToBuffer(ASDCP::FrameBuffer& Buffer) // -IABEssenceDescriptor::IABEssenceDescriptor(const Dictionary*& d) : FileDescriptor(d), m_Dict(d) +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) : FileDescriptor(rhs.m_Dict), m_Dict(rhs.m_Dict) +IABEssenceDescriptor::IABEssenceDescriptor(const IABEssenceDescriptor& rhs) : GenericSoundEssenceDescriptor(rhs.m_Dict), m_Dict(rhs.m_Dict) { assert(m_Dict); m_UL = m_Dict->ul(MDD_IABEssenceDescriptor); @@ -4879,7 +4879,7 @@ ASDCP::Result_t IABEssenceDescriptor::InitFromTLVSet(TLVReader& TLVSet) { assert(m_Dict); - Result_t result = FileDescriptor::InitFromTLVSet(TLVSet); + Result_t result = GenericSoundEssenceDescriptor::InitFromTLVSet(TLVSet); return result; } @@ -4888,7 +4888,7 @@ ASDCP::Result_t IABEssenceDescriptor::WriteToTLVSet(TLVWriter& TLVSet) { assert(m_Dict); - Result_t result = FileDescriptor::WriteToTLVSet(TLVSet); + Result_t result = GenericSoundEssenceDescriptor::WriteToTLVSet(TLVSet); return result; } @@ -4896,7 +4896,7 @@ IABEssenceDescriptor::WriteToTLVSet(TLVWriter& TLVSet) void IABEssenceDescriptor::Copy(const IABEssenceDescriptor& rhs) { - FileDescriptor::Copy(rhs); + GenericSoundEssenceDescriptor::Copy(rhs); } // @@ -4909,7 +4909,7 @@ IABEssenceDescriptor::Dump(FILE* stream) if ( stream == 0 ) stream = stderr; - FileDescriptor::Dump(stream); + GenericSoundEssenceDescriptor::Dump(stream); } // -- cgit v1.2.3