Compare commits

...

15 Commits

Author SHA1 Message Date
Marius Kittler 53701255ca WIP: Rework notifications 2017-11-16 17:54:42 +01:00
Marius Kittler e232c907ea status provider: Use inline not inside the class definition 2017-11-16 17:13:58 +01:00
Marius Kittler d5613954a4 Remove unnecessary cast 2017-11-16 17:12:21 +01:00
Martchus 44af199aeb Use unordered_map in MatroskaCuePositionUpdater
std::unordered_map should be faster than std::map
2017-10-30 20:48:30 +01:00
Martchus b69aab0cd1 Handle Mkv files with unknown element sizes correctly
Those elements are still assumed to fill the max available
space. However, if it turns out one "child" is more likely
a sibling, the wrong assumption is fixed.
2017-10-30 20:48:30 +01:00
Martchus 618efe3f96 Add function to determine Mkv element level by ID
Does not cover all element IDs but should be more
than sufficient for its purpose.
2017-10-30 20:48:30 +01:00
Martchus fdb23e3cd0 Add method to get last child of file element 2017-10-30 20:48:30 +01:00
Martchus bf31c9e89f Add methods to get n-th parent of file element 2017-10-30 20:48:30 +01:00
Martchus 4231f2679e Add method to determine level of file element 2017-10-30 20:48:30 +01:00
Martchus 8109f84f38 Allow specifying whether the size of an element is unknown 2017-10-30 20:48:30 +01:00
Martchus 1814cd9bfc Uniform spelling of Matroska IDs 2017-10-30 20:48:30 +01:00
Martchus 0f12cf346c Uniform/simplify typedefs in templates
* Begin type names with capital letter
* Remove typedefs for implementation type
* Remove useless/obsolete comments
* Simplify relevant code
2017-10-30 20:48:30 +01:00
Martchus 86d51f1e96 Use static polymorphism in FieldMapBasedTag 2017-10-30 20:48:30 +01:00
Martchus 90eac982a7 Restructure FieldMapBasedTag to use CRTP 2017-10-30 20:48:30 +01:00
Martchus 001cfc931d Update major version to 7 2017-10-30 20:48:30 +01:00
40 changed files with 892 additions and 538 deletions

View File

@ -171,8 +171,8 @@ set(META_APP_NAME "Tag Parser")
set(META_APP_AUTHOR "Martchus") set(META_APP_AUTHOR "Martchus")
set(META_APP_URL "https://github.com/${META_APP_AUTHOR}/${META_PROJECT_NAME}") set(META_APP_URL "https://github.com/${META_APP_AUTHOR}/${META_PROJECT_NAME}")
set(META_APP_DESCRIPTION "C++ library for reading and writing MP4 (iTunes), ID3, Vorbis, Opus, FLAC and Matroska tags") set(META_APP_DESCRIPTION "C++ library for reading and writing MP4 (iTunes), ID3, Vorbis, Opus, FLAC and Matroska tags")
set(META_VERSION_MAJOR 6) set(META_VERSION_MAJOR 7)
set(META_VERSION_MINOR 5) set(META_VERSION_MINOR 0)
set(META_VERSION_PATCH 0) set(META_VERSION_PATCH 0)
set(META_PUBLIC_SHARED_LIB_DEPENDS c++utilities) set(META_PUBLIC_SHARED_LIB_DEPENDS c++utilities)
set(META_PUBLIC_STATIC_LIB_DEPENDS c++utilities_static) set(META_PUBLIC_STATIC_LIB_DEPENDS c++utilities_static)

View File

