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