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