945911e82e8a7810e89fc76051015e98a00dad85
[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, bool);
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*, std::string* hash = 0);
937   Result_t FakeWriteFrame(int size, bool add_index);
938   Result_t Finalize();
939 };
940
941 // Open the file for writing. The file must not exist unless overwrite is true. Returns error if
942 // the operation cannot be completed.
943 ASDCP::Result_t
944 lh__Writer::OpenWrite(const std::string& filename, EssenceType_t type, ui32_t HeaderSize, bool overwrite)
945 {
946   if ( ! m_State.Test_BEGIN() )
947     return RESULT_STATE;
948
949   Result_t result = RESULT_OK;
950   if (overwrite)
951     {
952       result = m_File.OpenModify(filename);
953       m_File.Seek(0);
954     }
955   else
956     {
957       result = m_File.OpenWrite(filename);
958     }
959
960   if ( ASDCP_SUCCESS(result) )
961     {
962       m_HeaderSize = HeaderSize;
963       RGBAEssenceDescriptor* tmp_rgba = new RGBAEssenceDescriptor(m_Dict);
964       tmp_rgba->ComponentMaxRef = 4095;
965       tmp_rgba->ComponentMinRef = 0;
966
967       m_EssenceDescriptor = tmp_rgba;
968       m_EssenceSubDescriptor = new JPEG2000PictureSubDescriptor(m_Dict);
969       m_EssenceSubDescriptorList.push_back((InterchangeObject*)m_EssenceSubDescriptor);
970
971       GenRandomValue(m_EssenceSubDescriptor->InstanceUID);
972       m_EssenceDescriptor->SubDescriptors.push_back(m_EssenceSubDescriptor->InstanceUID);
973
974       if ( type == ASDCP::ESS_JPEG_2000_S && m_Info.LabelSetType == LS_MXF_SMPTE )
975         {
976           InterchangeObject* StereoSubDesc = new StereoscopicPictureSubDescriptor(m_Dict);
977           m_EssenceSubDescriptorList.push_back(StereoSubDesc);
978           GenRandomValue(StereoSubDesc->InstanceUID);
979           m_EssenceDescriptor->SubDescriptors.push_back(StereoSubDesc->InstanceUID);
980         }
981
982       result = m_State.Goto_INIT();
983     }
984
985   return result;
986 }
987
988 // Automatically sets the MXF file's metadata from the first jpeg codestream stream.
989 ASDCP::Result_t
990 lh__Writer::SetSourceStream(const PictureDescriptor& PDesc, const std::string& label, ASDCP::Rational LocalEditRate)
991 {
992   assert(m_Dict);
993   if ( ! m_State.Test_INIT() )
994     return RESULT_STATE;
995
996   if ( LocalEditRate == ASDCP::Rational(0,0) )
997     LocalEditRate = PDesc.EditRate;
998
999   m_PDesc = PDesc;
1000   assert(m_Dict);
1001   assert(m_EssenceDescriptor);
1002   assert(m_EssenceSubDescriptor);
1003   Result_t result = JP2K_PDesc_to_MD(m_PDesc, *m_Dict,
1004                                      *static_cast<ASDCP::MXF::GenericPictureEssenceDescriptor*>(m_EssenceDescriptor),
1005                                      *m_EssenceSubDescriptor);
1006
1007   if ( ASDCP_SUCCESS(result) )
1008     {
1009       if ( PDesc.StoredWidth < 2049 )
1010         {
1011           static_cast<ASDCP::MXF::RGBAEssenceDescriptor*>(m_EssenceDescriptor)->PictureEssenceCoding.Set(m_Dict->ul(MDD_JP2KEssenceCompression_2K));
1012           m_EssenceSubDescriptor->Rsize = 3;
1013         }
1014       else
1015         {
1016           static_cast<ASDCP::MXF::RGBAEssenceDescriptor*>(m_EssenceDescriptor)->PictureEssenceCoding.Set(m_Dict->ul(MDD_JP2KEssenceCompression_4K));
1017           m_EssenceSubDescriptor->Rsize = 4;
1018         }
1019
1020       memcpy(m_EssenceUL, m_Dict->ul(MDD_JPEG2000Essence), SMPTE_UL_LENGTH);
1021       m_EssenceUL[SMPTE_UL_LENGTH-1] = 1; // first (and only) essence container
1022       result = m_State.Goto_READY();
1023     }
1024
1025   if ( ASDCP_SUCCESS(result) )
1026     {
1027       result = WriteASDCPHeader(label, UL(m_Dict->ul(MDD_JPEG_2000WrappingFrame)),
1028                                 PICT_DEF_LABEL, UL(m_EssenceUL), UL(m_Dict->ul(MDD_PictureDataDef)),
1029                                 LocalEditRate, derive_timecode_rate_from_edit_rate(m_PDesc.EditRate));
1030     }
1031
1032   return result;
1033 }
1034
1035 // Writes a frame of essence to the MXF file. If the optional AESEncContext
1036 // argument is present, the essence is encrypted prior to writing.
1037 // Fails if the file is not open, is finalized, or an operating system
1038 // error occurs.
1039 //
1040 ASDCP::Result_t
1041 lh__Writer::WriteFrame(const JP2K::FrameBuffer& FrameBuf, bool add_index,
1042                        AESEncContext* Ctx, HMACContext* HMAC, std::string* hash)
1043 {
1044   Result_t result = RESULT_OK;
1045
1046   if ( m_State.Test_READY() )
1047     result = m_State.Goto_RUNNING(); // first time through
1048
1049   ui64_t StreamOffset = m_StreamOffset;
1050
1051   if ( ASDCP_SUCCESS(result) )
1052     result = WriteEKLVPacket(FrameBuf, m_EssenceUL, Ctx, HMAC, hash);
1053
1054   if ( ASDCP_SUCCESS(result) && add_index )
1055     {
1056       IndexTableSegment::IndexEntry Entry;
1057       Entry.StreamOffset = StreamOffset;
1058       m_FooterPart.PushIndexEntry(Entry);
1059     }
1060
1061   m_FramesWritten++;
1062   return result;
1063 }
1064
1065 Result_t
1066 lh__Writer::FakeWriteFrame(int size, bool add_index)
1067 {
1068   Result_t result = RESULT_OK;
1069
1070   if ( m_State.Test_READY() )
1071     result = m_State.Goto_RUNNING();
1072
1073   ui64_t StreamOffset = m_StreamOffset;
1074
1075   if ( ASDCP_SUCCESS(result) )
1076     result = FakeWriteEKLVPacket(size);
1077
1078   if ( ASDCP_SUCCESS(result) && add_index )
1079     {
1080       IndexTableSegment::IndexEntry Entry;
1081       Entry.StreamOffset = StreamOffset;
1082       m_FooterPart.PushIndexEntry(Entry);
1083     }
1084
1085   m_FramesWritten++;
1086   return result;
1087 }
1088
1089 // Closes the MXF file, writing the index and other closing information.
1090 //
1091 ASDCP::Result_t
1092 lh__Writer::Finalize()
1093 {
1094   if ( ! m_State.Test_RUNNING() )
1095     return RESULT_STATE;
1096
1097   m_State.Goto_FINAL();
1098
1099   return WriteASDCPFooter();
1100 }
1101
1102
1103 //
1104 class ASDCP::JP2K::MXFWriter::h__Writer : public lh__Writer
1105 {
1106   ASDCP_NO_COPY_CONSTRUCT(h__Writer);
1107   h__Writer();
1108
1109 public:
1110   h__Writer(const Dictionary& d) : lh__Writer(d) {}
1111 };
1112
1113
1114 //------------------------------------------------------------------------------------------
1115
1116
1117
1118 ASDCP::JP2K::MXFWriter::MXFWriter()
1119 {
1120 }
1121
1122 ASDCP::JP2K::MXFWriter::~MXFWriter()
1123 {
1124 }
1125
1126 // Warning: direct manipulation of MXF structures can interfere
1127 // with the normal operation of the wrapper.  Caveat emptor!
1128 //
1129 ASDCP::MXF::OP1aHeader&
1130 ASDCP::JP2K::MXFWriter::OP1aHeader()
1131 {
1132   if ( m_Writer.empty() )
1133     {
1134       assert(g_OP1aHeader);
1135       return *g_OP1aHeader;
1136     }
1137
1138   return m_Writer->m_HeaderPart;
1139 }
1140
1141 // Warning: direct manipulation of MXF structures can interfere
1142 // with the normal operation of the wrapper.  Caveat emptor!
1143 //
1144 ASDCP::MXF::OPAtomIndexFooter&
1145 ASDCP::JP2K::MXFWriter::OPAtomIndexFooter()
1146 {
1147   if ( m_Writer.empty() )
1148     {
1149       assert(g_OPAtomIndexFooter);
1150       return *g_OPAtomIndexFooter;
1151     }
1152
1153   return m_Writer->m_FooterPart;
1154 }
1155
1156 // Warning: direct manipulation of MXF structures can interfere
1157 // with the normal operation of the wrapper.  Caveat emptor!
1158 //
1159 ASDCP::MXF::RIP&
1160 ASDCP::JP2K::MXFWriter::RIP()
1161 {
1162   if ( m_Writer.empty() )
1163     {
1164       assert(g_RIP);
1165       return *g_RIP;
1166     }
1167
1168   return m_Writer->m_RIP;
1169 }
1170
1171 // Open the file for writing. The file must not exist unless overwrite is true. Returns error if
1172 // the operation cannot be completed.
1173 ASDCP::Result_t
1174 ASDCP::JP2K::MXFWriter::OpenWrite(const std::string& filename, const WriterInfo& Info,
1175                                   const PictureDescriptor& PDesc, ui32_t HeaderSize, bool overwrite)
1176 {
1177   if ( Info.LabelSetType == LS_MXF_SMPTE )
1178     m_Writer = new h__Writer(DefaultSMPTEDict());
1179   else
1180     m_Writer = new h__Writer(DefaultInteropDict());
1181
1182   m_Writer->m_Info = Info;
1183
1184   Result_t result = m_Writer->OpenWrite(filename, ASDCP::ESS_JPEG_2000, HeaderSize, overwrite);
1185
1186   if ( ASDCP_SUCCESS(result) )
1187     result = m_Writer->SetSourceStream(PDesc, JP2K_PACKAGE_LABEL);
1188
1189   if ( ASDCP_FAILURE(result) )
1190     m_Writer.release();
1191
1192   return result;
1193 }
1194
1195
1196 // Writes a frame of essence to the MXF file. If the optional AESEncContext
1197 // argument is present, the essence is encrypted prior to writing.
1198 // Fails if the file is not open, is finalized, or an operating system
1199 // error occurs.
1200 ASDCP::Result_t
1201 ASDCP::JP2K::MXFWriter::WriteFrame(const FrameBuffer& FrameBuf, AESEncContext* Ctx, HMACContext* HMAC, std::string* hash)
1202 {
1203   if ( m_Writer.empty() )
1204     return RESULT_INIT;
1205
1206   return m_Writer->WriteFrame(FrameBuf, true, Ctx, HMAC, hash);
1207 }
1208
1209 ASDCP::Result_t
1210 ASDCP::JP2K::MXFWriter::FakeWriteFrame(int size)
1211 {
1212   if ( m_Writer.empty() )
1213     return RESULT_INIT;
1214
1215   return m_Writer->FakeWriteFrame(size, true);
1216 }
1217
1218 // Closes the MXF file, writing the index and other closing information.
1219 ASDCP::Result_t
1220 ASDCP::JP2K::MXFWriter::Finalize()
1221 {
1222   if ( m_Writer.empty() )
1223     return RESULT_INIT;
1224
1225   return m_Writer->Finalize();
1226 }
1227
1228 ui64_t
1229 ASDCP::JP2K::MXFWriter::Tell() const
1230 {
1231   return m_Writer->m_File.Tell();
1232 }
1233
1234 //------------------------------------------------------------------------------------------
1235 //
1236
1237 //
1238 class ASDCP::JP2K::MXFSWriter::h__SWriter : public lh__Writer
1239 {
1240   ASDCP_NO_COPY_CONSTRUCT(h__SWriter);
1241   h__SWriter();
1242   StereoscopicPhase_t m_NextPhase;
1243
1244 public:
1245   h__SWriter(const Dictionary& d) : lh__Writer(d), m_NextPhase(SP_LEFT) {}
1246
1247   //
1248   Result_t WriteFrame(const FrameBuffer& FrameBuf, StereoscopicPhase_t phase,
1249                       AESEncContext* Ctx, HMACContext* HMAC, std::string* hash)
1250   {
1251     if ( m_NextPhase != phase )
1252       return RESULT_SPHASE;
1253
1254     if ( phase == SP_LEFT )
1255       {
1256         m_NextPhase = SP_RIGHT;
1257         return lh__Writer::WriteFrame(FrameBuf, true, Ctx, HMAC, hash);
1258       }
1259
1260     m_NextPhase = SP_LEFT;
1261     return lh__Writer::WriteFrame(FrameBuf, false, Ctx, HMAC, hash);
1262   }
1263
1264   Result_t FakeWriteFrame(int size, StereoscopicPhase_t phase)
1265   {
1266     if ( m_NextPhase != phase )
1267       return RESULT_SPHASE;
1268
1269     if ( phase == SP_LEFT )
1270       {
1271         m_NextPhase = SP_RIGHT;
1272         return lh__Writer::FakeWriteFrame(size, true);
1273       }
1274
1275     m_NextPhase = SP_LEFT;
1276     return lh__Writer::FakeWriteFrame(size, false);
1277   }
1278
1279   //
1280   Result_t Finalize()
1281   {
1282     if ( m_NextPhase != SP_LEFT )
1283       return RESULT_SPHASE;
1284
1285     assert( m_FramesWritten % 2 == 0 );
1286     m_FramesWritten /= 2;
1287     return lh__Writer::Finalize();
1288   }
1289 };
1290
1291
1292 //
1293 ASDCP::JP2K::MXFSWriter::MXFSWriter()
1294 {
1295 }
1296
1297 ASDCP::JP2K::MXFSWriter::~MXFSWriter()
1298 {
1299 }
1300
1301 // Warning: direct manipulation of MXF structures can interfere
1302 // with the normal operation of the wrapper.  Caveat emptor!
1303 //
1304 ASDCP::MXF::OP1aHeader&
1305 ASDCP::JP2K::MXFSWriter::OP1aHeader()
1306 {
1307   if ( m_Writer.empty() )
1308     {
1309       assert(g_OP1aHeader);
1310       return *g_OP1aHeader;
1311     }
1312
1313   return m_Writer->m_HeaderPart;
1314 }
1315
1316 // Warning: direct manipulation of MXF structures can interfere
1317 // with the normal operation of the wrapper.  Caveat emptor!
1318 //
1319 ASDCP::MXF::OPAtomIndexFooter&
1320 ASDCP::JP2K::MXFSWriter::OPAtomIndexFooter()
1321 {
1322   if ( m_Writer.empty() )
1323     {
1324       assert(g_OPAtomIndexFooter);
1325       return *g_OPAtomIndexFooter;
1326     }
1327
1328   return m_Writer->m_FooterPart;
1329 }
1330
1331 // Warning: direct manipulation of MXF structures can interfere
1332 // with the normal operation of the wrapper.  Caveat emptor!
1333 //
1334 ASDCP::MXF::RIP&
1335 ASDCP::JP2K::MXFSWriter::RIP()
1336 {
1337   if ( m_Writer.empty() )
1338     {
1339       assert(g_RIP);
1340       return *g_RIP;
1341     }
1342
1343   return m_Writer->m_RIP;
1344 }
1345
1346 // Open the file for writing. The file must not exist. Returns error if
1347 // the operation cannot be completed.
1348 ASDCP::Result_t
1349 ASDCP::JP2K::MXFSWriter::OpenWrite(const std::string& filename, const WriterInfo& Info,
1350                                    const PictureDescriptor& PDesc, ui32_t HeaderSize, bool overwrite)
1351 {
1352   if ( Info.LabelSetType == LS_MXF_SMPTE )
1353     m_Writer = new h__SWriter(DefaultSMPTEDict());
1354   else
1355     m_Writer = new h__SWriter(DefaultInteropDict());
1356
1357   if ( PDesc.EditRate != ASDCP::EditRate_24
1358        && PDesc.EditRate != ASDCP::EditRate_25
1359        && PDesc.EditRate != ASDCP::EditRate_30
1360        && PDesc.EditRate != ASDCP::EditRate_48
1361        && PDesc.EditRate != ASDCP::EditRate_50
1362        && PDesc.EditRate != ASDCP::EditRate_60 )
1363     {
1364       DefaultLogSink().Error("Stereoscopic wrapping requires 24, 25, 30, 48, 50 or 60 fps input streams.\n");
1365       return RESULT_FORMAT;
1366     }
1367
1368   if ( PDesc.StoredWidth > 2048 )
1369     DefaultLogSink().Warn("Wrapping non-standard 4K stereoscopic content. I hope you know what you are doing!\n");
1370
1371   m_Writer->m_Info = Info;
1372
1373   Result_t result = m_Writer->OpenWrite(filename, ASDCP::ESS_JPEG_2000_S, HeaderSize, overwrite);
1374
1375   if ( ASDCP_SUCCESS(result) )
1376     {
1377       PictureDescriptor TmpPDesc = PDesc;
1378
1379       if ( PDesc.EditRate == ASDCP::EditRate_24 )
1380         TmpPDesc.EditRate = ASDCP::EditRate_48;
1381
1382       else if ( PDesc.EditRate == ASDCP::EditRate_25 )
1383         TmpPDesc.EditRate = ASDCP::EditRate_50;
1384
1385       else if ( PDesc.EditRate == ASDCP::EditRate_30 )
1386         TmpPDesc.EditRate = ASDCP::EditRate_60;
1387
1388       else if ( PDesc.EditRate == ASDCP::EditRate_48 )
1389         TmpPDesc.EditRate = ASDCP::EditRate_96;
1390
1391       else if ( PDesc.EditRate == ASDCP::EditRate_50 )
1392         TmpPDesc.EditRate = ASDCP::EditRate_100;
1393
1394       else if ( PDesc.EditRate == ASDCP::EditRate_60 )
1395         TmpPDesc.EditRate = ASDCP::EditRate_120;
1396
1397       result = m_Writer->SetSourceStream(TmpPDesc, JP2K_S_PACKAGE_LABEL, PDesc.EditRate);
1398     }
1399
1400   if ( ASDCP_FAILURE(result) )
1401     m_Writer.release();
1402
1403   return result;
1404 }
1405
1406 ASDCP::Result_t
1407 ASDCP::JP2K::MXFSWriter::WriteFrame(const SFrameBuffer& FrameBuf, AESEncContext* Ctx, HMACContext* HMAC)
1408 {
1409   if ( m_Writer.empty() )
1410     return RESULT_INIT;
1411
1412   Result_t result = m_Writer->WriteFrame(FrameBuf.Left, SP_LEFT, Ctx, HMAC, 0);
1413
1414   if ( ASDCP_SUCCESS(result) )
1415     result = m_Writer->WriteFrame(FrameBuf.Right, SP_RIGHT, Ctx, HMAC, 0);
1416
1417   return result;
1418 }
1419
1420 // Writes a frame of essence to the MXF file. If the optional AESEncContext
1421 // argument is present, the essence is encrypted prior to writing.
1422 // Fails if the file is not open, is finalized, or an operating system
1423 // error occurs.
1424 ASDCP::Result_t
1425 ASDCP::JP2K::MXFSWriter::WriteFrame(const FrameBuffer& FrameBuf, StereoscopicPhase_t phase,
1426                                     AESEncContext* Ctx, HMACContext* HMAC, std::string* hash)
1427 {
1428   if ( m_Writer.empty() )
1429     return RESULT_INIT;
1430
1431   return m_Writer->WriteFrame(FrameBuf, phase, Ctx, HMAC, hash);
1432 }
1433
1434 ASDCP::Result_t
1435 ASDCP::JP2K::MXFSWriter::FakeWriteFrame(int size, StereoscopicPhase_t phase)
1436 {
1437   if ( m_Writer.empty() )
1438     return RESULT_INIT;
1439
1440   return m_Writer->FakeWriteFrame(size, phase);
1441 }
1442
1443 // Closes the MXF file, writing the index and other closing information.
1444 ASDCP::Result_t
1445 ASDCP::JP2K::MXFSWriter::Finalize()
1446 {
1447   if ( m_Writer.empty() )
1448     return RESULT_INIT;
1449
1450   return m_Writer->Finalize();
1451 }
1452
1453 ui64_t
1454 ASDCP::JP2K::MXFSWriter::Tell() const
1455 {
1456   return m_Writer->m_File.Tell();
1457 }
1458
1459 //
1460 // end AS_DCP_JP2K.cpp
1461 //