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