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