2 Copyright (c) 2004-2014, 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
28 \version $Id: AS_DCP_JP2K.cpp,v 1.69 2015/10/07 16:41:23 jhurst Exp $
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: %hhu\n", PDesc.CodingStyleDefault.Scod);
167 fprintf(stream, " ProgressionOrder: %hhu\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: %hhu\n", PDesc.CodingStyleDefault.SGcod.MultiCompTransform);
172 fprintf(stream, "DecompositionLevels: %hhu\n", PDesc.CodingStyleDefault.SPcod.DecompositionLevels);
173 fprintf(stream, " CodeblockWidth: %hhu\n", PDesc.CodingStyleDefault.SPcod.CodeblockWidth);
174 fprintf(stream, " CodeblockHeight: %hhu\n", PDesc.CodingStyleDefault.SPcod.CodeblockHeight);
175 fprintf(stream, " CodeblockStyle: %hhu\n", PDesc.CodingStyleDefault.SPcod.CodeblockStyle);
176 fprintf(stream, " Transformation: %hhu\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: %u\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: %hhu\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().Warn("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)) )
690 // get frame position
691 Kumu::fpos_t FilePosition = m_HeaderPart.BodyOffset + TmpEntry.StreamOffset;
692 Result_t result = RESULT_OK;
694 if ( phase == SP_LEFT )
696 if ( FilePosition != m_LastPosition )
698 m_LastPosition = FilePosition;
699 result = m_File.Seek(FilePosition);
702 // the call to ReadEKLVPacket() will leave the file on an R frame
703 m_StereoFrameReady = FrameNum;
705 else if ( phase == SP_RIGHT )
707 if ( m_StereoFrameReady != FrameNum )
709 // the file is not already positioned, we must do some work
710 // seek to the companion SP_LEFT frame and read the frame's key and length
711 if ( FilePosition != m_LastPosition )
713 m_LastPosition = FilePosition;
714 result = m_File.Seek(FilePosition);
718 result = Reader.ReadKLFromFile(m_File);
720 if ( ASDCP_SUCCESS(result) )
722 // skip over the companion SP_LEFT frame
723 Kumu::fpos_t new_pos = FilePosition + SMPTE_UL_LENGTH + Reader.KLLength() + Reader.Length();
724 result = m_File.Seek(new_pos);
728 // the call to ReadEKLVPacket() will leave the file not on an R frame
729 m_StereoFrameReady = 0xffffffff;
733 DefaultLogSink().Error("Unexpected stereoscopic phase value: %u\n", phase);
737 if( ASDCP_SUCCESS(result) )
739 ui32_t SequenceNum = FrameNum * 2;
740 SequenceNum += ( phase == SP_RIGHT ) ? 2 : 1;
742 result = ReadEKLVPacket(FrameNum, SequenceNum, FrameBuf, m_Dict->ul(MDD_JPEG2000Essence), Ctx, HMAC);
751 ASDCP::JP2K::MXFSReader::MXFSReader()
753 m_Reader = new h__SReader(DefaultCompositeDict());
757 ASDCP::JP2K::MXFSReader::~MXFSReader()
759 if ( m_Reader && m_Reader->m_File.IsOpen() )
763 // Warning: direct manipulation of MXF structures can interfere
764 // with the normal operation of the wrapper. Caveat emptor!
766 ASDCP::MXF::OP1aHeader&
767 ASDCP::JP2K::MXFSReader::OP1aHeader()
769 if ( m_Reader.empty() )
771 assert(g_OP1aHeader);
772 return *g_OP1aHeader;
775 return m_Reader->m_HeaderPart;
778 // Warning: direct manipulation of MXF structures can interfere
779 // with the normal operation of the wrapper. Caveat emptor!
781 ASDCP::MXF::OPAtomIndexFooter&
782 ASDCP::JP2K::MXFSReader::OPAtomIndexFooter()
784 if ( m_Reader.empty() )
786 assert(g_OPAtomIndexFooter);
787 return *g_OPAtomIndexFooter;
790 return m_Reader->m_IndexAccess;
793 // Warning: direct manipulation of MXF structures can interfere
794 // with the normal operation of the wrapper. Caveat emptor!
797 ASDCP::JP2K::MXFSReader::RIP()
799 if ( m_Reader.empty() )
805 return m_Reader->m_RIP;
808 // Open the file for reading. The file must exist. Returns error if the
809 // operation cannot be completed.
811 ASDCP::JP2K::MXFSReader::OpenRead(const std::string& filename) const
813 return m_Reader->OpenRead(filename, ASDCP::ESS_JPEG_2000_S);
818 ASDCP::JP2K::MXFSReader::ReadFrame(ui32_t FrameNum, SFrameBuffer& FrameBuf, AESDecContext* Ctx, HMACContext* HMAC) const
820 Result_t result = RESULT_INIT;
822 if ( m_Reader && m_Reader->m_File.IsOpen() )
824 result = m_Reader->ReadFrame(FrameNum, SP_LEFT, FrameBuf.Left, Ctx, HMAC);
826 if ( ASDCP_SUCCESS(result) )
827 result = m_Reader->ReadFrame(FrameNum, SP_RIGHT, FrameBuf.Right, Ctx, HMAC);
835 ASDCP::JP2K::MXFSReader::ReadFrame(ui32_t FrameNum, StereoscopicPhase_t phase, FrameBuffer& FrameBuf,
836 AESDecContext* Ctx, HMACContext* HMAC) const
838 if ( m_Reader && m_Reader->m_File.IsOpen() )
839 return m_Reader->ReadFrame(FrameNum, phase, FrameBuf, Ctx, HMAC);
845 ASDCP::JP2K::MXFSReader::LocateFrame(ui32_t FrameNum, Kumu::fpos_t& streamOffset, i8_t& temporalOffset, i8_t& keyFrameOffset) const
847 return m_Reader->LocateFrame(FrameNum, streamOffset, temporalOffset, keyFrameOffset);
850 // Fill the struct with the values from the file's header.
851 // Returns RESULT_INIT if the file is not open.
853 ASDCP::JP2K::MXFSReader::FillPictureDescriptor(PictureDescriptor& PDesc) const
855 if ( m_Reader && m_Reader->m_File.IsOpen() )
857 PDesc = m_Reader->m_PDesc;
865 // Fill the struct with the values from the file's header.
866 // Returns RESULT_INIT if the file is not open.
868 ASDCP::JP2K::MXFSReader::FillWriterInfo(WriterInfo& Info) const
870 if ( m_Reader && m_Reader->m_File.IsOpen() )
872 Info = m_Reader->m_Info;
881 ASDCP::JP2K::MXFSReader::DumpHeaderMetadata(FILE* stream) const
883 if ( m_Reader->m_File.IsOpen() )
884 m_Reader->m_HeaderPart.Dump(stream);
890 ASDCP::JP2K::MXFSReader::DumpIndex(FILE* stream) const
892 if ( m_Reader->m_File.IsOpen() )
893 m_Reader->m_IndexAccess.Dump(stream);
898 ASDCP::JP2K::MXFSReader::Close() const
900 if ( m_Reader && m_Reader->m_File.IsOpen() )
910 //------------------------------------------------------------------------------------------
914 class lh__Writer : public ASDCP::h__ASDCPWriter
916 ASDCP_NO_COPY_CONSTRUCT(lh__Writer);
919 JPEG2000PictureSubDescriptor* m_EssenceSubDescriptor;
922 PictureDescriptor m_PDesc;
923 byte_t m_EssenceUL[SMPTE_UL_LENGTH];
925 lh__Writer(const Dictionary& d) : ASDCP::h__ASDCPWriter(d), m_EssenceSubDescriptor(0) {
926 memset(m_EssenceUL, 0, SMPTE_UL_LENGTH);
929 virtual ~lh__Writer(){}
931 Result_t OpenWrite(const std::string&, EssenceType_t type, ui32_t HeaderSize);
932 Result_t SetSourceStream(const PictureDescriptor&, const std::string& label,
933 ASDCP::Rational LocalEditRate = ASDCP::Rational(0,0));
934 Result_t WriteFrame(const JP2K::FrameBuffer&, bool add_index, AESEncContext*, HMACContext*);
938 // Open the file for writing. The file must not exist. Returns error if
939 // the operation cannot be completed.
941 lh__Writer::OpenWrite(const std::string& filename, EssenceType_t type, ui32_t HeaderSize)
943 if ( ! m_State.Test_BEGIN() )
946 Result_t result = m_File.OpenWrite(filename);
948 if ( ASDCP_SUCCESS(result) )
950 m_HeaderSize = HeaderSize;
951 RGBAEssenceDescriptor* tmp_rgba = new RGBAEssenceDescriptor(m_Dict);
952 tmp_rgba->ComponentMaxRef = 4095;
953 tmp_rgba->ComponentMinRef = 0;
955 m_EssenceDescriptor = tmp_rgba;
956 m_EssenceSubDescriptor = new JPEG2000PictureSubDescriptor(m_Dict);
957 m_EssenceSubDescriptorList.push_back((InterchangeObject*)m_EssenceSubDescriptor);
959 GenRandomValue(m_EssenceSubDescriptor->InstanceUID);
960 m_EssenceDescriptor->SubDescriptors.push_back(m_EssenceSubDescriptor->InstanceUID);
962 if ( type == ASDCP::ESS_JPEG_2000_S && m_Info.LabelSetType == LS_MXF_SMPTE )
964 InterchangeObject* StereoSubDesc = new StereoscopicPictureSubDescriptor(m_Dict);
965 m_EssenceSubDescriptorList.push_back(StereoSubDesc);
966 GenRandomValue(StereoSubDesc->InstanceUID);
967 m_EssenceDescriptor->SubDescriptors.push_back(StereoSubDesc->InstanceUID);
970 result = m_State.Goto_INIT();
976 // Automatically sets the MXF file's metadata from the first jpeg codestream stream.
978 lh__Writer::SetSourceStream(const PictureDescriptor& PDesc, const std::string& label, ASDCP::Rational LocalEditRate)
981 if ( ! m_State.Test_INIT() )
984 if ( LocalEditRate == ASDCP::Rational(0,0) )
985 LocalEditRate = PDesc.EditRate;
989 assert(m_EssenceDescriptor);
990 assert(m_EssenceSubDescriptor);
991 Result_t result = JP2K_PDesc_to_MD(m_PDesc, *m_Dict,
992 *static_cast<ASDCP::MXF::GenericPictureEssenceDescriptor*>(m_EssenceDescriptor),
993 *m_EssenceSubDescriptor);
995 if ( ASDCP_SUCCESS(result) )
997 if ( PDesc.StoredWidth < 2049 )
999 static_cast<ASDCP::MXF::RGBAEssenceDescriptor*>(m_EssenceDescriptor)->PictureEssenceCoding.Set(m_Dict->ul(MDD_JP2KEssenceCompression_2K));
1000 m_EssenceSubDescriptor->Rsize = 3;
1004 static_cast<ASDCP::MXF::RGBAEssenceDescriptor*>(m_EssenceDescriptor)->PictureEssenceCoding.Set(m_Dict->ul(MDD_JP2KEssenceCompression_4K));
1005 m_EssenceSubDescriptor->Rsize = 4;
1008 memcpy(m_EssenceUL, m_Dict->ul(MDD_JPEG2000Essence), SMPTE_UL_LENGTH);
1009 m_EssenceUL[SMPTE_UL_LENGTH-1] = 1; // first (and only) essence container
1010 result = m_State.Goto_READY();
1013 if ( ASDCP_SUCCESS(result) )
1015 result = WriteASDCPHeader(label, UL(m_Dict->ul(MDD_JPEG_2000WrappingFrame)),
1016 PICT_DEF_LABEL, UL(m_EssenceUL), UL(m_Dict->ul(MDD_PictureDataDef)),
1017 LocalEditRate, derive_timecode_rate_from_edit_rate(m_PDesc.EditRate));
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
1029 lh__Writer::WriteFrame(const JP2K::FrameBuffer& FrameBuf, bool add_index,
1030 AESEncContext* Ctx, HMACContext* HMAC)
1032 Result_t result = RESULT_OK;
1034 if ( m_State.Test_READY() )
1035 result = m_State.Goto_RUNNING(); // first time through
1037 ui64_t StreamOffset = m_StreamOffset;
1039 if ( ASDCP_SUCCESS(result) )
1040 result = WriteEKLVPacket(FrameBuf, m_EssenceUL, Ctx, HMAC);
1042 if ( ASDCP_SUCCESS(result) && add_index )
1044 IndexTableSegment::IndexEntry Entry;
1045 Entry.StreamOffset = StreamOffset;
1046 m_FooterPart.PushIndexEntry(Entry);
1054 // Closes the MXF file, writing the index and other closing information.
1057 lh__Writer::Finalize()
1059 if ( ! m_State.Test_RUNNING() )
1060 return RESULT_STATE;
1062 m_State.Goto_FINAL();
1064 return WriteASDCPFooter();
1069 class ASDCP::JP2K::MXFWriter::h__Writer : public lh__Writer
1071 ASDCP_NO_COPY_CONSTRUCT(h__Writer);
1075 h__Writer(const Dictionary& d) : lh__Writer(d) {}
1079 //------------------------------------------------------------------------------------------
1083 ASDCP::JP2K::MXFWriter::MXFWriter()
1087 ASDCP::JP2K::MXFWriter::~MXFWriter()
1091 // Warning: direct manipulation of MXF structures can interfere
1092 // with the normal operation of the wrapper. Caveat emptor!
1094 ASDCP::MXF::OP1aHeader&
1095 ASDCP::JP2K::MXFWriter::OP1aHeader()
1097 if ( m_Writer.empty() )
1099 assert(g_OP1aHeader);
1100 return *g_OP1aHeader;
1103 return m_Writer->m_HeaderPart;
1106 // Warning: direct manipulation of MXF structures can interfere
1107 // with the normal operation of the wrapper. Caveat emptor!
1109 ASDCP::MXF::OPAtomIndexFooter&
1110 ASDCP::JP2K::MXFWriter::OPAtomIndexFooter()
1112 if ( m_Writer.empty() )
1114 assert(g_OPAtomIndexFooter);
1115 return *g_OPAtomIndexFooter;
1118 return m_Writer->m_FooterPart;
1121 // Warning: direct manipulation of MXF structures can interfere
1122 // with the normal operation of the wrapper. Caveat emptor!
1125 ASDCP::JP2K::MXFWriter::RIP()
1127 if ( m_Writer.empty() )
1133 return m_Writer->m_RIP;
1136 // Open the file for writing. The file must not exist. Returns error if
1137 // the operation cannot be completed.
1139 ASDCP::JP2K::MXFWriter::OpenWrite(const std::string& filename, const WriterInfo& Info,
1140 const PictureDescriptor& PDesc, ui32_t HeaderSize)
1142 if ( Info.LabelSetType == LS_MXF_SMPTE )
1143 m_Writer = new h__Writer(DefaultSMPTEDict());
1145 m_Writer = new h__Writer(DefaultInteropDict());
1147 m_Writer->m_Info = Info;
1149 Result_t result = m_Writer->OpenWrite(filename, ASDCP::ESS_JPEG_2000, HeaderSize);
1151 if ( ASDCP_SUCCESS(result) )
1152 result = m_Writer->SetSourceStream(PDesc, JP2K_PACKAGE_LABEL);
1154 if ( ASDCP_FAILURE(result) )
1161 // Writes a frame of essence to the MXF file. If the optional AESEncContext
1162 // argument is present, the essence is encrypted prior to writing.
1163 // Fails if the file is not open, is finalized, or an operating system
1166 ASDCP::JP2K::MXFWriter::WriteFrame(const FrameBuffer& FrameBuf, AESEncContext* Ctx, HMACContext* HMAC)
1168 if ( m_Writer.empty() )
1171 return m_Writer->WriteFrame(FrameBuf, true, Ctx, HMAC);
1174 // Closes the MXF file, writing the index and other closing information.
1176 ASDCP::JP2K::MXFWriter::Finalize()
1178 if ( m_Writer.empty() )
1181 return m_Writer->Finalize();
1185 //------------------------------------------------------------------------------------------
1189 class ASDCP::JP2K::MXFSWriter::h__SWriter : public lh__Writer
1191 ASDCP_NO_COPY_CONSTRUCT(h__SWriter);
1193 StereoscopicPhase_t m_NextPhase;
1196 h__SWriter(const Dictionary& d) : lh__Writer(d), m_NextPhase(SP_LEFT) {}
1199 Result_t WriteFrame(const FrameBuffer& FrameBuf, StereoscopicPhase_t phase,
1200 AESEncContext* Ctx, HMACContext* HMAC)
1202 if ( m_NextPhase != phase )
1203 return RESULT_SPHASE;
1205 if ( phase == SP_LEFT )
1207 m_NextPhase = SP_RIGHT;
1208 return lh__Writer::WriteFrame(FrameBuf, true, Ctx, HMAC);
1211 m_NextPhase = SP_LEFT;
1212 return lh__Writer::WriteFrame(FrameBuf, false, Ctx, HMAC);
1218 if ( m_NextPhase != SP_LEFT )
1219 return RESULT_SPHASE;
1221 assert( m_FramesWritten % 2 == 0 );
1222 m_FramesWritten /= 2;
1223 return lh__Writer::Finalize();
1229 ASDCP::JP2K::MXFSWriter::MXFSWriter()
1233 ASDCP::JP2K::MXFSWriter::~MXFSWriter()
1237 // Warning: direct manipulation of MXF structures can interfere
1238 // with the normal operation of the wrapper. Caveat emptor!
1240 ASDCP::MXF::OP1aHeader&
1241 ASDCP::JP2K::MXFSWriter::OP1aHeader()
1243 if ( m_Writer.empty() )
1245 assert(g_OP1aHeader);
1246 return *g_OP1aHeader;
1249 return m_Writer->m_HeaderPart;
1252 // Warning: direct manipulation of MXF structures can interfere
1253 // with the normal operation of the wrapper. Caveat emptor!
1255 ASDCP::MXF::OPAtomIndexFooter&
1256 ASDCP::JP2K::MXFSWriter::OPAtomIndexFooter()
1258 if ( m_Writer.empty() )
1260 assert(g_OPAtomIndexFooter);
1261 return *g_OPAtomIndexFooter;
1264 return m_Writer->m_FooterPart;
1267 // Warning: direct manipulation of MXF structures can interfere
1268 // with the normal operation of the wrapper. Caveat emptor!
1271 ASDCP::JP2K::MXFSWriter::RIP()
1273 if ( m_Writer.empty() )
1279 return m_Writer->m_RIP;
1282 // Open the file for writing. The file must not exist. Returns error if
1283 // the operation cannot be completed.
1285 ASDCP::JP2K::MXFSWriter::OpenWrite(const std::string& filename, const WriterInfo& Info,
1286 const PictureDescriptor& PDesc, ui32_t HeaderSize)
1288 if ( Info.LabelSetType == LS_MXF_SMPTE )
1289 m_Writer = new h__SWriter(DefaultSMPTEDict());
1291 m_Writer = new h__SWriter(DefaultInteropDict());
1293 if ( PDesc.EditRate != ASDCP::EditRate_24
1294 && PDesc.EditRate != ASDCP::EditRate_25
1295 && PDesc.EditRate != ASDCP::EditRate_30
1296 && PDesc.EditRate != ASDCP::EditRate_48
1297 && PDesc.EditRate != ASDCP::EditRate_50
1298 && PDesc.EditRate != ASDCP::EditRate_60 )
1300 DefaultLogSink().Error("Stereoscopic wrapping requires 24, 25, 30, 48, 50 or 60 fps input streams.\n");
1301 return RESULT_FORMAT;
1304 if ( PDesc.StoredWidth > 2048 )
1305 DefaultLogSink().Warn("Wrapping non-standard 4K stereoscopic content. I hope you know what you are doing!\n");
1307 m_Writer->m_Info = Info;
1309 Result_t result = m_Writer->OpenWrite(filename, ASDCP::ESS_JPEG_2000_S, HeaderSize);
1311 if ( ASDCP_SUCCESS(result) )
1313 PictureDescriptor TmpPDesc = PDesc;
1315 if ( PDesc.EditRate == ASDCP::EditRate_24 )
1316 TmpPDesc.EditRate = ASDCP::EditRate_48;
1318 else if ( PDesc.EditRate == ASDCP::EditRate_25 )
1319 TmpPDesc.EditRate = ASDCP::EditRate_50;
1321 else if ( PDesc.EditRate == ASDCP::EditRate_30 )
1322 TmpPDesc.EditRate = ASDCP::EditRate_60;
1324 else if ( PDesc.EditRate == ASDCP::EditRate_48 )
1325 TmpPDesc.EditRate = ASDCP::EditRate_96;
1327 else if ( PDesc.EditRate == ASDCP::EditRate_50 )
1328 TmpPDesc.EditRate = ASDCP::EditRate_100;
1330 else if ( PDesc.EditRate == ASDCP::EditRate_60 )
1331 TmpPDesc.EditRate = ASDCP::EditRate_120;
1333 result = m_Writer->SetSourceStream(TmpPDesc, JP2K_S_PACKAGE_LABEL, PDesc.EditRate);
1336 if ( ASDCP_FAILURE(result) )
1343 ASDCP::JP2K::MXFSWriter::WriteFrame(const SFrameBuffer& FrameBuf, AESEncContext* Ctx, HMACContext* HMAC)
1345 if ( m_Writer.empty() )
1348 Result_t result = m_Writer->WriteFrame(FrameBuf.Left, SP_LEFT, Ctx, HMAC);
1350 if ( ASDCP_SUCCESS(result) )
1351 result = m_Writer->WriteFrame(FrameBuf.Right, SP_RIGHT, Ctx, HMAC);
1356 // Writes a frame of essence to the MXF file. If the optional AESEncContext
1357 // argument is present, the essence is encrypted prior to writing.
1358 // Fails if the file is not open, is finalized, or an operating system
1361 ASDCP::JP2K::MXFSWriter::WriteFrame(const FrameBuffer& FrameBuf, StereoscopicPhase_t phase,
1362 AESEncContext* Ctx, HMACContext* HMAC)
1364 if ( m_Writer.empty() )
1367 return m_Writer->WriteFrame(FrameBuf, phase, Ctx, HMAC);
1370 // Closes the MXF file, writing the index and other closing information.
1372 ASDCP::JP2K::MXFSWriter::Finalize()
1374 if ( m_Writer.empty() )
1377 return m_Writer->Finalize();
1381 // end AS_DCP_JP2K.cpp