From 8d24b6effb0377fc3041c2e024e7c5593caecc52 Mon Sep 17 00:00:00 2001 From: mschroffel Date: Thu, 7 Aug 2014 19:14:00 +0000 Subject: [PATCH] version bump --- configure.ac | 2 +- src/AS_02_PHDR.cpp | 742 +++++++++++++++++++++++++++++++++++ src/AS_02_PHDR.h | 159 ++++++++ src/AS_DCP_internal.h | 40 +- src/MDD.cpp | 28 ++ src/MDD.h | 7 + src/Metadata.cpp | 158 ++++++++ src/Metadata.h | 47 +++ src/PHDR_Sequence_Parser.cpp | 405 +++++++++++++++++++ src/h__Writer.cpp | 2 +- src/phdr-unwrap.cpp | 427 ++++++++++++++++++++ src/phdr-wrap.cpp | 680 ++++++++++++++++++++++++++++++++ 12 files changed, 2678 insertions(+), 19 deletions(-) create mode 100644 src/AS_02_PHDR.cpp create mode 100644 src/AS_02_PHDR.h create mode 100755 src/PHDR_Sequence_Parser.cpp create mode 100755 src/phdr-unwrap.cpp create mode 100755 src/phdr-wrap.cpp diff --git a/configure.ac b/configure.ac index 74d6cd3..9c200cb 100644 --- a/configure.ac +++ b/configure.ac @@ -37,7 +37,7 @@ AC_PREREQ([2.59]) # For example, if asdcplib version 1.0.0 were modified to accomodate changes # in file format, and if no changes were made to AS_DCP.h, the new version would be # 1.0.1. If changes were also required in AS_DCP.h, the new version would be 1.1.1. -AC_INIT([asdcplib], [2.1.4], [asdcplib@cinecert.com]) +AC_INIT([asdcplib], [2.1.4a], [asdcplib@cinecert.com]) AC_CONFIG_AUX_DIR([build-aux]) AC_CONFIG_SRCDIR([src/KM_error.h]) diff --git a/src/AS_02_PHDR.cpp b/src/AS_02_PHDR.cpp new file mode 100644 index 0000000..c6017ef --- /dev/null +++ b/src/AS_02_PHDR.cpp @@ -0,0 +1,742 @@ +/* +Copyright (c) 2011-2014, John Hurst + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. +3. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/*! \file AS_02_PHDR.cpp + \version $Id$ + \brief AS-02 library, JPEG 2000 P-HDR essence reader and writer implementation +*/ + +#include "AS_02_internal.h" +#include "AS_02_PHDR.h" + +#include +#include + +using namespace ASDCP; +using namespace ASDCP::JP2K; +using Kumu::GenRandomValue; + +//------------------------------------------------------------------------------------------ + +static std::string JP2K_PACKAGE_LABEL = "File Package: PROTOTYPE SMPTE ST 422 / ST 2067-5 frame wrapping of JPEG 2000 codestreams with HDR metadata"; +static std::string PICT_DEF_LABEL = "PHDR Image Track"; +static std::string MD_DEF_LABEL = "PHDR Metadata Track"; + + + +//------------------------------------------------------------------------------------------ + +// +void +AS_02::PHDR::FrameBuffer::Dump(FILE* stream, ui32_t dump_bytes) const +{ + if ( stream == 0 ) + stream = stderr; + + fprintf(stream, "Hello, HDR world!\n"); +} + + +//------------------------------------------------------------------------------------------ +// +// hidden, internal implementation of JPEG 2000 reader + + +class AS_02::PHDR::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& filename, std::string& PHDR_master_metadata); + Result_t ReadFrame(ui32_t, AS_02::PHDR::FrameBuffer&, AESDecContext*, HMACContext*); +}; + +// +Result_t +AS_02::PHDR::MXFReader::h__Reader::OpenRead(const std::string& filename, std::string& PHDR_master_metadata) +{ + Result_t result = OpenMXFRead(filename.c_str()); + ui32_t SimplePayloadSID = 0; + + 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"); + return RESULT_AS02_FORMAT; + } + + m_HeaderPart.GetMDObjectByType(OBJ_TYPE_ARGS(JPEG2000PictureSubDescriptor), &tmp_iobj); + + if ( tmp_iobj == 0 ) + { + DefaultLogSink().Error("JPEG2000PictureSubDescriptor not found.\n"); + return RESULT_AS02_FORMAT; + } + + m_HeaderPart.GetMDObjectByType(OBJ_TYPE_ARGS(PHDRMetadataTrackSubDescriptor), &tmp_iobj); + + if ( tmp_iobj == 0 ) + { + DefaultLogSink().Error("PHDRMetadataTrackSubDescriptor not found.\n"); + return RESULT_AS02_FORMAT; + } + else + { + PHDRMetadataTrackSubDescriptor *tmp_desc = dynamic_cast(tmp_iobj); + assert(tmp_desc); + SimplePayloadSID = tmp_desc->SimplePayloadSID; + } + + std::list 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; + } + } + + // if PHDRSimplePayload exists, go get it + if ( KM_SUCCESS(result) && SimplePayloadSID ) + { + Array::const_iterator pi; + RIP::Pair TmpPair; + + // Look up the partition start in the RIP using the SID. + for ( pi = m_RIP.PairArray.begin(); pi != m_RIP.PairArray.end(); ++pi ) + { + if ( (*pi).BodySID == SimplePayloadSID ) + { + TmpPair = *pi; + break; + } + } + + if ( TmpPair.ByteOffset == 0 ) + { + DefaultLogSink().Error("Body SID not found in RIP set: %d\n", SimplePayloadSID); + return RESULT_AS02_FORMAT; + } + + // seek to the start of the partition + if ( (Kumu::fpos_t)TmpPair.ByteOffset != m_LastPosition ) + { + m_LastPosition = TmpPair.ByteOffset; + result = m_File.Seek(TmpPair.ByteOffset); + } + + // read the partition header + ASDCP::MXF::Partition GSPart(m_Dict); + result = GSPart.InitFromFile(m_File); + + if ( KM_SUCCESS(result) ) + { + // read the generic stream packet + if ( KM_SUCCESS(result) ) + { + ASDCP::FrameBuffer tmp_buf; + tmp_buf.Capacity(Kumu::Megabyte); + + result = Read_EKLV_Packet(m_File, *m_Dict, m_Info, m_LastPosition, m_CtFrameBuf, + 0, 0, tmp_buf, m_Dict->ul(MDD_GenericStream_DataElement), 0, 0); + + if ( KM_SUCCESS(result) ) + { + PHDR_master_metadata.assign((const char*)tmp_buf.RoData(), tmp_buf.Size()); + } + } + } + } + + m_IndexAccess.Dump(); + + return result; +} + +// +// +Result_t +AS_02::PHDR::MXFReader::h__Reader::ReadFrame(ui32_t FrameNum, AS_02::PHDR::FrameBuffer& FrameBuf, + ASDCP::AESDecContext* Ctx, ASDCP::HMACContext* HMAC) +{ + if ( ! m_File.IsOpen() ) + return RESULT_INIT; + + assert(m_Dict); + Result_t result = ReadEKLVFrame(FrameNum, FrameBuf, m_Dict->ul(MDD_JPEG2000Essence), Ctx, HMAC); + + if ( KM_SUCCESS(result) ) + { + ASDCP::FrameBuffer tmp_metadata_buffer; + tmp_metadata_buffer.Capacity(8192); + + result = Read_EKLV_Packet(m_File, *m_Dict, m_Info, m_LastPosition, m_CtFrameBuf, + FrameNum, FrameNum + 1, tmp_metadata_buffer, m_Dict->ul(MDD_PHDRImageMetadataItem), Ctx, HMAC); + + if ( KM_SUCCESS(result) ) + { + FrameBuf.OpaqueMetadata.assign((const char*)tmp_metadata_buffer.RoData(), tmp_metadata_buffer.Size()); + } + } + + return result; +} + +//------------------------------------------------------------------------------------------ +// + +AS_02::PHDR::MXFReader::MXFReader() +{ + m_Reader = new h__Reader(DefaultCompositeDict()); +} + + +AS_02::PHDR::MXFReader::~MXFReader() +{ +} + +// Warning: direct manipulation of MXF structures can interfere +// with the normal operation of the wrapper. Caveat emptor! +// +ASDCP::MXF::OP1aHeader& +AS_02::PHDR::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::PHDR::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::PHDR::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::PHDR::MXFReader::OpenRead(const std::string& filename, std::string& PHDR_master_metadata) const +{ + return m_Reader->OpenRead(filename, PHDR_master_metadata); +} + +// +Result_t +AS_02::PHDR::MXFReader::Close() const +{ + if ( m_Reader && m_Reader->m_File.IsOpen() ) + { + m_Reader->Close(); + return RESULT_OK; + } + + return RESULT_INIT; +} + +// +Result_t +AS_02::PHDR::MXFReader::ReadFrame(ui32_t FrameNum, AS_02::PHDR::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; +} + +// Fill the struct with the values from the file's header. +// Returns RESULT_INIT if the file is not open. +Result_t +AS_02::PHDR::MXFReader::FillWriterInfo(WriterInfo& Info) const +{ + if ( m_Reader && m_Reader->m_File.IsOpen() ) + { + Info = m_Reader->m_Info; + return RESULT_OK; + } + + return RESULT_INIT; +} + + +//------------------------------------------------------------------------------------------ + +// +class AS_02::PHDR::MXFWriter::h__Writer : public AS_02::h__AS02WriterFrame +{ + PHDRMetadataTrackSubDescriptor *m_MetadataTrackSubDescriptor; + + ASDCP_NO_COPY_CONSTRUCT(h__Writer); + h__Writer(); + + JPEG2000PictureSubDescriptor* m_EssenceSubDescriptor; + + Result_t WritePHDRHeader(const std::string& PackageLabel, const ASDCP::UL& WrappingUL, + const std::string& TrackName, const ASDCP::UL& EssenceUL, + const ASDCP::UL& DataDefinition, const ASDCP::Rational& EditRate, + const ui32_t& TCFrameRate); + +public: + byte_t m_EssenceUL[SMPTE_UL_LENGTH]; + byte_t m_MetadataUL[SMPTE_UL_LENGTH]; + + h__Writer(const Dictionary& d) : h__AS02WriterFrame(d), m_EssenceSubDescriptor(0), m_MetadataTrackSubDescriptor(0) { + memset(m_EssenceUL, 0, SMPTE_UL_LENGTH); + memset(m_MetadataUL, 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 AS_02::PHDR::FrameBuffer&, ASDCP::AESEncContext*, ASDCP::HMACContext*); + Result_t Finalize(const std::string& PHDR_master_metadata); +}; + + +// Open the file for writing. The file must not exist. Returns error if +// the operation cannot be completed. +Result_t +AS_02::PHDR::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() ) + { + 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); + + 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_JPEG2000PictureSubDescriptor)) ) + { + DefaultLogSink().Error("Essence sub-descriptor is not a JPEG2000PictureSubDescriptor.\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; +} + +// all the above for a single source clip +Result_t +AS_02::PHDR::MXFWriter::h__Writer::WritePHDRHeader(const std::string& PackageLabel, const ASDCP::UL& WrappingUL, + const std::string& TrackName, const ASDCP::UL& EssenceUL, + const ASDCP::UL& DataDefinition, const ASDCP::Rational& EditRate, + const ui32_t& TCFrameRate) +{ + if ( EditRate.Numerator == 0 || EditRate.Denominator == 0 ) + { + DefaultLogSink().Error("Non-zero edit-rate reqired.\n"); + return RESULT_PARAM; + } + + InitHeader(); + + AddSourceClip(EditRate, EditRate/*TODO: for a moment*/, TCFrameRate, TrackName, EssenceUL, DataDefinition, PackageLabel); + + // add metadata track + TrackSet metdata_track = + CreateTrackAndSequence(m_HeaderPart, *m_FilePackage, + MD_DEF_LABEL, EditRate, + UL(m_Dict->ul(MDD_PHDRImageMetadataItem)), + 3, m_Dict); + + metdata_track.Sequence->Duration.set_has_value(); + m_DurationUpdateList.push_back(&(metdata_track.Sequence->Duration.get())); + // Consult ST 379:2004 Sec. 6.3, "Element to track relationship" to see where "12" comes from. + metdata_track.Track->TrackNumber = KM_i32_BE(Kumu::cp2i((UL(m_MetadataUL).Value() + 12))); + + metdata_track.Clip = new SourceClip(m_Dict); + m_HeaderPart.AddChildObject(metdata_track.Clip); + metdata_track.Sequence->StructuralComponents.push_back(metdata_track.Clip->InstanceUID); + metdata_track.Clip->DataDefinition = UL(m_Dict->ul(MDD_PHDRImageMetadataWrappingFrame)); + + // for now we do not allow setting this value, so all files will be 'original' + metdata_track.Clip->SourceTrackID = 0; + metdata_track.Clip->SourcePackageID = NilUMID; + + metdata_track.Clip->Duration.set_has_value(); + m_DurationUpdateList.push_back(&(metdata_track.Clip->Duration.get())); + + // add PHDR subdescriptor + m_MetadataTrackSubDescriptor = new PHDRMetadataTrackSubDescriptor(m_Dict); + m_EssenceSubDescriptorList.push_back(m_MetadataTrackSubDescriptor); + GenRandomValue(m_MetadataTrackSubDescriptor->InstanceUID); + m_EssenceDescriptor->SubDescriptors.push_back(m_MetadataTrackSubDescriptor->InstanceUID); + m_MetadataTrackSubDescriptor->DataDefinition = UL(m_Dict->ul(MDD_PHDRImageMetadataWrappingFrame)); + m_MetadataTrackSubDescriptor->SourceTrackID = 3; + m_MetadataTrackSubDescriptor->SimplePayloadSID = 0; + + AddEssenceDescriptor(WrappingUL); + + m_IndexWriter.SetPrimerLookup(&m_HeaderPart.m_Primer); + m_RIP.PairArray.push_back(RIP::Pair(0, 0)); // Header partition RIP entry + m_IndexWriter.OperationalPattern = m_HeaderPart.OperationalPattern; + m_IndexWriter.EssenceContainers = m_HeaderPart.EssenceContainers; + + Result_t result = m_HeaderPart.WriteToFile(m_File, m_HeaderSize); + + if ( KM_SUCCESS(result) ) + { + m_PartitionSpace *= floor( EditRate.Quotient() + 0.5 ); // convert seconds to edit units + m_ECStart = m_File.Tell(); + m_IndexWriter.IndexSID = 129; + + UL body_ul(m_Dict->ul(MDD_ClosedCompleteBodyPartition)); + Partition body_part(m_Dict); + body_part.BodySID = 1; + body_part.OperationalPattern = m_HeaderPart.OperationalPattern; + body_part.EssenceContainers = m_HeaderPart.EssenceContainers; + body_part.ThisPartition = m_ECStart; + result = body_part.WriteToFile(m_File, body_ul); + m_RIP.PairArray.push_back(RIP::Pair(1, body_part.ThisPartition)); // Second RIP Entry + } + + return result; +} + +// Automatically sets the MXF file's metadata from the first jpeg codestream stream. +Result_t +AS_02::PHDR::MXFWriter::h__Writer::SetSourceStream(const std::string& label, const ASDCP::Rational& edit_rate) +{ + assert(m_Dict); + if ( ! m_State.Test_INIT() ) + { + return RESULT_STATE; + } + + memcpy(m_EssenceUL, m_Dict->ul(MDD_JPEG2000Essence), SMPTE_UL_LENGTH); + m_EssenceUL[SMPTE_UL_LENGTH-1] = 1; // first track of the essence container + + memcpy(m_MetadataUL, m_Dict->ul(MDD_PHDRImageMetadataItem), SMPTE_UL_LENGTH); + m_MetadataUL[SMPTE_UL_LENGTH-1] = 3; // third track of the essence container + + Result_t result = m_State.Goto_READY(); + + if ( KM_SUCCESS(result) ) + { + result = WritePHDRHeader(label, UL(m_Dict->ul(MDD_JPEG_2000WrappingFrame)), + PICT_DEF_LABEL, UL(m_EssenceUL), UL(m_Dict->ul(MDD_PictureDataDef)), + edit_rate, derive_timecode_rate_from_edit_rate(edit_rate)); + + if ( KM_SUCCESS(result) ) + { + this->m_IndexWriter.SetPrimerLookup(&this->m_HeaderPart.m_Primer); + } + } + + 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::PHDR::MXFWriter::h__Writer::WriteFrame(const AS_02::PHDR::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, Ctx, HMAC); + } + + if ( KM_SUCCESS(result) ) + { + ASDCP::FrameBuffer metadata_buffer_wrapper; + metadata_buffer_wrapper.SetData((byte_t*)(FrameBuf.OpaqueMetadata.c_str()), FrameBuf.OpaqueMetadata.size()); + metadata_buffer_wrapper.Size(FrameBuf.OpaqueMetadata.size()); + + + result = Write_EKLV_Packet(m_File, *m_Dict, m_HeaderPart, m_Info, m_CtFrameBuf, m_FramesWritten, + m_StreamOffset, metadata_buffer_wrapper, m_MetadataUL, Ctx, HMAC); + } + + if ( KM_SUCCESS(result) ) + { + m_FramesWritten++; + } + + return result; +} + +// Closes the MXF file, writing the index and other closing information. +// +Result_t +AS_02::PHDR::MXFWriter::h__Writer::Finalize(const std::string& PHDR_master_metadata) +{ + if ( ! m_State.Test_RUNNING() ) + return RESULT_STATE; + + Result_t result = m_State.Goto_FINAL(); + + if ( KM_SUCCESS(result) ) + { + if ( m_IndexWriter.GetDuration() > 0 ) + { + m_IndexWriter.ThisPartition = this->m_File.Tell(); + m_IndexWriter.WriteToFile(this->m_File); + m_RIP.PairArray.push_back(RIP::Pair(0, this->m_IndexWriter.ThisPartition)); + } + + if ( ! PHDR_master_metadata.empty() ) + { + // write PHDRSimplePayload + Kumu::fpos_t here = m_File.Tell(); + + // create generic stream partition header + static UL GenericStream_DataElement(m_Dict->ul(MDD_GenericStream_DataElement)); + ASDCP::MXF::Partition GSPart(m_Dict); + + GSPart.ThisPartition = here; + GSPart.PreviousPartition = m_RIP.PairArray.back().ByteOffset; + GSPart.OperationalPattern = m_HeaderPart.OperationalPattern; + GSPart.BodySID = 2; + m_MetadataTrackSubDescriptor->SimplePayloadSID = 2; + + m_RIP.PairArray.push_back(RIP::Pair(2, here)); + GSPart.EssenceContainers = m_HeaderPart.EssenceContainers; + + static UL gs_part_ul(m_Dict->ul(MDD_GenericStreamPartition)); + Result_t result = GSPart.WriteToFile(m_File, gs_part_ul); + + if ( KM_SUCCESS(result) ) + { + ASDCP::FrameBuffer tmp_buf; + tmp_buf.SetData((byte_t*)PHDR_master_metadata.c_str(), PHDR_master_metadata.size()); + tmp_buf.Size(PHDR_master_metadata.size()); + + result = Write_EKLV_Packet(m_File, *m_Dict, m_HeaderPart, m_Info, m_CtFrameBuf, m_FramesWritten, + m_StreamOffset, tmp_buf, GenericStream_DataElement.Value(), 0, 0); + + m_FramesWritten++; + } + } + + result = WriteAS02Footer(); + } + + return result; +} + + +//------------------------------------------------------------------------------------------ + + + +AS_02::PHDR::MXFWriter::MXFWriter() +{ +} + +AS_02::PHDR::MXFWriter::~MXFWriter() +{ +} + +// Warning: direct manipulation of MXF structures can interfere +// with the normal operation of the wrapper. Caveat emptor! +// +ASDCP::MXF::OP1aHeader& +AS_02::PHDR::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::PHDR::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::PHDR::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::PHDR::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(JP2K_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::PHDR::MXFWriter::WriteFrame(const AS_02::PHDR::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::PHDR::MXFWriter::Finalize(const std::string& PHDR_master_metadata) +{ + if ( m_Writer.empty() ) + return RESULT_INIT; + + return m_Writer->Finalize(PHDR_master_metadata); +} + + +// +// end AS_02_PHDR.cpp +// diff --git a/src/AS_02_PHDR.h b/src/AS_02_PHDR.h new file mode 100644 index 0000000..b3c94e7 --- /dev/null +++ b/src/AS_02_PHDR.h @@ -0,0 +1,159 @@ + + +#ifndef _AS_02_PHDR_H_ +#define _AS_02_PHDR_H_ + +#include "AS_02.h" + +namespace AS_02 +{ + + namespace PHDR + { + // + class FrameBuffer : public ASDCP::JP2K::FrameBuffer + { + public: + std::string OpaqueMetadata; + + 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 reads a sequence of files containing + // JPEG 2000 P-HDR pictures and metadata. + class SequenceParser + { + class h__SequenceParser; + Kumu::mem_ptr 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 pairs of files, each containing the codestream for exactly one picture (.j2c) + // and the corresponding P-HDR metadata (.xml). 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. The contents of the metadata files will + // not be analyzed (i.e., the raw bytes will be passed in without scrutiny.) If the + // "pedantic" parameter is given and is true, the J2C parser will check the JPEG 2000 + // metadata for each codestream and fail if a mismatch is detected. + Result_t OpenRead(const std::string& filename, bool pedantic = false) const; + + // Opens a file sequence for reading. The sequence is expected to contain one or + // more pairs of filenames, each naming a file containing the codestream (.j2c) and the + // corresponding P-HDR metadata (.xml) 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& file_list, bool pedantic = false) 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(ASDCP::JP2K::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(AS_02::PHDR::FrameBuffer&) const; + }; + + // + class MXFWriter + { + class h__Writer; + ASDCP::mem_ptr 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 AS_02::PHDR::FrameBuffer&, ASDCP::AESEncContext* = 0, ASDCP::HMACContext* = 0); + + // Closes the MXF file, writing the final index, the PHDR master metadata and the revised header. + Result_t Finalize(const std::string& PHDR_master_metadata); + }; + + // + class MXFReader + { + class h__Reader; + ASDCP::mem_ptr 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. If master metadata is available it will + // be placed into the string object passed as the second argument. + Result_t OpenRead(const std::string& filename, std::string& PHDR_master_metadata) 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; + + // 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, AS_02::PHDR::FrameBuffer&, ASDCP::AESDecContext* = 0, ASDCP::HMACContext* = 0) const; + + // Print debugging information to stream + void DumpHeaderMetadata(FILE* = 0) const; + void DumpIndex(FILE* = 0) const; + }; + + } // end namespace PHDR + +} // end namespace AS_02 + +#endif // _AS_02_PHDR_H_ + +// +// end AS_02_PHDR.h +// diff --git a/src/AS_DCP_internal.h b/src/AS_DCP_internal.h index a37c731..7875a4f 100755 --- a/src/AS_DCP_internal.h +++ b/src/AS_DCP_internal.h @@ -500,6 +500,7 @@ namespace ASDCP MaterialPackage* m_MaterialPackage; SourcePackage* m_FilePackage; + ContentStorage* m_ContentStorage; FileDescriptor* m_EssenceDescriptor; std::list m_EssenceSubDescriptorList; @@ -514,9 +515,9 @@ namespace ASDCP DurationElementList_t m_DurationUpdateList; TrackFileWriter(const Dictionary& d) : - m_Dict(&d), m_HeaderPart(m_Dict), m_RIP(m_Dict), - m_HeaderSize(0), m_EssenceDescriptor(0), - m_FramesWritten(0), m_StreamOffset(0) + m_Dict(&d), m_HeaderSize(0), m_HeaderPart(m_Dict), m_RIP(m_Dict), + m_MaterialPackage(0), m_FilePackage(0), m_ContentStorage(0), + m_EssenceDescriptor(0), m_FramesWritten(0), m_StreamOffset(0) { default_md_object_init(); } @@ -568,14 +569,16 @@ namespace ASDCP const std::string& TrackName, const UL& EssenceUL, const UL& DataDefinition, const std::string& PackageLabel) { - // - ContentStorage* Storage = new ContentStorage(m_Dict); - m_HeaderPart.AddChildObject(Storage); - m_HeaderPart.m_Preface->ContentStorage = Storage->InstanceUID; + if ( m_ContentStorage == 0 ) + { + m_ContentStorage = new ContentStorage(m_Dict); + m_HeaderPart.AddChildObject(m_ContentStorage); + m_HeaderPart.m_Preface->ContentStorage = m_ContentStorage->InstanceUID; + } EssenceContainerData* ECD = new EssenceContainerData(m_Dict); m_HeaderPart.AddChildObject(ECD); - Storage->EssenceContainerData.push_back(ECD->InstanceUID); + m_ContentStorage->EssenceContainerData.push_back(ECD->InstanceUID); ECD->IndexSID = 129; ECD->BodySID = 1; @@ -591,7 +594,7 @@ namespace ASDCP m_MaterialPackage->Name = "AS-DCP Material Package"; m_MaterialPackage->PackageUID = MaterialPackageUMID; m_HeaderPart.AddChildObject(m_MaterialPackage); - Storage->Packages.push_back(m_MaterialPackage->InstanceUID); + m_ContentStorage->Packages.push_back(m_MaterialPackage->InstanceUID); TrackSet MPTCTrack = CreateTimecodeTrack(m_HeaderPart, *m_MaterialPackage, @@ -629,7 +632,7 @@ namespace ASDCP ECD->LinkedPackageUID = SourcePackageUMID; m_HeaderPart.AddChildObject(m_FilePackage); - Storage->Packages.push_back(m_FilePackage->InstanceUID); + m_ContentStorage->Packages.push_back(m_FilePackage->InstanceUID); TrackSet FPTCTrack = CreateTimecodeTrack(m_HeaderPart, *m_FilePackage, @@ -673,14 +676,16 @@ namespace ASDCP const std::string& TrackName, const UL& DataDefinition, const std::string& PackageLabel) { - // - ContentStorage* Storage = new ContentStorage(m_Dict); - m_HeaderPart.AddChildObject(Storage); - m_HeaderPart.m_Preface->ContentStorage = Storage->InstanceUID; + if ( m_ContentStorage == 0 ) + { + m_ContentStorage = new ContentStorage(m_Dict); + m_HeaderPart.AddChildObject(m_ContentStorage); + m_HeaderPart.m_Preface->ContentStorage = m_ContentStorage->InstanceUID; + } EssenceContainerData* ECD = new EssenceContainerData(m_Dict); m_HeaderPart.AddChildObject(ECD); - Storage->EssenceContainerData.push_back(ECD->InstanceUID); + m_ContentStorage->EssenceContainerData.push_back(ECD->InstanceUID); ECD->IndexSID = 129; ECD->BodySID = 1; @@ -696,7 +701,7 @@ namespace ASDCP m_MaterialPackage->Name = "AS-DCP Material Package"; m_MaterialPackage->PackageUID = MaterialPackageUMID; m_HeaderPart.AddChildObject(m_MaterialPackage); - Storage->Packages.push_back(m_MaterialPackage->InstanceUID); + m_ContentStorage->Packages.push_back(m_MaterialPackage->InstanceUID); TrackSet MPTCTrack = CreateTimecodeTrack(m_HeaderPart, *m_MaterialPackage, @@ -733,7 +738,7 @@ namespace ASDCP ECD->LinkedPackageUID = SourcePackageUMID; m_HeaderPart.AddChildObject(m_FilePackage); - Storage->Packages.push_back(m_FilePackage->InstanceUID); + m_ContentStorage->Packages.push_back(m_FilePackage->InstanceUID); TrackSet FPTCTrack = CreateTimecodeTrack(m_HeaderPart, *m_FilePackage, @@ -786,6 +791,7 @@ namespace ASDCP m_HeaderPart.EssenceContainers.push_back(CryptEssenceUL); m_HeaderPart.m_Preface->DMSchemes.push_back(UL(m_Dict->ul(MDD_CryptographicFrameworkLabel))); AddDMScrypt(m_HeaderPart, *m_FilePackage, m_Info, WrappingUL, m_Dict); + //// TODO: fix DMSegment Duration value } else { diff --git a/src/MDD.cpp b/src/MDD.cpp index 6da1c91..c879d0f 100644 --- a/src/MDD.cpp +++ b/src/MDD.cpp @@ -1155,6 +1155,34 @@ static const ASDCP::MDDEntry s_MDD_Table[] = { 0x0e, 0x16, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02 }, {0}, false, "DBOXMotionCodeSecondaryStream" }, + // 379-2, Sec. 7: Encoders that conform to this specification shall add a + // ContainerConstraintSubDescriptor to the GenericDescriptor::SubDescriptors + // property of the top-most File Descriptor that describes the essence + // container. + { { 0x06, 0x0e, 0x2b, 0x34, 0x02, 0x7f, 0x01, 0x01, // 373 + 0x0d, 0x01, 0x01, 0x01, 0x01, 0x01, 0x67, 0x00 }, + {0}, false, "ContainerConstraintSubDescriptor" }, + + // protype for high dynamic range + { { 0x06, 0x0e, 0x2b, 0x34, 0x04, 0x01, 0x01, 0x05, // 374 + 0x0e, 0x09, 0x06, 0xe1, 0x00, 0x00, 0x00, 0x00 }, + {0}, false, "PHDRImageMetadataWrappingFrame" }, + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x02, 0x01, 0x05, // 375xxx + 0x0e, 0x09, 0x06, 0xe2, 0x00, 0x00, 0x01, 0x00 }, + {0}, false, "PHDRImageMetadataItem" }, + { { 0x06, 0x0e, 0x2b, 0x34, 0x02, 0x53, 0x01, 0x05, // 376xxx + 0x0e, 0x09, 0x05, 0xe3, 0x00, 0x00, 0x00, 0x00 }, + {0}, false, "PHDRMetadataTrackSubDescriptor" }, + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x05, // 377xxx + 0x0e, 0x09, 0x05, 0xe3, 0x01, 0x00, 0x00, 0x00 }, + {0}, false, "PHDRMetadataTrackSubDescriptor_DataDefinition" }, + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x05, // 378xxx + 0x0e, 0x09, 0x05, 0xe3, 0x02, 0x00, 0x00, 0x00 }, + {0}, false, "PHDRMetadataTrackSubDescriptor_SourceTrackID" }, + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x05, // 379xxx + 0x0e, 0x09, 0x05, 0xe3, 0x03, 0x00, 0x00, 0x00 }, + {0}, false, "PHDRMetadataTrackSubDescriptor_SimplePayloadSID" }, + { {0}, {0}, false, 0 } }; diff --git a/src/MDD.h b/src/MDD.h index 83def7b..b3852fa 100755 --- a/src/MDD.h +++ b/src/MDD.h @@ -408,6 +408,13 @@ namespace ASDCP { MDD_WAVWrappingClip, // 370 MDD_DBOXMotionCodePrimaryStream, // 371 MDD_DBOXMotionCodeSecondaryStream, // 372 + MDD_ContainerConstraintSubDescriptor, // 373 + MDD_PHDRImageMetadataWrappingFrame, // 374 + MDD_PHDRImageMetadataItem, // 375 + MDD_PHDRMetadataTrackSubDescriptor, // 376 + MDD_PHDRMetadataTrackSubDescriptor_DataDefinition, // 377 + MDD_PHDRMetadataTrackSubDescriptor_SourceTrackID, // 378 + MDD_PHDRMetadataTrackSubDescriptor_SimplePayloadSID, // 379 MDD_Max }; // enum MDD_t diff --git a/src/Metadata.cpp b/src/Metadata.cpp index d73fe69..0d163be 100755 --- a/src/Metadata.cpp +++ b/src/Metadata.cpp @@ -68,6 +68,7 @@ static InterchangeObject* GenericDataEssenceDescriptor_Factory(const Dictionary* static InterchangeObject* TimedTextDescriptor_Factory(const Dictionary*& Dict) { return new TimedTextDescriptor(Dict); } static InterchangeObject* TimedTextResourceSubDescriptor_Factory(const Dictionary*& Dict) { return new TimedTextResourceSubDescriptor(Dict); } static InterchangeObject* StereoscopicPictureSubDescriptor_Factory(const Dictionary*& Dict) { return new StereoscopicPictureSubDescriptor(Dict); } +static InterchangeObject* ContainerConstraintSubDescriptor_Factory(const Dictionary*& Dict) { return new ContainerConstraintSubDescriptor(Dict); } static InterchangeObject* NetworkLocator_Factory(const Dictionary*& Dict) { return new NetworkLocator(Dict); } static InterchangeObject* MCALabelSubDescriptor_Factory(const Dictionary*& Dict) { return new MCALabelSubDescriptor(Dict); } static InterchangeObject* AudioChannelLabelSubDescriptor_Factory(const Dictionary*& Dict) { return new AudioChannelLabelSubDescriptor(Dict); } @@ -75,6 +76,7 @@ static InterchangeObject* SoundfieldGroupLabelSubDescriptor_Factory(const Dictio static InterchangeObject* GroupOfSoundfieldGroupsLabelSubDescriptor_Factory(const Dictionary*& Dict) { return new GroupOfSoundfieldGroupsLabelSubDescriptor(Dict); } static InterchangeObject* DCDataDescriptor_Factory(const Dictionary*& Dict) { return new DCDataDescriptor(Dict); } static InterchangeObject* DolbyAtmosSubDescriptor_Factory(const Dictionary*& Dict) { return new DolbyAtmosSubDescriptor(Dict); } +static InterchangeObject* PHDRMetadataTrackSubDescriptor_Factory(const Dictionary*& Dict) { return new PHDRMetadataTrackSubDescriptor(Dict); } void @@ -109,6 +111,7 @@ ASDCP::MXF::Metadata_InitTypes(const Dictionary*& Dict) SetObjectFactory(Dict->ul(MDD_TimedTextDescriptor), TimedTextDescriptor_Factory); SetObjectFactory(Dict->ul(MDD_TimedTextResourceSubDescriptor), TimedTextResourceSubDescriptor_Factory); SetObjectFactory(Dict->ul(MDD_StereoscopicPictureSubDescriptor), StereoscopicPictureSubDescriptor_Factory); + SetObjectFactory(Dict->ul(MDD_ContainerConstraintSubDescriptor), ContainerConstraintSubDescriptor_Factory); SetObjectFactory(Dict->ul(MDD_NetworkLocator), NetworkLocator_Factory); SetObjectFactory(Dict->ul(MDD_MCALabelSubDescriptor), MCALabelSubDescriptor_Factory); SetObjectFactory(Dict->ul(MDD_AudioChannelLabelSubDescriptor), AudioChannelLabelSubDescriptor_Factory); @@ -116,6 +119,7 @@ ASDCP::MXF::Metadata_InitTypes(const Dictionary*& Dict) SetObjectFactory(Dict->ul(MDD_GroupOfSoundfieldGroupsLabelSubDescriptor), GroupOfSoundfieldGroupsLabelSubDescriptor_Factory); SetObjectFactory(Dict->ul(MDD_DCDataDescriptor), DCDataDescriptor_Factory); SetObjectFactory(Dict->ul(MDD_DolbyAtmosSubDescriptor), DolbyAtmosSubDescriptor_Factory); + SetObjectFactory(Dict->ul(MDD_PHDRMetadataTrackSubDescriptor), PHDRMetadataTrackSubDescriptor_Factory); } //------------------------------------------------------------------------------------------ @@ -3025,6 +3029,77 @@ StereoscopicPictureSubDescriptor::WriteToBuffer(ASDCP::FrameBuffer& Buffer) return InterchangeObject::WriteToBuffer(Buffer); } +//------------------------------------------------------------------------------------------ +// ContainerConstraintSubDescriptor + +// + +ContainerConstraintSubDescriptor::ContainerConstraintSubDescriptor(const Dictionary*& d) : InterchangeObject(d), m_Dict(d) +{ + assert(m_Dict); + m_UL = m_Dict->ul(MDD_ContainerConstraintSubDescriptor); +} + +ContainerConstraintSubDescriptor::ContainerConstraintSubDescriptor(const ContainerConstraintSubDescriptor& rhs) : InterchangeObject(rhs.m_Dict), m_Dict(rhs.m_Dict) +{ + assert(m_Dict); + m_UL = m_Dict->ul(MDD_ContainerConstraintSubDescriptor); + Copy(rhs); +} + + +// +ASDCP::Result_t +ContainerConstraintSubDescriptor::InitFromTLVSet(TLVReader& TLVSet) +{ + assert(m_Dict); + Result_t result = InterchangeObject::InitFromTLVSet(TLVSet); + return result; +} + +// +ASDCP::Result_t +ContainerConstraintSubDescriptor::WriteToTLVSet(TLVWriter& TLVSet) +{ + assert(m_Dict); + Result_t result = InterchangeObject::WriteToTLVSet(TLVSet); + return result; +} + +// +void +ContainerConstraintSubDescriptor::Copy(const ContainerConstraintSubDescriptor& rhs) +{ + InterchangeObject::Copy(rhs); +} + +// +void +ContainerConstraintSubDescriptor::Dump(FILE* stream) +{ + char identbuf[IdentBufferLen]; + *identbuf = 0; + + if ( stream == 0 ) + stream = stderr; + + InterchangeObject::Dump(stream); +} + +// +ASDCP::Result_t +ContainerConstraintSubDescriptor::InitFromBuffer(const byte_t* p, ui32_t l) +{ + return InterchangeObject::InitFromBuffer(p, l); +} + +// +ASDCP::Result_t +ContainerConstraintSubDescriptor::WriteToBuffer(ASDCP::FrameBuffer& Buffer) +{ + return InterchangeObject::WriteToBuffer(Buffer); +} + //------------------------------------------------------------------------------------------ // NetworkLocator @@ -3594,6 +3669,89 @@ DolbyAtmosSubDescriptor::WriteToBuffer(ASDCP::FrameBuffer& Buffer) return InterchangeObject::WriteToBuffer(Buffer); } +//------------------------------------------------------------------------------------------ +// PHDRMetadataTrackSubDescriptor + +// + +PHDRMetadataTrackSubDescriptor::PHDRMetadataTrackSubDescriptor(const Dictionary*& d) : InterchangeObject(d), m_Dict(d), SourceTrackID(0), SimplePayloadSID(0) +{ + assert(m_Dict); + m_UL = m_Dict->ul(MDD_PHDRMetadataTrackSubDescriptor); +} + +PHDRMetadataTrackSubDescriptor::PHDRMetadataTrackSubDescriptor(const PHDRMetadataTrackSubDescriptor& rhs) : InterchangeObject(rhs.m_Dict), m_Dict(rhs.m_Dict) +{ + assert(m_Dict); + m_UL = m_Dict->ul(MDD_PHDRMetadataTrackSubDescriptor); + Copy(rhs); +} + + +// +ASDCP::Result_t +PHDRMetadataTrackSubDescriptor::InitFromTLVSet(TLVReader& TLVSet) +{ + assert(m_Dict); + Result_t result = InterchangeObject::InitFromTLVSet(TLVSet); + if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadObject(OBJ_READ_ARGS(PHDRMetadataTrackSubDescriptor, DataDefinition)); + if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadUi32(OBJ_READ_ARGS(PHDRMetadataTrackSubDescriptor, SourceTrackID)); + if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadUi32(OBJ_READ_ARGS(PHDRMetadataTrackSubDescriptor, SimplePayloadSID)); + return result; +} + +// +ASDCP::Result_t +PHDRMetadataTrackSubDescriptor::WriteToTLVSet(TLVWriter& TLVSet) +{ + assert(m_Dict); + Result_t result = InterchangeObject::WriteToTLVSet(TLVSet); + if ( ASDCP_SUCCESS(result) ) result = TLVSet.WriteObject(OBJ_WRITE_ARGS(PHDRMetadataTrackSubDescriptor, DataDefinition)); + if ( ASDCP_SUCCESS(result) ) result = TLVSet.WriteUi32(OBJ_WRITE_ARGS(PHDRMetadataTrackSubDescriptor, SourceTrackID)); + if ( ASDCP_SUCCESS(result) ) result = TLVSet.WriteUi32(OBJ_WRITE_ARGS(PHDRMetadataTrackSubDescriptor, SimplePayloadSID)); + return result; +} + +// +void +PHDRMetadataTrackSubDescriptor::Copy(const PHDRMetadataTrackSubDescriptor& rhs) +{ + InterchangeObject::Copy(rhs); + DataDefinition = rhs.DataDefinition; + SourceTrackID = rhs.SourceTrackID; + SimplePayloadSID = rhs.SimplePayloadSID; +} + +// +void +PHDRMetadataTrackSubDescriptor::Dump(FILE* stream) +{ + char identbuf[IdentBufferLen]; + *identbuf = 0; + + if ( stream == 0 ) + stream = stderr; + + InterchangeObject::Dump(stream); + fprintf(stream, " %22s = %s\n", "DataDefinition", DataDefinition.EncodeString(identbuf, IdentBufferLen)); + fprintf(stream, " %22s = %d\n", "SourceTrackID", SourceTrackID); + fprintf(stream, " %22s = %d\n", "SimplePayloadSID", SimplePayloadSID); +} + +// +ASDCP::Result_t +PHDRMetadataTrackSubDescriptor::InitFromBuffer(const byte_t* p, ui32_t l) +{ + return InterchangeObject::InitFromBuffer(p, l); +} + +// +ASDCP::Result_t +PHDRMetadataTrackSubDescriptor::WriteToBuffer(ASDCP::FrameBuffer& Buffer) +{ + return InterchangeObject::WriteToBuffer(Buffer); +} + // // end Metadata.cpp // diff --git a/src/Metadata.h b/src/Metadata.h index 5c282b3..dc8d921 100755 --- a/src/Metadata.h +++ b/src/Metadata.h @@ -816,6 +816,28 @@ namespace ASDCP virtual Result_t WriteToBuffer(ASDCP::FrameBuffer&); }; + // + class ContainerConstraintSubDescriptor : public InterchangeObject + { + ContainerConstraintSubDescriptor(); + + public: + const Dictionary*& m_Dict; + + ContainerConstraintSubDescriptor(const Dictionary*& d); + ContainerConstraintSubDescriptor(const ContainerConstraintSubDescriptor& rhs); + virtual ~ContainerConstraintSubDescriptor() {} + + const ContainerConstraintSubDescriptor& operator=(const ContainerConstraintSubDescriptor& rhs) { Copy(rhs); return *this; } + virtual void Copy(const ContainerConstraintSubDescriptor& rhs); + virtual const char* HasName() { return "ContainerConstraintSubDescriptor"; } + 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 NetworkLocator : public InterchangeObject { @@ -984,6 +1006,31 @@ namespace ASDCP virtual Result_t WriteToBuffer(ASDCP::FrameBuffer&); }; + // + class PHDRMetadataTrackSubDescriptor : public InterchangeObject + { + PHDRMetadataTrackSubDescriptor(); + + public: + const Dictionary*& m_Dict; + UL DataDefinition; + ui32_t SourceTrackID; + ui32_t SimplePayloadSID; + + PHDRMetadataTrackSubDescriptor(const Dictionary*& d); + PHDRMetadataTrackSubDescriptor(const PHDRMetadataTrackSubDescriptor& rhs); + virtual ~PHDRMetadataTrackSubDescriptor() {} + + const PHDRMetadataTrackSubDescriptor& operator=(const PHDRMetadataTrackSubDescriptor& rhs) { Copy(rhs); return *this; } + virtual void Copy(const PHDRMetadataTrackSubDescriptor& rhs); + virtual const char* HasName() { return "PHDRMetadataTrackSubDescriptor"; } + virtual Result_t InitFromTLVSet(TLVReader& TLVSet); + virtual Result_t WriteToTLVSet(TLVWriter& TLVSet); + virtual void Dump(FILE* = 0); + virtual Result_t InitFromBuffer(const byte_t* p, ui32_t l); + virtual Result_t WriteToBuffer(ASDCP::FrameBuffer&); + }; + } // namespace MXF } // namespace ASDCP diff --git a/src/PHDR_Sequence_Parser.cpp b/src/PHDR_Sequence_Parser.cpp new file mode 100755 index 0000000..47d7fde --- /dev/null +++ b/src/PHDR_Sequence_Parser.cpp @@ -0,0 +1,405 @@ +/* +Copyright (c) 2004-2014, John Hurst +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. +3. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/*! \file PHDR_Sequence_Parser.cpp + \version $Id$ + \brief AS-DCP library, JPEG 2000 codestream essence reader implementation +*/ + +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace Kumu; +using namespace ASDCP; + + +//------------------------------------------------------------------------------------------ + +class FileList : public std::list +{ + std::string m_DirName; + +public: + FileList() {} + ~FileList() {} + + const FileList& operator=(const std::list& pathlist) { + std::list::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 ( PathGetExtension(next_file) == "j2c" ) + { + std::string path = PathJoin(m_DirName, next_file); + + if ( ! Kumu::PathIsDirectory(path) ) + push_back(path); + } + } + + sort(); + } + + return result; + } +}; + +//------------------------------------------------------------------------------------------ + +class AS_02::PHDR::SequenceParser::h__SequenceParser +{ + ui32_t m_FramesRead; + Rational m_PictureRate; + FileList m_FileList; + FileList::iterator m_CurrentFile; + ASDCP::JP2K::CodestreamParser m_Parser; + bool m_Pedantic; + + Result_t OpenRead(); + + ASDCP_NO_COPY_CONSTRUCT(h__SequenceParser); + +public: + ASDCP::JP2K::PictureDescriptor m_PDesc; + + h__SequenceParser() : m_FramesRead(0), m_Pedantic(false) + { + memset(&m_PDesc, 0, sizeof(m_PDesc)); + m_PDesc.EditRate = Rational(24,1); + } + + ~h__SequenceParser() + { + Close(); + } + + Result_t OpenRead(const std::string& filename, bool pedantic); + Result_t OpenRead(const std::list& file_list, bool pedantic); + void Close() {} + + Result_t Reset() + { + m_FramesRead = 0; + m_CurrentFile = m_FileList.begin(); + return RESULT_OK; + } + + Result_t ReadFrame(FrameBuffer&); +}; + + +// +ASDCP::Result_t +AS_02::PHDR::SequenceParser::h__SequenceParser::OpenRead() +{ + if ( m_FileList.empty() ) + return RESULT_ENDOFFILE; + + m_CurrentFile = m_FileList.begin(); + ASDCP::JP2K::CodestreamParser Parser; + AS_02::PHDR::FrameBuffer TmpBuffer; + + Kumu::fsize_t file_size = Kumu::FileSize(*m_CurrentFile); + + 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, 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 +AS_02::PHDR::SequenceParser::h__SequenceParser::OpenRead(const std::string& filename, bool pedantic) +{ + m_Pedantic = pedantic; + + Result_t result = m_FileList.InitFromDirectory(filename); + + if ( ASDCP_SUCCESS(result) ) + result = OpenRead(); + + return result; +} + + +// +ASDCP::Result_t +AS_02::PHDR::SequenceParser::h__SequenceParser::OpenRead(const std::list& file_list, bool pedantic) +{ + m_Pedantic = pedantic; + m_FileList = file_list; + return OpenRead(); +} + + +// +bool +operator==(const ASDCP::JP2K::ImageComponent_t& lhs, const ASDCP::JP2K::ImageComponent_t& rhs) +{ + if ( lhs.Ssize != rhs.Ssize ) return false; + if ( lhs.XRsize != rhs.XRsize ) return false; + if ( lhs.YRsize != rhs.YRsize ) return false; + return true; +} + +// +bool +operator==(const ASDCP::JP2K::QuantizationDefault_t& lhs, const ASDCP::JP2K::QuantizationDefault_t& rhs) +{ + if ( lhs.Sqcd != rhs.Sqcd ) return false; + if ( lhs.SPqcdLength != rhs.SPqcdLength ) return false; + + for ( ui32_t i = 0; i < JP2K::MaxDefaults; i++ ) + { + if ( lhs.SPqcd[i] != rhs.SPqcd[i] ) + return false; + } + + return true; +} + +// +bool +operator==(const ASDCP::JP2K::CodingStyleDefault_t& lhs, const ASDCP::JP2K::CodingStyleDefault_t& rhs) +{ + if ( lhs.Scod != rhs.Scod ) return false; + + // SGcod + if ( lhs.SGcod.ProgressionOrder != rhs.SGcod.ProgressionOrder ) return false; + if ( lhs.SGcod.MultiCompTransform != rhs.SGcod.MultiCompTransform ) return false; + + for ( ui32_t i = 0; i < sizeof(ui16_t); i++ ) + { + if ( lhs.SGcod.NumberOfLayers[i] != lhs.SGcod.NumberOfLayers[i] ) + return false; + } + + // SPcod + if ( lhs.SPcod.DecompositionLevels != rhs.SPcod.DecompositionLevels ) return false; + if ( lhs.SPcod.CodeblockWidth != rhs.SPcod.CodeblockWidth ) return false; + if ( lhs.SPcod.CodeblockHeight != rhs.SPcod.CodeblockHeight ) return false; + if ( lhs.SPcod.CodeblockStyle != rhs.SPcod.CodeblockStyle ) return false; + if ( lhs.SPcod.Transformation != rhs.SPcod.Transformation ) return false; + + for ( ui32_t i = 0; i < JP2K::MaxPrecincts; i++ ) + { + if ( lhs.SPcod.PrecinctSize[i] != rhs.SPcod.PrecinctSize[i] ) + return false; + } + + return true; +} + +// +bool +operator==(const ASDCP::JP2K::PictureDescriptor& lhs, const ASDCP::JP2K::PictureDescriptor& rhs) +{ + if ( lhs.EditRate != rhs.EditRate ) return false; + // if ( lhs.ContainerDuration != rhs.ContainerDuration ) return false; + if ( lhs.SampleRate != rhs.SampleRate ) return false; + if ( lhs.StoredWidth != rhs.StoredWidth ) return false; + if ( lhs.StoredHeight != rhs.StoredHeight ) return false; + if ( lhs.AspectRatio != rhs.AspectRatio ) return false; + if ( lhs.Rsize != rhs.Rsize ) return false; + if ( lhs.Xsize != rhs.Xsize ) return false; + if ( lhs.Ysize != rhs.Ysize ) return false; + if ( lhs.XOsize != rhs.XOsize ) return false; + if ( lhs.YOsize != rhs.YOsize ) return false; + if ( lhs.XTsize != rhs.XTsize ) return false; + if ( lhs.YTsize != rhs.YTsize ) return false; + if ( lhs.XTOsize != rhs.XTOsize ) return false; + if ( lhs.YTOsize != rhs.YTOsize ) return false; + if ( lhs.Csize != rhs.Csize ) return false; + if ( ! ( lhs.CodingStyleDefault == rhs.CodingStyleDefault ) ) return false; + if ( ! ( lhs.QuantizationDefault == rhs.QuantizationDefault ) ) return false; + + for ( ui32_t i = 0; i < JP2K::MaxComponents; i++ ) + { + if ( ! ( lhs.ImageComponents[i] == rhs.ImageComponents[i] ) ) + return false; + } + + return true; +} + +// +ASDCP::Result_t +AS_02::PHDR::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, FB); + std::string metadata_path = PathJoin(PathDirname(*m_CurrentFile), PathSetExtension(*m_CurrentFile, "xml")); + + if ( KM_SUCCESS(result) ) + { + result = ReadFileIntoString(metadata_path, FB.OpaqueMetadata); + + if ( KM_FAILURE(result) ) + { + DefaultLogSink().Error("%s: %s\n", metadata_path.c_str(), result.Label()); + } + } + else + { + DefaultLogSink().Error("%s: %s\n", m_CurrentFile->c_str(), result.Label()); + } + + if ( KM_SUCCESS(result) && m_Pedantic ) + { + ASDCP::JP2K::PictureDescriptor PDesc; + result = m_Parser.FillPictureDescriptor(PDesc); + + if ( KM_SUCCESS(result) && ! ( m_PDesc == PDesc ) ) + { + Kumu::DefaultLogSink().Error("JPEG-2000 codestream parameters do not match at frame %d\n", m_FramesRead + 1); + result = RESULT_RAW_FORMAT; + } + } + + if ( KM_SUCCESS(result) ) + { + FB.FrameNumber(m_FramesRead++); + m_CurrentFile++; + } + + return result; +} + + +//------------------------------------------------------------------------------------------ + +AS_02::PHDR::SequenceParser::SequenceParser() +{ +} + +AS_02::PHDR::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 +AS_02::PHDR::SequenceParser::OpenRead(const std::string& filename, bool pedantic) const +{ + const_cast(this)->m_Parser = new h__SequenceParser; + + Result_t result = m_Parser->OpenRead(filename, pedantic); + + if ( ASDCP_FAILURE(result) ) + const_cast(this)->m_Parser.release(); + + return result; +} + +// +Result_t +AS_02::PHDR::SequenceParser::OpenRead(const std::list& file_list, bool pedantic) const +{ + const_cast(this)->m_Parser = new h__SequenceParser; + + Result_t result = m_Parser->OpenRead(file_list, pedantic); + + if ( ASDCP_FAILURE(result) ) + const_cast(this)->m_Parser.release(); + + return result; +} + + +// Rewinds the stream to the beginning. +ASDCP::Result_t +AS_02::PHDR::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 +AS_02::PHDR::SequenceParser::ReadFrame(AS_02::PHDR::FrameBuffer& FB) const +{ + if ( m_Parser.empty() ) + return RESULT_INIT; + + return m_Parser->ReadFrame(FB); +} + +// +ASDCP::Result_t +AS_02::PHDR::SequenceParser::FillPictureDescriptor(ASDCP::JP2K::PictureDescriptor& PDesc) const +{ + if ( m_Parser.empty() ) + return RESULT_INIT; + + PDesc = m_Parser->m_PDesc; + return RESULT_OK; +} + + +// +// end PHDR_Sequence_Parser.cpp +// diff --git a/src/h__Writer.cpp b/src/h__Writer.cpp index e05cadb..b407ce6 100755 --- a/src/h__Writer.cpp +++ b/src/h__Writer.cpp @@ -65,7 +65,7 @@ ASDCP::AddDMScrypt(Partition& HeaderPart, SourcePackage& Package, HeaderPart.AddChildObject(Segment); Seq->StructuralComponents.push_back(Segment->InstanceUID); Segment->EventComment = "AS-DCP KLV Encryption"; - + CryptographicFramework* CFW = new CryptographicFramework(Dict); HeaderPart.AddChildObject(CFW); Segment->DMFramework = CFW->InstanceUID; diff --git a/src/phdr-unwrap.cpp b/src/phdr-unwrap.cpp new file mode 100755 index 0000000..162bb73 --- /dev/null +++ b/src/phdr-unwrap.cpp @@ -0,0 +1,427 @@ +/* +Copyright (c) 2011-2014, John Hurst + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. +3. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/*! \file phdr-unwrap.cpp + \version $Id$ + \brief prototype unwrapping for HDR images in AS-02 + + This program extracts picture (P-HDR picture) from an AS-02 MXF file. +*/ + +#include +#include + +using namespace ASDCP; + +const ui32_t FRAME_BUFFER_SIZE = 4 * Kumu::Megabyte; + +//------------------------------------------------------------------------------------------ +// +// command line option parser class + +static const char* PROGRAM_NAME = "as-02-unwrap"; // program name for messages + +// Increment the iterator, test for an additional non-option command line argument. +// Causes the caller to return if there are no remaining arguments or if the next +// argument begins with '-'. +#define TEST_EXTRA_ARG(i,c) \ + if ( ++i >= argc || argv[(i)][0] == '-' ) { \ + fprintf(stderr, "Argument not found for option -%c.\n", (c)); \ + return; \ + } + +// +void +banner(FILE* stream = stdout) +{ + fprintf(stream, "\n\ +%s (asdcplib %s)\n\n\ +Copyright (c) 2011-2014, John Hurst\n\n\ +asdcplib may be copied only under the terms of the license found at\n\ +the top of every file in the asdcplib distribution kit.\n\n\ +Specify the -h (help) option for further information about %s\n\n", + PROGRAM_NAME, ASDCP::Version(), PROGRAM_NAME); +} + +// +void +usage(FILE* stream = stdout) +{ + fprintf(stream, "\ +USAGE: %s [-h|-help] [-V]\n\ +\n\ + %s [-b ] [-d ]\n\ + [-f ] [-m] [-R] [-s ] [-v] [-W]\n\ + [-w] []\n\n", + PROGRAM_NAME, PROGRAM_NAME, PROGRAM_NAME); + + fprintf(stream, "\ +Options:\n\ + -b - Specify size in bytes of picture frame buffer\n\ + Defaults to 4,194,304 (4MB)\n\ + -d - Number of frames to process, default all\n\ + -f - Starting frame number, default 0\n\ + -h | -help - Show help\n\ + -k - Use key for ciphertext operations\n\ + -m - verify HMAC values when reading\n\ + -s - Number of bytes to dump to output when -v is given\n\ + -V - Show version information\n\ + -v - Verbose, prints informative messages to stderr\n\ + -W - Read input file only, do not write destination file\n\ + -w - Width of numeric element in a series of frame file names\n\ + (default 6)\n\ +\n\ + NOTES: o There is no option grouping, all options must be distinct arguments.\n\ + o All option arguments must be separated from the option by whitespace.\n\n"); +} + +// +class CommandOptions +{ + CommandOptions(); + +public: + bool error_flag; // true if the given options are in error or not complete + bool key_flag; // true if an encryption key was given + bool read_hmac; // true if HMAC values are to be validated + bool verbose_flag; // true if the verbose option was selected + ui32_t fb_dump_size; // number of bytes of frame buffer to dump + bool no_write_flag; // true if no output files are to be written + bool version_flag; // true if the version display option was selected + bool help_flag; // true if the help display option was selected + bool stereo_image_flag; // if true, expect stereoscopic JP2K input (left eye first) + ui32_t number_width; // number of digits in a serialized filename (for JPEG extract) + ui32_t start_frame; // frame number to begin processing + ui32_t duration; // number of frames to be processed + bool duration_flag; // true if duration argument given + ui32_t fb_size; // size of picture frame buffer + const char* file_prefix; // filename pre for files written by the extract mode + byte_t key_value[KeyLen]; // value of given encryption key (when key_flag is true) + byte_t key_id_value[UUIDlen];// value of given key ID (when key_id_flag is true) + const char* input_filename; + std::string prefix_buffer; + + // + CommandOptions(int argc, const char** argv) : + error_flag(true), key_flag(false), read_hmac(false), verbose_flag(false), + fb_dump_size(0), no_write_flag(false), + version_flag(false), help_flag(false), number_width(6), + start_frame(0), duration(0xffffffff), duration_flag(false), + fb_size(FRAME_BUFFER_SIZE), file_prefix(0), + input_filename(0) + { + memset(key_value, 0, KeyLen); + memset(key_id_value, 0, UUIDlen); + + for ( int i = 1; i < argc; ++i ) + { + + if ( (strcmp( argv[i], "-help") == 0) ) + { + help_flag = true; + continue; + } + + if ( argv[i][0] == '-' + && ( isalpha(argv[i][1]) || isdigit(argv[i][1]) ) + && argv[i][2] == 0 ) + { + switch ( argv[i][1] ) + { + case 'b': + TEST_EXTRA_ARG(i, 'b'); + fb_size = abs(atoi(argv[i])); + + if ( verbose_flag ) + fprintf(stderr, "Frame Buffer size: %u bytes.\n", fb_size); + + break; + + case 'd': + TEST_EXTRA_ARG(i, 'd'); + duration_flag = true; + duration = abs(atoi(argv[i])); + break; + + case 'f': + TEST_EXTRA_ARG(i, 'f'); + start_frame = abs(atoi(argv[i])); + break; + + case 'h': help_flag = true; break; + case 'm': read_hmac = true; break; + + case 's': + TEST_EXTRA_ARG(i, 's'); + fb_dump_size = abs(atoi(argv[i])); + break; + + case 'V': version_flag = true; break; + case 'v': verbose_flag = true; break; + case 'W': no_write_flag = true; break; + + case 'w': + TEST_EXTRA_ARG(i, 'w'); + number_width = abs(atoi(argv[i])); + break; + + default: + fprintf(stderr, "Unrecognized option: %s\n", argv[i]); + return; + } + } + else + { + if ( argv[i][0] != '-' ) + { + if ( input_filename == 0 ) + { + input_filename = argv[i]; + } + else if ( file_prefix == 0 ) + { + file_prefix = argv[i]; + } + } + else + { + fprintf(stderr, "Unrecognized argument: %s\n", argv[i]); + return; + } + } + } + + if ( help_flag || version_flag ) + return; + + if ( input_filename == 0 ) + { + fputs("At least one filename argument is required.\n", stderr); + return; + } + + if ( file_prefix == 0 ) + { + prefix_buffer = Kumu::PathSetExtension(input_filename, "") + "_"; + file_prefix = prefix_buffer.c_str(); + } + + error_flag = false; + } +}; + + +//------------------------------------------------------------------------------------------ +// JPEG 2000 essence + + +// Read one or more plaintext JPEG 2000 codestreams from a plaintext P-HDR file +// Read one or more plaintext JPEG 2000 codestreams from a ciphertext P-HDR file +// Read one or more ciphertext JPEG 2000 codestreams from a ciphertext P-HDR file +// +Result_t +read_JP2K_file(CommandOptions& Options) +{ + AESDecContext* Context = 0; + HMACContext* HMAC = 0; + AS_02::PHDR::MXFReader Reader; + AS_02::PHDR::FrameBuffer FrameBuffer(Options.fb_size); + ui32_t frame_count = 0; + + std::string PHDR_master_metadata; // todo: write to a file? + + Result_t result = Reader.OpenRead(Options.input_filename, PHDR_master_metadata); + fprintf(stderr, "PHDR_master_metadata size=%d\n", PHDR_master_metadata.size()); + + if ( ASDCP_SUCCESS(result) ) + { + if ( Options.verbose_flag ) + { + fprintf(stderr, "Frame Buffer size: %u\n", Options.fb_size); + } + + ASDCP::MXF::RGBAEssenceDescriptor *rgba_descriptor = 0; + ASDCP::MXF::CDCIEssenceDescriptor *cdci_descriptor = 0; + + result = Reader.OP1aHeader().GetMDObjectByType(DefaultCompositeDict().ul(MDD_RGBAEssenceDescriptor), + reinterpret_cast(&rgba_descriptor)); + + if ( KM_SUCCESS(result) ) + { + assert(rgba_descriptor); + frame_count = rgba_descriptor->ContainerDuration; + + if ( Options.verbose_flag ) + { + rgba_descriptor->Dump(); + } + } + else + { + result = Reader.OP1aHeader().GetMDObjectByType(DefaultCompositeDict().ul(MDD_CDCIEssenceDescriptor), + reinterpret_cast(&cdci_descriptor)); + + if ( KM_SUCCESS(result) ) + { + assert(cdci_descriptor); + frame_count = cdci_descriptor->ContainerDuration; + + if ( Options.verbose_flag ) + { + cdci_descriptor->Dump(); + } + } + else + { + fprintf(stderr, "File does not contain an essence descriptor.\n"); + frame_count = Reader.AS02IndexReader().GetDuration(); + } + } + + if ( frame_count == 0 ) + { + frame_count = Reader.AS02IndexReader().GetDuration(); + } + + if ( frame_count == 0 ) + { + fprintf(stderr, "Unable to determine file duration.\n"); + return RESULT_FAIL; + } + } + + if ( ASDCP_SUCCESS(result) && Options.key_flag ) + { + Context = new AESDecContext; + result = Context->InitKey(Options.key_value); + + if ( ASDCP_SUCCESS(result) && Options.read_hmac ) + { + WriterInfo Info; + Reader.FillWriterInfo(Info); + + if ( Info.UsesHMAC ) + { + HMAC = new HMACContext; + result = HMAC->InitKey(Options.key_value, Info.LabelSetType); + } + else + { + fputs("File does not contain HMAC values, ignoring -m option.\n", stderr); + } + } + } + + ui32_t last_frame = Options.start_frame + ( Options.duration ? Options.duration : frame_count); + if ( last_frame > frame_count ) + last_frame = frame_count; + + char name_format[64]; + snprintf(name_format, 64, "%%s%%0%du.j2c", Options.number_width); + + for ( ui32_t i = Options.start_frame; ASDCP_SUCCESS(result) && i < last_frame; i++ ) + { + result = Reader.ReadFrame(i, FrameBuffer, Context, HMAC); + + if ( ASDCP_SUCCESS(result) ) + { + Kumu::FileWriter OutFile; + char filename[256]; + ui32_t write_count; + snprintf(filename, 256, name_format, Options.file_prefix, i); + result = OutFile.OpenWrite(filename); + + if ( ASDCP_SUCCESS(result) ) + result = OutFile.Write(FrameBuffer.Data(), FrameBuffer.Size(), &write_count); + + if ( Options.verbose_flag ) + FrameBuffer.Dump(stderr, Options.fb_dump_size); + } + } + + return result; +} + +// +int +main(int argc, const char** argv) +{ + char str_buf[64]; + CommandOptions Options(argc, argv); + + if ( Options.version_flag ) + banner(); + + if ( Options.help_flag ) + usage(); + + if ( Options.version_flag || Options.help_flag ) + return 0; + + if ( Options.error_flag ) + { + fprintf(stderr, "There was a problem. Type %s -h for help.\n", PROGRAM_NAME); + return 3; + } + + EssenceType_t EssenceType; + Result_t result = ASDCP::EssenceType(Options.input_filename, EssenceType); + + if ( ASDCP_SUCCESS(result) ) + { + switch ( EssenceType ) + { + case ESS_AS02_JPEG_2000: + result = read_JP2K_file(Options); + break; + + default: + fprintf(stderr, "%s: Unknown file type, not P-HDR essence.\n", Options.input_filename); + return 5; + } + } + + if ( ASDCP_FAILURE(result) ) + { + fputs("Program stopped on error.\n", stderr); + + if ( result != RESULT_FAIL ) + { + fputs(result, stderr); + fputc('\n', stderr); + } + + return 1; + } + + return 0; +} + + +// +// end phdr-unwrap.cpp +// diff --git a/src/phdr-wrap.cpp b/src/phdr-wrap.cpp new file mode 100755 index 0000000..a00d091 --- /dev/null +++ b/src/phdr-wrap.cpp @@ -0,0 +1,680 @@ +/* +Copyright (c) 2011-2014, John Hurst + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. +3. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/*! \file phdr-wrap.cpp + \version $Id$ + \brief prototype wrapping for HDR images in AS-02 + + This program wraps IMF essence (P-HDR picture) in to an AS-02 MXF file. +*/ + +#include +#include +#include + +using namespace ASDCP; + +const ui32_t FRAME_BUFFER_SIZE = 4 * Kumu::Megabyte; +const ASDCP::Dictionary *g_dict = 0; + + +const char* +RationalToString(const ASDCP::Rational& r, char* buf, const ui32_t& len) +{ + snprintf(buf, len, "%d/%d", r.Numerator, r.Denominator); + return buf; +} + + + +//------------------------------------------------------------------------------------------ +// +// command line option parser class + +static const char* PROGRAM_NAME = "as-02-wrap"; // program name for messages + +// local program identification info written to file headers +class MyInfo : public WriterInfo +{ +public: + MyInfo() + { + static byte_t default_ProductUUID_Data[UUIDlen] = + { 0xef, 0xe4, 0x59, 0xab, 0xbe, 0x0f, 0x4c, 0x7d, + 0xb3, 0xa2, 0xb8, 0x96, 0x79, 0xe2, 0x3e, 0x8e }; + + memcpy(ProductUUID, default_ProductUUID_Data, UUIDlen); + CompanyName = "WidgetCo"; + ProductName = "phdr-wrap"; + ProductVersion = ASDCP::Version(); + } +} s_MyInfo; + + + +// Increment the iterator, test for an additional non-option command line argument. +// Causes the caller to return if there are no remaining arguments or if the next +// argument begins with '-'. +#define TEST_EXTRA_ARG(i,c) \ + if ( ++i >= argc || argv[(i)][0] == '-' ) { \ + fprintf(stderr, "Argument not found for option -%c.\n", (c)); \ + return; \ + } + +// +void +banner(FILE* stream = stdout) +{ + fprintf(stream, "\n\ +%s (asdcplib %s)\n\n\ +Copyright (c) 2011-2014, John Hurst\n\n\ +asdcplib may be copied only under the terms of the license found at\n\ +the top of every file in the asdcplib distribution kit.\n\n\ +Specify the -h (help) option for further information about %s\n\n", + PROGRAM_NAME, ASDCP::Version(), PROGRAM_NAME); +} + +// +void +usage(FILE* stream = stdout) +{ + fprintf(stream, "\ +USAGE: %s [-h|-help] [-V]\n\ +\n\ + %s [-a ] [-A /] [-b ] [-C
    ] [-d ]\n\ + [-D ] [-e|-E] [-i] [-j ] [-k ]\n\ + [-M] [-m ] [-p
      ] [-r /] [-R] [-s ]\n\ + [-t ] [-T ] [-u] [-v] [-W] [-x ] [-X ] [-Y]\n\ + [-z|-Z] + \n\n", + PROGRAM_NAME, PROGRAM_NAME); + + fprintf(stream, "\ +Options:\n\ + -h | -help - Show help\n\ + -V - Show version information\n\ + -a - Specify the Asset ID of the file\n\ + -A / - Set aspect ratio for image (default 4/3)\n\ + -b - Specify size in bytes of picture frame buffer\n\ + Defaults to 4,194,304 (4MB)\n\ + -C
        - Set ChannelAssignment UL value\n\ + -d - Number of frames to process, default all\n\ + -D - Component depth for YCbCr images (default: 10)\n\ + -e - Encrypt JP2K headers (default)\n\ + -E - Do not encrypt JP2K headers\n\ + -F (0|1) - Set field dominance for interlaced image (default: 0)\n\ + -i - Indicates input essence is interlaced fields (forces -Y)\n\ + -j - Write key ID instead of creating a random value\n\ + -k - Use key for ciphertext operations\n\ + -M - Do not create HMAC values when writing\n\ + -m - Filename of master metadata instance (the contents of\n\ + which will be placed in the MXF wrapper)\n\ + -p
          - Set broadcast profile\n\ + -r / - Edit Rate of the output file. 24/1 is the default\n\ + -R - Indicates RGB image essence (default)\n\ + -s - Duration of a frame-wrapped partition (default 60)\n\ + -t - Set RGB component minimum code value (default: 0)\n\ + -T - Set RGB component maximum code value (default: 1023)\n\ + -u - Print UL catalog to stderr\n\ + -v - Verbose, prints informative messages to stderr\n\ + -W - Read input file only, do not write source file\n\ + -x - Horizontal subsampling degree (default: 2)\n\ + -X - Vertical subsampling degree (default: 2)\n\ + -Y - Indicates YCbCr image essence (default: RGB)\n\ + -z - Fail if j2c inputs have unequal parameters (default)\n\ + -Z - Ignore unequal parameters in j2c inputs\n\ +\n\ + NOTES: o There is no option grouping, all options must be distinct arguments.\n\ + o All option arguments must be separated from the option by whitespace.\n\n"); +} + +// +static ASDCP::Rational +decode_rational(const char* str_rat) +{ + assert(str_rat); + ui32_t Num = atoi(str_rat); + ui32_t Den = 0; + + const char* den_str = strrchr(str_rat, '/'); + if ( den_str != 0 ) + Den = atoi(den_str+1); + + return ASDCP::Rational(Num, Den); +} + +// +// +class CommandOptions +{ + CommandOptions(); + +public: + bool error_flag; // true if the given options are in error or not complete + bool key_flag; // true if an encryption key was given + bool asset_id_flag; // true if an asset ID was given + bool encrypt_header_flag; // true if j2c headers are to be encrypted + bool write_hmac; // true if HMAC values are to be generated and written + bool verbose_flag; // true if the verbose option was selected + ui32_t fb_dump_size; // number of bytes of frame buffer to dump + bool no_write_flag; // true if no output files are to be written + bool version_flag; // true if the version display option was selected + bool help_flag; // true if the help display option was selected + ui32_t duration; // number of frames to be processed + bool j2c_pedantic; // passed to JP2K::SequenceParser::OpenRead + bool use_cdci_descriptor; // + Rational edit_rate; // edit rate of JP2K sequence + ui32_t fb_size; // size of picture frame buffer + byte_t key_value[KeyLen]; // value of given encryption key (when key_flag is true) + bool key_id_flag; // true if a key ID was given + byte_t key_id_value[UUIDlen];// value of given key ID (when key_id_flag is true) + byte_t asset_id_value[UUIDlen];// value of asset ID (when asset_id_flag is true) + std::string out_file; // + bool show_ul_values_flag; /// if true, dump the UL table before going tp work. + Kumu::PathList_t filenames; // list of filenames to be processed + + UL picture_coding; + ui32_t rgba_MaxRef; + ui32_t rgba_MinRef; + + ui32_t horizontal_subsampling; + ui32_t vertical_subsampling; + ui32_t component_depth; + ui8_t frame_layout; + ASDCP::Rational aspect_ratio; + ui8_t field_dominance; + ui32_t mxf_header_size; + + //new attributes for AS-02 support + AS_02::IndexStrategy_t index_strategy; //Shim parameter index_strategy_frame/clip + ui32_t partition_space; //Shim parameter partition_spacing + + std::string PHDR_master_metadata; // + + // + CommandOptions(int argc, const char** argv) : + error_flag(true), key_flag(false), key_id_flag(false), asset_id_flag(false), + encrypt_header_flag(true), write_hmac(true), verbose_flag(false), fb_dump_size(0), + no_write_flag(false), version_flag(false), help_flag(false), + duration(0xffffffff), j2c_pedantic(true), use_cdci_descriptor(false), edit_rate(24,1), fb_size(FRAME_BUFFER_SIZE), + show_ul_values_flag(false), index_strategy(AS_02::IS_FOLLOW), partition_space(60), + rgba_MaxRef(1023), rgba_MinRef(0), + horizontal_subsampling(2), vertical_subsampling(2), component_depth(10), + frame_layout(0), aspect_ratio(ASDCP::Rational(4,3)), field_dominance(0), + mxf_header_size(16384) + { + memset(key_value, 0, KeyLen); + memset(key_id_value, 0, UUIDlen); + + for ( int i = 1; i < argc; i++ ) + { + + if ( (strcmp( argv[i], "-help") == 0) ) + { + help_flag = true; + continue; + } + + if ( argv[i][0] == '-' + && ( isalpha(argv[i][1]) || isdigit(argv[i][1]) ) + && argv[i][2] == 0 ) + { + switch ( argv[i][1] ) + { + case 'A': + TEST_EXTRA_ARG(i, 'A'); + edit_rate = decode_rational(argv[i]); + break; + + case 'a': + asset_id_flag = true; + TEST_EXTRA_ARG(i, 'a'); + { + ui32_t length; + Kumu::hex2bin(argv[i], asset_id_value, UUIDlen, &length); + + if ( length != UUIDlen ) + { + fprintf(stderr, "Unexpected asset ID length: %u, expecting %u characters.\n", length, UUIDlen); + return; + } + } + break; + + case 'b': + TEST_EXTRA_ARG(i, 'b'); + fb_size = abs(atoi(argv[i])); + + if ( verbose_flag ) + fprintf(stderr, "Frame Buffer size: %u bytes.\n", fb_size); + + break; + + case 'D': + TEST_EXTRA_ARG(i, 'D'); + component_depth = abs(atoi(argv[i])); + break; + + case 'd': + TEST_EXTRA_ARG(i, 'd'); + duration = abs(atoi(argv[i])); + break; + + case 'E': encrypt_header_flag = false; break; + case 'e': encrypt_header_flag = true; break; + + case 'F': + TEST_EXTRA_ARG(i, 'F'); + field_dominance = abs(atoi(argv[i])); + if ( field_dominance > 1 ) + { + fprintf(stderr, "Field dominance value must be \"0\" or \"1\"\n"); + return; + } + break; + + case 'h': help_flag = true; break; + + case 'i': + frame_layout = 1; + use_cdci_descriptor = true; + break; + + case 'j': + key_id_flag = true; + TEST_EXTRA_ARG(i, 'j'); + { + ui32_t length; + Kumu::hex2bin(argv[i], key_id_value, UUIDlen, &length); + + if ( length != UUIDlen ) + { + fprintf(stderr, "Unexpected key ID length: %u, expecting %u characters.\n", length, UUIDlen); + return; + } + } + break; + + case 'k': key_flag = true; + TEST_EXTRA_ARG(i, 'k'); + { + ui32_t length; + Kumu::hex2bin(argv[i], key_value, KeyLen, &length); + + if ( length != KeyLen ) + { + fprintf(stderr, "Unexpected key length: %u, expecting %u characters.\n", length, KeyLen); + return; + } + } + break; + + case 'M': write_hmac = false; break; + + case 'm': + TEST_EXTRA_ARG(i, 'm'); + if ( KM_FAILURE(Kumu::ReadFileIntoString(argv[i], PHDR_master_metadata) ) ) + { + fprintf(stderr, "Unable to read metadata file %s\n", argv[i]); + return; + } + break; + + case 'p': + TEST_EXTRA_ARG(i, 'p'); + if ( ! picture_coding.DecodeHex(argv[i]) ) + { + fprintf(stderr, "Error decoding PictureEssenceCoding UL value: %s\n", argv[i]); + return; + } + break; + + case 'r': + TEST_EXTRA_ARG(i, 'r'); + edit_rate = decode_rational(argv[i]); + break; + + case 'R': + use_cdci_descriptor = false; + break; + + case 's': + TEST_EXTRA_ARG(i, 's'); + partition_space = abs(atoi(argv[i])); + break; + + case 't': + TEST_EXTRA_ARG(i, 't'); + rgba_MinRef = abs(atoi(argv[i])); + break; + + case 'T': + TEST_EXTRA_ARG(i, 'T'); + rgba_MaxRef = abs(atoi(argv[i])); + break; + + case 'u': show_ul_values_flag = true; break; + case 'V': version_flag = true; break; + case 'v': verbose_flag = true; break; + case 'W': no_write_flag = true; break; + + case 'x': + TEST_EXTRA_ARG(i, 'x'); + horizontal_subsampling = abs(atoi(argv[i])); + break; + + case 'X': + TEST_EXTRA_ARG(i, 'X'); + vertical_subsampling = abs(atoi(argv[i])); + break; + + case 'Y': + use_cdci_descriptor = true; + break; + + case 'Z': j2c_pedantic = false; break; + case 'z': j2c_pedantic = true; break; + + default: + fprintf(stderr, "Unrecognized option: %s\n", argv[i]); + return; + } + } + else + { + + if ( argv[i][0] != '-' ) + { + filenames.push_back(argv[i]); + } + else + { + fprintf(stderr, "Unrecognized argument: %s\n", argv[i]); + return; + } + } + } + + if ( help_flag || version_flag ) + return; + + if ( filenames.size() < 2 ) + { + fputs("Option requires at least two filename arguments: \n", stderr); + return; + } + + out_file = filenames.back(); + filenames.pop_back(); + + if ( ! picture_coding.HasValue() ) + { + picture_coding = UL(g_dict->ul(MDD_JP2KEssenceCompression_BroadcastProfile_1)); + } + + error_flag = false; + } +}; + + +//------------------------------------------------------------------------------------------ +// JPEG 2000 essence + +namespace ASDCP { + Result_t JP2K_PDesc_to_MD(const ASDCP::JP2K::PictureDescriptor& PDesc, + const ASDCP::Dictionary& dict, + ASDCP::MXF::GenericPictureEssenceDescriptor& GenericPictureEssenceDescriptor, + ASDCP::MXF::JPEG2000PictureSubDescriptor& EssenceSubDescriptor); +} + +// Write one or more plaintext JPEG 2000 codestreams to a plaintext AS-02 file +// Write one or more plaintext JPEG 2000 codestreams to a ciphertext AS-02 file +// +Result_t +write_JP2K_file(CommandOptions& Options) +{ + AS_02::PHDR::MXFWriter Writer; + AS_02::PHDR::FrameBuffer FrameBuffer(Options.fb_size); + AS_02::PHDR::SequenceParser Parser; + + AESEncContext* Context = 0; + HMACContext* HMAC = 0; + byte_t IV_buf[CBC_BLOCK_SIZE]; + Kumu::FortunaRNG RNG; + + ASDCP::MXF::FileDescriptor *essence_descriptor = 0; + ASDCP::MXF::InterchangeObject_list_t essence_sub_descriptors; + + // set up essence parser + Result_t result = Parser.OpenRead(Options.filenames.front().c_str(), Options.j2c_pedantic); + + // set up MXF writer + if ( ASDCP_SUCCESS(result) ) + { + ASDCP::JP2K::PictureDescriptor PDesc; + Parser.FillPictureDescriptor(PDesc); + PDesc.EditRate = Options.edit_rate; + + if ( Options.verbose_flag ) + { + fprintf(stderr, "JPEG 2000 P-HDR pictures\n"); + fputs("PictureDescriptor:\n", stderr); + fprintf(stderr, "Frame Buffer size: %u\n", Options.fb_size); + JP2K::PictureDescriptorDump(PDesc); + } + + if ( Options.use_cdci_descriptor ) + { + ASDCP::MXF::CDCIEssenceDescriptor* tmp_dscr = new ASDCP::MXF::CDCIEssenceDescriptor(g_dict); + essence_sub_descriptors.push_back(new ASDCP::MXF::JPEG2000PictureSubDescriptor(g_dict)); + + result = ASDCP::JP2K_PDesc_to_MD(PDesc, *g_dict, + *static_cast(tmp_dscr), + *static_cast(essence_sub_descriptors.back())); + + if ( ASDCP_SUCCESS(result) ) + { + tmp_dscr->PictureEssenceCoding = Options.picture_coding; + tmp_dscr->HorizontalSubsampling = Options.horizontal_subsampling; + tmp_dscr->VerticalSubsampling = Options.vertical_subsampling; + tmp_dscr->ComponentDepth = Options.component_depth; + tmp_dscr->FrameLayout = Options.frame_layout; + tmp_dscr->AspectRatio = Options.aspect_ratio; + tmp_dscr->FieldDominance = Options.field_dominance; + essence_descriptor = static_cast(tmp_dscr); + } + } + else + { // use RGB + ASDCP::MXF::RGBAEssenceDescriptor* tmp_dscr = new ASDCP::MXF::RGBAEssenceDescriptor(g_dict); + essence_sub_descriptors.push_back(new ASDCP::MXF::JPEG2000PictureSubDescriptor(g_dict)); + + result = ASDCP::JP2K_PDesc_to_MD(PDesc, *g_dict, + *static_cast(tmp_dscr), + *static_cast(essence_sub_descriptors.back())); + + if ( ASDCP_SUCCESS(result) ) + { + tmp_dscr->PictureEssenceCoding = UL(g_dict->ul(MDD_JP2KEssenceCompression_BroadcastProfile_1)); + tmp_dscr->ComponentMaxRef = Options.rgba_MaxRef; + tmp_dscr->ComponentMinRef = Options.rgba_MinRef; + essence_descriptor = static_cast(tmp_dscr); + } + } + } + + if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag ) + { + WriterInfo Info = s_MyInfo; // fill in your favorite identifiers here + Info.LabelSetType = LS_MXF_SMPTE; + + if ( Options.asset_id_flag ) + memcpy(Info.AssetUUID, Options.asset_id_value, UUIDlen); + else + Kumu::GenRandomUUID(Info.AssetUUID); + + // configure encryption + if( Options.key_flag ) + { + Kumu::GenRandomUUID(Info.ContextID); + Info.EncryptedEssence = true; + + if ( Options.key_id_flag ) + memcpy(Info.CryptographicKeyID, Options.key_id_value, UUIDlen); + else + RNG.FillRandom(Info.CryptographicKeyID, UUIDlen); + + Context = new AESEncContext; + result = Context->InitKey(Options.key_value); + + if ( ASDCP_SUCCESS(result) ) + result = Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE)); + + if ( ASDCP_SUCCESS(result) && Options.write_hmac ) + { + Info.UsesHMAC = true; + HMAC = new HMACContext; + result = HMAC->InitKey(Options.key_value, Info.LabelSetType); + } + } + + if ( ASDCP_SUCCESS(result) ) + { + result = Writer.OpenWrite(Options.out_file, Info, essence_descriptor, essence_sub_descriptors, + Options.edit_rate, Options.mxf_header_size, Options.index_strategy, Options.partition_space); + } + } + + if ( ASDCP_SUCCESS(result) ) + { + ui32_t duration = 0; + result = Parser.Reset(); + + while ( ASDCP_SUCCESS(result) && duration++ < Options.duration ) + { + result = Parser.ReadFrame(FrameBuffer); + + if ( ASDCP_SUCCESS(result) ) + { + if ( Options.verbose_flag ) + FrameBuffer.Dump(stderr, Options.fb_dump_size); + + if ( Options.encrypt_header_flag ) + FrameBuffer.PlaintextOffset(0); + } + + if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag ) + { + result = Writer.WriteFrame(FrameBuffer, Context, HMAC); + + // The Writer class will forward the last block of ciphertext + // to the encryption context for use as the IV for the next + // frame. If you want to use non-sequitur IV values, un-comment + // the following line of code. + // if ( ASDCP_SUCCESS(result) && Options.key_flag ) + // Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE)); + } + } + + if ( result == RESULT_ENDOFFILE ) + result = RESULT_OK; + } + + if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag ) + result = Writer.Finalize(Options.PHDR_master_metadata); + + return result; +} + +// +int +main(int argc, const char** argv) +{ + Result_t result = RESULT_OK; + char str_buf[64]; + g_dict = &ASDCP::DefaultSMPTEDict(); + assert(g_dict); + + CommandOptions Options(argc, argv); + + if ( Options.version_flag ) + banner(); + + if ( Options.help_flag ) + usage(); + + if ( Options.show_ul_values_flag ) + { + g_dict->Dump(stdout); + } + + if ( Options.version_flag || Options.help_flag || Options.show_ul_values_flag ) + return 0; + + if ( Options.error_flag ) + { + fprintf(stderr, "There was a problem. Type %s -h for help.\n", PROGRAM_NAME); + return 3; + } + + EssenceType_t EssenceType; + result = ASDCP::RawEssenceType(Options.filenames.front().c_str(), EssenceType); + + if ( ASDCP_SUCCESS(result) ) + { + switch ( EssenceType ) + { + case ESS_JPEG_2000: + result = write_JP2K_file(Options); + break; + + default: + fprintf(stderr, "%s: Unknown file type, not P-HDR-compatible essence.\n", + Options.filenames.front().c_str()); + return 5; + } + } + + if ( ASDCP_FAILURE(result) ) + { + fputs("Program stopped on error.\n", stderr); + + if ( result != RESULT_FAIL ) + { + fputs(result, stderr); + fputc('\n', stderr); + } + + return 1; + } + + return 0; +} + + +// +// end phdr-wrap.cpp +// -- 2.30.2