o Added support for SMPTE RDD 47 "ISXD Track File"
[asdcplib.git] / src / MXF.h
1 /*
2 Copyright (c) 2005-2018, John Hurst
3 All rights reserved.
4
5 Redistribution and use in source and binary forms, with or without
6 modification, are permitted provided that the following conditions
7 are met:
8 1. Redistributions of source code must retain the above copyright
9    notice, this list of conditions and the following disclaimer.
10 2. Redistributions in binary form must reproduce the above copyright
11    notice, this list of conditions and the following disclaimer in the
12    documentation and/or other materials provided with the distribution.
13 3. The name of the author may not be used to endorse or promote products
14    derived from this software without specific prior written permission.
15
16 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18 OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19 IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21 NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25 THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27 /*! \file    MXF.h
28     \version $Id$
29     \brief   MXF objects
30 */
31
32 #ifndef _MXF_H_
33 #define _MXF_H_
34
35 #include "MXFTypes.h"
36 #include <algorithm>
37
38 namespace ASDCP
39 {
40   namespace MXF
41     {
42       class InterchangeObject;
43
44       const ui32_t kl_length = ASDCP::SMPTE_UL_LENGTH + ASDCP::MXF_BER_LENGTH;
45
46       //
47       typedef ASDCP::MXF::InterchangeObject* (*MXFObjectFactory_t)(const Dictionary*&);
48
49       //
50       void SetObjectFactory(const UL& label, MXFObjectFactory_t factory);
51
52       //
53       InterchangeObject* CreateObject(const Dictionary*& Dict, const UL& label);
54
55
56       // seek an open file handle to the start of the RIP KLV packet
57       Result_t SeekToRIP(const Kumu::FileReader&);
58       
59       //
60       class RIP : public ASDCP::KLVFilePacket
61         {
62           ASDCP_NO_COPY_CONSTRUCT(RIP);
63           RIP();
64
65         public:
66           //
67           class PartitionPair : public Kumu::IArchive
68             {
69             public:
70               ui32_t BodySID;
71               ui64_t ByteOffset;
72
73               PartitionPair() : BodySID(0), ByteOffset(0) {}
74               PartitionPair(ui32_t sid, ui64_t offset) : BodySID(sid), ByteOffset(offset) {}
75               virtual ~PartitionPair() {}
76
77               ui32_t Size() { return sizeof(ui32_t) + sizeof(ui64_t); }
78
79               inline const char* EncodeString(char* str_buf, ui32_t buf_len) const {
80                 Kumu::ui64Printer offset_str(ByteOffset);
81                 snprintf(str_buf, buf_len, "%-6u: %s", BodySID, offset_str.c_str());
82                 return str_buf;
83               }
84
85               inline bool HasValue() const { return true; }
86               inline ui32_t ArchiveLength() const { return sizeof(ui32_t) + sizeof(ui64_t); }
87
88               inline bool Unarchive(Kumu::MemIOReader* Reader) {
89                 if ( ! Reader->ReadUi32BE(&BodySID) ) return false;
90                 if ( ! Reader->ReadUi64BE(&ByteOffset) ) return false;
91                 return true;
92               }
93               
94               inline bool Archive(Kumu::MemIOWriter* Writer) const {
95                 if ( ! Writer->WriteUi32BE(BodySID) ) return false;
96                 if ( ! Writer->WriteUi64BE(ByteOffset) ) return false;
97                 return true;
98               }
99             };
100
101           const Dictionary*& m_Dict;
102
103           typedef SimpleArray<PartitionPair>::iterator pair_iterator;
104           typedef SimpleArray<PartitionPair>::const_iterator const_pair_iterator;
105
106           SimpleArray<PartitionPair> PairArray;
107
108         RIP(const Dictionary*& d) : m_Dict(d) {}
109           virtual ~RIP() {}
110           virtual Result_t InitFromFile(const Kumu::FileReader& Reader);
111           virtual Result_t WriteToFile(Kumu::FileWriter& Writer);
112           virtual bool GetPairBySID(ui32_t, PartitionPair&) const;
113           virtual void     Dump(FILE* = 0);
114         };
115
116
117       //
118       class Partition : public ASDCP::KLVFilePacket
119         {
120           ASDCP_NO_COPY_CONSTRUCT(Partition);
121           Partition();
122
123         protected:
124           class PacketList
125           {
126           public:
127             std::list<InterchangeObject*> m_List;
128             std::map<UUID, InterchangeObject*> m_Map;
129
130             ~PacketList();
131             void AddPacket(InterchangeObject* ThePacket); // takes ownership
132             Result_t GetMDObjectByID(const UUID& ObjectID, InterchangeObject** Object);
133             Result_t GetMDObjectByType(const byte_t* ObjectID, InterchangeObject** Object);
134             Result_t GetMDObjectsByType(const byte_t* ObjectID, std::list<InterchangeObject*>& ObjectList);
135           };
136
137           mem_ptr<PacketList> m_PacketList;
138
139         public:
140           const Dictionary*& m_Dict;
141
142           ui16_t    MajorVersion;
143           ui16_t    MinorVersion;
144           ui32_t    KAGSize;
145           ui64_t    ThisPartition;
146           ui64_t    PreviousPartition;
147           ui64_t    FooterPartition;
148           ui64_t    HeaderByteCount;
149           ui64_t    IndexByteCount;
150           ui32_t    IndexSID;
151           ui64_t    BodyOffset;
152           ui32_t    BodySID;
153           UL        OperationalPattern;
154           Batch<UL> EssenceContainers;
155
156           Partition(const Dictionary*&);
157           virtual ~Partition();
158           virtual void     AddChildObject(InterchangeObject*); // takes ownership
159           virtual Result_t InitFromFile(const Kumu::FileReader& Reader);
160           virtual Result_t InitFromBuffer(const byte_t* p, ui32_t l);
161           virtual Result_t WriteToFile(Kumu::FileWriter& Writer, UL& PartitionLabel);
162           virtual ui32_t   ArchiveSize(); // returns the size of the archived structure
163           virtual void     Dump(FILE* = 0);
164         };
165
166
167       //
168       class Primer : public ASDCP::KLVFilePacket, public ASDCP::IPrimerLookup
169         {
170           class h__PrimerLookup;
171           mem_ptr<h__PrimerLookup> m_Lookup;
172           ui8_t   m_LocalTag;
173           ASDCP_NO_COPY_CONSTRUCT(Primer);
174           Primer();
175
176         public:
177           //
178         class LocalTagEntry : Kumu::IArchive
179             {
180             public:
181               TagValue    Tag;
182               ASDCP::UL   UL;
183
184               LocalTagEntry() { Tag.a = Tag.b = 0; }
185             LocalTagEntry(const TagValue& tag, ASDCP::UL& ul) : Tag(tag), UL(ul) {}
186
187               bool operator<(const LocalTagEntry& rhs) const {
188                 if ( Tag.a < rhs.Tag.a )
189                   {
190                     return true;
191                   }
192                 
193                 if ( Tag.a == rhs.Tag.a && Tag.b < rhs.Tag.b )
194                   {
195                     return true;
196                   }
197
198                 return false;
199               }
200
201               inline const char* EncodeString(char* str_buf, ui32_t buf_len) const {
202                 snprintf(str_buf, buf_len, "%02x %02x: ", Tag.a, Tag.b);
203                 UL.EncodeString(str_buf + strlen(str_buf), buf_len - (ui32_t)strlen(str_buf));
204                 return str_buf;
205               }
206
207               inline bool HasValue() const { return UL.HasValue(); }
208               inline ui32_t ArchiveLength() const { return 2 + UL.ArchiveLength(); }
209
210               inline bool Unarchive(Kumu::MemIOReader* Reader) {
211                 if ( ! Reader->ReadUi8(&Tag.a) ) return false;
212                 if ( ! Reader->ReadUi8(&Tag.b) ) return false;
213                 return UL.Unarchive(Reader);
214               }
215
216               inline bool Archive(Kumu::MemIOWriter* Writer) const {
217                 if ( ! Writer->WriteUi8(Tag.a) ) return false;
218                 if ( ! Writer->WriteUi8(Tag.b) ) return false;
219                 return UL.Archive(Writer);
220               }
221             };
222
223           Batch<LocalTagEntry> LocalTagEntryBatch;
224           const Dictionary*& m_Dict;
225
226           Primer(const Dictionary*&);
227           virtual ~Primer();
228
229           virtual void     ClearTagList();
230           virtual Result_t InsertTag(const MDDEntry& Entry, ASDCP::TagValue& Tag);
231           virtual Result_t TagForKey(const ASDCP::UL& Key, ASDCP::TagValue& Tag);
232
233           virtual Result_t InitFromBuffer(const byte_t* p, ui32_t l);
234           virtual Result_t WriteToBuffer(ASDCP::FrameBuffer&);
235           virtual Result_t WriteToFile(Kumu::FileWriter& Writer);
236           virtual void     Dump(FILE* = 0);
237         };
238
239       // wrapper object manages optional properties
240       template <class PropertyType>
241         class optional_property
242         {
243           PropertyType m_property;
244           bool m_has_value;
245
246         public:
247         optional_property() : m_has_value(false) {}
248         optional_property(const PropertyType& value) : m_property(value), m_has_value(true) {}
249           const optional_property<PropertyType>& operator=(const PropertyType& rhs) {
250             this->m_property = rhs;
251             this->m_has_value = true;
252             return *this;
253           }
254           bool operator==(const PropertyType& rhs) const { return this->m_property == rhs; }
255           bool operator==(const optional_property<PropertyType>& rhs) const { return this->m_property == rhs.m_property; }
256           operator PropertyType&() { return this->m_property; }
257           void set(const PropertyType& rhs) { this->m_property = rhs; this->m_has_value = true; }
258           void set_has_value(bool has_value = true) { this->m_has_value = has_value; }
259           void reset(const PropertyType& rhs) { this->m_has_value = false; }
260           bool empty() const { return ! m_has_value; }
261           PropertyType& get() { return m_property; }
262           const PropertyType& const_get() const { return m_property; }
263         };
264
265       // wrapper object manages optional properties
266       template <class PropertyType>
267         class optional_container_property
268         {
269           PropertyType m_property;
270
271         public:
272           optional_container_property() {}
273         optional_container_property(const PropertyType& value) : m_property(value) {}
274           const optional_container_property<PropertyType>& operator=(const PropertyType& rhs) {
275             this->Copy(rhs.m_property);
276             return *this;
277           }
278
279           bool operator==(const PropertyType& rhs) const { return this->m_property == rhs; }
280           bool operator==(const optional_property<PropertyType>& rhs) const { return this->m_property == rhs.m_property; }
281           operator PropertyType&() { return this->m_property; }
282           void set(const PropertyType& rhs) { this->m_property = rhs; }
283           void reset(const PropertyType& rhs) { this->clear(); }
284           bool empty() const { return ! this->m_property.HasValue(); }
285           PropertyType& get() { return m_property; }
286           const PropertyType& const_get() const { return m_property; }
287         };
288
289       // base class of all metadata objects
290       //
291       class InterchangeObject : public ASDCP::KLVPacket
292         {
293           InterchangeObject();
294
295         public:
296           const Dictionary*& m_Dict;
297           IPrimerLookup* m_Lookup;
298           UUID           InstanceUID;
299           optional_property<UUID>  GenerationUID;
300
301         InterchangeObject(const Dictionary*& d) : m_Dict(d), m_Lookup(0) {}
302           virtual ~InterchangeObject() {}
303
304           virtual void Copy(const InterchangeObject& rhs);
305           virtual Result_t InitFromTLVSet(TLVReader& TLVSet);
306           virtual Result_t InitFromBuffer(const byte_t* p, ui32_t l);
307           virtual Result_t WriteToTLVSet(TLVWriter& TLVSet);
308           virtual Result_t WriteToBuffer(ASDCP::FrameBuffer&);
309           virtual bool     IsA(const byte_t* label);
310           virtual const char* ObjectName() { return "InterchangeObject"; }
311           virtual void     Dump(FILE* stream = 0);
312         };
313
314       //
315       typedef std::list<InterchangeObject*> InterchangeObject_list_t;
316
317       //
318       class Preface : public InterchangeObject
319         {
320           ASDCP_NO_COPY_CONSTRUCT(Preface);
321           Preface();
322
323         public:
324           const Dictionary*& m_Dict;
325           Kumu::Timestamp    LastModifiedDate;
326           ui16_t       Version;
327           optional_property<ui32_t> ObjectModelVersion;
328           optional_property<UUID> PrimaryPackage;
329           Array<UUID>  Identifications;
330           UUID         ContentStorage;
331           UL           OperationalPattern;
332           Batch<UL>    EssenceContainers;
333           Batch<UL>    DMSchemes;
334           optional_property<Batch<UL> > ApplicationSchemes;
335           optional_property<Batch<UL> > ConformsToSpecifications;
336
337           Preface(const Dictionary*& d);
338           virtual ~Preface() {}
339
340           virtual void Copy(const Preface& rhs);
341           virtual Result_t InitFromTLVSet(TLVReader& TLVSet);
342           virtual Result_t InitFromBuffer(const byte_t* p, ui32_t l);
343           virtual Result_t WriteToTLVSet(TLVWriter& TLVSet);
344           virtual Result_t WriteToBuffer(ASDCP::FrameBuffer&);
345           virtual void     Dump(FILE* = 0);
346         };
347
348       const ui32_t MaxIndexSegmentSize = 65536;
349
350       //
351       class IndexTableSegment : public InterchangeObject
352         {
353           IndexTableSegment();
354           ASDCP_NO_COPY_CONSTRUCT(IndexTableSegment);
355
356         public:
357           //
358         class DeltaEntry : public Kumu::IArchive
359             {
360             public:
361               i8_t    PosTableIndex;
362               ui8_t   Slice;
363               ui32_t  ElementData;
364
365               DeltaEntry() : PosTableIndex(0), Slice(0), ElementData(0) {}
366               DeltaEntry(i8_t pos, ui8_t slice, ui32_t data) : PosTableIndex(pos), Slice(slice), ElementData(data) {}
367               inline bool HasValue() const { return true; }
368               ui32_t      ArchiveLength() const { return sizeof(ui32_t) + 2; }
369               bool        Unarchive(Kumu::MemIOReader* Reader);
370               bool        Archive(Kumu::MemIOWriter* Writer) const;
371               const char* EncodeString(char* str_buf, ui32_t buf_len) const;
372             };
373
374           //
375           class IndexEntry : public Kumu::IArchive
376             {
377             public:
378               i8_t               TemporalOffset;
379               i8_t               KeyFrameOffset;
380               ui8_t              Flags;
381               ui64_t             StreamOffset;
382
383               // if you use these, you will need to change CBRIndexEntriesPerSegment in MXF.cpp
384               // to a more suitable value
385               //              std::list<ui32_t>  SliceOffset;
386               //              Array<Rational>    PosTable;
387
388               IndexEntry() : TemporalOffset(0), KeyFrameOffset(0), Flags(0), StreamOffset(0) {}
389               IndexEntry(i8_t t_ofst, i8_t k_ofst, ui8_t flags, ui64_t s_ofst) :
390                     TemporalOffset(t_ofst), KeyFrameOffset(k_ofst), Flags(flags), StreamOffset(s_ofst) {}
391               inline bool HasValue() const { return true; }
392               ui32_t      ArchiveLength() const { return sizeof(ui64_t) + 3; };
393               bool        Unarchive(Kumu::MemIOReader* Reader);
394               bool        Archive(Kumu::MemIOWriter* Writer) const;
395               const char* EncodeString(char* str_buf, ui32_t buf_len) const;
396             };
397
398           const Dictionary*& m_Dict;
399           ui64_t  RtFileOffset; // not part of the MXF structure: used to manage runtime index access 
400           ui64_t  RtEntryOffset;
401
402           Rational    IndexEditRate;
403           ui64_t      IndexStartPosition;
404           ui64_t      IndexDuration;
405           ui32_t      EditUnitByteCount;
406           ui32_t      IndexSID;
407           ui32_t      BodySID;
408           ui8_t       SliceCount;
409           ui8_t       PosTableCount;
410           Array<DeltaEntry> DeltaEntryArray;
411           Array<IndexEntry> IndexEntryArray;
412
413           IndexTableSegment(const Dictionary*&);
414           virtual ~IndexTableSegment();
415
416           virtual void Copy(const IndexTableSegment& rhs);
417           virtual Result_t InitFromTLVSet(TLVReader& TLVSet);
418           virtual Result_t InitFromBuffer(const byte_t* p, ui32_t l);
419           virtual Result_t WriteToTLVSet(TLVWriter& TLVSet);
420           virtual Result_t WriteToBuffer(ASDCP::FrameBuffer&);
421           virtual void     Dump(FILE* = 0);
422         };
423
424       //---------------------------------------------------------------------------------
425       //
426       class Identification;
427       class SourcePackage;
428
429       //
430       class OP1aHeader : public Partition
431         {
432           Kumu::ByteString m_HeaderData;
433           ASDCP_NO_COPY_CONSTRUCT(OP1aHeader);
434           OP1aHeader();
435
436         public:
437           const Dictionary*&  m_Dict;
438           ASDCP::MXF::Primer  m_Primer;
439           Preface*            m_Preface;
440
441           OP1aHeader(const Dictionary*&);
442           virtual ~OP1aHeader();
443           virtual Result_t InitFromFile(const Kumu::FileReader& Reader);
444           virtual Result_t InitFromPartitionBuffer(const byte_t* p, ui32_t l);
445           virtual Result_t InitFromBuffer(const byte_t* p, ui32_t l);
446           virtual Result_t WriteToFile(Kumu::FileWriter& Writer, ui32_t HeaderLength = 16384);
447           virtual void     Dump(FILE* = 0);
448           virtual Result_t GetMDObjectByID(const UUID&, InterchangeObject** = 0);
449           virtual Result_t GetMDObjectByType(const byte_t*, InterchangeObject** = 0);
450           virtual Result_t GetMDObjectsByType(const byte_t* ObjectID, std::list<InterchangeObject*>& ObjectList);
451           Identification*  GetIdentification();
452           SourcePackage*   GetSourcePackage();
453         };
454
455       // Searches the header object and returns the edit rate based on the contents of the
456       // File Package items.  Logs an error message and returns false if anthing goes wrong.
457       bool GetEditRateFromFP(ASDCP::MXF::OP1aHeader& header, ASDCP::Rational& edit_rate);
458
459       //
460       class OPAtomIndexFooter : public Partition
461         {
462           Kumu::ByteString    m_FooterData;
463           IndexTableSegment*  m_CurrentSegment;
464           ui32_t              m_BytesPerEditUnit;
465           Rational            m_EditRate;
466           ui32_t              m_BodySID;
467           IndexTableSegment::DeltaEntry m_DefaultDeltaEntry;
468
469           ASDCP_NO_COPY_CONSTRUCT(OPAtomIndexFooter);
470           OPAtomIndexFooter();
471
472         public:
473           const Dictionary*&   m_Dict;
474           Kumu::fpos_t        m_ECOffset;
475           IPrimerLookup*      m_Lookup;
476          
477           OPAtomIndexFooter(const Dictionary*&);
478           virtual ~OPAtomIndexFooter();
479           virtual Result_t InitFromFile(const Kumu::FileReader& Reader);
480           virtual Result_t InitFromPartitionBuffer(const byte_t* p, ui32_t l);
481           virtual Result_t InitFromBuffer(const byte_t* p, ui32_t l);
482           virtual Result_t WriteToFile(Kumu::FileWriter& Writer, ui64_t duration);
483           virtual void     Dump(FILE* = 0);
484
485           virtual Result_t GetMDObjectByID(const UUID&, InterchangeObject** = 0);
486           virtual Result_t GetMDObjectByType(const byte_t*, InterchangeObject** = 0);
487           virtual Result_t GetMDObjectsByType(const byte_t* ObjectID, std::list<InterchangeObject*>& ObjectList);
488
489           virtual Result_t Lookup(ui32_t frame_num, IndexTableSegment::IndexEntry&) const;
490           virtual void     PushIndexEntry(const IndexTableSegment::IndexEntry&);
491           virtual void     SetDeltaParams(const IndexTableSegment::DeltaEntry&);
492           virtual void     SetIndexParamsCBR(IPrimerLookup* lookup, ui32_t size, const Rational& Rate);
493           virtual void     SetIndexParamsVBR(IPrimerLookup* lookup, const Rational& Rate, Kumu::fpos_t offset);
494         };
495
496       //---------------------------------------------------------------------------------
497       //
498
499       //
500       inline std::string to_lower(std::string str) {
501         std::transform(str.begin(), str.end(), str.begin(), ::tolower);
502         return str;
503       }
504
505       // ignore case when searching for audio labels
506       struct ci_comp
507       {
508         inline bool operator()(const std::string& a, const std::string& b) const {
509           return to_lower(a) < to_lower(b);
510         }
511       };
512
513       struct label_traits
514       {
515         const std::string tag_name;
516         const bool requires_prefix;
517         const UL ul;
518
519       label_traits(const std::string& tag_name, const bool requires_prefix, const UL ul) : 
520         tag_name(tag_name), requires_prefix(requires_prefix), ul(ul) { }
521       };
522
523       typedef std::map<const std::string, const label_traits, ci_comp> mca_label_map_t;
524
525       bool decode_mca_string(const std::string& s, const mca_label_map_t& labels,
526                              const Dictionary*& dict, const std::string& language, InterchangeObject_list_t&, ui32_t&);
527
528       //
529       class ASDCP_MCAConfigParser : public InterchangeObject_list_t
530         {
531           KM_NO_COPY_CONSTRUCT(ASDCP_MCAConfigParser);
532           ASDCP_MCAConfigParser();
533
534         protected:
535           mca_label_map_t m_LabelMap;
536           ui32_t m_ChannelCount;
537           const Dictionary*& m_Dict;
538
539           
540         public:
541           ASDCP_MCAConfigParser(const Dictionary*&);
542           bool DecodeString(const std::string& s, const std::string& language = "en-US");
543
544           // Valid only after a successful call to DecodeString
545           ui32_t ChannelCount() const;
546         };
547
548       //
549       class AS02_MCAConfigParser : public ASDCP_MCAConfigParser
550         {
551           KM_NO_COPY_CONSTRUCT(AS02_MCAConfigParser);
552           AS02_MCAConfigParser();
553           
554         public:
555           AS02_MCAConfigParser(const Dictionary*&);
556         };
557
558     } // namespace MXF
559 } // namespace ASDCP
560
561
562 #endif // _MXF_H_
563
564 //
565 // end MXF.h
566 //