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