aeb6dbc6550aea4a27596ba20bdfb29971bc298a
[asdcplib.git] / src / AS_02_JP2K.cpp
1 /*
2   Copyright (c) 2011-2013, Robert Scheler, Heiko Sparenberg Fraunhofer IIS, 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_02_JP2K.cpp
28   \version $Id$
29   \brief   AS-02 library, JPEG 2000 essence reader and writer implementation
30 */
31
32 #include "AS_02_internal.h"
33
34 #include <iostream>
35 #include <iomanip>
36
37 using namespace ASDCP;
38 using namespace ASDCP::JP2K;
39 using Kumu::GenRandomValue;
40
41 //------------------------------------------------------------------------------------------
42
43 static std::string JP2K_PACKAGE_LABEL = "File Package: SMPTE ST 422 / ST 2067-5 frame wrapping of JPEG 2000 codestreams";
44 static std::string PICT_DEF_LABEL = "Image Track";
45
46 //------------------------------------------------------------------------------------------
47 //
48 // hidden, internal implementation of JPEG 2000 reader
49
50
51 class AS_02::JP2K::MXFReader::h__Reader : public AS_02::h__AS02Reader
52 {
53   RGBAEssenceDescriptor*        m_RGBAEssenceDescriptor;
54   CDCIEssenceDescriptor*        m_CDCIEssenceDescriptor;
55   JPEG2000PictureSubDescriptor* m_EssenceSubDescriptor;
56   ASDCP::Rational               m_EditRate;
57   ASDCP::Rational               m_SampleRate;
58   EssenceType_t                 m_Format;
59
60   ASDCP_NO_COPY_CONSTRUCT(h__Reader);
61
62 public:
63   PictureDescriptor m_PDesc;        // codestream parameter list
64
65   h__Reader(const Dictionary& d) :
66     AS_02::h__AS02Reader(d), m_RGBAEssenceDescriptor(0), m_CDCIEssenceDescriptor(0),
67     m_EssenceSubDescriptor(0), m_Format(ESS_UNKNOWN) {}
68
69   virtual ~h__Reader() {}
70
71   Result_t    OpenRead(const char*);
72   Result_t    ReadFrame(ui32_t, ASDCP::JP2K::FrameBuffer&, AESDecContext*, HMACContext*);
73 };
74
75 //
76 Result_t
77 AS_02::JP2K::MXFReader::h__Reader::OpenRead(const char* filename)
78 {
79   Result_t result = OpenMXFRead(filename);
80
81   if( ASDCP_SUCCESS(result) )
82     {
83       InterchangeObject* tmp_iobj = 0;
84
85       m_HeaderPart.GetMDObjectByType(OBJ_TYPE_ARGS(CDCIEssenceDescriptor), &tmp_iobj);
86       m_CDCIEssenceDescriptor = static_cast<CDCIEssenceDescriptor*>(tmp_iobj);
87
88       if ( m_CDCIEssenceDescriptor == 0 )
89         {
90           m_HeaderPart.GetMDObjectByType(OBJ_TYPE_ARGS(RGBAEssenceDescriptor), &tmp_iobj);
91           m_RGBAEssenceDescriptor = static_cast<RGBAEssenceDescriptor*>(tmp_iobj);
92         }
93
94       if ( m_CDCIEssenceDescriptor == 0 && m_RGBAEssenceDescriptor == 0 )
95         {
96           DefaultLogSink().Error("RGBAEssenceDescriptor nor CDCIEssenceDescriptor found.\n");
97           return RESULT_FORMAT;
98         }
99
100       m_HeaderPart.GetMDObjectByType(OBJ_TYPE_ARGS(JPEG2000PictureSubDescriptor), &tmp_iobj);
101       m_EssenceSubDescriptor = static_cast<JPEG2000PictureSubDescriptor*>(tmp_iobj);
102
103       if ( m_EssenceSubDescriptor == 0 )
104         {
105           DefaultLogSink().Error("JPEG2000PictureSubDescriptor not found.\n");
106           return RESULT_FORMAT;
107         }
108
109       std::list<InterchangeObject*> ObjectList;
110       m_HeaderPart.GetMDObjectsByType(OBJ_TYPE_ARGS(Track), ObjectList);
111
112       if ( ObjectList.empty() )
113         {
114           DefaultLogSink().Error("MXF Metadata contains no Track Sets.\n");
115           return RESULT_FORMAT;
116         }
117
118       if ( m_CDCIEssenceDescriptor != 0 )
119         {
120           m_EditRate = ((Track*)ObjectList.front())->EditRate;
121           m_SampleRate = m_CDCIEssenceDescriptor->SampleRate;
122           result = MD_to_JP2K_PDesc(*m_CDCIEssenceDescriptor, *m_EssenceSubDescriptor, m_EditRate, m_SampleRate, m_PDesc);
123         }
124       else if ( m_RGBAEssenceDescriptor != 0 )
125         {
126           m_EditRate = ((Track*)ObjectList.front())->EditRate;
127           m_SampleRate = m_RGBAEssenceDescriptor->SampleRate;
128           result = MD_to_JP2K_PDesc(*m_RGBAEssenceDescriptor, *m_EssenceSubDescriptor, m_EditRate, m_SampleRate, m_PDesc);
129         }
130
131       if ( m_PDesc.ContainerDuration == 0 )
132         {
133           m_PDesc.ContainerDuration = m_IndexAccess.GetDuration();
134         }
135     }
136
137   return result;
138 }
139
140 //
141 //
142 Result_t
143 AS_02::JP2K::MXFReader::h__Reader::ReadFrame(ui32_t FrameNum, ASDCP::JP2K::FrameBuffer& FrameBuf,
144                       ASDCP::AESDecContext* Ctx, ASDCP::HMACContext* HMAC)
145 {
146   if ( ! m_File.IsOpen() )
147     return RESULT_INIT;
148
149   assert(m_Dict);
150   return ReadEKLVFrame(FrameNum, FrameBuf, m_Dict->ul(MDD_JPEG2000Essence), Ctx, HMAC);
151 }
152
153 //------------------------------------------------------------------------------------------
154 //
155
156 AS_02::JP2K::MXFReader::MXFReader()
157 {
158   m_Reader = new h__Reader(DefaultCompositeDict());
159 }
160
161
162 AS_02::JP2K::MXFReader::~MXFReader()
163 {
164 }
165
166 // Warning: direct manipulation of MXF structures can interfere
167 // with the normal operation of the wrapper.  Caveat emptor!
168 //
169 ASDCP::MXF::OP1aHeader&
170 AS_02::JP2K::MXFReader::OP1aHeader()
171 {
172   if ( m_Reader.empty() )
173     {
174       assert(g_OP1aHeader);
175       return *g_OP1aHeader;
176     }
177
178   return m_Reader->m_HeaderPart;
179 }
180
181 // Warning: direct manipulation of MXF structures can interfere
182 // with the normal operation of the wrapper.  Caveat emptor!
183 //
184 AS_02::MXF::AS02IndexReader&
185 AS_02::JP2K::MXFReader::AS02IndexReader()
186 {
187   if ( m_Reader.empty() )
188     {
189       assert(g_AS02IndexReader);
190       return *g_AS02IndexReader;
191     }
192
193   return m_Reader->m_IndexAccess;
194 }
195
196 // Warning: direct manipulation of MXF structures can interfere
197 // with the normal operation of the wrapper.  Caveat emptor!
198 //
199 ASDCP::MXF::RIP&
200 AS_02::JP2K::MXFReader::RIP()
201 {
202   if ( m_Reader.empty() )
203     {
204       assert(g_RIP);
205       return *g_RIP;
206     }
207
208   return m_Reader->m_RIP;
209 }
210
211 // Open the file for reading. The file must exist. Returns error if the
212 // operation cannot be completed.
213 Result_t
214 AS_02::JP2K::MXFReader::OpenRead(const char* filename) const
215 {
216   return m_Reader->OpenRead(filename);
217 }
218
219 //
220 Result_t
221 AS_02::JP2K::MXFReader::ReadFrame(ui32_t FrameNum, ASDCP::JP2K::FrameBuffer& FrameBuf,
222                                            ASDCP::AESDecContext* Ctx, ASDCP::HMACContext* HMAC) const
223 {
224   if ( m_Reader && m_Reader->m_File.IsOpen() )
225     return m_Reader->ReadFrame(FrameNum, FrameBuf, Ctx, HMAC);
226
227   return RESULT_INIT;
228 }
229
230
231 // Fill the struct with the values from the file's header.
232 // Returns RESULT_INIT if the file is not open.
233 Result_t
234 AS_02::JP2K::MXFReader::FillPictureDescriptor(PictureDescriptor& PDesc) const
235 {
236   if ( m_Reader && m_Reader->m_File.IsOpen() )
237     {
238       PDesc = m_Reader->m_PDesc;
239       return RESULT_OK;
240     }
241
242   return RESULT_INIT;
243 }
244
245
246 // Fill the struct with the values from the file's header.
247 // Returns RESULT_INIT if the file is not open.
248 Result_t
249 AS_02::JP2K::MXFReader::FillWriterInfo(WriterInfo& Info) const
250 {
251   if ( m_Reader && m_Reader->m_File.IsOpen() )
252     {
253       Info = m_Reader->m_Info;
254       return RESULT_OK;
255     }
256
257   return RESULT_INIT;
258 }
259
260
261 //------------------------------------------------------------------------------------------
262
263 //
264 class AS_02::JP2K::MXFWriter::h__Writer : public AS_02::h__AS02Writer
265 {
266   ASDCP_NO_COPY_CONSTRUCT(h__Writer);
267   h__Writer();
268
269   JPEG2000PictureSubDescriptor* m_EssenceSubDescriptor;
270
271 public:
272   PictureDescriptor m_PDesc;
273   byte_t            m_EssenceUL[SMPTE_UL_LENGTH];
274
275   h__Writer(const Dictionary& d) : h__AS02Writer(d), m_EssenceSubDescriptor(0) {
276     memset(m_EssenceUL, 0, SMPTE_UL_LENGTH);
277   }
278
279   virtual ~h__Writer(){}
280
281   Result_t OpenWrite(const char*, EssenceType_t type, const AS_02::IndexStrategy_t& IndexStrategy,
282                      const ui32_t& PartitionSpace, const ui32_t& HeaderSize);
283   Result_t SetSourceStream(const PictureDescriptor&, const std::string& label,
284                            ASDCP::Rational LocalEditRate = ASDCP::Rational(0,0));
285   Result_t WriteFrame(const ASDCP::JP2K::FrameBuffer&, ASDCP::AESEncContext*, ASDCP::HMACContext*);
286   Result_t Finalize();
287 };
288
289
290 // Open the file for writing. The file must not exist. Returns error if
291 // the operation cannot be completed.
292 Result_t
293 AS_02::JP2K::MXFWriter::h__Writer::OpenWrite(const char* filename, EssenceType_t type, const AS_02::IndexStrategy_t& IndexStrategy,
294                                              const ui32_t& PartitionSpace_sec, const ui32_t& HeaderSize)
295 {
296   if ( ! m_State.Test_BEGIN() )
297     return RESULT_STATE;
298
299   if ( m_IndexStrategy != AS_02::IS_FOLLOW )
300     {
301       DefaultLogSink().Error("Only strategy IS_FOLLOW is supported at this time.\n");
302       return Kumu::RESULT_NOTIMPL;
303     }
304
305   Result_t result = m_File.OpenWrite(filename);
306
307   if ( ASDCP_SUCCESS(result) )
308     {
309       m_IndexStrategy = IndexStrategy;
310       m_PartitionSpace = PartitionSpace_sec; // later converted to edit units by SetSourceStream()
311       m_HeaderSize = HeaderSize;
312
313       m_EssenceDescriptor = new RGBAEssenceDescriptor(m_Dict);
314       m_EssenceSubDescriptor = new JPEG2000PictureSubDescriptor(m_Dict);
315       m_EssenceSubDescriptorList.push_back((InterchangeObject*)m_EssenceSubDescriptor);
316
317       GenRandomValue(m_EssenceSubDescriptor->InstanceUID);
318       m_EssenceDescriptor->SubDescriptors.push_back(m_EssenceSubDescriptor->InstanceUID);
319       result = m_State.Goto_INIT();
320     }
321
322   return result;
323 }
324
325 // Automatically sets the MXF file's metadata from the first jpeg codestream stream.
326 Result_t
327 AS_02::JP2K::MXFWriter::h__Writer::SetSourceStream(const PictureDescriptor& PDesc, const std::string& label, ASDCP::Rational LocalEditRate)
328 {
329   assert(m_Dict);
330   if ( ! m_State.Test_INIT() )
331     return RESULT_STATE;
332
333   if ( LocalEditRate == ASDCP::Rational(0,0) )
334     LocalEditRate = PDesc.EditRate;
335
336   m_PDesc = PDesc;
337   assert(m_Dict);
338   Result_t result = JP2K_PDesc_to_MD(m_PDesc, *m_Dict,
339                                      static_cast<ASDCP::MXF::RGBAEssenceDescriptor*>(m_EssenceDescriptor),
340                                      m_EssenceSubDescriptor);
341
342   static_cast<ASDCP::MXF::RGBAEssenceDescriptor*>(m_EssenceDescriptor)->ComponentMaxRef = 4095; /// TODO: set with magic or some such thing
343   static_cast<ASDCP::MXF::RGBAEssenceDescriptor*>(m_EssenceDescriptor)->ComponentMinRef = 0;
344
345   if ( ASDCP_SUCCESS(result) )
346     {
347       memcpy(m_EssenceUL, m_Dict->ul(MDD_JPEG2000Essence), SMPTE_UL_LENGTH);
348       m_EssenceUL[SMPTE_UL_LENGTH-1] = 1; // first (and only) essence container
349       result = m_State.Goto_READY();
350     }
351
352   if ( ASDCP_SUCCESS(result) )
353     {
354       ui32_t TCFrameRate = ( m_PDesc.EditRate == EditRate_23_98  ) ? 24 : m_PDesc.EditRate.Numerator;
355
356       result = WriteAS02Header(label, UL(m_Dict->ul(MDD_JPEG_2000Wrapping)),
357                                PICT_DEF_LABEL, UL(m_EssenceUL), UL(m_Dict->ul(MDD_PictureDataDef)),
358                                LocalEditRate, TCFrameRate);
359     }
360
361   return result;
362 }
363
364 // Writes a frame of essence to the MXF file. If the optional AESEncContext
365 // argument is present, the essence is encrypted prior to writing.
366 // Fails if the file is not open, is finalized, or an operating system
367 // error occurs.
368 //
369 Result_t
370 AS_02::JP2K::MXFWriter::h__Writer::WriteFrame(const ASDCP::JP2K::FrameBuffer& FrameBuf,
371                        AESEncContext* Ctx, HMACContext* HMAC)
372 {
373   Result_t result = RESULT_OK;
374
375   if ( m_State.Test_READY() )
376     result = m_State.Goto_RUNNING(); // first time through
377  
378   ui64_t StreamOffset = m_StreamOffset; // m_StreamOffset will be changed by the call to WriteEKLVPacket
379
380   if ( ASDCP_SUCCESS(result) )
381     result = WriteEKLVPacket(FrameBuf, m_EssenceUL, Ctx, HMAC);
382
383   if ( ASDCP_SUCCESS(result) )
384     {  
385       IndexTableSegment::IndexEntry Entry;
386       Entry.StreamOffset = StreamOffset;
387       m_IndexWriter.PushIndexEntry(Entry);
388     }
389
390   m_FramesWritten++;
391   return result;
392 }
393
394 // Closes the MXF file, writing the index and other closing information.
395 //
396 Result_t
397 AS_02::JP2K::MXFWriter::h__Writer::Finalize()
398 {
399   if ( ! m_State.Test_RUNNING() )
400     return RESULT_STATE;
401
402   Result_t result = m_State.Goto_FINAL();
403
404   if ( ASDCP_SUCCESS(result) )
405     result = WriteAS02Footer();
406
407   return result;
408 }
409
410
411 //------------------------------------------------------------------------------------------
412
413
414
415 AS_02::JP2K::MXFWriter::MXFWriter()
416 {
417 }
418
419 AS_02::JP2K::MXFWriter::~MXFWriter()
420 {
421 }
422
423 // Warning: direct manipulation of MXF structures can interfere
424 // with the normal operation of the wrapper.  Caveat emptor!
425 //
426 ASDCP::MXF::OP1aHeader&
427 AS_02::JP2K::MXFWriter::OP1aHeader()
428 {
429   if ( m_Writer.empty() )
430     {
431       assert(g_OP1aHeader);
432       return *g_OP1aHeader;
433     }
434
435   return m_Writer->m_HeaderPart;
436 }
437
438 // Warning: direct manipulation of MXF structures can interfere
439 // with the normal operation of the wrapper.  Caveat emptor!
440 //
441 ASDCP::MXF::RIP&
442 AS_02::JP2K::MXFWriter::RIP()
443 {
444   if ( m_Writer.empty() )
445     {
446       assert(g_RIP);
447       return *g_RIP;
448     }
449
450   return m_Writer->m_RIP;
451 }
452
453 // Open the file for writing. The file must not exist. Returns error if
454 // the operation cannot be completed.
455 Result_t
456 AS_02::JP2K::MXFWriter::OpenWrite(const char* filename, const ASDCP::WriterInfo& Info,
457                                   const ASDCP::JP2K::PictureDescriptor& PDesc,
458                                   const IndexStrategy_t& Strategy,
459                                   const ui32_t& PartitionSpace,
460                                   const ui32_t& HeaderSize)
461 {
462   m_Writer = new AS_02::JP2K::MXFWriter::h__Writer(DefaultSMPTEDict());
463   m_Writer->m_Info = Info;
464
465   Result_t result = m_Writer->OpenWrite(filename, ASDCP::ESS_JPEG_2000, Strategy, PartitionSpace, HeaderSize);
466
467   if ( ASDCP_SUCCESS(result) )
468     result = m_Writer->SetSourceStream(PDesc, JP2K_PACKAGE_LABEL);
469
470   if ( ASDCP_FAILURE(result) )
471     m_Writer.release();
472
473   return result;
474 }
475
476 // Writes a frame of essence to the MXF file. If the optional AESEncContext
477 // argument is present, the essence is encrypted prior to writing.
478 // Fails if the file is not open, is finalized, or an operating system
479 // error occurs.
480 Result_t 
481 AS_02::JP2K::MXFWriter::WriteFrame(const ASDCP::JP2K::FrameBuffer& FrameBuf, AESEncContext* Ctx, HMACContext* HMAC)
482 {
483   if ( m_Writer.empty() )
484     return RESULT_INIT;
485
486   return m_Writer->WriteFrame(FrameBuf, Ctx, HMAC);
487 }
488
489 // Closes the MXF file, writing the index and other closing information.
490 Result_t
491 AS_02::JP2K::MXFWriter::Finalize()
492 {
493   if ( m_Writer.empty() )
494     return RESULT_INIT;
495
496   return m_Writer->Finalize();
497 }
498
499
500 //
501 // end AS_02_JP2K.cpp
502 //