imf bugs
[asdcplib.git] / src / AS_02_TimedText.cpp
1 /*
2 Copyright (c) 2008-2014, 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    AS_DCP_TimedText.cpp
28     \version $Id$       
29     \brief   AS-DCP library, PCM essence reader and writer implementation
30 */
31
32
33 #include "AS_02_internal.h"
34 #include "KM_xml.h"
35 #include <iostream>
36 #include <iomanip>
37
38 using Kumu::GenRandomValue;
39
40 static std::string TIMED_TEXT_PACKAGE_LABEL = "File Package: SMPTE ST 429-5 / ST 2067-5 clip wrapping of IMF Timed Text data";
41 static std::string TIMED_TEXT_DEF_LABEL = "Timed Text Track";
42
43
44 //------------------------------------------------------------------------------------------
45
46 const char*
47 MIME2str(TimedText::MIMEType_t m)
48 {
49   if ( m == TimedText::MT_PNG )
50     return "image/png";
51
52   else if ( m == TimedText::MT_OPENTYPE )
53     return "application/x-font-opentype";
54
55   return "application/octet-stream";
56 }
57
58 //------------------------------------------------------------------------------------------
59
60 typedef std::map<Kumu::UUID, Kumu::UUID> ResourceMap_t;
61
62 class AS_02::TimedText::MXFReader::h__Reader : public AS_02::h__AS02Reader
63 {
64   ASDCP::MXF::TimedTextDescriptor* m_EssenceDescriptor;
65   ResourceMap_t             m_ResourceMap;
66
67   ASDCP_NO_COPY_CONSTRUCT(h__Reader);
68
69 public:
70   TimedTextDescriptor m_TDesc;    
71
72   h__Reader(const Dictionary& d) : AS_02::h__AS02Reader(d), m_EssenceDescriptor(0) {
73     memset(&m_TDesc.AssetID, 0, UUIDlen);
74   }
75
76   virtual ~h__Reader() {}
77
78   Result_t    OpenRead(const std::string&);
79   Result_t    MD_to_TimedText_TDesc(TimedTextDescriptor& TDesc);
80   Result_t    ReadTimedTextResource(ASDCP::TimedText::FrameBuffer& FrameBuf, AESDecContext* Ctx, HMACContext* HMAC);
81   Result_t    ReadAncillaryResource(const Kumu::UUID&, ASDCP::TimedText::FrameBuffer& FrameBuf, AESDecContext* Ctx, HMACContext* HMAC);
82 };
83
84 //
85 ASDCP::Result_t
86 AS_02::TimedText::MXFReader::h__Reader::MD_to_TimedText_TDesc(TimedTextDescriptor& TDesc)
87 {
88   assert(m_EssenceDescriptor);
89   memset(&m_TDesc.AssetID, 0, UUIDlen);
90   ASDCP::MXF::TimedTextDescriptor* TDescObj = (ASDCP::MXF::TimedTextDescriptor*)m_EssenceDescriptor;
91
92   TDesc.EditRate = TDescObj->SampleRate;
93   assert(TDescObj->ContainerDuration <= 0xFFFFFFFFL);
94   TDesc.ContainerDuration = (ui32_t) TDescObj->ContainerDuration;
95   memcpy(TDesc.AssetID, TDescObj->ResourceID.Value(), UUIDlen);
96   TDesc.NamespaceName = TDescObj->NamespaceURI;
97   TDesc.EncodingName = TDescObj->UCSEncoding;
98
99   Batch<Kumu::UUID>::const_iterator sdi = TDescObj->SubDescriptors.begin();
100   TimedTextResourceSubDescriptor* DescObject = 0;
101   Result_t result = RESULT_OK;
102
103   for ( ; sdi != TDescObj->SubDescriptors.end() && KM_SUCCESS(result); sdi++ )
104     {
105       InterchangeObject* tmp_iobj = 0;
106       result = m_HeaderPart.GetMDObjectByID(*sdi, &tmp_iobj);
107       DescObject = static_cast<TimedTextResourceSubDescriptor*>(tmp_iobj);
108
109       if ( KM_SUCCESS(result) )
110         {
111           TimedTextResourceDescriptor TmpResource;
112           memcpy(TmpResource.ResourceID, DescObject->AncillaryResourceID.Value(), UUIDlen);
113
114           if ( DescObject->MIMEMediaType.find("application/x-font-opentype") != std::string::npos
115                || DescObject->MIMEMediaType.find("application/x-opentype") != std::string::npos
116                || DescObject->MIMEMediaType.find("font/opentype") != std::string::npos )
117             TmpResource.Type = ASDCP::TimedText::MT_OPENTYPE;
118
119           else if ( DescObject->MIMEMediaType.find("image/png") != std::string::npos )
120             TmpResource.Type = ASDCP::TimedText::MT_PNG;
121
122           else
123             TmpResource.Type = ASDCP::TimedText::MT_BIN;
124
125           TDesc.ResourceList.push_back(TmpResource);
126           m_ResourceMap.insert(ResourceMap_t::value_type(DescObject->AncillaryResourceID, *sdi));
127         }
128       else
129         {
130           DefaultLogSink().Error("Broken sub-descriptor link\n");
131           return RESULT_FORMAT;
132         }
133     }
134
135   return result;
136 }
137
138 //
139 ASDCP::Result_t
140 AS_02::TimedText::MXFReader::h__Reader::OpenRead(const std::string& filename)
141 {
142   Result_t result = OpenMXFRead(filename.c_str());
143   
144   if( ASDCP_SUCCESS(result) )
145     {
146       if ( m_EssenceDescriptor == 0 )
147         {
148           InterchangeObject* tmp_iobj = 0;
149           result = m_HeaderPart.GetMDObjectByType(OBJ_TYPE_ARGS(TimedTextDescriptor), &tmp_iobj);
150           m_EssenceDescriptor = static_cast<ASDCP::MXF::TimedTextDescriptor*>(tmp_iobj);
151         }
152
153       if( ASDCP_SUCCESS(result) )
154         result = MD_to_TimedText_TDesc(m_TDesc);
155     }
156
157   return result;
158 }
159
160 //
161 ASDCP::Result_t
162 AS_02::TimedText::MXFReader::h__Reader::ReadTimedTextResource(ASDCP::TimedText::FrameBuffer& FrameBuf,
163                                                               AESDecContext* Ctx, HMACContext* HMAC)
164 {
165   if ( ! m_File.IsOpen() )
166     return RESULT_INIT;
167
168   assert(m_Dict);
169   Result_t result = ReadEKLVFrame(0, FrameBuf, m_Dict->ul(MDD_TimedTextEssence), Ctx, HMAC);
170
171  if( ASDCP_SUCCESS(result) )
172    {
173      FrameBuf.AssetID(m_TDesc.AssetID);
174      FrameBuf.MIMEType("text/xml");
175    }
176
177  return result;
178 }
179
180 //
181 ASDCP::Result_t
182 AS_02::TimedText::MXFReader::h__Reader::ReadAncillaryResource(const Kumu::UUID& uuid,
183                                                               ASDCP::TimedText::FrameBuffer& FrameBuf,
184                                                               AESDecContext* Ctx, HMACContext* HMAC)
185 {
186   ResourceMap_t::const_iterator ri = m_ResourceMap.find(uuid);
187   if ( ri == m_ResourceMap.end() )
188     {
189       char buf[64];
190       DefaultLogSink().Error("No such resource: %s\n", uuid.EncodeHex(buf, 64));
191       return RESULT_RANGE;
192     }
193
194   TimedTextResourceSubDescriptor* DescObject = 0;
195   // get the subdescriptor
196   InterchangeObject* tmp_iobj = 0;
197   Result_t result = m_HeaderPart.GetMDObjectByID((*ri).second, &tmp_iobj);
198   DescObject = static_cast<TimedTextResourceSubDescriptor*>(tmp_iobj);
199
200   if ( KM_SUCCESS(result) )
201     {
202       Array<RIP::Pair>::const_iterator pi;
203       RIP::Pair TmpPair;
204       ui32_t sequence = 0;
205
206       // Look up the partition start in the RIP using the SID.
207       // Count the sequence length in because this is the sequence
208       // value needed to  complete the HMAC.
209       for ( pi = m_RIP.PairArray.begin(); pi != m_RIP.PairArray.end(); ++pi, ++sequence )
210         {
211           if ( (*pi).BodySID == DescObject->EssenceStreamID )
212             {
213               TmpPair = *pi;
214               break;
215             }
216         }
217
218       if ( TmpPair.ByteOffset == 0 )
219         {
220           DefaultLogSink().Error("Body SID not found in RIP set: %d\n", DescObject->EssenceStreamID);
221           return RESULT_FORMAT;
222         }
223
224       if ( KM_SUCCESS(result) )
225         {
226           FrameBuf.AssetID(uuid.Value());
227           FrameBuf.MIMEType(DescObject->MIMEMediaType);
228
229           // seek tp the start of the partition
230           if ( (Kumu::fpos_t)TmpPair.ByteOffset != m_LastPosition )
231             {
232               m_LastPosition = TmpPair.ByteOffset;
233               result = m_File.Seek(TmpPair.ByteOffset);
234             }
235
236           // read the partition header
237           ASDCP::MXF::Partition GSPart(m_Dict);
238           result = GSPart.InitFromFile(m_File);
239
240           if( ASDCP_SUCCESS(result) )
241             {
242               // check the SID
243               if ( DescObject->EssenceStreamID != GSPart.BodySID )
244                 {
245                   char buf[64];
246                   DefaultLogSink().Error("Generic stream partition body differs: %s\n", uuid.EncodeHex(buf, 64));
247                   return RESULT_FORMAT;
248                 }
249
250               // read the essence packet
251               assert(m_Dict);
252               if( ASDCP_SUCCESS(result) )
253                 result = ReadEKLVPacket(0, sequence, FrameBuf, m_Dict->ul(MDD_GenericStream_DataElement), Ctx, HMAC);
254             }
255         }
256     }
257
258   return result;
259 }
260
261
262 //------------------------------------------------------------------------------------------
263
264 AS_02::TimedText::MXFReader::MXFReader()
265 {
266   m_Reader = new h__Reader(DefaultSMPTEDict());
267 }
268
269
270 AS_02::TimedText::MXFReader::~MXFReader()
271 {
272 }
273
274 // Warning: direct manipulation of MXF structures can interfere
275 // with the normal operation of the wrapper.  Caveat emptor!
276 //
277 ASDCP::MXF::OP1aHeader&
278 AS_02::TimedText::MXFReader::OP1aHeader()
279 {
280   if ( m_Reader.empty() )
281     {
282       assert(g_OP1aHeader);
283       return *g_OP1aHeader;
284     }
285
286   return m_Reader->m_HeaderPart;
287 }
288
289 // Warning: direct manipulation of MXF structures can interfere
290 // with the normal operation of the wrapper.  Caveat emptor!
291 //
292 AS_02::MXF::AS02IndexReader&
293 AS_02::TimedText::MXFReader::AS02IndexReader()
294 {
295   if ( m_Reader.empty() )
296     {
297       assert(g_AS02IndexReader);
298       return *g_AS02IndexReader;
299     }
300
301   return m_Reader->m_IndexAccess;
302 }
303
304 // Warning: direct manipulation of MXF structures can interfere
305 // with the normal operation of the wrapper.  Caveat emptor!
306 //
307 ASDCP::MXF::RIP&
308 AS_02::TimedText::MXFReader::RIP()
309 {
310   if ( m_Reader.empty() )
311     {
312       assert(g_RIP);
313       return *g_RIP;
314     }
315
316   return m_Reader->m_RIP;
317 }
318
319 // Open the file for reading. The file must exist. Returns error if the
320 // operation cannot be completed.
321 ASDCP::Result_t
322 AS_02::TimedText::MXFReader::OpenRead(const std::string& filename) const
323 {
324   return m_Reader->OpenRead(filename);
325 }
326
327 // Fill the struct with the values from the file's header.
328 // Returns RESULT_INIT if the file is not open.
329 ASDCP::Result_t
330 AS_02::TimedText::MXFReader::FillTimedTextDescriptor(TimedText::TimedTextDescriptor& TDesc) const
331 {
332   if ( m_Reader && m_Reader->m_File.IsOpen() )
333     {
334       TDesc = m_Reader->m_TDesc;
335       return RESULT_OK;
336     }
337
338   return RESULT_INIT;
339 }
340
341 // Fill the struct with the values from the file's header.
342 // Returns RESULT_INIT if the file is not open.
343 ASDCP::Result_t
344 AS_02::TimedText::MXFReader::FillWriterInfo(WriterInfo& Info) const
345 {
346   if ( m_Reader && m_Reader->m_File.IsOpen() )
347     {
348       Info = m_Reader->m_Info;
349       return RESULT_OK;
350     }
351
352   return RESULT_INIT;
353 }
354
355 //
356 ASDCP::Result_t
357 AS_02::TimedText::MXFReader::ReadTimedTextResource(std::string& s, AESDecContext* Ctx, HMACContext* HMAC) const
358 {
359   ASDCP::TimedText::FrameBuffer FrameBuf(8*Kumu::Megabyte);
360
361   Result_t result = ReadTimedTextResource(FrameBuf, Ctx, HMAC);
362
363   if ( ASDCP_SUCCESS(result) )
364     s.assign((char*)FrameBuf.Data(), FrameBuf.Size());
365
366   return result;
367 }
368
369 //
370 ASDCP::Result_t
371 AS_02::TimedText::MXFReader::ReadTimedTextResource(ASDCP::TimedText::FrameBuffer& FrameBuf,
372                                                    AESDecContext* Ctx, HMACContext* HMAC) const
373 {
374   if ( m_Reader && m_Reader->m_File.IsOpen() )
375     return m_Reader->ReadTimedTextResource(FrameBuf, Ctx, HMAC);
376
377   return RESULT_INIT;
378 }
379
380 //
381 ASDCP::Result_t
382 AS_02::TimedText::MXFReader::ReadAncillaryResource(const Kumu::UUID& uuid, ASDCP::TimedText::FrameBuffer& FrameBuf,
383                                                    AESDecContext* Ctx, HMACContext* HMAC) const
384 {
385   if ( m_Reader && m_Reader->m_File.IsOpen() )
386     return m_Reader->ReadAncillaryResource(uuid, FrameBuf, Ctx, HMAC);
387
388   return RESULT_INIT;
389 }
390
391
392 //
393 void
394 AS_02::TimedText::MXFReader::DumpHeaderMetadata(FILE* stream) const
395 {
396   if ( m_Reader->m_File.IsOpen() )
397     m_Reader->m_HeaderPart.Dump(stream);
398 }
399
400
401 //
402 void
403 AS_02::TimedText::MXFReader::DumpIndex(FILE* stream) const
404 {
405   if ( m_Reader->m_File.IsOpen() )
406     m_Reader->m_IndexAccess.Dump(stream);
407 }
408
409 //
410 ASDCP::Result_t
411 AS_02::TimedText::MXFReader::Close() const
412 {
413   if ( m_Reader && m_Reader->m_File.IsOpen() )
414     {
415       m_Reader->Close();
416       return RESULT_OK;
417     }
418
419   return RESULT_INIT;
420 }
421
422
423 //------------------------------------------------------------------------------------------
424
425
426 //
427 class AS_02::TimedText::MXFWriter::h__Writer : public AS_02::h__AS02WriterClip
428 {
429   ASDCP_NO_COPY_CONSTRUCT(h__Writer);
430   h__Writer();
431
432 public:
433   TimedTextDescriptor m_TDesc;
434   byte_t m_EssenceUL[SMPTE_UL_LENGTH];
435   ui32_t m_EssenceStreamID;
436
437   h__Writer(const Dictionary& d) : AS_02::h__AS02WriterClip(d), m_EssenceStreamID(10)
438   {
439     memset(m_EssenceUL, 0, SMPTE_UL_LENGTH);
440   }
441
442   virtual ~h__Writer() {}
443
444   Result_t OpenWrite(const std::string&, ui32_t HeaderSize);
445   Result_t SetSourceStream(const ASDCP::TimedText::TimedTextDescriptor&);
446   Result_t WriteTimedTextResource(const std::string& XMLDoc, AESEncContext* = 0, HMACContext* = 0);
447   Result_t WriteAncillaryResource(const ASDCP::TimedText::FrameBuffer&, AESEncContext* = 0, HMACContext* = 0);
448   Result_t Finalize();
449   Result_t TimedText_TDesc_to_MD(ASDCP::TimedText::TimedTextDescriptor& TDesc);
450 };
451
452 //
453 ASDCP::Result_t
454 AS_02::TimedText::MXFWriter::h__Writer::TimedText_TDesc_to_MD(TimedText::TimedTextDescriptor& TDesc)
455 {
456   assert(m_EssenceDescriptor);
457   ASDCP::MXF::TimedTextDescriptor* TDescObj = (ASDCP::MXF::TimedTextDescriptor*)m_EssenceDescriptor;
458
459   TDescObj->SampleRate = TDesc.EditRate;
460   TDescObj->ContainerDuration = TDesc.ContainerDuration;
461   TDescObj->ResourceID.Set(TDesc.AssetID);
462   TDescObj->NamespaceURI = TDesc.NamespaceName;
463   TDescObj->UCSEncoding = TDesc.EncodingName;
464
465   return RESULT_OK;
466 }
467
468 //
469 ASDCP::Result_t
470 AS_02::TimedText::MXFWriter::h__Writer::OpenWrite(const std::string& filename, ui32_t HeaderSize)
471 {
472   if ( ! m_State.Test_BEGIN() )
473     {
474       KM_RESULT_STATE_HERE();
475       return RESULT_STATE;
476     }
477
478   Result_t result = m_File.OpenWrite(filename.c_str());
479
480   if ( ASDCP_SUCCESS(result) )
481     {
482       m_HeaderSize = HeaderSize;
483       m_EssenceDescriptor = new ASDCP::MXF::TimedTextDescriptor(m_Dict);
484       result = m_State.Goto_INIT();
485     }
486
487   return result;
488 }
489
490 //
491 ASDCP::Result_t
492 AS_02::TimedText::MXFWriter::h__Writer::SetSourceStream(ASDCP::TimedText::TimedTextDescriptor const& TDesc)
493 {
494   if ( ! m_State.Test_INIT() )
495     {
496       KM_RESULT_STATE_HERE();
497       return RESULT_STATE;
498     }
499
500   assert(m_Dict);
501   m_TDesc = TDesc;
502
503   Result_t result = TimedText_TDesc_to_MD(m_TDesc);
504
505   if ( KM_SUCCESS(result) )
506     {
507       ResourceList_t::const_iterator i;
508       for ( i = m_TDesc.ResourceList.begin() ; i != m_TDesc.ResourceList.end(); ++i )
509         {
510           TimedTextResourceSubDescriptor* resourceSubdescriptor = new TimedTextResourceSubDescriptor(m_Dict);
511           GenRandomValue(resourceSubdescriptor->InstanceUID);
512           resourceSubdescriptor->AncillaryResourceID.Set((*i).ResourceID);
513           resourceSubdescriptor->MIMEMediaType = MIME2str((*i).Type);
514           resourceSubdescriptor->EssenceStreamID = m_EssenceStreamID++;
515           m_EssenceSubDescriptorList.push_back((FileDescriptor*)resourceSubdescriptor);
516           m_EssenceDescriptor->SubDescriptors.push_back(resourceSubdescriptor->InstanceUID);
517         }
518     }
519   
520   if ( KM_SUCCESS(result) )
521     {
522       result = WriteAS02Header(TIMED_TEXT_PACKAGE_LABEL, UL(m_Dict->ul(MDD_TimedTextWrappingClip)),
523                                "Data Track", UL(m_EssenceUL), UL(m_Dict->ul(MDD_TimedTextEssence)),
524                                TDesc.EditRate, derive_timecode_rate_from_edit_rate(TDesc.EditRate));
525     }
526  
527   if ( KM_SUCCESS(result) )
528     {
529       this->m_IndexWriter.SetPrimerLookup(&this->m_HeaderPart.m_Primer);
530     }
531
532   if ( KM_SUCCESS(result) )
533     {
534       memcpy(m_EssenceUL, m_Dict->ul(MDD_TimedTextEssence), SMPTE_UL_LENGTH);
535       m_EssenceUL[SMPTE_UL_LENGTH-1] = 1; // first (and only) essence container
536       result = m_State.Goto_READY();
537     }
538
539   return result;
540 }
541
542 //
543 ASDCP::Result_t
544 AS_02::TimedText::MXFWriter::h__Writer::WriteTimedTextResource(const std::string& XMLDoc,
545                                                                ASDCP::AESEncContext* Ctx, ASDCP::HMACContext* HMAC)
546 {
547   Result_t result = m_State.Goto_RUNNING();
548
549   if ( KM_SUCCESS(result) )
550     {
551       // TODO: make sure it's XML
552
553       ui32_t str_size = XMLDoc.size();
554       ASDCP::TimedText::FrameBuffer FrameBuf(str_size);
555       
556       memcpy(FrameBuf.Data(), XMLDoc.c_str(), str_size);
557       FrameBuf.Size(str_size);
558
559       IndexTableSegment::IndexEntry Entry;
560       Entry.StreamOffset = m_StreamOffset;
561       
562       if ( KM_SUCCESS(result) )
563         {
564           ui64_t this_stream_offset = m_StreamOffset; // m_StreamOffset will be changed by the call to Write_EKLV_Packet
565
566           result = Write_EKLV_Packet(m_File, *m_Dict, m_HeaderPart, m_Info, m_CtFrameBuf, m_FramesWritten,
567                                      m_StreamOffset, FrameBuf, m_EssenceUL, Ctx, HMAC);
568         }
569     }
570
571   return result;
572 }
573
574
575 //
576 ASDCP::Result_t
577 AS_02::TimedText::MXFWriter::h__Writer::WriteAncillaryResource(const ASDCP::TimedText::FrameBuffer& FrameBuf,
578                                                                ASDCP::AESEncContext* Ctx, ASDCP::HMACContext* HMAC)
579 {
580   if ( ! m_State.Test_RUNNING() )
581     {
582       KM_RESULT_STATE_HERE();
583       return RESULT_STATE;
584     }
585
586   Kumu::fpos_t here = m_File.Tell();
587   assert(m_Dict);
588
589   // create generic stream partition header
590   static UL GenericStream_DataElement(m_Dict->ul(MDD_GenericStream_DataElement));
591   ASDCP::MXF::Partition GSPart(m_Dict);
592
593   GSPart.ThisPartition = here;
594   GSPart.PreviousPartition = m_RIP.PairArray.back().ByteOffset;
595   GSPart.BodySID = m_EssenceStreamID;
596   GSPart.OperationalPattern = m_HeaderPart.OperationalPattern;
597
598   m_RIP.PairArray.push_back(RIP::Pair(m_EssenceStreamID++, here));
599   GSPart.EssenceContainers.push_back(UL(m_Dict->ul(MDD_TimedTextEssence)));
600   UL TmpUL(m_Dict->ul(MDD_GenericStreamPartition));
601   Result_t result = GSPart.WriteToFile(m_File, TmpUL);
602
603   if ( KM_SUCCESS(result) )
604     {
605       ui64_t this_stream_offset = m_StreamOffset; // m_StreamOffset will be changed by the call to Write_EKLV_Packet
606       
607       result = Write_EKLV_Packet(m_File, *m_Dict, m_HeaderPart, m_Info, m_CtFrameBuf, m_FramesWritten,
608                                  m_StreamOffset, FrameBuf, GenericStream_DataElement.Value(), Ctx, HMAC);
609     }
610
611   m_FramesWritten++;
612   return result;
613 }
614
615 //
616 ASDCP::Result_t
617 AS_02::TimedText::MXFWriter::h__Writer::Finalize()
618 {
619   if ( ! m_State.Test_RUNNING() )
620     {
621       DefaultLogSink().Error("Cannot finalize file, the primary essence resource has not been written.\n");
622       return RESULT_STATE;
623     }
624
625   m_IndexWriter.m_Duration = m_FramesWritten = m_TDesc.ContainerDuration;
626   fprintf(stderr, "m_IndexWriter.m_Duration=%d\n", m_IndexWriter.m_Duration);
627
628   Result_t result = m_State.Goto_FINAL();
629
630   if ( KM_SUCCESS(result) )
631     {
632       result = WriteAS02Footer();
633     }
634
635   return result;
636 }
637
638
639 //------------------------------------------------------------------------------------------
640
641 AS_02::TimedText::MXFWriter::MXFWriter()
642 {
643 }
644
645 AS_02::TimedText::MXFWriter::~MXFWriter()
646 {
647 }
648
649 // Warning: direct manipulation of MXF structures can interfere
650 // with the normal operation of the wrapper.  Caveat emptor!
651 //
652 ASDCP::MXF::OP1aHeader&
653 AS_02::TimedText::MXFWriter::OP1aHeader()
654 {
655   if ( m_Writer.empty() )
656     {
657       assert(g_OP1aHeader);
658       return *g_OP1aHeader;
659     }
660
661   return m_Writer->m_HeaderPart;
662 }
663
664 // Warning: direct manipulation of MXF structures can interfere
665 // with the normal operation of the wrapper.  Caveat emptor!
666 //
667 ASDCP::MXF::RIP&
668 AS_02::TimedText::MXFWriter::RIP()
669 {
670   if ( m_Writer.empty() )
671     {
672       assert(g_RIP);
673       return *g_RIP;
674     }
675
676   return m_Writer->m_RIP;
677 }
678
679 // Open the file for writing. The file must not exist. Returns error if
680 // the operation cannot be completed.
681 ASDCP::Result_t
682 AS_02::TimedText::MXFWriter::OpenWrite(const std::string& filename, const WriterInfo& Info,
683                                        const TimedTextDescriptor& TDesc, ui32_t HeaderSize)
684 {
685   if ( Info.LabelSetType != LS_MXF_SMPTE )
686     {
687       DefaultLogSink().Error("Timed Text support requires LS_MXF_SMPTE\n");
688       return RESULT_FORMAT;
689     }
690
691   m_Writer = new h__Writer(DefaultSMPTEDict());
692   m_Writer->m_Info = Info;
693   
694   Result_t result = m_Writer->OpenWrite(filename, HeaderSize);
695
696   if ( ASDCP_SUCCESS(result) )
697     result = m_Writer->SetSourceStream(TDesc);
698
699   if ( ASDCP_FAILURE(result) )
700     m_Writer.release();
701
702   return result;
703 }
704
705 //
706 ASDCP::Result_t
707 AS_02::TimedText::MXFWriter::WriteTimedTextResource(const std::string& XMLDoc, AESEncContext* Ctx, HMACContext* HMAC)
708 {
709   if ( m_Writer.empty() )
710     return RESULT_INIT;
711
712   return m_Writer->WriteTimedTextResource(XMLDoc, Ctx, HMAC);
713 }
714
715 //
716 ASDCP::Result_t
717 AS_02::TimedText::MXFWriter::WriteAncillaryResource(const ASDCP::TimedText::FrameBuffer& FrameBuf, AESEncContext* Ctx, HMACContext* HMAC)
718 {
719   if ( m_Writer.empty() )
720     return RESULT_INIT;
721
722   return m_Writer->WriteAncillaryResource(FrameBuf, Ctx, HMAC);
723 }
724
725 // Closes the MXF file, writing the index and other closing information.
726 ASDCP::Result_t
727 AS_02::TimedText::MXFWriter::Finalize()
728 {
729   if ( m_Writer.empty() )
730     return RESULT_INIT;
731
732   return m_Writer->Finalize();
733 }
734
735
736
737 //
738 // end AS_02_timedText.cpp
739 //