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