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