Fix Object ref not being written: this prevented GenericStreamTextBasedSet to be...
[asdcplib.git] / src / AS_02_JP2K.cpp
1 /*
2 Copyright (c) 2011-2018, 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   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);
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 void
228 AS_02::JP2K::MXFReader::DumpHeaderMetadata(FILE* stream) const
229 {
230   if ( m_Reader && m_Reader->m_File.IsOpen() )
231     {
232       m_Reader->m_HeaderPart.Dump(stream);
233     }
234 }
235
236
237 //
238 void
239 AS_02::JP2K::MXFReader::DumpIndex(FILE* stream) const
240 {
241   if ( m_Reader && m_Reader->m_File.IsOpen() )
242     {
243       m_Reader->m_IndexAccess.Dump(stream);
244     }
245 }
246
247 //------------------------------------------------------------------------------------------
248
249 //
250 class AS_02::JP2K::MXFWriter::h__Writer : public AS_02::h__AS02WriterFrame
251 {
252   ASDCP_NO_COPY_CONSTRUCT(h__Writer);
253   h__Writer();
254
255   JPEG2000PictureSubDescriptor* m_EssenceSubDescriptor;
256
257 public:
258   byte_t            m_EssenceUL[SMPTE_UL_LENGTH];
259
260   h__Writer(const Dictionary& d) : h__AS02WriterFrame(d), m_EssenceSubDescriptor(0) {
261     memset(m_EssenceUL, 0, SMPTE_UL_LENGTH);
262   }
263
264   virtual ~h__Writer(){}
265
266   Result_t OpenWrite(const std::string&, ASDCP::MXF::FileDescriptor* essence_descriptor,
267                      ASDCP::MXF::InterchangeObject_list_t& essence_sub_descriptor_list,
268                      const AS_02::IndexStrategy_t& IndexStrategy,
269                      const ui32_t& PartitionSpace, const ui32_t& HeaderSize);
270   Result_t SetSourceStream(const std::string& label, const ASDCP::Rational& edit_rate);
271   Result_t WriteFrame(const ASDCP::JP2K::FrameBuffer&, ASDCP::AESEncContext*, ASDCP::HMACContext*);
272   Result_t Finalize();
273 };
274
275
276 // Open the file for writing. The file must not exist. Returns error if
277 // the operation cannot be completed.
278 Result_t
279 AS_02::JP2K::MXFWriter::h__Writer::OpenWrite(const std::string& filename,
280                                              ASDCP::MXF::FileDescriptor* essence_descriptor,
281                                              ASDCP::MXF::InterchangeObject_list_t& essence_sub_descriptor_list,
282                                              const AS_02::IndexStrategy_t& IndexStrategy,
283                                              const ui32_t& PartitionSpace_sec, const ui32_t& HeaderSize)
284 {
285   if ( ! m_State.Test_BEGIN() )
286     {
287       KM_RESULT_STATE_HERE();
288         return RESULT_STATE;
289     }
290
291   if ( m_IndexStrategy != AS_02::IS_FOLLOW )
292     {
293       DefaultLogSink().Error("Only strategy IS_FOLLOW is supported at this time.\n");
294       return Kumu::RESULT_NOTIMPL;
295     }
296
297   Result_t result = m_File.OpenWrite(filename.c_str());
298
299   if ( KM_SUCCESS(result) )
300     {
301       m_IndexStrategy = IndexStrategy;
302       m_PartitionSpace = PartitionSpace_sec; // later converted to edit units by SetSourceStream()
303       m_HeaderSize = HeaderSize;
304
305       if ( essence_descriptor->GetUL() != UL(m_Dict->ul(MDD_RGBAEssenceDescriptor))
306            && essence_descriptor->GetUL() != UL(m_Dict->ul(MDD_CDCIEssenceDescriptor)) )
307         {
308           DefaultLogSink().Error("Essence descriptor is not a RGBAEssenceDescriptor or CDCIEssenceDescriptor.\n");
309           essence_descriptor->Dump();
310           return RESULT_AS02_FORMAT;
311         }
312
313       m_EssenceDescriptor = essence_descriptor;
314
315       ASDCP::MXF::InterchangeObject_list_t::iterator i;
316       for ( i = essence_sub_descriptor_list.begin(); i != essence_sub_descriptor_list.end(); ++i )
317         {
318           if ( (*i)->GetUL() != UL(m_Dict->ul(MDD_JPEG2000PictureSubDescriptor)) )
319             {
320               DefaultLogSink().Error("Essence sub-descriptor is not a JPEG2000PictureSubDescriptor.\n");
321               (*i)->Dump();
322             }
323
324           m_EssenceSubDescriptorList.push_back(*i);
325           GenRandomValue((*i)->InstanceUID);
326           m_EssenceDescriptor->SubDescriptors.push_back((*i)->InstanceUID);
327           *i = 0; // parent will only free the ones we don't keep
328         }
329
330       result = m_State.Goto_INIT();
331     }
332
333   return result;
334 }
335
336 // Automatically sets the MXF file's metadata from the first jpeg codestream stream.
337 Result_t
338 AS_02::JP2K::MXFWriter::h__Writer::SetSourceStream(const std::string& label, const ASDCP::Rational& edit_rate)
339 {
340   assert(m_Dict);
341   if ( ! m_State.Test_INIT() )
342     {
343       KM_RESULT_STATE_HERE();
344         return RESULT_STATE;
345     }
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_t result = m_State.Goto_READY();
350
351   if ( KM_SUCCESS(result) )
352     {
353       UL wrapping_label = UL(m_Dict->ul(MDD_MXFGCP1FrameWrappedPictureElement));
354
355       CDCIEssenceDescriptor *cdci_descriptor = dynamic_cast<CDCIEssenceDescriptor*>(m_EssenceDescriptor);
356       if ( cdci_descriptor )
357         {
358           if ( cdci_descriptor->FrameLayout ) // 0 == progressive, 1 == interlace
359             {
360               wrapping_label = UL(m_Dict->ul(MDD_MXFGCI1FrameWrappedPictureElement));
361             }
362         }
363
364       result = WriteAS02Header(label, wrapping_label,
365                                PICT_DEF_LABEL, UL(m_EssenceUL), UL(m_Dict->ul(MDD_PictureDataDef)),
366                                edit_rate, derive_timecode_rate_from_edit_rate(edit_rate));
367
368       if ( KM_SUCCESS(result) )
369         {
370           this->m_IndexWriter.SetPrimerLookup(&this->m_HeaderPart.m_Primer);
371           this->m_IndexWriter.SetEditRate(m_EssenceDescriptor->SampleRate);
372         }
373     }
374
375   return result;
376 }
377
378 // Writes a frame of essence to the MXF file. If the optional AESEncContext
379 // argument is present, the essence is encrypted prior to writing.
380 // Fails if the file is not open, is finalized, or an operating system
381 // error occurs.
382 //
383 Result_t
384 AS_02::JP2K::MXFWriter::h__Writer::WriteFrame(const ASDCP::JP2K::FrameBuffer& FrameBuf,
385                                               AESEncContext* Ctx, HMACContext* HMAC)
386 {
387   if ( FrameBuf.Size() == 0 )
388     {
389       DefaultLogSink().Error("The frame buffer size is zero.\n");
390       return RESULT_PARAM;
391     }
392
393   Result_t result = RESULT_OK;
394
395   if ( m_State.Test_READY() )
396     {
397       result = m_State.Goto_RUNNING(); // first time through
398     }
399
400   if ( KM_SUCCESS(result) )
401     {
402       result = WriteEKLVPacket(FrameBuf, m_EssenceUL, MXF_BER_LENGTH, Ctx, HMAC);
403       m_FramesWritten++;
404     }
405
406   return result;
407 }
408
409 // Closes the MXF file, writing the index and other closing information.
410 //
411 Result_t
412 AS_02::JP2K::MXFWriter::h__Writer::Finalize()
413 {
414   if ( ! m_State.Test_RUNNING() )
415     {
416       KM_RESULT_STATE_HERE();
417         return RESULT_STATE;
418     }
419
420   Result_t result = m_State.Goto_FINAL();
421
422   if ( KM_SUCCESS(result) )
423     {
424       result = WriteAS02Footer();
425     }
426
427   return result;
428 }
429
430
431 //------------------------------------------------------------------------------------------
432
433
434
435 AS_02::JP2K::MXFWriter::MXFWriter()
436 {
437 }
438
439 AS_02::JP2K::MXFWriter::~MXFWriter()
440 {
441 }
442
443 // Warning: direct manipulation of MXF structures can interfere
444 // with the normal operation of the wrapper.  Caveat emptor!
445 //
446 ASDCP::MXF::OP1aHeader&
447 AS_02::JP2K::MXFWriter::OP1aHeader()
448 {
449   if ( m_Writer.empty() )
450     {
451       assert(g_OP1aHeader);
452       return *g_OP1aHeader;
453     }
454
455   return m_Writer->m_HeaderPart;
456 }
457
458 // Warning: direct manipulation of MXF structures can interfere
459 // with the normal operation of the wrapper.  Caveat emptor!
460 //
461 ASDCP::MXF::RIP&
462 AS_02::JP2K::MXFWriter::RIP()
463 {
464   if ( m_Writer.empty() )
465     {
466       assert(g_RIP);
467       return *g_RIP;
468     }
469
470   return m_Writer->m_RIP;
471 }
472
473 // Open the file for writing. The file must not exist. Returns error if
474 // the operation cannot be completed.
475 Result_t
476 AS_02::JP2K::MXFWriter::OpenWrite(const std::string& filename, const ASDCP::WriterInfo& Info,
477                                   ASDCP::MXF::FileDescriptor* essence_descriptor,
478                                   ASDCP::MXF::InterchangeObject_list_t& essence_sub_descriptor_list,
479                                   const ASDCP::Rational& edit_rate, const ui32_t& header_size,
480                                   const IndexStrategy_t& strategy, const ui32_t& partition_space)
481 {
482   if ( essence_descriptor == 0 )
483     {
484       DefaultLogSink().Error("Essence descriptor object required.\n");
485       return RESULT_PARAM;
486     }
487
488   m_Writer = new AS_02::JP2K::MXFWriter::h__Writer(DefaultSMPTEDict());
489   m_Writer->m_Info = Info;
490
491   Result_t result = m_Writer->OpenWrite(filename, essence_descriptor, essence_sub_descriptor_list,
492                                         strategy, partition_space, header_size);
493
494   if ( KM_SUCCESS(result) )
495     result = m_Writer->SetSourceStream(JP2K_PACKAGE_LABEL, edit_rate);
496
497   if ( KM_FAILURE(result) )
498     m_Writer.release();
499
500   return result;
501 }
502
503 // Writes a frame of essence to the MXF file. If the optional AESEncContext
504 // argument is present, the essence is encrypted prior to writing.
505 // Fails if the file is not open, is finalized, or an operating system
506 // error occurs.
507 Result_t 
508 AS_02::JP2K::MXFWriter::WriteFrame(const ASDCP::JP2K::FrameBuffer& FrameBuf, AESEncContext* Ctx, HMACContext* HMAC)
509 {
510   if ( m_Writer.empty() )
511     return RESULT_INIT;
512
513   return m_Writer->WriteFrame(FrameBuf, Ctx, HMAC);
514 }
515
516 // Closes the MXF file, writing the index and other closing information.
517 Result_t
518 AS_02::JP2K::MXFWriter::Finalize()
519 {
520   if ( m_Writer.empty() )
521     return RESULT_INIT;
522
523   return m_Writer->Finalize();
524 }
525
526
527 //
528 // end AS_02_JP2K.cpp
529 //