diff options
| author | jhurst <jhurst@cinecert.com> | 2018-09-14 07:27:20 +0000 |
|---|---|---|
| committer | jhurst <> | 2018-09-14 07:27:20 +0000 |
| commit | 1193b2819266ca836d0b319d777e4e1a1cb51a49 (patch) | |
| tree | 95e8d9623f9fc5fa61a5ff1451e7fd6b11fb8ae4 /src/AS_02_ACES.cpp | |
| parent | 9ff2485b9d68a206a00835e1e9862c24eee7eabc (diff) | |
ACES contribution from AMPAS/Ruppel
Diffstat (limited to 'src/AS_02_ACES.cpp')
| -rw-r--r-- | src/AS_02_ACES.cpp | 977 |
1 files changed, 977 insertions, 0 deletions
diff --git a/src/AS_02_ACES.cpp b/src/AS_02_ACES.cpp new file mode 100644 index 0000000..cac72c4 --- /dev/null +++ b/src/AS_02_ACES.cpp @@ -0,0 +1,977 @@ +/* +Copyright (c) 2018, Bjoern Stresing, Patrick Bichiou, Wolfgang Ruppel, +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. +*/ + + +#include "AS_02_ACES.h" +//#include "info.h" +#include "AS_02_internal.h" +#include <limits> +#include <iostream> +#include <openssl/sha.h> + +#ifdef min +#undef min +#undef max +#endif + +using namespace Kumu; + +const char* +AS_02::ACES::MIME2str(AS_02::ACES::MIMEType_t m) +{ + if(m == AS_02::ACES::MT_PNG) return "image/png"; + else if(m == AS_02::ACES::MT_TIFF) return "image/tiff"; + return "application/octet-stream"; +} + +using Kumu::GenRandomValue; + +//------------------------------------------------------------------------------------------ +static std::string ACES_PACKAGE_LABEL = "File Package: frame wrapping of ACES codestreams"; +static std::string PICT_DEF_LABEL = "Image Track"; +//------------------------------------------------------------------------------------------ + + +typedef std::map<Kumu::UUID, Kumu::UUID> ResourceMap_t; + +ASDCP::Rational AS_02::ConvertToRational(double in) +{ + + //rational approximation to given real number + //David Eppstein / UC Irvine / 8 Aug 1993 + i32_t m[2][2]; + double x, startx; + i32_t maxden = std::numeric_limits<i32_t>::max(); + i32_t ai; + + startx = x = in; + + /* initialize matrix */ + m[0][0] = m[1][1] = 1; + m[0][1] = m[1][0] = 0; + + /* loop finding terms until denom gets too big */ + while(m[1][0] * (ai = (long)x) + m[1][1] <= maxden) + { + i32_t t; + t = m[0][0] * ai + m[0][1]; + m[0][1] = m[0][0]; + m[0][0] = t; + t = m[1][0] * ai + m[1][1]; + m[1][1] = m[1][0]; + m[1][0] = t; + if(x == (double)ai) break; // AF: division by zero + x = 1 / (x - (double)ai); + if(x > (double)0x7FFFFFFF) break; // AF: representation failure + } + + /* now remaining x is between 0 and 1/ai */ + /* approx as either 0 or 1/m where m is max that will fit in maxden */ + /* first try zero */ + //printf("%ld/%ld, error = %e\n", m[0][0], m[1][0], startx - ((double)m[0][0] / (double)m[1][0])); + + return(ASDCP::Rational(m[0][0], m[1][0])); + /* now try other possibility */ + // ai = (maxden - m[1][1]) / m[1][0]; + // m[0][0] = m[0][0] * ai + m[0][1]; + // m[1][0] = m[1][0] * ai + m[1][1]; + // printf("%ld/%ld, error = %e\n", m[0][0], m[1][0], startx - ((double)m[0][0] / (double)m[1][0])); +} + +std::ostream& AS_02::ACES::operator<<(std::ostream& strm, const PictureDescriptor& PDesc) +{ + + strm << " EditRate: " << PDesc.EditRate.Numerator << "/" << PDesc.EditRate.Denominator << std::endl; + strm << " SampleRate: " << PDesc.SampleRate.Numerator << "/" << PDesc.SampleRate.Denominator << std::endl; + strm << " Chromaticities: " << std::endl; + strm << " x_red: " << PDesc.Chromaticities.red.x << " y_red: " << PDesc.Chromaticities.red.y << std::endl; + strm << " x_green: " << PDesc.Chromaticities.green.x << " y_green: " << PDesc.Chromaticities.green.y << std::endl; + strm << " x_blue: " << PDesc.Chromaticities.blue.x << " y_blue: " << PDesc.Chromaticities.blue.y << std::endl; + strm << " x_white: " << PDesc.Chromaticities.white.x << " y_white: " << PDesc.Chromaticities.white.y << std::endl; + strm << " Compression: " << (unsigned)PDesc.Compression << std::endl; + strm << " LineOrder: " << (unsigned)PDesc.LineOrder << std::endl; + strm << " DataWindow: " << std::endl; + strm << " xMin: " << PDesc.DataWindow.xMin << std::endl; + strm << " yMin: " << PDesc.DataWindow.yMin << std::endl; + strm << " xMax: " << PDesc.DataWindow.xMax << std::endl; + strm << " yMax: " << PDesc.DataWindow.yMax << std::endl; + strm << " DisplayWindow: " << std::endl; + strm << " xMin: " << PDesc.DisplayWindow.xMin << std::endl; + strm << " yMin: " << PDesc.DisplayWindow.yMin << std::endl; + strm << " xMax: " << PDesc.DisplayWindow.xMax << std::endl; + strm << " yMax: " << PDesc.DisplayWindow.yMax << std::endl; + strm << " PixelAspectRatio: " << PDesc.PixelAspectRatio; + strm << "ScreenWindowCenter: " << "x: " << PDesc.ScreenWindowCenter.x << "y: " << PDesc.ScreenWindowCenter.y << std::endl; + strm << " ScreenWindowWidth: " << PDesc.ScreenWindowWidth; + strm << " Channels: " << std::endl; + + for(ui32_t i = 0; i < PDesc.Channels.size(); i++) + { + if(PDesc.Channels[i].name.empty() == false) + { + strm << " Name: " << PDesc.Channels[i].name << std::endl; + strm << " pixelType: " << PDesc.Channels[i].pixelType << std::endl; + strm << " pLinear: " << PDesc.Channels[i].pLinear << std::endl; + strm << " xSampling: " << PDesc.Channels[i].xSampling << std::endl; + strm << " ySampling: " << PDesc.Channels[i].ySampling << std::endl; + } + } + strm << "Number of other entries: " << PDesc.Other.size(); + + return strm; +} + +void AS_02::ACES::PictureDescriptorDump(const PictureDescriptor &PDesc, FILE *stream /*= NULL*/) +{ + + if(stream == NULL) stream = stderr; + fprintf(stream, " EditRate: %i/%i\n", PDesc.EditRate.Numerator, PDesc.EditRate.Denominator); + fprintf(stream, " SampleRate: %i/%i\n", PDesc.SampleRate.Numerator, PDesc.SampleRate.Denominator); + fprintf(stream, " Chromaticities: \n"); + fprintf(stream, " x_red: %f y_red: %f\n", (double)PDesc.Chromaticities.red.x, (double)PDesc.Chromaticities.red.y); + fprintf(stream, " x_green: %f y_green: %f\n", (double)PDesc.Chromaticities.green.x, (double)PDesc.Chromaticities.green.y); + fprintf(stream, " x_blue: %f y_blue: %f\n", (double)PDesc.Chromaticities.blue.x, (double)PDesc.Chromaticities.blue.y); + fprintf(stream, " x_white: %f y_white: %f\n", (double)PDesc.Chromaticities.white.x, (double)PDesc.Chromaticities.white.y); + fprintf(stream, " Compression: %u\n", (unsigned)PDesc.Compression); + fprintf(stream, " LineOrder: %u\n", (unsigned)PDesc.LineOrder); + fprintf(stream, " DataWindow: \n"); + fprintf(stream, " xMin: %i\n", PDesc.DataWindow.xMin); + fprintf(stream, " yMin: %i\n", PDesc.DataWindow.yMin); + fprintf(stream, " xMax: %i\n", PDesc.DataWindow.xMax); + fprintf(stream, " yMax: %i\n", PDesc.DataWindow.yMax); + fprintf(stream, " DisplayWindow: \n"); + fprintf(stream, " xMin: %i\n", PDesc.DisplayWindow.xMin); + fprintf(stream, " yMin: %i\n", PDesc.DisplayWindow.yMin); + fprintf(stream, " xMax: %i\n", PDesc.DisplayWindow.xMax); + fprintf(stream, " yMax: %i\n", PDesc.DisplayWindow.yMax); + fprintf(stream, " PixelAspectRatio: %f \n", (double)PDesc.PixelAspectRatio); + fprintf(stream, "ScreenWindowCenter: x: %f y: %f\n", (double)PDesc.ScreenWindowCenter.x, (double)PDesc.ScreenWindowCenter.y); + fprintf(stream, " ScreenWindowWidth: %f\n", (double)PDesc.ScreenWindowWidth); + fprintf(stream, " Channels: \n"); + + for(ui32_t i = 0; i < PDesc.Channels.size(); i++) + { + if(PDesc.Channels[i].name.empty() == false) + { + fprintf(stream, " Name: %s\n", PDesc.Channels[i].name.c_str()); + fprintf(stream, " pixelType: %i\n", PDesc.Channels[i].pixelType); + fprintf(stream, " pLinear: %u\n", PDesc.Channels[i].pLinear); + fprintf(stream, " xSampling: %i\n", PDesc.Channels[i].xSampling); + fprintf(stream, " ySampling: %i\n", PDesc.Channels[i].ySampling); + } + } + fprintf(stream, "Number of other entries: %lu\n", PDesc.Other.size()); +} + +AS_02::Result_t AS_02::ACES::ACES_PDesc_to_MD(const PictureDescriptor &PDesc, const ASDCP::Dictionary &dict, ASDCP::MXF::RGBAEssenceDescriptor &EssenceDescriptor) +{ + + EssenceDescriptor.ContainerDuration = PDesc.ContainerDuration; + EssenceDescriptor.SampleRate = PDesc.EditRate; + EssenceDescriptor.FrameLayout = 0x00; + EssenceDescriptor.StoredWidth = PDesc.DataWindow.xMax - PDesc.DataWindow.xMin + 1; + EssenceDescriptor.StoredHeight = PDesc.DataWindow.yMax - PDesc.DataWindow.yMin + 1; + EssenceDescriptor.DisplayWidth = PDesc.DisplayWindow.xMax - PDesc.DisplayWindow.xMin + 1; + EssenceDescriptor.DisplayHeight = PDesc.DisplayWindow.yMax - PDesc.DisplayWindow.yMin + 1; + EssenceDescriptor.DisplayXOffset = PDesc.DisplayWindow.xMin - PDesc.DataWindow.xMin; + EssenceDescriptor.DisplayYOffset = PDesc.DisplayWindow.yMin - PDesc.DataWindow.yMin; + ASDCP::Rational aspect_ratio(EssenceDescriptor.DisplayWidth, EssenceDescriptor.DisplayHeight); + if(aspect_ratio.Denominator != 0) EssenceDescriptor.AspectRatio = AS_02::ConvertToRational(aspect_ratio.Quotient()); + EssenceDescriptor.AlphaTransparency = 0x00; + EssenceDescriptor.ColorPrimaries = dict.ul(MDD_ColorPrimaries_ACES); + EssenceDescriptor.TransferCharacteristic = dict.ul(MDD_TransferCharacteristic_linear); + if(PDesc.Channels.size() == 3 && PDesc.Channels.at(0).name == "B" && PDesc.Channels.at(1).name == "G" && PDesc.Channels.at(2).name == "R") + { + EssenceDescriptor.PictureEssenceCoding = dict.ul(MDD_ACESUncompressedMonoscopicWithoutAlpha); //Picture Essence Coding Label per 2065-5 section 8.2 + EssenceDescriptor.PixelLayout = RGBALayout(ACESPixelLayoutMonoscopicWOAlpha); + } + else if(PDesc.Channels.size() == 4 && PDesc.Channels.at(0).name == "A" && PDesc.Channels.at(1).name == "B" && PDesc.Channels.at(2).name == "G" && PDesc.Channels.at(3).name == "R") + { + EssenceDescriptor.PictureEssenceCoding = dict.ul(MDD_ACESUncompressedMonoscopicWithAlpha); //Picture Essence Coding Label per 2065-5 + EssenceDescriptor.PixelLayout = RGBALayout(ACESPixelLayoutMonoscopicWAlpha); + } + else if(PDesc.Channels.size() == 6 && PDesc.Channels.at(0).name == "B" && PDesc.Channels.at(1).name == "G" && PDesc.Channels.at(2).name == "R" && + PDesc.Channels.at(3).name == "left.B" && PDesc.Channels.at(4).name == "left.G" && PDesc.Channels.at(5).name == "left.R") + { + return RESULT_NOTIMPL; + } + else if(PDesc.Channels.size() == 8 && PDesc.Channels.at(0).name == "A" && PDesc.Channels.at(1).name == "B" && PDesc.Channels.at(2).name == "G" && PDesc.Channels.at(3).name == "R" && + PDesc.Channels.at(4).name == "left.A" && PDesc.Channels.at(5).name == "left.B" && PDesc.Channels.at(6).name == "left.G" && PDesc.Channels.at(7).name == "left.R") + { + return RESULT_NOTIMPL; + } + else + { + return RESULT_NOTIMPL; + } + return RESULT_OK; +} +//static Kumu::UUID CreateTargetFrameAssetId(const std::string& target_frame_file); + +AS_02::Result_t +AS_02::ACES::CreateTargetFrameAssetId(Kumu::UUID& rID, const std::string& target_frame_file) +{ + Kumu::FileReader reader; + Result_t result = Kumu::RESULT_OK; + result = reader.OpenRead(target_frame_file); + if (KM_SUCCESS(result)) + { + byte_t* read_buffer = (byte_t*)malloc(reader.Size()); + if (read_buffer) + { + result = reader.Read(read_buffer, reader.Size()); + rID = AS_02::ACES::create_4122_type5_id(read_buffer, reader.Size(), s_ns_id_target_frame_prefix); + free(read_buffer); + } else result = Kumu::RESULT_FAIL; + } + return result; +} + +static Kumu::UUID +AS_02::ACES::create_4122_type5_id(const byte_t* subject_name, Kumu::fsize_t size, const byte_t* ns_id) +{ + SHA_CTX ctx; + SHA1_Init(&ctx); + SHA1_Update(&ctx, ns_id, NS_ID_LENGTH); + SHA1_Update(&ctx, subject_name, size); + + const ui32_t sha_len = 20; + byte_t bin_buf[sha_len]; + SHA1_Final(bin_buf, &ctx); + + // Derive the asset ID from the digest. Make it a type-5 UUID + byte_t buf[Kumu::UUID_Length]; + memcpy(buf, bin_buf, Kumu::UUID_Length); + buf[6] &= 0x0f; // clear bits 4-7 + buf[6] |= 0x50; // set UUID version 'digest' + buf[8] &= 0x3f; // clear bits 6&7 + buf[8] |= 0x80; // set bit 7 + return Kumu::UUID(buf); +} + +void AS_02::ACES::FrameBuffer::Dump(FILE *stream /*= NULL*/, ui32_t dump_bytes /*= NULL*/) const +{ + + if(stream == 0) stream = stderr; + fprintf(stream, "Frame: %06u, %7u bytes", m_FrameNumber, m_Size); + fputc('\n', stream); + if(dump_bytes > 0) Kumu::hexdump(m_Data, dump_bytes, stream); +} + +class AS_02::ACES::MXFReader::h__Reader : public AS_02::h__AS02Reader +{ + ASDCP_NO_COPY_CONSTRUCT(h__Reader); + ResourceMap_t m_ResourceMap; + ASDCP::MXF::RGBAEssenceDescriptor *m_EssenceDescriptor; + +public: + h__Reader(const Dictionary& d) : + AS_02::h__AS02Reader(d), m_EssenceDescriptor(NULL) {} + + AS_02::ACES::ResourceList_t m_Anc_Resources; + + virtual ~h__Reader() {} + + Result_t OpenRead(const std::string&); + Result_t FillAncillaryResourceDescriptor(AS_02::ACES::ResourceList_t &ancillary_resources); + Result_t ReadFrame(ui32_t, AS_02::ACES::FrameBuffer&, AESDecContext*, HMACContext*); + Result_t ReadAncillaryResource(const Kumu::UUID&, AS_02::ACES::FrameBuffer& FrameBuf, AESDecContext* Ctx, HMACContext* HMAC); +}; + + + +Result_t AS_02::ACES::MXFReader::h__Reader::OpenRead(const std::string& filename) +{ + + Result_t result = OpenMXFRead(filename.c_str()); + + if(KM_SUCCESS(result)) + { + InterchangeObject* tmp_iobj = 0; + + result = m_HeaderPart.GetMDObjectByType(OBJ_TYPE_ARGS(RGBAEssenceDescriptor), &tmp_iobj); + + if(ASDCP_SUCCESS(result)) + { + if(m_EssenceDescriptor == NULL) + { + m_EssenceDescriptor = static_cast<RGBAEssenceDescriptor*>(tmp_iobj); + FillAncillaryResourceDescriptor(m_Anc_Resources); + } + } + else + { + DefaultLogSink().Error("RGBAEssenceDescriptor 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 AS_02::RESULT_AS02_FORMAT; + } + } + return result; +} + +Result_t AS_02::ACES::MXFReader::h__Reader::ReadFrame(ui32_t FrameNum, AS_02::ACES::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_ACESFrameWrappedEssence), Ctx, HMAC); //PB:new UL + +} + +AS_02::Result_t AS_02::ACES::MXFReader::h__Reader::ReadAncillaryResource(const Kumu::UUID &uuid, AS_02::ACES::FrameBuffer& FrameBuf, AESDecContext* Ctx, HMACContext* HMAC) +{ + + + ResourceMap_t::const_iterator ri = m_ResourceMap.find(uuid); + if(ri == m_ResourceMap.end()) + { + char buf[64]; + DefaultLogSink().Error("No such resource: %s\n", uuid.EncodeHex(buf, 64)); + return RESULT_RANGE; + } + TargetFrameSubDescriptor* DescObject = 0; + // get the subdescriptor + InterchangeObject* tmp_iobj = 0; + Result_t result = m_HeaderPart.GetMDObjectByID((*ri).second, &tmp_iobj); + if (KM_SUCCESS(result) && tmp_iobj->IsA(m_Dict->ul(MDD_TargetFrameSubDescriptor))) + { + DescObject = static_cast<TargetFrameSubDescriptor*>(tmp_iobj); + + RIP::const_pair_iterator pi; + RIP::PartitionPair TmpPair; + ui32_t sequence = 0; + + // Look up the partition start in the RIP using the SID. + // Count the sequence length in because this is the sequence + // value needed to complete the HMAC. + for(pi = m_RIP.PairArray.begin(); pi != m_RIP.PairArray.end(); ++pi, ++sequence) + { + if((*pi).BodySID == DescObject->TargetFrameEssenceStreamID) + { + TmpPair = *pi; + break; + } + } + + if(TmpPair.ByteOffset == 0) + { + DefaultLogSink().Error("Body SID not found in RIP set: %d\n", DescObject->TargetFrameEssenceStreamID); + return RESULT_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(ASDCP_SUCCESS(result)) + { + // check the SID + if(DescObject->TargetFrameEssenceStreamID != GSPart.BodySID) + { + char buf[64]; + DefaultLogSink().Error("Generic stream partition body differs: %s\n", uuid.EncodeHex(buf, 64)); + return RESULT_FORMAT; + } + + // read the essence packet + assert(m_Dict); + if(ASDCP_SUCCESS(result)) + result = ReadEKLVPacket(0, sequence, FrameBuf, m_Dict->ul(MDD_GenericStream_DataElement), Ctx, HMAC); + } + } + + return result; +} + +AS_02::Result_t AS_02::ACES::MXFReader::h__Reader::FillAncillaryResourceDescriptor(AS_02::ACES::ResourceList_t &ancillary_resources) +{ + + assert(m_EssenceDescriptor); + ASDCP::MXF::RGBAEssenceDescriptor* TDescObj = (ASDCP::MXF::RGBAEssenceDescriptor*)m_EssenceDescriptor; + + Array<Kumu::UUID>::const_iterator sdi = TDescObj->SubDescriptors.begin(); + TargetFrameSubDescriptor* DescObject = NULL; + Result_t result = RESULT_OK; + + for(; sdi != TDescObj->SubDescriptors.end() && KM_SUCCESS(result); sdi++) + { + InterchangeObject* tmp_iobj = NULL; + result = m_HeaderPart.GetMDObjectByID(*sdi, &tmp_iobj); + if (!tmp_iobj->IsA(m_Dict->ul(MDD_TargetFrameSubDescriptor))) + continue; + DescObject = static_cast<TargetFrameSubDescriptor*>(tmp_iobj); + if(KM_SUCCESS(result) && DescObject) + { + AncillaryResourceDescriptor TmpResource; + memcpy(TmpResource.ResourceID, DescObject->TargetFrameAncillaryResourceID.Value(), UUIDlen); + + if(DescObject->MediaType.find("image/png") != std::string::npos) + { + TmpResource.Type = AS_02::ACES::MT_PNG; + } + else if(DescObject->MediaType.find("image/tiff") != std::string::npos) + { + TmpResource.Type = AS_02::ACES::MT_TIFF; + } + else + { + TmpResource.Type = AS_02::ACES::MT_UNDEF; + } + + ancillary_resources.push_back(TmpResource); + m_ResourceMap.insert(ResourceMap_t::value_type(DescObject->TargetFrameAncillaryResourceID, *sdi)); + } + else + { + DefaultLogSink().Error("Broken sub-descriptor link\n"); + return RESULT_FORMAT; + } + } + + return result; +} + +void +AS_02::ACES::MXFReader::DumpHeaderMetadata(FILE* stream) const +{ + if ( m_Reader && m_Reader->m_File.IsOpen() ) + { + m_Reader->m_HeaderPart.Dump(stream); + } +} + + +// +void +AS_02::ACES::MXFReader::DumpIndex(FILE* stream) const +{ + if ( m_Reader && m_Reader->m_File.IsOpen() ) + { + m_Reader->m_IndexAccess.Dump(stream); + } +} + + + +class AS_02::ACES::MXFWriter::h__Writer : public AS_02::h__AS02WriterFrame{ + + ASDCP_NO_COPY_CONSTRUCT(h__Writer); + h__Writer(); + +public: + byte_t m_EssenceUL[SMPTE_UL_LENGTH]; + ui32_t m_EssenceStreamID; + + h__Writer(const Dictionary& d) : h__AS02WriterFrame(d), m_EssenceStreamID(10) + { + + memset(m_EssenceUL, 0, SMPTE_UL_LENGTH); + } + + virtual ~h__Writer() {} + + Result_t 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); + Result_t SetSourceStream(const std::string &label, const ASDCP::Rational &edit_rate); + Result_t WriteFrame(const AS_02::ACES::FrameBuffer &FrameBuf, ASDCP::AESEncContext *Ctx, ASDCP::HMACContext *HMAC); + Result_t WriteAncillaryResource(const AS_02::ACES::FrameBuffer&, AESEncContext* = 0, HMACContext* = 0); + Result_t Finalize(); +}; + +// Open the file for writing. The file must not exist. Returns error if +// the operation cannot be completed. +AS_02::Result_t AS_02::ACES::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))) + { + DefaultLogSink().Error("Essence descriptor is not a ACES Picture Essence Descriptor.\n"); + essence_descriptor->Dump(); + return AS_02::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_ACESPictureSubDescriptor)) ) && ( (*i)->GetUL() != UL(m_Dict->ul(MDD_TargetFrameSubDescriptor)) ) ) + { + DefaultLogSink().Error("Essence sub-descriptor is not an ACESPictureSubDescriptor or a TargetFrameSubDescriptor.\n"); + (*i)->Dump(); + } + + m_EssenceSubDescriptorList.push_back(*i); + if (!(*i)->InstanceUID.HasValue()) 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 aces codestream stream. +AS_02::Result_t AS_02::ACES::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; + } + + Result_t result = RESULT_OK; + ui32_t EssenceStreamID_backup = m_EssenceStreamID; + + if(KM_SUCCESS(result)) + { + memcpy(m_EssenceUL, m_Dict->ul(MDD_ACESFrameWrappedEssence), 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)) + { + result = WriteAS02Header(label, UL(m_Dict->ul(MDD_MXFGCFrameWrappedACESPictures)), //Essence Container Label per 2065-5 section 8.1 (frame wrapping) + PICT_DEF_LABEL, UL(m_EssenceUL), UL(m_Dict->ul(MDD_PictureDataDef)), + edit_rate, derive_timecode_rate_from_edit_rate(edit_rate)); + + if(KM_SUCCESS(result)) + { + this->m_IndexWriter.SetPrimerLookup(&this->m_HeaderPart.m_Primer); + } + } + + m_EssenceStreamID = EssenceStreamID_backup; + 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. +AS_02::Result_t AS_02::ACES::MXFWriter::h__Writer::WriteFrame(const AS_02::ACES::FrameBuffer &FrameBuf, ASDCP::AESEncContext *Ctx, ASDCP::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); + m_FramesWritten++; + } + + return result; +} + +// Closes the MXF file, writing the index and other closing information. +AS_02::Result_t AS_02::ACES::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::Result_t AS_02::ACES::MXFWriter::h__Writer::WriteAncillaryResource(const AS_02::ACES::FrameBuffer &FrameBuf, AESEncContext *Ctx , HMACContext *HMAC ) +{ + + if(!m_State.Test_RUNNING()) + { + KM_RESULT_STATE_HERE(); + return RESULT_STATE; + } + + Kumu::fpos_t here = m_File.Tell(); + assert(m_Dict); + + // create generic stream partition header + static UL GenericStream_DataElement(m_Dict->ul(MDD_GenericStream_DataElement)); + ASDCP::MXF::Partition GSPart(m_Dict); + + GSPart.MajorVersion = m_HeaderPart.MajorVersion; + GSPart.MinorVersion = m_HeaderPart.MinorVersion; + GSPart.ThisPartition = here; + GSPart.PreviousPartition = m_RIP.PairArray.back().ByteOffset; + GSPart.BodySID = m_EssenceStreamID; + GSPart.OperationalPattern = m_HeaderPart.OperationalPattern; + + m_RIP.PairArray.push_back(RIP::PartitionPair(m_EssenceStreamID++, here)); + GSPart.EssenceContainers = m_HeaderPart.EssenceContainers; + //GSPart.EssenceContainers.push_back(UL(m_Dict->ul(MDD_ACESFrameWrappedEssence))); //MDD_ACESEssence + UL TmpUL(m_Dict->ul(MDD_GenericStreamPartition)); + Result_t result = GSPart.WriteToFile(m_File, TmpUL); + + if(KM_SUCCESS(result)) + { + ui64_t this_stream_offset = m_StreamOffset; // m_StreamOffset will be changed by the call to Write_EKLV_Packet + + result = Write_EKLV_Packet(m_File, *m_Dict, m_HeaderPart, m_Info, m_CtFrameBuf, m_FramesWritten, m_StreamOffset, FrameBuf, GenericStream_DataElement.Value(), Ctx, HMAC); + } + return result; +} + +AS_02::ACES::MXFWriter::MXFWriter() +{ + +} + +AS_02::ACES::MXFWriter::~MXFWriter() +{ + +} + +ASDCP::MXF::OP1aHeader& AS_02::ACES::MXFWriter::OP1aHeader() +{ + + if(m_Writer.empty()) + { + assert(g_OP1aHeader); + return *g_OP1aHeader; + } + return m_Writer->m_HeaderPart; +} + +ASDCP::MXF::RIP& AS_02::ACES::MXFWriter::RIP() +{ + + if(m_Writer.empty()) + { + assert(g_RIP); + return *g_RIP; + } + return m_Writer->m_RIP; +} + +AS_02::Result_t AS_02::ACES::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 AS_02::ACES::ResourceList_t &ancillary_resources /*= ResourceList_t()*/, const ui32_t &header_size /*= 16384*/, const AS_02::IndexStrategy_t &strategy /*= IS_FOLLOW*/, const ui32_t &partition_space /*= 10*/) +{ + + if(essence_descriptor == NULL) + { + DefaultLogSink().Error("Essence descriptor object required.\n"); + return RESULT_PARAM; + } + + m_Writer = new AS_02::ACES::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(ACES_PACKAGE_LABEL, edit_rate); + if(KM_FAILURE(result)) m_Writer.release(); + return result; +} + +AS_02::Result_t AS_02::ACES::MXFWriter::WriteFrame(const FrameBuffer &FrameBuf, ASDCP::AESEncContext *Ctx /*= NULL*/, ASDCP::HMACContext *HMAC /*= NULL*/) +{ + + if(m_Writer.empty()) return RESULT_INIT; + return m_Writer->WriteFrame(FrameBuf, Ctx, HMAC); +} + +AS_02::Result_t AS_02::ACES::MXFWriter::Finalize() +{ + + if(m_Writer.empty()) return RESULT_INIT; + return m_Writer->Finalize(); +} + + +AS_02::Result_t AS_02::ACES::MXFWriter::WriteAncillaryResource(const AS_02::ACES::FrameBuffer &rBuf, ASDCP::AESEncContext *Ctx , ASDCP::HMACContext *HMAC ) +{ + + if(m_Writer.empty()) + return RESULT_INIT; + + return m_Writer->WriteAncillaryResource(rBuf, Ctx, HMAC); +} + + +AS_02::ACES::MXFReader::MXFReader() +{ + + m_Reader = new h__Reader(DefaultCompositeDict()); +} + +AS_02::ACES::MXFReader::~MXFReader() +{ + +} + +// Warning: direct manipulation of MXF structures can interfere +// with the normal operation of the wrapper. Caveat emptor! +ASDCP::MXF::OP1aHeader& AS_02::ACES::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::ACES::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::ACES::MXFReader::RIP() +{ + + if(m_Reader.empty()) + { + assert(g_RIP); + return *g_RIP; + } + return m_Reader->m_RIP; +} + +AS_02::Result_t AS_02::ACES::MXFReader::OpenRead(const std::string& filename) const +{ + + return m_Reader->OpenRead(filename); +} + +AS_02::Result_t AS_02::ACES::MXFReader::Close() const +{ + + if(m_Reader && m_Reader->m_File.IsOpen()) + { + m_Reader->Close(); + return RESULT_OK; + } + return RESULT_INIT; +} + +AS_02::Result_t AS_02::ACES::MXFReader::FillWriterInfo(ASDCP::WriterInfo& Info) const +{ + + if(m_Reader && m_Reader->m_File.IsOpen()) + { + Info = m_Reader->m_Info; + return RESULT_OK; + } + return RESULT_INIT; +} + +AS_02::Result_t AS_02::ACES::MXFReader::ReadFrame(ui32_t FrameNum, AS_02::ACES::FrameBuffer &FrameBuf, ASDCP::AESDecContext *Ctx /*= 0*/, ASDCP::HMACContext *HMAC /*= 0*/) const +{ + + if(m_Reader && m_Reader->m_File.IsOpen()) + return m_Reader->ReadFrame(FrameNum, FrameBuf, Ctx, HMAC); + + return RESULT_INIT; +} + +AS_02::Result_t AS_02::ACES::MXFReader::ReadAncillaryResource(const Kumu::UUID &uuid, AS_02::ACES::FrameBuffer &FrameBuf, ASDCP::AESDecContext *Ctx , ASDCP::HMACContext *HMAC ) const +{ + + if(m_Reader && m_Reader->m_File.IsOpen()) + return m_Reader->ReadAncillaryResource(uuid, FrameBuf, Ctx, HMAC); + + return RESULT_INIT; +} + +AS_02::Result_t AS_02::ACES::MXFReader::FillAncillaryResourceList(AS_02::ACES::ResourceList_t &ancillary_resources) const +{ + + if(m_Reader && m_Reader->m_File.IsOpen()) + { + ancillary_resources = m_Reader->m_Anc_Resources; + return RESULT_OK; + } + return RESULT_INIT; +} + +bool AS_02::ACES::channel::operator==(const channel &Other) const +{ + + if(name != Other.name) return false; + if(pixelType != Other.pixelType) return false; + if(pLinear != Other.pLinear) return false; + if(xSampling != Other.xSampling) return false; + if(ySampling != Other.ySampling) return false; + return true; +} + +bool AS_02::ACES::box2i::operator==(const box2i &Other) const +{ + + if(xMin != Other.xMin) return false; + if(yMin != Other.yMin) return false; + if(xMax != Other.xMax) return false; + if(yMax != Other.yMax) return false; + return true; +} + +bool AS_02::ACES::keycode::operator==(const keycode &Other) const +{ + + if(filmMfcCode != Other.filmMfcCode) return false; + if(filmType != Other.filmType) return false; + if(prefix != Other.prefix) return false; + if(count != Other.count) return false; + if(perfOffset != Other.perfOffset) return false; + if(perfsPerFrame != Other.perfsPerFrame) return false; + if(perfsPerCount != Other.perfsPerCount) return false; + return true; +} + +bool AS_02::ACES::v2f::operator==(const v2f &Other) const +{ + + if(x != Other.x) return false; + if(y != Other.y) return false; + return true; +} + +bool AS_02::ACES::v3f::operator==(const v3f &Other) const +{ + + if(x != Other.x) return false; + if(y != Other.y) return false; + if(z != Other.z) return false; + return true; +} + +bool AS_02::ACES::chromaticities::operator==(const chromaticities &Other) const +{ + + if(red != Other.red) return false; + if(green != Other.green) return false; + if(blue != Other.blue) return false; + if(white != Other.white) return false; + return true; +} + +bool AS_02::ACES::timecode::operator==(const timecode &Other) const +{ + + if(timeAndFlags != Other.timeAndFlags) return false; + if(userData != Other.userData) return false; + return true; +} + +bool AS_02::ACES::PictureDescriptor::operator==(const PictureDescriptor &Other) const +{ + + if(EditRate != Other.EditRate) return false; + if(SampleRate != Other.SampleRate) return false; + if(AcesImageContainerFlag != Other.AcesImageContainerFlag) return false; + if(Chromaticities != Other.Chromaticities) return false; + if(Compression != Other.Compression) return false; + if(LineOrder != Other.LineOrder) return false; + if(DataWindow != Other.DataWindow) return false; + if(DisplayWindow != Other.DisplayWindow) return false; + if(PixelAspectRatio != Other.PixelAspectRatio) return false; + if(ScreenWindowCenter != Other.ScreenWindowCenter) return false; + if(ScreenWindowWidth != Other.ScreenWindowWidth) return false; + if(Channels.size() != Other.Channels.size()) return false; + for(int i = 0; i < Channels.size(); i++) + { + if(Channels.at(i) != Other.Channels.at(i)) return false; + } + return true; +} |
