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