Forgot a fix.
[asdcplib.git] / src / h__Writer.cpp
1 /*
2 Copyright (c) 2004-2007, 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   // Encryption Descriptor
362   //
363   if ( m_Info.EncryptedEssence )
364     {
365       UL CryptEssenceUL(Dict::ul(MDD_EncryptedContainerLabel));
366       m_HeaderPart.EssenceContainers.push_back(CryptEssenceUL);
367       m_HeaderPart.m_Preface->DMSchemes.push_back(UL(Dict::ul(MDD_CryptographicFrameworkLabel)));
368       AddDMScrypt(m_HeaderPart, *m_FilePackage, m_Info, WrappingUL);
369     }
370   else
371     {
372       m_HeaderPart.EssenceContainers.push_back(UL(Dict::ul(MDD_GCMulti)));
373       m_HeaderPart.EssenceContainers.push_back(WrappingUL);
374     }
375
376   m_HeaderPart.m_Preface->EssenceContainers = m_HeaderPart.EssenceContainers;
377   m_HeaderPart.AddChildObject(m_EssenceDescriptor);
378
379   std::list<InterchangeObject*>::iterator sdli = m_EssenceSubDescriptorList.begin();
380   for ( ; sdli != m_EssenceSubDescriptorList.end(); sdli++ )
381       m_HeaderPart.AddChildObject(*sdli);
382
383   m_FilePackage->Descriptor = m_EssenceDescriptor->InstanceUID;
384 }
385
386 //
387 Result_t
388 ASDCP::h__Writer::CreateBodyPart(const MXF::Rational& EditRate, ui32_t BytesPerEditUnit)
389 {
390   Result_t result = RESULT_OK;
391
392   // create a body partition if we're writing proper 429-3/OP-Atom
393   if ( m_Info.LabelSetType == LS_MXF_SMPTE )
394     {
395       // Body Partition
396       m_BodyPart.EssenceContainers = m_HeaderPart.EssenceContainers;
397       m_BodyPart.ThisPartition = m_File.Tell();
398       m_BodyPart.BodySID = 1;
399       UL OPAtomUL(Dict::ul(MDD_OPAtom));
400       m_BodyPart.OperationalPattern = OPAtomUL;
401       m_HeaderPart.m_RIP.PairArray.push_back(RIP::Pair(1, m_BodyPart.ThisPartition)); // Second RIP Entry
402       
403       UL BodyUL(Dict::ul(MDD_ClosedCompleteBodyPartition));
404       result = m_BodyPart.WriteToFile(m_File, BodyUL);
405     }
406   else
407     {
408       m_HeaderPart.BodySID = 1;
409     }
410
411   if ( ASDCP_SUCCESS(result) )
412     {
413       // Index setup
414       Kumu::fpos_t ECoffset = m_File.Tell();
415       m_FooterPart.IndexSID = 129;
416
417       if ( BytesPerEditUnit == 0 )
418         m_FooterPart.SetIndexParamsVBR(&m_HeaderPart.m_Primer, EditRate, ECoffset);
419       else
420         m_FooterPart.SetIndexParamsCBR(&m_HeaderPart.m_Primer, BytesPerEditUnit, EditRate);
421     }
422
423   return result;
424 }
425
426 //
427 Result_t
428 ASDCP::h__Writer::WriteMXFHeader(const std::string& PackageLabel, const UL& WrappingUL,
429                                  const std::string& TrackName, const UL& DataDefinition,
430                                  const MXF::Rational& EditRate, ui32_t TCFrameRate, ui32_t BytesPerEditUnit)
431 {
432   InitHeader();
433   AddSourceClip(EditRate, TCFrameRate, TrackName, DataDefinition, PackageLabel);
434   AddEssenceDescriptor(WrappingUL);
435
436   Result_t result = m_HeaderPart.WriteToFile(m_File, m_HeaderSize);
437
438   if ( KM_SUCCESS(result) )
439     result = CreateBodyPart(EditRate, BytesPerEditUnit);
440
441   return result;
442 }
443
444
445 // standard method of writing a plaintext or encrypted frame
446 Result_t
447 ASDCP::h__Writer::WriteEKLVPacket(const ASDCP::FrameBuffer& FrameBuf, const byte_t* EssenceUL,
448                                   AESEncContext* Ctx, HMACContext* HMAC)
449 {
450   Result_t result = RESULT_OK;
451   IntegrityPack IntPack;
452
453   byte_t overhead[128];
454   Kumu::MemIOWriter Overhead(overhead, 128);
455
456   if ( FrameBuf.Size() == 0 )
457     {
458       DefaultLogSink().Error("Cannot write empty frame buffer\n");
459       return RESULT_EMPTY_FB;
460     }
461
462   if ( m_Info.EncryptedEssence )
463     {
464       if ( ! Ctx )
465         return RESULT_CRYPT_CTX;
466
467       if ( m_Info.UsesHMAC && ! HMAC )
468         return RESULT_HMAC_CTX;
469
470       if ( FrameBuf.PlaintextOffset() > FrameBuf.Size() )
471         return RESULT_LARGE_PTO;
472
473       // encrypt the essence data (create encrypted source value)
474       result = EncryptFrameBuffer(FrameBuf, m_CtFrameBuf, Ctx);
475
476       // create HMAC
477       if ( ASDCP_SUCCESS(result) && m_Info.UsesHMAC )
478         result = IntPack.CalcValues(m_CtFrameBuf, m_Info.AssetUUID, m_FramesWritten + 1, HMAC);
479
480       if ( ASDCP_SUCCESS(result) )
481         { // write UL
482           if ( m_Info.LabelSetType == LS_MXF_INTEROP )
483             Overhead.WriteRaw(Dict::ul(MDD_MXFInterop_CryptEssence), SMPTE_UL_LENGTH);
484           else
485             Overhead.WriteRaw(Dict::ul(MDD_CryptEssence), SMPTE_UL_LENGTH);
486
487           // construct encrypted triplet header
488           ui32_t ETLength = klv_cryptinfo_size + m_CtFrameBuf.Size();
489
490           if ( m_Info.UsesHMAC )
491             ETLength += klv_intpack_size;
492           else
493             ETLength += (MXF_BER_LENGTH * 3); // for empty intpack
494
495           Overhead.WriteBER(ETLength, MXF_BER_LENGTH);                  // write encrypted triplet length
496           Overhead.WriteBER(UUIDlen, MXF_BER_LENGTH);                   // write ContextID length
497           Overhead.WriteRaw(m_Info.ContextID, UUIDlen);                  // write ContextID
498           Overhead.WriteBER(sizeof(ui64_t), MXF_BER_LENGTH);            // write PlaintextOffset length
499           Overhead.WriteUi64BE(FrameBuf.PlaintextOffset());              // write PlaintextOffset
500           Overhead.WriteBER(SMPTE_UL_LENGTH, MXF_BER_LENGTH);              // write essence UL length
501           Overhead.WriteRaw((byte_t*)EssenceUL, SMPTE_UL_LENGTH);           // write the essence UL
502           Overhead.WriteBER(sizeof(ui64_t), MXF_BER_LENGTH);            // write SourceLength length
503           Overhead.WriteUi64BE(FrameBuf.Size());                         // write SourceLength
504           Overhead.WriteBER(m_CtFrameBuf.Size(), MXF_BER_LENGTH);       // write ESV length
505
506           result = m_File.Writev(Overhead.Data(), Overhead.Length());
507         }
508
509       if ( ASDCP_SUCCESS(result) )
510         {
511           m_StreamOffset += Overhead.Length();
512           // write encrypted source value
513           result = m_File.Writev((byte_t*)m_CtFrameBuf.RoData(), m_CtFrameBuf.Size());
514         }
515
516       if ( ASDCP_SUCCESS(result) )
517         {
518           m_StreamOffset += m_CtFrameBuf.Size();
519
520           byte_t hmoverhead[512];
521           Kumu::MemIOWriter HMACOverhead(hmoverhead, 512);
522
523           // write the HMAC
524           if ( m_Info.UsesHMAC )
525             {
526               HMACOverhead.WriteRaw(IntPack.Data, klv_intpack_size);
527             }
528           else
529             { // we still need the var-pack length values if the intpack is empty
530               for ( ui32_t i = 0; i < 3 ; i++ )
531                 HMACOverhead.WriteBER(0, MXF_BER_LENGTH);
532             }
533
534           // write HMAC
535           result = m_File.Writev(HMACOverhead.Data(), HMACOverhead.Length());
536           m_StreamOffset += HMACOverhead.Length();
537         }
538     }
539   else
540     {
541       Overhead.WriteRaw((byte_t*)EssenceUL, SMPTE_UL_LENGTH);
542       Overhead.WriteBER(FrameBuf.Size(), MXF_BER_LENGTH);
543       result = m_File.Writev(Overhead.Data(), Overhead.Length());
544  
545       if ( ASDCP_SUCCESS(result) )
546         result = m_File.Writev((byte_t*)FrameBuf.RoData(), FrameBuf.Size());
547
548       if ( ASDCP_SUCCESS(result) )
549         m_StreamOffset += Overhead.Length() + FrameBuf.Size();
550     }
551
552   if ( ASDCP_SUCCESS(result) )
553     result = m_File.Writev();
554
555   return result;
556 }
557
558
559 // standard method of writing the header and footer of a completed MXF file
560 //
561 Result_t
562 ASDCP::h__Writer::WriteMXFFooter()
563 {
564   // Set top-level file package correctly for OP-Atom
565
566   //  m_MPTCSequence->Duration = m_MPTimecode->Duration = m_MPClSequence->Duration = m_MPClip->Duration = 
567   //    m_FPTCSequence->Duration = m_FPTimecode->Duration = m_FPClSequence->Duration = m_FPClip->Duration = 
568
569   DurationElementList_t::iterator dli = m_DurationUpdateList.begin();
570
571   for (; dli != m_DurationUpdateList.end(); dli++ )
572     **dli = m_FramesWritten;
573
574   m_EssenceDescriptor->ContainerDuration = m_FramesWritten;
575   m_FooterPart.PreviousPartition = m_HeaderPart.m_RIP.PairArray.back().ByteOffset;
576
577   Kumu::fpos_t here = m_File.Tell();
578   m_HeaderPart.m_RIP.PairArray.push_back(RIP::Pair(0, here)); // Last RIP Entry
579   m_HeaderPart.FooterPartition = here;
580
581   // re-label the partition
582   UL OPAtomUL(Dict::ul(MDD_OPAtom));
583
584   if ( m_Info.LabelSetType == LS_MXF_INTEROP )
585     OPAtomUL.Set(Dict::ul(MDD_MXFInterop_OPAtom));
586   
587   m_HeaderPart.OperationalPattern = OPAtomUL;
588   m_HeaderPart.m_Preface->OperationalPattern = m_HeaderPart.OperationalPattern;
589
590   m_FooterPart.OperationalPattern = m_HeaderPart.OperationalPattern;
591   m_FooterPart.EssenceContainers = m_HeaderPart.EssenceContainers;
592   m_FooterPart.FooterPartition = here;
593   m_FooterPart.ThisPartition = here;
594
595   Result_t result = m_FooterPart.WriteToFile(m_File, m_FramesWritten);
596
597   if ( ASDCP_SUCCESS(result) )
598     result = m_HeaderPart.m_RIP.WriteToFile(m_File);
599
600   if ( ASDCP_SUCCESS(result) )
601     result = m_File.Seek(0);
602
603   if ( ASDCP_SUCCESS(result) )
604     result = m_HeaderPart.WriteToFile(m_File, m_HeaderSize);
605
606   m_File.Close();
607   return result;
608 }
609
610 //
611 // end h__Writer.cpp
612 //