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