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