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