@ -8,51 +8,75 @@
namespace Media { namespace Media {
/*!
* \class Media::FieldMapBasedTagTraits
* \brief Defines traits for the specified \a ImplementationType.
*
* A template specialization for each FieldMapBasedTag subclass must be provided.
*/
template<typename ImplementationType>
class FieldMapBasedTagTraits
{};
/*! /*!
* \class Media::FieldMapBasedTag * \class Media::FieldMapBasedTag
* \brief The FieldMapBasedTag provides a generic implementation of Tag which stores * \brief The FieldMapBasedTag provides a generic implementation of Tag which stores
* the tag fields using std::multimap. * the tag fields using std::multimap.
* *
* The FieldMapBasedTag class only provides the interface and common functionality. * The FieldMapBasedTag class only provides the interface and common functionality.
* It is meant to be subclassed. * It is meant to be subclassed using CRTP pattern.
* *
* \tparam FieldType Specifies the class used to store the fields. Should be a subclass * \remarks This template class is intended to be subclassed using
* of TagField. * with the "Curiously recurring template pattern".
*
* \tparam Compare Specifies the key comparsion function. Default is std::less.
*/ */
template <class FieldType, class Compare = std::less<typename FieldType::identifierType> > template <class ImplementationType>
class FieldMapBasedTag : public Tag class FieldMapBasedTag : public Tag
{ {
friend class FieldMapBasedTagTraits<ImplementationType>;
public: public:
typedef typename FieldMapBasedTagTraits<ImplementationType>::FieldType FieldType;
typedef typename FieldMapBasedTagTraits<ImplementationType>::FieldType::IdentifierType IdentifierType;
typedef typename FieldMapBasedTagTraits<ImplementationType>::Compare Compare;
FieldMapBasedTag(); FieldMapBasedTag();
virtual const TagValue &value(const typename FieldType::identifierType &id) const; // FIXME: use static polymorphism TagType type() const;
const char *typeName() const;
TagTextEncoding proposedTextEncoding() const;
const TagValue &value(const IdentifierType &id) const;
const TagValue &value(KnownField field) const; const TagValue &value(KnownField field) const;
std::vector<const TagValue *> values(const typename FieldType::identifierType &id) const; std::vector<const TagValue *> values(const IdentifierType &id) const;
std::vector<const TagValue *> values(KnownField field) const; std::vector<const TagValue *> values(KnownField field) const;
virtual bool setValue(const typename FieldType::identifierType &id, const TagValue &value); // FIXME: use static polymorphism bool setValue(const IdentifierType &id, const TagValue &value);
bool setValue(KnownField field, const TagValue &value); bool setValue(KnownField field, const TagValue &value);
bool setValues(const typename FieldType::identifierType &id, const std::vector<TagValue> &values); bool setValues(const IdentifierType &id, const std::vector<TagValue> &values);
bool setValues(KnownField field, const std::vector<TagValue> &values); bool setValues(KnownField field, const std::vector<TagValue> &values);
bool hasField(KnownField field) const; bool hasField(KnownField field) const;
virtual bool hasField(const typename FieldType::identifierType &id) const; // FIXME: use static polymorphism bool hasField(const IdentifierType &id) const;
void removeAllFields(); void removeAllFields();
const std::multimap<typename FieldType::identifierType, FieldType, Compare> &fields() const; const std::multimap<IdentifierType, FieldType, Compare> &fields() const;
std::multimap<typename FieldType::identifierType, FieldType, Compare> &fields(); std::multimap<IdentifierType, FieldType, Compare> &fields();
unsigned int fieldCount() const; unsigned int fieldCount() const;
virtual typename FieldType::identifierType fieldId(KnownField value) const = 0; // FIXME: use static polymorphism IdentifierType fieldId(KnownField value) const;
virtual KnownField knownField(const typename FieldType::identifierType &id) const = 0; // FIXME: use static polymorphism KnownField knownField(const IdentifierType &id) const;
bool supportsField(KnownField field) const; bool supportsField(KnownField field) const;
using Tag::proposedDataType; using Tag::proposedDataType;
virtual TagDataType proposedDataType(const typename FieldType::identifierType &id) const; // FIXME: use static polymorphism TagDataType proposedDataType(const IdentifierType &id) const;
int insertFields(const FieldMapBasedTag<FieldType, Compare> &from, bool overwrite); int insertFields(const FieldMapBasedTag<ImplementationType> &from, bool overwrite);
unsigned int insertValues(const Tag &from, bool overwrite); unsigned int insertValues(const Tag &from, bool overwrite);
void ensureTextValuesAreProperlyEncoded(); void ensureTextValuesAreProperlyEncoded();
typedef FieldType fieldType;
protected:
const TagValue &internallyGetValue(const IdentifierType &id) const;
bool internallySetValue(const IdentifierType &id, const TagValue &value);
bool internallyHasField(const IdentifierType &id) const;
// no default implementation: IdentifierType internallyGetFieldId(KnownField field) const;
// no default implementation: KnownField internallyGetKnownField(const IdentifierType &id) const;
TagDataType internallyGetProposedDataType(const IdentifierType &id) const;
private: private:
std::multimap<typename FieldType::identifierType, FieldType, Compare> m_fields; std::multimap<IdentifierType, FieldType, Compare> m_fields;
}; };
/*! /*!
@ -72,23 +96,51 @@ private:
/*! /*!
* \brief Constructs a new FieldMapBasedTag. * \brief Constructs a new FieldMapBasedTag.
*/ */
template <class FieldType, class Compare> template <class ImplementationType>
FieldMapBasedTag<FieldType, Compare>::FieldMapBasedTag() FieldMapBasedTag<ImplementationType>::FieldMapBasedTag()
{} {}
template <class ImplementationType>
TagType FieldMapBasedTag<ImplementationType>::type() const
{
return ImplementationType::tagType;
}
template <class ImplementationType>
const char *FieldMapBasedTag<ImplementationType>::typeName() const
{
return ImplementationType::tagName;
}
template<class ImplementationType>
TagTextEncoding FieldMapBasedTag<ImplementationType>::proposedTextEncoding() const
{
return ImplementationType::defaultTextEncoding;
}
/*! /*!
* \brief Returns the value of the field with the specified \a id. * \brief Default implementation for value().
* \sa Tag::value() * \remarks Shadow in subclass to provide custom implementation.
*/ */
template <class FieldType, class Compare> template<class ImplementationType>
inline const TagValue &FieldMapBasedTag<FieldType, Compare>::value(const typename FieldType::identifierType &id) const const TagValue &FieldMapBasedTag<ImplementationType>::internallyGetValue(const IdentifierType &id) const
{ {
auto i = m_fields.find(id); auto i = m_fields.find(id);
return i != m_fields.end() ? i->second.value() : TagValue::empty(); return i != m_fields.end() ? i->second.value() : TagValue::empty();
} }
template <class FieldType, class Compare> /*!
inline const TagValue &FieldMapBasedTag<FieldType, Compare>::value(KnownField field) const * \brief Returns the value of the field with the specified \a id.
* \sa Tag::value()
*/
template <class ImplementationType>
inline const TagValue &FieldMapBasedTag<ImplementationType>::value(const IdentifierType &id) const
{
return static_cast<const ImplementationType *>(this)->internallyGetValue(id);
}
template <class ImplementationType>
inline const TagValue &FieldMapBasedTag<ImplementationType>::value(KnownField field) const
{ {
return value(fieldId(field)); return value(fieldId(field));
} }
@ -97,8 +149,8 @@ inline const TagValue &FieldMapBasedTag<FieldType, Compare>::value(KnownField fi
* \brief Returns the values of the field with the specified \a id. * \brief Returns the values of the field with the specified \a id.
* \sa Tag::values() * \sa Tag::values()
*/ */
template <class FieldType, class Compare> template <class ImplementationType>
inline std::vector<const TagValue *> FieldMapBasedTag<FieldType, Compare>::values(const typename FieldType::identifierType &id) const inline std::vector<const TagValue *> FieldMapBasedTag<ImplementationType>::values(const IdentifierType &id) const
{ {
auto range = m_fields.equal_range(id); auto range = m_fields.equal_range(id);
std::vector<const TagValue *> values; std::vector<const TagValue *> values;
@ -110,24 +162,24 @@ inline std::vector<const TagValue *> FieldMapBasedTag<FieldType, Compare>::value
return values; return values;
} }
template <class FieldType, class Compare> template <class ImplementationType>
inline std::vector<const TagValue *> FieldMapBasedTag<FieldType, Compare>::values(KnownField field) const inline std::vector<const TagValue *> FieldMapBasedTag<ImplementationType>::values(KnownField field) const
{ {
return values(fieldId(field)); return values(fieldId(field));
} }
template <class FieldType, class Compare> template <class ImplementationType>
inline bool FieldMapBasedTag<FieldType, Compare>::setValue(KnownField field, const TagValue &value) inline bool FieldMapBasedTag<ImplementationType>::setValue(KnownField field, const TagValue &value)
{ {
return setValue(fieldId(field), value); return setValue(fieldId(field), value);
} }
/*! /*!
* \brief Assigns the given \a value to the field with the specified \a id. * \brief Default implementation for setValue().
* \sa Tag::setValue() * \remarks Shadow in subclass to provide custom implementation.
*/ */
template <class FieldType, class Compare> template<class ImplementationType>
bool FieldMapBasedTag<FieldType, Compare>::setValue(const typename FieldType::identifierType &id, const Media::TagValue &value) bool FieldMapBasedTag<ImplementationType>::internallySetValue(const IdentifierType &id, const TagValue &value)
{ {
auto i = m_fields.find(id); auto i = m_fields.find(id);
if(i != m_fields.end()) { // field already exists -> set its value if(i != m_fields.end()) { // field already exists -> set its value
@ -140,14 +192,24 @@ bool FieldMapBasedTag<FieldType, Compare>::setValue(const typename FieldType::id
return true; return true;
} }
/*!
* \brief Assigns the given \a value to the field with the specified \a id.
* \sa Tag::setValue()
*/
template <class ImplementationType>
bool FieldMapBasedTag<ImplementationType>::setValue(const IdentifierType &id, const Media::TagValue &value)
{
return static_cast<ImplementationType *>(this)->internallySetValue(id, value);
}
/*! /*!
* \brief Assigns the given \a values to the field with the specified \a id. * \brief Assigns the given \a values to the field with the specified \a id.
* \remarks There might me more than one value assigned to an \a id. Whereas setValue() only alters the first value, this * \remarks There might me more than one value assigned to an \a id. Whereas setValue() only alters the first value, this
* method will replace all currently assigned values with the specified \a values. * method will replace all currently assigned values with the specified \a values.
* \sa Tag::setValues() * \sa Tag::setValues()
*/ */
template <class FieldType, class Compare> template <class ImplementationType>
bool FieldMapBasedTag<FieldType, Compare>::setValues(const typename FieldType::identifierType &id, const std::vector<TagValue> &values) bool FieldMapBasedTag<ImplementationType>::setValues(const IdentifierType &id, const std::vector<TagValue> &values)
{ {
auto valuesIterator = values.cbegin(); auto valuesIterator = values.cbegin();
auto range = m_fields.equal_range(id); auto range = m_fields.equal_range(id);
@ -176,23 +238,24 @@ bool FieldMapBasedTag<FieldType, Compare>::setValues(const typename FieldType::i
* method will replace all currently assigned values with the specified \a values. * method will replace all currently assigned values with the specified \a values.
* \sa Tag::setValues() * \sa Tag::setValues()
*/ */
template <class FieldType, class Compare> template <class ImplementationType>
bool FieldMapBasedTag<FieldType, Compare>::setValues(KnownField field, const std::vector<TagValue> &values) bool FieldMapBasedTag<ImplementationType>::setValues(KnownField field, const std::vector<TagValue> &values)
{ {
return setValues(fieldId(field), values); return setValues(fieldId(field), values);
} }
template <class FieldType, class Compare> template <class ImplementationType>
inline bool FieldMapBasedTag<FieldType, Compare>::hasField(KnownField field) const inline bool FieldMapBasedTag<ImplementationType>::hasField(KnownField field) const
{ {
return hasField(fieldId(field)); return hasField(fieldId(field));
} }
/*! /*!
* \brief Returns an indication whether the field with the specified \a id is present. * \brief Default implementation for hasField().
* \remarks Shadow in subclass to provide custom implementation.
*/ */
template <class FieldType, class Compare> template<class ImplementationType>
inline bool FieldMapBasedTag<FieldType, Compare>::hasField(const typename FieldType::identifierType &id) const bool FieldMapBasedTag<ImplementationType>::internallyHasField(const IdentifierType &id) const
{ {
for (auto range = m_fields.equal_range(id); range.first != range.second; ++range.first) { for (auto range = m_fields.equal_range(id); range.first != range.second; ++range.first) {
if(!range.first->second.value().isEmpty()) { if(!range.first->second.value().isEmpty()) {
@ -202,8 +265,17 @@ inline bool FieldMapBasedTag<FieldType, Compare>::hasField(const typename FieldT
return false; return false;
} }
template <class FieldType, class Compare> /*!
inline void FieldMapBasedTag<FieldType, Compare>::removeAllFields() * \brief Returns an indication whether the field with the specified \a id is present.
*/
template <class ImplementationType>
inline bool FieldMapBasedTag<ImplementationType>::hasField(const IdentifierType &id) const
{
return static_cast<const ImplementationType *>(this)->internallyHasField(id);
}
template <class ImplementationType>
inline void FieldMapBasedTag<ImplementationType>::removeAllFields()
{ {
m_fields.clear(); m_fields.clear();
} }
@ -211,8 +283,8 @@ inline void FieldMapBasedTag<FieldType, Compare>::removeAllFields()
/*! /*!
* \brief Returns the fields of the tag by providing direct access to the field map of the tag. * \brief Returns the fields of the tag by providing direct access to the field map of the tag.
*/ */
template <class FieldType, class Compare> template <class ImplementationType>
inline const std::multimap<typename FieldType::identifierType, FieldType, Compare> &FieldMapBasedTag<FieldType, Compare>::fields() const inline auto FieldMapBasedTag<ImplementationType>::fields() const -> const std::multimap<IdentifierType, FieldType, Compare> &
{ {
return m_fields; return m_fields;
} }
@ -220,14 +292,14 @@ inline const std::multimap<typename FieldType::identifierType, FieldType, Compar
/*! /*!
* \brief Returns the fields of the tag by providing direct access to the field map of the tag. * \brief Returns the fields of the tag by providing direct access to the field map of the tag.
*/ */
template <class FieldType, class Compare> template <class ImplementationType>
inline std::multimap<typename FieldType::identifierType, FieldType, Compare> &FieldMapBasedTag<FieldType, Compare>::fields() inline auto FieldMapBasedTag<ImplementationType>::fields() -> std::multimap<IdentifierType, FieldType, Compare> &
{ {
return m_fields; return m_fields;
} }
template <class FieldType, class Compare> template <class ImplementationType>
unsigned int FieldMapBasedTag<FieldType, Compare>::fieldCount() const unsigned int FieldMapBasedTag<ImplementationType>::fieldCount() const
{ {
unsigned int count = 0; unsigned int count = 0;
for(const auto &field : m_fields) { for(const auto &field : m_fields) {
@ -238,20 +310,50 @@ unsigned int FieldMapBasedTag<FieldType, Compare>::fieldCount() const
return count; return count;
} }
template <class FieldType, class Compare> /*!
inline bool FieldMapBasedTag<FieldType, Compare>::supportsField(KnownField field) const * \brief Returns the field ID for the specified \a value.
* \remarks Must be implemented in internallyGetFieldId() when creating subclass.
*/
template<class ImplementationType>
inline typename FieldMapBasedTag<ImplementationType>::IdentifierType FieldMapBasedTag<ImplementationType>::fieldId(KnownField value) const
{ {
static typename FieldType::identifierType def; return static_cast<const ImplementationType *>(this)->internallyGetFieldId(value);
}
/*!
* \brief Returns the KnownField for the specified \a id.
* \remarks Must be implemented in internallyGetKnownField() when creating subclass.
*/
template<class ImplementationType>
inline KnownField FieldMapBasedTag<ImplementationType>::knownField(const IdentifierType &id) const
{
return static_cast<FieldMapBasedTag<ImplementationType> *>(this)->internallyGetKnownField(id);
}
template <class ImplementationType>
inline bool FieldMapBasedTag<ImplementationType>::supportsField(KnownField field) const
{
static IdentifierType def;
return fieldId(field) != def; return fieldId(field) != def;
} }
/*!
* \brief Default implementation for proposedDataType().
* \remarks Shadow in subclass to provide custom implementation.
*/
template<class ImplementationType>
inline TagDataType FieldMapBasedTag<ImplementationType>::internallyGetProposedDataType(const IdentifierType &id) const
{
return Tag::proposedDataType(knownField(id));
}
/*! /*!
* \brief Returns the proposed data type for the field with the specified \a id. * \brief Returns the proposed data type for the field with the specified \a id.
*/ */
template <class FieldType, class Compare> template <class ImplementationType>
inline TagDataType FieldMapBasedTag<FieldType, Compare>::proposedDataType(const typename FieldType::identifierType &id) const inline TagDataType FieldMapBasedTag<ImplementationType>::proposedDataType(const IdentifierType &id) const
{ {
return Tag::proposedDataType(knownField(id)); return static_cast<ImplementationType *>(this)->determineProposedDataType(id);
} }
/*! /*!
@ -260,8 +362,8 @@ inline TagDataType FieldMapBasedTag<FieldType, Compare>::proposedDataType(const
* \param overwrite Indicates whether existing fields should be overwritten. * \param overwrite Indicates whether existing fields should be overwritten.
* \return Returns the number of fields that have been inserted. * \return Returns the number of fields that have been inserted.
*/ */
template <class FieldType, class Compare> template <class ImplementationType>
int FieldMapBasedTag<FieldType, Compare>::insertFields(const FieldMapBasedTag<FieldType, Compare> &from, bool overwrite) int FieldMapBasedTag<ImplementationType>::insertFields(const FieldMapBasedTag<ImplementationType> &from, bool overwrite)
{ {
int fieldsInserted = 0; int fieldsInserted = 0;
for(const auto &pair : from.fields()) { for(const auto &pair : from.fields()) {
@ -291,19 +393,19 @@ int FieldMapBasedTag<FieldType, Compare>::insertFields(const FieldMapBasedTag<Fi
return fieldsInserted; return fieldsInserted;
} }
template <class FieldType, class Compare> template <class ImplementationType>
unsigned int FieldMapBasedTag<FieldType, Compare>::insertValues(const Tag &from, bool overwrite) unsigned int FieldMapBasedTag<ImplementationType>::insertValues(const Tag &from, bool overwrite)
{ {
if(type() == from.type()) { if(type() == from.type()) {
// the tags are of the same type, we can insert the fields directly // the tags are of the same type, we can insert the fields directly
return insertFields(static_cast<const FieldMapBasedTag<FieldType, Compare> &>(from), overwrite); return insertFields(static_cast<const FieldMapBasedTag<ImplementationType> &>(from), overwrite);
} else { } else {
return Tag::insertValues(from, overwrite); return Tag::insertValues(from, overwrite);
} }
} }
template <class FieldType, class Compare> template <class ImplementationType>
void FieldMapBasedTag<FieldType, Compare>::ensureTextValuesAreProperlyEncoded() void FieldMapBasedTag<ImplementationType>::ensureTextValuesAreProperlyEncoded()
{ {
for(auto &field : fields()) { for(auto &field : fields()) {
field.second.value().convertDataEncodingForTag(this); field.second.value().convertDataEncodingForTag(this);

View File

@ -52,10 +52,10 @@ public:
void removeAllTracks(); void removeAllTracks();
void reset(); void reset();
typedef FileInfoType fileInfoType; typedef FileInfoType ContainerFileInfoType;
typedef TagType tagType; typedef TagType ContainerTagType;
typedef TrackType trackType; typedef TrackType ContainerTrackType;
typedef ElementType elementType; typedef ElementType ContainerElementType;
protected: protected:
std::unique_ptr<ElementType> m_firstElement; std::unique_ptr<ElementType> m_firstElement;

View File

@ -108,8 +108,9 @@ class FileElementTraits
* \class Media::GenericFileElement * \class Media::GenericFileElement
* \brief The GenericFileElement class helps to parse binary files which consist * \brief The GenericFileElement class helps to parse binary files which consist
* of an arboreal element strucutre. * of an arboreal element strucutre.
*
* \tparam ImplementationType Specifies the type of the actual implementation. * \tparam ImplementationType Specifies the type of the actual implementation.
* \remarks This template class is intended to be subclassed using
* with the "Curiously recurring template pattern".
*/ */
template <class ImplementationType> template <class ImplementationType>
class TAG_PARSER_EXPORT GenericFileElement : public StatusProvider class TAG_PARSER_EXPORT GenericFileElement : public StatusProvider
@ -120,61 +121,61 @@ public:
/*! /*!
* \brief Specifies the type of the corresponding container. * \brief Specifies the type of the corresponding container.
*/ */
typedef typename FileElementTraits<ImplementationType>::containerType containerType; typedef typename FileElementTraits<ImplementationType>::ContainerType ContainerType;
/*! /*!
* \brief Specifies the type used to store identifiers. * \brief Specifies the type used to store identifiers.
*/ */
typedef typename FileElementTraits<ImplementationType>::identifierType identifierType; typedef typename FileElementTraits<ImplementationType>::IdentifierType IdentifierType;
/*! /*!
* \brief Specifies the type used to store data sizes. * \brief Specifies the type used to store data sizes.
*/ */
typedef typename FileElementTraits<ImplementationType>::dataSizeType dataSizeType; typedef typename FileElementTraits<ImplementationType>::DataSizeType DataSizeType;
/*! GenericFileElement(ContainerType &container, uint64 startOffset);
* \brief Specifies the type of the actual implementation. GenericFileElement(ImplementationType &parent, uint64 startOffset);
*/ GenericFileElement(ContainerType &container, uint64 startOffset, uint64 maxSize);
typedef typename FileElementTraits<ImplementationType>::implementationType implementationType;
GenericFileElement(containerType &container, uint64 startOffset);
GenericFileElement(implementationType &parent, uint64 startOffset);
GenericFileElement(containerType &container, uint64 startOffset, uint64 maxSize);
GenericFileElement(const GenericFileElement& other) = delete; GenericFileElement(const GenericFileElement& other) = delete;
GenericFileElement(GenericFileElement& other) = delete; GenericFileElement(GenericFileElement& other) = delete;
GenericFileElement& operator =(const GenericFileElement& other) = delete; GenericFileElement& operator =(const GenericFileElement& other) = delete;
containerType& container(); ContainerType& container();
const containerType& container() const; const ContainerType& container() const;
std::iostream &stream(); std::iostream &stream();
IoUtilities::BinaryReader &reader(); IoUtilities::BinaryReader &reader();
IoUtilities::BinaryWriter &writer(); IoUtilities::BinaryWriter &writer();
uint64 startOffset() const; uint64 startOffset() const;
uint64 relativeStartOffset() const; uint64 relativeStartOffset() const;
const identifierType &id() const; const IdentifierType &id() const;
std::string idToString() const; std::string idToString() const;
uint32 idLength() const; uint32 idLength() const;
uint32 headerSize() const; uint32 headerSize() const;
dataSizeType dataSize() const; DataSizeType dataSize() const;
uint32 sizeLength() const; uint32 sizeLength() const;
uint64 dataOffset() const; uint64 dataOffset() const;
uint64 totalSize() const; uint64 totalSize() const;
uint64 endOffset() const; uint64 endOffset() const;
uint64 maxTotalSize() const; uint64 maxTotalSize() const;
implementationType* parent(); byte level() const;
const implementationType* parent() const; ImplementationType* parent();
implementationType* nextSibling(); const ImplementationType* parent() const;
const implementationType* nextSibling() const; ImplementationType* parent(byte n);
implementationType* firstChild(); const ImplementationType* parent(byte n) const;
const implementationType* firstChild() const; ImplementationType* nextSibling();
implementationType* subelementByPath(const std::initializer_list<identifierType> &path); const ImplementationType* nextSibling() const;
implementationType* subelementByPath(std::list<identifierType> &path); ImplementationType* firstChild();
implementationType* childById(const identifierType &id); const ImplementationType* firstChild() const;
implementationType* siblingById(const identifierType &id, bool includeThis = false); ImplementationType* lastChild();
FileElementIterator<implementationType> begin(); const ImplementationType* lastChild() const;
FileElementIterator<implementationType> end(); ImplementationType* subelementByPath(const std::initializer_list<IdentifierType> &path);
const FileElementIterator<implementationType> begin() const; ImplementationType* subelementByPath(std::list<IdentifierType> &path);
const FileElementIterator<implementationType> end() const; ImplementationType* childById(const IdentifierType &id);
ImplementationType* siblingById(const IdentifierType &id, bool includeThis = false);
FileElementIterator<ImplementationType> begin();
FileElementIterator<ImplementationType> end();
const FileElementIterator<ImplementationType> begin() const;
const FileElementIterator<ImplementationType> end() const;
bool isParent() const; bool isParent() const;
bool isPadding() const; bool isPadding() const;
uint64 firstChildOffset() const; uint64 firstChildOffset() const;
@ -194,25 +195,27 @@ public:
void copyBuffer(std::ostream &targetStream); void copyBuffer(std::ostream &targetStream);
void copyPreferablyFromBuffer(std::ostream &targetStream); void copyPreferablyFromBuffer(std::ostream &targetStream);
const std::unique_ptr<char[]> &buffer(); const std::unique_ptr<char[]> &buffer();
implementationType *denoteFirstChild(uint32 offset); ImplementationType *denoteFirstChild(uint32 offset);
protected: protected:
identifierType m_id; IdentifierType m_id;
uint64 m_startOffset; uint64 m_startOffset;
uint64 m_maxSize; uint64 m_maxSize;
uint32 m_idLength; uint32 m_idLength;
dataSizeType m_dataSize; DataSizeType m_dataSize;
uint32 m_sizeLength; uint32 m_sizeLength;
implementationType* m_parent; ImplementationType* m_parent;
std::unique_ptr<implementationType> m_nextSibling; std::unique_ptr<ImplementationType> m_nextSibling;
std::unique_ptr<implementationType> m_firstChild; std::unique_ptr<ImplementationType> m_firstChild;
std::unique_ptr<char[]> m_buffer; std::unique_ptr<char[]> m_buffer;
private: private:
void copyInternal(std::ostream &targetStream, uint64 startOffset, uint64 bytesToCopy); void copyInternal(std::ostream &targetStream, uint64 startOffset, uint64 bytesToCopy);
containerType* m_container; ContainerType* m_container;
bool m_parsed; bool m_parsed;
protected:
bool m_sizeUnknown;
}; };
/*! /*!
@ -220,15 +223,16 @@ private:
* \remarks The available size is obtained using the stream of the \a container. * \remarks The available size is obtained using the stream of the \a container.
*/ */
template <class ImplementationType> template <class ImplementationType>
GenericFileElement<ImplementationType>::GenericFileElement(GenericFileElement<ImplementationType>::containerType &container, uint64 startOffset) : GenericFileElement<ImplementationType>::GenericFileElement(GenericFileElement<ImplementationType>::ContainerType &container, uint64 startOffset) :
m_id(identifierType()), m_id(IdentifierType()),
m_startOffset(startOffset), m_startOffset(startOffset),
m_idLength(0), m_idLength(0),
m_dataSize(0), m_dataSize(0),
m_sizeLength(0), m_sizeLength(0),
m_parent(nullptr), m_parent(nullptr),
m_container(&container), m_container(&container),
m_parsed(false) m_parsed(false),
m_sizeUnknown(false)
{ {
m_maxSize = container.fileInfo().size(); m_maxSize = container.fileInfo().size();
if(m_maxSize > startOffset) { if(m_maxSize > startOffset) {
@ -243,8 +247,8 @@ GenericFileElement<ImplementationType>::GenericFileElement(GenericFileElement<Im
* \brief Constructs a new sub level file element with the specified \a parent at the specified \a startOffset. * \brief Constructs a new sub level file element with the specified \a parent at the specified \a startOffset.
*/ */
template <class ImplementationType> template <class ImplementationType>
GenericFileElement<ImplementationType>::GenericFileElement(GenericFileElement<ImplementationType>::implementationType &parent, uint64 startOffset) : GenericFileElement<ImplementationType>::GenericFileElement(ImplementationType &parent, uint64 startOffset) :
m_id(identifierType()), m_id(IdentifierType()),
m_startOffset(startOffset), m_startOffset(startOffset),
m_maxSize(parent.startOffset() + parent.totalSize() - startOffset), m_maxSize(parent.startOffset() + parent.totalSize() - startOffset),
m_idLength(0), m_idLength(0),
@ -252,15 +256,16 @@ GenericFileElement<ImplementationType>::GenericFileElement(GenericFileElement<Im
m_sizeLength(0), m_sizeLength(0),
m_parent(&parent), m_parent(&parent),
m_container(&parent.container()), m_container(&parent.container()),
m_parsed(false) m_parsed(false),
m_sizeUnknown(false)
{} {}
/*! /*!
* \brief Constructs a new sub level file element with the specified \a container, \a startOffset and \a maxSize. * \brief Constructs a new sub level file element with the specified \a container, \a startOffset and \a maxSize.
*/ */
template <class ImplementationType> template <class ImplementationType>
GenericFileElement<ImplementationType>::GenericFileElement(GenericFileElement<ImplementationType>::containerType &container, uint64 startOffset, uint64 maxSize) : GenericFileElement<ImplementationType>::GenericFileElement(GenericFileElement<ImplementationType>::ContainerType &container, uint64 startOffset, uint64 maxSize) :
m_id(identifierType()), m_id(IdentifierType()),
m_startOffset(startOffset), m_startOffset(startOffset),
m_maxSize(maxSize), m_maxSize(maxSize),
m_idLength(0), m_idLength(0),
@ -268,14 +273,15 @@ GenericFileElement<ImplementationType>::GenericFileElement(GenericFileElement<Im
m_sizeLength(0), m_sizeLength(0),
m_parent(nullptr), m_parent(nullptr),
m_container(&container), m_container(&container),
m_parsed(false) m_parsed(false),
m_sizeUnknown(false)
{} {}
/*! /*!
* \brief Returns the related container. * \brief Returns the related container.
*/ */
template <class ImplementationType> template <class ImplementationType>
inline typename GenericFileElement<ImplementationType>::containerType& GenericFileElement<ImplementationType>::container() inline typename GenericFileElement<ImplementationType>::ContainerType& GenericFileElement<ImplementationType>::container()
{ {
return *m_container; return *m_container;
} }
@ -284,7 +290,7 @@ inline typename GenericFileElement<ImplementationType>::containerType& GenericFi
* \brief Returns the related container. * \brief Returns the related container.
*/ */
template <class ImplementationType> template <class ImplementationType>
inline const typename GenericFileElement<ImplementationType>::containerType &GenericFileElement<ImplementationType>::container() const inline const typename GenericFileElement<ImplementationType>::ContainerType &GenericFileElement<ImplementationType>::container() const
{ {
return *m_container; return *m_container;
} }
@ -338,7 +344,7 @@ inline uint64 GenericFileElement<ImplementationType>::relativeStartOffset() cons
* \brief Returns the element ID. * \brief Returns the element ID.
*/ */
template <class ImplementationType> template <class ImplementationType>
inline const typename GenericFileElement<ImplementationType>::identifierType &GenericFileElement<ImplementationType>::id() const inline const typename GenericFileElement<ImplementationType>::IdentifierType &GenericFileElement<ImplementationType>::id() const
{ {
return m_id; return m_id;
} }
@ -378,7 +384,7 @@ inline uint32 GenericFileElement<ImplementationType>::headerSize() const
* This is the size of the element excluding the header. * This is the size of the element excluding the header.
*/ */
template <class ImplementationType> template <class ImplementationType>
inline typename GenericFileElement<ImplementationType>::dataSizeType GenericFileElement<ImplementationType>::dataSize() const inline typename GenericFileElement<ImplementationType>::DataSizeType GenericFileElement<ImplementationType>::dataSize() const
{ {
return m_dataSize; return m_dataSize;
} }
@ -435,6 +441,18 @@ inline uint64 GenericFileElement<ImplementationType>::maxTotalSize() const
return m_maxSize; return m_maxSize;
} }
/*!
* \brief Returns how deep the element is nested (0 for top-level elements, 1 for children of
* top-level elements, ...).
*/
template<class ImplementationType>
byte GenericFileElement<ImplementationType>::level() const
{
byte level = 0;
for(const ImplementationType *parent = m_parent; parent; ++level, parent = parent->m_parent);
return level;
}
/*! /*!
* \brief Returns the parent of the element. * \brief Returns the parent of the element.
* *
@ -442,7 +460,7 @@ inline uint64 GenericFileElement<ImplementationType>::maxTotalSize() const
* If the current element is a top level element nullptr is returned. * If the current element is a top level element nullptr is returned.
*/ */
template <class ImplementationType> template <class ImplementationType>
inline typename GenericFileElement<ImplementationType>::implementationType *GenericFileElement<ImplementationType>::parent() inline ImplementationType *GenericFileElement<ImplementationType>::parent()
{ {
return m_parent; return m_parent;
} }
@ -454,11 +472,37 @@ inline typename GenericFileElement<ImplementationType>::implementationType *Gene
* If the current element is a top level element nullptr is returned. * If the current element is a top level element nullptr is returned.
*/ */
template <class ImplementationType> template <class ImplementationType>
inline const typename GenericFileElement<ImplementationType>::implementationType *GenericFileElement<ImplementationType>::parent() const inline const ImplementationType *GenericFileElement<ImplementationType>::parent() const
{ {
return m_parent; return m_parent;
} }
/*!
* \brief Returns the n-th parent of the element.
* \remarks
* - The returned element has ownership (at least indirect) over the current instance.
* - Returns nullptr if level() < \a n.
*/
template <class ImplementationType>
ImplementationType *GenericFileElement<ImplementationType>::parent(byte n)
{
ImplementationType *parent = static_cast<ImplementationType *>(this);
for(; n && parent; --n, parent = parent->m_parent);
return parent;
}
/*!
* \brief Returns the n-th parent of the element.
* \remarks
* - The returned element has ownership (at least indirect) over the current instance.
* - Returns nullptr if level() < \a n.
*/
template <class ImplementationType>
inline const ImplementationType *GenericFileElement<ImplementationType>::parent(byte n) const
{
return const_cast<GenericFileElement<ImplementationType> *>(this)->parent(n);
}
/*! /*!
* \brief Returns the next sibling of the element. * \brief Returns the next sibling of the element.
* *
@ -468,7 +512,7 @@ inline const typename GenericFileElement<ImplementationType>::implementationType
* \remarks parse() needs to be called before. * \remarks parse() needs to be called before.
*/ */
template <class ImplementationType> template <class ImplementationType>
inline typename GenericFileElement<ImplementationType>::implementationType *GenericFileElement<ImplementationType>::nextSibling() inline ImplementationType *GenericFileElement<ImplementationType>::nextSibling()
{ {
return m_nextSibling.get(); return m_nextSibling.get();
} }
@ -482,7 +526,7 @@ inline typename GenericFileElement<ImplementationType>::implementationType *Gene
* \remarks parse() needs to be called before. * \remarks parse() needs to be called before.
*/ */
template <class ImplementationType> template <class ImplementationType>
inline const typename GenericFileElement<ImplementationType>::implementationType *GenericFileElement<ImplementationType>::nextSibling() const inline const ImplementationType *GenericFileElement<ImplementationType>::nextSibling() const
{ {
return m_nextSibling.get(); return m_nextSibling.get();
} }
@ -496,7 +540,7 @@ inline const typename GenericFileElement<ImplementationType>::implementationType
* \remarks parse() needs to be called before. * \remarks parse() needs to be called before.
*/ */
template <class ImplementationType> template <class ImplementationType>
inline typename GenericFileElement<ImplementationType>::implementationType *GenericFileElement<ImplementationType>::firstChild() inline ImplementationType *GenericFileElement<ImplementationType>::firstChild()
{ {
return m_firstChild.get(); return m_firstChild.get();
} }
@ -510,11 +554,44 @@ inline typename GenericFileElement<ImplementationType>::implementationType *Gene
* \remarks parse() needs to be called before. * \remarks parse() needs to be called before.
*/ */
template <class ImplementationType> template <class ImplementationType>
inline const typename GenericFileElement<ImplementationType>::implementationType *GenericFileElement<ImplementationType>::firstChild() const inline const ImplementationType *GenericFileElement<ImplementationType>::firstChild() const
{ {
return m_firstChild.get(); return m_firstChild.get();
} }
/*!
* \brief Returns the last child of the element.
*
* The current element keeps ownership over the returned element.
* If no childs are present nullptr is returned.
*
* \remarks parse() needs to be called before.
*/
template <class ImplementationType>
inline ImplementationType *GenericFileElement<ImplementationType>::lastChild()
{
for(ImplementationType *child = firstChild(); child; child = child->nextSibling()) {
if(!child->m_nextSibling) {
return child;
}
}
return nullptr;
}
/*!
* \brief Returns the last child of the element.
*
* The current element keeps ownership over the returned element.
* If no childs are present nullptr is returned.
*
* \remarks parse() needs to be called before.
*/
template <class ImplementationType>
inline const ImplementationType *GenericFileElement<ImplementationType>::lastChild() const
{
return const_cast<GenericFileElement<ImplementationType> *>(this)->lastChild();
}
/*! /*!
* \brief Returns the sub element for the specified \a path. * \brief Returns the sub element for the specified \a path.
* *
@ -525,9 +602,9 @@ inline const typename GenericFileElement<ImplementationType>::implementationType
* \throws Throws std::ios_base::failure when an IO error occurs. * \throws Throws std::ios_base::failure when an IO error occurs.
*/ */
template <class ImplementationType> template <class ImplementationType>
inline typename GenericFileElement<ImplementationType>::implementationType *GenericFileElement<ImplementationType>::subelementByPath(const std::initializer_list<identifierType> &path) inline ImplementationType *GenericFileElement<ImplementationType>::subelementByPath(const std::initializer_list<IdentifierType> &path)
{ {
std::list<GenericFileElement<ImplementationType>::identifierType> list(path); std::list<GenericFileElement<ImplementationType>::IdentifierType> list(path);
return subelementByPath(list); return subelementByPath(list);
} }
@ -542,13 +619,13 @@ inline typename GenericFileElement<ImplementationType>::implementationType *Gene
* \throws Throws std::ios_base::failure when an IO error occurs. * \throws Throws std::ios_base::failure when an IO error occurs.
*/ */
template <class ImplementationType> template <class ImplementationType>
typename GenericFileElement<ImplementationType>::implementationType *GenericFileElement<ImplementationType>::subelementByPath(std::list<GenericFileElement<ImplementationType>::identifierType> &path) ImplementationType *GenericFileElement<ImplementationType>::subelementByPath(std::list<IdentifierType> &path)
{ {
parse(); // ensure element is parsed parse(); // ensure element is parsed
if(path.size()) { if(path.size()) {
if(path.front() == id()) { if(path.front() == id()) {
if(path.size() == 1) { if(path.size() == 1) {
return static_cast<implementationType*>(this); return static_cast<ImplementationType *>(this);
} else { } else {
if(firstChild()) { if(firstChild()) {
path.pop_front(); path.pop_front();
@ -574,10 +651,10 @@ typename GenericFileElement<ImplementationType>::implementationType *GenericFile
* \throws Throws std::ios_base::failure when an IO error occurs. * \throws Throws std::ios_base::failure when an IO error occurs.
*/ */
template <class ImplementationType> template <class ImplementationType>
typename GenericFileElement<ImplementationType>::implementationType *GenericFileElement<ImplementationType>::childById(const GenericFileElement<ImplementationType>::identifierType &id) ImplementationType *GenericFileElement<ImplementationType>::childById(const IdentifierType &id)
{ {
parse(); // ensure element is parsed parse(); // ensure element is parsed
for(implementationType *child = firstChild(); child; child = child->nextSibling()) { for(ImplementationType *child = firstChild(); child; child = child->nextSibling()) {
child->parse(); child->parse();
if(child->id() == id) { if(child->id() == id) {
return child; return child;
@ -601,10 +678,10 @@ typename GenericFileElement<ImplementationType>::implementationType *GenericFile
* \throws Throws std::ios_base::failure when an IO error occurs. * \throws Throws std::ios_base::failure when an IO error occurs.
*/ */
template <class ImplementationType> template <class ImplementationType>
typename GenericFileElement<ImplementationType>::implementationType *GenericFileElement<ImplementationType>::siblingById(const GenericFileElement<ImplementationType>::identifierType &id, bool includeThis) ImplementationType *GenericFileElement<ImplementationType>::siblingById(const IdentifierType &id, bool includeThis)
{ {
parse(); // ensure element is parsed parse(); // ensure element is parsed
for(implementationType *sibling = includeThis ? static_cast<implementationType*>(this) : nextSibling(); sibling; sibling = sibling->nextSibling()) { for(ImplementationType *sibling = includeThis ? static_cast<ImplementationType *>(this) : nextSibling(); sibling; sibling = sibling->nextSibling()) {
sibling->parse(); sibling->parse();
if(sibling->id() == id) { if(sibling->id() == id) {
return sibling; return sibling;
@ -617,25 +694,25 @@ typename GenericFileElement<ImplementationType>::implementationType *GenericFile
* \brief Returns an iterator for iterating over the element's childs. * \brief Returns an iterator for iterating over the element's childs.
*/ */
template <class ImplementationType> template <class ImplementationType>
FileElementIterator<typename GenericFileElement<ImplementationType>::implementationType> GenericFileElement<ImplementationType>::begin() FileElementIterator<ImplementationType> GenericFileElement<ImplementationType>::begin()
{ {
return FileElementIterator<implementationType>(firstChild()); return FileElementIterator<ImplementationType>(firstChild());
} }
/*! /*!
* \brief Returns an iterator for iterating over the element's childs (constant). * \brief Returns an iterator for iterating over the element's childs (constant).
*/ */
template <class ImplementationType> template <class ImplementationType>
const FileElementIterator<typename GenericFileElement<ImplementationType>::implementationType> GenericFileElement<ImplementationType>::begin() const const FileElementIterator<ImplementationType> GenericFileElement<ImplementationType>::begin() const
{ {
return FileElementIterator<implementationType>(firstChild()); return FileElementIterator<ImplementationType>(firstChild());
} }
/*! /*!
* \brief Returns an invalid iterator. * \brief Returns an invalid iterator.
*/ */
template <class ImplementationType> template <class ImplementationType>
FileElementIterator<typename GenericFileElement<ImplementationType>::implementationType> GenericFileElement<ImplementationType>::end() FileElementIterator<ImplementationType> GenericFileElement<ImplementationType>::end()
{ {
return FileElementIterator<ImplementationType>(); return FileElementIterator<ImplementationType>();
} }
@ -644,7 +721,7 @@ FileElementIterator<typename GenericFileElement<ImplementationType>::implementat
* \brief Returns an invalid iterator. * \brief Returns an invalid iterator.
*/ */
template <class ImplementationType> template <class ImplementationType>
const FileElementIterator<typename GenericFileElement<ImplementationType>::implementationType> GenericFileElement<ImplementationType>::end() const const FileElementIterator<ImplementationType> GenericFileElement<ImplementationType>::end() const
{ {
return FileElementIterator<ImplementationType>(); return FileElementIterator<ImplementationType>();
} }
@ -694,7 +771,7 @@ inline bool GenericFileElement<ImplementationType>::isParsed() const
template <class ImplementationType> template <class ImplementationType>
void GenericFileElement<ImplementationType>::clear() void GenericFileElement<ImplementationType>::clear()
{ {
m_id = identifierType(); m_id = IdentifierType();
//m_startOffset = 0; //m_startOffset = 0;
m_idLength = 0; m_idLength = 0;
m_dataSize = 0; m_dataSize = 0;
@ -905,10 +982,10 @@ void GenericFileElement<ImplementationType>::copyInternal(std::ostream &targetSt
* \remarks A new first child is constructed. A possibly existing subtree is invalidated. * \remarks A new first child is constructed. A possibly existing subtree is invalidated.
*/ */
template <class ImplementationType> template <class ImplementationType>
typename GenericFileElement<ImplementationType>::implementationType *GenericFileElement<ImplementationType>::denoteFirstChild(uint32 relativeFirstChildOffset) ImplementationType *GenericFileElement<ImplementationType>::denoteFirstChild(uint32 relativeFirstChildOffset)
{ {
if(relativeFirstChildOffset + minimumElementSize() <= totalSize()) { if(relativeFirstChildOffset + minimumElementSize() <= totalSize()) {
m_firstChild.reset(new implementationType(static_cast<implementationType &>(*this), startOffset() + relativeFirstChildOffset)); m_firstChild.reset(new ImplementationType(static_cast<ImplementationType &>(*this), startOffset() + relativeFirstChildOffset));
} else { } else {
m_firstChild.reset(); m_firstChild.reset();
} }
@ -921,7 +998,7 @@ typename GenericFileElement<ImplementationType>::implementationType *GenericFile
template <class ImplementationType> template <class ImplementationType>
constexpr uint32 GenericFileElement<ImplementationType>::maximumIdLengthSupported() constexpr uint32 GenericFileElement<ImplementationType>::maximumIdLengthSupported()
{ {
return sizeof(identifierType); return sizeof(IdentifierType);
} }
/*! /*!
@ -930,7 +1007,7 @@ constexpr uint32 GenericFileElement<ImplementationType>::maximumIdLengthSupporte
template <class ImplementationType> template <class ImplementationType>
constexpr uint32 GenericFileElement<ImplementationType>::maximumSizeLengthSupported() constexpr uint32 GenericFileElement<ImplementationType>::maximumSizeLengthSupported()
{ {
return sizeof(dataSizeType); return sizeof(DataSizeType);
} }
/*! /*!

View File

@ -12,7 +12,7 @@ class TagField;
* \class Media::TagFieldTraits * \class Media::TagFieldTraits
* \brief Defines traits for the specified \a ImplementationType. * \brief Defines traits for the specified \a ImplementationType.
* *
* A template specialization for each TagField derivat must be provided. * A template specialization for each TagField subclass must be provided.
*/ */
template<typename ImplementationType> template<typename ImplementationType>
class TagFieldTraits class TagFieldTraits
@ -25,25 +25,26 @@ class TagFieldTraits
* might be assigned as well. The usage of the type info depends on the * might be assigned as well. The usage of the type info depends on the
* particular tag implementation. * particular tag implementation.
* *
* \remarks This template class is intended to be used * \tparam ImplementationType Specifies the type of the actual implementation.
* \remarks This template class is intended to be subclassed using
* with the "Curiously recurring template pattern". * with the "Curiously recurring template pattern".
*/ */
template <class ImplementationType> template <class ImplementationType>
class TAG_PARSER_EXPORT TagField class TAG_PARSER_EXPORT TagField
{ {
public:
friend class TagFieldTraits<ImplementationType>; friend class TagFieldTraits<ImplementationType>;
typedef typename TagFieldTraits<ImplementationType>::implementationType implementationType;
typedef typename TagFieldTraits<ImplementationType>::identifierType identifierType; public:
typedef typename TagFieldTraits<ImplementationType>::typeInfoType typeInfoType; typedef typename TagFieldTraits<ImplementationType>::IdentifierType IdentifierType;
typedef typename TagFieldTraits<ImplementationType>::TypeInfoType TypeInfoType;
TagField(); TagField();
TagField(const identifierType &id, const TagValue &value); TagField(const IdentifierType &id, const TagValue &value);
~TagField(); ~TagField();
const identifierType &id() const; const IdentifierType &id() const;
std::string idToString() const; std::string idToString() const;
void setId(const identifierType &id); void setId(const IdentifierType &id);
void clearId(); void clearId();
TagValue &value(); TagValue &value();
@ -51,8 +52,8 @@ public:
void setValue(const TagValue &value); void setValue(const TagValue &value);
void clearValue(); void clearValue();
const typeInfoType &typeInfo() const; const TypeInfoType &typeInfo() const;
void setTypeInfo(const typeInfoType &typeInfo); void setTypeInfo(const TypeInfoType &typeInfo);
void removeTypeInfo(); void removeTypeInfo();
bool isTypeInfoAssigned() const; bool isTypeInfoAssigned() const;
@ -71,9 +72,9 @@ private:
void cleared(); void cleared();
private: private:
identifierType m_id; IdentifierType m_id;
TagValue m_value; TagValue m_value;
typeInfoType m_typeInfo; TypeInfoType m_typeInfo;
bool m_typeInfoAssigned; bool m_typeInfoAssigned;
bool m_default; bool m_default;
std::vector<ImplementationType> m_nestedFields; std::vector<ImplementationType> m_nestedFields;
@ -84,9 +85,9 @@ private:
*/ */
template <class ImplementationType> template <class ImplementationType>
TagField<ImplementationType>::TagField() : TagField<ImplementationType>::TagField() :
m_id(identifierType()), m_id(IdentifierType()),
m_value(TagValue()), m_value(TagValue()),
m_typeInfo(typeInfoType()), m_typeInfo(TypeInfoType()),
m_typeInfoAssigned(false), m_typeInfoAssigned(false),
m_default(false) m_default(false)
{} {}
@ -95,10 +96,10 @@ TagField<ImplementationType>::TagField() :
* \brief Constructs a new TagField with the specified \a id and \a value. * \brief Constructs a new TagField with the specified \a id and \a value.
*/ */
template <class ImplementationType> template <class ImplementationType>
TagField<ImplementationType>::TagField(const identifierType &id, const TagValue &value) : TagField<ImplementationType>::TagField(const IdentifierType &id, const TagValue &value) :
m_id(id), m_id(id),
m_value(value), m_value(value),
m_typeInfo(typeInfoType()), m_typeInfo(TypeInfoType()),
m_typeInfoAssigned(false), m_typeInfoAssigned(false),
m_default(false) m_default(false)
{} {}
@ -114,7 +115,7 @@ TagField<ImplementationType>::~TagField()
* \brief Returns the id of the current TagField. * \brief Returns the id of the current TagField.
*/ */
template <class ImplementationType> template <class ImplementationType>
inline const typename TagField<ImplementationType>::identifierType &TagField<ImplementationType>::id() const inline const typename TagField<ImplementationType>::IdentifierType &TagField<ImplementationType>::id() const
{ {
return m_id; return m_id;
} }
@ -129,7 +130,7 @@ inline std::string TagField<ImplementationType>::idToString() const
* \brief Sets the id of the current Tag Field. * \brief Sets the id of the current Tag Field.
*/ */
template <class ImplementationType> template <class ImplementationType>
inline void TagField<ImplementationType>::setId(const identifierType &id) inline void TagField<ImplementationType>::setId(const IdentifierType &id)
{ {
m_id = id; m_id = id;
} }
@ -140,7 +141,7 @@ inline void TagField<ImplementationType>::setId(const identifierType &id)
template <class ImplementationType> template <class ImplementationType>
inline void TagField<ImplementationType>::clearId() inline void TagField<ImplementationType>::clearId()
{ {
m_id = identifierType(); m_id = IdentifierType();
} }
/*! /*!
@ -183,7 +184,7 @@ inline void TagField<ImplementationType>::clearValue()
* \brief Returns the type info of the current TagField. * \brief Returns the type info of the current TagField.
*/ */
template <class ImplementationType> template <class ImplementationType>
inline const typename TagField<ImplementationType>::typeInfoType &TagField<ImplementationType>::typeInfo() const inline const typename TagField<ImplementationType>::TypeInfoType &TagField<ImplementationType>::typeInfo() const
{ {
return m_typeInfo; return m_typeInfo;
} }
@ -192,7 +193,7 @@ inline const typename TagField<ImplementationType>::typeInfoType &TagField<Imple
* \brief Sets the type info of the current TagField. * \brief Sets the type info of the current TagField.
*/ */
template <class ImplementationType> template <class ImplementationType>
inline void TagField<ImplementationType>::setTypeInfo(const typeInfoType &typeInfo) inline void TagField<ImplementationType>::setTypeInfo(const TypeInfoType &typeInfo)
{ {
m_typeInfo = typeInfo; m_typeInfo = typeInfo;
m_typeInfoAssigned = true; m_typeInfoAssigned = true;
@ -204,7 +205,7 @@ inline void TagField<ImplementationType>::setTypeInfo(const typeInfoType &typeIn
template <class ImplementationType> template <class ImplementationType>
inline void TagField<ImplementationType>::removeTypeInfo() inline void TagField<ImplementationType>::removeTypeInfo()
{ {
m_typeInfo = typeInfoType(); m_typeInfo = TypeInfoType();
m_typeInfoAssigned = false; m_typeInfoAssigned = false;
} }
@ -243,7 +244,7 @@ void TagField<ImplementationType>::clear()
{ {
clearId(); clearId();
clearValue(); clearValue();
m_typeInfo = typeInfoType(); m_typeInfo = TypeInfoType();
m_typeInfoAssigned = false; m_typeInfoAssigned = false;
m_default = true; m_default = true;
static_cast<ImplementationType *>(this)->cleared(); static_cast<ImplementationType *>(this)->cleared();

View File

@ -30,7 +30,7 @@ TagType Id3v1Tag::type() const
const char *Id3v1Tag::typeName() const const char *Id3v1Tag::typeName() const
{ {
return "ID3v1 tag"; return tagName;
} }
bool Id3v1Tag::canEncodingBeUsed(TagTextEncoding encoding) const bool Id3v1Tag::canEncodingBeUsed(TagTextEncoding encoding) const

View File

@ -12,6 +12,7 @@ public:
Id3v1Tag(); Id3v1Tag();
static constexpr TagType tagType = TagType::Id3v1Tag; static constexpr TagType tagType = TagType::Id3v1Tag;
static constexpr const char *tagName = "ID3v1 tag";
TagType type() const; TagType type() const;
const char *typeName() const; const char *typeName() const;
bool canEncodingBeUsed(TagTextEncoding encoding) const; bool canEncodingBeUsed(TagTextEncoding encoding) const;

View File

@ -50,7 +50,7 @@ Id3v2Frame::Id3v2Frame() :
/*! /*!
* \brief Constructs a new Id3v2Frame with the specified \a id, \a value, \a group and \a flag. * \brief Constructs a new Id3v2Frame with the specified \a id, \a value, \a group and \a flag.
*/ */
Id3v2Frame::Id3v2Frame(const identifierType &id, const TagValue &value, const byte group, const int16 flag) : Id3v2Frame::Id3v2Frame(const IdentifierType &id, const TagValue &value, const byte group, const int16 flag) :
TagField<Id3v2Frame>(id, value), TagField<Id3v2Frame>(id, value),
m_flag(flag), m_flag(flag),
m_group(group), m_group(group),

View File

@ -81,20 +81,8 @@ template <>
class TAG_PARSER_EXPORT TagFieldTraits<Id3v2Frame> class TAG_PARSER_EXPORT TagFieldTraits<Id3v2Frame>
{ {
public: public:
/*! typedef uint32 IdentifierType;
* \brief Fields in an ID3 tag are identified by 32-bit unsigned integers. typedef byte TypeInfoType;
*/
typedef uint32 identifierType;
/*!
* \brief The type info is stored using bytes.
*/
typedef byte typeInfoType;
/*!
* \brief The implementation type is Id3v2Frame.
*/
typedef Id3v2Frame implementationType;
}; };
class TAG_PARSER_EXPORT Id3v2Frame : public TagField<Id3v2Frame>, public StatusProvider class TAG_PARSER_EXPORT Id3v2Frame : public TagField<Id3v2Frame>, public StatusProvider
@ -103,7 +91,7 @@ class TAG_PARSER_EXPORT Id3v2Frame : public TagField<Id3v2Frame>, public StatusP
public: public:
Id3v2Frame(); Id3v2Frame();
Id3v2Frame(const identifierType &id, const TagValue &value, const byte group = 0, const int16 flag = 0); Id3v2Frame(const IdentifierType &id, const TagValue &value, const byte group = 0, const int16 flag = 0);
// parsing/making // parsing/making
void parse(IoUtilities::BinaryReader &reader, const uint32 version, const uint32 maximalSize = 0); void parse(IoUtilities::BinaryReader &reader, const uint32 version, const uint32 maximalSize = 0);
@ -153,8 +141,8 @@ public:
void makeCommentConsideringVersion(std::unique_ptr<char[]> &buffer, uint32 &bufferSize, const TagValue &comment, byte version); void makeCommentConsideringVersion(std::unique_ptr<char[]> &buffer, uint32 &bufferSize, const TagValue &comment, byte version);
void makeComment(std::unique_ptr<char[]> &buffer, uint32 &bufferSize, const TagValue &comment); void makeComment(std::unique_ptr<char[]> &buffer, uint32 &bufferSize, const TagValue &comment);
static identifierType fieldIdFromString(const char *idString, std::size_t idStringSize = std::string::npos); static IdentifierType fieldIdFromString(const char *idString, std::size_t idStringSize = std::string::npos);
static std::string fieldIdToString(identifierType id); static std::string fieldIdToString(IdentifierType id);
protected: protected:
void cleared(); void cleared();
@ -334,7 +322,7 @@ inline bool Id3v2Frame::supportsNestedFields() const
/*! /*!
* \brief Converts the specified ID string representation to an actual ID. * \brief Converts the specified ID string representation to an actual ID.
*/ */
inline Id3v2Frame::identifierType Id3v2Frame::fieldIdFromString(const char *idString, std::size_t idStringSize) inline Id3v2Frame::IdentifierType Id3v2Frame::fieldIdFromString(const char *idString, std::size_t idStringSize)
{ {
switch(idStringSize != std::string::npos ? idStringSize : std::strlen(idString)) { switch(idStringSize != std::string::npos ? idStringSize : std::strlen(idString)) {
case 3: case 3:
@ -349,7 +337,7 @@ inline Id3v2Frame::identifierType Id3v2Frame::fieldIdFromString(const char *idSt
/*! /*!
* \brief Returns the string representation for the specified \a id. * \brief Returns the string representation for the specified \a id.
*/ */
inline std::string Id3v2Frame::fieldIdToString(Id3v2Frame::identifierType id) inline std::string Id3v2Frame::fieldIdToString(Id3v2Frame::IdentifierType id)
{ {
return ConversionUtilities::interpretIntegerAsString<uint32>(id, Id3v2FrameIds::isLongId(id) ? 0 : 1); return ConversionUtilities::interpretIntegerAsString<uint32>(id, Id3v2FrameIds::isLongId(id) ? 0 : 1);
} }

View File

@ -17,7 +17,7 @@ namespace Media {
* \brief Implementation of Media::Tag for ID3v2 tags. * \brief Implementation of Media::Tag for ID3v2 tags.
*/ */
uint32 Id3v2Tag::fieldId(KnownField field) const Id3v2Tag::IdentifierType Id3v2Tag::internallyGetFieldId(KnownField field) const
{ {
using namespace Id3v2FrameIds; using namespace Id3v2FrameIds;
if(m_majorVersion >= 3) { if(m_majorVersion >= 3) {
@ -78,7 +78,7 @@ uint32 Id3v2Tag::fieldId(KnownField field) const
return 0; return 0;
} }
KnownField Id3v2Tag::knownField(const uint32 &id) const KnownField Id3v2Tag::internallyGetKnownField(const IdentifierType &id) const
{ {
using namespace Id3v2FrameIds; using namespace Id3v2FrameIds;
switch(id) { switch(id) {
@ -125,7 +125,7 @@ KnownField Id3v2Tag::knownField(const uint32 &id) const
} }
} }
TagDataType Id3v2Tag::proposedDataType(const uint32 &id) const TagDataType Id3v2Tag::internallyGetProposedDataType(const uint32 &id) const
{ {
using namespace Id3v2FrameIds; using namespace Id3v2FrameIds;
switch(id) { switch(id) {
@ -147,16 +147,6 @@ TagDataType Id3v2Tag::proposedDataType(const uint32 &id) const
} }
} }
const TagValue &Id3v2Tag::value(const typename Id3v2Frame::identifierType &id) const
{
return FieldMapBasedTag<Id3v2Frame, FrameComparer>::value(id);
}
bool Id3v2Tag::setValue(const typename Id3v2Frame::identifierType &id, const TagValue &value)
{
return FieldMapBasedTag<Id3v2Frame, FrameComparer>::setValue(id, value);
}
/*! /*!
* \brief Parses tag information from the specified \a stream. * \brief Parses tag information from the specified \a stream.
* *

View File

@ -52,23 +52,29 @@ inline uint64 Id3v2TagMaker::requiredSize() const
return m_requiredSize; return m_requiredSize;
} }
class TAG_PARSER_EXPORT Id3v2Tag : public FieldMapBasedTag<Id3v2Frame, FrameComparer> /*!
* \brief Defines traits for the TagField implementation of the Id3v2Tag class.
*/
template <>
class TAG_PARSER_EXPORT FieldMapBasedTagTraits<Id3v2Tag>
{ {
public:
typedef Id3v2Frame FieldType;
typedef FrameComparer Compare;
};
class TAG_PARSER_EXPORT Id3v2Tag : public FieldMapBasedTag<Id3v2Tag>
{
friend class FieldMapBasedTag<Id3v2Tag>;
public: public:
Id3v2Tag(); Id3v2Tag();
static constexpr TagType tagType = TagType::Id3v2Tag; static constexpr TagType tagType = TagType::Id3v2Tag;
TagType type() const; static constexpr const char *tagName = "ID3v2 tag";
const char *typeName() const; static constexpr TagTextEncoding defaultTextEncoding = TagTextEncoding::Utf16LittleEndian;
TagTextEncoding proposedTextEncoding() const; TagTextEncoding proposedTextEncoding() const;
bool canEncodingBeUsed(TagTextEncoding encoding) const; bool canEncodingBeUsed(TagTextEncoding encoding) const;
uint32 fieldId(KnownField value) const;
KnownField knownField(const uint32 &id) const;
TagDataType proposedDataType(const uint32 &id) const;
using FieldMapBasedTag<Id3v2Frame, FrameComparer>::value;
const TagValue &value(const typename Id3v2Frame::identifierType &id) const;
using FieldMapBasedTag<Id3v2Frame, FrameComparer>::setValue;
bool setValue(const typename Id3v2Frame::identifierType &id, const TagValue &value);
bool supportsDescription(KnownField field) const; bool supportsDescription(KnownField field) const;
bool supportsMimeType(KnownField field) const; bool supportsMimeType(KnownField field) const;
@ -88,6 +94,11 @@ public:
uint32 extendedHeaderSize() const; uint32 extendedHeaderSize() const;
uint32 paddingSize() const; uint32 paddingSize() const;
protected:
IdentifierType internallyGetFieldId(KnownField field) const;
KnownField internallyGetKnownField(const IdentifierType &id) const;
TagDataType internallyGetProposedDataType(const uint32 &id) const;
private: private:
byte m_majorVersion; byte m_majorVersion;
byte m_revisionVersion; byte m_revisionVersion;
@ -109,16 +120,6 @@ inline Id3v2Tag::Id3v2Tag() :
m_paddingSize(0) m_paddingSize(0)
{} {}
inline TagType Id3v2Tag::type() const
{
return TagType::Id3v2Tag;
}
inline const char *Id3v2Tag::typeName() const
{
return "ID3v2 tag";
}
inline TagTextEncoding Id3v2Tag::proposedTextEncoding() const inline TagTextEncoding Id3v2Tag::proposedTextEncoding() const
{ {
return m_majorVersion > 3 ? TagTextEncoding::Utf8 : TagTextEncoding::Utf16LittleEndian; return m_majorVersion > 3 ? TagTextEncoding::Utf8 : TagTextEncoding::Utf16LittleEndian;

View File

@ -77,15 +77,16 @@ void EbmlElement::internalParse()
throw TruncatedDataException(); throw TruncatedDataException();
} }
stream().seekg(startOffset()); stream().seekg(startOffset());
// read ID // read ID
char buf[maximumIdLengthSupported() > maximumSizeLengthSupported() ? maximumIdLengthSupported() : maximumSizeLengthSupported()] = {0}; char buf[maximumIdLengthSupported() > maximumSizeLengthSupported() ? maximumIdLengthSupported() : maximumSizeLengthSupported()] = {0};
byte beg = static_cast<byte>(stream().peek()), mask = 0x80; byte beg = static_cast<byte>(stream().peek()), mask = 0x80;
m_idLength = 1; m_idLength = 1;
while(m_idLength <= GenericFileElement<implementationType>::maximumIdLengthSupported() && (beg & mask) == 0) { while(m_idLength <= maximumIdLengthSupported() && (beg & mask) == 0) {
++m_idLength; ++m_idLength;
mask >>= 1; mask >>= 1;
} }
if(m_idLength > GenericFileElement<implementationType>::maximumIdLengthSupported()) { if(m_idLength > maximumIdLengthSupported()) {
if(!skipped) { if(!skipped) {
addNotification(NotificationType::Critical, argsToString("EBML ID length at ", startOffset(), " is not supported, trying to skip."), context); addNotification(NotificationType::Critical, argsToString("EBML ID length at ", startOffset(), " is not supported, trying to skip."), context);
} }
@ -97,22 +98,62 @@ void EbmlElement::internalParse()
} }
continue; // try again continue; // try again
} }
reader().read(buf + (GenericFileElement<implementationType>::maximumIdLengthSupported() - m_idLength), m_idLength); reader().read(buf + (maximumIdLengthSupported() - m_idLength), m_idLength);
m_id = BE::toUInt32(buf); m_id = BE::toUInt32(buf);
// check whether this element is actually a sibling of one of its parents rather then a child
// (might be the case if the parent's size is unknown and hence assumed to be the max file size)
if(m_parent && m_parent->m_sizeUnknown) {
// check at which level in the hierarchy the element is supposed to occour using its ID
// (the only chance to find out whether the element belongs higher up in the hierarchy)
const MatroskaElementLevel supposedLevel = matroskaIdLevel(m_id);
const byte actualLevel = level();
if(actualLevel > supposedLevel) {
// the file belongs higher up in the hierarchy so find a better parent
if(EbmlElement *betterParent = m_parent->parent(actualLevel - static_cast<byte>(supposedLevel))) {
// recompute the parent size (assumption - which was rest of the available space - was wrong)
m_parent->m_dataSize = m_startOffset - m_parent->m_startOffset - m_parent->headerSize();
m_parent->m_sizeUnknown = false;
// detatch from ...
if(m_parent->firstChild() == this) {
// ... parent
m_parent->m_firstChild.release();
m_parent->m_firstChild = move(m_nextSibling);
} else {
// ... previous sibling
for(EbmlElement *sibling = m_parent->firstChild(); sibling; sibling = sibling->nextSibling()) {
if(sibling->nextSibling() == this) {
sibling->m_nextSibling.release();
sibling->m_nextSibling = move(m_nextSibling);
break;
}
}
}
// insert as child of better parent
if(EbmlElement *previousSibling = betterParent->lastChild()) {
previousSibling->m_nextSibling.reset(this);
} else {
betterParent->m_firstChild.reset(this);
}
// update own reference to parent
m_parent = betterParent;
}
}
}
// read size // read size
beg = static_cast<byte>(stream().peek()), mask = 0x80; beg = static_cast<byte>(stream().peek()), mask = 0x80;
m_sizeLength = 1; m_sizeLength = 1;
if(beg == 0xFF) { if((m_sizeUnknown = (beg == 0xFF))) {
// this indicates that the element size is unknown // this indicates that the element size is unknown
// -> just assume the element takes the maximum available size // -> just assume the element takes the maximum available size
m_dataSize = maxTotalSize() - headerSize(); m_dataSize = maxTotalSize() - headerSize();
} else { } else {
while(m_sizeLength <= GenericFileElement<implementationType>::maximumSizeLengthSupported() && (beg & mask) == 0) { while(m_sizeLength <= maximumSizeLengthSupported() && (beg & mask) == 0) {
++m_sizeLength; ++m_sizeLength;
mask >>= 1; mask >>= 1;
} }
if(m_sizeLength > GenericFileElement<implementationType>::maximumSizeLengthSupported()) { if(m_sizeLength > maximumSizeLengthSupported()) {
if(!skipped) { if(!skipped) {
addNotification(NotificationType::Critical, "EBML size length is not supported.", parsingContext()); addNotification(NotificationType::Critical, "EBML size length is not supported.", parsingContext());
} }
@ -125,9 +166,10 @@ void EbmlElement::internalParse()
continue; // try again continue; // try again
} }
// read size into buffer // read size into buffer
memset(buf, 0, sizeof(dataSizeType)); // reset buffer memset(buf, 0, sizeof(DataSizeType)); // reset buffer
reader().read(buf + (GenericFileElement<implementationType>::maximumSizeLengthSupported() - m_sizeLength), m_sizeLength); reader().read(buf + (maximumSizeLengthSupported() - m_sizeLength), m_sizeLength);
*(buf + (GenericFileElement<implementationType>::maximumSizeLengthSupported() - m_sizeLength)) ^= mask; // xor the first byte in buffer which has been read from the file with mask // xor the first byte in buffer which has been read from the file with mask
*(buf + (maximumSizeLengthSupported() - m_sizeLength)) ^= mask;
m_dataSize = ConversionUtilities::BE::toUInt64(buf); m_dataSize = ConversionUtilities::BE::toUInt64(buf);
// check if element is truncated // check if element is truncated
if(totalSize() > maxTotalSize()) { if(totalSize() > maxTotalSize()) {
@ -223,7 +265,7 @@ float64 EbmlElement::readFloat()
* \brief Returns the length of the specified \a id in byte. * \brief Returns the length of the specified \a id in byte.
* \throws Throws InvalidDataException() if \a id can not be represented. * \throws Throws InvalidDataException() if \a id can not be represented.
*/ */
byte EbmlElement::calculateIdLength(GenericFileElement::identifierType id) byte EbmlElement::calculateIdLength(GenericFileElement::IdentifierType id)
{ {
if(id <= 0xFF) { if(id <= 0xFF) {
return 1; return 1;
@ -271,7 +313,7 @@ byte EbmlElement::calculateSizeDenotationLength(uint64 size)
* \returns Returns the number of bytes written to \a buff. * \returns Returns the number of bytes written to \a buff.
* \throws Throws InvalidDataException() if \a id can not be represented. * \throws Throws InvalidDataException() if \a id can not be represented.
*/ */
byte EbmlElement::makeId(GenericFileElement::identifierType id, char *buff) byte EbmlElement::makeId(GenericFileElement::IdentifierType id, char *buff)
{ {
if(id <= 0xFF) { if(id <= 0xFF) {
*buff = static_cast<byte>(id); *buff = static_cast<byte>(id);
@ -465,7 +507,7 @@ byte EbmlElement::makeUInteger(uint64 value, char *buff, byte minBytes)
* \param id Specifies the element ID. * \param id Specifies the element ID.
* \param content Specifies the value of the element as unsigned integer. * \param content Specifies the value of the element as unsigned integer.
*/ */
void EbmlElement::makeSimpleElement(ostream &stream, identifierType id, uint64 content) void EbmlElement::makeSimpleElement(ostream &stream, IdentifierType id, uint64 content)
{ {
char buff1[8]; char buff1[8];
char buff2[8]; char buff2[8];
@ -483,7 +525,7 @@ void EbmlElement::makeSimpleElement(ostream &stream, identifierType id, uint64 c
* \param id Specifies the element ID. * \param id Specifies the element ID.
* \param content Specifies the value of the element as string. * \param content Specifies the value of the element as string.
*/ */
void EbmlElement::makeSimpleElement(std::ostream &stream, GenericFileElement::identifierType id, const std::string &content) void EbmlElement::makeSimpleElement(std::ostream &stream, GenericFileElement::IdentifierType id, const std::string &content)
{ {
char buff1[8]; char buff1[8];
byte sizeLength = EbmlElement::makeId(id, buff1); byte sizeLength = EbmlElement::makeId(id, buff1);
@ -500,7 +542,7 @@ void EbmlElement::makeSimpleElement(std::ostream &stream, GenericFileElement::id
* \param data Specifies the data of the element. * \param data Specifies the data of the element.
* \param dataSize Specifies the size of \a data. * \param dataSize Specifies the size of \a data.
*/ */
void EbmlElement::makeSimpleElement(ostream &stream, GenericFileElement::identifierType id, const char *data, std::size_t dataSize) void EbmlElement::makeSimpleElement(ostream &stream, GenericFileElement::IdentifierType id, const char *data, std::size_t dataSize)
{ {
char buff1[8]; char buff1[8];
byte sizeLength = EbmlElement::makeId(id, buff1); byte sizeLength = EbmlElement::makeId(id, buff1);

View File

@ -26,25 +26,9 @@ template <>
class TAG_PARSER_EXPORT FileElementTraits<EbmlElement> class TAG_PARSER_EXPORT FileElementTraits<EbmlElement>
{ {
public: public:
/*! typedef MatroskaContainer ContainerType;
* \brief The container type used to store such elements is MatroskaContainer. typedef uint32 IdentifierType;
*/ typedef uint64 DataSizeType;
typedef MatroskaContainer containerType;
/*!
* \brief The type used to store element IDs is an unsigned 32-bit integer.
*/
typedef uint32 identifierType;
/*!
* \brief The type used to store element sizes is an unsigned 64-bit integer.
*/
typedef uint64 dataSizeType;
/*!
* \brief The implementation type is EbmlElement.
*/
typedef EbmlElement implementationType;
}; };
class TAG_PARSER_EXPORT EbmlElement : public GenericFileElement<EbmlElement> class TAG_PARSER_EXPORT EbmlElement : public GenericFileElement<EbmlElement>
@ -62,17 +46,17 @@ public:
uint64 readUInteger(); uint64 readUInteger();
float64 readFloat(); float64 readFloat();
static byte calculateIdLength(identifierType id); static byte calculateIdLength(IdentifierType id);
static byte calculateSizeDenotationLength(uint64 size); static byte calculateSizeDenotationLength(uint64 size);
static byte makeId(identifierType id, char *buff); static byte makeId(IdentifierType id, char *buff);
static byte makeSizeDenotation(uint64 size, char *buff); static byte makeSizeDenotation(uint64 size, char *buff);
static byte makeSizeDenotation(uint64 size, char *buff, byte minBytes); static byte makeSizeDenotation(uint64 size, char *buff, byte minBytes);
static byte calculateUIntegerLength(uint64 integer); static byte calculateUIntegerLength(uint64 integer);
static byte makeUInteger(uint64 value, char *buff); static byte makeUInteger(uint64 value, char *buff);
static byte makeUInteger(uint64 value, char *buff, byte minBytes); static byte makeUInteger(uint64 value, char *buff, byte minBytes);
static void makeSimpleElement(std::ostream &stream, identifierType id, uint64 content); static void makeSimpleElement(std::ostream &stream, IdentifierType id, uint64 content);
static void makeSimpleElement(std::ostream &stream, identifierType id, const std::string &content); static void makeSimpleElement(std::ostream &stream, IdentifierType id, const std::string &content);
static void makeSimpleElement(std::ostream &stream, GenericFileElement::identifierType id, const char *data, std::size_t dataSize); static void makeSimpleElement(std::ostream &stream, IdentifierType id, const char *data, std::size_t dataSize);
static uint64 bytesToBeSkipped; static uint64 bytesToBeSkipped;
protected: protected:

View File

@ -124,7 +124,7 @@ MatroskaAttachmentMaker::MatroskaAttachmentMaker(MatroskaAttachment &attachment)
} }
if(attachment.attachedFileElement()) { if(attachment.attachedFileElement()) {
EbmlElement *child; EbmlElement *child;
for(auto id : initializer_list<EbmlElement::identifierType>{MatroskaIds::FileReferral, MatroskaIds::FileUsedStartTime, MatroskaIds::FileUsedEndTime}) { for(auto id : initializer_list<EbmlElement::IdentifierType>{MatroskaIds::FileReferral, MatroskaIds::FileUsedStartTime, MatroskaIds::FileUsedEndTime}) {
if((child = attachment.attachedFileElement()->childById(id))) { if((child = attachment.attachedFileElement()->childById(id))) {
m_attachedFileElementSize += child->totalSize(); m_attachedFileElementSize += child->totalSize();
} }
@ -156,7 +156,7 @@ void MatroskaAttachmentMaker::make(ostream &stream) const
EbmlElement::makeSimpleElement(stream, MatroskaIds::FileUID, attachment().id()); EbmlElement::makeSimpleElement(stream, MatroskaIds::FileUID, attachment().id());
if(attachment().attachedFileElement()) { if(attachment().attachedFileElement()) {
EbmlElement *child; EbmlElement *child;
for(auto id : initializer_list<EbmlElement::identifierType>{MatroskaIds::FileReferral, MatroskaIds::FileUsedStartTime, MatroskaIds::FileUsedEndTime}) { for(auto id : initializer_list<EbmlElement::IdentifierType>{MatroskaIds::FileReferral, MatroskaIds::FileUsedStartTime, MatroskaIds::FileUsedEndTime}) {
if((child = attachment().attachedFileElement()->childById(id))) { if((child = attachment().attachedFileElement()->childById(id))) {
if(child->buffer()) { if(child->buffer()) {
child->copyBuffer(stream); child->copyBuffer(stream);
@ -179,7 +179,7 @@ void MatroskaAttachmentMaker::bufferCurrentAttachments()
{ {
EbmlElement *child; EbmlElement *child;
if(attachment().attachedFileElement()) { if(attachment().attachedFileElement()) {
for(auto id : initializer_list<EbmlElement::identifierType>{MatroskaIds::FileReferral, MatroskaIds::FileUsedStartTime, MatroskaIds::FileUsedEndTime}) { for(auto id : initializer_list<EbmlElement::IdentifierType>{MatroskaIds::FileReferral, MatroskaIds::FileUsedStartTime, MatroskaIds::FileUsedEndTime}) {
if((child = attachment().attachedFileElement()->childById(id))) { if((child = attachment().attachedFileElement()->childById(id))) {
child->makeBuffer(); child->makeBuffer();
} }

View File

@ -3,7 +3,7 @@
#include "./ebmlelement.h" #include "./ebmlelement.h"
#include <map> #include <unordered_map>
#include <ostream> #include <ostream>
namespace Media { namespace Media {
@ -77,9 +77,9 @@ private:
bool updateSize(EbmlElement *element, int shift); bool updateSize(EbmlElement *element, int shift);
EbmlElement *m_cuesElement; EbmlElement *m_cuesElement;
std::map<EbmlElement *, MatroskaOffsetStates> m_offsets; std::unordered_map<EbmlElement *, MatroskaOffsetStates> m_offsets;
std::map<EbmlElement *, MatroskaReferenceOffsetPair> m_relativeOffsets; std::unordered_map<EbmlElement *, MatroskaReferenceOffsetPair> m_relativeOffsets;
std::map<EbmlElement *, uint64> m_sizes; std::unordered_map<EbmlElement *, uint64> m_sizes;
}; };
/*! /*!

View File

@ -63,10 +63,10 @@ const char *matroskaIdName(uint32 matroskaId)
case DateUTC: return "date UTC"; case DateUTC: return "date UTC";
case SegmentUID: return "unique segment ID"; case SegmentUID: return "unique segment ID";
case SegmentFileName: return "segment file name"; case SegmentFileName: return "segment file name";
case PrevUId: return "previous unique id"; case PrevUID: return "previous unique id";
case PrevFileName: return "previous file name"; case PrevFileName: return "previous file name";
case NexUId: return "next unique ID"; case NexUID: return "next unique ID";
case NexFileName: return "next file name"; case NextFileName: return "next file name";
case Title: return "title"; case Title: return "title";
case SegmentFamily: return "segment family"; case SegmentFamily: return "segment family";
case ChapterTranslate: return "chapter translate"; case ChapterTranslate: return "chapter translate";
@ -184,10 +184,10 @@ const char *matroskaIdName(uint32 matroskaId)
// IDs in the Targets master // IDs in the Targets master
case TargetTypeValue: return "target type value"; case TargetTypeValue: return "target type value";
case TargetType: return "target type"; case TargetType: return "target type";
case TagTrackUId: return "tag track UID"; case TagTrackUID: return "tag track UID";
case TagEditionUId: return "tag edition UID"; case TagEditionUID: return "tag edition UID";
case TagChapterUId: return "tag chapter UID"; case TagChapterUID: return "tag chapter UID";
case TagAttachmentUId: return "tag attachment UID"; case TagAttachmentUID: return "tag attachment UID";
// IDs in the Cues master // IDs in the Cues master
case CuePoint: return "cue point"; case CuePoint: return "cue point";
@ -315,5 +315,156 @@ const char *matroskaIdName(uint32 matroskaId)
} }
} }
/*!
* \brief Returns the level at which elements with the specified \a matroskaId are supposed
* to occur in a Matroska file.
*/
MatroskaElementLevel matroskaIdLevel(uint32 matroskaId)
{
using namespace EbmlIds;
using namespace MatroskaIds;
switch(matroskaId) {
case Header:
case Segment:
return MatroskaElementLevel::TopLevel;
case SeekHead:
case SegmentInfo:
case Cluster:
case Tracks:
case Cues:
case Attachments:
case Chapters:
case Tags:
return MatroskaElementLevel::Level1;
case Seek:
case SegmentUID:
case SegmentFileName:
case PrevUID:
case PrevFileName:
case NexUID:
case NextFileName:
case SegmentFamily:
case ChapterTranslate:
case TimeCodeScale:
case Duration:
case DateUTC:
case Title:
case MuxingApp:
case WrittingApp:
case Timecode:
case SilentTracks:
case Position:
case PrevSize:
case SimpleBlock:
case BlockGroup:
case EncryptedBlock:
case TrackEntry:
case CuePoint:
case AttachedFile:
case EditionEntry:
case Tag:
return MatroskaElementLevel::Level2;
case SeekID:
case SeekPosition:
case ChapterTranslateEditionUID:
case ChapterTranslateCodec:
case ChapterTranslateID:
case SilentTrackNumber:
case BlockVirtual:
case BlockAdditions:
case BlockDuration:
case ReferencePriority:
case ReferenceBlock:
case ReferenceVirtual:
case CodecState:
case DiscardPadding:
case Slices:
case TrackNumber:
case TrackUID:
case TrackType:
case TrackFlagEnabled:
case TrackFlagDefault:
case TrackFlagForced:
case TrackFlagLacing:
case MinCache:
case MaxCache:
case DefaultDuration:
case DefaultDecodedFieldDuration:
case TrackTimeCodeScale:
case TrackOffset:
case MaxBlockAdditionId:
case TrackName:
case TrackLanguage:
case CodecID:
case CodecPrivate:
case CodecName:
case AttachmentLink:
case CodecSettings:
case CodecInfoUrl:
case CodecDownloadUrl:
case CodecDecodeAll:
case TrackOverlay:
case CodecDelay:
case SeekPreRoll:
case TrackTranslate:
case TrackVideo:
case TrackAudio:
case ContentEncodings:
case CueTime:
case CueTrackPositions:
case FileDescription:
case FileName:
case FileMimeType:
case FileData:
case FileUID:
case FileReferral:
case FileUsedStartTime:
case FileUsedEndTime:
case EditionUID:
case EditionFlagHidden:
case EditionFlagDefault:
case EditionFlagOrdered:
case Targets:
return MatroskaElementLevel::Level3;
case BlockMore:
case TimeSlice:
case ContentEncoding:
case CueTrack:
case CueClusterPosition:
case CueRelativePosition:
case CueDuration:
case CueBlockNumber:
case CueCodecState:
case CueReference:
case TargetTypeValue:
case TargetType:
case TagTrackUID:
case TagEditionUID:
case TagChapterUID:
case TagAttachmentUID:
return MatroskaElementLevel::Level4;
case BlockAddID:
case BlockAdditional:
case LaceNumber:
case FrameNumber:
case BlockAdditionID:
case Delay:
case SliceDuration:
case ReferenceFrame:
case ReferenceOffset:
case ReferenceTimeCode:
case CueRefTime:
case CueRefCluster:
case CueRefNumber:
case CueRefCodecState:
return MatroskaElementLevel::Level5;
case Void:
case Crc32:
return MatroskaElementLevel::Global;
default:
return MatroskaElementLevel::Unknown;
}
}
} }

View File

@ -50,10 +50,10 @@ enum SegmentInfoIds {
DateUTC = 0x4461, DateUTC = 0x4461,
SegmentUID = 0x73A4, SegmentUID = 0x73A4,
SegmentFileName = 0x7384, SegmentFileName = 0x7384,
PrevUId = 0x3CB923, PrevUID = 0x3CB923,
PrevFileName = 0x3C83AB, PrevFileName = 0x3C83AB,
NexUId = 0x3EB923, NexUID = 0x3EB923,
NexFileName = 0x3E83BB, NextFileName = 0x3E83BB,
Title = 0x7BA9, Title = 0x7BA9,
SegmentFamily = 0x4444, SegmentFamily = 0x4444,
ChapterTranslate = 0x6924 ChapterTranslate = 0x6924
@ -261,10 +261,10 @@ enum SimpleTagIds {
enum TargetsIds { enum TargetsIds {
TargetTypeValue = 0x68ca, TargetTypeValue = 0x68ca,
TargetType = 0x63ca, TargetType = 0x63ca,
TagTrackUId = 0x63c5, TagTrackUID = 0x63c5,
TagEditionUId = 0x63c9, TagEditionUID = 0x63c9,
TagChapterUId = 0x63c4, TagChapterUID = 0x63c4,
TagAttachmentUId = 0x63c6 TagAttachmentUID = 0x63c6
}; };
/*! /*!
@ -489,7 +489,51 @@ enum KnownValues {
} }
enum class MatroskaElementLevel : byte {
TopLevel = 0x0,
Level1,
Level2,
Level3,
Level4,
Level5,
Level6,
Global = 0xFE,
Unknown = 0xFF,
};
constexpr bool operator>(MatroskaElementLevel lhs, MatroskaElementLevel rhs)
{
return static_cast<byte>(lhs) < static_cast<byte>(MatroskaElementLevel::Global)
&& static_cast<byte>(rhs) < static_cast<byte>(MatroskaElementLevel::Global)
&& static_cast<byte>(lhs) > static_cast<byte>(rhs);
}
constexpr bool operator>(byte lhs, MatroskaElementLevel rhs)
{
return lhs < static_cast<byte>(MatroskaElementLevel::Global)
&& static_cast<byte>(rhs) < static_cast<byte>(MatroskaElementLevel::Global)
&& static_cast<byte>(lhs) > static_cast<byte>(rhs);
}
constexpr bool operator<(MatroskaElementLevel lhs, MatroskaElementLevel rhs)
{
return static_cast<byte>(lhs) < static_cast<byte>(MatroskaElementLevel::Global)
&& static_cast<byte>(rhs) < static_cast<byte>(MatroskaElementLevel::Global)
&& static_cast<byte>(lhs) < static_cast<byte>(rhs);
}
constexpr bool operator>=(MatroskaElementLevel lhs, MatroskaElementLevel rhs)
{
return lhs == rhs || lhs > rhs;
}
constexpr bool operator<=(MatroskaElementLevel lhs, MatroskaElementLevel rhs)
{
return lhs == rhs || lhs < rhs;
}
TAG_PARSER_EXPORT const char *matroskaIdName(uint32 matroskaId); TAG_PARSER_EXPORT const char *matroskaIdName(uint32 matroskaId);
TAG_PARSER_EXPORT MatroskaElementLevel matroskaIdLevel(uint32 matroskaId);
} }

View File

@ -180,7 +180,7 @@ uint64 MatroskaSeekInfo::actualSize() const
* *
* \returns Returns an indication whether the actualSize() has changed. * \returns Returns an indication whether the actualSize() has changed.
*/ */
bool MatroskaSeekInfo::push(unsigned int index, EbmlElement::identifierType id, uint64 offset) bool MatroskaSeekInfo::push(unsigned int index, EbmlElement::IdentifierType id, uint64 offset)
{ {
unsigned int currentIndex = 0; unsigned int currentIndex = 0;
for(auto &entry : info()) { for(auto &entry : info()) {
@ -209,7 +209,7 @@ void MatroskaSeekInfo::clear()
/*! /*!
* \brief Returns a pointer to the first pair with the specified \a offset or nullptr if no such pair could be found. * \brief Returns a pointer to the first pair with the specified \a offset or nullptr if no such pair could be found.
*/ */
std::pair<EbmlElement::identifierType, uint64> *MatroskaSeekInfo::findSeekInfo(std::vector<MatroskaSeekInfo> &seekInfos, uint64 offset) std::pair<EbmlElement::IdentifierType, uint64> *MatroskaSeekInfo::findSeekInfo(std::vector<MatroskaSeekInfo> &seekInfos, uint64 offset)
{ {
for(auto &seekInfo : seekInfos) { for(auto &seekInfo : seekInfos) {
for(auto &entry : seekInfo.info()) { for(auto &entry : seekInfo.info()) {

View File

@ -15,25 +15,25 @@ public:
MatroskaSeekInfo(); MatroskaSeekInfo();
EbmlElement *seekHeadElement() const; EbmlElement *seekHeadElement() const;
const std::vector<std::pair<EbmlElement::identifierType, uint64> > &info() const; const std::vector<std::pair<EbmlElement::IdentifierType, uint64> > &info() const;
std::vector<std::pair<EbmlElement::identifierType, uint64> > &info(); std::vector<std::pair<EbmlElement::IdentifierType, uint64> > &info();
void shift(uint64 start, int64 amount); void shift(uint64 start, int64 amount);
void parse(EbmlElement *seekHeadElement); void parse(EbmlElement *seekHeadElement);
void make(std::ostream &stream); void make(std::ostream &stream);
uint64 minSize() const; uint64 minSize() const;
uint64 maxSize() const; uint64 maxSize() const;
uint64 actualSize() const; uint64 actualSize() const;
bool push(unsigned int index, EbmlElement::identifierType id, uint64 offset); bool push(unsigned int index, EbmlElement::IdentifierType id, uint64 offset);
void clear(); void clear();
// these methods seem to be not needed anymore // these methods seem to be not needed anymore
static std::pair<EbmlElement::identifierType, uint64> *findSeekInfo(std::vector<MatroskaSeekInfo> &seekInfos, uint64 offset); static std::pair<EbmlElement::IdentifierType, uint64> *findSeekInfo(std::vector<MatroskaSeekInfo> &seekInfos, uint64 offset);
static bool updateSeekInfo(const std::vector<MatroskaSeekInfo> &oldSeekInfos, std::vector<MatroskaSeekInfo> &newSeekInfos, uint64 oldOffset, uint64 newOffset); static bool updateSeekInfo(const std::vector<MatroskaSeekInfo> &oldSeekInfos, std::vector<MatroskaSeekInfo> &newSeekInfos, uint64 oldOffset, uint64 newOffset);
static bool updateSeekInfo(std::vector<MatroskaSeekInfo> &newSeekInfos, uint64 oldOffset, uint64 newOffset); static bool updateSeekInfo(std::vector<MatroskaSeekInfo> &newSeekInfos, uint64 oldOffset, uint64 newOffset);
private: private:
EbmlElement *m_seekHeadElement; EbmlElement *m_seekHeadElement;
std::vector<std::pair<EbmlElement::identifierType, uint64> > m_info; std::vector<std::pair<EbmlElement::IdentifierType, uint64> > m_info;
}; };
/*! /*!
@ -55,7 +55,7 @@ inline EbmlElement *MatroskaSeekInfo::seekHeadElement() const
* \brief Returns the seek information gathered when the parse() method was called. * \brief Returns the seek information gathered when the parse() method was called.
* \returns Returns the seek information as pairs of element IDs and the associated offsets (relative to the beginning of the file). * \returns Returns the seek information as pairs of element IDs and the associated offsets (relative to the beginning of the file).
*/ */
inline const std::vector<std::pair<EbmlElement::identifierType, uint64> > &MatroskaSeekInfo::info() const inline const std::vector<std::pair<EbmlElement::IdentifierType, uint64> > &MatroskaSeekInfo::info() const
{ {
return m_info; return m_info;
} }
@ -64,7 +64,7 @@ inline const std::vector<std::pair<EbmlElement::identifierType, uint64> > &Matro
* \brief Returns a mutable version of the seek information gathered when the parse() method was called. * \brief Returns a mutable version of the seek information gathered when the parse() method was called.
* \returns Returns the seek information as pairs of element IDs and the associated offsets (relative to the beginning of the file). * \returns Returns the seek information as pairs of element IDs and the associated offsets (relative to the beginning of the file).
*/ */
inline std::vector<std::pair<EbmlElement::identifierType, uint64> > &MatroskaSeekInfo::info() inline std::vector<std::pair<EbmlElement::IdentifierType, uint64> > &MatroskaSeekInfo::info()
{ {
return m_info; return m_info;
} }

View File

@ -15,7 +15,7 @@ namespace Media {
* \brief Implementation of Media::Tag for the Matroska container. * \brief Implementation of Media::Tag for the Matroska container.
*/ */
std::string MatroskaTag::fieldId(KnownField field) const MatroskaTag::IdentifierType MatroskaTag::internallyGetFieldId(KnownField field) const
{ {
using namespace MatroskaTagIds; using namespace MatroskaTagIds;
switch(field) { switch(field) {
@ -45,7 +45,7 @@ std::string MatroskaTag::fieldId(KnownField field) const
} }
} }
KnownField MatroskaTag::knownField(const std::string &id) const KnownField MatroskaTag::internallyGetKnownField(const IdentifierType &id) const
{ {
using namespace MatroskaTagIds; using namespace MatroskaTagIds;
static const map<string, KnownField> map({ static const map<string, KnownField> map({
@ -178,16 +178,16 @@ void MatroskaTag::parseTargets(EbmlElement &targetsElement)
addNotification(NotificationType::Warning, "Targets element contains multiple TargetType elements. Surplus elements will be ignored.", context); addNotification(NotificationType::Warning, "Targets element contains multiple TargetType elements. Surplus elements will be ignored.", context);
} }
break; break;
case MatroskaIds::TagTrackUId: case MatroskaIds::TagTrackUID:
m_target.tracks().emplace_back(child->readUInteger()); m_target.tracks().emplace_back(child->readUInteger());
break; break;
case MatroskaIds::TagEditionUId: case MatroskaIds::TagEditionUID:
m_target.editions().emplace_back(child->readUInteger()); m_target.editions().emplace_back(child->readUInteger());
break; break;
case MatroskaIds::TagChapterUId: case MatroskaIds::TagChapterUID:
m_target.chapters().emplace_back(child->readUInteger()); m_target.chapters().emplace_back(child->readUInteger());
break; break;
case MatroskaIds::TagAttachmentUId: case MatroskaIds::TagAttachmentUID:
m_target.attachments().emplace_back(child->readUInteger()); m_target.attachments().emplace_back(child->readUInteger());
break; break;
default: default:
@ -285,7 +285,7 @@ void MatroskaTagMaker::make(ostream &stream) const
} }
// write UIDs // write UIDs
typedef pair<uint16, vector<uint64> > p; typedef pair<uint16, vector<uint64> > p;
for(const auto &pair : initializer_list<p>{p(MatroskaIds::TagTrackUId, t.tracks()), p(MatroskaIds::TagEditionUId, t.editions()), p(MatroskaIds::TagChapterUId, t.chapters()), p(MatroskaIds::TagAttachmentUId, t.attachments())}) { for(const auto &pair : initializer_list<p>{p(MatroskaIds::TagTrackUID, t.tracks()), p(MatroskaIds::TagEditionUID, t.editions()), p(MatroskaIds::TagChapterUID, t.chapters()), p(MatroskaIds::TagAttachmentUID, t.attachments())}) {
if(!pair.second.empty()) { if(!pair.second.empty()) {
BE::getBytes(pair.first, buff); BE::getBytes(pair.first, buff);
for(auto uid : pair.second) { for(auto uid : pair.second) {

View File

@ -47,27 +47,39 @@ inline uint64 MatroskaTagMaker::requiredSize() const
return m_totalSize; return m_totalSize;
} }
class TAG_PARSER_EXPORT MatroskaTag : public FieldMapBasedTag<MatroskaTagField> /*!
* \brief Defines traits for the TagField implementation of the MatroskaTag class.
*/
template <>
class TAG_PARSER_EXPORT FieldMapBasedTagTraits<MatroskaTag>
{ {
public:
typedef MatroskaTagField FieldType;
typedef std::less<typename FieldType::IdentifierType> Compare;
};
class TAG_PARSER_EXPORT MatroskaTag : public FieldMapBasedTag<MatroskaTag>
{
friend class FieldMapBasedTag<MatroskaTag>;
public: public:
MatroskaTag(); MatroskaTag();
static constexpr TagType tagType = TagType::MatroskaTag; static constexpr TagType tagType = TagType::MatroskaTag;
// FIXME: implement type() and typeName() in FieldMapBasedTag static constexpr const char *tagName = "Matroska tag";
TagType type() const; static constexpr TagTextEncoding defaultTextEncoding = TagTextEncoding::Utf8;
const char *typeName() const;
TagTextEncoding proposedTextEncoding() const;
bool canEncodingBeUsed(TagTextEncoding encoding) const; bool canEncodingBeUsed(TagTextEncoding encoding) const;
bool supportsTarget() const; bool supportsTarget() const;
TagTargetLevel targetLevel() const; TagTargetLevel targetLevel() const;
std::string fieldId(KnownField field) const;
KnownField knownField(const std::string &id) const;
void parse(EbmlElement &tagElement); void parse(EbmlElement &tagElement);
MatroskaTagMaker prepareMaking(); MatroskaTagMaker prepareMaking();
void make(std::ostream &stream); void make(std::ostream &stream);
protected:
IdentifierType internallyGetFieldId(KnownField field) const;
KnownField internallyGetKnownField(const IdentifierType &id) const;
private: private:
void parseTargets(EbmlElement &targetsElement); void parseTargets(EbmlElement &targetsElement);
}; };
@ -88,21 +100,6 @@ inline TagTargetLevel MatroskaTag::targetLevel() const
return matroskaTagTargetLevel(m_target.level()); return matroskaTagTargetLevel(m_target.level());
} }
inline TagType MatroskaTag::type() const
{
return TagType::MatroskaTag;
}
inline const char *MatroskaTag::typeName() const
{
return "Matroska tag";
}
inline TagTextEncoding MatroskaTag::proposedTextEncoding() const
{
return TagTextEncoding::Utf8;
}
inline bool MatroskaTag::canEncodingBeUsed(TagTextEncoding encoding) const inline bool MatroskaTag::canEncodingBeUsed(TagTextEncoding encoding) const
{ {
return encoding == TagTextEncoding::Utf8; return encoding == TagTextEncoding::Utf8;

View File

@ -16,21 +16,8 @@ template <>
class TAG_PARSER_EXPORT TagFieldTraits<MatroskaTagField> class TAG_PARSER_EXPORT TagFieldTraits<MatroskaTagField>
{ {
public: public:
/*! typedef std::string IdentifierType;
* \brief Fields in a Matroska tag are identified by strings. typedef std::string TypeInfoType;
*/
typedef std::string identifierType;
/*!
* \brief The type info is stored using strings.
*/
typedef std::string typeInfoType;
/*!
* \brief The implementation type is EbmlElement.
*/
typedef MatroskaTagField implementationType;
static bool supportsNestedFields(); static bool supportsNestedFields();
}; };

View File

@ -673,7 +673,7 @@ void MediaFileInfo::applyChanges()
if(hasId3v2Tag()) { if(hasId3v2Tag()) {
addNotification(NotificationType::Warning, "Assigned ID3v2 tag can't be attached and will be ignored.", context); addNotification(NotificationType::Warning, "Assigned ID3v2 tag can't be attached and will be ignored.", context);
} }
m_container->forwardStatusUpdateCalls(this); m_container->forwardStatus(this);
m_tracksParsingStatus = ParsingStatus::NotParsedYet; m_tracksParsingStatus = ParsingStatus::NotParsedYet;
m_tagsParsingStatus = ParsingStatus::NotParsedYet; m_tagsParsingStatus = ParsingStatus::NotParsedYet;
try { try {

View File

@ -27,21 +27,21 @@ namespace Media {
/*! /*!
* \brief Constructs a new top level atom with the specified \a container at the specified \a startOffset. * \brief Constructs a new top level atom with the specified \a container at the specified \a startOffset.
*/ */
Mp4Atom::Mp4Atom(GenericFileElement::containerType &container, uint64 startOffset) : Mp4Atom::Mp4Atom(GenericFileElement::ContainerType &container, uint64 startOffset) :
GenericFileElement<Mp4Atom>(container, startOffset) GenericFileElement<Mp4Atom>(container, startOffset)
{} {}
/*! /*!
* \brief Constructs a new top level atom with the specified \a container at the specified \a startOffset. * \brief Constructs a new top level atom with the specified \a container at the specified \a startOffset.
*/ */
Mp4Atom::Mp4Atom(GenericFileElement::containerType &container, uint64 startOffset, uint64 maxSize) : Mp4Atom::Mp4Atom(GenericFileElement::ContainerType &container, uint64 startOffset, uint64 maxSize) :
GenericFileElement<Mp4Atom>(container, startOffset, maxSize) GenericFileElement<Mp4Atom>(container, startOffset, maxSize)
{} {}
/*! /*!
* \brief Constructs a new sub level atom with the specified \a parent at the specified \a startOffset. * \brief Constructs a new sub level atom with the specified \a parent at the specified \a startOffset.
*/ */
Mp4Atom::Mp4Atom(GenericFileElement::implementationType &parent, uint64 startOffset) : Mp4Atom::Mp4Atom(Mp4Atom &parent, uint64 startOffset) :
GenericFileElement<Mp4Atom>(parent, startOffset) GenericFileElement<Mp4Atom>(parent, startOffset)
{} {}

View File

@ -25,25 +25,9 @@ template <>
class TAG_PARSER_EXPORT FileElementTraits<Mp4Atom> class TAG_PARSER_EXPORT FileElementTraits<Mp4Atom>
{ {
public: public:
/*! typedef Mp4Container ContainerType;
* \brief The container type used to store such elements is Mp4Container. typedef uint32 IdentifierType;
*/ typedef uint64 DataSizeType;
typedef Mp4Container containerType;
/*!
* \brief The type used to store atom IDs is an unsigned 32-bit integer.
*/
typedef uint32 identifierType;
/*!
* \brief The type used to store element sizes is an unsigned 64-bit integer.
*/
typedef uint64 dataSizeType;
/*!
* \brief The implementation type is Mp4Atom.
*/
typedef Mp4Atom implementationType;
/*! /*!
* \brief Returns the minimal atom size which is 8 byte. * \brief Returns the minimal atom size which is 8 byte.
@ -59,7 +43,7 @@ class TAG_PARSER_EXPORT Mp4Atom : public GenericFileElement<Mp4Atom>
friend class GenericFileElement<Mp4Atom>; friend class GenericFileElement<Mp4Atom>;
public: public:
Mp4Atom(containerType& container, uint64 startOffset); Mp4Atom(ContainerType &container, uint64 startOffset);
std::string idToString() const; std::string idToString() const;
bool isParent() const; bool isParent() const;
@ -72,8 +56,8 @@ public:
static void makeHeader(uint64 size, uint32 id, IoUtilities::BinaryWriter &writer); static void makeHeader(uint64 size, uint32 id, IoUtilities::BinaryWriter &writer);
protected: protected:
Mp4Atom(containerType& container, uint64 startOffset, uint64 maxSize); Mp4Atom(ContainerType &container, uint64 startOffset, uint64 maxSize);
Mp4Atom(implementationType &parent, uint64 startOffset); Mp4Atom(Mp4Atom &parent, uint64 startOffset);
void internalParse(); void internalParse();
@ -86,7 +70,7 @@ private:
*/ */
inline std::string Mp4Atom::idToString() const inline std::string Mp4Atom::idToString() const
{ {
auto idString = ConversionUtilities::interpretIntegerAsString<identifierType>(id()); auto idString = ConversionUtilities::interpretIntegerAsString<IdentifierType>(id());
for(char &c : idString) { for(char &c : idString) {
if(c < ' ') { if(c < ' ') {
c = '?'; c = '?';

View File

@ -685,7 +685,7 @@ calculatePadding:
// update status // update status
updateStatus("Writing atom: " + level0Atom->idToString()); updateStatus("Writing atom: " + level0Atom->idToString());
// copy atom entirely and forward status update calls // copy atom entirely and forward status update calls
level0Atom->forwardStatusUpdateCalls(this); level0Atom->forwardStatus(this);
level0Atom->copyEntirely(outputStream); level0Atom->copyEntirely(outputStream);
} }
} }

View File

@ -59,16 +59,16 @@ const TagValue &Mp4Tag::value(KnownField field) const
{ {
switch(field) { switch(field) {
case KnownField::Genre: { case KnownField::Genre: {
const TagValue &value = FieldMapBasedTag<fieldType>::value(Mp4TagAtomIds::Genre); const TagValue &value = FieldMapBasedTag<Mp4Tag>::value(Mp4TagAtomIds::Genre);
if(!value.isEmpty()) { if(!value.isEmpty()) {
return value; return value;
} else { } else {
return FieldMapBasedTag<fieldType>::value(Mp4TagAtomIds::PreDefinedGenre); return FieldMapBasedTag<Mp4Tag>::value(Mp4TagAtomIds::PreDefinedGenre);
} }
} case KnownField::EncoderSettings: } case KnownField::EncoderSettings:
return this->value(Mp4TagExtendedMeanIds::iTunes, Mp4TagExtendedNameIds::cdec); return this->value(Mp4TagExtendedMeanIds::iTunes, Mp4TagExtendedNameIds::cdec);
case KnownField::RecordLabel: { case KnownField::RecordLabel: {
const TagValue &value = FieldMapBasedTag<fieldType>::value(Mp4TagAtomIds::RecordLabel); const TagValue &value = FieldMapBasedTag<Mp4Tag>::value(Mp4TagAtomIds::RecordLabel);
if(!value.isEmpty()) { if(!value.isEmpty()) {
return value; return value;
} else { } else {
@ -76,13 +76,13 @@ const TagValue &Mp4Tag::value(KnownField field) const
} }
} }
default: default:
return FieldMapBasedTag<fieldType>::value(field); return FieldMapBasedTag<Mp4Tag>::value(field);
} }
} }
std::vector<const TagValue *> Mp4Tag::values(KnownField field) const std::vector<const TagValue *> Mp4Tag::values(KnownField field) const
{ {
auto values = FieldMapBasedTag<fieldType>::values(field); auto values = FieldMapBasedTag<Mp4Tag>::values(field);
const Mp4ExtendedFieldId extendedId(field); const Mp4ExtendedFieldId extendedId(field);
if(extendedId) { if(extendedId) {
auto range = fields().equal_range(Mp4TagAtomIds::Extended); auto range = fields().equal_range(Mp4TagAtomIds::Extended);
@ -119,7 +119,7 @@ const TagValue &Mp4Tag::value(const string mean, const string name) const
return (this->*static_cast<const TagValue &(Mp4Tag::*)(const string &, const string &) const>(&Mp4Tag::value))(mean, name); return (this->*static_cast<const TagValue &(Mp4Tag::*)(const string &, const string &) const>(&Mp4Tag::value))(mean, name);
} }
uint32 Mp4Tag::fieldId(KnownField field) const Mp4Tag::IdentifierType Mp4Tag::internallyGetFieldId(KnownField field) const
{ {
using namespace Mp4TagAtomIds; using namespace Mp4TagAtomIds;
switch(field) { switch(field) {
@ -146,7 +146,7 @@ uint32 Mp4Tag::fieldId(KnownField field) const
} }
} }
KnownField Mp4Tag::knownField(const uint32 &id) const KnownField Mp4Tag::internallyGetKnownField(const IdentifierType &id) const
{ {
using namespace Mp4TagAtomIds; using namespace Mp4TagAtomIds;
switch(id) { switch(id) {
@ -180,10 +180,10 @@ bool Mp4Tag::setValue(KnownField field, const TagValue &value)
switch(value.type()) { switch(value.type()) {
case TagDataType::StandardGenreIndex: case TagDataType::StandardGenreIndex:
fields().erase(Mp4TagAtomIds::Genre); fields().erase(Mp4TagAtomIds::Genre);
return FieldMapBasedTag<fieldType>::setValue(Mp4TagAtomIds::PreDefinedGenre, value); return FieldMapBasedTag<Mp4Tag>::setValue(Mp4TagAtomIds::PreDefinedGenre, value);
default: default:
fields().erase(Mp4TagAtomIds::PreDefinedGenre); fields().erase(Mp4TagAtomIds::PreDefinedGenre);
return FieldMapBasedTag<fieldType>::setValue(Mp4TagAtomIds::Genre, value); return FieldMapBasedTag<Mp4Tag>::setValue(Mp4TagAtomIds::Genre, value);
} }
case KnownField::EncoderSettings: case KnownField::EncoderSettings:
return setValue(Mp4TagExtendedMeanIds::iTunes, Mp4TagExtendedNameIds::cdec, value); return setValue(Mp4TagExtendedMeanIds::iTunes, Mp4TagExtendedNameIds::cdec, value);
@ -193,7 +193,7 @@ bool Mp4Tag::setValue(KnownField field, const TagValue &value)
} }
FALLTHROUGH; FALLTHROUGH;
default: default:
return FieldMapBasedTag<fieldType>::setValue(field, value); return FieldMapBasedTag<Mp4Tag>::setValue(field, value);
} }
} }
@ -225,7 +225,7 @@ bool Mp4Tag::setValues(KnownField field, const std::vector<TagValue> &values)
range.first->second.setValue(TagValue()); range.first->second.setValue(TagValue());
} }
} }
return FieldMapBasedTag<fieldType>::setValues(field, values); return FieldMapBasedTag<Mp4Tag>::setValues(field, values);
} }
/*! /*!
@ -244,7 +244,7 @@ bool Mp4Tag::setValue(const char *mean, const char *name, const TagValue &value)
return true; return true;
} }
} }
fields().insert(make_pair(Mp4TagAtomIds::Extended, fieldType(mean, name, value))); fields().insert(make_pair(Mp4TagAtomIds::Extended, FieldType(mean, name, value)));
return true; return true;
} }
@ -260,10 +260,10 @@ bool Mp4Tag::hasField(KnownField field) const
{ {
switch(field) { switch(field) {
case KnownField::Genre: case KnownField::Genre:
return FieldMapBasedTag<fieldType>::hasField(Mp4TagAtomIds::PreDefinedGenre) return FieldMapBasedTag<Mp4Tag>::hasField(Mp4TagAtomIds::PreDefinedGenre)
|| FieldMapBasedTag<fieldType>::hasField(Mp4TagAtomIds::Genre); || FieldMapBasedTag<Mp4Tag>::hasField(Mp4TagAtomIds::Genre);
default: default:
return FieldMapBasedTag<fieldType>::hasField(field); return FieldMapBasedTag<Mp4Tag>::hasField(field);
} }
} }
@ -320,7 +320,7 @@ void Mp4Tag::parse(Mp4Atom &metaAtom)
child->parse(); child->parse();
tagField.invalidateNotifications(); tagField.invalidateNotifications();
tagField.reparse(*child); tagField.reparse(*child);
fields().insert(pair<fieldType::identifierType, fieldType>(child->id(), tagField)); fields().emplace(child->id(), tagField);
} catch(const Failure &) { } catch(const Failure &) {
} }
addNotifications(context, *child); addNotifications(context, *child);

View File

@ -87,44 +87,59 @@ inline uint64 Mp4TagMaker::requiredSize() const
return m_metaSize; return m_metaSize;
} }
class TAG_PARSER_EXPORT Mp4Tag : public FieldMapBasedTag<Mp4TagField> /*!
* \brief Defines traits for the TagField implementation of the Mp4Tag class.
*/
template <>
class TAG_PARSER_EXPORT FieldMapBasedTagTraits<Mp4Tag>
{ {
public:
typedef Mp4TagField FieldType;
typedef std::less<typename FieldType::IdentifierType> Compare;
};
class TAG_PARSER_EXPORT Mp4Tag : public FieldMapBasedTag<Mp4Tag>
{
friend class FieldMapBasedTag<Mp4Tag>;
public: public:
Mp4Tag(); Mp4Tag();
static constexpr TagType tagType = TagType::Mp4Tag; static constexpr TagType tagType = TagType::Mp4Tag;
TagType type() const; static constexpr const char *tagName = "MP4/iTunes tag";
const char *typeName() const; static constexpr TagTextEncoding defaultTextEncoding = TagTextEncoding::Utf8;
TagTextEncoding proposedTextEncoding() const;
bool canEncodingBeUsed(TagTextEncoding encoding) const; bool canEncodingBeUsed(TagTextEncoding encoding) const;
uint32 fieldId(KnownField field) const;
KnownField knownField(const uint32 &id) const;
bool supportsField(KnownField field) const; bool supportsField(KnownField field) const;
using FieldMapBasedTag<Mp4TagField>::value; using FieldMapBasedTag<Mp4Tag>::value;
const TagValue &value(KnownField value) const; const TagValue &value(KnownField value) const;
using FieldMapBasedTag<Mp4TagField>::values; using FieldMapBasedTag<Mp4Tag>::values;
std::vector<const TagValue *> values(KnownField field) const; std::vector<const TagValue *> values(KnownField field) const;
#ifdef LEGACY_API #ifdef LEGACY_API
const TagValue &value(const std::string mean, const std::string name) const; const TagValue &value(const std::string mean, const std::string name) const;
#endif #endif
const TagValue &value(const std::string &mean, const std::string &name) const; const TagValue &value(const std::string &mean, const std::string &name) const;
const TagValue &value(const char *mean, const char *name) const; const TagValue &value(const char *mean, const char *name) const;
using FieldMapBasedTag<Mp4TagField>::setValue; using FieldMapBasedTag<Mp4Tag>::setValue;
bool setValue(KnownField field, const TagValue &value); bool setValue(KnownField field, const TagValue &value);
using FieldMapBasedTag<Mp4TagField>::setValues; using FieldMapBasedTag<Mp4Tag>::setValues;
bool setValues(KnownField field, const std::vector<TagValue> &values); bool setValues(KnownField field, const std::vector<TagValue> &values);
#ifdef LEGACY_API #ifdef LEGACY_API
bool setValue(const std::string mean, const std::string name, const TagValue &value); bool setValue(const std::string mean, const std::string name, const TagValue &value);
#endif #endif
bool setValue(const std::string &mean, const std::string &name, const TagValue &value); bool setValue(const std::string &mean, const std::string &name, const TagValue &value);
bool setValue(const char *mean, const char *name, const TagValue &value); bool setValue(const char *mean, const char *name, const TagValue &value);
using FieldMapBasedTag<Mp4TagField>::hasField; using FieldMapBasedTag<Mp4Tag>::hasField;
bool hasField(KnownField value) const; bool hasField(KnownField value) const;
void parse(Mp4Atom &metaAtom); void parse(Mp4Atom &metaAtom);
Mp4TagMaker prepareMaking(); Mp4TagMaker prepareMaking();
void make(std::ostream &stream); void make(std::ostream &stream);
protected:
IdentifierType internallyGetFieldId(KnownField field) const;
KnownField internallyGetKnownField(const IdentifierType &id) const;
}; };
/*! /*!
@ -133,28 +148,13 @@ public:
inline Mp4Tag::Mp4Tag() inline Mp4Tag::Mp4Tag()
{} {}
inline TagType Mp4Tag::type() const
{
return TagType::Mp4Tag;
}
inline const char *Mp4Tag::typeName() const
{
return "MP4/iTunes tag";
}
inline TagTextEncoding Mp4Tag::proposedTextEncoding() const
{
return TagTextEncoding::Utf8;
}
inline bool Mp4Tag::supportsField(KnownField field) const inline bool Mp4Tag::supportsField(KnownField field) const
{ {
switch(field) { switch(field) {
case KnownField::EncoderSettings: case KnownField::EncoderSettings:
return true; return true;
default: default:
return FieldMapBasedTag<Mp4TagField>::supportsField(field); return FieldMapBasedTag<Mp4Tag>::supportsField(field);
} }
} }

View File

@ -36,7 +36,7 @@ Mp4TagField::Mp4TagField() :
/*! /*!
* \brief Constructs a new Mp4TagField with the specified \a id and \a value. * \brief Constructs a new Mp4TagField with the specified \a id and \a value.
*/ */
Mp4TagField::Mp4TagField(identifierType id, const TagValue &value) : Mp4TagField::Mp4TagField(IdentifierType id, const TagValue &value) :
TagField<Mp4TagField>(id, value), TagField<Mp4TagField>(id, value),
m_parsedRawDataType(RawDataType::Reserved), m_parsedRawDataType(RawDataType::Reserved),
m_countryIndicator(0), m_countryIndicator(0),

View File

@ -57,20 +57,8 @@ template <>
class TAG_PARSER_EXPORT TagFieldTraits<Mp4TagField> class TAG_PARSER_EXPORT TagFieldTraits<Mp4TagField>
{ {
public: public:
/*! typedef uint32 IdentifierType;
* \brief Fields in a iTunes-style MP4 tag are identified by 32-bit unsigned integers. typedef uint32 TypeInfoType;
*/
typedef uint32 identifierType;
/*!
* \brief The type info is stored using 32-bit unsigned integers.
*/
typedef uint32 typeInfoType;
/*!
* \brief The implementation type is Mp4TagField.
*/
typedef Mp4TagField implementationType;
}; };
class Mp4Atom; class Mp4Atom;
@ -117,7 +105,7 @@ class TAG_PARSER_EXPORT Mp4TagField : public TagField<Mp4TagField>, public Statu
public: public:
Mp4TagField(); Mp4TagField();
Mp4TagField(identifierType id, const TagValue &value); Mp4TagField(IdentifierType id, const TagValue &value);
Mp4TagField(const std::string &mean, const std::string &name, const TagValue &value); Mp4TagField(const std::string &mean, const std::string &name, const TagValue &value);
void reparse(Mp4Atom &ilstChild); void reparse(Mp4Atom &ilstChild);
@ -136,8 +124,8 @@ public:
std::vector<uint32> expectedRawDataTypes() const; std::vector<uint32> expectedRawDataTypes() const;
uint32 appropriateRawDataType() const; uint32 appropriateRawDataType() const;
static identifierType fieldIdFromString(const char *idString, std::size_t idStringSize = std::string::npos); static IdentifierType fieldIdFromString(const char *idString, std::size_t idStringSize = std::string::npos);
static std::string fieldIdToString(identifierType id); static std::string fieldIdToString(IdentifierType id);
protected: protected:
void cleared(); void cleared();
@ -227,7 +215,7 @@ inline bool Mp4TagField::supportsNestedFields() const
* \remarks The specified \a idString is assumed to be UTF-8 encoded. In order to get the ©-sign * \remarks The specified \a idString is assumed to be UTF-8 encoded. In order to get the ©-sign
* correctly, it is converted to Latin-1. * correctly, it is converted to Latin-1.
*/ */
inline Mp4TagField::identifierType Mp4TagField::fieldIdFromString(const char *idString, std::size_t idStringSize) inline Mp4TagField::IdentifierType Mp4TagField::fieldIdFromString(const char *idString, std::size_t idStringSize)
{ {
const auto latin1 = ConversionUtilities::convertUtf8ToLatin1(idString, idStringSize != std::string::npos ? idStringSize : std::strlen(idString)); const auto latin1 = ConversionUtilities::convertUtf8ToLatin1(idString, idStringSize != std::string::npos ? idStringSize : std::strlen(idString));
switch(latin1.second) { switch(latin1.second) {
@ -243,7 +231,7 @@ inline Mp4TagField::identifierType Mp4TagField::fieldIdFromString(const char *id
* \remarks The specified \a id is considered Latin-1 encoded. In order to get the ©-sign * \remarks The specified \a id is considered Latin-1 encoded. In order to get the ©-sign
* correctly, it is converted to UTF-8. * correctly, it is converted to UTF-8.
*/ */
inline std::string Mp4TagField::fieldIdToString(Mp4TagField::identifierType id) inline std::string Mp4TagField::fieldIdToString(Mp4TagField::IdentifierType id)
{ {
const auto utf8 = ConversionUtilities::convertLatin1ToUtf8(ConversionUtilities::interpretIntegerAsString<uint32>(id).data(), 4); const auto utf8 = ConversionUtilities::convertLatin1ToUtf8(ConversionUtilities::interpretIntegerAsString<uint32>(id).data(), 4);
return std::string(utf8.first.get(), utf8.second); return std::string(utf8.first.get(), utf8.second);

View File

@ -20,14 +20,14 @@ namespace Media {
* \brief Constructs a new top level descriptor with the specified \a container at the specified \a startOffset * \brief Constructs a new top level descriptor with the specified \a container at the specified \a startOffset
* and with the specified \a maxSize. * and with the specified \a maxSize.
*/ */
Mpeg4Descriptor::Mpeg4Descriptor(containerType &container, uint64 startOffset, uint64 maxSize) : Mpeg4Descriptor::Mpeg4Descriptor(ContainerType &container, uint64 startOffset, uint64 maxSize) :
GenericFileElement<Mpeg4Descriptor>(container, startOffset, maxSize) GenericFileElement<Mpeg4Descriptor>(container, startOffset, maxSize)
{} {}
/*! /*!
* \brief Constructs a new sub level descriptor with the specified \a parent at the specified \a startOffset. * \brief Constructs a new sub level descriptor with the specified \a parent at the specified \a startOffset.
*/ */
Mpeg4Descriptor::Mpeg4Descriptor(implementationType &parent, uint64 startOffset) : Mpeg4Descriptor::Mpeg4Descriptor(Mpeg4Descriptor &parent, uint64 startOffset) :
GenericFileElement<Mpeg4Descriptor>(parent, startOffset) GenericFileElement<Mpeg4Descriptor>(parent, startOffset)
{} {}
@ -75,12 +75,12 @@ void Mpeg4Descriptor::internalParse()
m_dataSize = maxTotalSize(); // using max size instead m_dataSize = maxTotalSize(); // using max size instead
} }
m_firstChild.reset(); m_firstChild.reset();
implementationType *sibling = nullptr; Mpeg4Descriptor *sibling = nullptr;
if(totalSize() < maxTotalSize()) { if(totalSize() < maxTotalSize()) {
if(parent()) { if(parent()) {
sibling = new implementationType(*(parent()), startOffset() + totalSize()); sibling = new Mpeg4Descriptor(*(parent()), startOffset() + totalSize());
} else { } else {
sibling = new implementationType(container(), startOffset() + totalSize(), maxTotalSize() - totalSize()); sibling = new Mpeg4Descriptor(container(), startOffset() + totalSize(), maxTotalSize() - totalSize());
} }
} }
m_nextSibling.reset(sibling); m_nextSibling.reset(sibling);

View File

@ -17,25 +17,9 @@ template <>
class TAG_PARSER_EXPORT FileElementTraits<Mpeg4Descriptor> class TAG_PARSER_EXPORT FileElementTraits<Mpeg4Descriptor>
{ {
public: public:
/*! typedef Mp4Container ContainerType;
* \brief The container type used to store such elements is Mp4Container. typedef byte IdentifierType;
*/ typedef uint32 DataSizeType;
typedef Mp4Container containerType;
/*!
* \brief The type used to store atom IDs is an unsigned 32-bit integer.
*/
typedef byte identifierType;
/*!
* \brief The type used to store element sizes is an unsigned 32-bit integer.
*/
typedef uint32 dataSizeType;
/*!
* \brief The implementation type is Mp4Atom.
*/
typedef Mpeg4Descriptor implementationType;
/*! /*!
* \brief Returns the minimal descriptor size which is 2 byte. * \brief Returns the minimal descriptor size which is 2 byte.
@ -51,7 +35,7 @@ class TAG_PARSER_EXPORT Mpeg4Descriptor : public GenericFileElement<Mpeg4Descrip
friend class GenericFileElement<Mpeg4Descriptor>; friend class GenericFileElement<Mpeg4Descriptor>;
public: public:
Mpeg4Descriptor(containerType& container, uint64 startOffset, uint64 maxSize); Mpeg4Descriptor(ContainerType &container, uint64 startOffset, uint64 maxSize);
std::string idToString() const; std::string idToString() const;
bool isParent() const; bool isParent() const;
@ -59,7 +43,7 @@ public:
uint64 firstChildOffset() const; uint64 firstChildOffset() const;
protected: protected:
Mpeg4Descriptor(implementationType &parent, uint64 startOffset); Mpeg4Descriptor(Mpeg4Descriptor &parent, uint64 startOffset);
void internalParse(); void internalParse();

View File

@ -52,6 +52,10 @@ size_t StatusProvider::registerCallback(CallbackFunction callback)
*/ */
void StatusProvider::addNotification(const Notification &notification) void StatusProvider::addNotification(const Notification &notification)
{ {
if(m_forward) {
m_forward->addNotification(notification);
return;
}
m_notifications.push_back(notification); m_notifications.push_back(notification);
m_worstNotificationType |= notification.type(); m_worstNotificationType |= notification.type();
invokeCallbacks(); invokeCallbacks();
@ -63,6 +67,10 @@ void StatusProvider::addNotification(const Notification &notification)
*/ */
void StatusProvider::addNotification(NotificationType type, const string &message, const string &context) void StatusProvider::addNotification(NotificationType type, const string &message, const string &context)
{ {
if(m_forward) {
m_forward->addNotification(type, message, context);
return;
}
m_notifications.emplace_back(type, message, context); m_notifications.emplace_back(type, message, context);
m_worstNotificationType |= type; m_worstNotificationType |= type;
invokeCallbacks(); invokeCallbacks();
@ -72,15 +80,19 @@ void StatusProvider::addNotification(NotificationType type, const string &messag
* \brief This method is meant to be called by the derived class to add all notifications \a from another * \brief This method is meant to be called by the derived class to add all notifications \a from another
* StatusProvider instance. * StatusProvider instance.
*/ */
void StatusProvider::addNotifications(const StatusProvider &from) /*void StatusProvider::addNotifications(const StatusProvider &from)
{ {
if(m_forward) {
m_forward->addNotifications(from);
return;
}
if(&from == this) { if(&from == this) {
return; return;
} }
m_notifications.insert(m_notifications.end(), from.m_notifications.cbegin(), from.m_notifications.cend()); m_notifications.insert(m_notifications.end(), from.m_notifications.cbegin(), from.m_notifications.cend());
m_worstNotificationType |= from.worstNotificationType(); m_worstNotificationType |= from.worstNotificationType();
invokeCallbacks(); invokeCallbacks();
} }*/
/*! /*!
* \brief This method is meant to be called by the derived class to add all notifications \a from another * \brief This method is meant to be called by the derived class to add all notifications \a from another
@ -88,21 +100,29 @@ void StatusProvider::addNotifications(const StatusProvider &from)
* *
* The specified \a higherContext is concatenated with the original context string. * The specified \a higherContext is concatenated with the original context string.
*/ */
void StatusProvider::addNotifications(const string &higherContext, const StatusProvider &from) /*void StatusProvider::addNotifications(const string &higherContext, const StatusProvider &from)
{ {
if(m_forward) {
m_forward->addNotifications(higherContext, from);
return;
}
if(&from == this) { if(&from == this) {
return; return;
} }
for(const auto &notification : from.m_notifications) { for(const auto &notification : from.m_notifications) {
addNotification(notification.type(), notification.message(), higherContext % ',' % ' ' + notification.context()); addNotification(notification.type(), notification.message(), higherContext % ',' % ' ' + notification.context());
} }
} }*/
/*! /*!
* \brief This method is meant to be called by the derived class to add the specified \a notifications. * \brief This method is meant to be called by the derived class to add the specified \a notifications.
*/ */
void StatusProvider::addNotifications(const NotificationList &notifications) /*void StatusProvider::addNotifications(const NotificationList &notifications)
{ {
if(m_forward) {
m_forward->addNotifications(notifications);
return;
}
m_notifications.insert(m_notifications.end(), notifications.cbegin(), notifications.cend()); m_notifications.insert(m_notifications.end(), notifications.cbegin(), notifications.cend());
if(m_worstNotificationType != Notification::worstNotificationType()) { if(m_worstNotificationType != Notification::worstNotificationType()) {
for(const Notification &notification : notifications) { for(const Notification &notification : notifications) {
@ -112,6 +132,7 @@ void StatusProvider::addNotifications(const NotificationList &notifications)
} }
} }
invokeCallbacks(); invokeCallbacks();
} }*/
} }

View File

@ -29,8 +29,8 @@ public:
size_t registerCallback(CallbackFunction callback); size_t registerCallback(CallbackFunction callback);
void unregisterCallback(size_t id); void unregisterCallback(size_t id);
void unregisterAllCallbacks(); void unregisterAllCallbacks();
void forwardStatusUpdateCalls(StatusProvider *other = nullptr); void forwardStatus(StatusProvider *other = nullptr);
inline StatusProvider *usedProvider(); StatusProvider *usedProvider();
void tryToAbort(); void tryToAbort();
bool isAborted() const; bool isAborted() const;
void invalidateStatus(); void invalidateStatus();
@ -40,25 +40,25 @@ public:
void updatePercentage(double percentage); void updatePercentage(double percentage);
void addNotification(const Notification &notification); void addNotification(const Notification &notification);
void addNotification(NotificationType type, const std::string &message, const std::string &context); void addNotification(NotificationType type, const std::string &message, const std::string &context);
void addNotifications(const StatusProvider &from); //void addNotifications(const StatusProvider &from);
void addNotifications(const std::string &higherContext, const StatusProvider &from); //void addNotifications(const std::string &higherContext, const StatusProvider &from);
void addNotifications(const NotificationList &notifications); //void addNotifications(const NotificationList &notifications);
protected: protected:
StatusProvider(); StatusProvider();
private: private:
inline void invokeCallbacks(); void invokeCallbacks();
inline void updateWorstNotificationType(NotificationType notificationType); void updateWorstNotificationType(NotificationType notificationType);
inline void transferNotifications(StatusProvider &from); //void transferNotifications(StatusProvider &from);
NotificationList m_notifications; StatusProvider *m_forward;
NotificationType m_worstNotificationType;
std::string m_status; std::string m_status;
double m_percentage; double m_percentage;
CallbackVector m_callbacks; CallbackVector m_callbacks;
NotificationList m_notifications;
NotificationType m_worstNotificationType;
bool m_abort; bool m_abort;
StatusProvider *m_forward;
}; };
/*! /*!
@ -66,6 +66,10 @@ private:
*/ */
inline void StatusProvider::updateStatus(const std::string &status) inline void StatusProvider::updateStatus(const std::string &status)
{ {
if(m_forward) {
m_forward->updateStatus(status);
return;
}
m_status = status; m_status = status;
invokeCallbacks(); invokeCallbacks();
} }
@ -77,6 +81,10 @@ inline void StatusProvider::updateStatus(const std::string &status)
*/ */
inline void StatusProvider::updateStatus(const std::string &status, double percentage) inline void StatusProvider::updateStatus(const std::string &status, double percentage)
{ {
if(m_forward) {
m_forward->updateStatus(status, percentage);
return;
}
m_status = status; m_status = status;
m_percentage = percentage; m_percentage = percentage;
invokeCallbacks(); invokeCallbacks();
@ -89,6 +97,10 @@ inline void StatusProvider::updateStatus(const std::string &status, double perce
*/ */
inline void StatusProvider::updatePercentage(double percentage) inline void StatusProvider::updatePercentage(double percentage)
{ {
if(m_forward) {
m_forward->updatePercentage(percentage);
return;
}
m_percentage = percentage; m_percentage = percentage;
invokeCallbacks(); invokeCallbacks();
} }
@ -96,7 +108,7 @@ inline void StatusProvider::updatePercentage(double percentage)
/*! /*!
* \brief Returns the provider which callback functions will be called when the status or the percentage is updated. * \brief Returns the provider which callback functions will be called when the status or the percentage is updated.
* *
* The default is the current instance. This can be changed using the forwardStatusUpdateCalls() method. * The default is the current instance. This can be changed using the forwardStatus() method.
*/ */
inline StatusProvider *StatusProvider::usedProvider() inline StatusProvider *StatusProvider::usedProvider()
{ {
@ -160,7 +172,7 @@ inline double StatusProvider::currentPercentage() const
/*! /*!
* \brief Returns an indication whether the current operation should be aborted. * \brief Returns an indication whether the current operation should be aborted.
* *
* This can be tested when implementing an operation that should be able to be * This flag can be tested when implementing an operation that should be able to be
* aborted. * aborted.
*/ */
inline bool StatusProvider::isAborted() const inline bool StatusProvider::isAborted() const
@ -192,26 +204,30 @@ inline void StatusProvider::unregisterAllCallbacks()
} }
/*! /*!
* \brief Forwards all status updates calls to the specified \a statusProvider. * \brief Forwards all status updates calls and notifications to the specified \a statusProvider.
* *
* Not the callback methods associated to the current instance will be called * This basically forwards status information updates and notifications to the specified instance.
* to inform about status updates. Instead the callback methods associated to
* the specified instance will be called.
* *
* The current instance is still the sender. * \remarks
* * - Any notifications will *not* be added to the current instance anymore. Instead the
* The current instance is considered as abortet if the specified provider is * notifications will be added to the specified instance.
* abortet even if tryToAbort() has not been called. * - The callback methods assigned to the current instance will *not* be called
* * to inform about status updates anymore. Instead the callback methods associated to
* The current instance will return the status and percentage of the specified * the specified instance will be called.
* provider if it provides no own status or percentage. * - The current instance is still passed as the sender when invoking callback methods.
* * - The current instance is also considered as aborted if the specified provider is
* Provide nullptr to revert to the default behaviour. * aborted - even if tryToAbort() has not been called on the current instance.
* * - The current instance will return the status and percentage of the specified
* \remarks Leads to endless recursion if \a statusProvider forwards (indirectly * provider if it provides no own status or percentage.
* or directly) to the current instance. * - Provide nullptr to revert to the default behaviour.
* - The methods invalidateStatus() and invalidateNotifications() are *not* forwarded.
* - The methods updateStatus() and updatePercentage() are *not* forwarded.
* - The method transferNotifications() is *not* forwarded.
* - Leads to endless recursion if \a statusProvider forwards (indirectly
* or directly) to the current instance. (So better prevent it.)
* - Set \a other to nullptr to restore the usual behaviour.
*/ */
inline void StatusProvider::forwardStatusUpdateCalls(StatusProvider *other) inline void StatusProvider::forwardStatus(StatusProvider *other)
{ {
m_forward = other; m_forward = other;
} }
@ -272,7 +288,11 @@ inline void StatusProvider::invokeCallbacks()
*/ */
inline void StatusProvider::updateWorstNotificationType(NotificationType notificationType) inline void StatusProvider::updateWorstNotificationType(NotificationType notificationType)
{ {
if(static_cast<int>(m_worstNotificationType) < static_cast<int>(notificationType)) { if(m_forward) {
m_forward->updateWorstNotificationType(notificationType);
return;
}
if(m_worstNotificationType < notificationType) {
m_worstNotificationType = notificationType; m_worstNotificationType = notificationType;
} }
} }
@ -282,12 +302,14 @@ inline void StatusProvider::updateWorstNotificationType(NotificationType notific
* \remarks In constrast to the similar addNotifications() overload, this method does not copy the notifications. Instead * \remarks In constrast to the similar addNotifications() overload, this method does not copy the notifications. Instead
* the notifications are transfered. It also doesn't check whether \a from is the current instance and doesn't * the notifications are transfered. It also doesn't check whether \a from is the current instance and doesn't
* invoke callbacks. * invoke callbacks.
* \deprecated This method should likely be removed after the status provider rework.
*/ */
void StatusProvider::transferNotifications(StatusProvider &from) /*inline void StatusProvider::transferNotifications(StatusProvider &from)
{ {
m_notifications.splice(m_notifications.end(), from.m_notifications); m_notifications.splice(m_notifications.end(), from.m_notifications);
m_worstNotificationType |= from.worstNotificationType(); m_worstNotificationType |= from.worstNotificationType();
} }*/
} }

View File

@ -152,8 +152,7 @@ void OverallTests::checkMkvTestfile3()
/*! /*!
* \brief Checks "matroska_wave1/test4.mkv". * \brief Checks "matroska_wave1/test4.mkv".
* \remarks This file is using the EBML feature that allows Master elements to have no known size. Handling * \remarks This file is using the EBML feature that allows Master elements to have no known size.
* such files is not supported yet.
*/ */
void OverallTests::checkMkvTestfile4() void OverallTests::checkMkvTestfile4()
{ {

View File

@ -156,7 +156,7 @@ void UtilitiesTests::testStatusProvider()
// forwarding // forwarding
TestStatusProvider forwardReceiver; TestStatusProvider forwardReceiver;
status.forwardStatusUpdateCalls(&forwardReceiver); status.forwardStatus(&forwardReceiver);
statusUpdateReceived = false; statusUpdateReceived = false;
forwardReceiver.registerCallback([&status, &statusUpdateReceived] (StatusProvider &sender) { forwardReceiver.registerCallback([&status, &statusUpdateReceived] (StatusProvider &sender) {
CPPUNIT_ASSERT(&status == &sender); CPPUNIT_ASSERT(&status == &sender);

View File

@ -29,7 +29,7 @@ const TagValue &VorbisComment::value(KnownField field) const
case KnownField::Vendor: case KnownField::Vendor:
return vendor(); return vendor();
default: default:
return FieldMapBasedTag<VorbisCommentField, CaseInsensitiveStringComparer>::value(field); return FieldMapBasedTag<VorbisComment>::value(field);
} }
} }
@ -40,11 +40,11 @@ bool VorbisComment::setValue(KnownField field, const TagValue &value)
setVendor(value); setVendor(value);
return true; return true;
default: default:
return FieldMapBasedTag<VorbisCommentField, CaseInsensitiveStringComparer>::setValue(field, value); return FieldMapBasedTag<VorbisComment>::setValue(field, value);
} }
} }
string VorbisComment::fieldId(KnownField field) const VorbisComment::IdentifierType VorbisComment::internallyGetFieldId(KnownField field) const
{ {
using namespace VorbisCommentIds; using namespace VorbisCommentIds;
switch(field) { switch(field) {
@ -70,7 +70,7 @@ string VorbisComment::fieldId(KnownField field) const
} }
} }
KnownField VorbisComment::knownField(const string &id) const KnownField VorbisComment::internallyGetKnownField(const IdentifierType &id) const
{ {
using namespace VorbisCommentIds; using namespace VorbisCommentIds;
static const map<string, KnownField> fieldMap({ static const map<string, KnownField> fieldMap({
@ -145,7 +145,7 @@ void VorbisComment::internalParse(StreamType &stream, uint64 maxSize, VorbisComm
// read fields // read fields
try { try {
field.parse(stream, maxSize); field.parse(stream, maxSize);
fields().insert(pair<fieldType::identifierType, fieldType>(fieldId, field)); fields().emplace(fieldId, field);
} catch(const TruncatedDataException &) { } catch(const TruncatedDataException &) {
addNotifications(field); addNotifications(field);
throw; throw;

View File

@ -12,21 +12,33 @@ namespace Media {
class OggIterator; class OggIterator;
class VorbisComment; class VorbisComment;
class TAG_PARSER_EXPORT VorbisComment : public FieldMapBasedTag<VorbisCommentField, CaseInsensitiveStringComparer> /*!
* \brief Defines traits for the TagField implementation of the VorbisComment class.
*/
template <>
class TAG_PARSER_EXPORT FieldMapBasedTagTraits<VorbisComment>
{ {
public:
typedef VorbisCommentField FieldType;
typedef CaseInsensitiveStringComparer Compare;
};
class TAG_PARSER_EXPORT VorbisComment : public FieldMapBasedTag<VorbisComment>
{
friend class FieldMapBasedTag<VorbisComment>;
public: public:
VorbisComment(); VorbisComment();
static constexpr TagType tagType = TagType::VorbisComment; static constexpr TagType tagType = TagType::VorbisComment;
TagType type() const; static constexpr const char *tagName = "Vorbis comment";
const char *typeName() const; static constexpr TagTextEncoding defaultTextEncoding = TagTextEncoding::Utf8;
TagTextEncoding proposedTextEncoding() const;
bool canEncodingBeUsed(TagTextEncoding encoding) const; bool canEncodingBeUsed(TagTextEncoding encoding) const;
using FieldMapBasedTag<VorbisComment>::value;
const TagValue &value(KnownField field) const; const TagValue &value(KnownField field) const;
using FieldMapBasedTag<VorbisComment>::setValue;
bool setValue(KnownField field, const TagValue &value); bool setValue(KnownField field, const TagValue &value);
std::string fieldId(KnownField field) const;
KnownField knownField(const std::string &id) const;
void parse(OggIterator &iterator, VorbisCommentFlags flags = VorbisCommentFlags::None); void parse(OggIterator &iterator, VorbisCommentFlags flags = VorbisCommentFlags::None);
void parse(std::istream &stream, uint64 maxSize, VorbisCommentFlags flags = VorbisCommentFlags::None); void parse(std::istream &stream, uint64 maxSize, VorbisCommentFlags flags = VorbisCommentFlags::None);
@ -35,6 +47,10 @@ public:
const TagValue &vendor() const; const TagValue &vendor() const;
void setVendor(const TagValue &vendor); void setVendor(const TagValue &vendor);
protected:
IdentifierType internallyGetFieldId(KnownField field) const;
KnownField internallyGetKnownField(const IdentifierType &id) const;
private: private:
template<class StreamType> template<class StreamType>
void internalParse(StreamType &stream, uint64 maxSize, VorbisCommentFlags flags); void internalParse(StreamType &stream, uint64 maxSize, VorbisCommentFlags flags);
@ -49,21 +65,6 @@ private:
inline VorbisComment::VorbisComment() inline VorbisComment::VorbisComment()
{} {}
inline TagType VorbisComment::type() const
{
return TagType::VorbisComment;
}
inline const char *VorbisComment::typeName() const
{
return "Vorbis comment";
}
inline TagTextEncoding VorbisComment::proposedTextEncoding() const
{
return TagTextEncoding::Utf8;
}
inline bool VorbisComment::canEncodingBeUsed(TagTextEncoding encoding) const inline bool VorbisComment::canEncodingBeUsed(TagTextEncoding encoding) const
{ {
return encoding == TagTextEncoding::Utf8; return encoding == TagTextEncoding::Utf8;

View File

@ -38,7 +38,7 @@ VorbisCommentField::VorbisCommentField()
/*! /*!
* \brief Constructs a new Vorbis comment with the specified \a id and \a value. * \brief Constructs a new Vorbis comment with the specified \a id and \a value.
*/ */
VorbisCommentField::VorbisCommentField(const identifierType &id, const TagValue &value) : VorbisCommentField::VorbisCommentField(const IdentifierType &id, const TagValue &value) :
TagField<VorbisCommentField>(id, value) TagField<VorbisCommentField>(id, value)
{} {}

View File

@ -41,18 +41,8 @@ template <>
class TAG_PARSER_EXPORT TagFieldTraits<VorbisCommentField> class TAG_PARSER_EXPORT TagFieldTraits<VorbisCommentField>
{ {
public: public:
/*! typedef std::string IdentifierType;
* \brief Fields in a Vorbis comment are identified by 32-bit unsigned integers. typedef uint32 TypeInfoType;
*/
typedef std::string identifierType;
/*!
* \brief The type info is stored using 32-bit unsigned integers.
*/
typedef uint32 typeInfoType;
/*!
* \brief The implementation type is VorbisCommentField.
*/
typedef VorbisCommentField implementationType;
}; };
class OggIterator; class OggIterator;
@ -63,7 +53,7 @@ class TAG_PARSER_EXPORT VorbisCommentField : public TagField<VorbisCommentField>
public: public:
VorbisCommentField(); VorbisCommentField();
VorbisCommentField(const identifierType &id, const TagValue &value); VorbisCommentField(const IdentifierType &id, const TagValue &value);
void parse(OggIterator &iterator); void parse(OggIterator &iterator);
void parse(OggIterator &iterator, uint64 &maxSize); void parse(OggIterator &iterator, uint64 &maxSize);