3 #include "../flac/flacmetadata.h"
5 #include "../backuphelper.h"
6 #include "../mediafileinfo.h"
7 #include "../progressfeedback.h"
9 #include <c++utilities/conversion/stringbuilder.h>
10 #include <c++utilities/io/copy.h>
24 std::string_view OggVorbisComment::typeName()
const
26 switch (m_oggParams.streamFormat) {
28 return "Vorbis comment (in FLAC stream)";
30 return "Vorbis comment (in Opus stream)";
31 case GeneralMediaFormat::Theora:
32 return "Vorbis comment (in Theora stream)";
34 return "Vorbis comment";
46 OggContainer::OggContainer(
MediaFileInfo &fileInfo, std::uint64_t startOffset)
48 , m_iterator(fileInfo.stream(), startOffset, fileInfo.size())
49 , m_validateChecksums(false)
74 if (!target.
tracks().empty()) {
91 }
else if (!
m_tags.empty()) {
93 m_tags.front()->oggParams().removed =
false;
94 return m_tags.front().get();
108 m_tags.back()->setTarget(target);
109 return m_tags.back().get();
158 for (
auto &existingTag :
m_tags) {
159 if (
static_cast<Tag *
>(existingTag.get()) ==
tag) {
160 existingTag->removeAllFields();
161 existingTag->oggParams().removed =
true;
180 for (
auto &existingTag :
m_tags) {
181 existingTag->removeAllFields();
182 existingTag->oggParams().removed =
true;
188 CPP_UTILITIES_UNUSED(progress)
190 static const string context(
"parsing OGG bitstream header");
191 bool pagesSkipped =
false;
201 "The denoted checksum of the OGG page at ", m_iterator.
currentSegmentOffset(),
" does not match the computed checksum."),
205 std::uint64_t lastNewStreamOffset = 0;
209 }
catch (
const out_of_range &) {
217 if (
stream->m_currentSequenceNumber) {
218 diag.emplace_back(
DiagLevel::Warning,
"Page is missing (page sequence number omitted).", context);
222 ++
stream->m_currentSequenceNumber;
227 && (page.
startOffset() - lastNewStreamOffset) > (20 * 0x100000)) {
234 argsToString(
"Pages in the middle of the file (", dataSizeToString(resyncedPage.
startOffset() - page.
startOffset()),
235 ") have been skipped to improve parsing speed. Hence track sizes can not be computed. Maybe not even all tracks could be "
236 "detected. Force a full parse to prevent this."),
241 "Unable to re-sync after skipping OGG pages in the middle of the file. Try forcing a full parse.", context);
285 diag.emplace_back(
DiagLevel::Critical,
"Stream format not supported.",
"parsing tags from OGG streams");
303 void OggContainer::announceComment(std::size_t pageIndex, std::size_t segmentIndex,
bool lastMetaDataBlock,
GeneralMediaFormat mediaFormat)
305 m_tags.emplace_back(make_unique<OggVorbisComment>());
306 m_tags.back()->oggParams().set(pageIndex, segmentIndex, lastMetaDataBlock, mediaFormat);
311 static const string context(
"parsing OGG stream");
317 stream->parseHeader(diag, progress);
331 void OggContainer::makeVorbisCommentSegment(stringstream &buffer,
CopyHelper<65307> ©Helper, vector<std::uint32_t> &newSegmentSizes,
334 const auto offset = buffer.tellp();
340 BE::getBytes(
static_cast<std::uint64_t
>(0x4F70757354616773u), copyHelper.buffer());
341 buffer.write(copyHelper.buffer(), 8);
351 buffer.write(copyHelper.buffer(), 4);
356 header.
setDataSize(
static_cast<std::uint32_t
>(buffer.tellp() - offset - 4));
359 DiagLevel::Critical,
"Size of Vorbis comment exceeds size limit for FLAC \"METADATA_BLOCK_HEADER\".",
"making Vorbis Comment");
361 buffer.seekp(offset);
363 buffer.seekp(header.
dataSize(), ios_base::cur);
368 newSegmentSizes.push_back(
static_cast<std::uint32_t
>(buffer.tellp() - offset));
373 const string context(
"making OGG file");
374 progress.
updateStep(
"Prepare for rewriting OGG file ...");
377 NativeFileStream backupStream;
379 if (
fileInfo().saveFilePath().empty()) {
385 }
catch (
const std::ios_base::failure &failure) {
387 DiagLevel::Critical, argsToString(
"Creation of temporary file (to rewrite the original file) failed: ", failure.what()), context);
393 backupStream.exceptions(ios_base::badbit | ios_base::failbit);
398 }
catch (
const std::ios_base::failure &failure) {
399 diag.emplace_back(
DiagLevel::Critical, argsToString(
"Opening streams to write output file failed: ", failure.what()), context);
408 auto tagIterator =
m_tags.cbegin(), tagEnd =
m_tags.cend();
409 if (tagIterator != tagEnd) {
410 currentParams = &(currentComment = tagIterator->get())->oggParams();
412 currentComment =
nullptr;
413 currentParams =
nullptr;
418 vector<std::uint64_t> updatedPageOffsets;
419 unordered_map<std::uint32_t, std::uint32_t> pageSequenceNumberBySerialNo;
424 const auto pageSize = currentPage.
totalSize();
425 std::uint32_t &pageSequenceNumber = pageSequenceNumberBySerialNo[currentPage.
streamSerialNumber()];
431 stringstream buffer(ios_base::in | ios_base::out | ios_base::binary);
432 vector<std::uint32_t> newSegmentSizes;
433 newSegmentSizes.reserve(currentPage.
segmentSizes().size());
435 vector<std::uint32_t>::size_type segmentIndex = 0;
436 for (
const auto segmentSize : currentPage.
segmentSizes()) {
448 makeVorbisCommentSegment(buffer, copyHelper, newSegmentSizes, currentComment, currentParams, diag);
454 if (++tagIterator != tagEnd) {
455 currentParams = &(currentComment = tagIterator->get())->oggParams();
457 currentComment =
nullptr;
458 currentParams =
nullptr;
463 backupStream.seekg(
static_cast<streamoff
>(segmentOffset));
464 copyHelper.copy(backupStream, buffer, segmentSize);
465 newSegmentSizes.push_back(segmentSize);
471 makeVorbisCommentSegment(buffer, copyHelper, newSegmentSizes, currentComment, currentParams, diag);
474 if (++tagIterator != tagEnd) {
475 currentParams = &(currentComment = tagIterator->get())->oggParams();
477 currentComment =
nullptr;
478 currentParams =
nullptr;
482 segmentOffset += segmentSize;
487 auto newSegmentSizesIterator = newSegmentSizes.cbegin(), newSegmentSizesEnd = newSegmentSizes.cend();
488 bool continuePreviousSegment =
false;
489 if (newSegmentSizesIterator != newSegmentSizesEnd) {
490 std::uint32_t bytesLeft = *newSegmentSizesIterator;
492 while (newSegmentSizesIterator != newSegmentSizesEnd) {
494 backupStream.seekg(
static_cast<streamoff
>(currentPage.
startOffset()));
495 updatedPageOffsets.push_back(
static_cast<std::uint64_t
>(
stream().tellp()));
496 copyHelper.copy(backupStream,
stream(), 27);
498 stream().seekp(-22, ios_base::cur);
499 stream().put(
static_cast<char>(currentPage.
headerTypeFlag() & (continuePreviousSegment ? 0xFF : 0xFE)));
500 continuePreviousSegment =
true;
502 stream().seekp(12, ios_base::cur);
503 writer().writeUInt32LE(pageSequenceNumber);
504 stream().seekp(5, ios_base::cur);
505 std::int16_t segmentSizesWritten = 0;
508 std::uint32_t currentSize = 0;
509 while (bytesLeft && segmentSizesWritten < 0xFF) {
510 while (bytesLeft >= 0xFF && segmentSizesWritten < 0xFF) {
511 stream().put(
static_cast<char>(0xFF));
514 ++segmentSizesWritten;
516 if (bytesLeft && segmentSizesWritten < 0xFF) {
518 stream().put(
static_cast<char>(bytesLeft));
519 currentSize += bytesLeft;
521 ++segmentSizesWritten;
526 if (++newSegmentSizesIterator != newSegmentSizesEnd) {
527 bytesLeft = *newSegmentSizesIterator;
528 continuePreviousSegment =
false;
535 continuePreviousSegment =
false;
541 stream().seekp(-1 - segmentSizesWritten, ios_base::cur);
542 stream().put(
static_cast<char>(segmentSizesWritten));
543 stream().seekp(segmentSizesWritten, ios_base::cur);
545 copyHelper.copy(buffer,
stream(), currentSize);
547 ++pageSequenceNumber;
554 backupStream.seekg(
static_cast<streamoff
>(currentPage.
startOffset()));
555 updatedPageOffsets.push_back(
static_cast<std::uint64_t
>(
stream().tellp()));
556 copyHelper.copy(backupStream,
stream(), 27);
557 stream().seekp(-9, ios_base::cur);
558 writer().writeUInt32LE(pageSequenceNumber);
559 stream().seekp(5, ios_base::cur);
560 copyHelper.copy(backupStream,
stream(), pageSize - 27);
563 backupStream.seekg(
static_cast<streamoff
>(currentPage.
startOffset()));
564 copyHelper.copy(backupStream,
stream(), pageSize);
566 ++pageSequenceNumber;
574 if (!
fileInfo().saveFilePath().empty()) {
580 backupStream.close();
585 for (
auto offset : updatedPageOffsets) {
The AbortableProgressFeedback class provides feedback about an ongoing operation via callbacks.
bool isAborted() const
Returns whether the operation has been aborted via tryToAbort().
std::iostream & stream()
Returns the related stream.
std::uint64_t startOffset() const
Returns the start offset in the related stream.
void parseTracks(Diagnostics &diag, AbortableProgressFeedback &progress)
Parses the tracks of the file if not parsed yet.
CppUtilities::BinaryWriter & writer()
Returns the related BinaryWriter.
void parseTags(Diagnostics &diag, AbortableProgressFeedback &progress)
Parses the tag information if not parsed yet.
CppUtilities::TimeSpan m_duration
std::uint64_t id() const
Returns the track ID if known; otherwise returns 0.
MediaFormat format() const
Returns the format of the track if known; otherwise returns MediaFormat::Unknown.
void reportPathChanged(std::string_view newPath)
Call this function to report that the path changed.
CppUtilities::NativeFileStream & stream()
Returns the std::fstream for the current instance.
void close()
A possibly opened std::fstream will be closed.
static std::string_view pathForOpen(std::string_view url)
Returns removes the "file:/" prefix from url to be able to pass it to functions like open(),...
void reportSizeChanged(std::uint64_t newSize)
Call this function to report that the size changed.
void updateStep(const std::string &step, std::uint8_t stepPercentage=0)
Updates the current step and invokes the first callback specified on construction.
The Diagnostics class is a container for DiagMessage.
The class inherits from std::exception and serves as base class for exceptions thrown by the elements...
The GenericContainer class helps parsing header, track, tag and chapter information of a file.
OggStream * track(std::size_t index) override
std::vector< std::unique_ptr< OggStream > > m_tracks
MediaFileInfo & fileInfo() const
Returns the related file info.
std::vector< std::unique_ptr< OggVorbisComment > > m_tags
The exception that is thrown when the data to be parsed or to be made seems invalid and therefore can...
void internalParseTags(Diagnostics &diag, AbortableProgressFeedback &progress) override
Internally called to parse the tags.
OggVorbisComment * tag(std::size_t index) override
Returns the tag with the specified index.
std::size_t tagCount() const override
Returns the number of tags attached to the container.
void internalParseTracks(Diagnostics &diag, AbortableProgressFeedback &progress) override
Internally called to parse the tracks.
void internalParseHeader(Diagnostics &diag, AbortableProgressFeedback &progress) override
Internally called to parse the header.
void removeAllTags() override
Actually just flags all tags as removed and clears all assigned fields.
OggVorbisComment * createTag(const TagTarget &target) override
Creates a new tag.
void reset() override
Discards all parsing results.
void internalMakeFile(Diagnostics &diag, AbortableProgressFeedback &progress) override
Internally called to make the file.
bool removeTag(Tag *tag) override
Actually just flags the specified tag as removed and clears all assigned fields.
void nextPage()
Increases the current position by one page.
void setSegmentIndex(std::vector< std::uint32_t >::size_type index)
Sets the current segment index.
void removeFilter()
Removes a previously set filter.
void setStream(std::istream &stream)
Sets the stream.
std::vector< std::uint32_t >::size_type currentSegmentIndex() const
Returns the index of the current segment (in the current page) if the iterator is valid; otherwise an...
void clear(std::istream &stream, std::uint64_t startOffset, std::uint64_t streamSize)
Sets the stream and related parameters and clears all available pages.
std::uint64_t currentSegmentOffset() const
Returns the start offset of the current segment in the input stream if the iterator is valid; otherwi...
const OggPage & currentPage() const
Returns the current OGG page.
const std::vector< OggPage > & pages() const
Returns a vector of containing the OGG pages that have been fetched yet.
bool resyncAt(std::uint64_t offset)
Fetches the next page at the specified offset.
void setPageIndex(std::vector< OggPage >::size_type index)
Sets the current page index.
std::vector< OggPage >::size_type currentPageIndex() const
Returns the index of the current page if the iterator is valid; otherwise an undefined index is retur...
void reset()
Resets the iterator to point at the first segment of the first page (matching the filter if set).
void ignore(std::size_t count=1)
Advances the position of the next character to be read from the OGG stream by count bytes.
The OggPage class is used to parse OGG pages.
std::uint32_t sequenceNumber() const
Returns the page sequence number.
std::uint32_t dataSize() const
Returns the data size in byte.
std::uint32_t totalSize() const
Returns the total size of the page in byte.
std::uint32_t streamSerialNumber() const
Returns the stream serial number.
const std::vector< std::uint32_t > & segmentSizes() const
Returns the sizes of the segments of the page in byte.
std::uint8_t headerTypeFlag() const
Returns the header type flag.
std::uint64_t startOffset() const
Returns the start offset of the page.
std::uint32_t checksum() const
Returns the page checksum.
static std::uint32_t computeChecksum(std::istream &stream, std::uint64_t startOffset)
Computes the actual checksum of the page read from the specified stream at the specified startOffset.
static void updateChecksum(std::iostream &stream, std::uint64_t startOffset)
Updates the checksum of the page read from the specified stream at the specified startOffset.
Implementation of TagParser::AbstractTrack for OGG streams.
std::size_t startPage() const
The exception that is thrown when an operation has been stopped and thus not successfully completed b...
The TagTarget class specifies the target of a tag.
const IdContainerType & tracks() const
Returns the tracks.
The Tag class is used to store, read and write tag information.
const TagTarget & target() const
Returns the target of tag.
The exception that is thrown when the data to be parsed is truncated and therefore can not be parsed ...
TAG_PARSER_EXPORT void createBackupFile(const std::string &backupDir, const std::string &originalPath, std::string &backupPath, CppUtilities::NativeFileStream &originalStream, CppUtilities::NativeFileStream &backupStream)
TAG_PARSER_EXPORT void handleFailureAfterFileModified(MediaFileInfo &mediaFileInfo, const std::string &backupPath, CppUtilities::NativeFileStream &outputStream, CppUtilities::NativeFileStream &backupStream, Diagnostics &diag, const std::string &context="making file")
constexpr TAG_PARSER_EXPORT std::string_view comment()
Contains all classes and functions of the TagInfo library.
GeneralMediaFormat
The GeneralMediaFormat enum specifies the general format of media data (PCM, MPEG-4,...
The OggParameter struct holds the OGG parameter for a VorbisComment.
GeneralMediaFormat streamFormat
std::size_t lastPageIndex
std::size_t lastSegmentIndex
std::size_t firstSegmentIndex
std::size_t firstPageIndex