Fix Object ref not being written: this prevented GenericStreamTextBasedSet to be...
[asdcplib.git] / src / AS_02_PHDR.cpp
1 /*
2 Copyright (c) 2011-2018, John Hurst
3
4 All rights reserved.
5
6 Redistribution and use in source and binary forms, with or without
7 modification, are permitted provided that the following conditions
8 are met:
9 1. Redistributions of source code must retain the above copyright
10    notice, this list of conditions and the following disclaimer.
11 2. Redistributions in binary form must reproduce the above copyright
12    notice, this list of conditions and the following disclaimer in the
13    documentation and/or other materials provided with the distribution.
14 3. The name of the author may not be used to endorse or promote products
15    derived from this software without specific prior written permission.
16
17 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */ 
28 /*! \file    AS_02_PHDR.cpp
29   \version $Id$
30   \brief   AS-02 library, JPEG 2000 P-HDR essence reader and writer implementation
31 */
32
33 #include "AS_02_internal.h"
34 #include "AS_02_PHDR.h"
35
36 #include <iostream>
37 #include <iomanip>
38
39 using namespace ASDCP;
40 using namespace ASDCP::JP2K;
41 using Kumu::GenRandomValue;
42
43 //------------------------------------------------------------------------------------------
44
45 static std::string JP2K_PACKAGE_LABEL = "File Package: PROTOTYPE SMPTE ST 422 / ST 2067-5 frame wrapping of JPEG 2000 codestreams with HDR metadata";
46 static std::string PICT_DEF_LABEL = "PHDR Image Track";
47 static std::string MD_DEF_LABEL = "PHDR Metadata Track";
48
49
50
51 //------------------------------------------------------------------------------------------
52
53 //
54 void
55 AS_02::PHDR::FrameBuffer::Dump(FILE* stream, ui32_t dump_bytes) const
56 {
57   if ( stream == 0 )
58     stream = stderr;
59
60   fprintf(stream, "Frame %d, %d bytes (metadata: %zd bytes)\n", FrameNumber(), Size(), OpaqueMetadata.size());
61
62   if ( dump_bytes > 0 )
63     {
64       Kumu::hexdump(RoData(), Kumu::xmin(dump_bytes, Size()), stream);
65     }
66 }
67
68
69 //------------------------------------------------------------------------------------------
70 //
71 // hidden, internal implementation of JPEG 2000 reader
72
73
74 class AS_02::PHDR::MXFReader::h__Reader : public AS_02::h__AS02Reader
75 {
76   ASDCP_NO_COPY_CONSTRUCT(h__Reader);
77
78 public:
79   h__Reader(const Dictionary& d) :
80     AS_02::h__AS02Reader(d) {}
81
82   virtual ~h__Reader() {}
83
84   Result_t    OpenRead(const std::string& filename, std::string& PHDR_master_metadata);
85   Result_t    ReadFrame(ui32_t, AS_02::PHDR::FrameBuffer&, AESDecContext*, HMACContext*);
86 };
87
88 //
89 Result_t
90 AS_02::PHDR::MXFReader::h__Reader::OpenRead(const std::string& filename, std::string& PHDR_master_metadata)
91 {
92   Result_t result = OpenMXFRead(filename.c_str());
93   ui32_t SimplePayloadSID = 0;
94
95   if( KM_SUCCESS(result) )
96     {
97       InterchangeObject* tmp_iobj = 0;
98
99       m_HeaderPart.GetMDObjectByType(OBJ_TYPE_ARGS(CDCIEssenceDescriptor), &tmp_iobj);
100
101       if ( tmp_iobj == 0 )
102         {
103           m_HeaderPart.GetMDObjectByType(OBJ_TYPE_ARGS(RGBAEssenceDescriptor), &tmp_iobj);
104         }
105
106       if ( tmp_iobj == 0 )
107         {
108           DefaultLogSink().Error("RGBAEssenceDescriptor nor CDCIEssenceDescriptor found.\n");
109           return RESULT_AS02_FORMAT;
110         }
111
112       m_HeaderPart.GetMDObjectByType(OBJ_TYPE_ARGS(JPEG2000PictureSubDescriptor), &tmp_iobj);
113
114       if ( tmp_iobj == 0 )
115         {
116           DefaultLogSink().Error("JPEG2000PictureSubDescriptor not found.\n");
117           return RESULT_AS02_FORMAT;
118         }
119
120       m_HeaderPart.GetMDObjectByType(OBJ_TYPE_ARGS(PHDRMetadataTrackSubDescriptor), &tmp_iobj);
121
122       if ( tmp_iobj == 0 )
123         {
124           DefaultLogSink().Error("PHDRMetadataTrackSubDescriptor not found.\n");
125           return RESULT_AS02_FORMAT;
126         }
127       else
128         {
129           PHDRMetadataTrackSubDescriptor *tmp_desc = dynamic_cast<PHDRMetadataTrackSubDescriptor*>(tmp_iobj);
130           assert(tmp_desc);
131           SimplePayloadSID = tmp_desc->SimplePayloadSID;
132         }
133
134       std::list<InterchangeObject*> ObjectList;
135       m_HeaderPart.GetMDObjectsByType(OBJ_TYPE_ARGS(Track), ObjectList);
136
137       if ( ObjectList.empty() )
138         {
139           DefaultLogSink().Error("MXF Metadata contains no Track Sets.\n");
140           return RESULT_AS02_FORMAT;
141         }
142     }
143
144   // if PHDRSimplePayload exists, go get it
145   if ( KM_SUCCESS(result) && SimplePayloadSID )
146     {
147       RIP::const_pair_iterator pi;
148       RIP::PartitionPair TmpPair;
149       
150       // Look up the partition start in the RIP using the SID.
151       for ( pi = m_RIP.PairArray.begin(); pi != m_RIP.PairArray.end(); ++pi )
152         {
153           if ( (*pi).BodySID == SimplePayloadSID )
154             {
155               TmpPair = *pi;
156               break;
157             }
158         }
159
160       if ( TmpPair.ByteOffset == 0 )
161         {
162           DefaultLogSink().Error("Body SID not found in RIP set: %d\n", SimplePayloadSID);
163           return RESULT_AS02_FORMAT;
164         }
165
166       // seek to the start of the partition
167       if ( (Kumu::fpos_t)TmpPair.ByteOffset != m_LastPosition )
168         {
169           m_LastPosition = TmpPair.ByteOffset;
170           result = m_File.Seek(TmpPair.ByteOffset);
171         }
172
173       // read the partition header
174       ASDCP::MXF::Partition GSPart(m_Dict);
175       result = GSPart.InitFromFile(m_File);
176
177       if ( KM_SUCCESS(result) )
178         {
179           // read the generic stream packet
180           if ( KM_SUCCESS(result) )
181             {
182               ASDCP::FrameBuffer tmp_buf;
183               tmp_buf.Capacity(Kumu::Megabyte);
184
185               result = Read_EKLV_Packet(m_File, *m_Dict, m_Info, m_LastPosition, m_CtFrameBuf,
186                                         0, 0, tmp_buf, m_Dict->ul(MDD_GenericStream_DataElement), 0, 0);
187
188               if ( KM_SUCCESS(result) )
189                 {
190                   PHDR_master_metadata.assign((const char*)tmp_buf.RoData(), tmp_buf.Size());
191                 }
192             }
193         }
194     }
195
196   return result;
197 }
198
199 //
200 //
201 Result_t
202 AS_02::PHDR::MXFReader::h__Reader::ReadFrame(ui32_t FrameNum, AS_02::PHDR::FrameBuffer& FrameBuf,
203                       ASDCP::AESDecContext* Ctx, ASDCP::HMACContext* HMAC)
204 {
205   if ( ! m_File.IsOpen() )
206     return RESULT_INIT;
207
208   assert(m_Dict);
209   Result_t result = ReadEKLVFrame(FrameNum, FrameBuf, m_Dict->ul(MDD_JPEG2000Essence), Ctx, HMAC);
210
211   if ( KM_SUCCESS(result) )
212     {
213       ASDCP::FrameBuffer tmp_metadata_buffer;
214       tmp_metadata_buffer.Capacity(8192);
215
216       result = Read_EKLV_Packet(m_File, *m_Dict, m_Info, m_LastPosition, m_CtFrameBuf,
217                                 FrameNum, FrameNum + 1, tmp_metadata_buffer, m_Dict->ul(MDD_PHDRImageMetadataItem), Ctx, HMAC);
218
219       if ( KM_SUCCESS(result) )
220         {
221           FrameBuf.OpaqueMetadata.assign((const char*)tmp_metadata_buffer.RoData(), tmp_metadata_buffer.Size());
222         }
223       else
224         {
225           DefaultLogSink().Error("Metadata packet not found at frame %d.\n", FrameNum);
226           result = RESULT_OK;
227         }
228     }
229
230   return result;
231 }
232
233 //------------------------------------------------------------------------------------------
234 //
235
236 AS_02::PHDR::MXFReader::MXFReader()
237 {
238   m_Reader = new h__Reader(DefaultCompositeDict());
239 }
240
241
242 AS_02::PHDR::MXFReader::~MXFReader()
243 {
244 }
245
246 // Warning: direct manipulation of MXF structures can interfere
247 // with the normal operation of the wrapper.  Caveat emptor!
248 //
249 ASDCP::MXF::OP1aHeader&
250 AS_02::PHDR::MXFReader::OP1aHeader()
251 {
252   if ( m_Reader.empty() )
253     {
254       assert(g_OP1aHeader);
255       return *g_OP1aHeader;
256     }
257
258   return m_Reader->m_HeaderPart;
259 }
260
261 // Warning: direct manipulation of MXF structures can interfere
262 // with the normal operation of the wrapper.  Caveat emptor!
263 //
264 AS_02::MXF::AS02IndexReader&
265 AS_02::PHDR::MXFReader::AS02IndexReader()
266 {
267   if ( m_Reader.empty() )
268     {
269       assert(g_AS02IndexReader);
270       return *g_AS02IndexReader;
271     }
272
273   return m_Reader->m_IndexAccess;
274 }
275
276 // Warning: direct manipulation of MXF structures can interfere
277 // with the normal operation of the wrapper.  Caveat emptor!
278 //
279 ASDCP::MXF::RIP&
280 AS_02::PHDR::MXFReader::RIP()
281 {
282   if ( m_Reader.empty() )
283     {
284       assert(g_RIP);
285       return *g_RIP;
286     }
287
288   return m_Reader->m_RIP;
289 }
290
291 // Open the file for reading. The file must exist. Returns error if the
292 // operation cannot be completed.
293 Result_t
294 AS_02::PHDR::MXFReader::OpenRead(const std::string& filename, std::string& PHDR_master_metadata) const
295 {
296   return m_Reader->OpenRead(filename, PHDR_master_metadata);
297 }
298
299 //
300 Result_t
301 AS_02::PHDR::MXFReader::Close() const
302 {
303   if ( m_Reader && m_Reader->m_File.IsOpen() )
304     {
305       m_Reader->Close();
306       return RESULT_OK;
307     }
308
309   return RESULT_INIT;
310 }
311
312 //
313 Result_t
314 AS_02::PHDR::MXFReader::ReadFrame(ui32_t FrameNum, AS_02::PHDR::FrameBuffer& FrameBuf,
315                                            ASDCP::AESDecContext* Ctx, ASDCP::HMACContext* HMAC) const
316 {
317   if ( m_Reader && m_Reader->m_File.IsOpen() )
318     return m_Reader->ReadFrame(FrameNum, FrameBuf, Ctx, HMAC);
319
320   return RESULT_INIT;
321 }
322
323 // Fill the struct with the values from the file's header.
324 // Returns RESULT_INIT if the file is not open.
325 Result_t
326 AS_02::PHDR::MXFReader::FillWriterInfo(WriterInfo& Info) const
327 {
328   if ( m_Reader && m_Reader->m_File.IsOpen() )
329     {
330       Info = m_Reader->m_Info;
331       return RESULT_OK;
332     }
333
334   return RESULT_INIT;
335 }
336
337
338 //------------------------------------------------------------------------------------------
339
340 //
341 class AS_02::PHDR::MXFWriter::h__Writer : public AS_02::h__AS02WriterFrame
342 {
343   PHDRMetadataTrackSubDescriptor *m_MetadataTrackSubDescriptor;
344
345   ASDCP_NO_COPY_CONSTRUCT(h__Writer);
346   h__Writer();
347
348   JPEG2000PictureSubDescriptor* m_EssenceSubDescriptor;
349
350   Result_t WritePHDRHeader(const std::string& PackageLabel, const ASDCP::UL& WrappingUL,
351                            const std::string& TrackName, const ASDCP::UL& EssenceUL,
352                            const ASDCP::UL& DataDefinition, const ASDCP::Rational& EditRate,
353                            const ui32_t& TCFrameRate);
354
355 public:
356   byte_t            m_EssenceUL[SMPTE_UL_LENGTH];
357   byte_t            m_MetadataUL[SMPTE_UL_LENGTH];
358
359   h__Writer(const Dictionary& d) : h__AS02WriterFrame(d), m_EssenceSubDescriptor(0), m_MetadataTrackSubDescriptor(0) {
360     memset(m_EssenceUL, 0, SMPTE_UL_LENGTH);
361     memset(m_MetadataUL, 0, SMPTE_UL_LENGTH);
362   }
363
364   virtual ~h__Writer(){}
365
366   Result_t OpenWrite(const std::string&, ASDCP::MXF::FileDescriptor* essence_descriptor,
367                      ASDCP::MXF::InterchangeObject_list_t& essence_sub_descriptor_list,
368                      const AS_02::IndexStrategy_t& IndexStrategy,
369                      const ui32_t& PartitionSpace, const ui32_t& HeaderSize);
370   Result_t SetSourceStream(const std::string& label, const ASDCP::Rational& edit_rate);
371   Result_t WriteFrame(const AS_02::PHDR::FrameBuffer&, ASDCP::AESEncContext*, ASDCP::HMACContext*);
372   Result_t Finalize(const std::string& PHDR_master_metadata);
373 };
374
375
376 // Open the file for writing. The file must not exist. Returns error if
377 // the operation cannot be completed.
378 Result_t
379 AS_02::PHDR::MXFWriter::h__Writer::OpenWrite(const std::string& filename,
380                                              ASDCP::MXF::FileDescriptor* essence_descriptor,
381                                              ASDCP::MXF::InterchangeObject_list_t& essence_sub_descriptor_list,
382                                              const AS_02::IndexStrategy_t& IndexStrategy,
383                                              const ui32_t& PartitionSpace_sec, const ui32_t& HeaderSize)
384 {
385   if ( ! m_State.Test_BEGIN() )
386     {
387       return RESULT_STATE;
388     }
389
390   if ( m_IndexStrategy != AS_02::IS_FOLLOW )
391     {
392       DefaultLogSink().Error("Only strategy IS_FOLLOW is supported at this time.\n");
393       return Kumu::RESULT_NOTIMPL;
394     }
395
396   Result_t result = m_File.OpenWrite(filename);
397
398   if ( KM_SUCCESS(result) )
399     {
400       m_IndexStrategy = IndexStrategy;
401       m_PartitionSpace = PartitionSpace_sec; // later converted to edit units by WritePHDRHeader()
402       m_HeaderSize = HeaderSize;
403
404       if ( essence_descriptor->GetUL() != UL(m_Dict->ul(MDD_RGBAEssenceDescriptor))
405            && essence_descriptor->GetUL() != UL(m_Dict->ul(MDD_CDCIEssenceDescriptor)) )
406         {
407           DefaultLogSink().Error("Essence descriptor is not a RGBAEssenceDescriptor or CDCIEssenceDescriptor.\n");
408           essence_descriptor->Dump();
409           return RESULT_AS02_FORMAT;
410         }
411
412       m_EssenceDescriptor = essence_descriptor;
413
414       ASDCP::MXF::InterchangeObject_list_t::iterator i;
415       for ( i = essence_sub_descriptor_list.begin(); i != essence_sub_descriptor_list.end(); ++i )
416         {
417           if ( (*i)->GetUL() != UL(m_Dict->ul(MDD_JPEG2000PictureSubDescriptor)) )
418             {
419               DefaultLogSink().Error("Essence sub-descriptor is not a JPEG2000PictureSubDescriptor.\n");
420               (*i)->Dump();
421             }
422
423           m_EssenceSubDescriptorList.push_back(*i);
424           GenRandomValue((*i)->InstanceUID);
425           m_EssenceDescriptor->SubDescriptors.push_back((*i)->InstanceUID);
426           *i = 0; // parent will only free the ones we don't keep
427         }
428
429       result = m_State.Goto_INIT();
430     }
431
432   return result;
433 }
434
435 // all the above for a single source clip
436 Result_t
437 AS_02::PHDR::MXFWriter::h__Writer::WritePHDRHeader(const std::string& PackageLabel, const ASDCP::UL& WrappingUL,
438                                                    const std::string& TrackName, const ASDCP::UL& EssenceUL,
439                                                    const ASDCP::UL& DataDefinition, const ASDCP::Rational& EditRate,
440                                                    const ui32_t& TCFrameRate)
441 {
442   if ( EditRate.Numerator == 0 || EditRate.Denominator == 0 )
443     {
444       DefaultLogSink().Error("Non-zero edit-rate reqired.\n");
445       return RESULT_PARAM;
446     }
447   
448   InitHeader(MXFVersion_2011);
449   
450   AddSourceClip(EditRate, EditRate/*TODO: for a moment*/, TCFrameRate, TrackName, EssenceUL, DataDefinition, PackageLabel);
451
452   // add metadata track
453   TrackSet<SourceClip> metdata_track =
454     CreateTrackAndSequence<SourcePackage, SourceClip>(m_HeaderPart, *m_FilePackage,
455                                                       MD_DEF_LABEL, EditRate,
456                                                       UL(m_Dict->ul(MDD_PHDRImageMetadataItem)),
457                                                       3 /* track id */, m_Dict);
458
459   metdata_track.Sequence->Duration.set_has_value();
460   m_DurationUpdateList.push_back(&(metdata_track.Sequence->Duration.get()));
461   // Consult ST 379:2004 Sec. 6.3, "Element to track relationship" to see where "12" comes from.
462   metdata_track.Track->TrackNumber = KM_i32_BE(Kumu::cp2i<ui32_t>((UL(m_MetadataUL).Value() + 12)));
463
464   metdata_track.Clip = new SourceClip(m_Dict);
465   m_HeaderPart.AddChildObject(metdata_track.Clip);
466   metdata_track.Sequence->StructuralComponents.push_back(metdata_track.Clip->InstanceUID);
467   metdata_track.Clip->DataDefinition = UL(m_Dict->ul(MDD_PHDRImageMetadataWrappingFrame));
468
469   // for now we do not allow setting this value, so all files will be 'original'
470   metdata_track.Clip->SourceTrackID = 0;
471   metdata_track.Clip->SourcePackageID = NilUMID;
472   
473   metdata_track.Clip->Duration.set_has_value();
474   m_DurationUpdateList.push_back(&(metdata_track.Clip->Duration.get()));
475
476   // add PHDR subdescriptor
477   m_MetadataTrackSubDescriptor = new PHDRMetadataTrackSubDescriptor(m_Dict);
478   m_EssenceSubDescriptorList.push_back(m_MetadataTrackSubDescriptor);
479   GenRandomValue(m_MetadataTrackSubDescriptor->InstanceUID);
480   m_EssenceDescriptor->SubDescriptors.push_back(m_MetadataTrackSubDescriptor->InstanceUID);
481   m_MetadataTrackSubDescriptor->DataDefinition = UL(m_Dict->ul(MDD_PHDRImageMetadataWrappingFrame));
482   m_MetadataTrackSubDescriptor->SourceTrackID = 3;
483   m_MetadataTrackSubDescriptor->SimplePayloadSID = 0;
484
485   AddEssenceDescriptor(WrappingUL);
486
487   m_IndexWriter.SetPrimerLookup(&m_HeaderPart.m_Primer);
488   m_RIP.PairArray.push_back(RIP::PartitionPair(0, 0)); // Header partition RIP entry
489   m_IndexWriter.OperationalPattern = m_HeaderPart.OperationalPattern;
490   m_IndexWriter.EssenceContainers = m_HeaderPart.EssenceContainers;
491
492   Result_t result = m_HeaderPart.WriteToFile(m_File, m_HeaderSize);
493
494   if ( KM_SUCCESS(result) )
495     {
496       m_PartitionSpace *= floor( EditRate.Quotient() + 0.5 );  // convert seconds to edit units
497       m_ECStart = m_File.Tell();
498       m_IndexWriter.IndexSID = 129;
499
500       UL body_ul(m_Dict->ul(MDD_ClosedCompleteBodyPartition));
501       Partition body_part(m_Dict);
502       body_part.BodySID = 1;
503       body_part.MajorVersion = m_HeaderPart.MajorVersion;
504       body_part.MinorVersion = m_HeaderPart.MinorVersion;
505       body_part.OperationalPattern = m_HeaderPart.OperationalPattern;
506       body_part.EssenceContainers = m_HeaderPart.EssenceContainers;
507       body_part.ThisPartition = m_ECStart;
508       result = body_part.WriteToFile(m_File, body_ul);
509       m_RIP.PairArray.push_back(RIP::PartitionPair(1, body_part.ThisPartition)); // Second RIP Entry
510     }
511
512   return result;
513 }
514
515 // Automatically sets the MXF file's metadata from the first jpeg codestream stream.
516 Result_t
517 AS_02::PHDR::MXFWriter::h__Writer::SetSourceStream(const std::string& label, const ASDCP::Rational& edit_rate)
518 {
519   assert(m_Dict);
520   if ( ! m_State.Test_INIT() )
521     {
522       return RESULT_STATE;
523     }
524
525   memcpy(m_EssenceUL, m_Dict->ul(MDD_JPEG2000Essence), SMPTE_UL_LENGTH);
526   m_EssenceUL[SMPTE_UL_LENGTH-1] = 1; // first track of the essence container
527
528   memcpy(m_MetadataUL, m_Dict->ul(MDD_PHDRImageMetadataItem), SMPTE_UL_LENGTH);
529   m_MetadataUL[SMPTE_UL_LENGTH-1] = 3; // third track of the essence container
530
531   Result_t result = m_State.Goto_READY();
532
533   if ( KM_SUCCESS(result) )
534     {
535       result = WritePHDRHeader(label, UL(m_Dict->ul(MDD_MXFGCFUFrameWrappedPictureElement)),
536                                PICT_DEF_LABEL, UL(m_EssenceUL), UL(m_Dict->ul(MDD_PictureDataDef)),
537                                edit_rate, derive_timecode_rate_from_edit_rate(edit_rate));
538
539       if ( KM_SUCCESS(result) )
540         {
541           this->m_IndexWriter.SetPrimerLookup(&this->m_HeaderPart.m_Primer);
542         }
543     }
544
545   return result;
546 }
547
548 // Writes a frame of essence to the MXF file. If the optional AESEncContext
549 // argument is present, the essence is encrypted prior to writing.
550 // Fails if the file is not open, is finalized, or an operating system
551 // error occurs.
552 //
553 Result_t
554 AS_02::PHDR::MXFWriter::h__Writer::WriteFrame(const AS_02::PHDR::FrameBuffer& FrameBuf,
555                                               AESEncContext* Ctx, HMACContext* HMAC)
556 {
557   if ( FrameBuf.Size() == 0 )
558     {
559       DefaultLogSink().Error("The frame buffer size is zero.\n");
560       return RESULT_PARAM;
561     }
562
563   Result_t result = RESULT_OK;
564
565   if ( m_State.Test_READY() )
566     {
567       result = m_State.Goto_RUNNING(); // first time through
568     }
569
570   if ( KM_SUCCESS(result) )
571     {
572       ui64_t this_stream_offset = m_StreamOffset; // m_StreamOffset will be changed by the call to Write_EKLV_Packet
573
574       result = Write_EKLV_Packet(m_File, *m_Dict, m_HeaderPart, m_Info, m_CtFrameBuf, m_FramesWritten,
575                                  m_StreamOffset, FrameBuf, m_EssenceUL, MXF_BER_LENGTH, Ctx, HMAC);
576       
577       if ( KM_SUCCESS(result) )
578         {
579           ASDCP::FrameBuffer metadata_buffer_wrapper;
580           metadata_buffer_wrapper.SetData((byte_t*)(FrameBuf.OpaqueMetadata.c_str()), FrameBuf.OpaqueMetadata.size());
581           metadata_buffer_wrapper.Size(FrameBuf.OpaqueMetadata.size());
582           
583           
584           result = Write_EKLV_Packet(m_File, *m_Dict, m_HeaderPart, m_Info, m_CtFrameBuf, m_FramesWritten,
585                                      m_StreamOffset, metadata_buffer_wrapper, m_MetadataUL,
586                                      MXF_BER_LENGTH, Ctx, HMAC);
587         }
588       
589       if ( KM_SUCCESS(result) )
590         {  
591           IndexTableSegment::IndexEntry Entry;
592           Entry.StreamOffset = this_stream_offset;
593           m_IndexWriter.PushIndexEntry(Entry);
594         }
595
596       if ( m_FramesWritten > 1 && ( ( m_FramesWritten + 1 ) % m_PartitionSpace ) == 0 )
597         {
598           assert(m_IndexWriter.GetDuration() > 0);
599           FlushIndexPartition();
600
601           UL body_ul(m_Dict->ul(MDD_ClosedCompleteBodyPartition));
602           Partition body_part(m_Dict);
603           body_part.BodySID = 1;
604           body_part.MajorVersion = m_HeaderPart.MajorVersion;
605           body_part.MinorVersion = m_HeaderPart.MinorVersion;
606           body_part.OperationalPattern = m_HeaderPart.OperationalPattern;
607           body_part.EssenceContainers = m_HeaderPart.EssenceContainers;
608           body_part.ThisPartition = m_File.Tell();
609
610           body_part.BodyOffset = m_StreamOffset;
611           result = body_part.WriteToFile(m_File, body_ul);
612           m_RIP.PairArray.push_back(RIP::PartitionPair(1, body_part.ThisPartition));
613         }
614     }
615
616   if ( KM_SUCCESS(result) )
617     {
618       m_FramesWritten++;
619     }
620
621   return result;
622 }
623
624 // Closes the MXF file, writing the index and other closing information.
625 //
626 Result_t
627 AS_02::PHDR::MXFWriter::h__Writer::Finalize(const std::string& PHDR_master_metadata)
628 {
629   if ( ! m_State.Test_RUNNING() )
630     {
631       KM_RESULT_STATE_HERE();
632       return RESULT_STATE;
633     }
634
635   Result_t result = m_State.Goto_FINAL();
636
637   if ( KM_SUCCESS(result) )
638     {
639       FlushIndexPartition();
640
641       if ( ! PHDR_master_metadata.empty() )
642         {
643           // write PHDRSimplePayload
644           Kumu::fpos_t here = m_File.Tell();
645
646           // create generic stream partition header
647           static UL GenericStream_DataElement(m_Dict->ul(MDD_GenericStream_DataElement));
648           ASDCP::MXF::Partition GSPart(m_Dict);
649
650           GSPart.MajorVersion = m_HeaderPart.MajorVersion;
651           GSPart.MinorVersion = m_HeaderPart.MinorVersion;
652           GSPart.ThisPartition = here;
653           GSPart.PreviousPartition = m_RIP.PairArray.back().ByteOffset;
654           GSPart.OperationalPattern = m_HeaderPart.OperationalPattern;
655           GSPart.BodySID = 2;
656           m_MetadataTrackSubDescriptor->SimplePayloadSID = 2;
657
658           m_RIP.PairArray.push_back(RIP::PartitionPair(2, here));
659           GSPart.EssenceContainers = m_HeaderPart.EssenceContainers;
660
661           static UL gs_part_ul(m_Dict->ul(MDD_GenericStreamPartition));
662           Result_t result = GSPart.WriteToFile(m_File, gs_part_ul);
663
664           if ( KM_SUCCESS(result) )
665             {
666               ASDCP::FrameBuffer tmp_buf;
667               tmp_buf.SetData((byte_t*)PHDR_master_metadata.c_str(), PHDR_master_metadata.size());
668               tmp_buf.Size(PHDR_master_metadata.size());
669
670               result = Write_EKLV_Packet(m_File, *m_Dict, m_HeaderPart, m_Info, m_CtFrameBuf, m_FramesWritten,
671                                          m_StreamOffset, tmp_buf, GenericStream_DataElement.Value(),
672                                          MXF_BER_LENGTH, 0, 0);
673             }
674         }
675
676       result = WriteAS02Footer();
677     }
678
679   return result;
680 }
681
682
683 //------------------------------------------------------------------------------------------
684
685
686
687 AS_02::PHDR::MXFWriter::MXFWriter()
688 {
689 }
690
691 AS_02::PHDR::MXFWriter::~MXFWriter()
692 {
693 }
694
695 // Warning: direct manipulation of MXF structures can interfere
696 // with the normal operation of the wrapper.  Caveat emptor!
697 //
698 ASDCP::MXF::OP1aHeader&
699 AS_02::PHDR::MXFWriter::OP1aHeader()
700 {
701   if ( m_Writer.empty() )
702     {
703       assert(g_OP1aHeader);
704       return *g_OP1aHeader;
705     }
706
707   return m_Writer->m_HeaderPart;
708 }
709
710 // Warning: direct manipulation of MXF structures can interfere
711 // with the normal operation of the wrapper.  Caveat emptor!
712 //
713 ASDCP::MXF::RIP&
714 AS_02::PHDR::MXFWriter::RIP()
715 {
716   if ( m_Writer.empty() )
717     {
718       assert(g_RIP);
719       return *g_RIP;
720     }
721
722   return m_Writer->m_RIP;
723 }
724
725 // Open the file for writing. The file must not exist. Returns error if
726 // the operation cannot be completed.
727 Result_t
728 AS_02::PHDR::MXFWriter::OpenWrite(const std::string& filename, const ASDCP::WriterInfo& Info,
729                                   ASDCP::MXF::FileDescriptor* essence_descriptor,
730                                   ASDCP::MXF::InterchangeObject_list_t& essence_sub_descriptor_list,
731                                   const ASDCP::Rational& edit_rate, const ui32_t& header_size,
732                                   const IndexStrategy_t& strategy, const ui32_t& partition_space)
733 {
734   if ( essence_descriptor == 0 )
735     {
736       DefaultLogSink().Error("Essence descriptor object required.\n");
737       return RESULT_PARAM;
738     }
739
740   m_Writer = new AS_02::PHDR::MXFWriter::h__Writer(DefaultSMPTEDict());
741   m_Writer->m_Info = Info;
742
743   Result_t result = m_Writer->OpenWrite(filename, essence_descriptor, essence_sub_descriptor_list,
744                                         strategy, partition_space, header_size);
745
746   if ( KM_SUCCESS(result) )
747     result = m_Writer->SetSourceStream(JP2K_PACKAGE_LABEL, edit_rate);
748
749   if ( KM_FAILURE(result) )
750     m_Writer.release();
751
752   return result;
753 }
754
755 // Writes a frame of essence to the MXF file. If the optional AESEncContext
756 // argument is present, the essence is encrypted prior to writing.
757 // Fails if the file is not open, is finalized, or an operating system
758 // error occurs.
759 Result_t 
760 AS_02::PHDR::MXFWriter::WriteFrame(const AS_02::PHDR::FrameBuffer& FrameBuf, AESEncContext* Ctx, HMACContext* HMAC)
761 {
762   if ( m_Writer.empty() )
763     return RESULT_INIT;
764
765   return m_Writer->WriteFrame(FrameBuf, Ctx, HMAC);
766 }
767
768 // Closes the MXF file, writing the index and other closing information.
769 Result_t
770 AS_02::PHDR::MXFWriter::Finalize(const std::string& PHDR_master_metadata)
771 {
772   if ( m_Writer.empty() )
773     return RESULT_INIT;
774
775   return m_Writer->Finalize(PHDR_master_metadata);
776 }
777
778
779 //
780 // end AS_02_PHDR.cpp
781 //