187 CPP_UTILITIES_UNUSED(progress)
189 static const auto context = std::string(
"parsing Ogg bitstream header");
190 auto pagesSkipped =
false, continueFromHere =
false;
195 for (m_iterator.removeFilter(), m_iterator.reset(); m_iterator;
196 continueFromHere ? [&] { continueFromHere = false; }() : m_iterator.nextPage()) {
198 const OggPage &page = m_iterator.currentPage();
202 "The denoted checksum of the Ogg page at ", m_iterator.currentSegmentOffset(),
" does not match the computed checksum."),
206 auto lastNewStreamOffset = std::uint64_t();
207 if (
const auto streamIndex = m_streamsBySerialNo.find(page.
streamSerialNumber()); streamIndex != m_streamsBySerialNo.end()) {
212 stream =
m_tracks.emplace_back(make_unique<OggStream>(*
this, m_iterator.currentPageIndex())).get();
219 if (
stream->m_currentSequenceNumber) {
221 argsToString(
"Page of stream ", page.
streamSerialNumber(),
" missing; page sequence number ",
stream->m_currentSequenceNumber,
227 ++
stream->m_currentSequenceNumber;
232 && (page.
startOffset() - lastNewStreamOffset) > (20 * 0x100000)) {
233 if (m_iterator.resyncAt(
fileInfo().size() - (20 * 0x100000))) {
234 const OggPage &resyncedPage = m_iterator.currentPage();
236 for (
auto &trackStream :
m_tracks) {
237 trackStream->m_currentSequenceNumber = 0;
238 trackStream->m_size = 0;
240 pagesSkipped = continueFromHere =
true;
242 argsToString(
"Pages in the middle of the file (", dataSizeToString(resyncedPage.
startOffset() - page.
startOffset()),
243 ") have been skipped to improve parsing speed. Hence track sizes can not be computed. Maybe not even all tracks could be "
244 "detected. Force a full parse to prevent this."),
249 "Unable to re-sync after skipping Ogg pages in the middle of the file. Try forcing a full parse.", context);
259 const auto expectedOffset = m_iterator.currentSegmentOffset();
260 diag.emplace_back(
DiagLevel::Critical, argsToString(
"Capture pattern \"OggS\" at ", expectedOffset,
" expected."), context);
261 if (m_iterator.resyncAt(expectedOffset)) {
263 argsToString(
"Found next capture pattern \"OggS\" at ", m_iterator.currentPageOffset(),
". Skipped ",
264 m_iterator.currentPageOffset() - expectedOffset,
" invalid bytes."),
266 continueFromHere =
true;
270 "Aborting after not being able to find any \"OggS\" capture patterns within 65307 bytes (from offset ", expectedOffset,
")."),
423 const auto context = std::string(
"making Ogg file");
426 auto originalPath =
fileInfo().
path(), backupPath = std::string();
427 auto backupStream = NativeFileStream();
429 if (
fileInfo().saveFilePath().empty()) {
434 fileInfo().
stream().open(originalPath, ios_base::out | ios_base::binary | ios_base::trunc);
435 }
catch (
const std::ios_base::failure &failure) {
437 DiagLevel::Critical, argsToString(
"Creation of temporary file (to rewrite the original file) failed: ", failure.what()), context);
443 backupStream.exceptions(ios_base::badbit | ios_base::failbit);
448 }
catch (
const std::ios_base::failure &failure) {
449 diag.emplace_back(
DiagLevel::Critical, argsToString(
"Opening streams to write output file failed: ", failure.what()), context);
461 auto tagIterator =
m_tags.cbegin(), tagEnd =
m_tags.cend();
462 if (tagIterator != tagEnd) {
463 currentParams = &(currentComment = tagIterator->get())->oggParams();
465 currentComment =
nullptr;
466 currentParams =
nullptr;
470 const OggPage *lastPage =
nullptr;
471 static constexpr auto oggPageHeaderSize = 27u;
472 auto lastPageNewOffset = std::uint64_t();
473 auto copyHelper = CopyHelper<65307>();
474 auto updatedPageOffsets = std::vector<std::uint64_t>();
475 auto nextPageOffset = std::uint64_t();
476 auto pageSequenceNumberBySerialNo = std::unordered_map<std::uint32_t, std::uint32_t>();
479 auto updateTick = 0u;
480 for (m_iterator.setStream(backupStream), m_iterator.removeFilter(), m_iterator.reset(); m_iterator; m_iterator.nextPage(), ++updateTick) {
481 const OggPage ¤tPage = m_iterator.currentPage();
482 if (updateTick % 10) {
490 if (lastPage && currentPage.
startOffset() != nextPageOffset) {
491 m_iterator.pages().resize(m_iterator.currentPageIndex() - 1);
492 if (m_iterator.resyncAt(nextPageOffset)) {
494 const auto actuallyNextPageOffset = m_iterator.currentPageOffset();
495 if (actuallyNextPageOffset != nextPageOffset) {
497 argsToString(
"Expected Ogg page at offset ", nextPageOffset,
" but found the next Ogg page only at offset ",
498 actuallyNextPageOffset,
". Skipped ", (actuallyNextPageOffset - nextPageOffset),
" invalid bytes."),
500 nextPageOffset = actuallyNextPageOffset;
502 m_iterator.previousPage();
507 "Expected Ogg page at offset ", nextPageOffset,
" but could not find any further pages. Skipped the rest of the file."),
512 const auto pageSize = currentPage.
totalSize();
513 auto &pageSequenceNumber = pageSequenceNumberBySerialNo[currentPage.
streamSerialNumber()];
514 lastPage = ¤tPage;
515 lastPageNewOffset =
static_cast<std::uint64_t
>(
stream().tellp());
516 nextPageOffset = currentPage.
startOffset() + pageSize;
519 if (currentComment && m_iterator.currentPageIndex() >= currentParams->
firstPageIndex
523 auto buffer = std::stringstream(std::ios_base::in | std::ios_base::out | std::ios_base::binary);
524 auto newSegmentSizes = std::vector<std::uint32_t>();
525 newSegmentSizes.reserve(currentPage.
segmentSizes().size());
526 auto segmentOffset = m_iterator.currentSegmentOffset();
527 auto segmentIndex = std::vector<std::uint32_t>::size_type();
528 for (
const auto segmentSize : currentPage.
segmentSizes()) {
536 && (m_iterator.currentPageIndex() <= currentParams->
lastPageIndex && segmentIndex <= currentParams->lastSegmentIndex)) {
539 && ((m_iterator.currentPageIndex() == currentParams->
firstPageIndex
541 makeVorbisCommentSegment(buffer, copyHelper, newSegmentSizes, currentComment, currentParams, diag);
545 if (m_iterator.currentPageIndex() > currentParams->
lastPageIndex
547 if (++tagIterator != tagEnd) {
548 currentParams = &(currentComment = tagIterator->get())->oggParams();
550 currentComment =
nullptr;
551 currentParams =
nullptr;
556 backupStream.seekg(
static_cast<std::streamoff
>(segmentOffset));
557 copyHelper.copy(backupStream, buffer, segmentSize);
558 newSegmentSizes.push_back(segmentSize);
561 if (currentParams && m_iterator.currentPageIndex() == currentParams->
lastPageIndex
564 makeVorbisCommentSegment(buffer, copyHelper, newSegmentSizes, currentComment, currentParams, diag);
567 if (++tagIterator != tagEnd) {
568 currentParams = &(currentComment = tagIterator->get())->oggParams();
570 currentComment =
nullptr;
571 currentParams =
nullptr;
575 segmentOffset += segmentSize;
580 if (
auto newSegmentSizesIterator = newSegmentSizes.cbegin(), newSegmentSizesEnd = newSegmentSizes.cend();
581 newSegmentSizesIterator != newSegmentSizesEnd) {
582 auto bytesLeft = *newSegmentSizesIterator;
583 auto continuePreviousSegment =
false, needsZeroLacingValue =
false;
585 while (newSegmentSizesIterator != newSegmentSizesEnd) {
587 updatedPageOffsets.push_back(
static_cast<std::uint64_t
>(
stream().tellp()));
589 backupStream.seekg(
static_cast<streamoff
>(currentPage.
startOffset()));
590 copyHelper.copy(backupStream,
stream(), oggPageHeaderSize);
592 auto flags = (currentPage.
headerTypeFlag() & 0xFE) | (continuePreviousSegment ? 0x01 : 0x00);
593 continuePreviousSegment =
true;
596 flags = flags & (newSegmentSizesIterator != newSegmentSizes.cbegin() ? 0xFD : 0xF);
598 stream().seekp(-22, ios_base::cur);
599 stream().put(
static_cast<char>(flags));
601 stream().seekp(12, ios_base::cur);
603 writer().writeUInt32LE(pageSequenceNumber);
605 stream().seekp(5, ios_base::cur);
608 auto segmentSizesWritten = std::int16_t();
609 auto currentSize = std::uint32_t();
610 while ((bytesLeft || needsZeroLacingValue) && segmentSizesWritten < 0xFF) {
611 while (bytesLeft > 0xFF && segmentSizesWritten < 0xFF) {
612 stream().put(
static_cast<char>(0xFF));
615 ++segmentSizesWritten;
617 if ((bytesLeft || needsZeroLacingValue) && segmentSizesWritten < 0xFF) {
619 stream().put(
static_cast<char>(bytesLeft));
620 currentSize += bytesLeft;
621 needsZeroLacingValue = bytesLeft == 0xFF;
623 ++segmentSizesWritten;
625 if (!bytesLeft && !needsZeroLacingValue) {
628 if (++newSegmentSizesIterator != newSegmentSizesEnd) {
629 bytesLeft = *newSegmentSizesIterator;
630 continuePreviousSegment =
false;
636 if (!bytesLeft && !needsZeroLacingValue) {
637 continuePreviousSegment =
false;
641 if (newSegmentSizesIterator != newSegmentSizesEnd) {
643 stream().seekp(-21 - segmentSizesWritten, ios_base::cur);
644 writer().writeInt64LE(-1);
645 stream().seekp(12, ios_base::cur);
648 backupStream.seekg(
static_cast<streamoff
>(m_iterator.pages()[currentParams->
lastPageIndex].startOffset() + 6));
649 stream().seekp(-21 - segmentSizesWritten, ios_base::cur);
650 copyHelper.copy(backupStream,
stream(), 8);
651 stream().seekp(12, ios_base::cur);
654 stream().seekp(-1 - segmentSizesWritten, ios_base::cur);
660 stream().put(
static_cast<char>(segmentSizesWritten));
661 stream().seekp(segmentSizesWritten, ios_base::cur);
663 copyHelper.copy(buffer,
stream(), currentSize);
665 ++pageSequenceNumber;
670 if (pageSequenceNumber != m_iterator.currentPageIndex()) {
672 backupStream.seekg(
static_cast<streamoff
>(currentPage.
startOffset()));
673 updatedPageOffsets.push_back(
static_cast<std::uint64_t
>(
stream().tellp()));
674 copyHelper.copy(backupStream,
stream(), oggPageHeaderSize);
675 stream().seekp(-9, ios_base::cur);
676 writer().writeUInt32LE(pageSequenceNumber);
677 stream().seekp(5, ios_base::cur);
678 copyHelper.copy(backupStream,
stream(), pageSize - oggPageHeaderSize);
681 backupStream.seekg(
static_cast<streamoff
>(currentPage.
startOffset()));
682 copyHelper.copy(backupStream,
stream(), pageSize);
684 ++pageSequenceNumber;
693 if (!
fileInfo().saveFilePath().empty()) {
700 backupStream.close();
705 if (lastPage && lastPageNewOffset) {
706 const auto offset =
static_cast<std::streamoff
>(lastPageNewOffset + 5ul);
708 if (
const auto flag =
stream.get(); !(flag & 0x04)) {
709 updatedPageOffsets.emplace_back(lastPageNewOffset);
711 stream.put(
static_cast<char>(flag | 0x04));
718 for (
auto offset : updatedPageOffsets) {
719 if (updateTick++ % 10) {