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