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 #ifdef DEFAULT_MD_DECL
51 ASDCP::MXF::OP1aHeader *g_OP1aHeader;
52 ASDCP::MXF::OPAtomIndexFooter *g_OPAtomIndexFooter;
53 ASDCP::MXF::RIP *g_RIP;
55 extern MXF::OP1aHeader *g_OP1aHeader;
56 extern MXF::OPAtomIndexFooter *g_OPAtomIndexFooter;
57 extern MXF::RIP *g_RIP;
65 static std::vector<int>
66 version_split(const char* str)
68 std::vector<int> result;
69 const char* pstr = str;
70 const char* r = strchr(pstr, '.');
76 result.push_back(strtol(pstr, 0, 10));
79 r = strchr(pstr, '.');
82 if( strlen(pstr) > 0 )
83 result.push_back(strtol(pstr, 0, 10));
85 assert(result.size() == 3);
89 // constant values used to calculate KLV and EKLV packet sizes
90 static const ui32_t klv_cryptinfo_size =
92 + UUIDlen /* ContextID */
94 + sizeof(ui64_t) /* PlaintextOffset */
96 + SMPTE_UL_LENGTH /* SourceKey */
98 + sizeof(ui64_t) /* SourceLength */
99 + MXF_BER_LENGTH /* ESV length */ ;
101 static const ui32_t klv_intpack_size =
103 + UUIDlen /* TrackFileID */
105 + sizeof(ui64_t) /* SequenceNumber */
107 + 20; /* HMAC length*/
109 // calculate size of encrypted essence with IV, CheckValue, and padding
111 calc_esv_length(ui32_t source_length, ui32_t plaintext_offset)
113 ui32_t ct_size = source_length - plaintext_offset;
114 ui32_t diff = ct_size % CBC_BLOCK_SIZE;
115 ui32_t block_size = ct_size - diff;
116 return plaintext_offset + block_size + (CBC_BLOCK_SIZE * 3);
119 // the check value for EKLV packets
121 static const byte_t ESV_CheckValue[CBC_BLOCK_SIZE] =
122 { 0x43, 0x48, 0x55, 0x4b, 0x43, 0x48, 0x55, 0x4b,
123 0x43, 0x48, 0x55, 0x4b, 0x43, 0x48, 0x55, 0x4b };
125 // Version of MXF spec to which an MXF file conforms
133 // version numbers from the MXF spec, to be written into files
135 ui8_t const MXF_ObjectModelVersion = 1;
136 ui8_t const MXF_2004_MinorVersion = 2;
137 ui8_t const MXF_2011_MinorVersion = 3;
140 //------------------------------------------------------------------------------------------
143 ui32_t derive_timecode_rate_from_edit_rate(const ASDCP::Rational& edit_rate);
145 Result_t MD_to_WriterInfo(MXF::Identification*, WriterInfo&);
146 Result_t MD_to_CryptoInfo(MXF::CryptographicContext*, WriterInfo&, const Dictionary&);
148 Result_t EncryptFrameBuffer(const ASDCP::FrameBuffer&, ASDCP::FrameBuffer&, AESEncContext*);
149 Result_t DecryptFrameBuffer(const ASDCP::FrameBuffer&, ASDCP::FrameBuffer&, AESDecContext*);
151 Result_t MD_to_JP2K_PDesc(const ASDCP::MXF::GenericPictureEssenceDescriptor& EssenceDescriptor,
152 const ASDCP::MXF::JPEG2000PictureSubDescriptor& EssenceSubDescriptor,
153 const ASDCP::Rational& EditRate, const ASDCP::Rational& SampleRate,
154 ASDCP::JP2K::PictureDescriptor& PDesc);
156 Result_t JP2K_PDesc_to_MD(const JP2K::PictureDescriptor& PDesc,
157 const ASDCP::Dictionary& dict,
158 ASDCP::MXF::GenericPictureEssenceDescriptor& EssenceDescriptor,
159 ASDCP::MXF::JPEG2000PictureSubDescriptor& EssenceSubDescriptor);
161 Result_t PCM_ADesc_to_MD(PCM::AudioDescriptor& ADesc, ASDCP::MXF::WaveAudioDescriptor* ADescObj);
162 Result_t MD_to_PCM_ADesc(ASDCP::MXF::WaveAudioDescriptor* ADescObj, PCM::AudioDescriptor& ADesc);
164 void AddDmsCrypt(Partition& HeaderPart, SourcePackage& Package,
165 WriterInfo& Descr, const UL& WrappingUL, const Dictionary*& Dict);
167 Result_t AddDmsTrackGenericPartUtf8Text(Kumu::FileWriter&, ASDCP::MXF::OP1aHeader&, SourcePackage&,
168 ASDCP::MXF::RIP&, const Dictionary*&);
170 Result_t WriteGenericStreamPartition(Kumu::FileWriter&, ASDCP::MXF::OP1aHeader&, ASDCP::MXF::RIP&, const Dictionary*&,
171 const ASDCP::FrameBuffer&, ASDCP::AESEncContext* = 0, ASDCP::HMACContext* = 0);
173 Result_t Read_EKLV_Packet(Kumu::FileReader& File, const ASDCP::Dictionary& Dict,
174 const ASDCP::WriterInfo& Info, Kumu::fpos_t& LastPosition, ASDCP::FrameBuffer& CtFrameBuf,
175 ui32_t FrameNum, ui32_t SequenceNum, ASDCP::FrameBuffer& FrameBuf,
176 const byte_t* EssenceUL, AESDecContext* Ctx, HMACContext* HMAC);
178 Result_t Write_EKLV_Packet(Kumu::FileWriter& File, const ASDCP::Dictionary& Dict, const MXF::OP1aHeader& HeaderPart,
179 const ASDCP::WriterInfo& Info, ASDCP::FrameBuffer& CtFrameBuf, ui32_t& FramesWritten,
180 ui64_t & StreamOffset, const ASDCP::FrameBuffer& FrameBuf, const byte_t* EssenceUL,
181 AESEncContext* Ctx, HMACContext* HMAC);
184 class KLReader : public ASDCP::KLVPacket
186 ASDCP_NO_COPY_CONSTRUCT(KLReader);
187 byte_t m_KeyBuf[SMPTE_UL_LENGTH*2];
193 inline const byte_t* Key() { return m_KeyBuf; }
194 inline const ui64_t Length() { return m_ValueLength; }
195 inline const ui64_t KLLength() { return m_KLLength; }
197 Result_t ReadKLFromFile(Kumu::FileReader& Reader);
202 //---------------------------------------------------------------------------------
205 /// void default_md_object_init();
207 template <class HeaderType, class IndexAccessType>
208 class TrackFileReader
210 KM_NO_COPY_CONSTRUCT(TrackFileReader);
214 const Dictionary* m_Dict;
215 Kumu::FileReader m_File;
216 HeaderType m_HeaderPart;
217 IndexAccessType m_IndexAccess;
220 ASDCP::FrameBuffer m_CtFrameBuf;
221 Kumu::fpos_t m_LastPosition;
223 TrackFileReader(const Dictionary& d) :
224 m_HeaderPart(m_Dict), m_IndexAccess(m_Dict), m_RIP(m_Dict), m_Dict(&d)
226 default_md_object_init();
229 virtual ~TrackFileReader() {
233 const MXF::RIP& GetRIP() const { return m_RIP; }
236 Result_t OpenMXFRead(const std::string& filename)
239 Result_t result = m_File.OpenRead(filename);
241 if ( ASDCP_SUCCESS(result) )
242 result = SeekToRIP(m_File);
244 if ( ASDCP_SUCCESS(result) )
246 result = m_RIP.InitFromFile(m_File);
247 ui32_t test_s = (ui32_t)m_RIP.PairArray.size();
249 if ( ASDCP_FAILURE(result) )
251 DefaultLogSink().Error("File contains no RIP\n");
253 else if ( m_RIP.PairArray.empty() )
255 DefaultLogSink().Error("RIP contains no Pairs.\n");
260 DefaultLogSink().Error("TrackFileReader::OpenMXFRead, SeekToRIP failed\n");
264 result = m_HeaderPart.InitFromFile(m_File);
266 if ( KM_FAILURE(result) )
268 DefaultLogSink().Error("TrackFileReader::OpenMXFRead, header init failed\n");
278 InterchangeObject* Object;
281 Result_t result = m_HeaderPart.GetMDObjectByType(OBJ_TYPE_ARGS(Identification), &Object);
283 // Writer Info and SourcePackage
284 if ( KM_SUCCESS(result) )
286 MD_to_WriterInfo((Identification*)Object, m_Info);
287 result = m_HeaderPart.GetMDObjectByType(OBJ_TYPE_ARGS(SourcePackage), &Object);
290 if ( KM_SUCCESS(result) )
292 SourcePackage* SP = (SourcePackage*)Object;
293 memcpy(m_Info.AssetUUID, SP->PackageUID.Value() + 16, UUIDlen);
296 // optional CryptographicContext
297 if ( KM_SUCCESS(result) )
299 Result_t cr_result = m_HeaderPart.GetMDObjectByType(OBJ_TYPE_ARGS(CryptographicContext), &Object);
301 if ( KM_SUCCESS(cr_result) )
302 MD_to_CryptoInfo((CryptographicContext*)Object, m_Info, *m_Dict);
308 // positions file before reading
309 // allows external control of index offset
310 Result_t ReadEKLVFrame(const ui64_t& body_offset,
311 ui32_t FrameNum, ASDCP::FrameBuffer& FrameBuf,
312 const byte_t* EssenceUL, AESDecContext* Ctx, HMACContext* HMAC)
314 // look up frame index node
315 IndexTableSegment::IndexEntry TmpEntry;
317 if ( KM_FAILURE(m_IndexAccess.Lookup(FrameNum, TmpEntry)) )
319 DefaultLogSink().Error("Frame value out of range: %u\n", FrameNum);
323 // get relative frame position, apply offset and go read the frame's key and length
324 Kumu::fpos_t FilePosition = body_offset + TmpEntry.StreamOffset;
325 Result_t result = RESULT_OK;
327 if ( FilePosition != m_LastPosition )
329 m_LastPosition = FilePosition;
330 result = m_File.Seek(FilePosition);
333 if ( KM_SUCCESS(result) )
334 result = ReadEKLVPacket(FrameNum, FrameNum + 1, FrameBuf, EssenceUL, Ctx, HMAC);
339 // positions file before reading
340 // assumes "processed" index entries have absolute positions
341 Result_t ReadEKLVFrame(ui32_t FrameNum, ASDCP::FrameBuffer& FrameBuf,
342 const byte_t* EssenceUL, AESDecContext* Ctx, HMACContext* HMAC)
344 // look up frame index node
345 IndexTableSegment::IndexEntry TmpEntry;
347 if ( KM_FAILURE(m_IndexAccess.Lookup(FrameNum, TmpEntry)) )
349 DefaultLogSink().Error("Frame value out of range: %u\n", FrameNum);
353 // get absolute frame position and go read the frame's key and length
354 Result_t result = RESULT_OK;
356 if ( TmpEntry.StreamOffset != m_LastPosition )
358 m_LastPosition = TmpEntry.StreamOffset;
359 result = m_File.Seek(TmpEntry.StreamOffset);
362 if ( KM_SUCCESS(result) )
363 result = ReadEKLVPacket(FrameNum, FrameNum + 1, FrameBuf, EssenceUL, Ctx, HMAC);
368 // reads from current position
369 Result_t ReadEKLVPacket(ui32_t FrameNum, ui32_t SequenceNum, ASDCP::FrameBuffer& FrameBuf,
370 const byte_t* EssenceUL, AESDecContext* Ctx, HMACContext* HMAC)
373 return Read_EKLV_Packet(m_File, *m_Dict, m_Info, m_LastPosition, m_CtFrameBuf,
374 FrameNum, SequenceNum, FrameBuf, EssenceUL, Ctx, HMAC);
377 // Get the position of a frame from a track file
378 Result_t LocateFrame(const ui64_t& body_offset,
379 ui32_t FrameNum, Kumu::fpos_t& streamOffset,
380 i8_t& temporalOffset, i8_t& keyFrameOffset)
382 // look up frame index node
383 IndexTableSegment::IndexEntry TmpEntry;
385 if ( KM_FAILURE(m_IndexAccess.Lookup(FrameNum, TmpEntry)) )
387 DefaultLogSink().Error("Frame value out of range: %u\n", FrameNum);
391 // get frame position, temporal offset, and key frame ofset
392 streamOffset = body_offset + TmpEntry.StreamOffset;
393 temporalOffset = TmpEntry.TemporalOffset;
394 keyFrameOffset = TmpEntry.KeyFrameOffset;
399 // Reads a Generic Stream Partition payload. Returns RESULT_FORMAT if the SID is
400 // not present in the RIP, or if the actual partition at ByteOffset does not have
401 // a matching BodySID value. Encryption is not currently supported.
402 Result_t ReadGenericStreamPartitionPayload(const ui32_t sid, ASDCP::FrameBuffer& frame_buf,
403 AESDecContext* Ctx, HMACContext* HMAC)
405 Kumu::fpos_t start_offset = 0, end_offset = 0;
408 // locate SID, record the offset
409 // Count the sequence length in because this is the sequence
410 // value needed to complete the HMAC.
411 ASDCP::MXF::RIP::const_pair_iterator i;
412 for ( i = m_RIP.PairArray.begin(); i != m_RIP.PairArray.end(); ++i, ++sequence )
414 if ( sid == i->BodySID )
416 start_offset = i->ByteOffset;
418 else if ( start_offset != 0 )
420 end_offset = i->ByteOffset;
425 if ( start_offset == 0 || end_offset == 0 )
427 DefaultLogSink().Error("Body SID not found: %d.\n", sid);
428 return RESULT_NOT_FOUND;
431 // Read the Partition header and then read the payload.
432 Result_t result = m_File.Seek(start_offset);
434 if ( KM_SUCCESS(result) )
436 result = frame_buf.Capacity(end_offset-start_offset);
439 if ( KM_SUCCESS(result) )
441 // read the partition header
442 ASDCP::MXF::Partition GSPart(m_Dict);
443 result = GSPart.InitFromFile(m_File);
445 if ( KM_SUCCESS(result) )
448 if ( GSPart.BodySID != sid )
450 DefaultLogSink().Error("Generic stream partition Body SID differs: %s\n", sid);
451 result = RESULT_FORMAT;
455 result = ReadEKLVPacket(0, sequence, frame_buf, m_Dict->ul(MDD_GenericStream_DataElement), Ctx, HMAC);
470 //------------------------------------------------------------------------------------------
474 template <class ClipT>
478 MXF::Sequence* Sequence;
481 TrackSet() : Track(0), Sequence(0), Clip(0) {}
485 template <class PackageT, class ClipT>
487 CreateTrackAndSequence(OP1aHeader& Header, PackageT& Package, const std::string TrackName,
488 const MXF::Rational& clip_edit_rate, const UL& Definition, ui32_t TrackID, const Dictionary*& Dict)
490 TrackSet<ClipT> NewTrack;
492 NewTrack.Track = new Track(Dict);
493 Header.AddChildObject(NewTrack.Track);
494 NewTrack.Track->EditRate = clip_edit_rate;
495 Package.Tracks.push_back(NewTrack.Track->InstanceUID);
496 NewTrack.Track->TrackID = TrackID;
497 NewTrack.Track->TrackName = TrackName.c_str();
499 NewTrack.Sequence = new Sequence(Dict);
500 Header.AddChildObject(NewTrack.Sequence);
501 NewTrack.Track->Sequence = NewTrack.Sequence->InstanceUID;
502 NewTrack.Sequence->DataDefinition = Definition;
508 template <class PackageT>
509 TrackSet<TimecodeComponent>
510 CreateTimecodeTrack(OP1aHeader& Header, PackageT& Package,
511 const MXF::Rational& tc_edit_rate, ui32_t tc_frame_rate, ui64_t TCStart, const Dictionary*& Dict)
514 UL TCUL(Dict->ul(MDD_TimecodeDataDef));
516 TrackSet<TimecodeComponent> NewTrack =
517 CreateTrackAndSequence<PackageT, TimecodeComponent>(Header, Package, "Timecode Track",
518 tc_edit_rate, TCUL, 1, Dict);
520 NewTrack.Clip = new TimecodeComponent(Dict);
521 Header.AddChildObject(NewTrack.Clip);
522 NewTrack.Sequence->StructuralComponents.push_back(NewTrack.Clip->InstanceUID);
523 NewTrack.Clip->RoundedTimecodeBase = tc_frame_rate;
524 NewTrack.Clip->StartTimecode = TCStart;
525 NewTrack.Clip->DataDefinition = TCUL;
531 // state machine for mxf writer
533 ST_BEGIN, // waiting for Open()
534 ST_INIT, // waiting for SetSourceStream()
535 ST_READY, // ready to write frames
536 ST_RUNNING, // one or more frames written
537 ST_FINAL, // index written, file closed
541 // implementation of h__WriterState class Goto_* methods
542 #define Goto_body(s1,s2) \
543 if ( m_State != (s1) ) { \
544 return RESULT_STATE; \
551 ASDCP_NO_COPY_CONSTRUCT(h__WriterState);
554 WriterState_t m_State;
555 h__WriterState() : m_State(ST_BEGIN) {}
558 inline bool Test_BEGIN() { return m_State == ST_BEGIN; }
559 inline bool Test_INIT() { return m_State == ST_INIT; }
560 inline bool Test_READY() { return m_State == ST_READY;}
561 inline bool Test_RUNNING() { return m_State == ST_RUNNING; }
562 inline bool Test_FINAL() { return m_State == ST_FINAL; }
563 inline Result_t Goto_INIT() { Goto_body(ST_BEGIN, ST_INIT); }
564 inline Result_t Goto_READY() { Goto_body(ST_INIT, ST_READY); }
565 inline Result_t Goto_RUNNING() { Goto_body(ST_READY, ST_RUNNING); }
566 inline Result_t Goto_FINAL() { Goto_body(ST_RUNNING, ST_FINAL); }
569 //------------------------------------------------------------------------------------------
573 template <class HeaderType>
574 class TrackFileWriter
576 KM_NO_COPY_CONSTRUCT(TrackFileWriter);
580 const Dictionary* m_Dict;
581 Kumu::FileWriter m_File;
583 HeaderType m_HeaderPart;
586 MaterialPackage* m_MaterialPackage;
587 SourcePackage* m_FilePackage;
588 ContentStorage* m_ContentStorage;
590 FileDescriptor* m_EssenceDescriptor;
591 std::list<InterchangeObject*> m_EssenceSubDescriptorList;
593 ui32_t m_FramesWritten;
594 ui64_t m_StreamOffset;
595 ASDCP::FrameBuffer m_CtFrameBuf;
596 h__WriterState m_State;
599 typedef std::list<ui64_t*> DurationElementList_t;
600 DurationElementList_t m_DurationUpdateList;
602 TrackFileWriter(const Dictionary& d) :
603 m_Dict(&d), m_HeaderSize(0), m_HeaderPart(m_Dict), m_RIP(m_Dict),
604 m_MaterialPackage(0), m_FilePackage(0), m_ContentStorage(0),
605 m_EssenceDescriptor(0), m_FramesWritten(0), m_StreamOffset(0)
607 default_md_object_init();
610 virtual ~TrackFileWriter() {
614 const MXF::RIP& GetRIP() const { return m_RIP; }
616 void InitHeader(const MXFVersion& mxf_ver)
619 assert(m_EssenceDescriptor);
621 m_HeaderPart.m_Primer.ClearTagList();
622 m_HeaderPart.m_Preface = new Preface(m_Dict);
623 m_HeaderPart.AddChildObject(m_HeaderPart.m_Preface);
625 // Set the Operational Pattern label -- we're just starting and have no RIP or index,
626 // so we tell the world by using OP1a
627 m_HeaderPart.m_Preface->OperationalPattern = UL(m_Dict->ul(MDD_OP1a));
628 m_HeaderPart.OperationalPattern = m_HeaderPart.m_Preface->OperationalPattern;
630 if ( mxf_ver == MXFVersion_2004 )
632 m_HeaderPart.MinorVersion = MXF_2004_MinorVersion;
633 m_HeaderPart.m_Preface->Version = ((MXF_ObjectModelVersion < 8) | MXF_2004_MinorVersion);
634 m_HeaderPart.m_Preface->ObjectModelVersion = MXF_ObjectModelVersion;
638 assert(mxf_ver == MXFVersion_2011);
639 m_HeaderPart.MinorVersion = MXF_2011_MinorVersion;
640 m_HeaderPart.m_Preface->Version = ((MXF_ObjectModelVersion < 8) | MXF_2011_MinorVersion);
641 m_HeaderPart.m_Preface->ObjectModelVersion = MXF_ObjectModelVersion;
645 Identification* Ident = new Identification(m_Dict);
646 m_HeaderPart.AddChildObject(Ident);
647 m_HeaderPart.m_Preface->Identifications.push_back(Ident->InstanceUID);
649 Kumu::GenRandomValue(Ident->ThisGenerationUID);
650 Ident->CompanyName = m_Info.CompanyName.c_str();
651 Ident->ProductName = m_Info.ProductName.c_str();
652 Ident->VersionString = m_Info.ProductVersion.c_str();
653 Ident->ProductUID.Set(m_Info.ProductUUID);
654 Ident->Platform = ASDCP_PLATFORM;
656 std::vector<int> version = version_split(Version());
658 Ident->ToolkitVersion.Major = version[0];
659 Ident->ToolkitVersion.Minor = version[1];
660 Ident->ToolkitVersion.Patch = version[2];
661 Ident->ToolkitVersion.Build = ASDCP_BUILD_NUMBER;
662 Ident->ToolkitVersion.Release = VersionType::RL_RELEASE;
666 void AddSourceClip(const MXF::Rational& clip_edit_rate,
667 const MXF::Rational& tc_edit_rate, ui32_t TCFrameRate,
668 const std::string& TrackName, const UL& EssenceUL,
669 const UL& DataDefinition, const std::string& PackageLabel)
671 if ( m_ContentStorage == 0 )
673 m_ContentStorage = new ContentStorage(m_Dict);
674 m_HeaderPart.AddChildObject(m_ContentStorage);
675 m_HeaderPart.m_Preface->ContentStorage = m_ContentStorage->InstanceUID;
678 EssenceContainerData* ECD = new EssenceContainerData(m_Dict);
679 m_HeaderPart.AddChildObject(ECD);
680 m_ContentStorage->EssenceContainerData.push_back(ECD->InstanceUID);
684 UUID assetUUID(m_Info.AssetUUID);
685 UMID SourcePackageUMID, MaterialPackageUMID;
686 SourcePackageUMID.MakeUMID(0x0f, assetUUID);
687 MaterialPackageUMID.MakeUMID(0x0f); // unidentified essence
692 m_MaterialPackage = new MaterialPackage(m_Dict);
693 m_MaterialPackage->Name = "Material Package";
694 m_MaterialPackage->PackageUID = MaterialPackageUMID;
695 m_HeaderPart.AddChildObject(m_MaterialPackage);
696 m_ContentStorage->Packages.push_back(m_MaterialPackage->InstanceUID);
698 TrackSet<TimecodeComponent> MPTCTrack =
699 CreateTimecodeTrack<MaterialPackage>(m_HeaderPart, *m_MaterialPackage,
700 tc_edit_rate, TCFrameRate, 0, m_Dict);
702 MPTCTrack.Sequence->Duration.set_has_value();
703 m_DurationUpdateList.push_back(&(MPTCTrack.Sequence->Duration.get()));
704 MPTCTrack.Clip->Duration.set_has_value();
705 m_DurationUpdateList.push_back(&(MPTCTrack.Clip->Duration.get()));
707 TrackSet<SourceClip> MPTrack =
708 CreateTrackAndSequence<MaterialPackage, SourceClip>(m_HeaderPart, *m_MaterialPackage,
709 TrackName, clip_edit_rate, DataDefinition,
711 MPTrack.Sequence->Duration.set_has_value();
712 m_DurationUpdateList.push_back(&(MPTrack.Sequence->Duration.get()));
714 MPTrack.Clip = new SourceClip(m_Dict);
715 m_HeaderPart.AddChildObject(MPTrack.Clip);
716 MPTrack.Sequence->StructuralComponents.push_back(MPTrack.Clip->InstanceUID);
717 MPTrack.Clip->DataDefinition = DataDefinition;
718 MPTrack.Clip->SourcePackageID = SourcePackageUMID;
719 MPTrack.Clip->SourceTrackID = 2;
721 MPTrack.Clip->Duration.set_has_value();
722 m_DurationUpdateList.push_back(&(MPTrack.Clip->Duration.get()));
726 // File (Source) Package
728 m_FilePackage = new SourcePackage(m_Dict);
729 m_FilePackage->Name = PackageLabel.c_str();
730 m_FilePackage->PackageUID = SourcePackageUMID;
731 ECD->LinkedPackageUID = SourcePackageUMID;
733 m_HeaderPart.AddChildObject(m_FilePackage);
734 m_ContentStorage->Packages.push_back(m_FilePackage->InstanceUID);
736 TrackSet<TimecodeComponent> FPTCTrack =
737 CreateTimecodeTrack<SourcePackage>(m_HeaderPart, *m_FilePackage,
738 tc_edit_rate, TCFrameRate, 0, m_Dict);
740 FPTCTrack.Sequence->Duration.set_has_value();
741 m_DurationUpdateList.push_back(&(FPTCTrack.Sequence->Duration.get()));
742 FPTCTrack.Clip->Duration.set_has_value();
743 m_DurationUpdateList.push_back(&(FPTCTrack.Clip->Duration.get()));
745 TrackSet<SourceClip> FPTrack =
746 CreateTrackAndSequence<SourcePackage, SourceClip>(m_HeaderPart, *m_FilePackage,
747 TrackName, clip_edit_rate, DataDefinition,
750 FPTrack.Sequence->Duration.set_has_value();
751 m_DurationUpdateList.push_back(&(FPTrack.Sequence->Duration.get()));
753 // Consult ST 379:2004 Sec. 6.3, "Element to track relationship" to see where "12" comes from.
754 FPTrack.Track->TrackNumber = KM_i32_BE(Kumu::cp2i<ui32_t>((EssenceUL.Value() + 12)));
756 FPTrack.Clip = new SourceClip(m_Dict);
757 m_HeaderPart.AddChildObject(FPTrack.Clip);
758 FPTrack.Sequence->StructuralComponents.push_back(FPTrack.Clip->InstanceUID);
759 FPTrack.Clip->DataDefinition = DataDefinition;
761 // for now we do not allow setting this value, so all files will be 'original'
762 FPTrack.Clip->SourceTrackID = 0;
763 FPTrack.Clip->SourcePackageID = NilUMID;
765 FPTrack.Clip->Duration.set_has_value();
766 m_DurationUpdateList.push_back(&(FPTrack.Clip->Duration.get()));
768 m_EssenceDescriptor->LinkedTrackID = FPTrack.Track->TrackID;
772 void AddEssenceDescriptor(const UL& WrappingUL)
775 // Essence Descriptor
777 m_EssenceDescriptor->EssenceContainer = WrappingUL;
778 m_HeaderPart.m_Preface->PrimaryPackage = m_FilePackage->InstanceUID;
781 // Essence Descriptors
784 UL GenericContainerUL(m_Dict->ul(MDD_GCMulti));
785 m_HeaderPart.EssenceContainers.push_back(GenericContainerUL);
787 if ( m_Info.EncryptedEssence )
789 UL CryptEssenceUL(m_Dict->ul(MDD_EncryptedContainerLabel));
790 m_HeaderPart.EssenceContainers.push_back(CryptEssenceUL);
791 m_HeaderPart.m_Preface->DMSchemes.push_back(UL(m_Dict->ul(MDD_CryptographicFrameworkLabel)));
792 AddDmsCrypt(m_HeaderPart, *m_FilePackage, m_Info, WrappingUL, m_Dict);
793 //// TODO: fix DMSegment Duration value
797 m_HeaderPart.EssenceContainers.push_back(WrappingUL);
800 m_HeaderPart.m_Preface->EssenceContainers = m_HeaderPart.EssenceContainers;
801 m_HeaderPart.AddChildObject(m_EssenceDescriptor);
803 std::list<InterchangeObject*>::iterator sdli = m_EssenceSubDescriptorList.begin();
804 for ( ; sdli != m_EssenceSubDescriptorList.end(); sdli++ )
805 m_HeaderPart.AddChildObject(*sdli);
807 m_FilePackage->Descriptor = m_EssenceDescriptor->InstanceUID;
810 Result_t AddDmsGenericPartUtf8Text(const ASDCP::FrameBuffer& frame_buffer,
811 ASDCP::AESEncContext* enc = 0, ASDCP::HMACContext* hmac = 0)
813 Kumu::fpos_t previous_partition_offset = m_RIP.PairArray.back().ByteOffset;
814 Result_t result = AddDmsTrackGenericPartUtf8Text(m_File, m_HeaderPart, *m_FilePackage, m_RIP, m_Dict);
816 if ( KM_SUCCESS(result) )
818 // m_RIP now contains an entry (at the back) for the new generic stream
819 // (this entry was created during the call to AddDmsTrackGenericPartUtf8Text())
820 if ( m_File.Tell() != m_RIP.PairArray.back().ByteOffset )
822 DefaultLogSink().Error("File offset has moved since RIP modification. Unrecoverable error.\n");
826 // create generic stream partition header
827 static UL GenericStream_DataElement(m_Dict->ul(MDD_GenericStream_DataElement));
828 ASDCP::MXF::Partition GSPart(m_Dict);
830 GSPart.MajorVersion = m_HeaderPart.MajorVersion;
831 GSPart.MinorVersion = m_HeaderPart.MinorVersion;
832 GSPart.ThisPartition = m_RIP.PairArray.back().ByteOffset;
833 GSPart.PreviousPartition = previous_partition_offset;
834 GSPart.OperationalPattern = m_HeaderPart.OperationalPattern;
835 GSPart.BodySID = m_RIP.PairArray.back().BodySID;
836 GSPart.EssenceContainers = m_HeaderPart.EssenceContainers;
838 static UL gs_part_ul(m_Dict->ul(MDD_GenericStreamPartition));
839 Result_t result = GSPart.WriteToFile(m_File, gs_part_ul);
841 if ( KM_SUCCESS(result) )
843 result = Write_EKLV_Packet(m_File, *m_Dict, m_HeaderPart, m_Info, m_CtFrameBuf, m_FramesWritten,
844 m_StreamOffset, frame_buffer, GenericStream_DataElement.Value(), enc, hmac);
861 //------------------------------------------------------------------------------------------
865 class h__ASDCPReader : public MXF::TrackFileReader<OP1aHeader, OPAtomIndexFooter>
867 ASDCP_NO_COPY_CONSTRUCT(h__ASDCPReader);
871 Partition m_BodyPart;
873 h__ASDCPReader(const Dictionary&);
874 virtual ~h__ASDCPReader();
876 Result_t OpenMXFRead(const std::string& filename);
877 Result_t ReadEKLVFrame(ui32_t FrameNum, ASDCP::FrameBuffer& FrameBuf,
878 const byte_t* EssenceUL, AESDecContext* Ctx, HMACContext* HMAC);
879 Result_t LocateFrame(ui32_t FrameNum, Kumu::fpos_t& streamOffset,
880 i8_t& temporalOffset, i8_t& keyFrameOffset);
884 class h__ASDCPWriter : public MXF::TrackFileWriter<OP1aHeader>
886 ASDCP_NO_COPY_CONSTRUCT(h__ASDCPWriter);
890 Partition m_BodyPart;
891 OPAtomIndexFooter m_FooterPart;
893 h__ASDCPWriter(const Dictionary&);
894 virtual ~h__ASDCPWriter();
896 // all the above for a single source clip
897 Result_t WriteASDCPHeader(const std::string& PackageLabel, const UL& WrappingUL,
898 const std::string& TrackName, const UL& EssenceUL,
899 const UL& DataDefinition, const MXF::Rational& EditRate,
900 ui32_t TCFrameRate, ui32_t BytesPerEditUnit = 0);
902 Result_t CreateBodyPart(const MXF::Rational& EditRate, ui32_t BytesPerEditUnit = 0);
903 Result_t WriteEKLVPacket(const ASDCP::FrameBuffer& FrameBuf,const byte_t* EssenceUL,
904 AESEncContext* Ctx, HMACContext* HMAC);
905 Result_t WriteASDCPFooter();
909 // helper class for calculating Integrity Packs, used by WriteEKLVPacket() below.
914 byte_t Data[klv_intpack_size];
917 memset(Data, 0, klv_intpack_size);
922 Result_t CalcValues(const ASDCP::FrameBuffer&, const byte_t* AssetID, ui32_t sequence, HMACContext* HMAC);
923 Result_t TestValues(const ASDCP::FrameBuffer&, const byte_t* AssetID, ui32_t sequence, HMACContext* HMAC);
929 #endif // _AS_DCP_INTERNAL_H_
933 // end AS_DCP_internal.h