diff --git a/aac/aaccodebook.cpp b/aac/aaccodebook.cpp index 653f049..057f4c5 100644 --- a/aac/aaccodebook.cpp +++ b/aac/aaccodebook.cpp @@ -1,7 +1,13 @@ #include "./aaccodebook.h" +/*! + * \file aaccodebook.cpp + * \remarks The AAC parser is still WIP. It does not work yet and its API/ABI may change even in patch releases. + */ + namespace TagParser { +// clang-format off const AacHcb *const aacHcbTable[] = { 0, aacHcb1Step1, aacHcb2Step1, 0, aacHcb4Step1, 0, aacHcb6Step1, 0, aacHcb8Step1, 0, aacHcb10Step1, aacHcb11Step1 }; @@ -2614,5 +2620,6 @@ const sbyte tHuffmanNoiseBal30dB[24][2] = { { -61, -60 }, { 18, 21 }, { 19, 20 }, { -59, -58 }, { -57, -56 }, { 22, 23 }, { -55, -54 }, { -53, -52 } }; +// clang-format on } diff --git a/aac/aacframe.cpp b/aac/aacframe.cpp index a78581d..de12d9d 100644 --- a/aac/aacframe.cpp +++ b/aac/aacframe.cpp @@ -17,8 +17,7 @@ using namespace IoUtilities; /*! * \file aacframe.cpp - * \remarks Nothing of this code has been tested yet. It is currently not used - * in the rest of the library. + * \remarks The AAC parser is still WIP. It does not work yet and its API/ABI may change even in patch releases. */ namespace TagParser { diff --git a/adts/adtsstream.h b/adts/adtsstream.h index e3483da..0137ccb 100644 --- a/adts/adtsstream.h +++ b/adts/adtsstream.h @@ -11,12 +11,12 @@ class TAG_PARSER_EXPORT AdtsStream : public AbstractTrack { public: AdtsStream(std::iostream &stream, uint64 startOffset); - ~AdtsStream(); + ~AdtsStream() override; - TrackType type() const; + TrackType type() const override; protected: - void internalParseHeader(Diagnostics &diag); + void internalParseHeader(Diagnostics &diag) override; private: AdtsFrame m_firstFrame; diff --git a/fieldbasedtag.h b/fieldbasedtag.h index ee74d6c..5729229 100644 --- a/fieldbasedtag.h +++ b/fieldbasedtag.h @@ -368,8 +368,9 @@ int FieldMapBasedTag::insertFields(const FieldMapBasedTagread(buffer, 4); - header.parseHeader(buffer); - - // remember start offset - const auto startOffset = m_istream->tellg(); - - // parse relevant meta data - switch(static_cast(header.type())) { - case FlacMetaDataBlockType::StreamInfo: - if(header.dataSize() >= 0x22) { - m_istream->read(buffer, 0x22); - FlacMetaDataBlockStreamInfo streamInfo; - streamInfo.parse(buffer); - m_channelCount = streamInfo.channelCount(); - m_samplingFrequency = streamInfo.samplingFrequency(); - m_sampleCount = streamInfo.totalSampleCount(); - m_bitsPerSample = streamInfo.bitsPerSample(); - m_duration = TimeSpan::fromSeconds(static_cast(m_sampleCount) / m_samplingFrequency); - } else { - diag.emplace_back(DiagLevel::Critical, "\"METADATA_BLOCK_STREAMINFO\" is truncated and will be ignored.", context); - } - break; - - case FlacMetaDataBlockType::VorbisComment: - // parse Vorbis comment - // if more than one comment exist, simply thread those comments as one - if(!m_vorbisComment) { - m_vorbisComment = make_unique(); - } - try { - m_vorbisComment->parse(*m_istream, header.dataSize(), VorbisCommentFlags::NoSignature | VorbisCommentFlags::NoFramingByte, diag); - } catch(const Failure &) { - // error is logged via notifications, just continue with the next metadata block - } - break; - - case FlacMetaDataBlockType::Picture: - try { - // parse the cover - VorbisCommentField coverField; - coverField.setId(m_vorbisComment->fieldId(KnownField::Cover)); - FlacMetaDataBlockPicture picture(coverField.value()); - picture.parse(*m_istream, header.dataSize()); - coverField.setTypeInfo(picture.pictureType()); - - if(coverField.value().isEmpty()) { - diag.emplace_back(DiagLevel::Warning, "\"METADATA_BLOCK_PICTURE\" contains no picture.", context); - } else { - // add the cover to the Vorbis comment - if(!m_vorbisComment) { - // create one if none exists yet - m_vorbisComment = make_unique(); - m_vorbisComment->setVendor(TagValue(APP_NAME " v" APP_VERSION, TagTextEncoding::Utf8)); - } - m_vorbisComment->fields().insert(make_pair(coverField.id(), move(coverField))); - } - - } catch(const TruncatedDataException &) { - diag.emplace_back(DiagLevel::Critical, "\"METADATA_BLOCK_PICTURE\" is truncated and will be ignored.", context); - } - break; - - case FlacMetaDataBlockType::Padding: - m_paddingSize += 4 + header.dataSize(); - break; - - default: - ; - } - - // seek to next block - m_istream->seekg(startOffset + static_cast(header.dataSize())); - - // TODO: check first FLAC frame - } - - m_streamOffset = m_istream->tellg(); - - } else { + if(m_reader.readUInt32BE() != 0x664C6143) { diag.emplace_back(DiagLevel::Critical, "Signature (fLaC) not found.", context); throw InvalidDataException(); } + m_format = GeneralMediaFormat::Flac; + + // parse meta data blocks + for(FlacMetaDataBlockHeader header; !header.isLast(); ) { + // parse block header + m_istream->read(buffer, 4); + header.parseHeader(buffer); + + // remember start offset + const auto startOffset = m_istream->tellg(); + + // parse relevant meta data + switch(static_cast(header.type())) { + case FlacMetaDataBlockType::StreamInfo: + if(header.dataSize() >= 0x22) { + m_istream->read(buffer, 0x22); + FlacMetaDataBlockStreamInfo streamInfo; + streamInfo.parse(buffer); + m_channelCount = streamInfo.channelCount(); + m_samplingFrequency = streamInfo.samplingFrequency(); + m_sampleCount = streamInfo.totalSampleCount(); + m_bitsPerSample = streamInfo.bitsPerSample(); + m_duration = TimeSpan::fromSeconds(static_cast(m_sampleCount) / m_samplingFrequency); + } else { + diag.emplace_back(DiagLevel::Critical, "\"METADATA_BLOCK_STREAMINFO\" is truncated and will be ignored.", context); + } + break; + + case FlacMetaDataBlockType::VorbisComment: + // parse Vorbis comment + // if more than one comment exist, simply thread those comments as one + if(!m_vorbisComment) { + m_vorbisComment = make_unique(); + } + try { + m_vorbisComment->parse(*m_istream, header.dataSize(), VorbisCommentFlags::NoSignature | VorbisCommentFlags::NoFramingByte, diag); + } catch(const Failure &) { + // error is logged via notifications, just continue with the next metadata block + } + break; + + case FlacMetaDataBlockType::Picture: + try { + // parse the cover + VorbisCommentField coverField; + coverField.setId(m_vorbisComment->fieldId(KnownField::Cover)); + FlacMetaDataBlockPicture picture(coverField.value()); + picture.parse(*m_istream, header.dataSize()); + coverField.setTypeInfo(picture.pictureType()); + + if(coverField.value().isEmpty()) { + diag.emplace_back(DiagLevel::Warning, "\"METADATA_BLOCK_PICTURE\" contains no picture.", context); + } else { + // add the cover to the Vorbis comment + if(!m_vorbisComment) { + // create one if none exists yet + m_vorbisComment = make_unique(); + m_vorbisComment->setVendor(TagValue(APP_NAME " v" APP_VERSION, TagTextEncoding::Utf8)); + } + m_vorbisComment->fields().insert(make_pair(coverField.id(), move(coverField))); + } + + } catch(const TruncatedDataException &) { + diag.emplace_back(DiagLevel::Critical, "\"METADATA_BLOCK_PICTURE\" is truncated and will be ignored.", context); + } + break; + + case FlacMetaDataBlockType::Padding: + m_paddingSize += 4 + header.dataSize(); + break; + + default: + ; + } + + // seek to next block + m_istream->seekg(startOffset + static_cast(header.dataSize())); + + // TODO: check first FLAC frame + } + + m_streamOffset = m_istream->tellg(); } /*! @@ -210,41 +207,43 @@ uint32 FlacStream::makeHeader(ostream &outputStream, Diagnostics &diag) } // write Vorbis comment - if(m_vorbisComment) { - // leave 4 bytes space for the "METADATA_BLOCK_HEADER" + if(!m_vorbisComment) { + return lastStartOffset; + } + // leave 4 bytes space for the "METADATA_BLOCK_HEADER" + lastStartOffset = outputStream.tellp(); + outputStream.write(copy.buffer(), 4); + + // determine cover ID since covers must be written separately + const auto coverId = m_vorbisComment->fieldId(KnownField::Cover); + + // write Vorbis comment + m_vorbisComment->make(outputStream, VorbisCommentFlags::NoSignature | VorbisCommentFlags::NoFramingByte | VorbisCommentFlags::NoCovers, diag); + + // write "METADATA_BLOCK_HEADER" + const uint32 endOffset = outputStream.tellp(); + FlacMetaDataBlockHeader header; + header.setType(FlacMetaDataBlockType::VorbisComment); + header.setDataSize(endOffset - lastStartOffset - 4); + header.setLast(!m_vorbisComment->hasField(coverId)); + outputStream.seekp(lastStartOffset); + header.makeHeader(outputStream); + outputStream.seekp(endOffset); + + // write cover fields separately as "METADATA_BLOCK_PICTURE" + if(header.isLast()) { + return lastStartOffset; + } + header.setType(FlacMetaDataBlockType::Picture); + const auto coverFields = m_vorbisComment->fields().equal_range(coverId); + for(auto i = coverFields.first; i != coverFields.second; ) { lastStartOffset = outputStream.tellp(); - outputStream.write(copy.buffer(), 4); - - // determine cover ID since covers must be written separately - const auto coverId = m_vorbisComment->fieldId(KnownField::Cover); - - // write Vorbis comment - m_vorbisComment->make(outputStream, VorbisCommentFlags::NoSignature | VorbisCommentFlags::NoFramingByte | VorbisCommentFlags::NoCovers, diag); - - // write "METADATA_BLOCK_HEADER" - const uint32 endOffset = outputStream.tellp(); - FlacMetaDataBlockHeader header; - header.setType(FlacMetaDataBlockType::VorbisComment); - header.setDataSize(endOffset - lastStartOffset - 4); - header.setLast(!m_vorbisComment->hasField(coverId)); - outputStream.seekp(lastStartOffset); + FlacMetaDataBlockPicture pictureBlock(i->second.value()); + pictureBlock.setPictureType(i->second.typeInfo()); + header.setDataSize(pictureBlock.requiredSize()); + header.setLast(++i == coverFields.second); header.makeHeader(outputStream); - outputStream.seekp(endOffset); - - // write cover fields separately as "METADATA_BLOCK_PICTURE" - if(!header.isLast()) { - header.setType(FlacMetaDataBlockType::Picture); - const auto coverFields = m_vorbisComment->fields().equal_range(coverId); - for(auto i = coverFields.first; i != coverFields.second; ) { - lastStartOffset = outputStream.tellp(); - FlacMetaDataBlockPicture pictureBlock(i->second.value()); - pictureBlock.setPictureType(i->second.typeInfo()); - header.setDataSize(pictureBlock.requiredSize()); - header.setLast(++i == coverFields.second); - header.makeHeader(outputStream); - pictureBlock.make(outputStream); - } - } + pictureBlock.make(outputStream); } return lastStartOffset; diff --git a/genericcontainer.h b/genericcontainer.h index f01a83d..18d4f8d 100644 --- a/genericcontainer.h +++ b/genericcontainer.h @@ -27,30 +27,30 @@ class TAG_PARSER_EXPORT GenericContainer : public AbstractContainer public: GenericContainer(FileInfoType &fileInfo, uint64 startOffset); - ~GenericContainer(); + ~GenericContainer() override; void validateElementStructure(Diagnostics &diag, uint64 *paddingSize = nullptr); FileInfoType &fileInfo() const; ElementType *firstElement() const; const std::vector > &additionalElements() const; std::vector > &additionalElements(); - TagType *tag(std::size_t index); - std::size_t tagCount() const; - TrackType *track(std::size_t index); + TagType *tag(std::size_t index) override; + std::size_t tagCount() const override; + TrackType *track(std::size_t index) override; TrackType *trackById(uint64 id); - std::size_t trackCount() const; + std::size_t trackCount() const override; const std::vector > &tags() const; std::vector > &tags(); const std::vector > &tracks() const; std::vector > &tracks(); - TagType *createTag(const TagTarget &target = TagTarget()); - bool removeTag(Tag *tag); - void removeAllTags(); + TagType *createTag(const TagTarget &target = TagTarget()) override; + bool removeTag(Tag *tag) override; + void removeAllTags() override; bool addTrack(TrackType *track); - bool removeTrack(AbstractTrack *track); - void removeAllTracks(); - void reset(); + bool removeTrack(AbstractTrack *track) override; + void removeAllTracks() override; + void reset() override; typedef FileInfoType ContainerFileInfoType; typedef TagType ContainerTagType; diff --git a/id3/id3v1tag.cpp b/id3/id3v1tag.cpp index d90c3f0..79a581b 100644 --- a/id3/id3v1tag.cpp +++ b/id3/id3v1tag.cpp @@ -100,11 +100,12 @@ void Id3v1Tag::make(ostream &stream, Diagnostics &diag) buffer[1] = 0x0; // track nr buffer[2] = 0x0; // genre // track - try { - if(!m_trackPos.isEmpty() && m_trackPos.type() == TagDataType::PositionInSet) - buffer[1] = m_trackPos.toPositionInSet().position(); - } catch(const ConversionException &) { - diag.emplace_back(DiagLevel::Warning, "Track position field can not be set because given value can not be converted appropriately.", context); + if(!m_trackPos.isEmpty() && m_trackPos.type() == TagDataType::PositionInSet) { + try { + buffer[1] = m_trackPos.toPositionInSet().position(); + } catch(const ConversionException &) { + diag.emplace_back(DiagLevel::Warning, "Track position field can not be set because given value can not be converted appropriately.", context); + } } // genre try { diff --git a/id3/id3v1tag.h b/id3/id3v1tag.h index a6fe6ec..19a2bc0 100644 --- a/id3/id3v1tag.h +++ b/id3/id3v1tag.h @@ -15,17 +15,17 @@ public: static constexpr TagType tagType = TagType::Id3v1Tag; static constexpr const char *tagName = "ID3v1 tag"; - TagType type() const; - const char *typeName() const; - bool canEncodingBeUsed(TagTextEncoding encoding) const; - const TagValue &value(KnownField value) const; - bool setValue(KnownField field, const TagValue &value); + TagType type() const override; + const char *typeName() const override; + bool canEncodingBeUsed(TagTextEncoding encoding) const override; + const TagValue &value(KnownField value) const override; + bool setValue(KnownField field, const TagValue &value) override; bool setValueConsideringTypeInfo(KnownField field, const TagValue &value, const std::string &typeInfo); - bool hasField(KnownField field) const; - void removeAllFields(); - unsigned int fieldCount() const; - bool supportsField(KnownField field) const; - void ensureTextValuesAreProperlyEncoded(); + bool hasField(KnownField field) const override; + void removeAllFields() override; + unsigned int fieldCount() const override; + bool supportsField(KnownField field) const override; + void ensureTextValuesAreProperlyEncoded() override; void parse(std::istream &sourceStream, Diagnostics &diag); void make(std::ostream &targetStream, Diagnostics &diag); diff --git a/id3/id3v2tag.cpp b/id3/id3v2tag.cpp index c4fc860..caf0226 100644 --- a/id3/id3v2tag.cpp +++ b/id3/id3v2tag.cpp @@ -169,98 +169,99 @@ void Id3v2Tag::parse(istream &stream, const uint64 maximalSize, Diagnostics &dia } // read signature: ID3 - if(reader.readUInt24BE() == 0x494433u) { - // read header data - byte majorVersion = reader.readByte(); - byte revisionVersion = reader.readByte(); - setVersion(majorVersion, revisionVersion); - m_flags = reader.readByte(); - m_sizeExcludingHeader = reader.readSynchsafeUInt32BE(); - m_size = 10 + m_sizeExcludingHeader; - if(m_sizeExcludingHeader == 0) { - diag.emplace_back(DiagLevel::Warning, "ID3v2 tag seems to be empty.", context); - } else { - // check if the version - if(!isVersionSupported()) { - diag.emplace_back(DiagLevel::Critical, "The ID3v2 tag couldn't be parsed, because its version is not supported.", context); - throw VersionNotSupportedException(); - } - - // read extended header (if present) - if(hasExtendedHeader()) { - if(maximalSize && maximalSize < 14) { - diag.emplace_back(DiagLevel::Critical, "Extended header denoted but not present.", context); - throw TruncatedDataException(); - } - m_extendedHeaderSize = reader.readSynchsafeUInt32BE(); - if(m_extendedHeaderSize < 6 || m_extendedHeaderSize > m_sizeExcludingHeader || (maximalSize && maximalSize < (10 + m_extendedHeaderSize))) { - diag.emplace_back(DiagLevel::Critical, "Extended header is invalid/truncated.", context); - throw TruncatedDataException(); - } - stream.seekg(m_extendedHeaderSize - 4, ios_base::cur); - } - - // how many bytes remain for frames and padding? - uint32 bytesRemaining = m_sizeExcludingHeader - m_extendedHeaderSize; - if(maximalSize && bytesRemaining > maximalSize) { - bytesRemaining = maximalSize; - diag.emplace_back(DiagLevel::Critical, "Frames are truncated.", context); - } - - // read frames - auto pos = stream.tellg(); - Id3v2Frame frame; - while(bytesRemaining) { - // seek to next frame - stream.seekg(pos); - // parse frame - try { - frame.parse(reader, majorVersion, bytesRemaining, diag); - if(frame.id()) { - // add frame if parsing was successfull - if(Id3v2FrameIds::isTextFrame(frame.id()) && fields().count(frame.id()) == 1) { - diag.emplace_back(DiagLevel::Warning, "The text frame " % frame.frameIdString() + " exists more than once.", context); - } - fields().insert(make_pair(frame.id(), frame)); - } - } catch(const NoDataFoundException &) { - if(frame.hasPaddingReached()) { - m_paddingSize = startOffset + m_size - pos; - break; - } - } catch(const Failure &) { - } - - // calculate next frame offset - if(frame.totalSize() <= bytesRemaining) { - pos += frame.totalSize(); - bytesRemaining -= frame.totalSize(); - } else { - pos += bytesRemaining; - bytesRemaining = 0; - } - } - - // check for extended header - if(hasFooter()) { - if(maximalSize && m_size + 10 < maximalSize) { - // the footer does not provide additional information, just check the signature - stream.seekg(startOffset + (m_size += 10)); - if(reader.readUInt24LE() != 0x494433u) { - diag.emplace_back(DiagLevel::Critical, "Footer signature is invalid.", context); - } - // skip remaining footer - stream.seekg(7, ios_base::cur); - } else { - diag.emplace_back(DiagLevel::Critical, "Footer denoted but not present.", context); - throw TruncatedDataException(); - } - } - } - } else { + if(reader.readUInt24BE() != 0x494433u) { diag.emplace_back(DiagLevel::Critical, "Signature is invalid.", context); throw InvalidDataException(); } + // read header data + byte majorVersion = reader.readByte(); + byte revisionVersion = reader.readByte(); + setVersion(majorVersion, revisionVersion); + m_flags = reader.readByte(); + m_sizeExcludingHeader = reader.readSynchsafeUInt32BE(); + m_size = 10 + m_sizeExcludingHeader; + if(m_sizeExcludingHeader == 0) { + diag.emplace_back(DiagLevel::Warning, "ID3v2 tag seems to be empty.", context); + return; + } + + // check if the version + if(!isVersionSupported()) { + diag.emplace_back(DiagLevel::Critical, "The ID3v2 tag couldn't be parsed, because its version is not supported.", context); + throw VersionNotSupportedException(); + } + + // read extended header (if present) + if(hasExtendedHeader()) { + if(maximalSize && maximalSize < 14) { + diag.emplace_back(DiagLevel::Critical, "Extended header denoted but not present.", context); + throw TruncatedDataException(); + } + m_extendedHeaderSize = reader.readSynchsafeUInt32BE(); + if(m_extendedHeaderSize < 6 || m_extendedHeaderSize > m_sizeExcludingHeader || (maximalSize && maximalSize < (10 + m_extendedHeaderSize))) { + diag.emplace_back(DiagLevel::Critical, "Extended header is invalid/truncated.", context); + throw TruncatedDataException(); + } + stream.seekg(m_extendedHeaderSize - 4, ios_base::cur); + } + + // how many bytes remain for frames and padding? + uint32 bytesRemaining = m_sizeExcludingHeader - m_extendedHeaderSize; + if(maximalSize && bytesRemaining > maximalSize) { + bytesRemaining = maximalSize; + diag.emplace_back(DiagLevel::Critical, "Frames are truncated.", context); + } + + // read frames + auto pos = stream.tellg(); + Id3v2Frame frame; + while(bytesRemaining) { + // seek to next frame + stream.seekg(pos); + // parse frame + try { + frame.parse(reader, majorVersion, bytesRemaining, diag); + if(frame.id()) { + // add frame if parsing was successfull + if(Id3v2FrameIds::isTextFrame(frame.id()) && fields().count(frame.id()) == 1) { + diag.emplace_back(DiagLevel::Warning, "The text frame " % frame.frameIdString() + " exists more than once.", context); + } + fields().insert(make_pair(frame.id(), frame)); + } + } catch(const NoDataFoundException &) { + if(frame.hasPaddingReached()) { + m_paddingSize = startOffset + m_size - pos; + break; + } + } catch(const Failure &) { + } + + // calculate next frame offset + if(frame.totalSize() <= bytesRemaining) { + pos += frame.totalSize(); + bytesRemaining -= frame.totalSize(); + } else { + pos += bytesRemaining; + bytesRemaining = 0; + } + } + + // check for extended header + if(!hasFooter()) { + return; + } + if(maximalSize && m_size + 10 < maximalSize) { + // the footer does not provide additional information, just check the signature + stream.seekg(startOffset + (m_size += 10)); + if(reader.readUInt24LE() != 0x494433u) { + diag.emplace_back(DiagLevel::Critical, "Footer signature is invalid.", context); + } + // skip remaining footer + stream.seekg(7, ios_base::cur); + } else { + diag.emplace_back(DiagLevel::Critical, "Footer denoted but not present.", context); + throw TruncatedDataException(); + } } /*! diff --git a/id3/id3v2tag.h b/id3/id3v2tag.h index d336818..3e19144 100644 --- a/id3/id3v2tag.h +++ b/id3/id3v2tag.h @@ -73,10 +73,10 @@ public: static constexpr TagType tagType = TagType::Id3v2Tag; static constexpr const char *tagName = "ID3v2 tag"; static constexpr TagTextEncoding defaultTextEncoding = TagTextEncoding::Utf16LittleEndian; - TagTextEncoding proposedTextEncoding() const; - bool canEncodingBeUsed(TagTextEncoding encoding) const; - bool supportsDescription(KnownField field) const; - bool supportsMimeType(KnownField field) const; + TagTextEncoding proposedTextEncoding() const override; + bool canEncodingBeUsed(TagTextEncoding encoding) const override; + bool supportsDescription(KnownField field) const override; + bool supportsMimeType(KnownField field) const override; void parse(std::istream &sourceStream, const uint64 maximalSize, Diagnostics &diag); Id3v2TagMaker prepareMaking(Diagnostics &diag); diff --git a/localeawarestring.cpp b/localeawarestring.cpp index 92d3758..2bdaa38 100644 --- a/localeawarestring.cpp +++ b/localeawarestring.cpp @@ -1,10 +1,7 @@ -#include "localeawarestring.h" +#include "./localeawarestring.h" using namespace std; namespace TagParser { - - } // namespace Media - diff --git a/mp4/mp4container.cpp b/mp4/mp4container.cpp index 1b25ae8..ac65968 100644 --- a/mp4/mp4container.cpp +++ b/mp4/mp4container.cpp @@ -75,42 +75,43 @@ void Mp4Container::internalParseHeader(Diagnostics &diag) //const string context("parsing header of MP4 container"); will be used when generating notifications m_firstElement = make_unique(*this, startOffset()); m_firstElement->parse(diag); - Mp4Atom *ftypAtom = m_firstElement->siblingByIdIncludingThis(Mp4AtomIds::FileType, diag); - if(ftypAtom) { - stream().seekg(static_cast(ftypAtom->dataOffset())); - m_doctype = reader().readString(4); - m_version = reader().readUInt32BE(); - } else { + auto *const ftypAtom = m_firstElement->siblingByIdIncludingThis(Mp4AtomIds::FileType, diag); + if(!ftypAtom) { m_doctype.clear(); m_version = 0; + return; } + stream().seekg(static_cast(ftypAtom->dataOffset())); + m_doctype = reader().readString(4); + m_version = reader().readUInt32BE(); } void Mp4Container::internalParseTags(Diagnostics &diag) { const string context("parsing tags of MP4 container"); - if(Mp4Atom *udtaAtom = firstElement()->subelementByPath(diag, Mp4AtomIds::Movie, Mp4AtomIds::UserData)) { - Mp4Atom *metaAtom = udtaAtom->childById(Mp4AtomIds::Meta, diag); - bool surplusMetaAtoms = false; - while(metaAtom) { - metaAtom->parse(diag); - m_tags.emplace_back(make_unique()); - try { - m_tags.back()->parse(*metaAtom, diag); - } catch(const NoDataFoundException &) { - m_tags.pop_back(); - } - metaAtom = metaAtom->siblingById(Mp4AtomIds::Meta, diag); - if(metaAtom) { - surplusMetaAtoms = true; - } - if(!m_tags.empty()) { - break; - } + auto *const udtaAtom = firstElement()->subelementByPath(diag, Mp4AtomIds::Movie, Mp4AtomIds::UserData); + if(!udtaAtom) { + return; + } + auto *metaAtom = udtaAtom->childById(Mp4AtomIds::Meta, diag); + bool surplusMetaAtoms = false; + while(metaAtom) { + metaAtom->parse(diag); + m_tags.emplace_back(make_unique()); + try { + m_tags.back()->parse(*metaAtom, diag); + } catch(const NoDataFoundException &) { + m_tags.pop_back(); } - if(surplusMetaAtoms) { - diag.emplace_back(DiagLevel::Warning, "udta atom contains multiple meta atoms. Surplus meta atoms will be ignored.", context); + if((metaAtom = metaAtom->siblingById(Mp4AtomIds::Meta, diag))) { + surplusMetaAtoms = true; } + if(!m_tags.empty()) { + break; + } + } + if(surplusMetaAtoms) { + diag.emplace_back(DiagLevel::Warning, "udta atom contains multiple meta atoms. Surplus meta atoms will be ignored.", context); } } @@ -217,7 +218,7 @@ void Mp4Container::internalParseTracks(Diagnostics &diag) void Mp4Container::internalMakeFile(Diagnostics &diag, AbortableProgressFeedback &progress) { static const string context("making MP4 container"); - progress.updateStep("Calculating atom sizes and padding ...", 0); + progress.updateStep("Calculating atom sizes and padding ..."); // basic validation of original file if(!isHeaderParsed()) { @@ -880,28 +881,28 @@ void Mp4Container::updateOffsets(const std::vector &oldMdatOffsets, const tfhdAtom = tfhdAtom->siblingById(Mp4AtomIds::TrackFragmentHeader, diag)) { tfhdAtom->parse(diag); ++tfhdAtomCount; - if(tfhdAtom->dataSize() >= 8) { - stream().seekg(static_cast(tfhdAtom->dataOffset()) + 1); - uint32 flags = reader().readUInt24BE(); - if(flags & 1) { - if(tfhdAtom->dataSize() >= 16) { - stream().seekg(4, ios_base::cur); // skip track ID - uint64 off = reader().readUInt64BE(); - for(auto iOld = oldMdatOffsets.cbegin(), iNew = newMdatOffsets.cbegin(), end = oldMdatOffsets.cend(); - iOld != end; ++iOld, ++iNew) { - if(off >= static_cast(*iOld)) { - off += (*iNew - *iOld); - stream().seekp(static_cast(tfhdAtom->dataOffset()) + 8); - writer().writeUInt64BE(off); - break; - } - } - } else { - diag.emplace_back(DiagLevel::Warning, "tfhd atom (denoting base-data-offset-present) is truncated.", context); - } - } - } else { + if(tfhdAtom->dataSize() < 8) { diag.emplace_back(DiagLevel::Warning, "tfhd atom is truncated.", context); + continue; + } + stream().seekg(static_cast(tfhdAtom->dataOffset()) + 1); + uint32 flags = reader().readUInt24BE(); + if(!(flags & 1)) { + continue; + } + if(tfhdAtom->dataSize() < 16) { + diag.emplace_back(DiagLevel::Warning, "tfhd atom (denoting base-data-offset-present) is truncated.", context); + continue; + } + stream().seekg(4, ios_base::cur); // skip track ID + uint64 off = reader().readUInt64BE(); + for(auto iOld = oldMdatOffsets.cbegin(), iNew = newMdatOffsets.cbegin(), end = oldMdatOffsets.cend(); iOld != end; ++iOld, ++iNew) { + if(off >= static_cast(*iOld)) { + off += (*iNew - *iOld); + stream().seekp(static_cast(tfhdAtom->dataOffset()) + 8); + writer().writeUInt64BE(off); + break; + } } } switch(tfhdAtomCount) { diff --git a/mp4/mp4tag.h b/mp4/mp4tag.h index a9233af..bb237aa 100644 --- a/mp4/mp4tag.h +++ b/mp4/mp4tag.h @@ -108,29 +108,29 @@ public: static constexpr TagType tagType = TagType::Mp4Tag; static constexpr const char *tagName = "MP4/iTunes tag"; static constexpr TagTextEncoding defaultTextEncoding = TagTextEncoding::Utf8; - bool canEncodingBeUsed(TagTextEncoding encoding) const; + bool canEncodingBeUsed(TagTextEncoding encoding) const override; - bool supportsField(KnownField field) const; + bool supportsField(KnownField field) const override; using FieldMapBasedTag::value; - const TagValue &value(KnownField value) const; + const TagValue &value(KnownField value) const override; using FieldMapBasedTag::values; - std::vector values(KnownField field) const; + std::vector values(KnownField field) const override; #ifdef LEGACY_API const TagValue &value(const std::string mean, const std::string name) const; #endif const TagValue &value(const std::string &mean, const std::string &name) const; const TagValue &value(const char *mean, const char *name) const; using FieldMapBasedTag::setValue; - bool setValue(KnownField field, const TagValue &value); + bool setValue(KnownField field, const TagValue &value) override; using FieldMapBasedTag::setValues; - bool setValues(KnownField field, const std::vector &values); + bool setValues(KnownField field, const std::vector &values) override; #ifdef LEGACY_API bool setValue(const std::string mean, const std::string name, const TagValue &value); #endif bool setValue(const std::string &mean, const std::string &name, const TagValue &value); bool setValue(const char *mean, const char *name, const TagValue &value); using FieldMapBasedTag::hasField; - bool hasField(KnownField value) const; + bool hasField(KnownField value) const override; void parse(Mp4Atom &metaAtom, Diagnostics &diag); Mp4TagMaker prepareMaking(Diagnostics &diag); diff --git a/mp4/mp4track.cpp b/mp4/mp4track.cpp index 35da101..1f5b64f 100644 --- a/mp4/mp4track.cpp +++ b/mp4/mp4track.cpp @@ -1551,7 +1551,7 @@ void Mp4Track::internalParseHeader(Diagnostics &diag) m_istream->seekg(m_stsdAtom->dataOffset() + 4); // seek to beg, skip size, name, version and flags uint32 entryCount = reader.readUInt32BE(); Mp4Atom *esDescParentAtom = nullptr; - if(entryCount > 0) { + if(entryCount) { try { for(Mp4Atom *codecConfigContainerAtom = m_stsdAtom->firstChild(); codecConfigContainerAtom; codecConfigContainerAtom = codecConfigContainerAtom->nextSibling()) { codecConfigContainerAtom->parse(diag); @@ -1703,11 +1703,11 @@ void Mp4Track::internalParseHeader(Diagnostics &diag) ; } } - } catch(Failure &) { + } catch(const Failure &) { } } } - } catch (Failure &) { + } catch (const Failure &) { diag.emplace_back(DiagLevel::Critical, "Unable to parse child atoms of \"stsd\"-atom.", context); } } diff --git a/mp4/mp4track.h b/mp4/mp4track.h index 56a5ac8..1b88f82 100644 --- a/mp4/mp4track.h +++ b/mp4/mp4track.h @@ -121,8 +121,8 @@ class TAG_PARSER_EXPORT Mp4Track : public AbstractTrack { public: Mp4Track(Mp4Atom &trakAtom); - ~Mp4Track(); - TrackType type() const; + ~Mp4Track() override; + TrackType type() const override; // getter methods specific for MP4 tracks Mp4Atom &trakAtom(); @@ -160,7 +160,7 @@ public: static void addInfo(const AvcConfiguration &avcConfig, AbstractTrack &track); protected: - void internalParseHeader(Diagnostics &diag); + void internalParseHeader(Diagnostics &diag) override; private: // private helper methods diff --git a/mpegaudio/mpegaudioframestream.h b/mpegaudio/mpegaudioframestream.h index 9a93613..5d40f30 100644 --- a/mpegaudio/mpegaudioframestream.h +++ b/mpegaudio/mpegaudioframestream.h @@ -14,14 +14,14 @@ class TAG_PARSER_EXPORT MpegAudioFrameStream : public AbstractTrack { public: MpegAudioFrameStream(std::iostream &stream, uint64 startOffset); - ~MpegAudioFrameStream(); + ~MpegAudioFrameStream() override; - TrackType type() const; + TrackType type() const override; static void addInfo(const MpegAudioFrame &frame, AbstractTrack &track); protected: - void internalParseHeader(Diagnostics &diag); + void internalParseHeader(Diagnostics &diag) override; private: std::list m_frames; diff --git a/ogg/oggstream.h b/ogg/oggstream.h index a3ec882..3098c01 100644 --- a/ogg/oggstream.h +++ b/ogg/oggstream.h @@ -16,13 +16,13 @@ class TAG_PARSER_EXPORT OggStream : public AbstractTrack public: OggStream(OggContainer &container, std::vector::size_type startPage); - ~OggStream(); + ~OggStream() override; - TrackType type() const; + TrackType type() const override; std::size_t startPage() const; protected: - void internalParseHeader(Diagnostics &diag); + void internalParseHeader(Diagnostics &diag) override; private: void calculateDurationViaSampleCount(uint16 preSkip = 0); diff --git a/vorbis/vorbiscomment.h b/vorbis/vorbiscomment.h index d9189a7..1273cdf 100644 --- a/vorbis/vorbiscomment.h +++ b/vorbis/vorbiscomment.h @@ -34,12 +34,12 @@ public: static constexpr TagType tagType = TagType::VorbisComment; static constexpr const char *tagName = "Vorbis comment"; static constexpr TagTextEncoding defaultTextEncoding = TagTextEncoding::Utf8; - bool canEncodingBeUsed(TagTextEncoding encoding) const; + bool canEncodingBeUsed(TagTextEncoding encoding) const override; using FieldMapBasedTag::value; - const TagValue &value(KnownField field) const; + const TagValue &value(KnownField field) const override; using FieldMapBasedTag::setValue; - bool setValue(KnownField field, const TagValue &value); + bool setValue(KnownField field, const TagValue &value) override; void parse(OggIterator &iterator, VorbisCommentFlags flags, Diagnostics &diag); void parse(std::istream &stream, uint64 maxSize, VorbisCommentFlags flags, Diagnostics &diag); diff --git a/wav/waveaudiostream.cpp b/wav/waveaudiostream.cpp index 56fcdf2..68b62fd 100644 --- a/wav/waveaudiostream.cpp +++ b/wav/waveaudiostream.cpp @@ -105,51 +105,50 @@ void WaveAudioStream::internalParseHeader(Diagnostics &diag) if(!m_istream) { throw NoDataFoundException(); } - if(m_reader.readUInt32BE() == 0x52494646u) { - m_istream->seekg(m_startOffset + 8); - if(m_reader.readUInt32BE() == 0x57415645u) { - while(!m_dataOffset) { - uint32 segmentId = m_reader.readUInt32BE(); - uint32 restHeaderLen = m_reader.readUInt32LE(); - switch(segmentId) { - case 0x666D7420u: - if(restHeaderLen >= 16u) { - WaveFormatHeader waveHeader; - waveHeader.parse(m_reader); - addInfo(waveHeader, *this); - restHeaderLen -= 16u; - } else { - diag.emplace_back(DiagLevel::Warning, "\"fmt \" segment is truncated.", context); - } - break; - case 0x64617461u: - m_dataOffset = m_istream->tellg(); - m_size = restHeaderLen; - m_sampleCount = m_size / m_chunkSize; - m_duration = TimeSpan::fromSeconds(static_cast(m_sampleCount) / static_cast(m_samplingFrequency)); - break; - default: - ; - } - m_istream->seekg(restHeaderLen, ios_base::cur); - } - } else { - throw NoDataFoundException(); - } - } else { + if(m_reader.readUInt32BE() != 0x52494646u) { throw NoDataFoundException(); } - if(m_format.general == GeneralMediaFormat::Mpeg1Audio && m_dataOffset) { - m_istream->seekg(m_dataOffset); - MpegAudioFrame frame; - frame.parseHeader(m_reader); - MpegAudioFrameStream::addInfo(frame, *this); - m_bitrate = frame.isXingFramefieldPresent() - ? ((static_cast(m_size) * 8.0) / (static_cast(frame.xingFrameCount() * frame.sampleCount()) / static_cast(frame.samplingFrequency())) / 1024.0) - : frame.bitrate(); - m_bytesPerSecond = m_bitrate * 125; - m_duration = TimeSpan::fromSeconds(static_cast(m_size) / (m_bitrate * 128.0)); + m_istream->seekg(m_startOffset + 8); + if(m_reader.readUInt32BE() != 0x57415645u) { + throw NoDataFoundException(); } + while(!m_dataOffset) { + uint32 segmentId = m_reader.readUInt32BE(); + uint32 restHeaderLen = m_reader.readUInt32LE(); + switch(segmentId) { + case 0x666D7420u: + if(restHeaderLen >= 16u) { + WaveFormatHeader waveHeader; + waveHeader.parse(m_reader); + addInfo(waveHeader, *this); + restHeaderLen -= 16u; + } else { + diag.emplace_back(DiagLevel::Warning, "\"fmt \" segment is truncated.", context); + } + break; + case 0x64617461u: + m_dataOffset = m_istream->tellg(); + m_size = restHeaderLen; + m_sampleCount = m_size / m_chunkSize; + m_duration = TimeSpan::fromSeconds(static_cast(m_sampleCount) / static_cast(m_samplingFrequency)); + break; + default: + ; + } + m_istream->seekg(restHeaderLen, ios_base::cur); + } + if(m_format.general != GeneralMediaFormat::Mpeg1Audio || !m_dataOffset) { + return; + } + m_istream->seekg(m_dataOffset); + MpegAudioFrame frame; + frame.parseHeader(m_reader); + MpegAudioFrameStream::addInfo(frame, *this); + m_bitrate = frame.isXingFramefieldPresent() + ? ((static_cast(m_size) * 8.0) / (static_cast(frame.xingFrameCount() * frame.sampleCount()) / static_cast(frame.samplingFrequency())) / 1024.0) + : frame.bitrate(); + m_bytesPerSecond = m_bitrate * 125; + m_duration = TimeSpan::fromSeconds(static_cast(m_size) / (m_bitrate * 128.0)); } }