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