diff --git a/README.md b/README.md index fe58c5c..ee9dea6 100644 --- a/README.md +++ b/README.md @@ -51,33 +51,74 @@ This example shows how to read and write tag fields in a format-independent way: ``` #include #include +#include // create a MediaFileInfo for high-level access to overall functionality of the library -TagParser::MediaFileInfo fileInfo; +auto fileInfo = MediaFileInfo(); + // create container for errors, warnings, etc. -Diagnostics diag; +auto diag = Diagnostics(); + +// create handle to abort gracefully and get feedback during during long operations +auto progress = AbortableProgressFeedback([callback for status update], [callback for percentage-only updates]); // open file (might throw ios_base::failure) fileInfo.setPath("/path/to/some/file"); fileInfo.open(); -// parse tags -// (might throw exception derived from TagParser::Failure for fatal parsing error or ios_base::failure for IO errors) -fileInfo.parseTags(diag); -// get first tag as an object derived from the Tag class +// parse container format, tags, attachments and/or chapters as needed +// notes: +// - These functions might throw exceptions derived from ios_base::failure for IO errors and +// populate diag with possibly critical parsing messages you definitely want to check in production +// 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. +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 over the above + +// get tag as an object derived from the Tag class +// notes: +// - In real code you might want to check how many tags are assigned or use +// fileInfo.createAppropriateTags(…) to create tags as needed. auto tag = fileInfo.tags().at(0); -// extract title and convert it to UTF-8 std::string -// (toString() might throw ConversionException) + +// extract a field value and convert it to UTF-8 std::string (toString() might throw ConversionException) +#include +#include auto title = tag->value(TagParser::KnownField::Title).toString(TagParser::TagTextEncoding::Utf8); -// change album using an encoding suitable for the tag format -tag->setValue(TagParser::KnownField::Album, TagParser::TagValue("some UTF-8 string", TagParser::TagTextEncoding::Utf8, tag->proposedTextEncoding())); +// change a field value using an encoding suitable for the tag format +tag->setValue(KnownField::Album, TagValue("some UTF-8 string", TagTextEncoding::Utf8, tag->proposedTextEncoding())); -// create progress -TagParser::AbortableProgressFeedback progress([callback for status update], [callback for percentage-only updates]); +// get/remove/create attachments +#include +if (auto *const container = fileInfo.container()) { + for (auto i = 0, count = container->attachmentCount(); i != count; ++i) { + auto attachment = container->attachment(i); + if (attachment->mimeType() == "image/jpeg") { + attachment->setIgnored(true); // remove existing attachment + } + } + // create new attachment + auto attachment = container->createAttachment(); + attachment->setName("cover.jpg"); + attachment->setFile(cover, diag); +} // apply changes to the file on disk -// (might throw exception derived from TagParser::Failure for fatal processing error or ios_base::failure for IO errors) +// notes: +// - Might throw exception derived from TagParser::Failure for fatal processing error or ios_base::failure +// for IO errors. +// - Applying changes can be expensive if the file is big or the disk IO is slow. You might want to +// run it in a separate thread. +// - Use progress.tryToAbort() from another thread or an interrupt handler to abort gracefully without leaving +// the file in an inconsistent state. +// - Be sure everyting has been parsed before as the library needs to be aware of the whole file structure. +fileInfo.parseEverything(diag, progress); fileInfo.applyChanges(diag, progress); ``` @@ -87,8 +128,8 @@ fileInfo.applyChanges(diag, progress); * Fatal processing errors are propagated by throwing a class derived from `TagParser::Failure`. * All operations which might generate warnings, non-fatal errors, ... take a `TagParser::Diagnostics` object to store those messages. -* All operations which can be aborted or provide progress feedback take a `TagParser::AbortableProgressFeedback` object - for callbacks and aborting. +* All operations which might be aborted or might provide progress feedback take a `TagParser::AbortableProgressFeedback` + object for callbacks and aborting. * Field values are stored using `TagParser::TagValue` objects. Those objects erase the actual type similar to `QVariant` from the Qt framework. The documentation of `TagParser::TagValue` covers how different types and encodings are handled. diff --git a/abstractattachment.cpp b/abstractattachment.cpp index 1f018b2..27c9236 100644 --- a/abstractattachment.cpp +++ b/abstractattachment.cpp @@ -2,6 +2,7 @@ #include "./exceptions.h" #include "./mediafileinfo.h" +#include "./progressfeedback.h" #include @@ -100,12 +101,12 @@ void StreamDataBlock::copyTo(ostream &stream) const * * \throws Throws ios_base::failure when an IO error occurs. */ -FileDataBlock::FileDataBlock(std::string_view path, Diagnostics &diag) +FileDataBlock::FileDataBlock(std::string_view path, Diagnostics &diag, AbortableProgressFeedback &progress) : m_fileInfo(make_unique()) { m_fileInfo->setPath(path); m_fileInfo->open(true); - m_fileInfo->parseContainerFormat(diag); + m_fileInfo->parseContainerFormat(diag, progress); m_startOffset = 0; m_endOffset = m_fileInfo->size(); m_stream = [this]() -> std::istream & { return this->m_fileInfo->stream(); }; @@ -167,10 +168,10 @@ void AbstractAttachment::clear() * * When such an exception is thrown, the attachment remains unchanged. */ -void AbstractAttachment::setFile(string_view path, Diagnostics &diag) +void AbstractAttachment::setFile(string_view path, Diagnostics &diag, AbortableProgressFeedback &progress) { m_data.reset(); - auto file = make_unique(path, diag); + auto file = make_unique(path, diag, progress); const auto fileName = file->fileInfo()->fileName(); if (!fileName.empty()) { m_name = fileName; diff --git a/abstractattachment.h b/abstractattachment.h index 9c968fb..e2a3b1a 100644 --- a/abstractattachment.h +++ b/abstractattachment.h @@ -10,6 +10,7 @@ namespace TagParser { +class AbortableProgressFeedback; class MediaFileInfo; class TAG_PARSER_EXPORT StreamDataBlock { @@ -89,7 +90,7 @@ inline void StreamDataBlock::discardBuffer() class TAG_PARSER_EXPORT FileDataBlock : public StreamDataBlock { public: - FileDataBlock(std::string_view path, Diagnostics &diag); + FileDataBlock(std::string_view path, Diagnostics &diag, AbortableProgressFeedback &progress); ~FileDataBlock(); const MediaFileInfo *fileInfo() const; @@ -114,7 +115,7 @@ public: void setId(std::uint64_t id); const StreamDataBlock *data() const; void setData(std::unique_ptr &&data); - void setFile(std::string_view path, Diagnostics &diag); + void setFile(std::string_view path, Diagnostics &diag, AbortableProgressFeedback &progress); bool isDataFromFile() const; std::string label() const; void clear(); diff --git a/abstractchapter.cpp b/abstractchapter.cpp index 59dae08..e96f53d 100644 --- a/abstractchapter.cpp +++ b/abstractchapter.cpp @@ -1,4 +1,5 @@ #include "./abstractchapter.h" +#include "./progressfeedback.h" #include @@ -67,10 +68,10 @@ void AbstractChapter::clear() * * Clears all previous parsing results. */ -void AbstractChapter::parse(Diagnostics &diag) +void AbstractChapter::parse(Diagnostics &diag, AbortableProgressFeedback &progress) { clear(); - internalParse(diag); + internalParse(diag, progress); } /*! @@ -78,12 +79,13 @@ void AbstractChapter::parse(Diagnostics &diag) * * Clears all previous parsing results. */ -void AbstractChapter::parseNested(Diagnostics &diag) +void AbstractChapter::parseNested(Diagnostics &diag, AbortableProgressFeedback &progress) { + progress.stopIfAborted(); clear(); - internalParse(diag); + internalParse(diag, progress); for (size_t i = 0, count = nestedChapterCount(); i < count; ++i) { - nestedChapter(i)->parseNested(diag); + nestedChapter(i)->parseNested(diag, progress); } } diff --git a/abstractchapter.h b/abstractchapter.h index abce3b8..a47c645 100644 --- a/abstractchapter.h +++ b/abstractchapter.h @@ -10,6 +10,7 @@ namespace TagParser { +class AbortableProgressFeedback; class Diagnostics; class TAG_PARSER_EXPORT AbstractChapter { @@ -28,12 +29,12 @@ public: virtual const AbstractChapter *nestedChapter(std::size_t index) const; virtual std::size_t nestedChapterCount() const; virtual void clear(); - void parse(Diagnostics &diag); - void parseNested(Diagnostics &diag); + void parse(Diagnostics &diag, AbortableProgressFeedback &progress); + void parseNested(Diagnostics &diag, AbortableProgressFeedback &progress); protected: AbstractChapter(); - virtual void internalParse(Diagnostics &diag) = 0; + virtual void internalParse(Diagnostics &diag, AbortableProgressFeedback &progress) = 0; std::uint64_t m_id; std::vector m_names; diff --git a/abstractcontainer.cpp b/abstractcontainer.cpp index 824ebb4..14f9cf5 100644 --- a/abstractcontainer.cpp +++ b/abstractcontainer.cpp @@ -52,12 +52,12 @@ AbstractContainer::~AbstractContainer() * \throws Throws std::ios_base::failure when an IO error occurs. * \throws Throws Failure or a derived class when an parsing error occurs. */ -void AbstractContainer::parseHeader(Diagnostics &diag) +void AbstractContainer::parseHeader(Diagnostics &diag, AbortableProgressFeedback &progress) { if (!isHeaderParsed()) { removeAllTags(); removeAllTracks(); - internalParseHeader(diag); + internalParseHeader(diag, progress); m_headerParsed = true; } } @@ -79,11 +79,11 @@ void AbstractContainer::parseHeader(Diagnostics &diag) * \sa parseChapters() * \sa tags() */ -void AbstractContainer::parseTags(Diagnostics &diag) +void AbstractContainer::parseTags(Diagnostics &diag, AbortableProgressFeedback &progress) { if (!areTagsParsed()) { - parseHeader(diag); - internalParseTags(diag); + parseHeader(diag, progress); + internalParseTags(diag, progress); m_tagsParsed = true; } } @@ -103,11 +103,11 @@ void AbstractContainer::parseTags(Diagnostics &diag) * \sa parseTags() * \sa tracks() */ -void AbstractContainer::parseTracks(Diagnostics &diag) +void AbstractContainer::parseTracks(Diagnostics &diag, AbortableProgressFeedback &progress) { if (!areTracksParsed()) { - parseHeader(diag); - internalParseTracks(diag); + parseHeader(diag, progress); + internalParseTracks(diag, progress); m_tracksParsed = true; m_tracksAltered = false; } @@ -123,11 +123,11 @@ void AbstractContainer::parseTracks(Diagnostics &diag) * \throws Throws TagParser::Failure or a derived exception when a parsing * error occurs. */ -void AbstractContainer::parseChapters(Diagnostics &diag) +void AbstractContainer::parseChapters(Diagnostics &diag, AbortableProgressFeedback &progress) { if (!areChaptersParsed()) { - parseHeader(diag); - internalParseChapters(diag); + parseHeader(diag, progress); + internalParseChapters(diag, progress); m_chaptersParsed = true; } } @@ -142,11 +142,11 @@ void AbstractContainer::parseChapters(Diagnostics &diag) * \throws Throws TagParser::Failure or a derived exception when a parsing * error occurs. */ -void AbstractContainer::parseAttachments(Diagnostics &diag) +void AbstractContainer::parseAttachments(Diagnostics &diag, AbortableProgressFeedback &progress) { if (!areAttachmentsParsed()) { - parseHeader(diag); - internalParseAttachments(diag); + parseHeader(diag, progress); + internalParseAttachments(diag, progress); m_attachmentsParsed = true; } } @@ -194,9 +194,10 @@ ElementPosition AbstractContainer::determineIndexPosition(Diagnostics &diag) con * \throws Throws Failure or a derived class when a parsing error occurs. * \throws Throws std::ios_base::failure when an IO error occurs. */ -void AbstractContainer::internalParseHeader(Diagnostics &diag) +void AbstractContainer::internalParseHeader(Diagnostics &diag, AbortableProgressFeedback &progress) { CPP_UTILITIES_UNUSED(diag); + CPP_UTILITIES_UNUSED(progress); throw NotImplementedException(); } @@ -208,9 +209,10 @@ void AbstractContainer::internalParseHeader(Diagnostics &diag) * \throws Throws Failure or a derived class when a parsing error occurs. * \throws Throws std::ios_base::failure when an IO error occurs. */ -void AbstractContainer::internalParseTags(Diagnostics &diag) +void AbstractContainer::internalParseTags(Diagnostics &diag, AbortableProgressFeedback &progress) { CPP_UTILITIES_UNUSED(diag); + CPP_UTILITIES_UNUSED(progress); throw NotImplementedException(); } @@ -222,9 +224,10 @@ void AbstractContainer::internalParseTags(Diagnostics &diag) * \throws Throws Failure or a derived class when a parsing error occurs. * \throws Throws std::ios_base::failure when an IO error occurs. */ -void AbstractContainer::internalParseTracks(Diagnostics &diag) +void AbstractContainer::internalParseTracks(Diagnostics &diag, AbortableProgressFeedback &progress) { CPP_UTILITIES_UNUSED(diag); + CPP_UTILITIES_UNUSED(progress); throw NotImplementedException(); } @@ -236,9 +239,10 @@ void AbstractContainer::internalParseTracks(Diagnostics &diag) * \throws Throws Failure or a derived class when a parsing error occurs. * \throws Throws std::ios_base::failure when an IO error occurs. */ -void AbstractContainer::internalParseChapters(Diagnostics &diag) +void AbstractContainer::internalParseChapters(Diagnostics &diag, AbortableProgressFeedback &progress) { CPP_UTILITIES_UNUSED(diag); + CPP_UTILITIES_UNUSED(progress); throw NotImplementedException(); } @@ -250,9 +254,10 @@ void AbstractContainer::internalParseChapters(Diagnostics &diag) * \throws Throws Failure or a derived class when a parsing error occurs. * \throws Throws std::ios_base::failure when an IO error occurs. */ -void AbstractContainer::internalParseAttachments(Diagnostics &diag) +void AbstractContainer::internalParseAttachments(Diagnostics &diag, AbortableProgressFeedback &progress) { CPP_UTILITIES_UNUSED(diag); + CPP_UTILITIES_UNUSED(progress); throw NotImplementedException(); } diff --git a/abstractcontainer.h b/abstractcontainer.h index 31e97ac..1b53806 100644 --- a/abstractcontainer.h +++ b/abstractcontainer.h @@ -36,11 +36,11 @@ public: CppUtilities::BinaryReader &reader(); CppUtilities::BinaryWriter &writer(); - void parseHeader(Diagnostics &diag); - void parseTags(Diagnostics &diag); - void parseTracks(Diagnostics &diag); - void parseChapters(Diagnostics &diag); - void parseAttachments(Diagnostics &diag); + void parseHeader(Diagnostics &diag, AbortableProgressFeedback &progress); + void parseTags(Diagnostics &diag, AbortableProgressFeedback &progress); + void parseTracks(Diagnostics &diag, AbortableProgressFeedback &progress); + void parseChapters(Diagnostics &diag, AbortableProgressFeedback &progress); + void parseAttachments(Diagnostics &diag, AbortableProgressFeedback &progress); void makeFile(Diagnostics &diag, AbortableProgressFeedback &progress); bool isHeaderParsed() const; @@ -89,11 +89,11 @@ public: protected: AbstractContainer(std::iostream &stream, std::uint64_t startOffset); - virtual void internalParseHeader(Diagnostics &diag); - virtual void internalParseTags(Diagnostics &diag); - virtual void internalParseTracks(Diagnostics &diag); - virtual void internalParseChapters(Diagnostics &diag); - virtual void internalParseAttachments(Diagnostics &diag); + virtual void internalParseHeader(Diagnostics &diag, AbortableProgressFeedback &progress); + virtual void internalParseTags(Diagnostics &diag, AbortableProgressFeedback &progress); + virtual void internalParseTracks(Diagnostics &diag, AbortableProgressFeedback &progress); + virtual void internalParseChapters(Diagnostics &diag, AbortableProgressFeedback &progress); + virtual void internalParseAttachments(Diagnostics &diag, AbortableProgressFeedback &progress); virtual void internalMakeFile(Diagnostics &diag, AbortableProgressFeedback &progress); std::uint64_t m_version; diff --git a/abstracttrack.cpp b/abstracttrack.cpp index ecb5653..a53bdd2 100644 --- a/abstracttrack.cpp +++ b/abstracttrack.cpp @@ -232,12 +232,12 @@ string AbstractTrack::shortDescription() const * \throws Throws TagParser::Failure or a derived exception when a parsing * error occurs. */ -void AbstractTrack::parseHeader(Diagnostics &diag) +void AbstractTrack::parseHeader(Diagnostics &diag, AbortableProgressFeedback &progress) { m_flags -= TrackFlags::HeaderValid; m_istream->seekg(static_cast(m_startOffset), ios_base::beg); try { - internalParseHeader(diag); + internalParseHeader(diag, progress); m_flags += TrackFlags::HeaderValid; } catch (const Failure &) { throw; diff --git a/abstracttrack.h b/abstracttrack.h index 7485e53..b73c8e3 100644 --- a/abstracttrack.h +++ b/abstracttrack.h @@ -20,6 +20,7 @@ namespace TagParser { +class AbortableProgressFeedback; class MpegAudioFrameStream; class WaveAudioStream; class Mp4Track; @@ -134,13 +135,13 @@ public: std::string description() const; std::string shortDescription() const; - void parseHeader(Diagnostics &diag); + void parseHeader(Diagnostics &diag, AbortableProgressFeedback &progress); bool isHeaderValid() const; protected: AbstractTrack(std::istream &inputStream, std::ostream &outputStream, std::uint64_t startOffset); AbstractTrack(std::iostream &stream, std::uint64_t startOffset); - virtual void internalParseHeader(Diagnostics &diag) = 0; + virtual void internalParseHeader(Diagnostics &diag, AbortableProgressFeedback &progress) = 0; std::istream *m_istream; std::ostream *m_ostream; diff --git a/adts/adtsstream.cpp b/adts/adtsstream.cpp index e29709a..057d157 100644 --- a/adts/adtsstream.cpp +++ b/adts/adtsstream.cpp @@ -15,9 +15,10 @@ namespace TagParser { * \brief Implementation of TagParser::AbstractTrack for ADTS streams. */ -void AdtsStream::internalParseHeader(Diagnostics &diag) +void AdtsStream::internalParseHeader(Diagnostics &diag, AbortableProgressFeedback &progress) { CPP_UTILITIES_UNUSED(diag) + CPP_UTILITIES_UNUSED(progress) //static const string context("parsing ADTS frame header"); if (!m_istream) { diff --git a/adts/adtsstream.h b/adts/adtsstream.h index 3e79573..c031eb1 100644 --- a/adts/adtsstream.h +++ b/adts/adtsstream.h @@ -15,7 +15,7 @@ public: TrackType type() const override; protected: - void internalParseHeader(Diagnostics &diag) override; + void internalParseHeader(Diagnostics &diag, AbortableProgressFeedback &progress) override; private: AdtsFrame m_firstFrame; diff --git a/flac/flacstream.cpp b/flac/flacstream.cpp index ea0472c..09fabf5 100644 --- a/flac/flacstream.cpp +++ b/flac/flacstream.cpp @@ -62,8 +62,10 @@ bool FlacStream::removeVorbisComment() return true; } -void FlacStream::internalParseHeader(Diagnostics &diag) +void FlacStream::internalParseHeader(Diagnostics &diag, AbortableProgressFeedback &progress) { + CPP_UTILITIES_UNUSED(progress) + static const string context("parsing raw FLAC header"); if (!m_istream) { throw NoDataFoundException(); diff --git a/flac/flacstream.h b/flac/flacstream.h index 155ddb7..19771ce 100644 --- a/flac/flacstream.h +++ b/flac/flacstream.h @@ -27,7 +27,7 @@ public: static void makePadding(std::ostream &stream, std::uint32_t size, bool isLast, Diagnostics &diag); protected: - void internalParseHeader(Diagnostics &diag) override; + void internalParseHeader(Diagnostics &diag, AbortableProgressFeedback &progress) override; private: MediaFileInfo &m_mediaFileInfo; diff --git a/genericcontainer.h b/genericcontainer.h index bd1eb29..35aaf32 100644 --- a/genericcontainer.h +++ b/genericcontainer.h @@ -26,7 +26,7 @@ public: GenericContainer(FileInfoType &fileInfo, std::uint64_t startOffset); ~GenericContainer() override; - void validateElementStructure(Diagnostics &diag, std::uint64_t *paddingSize = nullptr); + void validateElementStructure(Diagnostics &diag, AbortableProgressFeedback &progress, std::uint64_t *paddingSize = nullptr); FileInfoType &fileInfo() const; ElementType *firstElement() const; const std::vector> &additionalElements() const; @@ -96,11 +96,12 @@ GenericContainer::~GenericContain * \throws Throws std::ios_base::failure when an IO error occurs. */ template -inline void GenericContainer::validateElementStructure(Diagnostics &diag, std::uint64_t *paddingSize) +inline void GenericContainer::validateElementStructure( + Diagnostics &diag, AbortableProgressFeedback &progress, std::uint64_t *paddingSize) { - parseHeader(diag); + parseHeader(diag, progress); if (m_firstElement) { - m_firstElement->validateSubsequentElementStructure(diag, paddingSize); + m_firstElement->validateSubsequentElementStructure(diag, paddingSize, &progress); } } diff --git a/genericfileelement.h b/genericfileelement.h index b1c7af8..f27024a 100644 --- a/genericfileelement.h +++ b/genericfileelement.h @@ -113,7 +113,7 @@ public: void clear(); void parse(Diagnostics &diag); void reparse(Diagnostics &diag); - void validateSubsequentElementStructure(Diagnostics &diag, std::uint64_t *paddingSize = nullptr); + void validateSubsequentElementStructure(Diagnostics &diag, std::uint64_t *paddingSize = nullptr, AbortableProgressFeedback *progress = nullptr); static constexpr std::uint32_t maximumIdLengthSupported(); static constexpr std::uint32_t maximumSizeLengthSupported(); static constexpr std::uint8_t minimumElementSize(); @@ -812,14 +812,18 @@ template void GenericFileElement: * \sa parse() */ template -void GenericFileElement::validateSubsequentElementStructure(Diagnostics &diag, std::uint64_t *paddingSize) +void GenericFileElement::validateSubsequentElementStructure( + Diagnostics &diag, std::uint64_t *paddingSize, AbortableProgressFeedback *progress) { + if (progress) { + progress->stopIfAborted(); + } // validate element itself by just parsing it parse(diag); // validate children if (firstChild()) { try { - firstChild()->validateSubsequentElementStructure(diag, paddingSize); + firstChild()->validateSubsequentElementStructure(diag, paddingSize, progress); } catch (const Failure &) { // ignore critical errors in child structure to continue validating siblings // (critical notifications about the errors should have already been added to diag, so nothing to do) @@ -829,7 +833,7 @@ void GenericFileElement::validateSubsequentElementStructure( } // validate siblings if (nextSibling()) { - nextSibling()->validateSubsequentElementStructure(diag, paddingSize); + nextSibling()->validateSubsequentElementStructure(diag, paddingSize, progress); } } diff --git a/ivf/ivfstream.cpp b/ivf/ivfstream.cpp index e0dd214..496205c 100644 --- a/ivf/ivfstream.cpp +++ b/ivf/ivfstream.cpp @@ -20,8 +20,10 @@ namespace TagParser { * \sa https://wiki.multimedia.cx/index.php/IVF */ -void IvfStream::internalParseHeader(Diagnostics &diag) +void IvfStream::internalParseHeader(Diagnostics &diag, AbortableProgressFeedback &progress) { + CPP_UTILITIES_UNUSED(progress) + static const string context("parsing IVF header"); if (!m_istream) { throw NoDataFoundException(); diff --git a/ivf/ivfstream.h b/ivf/ivfstream.h index 6b4f3d1..da0a2d2 100644 --- a/ivf/ivfstream.h +++ b/ivf/ivfstream.h @@ -17,7 +17,7 @@ public: void readFrame(Diagnostics &diag); protected: - void internalParseHeader(Diagnostics &diag) override; + void internalParseHeader(Diagnostics &diag, AbortableProgressFeedback &progress) override; private: std::vector m_frames; diff --git a/matroska/matroskachapter.cpp b/matroska/matroskachapter.cpp index e1a23a7..0450a8c 100644 --- a/matroska/matroskachapter.cpp +++ b/matroska/matroskachapter.cpp @@ -39,8 +39,10 @@ MatroskaChapter::~MatroskaChapter() * - Fetches nested chapters but does not parse them. * - Clears all previous parsing results. */ -void MatroskaChapter::internalParse(Diagnostics &diag) +void MatroskaChapter::internalParse(Diagnostics &diag, AbortableProgressFeedback &progress) { + CPP_UTILITIES_UNUSED(progress) + // clear previous values and status static const string context("parsing \"ChapterAtom\"-element"); clear(); diff --git a/matroska/matroskachapter.h b/matroska/matroskachapter.h index 9061be2..5078f2c 100644 --- a/matroska/matroskachapter.h +++ b/matroska/matroskachapter.h @@ -20,7 +20,7 @@ public: void clear() override; protected: - void internalParse(Diagnostics &diag) override; + void internalParse(Diagnostics &diag, AbortableProgressFeedback &progress) override; private: EbmlElement *m_chapterAtomElement; diff --git a/matroska/matroskacontainer.cpp b/matroska/matroskacontainer.cpp index ef14482..4139071 100644 --- a/matroska/matroskacontainer.cpp +++ b/matroska/matroskacontainer.cpp @@ -82,7 +82,7 @@ void MatroskaContainer::reset() * \brief Validates the file index (cue entries). * \remarks Checks only for cluster positions and missing, unknown or surplus elements. */ -void MatroskaContainer::validateIndex(Diagnostics &diag) +void MatroskaContainer::validateIndex(Diagnostics &diag, AbortableProgressFeedback &progress) { static const string context("validating Matroska file index (cues)"); bool cuesElementsFound = false; @@ -98,6 +98,7 @@ void MatroskaContainer::validateIndex(Diagnostics &diag) // iterate throught all child elements of the segment (only "Cues"- and "Cluster"-elements are relevant for this method) for (EbmlElement *segmentChildElement = segmentElement->firstChild(); segmentChildElement; segmentChildElement = segmentChildElement->nextSibling()) { + progress.stopIfAborted(); segmentChildElement->parse(diag); switch (segmentChildElement->id()) { case EbmlIds::Void: @@ -108,6 +109,7 @@ void MatroskaContainer::validateIndex(Diagnostics &diag) // parse children of "Cues"-element ("CuePoint"-elements) for (EbmlElement *cuePointElement = segmentChildElement->firstChild(); cuePointElement; cuePointElement = cuePointElement->nextSibling()) { + progress.stopIfAborted(); cuePointElement->parse(diag); cueTimeFound = cueTrackPositionsFound = false; // to validate quantity of these elements switch (cuePointElement->id()) { @@ -398,8 +400,10 @@ ElementPosition MatroskaContainer::determineIndexPosition(Diagnostics &diag) con return determineElementPosition(MatroskaIds::Cues, diag); } -void MatroskaContainer::internalParseHeader(Diagnostics &diag) +void MatroskaContainer::internalParseHeader(Diagnostics &diag, AbortableProgressFeedback &progress) { + CPP_UTILITIES_UNUSED(progress) + static const string context("parsing header of Matroska container"); // reset old results m_firstElement = make_unique(*this, startOffset()); @@ -654,8 +658,10 @@ void MatroskaContainer::readTrackStatisticsFromTags(Diagnostics &diag) } } -void MatroskaContainer::internalParseTags(Diagnostics &diag) +void MatroskaContainer::internalParseTags(Diagnostics &diag, AbortableProgressFeedback &progress) { + CPP_UTILITIES_UNUSED(progress) + static const string context("parsing tags of Matroska container"); for (EbmlElement *element : m_tagsElements) { try { @@ -689,7 +695,7 @@ void MatroskaContainer::internalParseTags(Diagnostics &diag) readTrackStatisticsFromTags(diag); } -void MatroskaContainer::internalParseTracks(Diagnostics &diag) +void MatroskaContainer::internalParseTracks(Diagnostics &diag, AbortableProgressFeedback &progress) { static const string context("parsing tracks of Matroska container"); for (EbmlElement *element : m_tracksElements) { @@ -701,7 +707,7 @@ void MatroskaContainer::internalParseTracks(Diagnostics &diag) case MatroskaIds::TrackEntry: m_tracks.emplace_back(make_unique(*subElement)); try { - m_tracks.back()->parseHeader(diag); + m_tracks.back()->parseHeader(diag, progress); } catch (const NoDataFoundException &) { m_tracks.pop_back(); } catch (const Failure &) { @@ -725,7 +731,7 @@ void MatroskaContainer::internalParseTracks(Diagnostics &diag) readTrackStatisticsFromTags(diag); } -void MatroskaContainer::internalParseChapters(Diagnostics &diag) +void MatroskaContainer::internalParseChapters(Diagnostics &diag, AbortableProgressFeedback &progress) { static const string context("parsing editions/chapters of Matroska container"); for (EbmlElement *element : m_chaptersElements) { @@ -737,7 +743,7 @@ void MatroskaContainer::internalParseChapters(Diagnostics &diag) case MatroskaIds::EditionEntry: m_editionEntries.emplace_back(make_unique(subElement)); try { - m_editionEntries.back()->parseNested(diag); + m_editionEntries.back()->parseNested(diag, progress); } catch (const NoDataFoundException &) { m_editionEntries.pop_back(); } catch (const Failure &) { @@ -759,8 +765,10 @@ void MatroskaContainer::internalParseChapters(Diagnostics &diag) } } -void MatroskaContainer::internalParseAttachments(Diagnostics &diag) +void MatroskaContainer::internalParseAttachments(Diagnostics &diag, AbortableProgressFeedback &progress) { + CPP_UTILITIES_UNUSED(progress) + static const string context("parsing attachments of Matroska container"); for (EbmlElement *element : m_attachmentsElements) { try { @@ -1839,7 +1847,9 @@ void MatroskaContainer::internalMakeFile(Diagnostics &diag, AbortableProgressFee } reset(); try { - parseHeader(diag); + parseHeader(diag, progress); + } catch (const OperationAbortedException &) { + throw; } catch (const Failure &) { diag.emplace_back(DiagLevel::Critical, "Unable to reparse the header of the new file.", context); throw; diff --git a/matroska/matroskacontainer.h b/matroska/matroskacontainer.h index 58fe8cc..a1ccbe6 100644 --- a/matroska/matroskacontainer.h +++ b/matroska/matroskacontainer.h @@ -26,7 +26,7 @@ public: MatroskaContainer(MediaFileInfo &stream, std::uint64_t startOffset); ~MatroskaContainer() override; - void validateIndex(Diagnostics &diag); + void validateIndex(Diagnostics &diag, AbortableProgressFeedback &progress); std::uint64_t maxIdLength() const; std::uint64_t maxSizeLength() const; const std::vector> &seekInfos() const; @@ -49,11 +49,11 @@ public: void reset() override; protected: - void internalParseHeader(Diagnostics &diag) override; - void internalParseTags(Diagnostics &diag) override; - void internalParseTracks(Diagnostics &diag) override; - void internalParseChapters(Diagnostics &diag) override; - void internalParseAttachments(Diagnostics &diag) override; + void internalParseHeader(Diagnostics &diag, AbortableProgressFeedback &progress) override; + void internalParseTags(Diagnostics &diag, AbortableProgressFeedback &progress) override; + void internalParseTracks(Diagnostics &diag, AbortableProgressFeedback &progress) override; + void internalParseChapters(Diagnostics &diag, AbortableProgressFeedback &progress) override; + void internalParseAttachments(Diagnostics &diag, AbortableProgressFeedback &progress) override; void internalMakeFile(Diagnostics &diag, AbortableProgressFeedback &progress) override; private: diff --git a/matroska/matroskaeditionentry.cpp b/matroska/matroskaeditionentry.cpp index d722a6c..bf644c2 100644 --- a/matroska/matroskaeditionentry.cpp +++ b/matroska/matroskaeditionentry.cpp @@ -93,11 +93,12 @@ void MatroskaEditionEntry::parse(Diagnostics &diag) * - Parses also fetched chapters and nested chapters. * - Clears all previous parsing results. */ -void MatroskaEditionEntry::parseNested(Diagnostics &diag) +void MatroskaEditionEntry::parseNested(Diagnostics &diag, AbortableProgressFeedback &progress) { + progress.stopIfAborted(); parse(diag); for (auto &chapter : chapters()) { - chapter->parseNested(diag); + chapter->parseNested(diag, progress); } } diff --git a/matroska/matroskaeditionentry.h b/matroska/matroskaeditionentry.h index a728eae..9aef02d 100644 --- a/matroska/matroskaeditionentry.h +++ b/matroska/matroskaeditionentry.h @@ -21,7 +21,7 @@ public: const std::vector> &chapters() const; void parse(Diagnostics &diag); - void parseNested(Diagnostics &diag); + void parseNested(Diagnostics &diag, AbortableProgressFeedback &progress); void clear(); private: diff --git a/matroska/matroskatrack.cpp b/matroska/matroskatrack.cpp index 782836e..8c73555 100644 --- a/matroska/matroskatrack.cpp +++ b/matroska/matroskatrack.cpp @@ -283,8 +283,10 @@ void MatroskaTrack::readStatisticsFromTags(const std::vectorparse(diag); diff --git a/matroska/matroskatrack.h b/matroska/matroskatrack.h index 43336a9..8e7224a 100644 --- a/matroska/matroskatrack.h +++ b/matroska/matroskatrack.h @@ -61,7 +61,7 @@ public: void makeHeader(std::ostream &stream, Diagnostics &diag) const; protected: - void internalParseHeader(Diagnostics &diag) override; + void internalParseHeader(Diagnostics &diag, AbortableProgressFeedback &progress) override; private: template diff --git a/mediafileinfo.cpp b/mediafileinfo.cpp index 562f859..d732d30 100644 --- a/mediafileinfo.cpp +++ b/mediafileinfo.cpp @@ -144,12 +144,12 @@ MediaFileInfo::~MediaFileInfo() * container(), mp4Container() and matroskaContainer() will return the parsed * information. * \throws Throws std::ios_base::failure when an IO error occurs. - * \throws Throws TagParser::Failure or a derived exception when a parsing - * error occurs. * \sa isContainerParsed(), parseTracks(), parseTag(), parseChapters(), parseEverything() */ -void MediaFileInfo::parseContainerFormat(Diagnostics &diag) +void MediaFileInfo::parseContainerFormat(Diagnostics &diag, AbortableProgressFeedback &progress) { + CPP_UTILITIES_UNUSED(progress) + // skip if container format already parsed if (containerParsingStatus() != ParsingStatus::NotParsedYet) { return; @@ -168,6 +168,10 @@ void MediaFileInfo::parseContainerFormat(Diagnostics &diag) char buff[16]; const char *const buffEnd = buff + sizeof(buff), *buffOffset; startParsingSignature: + if (progress.isAborted()) { + diag.emplace_back(DiagLevel::Information, "Parsing the container format has been aborted.", context); + return; + } if (size() - containerOffset() >= 16) { stream().seekg(m_containerOffset, ios_base::beg); stream().read(buff, sizeof(buff)); @@ -224,7 +228,9 @@ startParsingSignature: // MP4/QuickTime is handled using Mp4Container instance m_container = make_unique(*this, m_containerOffset); try { - static_cast(m_container.get())->validateElementStructure(diag, &m_paddingSize); + static_cast(m_container.get())->validateElementStructure(diag, progress, &m_paddingSize); + } catch (const OperationAbortedException &) { + diag.emplace_back(DiagLevel::Information, "Validating the MP4 element structure has been aborted.", context); } catch (const Failure &) { m_containerParsingStatus = ParsingStatus::CriticalFailure; } @@ -234,7 +240,7 @@ startParsingSignature: // EBML/Matroska is handled using MatroskaContainer instance auto container = make_unique(*this, m_containerOffset); try { - container->parseHeader(diag); + container->parseHeader(diag, progress); if (container->documentType() == "matroska") { m_containerFormat = ContainerFormat::Matroska; } else if (container->documentType() == "webm") { @@ -243,9 +249,11 @@ startParsingSignature: if (m_forceFullParse) { // validating the element structure of Matroska files takes too long when // parsing big files so do this only when explicitely desired - container->validateElementStructure(diag, &m_paddingSize); - container->validateIndex(diag); + container->validateElementStructure(diag, progress, &m_paddingSize); + container->validateIndex(diag, progress); } + } catch (const OperationAbortedException &) { + diag.emplace_back(DiagLevel::Information, "Validating the Matroska element structure has been aborted.", context); } catch (const Failure &) { m_containerParsingStatus = ParsingStatus::CriticalFailure; } @@ -299,12 +307,10 @@ startParsingSignature: * hasTracksOfType() will return the parsed information. * * \throws Throws std::ios_base::failure when an IO error occurs. - * \throws Throws TagParser::Failure or a derived exception when a parsing - * error occurs. * \remarks parseContainerFormat() must be called before. * \sa areTracksParsed(), parseContainerFormat(), parseTags(), parseChapters(), parseEverything() */ -void MediaFileInfo::parseTracks(Diagnostics &diag) +void MediaFileInfo::parseTracks(Diagnostics &diag, AbortableProgressFeedback &progress) { // skip if tracks already parsed if (tracksParsingStatus() != ParsingStatus::NotParsedYet) { @@ -315,7 +321,7 @@ void MediaFileInfo::parseTracks(Diagnostics &diag) try { // parse tracks via container object if (m_container) { - m_container->parseTracks(diag); + m_container->parseTracks(diag, progress); m_tracksParsingStatus = ParsingStatus::Ok; return; } @@ -340,7 +346,7 @@ void MediaFileInfo::parseTracks(Diagnostics &diag) default: throw NotImplementedException(); } - m_singleTrack->parseHeader(diag); + m_singleTrack->parseHeader(diag, progress); // take padding for some "single-track" formats into account switch (m_containerFormat) { @@ -355,6 +361,8 @@ void MediaFileInfo::parseTracks(Diagnostics &diag) } catch (const NotImplementedException &) { diag.emplace_back(DiagLevel::Information, "Parsing tracks is not implemented for the container format of the file.", context); m_tracksParsingStatus = ParsingStatus::NotSupported; + } catch (const OperationAbortedException &) { + diag.emplace_back(DiagLevel::Information, "Parsing tracks has been aborted.", context); } catch (const Failure &) { diag.emplace_back(DiagLevel::Critical, "Unable to parse tracks.", context); m_tracksParsingStatus = ParsingStatus::CriticalFailure; @@ -370,12 +378,10 @@ void MediaFileInfo::parseTracks(Diagnostics &diag) * * Previously assigned but not applied tag information will be discarted. * \throws Throws std::ios_base::failure when an IO error occurs. - * \throws Throws TagParser::Failure or a derived exception when a parsing - * error occurs. * \remarks parseContainerFormat() must be called before. * \sa isTagParsed(), parseContainerFormat(), parseTracks(), parseChapters(), parseEverything() */ -void MediaFileInfo::parseTags(Diagnostics &diag) +void MediaFileInfo::parseTags(Diagnostics &diag, AbortableProgressFeedback &progress) { // skip if tags already parsed if (tagsParsingStatus() != ParsingStatus::NotParsedYet) { @@ -392,6 +398,9 @@ void MediaFileInfo::parseTags(Diagnostics &diag) m_actualExistingId3v1Tag = true; } catch (const NoDataFoundException &) { m_id3v1Tag.reset(); + } catch (const OperationAbortedException &) { + diag.emplace_back(DiagLevel::Information, "Parsing ID3v1 tag has been aborted.", context); + return; } catch (const Failure &) { m_tagsParsingStatus = ParsingStatus::CriticalFailure; diag.emplace_back(DiagLevel::Critical, "Unable to parse ID3v1 tag.", context); @@ -408,6 +417,9 @@ void MediaFileInfo::parseTags(Diagnostics &diag) m_paddingSize += id3v2Tag->paddingSize(); } catch (const NoDataFoundException &) { continue; + } catch (const OperationAbortedException &) { + diag.emplace_back(DiagLevel::Information, "Parsing ID3v2 tags has been aborted.", context); + return; } catch (const Failure &) { m_tagsParsingStatus = ParsingStatus::CriticalFailure; diag.emplace_back(DiagLevel::Critical, "Unable to parse ID3v2 tag.", context); @@ -418,13 +430,13 @@ void MediaFileInfo::parseTags(Diagnostics &diag) // check for tags in tracks (FLAC only) or via container object try { if (m_containerFormat == ContainerFormat::Flac) { - parseTracks(diag); + parseTracks(diag, progress); if (m_tagsParsingStatus == ParsingStatus::NotParsedYet) { m_tagsParsingStatus = m_tracksParsingStatus; } return; } else if (m_container) { - m_container->parseTags(diag); + m_container->parseTags(diag, progress); } else { throw NotImplementedException(); } @@ -440,6 +452,9 @@ void MediaFileInfo::parseTags(Diagnostics &diag) m_tagsParsingStatus = ParsingStatus::NotSupported; } diag.emplace_back(DiagLevel::Information, "Parsing tags is not implemented for the container format of the file.", context); + } catch (const OperationAbortedException &) { + diag.emplace_back(DiagLevel::Information, "Parsing tags from container/streams has been aborted.", context); + return; } catch (const Failure &) { m_tagsParsingStatus = ParsingStatus::CriticalFailure; diag.emplace_back(DiagLevel::Critical, "Unable to parse tag.", context); @@ -452,12 +467,10 @@ void MediaFileInfo::parseTags(Diagnostics &diag) * This method parses the chapters of the current file if not been parsed yet. * * \throws Throws std::ios_base::failure when an IO error occurs. - * \throws Throws TagParser::Failure or a derived exception when a parsing - * error occurs. * \remarks parseContainerFormat() must be called before. * \sa areChaptersParsed(), parseContainerFormat(), parseTracks(), parseTags(), parseEverything() */ -void MediaFileInfo::parseChapters(Diagnostics &diag) +void MediaFileInfo::parseChapters(Diagnostics &diag, AbortableProgressFeedback &progress) { // skip if chapters already parsed if (chaptersParsingStatus() != ParsingStatus::NotParsedYet) { @@ -470,7 +483,7 @@ void MediaFileInfo::parseChapters(Diagnostics &diag) if (!m_container) { throw NotImplementedException(); } - m_container->parseChapters(diag); + m_container->parseChapters(diag, progress); m_chaptersParsingStatus = ParsingStatus::Ok; } catch (const NotImplementedException &) { m_chaptersParsingStatus = ParsingStatus::NotSupported; @@ -487,12 +500,10 @@ void MediaFileInfo::parseChapters(Diagnostics &diag) * This method parses the attachments of the current file if not been parsed yet. * * \throws Throws std::ios_base::failure when an IO error occurs. - * \throws Throws TagParser::Failure or a derived exception when a parsing - * error occurs. * \remarks parseContainerFormat() must be called before. * \sa areChaptersParsed(), parseContainerFormat(), parseTracks(), parseTags(), parseEverything() */ -void MediaFileInfo::parseAttachments(Diagnostics &diag) +void MediaFileInfo::parseAttachments(Diagnostics &diag, AbortableProgressFeedback &progress) { // skip if attachments already parsed if (attachmentsParsingStatus() != ParsingStatus::NotParsedYet) { @@ -505,7 +516,7 @@ void MediaFileInfo::parseAttachments(Diagnostics &diag) if (!m_container) { throw NotImplementedException(); } - m_container->parseAttachments(diag); + m_container->parseAttachments(diag, progress); m_attachmentsParsingStatus = ParsingStatus::Ok; } catch (const NotImplementedException &) { m_attachmentsParsingStatus = ParsingStatus::NotSupported; @@ -522,13 +533,25 @@ void MediaFileInfo::parseAttachments(Diagnostics &diag) * See the individual methods to for more details and exceptions which might be thrown. * \sa parseContainerFormat(), parseTracks(), parseTags() */ -void MediaFileInfo::parseEverything(Diagnostics &diag) +void MediaFileInfo::parseEverything(Diagnostics &diag, AbortableProgressFeedback &progress) { - parseContainerFormat(diag); - parseTracks(diag); - parseTags(diag); - parseChapters(diag); - parseAttachments(diag); + parseContainerFormat(diag, progress); + if (progress.isAborted()) { + return; + } + parseTracks(diag, progress); + if (progress.isAborted()) { + return; + } + parseTags(diag, progress); + if (progress.isAborted()) { + return; + } + parseChapters(diag, progress); + if (progress.isAborted()) { + return; + } + parseAttachments(diag, progress); } /*! diff --git a/mediafileinfo.h b/mediafileinfo.h index 0cc9de4..f1a4410 100644 --- a/mediafileinfo.h +++ b/mediafileinfo.h @@ -52,12 +52,12 @@ public: ~MediaFileInfo() override; // methods to parse file - void parseContainerFormat(Diagnostics &diag); - void parseTracks(Diagnostics &diag); - void parseTags(Diagnostics &diag); - void parseChapters(Diagnostics &diag); - void parseAttachments(Diagnostics &diag); - void parseEverything(Diagnostics &diag); + void parseContainerFormat(Diagnostics &diag, AbortableProgressFeedback &progress); + void parseTracks(Diagnostics &diag, AbortableProgressFeedback &progress); + void parseTags(Diagnostics &diag, AbortableProgressFeedback &progress); + void parseChapters(Diagnostics &diag, AbortableProgressFeedback &progress); + void parseAttachments(Diagnostics &diag, AbortableProgressFeedback &progress); + void parseEverything(Diagnostics &diag, AbortableProgressFeedback &progress); // methods to apply changes void applyChanges(Diagnostics &diag, AbortableProgressFeedback &progress); diff --git a/mp4/mp4container.cpp b/mp4/mp4container.cpp index 959bbb1..63f1665 100644 --- a/mp4/mp4container.cpp +++ b/mp4/mp4container.cpp @@ -69,9 +69,9 @@ ElementPosition Mp4Container::determineIndexPosition(Diagnostics &diag) const return ElementPosition::Keep; } -void Mp4Container::internalParseHeader(Diagnostics &diag) +void Mp4Container::internalParseHeader(Diagnostics &diag, AbortableProgressFeedback &progress) { - //const string context("parsing header of MP4 container"); will be used when generating notifications + CPP_UTILITIES_UNUSED(progress) //const string context("parsing header of MP4 container"); will be used when generating notifications m_firstElement = make_unique(*this, startOffset()); m_firstElement->parse(diag); auto *const ftypAtom = m_firstElement->siblingByIdIncludingThis(Mp4AtomIds::FileType, diag); @@ -85,8 +85,9 @@ void Mp4Container::internalParseHeader(Diagnostics &diag) m_version = reader().readUInt32BE(); } -void Mp4Container::internalParseTags(Diagnostics &diag) +void Mp4Container::internalParseTags(Diagnostics &diag, AbortableProgressFeedback &progress) { + CPP_UTILITIES_UNUSED(progress) const string context("parsing tags of MP4 container"); auto *const udtaAtom = firstElement()->subelementByPath(diag, Mp4AtomIds::Movie, Mp4AtomIds::UserData); if (!udtaAtom) { @@ -114,7 +115,7 @@ void Mp4Container::internalParseTags(Diagnostics &diag) } } -void Mp4Container::internalParseTracks(Diagnostics &diag) +void Mp4Container::internalParseTracks(Diagnostics &diag, AbortableProgressFeedback &progress) { static const string context("parsing tracks of MP4 container"); try { @@ -185,7 +186,7 @@ void Mp4Container::internalParseTracks(Diagnostics &diag) // parse the trak atom using the Mp4Track class m_tracks.emplace_back(make_unique(*trakAtom)); try { // try to parse header - m_tracks.back()->parseHeader(diag); + m_tracks.back()->parseHeader(diag, progress); } catch (const Failure &) { diag.emplace_back(DiagLevel::Critical, argsToString("Unable to parse track ", trackNum, '.'), context); } @@ -843,7 +844,9 @@ calculatePadding: reset(); try { - parseTracks(diag); + parseTracks(diag, progress); + } catch (const OperationAbortedException &) { + throw; } catch (const Failure &) { diag.emplace_back(DiagLevel::Critical, "Unable to reparse the new file.", context); throw; @@ -877,7 +880,7 @@ calculatePadding: } } else { progress.updateStep("Updating chunk offset table for each track ..."); - updateOffsets(origMediaDataOffsets, newMediaDataOffsets, diag); + updateOffsets(origMediaDataOffsets, newMediaDataOffsets, diag, progress); } } @@ -902,7 +905,8 @@ calculatePadding: * \throws Throws TagParser::Failure or a derived exception when a making * error occurs. */ -void Mp4Container::updateOffsets(const std::vector &oldMdatOffsets, const std::vector &newMdatOffsets, Diagnostics &diag) +void Mp4Container::updateOffsets(const std::vector &oldMdatOffsets, const std::vector &newMdatOffsets, Diagnostics &diag, + AbortableProgressFeedback &progress) { // do NOT invalidate the status here since this method is internally called by internalMakeFile(), just update the status const string context("updating MP4 container chunk offset table"); @@ -972,7 +976,7 @@ void Mp4Container::updateOffsets(const std::vector &oldMdatOffsets for (auto &track : tracks()) { if (!track->isHeaderValid()) { try { - track->parseHeader(diag); + track->parseHeader(diag, progress); } catch (const Failure &) { diag.emplace_back(DiagLevel::Warning, "The chunk offsets of track " % track->name() + " couldn't be updated because the track seems to be invalid..", context); diff --git a/mp4/mp4container.h b/mp4/mp4container.h index 3a9d511..e26f931 100644 --- a/mp4/mp4container.h +++ b/mp4/mp4container.h @@ -27,13 +27,14 @@ public: ElementPosition determineIndexPosition(Diagnostics &diag) const override; protected: - void internalParseHeader(Diagnostics &diag) override; - void internalParseTags(Diagnostics &diag) override; - void internalParseTracks(Diagnostics &diag) override; + void internalParseHeader(Diagnostics &diag, AbortableProgressFeedback &progress) override; + void internalParseTags(Diagnostics &diag, AbortableProgressFeedback &progress) override; + void internalParseTracks(Diagnostics &diag, AbortableProgressFeedback &progress) override; void internalMakeFile(Diagnostics &diag, AbortableProgressFeedback &progress) override; private: - void updateOffsets(const std::vector &oldMdatOffsets, const std::vector &newMdatOffsets, Diagnostics &diag); + void updateOffsets(const std::vector &oldMdatOffsets, const std::vector &newMdatOffsets, Diagnostics &diag, + AbortableProgressFeedback &progress); bool m_fragmented; }; diff --git a/mp4/mp4track.cpp b/mp4/mp4track.cpp index f7ea73d..f5537b5 100644 --- a/mp4/mp4track.cpp +++ b/mp4/mp4track.cpp @@ -1472,8 +1472,10 @@ void Mp4Track::makeSampleTable(Diagnostics &diag) // Mp4Atom::seekBackAndWriteAtomSize(outputStream(), stblStartOffset, diag); } -void Mp4Track::internalParseHeader(Diagnostics &diag) +void Mp4Track::internalParseHeader(Diagnostics &diag, AbortableProgressFeedback &progress) { + CPP_UTILITIES_UNUSED(progress) + static const string context("parsing MP4 track"); using namespace Mp4AtomIds; if (!m_trakAtom) { diff --git a/mp4/mp4track.h b/mp4/mp4track.h index 8dcbaaf..99af20e 100644 --- a/mp4/mp4track.h +++ b/mp4/mp4track.h @@ -162,7 +162,7 @@ public: static void addInfo(const Av1Configuration &av1Config, AbstractTrack &track); protected: - void internalParseHeader(Diagnostics &diag) override; + void internalParseHeader(Diagnostics &diag, AbortableProgressFeedback &progress) override; private: // private helper methods diff --git a/mpegaudio/mpegaudioframestream.cpp b/mpegaudio/mpegaudioframestream.cpp index 5ee8c6e..6f1d22e 100644 --- a/mpegaudio/mpegaudioframestream.cpp +++ b/mpegaudio/mpegaudioframestream.cpp @@ -27,8 +27,10 @@ void MpegAudioFrameStream::addInfo(const MpegAudioFrame &frame, AbstractTrack &t track.m_samplingFrequency = frame.samplingFrequency(); } -void MpegAudioFrameStream::internalParseHeader(Diagnostics &diag) +void MpegAudioFrameStream::internalParseHeader(Diagnostics &diag, AbortableProgressFeedback &progress) { + CPP_UTILITIES_UNUSED(progress) + static const string context("parsing MPEG audio frame header"); if (!m_istream) { throw NoDataFoundException(); diff --git a/mpegaudio/mpegaudioframestream.h b/mpegaudio/mpegaudioframestream.h index b7f9c1c..9856680 100644 --- a/mpegaudio/mpegaudioframestream.h +++ b/mpegaudio/mpegaudioframestream.h @@ -19,7 +19,7 @@ public: static void addInfo(const MpegAudioFrame &frame, AbstractTrack &track); protected: - void internalParseHeader(Diagnostics &diag) override; + void internalParseHeader(Diagnostics &diag, AbortableProgressFeedback &progress) override; private: std::list m_frames; diff --git a/ogg/oggcontainer.cpp b/ogg/oggcontainer.cpp index a9aeb34..97e4ccb 100644 --- a/ogg/oggcontainer.cpp +++ b/ogg/oggcontainer.cpp @@ -183,8 +183,10 @@ void OggContainer::removeAllTags() } } -void OggContainer::internalParseHeader(Diagnostics &diag) +void OggContainer::internalParseHeader(Diagnostics &diag, AbortableProgressFeedback &progress) { + CPP_UTILITIES_UNUSED(progress) + static const string context("parsing OGG bitstream header"); bool pagesSkipped = false; @@ -258,10 +260,10 @@ void OggContainer::internalParseHeader(Diagnostics &diag) } } -void OggContainer::internalParseTags(Diagnostics &diag) +void OggContainer::internalParseTags(Diagnostics &diag, AbortableProgressFeedback &progress) { // tracks needs to be parsed before because tags are stored at stream level - parseTracks(diag); + parseTracks(diag, progress); for (auto &comment : m_tags) { OggParameter ¶ms = comment->oggParams(); m_iterator.setPageIndex(params.firstPageIndex); @@ -304,12 +306,15 @@ void OggContainer::announceComment(std::size_t pageIndex, std::size_t segmentInd m_tags.back()->oggParams().set(pageIndex, segmentIndex, lastMetaDataBlock, mediaFormat); } -void OggContainer::internalParseTracks(Diagnostics &diag) +void OggContainer::internalParseTracks(Diagnostics &diag, AbortableProgressFeedback &progress) { static const string context("parsing OGG stream"); for (auto &stream : m_tracks) { + if (progress.isAborted()) { + throw OperationAbortedException(); + } try { // try to parse header - stream->parseHeader(diag); + stream->parseHeader(diag, progress); if (stream->duration() > m_duration) { m_duration = stream->duration(); } @@ -367,7 +372,7 @@ void OggContainer::internalMakeFile(Diagnostics &diag, AbortableProgressFeedback { const string context("making OGG file"); progress.updateStep("Prepare for rewriting OGG file ..."); - parseTags(diag); // tags need to be parsed before the file can be rewritten + parseTags(diag, progress); // tags need to be parsed before the file can be rewritten string backupPath; NativeFileStream backupStream; diff --git a/ogg/oggcontainer.h b/ogg/oggcontainer.h index e36b6b4..4157618 100644 --- a/ogg/oggcontainer.h +++ b/ogg/oggcontainer.h @@ -144,9 +144,9 @@ public: void removeAllTags() override; protected: - void internalParseHeader(Diagnostics &diag) override; - void internalParseTags(Diagnostics &diag) override; - void internalParseTracks(Diagnostics &diag) override; + void internalParseHeader(Diagnostics &diag, AbortableProgressFeedback &progress) override; + void internalParseTags(Diagnostics &diag, AbortableProgressFeedback &progress) override; + void internalParseTracks(Diagnostics &diag, AbortableProgressFeedback &progress) override; void internalMakeFile(Diagnostics &diag, AbortableProgressFeedback &progress) override; private: diff --git a/ogg/oggstream.cpp b/ogg/oggstream.cpp index b7a06b3..9dd0d92 100644 --- a/ogg/oggstream.cpp +++ b/ogg/oggstream.cpp @@ -46,8 +46,10 @@ OggStream::~OggStream() { } -void OggStream::internalParseHeader(Diagnostics &diag) +void OggStream::internalParseHeader(Diagnostics &diag, AbortableProgressFeedback &progress) { + CPP_UTILITIES_UNUSED(progress) + static const string context("parsing OGG page header"); // read basic information from first page diff --git a/ogg/oggstream.h b/ogg/oggstream.h index 21e67b6..11d85e0 100644 --- a/ogg/oggstream.h +++ b/ogg/oggstream.h @@ -21,7 +21,7 @@ public: std::size_t startPage() const; protected: - void internalParseHeader(Diagnostics &diag) override; + void internalParseHeader(Diagnostics &diag, AbortableProgressFeedback &progress) override; private: void calculateDurationViaSampleCount(std::uint16_t preSkip = 0); diff --git a/progressfeedback.h b/progressfeedback.h index 17a8226..635296b 100644 --- a/progressfeedback.h +++ b/progressfeedback.h @@ -15,7 +15,7 @@ public: using Callback = std::function; explicit BasicProgressFeedback(const Callback &callback, const Callback &percentageOnlyCallback = Callback()); - explicit BasicProgressFeedback(Callback &&callback, Callback &&percentageOnlyCallback = Callback()); + explicit BasicProgressFeedback(Callback &&callback = Callback(), Callback &&percentageOnlyCallback = Callback()); const std::string &step() const; std::uint8_t stepPercentage() const; @@ -159,8 +159,8 @@ inline void BasicProgressFeedback::updateOverallPercenta class ProgressFeedback : public BasicProgressFeedback { public: - ProgressFeedback(const Callback &callback, const Callback &percentageOnlyCallback = Callback()); - ProgressFeedback(Callback &&callback, Callback &&percentageOnlyCallback = Callback()); + explicit ProgressFeedback(const Callback &callback, const Callback &percentageOnlyCallback = Callback()); + explicit ProgressFeedback(Callback &&callback = Callback(), Callback &&percentageOnlyCallback = Callback()); }; /*! @@ -185,8 +185,8 @@ inline ProgressFeedback::ProgressFeedback(Callback &&callback, Callback &&percen class AbortableProgressFeedback : public BasicProgressFeedback { public: - AbortableProgressFeedback(const Callback &callback, const Callback &percentageOnlyCallback = Callback()); - AbortableProgressFeedback(Callback &&callback, Callback &&percentageOnlyCallback = Callback()); + explicit AbortableProgressFeedback(const Callback &callback, const Callback &percentageOnlyCallback = Callback()); + explicit AbortableProgressFeedback(Callback &&callback = Callback(), Callback &&percentageOnlyCallback = Callback()); bool isAborted() const; void tryToAbort(); diff --git a/tests/mediafileinfo.cpp b/tests/mediafileinfo.cpp index 8c57208..4625475 100644 --- a/tests/mediafileinfo.cpp +++ b/tests/mediafileinfo.cpp @@ -2,6 +2,7 @@ #include "../abstracttrack.h" #include "../mediafileinfo.h" +#include "../progressfeedback.h" #include "../tag.h" #include @@ -86,9 +87,10 @@ void MediaFileInfoTests::testFileSystemMethods() void MediaFileInfoTests::testParsingUnsupportedFile() { Diagnostics diag; + AbortableProgressFeedback progress; MediaFileInfo file(testFilePath("unsupported.bin")); - file.parseContainerFormat(diag); - file.parseTags(diag); + file.parseContainerFormat(diag, progress); + file.parseTags(diag, progress); CPPUNIT_ASSERT_EQUAL(ParsingStatus::NotSupported, file.containerParsingStatus()); CPPUNIT_ASSERT_EQUAL(ParsingStatus::NotSupported, file.tagsParsingStatus()); CPPUNIT_ASSERT_EQUAL(ParsingStatus::NotParsedYet, file.tracksParsingStatus()); @@ -101,13 +103,14 @@ void MediaFileInfoTests::testParsingUnsupportedFile() void MediaFileInfoTests::testPartialParsingAndTagCreationOfMp4File() { Diagnostics diag; + AbortableProgressFeedback progress; MediaFileInfo file(testFilePath("mtx-test-data/aac/he-aacv2-ps.m4a")); file.open(true); - file.parseContainerFormat(diag); - file.parseTags(diag); - file.parseAttachments(diag); + file.parseContainerFormat(diag, progress); + file.parseTags(diag, progress); + file.parseAttachments(diag, progress); file.close(); - CPPUNIT_ASSERT_THROW_MESSAGE("std::ios_base::failure thrown if file closed", file.parseTracks(diag), std::ios_base::failure); + CPPUNIT_ASSERT_THROW_MESSAGE("std::ios_base::failure thrown if file closed", file.parseTracks(diag, progress), std::ios_base::failure); CPPUNIT_ASSERT(file.areTagsSupported()); CPPUNIT_ASSERT(file.areTracksSupported()); CPPUNIT_ASSERT(!file.areChaptersSupported()); @@ -145,12 +148,13 @@ void MediaFileInfoTests::testPartialParsingAndTagCreationOfMp4File() void MediaFileInfoTests::testFullParseAndFurtherProperties() { Diagnostics diag; + AbortableProgressFeedback progress; MediaFileInfo file(testFilePath("matroska_wave1/test1.mkv")); file.open(true); - file.parseEverything(diag); + file.parseEverything(diag, progress); // calling parse methods twice should not do anything (and hence can not fail anymore because the file has already been closed) file.close(); - file.parseEverything(diag); + file.parseEverything(diag, progress); CPPUNIT_ASSERT_EQUAL(ParsingStatus::Ok, file.containerParsingStatus()); CPPUNIT_ASSERT_EQUAL(ParsingStatus::Ok, file.tagsParsingStatus()); CPPUNIT_ASSERT_EQUAL(ParsingStatus::Ok, file.tracksParsingStatus()); diff --git a/tests/overallgeneral.cpp b/tests/overallgeneral.cpp index d7b95a7..4402ed6 100644 --- a/tests/overallgeneral.cpp +++ b/tests/overallgeneral.cpp @@ -37,7 +37,7 @@ void OverallTests::parseFile(const string &path, void (OverallTests::*checkRouti m_diag.clear(); m_fileInfo.setPath(path); m_fileInfo.reopen(true); - m_fileInfo.parseEverything(m_diag); + m_fileInfo.parseEverything(m_diag, m_progress); // invoke testroutine to check whether parsing results are correct (this->*checkRoutine)(); m_fileInfo.close(); @@ -55,7 +55,7 @@ void OverallTests::makeFile(const string &path, void (OverallTests::*modifyRouti m_diag.clear(); m_fileInfo.setPath(path); m_fileInfo.reopen(true); - m_fileInfo.parseEverything(m_diag); + m_fileInfo.parseEverything(m_diag, m_progress); // determine expected tag and index position switch (m_fileInfo.containerFormat()) { @@ -96,7 +96,7 @@ void OverallTests::makeFile(const string &path, void (OverallTests::*modifyRouti m_fileInfo.applyChanges(m_diag, m_progress); m_fileInfo.clearParsingResults(); // reparse the file and invoke testroutine to check whether changings have been applied correctly - m_fileInfo.parseEverything(m_diag); + m_fileInfo.parseEverything(m_diag, m_progress); (this->*checkRoutine)(); // invoke suitable testroutine to check padding constraints switch (m_fileInfo.containerFormat()) { diff --git a/tests/overallmkv.cpp b/tests/overallmkv.cpp index ce4e970..d3fb64f 100644 --- a/tests/overallmkv.cpp +++ b/tests/overallmkv.cpp @@ -622,7 +622,7 @@ void OverallTests::setMkvTestMetaData() // assign an attachment AbstractAttachment *const attachment = container->createAttachment(); CPPUNIT_ASSERT_MESSAGE("create attachment", attachment); - attachment->setFile(testFilePath("matroska_wave1/logo3_256x256.png"), m_diag); + attachment->setFile(testFilePath("matroska_wave1/logo3_256x256.png"), m_diag, m_progress); attachment->setMimeType("image/png"); attachment->setName("cover.jpg"); } diff --git a/tests/overallmp4.cpp b/tests/overallmp4.cpp index 92adc5d..bfc8aca 100644 --- a/tests/overallmp4.cpp +++ b/tests/overallmp4.cpp @@ -497,8 +497,8 @@ void OverallTests::alterMp4Tracks() { m_additionalFileInfo.setPath(testFilePath("mtx-test-data/mp4/10-DanseMacabreOp.40.m4a")); m_additionalFileInfo.reopen(true); - m_additionalFileInfo.parseContainerFormat(m_diag); - m_additionalFileInfo.parseTracks(m_diag); + m_additionalFileInfo.parseContainerFormat(m_diag, m_progress); + m_additionalFileInfo.parseTracks(m_diag, m_progress); CPPUNIT_ASSERT_EQUAL(ContainerFormat::Mp4, m_additionalFileInfo.containerFormat()); CPPUNIT_ASSERT_EQUAL(ContainerFormat::Mp4, m_fileInfo.containerFormat()); const auto &tracks = m_additionalFileInfo.tracks(); diff --git a/wav/waveaudiostream.cpp b/wav/waveaudiostream.cpp index 230a219..bf293a1 100644 --- a/wav/waveaudiostream.cpp +++ b/wav/waveaudiostream.cpp @@ -144,8 +144,10 @@ void WaveAudioStream::addInfo(const WaveFormatHeader &waveHeader, AbstractTrack track.m_bitrate = waveHeader.bitrate(); } -void WaveAudioStream::internalParseHeader(Diagnostics &diag) +void WaveAudioStream::internalParseHeader(Diagnostics &diag, AbortableProgressFeedback &progress) { + CPP_UTILITIES_UNUSED(progress) + const string context("parsing RIFF/WAVE header"); if (!m_istream) { throw NoDataFoundException(); diff --git a/wav/waveaudiostream.h b/wav/waveaudiostream.h index f881040..048628e 100644 --- a/wav/waveaudiostream.h +++ b/wav/waveaudiostream.h @@ -58,7 +58,7 @@ public: static void addInfo(const WaveFormatHeader &waveHeader, AbstractTrack &track); protected: - void internalParseHeader(Diagnostics &diag) override; + void internalParseHeader(Diagnostics &diag, AbortableProgressFeedback &progress) override; private: std::uint64_t m_dataOffset;