2 Copyright (c) 2018, John Hurst
6 Redistribution and use in source and binary forms, with or without
7 modification, are permitted provided that the following conditions
9 1. Redistributions of source code must retain the above copyright
10 notice, this list of conditions and the following disclaimer.
11 2. Redistributions in binary form must reproduce the above copyright
12 notice, this list of conditions and the following disclaimer in the
13 documentation and/or other materials provided with the distribution.
14 3. The name of the author may not be used to endorse or promote products
15 derived from this software without specific prior written permission.
17 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 /*! \file AS_02_ISXD.cpp
30 \brief AS-02 library, ISXD (RDD 47) essence reader and writer implementation
33 #include "AS_02_internal.h"
39 using namespace ASDCP;
40 using Kumu::GenRandomValue;
42 //------------------------------------------------------------------------------------------
44 static std::string AUXDATA_PACKAGE_LABEL = "File Package: RDD 47 frame wrapping of ISXD data";
45 static std::string PICT_DEF_LABEL = "Isochronous Stream of XML Documents Track";
47 //------------------------------------------------------------------------------------------
49 // hidden, internal implementation of Aux Data reader
52 class AS_02::ISXD::MXFReader::h__Reader : public AS_02::h__AS02Reader
54 ASDCP_NO_COPY_CONSTRUCT(h__Reader);
57 h__Reader(const Dictionary& d) :
58 AS_02::h__AS02Reader(d) {}
60 virtual ~h__Reader() {}
62 Result_t OpenRead(const std::string& filename);
63 Result_t ReadFrame(ui32_t, ASDCP::FrameBuffer&, AESDecContext*, HMACContext*);
68 AS_02::ISXD::MXFReader::h__Reader::OpenRead(const std::string& filename)
70 Result_t result = OpenMXFRead(filename);
72 if( KM_SUCCESS(result) )
74 InterchangeObject* tmp_iobj = 0;
76 m_HeaderPart.GetMDObjectByType(OBJ_TYPE_ARGS(ISXDDataEssenceDescriptor), &tmp_iobj);
80 DefaultLogSink().Error("ISXDDataEssenceDescriptor not found.\n");
83 m_HeaderPart.GetMDObjectByType(OBJ_TYPE_ARGS(ISXDDataEssenceDescriptor), &tmp_iobj);
85 std::list<InterchangeObject*> ObjectList;
86 m_HeaderPart.GetMDObjectsByType(OBJ_TYPE_ARGS(Track), ObjectList);
88 if ( ObjectList.empty() )
90 DefaultLogSink().Error("MXF Metadata contains no Track Sets.\n");
91 return RESULT_AS02_FORMAT;
101 AS_02::ISXD::MXFReader::h__Reader::ReadFrame(ui32_t FrameNum, ASDCP::FrameBuffer& FrameBuf,
102 ASDCP::AESDecContext* Ctx, ASDCP::HMACContext* HMAC)
104 if ( ! m_File.IsOpen() )
108 return ReadEKLVFrame(FrameNum, FrameBuf, m_Dict->ul(MDD_FrameWrappedISXDData), Ctx, HMAC);
113 //------------------------------------------------------------------------------------------
116 AS_02::ISXD::MXFReader::MXFReader()
118 m_Reader = new h__Reader(DefaultCompositeDict());
122 AS_02::ISXD::MXFReader::~MXFReader()
126 // Warning: direct manipulation of MXF structures can interfere
127 // with the normal operation of the wrapper. Caveat emptor!
129 ASDCP::MXF::OP1aHeader&
130 AS_02::ISXD::MXFReader::OP1aHeader()
132 if ( m_Reader.empty() )
134 assert(g_OP1aHeader);
135 return *g_OP1aHeader;
138 return m_Reader->m_HeaderPart;
141 // Warning: direct manipulation of MXF structures can interfere
142 // with the normal operation of the wrapper. Caveat emptor!
144 AS_02::MXF::AS02IndexReader&
145 AS_02::ISXD::MXFReader::AS02IndexReader()
147 if ( m_Reader.empty() )
149 assert(g_AS02IndexReader);
150 return *g_AS02IndexReader;
153 return m_Reader->m_IndexAccess;
156 // Warning: direct manipulation of MXF structures can interfere
157 // with the normal operation of the wrapper. Caveat emptor!
160 AS_02::ISXD::MXFReader::RIP()
162 if ( m_Reader.empty() )
168 return m_Reader->m_RIP;
171 // Open the file for reading. The file must exist. Returns error if the
172 // operation cannot be completed.
175 AS_02::ISXD::MXFReader::OpenRead(const std::string& filename) const
177 return m_Reader->OpenRead(filename);
182 AS_02::ISXD::MXFReader::Close() const
184 if ( m_Reader && m_Reader->m_File.IsOpen() )
195 AS_02::ISXD::MXFReader::ReadFrame(ui32_t FrameNum, ASDCP::FrameBuffer& FrameBuf,
196 ASDCP::AESDecContext* Ctx, ASDCP::HMACContext* HMAC) const
198 if ( m_Reader && m_Reader->m_File.IsOpen() )
200 return m_Reader->ReadFrame(FrameNum, FrameBuf, Ctx, HMAC);
208 AS_02::ISXD::MXFReader::ReadGenericStreamPartitionPayload(const ui32_t SID, ASDCP::FrameBuffer& frame_buf)
210 if ( m_Reader && m_Reader->m_File.IsOpen() )
212 return m_Reader->ReadGenericStreamPartitionPayload(SID, frame_buf, 0, 0 /*no encryption*/);
218 // Fill the struct with the values from the file's header.
219 // Returns RESULT_INIT if the file is not open.
221 AS_02::ISXD::MXFReader::FillWriterInfo(WriterInfo& Info) const
223 if ( m_Reader && m_Reader->m_File.IsOpen() )
225 Info = m_Reader->m_Info;
234 AS_02::ISXD::MXFReader::DumpHeaderMetadata(FILE* stream) const
236 if ( m_Reader && m_Reader->m_File.IsOpen() )
238 m_Reader->m_HeaderPart.Dump(stream);
245 AS_02::ISXD::MXFReader::DumpIndex(FILE* stream) const
247 if ( m_Reader && m_Reader->m_File.IsOpen() )
249 m_Reader->m_IndexAccess.Dump(stream);
253 //------------------------------------------------------------------------------------------
256 class AS_02::ISXD::MXFWriter::h__Writer : public AS_02::h__AS02WriterFrame
258 ASDCP_NO_COPY_CONSTRUCT(h__Writer);
262 byte_t m_EssenceUL[SMPTE_UL_LENGTH];
263 ISXDDataEssenceDescriptor *m_DataEssenceDescriptor;
265 h__Writer(const Dictionary& d) : h__AS02WriterFrame(d) {
266 memset(m_EssenceUL, 0, SMPTE_UL_LENGTH);
269 virtual ~h__Writer(){}
271 Result_t OpenWrite(const std::string& filename, const ASDCP::WriterInfo& Info,
272 const std::string& isxd_document_namespace,
273 const ASDCP::Rational& edit_rate,
274 const AS_02::IndexStrategy_t& IndexStrategy,
275 const ui32_t& PartitionSpace, const ui32_t& HeaderSize);
276 Result_t SetSourceStream(const std::string& label, const ASDCP::Rational& edit_rate);
277 Result_t WriteFrame(const ASDCP::FrameBuffer&, ASDCP::AESEncContext*, ASDCP::HMACContext*);
282 // Open the file for writing. The file must not exist. Returns error if
283 // the operation cannot be completed.
285 AS_02::ISXD::MXFWriter::h__Writer::OpenWrite(const std::string& filename, const ASDCP::WriterInfo& Info,
286 const std::string& isxd_document_namespace,
287 const ASDCP::Rational& edit_rate,
288 const AS_02::IndexStrategy_t& IndexStrategy,
289 const ui32_t& PartitionSpace_sec, const ui32_t& HeaderSize)
291 m_DataEssenceDescriptor = new ISXDDataEssenceDescriptor(m_Dict);
292 m_DataEssenceDescriptor->DataEssenceCoding = m_Dict->ul(MDD_FrameWrappedISXDContainer);
293 m_DataEssenceDescriptor->SampleRate = edit_rate;
294 m_DataEssenceDescriptor->NamespaceURI = isxd_document_namespace;
296 if ( ! m_State.Test_BEGIN() )
298 KM_RESULT_STATE_HERE();
302 if ( m_IndexStrategy != AS_02::IS_FOLLOW )
304 DefaultLogSink().Error("Only strategy IS_FOLLOW is supported at this time.\n");
305 return Kumu::RESULT_NOTIMPL;
308 Result_t result = m_File.OpenWrite(filename);
310 if ( KM_SUCCESS(result) )
312 m_IndexStrategy = IndexStrategy;
313 m_PartitionSpace = PartitionSpace_sec; // later converted to edit units by SetSourceStream()
314 m_HeaderSize = HeaderSize;
315 m_EssenceDescriptor = m_DataEssenceDescriptor;
316 result = m_State.Goto_INIT();
322 // Automatically sets the MXF file's metadata from the first jpeg codestream stream.
324 AS_02::ISXD::MXFWriter::h__Writer::SetSourceStream(const std::string& label, const ASDCP::Rational& edit_rate)
327 if ( ! m_State.Test_INIT() )
329 KM_RESULT_STATE_HERE();
333 memcpy(m_EssenceUL, m_Dict->ul(MDD_FrameWrappedISXDContainer), SMPTE_UL_LENGTH);
334 m_EssenceUL[SMPTE_UL_LENGTH-1] = 1; // first (and only) essence container
335 Result_t result = m_State.Goto_READY();
337 if ( KM_SUCCESS(result) )
339 result = WriteAS02Header(label, UL(m_Dict->ul(MDD_FrameWrappedISXDData)),
340 PICT_DEF_LABEL, UL(m_EssenceUL), UL(m_Dict->ul(MDD_DataDataDef)),
341 edit_rate, derive_timecode_rate_from_edit_rate(edit_rate));
343 if ( KM_SUCCESS(result) )
345 this->m_IndexWriter.SetPrimerLookup(&this->m_HeaderPart.m_Primer);
352 // Writes a frame of essence to the MXF file. If the optional AESEncContext
353 // argument is present, the essence is encrypted prior to writing.
354 // Fails if the file is not open, is finalized, or an operating system
358 AS_02::ISXD::MXFWriter::h__Writer::WriteFrame(const ASDCP::FrameBuffer& FrameBuf,
359 AESEncContext* Ctx, HMACContext* HMAC)
361 if ( FrameBuf.Size() == 0 )
363 DefaultLogSink().Error("The frame buffer size is zero.\n");
367 Result_t result = RESULT_OK;
369 if ( m_State.Test_READY() )
371 result = m_State.Goto_RUNNING(); // first time through
374 if ( KM_SUCCESS(result) )
376 result = WriteEKLVPacket(FrameBuf, m_EssenceUL, Ctx, HMAC);
383 // Closes the MXF file, writing the index and other closing information.
386 AS_02::ISXD::MXFWriter::h__Writer::Finalize()
388 if ( ! m_State.Test_RUNNING() )
390 KM_RESULT_STATE_HERE();
394 Result_t result = m_State.Goto_FINAL();
396 if ( KM_SUCCESS(result) )
398 result = WriteAS02Footer();
404 //------------------------------------------------------------------------------------------
408 AS_02::ISXD::MXFWriter::MXFWriter()
412 AS_02::ISXD::MXFWriter::~MXFWriter()
416 // Warning: direct manipulation of MXF structures can interfere
417 // with the normal operation of the wrapper. Caveat emptor!
419 ASDCP::MXF::OP1aHeader&
420 AS_02::ISXD::MXFWriter::OP1aHeader()
422 if ( m_Writer.empty() )
424 assert(g_OP1aHeader);
425 return *g_OP1aHeader;
428 return m_Writer->m_HeaderPart;
431 // Warning: direct manipulation of MXF structures can interfere
432 // with the normal operation of the wrapper. Caveat emptor!
435 AS_02::ISXD::MXFWriter::RIP()
437 if ( m_Writer.empty() )
443 return m_Writer->m_RIP;
446 // Open the file for writing. The file must not exist. Returns error if
447 // the operation cannot be completed.
449 AS_02::ISXD::MXFWriter::OpenWrite(const std::string& filename, const ASDCP::WriterInfo& Info,
450 const std::string& isxd_document_namespace,
451 const ASDCP::Rational& edit_rate, const ui32_t& header_size,
452 const IndexStrategy_t& strategy, const ui32_t& partition_space)
454 m_Writer = new AS_02::ISXD::MXFWriter::h__Writer(DefaultSMPTEDict());
455 m_Writer->m_Info = Info;
457 Result_t result = m_Writer->OpenWrite(filename, Info, isxd_document_namespace, edit_rate,
458 strategy, partition_space, header_size);
460 if ( KM_SUCCESS(result) )
461 result = m_Writer->SetSourceStream(AUXDATA_PACKAGE_LABEL, edit_rate);
463 if ( KM_FAILURE(result) )
469 // Writes a frame of essence to the MXF file. If the optional AESEncContext
470 // argument is present, the essence is encrypted prior to writing.
471 // Fails if the file is not open, is finalized, or an operating system
474 AS_02::ISXD::MXFWriter::WriteFrame(const ASDCP::FrameBuffer& FrameBuf, AESEncContext* Ctx, HMACContext* HMAC)
476 if ( m_Writer.empty() )
479 return m_Writer->WriteFrame(FrameBuf, Ctx, HMAC);
483 AS_02::ISXD::MXFWriter::AddDmsGenericPartUtf8Text(const ASDCP::FrameBuffer& FrameBuf, ASDCP::AESEncContext* Ctx,
484 ASDCP::HMACContext* HMAC)
486 if ( m_Writer.empty() )
489 m_Writer->FlushIndexPartition();
490 return m_Writer->AddDmsGenericPartUtf8Text(FrameBuf, Ctx, HMAC);
494 // Closes the MXF file, writing the index and other closing information.
496 AS_02::ISXD::MXFWriter::Finalize()
498 if ( m_Writer.empty() )
501 return m_Writer->Finalize();
506 // end AS_02_ISXD.cpp