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