Add API to allow aborting overall/expensive parsing functions

* Not really implemented within the various code paths of the parsers at
  this point; this commit mainly adds the API.
* Adjust example in README
This commit is contained in:
Martchus 2021-02-04 23:21:50 +01:00
parent 763eb1bd53
commit 65597fd71e
44 changed files with 306 additions and 184 deletions

View File

@ -51,33 +51,74 @@ This example shows how to read and write tag fields in a format-independent way:
```
#include <tagparser/mediafileinfo.h>
#include <tagparser/diagnostics.h>
#include <tagparser/progressfeedback.h>
// 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 <tagparser/tag.h>
#include <tagparser/tagvalue.h>
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 <tagparser/abstractattachment.h>
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.

View File

@ -2,6 +2,7 @@
#include "./exceptions.h"
#include "./mediafileinfo.h"
#include "./progressfeedback.h"
#include <c++utilities/io/copy.h>
@ -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<MediaFileInfo>())
{
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<FileDataBlock>(path, diag);
auto file = make_unique<FileDataBlock>(path, diag, progress);
const auto fileName = file->fileInfo()->fileName();
if (!fileName.empty()) {
m_name = fileName;

View File

@ -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<StreamDataBlock> &&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();

View File

@ -1,4 +1,5 @@
#include "./abstractchapter.h"
#include "./progressfeedback.h"
#include <sstream>
@ -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);
}
}

View File

@ -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<LocaleAwareString> m_names;

View File

@ -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();
}

View File

@ -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;

View File

@ -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<streamoff>(m_startOffset), ios_base::beg);
try {
internalParseHeader(diag);
internalParseHeader(diag, progress);
m_flags += TrackFlags::HeaderValid;
} catch (const Failure &) {
throw;

View File

@ -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;

View File

@ -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) {

View File

@ -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;

View File

@ -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();

View File

@ -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;

View File

@ -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<std::unique_ptr<ElementType>> &additionalElements() const;
@ -96,11 +96,12 @@ GenericContainer<FileInfoType, TagType, TrackType, ElementType>::~GenericContain
* \throws Throws std::ios_base::failure when an IO error occurs.
*/
template <class FileInfoType, class TagType, class TrackType, class ElementType>
inline void GenericContainer<FileInfoType, TagType, TrackType, ElementType>::validateElementStructure(Diagnostics &diag, std::uint64_t *paddingSize)
inline void GenericContainer<FileInfoType, TagType, TrackType, ElementType>::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);
}
}

View File

@ -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 <class ImplementationType> void GenericFileElement<ImplementationType>:
* \sa parse()
*/
template <class ImplementationType>
void GenericFileElement<ImplementationType>::validateSubsequentElementStructure(Diagnostics &diag, std::uint64_t *paddingSize)
void GenericFileElement<ImplementationType>::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<ImplementationType>::validateSubsequentElementStructure(
}
// validate siblings
if (nextSibling()) {
nextSibling()->validateSubsequentElementStructure(diag, paddingSize);
nextSibling()->validateSubsequentElementStructure(diag, paddingSize, progress);
}
}

View File

@ -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();

View File

@ -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<IvfFrame> m_frames;

View File

@ -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();

View File

@ -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;

View File

@ -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<EbmlElement>(*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<MatroskaTrack>(*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<MatroskaEditionEntry>(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;

View File

@ -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<std::unique_ptr<MatroskaSeekInfo>> &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:

View File

@ -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);
}
}

View File

@ -21,7 +21,7 @@ public:
const std::vector<std::unique_ptr<MatroskaChapter>> &chapters() const;
void parse(Diagnostics &diag);
void parseNested(Diagnostics &diag);
void parseNested(Diagnostics &diag, AbortableProgressFeedback &progress);
void clear();
private:

View File

@ -283,8 +283,10 @@ void MatroskaTrack::readStatisticsFromTags(const std::vector<std::unique_ptr<Mat
}
}
void MatroskaTrack::internalParseHeader(Diagnostics &diag)
void MatroskaTrack::internalParseHeader(Diagnostics &diag, AbortableProgressFeedback &progress)
{
CPP_UTILITIES_UNUSED(progress)
static const string context("parsing header of Matroska track");
try {
m_trackElement->parse(diag);

View File

@ -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 <typename PropertyType, typename ConversionFunction>

View File

@ -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<Mp4Container>(*this, m_containerOffset);
try {
static_cast<Mp4Container *>(m_container.get())->validateElementStructure(diag, &m_paddingSize);
static_cast<Mp4Container *>(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<MatroskaContainer>(*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);
}
/*!

View File

@ -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);

View File

@ -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<Mp4Atom>(*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<Mp4Track>(*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<std::int64_t> &oldMdatOffsets, const std::vector<std::int64_t> &newMdatOffsets, Diagnostics &diag)
void Mp4Container::updateOffsets(const std::vector<std::int64_t> &oldMdatOffsets, const std::vector<std::int64_t> &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<std::int64_t> &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);

View File

@ -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<std::int64_t> &oldMdatOffsets, const std::vector<std::int64_t> &newMdatOffsets, Diagnostics &diag);
void updateOffsets(const std::vector<std::int64_t> &oldMdatOffsets, const std::vector<std::int64_t> &newMdatOffsets, Diagnostics &diag,
AbortableProgressFeedback &progress);
bool m_fragmented;
};

View File

@ -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) {

View File

@ -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

View File

@ -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();

View File

@ -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<MpegAudioFrame> m_frames;

View File

@ -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 &params = 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;

View File

@ -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:

View File

@ -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

View File

@ -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);

View File

@ -15,7 +15,7 @@ public:
using Callback = std::function<void(ActualProgressFeedback &feedback)>;
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<ActualProgressFeedback>::updateOverallPercenta
class ProgressFeedback : public BasicProgressFeedback<ProgressFeedback> {
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<AbortableProgressFeedback> {
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();

View File

@ -2,6 +2,7 @@
#include "../abstracttrack.h"
#include "../mediafileinfo.h"
#include "../progressfeedback.h"
#include "../tag.h"
#include <c++utilities/tests/testutils.h>
@ -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());

View File

@ -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()) {

View File

@ -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");
}

View File

@ -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();

View File

@ -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();

View File

@ -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;