mega datetime patch
[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 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(const Dictionary& d) : ASDCP::h__Reader(d), 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   assert(m_Dict);
237   Result_t result = ReadEKLVFrame(0, FrameBuf, m_Dict->ul(MDD_TimedTextEssence), Ctx, HMAC);
238
239  if( ASDCP_SUCCESS(result) )
240    {
241      FrameBuf.AssetID(m_TDesc.AssetID);
242      FrameBuf.MIMEType("text/xml");
243    }
244
245  return result;
246 }
247
248 //
249 ASDCP::Result_t
250 ASDCP::TimedText::MXFReader::h__Reader::ReadAncillaryResource(const byte_t* uuid, FrameBuffer& FrameBuf,
251                                                               AESDecContext* Ctx, HMACContext* HMAC)
252 {
253   KM_TEST_NULL_L(uuid);
254   UUID RID(uuid);
255
256   ResourceMap_t::const_iterator ri = m_ResourceMap.find(RID);
257   if ( ri == m_ResourceMap.end() )
258     {
259       char buf[64];
260       DefaultLogSink().Error("No such resource: %s\n", RID.EncodeHex(buf, 64));
261       return RESULT_RANGE;
262     }
263
264   TimedTextResourceSubDescriptor* DescObject = 0;
265   // get the subdescriptor
266   InterchangeObject* tmp_iobj = 0;
267   Result_t result = m_HeaderPart.GetMDObjectByID((*ri).second, &tmp_iobj);
268   DescObject = static_cast<TimedTextResourceSubDescriptor*>(tmp_iobj);
269
270   if ( KM_SUCCESS(result) )
271     {
272       Array<RIP::Pair>::const_iterator pi;
273       RIP::Pair TmpPair;
274       ui32_t sequence = 1;
275
276       // Look up the partition start in the RIP using the SID.
277       // Count the sequence length in because this is the sequence
278       // value needed to  complete the HMAC.
279       for ( pi = m_HeaderPart.m_RIP.PairArray.begin(); pi != m_HeaderPart.m_RIP.PairArray.end(); pi++, sequence++ )
280         {
281           if ( (*pi).BodySID == DescObject->EssenceStreamID )
282             {
283               TmpPair = *pi;
284               break;
285             }
286         }
287
288       if ( TmpPair.ByteOffset == 0 )
289         {
290           DefaultLogSink().Error("Body SID not found in RIP set: %d\n", DescObject->EssenceStreamID);
291           return RESULT_FORMAT;
292         }
293
294       if ( KM_SUCCESS(result) )
295         {
296           FrameBuf.AssetID(uuid);
297           FrameBuf.MIMEType(DescObject->MIMEMediaType);
298
299           // seek tp the start of the partition
300           if ( (Kumu::fpos_t)TmpPair.ByteOffset != m_LastPosition )
301             {
302               m_LastPosition = TmpPair.ByteOffset;
303               result = m_File.Seek(TmpPair.ByteOffset);
304             }
305
306           // read the partition header
307           MXF::Partition GSPart(m_Dict);
308           result = GSPart.InitFromFile(m_File);
309
310           if( ASDCP_SUCCESS(result) )
311             {
312               // check the SID
313               if ( DescObject->EssenceStreamID != GSPart.BodySID )
314                 {
315                   char buf[64];
316                   DefaultLogSink().Error("Generic stream partition body differs: %s\n", RID.EncodeHex(buf, 64));
317                   return RESULT_FORMAT;
318                 }
319
320               // read the essence packet
321               assert(m_Dict);
322               if( ASDCP_SUCCESS(result) )
323                 result = ReadEKLVPacket(0, 1, FrameBuf, m_Dict->ul(MDD_GenericStream_DataElement), Ctx, HMAC);
324             }
325         }
326     }
327
328   return result;
329 }
330
331
332 //------------------------------------------------------------------------------------------
333
334 ASDCP::TimedText::MXFReader::MXFReader()
335 {
336   m_Reader = new h__Reader(DefaultSMPTEDict());
337 }
338
339
340 ASDCP::TimedText::MXFReader::~MXFReader()
341 {
342 }
343
344 // Warning: direct manipulation of MXF structures can interfere
345 // with the normal operation of the wrapper.  Caveat emptor!
346 //
347 ASDCP::MXF::OPAtomHeader&
348 ASDCP::TimedText::MXFReader::OPAtomHeader()
349 {
350   if ( m_Reader.empty() )
351     {
352       assert(g_OPAtomHeader);
353       return *g_OPAtomHeader;
354     }
355
356   return m_Reader->m_HeaderPart;
357 }
358
359 // Warning: direct manipulation of MXF structures can interfere
360 // with the normal operation of the wrapper.  Caveat emptor!
361 //
362 ASDCP::MXF::OPAtomIndexFooter&
363 ASDCP::TimedText::MXFReader::OPAtomIndexFooter()
364 {
365   if ( m_Reader.empty() )
366     {
367       assert(g_OPAtomIndexFooter);
368       return *g_OPAtomIndexFooter;
369     }
370
371   return m_Reader->m_FooterPart;
372 }
373
374 // Open the file for reading. The file must exist. Returns error if the
375 // operation cannot be completed.
376 ASDCP::Result_t
377 ASDCP::TimedText::MXFReader::OpenRead(const char* filename) const
378 {
379   return m_Reader->OpenRead(filename);
380 }
381
382 // Fill the struct with the values from the file's header.
383 // Returns RESULT_INIT if the file is not open.
384 ASDCP::Result_t
385 ASDCP::TimedText::MXFReader::FillTimedTextDescriptor(TimedText::TimedTextDescriptor& TDesc) const
386 {
387   if ( m_Reader && m_Reader->m_File.IsOpen() )
388     {
389       TDesc = m_Reader->m_TDesc;
390       return RESULT_OK;
391     }
392
393   return RESULT_INIT;
394 }
395
396 // Fill the struct with the values from the file's header.
397 // Returns RESULT_INIT if the file is not open.
398 ASDCP::Result_t
399 ASDCP::TimedText::MXFReader::FillWriterInfo(WriterInfo& Info) const
400 {
401   if ( m_Reader && m_Reader->m_File.IsOpen() )
402     {
403       Info = m_Reader->m_Info;
404       return RESULT_OK;
405     }
406
407   return RESULT_INIT;
408 }
409
410 //
411 ASDCP::Result_t
412 ASDCP::TimedText::MXFReader::ReadTimedTextResource(std::string& s, AESDecContext* Ctx, HMACContext* HMAC) const
413 {
414   FrameBuffer FrameBuf(2*Kumu::Megabyte);
415
416   Result_t result = ReadTimedTextResource(FrameBuf, Ctx, HMAC);
417
418   if ( ASDCP_SUCCESS(result) )
419     s.assign((char*)FrameBuf.Data(), FrameBuf.Size());
420
421   return result;
422 }
423
424 //
425 ASDCP::Result_t
426 ASDCP::TimedText::MXFReader::ReadTimedTextResource(FrameBuffer& FrameBuf,
427                                                    AESDecContext* Ctx, HMACContext* HMAC) const
428 {
429   if ( m_Reader && m_Reader->m_File.IsOpen() )
430     return m_Reader->ReadTimedTextResource(FrameBuf, Ctx, HMAC);
431
432   return RESULT_INIT;
433 }
434
435 //
436 ASDCP::Result_t
437 ASDCP::TimedText::MXFReader::ReadAncillaryResource(const byte_t* uuid, FrameBuffer& FrameBuf,
438                                                    AESDecContext* Ctx, HMACContext* HMAC) const
439 {
440   if ( m_Reader && m_Reader->m_File.IsOpen() )
441     return m_Reader->ReadAncillaryResource(uuid, FrameBuf, Ctx, HMAC);
442
443   return RESULT_INIT;
444 }
445
446
447 //
448 void
449 ASDCP::TimedText::MXFReader::DumpHeaderMetadata(FILE* stream) const
450 {
451   if ( m_Reader->m_File.IsOpen() )
452     m_Reader->m_HeaderPart.Dump(stream);
453 }
454
455
456 //
457 void
458 ASDCP::TimedText::MXFReader::DumpIndex(FILE* stream) const
459 {
460   if ( m_Reader->m_File.IsOpen() )
461     m_Reader->m_FooterPart.Dump(stream);
462 }
463
464 //
465 ASDCP::Result_t
466 ASDCP::TimedText::MXFReader::Close() const
467 {
468   if ( m_Reader && m_Reader->m_File.IsOpen() )
469     {
470       m_Reader->Close();
471       return RESULT_OK;
472     }
473
474   return RESULT_INIT;
475 }
476
477
478 //------------------------------------------------------------------------------------------
479
480
481 //
482 class ASDCP::TimedText::MXFWriter::h__Writer : public ASDCP::h__Writer
483 {
484   ASDCP_NO_COPY_CONSTRUCT(h__Writer);
485   h__Writer();
486
487 public:
488   TimedTextDescriptor m_TDesc;
489   byte_t              m_EssenceUL[SMPTE_UL_LENGTH];
490   ui32_t              m_EssenceStreamID;
491
492   h__Writer(const Dictionary& d) : ASDCP::h__Writer(d), m_EssenceStreamID(10) {
493     memset(m_EssenceUL, 0, SMPTE_UL_LENGTH);
494   }
495
496   ~h__Writer(){}
497
498   Result_t OpenWrite(const char*, ui32_t HeaderSize);
499   Result_t SetSourceStream(const TimedTextDescriptor&);
500   Result_t WriteTimedTextResource(const std::string& XMLDoc, AESEncContext* = 0, HMACContext* = 0);
501   Result_t WriteAncillaryResource(const FrameBuffer&, AESEncContext* = 0, HMACContext* = 0);
502   Result_t Finalize();
503   Result_t TimedText_TDesc_to_MD(TimedText::TimedTextDescriptor& TDesc);
504 };
505
506 //
507 ASDCP::Result_t
508 ASDCP::TimedText::MXFWriter::h__Writer::TimedText_TDesc_to_MD(TimedText::TimedTextDescriptor& TDesc)
509 {
510   assert(m_EssenceDescriptor);
511   MXF::TimedTextDescriptor* TDescObj = (MXF::TimedTextDescriptor*)m_EssenceDescriptor;
512
513   TDescObj->SampleRate = TDesc.EditRate;
514   TDescObj->ContainerDuration = TDesc.ContainerDuration;
515   TDescObj->ResourceID.Set(TDesc.AssetID);
516   TDescObj->NamespaceURI = TDesc.NamespaceName;
517   TDescObj->UCSEncoding = TDesc.EncodingName;
518
519   return RESULT_OK;
520 }
521
522 //
523 ASDCP::Result_t
524 ASDCP::TimedText::MXFWriter::h__Writer::OpenWrite(char const* filename, ui32_t HeaderSize)
525 {
526   if ( ! m_State.Test_BEGIN() )
527     return RESULT_STATE;
528
529   Result_t result = m_File.OpenWrite(filename);
530
531   if ( ASDCP_SUCCESS(result) )
532     {
533       m_HeaderSize = HeaderSize;
534       m_EssenceDescriptor = new MXF::TimedTextDescriptor(m_Dict);
535       result = m_State.Goto_INIT();
536     }
537
538   return result;
539 }
540
541 //
542 ASDCP::Result_t
543 ASDCP::TimedText::MXFWriter::h__Writer::SetSourceStream(ASDCP::TimedText::TimedTextDescriptor const& TDesc)
544 {
545   if ( ! m_State.Test_INIT() )
546     return RESULT_STATE;
547
548   m_TDesc = TDesc;
549   ResourceList_t::const_iterator ri;
550   Result_t result = TimedText_TDesc_to_MD(m_TDesc);
551
552   for ( ri = m_TDesc.ResourceList.begin() ; ri != m_TDesc.ResourceList.end() && ASDCP_SUCCESS(result); ri++ )
553     {
554       TimedTextResourceSubDescriptor* resourceSubdescriptor = new TimedTextResourceSubDescriptor(m_Dict);
555       GenRandomValue(resourceSubdescriptor->InstanceUID);
556       resourceSubdescriptor->AncillaryResourceID.Set((*ri).ResourceID);
557       resourceSubdescriptor->MIMEMediaType = MIME2str((*ri).Type);
558       resourceSubdescriptor->EssenceStreamID = m_EssenceStreamID++;
559       m_EssenceSubDescriptorList.push_back((FileDescriptor*)resourceSubdescriptor);
560       m_EssenceDescriptor->SubDescriptors.push_back(resourceSubdescriptor->InstanceUID);
561     }
562
563   m_EssenceStreamID = 10;
564   assert(m_Dict);
565
566   if ( ASDCP_SUCCESS(result) )
567     {
568       InitHeader();
569       AddDMSegment(m_TDesc.EditRate, 24, TIMED_TEXT_DEF_LABEL,
570                    UL(m_Dict->ul(MDD_PictureDataDef)), TIMED_TEXT_PACKAGE_LABEL);
571
572       AddEssenceDescriptor(UL(m_Dict->ul(MDD_TimedTextWrapping)));
573
574       result = m_HeaderPart.WriteToFile(m_File, m_HeaderSize);
575       
576       if ( KM_SUCCESS(result) )
577         result = CreateBodyPart(m_TDesc.EditRate);
578     }
579
580   if ( ASDCP_SUCCESS(result) )
581     {
582       memcpy(m_EssenceUL, m_Dict->ul(MDD_TimedTextEssence), SMPTE_UL_LENGTH);
583       m_EssenceUL[SMPTE_UL_LENGTH-1] = 1; // first (and only) essence container
584       result = m_State.Goto_READY();
585     }
586
587   return result;
588 }
589
590 //
591 ASDCP::Result_t
592 ASDCP::TimedText::MXFWriter::h__Writer::WriteTimedTextResource(const std::string& XMLDoc,
593                                                                ASDCP::AESEncContext* Ctx, ASDCP::HMACContext* HMAC)
594 {
595   Result_t result = m_State.Goto_RUNNING();
596
597   if ( ASDCP_SUCCESS(result) )
598     {
599       // TODO: make sure it's XML
600
601       ui32_t str_size = XMLDoc.size();
602       FrameBuffer FrameBuf(str_size);
603       
604       memcpy(FrameBuf.Data(), XMLDoc.c_str(), str_size);
605       FrameBuf.Size(str_size);
606
607       IndexTableSegment::IndexEntry Entry;
608       Entry.StreamOffset = m_StreamOffset;
609       
610       if ( ASDCP_SUCCESS(result) )
611         result = WriteEKLVPacket(FrameBuf, m_EssenceUL, Ctx, HMAC);
612
613       if ( ASDCP_SUCCESS(result) )
614         {
615           m_FooterPart.PushIndexEntry(Entry);
616           m_FramesWritten++;
617         }
618     }
619
620   return result;
621 }
622
623
624 //
625 ASDCP::Result_t
626 ASDCP::TimedText::MXFWriter::h__Writer::WriteAncillaryResource(const ASDCP::TimedText::FrameBuffer& FrameBuf,
627                                                                ASDCP::AESEncContext* Ctx, ASDCP::HMACContext* HMAC)
628 {
629   if ( ! m_State.Test_RUNNING() )
630     return RESULT_STATE;
631
632   Kumu::fpos_t here = m_File.Tell();
633   assert(m_Dict);
634
635   // create generic stream partition header
636   static UL GenericStream_DataElement(m_Dict->ul(MDD_GenericStream_DataElement));
637   MXF::Partition GSPart(m_Dict);
638
639   GSPart.ThisPartition = here;
640   GSPart.PreviousPartition = m_HeaderPart.m_RIP.PairArray.back().ByteOffset;
641   GSPart.BodySID = m_EssenceStreamID;
642   GSPart.OperationalPattern = m_HeaderPart.OperationalPattern;
643
644   m_HeaderPart.m_RIP.PairArray.push_back(RIP::Pair(m_EssenceStreamID++, here));
645   GSPart.EssenceContainers.push_back(UL(m_Dict->ul(MDD_TimedTextEssence)));
646   UL TmpUL(m_Dict->ul(MDD_GenericStreamPartition));
647   Result_t result = GSPart.WriteToFile(m_File, TmpUL);
648
649   if ( ASDCP_SUCCESS(result) )
650     result = WriteEKLVPacket(FrameBuf, GenericStream_DataElement.Value(), Ctx, HMAC);
651
652   m_FramesWritten++;
653   return result;
654 }
655
656 //
657 ASDCP::Result_t
658 ASDCP::TimedText::MXFWriter::h__Writer::Finalize()
659 {
660   if ( ! m_State.Test_RUNNING() )
661     return RESULT_STATE;
662
663   m_FramesWritten = m_TDesc.ContainerDuration;
664   m_State.Goto_FINAL();
665
666   return WriteMXFFooter();
667 }
668
669
670 //------------------------------------------------------------------------------------------
671
672 ASDCP::TimedText::MXFWriter::MXFWriter()
673 {
674 }
675
676 ASDCP::TimedText::MXFWriter::~MXFWriter()
677 {
678 }
679
680 // Warning: direct manipulation of MXF structures can interfere
681 // with the normal operation of the wrapper.  Caveat emptor!
682 //
683 ASDCP::MXF::OPAtomHeader&
684 ASDCP::TimedText::MXFWriter::OPAtomHeader()
685 {
686   if ( m_Writer.empty() )
687     {
688       assert(g_OPAtomHeader);
689       return *g_OPAtomHeader;
690     }
691
692   return m_Writer->m_HeaderPart;
693 }
694
695 // Warning: direct manipulation of MXF structures can interfere
696 // with the normal operation of the wrapper.  Caveat emptor!
697 //
698 ASDCP::MXF::OPAtomIndexFooter&
699 ASDCP::TimedText::MXFWriter::OPAtomIndexFooter()
700 {
701   if ( m_Writer.empty() )
702     {
703       assert(g_OPAtomIndexFooter);
704       return *g_OPAtomIndexFooter;
705     }
706
707   return m_Writer->m_FooterPart;
708 }
709
710 // Open the file for writing. The file must not exist. Returns error if
711 // the operation cannot be completed.
712 ASDCP::Result_t
713 ASDCP::TimedText::MXFWriter::OpenWrite(const char* filename, const WriterInfo& Info,
714                                        const TimedTextDescriptor& TDesc, ui32_t HeaderSize)
715 {
716   if ( Info.LabelSetType != LS_MXF_SMPTE )
717     {
718       DefaultLogSink().Error("Timed Text support requires LS_MXF_SMPTE\n");
719       return RESULT_FORMAT;
720     }
721
722   m_Writer = new h__Writer(DefaultSMPTEDict());
723   m_Writer->m_Info = Info;
724   
725   Result_t result = m_Writer->OpenWrite(filename, HeaderSize);
726
727   if ( ASDCP_SUCCESS(result) )
728     result = m_Writer->SetSourceStream(TDesc);
729
730   if ( ASDCP_FAILURE(result) )
731     m_Writer.release();
732
733   return result;
734 }
735
736 //
737 ASDCP::Result_t
738 ASDCP::TimedText::MXFWriter::WriteTimedTextResource(const std::string& XMLDoc, AESEncContext* Ctx, HMACContext* HMAC)
739 {
740   if ( m_Writer.empty() )
741     return RESULT_INIT;
742
743   return m_Writer->WriteTimedTextResource(XMLDoc, Ctx, HMAC);
744 }
745
746 //
747 ASDCP::Result_t
748 ASDCP::TimedText::MXFWriter::WriteAncillaryResource(const FrameBuffer& FrameBuf, AESEncContext* Ctx, HMACContext* HMAC)
749 {
750   if ( m_Writer.empty() )
751     return RESULT_INIT;
752
753   return m_Writer->WriteAncillaryResource(FrameBuf, Ctx, HMAC);
754 }
755
756 // Closes the MXF file, writing the index and other closing information.
757 ASDCP::Result_t
758 ASDCP::TimedText::MXFWriter::Finalize()
759 {
760   if ( m_Writer.empty() )
761     return RESULT_INIT;
762
763   return m_Writer->Finalize();
764 }
765
766
767
768 //
769 // end AS_DCP_timedText.cpp
770 //