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