Compare commits

..

1 Commits

Author SHA1 Message Date
Martchus 41a4c0c165 WIP: Make use of copy-overloads using sendfile64() 2023-04-29 12:08:58 +02:00
60 changed files with 318 additions and 740 deletions

4
.gitignore vendored
View File

@ -42,7 +42,3 @@ Makefile*
# clang-format
/.clang-format
# file with language codes one might store in the a local checkout
iso_639-2.json

View File

@ -1,4 +1,4 @@
cmake_minimum_required(VERSION 3.17.0 FATAL_ERROR)
cmake_minimum_required(VERSION 3.3.0 FATAL_ERROR)
# meta data
project(tagparser)
@ -8,8 +8,8 @@ set(META_APP_NAME "Tag Parser")
set(META_APP_AUTHOR "Martchus")
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_VERSION_MAJOR 12)
set(META_VERSION_MINOR 2)
set(META_VERSION_MAJOR 11)
set(META_VERSION_MINOR 6)
set(META_VERSION_PATCH 0)
set(META_REQUIRED_CPP_UNIT_VERSION 1.14.0)
set(META_ADD_DEFAULT_CPP_UNIT_TEST_APPLICATION ON)
@ -185,9 +185,8 @@ set(RES_FILES "${LANGUAGE_HEADER_ISO_639_2}")
set(CONFIGURATION_PACKAGE_SUFFIX
""
CACHE STRING "sets the suffix for find_package() calls to packages configured via c++utilities")
find_package(c++utilities${CONFIGURATION_PACKAGE_SUFFIX} 5.21.0 REQUIRED)
find_package(c++utilities${CONFIGURATION_PACKAGE_SUFFIX} 5.19.0 REQUIRED)
use_cpp_utilities(VISIBILITY PUBLIC)
list(APPEND META_PRIVATE_COMPILE_DEFINITIONS CHRONO_UTILITIES_TIMESPAN_INTEGER_SCALE_OVERLOADS)
# link against a possibly required extra library for std::filesystem
use_standard_filesystem()

View File

