2 Copyright (c) 2004-2018, John Hurst
5 Redistribution and use in source and binary forms, with or without
6 modification, are permitted provided that the following conditions
8 1. Redistributions of source code must retain the above copyright
9 notice, this list of conditions and the following disclaimer.
10 2. Redistributions in binary form must reproduce the above copyright
11 notice, this list of conditions and the following disclaimer in the
12 documentation and/or other materials provided with the distribution.
13 3. The name of the author may not be used to endorse or promote products
14 derived from this software without specific prior written permission.
16 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18 OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19 IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21 NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25 THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 /*! \file AS_DCP_internal.h
29 \brief AS-DCP library, non-public common elements
32 #ifndef _AS_DCP_INTERNAL_H_
33 #define _AS_DCP_INTERNAL_H_
35 #include <KM_platform.h>
40 using Kumu::DefaultLogSink;
41 using namespace ASDCP;
42 using namespace ASDCP::MXF;
44 // a magic number identifying asdcplib
45 #ifndef ASDCP_BUILD_NUMBER
46 #define ASDCP_BUILD_NUMBER 0x6A68
50 // uncomment to remove MXFGCGenericEssenceMultipleMappings from your AS-02 files
51 // #define ASDCP_GCMULTI_PATCH
54 #ifdef DEFAULT_MD_DECL
55 ASDCP::MXF::OP1aHeader *g_OP1aHeader;
56 ASDCP::MXF::OPAtomIndexFooter *g_OPAtomIndexFooter;
57 ASDCP::MXF::RIP *g_RIP;
59 extern MXF::OP1aHeader *g_OP1aHeader;
60 extern MXF::OPAtomIndexFooter *g_OPAtomIndexFooter;
61 extern MXF::RIP *g_RIP;
69 static std::vector<int>
70 version_split(const char* str)
72 std::vector<int> result;
73 const char* pstr = str;
74 const char* r = strchr(pstr, '.');
80 result.push_back(strtol(pstr, 0, 10));
83 r = strchr(pstr, '.');
86 if( strlen(pstr) > 0 )
87 result.push_back(strtol(pstr, 0, 10));
89 assert(result.size() == 3);
93 // constant values used to calculate KLV and EKLV packet sizes
94 static const ui32_t klv_cryptinfo_size =
96 + UUIDlen /* ContextID */
98 + sizeof(ui64_t) /* PlaintextOffset */
100 + SMPTE_UL_LENGTH /* SourceKey */
102 + sizeof(ui64_t) /* SourceLength */
103 + MXF_BER_LENGTH /* ESV length */ ;
105 static const ui32_t klv_intpack_size =
107 + UUIDlen /* TrackFileID */
109 + sizeof(ui64_t) /* SequenceNumber */
111 + 20; /* HMAC length*/
113 // calculate size of encrypted essence with IV, CheckValue, and padding
115 calc_esv_length(ui32_t source_length, ui32_t plaintext_offset)
117 ui32_t ct_size = source_length - plaintext_offset;
118 ui32_t diff = ct_size % CBC_BLOCK_SIZE;
119 ui32_t block_size = ct_size - diff;
120 return plaintext_offset + block_size + (CBC_BLOCK_SIZE * 3);
123 // the check value for EKLV packets
125 static const byte_t ESV_CheckValue[CBC_BLOCK_SIZE] =
126 { 0x43, 0x48, 0x55, 0x4b, 0x43, 0x48, 0x55, 0x4b,
127 0x43, 0x48, 0x55, 0x4b, 0x43, 0x48, 0x55, 0x4b };
129 // Version of MXF spec to which an MXF file conforms
137 // version numbers from the MXF spec, to be written into files
139 ui8_t const MXF_ObjectModelVersion = 1;
140 ui8_t const MXF_2004_MinorVersion = 2;
141 ui8_t const MXF_2011_MinorVersion = 3;
144 //------------------------------------------------------------------------------------------
147 ui32_t derive_timecode_rate_from_edit_rate(const ASDCP::Rational& edit_rate);
149 Result_t MD_to_WriterInfo(MXF::Identification*, WriterInfo&);
150 Result_t MD_to_CryptoInfo(MXF::CryptographicContext*, WriterInfo&, const Dictionary&);
152 Result_t EncryptFrameBuffer(const ASDCP::FrameBuffer&, ASDCP::FrameBuffer&, AESEncContext*);
153 Result_t DecryptFrameBuffer(const ASDCP::FrameBuffer&, ASDCP::FrameBuffer&, AESDecContext*);
155 Result_t MD_to_JP2K_PDesc(const ASDCP::MXF::GenericPictureEssenceDescriptor& EssenceDescriptor,
156 const ASDCP::MXF::JPEG2000PictureSubDescriptor& EssenceSubDescriptor,
157 const ASDCP::Rational& EditRate, const ASDCP::Rational& SampleRate,
158 ASDCP::JP2K::PictureDescriptor& PDesc);
160 Result_t JP2K_PDesc_to_MD(const JP2K::PictureDescriptor& PDesc,
161 const ASDCP::Dictionary& dict,
162 ASDCP::MXF::GenericPictureEssenceDescriptor& EssenceDescriptor,
163 ASDCP::MXF::JPEG2000PictureSubDescriptor& EssenceSubDescriptor);
165 Result_t PCM_ADesc_to_MD(PCM::AudioDescriptor& ADesc, ASDCP::MXF::WaveAudioDescriptor* ADescObj);
166 Result_t MD_to_PCM_ADesc(ASDCP::MXF::WaveAudioDescriptor* ADescObj, PCM::AudioDescriptor& ADesc);
168 void AddDmsCrypt(Partition& HeaderPart, SourcePackage& Package,
169 WriterInfo& Descr, const UL& WrappingUL, const Dictionary*& Dict);
171 Result_t AddDmsTrackGenericPartUtf8Text(Kumu::FileWriter&, ASDCP::MXF::OP1aHeader&, SourcePackage&,
172 ASDCP::MXF::RIP&, const Dictionary*&);
174 Result_t WriteGenericStreamPartition(Kumu::FileWriter&, ASDCP::MXF::OP1aHeader&, ASDCP::MXF::RIP&, const Dictionary*&,
175 const ASDCP::FrameBuffer&, ASDCP::AESEncContext* = 0, ASDCP::HMACContext* = 0);
177 Result_t Read_EKLV_Packet(Kumu::FileReader& File, const ASDCP::Dictionary& Dict,
178 const ASDCP::WriterInfo& Info, Kumu::fpos_t& LastPosition, ASDCP::FrameBuffer& CtFrameBuf,
179 ui32_t FrameNum, ui32_t SequenceNum, ASDCP::FrameBuffer& FrameBuf,
180 const byte_t* EssenceUL, AESDecContext* Ctx, HMACContext* HMAC);
182 Result_t Write_EKLV_Packet(Kumu::FileWriter& File, const ASDCP::Dictionary& Dict, const MXF::OP1aHeader& HeaderPart,
183 const ASDCP::WriterInfo& Info, ASDCP::FrameBuffer& CtFrameBuf, ui32_t& FramesWritten,
184 ui64_t & StreamOffset, const ASDCP::FrameBuffer& FrameBuf, const byte_t* EssenceUL,
185 const ui32_t& MinEssenceElementBerLength,
186 AESEncContext* Ctx, HMACContext* HMAC);
189 class KLReader : public ASDCP::KLVPacket
191 ASDCP_NO_COPY_CONSTRUCT(KLReader);
192 byte_t m_KeyBuf[SMPTE_UL_LENGTH*2];
198 inline const byte_t* Key() { return m_KeyBuf; }
199 inline const ui64_t Length() { return m_ValueLength; }
200 inline const ui64_t KLLength() { return m_KLLength; }
202 Result_t ReadKLFromFile(Kumu::FileReader& Reader);
207 //---------------------------------------------------------------------------------
210 /// void default_md_object_init();
212 template <class HeaderType, class IndexAccessType>
213 class TrackFileReader
215 KM_NO_COPY_CONSTRUCT(TrackFileReader);
219 const Dictionary* m_Dict;
220 Kumu::FileReader m_File;
221 HeaderType m_HeaderPart;
222 IndexAccessType m_IndexAccess;
225 ASDCP::FrameBuffer m_CtFrameBuf;
226 Kumu::fpos_t m_LastPosition;
228 TrackFileReader(const Dictionary& d) :
229 m_HeaderPart(m_Dict), m_IndexAccess(m_Dict), m_RIP(m_Dict), m_Dict(&d)
231 default_md_object_init();
234 virtual ~TrackFileReader() {
238 const MXF::RIP& GetRIP() const { return m_RIP; }
241 Result_t OpenMXFRead(const std::string& filename)
244 Result_t result = m_File.OpenRead(filename);
246 if ( ASDCP_SUCCESS(result) )
247 result = SeekToRIP(m_File);
249 if ( ASDCP_SUCCESS(result) )
251 result = m_RIP.InitFromFile(m_File);
252 ui32_t test_s = (ui32_t)m_RIP.PairArray.size();
254 if ( ASDCP_FAILURE(result) )
256 DefaultLogSink().Error("File contains no RIP\n");
258 else if ( m_RIP.PairArray.empty() )
260 DefaultLogSink().Error("RIP contains no Pairs.\n");
265 DefaultLogSink().Error("TrackFileReader::OpenMXFRead, SeekToRIP failed\n");
269 result = m_HeaderPart.InitFromFile(m_File);
271 if ( KM_FAILURE(result) )
273 DefaultLogSink().Error("TrackFileReader::OpenMXFRead, header init failed\n");
283 InterchangeObject* Object;
286 Result_t result = m_HeaderPart.GetMDObjectByType(OBJ_TYPE_ARGS(Identification), &Object);
288 // Writer Info and SourcePackage
289 if ( KM_SUCCESS(result) )
291 MD_to_WriterInfo((Identification*)Object, m_Info);
292 result = m_HeaderPart.GetMDObjectByType(OBJ_TYPE_ARGS(SourcePackage), &Object);
295 if ( KM_SUCCESS(result) )
297 SourcePackage* SP = (SourcePackage*)Object;
298 memcpy(m_Info.AssetUUID, SP->PackageUID.Value() + 16, UUIDlen);
301 // optional CryptographicContext
302 if ( KM_SUCCESS(result) )
304 Result_t cr_result = m_HeaderPart.GetMDObjectByType(OBJ_TYPE_ARGS(CryptographicContext), &Object);
306 if ( KM_SUCCESS(cr_result) )
307 MD_to_CryptoInfo((CryptographicContext*)Object, m_Info, *m_Dict);
313 // positions file before reading
314 // allows external control of index offset
315 Result_t ReadEKLVFrame(const ui64_t& body_offset,
316 ui32_t FrameNum, ASDCP::FrameBuffer& FrameBuf,
317 const byte_t* EssenceUL, AESDecContext* Ctx, HMACContext* HMAC)
319 // look up frame index node
320 IndexTableSegment::IndexEntry TmpEntry;
322 if ( KM_FAILURE(m_IndexAccess.Lookup(FrameNum, TmpEntry)) )
324 DefaultLogSink().Error("Frame value out of range: %u\n", FrameNum);
328 // get relative frame position, apply offset and go read the frame's key and length
329 Kumu::fpos_t FilePosition = body_offset + TmpEntry.StreamOffset;
330 Result_t result = RESULT_OK;
332 if ( FilePosition != m_LastPosition )
334 m_LastPosition = FilePosition;
335 result = m_File.Seek(FilePosition);
338 if ( KM_SUCCESS(result) )
339 result = ReadEKLVPacket(FrameNum, FrameNum + 1, FrameBuf, EssenceUL, Ctx, HMAC);
344 // positions file before reading
345 // assumes "processed" index entries have absolute positions
346 Result_t ReadEKLVFrame(ui32_t FrameNum, ASDCP::FrameBuffer& FrameBuf,
347 const byte_t* EssenceUL, AESDecContext* Ctx, HMACContext* HMAC)
349 // look up frame index node
350 IndexTableSegment::IndexEntry TmpEntry;
352 if ( KM_FAILURE(m_IndexAccess.Lookup(FrameNum, TmpEntry)) )
354 DefaultLogSink().Error("Frame value out of range: %u\n", FrameNum);
358 // get absolute frame position and go read the frame's key and length
359 Result_t result = RESULT_OK;
361 if ( TmpEntry.StreamOffset != m_LastPosition )
363 m_LastPosition = TmpEntry.StreamOffset;
364 result = m_File.Seek(TmpEntry.StreamOffset);
367 if ( KM_SUCCESS(result) )
368 result = ReadEKLVPacket(FrameNum, FrameNum + 1, FrameBuf, EssenceUL, Ctx, HMAC);
373 // reads from current position
374 Result_t ReadEKLVPacket(ui32_t FrameNum, ui32_t SequenceNum, ASDCP::FrameBuffer& FrameBuf,
375 const byte_t* EssenceUL, AESDecContext* Ctx, HMACContext* HMAC)
378 return Read_EKLV_Packet(m_File, *m_Dict, m_Info, m_LastPosition, m_CtFrameBuf,
379 FrameNum, SequenceNum, FrameBuf, EssenceUL, Ctx, HMAC);
382 // Get the position of a frame from a track file
383 Result_t LocateFrame(const ui64_t& body_offset,
384 ui32_t FrameNum, Kumu::fpos_t& streamOffset,
385 i8_t& temporalOffset, i8_t& keyFrameOffset)
387 // look up frame index node
388 IndexTableSegment::IndexEntry TmpEntry;
390 if ( KM_FAILURE(m_IndexAccess.Lookup(FrameNum, TmpEntry)) )
392 DefaultLogSink().Error("Frame value out of range: %u\n", FrameNum);
396 // get frame position, temporal offset, and key frame ofset
397 streamOffset = body_offset + TmpEntry.StreamOffset;
398 temporalOffset = TmpEntry.TemporalOffset;
399 keyFrameOffset = TmpEntry.KeyFrameOffset;
404 // Reads a Generic Stream Partition payload. Returns RESULT_FORMAT if the SID is
405 // not present in the RIP, or if the actual partition at ByteOffset does not have
406 // a matching BodySID value. Encryption is not currently supported.
407 Result_t ReadGenericStreamPartitionPayload(const ui32_t sid, ASDCP::FrameBuffer& frame_buf,
408 AESDecContext* Ctx, HMACContext* HMAC)
410 Kumu::fpos_t start_offset = 0, end_offset = 0;
413 // locate SID, record the offset
414 // Count the sequence length in because this is the sequence
415 // value needed to complete the HMAC.
416 ASDCP::MXF::RIP::const_pair_iterator i;
417 for ( i = m_RIP.PairArray.begin(); i != m_RIP.PairArray.end(); ++i)
419 if ( sid == i->BodySID )
421 assert( start_offset == 0);
422 start_offset = i->ByteOffset;
424 else if ( start_offset != 0 )
426 end_offset = i->ByteOffset;
430 if ( i->BodySID > 0 )
436 if ( start_offset == 0 || end_offset == 0 )
438 DefaultLogSink().Error("Body SID not found: %d.\n", sid);
439 return RESULT_NOT_FOUND;
442 // Read the Partition header and then read the payload.
443 Result_t result = m_File.Seek(start_offset);
445 if ( KM_SUCCESS(result) )
447 result = frame_buf.Capacity(end_offset-start_offset);
450 if ( KM_SUCCESS(result) )
452 // read the partition header
453 ASDCP::MXF::Partition GSPart(m_Dict);
454 result = GSPart.InitFromFile(m_File);
456 if ( KM_SUCCESS(result) )
459 if ( GSPart.BodySID != sid )
461 DefaultLogSink().Error("Generic stream partition Body SID differs: %s\n", sid);
462 result = RESULT_FORMAT;
466 result = ReadEKLVPacket(0, sequence, frame_buf, m_Dict->ul(MDD_GenericStream_DataElement), Ctx, HMAC);
481 //------------------------------------------------------------------------------------------
485 template <class ClipT>
489 MXF::Sequence* Sequence;
492 TrackSet() : Track(0), Sequence(0), Clip(0) {}
496 template <class PackageT, class ClipT>
498 CreateTrackAndSequence(OP1aHeader& Header, PackageT& Package, const std::string TrackName,
499 const MXF::Rational& clip_edit_rate, const UL& Definition, ui32_t TrackID, const Dictionary*& Dict)
501 TrackSet<ClipT> NewTrack;
503 NewTrack.Track = new Track(Dict);
504 Header.AddChildObject(NewTrack.Track);
505 NewTrack.Track->EditRate = clip_edit_rate;
506 Package.Tracks.push_back(NewTrack.Track->InstanceUID);
507 NewTrack.Track->TrackID = TrackID;
508 NewTrack.Track->TrackName = TrackName.c_str();
510 NewTrack.Sequence = new Sequence(Dict);
511 Header.AddChildObject(NewTrack.Sequence);
512 NewTrack.Track->Sequence = NewTrack.Sequence->InstanceUID;
513 NewTrack.Sequence->DataDefinition = Definition;
519 template <class PackageT>
520 TrackSet<TimecodeComponent>
521 CreateTimecodeTrack(OP1aHeader& Header, PackageT& Package,
522 const MXF::Rational& tc_edit_rate, ui32_t tc_frame_rate, ui64_t TCStart, const Dictionary*& Dict)
525 UL TCUL(Dict->ul(MDD_TimecodeDataDef));
527 TrackSet<TimecodeComponent> NewTrack =
528 CreateTrackAndSequence<PackageT, TimecodeComponent>(Header, Package, "Timecode Track",
529 tc_edit_rate, TCUL, 1, Dict);
531 NewTrack.Clip = new TimecodeComponent(Dict);
532 Header.AddChildObject(NewTrack.Clip);
533 NewTrack.Sequence->StructuralComponents.push_back(NewTrack.Clip->InstanceUID);
534 NewTrack.Clip->RoundedTimecodeBase = tc_frame_rate;
535 NewTrack.Clip->StartTimecode = TCStart;
536 NewTrack.Clip->DataDefinition = TCUL;
542 // state machine for mxf writer
544 ST_BEGIN, // waiting for Open()
545 ST_INIT, // waiting for SetSourceStream()
546 ST_READY, // ready to write frames
547 ST_RUNNING, // one or more frames written
548 ST_FINAL, // index written, file closed
552 // implementation of h__WriterState class Goto_* methods
553 #define Goto_body(s1,s2) \
554 if ( m_State != (s1) ) { \
555 return RESULT_STATE; \
562 ASDCP_NO_COPY_CONSTRUCT(h__WriterState);
565 WriterState_t m_State;
566 h__WriterState() : m_State(ST_BEGIN) {}
569 inline bool Test_BEGIN() { return m_State == ST_BEGIN; }
570 inline bool Test_INIT() { return m_State == ST_INIT; }
571 inline bool Test_READY() { return m_State == ST_READY;}
572 inline bool Test_RUNNING() { return m_State == ST_RUNNING; }
573 inline bool Test_FINAL() { return m_State == ST_FINAL; }
574 inline Result_t Goto_INIT() { Goto_body(ST_BEGIN, ST_INIT); }
575 inline Result_t Goto_READY() { Goto_body(ST_INIT, ST_READY); }
576 inline Result_t Goto_RUNNING() { Goto_body(ST_READY, ST_RUNNING); }
577 inline Result_t Goto_FINAL() { Goto_body(ST_RUNNING, ST_FINAL); }
580 //------------------------------------------------------------------------------------------
584 template <class HeaderType>
585 class TrackFileWriter
587 KM_NO_COPY_CONSTRUCT(TrackFileWriter);
591 const Dictionary* m_Dict;
592 Kumu::FileWriter m_File;
594 HeaderType m_HeaderPart;
597 MaterialPackage* m_MaterialPackage;
598 SourcePackage* m_FilePackage;
599 ContentStorage* m_ContentStorage;
601 FileDescriptor* m_EssenceDescriptor;
602 std::list<InterchangeObject*> m_EssenceSubDescriptorList;
604 ui32_t m_FramesWritten;
605 ui64_t m_StreamOffset;
606 ASDCP::FrameBuffer m_CtFrameBuf;
607 h__WriterState m_State;
610 typedef std::list<ui64_t*> DurationElementList_t;
611 DurationElementList_t m_DurationUpdateList;
613 TrackFileWriter(const Dictionary& d) :
614 m_Dict(&d), m_HeaderSize(0), m_HeaderPart(m_Dict), m_RIP(m_Dict),
615 m_MaterialPackage(0), m_FilePackage(0), m_ContentStorage(0),
616 m_EssenceDescriptor(0), m_FramesWritten(0), m_StreamOffset(0)
618 default_md_object_init();
621 virtual ~TrackFileWriter() {
625 const MXF::RIP& GetRIP() const { return m_RIP; }
627 void InitHeader(const MXFVersion& mxf_ver)
630 assert(m_EssenceDescriptor);
632 m_HeaderPart.m_Primer.ClearTagList();
633 m_HeaderPart.m_Preface = new Preface(m_Dict);
634 m_HeaderPart.AddChildObject(m_HeaderPart.m_Preface);
636 // Set the Operational Pattern label -- we're just starting and have no RIP or index,
637 // so we tell the world by using OP1a
638 m_HeaderPart.m_Preface->OperationalPattern = UL(m_Dict->ul(MDD_OP1a));
639 m_HeaderPart.OperationalPattern = m_HeaderPart.m_Preface->OperationalPattern;
641 if ( mxf_ver == MXFVersion_2004 )
643 m_HeaderPart.MinorVersion = MXF_2004_MinorVersion;
644 m_HeaderPart.m_Preface->Version = ((MXF_ObjectModelVersion << 8) | MXF_2004_MinorVersion);
645 m_HeaderPart.m_Preface->ObjectModelVersion = MXF_ObjectModelVersion;
649 assert(mxf_ver == MXFVersion_2011);
650 m_HeaderPart.MinorVersion = MXF_2011_MinorVersion;
651 m_HeaderPart.m_Preface->Version = ((MXF_ObjectModelVersion << 8) | MXF_2011_MinorVersion);
652 m_HeaderPart.m_Preface->ObjectModelVersion = MXF_ObjectModelVersion;
656 Identification* Ident = new Identification(m_Dict);
657 m_HeaderPart.AddChildObject(Ident);
658 m_HeaderPart.m_Preface->Identifications.push_back(Ident->InstanceUID);
660 Kumu::GenRandomValue(Ident->ThisGenerationUID);
661 Ident->CompanyName = m_Info.CompanyName.c_str();
662 Ident->ProductName = m_Info.ProductName.c_str();
663 Ident->VersionString = m_Info.ProductVersion.c_str();
664 Ident->ProductUID.Set(m_Info.ProductUUID);
665 Ident->Platform = ASDCP_PLATFORM;
667 std::vector<int> version = version_split(Version());
669 Ident->ToolkitVersion.Major = version[0];
670 Ident->ToolkitVersion.Minor = version[1];
671 Ident->ToolkitVersion.Patch = version[2];
672 Ident->ToolkitVersion.Build = ASDCP_BUILD_NUMBER;
673 Ident->ToolkitVersion.Release = VersionType::RL_RELEASE;
677 void AddSourceClip(const MXF::Rational& clip_edit_rate,
678 const MXF::Rational& tc_edit_rate, ui32_t TCFrameRate,
679 const std::string& TrackName, const UL& EssenceUL,
680 const UL& DataDefinition, const std::string& PackageLabel)
682 if ( m_ContentStorage == 0 )
684 m_ContentStorage = new ContentStorage(m_Dict);
685 m_HeaderPart.AddChildObject(m_ContentStorage);
686 m_HeaderPart.m_Preface->ContentStorage = m_ContentStorage->InstanceUID;
689 EssenceContainerData* ECD = new EssenceContainerData(m_Dict);
690 m_HeaderPart.AddChildObject(ECD);
691 m_ContentStorage->EssenceContainerData.push_back(ECD->InstanceUID);
695 UUID assetUUID(m_Info.AssetUUID);
696 UMID SourcePackageUMID, MaterialPackageUMID;
697 SourcePackageUMID.MakeUMID(0x0f, assetUUID);
698 MaterialPackageUMID.MakeUMID(0x0f); // unidentified essence
703 m_MaterialPackage = new MaterialPackage(m_Dict);
704 m_MaterialPackage->Name = "Material Package";
705 m_MaterialPackage->PackageUID = MaterialPackageUMID;
706 m_HeaderPart.AddChildObject(m_MaterialPackage);
707 m_ContentStorage->Packages.push_back(m_MaterialPackage->InstanceUID);
709 TrackSet<TimecodeComponent> MPTCTrack =
710 CreateTimecodeTrack<MaterialPackage>(m_HeaderPart, *m_MaterialPackage,
711 tc_edit_rate, TCFrameRate, 0, m_Dict);
713 MPTCTrack.Sequence->Duration.set_has_value();
714 m_DurationUpdateList.push_back(&(MPTCTrack.Sequence->Duration.get()));
715 MPTCTrack.Clip->Duration.set_has_value();
716 m_DurationUpdateList.push_back(&(MPTCTrack.Clip->Duration.get()));
718 TrackSet<SourceClip> MPTrack =
719 CreateTrackAndSequence<MaterialPackage, SourceClip>(m_HeaderPart, *m_MaterialPackage,
720 TrackName, clip_edit_rate, DataDefinition,
722 MPTrack.Sequence->Duration.set_has_value();
723 m_DurationUpdateList.push_back(&(MPTrack.Sequence->Duration.get()));
725 MPTrack.Clip = new SourceClip(m_Dict);
726 m_HeaderPart.AddChildObject(MPTrack.Clip);
727 MPTrack.Sequence->StructuralComponents.push_back(MPTrack.Clip->InstanceUID);
728 MPTrack.Clip->DataDefinition = DataDefinition;
729 MPTrack.Clip->SourcePackageID = SourcePackageUMID;
730 MPTrack.Clip->SourceTrackID = 2;
732 MPTrack.Clip->Duration.set_has_value();
733 m_DurationUpdateList.push_back(&(MPTrack.Clip->Duration.get()));
737 // File (Source) Package
739 m_FilePackage = new SourcePackage(m_Dict);
740 m_FilePackage->Name = PackageLabel.c_str();
741 m_FilePackage->PackageUID = SourcePackageUMID;
742 ECD->LinkedPackageUID = SourcePackageUMID;
744 m_HeaderPart.AddChildObject(m_FilePackage);
745 m_ContentStorage->Packages.push_back(m_FilePackage->InstanceUID);
747 TrackSet<TimecodeComponent> FPTCTrack =
748 CreateTimecodeTrack<SourcePackage>(m_HeaderPart, *m_FilePackage,
749 tc_edit_rate, TCFrameRate, 0, m_Dict);
751 FPTCTrack.Sequence->Duration.set_has_value();
752 m_DurationUpdateList.push_back(&(FPTCTrack.Sequence->Duration.get()));
753 FPTCTrack.Clip->Duration.set_has_value();
754 m_DurationUpdateList.push_back(&(FPTCTrack.Clip->Duration.get()));
756 TrackSet<SourceClip> FPTrack =
757 CreateTrackAndSequence<SourcePackage, SourceClip>(m_HeaderPart, *m_FilePackage,
758 TrackName, clip_edit_rate, DataDefinition,
761 FPTrack.Sequence->Duration.set_has_value();
762 m_DurationUpdateList.push_back(&(FPTrack.Sequence->Duration.get()));
764 // Consult ST 379:2004 Sec. 6.3, "Element to track relationship" to see where "12" comes from.
765 FPTrack.Track->TrackNumber = KM_i32_BE(Kumu::cp2i<ui32_t>((EssenceUL.Value() + 12)));
767 FPTrack.Clip = new SourceClip(m_Dict);
768 m_HeaderPart.AddChildObject(FPTrack.Clip);
769 FPTrack.Sequence->StructuralComponents.push_back(FPTrack.Clip->InstanceUID);
770 FPTrack.Clip->DataDefinition = DataDefinition;
772 // for now we do not allow setting this value, so all files will be 'original'
773 FPTrack.Clip->SourceTrackID = 0;
774 FPTrack.Clip->SourcePackageID = NilUMID;
776 FPTrack.Clip->Duration.set_has_value();
777 m_DurationUpdateList.push_back(&(FPTrack.Clip->Duration.get()));
779 m_EssenceDescriptor->LinkedTrackID = FPTrack.Track->TrackID;
783 void AddEssenceDescriptor(const UL& WrappingUL)
786 // Essence Descriptor
788 m_EssenceDescriptor->EssenceContainer = WrappingUL;
789 m_HeaderPart.m_Preface->PrimaryPackage = m_FilePackage->InstanceUID;
792 // Essence Descriptors
795 #ifndef ASDCP_GCMULTI_PATCH
796 UL GenericContainerUL(m_Dict->ul(MDD_GCMulti));
797 m_HeaderPart.EssenceContainers.push_back(GenericContainerUL);
800 if ( m_Info.EncryptedEssence )
802 UL CryptEssenceUL(m_Dict->ul(MDD_EncryptedContainerLabel));
803 m_HeaderPart.EssenceContainers.push_back(CryptEssenceUL);
804 m_HeaderPart.m_Preface->DMSchemes.push_back(UL(m_Dict->ul(MDD_CryptographicFrameworkLabel)));
805 AddDmsCrypt(m_HeaderPart, *m_FilePackage, m_Info, WrappingUL, m_Dict);
806 //// TODO: fix DMSegment Duration value
810 m_HeaderPart.EssenceContainers.push_back(WrappingUL);
813 m_HeaderPart.m_Preface->EssenceContainers = m_HeaderPart.EssenceContainers;
814 m_HeaderPart.AddChildObject(m_EssenceDescriptor);
816 std::list<InterchangeObject*>::iterator sdli = m_EssenceSubDescriptorList.begin();
817 for ( ; sdli != m_EssenceSubDescriptorList.end(); sdli++ )
818 m_HeaderPart.AddChildObject(*sdli);
820 m_FilePackage->Descriptor = m_EssenceDescriptor->InstanceUID;
823 Result_t AddDmsGenericPartUtf8Text(const ASDCP::FrameBuffer& frame_buffer,
824 ASDCP::AESEncContext* enc = 0, ASDCP::HMACContext* hmac = 0)
826 Kumu::fpos_t previous_partition_offset = m_RIP.PairArray.back().ByteOffset;
827 Result_t result = AddDmsTrackGenericPartUtf8Text(m_File, m_HeaderPart, *m_FilePackage, m_RIP, m_Dict);
829 if ( KM_SUCCESS(result) )
831 // m_RIP now contains an entry (at the back) for the new generic stream
832 // (this entry was created during the call to AddDmsTrackGenericPartUtf8Text())
833 if ( m_File.Tell() != m_RIP.PairArray.back().ByteOffset )
835 DefaultLogSink().Error("File offset has moved since RIP modification. Unrecoverable error.\n");
839 // create generic stream partition header
840 static UL GenericStream_DataElement(m_Dict->ul(MDD_GenericStream_DataElement));
841 ASDCP::MXF::Partition GSPart(m_Dict);
843 GSPart.MajorVersion = m_HeaderPart.MajorVersion;
844 GSPart.MinorVersion = m_HeaderPart.MinorVersion;
845 GSPart.ThisPartition = m_RIP.PairArray.back().ByteOffset;
846 GSPart.PreviousPartition = previous_partition_offset;
847 GSPart.OperationalPattern = m_HeaderPart.OperationalPattern;
848 GSPart.BodySID = m_RIP.PairArray.back().BodySID;
849 GSPart.EssenceContainers = m_HeaderPart.EssenceContainers;
851 static UL gs_part_ul(m_Dict->ul(MDD_GenericStreamPartition));
852 Result_t result = GSPart.WriteToFile(m_File, gs_part_ul);
854 if ( KM_SUCCESS(result) )
856 result = Write_EKLV_Packet(m_File, *m_Dict, m_HeaderPart, m_Info, m_CtFrameBuf, m_FramesWritten,
857 m_StreamOffset, frame_buffer, GenericStream_DataElement.Value(),
858 MXF_BER_LENGTH, enc, hmac);
875 //------------------------------------------------------------------------------------------
879 class h__ASDCPReader : public MXF::TrackFileReader<OP1aHeader, OPAtomIndexFooter>
881 ASDCP_NO_COPY_CONSTRUCT(h__ASDCPReader);
885 Partition m_BodyPart;
887 h__ASDCPReader(const Dictionary&);
888 virtual ~h__ASDCPReader();
890 Result_t OpenMXFRead(const std::string& filename);
891 Result_t ReadEKLVFrame(ui32_t FrameNum, ASDCP::FrameBuffer& FrameBuf,
892 const byte_t* EssenceUL, AESDecContext* Ctx, HMACContext* HMAC);
893 Result_t LocateFrame(ui32_t FrameNum, Kumu::fpos_t& streamOffset,
894 i8_t& temporalOffset, i8_t& keyFrameOffset);
898 class h__ASDCPWriter : public MXF::TrackFileWriter<OP1aHeader>
900 ASDCP_NO_COPY_CONSTRUCT(h__ASDCPWriter);
904 Partition m_BodyPart;
905 OPAtomIndexFooter m_FooterPart;
907 h__ASDCPWriter(const Dictionary&);
908 virtual ~h__ASDCPWriter();
910 // all the above for a single source clip
911 Result_t WriteASDCPHeader(const std::string& PackageLabel, const UL& WrappingUL,
912 const std::string& TrackName, const UL& EssenceUL,
913 const UL& DataDefinition, const MXF::Rational& EditRate,
914 ui32_t TCFrameRate, ui32_t BytesPerEditUnit = 0);
916 Result_t CreateBodyPart(const MXF::Rational& EditRate, ui32_t BytesPerEditUnit = 0);
917 Result_t WriteEKLVPacket(const ASDCP::FrameBuffer& FrameBuf,const byte_t* EssenceUL,
918 const ui32_t& MinEssenceElementBerLength,
919 AESEncContext* Ctx, HMACContext* HMAC);
920 Result_t WriteASDCPFooter();
924 // helper class for calculating Integrity Packs, used by WriteEKLVPacket() below.
929 byte_t Data[klv_intpack_size];
932 memset(Data, 0, klv_intpack_size);
937 Result_t CalcValues(const ASDCP::FrameBuffer&, const byte_t* AssetID, ui32_t sequence, HMACContext* HMAC);
938 Result_t TestValues(const ASDCP::FrameBuffer&, const byte_t* AssetID, ui32_t sequence, HMACContext* HMAC);
944 #endif // _AS_DCP_INTERNAL_H_
948 // end AS_DCP_internal.h