logging re-write
[asdcplib.git] / src / AS_DCP_JP2K.cpp
1 /*
2 Copyright (c) 2004-2007, 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
34 using namespace ASDCP::JP2K;
35
36
37 //------------------------------------------------------------------------------------------
38
39 static std::string JP2K_PACKAGE_LABEL = "File Package: SMPTE 429-4 frame wrapping of JPEG 2000 codestreams";
40 static std::string JP2K_S_PACKAGE_LABEL = "File Package: SMPTE 429-10 frame wrapping of stereoscopic JPEG 2000 codestreams";
41 static std::string PICT_DEF_LABEL = "Picture Track";
42
43 int s_exp_lookup[16] = { 0, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024,2048, 4096, 8192, 16384, 32768 };
44
45 //
46 void
47 ASDCP::JP2K::PictureDescriptorDump(const PictureDescriptor& PDesc, FILE* stream)
48 {
49   if ( stream == 0 )
50     stream = stderr;
51
52   fprintf(stream, "\
53        AspectRatio: %d/%d\n\
54           EditRate: %d/%d\n\
55        StoredWidth: %u\n\
56       StoredHeight: %u\n\
57              Rsize: %u\n\
58              Xsize: %u\n\
59              Ysize: %u\n\
60             XOsize: %u\n\
61             YOsize: %u\n\
62             XTsize: %u\n\
63             YTsize: %u\n\
64            XTOsize: %u\n\
65            YTOsize: %u\n\
66  ContainerDuration: %u\n",
67           PDesc.AspectRatio.Numerator, PDesc.AspectRatio.Denominator,
68           PDesc.EditRate.Numerator, PDesc.EditRate.Denominator,
69           PDesc.StoredWidth,
70           PDesc.StoredHeight,
71           PDesc.Rsize,
72           PDesc.Xsize,
73           PDesc.Ysize,
74           PDesc.XOsize,
75           PDesc.YOsize,
76           PDesc.XTsize,
77           PDesc.YTsize,
78           PDesc.XTOsize,
79           PDesc.YTOsize,
80           PDesc.ContainerDuration
81           );
82
83   fprintf(stream, "-- JPEG 2000 Metadata --\n");
84   fprintf(stream, "    ImageComponents:\n");
85   fprintf(stream, "  bits  h-sep v-sep\n");
86
87   for ( ui32_t i = 0; i < PDesc.Csize; i++ )
88     {
89       fprintf(stream, "  %4d  %5d %5d\n",
90               PDesc.ImageComponents[i].Ssize + 1, // See ISO 15444-1, Table A11, for the origin of '+1'
91               PDesc.ImageComponents[i].XRsize,
92               PDesc.ImageComponents[i].YRsize
93               );
94     }
95   
96   fprintf(stream, "               Scod: %hd\n", PDesc.CodingStyleDefault.Scod);
97   fprintf(stream, "   ProgressionOrder: %hd\n", PDesc.CodingStyleDefault.SGcod.ProgressionOrder);
98   fprintf(stream, "     NumberOfLayers: %hd\n",
99           KM_i16_BE(Kumu::cp2i<ui16_t>(PDesc.CodingStyleDefault.SGcod.NumberOfLayers)));
100
101   fprintf(stream, " MultiCompTransform: %hd\n", PDesc.CodingStyleDefault.SGcod.MultiCompTransform);
102   fprintf(stream, "DecompositionLevels: %hd\n", PDesc.CodingStyleDefault.SPcod.DecompositionLevels);
103   fprintf(stream, "     CodeblockWidth: %hd\n", PDesc.CodingStyleDefault.SPcod.CodeblockWidth);
104   fprintf(stream, "    CodeblockHeight: %hd\n", PDesc.CodingStyleDefault.SPcod.CodeblockHeight);
105   fprintf(stream, "     CodeblockStyle: %hd\n", PDesc.CodingStyleDefault.SPcod.CodeblockStyle);
106   fprintf(stream, "     Transformation: %hd\n", PDesc.CodingStyleDefault.SPcod.Transformation);
107
108
109   ui32_t precinct_set_size = 0, i;
110
111   for ( i = 0; PDesc.CodingStyleDefault.SPcod.PrecinctSize[i] != 0 && i < MaxPrecincts; i++ )
112     precinct_set_size++;
113
114   fprintf(stream, "          Precincts: %hd\n", precinct_set_size);
115   fprintf(stream, "precinct dimensions:\n");
116
117   for ( i = 0; i < precinct_set_size; i++ )
118     fprintf(stream, "    %d: %d x %d\n", i + 1,
119             s_exp_lookup[PDesc.CodingStyleDefault.SPcod.PrecinctSize[i]&0x0f],
120             s_exp_lookup[(PDesc.CodingStyleDefault.SPcod.PrecinctSize[i]>>4)&0x0f]
121             );
122
123   fprintf(stream, "               Sqcd: %hd\n", PDesc.QuantizationDefault.Sqcd);
124
125   char tmp_buf[MaxDefaults*2];
126   fprintf(stream, "              SPqcd: %s\n",
127           Kumu::bin2hex(PDesc.QuantizationDefault.SPqcd, PDesc.QuantizationDefault.SPqcdLength,
128                         tmp_buf, MaxDefaults*2)
129           );
130 }
131
132 //------------------------------------------------------------------------------------------
133 //
134 // hidden, internal implementation of JPEG 2000 reader
135
136 class lh__Reader : public ASDCP::h__Reader
137 {
138   RGBAEssenceDescriptor*        m_EssenceDescriptor;
139   JPEG2000PictureSubDescriptor* m_EssenceSubDescriptor;
140   ASDCP::Rational               m_EditRate;
141   EssenceType_t                 m_Format;
142
143   ASDCP_NO_COPY_CONSTRUCT(lh__Reader);
144
145 public:
146   PictureDescriptor m_PDesc;        // codestream parameter list
147
148   lh__Reader() : m_EssenceDescriptor(0), m_EssenceSubDescriptor(0), m_Format(ESS_UNKNOWN) {}
149   Result_t    OpenRead(const char*, EssenceType_t);
150   Result_t    ReadFrame(ui32_t, JP2K::FrameBuffer&, AESDecContext*, HMACContext*);
151   Result_t    MD_to_JP2K_PDesc(JP2K::PictureDescriptor& PDesc);
152 };
153
154 //
155 ASDCP::Result_t
156 lh__Reader::MD_to_JP2K_PDesc(JP2K::PictureDescriptor& PDesc)
157 {
158   memset(&PDesc, 0, sizeof(PDesc));
159   MXF::RGBAEssenceDescriptor* PDescObj = (MXF::RGBAEssenceDescriptor*)m_EssenceDescriptor;
160
161   PDesc.EditRate           = m_EditRate;
162   PDesc.ContainerDuration  = PDescObj->ContainerDuration;
163   PDesc.StoredWidth        = PDescObj->StoredWidth;
164   PDesc.StoredHeight       = PDescObj->StoredHeight;
165   PDesc.AspectRatio        = PDescObj->AspectRatio;
166
167   if ( m_EssenceSubDescriptor != 0 )
168     {
169       PDesc.Rsize   = m_EssenceSubDescriptor->Rsize;
170       PDesc.Xsize   = m_EssenceSubDescriptor->Xsize;
171       PDesc.Ysize   = m_EssenceSubDescriptor->Ysize;
172       PDesc.XOsize  = m_EssenceSubDescriptor->XOsize;
173       PDesc.YOsize  = m_EssenceSubDescriptor->YOsize;
174       PDesc.XTsize  = m_EssenceSubDescriptor->XTsize;
175       PDesc.YTsize  = m_EssenceSubDescriptor->YTsize;
176       PDesc.XTOsize = m_EssenceSubDescriptor->XTOsize;
177       PDesc.YTOsize = m_EssenceSubDescriptor->YTOsize;
178       PDesc.Csize   = m_EssenceSubDescriptor->Csize;
179
180       // PictureComponentSizing
181       ui32_t tmp_size = m_EssenceSubDescriptor->PictureComponentSizing.Length();
182
183       if ( tmp_size == 17 ) // ( 2 * sizeof(ui32_t) ) + 3 components * 3 byte each
184         memcpy(&PDesc.ImageComponents, m_EssenceSubDescriptor->PictureComponentSizing.RoData() + 8, tmp_size - 8);
185
186       else
187         DefaultLogSink().Error("Unexpected PictureComponentSizing size: %u, should be 17\n", tmp_size);
188
189       // CodingStyleDefault
190       memset(&m_PDesc.CodingStyleDefault, 0, sizeof(CodingStyleDefault_t));
191       memcpy(&m_PDesc.CodingStyleDefault,
192              m_EssenceSubDescriptor->CodingStyleDefault.RoData(),
193              m_EssenceSubDescriptor->CodingStyleDefault.Length());
194
195       // QuantizationDefault
196       memset(&m_PDesc.QuantizationDefault, 0, sizeof(QuantizationDefault_t));
197       memcpy(&m_PDesc.QuantizationDefault,
198              m_EssenceSubDescriptor->QuantizationDefault.RoData(),
199              m_EssenceSubDescriptor->QuantizationDefault.Length());
200
201       m_PDesc.QuantizationDefault.SPqcdLength = m_EssenceSubDescriptor->QuantizationDefault.Length() - 1;
202     }
203
204   return RESULT_OK;
205 }
206
207 //
208 //
209 ASDCP::Result_t
210 lh__Reader::OpenRead(const char* filename, EssenceType_t type)
211 {
212   Result_t result = OpenMXFRead(filename);
213
214   if( ASDCP_SUCCESS(result) )
215     {
216       InterchangeObject* tmp_iobj = 0;
217       m_HeaderPart.GetMDObjectByType(OBJ_TYPE_ARGS(RGBAEssenceDescriptor), &tmp_iobj);
218       m_EssenceDescriptor = static_cast<RGBAEssenceDescriptor*>(tmp_iobj);
219
220       m_HeaderPart.GetMDObjectByType(OBJ_TYPE_ARGS(JPEG2000PictureSubDescriptor), &tmp_iobj);
221       m_EssenceSubDescriptor = static_cast<JPEG2000PictureSubDescriptor*>(tmp_iobj);
222
223       std::list<InterchangeObject*> ObjectList;
224       m_HeaderPart.GetMDObjectsByType(OBJ_TYPE_ARGS(Track), ObjectList);
225
226       if ( ObjectList.empty() )
227         {
228           DefaultLogSink().Error("MXF Metadata contains no Track Sets\n");
229           return RESULT_FORMAT;
230         }
231
232       m_EditRate = ((Track*)ObjectList.front())->EditRate;
233
234       if ( type == ASDCP::ESS_JPEG_2000 )
235         {
236           if ( m_EditRate != m_EssenceDescriptor->SampleRate )
237             {
238               DefaultLogSink().Error("EditRate and SampleRate do not match (%.03f, %.03f)\n",
239                                      m_EditRate.Quotient(), m_EssenceDescriptor->SampleRate.Quotient());
240               return RESULT_SFORMAT;
241             }
242         }
243       else if ( type == ASDCP::ESS_JPEG_2000_S )
244         {
245           if ( ! ( m_EditRate == EditRate_24 && m_EssenceDescriptor->SampleRate == EditRate_48 ) )
246             {
247               DefaultLogSink().Error("EditRate and SampleRate not correct for 24/48 stereoscopic essence\n");
248               return RESULT_FORMAT;
249             }
250         }
251       else
252         {
253           DefaultLogSink().Error("'type' argument unexpected: %x\n", type);
254           return RESULT_STATE;
255         }
256
257       result = MD_to_JP2K_PDesc(m_PDesc);
258     }
259
260   if( ASDCP_SUCCESS(result) )
261     result = InitMXFIndex();
262
263   if( ASDCP_SUCCESS(result) )
264     result = InitInfo();
265
266   return result;
267 }
268
269 //
270 //
271 ASDCP::Result_t
272 lh__Reader::ReadFrame(ui32_t FrameNum, JP2K::FrameBuffer& FrameBuf,
273                       AESDecContext* Ctx, HMACContext* HMAC)
274 {
275   if ( ! m_File.IsOpen() )
276     return RESULT_INIT;
277
278   return ReadEKLVFrame(FrameNum, FrameBuf, Dict::ul(MDD_JPEG2000Essence), Ctx, HMAC);
279 }
280
281
282 //
283 class ASDCP::JP2K::MXFReader::h__Reader : public lh__Reader
284 {
285 };
286
287
288
289 //------------------------------------------------------------------------------------------
290
291
292 //
293 void
294 ASDCP::JP2K::FrameBuffer::Dump(FILE* stream, ui32_t dump_len) const
295 {
296   if ( stream == 0 )
297     stream = stderr;
298
299   fprintf(stream, "Frame: %06u, %7u bytes", m_FrameNumber, m_Size);
300   
301   fputc('\n', stream);
302
303   if ( dump_len > 0 )
304     Kumu::hexdump(m_Data, dump_len, stream);
305 }
306
307
308 //------------------------------------------------------------------------------------------
309
310 ASDCP::JP2K::MXFReader::MXFReader()
311 {
312   m_Reader = new h__Reader;
313 }
314
315
316 ASDCP::JP2K::MXFReader::~MXFReader()
317 {
318 }
319
320 // Open the file for reading. The file must exist. Returns error if the
321 // operation cannot be completed.
322 ASDCP::Result_t
323 ASDCP::JP2K::MXFReader::OpenRead(const char* filename) const
324 {
325   return m_Reader->OpenRead(filename, ASDCP::ESS_JPEG_2000);
326 }
327
328 //
329 ASDCP::Result_t
330 ASDCP::JP2K::MXFReader::ReadFrame(ui32_t FrameNum, FrameBuffer& FrameBuf,
331                                    AESDecContext* Ctx, HMACContext* HMAC) const
332 {
333   if ( m_Reader && m_Reader->m_File.IsOpen() )
334     return m_Reader->ReadFrame(FrameNum, FrameBuf, Ctx, HMAC);
335
336   return RESULT_INIT;
337 }
338
339
340 // Fill the struct with the values from the file's header.
341 // Returns RESULT_INIT if the file is not open.
342 ASDCP::Result_t
343 ASDCP::JP2K::MXFReader::FillPictureDescriptor(PictureDescriptor& PDesc) const
344 {
345   if ( m_Reader && m_Reader->m_File.IsOpen() )
346     {
347       PDesc = m_Reader->m_PDesc;
348       return RESULT_OK;
349     }
350
351   return RESULT_INIT;
352 }
353
354
355 // Fill the struct with the values from the file's header.
356 // Returns RESULT_INIT if the file is not open.
357 ASDCP::Result_t
358 ASDCP::JP2K::MXFReader::FillWriterInfo(WriterInfo& Info) const
359 {
360   if ( m_Reader && m_Reader->m_File.IsOpen() )
361     {
362       Info = m_Reader->m_Info;
363       return RESULT_OK;
364     }
365
366   return RESULT_INIT;
367 }
368
369 //
370 void
371 ASDCP::JP2K::MXFReader::DumpHeaderMetadata(FILE* stream) const
372 {
373   if ( m_Reader->m_File.IsOpen() )
374     m_Reader->m_HeaderPart.Dump(stream);
375 }
376
377
378 //
379 void
380 ASDCP::JP2K::MXFReader::DumpIndex(FILE* stream) const
381 {
382   if ( m_Reader->m_File.IsOpen() )
383     m_Reader->m_FooterPart.Dump(stream);
384 }
385
386
387 //------------------------------------------------------------------------------------------
388
389 class ASDCP::JP2K::MXFSReader::h__SReader : public lh__Reader
390 {
391   ui32_t m_StereoFrameReady;
392
393 public:
394   h__SReader() : m_StereoFrameReady(0xffffffff) {}
395
396   //
397   Result_t ReadFrame(ui32_t FrameNum, StereoscopicPhase_t phase, FrameBuffer& FrameBuf,
398                      AESDecContext* Ctx, HMACContext* HMAC)
399   {
400     // look up frame index node
401     IndexTableSegment::IndexEntry TmpEntry;
402
403     if ( ASDCP_FAILURE(m_FooterPart.Lookup(FrameNum, TmpEntry)) )
404       {
405         DefaultLogSink().Error("Frame value out of range: %u\n", FrameNum);
406         return RESULT_RANGE;
407       }
408
409     // get frame position
410     Kumu::fpos_t FilePosition = m_EssenceStart + TmpEntry.StreamOffset;
411     Result_t result = RESULT_OK;
412
413     if ( phase == SP_LEFT )
414       {    
415         if ( FilePosition != m_LastPosition )
416           {
417             m_LastPosition = FilePosition;
418             result = m_File.Seek(FilePosition);
419           }
420
421         // the call to ReadEKLVPacket() will leave the file on an R frame
422         m_StereoFrameReady = FrameNum;
423       }
424     else if ( phase == SP_RIGHT )
425       {
426         if ( m_StereoFrameReady != FrameNum )
427           {
428             // the file is not already positioned, we must do some work
429             // seek to the companion SP_LEFT frame and read the frame's key and length
430             if ( FilePosition != m_LastPosition )
431               {
432                 m_LastPosition = FilePosition;
433                 result = m_File.Seek(FilePosition);
434               }
435
436             KLReader Reader;
437             result = Reader.ReadKLFromFile(m_File);
438
439             if ( ASDCP_SUCCESS(result) )
440               {
441                 // skip over the companion SP_LEFT frame
442                 Kumu::fpos_t new_pos = FilePosition + SMPTE_UL_LENGTH + Reader.KLLength() + Reader.Length();
443                 result = m_File.Seek(new_pos);
444               }
445           }
446
447         // the call to ReadEKLVPacket() will leave the file not on an R frame
448         m_StereoFrameReady = 0xffffffff;
449       }
450     else
451       {
452         DefaultLogSink().Error("Unexpected stereoscopic phase value: %u\n", phase);
453         return RESULT_STATE;
454       }
455
456     if( ASDCP_SUCCESS(result) )
457       {
458         ui32_t SequenceNum = FrameNum * 2;
459         SequenceNum += ( phase == SP_RIGHT ) ? 2 : 1;
460         result = ReadEKLVPacket(FrameNum, SequenceNum, FrameBuf, Dict::ul(MDD_JPEG2000Essence), Ctx, HMAC);
461       }
462
463     return result;
464   }
465 };
466
467
468
469 ASDCP::JP2K::MXFSReader::MXFSReader()
470 {
471   m_Reader = new h__SReader;
472 }
473
474
475 ASDCP::JP2K::MXFSReader::~MXFSReader()
476 {
477 }
478
479 // Open the file for reading. The file must exist. Returns error if the
480 // operation cannot be completed.
481 ASDCP::Result_t
482 ASDCP::JP2K::MXFSReader::OpenRead(const char* filename) const
483 {
484   return m_Reader->OpenRead(filename, ASDCP::ESS_JPEG_2000_S);
485 }
486
487 //
488 ASDCP::Result_t
489 ASDCP::JP2K::MXFSReader::ReadFrame(ui32_t FrameNum, StereoscopicPhase_t phase, FrameBuffer& FrameBuf,
490                                    AESDecContext* Ctx, HMACContext* HMAC) const
491 {
492   if ( m_Reader && m_Reader->m_File.IsOpen() )
493     return m_Reader->ReadFrame(FrameNum, phase, FrameBuf, Ctx, HMAC);
494
495   return RESULT_INIT;
496 }
497
498
499 // Fill the struct with the values from the file's header.
500 // Returns RESULT_INIT if the file is not open.
501 ASDCP::Result_t
502 ASDCP::JP2K::MXFSReader::FillPictureDescriptor(PictureDescriptor& PDesc) const
503 {
504   if ( m_Reader && m_Reader->m_File.IsOpen() )
505     {
506       PDesc = m_Reader->m_PDesc;
507       return RESULT_OK;
508     }
509
510   return RESULT_INIT;
511 }
512
513
514 // Fill the struct with the values from the file's header.
515 // Returns RESULT_INIT if the file is not open.
516 ASDCP::Result_t
517 ASDCP::JP2K::MXFSReader::FillWriterInfo(WriterInfo& Info) const
518 {
519   if ( m_Reader && m_Reader->m_File.IsOpen() )
520     {
521       Info = m_Reader->m_Info;
522       return RESULT_OK;
523     }
524
525   return RESULT_INIT;
526 }
527
528 //
529 void
530 ASDCP::JP2K::MXFSReader::DumpHeaderMetadata(FILE* stream) const
531 {
532   if ( m_Reader->m_File.IsOpen() )
533     m_Reader->m_HeaderPart.Dump(stream);
534 }
535
536
537 //
538 void
539 ASDCP::JP2K::MXFSReader::DumpIndex(FILE* stream) const
540 {
541   if ( m_Reader->m_File.IsOpen() )
542     m_Reader->m_FooterPart.Dump(stream);
543 }
544
545 //------------------------------------------------------------------------------------------
546
547
548 //
549 class lh__Writer : public ASDCP::h__Writer
550 {
551   JPEG2000PictureSubDescriptor* m_EssenceSubDescriptor;
552
553 public:
554   PictureDescriptor m_PDesc;
555   byte_t            m_EssenceUL[SMPTE_UL_LENGTH];
556
557   ASDCP_NO_COPY_CONSTRUCT(lh__Writer);
558
559   lh__Writer() : m_EssenceSubDescriptor(0) {
560     memset(m_EssenceUL, 0, SMPTE_UL_LENGTH);
561   }
562
563   ~lh__Writer(){}
564
565   Result_t OpenWrite(const char*, EssenceType_t type, ui32_t HeaderSize);
566   Result_t SetSourceStream(const PictureDescriptor&, const std::string& label,
567                            ASDCP::Rational LocalEditRate = ASDCP::Rational(0,0));
568   Result_t WriteFrame(const JP2K::FrameBuffer&, bool add_index, AESEncContext*, HMACContext*);
569   Result_t Finalize();
570   Result_t JP2K_PDesc_to_MD(JP2K::PictureDescriptor& PDesc);
571 };
572
573 const int VideoLineMapSize = 16; // See SMPTE 377M D.2.1
574 const int PixelLayoutSize = 8*2; // See SMPTE 377M D.2.3
575 static const byte_t s_PixelLayoutXYZ[PixelLayoutSize] = { 0xd8, 0x0c, 0xd9, 0x0c, 0xda, 0x0c, 0x00 };
576
577 //
578 ASDCP::Result_t
579 lh__Writer::JP2K_PDesc_to_MD(JP2K::PictureDescriptor& PDesc)
580 {
581   assert(m_EssenceDescriptor);
582   assert(m_EssenceSubDescriptor);
583   MXF::RGBAEssenceDescriptor* PDescObj = (MXF::RGBAEssenceDescriptor*)m_EssenceDescriptor;
584
585   PDescObj->ContainerDuration = PDesc.ContainerDuration;
586   PDescObj->SampleRate = PDesc.EditRate;
587   PDescObj->FrameLayout = 0;
588   PDescObj->StoredWidth = PDesc.StoredWidth;
589   PDescObj->StoredHeight = PDesc.StoredHeight;
590   PDescObj->AspectRatio = PDesc.AspectRatio;
591
592   //  if ( m_Info.LabelSetType == LS_MXF_SMPTE )
593   //    {
594   // PictureEssenceCoding UL = 
595   // Video Line Map       ui32_t[VideoLineMapSize] = { 2, 4, 0, 0 }
596   // CaptureGamma         UL = 
597   // ComponentMaxRef      ui32_t = 4095
598   // ComponentMinRef      ui32_t = 0
599   // PixelLayout          byte_t[PixelLayoutSize] = s_PixelLayoutXYZ
600   //    }
601
602   if ( PDesc.StoredWidth < 2049 )
603     {
604       PDescObj->PictureEssenceCoding.Set(Dict::ul(MDD_JP2KEssenceCompression_2K));
605       m_EssenceSubDescriptor->Rsize = 3;
606     }
607   else
608     {
609       PDescObj->PictureEssenceCoding.Set(Dict::ul(MDD_JP2KEssenceCompression_4K));
610       m_EssenceSubDescriptor->Rsize = 4;
611     }
612
613   m_EssenceSubDescriptor->Xsize = PDesc.Xsize;
614   m_EssenceSubDescriptor->Ysize = PDesc.Ysize;
615   m_EssenceSubDescriptor->XOsize = PDesc.XOsize;
616   m_EssenceSubDescriptor->YOsize = PDesc.YOsize;
617   m_EssenceSubDescriptor->XTsize = PDesc.XTsize;
618   m_EssenceSubDescriptor->YTsize = PDesc.YTsize;
619   m_EssenceSubDescriptor->XTOsize = PDesc.XTOsize;
620   m_EssenceSubDescriptor->YTOsize = PDesc.YTOsize;
621   m_EssenceSubDescriptor->Csize = PDesc.Csize;
622
623   const ui32_t tmp_buffer_len = 1024;
624   byte_t tmp_buffer[tmp_buffer_len];
625
626   *(ui32_t*)tmp_buffer = KM_i32_BE(MaxComponents); // three components
627   *(ui32_t*)(tmp_buffer+4) = KM_i32_BE(sizeof(ASDCP::JP2K::ImageComponent_t));
628   memcpy(tmp_buffer + 8, &PDesc.ImageComponents, sizeof(ASDCP::JP2K::ImageComponent_t) * MaxComponents);
629
630   const ui32_t pcomp_size = (sizeof(int) * 2) + (sizeof(ASDCP::JP2K::ImageComponent_t) * MaxComponents);
631   memcpy(m_EssenceSubDescriptor->PictureComponentSizing.Data(), tmp_buffer, pcomp_size);
632   m_EssenceSubDescriptor->PictureComponentSizing.Length(pcomp_size);
633
634   ui32_t precinct_set_size = 0, i;
635   for ( i = 0; PDesc.CodingStyleDefault.SPcod.PrecinctSize[i] != 0 && i < MaxPrecincts; i++ )
636     precinct_set_size++;
637
638   ui32_t csd_size = sizeof(CodingStyleDefault_t) - MaxPrecincts + precinct_set_size;
639   memcpy(m_EssenceSubDescriptor->CodingStyleDefault.Data(), &PDesc.CodingStyleDefault, csd_size);
640   m_EssenceSubDescriptor->CodingStyleDefault.Length(csd_size);
641
642   ui32_t qdflt_size = PDesc.QuantizationDefault.SPqcdLength + 1;
643   memcpy(m_EssenceSubDescriptor->QuantizationDefault.Data(), &PDesc.QuantizationDefault, qdflt_size);
644   m_EssenceSubDescriptor->QuantizationDefault.Length(qdflt_size);
645
646   return RESULT_OK;
647 }
648
649
650 // Open the file for writing. The file must not exist. Returns error if
651 // the operation cannot be completed.
652 ASDCP::Result_t
653 lh__Writer::OpenWrite(const char* filename, EssenceType_t type, ui32_t HeaderSize)
654 {
655   if ( ! m_State.Test_BEGIN() )
656     return RESULT_STATE;
657
658   Result_t result = m_File.OpenWrite(filename);
659
660   if ( ASDCP_SUCCESS(result) )
661     {
662       m_HeaderSize = HeaderSize;
663       RGBAEssenceDescriptor* tmp_rgba = new RGBAEssenceDescriptor;
664       tmp_rgba->ComponentMaxRef = 4095;
665       tmp_rgba->ComponentMinRef = 0;
666
667       m_EssenceDescriptor = tmp_rgba;
668       m_EssenceSubDescriptor = new JPEG2000PictureSubDescriptor;
669       m_EssenceSubDescriptorList.push_back((InterchangeObject*)m_EssenceSubDescriptor);
670
671       GenRandomValue(m_EssenceSubDescriptor->InstanceUID);
672       m_EssenceDescriptor->SubDescriptors.push_back(m_EssenceSubDescriptor->InstanceUID);
673
674       if ( type == ASDCP::ESS_JPEG_2000_S && m_Info.LabelSetType == LS_MXF_SMPTE )
675         {
676           InterchangeObject* StereoSubDesc = new StereoscopicPictureSubDescriptor;
677           m_EssenceSubDescriptorList.push_back(StereoSubDesc);
678           GenRandomValue(StereoSubDesc->InstanceUID);
679           m_EssenceDescriptor->SubDescriptors.push_back(StereoSubDesc->InstanceUID);
680         }
681
682       result = m_State.Goto_INIT();
683     }
684
685   return result;
686 }
687
688 // Automatically sets the MXF file's metadata from the first jpeg codestream stream.
689 ASDCP::Result_t
690 lh__Writer::SetSourceStream(const PictureDescriptor& PDesc, const std::string& label, ASDCP::Rational LocalEditRate)
691 {
692   if ( ! m_State.Test_INIT() )
693     return RESULT_STATE;
694
695   if ( LocalEditRate == ASDCP::Rational(0,0) )
696     LocalEditRate = PDesc.EditRate;
697
698   m_PDesc = PDesc;
699   Result_t result = JP2K_PDesc_to_MD(m_PDesc);
700
701   if ( ASDCP_SUCCESS(result) )
702       result = WriteMXFHeader(label, UL(Dict::ul(MDD_JPEG_2000Wrapping)),
703                               PICT_DEF_LABEL,     UL(Dict::ul(MDD_PictureDataDef)),
704                               LocalEditRate, 24 /* TCFrameRate */);
705
706   if ( ASDCP_SUCCESS(result) )
707     {
708       memcpy(m_EssenceUL, Dict::ul(MDD_JPEG2000Essence), SMPTE_UL_LENGTH);
709       m_EssenceUL[SMPTE_UL_LENGTH-1] = 1; // first (and only) essence container
710       result = m_State.Goto_READY();
711     }
712
713   return result;
714 }
715
716 // Writes a frame of essence to the MXF file. If the optional AESEncContext
717 // argument is present, the essence is encrypted prior to writing.
718 // Fails if the file is not open, is finalized, or an operating system
719 // error occurs.
720 //
721 ASDCP::Result_t
722 lh__Writer::WriteFrame(const JP2K::FrameBuffer& FrameBuf, bool add_index,
723                        AESEncContext* Ctx, HMACContext* HMAC)
724 {
725   Result_t result = RESULT_OK;
726
727   if ( m_State.Test_READY() )
728     result = m_State.Goto_RUNNING(); // first time through
729  
730   ui64_t StreamOffset = m_StreamOffset;
731
732   if ( ASDCP_SUCCESS(result) )
733     result = WriteEKLVPacket(FrameBuf, m_EssenceUL, Ctx, HMAC);
734
735   if ( ASDCP_SUCCESS(result) && add_index )
736     {  
737       IndexTableSegment::IndexEntry Entry;
738       Entry.StreamOffset = StreamOffset;
739       m_FooterPart.PushIndexEntry(Entry);
740     }
741
742   m_FramesWritten++;
743   return result;
744 }
745
746
747 // Closes the MXF file, writing the index and other closing information.
748 //
749 ASDCP::Result_t
750 lh__Writer::Finalize()
751 {
752   if ( ! m_State.Test_RUNNING() )
753     return RESULT_STATE;
754
755   m_State.Goto_FINAL();
756
757   return WriteMXFFooter();
758 }
759
760
761 //
762 class ASDCP::JP2K::MXFWriter::h__Writer : public lh__Writer
763 {
764 };
765
766
767 //------------------------------------------------------------------------------------------
768
769
770
771 ASDCP::JP2K::MXFWriter::MXFWriter()
772 {
773 }
774
775 ASDCP::JP2K::MXFWriter::~MXFWriter()
776 {
777 }
778
779
780 // Open the file for writing. The file must not exist. Returns error if
781 // the operation cannot be completed.
782 ASDCP::Result_t
783 ASDCP::JP2K::MXFWriter::OpenWrite(const char* filename, const WriterInfo& Info,
784                                   const PictureDescriptor& PDesc, ui32_t HeaderSize)
785 {
786   m_Writer = new h__Writer;
787   
788   Result_t result = m_Writer->OpenWrite(filename, ASDCP::ESS_JPEG_2000, HeaderSize);
789
790   if ( ASDCP_SUCCESS(result) )
791     {
792       m_Writer->m_Info = Info;
793       result = m_Writer->SetSourceStream(PDesc, JP2K_PACKAGE_LABEL);
794     }
795
796   if ( ASDCP_FAILURE(result) )
797     m_Writer.release();
798
799   return result;
800 }
801
802
803 // Writes a frame of essence to the MXF file. If the optional AESEncContext
804 // argument is present, the essence is encrypted prior to writing.
805 // Fails if the file is not open, is finalized, or an operating system
806 // error occurs.
807 ASDCP::Result_t
808 ASDCP::JP2K::MXFWriter::WriteFrame(const FrameBuffer& FrameBuf, AESEncContext* Ctx, HMACContext* HMAC)
809 {
810   if ( m_Writer.empty() )
811     return RESULT_INIT;
812
813   return m_Writer->WriteFrame(FrameBuf, true, Ctx, HMAC);
814 }
815
816 // Closes the MXF file, writing the index and other closing information.
817 ASDCP::Result_t
818 ASDCP::JP2K::MXFWriter::Finalize()
819 {
820   if ( m_Writer.empty() )
821     return RESULT_INIT;
822
823   return m_Writer->Finalize();
824 }
825
826
827 //------------------------------------------------------------------------------------------
828 //
829
830 //
831 class ASDCP::JP2K::MXFSWriter::h__SWriter : public lh__Writer
832 {
833   StereoscopicPhase_t m_NextPhase;
834
835 public:
836   h__SWriter() : m_NextPhase(SP_LEFT) {}
837
838   //
839   Result_t WriteFrame(const FrameBuffer& FrameBuf, StereoscopicPhase_t phase,
840                       AESEncContext* Ctx, HMACContext* HMAC)
841   {
842     if ( m_NextPhase != phase )
843       return RESULT_SPHASE;
844
845     if ( phase == SP_LEFT )
846       {
847         m_NextPhase = SP_RIGHT;
848         return lh__Writer::WriteFrame(FrameBuf, true, Ctx, HMAC);
849       }
850
851     m_NextPhase = SP_LEFT;
852     return lh__Writer::WriteFrame(FrameBuf, false, Ctx, HMAC);
853   }
854
855   //
856   Result_t Finalize()
857   {
858     if ( m_NextPhase != SP_LEFT )
859       return RESULT_SPHASE;
860
861     assert( m_FramesWritten % 2 == 0 );
862     m_FramesWritten /= 2;
863     return lh__Writer::Finalize();
864   }
865 };
866
867
868 //
869 ASDCP::JP2K::MXFSWriter::MXFSWriter()
870 {
871 }
872
873 ASDCP::JP2K::MXFSWriter::~MXFSWriter()
874 {
875 }
876
877
878 // Open the file for writing. The file must not exist. Returns error if
879 // the operation cannot be completed.
880 ASDCP::Result_t
881 ASDCP::JP2K::MXFSWriter::OpenWrite(const char* filename, const WriterInfo& Info,
882                                    const PictureDescriptor& PDesc, ui32_t HeaderSize)
883 {
884   m_Writer = new h__SWriter;
885   
886   if ( PDesc.EditRate != ASDCP::EditRate_24 )
887     {
888       DefaultLogSink().Error("Stereoscopic wrapping requires 24 fps input streams.\n");
889       return RESULT_FORMAT;
890     }
891
892   Result_t result = m_Writer->OpenWrite(filename, ASDCP::ESS_JPEG_2000_S, HeaderSize);
893
894   if ( ASDCP_SUCCESS(result) )
895     {
896       m_Writer->m_Info = Info;
897       PictureDescriptor TmpPDesc = PDesc;
898       TmpPDesc.EditRate = ASDCP::EditRate_48;
899
900       result = m_Writer->SetSourceStream(TmpPDesc, JP2K_S_PACKAGE_LABEL, ASDCP::EditRate_24);
901     }
902
903   if ( ASDCP_FAILURE(result) )
904     m_Writer.release();
905
906   return result;
907 }
908
909
910 // Writes a frame of essence to the MXF file. If the optional AESEncContext
911 // argument is present, the essence is encrypted prior to writing.
912 // Fails if the file is not open, is finalized, or an operating system
913 // error occurs.
914 ASDCP::Result_t
915 ASDCP::JP2K::MXFSWriter::WriteFrame(const FrameBuffer& FrameBuf, StereoscopicPhase_t phase,
916                                     AESEncContext* Ctx, HMACContext* HMAC)
917 {
918   if ( m_Writer.empty() )
919     return RESULT_INIT;
920
921   return m_Writer->WriteFrame(FrameBuf, phase, Ctx, HMAC);
922 }
923
924 // Closes the MXF file, writing the index and other closing information.
925 ASDCP::Result_t
926 ASDCP::JP2K::MXFSWriter::Finalize()
927 {
928   if ( m_Writer.empty() )
929     return RESULT_INIT;
930
931   return m_Writer->Finalize();
932 }
933
934 //
935 // end AS_DCP_JP2K.cpp
936 //