64d6faabf9894f9a7da1912efe798c7fcb3f1211
[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           Overhead.WriteRaw(m_Dict->ul(MDD_CryptEssence), SMPTE_UL_LENGTH);
527
528           // construct encrypted triplet header
529           ui32_t ETLength = klv_cryptinfo_size + m_CtFrameBuf.Size();
530           ui32_t BER_length = MXF_BER_LENGTH;
531
532           if ( m_Info.UsesHMAC )
533             ETLength += klv_intpack_size;
534           else
535             ETLength += (MXF_BER_LENGTH * 3); // for empty intpack
536
537           if ( ETLength > 0x00ffffff ) // Need BER integer longer than MXF_BER_LENGTH bytes
538             {
539               BER_length = Kumu::get_BER_length_for_value(ETLength);
540
541               if ( BER_length == 0 )
542                 result = RESULT_KLV_CODING;
543             }
544
545           if ( ASDCP_SUCCESS(result) )
546             {
547               if ( ! ( Overhead.WriteBER(ETLength, BER_length)                      // write encrypted triplet length
548                        && Overhead.WriteBER(UUIDlen, MXF_BER_LENGTH)                // write ContextID length
549                        && Overhead.WriteRaw(m_Info.ContextID, UUIDlen)              // write ContextID
550                        && Overhead.WriteBER(sizeof(ui64_t), MXF_BER_LENGTH)         // write PlaintextOffset length
551                        && Overhead.WriteUi64BE(FrameBuf.PlaintextOffset())          // write PlaintextOffset
552                        && Overhead.WriteBER(SMPTE_UL_LENGTH, MXF_BER_LENGTH)        // write essence UL length
553                        && Overhead.WriteRaw((byte_t*)EssenceUL, SMPTE_UL_LENGTH)    // write the essence UL
554                        && Overhead.WriteBER(sizeof(ui64_t), MXF_BER_LENGTH)         // write SourceLength length
555                        && Overhead.WriteUi64BE(FrameBuf.Size())                     // write SourceLength
556                        && Overhead.WriteBER(m_CtFrameBuf.Size(), BER_length) ) )    // write ESV length
557                 {
558                   result = RESULT_KLV_CODING;
559                 }
560             }
561
562           if ( ASDCP_SUCCESS(result) )
563             result = m_File.Writev(Overhead.Data(), Overhead.Length());
564         }
565
566       if ( ASDCP_SUCCESS(result) )
567         {
568           m_StreamOffset += Overhead.Length();
569           // write encrypted source value
570           result = m_File.Writev((byte_t*)m_CtFrameBuf.RoData(), m_CtFrameBuf.Size());
571         }
572
573       if ( ASDCP_SUCCESS(result) )
574         {
575           m_StreamOffset += m_CtFrameBuf.Size();
576
577           byte_t hmoverhead[512];
578           Kumu::MemIOWriter HMACOverhead(hmoverhead, 512);
579
580           // write the HMAC
581           if ( m_Info.UsesHMAC )
582             {
583               HMACOverhead.WriteRaw(IntPack.Data, klv_intpack_size);
584             }
585           else
586             { // we still need the var-pack length values if the intpack is empty
587               for ( ui32_t i = 0; i < 3 ; i++ )
588                 HMACOverhead.WriteBER(0, MXF_BER_LENGTH);
589             }
590
591           // write HMAC
592           result = m_File.Writev(HMACOverhead.Data(), HMACOverhead.Length());
593           m_StreamOffset += HMACOverhead.Length();
594         }
595     }
596   else
597     {
598       Overhead.WriteRaw((byte_t*)EssenceUL, SMPTE_UL_LENGTH);
599       Overhead.WriteBER(FrameBuf.Size(), MXF_BER_LENGTH);
600       result = m_File.Writev(Overhead.Data(), Overhead.Length());
601  
602       if ( ASDCP_SUCCESS(result) )
603         result = m_File.Writev((byte_t*)FrameBuf.RoData(), FrameBuf.Size());
604
605       if ( ASDCP_SUCCESS(result) )
606         m_StreamOffset += Overhead.Length() + FrameBuf.Size();
607     }
608
609   if ( ASDCP_SUCCESS(result) )
610     result = m_File.Writev();
611
612   return result;
613 }
614
615
616 // standard method of writing the header and footer of a completed MXF file
617 //
618 Result_t
619 ASDCP::h__Writer::WriteMXFFooter()
620 {
621   // Set top-level file package correctly for OP-Atom
622
623   //  m_MPTCSequence->Duration = m_MPTimecode->Duration = m_MPClSequence->Duration = m_MPClip->Duration = 
624   //    m_FPTCSequence->Duration = m_FPTimecode->Duration = m_FPClSequence->Duration = m_FPClip->Duration = 
625
626   DurationElementList_t::iterator dli = m_DurationUpdateList.begin();
627
628   for (; dli != m_DurationUpdateList.end(); dli++ )
629     **dli = m_FramesWritten;
630
631   m_EssenceDescriptor->ContainerDuration = m_FramesWritten;
632   m_FooterPart.PreviousPartition = m_HeaderPart.m_RIP.PairArray.back().ByteOffset;
633
634   Kumu::fpos_t here = m_File.Tell();
635   m_HeaderPart.m_RIP.PairArray.push_back(RIP::Pair(0, here)); // Last RIP Entry
636   m_HeaderPart.FooterPartition = here;
637
638   assert(m_Dict);
639   // re-label the partition
640   UL OPAtomUL(m_Dict->ul(MDD_OPAtom));
641   m_HeaderPart.OperationalPattern = OPAtomUL;
642   m_HeaderPart.m_Preface->OperationalPattern = m_HeaderPart.OperationalPattern;
643
644   m_FooterPart.OperationalPattern = m_HeaderPart.OperationalPattern;
645   m_FooterPart.EssenceContainers = m_HeaderPart.EssenceContainers;
646   m_FooterPart.FooterPartition = here;
647   m_FooterPart.ThisPartition = here;
648
649   Result_t result = m_FooterPart.WriteToFile(m_File, m_FramesWritten);
650
651   if ( ASDCP_SUCCESS(result) )
652     result = m_HeaderPart.m_RIP.WriteToFile(m_File);
653
654   if ( ASDCP_SUCCESS(result) )
655     result = m_File.Seek(0);
656
657   if ( ASDCP_SUCCESS(result) )
658     result = m_HeaderPart.WriteToFile(m_File, m_HeaderSize);
659
660   m_File.Close();
661   return result;
662 }
663
664 //
665 // end h__Writer.cpp
666 //