2 Copyright (c) 2011-2018, Robert Scheler, Heiko Sparenberg Fraunhofer IIS,
7 Redistribution and use in source and binary forms, with or without
8 modification, are permitted provided that the following conditions
10 1. Redistributions of source code must retain the above copyright
11 notice, this list of conditions and the following disclaimer.
12 2. Redistributions in binary form must reproduce the above copyright
13 notice, this list of conditions and the following disclaimer in the
14 documentation and/or other materials provided with the distribution.
15 3. The name of the author may not be used to endorse or promote products
16 derived from this software without specific prior written permission.
18 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20 OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21 IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23 NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27 THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 /*! \file AS_02_JP2K.cpp
31 \brief AS-02 library, JPEG 2000 essence reader and writer implementation
34 #include "AS_02_internal.h"
39 using namespace ASDCP;
40 using namespace ASDCP::JP2K;
41 using Kumu::GenRandomValue;
43 //------------------------------------------------------------------------------------------
45 static std::string JP2K_PACKAGE_LABEL = "File Package: SMPTE ST 422 / ST 2067-5 frame wrapping of JPEG 2000 codestreams";
46 static std::string PICT_DEF_LABEL = "Image Track";
48 //------------------------------------------------------------------------------------------
50 // hidden, internal implementation of JPEG 2000 reader
53 class AS_02::JP2K::MXFReader::h__Reader : public AS_02::h__AS02Reader
55 ASDCP_NO_COPY_CONSTRUCT(h__Reader);
58 h__Reader(const Dictionary& d) :
59 AS_02::h__AS02Reader(d) {}
61 virtual ~h__Reader() {}
63 Result_t OpenRead(const std::string&);
64 Result_t ReadFrame(ui32_t, ASDCP::JP2K::FrameBuffer&, AESDecContext*, HMACContext*);
69 AS_02::JP2K::MXFReader::h__Reader::OpenRead(const std::string& filename)
71 Result_t result = OpenMXFRead(filename);
73 if( KM_SUCCESS(result) )
75 InterchangeObject* tmp_iobj = 0;
77 m_HeaderPart.GetMDObjectByType(OBJ_TYPE_ARGS(CDCIEssenceDescriptor), &tmp_iobj);
81 m_HeaderPart.GetMDObjectByType(OBJ_TYPE_ARGS(RGBAEssenceDescriptor), &tmp_iobj);
86 DefaultLogSink().Error("RGBAEssenceDescriptor nor CDCIEssenceDescriptor found.\n");
89 m_HeaderPart.GetMDObjectByType(OBJ_TYPE_ARGS(JPEG2000PictureSubDescriptor), &tmp_iobj);
93 DefaultLogSink().Error("JPEG2000PictureSubDescriptor not found.\n");
96 std::list<InterchangeObject*> ObjectList;
97 m_HeaderPart.GetMDObjectsByType(OBJ_TYPE_ARGS(Track), ObjectList);
99 if ( ObjectList.empty() )
101 DefaultLogSink().Error("MXF Metadata contains no Track Sets.\n");
102 return RESULT_AS02_FORMAT;
112 AS_02::JP2K::MXFReader::h__Reader::ReadFrame(ui32_t FrameNum, ASDCP::JP2K::FrameBuffer& FrameBuf,
113 ASDCP::AESDecContext* Ctx, ASDCP::HMACContext* HMAC)
115 if ( ! m_File.IsOpen() )
119 return ReadEKLVFrame(FrameNum, FrameBuf, m_Dict->ul(MDD_JPEG2000Essence), Ctx, HMAC);
122 //------------------------------------------------------------------------------------------
125 AS_02::JP2K::MXFReader::MXFReader()
127 m_Reader = new h__Reader(DefaultCompositeDict());
131 AS_02::JP2K::MXFReader::~MXFReader()
135 // Warning: direct manipulation of MXF structures can interfere
136 // with the normal operation of the wrapper. Caveat emptor!
138 ASDCP::MXF::OP1aHeader&
139 AS_02::JP2K::MXFReader::OP1aHeader()
141 if ( m_Reader.empty() )
143 assert(g_OP1aHeader);
144 return *g_OP1aHeader;
147 return m_Reader->m_HeaderPart;
150 // Warning: direct manipulation of MXF structures can interfere
151 // with the normal operation of the wrapper. Caveat emptor!
153 AS_02::MXF::AS02IndexReader&
154 AS_02::JP2K::MXFReader::AS02IndexReader()
156 if ( m_Reader.empty() )
158 assert(g_AS02IndexReader);
159 return *g_AS02IndexReader;
162 return m_Reader->m_IndexAccess;
165 // Warning: direct manipulation of MXF structures can interfere
166 // with the normal operation of the wrapper. Caveat emptor!
169 AS_02::JP2K::MXFReader::RIP()
171 if ( m_Reader.empty() )
177 return m_Reader->m_RIP;
180 // Open the file for reading. The file must exist. Returns error if the
181 // operation cannot be completed.
183 AS_02::JP2K::MXFReader::OpenRead(const std::string& filename) const
185 return m_Reader->OpenRead(filename);
190 AS_02::JP2K::MXFReader::Close() const
192 if ( m_Reader && m_Reader->m_File.IsOpen() )
203 AS_02::JP2K::MXFReader::ReadFrame(ui32_t FrameNum, ASDCP::JP2K::FrameBuffer& FrameBuf,
204 ASDCP::AESDecContext* Ctx, ASDCP::HMACContext* HMAC) const
206 if ( m_Reader && m_Reader->m_File.IsOpen() )
207 return m_Reader->ReadFrame(FrameNum, FrameBuf, Ctx, HMAC);
212 // Fill the struct with the values from the file's header.
213 // Returns RESULT_INIT if the file is not open.
215 AS_02::JP2K::MXFReader::FillWriterInfo(WriterInfo& Info) const
217 if ( m_Reader && m_Reader->m_File.IsOpen() )
219 Info = m_Reader->m_Info;
228 AS_02::JP2K::MXFReader::DumpHeaderMetadata(FILE* stream) const
230 if ( m_Reader && m_Reader->m_File.IsOpen() )
232 m_Reader->m_HeaderPart.Dump(stream);
239 AS_02::JP2K::MXFReader::DumpIndex(FILE* stream) const
241 if ( m_Reader && m_Reader->m_File.IsOpen() )
243 m_Reader->m_IndexAccess.Dump(stream);
247 //------------------------------------------------------------------------------------------
250 class AS_02::JP2K::MXFWriter::h__Writer : public AS_02::h__AS02WriterFrame
252 ASDCP_NO_COPY_CONSTRUCT(h__Writer);
255 JPEG2000PictureSubDescriptor* m_EssenceSubDescriptor;
258 byte_t m_EssenceUL[SMPTE_UL_LENGTH];
260 h__Writer(const Dictionary& d) : h__AS02WriterFrame(d), m_EssenceSubDescriptor(0) {
261 memset(m_EssenceUL, 0, SMPTE_UL_LENGTH);
264 virtual ~h__Writer(){}
266 Result_t OpenWrite(const std::string&, ASDCP::MXF::FileDescriptor* essence_descriptor,
267 ASDCP::MXF::InterchangeObject_list_t& essence_sub_descriptor_list,
268 const AS_02::IndexStrategy_t& IndexStrategy,
269 const ui32_t& PartitionSpace, const ui32_t& HeaderSize);
270 Result_t SetSourceStream(const std::string& label, const ASDCP::Rational& edit_rate);
271 Result_t WriteFrame(const ASDCP::JP2K::FrameBuffer&, ASDCP::AESEncContext*, ASDCP::HMACContext*);
276 // Open the file for writing. The file must not exist. Returns error if
277 // the operation cannot be completed.
279 AS_02::JP2K::MXFWriter::h__Writer::OpenWrite(const std::string& filename,
280 ASDCP::MXF::FileDescriptor* essence_descriptor,
281 ASDCP::MXF::InterchangeObject_list_t& essence_sub_descriptor_list,
282 const AS_02::IndexStrategy_t& IndexStrategy,
283 const ui32_t& PartitionSpace_sec, const ui32_t& HeaderSize)
285 if ( ! m_State.Test_BEGIN() )
287 KM_RESULT_STATE_HERE();
291 if ( m_IndexStrategy != AS_02::IS_FOLLOW )
293 DefaultLogSink().Error("Only strategy IS_FOLLOW is supported at this time.\n");
294 return Kumu::RESULT_NOTIMPL;
297 Result_t result = m_File.OpenWrite(filename.c_str());
299 if ( KM_SUCCESS(result) )
301 m_IndexStrategy = IndexStrategy;
302 m_PartitionSpace = PartitionSpace_sec; // later converted to edit units by SetSourceStream()
303 m_HeaderSize = HeaderSize;
305 if ( essence_descriptor->GetUL() != UL(m_Dict->ul(MDD_RGBAEssenceDescriptor))
306 && essence_descriptor->GetUL() != UL(m_Dict->ul(MDD_CDCIEssenceDescriptor)) )
308 DefaultLogSink().Error("Essence descriptor is not a RGBAEssenceDescriptor or CDCIEssenceDescriptor.\n");
309 essence_descriptor->Dump();
310 return RESULT_AS02_FORMAT;
313 m_EssenceDescriptor = essence_descriptor;
315 ASDCP::MXF::InterchangeObject_list_t::iterator i;
316 for ( i = essence_sub_descriptor_list.begin(); i != essence_sub_descriptor_list.end(); ++i )
318 if ( (*i)->GetUL() != UL(m_Dict->ul(MDD_JPEG2000PictureSubDescriptor)) )
320 DefaultLogSink().Error("Essence sub-descriptor is not a JPEG2000PictureSubDescriptor.\n");
324 m_EssenceSubDescriptorList.push_back(*i);
325 GenRandomValue((*i)->InstanceUID);
326 m_EssenceDescriptor->SubDescriptors.push_back((*i)->InstanceUID);
327 *i = 0; // parent will only free the ones we don't keep
330 result = m_State.Goto_INIT();
336 // Automatically sets the MXF file's metadata from the first jpeg codestream stream.
338 AS_02::JP2K::MXFWriter::h__Writer::SetSourceStream(const std::string& label, const ASDCP::Rational& edit_rate)
341 if ( ! m_State.Test_INIT() )
343 KM_RESULT_STATE_HERE();
347 memcpy(m_EssenceUL, m_Dict->ul(MDD_JPEG2000Essence), SMPTE_UL_LENGTH);
348 m_EssenceUL[SMPTE_UL_LENGTH-1] = 1; // first (and only) essence container
349 Result_t result = m_State.Goto_READY();
351 if ( KM_SUCCESS(result) )
353 UL wrapping_label = UL(m_Dict->ul(MDD_MXFGCP1FrameWrappedPictureElement));
355 CDCIEssenceDescriptor *cdci_descriptor = dynamic_cast<CDCIEssenceDescriptor*>(m_EssenceDescriptor);
356 if ( cdci_descriptor )
358 if ( cdci_descriptor->FrameLayout ) // 0 == progressive, 1 == interlace
360 wrapping_label = UL(m_Dict->ul(MDD_MXFGCI1FrameWrappedPictureElement));
364 result = WriteAS02Header(label, wrapping_label,
365 PICT_DEF_LABEL, UL(m_EssenceUL), UL(m_Dict->ul(MDD_PictureDataDef)),
366 edit_rate, derive_timecode_rate_from_edit_rate(edit_rate));
368 if ( KM_SUCCESS(result) )
370 this->m_IndexWriter.SetPrimerLookup(&this->m_HeaderPart.m_Primer);
371 this->m_IndexWriter.SetEditRate(m_EssenceDescriptor->SampleRate);
378 // Writes a frame of essence to the MXF file. If the optional AESEncContext
379 // argument is present, the essence is encrypted prior to writing.
380 // Fails if the file is not open, is finalized, or an operating system
384 AS_02::JP2K::MXFWriter::h__Writer::WriteFrame(const ASDCP::JP2K::FrameBuffer& FrameBuf,
385 AESEncContext* Ctx, HMACContext* HMAC)
387 if ( FrameBuf.Size() == 0 )
389 DefaultLogSink().Error("The frame buffer size is zero.\n");
393 Result_t result = RESULT_OK;
395 if ( m_State.Test_READY() )
397 result = m_State.Goto_RUNNING(); // first time through
400 if ( KM_SUCCESS(result) )
402 result = WriteEKLVPacket(FrameBuf, m_EssenceUL, MXF_BER_LENGTH, Ctx, HMAC);
409 // Closes the MXF file, writing the index and other closing information.
412 AS_02::JP2K::MXFWriter::h__Writer::Finalize()
414 if ( ! m_State.Test_RUNNING() )
416 KM_RESULT_STATE_HERE();
420 Result_t result = m_State.Goto_FINAL();
422 if ( KM_SUCCESS(result) )
424 result = WriteAS02Footer();
431 //------------------------------------------------------------------------------------------
435 AS_02::JP2K::MXFWriter::MXFWriter()
439 AS_02::JP2K::MXFWriter::~MXFWriter()
443 // Warning: direct manipulation of MXF structures can interfere
444 // with the normal operation of the wrapper. Caveat emptor!
446 ASDCP::MXF::OP1aHeader&
447 AS_02::JP2K::MXFWriter::OP1aHeader()
449 if ( m_Writer.empty() )
451 assert(g_OP1aHeader);
452 return *g_OP1aHeader;
455 return m_Writer->m_HeaderPart;
458 // Warning: direct manipulation of MXF structures can interfere
459 // with the normal operation of the wrapper. Caveat emptor!
462 AS_02::JP2K::MXFWriter::RIP()
464 if ( m_Writer.empty() )
470 return m_Writer->m_RIP;
473 // Open the file for writing. The file must not exist. Returns error if
474 // the operation cannot be completed.
476 AS_02::JP2K::MXFWriter::OpenWrite(const std::string& filename, const ASDCP::WriterInfo& Info,
477 ASDCP::MXF::FileDescriptor* essence_descriptor,
478 ASDCP::MXF::InterchangeObject_list_t& essence_sub_descriptor_list,
479 const ASDCP::Rational& edit_rate, const ui32_t& header_size,
480 const IndexStrategy_t& strategy, const ui32_t& partition_space)
482 if ( essence_descriptor == 0 )
484 DefaultLogSink().Error("Essence descriptor object required.\n");
488 m_Writer = new AS_02::JP2K::MXFWriter::h__Writer(DefaultSMPTEDict());
489 m_Writer->m_Info = Info;
491 Result_t result = m_Writer->OpenWrite(filename, essence_descriptor, essence_sub_descriptor_list,
492 strategy, partition_space, header_size);
494 if ( KM_SUCCESS(result) )
495 result = m_Writer->SetSourceStream(JP2K_PACKAGE_LABEL, edit_rate);
497 if ( KM_FAILURE(result) )
503 // Writes a frame of essence to the MXF file. If the optional AESEncContext
504 // argument is present, the essence is encrypted prior to writing.
505 // Fails if the file is not open, is finalized, or an operating system
508 AS_02::JP2K::MXFWriter::WriteFrame(const ASDCP::JP2K::FrameBuffer& FrameBuf, AESEncContext* Ctx, HMACContext* HMAC)
510 if ( m_Writer.empty() )
513 return m_Writer->WriteFrame(FrameBuf, Ctx, HMAC);
516 // Closes the MXF file, writing the index and other closing information.
518 AS_02::JP2K::MXFWriter::Finalize()
520 if ( m_Writer.empty() )
523 return m_Writer->Finalize();
528 // end AS_02_JP2K.cpp