2 Copyright (c) 2011-2018, Robert Scheler, Heiko Sparenberg Fraunhofer IIS,
7 Redistribution and use in source and binary forms, with or without
8 modification, are permitted provided that the following conditions
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.
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.
31 \brief AS-02 library, public interface
33 This module implements MXF AS-02 is a set of file access objects that
34 offer simplified access to files conforming to the standards published
35 by the SMPTE Media and Packaging Technology Committee 35PM. The file
36 format, labeled IMF Essence Component (AKA "AS-02" for historical
37 reasons), is described in the following document:
39 o SMPTE 2067-5:2013 IMF Essence Component
41 The following use cases are supported by the module:
43 o Write essence to a plaintext or ciphertext AS-02 file:
47 o Read essence from a plaintext or ciphertext AS-02 file:
51 o Read header metadata from an AS-02 file
53 NOTE: ciphertext support for clip-wrapped PCM is not yet complete.
66 KM_DECLARE_RESULT(AS02_FORMAT, -116, "The file format is not proper OP-1a/AS-02.");
70 // reads distributed index tables and provides a uniform lookup with
71 // translated StreamOffest values (that is, StreamOffest is adjusted
72 // to the actual file position
73 class AS02IndexReader : public ASDCP::MXF::Partition
75 Kumu::ByteString m_IndexSegmentData;
77 ui32_t m_BytesPerEditUnit;
79 Result_t InitFromBuffer(const byte_t* p, ui32_t l, const ui64_t& body_offset, const ui64_t& essence_container_offset);
81 ASDCP_NO_COPY_CONSTRUCT(AS02IndexReader);
85 const ASDCP::Dictionary*& m_Dict;
86 ASDCP::IPrimerLookup *m_Lookup;
88 AS02IndexReader(const ASDCP::Dictionary*&);
89 virtual ~AS02IndexReader();
91 Result_t InitFromFile(const Kumu::FileReader& reader, const ASDCP::MXF::RIP& rip, const bool has_header_essence);
92 ui32_t GetDuration() const;
94 Result_t GetMDObjectByID(const Kumu::UUID&, ASDCP::MXF::InterchangeObject** = 0);
95 Result_t GetMDObjectByType(const byte_t*, ASDCP::MXF::InterchangeObject** = 0);
96 Result_t GetMDObjectsByType(const byte_t* ObjectID, std::list<ASDCP::MXF::InterchangeObject*>& ObjectList);
97 Result_t Lookup(ui32_t frame_num, ASDCP::MXF::IndexTableSegment::IndexEntry&) const;
101 // Returns size in bytes of a single sample of data described by ADesc
102 inline ui32_t CalcSampleSize(const ASDCP::MXF::WaveAudioDescriptor& d)
104 return (d.QuantizationBits / 8) * d.ChannelCount;
107 // Returns number of samples per frame of data described by ADesc
108 inline ui32_t CalcSamplesPerFrame(const ASDCP::MXF::WaveAudioDescriptor& d, const ASDCP::Rational& edit_rate)
110 double tmpd = d.AudioSamplingRate.Quotient() / edit_rate.Quotient();
111 return (ui32_t)ceil(tmpd);
114 // Returns the size in bytes of a frame of data described by ADesc
115 inline ui32_t CalcFrameBufferSize(const ASDCP::MXF::WaveAudioDescriptor& d, const ASDCP::Rational& edit_rate)
117 return CalcSampleSize(d) * CalcSamplesPerFrame(d, edit_rate);
120 // Returns number of frames for data described by ADesc, given a duration in samples and an edit rate
121 inline ui32_t CalcFramesFromDurationInSamples(const ui32_t duration_samples, const ASDCP::MXF::WaveAudioDescriptor& d,
122 const ASDCP::Rational& edit_rate)
124 ui32_t spf = CalcSamplesPerFrame(d, edit_rate);
125 ui32_t frames = duration_samples / spf;
127 if ( duration_samples % spf != 0 )
138 // IMF App 2 edit rates not already exposed in namespace ASDCP
139 const ASDCP::Rational EditRate_29_97 = ASDCP::Rational(30000, 1001);
140 const ASDCP::Rational EditRate_59_94 = ASDCP::Rational(60000, 1001);
142 //---------------------------------------------------------------------------------
143 // Accessors in the MXFReader and MXFWriter classes below return these types to
144 // provide direct access to MXF metadata structures declared in MXF.h and Metadata.h
146 // See ST 2067-5 Sec. x.y.z
161 ASDCP::mem_ptr<h__Writer> m_Writer;
162 ASDCP_NO_COPY_CONSTRUCT(MXFWriter);
166 virtual ~MXFWriter();
168 // Warning: direct manipulation of MXF structures can interfere
169 // with the normal operation of the wrapper. Caveat emptor!
170 virtual ASDCP::MXF::OP1aHeader& OP1aHeader();
171 virtual ASDCP::MXF::RIP& RIP();
173 // Open the file for writing. The file must not exist. Returns error if
174 // the operation cannot be completed or if nonsensical data is discovered
175 // in the essence descriptor.
176 Result_t OpenWrite(const std::string& filename, const ASDCP::WriterInfo&,
177 ASDCP::MXF::FileDescriptor* essence_descriptor,
178 ASDCP::MXF::InterchangeObject_list_t& essence_sub_descriptor_list,
179 const ASDCP::Rational& edit_rate, const ui32_t& header_size = 16384,
180 const IndexStrategy_t& strategy = IS_FOLLOW, const ui32_t& partition_space = 10);
182 // Writes a frame of essence to the MXF file. If the optional AESEncContext
183 // argument is present, the essence is encrypted prior to writing.
184 // Fails if the file is not open, is finalized, or an operating system
186 Result_t WriteFrame(const ASDCP::JP2K::FrameBuffer&, ASDCP::AESEncContext* = 0, ASDCP::HMACContext* = 0);
188 // Closes the MXF file, writing the index and revised header.
196 ASDCP::mem_ptr<h__Reader> m_Reader;
197 ASDCP_NO_COPY_CONSTRUCT(MXFReader);
201 virtual ~MXFReader();
203 // Warning: direct manipulation of MXF structures can interfere
204 // with the normal operation of the wrapper. Caveat emptor!
205 virtual ASDCP::MXF::OP1aHeader& OP1aHeader();
206 virtual AS_02::MXF::AS02IndexReader& AS02IndexReader();
207 virtual ASDCP::MXF::RIP& RIP();
209 // Open the file for reading. The file must exist. Returns error if the
210 // operation cannot be completed.
211 Result_t OpenRead(const std::string& filename) const;
213 // Returns RESULT_INIT if the file is not open.
214 Result_t Close() const;
216 // Fill a WriterInfo struct with the values from the file's header.
217 // Returns RESULT_INIT if the file is not open.
218 Result_t FillWriterInfo(ASDCP::WriterInfo&) const;
220 // Reads a frame of essence from the MXF file. If the optional AESEncContext
221 // argument is present, the essence is decrypted after reading. If the MXF
222 // file is encrypted and the AESDecContext argument is NULL, the frame buffer
223 // will contain the ciphertext frame data. If the HMACContext argument is
224 // not NULL, the HMAC will be calculated (if the file supports it).
225 // Returns RESULT_INIT if the file is not open, failure if the frame number is
226 // out of range, or if optional decrypt or HAMC operations fail.
227 Result_t ReadFrame(ui32_t frame_number, ASDCP::JP2K::FrameBuffer&, ASDCP::AESDecContext* = 0, ASDCP::HMACContext* = 0) const;
229 // Print debugging information to stream
230 void DumpHeaderMetadata(FILE* = 0) const;
231 void DumpIndex(FILE* = 0) const;
237 //---------------------------------------------------------------------------------
241 // see AS_DCP.h for related data types
243 // An AS-02 PCM file is clip-wrapped, but the interface defined below mimics that used
244 // for frame-wrapped essence elsewhere in this library. The concept of frame rate
245 // therefore is only relevant to these classes and is not reflected in or affected by
246 // the contents of the MXF file. The frame rate that is set on the writer is only
247 // for compatibility with the existing parsers, samples are packed contiguously into
248 // the same clip-wrapped packet. Similarly, the edit rate must be set when initializing
249 // the reader to signal the number of samples to be read by each call to ReadFrame();
255 ASDCP::mem_ptr<h__Writer> m_Writer;
256 ASDCP_NO_COPY_CONSTRUCT(MXFWriter);
260 virtual ~MXFWriter();
262 // Warning: direct manipulation of MXF structures can interfere
263 // with the normal operation of the wrapper. Caveat emptor!
264 virtual ASDCP::MXF::OP1aHeader& OP1aHeader();
265 virtual ASDCP::MXF::RIP& RIP();
267 // Open the file for writing. The file must not exist. Returns error if
268 // the operation cannot be completed or if nonsensical data is discovered
269 // in the essence descriptor.
270 Result_t OpenWrite(const std::string& filename, const ASDCP::WriterInfo&,
271 ASDCP::MXF::FileDescriptor* essence_descriptor,
272 ASDCP::MXF::InterchangeObject_list_t& essence_sub_descriptor_list,
273 const ASDCP::Rational& edit_rate, ui32_t HeaderSize = 16384);
275 // Writes a frame of essence to the MXF file. If the optional AESEncContext
276 // argument is present, the essence is encrypted prior to writing.
277 // Fails if the file is not open, is finalized, or an operating system
279 Result_t WriteFrame(const ASDCP::FrameBuffer&, ASDCP::AESEncContext* = 0, ASDCP::HMACContext* = 0);
281 // Closes the MXF file, writing the index and revised header.
289 ASDCP::mem_ptr<h__Reader> m_Reader;
290 ASDCP_NO_COPY_CONSTRUCT(MXFReader);
294 virtual ~MXFReader();
296 // Warning: direct manipulation of MXF structures can interfere
297 // with the normal operation of the wrapper. Caveat emptor!
298 virtual ASDCP::MXF::OP1aHeader& OP1aHeader();
299 virtual AS_02::MXF::AS02IndexReader& AS02IndexReader();
300 virtual ASDCP::MXF::RIP& RIP();
302 // Open the file for reading. The file must exist. Returns error if the
303 // operation cannot be completed.
304 Result_t OpenRead(const std::string& filename, const ASDCP::Rational& EditRate) const;
306 // Returns RESULT_INIT if the file is not open.
307 Result_t Close() const;
309 // Fill a WriterInfo struct with the values from the file's header.
310 // Returns RESULT_INIT if the file is not open.
311 Result_t FillWriterInfo(ASDCP::WriterInfo&) const;
313 // Reads a frame of essence from the MXF file. If the optional AESEncContext
314 // argument is present, the essence is decrypted after reading. If the MXF
315 // file is encrypted and the AESDecContext argument is NULL, the frame buffer
316 // will contain the ciphertext frame data. If the HMACContext argument is
317 // not NULL, the HMAC will be calculated (if the file supports it).
318 // Returns RESULT_INIT if the file is not open, failure if the frame number is
319 // out of range, or if optional decrypt or HAMC operations fail.
320 Result_t ReadFrame(ui32_t frame_number, ASDCP::PCM::FrameBuffer&, ASDCP::AESDecContext* = 0, ASDCP::HMACContext* = 0) const;
322 // Print debugging information to stream
323 void DumpHeaderMetadata(FILE* = 0) const;
324 void DumpIndex(FILE* = 0) const;
328 //---------------------------------------------------------------------------------
332 using ASDCP::TimedText::TimedTextDescriptor;
333 using ASDCP::TimedText::TimedTextResourceDescriptor;
334 using ASDCP::TimedText::ResourceList_t;
337 class Type5UUIDFilenameResolver : public ASDCP::TimedText::IResourceResolver
339 typedef std::map<Kumu::UUID, std::string> ResourceMap;
341 ResourceMap m_ResourceMap;
342 std::string m_Dirname;
343 KM_NO_COPY_CONSTRUCT(Type5UUIDFilenameResolver);
346 Type5UUIDFilenameResolver();
347 virtual ~Type5UUIDFilenameResolver();
348 Result_t OpenRead(const std::string& dirname);
349 Result_t ResolveRID(const byte_t* uuid, ASDCP::TimedText::FrameBuffer& FrameBuf) const;
353 // Generate UUID asset ID values from file contents
354 Kumu::UUID CreatePNGNameId(const std::string& image_name);
355 Kumu::UUID CreateFontNameId(const std::string& font_name);
358 class ST2052_TextParser
361 ASDCP::mem_ptr<h__TextParser> m_Parser;
362 ASDCP_NO_COPY_CONSTRUCT(ST2052_TextParser);
366 virtual ~ST2052_TextParser();
368 // Opens an XML file for reading, parses data to provide a complete
369 // set of stream metadata for the MXFWriter below.
370 Result_t OpenRead(const std::string& filename) const;
372 // Parse an XML string
373 Result_t OpenRead(const std::string& xml_doc, const std::string& filename) const;
375 // The "profile_name" parameter was removed from OpenRead() because it made the API
376 // awkward WRT lexical compatibility with TimedText_Parser. The value can still be
377 // modified by changing the descriptor's NamespaceName property after the call to
378 // FillTimedTextDescriptor() has set it.
380 // Fill a TimedTextDescriptor struct with the values from the file's contents.
381 // Returns RESULT_INIT if the file is not open.
382 Result_t FillTimedTextDescriptor(ASDCP::TimedText::TimedTextDescriptor&) const;
384 // Reads the complete Timed Text Resource into the given string.
385 Result_t ReadTimedTextResource(std::string&) const;
387 // Reads the Ancillary Resource having the given ID. Fails if the buffer
388 // is too small or the resource does not exist. The optional Resolver
389 // argument can be provided which will be used to retrieve the resource
390 // having a particulat UUID. If a Resolver is not supplied, the default
391 // internal resolver will return the contents of the file having the UUID
392 // as the filename. The filename must exist in the same directory as the
393 // XML file opened with OpenRead().
394 Result_t ReadAncillaryResource(const Kumu::UUID&, ASDCP::TimedText::FrameBuffer&,
395 const ASDCP::TimedText::IResourceResolver* Resolver = 0) const;
402 ASDCP::mem_ptr<h__Writer> m_Writer;
403 ASDCP_NO_COPY_CONSTRUCT(MXFWriter);
407 virtual ~MXFWriter();
409 // Warning: direct manipulation of MXF structures can interfere
410 // with the normal operation of the wrapper. Caveat emptor!
411 virtual ASDCP::MXF::OP1aHeader& OP1aHeader();
412 virtual ASDCP::MXF::RIP& RIP();
414 // Open the file for writing. The file must not exist. Returns error if
415 // the operation cannot be completed or if nonsensical data is discovered
416 // in the essence descriptor.
417 Result_t OpenWrite(const std::string& filename, const ASDCP::WriterInfo&,
418 const ASDCP::TimedText::TimedTextDescriptor&, ui32_t HeaderSize = 16384);
420 // Writes the Timed-Text Resource to the MXF file. The file must be UTF-8
421 // encoded. If the optional AESEncContext argument is present, the essence
422 // is encrypted prior to writing. Fails if the file is not open, is finalized,
423 // or an operating system error occurs.
424 // This method may only be called once, and it must be called before any
425 // call to WriteAncillaryResource(). RESULT_STATE will be returned if these
426 // conditions are not met.
427 Result_t WriteTimedTextResource(const std::string& XMLDoc, ASDCP::AESEncContext* = 0, ASDCP::HMACContext* = 0);
429 // Writes an Ancillary Resource to the MXF file. If the optional AESEncContext
430 // argument is present, the essence is encrypted prior to writing.
431 // Fails if the file is not open, is finalized, or an operating system
432 // error occurs. RESULT_STATE will be returned if the method is called before
433 // WriteTimedTextResource()
434 Result_t WriteAncillaryResource(const ASDCP::TimedText::FrameBuffer&, ASDCP::AESEncContext* = 0, ASDCP::HMACContext* = 0);
436 // Closes the MXF file, writing the index and revised header.
444 ASDCP::mem_ptr<h__Reader> m_Reader;
445 ASDCP_NO_COPY_CONSTRUCT(MXFReader);
449 virtual ~MXFReader();
451 // Warning: direct manipulation of MXF structures can interfere
452 // with the normal operation of the wrapper. Caveat emptor!
453 virtual ASDCP::MXF::OP1aHeader& OP1aHeader();
454 virtual AS_02::MXF::AS02IndexReader& AS02IndexReader();
455 virtual ASDCP::MXF::RIP& RIP();
457 // Open the file for reading. The file must exist. Returns error if the
458 // operation cannot be completed.
459 Result_t OpenRead(const std::string& filename) const;
461 // Returns RESULT_INIT if the file is not open.
462 Result_t Close() const;
464 // Fill a TimedTextDescriptor struct with the values from the file's header.
465 // Returns RESULT_INIT if the file is not open.
466 Result_t FillTimedTextDescriptor(ASDCP::TimedText::TimedTextDescriptor&) const;
468 // Fill a WriterInfo struct with the values from the file's header.
469 // Returns RESULT_INIT if the file is not open.
470 Result_t FillWriterInfo(ASDCP::WriterInfo&) const;
472 // Reads the complete Timed Text Resource into the given string. Fails if the resource
473 // is encrypted and AESDecContext is NULL (use the following method to retrieve the
474 // raw ciphertet block).
475 Result_t ReadTimedTextResource(std::string&, ASDCP::AESDecContext* = 0, ASDCP::HMACContext* = 0) const;
477 // Reads the complete Timed Text Resource from the MXF file. If the optional AESEncContext
478 // argument is present, the resource is decrypted after reading. If the MXF
479 // file is encrypted and the AESDecContext argument is NULL, the frame buffer
480 // will contain the ciphertext frame data. If the HMACContext argument is
481 // not NULL, the HMAC will be calculated (if the file supports it).
482 // Returns RESULT_INIT if the file is not open, failure if the frame number is
483 // out of range, or if optional decrypt or HAMC operations fail.
484 Result_t ReadTimedTextResource(ASDCP::TimedText::FrameBuffer&, ASDCP::AESDecContext* = 0, ASDCP::HMACContext* = 0) const;
486 // Reads the timed-text resource having the given UUID from the MXF file. If the
487 // optional AESEncContext argument is present, the resource is decrypted after
488 // reading. If the MXF file is encrypted and the AESDecContext argument is NULL,
489 // the frame buffer will contain the ciphertext frame data. If the HMACContext
490 // argument is not NULL, the HMAC will be calculated (if the file supports it).
491 // Returns RESULT_INIT if the file is not open, failure if the frame number is
492 // out of range, or if optional decrypt or HAMC operations fail.
493 Result_t ReadAncillaryResource(const Kumu::UUID&, ASDCP::TimedText::FrameBuffer&, ASDCP::AESDecContext* = 0, ASDCP::HMACContext* = 0) const;
495 // Print debugging information to stream
496 void DumpHeaderMetadata(FILE* = 0) const;
497 void DumpIndex(FILE* = 0) const;
499 } // namespace TimedText
507 ASDCP::mem_ptr<h__Writer> m_Writer;
508 ASDCP_NO_COPY_CONSTRUCT(MXFWriter);
512 virtual ~MXFWriter();
514 // Warning: direct manipulation of MXF structures can interfere
515 // with the normal operation of the wrapper. Caveat emptor!
516 virtual ASDCP::MXF::OP1aHeader& OP1aHeader();
517 virtual ASDCP::MXF::RIP& RIP();
519 // Open the file for writing. The file must not exist. Returns error if
520 // the operation cannot be completed or if nonsensical data is discovered
521 // in the essence descriptor.
522 Result_t OpenWrite(const std::string& filename, const ASDCP::WriterInfo&,
523 const std::string& isxd_document_namespace,
524 const ASDCP::Rational& edit_rate, const ui32_t& header_size = 16384,
525 const IndexStrategy_t& strategy = IS_FOLLOW, const ui32_t& partition_space = 10);
527 // Writes a frame of essence to the MXF file. If the optional AESEncContext
528 // argument is present, the essence is encrypted prior to writing.
529 // Fails if the file is not open, is finalized, or an operating system
531 Result_t WriteFrame(const ASDCP::FrameBuffer&, ASDCP::AESEncContext* = 0, ASDCP::HMACContext* = 0);
533 // Writes an XML text document to the MXF file as per RP 2067. If the
534 // optional AESEncContext argument is present, the document is encrypted
535 // prior to writing. Fails if the file is not open, is finalized, or an
536 // operating system error occurs.
537 Result_t AddDmsGenericPartUtf8Text(const ASDCP::FrameBuffer& frame_buffer, ASDCP::AESEncContext* enc = 0, ASDCP::HMACContext* hmac = 0);
539 // Closes the MXF file, writing the index and revised header.
547 ASDCP::mem_ptr<h__Reader> m_Reader;
548 ASDCP_NO_COPY_CONSTRUCT(MXFReader);
552 virtual ~MXFReader();
554 // Warning: direct manipulation of MXF structures can interfere
555 // with the normal operation of the wrapper. Caveat emptor!
556 virtual ASDCP::MXF::OP1aHeader& OP1aHeader();
557 virtual AS_02::MXF::AS02IndexReader& AS02IndexReader();
558 virtual ASDCP::MXF::RIP& RIP();
560 // Open the file for reading. The file must exist. Returns error if the
561 // operation cannot be completed.
562 Result_t OpenRead(const std::string& filename) const;
564 // Returns RESULT_INIT if the file is not open.
565 Result_t Close() const;
567 // Fill a WriterInfo struct with the values from the file's header.
568 // Returns RESULT_INIT if the file is not open.
569 Result_t FillWriterInfo(ASDCP::WriterInfo&) const;
571 // Reads a frame of essence from the MXF file. If the optional AESEncContext
572 // argument is present, the essence is decrypted after reading. If the MXF
573 // file is encrypted and the AESDecContext argument is NULL, the frame buffer
574 // will contain the ciphertext frame data. If the HMACContext argument is
575 // not NULL, the HMAC will be calculated (if the file supports it).
576 // Returns RESULT_INIT if the file is not open, failure if the frame number is
577 // out of range, or if optional decrypt or HAMC operations fail.
578 Result_t ReadFrame(ui32_t frame_number, ASDCP::FrameBuffer&,
579 ASDCP::AESDecContext* = 0, ASDCP::HMACContext* = 0) const;
581 // Reads a Generic Stream Partition payload. Returns RESULT_INIT if the file is
582 // not open, or RESULT_FORMAT if the SID is not present in the RIP, or if the
583 // actual partition at ByteOffset does not have a matching BodySID value.
584 // Encryption is not currently supported.
585 Result_t ReadGenericStreamPartitionPayload(ui32_t SID, ASDCP::FrameBuffer& FrameBuf);
587 // Print debugging information to stream
588 void DumpHeaderMetadata(FILE* = 0) const;
589 void DumpIndex(FILE* = 0) const;