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