451796654be01e5e0acd649aed43549015cdbf0e
[asdcplib.git] / src / h__Writer.cpp
1 /*
2 Copyright (c) 2004-2008, John Hurst
3 All rights reserved.
4
5 Redistribution and use in source and binary forms, with or without
6 modification, are permitted provided that the following conditions
7 are met:
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.
15
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.
26 */
27 /*! \file    h__Writer.cpp
28     \version $Id$
29     \brief   MXF file writer base class
30 */
31
32 #include "AS_DCP_internal.h"
33 #include "KLV.h"
34
35 using namespace ASDCP;
36 using namespace ASDCP::MXF;
37
38 // a magic number identifying asdcplib
39 #ifndef ASDCP_BUILD_NUMBER
40 #define ASDCP_BUILD_NUMBER 0x6A68
41 #endif
42
43
44
45 //
46 ASDCP::h__Writer::h__Writer() :
47   m_HeaderSize(0), m_EssenceStart(0),
48   m_EssenceDescriptor(0), m_FramesWritten(0), m_StreamOffset(0)
49 {
50 }
51
52 ASDCP::h__Writer::~h__Writer()
53 {
54 }
55
56 //
57 // add DMS CryptographicFramework entry to source package
58 void
59 AddDMScrypt(Partition& HeaderPart, SourcePackage& Package, WriterInfo& Descr, const UL& WrappingUL)
60 {
61   // Essence Track
62   StaticTrack* NewTrack = new StaticTrack;
63   HeaderPart.AddChildObject(NewTrack);
64   Package.Tracks.push_back(NewTrack->InstanceUID);
65   NewTrack->TrackName = "Descriptive Track";
66   NewTrack->TrackID = 3;
67
68   Sequence* Seq = new Sequence;
69   HeaderPart.AddChildObject(Seq);
70   NewTrack->Sequence = Seq->InstanceUID;
71   Seq->DataDefinition = UL(Dict::ul(MDD_DescriptiveMetaDataDef));
72
73   DMSegment* Segment = new DMSegment;
74   HeaderPart.AddChildObject(Segment);
75   Seq->StructuralComponents.push_back(Segment->InstanceUID);
76   Segment->EventComment = "AS-DCP KLV Encryption";
77   
78   CryptographicFramework* CFW = new CryptographicFramework;
79   HeaderPart.AddChildObject(CFW);
80   Segment->DMFramework = CFW->InstanceUID;
81
82   CryptographicContext* Context = new CryptographicContext;
83   HeaderPart.AddChildObject(Context);
84   CFW->ContextSR = Context->InstanceUID;
85
86   Context->ContextID.Set(Descr.ContextID);
87   Context->SourceEssenceContainer = WrappingUL; // ??????
88   Context->CipherAlgorithm.Set(Dict::ul(MDD_CipherAlgorithm_AES));
89   Context->MICAlgorithm.Set( Descr.UsesHMAC ? Dict::ul(MDD_MICAlgorithm_HMAC_SHA1) : Dict::ul(MDD_MICAlgorithm_NONE) );
90   Context->CryptographicKeyID.Set(Descr.CryptographicKeyID);
91 }
92
93 //
94 void
95 ASDCP::h__Writer::InitHeader()
96 {
97   assert(m_EssenceDescriptor);
98
99   m_HeaderPart.m_Primer.ClearTagList();
100   m_HeaderPart.m_Preface = new Preface;
101   m_HeaderPart.AddChildObject(m_HeaderPart.m_Preface);
102
103   // Set the Operational Pattern label -- we're just starting and have no RIP or index,
104   // so we tell the world by using OP1a
105   m_HeaderPart.m_Preface->OperationalPattern = UL(Dict::ul(MDD_OP1a));
106   m_HeaderPart.OperationalPattern = m_HeaderPart.m_Preface->OperationalPattern;
107
108   // First RIP Entry
109   if ( m_Info.LabelSetType == LS_MXF_SMPTE )
110     m_HeaderPart.m_RIP.PairArray.push_back(RIP::Pair(0, 0)); // 3-part, no essence in header
111   else
112     m_HeaderPart.m_RIP.PairArray.push_back(RIP::Pair(1, 0)); // 2-part, essence in header
113
114   //
115   // Identification
116   //
117   Identification* Ident = new Identification;
118   m_HeaderPart.AddChildObject(Ident);
119   m_HeaderPart.m_Preface->Identifications.push_back(Ident->InstanceUID);
120
121   Kumu::GenRandomValue(Ident->ThisGenerationUID);
122   Ident->CompanyName = m_Info.CompanyName.c_str();
123   Ident->ProductName = m_Info.ProductName.c_str();
124   Ident->VersionString = m_Info.ProductVersion.c_str();
125   Ident->ProductUID.Set(m_Info.ProductUUID);
126   Ident->Platform = ASDCP_PLATFORM;
127   Ident->ToolkitVersion.Major = VERSION_MAJOR;
128   Ident->ToolkitVersion.Minor = VERSION_APIMINOR;
129   Ident->ToolkitVersion.Patch = VERSION_IMPMINOR;
130   Ident->ToolkitVersion.Build = ASDCP_BUILD_NUMBER;
131   Ident->ToolkitVersion.Release = VersionType::RL_RELEASE;
132 }
133
134 //
135 template <class ClipT>
136 struct TrackSet
137 {
138   MXF::Track*    Track;
139   MXF::Sequence* Sequence;
140   ClipT*         Clip;
141
142   TrackSet() : Track(0), Sequence(0), Clip(0) {}
143 };
144
145 //
146 template <class PackageT, class ClipT>
147 TrackSet<ClipT>
148 CreateTrackAndSequence(OPAtomHeader& Header, PackageT& Package, const std::string TrackName,
149                        const MXF::Rational& EditRate, const UL& Definition, ui32_t TrackID)
150 {
151   TrackSet<ClipT> NewTrack;
152
153   NewTrack.Track = new Track;
154   Header.AddChildObject(NewTrack.Track);
155   NewTrack.Track->EditRate = EditRate;
156   Package.Tracks.push_back(NewTrack.Track->InstanceUID);
157   NewTrack.Track->TrackID = TrackID;
158   NewTrack.Track->TrackName = TrackName.c_str();
159
160   NewTrack.Sequence = new Sequence;
161   Header.AddChildObject(NewTrack.Sequence);
162   NewTrack.Track->Sequence = NewTrack.Sequence->InstanceUID;
163   NewTrack.Sequence->DataDefinition = Definition;
164
165   return NewTrack;
166 }
167
168 //
169 template <class PackageT>
170 TrackSet<TimecodeComponent>
171 CreateTimecodeTrack(OPAtomHeader& Header, PackageT& Package,
172                     const MXF::Rational& EditRate, ui32_t TCFrameRate, ui64_t TCStart)
173 {
174   UL TCUL(Dict::ul(MDD_TimecodeDataDef));
175
176   TrackSet<TimecodeComponent> NewTrack = CreateTrackAndSequence<PackageT, TimecodeComponent>(Header, Package, "Timecode Track", EditRate, TCUL, 1);
177
178   NewTrack.Clip = new TimecodeComponent;
179   Header.AddChildObject(NewTrack.Clip);
180   NewTrack.Sequence->StructuralComponents.push_back(NewTrack.Clip->InstanceUID);
181   NewTrack.Clip->RoundedTimecodeBase = TCFrameRate;
182   NewTrack.Clip->StartTimecode = TCStart;
183   NewTrack.Clip->DataDefinition = TCUL;
184
185   return NewTrack;
186 }
187
188
189 //
190 void
191 ASDCP::h__Writer::AddSourceClip(const MXF::Rational& EditRate, ui32_t TCFrameRate,
192                                 const std::string& TrackName, const UL& DataDefinition,
193                                 const std::string& PackageLabel)
194 {
195   //
196   ContentStorage* Storage = new ContentStorage;
197   m_HeaderPart.AddChildObject(Storage);
198   m_HeaderPart.m_Preface->ContentStorage = Storage->InstanceUID;
199
200   EssenceContainerData* ECD = new EssenceContainerData;
201   m_HeaderPart.AddChildObject(ECD);
202   Storage->EssenceContainerData.push_back(ECD->InstanceUID);
203   ECD->IndexSID = 129;
204   ECD->BodySID = 1;
205
206   UUID assetUUID(m_Info.AssetUUID);
207   UMID SourcePackageUMID, MaterialPackageUMID;
208   SourcePackageUMID.MakeUMID(0x0f, assetUUID);
209   MaterialPackageUMID.MakeUMID(0x0f); // unidentified essence
210
211   //
212   // Material Package
213   //
214   m_MaterialPackage = new MaterialPackage;
215   m_MaterialPackage->Name = "AS-DCP Material Package";
216   m_MaterialPackage->PackageUID = MaterialPackageUMID;
217   m_HeaderPart.AddChildObject(m_MaterialPackage);
218   Storage->Packages.push_back(m_MaterialPackage->InstanceUID);
219
220   TrackSet<TimecodeComponent> MPTCTrack = CreateTimecodeTrack<MaterialPackage>(m_HeaderPart, *m_MaterialPackage,
221                                                                                EditRate, TCFrameRate, 0);
222   m_DurationUpdateList.push_back(&(MPTCTrack.Sequence->Duration));
223   m_DurationUpdateList.push_back(&(MPTCTrack.Clip->Duration));
224
225   TrackSet<SourceClip> MPTrack = CreateTrackAndSequence<MaterialPackage, SourceClip>(m_HeaderPart, *m_MaterialPackage,
226                                                                            TrackName, EditRate, DataDefinition, 2);
227   m_DurationUpdateList.push_back(&(MPTrack.Sequence->Duration));
228
229   MPTrack.Clip = new SourceClip;
230   m_HeaderPart.AddChildObject(MPTrack.Clip);
231   MPTrack.Sequence->StructuralComponents.push_back(MPTrack.Clip->InstanceUID);
232   MPTrack.Clip->DataDefinition = DataDefinition;
233   MPTrack.Clip->SourcePackageID = SourcePackageUMID;
234   MPTrack.Clip->SourceTrackID = 2;
235   m_DurationUpdateList.push_back(&(MPTrack.Clip->Duration));
236
237   
238   //
239   // File (Source) Package
240   //
241   m_FilePackage = new SourcePackage;
242   m_FilePackage->Name = PackageLabel.c_str();
243   m_FilePackage->PackageUID = SourcePackageUMID;
244   ECD->LinkedPackageUID = SourcePackageUMID;
245
246   m_HeaderPart.AddChildObject(m_FilePackage);
247   Storage->Packages.push_back(m_FilePackage->InstanceUID);
248
249   TrackSet<TimecodeComponent> FPTCTrack = CreateTimecodeTrack<SourcePackage>(m_HeaderPart, *m_FilePackage,
250                                                                              EditRate, TCFrameRate, ui64_C(3600) * TCFrameRate);
251   m_DurationUpdateList.push_back(&(FPTCTrack.Sequence->Duration));
252   m_DurationUpdateList.push_back(&(FPTCTrack.Clip->Duration));
253
254   TrackSet<SourceClip> FPTrack = CreateTrackAndSequence<SourcePackage, SourceClip>(m_HeaderPart, *m_FilePackage,
255                                                                          TrackName, EditRate, DataDefinition, 2);
256   m_DurationUpdateList.push_back(&(FPTrack.Sequence->Duration));
257
258   FPTrack.Clip = new SourceClip;
259   m_HeaderPart.AddChildObject(FPTrack.Clip);
260   FPTrack.Sequence->StructuralComponents.push_back(FPTrack.Clip->InstanceUID);
261   FPTrack.Clip->DataDefinition = DataDefinition;
262
263   // for now we do not allow setting this value, so all files will be 'original'
264   FPTrack.Clip->SourceTrackID = 0;
265   FPTrack.Clip->SourcePackageID = NilUMID;
266   m_DurationUpdateList.push_back(&(FPTrack.Clip->Duration));
267
268   m_EssenceDescriptor->LinkedTrackID = FPTrack.Track->TrackID;
269 }
270
271 //
272 void
273 ASDCP::h__Writer::AddDMSegment(const MXF::Rational& EditRate, ui32_t TCFrameRate,
274                                 const std::string& TrackName, const UL& DataDefinition,
275                                const std::string& PackageLabel)
276 {
277   //
278   ContentStorage* Storage = new ContentStorage;
279   m_HeaderPart.AddChildObject(Storage);
280   m_HeaderPart.m_Preface->ContentStorage = Storage->InstanceUID;
281
282   EssenceContainerData* ECD = new EssenceContainerData;
283   m_HeaderPart.AddChildObject(ECD);
284   Storage->EssenceContainerData.push_back(ECD->InstanceUID);
285   ECD->IndexSID = 129;
286   ECD->BodySID = 1;
287
288   UUID assetUUID(m_Info.AssetUUID);
289   UMID SourcePackageUMID, MaterialPackageUMID;
290   SourcePackageUMID.MakeUMID(0x0f, assetUUID);
291   MaterialPackageUMID.MakeUMID(0x0f); // unidentified essence
292
293   //
294   // Material Package
295   //
296   m_MaterialPackage = new MaterialPackage;
297   m_MaterialPackage->Name = "AS-DCP Material Package";
298   m_MaterialPackage->PackageUID = MaterialPackageUMID;
299   m_HeaderPart.AddChildObject(m_MaterialPackage);
300   Storage->Packages.push_back(m_MaterialPackage->InstanceUID);
301
302   TrackSet<TimecodeComponent> MPTCTrack = CreateTimecodeTrack<MaterialPackage>(m_HeaderPart, *m_MaterialPackage,
303                                                                                EditRate, TCFrameRate, 0);
304   m_DurationUpdateList.push_back(&(MPTCTrack.Sequence->Duration));
305   m_DurationUpdateList.push_back(&(MPTCTrack.Clip->Duration));
306
307   TrackSet<DMSegment> MPTrack = CreateTrackAndSequence<MaterialPackage, DMSegment>(m_HeaderPart, *m_MaterialPackage,
308                                                                TrackName, EditRate, DataDefinition, 2);
309   m_DurationUpdateList.push_back(&(MPTrack.Sequence->Duration));
310
311   MPTrack.Clip = new DMSegment;
312   m_HeaderPart.AddChildObject(MPTrack.Clip);
313   MPTrack.Sequence->StructuralComponents.push_back(MPTrack.Clip->InstanceUID);
314   MPTrack.Clip->DataDefinition = DataDefinition;
315   //  MPTrack.Clip->SourcePackageID = SourcePackageUMID;
316   //  MPTrack.Clip->SourceTrackID = 2;
317   m_DurationUpdateList.push_back(&(MPTrack.Clip->Duration));
318
319   
320   //
321   // File (Source) Package
322   //
323   m_FilePackage = new SourcePackage;
324   m_FilePackage->Name = PackageLabel.c_str();
325   m_FilePackage->PackageUID = SourcePackageUMID;
326   ECD->LinkedPackageUID = SourcePackageUMID;
327
328   m_HeaderPart.AddChildObject(m_FilePackage);
329   Storage->Packages.push_back(m_FilePackage->InstanceUID);
330
331   TrackSet<TimecodeComponent> FPTCTrack = CreateTimecodeTrack<SourcePackage>(m_HeaderPart, *m_FilePackage,
332                                                                              EditRate, TCFrameRate, ui64_C(3600) * TCFrameRate);
333   m_DurationUpdateList.push_back(&(FPTCTrack.Sequence->Duration));
334   m_DurationUpdateList.push_back(&(FPTCTrack.Clip->Duration));
335
336   TrackSet<DMSegment> FPTrack = CreateTrackAndSequence<SourcePackage, DMSegment>(m_HeaderPart, *m_FilePackage,
337                                                                                  TrackName, EditRate, DataDefinition, 2);
338   m_DurationUpdateList.push_back(&(FPTrack.Sequence->Duration));
339
340   FPTrack.Clip = new DMSegment;
341   m_HeaderPart.AddChildObject(FPTrack.Clip);
342   FPTrack.Sequence->StructuralComponents.push_back(FPTrack.Clip->InstanceUID);
343   FPTrack.Clip->DataDefinition = DataDefinition;
344   FPTrack.Clip->EventComment = "D-Cinema Timed Text";
345
346   m_DurationUpdateList.push_back(&(FPTrack.Clip->Duration));
347   m_EssenceDescriptor->LinkedTrackID = FPTrack.Track->TrackID;
348 }
349
350 //
351 void
352 ASDCP::h__Writer::AddEssenceDescriptor(const UL& WrappingUL)
353 {
354   //
355   // Essence Descriptor
356   //
357   m_EssenceDescriptor->EssenceContainer = WrappingUL;
358   m_HeaderPart.m_Preface->PrimaryPackage = m_FilePackage->InstanceUID;
359
360   //
361   // Essence Descriptors
362   //
363   UL GenericContainerUL(Dict::ul(MDD_GCMulti));
364   m_HeaderPart.EssenceContainers.push_back(GenericContainerUL);
365
366   if ( m_Info.EncryptedEssence )
367     {
368       UL CryptEssenceUL(Dict::ul(MDD_EncryptedContainerLabel));
369       m_HeaderPart.EssenceContainers.push_back(CryptEssenceUL);
370       m_HeaderPart.m_Preface->DMSchemes.push_back(UL(Dict::ul(MDD_CryptographicFrameworkLabel)));
371       AddDMScrypt(m_HeaderPart, *m_FilePackage, m_Info, WrappingUL);
372     }
373   else
374     {
375       m_HeaderPart.EssenceContainers.push_back(WrappingUL);
376     }
377
378   m_HeaderPart.m_Preface->EssenceContainers = m_HeaderPart.EssenceContainers;
379   m_HeaderPart.AddChildObject(m_EssenceDescriptor);
380
381   std::list<InterchangeObject*>::iterator sdli = m_EssenceSubDescriptorList.begin();
382   for ( ; sdli != m_EssenceSubDescriptorList.end(); sdli++ )
383       m_HeaderPart.AddChildObject(*sdli);
384
385   m_FilePackage->Descriptor = m_EssenceDescriptor->InstanceUID;
386 }
387
388 //
389 Result_t
390 ASDCP::h__Writer::CreateBodyPart(const MXF::Rational& EditRate, ui32_t BytesPerEditUnit)
391 {
392   Result_t result = RESULT_OK;
393
394   // create a body partition if we're writing proper 429-3/OP-Atom
395   if ( m_Info.LabelSetType == LS_MXF_SMPTE )
396     {
397       // Body Partition
398       m_BodyPart.EssenceContainers = m_HeaderPart.EssenceContainers;
399       m_BodyPart.ThisPartition = m_File.Tell();
400       m_BodyPart.BodySID = 1;
401       UL OPAtomUL(Dict::ul(MDD_OPAtom));
402       m_BodyPart.OperationalPattern = OPAtomUL;
403       m_HeaderPart.m_RIP.PairArray.push_back(RIP::Pair(1, m_BodyPart.ThisPartition)); // Second RIP Entry
404       
405       UL BodyUL(Dict::ul(MDD_ClosedCompleteBodyPartition));
406       result = m_BodyPart.WriteToFile(m_File, BodyUL);
407     }
408   else
409     {
410       m_HeaderPart.BodySID = 1;
411     }
412
413   if ( ASDCP_SUCCESS(result) )
414     {
415       // Index setup
416       Kumu::fpos_t ECoffset = m_File.Tell();
417       m_FooterPart.IndexSID = 129;
418
419       if ( BytesPerEditUnit == 0 )
420         m_FooterPart.SetIndexParamsVBR(&m_HeaderPart.m_Primer, EditRate, ECoffset);
421       else
422         m_FooterPart.SetIndexParamsCBR(&m_HeaderPart.m_Primer, BytesPerEditUnit, EditRate);
423     }
424
425   return result;
426 }
427
428 //
429 Result_t
430 ASDCP::h__Writer::WriteMXFHeader(const std::string& PackageLabel, const UL& WrappingUL,
431                                  const std::string& TrackName, const UL& DataDefinition,
432                                  const MXF::Rational& EditRate, ui32_t TCFrameRate, ui32_t BytesPerEditUnit)
433 {
434   InitHeader();
435   AddSourceClip(EditRate, TCFrameRate, TrackName, DataDefinition, PackageLabel);
436   AddEssenceDescriptor(WrappingUL);
437
438   Result_t result = m_HeaderPart.WriteToFile(m_File, m_HeaderSize);
439
440   if ( KM_SUCCESS(result) )
441     result = CreateBodyPart(EditRate, BytesPerEditUnit);
442
443   return result;
444 }
445
446
447 // standard method of writing a plaintext or encrypted frame
448 Result_t
449 ASDCP::h__Writer::WriteEKLVPacket(const ASDCP::FrameBuffer& FrameBuf, const byte_t* EssenceUL,
450                                   AESEncContext* Ctx, HMACContext* HMAC)
451 {
452   Result_t result = RESULT_OK;
453   IntegrityPack IntPack;
454
455   byte_t overhead[128];
456   Kumu::MemIOWriter Overhead(overhead, 128);
457
458   if ( FrameBuf.Size() == 0 )
459     {
460       DefaultLogSink().Error("Cannot write empty frame buffer\n");
461       return RESULT_EMPTY_FB;
462     }
463
464   if ( m_Info.EncryptedEssence )
465     {
466       if ( ! Ctx )
467         return RESULT_CRYPT_CTX;
468
469       if ( m_Info.UsesHMAC && ! HMAC )
470         return RESULT_HMAC_CTX;
471
472       if ( FrameBuf.PlaintextOffset() > FrameBuf.Size() )
473         return RESULT_LARGE_PTO;
474
475       // encrypt the essence data (create encrypted source value)
476       result = EncryptFrameBuffer(FrameBuf, m_CtFrameBuf, Ctx);
477
478       // create HMAC
479       if ( ASDCP_SUCCESS(result) && m_Info.UsesHMAC )
480         result = IntPack.CalcValues(m_CtFrameBuf, m_Info.AssetUUID, m_FramesWritten + 1, HMAC);
481
482       if ( ASDCP_SUCCESS(result) )
483         { // write UL
484           if ( m_Info.LabelSetType == LS_MXF_INTEROP )
485             Overhead.WriteRaw(Dict::ul(MDD_MXFInterop_CryptEssence), SMPTE_UL_LENGTH);
486           else
487             Overhead.WriteRaw(Dict::ul(MDD_CryptEssence), SMPTE_UL_LENGTH);
488
489           // construct encrypted triplet header
490           ui32_t ETLength = klv_cryptinfo_size + m_CtFrameBuf.Size();
491
492           if ( m_Info.UsesHMAC )
493             ETLength += klv_intpack_size;
494           else
495             ETLength += (MXF_BER_LENGTH * 3); // for empty intpack
496
497           Overhead.WriteBER(ETLength, MXF_BER_LENGTH);                  // write encrypted triplet length
498           Overhead.WriteBER(UUIDlen, MXF_BER_LENGTH);                   // write ContextID length
499           Overhead.WriteRaw(m_Info.ContextID, UUIDlen);                  // write ContextID
500           Overhead.WriteBER(sizeof(ui64_t), MXF_BER_LENGTH);            // write PlaintextOffset length
501           Overhead.WriteUi64BE(FrameBuf.PlaintextOffset());              // write PlaintextOffset
502           Overhead.WriteBER(SMPTE_UL_LENGTH, MXF_BER_LENGTH);              // write essence UL length
503           Overhead.WriteRaw((byte_t*)EssenceUL, SMPTE_UL_LENGTH);           // write the essence UL
504           Overhead.WriteBER(sizeof(ui64_t), MXF_BER_LENGTH);            // write SourceLength length
505           Overhead.WriteUi64BE(FrameBuf.Size());                         // write SourceLength
506           Overhead.WriteBER(m_CtFrameBuf.Size(), MXF_BER_LENGTH);       // write ESV length
507
508           result = m_File.Writev(Overhead.Data(), Overhead.Length());
509         }
510
511       if ( ASDCP_SUCCESS(result) )
512         {
513           m_StreamOffset += Overhead.Length();
514           // write encrypted source value
515           result = m_File.Writev((byte_t*)m_CtFrameBuf.RoData(), m_CtFrameBuf.Size());
516         }
517
518       if ( ASDCP_SUCCESS(result) )
519         {
520           m_StreamOffset += m_CtFrameBuf.Size();
521
522           byte_t hmoverhead[512];
523           Kumu::MemIOWriter HMACOverhead(hmoverhead, 512);
524
525           // write the HMAC
526           if ( m_Info.UsesHMAC )
527             {
528               HMACOverhead.WriteRaw(IntPack.Data, klv_intpack_size);
529             }
530           else
531             { // we still need the var-pack length values if the intpack is empty
532               for ( ui32_t i = 0; i < 3 ; i++ )
533                 HMACOverhead.WriteBER(0, MXF_BER_LENGTH);
534             }
535
536           // write HMAC
537           result = m_File.Writev(HMACOverhead.Data(), HMACOverhead.Length());
538           m_StreamOffset += HMACOverhead.Length();
539         }
540     }
541   else
542     {
543       Overhead.WriteRaw((byte_t*)EssenceUL, SMPTE_UL_LENGTH);
544       Overhead.WriteBER(FrameBuf.Size(), MXF_BER_LENGTH);
545       result = m_File.Writev(Overhead.Data(), Overhead.Length());
546  
547       if ( ASDCP_SUCCESS(result) )
548         result = m_File.Writev((byte_t*)FrameBuf.RoData(), FrameBuf.Size());
549
550       if ( ASDCP_SUCCESS(result) )
551         m_StreamOffset += Overhead.Length() + FrameBuf.Size();
552     }
553
554   if ( ASDCP_SUCCESS(result) )
555     result = m_File.Writev();
556
557   return result;
558 }
559
560
561 // standard method of writing the header and footer of a completed MXF file
562 //
563 Result_t
564 ASDCP::h__Writer::WriteMXFFooter()
565 {
566   // Set top-level file package correctly for OP-Atom
567
568   //  m_MPTCSequence->Duration = m_MPTimecode->Duration = m_MPClSequence->Duration = m_MPClip->Duration = 
569   //    m_FPTCSequence->Duration = m_FPTimecode->Duration = m_FPClSequence->Duration = m_FPClip->Duration = 
570
571   DurationElementList_t::iterator dli = m_DurationUpdateList.begin();
572
573   for (; dli != m_DurationUpdateList.end(); dli++ )
574     **dli = m_FramesWritten;
575
576   m_EssenceDescriptor->ContainerDuration = m_FramesWritten;
577   m_FooterPart.PreviousPartition = m_HeaderPart.m_RIP.PairArray.back().ByteOffset;
578
579   Kumu::fpos_t here = m_File.Tell();
580   m_HeaderPart.m_RIP.PairArray.push_back(RIP::Pair(0, here)); // Last RIP Entry
581   m_HeaderPart.FooterPartition = here;
582
583   // re-label the partition
584   UL OPAtomUL(Dict::ul(MDD_OPAtom));
585
586   if ( m_Info.LabelSetType == LS_MXF_INTEROP )
587     OPAtomUL.Set(Dict::ul(MDD_MXFInterop_OPAtom));
588   
589   m_HeaderPart.OperationalPattern = OPAtomUL;
590   m_HeaderPart.m_Preface->OperationalPattern = m_HeaderPart.OperationalPattern;
591
592   m_FooterPart.OperationalPattern = m_HeaderPart.OperationalPattern;
593   m_FooterPart.EssenceContainers = m_HeaderPart.EssenceContainers;
594   m_FooterPart.FooterPartition = here;
595   m_FooterPart.ThisPartition = here;
596
597   Result_t result = m_FooterPart.WriteToFile(m_File, m_FramesWritten);
598
599   if ( ASDCP_SUCCESS(result) )
600     result = m_HeaderPart.m_RIP.WriteToFile(m_File);
601
602   if ( ASDCP_SUCCESS(result) )
603     result = m_File.Seek(0);
604
605   if ( ASDCP_SUCCESS(result) )
606     result = m_HeaderPart.WriteToFile(m_File, m_HeaderSize);
607
608   m_File.Close();
609   return result;
610 }
611
612 //
613 // end h__Writer.cpp
614 //