asdcplib 1.9.45
[libdcp.git] / asdcplib / src / AS_DCP_JP2K.cpp
1 /*
2 Copyright (c) 2004-2012, 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_DCP_JP2k.cpp
28     \version $Id: AS_DCP_JP2K.cpp,v 1.54 2012/02/07 18:54:24 jhurst Exp $
29     \brief   AS-DCP library, JPEG 2000 essence reader and writer implementation
30 */
31
32 #include "AS_DCP_internal.h"
33 #include <iostream>
34 #include <iomanip>
35
36 using namespace ASDCP::JP2K;
37 using Kumu::GenRandomValue;
38
39 //------------------------------------------------------------------------------------------
40
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";
44
45 int s_exp_lookup[16] = { 0, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024,2048, 4096, 8192, 16384, 32768 };
46
47 //
48 std::ostream&
49 ASDCP::JP2K::operator << (std::ostream& strm, const PictureDescriptor& PDesc)
50 {
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;
66
67   strm << "-- JPEG 2000 Metadata --" << std::endl;
68   strm << "    ImageComponents:" << std::endl;
69   strm << "  bits  h-sep v-sep" << std::endl;
70
71   ui32_t i;
72   for ( i = 0; i < PDesc.Csize; i++ )
73     {
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
77            << std::endl;
78     }
79
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;
89
90
91   ui32_t precinct_set_size = 0;
92
93   for ( i = 0; PDesc.CodingStyleDefault.SPcod.PrecinctSize[i] != 0 && i < MaxPrecincts; i++ )
94     precinct_set_size++;
95
96   strm << "          Precincts: " << (short) precinct_set_size << std::endl;
97   strm << "precinct dimensions:" << std::endl;
98
99   for ( i = 0; i < precinct_set_size; i++ )
100     strm << "    " << i + 1 << ": " << s_exp_lookup[PDesc.CodingStyleDefault.SPcod.PrecinctSize[i]&0x0f] << " x "
101          << s_exp_lookup[(PDesc.CodingStyleDefault.SPcod.PrecinctSize[i]>>4)&0x0f] << std::endl;
102
103   strm << "               Sqcd: " << (short) PDesc.QuantizationDefault.Sqcd << std::endl;
104
105   char tmp_buf[MaxDefaults*2];
106   strm << "              SPqcd: " << Kumu::bin2hex(PDesc.QuantizationDefault.SPqcd, PDesc.QuantizationDefault.SPqcdLength, tmp_buf, MaxDefaults*2)
107        << std::endl;
108
109   return strm;
110 }
111
112 //
113 void
114 ASDCP::JP2K::PictureDescriptorDump(const PictureDescriptor& PDesc, FILE* stream)
115 {
116   if ( stream == 0 )
117     stream = stderr;
118
119   fprintf(stream, "\
120        AspectRatio: %d/%d\n\
121           EditRate: %d/%d\n\
122         SampleRate: %d/%d\n\
123        StoredWidth: %u\n\
124       StoredHeight: %u\n\
125              Rsize: %u\n\
126              Xsize: %u\n\
127              Ysize: %u\n\
128             XOsize: %u\n\
129             YOsize: %u\n\
130             XTsize: %u\n\
131             YTsize: %u\n\
132            XTOsize: %u\n\
133            YTOsize: %u\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,
138           PDesc.StoredWidth,
139           PDesc.StoredHeight,
140           PDesc.Rsize,
141           PDesc.Xsize,
142           PDesc.Ysize,
143           PDesc.XOsize,
144           PDesc.YOsize,
145           PDesc.XTsize,
146           PDesc.YTsize,
147           PDesc.XTOsize,
148           PDesc.YTOsize,
149           PDesc.ContainerDuration
150           );
151
152   fprintf(stream, "-- JPEG 2000 Metadata --\n");
153   fprintf(stream, "    ImageComponents:\n");
154   fprintf(stream, "  bits  h-sep v-sep\n");
155
156   ui32_t i;
157   for ( i = 0; i < PDesc.Csize; i++ )
158     {
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
163               );
164     }
165   
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)));
170
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);
177
178
179   ui32_t precinct_set_size = 0;
180
181   for ( i = 0; PDesc.CodingStyleDefault.SPcod.PrecinctSize[i] != 0 && i < MaxPrecincts; i++ )
182     precinct_set_size++;
183
184   fprintf(stream, "          Precincts: %hd\n", precinct_set_size);
185   fprintf(stream, "precinct dimensions:\n");
186
187   for ( i = 0; i < precinct_set_size; i++ )
188     fprintf(stream, "    %d: %d x %d\n", i + 1,
189             s_exp_lookup[PDesc.CodingStyleDefault.SPcod.PrecinctSize[i]&0x0f],
190             s_exp_lookup[(PDesc.CodingStyleDefault.SPcod.PrecinctSize[i]>>4)&0x0f]
191             );
192
193   fprintf(stream, "               Sqcd: %hd\n", PDesc.QuantizationDefault.Sqcd);
194
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)
199           );
200 }
201
202
203 //------------------------------------------------------------------------------------------
204 //
205 // hidden, internal implementation of JPEG 2000 reader
206
207
208 class lh__Reader : public ASDCP::h__Reader
209 {
210   RGBAEssenceDescriptor*        m_EssenceDescriptor;
211   JPEG2000PictureSubDescriptor* m_EssenceSubDescriptor;
212   ASDCP::Rational               m_EditRate;
213   ASDCP::Rational               m_SampleRate;
214   EssenceType_t                 m_Format;
215
216   ASDCP_NO_COPY_CONSTRUCT(lh__Reader);
217
218 public:
219   PictureDescriptor m_PDesc;        // codestream parameter list
220
221   lh__Reader(const Dictionary& d) :
222     ASDCP::h__Reader(d), m_EssenceDescriptor(0), m_EssenceSubDescriptor(0), m_Format(ESS_UNKNOWN) {}
223   Result_t    OpenRead(const char*, EssenceType_t);
224   Result_t    ReadFrame(ui32_t, JP2K::FrameBuffer&, AESDecContext*, HMACContext*);
225   Result_t    MD_to_JP2K_PDesc(JP2K::PictureDescriptor& PDesc);
226 };
227
228 //
229 ASDCP::Result_t
230 lh__Reader::MD_to_JP2K_PDesc(JP2K::PictureDescriptor& PDesc)
231 {
232   memset(&PDesc, 0, sizeof(PDesc));
233   MXF::RGBAEssenceDescriptor* PDescObj = (MXF::RGBAEssenceDescriptor*)m_EssenceDescriptor;
234
235   PDesc.EditRate           = m_EditRate;
236   PDesc.SampleRate         = m_SampleRate;
237   assert(PDescObj->ContainerDuration <= 0xFFFFFFFFL);
238   PDesc.ContainerDuration  = (ui32_t) PDescObj->ContainerDuration;
239   PDesc.StoredWidth        = PDescObj->StoredWidth;
240   PDesc.StoredHeight       = PDescObj->StoredHeight;
241   PDesc.AspectRatio        = PDescObj->AspectRatio;
242
243   if ( m_EssenceSubDescriptor != 0 )
244     {
245       PDesc.Rsize   = m_EssenceSubDescriptor->Rsize;
246       PDesc.Xsize   = m_EssenceSubDescriptor->Xsize;
247       PDesc.Ysize   = m_EssenceSubDescriptor->Ysize;
248       PDesc.XOsize  = m_EssenceSubDescriptor->XOsize;
249       PDesc.YOsize  = m_EssenceSubDescriptor->YOsize;
250       PDesc.XTsize  = m_EssenceSubDescriptor->XTsize;
251       PDesc.YTsize  = m_EssenceSubDescriptor->YTsize;
252       PDesc.XTOsize = m_EssenceSubDescriptor->XTOsize;
253       PDesc.YTOsize = m_EssenceSubDescriptor->YTOsize;
254       PDesc.Csize   = m_EssenceSubDescriptor->Csize;
255
256       // PictureComponentSizing
257       ui32_t tmp_size = m_EssenceSubDescriptor->PictureComponentSizing.Length();
258
259       if ( tmp_size == 17 ) // ( 2 * sizeof(ui32_t) ) + 3 components * 3 byte each
260         memcpy(&PDesc.ImageComponents, m_EssenceSubDescriptor->PictureComponentSizing.RoData() + 8, tmp_size - 8);
261
262       else
263         DefaultLogSink().Error("Unexpected PictureComponentSizing size: %u, should be 17\n", tmp_size);
264
265       // CodingStyleDefault
266       memset(&PDesc.CodingStyleDefault, 0, sizeof(CodingStyleDefault_t));
267       memcpy(&PDesc.CodingStyleDefault,
268              m_EssenceSubDescriptor->CodingStyleDefault.RoData(),
269              m_EssenceSubDescriptor->CodingStyleDefault.Length());
270
271       // QuantizationDefault
272       memset(&PDesc.QuantizationDefault, 0, sizeof(QuantizationDefault_t));
273       memcpy(&PDesc.QuantizationDefault,
274              m_EssenceSubDescriptor->QuantizationDefault.RoData(),
275              m_EssenceSubDescriptor->QuantizationDefault.Length());
276
277       PDesc.QuantizationDefault.SPqcdLength = m_EssenceSubDescriptor->QuantizationDefault.Length() - 1;
278     }
279
280   return RESULT_OK;
281 }
282
283 //
284 //
285 ASDCP::Result_t
286 lh__Reader::OpenRead(const char* filename, EssenceType_t type)
287 {
288   Result_t result = OpenMXFRead(filename);
289
290   if( ASDCP_SUCCESS(result) )
291     {
292       InterchangeObject* tmp_iobj = 0;
293       m_HeaderPart.GetMDObjectByType(OBJ_TYPE_ARGS(RGBAEssenceDescriptor), &tmp_iobj);
294       m_EssenceDescriptor = static_cast<RGBAEssenceDescriptor*>(tmp_iobj);
295
296       m_HeaderPart.GetMDObjectByType(OBJ_TYPE_ARGS(JPEG2000PictureSubDescriptor), &tmp_iobj);
297       m_EssenceSubDescriptor = static_cast<JPEG2000PictureSubDescriptor*>(tmp_iobj);
298
299       std::list<InterchangeObject*> ObjectList;
300       m_HeaderPart.GetMDObjectsByType(OBJ_TYPE_ARGS(Track), ObjectList);
301
302       if ( ObjectList.empty() )
303         {
304           DefaultLogSink().Error("MXF Metadata contains no Track Sets.\n");
305           return RESULT_FORMAT;
306         }
307
308       m_EditRate = ((Track*)ObjectList.front())->EditRate;
309       m_SampleRate = m_EssenceDescriptor->SampleRate;
310
311       if ( type == ASDCP::ESS_JPEG_2000 )
312         {
313           if ( m_EditRate != m_SampleRate )
314             {
315               DefaultLogSink().Warn("EditRate and SampleRate do not match (%.03f, %.03f).\n",
316                                     m_EditRate.Quotient(), m_SampleRate.Quotient());
317               
318               if ( m_EditRate == EditRate_24 && m_SampleRate == EditRate_48 ||
319                    m_EditRate == EditRate_25 && m_SampleRate == EditRate_50 ||
320                    m_EditRate == EditRate_30 && m_SampleRate == EditRate_60 ||
321                    m_EditRate == EditRate_48 && m_SampleRate == EditRate_96 ||
322                    m_EditRate == EditRate_50 && m_SampleRate == EditRate_100 ||
323                    m_EditRate == EditRate_60 && m_SampleRate == EditRate_120 )
324                 {
325                   DefaultLogSink().Debug("File may contain JPEG Interop stereoscopic images.\n");
326                   return RESULT_SFORMAT;
327                 }
328
329               return RESULT_FORMAT;
330             }
331         }
332       else if ( type == ASDCP::ESS_JPEG_2000_S )
333         {
334           if ( m_EditRate == EditRate_24 )
335             {
336               if ( m_SampleRate != EditRate_48 )
337                 {
338                   DefaultLogSink().Error("EditRate and SampleRate not correct for 24/48 stereoscopic essence.\n");
339                   return RESULT_FORMAT;
340                 }
341             }
342           else if ( m_EditRate == EditRate_25 )
343             {
344               if ( m_SampleRate != EditRate_50 )
345                 {
346                   DefaultLogSink().Error("EditRate and SampleRate not correct for 25/50 stereoscopic essence.\n");
347                   return RESULT_FORMAT;
348                 }
349             }
350           else if ( m_EditRate == EditRate_30 )
351             {
352               if ( m_SampleRate != EditRate_60 )
353                 {
354                   DefaultLogSink().Error("EditRate and SampleRate not correct for 30/60 stereoscopic essence.\n");
355                   return RESULT_FORMAT;
356                 }
357             }
358           else if ( m_EditRate == EditRate_48 )
359             {
360               if ( m_SampleRate != EditRate_96 )
361                 {
362                   DefaultLogSink().Error("EditRate and SampleRate not correct for 48/96 stereoscopic essence.\n");
363                   return RESULT_FORMAT;
364                 }
365             }
366           else if ( m_EditRate == EditRate_50 )
367             {
368               if ( m_SampleRate != EditRate_100 )
369                 {
370                   DefaultLogSink().Error("EditRate and SampleRate not correct for 50/100 stereoscopic essence.\n");
371                   return RESULT_FORMAT;
372                 }
373             }
374           else if ( m_EditRate == EditRate_60 )
375             {
376               if ( m_SampleRate != EditRate_120 )
377                 {
378                   DefaultLogSink().Error("EditRate and SampleRate not correct for 60/120 stereoscopic essence.\n");
379                   return RESULT_FORMAT;
380                 }
381             }
382           else
383             {
384               DefaultLogSink().Error("EditRate not correct for stereoscopic essence: %d/%d.\n",
385                                      m_EditRate.Numerator, m_EditRate.Denominator);
386               return RESULT_FORMAT;
387             }
388         }
389       else
390         {
391           DefaultLogSink().Error("'type' argument unexpected: %x\n", type);
392           return RESULT_STATE;
393         }
394
395       result = MD_to_JP2K_PDesc(m_PDesc);
396     }
397
398   if( ASDCP_SUCCESS(result) )
399     result = InitMXFIndex();
400
401   if( ASDCP_SUCCESS(result) )
402     result = InitInfo();
403
404   return result;
405 }
406
407 //
408 //
409 ASDCP::Result_t
410 lh__Reader::ReadFrame(ui32_t FrameNum, JP2K::FrameBuffer& FrameBuf,
411                       AESDecContext* Ctx, HMACContext* HMAC)
412 {
413   if ( ! m_File.IsOpen() )
414     return RESULT_INIT;
415
416   assert(m_Dict);
417   return ReadEKLVFrame(FrameNum, FrameBuf, m_Dict->ul(MDD_JPEG2000Essence), Ctx, HMAC);
418 }
419
420
421 //
422 class ASDCP::JP2K::MXFReader::h__Reader : public lh__Reader
423 {
424   ASDCP_NO_COPY_CONSTRUCT(h__Reader);
425   h__Reader();
426
427 public:
428   h__Reader(const Dictionary& d) : lh__Reader(d) {}
429 };
430
431
432
433 //------------------------------------------------------------------------------------------
434
435
436 //
437 void
438 ASDCP::JP2K::FrameBuffer::Dump(FILE* stream, ui32_t dump_len) const
439 {
440   if ( stream == 0 )
441     stream = stderr;
442
443   fprintf(stream, "Frame: %06u, %7u bytes", m_FrameNumber, m_Size);
444   
445   fputc('\n', stream);
446
447   if ( dump_len > 0 )
448     Kumu::hexdump(m_Data, dump_len, stream);
449 }
450
451
452 //------------------------------------------------------------------------------------------
453
454 ASDCP::JP2K::MXFReader::MXFReader()
455 {
456   m_Reader = new h__Reader(DefaultCompositeDict());
457 }
458
459
460 ASDCP::JP2K::MXFReader::~MXFReader()
461 {
462 }
463
464 // Warning: direct manipulation of MXF structures can interfere
465 // with the normal operation of the wrapper.  Caveat emptor!
466 //
467 ASDCP::MXF::OPAtomHeader&
468 ASDCP::JP2K::MXFReader::OPAtomHeader()
469 {
470   if ( m_Reader.empty() )
471     {
472       assert(g_OPAtomHeader);
473       return *g_OPAtomHeader;
474     }
475
476   return m_Reader->m_HeaderPart;
477 }
478
479 // Warning: direct manipulation of MXF structures can interfere
480 // with the normal operation of the wrapper.  Caveat emptor!
481 //
482 ASDCP::MXF::OPAtomIndexFooter&
483 ASDCP::JP2K::MXFReader::OPAtomIndexFooter()
484 {
485   if ( m_Reader.empty() )
486     {
487       assert(g_OPAtomIndexFooter);
488       return *g_OPAtomIndexFooter;
489     }
490
491   return m_Reader->m_FooterPart;
492 }
493
494 // Open the file for reading. The file must exist. Returns error if the
495 // operation cannot be completed.
496 ASDCP::Result_t
497 ASDCP::JP2K::MXFReader::OpenRead(const char* filename) const
498 {
499   return m_Reader->OpenRead(filename, ASDCP::ESS_JPEG_2000);
500 }
501
502 //
503 ASDCP::Result_t
504 ASDCP::JP2K::MXFReader::ReadFrame(ui32_t FrameNum, FrameBuffer& FrameBuf,
505                                    AESDecContext* Ctx, HMACContext* HMAC) const
506 {
507   if ( m_Reader && m_Reader->m_File.IsOpen() )
508     return m_Reader->ReadFrame(FrameNum, FrameBuf, Ctx, HMAC);
509
510   return RESULT_INIT;
511 }
512
513
514 // Fill the struct with the values from the file's header.
515 // Returns RESULT_INIT if the file is not open.
516 ASDCP::Result_t
517 ASDCP::JP2K::MXFReader::FillPictureDescriptor(PictureDescriptor& PDesc) const
518 {
519   if ( m_Reader && m_Reader->m_File.IsOpen() )
520     {
521       PDesc = m_Reader->m_PDesc;
522       return RESULT_OK;
523     }
524
525   return RESULT_INIT;
526 }
527
528
529 // Fill the struct with the values from the file's header.
530 // Returns RESULT_INIT if the file is not open.
531 ASDCP::Result_t
532 ASDCP::JP2K::MXFReader::FillWriterInfo(WriterInfo& Info) const
533 {
534   if ( m_Reader && m_Reader->m_File.IsOpen() )
535     {
536       Info = m_Reader->m_Info;
537       return RESULT_OK;
538     }
539
540   return RESULT_INIT;
541 }
542
543 //
544 void
545 ASDCP::JP2K::MXFReader::DumpHeaderMetadata(FILE* stream) const
546 {
547   if ( m_Reader->m_File.IsOpen() )
548     m_Reader->m_HeaderPart.Dump(stream);
549 }
550
551
552 //
553 void
554 ASDCP::JP2K::MXFReader::DumpIndex(FILE* stream) const
555 {
556   if ( m_Reader->m_File.IsOpen() )
557     m_Reader->m_FooterPart.Dump(stream);
558 }
559
560 //
561 ASDCP::Result_t
562 ASDCP::JP2K::MXFReader::Close() const
563 {
564   if ( m_Reader && m_Reader->m_File.IsOpen() )
565     {
566       m_Reader->Close();
567       return RESULT_OK;
568     }
569
570   return RESULT_INIT;
571 }
572
573
574 //------------------------------------------------------------------------------------------
575
576
577 class ASDCP::JP2K::MXFSReader::h__SReader : public lh__Reader
578 {
579   ui32_t m_StereoFrameReady;
580
581 public:
582   h__SReader(const Dictionary& d) : lh__Reader(d), m_StereoFrameReady(0xffffffff) {}
583
584   //
585   Result_t ReadFrame(ui32_t FrameNum, StereoscopicPhase_t phase, FrameBuffer& FrameBuf,
586                      AESDecContext* Ctx, HMACContext* HMAC)
587   {
588     // look up frame index node
589     IndexTableSegment::IndexEntry TmpEntry;
590
591     if ( ASDCP_FAILURE(m_FooterPart.Lookup(FrameNum, TmpEntry)) )
592       {
593         DefaultLogSink().Error("Frame value out of range: %u\n", FrameNum);
594         return RESULT_RANGE;
595       }
596
597     // get frame position
598     Kumu::fpos_t FilePosition = m_EssenceStart + TmpEntry.StreamOffset;
599     Result_t result = RESULT_OK;
600
601     if ( phase == SP_LEFT )
602       {    
603         if ( FilePosition != m_LastPosition )
604           {
605             m_LastPosition = FilePosition;
606             result = m_File.Seek(FilePosition);
607           }
608
609         // the call to ReadEKLVPacket() will leave the file on an R frame
610         m_StereoFrameReady = FrameNum;
611       }
612     else if ( phase == SP_RIGHT )
613       {
614         if ( m_StereoFrameReady != FrameNum )
615           {
616             // the file is not already positioned, we must do some work
617             // seek to the companion SP_LEFT frame and read the frame's key and length
618             if ( FilePosition != m_LastPosition )
619               {
620                 m_LastPosition = FilePosition;
621                 result = m_File.Seek(FilePosition);
622               }
623
624             KLReader Reader;
625             result = Reader.ReadKLFromFile(m_File);
626
627             if ( ASDCP_SUCCESS(result) )
628               {
629                 // skip over the companion SP_LEFT frame
630                 Kumu::fpos_t new_pos = FilePosition + SMPTE_UL_LENGTH + Reader.KLLength() + Reader.Length();
631                 result = m_File.Seek(new_pos);
632               }
633           }
634
635         // the call to ReadEKLVPacket() will leave the file not on an R frame
636         m_StereoFrameReady = 0xffffffff;
637       }
638     else
639       {
640         DefaultLogSink().Error("Unexpected stereoscopic phase value: %u\n", phase);
641         return RESULT_STATE;
642       }
643
644     if( ASDCP_SUCCESS(result) )
645       {
646         ui32_t SequenceNum = FrameNum * 2;
647         SequenceNum += ( phase == SP_RIGHT ) ? 2 : 1;
648         assert(m_Dict);
649         result = ReadEKLVPacket(FrameNum, SequenceNum, FrameBuf, m_Dict->ul(MDD_JPEG2000Essence), Ctx, HMAC);
650       }
651
652     return result;
653   }
654 };
655
656
657
658 ASDCP::JP2K::MXFSReader::MXFSReader()
659 {
660   m_Reader = new h__SReader(DefaultCompositeDict());
661 }
662
663
664 ASDCP::JP2K::MXFSReader::~MXFSReader()
665 {
666 }
667
668 // Warning: direct manipulation of MXF structures can interfere
669 // with the normal operation of the wrapper.  Caveat emptor!
670 //
671 ASDCP::MXF::OPAtomHeader&
672 ASDCP::JP2K::MXFSReader::OPAtomHeader()
673 {
674   if ( m_Reader.empty() )
675     {
676       assert(g_OPAtomHeader);
677       return *g_OPAtomHeader;
678     }
679
680   return m_Reader->m_HeaderPart;
681 }
682
683 // Warning: direct manipulation of MXF structures can interfere
684 // with the normal operation of the wrapper.  Caveat emptor!
685 //
686 ASDCP::MXF::OPAtomIndexFooter&
687 ASDCP::JP2K::MXFSReader::OPAtomIndexFooter()
688 {
689   if ( m_Reader.empty() )
690     {
691       assert(g_OPAtomIndexFooter);
692       return *g_OPAtomIndexFooter;
693     }
694
695   return m_Reader->m_FooterPart;
696 }
697
698 // Open the file for reading. The file must exist. Returns error if the
699 // operation cannot be completed.
700 ASDCP::Result_t
701 ASDCP::JP2K::MXFSReader::OpenRead(const char* filename) const
702 {
703   return m_Reader->OpenRead(filename, ASDCP::ESS_JPEG_2000_S);
704 }
705
706 //
707 ASDCP::Result_t
708 ASDCP::JP2K::MXFSReader::ReadFrame(ui32_t FrameNum, SFrameBuffer& FrameBuf, AESDecContext* Ctx, HMACContext* HMAC) const
709 {
710   Result_t result = RESULT_INIT;
711
712   if ( m_Reader && m_Reader->m_File.IsOpen() )
713     {
714       result = m_Reader->ReadFrame(FrameNum, SP_LEFT, FrameBuf.Left, Ctx, HMAC);
715
716       if ( ASDCP_SUCCESS(result) )
717         result = m_Reader->ReadFrame(FrameNum, SP_RIGHT, FrameBuf.Right, Ctx, HMAC);
718     }
719
720   return result;
721 }
722
723 //
724 ASDCP::Result_t
725 ASDCP::JP2K::MXFSReader::ReadFrame(ui32_t FrameNum, StereoscopicPhase_t phase, FrameBuffer& FrameBuf,
726                                    AESDecContext* Ctx, HMACContext* HMAC) const
727 {
728   if ( m_Reader && m_Reader->m_File.IsOpen() )
729     return m_Reader->ReadFrame(FrameNum, phase, FrameBuf, Ctx, HMAC);
730
731   return RESULT_INIT;
732 }
733
734 // Fill the struct with the values from the file's header.
735 // Returns RESULT_INIT if the file is not open.
736 ASDCP::Result_t
737 ASDCP::JP2K::MXFSReader::FillPictureDescriptor(PictureDescriptor& PDesc) const
738 {
739   if ( m_Reader && m_Reader->m_File.IsOpen() )
740     {
741       PDesc = m_Reader->m_PDesc;
742       return RESULT_OK;
743     }
744
745   return RESULT_INIT;
746 }
747
748
749 // Fill the struct with the values from the file's header.
750 // Returns RESULT_INIT if the file is not open.
751 ASDCP::Result_t
752 ASDCP::JP2K::MXFSReader::FillWriterInfo(WriterInfo& Info) const
753 {
754   if ( m_Reader && m_Reader->m_File.IsOpen() )
755     {
756       Info = m_Reader->m_Info;
757       return RESULT_OK;
758     }
759
760   return RESULT_INIT;
761 }
762
763 //
764 void
765 ASDCP::JP2K::MXFSReader::DumpHeaderMetadata(FILE* stream) const
766 {
767   if ( m_Reader->m_File.IsOpen() )
768     m_Reader->m_HeaderPart.Dump(stream);
769 }
770
771
772 //
773 void
774 ASDCP::JP2K::MXFSReader::DumpIndex(FILE* stream) const
775 {
776   if ( m_Reader->m_File.IsOpen() )
777     m_Reader->m_FooterPart.Dump(stream);
778 }
779
780 //
781 ASDCP::Result_t
782 ASDCP::JP2K::MXFSReader::Close() const
783 {
784   if ( m_Reader && m_Reader->m_File.IsOpen() )
785     {
786       m_Reader->Close();
787       return RESULT_OK;
788     }
789
790   return RESULT_INIT;
791 }
792
793
794 //------------------------------------------------------------------------------------------
795
796
797 //
798 class lh__Writer : public ASDCP::h__Writer
799 {
800   ASDCP_NO_COPY_CONSTRUCT(lh__Writer);
801   lh__Writer();
802
803   JPEG2000PictureSubDescriptor* m_EssenceSubDescriptor;
804
805 public:
806   PictureDescriptor m_PDesc;
807   byte_t            m_EssenceUL[SMPTE_UL_LENGTH];
808
809   lh__Writer(const Dictionary& d) : ASDCP::h__Writer(d), m_EssenceSubDescriptor(0) {
810     memset(m_EssenceUL, 0, SMPTE_UL_LENGTH);
811   }
812
813   ~lh__Writer(){}
814
815   Result_t OpenWrite(const char*, EssenceType_t type, ui32_t HeaderSize);
816   Result_t SetSourceStream(const PictureDescriptor&, const std::string& label,
817                            ASDCP::Rational LocalEditRate = ASDCP::Rational(0,0));
818   Result_t WriteFrame(const JP2K::FrameBuffer&, bool add_index, AESEncContext*, HMACContext*);
819   Result_t Finalize();
820   Result_t JP2K_PDesc_to_MD(JP2K::PictureDescriptor& PDesc);
821 };
822
823 const int VideoLineMapSize = 16; // See SMPTE 377M D.2.1
824 const int PixelLayoutSize = 8*2; // See SMPTE 377M D.2.3
825 static const byte_t s_PixelLayoutXYZ[PixelLayoutSize] = { 0xd8, 0x0c, 0xd9, 0x0c, 0xda, 0x0c, 0x00 };
826
827 //
828 ASDCP::Result_t
829 lh__Writer::JP2K_PDesc_to_MD(JP2K::PictureDescriptor& PDesc)
830 {
831   assert(m_EssenceDescriptor);
832   assert(m_EssenceSubDescriptor);
833   MXF::RGBAEssenceDescriptor* PDescObj = (MXF::RGBAEssenceDescriptor*)m_EssenceDescriptor;
834
835   PDescObj->ContainerDuration = PDesc.ContainerDuration;
836   PDescObj->SampleRate = PDesc.EditRate;
837   PDescObj->FrameLayout = 0;
838   PDescObj->StoredWidth = PDesc.StoredWidth;
839   PDescObj->StoredHeight = PDesc.StoredHeight;
840   PDescObj->AspectRatio = PDesc.AspectRatio;
841
842   //  if ( m_Info.LabelSetType == LS_MXF_SMPTE )
843   //    {
844   // PictureEssenceCoding UL = 
845   // Video Line Map       ui32_t[VideoLineMapSize] = { 2, 4, 0, 0 }
846   // CaptureGamma         UL = 
847   // ComponentMaxRef      ui32_t = 4095
848   // ComponentMinRef      ui32_t = 0
849   // PixelLayout          byte_t[PixelLayoutSize] = s_PixelLayoutXYZ
850   //    }
851
852   assert(m_Dict);
853   if ( PDesc.StoredWidth < 2049 )
854     {
855       PDescObj->PictureEssenceCoding.Set(m_Dict->ul(MDD_JP2KEssenceCompression_2K));
856       m_EssenceSubDescriptor->Rsize = 3;
857     }
858   else
859     {
860       PDescObj->PictureEssenceCoding.Set(m_Dict->ul(MDD_JP2KEssenceCompression_4K));
861       m_EssenceSubDescriptor->Rsize = 4;
862     }
863
864   m_EssenceSubDescriptor->Xsize = PDesc.Xsize;
865   m_EssenceSubDescriptor->Ysize = PDesc.Ysize;
866   m_EssenceSubDescriptor->XOsize = PDesc.XOsize;
867   m_EssenceSubDescriptor->YOsize = PDesc.YOsize;
868   m_EssenceSubDescriptor->XTsize = PDesc.XTsize;
869   m_EssenceSubDescriptor->YTsize = PDesc.YTsize;
870   m_EssenceSubDescriptor->XTOsize = PDesc.XTOsize;
871   m_EssenceSubDescriptor->YTOsize = PDesc.YTOsize;
872   m_EssenceSubDescriptor->Csize = PDesc.Csize;
873
874   const ui32_t tmp_buffer_len = 1024;
875   byte_t tmp_buffer[tmp_buffer_len];
876
877   *(ui32_t*)tmp_buffer = KM_i32_BE(MaxComponents); // three components
878   *(ui32_t*)(tmp_buffer+4) = KM_i32_BE(sizeof(ASDCP::JP2K::ImageComponent_t));
879   memcpy(tmp_buffer + 8, &PDesc.ImageComponents, sizeof(ASDCP::JP2K::ImageComponent_t) * MaxComponents);
880
881   const ui32_t pcomp_size = (sizeof(int) * 2) + (sizeof(ASDCP::JP2K::ImageComponent_t) * MaxComponents);
882   memcpy(m_EssenceSubDescriptor->PictureComponentSizing.Data(), tmp_buffer, pcomp_size);
883   m_EssenceSubDescriptor->PictureComponentSizing.Length(pcomp_size);
884
885   ui32_t precinct_set_size = 0, i;
886   for ( i = 0; PDesc.CodingStyleDefault.SPcod.PrecinctSize[i] != 0 && i < MaxPrecincts; i++ )
887     precinct_set_size++;
888
889   ui32_t csd_size = sizeof(CodingStyleDefault_t) - MaxPrecincts + precinct_set_size;
890   memcpy(m_EssenceSubDescriptor->CodingStyleDefault.Data(), &PDesc.CodingStyleDefault, csd_size);
891   m_EssenceSubDescriptor->CodingStyleDefault.Length(csd_size);
892
893   ui32_t qdflt_size = PDesc.QuantizationDefault.SPqcdLength + 1;
894   memcpy(m_EssenceSubDescriptor->QuantizationDefault.Data(), &PDesc.QuantizationDefault, qdflt_size);
895   m_EssenceSubDescriptor->QuantizationDefault.Length(qdflt_size);
896
897   return RESULT_OK;
898 }
899
900
901 // Open the file for writing. The file must not exist. Returns error if
902 // the operation cannot be completed.
903 ASDCP::Result_t
904 lh__Writer::OpenWrite(const char* filename, EssenceType_t type, ui32_t HeaderSize)
905 {
906   if ( ! m_State.Test_BEGIN() )
907     return RESULT_STATE;
908
909   Result_t result = m_File.OpenWrite(filename);
910
911   if ( ASDCP_SUCCESS(result) )
912     {
913       m_HeaderSize = HeaderSize;
914       RGBAEssenceDescriptor* tmp_rgba = new RGBAEssenceDescriptor(m_Dict);
915       tmp_rgba->ComponentMaxRef = 4095;
916       tmp_rgba->ComponentMinRef = 0;
917
918       m_EssenceDescriptor = tmp_rgba;
919       m_EssenceSubDescriptor = new JPEG2000PictureSubDescriptor(m_Dict);
920       m_EssenceSubDescriptorList.push_back((InterchangeObject*)m_EssenceSubDescriptor);
921
922       GenRandomValue(m_EssenceSubDescriptor->InstanceUID);
923       m_EssenceDescriptor->SubDescriptors.push_back(m_EssenceSubDescriptor->InstanceUID);
924
925       if ( type == ASDCP::ESS_JPEG_2000_S && m_Info.LabelSetType == LS_MXF_SMPTE )
926         {
927           InterchangeObject* StereoSubDesc = new StereoscopicPictureSubDescriptor(m_Dict);
928           m_EssenceSubDescriptorList.push_back(StereoSubDesc);
929           GenRandomValue(StereoSubDesc->InstanceUID);
930           m_EssenceDescriptor->SubDescriptors.push_back(StereoSubDesc->InstanceUID);
931         }
932
933       result = m_State.Goto_INIT();
934     }
935
936   return result;
937 }
938
939 // Automatically sets the MXF file's metadata from the first jpeg codestream stream.
940 ASDCP::Result_t
941 lh__Writer::SetSourceStream(const PictureDescriptor& PDesc, const std::string& label, ASDCP::Rational LocalEditRate)
942 {
943   assert(m_Dict);
944   if ( ! m_State.Test_INIT() )
945     return RESULT_STATE;
946
947   if ( LocalEditRate == ASDCP::Rational(0,0) )
948     LocalEditRate = PDesc.EditRate;
949
950   m_PDesc = PDesc;
951   Result_t result = JP2K_PDesc_to_MD(m_PDesc);
952
953   if ( ASDCP_SUCCESS(result) )
954     {
955       memcpy(m_EssenceUL, m_Dict->ul(MDD_JPEG2000Essence), SMPTE_UL_LENGTH);
956       m_EssenceUL[SMPTE_UL_LENGTH-1] = 1; // first (and only) essence container
957       result = m_State.Goto_READY();
958     }
959
960   if ( ASDCP_SUCCESS(result) )
961     {
962       ui32_t TCFrameRate = ( m_PDesc.EditRate == EditRate_23_98  ) ? 24 : m_PDesc.EditRate.Numerator;
963
964       result = WriteMXFHeader(label, UL(m_Dict->ul(MDD_JPEG_2000Wrapping)),
965                               PICT_DEF_LABEL, UL(m_EssenceUL), UL(m_Dict->ul(MDD_PictureDataDef)),
966                               LocalEditRate, TCFrameRate);
967     }
968
969   return result;
970 }
971
972 // Writes a frame of essence to the MXF file. If the optional AESEncContext
973 // argument is present, the essence is encrypted prior to writing.
974 // Fails if the file is not open, is finalized, or an operating system
975 // error occurs.
976 //
977 ASDCP::Result_t
978 lh__Writer::WriteFrame(const JP2K::FrameBuffer& FrameBuf, bool add_index,
979                        AESEncContext* Ctx, HMACContext* HMAC)
980 {
981   Result_t result = RESULT_OK;
982
983   if ( m_State.Test_READY() )
984     result = m_State.Goto_RUNNING(); // first time through
985  
986   ui64_t StreamOffset = m_StreamOffset;
987
988   if ( ASDCP_SUCCESS(result) )
989     result = WriteEKLVPacket(FrameBuf, m_EssenceUL, Ctx, HMAC);
990
991   if ( ASDCP_SUCCESS(result) && add_index )
992     {  
993       IndexTableSegment::IndexEntry Entry;
994       Entry.StreamOffset = StreamOffset;
995       m_FooterPart.PushIndexEntry(Entry);
996     }
997
998   m_FramesWritten++;
999   return result;
1000 }
1001
1002
1003 // Closes the MXF file, writing the index and other closing information.
1004 //
1005 ASDCP::Result_t
1006 lh__Writer::Finalize()
1007 {
1008   if ( ! m_State.Test_RUNNING() )
1009     return RESULT_STATE;
1010
1011   m_State.Goto_FINAL();
1012
1013   return WriteMXFFooter();
1014 }
1015
1016
1017 //
1018 class ASDCP::JP2K::MXFWriter::h__Writer : public lh__Writer
1019 {
1020   ASDCP_NO_COPY_CONSTRUCT(h__Writer);
1021   h__Writer();
1022
1023 public:
1024   h__Writer(const Dictionary& d) : lh__Writer(d) {}
1025 };
1026
1027
1028 //------------------------------------------------------------------------------------------
1029
1030
1031
1032 ASDCP::JP2K::MXFWriter::MXFWriter()
1033 {
1034 }
1035
1036 ASDCP::JP2K::MXFWriter::~MXFWriter()
1037 {
1038 }
1039
1040 // Warning: direct manipulation of MXF structures can interfere
1041 // with the normal operation of the wrapper.  Caveat emptor!
1042 //
1043 ASDCP::MXF::OPAtomHeader&
1044 ASDCP::JP2K::MXFWriter::OPAtomHeader()
1045 {
1046   if ( m_Writer.empty() )
1047     {
1048       assert(g_OPAtomHeader);
1049       return *g_OPAtomHeader;
1050     }
1051
1052   return m_Writer->m_HeaderPart;
1053 }
1054
1055 // Warning: direct manipulation of MXF structures can interfere
1056 // with the normal operation of the wrapper.  Caveat emptor!
1057 //
1058 ASDCP::MXF::OPAtomIndexFooter&
1059 ASDCP::JP2K::MXFWriter::OPAtomIndexFooter()
1060 {
1061   if ( m_Writer.empty() )
1062     {
1063       assert(g_OPAtomIndexFooter);
1064       return *g_OPAtomIndexFooter;
1065     }
1066
1067   return m_Writer->m_FooterPart;
1068 }
1069
1070 // Open the file for writing. The file must not exist. Returns error if
1071 // the operation cannot be completed.
1072 ASDCP::Result_t
1073 ASDCP::JP2K::MXFWriter::OpenWrite(const char* filename, const WriterInfo& Info,
1074                                   const PictureDescriptor& PDesc, ui32_t HeaderSize)
1075 {
1076   if ( Info.LabelSetType == LS_MXF_SMPTE )
1077     m_Writer = new h__Writer(DefaultSMPTEDict());
1078   else
1079     m_Writer = new h__Writer(DefaultInteropDict());
1080
1081   m_Writer->m_Info = Info;
1082
1083   Result_t result = m_Writer->OpenWrite(filename, ASDCP::ESS_JPEG_2000, HeaderSize);
1084
1085   if ( ASDCP_SUCCESS(result) )
1086     result = m_Writer->SetSourceStream(PDesc, JP2K_PACKAGE_LABEL);
1087
1088   if ( ASDCP_FAILURE(result) )
1089     m_Writer.release();
1090
1091   return result;
1092 }
1093
1094
1095 // Writes a frame of essence to the MXF file. If the optional AESEncContext
1096 // argument is present, the essence is encrypted prior to writing.
1097 // Fails if the file is not open, is finalized, or an operating system
1098 // error occurs.
1099 ASDCP::Result_t
1100 ASDCP::JP2K::MXFWriter::WriteFrame(const FrameBuffer& FrameBuf, AESEncContext* Ctx, HMACContext* HMAC)
1101 {
1102   if ( m_Writer.empty() )
1103     return RESULT_INIT;
1104
1105   return m_Writer->WriteFrame(FrameBuf, true, Ctx, HMAC);
1106 }
1107
1108 // Closes the MXF file, writing the index and other closing information.
1109 ASDCP::Result_t
1110 ASDCP::JP2K::MXFWriter::Finalize()
1111 {
1112   if ( m_Writer.empty() )
1113     return RESULT_INIT;
1114
1115   return m_Writer->Finalize();
1116 }
1117
1118
1119 //------------------------------------------------------------------------------------------
1120 //
1121
1122 //
1123 class ASDCP::JP2K::MXFSWriter::h__SWriter : public lh__Writer
1124 {
1125   ASDCP_NO_COPY_CONSTRUCT(h__SWriter);
1126   h__SWriter();
1127   StereoscopicPhase_t m_NextPhase;
1128
1129 public:
1130   h__SWriter(const Dictionary& d) : lh__Writer(d), m_NextPhase(SP_LEFT) {}
1131
1132   //
1133   Result_t WriteFrame(const FrameBuffer& FrameBuf, StereoscopicPhase_t phase,
1134                       AESEncContext* Ctx, HMACContext* HMAC)
1135   {
1136     if ( m_NextPhase != phase )
1137       return RESULT_SPHASE;
1138
1139     if ( phase == SP_LEFT )
1140       {
1141         m_NextPhase = SP_RIGHT;
1142         return lh__Writer::WriteFrame(FrameBuf, true, Ctx, HMAC);
1143       }
1144
1145     m_NextPhase = SP_LEFT;
1146     return lh__Writer::WriteFrame(FrameBuf, false, Ctx, HMAC);
1147   }
1148
1149   //
1150   Result_t Finalize()
1151   {
1152     if ( m_NextPhase != SP_LEFT )
1153       return RESULT_SPHASE;
1154
1155     assert( m_FramesWritten % 2 == 0 );
1156     m_FramesWritten /= 2;
1157     return lh__Writer::Finalize();
1158   }
1159 };
1160
1161
1162 //
1163 ASDCP::JP2K::MXFSWriter::MXFSWriter()
1164 {
1165 }
1166
1167 ASDCP::JP2K::MXFSWriter::~MXFSWriter()
1168 {
1169 }
1170
1171 // Warning: direct manipulation of MXF structures can interfere
1172 // with the normal operation of the wrapper.  Caveat emptor!
1173 //
1174 ASDCP::MXF::OPAtomHeader&
1175 ASDCP::JP2K::MXFSWriter::OPAtomHeader()
1176 {
1177   if ( m_Writer.empty() )
1178     {
1179       assert(g_OPAtomHeader);
1180       return *g_OPAtomHeader;
1181     }
1182
1183   return m_Writer->m_HeaderPart;
1184 }
1185
1186 // Warning: direct manipulation of MXF structures can interfere
1187 // with the normal operation of the wrapper.  Caveat emptor!
1188 //
1189 ASDCP::MXF::OPAtomIndexFooter&
1190 ASDCP::JP2K::MXFSWriter::OPAtomIndexFooter()
1191 {
1192   if ( m_Writer.empty() )
1193     {
1194       assert(g_OPAtomIndexFooter);
1195       return *g_OPAtomIndexFooter;
1196     }
1197
1198   return m_Writer->m_FooterPart;
1199 }
1200
1201 // Open the file for writing. The file must not exist. Returns error if
1202 // the operation cannot be completed.
1203 ASDCP::Result_t
1204 ASDCP::JP2K::MXFSWriter::OpenWrite(const char* filename, const WriterInfo& Info,
1205                                    const PictureDescriptor& PDesc, ui32_t HeaderSize)
1206 {
1207   if ( Info.LabelSetType == LS_MXF_SMPTE )
1208     m_Writer = new h__SWriter(DefaultSMPTEDict());
1209   else
1210     m_Writer = new h__SWriter(DefaultInteropDict());
1211
1212   if ( PDesc.EditRate != ASDCP::EditRate_24
1213        && PDesc.EditRate != ASDCP::EditRate_25
1214        && PDesc.EditRate != ASDCP::EditRate_30
1215        && PDesc.EditRate != ASDCP::EditRate_48
1216        && PDesc.EditRate != ASDCP::EditRate_50
1217        && PDesc.EditRate != ASDCP::EditRate_60 )
1218     {
1219       DefaultLogSink().Error("Stereoscopic wrapping requires 24, 25, 30, 48, 50 or 60 fps input streams.\n");
1220       return RESULT_FORMAT;
1221     }
1222
1223   if ( PDesc.StoredWidth > 2048 )
1224     DefaultLogSink().Warn("Wrapping non-standard 4K stereoscopic content. I hope you know what you are doing!\n");
1225
1226   m_Writer->m_Info = Info;
1227
1228   Result_t result = m_Writer->OpenWrite(filename, ASDCP::ESS_JPEG_2000_S, HeaderSize);
1229
1230   if ( ASDCP_SUCCESS(result) )
1231     {
1232       PictureDescriptor TmpPDesc = PDesc;
1233
1234       if ( PDesc.EditRate == ASDCP::EditRate_24 )
1235         TmpPDesc.EditRate = ASDCP::EditRate_48;
1236
1237       else if ( PDesc.EditRate == ASDCP::EditRate_25 )
1238         TmpPDesc.EditRate = ASDCP::EditRate_50;
1239
1240       else if ( PDesc.EditRate == ASDCP::EditRate_30 )
1241         TmpPDesc.EditRate = ASDCP::EditRate_60;
1242
1243       else if ( PDesc.EditRate == ASDCP::EditRate_48 )
1244         TmpPDesc.EditRate = ASDCP::EditRate_96;
1245
1246       else if ( PDesc.EditRate == ASDCP::EditRate_50 )
1247         TmpPDesc.EditRate = ASDCP::EditRate_100;
1248
1249       else if ( PDesc.EditRate == ASDCP::EditRate_60 )
1250         TmpPDesc.EditRate = ASDCP::EditRate_120;
1251
1252       result = m_Writer->SetSourceStream(TmpPDesc, JP2K_S_PACKAGE_LABEL, PDesc.EditRate);
1253     }
1254
1255   if ( ASDCP_FAILURE(result) )
1256     m_Writer.release();
1257
1258   return result;
1259 }
1260
1261 ASDCP::Result_t
1262 ASDCP::JP2K::MXFSWriter::WriteFrame(const SFrameBuffer& FrameBuf, AESEncContext* Ctx, HMACContext* HMAC)
1263 {
1264   if ( m_Writer.empty() )
1265     return RESULT_INIT;
1266
1267   Result_t result = m_Writer->WriteFrame(FrameBuf.Left, SP_LEFT, Ctx, HMAC);
1268
1269   if ( ASDCP_SUCCESS(result) )
1270     result = m_Writer->WriteFrame(FrameBuf.Right, SP_RIGHT, Ctx, HMAC);
1271
1272   return result;
1273 }
1274
1275 // Writes a frame of essence to the MXF file. If the optional AESEncContext
1276 // argument is present, the essence is encrypted prior to writing.
1277 // Fails if the file is not open, is finalized, or an operating system
1278 // error occurs.
1279 ASDCP::Result_t
1280 ASDCP::JP2K::MXFSWriter::WriteFrame(const FrameBuffer& FrameBuf, StereoscopicPhase_t phase,
1281                                     AESEncContext* Ctx, HMACContext* HMAC)
1282 {
1283   if ( m_Writer.empty() )
1284     return RESULT_INIT;
1285
1286   return m_Writer->WriteFrame(FrameBuf, phase, Ctx, HMAC);
1287 }
1288
1289 // Closes the MXF file, writing the index and other closing information.
1290 ASDCP::Result_t
1291 ASDCP::JP2K::MXFSWriter::Finalize()
1292 {
1293   if ( m_Writer.empty() )
1294     return RESULT_INIT;
1295
1296   return m_Writer->Finalize();
1297 }
1298
1299 //
1300 // end AS_DCP_JP2K.cpp
1301 //