05166d68c23a0a5d14bb36075d80c1f6d74dbd33
[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
1000   if ( ASDCP_SUCCESS(result) )
1001     result = WriteEKLVPacket(FrameBuf, m_EssenceUL, Ctx, HMAC, hash);
1002
1003   if ( ASDCP_SUCCESS(result) && add_index )
1004     {  
1005       IndexTableSegment::IndexEntry Entry;
1006       Entry.StreamOffset = StreamOffset;
1007       m_FooterPart.PushIndexEntry(Entry);
1008     }
1009
1010   m_FramesWritten++;
1011   return result;
1012 }
1013
1014 Result_t
1015 lh__Writer::FakeWriteFrame(int size, bool add_index)
1016 {
1017   Result_t result = RESULT_OK;
1018
1019   if ( m_State.Test_READY() )
1020     result = m_State.Goto_RUNNING();
1021
1022   ui64_t StreamOffset = m_StreamOffset;
1023
1024   if ( ASDCP_SUCCESS(result) )
1025     result = FakeWriteEKLVPacket(size);
1026
1027   if ( ASDCP_SUCCESS(result) && add_index )
1028     {  
1029       IndexTableSegment::IndexEntry Entry;
1030       Entry.StreamOffset = StreamOffset;
1031       m_FooterPart.PushIndexEntry(Entry);
1032     }
1033
1034   m_FramesWritten++;
1035   return result;
1036 }
1037
1038
1039 // Closes the MXF file, writing the index and other closing information.
1040 //
1041 ASDCP::Result_t
1042 lh__Writer::Finalize()
1043 {
1044   if ( ! m_State.Test_RUNNING() )
1045     return RESULT_STATE;
1046
1047   m_State.Goto_FINAL();
1048
1049   return WriteMXFFooter();
1050 }
1051
1052
1053 //
1054 class ASDCP::JP2K::MXFWriter::h__Writer : public lh__Writer
1055 {
1056   ASDCP_NO_COPY_CONSTRUCT(h__Writer);
1057   h__Writer();
1058
1059 public:
1060   h__Writer(const Dictionary& d) : lh__Writer(d) {}
1061 };
1062
1063
1064 //------------------------------------------------------------------------------------------
1065
1066
1067
1068 ASDCP::JP2K::MXFWriter::MXFWriter()
1069 {
1070 }
1071
1072 ASDCP::JP2K::MXFWriter::~MXFWriter()
1073 {
1074 }
1075
1076 // Warning: direct manipulation of MXF structures can interfere
1077 // with the normal operation of the wrapper.  Caveat emptor!
1078 //
1079 ASDCP::MXF::OPAtomHeader&
1080 ASDCP::JP2K::MXFWriter::OPAtomHeader()
1081 {
1082   if ( m_Writer.empty() )
1083     {
1084       assert(g_OPAtomHeader);
1085       return *g_OPAtomHeader;
1086     }
1087
1088   return m_Writer->m_HeaderPart;
1089 }
1090
1091 // Warning: direct manipulation of MXF structures can interfere
1092 // with the normal operation of the wrapper.  Caveat emptor!
1093 //
1094 ASDCP::MXF::OPAtomIndexFooter&
1095 ASDCP::JP2K::MXFWriter::OPAtomIndexFooter()
1096 {
1097   if ( m_Writer.empty() )
1098     {
1099       assert(g_OPAtomIndexFooter);
1100       return *g_OPAtomIndexFooter;
1101     }
1102
1103   return m_Writer->m_FooterPart;
1104 }
1105
1106 // Open the file for writing. The file must not exist unless overwrite is true. Returns error if
1107 // the operation cannot be completed.
1108 ASDCP::Result_t
1109 ASDCP::JP2K::MXFWriter::OpenWrite(const char* filename, const WriterInfo& Info,
1110                                   const PictureDescriptor& PDesc, ui32_t HeaderSize, bool overwrite)
1111 {
1112   if ( Info.LabelSetType == LS_MXF_SMPTE )
1113     m_Writer = new h__Writer(DefaultSMPTEDict());
1114   else
1115     m_Writer = new h__Writer(DefaultInteropDict());
1116
1117   m_Writer->m_Info = Info;
1118
1119   Result_t result = m_Writer->OpenWrite(filename, ASDCP::ESS_JPEG_2000, HeaderSize, overwrite);
1120
1121   if ( ASDCP_SUCCESS(result) )
1122     result = m_Writer->SetSourceStream(PDesc, JP2K_PACKAGE_LABEL);
1123
1124   if ( ASDCP_FAILURE(result) )
1125     m_Writer.release();
1126
1127   return result;
1128 }
1129
1130
1131 // Writes a frame of essence to the MXF file. If the optional AESEncContext
1132 // argument is present, the essence is encrypted prior to writing.
1133 // Fails if the file is not open, is finalized, or an operating system
1134 // error occurs.
1135 ASDCP::Result_t
1136 ASDCP::JP2K::MXFWriter::WriteFrame(const FrameBuffer& FrameBuf, AESEncContext* Ctx, HMACContext* HMAC, std::string* hash)
1137 {
1138   if ( m_Writer.empty() )
1139     return RESULT_INIT;
1140
1141   return m_Writer->WriteFrame(FrameBuf, true, Ctx, HMAC, hash);
1142 }
1143
1144 ASDCP::Result_t
1145 ASDCP::JP2K::MXFWriter::FakeWriteFrame(int size)
1146 {
1147   if ( m_Writer.empty() )
1148     return RESULT_INIT;
1149
1150   return m_Writer->FakeWriteFrame(size, true);
1151 }
1152
1153 // Closes the MXF file, writing the index and other closing information.
1154 ASDCP::Result_t
1155 ASDCP::JP2K::MXFWriter::Finalize()
1156 {
1157   if ( m_Writer.empty() )
1158     return RESULT_INIT;
1159
1160   return m_Writer->Finalize();
1161 }
1162
1163 ui64_t
1164 ASDCP::JP2K::MXFWriter::Tell() const
1165 {
1166         return m_Writer->m_File.Tell();
1167 }
1168
1169
1170 //------------------------------------------------------------------------------------------
1171 //
1172
1173 //
1174 class ASDCP::JP2K::MXFSWriter::h__SWriter : public lh__Writer
1175 {
1176   ASDCP_NO_COPY_CONSTRUCT(h__SWriter);
1177   h__SWriter();
1178   StereoscopicPhase_t m_NextPhase;
1179
1180 public:
1181   h__SWriter(const Dictionary& d) : lh__Writer(d), m_NextPhase(SP_LEFT) {}
1182
1183   //
1184   Result_t WriteFrame(const FrameBuffer& FrameBuf, StereoscopicPhase_t phase,
1185                       AESEncContext* Ctx, HMACContext* HMAC, std::string* hash)
1186   {
1187     if ( m_NextPhase != phase )
1188       return RESULT_SPHASE;
1189
1190     if ( phase == SP_LEFT )
1191       {
1192         m_NextPhase = SP_RIGHT;
1193         return lh__Writer::WriteFrame(FrameBuf, true, Ctx, HMAC, hash);
1194       }
1195
1196     m_NextPhase = SP_LEFT;
1197     return lh__Writer::WriteFrame(FrameBuf, false, Ctx, HMAC, hash);
1198   }
1199
1200   Result_t FakeWriteFrame(int size, StereoscopicPhase_t phase)
1201   {
1202     if (m_NextPhase != phase)
1203       {
1204         return RESULT_SPHASE;
1205       }
1206
1207     if (phase == SP_LEFT)
1208       {
1209         m_NextPhase = SP_RIGHT;
1210         return lh__Writer::FakeWriteFrame(size, true);
1211       }
1212
1213     m_NextPhase = SP_LEFT;
1214     return lh__Writer::FakeWriteFrame(size, false);
1215   }
1216
1217   //
1218   Result_t Finalize()
1219   {
1220     if ( m_NextPhase != SP_LEFT )
1221       return RESULT_SPHASE;
1222
1223     assert( m_FramesWritten % 2 == 0 );
1224     m_FramesWritten /= 2;
1225     return lh__Writer::Finalize();
1226   }
1227 };
1228
1229
1230 //
1231 ASDCP::JP2K::MXFSWriter::MXFSWriter()
1232 {
1233 }
1234
1235 ASDCP::JP2K::MXFSWriter::~MXFSWriter()
1236 {
1237 }
1238
1239 // Warning: direct manipulation of MXF structures can interfere
1240 // with the normal operation of the wrapper.  Caveat emptor!
1241 //
1242 ASDCP::MXF::OPAtomHeader&
1243 ASDCP::JP2K::MXFSWriter::OPAtomHeader()
1244 {
1245   if ( m_Writer.empty() )
1246     {
1247       assert(g_OPAtomHeader);
1248       return *g_OPAtomHeader;
1249     }
1250
1251   return m_Writer->m_HeaderPart;
1252 }
1253
1254 // Warning: direct manipulation of MXF structures can interfere
1255 // with the normal operation of the wrapper.  Caveat emptor!
1256 //
1257 ASDCP::MXF::OPAtomIndexFooter&
1258 ASDCP::JP2K::MXFSWriter::OPAtomIndexFooter()
1259 {
1260   if ( m_Writer.empty() )
1261     {
1262       assert(g_OPAtomIndexFooter);
1263       return *g_OPAtomIndexFooter;
1264     }
1265
1266   return m_Writer->m_FooterPart;
1267 }
1268
1269 // Open the file for writing. The file must not exist. Returns error if
1270 // the operation cannot be completed.
1271 ASDCP::Result_t
1272 ASDCP::JP2K::MXFSWriter::OpenWrite(const char* filename, const WriterInfo& Info,
1273                                    const PictureDescriptor& PDesc, ui32_t HeaderSize, bool overwrite)
1274 {
1275   if ( Info.LabelSetType == LS_MXF_SMPTE )
1276     m_Writer = new h__SWriter(DefaultSMPTEDict());
1277   else
1278     m_Writer = new h__SWriter(DefaultInteropDict());
1279
1280   if ( PDesc.EditRate != ASDCP::EditRate_24
1281        && PDesc.EditRate != ASDCP::EditRate_25
1282        && PDesc.EditRate != ASDCP::EditRate_30
1283        && PDesc.EditRate != ASDCP::EditRate_48
1284        && PDesc.EditRate != ASDCP::EditRate_50
1285        && PDesc.EditRate != ASDCP::EditRate_60 )
1286     {
1287       DefaultLogSink().Error("Stereoscopic wrapping requires 24, 25, 30, 48, 50 or 60 fps input streams.\n");
1288       return RESULT_FORMAT;
1289     }
1290
1291   if ( PDesc.StoredWidth > 2048 )
1292     DefaultLogSink().Warn("Wrapping non-standard 4K stereoscopic content. I hope you know what you are doing!\n");
1293
1294   m_Writer->m_Info = Info;
1295
1296   Result_t result = m_Writer->OpenWrite(filename, ASDCP::ESS_JPEG_2000_S, HeaderSize, overwrite);
1297
1298   if ( ASDCP_SUCCESS(result) )
1299     {
1300       PictureDescriptor TmpPDesc = PDesc;
1301
1302       if ( PDesc.EditRate == ASDCP::EditRate_24 )
1303         TmpPDesc.EditRate = ASDCP::EditRate_48;
1304
1305       else if ( PDesc.EditRate == ASDCP::EditRate_25 )
1306         TmpPDesc.EditRate = ASDCP::EditRate_50;
1307
1308       else if ( PDesc.EditRate == ASDCP::EditRate_30 )
1309         TmpPDesc.EditRate = ASDCP::EditRate_60;
1310
1311       else if ( PDesc.EditRate == ASDCP::EditRate_48 )
1312         TmpPDesc.EditRate = ASDCP::EditRate_96;
1313
1314       else if ( PDesc.EditRate == ASDCP::EditRate_50 )
1315         TmpPDesc.EditRate = ASDCP::EditRate_100;
1316
1317       else if ( PDesc.EditRate == ASDCP::EditRate_60 )
1318         TmpPDesc.EditRate = ASDCP::EditRate_120;
1319
1320       result = m_Writer->SetSourceStream(TmpPDesc, JP2K_S_PACKAGE_LABEL, PDesc.EditRate);
1321     }
1322
1323   if ( ASDCP_FAILURE(result) )
1324     m_Writer.release();
1325
1326   return result;
1327 }
1328
1329 ASDCP::Result_t
1330 ASDCP::JP2K::MXFSWriter::WriteFrame(const SFrameBuffer& FrameBuf, AESEncContext* Ctx, HMACContext* HMAC)
1331 {
1332   if ( m_Writer.empty() )
1333     return RESULT_INIT;
1334
1335   Result_t result = m_Writer->WriteFrame(FrameBuf.Left, SP_LEFT, Ctx, HMAC, 0);
1336
1337   if ( ASDCP_SUCCESS(result) )
1338     result = m_Writer->WriteFrame(FrameBuf.Right, SP_RIGHT, Ctx, HMAC, 0);
1339
1340   return result;
1341 }
1342
1343 // Writes a frame of essence to the MXF file. If the optional AESEncContext
1344 // argument is present, the essence is encrypted prior to writing.
1345 // Fails if the file is not open, is finalized, or an operating system
1346 // error occurs.
1347 ASDCP::Result_t
1348 ASDCP::JP2K::MXFSWriter::WriteFrame(const FrameBuffer& FrameBuf, StereoscopicPhase_t phase,
1349                                     AESEncContext* Ctx, HMACContext* HMAC, std::string* hash)
1350 {
1351   if ( m_Writer.empty() )
1352     return RESULT_INIT;
1353
1354   return m_Writer->WriteFrame(FrameBuf, phase, Ctx, HMAC, hash);
1355 }
1356
1357 ASDCP::Result_t
1358 ASDCP::JP2K::MXFSWriter::FakeWriteFrame(int size, StereoscopicPhase_t phase)
1359 {
1360   if ( m_Writer.empty() )
1361     return RESULT_INIT;
1362
1363   return m_Writer->FakeWriteFrame(size, phase);
1364 }
1365
1366 // Closes the MXF file, writing the index and other closing information.
1367 ASDCP::Result_t
1368 ASDCP::JP2K::MXFSWriter::Finalize()
1369 {
1370   if ( m_Writer.empty() )
1371     return RESULT_INIT;
1372
1373   return m_Writer->Finalize();
1374 }
1375
1376 ui64_t
1377 ASDCP::JP2K::MXFSWriter::Tell() const
1378 {
1379         return m_Writer->m_File.Tell();
1380 }
1381
1382 //
1383 // end AS_DCP_JP2K.cpp
1384 //