Fix style issues
This commit is contained in:
parent
efa67d6a1a
commit
a0986ad4a9
|
@ -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
|
||||
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -368,8 +368,9 @@ int FieldMapBasedTag<ImplementationType>::insertFields(const FieldMapBasedTag<Im
|
|||
int fieldsInserted = 0;
|
||||
for(const auto &pair : from.fields()) {
|
||||
const FieldType &fromField = pair.second;
|
||||
if(fromField.value().isEmpty())
|
||||
if(fromField.value().isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
bool fieldInserted = false;
|
||||
auto range = fields().equal_range(fromField.id());
|
||||
for(auto i = range.first; i != range.second; ++i) {
|
||||
|
|
|
@ -57,12 +57,11 @@ VorbisComment *FlacStream::createVorbisComment()
|
|||
*/
|
||||
bool FlacStream::removeVorbisComment()
|
||||
{
|
||||
if(m_vorbisComment) {
|
||||
m_vorbisComment.reset();
|
||||
return true;
|
||||
} else {
|
||||
if(!m_vorbisComment) {
|
||||
return false;
|
||||
}
|
||||
m_vorbisComment.reset();
|
||||
return true;
|
||||
}
|
||||
|
||||
void FlacStream::internalParseHeader(Diagnostics &diag)
|
||||
|
@ -76,94 +75,92 @@ void FlacStream::internalParseHeader(Diagnostics &diag)
|
|||
char buffer[0x22];
|
||||
|
||||
// check signature
|
||||
if(m_reader.readUInt32BE() == 0x664C6143) {
|
||||
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<FlacMetaDataBlockType>(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<double>(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<VorbisComment>();
|
||||
}
|
||||
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<VorbisComment>();
|
||||
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<decltype(startOffset)>(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<FlacMetaDataBlockType>(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<double>(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<VorbisComment>();
|
||||
}
|
||||
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<VorbisComment>();
|
||||
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<decltype(startOffset)>(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;
|
||||
|
|
|
@ -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<std::unique_ptr<ElementType> > &additionalElements() const;
|
||||
std::vector<std::unique_ptr<ElementType> > &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<std::unique_ptr<TagType> > &tags() const;
|
||||
std::vector<std::unique_ptr<TagType> > &tags();
|
||||
const std::vector<std::unique_ptr<TrackType> > &tracks() const;
|
||||
std::vector<std::unique_ptr<TrackType> > &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;
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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);
|
||||
|
|
179
id3/id3v2tag.cpp
179
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();
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -1,10 +1,7 @@
|
|||
#include "localeawarestring.h"
|
||||
#include "./localeawarestring.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace TagParser {
|
||||
|
||||
|
||||
|
||||
} // namespace Media
|
||||
|
||||
|
|
|
@ -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<Mp4Atom>(*this, startOffset());
|
||||
m_firstElement->parse(diag);
|
||||
Mp4Atom *ftypAtom = m_firstElement->siblingByIdIncludingThis(Mp4AtomIds::FileType, diag);
|
||||
if(ftypAtom) {
|
||||
stream().seekg(static_cast<iostream::off_type>(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<iostream::off_type>(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<Mp4Tag>());
|
||||
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<Mp4Tag>());
|
||||
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<int64> &oldMdatOffsets, const
|
|||
tfhdAtom = tfhdAtom->siblingById(Mp4AtomIds::TrackFragmentHeader, diag)) {
|
||||
tfhdAtom->parse(diag);
|
||||
++tfhdAtomCount;
|
||||
if(tfhdAtom->dataSize() >= 8) {
|
||||
stream().seekg(static_cast<iostream::off_type>(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<uint64>(*iOld)) {
|
||||
off += (*iNew - *iOld);
|
||||
stream().seekp(static_cast<iostream::off_type>(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<iostream::off_type>(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<uint64>(*iOld)) {
|
||||
off += (*iNew - *iOld);
|
||||
stream().seekp(static_cast<iostream::off_type>(tfhdAtom->dataOffset()) + 8);
|
||||
writer().writeUInt64BE(off);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
switch(tfhdAtomCount) {
|
||||
|
|
14
mp4/mp4tag.h
14
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<Mp4Tag>::value;
|
||||
const TagValue &value(KnownField value) const;
|
||||
const TagValue &value(KnownField value) const override;
|
||||
using FieldMapBasedTag<Mp4Tag>::values;
|
||||
std::vector<const TagValue *> values(KnownField field) const;
|
||||
std::vector<const TagValue *> 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<Mp4Tag>::setValue;
|
||||
bool setValue(KnownField field, const TagValue &value);
|
||||
bool setValue(KnownField field, const TagValue &value) override;
|
||||
using FieldMapBasedTag<Mp4Tag>::setValues;
|
||||
bool setValues(KnownField field, const std::vector<TagValue> &values);
|
||||
bool setValues(KnownField field, const std::vector<TagValue> &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<Mp4Tag>::hasField;
|
||||
bool hasField(KnownField value) const;
|
||||
bool hasField(KnownField value) const override;
|
||||
|
||||
void parse(Mp4Atom &metaAtom, Diagnostics &diag);
|
||||
Mp4TagMaker prepareMaking(Diagnostics &diag);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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<MpegAudioFrame> m_frames;
|
||||
|
|
|
@ -16,13 +16,13 @@ class TAG_PARSER_EXPORT OggStream : public AbstractTrack
|
|||
|
||||
public:
|
||||
OggStream(OggContainer &container, std::vector<OggPage>::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);
|
||||
|
|
|
@ -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<VorbisComment>::value;
|
||||
const TagValue &value(KnownField field) const;
|
||||
const TagValue &value(KnownField field) const override;
|
||||
using FieldMapBasedTag<VorbisComment>::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);
|
||||
|
|
|
@ -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<double>(m_sampleCount) / static_cast<double>(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<double>(m_size) * 8.0) / (static_cast<double>(frame.xingFrameCount() * frame.sampleCount()) / static_cast<double>(frame.samplingFrequency())) / 1024.0)
|
||||
: frame.bitrate();
|
||||
m_bytesPerSecond = m_bitrate * 125;
|
||||
m_duration = TimeSpan::fromSeconds(static_cast<double>(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<double>(m_sampleCount) / static_cast<double>(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<double>(m_size) * 8.0) / (static_cast<double>(frame.xingFrameCount() * frame.sampleCount()) / static_cast<double>(frame.samplingFrequency())) / 1024.0)
|
||||
: frame.bitrate();
|
||||
m_bytesPerSecond = m_bitrate * 125;
|
||||
m_duration = TimeSpan::fromSeconds(static_cast<double>(m_size) / (m_bitrate * 128.0));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue