2 Copyright (c) 2004-2013, 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 < MaxComponents; ++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 < MaxPrecincts; ++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 < MaxComponents; 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 < MaxPrecincts; 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 const int VideoLineMapSize = 16; // See SMPTE 377M D.2.1
204 const int PixelLayoutSize = 8*2; // See SMPTE 377M D.2.3
205 static const byte_t s_PixelLayoutXYZ[PixelLayoutSize] = { 0xd8, 0x0c, 0xd9, 0x0c, 0xda, 0x0c, 0x00 };
209 ASDCP::JP2K_PDesc_to_MD(const JP2K::PictureDescriptor& PDesc,
210 const ASDCP::Dictionary& dict,
211 ASDCP::MXF::GenericPictureEssenceDescriptor& EssenceDescriptor,
212 ASDCP::MXF::JPEG2000PictureSubDescriptor& EssenceSubDescriptor)
214 EssenceDescriptor.ContainerDuration = PDesc.ContainerDuration;
215 EssenceDescriptor.SampleRate = PDesc.EditRate;
216 EssenceDescriptor.FrameLayout = 0;
217 EssenceDescriptor.StoredWidth = PDesc.StoredWidth;
218 EssenceDescriptor.StoredHeight = PDesc.StoredHeight;
219 EssenceDescriptor.AspectRatio = PDesc.AspectRatio;
221 EssenceSubDescriptor.Rsize = PDesc.Rsize;
222 EssenceSubDescriptor.Xsize = PDesc.Xsize;
223 EssenceSubDescriptor.Ysize = PDesc.Ysize;
224 EssenceSubDescriptor.XOsize = PDesc.XOsize;
225 EssenceSubDescriptor.YOsize = PDesc.YOsize;
226 EssenceSubDescriptor.XTsize = PDesc.XTsize;
227 EssenceSubDescriptor.YTsize = PDesc.YTsize;
228 EssenceSubDescriptor.XTOsize = PDesc.XTOsize;
229 EssenceSubDescriptor.YTOsize = PDesc.YTOsize;
230 EssenceSubDescriptor.Csize = PDesc.Csize;
232 const ui32_t tmp_buffer_len = 1024;
233 byte_t tmp_buffer[tmp_buffer_len];
235 *(ui32_t*)tmp_buffer = KM_i32_BE(MaxComponents); // three components
236 *(ui32_t*)(tmp_buffer+4) = KM_i32_BE(sizeof(ASDCP::JP2K::ImageComponent_t));
237 memcpy(tmp_buffer + 8, &PDesc.ImageComponents, sizeof(ASDCP::JP2K::ImageComponent_t) * MaxComponents);
239 const ui32_t pcomp_size = (sizeof(ui32_t) * 2) + (sizeof(ASDCP::JP2K::ImageComponent_t) * MaxComponents);
240 memcpy(EssenceSubDescriptor.PictureComponentSizing.get().Data(), tmp_buffer, pcomp_size);
241 EssenceSubDescriptor.PictureComponentSizing.get().Length(pcomp_size);
242 EssenceSubDescriptor.PictureComponentSizing.set_has_value();
244 ui32_t precinct_set_size = 0;
245 for ( ui32_t i = 0; PDesc.CodingStyleDefault.SPcod.PrecinctSize[i] != 0 && i < MaxPrecincts; ++i )
248 ui32_t csd_size = sizeof(CodingStyleDefault_t) - MaxPrecincts + precinct_set_size;
249 memcpy(EssenceSubDescriptor.CodingStyleDefault.get().Data(), &PDesc.CodingStyleDefault, csd_size);
250 EssenceSubDescriptor.CodingStyleDefault.get().Length(csd_size);
251 EssenceSubDescriptor.CodingStyleDefault.set_has_value();
253 ui32_t qdflt_size = PDesc.QuantizationDefault.SPqcdLength + 1;
254 memcpy(EssenceSubDescriptor.QuantizationDefault.get().Data(), &PDesc.QuantizationDefault, qdflt_size);
255 EssenceSubDescriptor.QuantizationDefault.get().Length(qdflt_size);
256 EssenceSubDescriptor.QuantizationDefault.set_has_value();
264 ASDCP::MD_to_JP2K_PDesc(const ASDCP::MXF::GenericPictureEssenceDescriptor& EssenceDescriptor,
265 const ASDCP::MXF::JPEG2000PictureSubDescriptor& EssenceSubDescriptor,
266 const ASDCP::Rational& EditRate, const ASDCP::Rational& SampleRate,
267 ASDCP::JP2K::PictureDescriptor& PDesc)
269 memset(&PDesc, 0, sizeof(PDesc));
271 PDesc.EditRate = EditRate;
272 PDesc.SampleRate = SampleRate;
273 assert(EssenceDescriptor.ContainerDuration.const_get() <= 0xFFFFFFFFL);
274 PDesc.ContainerDuration = static_cast<ui32_t>(EssenceDescriptor.ContainerDuration.const_get());
275 PDesc.StoredWidth = EssenceDescriptor.StoredWidth;
276 PDesc.StoredHeight = EssenceDescriptor.StoredHeight;
277 PDesc.AspectRatio = EssenceDescriptor.AspectRatio;
279 PDesc.Rsize = EssenceSubDescriptor.Rsize;
280 PDesc.Xsize = EssenceSubDescriptor.Xsize;
281 PDesc.Ysize = EssenceSubDescriptor.Ysize;
282 PDesc.XOsize = EssenceSubDescriptor.XOsize;
283 PDesc.YOsize = EssenceSubDescriptor.YOsize;
284 PDesc.XTsize = EssenceSubDescriptor.XTsize;
285 PDesc.YTsize = EssenceSubDescriptor.YTsize;
286 PDesc.XTOsize = EssenceSubDescriptor.XTOsize;
287 PDesc.YTOsize = EssenceSubDescriptor.YTOsize;
288 PDesc.Csize = EssenceSubDescriptor.Csize;
290 // PictureComponentSizing
291 ui32_t tmp_size = EssenceSubDescriptor.PictureComponentSizing.const_get().Length();
293 if ( tmp_size == 17 ) // ( 2 * sizeof(ui32_t) ) + 3 components * 3 byte each
295 memcpy(&PDesc.ImageComponents, EssenceSubDescriptor.PictureComponentSizing.const_get().RoData() + 8, tmp_size - 8);
299 DefaultLogSink().Error("Unexpected PictureComponentSizing size: %u, should be 17\n", tmp_size);
302 // CodingStyleDefault
303 memset(&PDesc.CodingStyleDefault, 0, sizeof(CodingStyleDefault_t));
304 memcpy(&PDesc.CodingStyleDefault,
305 EssenceSubDescriptor.CodingStyleDefault.const_get().RoData(),
306 EssenceSubDescriptor.CodingStyleDefault.const_get().Length());
308 // QuantizationDefault
309 memset(&PDesc.QuantizationDefault, 0, sizeof(QuantizationDefault_t));
310 memcpy(&PDesc.QuantizationDefault,
311 EssenceSubDescriptor.QuantizationDefault.const_get().RoData(),
312 EssenceSubDescriptor.QuantizationDefault.const_get().Length());
314 PDesc.QuantizationDefault.SPqcdLength = EssenceSubDescriptor.QuantizationDefault.const_get().Length() - 1;
319 //------------------------------------------------------------------------------------------
321 // hidden, internal implementation of JPEG 2000 reader
324 class lh__Reader : public ASDCP::h__ASDCPReader
326 RGBAEssenceDescriptor* m_EssenceDescriptor;
327 JPEG2000PictureSubDescriptor* m_EssenceSubDescriptor;
328 ASDCP::Rational m_EditRate;
329 ASDCP::Rational m_SampleRate;
330 EssenceType_t m_Format;
332 ASDCP_NO_COPY_CONSTRUCT(lh__Reader);
335 PictureDescriptor m_PDesc; // codestream parameter list
337 lh__Reader(const Dictionary& d) :
338 ASDCP::h__ASDCPReader(d), m_EssenceDescriptor(0), m_EssenceSubDescriptor(0), m_Format(ESS_UNKNOWN) {}
340 virtual ~lh__Reader() {}
342 Result_t OpenRead(const std::string&, EssenceType_t);
343 Result_t ReadFrame(ui32_t, JP2K::FrameBuffer&, AESDecContext*, HMACContext*);
350 lh__Reader::OpenRead(const std::string& filename, EssenceType_t type)
352 Result_t result = OpenMXFRead(filename);
354 if( ASDCP_SUCCESS(result) )
356 InterchangeObject* tmp_iobj = 0;
357 m_HeaderPart.GetMDObjectByType(OBJ_TYPE_ARGS(RGBAEssenceDescriptor), &tmp_iobj);
358 m_EssenceDescriptor = static_cast<RGBAEssenceDescriptor*>(tmp_iobj);
360 if ( m_EssenceDescriptor == 0 )
362 DefaultLogSink().Error("RGBAEssenceDescriptor object not found.\n");
363 return RESULT_FORMAT;
366 m_HeaderPart.GetMDObjectByType(OBJ_TYPE_ARGS(JPEG2000PictureSubDescriptor), &tmp_iobj);
367 m_EssenceSubDescriptor = static_cast<JPEG2000PictureSubDescriptor*>(tmp_iobj);
369 if ( m_EssenceSubDescriptor == 0 )
371 m_EssenceDescriptor = 0;
372 DefaultLogSink().Error("JPEG2000PictureSubDescriptor object not found.\n");
373 return RESULT_FORMAT;
376 std::list<InterchangeObject*> ObjectList;
377 m_HeaderPart.GetMDObjectsByType(OBJ_TYPE_ARGS(Track), ObjectList);
379 if ( ObjectList.empty() )
381 DefaultLogSink().Error("MXF Metadata contains no Track Sets.\n");
382 return RESULT_FORMAT;
385 m_EditRate = ((Track*)ObjectList.front())->EditRate;
386 m_SampleRate = m_EssenceDescriptor->SampleRate;
388 if ( type == ASDCP::ESS_JPEG_2000 )
390 if ( m_EditRate != m_SampleRate )
392 DefaultLogSink().Warn("EditRate and SampleRate do not match (%.03f, %.03f).\n",
393 m_EditRate.Quotient(), m_SampleRate.Quotient());
395 if ( m_EditRate == EditRate_24 && m_SampleRate == EditRate_48 ||
396 m_EditRate == EditRate_25 && m_SampleRate == EditRate_50 ||
397 m_EditRate == EditRate_30 && m_SampleRate == EditRate_60 ||
398 m_EditRate == EditRate_48 && m_SampleRate == EditRate_96 ||
399 m_EditRate == EditRate_50 && m_SampleRate == EditRate_100 ||
400 m_EditRate == EditRate_60 && m_SampleRate == EditRate_120 )
402 DefaultLogSink().Debug("File may contain JPEG Interop stereoscopic images.\n");
403 return RESULT_SFORMAT;
406 return RESULT_FORMAT;
409 else if ( type == ASDCP::ESS_JPEG_2000_S )
411 if ( m_EditRate == EditRate_24 )
413 if ( m_SampleRate != EditRate_48 )
415 DefaultLogSink().Error("EditRate and SampleRate not correct for 24/48 stereoscopic essence.\n");
416 return RESULT_FORMAT;
419 else if ( m_EditRate == EditRate_25 )
421 if ( m_SampleRate != EditRate_50 )
423 DefaultLogSink().Error("EditRate and SampleRate not correct for 25/50 stereoscopic essence.\n");
424 return RESULT_FORMAT;
427 else if ( m_EditRate == EditRate_30 )
429 if ( m_SampleRate != EditRate_60 )
431 DefaultLogSink().Error("EditRate and SampleRate not correct for 30/60 stereoscopic essence.\n");
432 return RESULT_FORMAT;
435 else if ( m_EditRate == EditRate_48 )
437 if ( m_SampleRate != EditRate_96 )
439 DefaultLogSink().Error("EditRate and SampleRate not correct for 48/96 stereoscopic essence.\n");
440 return RESULT_FORMAT;
443 else if ( m_EditRate == EditRate_50 )
445 if ( m_SampleRate != EditRate_100 )
447 DefaultLogSink().Error("EditRate and SampleRate not correct for 50/100 stereoscopic essence.\n");
448 return RESULT_FORMAT;
451 else if ( m_EditRate == EditRate_60 )
453 if ( m_SampleRate != EditRate_120 )
455 DefaultLogSink().Error("EditRate and SampleRate not correct for 60/120 stereoscopic essence.\n");
456 return RESULT_FORMAT;
461 DefaultLogSink().Error("EditRate not correct for stereoscopic essence: %d/%d.\n",
462 m_EditRate.Numerator, m_EditRate.Denominator);
463 return RESULT_FORMAT;
468 DefaultLogSink().Error("'type' argument unexpected: %x\n", type);
472 result = MD_to_JP2K_PDesc(*m_EssenceDescriptor, *m_EssenceSubDescriptor, m_EditRate, m_SampleRate, m_PDesc);
481 lh__Reader::ReadFrame(ui32_t FrameNum, JP2K::FrameBuffer& FrameBuf,
482 AESDecContext* Ctx, HMACContext* HMAC)
484 if ( ! m_File.IsOpen() )
488 return ReadEKLVFrame(FrameNum, FrameBuf, m_Dict->ul(MDD_JPEG2000Essence), Ctx, HMAC);
493 class ASDCP::JP2K::MXFReader::h__Reader : public lh__Reader
495 ASDCP_NO_COPY_CONSTRUCT(h__Reader);
499 h__Reader(const Dictionary& d) : lh__Reader(d) {}
504 //------------------------------------------------------------------------------------------
509 ASDCP::JP2K::FrameBuffer::Dump(FILE* stream, ui32_t dump_len) const
514 fprintf(stream, "Frame: %06u, %7u bytes", m_FrameNumber, m_Size);
519 Kumu::hexdump(m_Data, dump_len, stream);
523 //------------------------------------------------------------------------------------------
525 ASDCP::JP2K::MXFReader::MXFReader()
527 m_Reader = new h__Reader(DefaultCompositeDict());
531 ASDCP::JP2K::MXFReader::~MXFReader()
533 if ( m_Reader && m_Reader->m_File.IsOpen() )
537 // Warning: direct manipulation of MXF structures can interfere
538 // with the normal operation of the wrapper. Caveat emptor!
540 ASDCP::MXF::OP1aHeader&
541 ASDCP::JP2K::MXFReader::OP1aHeader()
543 if ( m_Reader.empty() )
545 assert(g_OP1aHeader);
546 return *g_OP1aHeader;
549 return m_Reader->m_HeaderPart;
552 // Warning: direct manipulation of MXF structures can interfere
553 // with the normal operation of the wrapper. Caveat emptor!
555 ASDCP::MXF::OPAtomIndexFooter&
556 ASDCP::JP2K::MXFReader::OPAtomIndexFooter()
558 if ( m_Reader.empty() )
560 assert(g_OPAtomIndexFooter);
561 return *g_OPAtomIndexFooter;
564 return m_Reader->m_IndexAccess;
567 // Warning: direct manipulation of MXF structures can interfere
568 // with the normal operation of the wrapper. Caveat emptor!
571 ASDCP::JP2K::MXFReader::RIP()
573 if ( m_Reader.empty() )
579 return m_Reader->m_RIP;
582 // Open the file for reading. The file must exist. Returns error if the
583 // operation cannot be completed.
585 ASDCP::JP2K::MXFReader::OpenRead(const std::string& filename) const
587 return m_Reader->OpenRead(filename, ASDCP::ESS_JPEG_2000);
592 ASDCP::JP2K::MXFReader::ReadFrame(ui32_t FrameNum, FrameBuffer& FrameBuf,
593 AESDecContext* Ctx, HMACContext* HMAC) const
595 if ( m_Reader && m_Reader->m_File.IsOpen() )
596 return m_Reader->ReadFrame(FrameNum, FrameBuf, Ctx, HMAC);
602 ASDCP::JP2K::MXFReader::LocateFrame(ui32_t FrameNum, Kumu::fpos_t& streamOffset, i8_t& temporalOffset, i8_t& keyFrameOffset) const
604 return m_Reader->LocateFrame(FrameNum, streamOffset, temporalOffset, keyFrameOffset);
608 // Fill the struct with the values from the file's header.
609 // Returns RESULT_INIT if the file is not open.
611 ASDCP::JP2K::MXFReader::FillPictureDescriptor(PictureDescriptor& PDesc) const
613 if ( m_Reader && m_Reader->m_File.IsOpen() )
615 PDesc = m_Reader->m_PDesc;
623 // Fill the struct with the values from the file's header.
624 // Returns RESULT_INIT if the file is not open.
626 ASDCP::JP2K::MXFReader::FillWriterInfo(WriterInfo& Info) const
628 if ( m_Reader && m_Reader->m_File.IsOpen() )
630 Info = m_Reader->m_Info;
639 ASDCP::JP2K::MXFReader::DumpHeaderMetadata(FILE* stream) const
641 if ( m_Reader->m_File.IsOpen() )
642 m_Reader->m_HeaderPart.Dump(stream);
648 ASDCP::JP2K::MXFReader::DumpIndex(FILE* stream) const
650 if ( m_Reader->m_File.IsOpen() )
651 m_Reader->m_IndexAccess.Dump(stream);
656 ASDCP::JP2K::MXFReader::Close() const
658 if ( m_Reader && m_Reader->m_File.IsOpen() )
668 //------------------------------------------------------------------------------------------
671 class ASDCP::JP2K::MXFSReader::h__SReader : public lh__Reader
673 ui32_t m_StereoFrameReady;
676 h__SReader(const Dictionary& d) : lh__Reader(d), m_StereoFrameReady(0xffffffff) {}
679 Result_t ReadFrame(ui32_t FrameNum, StereoscopicPhase_t phase, FrameBuffer& FrameBuf,
680 AESDecContext* Ctx, HMACContext* HMAC)
682 // look up frame index node
683 IndexTableSegment::IndexEntry TmpEntry;
685 if ( ASDCP_FAILURE(m_IndexAccess.Lookup(FrameNum, TmpEntry)) )
687 DefaultLogSink().Error("Frame value out of range: %u\n", FrameNum);
691 // get frame position
692 Kumu::fpos_t FilePosition = m_HeaderPart.BodyOffset + TmpEntry.StreamOffset;
693 Result_t result = RESULT_OK;
695 if ( phase == SP_LEFT )
697 if ( FilePosition != m_LastPosition )
699 m_LastPosition = FilePosition;
700 result = m_File.Seek(FilePosition);
703 // the call to ReadEKLVPacket() will leave the file on an R frame
704 m_StereoFrameReady = FrameNum;
706 else if ( phase == SP_RIGHT )
708 if ( m_StereoFrameReady != FrameNum )
710 // the file is not already positioned, we must do some work
711 // seek to the companion SP_LEFT frame and read the frame's key and length
712 if ( FilePosition != m_LastPosition )
714 m_LastPosition = FilePosition;
715 result = m_File.Seek(FilePosition);
719 result = Reader.ReadKLFromFile(m_File);
721 if ( ASDCP_SUCCESS(result) )
723 // skip over the companion SP_LEFT frame
724 Kumu::fpos_t new_pos = FilePosition + SMPTE_UL_LENGTH + Reader.KLLength() + Reader.Length();
725 result = m_File.Seek(new_pos);
729 // the call to ReadEKLVPacket() will leave the file not on an R frame
730 m_StereoFrameReady = 0xffffffff;
734 DefaultLogSink().Error("Unexpected stereoscopic phase value: %u\n", phase);
738 if( ASDCP_SUCCESS(result) )
740 ui32_t SequenceNum = FrameNum * 2;
741 SequenceNum += ( phase == SP_RIGHT ) ? 2 : 1;
743 result = ReadEKLVPacket(FrameNum, SequenceNum, FrameBuf, m_Dict->ul(MDD_JPEG2000Essence), Ctx, HMAC);
752 ASDCP::JP2K::MXFSReader::MXFSReader()
754 m_Reader = new h__SReader(DefaultCompositeDict());
758 ASDCP::JP2K::MXFSReader::~MXFSReader()
760 if ( m_Reader && m_Reader->m_File.IsOpen() )
764 // Warning: direct manipulation of MXF structures can interfere
765 // with the normal operation of the wrapper. Caveat emptor!
767 ASDCP::MXF::OP1aHeader&
768 ASDCP::JP2K::MXFSReader::OP1aHeader()
770 if ( m_Reader.empty() )
772 assert(g_OP1aHeader);
773 return *g_OP1aHeader;
776 return m_Reader->m_HeaderPart;
779 // Warning: direct manipulation of MXF structures can interfere
780 // with the normal operation of the wrapper. Caveat emptor!
782 ASDCP::MXF::OPAtomIndexFooter&
783 ASDCP::JP2K::MXFSReader::OPAtomIndexFooter()
785 if ( m_Reader.empty() )
787 assert(g_OPAtomIndexFooter);
788 return *g_OPAtomIndexFooter;
791 return m_Reader->m_IndexAccess;
794 // Warning: direct manipulation of MXF structures can interfere
795 // with the normal operation of the wrapper. Caveat emptor!
798 ASDCP::JP2K::MXFSReader::RIP()
800 if ( m_Reader.empty() )
806 return m_Reader->m_RIP;
809 // Open the file for reading. The file must exist. Returns error if the
810 // operation cannot be completed.
812 ASDCP::JP2K::MXFSReader::OpenRead(const std::string& filename) const
814 return m_Reader->OpenRead(filename, ASDCP::ESS_JPEG_2000_S);
819 ASDCP::JP2K::MXFSReader::ReadFrame(ui32_t FrameNum, SFrameBuffer& FrameBuf, AESDecContext* Ctx, HMACContext* HMAC) const
821 Result_t result = RESULT_INIT;
823 if ( m_Reader && m_Reader->m_File.IsOpen() )
825 result = m_Reader->ReadFrame(FrameNum, SP_LEFT, FrameBuf.Left, Ctx, HMAC);
827 if ( ASDCP_SUCCESS(result) )
828 result = m_Reader->ReadFrame(FrameNum, SP_RIGHT, FrameBuf.Right, Ctx, HMAC);
836 ASDCP::JP2K::MXFSReader::ReadFrame(ui32_t FrameNum, StereoscopicPhase_t phase, FrameBuffer& FrameBuf,
837 AESDecContext* Ctx, HMACContext* HMAC) const
839 if ( m_Reader && m_Reader->m_File.IsOpen() )
840 return m_Reader->ReadFrame(FrameNum, phase, FrameBuf, Ctx, HMAC);
846 ASDCP::JP2K::MXFSReader::LocateFrame(ui32_t FrameNum, Kumu::fpos_t& streamOffset, i8_t& temporalOffset, i8_t& keyFrameOffset) const
848 return m_Reader->LocateFrame(FrameNum, streamOffset, temporalOffset, keyFrameOffset);
851 // Fill the struct with the values from the file's header.
852 // Returns RESULT_INIT if the file is not open.
854 ASDCP::JP2K::MXFSReader::FillPictureDescriptor(PictureDescriptor& PDesc) const
856 if ( m_Reader && m_Reader->m_File.IsOpen() )
858 PDesc = m_Reader->m_PDesc;
866 // Fill the struct with the values from the file's header.
867 // Returns RESULT_INIT if the file is not open.
869 ASDCP::JP2K::MXFSReader::FillWriterInfo(WriterInfo& Info) const
871 if ( m_Reader && m_Reader->m_File.IsOpen() )
873 Info = m_Reader->m_Info;
882 ASDCP::JP2K::MXFSReader::DumpHeaderMetadata(FILE* stream) const
884 if ( m_Reader->m_File.IsOpen() )
885 m_Reader->m_HeaderPart.Dump(stream);
891 ASDCP::JP2K::MXFSReader::DumpIndex(FILE* stream) const
893 if ( m_Reader->m_File.IsOpen() )
894 m_Reader->m_IndexAccess.Dump(stream);
899 ASDCP::JP2K::MXFSReader::Close() const
901 if ( m_Reader && m_Reader->m_File.IsOpen() )
911 //------------------------------------------------------------------------------------------
915 class lh__Writer : public ASDCP::h__ASDCPWriter
917 ASDCP_NO_COPY_CONSTRUCT(lh__Writer);
920 JPEG2000PictureSubDescriptor* m_EssenceSubDescriptor;
923 PictureDescriptor m_PDesc;
924 byte_t m_EssenceUL[SMPTE_UL_LENGTH];
926 lh__Writer(const Dictionary& d) : ASDCP::h__ASDCPWriter(d), m_EssenceSubDescriptor(0) {
927 memset(m_EssenceUL, 0, SMPTE_UL_LENGTH);
930 virtual ~lh__Writer(){}
932 Result_t OpenWrite(const std::string&, EssenceType_t type, ui32_t HeaderSize);
933 Result_t SetSourceStream(const PictureDescriptor&, const std::string& label,
934 ASDCP::Rational LocalEditRate = ASDCP::Rational(0,0));
935 Result_t WriteFrame(const JP2K::FrameBuffer&, bool add_index, AESEncContext*, HMACContext*);
939 // Open the file for writing. The file must not exist. Returns error if
940 // the operation cannot be completed.
942 lh__Writer::OpenWrite(const std::string& filename, EssenceType_t type, ui32_t HeaderSize)
944 if ( ! m_State.Test_BEGIN() )
947 Result_t result = m_File.OpenWrite(filename);
949 if ( ASDCP_SUCCESS(result) )
951 m_HeaderSize = HeaderSize;
952 RGBAEssenceDescriptor* tmp_rgba = new RGBAEssenceDescriptor(m_Dict);
953 tmp_rgba->ComponentMaxRef = 4095;
954 tmp_rgba->ComponentMinRef = 0;
956 m_EssenceDescriptor = tmp_rgba;
957 m_EssenceSubDescriptor = new JPEG2000PictureSubDescriptor(m_Dict);
958 m_EssenceSubDescriptorList.push_back((InterchangeObject*)m_EssenceSubDescriptor);
960 GenRandomValue(m_EssenceSubDescriptor->InstanceUID);
961 m_EssenceDescriptor->SubDescriptors.push_back(m_EssenceSubDescriptor->InstanceUID);
963 if ( type == ASDCP::ESS_JPEG_2000_S && m_Info.LabelSetType == LS_MXF_SMPTE )
965 InterchangeObject* StereoSubDesc = new StereoscopicPictureSubDescriptor(m_Dict);
966 m_EssenceSubDescriptorList.push_back(StereoSubDesc);
967 GenRandomValue(StereoSubDesc->InstanceUID);
968 m_EssenceDescriptor->SubDescriptors.push_back(StereoSubDesc->InstanceUID);
971 result = m_State.Goto_INIT();
977 // Automatically sets the MXF file's metadata from the first jpeg codestream stream.
979 lh__Writer::SetSourceStream(const PictureDescriptor& PDesc, const std::string& label, ASDCP::Rational LocalEditRate)
982 if ( ! m_State.Test_INIT() )
985 if ( LocalEditRate == ASDCP::Rational(0,0) )
986 LocalEditRate = PDesc.EditRate;
990 assert(m_EssenceDescriptor);
991 assert(m_EssenceSubDescriptor);
992 Result_t result = JP2K_PDesc_to_MD(m_PDesc, *m_Dict,
993 *static_cast<ASDCP::MXF::GenericPictureEssenceDescriptor*>(m_EssenceDescriptor),
994 *m_EssenceSubDescriptor);
996 if ( ASDCP_SUCCESS(result) )
998 if ( PDesc.StoredWidth < 2049 )
1000 static_cast<ASDCP::MXF::RGBAEssenceDescriptor*>(m_EssenceDescriptor)->PictureEssenceCoding.Set(m_Dict->ul(MDD_JP2KEssenceCompression_2K));
1001 m_EssenceSubDescriptor->Rsize = 3;
1005 static_cast<ASDCP::MXF::RGBAEssenceDescriptor*>(m_EssenceDescriptor)->PictureEssenceCoding.Set(m_Dict->ul(MDD_JP2KEssenceCompression_4K));
1006 m_EssenceSubDescriptor->Rsize = 4;
1009 memcpy(m_EssenceUL, m_Dict->ul(MDD_JPEG2000Essence), SMPTE_UL_LENGTH);
1010 m_EssenceUL[SMPTE_UL_LENGTH-1] = 1; // first (and only) essence container
1011 result = m_State.Goto_READY();
1014 if ( ASDCP_SUCCESS(result) )
1016 result = WriteASDCPHeader(label, UL(m_Dict->ul(MDD_JPEG_2000WrappingFrame)),
1017 PICT_DEF_LABEL, UL(m_EssenceUL), UL(m_Dict->ul(MDD_PictureDataDef)),
1018 LocalEditRate, derive_timecode_rate_from_edit_rate(m_PDesc.EditRate));
1024 // Writes a frame of essence to the MXF file. If the optional AESEncContext
1025 // argument is present, the essence is encrypted prior to writing.
1026 // Fails if the file is not open, is finalized, or an operating system
1030 lh__Writer::WriteFrame(const JP2K::FrameBuffer& FrameBuf, bool add_index,
1031 AESEncContext* Ctx, HMACContext* HMAC)
1033 Result_t result = RESULT_OK;
1035 if ( m_State.Test_READY() )
1036 result = m_State.Goto_RUNNING(); // first time through
1038 ui64_t StreamOffset = m_StreamOffset;
1040 if ( ASDCP_SUCCESS(result) )
1041 result = WriteEKLVPacket(FrameBuf, m_EssenceUL, Ctx, HMAC);
1043 if ( ASDCP_SUCCESS(result) && add_index )
1045 IndexTableSegment::IndexEntry Entry;
1046 Entry.StreamOffset = StreamOffset;
1047 m_FooterPart.PushIndexEntry(Entry);
1055 // Closes the MXF file, writing the index and other closing information.
1058 lh__Writer::Finalize()
1060 if ( ! m_State.Test_RUNNING() )
1061 return RESULT_STATE;
1063 m_State.Goto_FINAL();
1065 return WriteASDCPFooter();
1070 class ASDCP::JP2K::MXFWriter::h__Writer : public lh__Writer
1072 ASDCP_NO_COPY_CONSTRUCT(h__Writer);
1076 h__Writer(const Dictionary& d) : lh__Writer(d) {}
1080 //------------------------------------------------------------------------------------------
1084 ASDCP::JP2K::MXFWriter::MXFWriter()
1088 ASDCP::JP2K::MXFWriter::~MXFWriter()
1092 // Warning: direct manipulation of MXF structures can interfere
1093 // with the normal operation of the wrapper. Caveat emptor!
1095 ASDCP::MXF::OP1aHeader&
1096 ASDCP::JP2K::MXFWriter::OP1aHeader()
1098 if ( m_Writer.empty() )
1100 assert(g_OP1aHeader);
1101 return *g_OP1aHeader;
1104 return m_Writer->m_HeaderPart;
1107 // Warning: direct manipulation of MXF structures can interfere
1108 // with the normal operation of the wrapper. Caveat emptor!
1110 ASDCP::MXF::OPAtomIndexFooter&
1111 ASDCP::JP2K::MXFWriter::OPAtomIndexFooter()
1113 if ( m_Writer.empty() )
1115 assert(g_OPAtomIndexFooter);
1116 return *g_OPAtomIndexFooter;
1119 return m_Writer->m_FooterPart;
1122 // Warning: direct manipulation of MXF structures can interfere
1123 // with the normal operation of the wrapper. Caveat emptor!
1126 ASDCP::JP2K::MXFWriter::RIP()
1128 if ( m_Writer.empty() )
1134 return m_Writer->m_RIP;
1137 // Open the file for writing. The file must not exist. Returns error if
1138 // the operation cannot be completed.
1140 ASDCP::JP2K::MXFWriter::OpenWrite(const std::string& filename, const WriterInfo& Info,
1141 const PictureDescriptor& PDesc, ui32_t HeaderSize)
1143 if ( Info.LabelSetType == LS_MXF_SMPTE )
1144 m_Writer = new h__Writer(DefaultSMPTEDict());
1146 m_Writer = new h__Writer(DefaultInteropDict());
1148 m_Writer->m_Info = Info;
1150 Result_t result = m_Writer->OpenWrite(filename, ASDCP::ESS_JPEG_2000, HeaderSize);
1152 if ( ASDCP_SUCCESS(result) )
1153 result = m_Writer->SetSourceStream(PDesc, JP2K_PACKAGE_LABEL);
1155 if ( ASDCP_FAILURE(result) )
1162 // Writes a frame of essence to the MXF file. If the optional AESEncContext
1163 // argument is present, the essence is encrypted prior to writing.
1164 // Fails if the file is not open, is finalized, or an operating system
1167 ASDCP::JP2K::MXFWriter::WriteFrame(const FrameBuffer& FrameBuf, AESEncContext* Ctx, HMACContext* HMAC)
1169 if ( m_Writer.empty() )
1172 return m_Writer->WriteFrame(FrameBuf, true, Ctx, HMAC);
1175 // Closes the MXF file, writing the index and other closing information.
1177 ASDCP::JP2K::MXFWriter::Finalize()
1179 if ( m_Writer.empty() )
1182 return m_Writer->Finalize();
1186 //------------------------------------------------------------------------------------------
1190 class ASDCP::JP2K::MXFSWriter::h__SWriter : public lh__Writer
1192 ASDCP_NO_COPY_CONSTRUCT(h__SWriter);
1194 StereoscopicPhase_t m_NextPhase;
1197 h__SWriter(const Dictionary& d) : lh__Writer(d), m_NextPhase(SP_LEFT) {}
1200 Result_t WriteFrame(const FrameBuffer& FrameBuf, StereoscopicPhase_t phase,
1201 AESEncContext* Ctx, HMACContext* HMAC)
1203 if ( m_NextPhase != phase )
1204 return RESULT_SPHASE;
1206 if ( phase == SP_LEFT )
1208 m_NextPhase = SP_RIGHT;
1209 return lh__Writer::WriteFrame(FrameBuf, true, Ctx, HMAC);
1212 m_NextPhase = SP_LEFT;
1213 return lh__Writer::WriteFrame(FrameBuf, false, Ctx, HMAC);
1219 if ( m_NextPhase != SP_LEFT )
1220 return RESULT_SPHASE;
1222 assert( m_FramesWritten % 2 == 0 );
1223 m_FramesWritten /= 2;
1224 return lh__Writer::Finalize();
1230 ASDCP::JP2K::MXFSWriter::MXFSWriter()
1234 ASDCP::JP2K::MXFSWriter::~MXFSWriter()
1238 // Warning: direct manipulation of MXF structures can interfere
1239 // with the normal operation of the wrapper. Caveat emptor!
1241 ASDCP::MXF::OP1aHeader&
1242 ASDCP::JP2K::MXFSWriter::OP1aHeader()
1244 if ( m_Writer.empty() )
1246 assert(g_OP1aHeader);
1247 return *g_OP1aHeader;
1250 return m_Writer->m_HeaderPart;
1253 // Warning: direct manipulation of MXF structures can interfere
1254 // with the normal operation of the wrapper. Caveat emptor!
1256 ASDCP::MXF::OPAtomIndexFooter&
1257 ASDCP::JP2K::MXFSWriter::OPAtomIndexFooter()
1259 if ( m_Writer.empty() )
1261 assert(g_OPAtomIndexFooter);
1262 return *g_OPAtomIndexFooter;
1265 return m_Writer->m_FooterPart;
1268 // Warning: direct manipulation of MXF structures can interfere
1269 // with the normal operation of the wrapper. Caveat emptor!
1272 ASDCP::JP2K::MXFSWriter::RIP()
1274 if ( m_Writer.empty() )
1280 return m_Writer->m_RIP;
1283 // Open the file for writing. The file must not exist. Returns error if
1284 // the operation cannot be completed.
1286 ASDCP::JP2K::MXFSWriter::OpenWrite(const std::string& filename, const WriterInfo& Info,
1287 const PictureDescriptor& PDesc, ui32_t HeaderSize)
1289 if ( Info.LabelSetType == LS_MXF_SMPTE )
1290 m_Writer = new h__SWriter(DefaultSMPTEDict());
1292 m_Writer = new h__SWriter(DefaultInteropDict());
1294 if ( PDesc.EditRate != ASDCP::EditRate_24
1295 && PDesc.EditRate != ASDCP::EditRate_25
1296 && PDesc.EditRate != ASDCP::EditRate_30
1297 && PDesc.EditRate != ASDCP::EditRate_48
1298 && PDesc.EditRate != ASDCP::EditRate_50
1299 && PDesc.EditRate != ASDCP::EditRate_60 )
1301 DefaultLogSink().Error("Stereoscopic wrapping requires 24, 25, 30, 48, 50 or 60 fps input streams.\n");
1302 return RESULT_FORMAT;
1305 if ( PDesc.StoredWidth > 2048 )
1306 DefaultLogSink().Warn("Wrapping non-standard 4K stereoscopic content. I hope you know what you are doing!\n");
1308 m_Writer->m_Info = Info;
1310 Result_t result = m_Writer->OpenWrite(filename, ASDCP::ESS_JPEG_2000_S, HeaderSize);
1312 if ( ASDCP_SUCCESS(result) )
1314 PictureDescriptor TmpPDesc = PDesc;
1316 if ( PDesc.EditRate == ASDCP::EditRate_24 )
1317 TmpPDesc.EditRate = ASDCP::EditRate_48;
1319 else if ( PDesc.EditRate == ASDCP::EditRate_25 )
1320 TmpPDesc.EditRate = ASDCP::EditRate_50;
1322 else if ( PDesc.EditRate == ASDCP::EditRate_30 )
1323 TmpPDesc.EditRate = ASDCP::EditRate_60;
1325 else if ( PDesc.EditRate == ASDCP::EditRate_48 )
1326 TmpPDesc.EditRate = ASDCP::EditRate_96;
1328 else if ( PDesc.EditRate == ASDCP::EditRate_50 )
1329 TmpPDesc.EditRate = ASDCP::EditRate_100;
1331 else if ( PDesc.EditRate == ASDCP::EditRate_60 )
1332 TmpPDesc.EditRate = ASDCP::EditRate_120;
1334 result = m_Writer->SetSourceStream(TmpPDesc, JP2K_S_PACKAGE_LABEL, PDesc.EditRate);
1337 if ( ASDCP_FAILURE(result) )
1344 ASDCP::JP2K::MXFSWriter::WriteFrame(const SFrameBuffer& FrameBuf, AESEncContext* Ctx, HMACContext* HMAC)
1346 if ( m_Writer.empty() )
1349 Result_t result = m_Writer->WriteFrame(FrameBuf.Left, SP_LEFT, Ctx, HMAC);
1351 if ( ASDCP_SUCCESS(result) )
1352 result = m_Writer->WriteFrame(FrameBuf.Right, SP_RIGHT, Ctx, HMAC);
1357 // Writes a frame of essence to the MXF file. If the optional AESEncContext
1358 // argument is present, the essence is encrypted prior to writing.
1359 // Fails if the file is not open, is finalized, or an operating system
1362 ASDCP::JP2K::MXFSWriter::WriteFrame(const FrameBuffer& FrameBuf, StereoscopicPhase_t phase,
1363 AESEncContext* Ctx, HMACContext* HMAC)
1365 if ( m_Writer.empty() )
1368 return m_Writer->WriteFrame(FrameBuf, phase, Ctx, HMAC);
1371 // Closes the MXF file, writing the index and other closing information.
1373 ASDCP::JP2K::MXFSWriter::Finalize()
1375 if ( m_Writer.empty() )
1378 return m_Writer->Finalize();
1382 // end AS_DCP_JP2K.cpp