190 CPP_UTILITIES_UNUSED(progress)
192 static const auto context = std::string(
"parsing Ogg bitstream header");
193 auto pagesSkipped =
false, continueFromHere =
false;
198 for (m_iterator.removeFilter(), m_iterator.reset(); m_iterator;
199 continueFromHere ? [&] { continueFromHere = false; }() : m_iterator.nextPage()) {
201 const OggPage &page = m_iterator.currentPage();
205 "The denoted checksum of the Ogg page at ", m_iterator.currentSegmentOffset(),
" does not match the computed checksum."),
209 auto lastNewStreamOffset = std::uint64_t();
210 if (
const auto streamIndex = m_streamsBySerialNo.find(page.
streamSerialNumber()); streamIndex != m_streamsBySerialNo.end()) {
215 stream =
m_tracks.emplace_back(make_unique<OggStream>(*
this, m_iterator.currentPageIndex())).get();
222 if (
stream->m_currentSequenceNumber) {
224 argsToString(
"Page of stream ", page.
streamSerialNumber(),
" missing; page sequence number ",
stream->m_currentSequenceNumber,
230 ++
stream->m_currentSequenceNumber;
235 && (page.
startOffset() - lastNewStreamOffset) > (20 * 0x100000)) {
236 if (m_iterator.resyncAt(
fileInfo().size() - (20 * 0x100000))) {
237 const OggPage &resyncedPage = m_iterator.currentPage();
239 for (
auto &trackStream :
m_tracks) {
240 trackStream->m_currentSequenceNumber = 0;
241 trackStream->m_size = 0;
243 pagesSkipped = continueFromHere =
true;
245 argsToString(
"Pages in the middle of the file (", dataSizeToString(resyncedPage.
startOffset() - page.
startOffset()),
246 ") have been skipped to improve parsing speed. Hence track sizes can not be computed. Maybe not even all tracks could be "
247 "detected. Force a full parse to prevent this."),
252 "Unable to re-sync after skipping Ogg pages in the middle of the file. Try forcing a full parse.", context);
262 const auto expectedOffset = m_iterator.currentSegmentOffset();
263 diag.emplace_back(
DiagLevel::Critical, argsToString(
"Capture pattern \"OggS\" at ", expectedOffset,
" expected."), context);
264 if (m_iterator.resyncAt(expectedOffset)) {
266 argsToString(
"Found next capture pattern \"OggS\" at ", m_iterator.currentPageOffset(),
". Skipped ",
267 m_iterator.currentPageOffset() - expectedOffset,
" invalid bytes."),
269 continueFromHere =
true;
273 "Aborting after not being able to find any \"OggS\" capture patterns within 65307 bytes (from offset ", expectedOffset,
")."),
426 const auto context = std::string(
"making Ogg file");
429 auto originalPath =
fileInfo().
path(), backupPath = std::string();
430 auto backupStream = NativeFileStream();
432 if (
fileInfo().saveFilePath().empty()) {
437 fileInfo().
stream().open(originalPath, ios_base::out | ios_base::binary | ios_base::trunc);
438 }
catch (
const std::ios_base::failure &failure) {
440 DiagLevel::Critical, argsToString(
"Creation of temporary file (to rewrite the original file) failed: ", failure.what()), context);
446 backupStream.exceptions(ios_base::badbit | ios_base::failbit);
451 }
catch (
const std::ios_base::failure &failure) {
452 diag.emplace_back(
DiagLevel::Critical, argsToString(
"Opening streams to write output file failed: ", failure.what()), context);
464 auto tagIterator =
m_tags.cbegin(), tagEnd =
m_tags.cend();
465 if (tagIterator != tagEnd) {
466 currentParams = &(currentComment = tagIterator->get())->oggParams();
468 currentComment =
nullptr;
469 currentParams =
nullptr;
473 const OggPage *lastPage =
nullptr;
474 static constexpr auto oggPageHeaderSize = 27u;
475 auto lastPageNewOffset = std::uint64_t();
476 auto copyHelper = CopyHelper<65307>();
477 auto updatedPageOffsets = std::vector<std::uint64_t>();
478 auto nextPageOffset = std::uint64_t();
479 auto pageSequenceNumberBySerialNo = std::unordered_map<std::uint32_t, std::uint32_t>();
482 auto updateTick = 0u;
483 for (m_iterator.setStream(backupStream), m_iterator.removeFilter(), m_iterator.reset(); m_iterator; m_iterator.nextPage(), ++updateTick) {
484 const OggPage ¤tPage = m_iterator.currentPage();
485 if (updateTick % 10) {
493 if (lastPage && currentPage.
startOffset() != nextPageOffset) {
494 m_iterator.pages().resize(m_iterator.currentPageIndex() - 1);
495 if (m_iterator.resyncAt(nextPageOffset)) {
497 const auto actuallyNextPageOffset = m_iterator.currentPageOffset();
498 if (actuallyNextPageOffset != nextPageOffset) {
500 argsToString(
"Expected Ogg page at offset ", nextPageOffset,
" but found the next Ogg page only at offset ",
501 actuallyNextPageOffset,
". Skipped ", (actuallyNextPageOffset - nextPageOffset),
" invalid bytes."),
503 nextPageOffset = actuallyNextPageOffset;
505 m_iterator.previousPage();
510 "Expected Ogg page at offset ", nextPageOffset,
" but could not find any further pages. Skipped the rest of the file."),
515 const auto pageSize = currentPage.
totalSize();
516 auto &pageSequenceNumber = pageSequenceNumberBySerialNo[currentPage.
streamSerialNumber()];
517 lastPage = ¤tPage;
518 lastPageNewOffset =
static_cast<std::uint64_t
>(
stream().tellp());
519 nextPageOffset = currentPage.
startOffset() + pageSize;
522 if (currentComment && m_iterator.currentPageIndex() >= currentParams->
firstPageIndex
526 auto buffer = std::stringstream(std::ios_base::in | std::ios_base::out | std::ios_base::binary);
527 auto newSegmentSizes = std::vector<std::uint32_t>();
528 newSegmentSizes.reserve(currentPage.
segmentSizes().size());
529 auto segmentOffset = m_iterator.currentSegmentOffset();
530 auto segmentIndex = std::vector<std::uint32_t>::size_type();
531 for (
const auto segmentSize : currentPage.
segmentSizes()) {
539 && (m_iterator.currentPageIndex() <= currentParams->
lastPageIndex && segmentIndex <= currentParams->lastSegmentIndex)) {
542 && ((m_iterator.currentPageIndex() == currentParams->
firstPageIndex
544 makeVorbisCommentSegment(buffer, copyHelper, newSegmentSizes, currentComment, currentParams, diag);
548 if (m_iterator.currentPageIndex() > currentParams->
lastPageIndex
550 if (++tagIterator != tagEnd) {
551 currentParams = &(currentComment = tagIterator->get())->oggParams();
553 currentComment =
nullptr;
554 currentParams =
nullptr;
559 backupStream.seekg(
static_cast<std::streamoff
>(segmentOffset));
560 copyHelper.copy(backupStream, buffer, segmentSize);
561 newSegmentSizes.push_back(segmentSize);
564 if (currentParams && m_iterator.currentPageIndex() == currentParams->
lastPageIndex
567 makeVorbisCommentSegment(buffer, copyHelper, newSegmentSizes, currentComment, currentParams, diag);
570 if (++tagIterator != tagEnd) {
571 currentParams = &(currentComment = tagIterator->get())->oggParams();
573 currentComment =
nullptr;
574 currentParams =
nullptr;
578 segmentOffset += segmentSize;
583 if (
auto newSegmentSizesIterator = newSegmentSizes.cbegin(), newSegmentSizesEnd = newSegmentSizes.cend();
584 newSegmentSizesIterator != newSegmentSizesEnd) {
585 auto bytesLeft = *newSegmentSizesIterator;
586 auto continuePreviousSegment =
false, needsZeroLacingValue =
false;
588 while (newSegmentSizesIterator != newSegmentSizesEnd) {
590 updatedPageOffsets.push_back(
static_cast<std::uint64_t
>(
stream().tellp()));
592 backupStream.seekg(
static_cast<streamoff
>(currentPage.
startOffset()));
593 copyHelper.copy(backupStream,
stream(), oggPageHeaderSize);
595 auto flags = (currentPage.
headerTypeFlag() & 0xFE) | (continuePreviousSegment ? 0x01 : 0x00);
596 continuePreviousSegment =
true;
599 flags = flags & (newSegmentSizesIterator != newSegmentSizes.cbegin() ? 0xFD : 0xF);
601 stream().seekp(-22, ios_base::cur);
602 stream().put(
static_cast<char>(flags));
604 stream().seekp(12, ios_base::cur);
606 writer().writeUInt32LE(pageSequenceNumber);
608 stream().seekp(5, ios_base::cur);
611 auto segmentSizesWritten = std::int16_t();
612 auto currentSize = std::uint32_t();
613 while ((bytesLeft || needsZeroLacingValue) && segmentSizesWritten < 0xFF) {
614 while (bytesLeft > 0xFF && segmentSizesWritten < 0xFF) {
615 stream().put(
static_cast<char>(0xFF));
618 ++segmentSizesWritten;
620 if ((bytesLeft || needsZeroLacingValue) && segmentSizesWritten < 0xFF) {
622 stream().put(
static_cast<char>(bytesLeft));
623 currentSize += bytesLeft;
624 needsZeroLacingValue = bytesLeft == 0xFF;
626 ++segmentSizesWritten;
628 if (!bytesLeft && !needsZeroLacingValue) {
631 if (++newSegmentSizesIterator != newSegmentSizesEnd) {
632 bytesLeft = *newSegmentSizesIterator;
633 continuePreviousSegment =
false;
639 if (!bytesLeft && !needsZeroLacingValue) {
640 continuePreviousSegment =
false;
644 if (newSegmentSizesIterator != newSegmentSizesEnd) {
646 stream().seekp(-21 - segmentSizesWritten, ios_base::cur);
647 writer().writeInt64LE(-1);
648 stream().seekp(12, ios_base::cur);
651 backupStream.seekg(
static_cast<streamoff
>(m_iterator.pages()[currentParams->
lastPageIndex].startOffset() + 6));
652 stream().seekp(-21 - segmentSizesWritten, ios_base::cur);
653 copyHelper.copy(backupStream,
stream(), 8);
654 stream().seekp(12, ios_base::cur);
657 stream().seekp(-1 - segmentSizesWritten, ios_base::cur);
663 stream().put(
static_cast<char>(segmentSizesWritten));
664 stream().seekp(segmentSizesWritten, ios_base::cur);
666 copyHelper.copy(buffer,
stream(), currentSize);
668 ++pageSequenceNumber;
673 if (pageSequenceNumber != m_iterator.currentPageIndex()) {
675 backupStream.seekg(
static_cast<streamoff
>(currentPage.
startOffset()));
676 updatedPageOffsets.push_back(
static_cast<std::uint64_t
>(
stream().tellp()));
677 copyHelper.copy(backupStream,
stream(), oggPageHeaderSize);
678 stream().seekp(-9, ios_base::cur);
679 writer().writeUInt32LE(pageSequenceNumber);
680 stream().seekp(5, ios_base::cur);
681 copyHelper.copy(backupStream,
stream(), pageSize - oggPageHeaderSize);
684 backupStream.seekg(
static_cast<streamoff
>(currentPage.
startOffset()));
685 copyHelper.copy(backupStream,
stream(), pageSize);
687 ++pageSequenceNumber;
696 if (!
fileInfo().saveFilePath().empty()) {
703 backupStream.close();
708 if (lastPage && lastPageNewOffset) {
709 const auto offset =
static_cast<std::streamoff
>(lastPageNewOffset + 5ul);
711 if (
const auto flag =
stream.get(); !(flag & 0x04)) {
712 updatedPageOffsets.emplace_back(lastPageNewOffset);
714 stream.put(
static_cast<char>(flag | 0x04));
721 for (
auto offset : updatedPageOffsets) {
722 if (updateTick++ % 10) {