2 Copyright (c) 2011-2013, Robert Scheler, Heiko Sparenberg Fraunhofer IIS, 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_02_JP2K.cpp
29 \brief AS-02 library, JPEG 2000 essence reader and writer implementation
32 #include "AS_02_internal.h"
37 using namespace ASDCP;
38 using namespace ASDCP::JP2K;
39 using Kumu::GenRandomValue;
41 //------------------------------------------------------------------------------------------
43 static std::string JP2K_PACKAGE_LABEL = "File Package: SMPTE ST 422 / ST 2067-5 frame wrapping of JPEG 2000 codestreams";
44 static std::string PICT_DEF_LABEL = "Image Track";
46 //------------------------------------------------------------------------------------------
48 // hidden, internal implementation of JPEG 2000 reader
51 class AS_02::JP2K::MXFReader::h__Reader : public AS_02::h__AS02Reader
53 RGBAEssenceDescriptor* m_RGBAEssenceDescriptor;
54 CDCIEssenceDescriptor* m_CDCIEssenceDescriptor;
55 JPEG2000PictureSubDescriptor* m_EssenceSubDescriptor;
56 ASDCP::Rational m_EditRate;
57 ASDCP::Rational m_SampleRate;
58 EssenceType_t m_Format;
60 ASDCP_NO_COPY_CONSTRUCT(h__Reader);
63 PictureDescriptor m_PDesc; // codestream parameter list
65 h__Reader(const Dictionary& d) :
66 AS_02::h__AS02Reader(d), m_RGBAEssenceDescriptor(0), m_CDCIEssenceDescriptor(0),
67 m_EssenceSubDescriptor(0), m_Format(ESS_UNKNOWN) {}
69 virtual ~h__Reader() {}
71 Result_t OpenRead(const char*);
72 Result_t ReadFrame(ui32_t, ASDCP::JP2K::FrameBuffer&, AESDecContext*, HMACContext*);
77 AS_02::JP2K::MXFReader::h__Reader::OpenRead(const char* filename)
79 Result_t result = OpenMXFRead(filename);
81 if( ASDCP_SUCCESS(result) )
83 InterchangeObject* tmp_iobj = 0;
85 m_HeaderPart.GetMDObjectByType(OBJ_TYPE_ARGS(CDCIEssenceDescriptor), &tmp_iobj);
86 m_CDCIEssenceDescriptor = static_cast<CDCIEssenceDescriptor*>(tmp_iobj);
88 if ( m_CDCIEssenceDescriptor == 0 )
90 m_HeaderPart.GetMDObjectByType(OBJ_TYPE_ARGS(RGBAEssenceDescriptor), &tmp_iobj);
91 m_RGBAEssenceDescriptor = static_cast<RGBAEssenceDescriptor*>(tmp_iobj);
94 if ( m_CDCIEssenceDescriptor == 0 && m_RGBAEssenceDescriptor == 0 )
96 DefaultLogSink().Error("RGBAEssenceDescriptor nor CDCIEssenceDescriptor found.\n");
100 m_HeaderPart.GetMDObjectByType(OBJ_TYPE_ARGS(JPEG2000PictureSubDescriptor), &tmp_iobj);
101 m_EssenceSubDescriptor = static_cast<JPEG2000PictureSubDescriptor*>(tmp_iobj);
103 if ( m_EssenceSubDescriptor == 0 )
105 DefaultLogSink().Error("JPEG2000PictureSubDescriptor not found.\n");
106 return RESULT_FORMAT;
109 std::list<InterchangeObject*> ObjectList;
110 m_HeaderPart.GetMDObjectsByType(OBJ_TYPE_ARGS(Track), ObjectList);
112 if ( ObjectList.empty() )
114 DefaultLogSink().Error("MXF Metadata contains no Track Sets.\n");
115 return RESULT_FORMAT;
118 if ( m_CDCIEssenceDescriptor != 0 )
120 m_EditRate = ((Track*)ObjectList.front())->EditRate;
121 m_SampleRate = m_CDCIEssenceDescriptor->SampleRate;
122 result = MD_to_JP2K_PDesc(*m_CDCIEssenceDescriptor, *m_EssenceSubDescriptor, m_EditRate, m_SampleRate, m_PDesc);
124 else if ( m_RGBAEssenceDescriptor != 0 )
126 m_EditRate = ((Track*)ObjectList.front())->EditRate;
127 m_SampleRate = m_RGBAEssenceDescriptor->SampleRate;
128 result = MD_to_JP2K_PDesc(*m_RGBAEssenceDescriptor, *m_EssenceSubDescriptor, m_EditRate, m_SampleRate, m_PDesc);
131 if ( m_PDesc.ContainerDuration == 0 )
133 m_PDesc.ContainerDuration = m_IndexAccess.GetDuration();
143 AS_02::JP2K::MXFReader::h__Reader::ReadFrame(ui32_t FrameNum, ASDCP::JP2K::FrameBuffer& FrameBuf,
144 ASDCP::AESDecContext* Ctx, ASDCP::HMACContext* HMAC)
146 if ( ! m_File.IsOpen() )
150 return ReadEKLVFrame(FrameNum, FrameBuf, m_Dict->ul(MDD_JPEG2000Essence), Ctx, HMAC);
153 //------------------------------------------------------------------------------------------
156 AS_02::JP2K::MXFReader::MXFReader()
158 m_Reader = new h__Reader(DefaultCompositeDict());
162 AS_02::JP2K::MXFReader::~MXFReader()
166 // Warning: direct manipulation of MXF structures can interfere
167 // with the normal operation of the wrapper. Caveat emptor!
169 ASDCP::MXF::OP1aHeader&
170 AS_02::JP2K::MXFReader::OP1aHeader()
172 if ( m_Reader.empty() )
174 assert(g_OP1aHeader);
175 return *g_OP1aHeader;
178 return m_Reader->m_HeaderPart;
181 // Warning: direct manipulation of MXF structures can interfere
182 // with the normal operation of the wrapper. Caveat emptor!
184 AS_02::MXF::AS02IndexReader&
185 AS_02::JP2K::MXFReader::AS02IndexReader()
187 if ( m_Reader.empty() )
189 assert(g_AS02IndexReader);
190 return *g_AS02IndexReader;
193 return m_Reader->m_IndexAccess;
196 // Warning: direct manipulation of MXF structures can interfere
197 // with the normal operation of the wrapper. Caveat emptor!
200 AS_02::JP2K::MXFReader::RIP()
202 if ( m_Reader.empty() )
208 return m_Reader->m_RIP;
211 // Open the file for reading. The file must exist. Returns error if the
212 // operation cannot be completed.
214 AS_02::JP2K::MXFReader::OpenRead(const char* filename) const
216 return m_Reader->OpenRead(filename);
221 AS_02::JP2K::MXFReader::ReadFrame(ui32_t FrameNum, ASDCP::JP2K::FrameBuffer& FrameBuf,
222 ASDCP::AESDecContext* Ctx, ASDCP::HMACContext* HMAC) const
224 if ( m_Reader && m_Reader->m_File.IsOpen() )
225 return m_Reader->ReadFrame(FrameNum, FrameBuf, Ctx, HMAC);
231 // Fill the struct with the values from the file's header.
232 // Returns RESULT_INIT if the file is not open.
234 AS_02::JP2K::MXFReader::FillPictureDescriptor(PictureDescriptor& PDesc) const
236 if ( m_Reader && m_Reader->m_File.IsOpen() )
238 PDesc = m_Reader->m_PDesc;
246 // Fill the struct with the values from the file's header.
247 // Returns RESULT_INIT if the file is not open.
249 AS_02::JP2K::MXFReader::FillWriterInfo(WriterInfo& Info) const
251 if ( m_Reader && m_Reader->m_File.IsOpen() )
253 Info = m_Reader->m_Info;
261 //------------------------------------------------------------------------------------------
264 class AS_02::JP2K::MXFWriter::h__Writer : public AS_02::h__AS02Writer
266 ASDCP_NO_COPY_CONSTRUCT(h__Writer);
269 JPEG2000PictureSubDescriptor* m_EssenceSubDescriptor;
272 PictureDescriptor m_PDesc;
273 byte_t m_EssenceUL[SMPTE_UL_LENGTH];
275 h__Writer(const Dictionary& d) : h__AS02Writer(d), m_EssenceSubDescriptor(0) {
276 memset(m_EssenceUL, 0, SMPTE_UL_LENGTH);
279 virtual ~h__Writer(){}
281 Result_t OpenWrite(const char*, EssenceType_t type, const AS_02::IndexStrategy_t& IndexStrategy,
282 const ui32_t& PartitionSpace, const ui32_t& HeaderSize);
283 Result_t SetSourceStream(const PictureDescriptor&, const std::string& label,
284 ASDCP::Rational LocalEditRate = ASDCP::Rational(0,0));
285 Result_t WriteFrame(const ASDCP::JP2K::FrameBuffer&, ASDCP::AESEncContext*, ASDCP::HMACContext*);
290 // Open the file for writing. The file must not exist. Returns error if
291 // the operation cannot be completed.
293 AS_02::JP2K::MXFWriter::h__Writer::OpenWrite(const char* filename, EssenceType_t type, const AS_02::IndexStrategy_t& IndexStrategy,
294 const ui32_t& PartitionSpace_sec, const ui32_t& HeaderSize)
296 if ( ! m_State.Test_BEGIN() )
299 if ( m_IndexStrategy != AS_02::IS_FOLLOW )
301 DefaultLogSink().Error("Only strategy IS_FOLLOW is supported at this time.\n");
302 return Kumu::RESULT_NOTIMPL;
305 Result_t result = m_File.OpenWrite(filename);
307 if ( ASDCP_SUCCESS(result) )
309 m_IndexStrategy = IndexStrategy;
310 m_PartitionSpace = PartitionSpace_sec; // later converted to edit units by SetSourceStream()
311 m_HeaderSize = HeaderSize;
313 m_EssenceDescriptor = new RGBAEssenceDescriptor(m_Dict);
314 m_EssenceSubDescriptor = new JPEG2000PictureSubDescriptor(m_Dict);
315 m_EssenceSubDescriptorList.push_back((InterchangeObject*)m_EssenceSubDescriptor);
317 GenRandomValue(m_EssenceSubDescriptor->InstanceUID);
318 m_EssenceDescriptor->SubDescriptors.push_back(m_EssenceSubDescriptor->InstanceUID);
319 result = m_State.Goto_INIT();
325 // Automatically sets the MXF file's metadata from the first jpeg codestream stream.
327 AS_02::JP2K::MXFWriter::h__Writer::SetSourceStream(const PictureDescriptor& PDesc, const std::string& label, ASDCP::Rational LocalEditRate)
330 if ( ! m_State.Test_INIT() )
333 if ( LocalEditRate == ASDCP::Rational(0,0) )
334 LocalEditRate = PDesc.EditRate;
338 Result_t result = JP2K_PDesc_to_MD(m_PDesc, *m_Dict,
339 static_cast<ASDCP::MXF::RGBAEssenceDescriptor*>(m_EssenceDescriptor),
340 m_EssenceSubDescriptor);
342 static_cast<ASDCP::MXF::RGBAEssenceDescriptor*>(m_EssenceDescriptor)->ComponentMaxRef = 4095; /// TODO: set with magic or some such thing
343 static_cast<ASDCP::MXF::RGBAEssenceDescriptor*>(m_EssenceDescriptor)->ComponentMinRef = 0;
345 if ( ASDCP_SUCCESS(result) )
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 = m_State.Goto_READY();
352 if ( ASDCP_SUCCESS(result) )
354 ui32_t TCFrameRate = ( m_PDesc.EditRate == EditRate_23_98 ) ? 24 : m_PDesc.EditRate.Numerator;
356 result = WriteAS02Header(label, UL(m_Dict->ul(MDD_JPEG_2000Wrapping)),
357 PICT_DEF_LABEL, UL(m_EssenceUL), UL(m_Dict->ul(MDD_PictureDataDef)),
358 LocalEditRate, TCFrameRate);
364 // Writes a frame of essence to the MXF file. If the optional AESEncContext
365 // argument is present, the essence is encrypted prior to writing.
366 // Fails if the file is not open, is finalized, or an operating system
370 AS_02::JP2K::MXFWriter::h__Writer::WriteFrame(const ASDCP::JP2K::FrameBuffer& FrameBuf,
371 AESEncContext* Ctx, HMACContext* HMAC)
373 Result_t result = RESULT_OK;
375 if ( m_State.Test_READY() )
376 result = m_State.Goto_RUNNING(); // first time through
378 ui64_t StreamOffset = m_StreamOffset; // m_StreamOffset will be changed by the call to WriteEKLVPacket
380 if ( ASDCP_SUCCESS(result) )
381 result = WriteEKLVPacket(FrameBuf, m_EssenceUL, Ctx, HMAC);
383 if ( ASDCP_SUCCESS(result) )
385 IndexTableSegment::IndexEntry Entry;
386 Entry.StreamOffset = StreamOffset;
387 m_IndexWriter.PushIndexEntry(Entry);
394 // Closes the MXF file, writing the index and other closing information.
397 AS_02::JP2K::MXFWriter::h__Writer::Finalize()
399 if ( ! m_State.Test_RUNNING() )
402 Result_t result = m_State.Goto_FINAL();
404 if ( ASDCP_SUCCESS(result) )
405 result = WriteAS02Footer();
411 //------------------------------------------------------------------------------------------
415 AS_02::JP2K::MXFWriter::MXFWriter()
419 AS_02::JP2K::MXFWriter::~MXFWriter()
423 // Warning: direct manipulation of MXF structures can interfere
424 // with the normal operation of the wrapper. Caveat emptor!
426 ASDCP::MXF::OP1aHeader&
427 AS_02::JP2K::MXFWriter::OP1aHeader()
429 if ( m_Writer.empty() )
431 assert(g_OP1aHeader);
432 return *g_OP1aHeader;
435 return m_Writer->m_HeaderPart;
438 // Warning: direct manipulation of MXF structures can interfere
439 // with the normal operation of the wrapper. Caveat emptor!
442 AS_02::JP2K::MXFWriter::RIP()
444 if ( m_Writer.empty() )
450 return m_Writer->m_RIP;
453 // Open the file for writing. The file must not exist. Returns error if
454 // the operation cannot be completed.
456 AS_02::JP2K::MXFWriter::OpenWrite(const char* filename, const ASDCP::WriterInfo& Info,
457 const ASDCP::JP2K::PictureDescriptor& PDesc,
458 const IndexStrategy_t& Strategy,
459 const ui32_t& PartitionSpace,
460 const ui32_t& HeaderSize)
462 m_Writer = new AS_02::JP2K::MXFWriter::h__Writer(DefaultSMPTEDict());
463 m_Writer->m_Info = Info;
465 Result_t result = m_Writer->OpenWrite(filename, ASDCP::ESS_JPEG_2000, Strategy, PartitionSpace, HeaderSize);
467 if ( ASDCP_SUCCESS(result) )
468 result = m_Writer->SetSourceStream(PDesc, JP2K_PACKAGE_LABEL);
470 if ( ASDCP_FAILURE(result) )
476 // Writes a frame of essence to the MXF file. If the optional AESEncContext
477 // argument is present, the essence is encrypted prior to writing.
478 // Fails if the file is not open, is finalized, or an operating system
481 AS_02::JP2K::MXFWriter::WriteFrame(const ASDCP::JP2K::FrameBuffer& FrameBuf, AESEncContext* Ctx, HMACContext* HMAC)
483 if ( m_Writer.empty() )
486 return m_Writer->WriteFrame(FrameBuf, Ctx, HMAC);
489 // Closes the MXF file, writing the index and other closing information.
491 AS_02::JP2K::MXFWriter::Finalize()
493 if ( m_Writer.empty() )
496 return m_Writer->Finalize();
501 // end AS_02_JP2K.cpp