rrrrr
[asdcplib.git] / src / AS_DCP_TimedText.cpp
1 /*
2 Copyright (c) 2008-2009, 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 // Open the file for reading. The file must exist. Returns error if the
345 // operation cannot be completed.
346 ASDCP::Result_t
347 ASDCP::TimedText::MXFReader::OpenRead(const char* filename) const
348 {
349   return m_Reader->OpenRead(filename);
350 }
351
352 // Fill the struct with the values from the file's header.
353 // Returns RESULT_INIT if the file is not open.
354 ASDCP::Result_t
355 ASDCP::TimedText::MXFReader::FillTimedTextDescriptor(TimedText::TimedTextDescriptor& TDesc) const
356 {
357   if ( m_Reader && m_Reader->m_File.IsOpen() )
358     {
359       TDesc = m_Reader->m_TDesc;
360       return RESULT_OK;
361     }
362
363   return RESULT_INIT;
364 }
365
366 // Fill the struct with the values from the file's header.
367 // Returns RESULT_INIT if the file is not open.
368 ASDCP::Result_t
369 ASDCP::TimedText::MXFReader::FillWriterInfo(WriterInfo& Info) const
370 {
371   if ( m_Reader && m_Reader->m_File.IsOpen() )
372     {
373       Info = m_Reader->m_Info;
374       return RESULT_OK;
375     }
376
377   return RESULT_INIT;
378 }
379
380 //
381 ASDCP::Result_t
382 ASDCP::TimedText::MXFReader::ReadTimedTextResource(std::string& s, AESDecContext* Ctx, HMACContext* HMAC) const
383 {
384   FrameBuffer FrameBuf(2*Kumu::Megabyte);
385
386   Result_t result = ReadTimedTextResource(FrameBuf, Ctx, HMAC);
387
388   if ( ASDCP_SUCCESS(result) )
389     s.assign((char*)FrameBuf.Data(), FrameBuf.Size());
390
391   return result;
392 }
393
394 //
395 ASDCP::Result_t
396 ASDCP::TimedText::MXFReader::ReadTimedTextResource(FrameBuffer& FrameBuf,
397                                                    AESDecContext* Ctx, HMACContext* HMAC) const
398 {
399   if ( m_Reader && m_Reader->m_File.IsOpen() )
400     return m_Reader->ReadTimedTextResource(FrameBuf, Ctx, HMAC);
401
402   return RESULT_INIT;
403 }
404
405 //
406 ASDCP::Result_t
407 ASDCP::TimedText::MXFReader::ReadAncillaryResource(const byte_t* uuid, FrameBuffer& FrameBuf,
408                                                    AESDecContext* Ctx, HMACContext* HMAC) const
409 {
410   if ( m_Reader && m_Reader->m_File.IsOpen() )
411     return m_Reader->ReadAncillaryResource(uuid, FrameBuf, Ctx, HMAC);
412
413   return RESULT_INIT;
414 }
415
416
417 //
418 void
419 ASDCP::TimedText::MXFReader::DumpHeaderMetadata(FILE* stream) const
420 {
421   if ( m_Reader->m_File.IsOpen() )
422     m_Reader->m_HeaderPart.Dump(stream);
423 }
424
425
426 //
427 void
428 ASDCP::TimedText::MXFReader::DumpIndex(FILE* stream) const
429 {
430   if ( m_Reader->m_File.IsOpen() )
431     m_Reader->m_FooterPart.Dump(stream);
432 }
433
434 //------------------------------------------------------------------------------------------
435
436
437 //
438 class ASDCP::TimedText::MXFWriter::h__Writer : public ASDCP::h__Writer
439 {
440   ASDCP_NO_COPY_CONSTRUCT(h__Writer);
441   h__Writer();
442
443 public:
444   TimedTextDescriptor m_TDesc;
445   byte_t              m_EssenceUL[SMPTE_UL_LENGTH];
446   ui32_t              m_EssenceStreamID;
447
448   h__Writer(const Dictionary& d) : ASDCP::h__Writer(d), m_EssenceStreamID(10) {
449     memset(m_EssenceUL, 0, SMPTE_UL_LENGTH);
450   }
451
452   ~h__Writer(){}
453
454   Result_t OpenWrite(const char*, ui32_t HeaderSize);
455   Result_t SetSourceStream(const TimedTextDescriptor&);
456   Result_t WriteTimedTextResource(const std::string& XMLDoc, AESEncContext* = 0, HMACContext* = 0);
457   Result_t WriteAncillaryResource(const FrameBuffer&, AESEncContext* = 0, HMACContext* = 0);
458   Result_t Finalize();
459   Result_t TimedText_TDesc_to_MD(TimedText::TimedTextDescriptor& TDesc);
460 };
461
462 //
463 ASDCP::Result_t
464 ASDCP::TimedText::MXFWriter::h__Writer::TimedText_TDesc_to_MD(TimedText::TimedTextDescriptor& TDesc)
465 {
466   assert(m_EssenceDescriptor);
467   MXF::TimedTextDescriptor* TDescObj = (MXF::TimedTextDescriptor*)m_EssenceDescriptor;
468
469   TDescObj->SampleRate = TDesc.EditRate;
470   TDescObj->ContainerDuration = TDesc.ContainerDuration;
471   TDescObj->ResourceID.Set(TDesc.AssetID);
472   TDescObj->NamespaceURI = TDesc.NamespaceName;
473   TDescObj->UCSEncoding = TDesc.EncodingName;
474
475   return RESULT_OK;
476 }
477
478 //
479 ASDCP::Result_t
480 ASDCP::TimedText::MXFWriter::h__Writer::OpenWrite(char const* filename, ui32_t HeaderSize)
481 {
482   if ( ! m_State.Test_BEGIN() )
483     return RESULT_STATE;
484
485   Result_t result = m_File.OpenWrite(filename);
486
487   if ( ASDCP_SUCCESS(result) )
488     {
489       m_HeaderSize = HeaderSize;
490       m_EssenceDescriptor = new MXF::TimedTextDescriptor(m_Dict);
491       result = m_State.Goto_INIT();
492     }
493
494   return result;
495 }
496
497 //
498 ASDCP::Result_t
499 ASDCP::TimedText::MXFWriter::h__Writer::SetSourceStream(ASDCP::TimedText::TimedTextDescriptor const& TDesc)
500 {
501   if ( ! m_State.Test_INIT() )
502     return RESULT_STATE;
503
504   m_TDesc = TDesc;
505   ResourceList_t::const_iterator ri;
506   Result_t result = TimedText_TDesc_to_MD(m_TDesc);
507
508   for ( ri = m_TDesc.ResourceList.begin() ; ri != m_TDesc.ResourceList.end() && ASDCP_SUCCESS(result); ri++ )
509     {
510       TimedTextResourceSubDescriptor* resourceSubdescriptor = new TimedTextResourceSubDescriptor(m_Dict);
511       GenRandomValue(resourceSubdescriptor->InstanceUID);
512       resourceSubdescriptor->AncillaryResourceID.Set((*ri).ResourceID);
513       resourceSubdescriptor->MIMEMediaType = MIME2str((*ri).Type);
514       resourceSubdescriptor->EssenceStreamID = m_EssenceStreamID++;
515       m_EssenceSubDescriptorList.push_back((FileDescriptor*)resourceSubdescriptor);
516       m_EssenceDescriptor->SubDescriptors.push_back(resourceSubdescriptor->InstanceUID);
517     }
518
519   m_EssenceStreamID = 10;
520   assert(m_Dict);
521
522   if ( ASDCP_SUCCESS(result) )
523     {
524       InitHeader();
525       AddDMSegment(m_TDesc.EditRate, 24, TIMED_TEXT_DEF_LABEL,
526                    UL(m_Dict->ul(MDD_PictureDataDef)), TIMED_TEXT_PACKAGE_LABEL);
527
528       AddEssenceDescriptor(UL(m_Dict->ul(MDD_TimedTextWrapping)));
529
530       result = m_HeaderPart.WriteToFile(m_File, m_HeaderSize);
531       
532       if ( KM_SUCCESS(result) )
533         result = CreateBodyPart(m_TDesc.EditRate);
534     }
535
536   if ( ASDCP_SUCCESS(result) )
537     {
538       memcpy(m_EssenceUL, m_Dict->ul(MDD_TimedTextEssence), SMPTE_UL_LENGTH);
539       m_EssenceUL[SMPTE_UL_LENGTH-1] = 1; // first (and only) essence container
540       result = m_State.Goto_READY();
541     }
542
543   return result;
544 }
545
546 //
547 ASDCP::Result_t
548 ASDCP::TimedText::MXFWriter::h__Writer::WriteTimedTextResource(const std::string& XMLDoc,
549                                                                ASDCP::AESEncContext* Ctx, ASDCP::HMACContext* HMAC)
550 {
551   Result_t result = m_State.Goto_RUNNING();
552
553   if ( ASDCP_SUCCESS(result) )
554     {
555       // TODO: make sure it's XML
556
557       ui32_t str_size = XMLDoc.size();
558       FrameBuffer FrameBuf(str_size);
559       
560       memcpy(FrameBuf.Data(), XMLDoc.c_str(), str_size);
561       FrameBuf.Size(str_size);
562
563       IndexTableSegment::IndexEntry Entry;
564       Entry.StreamOffset = m_StreamOffset;
565       
566       if ( ASDCP_SUCCESS(result) )
567         result = WriteEKLVPacket(FrameBuf, m_EssenceUL, Ctx, HMAC);
568
569       if ( ASDCP_SUCCESS(result) )
570         {
571           m_FooterPart.PushIndexEntry(Entry);
572           m_FramesWritten++;
573         }
574     }
575
576   return result;
577 }
578
579
580 //
581 ASDCP::Result_t
582 ASDCP::TimedText::MXFWriter::h__Writer::WriteAncillaryResource(const ASDCP::TimedText::FrameBuffer& FrameBuf,
583                                                                ASDCP::AESEncContext* Ctx, ASDCP::HMACContext* HMAC)
584 {
585   if ( ! m_State.Test_RUNNING() )
586     return RESULT_STATE;
587
588   Kumu::fpos_t here = m_File.Tell();
589   assert(m_Dict);
590
591   // create generic stream partition header
592   static UL GenericStream_DataElement(m_Dict->ul(MDD_GenericStream_DataElement));
593   MXF::Partition GSPart(m_Dict);
594
595   GSPart.ThisPartition = here;
596   GSPart.PreviousPartition = m_HeaderPart.m_RIP.PairArray.back().ByteOffset;
597   GSPart.BodySID = m_EssenceStreamID;
598   GSPart.OperationalPattern = m_HeaderPart.OperationalPattern;
599
600   m_HeaderPart.m_RIP.PairArray.push_back(RIP::Pair(m_EssenceStreamID++, here));
601   GSPart.EssenceContainers.push_back(UL(m_Dict->ul(MDD_TimedTextEssence)));
602   UL TmpUL(m_Dict->ul(MDD_GenericStreamPartition));
603   Result_t result = GSPart.WriteToFile(m_File, TmpUL);
604
605   if ( ASDCP_SUCCESS(result) )
606     result = WriteEKLVPacket(FrameBuf, GenericStream_DataElement.Value(), Ctx, HMAC);
607
608   m_FramesWritten++;
609   return result;
610 }
611
612 //
613 ASDCP::Result_t
614 ASDCP::TimedText::MXFWriter::h__Writer::Finalize()
615 {
616   if ( ! m_State.Test_RUNNING() )
617     return RESULT_STATE;
618
619   m_FramesWritten = m_TDesc.ContainerDuration;
620   m_State.Goto_FINAL();
621
622   return WriteMXFFooter();
623 }
624
625
626 //------------------------------------------------------------------------------------------
627
628 ASDCP::TimedText::MXFWriter::MXFWriter()
629 {
630 }
631
632 ASDCP::TimedText::MXFWriter::~MXFWriter()
633 {
634 }
635
636
637 // Open the file for writing. The file must not exist. Returns error if
638 // the operation cannot be completed.
639 ASDCP::Result_t
640 ASDCP::TimedText::MXFWriter::OpenWrite(const char* filename, const WriterInfo& Info,
641                                        const TimedTextDescriptor& TDesc, ui32_t HeaderSize)
642 {
643   if ( Info.LabelSetType != LS_MXF_SMPTE )
644     {
645       DefaultLogSink().Error("Timed Text support requires LS_MXF_SMPTE\n");
646       return RESULT_FORMAT;
647     }
648
649   m_Writer = new h__Writer(DefaultSMPTEDict());
650   m_Writer->m_Info = Info;
651   
652   Result_t result = m_Writer->OpenWrite(filename, HeaderSize);
653
654   if ( ASDCP_SUCCESS(result) )
655     result = m_Writer->SetSourceStream(TDesc);
656
657   if ( ASDCP_FAILURE(result) )
658     m_Writer.release();
659
660   return result;
661 }
662
663 //
664 ASDCP::Result_t
665 ASDCP::TimedText::MXFWriter::WriteTimedTextResource(const std::string& XMLDoc, AESEncContext* Ctx, HMACContext* HMAC)
666 {
667   if ( m_Writer.empty() )
668     return RESULT_INIT;
669
670   return m_Writer->WriteTimedTextResource(XMLDoc, Ctx, HMAC);
671 }
672
673 //
674 ASDCP::Result_t
675 ASDCP::TimedText::MXFWriter::WriteAncillaryResource(const FrameBuffer& FrameBuf, AESEncContext* Ctx, HMACContext* HMAC)
676 {
677   if ( m_Writer.empty() )
678     return RESULT_INIT;
679
680   return m_Writer->WriteAncillaryResource(FrameBuf, Ctx, HMAC);
681 }
682
683 // Closes the MXF file, writing the index and other closing information.
684 ASDCP::Result_t
685 ASDCP::TimedText::MXFWriter::Finalize()
686 {
687   if ( m_Writer.empty() )
688     return RESULT_INIT;
689
690   return m_Writer->Finalize();
691 }
692
693
694
695 //
696 // end AS_DCP_timedText.cpp
697 //