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