Bump patch version post tag.
[asdcplib.git] / src / AS_DCP_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_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 //
47 static const char*
48 MIME2str(TimedText::MIMEType_t m)
49 {
50   if ( m == TimedText::MT_PNG )
51     return "image/png";
52
53   else if( m == TimedText::MT_OPENTYPE )
54     return "application/x-font-opentype";
55   
56   return "application/octet-stream";
57 }
58
59 //
60 std::ostream&
61 ASDCP::TimedText::operator << (std::ostream& strm, const TimedTextDescriptor& TDesc)
62 {
63   UUID TmpID(TDesc.AssetID);
64   char buf[64];
65
66   strm << "         EditRate: " << (unsigned) TDesc.EditRate.Numerator << "/" << (unsigned) TDesc.EditRate.Denominator << std::endl;
67   strm << "ContainerDuration: " << (unsigned) TDesc.ContainerDuration << std::endl;
68   strm << "          AssetID: " << TmpID.EncodeHex(buf, 64) << std::endl;
69   strm << "    NamespaceName: " << TDesc.NamespaceName << std::endl;
70   strm << "    ResourceCount: " << (unsigned long) TDesc.ResourceList.size() << std::endl;
71
72   TimedText::ResourceList_t::const_iterator ri;
73   for ( ri = TDesc.ResourceList.begin() ; ri != TDesc.ResourceList.end(); ri++ )
74     {
75       TmpID.Set((*ri).ResourceID);
76       strm << "    " << TmpID.EncodeHex(buf, 64) << ": " << MIME2str((*ri).Type) << std::endl;
77     }
78
79   return strm;
80 }
81
82 //
83 void
84 ASDCP::TimedText::DescriptorDump(ASDCP::TimedText::TimedTextDescriptor const& TDesc, FILE* stream)
85 {
86   if ( stream == 0 )
87     stream = stderr;
88
89   UUID TmpID(TDesc.AssetID);
90   char buf[64];
91
92   fprintf(stream, "         EditRate: %u/%u\n", TDesc.EditRate.Numerator, TDesc.EditRate.Denominator);
93   fprintf(stream, "ContainerDuration: %u\n",    TDesc.ContainerDuration);
94   fprintf(stream, "          AssetID: %s\n",    TmpID.EncodeHex(buf, 64));
95   fprintf(stream, "    NamespaceName: %s\n",    TDesc.NamespaceName.c_str());
96   fprintf(stream, "    ResourceCount: %zu\n",   TDesc.ResourceList.size());
97
98   TimedText::ResourceList_t::const_iterator ri;
99   for ( ri = TDesc.ResourceList.begin() ; ri != TDesc.ResourceList.end(); ri++ )
100     {
101       TmpID.Set((*ri).ResourceID);
102       fprintf(stream, "    %s: %s\n",
103               TmpID.EncodeHex(buf, 64), 
104               MIME2str((*ri).Type));
105     }
106 }
107
108 //
109 void
110 ASDCP::TimedText::FrameBuffer::Dump(FILE* stream, ui32_t dump_len) const
111 {
112   if ( stream == 0 )
113     stream = stderr;
114
115   UUID TmpID(m_AssetID);
116   char buf[64];
117   fprintf(stream, "%s | %s | %u\n", TmpID.EncodeHex(buf, 64), m_MIMEType.c_str(), Size());
118
119   if ( dump_len > 0 )
120     Kumu::hexdump(m_Data, dump_len, stream);
121 }
122
123 //------------------------------------------------------------------------------------------
124
125 typedef std::map<UUID, UUID> ResourceMap_t;
126
127 class ASDCP::TimedText::MXFReader::h__Reader : public ASDCP::h__ASDCPReader
128 {
129   MXF::TimedTextDescriptor* m_EssenceDescriptor;
130   ResourceMap_t             m_ResourceMap;
131
132   ASDCP_NO_COPY_CONSTRUCT(h__Reader);
133
134 public:
135   TimedTextDescriptor m_TDesc;    
136
137   h__Reader(const Dictionary& d) : ASDCP::h__ASDCPReader(d), m_EssenceDescriptor(0) {
138     memset(&m_TDesc.AssetID, 0, UUIDlen);
139   }
140
141   virtual ~h__Reader() {}
142
143   Result_t    OpenRead(const std::string&);
144   Result_t    MD_to_TimedText_TDesc(TimedText::TimedTextDescriptor& TDesc);
145   Result_t    ReadTimedTextResource(FrameBuffer& FrameBuf, AESDecContext* Ctx, HMACContext* HMAC);
146   Result_t    ReadAncillaryResource(const byte_t*, FrameBuffer& FrameBuf, AESDecContext* Ctx, HMACContext* HMAC);
147 };
148
149 //
150 ASDCP::Result_t
151 ASDCP::TimedText::MXFReader::h__Reader::MD_to_TimedText_TDesc(TimedText::TimedTextDescriptor& TDesc)
152 {
153   assert(m_EssenceDescriptor);
154   memset(&m_TDesc.AssetID, 0, UUIDlen);
155   MXF::TimedTextDescriptor* TDescObj = (MXF::TimedTextDescriptor*)m_EssenceDescriptor;
156
157   TDesc.EditRate = TDescObj->SampleRate;
158   assert(TDescObj->ContainerDuration <= 0xFFFFFFFFL);
159   TDesc.ContainerDuration = (ui32_t) TDescObj->ContainerDuration;
160   memcpy(TDesc.AssetID, TDescObj->ResourceID.Value(), UUIDlen);
161   TDesc.NamespaceName = TDescObj->NamespaceURI;
162   TDesc.EncodingName = TDescObj->UCSEncoding;
163   TDesc.ResourceList.clear();
164
165   Array<UUID>::const_iterator sdi = TDescObj->SubDescriptors.begin();
166   TimedTextResourceSubDescriptor* DescObject = 0;
167   Result_t result = RESULT_OK;
168
169   for ( ; sdi != TDescObj->SubDescriptors.end() && KM_SUCCESS(result); sdi++ )
170     {
171       InterchangeObject* tmp_iobj = 0;
172       result = m_HeaderPart.GetMDObjectByID(*sdi, &tmp_iobj);
173       DescObject = static_cast<TimedTextResourceSubDescriptor*>(tmp_iobj);
174
175       if ( KM_SUCCESS(result) )
176         {
177           TimedTextResourceDescriptor TmpResource;
178           memcpy(TmpResource.ResourceID, DescObject->AncillaryResourceID.Value(), UUIDlen);
179
180           if ( DescObject->MIMEMediaType.find("application/x-font-opentype") != std::string::npos
181                || DescObject->MIMEMediaType.find("application/x-opentype") != std::string::npos
182                || DescObject->MIMEMediaType.find("font/opentype") != std::string::npos )
183             TmpResource.Type = MT_OPENTYPE;
184
185           else if ( DescObject->MIMEMediaType.find("image/png") != std::string::npos )
186             TmpResource.Type = MT_PNG;
187
188           else
189             TmpResource.Type = MT_BIN;
190
191           TDesc.ResourceList.push_back(TmpResource);
192           m_ResourceMap.insert(ResourceMap_t::value_type(DescObject->AncillaryResourceID, *sdi));
193         }
194       else
195         {
196           DefaultLogSink().Error("Broken sub-descriptor link\n");
197           return RESULT_FORMAT;
198         }
199     }
200
201   return result;
202 }
203
204 //
205 ASDCP::Result_t
206 ASDCP::TimedText::MXFReader::h__Reader::OpenRead(const std::string& filename)
207 {
208   Result_t result = OpenMXFRead(filename);
209   
210   if( ASDCP_SUCCESS(result) )
211     {
212       if ( m_EssenceDescriptor == 0 )
213         {
214           InterchangeObject* tmp_iobj = 0;
215           result = m_HeaderPart.GetMDObjectByType(OBJ_TYPE_ARGS(TimedTextDescriptor), &tmp_iobj);
216           m_EssenceDescriptor = static_cast<MXF::TimedTextDescriptor*>(tmp_iobj);
217         }
218
219       if( ASDCP_SUCCESS(result) )
220         result = MD_to_TimedText_TDesc(m_TDesc);
221     }
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   assert(m_Dict);
235   Result_t result = ReadEKLVFrame(0, FrameBuf, m_Dict->ul(MDD_TimedTextEssence), Ctx, HMAC);
236
237  if( ASDCP_SUCCESS(result) )
238    {
239      FrameBuf.AssetID(m_TDesc.AssetID);
240      FrameBuf.MIMEType("text/xml");
241    }
242
243  return result;
244 }
245
246 //
247 ASDCP::Result_t
248 ASDCP::TimedText::MXFReader::h__Reader::ReadAncillaryResource(const byte_t* uuid, FrameBuffer& frame_buf,
249                                                               AESDecContext* Ctx, HMACContext* HMAC)
250 {
251   KM_TEST_NULL_L(uuid);
252   UUID RID(uuid);
253
254   ResourceMap_t::const_iterator ri = m_ResourceMap.find(RID);
255   if ( ri == m_ResourceMap.end() )
256     {
257       char buf[64];
258       DefaultLogSink().Error("No such resource: %s\n", RID.EncodeHex(buf, 64));
259       return RESULT_RANGE;
260     }
261
262   // get the subdescriptor
263   InterchangeObject* tmp_iobj = 0;
264   Result_t result = m_HeaderPart.GetMDObjectByID((*ri).second, &tmp_iobj);
265   TimedTextResourceSubDescriptor* desc_object = dynamic_cast<TimedTextResourceSubDescriptor*>(tmp_iobj);
266
267   if ( KM_SUCCESS(result) )
268     {
269       assert(desc_object);
270       result = ReadGenericStreamPartitionPayload(desc_object->EssenceStreamID, frame_buf, Ctx, HMAC);
271     }
272
273   if ( KM_SUCCESS(result) )
274     {
275       frame_buf.AssetID(uuid);
276       frame_buf.MIMEType(desc_object->MIMEMediaType);
277     }
278
279   return result;
280 }
281
282
283 //------------------------------------------------------------------------------------------
284
285 ASDCP::TimedText::MXFReader::MXFReader()
286 {
287   m_Reader = new h__Reader(DefaultSMPTEDict());
288 }
289
290
291 ASDCP::TimedText::MXFReader::~MXFReader()
292 {
293 }
294
295 // Warning: direct manipulation of MXF structures can interfere
296 // with the normal operation of the wrapper.  Caveat emptor!
297 //
298 ASDCP::MXF::OP1aHeader&
299 ASDCP::TimedText::MXFReader::OP1aHeader()
300 {
301   if ( m_Reader.empty() )
302     {
303       assert(g_OP1aHeader);
304       return *g_OP1aHeader;
305     }
306
307   return m_Reader->m_HeaderPart;
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::OPAtomIndexFooter&
314 ASDCP::TimedText::MXFReader::OPAtomIndexFooter()
315 {
316   if ( m_Reader.empty() )
317     {
318       assert(g_OPAtomIndexFooter);
319       return *g_OPAtomIndexFooter;
320     }
321
322   return m_Reader->m_IndexAccess;
323 }
324
325 // Warning: direct manipulation of MXF structures can interfere
326 // with the normal operation of the wrapper.  Caveat emptor!
327 //
328 ASDCP::MXF::RIP&
329 ASDCP::TimedText::MXFReader::RIP()
330 {
331   if ( m_Reader.empty() )
332     {
333       assert(g_RIP);
334       return *g_RIP;
335     }
336
337   return m_Reader->m_RIP;
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 std::string& 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::FillTimedTextDescriptor(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_IndexAccess.Dump(stream);
428 }
429
430 //
431 ASDCP::Result_t
432 ASDCP::TimedText::MXFReader::Close() const
433 {
434   if ( m_Reader && m_Reader->m_File.IsOpen() )
435     {
436       m_Reader->Close();
437       return RESULT_OK;
438     }
439
440   return RESULT_INIT;
441 }
442
443
444 //------------------------------------------------------------------------------------------
445
446
447 //
448 class ASDCP::TimedText::MXFWriter::h__Writer : public ASDCP::h__ASDCPWriter
449 {
450   ASDCP_NO_COPY_CONSTRUCT(h__Writer);
451   h__Writer();
452
453 public:
454   TimedTextDescriptor m_TDesc;
455   byte_t              m_EssenceUL[SMPTE_UL_LENGTH];
456   ui32_t              m_EssenceStreamID;
457
458   h__Writer(const Dictionary& d) : ASDCP::h__ASDCPWriter(d), m_EssenceStreamID(10) {
459     memset(m_EssenceUL, 0, SMPTE_UL_LENGTH);
460   }
461
462   virtual ~h__Writer() {}
463
464   Result_t OpenWrite(const std::string&, ui32_t HeaderSize);
465   Result_t SetSourceStream(const TimedTextDescriptor&);
466   Result_t WriteTimedTextResource(const std::string& XMLDoc, AESEncContext* = 0, HMACContext* = 0);
467   Result_t WriteAncillaryResource(const FrameBuffer&, AESEncContext* = 0, HMACContext* = 0);
468   Result_t Finalize();
469   Result_t TimedText_TDesc_to_MD(TimedText::TimedTextDescriptor& TDesc);
470 };
471
472 //
473 ASDCP::Result_t
474 ASDCP::TimedText::MXFWriter::h__Writer::TimedText_TDesc_to_MD(TimedText::TimedTextDescriptor& TDesc)
475 {
476   assert(m_EssenceDescriptor);
477   MXF::TimedTextDescriptor* TDescObj = (MXF::TimedTextDescriptor*)m_EssenceDescriptor;
478
479   TDescObj->SampleRate = TDesc.EditRate;
480   TDescObj->ContainerDuration = TDesc.ContainerDuration;
481   TDescObj->ResourceID.Set(TDesc.AssetID);
482   TDescObj->NamespaceURI = TDesc.NamespaceName;
483   TDescObj->UCSEncoding = TDesc.EncodingName;
484
485   return RESULT_OK;
486 }
487
488 //
489 ASDCP::Result_t
490 ASDCP::TimedText::MXFWriter::h__Writer::OpenWrite(const std::string& filename, ui32_t HeaderSize)
491 {
492   if ( ! m_State.Test_BEGIN() )
493     return RESULT_STATE;
494
495   Result_t result = m_File.OpenWrite(filename);
496
497   if ( ASDCP_SUCCESS(result) )
498     {
499       m_HeaderSize = HeaderSize;
500       m_EssenceDescriptor = new MXF::TimedTextDescriptor(m_Dict);
501       result = m_State.Goto_INIT();
502     }
503
504   return result;
505 }
506
507 //
508 ASDCP::Result_t
509 ASDCP::TimedText::MXFWriter::h__Writer::SetSourceStream(ASDCP::TimedText::TimedTextDescriptor const& TDesc)
510 {
511   if ( ! m_State.Test_INIT() )
512     return RESULT_STATE;
513
514   m_TDesc = TDesc;
515   ResourceList_t::const_iterator ri;
516   Result_t result = TimedText_TDesc_to_MD(m_TDesc);
517
518   for ( ri = m_TDesc.ResourceList.begin() ; ri != m_TDesc.ResourceList.end() && ASDCP_SUCCESS(result); ri++ )
519     {
520       TimedTextResourceSubDescriptor* resourceSubdescriptor = new TimedTextResourceSubDescriptor(m_Dict);
521       GenRandomValue(resourceSubdescriptor->InstanceUID);
522       resourceSubdescriptor->AncillaryResourceID.Set((*ri).ResourceID);
523       resourceSubdescriptor->MIMEMediaType = MIME2str((*ri).Type);
524       resourceSubdescriptor->EssenceStreamID = m_EssenceStreamID++;
525       m_EssenceSubDescriptorList.push_back((FileDescriptor*)resourceSubdescriptor);
526       m_EssenceDescriptor->SubDescriptors.push_back(resourceSubdescriptor->InstanceUID);
527
528       // 72 == sizeof K, L, instanceuid, uuid + sizeof int32 + tag/len * 4
529       m_HeaderSize += ( resourceSubdescriptor->MIMEMediaType.ArchiveLength() * 2 /*ArchiveLength is broken*/ ) + 72;
530     }
531
532   m_EssenceStreamID = 10;
533   assert(m_Dict);
534
535   if ( ASDCP_SUCCESS(result) )
536     {
537       InitHeader(MXFVersion_2004);
538
539       // First RIP Entry
540       if ( m_Info.LabelSetType == LS_MXF_SMPTE )  // ERK
541         {
542           m_RIP.PairArray.push_back(RIP::PartitionPair(0, 0)); // 3-part, no essence in header
543         }
544       else
545         {
546           DefaultLogSink().Error("Unable to write Interop timed-text MXF file.  Use SMPTE DCP options instead.\n");
547           return RESULT_FORMAT;
548         }
549
550       // timecode rate and essence rate are the same
551       AddSourceClip(m_TDesc.EditRate, m_TDesc.EditRate, derive_timecode_rate_from_edit_rate(m_TDesc.EditRate),
552                     TIMED_TEXT_DEF_LABEL, m_EssenceUL, UL(m_Dict->ul(MDD_DataDataDef)), TIMED_TEXT_PACKAGE_LABEL);
553
554       AddEssenceDescriptor(UL(m_Dict->ul(MDD_TimedTextWrappingClip)));
555       result = m_HeaderPart.WriteToFile(m_File, m_HeaderSize);
556       
557       if ( KM_SUCCESS(result) )
558         result = CreateBodyPart(m_TDesc.EditRate);
559     }
560
561   if ( ASDCP_SUCCESS(result) )
562     {
563       memcpy(m_EssenceUL, m_Dict->ul(MDD_TimedTextEssence), SMPTE_UL_LENGTH);
564       m_EssenceUL[SMPTE_UL_LENGTH-1] = 1; // first (and only) essence container
565       result = m_State.Goto_READY();
566     }
567
568   return result;
569 }
570
571 //
572 ASDCP::Result_t
573 ASDCP::TimedText::MXFWriter::h__Writer::WriteTimedTextResource(const std::string& XMLDoc,
574                                                                ASDCP::AESEncContext* Ctx, ASDCP::HMACContext* HMAC)
575 {
576   Result_t result = m_State.Goto_RUNNING();
577
578   if ( ASDCP_SUCCESS(result) )
579     {
580       // TODO: make sure it's XML
581
582       ui32_t str_size = XMLDoc.size();
583       FrameBuffer FrameBuf(str_size);
584       
585       memcpy(FrameBuf.Data(), XMLDoc.c_str(), str_size);
586       FrameBuf.Size(str_size);
587
588       IndexTableSegment::IndexEntry Entry;
589       Entry.StreamOffset = m_StreamOffset;
590       result = WriteEKLVPacket(FrameBuf, m_EssenceUL, MXF_BER_LENGTH, Ctx, HMAC);
591
592       if ( ASDCP_SUCCESS(result) )
593         {
594           m_FooterPart.PushIndexEntry(Entry);
595           m_FramesWritten++;
596         }
597     }
598
599   return result;
600 }
601
602
603 //
604 ASDCP::Result_t
605 ASDCP::TimedText::MXFWriter::h__Writer::WriteAncillaryResource(const ASDCP::TimedText::FrameBuffer& FrameBuf,
606                                                                ASDCP::AESEncContext* Ctx, ASDCP::HMACContext* HMAC)
607 {
608   if ( ! m_State.Test_RUNNING() )
609     return RESULT_STATE;
610
611   Kumu::fpos_t here = m_File.Tell();
612   assert(m_Dict);
613
614   // create generic stream partition header
615   static UL GenericStream_DataElement(m_Dict->ul(MDD_GenericStream_DataElement));
616   MXF::Partition GSPart(m_Dict);
617
618   GSPart.ThisPartition = here;
619   GSPart.PreviousPartition = m_RIP.PairArray.back().ByteOffset;
620   GSPart.BodySID = m_EssenceStreamID;
621   GSPart.OperationalPattern = m_HeaderPart.OperationalPattern;
622
623   m_RIP.PairArray.push_back(RIP::PartitionPair(m_EssenceStreamID++, here));
624   GSPart.EssenceContainers = m_HeaderPart.EssenceContainers;
625   UL TmpUL(m_Dict->ul(MDD_GenericStreamPartition));
626   Result_t result = GSPart.WriteToFile(m_File, TmpUL);
627
628   if ( ASDCP_SUCCESS(result) )
629     result = WriteEKLVPacket(FrameBuf, GenericStream_DataElement.Value(), MXF_BER_LENGTH, Ctx, HMAC);
630
631   m_FramesWritten++;
632   return result;
633 }
634
635 //
636 ASDCP::Result_t
637 ASDCP::TimedText::MXFWriter::h__Writer::Finalize()
638 {
639   if ( ! m_State.Test_RUNNING() )
640     return RESULT_STATE;
641
642   m_FramesWritten = m_TDesc.ContainerDuration;
643   m_State.Goto_FINAL();
644
645   return WriteASDCPFooter();
646 }
647
648
649 //------------------------------------------------------------------------------------------
650
651 ASDCP::TimedText::MXFWriter::MXFWriter()
652 {
653 }
654
655 ASDCP::TimedText::MXFWriter::~MXFWriter()
656 {
657 }
658
659 // Warning: direct manipulation of MXF structures can interfere
660 // with the normal operation of the wrapper.  Caveat emptor!
661 //
662 ASDCP::MXF::OP1aHeader&
663 ASDCP::TimedText::MXFWriter::OP1aHeader()
664 {
665   if ( m_Writer.empty() )
666     {
667       assert(g_OP1aHeader);
668       return *g_OP1aHeader;
669     }
670
671   return m_Writer->m_HeaderPart;
672 }
673
674 // Warning: direct manipulation of MXF structures can interfere
675 // with the normal operation of the wrapper.  Caveat emptor!
676 //
677 ASDCP::MXF::OPAtomIndexFooter&
678 ASDCP::TimedText::MXFWriter::OPAtomIndexFooter()
679 {
680   if ( m_Writer.empty() )
681     {
682       assert(g_OPAtomIndexFooter);
683       return *g_OPAtomIndexFooter;
684     }
685
686   return m_Writer->m_FooterPart;
687 }
688
689 // Warning: direct manipulation of MXF structures can interfere
690 // with the normal operation of the wrapper.  Caveat emptor!
691 //
692 ASDCP::MXF::RIP&
693 ASDCP::TimedText::MXFWriter::RIP()
694 {
695   if ( m_Writer.empty() )
696     {
697       assert(g_RIP);
698       return *g_RIP;
699     }
700
701   return m_Writer->m_RIP;
702 }
703
704 // Open the file for writing. The file must not exist. Returns error if
705 // the operation cannot be completed.
706 ASDCP::Result_t
707 ASDCP::TimedText::MXFWriter::OpenWrite(const std::string& filename, const WriterInfo& Info,
708                                        const TimedTextDescriptor& TDesc, ui32_t HeaderSize)
709 {
710   if ( Info.LabelSetType != LS_MXF_SMPTE )
711     {
712       DefaultLogSink().Error("Timed Text support requires LS_MXF_SMPTE\n");
713       return RESULT_FORMAT;
714     }
715
716   m_Writer = new h__Writer(DefaultSMPTEDict());
717   m_Writer->m_Info = Info;
718   
719   Result_t result = m_Writer->OpenWrite(filename, HeaderSize);
720
721   if ( ASDCP_SUCCESS(result) )
722     result = m_Writer->SetSourceStream(TDesc);
723
724   if ( ASDCP_FAILURE(result) )
725     m_Writer.release();
726
727   return result;
728 }
729
730 //
731 ASDCP::Result_t
732 ASDCP::TimedText::MXFWriter::WriteTimedTextResource(const std::string& XMLDoc, AESEncContext* Ctx, HMACContext* HMAC)
733 {
734   if ( m_Writer.empty() )
735     return RESULT_INIT;
736
737   return m_Writer->WriteTimedTextResource(XMLDoc, Ctx, HMAC);
738 }
739
740 //
741 ASDCP::Result_t
742 ASDCP::TimedText::MXFWriter::WriteAncillaryResource(const FrameBuffer& FrameBuf, AESEncContext* Ctx, HMACContext* HMAC)
743 {
744   if ( m_Writer.empty() )
745     return RESULT_INIT;
746
747   return m_Writer->WriteAncillaryResource(FrameBuf, Ctx, HMAC);
748 }
749
750 // Closes the MXF file, writing the index and other closing information.
751 ASDCP::Result_t
752 ASDCP::TimedText::MXFWriter::Finalize()
753 {
754   if ( m_Writer.empty() )
755     return RESULT_INIT;
756
757   return m_Writer->Finalize();
758 }
759
760
761
762 //
763 // end AS_DCP_timedText.cpp
764 //