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