Release me
[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
164   Array<UUID>::const_iterator sdi = TDescObj->SubDescriptors.begin();
165   TimedTextResourceSubDescriptor* DescObject = 0;
166   Result_t result = RESULT_OK;
167
168   for ( ; sdi != TDescObj->SubDescriptors.end() && KM_SUCCESS(result); sdi++ )
169     {
170       InterchangeObject* tmp_iobj = 0;
171       result = m_HeaderPart.GetMDObjectByID(*sdi, &tmp_iobj);
172       DescObject = static_cast<TimedTextResourceSubDescriptor*>(tmp_iobj);
173
174       if ( KM_SUCCESS(result) )
175         {
176           TimedTextResourceDescriptor TmpResource;
177           memcpy(TmpResource.ResourceID, DescObject->AncillaryResourceID.Value(), UUIDlen);
178
179           if ( DescObject->MIMEMediaType.find("application/x-font-opentype") != std::string::npos
180                || DescObject->MIMEMediaType.find("application/x-opentype") != std::string::npos
181                || DescObject->MIMEMediaType.find("font/opentype") != std::string::npos )
182             TmpResource.Type = MT_OPENTYPE;
183
184           else if ( DescObject->MIMEMediaType.find("image/png") != std::string::npos )
185             TmpResource.Type = MT_PNG;
186
187           else
188             TmpResource.Type = MT_BIN;
189
190           TDesc.ResourceList.push_back(TmpResource);
191           m_ResourceMap.insert(ResourceMap_t::value_type(DescObject->AncillaryResourceID, *sdi));
192         }
193       else
194         {
195           DefaultLogSink().Error("Broken sub-descriptor link\n");
196           return RESULT_FORMAT;
197         }
198     }
199
200   return result;
201 }
202
203 //
204 ASDCP::Result_t
205 ASDCP::TimedText::MXFReader::h__Reader::OpenRead(const std::string& filename)
206 {
207   Result_t result = OpenMXFRead(filename);
208   
209   if( ASDCP_SUCCESS(result) )
210     {
211       if ( m_EssenceDescriptor == 0 )
212         {
213           InterchangeObject* tmp_iobj = 0;
214           result = m_HeaderPart.GetMDObjectByType(OBJ_TYPE_ARGS(TimedTextDescriptor), &tmp_iobj);
215           m_EssenceDescriptor = static_cast<MXF::TimedTextDescriptor*>(tmp_iobj);
216         }
217
218       if( ASDCP_SUCCESS(result) )
219         result = MD_to_TimedText_TDesc(m_TDesc);
220     }
221
222   return result;
223 }
224
225 //
226 ASDCP::Result_t
227 ASDCP::TimedText::MXFReader::h__Reader::ReadTimedTextResource(FrameBuffer& FrameBuf,
228                                                               AESDecContext* Ctx, HMACContext* HMAC)
229 {
230   if ( ! m_File.IsOpen() )
231     return RESULT_INIT;
232
233   assert(m_Dict);
234   Result_t result = ReadEKLVFrame(0, FrameBuf, m_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& frame_buf,
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   // get the subdescriptor
262   InterchangeObject* tmp_iobj = 0;
263   Result_t result = m_HeaderPart.GetMDObjectByID((*ri).second, &tmp_iobj);
264   TimedTextResourceSubDescriptor* desc_object = dynamic_cast<TimedTextResourceSubDescriptor*>(tmp_iobj);
265
266   if ( KM_SUCCESS(result) )
267     {
268       assert(desc_object);
269       result = ReadGenericStreamPartitionPayload(desc_object->EssenceStreamID, frame_buf, Ctx, HMAC);
270     }
271
272   if ( KM_SUCCESS(result) )
273     {
274       frame_buf.AssetID(uuid);
275       frame_buf.MIMEType(desc_object->MIMEMediaType);
276     }
277
278   return result;
279 }
280
281
282 //------------------------------------------------------------------------------------------
283
284 ASDCP::TimedText::MXFReader::MXFReader()
285 {
286   m_Reader = new h__Reader(DefaultSMPTEDict());
287 }
288
289
290 ASDCP::TimedText::MXFReader::~MXFReader()
291 {
292 }
293
294 // Warning: direct manipulation of MXF structures can interfere
295 // with the normal operation of the wrapper.  Caveat emptor!
296 //
297 ASDCP::MXF::OP1aHeader&
298 ASDCP::TimedText::MXFReader::OP1aHeader()
299 {
300   if ( m_Reader.empty() )
301     {
302       assert(g_OP1aHeader);
303       return *g_OP1aHeader;
304     }
305
306   return m_Reader->m_HeaderPart;
307 }
308
309 // Warning: direct manipulation of MXF structures can interfere
310 // with the normal operation of the wrapper.  Caveat emptor!
311 //
312 ASDCP::MXF::OPAtomIndexFooter&
313 ASDCP::TimedText::MXFReader::OPAtomIndexFooter()
314 {
315   if ( m_Reader.empty() )
316     {
317       assert(g_OPAtomIndexFooter);
318       return *g_OPAtomIndexFooter;
319     }
320
321   return m_Reader->m_IndexAccess;
322 }
323
324 // Warning: direct manipulation of MXF structures can interfere
325 // with the normal operation of the wrapper.  Caveat emptor!
326 //
327 ASDCP::MXF::RIP&
328 ASDCP::TimedText::MXFReader::RIP()
329 {
330   if ( m_Reader.empty() )
331     {
332       assert(g_RIP);
333       return *g_RIP;
334     }
335
336   return m_Reader->m_RIP;
337 }
338
339 // Open the file for reading. The file must exist. Returns error if the
340 // operation cannot be completed.
341 ASDCP::Result_t
342 ASDCP::TimedText::MXFReader::OpenRead(const std::string& filename) const
343 {
344   return m_Reader->OpenRead(filename);
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 ASDCP::TimedText::MXFReader::FillTimedTextDescriptor(TimedText::TimedTextDescriptor& TDesc) const
351 {
352   if ( m_Reader && m_Reader->m_File.IsOpen() )
353     {
354       TDesc = m_Reader->m_TDesc;
355       return RESULT_OK;
356     }
357
358   return RESULT_INIT;
359 }
360
361 // Fill the struct with the values from the file's header.
362 // Returns RESULT_INIT if the file is not open.
363 ASDCP::Result_t
364 ASDCP::TimedText::MXFReader::FillWriterInfo(WriterInfo& Info) const
365 {
366   if ( m_Reader && m_Reader->m_File.IsOpen() )
367     {
368       Info = m_Reader->m_Info;
369       return RESULT_OK;
370     }
371
372   return RESULT_INIT;
373 }
374
375 //
376 ASDCP::Result_t
377 ASDCP::TimedText::MXFReader::ReadTimedTextResource(std::string& s, AESDecContext* Ctx, HMACContext* HMAC) const
378 {
379   FrameBuffer FrameBuf(2*Kumu::Megabyte);
380
381   Result_t result = ReadTimedTextResource(FrameBuf, Ctx, HMAC);
382
383   if ( ASDCP_SUCCESS(result) )
384     s.assign((char*)FrameBuf.Data(), FrameBuf.Size());
385
386   return result;
387 }
388
389 //
390 ASDCP::Result_t
391 ASDCP::TimedText::MXFReader::ReadTimedTextResource(FrameBuffer& FrameBuf,
392                                                    AESDecContext* Ctx, HMACContext* HMAC) const
393 {
394   if ( m_Reader && m_Reader->m_File.IsOpen() )
395     return m_Reader->ReadTimedTextResource(FrameBuf, Ctx, HMAC);
396
397   return RESULT_INIT;
398 }
399
400 //
401 ASDCP::Result_t
402 ASDCP::TimedText::MXFReader::ReadAncillaryResource(const byte_t* uuid, FrameBuffer& FrameBuf,
403                                                    AESDecContext* Ctx, HMACContext* HMAC) const
404 {
405   if ( m_Reader && m_Reader->m_File.IsOpen() )
406     return m_Reader->ReadAncillaryResource(uuid, FrameBuf, Ctx, HMAC);
407
408   return RESULT_INIT;
409 }
410
411
412 //
413 void
414 ASDCP::TimedText::MXFReader::DumpHeaderMetadata(FILE* stream) const
415 {
416   if ( m_Reader->m_File.IsOpen() )
417     m_Reader->m_HeaderPart.Dump(stream);
418 }
419
420
421 //
422 void
423 ASDCP::TimedText::MXFReader::DumpIndex(FILE* stream) const
424 {
425   if ( m_Reader->m_File.IsOpen() )
426     m_Reader->m_IndexAccess.Dump(stream);
427 }
428
429 //
430 ASDCP::Result_t
431 ASDCP::TimedText::MXFReader::Close() const
432 {
433   if ( m_Reader && m_Reader->m_File.IsOpen() )
434     {
435       m_Reader->Close();
436       return RESULT_OK;
437     }
438
439   return RESULT_INIT;
440 }
441
442
443 //------------------------------------------------------------------------------------------
444
445
446 //
447 class ASDCP::TimedText::MXFWriter::h__Writer : public ASDCP::h__ASDCPWriter
448 {
449   ASDCP_NO_COPY_CONSTRUCT(h__Writer);
450   h__Writer();
451
452 public:
453   TimedTextDescriptor m_TDesc;
454   byte_t              m_EssenceUL[SMPTE_UL_LENGTH];
455   ui32_t              m_EssenceStreamID;
456
457   h__Writer(const Dictionary& d) : ASDCP::h__ASDCPWriter(d), m_EssenceStreamID(10) {
458     memset(m_EssenceUL, 0, SMPTE_UL_LENGTH);
459   }
460
461   virtual ~h__Writer() {}
462
463   Result_t OpenWrite(const std::string&, ui32_t HeaderSize);
464   Result_t SetSourceStream(const TimedTextDescriptor&);
465   Result_t WriteTimedTextResource(const std::string& XMLDoc, AESEncContext* = 0, HMACContext* = 0);
466   Result_t WriteAncillaryResource(const FrameBuffer&, AESEncContext* = 0, HMACContext* = 0);
467   Result_t Finalize();
468   Result_t TimedText_TDesc_to_MD(TimedText::TimedTextDescriptor& TDesc);
469 };
470
471 //
472 ASDCP::Result_t
473 ASDCP::TimedText::MXFWriter::h__Writer::TimedText_TDesc_to_MD(TimedText::TimedTextDescriptor& TDesc)
474 {
475   assert(m_EssenceDescriptor);
476   MXF::TimedTextDescriptor* TDescObj = (MXF::TimedTextDescriptor*)m_EssenceDescriptor;
477
478   TDescObj->SampleRate = TDesc.EditRate;
479   TDescObj->ContainerDuration = TDesc.ContainerDuration;
480   TDescObj->ResourceID.Set(TDesc.AssetID);
481   TDescObj->NamespaceURI = TDesc.NamespaceName;
482   TDescObj->UCSEncoding = TDesc.EncodingName;
483
484   return RESULT_OK;
485 }
486
487 //
488 ASDCP::Result_t
489 ASDCP::TimedText::MXFWriter::h__Writer::OpenWrite(const std::string& filename, ui32_t HeaderSize)
490 {
491   if ( ! m_State.Test_BEGIN() )
492     return RESULT_STATE;
493
494   Result_t result = m_File.OpenWrite(filename);
495
496   if ( ASDCP_SUCCESS(result) )
497     {
498       m_HeaderSize = HeaderSize;
499       m_EssenceDescriptor = new MXF::TimedTextDescriptor(m_Dict);
500       result = m_State.Goto_INIT();
501     }
502
503   return result;
504 }
505
506 //
507 ASDCP::Result_t
508 ASDCP::TimedText::MXFWriter::h__Writer::SetSourceStream(ASDCP::TimedText::TimedTextDescriptor const& TDesc)
509 {
510   if ( ! m_State.Test_INIT() )
511     return RESULT_STATE;
512
513   m_TDesc = TDesc;
514   ResourceList_t::const_iterator ri;
515   Result_t result = TimedText_TDesc_to_MD(m_TDesc);
516
517   for ( ri = m_TDesc.ResourceList.begin() ; ri != m_TDesc.ResourceList.end() && ASDCP_SUCCESS(result); ri++ )
518     {
519       TimedTextResourceSubDescriptor* resourceSubdescriptor = new TimedTextResourceSubDescriptor(m_Dict);
520       GenRandomValue(resourceSubdescriptor->InstanceUID);
521       resourceSubdescriptor->AncillaryResourceID.Set((*ri).ResourceID);
522       resourceSubdescriptor->MIMEMediaType = MIME2str((*ri).Type);
523       resourceSubdescriptor->EssenceStreamID = m_EssenceStreamID++;
524       m_EssenceSubDescriptorList.push_back((FileDescriptor*)resourceSubdescriptor);
525       m_EssenceDescriptor->SubDescriptors.push_back(resourceSubdescriptor->InstanceUID);
526
527       // 72 == sizeof K, L, instanceuid, uuid + sizeof int32 + tag/len * 4
528       m_HeaderSize += ( resourceSubdescriptor->MIMEMediaType.ArchiveLength() * 2 /*ArchiveLength is broken*/ ) + 72;
529     }
530
531   m_EssenceStreamID = 10;
532   assert(m_Dict);
533
534   if ( ASDCP_SUCCESS(result) )
535     {
536       InitHeader(MXFVersion_2004);
537
538       // First RIP Entry
539       if ( m_Info.LabelSetType == LS_MXF_SMPTE )  // ERK
540         {
541           m_RIP.PairArray.push_back(RIP::PartitionPair(0, 0)); // 3-part, no essence in header
542         }
543       else
544         {
545           DefaultLogSink().Error("Unable to write Interop timed-text MXF file.  Use SMPTE DCP options instead.\n");
546           return RESULT_FORMAT;
547         }
548
549       // timecode rate and essence rate are the same
550       AddSourceClip(m_TDesc.EditRate, m_TDesc.EditRate, derive_timecode_rate_from_edit_rate(m_TDesc.EditRate),
551                     TIMED_TEXT_DEF_LABEL, m_EssenceUL, UL(m_Dict->ul(MDD_DataDataDef)), TIMED_TEXT_PACKAGE_LABEL);
552
553       AddEssenceDescriptor(UL(m_Dict->ul(MDD_TimedTextWrappingClip)));
554       result = m_HeaderPart.WriteToFile(m_File, m_HeaderSize);
555       
556       if ( KM_SUCCESS(result) )
557         result = CreateBodyPart(m_TDesc.EditRate);
558     }
559
560   if ( ASDCP_SUCCESS(result) )
561     {
562       memcpy(m_EssenceUL, m_Dict->ul(MDD_TimedTextEssence), SMPTE_UL_LENGTH);
563       m_EssenceUL[SMPTE_UL_LENGTH-1] = 1; // first (and only) essence container
564       result = m_State.Goto_READY();
565     }
566
567   return result;
568 }
569
570 //
571 ASDCP::Result_t
572 ASDCP::TimedText::MXFWriter::h__Writer::WriteTimedTextResource(const std::string& XMLDoc,
573                                                                ASDCP::AESEncContext* Ctx, ASDCP::HMACContext* HMAC)
574 {
575   Result_t result = m_State.Goto_RUNNING();
576
577   if ( ASDCP_SUCCESS(result) )
578     {
579       // TODO: make sure it's XML
580
581       ui32_t str_size = XMLDoc.size();
582       FrameBuffer FrameBuf(str_size);
583       
584       memcpy(FrameBuf.Data(), XMLDoc.c_str(), str_size);
585       FrameBuf.Size(str_size);
586
587       IndexTableSegment::IndexEntry Entry;
588       Entry.StreamOffset = m_StreamOffset;
589       result = WriteEKLVPacket(FrameBuf, m_EssenceUL, MXF_BER_LENGTH, Ctx, HMAC);
590
591       if ( ASDCP_SUCCESS(result) )
592         {
593           m_FooterPart.PushIndexEntry(Entry);
594           m_FramesWritten++;
595         }
596     }
597
598   return result;
599 }
600
601
602 //
603 ASDCP::Result_t
604 ASDCP::TimedText::MXFWriter::h__Writer::WriteAncillaryResource(const ASDCP::TimedText::FrameBuffer& FrameBuf,
605                                                                ASDCP::AESEncContext* Ctx, ASDCP::HMACContext* HMAC)
606 {
607   if ( ! m_State.Test_RUNNING() )
608     return RESULT_STATE;
609
610   Kumu::fpos_t here = m_File.Tell();
611   assert(m_Dict);
612
613   // create generic stream partition header
614   static UL GenericStream_DataElement(m_Dict->ul(MDD_GenericStream_DataElement));
615   MXF::Partition GSPart(m_Dict);
616
617   GSPart.ThisPartition = here;
618   GSPart.PreviousPartition = m_RIP.PairArray.back().ByteOffset;
619   GSPart.BodySID = m_EssenceStreamID;
620   GSPart.OperationalPattern = m_HeaderPart.OperationalPattern;
621
622   m_RIP.PairArray.push_back(RIP::PartitionPair(m_EssenceStreamID++, here));
623   GSPart.EssenceContainers = m_HeaderPart.EssenceContainers;
624   UL TmpUL(m_Dict->ul(MDD_GenericStreamPartition));
625   Result_t result = GSPart.WriteToFile(m_File, TmpUL);
626
627   if ( ASDCP_SUCCESS(result) )
628     result = WriteEKLVPacket(FrameBuf, GenericStream_DataElement.Value(), MXF_BER_LENGTH, Ctx, HMAC);
629
630   m_FramesWritten++;
631   return result;
632 }
633
634 //
635 ASDCP::Result_t
636 ASDCP::TimedText::MXFWriter::h__Writer::Finalize()
637 {
638   if ( ! m_State.Test_RUNNING() )
639     return RESULT_STATE;
640
641   m_FramesWritten = m_TDesc.ContainerDuration;
642   m_State.Goto_FINAL();
643
644   return WriteASDCPFooter();
645 }
646
647
648 //------------------------------------------------------------------------------------------
649
650 ASDCP::TimedText::MXFWriter::MXFWriter()
651 {
652 }
653
654 ASDCP::TimedText::MXFWriter::~MXFWriter()
655 {
656 }
657
658 // Warning: direct manipulation of MXF structures can interfere
659 // with the normal operation of the wrapper.  Caveat emptor!
660 //
661 ASDCP::MXF::OP1aHeader&
662 ASDCP::TimedText::MXFWriter::OP1aHeader()
663 {
664   if ( m_Writer.empty() )
665     {
666       assert(g_OP1aHeader);
667       return *g_OP1aHeader;
668     }
669
670   return m_Writer->m_HeaderPart;
671 }
672
673 // Warning: direct manipulation of MXF structures can interfere
674 // with the normal operation of the wrapper.  Caveat emptor!
675 //
676 ASDCP::MXF::OPAtomIndexFooter&
677 ASDCP::TimedText::MXFWriter::OPAtomIndexFooter()
678 {
679   if ( m_Writer.empty() )
680     {
681       assert(g_OPAtomIndexFooter);
682       return *g_OPAtomIndexFooter;
683     }
684
685   return m_Writer->m_FooterPart;
686 }
687
688 // Warning: direct manipulation of MXF structures can interfere
689 // with the normal operation of the wrapper.  Caveat emptor!
690 //
691 ASDCP::MXF::RIP&
692 ASDCP::TimedText::MXFWriter::RIP()
693 {
694   if ( m_Writer.empty() )
695     {
696       assert(g_RIP);
697       return *g_RIP;
698     }
699
700   return m_Writer->m_RIP;
701 }
702
703 // Open the file for writing. The file must not exist. Returns error if
704 // the operation cannot be completed.
705 ASDCP::Result_t
706 ASDCP::TimedText::MXFWriter::OpenWrite(const std::string& filename, const WriterInfo& Info,
707                                        const TimedTextDescriptor& TDesc, ui32_t HeaderSize)
708 {
709   if ( Info.LabelSetType != LS_MXF_SMPTE )
710     {
711       DefaultLogSink().Error("Timed Text support requires LS_MXF_SMPTE\n");
712       return RESULT_FORMAT;
713     }
714
715   m_Writer = new h__Writer(DefaultSMPTEDict());
716   m_Writer->m_Info = Info;
717   
718   Result_t result = m_Writer->OpenWrite(filename, HeaderSize);
719
720   if ( ASDCP_SUCCESS(result) )
721     result = m_Writer->SetSourceStream(TDesc);
722
723   if ( ASDCP_FAILURE(result) )
724     m_Writer.release();
725
726   return result;
727 }
728
729 //
730 ASDCP::Result_t
731 ASDCP::TimedText::MXFWriter::WriteTimedTextResource(const std::string& XMLDoc, AESEncContext* Ctx, HMACContext* HMAC)
732 {
733   if ( m_Writer.empty() )
734     return RESULT_INIT;
735
736   return m_Writer->WriteTimedTextResource(XMLDoc, Ctx, HMAC);
737 }
738
739 //
740 ASDCP::Result_t
741 ASDCP::TimedText::MXFWriter::WriteAncillaryResource(const FrameBuffer& FrameBuf, AESEncContext* Ctx, HMACContext* HMAC)
742 {
743   if ( m_Writer.empty() )
744     return RESULT_INIT;
745
746   return m_Writer->WriteAncillaryResource(FrameBuf, Ctx, HMAC);
747 }
748
749 // Closes the MXF file, writing the index and other closing information.
750 ASDCP::Result_t
751 ASDCP::TimedText::MXFWriter::Finalize()
752 {
753   if ( m_Writer.empty() )
754     return RESULT_INIT;
755
756   return m_Writer->Finalize();
757 }
758
759
760
761 //
762 // end AS_DCP_timedText.cpp
763 //