2 Copyright (c) 2008-2018, John Hurst
5 Redistribution and use in source and binary forms, with or without
6 modification, are permitted provided that the following conditions
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.
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.
27 /*! \file AS_DCP_TimedText.cpp
29 \brief AS-DCP library, PCM essence reader and writer implementation
33 #include "AS_DCP_internal.h"
38 using Kumu::GenRandomValue;
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";
44 //------------------------------------------------------------------------------------------
48 MIME2str(TimedText::MIMEType_t m)
50 if ( m == TimedText::MT_PNG )
53 else if( m == TimedText::MT_OPENTYPE )
54 return "application/x-font-opentype";
56 return "application/octet-stream";
61 ASDCP::TimedText::operator << (std::ostream& strm, const TimedTextDescriptor& TDesc)
63 UUID TmpID(TDesc.AssetID);
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;
72 TimedText::ResourceList_t::const_iterator ri;
73 for ( ri = TDesc.ResourceList.begin() ; ri != TDesc.ResourceList.end(); ri++ )
75 TmpID.Set((*ri).ResourceID);
76 strm << " " << TmpID.EncodeHex(buf, 64) << ": " << MIME2str((*ri).Type) << std::endl;
84 ASDCP::TimedText::DescriptorDump(ASDCP::TimedText::TimedTextDescriptor const& TDesc, FILE* stream)
89 UUID TmpID(TDesc.AssetID);
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());
98 TimedText::ResourceList_t::const_iterator ri;
99 for ( ri = TDesc.ResourceList.begin() ; ri != TDesc.ResourceList.end(); ri++ )
101 TmpID.Set((*ri).ResourceID);
102 fprintf(stream, " %s: %s\n",
103 TmpID.EncodeHex(buf, 64),
104 MIME2str((*ri).Type));
110 ASDCP::TimedText::FrameBuffer::Dump(FILE* stream, ui32_t dump_len) const
115 UUID TmpID(m_AssetID);
117 fprintf(stream, "%s | %s | %u\n", TmpID.EncodeHex(buf, 64), m_MIMEType.c_str(), Size());
120 Kumu::hexdump(m_Data, dump_len, stream);
123 //------------------------------------------------------------------------------------------
125 typedef std::map<UUID, UUID> ResourceMap_t;
127 class ASDCP::TimedText::MXFReader::h__Reader : public ASDCP::h__ASDCPReader
129 MXF::TimedTextDescriptor* m_EssenceDescriptor;
130 ResourceMap_t m_ResourceMap;
132 ASDCP_NO_COPY_CONSTRUCT(h__Reader);
135 TimedTextDescriptor m_TDesc;
137 h__Reader(const Dictionary& d) : ASDCP::h__ASDCPReader(d), m_EssenceDescriptor(0) {
138 memset(&m_TDesc.AssetID, 0, UUIDlen);
141 virtual ~h__Reader() {}
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);
151 ASDCP::TimedText::MXFReader::h__Reader::MD_to_TimedText_TDesc(TimedText::TimedTextDescriptor& TDesc)
153 assert(m_EssenceDescriptor);
154 memset(&m_TDesc.AssetID, 0, UUIDlen);
155 MXF::TimedTextDescriptor* TDescObj = (MXF::TimedTextDescriptor*)m_EssenceDescriptor;
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;
164 Array<UUID>::const_iterator sdi = TDescObj->SubDescriptors.begin();
165 TimedTextResourceSubDescriptor* DescObject = 0;
166 Result_t result = RESULT_OK;
168 for ( ; sdi != TDescObj->SubDescriptors.end() && KM_SUCCESS(result); sdi++ )
170 InterchangeObject* tmp_iobj = 0;
171 result = m_HeaderPart.GetMDObjectByID(*sdi, &tmp_iobj);
172 DescObject = static_cast<TimedTextResourceSubDescriptor*>(tmp_iobj);
174 if ( KM_SUCCESS(result) )
176 TimedTextResourceDescriptor TmpResource;
177 memcpy(TmpResource.ResourceID, DescObject->AncillaryResourceID.Value(), UUIDlen);
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;
184 else if ( DescObject->MIMEMediaType.find("image/png") != std::string::npos )
185 TmpResource.Type = MT_PNG;
188 TmpResource.Type = MT_BIN;
190 TDesc.ResourceList.push_back(TmpResource);
191 m_ResourceMap.insert(ResourceMap_t::value_type(DescObject->AncillaryResourceID, *sdi));
195 DefaultLogSink().Error("Broken sub-descriptor link\n");
196 return RESULT_FORMAT;
205 ASDCP::TimedText::MXFReader::h__Reader::OpenRead(const std::string& filename)
207 Result_t result = OpenMXFRead(filename);
209 if( ASDCP_SUCCESS(result) )
211 if ( m_EssenceDescriptor == 0 )
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);
218 if( ASDCP_SUCCESS(result) )
219 result = MD_to_TimedText_TDesc(m_TDesc);
227 ASDCP::TimedText::MXFReader::h__Reader::ReadTimedTextResource(FrameBuffer& FrameBuf,
228 AESDecContext* Ctx, HMACContext* HMAC)
230 if ( ! m_File.IsOpen() )
234 Result_t result = ReadEKLVFrame(0, FrameBuf, m_Dict->ul(MDD_TimedTextEssence), Ctx, HMAC);
236 if( ASDCP_SUCCESS(result) )
238 FrameBuf.AssetID(m_TDesc.AssetID);
239 FrameBuf.MIMEType("text/xml");
247 ASDCP::TimedText::MXFReader::h__Reader::ReadAncillaryResource(const byte_t* uuid, FrameBuffer& frame_buf,
248 AESDecContext* Ctx, HMACContext* HMAC)
250 KM_TEST_NULL_L(uuid);
253 ResourceMap_t::const_iterator ri = m_ResourceMap.find(RID);
254 if ( ri == m_ResourceMap.end() )
257 DefaultLogSink().Error("No such resource: %s\n", RID.EncodeHex(buf, 64));
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);
266 if ( KM_SUCCESS(result) )
269 result = ReadGenericStreamPartitionPayload(desc_object->EssenceStreamID, frame_buf, Ctx, HMAC);
272 if ( KM_SUCCESS(result) )
274 frame_buf.AssetID(uuid);
275 frame_buf.MIMEType(desc_object->MIMEMediaType);
282 //------------------------------------------------------------------------------------------
284 ASDCP::TimedText::MXFReader::MXFReader()
286 m_Reader = new h__Reader(DefaultSMPTEDict());
290 ASDCP::TimedText::MXFReader::~MXFReader()
294 // Warning: direct manipulation of MXF structures can interfere
295 // with the normal operation of the wrapper. Caveat emptor!
297 ASDCP::MXF::OP1aHeader&
298 ASDCP::TimedText::MXFReader::OP1aHeader()
300 if ( m_Reader.empty() )
302 assert(g_OP1aHeader);
303 return *g_OP1aHeader;
306 return m_Reader->m_HeaderPart;
309 // Warning: direct manipulation of MXF structures can interfere
310 // with the normal operation of the wrapper. Caveat emptor!
312 ASDCP::MXF::OPAtomIndexFooter&
313 ASDCP::TimedText::MXFReader::OPAtomIndexFooter()
315 if ( m_Reader.empty() )
317 assert(g_OPAtomIndexFooter);
318 return *g_OPAtomIndexFooter;
321 return m_Reader->m_IndexAccess;
324 // Warning: direct manipulation of MXF structures can interfere
325 // with the normal operation of the wrapper. Caveat emptor!
328 ASDCP::TimedText::MXFReader::RIP()
330 if ( m_Reader.empty() )
336 return m_Reader->m_RIP;
339 // Open the file for reading. The file must exist. Returns error if the
340 // operation cannot be completed.
342 ASDCP::TimedText::MXFReader::OpenRead(const std::string& filename) const
344 return m_Reader->OpenRead(filename);
347 // Fill the struct with the values from the file's header.
348 // Returns RESULT_INIT if the file is not open.
350 ASDCP::TimedText::MXFReader::FillTimedTextDescriptor(TimedText::TimedTextDescriptor& TDesc) const
352 if ( m_Reader && m_Reader->m_File.IsOpen() )
354 TDesc = m_Reader->m_TDesc;
361 // Fill the struct with the values from the file's header.
362 // Returns RESULT_INIT if the file is not open.
364 ASDCP::TimedText::MXFReader::FillWriterInfo(WriterInfo& Info) const
366 if ( m_Reader && m_Reader->m_File.IsOpen() )
368 Info = m_Reader->m_Info;
377 ASDCP::TimedText::MXFReader::ReadTimedTextResource(std::string& s, AESDecContext* Ctx, HMACContext* HMAC) const
379 FrameBuffer FrameBuf(2*Kumu::Megabyte);
381 Result_t result = ReadTimedTextResource(FrameBuf, Ctx, HMAC);
383 if ( ASDCP_SUCCESS(result) )
384 s.assign((char*)FrameBuf.Data(), FrameBuf.Size());
391 ASDCP::TimedText::MXFReader::ReadTimedTextResource(FrameBuffer& FrameBuf,
392 AESDecContext* Ctx, HMACContext* HMAC) const
394 if ( m_Reader && m_Reader->m_File.IsOpen() )
395 return m_Reader->ReadTimedTextResource(FrameBuf, Ctx, HMAC);
402 ASDCP::TimedText::MXFReader::ReadAncillaryResource(const byte_t* uuid, FrameBuffer& FrameBuf,
403 AESDecContext* Ctx, HMACContext* HMAC) const
405 if ( m_Reader && m_Reader->m_File.IsOpen() )
406 return m_Reader->ReadAncillaryResource(uuid, FrameBuf, Ctx, HMAC);
414 ASDCP::TimedText::MXFReader::DumpHeaderMetadata(FILE* stream) const
416 if ( m_Reader->m_File.IsOpen() )
417 m_Reader->m_HeaderPart.Dump(stream);
423 ASDCP::TimedText::MXFReader::DumpIndex(FILE* stream) const
425 if ( m_Reader->m_File.IsOpen() )
426 m_Reader->m_IndexAccess.Dump(stream);
431 ASDCP::TimedText::MXFReader::Close() const
433 if ( m_Reader && m_Reader->m_File.IsOpen() )
443 //------------------------------------------------------------------------------------------
447 class ASDCP::TimedText::MXFWriter::h__Writer : public ASDCP::h__ASDCPWriter
449 ASDCP_NO_COPY_CONSTRUCT(h__Writer);
453 TimedTextDescriptor m_TDesc;
454 byte_t m_EssenceUL[SMPTE_UL_LENGTH];
455 ui32_t m_EssenceStreamID;
457 h__Writer(const Dictionary& d) : ASDCP::h__ASDCPWriter(d), m_EssenceStreamID(10) {
458 memset(m_EssenceUL, 0, SMPTE_UL_LENGTH);
461 virtual ~h__Writer() {}
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);
468 Result_t TimedText_TDesc_to_MD(TimedText::TimedTextDescriptor& TDesc);
473 ASDCP::TimedText::MXFWriter::h__Writer::TimedText_TDesc_to_MD(TimedText::TimedTextDescriptor& TDesc)
475 assert(m_EssenceDescriptor);
476 MXF::TimedTextDescriptor* TDescObj = (MXF::TimedTextDescriptor*)m_EssenceDescriptor;
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;
489 ASDCP::TimedText::MXFWriter::h__Writer::OpenWrite(const std::string& filename, ui32_t HeaderSize)
491 if ( ! m_State.Test_BEGIN() )
494 Result_t result = m_File.OpenWrite(filename);
496 if ( ASDCP_SUCCESS(result) )
498 m_HeaderSize = HeaderSize;
499 m_EssenceDescriptor = new MXF::TimedTextDescriptor(m_Dict);
500 result = m_State.Goto_INIT();
508 ASDCP::TimedText::MXFWriter::h__Writer::SetSourceStream(ASDCP::TimedText::TimedTextDescriptor const& TDesc)
510 if ( ! m_State.Test_INIT() )
514 ResourceList_t::const_iterator ri;
515 Result_t result = TimedText_TDesc_to_MD(m_TDesc);
517 for ( ri = m_TDesc.ResourceList.begin() ; ri != m_TDesc.ResourceList.end() && ASDCP_SUCCESS(result); ri++ )
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);
527 // 72 == sizeof K, L, instanceuid, uuid + sizeof int32 + tag/len * 4
528 m_HeaderSize += ( resourceSubdescriptor->MIMEMediaType.ArchiveLength() * 2 /*ArchiveLength is broken*/ ) + 72;
531 m_EssenceStreamID = 10;
534 if ( ASDCP_SUCCESS(result) )
536 InitHeader(MXFVersion_2004);
539 if ( m_Info.LabelSetType == LS_MXF_SMPTE ) // ERK
541 m_RIP.PairArray.push_back(RIP::PartitionPair(0, 0)); // 3-part, no essence in header
545 DefaultLogSink().Error("Unable to write Interop timed-text MXF file. Use SMPTE DCP options instead.\n");
546 return RESULT_FORMAT;
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);
553 AddEssenceDescriptor(UL(m_Dict->ul(MDD_TimedTextWrappingClip)));
554 result = m_HeaderPart.WriteToFile(m_File, m_HeaderSize);
556 if ( KM_SUCCESS(result) )
557 result = CreateBodyPart(m_TDesc.EditRate);
560 if ( ASDCP_SUCCESS(result) )
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();
572 ASDCP::TimedText::MXFWriter::h__Writer::WriteTimedTextResource(const std::string& XMLDoc,
573 ASDCP::AESEncContext* Ctx, ASDCP::HMACContext* HMAC)
575 Result_t result = m_State.Goto_RUNNING();
577 if ( ASDCP_SUCCESS(result) )
579 // TODO: make sure it's XML
581 ui32_t str_size = XMLDoc.size();
582 FrameBuffer FrameBuf(str_size);
584 memcpy(FrameBuf.Data(), XMLDoc.c_str(), str_size);
585 FrameBuf.Size(str_size);
587 IndexTableSegment::IndexEntry Entry;
588 Entry.StreamOffset = m_StreamOffset;
589 result = WriteEKLVPacket(FrameBuf, m_EssenceUL, MXF_BER_LENGTH, Ctx, HMAC);
591 if ( ASDCP_SUCCESS(result) )
593 m_FooterPart.PushIndexEntry(Entry);
604 ASDCP::TimedText::MXFWriter::h__Writer::WriteAncillaryResource(const ASDCP::TimedText::FrameBuffer& FrameBuf,
605 ASDCP::AESEncContext* Ctx, ASDCP::HMACContext* HMAC)
607 if ( ! m_State.Test_RUNNING() )
610 Kumu::fpos_t here = m_File.Tell();
613 // create generic stream partition header
614 static UL GenericStream_DataElement(m_Dict->ul(MDD_GenericStream_DataElement));
615 MXF::Partition GSPart(m_Dict);
617 GSPart.ThisPartition = here;
618 GSPart.PreviousPartition = m_RIP.PairArray.back().ByteOffset;
619 GSPart.BodySID = m_EssenceStreamID;
620 GSPart.OperationalPattern = m_HeaderPart.OperationalPattern;
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);
627 if ( ASDCP_SUCCESS(result) )
628 result = WriteEKLVPacket(FrameBuf, GenericStream_DataElement.Value(), MXF_BER_LENGTH, Ctx, HMAC);
636 ASDCP::TimedText::MXFWriter::h__Writer::Finalize()
638 if ( ! m_State.Test_RUNNING() )
641 m_FramesWritten = m_TDesc.ContainerDuration;
642 m_State.Goto_FINAL();
644 return WriteASDCPFooter();
648 //------------------------------------------------------------------------------------------
650 ASDCP::TimedText::MXFWriter::MXFWriter()
654 ASDCP::TimedText::MXFWriter::~MXFWriter()
658 // Warning: direct manipulation of MXF structures can interfere
659 // with the normal operation of the wrapper. Caveat emptor!
661 ASDCP::MXF::OP1aHeader&
662 ASDCP::TimedText::MXFWriter::OP1aHeader()
664 if ( m_Writer.empty() )
666 assert(g_OP1aHeader);
667 return *g_OP1aHeader;
670 return m_Writer->m_HeaderPart;
673 // Warning: direct manipulation of MXF structures can interfere
674 // with the normal operation of the wrapper. Caveat emptor!
676 ASDCP::MXF::OPAtomIndexFooter&
677 ASDCP::TimedText::MXFWriter::OPAtomIndexFooter()
679 if ( m_Writer.empty() )
681 assert(g_OPAtomIndexFooter);
682 return *g_OPAtomIndexFooter;
685 return m_Writer->m_FooterPart;
688 // Warning: direct manipulation of MXF structures can interfere
689 // with the normal operation of the wrapper. Caveat emptor!
692 ASDCP::TimedText::MXFWriter::RIP()
694 if ( m_Writer.empty() )
700 return m_Writer->m_RIP;
703 // Open the file for writing. The file must not exist. Returns error if
704 // the operation cannot be completed.
706 ASDCP::TimedText::MXFWriter::OpenWrite(const std::string& filename, const WriterInfo& Info,
707 const TimedTextDescriptor& TDesc, ui32_t HeaderSize)
709 if ( Info.LabelSetType != LS_MXF_SMPTE )
711 DefaultLogSink().Error("Timed Text support requires LS_MXF_SMPTE\n");
712 return RESULT_FORMAT;
715 m_Writer = new h__Writer(DefaultSMPTEDict());
716 m_Writer->m_Info = Info;
718 Result_t result = m_Writer->OpenWrite(filename, HeaderSize);
720 if ( ASDCP_SUCCESS(result) )
721 result = m_Writer->SetSourceStream(TDesc);
723 if ( ASDCP_FAILURE(result) )
731 ASDCP::TimedText::MXFWriter::WriteTimedTextResource(const std::string& XMLDoc, AESEncContext* Ctx, HMACContext* HMAC)
733 if ( m_Writer.empty() )
736 return m_Writer->WriteTimedTextResource(XMLDoc, Ctx, HMAC);
741 ASDCP::TimedText::MXFWriter::WriteAncillaryResource(const FrameBuffer& FrameBuf, AESEncContext* Ctx, HMACContext* HMAC)
743 if ( m_Writer.empty() )
746 return m_Writer->WriteAncillaryResource(FrameBuf, Ctx, HMAC);
749 // Closes the MXF file, writing the index and other closing information.
751 ASDCP::TimedText::MXFWriter::Finalize()
753 if ( m_Writer.empty() )
756 return m_Writer->Finalize();
762 // end AS_DCP_timedText.cpp