4 #include "../flac/flacmetadata.h" 6 #include "../mediafileinfo.h" 7 #include "../backuphelper.h" 9 #include <c++utilities/conversion/stringbuilder.h> 10 #include <c++utilities/io/copy.h> 11 #include <c++utilities/io/catchiofailure.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";
48 OggContainer::OggContainer(
MediaFileInfo &fileInfo, uint64 startOffset) :
50 m_iterator(fileInfo.stream(), startOffset, fileInfo.size()),
51 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 static const string context(
"parsing OGG bitstream header");
189 bool pagesSkipped =
false;
200 uint64 lastNewStreamOffset = 0;
204 }
catch(
const out_of_range &) {
212 if(
stream->m_currentSequenceNumber) {
217 ++
stream->m_currentSequenceNumber;
223 && (page.
startOffset() - lastNewStreamOffset) > (20 * 0x100000)) {
230 argsToString(
"Pages in the middle of the file (", dataSizeToString(resyncedPage.
startOffset() - page.
startOffset()) ,
") have been skipped to improve parsing speed. Hence track sizes can not be computed. Maybe not even all tracks could be detected. Force a full parse to prevent this."),
295 void OggContainer::announceComment(std::size_t pageIndex, std::size_t segmentIndex,
bool lastMetaDataBlock,
GeneralMediaFormat mediaFormat)
297 m_tags.emplace_back(make_unique<OggVorbisComment>());
298 m_tags.back()->oggParams().set(pageIndex, segmentIndex, lastMetaDataBlock, mediaFormat);
303 static const string context(
"parsing OGG stream");
322 const auto offset = buffer.tellp();
328 ConversionUtilities::BE::getBytes(static_cast<uint64>(0x4F70757354616773u), copyHelper.buffer());
329 buffer.write(copyHelper.buffer(), 8);
339 buffer.write(copyHelper.buffer(), 4);
348 buffer.seekp(offset);
350 buffer.seekp(header.
dataSize(), ios_base::cur);
355 newSegmentSizes.push_back(buffer.tellp() - offset);
360 const string context(
"making OGG file");
364 NativeFileStream backupStream;
366 if(
fileInfo().saveFilePath().empty()) {
373 const char *what = catchIoFailure();
375 throwIoFailure(what);
380 backupStream.exceptions(ios_base::badbit | ios_base::failbit);
381 backupStream.open(
fileInfo().path(), ios_base::in | ios_base::binary);
385 const char *what = catchIoFailure();
387 throwIoFailure(what);
395 auto tagIterator =
m_tags.cbegin(), tagEnd =
m_tags.cend();
396 if(tagIterator != tagEnd) {
397 currentParams = &(currentComment = tagIterator->get())->oggParams();
399 currentComment =
nullptr;
400 currentParams =
nullptr;
405 vector<uint64> updatedPageOffsets;
406 unordered_map<uint32, uint32> pageSequenceNumberBySerialNo;
411 const auto pageSize = currentPage.
totalSize();
412 uint32 &pageSequenceNumber = pageSequenceNumberBySerialNo[currentPage.
streamSerialNumber()];
420 stringstream buffer(ios_base::in | ios_base::out | ios_base::binary);
421 vector<uint32> newSegmentSizes;
422 newSegmentSizes.reserve(currentPage.
segmentSizes().size());
424 vector<uint32>::size_type segmentIndex = 0;
425 for(
const auto segmentSize : currentPage.
segmentSizes()) {
434 makeVorbisCommentSegment(buffer, copyHelper, newSegmentSizes, currentComment, currentParams);
440 if(++tagIterator != tagEnd) {
441 currentParams = &(currentComment = tagIterator->get())->oggParams();
443 currentComment =
nullptr;
444 currentParams =
nullptr;
449 backupStream.seekg(segmentOffset);
450 copyHelper.copy(backupStream, buffer, segmentSize);
451 newSegmentSizes.push_back(segmentSize);
456 makeVorbisCommentSegment(buffer, copyHelper, newSegmentSizes, currentComment, currentParams);
459 if(++tagIterator != tagEnd) {
460 currentParams = &(currentComment = tagIterator->get())->oggParams();
462 currentComment =
nullptr;
463 currentParams =
nullptr;
467 segmentOffset += segmentSize;
473 auto newSegmentSizesIterator = newSegmentSizes.cbegin(), newSegmentSizesEnd = newSegmentSizes.cend();
474 bool continuePreviousSegment =
false;
475 if(newSegmentSizesIterator != newSegmentSizesEnd) {
476 uint32 bytesLeft = *newSegmentSizesIterator;
478 while(newSegmentSizesIterator != newSegmentSizesEnd) {
481 updatedPageOffsets.push_back(
stream().tellp());
482 copyHelper.copy(backupStream,
stream(), 27);
484 stream().seekp(-22, ios_base::cur);
486 continuePreviousSegment =
true;
488 stream().seekp(12, ios_base::cur);
489 writer().writeUInt32LE(pageSequenceNumber);
490 stream().seekp(5, ios_base::cur);
491 int16 segmentSizesWritten = 0;
494 uint32 currentSize = 0;
495 while(bytesLeft && segmentSizesWritten < 0xFF) {
496 while(bytesLeft >= 0xFF && segmentSizesWritten < 0xFF) {
500 ++segmentSizesWritten;
502 if(bytesLeft && segmentSizesWritten < 0xFF) {
505 currentSize += bytesLeft;
507 ++segmentSizesWritten;
512 if(++newSegmentSizesIterator != newSegmentSizesEnd) {
513 bytesLeft = *newSegmentSizesIterator;
514 continuePreviousSegment =
false;
521 continuePreviousSegment =
false;
527 stream().seekp(-1 - segmentSizesWritten, ios_base::cur);
528 stream().put(segmentSizesWritten);
529 stream().seekp(segmentSizesWritten, ios_base::cur);
531 copyHelper.copy(buffer,
stream(), currentSize);
533 ++pageSequenceNumber;
541 updatedPageOffsets.push_back(
stream().tellp());
542 copyHelper.copy(backupStream,
stream(), 27);
543 stream().seekp(-9, ios_base::cur);
544 writer().writeUInt32LE(pageSequenceNumber);
545 stream().seekp(5, ios_base::cur);
546 copyHelper.copy(backupStream,
stream(), pageSize - 27);
550 copyHelper.copy(backupStream,
stream(), pageSize);
552 ++pageSequenceNumber;
560 if(!
fileInfo().saveFilePath().empty()) {
566 backupStream.close();
571 for(
auto offset : updatedPageOffsets) {
uint32 checksum() const
Returns the page checksum.
uint32 sequenceNumber() const
Returns the page sequence number.
uint32 streamSerialNumber() const
Returns the stream serial number.
uint32 totalSize() const
Returns the total size of the page in byte.
The OggPage class is used to parse OGG pages.
static void updateChecksum(std::iostream &stream, uint64 startOffset)
Updates the checksum of the page read from the specified stream at the specified startOffset.
Contains utility classes helping to read and write streams.
byte headerTypeFlag() const
Returns the header type flag.
uint64 startOffset() const
Returns the start offset of the page.
static uint32 computeChecksum(std::istream &stream, uint64 startOffset)
Computes the actual checksum of the page read from the specified stream at the specified startOffset...
uint32 dataSize() const
Returns the data size in byte.
const std::vector< uint32 > & segmentSizes() const
Returns the sizes of the segments of the page in byte.