Add call to parent constructor.
[asdcplib-cth.git] / src / AS_DCP_TimedText.cpp
1 /*
2 Copyright (c) 2008-2015, 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: AS_DCP_TimedText.cpp,v 1.38 2015/10/09 23:41:11 jhurst Exp $       
29     \brief   AS-DCP library, PCM essence reader and writer implementation
30 */
31
32
33 #include "AS_DCP_internal.h"
34 #include "KM_xml.h"
35 #include <iostream>
36 #include <iomanip>
37
38 using Kumu::GenRandomValue;
39
40 static std::string TIMED_TEXT_PACKAGE_LABEL = "File Package: SMPTE 429-5 clip wrapping of D-Cinema Timed Text data";
41 static std::string TIMED_TEXT_DEF_LABEL = "Timed Text Track";
42
43
44 //------------------------------------------------------------------------------------------
45
46 const char*
47 MIME2str(TimedText::MIMEType_t m)
48 {
49   if ( m == TimedText::MT_PNG )
50     return "image/png";
51
52   else if ( m == TimedText::MT_OPENTYPE )
53     return "application/x-font-opentype";
54
55   return "application/octet-stream";
56 }
57
58 //
59 std::ostream&
60 ASDCP::TimedText::operator << (std::ostream& strm, const TimedTextDescriptor& TDesc)
61 {
62   UUID TmpID(TDesc.AssetID);
63   char buf[64];
64
65   strm << "         EditRate: " << (unsigned) TDesc.EditRate.Numerator << "/" << (unsigned) TDesc.EditRate.Denominator << std::endl;
66   strm << "ContainerDuration: " << (unsigned) TDesc.ContainerDuration << std::endl;
67   strm << "          AssetID: " << TmpID.EncodeHex(buf, 64) << std::endl;
68   strm << "    NamespaceName: " << TDesc.NamespaceName << std::endl;
69   strm << "    ResourceCount: " << (unsigned long) TDesc.ResourceList.size() << std::endl;
70
71   TimedText::ResourceList_t::const_iterator ri;
72   for ( ri = TDesc.ResourceList.begin() ; ri != TDesc.ResourceList.end(); ri++ )
73     {
74       TmpID.Set((*ri).ResourceID);
75       strm << "    " << TmpID.EncodeHex(buf, 64) << ": " << MIME2str((*ri).Type) << std::endl;
76     }
77
78   return strm;
79 }
80
81 //
82 void
83 ASDCP::TimedText::DescriptorDump(ASDCP::TimedText::TimedTextDescriptor const& TDesc, FILE* stream)
84 {
85   if ( stream == 0 )
86     stream = stderr;
87
88   UUID TmpID(TDesc.AssetID);
89   char buf[64];
90
91   fprintf(stream, "         EditRate: %u/%u\n", TDesc.EditRate.Numerator, TDesc.EditRate.Denominator);
92   fprintf(stream, "ContainerDuration: %u\n",    TDesc.ContainerDuration);
93   fprintf(stream, "          AssetID: %s\n",    TmpID.EncodeHex(buf, 64));
94   fprintf(stream, "    NamespaceName: %s\n",    TDesc.NamespaceName.c_str());
95   fprintf(stream, "    ResourceCount: %zu\n",   TDesc.ResourceList.size());
96
97   TimedText::ResourceList_t::const_iterator ri;
98   for ( ri = TDesc.ResourceList.begin() ; ri != TDesc.ResourceList.end(); ri++ )
99     {
100       TmpID.Set((*ri).ResourceID);
101       fprintf(stream, "    %s: %s\n",
102               TmpID.EncodeHex(buf, 64), 
103               MIME2str((*ri).Type));
104     }
105 }
106
107 //
108 void
109 ASDCP::TimedText::FrameBuffer::Dump(FILE* stream, ui32_t dump_len) const
110 {
111   if ( stream == 0 )
112     stream = stderr;
113
114   UUID TmpID(m_AssetID);
115   char buf[64];
116   fprintf(stream, "%s | %s | %u\n", TmpID.EncodeHex(buf, 64), m_MIMEType.c_str(), Size());
117
118   if ( dump_len > 0 )
119     Kumu::hexdump(m_Data, dump_len, stream);
120 }
121
122 //------------------------------------------------------------------------------------------
123
124 typedef std::map<UUID, UUID> ResourceMap_t;
125
126 class ASDCP::TimedText::MXFReader::h__Reader : public ASDCP::h__ASDCPReader
127 {
128   MXF::TimedTextDescriptor* m_EssenceDescriptor;
129   ResourceMap_t             m_ResourceMap;
130
131   ASDCP_NO_COPY_CONSTRUCT(h__Reader);
132
133 public:
134   TimedTextDescriptor m_TDesc;    
135
136   h__Reader(const Dictionary& d) : ASDCP::h__ASDCPReader(d), m_EssenceDescriptor(0) {
137     memset(&m_TDesc.AssetID, 0, UUIDlen);
138   }
139
140   virtual ~h__Reader() {}
141
142   Result_t    OpenRead(const std::string&);
143   Result_t    MD_to_TimedText_TDesc(TimedText::TimedTextDescriptor& TDesc);
144   Result_t    ReadTimedTextResource(FrameBuffer& FrameBuf, AESDecContext* Ctx, HMACContext* HMAC);
145   Result_t    ReadAncillaryResource(const byte_t*, FrameBuffer& FrameBuf, AESDecContext* Ctx, HMACContext* HMAC);
146 };
147
148 //
149 ASDCP::Result_t
150 ASDCP::TimedText::MXFReader::h__Reader::MD_to_TimedText_TDesc(TimedText::TimedTextDescriptor& TDesc)
151 {
152   assert(m_EssenceDescriptor);
153   memset(&m_TDesc.AssetID, 0, UUIDlen);
154   MXF::TimedTextDescriptor* TDescObj = (MXF::TimedTextDescriptor*)m_EssenceDescriptor;
155
156   TDesc.EditRate = TDescObj->SampleRate;
157   assert(TDescObj->ContainerDuration <= 0xFFFFFFFFL);
158   TDesc.ContainerDuration = (ui32_t) TDescObj->ContainerDuration;
159   memcpy(TDesc.AssetID, TDescObj->ResourceID.Value(), UUIDlen);
160   TDesc.NamespaceName = TDescObj->NamespaceURI;
161   TDesc.EncodingName = TDescObj->UCSEncoding;
162
163   Array<UUID>::const_iterator sdi = TDescObj->SubDescriptors.begin();
164   TimedTextResourceSubDescriptor* DescObject = 0;
165   Result_t result = RESULT_OK;
166
167   for ( ; sdi != TDescObj->SubDescriptors.end() && KM_SUCCESS(result); sdi++ )
168     {
169       InterchangeObject* tmp_iobj = 0;
170       result = m_HeaderPart.GetMDObjectByID(*sdi, &tmp_iobj);
171       DescObject = static_cast<TimedTextResourceSubDescriptor*>(tmp_iobj);
172
173       if ( KM_SUCCESS(result) )
174         {
175           TimedTextResourceDescriptor TmpResource;
176           memcpy(TmpResource.ResourceID, DescObject->AncillaryResourceID.Value(), UUIDlen);
177
178           if ( DescObject->MIMEMediaType.find("application/x-font-opentype") != std::string::npos
179                || DescObject->MIMEMediaType.find("application/x-opentype") != std::string::npos
180                || DescObject->MIMEMediaType.find("font/opentype") != std::string::npos )
181             TmpResource.Type = MT_OPENTYPE;
182
183           else if ( DescObject->MIMEMediaType.find("image/png") != std::string::npos )
184             TmpResource.Type = MT_PNG;
185
186           else
187             TmpResource.Type = MT_BIN;
188
189           TDesc.ResourceList.push_back(TmpResource);
190           m_ResourceMap.insert(ResourceMap_t::value_type(DescObject->AncillaryResourceID, *sdi));
191         }
192       else
193         {
194           DefaultLogSink().Error("Broken sub-descriptor link\n");
195           return RESULT_FORMAT;
196         }
197     }
198
199   return result;
200 }
201
202 //
203 ASDCP::Result_t
204 ASDCP::TimedText::MXFReader::h__Reader::OpenRead(const std::string& filename)
205 {
206   Result_t result = OpenMXFRead(filename);
207   
208   if( ASDCP_SUCCESS(result) )
209     {
210       if ( m_EssenceDescriptor == 0 )
211         {
212           InterchangeObject* tmp_iobj = 0;
213           result = m_HeaderPart.GetMDObjectByType(OBJ_TYPE_ARGS(TimedTextDescriptor), &tmp_iobj);
214           m_EssenceDescriptor = static_cast<MXF::TimedTextDescriptor*>(tmp_iobj);
215         }
216
217       if( ASDCP_SUCCESS(result) )
218         result = MD_to_TimedText_TDesc(m_TDesc);
219     }
220
221   return result;
222 }
223
224 //
225 ASDCP::Result_t
226 ASDCP::TimedText::MXFReader::h__Reader::ReadTimedTextResource(FrameBuffer& FrameBuf,
227                                                               AESDecContext* Ctx, HMACContext* HMAC)
228 {
229   if ( ! m_File.IsOpen() )
230     return RESULT_INIT;
231
232   assert(m_Dict);
233   Result_t result = ReadEKLVFrame(0, FrameBuf, m_Dict->ul(MDD_TimedTextEssence), Ctx, HMAC);
234
235  if( ASDCP_SUCCESS(result) )
236    {
237      FrameBuf.AssetID(m_TDesc.AssetID);
238      FrameBuf.MIMEType("text/xml");
239    }
240
241  return result;
242 }
243
244 //
245 ASDCP::Result_t
246 ASDCP::TimedText::MXFReader::h__Reader::ReadAncillaryResource(const byte_t* uuid, FrameBuffer& FrameBuf,
247                                                               AESDecContext* Ctx, HMACContext* HMAC)
248 {
249   KM_TEST_NULL_L(uuid);
250   UUID RID(uuid);
251
252   ResourceMap_t::const_iterator ri = m_ResourceMap.find(RID);
253   if ( ri == m_ResourceMap.end() )
254     {
255       char buf[64];
256       DefaultLogSink().Error("No such resource: %s\n", RID.EncodeHex(buf, 64));
257       return RESULT_RANGE;
258     }
259
260   TimedTextResourceSubDescriptor* DescObject = 0;
261   // get the subdescriptor
262   InterchangeObject* tmp_iobj = 0;
263   Result_t result = m_HeaderPart.GetMDObjectByID((*ri).second, &tmp_iobj);
264   DescObject = static_cast<TimedTextResourceSubDescriptor*>(tmp_iobj);
265
266   if ( KM_SUCCESS(result) )
267     {
268       RIP::const_pair_iterator pi;
269       RIP::PartitionPair TmpPair;
270       ui32_t sequence = 0;
271
272       // Look up the partition start in the RIP using the SID.
273       // Count the sequence length in because this is the sequence
274       // value needed to  complete the HMAC.
275       for ( pi = m_RIP.PairArray.begin(); pi != m_RIP.PairArray.end(); ++pi, ++sequence )
276         {
277           if ( (*pi).BodySID == DescObject->EssenceStreamID )
278             {
279               TmpPair = *pi;
280               break;
281             }
282         }
283
284       if ( TmpPair.ByteOffset == 0 )
285         {
286           DefaultLogSink().Error("Body SID not found in RIP set: %d\n", DescObject->EssenceStreamID);
287           return RESULT_FORMAT;
288         }
289
290       if ( KM_SUCCESS(result) )
291         {
292           FrameBuf.AssetID(uuid);
293           FrameBuf.MIMEType(DescObject->MIMEMediaType);
294
295           // seek tp the start of the partition
296           if ( (Kumu::fpos_t)TmpPair.ByteOffset != m_LastPosition )
297             {
298               m_LastPosition = TmpPair.ByteOffset;
299               result = m_File.Seek(TmpPair.ByteOffset);
300             }
301
302           // read the partition header
303           MXF::Partition GSPart(m_Dict);
304           result = GSPart.InitFromFile(m_File);
305
306           if( ASDCP_SUCCESS(result) )
307             {
308               // check the SID
309               if ( DescObject->EssenceStreamID != GSPart.BodySID )
310                 {
311                   char buf[64];
312                   DefaultLogSink().Error("Generic stream partition body differs: %s\n", RID.EncodeHex(buf, 64));
313                   return RESULT_FORMAT;
314                 }
315
316               // read the essence packet
317               assert(m_Dict);
318               if( ASDCP_SUCCESS(result) )
319                 result = ReadEKLVPacket(0, sequence, FrameBuf, m_Dict->ul(MDD_GenericStream_DataElement), Ctx, HMAC);
320             }
321         }
322     }
323
324   return result;
325 }
326
327
328 //------------------------------------------------------------------------------------------
329
330 ASDCP::TimedText::MXFReader::MXFReader()
331 {
332   m_Reader = new h__Reader(DefaultSMPTEDict());
333 }
334
335
336 ASDCP::TimedText::MXFReader::~MXFReader()
337 {
338 }
339
340 // Warning: direct manipulation of MXF structures can interfere
341 // with the normal operation of the wrapper.  Caveat emptor!
342 //
343 ASDCP::MXF::OP1aHeader&
344 ASDCP::TimedText::MXFReader::OP1aHeader()
345 {
346   if ( m_Reader.empty() )
347     {
348       assert(g_OP1aHeader);
349       return *g_OP1aHeader;
350     }
351
352   return m_Reader->m_HeaderPart;
353 }
354
355 // Warning: direct manipulation of MXF structures can interfere
356 // with the normal operation of the wrapper.  Caveat emptor!
357 //
358 ASDCP::MXF::OPAtomIndexFooter&
359 ASDCP::TimedText::MXFReader::OPAtomIndexFooter()
360 {
361   if ( m_Reader.empty() )
362     {
363       assert(g_OPAtomIndexFooter);
364       return *g_OPAtomIndexFooter;
365     }
366
367   return m_Reader->m_IndexAccess;
368 }
369
370 // Warning: direct manipulation of MXF structures can interfere
371 // with the normal operation of the wrapper.  Caveat emptor!
372 //
373 ASDCP::MXF::RIP&
374 ASDCP::TimedText::MXFReader::RIP()
375 {
376   if ( m_Reader.empty() )
377     {
378       assert(g_RIP);
379       return *g_RIP;
380     }
381
382   return m_Reader->m_RIP;
383 }
384
385 // Open the file for reading. The file must exist. Returns error if the
386 // operation cannot be completed.
387 ASDCP::Result_t
388 ASDCP::TimedText::MXFReader::OpenRead(const std::string& filename) const
389 {
390   return m_Reader->OpenRead(filename);
391 }
392
393 // Fill the struct with the values from the file's header.
394 // Returns RESULT_INIT if the file is not open.
395 ASDCP::Result_t
396 ASDCP::TimedText::MXFReader::FillTimedTextDescriptor(TimedText::TimedTextDescriptor& TDesc) const
397 {
398   if ( m_Reader && m_Reader->m_File.IsOpen() )
399     {
400       TDesc = m_Reader->m_TDesc;
401       return RESULT_OK;
402     }
403
404   return RESULT_INIT;
405 }
406
407 // Fill the struct with the values from the file's header.
408 // Returns RESULT_INIT if the file is not open.
409 ASDCP::Result_t
410 ASDCP::TimedText::MXFReader::FillWriterInfo(WriterInfo& Info) const
411 {
412   if ( m_Reader && m_Reader->m_File.IsOpen() )
413     {
414       Info = m_Reader->m_Info;
415       return RESULT_OK;
416     }
417
418   return RESULT_INIT;
419 }
420
421 //
422 ASDCP::Result_t
423 ASDCP::TimedText::MXFReader::ReadTimedTextResource(std::string& s, AESDecContext* Ctx, HMACContext* HMAC) const
424 {
425   FrameBuffer FrameBuf(2*Kumu::Megabyte);
426
427   Result_t result = ReadTimedTextResource(FrameBuf, Ctx, HMAC);
428
429   if ( ASDCP_SUCCESS(result) )
430     s.assign((char*)FrameBuf.Data(), FrameBuf.Size());
431
432   return result;
433 }
434
435 //
436 ASDCP::Result_t
437 ASDCP::TimedText::MXFReader::ReadTimedTextResource(FrameBuffer& FrameBuf,
438                                                    AESDecContext* Ctx, HMACContext* HMAC) const
439 {
440   if ( m_Reader && m_Reader->m_File.IsOpen() )
441     return m_Reader->ReadTimedTextResource(FrameBuf, Ctx, HMAC);
442
443   return RESULT_INIT;
444 }
445
446 //
447 ASDCP::Result_t
448 ASDCP::TimedText::MXFReader::ReadAncillaryResource(const byte_t* uuid, FrameBuffer& FrameBuf,
449                                                    AESDecContext* Ctx, HMACContext* HMAC) const
450 {
451   if ( m_Reader && m_Reader->m_File.IsOpen() )
452     return m_Reader->ReadAncillaryResource(uuid, FrameBuf, Ctx, HMAC);
453
454   return RESULT_INIT;
455 }
456
457
458 //
459 void
460 ASDCP::TimedText::MXFReader::DumpHeaderMetadata(FILE* stream) const
461 {
462   if ( m_Reader->m_File.IsOpen() )
463     m_Reader->m_HeaderPart.Dump(stream);
464 }
465
466
467 //
468 void
469 ASDCP::TimedText::MXFReader::DumpIndex(FILE* stream) const
470 {
471   if ( m_Reader->m_File.IsOpen() )
472     m_Reader->m_IndexAccess.Dump(stream);
473 }
474
475 //
476 ASDCP::Result_t
477 ASDCP::TimedText::MXFReader::Close() const
478 {
479   if ( m_Reader && m_Reader->m_File.IsOpen() )
480     {
481       m_Reader->Close();
482       return RESULT_OK;
483     }
484
485   return RESULT_INIT;
486 }
487
488
489 //------------------------------------------------------------------------------------------
490
491
492 //
493 class ASDCP::TimedText::MXFWriter::h__Writer : public ASDCP::h__ASDCPWriter
494 {
495   ASDCP_NO_COPY_CONSTRUCT(h__Writer);
496   h__Writer();
497
498 public:
499   TimedTextDescriptor m_TDesc;
500   byte_t              m_EssenceUL[SMPTE_UL_LENGTH];
501   ui32_t              m_EssenceStreamID;
502
503   h__Writer(const Dictionary& d) : ASDCP::h__ASDCPWriter(d), m_EssenceStreamID(10) {
504     memset(m_EssenceUL, 0, SMPTE_UL_LENGTH);
505   }
506
507   virtual ~h__Writer() {}
508
509   Result_t OpenWrite(const std::string&, ui32_t HeaderSize);
510   Result_t SetSourceStream(const TimedTextDescriptor&);
511   Result_t WriteTimedTextResource(const std::string& XMLDoc, AESEncContext* = 0, HMACContext* = 0);
512   Result_t WriteAncillaryResource(const FrameBuffer&, AESEncContext* = 0, HMACContext* = 0);
513   Result_t Finalize();
514   Result_t TimedText_TDesc_to_MD(TimedText::TimedTextDescriptor& TDesc);
515 };
516
517 //
518 ASDCP::Result_t
519 ASDCP::TimedText::MXFWriter::h__Writer::TimedText_TDesc_to_MD(TimedText::TimedTextDescriptor& TDesc)
520 {
521   assert(m_EssenceDescriptor);
522   MXF::TimedTextDescriptor* TDescObj = (MXF::TimedTextDescriptor*)m_EssenceDescriptor;
523
524   TDescObj->SampleRate = TDesc.EditRate;
525   TDescObj->ContainerDuration = TDesc.ContainerDuration;
526   TDescObj->ResourceID.Set(TDesc.AssetID);
527   TDescObj->NamespaceURI = TDesc.NamespaceName;
528   TDescObj->UCSEncoding = TDesc.EncodingName;
529
530   return RESULT_OK;
531 }
532
533 //
534 ASDCP::Result_t
535 ASDCP::TimedText::MXFWriter::h__Writer::OpenWrite(const std::string& filename, ui32_t HeaderSize)
536 {
537   if ( ! m_State.Test_BEGIN() )
538     return RESULT_STATE;
539
540   Result_t result = m_File.OpenWrite(filename);
541
542   if ( ASDCP_SUCCESS(result) )
543     {
544       m_HeaderSize = HeaderSize;
545       m_EssenceDescriptor = new MXF::TimedTextDescriptor(m_Dict);
546       result = m_State.Goto_INIT();
547     }
548
549   return result;
550 }
551
552 //
553 ASDCP::Result_t
554 ASDCP::TimedText::MXFWriter::h__Writer::SetSourceStream(ASDCP::TimedText::TimedTextDescriptor const& TDesc)
555 {
556   if ( ! m_State.Test_INIT() )
557     return RESULT_STATE;
558
559   m_TDesc = TDesc;
560   ResourceList_t::const_iterator ri;
561   Result_t result = TimedText_TDesc_to_MD(m_TDesc);
562
563   for ( ri = m_TDesc.ResourceList.begin() ; ri != m_TDesc.ResourceList.end() && ASDCP_SUCCESS(result); ri++ )
564     {
565       TimedTextResourceSubDescriptor* resourceSubdescriptor = new TimedTextResourceSubDescriptor(m_Dict);
566       GenRandomValue(resourceSubdescriptor->InstanceUID);
567       resourceSubdescriptor->AncillaryResourceID.Set((*ri).ResourceID);
568       resourceSubdescriptor->MIMEMediaType = MIME2str((*ri).Type);
569       resourceSubdescriptor->EssenceStreamID = m_EssenceStreamID++;
570       m_EssenceSubDescriptorList.push_back((FileDescriptor*)resourceSubdescriptor);
571       m_EssenceDescriptor->SubDescriptors.push_back(resourceSubdescriptor->InstanceUID);
572
573       // 72 == sizeof K, L, instanceuid, uuid + sizeof int32 + tag/len * 4
574       m_HeaderSize += ( resourceSubdescriptor->MIMEMediaType.ArchiveLength() * 2 /*ArchiveLength is broken*/ ) + 72;
575     }
576
577   m_EssenceStreamID = 10;
578   assert(m_Dict);
579
580   if ( ASDCP_SUCCESS(result) )
581     {
582       InitHeader();
583
584       // First RIP Entry
585       if ( m_Info.LabelSetType == LS_MXF_SMPTE )  // ERK
586         {
587           m_RIP.PairArray.push_back(RIP::PartitionPair(0, 0)); // 3-part, no essence in header
588         }
589       else
590         {
591           DefaultLogSink().Error("Unable to write Interop timed-text MXF file.  Use SMPTE DCP options instead.\n");
592           return RESULT_FORMAT;
593         }
594
595       // timecode rate and essence rate are the same
596       AddDMSegment(m_TDesc.EditRate, m_TDesc.EditRate, derive_timecode_rate_from_edit_rate(m_TDesc.EditRate), TIMED_TEXT_DEF_LABEL,
597                    UL(m_Dict->ul(MDD_DataDataDef)), TIMED_TEXT_PACKAGE_LABEL);
598
599       AddEssenceDescriptor(UL(m_Dict->ul(MDD_TimedTextWrappingClip)));
600
601       result = m_HeaderPart.WriteToFile(m_File, m_HeaderSize);
602       
603       if ( KM_SUCCESS(result) )
604         result = CreateBodyPart(m_TDesc.EditRate);
605     }
606
607   if ( ASDCP_SUCCESS(result) )
608     {
609       memcpy(m_EssenceUL, m_Dict->ul(MDD_TimedTextEssence), SMPTE_UL_LENGTH);
610       m_EssenceUL[SMPTE_UL_LENGTH-1] = 1; // first (and only) essence container
611       result = m_State.Goto_READY();
612     }
613
614   return result;
615 }
616
617 //
618 ASDCP::Result_t
619 ASDCP::TimedText::MXFWriter::h__Writer::WriteTimedTextResource(const std::string& XMLDoc,
620                                                                ASDCP::AESEncContext* Ctx, ASDCP::HMACContext* HMAC)
621 {
622   Result_t result = m_State.Goto_RUNNING();
623
624   if ( ASDCP_SUCCESS(result) )
625     {
626       // TODO: make sure it's XML
627
628       ui32_t str_size = XMLDoc.size();
629       FrameBuffer FrameBuf(str_size);
630       
631       memcpy(FrameBuf.Data(), XMLDoc.c_str(), str_size);
632       FrameBuf.Size(str_size);
633
634       IndexTableSegment::IndexEntry Entry;
635       Entry.StreamOffset = m_StreamOffset;
636       
637       if ( ASDCP_SUCCESS(result) )
638         result = WriteEKLVPacket(FrameBuf, m_EssenceUL, Ctx, HMAC);
639
640       if ( ASDCP_SUCCESS(result) )
641         {
642           m_FooterPart.PushIndexEntry(Entry);
643           m_FramesWritten++;
644         }
645     }
646
647   return result;
648 }
649
650
651 //
652 ASDCP::Result_t
653 ASDCP::TimedText::MXFWriter::h__Writer::WriteAncillaryResource(const ASDCP::TimedText::FrameBuffer& FrameBuf,
654                                                                ASDCP::AESEncContext* Ctx, ASDCP::HMACContext* HMAC)
655 {
656   if ( ! m_State.Test_RUNNING() )
657     return RESULT_STATE;
658
659   Kumu::fpos_t here = m_File.Tell();
660   assert(m_Dict);
661
662   // create generic stream partition header
663   static UL GenericStream_DataElement(m_Dict->ul(MDD_GenericStream_DataElement));
664   MXF::Partition GSPart(m_Dict);
665
666   GSPart.ThisPartition = here;
667   GSPart.PreviousPartition = m_RIP.PairArray.back().ByteOffset;
668   GSPart.BodySID = m_EssenceStreamID;
669   GSPart.OperationalPattern = m_HeaderPart.OperationalPattern;
670
671   m_RIP.PairArray.push_back(RIP::PartitionPair(m_EssenceStreamID++, here));
672   GSPart.EssenceContainers.push_back(UL(m_Dict->ul(MDD_TimedTextEssence)));
673   UL TmpUL(m_Dict->ul(MDD_GenericStreamPartition));
674   Result_t result = GSPart.WriteToFile(m_File, TmpUL);
675
676   if ( ASDCP_SUCCESS(result) )
677     result = WriteEKLVPacket(FrameBuf, GenericStream_DataElement.Value(), Ctx, HMAC);
678
679   m_FramesWritten++;
680   return result;
681 }
682
683 //
684 ASDCP::Result_t
685 ASDCP::TimedText::MXFWriter::h__Writer::Finalize()
686 {
687   if ( ! m_State.Test_RUNNING() )
688     return RESULT_STATE;
689
690   m_FramesWritten = m_TDesc.ContainerDuration;
691   m_State.Goto_FINAL();
692
693   return WriteASDCPFooter();
694 }
695
696
697 //------------------------------------------------------------------------------------------
698
699 ASDCP::TimedText::MXFWriter::MXFWriter()
700 {
701 }
702
703 ASDCP::TimedText::MXFWriter::~MXFWriter()
704 {
705 }
706
707 // Warning: direct manipulation of MXF structures can interfere
708 // with the normal operation of the wrapper.  Caveat emptor!
709 //
710 ASDCP::MXF::OP1aHeader&
711 ASDCP::TimedText::MXFWriter::OP1aHeader()
712 {
713   if ( m_Writer.empty() )
714     {
715       assert(g_OP1aHeader);
716       return *g_OP1aHeader;
717     }
718
719   return m_Writer->m_HeaderPart;
720 }
721
722 // Warning: direct manipulation of MXF structures can interfere
723 // with the normal operation of the wrapper.  Caveat emptor!
724 //
725 ASDCP::MXF::OPAtomIndexFooter&
726 ASDCP::TimedText::MXFWriter::OPAtomIndexFooter()
727 {
728   if ( m_Writer.empty() )
729     {
730       assert(g_OPAtomIndexFooter);
731       return *g_OPAtomIndexFooter;
732     }
733
734   return m_Writer->m_FooterPart;
735 }
736
737 // Warning: direct manipulation of MXF structures can interfere
738 // with the normal operation of the wrapper.  Caveat emptor!
739 //
740 ASDCP::MXF::RIP&
741 ASDCP::TimedText::MXFWriter::RIP()
742 {
743   if ( m_Writer.empty() )
744     {
745       assert(g_RIP);
746       return *g_RIP;
747     }
748
749   return m_Writer->m_RIP;
750 }
751
752 // Open the file for writing. The file must not exist. Returns error if
753 // the operation cannot be completed.
754 ASDCP::Result_t
755 ASDCP::TimedText::MXFWriter::OpenWrite(const std::string& filename, const WriterInfo& Info,
756                                        const TimedTextDescriptor& TDesc, ui32_t HeaderSize)
757 {
758   if ( Info.LabelSetType != LS_MXF_SMPTE )
759     {
760       DefaultLogSink().Error("Timed Text support requires LS_MXF_SMPTE\n");
761       return RESULT_FORMAT;
762     }
763
764   m_Writer = new h__Writer(DefaultSMPTEDict());
765   m_Writer->m_Info = Info;
766   
767   Result_t result = m_Writer->OpenWrite(filename, HeaderSize);
768
769   if ( ASDCP_SUCCESS(result) )
770     result = m_Writer->SetSourceStream(TDesc);
771
772   if ( ASDCP_FAILURE(result) )
773     m_Writer.release();
774
775   return result;
776 }
777
778 //
779 ASDCP::Result_t
780 ASDCP::TimedText::MXFWriter::WriteTimedTextResource(const std::string& XMLDoc, AESEncContext* Ctx, HMACContext* HMAC)
781 {
782   if ( m_Writer.empty() )
783     return RESULT_INIT;
784
785   return m_Writer->WriteTimedTextResource(XMLDoc, Ctx, HMAC);
786 }
787
788 //
789 ASDCP::Result_t
790 ASDCP::TimedText::MXFWriter::WriteAncillaryResource(const FrameBuffer& FrameBuf, AESEncContext* Ctx, HMACContext* HMAC)
791 {
792   if ( m_Writer.empty() )
793     return RESULT_INIT;
794
795   return m_Writer->WriteAncillaryResource(FrameBuf, Ctx, HMAC);
796 }
797
798 // Closes the MXF file, writing the index and other closing information.
799 ASDCP::Result_t
800 ASDCP::TimedText::MXFWriter::Finalize()
801 {
802   if ( m_Writer.empty() )
803     return RESULT_INIT;
804
805   return m_Writer->Finalize();
806 }
807
808
809
810 //
811 // end AS_DCP_timedText.cpp
812 //