3 #include "../flac/flacmetadata.h" 5 #include "../mediafileinfo.h" 6 #include "../backuphelper.h" 8 #include <c++utilities/conversion/stringbuilder.h> 9 #include <c++utilities/io/copy.h> 10 #include <c++utilities/io/catchiofailure.h> 25 const char *OggVorbisComment::typeName()
const 27 switch(m_oggParams.streamFormat) {
29 return "Vorbis comment (in FLAC stream)";
30 case GeneralMediaFormat::Opus:
31 return "Vorbis comment (in Opus stream)";
32 case GeneralMediaFormat::Theora:
33 return "Vorbis comment (in Theora stream)";
35 return "Vorbis comment";
47 OggContainer::OggContainer(
MediaFileInfo &fileInfo, uint64 startOffset) :
49 m_iterator(fileInfo.stream(), startOffset, fileInfo.size()),
50 m_validateChecksums(false)
73 if(!target.
tracks().empty()) {
81 for(
auto &
tag : m_tags) {
90 }
else if(!
m_tags.empty()) {
92 m_tags.front()->oggParams().removed =
false;
93 return m_tags.front().get();
107 m_tags.back()->setTarget(target);
108 return m_tags.back().get();
157 for(
auto &existingTag :
m_tags) {
158 if(static_cast<Tag *>(existingTag.get()) == tag) {
159 existingTag->removeAllFields();
160 existingTag->oggParams().removed =
true;
179 for(
auto &existingTag :
m_tags) {
180 existingTag->removeAllFields();
181 existingTag->oggParams().removed =
true;
187 static const string context(
"parsing OGG bitstream header");
193 if(m_validateChecksums) {
201 }
catch(
const out_of_range &) {
208 if(stream->m_currentSequenceNumber) {
213 ++stream->m_currentSequenceNumber;
265 void OggContainer::announceComment(std::size_t pageIndex, std::size_t segmentIndex,
bool lastMetaDataBlock,
GeneralMediaFormat mediaFormat)
267 m_tags.emplace_back(make_unique<OggVorbisComment>());
268 m_tags.back()->oggParams().set(pageIndex, segmentIndex, lastMetaDataBlock, mediaFormat);
273 static const string context(
"parsing OGG stream");
292 const auto offset = buffer.tellp();
295 comment->
make(buffer);
298 ConversionUtilities::BE::getBytes(0x4F70757354616773u, copyHelper.buffer());
299 buffer.write(copyHelper.buffer(), 8);
309 buffer.write(copyHelper.buffer(), 4);
318 buffer.seekp(offset);
320 buffer.seekp(header.
dataSize(), ios_base::cur);
325 newSegmentSizes.push_back(buffer.tellp() - offset);
330 const string context(
"making OGG file");
334 NativeFileStream backupStream;
336 if(
fileInfo().saveFilePath().empty()) {
343 const char *what = catchIoFailure();
345 throwIoFailure(what);
350 backupStream.exceptions(ios_base::badbit | ios_base::failbit);
351 backupStream.open(
fileInfo().path(), ios_base::in | ios_base::binary);
355 const char *what = catchIoFailure();
357 throwIoFailure(what);
365 auto tagIterator =
m_tags.cbegin(), tagEnd =
m_tags.cend();
366 if(tagIterator != tagEnd) {
367 currentParams = &(currentComment = tagIterator->get())->oggParams();
369 currentComment =
nullptr;
370 currentParams =
nullptr;
375 vector<uint64> updatedPageOffsets;
376 unordered_map<uint32, uint32> pageSequenceNumberBySerialNo;
381 const auto pageSize = currentPage.
totalSize();
382 uint32 &pageSequenceNumber = pageSequenceNumberBySerialNo[currentPage.
streamSerialNumber()];
390 stringstream buffer(ios_base::in | ios_base::out | ios_base::binary);
391 vector<uint32> newSegmentSizes;
392 newSegmentSizes.reserve(currentPage.
segmentSizes().size());
394 vector<uint32>::size_type segmentIndex = 0;
395 for(
const auto segmentSize : currentPage.
segmentSizes()) {
404 makeVorbisCommentSegment(buffer, copyHelper, newSegmentSizes, currentComment, currentParams);
410 if(++tagIterator != tagEnd) {
411 currentParams = &(currentComment = tagIterator->get())->oggParams();
413 currentComment =
nullptr;
414 currentParams =
nullptr;
419 backupStream.seekg(segmentOffset);
420 copyHelper.copy(backupStream, buffer, segmentSize);
421 newSegmentSizes.push_back(segmentSize);
426 makeVorbisCommentSegment(buffer, copyHelper, newSegmentSizes, currentComment, currentParams);
429 if(++tagIterator != tagEnd) {
430 currentParams = &(currentComment = tagIterator->get())->oggParams();
432 currentComment =
nullptr;
433 currentParams =
nullptr;
437 segmentOffset += segmentSize;
443 auto newSegmentSizesIterator = newSegmentSizes.cbegin(), newSegmentSizesEnd = newSegmentSizes.cend();
444 bool continuePreviousSegment =
false;
445 if(newSegmentSizesIterator != newSegmentSizesEnd) {
446 uint32 bytesLeft = *newSegmentSizesIterator;
448 while(newSegmentSizesIterator != newSegmentSizesEnd) {
451 updatedPageOffsets.push_back(
stream().tellp());
452 copyHelper.copy(backupStream,
stream(), 27);
454 stream().seekp(-22, ios_base::cur);
456 continuePreviousSegment =
true;
458 stream().seekp(12, ios_base::cur);
459 writer().writeUInt32LE(pageSequenceNumber);
460 stream().seekp(5, ios_base::cur);
461 int16 segmentSizesWritten = 0;
464 uint32 currentSize = 0;
465 while(bytesLeft && segmentSizesWritten < 0xFF) {
466 while(bytesLeft >= 0xFF && segmentSizesWritten < 0xFF) {
470 ++segmentSizesWritten;
472 if(bytesLeft && segmentSizesWritten < 0xFF) {
475 currentSize += bytesLeft;
477 ++segmentSizesWritten;
482 if(++newSegmentSizesIterator != newSegmentSizesEnd) {
483 bytesLeft = *newSegmentSizesIterator;
484 continuePreviousSegment =
false;
491 continuePreviousSegment =
false;
497 stream().seekp(-1 - segmentSizesWritten, ios_base::cur);
498 stream().put(segmentSizesWritten);
499 stream().seekp(segmentSizesWritten, ios_base::cur);
501 copyHelper.copy(buffer,
stream(), currentSize);
503 ++pageSequenceNumber;
511 updatedPageOffsets.push_back(
stream().tellp());
512 copyHelper.copy(backupStream,
stream(), 27);
513 stream().seekp(-9, ios_base::cur);
514 writer().writeUInt32LE(pageSequenceNumber);
515 stream().seekp(5, ios_base::cur);
516 copyHelper.copy(backupStream,
stream(), pageSize - 27);
520 copyHelper.copy(backupStream,
stream(), pageSize);
522 ++pageSequenceNumber;
530 if(!
fileInfo().saveFilePath().empty()) {
536 backupStream.close();
541 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...
const std::vector< uint32 > & segmentSizes() const
Returns the sizes of the segments of the page in byte.