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;
163 TDesc.ResourceList.clear();
165 Array<UUID>::const_iterator sdi = TDescObj->SubDescriptors.begin();
166 TimedTextResourceSubDescriptor* DescObject = 0;
167 Result_t result = RESULT_OK;
169 for ( ; sdi != TDescObj->SubDescriptors.end() && KM_SUCCESS(result); sdi++ )
171 InterchangeObject* tmp_iobj = 0;
172 result = m_HeaderPart.GetMDObjectByID(*sdi, &tmp_iobj);
173 DescObject = static_cast<TimedTextResourceSubDescriptor*>(tmp_iobj);
175 if ( KM_SUCCESS(result) )
177 TimedTextResourceDescriptor TmpResource;
178 memcpy(TmpResource.ResourceID, DescObject->AncillaryResourceID.Value(), UUIDlen);
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;
185 else if ( DescObject->MIMEMediaType.find("image/png") != std::string::npos )
186 TmpResource.Type = MT_PNG;
189 TmpResource.Type = MT_BIN;
191 TDesc.ResourceList.push_back(TmpResource);
192 m_ResourceMap.insert(ResourceMap_t::value_type(DescObject->AncillaryResourceID, *sdi));
196 DefaultLogSink().Error("Broken sub-descriptor link\n");
197 return RESULT_FORMAT;
206 ASDCP::TimedText::MXFReader::h__Reader::OpenRead(const std::string& filename)
208 Result_t result = OpenMXFRead(filename);
210 if( ASDCP_SUCCESS(result) )
212 if ( m_EssenceDescriptor == 0 )
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);
219 if( ASDCP_SUCCESS(result) )
220 result = MD_to_TimedText_TDesc(m_TDesc);
228 ASDCP::TimedText::MXFReader::h__Reader::ReadTimedTextResource(FrameBuffer& FrameBuf,
229 AESDecContext* Ctx, HMACContext* HMAC)
231 if ( ! m_File.IsOpen() )
235 Result_t result = ReadEKLVFrame(0, FrameBuf, m_Dict->ul(MDD_TimedTextEssence), Ctx, HMAC);
237 if( ASDCP_SUCCESS(result) )
239 FrameBuf.AssetID(m_TDesc.AssetID);
240 FrameBuf.MIMEType("text/xml");
248 ASDCP::TimedText::MXFReader::h__Reader::ReadAncillaryResource(const byte_t* uuid, FrameBuffer& frame_buf,
249 AESDecContext* Ctx, HMACContext* HMAC)
251 KM_TEST_NULL_L(uuid);
254 ResourceMap_t::const_iterator ri = m_ResourceMap.find(RID);
255 if ( ri == m_ResourceMap.end() )
258 DefaultLogSink().Error("No such resource: %s\n", RID.EncodeHex(buf, 64));
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);
267 if ( KM_SUCCESS(result) )
270 result = ReadGenericStreamPartitionPayload(desc_object->EssenceStreamID, frame_buf, Ctx, HMAC);
273 if ( KM_SUCCESS(result) )
275 frame_buf.AssetID(uuid);
276 frame_buf.MIMEType(desc_object->MIMEMediaType);
283 //------------------------------------------------------------------------------------------
285 ASDCP::TimedText::MXFReader::MXFReader()
287 m_Reader = new h__Reader(DefaultSMPTEDict());
291 ASDCP::TimedText::MXFReader::~MXFReader()
295 // Warning: direct manipulation of MXF structures can interfere
296 // with the normal operation of the wrapper. Caveat emptor!
298 ASDCP::MXF::OP1aHeader&
299 ASDCP::TimedText::MXFReader::OP1aHeader()
301 if ( m_Reader.empty() )
303 assert(g_OP1aHeader);
304 return *g_OP1aHeader;
307 return m_Reader->m_HeaderPart;
310 // Warning: direct manipulation of MXF structures can interfere
311 // with the normal operation of the wrapper. Caveat emptor!
313 ASDCP::MXF::OPAtomIndexFooter&
314 ASDCP::TimedText::MXFReader::OPAtomIndexFooter()
316 if ( m_Reader.empty() )
318 assert(g_OPAtomIndexFooter);
319 return *g_OPAtomIndexFooter;
322 return m_Reader->m_IndexAccess;
325 // Warning: direct manipulation of MXF structures can interfere
326 // with the normal operation of the wrapper. Caveat emptor!
329 ASDCP::TimedText::MXFReader::RIP()
331 if ( m_Reader.empty() )
337 return m_Reader->m_RIP;
340 // Open the file for reading. The file must exist. Returns error if the
341 // operation cannot be completed.
343 ASDCP::TimedText::MXFReader::OpenRead(const std::string& filename) const
345 return m_Reader->OpenRead(filename);
348 // Fill the struct with the values from the file's header.
349 // Returns RESULT_INIT if the file is not open.
351 ASDCP::TimedText::MXFReader::FillTimedTextDescriptor(TimedText::TimedTextDescriptor& TDesc) const
353 if ( m_Reader && m_Reader->m_File.IsOpen() )
355 TDesc = m_Reader->m_TDesc;
362 // Fill the struct with the values from the file's header.
363 // Returns RESULT_INIT if the file is not open.
365 ASDCP::TimedText::MXFReader::FillWriterInfo(WriterInfo& Info) const
367 if ( m_Reader && m_Reader->m_File.IsOpen() )
369 Info = m_Reader->m_Info;
378 ASDCP::TimedText::MXFReader::ReadTimedTextResource(std::string& s, AESDecContext* Ctx, HMACContext* HMAC) const
380 FrameBuffer FrameBuf(2*Kumu::Megabyte);
382 Result_t result = ReadTimedTextResource(FrameBuf, Ctx, HMAC);
384 if ( ASDCP_SUCCESS(result) )
385 s.assign((char*)FrameBuf.Data(), FrameBuf.Size());
392 ASDCP::TimedText::MXFReader::ReadTimedTextResource(FrameBuffer& FrameBuf,
393 AESDecContext* Ctx, HMACContext* HMAC) const
395 if ( m_Reader && m_Reader->m_File.IsOpen() )
396 return m_Reader->ReadTimedTextResource(FrameBuf, Ctx, HMAC);
403 ASDCP::TimedText::MXFReader::ReadAncillaryResource(const byte_t* uuid, FrameBuffer& FrameBuf,
404 AESDecContext* Ctx, HMACContext* HMAC) const
406 if ( m_Reader && m_Reader->m_File.IsOpen() )
407 return m_Reader->ReadAncillaryResource(uuid, FrameBuf, Ctx, HMAC);
415 ASDCP::TimedText::MXFReader::DumpHeaderMetadata(FILE* stream) const
417 if ( m_Reader->m_File.IsOpen() )
418 m_Reader->m_HeaderPart.Dump(stream);
424 ASDCP::TimedText::MXFReader::DumpIndex(FILE* stream) const
426 if ( m_Reader->m_File.IsOpen() )
427 m_Reader->m_IndexAccess.Dump(stream);
432 ASDCP::TimedText::MXFReader::Close() const
434 if ( m_Reader && m_Reader->m_File.IsOpen() )
444 //------------------------------------------------------------------------------------------
448 class ASDCP::TimedText::MXFWriter::h__Writer : public ASDCP::h__ASDCPWriter
450 ASDCP_NO_COPY_CONSTRUCT(h__Writer);
454 TimedTextDescriptor m_TDesc;
455 byte_t m_EssenceUL[SMPTE_UL_LENGTH];
456 ui32_t m_EssenceStreamID;
458 h__Writer(const Dictionary& d) : ASDCP::h__ASDCPWriter(d), m_EssenceStreamID(10) {
459 memset(m_EssenceUL, 0, SMPTE_UL_LENGTH);
462 virtual ~h__Writer() {}
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);
469 Result_t TimedText_TDesc_to_MD(TimedText::TimedTextDescriptor& TDesc);
474 ASDCP::TimedText::MXFWriter::h__Writer::TimedText_TDesc_to_MD(TimedText::TimedTextDescriptor& TDesc)
476 assert(m_EssenceDescriptor);
477 MXF::TimedTextDescriptor* TDescObj = (MXF::TimedTextDescriptor*)m_EssenceDescriptor;
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;
490 ASDCP::TimedText::MXFWriter::h__Writer::OpenWrite(const std::string& filename, ui32_t HeaderSize)
492 if ( ! m_State.Test_BEGIN() )
495 Result_t result = m_File.OpenWrite(filename);
497 if ( ASDCP_SUCCESS(result) )
499 m_HeaderSize = HeaderSize;
500 m_EssenceDescriptor = new MXF::TimedTextDescriptor(m_Dict);
501 result = m_State.Goto_INIT();
509 ASDCP::TimedText::MXFWriter::h__Writer::SetSourceStream(ASDCP::TimedText::TimedTextDescriptor const& TDesc)
511 if ( ! m_State.Test_INIT() )
515 ResourceList_t::const_iterator ri;
516 Result_t result = TimedText_TDesc_to_MD(m_TDesc);
518 for ( ri = m_TDesc.ResourceList.begin() ; ri != m_TDesc.ResourceList.end() && ASDCP_SUCCESS(result); ri++ )
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);
528 // 72 == sizeof K, L, instanceuid, uuid + sizeof int32 + tag/len * 4
529 m_HeaderSize += ( resourceSubdescriptor->MIMEMediaType.ArchiveLength() * 2 /*ArchiveLength is broken*/ ) + 72;
532 m_EssenceStreamID = 10;
535 if ( ASDCP_SUCCESS(result) )
537 InitHeader(MXFVersion_2004);
540 if ( m_Info.LabelSetType == LS_MXF_SMPTE ) // ERK
542 m_RIP.PairArray.push_back(RIP::PartitionPair(0, 0)); // 3-part, no essence in header
546 DefaultLogSink().Error("Unable to write Interop timed-text MXF file. Use SMPTE DCP options instead.\n");
547 return RESULT_FORMAT;
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);
554 AddEssenceDescriptor(UL(m_Dict->ul(MDD_TimedTextWrappingClip)));
555 result = m_HeaderPart.WriteToFile(m_File, m_HeaderSize);
557 if ( KM_SUCCESS(result) )
558 result = CreateBodyPart(m_TDesc.EditRate);
561 if ( ASDCP_SUCCESS(result) )
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();
573 ASDCP::TimedText::MXFWriter::h__Writer::WriteTimedTextResource(const std::string& XMLDoc,
574 ASDCP::AESEncContext* Ctx, ASDCP::HMACContext* HMAC)
576 Result_t result = m_State.Goto_RUNNING();
578 if ( ASDCP_SUCCESS(result) )
580 // TODO: make sure it's XML
582 ui32_t str_size = XMLDoc.size();
583 FrameBuffer FrameBuf(str_size);
585 memcpy(FrameBuf.Data(), XMLDoc.c_str(), str_size);
586 FrameBuf.Size(str_size);
588 IndexTableSegment::IndexEntry Entry;
589 Entry.StreamOffset = m_StreamOffset;
590 result = WriteEKLVPacket(FrameBuf, m_EssenceUL, MXF_BER_LENGTH, Ctx, HMAC);
592 if ( ASDCP_SUCCESS(result) )
594 m_FooterPart.PushIndexEntry(Entry);
605 ASDCP::TimedText::MXFWriter::h__Writer::WriteAncillaryResource(const ASDCP::TimedText::FrameBuffer& FrameBuf,
606 ASDCP::AESEncContext* Ctx, ASDCP::HMACContext* HMAC)
608 if ( ! m_State.Test_RUNNING() )
611 Kumu::fpos_t here = m_File.Tell();
614 // create generic stream partition header
615 static UL GenericStream_DataElement(m_Dict->ul(MDD_GenericStream_DataElement));
616 MXF::Partition GSPart(m_Dict);
618 GSPart.ThisPartition = here;
619 GSPart.PreviousPartition = m_RIP.PairArray.back().ByteOffset;
620 GSPart.BodySID = m_EssenceStreamID;
621 GSPart.OperationalPattern = m_HeaderPart.OperationalPattern;
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);
628 if ( ASDCP_SUCCESS(result) )
629 result = WriteEKLVPacket(FrameBuf, GenericStream_DataElement.Value(), MXF_BER_LENGTH, Ctx, HMAC);
637 ASDCP::TimedText::MXFWriter::h__Writer::Finalize()
639 if ( ! m_State.Test_RUNNING() )
642 m_FramesWritten = m_TDesc.ContainerDuration;
643 m_State.Goto_FINAL();
645 return WriteASDCPFooter();
649 //------------------------------------------------------------------------------------------
651 ASDCP::TimedText::MXFWriter::MXFWriter()
655 ASDCP::TimedText::MXFWriter::~MXFWriter()
659 // Warning: direct manipulation of MXF structures can interfere
660 // with the normal operation of the wrapper. Caveat emptor!
662 ASDCP::MXF::OP1aHeader&
663 ASDCP::TimedText::MXFWriter::OP1aHeader()
665 if ( m_Writer.empty() )
667 assert(g_OP1aHeader);
668 return *g_OP1aHeader;
671 return m_Writer->m_HeaderPart;
674 // Warning: direct manipulation of MXF structures can interfere
675 // with the normal operation of the wrapper. Caveat emptor!
677 ASDCP::MXF::OPAtomIndexFooter&
678 ASDCP::TimedText::MXFWriter::OPAtomIndexFooter()
680 if ( m_Writer.empty() )
682 assert(g_OPAtomIndexFooter);
683 return *g_OPAtomIndexFooter;
686 return m_Writer->m_FooterPart;
689 // Warning: direct manipulation of MXF structures can interfere
690 // with the normal operation of the wrapper. Caveat emptor!
693 ASDCP::TimedText::MXFWriter::RIP()
695 if ( m_Writer.empty() )
701 return m_Writer->m_RIP;
704 // Open the file for writing. The file must not exist. Returns error if
705 // the operation cannot be completed.
707 ASDCP::TimedText::MXFWriter::OpenWrite(const std::string& filename, const WriterInfo& Info,
708 const TimedTextDescriptor& TDesc, ui32_t HeaderSize)
710 if ( Info.LabelSetType != LS_MXF_SMPTE )
712 DefaultLogSink().Error("Timed Text support requires LS_MXF_SMPTE\n");
713 return RESULT_FORMAT;
716 m_Writer = new h__Writer(DefaultSMPTEDict());
717 m_Writer->m_Info = Info;
719 Result_t result = m_Writer->OpenWrite(filename, HeaderSize);
721 if ( ASDCP_SUCCESS(result) )
722 result = m_Writer->SetSourceStream(TDesc);
724 if ( ASDCP_FAILURE(result) )
732 ASDCP::TimedText::MXFWriter::WriteTimedTextResource(const std::string& XMLDoc, AESEncContext* Ctx, HMACContext* HMAC)
734 if ( m_Writer.empty() )
737 return m_Writer->WriteTimedTextResource(XMLDoc, Ctx, HMAC);
742 ASDCP::TimedText::MXFWriter::WriteAncillaryResource(const FrameBuffer& FrameBuf, AESEncContext* Ctx, HMACContext* HMAC)
744 if ( m_Writer.empty() )
747 return m_Writer->WriteAncillaryResource(FrameBuf, Ctx, HMAC);
750 // Closes the MXF file, writing the index and other closing information.
752 ASDCP::TimedText::MXFWriter::Finalize()
754 if ( m_Writer.empty() )
757 return m_Writer->Finalize();
763 // end AS_DCP_timedText.cpp