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