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_02_internal.h"
38 using Kumu::GenRandomValue;
40 static std::string TIMED_TEXT_PACKAGE_LABEL = "File Package: SMPTE ST 429-5 / ST 2067-5 clip wrapping of IMF Timed Text data";
41 static std::string TIMED_TEXT_DEF_LABEL = "Timed Text Track";
44 //------------------------------------------------------------------------------------------
47 MIME2str(TimedText::MIMEType_t m)
49 if ( m == TimedText::MT_PNG )
52 else if ( m == TimedText::MT_OPENTYPE )
53 return "application/x-font-opentype";
55 return "application/octet-stream";
58 //------------------------------------------------------------------------------------------
60 typedef std::map<Kumu::UUID, Kumu::UUID> ResourceMap_t;
62 class AS_02::TimedText::MXFReader::h__Reader : public AS_02::h__AS02Reader
64 ASDCP::MXF::TimedTextDescriptor* m_EssenceDescriptor;
65 ResourceMap_t m_ResourceMap;
67 ASDCP_NO_COPY_CONSTRUCT(h__Reader);
70 TimedTextDescriptor m_TDesc;
72 h__Reader(const Dictionary& d) : AS_02::h__AS02Reader(d), m_EssenceDescriptor(0) {
73 memset(&m_TDesc.AssetID, 0, UUIDlen);
76 virtual ~h__Reader() {}
78 Result_t OpenRead(const std::string&);
79 Result_t MD_to_TimedText_TDesc(TimedTextDescriptor& TDesc);
80 Result_t ReadTimedTextResource(ASDCP::TimedText::FrameBuffer& FrameBuf, AESDecContext* Ctx, HMACContext* HMAC);
81 Result_t ReadAncillaryResource(const Kumu::UUID&, ASDCP::TimedText::FrameBuffer& FrameBuf, AESDecContext* Ctx, HMACContext* HMAC);
86 AS_02::TimedText::MXFReader::h__Reader::MD_to_TimedText_TDesc(TimedTextDescriptor& TDesc)
88 assert(m_EssenceDescriptor);
89 memset(&m_TDesc.AssetID, 0, UUIDlen);
90 ASDCP::MXF::TimedTextDescriptor* TDescObj = (ASDCP::MXF::TimedTextDescriptor*)m_EssenceDescriptor;
92 TDesc.EditRate = TDescObj->SampleRate;
93 assert(TDescObj->ContainerDuration <= 0xFFFFFFFFL);
94 TDesc.ContainerDuration = (ui32_t) TDescObj->ContainerDuration;
95 memcpy(TDesc.AssetID, TDescObj->ResourceID.Value(), UUIDlen);
96 TDesc.NamespaceName = TDescObj->NamespaceURI;
97 TDesc.EncodingName = TDescObj->UCSEncoding;
99 Array<Kumu::UUID>::const_iterator sdi = TDescObj->SubDescriptors.begin();
100 TimedTextResourceSubDescriptor* DescObject = 0;
101 Result_t result = RESULT_OK;
103 for ( ; sdi != TDescObj->SubDescriptors.end() && KM_SUCCESS(result); sdi++ )
105 InterchangeObject* tmp_iobj = 0;
106 result = m_HeaderPart.GetMDObjectByID(*sdi, &tmp_iobj);
107 DescObject = static_cast<TimedTextResourceSubDescriptor*>(tmp_iobj);
109 if ( KM_SUCCESS(result) )
111 TimedTextResourceDescriptor TmpResource;
112 memcpy(TmpResource.ResourceID, DescObject->AncillaryResourceID.Value(), UUIDlen);
114 if ( DescObject->MIMEMediaType.find("application/x-font-opentype") != std::string::npos
115 || DescObject->MIMEMediaType.find("application/x-opentype") != std::string::npos
116 || DescObject->MIMEMediaType.find("font/opentype") != std::string::npos )
118 TmpResource.Type = ASDCP::TimedText::MT_OPENTYPE;
120 else if ( DescObject->MIMEMediaType.find("image/png") != std::string::npos )
122 TmpResource.Type = ASDCP::TimedText::MT_PNG;
126 TmpResource.Type = ASDCP::TimedText::MT_BIN;
129 TDesc.ResourceList.push_back(TmpResource);
130 m_ResourceMap.insert(ResourceMap_t::value_type(DescObject->AncillaryResourceID, *sdi));
134 DefaultLogSink().Error("Broken sub-descriptor link\n");
135 return RESULT_FORMAT;
144 AS_02::TimedText::MXFReader::h__Reader::OpenRead(const std::string& filename)
146 Result_t result = OpenMXFRead(filename.c_str());
148 if( ASDCP_SUCCESS(result) )
150 if ( m_EssenceDescriptor == 0 )
152 InterchangeObject* tmp_iobj = 0;
153 result = m_HeaderPart.GetMDObjectByType(OBJ_TYPE_ARGS(TimedTextDescriptor), &tmp_iobj);
154 m_EssenceDescriptor = static_cast<ASDCP::MXF::TimedTextDescriptor*>(tmp_iobj);
157 if( ASDCP_SUCCESS(result) )
158 result = MD_to_TimedText_TDesc(m_TDesc);
166 AS_02::TimedText::MXFReader::h__Reader::ReadTimedTextResource(ASDCP::TimedText::FrameBuffer& FrameBuf,
167 AESDecContext* Ctx, HMACContext* HMAC)
169 if ( ! m_File.IsOpen() )
175 Result_t result = ReadEKLVFrame(0, FrameBuf, m_Dict->ul(MDD_TimedTextEssence), Ctx, HMAC);
177 if( ASDCP_SUCCESS(result) )
179 FrameBuf.AssetID(m_TDesc.AssetID);
180 FrameBuf.MIMEType("text/xml");
188 AS_02::TimedText::MXFReader::h__Reader::ReadAncillaryResource(const Kumu::UUID& uuid,
189 ASDCP::TimedText::FrameBuffer& frame_buf,
190 AESDecContext* Ctx, HMACContext* HMAC)
192 ResourceMap_t::const_iterator ri = m_ResourceMap.find(uuid);
193 if ( ri == m_ResourceMap.end() )
196 DefaultLogSink().Error("No such resource: %s\n", uuid.EncodeHex(buf, 64));
200 // get the subdescriptor
201 InterchangeObject* tmp_iobj = 0;
202 Result_t result = m_HeaderPart.GetMDObjectByID((*ri).second, &tmp_iobj);
203 TimedTextResourceSubDescriptor* desc_object = dynamic_cast<TimedTextResourceSubDescriptor*>(tmp_iobj);
205 if ( KM_SUCCESS(result) )
208 result = ReadGenericStreamPartitionPayload(desc_object->EssenceStreamID, frame_buf, Ctx, HMAC);
211 if ( KM_SUCCESS(result) )
213 frame_buf.AssetID(uuid.Value());
214 frame_buf.MIMEType(desc_object->MIMEMediaType);
221 //------------------------------------------------------------------------------------------
223 AS_02::TimedText::MXFReader::MXFReader()
225 m_Reader = new h__Reader(DefaultSMPTEDict());
229 AS_02::TimedText::MXFReader::~MXFReader()
233 // Warning: direct manipulation of MXF structures can interfere
234 // with the normal operation of the wrapper. Caveat emptor!
236 ASDCP::MXF::OP1aHeader&
237 AS_02::TimedText::MXFReader::OP1aHeader()
239 if ( m_Reader.empty() )
241 assert(g_OP1aHeader);
242 return *g_OP1aHeader;
245 return m_Reader->m_HeaderPart;
248 // Warning: direct manipulation of MXF structures can interfere
249 // with the normal operation of the wrapper. Caveat emptor!
251 AS_02::MXF::AS02IndexReader&
252 AS_02::TimedText::MXFReader::AS02IndexReader()
254 if ( m_Reader.empty() )
256 assert(g_AS02IndexReader);
257 return *g_AS02IndexReader;
260 return m_Reader->m_IndexAccess;
263 // Warning: direct manipulation of MXF structures can interfere
264 // with the normal operation of the wrapper. Caveat emptor!
267 AS_02::TimedText::MXFReader::RIP()
269 if ( m_Reader.empty() )
275 return m_Reader->m_RIP;
278 // Open the file for reading. The file must exist. Returns error if the
279 // operation cannot be completed.
281 AS_02::TimedText::MXFReader::OpenRead(const std::string& filename) const
283 return m_Reader->OpenRead(filename);
286 // Fill the struct with the values from the file's header.
287 // Returns RESULT_INIT if the file is not open.
289 AS_02::TimedText::MXFReader::FillTimedTextDescriptor(TimedText::TimedTextDescriptor& TDesc) const
291 if ( m_Reader && m_Reader->m_File.IsOpen() )
293 TDesc = m_Reader->m_TDesc;
300 // Fill the struct with the values from the file's header.
301 // Returns RESULT_INIT if the file is not open.
303 AS_02::TimedText::MXFReader::FillWriterInfo(WriterInfo& Info) const
305 if ( m_Reader && m_Reader->m_File.IsOpen() )
307 Info = m_Reader->m_Info;
316 AS_02::TimedText::MXFReader::ReadTimedTextResource(std::string& s, AESDecContext* Ctx, HMACContext* HMAC) const
318 ASDCP::TimedText::FrameBuffer FrameBuf(8*Kumu::Megabyte);
320 Result_t result = ReadTimedTextResource(FrameBuf, Ctx, HMAC);
322 if ( ASDCP_SUCCESS(result) )
323 s.assign((char*)FrameBuf.Data(), FrameBuf.Size());
330 AS_02::TimedText::MXFReader::ReadTimedTextResource(ASDCP::TimedText::FrameBuffer& FrameBuf,
331 AESDecContext* Ctx, HMACContext* HMAC) const
333 if ( m_Reader && m_Reader->m_File.IsOpen() )
334 return m_Reader->ReadTimedTextResource(FrameBuf, Ctx, HMAC);
341 AS_02::TimedText::MXFReader::ReadAncillaryResource(const Kumu::UUID& uuid, ASDCP::TimedText::FrameBuffer& FrameBuf,
342 AESDecContext* Ctx, HMACContext* HMAC) const
344 if ( m_Reader && m_Reader->m_File.IsOpen() )
345 return m_Reader->ReadAncillaryResource(uuid, FrameBuf, Ctx, HMAC);
353 AS_02::TimedText::MXFReader::DumpHeaderMetadata(FILE* stream) const
355 if ( m_Reader->m_File.IsOpen() )
356 m_Reader->m_HeaderPart.Dump(stream);
362 AS_02::TimedText::MXFReader::DumpIndex(FILE* stream) const
364 if ( m_Reader->m_File.IsOpen() )
365 m_Reader->m_IndexAccess.Dump(stream);
370 AS_02::TimedText::MXFReader::Close() const
372 if ( m_Reader && m_Reader->m_File.IsOpen() )
382 //------------------------------------------------------------------------------------------
386 class AS_02::TimedText::MXFWriter::h__Writer : public AS_02::h__AS02WriterClip
388 ASDCP_NO_COPY_CONSTRUCT(h__Writer);
392 TimedTextDescriptor m_TDesc;
393 byte_t m_EssenceUL[SMPTE_UL_LENGTH];
394 ui32_t m_EssenceStreamID;
395 ASDCP::Rational m_EditRate;
397 h__Writer(const Dictionary& d) : AS_02::h__AS02WriterClip(d), m_EssenceStreamID(10)
399 memset(m_EssenceUL, 0, SMPTE_UL_LENGTH);
402 virtual ~h__Writer() {}
404 Result_t OpenWrite(const std::string&, ui32_t HeaderSize);
405 Result_t SetSourceStream(const ASDCP::TimedText::TimedTextDescriptor&);
406 Result_t WriteTimedTextResource(const std::string& XMLDoc, AESEncContext* = 0, HMACContext* = 0);
407 Result_t WriteAncillaryResource(const ASDCP::TimedText::FrameBuffer&, AESEncContext* = 0, HMACContext* = 0);
409 Result_t TimedText_TDesc_to_MD(ASDCP::TimedText::TimedTextDescriptor& TDesc);
414 AS_02::TimedText::MXFWriter::h__Writer::TimedText_TDesc_to_MD(TimedText::TimedTextDescriptor& TDesc)
416 assert(m_EssenceDescriptor);
417 ASDCP::MXF::TimedTextDescriptor* TDescObj = (ASDCP::MXF::TimedTextDescriptor*)m_EssenceDescriptor;
419 TDescObj->SampleRate = TDesc.EditRate;
420 TDescObj->ContainerDuration = TDesc.ContainerDuration;
421 TDescObj->ResourceID.Set(TDesc.AssetID);
422 TDescObj->NamespaceURI = TDesc.NamespaceName;
423 TDescObj->UCSEncoding = TDesc.EncodingName;
430 AS_02::TimedText::MXFWriter::h__Writer::OpenWrite(const std::string& filename, ui32_t HeaderSize)
432 if ( ! m_State.Test_BEGIN() )
434 KM_RESULT_STATE_HERE();
438 Result_t result = m_File.OpenWrite(filename.c_str());
440 if ( ASDCP_SUCCESS(result) )
442 m_HeaderSize = HeaderSize;
443 m_EssenceDescriptor = new ASDCP::MXF::TimedTextDescriptor(m_Dict);
444 result = m_State.Goto_INIT();
452 AS_02::TimedText::MXFWriter::h__Writer::SetSourceStream(ASDCP::TimedText::TimedTextDescriptor const& TDesc)
454 if ( ! m_State.Test_INIT() )
456 KM_RESULT_STATE_HERE();
463 Result_t result = TimedText_TDesc_to_MD(m_TDesc);
465 if ( KM_SUCCESS(result) )
467 ResourceList_t::const_iterator i;
468 for ( i = m_TDesc.ResourceList.begin() ; i != m_TDesc.ResourceList.end(); ++i )
470 TimedTextResourceSubDescriptor* resourceSubdescriptor = new TimedTextResourceSubDescriptor(m_Dict);
471 GenRandomValue(resourceSubdescriptor->InstanceUID);
472 resourceSubdescriptor->AncillaryResourceID.Set((*i).ResourceID);
473 resourceSubdescriptor->MIMEMediaType = MIME2str((*i).Type);
474 resourceSubdescriptor->EssenceStreamID = m_EssenceStreamID++;
475 m_EssenceSubDescriptorList.push_back((FileDescriptor*)resourceSubdescriptor);
476 m_EssenceDescriptor->SubDescriptors.push_back(resourceSubdescriptor->InstanceUID);
478 // 72 == sizeof K, L, instanceuid, uuid + sizeof int32 + tag/len * 4
479 m_HeaderSize += ( resourceSubdescriptor->MIMEMediaType.ArchiveLength() * 2 /*ArchiveLength is broken*/ ) + 72;
483 //Reset m_EssenceStreamID to 10 for later usage in WriteAncillaryResource
484 m_EssenceStreamID = 10;
487 if ( KM_SUCCESS(result) )
489 memcpy(m_EssenceUL, m_Dict->ul(MDD_TimedTextEssence), SMPTE_UL_LENGTH);
490 m_EssenceUL[SMPTE_UL_LENGTH-1] = 1; // first (and only) essence container
491 result = m_State.Goto_READY();
494 if ( KM_SUCCESS(result) )
496 m_EditRate = TDesc.EditRate;
497 result = WriteAS02Header(TIMED_TEXT_PACKAGE_LABEL, UL(m_Dict->ul(MDD_TimedTextWrappingClip)),
498 "Data Track", UL(m_EssenceUL), UL(m_Dict->ul(MDD_DataDataDef)),
499 TDesc.EditRate, derive_timecode_rate_from_edit_rate(TDesc.EditRate));
507 AS_02::TimedText::MXFWriter::h__Writer::WriteTimedTextResource(const std::string& XMLDoc,
508 ASDCP::AESEncContext* Ctx, ASDCP::HMACContext* HMAC)
510 ASDCP::FrameBuffer segment_buffer;
511 IndexTableSegment::IndexEntry index_entry;
512 Result_t result = m_State.Goto_RUNNING();
514 if ( KM_SUCCESS(result) )
516 // TODO: make sure it's XML
518 ui32_t str_size = XMLDoc.size();
519 ASDCP::TimedText::FrameBuffer FrameBuf(str_size);
521 memcpy(FrameBuf.Data(), XMLDoc.c_str(), str_size);
522 FrameBuf.Size(str_size);
523 index_entry.StreamOffset = m_StreamOffset;
525 result = Write_EKLV_Packet(m_File, *m_Dict, m_HeaderPart, m_Info, m_CtFrameBuf, m_FramesWritten,
526 m_StreamOffset, FrameBuf, m_EssenceUL, MXF_BER_LENGTH, Ctx, HMAC);
529 if ( KM_SUCCESS(result) )
531 // encode the index table
532 IndexTableSegment::DeltaEntry nil_delta_entry;
533 IndexTableSegment segment(m_Dict);
534 segment.m_Lookup = &m_HeaderPart.m_Primer;
535 GenRandomValue(segment.InstanceUID);
537 segment.DeltaEntryArray.push_back(nil_delta_entry);
538 segment.IndexEditRate = m_EditRate;
539 segment.IndexStartPosition = 0;
540 segment.IndexDuration = -1;
541 segment.IndexEntryArray.push_back(index_entry);
543 result = segment_buffer.Capacity(MaxIndexSegmentSize); // segment-count * max-segment-size
545 if ( KM_SUCCESS(result) )
547 result = segment.WriteToBuffer(segment_buffer);
551 if ( KM_SUCCESS(result) )
553 // create an index partition header
554 Kumu::fpos_t here = m_File.Tell();
557 ASDCP::MXF::Partition partition(m_Dict);
558 partition.MajorVersion = m_HeaderPart.MajorVersion;
559 partition.MinorVersion = m_HeaderPart.MinorVersion;
560 partition.ThisPartition = here;
561 partition.BodySID = 0;
562 partition.IndexSID = 129;
563 partition.IndexByteCount = segment_buffer.Size();
564 partition.PreviousPartition = m_RIP.PairArray.back().ByteOffset;
565 partition.OperationalPattern = m_HeaderPart.OperationalPattern;
567 m_RIP.PairArray.push_back(RIP::PartitionPair(0, here));
568 partition.EssenceContainers = m_HeaderPart.EssenceContainers;
569 UL TmpUL(m_Dict->ul(MDD_ClosedCompleteBodyPartition));
570 result = partition.WriteToFile(m_File, TmpUL);
573 if ( KM_SUCCESS(result) )
575 // write the encoded index table
576 ui32_t write_count = 0;
577 result = m_File.Write(segment_buffer.RoData(), segment_buffer.Size(), &write_count);
578 assert(write_count == segment_buffer.Size());
581 if ( KM_SUCCESS(result) )
592 AS_02::TimedText::MXFWriter::h__Writer::WriteAncillaryResource(const ASDCP::TimedText::FrameBuffer& FrameBuf,
593 ASDCP::AESEncContext* Ctx, ASDCP::HMACContext* HMAC)
595 if ( ! m_State.Test_RUNNING() )
597 KM_RESULT_STATE_HERE();
601 Kumu::fpos_t here = m_File.Tell();
604 // create generic stream partition header
605 static UL GenericStream_DataElement(m_Dict->ul(MDD_GenericStream_DataElement));
606 ASDCP::MXF::Partition GSPart(m_Dict);
608 GSPart.MajorVersion = m_HeaderPart.MajorVersion;
609 GSPart.MinorVersion = m_HeaderPart.MinorVersion;
610 GSPart.ThisPartition = here;
611 GSPart.PreviousPartition = m_RIP.PairArray.back().ByteOffset;
612 GSPart.BodySID = m_EssenceStreamID;
613 GSPart.OperationalPattern = m_HeaderPart.OperationalPattern;
615 m_RIP.PairArray.push_back(RIP::PartitionPair(m_EssenceStreamID++, here));
616 GSPart.EssenceContainers = m_HeaderPart.EssenceContainers;
617 UL TmpUL(m_Dict->ul(MDD_GenericStreamPartition));
618 Result_t result = GSPart.WriteToFile(m_File, TmpUL);
620 if ( KM_SUCCESS(result) )
622 result = Write_EKLV_Packet(m_File, *m_Dict, m_HeaderPart, m_Info, m_CtFrameBuf, m_FramesWritten,
623 m_StreamOffset, FrameBuf, GenericStream_DataElement.Value(),
624 MXF_BER_LENGTH, Ctx, HMAC);
633 AS_02::TimedText::MXFWriter::h__Writer::Finalize()
635 if ( ! m_State.Test_RUNNING() )
637 DefaultLogSink().Error("Cannot finalize file, the primary essence resource has not been written.\n");
641 m_FramesWritten = m_TDesc.ContainerDuration;
643 Result_t result = m_State.Goto_FINAL();
645 if ( KM_SUCCESS(result) )
647 result = WriteAS02Footer();
654 //------------------------------------------------------------------------------------------
656 AS_02::TimedText::MXFWriter::MXFWriter()
660 AS_02::TimedText::MXFWriter::~MXFWriter()
664 // Warning: direct manipulation of MXF structures can interfere
665 // with the normal operation of the wrapper. Caveat emptor!
667 ASDCP::MXF::OP1aHeader&
668 AS_02::TimedText::MXFWriter::OP1aHeader()
670 if ( m_Writer.empty() )
672 assert(g_OP1aHeader);
673 return *g_OP1aHeader;
676 return m_Writer->m_HeaderPart;
679 // Warning: direct manipulation of MXF structures can interfere
680 // with the normal operation of the wrapper. Caveat emptor!
683 AS_02::TimedText::MXFWriter::RIP()
685 if ( m_Writer.empty() )
691 return m_Writer->m_RIP;
694 // Open the file for writing. The file must not exist. Returns error if
695 // the operation cannot be completed.
697 AS_02::TimedText::MXFWriter::OpenWrite(const std::string& filename, const WriterInfo& Info,
698 const TimedTextDescriptor& TDesc, ui32_t HeaderSize)
700 if ( Info.LabelSetType != LS_MXF_SMPTE )
702 DefaultLogSink().Error("Timed Text support requires LS_MXF_SMPTE\n");
703 return RESULT_FORMAT;
706 m_Writer = new h__Writer(DefaultSMPTEDict());
707 m_Writer->m_Info = Info;
709 Result_t result = m_Writer->OpenWrite(filename, HeaderSize);
711 if ( ASDCP_SUCCESS(result) )
712 result = m_Writer->SetSourceStream(TDesc);
714 if ( ASDCP_FAILURE(result) )
722 AS_02::TimedText::MXFWriter::WriteTimedTextResource(const std::string& XMLDoc, AESEncContext* Ctx, HMACContext* HMAC)
724 if ( m_Writer.empty() )
727 return m_Writer->WriteTimedTextResource(XMLDoc, Ctx, HMAC);
732 AS_02::TimedText::MXFWriter::WriteAncillaryResource(const ASDCP::TimedText::FrameBuffer& FrameBuf, AESEncContext* Ctx, HMACContext* HMAC)
734 if ( m_Writer.empty() )
737 return m_Writer->WriteAncillaryResource(FrameBuf, Ctx, HMAC);
740 // Closes the MXF file, writing the index and other closing information.
742 AS_02::TimedText::MXFWriter::Finalize()
744 if ( m_Writer.empty() )
747 return m_Writer->Finalize();
753 // end AS_02_timedText.cpp