diff options
| author | Thomas Richter <thomas.richter@iis.fraunhofer.de> | 2021-05-21 17:17:15 +0200 |
|---|---|---|
| committer | Thomas Richter <thomas.richter@iis.fraunhofer.de> | 2021-05-21 17:17:15 +0200 |
| commit | 1dfee29db933f05601e632b5a61ddecaf66d21aa (patch) | |
| tree | 5200eefecc85cd3610257b506e78c3fc3a613df9 /src | |
| parent | face28d822719aa6028ffca21097225c2a94e4ca (diff) | |
Added the JXS functionality again into the core library.
Diffstat (limited to 'src')
| -rw-r--r-- | src/AS_02.h | 86 | ||||
| -rw-r--r-- | src/AS_02_JXS.cpp | 577 | ||||
| -rwxr-xr-x | src/AS_DCP.h | 229 | ||||
| -rw-r--r-- | src/AS_DCP_JXS.cpp | 1188 | ||||
| -rwxr-xr-x | src/AS_DCP_internal.h | 25 | ||||
| -rw-r--r-- | src/CMakeLists.txt | 13 | ||||
| -rw-r--r-- | src/JXS.cpp | 156 | ||||
| -rw-r--r-- | src/JXS.h | 239 | ||||
| -rw-r--r-- | src/JXS_Codestream_Parser.cpp | 251 | ||||
| -rw-r--r-- | src/JXS_Sequence_Parser.cpp | 281 | ||||
| -rw-r--r-- | src/Makefile.am | 11 | ||||
| -rw-r--r-- | src/Metadata.cpp | 135 | ||||
| -rw-r--r-- | src/Metadata.h | 32 |
13 files changed, 3213 insertions, 10 deletions
diff --git a/src/AS_02.h b/src/AS_02.h index ab58f31..55d7e8b 100644 --- a/src/AS_02.h +++ b/src/AS_02.h @@ -232,6 +232,90 @@ namespace AS_02 } //namespace JP2K + namespace JXS + { + // + class MXFWriter + { + class h__Writer; + ASDCP::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 ASDCP::MXF::OP1aHeader& OP1aHeader(); + virtual ASDCP::MXF::RIP& RIP(); + + // 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 std::string& filename, const ASDCP::WriterInfo&, + ASDCP::MXF::FileDescriptor* essence_descriptor, + ASDCP::MXF::InterchangeObject_list_t& essence_sub_descriptor_list, + const ASDCP::Rational& edit_rate, const ui32_t& header_size = 16384, + const IndexStrategy_t& strategy = IS_FOLLOW, const ui32_t& partition_space = 10); + + // 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 ASDCP::JXS::FrameBuffer&, ASDCP::AESEncContext* = 0, ASDCP::HMACContext* = 0); + + // Closes the MXF file, writing the index and revised header. + Result_t Finalize(); + }; + + // + class MXFReader + { + class h__Reader; + ASDCP::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 ASDCP::MXF::OP1aHeader& OP1aHeader(); + virtual AS_02::MXF::AS02IndexReader& AS02IndexReader(); + virtual ASDCP::MXF::RIP& RIP(); + + // Open the file for reading. The file must exist. Returns error if the + // operation cannot be completed. + Result_t OpenRead(const std::string& filename) const; + + // Returns RESULT_INIT if the file is not open. + Result_t Close() 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(ASDCP::WriterInfo&) const; + + // Compute the necessary frame buffer size in bytes for CBR + // codestreamss. + Result_t CalcFrameBufferSize(ui64_t &size) 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, ASDCP::JXS::FrameBuffer&, ASDCP::AESDecContext* = 0, ASDCP::HMACContext* = 0) const; + + // Print debugging information to stream + void DumpHeaderMetadata(FILE* = 0) const; + void DumpIndex(FILE* = 0) const; + }; + + } //namespace JXS //--------------------------------------------------------------------------------- // @@ -248,7 +332,7 @@ namespace AS_02 // the reader to signal the number of samples to be read by each call to ReadFrame(); // - class MXFWriter + class MXFWriter { class h__Writer; ASDCP::mem_ptr<h__Writer> m_Writer; diff --git a/src/AS_02_JXS.cpp b/src/AS_02_JXS.cpp new file mode 100644 index 0000000..b862f26 --- /dev/null +++ b/src/AS_02_JXS.cpp @@ -0,0 +1,577 @@ +/* +Copyright (c) 2011-2018, Robert Scheler, Heiko Sparenberg Fraunhofer IIS, +John Hurst +Copyright (c) 2020, Thomas Richter, Fraunhofer IIS + +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_02_JXS.cpp + \version $Id$ + \brief AS-02 library, JPEG XS essence reader and writer implementation +*/ + +#include "AS_02_internal.h" + +#include <iostream> +#include <iomanip> + +using namespace ASDCP; +using namespace ASDCP::JXS; +using Kumu::GenRandomValue; + +//------------------------------------------------------------------------------------------ + +static std::string JXS_PACKAGE_LABEL = "File Package: SMPTE ST 422 / ST 2124 frame wrapping of JPEG XS codestreams"; +static std::string PICT_DEF_LABEL = "Image Track"; + +//------------------------------------------------------------------------------------------ +// +// hidden, internal implementation of JPEG XS reader + + +class AS_02::JXS::MXFReader::h__Reader : public AS_02::h__AS02Reader +{ + ASDCP_NO_COPY_CONSTRUCT(h__Reader); + +public: + h__Reader(const Dictionary *d) : + AS_02::h__AS02Reader(d) {} + + virtual ~h__Reader() {} + + Result_t OpenRead(const std::string&); + Result_t ReadFrame(ui32_t, ASDCP::JXS::FrameBuffer&, AESDecContext*, HMACContext*); + Result_t CalcFrameBufferSize(ui64_t &size); +}; + +// +Result_t +AS_02::JXS::MXFReader::h__Reader::CalcFrameBufferSize(ui64_t &size) +{ + IndexTableSegment::IndexEntry TmpEntry; + + if ( ! m_File.IsOpen() ) + return RESULT_INIT; + + if ( KM_FAILURE(m_IndexAccess.Lookup(0, TmpEntry)) ) { + return RESULT_RANGE; + } + + // get relative frame position, apply offset and go read the frame's key and length + Kumu::fpos_t FilePosition = TmpEntry.StreamOffset; + Result_t result = RESULT_OK; + Kumu::fpos_t old = m_LastPosition; + + if ( FilePosition != m_LastPosition ) { + m_LastPosition = FilePosition; + result = m_File.Seek(FilePosition); + } + + if ( KM_SUCCESS(result) ) { + result = AS_02::h__AS02Reader::CalcFrameBufferSize(size); + } + + // + // Return the file to where it was + m_LastPosition = old; + m_File.Seek(old); + + return result; +} + +// +Result_t +AS_02::JXS::MXFReader::h__Reader::OpenRead(const std::string& filename) +{ + Result_t result = OpenMXFRead(filename); + + if( KM_SUCCESS(result) ) + { + InterchangeObject* tmp_iobj = 0; + + m_HeaderPart.GetMDObjectByType(OBJ_TYPE_ARGS(CDCIEssenceDescriptor), &tmp_iobj); + + if ( tmp_iobj == 0 ) + { + m_HeaderPart.GetMDObjectByType(OBJ_TYPE_ARGS(RGBAEssenceDescriptor), &tmp_iobj); + } + + if ( tmp_iobj == 0 ) + { + DefaultLogSink().Error("RGBAEssenceDescriptor nor CDCIEssenceDescriptor found.\n"); + } + + m_HeaderPart.GetMDObjectByType(OBJ_TYPE_ARGS(JPEGXSPictureSubDescriptor), &tmp_iobj); + + if ( tmp_iobj == 0 ) + { + DefaultLogSink().Error("JPEGXSPictureSubDescriptor not found.\n"); + } + + std::list<InterchangeObject*> ObjectList; + m_HeaderPart.GetMDObjectsByType(OBJ_TYPE_ARGS(Track), ObjectList); + + if ( ObjectList.empty() ) + { + DefaultLogSink().Error("MXF Metadata contains no Track Sets.\n"); + return RESULT_AS02_FORMAT; + } + } + + return result; +} + +// +// +Result_t +AS_02::JXS::MXFReader::h__Reader::ReadFrame(ui32_t FrameNum, ASDCP::JXS::FrameBuffer& FrameBuf, + ASDCP::AESDecContext* Ctx, ASDCP::HMACContext* HMAC) +{ + if ( ! m_File.IsOpen() ) + return RESULT_INIT; + + assert(m_Dict); + return ReadEKLVFrame(FrameNum, FrameBuf, m_Dict->ul(MDD_JPEGXSEssence), Ctx, HMAC); +} + +//------------------------------------------------------------------------------------------ +// + +AS_02::JXS::MXFReader::MXFReader() +{ + m_Reader = new h__Reader(&DefaultCompositeDict()); +} + + +AS_02::JXS::MXFReader::~MXFReader() +{ +} + +// Warning: direct manipulation of MXF structures can interfere +// with the normal operation of the wrapper. Caveat emptor! +// +ASDCP::MXF::OP1aHeader& +AS_02::JXS::MXFReader::OP1aHeader() +{ + if ( m_Reader.empty() ) + { + assert(g_OP1aHeader); + return *g_OP1aHeader; + } + + return m_Reader->m_HeaderPart; +} + +// Warning: direct manipulation of MXF structures can interfere +// with the normal operation of the wrapper. Caveat emptor! +// +AS_02::MXF::AS02IndexReader& +AS_02::JXS::MXFReader::AS02IndexReader() +{ + if ( m_Reader.empty() ) + { + assert(g_AS02IndexReader); + return *g_AS02IndexReader; + } + + return m_Reader->m_IndexAccess; +} + +// Warning: direct manipulation of MXF structures can interfere +// with the normal operation of the wrapper. Caveat emptor! +// +ASDCP::MXF::RIP& +AS_02::JXS::MXFReader::RIP() +{ + if ( m_Reader.empty() ) + { + assert(g_RIP); + return *g_RIP; + } + + return m_Reader->m_RIP; +} + +// Open the file for reading. The file must exist. Returns error if the +// operation cannot be completed. +Result_t +AS_02::JXS::MXFReader::OpenRead(const std::string& filename) const +{ + return m_Reader->OpenRead(filename); +} + +// +Result_t +AS_02::JXS::MXFReader::Close() const +{ + if ( m_Reader && m_Reader->m_File.IsOpen() ) + { + m_Reader->Close(); + return RESULT_OK; + } + + return RESULT_INIT; +} + +// +Result_t +AS_02::JXS::MXFReader::ReadFrame(ui32_t FrameNum, ASDCP::JXS::FrameBuffer& FrameBuf, + ASDCP::AESDecContext* Ctx, ASDCP::HMACContext* HMAC) const +{ + if ( m_Reader && m_Reader->m_File.IsOpen() ) + return m_Reader->ReadFrame(FrameNum, FrameBuf, Ctx, HMAC); + + return RESULT_INIT; +} + +// +// +Result_t +AS_02::JXS::MXFReader::CalcFrameBufferSize(ui64_t &size) const +{ + if ( m_Reader && m_Reader->m_File.IsOpen() ) + return m_Reader->CalcFrameBufferSize(size); + + return RESULT_INIT; +} + +// Fill the struct with the values from the file's header. +// Returns RESULT_INIT if the file is not open. +Result_t +AS_02::JXS::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 +AS_02::JXS::MXFReader::DumpHeaderMetadata(FILE* stream) const +{ + if ( m_Reader && m_Reader->m_File.IsOpen() ) + { + m_Reader->m_HeaderPart.Dump(stream); + } +} + + +// +void +AS_02::JXS::MXFReader::DumpIndex(FILE* stream) const +{ + if ( m_Reader && m_Reader->m_File.IsOpen() ) + { + m_Reader->m_IndexAccess.Dump(stream); + } +} + +//------------------------------------------------------------------------------------------ + +// +class AS_02::JXS::MXFWriter::h__Writer : public AS_02::h__AS02WriterFrame +{ + ASDCP_NO_COPY_CONSTRUCT(h__Writer); + h__Writer(); + + JPEGXSPictureSubDescriptor* m_EssenceSubDescriptor; + +public: + byte_t m_EssenceUL[SMPTE_UL_LENGTH]; + + h__Writer(const Dictionary *d) : h__AS02WriterFrame(d), m_EssenceSubDescriptor(0) { + memset(m_EssenceUL, 0, SMPTE_UL_LENGTH); + } + + virtual ~h__Writer(){} + + Result_t OpenWrite(const std::string&, ASDCP::MXF::FileDescriptor* essence_descriptor, + ASDCP::MXF::InterchangeObject_list_t& essence_sub_descriptor_list, + const AS_02::IndexStrategy_t& IndexStrategy, + const ui32_t& PartitionSpace, const ui32_t& HeaderSize); + Result_t SetSourceStream(const std::string& label, const ASDCP::Rational& edit_rate); + Result_t WriteFrame(const ASDCP::JXS::FrameBuffer&, ASDCP::AESEncContext*, ASDCP::HMACContext*); + Result_t Finalize(); +}; + + +// Open the file for writing. The file must not exist. Returns error if +// the operation cannot be completed. +Result_t +AS_02::JXS::MXFWriter::h__Writer::OpenWrite(const std::string& filename, + ASDCP::MXF::FileDescriptor* essence_descriptor, + ASDCP::MXF::InterchangeObject_list_t& essence_sub_descriptor_list, + const AS_02::IndexStrategy_t& IndexStrategy, + const ui32_t& PartitionSpace_sec, const ui32_t& HeaderSize) +{ + if ( ! m_State.Test_BEGIN() ) + { + KM_RESULT_STATE_HERE(); + return RESULT_STATE; + } + + if ( m_IndexStrategy != AS_02::IS_FOLLOW ) + { + DefaultLogSink().Error("Only strategy IS_FOLLOW is supported at this time.\n"); + return Kumu::RESULT_NOTIMPL; + } + + Result_t result = m_File.OpenWrite(filename.c_str()); + + if ( KM_SUCCESS(result) ) + { + m_IndexStrategy = IndexStrategy; + m_PartitionSpace = PartitionSpace_sec; // later converted to edit units by SetSourceStream() + m_HeaderSize = HeaderSize; + + if ( essence_descriptor->GetUL() != UL(m_Dict->ul(MDD_RGBAEssenceDescriptor)) + && essence_descriptor->GetUL() != UL(m_Dict->ul(MDD_CDCIEssenceDescriptor)) ) + { + DefaultLogSink().Error("Essence descriptor is not a RGBAEssenceDescriptor or CDCIEssenceDescriptor.\n"); + essence_descriptor->Dump(); + return RESULT_AS02_FORMAT; + } + + m_EssenceDescriptor = essence_descriptor; + + ASDCP::MXF::InterchangeObject_list_t::iterator i; + for ( i = essence_sub_descriptor_list.begin(); i != essence_sub_descriptor_list.end(); ++i ) + { + if ( (*i)->GetUL() != UL(m_Dict->ul(MDD_JPEGXSPictureSubDescriptor)) ) + { + DefaultLogSink().Error("Essence sub-descriptor is not a JPEGXSPictureSubDescriptor.\n"); + (*i)->Dump(); + } + + m_EssenceSubDescriptorList.push_back(*i); + GenRandomValue((*i)->InstanceUID); + m_EssenceDescriptor->SubDescriptors.push_back((*i)->InstanceUID); + *i = 0; // parent will only free the ones we don't keep + } + + result = m_State.Goto_INIT(); + } + + return result; +} + +// Automatically sets the MXF file's metadata from the first jpeg codestream stream. +Result_t +AS_02::JXS::MXFWriter::h__Writer::SetSourceStream(const std::string& label, const ASDCP::Rational& edit_rate) +{ + assert(m_Dict); + if ( ! m_State.Test_INIT() ) + { + KM_RESULT_STATE_HERE(); + return RESULT_STATE; + } + + memcpy(m_EssenceUL, m_Dict->ul(MDD_JPEGXSEssence), SMPTE_UL_LENGTH); + m_EssenceUL[SMPTE_UL_LENGTH-1] = 1; // first (and only) essence container + Result_t result = m_State.Goto_READY(); + + if ( KM_SUCCESS(result) ) + { + UL wrapping_label = UL(m_Dict->ul(MDD_MXFGCP1FrameWrappedPictureElement)); + + CDCIEssenceDescriptor *cdci_descriptor = dynamic_cast<CDCIEssenceDescriptor*>(m_EssenceDescriptor); + if ( cdci_descriptor ) + { + if ( cdci_descriptor->FrameLayout ) // 0 == progressive, 1 == interlace + { + wrapping_label = UL(m_Dict->ul(MDD_MXFGCI1FrameWrappedPictureElement)); + } + } + + result = WriteAS02Header(label, wrapping_label, + PICT_DEF_LABEL, UL(m_EssenceUL), UL(m_Dict->ul(MDD_PictureDataDef)), + edit_rate); + + if ( KM_SUCCESS(result) ) + { + this->m_IndexWriter.SetPrimerLookup(&this->m_HeaderPart.m_Primer); + this->m_IndexWriter.SetEditRate(m_EssenceDescriptor->SampleRate); + } + } + + 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. +// +Result_t +AS_02::JXS::MXFWriter::h__Writer::WriteFrame(const ASDCP::JXS::FrameBuffer& FrameBuf, + AESEncContext* Ctx, HMACContext* HMAC) +{ + if ( FrameBuf.Size() == 0 ) + { + DefaultLogSink().Error("The frame buffer size is zero.\n"); + return RESULT_PARAM; + } + + Result_t result = RESULT_OK; + + if ( m_State.Test_READY() ) + { + result = m_State.Goto_RUNNING(); // first time through + } + + if ( KM_SUCCESS(result) ) + { + result = WriteEKLVPacket(FrameBuf, m_EssenceUL, MXF_BER_LENGTH, Ctx, HMAC); + m_FramesWritten++; + } + + return result; +} + +// Closes the MXF file, writing the index and other closing information. +// +Result_t +AS_02::JXS::MXFWriter::h__Writer::Finalize() +{ + if ( ! m_State.Test_RUNNING() ) + { + KM_RESULT_STATE_HERE(); + return RESULT_STATE; + } + + Result_t result = m_State.Goto_FINAL(); + + if ( KM_SUCCESS(result) ) + { + result = WriteAS02Footer(); + } + + return result; +} + + +//------------------------------------------------------------------------------------------ + + + +AS_02::JXS::MXFWriter::MXFWriter() +{ +} + +AS_02::JXS::MXFWriter::~MXFWriter() +{ +} + +// Warning: direct manipulation of MXF structures can interfere +// with the normal operation of the wrapper. Caveat emptor! +// +ASDCP::MXF::OP1aHeader& +AS_02::JXS::MXFWriter::OP1aHeader() +{ + if ( m_Writer.empty() ) + { + assert(g_OP1aHeader); + return *g_OP1aHeader; + } + + return m_Writer->m_HeaderPart; +} + +// Warning: direct manipulation of MXF structures can interfere +// with the normal operation of the wrapper. Caveat emptor! +// +ASDCP::MXF::RIP& +AS_02::JXS::MXFWriter::RIP() +{ + if ( m_Writer.empty() ) + { + assert(g_RIP); + return *g_RIP; + } + + return m_Writer->m_RIP; +} + +// Open the file for writing. The file must not exist. Returns error if +// the operation cannot be completed. +Result_t +AS_02::JXS::MXFWriter::OpenWrite(const std::string& filename, const ASDCP::WriterInfo& Info, + ASDCP::MXF::FileDescriptor* essence_descriptor, + ASDCP::MXF::InterchangeObject_list_t& essence_sub_descriptor_list, + const ASDCP::Rational& edit_rate, const ui32_t& header_size, + const IndexStrategy_t& strategy, const ui32_t& partition_space) +{ + if ( essence_descriptor == 0 ) + { + DefaultLogSink().Error("Essence descriptor object required.\n"); + return RESULT_PARAM; + } + + m_Writer = new AS_02::JXS::MXFWriter::h__Writer(&DefaultSMPTEDict()); + m_Writer->m_Info = Info; + + Result_t result = m_Writer->OpenWrite(filename, essence_descriptor, essence_sub_descriptor_list, + strategy, partition_space, header_size); + + if ( KM_SUCCESS(result) ) + result = m_Writer->SetSourceStream(JXS_PACKAGE_LABEL, edit_rate); + + if ( KM_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. +Result_t +AS_02::JXS::MXFWriter::WriteFrame(const ASDCP::JXS::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. +Result_t +AS_02::JXS::MXFWriter::Finalize() +{ + if ( m_Writer.empty() ) + return RESULT_INIT; + + return m_Writer->Finalize(); +} + + +// +// end AS_02_JXS.cpp +// diff --git a/src/AS_DCP.h b/src/AS_DCP.h index fa8b00a..c16ea57 100755 --- a/src/AS_DCP.h +++ b/src/AS_DCP.h @@ -1,5 +1,7 @@ /* -Copyright (c) 2003-2018, John Hurst +Copyright (c) 2003-2018, John Hurst, +Copyright (c) 2020, Thomas Richter Fraunhofer IIS, +Copyright (c) 2020, Christian Minuth Fraunhofer IIS, All rights reserved. Redistribution and use in source and binary forms, with or without @@ -209,6 +211,7 @@ namespace ASDCP { // d-cinema essence types ESS_JPEG_2000, // the file contains one or more JPEG 2000 codestreams + ESS_JPEG_XS, // the file contains one or more JPEG XS 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 @@ -218,6 +221,7 @@ namespace ASDCP { // IMF essence types ESS_AS02_JPEG_2000, // the file contains one or more JPEG 2000 codestreams + ESS_AS02_JPEG_XS, // the file contains one or more JPEG XS codestreams ESS_AS02_PCM_24b_48k, // the file contains one or more PCM audio pairs, clip wrapped ESS_AS02_PCM_24b_96k, // the file contains one or more PCM audio pairs, clip wrapped ESS_AS02_TIMED_TEXT, // the file contains a TTML document and zero or more resources @@ -1452,6 +1456,229 @@ namespace ASDCP { //--------------------------------------------------------------------------------- // + namespace JXS + { + const ui32_t MaxComponents = 4; // ISO 21122-1 Annex A.2 up to 8 components + const ui32_t MaxHorizontalLevels = 15; + const ui32_t MaxVerticalLevels = 2; + +#pragma pack(1) + struct ImageComponent_t // Essentially, a lookalike of the CDT marker, just with less bit-packing + { + ui8_t Bc; // Bitdepth (literal, not -1) + ui8_t Sx; + ui8_t Sy; // Subsampling factors, horizontal and vertically. Bit-packed in the marker. + }; +#pragma pack() + + struct PictureDescriptor + { + Rational EditRate; + ui32_t ContainerDuration; + Rational SampleRate; + ui32_t StoredWidth; + ui32_t StoredHeight; + Rational AspectRatio; + ui16_t Ppih; // Profile, copy from the PIH marker + ui16_t Plev; // Level and sublevel, copy from the PIH marker + ui16_t Wf; // Frame width, copy from the PIH marker + ui16_t Hf; // Frame height, copy from the PIH marker + ui16_t Hsl; // slice height, copy from the PIH marker + ui16_t Cw; // column width, or 0 for no columns, copy from the PIH marker + ui8_t Nc; // number of components, copy from the PIH marker + ui32_t MaximumBitRate; // bit rate in MB/sec, or 0 if not known + ui8_t Primaries; // Color primaries as defined by CICP + ui8_t TransferCurve; // Transfer curve as defined by CICP + ui8_t Matrix; // Transform matrix, as defined by CICP + bool fullRange; // If true, no head and toe region + ImageComponent_t ImageComponents[MaxComponents]; // These are copies from the CDT (component table) + }; + // Print debugging information to std::ostream + std::ostream& operator << (std::ostream& strm, const PictureDescriptor& pdesc); + // Print debugging information to stream (stderr default) + void PictureDescriptorDump(const PictureDescriptor&, FILE* = 0); + + // + class FrameBuffer : public ASDCP::FrameBuffer + { + public: + FrameBuffer() {} + FrameBuffer(ui32_t size) { Capacity(size); } + virtual ~FrameBuffer() {} + + // Print debugging information to stream (stderr default) + void Dump(FILE* = 0, ui32_t dump_bytes = 0) const; + }; + + + // An object which opens and reads a JPEG XS codestream file. The file is expected + // to contain exactly one complete frame of picture essence as an unwrapped (raw) + // ISO/IEC 21122 codestream. + class CodestreamParser + { + class h__CodestreamParser; + mem_ptr<h__CodestreamParser> m_Parser; + ASDCP_NO_COPY_CONSTRUCT(CodestreamParser); + + public: + CodestreamParser(); + virtual ~CodestreamParser(); + + // Opens a file for reading, parses enough data to provide a complete + // set of stream metadata for the MXFWriter below. + // The frame buffer's PlaintextOffset parameter will be set to the first + // byte of the data segment. Set this value to zero if you want + // encrypted headers. + Result_t OpenReadFrame(const std::string& filename, FrameBuffer&) const; + + // Fill a PictureDescriptor struct with the values from the file's codestream. + // Returns RESULT_INIT if the file is not open. + Result_t FillPictureDescriptor(PictureDescriptor&) const; + }; + + // Parses the data in the frame buffer to fill in the picture descriptor. Copies + // the offset of the image data into start_of_data. Returns error if the parser fails. + Result_t ParseMetadataIntoDesc(const FrameBuffer&, PictureDescriptor&, byte_t* start_of_data = 0); + + // An object which reads a sequence of files containing JPEG XS pictures. + class SequenceParser + { + class h__SequenceParser; + mem_ptr<h__SequenceParser> m_Parser; + ASDCP_NO_COPY_CONSTRUCT(SequenceParser); + + public: + SequenceParser(); + virtual ~SequenceParser(); + + // Opens a directory for reading. The directory is expected to contain one or + // more files, each containing the codestream for exactly one picture. The + // files must be named such that the frames are in temporal order when sorted + // alphabetically by filename. The parser will automatically parse enough data + // from the first file to provide a complete set of stream metadata for the + // MXFWriter below. If the "pedantic" parameter is given and is true, the + // parser will check the metadata for each codestream and fail if a + // mismatch is detected. + Result_t OpenRead(const std::string& filename) const; + + // Opens a file sequence for reading. The sequence is expected to contain one or + // more filenames, each naming a file containing the codestream for exactly one + // 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 + // mismatch is detected. + Result_t OpenRead(const std::list<std::string>& file_list) const; + + // Fill a PictureDescriptor struct with the values from the first file's codestream. + // Returns RESULT_INIT if the directory is not open. + Result_t FillPictureDescriptor(PictureDescriptor&) const; + + // Rewind the directory to the beginning. + Result_t Reset() const; + + // Reads the next sequential frame in the directory and places it in the + // frame buffer. Fails if the buffer is too small or the direcdtory + // contains no more files. + // The frame buffer's PlaintextOffset parameter will be set to the first + // byte of the data segment. Set this value to zero if you want + // encrypted headers. + Result_t ReadFrame(FrameBuffer&) const; + }; + + + // + class MXFWriter + { + class h__Writer; + mem_ptr<h__Writer> m_Writer; + ASDCP_NO_COPY_CONSTRUCT(MXFWriter); + + public: + MXFWriter(); + virtual ~MXFWriter(); + + // Warning: direct manipulation of MXF structures can interfere + // with the normal operation of the wrapper. Caveat emptor! + virtual MXF::OP1aHeader& OP1aHeader(); + virtual MXF::OPAtomIndexFooter& OPAtomIndexFooter(); + virtual MXF::RIP& RIP(); + + // 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 std::string& filename, const WriterInfo&, + const PictureDescriptor&, ui32_t HeaderSize = 16384); + + // Writes a frame of essence to the MXF file. If the optional AESEncContext + // argument is present, the essence is encrypted prior to writing. + // Fails if the file is not open, is finalized, or an operating system + // error occurs. + Result_t WriteFrame(const FrameBuffer&, AESEncContext* = 0, HMACContext* = 0); + + // Closes the MXF file, writing the index and revised header. + Result_t Finalize(); + }; + + // + class MXFReader + { + class h__Reader; + mem_ptr<h__Reader> m_Reader; + ASDCP_NO_COPY_CONSTRUCT(MXFReader); + + public: + MXFReader(); + virtual ~MXFReader(); + + // Warning: direct manipulation of MXF structures can interfere + // with the normal operation of the wrapper. Caveat emptor! + virtual MXF::OP1aHeader& OP1aHeader(); + virtual MXF::OPAtomIndexFooter& OPAtomIndexFooter(); + virtual MXF::RIP& RIP(); + + // Open the file for reading. The file must exist. Returns error if the + // operation cannot be completed. + Result_t OpenRead(const std::string& filename) const; + + // Returns RESULT_INIT if the file is not open. + Result_t Close() const; + + // Fill an AudioDescriptor struct with the values from the file's header. + // Returns RESULT_INIT if the file is not open. + Result_t FillPictureDescriptor(PictureDescriptor&) const; + + // Fill a WriterInfo struct with the values from the file's header. + // Returns RESULT_INIT if the file is not open. + Result_t FillWriterInfo(WriterInfo&) const; + + // Compute the necessary frame buffer size in bytes for CBR + // codestreamss. + Result_t CalcFrameBufferSize(ui64_t &size) 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_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; + }; + } + + //--------------------------------------------------------------------------------- + // namespace TimedText { enum MIMEType_t { MT_BIN, MT_PNG, MT_OPENTYPE }; diff --git a/src/AS_DCP_JXS.cpp b/src/AS_DCP_JXS.cpp new file mode 100644 index 0000000..c69394a --- /dev/null +++ b/src/AS_DCP_JXS.cpp @@ -0,0 +1,1188 @@ +/* +Copyright (c) 2004-2016, John Hurst, +Copyright (c) 2020, Thomas Richter Fraunhofer IIS +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_JXS.cpp + \version $Id$ + \brief AS-DCP library, JPEG XS essence reader and writer implementation +*/ + +#include "AS_DCP_internal.h" +#include <iostream> +#include <iomanip> +#include <algorithm> + +using namespace ASDCP::JXS; +using Kumu::GenRandomValue; + +//------------------------------------------------------------------------------------------ + +static std::string JXS_PACKAGE_LABEL = "File Package: SMPTE 2124 frame wrapping of JPEG XS codestreams"; +//static std::string JP2K_S_PACKAGE_LABEL = "File Package: SMPTE 429-10 frame wrapping of stereoscopic JPEG XS codestreams"; +static std::string PICT_DEF_LABEL = "Picture Track"; + +static int s_exp_lookup[16] = { 0, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024,2048, 4096, 8192, 16384, 32768 }; + +// +std::ostream& +ASDCP::JXS::operator << (std::ostream& strm, const PictureDescriptor& PDesc) +{ + strm << " AspectRatio: " << PDesc.AspectRatio.Numerator << "/" << PDesc.AspectRatio.Denominator << std::endl; + strm << " EditRate: " << PDesc.EditRate.Numerator << "/" << PDesc.EditRate.Denominator << std::endl; + strm << " SampleRate: " << PDesc.SampleRate.Numerator << "/" << PDesc.SampleRate.Denominator << std::endl; + strm << " StoredWidth: " << (unsigned) PDesc.StoredWidth << std::endl; + strm << " StoredHeight: " << (unsigned) PDesc.StoredHeight << std::endl; + strm << " Wf: " << (unsigned) PDesc.Wf << std::endl; // width of the frame + strm << " Hf: " << (unsigned) PDesc.Hf << std::endl; // height of the frame + strm << " ContainerDuration: " << (unsigned) PDesc.ContainerDuration << std::endl; + + strm << "-- JPEG XS Metadata --" << std::endl; + strm << " ImageComponents:" << std::endl; + strm << " bits h-sep v-sep" << std::endl; + + ui32_t i; + for ( i = 0; i < PDesc.Nc && i < MaxComponents; ++i ) + { + strm << " " << std::setw(4) << PDesc.ImageComponents[i].Bc + << " " << std::setw(5) << PDesc.ImageComponents[i].Sx + << " " << std::setw(5) << PDesc.ImageComponents[i].Sy + << std::endl; + } + + strm << " Slice height: " << (short) PDesc.Hsl << std::endl; + strm << " Profile: " << (short) PDesc.Ppih << std::endl; + strm << " Level: " << (short) (PDesc.Plev >> 8) << std::endl; + strm << " Sublevel: " << (short) (PDesc.Plev & 0xff) << std::endl; + strm << " Column Width: " << (short) (PDesc.Cw) << std::endl; + strm << " Maximum Bit Rate: " << (PDesc.MaximumBitRate) << std::endl; + strm << " Primaries: " << (short) (PDesc.Primaries) << std::endl; + strm << " Transfer Curve: " << (short) (PDesc.TransferCurve) << std::endl; + strm << " Matrix: " << (short) (PDesc.Matrix) << std::endl; + strm << " full range: " << (PDesc.fullRange?("yes"):("no")) << std::endl; + /* + ** thor: at this point, do not print the CAP marker + */ + + return strm; +} + +// +void +ASDCP::JXS::PictureDescriptorDump(const PictureDescriptor& PDesc, FILE* stream) +{ + if ( stream == 0 ) + stream = stderr; + + fprintf(stream, "\ + AspectRatio: %d/%d\n\ + EditRate: %d/%d\n\ + SampleRate: %d/%d\n\ + StoredWidth: %u\n\ + StoredHeight: %u\n\ + Wf: %u\n\ + Hf: %u\n\ + Profile: %u\n\ + Level: %u\n\ + Sublevel: %u\n\ + Maximum BitRate: %u\n\ + ContainerDuration: %u\n\ + Primaries: %u\n\ + Transfer Curve: %u\n\ + Matrix: %u\n\ + full range: %s\n", + PDesc.AspectRatio.Numerator, PDesc.AspectRatio.Denominator, + PDesc.EditRate.Numerator, PDesc.EditRate.Denominator, + PDesc.SampleRate.Numerator, PDesc.SampleRate.Denominator, + PDesc.StoredWidth, + PDesc.StoredHeight, + PDesc.Wf, + PDesc.Hf, + PDesc.Ppih, + PDesc.Plev >> 4, + PDesc.Plev & 0x0f, + PDesc.MaximumBitRate, + PDesc.ContainerDuration, + PDesc.Primaries, + PDesc.TransferCurve, + PDesc.Matrix, + PDesc.fullRange?("yes"):("no") + ); + + fprintf(stream, "-- JPEG XS Metadata --\n"); + fprintf(stream, " ImageComponents:\n"); + fprintf(stream, " bits h-sep v-sep\n"); + + ui32_t i; + for ( i = 0; i < PDesc.Nc && i < MaxComponents; i++ ) + { + fprintf(stream, " %4d %5d %5d\n", + PDesc.ImageComponents[i].Bc, + PDesc.ImageComponents[i].Sx, + PDesc.ImageComponents[i].Sy + ); + } +} + +// +ASDCP::Result_t +ASDCP::JXS_PDesc_to_MD(const JXS::PictureDescriptor& PDesc, + const ASDCP::Dictionary&dict, + ASDCP::MXF::GenericPictureEssenceDescriptor& EssenceDescriptor, + ASDCP::MXF::JPEGXSPictureSubDescriptor& EssenceSubDescriptor) +{ + EssenceDescriptor.ContainerDuration = PDesc.ContainerDuration; + EssenceDescriptor.SampleRate = PDesc.EditRate; + EssenceDescriptor.FrameLayout = 0; + EssenceDescriptor.StoredWidth = PDesc.StoredWidth; + EssenceDescriptor.StoredHeight = PDesc.StoredHeight; + EssenceDescriptor.AspectRatio = PDesc.AspectRatio; + + EssenceSubDescriptor.JPEGXSPpih = PDesc.Ppih; + EssenceSubDescriptor.JPEGXSPlev = PDesc.Plev; + EssenceSubDescriptor.JPEGXSWf = PDesc.Wf; + EssenceSubDescriptor.JPEGXSHf = PDesc.Hf; + EssenceSubDescriptor.JPEGXSNc = PDesc.Nc; + + // Copy the value of the columns, but only if there are some + if (PDesc.Cw) { + EssenceSubDescriptor.JPEGXSCw = optional_property<ui16_t>(PDesc.Cw); + } else { + EssenceSubDescriptor.JPEGXSCw.set_has_value(false); + } + + // Copy the slice height. Actually, this is optional + // and does not necessarily require copying all the time, + // but let's copy it nevertheless. + EssenceSubDescriptor.JPEGXSHsl = optional_property<ui16_t>(PDesc.Hsl); + + if (PDesc.MaximumBitRate) { + EssenceSubDescriptor.JPEGXSMaximumBitRate = PDesc.MaximumBitRate; + } else { + EssenceSubDescriptor.JPEGXSMaximumBitRate.set_has_value(false); + } + + const ui32_t cdt_buffer_len = 8 * 2; // at most 8 components. + byte_t tmp_buffer[cdt_buffer_len]; + int i,comps = (PDesc.Nc > 8)?8:PDesc.Nc; + EssenceSubDescriptor.JPEGXSComponentTable.Length(4 + (comps << 1)); + // thor: unclear whether the marker size is part of this data. + tmp_buffer[0] = 0xff; + tmp_buffer[1] = 0x13; // the marker + tmp_buffer[2] = 0x00; + tmp_buffer[3] = comps * 2 + 2; // The size. + for(i = 0;i < comps;i++) { + tmp_buffer[4 + (i << 1)] = PDesc.ImageComponents[i].Bc; + tmp_buffer[5 + (i << 1)] = (PDesc.ImageComponents[i].Sx << 4) | (PDesc.ImageComponents[i].Sy); + } + + memcpy(EssenceSubDescriptor.JPEGXSComponentTable.Data(), tmp_buffer, 4 + (comps << 1)); + + // + switch(PDesc.Primaries) { + case 1: + EssenceDescriptor.ColorPrimaries = dict.ul(ASDCP::MDD_ColorPrimaries_ITU709); + break; + case 5: + EssenceDescriptor.ColorPrimaries = dict.ul(ASDCP::MDD_ColorPrimaries_ITU470_PAL); + break; + case 6: + EssenceDescriptor.ColorPrimaries = dict.ul(ASDCP::MDD_ColorPrimaries_SMPTE170M); + break; + case 9: + EssenceDescriptor.ColorPrimaries = dict.ul(ASDCP::MDD_ColorPrimaries_ITU2020); + break; + case 10: + EssenceDescriptor.ColorPrimaries = dict.ul(ASDCP::MDD_ColorPrimaries_SMPTE_DCDM); + break; + case 11: + EssenceDescriptor.ColorPrimaries = dict.ul(ASDCP::MDD_TheatricalViewingEnvironment); + break; + case 12: + EssenceDescriptor.ColorPrimaries = dict.ul(ASDCP::MDD_ColorPrimaries_P3D65); + break; + default: + return RESULT_PARAM; + break; + } + + switch(PDesc.TransferCurve) { + case 1: + case 6: + EssenceDescriptor.TransferCharacteristic = dict.ul(ASDCP::MDD_TransferCharacteristic_ITU709); + break; + case 5: // Display Gamma 2.8, BT.470-6 This does not seem to be supported + case 9: // Log(100:1) range This does not seem to be supported + case 10:// Log(100*Sqrt(10):1 range) + return Kumu::RESULT_NOTIMPL; + break; + case 8: + EssenceDescriptor.TransferCharacteristic = dict.ul(ASDCP::MDD_TransferCharacteristic_linear); + break; + case 11: + EssenceDescriptor.TransferCharacteristic = dict.ul(ASDCP::MDD_TransferCharacteristic_IEC6196624_xvYCC); + break; + case 13: + EssenceDescriptor.TransferCharacteristic = dict.ul(ASDCP::MDD_TransferCharacteristic_sRGB); + break; + case 14: + case 15: + EssenceDescriptor.TransferCharacteristic = dict.ul(ASDCP::MDD_TransferCharacteristic_ITU2020); + break; + case 16: + EssenceDescriptor.TransferCharacteristic = dict.ul(ASDCP::MDD_TransferCharacteristic_SMPTEST2084); + break; + case 17: + EssenceDescriptor.TransferCharacteristic = dict.ul(ASDCP::MDD_TransferCharacteristic_ST428); + break; + case 18: // HLG + EssenceDescriptor.TransferCharacteristic = dict.ul(ASDCP::MDD_TransferCharacteristic_HLG); + break; + case 12: // Rec. BT.1361 + EssenceDescriptor.TransferCharacteristic = dict.ul(ASDCP::MDD_TransferCharacteristic_BT1361); + break; + case 4: // Rec. BT.470 + EssenceDescriptor.TransferCharacteristic = dict.ul(ASDCP::MDD_TransferCharacteristic_BT470); + break; + case 7: // SMPTE 240M + EssenceDescriptor.TransferCharacteristic = dict.ul(ASDCP::MDD_TransferCharacteristic_ST240M); + break; + case 2: // Unspecified. This leaves the data intentionally undefined. + EssenceDescriptor.TransferCharacteristic.set_has_value(false); + break; + default: + return RESULT_PARAM; + break; + } + // + switch(PDesc.Matrix) { + case 0: // Identity matrix. Use the BGR coding equations. + EssenceDescriptor.CodingEquations = dict.ul(ASDCP::MDD_CodingEquations_BGR); + break; + case 4: // Title 47. + case 10: // ITU 2020 constant luminance? Does not seem to be supported + case 11: // SMPTE ST-2085 + return Kumu::RESULT_NOTIMPL; + case 1: + EssenceDescriptor.CodingEquations = dict.ul(ASDCP::MDD_CodingEquations_709); + break; + // Note: Matrix=2 does not set the optional parameter. This is intentional. + case 5: + case 6: + EssenceDescriptor.CodingEquations = dict.ul(ASDCP::MDD_CodingEquations_601); + break; + case 9: // ITU 2020 non-constant luminance? + EssenceDescriptor.CodingEquations = dict.ul(ASDCP::MDD_CodingEquations_Rec2020); + break; + case 2: // This is unspecified. The metadata item remains undefined on purpose. + EssenceDescriptor.CodingEquations.set_has_value(false); + break; + case 7: // ST 240M + EssenceDescriptor.CodingEquations = dict.ul(ASDCP::MDD_CodingEquations_ST240M); + break; + case 8: // YCgCo + EssenceDescriptor.CodingEquations = dict.ul(ASDCP::MDD_CodingEquations_YCGCO); + break; + default: + return RESULT_PARAM; + break; + } + // +#if 0 + if (rgba) { + byte_t layout[ASDCP::MXF::RGBAValueLength]; + if (m_bFullRange) { + rgba->ComponentMaxRef = (1UL << m_ucPrecision) - 1; + rgba->ComponentMinRef = 0; + } else { + rgba->ComponentMaxRef = 235 * (1UL << (m_ucPrecision - 8)); + rgba->ComponentMinRef = 16 * (1UL << (m_ucPrecision - 8)); + } + layout[0] = 'R'; + layout[1] = m_ucPrecision; + layout[2] = 'G'; + layout[3] = m_ucPrecision; + layout[4] = 'B'; + layout[5] = m_ucPrecision; + layout[6] = 0; +#endif + return RESULT_OK; +} + +// +ASDCP::Result_t +ASDCP::MD_to_JXS_PDesc(const ASDCP::MXF::GenericPictureEssenceDescriptor& EssenceDescriptor, + const ASDCP::MXF::JPEGXSPictureSubDescriptor& EssenceSubDescriptor, + const ASDCP::Rational& EditRate, const ASDCP::Rational& SampleRate, + ASDCP::JXS::PictureDescriptor& PDesc) +{ + const Dictionary *dict = &ASDCP::DefaultSMPTEDict(); + const ASDCP::MXF::RGBAEssenceDescriptor *rgba = dynamic_cast<const ASDCP::MXF::RGBAEssenceDescriptor *>(&EssenceDescriptor); + const ASDCP::MXF::CDCIEssenceDescriptor *cdci = dynamic_cast<const ASDCP::MXF::CDCIEssenceDescriptor *>(&EssenceDescriptor); + memset(&PDesc, 0, sizeof(PDesc)); + + PDesc.EditRate = EditRate; + PDesc.SampleRate = SampleRate; + assert(EssenceDescriptor.ContainerDuration.const_get() <= 0xFFFFFFFFL); + PDesc.ContainerDuration = static_cast<ui32_t>(EssenceDescriptor.ContainerDuration.const_get()); + PDesc.StoredWidth = EssenceDescriptor.StoredWidth; + PDesc.StoredHeight = EssenceDescriptor.StoredHeight; + PDesc.AspectRatio = EssenceDescriptor.AspectRatio; + + PDesc.Ppih = EssenceSubDescriptor.JPEGXSPpih; + PDesc.Plev = EssenceSubDescriptor.JPEGXSPlev; + PDesc.Wf = EssenceSubDescriptor.JPEGXSWf; + PDesc.Hf = EssenceSubDescriptor.JPEGXSHf; + PDesc.Nc = EssenceSubDescriptor.JPEGXSNc; + + if (EssenceDescriptor.ColorPrimaries.empty()) { + PDesc.Primaries = 1; // If not set, let us assume 709 primaries. Yuck! + } else if (EssenceDescriptor.ColorPrimaries == dict->ul(ASDCP::MDD_ColorPrimaries_ITU709)) { + PDesc.Primaries = 1; + } else if (EssenceDescriptor.ColorPrimaries == dict->ul(ASDCP::MDD_ColorPrimaries_ITU470_PAL)) { + PDesc.Primaries = 5; + } else if (EssenceDescriptor.ColorPrimaries == dict->ul(ASDCP::MDD_ColorPrimaries_SMPTE170M)) { + PDesc.Primaries = 6; + } else if (EssenceDescriptor.ColorPrimaries == dict->ul(ASDCP::MDD_ColorPrimaries_ITU2020)) { + PDesc.Primaries = 9; + } else if (EssenceDescriptor.ColorPrimaries == dict->ul(ASDCP::MDD_ColorPrimaries_SMPTE_DCDM)) { + PDesc.Primaries = 10; + } else if (EssenceDescriptor.ColorPrimaries == dict->ul(ASDCP::MDD_TheatricalViewingEnvironment)) { + PDesc.Primaries = 11; + } else if (EssenceDescriptor.ColorPrimaries == dict->ul(ASDCP::MDD_ColorPrimaries_P3D65)) { + PDesc.Primaries = 12; + } else { + PDesc.Primaries = 0; + } + + if (EssenceDescriptor.TransferCharacteristic.empty()) { + PDesc.TransferCurve = 2; // Unspecified + } else if (EssenceDescriptor.TransferCharacteristic == dict->ul(ASDCP::MDD_TransferCharacteristic_ITU709)) { + PDesc.TransferCurve = 1; + } else if (EssenceDescriptor.TransferCharacteristic == dict->ul(ASDCP::MDD_TransferCharacteristic_linear)) { + PDesc.TransferCurve = 8; + } else if (EssenceDescriptor.TransferCharacteristic == dict->ul(ASDCP::MDD_TransferCharacteristic_IEC6196624_xvYCC)) { + PDesc.TransferCurve = 11; + } else if (EssenceDescriptor.TransferCharacteristic == dict->ul(ASDCP::MDD_TransferCharacteristic_sRGB)) { + PDesc.TransferCurve = 13; + } else if (EssenceDescriptor.TransferCharacteristic == dict->ul(ASDCP::MDD_TransferCharacteristic_SMPTEST2084)) { + PDesc.TransferCurve = 16; + } else if (EssenceDescriptor.TransferCharacteristic == dict->ul(ASDCP::MDD_TransferCharacteristic_ST428)) { + PDesc.TransferCurve = 17; + } else if (EssenceDescriptor.TransferCharacteristic == dict->ul(ASDCP::MDD_TransferCharacteristic_HLG)) { + PDesc.TransferCurve = 18; + } else if (EssenceDescriptor.TransferCharacteristic == dict->ul(ASDCP::MDD_TransferCharacteristic_BT1361)) { + PDesc.TransferCurve = 12; + } else if (EssenceDescriptor.TransferCharacteristic == dict->ul(ASDCP::MDD_TransferCharacteristic_BT470)) { + PDesc.TransferCurve = 4; + } else if (EssenceDescriptor.TransferCharacteristic == dict->ul(ASDCP::MDD_TransferCharacteristic_ST240M)) { + PDesc.TransferCurve = 7; + } else { + PDesc.TransferCurve = 0; + } + + if (EssenceDescriptor.CodingEquations.empty()) { + PDesc.Matrix = 2; + } else if (EssenceDescriptor.CodingEquations == dict->ul(ASDCP::MDD_CodingEquations_BGR)) { + PDesc.Matrix = 0; + } else if (EssenceDescriptor.CodingEquations == dict->ul(ASDCP::MDD_CodingEquations_709)) { + PDesc.Matrix = 1; + } else if (EssenceDescriptor.CodingEquations == dict->ul(ASDCP::MDD_CodingEquations_601)) { + PDesc.Matrix = 5; + } else if (EssenceDescriptor.CodingEquations == dict->ul(ASDCP::MDD_CodingEquations_Rec2020)) { + PDesc.Matrix = 9; + } else if (EssenceDescriptor.CodingEquations == dict->ul(ASDCP::MDD_CodingEquations_ST240M)) { + PDesc.Matrix = 7; + } else if (EssenceDescriptor.CodingEquations == dict->ul(ASDCP::MDD_CodingEquations_YCGCO)) { + PDesc.Matrix = 8; + } else { + PDesc.Matrix = 0; + } + + if (EssenceSubDescriptor.JPEGXSCw.const_get()==0 || EssenceSubDescriptor.JPEGXSCw.const_get() == 0) + PDesc.Cw = 0; + else + PDesc.Cw = static_cast<ui16_t>(EssenceSubDescriptor.JPEGXSCw.const_get()); + + PDesc.Hsl = static_cast<ui16_t>(EssenceSubDescriptor.JPEGXSHsl.const_get()); + + PDesc.MaximumBitRate = static_cast<ui32_t>(EssenceSubDescriptor.JPEGXSMaximumBitRate.const_get()); + + // JPEGXSComponentTable + ui32_t tmp_size = EssenceSubDescriptor.JPEGXSComponentTable.Length(); + + if (tmp_size > 4 && (tmp_size & 1) == 0 && (PDesc.Nc << 1) + 4 == tmp_size) { + const byte_t *data = EssenceSubDescriptor.JPEGXSComponentTable.RoData() + 4; + for(int i = 0;i < PDesc.Nc;i++) { + PDesc.ImageComponents[i].Bc = data[0]; + PDesc.ImageComponents[i].Sy = data[1] >> 4; + PDesc.ImageComponents[i].Sx = data[1] & 0x0f; + data += 2; + } + } else { + return RESULT_FAIL; + } + + if (rgba) { + if (rgba->ComponentMinRef.empty()) { + if (rgba->ComponentMaxRef.empty()) { + PDesc.fullRange = false; + } else if (rgba->ComponentMaxRef == (1UL << PDesc.ImageComponents[0].Bc) - 1) { + PDesc.fullRange = true; + } else { + PDesc.fullRange = false; + } + } else if (rgba->ComponentMinRef == 0) { + PDesc.fullRange = true; + } else { + PDesc.fullRange = false; + } + } else if (cdci) { + if (cdci->BlackRefLevel.empty()) { + if (cdci->WhiteReflevel.empty()) { + PDesc.fullRange = false; + } else if (cdci->WhiteReflevel == (1UL << PDesc.ImageComponents[0].Bc) - 1) { + PDesc.fullRange = true; + } else { + PDesc.fullRange = false; + } + } else if (cdci->BlackRefLevel == 0) { + PDesc.fullRange = true; + } else { + PDesc.fullRange = false; + } + } else { + PDesc.fullRange = false; + } + + return RESULT_OK; +} + + +//------------------------------------------------------------------------------------------ +// +// hidden, internal implementation of JPEG XS reader + + +class ih__Reader : public ASDCP::h__ASDCPReader +{ + RGBAEssenceDescriptor* m_EssenceDescriptor; + JPEGXSPictureSubDescriptor* m_EssenceSubDescriptor; + ASDCP::Rational m_EditRate; + ASDCP::Rational m_SampleRate; + EssenceType_t m_Format; + + ASDCP_NO_COPY_CONSTRUCT(ih__Reader); + +public: + PictureDescriptor m_PDesc; // codestream parameter list + + ih__Reader(const Dictionary *d) : + ASDCP::h__ASDCPReader(d), m_EssenceDescriptor(0), m_EssenceSubDescriptor(0), m_Format(ESS_UNKNOWN) {} + + virtual ~ih__Reader() {} + + Result_t OpenRead(const std::string&, EssenceType_t); + Result_t ReadFrame(ui32_t, JXS::FrameBuffer&, AESDecContext*, HMACContext*); + Result_t CalcFrameBufferSize(ui64_t &size); +}; + + +// +// +ASDCP::Result_t +ih__Reader::OpenRead(const std::string& filename, EssenceType_t type) +{ + Result_t result = OpenMXFRead(filename); + + if( ASDCP_SUCCESS(result) ) + { + InterchangeObject* tmp_iobj = 0; + m_HeaderPart.GetMDObjectByType(OBJ_TYPE_ARGS(RGBAEssenceDescriptor), &tmp_iobj); + m_EssenceDescriptor = static_cast<RGBAEssenceDescriptor*>(tmp_iobj); + + if ( m_EssenceDescriptor == 0 ) + { + DefaultLogSink().Error("RGBAEssenceDescriptor object not found.\n"); + return RESULT_FORMAT; + } + + m_HeaderPart.GetMDObjectByType(OBJ_TYPE_ARGS(JPEGXSPictureSubDescriptor), &tmp_iobj); + m_EssenceSubDescriptor = static_cast<JPEGXSPictureSubDescriptor*>(tmp_iobj); + + if ( m_EssenceSubDescriptor == 0 ) + { + m_EssenceDescriptor = 0; + DefaultLogSink().Error("JPEGXSPictureSubDescriptor object not found.\n"); + return RESULT_FORMAT; + } + + std::list<InterchangeObject*> ObjectList; + m_HeaderPart.GetMDObjectsByType(OBJ_TYPE_ARGS(Track), ObjectList); + + if ( ObjectList.empty() ) + { + DefaultLogSink().Error("MXF Metadata contains no Track Sets.\n"); + return RESULT_FORMAT; + } + + m_EditRate = ((Track*)ObjectList.front())->EditRate; + m_SampleRate = m_EssenceDescriptor->SampleRate; + + if ( type == ASDCP::ESS_JPEG_XS ) + { + if ( m_EditRate != m_SampleRate ) + { + DefaultLogSink().Warn("EditRate and SampleRate do not match (%.03f, %.03f).\n", + m_EditRate.Quotient(), m_SampleRate.Quotient()); + /* + ** thor: currently, I do not see support for stereoskopic content, + ** thus skip this at the time being. + if ( ( m_EditRate == EditRate_24 && m_SampleRate == EditRate_48 ) + || ( m_EditRate == EditRate_25 && m_SampleRate == EditRate_50 ) + || ( m_EditRate == EditRate_30 && m_SampleRate == EditRate_60 ) + || ( m_EditRate == EditRate_48 && m_SampleRate == EditRate_96 ) + || ( m_EditRate == EditRate_50 && m_SampleRate == EditRate_100 ) + || ( m_EditRate == EditRate_60 && m_SampleRate == EditRate_120 ) + || ( m_EditRate == EditRate_96 && m_SampleRate == EditRate_192 ) + || ( m_EditRate == EditRate_100 && m_SampleRate == EditRate_200 ) + || ( m_EditRate == EditRate_120 && m_SampleRate == EditRate_240 ) ) + { + DefaultLogSink().Debug("File may contain JPEG Interop stereoscopic images.\n"); + return RESULT_SFORMAT; + } + ** + */ + + return RESULT_FORMAT; + } + } + /* + ** thor: support for stereoskopic JPEG XS disabled as it may + ** not yet be supported by a standard. + else if ( type == ASDCP::ESS_JPEG_XS_S ) + { + if ( m_EditRate == EditRate_24 ) + { + if ( m_SampleRate != EditRate_48 ) + { + DefaultLogSink().Error("EditRate and SampleRate not correct for 24/48 stereoscopic essence.\n"); + return RESULT_FORMAT; + } + } + else if ( m_EditRate == EditRate_25 ) + { + if ( m_SampleRate != EditRate_50 ) + { + DefaultLogSink().Error("EditRate and SampleRate not correct for 25/50 stereoscopic essence.\n"); + return RESULT_FORMAT; + } + } + else if ( m_EditRate == EditRate_30 ) + { + if ( m_SampleRate != EditRate_60 ) + { + DefaultLogSink().Error("EditRate and SampleRate not correct for 30/60 stereoscopic essence.\n"); + return RESULT_FORMAT; + } + } + else if ( m_EditRate == EditRate_48 ) + { + if ( m_SampleRate != EditRate_96 ) + { + DefaultLogSink().Error("EditRate and SampleRate not correct for 48/96 stereoscopic essence.\n"); + return RESULT_FORMAT; + } + } + else if ( m_EditRate == EditRate_50 ) + { + if ( m_SampleRate != EditRate_100 ) + { + DefaultLogSink().Error("EditRate and SampleRate not correct for 50/100 stereoscopic essence.\n"); + return RESULT_FORMAT; + } + } + else if ( m_EditRate == EditRate_60 ) + { + if ( m_SampleRate != EditRate_120 ) + { + DefaultLogSink().Error("EditRate and SampleRate not correct for 60/120 stereoscopic essence.\n"); + return RESULT_FORMAT; + } + } + else if ( m_EditRate == EditRate_96 ) + { + if ( m_SampleRate != EditRate_192 ) + { + DefaultLogSink().Error("EditRate and SampleRate not correct for 96/192 stereoscopic essence.\n"); + return RESULT_FORMAT; + } + } + else if ( m_EditRate == EditRate_100 ) + { + if ( m_SampleRate != EditRate_200 ) + { + DefaultLogSink().Error("EditRate and SampleRate not correct for 100/200 stereoscopic essence.\n"); + return RESULT_FORMAT; + } + } + else if ( m_EditRate == EditRate_120 ) + { + if ( m_SampleRate != EditRate_240 ) + { + DefaultLogSink().Error("EditRate and SampleRate not correct for 120/240 stereoscopic essence.\n"); + return RESULT_FORMAT; + } + } + else + { + DefaultLogSink().Error("EditRate not correct for stereoscopic essence: %d/%d.\n", + m_EditRate.Numerator, m_EditRate.Denominator); + return RESULT_FORMAT; + } + } + */ + else + { + DefaultLogSink().Error("'type' argument unexpected: %x\n", type); + return RESULT_STATE; + } + + result = MD_to_JXS_PDesc(*m_EssenceDescriptor, *m_EssenceSubDescriptor, m_EditRate, m_SampleRate, m_PDesc); + } + + return result; +} + +// +// +ASDCP::Result_t +ih__Reader::ReadFrame(ui32_t FrameNum, JXS::FrameBuffer& FrameBuf, + AESDecContext* Ctx, HMACContext* HMAC) +{ + if ( ! m_File.IsOpen() ) + return RESULT_INIT; + + assert(m_Dict); + return ReadEKLVFrame(FrameNum, FrameBuf, m_Dict->ul(MDD_JPEGXSEssence), Ctx, HMAC); +} + +// +Result_t ih__Reader::CalcFrameBufferSize(ui64_t &size) +{ + if ( ! m_File.IsOpen() ) + return RESULT_INIT; + + assert(m_Dict); + return ASDCP::h__ASDCPReader::CalcFrameBufferSize(size); +} + +// +class ASDCP::JXS::MXFReader::h__Reader : public ih__Reader +{ + ASDCP_NO_COPY_CONSTRUCT(h__Reader); + h__Reader(); + +public: + h__Reader(const Dictionary *d) : ih__Reader(d) {} +}; + + + +//------------------------------------------------------------------------------------------ + + +// +void +ASDCP::JXS::FrameBuffer::Dump(FILE* stream, ui32_t dump_len) const +{ + if ( stream == 0 ) + stream = stderr; + + fprintf(stream, "Frame: %06u, %7u bytes", m_FrameNumber, m_Size); + + fputc('\n', stream); + + if ( dump_len > 0 ) + Kumu::hexdump(m_Data, dump_len, stream); +} + + +//------------------------------------------------------------------------------------------ + +ASDCP::JXS::MXFReader::MXFReader() +{ + m_Reader = new h__Reader(&DefaultCompositeDict()); +} + + +ASDCP::JXS::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::OP1aHeader& +ASDCP::JXS::MXFReader::OP1aHeader() +{ + if ( m_Reader.empty() ) + { + assert(g_OP1aHeader); + return *g_OP1aHeader; + } + + 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::JXS::MXFReader::OPAtomIndexFooter() +{ + if ( m_Reader.empty() ) + { + assert(g_OPAtomIndexFooter); + return *g_OPAtomIndexFooter; + } + + return m_Reader->m_IndexAccess; +} + +// Warning: direct manipulation of MXF structures can interfere +// with the normal operation of the wrapper. Caveat emptor! +// +ASDCP::MXF::RIP& +ASDCP::JXS::MXFReader::RIP() +{ + if ( m_Reader.empty() ) + { + assert(g_RIP); + return *g_RIP; + } + + return m_Reader->m_RIP; +} + +// Open the file for reading. The file must exist. Returns error if the +// operation cannot be completed. +ASDCP::Result_t +ASDCP::JXS::MXFReader::OpenRead(const std::string& filename) const +{ + return m_Reader->OpenRead(filename, ASDCP::ESS_JPEG_XS); +} + +// +ASDCP::Result_t +ASDCP::JXS::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::JXS::MXFReader::CalcFrameBufferSize(ui64_t &size) const +{ + if ( m_Reader && m_Reader->m_File.IsOpen() ) + return m_Reader->CalcFrameBufferSize(size); + + return RESULT_INIT; +} + +ASDCP::Result_t +ASDCP::JXS::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::JXS::MXFReader::FillPictureDescriptor(PictureDescriptor& PDesc) const +{ + if ( m_Reader && m_Reader->m_File.IsOpen() ) + { + PDesc = m_Reader->m_PDesc; + return RESULT_OK; + } + + return RESULT_INIT; +} + + +// Fill the struct with the values from the file's header. +// Returns RESULT_INIT if the file is not open. +ASDCP::Result_t +ASDCP::JXS::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::JXS::MXFReader::DumpHeaderMetadata(FILE* stream) const +{ + if ( m_Reader->m_File.IsOpen() ) + m_Reader->m_HeaderPart.Dump(stream); +} + + +// +void +ASDCP::JXS::MXFReader::DumpIndex(FILE* stream) const +{ + if ( m_Reader->m_File.IsOpen() ) + m_Reader->m_IndexAccess.Dump(stream); +} + +// +ASDCP::Result_t +ASDCP::JXS::MXFReader::Close() const +{ + if ( m_Reader && m_Reader->m_File.IsOpen() ) + { + m_Reader->Close(); + return RESULT_OK; + } + + return RESULT_INIT; +} + + +//------------------------------------------------------------------------------------------ + +// +class ih__Writer : public ASDCP::h__ASDCPWriter +{ + ASDCP_NO_COPY_CONSTRUCT(ih__Writer); + ih__Writer(); + + JPEGXSPictureSubDescriptor* m_EssenceSubDescriptor; + +public: + PictureDescriptor m_PDesc; + byte_t m_EssenceUL[SMPTE_UL_LENGTH]; + + ih__Writer(const Dictionary *d) : ASDCP::h__ASDCPWriter(d), m_EssenceSubDescriptor(0) { + memset(m_EssenceUL, 0, SMPTE_UL_LENGTH); + } + + virtual ~ih__Writer(){} + + Result_t OpenWrite(const std::string&, EssenceType_t type, ui32_t HeaderSize); + Result_t SetSourceStream(const PictureDescriptor&, const std::string& label, + ASDCP::Rational LocalEditRate = ASDCP::Rational(0,0)); + Result_t WriteFrame(const JXS::FrameBuffer&, bool add_index, AESEncContext*, HMACContext*); + Result_t Finalize(); +}; + +// Open the file for writing. The file must not exist. Returns error if +// the operation cannot be completed. +ASDCP::Result_t +ih__Writer::OpenWrite(const std::string& filename, EssenceType_t type, ui32_t HeaderSize) +{ + if ( ! m_State.Test_BEGIN() ) + return RESULT_STATE; + + Result_t result = m_File.OpenWrite(filename); + + if ( ASDCP_SUCCESS(result) ) + { + m_HeaderSize = HeaderSize; + RGBAEssenceDescriptor* tmp_rgba = new RGBAEssenceDescriptor(m_Dict); + tmp_rgba->ComponentMaxRef = 4095; + tmp_rgba->ComponentMinRef = 0; + + m_EssenceDescriptor = tmp_rgba; + m_EssenceSubDescriptor = new JPEGXSPictureSubDescriptor(m_Dict); + m_EssenceSubDescriptorList.push_back((InterchangeObject*)m_EssenceSubDescriptor); + + GenRandomValue(m_EssenceSubDescriptor->InstanceUID); + m_EssenceDescriptor->SubDescriptors.push_back(m_EssenceSubDescriptor->InstanceUID); + + /* + ** thor: stereoskopic images are currently disabled. + if ( type == ASDCP::ESS_JPEG_XS_S && m_Info.LabelSetType == LS_MXF_SMPTE ) + { + InterchangeObject* StereoSubDesc = new StereoscopicPictureSubDescriptor(m_Dict); + m_EssenceSubDescriptorList.push_back(StereoSubDesc); + GenRandomValue(StereoSubDesc->InstanceUID); + m_EssenceDescriptor->SubDescriptors.push_back(StereoSubDesc->InstanceUID); + } + ** + */ + + result = m_State.Goto_INIT(); + } + + return result; +} + +// Automatically sets the MXF file's metadata from the first jpeg codestream stream. +ASDCP::Result_t +ih__Writer::SetSourceStream(const PictureDescriptor& PDesc, const std::string& label, ASDCP::Rational LocalEditRate) +{ + assert(m_Dict); + if ( ! m_State.Test_INIT() ) + return RESULT_STATE; + + if ( LocalEditRate == ASDCP::Rational(0,0) ) + LocalEditRate = PDesc.EditRate; + + m_PDesc = PDesc; + assert(m_Dict); + assert(m_EssenceDescriptor); + assert(m_EssenceSubDescriptor); + Result_t result = JXS_PDesc_to_MD(m_PDesc, *m_Dict, + *static_cast<ASDCP::MXF::GenericPictureEssenceDescriptor*>(m_EssenceDescriptor), + *m_EssenceSubDescriptor); + + if ( ASDCP_SUCCESS(result) ) + { + ASDCP::MXF::GenericPictureEssenceDescriptor *gpe = static_cast<ASDCP::MXF::RGBAEssenceDescriptor*>(m_EssenceDescriptor); + switch(PDesc.Ppih) { + case 0: // Profile_Unrestricted + gpe->PictureEssenceCoding.Set(m_Dict->ul(MDD_JPEGXSUnrestrictedCodestream)); + break; + case 0x1500: // Profile_Light422 + gpe->PictureEssenceCoding.Set(m_Dict->ul(MDD_JPEGXSLight422_10Profile)); + break; + case 0x1a00: // Profile_Light444 + gpe->PictureEssenceCoding.Set(m_Dict->ul(MDD_JPEGXSLight444_12Profile)); + break; + case 0x2500: // Profile_LightSubline + gpe->PictureEssenceCoding.Set(m_Dict->ul(MDD_JPEGXSLightSubline422_10Profile)); + break; + case 0x3540: // Profile_Main422 + gpe->PictureEssenceCoding.Set(m_Dict->ul(MDD_JPEGXSMain422_10Profile)); + break; + case 0x3a40: // Profile_Main444 + gpe->PictureEssenceCoding.Set(m_Dict->ul(MDD_JPEGXSMain444_12Profile)); + break; + case 0x3e40: // Profile_Main4444 + gpe->PictureEssenceCoding.Set(m_Dict->ul(MDD_JPEGXSMain4444_12Profile)); + break; + case 0x4a40: // Profile_High444 + gpe->PictureEssenceCoding.Set(m_Dict->ul(MDD_JPEGXSHigh444_12Profile)); + break; + case 0x4e40: // Profile_High4444 + gpe->PictureEssenceCoding.Set(m_Dict->ul(MDD_JPEGXSHigh4444_12Profile)); + break; + default: + return RESULT_PARAM; + } + memcpy(m_EssenceUL, m_Dict->ul(MDD_JPEGXSEssence), SMPTE_UL_LENGTH); + m_EssenceUL[SMPTE_UL_LENGTH-1] = 1; // first (and only) essence container + result = m_State.Goto_READY(); + } + + if ( ASDCP_SUCCESS(result) ) + { + result = WriteASDCPHeader(label, UL(m_Dict->ul(MDD_MXFGCFUFrameWrappedPictureElement)), + PICT_DEF_LABEL, UL(m_EssenceUL), UL(m_Dict->ul(MDD_PictureDataDef)), + LocalEditRate, derive_timecode_rate_from_edit_rate(m_PDesc.EditRate)); + } + + 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 +ih__Writer::WriteFrame(const JXS::FrameBuffer& FrameBuf, bool add_index, + AESEncContext* Ctx, 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, MXF_BER_LENGTH, Ctx, HMAC); + + if ( ASDCP_SUCCESS(result) && add_index ) + { + 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 +ih__Writer::Finalize() +{ + if ( ! m_State.Test_RUNNING() ) + return RESULT_STATE; + + m_State.Goto_FINAL(); + + return WriteASDCPFooter(); +} + + +// +class ASDCP::JXS::MXFWriter::h__Writer : public ih__Writer +{ + ASDCP_NO_COPY_CONSTRUCT(h__Writer); + h__Writer(); + +public: + h__Writer(const Dictionary *d) : ih__Writer(d) {} +}; + + +//------------------------------------------------------------------------------------------ + + + +ASDCP::JXS::MXFWriter::MXFWriter() +{ +} + +ASDCP::JXS::MXFWriter::~MXFWriter() +{ +} + +// Warning: direct manipulation of MXF structures can interfere +// with the normal operation of the wrapper. Caveat emptor! +// +ASDCP::MXF::OP1aHeader& +ASDCP::JXS::MXFWriter::OP1aHeader() +{ + if ( m_Writer.empty() ) + { + assert(g_OP1aHeader); + return *g_OP1aHeader; + } + + 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::JXS::MXFWriter::OPAtomIndexFooter() +{ + if ( m_Writer.empty() ) + { + assert(g_OPAtomIndexFooter); + return *g_OPAtomIndexFooter; + } + + return m_Writer->m_FooterPart; +} + +// Warning: direct manipulation of MXF structures can interfere +// with the normal operation of the wrapper. Caveat emptor! +// +ASDCP::MXF::RIP& +ASDCP::JXS::MXFWriter::RIP() +{ + if ( m_Writer.empty() ) + { + assert(g_RIP); + return *g_RIP; + } + + return m_Writer->m_RIP; +} + +// Open the file for writing. The file must not exist. Returns error if +// the operation cannot be completed. +ASDCP::Result_t +ASDCP::JXS::MXFWriter::OpenWrite(const std::string& filename, const WriterInfo& Info, + const PictureDescriptor& PDesc, ui32_t HeaderSize) +{ + if ( Info.LabelSetType == LS_MXF_SMPTE ) + m_Writer = new h__Writer(&DefaultSMPTEDict()); + else + m_Writer = new h__Writer(&DefaultInteropDict()); + + m_Writer->m_Info = Info; + + Result_t result = m_Writer->OpenWrite(filename, ASDCP::ESS_JPEG_XS, HeaderSize); + + if ( ASDCP_SUCCESS(result) ) + result = m_Writer->SetSourceStream(PDesc, JXS_PACKAGE_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::JXS::MXFWriter::WriteFrame(const FrameBuffer& FrameBuf, AESEncContext* Ctx, HMACContext* HMAC) +{ + if ( m_Writer.empty() ) + return RESULT_INIT; + + return m_Writer->WriteFrame(FrameBuf, true, Ctx, HMAC); +} + +// Closes the MXF file, writing the index and other closing information. +ASDCP::Result_t +ASDCP::JXS::MXFWriter::Finalize() +{ + if ( m_Writer.empty() ) + return RESULT_INIT; + + return m_Writer->Finalize(); +} + +// +// end AS_DCP_JXS.cpp +// diff --git a/src/AS_DCP_internal.h b/src/AS_DCP_internal.h index f3d3c96..d09fa6d 100755 --- a/src/AS_DCP_internal.h +++ b/src/AS_DCP_internal.h @@ -161,6 +161,16 @@ namespace ASDCP const ASDCP::Dictionary& dict, ASDCP::MXF::GenericPictureEssenceDescriptor& EssenceDescriptor, ASDCP::MXF::JPEG2000PictureSubDescriptor& EssenceSubDescriptor); + + Result_t MD_to_JXS_PDesc(const ASDCP::MXF::GenericPictureEssenceDescriptor& EssenceDescriptor, + const ASDCP::MXF::JPEGXSPictureSubDescriptor& EssenceSubDescriptor, + const ASDCP::Rational& EditRate, const ASDCP::Rational& SampleRate, + ASDCP::JXS::PictureDescriptor& PDesc); + + Result_t JXS_PDesc_to_MD(const JXS::PictureDescriptor& PDesc, + const ASDCP::Dictionary& dict, + ASDCP::MXF::GenericPictureEssenceDescriptor& EssenceDescriptor, + ASDCP::MXF::JPEGXSPictureSubDescriptor& EssenceSubDescriptor); Result_t PCM_ADesc_to_MD(PCM::AudioDescriptor& ADesc, ASDCP::MXF::WaveAudioDescriptor* ADescObj); Result_t MD_to_PCM_ADesc(ASDCP::MXF::WaveAudioDescriptor* ADescObj, PCM::AudioDescriptor& ADesc); @@ -378,6 +388,21 @@ namespace ASDCP FrameNum, SequenceNum, FrameBuf, EssenceUL, Ctx, HMAC); } + // + // Return the size of the packet + Result_t CalcFrameBufferSize(ui64_t &PacketLength) + { + KLReader Reader; + Result_t result = Reader.ReadKLFromFile(m_File); + + if ( KM_FAILURE(result) ) + return result; + + PacketLength = Reader.Length(); + + return result; + } + // Get the position of a frame from a track file Result_t LocateFrame(const ui64_t& body_offset, ui32_t FrameNum, Kumu::fpos_t& streamOffset, diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 85883a1..50d0b64 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -48,9 +48,12 @@ set(kumu_src ${kumu_src} KM_fileio.h KM_log.h KM_prng.h KM_util.h KM_tai.h KM_er # ----------libasdcp----------
# source
-set(asdcp_src MPEG2_Parser.cpp MPEG.cpp JP2K_Codestream_Parser.cpp JP2K_Sequence_Parser.cpp JP2K.cpp PCM_Parser.cpp Wav.cpp
+set(asdcp_src MPEG2_Parser.cpp MPEG.cpp JP2K_Codestream_Parser.cpp
+ JP2K_Sequence_Parser.cpp JP2K.cpp PCM_Parser.cpp Wav.cpp
+ JXS_Codestream_Parser.cpp JXS_Sequence_Parser.cpp JXS.cpp
KLV.cpp Dict.cpp MXFTypes.cpp MXF.cpp Index.cpp Metadata.cpp AS_DCP.cpp AS_DCP_MXF.cpp TimedText_Parser.cpp
- h__Reader.cpp h__Writer.cpp AS_DCP_MPEG2.cpp AS_DCP_JP2K.cpp AS_DCP_PCM.cpp AS_DCP_TimedText.cpp PCMParserList.cpp MDD.cpp
+ h__Reader.cpp h__Writer.cpp AS_DCP_MPEG2.cpp AS_DCP_JP2K.cpp
+ AS_DCP_JXS.cpp AS_DCP_PCM.cpp AS_DCP_TimedText.cpp PCMParserList.cpp MDD.cpp
AS_DCP_ATMOS.cpp AS_DCP_DCData.cpp DCData_ByteStream_Parser.cpp DCData_Sequence_Parser.cpp AtmosSyncChannel_Generator.cpp
AtmosSyncChannel_Mixer.cpp PCMDataProviders.cpp SyncEncoder.c CRC16.c UUIDInformation.c
)
@@ -63,14 +66,16 @@ endif() set(asdcp_deploy_header AS_DCP.h PCMParserList.h AS_DCP_internal.h KM_error.h KM_fileio.h KM_util.h KM_memio.h KM_tai.h KM_platform.h KM_log.h KM_mutex.h dirent_win.h)
# header
-set(asdcp_src ${asdcp_src} 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
+set(asdcp_src ${asdcp_src} Wav.h WavFileWriter.h MXF.h Metadata.h JP2K.h
+JXS.h AS_DCP.h AS_DCP_internal.h KLV.h MPEG.h MXFTypes.h MDD.h
PCMParserList.h S12MTimecode.h AtmosSyncChannel_Generator.h AtmosSyncChannel_Mixer.h PCMDataProviders.h
SyncEncoder.h SyncCommon.h CRC16.h UUIDInformation.h dirent_win.h
)
# ----------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 AS_02_IAB.cpp ST2052_TextParser.cpp)
+set(as02_src h__02_Reader.cpp h__02_Writer.cpp AS_02_ISXD.cpp AS_02_JP2K.cpp
+AS_02_JXS.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 ST2052_TextParser.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 AS_02_IAB.h AS_02_internal.h)
diff --git a/src/JXS.cpp b/src/JXS.cpp new file mode 100644 index 0000000..0cb6eed --- /dev/null +++ b/src/JXS.cpp @@ -0,0 +1,156 @@ +/* +Copyright (c) 2004-2013, John Hurst, +Copyright (c) 2020, Thomas Richter Fraunhofer IIS, +Copyright (c) 2020, Christian Minuth Fraunhofer IIS, +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 JXS.h +\version $Id$ +\brief JPEG XS parser implementation + +This is not a complete enumeration of all things JXS.There is just enough here to +support parsing picture metadata from a codestream header. +*/ + +#include "JXS.h" +#include <KM_log.h> +using Kumu::DefaultLogSink; + +// +ASDCP::Result_t +ASDCP::JXS::GetNextMarker(const byte_t** buf, JXS::Marker& Marker) +{ + assert((buf != 0) && (*buf != 0)); + + if (*(*buf)++ != 0xff) + return ASDCP::RESULT_FAIL; + + Marker.m_Type = (Marker_t)(0xff00 | *(*buf)++); + Marker.m_IsSegment = Marker.m_Type != MRK_SOC && Marker.m_Type != MRK_SLH && Marker.m_Type != MRK_EOC; + + if (Marker.m_IsSegment) + { + Marker.m_DataSize = *(*buf)++ << 8; + Marker.m_DataSize |= *(*buf)++; + Marker.m_DataSize -= 2; + Marker.m_Data = *buf; + *buf += Marker.m_DataSize; + } + + return ASDCP::RESULT_OK; +} + +// +const char* +ASDCP::JXS::GetMarkerString(Marker_t m) +{ + switch (m) + { + case MRK_NIL: return "NIL"; break; + case MRK_SOC: return "SOC: Start of codestream"; break; + case MRK_EOC: return "SOT: End of codestream"; break; + case MRK_PIH: return "PIH: Picture header"; break; + case MRK_CDT: return "CDT: Component table"; break; + case MRK_WGT: return "WGT: Weights table"; break; + case MRK_COM: return "COM: Extension marker"; break; + case MRK_NLT: return "NLT: Nonlinearity marker"; break; + case MRK_CWD: return "CWD: Component dependent wavelet decomposition marker"; break; + case MRK_CTS: return "CTS: Colour transformation specification marker"; break; + case MRK_CRG: return "CRG: Component registration marker"; break; + case MRK_SLH: return "SLH: Slice header"; break; + case MRK_CAP: return "CAP: Capabilities marker"; break; + } + + return "Unknown marker code"; +} + +//TODO: Dump functions for markers + +// +void +ASDCP::JXS::Accessor::CAP::Dump(FILE* stream) const +{ + if (stream == 0) + stream = stderr; + + fprintf(stream, "CAP: \n"); + fprintf(stream, " Size: %hu\n", Size()); + Kumu::hexdump(m_MarkerData, m_DataSize, stream); +} + +// +void +ASDCP::JXS::Accessor::NLT::Dump(FILE* stream) const +{ + if (stream == 0) + stream = stderr; + + fprintf(stream, "SLZ: \n"); + fprintf(stream, " Size: %hu\n", Size()); +} + +// +void +ASDCP::JXS::Accessor::PIH::Dump(FILE* stream) const +{ + if (stream == 0) + stream = stderr; + + fprintf(stream, "PIH: \n"); + fprintf(stream, " LpihSize: %hu\n", LpihSize()); + fprintf(stream, " LcodSize: %hu\n", LcodSize()); + fprintf(stream, " Ppih: %hu\n", Ppih()); + fprintf(stream, " Plev: %hu\n", Plev()); + fprintf(stream, " Wf: %hu\n", Wf()); + fprintf(stream, " Hf: %hu\n", Hf()); + fprintf(stream, " Cw: %hu\n", Cw()); + fprintf(stream, " Hsl: %hu\n", Hsl()); + fprintf(stream, " Nc: %hu\n", Nc()); + fprintf(stream, " Ng: %hu\n", Ng()); + fprintf(stream, " Ss: %hu\n", Ss()); + fprintf(stream, " Cpih: %hu\n", Cpih()); + fprintf(stream, " Nlx: %hu\n", Nlx()); + fprintf(stream, " Nly: %hu\n", Nly()); + Kumu::hexdump(m_MarkerData, m_DataSize, stream); +} + +// +void +ASDCP::JXS::Accessor::CDT::Dump(FILE* stream) const +{ + if (stream == 0) + stream = stderr; + + fprintf(stream, "CDT: \n"); + + for (ui32_t i = 0; i < 3; i++) { + fprintf(stream, " Component %u Bc: %hu\n", i,Bc(i)); + fprintf(stream, " Component %u Sx: %hu\n", i,Sx(i)); + fprintf(stream, " Component %u Sy: %hu\n", i,Sy(i)); + } +} +// +// end JXS.cpp +// diff --git a/src/JXS.h b/src/JXS.h new file mode 100644 index 0000000..bdfc57e --- /dev/null +++ b/src/JXS.h @@ -0,0 +1,239 @@ +/* +Copyright (c) 2005-2014, John Hurst, +Copyright (c) 2020, Thomas Richter Fraunhofer IIS, +Copyright (c) 2020, Christian Minuth Fraunhofer IIS, +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 JXS.h +\version $Id$ +\brief JPEG XS constants and data structures + +This is not a complete enumeration of all things JXS.There is just enough here to +support parsing picture metadata from a codestream header. +*/ + +#ifndef _JXS_H_ +#define _JXS_H_ + +// AS_DCP.h is included only for it's base type definitions. +#include <KM_platform.h> +#include <KM_util.h> +#include <AS_DCP.h> +#include <assert.h> + +namespace ASDCP +{ + namespace JXS + { + const byte_t Magic[] = { 0xff, 0x10, 0xff }; + + enum Marker_t + { + MRK_NIL = 0, + MRK_SOC = 0xff10, // Start of codestream + MRK_EOC = 0xff11, // End of codestream + MRK_PIH = 0xff12, // Picture header + MRK_CDT = 0xff13, // Component table + MRK_WGT = 0xff14, // Weights table + MRK_COM = 0xff15, // Extensions marker + MRK_NLT = 0xff16, // Nonlinearity marker (21122-1 2nd) + MRK_CWD = 0xff17, // Component-dependent decomposition marker (21122-1 2nd) + MRK_CTS = 0xff18, // Colour transformation spec marker (21122-1 2nd) + MRK_CRG = 0xff19, // Component registration marker (21122-1 2nd) + MRK_SLH = 0xff20, // Slice header + MRK_CAP = 0xff50 // Capabilities marker + }; + + const char* GetMarkerString(Marker_t m); + + // + class Marker + { + KM_NO_COPY_CONSTRUCT(Marker); + + public: + Marker_t m_Type; + bool m_IsSegment; + ui32_t m_DataSize; + const byte_t* m_Data; + + Marker() : m_Type(MRK_NIL), m_IsSegment(false), m_DataSize(0), m_Data(0) {} + ~Marker() {} + + void Dump(FILE* stream = 0) const; + }; + + // + ASDCP::Result_t GetNextMarker(const byte_t**, Marker&); + + // accessor objects for marker segments + namespace Accessor + { + // capability marker + class CAP + { + const byte_t* m_MarkerData; + ui32_t m_DataSize; + KM_NO_COPY_CONSTRUCT(CAP); + CAP(); + + public: + CAP(const Marker& M) + { + assert(M.m_Type == MRK_CAP); + m_MarkerData = M.m_Data; + m_DataSize = M.m_DataSize -2; + } + + ~CAP() {} + + inline ui16_t Size() const { return KM_i16_BE(*(ui16_t*)(m_MarkerData -2 )); } + + void Dump(FILE* stream = 0) const; + }; + + // Nonlinearity marker(21122 - 1 2nd) + class NLT + { + const byte_t* m_MarkerData; + KM_NO_COPY_CONSTRUCT(NLT); + NLT(); + + public: + NLT(const Marker& M) + { + assert(M.m_Type == MRK_NLT); + m_MarkerData = M.m_Data; + } + + ~NLT() {} + + inline ui16_t Size() const { return KM_i16_BE(*(ui16_t*)(m_MarkerData - 2)); } + + void Dump(FILE* stream = 0) const; + }; + + // picture header + class PIH + { + const byte_t* m_MarkerData; + ui32_t m_DataSize; + KM_NO_COPY_CONSTRUCT(PIH); + PIH(); + + public: + PIH(const Marker& M) + { + assert(M.m_Type == MRK_PIH); + m_MarkerData = M.m_Data; + m_DataSize = M.m_DataSize; + } + + ~PIH() {} + + inline ui16_t LpihSize() const { return KM_i16_BE(*(ui16_t*)(m_MarkerData - 2)); } + inline ui32_t LcodSize() const { return KM_i32_BE(*(ui32_t*)(m_MarkerData)); } + inline ui16_t Ppih() const { return KM_i16_BE(*(ui16_t*)(m_MarkerData + 4)); } + inline ui16_t Plev() const { return KM_i16_BE(*(ui16_t*)(m_MarkerData + 6)); } + inline ui16_t Wf() const { return KM_i16_BE(*(ui16_t*)(m_MarkerData + 8)); } + inline ui16_t Hf() const { return KM_i16_BE(*(ui16_t*)(m_MarkerData + 10)); } + inline ui16_t Cw() const { return KM_i16_BE(*(ui16_t*)(m_MarkerData + 12)); } + inline ui16_t Hsl() const { return KM_i16_BE(*(ui16_t*)(m_MarkerData + 14)); } + inline ui8_t Nc() const { return (*(ui8_t*)(m_MarkerData + 16)); } + inline ui8_t Ng() const { return *(ui8_t*)(m_MarkerData + 17); } + inline ui8_t Ss() const { return *(ui8_t*)(m_MarkerData + 18); } + inline ui8_t Cpih() const { return (*(ui8_t*)(m_MarkerData + 21)) & 0x0f; } + inline ui8_t Nlx() const { return (*(ui8_t*)(m_MarkerData + 22)) >> 4; } + inline ui8_t Nly() const { return (*(ui8_t*)(m_MarkerData + 22)) & 0x0f; } + + void Dump(FILE* stream = 0) const; + }; + + class CDT + { + const byte_t* m_MarkerData; + KM_NO_COPY_CONSTRUCT(CDT); + CDT(); + + public: + CDT(const Marker& M) + { + assert(M.m_Type == MRK_CDT); + m_MarkerData = M.m_Data; + } + + ~CDT() {} + + inline ui8_t Bc(int i) const { return *(m_MarkerData + (i << 1)); } + inline ui8_t Sx(int i) const { return *(m_MarkerData + 1 + (i << 1)) >> 4; } + inline ui8_t Sy(int i) const { return *(m_MarkerData + 1 + (i << 1)) & 0x0f; } + + void Dump(FILE* stream = 0) const; + }; + + class WGT + { + const byte_t* m_MarkerData; + KM_NO_COPY_CONSTRUCT(WGT); + WGT(); + + public: + WGT(const Marker& M) + { + assert(M.m_Type == MRK_WGT); + m_MarkerData = M.m_Data; + } + + ~WGT() {} + + void Dump(FILE* stream = 0) const; + }; + + class COM + { + const byte_t* m_MarkerData; + KM_NO_COPY_CONSTRUCT(COM); + COM(); + + public: + COM(const Marker& M) + { + assert(M.m_Type == MRK_COM); + m_MarkerData = M.m_Data; + } + + ~COM() {} + + void Dump(FILE* stream = 0) const; + }; + } + } //namespace JXS +} // namespace ASDCP + +#endif // _JXS_H_ + +// +// end JP2K.h +//
\ No newline at end of file diff --git a/src/JXS_Codestream_Parser.cpp b/src/JXS_Codestream_Parser.cpp new file mode 100644 index 0000000..c27dd45 --- /dev/null +++ b/src/JXS_Codestream_Parser.cpp @@ -0,0 +1,251 @@ +/* +Copyright (c) 2004-2013, John Hurst +Copyright (c) 2020, Thomas Richter Fraunhofer IIS, +Copyright (c) 2020, Christian Minuth Fraunhofer IIS, +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 JXS_Codestream_Parser.cpp + \version $Id$ + \brief AS-DCP library, JPEG XS codestream essence reader implementation +*/ + +#include <KM_fileio.h> +#include <AS_DCP.h> +#include <JXS.h> +#include <assert.h> +#include <KM_log.h> +using Kumu::DefaultLogSink; + +//------------------------------------------------------------------------------------------ + +class ASDCP::JXS::CodestreamParser::h__CodestreamParser +{ + ASDCP_NO_COPY_CONSTRUCT(h__CodestreamParser); + +public: + PictureDescriptor m_PDesc; + Kumu::FileReader m_File; + + h__CodestreamParser() + { + memset(&m_PDesc, 0, sizeof(m_PDesc)); + m_PDesc.EditRate = Rational(24, 1); + m_PDesc.SampleRate = m_PDesc.EditRate; + } + + ~h__CodestreamParser() {} + + Result_t OpenReadFrame(const std::string& filename, FrameBuffer& FB) + { + 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); + + if (ASDCP_SUCCESS(result)) + { + byte_t start_of_data = 0; // out param + result = ParseMetadataIntoDesc(FB, m_PDesc, &start_of_data); + + if (ASDCP_SUCCESS(result)) + FB.PlaintextOffset(start_of_data); + } + + return result; + } +}; + +ASDCP::Result_t +ASDCP::JXS::ParseMetadataIntoDesc(const FrameBuffer& FB, PictureDescriptor& PDesc, byte_t* start_of_data) +{ + Result_t result = RESULT_OK; + Marker NextMarker; + ui32_t i; + const byte_t* p = FB.RoData(); + const byte_t* end_p = p + FB.Size(); + + /* initialize optional items */ + bool pih = false; + bool havesoc = false; + + while (p < end_p && ASDCP_SUCCESS(result)) + { + result = GetNextMarker(&p, NextMarker); + + if (ASDCP_FAILURE(result)) + { + result = RESULT_RAW_ESS; + break; + } + + switch (NextMarker.m_Type) + { + case MRK_SOC: + { + // This is the SOC symbol. + if (havesoc) + DefaultLogSink().Error("JXS::ParseMetadataIntoDesc: found a duplicate SOC marker"); + havesoc = true; + } + break; + case MRK_EOC: + { + DefaultLogSink().Error("JXS::ParseMetadataIntoDesc: found an EOC before any actual picture data"); + } + break; + + case MRK_PIH: + { + // This is the real picture header. + + if (!havesoc) + DefaultLogSink().Error("JXS::ParseMetadataIntoDesc: the SOC marker is missing at the start of the frame"); + if (pih) { + DefaultLogSink().Error("JXS::ParseMetadataIntoDesc: found a duplicate picture header"); + } + else { + ui32_t size; + ui8_t flags; + // + + Accessor::PIH PIH_(NextMarker); + pih = true; + size = PIH_.LpihSize(); + + if (size != 28 - 2) + DefaultLogSink().Error("JXS::ParseMetadataIntoDesc: the jpeg xs picture header has an unsupported size"); + + // size of the bitstream: ignore, just store away. + size = PIH_.LcodSize(); + // Profile and level + PDesc.Ppih = PIH_.Ppih(); + PDesc.Plev = PIH_.Plev(); + // Width and Height + PDesc.AspectRatio = Rational(PIH_.Wf(), PIH_.Hf()); + PDesc.StoredWidth = PIH_.Wf(); + PDesc.StoredHeight = PIH_.Hf(); + PDesc.Wf = PIH_.Wf(); + PDesc.Hf = PIH_.Hf(); + PDesc.Cw = PIH_.Cw(); + PDesc.Hsl = PIH_.Hsl(); + if (PIH_.Hsl() < 1 || PIH_.Hsl() > 0xffff) // This includes the EOF check + DefaultLogSink().Error("JXS::ParseMetadataIntoDesc: unsupported slice height specified, must be > 0 and < 65536"); + // Number of compoennts. + PDesc.Nc = PIH_.Nc(); + if (PDesc.Nc != 3) + { + DefaultLogSink().Error("Unexpected number of components: %u\n", PDesc.Nc); + return RESULT_RAW_FORMAT; + } + // A lot of settings that must be fixed right now. + if (PIH_.Ng() != 4) + DefaultLogSink().Error("JXS::ParseMetadataIntoDesc: the number of coefficients per coding group must be 4"); + if (PIH_.Ss() != 8) + DefaultLogSink().Error("JXS::ParseMetadataIntoDesc: the number of coding groups per significance group must be 8"); + // + if (PIH_.Nlx() == 0) + DefaultLogSink().Error("JXS::ParseMetadataIntoDesc: number of horizontal decomposition levels is out of range, must be >0"); + if (PIH_.Nly() > MaxVerticalLevels) + DefaultLogSink().Error("JXS::ParseMetadataIntoDesc: number of vertical decomposition levels is out of range, must be <2"); + } + } + break; + case MRK_CDT: + { + if(pih) { + Accessor::CDT CDT_(NextMarker); + int i, count = NextMarker.m_DataSize >> 1; + + for (i = 0; i < count && i < PDesc.Nc; i++) { + PDesc.ImageComponents[i].Bc = CDT_.Bc(i); + PDesc.ImageComponents[i].Sx = CDT_.Sx(i); + PDesc.ImageComponents[i].Sy = CDT_.Sy(i); + } + } + else { + DefaultLogSink().Error("JXS::ParseMetadataIntoDesc: found a component table marker upfront the picture header"); + } + } + break; + case MRK_SLH: /* slice header: the entropy coded data starts here */ + if (start_of_data != 0) + *start_of_data = p - FB.RoData(); + + p = end_p; + break; + } +} + return result; +} +//------------------------------------------------------------------------------------------ + +ASDCP::JXS::CodestreamParser::CodestreamParser() +{ +} + +ASDCP::JXS::CodestreamParser::~CodestreamParser() +{ +} + +// Opens the stream for reading, parses enough data to provide a complete +// set of stream metadata for the MXFWriter below. +ASDCP::Result_t +ASDCP::JXS::CodestreamParser::OpenReadFrame(const std::string& filename, FrameBuffer& FB) const +{ + const_cast<ASDCP::JXS::CodestreamParser*>(this)->m_Parser = new h__CodestreamParser; + return m_Parser->OpenReadFrame(filename, FB); +} + +// +ASDCP::Result_t +ASDCP::JXS::CodestreamParser::FillPictureDescriptor(PictureDescriptor& PDesc) const +{ + if (m_Parser.empty()) + return RESULT_INIT; + + PDesc = m_Parser->m_PDesc; + return RESULT_OK; +} + +// +// end JXS_Codestream_Parser.cpp +// diff --git a/src/JXS_Sequence_Parser.cpp b/src/JXS_Sequence_Parser.cpp new file mode 100644 index 0000000..f16e730 --- /dev/null +++ b/src/JXS_Sequence_Parser.cpp @@ -0,0 +1,281 @@ +/* +Copyright (c) 2004-2013, John Hurst, +Copyright (c) 2020, Thomas Richter Fraunhofer IIS, +Copyright (c) 2020, Christian Minuth Fraunhofer IIS, +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 JXS_Sequence_Parser.cpp + \version $Id$ + \brief AS-DCP library, JPEG XS sequence codestream essence reader implementation +*/ + +#include <AS_DCP.h> +#include <KM_fileio.h> +#include <KM_log.h> +#include <list> +#include <string> +#include <algorithm> +#include <string.h> +#include <assert.h> + +using namespace ASDCP; + +//------------------------------------------------------------------------------------------ + +class FileList : public std::list<std::string> +{ + std::string m_DirName; + +public: + FileList() {} + ~FileList() {} + + 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 std::string& 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::JXS::SequenceParser::h__SequenceParser +{ + ui32_t m_FramesRead; + Rational m_PictureRate; + FileList m_FileList; + FileList::iterator m_CurrentFile; + CodestreamParser m_Parser; + + Result_t OpenRead(); + + ASDCP_NO_COPY_CONSTRUCT(h__SequenceParser); + +public: + PictureDescriptor m_PDesc; + + h__SequenceParser() : m_FramesRead(0) + { + memset(&m_PDesc, 0, sizeof(m_PDesc)); + m_PDesc.EditRate = Rational(24, 1); + } + + ~h__SequenceParser() + { + Close(); + } + + Result_t OpenRead(const std::string& 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::JXS::SequenceParser::h__SequenceParser::OpenRead() +{ + if (m_FileList.empty()) + return RESULT_ENDOFFILE; + + m_CurrentFile = m_FileList.begin(); + CodestreamParser 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.FillPictureDescriptor(m_PDesc); + + // how big is it? + if (ASDCP_SUCCESS(result)) + m_PDesc.ContainerDuration = m_FileList.size(); + + return result; +} + +// +ASDCP::Result_t +ASDCP::JXS::SequenceParser::h__SequenceParser::OpenRead(const std::string& filename) +{ + Result_t result = m_FileList.InitFromDirectory(filename); + + if (ASDCP_SUCCESS(result)) + result = OpenRead(); + + return result; +} + +// +ASDCP::Result_t +ASDCP::JXS::SequenceParser::h__SequenceParser::OpenRead(const std::list<std::string>& file_list) +{ + m_FileList = file_list; + return OpenRead(); +} + +// +ASDCP::Result_t +ASDCP::JXS::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::JXS::SequenceParser::SequenceParser() +{ +} + +ASDCP::JXS::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::JXS::SequenceParser::OpenRead(const std::string& filename) const +{ + const_cast<ASDCP::JXS::SequenceParser*>(this)->m_Parser = new h__SequenceParser; + + Result_t result = m_Parser->OpenRead(filename); + + if (ASDCP_FAILURE(result)) + const_cast<ASDCP::JXS::SequenceParser*>(this)->m_Parser.release(); + + return result; +} + +// +Result_t +ASDCP::JXS::SequenceParser::OpenRead(const std::list<std::string>& file_list) const +{ + const_cast<ASDCP::JXS::SequenceParser*>(this)->m_Parser = new h__SequenceParser; + + Result_t result = m_Parser->OpenRead(file_list); + + if (ASDCP_FAILURE(result)) + const_cast<ASDCP::JXS::SequenceParser*>(this)->m_Parser.release(); + + return result; +} + + +// Rewinds the stream to the beginning. +ASDCP::Result_t +ASDCP::JXS::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::JXS::SequenceParser::ReadFrame(FrameBuffer& FB) const +{ + if (m_Parser.empty()) + return RESULT_INIT; + + return m_Parser->ReadFrame(FB); +} + +// +ASDCP::Result_t +ASDCP::JXS::SequenceParser::FillPictureDescriptor(PictureDescriptor& PDesc) const +{ + if (m_Parser.empty()) + return RESULT_INIT; + + PDesc = m_Parser->m_PDesc; + return RESULT_OK; +} + +// +// end JXS_Sequence_Parser.cpp +//
\ No newline at end of file diff --git a/src/Makefile.am b/src/Makefile.am index 1d8780f..60ef423 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -65,6 +65,7 @@ include_HEADERS += \ Metadata.h \ KLV.h \ JP2K.h \ + JXS.h \ MXFTypes.h \ MXF.h \ Wav.h \ @@ -118,13 +119,14 @@ nodist_libasdcp_la_SOURCES = Metadata_h.tt2 Metadata_cpp.tt2 \ # sources for asdcp library libasdcp_la_SOURCES = MPEG2_Parser.cpp MPEG.cpp JP2K_Codestream_Parser.cpp \ - JP2K_Sequence_Parser.cpp JP2K.cpp PCM_Parser.cpp Wav.cpp \ - TimedText_Parser.cpp KLV.cpp Dict.cpp MXFTypes.cpp MXF.cpp \ + JP2K_Sequence_Parser.cpp JP2K.cpp \ + JXS_Codestream_Parser.cpp JXS_Sequence_Parser.cpp JXS.cpp \ + PCM_Parser.cpp Wav.cpp TimedText_Parser.cpp KLV.cpp Dict.cpp MXFTypes.cpp MXF.cpp \ Index.cpp Metadata.cpp AS_DCP.cpp AS_DCP_MXF.cpp \ h__Reader.cpp h__Writer.cpp AS_DCP_MPEG2.cpp AS_DCP_JP2K.cpp \ - AS_DCP_PCM.cpp AS_DCP_TimedText.cpp PCMParserList.cpp \ + AS_DCP_JXS.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 \ + JP2K.h JXS.h AS_DCP.h AS_DCP_internal.h KLV.h MPEG.h MXFTypes.h MDD.h \ PCMParserList.h S12MTimecode.h MDD.cpp \ AS_DCP_ATMOS.cpp AS_DCP_DCData.cpp info.in \ DCData_ByteStream_Parser.cpp DCData_Sequence_Parser.cpp \ @@ -155,6 +157,7 @@ libas02_la_SOURCES = \ h__02_Reader.cpp \ h__02_Writer.cpp \ AS_02_JP2K.cpp \ + AS_02_JXS.cpp \ AS_02_PCM.cpp \ AS_02_ISXD.cpp \ ST2052_TextParser.cpp \ diff --git a/src/Metadata.cpp b/src/Metadata.cpp index 455ba2a..338b5ee 100644 --- a/src/Metadata.cpp +++ b/src/Metadata.cpp @@ -59,6 +59,7 @@ static InterchangeObject* WaveAudioDescriptor_Factory(const Dictionary* Dict) { static InterchangeObject* GenericPictureEssenceDescriptor_Factory(const Dictionary* Dict) { return new GenericPictureEssenceDescriptor(Dict); } static InterchangeObject* RGBAEssenceDescriptor_Factory(const Dictionary* Dict) { return new RGBAEssenceDescriptor(Dict); } static InterchangeObject* JPEG2000PictureSubDescriptor_Factory(const Dictionary* Dict) { return new JPEG2000PictureSubDescriptor(Dict); } +static InterchangeObject* JPEGXSPictureSubDescriptor_Factory(const Dictionary* Dict) { return new JPEGXSPictureSubDescriptor(Dict); } static InterchangeObject* CDCIEssenceDescriptor_Factory(const Dictionary* Dict) { return new CDCIEssenceDescriptor(Dict); } static InterchangeObject* MPEG2VideoDescriptor_Factory(const Dictionary* Dict) { return new MPEG2VideoDescriptor(Dict); } static InterchangeObject* DMSegment_Factory(const Dictionary* Dict) { return new DMSegment(Dict); } @@ -114,6 +115,7 @@ ASDCP::MXF::Metadata_InitTypes(const Dictionary* Dict) SetObjectFactory(Dict->ul(MDD_GenericPictureEssenceDescriptor), GenericPictureEssenceDescriptor_Factory); SetObjectFactory(Dict->ul(MDD_RGBAEssenceDescriptor), RGBAEssenceDescriptor_Factory); SetObjectFactory(Dict->ul(MDD_JPEG2000PictureSubDescriptor), JPEG2000PictureSubDescriptor_Factory); + SetObjectFactory(Dict->ul(MDD_JPEGXSPictureSubDescriptor), JPEGXSPictureSubDescriptor_Factory); SetObjectFactory(Dict->ul(MDD_CDCIEssenceDescriptor), CDCIEssenceDescriptor_Factory); SetObjectFactory(Dict->ul(MDD_MPEG2VideoDescriptor), MPEG2VideoDescriptor_Factory); SetObjectFactory(Dict->ul(MDD_DMSegment), DMSegment_Factory); @@ -2395,6 +2397,139 @@ JPEG2000PictureSubDescriptor::WriteToBuffer(ASDCP::FrameBuffer& Buffer) } //------------------------------------------------------------------------------------------ +// JPEGXSPictureSubDescriptor + +// +JPEGXSPictureSubDescriptor::JPEGXSPictureSubDescriptor(const Dictionary* d) : InterchangeObject(d), + JPEGXSPpih(0), JPEGXSPlev(0), + JPEGXSWf(0), JPEGXSHf(0), JPEGXSNc(0) +{ + assert(m_Dict); + m_UL = m_Dict->ul(MDD_JPEGXSPictureSubDescriptor); +} + +JPEGXSPictureSubDescriptor::JPEGXSPictureSubDescriptor(const JPEGXSPictureSubDescriptor& rhs) : InterchangeObject(rhs.m_Dict) +{ + assert(m_Dict); + m_UL = m_Dict->ul(MDD_JPEGXSPictureSubDescriptor); + Copy(rhs); +} + + +// +ASDCP::Result_t +JPEGXSPictureSubDescriptor::InitFromTLVSet(TLVReader& TLVSet) +{ + assert(m_Dict); + Result_t result = InterchangeObject::InitFromTLVSet(TLVSet); + if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadUi16(OBJ_READ_ARGS(JPEGXSPictureSubDescriptor, JPEGXSPpih)); + if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadUi16(OBJ_READ_ARGS(JPEGXSPictureSubDescriptor, JPEGXSPlev)); + if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadUi16(OBJ_READ_ARGS(JPEGXSPictureSubDescriptor, JPEGXSWf)); + if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadUi16(OBJ_READ_ARGS(JPEGXSPictureSubDescriptor, JPEGXSHf)); + if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadUi8(OBJ_READ_ARGS(JPEGXSPictureSubDescriptor, JPEGXSNc)); + if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadObject(OBJ_READ_ARGS(JPEGXSPictureSubDescriptor, JPEGXSComponentTable)); + if ( ASDCP_SUCCESS(result) ) { + result = TLVSet.ReadUi16(OBJ_READ_ARGS_OPT(JPEGXSPictureSubDescriptor, JPEGXSCw)); + JPEGXSCw.set_has_value( result == RESULT_OK ); + } + if ( ASDCP_SUCCESS(result) ) { + result = TLVSet.ReadUi16(OBJ_READ_ARGS_OPT(JPEGXSPictureSubDescriptor, JPEGXSHsl)); + JPEGXSHsl.set_has_value( result == RESULT_OK ); + } + if ( ASDCP_SUCCESS(result) ) { + result = TLVSet.ReadUi32(OBJ_READ_ARGS_OPT(JPEGXSPictureSubDescriptor, JPEGXSMaximumBitRate)); + JPEGXSMaximumBitRate.set_has_value( result == RESULT_OK ); + } + + return result; +} + +// +ASDCP::Result_t +JPEGXSPictureSubDescriptor::WriteToTLVSet(TLVWriter& TLVSet) +{ + assert(m_Dict); + Result_t result = InterchangeObject::WriteToTLVSet(TLVSet); + if ( ASDCP_SUCCESS(result) ) result = TLVSet.WriteUi16(OBJ_WRITE_ARGS(JPEGXSPictureSubDescriptor, JPEGXSPpih)); + if ( ASDCP_SUCCESS(result) ) result = TLVSet.WriteUi16(OBJ_WRITE_ARGS(JPEGXSPictureSubDescriptor, JPEGXSPlev)); + if ( ASDCP_SUCCESS(result) ) result = TLVSet.WriteUi16(OBJ_WRITE_ARGS(JPEGXSPictureSubDescriptor, JPEGXSWf)); + if ( ASDCP_SUCCESS(result) ) result = TLVSet.WriteUi16(OBJ_WRITE_ARGS(JPEGXSPictureSubDescriptor, JPEGXSHf)); + if ( ASDCP_SUCCESS(result) ) result = TLVSet.WriteUi8(OBJ_WRITE_ARGS(JPEGXSPictureSubDescriptor, JPEGXSNc)); + if ( ASDCP_SUCCESS(result) ) result = TLVSet.WriteObject(OBJ_WRITE_ARGS(JPEGXSPictureSubDescriptor, JPEGXSComponentTable)); + if ( ASDCP_SUCCESS(result) && ! JPEGXSCw.empty() ) result = TLVSet.WriteUi16(OBJ_WRITE_ARGS_OPT(JPEGXSPictureSubDescriptor, JPEGXSCw)); + if ( ASDCP_SUCCESS(result) && ! JPEGXSHsl.empty() ) result = TLVSet.WriteUi16(OBJ_WRITE_ARGS_OPT(JPEGXSPictureSubDescriptor, JPEGXSHsl)); + if ( ASDCP_SUCCESS(result) && ! JPEGXSMaximumBitRate.empty() ) result = TLVSet.WriteUi32(OBJ_WRITE_ARGS_OPT(JPEGXSPictureSubDescriptor, JPEGXSMaximumBitRate)); + return result; +} + +// +void +JPEGXSPictureSubDescriptor::Copy(const JPEGXSPictureSubDescriptor& rhs) +{ + InterchangeObject::Copy(rhs); + + JPEGXSPpih = rhs.JPEGXSPpih; + JPEGXSPlev = rhs.JPEGXSPlev; + JPEGXSWf = rhs.JPEGXSWf; + JPEGXSHf = rhs.JPEGXSHf; + JPEGXSNc = rhs.JPEGXSNc; + JPEGXSComponentTable = rhs.JPEGXSComponentTable; + JPEGXSCw = rhs.JPEGXSCw; + JPEGXSHsl = rhs.JPEGXSHsl; + JPEGXSMaximumBitRate = rhs.JPEGXSMaximumBitRate; +} + +// +InterchangeObject* +JPEGXSPictureSubDescriptor::Clone() const +{ + return new JPEGXSPictureSubDescriptor(*this); +} + +// +void +JPEGXSPictureSubDescriptor::Dump(FILE* stream) +{ + char identbuf[IdentBufferLen]; + *identbuf = 0; + + if ( stream == 0 ) + stream = stderr; + + InterchangeObject::Dump(stream); + fprintf(stream, " %22s = %d\n", "JPEGXSPpih", JPEGXSPpih); + fprintf(stream, " %22s = %d\n", "JPEGXSPlev", JPEGXSPlev); + fprintf(stream, " %22s = %d\n", "JPEGXSWf", JPEGXSWf); + fprintf(stream, " %22s = %d\n", "JPEGXSHf", JPEGXSHf); + fprintf(stream, " %22s = %d\n", "JPEGXSNc", JPEGXSNc); + fprintf(stream, " %22s = %s\n", "JPEGXSComponentTable", JPEGXSComponentTable.EncodeString(identbuf, IdentBufferLen)); + if ( ! JPEGXSCw.empty()) { + fprintf(stream, " %22s = %d\n", "JPEGXSCw", JPEGXSCw.get()); + } + if ( ! JPEGXSHsl.empty()) { + fprintf(stream, " %22s = %d\n", "JPEGXSHsl", JPEGXSHsl.get()); + } + if ( ! JPEGXSMaximumBitRate.empty()) { + fprintf(stream, " %22s = %d\n", "JPEGXSMaximumBitRate", JPEGXSMaximumBitRate.get()); + } +} + +// +ASDCP::Result_t +JPEGXSPictureSubDescriptor::InitFromBuffer(const byte_t* p, ui32_t l) +{ + return InterchangeObject::InitFromBuffer(p, l); +} + +// +ASDCP::Result_t +JPEGXSPictureSubDescriptor::WriteToBuffer(ASDCP::FrameBuffer& Buffer) +{ + return InterchangeObject::WriteToBuffer(Buffer); +} + + +//------------------------------------------------------------------------------------------ // CDCIEssenceDescriptor // diff --git a/src/Metadata.h b/src/Metadata.h index e559909..82bb7b9 100644 --- a/src/Metadata.h +++ b/src/Metadata.h @@ -550,6 +550,38 @@ namespace ASDCP virtual Result_t WriteToBuffer(ASDCP::FrameBuffer&); }; + + // thor: this is taken from ST2124 + class JPEGXSPictureSubDescriptor : public InterchangeObject + { + JPEGXSPictureSubDescriptor(); + + public: + ui16_t JPEGXSPpih; // Profile of this codestream, copy from PIH.Ppih + ui16_t JPEGXSPlev; // Level and sublevel of this codestream, copy from PIH.Plev + ui16_t JPEGXSWf; // Frame width in sample grid positions, copy from PIH.Wf + ui16_t JPEGXSHf; // Frame height in sample grid positions, copy from PIH.Hf + ui8_t JPEGXSNc; // Number of components, copy from PIH.Nc + Raw JPEGXSComponentTable; // raw copy of the component table + optional_property<ui16_t> JPEGXSCw; // Column width, copy from PIH.Cw + optional_property<ui16_t> JPEGXSHsl; // Slice height, copy from PIH.Slh + optional_property<ui32_t> JPEGXSMaximumBitRate; // Bitrate in MB/sec. This shall be consistent with the brat field of the video information box + + JPEGXSPictureSubDescriptor(const Dictionary* d); + JPEGXSPictureSubDescriptor(const JPEGXSPictureSubDescriptor& rhs); + virtual ~JPEGXSPictureSubDescriptor() {} + + const JPEGXSPictureSubDescriptor& operator=(const JPEGXSPictureSubDescriptor& rhs) { Copy(rhs); return *this; } + virtual void Copy(const JPEGXSPictureSubDescriptor& rhs); + virtual InterchangeObject *Clone() const; + virtual const char* HasName() { return "JPEGXSPictureSubDescriptor"; } + 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 JPEG2000PictureSubDescriptor : public InterchangeObject { |
