2 Copyright (c) 2004-2009, 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_JP2k.cpp
29 \brief AS-DCP library, JPEG 2000 essence reader and writer implementation
32 #include "AS_DCP_internal.h"
36 using namespace ASDCP::JP2K;
37 using Kumu::GenRandomValue;
39 //------------------------------------------------------------------------------------------
41 static std::string JP2K_PACKAGE_LABEL = "File Package: SMPTE 429-4 frame wrapping of JPEG 2000 codestreams";
42 static std::string JP2K_S_PACKAGE_LABEL = "File Package: SMPTE 429-10 frame wrapping of stereoscopic JPEG 2000 codestreams";
43 static std::string PICT_DEF_LABEL = "Picture Track";
45 int s_exp_lookup[16] = { 0, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024,2048, 4096, 8192, 16384, 32768 };
49 ASDCP::JP2K::operator << (std::ostream& strm, const PictureDescriptor& PDesc)
51 strm << " AspectRatio: " << PDesc.AspectRatio.Numerator << "/" << PDesc.AspectRatio.Denominator << std::endl;
52 strm << " EditRate: " << PDesc.EditRate.Numerator << "/" << PDesc.EditRate.Denominator << std::endl;
53 strm << " SampleRate: " << PDesc.SampleRate.Numerator << "/" << PDesc.SampleRate.Denominator << std::endl;
54 strm << " StoredWidth: " << (unsigned) PDesc.StoredWidth << std::endl;
55 strm << " StoredHeight: " << (unsigned) PDesc.StoredHeight << std::endl;
56 strm << " Rsize: " << (unsigned) PDesc.Rsize << std::endl;
57 strm << " Xsize: " << (unsigned) PDesc.Xsize << std::endl;
58 strm << " Ysize: " << (unsigned) PDesc.Ysize << std::endl;
59 strm << " XOsize: " << (unsigned) PDesc.XOsize << std::endl;
60 strm << " YOsize: " << (unsigned) PDesc.YOsize << std::endl;
61 strm << " XTsize: " << (unsigned) PDesc.XTsize << std::endl;
62 strm << " YTsize: " << (unsigned) PDesc.YTsize << std::endl;
63 strm << " XTOsize: " << (unsigned) PDesc.XTOsize << std::endl;
64 strm << " YTOsize: " << (unsigned) PDesc.YTOsize << std::endl;
65 strm << " ContainerDuration: " << (unsigned) PDesc.ContainerDuration << std::endl;
67 strm << "-- JPEG 2000 Metadata --" << std::endl;
68 strm << " ImageComponents:" << std::endl;
69 strm << " bits h-sep v-sep" << std::endl;
72 for ( i = 0; i < PDesc.Csize; i++ )
74 strm << " " << std::setw(4) << PDesc.ImageComponents[i].Ssize + 1 /* See ISO 15444-1, Table A11, for the origin of '+1' */
75 << " " << std::setw(5) << PDesc.ImageComponents[i].XRsize
76 << " " << std::setw(5) << PDesc.ImageComponents[i].YRsize
80 strm << " Scod: " << (short) PDesc.CodingStyleDefault.Scod << std::endl;
81 strm << " ProgressionOrder: " << (short) PDesc.CodingStyleDefault.SGcod.ProgressionOrder << std::endl;
82 strm << " NumberOfLayers: " << (short) KM_i16_BE(Kumu::cp2i<ui16_t>(PDesc.CodingStyleDefault.SGcod.NumberOfLayers)) << std::endl;
83 strm << " MultiCompTransform: " << (short) PDesc.CodingStyleDefault.SGcod.MultiCompTransform << std::endl;
84 strm << "DecompositionLevels: " << (short) PDesc.CodingStyleDefault.SPcod.DecompositionLevels << std::endl;
85 strm << " CodeblockWidth: " << (short) PDesc.CodingStyleDefault.SPcod.CodeblockWidth << std::endl;
86 strm << " CodeblockHeight: " << (short) PDesc.CodingStyleDefault.SPcod.CodeblockHeight << std::endl;
87 strm << " CodeblockStyle: " << (short) PDesc.CodingStyleDefault.SPcod.CodeblockStyle << std::endl;
88 strm << " Transformation: " << (short) PDesc.CodingStyleDefault.SPcod.Transformation << std::endl;
91 ui32_t precinct_set_size = 0;
93 for ( i = 0; PDesc.CodingStyleDefault.SPcod.PrecinctSize[i] != 0 && i < MaxPrecincts; i++ )
96 strm << " Precincts: " << (short) precinct_set_size << std::endl;
97 strm << "precinct dimensions:" << std::endl;
99 for ( i = 0; i < precinct_set_size; i++ )
100 strm << " " << i + 1 << ": " << s_exp_lookup[PDesc.CodingStyleDefault.SPcod.PrecinctSize[i]&0x0f] << " x "
101 << s_exp_lookup[(PDesc.CodingStyleDefault.SPcod.PrecinctSize[i]>>4)&0x0f] << std::endl;
103 strm << " Sqcd: " << (short) PDesc.QuantizationDefault.Sqcd << std::endl;
105 char tmp_buf[MaxDefaults*2];
106 strm << " SPqcd: " << Kumu::bin2hex(PDesc.QuantizationDefault.SPqcd, PDesc.QuantizationDefault.SPqcdLength, tmp_buf, MaxDefaults*2)
114 ASDCP::JP2K::PictureDescriptorDump(const PictureDescriptor& PDesc, FILE* stream)
120 AspectRatio: %d/%d\n\
134 ContainerDuration: %u\n",
135 PDesc.AspectRatio.Numerator, PDesc.AspectRatio.Denominator,
136 PDesc.EditRate.Numerator, PDesc.EditRate.Denominator,
137 PDesc.SampleRate.Numerator, PDesc.SampleRate.Denominator,
149 PDesc.ContainerDuration
152 fprintf(stream, "-- JPEG 2000 Metadata --\n");
153 fprintf(stream, " ImageComponents:\n");
154 fprintf(stream, " bits h-sep v-sep\n");
157 for ( i = 0; i < PDesc.Csize; i++ )
159 fprintf(stream, " %4d %5d %5d\n",
160 PDesc.ImageComponents[i].Ssize + 1, // See ISO 15444-1, Table A11, for the origin of '+1'
161 PDesc.ImageComponents[i].XRsize,
162 PDesc.ImageComponents[i].YRsize
166 fprintf(stream, " Scod: %hd\n", PDesc.CodingStyleDefault.Scod);
167 fprintf(stream, " ProgressionOrder: %hd\n", PDesc.CodingStyleDefault.SGcod.ProgressionOrder);
168 fprintf(stream, " NumberOfLayers: %hd\n",
169 KM_i16_BE(Kumu::cp2i<ui16_t>(PDesc.CodingStyleDefault.SGcod.NumberOfLayers)));
171 fprintf(stream, " MultiCompTransform: %hd\n", PDesc.CodingStyleDefault.SGcod.MultiCompTransform);
172 fprintf(stream, "DecompositionLevels: %hd\n", PDesc.CodingStyleDefault.SPcod.DecompositionLevels);
173 fprintf(stream, " CodeblockWidth: %hd\n", PDesc.CodingStyleDefault.SPcod.CodeblockWidth);
174 fprintf(stream, " CodeblockHeight: %hd\n", PDesc.CodingStyleDefault.SPcod.CodeblockHeight);
175 fprintf(stream, " CodeblockStyle: %hd\n", PDesc.CodingStyleDefault.SPcod.CodeblockStyle);
176 fprintf(stream, " Transformation: %hd\n", PDesc.CodingStyleDefault.SPcod.Transformation);
179 ui32_t precinct_set_size = 0;
181 for ( i = 0; PDesc.CodingStyleDefault.SPcod.PrecinctSize[i] != 0 && i < MaxPrecincts; i++ )
184 fprintf(stream, " Precincts: %hd\n", precinct_set_size);
185 fprintf(stream, "precinct dimensions:\n");
187 for ( i = 0; i < precinct_set_size; i++ )
188 fprintf(stream, " %d: %d x %d\n", i + 1,
189 s_exp_lookup[PDesc.CodingStyleDefault.SPcod.PrecinctSize[i]&0x0f],
190 s_exp_lookup[(PDesc.CodingStyleDefault.SPcod.PrecinctSize[i]>>4)&0x0f]
193 fprintf(stream, " Sqcd: %hd\n", PDesc.QuantizationDefault.Sqcd);
195 char tmp_buf[MaxDefaults*2];
196 fprintf(stream, " SPqcd: %s\n",
197 Kumu::bin2hex(PDesc.QuantizationDefault.SPqcd, PDesc.QuantizationDefault.SPqcdLength,
198 tmp_buf, MaxDefaults*2)
203 //------------------------------------------------------------------------------------------
205 // hidden, internal implementation of JPEG 2000 reader
208 class lh__Reader : public ASDCP::h__Reader
210 RGBAEssenceDescriptor* m_EssenceDescriptor;
211 JPEG2000PictureSubDescriptor* m_EssenceSubDescriptor;
212 ASDCP::Rational m_EditRate;
213 ASDCP::Rational m_SampleRate;
214 EssenceType_t m_Format;
216 ASDCP_NO_COPY_CONSTRUCT(lh__Reader);
219 PictureDescriptor m_PDesc; // codestream parameter list
221 lh__Reader(const Dictionary& d) :
222 ASDCP::h__Reader(d), m_EssenceDescriptor(0), m_EssenceSubDescriptor(0), m_Format(ESS_UNKNOWN) {}
223 Result_t OpenRead(const char*, EssenceType_t);
224 Result_t ReadFrame(ui32_t, JP2K::FrameBuffer&, AESDecContext*, HMACContext*);
225 Result_t MD_to_JP2K_PDesc(JP2K::PictureDescriptor& PDesc);
230 lh__Reader::MD_to_JP2K_PDesc(JP2K::PictureDescriptor& PDesc)
232 memset(&PDesc, 0, sizeof(PDesc));
233 MXF::RGBAEssenceDescriptor* PDescObj = (MXF::RGBAEssenceDescriptor*)m_EssenceDescriptor;
235 PDesc.EditRate = m_EditRate;
236 PDesc.SampleRate = m_SampleRate;
237 assert(PDescObj->ContainerDuration <= 0xFFFFFFFFL);
238 PDesc.ContainerDuration = (ui32_t) PDescObj->ContainerDuration;
239 PDesc.StoredWidth = PDescObj->StoredWidth;
240 PDesc.StoredHeight = PDescObj->StoredHeight;
241 PDesc.AspectRatio = PDescObj->AspectRatio;
243 if ( m_EssenceSubDescriptor != 0 )
245 PDesc.Rsize = m_EssenceSubDescriptor->Rsize;
246 PDesc.Xsize = m_EssenceSubDescriptor->Xsize;
247 PDesc.Ysize = m_EssenceSubDescriptor->Ysize;
248 PDesc.XOsize = m_EssenceSubDescriptor->XOsize;
249 PDesc.YOsize = m_EssenceSubDescriptor->YOsize;
250 PDesc.XTsize = m_EssenceSubDescriptor->XTsize;
251 PDesc.YTsize = m_EssenceSubDescriptor->YTsize;
252 PDesc.XTOsize = m_EssenceSubDescriptor->XTOsize;
253 PDesc.YTOsize = m_EssenceSubDescriptor->YTOsize;
254 PDesc.Csize = m_EssenceSubDescriptor->Csize;
256 // PictureComponentSizing
257 ui32_t tmp_size = m_EssenceSubDescriptor->PictureComponentSizing.Length();
259 if ( tmp_size == 17 ) // ( 2 * sizeof(ui32_t) ) + 3 components * 3 byte each
260 memcpy(&PDesc.ImageComponents, m_EssenceSubDescriptor->PictureComponentSizing.RoData() + 8, tmp_size - 8);
263 DefaultLogSink().Error("Unexpected PictureComponentSizing size: %u, should be 17\n", tmp_size);
265 // CodingStyleDefault
266 memset(&m_PDesc.CodingStyleDefault, 0, sizeof(CodingStyleDefault_t));
267 memcpy(&m_PDesc.CodingStyleDefault,
268 m_EssenceSubDescriptor->CodingStyleDefault.RoData(),
269 m_EssenceSubDescriptor->CodingStyleDefault.Length());
271 // QuantizationDefault
272 memset(&m_PDesc.QuantizationDefault, 0, sizeof(QuantizationDefault_t));
273 memcpy(&m_PDesc.QuantizationDefault,
274 m_EssenceSubDescriptor->QuantizationDefault.RoData(),
275 m_EssenceSubDescriptor->QuantizationDefault.Length());
277 m_PDesc.QuantizationDefault.SPqcdLength = m_EssenceSubDescriptor->QuantizationDefault.Length() - 1;
286 lh__Reader::OpenRead(const char* filename, EssenceType_t type)
288 Result_t result = OpenMXFRead(filename);
290 if( ASDCP_SUCCESS(result) )
292 InterchangeObject* tmp_iobj = 0;
293 m_HeaderPart.GetMDObjectByType(OBJ_TYPE_ARGS(RGBAEssenceDescriptor), &tmp_iobj);
294 m_EssenceDescriptor = static_cast<RGBAEssenceDescriptor*>(tmp_iobj);
296 m_HeaderPart.GetMDObjectByType(OBJ_TYPE_ARGS(JPEG2000PictureSubDescriptor), &tmp_iobj);
297 m_EssenceSubDescriptor = static_cast<JPEG2000PictureSubDescriptor*>(tmp_iobj);
299 std::list<InterchangeObject*> ObjectList;
300 m_HeaderPart.GetMDObjectsByType(OBJ_TYPE_ARGS(Track), ObjectList);
302 if ( ObjectList.empty() )
304 DefaultLogSink().Error("MXF Metadata contains no Track Sets.\n");
305 return RESULT_FORMAT;
308 m_EditRate = ((Track*)ObjectList.front())->EditRate;
309 m_SampleRate = m_EssenceDescriptor->SampleRate;
311 if ( type == ASDCP::ESS_JPEG_2000 )
313 if ( m_EditRate != m_SampleRate )
315 DefaultLogSink().Error("EditRate and SampleRate do not match (%.03f, %.03f).\n",
316 m_EditRate.Quotient(), m_SampleRate.Quotient());
318 if ( m_EditRate == EditRate_24 && m_SampleRate == EditRate_48 )
320 DefaultLogSink().Debug("File may contain JPEG Interop stereoscopic images.\n");
321 return RESULT_SFORMAT;
324 return RESULT_FORMAT;
327 else if ( type == ASDCP::ESS_JPEG_2000_S )
329 if ( ! ( m_EditRate == EditRate_24 && m_SampleRate == EditRate_48 ) )
331 DefaultLogSink().Error("EditRate and SampleRate not correct for 24/48 stereoscopic essence.\n");
332 return RESULT_FORMAT;
337 DefaultLogSink().Error("'type' argument unexpected: %x\n", type);
341 result = MD_to_JP2K_PDesc(m_PDesc);
344 if( ASDCP_SUCCESS(result) )
345 result = InitMXFIndex();
347 if( ASDCP_SUCCESS(result) )
356 lh__Reader::ReadFrame(ui32_t FrameNum, JP2K::FrameBuffer& FrameBuf,
357 AESDecContext* Ctx, HMACContext* HMAC)
359 if ( ! m_File.IsOpen() )
363 return ReadEKLVFrame(FrameNum, FrameBuf, m_Dict->ul(MDD_JPEG2000Essence), Ctx, HMAC);
368 class ASDCP::JP2K::MXFReader::h__Reader : public lh__Reader
370 ASDCP_NO_COPY_CONSTRUCT(h__Reader);
374 h__Reader(const Dictionary& d) : lh__Reader(d) {}
379 //------------------------------------------------------------------------------------------
384 ASDCP::JP2K::FrameBuffer::Dump(FILE* stream, ui32_t dump_len) const
389 fprintf(stream, "Frame: %06u, %7u bytes", m_FrameNumber, m_Size);
394 Kumu::hexdump(m_Data, dump_len, stream);
398 //------------------------------------------------------------------------------------------
400 ASDCP::JP2K::MXFReader::MXFReader()
402 m_Reader = new h__Reader(DefaultCompositeDict());
406 ASDCP::JP2K::MXFReader::~MXFReader()
410 // Open the file for reading. The file must exist. Returns error if the
411 // operation cannot be completed.
413 ASDCP::JP2K::MXFReader::OpenRead(const char* filename) const
415 return m_Reader->OpenRead(filename, ASDCP::ESS_JPEG_2000);
420 ASDCP::JP2K::MXFReader::ReadFrame(ui32_t FrameNum, FrameBuffer& FrameBuf,
421 AESDecContext* Ctx, HMACContext* HMAC) const
423 if ( m_Reader && m_Reader->m_File.IsOpen() )
424 return m_Reader->ReadFrame(FrameNum, FrameBuf, Ctx, HMAC);
430 // Fill the struct with the values from the file's header.
431 // Returns RESULT_INIT if the file is not open.
433 ASDCP::JP2K::MXFReader::FillPictureDescriptor(PictureDescriptor& PDesc) const
435 if ( m_Reader && m_Reader->m_File.IsOpen() )
437 PDesc = m_Reader->m_PDesc;
445 // Fill the struct with the values from the file's header.
446 // Returns RESULT_INIT if the file is not open.
448 ASDCP::JP2K::MXFReader::FillWriterInfo(WriterInfo& Info) const
450 if ( m_Reader && m_Reader->m_File.IsOpen() )
452 Info = m_Reader->m_Info;
461 ASDCP::JP2K::MXFReader::DumpHeaderMetadata(FILE* stream) const
463 if ( m_Reader->m_File.IsOpen() )
464 m_Reader->m_HeaderPart.Dump(stream);
470 ASDCP::JP2K::MXFReader::DumpIndex(FILE* stream) const
472 if ( m_Reader->m_File.IsOpen() )
473 m_Reader->m_FooterPart.Dump(stream);
477 //------------------------------------------------------------------------------------------
480 class ASDCP::JP2K::MXFSReader::h__SReader : public lh__Reader
482 ui32_t m_StereoFrameReady;
485 h__SReader(const Dictionary& d) : lh__Reader(d), m_StereoFrameReady(0xffffffff) {}
488 Result_t ReadFrame(ui32_t FrameNum, StereoscopicPhase_t phase, FrameBuffer& FrameBuf,
489 AESDecContext* Ctx, HMACContext* HMAC)
491 // look up frame index node
492 IndexTableSegment::IndexEntry TmpEntry;
494 if ( ASDCP_FAILURE(m_FooterPart.Lookup(FrameNum, TmpEntry)) )
496 DefaultLogSink().Error("Frame value out of range: %u\n", FrameNum);
500 // get frame position
501 Kumu::fpos_t FilePosition = m_EssenceStart + TmpEntry.StreamOffset;
502 Result_t result = RESULT_OK;
504 if ( phase == SP_LEFT )
506 if ( FilePosition != m_LastPosition )
508 m_LastPosition = FilePosition;
509 result = m_File.Seek(FilePosition);
512 // the call to ReadEKLVPacket() will leave the file on an R frame
513 m_StereoFrameReady = FrameNum;
515 else if ( phase == SP_RIGHT )
517 if ( m_StereoFrameReady != FrameNum )
519 // the file is not already positioned, we must do some work
520 // seek to the companion SP_LEFT frame and read the frame's key and length
521 if ( FilePosition != m_LastPosition )
523 m_LastPosition = FilePosition;
524 result = m_File.Seek(FilePosition);
528 result = Reader.ReadKLFromFile(m_File);
530 if ( ASDCP_SUCCESS(result) )
532 // skip over the companion SP_LEFT frame
533 Kumu::fpos_t new_pos = FilePosition + SMPTE_UL_LENGTH + Reader.KLLength() + Reader.Length();
534 result = m_File.Seek(new_pos);
538 // the call to ReadEKLVPacket() will leave the file not on an R frame
539 m_StereoFrameReady = 0xffffffff;
543 DefaultLogSink().Error("Unexpected stereoscopic phase value: %u\n", phase);
547 if( ASDCP_SUCCESS(result) )
549 ui32_t SequenceNum = FrameNum * 2;
550 SequenceNum += ( phase == SP_RIGHT ) ? 2 : 1;
552 result = ReadEKLVPacket(FrameNum, SequenceNum, FrameBuf, m_Dict->ul(MDD_JPEG2000Essence), Ctx, HMAC);
561 ASDCP::JP2K::MXFSReader::MXFSReader()
563 m_Reader = new h__SReader(DefaultCompositeDict());
567 ASDCP::JP2K::MXFSReader::~MXFSReader()
571 // Open the file for reading. The file must exist. Returns error if the
572 // operation cannot be completed.
574 ASDCP::JP2K::MXFSReader::OpenRead(const char* filename) const
576 return m_Reader->OpenRead(filename, ASDCP::ESS_JPEG_2000_S);
581 ASDCP::JP2K::MXFSReader::ReadFrame(ui32_t FrameNum, SFrameBuffer& FrameBuf, AESDecContext* Ctx, HMACContext* HMAC) const
583 Result_t result = RESULT_INIT;
585 if ( m_Reader && m_Reader->m_File.IsOpen() )
587 result = m_Reader->ReadFrame(FrameNum, SP_LEFT, FrameBuf.Left, Ctx, HMAC);
589 if ( ASDCP_SUCCESS(result) )
590 result = m_Reader->ReadFrame(FrameNum, SP_RIGHT, FrameBuf.Right, Ctx, HMAC);
598 ASDCP::JP2K::MXFSReader::ReadFrame(ui32_t FrameNum, StereoscopicPhase_t phase, FrameBuffer& FrameBuf,
599 AESDecContext* Ctx, HMACContext* HMAC) const
601 if ( m_Reader && m_Reader->m_File.IsOpen() )
602 return m_Reader->ReadFrame(FrameNum, phase, FrameBuf, Ctx, HMAC);
607 // Fill the struct with the values from the file's header.
608 // Returns RESULT_INIT if the file is not open.
610 ASDCP::JP2K::MXFSReader::FillPictureDescriptor(PictureDescriptor& PDesc) const
612 if ( m_Reader && m_Reader->m_File.IsOpen() )
614 PDesc = m_Reader->m_PDesc;
622 // Fill the struct with the values from the file's header.
623 // Returns RESULT_INIT if the file is not open.
625 ASDCP::JP2K::MXFSReader::FillWriterInfo(WriterInfo& Info) const
627 if ( m_Reader && m_Reader->m_File.IsOpen() )
629 Info = m_Reader->m_Info;
638 ASDCP::JP2K::MXFSReader::DumpHeaderMetadata(FILE* stream) const
640 if ( m_Reader->m_File.IsOpen() )
641 m_Reader->m_HeaderPart.Dump(stream);
647 ASDCP::JP2K::MXFSReader::DumpIndex(FILE* stream) const
649 if ( m_Reader->m_File.IsOpen() )
650 m_Reader->m_FooterPart.Dump(stream);
653 //------------------------------------------------------------------------------------------
657 class lh__Writer : public ASDCP::h__Writer
659 ASDCP_NO_COPY_CONSTRUCT(lh__Writer);
662 JPEG2000PictureSubDescriptor* m_EssenceSubDescriptor;
665 PictureDescriptor m_PDesc;
666 byte_t m_EssenceUL[SMPTE_UL_LENGTH];
668 lh__Writer(const Dictionary& d) : ASDCP::h__Writer(d), m_EssenceSubDescriptor(0) {
669 memset(m_EssenceUL, 0, SMPTE_UL_LENGTH);
674 Result_t OpenWrite(const char*, EssenceType_t type, ui32_t HeaderSize);
675 Result_t SetSourceStream(const PictureDescriptor&, const std::string& label,
676 ASDCP::Rational LocalEditRate = ASDCP::Rational(0,0));
677 Result_t WriteFrame(const JP2K::FrameBuffer&, bool add_index, AESEncContext*, HMACContext*);
679 Result_t JP2K_PDesc_to_MD(JP2K::PictureDescriptor& PDesc);
682 const int VideoLineMapSize = 16; // See SMPTE 377M D.2.1
683 const int PixelLayoutSize = 8*2; // See SMPTE 377M D.2.3
684 static const byte_t s_PixelLayoutXYZ[PixelLayoutSize] = { 0xd8, 0x0c, 0xd9, 0x0c, 0xda, 0x0c, 0x00 };
688 lh__Writer::JP2K_PDesc_to_MD(JP2K::PictureDescriptor& PDesc)
690 assert(m_EssenceDescriptor);
691 assert(m_EssenceSubDescriptor);
692 MXF::RGBAEssenceDescriptor* PDescObj = (MXF::RGBAEssenceDescriptor*)m_EssenceDescriptor;
694 PDescObj->ContainerDuration = PDesc.ContainerDuration;
695 PDescObj->SampleRate = PDesc.EditRate;
696 PDescObj->FrameLayout = 0;
697 PDescObj->StoredWidth = PDesc.StoredWidth;
698 PDescObj->StoredHeight = PDesc.StoredHeight;
699 PDescObj->AspectRatio = PDesc.AspectRatio;
701 // if ( m_Info.LabelSetType == LS_MXF_SMPTE )
703 // PictureEssenceCoding UL =
704 // Video Line Map ui32_t[VideoLineMapSize] = { 2, 4, 0, 0 }
706 // ComponentMaxRef ui32_t = 4095
707 // ComponentMinRef ui32_t = 0
708 // PixelLayout byte_t[PixelLayoutSize] = s_PixelLayoutXYZ
712 if ( PDesc.StoredWidth < 2049 )
714 PDescObj->PictureEssenceCoding.Set(m_Dict->ul(MDD_JP2KEssenceCompression_2K));
715 m_EssenceSubDescriptor->Rsize = 3;
719 PDescObj->PictureEssenceCoding.Set(m_Dict->ul(MDD_JP2KEssenceCompression_4K));
720 m_EssenceSubDescriptor->Rsize = 4;
723 m_EssenceSubDescriptor->Xsize = PDesc.Xsize;
724 m_EssenceSubDescriptor->Ysize = PDesc.Ysize;
725 m_EssenceSubDescriptor->XOsize = PDesc.XOsize;
726 m_EssenceSubDescriptor->YOsize = PDesc.YOsize;
727 m_EssenceSubDescriptor->XTsize = PDesc.XTsize;
728 m_EssenceSubDescriptor->YTsize = PDesc.YTsize;
729 m_EssenceSubDescriptor->XTOsize = PDesc.XTOsize;
730 m_EssenceSubDescriptor->YTOsize = PDesc.YTOsize;
731 m_EssenceSubDescriptor->Csize = PDesc.Csize;
733 const ui32_t tmp_buffer_len = 1024;
734 byte_t tmp_buffer[tmp_buffer_len];
736 *(ui32_t*)tmp_buffer = KM_i32_BE(MaxComponents); // three components
737 *(ui32_t*)(tmp_buffer+4) = KM_i32_BE(sizeof(ASDCP::JP2K::ImageComponent_t));
738 memcpy(tmp_buffer + 8, &PDesc.ImageComponents, sizeof(ASDCP::JP2K::ImageComponent_t) * MaxComponents);
740 const ui32_t pcomp_size = (sizeof(int) * 2) + (sizeof(ASDCP::JP2K::ImageComponent_t) * MaxComponents);
741 memcpy(m_EssenceSubDescriptor->PictureComponentSizing.Data(), tmp_buffer, pcomp_size);
742 m_EssenceSubDescriptor->PictureComponentSizing.Length(pcomp_size);
744 ui32_t precinct_set_size = 0, i;
745 for ( i = 0; PDesc.CodingStyleDefault.SPcod.PrecinctSize[i] != 0 && i < MaxPrecincts; i++ )
748 ui32_t csd_size = sizeof(CodingStyleDefault_t) - MaxPrecincts + precinct_set_size;
749 memcpy(m_EssenceSubDescriptor->CodingStyleDefault.Data(), &PDesc.CodingStyleDefault, csd_size);
750 m_EssenceSubDescriptor->CodingStyleDefault.Length(csd_size);
752 ui32_t qdflt_size = PDesc.QuantizationDefault.SPqcdLength + 1;
753 memcpy(m_EssenceSubDescriptor->QuantizationDefault.Data(), &PDesc.QuantizationDefault, qdflt_size);
754 m_EssenceSubDescriptor->QuantizationDefault.Length(qdflt_size);
760 // Open the file for writing. The file must not exist. Returns error if
761 // the operation cannot be completed.
763 lh__Writer::OpenWrite(const char* filename, EssenceType_t type, ui32_t HeaderSize)
765 if ( ! m_State.Test_BEGIN() )
768 Result_t result = m_File.OpenWrite(filename);
770 if ( ASDCP_SUCCESS(result) )
772 m_HeaderSize = HeaderSize;
773 RGBAEssenceDescriptor* tmp_rgba = new RGBAEssenceDescriptor(m_Dict);
774 tmp_rgba->ComponentMaxRef = 4095;
775 tmp_rgba->ComponentMinRef = 0;
777 m_EssenceDescriptor = tmp_rgba;
778 m_EssenceSubDescriptor = new JPEG2000PictureSubDescriptor(m_Dict);
779 m_EssenceSubDescriptorList.push_back((InterchangeObject*)m_EssenceSubDescriptor);
781 GenRandomValue(m_EssenceSubDescriptor->InstanceUID);
782 m_EssenceDescriptor->SubDescriptors.push_back(m_EssenceSubDescriptor->InstanceUID);
784 if ( type == ASDCP::ESS_JPEG_2000_S && m_Info.LabelSetType == LS_MXF_SMPTE )
786 InterchangeObject* StereoSubDesc = new StereoscopicPictureSubDescriptor(m_Dict);
787 m_EssenceSubDescriptorList.push_back(StereoSubDesc);
788 GenRandomValue(StereoSubDesc->InstanceUID);
789 m_EssenceDescriptor->SubDescriptors.push_back(StereoSubDesc->InstanceUID);
792 result = m_State.Goto_INIT();
798 // Automatically sets the MXF file's metadata from the first jpeg codestream stream.
800 lh__Writer::SetSourceStream(const PictureDescriptor& PDesc, const std::string& label, ASDCP::Rational LocalEditRate)
803 if ( ! m_State.Test_INIT() )
806 if ( LocalEditRate == ASDCP::Rational(0,0) )
807 LocalEditRate = PDesc.EditRate;
810 Result_t result = JP2K_PDesc_to_MD(m_PDesc);
812 if ( ASDCP_SUCCESS(result) )
813 result = WriteMXFHeader(label, UL(m_Dict->ul(MDD_JPEG_2000Wrapping)),
814 PICT_DEF_LABEL, UL(m_Dict->ul(MDD_PictureDataDef)),
815 LocalEditRate, 24 /* TCFrameRate */);
817 if ( ASDCP_SUCCESS(result) )
819 memcpy(m_EssenceUL, m_Dict->ul(MDD_JPEG2000Essence), SMPTE_UL_LENGTH);
820 m_EssenceUL[SMPTE_UL_LENGTH-1] = 1; // first (and only) essence container
821 result = m_State.Goto_READY();
827 // Writes a frame of essence to the MXF file. If the optional AESEncContext
828 // argument is present, the essence is encrypted prior to writing.
829 // Fails if the file is not open, is finalized, or an operating system
833 lh__Writer::WriteFrame(const JP2K::FrameBuffer& FrameBuf, bool add_index,
834 AESEncContext* Ctx, HMACContext* HMAC)
836 Result_t result = RESULT_OK;
838 if ( m_State.Test_READY() )
839 result = m_State.Goto_RUNNING(); // first time through
841 ui64_t StreamOffset = m_StreamOffset;
843 if ( ASDCP_SUCCESS(result) )
844 result = WriteEKLVPacket(FrameBuf, m_EssenceUL, Ctx, HMAC);
846 if ( ASDCP_SUCCESS(result) && add_index )
848 IndexTableSegment::IndexEntry Entry;
849 Entry.StreamOffset = StreamOffset;
850 m_FooterPart.PushIndexEntry(Entry);
858 // Closes the MXF file, writing the index and other closing information.
861 lh__Writer::Finalize()
863 if ( ! m_State.Test_RUNNING() )
866 m_State.Goto_FINAL();
868 return WriteMXFFooter();
873 class ASDCP::JP2K::MXFWriter::h__Writer : public lh__Writer
875 ASDCP_NO_COPY_CONSTRUCT(h__Writer);
879 h__Writer(const Dictionary& d) : lh__Writer(d) {}
883 //------------------------------------------------------------------------------------------
887 ASDCP::JP2K::MXFWriter::MXFWriter()
891 ASDCP::JP2K::MXFWriter::~MXFWriter()
896 // Open the file for writing. The file must not exist. Returns error if
897 // the operation cannot be completed.
899 ASDCP::JP2K::MXFWriter::OpenWrite(const char* filename, const WriterInfo& Info,
900 const PictureDescriptor& PDesc, ui32_t HeaderSize)
902 if ( Info.LabelSetType == LS_MXF_SMPTE )
903 m_Writer = new h__Writer(DefaultSMPTEDict());
905 m_Writer = new h__Writer(DefaultInteropDict());
907 m_Writer->m_Info = Info;
909 Result_t result = m_Writer->OpenWrite(filename, ASDCP::ESS_JPEG_2000, HeaderSize);
911 if ( ASDCP_SUCCESS(result) )
912 result = m_Writer->SetSourceStream(PDesc, JP2K_PACKAGE_LABEL);
914 if ( ASDCP_FAILURE(result) )
921 // Writes a frame of essence to the MXF file. If the optional AESEncContext
922 // argument is present, the essence is encrypted prior to writing.
923 // Fails if the file is not open, is finalized, or an operating system
926 ASDCP::JP2K::MXFWriter::WriteFrame(const FrameBuffer& FrameBuf, AESEncContext* Ctx, HMACContext* HMAC)
928 if ( m_Writer.empty() )
931 return m_Writer->WriteFrame(FrameBuf, true, Ctx, HMAC);
934 // Closes the MXF file, writing the index and other closing information.
936 ASDCP::JP2K::MXFWriter::Finalize()
938 if ( m_Writer.empty() )
941 return m_Writer->Finalize();
945 //------------------------------------------------------------------------------------------
949 class ASDCP::JP2K::MXFSWriter::h__SWriter : public lh__Writer
951 ASDCP_NO_COPY_CONSTRUCT(h__SWriter);
953 StereoscopicPhase_t m_NextPhase;
956 h__SWriter(const Dictionary& d) : lh__Writer(d), m_NextPhase(SP_LEFT) {}
959 Result_t WriteFrame(const FrameBuffer& FrameBuf, StereoscopicPhase_t phase,
960 AESEncContext* Ctx, HMACContext* HMAC)
962 if ( m_NextPhase != phase )
963 return RESULT_SPHASE;
965 if ( phase == SP_LEFT )
967 m_NextPhase = SP_RIGHT;
968 return lh__Writer::WriteFrame(FrameBuf, true, Ctx, HMAC);
971 m_NextPhase = SP_LEFT;
972 return lh__Writer::WriteFrame(FrameBuf, false, Ctx, HMAC);
978 if ( m_NextPhase != SP_LEFT )
979 return RESULT_SPHASE;
981 assert( m_FramesWritten % 2 == 0 );
982 m_FramesWritten /= 2;
983 return lh__Writer::Finalize();
989 ASDCP::JP2K::MXFSWriter::MXFSWriter()
993 ASDCP::JP2K::MXFSWriter::~MXFSWriter()
998 // Open the file for writing. The file must not exist. Returns error if
999 // the operation cannot be completed.
1001 ASDCP::JP2K::MXFSWriter::OpenWrite(const char* filename, const WriterInfo& Info,
1002 const PictureDescriptor& PDesc, ui32_t HeaderSize)
1004 if ( Info.LabelSetType == LS_MXF_SMPTE )
1005 m_Writer = new h__SWriter(DefaultSMPTEDict());
1007 m_Writer = new h__SWriter(DefaultInteropDict());
1009 if ( PDesc.EditRate != ASDCP::EditRate_24 )
1011 DefaultLogSink().Error("Stereoscopic wrapping requires 24 fps input streams.\n");
1012 return RESULT_FORMAT;
1015 if ( PDesc.StoredWidth > 2048 )
1016 DefaultLogSink().Warn("Wrapping non-standard 4K stereoscopic content. I hope you know what you are doing!\n");
1018 m_Writer->m_Info = Info;
1020 Result_t result = m_Writer->OpenWrite(filename, ASDCP::ESS_JPEG_2000_S, HeaderSize);
1022 if ( ASDCP_SUCCESS(result) )
1024 PictureDescriptor TmpPDesc = PDesc;
1025 TmpPDesc.EditRate = ASDCP::EditRate_48;
1027 result = m_Writer->SetSourceStream(TmpPDesc, JP2K_S_PACKAGE_LABEL, ASDCP::EditRate_24);
1030 if ( ASDCP_FAILURE(result) )
1037 ASDCP::JP2K::MXFSWriter::WriteFrame(const SFrameBuffer& FrameBuf, AESEncContext* Ctx, HMACContext* HMAC)
1039 if ( m_Writer.empty() )
1042 Result_t result = m_Writer->WriteFrame(FrameBuf.Left, SP_LEFT, Ctx, HMAC);
1044 if ( ASDCP_SUCCESS(result) )
1045 result = m_Writer->WriteFrame(FrameBuf.Right, SP_RIGHT, Ctx, HMAC);
1050 // Writes a frame of essence to the MXF file. If the optional AESEncContext
1051 // argument is present, the essence is encrypted prior to writing.
1052 // Fails if the file is not open, is finalized, or an operating system
1055 ASDCP::JP2K::MXFSWriter::WriteFrame(const FrameBuffer& FrameBuf, StereoscopicPhase_t phase,
1056 AESEncContext* Ctx, HMACContext* HMAC)
1058 if ( m_Writer.empty() )
1061 return m_Writer->WriteFrame(FrameBuf, phase, Ctx, HMAC);
1064 // Closes the MXF file, writing the index and other closing information.
1066 ASDCP::JP2K::MXFSWriter::Finalize()
1068 if ( m_Writer.empty() )
1071 return m_Writer->Finalize();
1075 // end AS_DCP_JP2K.cpp