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