2 Copyright (c) 2005-2015, 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.
28 \version $Id: MXF.h,v 1.57 2015/10/10 20:26:29 jhurst Exp $
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 return ( ( Tag.a < rhs.Tag.a ) || ( Tag.b < rhs.Tag.b ) );
191 inline const char* EncodeString(char* str_buf, ui32_t buf_len) const {
192 snprintf(str_buf, buf_len, "%02x %02x: ", Tag.a, Tag.b);
193 UL.EncodeString(str_buf + strlen(str_buf), buf_len - strlen(str_buf));
197 inline bool HasValue() const { return UL.HasValue(); }
198 inline ui32_t ArchiveLength() const { return 2 + UL.ArchiveLength(); }
200 inline bool Unarchive(Kumu::MemIOReader* Reader) {
201 if ( ! Reader->ReadUi8(&Tag.a) ) return false;
202 if ( ! Reader->ReadUi8(&Tag.b) ) return false;
203 return UL.Unarchive(Reader);
206 inline bool Archive(Kumu::MemIOWriter* Writer) const {
207 if ( ! Writer->WriteUi8(Tag.a) ) return false;
208 if ( ! Writer->WriteUi8(Tag.b) ) return false;
209 return UL.Archive(Writer);
213 Batch<LocalTagEntry> LocalTagEntryBatch;
214 const Dictionary*& m_Dict;
216 Primer(const Dictionary*&);
219 virtual void ClearTagList();
220 virtual Result_t InsertTag(const MDDEntry& Entry, ASDCP::TagValue& Tag);
221 virtual Result_t TagForKey(const ASDCP::UL& Key, ASDCP::TagValue& Tag);
223 virtual Result_t InitFromBuffer(const byte_t* p, ui32_t l);
224 virtual Result_t WriteToBuffer(ASDCP::FrameBuffer&);
225 virtual Result_t WriteToFile(Kumu::FileWriter& Writer);
226 virtual void Dump(FILE* = 0);
229 // wrapper object manages optional properties
230 template <class PropertyType>
231 class optional_property
233 PropertyType m_property;
237 optional_property() : m_has_value(false) {}
238 optional_property(const PropertyType& value) : m_property(value), m_has_value(true) {}
239 const optional_property<PropertyType>& operator=(const PropertyType& rhs) {
240 this->m_property = rhs;
241 this->m_has_value = true;
244 bool operator==(const PropertyType& rhs) const { return this->m_property == rhs; }
245 bool operator==(const optional_property<PropertyType>& rhs) const { return this->m_property == rhs.m_property; }
246 operator PropertyType&() { return this->m_property; }
247 void set(const PropertyType& rhs) { this->m_property = rhs; this->m_has_value = true; }
248 void set_has_value(bool has_value = true) { this->m_has_value = has_value; }
249 void reset(const PropertyType& rhs) { this->m_has_value = false; }
250 bool empty() const { return ! m_has_value; }
251 PropertyType& get() { return m_property; }
252 const PropertyType& const_get() const { return m_property; }
255 // wrapper object manages optional properties
256 template <class PropertyType>
257 class optional_container_property
259 PropertyType m_property;
262 optional_container_property() {}
263 optional_container_property(const PropertyType& value) : m_property(value) {}
264 const optional_container_property<PropertyType>& operator=(const PropertyType& rhs) {
265 this->Copy(rhs.m_property);
269 bool operator==(const PropertyType& rhs) const { return this->m_property == rhs; }
270 bool operator==(const optional_property<PropertyType>& rhs) const { return this->m_property == rhs.m_property; }
271 operator PropertyType&() { return this->m_property; }
272 void set(const PropertyType& rhs) { this->m_property = rhs; }
273 void reset(const PropertyType& rhs) { this->clear(); }
274 bool empty() const { return ! this->m_property.HasValue(); }
275 PropertyType& get() { return m_property; }
276 const PropertyType& const_get() const { return m_property; }
279 // base class of all metadata objects
281 class InterchangeObject : public ASDCP::KLVPacket
286 const Dictionary*& m_Dict;
287 IPrimerLookup* m_Lookup;
289 optional_property<UUID> GenerationUID;
291 InterchangeObject(const Dictionary*& d) : m_Dict(d), m_Lookup(0) {}
292 virtual ~InterchangeObject() {}
294 virtual void Copy(const InterchangeObject& rhs);
295 virtual Result_t InitFromTLVSet(TLVReader& TLVSet);
296 virtual Result_t InitFromBuffer(const byte_t* p, ui32_t l);
297 virtual Result_t WriteToTLVSet(TLVWriter& TLVSet);
298 virtual Result_t WriteToBuffer(ASDCP::FrameBuffer&);
299 virtual bool IsA(const byte_t* label);
300 virtual const char* ObjectName() { return "InterchangeObject"; }
301 virtual void Dump(FILE* stream = 0);
305 typedef std::list<InterchangeObject*> InterchangeObject_list_t;
308 class Preface : public InterchangeObject
310 ASDCP_NO_COPY_CONSTRUCT(Preface);
314 const Dictionary*& m_Dict;
315 Kumu::Timestamp LastModifiedDate;
317 optional_property<ui32_t> ObjectModelVersion;
318 optional_property<UUID> PrimaryPackage;
319 Array<UUID> Identifications;
321 UL OperationalPattern;
322 Batch<UL> EssenceContainers;
324 optional_property<Batch<UL> > ApplicationSchemes;
326 Preface(const Dictionary*& d);
327 virtual ~Preface() {}
329 virtual void Copy(const Preface& rhs);
330 virtual Result_t InitFromTLVSet(TLVReader& TLVSet);
331 virtual Result_t InitFromBuffer(const byte_t* p, ui32_t l);
332 virtual Result_t WriteToTLVSet(TLVWriter& TLVSet);
333 virtual Result_t WriteToBuffer(ASDCP::FrameBuffer&);
334 virtual void Dump(FILE* = 0);
337 const ui32_t MaxIndexSegmentSize = 65536;
340 class IndexTableSegment : public InterchangeObject
343 ASDCP_NO_COPY_CONSTRUCT(IndexTableSegment);
347 class DeltaEntry : public Kumu::IArchive
354 DeltaEntry() : PosTableIndex(0), Slice(0), ElementData(0) {}
355 DeltaEntry(i8_t pos, ui8_t slice, ui32_t data) : PosTableIndex(pos), Slice(slice), ElementData(data) {}
356 inline bool HasValue() const { return true; }
357 ui32_t ArchiveLength() const { return sizeof(ui32_t) + 2; }
358 bool Unarchive(Kumu::MemIOReader* Reader);
359 bool Archive(Kumu::MemIOWriter* Writer) const;
360 const char* EncodeString(char* str_buf, ui32_t buf_len) const;
364 class IndexEntry : public Kumu::IArchive
372 // if you use these, you will need to change CBRIndexEntriesPerSegment in MXF.cpp
373 // to a more suitable value
374 // std::list<ui32_t> SliceOffset;
375 // Array<Rational> PosTable;
377 IndexEntry() : TemporalOffset(0), KeyFrameOffset(0), Flags(0), StreamOffset(0) {}
378 IndexEntry(i8_t t_ofst, i8_t k_ofst, ui8_t flags, ui64_t s_ofst) :
379 TemporalOffset(t_ofst), KeyFrameOffset(k_ofst), Flags(flags), StreamOffset(s_ofst) {}
380 inline bool HasValue() const { return true; }
381 ui32_t ArchiveLength() const { return sizeof(ui64_t) + 3; };
382 bool Unarchive(Kumu::MemIOReader* Reader);
383 bool Archive(Kumu::MemIOWriter* Writer) const;
384 const char* EncodeString(char* str_buf, ui32_t buf_len) const;
387 const Dictionary*& m_Dict;
388 ui64_t RtFileOffset; // not part of the MXF structure: used to manage runtime index access
389 ui64_t RtEntryOffset;
391 Rational IndexEditRate;
392 ui64_t IndexStartPosition;
393 ui64_t IndexDuration;
394 ui32_t EditUnitByteCount;
399 Array<DeltaEntry> DeltaEntryArray;
400 Array<IndexEntry> IndexEntryArray;
402 IndexTableSegment(const Dictionary*&);
403 virtual ~IndexTableSegment();
405 virtual void Copy(const IndexTableSegment& rhs);
406 virtual Result_t InitFromTLVSet(TLVReader& TLVSet);
407 virtual Result_t InitFromBuffer(const byte_t* p, ui32_t l);
408 virtual Result_t WriteToTLVSet(TLVWriter& TLVSet);
409 virtual Result_t WriteToBuffer(ASDCP::FrameBuffer&);
410 virtual void Dump(FILE* = 0);
413 //---------------------------------------------------------------------------------
415 class Identification;
419 class OP1aHeader : public Partition
421 Kumu::ByteString m_HeaderData;
422 ASDCP_NO_COPY_CONSTRUCT(OP1aHeader);
426 const Dictionary*& m_Dict;
427 ASDCP::MXF::Primer m_Primer;
430 OP1aHeader(const Dictionary*&);
431 virtual ~OP1aHeader();
432 virtual Result_t InitFromFile(const Kumu::FileReader& Reader);
433 virtual Result_t InitFromPartitionBuffer(const byte_t* p, ui32_t l);
434 virtual Result_t InitFromBuffer(const byte_t* p, ui32_t l);
435 virtual Result_t WriteToFile(Kumu::FileWriter& Writer, ui32_t HeaderLength = 16384);
436 virtual void Dump(FILE* = 0);
437 virtual Result_t GetMDObjectByID(const UUID&, InterchangeObject** = 0);
438 virtual Result_t GetMDObjectByType(const byte_t*, InterchangeObject** = 0);
439 virtual Result_t GetMDObjectsByType(const byte_t* ObjectID, std::list<InterchangeObject*>& ObjectList);
440 Identification* GetIdentification();
441 SourcePackage* GetSourcePackage();
444 // Searches the header object and returns the edit rate based on the contents of the
445 // File Package items. Logs an error message and returns false if anthing goes wrong.
446 bool GetEditRateFromFP(ASDCP::MXF::OP1aHeader& header, ASDCP::Rational& edit_rate);
449 class OPAtomIndexFooter : public Partition
451 Kumu::ByteString m_FooterData;
452 IndexTableSegment* m_CurrentSegment;
453 ui32_t m_BytesPerEditUnit;
456 IndexTableSegment::DeltaEntry m_DefaultDeltaEntry;
458 ASDCP_NO_COPY_CONSTRUCT(OPAtomIndexFooter);
462 const Dictionary*& m_Dict;
463 Kumu::fpos_t m_ECOffset;
464 IPrimerLookup* m_Lookup;
466 OPAtomIndexFooter(const Dictionary*&);
467 virtual ~OPAtomIndexFooter();
468 virtual Result_t InitFromFile(const Kumu::FileReader& Reader);
469 virtual Result_t InitFromPartitionBuffer(const byte_t* p, ui32_t l);
470 virtual Result_t InitFromBuffer(const byte_t* p, ui32_t l);
471 virtual Result_t WriteToFile(Kumu::FileWriter& Writer, ui64_t duration);
472 virtual void Dump(FILE* = 0);
474 virtual Result_t GetMDObjectByID(const UUID&, InterchangeObject** = 0);
475 virtual Result_t GetMDObjectByType(const byte_t*, InterchangeObject** = 0);
476 virtual Result_t GetMDObjectsByType(const byte_t* ObjectID, std::list<InterchangeObject*>& ObjectList);
478 virtual Result_t Lookup(ui32_t frame_num, IndexTableSegment::IndexEntry&) const;
479 virtual void PushIndexEntry(const IndexTableSegment::IndexEntry&);
480 virtual void SetDeltaParams(const IndexTableSegment::DeltaEntry&);
481 virtual void SetIndexParamsCBR(IPrimerLookup* lookup, ui32_t size, const Rational& Rate);
482 virtual void SetIndexParamsVBR(IPrimerLookup* lookup, const Rational& Rate, Kumu::fpos_t offset);
485 //---------------------------------------------------------------------------------
489 inline std::string to_lower(std::string str) {
490 std::transform(str.begin(), str.end(), str.begin(), ::tolower);
494 // ignore case when searching for audio labels
497 inline bool operator()(const std::string& a, const std::string& b) const {
498 return to_lower(a) < to_lower(b);
504 const std::string tag_name;
505 const bool requires_prefix;
508 label_traits(const std::string& tag_name, const bool requires_prefix, const UL ul) :
509 tag_name(tag_name), requires_prefix(requires_prefix), ul(ul) { }
512 typedef std::map<const std::string, const label_traits, ci_comp> mca_label_map_t;
514 bool decode_mca_string(const std::string& s, const mca_label_map_t& labels,
515 const Dictionary*& dict, const std::string& language, InterchangeObject_list_t&, ui32_t&);
518 class ASDCP_MCAConfigParser : public InterchangeObject_list_t
520 KM_NO_COPY_CONSTRUCT(ASDCP_MCAConfigParser);
521 ASDCP_MCAConfigParser();
524 mca_label_map_t m_LabelMap;
525 ui32_t m_ChannelCount;
526 const Dictionary*& m_Dict;
530 ASDCP_MCAConfigParser(const Dictionary*&);
531 bool DecodeString(const std::string& s, const std::string& language = "en-US");
533 // Valid only after a successful call to DecodeString
534 ui32_t ChannelCount() const;
538 class AS02_MCAConfigParser : public ASDCP_MCAConfigParser
540 KM_NO_COPY_CONSTRUCT(AS02_MCAConfigParser);
541 AS02_MCAConfigParser();
544 AS02_MCAConfigParser(const Dictionary*&);