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