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