as-02ooooooooooo!
[asdcplib.git] / src / AS_02_JP2K.cpp
1 /*
2   Copyright (c) 2011-2012, Robert Scheler, Heiko Sparenberg Fraunhofer IIS, John Hurst
3   All rights reserved.
4
5   Redistribution and use in source and binary forms, with or without
6   modification, are permitted provided that the following conditions
7   are met:
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.
15
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.
26 */ 
27 /*! \file    AS_02_JP2K.cpp
28   \version $Id$
29   \brief   AS-02 library, JPEG 2000 essence reader and writer implementation
30 */
31
32 #include "AS_02_internal.h"
33
34 #include <iostream>
35 #include <iomanip>
36
37 using namespace ASDCP::JP2K;
38 using Kumu::GenRandomValue;
39
40 //------------------------------------------------------------------------------------------
41
42 static std::string JP2K_PACKAGE_LABEL = "File Package: SMPTE 429-4 frame wrapping of JPEG 2000 codestreams";
43 static std::string PICT_DEF_LABEL = "Image Track";
44
45 //------------------------------------------------------------------------------------------
46 //
47 // hidden, internal implementation of JPEG 2000 reader
48
49
50 class lf__Reader : public AS_02::h__Reader
51 {
52   RGBAEssenceDescriptor*        m_EssenceDescriptor;
53   JPEG2000PictureSubDescriptor* m_EssenceSubDescriptor;
54   ASDCP::Rational               m_EditRate;
55   ASDCP::Rational               m_SampleRate;
56   EssenceType_t                 m_Format;
57
58   ASDCP_NO_COPY_CONSTRUCT(lf__Reader);
59
60 public:
61   PictureDescriptor m_PDesc;        // codestream parameter list
62
63   lf__Reader(const Dictionary& d) :
64     AS_02::h__Reader(d), m_EssenceDescriptor(0), m_EssenceSubDescriptor(0), m_Format(ESS_UNKNOWN) {}
65   Result_t    OpenRead(const char*, EssenceType_t);
66   Result_t    ReadFrame(ui32_t, JP2K::FrameBuffer&, AESDecContext*, HMACContext*);
67   Result_t    MD_to_JP2K_PDesc(JP2K::PictureDescriptor& PDesc);
68
69   Result_t OpenMXFRead(const char* filename);
70   // positions file before reading
71   Result_t ReadEKLVFrame(ui32_t FrameNum, ASDCP::FrameBuffer& FrameBuf,
72                          const byte_t* EssenceUL, AESDecContext* Ctx, HMACContext* HMAC);
73
74   // reads from current position
75   Result_t ReadEKLVPacket(ui32_t FrameNum, ui32_t SequenceNum, ASDCP::FrameBuffer& FrameBuf,
76                           const byte_t* EssenceUL, AESDecContext* Ctx, HMACContext* HMAC);
77
78 };
79
80 //
81 ASDCP::Result_t
82 lf__Reader::MD_to_JP2K_PDesc(ASDCP::JP2K::PictureDescriptor& PDesc)
83 {
84   memset(&PDesc, 0, sizeof(PDesc));
85   MXF::RGBAEssenceDescriptor* PDescObj = (MXF::RGBAEssenceDescriptor*)m_EssenceDescriptor;
86
87   PDesc.EditRate           = m_EditRate;
88   PDesc.SampleRate         = m_SampleRate;
89   assert(PDescObj->ContainerDuration <= 0xFFFFFFFFL);
90   PDesc.ContainerDuration  = (ui32_t) PDescObj->ContainerDuration;
91   PDesc.StoredWidth        = PDescObj->StoredWidth;
92   PDesc.StoredHeight       = PDescObj->StoredHeight;
93   PDesc.AspectRatio        = PDescObj->AspectRatio;
94
95   if ( m_EssenceSubDescriptor != 0 )
96     {
97       PDesc.Rsize   = m_EssenceSubDescriptor->Rsize;
98       PDesc.Xsize   = m_EssenceSubDescriptor->Xsize;
99       PDesc.Ysize   = m_EssenceSubDescriptor->Ysize;
100       PDesc.XOsize  = m_EssenceSubDescriptor->XOsize;
101       PDesc.YOsize  = m_EssenceSubDescriptor->YOsize;
102       PDesc.XTsize  = m_EssenceSubDescriptor->XTsize;
103       PDesc.YTsize  = m_EssenceSubDescriptor->YTsize;
104       PDesc.XTOsize = m_EssenceSubDescriptor->XTOsize;
105       PDesc.YTOsize = m_EssenceSubDescriptor->YTOsize;
106       PDesc.Csize   = m_EssenceSubDescriptor->Csize;
107
108       // PictureComponentSizing
109       ui32_t tmp_size = m_EssenceSubDescriptor->PictureComponentSizing.Length();
110
111       if ( tmp_size == 17 ) // ( 2 * sizeof(ui32_t) ) + 3 components * 3 byte each
112         memcpy(&PDesc.ImageComponents, m_EssenceSubDescriptor->PictureComponentSizing.RoData() + 8, tmp_size - 8);
113
114       else
115         DefaultLogSink().Error("Unexpected PictureComponentSizing size: %u, should be 17\n", tmp_size);
116
117       // CodingStyleDefault
118       memset(&PDesc.CodingStyleDefault, 0, sizeof(CodingStyleDefault_t));
119       memcpy(&PDesc.CodingStyleDefault,
120              m_EssenceSubDescriptor->CodingStyleDefault.RoData(),
121              m_EssenceSubDescriptor->CodingStyleDefault.Length());
122
123       // QuantizationDefault
124       memset(&PDesc.QuantizationDefault, 0, sizeof(QuantizationDefault_t));
125       memcpy(&PDesc.QuantizationDefault,
126              m_EssenceSubDescriptor->QuantizationDefault.RoData(),
127              m_EssenceSubDescriptor->QuantizationDefault.Length());
128
129       PDesc.QuantizationDefault.SPqcdLength = m_EssenceSubDescriptor->QuantizationDefault.Length() - 1;
130     }
131
132   return RESULT_OK;
133 }
134
135 //
136 //
137 ASDCP::Result_t
138 lf__Reader::OpenRead(const char* filename, ASDCP::EssenceType_t type)
139 {
140   Result_t result = OpenMXFRead(filename);
141
142   if( ASDCP_SUCCESS(result) )
143     {
144       InterchangeObject* tmp_iobj = 0;
145       m_HeaderPart.GetMDObjectByType(OBJ_TYPE_ARGS(RGBAEssenceDescriptor), &tmp_iobj);
146       m_EssenceDescriptor = static_cast<RGBAEssenceDescriptor*>(tmp_iobj);
147
148       m_HeaderPart.GetMDObjectByType(OBJ_TYPE_ARGS(JPEG2000PictureSubDescriptor), &tmp_iobj);
149       m_EssenceSubDescriptor = static_cast<JPEG2000PictureSubDescriptor*>(tmp_iobj);
150
151       std::list<InterchangeObject*> ObjectList;
152       m_HeaderPart.GetMDObjectsByType(OBJ_TYPE_ARGS(Track), ObjectList);
153
154       if ( ObjectList.empty() )
155         {
156           DefaultLogSink().Error("MXF Metadata contains no Track Sets.\n");
157           return RESULT_FORMAT;
158         }
159
160       m_EditRate = ((Track*)ObjectList.front())->EditRate;
161       m_SampleRate = m_EssenceDescriptor->SampleRate;
162
163       if ( type == ASDCP::ESS_JPEG_2000 )
164         {
165           if ( m_EditRate != m_SampleRate )
166             {
167               DefaultLogSink().Warn("EditRate and SampleRate do not match (%.03f, %.03f).\n",
168                                     m_EditRate.Quotient(), m_SampleRate.Quotient());
169
170               if ( m_EditRate == EditRate_24 && m_SampleRate == EditRate_48 )
171                 {
172                   DefaultLogSink().Debug("File may contain JPEG Interop stereoscopic images.\n");
173                   return RESULT_SFORMAT;
174                 }
175
176               return RESULT_FORMAT;
177             }
178         }
179       else if ( type == ASDCP::ESS_JPEG_2000_S )
180         {
181           if ( m_EditRate == EditRate_24 )
182             {
183               if ( m_SampleRate != EditRate_48 )
184                 {
185                   DefaultLogSink().Error("EditRate and SampleRate not correct for 24/48 stereoscopic essence.\n");
186                   return RESULT_FORMAT;
187                 }
188             }
189           else if ( m_EditRate == EditRate_25 )
190             {
191               if ( m_SampleRate != EditRate_50 )
192                 {
193                   DefaultLogSink().Error("EditRate and SampleRate not correct for 25/50 stereoscopic essence.\n");
194                   return RESULT_FORMAT;
195                 }
196             }
197           else if ( m_EditRate == EditRate_30 )
198             {
199               if ( m_SampleRate != EditRate_60 )
200                 {
201                   DefaultLogSink().Error("EditRate and SampleRate not correct for 30/60 stereoscopic essence.\n");
202                   return RESULT_FORMAT;
203                 }
204             }
205           else
206             {
207               DefaultLogSink().Error("EditRate not correct for stereoscopic essence: %d/%d.\n",
208                                      m_EditRate.Numerator, m_EditRate.Denominator);
209               return RESULT_FORMAT;
210             }
211         }
212       else
213         {
214           DefaultLogSink().Error("'type' argument unexpected: %x\n", type);
215           return RESULT_STATE;
216         }
217
218       result = MD_to_JP2K_PDesc(m_PDesc);
219     }
220
221   if( ASDCP_SUCCESS(result) )
222     result = InitMXFIndex();
223
224   if( ASDCP_SUCCESS(result) )
225     result = InitInfo();
226
227   return result;
228 }
229
230 //
231 //
232 ASDCP::Result_t
233 lf__Reader::ReadFrame(ui32_t FrameNum, JP2K::FrameBuffer& FrameBuf,
234                       AESDecContext* Ctx, HMACContext* HMAC)
235 {
236   if ( ! m_File.IsOpen() )
237     return RESULT_INIT;
238
239   assert(m_Dict);
240   return ReadEKLVFrame(FrameNum, FrameBuf, m_Dict->ul(MDD_JPEG2000Essence), Ctx, HMAC);
241 }
242
243
244 //
245 class AS_02::JP2K::MXFReader::h__Reader : public lf__Reader
246 {
247   ASDCP_NO_COPY_CONSTRUCT(h__Reader);
248   h__Reader();
249
250 public:
251   h__Reader(const Dictionary& d) : lf__Reader(d) {}
252 };
253
254 AS_02::JP2K::MXFReader::MXFReader()
255 {
256   m_Reader = new h__Reader(DefaultCompositeDict());
257 }
258
259
260 AS_02::JP2K::MXFReader::~MXFReader()
261 {
262 }
263
264 // Warning: direct manipulation of MXF structures can interfere
265 // with the normal operation of the wrapper.  Caveat emptor!
266 //
267 ASDCP::MXF::OPAtomHeader&
268 AS_02::JP2K::MXFReader::OPAtomHeader()
269 {
270   if ( m_Reader.empty() )
271     {
272       assert(g_OPAtomHeader);
273       return *g_OPAtomHeader;
274     }
275
276   return m_Reader->m_HeaderPart;
277 }
278
279 // Warning: direct manipulation of MXF structures can interfere
280 // with the normal operation of the wrapper.  Caveat emptor!
281 //
282 /*
283 ASDCP::MXF::OPAtomIndexFooter&
284 AS_02::JP2K::MXFReader::OPAtomIndexFooter()
285 {
286   if ( m_Reader.empty() )
287     {
288       assert(g_OPAtomIndexFooter);
289       return *g_OPAtomIndexFooter;
290     }
291
292   return m_Reader->m_FooterPart;
293 }
294 */
295
296 // Open the file for reading. The file must exist. Returns error if the
297 // operation cannot be completed.
298 Result_t AS_02::JP2K::MXFReader::OpenRead(const char* filename) const
299 {
300   return m_Reader->OpenRead(filename, ASDCP::ESS_JPEG_2000);
301 }
302
303 //
304 Result_t AS_02::JP2K::MXFReader::ReadFrame(ui32_t FrameNum, ASDCP::JP2K::FrameBuffer& FrameBuf,
305                                            ASDCP::AESDecContext* Ctx, ASDCP::HMACContext* HMAC) const
306 {
307   if ( m_Reader && m_Reader->m_File.IsOpen() )
308     return m_Reader->ReadFrame(FrameNum, FrameBuf, Ctx, HMAC);
309
310   return RESULT_INIT;
311 }
312
313
314 // Fill the struct with the values from the file's header.
315 // Returns RESULT_INIT if the file is not open.
316 ASDCP::Result_t AS_02::JP2K::MXFReader::FillPictureDescriptor(PictureDescriptor& PDesc) const
317 {
318   if ( m_Reader && m_Reader->m_File.IsOpen() )
319     {
320       PDesc = m_Reader->m_PDesc;
321       return RESULT_OK;
322     }
323
324   return RESULT_INIT;
325 }
326
327
328 // Fill the struct with the values from the file's header.
329 // Returns RESULT_INIT if the file is not open.
330 ASDCP::Result_t AS_02::JP2K::MXFReader::FillWriterInfo(WriterInfo& Info) const
331 {
332   if ( m_Reader && m_Reader->m_File.IsOpen() )
333     {
334       Info = m_Reader->m_Info;
335       return RESULT_OK;
336     }
337
338   return RESULT_INIT;
339 }
340
341 //
342 void
343 AS_02::JP2K::MXFReader::DumpHeaderMetadata(FILE* stream) const
344 {
345   if ( m_Reader->m_File.IsOpen() )
346     m_Reader->m_HeaderPart.Dump(stream);
347 }
348
349
350 //
351 void
352 AS_02::JP2K::MXFReader::DumpIndex(FILE* stream) const
353 {
354   if ( m_Reader->m_File.IsOpen() )
355     m_Reader->m_FooterPart.Dump(stream);
356 }
357
358 // standard method of opening an MXF file for read
359 Result_t
360 lf__Reader::OpenMXFRead(const char* filename)
361 {
362   m_LastPosition = 0;
363   AS_02::MXF::OP1aIndexBodyPartion* pCurrentBodyPartIndex = NULL;
364   Partition* pPart = NULL;
365   ui64_t EssenceStart = 0;
366   Result_t result = m_File.OpenRead(filename);
367
368   if ( ASDCP_SUCCESS(result) )
369     result = m_HeaderPart.InitFromFile(m_File);
370
371   if ( ASDCP_SUCCESS(result) )
372     {
373       ui32_t partition_size = m_HeaderPart.m_RIP.PairArray.size();
374       
375       if ( partition_size > 3 )
376         {
377           //for all entry except the first and the last(header&footer)
378           Array<RIP::Pair>::iterator r_i = m_HeaderPart.m_RIP.PairArray.begin();
379           r_i++;
380           ui32_t i=2;
381
382           while(r_i != m_HeaderPart.m_RIP.PairArray.end() && i<partition_size)
383             {
384               m_File.Seek((*r_i).ByteOffset);
385               pPart = new Partition(this->m_Dict);
386               result = pPart->InitFromFile(m_File);
387
388               if(pPart->BodySID != 0 && pPart->IndexSID == 0)
389                 { // AS_02::IS_FOLLOW
390                   r_i++; i++;
391                   EssenceStart = m_File.Tell();
392
393                   m_File.Seek((*r_i).ByteOffset);
394                   pCurrentBodyPartIndex = new AS_02::MXF::OP1aIndexBodyPartion(this->m_Dict);
395                   pCurrentBodyPartIndex->m_Lookup = &m_HeaderPart.m_Primer;
396                   result = pCurrentBodyPartIndex->InitFromFile(m_File);
397                 }
398               else
399                 { // AS_02::IS_LEAD
400                   delete pPart;
401                   m_File.Seek((*r_i).ByteOffset);
402                   pCurrentBodyPartIndex = new AS_02::MXF::OP1aIndexBodyPartion(this->m_Dict);
403                   pCurrentBodyPartIndex->m_Lookup = &m_HeaderPart.m_Primer;
404                   result = pCurrentBodyPartIndex->InitFromFile(m_File);
405                   
406                   if ( ASDCP_FAILURE(result) )
407                     {
408                       break;
409                     }
410         
411                   r_i++; i++;
412                   m_File.Seek((*r_i).ByteOffset);
413                   pPart = new Partition(this->m_Dict);
414                   result = pPart->InitFromFile(m_File);
415                   EssenceStart = m_File.Tell();
416                 }
417
418               if ( ASDCP_FAILURE(result) )
419                 {
420                   break;
421                 }
422
423               if(i==3)
424                 {
425                   this->m_EssenceStart = EssenceStart;
426                   this->m_pCurrentBodyPartition = pPart;
427                   this->m_pCurrentIndexPartition = pCurrentBodyPartIndex;
428                 }
429
430               this->m_BodyPartList.push_back(pCurrentBodyPartIndex);
431               this->m_BodyPartList.push_back(pPart);
432               r_i++; i++;
433             }
434         }
435     }
436
437   return result;
438 }
439
440 //
441 class lf__Writer : public AS_02::h__Writer
442 {
443   ASDCP_NO_COPY_CONSTRUCT(lf__Writer);
444   lf__Writer();
445
446   JPEG2000PictureSubDescriptor* m_EssenceSubDescriptor;
447
448 public:
449   PictureDescriptor m_PDesc;
450   byte_t            m_EssenceUL[SMPTE_UL_LENGTH];
451
452   //new attributes for AS-02 support 
453   AS_02::IndexStrategy_t m_IndexStrategy; //Shim parameter index_strategy_frame/clip
454   ui32_t m_PartitionSpace; //Shim parameter partition_spacing
455
456   lf__Writer(const Dictionary& d) : h__Writer(d), m_EssenceSubDescriptor(0), m_IndexStrategy(AS_02::IS_FOLLOW), m_PartitionSpace(60) {
457     memset(m_EssenceUL, 0, SMPTE_UL_LENGTH);
458   }
459
460   ~lf__Writer(){}
461
462   Result_t OpenWrite(const char*, EssenceType_t type, const AS_02::IndexStrategy_t& IndexStrategy,
463                      const ui32_t& PartitionSpace, const ui32_t& HeaderSize);
464   Result_t SetSourceStream(const PictureDescriptor&, const std::string& label,
465                            ASDCP::Rational LocalEditRate = ASDCP::Rational(0,0));
466   Result_t WriteFrame(const JP2K::FrameBuffer&, bool add_index, AESEncContext*, HMACContext*);
467   Result_t Finalize();
468   Result_t JP2K_PDesc_to_MD(JP2K::PictureDescriptor& PDesc);
469
470   //void AddSourceClip(const MXF::Rational& EditRate, ui32_t TCFrameRate,
471   // const std::string& TrackName, const UL& EssenceUL,
472   // const UL& DataDefinition, const std::string& PackageLabel);
473   //void AddDMSegment(const MXF::Rational& EditRate, ui32_t TCFrameRate,
474   // const std::string& TrackName, const UL& DataDefinition,
475   // const std::string& PackageLabel);
476   //void AddEssenceDescriptor(const UL& WrappingUL);
477   //Result_t CreateBodyPart(const MXF::Rational& EditRate, ui32_t BytesPerEditUnit = 0);
478
479   ////new method to create BodyPartition for essence and index
480   //Result_t CreateBodyPartPair();
481   ////new method to finalize BodyPartion(index)
482   //Result_t CompleteIndexBodyPart();
483
484   // reimplement these functions in AS_02_PCM to support modifications for AS-02
485   //Result_t WriteEKLVPacket(const ASDCP::FrameBuffer& FrameBuf,
486   //    const byte_t* EssenceUL, AESEncContext* Ctx, HMACContext* HMAC);
487   Result_t WriteMXFFooter();
488
489
490 };
491
492 const int VideoLineMapSize = 16; // See SMPTE 377M D.2.1
493 const int PixelLayoutSize = 8*2; // See SMPTE 377M D.2.3
494 static const byte_t s_PixelLayoutXYZ[PixelLayoutSize] = { 0xd8, 0x0c, 0xd9, 0x0c, 0xda, 0x0c, 0x00 };
495
496 //
497 ASDCP::Result_t
498 lf__Writer::JP2K_PDesc_to_MD(JP2K::PictureDescriptor& PDesc)
499 {
500   assert(m_EssenceDescriptor);
501   assert(m_EssenceSubDescriptor);
502   MXF::RGBAEssenceDescriptor* PDescObj = (MXF::RGBAEssenceDescriptor*)m_EssenceDescriptor;
503
504   PDescObj->ContainerDuration = PDesc.ContainerDuration;
505   PDescObj->SampleRate = PDesc.EditRate;
506   PDescObj->FrameLayout = 0;
507   PDescObj->StoredWidth = PDesc.StoredWidth;
508   PDescObj->StoredHeight = PDesc.StoredHeight;
509   PDescObj->AspectRatio = PDesc.AspectRatio;
510
511   //  if ( m_Info.LabelSetType == LS_MXF_SMPTE )
512   //    {
513   // PictureEssenceCoding UL = 
514   // Video Line Map       ui32_t[VideoLineMapSize] = { 2, 4, 0, 0 }
515   // CaptureGamma         UL = 
516   // ComponentMaxRef      ui32_t = 4095
517   // ComponentMinRef      ui32_t = 0
518   // PixelLayout          byte_t[PixelLayoutSize] = s_PixelLayoutXYZ
519   //    }
520
521   assert(m_Dict);
522   if ( PDesc.StoredWidth < 2049 )
523     {
524       PDescObj->PictureEssenceCoding.Set(m_Dict->ul(MDD_JP2KEssenceCompression_2K));
525       m_EssenceSubDescriptor->Rsize = 3;
526     }
527   else
528     {
529       PDescObj->PictureEssenceCoding.Set(m_Dict->ul(MDD_JP2KEssenceCompression_4K));
530       m_EssenceSubDescriptor->Rsize = 4;
531     }
532
533   m_EssenceSubDescriptor->Xsize = PDesc.Xsize;
534   m_EssenceSubDescriptor->Ysize = PDesc.Ysize;
535   m_EssenceSubDescriptor->XOsize = PDesc.XOsize;
536   m_EssenceSubDescriptor->YOsize = PDesc.YOsize;
537   m_EssenceSubDescriptor->XTsize = PDesc.XTsize;
538   m_EssenceSubDescriptor->YTsize = PDesc.YTsize;
539   m_EssenceSubDescriptor->XTOsize = PDesc.XTOsize;
540   m_EssenceSubDescriptor->YTOsize = PDesc.YTOsize;
541   m_EssenceSubDescriptor->Csize = PDesc.Csize;
542
543   const ui32_t tmp_buffer_len = 1024;
544   byte_t tmp_buffer[tmp_buffer_len];
545
546   // slh: this has to be done dynamically since the number of components is not always 3
547   *(ui32_t*)tmp_buffer = KM_i32_BE(m_EssenceSubDescriptor->Csize);
548   *(ui32_t*)(tmp_buffer+4) = KM_i32_BE(sizeof(ASDCP::JP2K::ImageComponent_t));
549   memcpy(tmp_buffer + 8, &PDesc.ImageComponents, sizeof(ASDCP::JP2K::ImageComponent_t) * m_EssenceSubDescriptor->Csize);
550   const ui32_t pcomp_size = (sizeof(int) * 2) + (sizeof(ASDCP::JP2K::ImageComponent_t) * m_EssenceSubDescriptor->Csize);
551
552   /*
553    *(ui32_t*)tmp_buffer = KM_i32_BE(MaxComponents); // three components
554    *(ui32_t*)(tmp_buffer+4) = KM_i32_BE(sizeof(ASDCP::JP2K::ImageComponent_t)); 
555    memcpy(tmp_buffer + 8, &PDesc.ImageComponents, sizeof(ASDCP::JP2K::ImageComponent_t) * MaxComponents);
556    const ui32_t pcomp_size = (sizeof(int) * 2) + (sizeof(ASDCP::JP2K::ImageComponent_t) * MaxComponents);
557   */
558
559   memcpy(m_EssenceSubDescriptor->PictureComponentSizing.Data(), tmp_buffer, pcomp_size);
560   m_EssenceSubDescriptor->PictureComponentSizing.Length(pcomp_size);
561
562   ui32_t precinct_set_size = 0, i;
563   for ( i = 0; PDesc.CodingStyleDefault.SPcod.PrecinctSize[i] != 0 && i < MaxPrecincts; i++ )
564     precinct_set_size++;
565
566   ui32_t csd_size = sizeof(CodingStyleDefault_t) - MaxPrecincts + precinct_set_size;
567   memcpy(m_EssenceSubDescriptor->CodingStyleDefault.Data(), &PDesc.CodingStyleDefault, csd_size);
568   m_EssenceSubDescriptor->CodingStyleDefault.Length(csd_size);
569
570   ui32_t qdflt_size = PDesc.QuantizationDefault.SPqcdLength + 1;
571   memcpy(m_EssenceSubDescriptor->QuantizationDefault.Data(), &PDesc.QuantizationDefault, qdflt_size);
572   m_EssenceSubDescriptor->QuantizationDefault.Length(qdflt_size);
573
574   return RESULT_OK;
575 }
576
577
578 // Open the file for writing. The file must not exist. Returns error if
579 // the operation cannot be completed.
580 ASDCP::Result_t
581 lf__Writer::OpenWrite(const char* filename, EssenceType_t type, const AS_02::IndexStrategy_t& IndexStrategy,
582                       const ui32_t& PartitionSpace, const ui32_t& HeaderSize)
583 {
584   if ( ! m_State.Test_BEGIN() )
585     return RESULT_STATE;
586
587   ASDCP::Result_t result = m_File.OpenWrite(filename);
588
589   if ( ASDCP_SUCCESS(result) )
590     {
591       m_IndexStrategy = IndexStrategy;
592       m_PartitionSpace = PartitionSpace;
593       m_HeaderSize = HeaderSize;
594       RGBAEssenceDescriptor* tmp_rgba = new RGBAEssenceDescriptor(m_Dict);
595       tmp_rgba->ComponentMaxRef = 4095;
596       tmp_rgba->ComponentMinRef = 0;
597
598       m_EssenceDescriptor = tmp_rgba;
599       m_EssenceSubDescriptor = new JPEG2000PictureSubDescriptor(m_Dict);
600       m_EssenceSubDescriptorList.push_back((InterchangeObject*)m_EssenceSubDescriptor);
601
602       GenRandomValue(m_EssenceSubDescriptor->InstanceUID);
603       m_EssenceDescriptor->SubDescriptors.push_back(m_EssenceSubDescriptor->InstanceUID);
604       result = m_State.Goto_INIT();
605     }
606
607   return result;
608 }
609
610 // Automatically sets the MXF file's metadata from the first jpeg codestream stream.
611 ASDCP::Result_t
612 lf__Writer::SetSourceStream(const PictureDescriptor& PDesc, const std::string& label, ASDCP::Rational LocalEditRate)
613 {
614   assert(m_Dict);
615   if ( ! m_State.Test_INIT() )
616     return RESULT_STATE;
617
618   if ( LocalEditRate == ASDCP::Rational(0,0) )
619     LocalEditRate = PDesc.EditRate;
620
621   m_PDesc = PDesc;
622   Result_t result = JP2K_PDesc_to_MD(m_PDesc);
623
624   if ( ASDCP_SUCCESS(result) )
625     {
626       memcpy(m_EssenceUL, m_Dict->ul(MDD_JPEG2000Essence), SMPTE_UL_LENGTH);
627       m_EssenceUL[SMPTE_UL_LENGTH-1] = 1; // first (and only) essence container
628       result = m_State.Goto_READY();
629     }
630
631   if ( ASDCP_SUCCESS(result) )
632     {
633       ui32_t TCFrameRate = ( m_PDesc.EditRate == EditRate_23_98  ) ? 24 : m_PDesc.EditRate.Numerator;
634
635       result = WriteMXFHeader(label, UL(m_Dict->ul(MDD_JPEG_2000Wrapping)),
636                               PICT_DEF_LABEL, UL(m_EssenceUL), UL(m_Dict->ul(MDD_PictureDataDef)),
637                               LocalEditRate, TCFrameRate);
638     }
639
640   return result;
641 }
642
643 // Writes a frame of essence to the MXF file. If the optional AESEncContext
644 // argument is present, the essence is encrypted prior to writing.
645 // Fails if the file is not open, is finalized, or an operating system
646 // error occurs.
647 //
648 ASDCP::Result_t
649 lf__Writer::WriteFrame(const ASDCP::JP2K::FrameBuffer& FrameBuf, bool add_index,
650                        AESEncContext* Ctx, HMACContext* HMAC)
651 {
652   Result_t result = RESULT_OK;
653
654   if ( m_State.Test_READY() ){
655     result = m_State.Goto_RUNNING(); // first time through
656   }
657   ui64_t StreamOffset = m_StreamOffset;
658
659   if ( ASDCP_SUCCESS(result) )
660     result = WriteEKLVPacket(FrameBuf, m_EssenceUL, Ctx, HMAC);
661
662   if ( ASDCP_SUCCESS(result) && add_index )
663     {
664       //create new Index and add it to the IndexTableSegment in the IndexPartition
665       IndexTableSegment::IndexEntry Entry;
666       Entry.StreamOffset = StreamOffset;
667       m_CurrentIndexBodyPartition->m_FramesWritten = m_FramesWritten;
668       m_CurrentIndexBodyPartition->PushIndexEntry(Entry);
669
670       //here we must check if the number of frames per partition are reached 
671       if(m_FramesWritten!=0 &&((m_FramesWritten+1) % m_PartitionSpace) == 0){
672         this->m_BodyOffset += m_StreamOffset;
673         //StreamOffset - Offset in bytes from the start of the Essence\r
674         //Container of first Essence Element in this Edit Unit of\r
675         //stored Essence within the Essence Container Stream
676         //this->m_StreamOffset = 0; ???
677
678         //Complete the Index-BodyPartion
679         result = CompleteIndexBodyPart();\r
680           //Create new BodyPartions for Essence and Index
681           result = CreateBodyPartPair();                 
682       }
683       //else do nothing, we must only insert the current frame
684       //else{}
685     }
686   m_FramesWritten++;
687   return result;
688 }
689
690
691 // Closes the MXF file, writing the index and other closing information.
692 //
693 ASDCP::Result_t
694 lf__Writer::Finalize()
695 {
696   Result_t result = RESULT_OK;
697
698   if ( ! m_State.Test_RUNNING() )
699     return RESULT_STATE;
700
701   m_State.Goto_FINAL();
702
703   //the last Frame was written, complete the BodyPartion(Index), after that write the MXF-Footer
704   result = CompleteIndexBodyPart();
705
706   if ( ASDCP_FAILURE(result) ){
707     return result;
708   }
709   return WriteMXFFooter();
710 }
711
712
713 //
714 class AS_02::JP2K::MXFWriter::h__Writer : public lf__Writer
715 {
716   ASDCP_NO_COPY_CONSTRUCT(h__Writer);
717   h__Writer();
718
719 public:
720   h__Writer(const Dictionary& d) : lf__Writer(d) {}
721 };
722
723
724 //------------------------------------------------------------------------------------------
725
726
727
728 AS_02::JP2K::MXFWriter::MXFWriter()
729 {
730 }
731
732 AS_02::JP2K::MXFWriter::~MXFWriter()
733 {
734 }
735
736 // Warning: direct manipulation of MXF structures can interfere
737 // with the normal operation of the wrapper.  Caveat emptor!
738 //
739 ASDCP::MXF::OPAtomHeader&
740 AS_02::JP2K::MXFWriter::OPAtomHeader()
741 {
742   if ( m_Writer.empty() )
743     {
744       assert(g_OPAtomHeader);
745       return *g_OPAtomHeader;
746     }
747
748   return m_Writer->m_HeaderPart;
749 }
750
751 // Warning: direct manipulation of MXF structures can interfere
752 // with the normal operation of the wrapper.  Caveat emptor!
753 //
754 /*
755 ASDCP::MXF::OPAtomIndexFooter&
756 AS_02::JP2K::MXFWriter::OPAtomIndexFooter()
757 {
758   if ( m_Writer.empty() )
759     {
760       assert(g_OPAtomIndexFooter);
761       return *g_OPAtomIndexFooter;
762     }
763
764   return m_Writer->m_FooterPart;
765 }
766 */
767
768 // Open the file for writing. The file must not exist. Returns error if
769 // the operation cannot be completed.
770 ASDCP::Result_t
771 AS_02::JP2K::MXFWriter::OpenWrite(const char* filename, const ASDCP::WriterInfo& Info,
772                                   const ASDCP::JP2K::PictureDescriptor& PDesc,
773                                   const IndexStrategy_t& Strategy,
774                                   const ui32_t& PartitionSpace,
775                                   const ui32_t& HeaderSize)
776 {
777   m_Writer = new AS_02::JP2K::MXFWriter::h__Writer(DefaultSMPTEDict());
778   m_Writer->m_Info = Info;
779
780   ASDCP::Result_t result = m_Writer->OpenWrite(filename, ASDCP::ESS_JPEG_2000, Strategy, PartitionSpace, HeaderSize);
781
782   if ( ASDCP_SUCCESS(result) )
783     result = m_Writer->SetSourceStream(PDesc, JP2K_PACKAGE_LABEL);
784
785   if ( ASDCP_FAILURE(result) )
786     m_Writer.release();
787
788   return result;
789 }
790
791
792 // Writes a frame of essence to the MXF file. If the optional AESEncContext
793 // argument is present, the essence is encrypted prior to writing.
794 // Fails if the file is not open, is finalized, or an operating system
795 // error occurs.
796 ASDCP::Result_t 
797 AS_02::JP2K::MXFWriter::WriteFrame(const ASDCP::JP2K::FrameBuffer& FrameBuf, AESEncContext* Ctx, HMACContext* HMAC)
798 {
799   if ( m_Writer.empty() )
800     return RESULT_INIT;
801
802   return m_Writer->WriteFrame(FrameBuf, true, Ctx, HMAC);
803 }
804
805 // Closes the MXF file, writing the index and other closing information.
806 ASDCP::Result_t
807 AS_02::JP2K::MXFWriter::Finalize()
808 {
809   if ( m_Writer.empty() )
810     return RESULT_INIT;
811
812   return m_Writer->Finalize();
813 }
814
815
816 //------------------------------------------------------------------------------------------
817
818 // standard method of reading a plaintext or encrypted frame
819 Result_t
820 lf__Reader::ReadEKLVFrame(ui32_t FrameNum, ASDCP::FrameBuffer& FrameBuf,
821                           const byte_t* EssenceUL, AESDecContext* Ctx, HMACContext* HMAC)
822 {
823   Result_t result = RESULT_OK;
824   IndexTableSegment::IndexEntry TmpEntry;
825
826   assert(m_pCurrentIndexPartition != NULL);
827
828   if( m_pCurrentIndexPartition->Lookup(FrameNum, TmpEntry, m_start_pos) == false)
829     {
830       m_start_pos = 0;
831
832       //compute new indexPartition for this FrameNum
833       for (ui32_t i = 0; i < m_BodyPartList.size(); i+=2 )
834         {
835           if( m_IndexStrategy == AS_02::IS_FOLLOW )
836             {
837               m_pCurrentBodyPartition = m_BodyPartList.at(i);
838               m_pCurrentIndexPartition = dynamic_cast<AS_02::MXF::OP1aIndexBodyPartion*> (m_BodyPartList.at(i+1));
839             }
840           else if( m_IndexStrategy == AS_02::IS_LEAD )
841             {
842               m_pCurrentIndexPartition = dynamic_cast<AS_02::MXF::OP1aIndexBodyPartion*> (m_BodyPartList.at(i));
843               m_pCurrentBodyPartition = m_BodyPartList.at(i+1);
844             }
845           else
846             {
847               return RESULT_FORMAT; //return error
848             }
849           
850           if( m_pCurrentIndexPartition == 0 )
851             return RESULT_FORMAT;
852           
853           if(m_pCurrentIndexPartition->Lookup(FrameNum, TmpEntry, m_start_pos))
854             {
855               if ( FrameNum % m_PartitionSpace == 0 )
856                 {
857                   ui64_t offset = m_pCurrentBodyPartition->ThisPartition - m_pCurrentBodyPartition->PreviousPartition;
858                   offset += m_pCurrentBodyPartition->ArchiveSize();//Offset for the BodyPartitionHeader - Partition::ArchiveSize();
859                   m_EssenceStart += offset;
860                 }
861               break;
862             }
863         }
864     }
865   
866   // get frame position and go read the frame's key and length
867   Kumu::fpos_t FilePosition = this->m_EssenceStart + TmpEntry.StreamOffset;
868
869   if ( FilePosition != m_LastPosition )
870     {
871       m_LastPosition = FilePosition;
872       result = m_File.Seek(FilePosition);
873     }
874
875   if( ASDCP_SUCCESS(result) )
876     result = ReadEKLVPacket(FrameNum, FrameNum + 1, FrameBuf, EssenceUL, Ctx, HMAC);
877
878   return result;
879 }
880
881 //
882 Result_t
883 lf__Reader::ReadEKLVPacket(ui32_t FrameNum, ui32_t SequenceNum, ASDCP::FrameBuffer& FrameBuf,
884                            const byte_t* EssenceUL, AESDecContext* Ctx, HMACContext* HMAC)
885 {
886   KLReader Reader;
887   Result_t result = Reader.ReadKLFromFile(m_File);
888
889   if ( ASDCP_FAILURE(result) )
890     return result;
891
892   UL Key(Reader.Key());
893   ui64_t PacketLength = Reader.Length();
894   m_LastPosition = m_LastPosition + Reader.KLLength() + PacketLength;
895   assert(m_Dict);
896
897   if ( memcmp(Key.Value(), m_Dict->ul(MDD_CryptEssence), Key.Size() - 1) == 0 )  // ignore the stream numbers
898     {
899       if ( ! m_Info.EncryptedEssence )
900         {
901           DefaultLogSink().Error("EKLV packet found, no Cryptographic Context in header.\n");
902           return RESULT_FORMAT;
903         }
904
905       // read encrypted triplet value into internal buffer
906       assert(PacketLength <= 0xFFFFFFFFL);
907       m_CtFrameBuf.Capacity((ui32_t) PacketLength);
908       ui32_t read_count;
909       result = m_File.Read(m_CtFrameBuf.Data(), (ui32_t) PacketLength,
910                            &read_count);
911
912       if ( ASDCP_FAILURE(result) )
913         return result;
914
915       if ( read_count != PacketLength )
916         {
917           DefaultLogSink().Error("read length is smaller than EKLV packet length.\n");
918           return RESULT_FORMAT;
919         }
920
921       m_CtFrameBuf.Size((ui32_t) PacketLength);
922
923       // should be const but mxflib::ReadBER is not
924       byte_t* ess_p = m_CtFrameBuf.Data();
925
926       // read context ID length
927       if ( ! Kumu::read_test_BER(&ess_p, UUIDlen) )
928         return RESULT_FORMAT;
929
930       // test the context ID
931       if ( memcmp(ess_p, m_Info.ContextID, UUIDlen) != 0 )
932         {
933           DefaultLogSink().Error("Packet's Cryptographic Context ID does not match the header.\n");
934           return RESULT_FORMAT;
935         }
936       ess_p += UUIDlen;
937
938       // read PlaintextOffset length
939       if ( ! Kumu::read_test_BER(&ess_p, sizeof(ui64_t)) )
940         return RESULT_FORMAT;
941
942       ui32_t PlaintextOffset = (ui32_t)KM_i64_BE(Kumu::cp2i<ui64_t>(ess_p));
943       ess_p += sizeof(ui64_t);
944
945       // read essence UL length
946       if ( ! Kumu::read_test_BER(&ess_p, SMPTE_UL_LENGTH) )
947         return RESULT_FORMAT;
948
949       // test essence UL
950       if ( memcmp(ess_p, EssenceUL, SMPTE_UL_LENGTH - 1) != 0 ) // ignore the stream number
951         {
952           char strbuf[IntBufferLen];
953           const MDDEntry* Entry = m_Dict->FindUL(Key.Value());
954           if ( Entry == 0 )
955             DefaultLogSink().Warn("Unexpected Essence UL found: %s.\n", Key.EncodeString(strbuf, IntBufferLen));
956           else
957             DefaultLogSink().Warn("Unexpected Essence UL found: %s.\n", Entry->name);
958           return RESULT_FORMAT;
959         }
960       ess_p += SMPTE_UL_LENGTH;
961
962       // read SourceLength length
963       if ( ! Kumu::read_test_BER(&ess_p, sizeof(ui64_t)) )
964         return RESULT_FORMAT;
965
966       ui32_t SourceLength = (ui32_t)KM_i64_BE(Kumu::cp2i<ui64_t>(ess_p));
967       ess_p += sizeof(ui64_t);
968       assert(SourceLength);
969
970       if ( FrameBuf.Capacity() < SourceLength )
971         {
972           DefaultLogSink().Error("FrameBuf.Capacity: %u SourceLength: %u\n", FrameBuf.Capacity(), SourceLength);
973           return RESULT_SMALLBUF;
974         }
975
976       ui32_t esv_length = calc_esv_length(SourceLength, PlaintextOffset);
977
978       // read ESV length
979       if ( ! Kumu::read_test_BER(&ess_p, esv_length) )
980         {
981           DefaultLogSink().Error("read_test_BER did not return %u\n", esv_length);
982           return RESULT_FORMAT;
983         }
984
985       ui32_t tmp_len = esv_length + (m_Info.UsesHMAC ? klv_intpack_size : 0);
986
987       if ( PacketLength < tmp_len )
988         {
989           DefaultLogSink().Error("Frame length is larger than EKLV packet length.\n");
990           return RESULT_FORMAT;
991         }
992
993       if ( Ctx )
994         {
995           // wrap the pointer and length as a FrameBuffer for use by
996           // DecryptFrameBuffer() and TestValues()
997           ASDCP::FrameBuffer TmpWrapper;
998           TmpWrapper.SetData(ess_p, tmp_len);
999           TmpWrapper.Size(tmp_len);
1000           TmpWrapper.SourceLength(SourceLength);
1001           TmpWrapper.PlaintextOffset(PlaintextOffset);
1002
1003           result = DecryptFrameBuffer(TmpWrapper, FrameBuf, Ctx);
1004           FrameBuf.FrameNumber(FrameNum);
1005
1006           // detect and test integrity pack
1007           if ( ASDCP_SUCCESS(result) && m_Info.UsesHMAC && HMAC )
1008             {
1009               IntegrityPack IntPack;
1010               result = IntPack.TestValues(TmpWrapper, m_Info.AssetUUID, SequenceNum, HMAC);
1011             }
1012         }
1013       else // return ciphertext to caller
1014         {
1015           if ( FrameBuf.Capacity() < tmp_len )
1016             {
1017               char intbuf[IntBufferLen];
1018               DefaultLogSink().Error("FrameBuf.Capacity: %u FrameLength: %s\n",
1019                                      FrameBuf.Capacity(), ui64sz(PacketLength, intbuf));
1020               return RESULT_SMALLBUF;
1021             }
1022
1023           memcpy(FrameBuf.Data(), ess_p, tmp_len);
1024           FrameBuf.Size(tmp_len);
1025           FrameBuf.FrameNumber(FrameNum);
1026           FrameBuf.SourceLength(SourceLength);
1027           FrameBuf.PlaintextOffset(PlaintextOffset);
1028         }
1029     }
1030   else if ( memcmp(Key.Value(), EssenceUL, Key.Size() - 1) == 0 ) // ignore the stream number
1031     { // read plaintext frame
1032       if ( FrameBuf.Capacity() < PacketLength )
1033         {
1034           char intbuf[IntBufferLen];
1035           DefaultLogSink().Error("FrameBuf.Capacity: %u FrameLength: %s\n",
1036                                  FrameBuf.Capacity(), ui64sz(PacketLength, intbuf));
1037           return RESULT_SMALLBUF;
1038         }
1039
1040       // read the data into the supplied buffer
1041       ui32_t read_count;
1042       assert(PacketLength <= 0xFFFFFFFFL);
1043       result = m_File.Read(FrameBuf.Data(), (ui32_t) PacketLength, &read_count);
1044
1045       if ( ASDCP_FAILURE(result) )
1046         return result;
1047
1048       if ( read_count != PacketLength )
1049         {
1050           char intbuf1[IntBufferLen];
1051           char intbuf2[IntBufferLen];
1052           DefaultLogSink().Error("read_count: %s != FrameLength: %s\n",
1053                                  ui64sz(read_count, intbuf1),
1054                                  ui64sz(PacketLength, intbuf2) );
1055
1056           return RESULT_READFAIL;
1057         }
1058
1059       FrameBuf.FrameNumber(FrameNum);
1060       FrameBuf.Size(read_count);
1061     }
1062   else
1063     {
1064       char strbuf[IntBufferLen];
1065       const MDDEntry* Entry = m_Dict->FindUL(Key.Value());
1066       if ( Entry == 0 )
1067         DefaultLogSink().Warn("Unexpected Essence UL found: %s.\n", Key.EncodeString(strbuf, IntBufferLen));
1068       else
1069         DefaultLogSink().Warn("Unexpected Essence UL found: %s.\n", Entry->name);
1070       return RESULT_FORMAT;
1071     }
1072
1073   return result;
1074 }
1075
1076
1077 // standard method of writing the header and footer of a completed MXF file
1078 //
1079 Result_t
1080 lf__Writer::WriteMXFFooter()
1081 {
1082   // Set top-level file package correctly for OP-Atom
1083
1084   //  m_MPTCSequence->Duration = m_MPTimecode->Duration = m_MPClSequence->Duration = m_MPClip->Duration = 
1085   //    m_FPTCSequence->Duration = m_FPTimecode->Duration = m_FPClSequence->Duration = m_FPClip->Duration = 
1086
1087   DurationElementList_t::iterator dli = m_DurationUpdateList.begin();
1088
1089   for (; dli != m_DurationUpdateList.end(); dli++ )
1090     **dli = m_FramesWritten;
1091
1092   m_EssenceDescriptor->ContainerDuration = m_FramesWritten;
1093   m_FooterPart.PreviousPartition = m_HeaderPart.m_RIP.PairArray.back().ByteOffset;
1094
1095   Kumu::fpos_t here = m_File.Tell();
1096   m_HeaderPart.m_RIP.PairArray.push_back(RIP::Pair(0, here)); // Last RIP Entry
1097   m_HeaderPart.FooterPartition = here;
1098
1099   assert(m_Dict);
1100   // re-label the partition
1101   UL OPAtomUL(m_Dict->ul(MDD_OP1a));
1102   m_HeaderPart.OperationalPattern = OPAtomUL;
1103   m_HeaderPart.m_Preface->OperationalPattern = m_HeaderPart.OperationalPattern;
1104
1105   m_FooterPart.OperationalPattern = m_HeaderPart.OperationalPattern;
1106   m_FooterPart.EssenceContainers = m_HeaderPart.EssenceContainers;
1107   m_FooterPart.FooterPartition = here;
1108   m_FooterPart.ThisPartition = here;
1109
1110   Result_t result = m_FooterPart.WriteToFile(m_File, m_FramesWritten);
1111
1112   if ( ASDCP_SUCCESS(result) )
1113     result = m_HeaderPart.m_RIP.WriteToFile(m_File);
1114
1115   if ( ASDCP_SUCCESS(result) )
1116     result = m_File.Seek(0);
1117
1118   if ( ASDCP_SUCCESS(result) )
1119     result = m_HeaderPart.WriteToFile(m_File, m_HeaderSize);
1120
1121   //update the value of FooterPartition in all Partitions
1122   std::vector<Partition*>::iterator iter = this->m_BodyPartList.begin();
1123   for (; iter != this->m_BodyPartList.end(); iter++ ){
1124     (*iter)->FooterPartition =  m_FooterPart.ThisPartition;
1125     if ( ASDCP_SUCCESS(result) )
1126       result = m_File.Seek((*iter)->ThisPartition);
1127     if ( ASDCP_SUCCESS(result) ){
1128       UL BodyUL(m_Dict->ul(MDD_ClosedCompleteBodyPartition));
1129       result = (*iter)->WriteToFile(m_File, BodyUL);
1130     }
1131   } 
1132
1133   m_File.Close();
1134   return result;
1135 }
1136
1137 //
1138 // end AS_02_JP2K.cpp
1139 //