2 Copyright (c) 2005-2018, John Hurst
5 Redistribution and use in source and binary forms, with or without
6 modification, are permitted provided that the following conditions
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.
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.
42 class InterchangeObject;
44 const ui32_t kl_length = ASDCP::SMPTE_UL_LENGTH + ASDCP::MXF_BER_LENGTH;
47 typedef ASDCP::MXF::InterchangeObject* (*MXFObjectFactory_t)(const Dictionary*&);
50 void SetObjectFactory(const UL& label, MXFObjectFactory_t factory);
53 InterchangeObject* CreateObject(const Dictionary*& Dict, const UL& label);
56 // seek an open file handle to the start of the RIP KLV packet
57 Result_t SeekToRIP(const Kumu::FileReader&);
60 class RIP : public ASDCP::KLVFilePacket
62 ASDCP_NO_COPY_CONSTRUCT(RIP);
67 class PartitionPair : public Kumu::IArchive
73 PartitionPair() : BodySID(0), ByteOffset(0) {}
74 PartitionPair(ui32_t sid, ui64_t offset) : BodySID(sid), ByteOffset(offset) {}
75 virtual ~PartitionPair() {}
77 ui32_t Size() { return sizeof(ui32_t) + sizeof(ui64_t); }
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());
85 inline bool HasValue() const { return true; }
86 inline ui32_t ArchiveLength() const { return sizeof(ui32_t) + sizeof(ui64_t); }
88 inline bool Unarchive(Kumu::MemIOReader* Reader) {
89 if ( ! Reader->ReadUi32BE(&BodySID) ) return false;
90 if ( ! Reader->ReadUi64BE(&ByteOffset) ) return false;
94 inline bool Archive(Kumu::MemIOWriter* Writer) const {
95 if ( ! Writer->WriteUi32BE(BodySID) ) return false;
96 if ( ! Writer->WriteUi64BE(ByteOffset) ) return false;
101 const Dictionary*& m_Dict;
103 typedef SimpleArray<PartitionPair>::iterator pair_iterator;
104 typedef SimpleArray<PartitionPair>::const_iterator const_pair_iterator;
106 SimpleArray<PartitionPair> PairArray;
108 RIP(const Dictionary*& d) : m_Dict(d) {}
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);
118 class Partition : public ASDCP::KLVFilePacket
120 ASDCP_NO_COPY_CONSTRUCT(Partition);
127 std::list<InterchangeObject*> m_List;
128 std::map<UUID, InterchangeObject*> m_Map;
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);
137 mem_ptr<PacketList> m_PacketList;
140 const Dictionary*& m_Dict;
145 ui64_t ThisPartition;
146 ui64_t PreviousPartition;
147 ui64_t FooterPartition;
148 ui64_t HeaderByteCount;
149 ui64_t IndexByteCount;
153 UL OperationalPattern;
154 Batch<UL> EssenceContainers;
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);
168 class Primer : public ASDCP::KLVFilePacket, public ASDCP::IPrimerLookup
170 class h__PrimerLookup;
171 mem_ptr<h__PrimerLookup> m_Lookup;
173 ASDCP_NO_COPY_CONSTRUCT(Primer);
178 class LocalTagEntry : Kumu::IArchive
184 LocalTagEntry() { Tag.a = Tag.b = 0; }
185 LocalTagEntry(const TagValue& tag, ASDCP::UL& ul) : Tag(tag), UL(ul) {}
187 bool operator<(const LocalTagEntry& rhs) const {
188 if ( Tag.a < rhs.Tag.a )
193 if ( Tag.a == rhs.Tag.a && Tag.b < rhs.Tag.b )
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));
207 inline bool HasValue() const { return UL.HasValue(); }
208 inline ui32_t ArchiveLength() const { return 2 + UL.ArchiveLength(); }
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);
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);
223 Batch<LocalTagEntry> LocalTagEntryBatch;
224 const Dictionary*& m_Dict;
226 Primer(const Dictionary*&);
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);
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);
239 // wrapper object manages optional properties
240 template <class PropertyType>
241 class optional_property
243 PropertyType m_property;
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;
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; }
265 // wrapper object manages optional properties
266 template <class PropertyType>
267 class optional_container_property
269 PropertyType m_property;
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);
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; }
289 // base class of all metadata objects
291 class InterchangeObject : public ASDCP::KLVPacket
296 const Dictionary*& m_Dict;
297 IPrimerLookup* m_Lookup;
299 optional_property<UUID> GenerationUID;
301 InterchangeObject(const Dictionary*& d) : m_Dict(d), m_Lookup(0) {}
302 virtual ~InterchangeObject() {}
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);
315 typedef std::list<InterchangeObject*> InterchangeObject_list_t;
318 class Preface : public InterchangeObject
320 ASDCP_NO_COPY_CONSTRUCT(Preface);
324 const Dictionary*& m_Dict;
325 Kumu::Timestamp LastModifiedDate;
327 optional_property<ui32_t> ObjectModelVersion;
328 optional_property<UUID> PrimaryPackage;
329 Array<UUID> Identifications;
331 UL OperationalPattern;
332 Batch<UL> EssenceContainers;
334 optional_property<Batch<UL> > ApplicationSchemes;
335 optional_property<Batch<UL> > ConformsToSpecifications;
337 Preface(const Dictionary*& d);
338 virtual ~Preface() {}
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);
348 const ui32_t MaxIndexSegmentSize = 65536;
351 class IndexTableSegment : public InterchangeObject
354 ASDCP_NO_COPY_CONSTRUCT(IndexTableSegment);
358 class DeltaEntry : public Kumu::IArchive
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;
375 class IndexEntry : public Kumu::IArchive
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;
388 IndexEntry() : TemporalOffset(0), KeyFrameOffset(0), Flags(0x80), 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;
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;
402 Rational IndexEditRate;
403 ui64_t IndexStartPosition;
404 ui64_t IndexDuration;
405 ui32_t EditUnitByteCount;
410 Array<DeltaEntry> DeltaEntryArray;
411 Array<IndexEntry> IndexEntryArray;
413 IndexTableSegment(const Dictionary*&);
414 virtual ~IndexTableSegment();
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);
424 //---------------------------------------------------------------------------------
426 class Identification;
430 class OP1aHeader : public Partition
432 Kumu::ByteString m_HeaderData;
433 ASDCP_NO_COPY_CONSTRUCT(OP1aHeader);
437 const Dictionary*& m_Dict;
438 ASDCP::MXF::Primer m_Primer;
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();
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);
460 class OPAtomIndexFooter : public Partition
462 Kumu::ByteString m_FooterData;
463 IndexTableSegment* m_CurrentSegment;
464 ui32_t m_BytesPerEditUnit;
467 IndexTableSegment::DeltaEntry m_DefaultDeltaEntry;
469 ASDCP_NO_COPY_CONSTRUCT(OPAtomIndexFooter);
473 const Dictionary*& m_Dict;
474 Kumu::fpos_t m_ECOffset;
475 IPrimerLookup* m_Lookup;
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);
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);
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);
496 //---------------------------------------------------------------------------------
500 inline std::string to_lower(std::string str) {
501 std::transform(str.begin(), str.end(), str.begin(), ::tolower);
505 // ignore case when searching for audio labels
508 inline bool operator()(const std::string& a, const std::string& b) const {
509 return to_lower(a) < to_lower(b);
515 const std::string tag_name;
516 const bool requires_prefix;
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) { }
523 typedef std::map<const std::string, const label_traits, ci_comp> mca_label_map_t;
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&);
529 class ASDCP_MCAConfigParser : public InterchangeObject_list_t
531 KM_NO_COPY_CONSTRUCT(ASDCP_MCAConfigParser);
532 ASDCP_MCAConfigParser();
535 mca_label_map_t m_LabelMap;
536 ui32_t m_ChannelCount;
537 const Dictionary*& m_Dict;
541 ASDCP_MCAConfigParser(const Dictionary*&);
542 bool DecodeString(const std::string& s, const std::string& language = "en-US");
544 // Valid only after a successful call to DecodeString
545 ui32_t ChannelCount() const;
549 class AS02_MCAConfigParser : public ASDCP_MCAConfigParser
551 KM_NO_COPY_CONSTRUCT(AS02_MCAConfigParser);
552 AS02_MCAConfigParser();
555 AS02_MCAConfigParser(const Dictionary*&);