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