a8eff1a4e91060e5bfefb0d4326fd386be49a159
[asdcplib.git] / src / AS_02_JP2K.cpp
1 /*
2 Copyright (c) 2011-2013, Robert Scheler, Heiko Sparenberg Fraunhofer IIS,
3 John Hurst
4
5 All rights reserved.
6
7 Redistribution and use in source and binary forms, with or without
8 modification, are permitted provided that the following conditions
9 are met:
10 1. Redistributions of source code must retain the above copyright
11    notice, this list of conditions and the following disclaimer.
12 2. Redistributions in binary form must reproduce the above copyright
13    notice, this list of conditions and the following disclaimer in the
14    documentation and/or other materials provided with the distribution.
15 3. The name of the author may not be used to endorse or promote products
16    derived from this software without specific prior written permission.
17
18 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20 OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21 IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23 NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27 THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */ 
29 /*! \file    AS_02_JP2K.cpp
30   \version $Id$
31   \brief   AS-02 library, JPEG 2000 essence reader and writer implementation
32 */
33
34 #include "AS_02_internal.h"
35
36 #include <iostream>
37 #include <iomanip>
38
39 using namespace ASDCP;
40 using namespace ASDCP::JP2K;
41 using Kumu::GenRandomValue;
42
43 //------------------------------------------------------------------------------------------
44
45 static std::string JP2K_PACKAGE_LABEL = "File Package: SMPTE ST 422 / ST 2067-5 frame wrapping of JPEG 2000 codestreams";
46 static std::string PICT_DEF_LABEL = "Image Track";
47
48 //------------------------------------------------------------------------------------------
49 //
50 // hidden, internal implementation of JPEG 2000 reader
51
52
53 class AS_02::JP2K::MXFReader::h__Reader : public AS_02::h__AS02Reader
54 {
55   ASDCP_NO_COPY_CONSTRUCT(h__Reader);
56
57 public:
58   PictureDescriptor m_PDesc;        // codestream parameter list
59
60   h__Reader(const Dictionary& d) :
61     AS_02::h__AS02Reader(d) {}
62
63   virtual ~h__Reader() {}
64
65   Result_t    OpenRead(const std::string&);
66   Result_t    ReadFrame(ui32_t, ASDCP::JP2K::FrameBuffer&, AESDecContext*, HMACContext*);
67 };
68
69 //
70 Result_t
71 AS_02::JP2K::MXFReader::h__Reader::OpenRead(const std::string& filename)
72 {
73   Result_t result = OpenMXFRead(filename.c_str());
74
75   if( KM_SUCCESS(result) )
76     {
77       InterchangeObject* tmp_iobj = 0;
78
79       m_HeaderPart.GetMDObjectByType(OBJ_TYPE_ARGS(CDCIEssenceDescriptor), &tmp_iobj);
80
81       if ( tmp_iobj == 0 )
82         {
83           m_HeaderPart.GetMDObjectByType(OBJ_TYPE_ARGS(RGBAEssenceDescriptor), &tmp_iobj);
84         }
85
86       if ( tmp_iobj == 0 )
87         {
88           DefaultLogSink().Error("RGBAEssenceDescriptor nor CDCIEssenceDescriptor found.\n");
89         }
90
91       m_HeaderPart.GetMDObjectByType(OBJ_TYPE_ARGS(JPEG2000PictureSubDescriptor), &tmp_iobj);
92
93       if ( tmp_iobj == 0 )
94         {
95           DefaultLogSink().Error("JPEG2000PictureSubDescriptor not found.\n");
96         }
97
98       std::list<InterchangeObject*> ObjectList;
99       m_HeaderPart.GetMDObjectsByType(OBJ_TYPE_ARGS(Track), ObjectList);
100
101       if ( ObjectList.empty() )
102         {
103           DefaultLogSink().Error("MXF Metadata contains no Track Sets.\n");
104           return RESULT_AS02_FORMAT;
105         }
106     }
107
108   return result;
109 }
110
111 //
112 //
113 Result_t
114 AS_02::JP2K::MXFReader::h__Reader::ReadFrame(ui32_t FrameNum, ASDCP::JP2K::FrameBuffer& FrameBuf,
115                       ASDCP::AESDecContext* Ctx, ASDCP::HMACContext* HMAC)
116 {
117   if ( ! m_File.IsOpen() )
118     return RESULT_INIT;
119
120   assert(m_Dict);
121   return ReadEKLVFrame(FrameNum, FrameBuf, m_Dict->ul(MDD_JPEG2000Essence), Ctx, HMAC);
122 }
123
124 //------------------------------------------------------------------------------------------
125 //
126
127 AS_02::JP2K::MXFReader::MXFReader()
128 {
129   m_Reader = new h__Reader(DefaultCompositeDict());
130 }
131
132
133 AS_02::JP2K::MXFReader::~MXFReader()
134 {
135 }
136
137 // Warning: direct manipulation of MXF structures can interfere
138 // with the normal operation of the wrapper.  Caveat emptor!
139 //
140 ASDCP::MXF::OP1aHeader&
141 AS_02::JP2K::MXFReader::OP1aHeader()
142 {
143   if ( m_Reader.empty() )
144     {
145       assert(g_OP1aHeader);
146       return *g_OP1aHeader;
147     }
148
149   return m_Reader->m_HeaderPart;
150 }
151
152 // Warning: direct manipulation of MXF structures can interfere
153 // with the normal operation of the wrapper.  Caveat emptor!
154 //
155 AS_02::MXF::AS02IndexReader&
156 AS_02::JP2K::MXFReader::AS02IndexReader()
157 {
158   if ( m_Reader.empty() )
159     {
160       assert(g_AS02IndexReader);
161       return *g_AS02IndexReader;
162     }
163
164   return m_Reader->m_IndexAccess;
165 }
166
167 // Warning: direct manipulation of MXF structures can interfere
168 // with the normal operation of the wrapper.  Caveat emptor!
169 //
170 ASDCP::MXF::RIP&
171 AS_02::JP2K::MXFReader::RIP()
172 {
173   if ( m_Reader.empty() )
174     {
175       assert(g_RIP);
176       return *g_RIP;
177     }
178
179   return m_Reader->m_RIP;
180 }
181
182 // Open the file for reading. The file must exist. Returns error if the
183 // operation cannot be completed.
184 Result_t
185 AS_02::JP2K::MXFReader::OpenRead(const std::string& filename) const
186 {
187   return m_Reader->OpenRead(filename);
188 }
189
190 //
191 Result_t
192 AS_02::JP2K::MXFReader::Close() const
193 {
194   if ( m_Reader && m_Reader->m_File.IsOpen() )
195     {
196       m_Reader->Close();
197       return RESULT_OK;
198     }
199
200   return RESULT_INIT;
201 }
202
203 //
204 Result_t
205 AS_02::JP2K::MXFReader::ReadFrame(ui32_t FrameNum, ASDCP::JP2K::FrameBuffer& FrameBuf,
206                                            ASDCP::AESDecContext* Ctx, ASDCP::HMACContext* HMAC) const
207 {
208   if ( m_Reader && m_Reader->m_File.IsOpen() )
209     return m_Reader->ReadFrame(FrameNum, FrameBuf, Ctx, HMAC);
210
211   return RESULT_INIT;
212 }
213
214 // Fill the struct with the values from the file's header.
215 // Returns RESULT_INIT if the file is not open.
216 Result_t
217 AS_02::JP2K::MXFReader::FillWriterInfo(WriterInfo& Info) const
218 {
219   if ( m_Reader && m_Reader->m_File.IsOpen() )
220     {
221       Info = m_Reader->m_Info;
222       return RESULT_OK;
223     }
224
225   return RESULT_INIT;
226 }
227
228
229 //------------------------------------------------------------------------------------------
230
231 //
232 class AS_02::JP2K::MXFWriter::h__Writer : public AS_02::h__AS02Writer
233 {
234   ASDCP_NO_COPY_CONSTRUCT(h__Writer);
235   h__Writer();
236
237   JPEG2000PictureSubDescriptor* m_EssenceSubDescriptor;
238
239 public:
240   PictureDescriptor m_PDesc;
241   byte_t            m_EssenceUL[SMPTE_UL_LENGTH];
242
243   h__Writer(const Dictionary& d) : h__AS02Writer(d), m_EssenceSubDescriptor(0) {
244     memset(m_EssenceUL, 0, SMPTE_UL_LENGTH);
245   }
246
247   virtual ~h__Writer(){}
248
249   Result_t OpenWrite(const std::string&, ASDCP::MXF::FileDescriptor* essence_descriptor,
250                      ASDCP::MXF::InterchangeObject_list_t& essence_sub_descriptor_list,
251                      const AS_02::IndexStrategy_t& IndexStrategy,
252                      const ui32_t& PartitionSpace, const ui32_t& HeaderSize);
253   Result_t SetSourceStream(const std::string& label, const ASDCP::Rational& edit_rate);
254   Result_t WriteFrame(const ASDCP::JP2K::FrameBuffer&, ASDCP::AESEncContext*, ASDCP::HMACContext*);
255   Result_t Finalize();
256 };
257
258
259 // Open the file for writing. The file must not exist. Returns error if
260 // the operation cannot be completed.
261 Result_t
262 AS_02::JP2K::MXFWriter::h__Writer::OpenWrite(const std::string& filename,
263                                              ASDCP::MXF::FileDescriptor* essence_descriptor,
264                                              ASDCP::MXF::InterchangeObject_list_t& essence_sub_descriptor_list,
265                                              const AS_02::IndexStrategy_t& IndexStrategy,
266                                              const ui32_t& PartitionSpace_sec, const ui32_t& HeaderSize)
267 {
268   if ( ! m_State.Test_BEGIN() )
269     {
270       return RESULT_STATE;
271     }
272
273   if ( m_IndexStrategy != AS_02::IS_FOLLOW )
274     {
275       DefaultLogSink().Error("Only strategy IS_FOLLOW is supported at this time.\n");
276       return Kumu::RESULT_NOTIMPL;
277     }
278
279   Result_t result = m_File.OpenWrite(filename.c_str());
280
281   if ( KM_SUCCESS(result) )
282     {
283       m_IndexStrategy = IndexStrategy;
284       m_PartitionSpace = PartitionSpace_sec; // later converted to edit units by SetSourceStream()
285       m_HeaderSize = HeaderSize;
286
287       if ( essence_descriptor->GetUL() != UL(m_Dict->ul(MDD_RGBAEssenceDescriptor))
288            && essence_descriptor->GetUL() != UL(m_Dict->ul(MDD_CDCIEssenceDescriptor)) )
289         {
290           DefaultLogSink().Error("Essence descriptor is not a RGBAEssenceDescriptor or CDCIEssenceDescriptor.\n");
291           essence_descriptor->Dump();
292           return RESULT_AS02_FORMAT;
293         }
294
295       m_EssenceDescriptor = essence_descriptor;
296
297       ASDCP::MXF::InterchangeObject_list_t::iterator i;
298       for ( i = essence_sub_descriptor_list.begin(); i != essence_sub_descriptor_list.end(); ++i )
299         {
300           if ( (*i)->GetUL() != UL(m_Dict->ul(MDD_JPEG2000PictureSubDescriptor)) )
301             {
302               DefaultLogSink().Error("Essence sub-descriptor is not a JPEG2000PictureSubDescriptor.\n");
303               (*i)->Dump();
304             }
305
306           m_EssenceSubDescriptorList.push_back(*i);
307           GenRandomValue((*i)->InstanceUID);
308           m_EssenceDescriptor->SubDescriptors.push_back((*i)->InstanceUID);
309           *i = 0; // parent will only free the ones we don't keep
310         }
311
312       result = m_State.Goto_INIT();
313     }
314
315   return result;
316 }
317
318 // Automatically sets the MXF file's metadata from the first jpeg codestream stream.
319 Result_t
320 AS_02::JP2K::MXFWriter::h__Writer::SetSourceStream(const std::string& label, const ASDCP::Rational& edit_rate)
321 {
322   assert(m_Dict);
323   if ( ! m_State.Test_INIT() )
324     {
325       return RESULT_STATE;
326     }
327
328   memcpy(m_EssenceUL, m_Dict->ul(MDD_JPEG2000Essence), SMPTE_UL_LENGTH);
329   m_EssenceUL[SMPTE_UL_LENGTH-1] = 1; // first (and only) essence container
330   Result_t result = m_State.Goto_READY();
331
332   if ( KM_SUCCESS(result) )
333     {
334       result = WriteAS02Header(label, UL(m_Dict->ul(MDD_JPEG_2000WrappingFrame)),
335                                PICT_DEF_LABEL, UL(m_EssenceUL), UL(m_Dict->ul(MDD_PictureDataDef)),
336                                edit_rate, derive_timecode_rate_from_edit_rate(edit_rate));
337     }
338
339   return result;
340 }
341
342 // Writes a frame of essence to the MXF file. If the optional AESEncContext
343 // argument is present, the essence is encrypted prior to writing.
344 // Fails if the file is not open, is finalized, or an operating system
345 // error occurs.
346 //
347 Result_t
348 AS_02::JP2K::MXFWriter::h__Writer::WriteFrame(const ASDCP::JP2K::FrameBuffer& FrameBuf,
349                                               AESEncContext* Ctx, HMACContext* HMAC)
350 {
351   if ( FrameBuf.Size() == 0 )
352     {
353       DefaultLogSink().Error("The frame buffer size is zero.\n");
354       return RESULT_PARAM;
355     }
356
357   Result_t result = RESULT_OK;
358
359   if ( m_State.Test_READY() )
360     {
361       result = m_State.Goto_RUNNING(); // first time through
362     }
363
364   if ( KM_SUCCESS(result) )
365     {
366       result = WriteEKLVPacket(FrameBuf, m_EssenceUL, Ctx, HMAC);
367       m_FramesWritten++;
368     }
369
370   return result;
371 }
372
373 // Closes the MXF file, writing the index and other closing information.
374 //
375 Result_t
376 AS_02::JP2K::MXFWriter::h__Writer::Finalize()
377 {
378   if ( ! m_State.Test_RUNNING() )
379     return RESULT_STATE;
380
381   Result_t result = m_State.Goto_FINAL();
382
383   if ( KM_SUCCESS(result) )
384     {
385       result = WriteAS02Footer();
386     }
387
388   return result;
389 }
390
391
392 //------------------------------------------------------------------------------------------
393
394
395
396 AS_02::JP2K::MXFWriter::MXFWriter()
397 {
398 }
399
400 AS_02::JP2K::MXFWriter::~MXFWriter()
401 {
402 }
403
404 // Warning: direct manipulation of MXF structures can interfere
405 // with the normal operation of the wrapper.  Caveat emptor!
406 //
407 ASDCP::MXF::OP1aHeader&
408 AS_02::JP2K::MXFWriter::OP1aHeader()
409 {
410   if ( m_Writer.empty() )
411     {
412       assert(g_OP1aHeader);
413       return *g_OP1aHeader;
414     }
415
416   return m_Writer->m_HeaderPart;
417 }
418
419 // Warning: direct manipulation of MXF structures can interfere
420 // with the normal operation of the wrapper.  Caveat emptor!
421 //
422 ASDCP::MXF::RIP&
423 AS_02::JP2K::MXFWriter::RIP()
424 {
425   if ( m_Writer.empty() )
426     {
427       assert(g_RIP);
428       return *g_RIP;
429     }
430
431   return m_Writer->m_RIP;
432 }
433
434 // Open the file for writing. The file must not exist. Returns error if
435 // the operation cannot be completed.
436 Result_t
437 AS_02::JP2K::MXFWriter::OpenWrite(const std::string& filename, const ASDCP::WriterInfo& Info,
438                                   ASDCP::MXF::FileDescriptor* essence_descriptor,
439                                   ASDCP::MXF::InterchangeObject_list_t& essence_sub_descriptor_list,
440                                   const ASDCP::Rational& edit_rate, const ui32_t& header_size,
441                                   const IndexStrategy_t& strategy, const ui32_t& partition_space)
442 {
443   if ( essence_descriptor == 0 )
444     {
445       DefaultLogSink().Error("Essence descriptor object required.\n");
446       return RESULT_PARAM;
447     }
448
449   m_Writer = new AS_02::JP2K::MXFWriter::h__Writer(DefaultSMPTEDict());
450   m_Writer->m_Info = Info;
451
452   Result_t result = m_Writer->OpenWrite(filename, essence_descriptor, essence_sub_descriptor_list,
453                                         strategy, partition_space, header_size);
454
455   if ( KM_SUCCESS(result) )
456     result = m_Writer->SetSourceStream(JP2K_PACKAGE_LABEL, edit_rate);
457
458   if ( KM_FAILURE(result) )
459     m_Writer.release();
460
461   return result;
462 }
463
464 // Writes a frame of essence to the MXF file. If the optional AESEncContext
465 // argument is present, the essence is encrypted prior to writing.
466 // Fails if the file is not open, is finalized, or an operating system
467 // error occurs.
468 Result_t 
469 AS_02::JP2K::MXFWriter::WriteFrame(const ASDCP::JP2K::FrameBuffer& FrameBuf, AESEncContext* Ctx, HMACContext* HMAC)
470 {
471   if ( m_Writer.empty() )
472     return RESULT_INIT;
473
474   return m_Writer->WriteFrame(FrameBuf, Ctx, HMAC);
475 }
476
477 // Closes the MXF file, writing the index and other closing information.
478 Result_t
479 AS_02::JP2K::MXFWriter::Finalize()
480 {
481   if ( m_Writer.empty() )
482     return RESULT_INIT;
483
484   return m_Writer->Finalize();
485 }
486
487
488 //
489 // end AS_02_JP2K.cpp
490 //