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