3 #include "../flac/flacmetadata.h" 5 #include "../mediafileinfo.h" 6 #include "../backuphelper.h" 8 #include <c++utilities/io/copy.h> 9 #include <c++utilities/io/catchiofailure.h> 10 #include <c++utilities/misc/memory.h> 22 const char *OggVorbisComment::typeName()
const 24 switch(m_oggParams.streamFormat) {
26 return "Vorbis comment (in FLAC stream)";
27 case GeneralMediaFormat::Opus:
28 return "Vorbis comment (in Opus stream)";
29 case GeneralMediaFormat::Theora:
30 return "Vorbis comment (in Theora stream)";
32 return "Vorbis comment";
44 OggContainer::OggContainer(
MediaFileInfo &fileInfo, uint64 startOffset) :
46 m_iterator(fileInfo.stream(), startOffset, fileInfo.size()),
47 m_validateChecksums(false)
70 if(!target.
tracks().empty()) {
78 for(
auto &
tag : m_tags) {
87 }
else if(!
m_tags.empty()) {
89 m_tags.front()->oggParams().removed =
false;
90 return m_tags.front().get();
104 m_tags.back()->setTarget(target);
105 return m_tags.back().get();
154 for(
auto &existingTag :
m_tags) {
155 if(static_cast<Tag *>(existingTag.get()) == tag) {
156 existingTag->removeAllFields();
157 existingTag->oggParams().removed =
true;
176 for(
auto &existingTag :
m_tags) {
177 existingTag->removeAllFields();
178 existingTag->oggParams().removed =
true;
184 static const string context(
"parsing OGG bitstream header");
190 if(m_validateChecksums) {
198 }
catch(
const out_of_range &) {
205 if(stream->m_currentSequenceNumber) {
210 ++stream->m_currentSequenceNumber;
262 void OggContainer::announceComment(std::size_t pageIndex, std::size_t segmentIndex,
bool lastMetaDataBlock,
GeneralMediaFormat mediaFormat)
264 m_tags.emplace_back(make_unique<OggVorbisComment>());
265 m_tags.back()->oggParams().set(pageIndex, segmentIndex, lastMetaDataBlock, mediaFormat);
270 static const string context(
"parsing OGG stream");
289 const auto offset = buffer.tellp();
292 comment->
make(buffer);
295 ConversionUtilities::BE::getBytes(0x4F70757354616773u, copyHelper.buffer());
296 buffer.write(copyHelper.buffer(), 8);
306 buffer.write(copyHelper.buffer(), 4);
315 buffer.seekp(offset);
317 buffer.seekp(header.
dataSize(), ios_base::cur);
322 newSegmentSizes.push_back(buffer.tellp() - offset);
327 const string context(
"making OGG file");
331 NativeFileStream backupStream;
333 if(
fileInfo().saveFilePath().empty()) {
340 const char *what = catchIoFailure();
342 throwIoFailure(what);
347 backupStream.exceptions(ios_base::badbit | ios_base::failbit);
348 backupStream.open(
fileInfo().path(), ios_base::in | ios_base::binary);
352 const char *what = catchIoFailure();
354 throwIoFailure(what);
362 auto tagIterator =
m_tags.cbegin(), tagEnd =
m_tags.cend();
363 if(tagIterator != tagEnd) {
364 currentParams = &(currentComment = tagIterator->get())->oggParams();
366 currentComment =
nullptr;
367 currentParams =
nullptr;
372 vector<uint64> updatedPageOffsets;
373 unordered_map<uint32, uint32> pageSequenceNumberBySerialNo;
378 const auto pageSize = currentPage.
totalSize();
379 uint32 &pageSequenceNumber = pageSequenceNumberBySerialNo[currentPage.
streamSerialNumber()];
387 stringstream buffer(ios_base::in | ios_base::out | ios_base::binary);
388 vector<uint32> newSegmentSizes;
389 newSegmentSizes.reserve(currentPage.
segmentSizes().size());
391 vector<uint32>::size_type segmentIndex = 0;
392 for(
const auto segmentSize : currentPage.
segmentSizes()) {
401 makeVorbisCommentSegment(buffer, copyHelper, newSegmentSizes, currentComment, currentParams);
407 if(++tagIterator != tagEnd) {
408 currentParams = &(currentComment = tagIterator->get())->oggParams();
410 currentComment =
nullptr;
411 currentParams =
nullptr;
416 backupStream.seekg(segmentOffset);
417 copyHelper.copy(backupStream, buffer, segmentSize);
418 newSegmentSizes.push_back(segmentSize);
423 makeVorbisCommentSegment(buffer, copyHelper, newSegmentSizes, currentComment, currentParams);
426 if(++tagIterator != tagEnd) {
427 currentParams = &(currentComment = tagIterator->get())->oggParams();
429 currentComment =
nullptr;
430 currentParams =
nullptr;
434 segmentOffset += segmentSize;
440 auto newSegmentSizesIterator = newSegmentSizes.cbegin(), newSegmentSizesEnd = newSegmentSizes.cend();
441 bool continuePreviousSegment =
false;
442 if(newSegmentSizesIterator != newSegmentSizesEnd) {
443 uint32 bytesLeft = *newSegmentSizesIterator;
445 while(newSegmentSizesIterator != newSegmentSizesEnd) {
448 updatedPageOffsets.push_back(
stream().tellp());
449 copyHelper.copy(backupStream,
stream(), 27);
451 stream().seekp(-22, ios_base::cur);
453 continuePreviousSegment =
true;
455 stream().seekp(12, ios_base::cur);
456 writer().writeUInt32LE(pageSequenceNumber);
457 stream().seekp(5, ios_base::cur);
458 int16 segmentSizesWritten = 0;
461 uint32 currentSize = 0;
462 while(bytesLeft && segmentSizesWritten < 0xFF) {
463 while(bytesLeft >= 0xFF && segmentSizesWritten < 0xFF) {
467 ++segmentSizesWritten;
469 if(bytesLeft && segmentSizesWritten < 0xFF) {
472 currentSize += bytesLeft;
474 ++segmentSizesWritten;
479 if(++newSegmentSizesIterator != newSegmentSizesEnd) {
480 bytesLeft = *newSegmentSizesIterator;
481 continuePreviousSegment =
false;
488 continuePreviousSegment =
false;
494 stream().seekp(-1 - segmentSizesWritten, ios_base::cur);
495 stream().put(segmentSizesWritten);
496 stream().seekp(segmentSizesWritten, ios_base::cur);
498 copyHelper.copy(buffer,
stream(), currentSize);
500 ++pageSequenceNumber;
508 updatedPageOffsets.push_back(
stream().tellp());
509 copyHelper.copy(backupStream,
stream(), 27);
510 stream().seekp(-9, ios_base::cur);
511 writer().writeUInt32LE(pageSequenceNumber);
512 stream().seekp(5, ios_base::cur);
513 copyHelper.copy(backupStream,
stream(), pageSize - 27);
517 copyHelper.copy(backupStream,
stream(), pageSize);
519 ++pageSequenceNumber;
527 if(!
fileInfo().saveFilePath().empty()) {
533 backupStream.close();
538 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.