/*
-Copyright (c) 2004-2006, John Hurst
+Copyright (c) 2004-2018, John Hurst
All rights reserved.
Redistribution and use in source and binary forms, with or without
*/
#include "AS_DCP_internal.h"
-#include "MemIO.h"
-#include "Timecode.h"
#include "KLV.h"
-#include "MDD.h"
-#include <assert.h>
using namespace ASDCP;
using namespace ASDCP::MXF;
+//
+ui32_t
+ASDCP::derive_timecode_rate_from_edit_rate(const ASDCP::Rational& edit_rate)
+{
+ return (ui32_t)floor(0.5 + edit_rate.Quotient());
+}
-ASDCP::h__Writer::h__Writer() : m_FramesWritten(0), m_StreamOffset(0)
+//
+// add DMS CryptographicFramework entry to source package
+void
+ASDCP::AddDmsCrypt(Partition& HeaderPart, SourcePackage& Package,
+ WriterInfo& Descr, const UL& WrappingUL, const Dictionary*& Dict)
{
+ assert(Dict);
+ // Essence Track
+ StaticTrack* NewTrack = new StaticTrack(Dict);
+ HeaderPart.AddChildObject(NewTrack);
+ Package.Tracks.push_back(NewTrack->InstanceUID);
+ NewTrack->TrackName = "Descriptive Track";
+ NewTrack->TrackID = 3;
+
+ Sequence* Seq = new Sequence(Dict);
+ HeaderPart.AddChildObject(Seq);
+ NewTrack->Sequence = Seq->InstanceUID;
+ Seq->DataDefinition = UL(Dict->ul(MDD_DescriptiveMetaDataDef));
+
+ DMSegment* Segment = new DMSegment(Dict);
+ HeaderPart.AddChildObject(Segment);
+ Seq->StructuralComponents.push_back(Segment->InstanceUID);
+ Segment->EventComment = "AS-DCP KLV Encryption";
+ Segment->DataDefinition = UL(Dict->ul(MDD_DescriptiveMetaDataDef));
+
+ CryptographicFramework* CFW = new CryptographicFramework(Dict);
+ HeaderPart.AddChildObject(CFW);
+ Segment->DMFramework = CFW->InstanceUID;
+
+ CryptographicContext* Context = new CryptographicContext(Dict);
+ HeaderPart.AddChildObject(Context);
+ CFW->ContextSR = Context->InstanceUID;
+
+ Context->ContextID.Set(Descr.ContextID);
+ Context->SourceEssenceContainer = WrappingUL; // ??????
+ Context->CipherAlgorithm.Set(Dict->ul(MDD_CipherAlgorithm_AES));
+ Context->MICAlgorithm.Set( Descr.UsesHMAC ? Dict->ul(MDD_MICAlgorithm_HMAC_SHA1) : Dict->ul(MDD_MICAlgorithm_NONE) );
+ Context->CryptographicKeyID.Set(Descr.CryptographicKeyID);
}
-ASDCP::h__Writer::~h__Writer()
+static std::string const rp2057_static_track_label = "SMPTE RP 2057 Generic Stream Text-Based Set";
+
+//
+static bool
+id_batch_contains(const Array<Kumu::UUID>& batch, const Kumu::UUID& value)
{
+ Array<Kumu::UUID>::const_iterator i;
+ for ( i = batch.begin(); i != batch.end(); ++i )
+ {
+ if ( *i == value )
+ {
+ return true;
+ }
+ }
+ return false;
}
-// standard method of writing the header of a new MXF file
+//
Result_t
-ASDCP::h__Writer::WriteMXFHeader(EssenceType_t EssenceType, ASDCP::Rational& EditRate,
- ui32_t TCFrameRate, ui32_t BytesPerEditUnit)
+ASDCP::AddDmsTrackGenericPartUtf8Text(Kumu::FileWriter& file_writer, MXF::OP1aHeader& header_part,
+ SourcePackage& source_package, MXF::RIP& rip, const Dictionary*& Dict)
{
-#if 0
- // write the stream metadata
- m_Metadata = new Metadata();
- assert(m_Metadata);
- assert(m_Metadata->m_Object);
+ Sequence* Sequence_obj = 0;
+ InterchangeObject* tmp_iobj = 0;
+ std::list<InterchangeObject*> object_list;
+
+ // get the SourcePackage else die
+ header_part.GetMDObjectByType(Dict->ul(MDD_SourcePackage), &tmp_iobj);
+ SourcePackage *SourcePackage_obj = dynamic_cast<SourcePackage*>(tmp_iobj);
+ if ( SourcePackage_obj == 0 )
+ {
+ DefaultLogSink().Error("MXF Metadata contains no SourcePackage Set.\n");
+ return RESULT_FORMAT;
+ }
- if ( m_Info.EncryptedEssence )
+ // find the first StaticTrack object, having the right label, that is ref'd by the source package
+ StaticTrack *StaticTrack_obj = 0;
+ header_part.GetMDObjectsByType(Dict->ul(MDD_StaticTrack), object_list);
+ std::list<InterchangeObject*>::iterator j;
+ for ( j = object_list.begin(); j != object_list.end(); ++j )
{
- UL DMSUL(CryptoFrameworkUL_Data);
- m_Metadata->AddDMScheme(DMSUL);
+ StaticTrack_obj = dynamic_cast<StaticTrack*>(*j);
+ assert(StaticTrack_obj);
+ if ( id_batch_contains(SourcePackage_obj->Tracks, StaticTrack_obj->InstanceUID)
+ && StaticTrack_obj->TrackName.get() == rp2057_static_track_label )
+ {
+ break;
+ }
+ StaticTrack_obj = 0;
}
- // Set the OP label
- // If we are writing OP-Atom we write the header as OP1a initially as another process
- // may try to read the file before it is complete and then it will NOT be a valid OP-Atom file
- m_Metadata->SetOP(OP1aUL);
+ // find the Sequence associated with this Track
+ if ( StaticTrack_obj )
+ {
+ object_list.clear();
+ header_part.GetMDObjectsByType(Dict->ul(MDD_Sequence), object_list);
+ for ( j = object_list.begin(); j != object_list.end(); ++j )
+ {
+ Sequence_obj = dynamic_cast<Sequence*>(*j);
+ assert(Sequence_obj);
+ if ( Sequence_obj->InstanceUID == StaticTrack_obj->Sequence )
+ {
+ break;
+ }
+ Sequence_obj = 0;
+ }
+ }
- // Build the Material Package
- // DRAGONS: We should really try and determine the UMID type rather than cop-out!
- UMID PackageUMID;
- PackageUMID.MakeUMID(0x0d); // mixed type
+ if ( Sequence_obj == 0 )
+ {
+ // this is the first insertion, create the static track
+ assert(Dict);
+ StaticTrack* static_track = new StaticTrack(Dict);
+ header_part.AddChildObject(static_track);
+ source_package.Tracks.push_back(static_track->InstanceUID);
+ static_track->TrackName = "Descriptive Track";
+ static_track->TrackID = 4;
+
+ Sequence_obj = new Sequence(Dict);
+ header_part.AddChildObject(Sequence_obj);
+ static_track->Sequence = Sequence_obj->InstanceUID;
+ Sequence_obj->DataDefinition = UL(Dict->ul(MDD_DescriptiveMetaDataDef));
+ header_part.m_Preface->DMSchemes.push_back(UL(Dict->ul(MDD_MXFTextBasedFramework)));
+ }
- m_MaterialPackage = m_Metadata->AddMaterialPackage("AS-DCP Material Package", PackageUMID);
- m_Metadata->SetPrimaryPackage(m_MaterialPackage); // This will be overwritten for OP-Atom
-
- TrackPtr MPTimecodeTrack = m_MaterialPackage->AddTimecodeTrack(EditRate);
- m_MPTimecode = MPTimecodeTrack->AddTimecodeComponent(TCFrameRate, 0, 0);
+ assert(Sequence_obj);
+ // Create the DM segment and framework packs
+ DMSegment* Segment = new DMSegment(Dict);
+ header_part.AddChildObject(Segment);
+ Sequence_obj->StructuralComponents.push_back(Segment->InstanceUID);
+ Segment->EventComment = rp2057_static_track_label;
+ Segment->DataDefinition = UL(Dict->ul(MDD_DescriptiveMetaDataDef));
- TrackPtr FPTimecodeTrack = 0;
- mxflib::UUID assetUUID(m_Info.AssetUUID);
+ //
+ TextBasedDMFramework *dmf_obj = new TextBasedDMFramework(Dict);
+ assert(dmf_obj);
+ header_part.AddChildObject(dmf_obj);
+ Segment->DMFramework = dmf_obj->InstanceUID;
+ GenRandomValue(dmf_obj->ObjectRef);
+
+ // Create a new SID on the RIP, located at the current file position
+ ui32_t max_sid = 0;
+ ASDCP::MXF::RIP::pair_iterator i;
+ for ( i = rip.PairArray.begin(); i != rip.PairArray.end(); ++i )
+ {
+ if ( max_sid < i->BodySID )
+ {
+ max_sid = i->BodySID;
+ }
+ }
- UMID EssenceUMID;
-
- switch ( EssenceType )
+ if ( max_sid == 0 )
{
- case ESS_MPEG2_VES:
- PackageUMID.MakeUMID(0x0f, assetUUID);
- m_FilePackage = m_Metadata->AddFilePackage(1, MPEG_PACKAGE_LABEL, PackageUMID);
- m_MPTrack = m_MaterialPackage->AddPictureTrack(EditRate);
- m_FPTrack = m_FilePackage->AddPictureTrack(0, EditRate);
- break;
-
- case ESS_JPEG_2000:
- PackageUMID.MakeUMID(0x0f, assetUUID);
- m_FilePackage = m_Metadata->AddFilePackage(1, JP2K_PACKAGE_LABEL, PackageUMID);
- m_MPTrack = m_MaterialPackage->AddPictureTrack(EditRate);
- m_FPTrack = m_FilePackage->AddPictureTrack(0, EditRate);
- break;
-
- case ESS_PCM_24b_48k:
- PackageUMID.MakeUMID(0x0f, assetUUID);
- m_FilePackage = m_Metadata->AddFilePackage(1, PCM_PACKAGE_LABEL, PackageUMID);
- m_MPTrack = m_MaterialPackage->AddSoundTrack(EditRate);
- m_FPTrack = m_FilePackage->AddSoundTrack(0, EditRate);
- break;
-
- default: return RESULT_RAW_ESS;
+ DefaultLogSink().Error("Unable to add a GS Partition before the essence container has been established.\n");
+ return RESULT_FORMAT;
}
- // Add an essence element
- FPTimecodeTrack = m_FilePackage->AddTimecodeTrack(EditRate);
- m_FPTimecode = FPTimecodeTrack->AddTimecodeComponent(TCFrameRate, 0/* NDF */,
- tc_to_frames(TCFrameRate, 1, 0, 0, 0) );
-
- // Add a single Component to this Track of the Material Package
- m_MPClip = m_MPTrack->AddSourceClip();
+ rip.PairArray.push_back(RIP::PartitionPair(max_sid + 1, file_writer.Tell()));
+
+ // Add new GSTBS linked to DMF
+ GenericStreamTextBasedSet *gst_obj = new GenericStreamTextBasedSet(Dict);
+ header_part.AddChildObject(gst_obj);
+ gst_obj->InstanceUID = dmf_obj->ObjectRef;
+ gst_obj->GenericStreamSID = max_sid + 1;
+ gst_obj->PayloadSchemeID = UL(Dict->ul(MDD_MXFTextBasedFramework));
- // Add a single Component to this Track of the File Package
- m_FPClip = m_FPTrack->AddSourceClip();
- const byte_t* SourceEssenceContainerLabel = 0;
+ return RESULT_OK;
+}
+
+//
+ASDCP::h__ASDCPWriter::h__ASDCPWriter(const Dictionary& d) :
+ MXF::TrackFileWriter<OP1aHeader>(d), m_BodyPart(m_Dict), m_FooterPart(m_Dict) {}
- // Frame wrapping
- if ( m_Info.EncryptedEssence )
+ASDCP::h__ASDCPWriter::~h__ASDCPWriter() {}
+
+
+//
+Result_t
+ASDCP::h__ASDCPWriter::CreateBodyPart(const MXF::Rational& EditRate, ui32_t BytesPerEditUnit)
+{
+ assert(m_Dict);
+ Result_t result = RESULT_OK;
+
+ // create a body partition if we're writing proper 429-3/OP-Atom
+ if ( m_Info.LabelSetType == LS_MXF_SMPTE )
{
- switch ( EssenceType )
- {
- case ESS_MPEG2_VES:
- SourceEssenceContainerLabel = WrappingUL_Data_MPEG2_VES;
- break;
-
- case ESS_JPEG_2000:
- SourceEssenceContainerLabel = WrappingUL_Data_JPEG_2000;
- break;
-
- case ESS_PCM_24b_48k:
- SourceEssenceContainerLabel = WrappingUL_Data_PCM_24b_48k;
- break;
-
- default:
- return RESULT_RAW_ESS;
- }
+ // Body Partition
+ m_BodyPart.EssenceContainers = m_HeaderPart.EssenceContainers;
+ m_BodyPart.ThisPartition = m_File.Tell();
+ m_BodyPart.BodySID = 1;
+ UL OPAtomUL(m_Dict->ul(MDD_OPAtom));
+ m_BodyPart.OperationalPattern = OPAtomUL;
+ m_RIP.PairArray.push_back(RIP::PartitionPair(1, m_BodyPart.ThisPartition)); // Second RIP Entry
+
+ UL BodyUL(m_Dict->ul(MDD_ClosedCompleteBodyPartition));
+ result = m_BodyPart.WriteToFile(m_File, BodyUL);
+ }
+ else
+ {
+ m_HeaderPart.BodySID = 1;
}
- mem_ptr<UL> WrappingUL;
- switch ( EssenceType )
+ if ( ASDCP_SUCCESS(result) )
{
- case ESS_MPEG2_VES:
- WrappingUL = new UL(WrappingUL_Data_MPEG2_VES); // memchk TESTED
- break;
-
- case ESS_JPEG_2000:
- WrappingUL = new UL(WrappingUL_Data_JPEG_2000); // memchk TESTED
- break;
-
- case ESS_PCM_24b_48k:
- WrappingUL = new UL(WrappingUL_Data_PCM_24b_48k); // memchk TESTED
- break;
-
- default:
- return RESULT_RAW_ESS;
+ // Index setup
+ Kumu::fpos_t ECoffset = m_File.Tell();
+ m_FooterPart.IndexSID = 129;
+
+ if ( BytesPerEditUnit == 0 )
+ {
+ m_FooterPart.SetIndexParamsVBR(&m_HeaderPart.m_Primer, EditRate, ECoffset);
+ }
+ else
+ {
+ m_FooterPart.SetIndexParamsCBR(&m_HeaderPart.m_Primer, BytesPerEditUnit, EditRate);
+ }
}
- assert(!WrappingUL.empty());
- m_EssenceDescriptor->SetValue("EssenceContainer", DataChunk(klv_key_size, WrappingUL->GetValue()));
-
- // Write a File Descriptor only on the internally ref'ed Track
- m_EssenceDescriptor->SetUint("LinkedTrackID", m_FPTrack->GetUint("TrackID"));
- m_FilePackage->AddChild("Descriptor")->MakeLink(*m_EssenceDescriptor);
- UL CryptEssenceUL(WrappingUL_Data_Crypt);
+ return result;
+}
- if ( m_Info.EncryptedEssence )
+//
+Result_t
+ASDCP::h__ASDCPWriter::WriteASDCPHeader(const std::string& PackageLabel, const UL& WrappingUL,
+ const std::string& TrackName, const UL& EssenceUL, const UL& DataDefinition,
+ const MXF::Rational& EditRate, ui32_t TCFrameRate, ui32_t BytesPerEditUnit)
+{
+ InitHeader(MXFVersion_2004);
+
+ // First RIP Entry
+ if ( m_Info.LabelSetType == LS_MXF_SMPTE ) // ERK
{
- m_Metadata->AddEssenceType(CryptEssenceUL);
+ m_RIP.PairArray.push_back(RIP::PartitionPair(0, 0)); // 3-part, no essence in header
}
else
{
- UL GCUL(GCMulti_Data);
- m_Metadata->AddEssenceType(GCUL);
- m_Metadata->AddEssenceType(*WrappingUL);
+ m_RIP.PairArray.push_back(RIP::PartitionPair(1, 0)); // 2-part, essence in header
}
- // Link the MP to the FP
- m_MPClip->MakeLink(m_FPTrack, 0);
+ // timecode rate and essence rate are the same
+ AddSourceClip(EditRate, EditRate, TCFrameRate, TrackName, EssenceUL, DataDefinition, PackageLabel);
+ AddEssenceDescriptor(WrappingUL);
- //
- // ** Write out the header **
- //
+#ifdef ASDCP_GCMULTI_PATCH
+ UL GenericContainerUL(m_Dict->ul(MDD_GCMulti));
+ m_HeaderPart.EssenceContainers.push_back(GenericContainerUL);
+#endif
- m_HeaderPart = new Partition("OpenHeader");
- assert(m_HeaderPart);
- m_HeaderPart->SetKAG(1); // Everything else can stay at default
- m_HeaderPart->SetUint("BodySID", 1);
-
- m_HeaderPart->AddMetadata(m_Metadata);
+ Result_t result = m_HeaderPart.WriteToFile(m_File, m_HeaderSize);
- // Build an Ident set describing us and link into the metadata
- MDObject* Ident = new MDObject("Identification");
- assert(Ident);
- Ident->SetString("CompanyName", m_Info.CompanyName);
- Ident->SetString("ProductName", m_Info.ProductName);
- Ident->SetString("VersionString", m_Info.ProductVersion);
- UUID ProductUID(m_Info.ProductUUID);
- Ident->SetValue("ProductUID", DataChunk(UUIDlen, ProductUID.GetValue()));
+ if ( KM_SUCCESS(result) )
+ result = CreateBodyPart(EditRate, BytesPerEditUnit);
- // TODO: get Oliver to show me how this works
- // Ident->SetString("ToolkitVersion", ?);
+ return result;
+}
- m_Metadata->UpdateGenerations(*Ident);
+//
+Result_t
+ASDCP::h__ASDCPWriter::WriteEKLVPacket(const ASDCP::FrameBuffer& FrameBuf,const byte_t* EssenceUL,
+ const ui32_t& MinEssenceElementBerLength,
+ AESEncContext* Ctx, HMACContext* HMAC)
+{
+ return Write_EKLV_Packet(m_File, *m_Dict, m_HeaderPart, m_Info, m_CtFrameBuf, m_FramesWritten,
+ m_StreamOffset, FrameBuf, EssenceUL, MinEssenceElementBerLength,
+ Ctx, HMAC);
+}
- if ( m_Info.EncryptedEssence )
- AddDMScrypt(m_FilePackage, m_Info, SourceEssenceContainerLabel);
+// standard method of writing the header and footer of a completed MXF file
+//
+Result_t
+ASDCP::h__ASDCPWriter::WriteASDCPFooter()
+{
+ // update all Duration properties
+ DurationElementList_t::iterator dli = m_DurationUpdateList.begin();
- // Write the header partition
- m_File->WritePartition(*m_HeaderPart, HeaderPadding);
-
- // set up the index
- switch ( EssenceType )
+ for (; dli != m_DurationUpdateList.end(); ++dli )
{
- case ESS_MPEG2_VES:
- case ESS_JPEG_2000:
- m_IndexMan = new IndexManager(0, 0);
- m_IndexMan->SetPosTableIndex(0, -1);
- break;
-
- case ESS_PCM_24b_48k:
- m_IndexMan = new IndexManager(0, BytesPerEditUnit);
- break;
-
- case ESS_UNKNOWN:
- return RESULT_INIT;
+ **dli = m_FramesWritten;
}
- m_IndexMan->SetBodySID(1);
- m_IndexMan->SetIndexSID(129);
- m_IndexMan->SetEditRate(EditRate);
-#endif
+ m_EssenceDescriptor->ContainerDuration = m_FramesWritten;
+ m_FooterPart.PreviousPartition = m_RIP.PairArray.back().ByteOffset;
- return RESULT_OK;
+ Kumu::fpos_t here = m_File.Tell();
+ m_RIP.PairArray.push_back(RIP::PartitionPair(0, here)); // Last RIP Entry
+ m_HeaderPart.FooterPartition = here;
+
+ assert(m_Dict);
+ // re-label the header partition, set the footer
+ UL OPAtomUL(m_Dict->ul(MDD_OPAtom));
+ m_HeaderPart.OperationalPattern = OPAtomUL;
+ m_HeaderPart.m_Preface->OperationalPattern = OPAtomUL;
+ m_FooterPart.OperationalPattern = OPAtomUL;
+
+ m_FooterPart.EssenceContainers = m_HeaderPart.EssenceContainers;
+ m_FooterPart.FooterPartition = here;
+ m_FooterPart.ThisPartition = here;
+
+ Result_t result = m_FooterPart.WriteToFile(m_File, m_FramesWritten);
+
+ if ( ASDCP_SUCCESS(result) )
+ result = m_RIP.WriteToFile(m_File);
+
+ if ( ASDCP_SUCCESS(result) )
+ result = m_File.Seek(0);
+
+ if ( ASDCP_SUCCESS(result) )
+ result = m_HeaderPart.WriteToFile(m_File, m_HeaderSize);
+
+ m_File.Close();
+ return result;
}
+
+//------------------------------------------------------------------------------------------
+//
+
+
// standard method of writing a plaintext or encrypted frame
Result_t
-ASDCP::h__Writer::WriteEKLVPacket(const ASDCP::FrameBuffer& FrameBuf, const byte_t* EssenceUL,
- AESEncContext* Ctx, HMACContext* HMAC)
+ASDCP::Write_EKLV_Packet(Kumu::FileWriter& File, const ASDCP::Dictionary& Dict, const MXF::OP1aHeader& HeaderPart,
+ const ASDCP::WriterInfo& Info, ASDCP::FrameBuffer& CtFrameBuf, ui32_t& FramesWritten,
+ ui64_t & StreamOffset, const ASDCP::FrameBuffer& FrameBuf, const byte_t* EssenceUL,
+ const ui32_t& MinEssenceElementBerLength,
+ AESEncContext* Ctx, HMACContext* HMAC)
{
- Result_t result;
+ Result_t result = RESULT_OK;
IntegrityPack IntPack;
byte_t overhead[128];
- MemIOWriter Overhead(overhead, 128);
+ Kumu::MemIOWriter Overhead(overhead, 128);
if ( FrameBuf.Size() == 0 )
{
return RESULT_EMPTY_FB;
}
- if ( m_Info.EncryptedEssence )
+ if ( Info.EncryptedEssence )
{
if ( ! Ctx )
return RESULT_CRYPT_CTX;
- if ( m_Info.UsesHMAC && ! HMAC )
+ if ( Info.UsesHMAC && ! HMAC )
return RESULT_HMAC_CTX;
if ( FrameBuf.PlaintextOffset() > FrameBuf.Size() )
return RESULT_LARGE_PTO;
// encrypt the essence data (create encrypted source value)
- result = EncryptFrameBuffer(FrameBuf, m_CtFrameBuf, Ctx);
+ result = EncryptFrameBuffer(FrameBuf, CtFrameBuf, Ctx);
// create HMAC
- if ( ASDCP_SUCCESS(result) && m_Info.UsesHMAC )
- result = IntPack.CalcValues(m_CtFrameBuf, m_Info.AssetUUID, m_FramesWritten + 1, HMAC);
+ if ( ASDCP_SUCCESS(result) && Info.UsesHMAC )
+ result = IntPack.CalcValues(CtFrameBuf, Info.AssetUUID, FramesWritten + 1, HMAC);
if ( ASDCP_SUCCESS(result) )
{ // write UL
- Overhead.WriteRaw((byte_t*)CryptEssenceUL_Data, klv_key_size);
+ Overhead.WriteRaw(Dict.ul(MDD_CryptEssence), SMPTE_UL_LENGTH);
// construct encrypted triplet header
- ui32_t ETLength = klv_cryptinfo_size + m_CtFrameBuf.Size();
+ ui32_t ETLength = klv_cryptinfo_size + CtFrameBuf.Size();
+ ui32_t essence_element_BER_length = MinEssenceElementBerLength;
- if ( m_Info.UsesHMAC )
+ if ( Info.UsesHMAC )
ETLength += klv_intpack_size;
else
- ETLength += (klv_length_size * 3); // for empty intpack
-
- Overhead.WriteBER(ETLength, klv_length_size); // write encrypted triplet length
- Overhead.WriteBER(UUIDlen, klv_length_size); // write ContextID length
- Overhead.WriteRaw(m_Info.ContextID, UUIDlen); // write ContextID
- Overhead.WriteBER(sizeof(ui64_t), klv_length_size); // write PlaintextOffset length
- Overhead.WriteUi64BE(FrameBuf.PlaintextOffset()); // write PlaintextOffset
- Overhead.WriteBER(klv_key_size, klv_length_size); // write essence UL length
- Overhead.WriteRaw((byte_t*)EssenceUL, klv_key_size); // write the essence UL
- Overhead.WriteBER(sizeof(ui64_t), klv_length_size); // write SourceLength length
- Overhead.WriteUi64BE(FrameBuf.Size()); // write SourceLength
- Overhead.WriteBER(m_CtFrameBuf.Size(), klv_length_size); // write ESV length
-
- result = m_File.Writev(Overhead.Data(), Overhead.Size());
+ ETLength += (MXF_BER_LENGTH * 3); // for empty intpack
+
+ if ( ETLength > 0x00ffffff ) // Need BER integer longer than MXF_BER_LENGTH bytes
+ {
+ essence_element_BER_length = Kumu::get_BER_length_for_value(ETLength);
+
+ // the packet is longer by the difference in expected vs. actual BER length
+ ETLength += essence_element_BER_length - MXF_BER_LENGTH;
+
+ if ( essence_element_BER_length == 0 )
+ result = RESULT_KLV_CODING;
+ }
+
+ if ( ASDCP_SUCCESS(result) )
+ {
+ if ( ! ( Overhead.WriteBER(ETLength, essence_element_BER_length) // write encrypted triplet length
+ && Overhead.WriteBER(UUIDlen, MXF_BER_LENGTH) // write ContextID length
+ && Overhead.WriteRaw(Info.ContextID, UUIDlen) // write ContextID
+ && Overhead.WriteBER(sizeof(ui64_t), MXF_BER_LENGTH) // write PlaintextOffset length
+ && Overhead.WriteUi64BE(FrameBuf.PlaintextOffset()) // write PlaintextOffset
+ && Overhead.WriteBER(SMPTE_UL_LENGTH, MXF_BER_LENGTH) // write essence UL length
+ && Overhead.WriteRaw((byte_t*)EssenceUL, SMPTE_UL_LENGTH) // write the essence UL
+ && Overhead.WriteBER(sizeof(ui64_t), MXF_BER_LENGTH) // write SourceLength length
+ && Overhead.WriteUi64BE(FrameBuf.Size()) // write SourceLength
+ && Overhead.WriteBER(CtFrameBuf.Size(), essence_element_BER_length) ) ) // write ESV length
+ {
+ result = RESULT_KLV_CODING;
+ }
+ }
+
+ if ( ASDCP_SUCCESS(result) )
+ result = File.Writev(Overhead.Data(), Overhead.Length());
}
if ( ASDCP_SUCCESS(result) )
{
- m_StreamOffset += Overhead.Size();
+ StreamOffset += Overhead.Length();
// write encrypted source value
- result = m_File.Writev((byte_t*)m_CtFrameBuf.RoData(), m_CtFrameBuf.Size());
+ result = File.Writev((byte_t*)CtFrameBuf.RoData(), CtFrameBuf.Size());
}
if ( ASDCP_SUCCESS(result) )
{
- m_StreamOffset += m_CtFrameBuf.Size();
+ StreamOffset += CtFrameBuf.Size();
byte_t hmoverhead[512];
- MemIOWriter HMACOverhead(hmoverhead, 512);
+ Kumu::MemIOWriter HMACOverhead(hmoverhead, 512);
// write the HMAC
- if ( m_Info.UsesHMAC )
+ if ( Info.UsesHMAC )
{
HMACOverhead.WriteRaw(IntPack.Data, klv_intpack_size);
}
else
{ // we still need the var-pack length values if the intpack is empty
for ( ui32_t i = 0; i < 3 ; i++ )
- HMACOverhead.WriteBER(0, klv_length_size);
+ HMACOverhead.WriteBER(0, MXF_BER_LENGTH);
}
// write HMAC
- result = m_File.Writev(HMACOverhead.Data(), HMACOverhead.Size());
- m_StreamOffset += HMACOverhead.Size();
+ result = File.Writev(HMACOverhead.Data(), HMACOverhead.Length());
+ StreamOffset += HMACOverhead.Length();
}
}
else
{
- Overhead.WriteRaw((byte_t*)EssenceUL, klv_key_size);
- Overhead.WriteBER(FrameBuf.Size(), klv_length_size);
- result = m_File.Writev(Overhead.Data(), Overhead.Size());
+ ui32_t essence_element_BER_length = MinEssenceElementBerLength;
+
+ if ( FrameBuf.Size() > 0x00ffffff ) // Need BER integer longer than MXF_BER_LENGTH bytes
+ {
+ essence_element_BER_length = Kumu::get_BER_length_for_value(FrameBuf.Size());
+
+ if ( essence_element_BER_length == 0 )
+ result = RESULT_KLV_CODING;
+ }
+
+ Overhead.WriteRaw((byte_t*)EssenceUL, SMPTE_UL_LENGTH);
+ Overhead.WriteBER(FrameBuf.Size(), essence_element_BER_length);
+
+ if ( ASDCP_SUCCESS(result) )
+ result = File.Writev(Overhead.Data(), Overhead.Length());
if ( ASDCP_SUCCESS(result) )
- result = m_File.Writev((byte_t*)FrameBuf.RoData(), FrameBuf.Size());
+ result = File.Writev((byte_t*)FrameBuf.RoData(), FrameBuf.Size());
if ( ASDCP_SUCCESS(result) )
- m_StreamOffset += Overhead.Size() + FrameBuf.Size();
+ StreamOffset += Overhead.Length() + FrameBuf.Size();
}
if ( ASDCP_SUCCESS(result) )
- result = m_File.Writev();
+ result = File.Writev();
return result;
}
-
-// standard method of writing the header and footer of a completed MXF file
-//
-Result_t
-ASDCP::h__Writer::WriteMXFFooter(EssenceType_t EssenceType)
-{
-#if 0
- // write the index
- DataChunk IndexChunk;
- ui32_t IndexSID = 0;
-
- // Find all essence container data sets so we can update "IndexSID"
- MDObjectListPtr ECDataSets = 0;
- MDObject* Ptr = (*m_Metadata)["ContentStorage"];
- if ( Ptr )
- Ptr = Ptr->GetLink();
- if ( Ptr )
- Ptr = (*Ptr)["EssenceContainerData"];
- if ( Ptr )
- ECDataSets = Ptr->ChildList("EssenceContainer");
-
- // ** Handle full index tables next **
- // ***********************************
-
- // Make an index table containing all available entries
- IndexTablePtr Index = m_IndexMan->MakeIndex();
- m_IndexMan->AddEntriesToIndex(Index);
-
- // Write the index table
- Index->WriteIndex(IndexChunk);
-
- // Record the IndexSID for when the index is written
- IndexSID = Index->IndexSID;
-
- // Update IndexSID in essence container data set
- MDObjectList::iterator ECD_it = ECDataSets->begin();
- while(ECD_it != ECDataSets->end())
- {
- if((*ECD_it)->GetLink())
- {
- if((*ECD_it)->GetLink()->GetUint("BodySID") == m_IndexMan->GetBodySID())
- {
- (*ECD_it)->GetLink()->SetUint("IndexSID", m_IndexMan->GetIndexSID());
- break;
- }
- }
-
- ECD_it++;
- }
-
- // If we are writing OP-Atom this is the first place we can claim it
- m_Metadata->SetOP(OPAtomUL);
-
- // Set top-level file package correctly for OP-Atom
- m_Metadata->SetPrimaryPackage(m_FilePackage);
-
- m_Metadata->SetTime();
- m_MPTimecode->SetDuration(m_FramesWritten);
-
- m_MPClip->SetDuration(m_FramesWritten);
- m_FPTimecode->SetDuration(m_FramesWritten);
- m_FPClip->SetDuration(m_FramesWritten);
- m_EssenceDescriptor->SetInt64("ContainerDuration", m_FramesWritten);
-
- // Turn the header or body partition into a footer
- m_HeaderPart->ChangeType("CompleteFooter");
- m_HeaderPart->SetUint("IndexSID", IndexSID);
-
- // Make sure any new sets are linked in
- m_HeaderPart->UpdateMetadata(m_Metadata);
-
- // Actually write the footer
- m_File.WritePartitionWithIndex(*m_HeaderPart, &IndexChunk, false);
-
- // Add a RIP
- m_File.WriteRIP();
-
- //
- // ** Update the header **
- //
- // For generalized OPs update the value of "FooterPartition" in the header pack
- // For OP-Atom re-write the entire header
- //
- ASDCP::fpos_t FooterPos = m_HeaderPart->GetUint64("FooterPartition");
- m_File.Seek(0);
-
- m_HeaderPart->ChangeType("ClosedCompleteHeader");
- m_HeaderPart->SetUint64("FooterPartition", FooterPos);
- m_HeaderPart->SetUint64("BodySID", 1);
-
- m_File.ReWritePartition(*m_HeaderPart);
- m_File.Close();
-#endif
- return RESULT_OK;
-}
-
//
// end h__Writer.cpp
//