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