2 Copyright (c) 2004-2008, 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
207 class lh__Reader : public ASDCP::h__Reader
209 RGBAEssenceDescriptor* m_EssenceDescriptor;
210 JPEG2000PictureSubDescriptor* m_EssenceSubDescriptor;
211 ASDCP::Rational m_EditRate;
212 ASDCP::Rational m_SampleRate;
213 EssenceType_t m_Format;
215 ASDCP_NO_COPY_CONSTRUCT(lh__Reader);
218 PictureDescriptor m_PDesc; // codestream parameter list
220 lh__Reader() : m_EssenceDescriptor(0), m_EssenceSubDescriptor(0), m_Format(ESS_UNKNOWN) {}
221 Result_t OpenRead(const char*, EssenceType_t);
222 Result_t ReadFrame(ui32_t, JP2K::FrameBuffer&, AESDecContext*, HMACContext*);
223 Result_t MD_to_JP2K_PDesc(JP2K::PictureDescriptor& PDesc);
228 lh__Reader::MD_to_JP2K_PDesc(JP2K::PictureDescriptor& PDesc)
230 memset(&PDesc, 0, sizeof(PDesc));
231 MXF::RGBAEssenceDescriptor* PDescObj = (MXF::RGBAEssenceDescriptor*)m_EssenceDescriptor;
233 PDesc.EditRate = m_EditRate;
234 PDesc.SampleRate = m_SampleRate;
235 assert(PDescObj->ContainerDuration <= 0xFFFFFFFFL);
236 PDesc.ContainerDuration = (ui32_t) PDescObj->ContainerDuration;
237 PDesc.StoredWidth = PDescObj->StoredWidth;
238 PDesc.StoredHeight = PDescObj->StoredHeight;
239 PDesc.AspectRatio = PDescObj->AspectRatio;
241 if ( m_EssenceSubDescriptor != 0 )
243 PDesc.Rsize = m_EssenceSubDescriptor->Rsize;
244 PDesc.Xsize = m_EssenceSubDescriptor->Xsize;
245 PDesc.Ysize = m_EssenceSubDescriptor->Ysize;
246 PDesc.XOsize = m_EssenceSubDescriptor->XOsize;
247 PDesc.YOsize = m_EssenceSubDescriptor->YOsize;
248 PDesc.XTsize = m_EssenceSubDescriptor->XTsize;
249 PDesc.YTsize = m_EssenceSubDescriptor->YTsize;
250 PDesc.XTOsize = m_EssenceSubDescriptor->XTOsize;
251 PDesc.YTOsize = m_EssenceSubDescriptor->YTOsize;
252 PDesc.Csize = m_EssenceSubDescriptor->Csize;
254 // PictureComponentSizing
255 ui32_t tmp_size = m_EssenceSubDescriptor->PictureComponentSizing.Length();
257 if ( tmp_size == 17 ) // ( 2 * sizeof(ui32_t) ) + 3 components * 3 byte each
258 memcpy(&PDesc.ImageComponents, m_EssenceSubDescriptor->PictureComponentSizing.RoData() + 8, tmp_size - 8);
261 DefaultLogSink().Error("Unexpected PictureComponentSizing size: %u, should be 17\n", tmp_size);
263 // CodingStyleDefault
264 memset(&m_PDesc.CodingStyleDefault, 0, sizeof(CodingStyleDefault_t));
265 memcpy(&m_PDesc.CodingStyleDefault,
266 m_EssenceSubDescriptor->CodingStyleDefault.RoData(),
267 m_EssenceSubDescriptor->CodingStyleDefault.Length());
269 // QuantizationDefault
270 memset(&m_PDesc.QuantizationDefault, 0, sizeof(QuantizationDefault_t));
271 memcpy(&m_PDesc.QuantizationDefault,
272 m_EssenceSubDescriptor->QuantizationDefault.RoData(),
273 m_EssenceSubDescriptor->QuantizationDefault.Length());
275 m_PDesc.QuantizationDefault.SPqcdLength = m_EssenceSubDescriptor->QuantizationDefault.Length() - 1;
284 lh__Reader::OpenRead(const char* filename, EssenceType_t type)
286 Result_t result = OpenMXFRead(filename);
288 if( ASDCP_SUCCESS(result) )
290 InterchangeObject* tmp_iobj = 0;
291 m_HeaderPart.GetMDObjectByType(OBJ_TYPE_ARGS(RGBAEssenceDescriptor), &tmp_iobj);
292 m_EssenceDescriptor = static_cast<RGBAEssenceDescriptor*>(tmp_iobj);
294 m_HeaderPart.GetMDObjectByType(OBJ_TYPE_ARGS(JPEG2000PictureSubDescriptor), &tmp_iobj);
295 m_EssenceSubDescriptor = static_cast<JPEG2000PictureSubDescriptor*>(tmp_iobj);
297 std::list<InterchangeObject*> ObjectList;
298 m_HeaderPart.GetMDObjectsByType(OBJ_TYPE_ARGS(Track), ObjectList);
300 if ( ObjectList.empty() )
302 DefaultLogSink().Error("MXF Metadata contains no Track Sets.\n");
303 return RESULT_FORMAT;
306 m_EditRate = ((Track*)ObjectList.front())->EditRate;
307 m_SampleRate = m_EssenceDescriptor->SampleRate;
309 if ( type == ASDCP::ESS_JPEG_2000 )
311 if ( m_EditRate != m_SampleRate )
313 DefaultLogSink().Error("EditRate and SampleRate do not match (%.03f, %.03f).\n",
314 m_EditRate.Quotient(), m_SampleRate.Quotient());
316 if ( m_EditRate == EditRate_24 && m_SampleRate == EditRate_48 )
318 DefaultLogSink().Debug("File may contain JPEG Interop stereoscopic images.\n");
319 return RESULT_SFORMAT;
322 return RESULT_FORMAT;
325 else if ( type == ASDCP::ESS_JPEG_2000_S )
327 if ( ! ( m_EditRate == EditRate_24 && m_SampleRate == EditRate_48 ) )
329 DefaultLogSink().Error("EditRate and SampleRate not correct for 24/48 stereoscopic essence.\n");
330 return RESULT_FORMAT;
335 DefaultLogSink().Error("'type' argument unexpected: %x\n", type);
339 result = MD_to_JP2K_PDesc(m_PDesc);
342 if( ASDCP_SUCCESS(result) )
343 result = InitMXFIndex();
345 if( ASDCP_SUCCESS(result) )
354 lh__Reader::ReadFrame(ui32_t FrameNum, JP2K::FrameBuffer& FrameBuf,
355 AESDecContext* Ctx, HMACContext* HMAC)
357 if ( ! m_File.IsOpen() )
360 return ReadEKLVFrame(FrameNum, FrameBuf, Dict::ul(MDD_JPEG2000Essence), Ctx, HMAC);
365 class ASDCP::JP2K::MXFReader::h__Reader : public lh__Reader
371 //------------------------------------------------------------------------------------------
376 ASDCP::JP2K::FrameBuffer::Dump(FILE* stream, ui32_t dump_len) const
381 fprintf(stream, "Frame: %06u, %7u bytes", m_FrameNumber, m_Size);
386 Kumu::hexdump(m_Data, dump_len, stream);
390 //------------------------------------------------------------------------------------------
392 ASDCP::JP2K::MXFReader::MXFReader()
394 m_Reader = new h__Reader;
398 ASDCP::JP2K::MXFReader::~MXFReader()
402 // Open the file for reading. The file must exist. Returns error if the
403 // operation cannot be completed.
405 ASDCP::JP2K::MXFReader::OpenRead(const char* filename) const
407 return m_Reader->OpenRead(filename, ASDCP::ESS_JPEG_2000);
412 ASDCP::JP2K::MXFReader::ReadFrame(ui32_t FrameNum, FrameBuffer& FrameBuf,
413 AESDecContext* Ctx, HMACContext* HMAC) const
415 if ( m_Reader && m_Reader->m_File.IsOpen() )
416 return m_Reader->ReadFrame(FrameNum, FrameBuf, Ctx, HMAC);
422 // Fill the struct with the values from the file's header.
423 // Returns RESULT_INIT if the file is not open.
425 ASDCP::JP2K::MXFReader::FillPictureDescriptor(PictureDescriptor& PDesc) const
427 if ( m_Reader && m_Reader->m_File.IsOpen() )
429 PDesc = m_Reader->m_PDesc;
437 // Fill the struct with the values from the file's header.
438 // Returns RESULT_INIT if the file is not open.
440 ASDCP::JP2K::MXFReader::FillWriterInfo(WriterInfo& Info) const
442 if ( m_Reader && m_Reader->m_File.IsOpen() )
444 Info = m_Reader->m_Info;
453 ASDCP::JP2K::MXFReader::DumpHeaderMetadata(FILE* stream) const
455 if ( m_Reader->m_File.IsOpen() )
456 m_Reader->m_HeaderPart.Dump(stream);
462 ASDCP::JP2K::MXFReader::DumpIndex(FILE* stream) const
464 if ( m_Reader->m_File.IsOpen() )
465 m_Reader->m_FooterPart.Dump(stream);
469 //------------------------------------------------------------------------------------------
471 class ASDCP::JP2K::MXFSReader::h__SReader : public lh__Reader
473 ui32_t m_StereoFrameReady;
476 h__SReader() : m_StereoFrameReady(0xffffffff) {}
479 Result_t ReadFrame(ui32_t FrameNum, StereoscopicPhase_t phase, FrameBuffer& FrameBuf,
480 AESDecContext* Ctx, HMACContext* HMAC)
482 // look up frame index node
483 IndexTableSegment::IndexEntry TmpEntry;
485 if ( ASDCP_FAILURE(m_FooterPart.Lookup(FrameNum, TmpEntry)) )
487 DefaultLogSink().Error("Frame value out of range: %u\n", FrameNum);
491 // get frame position
492 Kumu::fpos_t FilePosition = m_EssenceStart + TmpEntry.StreamOffset;
493 Result_t result = RESULT_OK;
495 if ( phase == SP_LEFT )
497 if ( FilePosition != m_LastPosition )
499 m_LastPosition = FilePosition;
500 result = m_File.Seek(FilePosition);
503 // the call to ReadEKLVPacket() will leave the file on an R frame
504 m_StereoFrameReady = FrameNum;
506 else if ( phase == SP_RIGHT )
508 if ( m_StereoFrameReady != FrameNum )
510 // the file is not already positioned, we must do some work
511 // seek to the companion SP_LEFT frame and read the frame's key and length
512 if ( FilePosition != m_LastPosition )
514 m_LastPosition = FilePosition;
515 result = m_File.Seek(FilePosition);
519 result = Reader.ReadKLFromFile(m_File);
521 if ( ASDCP_SUCCESS(result) )
523 // skip over the companion SP_LEFT frame
524 Kumu::fpos_t new_pos = FilePosition + SMPTE_UL_LENGTH + Reader.KLLength() + Reader.Length();
525 result = m_File.Seek(new_pos);
529 // the call to ReadEKLVPacket() will leave the file not on an R frame
530 m_StereoFrameReady = 0xffffffff;
534 DefaultLogSink().Error("Unexpected stereoscopic phase value: %u\n", phase);
538 if( ASDCP_SUCCESS(result) )
540 ui32_t SequenceNum = FrameNum * 2;
541 SequenceNum += ( phase == SP_RIGHT ) ? 2 : 1;
542 result = ReadEKLVPacket(FrameNum, SequenceNum, FrameBuf, Dict::ul(MDD_JPEG2000Essence), Ctx, HMAC);
551 ASDCP::JP2K::MXFSReader::MXFSReader()
553 m_Reader = new h__SReader;
557 ASDCP::JP2K::MXFSReader::~MXFSReader()
561 // Open the file for reading. The file must exist. Returns error if the
562 // operation cannot be completed.
564 ASDCP::JP2K::MXFSReader::OpenRead(const char* filename) const
566 return m_Reader->OpenRead(filename, ASDCP::ESS_JPEG_2000_S);
571 ASDCP::JP2K::MXFSReader::ReadFrame(ui32_t FrameNum, SFrameBuffer& FrameBuf, AESDecContext* Ctx, HMACContext* HMAC) const
573 Result_t result = RESULT_INIT;
575 if ( m_Reader && m_Reader->m_File.IsOpen() )
577 result = m_Reader->ReadFrame(FrameNum, SP_LEFT, FrameBuf.Left, Ctx, HMAC);
579 if ( ASDCP_SUCCESS(result) )
580 result = m_Reader->ReadFrame(FrameNum, SP_RIGHT, FrameBuf.Right, Ctx, HMAC);
588 ASDCP::JP2K::MXFSReader::ReadFrame(ui32_t FrameNum, StereoscopicPhase_t phase, FrameBuffer& FrameBuf,
589 AESDecContext* Ctx, HMACContext* HMAC) const
591 if ( m_Reader && m_Reader->m_File.IsOpen() )
592 return m_Reader->ReadFrame(FrameNum, phase, FrameBuf, Ctx, HMAC);
597 // Fill the struct with the values from the file's header.
598 // Returns RESULT_INIT if the file is not open.
600 ASDCP::JP2K::MXFSReader::FillPictureDescriptor(PictureDescriptor& PDesc) const
602 if ( m_Reader && m_Reader->m_File.IsOpen() )
604 PDesc = m_Reader->m_PDesc;
612 // Fill the struct with the values from the file's header.
613 // Returns RESULT_INIT if the file is not open.
615 ASDCP::JP2K::MXFSReader::FillWriterInfo(WriterInfo& Info) const
617 if ( m_Reader && m_Reader->m_File.IsOpen() )
619 Info = m_Reader->m_Info;
628 ASDCP::JP2K::MXFSReader::DumpHeaderMetadata(FILE* stream) const
630 if ( m_Reader->m_File.IsOpen() )
631 m_Reader->m_HeaderPart.Dump(stream);
637 ASDCP::JP2K::MXFSReader::DumpIndex(FILE* stream) const
639 if ( m_Reader->m_File.IsOpen() )
640 m_Reader->m_FooterPart.Dump(stream);
643 //------------------------------------------------------------------------------------------
647 class lh__Writer : public ASDCP::h__Writer
649 JPEG2000PictureSubDescriptor* m_EssenceSubDescriptor;
652 PictureDescriptor m_PDesc;
653 byte_t m_EssenceUL[SMPTE_UL_LENGTH];
655 ASDCP_NO_COPY_CONSTRUCT(lh__Writer);
657 lh__Writer() : m_EssenceSubDescriptor(0) {
658 memset(m_EssenceUL, 0, SMPTE_UL_LENGTH);
663 Result_t OpenWrite(const char*, EssenceType_t type, ui32_t HeaderSize);
664 Result_t SetSourceStream(const PictureDescriptor&, const std::string& label,
665 ASDCP::Rational LocalEditRate = ASDCP::Rational(0,0));
666 Result_t WriteFrame(const JP2K::FrameBuffer&, bool add_index, AESEncContext*, HMACContext*);
668 Result_t JP2K_PDesc_to_MD(JP2K::PictureDescriptor& PDesc);
671 const int VideoLineMapSize = 16; // See SMPTE 377M D.2.1
672 const int PixelLayoutSize = 8*2; // See SMPTE 377M D.2.3
673 static const byte_t s_PixelLayoutXYZ[PixelLayoutSize] = { 0xd8, 0x0c, 0xd9, 0x0c, 0xda, 0x0c, 0x00 };
677 lh__Writer::JP2K_PDesc_to_MD(JP2K::PictureDescriptor& PDesc)
679 assert(m_EssenceDescriptor);
680 assert(m_EssenceSubDescriptor);
681 MXF::RGBAEssenceDescriptor* PDescObj = (MXF::RGBAEssenceDescriptor*)m_EssenceDescriptor;
683 PDescObj->ContainerDuration = PDesc.ContainerDuration;
684 PDescObj->SampleRate = PDesc.EditRate;
685 PDescObj->FrameLayout = 0;
686 PDescObj->StoredWidth = PDesc.StoredWidth;
687 PDescObj->StoredHeight = PDesc.StoredHeight;
688 PDescObj->AspectRatio = PDesc.AspectRatio;
690 // if ( m_Info.LabelSetType == LS_MXF_SMPTE )
692 // PictureEssenceCoding UL =
693 // Video Line Map ui32_t[VideoLineMapSize] = { 2, 4, 0, 0 }
695 // ComponentMaxRef ui32_t = 4095
696 // ComponentMinRef ui32_t = 0
697 // PixelLayout byte_t[PixelLayoutSize] = s_PixelLayoutXYZ
700 if ( PDesc.StoredWidth < 2049 )
702 PDescObj->PictureEssenceCoding.Set(Dict::ul(MDD_JP2KEssenceCompression_2K));
703 m_EssenceSubDescriptor->Rsize = 3;
707 PDescObj->PictureEssenceCoding.Set(Dict::ul(MDD_JP2KEssenceCompression_4K));
708 m_EssenceSubDescriptor->Rsize = 4;
711 m_EssenceSubDescriptor->Xsize = PDesc.Xsize;
712 m_EssenceSubDescriptor->Ysize = PDesc.Ysize;
713 m_EssenceSubDescriptor->XOsize = PDesc.XOsize;
714 m_EssenceSubDescriptor->YOsize = PDesc.YOsize;
715 m_EssenceSubDescriptor->XTsize = PDesc.XTsize;
716 m_EssenceSubDescriptor->YTsize = PDesc.YTsize;
717 m_EssenceSubDescriptor->XTOsize = PDesc.XTOsize;
718 m_EssenceSubDescriptor->YTOsize = PDesc.YTOsize;
719 m_EssenceSubDescriptor->Csize = PDesc.Csize;
721 const ui32_t tmp_buffer_len = 1024;
722 byte_t tmp_buffer[tmp_buffer_len];
724 *(ui32_t*)tmp_buffer = KM_i32_BE(MaxComponents); // three components
725 *(ui32_t*)(tmp_buffer+4) = KM_i32_BE(sizeof(ASDCP::JP2K::ImageComponent_t));
726 memcpy(tmp_buffer + 8, &PDesc.ImageComponents, sizeof(ASDCP::JP2K::ImageComponent_t) * MaxComponents);
728 const ui32_t pcomp_size = (sizeof(int) * 2) + (sizeof(ASDCP::JP2K::ImageComponent_t) * MaxComponents);
729 memcpy(m_EssenceSubDescriptor->PictureComponentSizing.Data(), tmp_buffer, pcomp_size);
730 m_EssenceSubDescriptor->PictureComponentSizing.Length(pcomp_size);
732 ui32_t precinct_set_size = 0, i;
733 for ( i = 0; PDesc.CodingStyleDefault.SPcod.PrecinctSize[i] != 0 && i < MaxPrecincts; i++ )
736 ui32_t csd_size = sizeof(CodingStyleDefault_t) - MaxPrecincts + precinct_set_size;
737 memcpy(m_EssenceSubDescriptor->CodingStyleDefault.Data(), &PDesc.CodingStyleDefault, csd_size);
738 m_EssenceSubDescriptor->CodingStyleDefault.Length(csd_size);
740 ui32_t qdflt_size = PDesc.QuantizationDefault.SPqcdLength + 1;
741 memcpy(m_EssenceSubDescriptor->QuantizationDefault.Data(), &PDesc.QuantizationDefault, qdflt_size);
742 m_EssenceSubDescriptor->QuantizationDefault.Length(qdflt_size);
748 // Open the file for writing. The file must not exist. Returns error if
749 // the operation cannot be completed.
751 lh__Writer::OpenWrite(const char* filename, EssenceType_t type, ui32_t HeaderSize)
753 if ( ! m_State.Test_BEGIN() )
756 Result_t result = m_File.OpenWrite(filename);
758 if ( ASDCP_SUCCESS(result) )
760 m_HeaderSize = HeaderSize;
761 RGBAEssenceDescriptor* tmp_rgba = new RGBAEssenceDescriptor;
762 tmp_rgba->ComponentMaxRef = 4095;
763 tmp_rgba->ComponentMinRef = 0;
765 m_EssenceDescriptor = tmp_rgba;
766 m_EssenceSubDescriptor = new JPEG2000PictureSubDescriptor;
767 m_EssenceSubDescriptorList.push_back((InterchangeObject*)m_EssenceSubDescriptor);
769 GenRandomValue(m_EssenceSubDescriptor->InstanceUID);
770 m_EssenceDescriptor->SubDescriptors.push_back(m_EssenceSubDescriptor->InstanceUID);
772 if ( type == ASDCP::ESS_JPEG_2000_S && m_Info.LabelSetType == LS_MXF_SMPTE )
774 InterchangeObject* StereoSubDesc = new StereoscopicPictureSubDescriptor;
775 m_EssenceSubDescriptorList.push_back(StereoSubDesc);
776 GenRandomValue(StereoSubDesc->InstanceUID);
777 m_EssenceDescriptor->SubDescriptors.push_back(StereoSubDesc->InstanceUID);
780 result = m_State.Goto_INIT();
786 // Automatically sets the MXF file's metadata from the first jpeg codestream stream.
788 lh__Writer::SetSourceStream(const PictureDescriptor& PDesc, const std::string& label, ASDCP::Rational LocalEditRate)
790 if ( ! m_State.Test_INIT() )
793 if ( LocalEditRate == ASDCP::Rational(0,0) )
794 LocalEditRate = PDesc.EditRate;
797 Result_t result = JP2K_PDesc_to_MD(m_PDesc);
799 if ( ASDCP_SUCCESS(result) )
800 result = WriteMXFHeader(label, UL(Dict::ul(MDD_JPEG_2000Wrapping)),
801 PICT_DEF_LABEL, UL(Dict::ul(MDD_PictureDataDef)),
802 LocalEditRate, 24 /* TCFrameRate */);
804 if ( ASDCP_SUCCESS(result) )
806 memcpy(m_EssenceUL, Dict::ul(MDD_JPEG2000Essence), SMPTE_UL_LENGTH);
807 m_EssenceUL[SMPTE_UL_LENGTH-1] = 1; // first (and only) essence container
808 result = m_State.Goto_READY();
814 // Writes a frame of essence to the MXF file. If the optional AESEncContext
815 // argument is present, the essence is encrypted prior to writing.
816 // Fails if the file is not open, is finalized, or an operating system
820 lh__Writer::WriteFrame(const JP2K::FrameBuffer& FrameBuf, bool add_index,
821 AESEncContext* Ctx, HMACContext* HMAC)
823 Result_t result = RESULT_OK;
825 if ( m_State.Test_READY() )
826 result = m_State.Goto_RUNNING(); // first time through
828 ui64_t StreamOffset = m_StreamOffset;
830 if ( ASDCP_SUCCESS(result) )
831 result = WriteEKLVPacket(FrameBuf, m_EssenceUL, Ctx, HMAC);
833 if ( ASDCP_SUCCESS(result) && add_index )
835 IndexTableSegment::IndexEntry Entry;
836 Entry.StreamOffset = StreamOffset;
837 m_FooterPart.PushIndexEntry(Entry);
845 // Closes the MXF file, writing the index and other closing information.
848 lh__Writer::Finalize()
850 if ( ! m_State.Test_RUNNING() )
853 m_State.Goto_FINAL();
855 return WriteMXFFooter();
860 class ASDCP::JP2K::MXFWriter::h__Writer : public lh__Writer
865 //------------------------------------------------------------------------------------------
869 ASDCP::JP2K::MXFWriter::MXFWriter()
873 ASDCP::JP2K::MXFWriter::~MXFWriter()
878 // Open the file for writing. The file must not exist. Returns error if
879 // the operation cannot be completed.
881 ASDCP::JP2K::MXFWriter::OpenWrite(const char* filename, const WriterInfo& Info,
882 const PictureDescriptor& PDesc, ui32_t HeaderSize)
884 m_Writer = new h__Writer;
885 m_Writer->m_Info = Info;
887 Result_t result = m_Writer->OpenWrite(filename, ASDCP::ESS_JPEG_2000, HeaderSize);
889 if ( ASDCP_SUCCESS(result) )
890 result = m_Writer->SetSourceStream(PDesc, JP2K_PACKAGE_LABEL);
892 if ( ASDCP_FAILURE(result) )
899 // Writes a frame of essence to the MXF file. If the optional AESEncContext
900 // argument is present, the essence is encrypted prior to writing.
901 // Fails if the file is not open, is finalized, or an operating system
904 ASDCP::JP2K::MXFWriter::WriteFrame(const FrameBuffer& FrameBuf, AESEncContext* Ctx, HMACContext* HMAC)
906 if ( m_Writer.empty() )
909 return m_Writer->WriteFrame(FrameBuf, true, Ctx, HMAC);
912 // Closes the MXF file, writing the index and other closing information.
914 ASDCP::JP2K::MXFWriter::Finalize()
916 if ( m_Writer.empty() )
919 return m_Writer->Finalize();
923 //------------------------------------------------------------------------------------------
927 class ASDCP::JP2K::MXFSWriter::h__SWriter : public lh__Writer
929 StereoscopicPhase_t m_NextPhase;
932 h__SWriter() : m_NextPhase(SP_LEFT) {}
935 Result_t WriteFrame(const FrameBuffer& FrameBuf, StereoscopicPhase_t phase,
936 AESEncContext* Ctx, HMACContext* HMAC)
938 if ( m_NextPhase != phase )
939 return RESULT_SPHASE;
941 if ( phase == SP_LEFT )
943 m_NextPhase = SP_RIGHT;
944 return lh__Writer::WriteFrame(FrameBuf, true, Ctx, HMAC);
947 m_NextPhase = SP_LEFT;
948 return lh__Writer::WriteFrame(FrameBuf, false, Ctx, HMAC);
954 if ( m_NextPhase != SP_LEFT )
955 return RESULT_SPHASE;
957 assert( m_FramesWritten % 2 == 0 );
958 m_FramesWritten /= 2;
959 return lh__Writer::Finalize();
965 ASDCP::JP2K::MXFSWriter::MXFSWriter()
969 ASDCP::JP2K::MXFSWriter::~MXFSWriter()
974 // Open the file for writing. The file must not exist. Returns error if
975 // the operation cannot be completed.
977 ASDCP::JP2K::MXFSWriter::OpenWrite(const char* filename, const WriterInfo& Info,
978 const PictureDescriptor& PDesc, ui32_t HeaderSize)
980 m_Writer = new h__SWriter;
982 if ( PDesc.EditRate != ASDCP::EditRate_24 )
984 DefaultLogSink().Error("Stereoscopic wrapping requires 24 fps input streams.\n");
985 return RESULT_FORMAT;
988 if ( PDesc.StoredWidth > 2048 )
989 DefaultLogSink().Warn("Wrapping non-standard 4K stereoscopic content. I hope you know what you are doing!\n");
991 m_Writer->m_Info = Info;
993 Result_t result = m_Writer->OpenWrite(filename, ASDCP::ESS_JPEG_2000_S, HeaderSize);
995 if ( ASDCP_SUCCESS(result) )
997 PictureDescriptor TmpPDesc = PDesc;
998 TmpPDesc.EditRate = ASDCP::EditRate_48;
1000 result = m_Writer->SetSourceStream(TmpPDesc, JP2K_S_PACKAGE_LABEL, ASDCP::EditRate_24);
1003 if ( ASDCP_FAILURE(result) )
1010 ASDCP::JP2K::MXFSWriter::WriteFrame(const SFrameBuffer& FrameBuf, AESEncContext* Ctx, HMACContext* HMAC)
1012 if ( m_Writer.empty() )
1015 Result_t result = m_Writer->WriteFrame(FrameBuf.Left, SP_LEFT, Ctx, HMAC);
1017 if ( ASDCP_SUCCESS(result) )
1018 result = m_Writer->WriteFrame(FrameBuf.Right, SP_RIGHT, Ctx, HMAC);
1023 // Writes a frame of essence to the MXF file. If the optional AESEncContext
1024 // argument is present, the essence is encrypted prior to writing.
1025 // Fails if the file is not open, is finalized, or an operating system
1028 ASDCP::JP2K::MXFSWriter::WriteFrame(const FrameBuffer& FrameBuf, StereoscopicPhase_t phase,
1029 AESEncContext* Ctx, HMACContext* HMAC)
1031 if ( m_Writer.empty() )
1034 return m_Writer->WriteFrame(FrameBuf, phase, Ctx, HMAC);
1037 // Closes the MXF file, writing the index and other closing information.
1039 ASDCP::JP2K::MXFSWriter::Finalize()
1041 if ( m_Writer.empty() )
1044 return m_Writer->Finalize();
1048 // end AS_DCP_JP2K.cpp