2.5.11.
[asdcplib-cth.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: AS_02_JP2K.cpp,v 1.14 2014/09/21 13:27:43 jhurst Exp $
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   h__Reader(const Dictionary& d) :
59     AS_02::h__AS02Reader(d) {}
60
61   virtual ~h__Reader() {}
62
63   Result_t    OpenRead(const std::string&);
64   Result_t    ReadFrame(ui32_t, ASDCP::JP2K::FrameBuffer&, AESDecContext*, HMACContext*);
65 };
66
67 //
68 Result_t
69 AS_02::JP2K::MXFReader::h__Reader::OpenRead(const std::string& filename)
70 {
71   Result_t result = OpenMXFRead(filename.c_str());
72
73   if( KM_SUCCESS(result) )
74     {
75       InterchangeObject* tmp_iobj = 0;
76
77       m_HeaderPart.GetMDObjectByType(OBJ_TYPE_ARGS(CDCIEssenceDescriptor), &tmp_iobj);
78
79       if ( tmp_iobj == 0 )
80         {
81           m_HeaderPart.GetMDObjectByType(OBJ_TYPE_ARGS(RGBAEssenceDescriptor), &tmp_iobj);
82         }
83
84       if ( tmp_iobj == 0 )
85         {
86           DefaultLogSink().Error("RGBAEssenceDescriptor nor CDCIEssenceDescriptor found.\n");
87         }
88
89       m_HeaderPart.GetMDObjectByType(OBJ_TYPE_ARGS(JPEG2000PictureSubDescriptor), &tmp_iobj);
90
91       if ( tmp_iobj == 0 )
92         {
93           DefaultLogSink().Error("JPEG2000PictureSubDescriptor not found.\n");
94         }
95
96       std::list<InterchangeObject*> ObjectList;
97       m_HeaderPart.GetMDObjectsByType(OBJ_TYPE_ARGS(Track), ObjectList);
98
99       if ( ObjectList.empty() )
100         {
101           DefaultLogSink().Error("MXF Metadata contains no Track Sets.\n");
102           return RESULT_AS02_FORMAT;
103         }
104     }
105
106   return result;
107 }
108
109 //
110 //
111 Result_t
112 AS_02::JP2K::MXFReader::h__Reader::ReadFrame(ui32_t FrameNum, ASDCP::JP2K::FrameBuffer& FrameBuf,
113                       ASDCP::AESDecContext* Ctx, ASDCP::HMACContext* HMAC)
114 {
115   if ( ! m_File.IsOpen() )
116     return RESULT_INIT;
117
118   assert(m_Dict);
119   return ReadEKLVFrame(FrameNum, FrameBuf, m_Dict->ul(MDD_JPEG2000Essence), Ctx, HMAC);
120 }
121
122 //------------------------------------------------------------------------------------------
123 //
124
125 AS_02::JP2K::MXFReader::MXFReader()
126 {
127   m_Reader = new h__Reader(DefaultCompositeDict());
128 }
129
130
131 AS_02::JP2K::MXFReader::~MXFReader()
132 {
133 }
134
135 // Warning: direct manipulation of MXF structures can interfere
136 // with the normal operation of the wrapper.  Caveat emptor!
137 //
138 ASDCP::MXF::OP1aHeader&
139 AS_02::JP2K::MXFReader::OP1aHeader()
140 {
141   if ( m_Reader.empty() )
142     {
143       assert(g_OP1aHeader);
144       return *g_OP1aHeader;
145     }
146
147   return m_Reader->m_HeaderPart;
148 }
149
150 // Warning: direct manipulation of MXF structures can interfere
151 // with the normal operation of the wrapper.  Caveat emptor!
152 //
153 AS_02::MXF::AS02IndexReader&
154 AS_02::JP2K::MXFReader::AS02IndexReader()
155 {
156   if ( m_Reader.empty() )
157     {
158       assert(g_AS02IndexReader);
159       return *g_AS02IndexReader;
160     }
161
162   return m_Reader->m_IndexAccess;
163 }
164
165 // Warning: direct manipulation of MXF structures can interfere
166 // with the normal operation of the wrapper.  Caveat emptor!
167 //
168 ASDCP::MXF::RIP&
169 AS_02::JP2K::MXFReader::RIP()
170 {
171   if ( m_Reader.empty() )
172     {
173       assert(g_RIP);
174       return *g_RIP;
175     }
176
177   return m_Reader->m_RIP;
178 }
179
180 // Open the file for reading. The file must exist. Returns error if the
181 // operation cannot be completed.
182 Result_t
183 AS_02::JP2K::MXFReader::OpenRead(const std::string& filename) const
184 {
185   return m_Reader->OpenRead(filename);
186 }
187
188 //
189 Result_t
190 AS_02::JP2K::MXFReader::Close() const
191 {
192   if ( m_Reader && m_Reader->m_File.IsOpen() )
193     {
194       m_Reader->Close();
195       return RESULT_OK;
196     }
197
198   return RESULT_INIT;
199 }
200
201 //
202 Result_t
203 AS_02::JP2K::MXFReader::ReadFrame(ui32_t FrameNum, ASDCP::JP2K::FrameBuffer& FrameBuf,
204                                            ASDCP::AESDecContext* Ctx, ASDCP::HMACContext* HMAC) const
205 {
206   if ( m_Reader && m_Reader->m_File.IsOpen() )
207     return m_Reader->ReadFrame(FrameNum, FrameBuf, Ctx, HMAC);
208
209   return RESULT_INIT;
210 }
211
212 // Fill the struct with the values from the file's header.
213 // Returns RESULT_INIT if the file is not open.
214 Result_t
215 AS_02::JP2K::MXFReader::FillWriterInfo(WriterInfo& Info) const
216 {
217   if ( m_Reader && m_Reader->m_File.IsOpen() )
218     {
219       Info = m_Reader->m_Info;
220       return RESULT_OK;
221     }
222
223   return RESULT_INIT;
224 }
225
226
227 //------------------------------------------------------------------------------------------
228
229 //
230 class AS_02::JP2K::MXFWriter::h__Writer : public AS_02::h__AS02WriterFrame
231 {
232   ASDCP_NO_COPY_CONSTRUCT(h__Writer);
233   h__Writer();
234
235   JPEG2000PictureSubDescriptor* m_EssenceSubDescriptor;
236
237 public:
238   byte_t            m_EssenceUL[SMPTE_UL_LENGTH];
239
240   h__Writer(const Dictionary& d) : h__AS02WriterFrame(d), m_EssenceSubDescriptor(0) {
241     memset(m_EssenceUL, 0, SMPTE_UL_LENGTH);
242   }
243
244   virtual ~h__Writer(){}
245
246   Result_t OpenWrite(const std::string&, ASDCP::MXF::FileDescriptor* essence_descriptor,
247                      ASDCP::MXF::InterchangeObject_list_t& essence_sub_descriptor_list,
248                      const AS_02::IndexStrategy_t& IndexStrategy,
249                      const ui32_t& PartitionSpace, const ui32_t& HeaderSize);
250   Result_t SetSourceStream(const std::string& label, const ASDCP::Rational& edit_rate);
251   Result_t WriteFrame(const ASDCP::JP2K::FrameBuffer&, ASDCP::AESEncContext*, ASDCP::HMACContext*);
252   Result_t Finalize();
253 };
254
255
256 // Open the file for writing. The file must not exist. Returns error if
257 // the operation cannot be completed.
258 Result_t
259 AS_02::JP2K::MXFWriter::h__Writer::OpenWrite(const std::string& filename,
260                                              ASDCP::MXF::FileDescriptor* essence_descriptor,
261                                              ASDCP::MXF::InterchangeObject_list_t& essence_sub_descriptor_list,
262                                              const AS_02::IndexStrategy_t& IndexStrategy,
263                                              const ui32_t& PartitionSpace_sec, const ui32_t& HeaderSize)
264 {
265   if ( ! m_State.Test_BEGIN() )
266     {
267       KM_RESULT_STATE_HERE();
268         return RESULT_STATE;
269     }
270
271   if ( m_IndexStrategy != AS_02::IS_FOLLOW )
272     {
273       DefaultLogSink().Error("Only strategy IS_FOLLOW is supported at this time.\n");
274       return Kumu::RESULT_NOTIMPL;
275     }
276
277   Result_t result = m_File.OpenWrite(filename.c_str());
278
279   if ( KM_SUCCESS(result) )
280     {
281       m_IndexStrategy = IndexStrategy;
282       m_PartitionSpace = PartitionSpace_sec; // later converted to edit units by SetSourceStream()
283       m_HeaderSize = HeaderSize;
284
285       if ( essence_descriptor->GetUL() != UL(m_Dict->ul(MDD_RGBAEssenceDescriptor))
286            && essence_descriptor->GetUL() != UL(m_Dict->ul(MDD_CDCIEssenceDescriptor)) )
287         {
288           DefaultLogSink().Error("Essence descriptor is not a RGBAEssenceDescriptor or CDCIEssenceDescriptor.\n");
289           essence_descriptor->Dump();
290           return RESULT_AS02_FORMAT;
291         }
292
293       m_EssenceDescriptor = essence_descriptor;
294
295       ASDCP::MXF::InterchangeObject_list_t::iterator i;
296       for ( i = essence_sub_descriptor_list.begin(); i != essence_sub_descriptor_list.end(); ++i )
297         {
298           if ( (*i)->GetUL() != UL(m_Dict->ul(MDD_JPEG2000PictureSubDescriptor)) )
299             {
300               DefaultLogSink().Error("Essence sub-descriptor is not a JPEG2000PictureSubDescriptor.\n");
301               (*i)->Dump();
302             }
303
304           m_EssenceSubDescriptorList.push_back(*i);
305           GenRandomValue((*i)->InstanceUID);
306           m_EssenceDescriptor->SubDescriptors.push_back((*i)->InstanceUID);
307           *i = 0; // parent will only free the ones we don't keep
308         }
309
310       result = m_State.Goto_INIT();
311     }
312
313   return result;
314 }
315
316 // Automatically sets the MXF file's metadata from the first jpeg codestream stream.
317 Result_t
318 AS_02::JP2K::MXFWriter::h__Writer::SetSourceStream(const std::string& label, const ASDCP::Rational& edit_rate)
319 {
320   assert(m_Dict);
321   if ( ! m_State.Test_INIT() )
322     {
323       KM_RESULT_STATE_HERE();
324         return RESULT_STATE;
325     }
326
327   memcpy(m_EssenceUL, m_Dict->ul(MDD_JPEG2000Essence), SMPTE_UL_LENGTH);
328   m_EssenceUL[SMPTE_UL_LENGTH-1] = 1; // first (and only) essence container
329   Result_t result = m_State.Goto_READY();
330
331   if ( KM_SUCCESS(result) )
332     {
333       result = WriteAS02Header(label, UL(m_Dict->ul(MDD_JPEG_2000WrappingFrame)),
334                                PICT_DEF_LABEL, UL(m_EssenceUL), UL(m_Dict->ul(MDD_PictureDataDef)),
335                                edit_rate, derive_timecode_rate_from_edit_rate(edit_rate));
336
337       if ( KM_SUCCESS(result) )
338         {
339           this->m_IndexWriter.SetPrimerLookup(&this->m_HeaderPart.m_Primer);
340         }
341     }
342
343   return result;
344 }
345
346 // Writes a frame of essence to the MXF file. If the optional AESEncContext
347 // argument is present, the essence is encrypted prior to writing.
348 // Fails if the file is not open, is finalized, or an operating system
349 // error occurs.
350 //
351 Result_t
352 AS_02::JP2K::MXFWriter::h__Writer::WriteFrame(const ASDCP::JP2K::FrameBuffer& FrameBuf,
353                                               AESEncContext* Ctx, HMACContext* HMAC)
354 {
355   if ( FrameBuf.Size() == 0 )
356     {
357       DefaultLogSink().Error("The frame buffer size is zero.\n");
358       return RESULT_PARAM;
359     }
360
361   Result_t result = RESULT_OK;
362
363   if ( m_State.Test_READY() )
364     {
365       result = m_State.Goto_RUNNING(); // first time through
366     }
367
368   if ( KM_SUCCESS(result) )
369     {
370       result = WriteEKLVPacket(FrameBuf, m_EssenceUL, Ctx, HMAC);
371       m_FramesWritten++;
372     }
373
374   return result;
375 }
376
377 // Closes the MXF file, writing the index and other closing information.
378 //
379 Result_t
380 AS_02::JP2K::MXFWriter::h__Writer::Finalize()
381 {
382   if ( ! m_State.Test_RUNNING() )
383     {
384       KM_RESULT_STATE_HERE();
385         return RESULT_STATE;
386     }
387
388   Result_t result = m_State.Goto_FINAL();
389
390   if ( KM_SUCCESS(result) )
391     {
392       result = WriteAS02Footer();
393     }
394
395   return result;
396 }
397
398
399 //------------------------------------------------------------------------------------------
400
401
402
403 AS_02::JP2K::MXFWriter::MXFWriter()
404 {
405 }
406
407 AS_02::JP2K::MXFWriter::~MXFWriter()
408 {
409 }
410
411 // Warning: direct manipulation of MXF structures can interfere
412 // with the normal operation of the wrapper.  Caveat emptor!
413 //
414 ASDCP::MXF::OP1aHeader&
415 AS_02::JP2K::MXFWriter::OP1aHeader()
416 {
417   if ( m_Writer.empty() )
418     {
419       assert(g_OP1aHeader);
420       return *g_OP1aHeader;
421     }
422
423   return m_Writer->m_HeaderPart;
424 }
425
426 // Warning: direct manipulation of MXF structures can interfere
427 // with the normal operation of the wrapper.  Caveat emptor!
428 //
429 ASDCP::MXF::RIP&
430 AS_02::JP2K::MXFWriter::RIP()
431 {
432   if ( m_Writer.empty() )
433     {
434       assert(g_RIP);
435       return *g_RIP;
436     }
437
438   return m_Writer->m_RIP;
439 }
440
441 // Open the file for writing. The file must not exist. Returns error if
442 // the operation cannot be completed.
443 Result_t
444 AS_02::JP2K::MXFWriter::OpenWrite(const std::string& filename, const ASDCP::WriterInfo& Info,
445                                   ASDCP::MXF::FileDescriptor* essence_descriptor,
446                                   ASDCP::MXF::InterchangeObject_list_t& essence_sub_descriptor_list,
447                                   const ASDCP::Rational& edit_rate, const ui32_t& header_size,
448                                   const IndexStrategy_t& strategy, const ui32_t& partition_space)
449 {
450   if ( essence_descriptor == 0 )
451     {
452       DefaultLogSink().Error("Essence descriptor object required.\n");
453       return RESULT_PARAM;
454     }
455
456   m_Writer = new AS_02::JP2K::MXFWriter::h__Writer(DefaultSMPTEDict());
457   m_Writer->m_Info = Info;
458
459   Result_t result = m_Writer->OpenWrite(filename, essence_descriptor, essence_sub_descriptor_list,
460                                         strategy, partition_space, header_size);
461
462   if ( KM_SUCCESS(result) )
463     result = m_Writer->SetSourceStream(JP2K_PACKAGE_LABEL, edit_rate);
464
465   if ( KM_FAILURE(result) )
466     m_Writer.release();
467
468   return result;
469 }
470
471 // Writes a frame of essence to the MXF file. If the optional AESEncContext
472 // argument is present, the essence is encrypted prior to writing.
473 // Fails if the file is not open, is finalized, or an operating system
474 // error occurs.
475 Result_t 
476 AS_02::JP2K::MXFWriter::WriteFrame(const ASDCP::JP2K::FrameBuffer& FrameBuf, AESEncContext* Ctx, HMACContext* HMAC)
477 {
478   if ( m_Writer.empty() )
479     return RESULT_INIT;
480
481   return m_Writer->WriteFrame(FrameBuf, Ctx, HMAC);
482 }
483
484 // Closes the MXF file, writing the index and other closing information.
485 Result_t
486 AS_02::JP2K::MXFWriter::Finalize()
487 {
488   if ( m_Writer.empty() )
489     return RESULT_INIT;
490
491   return m_Writer->Finalize();
492 }
493
494
495 //
496 // end AS_02_JP2K.cpp
497 //