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