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/catchiofailure.h> 11 #include <c++utilities/io/copy.h> 26 const char *OggVorbisComment::typeName()
const 28 switch (m_oggParams.streamFormat) {
30 return "Vorbis comment (in FLAC stream)";
31 case GeneralMediaFormat::Opus:
32 return "Vorbis comment (in Opus stream)";
33 case GeneralMediaFormat::Theora:
34 return "Vorbis comment (in Theora stream)";
36 return "Vorbis comment";
50 , m_iterator(fileInfo.stream(), startOffset, fileInfo.size())
51 , m_validateChecksums(false)
76 if (!target.
tracks().empty()) {
93 }
else if (!
m_tags.empty()) {
95 m_tags.front()->oggParams().removed =
false;
96 return m_tags.front().get();
110 m_tags.back()->setTarget(target);
111 return m_tags.back().get();
160 for (
auto &existingTag :
m_tags) {
161 if (static_cast<Tag *>(existingTag.get()) ==
tag) {
162 existingTag->removeAllFields();
163 existingTag->oggParams().removed =
true;
182 for (
auto &existingTag :
m_tags) {
183 existingTag->removeAllFields();
184 existingTag->oggParams().removed =
true;
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 uint64 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");
314 stream->parseHeader(diag);
328 void OggContainer::makeVorbisCommentSegment(stringstream &buffer,
CopyHelper<65307> ©Helper, vector<uint32> &newSegmentSizes,
331 const auto offset = buffer.tellp();
337 ConversionUtilities::BE::getBytes(static_cast<uint64>(0x4F70757354616773u), copyHelper.buffer());
338 buffer.write(copyHelper.buffer(), 8);
348 buffer.write(copyHelper.buffer(), 4);
353 header.
setDataSize(static_cast<uint32>(buffer.tellp() - offset - 4));
356 DiagLevel::Critical,
"Size of Vorbis comment exceeds size limit for FLAC \"METADATA_BLOCK_HEADER\".",
"making Vorbis Comment");
358 buffer.seekp(offset);
360 buffer.seekp(header.
dataSize(), ios_base::cur);
365 newSegmentSizes.push_back(static_cast<uint32>(buffer.tellp() - offset));
370 const string context(
"making OGG file");
371 progress.
updateStep(
"Prepare for rewriting OGG file ...");
374 NativeFileStream backupStream;
376 if (
fileInfo().saveFilePath().empty()) {
383 const char *what = catchIoFailure();
384 diag.emplace_back(
DiagLevel::Critical,
"Creation of temporary file (to rewrite the original file) failed.", context);
385 throwIoFailure(what);
390 backupStream.exceptions(ios_base::badbit | ios_base::failbit);
391 backupStream.open(
fileInfo().path(), ios_base::in | ios_base::binary);
395 const char *what = catchIoFailure();
396 diag.emplace_back(
DiagLevel::Critical,
"Opening streams to write output file failed.", context);
397 throwIoFailure(what);
405 auto tagIterator =
m_tags.cbegin(), tagEnd =
m_tags.cend();
406 if (tagIterator != tagEnd) {
407 currentParams = &(currentComment = tagIterator->get())->oggParams();
409 currentComment =
nullptr;
410 currentParams =
nullptr;
415 vector<uint64> updatedPageOffsets;
416 unordered_map<uint32, uint32> pageSequenceNumberBySerialNo;
421 const auto pageSize = currentPage.
totalSize();
422 uint32 &pageSequenceNumber = pageSequenceNumberBySerialNo[currentPage.
streamSerialNumber()];
428 stringstream buffer(ios_base::in | ios_base::out | ios_base::binary);
429 vector<uint32> newSegmentSizes;
430 newSegmentSizes.reserve(currentPage.
segmentSizes().size());
432 vector<uint32>::size_type segmentIndex = 0;
433 for (
const auto segmentSize : currentPage.
segmentSizes()) {
445 makeVorbisCommentSegment(buffer, copyHelper, newSegmentSizes, currentComment, currentParams, diag);
451 if (++tagIterator != tagEnd) {
452 currentParams = &(currentComment = tagIterator->get())->oggParams();
454 currentComment =
nullptr;
455 currentParams =
nullptr;
460 backupStream.seekg(static_cast<streamoff>(segmentOffset));
461 copyHelper.copy(backupStream, buffer, segmentSize);
462 newSegmentSizes.push_back(segmentSize);
468 makeVorbisCommentSegment(buffer, copyHelper, newSegmentSizes, currentComment, currentParams, diag);
471 if (++tagIterator != tagEnd) {
472 currentParams = &(currentComment = tagIterator->get())->oggParams();
474 currentComment =
nullptr;
475 currentParams =
nullptr;
479 segmentOffset += segmentSize;
484 auto newSegmentSizesIterator = newSegmentSizes.cbegin(), newSegmentSizesEnd = newSegmentSizes.cend();
485 bool continuePreviousSegment =
false;
486 if (newSegmentSizesIterator != newSegmentSizesEnd) {
487 uint32 bytesLeft = *newSegmentSizesIterator;
489 while (newSegmentSizesIterator != newSegmentSizesEnd) {
491 backupStream.seekg(static_cast<streamoff>(currentPage.
startOffset()));
492 updatedPageOffsets.push_back(static_cast<uint64>(
stream().tellp()));
493 copyHelper.copy(backupStream,
stream(), 27);
495 stream().seekp(-22, ios_base::cur);
496 stream().put(static_cast<char>(currentPage.
headerTypeFlag() & (continuePreviousSegment ? 0xFF : 0xFE)));
497 continuePreviousSegment =
true;
499 stream().seekp(12, ios_base::cur);
500 writer().writeUInt32LE(pageSequenceNumber);
501 stream().seekp(5, ios_base::cur);
502 int16 segmentSizesWritten = 0;
505 uint32 currentSize = 0;
506 while (bytesLeft && segmentSizesWritten < 0xFF) {
507 while (bytesLeft >= 0xFF && segmentSizesWritten < 0xFF) {
508 stream().put(static_cast<char>(0xFF));
511 ++segmentSizesWritten;
513 if (bytesLeft && segmentSizesWritten < 0xFF) {
515 stream().put(static_cast<char>(bytesLeft));
516 currentSize += bytesLeft;
518 ++segmentSizesWritten;
523 if (++newSegmentSizesIterator != newSegmentSizesEnd) {
524 bytesLeft = *newSegmentSizesIterator;
525 continuePreviousSegment =
false;
532 continuePreviousSegment =
false;
538 stream().seekp(-1 - segmentSizesWritten, ios_base::cur);
539 stream().put(static_cast<char>(segmentSizesWritten));
540 stream().seekp(segmentSizesWritten, ios_base::cur);
542 copyHelper.copy(buffer,
stream(), currentSize);
544 ++pageSequenceNumber;
551 backupStream.seekg(static_cast<streamoff>(currentPage.
startOffset()));
552 updatedPageOffsets.push_back(static_cast<uint64>(
stream().tellp()));
553 copyHelper.copy(backupStream,
stream(), 27);
554 stream().seekp(-9, ios_base::cur);
555 writer().writeUInt32LE(pageSequenceNumber);
556 stream().seekp(5, ios_base::cur);
557 copyHelper.copy(backupStream,
stream(), pageSize - 27);
560 backupStream.seekg(static_cast<streamoff>(currentPage.
startOffset()));
561 copyHelper.copy(backupStream,
stream(), pageSize);
563 ++pageSequenceNumber;
571 if (!
fileInfo().saveFilePath().empty()) {
577 backupStream.close();
582 for (
auto offset : updatedPageOffsets) {
void nextPage()
Increases the current position by one page.
byte headerTypeFlag() const
Returns the header type flag.
The exception that is thrown when the data to be parsed is truncated and therefore can not be parsed ...
The Tag class is used to store, read and write tag information.
void reset()
Resets the iterator to point at the first segment of the first page (matching the filter if set)...
The OggParameter struct holds the OGG parameter for a VorbisComment.
uint32 dataSize() const
Returns the data size in byte.
uint32 totalSize() const
Returns the total size of the page in byte.
GeneralMediaFormat
The GeneralMediaFormat enum specifies the general format of media data (PCM, MPEG-4, PNG, ...).
const OggPage & currentPage() const
Returns the current OGG page.
void removeFilter()
Removes a previously set filter.
std::iostream & stream()
Returns the related stream.
OggVorbisComment * createTag(const TagTarget &target) override
Creates a new tag.
const std::vector< uint32 > & segmentSizes() const
Returns the sizes of the segments of the page in byte.
OggStream * track(std::size_t index) override
uint64 currentSegmentOffset() const
Returns the start offset of the current segment in the input stream if the iterator is valid; otherwi...
uint32 sequenceNumber() const
Returns the page sequence number.
void ignore(std::size_t count=1)
Advances the position of the next character to be read from the OGG stream by count bytes...
TAG_PARSER_EXPORT const char * comment()
void parseTracks(Diagnostics &diag)
Parses the tracks of the file if not parsed yet.
uint64 startOffset() const
Returns the start offset of the page.
std::size_t startPage() const
std::vector< uint32 >::size_type currentSegmentIndex() const
Returns the index of the current segment (in the current page) if the iterator is valid; otherwise an...
Implementation of TagParser::AbstractTrack for OGG streams.
const TagTarget & target() const
Returns the target of tag.
std::size_t lastSegmentIndex
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...
ChronoUtilities::TimeSpan m_duration
void removeAllTags() override
Actually just flags all tags as removed and clears all assigned fields.
uint32 checksum() const
Returns the page checksum.
const std::vector< OggPage > & pages() const
Returns a vector of containing the OGG pages that have been fetched yet.
bool resyncAt(uint64 offset)
Fetches the next page at the specified offset.
Contains utility classes helping to read and write streams.
void setStream(std::istream &stream)
Sets the stream.
uint32 streamSerialNumber() const
Returns the stream serial number.
std::size_t firstSegmentIndex
void setPageIndex(std::vector< OggPage >::size_type index)
Sets the current page index.
OggVorbisComment * tag(std::size_t index) override
Returns the tag with the specified index.
IoUtilities::BinaryWriter & writer()
Returns the related BinaryWriter.
bool removeTag(Tag *tag) override
Actually just flags the specified tag as removed and clears all assigned fields.
The TagTarget class specifies the target of a tag.
MediaFormat format() const
Returns the format of the track if known; otherwise returns MediaFormat::Unknown. ...
std::vector< std::unique_ptr< OggStream > > m_tracks
void reportPathChanged(const std::string &newPath)
Call this function to report that the path changed.
The AbortableProgressFeedback class provides feedback about an ongoing operation via callbacks...
void close()
A possibly opened std::fstream will be closed.
void internalParseHeader(Diagnostics &diag) override
Internally called to parse the header.
uint64 id() const
Returns the track ID if known; otherwise returns 0.
void updateStep(const std::string &step, byte stepPercentage=0)
Updates the current step and invokes the first callback specified on construction.
void internalParseTracks(Diagnostics &diag) override
Internally called to parse the tracks.
TAG_PARSER_EXPORT void handleFailureAfterFileModified(MediaFileInfo &mediaFileInfo, const std::string &backupPath, IoUtilities::NativeFileStream &outputStream, IoUtilities::NativeFileStream &backupStream, Diagnostics &diag, const std::string &context="making file")
void reportSizeChanged(uint64 newSize)
Call this function to report that the size changed.
const IdContainerType & tracks() const
Returns the tracks.
static void updateChecksum(std::iostream &stream, uint64 startOffset)
Updates the checksum of the page read from the specified stream at the specified startOffset.
The exception that is thrown when the data to be parsed or to be made seems invalid and therefore can...
std::size_t lastPageIndex
std::size_t tagCount() const override
Returns the number of tags attached to the container.
GeneralMediaFormat streamFormat
uint64 startOffset() const
Returns the start offset in the related stream.
IoUtilities::NativeFileStream & stream()
Returns the std::fstream for the current instance.
The OggPage class is used to parse OGG pages.
void setSegmentIndex(std::vector< uint32 >::size_type index)
Sets the current segment index.
void internalParseTags(Diagnostics &diag) override
Internally called to parse the tags.
MediaFileInfo & fileInfo() const
Returns the related file info.
The class inherits from std::exception and serves as base class for exceptions thrown by the elements...
TAG_PARSER_EXPORT void createBackupFile(const std::string &originalPath, std::string &backupPath, IoUtilities::NativeFileStream &originalStream, IoUtilities::NativeFileStream &backupStream)
static uint32 computeChecksum(std::istream &stream, uint64 startOffset)
Computes the actual checksum of the page read from the specified stream at the specified startOffset...
Contains all classes and functions of the TagInfo library.
std::size_t firstPageIndex
std::vector< std::unique_ptr< OggVorbisComment > > m_tags
void parseTags(Diagnostics &diag)
Parses the tag information if not parsed yet.
The Diagnostics class is a container for DiagMessage.
void reset() override
Discards all parsing results.
void clear(std::istream &stream, uint64 startOffset, uint64 streamSize)
Sets the stream and related parameters and clears all available pages.
void internalMakeFile(Diagnostics &diag, AbortableProgressFeedback &progress) override
Internally called to make the file.
The GenericContainer class helps parsing header, track, tag and chapter information of a file...