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