@ -7,19 +7,11 @@ The tag library can read and write the following tag formats:
* iTunes-style MP4/M4A tags (MP4-DASH is supported)
* ID3v1 and ID3v2 tags
* conversion between ID3v1 and different versions of ID3v2 is possible
* mainly for use in MP3 files but can be added to any kind of file
* Vorbis, Opus and FLAC comments in Ogg streams
* cover art via "METADATA_BLOCK_PICTURE" is supported
* Vorbis comments and "METADATA_BLOCK_PICTURE" in raw FLAC streams
* Matroska/WebM tags and attachments
Further remarks:
* Unsupported file contents (such as unsupported tag formats) are *generally* preserved as-is.
* Note that APE tags are *not* supported. APE tags in the beginning of a file are strongly
unrecommended and thus discarded when applying changes. APE tags at the end of the file
are preserved as-is when applying changes.
## File layout options
### Tag position
The library allows you to choose whether tags should be placed at the beginning or at
@ -67,7 +59,7 @@ The library is aware of different text encodings and can convert between differe
### Further documentation
For more examples check out the command line interface of [Tag Editor](https://github.com/Martchus/tageditor).
API documentation can be generated using Doxygen with `cmake --build … --target tagparser_apidoc`.
API documentation can be generated using Doxygen with `make tagparser_apidoc`.
## Bugs, stability
Bugs can be reported on GitHub.
@ -94,6 +86,6 @@ the ["Building this straight"](https://github.com/Martchus/tageditor#building-th
More TODOs are tracked in the [issue section at GitHub](https://github.com/Martchus/tagparser/issues).
## Copyright notice and license
Copyright © 2015-2024 Marius Kittler
Copyright © 2015-2023 Marius Kittler
All code is licensed under [GPL-2-or-later](LICENSE).

View File

@ -955,7 +955,7 @@ std::int16_t AacFrameElementParser::sbrHuffmanDec(SbrHuffTab table)
void AacFrameElementParser::parseSbrGrid(std::shared_ptr<AacSbrInfo> &sbr, std::uint8_t channel)
{
std::uint8_t tmp, bsEnvCount = 0;
std::uint8_t tmp, bsEnvCount;
//byte bsRelCount0, bsRelCount1;
switch ((sbr->bsFrameClass[channel] = m_reader.readBits<std::uint8_t>(2))) {
using namespace BsFrameClasses;

View File

@ -14,9 +14,6 @@ using namespace CppUtilities;
namespace TagParser {
/// \brief The AbstractAttachmentPrivate struct contains private fields of the AbstractAttachment class.
struct AbstractAttachmentPrivate {};
/*!
* \class TagParser::StreamDataBlock
* \brief The StreamDataBlock class is a reference to a certain data block of a stream.
@ -130,23 +127,6 @@ FileDataBlock::~FileDataBlock()
* \brief The AbstractAttachment class parses and stores attachment information.
*/
/*!
* \brief Constructs a new attachment.
*/
AbstractAttachment::AbstractAttachment()
: m_id(0)
, m_isDataFromFile(false)
, m_ignored(false)
{
}
/*!
* \brief Destroys the attachment.
*/
AbstractAttachment::~AbstractAttachment()
{
}
/*!
* \brief Returns a label for the track.
*/

View File

@ -102,8 +102,6 @@ inline const MediaFileInfo *FileDataBlock::fileInfo() const
return m_fileInfo.get();
}
struct AbstractAttachmentPrivate;
class TAG_PARSER_EXPORT AbstractAttachment {
public:
const std::string &description() const;
@ -125,8 +123,7 @@ public:
bool isEmpty() const;
protected:
explicit AbstractAttachment();
virtual ~AbstractAttachment();
AbstractAttachment();
private:
std::string m_description;
@ -134,11 +131,20 @@ private:
std::string m_mimeType;
std::uint64_t m_id;
std::unique_ptr<StreamDataBlock> m_data;
std::unique_ptr<AbstractAttachmentPrivate> m_p;
bool m_isDataFromFile;
bool m_ignored;
};
/*!
* \brief Constructs a new attachment.
*/
inline AbstractAttachment::AbstractAttachment()
: m_id(0)
, m_isDataFromFile(false)
, m_ignored(false)
{
}
/*!
* \brief Returns a description of the attachment.
*/

View File

@ -8,9 +8,6 @@ using namespace CppUtilities;
namespace TagParser {
/// \brief The AbstractChapterPrivate struct contains private fields of the AbstractChapter class.
struct AbstractChapterPrivate {};
/*!
* \class TagParser::AbstractChapter
* \brief The AbstractChapter class parses chapter information.

View File

@ -5,7 +5,6 @@
#include <c++utilities/chrono/timespan.h>
#include <memory>
#include <string>
#include <vector>
@ -13,7 +12,6 @@ namespace TagParser {
class AbortableProgressFeedback;
class Diagnostics;
struct AbstractChapterPrivate;
class TAG_PARSER_EXPORT AbstractChapter {
public:
@ -43,7 +41,6 @@ protected:
CppUtilities::TimeSpan m_startTime;
CppUtilities::TimeSpan m_endTime;
std::vector<std::uint64_t> m_tracks;
std::unique_ptr<AbstractChapterPrivate> m_p;
bool m_hidden;
bool m_enabled;
};

View File

@ -6,11 +6,6 @@ using namespace CppUtilities;
namespace TagParser {
/// \brief The AbstractContainerPrivate struct contains private fields of the AbstractContainer class.
struct AbstractContainerPrivate {
std::vector<std::string> muxingApps, writingApps;
};
/*!
* \class TagParser::AbstractContainer
* \brief The AbstractContainer class provides an interface and common functionality to parse and make a certain container format.
@ -477,40 +472,6 @@ bool AbstractContainer::supportsTitle() const
return false;
}
/*!
* \brief Returns the muxing applications specified as meta-data.
*/
const std::vector<std::string> &AbstractContainer::muxingApplications() const
{
static const auto empty = std::vector<std::string>();
return m_p ? m_p->muxingApps : empty;
}
/*!
* \brief Returns the muxing applications specified as meta-data.
*/
std::vector<std::string> &AbstractContainer::muxingApplications()
{
return p()->muxingApps;
}
/*!
* \brief Returns the writing applications specified as meta-data.
*/
const std::vector<std::string> &AbstractContainer::writingApplications() const
{
static const auto empty = std::vector<std::string>();
return m_p ? m_p->writingApps : empty;
}
/*!
* \brief Returns the writing applications specified as meta-data.
*/
std::vector<std::string> &AbstractContainer::writingApplications()
{
return p()->writingApps;
}
/*!
* \brief Returns the number of segments.
*/
@ -538,15 +499,4 @@ void AbstractContainer::reset()
m_titles.clear();
}
/*!
* \brief Returns the private data for the container.
*/
std::unique_ptr<AbstractContainerPrivate> &AbstractContainer::p()
{
if (!m_p) {
m_p = std::make_unique<AbstractContainerPrivate>();
}
return m_p;
}
} // namespace TagParser

View File

@ -11,7 +11,6 @@
#include <c++utilities/io/binarywriter.h>
#include <iostream>
#include <memory>
namespace CppUtilities {
class BinaryReader;
@ -26,7 +25,6 @@ class AbstractChapter;
class AbstractAttachment;
class Diagnostics;
class AbortableProgressFeedback;
struct AbstractContainerPrivate;
class TAG_PARSER_EXPORT AbstractContainer {
public:
@ -80,8 +78,6 @@ public:
const std::vector<std::string> &titles() const;
void setTitle(std::string_view title, std::size_t segmentIndex = 0);
virtual bool supportsTitle() const;
const std::vector<std::string> &muxingApplications() const;
const std::vector<std::string> &writingApplications() const;
virtual std::size_t segmentCount() const;
CppUtilities::TimeSpan duration() const;
CppUtilities::DateTime creationTime() const;
@ -99,8 +95,6 @@ protected:
virtual void internalParseChapters(Diagnostics &diag, AbortableProgressFeedback &progress);
virtual void internalParseAttachments(Diagnostics &diag, AbortableProgressFeedback &progress);
virtual void internalMakeFile(Diagnostics &diag, AbortableProgressFeedback &progress);
std::vector<std::string> &muxingApplications();
std::vector<std::string> &writingApplications();
std::uint64_t m_version;
std::uint64_t m_readVersion;
@ -121,13 +115,10 @@ protected:
bool m_attachmentsParsed;
private:
std::unique_ptr<AbstractContainerPrivate> &p();
std::uint64_t m_startOffset;
std::iostream *m_stream;
CppUtilities::BinaryReader m_reader;
CppUtilities::BinaryWriter m_writer;
std::unique_ptr<AbstractContainerPrivate> m_p;
};
/*!

View File

@ -11,9 +11,6 @@ using namespace CppUtilities;
namespace TagParser {
/// \brief The AbstractTrackPrivate struct contains private fields of the AbstractTrack class.
struct AbstractTrackPrivate {};
/*!
* \class TagParser::AbstractTrack
* \brief The AbstractTrack class parses and stores technical information about

View File

@ -15,7 +15,6 @@
#include <c++utilities/misc/flagenumclass.h>
#include <iosfwd>
#include <memory>
#include <string>
#include <string_view>
@ -105,8 +104,6 @@ CPP_UTILITIES_MARK_FLAG_ENUM_CLASS(TagParser, TagParser::TrackFlags)
namespace TagParser {
struct AbstractTrackPrivate;
class TAG_PARSER_EXPORT AbstractTrack {
friend class MpegAudioFrameStream;
friend class WaveAudioStream;
@ -132,7 +129,6 @@ public:
MediaType mediaType() const;
std::string_view mediaTypeName() const;
std::uint64_t size() const;
void setSize(std::uint64_t size);
std::uint32_t trackNumber() const;
void setTrackNumber(std::uint32_t trackNumber);
std::uint64_t id() const;
@ -236,7 +232,6 @@ protected:
AlphaMode m_alphaMode;
DisplayUnit m_displayUnit;
AspectRatioType m_aspectRatioType;
std::unique_ptr<AbstractTrackPrivate> m_p;
private:
std::string makeDescription(bool verbose) const;
@ -397,19 +392,6 @@ inline std::uint64_t AbstractTrack::size() const
return m_size;
}
/*!
* \brief Sets the size in bytes.
* \remarks
* This is used by MediaFileInfo to set the track size for certain types of tracks before invoking the parsing.
* If you use this a class derived from AbstractTrack directly you may want to do the same if not the entire
* input stream is supposed to be considered part of the track and the parser would otherwise assume that (like
* the parser of MpegAudioFrameStream might do).
*/
inline void AbstractTrack::setSize(std::uint64_t size)
{
m_size = size;
}
/*!
* \brief Returns the track number if known; otherwise returns 0.
*/

View File

@ -24,8 +24,15 @@ void AdtsStream::internalParseHeader(Diagnostics &diag, AbortableProgressFeedbac
if (!m_istream) {
throw NoDataFoundException();
}
// get size
m_istream->seekg(-128, ios_base::end);
if (m_reader.readUInt24BE() == 0x544147) {
m_size = static_cast<std::uint64_t>(m_istream->tellg()) - 3u - m_startOffset;
} else {
m_size = static_cast<std::uint64_t>(m_istream->tellg()) + 125u - m_startOffset;
}
m_istream->seekg(static_cast<streamoff>(m_startOffset), ios_base::beg);
// parse frame header
m_istream->seekg(static_cast<std::streamoff>(m_startOffset), ios_base::beg);
m_firstFrame.parseHeader(m_reader);
m_format = Mpeg4AudioObjectIds::idToMediaFormat(m_firstFrame.mpeg4AudioObjectId());
m_channelCount = Mpeg4ChannelConfigs::channelCount(m_channelConfig = m_firstFrame.mpeg4ChannelConfig());

View File

@ -42,13 +42,12 @@ void example()
// code.
// - Parsing a file can be expensive if the file is big or the disk IO is slow. You might want to
// run it in a separate thread.
// - At this point the parser does not make much use of the progress object (in contrast to applyChanges()
// shown below).
// - At this point the parser does not make much use of the progress object.
fileInfo.parseContainerFormat(diag, progress);
fileInfo.parseTags(diag, progress);
fileInfo.parseAttachments(diag, progress);
fileInfo.parseChapters(diag, progress);
fileInfo.parseEverything(diag, progress); // just use that one if you want all of the above
fileInfo.parseEverything(diag, progress); // just use that one if you want all over the above
// get tag as an object derived from the Tag class
// notes:
@ -56,7 +55,7 @@ void example()
// fileInfo.createAppropriateTags(…) to create tags as needed.
auto tag = fileInfo.tags().at(0);
// extract a field value and convert it to a UTF-8 std::string (toString() might throw ConversionException)
// extract a field value and convert it to UTF-8 std::string (toString() might throw ConversionException)
auto title = tag->value(TagParser::KnownField::Title).toString(TagParser::TagTextEncoding::Utf8);
// change a field value using an encoding suitable for the tag format
@ -85,5 +84,6 @@ void example()
// - Use progress.tryToAbort() from another thread or an interrupt handler to abort gracefully without leaving
// the file in an inconsistent state.
// - Be sure everything has been parsed before as the library needs to be aware of the whole file structure.
fileInfo.parseEverything(diag, progress);
fileInfo.applyChanges(diag, progress);
}

View File

@ -115,11 +115,7 @@ void FlacStream::internalParseHeader(Diagnostics &diag, AbortableProgressFeedbac
m_vorbisComment = make_unique<VorbisComment>();
}
try {
auto flags = VorbisCommentFlags::NoSignature | VorbisCommentFlags::NoFramingByte;
if (m_mediaFileInfo.fileHandlingFlags() & MediaFileHandlingFlags::ConvertTotalFields) {
flags += VorbisCommentFlags::ConvertTotalFields;
}
m_vorbisComment->parse(*m_istream, header.dataSize(), flags, diag);
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
}

View File

@ -27,7 +27,7 @@ void FlacToOggMappingHeader::parseHeader(OggIterator &iterator)
constexpr auto idSize = 0x05, mappingHeaderSize = 0x0D, blockHeaderSize = 0x04, streamInfoSize = 0x22;
char buff[mappingHeaderSize + blockHeaderSize + streamInfoSize - idSize];
iterator.read(buff, idSize);
if (*buff != 0x7Fu || BE::toInt<std::uint32_t>(buff + 1) != 0x464C4143u) {
if (*buff != 0x7Fu || BE::toUInt32(buff + 1) != 0x464C4143u) {
throw InvalidDataException(); // not FLAC-to-Ogg mapping header
}
iterator.read(buff, sizeof(buff));
@ -35,8 +35,8 @@ void FlacToOggMappingHeader::parseHeader(OggIterator &iterator)
// parse FLAC-to-Ogg mapping header
m_majorVersion = static_cast<std::uint8_t>(*(buff + 0x00));
m_minorVersion = static_cast<std::uint8_t>(*(buff + 0x01));
m_headerCount = BE::toInt<std::uint16_t>(buff + 0x02);
if (BE::toInt<std::uint32_t>(buff + 0x04) != 0x664C6143u) {
m_headerCount = BE::toUInt16(buff + 0x02);
if (BE::toUInt32(buff + 0x04) != 0x664C6143u) {
throw InvalidDataException(); // native FLAC signature not present
}

View File

@ -944,7 +944,7 @@ void GenericFileElement<ImplementationType>::copyInternal(
} catch (const Failure &) {
throw InvalidDataException();
}
auto &stream = container().stream();
auto &stream = container().fileInfo().stream();
stream.seekg(static_cast<std::streamoff>(startOffset), std::ios_base::beg);
CppUtilities::CopyHelper<0x10000> copyHelper;
if (progress) {

View File

@ -4,7 +4,6 @@
#ifndef TAG_PARSER_GLOBAL
#define TAG_PARSER_GLOBAL
#include "tagparser-definitions.h"
#include <c++utilities/application/global.h>
#ifdef TAG_PARSER_STATIC

View File

@ -934,7 +934,7 @@ tuple<const char *, size_t, const char *> Id3v2Frame::parseSubstring(
case TagTextEncoding::Utf16BigEndian:
case TagTextEncoding::Utf16LittleEndian: {
if (bufferSize >= 2) {
switch (LE::toInt<std::uint16_t>(buffer)) {
switch (LE::toUInt16(buffer)) {
case 0xFEFF:
if (encoding == TagTextEncoding::Utf16BigEndian) {
diag.emplace_back(DiagLevel::Critical,
@ -1002,9 +1002,9 @@ void Id3v2Frame::parseBom(const char *buffer, std::size_t maxSize, TagTextEncodi
switch (encoding) {
case TagTextEncoding::Utf16BigEndian:
case TagTextEncoding::Utf16LittleEndian:
if ((maxSize >= 2) && (BE::toInt<std::uint16_t>(buffer) == 0xFFFE)) {
if ((maxSize >= 2) && (BE::toUInt16(buffer) == 0xFFFE)) {
encoding = TagTextEncoding::Utf16LittleEndian;
} else if ((maxSize >= 2) && (BE::toInt<std::uint16_t>(buffer) == 0xFEFF)) {
} else if ((maxSize >= 2) && (BE::toUInt16(buffer) == 0xFEFF)) {
encoding = TagTextEncoding::Utf16BigEndian;
}
break;
@ -1172,7 +1172,7 @@ void Id3v2Frame::makeLegacyPicture(
} else {
imageFormat = "UND";
}
std::memcpy(++offset, imageFormat, 3);
strncpy(++offset, imageFormat, 3);
// write picture type
*(offset += 3) = static_cast<char>(typeInfo);

View File

@ -338,7 +338,7 @@ inline Id3v2Frame::IdentifierType Id3v2Frame::fieldIdFromString(std::string_view
case 3:
return CppUtilities::BE::toUInt24(idString.data());
case 4:
return CppUtilities::BE::toInt<std::uint32_t>(idString.data());
return CppUtilities::BE::toUInt32(idString.data());
default:
throw CppUtilities::ConversionException("ID3v2 ID must be 3 or 4 chars");
}

View File

@ -85,10 +85,6 @@ std::uint32_t convertToShortId(std::uint32_t id)
return sRating;
case lISRC:
return sISRC;
case lPublisherWebpage:
return sPublisherWebpage;
case lUserDefinedURL:
return sUserDefinedURL;
default:
return 0;
}
@ -159,10 +155,6 @@ std::uint32_t convertToLongId(std::uint32_t id)
return lRating;
case sISRC:
return lISRC;
case sPublisherWebpage:
return lPublisherWebpage;
case sUserDefinedURL:
return lUserDefinedURL;
default:
return 0;
}

View File

@ -49,8 +49,6 @@ enum KnownValue : std::uint32_t {
lMood = 0x544D4F4F, /**< TMOO */
lISRC = 0x54535243, /**< TSRC */
lUserDefinedText = 0x54585858, /**< TXXX */
lPublisherWebpage = 0x57505542, /**< WPUB */
lUserDefinedURL = 0x57585858, /**< WXXX */
sAlbum = 0x54414c, /**< ?TAL */
sArtist = 0x545031, /**< ?TP1 */
@ -84,8 +82,6 @@ enum KnownValue : std::uint32_t {
sCopyright = 0x544352, /**< TCR */
sISRC = 0x545243, /**< TRC */
sUserDefinedText = 0x545858, /**< ?TXX */
sPublisherWebpage = 0x575042, /**< ?WPB */
sUserDefinedURL = 0x575858, /**< ?WXX */
};
TAG_PARSER_EXPORT std::uint32_t convertToShortId(std::uint32_t id);
@ -121,14 +117,6 @@ constexpr bool isTextFrame(std::uint32_t id)
}
}
/*!
* \brief Returns an indication whether the specified \a id is a URL frame id.
*/
constexpr bool isUrlFrame(std::uint32_t id)
{
return (id & 0xFF000000u) == 0x57000000u && (id != Id3v2FrameIds::lUserDefinedURL);
}
} // namespace Id3v2FrameIds
} // namespace TagParser

View File

@ -16,7 +16,7 @@ namespace TagParser {
/*!
* \class TagParser::IvfStream
* \brief Implementation of TagParser::AbstractTrack for IVF streams.
* \brief Implementation of TagParser::AbstractTrack for ADTS streams.
* \sa https://wiki.multimedia.cx/index.php/IVF
*/

View File

@ -100,7 +100,7 @@ void EbmlElement::internalParse(Diagnostics &diag)
continue; // try again
}
reader().read(buf + (maximumIdLengthSupported() - m_idLength), m_idLength);
m_id = BE::toInt<std::uint32_t>(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)
@ -172,7 +172,7 @@ void EbmlElement::internalParse(Diagnostics &diag)
reader().read(buf + (maximumSizeLengthSupported() - m_sizeLength), m_sizeLength);
// xor the first byte in buffer which has been read from the file with mask
*(buf + (maximumSizeLengthSupported() - m_sizeLength)) ^= static_cast<char>(mask);
m_dataSize = BE::toInt<std::uint64_t>(buf);
m_dataSize = BE::toUInt64(buf);
// check if element is truncated
if (totalSize() > maxTotalSize()) {
if (m_idLength + m_sizeLength > maxTotalSize()) { // header truncated
@ -242,7 +242,7 @@ std::uint64_t EbmlElement::readUInteger()
const auto bytesToSkip = maxBytesToRead - min(dataSize(), maxBytesToRead);
stream().seekg(static_cast<streamoff>(dataOffset()), ios_base::beg);
stream().read(buff + bytesToSkip, static_cast<streamoff>(sizeof(buff) - bytesToSkip));
return BE::toInt<std::uint64_t>(buff);
return BE::toUInt64(buff);
}
/*!

View File

@ -35,6 +35,8 @@ namespace TagParser {
* \brief Implementation of GenericContainer<MediaFileInfo, MatroskaTag, MatroskaTrack, EbmlElement>.
*/
std::uint64_t MatroskaContainer::m_maxFullParseSize = 0x3200000; // FIXME v11: move to MediaFileInfo
/*!
* \brief Constructs a new container for the specified \a fileInfo at the specified \a startOffset.
*/
@ -82,13 +84,13 @@ void MatroskaContainer::reset()
*/
void MatroskaContainer::validateIndex(Diagnostics &diag, AbortableProgressFeedback &progress)
{
static const auto context = std::string("validating Matroska file index (cues)");
auto cuesElementsFound = false;
static const string context("validating Matroska file index (cues)");
bool cuesElementsFound = false;
if (m_firstElement) {
auto ids = std::unordered_set<EbmlElement::IdentifierType>();
auto cueTimeFound = false, cueTrackPositionsFound = false;
auto clusterElement = std::unique_ptr<EbmlElement>();
auto pos = std::uint64_t(), prevClusterSize = std::uint64_t(), currentOffset = std::uint64_t();
unordered_set<EbmlElement::IdentifierType> ids;
bool cueTimeFound = false, cueTrackPositionsFound = false;
unique_ptr<EbmlElement> clusterElement;
std::uint64_t pos, prevClusterSize = 0, currentOffset = 0;
// iterate through all segments
for (EbmlElement *segmentElement = m_firstElement->siblingById(MatroskaIds::Segment, diag); segmentElement;
segmentElement = segmentElement->siblingById(MatroskaIds::Segment, diag)) {
@ -562,7 +564,7 @@ void MatroskaContainer::internalParseHeader(Diagnostics &diag, AbortableProgress
}
}
// -> stop if tracks and tags have been found or the file exceeds the max. size to fully process
if (((!m_tracksElements.empty() && !m_tagsElements.empty()) || fileInfo().size() > fileInfo().maxFullParseSize())
if (((!m_tracksElements.empty() && !m_tagsElements.empty()) || fileInfo().size() > m_maxFullParseSize)
&& !m_segmentInfoElements.empty()) {
goto finish;
}
@ -627,12 +629,6 @@ void MatroskaContainer::parseSegmentInfo(Diagnostics &diag)
case MatroskaIds::TimeCodeScale:
timeScale = subElement->readUInteger();
break;
case MatroskaIds::MuxingApp:
muxingApplications().emplace_back(subElement->readString());
break;
case MatroskaIds::WrittingApp:
writingApplications().emplace_back(subElement->readString());
break;
}
subElement = subElement->nextSibling();
}
@ -838,7 +834,7 @@ struct SegmentData {
MatroskaCuePositionUpdater cuesUpdater;
/// \brief size of the "SegmentInfo"-element
std::uint64_t infoDataSize;
/// \brief cluster sizes, needed because cluster elements are not necessarily copied as-is so they're size might change
/// \brief cluster sizes
std::vector<std::uint64_t> clusterSizes;
/// \brief first "Cluster"-element (original file)
EbmlElement *firstClusterElement;
@ -948,18 +944,13 @@ void MatroskaContainer::internalMakeFile(Diagnostics &diag, AbortableProgressFee
const std::uint64_t ebmlHeaderSize = 4 + EbmlElement::calculateSizeDenotationLength(ebmlHeaderDataSize) + ebmlHeaderDataSize;
// calculate size of "WritingLib"-element
const auto &muxingApps = const_cast<const MatroskaContainer *>(this)->muxingApplications();
const auto muxingAppName = (fileInfo().fileHandlingFlags() & MediaFileHandlingFlags::PreserveMuxingApplication && !muxingApps.empty())
? std::string_view(muxingApps.front())
: std::string_view(APP_NAME " v" APP_VERSION);
const auto muxingAppElementTotalSize = std::uint64_t(2 + 1 + muxingAppName.size());
constexpr std::string_view muxingAppName = APP_NAME " v" APP_VERSION;
constexpr std::uint64_t muxingAppElementTotalSize = 2 + 1 + muxingAppName.size();
// calculate size of "WritingApp"-element
const auto writingApps = const_cast<const MatroskaContainer *>(this)->writingApplications();
const auto writingAppName = (fileInfo().fileHandlingFlags() & MediaFileHandlingFlags::PreserveWritingApplication && !writingApps.empty())
? std::string_view(writingApps.front())
: std::string_view(fileInfo().writingApplication().empty() ? muxingAppName : std::string_view(fileInfo().writingApplication()));
const auto writingAppElementTotalSize = std::uint64_t(2 + 1 + writingAppName.size());
const std::uint64_t writingAppElementDataSize
= fileInfo().writingApplication().empty() ? muxingAppName.size() : fileInfo().writingApplication().size();
const std::uint64_t writingAppElementTotalSize = 2 + 1 + writingAppElementDataSize;
try {
// calculate size of "Tags"-element
@ -1228,7 +1219,7 @@ void MatroskaContainer::internalMakeFile(Diagnostics &diag, AbortableProgressFee
progress.updateStep("Calculating cluster offsets ...");
}
// decide whether it is necessary to rewrite the entire file (if not already rewriting)
// decided whether it is necessary to rewrite the entire file (if not already rewriting)
if (!rewriteRequired) {
// find first "Cluster"-element
if ((level1Element = segment.firstClusterElement)) {
@ -1366,7 +1357,7 @@ void MatroskaContainer::internalMakeFile(Diagnostics &diag, AbortableProgressFee
goto calculateSegmentData;
}
} else {
// if rewrite is required, pretend writing the remaining elements to compute total segment size and cluster sizes
// if rewrite is required, pretend writing the remaining elements to compute total segment size
// pretend writing "Void"-element (only if there is at least one "Cluster"-element in the segment)
if (!segmentIndex && rewriteRequired && (level1Element = level0Element->childById(MatroskaIds::Cluster, diag))) {
@ -1648,7 +1639,8 @@ void MatroskaContainer::internalMakeFile(Diagnostics &diag, AbortableProgressFee
}
// -> write "MuxingApp"- and "WritingApp"-element
EbmlElement::makeSimpleElement(outputStream, MatroskaIds::MuxingApp, muxingAppName);
EbmlElement::makeSimpleElement(outputStream, MatroskaIds::WrittingApp, writingAppName);
EbmlElement::makeSimpleElement(outputStream, MatroskaIds::WrittingApp,
fileInfo().writingApplication().empty() ? muxingAppName : fileInfo().writingApplication());
}
// write "Tracks"-element

View File

@ -31,6 +31,8 @@ public:
std::uint64_t maxSizeLength() const;
const std::vector<std::unique_ptr<MatroskaSeekInfo>> &seekInfos() const;
static std::uint64_t maxFullParseSize();
void setMaxFullParseSize(std::uint64_t maxFullParseSize);
const std::vector<std::unique_ptr<MatroskaEditionEntry>> &editionEntires() const;
MatroskaChapter *chapter(std::size_t index) override;
std::size_t chapterCount() const override;
@ -70,6 +72,7 @@ private:
std::vector<std::unique_ptr<MatroskaEditionEntry>> m_editionEntries;
std::vector<std::unique_ptr<MatroskaAttachment>> m_attachments;
std::size_t m_segmentCount;
static std::uint64_t m_maxFullParseSize;
};
/*!
@ -96,6 +99,33 @@ inline const std::vector<std::unique_ptr<MatroskaSeekInfo>> &MatroskaContainer::
return m_seekInfos;
}
/*!
* \brief Returns the maximal file size for a "full parse" in byte.
*
* The "Tags" element (which holds the tag information) is commonly at the end of a Matroska file. Hence the
* parser needs to walk through the entire file to find the tag information if no "SeekHead" element is present
* which might causes long loading times. To avoid this a maximal file size for a "full parse" can be specified.
* The disadvantage is that the parser relies on the presence of a SeekHead element on larger files to retrieve
* tag information.
*
* The default value is 50 MiB.
*
* \sa setMaxFullParseSize()
*/
inline std::uint64_t MatroskaContainer::maxFullParseSize()
{
return m_maxFullParseSize;
}
/*!
* \brief Sets the maximal file size for a "full parse" in byte.
* \sa maxFullParseSize()
*/
inline void MatroskaContainer::setMaxFullParseSize(std::uint64_t maxFullParseSize)
{
m_maxFullParseSize = maxFullParseSize;
}
/*!
* \brief Returns the edition entries.
*/

View File

@ -78,7 +78,7 @@ void MatroskaCuePositionUpdater::parse(EbmlElement *cuesElement, Diagnostics &di
cuePointElementSize += cuePointChild->totalSize();
break;
case MatroskaIds::CueTrackPositions:
cueTrackPositionsElementSize = relPos = 0;
cueTrackPositionsElementSize = 0;
cueRelativePositionElement = cueClusterPositionElement = nullptr;
for (EbmlElement *cueTrackPositionsChild = cuePointChild->firstChild(); cueTrackPositionsChild;
cueTrackPositionsChild = cueTrackPositionsChild->nextSibling()) {

View File

@ -122,7 +122,7 @@ std::string_view matroskaIdName(std::uint32_t matroskaId)
return "track number";
case TrackUID:
return "unique track id";
case TrackEntryIds::TrackType:
case TrackType:
return "track type";
case TrackAudio:
return "audio track";
@ -192,7 +192,7 @@ std::string_view matroskaIdName(std::uint32_t matroskaId)
return "video display width";
case DisplayHeight:
return "video display height";
case TrackVideoIds::DisplayUnit:
case DisplayUnit:
return "video display unit";
case PixelWidth:
return "video pixel width";
@ -208,9 +208,9 @@ std::string_view matroskaIdName(std::uint32_t matroskaId)
return "video pixel crop right";
case FlagInterlaced:
return "video flag interlaced";
case TrackVideoIds::StereoMode:
case StereoMode:
return "video stereo mode";
case TrackVideoIds::AspectRatioType:
case AspectRatioType:
return "video aspect ratio type";
case ColorSpace:
return "video color space";
@ -276,7 +276,7 @@ std::string_view matroskaIdName(std::uint32_t matroskaId)
return "content encryption signature hash algorithmus";
// IDs in the Tags master
case TagsIds::Tag:
case Tag:
return "tag";
// IDs in the Tag master
@ -569,7 +569,7 @@ MatroskaElementLevel matroskaIdLevel(std::uint32_t matroskaId)
case CuePoint:
case AttachedFile:
case EditionEntry:
case TagsIds::Tag:
case Tag:
return MatroskaElementLevel::Level2;
case SeekID:
case SeekPosition:
@ -588,7 +588,7 @@ MatroskaElementLevel matroskaIdLevel(std::uint32_t matroskaId)
case Slices:
case TrackNumber:
case TrackUID:
case TrackEntryIds::TrackType:
case TrackType:
case TrackFlagEnabled:
case TrackFlagDefault:
case TrackFlagForced:

View File

@ -43,7 +43,7 @@ enum SeekIds { SeekID = 0x53AB, SeekPosition = 0x53AC };
enum SegmentInfoIds {
TimeCodeScale = 0x2AD7B1,
Duration = 0x4489,
WrittingApp = 0x5741, // TODOv13: change to WritingApp
WrittingApp = 0x5741,
MuxingApp = 0x4D80,
DateUTC = 0x4461,
SegmentUID = 0x73A4,

View File

@ -40,7 +40,6 @@
#include <c++utilities/io/path.h>
#include <algorithm>
#include <cstdint>
#include <cstdio>
#include <filesystem>
#include <functional>
@ -60,9 +59,6 @@ using namespace CppUtilities;
namespace TagParser {
/// \brief The MediaFileInfoPrivate struct contains private fields of the MediaFileInfo class.
struct MediaFileInfoPrivate {};
/*!
* \class TagParser::MediaFileInfo
* \brief The MediaFileInfo class allows to read and write tag information providing
@ -84,7 +80,6 @@ MediaFileInfo::MediaFileInfo(std::string &&path)
, m_containerFormat(ContainerFormat::Unknown)
, m_containerOffset(0)
, m_paddingSize(0)
, m_effectiveSize(0)
, m_fileStructureFlags(MediaFileStructureFlags::None)
, m_tracksParsingStatus(ParsingStatus::NotParsedYet)
, m_tagsParsingStatus(ParsingStatus::NotParsedYet)
@ -97,7 +92,6 @@ MediaFileInfo::MediaFileInfo(std::string &&path)
, m_indexPosition(ElementPosition::BeforeData)
, m_fileHandlingFlags(MediaFileHandlingFlags::ForceRewrite | MediaFileHandlingFlags::ForceTagPosition | MediaFileHandlingFlags::ForceIndexPosition
| MediaFileHandlingFlags::NormalizeKnownTagFieldIds | MediaFileHandlingFlags::PreserveRawTimingValues)
, m_maxFullParseSize(0x3200000)
{
}
@ -195,7 +189,7 @@ startParsingSignature:
// parse signature
switch ((m_containerFormat = parseSignature(buff, sizeof(buff)))) {
case ContainerFormat::Id3v2Tag:
case ContainerFormat::Id2v2Tag:
// save position of ID3v2 tag
m_actualId3v2TagOffsets.push_back(m_containerOffset);
if (m_actualId3v2TagOffsets.size() == 2) {
@ -207,7 +201,7 @@ startParsingSignature:
stream().read(buff, 5);
// set the container offset to skip ID3v2 header
m_containerOffset += toNormalInt(BE::toInt<std::uint32_t>(buff + 1)) + 10;
m_containerOffset += toNormalInt(BE::toUInt32(buff + 1)) + 10;
if ((*buff) & 0x10) {
// footer present
m_containerOffset += 10;
@ -260,23 +254,6 @@ startParsingSignature:
static_cast<OggContainer *>(m_container.get())->setChecksumValidationEnabled(isForcingFullParse());
break;
case ContainerFormat::Unknown:
case ContainerFormat::ApeTag:
// skip APE tag if the specified size makes sense at all
if (m_containerFormat == ContainerFormat::ApeTag) {
if (const auto apeEnd = m_containerOffset + 32 + LE::toUInt32(buff + 12); apeEnd <= static_cast<std::streamoff>(size())) {
// take record of APE tag
diag.emplace_back(DiagLevel::Critical,
argsToString("Found an APE tag at the beginning of the file at offset ", m_containerOffset,
". This tag format is not supported and the tag will therefore be ignored. It will NOT be preserved when saving as "
"placing an APE tag at the beginning of a file is strongly unrecommended."),
context);
// continue reading signature
m_containerOffset = apeEnd;
goto startParsingSignature;
}
m_containerFormat = ContainerFormat::Unknown;
}
// check for magic numbers at odd offsets
// -> check for tar (magic number at offset 0x101)
if (size() > 0x107) {
@ -357,13 +334,6 @@ void MediaFileInfo::parseTracks(Diagnostics &diag, AbortableProgressFeedback &pr
default:
throw NotImplementedException();
}
if (m_containerFormat != ContainerFormat::Flac) {
// ensure the effective size has been determined
// note: This is not required for FLAC and should also be avoided as parseTags() will invoke
// parseTracks() when dealing with FLAC files.
parseTags(diag, progress);
m_singleTrack->setSize(m_effectiveSize);
}
m_singleTrack->parseHeader(diag, progress);
// take padding for some "single-track" formats into account
@ -408,14 +378,12 @@ void MediaFileInfo::parseTags(Diagnostics &diag, AbortableProgressFeedback &prog
static const string context("parsing tag");
// check for ID3v1 tag
auto effectiveSize = static_cast<std::streamoff>(size());
if (effectiveSize >= 128) {
if (size() >= 128) {
m_id3v1Tag = make_unique<Id3v1Tag>();
try {
stream().seekg(effectiveSize - 128, std::ios_base::beg);
stream().seekg(-128, ios_base::end);
m_id3v1Tag->parse(stream(), diag);
m_fileStructureFlags += MediaFileStructureFlags::ActualExistingId3v1Tag;
effectiveSize -= 128;
} catch (const NoDataFoundException &) {
m_id3v1Tag.reset();
} catch (const OperationAbortedException &) {
@ -427,31 +395,6 @@ void MediaFileInfo::parseTags(Diagnostics &diag, AbortableProgressFeedback &prog
}
}
// check for APE tag at the end of the file (APE tags a the beginning are already covered when parsing the container format)
if (constexpr auto apeHeaderSize = 32; effectiveSize >= apeHeaderSize) {
const auto footerOffset = effectiveSize - apeHeaderSize;
char buffer[apeHeaderSize];
stream().seekg(footerOffset, std::ios_base::beg);
stream().read(buffer, sizeof(buffer));
if (BE::toInt<std::uint64_t>(buffer) == 0x4150455441474558ul /* APETAGEX */) {
// take record of APE tag
const auto tagSize = static_cast<std::streamoff>(LE::toInt<std::uint32_t>(buffer + 12));
const auto flags = LE::toInt<std::uint32_t>(buffer + 20);
// subtract tag size (footer size and contents) from effective size
if (tagSize <= effectiveSize) {
effectiveSize -= tagSize;
}
// subtract header size (not included in tag size) from effective size if flags indicate presence of header
if ((flags & 0x80000000u) && (apeHeaderSize <= effectiveSize)) {
effectiveSize -= apeHeaderSize;
}
diag.emplace_back(DiagLevel::Warning,
argsToString("Found an APE tag at the end of the file at offset ", (footerOffset - tagSize),
". This tag format is not supported and the tag will therefore be ignored. It will be preserved when saving as-is."),
context);
}
}
// check for ID3v2 tags: the offsets of the ID3v2 tags have already been parsed when parsing the container format
m_id3v2Tags.clear();
for (const auto offset : m_actualId3v2TagOffsets) {
@ -472,9 +415,6 @@ void MediaFileInfo::parseTags(Diagnostics &diag, AbortableProgressFeedback &prog
m_id3v2Tags.emplace_back(id3v2Tag.release());
}
// compute effective size
m_effectiveSize = static_cast<std::uint64_t>(effectiveSize - m_containerOffset);
// check for tags in tracks (FLAC only) or via container object
try {
if (m_containerFormat == ContainerFormat::Flac) {
@ -1702,9 +1642,9 @@ void MediaFileInfo::makeMp3File(Diagnostics &diag, AbortableProgressFeedback &pr
progress.updateStep(flacStream ? "Updating FLAC tags ..." : "Updating ID3v2 tags ...");
// prepare ID3v2 tags
auto makers = std::vector<Id3v2TagMaker>();
vector<Id3v2TagMaker> makers;
makers.reserve(m_id3v2Tags.size());
auto tagsSize = std::uint64_t();
std::uint64_t tagsSize = 0;
for (auto &tag : m_id3v2Tags) {
try {
makers.emplace_back(tag->prepareMaking(diag));
@ -1714,10 +1654,10 @@ void MediaFileInfo::makeMp3File(Diagnostics &diag, AbortableProgressFeedback &pr
}
// determine stream offset and make track/format specific metadata
auto streamOffset = std::uint32_t(); // where the actual stream starts
auto flacMetaData = std::stringstream(std::ios_base::in | std::ios_base::out | std::ios_base::binary);
flacMetaData.exceptions(std::ios_base::badbit | std::ios_base::failbit);
auto startOfLastMetaDataBlock = std::streamoff();
std::uint32_t streamOffset; // where the actual stream starts
stringstream flacMetaData(ios_base::in | ios_base::out | ios_base::binary);
flacMetaData.exceptions(ios_base::badbit | ios_base::failbit);
std::streamoff startOfLastMetaDataBlock;
if (flacStream) {
// if it is a raw FLAC stream, make FLAC metadata
startOfLastMetaDataBlock = flacStream->makeHeader(flacMetaData, diag);
@ -1729,8 +1669,8 @@ void MediaFileInfo::makeMp3File(Diagnostics &diag, AbortableProgressFeedback &pr
}
// check whether rewrite is required
auto rewriteRequired = isForcingRewrite() || !m_saveFilePath.empty() || (tagsSize > streamOffset);
auto padding = std::size_t();
bool rewriteRequired = isForcingRewrite() || !m_saveFilePath.empty() || (tagsSize > streamOffset);
size_t padding = 0;
if (!rewriteRequired) {
// rewriting is not forced and new tag is not too big for available space
// -> calculate new padding

View File

@ -64,11 +64,6 @@ enum class MediaFileHandlingFlags : std::uint64_t {
ForceIndexPosition = (1 << 3), /**< enforces the index position when applying changes, see remarks of MediaFileInfo::setIndexPosition() */
NormalizeKnownTagFieldIds = (1 << 4), /**< normalizes known tag field IDs when parsing to match the tag specification's recommendations */
PreserveRawTimingValues = (1 << 8), /**< preverves raw timing values (so far only used when making MP4 tracks) */
PreserveMuxingApplication = (1 << 9), /**< preverves the muxing application (so far only used when making Matroska container) */
PreserveWritingApplication = (1 << 10), /**< preverves the writing application (so far only used when making Matroska container) */
ConvertTotalFields = (1 << 11), /**< ensures fields usually holding PositionInSet values such as KnownField::TrackPosition are actually
stored as such (and *not* as two separate fields for the position and total values); currently only relevant for Vorbis Comments
\sa VorbisCommentFlags::ConvertTotalFields */
};
} // namespace TagParser
@ -78,8 +73,6 @@ CPP_UTILITIES_MARK_FLAG_ENUM_CLASS(TagParser, TagParser::MediaFileHandlingFlags)
namespace TagParser {
struct MediaFileInfoPrivate;
class TAG_PARSER_EXPORT MediaFileInfo : public BasicFileInfo {
public:
// constructor, destructor
@ -110,10 +103,9 @@ public:
std::string_view mimeType() const;
std::uint64_t containerOffset() const;
std::uint64_t paddingSize() const;
std::uint64_t effectiveSize() const;
AbstractContainer *container() const;
ParsingStatus containerParsingStatus() const;
// ... the chapters
// ... the capters
ParsingStatus chaptersParsingStatus() const;
std::vector<AbstractChapter *> chapters() const;
bool areChaptersSupported() const;
@ -192,8 +184,6 @@ public:
void setIndexPosition(ElementPosition indexPosition);
bool forceIndexPosition() const;
void setForceIndexPosition(bool forceTagPosition);
std::uint64_t maxFullParseSize() const;
void setMaxFullParseSize(std::uint64_t maxFullParseSize);
protected:
void invalidated() override;
@ -209,7 +199,6 @@ private:
ContainerFormat m_containerFormat;
std::streamoff m_containerOffset;
std::uint64_t m_paddingSize;
std::uint64_t m_effectiveSize;
std::vector<std::streamoff> m_actualId3v2TagOffsets;
std::unique_ptr<AbstractContainer> m_container;
MediaFileStructureFlags m_fileStructureFlags;
@ -237,8 +226,6 @@ private:
ElementPosition m_tagPosition;
ElementPosition m_indexPosition;
MediaFileHandlingFlags m_fileHandlingFlags;
std::uint64_t m_maxFullParseSize;
std::unique_ptr<MediaFileInfoPrivate> m_p;
};
/*!
@ -306,15 +293,6 @@ inline std::uint64_t MediaFileInfo::paddingSize() const
return m_paddingSize;
}
/*!
* \brief Returns the "effective size" of the file if know; otherwise returns 0.
* \remarks This is the size of the file minus tags at the beginning and the end.
*/
inline std::uint64_t MediaFileInfo::effectiveSize() const
{
return m_effectiveSize;
}
/*!
* \brief Returns an indication whether tag information has been parsed yet.
*/
@ -476,9 +454,7 @@ inline const std::string &MediaFileInfo::writingApplication() const
/*!
* \brief Sets the writing application as container-level meta-data. Put the name of your application here.
* \remarks
* - Currently only used when making Matroska files.
* - The assigned value is ignored when MediaFileHandlingFlags::PreserveWritingApplication is set.
* \remarks Might not be used (depends on the format).
*/
inline void MediaFileInfo::setWritingApplication(std::string_view writingApplication)
{
@ -716,33 +692,6 @@ inline void MediaFileInfo::setForceIndexPosition(bool forceIndexPosition)
CppUtilities::modFlagEnum(m_fileHandlingFlags, MediaFileHandlingFlags::ForceIndexPosition, forceIndexPosition);
}
/*!
* \brief Returns the maximal file size for a "full parse" in byte.
* \remarks
* So far this is Matroska-specific: The "Tags" element (which holds the tag information) is commonly at the end
* of a Matroska file. Hence the parser needs to walk through the entire file to find the tag information if no
* "SeekHead" element is present which might causes long loading times. To avoid this a maximal file size for a
* "full parse" can be specified. The disadvantage is that the parser relies on the presence of a SeekHead element
* on larger files to retrieve tag information.
*
* The default value is 50 MiB.
*
* \sa setMaxFullParseSize()
*/
inline std::uint64_t MediaFileInfo::maxFullParseSize() const
{
return m_maxFullParseSize;
}
/*!
* \brief Sets the maximal file size for a "full parse" in byte.
* \sa maxFullParseSize()
*/
inline void MediaFileInfo::setMaxFullParseSize(std::uint64_t maxFullParseSize)
{
m_maxFullParseSize = maxFullParseSize;
}
} // namespace TagParser
#endif // TAG_PARSER_MEDIAINFO_H

View File

@ -1,3 +1,5 @@
#define CHRONO_UTILITIES_TIMESPAN_INTEGER_SCALE_OVERLOADS
#include "./mp4container.h"
#include "./mp4ids.h"
@ -267,7 +269,7 @@ void Mp4Container::internalMakeFile(Diagnostics &diag, AbortableProgressFeedback
// find relevant atoms in original file
Mp4Atom *fileTypeAtom, *progressiveDownloadInfoAtom, *movieAtom, *firstMediaDataAtom, *firstMovieFragmentAtom /*, *userDataAtom*/;
Mp4Atom *level0Atom, *level1Atom, *level2Atom, *lastAtomToBeWritten = nullptr;
Mp4Atom *level0Atom, *level1Atom, *level2Atom, *lastAtomToBeWritten;
try {
// file type atom (mandatory)
if ((fileTypeAtom = firstElement()->siblingByIdIncludingThis(Mp4AtomIds::FileType, diag))) {
@ -747,7 +749,7 @@ calculatePadding:
// increase total chunk count and size
totalChunkCount += track->chunkCount();
totalMediaDataSize += std::accumulate(chunkSizesTable.cbegin(), chunkSizesTable.cend(), static_cast<std::uint64_t>(0u));
totalMediaDataSize += accumulate(chunkSizesTable.cbegin(), chunkSizesTable.cend(), 0ul);
}
// write media data chunk-by-chunk

View File

@ -257,7 +257,7 @@ inline Mp4TagField::IdentifierType Mp4TagField::fieldIdFromString(std::string_vi
const auto latin1 = CppUtilities::convertUtf8ToLatin1(idString.data(), idString.size());
switch (latin1.second) {
case 4:
return CppUtilities::BE::toInt<std::uint32_t>(latin1.first.get());
return CppUtilities::BE::toUInt32(latin1.first.get());
default:
throw CppUtilities::ConversionException("MP4 ID must be exactly 4 chars");
}

View File

@ -1,3 +1,5 @@
#define CHRONO_UTILITIES_TIMESPAN_INTEGER_SCALE_OVERLOADS
#include "./mp4track.h"
#include "./mp4atom.h"
#include "./mp4container.h"

View File

@ -74,7 +74,7 @@ void MpegAudioFrame::parseHeader(BinaryReader &reader, Diagnostics &diag)
m_xingBytesfield = reader.readUInt32BE();
}
if (isXingTocFieldPresent()) {
reader.stream()->seekg(0x64, ios_base::cur);
reader.stream()->seekg(64, ios_base::cur);
}
if (isXingQualityIndicatorFieldPresent()) {
m_xingQualityIndicator = reader.readUInt32BE();

View File

@ -174,7 +174,7 @@ constexpr XingHeaderFlags MpegAudioFrame::xingHeaderFlags() const
*/
constexpr bool MpegAudioFrame::isXingFramefieldPresent() const
{
return isXingHeaderAvailable() && ((m_xingHeaderFlags & XingHeaderFlags::HasFramesField) == XingHeaderFlags::HasFramesField);
return (isXingHeaderAvailable()) ? ((m_xingHeaderFlags & XingHeaderFlags::HasFramesField) == XingHeaderFlags::HasFramesField) : false;
}
/*!
@ -182,7 +182,7 @@ constexpr bool MpegAudioFrame::isXingFramefieldPresent() const
*/
constexpr bool MpegAudioFrame::isXingBytesfieldPresent() const
{
return isXingHeaderAvailable() && ((m_xingHeaderFlags & XingHeaderFlags::HasBytesField) == XingHeaderFlags::HasBytesField);
return (isXingHeaderAvailable()) ? ((m_xingHeaderFlags & XingHeaderFlags::HasFramesField) == XingHeaderFlags::HasFramesField) : false;
}
/*!
@ -190,7 +190,7 @@ constexpr bool MpegAudioFrame::isXingBytesfieldPresent() const
*/
constexpr bool MpegAudioFrame::isXingTocFieldPresent() const
{
return isXingHeaderAvailable() && ((m_xingHeaderFlags & XingHeaderFlags::HasTocField) == XingHeaderFlags::HasTocField);
return (isXingHeaderAvailable()) ? ((m_xingHeaderFlags & XingHeaderFlags::HasTocField) == XingHeaderFlags::HasTocField) : false;
}
/*!
@ -198,7 +198,7 @@ constexpr bool MpegAudioFrame::isXingTocFieldPresent() const
*/
constexpr bool MpegAudioFrame::isXingQualityIndicatorFieldPresent() const
{
return isXingHeaderAvailable() && ((m_xingHeaderFlags & XingHeaderFlags::HasQualityIndicator) == XingHeaderFlags::HasQualityIndicator);
return (isXingHeaderAvailable()) ? ((m_xingHeaderFlags & XingHeaderFlags::HasQualityIndicator) == XingHeaderFlags::HasQualityIndicator) : false;
}
/*!

View File

@ -3,8 +3,6 @@
#include "../exceptions.h"
#include "../mediaformat.h"
#include <c++utilities/conversion/stringbuilder.h>
#include <sstream>
using namespace std;
@ -37,13 +35,20 @@ void MpegAudioFrameStream::internalParseHeader(Diagnostics &diag, AbortableProgr
if (!m_istream) {
throw NoDataFoundException();
}
// get size
m_istream->seekg(-128, ios_base::end);
if (m_reader.readUInt24BE() == 0x544147) {
m_size = static_cast<std::uint64_t>(m_istream->tellg()) - 3u - m_startOffset;
} else {
m_size = static_cast<std::uint64_t>(m_istream->tellg()) + 125u - m_startOffset;
}
m_istream->seekg(static_cast<streamoff>(m_startOffset), ios_base::beg);
// parse frames until the first valid, non-empty frame is reached
m_istream->seekg(static_cast<std::streamoff>(m_startOffset), ios_base::beg);
for (size_t invalidByteskipped = 0; m_frames.size() < 200 && invalidByteskipped <= 0x600u;) {
MpegAudioFrame &frame = invalidByteskipped > 0 ? m_frames.back() : m_frames.emplace_back();
try {
frame.parseHeader(m_reader, diag);
} catch (const InvalidDataException &) {
} catch (const InvalidDataException &e) {
if (++invalidByteskipped > 1) {
diag.pop_back();
}
@ -68,24 +73,18 @@ void MpegAudioFrameStream::internalParseHeader(Diagnostics &diag, AbortableProgr
const MpegAudioFrame &frame = m_frames.back();
addInfo(frame, *this);
if (frame.isXingBytesfieldPresent()) {
const auto xingSize = frame.xingBytesfield();
if (!m_size) {
m_size = xingSize;
} else if (xingSize != m_size) {
std::uint32_t xingSize = frame.xingBytesfield();
if (m_size && xingSize != m_size) {
diag.emplace_back(DiagLevel::Warning,
argsToString("Real size of MPEG audio frames (", m_size, " byte) is not in accordance with value provided by Xing header (", xingSize,
" byte). The real size will be used."),
"Real length of MPEG audio frames is not in accordance with value provided by Xing header. The Xing header value will be used.",
context);
m_size = xingSize;
}
}
if (frame.isXingFramefieldPresent()) {
const auto duration = static_cast<double>(frame.xingFrameCount() * frame.sampleCount()) / static_cast<double>(frame.samplingFrequency());
m_bitrate = static_cast<double>(m_size) / duration / 125.0;
m_duration = TimeSpan::fromSeconds(duration);
} else {
m_bitrate = frame.bitrate();
m_duration = TimeSpan::fromSeconds(static_cast<double>(m_size) / (m_bytesPerSecond = static_cast<std::uint32_t>(m_bitrate * 125)));
}
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_duration = TimeSpan::fromSeconds(static_cast<double>(m_size) / (m_bytesPerSecond = static_cast<std::uint32_t>(m_bitrate * 125)));
}
} // namespace TagParser

View File

@ -280,10 +280,6 @@ void OggContainer::internalParseTags(Diagnostics &diag, AbortableProgressFeedbac
{
// tracks needs to be parsed before because tags are stored at stream level
parseTracks(diag, progress);
auto flags = VorbisCommentFlags::None;
if (fileInfo().fileHandlingFlags() & MediaFileHandlingFlags::ConvertTotalFields) {
flags += VorbisCommentFlags::ConvertTotalFields;
}
for (auto &comment : m_tags) {
OggParameter &params = comment->oggParams();
m_iterator.setPageIndex(params.firstPageIndex);
@ -291,16 +287,16 @@ void OggContainer::internalParseTags(Diagnostics &diag, AbortableProgressFeedbac
m_iterator.setFilter(m_iterator.currentPage().streamSerialNumber());
switch (params.streamFormat) {
case GeneralMediaFormat::Vorbis:
comment->parse(m_iterator, flags, diag);
comment->parse(m_iterator, VorbisCommentFlags::None, diag);
break;
case GeneralMediaFormat::Opus:
// skip header (has already been detected by OggStream)
m_iterator.ignore(8);
comment->parse(m_iterator, flags | VorbisCommentFlags::NoSignature | VorbisCommentFlags::NoFramingByte, diag);
comment->parse(m_iterator, VorbisCommentFlags::NoSignature | VorbisCommentFlags::NoFramingByte, diag);
break;
case GeneralMediaFormat::Flac:
m_iterator.ignore(4);
comment->parse(m_iterator, flags | VorbisCommentFlags::NoSignature | VorbisCommentFlags::NoFramingByte, diag);
comment->parse(m_iterator, VorbisCommentFlags::NoSignature | VorbisCommentFlags::NoFramingByte, diag);
break;
default:
diag.emplace_back(DiagLevel::Critical, "Stream format not supported.", "parsing tags from OGG streams");
@ -439,11 +435,11 @@ void OggContainer::internalMakeFile(Diagnostics &diag, AbortableProgressFeedback
}
// define misc variables
CopyHelper<65307> copyHelper;
vector<std::uint64_t> updatedPageOffsets;
const OggPage *lastPage = nullptr;
auto copyHelper = CopyHelper<65307>();
auto updatedPageOffsets = std::vector<std::uint64_t>();
auto nextPageOffset = std::uint64_t();
auto pageSequenceNumberBySerialNo = std::unordered_map<std::uint32_t, std::uint32_t>();
std::uint64_t nextPageOffset;
unordered_map<std::uint32_t, std::uint32_t> pageSequenceNumberBySerialNo;
// iterate through all pages of the original file
auto updateTick = 0u;

View File

@ -62,7 +62,7 @@ void OggPage::parseHeader(istream &stream, std::uint64_t startOffset, std::int32
if (++i < m_segmentCount && entry < 0xFF) {
m_segmentSizes.emplace_back(0);
} else if (i == m_segmentCount && entry == 0xFF) {
m_lastSegmentUnconcluded = true;
m_headerTypeFlag |= 0x80; // FIXME v11: don't abuse header type flags
}
}
// check whether the maximum size is exceeded

View File

@ -48,7 +48,6 @@ private:
std::uint32_t m_sequenceNumber;
std::uint32_t m_checksum;
std::uint8_t m_segmentCount;
bool m_lastSegmentUnconcluded;
std::vector<std::uint32_t> m_segmentSizes;
};
@ -64,7 +63,6 @@ inline OggPage::OggPage()
, m_sequenceNumber(0)
, m_checksum(0)
, m_segmentCount(0)
, m_lastSegmentUnconcluded(false)
{
}
@ -136,7 +134,7 @@ inline bool OggPage::isLastPage() const
*/
inline bool OggPage::isLastSegmentUnconcluded() const
{
return m_lastSegmentUnconcluded;
return m_headerTypeFlag & 0x80;
}
/*!

View File

@ -25,15 +25,15 @@ void OpusIdentificationHeader::parseHeader(OggIterator &iterator)
{
char buff[19 - 8];
iterator.read(buff, 8);
if (BE::toInt<std::uint64_t>(buff) != 0x4F70757348656164u) {
if (BE::toUInt64(buff) != 0x4F70757348656164u) {
throw InvalidDataException(); // not Opus identification header
}
iterator.read(buff, sizeof(buff));
m_version = static_cast<std::uint8_t>(*(buff));
m_channels = static_cast<std::uint8_t>(*(buff + 1));
m_preSkip = LE::toInt<std::uint16_t>(buff + 2);
m_preSkip = LE::toUInt16(buff + 2);
m_sampleRate = LE::toUInt32(buff + 4);
m_outputGain = LE::toInt<std::uint16_t>(buff + 8);
m_outputGain = LE::toUInt16(buff + 8);
m_channelMap = static_cast<std::uint8_t>(*(buff + 10));
}

View File

@ -26,9 +26,7 @@ public:
PositionInSet(const StringType &numericString);
constexpr std::int32_t position() const;
void setPosition(std::int32_t position);
constexpr std::int32_t total() const;
void setTotal(std::int32_t total);
constexpr bool isNull() const;
constexpr bool operator==(const PositionInSet &other) const;
@ -82,14 +80,6 @@ constexpr inline std::int32_t PositionInSet::position() const
return m_position;
}
/*!
* \brief Sets the element position of the current instance.
*/
inline void PositionInSet::setPosition(int32_t position)
{
m_position = position;
}
/*!
* \brief Returns the total element count of the current instance.
*/
@ -98,14 +88,6 @@ constexpr inline std::int32_t PositionInSet::total() const
return m_total;
}
/*!
* \brief Sets the total element count of the current instance.
*/
inline void PositionInSet::setTotal(int32_t total)
{
m_total = total;
}
/*!
* \brief Returns an indication whether both the element position and total element count is 0.
*/

View File

@ -13,7 +13,6 @@ namespace TagParser {
* \brief Holds 64-bit signatures.
*/
enum Sig64 : std::uint64_t {
ApeTag = 0x4150455441474558ul, // APETAGEX
Ar = 0x213C617263683E0A,
Asf1 = 0x3026B2758E66CF11ul,
Asf2 = 0xA6D900AA0062CE6Cul,
@ -115,20 +114,18 @@ ContainerFormat parseSignature(std::string_view buffer)
// read signature
std::uint64_t sig = 0;
if (buffer.size() >= 8) {
sig = BE::toInt<std::uint64_t>(buffer.data());
sig = BE::toUInt64(buffer.data());
} else if (buffer.size() >= 4) {
sig = BE::toInt<std::uint32_t>(buffer.data());
sig = BE::toUInt32(buffer.data());
sig <<= 4;
} else if (buffer.size() >= 2) {
sig = BE::toInt<std::uint16_t>(buffer.data());
sig = BE::toUInt16(buffer.data());
sig <<= 6;
} else {
return ContainerFormat::Unknown;
}
// return corresponding container format
switch (sig) { // check 64-bit signatures
case ApeTag:
return ContainerFormat::ApeTag;
case Ar:
return ContainerFormat::Ar;
case Asf1:
@ -194,9 +191,9 @@ ContainerFormat parseSignature(std::string_view buffer)
case PhotoshopDocument:
return ContainerFormat::PhotoshopDocument;
case Riff:
if (buffer.size() >= 16 && BE::toInt<std::uint64_t>(buffer.data() + 8) == Sig64::RiffAvi) {
if (buffer.size() >= 16 && BE::toUInt64(buffer.data() + 8) == Sig64::RiffAvi) {
return ContainerFormat::RiffAvi;
} else if (buffer.size() >= 12 && BE::toInt<std::uint32_t>(buffer.data() + 8) == RiffWave) {
} else if (buffer.size() >= 12 && BE::toUInt32(buffer.data() + 8) == RiffWave) {
return ContainerFormat::RiffWave;
} else {
return ContainerFormat::Riff;
@ -229,7 +226,7 @@ ContainerFormat parseSignature(std::string_view buffer)
case Gzip:
return ContainerFormat::Gzip;
case Id3v2:
return ContainerFormat::Id3v2Tag;
return ContainerFormat::Id2v2Tag;
case Utf8Text:
return ContainerFormat::Utf8Text;
}
@ -491,10 +488,6 @@ std::string_view containerFormatName(ContainerFormat containerFormat)
return "Audio Interchange File Format";
case ContainerFormat::Zstd:
return "Zstandard compressed file";
case ContainerFormat::Id3v2Tag:
return "ID3v2 tag";
case ContainerFormat::ApeTag:
return "APE tag";
default:
return "unknown";
}

View File

@ -30,7 +30,7 @@ enum class ContainerFormat : unsigned int {
Gif87a, /**< Graphics Interchange Format (1987) */
Gif89a, /**< Graphics Interchange Format (1989) */
Gzip, /**< gzip compressed file */
Id3v2Tag, /**< file holding an ID3v2 tag only */
Id2v2Tag, /**< file holding an ID2v2 tag only */
Ivf, /**< IVF (simple file format that transports raw VP8/VP9/AV1 data) */
JavaClassFile, /**< Java class file */
Jpeg, /**< JPEG File Interchange Format */
@ -67,7 +67,6 @@ enum class ContainerFormat : unsigned int {
Zip, /**< ZIP archive */
Aiff, /**< Audio Interchange File Format */
Zstd, /**< Zstandard-compressed data */
ApeTag, /**< APE tag */
};
TAG_PARSER_EXPORT ContainerFormat parseSignature(const char *buffer, std::size_t bufferSize);

View File

@ -4,9 +4,6 @@ using namespace std;
namespace TagParser {
/// \brief The TagPrivate struct contains private fields of the Tag class.
struct TagPrivate {};
/*!
* \class TagParser::Tag
* \brief The Tag class is used to store, read and write tag information.

7
tag.h
View File

@ -8,7 +8,6 @@
#include <c++utilities/io/binaryreader.h>
#include <cstdint>
#include <memory>
#include <string>
#include <type_traits>
@ -126,7 +125,6 @@ enum class KnownField : unsigned int {
ProductionCopyright, /** production copyright */
License, /** license */
TermsOfUse, /** terms of use */
PublisherWebpage, /** the publisher's official webpage */
};
/*!
@ -137,7 +135,7 @@ constexpr KnownField firstKnownField = KnownField::Title;
/*!
* \brief The last valid entry in the TagParser::KnownField enum.
*/
constexpr KnownField lastKnownField = KnownField::PublisherWebpage;
constexpr KnownField lastKnownField = KnownField::TermsOfUse;
/*!
* \brief The number of valid entries in the TagParser::KnownField enum.
@ -162,8 +160,6 @@ constexpr KnownField nextKnownField(KnownField field)
return isKnownFieldDeprecated(next) ? nextKnownField(next) : next;
}
struct TagPrivate;
class TAG_PARSER_EXPORT Tag {
public:
virtual ~Tag();
@ -203,7 +199,6 @@ protected:
std::string m_version;
std::uint64_t m_size;
std::unique_ptr<TagPrivate> m_p;
TagTarget m_target;
};

View File

@ -22,9 +22,6 @@ using namespace CppUtilities;
namespace TagParser {
/// \brief The TagValuePrivate struct contains private fields of the TagValue class.
struct TagValuePrivate {};
/*!
* \brief Returns the string representation of the specified \a dataType.
*/
@ -101,7 +98,7 @@ pair<const char *, float> encodingParameter(TagTextEncoding tagTextEncoding)
*
* Values of the type TagDataType::Text can be differently encoded.
* - See TagParser::TagTextEncoding for a list of encodings supported by this library.
* - Tag formats usually only support a subset of these encodings. The serializers for the various tag
* - Tag formats usually only support a subset of these encodings. The serializers for the varoius tag
* formats provided by this library will keep the encoding if possible and otherwise convert the assigned
* text to an encoding supported by the tag format on the fly. Note that ID3v1 does not specify which
* encodings are supported (or unsupported) so the serializer will just write text data as-is.
@ -148,140 +145,6 @@ TagValue::TagValue(const TagValue &other)
}
}
TagValue::TagValue(TagValue &&other) = default;
/*!
* \brief Constructs an empty TagValue.
*/
TagValue::TagValue()
: m_size(0)
, m_type(TagDataType::Undefined)
, m_encoding(TagTextEncoding::Latin1)
, m_descEncoding(TagTextEncoding::Latin1)
, m_flags(TagValueFlags::None)
{
}
/*!
* \brief Constructs a new TagValue holding a copy of the given \a text.
* \param text Specifies the text to be assigned.
* \param textSize Specifies the size of \a text. (The actual number of bytes, not the number of characters.)
* \param textEncoding Specifies the encoding of the given \a text.
* \param convertTo Specifies the encoding to convert \a text to; set to TagTextEncoding::Unspecified to
* use \a textEncoding without any character set conversions.
* \throws Throws a ConversionException if the conversion the specified character set fails.
* \remarks Strips the BOM of the specified \a text.
*/
TagValue::TagValue(const char *text, std::size_t textSize, TagTextEncoding textEncoding, TagTextEncoding convertTo)
: m_descEncoding(TagTextEncoding::Latin1)
, m_flags(TagValueFlags::None)
{
assignText(text, textSize, textEncoding, convertTo);
}
/*!
* \brief Constructs a new TagValue holding a copy of the given \a text.
* \param text Specifies the text to be assigned. This string must be null-terminated.
* \param textEncoding Specifies the encoding of the given \a text.
* \param convertTo Specifies the encoding to convert \a text to; set to TagTextEncoding::Unspecified to
* use \a textEncoding without any character set conversions.
* \throws Throws a ConversionException if the conversion the specified character set fails.
* \remarks Strips the BOM of the specified \a text.
*/
TagValue::TagValue(const char *text, TagTextEncoding textEncoding, TagTextEncoding convertTo)
{
assignText(text, std::strlen(text), textEncoding, convertTo);
}
/*!
* \brief Constructs a new TagValue holding a copy of the given \a text.
* \param text Specifies the text to be assigned.
* \param textEncoding Specifies the encoding of the given \a text.
* \param convertTo Specifies the encoding to convert \a text to; set to TagTextEncoding::Unspecified to
* use \a textEncoding without any character set conversions.
* \throws Throws a ConversionException if the conversion the specified character set fails.
* \remarks Strips the BOM of the specified \a text.
*/
TagValue::TagValue(const std::string &text, TagTextEncoding textEncoding, TagTextEncoding convertTo)
: m_descEncoding(TagTextEncoding::Latin1)
, m_flags(TagValueFlags::None)
{
assignText(text, textEncoding, convertTo);
}
/*!
* \brief Constructs a new TagValue holding a copy of the given \a text.
* \param text Specifies the text to be assigned.
* \param textEncoding Specifies the encoding of the given \a text.
* \param convertTo Specifies the encoding to convert \a text to; set to TagTextEncoding::Unspecified to
* use \a textEncoding without any character set conversions.
* \throws Throws a ConversionException if the conversion the specified character set fails.
* \remarks Strips the BOM of the specified \a text.
*/
TagValue::TagValue(std::string_view text, TagTextEncoding textEncoding, TagTextEncoding convertTo)
: m_descEncoding(TagTextEncoding::Latin1)
, m_flags(TagValueFlags::None)
{
assignText(text, textEncoding, convertTo);
}
/*!
* \brief Destroys the TagValue.
*/
TagValue::~TagValue()
{
}
/*!
* \brief Constructs a new TagValue with a copy of the given \a data.
*
* \param data Specifies a pointer to the data.
* \param length Specifies the length of the data.
* \param type Specifies the type of the data as TagDataType.
* \param encoding Specifies the encoding of the data as TagTextEncoding. The
* encoding will only be considered if a text is assigned.
* \remarks Strips the BOM of the specified \a data if \a type is TagDataType::Text.
*/
TagValue::TagValue(const char *data, std::size_t length, TagDataType type, TagTextEncoding encoding)
: m_size(length)
, m_type(type)
, m_encoding(encoding)
, m_descEncoding(TagTextEncoding::Latin1)
, m_flags(TagValueFlags::None)
{
if (length) {
if (type == TagDataType::Text) {
stripBom(data, m_size, encoding);
}
m_ptr = std::make_unique<char[]>(m_size);
std::copy(data, data + m_size, m_ptr.get());
}
}
/*!
* \brief Constructs a new TagValue holding with the given \a data.
*
* The \a data is not copied. It is moved.
*
* \param data Specifies a pointer to the data.
* \param length Specifies the length of the data.
* \param type Specifies the type of the data as TagDataType.
* \param encoding Specifies the encoding of the data as TagTextEncoding. The
* encoding will only be considered if a text is assigned.
* \remarks Does not strip the BOM so for consistency the caller must ensure there is no BOM present.
*/
TagValue::TagValue(std::unique_ptr<char[]> &&data, std::size_t length, TagDataType type, TagTextEncoding encoding)
: m_size(length)
, m_type(type)
, m_encoding(encoding)
, m_descEncoding(TagTextEncoding::Latin1)
, m_flags(TagValueFlags::None)
{
if (length) {
m_ptr = std::move(data);
}
}
/*!
* \brief Assigns the value of another TagValue to the current instance.
*/
@ -307,8 +170,6 @@ TagValue &TagValue::operator=(const TagValue &other)
return *this;
}
TagValue &TagValue::operator=(TagValue &&other) = default;
/// \cond
TagTextEncoding pickUtfEncoding(TagTextEncoding encoding1, TagTextEncoding encoding2)
{
@ -743,7 +604,7 @@ TimeSpan TagValue::toTimeSpan() const
switch (m_size) {
case sizeof(std::uint64_t): {
const auto ticks = *(reinterpret_cast<std::uint64_t *>(m_ptr.get()));
if (ticks < static_cast<std::uint64_t>(std::numeric_limits<std::int64_t>::max())) {
if (ticks < std::numeric_limits<std::int64_t>::max()) {
return TimeSpan(static_cast<std::int64_t>(ticks));
}
}
@ -1360,13 +1221,13 @@ void TagValue::stripBom(const char *&text, size_t &length, TagTextEncoding encod
}
break;
case TagTextEncoding::Utf16LittleEndian:
if ((length >= 2) && (LE::toInt<std::uint16_t>(text) == 0xFEFF)) {
if ((length >= 2) && (LE::toUInt16(text) == 0xFEFF)) {
text += 2;
length -= 2;
}
break;
case TagTextEncoding::Utf16BigEndian:
if ((length >= 2) && (BE::toInt<std::uint16_t>(text) == 0xFEFF)) {
if ((length >= 2) && (BE::toUInt16(text) == 0xFEFF)) {
text += 2;
length -= 2;
}

View File

@ -131,8 +131,6 @@ enum class TagDataType : unsigned int {
DateTimeExpression, /**< date time expression, see CppUtilities::DateTimeExpression */
};
TAG_PARSER_EXPORT std::string_view tagDataTypeString(TagDataType dataType);
/*!
* \brief The TagValueComparisionOption enum specifies options for TagValue::compareTo().
*/
@ -142,8 +140,6 @@ enum class TagValueComparisionFlags : unsigned int {
IgnoreMetaData = 0x2, /**< do *not* take meta-data like description and MIME-types into account */
};
struct TagValuePrivate;
class TAG_PARSER_EXPORT TagValue {
public:
// constructor, destructor
@ -168,12 +164,12 @@ public:
explicit TagValue(CppUtilities::TimeSpan value);
explicit TagValue(const Popularity &value);
TagValue(const TagValue &other);
TagValue(TagValue &&other);
TagValue(TagValue &&other) = default;
~TagValue();
// operators
TagValue &operator=(const TagValue &other);
TagValue &operator=(TagValue &&other);
TagValue &operator=(TagValue &&other) = default;
bool operator==(const TagValue &other) const;
bool operator!=(const TagValue &other) const;
operator bool() const;
@ -264,9 +260,90 @@ private:
TagTextEncoding m_encoding;
TagTextEncoding m_descEncoding;
TagValueFlags m_flags;
std::unique_ptr<TagValuePrivate> m_p;
};
/*!
* \brief Constructs an empty TagValue.
*/
inline TagValue::TagValue()
: m_size(0)
, m_type(TagDataType::Undefined)
, m_encoding(TagTextEncoding::Latin1)
, m_descEncoding(TagTextEncoding::Latin1)
, m_flags(TagValueFlags::None)
{
}
/*!
* \brief Destroys the TagValue.
*/
inline TagValue::~TagValue()
{
}
/*!
* \brief Constructs a new TagValue holding a copy of the given \a text.
* \param text Specifies the text to be assigned.
* \param textSize Specifies the size of \a text. (The actual number of bytes, not the number of characters.)
* \param textEncoding Specifies the encoding of the given \a text.
* \param convertTo Specifies the encoding to convert \a text to; set to TagTextEncoding::Unspecified to
* use \a textEncoding without any character set conversions.
* \throws Throws a ConversionException if the conversion the specified character set fails.
* \remarks Strips the BOM of the specified \a text.
*/
inline TagValue::TagValue(const char *text, std::size_t textSize, TagTextEncoding textEncoding, TagTextEncoding convertTo)
: m_descEncoding(TagTextEncoding::Latin1)
, m_flags(TagValueFlags::None)
{
assignText(text, textSize, textEncoding, convertTo);
}
/*!
* \brief Constructs a new TagValue holding a copy of the given \a text.
* \param text Specifies the text to be assigned. This string must be null-terminated.
* \param textEncoding Specifies the encoding of the given \a text.
* \param convertTo Specifies the encoding to convert \a text to; set to TagTextEncoding::Unspecified to
* use \a textEncoding without any character set conversions.
* \throws Throws a ConversionException if the conversion the specified character set fails.
* \remarks Strips the BOM of the specified \a text.
*/
inline TagValue::TagValue(const char *text, TagTextEncoding textEncoding, TagTextEncoding convertTo)
{
assignText(text, std::strlen(text), textEncoding, convertTo);
}
/*!
* \brief Constructs a new TagValue holding a copy of the given \a text.
* \param text Specifies the text to be assigned.
* \param textEncoding Specifies the encoding of the given \a text.
* \param convertTo Specifies the encoding to convert \a text to; set to TagTextEncoding::Unspecified to
* use \a textEncoding without any character set conversions.
* \throws Throws a ConversionException if the conversion the specified character set fails.
* \remarks Strips the BOM of the specified \a text.
*/
inline TagValue::TagValue(const std::string &text, TagTextEncoding textEncoding, TagTextEncoding convertTo)
: m_descEncoding(TagTextEncoding::Latin1)
, m_flags(TagValueFlags::None)
{
assignText(text, textEncoding, convertTo);
}
/*!
* \brief Constructs a new TagValue holding a copy of the given \a text.
* \param text Specifies the text to be assigned.
* \param textEncoding Specifies the encoding of the given \a text.
* \param convertTo Specifies the encoding to convert \a text to; set to TagTextEncoding::Unspecified to
* use \a textEncoding without any character set conversions.
* \throws Throws a ConversionException if the conversion the specified character set fails.
* \remarks Strips the BOM of the specified \a text.
*/
inline TagValue::TagValue(std::string_view text, TagTextEncoding textEncoding, TagTextEncoding convertTo)
: m_descEncoding(TagTextEncoding::Latin1)
, m_flags(TagValueFlags::None)
{
assignText(text, textEncoding, convertTo);
}
/*!
* \brief Constructs a new TagValue holding the given integer \a value.
*/
@ -283,6 +360,56 @@ inline TagParser::TagValue::TagValue(std::uint64_t value)
{
}
/*!
* \brief Constructs a new TagValue with a copy of the given \a data.
*
* \param data Specifies a pointer to the data.
* \param length Specifies the length of the data.
* \param type Specifies the type of the data as TagDataType.
* \param encoding Specifies the encoding of the data as TagTextEncoding. The
* encoding will only be considered if a text is assigned.
* \remarks Strips the BOM of the specified \a data if \a type is TagDataType::Text.
*/
inline TagValue::TagValue(const char *data, std::size_t length, TagDataType type, TagTextEncoding encoding)
: m_size(length)
, m_type(type)
, m_encoding(encoding)
, m_descEncoding(TagTextEncoding::Latin1)
, m_flags(TagValueFlags::None)
{
if (length) {
if (type == TagDataType::Text) {
stripBom(data, m_size, encoding);
}
m_ptr = std::make_unique<char[]>(m_size);
std::copy(data, data + m_size, m_ptr.get());
}
}
/*!
* \brief Constructs a new TagValue holding with the given \a data.
*
* The \a data is not copied. It is moved.
*
* \param data Specifies a pointer to the data.
* \param length Specifies the length of the data.
* \param type Specifies the type of the data as TagDataType.
* \param encoding Specifies the encoding of the data as TagTextEncoding. The
* encoding will only be considered if a text is assigned.
* \remarks Does not strip the BOM so for consistency the caller must ensure there is no BOM present.
*/
inline TagValue::TagValue(std::unique_ptr<char[]> &&data, std::size_t length, TagDataType type, TagTextEncoding encoding)
: m_size(length)
, m_type(type)
, m_encoding(encoding)
, m_descEncoding(TagTextEncoding::Latin1)
, m_flags(TagValueFlags::None)
{
if (length) {
m_ptr = std::move(data);
}
}
/*!
* \brief Constructs a new TagValue holding a copy of the given PositionInSet \a value.
*/

View File

@ -50,7 +50,6 @@ class OverallTests : public TestFixture {
CPPUNIT_TEST(testFlacMaking);
CPPUNIT_TEST(testMkvMakingWithDifferentSettings);
CPPUNIT_TEST(testMkvMakingNestedTags);
CPPUNIT_TEST(testVorbisCommentFieldHandling);
CPPUNIT_TEST_SUITE_END();
public:
@ -123,7 +122,6 @@ public:
void testMp3Making();
void testOggMaking();
void testFlacMaking();
void testVorbisCommentFieldHandling();
private:
MediaFileInfo m_fileInfo;

View File

@ -35,7 +35,7 @@ void OverallTests::checkFlacTestfile1()
CPPUNIT_ASSERT_EQUAL("1998"s, tags.front()->value(KnownField::RecordDate).toString());
CPPUNIT_ASSERT(tags.front()->value(KnownField::Comment).isEmpty());
//CPPUNIT_ASSERT(tags.front()->value(KnownField::Cover).dataSize() == 0x58f3);
//CPPUNIT_ASSERT(BE::toInt<std::uint64_t>(tags.front()->value(KnownField::Cover).dataPointer()) == 0xFFD8FFE000104A46);
//CPPUNIT_ASSERT(BE::toUInt64(tags.front()->value(KnownField::Cover).dataPointer()) == 0xFFD8FFE000104A46);
CPPUNIT_ASSERT_EQUAL(PositionInSet(3, 4), tags.front()->value(KnownField::TrackPosition).toPositionInSet());
CPPUNIT_ASSERT_EQUAL(PositionInSet(1, 1), tags.front()->value(KnownField::DiskPosition).toPositionInSet());
break;

View File

@ -37,7 +37,7 @@ enum TestFlag {
void OverallTests::checkMkvTestfile1()
{
CPPUNIT_ASSERT_EQUAL(ContainerFormat::Matroska, m_fileInfo.containerFormat());
CPPUNIT_ASSERT_EQUAL(TimeSpan::fromMinutes(1.0) + TimeSpan::fromSeconds(27.0) + TimeSpan::fromMilliseconds(336.0), m_fileInfo.duration());
CPPUNIT_ASSERT_EQUAL(TimeSpan::fromMinutes(1) + TimeSpan::fromSeconds(27) + TimeSpan::fromMilliseconds(336), m_fileInfo.duration());
const auto tracks = m_fileInfo.tracks();
CPPUNIT_ASSERT_EQUAL(2_st, tracks.size());
for (const auto &track : tracks) {
@ -86,7 +86,7 @@ void OverallTests::checkMkvTestfile1()
void OverallTests::checkMkvTestfile2()
{
CPPUNIT_ASSERT_EQUAL(ContainerFormat::Matroska, m_fileInfo.containerFormat());
CPPUNIT_ASSERT_EQUAL(TimeSpan::fromSeconds(47.0) + TimeSpan::fromMilliseconds(509.0), m_fileInfo.duration());
CPPUNIT_ASSERT_EQUAL(TimeSpan::fromSeconds(47) + TimeSpan::fromMilliseconds(509), m_fileInfo.duration());
const auto tracks = m_fileInfo.tracks();
CPPUNIT_ASSERT_EQUAL(2_st, tracks.size());
for (const auto &track : tracks) {
@ -135,7 +135,7 @@ void OverallTests::checkMkvTestfile2()
void OverallTests::checkMkvTestfile3()
{
CPPUNIT_ASSERT_EQUAL(ContainerFormat::Matroska, m_fileInfo.containerFormat());
CPPUNIT_ASSERT_EQUAL(TimeSpan::fromSeconds(49.0) + TimeSpan::fromMilliseconds(64.0), m_fileInfo.duration());
CPPUNIT_ASSERT_EQUAL(TimeSpan::fromSeconds(49) + TimeSpan::fromMilliseconds(64), m_fileInfo.duration());
const auto tracks = m_fileInfo.tracks();
CPPUNIT_ASSERT_EQUAL(2_st, tracks.size());
for (const auto &track : tracks) {
@ -244,7 +244,7 @@ void OverallTests::checkMkvTestfile4()
void OverallTests::checkMkvTestfile5()
{
CPPUNIT_ASSERT_EQUAL(ContainerFormat::Matroska, m_fileInfo.containerFormat());
CPPUNIT_ASSERT_EQUAL(TimeSpan::fromSeconds(46.0) + TimeSpan::fromMilliseconds(665.0), m_fileInfo.duration());
CPPUNIT_ASSERT_EQUAL(TimeSpan::fromSeconds(46) + TimeSpan::fromMilliseconds(665), m_fileInfo.duration());
const auto tracks = m_fileInfo.tracks();
CPPUNIT_ASSERT_EQUAL(11_st, tracks.size());
for (const auto &track : tracks) {
@ -298,7 +298,7 @@ void OverallTests::checkMkvTestfile5()
void OverallTests::checkMkvTestfile6()
{
CPPUNIT_ASSERT_EQUAL(ContainerFormat::Matroska, m_fileInfo.containerFormat());
CPPUNIT_ASSERT_EQUAL(TimeSpan::fromMinutes(1.0) + TimeSpan::fromSeconds(27.0) + TimeSpan::fromMilliseconds(336.0), m_fileInfo.duration());
CPPUNIT_ASSERT_EQUAL(TimeSpan::fromMinutes(1) + TimeSpan::fromSeconds(27) + TimeSpan::fromMilliseconds(336), m_fileInfo.duration());
const auto tracks = m_fileInfo.tracks();
CPPUNIT_ASSERT_EQUAL(2_st, tracks.size());
for (const auto &track : tracks) {
@ -348,7 +348,7 @@ void OverallTests::checkMkvTestfile6()
void OverallTests::checkMkvTestfile7()
{
CPPUNIT_ASSERT_EQUAL(ContainerFormat::Matroska, m_fileInfo.containerFormat());
CPPUNIT_ASSERT_EQUAL(TimeSpan::fromSeconds(37.0) + TimeSpan::fromMilliseconds(43.0), m_fileInfo.duration());
CPPUNIT_ASSERT_EQUAL(TimeSpan::fromSeconds(37) + TimeSpan::fromMilliseconds(43), m_fileInfo.duration());
const auto tracks = m_fileInfo.tracks();
CPPUNIT_ASSERT_EQUAL(2_st, tracks.size());
for (const auto &track : tracks) {
@ -408,7 +408,7 @@ void OverallTests::checkMkvTestfile7()
void OverallTests::checkMkvTestfile8()
{
CPPUNIT_ASSERT_EQUAL(ContainerFormat::Matroska, m_fileInfo.containerFormat());
CPPUNIT_ASSERT_EQUAL(TimeSpan::fromSeconds(47.0) + TimeSpan::fromMilliseconds(341.0), m_fileInfo.duration());
CPPUNIT_ASSERT_EQUAL(TimeSpan::fromSeconds(47) + TimeSpan::fromMilliseconds(341), m_fileInfo.duration());
const auto tracks = m_fileInfo.tracks();
CPPUNIT_ASSERT_EQUAL(2_st, tracks.size());
for (const auto &track : tracks) {
@ -459,7 +459,7 @@ void OverallTests::checkMkvTestfile8()
void OverallTests::checkMkvTestfileHandbrakeChapters()
{
CPPUNIT_ASSERT_EQUAL(ContainerFormat::Matroska, m_fileInfo.containerFormat());
CPPUNIT_ASSERT_EQUAL(TimeSpan::fromSeconds(27.0) + TimeSpan::fromMilliseconds(569.0), m_fileInfo.duration());
CPPUNIT_ASSERT_EQUAL(TimeSpan::fromSeconds(27) + TimeSpan::fromMilliseconds(569), m_fileInfo.duration());
const auto tracks = m_fileInfo.tracks();
CPPUNIT_ASSERT_EQUAL(2_st, tracks.size());
for (const auto &track : tracks) {

View File

@ -209,7 +209,7 @@ void OverallTests::checkMp4Testfile4()
CPPUNIT_ASSERT_EQUAL("1998"s, tags.front()->value(KnownField::RecordDate).toString());
CPPUNIT_ASSERT(tags.front()->value(KnownField::Comment).isEmpty());
CPPUNIT_ASSERT_EQUAL(0x58f3_st, tags.front()->value(KnownField::Cover).dataSize());
CPPUNIT_ASSERT_EQUAL(0xFFD8FFE000104A46ul, BE::toInt<std::uint64_t>(tags.front()->value(KnownField::Cover).dataPointer()));
CPPUNIT_ASSERT_EQUAL(0xFFD8FFE000104A46ul, BE::toUInt64(tags.front()->value(KnownField::Cover).dataPointer()));
CPPUNIT_ASSERT_EQUAL(PositionInSet(3, 4), tags.front()->value(KnownField::TrackPosition).toPositionInSet());
CPPUNIT_ASSERT_EQUAL(PositionInSet(1, 1), tags.front()->value(KnownField::DiskPosition).toPositionInSet());
break;

View File

@ -4,8 +4,6 @@
#include "../abstracttrack.h"
#include "../tag.h"
#include "../vorbis/vorbiscomment.h"
#include "../vorbis/vorbiscommentfield.h"
#include "../vorbis/vorbiscommentids.h"
#include <c++utilities/io/misc.h>
@ -252,52 +250,3 @@ void OverallTests::testOggMaking()
makeFile(workingCopyPath("ogg/noise-without-cover.opus"), modifyRoutineCover, &OverallTests::checkOggTestfile3);
}
}
/*!
* \brief Tests the Vorbis Comment specifc handling of certain fields done in VorbisComment::convertTotalFields().
*/
void OverallTests::testVorbisCommentFieldHandling()
{
const auto context = std::string();
const auto trackNumberFieldId = std::string(VorbisCommentIds::trackNumber());
const auto trackTotalFieldId = std::string(VorbisCommentIds::trackTotal());
const auto diskNumberFieldId = std::string(VorbisCommentIds::diskNumber());
const auto diskTotalFieldId = std::string(VorbisCommentIds::diskTotal());
auto diag = Diagnostics();
auto vc = VorbisComment();
auto trackNumber = VorbisCommentField(trackNumberFieldId, TagValue(5));
auto trackTotal = VorbisCommentField(trackTotalFieldId, TagValue(20));
auto &fields = vc.fields();
fields.insert(std::make_pair(trackNumberFieldId, std::move(trackNumber)));
fields.insert(std::make_pair(trackTotalFieldId, std::move(trackTotal)));
vc.convertTotalFields(context, diag);
const auto convertedValues = vc.values(trackNumberFieldId);
CPPUNIT_ASSERT_EQUAL_MESSAGE("the two fileds have been combined into one", 1_st, fields.size());
CPPUNIT_ASSERT_EQUAL_MESSAGE("there is exactly one track number value", 1_st, convertedValues.size());
const auto convertedTrackNumber = convertedValues.front()->toPositionInSet();
CPPUNIT_ASSERT_EQUAL(PositionInSet(5, 20), convertedTrackNumber);
CPPUNIT_ASSERT_EQUAL(0_st, diag.size());
auto diskNumber = VorbisCommentField(diskNumberFieldId, TagValue("invalid pos"));
auto diskTotal = VorbisCommentField(diskTotalFieldId, TagValue("invalid total"));
auto diskTotal2 = VorbisCommentField(diskTotalFieldId, TagValue(42));
fields.insert(std::make_pair(diskNumberFieldId, std::move(diskNumber)));
fields.insert(std::make_pair(diskTotalFieldId, std::move(diskTotal)));
fields.insert(std::make_pair(diskTotalFieldId, std::move(diskTotal2)));
vc.convertTotalFields(context, diag);
const auto newDiskNumberValues = vc.values(diskNumberFieldId);
const auto newDiskTotalValues = vc.values(diskTotalFieldId);
CPPUNIT_ASSERT_EQUAL_MESSAGE("invalid fields have not been combined", 4_st, fields.size());
CPPUNIT_ASSERT_EQUAL_MESSAGE("invalid disk position has been preserved and valid disk total converted", 2_st, newDiskNumberValues.size());
CPPUNIT_ASSERT_EQUAL_MESSAGE("invalid disk total has been preserved", 1_st, newDiskTotalValues.size());
const auto preservedDiskNumber = newDiskNumberValues[0]->toString();
const auto convertedDiskTotal = newDiskNumberValues[1]->toPositionInSet();
const auto preservedDiskTotal = newDiskTotalValues[0]->toString();
CPPUNIT_ASSERT_EQUAL("invalid pos"s, preservedDiskNumber);
CPPUNIT_ASSERT_EQUAL(PositionInSet(0, 42), convertedDiskTotal);
CPPUNIT_ASSERT_EQUAL("invalid total"s, preservedDiskTotal);
CPPUNIT_ASSERT_EQUAL(3_st, diag.size());
}

View File

@ -157,7 +157,7 @@ void TagValueTests::testPositionInSet()
void TagValueTests::testTimeSpan()
{
const TimeSpan fiveMinutes(TimeSpan::fromMinutes(5.0));
const TimeSpan fiveMinutes(TimeSpan::fromMinutes(5));
TagValue timeSpan;
timeSpan.assignTimeSpan(fiveMinutes);
CPPUNIT_ASSERT_EQUAL(timeSpan, TagValue(timeSpan));

View File

@ -82,7 +82,6 @@ struct TestFile {
{ "ogg/example-cover.png", { "897e1a2d0cfb79c1fe5068108bb34610c3758bd0b9a7e90c1702c4e6972e0801" } },
};
/// \cond
struct EvpMdCtx {
EvpMdCtx()
: handle(EVP_MD_CTX_new())
@ -96,7 +95,6 @@ struct EvpMdCtx {
}
EVP_MD_CTX *handle;
};
/// \endcond
/*!
* \brief Computes the SHA-256 checksums for the file using OpenSSL.

View File

@ -107,10 +107,6 @@ VorbisComment::IdentifierType VorbisComment::internallyGetFieldId(KnownField fie
return std::string(rating());
case KnownField::Bpm:
return std::string(bpm());
case KnownField::Publisher:
return std::string(publisher());
case KnownField::PublisherWebpage:
return std::string(publisherWebpage());
default:
return std::string();
}
@ -150,75 +146,12 @@ KnownField VorbisComment::internallyGetKnownField(const IdentifierType &id) cons
{ isrc(), KnownField::ISRC },
{ rating(), KnownField::Rating },
{ bpm(), KnownField::Bpm },
{ publisher(), KnownField::Publisher },
{ publisherWebpage(), KnownField::PublisherWebpage },
});
// clang-format on
const auto knownField(fieldMap.find(id));
return knownField != fieldMap.cend() ? knownField->second : KnownField::Invalid;
}
/// \cond
void VorbisComment::extendPositionInSetField(std::string_view field, std::string_view totalField, const std::string &diagContext, Diagnostics &diag)
{
auto totalValues = std::vector<std::int32_t>();
auto fieldsIter = fields().equal_range(std::string(totalField));
auto fieldsDist = std::distance(fieldsIter.first, fieldsIter.second);
if (!fieldsDist) {
return;
}
totalValues.reserve(static_cast<std::size_t>(fieldsDist));
for (; fieldsIter.first != fieldsIter.second;) {
try {
totalValues.emplace_back(fieldsIter.first->second.value().toInteger());
fields().erase(fieldsIter.first++);
} catch (const ConversionException &e) {
diag.emplace_back(DiagLevel::Warning, argsToString("Unable to parse \"", totalField, "\" as integer: ", e.what()), diagContext);
totalValues.emplace_back(0);
++fieldsIter.first;
}
}
auto totalIter = totalValues.begin(), totalEnd = totalValues.end();
for (fieldsIter = fields().equal_range(std::string(field)); fieldsIter.first != fieldsIter.second && totalIter != totalEnd;
++fieldsIter.first, ++totalIter) {
auto &v = fieldsIter.first->second.value();
try {
auto p = v.toPositionInSet();
if (p.total() && p.total() != *totalIter) {
diag.emplace_back(DiagLevel::Warning,
argsToString("The \"", totalField, "\" field value (", *totalIter, ") does not match \"", field, "\" field value (", p.total(),
"). Discarding the former in favor of the latter."),
diagContext);
} else {
p.setTotal(*totalIter);
v.assignPosition(p);
}
} catch (const ConversionException &e) {
diag.emplace_back(DiagLevel::Warning,
argsToString("Unable to parse \"", field, "\" as position in set for incorporating \"", totalField, "\": ", e.what()), diagContext);
}
}
if (totalIter != totalEnd) {
diag.emplace_back(
DiagLevel::Warning, argsToString("Vorbis Comment contains more \"", totalField, "\" fields than \"", field, "\" fields."), diagContext);
}
for (; totalIter != totalEnd; ++totalIter) {
fields().insert(std::make_pair(field, VorbisCommentField(std::string(field), TagValue(PositionInSet(0, *totalIter)))));
}
}
/// \endcond
/*!
* \brief Converts TRACKTOTAL/DISCTOTAL/PARTTOTAL to be included in the TRACKNUMBER/DISCNUMBER/PARTNUMBER fields instead.
*/
void VorbisComment::convertTotalFields(const std::string &diagContext, Diagnostics &diag)
{
extendPositionInSetField(VorbisCommentIds::trackNumber(), VorbisCommentIds::trackTotal(), diagContext, diag);
extendPositionInSetField(VorbisCommentIds::diskNumber(), VorbisCommentIds::diskTotal(), diagContext, diag);
extendPositionInSetField(VorbisCommentIds::partNumber(), VorbisCommentIds::partTotal(), diagContext, diag);
}
/*!
* \brief Internal implementation for parsing.
*/
@ -234,7 +167,7 @@ template <class StreamType> void VorbisComment::internalParse(StreamType &stream
if (!skipSignature) {
CHECK_MAX_SIZE(7)
stream.read(sig, 7);
skipSignature = (BE::toInt<std::uint64_t>(sig) & 0xffffffffffffff00u) == 0x03766F7262697300u;
skipSignature = (BE::toUInt64(sig) & 0xffffffffffffff00u) == 0x03766F7262697300u;
}
if (skipSignature) {
// read vendor (length prefixed string)
@ -310,10 +243,6 @@ template <class StreamType> void VorbisComment::internalParse(StreamType &stream
diag.emplace_back(DiagLevel::Warning, argsToString(bytesRemaining, " bytes left in last segment."), context);
}
}
if (flags & VorbisCommentFlags::ConvertTotalFields) {
convertTotalFields(context, diag);
}
}
/*!

View File

@ -7,8 +7,6 @@
#include "../fieldbasedtag.h"
#include "../mediaformat.h"
class OverallTests;
namespace TagParser {
class OggIterator;
@ -26,7 +24,6 @@ public:
class TAG_PARSER_EXPORT VorbisComment : public FieldMapBasedTag<VorbisComment> {
friend class FieldMapBasedTag<VorbisComment>;
friend class ::OverallTests;
public:
VorbisComment();
@ -55,8 +52,6 @@ protected:
private:
template <class StreamType> void internalParse(StreamType &stream, std::uint64_t maxSize, VorbisCommentFlags flags, Diagnostics &diag);
void extendPositionInSetField(std::string_view field, std::string_view totalField, const std::string &diagContext, Diagnostics &diag);
void convertTotalFields(const std::string &diagContext, Diagnostics &diag);
private:
TagValue m_vendor;

View File

@ -19,8 +19,7 @@ enum class VorbisCommentFlags : std::uint8_t {
None = 0x0, /**< Regular parsing/making. */
NoSignature = 0x1, /**< Skips the signature when parsing and making. */
NoFramingByte = 0x2, /**< Doesn't expect the framing bit to be present when parsing; does not make the framing bit when making. */
NoCovers = 0x4, /**< Skips all covers when making. */
ConvertTotalFields = 0x8, /**< Converts TRACKTOTAL/DISCTOTAL/PARTTOTAL to be included in the TRACKNUMBER/DISCNUMBER/PARTNUMBER fields. */
NoCovers = 0x4 /**< Skips all covers when making. */
};
} // namespace TagParser

View File

@ -9,12 +9,7 @@ namespace TagParser {
/*!
* \brief Encapsulates Vorbis comment field names.
* \sa
* - See https://xiph.org/vorbis/doc/v-comment.html for the upstream documentation of the field names.
* - See https://wiki.xiph.org/Field_names for an additional proposal that is most notably introducing
* `DISCNUMBER` and `TOTAL` fields.
* - See https://wiki.hydrogenaud.io/index.php?title=Tag_Mapping for further conventions and a
* comparision with other formats.
* \sa See https://xiph.org/vorbis/doc/v-comment.html for the upstream documentation of the field names.
*/
namespace VorbisCommentIds {
@ -22,18 +17,10 @@ constexpr TAG_PARSER_EXPORT std::string_view trackNumber()
{
return "TRACKNUMBER";
}
constexpr TAG_PARSER_EXPORT std::string_view trackTotal()
{
return "TRACKTOTAL";
}
constexpr TAG_PARSER_EXPORT std::string_view diskNumber()
{
return "DISCNUMBER";
}
constexpr TAG_PARSER_EXPORT std::string_view diskTotal()
{
return "DISCTOTAL";
}
constexpr TAG_PARSER_EXPORT std::string_view part()
{
return "PART";
@ -42,10 +29,6 @@ constexpr TAG_PARSER_EXPORT std::string_view partNumber()
{
return "PARTNUMBER";
}
constexpr TAG_PARSER_EXPORT std::string_view partTotal()
{
return "PARTTOTAL";
}
constexpr TAG_PARSER_EXPORT std::string_view title()
{
return "TITLE";
@ -206,10 +189,6 @@ constexpr TAG_PARSER_EXPORT std::string_view bpm()
{
return "BPM";
}
constexpr TAG_PARSER_EXPORT std::string_view publisherWebpage()
{
return "WWWPUBLISHER";
}
} // namespace VorbisCommentIds

View File

@ -25,7 +25,7 @@ void VorbisIdentificationHeader::parseHeader(OggIterator &iterator)
{
char buff[30 - 7];
iterator.read(buff, 7);
if ((BE::toInt<std::uint64_t>(buff) & 0xffffffffffffff00u) != 0x01766F7262697300u) {
if ((BE::toUInt64(buff) & 0xffffffffffffff00u) != 0x01766F7262697300u) {
throw InvalidDataException(); // not Vorbis identification header
}
iterator.read(buff, sizeof(buff));