summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorThomas Richter <thomas.richter@iis.fraunhofer.de>2021-05-21 17:17:15 +0200
committerThomas Richter <thomas.richter@iis.fraunhofer.de>2021-05-21 17:17:15 +0200
commit1dfee29db933f05601e632b5a61ddecaf66d21aa (patch)
tree5200eefecc85cd3610257b506e78c3fc3a613df9 /src
parentface28d822719aa6028ffca21097225c2a94e4ca (diff)
Added the JXS functionality again into the core library.
Diffstat (limited to 'src')
-rw-r--r--src/AS_02.h86
-rw-r--r--src/AS_02_JXS.cpp577
-rwxr-xr-xsrc/AS_DCP.h229
-rw-r--r--src/AS_DCP_JXS.cpp1188
-rwxr-xr-xsrc/AS_DCP_internal.h25
-rw-r--r--src/CMakeLists.txt13
-rw-r--r--src/JXS.cpp156
-rw-r--r--src/JXS.h239
-rw-r--r--src/JXS_Codestream_Parser.cpp251
-rw-r--r--src/JXS_Sequence_Parser.cpp281
-rw-r--r--src/Makefile.am11
-rw-r--r--src/Metadata.cpp135
-rw-r--r--src/Metadata.h32
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
{