Tag Parser  10.0.0
C++ library for reading and writing MP4 (iTunes), ID3, Vorbis, Opus, FLAC and Matroska tags
oggcontainer.cpp
Go to the documentation of this file.
1 #include "./oggcontainer.h"
2 
3 #include "../flac/flacmetadata.h"
4 
5 #include "../backuphelper.h"
6 #include "../mediafileinfo.h"
7 #include "../progressfeedback.h"
8 
9 #include <c++utilities/conversion/stringbuilder.h>
10 #include <c++utilities/io/copy.h>
11 
12 #include <memory>
13 
14 using namespace std;
15 using namespace CppUtilities;
16 
17 namespace TagParser {
18 
24 std::string_view OggVorbisComment::typeName() const
25 {
26  switch (m_oggParams.streamFormat) {
28  return "Vorbis comment (in FLAC stream)";
30  return "Vorbis comment (in Opus stream)";
31  case GeneralMediaFormat::Theora:
32  return "Vorbis comment (in Theora stream)";
33  default:
34  return "Vorbis comment";
35  }
36 }
37 
46 OggContainer::OggContainer(MediaFileInfo &fileInfo, std::uint64_t startOffset)
48  , m_iterator(fileInfo.stream(), startOffset, fileInfo.size())
49  , m_validateChecksums(false)
50 {
51 }
52 
54 {
55 }
56 
58 {
59  m_iterator.reset();
60 }
61 
73 {
74  if (!target.tracks().empty()) {
75  // return the tag for the first matching track ID
76  for (auto &tag : m_tags) {
77  if (!tag->target().tracks().empty() && tag->target().tracks().front() == target.tracks().front() && !tag->oggParams().removed) {
78  return tag.get();
79  }
80  }
81  // not tag found -> try to re-use a tag which has been flagged as removed
82  for (auto &tag : m_tags) {
83  if (!tag->target().tracks().empty() && tag->target().tracks().front() == target.tracks().front()) {
84  tag->oggParams().removed = false;
85  return tag.get();
86  }
87  }
88  } else if (OggVorbisComment *comment = tag(0)) {
89  // no track ID specified -> just return the first tag (if one exists)
90  return comment;
91  } else if (!m_tags.empty()) {
92  // no track ID specified -> just return the first tag (try to re-use a tag which has been flagged as removed)
93  m_tags.front()->oggParams().removed = false;
94  return m_tags.front().get();
95  }
96 
97  // a new tag needs to be created
98  // -> determine an appropriate track for the tag
99  // -> just use the first Vorbis/Opus track
100  for (const auto &track : m_tracks) {
101  if (target.tracks().empty() || target.tracks().front() == track->id()) {
102  switch (track->format().general) {
105  // check whether start page has a valid value
106  if (track->startPage() < m_iterator.pages().size()) {
107  announceComment(track->startPage(), numeric_limits<size_t>::max(), false, track->format().general);
108  m_tags.back()->setTarget(target);
109  return m_tags.back().get();
110  } else {
111  // TODO: error handling?
112  }
113  break;
114  default:;
115  }
116  // TODO: allow adding tags to FLAC tracks (not really important, because a tag should always be present)
117  }
118  }
119  return nullptr;
120 }
121 
123 {
124  size_t i = 0;
125  for (const auto &tag : m_tags) {
126  if (!tag->oggParams().removed) {
127  if (index == i) {
128  return tag.get();
129  }
130  ++i;
131  }
132  }
133  return nullptr;
134 }
135 
137 {
138  size_t count = 0;
139  for (const auto &tag : m_tags) {
140  if (!tag->oggParams().removed) {
141  ++count;
142  }
143  }
144  return count;
145 }
146 
157 {
158  for (auto &existingTag : m_tags) {
159  if (static_cast<Tag *>(existingTag.get()) == tag) {
160  existingTag->removeAllFields();
161  existingTag->oggParams().removed = true;
162  return true;
163  }
164  }
165  return false;
166 }
167 
179 {
180  for (auto &existingTag : m_tags) {
181  existingTag->removeAllFields();
182  existingTag->oggParams().removed = true;
183  }
184 }
185 
187 {
188  CPP_UTILITIES_UNUSED(progress)
189 
190  static const string context("parsing OGG bitstream header");
191  bool pagesSkipped = false;
192 
193  // iterate through pages using OggIterator helper class
194  try {
195  // ensure iterator is setup properly
196  for (m_iterator.removeFilter(), m_iterator.reset(); m_iterator; m_iterator.nextPage()) {
197  const OggPage &page = m_iterator.currentPage();
198  if (m_validateChecksums && page.checksum() != OggPage::computeChecksum(stream(), page.startOffset())) {
199  diag.emplace_back(DiagLevel::Warning,
200  argsToString(
201  "The denoted checksum of the OGG page at ", m_iterator.currentSegmentOffset(), " does not match the computed checksum."),
202  context);
203  }
204  OggStream *stream;
205  std::uint64_t lastNewStreamOffset = 0;
206  try {
207  stream = m_tracks[m_streamsBySerialNo.at(page.streamSerialNumber())].get();
208  stream->m_size += page.dataSize();
209  } catch (const out_of_range &) {
210  // new stream serial number recognized -> add new stream
211  m_streamsBySerialNo[page.streamSerialNumber()] = m_tracks.size();
212  m_tracks.emplace_back(make_unique<OggStream>(*this, m_iterator.currentPageIndex()));
213  stream = m_tracks.back().get();
214  lastNewStreamOffset = page.startOffset();
215  }
216  if (stream->m_currentSequenceNumber != page.sequenceNumber()) {
217  if (stream->m_currentSequenceNumber) {
218  diag.emplace_back(DiagLevel::Warning, "Page is missing (page sequence number omitted).", context);
219  }
220  stream->m_currentSequenceNumber = page.sequenceNumber() + 1;
221  } else {
222  ++stream->m_currentSequenceNumber;
223  }
224 
225  // skip pages in the middle of a big file (still more than 100 MiB to parse) if no new track has been seen since the last 20 MiB
226  if (!fileInfo().isForcingFullParse() && (fileInfo().size() - page.startOffset()) > (100 * 0x100000)
227  && (page.startOffset() - lastNewStreamOffset) > (20 * 0x100000)) {
228  if (m_iterator.resyncAt(fileInfo().size() - (20 * 0x100000))) {
229  const OggPage &resyncedPage = m_iterator.currentPage();
230  // prevent warning about missing pages
231  stream->m_currentSequenceNumber = resyncedPage.sequenceNumber() + 1;
232  pagesSkipped = true;
233  diag.emplace_back(DiagLevel::Information,
234  argsToString("Pages in the middle of the file (", dataSizeToString(resyncedPage.startOffset() - page.startOffset()),
235  ") have been skipped to improve parsing speed. Hence track sizes can not be computed. Maybe not even all tracks could be "
236  "detected. Force a full parse to prevent this."),
237  context);
238  } else {
239  // abort if skipping pages didn't work
240  diag.emplace_back(DiagLevel::Critical,
241  "Unable to re-sync after skipping OGG pages in the middle of the file. Try forcing a full parse.", context);
242  return;
243  }
244  }
245  }
246  } catch (const TruncatedDataException &) {
247  // thrown when page exceeds max size
248  diag.emplace_back(DiagLevel::Critical, "The OGG file is truncated.", context);
249  } catch (const InvalidDataException &) {
250  // thrown when first 4 byte do not match capture pattern
251  diag.emplace_back(
252  DiagLevel::Critical, argsToString("Capture pattern \"OggS\" at ", m_iterator.currentSegmentOffset(), " expected."), context);
253  }
254 
255  // invalidate stream sizes in case pages have been skipped
256  if (pagesSkipped) {
257  for (auto &stream : m_tracks) {
258  stream->m_size = 0;
259  }
260  }
261 }
262 
264 {
265  // tracks needs to be parsed before because tags are stored at stream level
266  parseTracks(diag, progress);
267  for (auto &comment : m_tags) {
268  OggParameter &params = comment->oggParams();
269  m_iterator.setPageIndex(params.firstPageIndex);
270  m_iterator.setSegmentIndex(params.firstSegmentIndex);
271  switch (params.streamFormat) {
273  comment->parse(m_iterator, VorbisCommentFlags::None, diag);
274  break;
276  // skip header (has already been detected by OggStream)
277  m_iterator.ignore(8);
279  break;
281  m_iterator.ignore(4);
283  break;
284  default:
285  diag.emplace_back(DiagLevel::Critical, "Stream format not supported.", "parsing tags from OGG streams");
286  }
287  params.lastPageIndex = m_iterator.currentPageIndex();
288  params.lastSegmentIndex = m_iterator.currentSegmentIndex();
289  }
290 }
291 
303 void OggContainer::announceComment(std::size_t pageIndex, std::size_t segmentIndex, bool lastMetaDataBlock, GeneralMediaFormat mediaFormat)
304 {
305  m_tags.emplace_back(make_unique<OggVorbisComment>());
306  m_tags.back()->oggParams().set(pageIndex, segmentIndex, lastMetaDataBlock, mediaFormat);
307 }
308 
310 {
311  static const string context("parsing OGG stream");
312  for (auto &stream : m_tracks) {
313  if (progress.isAborted()) {
315  }
316  try { // try to parse header
317  stream->parseHeader(diag, progress);
318  if (stream->duration() > m_duration) {
319  m_duration = stream->duration();
320  }
321  } catch (const Failure &) {
322  diag.emplace_back(DiagLevel::Critical, argsToString("Unable to parse stream at ", stream->startOffset(), '.'), context);
323  }
324  }
325 }
326 
331 void OggContainer::makeVorbisCommentSegment(stringstream &buffer, CopyHelper<65307> &copyHelper, vector<std::uint32_t> &newSegmentSizes,
333 {
334  const auto offset = buffer.tellp();
335  switch (params->streamFormat) {
337  comment->make(buffer, VorbisCommentFlags::None, diag);
338  break;
340  BE::getBytes(static_cast<std::uint64_t>(0x4F70757354616773u), copyHelper.buffer());
341  buffer.write(copyHelper.buffer(), 8);
343  break;
345  // Vorbis comment must be wrapped in "METADATA_BLOCK_HEADER"
347  header.setLast(params->lastMetaDataBlock);
349 
350  // write the header later, when the size is known
351  buffer.write(copyHelper.buffer(), 4);
352 
354 
355  // finally make the header
356  header.setDataSize(static_cast<std::uint32_t>(buffer.tellp() - offset - 4));
357  if (header.dataSize() > 0xFFFFFF) {
358  diag.emplace_back(
359  DiagLevel::Critical, "Size of Vorbis comment exceeds size limit for FLAC \"METADATA_BLOCK_HEADER\".", "making Vorbis Comment");
360  }
361  buffer.seekp(offset);
362  header.makeHeader(buffer);
363  buffer.seekp(header.dataSize(), ios_base::cur);
364  break;
365  }
366  default:;
367  }
368  newSegmentSizes.push_back(static_cast<std::uint32_t>(buffer.tellp() - offset));
369 }
370 
372 {
373  const string context("making OGG file");
374  progress.updateStep("Prepare for rewriting OGG file ...");
375  parseTags(diag, progress); // tags need to be parsed before the file can be rewritten
376  string backupPath;
377  NativeFileStream backupStream;
378 
379  if (fileInfo().saveFilePath().empty()) {
380  // move current file to temp dir and reopen it as backupStream, recreate original file
381  try {
382  BackupHelper::createBackupFile(fileInfo().backupDirectory(), fileInfo().path(), backupPath, fileInfo().stream(), backupStream);
383  // recreate original file, define buffer variables
384  fileInfo().stream().open(BasicFileInfo::pathForOpen(fileInfo().path()).data(), ios_base::out | ios_base::binary | ios_base::trunc);
385  } catch (const std::ios_base::failure &failure) {
386  diag.emplace_back(
387  DiagLevel::Critical, argsToString("Creation of temporary file (to rewrite the original file) failed: ", failure.what()), context);
388  throw;
389  }
390  } else {
391  // open the current file as backupStream and create a new outputStream at the specified "save file path"
392  try {
393  backupStream.exceptions(ios_base::badbit | ios_base::failbit);
394  backupStream.open(BasicFileInfo::pathForOpen(fileInfo().path()).data(), ios_base::in | ios_base::binary);
395  fileInfo().close();
396  fileInfo().stream().open(
397  BasicFileInfo::pathForOpen(fileInfo().saveFilePath()).data(), ios_base::out | ios_base::binary | ios_base::trunc);
398  } catch (const std::ios_base::failure &failure) {
399  diag.emplace_back(DiagLevel::Critical, argsToString("Opening streams to write output file failed: ", failure.what()), context);
400  throw;
401  }
402  }
403 
404  try {
405  // prepare iterating comments
406  OggVorbisComment *currentComment;
407  OggParameter *currentParams;
408  auto tagIterator = m_tags.cbegin(), tagEnd = m_tags.cend();
409  if (tagIterator != tagEnd) {
410  currentParams = &(currentComment = tagIterator->get())->oggParams();
411  } else {
412  currentComment = nullptr;
413  currentParams = nullptr;
414  }
415 
416  // define misc variables
417  CopyHelper<65307> copyHelper;
418  vector<std::uint64_t> updatedPageOffsets;
419  unordered_map<std::uint32_t, std::uint32_t> pageSequenceNumberBySerialNo;
420 
421  // iterate through all pages of the original file
422  for (m_iterator.setStream(backupStream), m_iterator.removeFilter(), m_iterator.reset(); m_iterator; m_iterator.nextPage()) {
423  const OggPage &currentPage = m_iterator.currentPage();
424  const auto pageSize = currentPage.totalSize();
425  std::uint32_t &pageSequenceNumber = pageSequenceNumberBySerialNo[currentPage.streamSerialNumber()];
426  // check whether the Vorbis Comment is present in this Ogg page
427  if (currentComment && m_iterator.currentPageIndex() >= currentParams->firstPageIndex
428  && m_iterator.currentPageIndex() <= currentParams->lastPageIndex && !currentPage.segmentSizes().empty()) {
429  // page needs to be rewritten (not just copied)
430  // -> write segments to a buffer first
431  stringstream buffer(ios_base::in | ios_base::out | ios_base::binary);
432  vector<std::uint32_t> newSegmentSizes;
433  newSegmentSizes.reserve(currentPage.segmentSizes().size());
434  std::uint64_t segmentOffset = m_iterator.currentSegmentOffset();
435  vector<std::uint32_t>::size_type segmentIndex = 0;
436  for (const auto segmentSize : currentPage.segmentSizes()) {
437  if (!segmentSize) {
438  ++segmentIndex;
439  continue;
440  }
441  // check whether this segment contains the Vorbis Comment
442  if ((m_iterator.currentPageIndex() >= currentParams->firstPageIndex && segmentIndex >= currentParams->firstSegmentIndex)
443  && (m_iterator.currentPageIndex() <= currentParams->lastPageIndex && segmentIndex <= currentParams->lastSegmentIndex)) {
444  // prevent making the comment twice if it spreads over multiple pages/segments
445  if (!currentParams->removed
446  && ((m_iterator.currentPageIndex() == currentParams->firstPageIndex
447  && m_iterator.currentSegmentIndex() == currentParams->firstSegmentIndex))) {
448  makeVorbisCommentSegment(buffer, copyHelper, newSegmentSizes, currentComment, currentParams, diag);
449  }
450 
451  // proceed with next comment?
452  if (m_iterator.currentPageIndex() > currentParams->lastPageIndex
453  || (m_iterator.currentPageIndex() == currentParams->lastPageIndex && segmentIndex > currentParams->lastSegmentIndex)) {
454  if (++tagIterator != tagEnd) {
455  currentParams = &(currentComment = tagIterator->get())->oggParams();
456  } else {
457  currentComment = nullptr;
458  currentParams = nullptr;
459  }
460  }
461  } else {
462  // copy other segments unchanged
463  backupStream.seekg(static_cast<streamoff>(segmentOffset));
464  copyHelper.copy(backupStream, buffer, segmentSize);
465  newSegmentSizes.push_back(segmentSize);
466 
467  // check whether there is a new comment to be inserted into the current page
468  if (m_iterator.currentPageIndex() == currentParams->lastPageIndex
469  && currentParams->firstSegmentIndex == numeric_limits<size_t>::max()) {
470  if (!currentParams->removed) {
471  makeVorbisCommentSegment(buffer, copyHelper, newSegmentSizes, currentComment, currentParams, diag);
472  }
473  // proceed with next comment
474  if (++tagIterator != tagEnd) {
475  currentParams = &(currentComment = tagIterator->get())->oggParams();
476  } else {
477  currentComment = nullptr;
478  currentParams = nullptr;
479  }
480  }
481  }
482  segmentOffset += segmentSize;
483  ++segmentIndex;
484  }
485 
486  // write buffered data to actual stream
487  auto newSegmentSizesIterator = newSegmentSizes.cbegin(), newSegmentSizesEnd = newSegmentSizes.cend();
488  bool continuePreviousSegment = false;
489  if (newSegmentSizesIterator != newSegmentSizesEnd) {
490  std::uint32_t bytesLeft = *newSegmentSizesIterator;
491  // write pages until all data in the buffer is written
492  while (newSegmentSizesIterator != newSegmentSizesEnd) {
493  // write header
494  backupStream.seekg(static_cast<streamoff>(currentPage.startOffset()));
495  updatedPageOffsets.push_back(static_cast<std::uint64_t>(stream().tellp())); // memorize offset to update checksum later
496  copyHelper.copy(backupStream, stream(), 27); // just copy header from original file
497  // set continue flag
498  stream().seekp(-22, ios_base::cur);
499  stream().put(static_cast<char>(currentPage.headerTypeFlag() & (continuePreviousSegment ? 0xFF : 0xFE)));
500  continuePreviousSegment = true;
501  // adjust page sequence number
502  stream().seekp(12, ios_base::cur);
503  writer().writeUInt32LE(pageSequenceNumber);
504  stream().seekp(5, ios_base::cur);
505  std::int16_t segmentSizesWritten = 0; // in the current page header only
506  // write segment sizes as long as there are segment sizes to be written and
507  // the max number of segment sizes (255) is not exceeded
508  std::uint32_t currentSize = 0;
509  while (bytesLeft && segmentSizesWritten < 0xFF) {
510  while (bytesLeft >= 0xFF && segmentSizesWritten < 0xFF) {
511  stream().put(static_cast<char>(0xFF));
512  currentSize += 0xFF;
513  bytesLeft -= 0xFF;
514  ++segmentSizesWritten;
515  }
516  if (bytesLeft && segmentSizesWritten < 0xFF) {
517  // bytes left is here < 0xFF
518  stream().put(static_cast<char>(bytesLeft));
519  currentSize += bytesLeft;
520  bytesLeft = 0;
521  ++segmentSizesWritten;
522  }
523  if (!bytesLeft) {
524  // sizes for the segment have been written
525  // -> continue with next segment
526  if (++newSegmentSizesIterator != newSegmentSizesEnd) {
527  bytesLeft = *newSegmentSizesIterator;
528  continuePreviousSegment = false;
529  }
530  }
531  }
532 
533  // there are no bytes left in the current segment; remove continue flag
534  if (!bytesLeft) {
535  continuePreviousSegment = false;
536  }
537 
538  // page is full or all segment data has been covered
539  // -> write segment table size (segmentSizesWritten) and segment data
540  // -> seek back and write updated page segment number
541  stream().seekp(-1 - segmentSizesWritten, ios_base::cur);
542  stream().put(static_cast<char>(segmentSizesWritten));
543  stream().seekp(segmentSizesWritten, ios_base::cur);
544  // -> write actual page data
545  copyHelper.copy(buffer, stream(), currentSize);
546 
547  ++pageSequenceNumber;
548  }
549  }
550 
551  } else {
552  if (pageSequenceNumber != m_iterator.currentPageIndex()) {
553  // just update page sequence number
554  backupStream.seekg(static_cast<streamoff>(currentPage.startOffset()));
555  updatedPageOffsets.push_back(static_cast<std::uint64_t>(stream().tellp())); // memorize offset to update checksum later
556  copyHelper.copy(backupStream, stream(), 27);
557  stream().seekp(-9, ios_base::cur);
558  writer().writeUInt32LE(pageSequenceNumber);
559  stream().seekp(5, ios_base::cur);
560  copyHelper.copy(backupStream, stream(), pageSize - 27);
561  } else {
562  // copy page unchanged
563  backupStream.seekg(static_cast<streamoff>(currentPage.startOffset()));
564  copyHelper.copy(backupStream, stream(), pageSize);
565  }
566  ++pageSequenceNumber;
567  }
568  }
569 
570  // report new size
571  fileInfo().reportSizeChanged(static_cast<std::uint64_t>(stream().tellp()));
572 
573  // "save as path" is now the regular path
574  if (!fileInfo().saveFilePath().empty()) {
575  fileInfo().reportPathChanged(fileInfo().saveFilePath());
576  fileInfo().setSaveFilePath(string());
577  }
578 
579  // close backups stream; reopen new file as readable stream
580  backupStream.close();
581  fileInfo().close();
582  fileInfo().stream().open(fileInfo().path(), ios_base::in | ios_base::out | ios_base::binary);
583 
584  // update checksums of modified pages
585  for (auto offset : updatedPageOffsets) {
587  }
588 
589  // prevent deferring final write operations (to catch and handle possible errors here)
590  fileInfo().stream().flush();
591 
592  // clear iterator
593  m_iterator.clear(fileInfo().stream(), startOffset(), fileInfo().size());
594 
595  } catch (...) {
596  m_iterator.setStream(fileInfo().stream());
597  BackupHelper::handleFailureAfterFileModified(fileInfo(), backupPath, fileInfo().stream(), backupStream, diag, context);
598  }
599 }
600 
601 } // namespace TagParser
The AbortableProgressFeedback class provides feedback about an ongoing operation via callbacks.
bool isAborted() const
Returns whether the operation has been aborted via tryToAbort().
std::iostream & stream()
Returns the related stream.
std::uint64_t startOffset() const
Returns the start offset in the related stream.
void parseTracks(Diagnostics &diag, AbortableProgressFeedback &progress)
Parses the tracks of the file if not parsed yet.
CppUtilities::BinaryWriter & writer()
Returns the related BinaryWriter.
void parseTags(Diagnostics &diag, AbortableProgressFeedback &progress)
Parses the tag information if not parsed yet.
CppUtilities::TimeSpan m_duration
std::uint64_t id() const
Returns the track ID if known; otherwise returns 0.
MediaFormat format() const
Returns the format of the track if known; otherwise returns MediaFormat::Unknown.
void reportPathChanged(std::string_view newPath)
Call this function to report that the path changed.
CppUtilities::NativeFileStream & stream()
Returns the std::fstream for the current instance.
Definition: basicfileinfo.h:85
void close()
A possibly opened std::fstream will be closed.
static std::string_view pathForOpen(std::string_view url)
Returns removes the "file:/" prefix from url to be able to pass it to functions like open(),...
void reportSizeChanged(std::uint64_t newSize)
Call this function to report that the size changed.
void updateStep(const std::string &step, std::uint8_t stepPercentage=0)
Updates the current step and invokes the first callback specified on construction.
The Diagnostics class is a container for DiagMessage.
Definition: diagnostics.h:156
The class inherits from std::exception and serves as base class for exceptions thrown by the elements...
Definition: exceptions.h:11
The FlacMetaDataBlockHeader class is a FLAC "METADATA_BLOCK_HEADER" parser and maker.
Definition: flacmetadata.h:28
constexpr std::uint32_t dataSize() const
Returns the length in bytes of the meta data (excluding the size of the header itself).
Definition: flacmetadata.h:95
void setType(FlacMetaDataBlockType type)
Sets the block type.
Definition: flacmetadata.h:87
void setLast(std::uint8_t last)
Sets whether this is the last metadata block before the audio blocks.
Definition: flacmetadata.h:70
void setDataSize(std::uint32_t dataSize)
Sets the length in bytes of the meta data (excluding the size of the header itself).
Definition: flacmetadata.h:104
void makeHeader(std::ostream &outputStream)
Writes the header to the specified outputStream.
The GenericContainer class helps parsing header, track, tag and chapter information of a file.
The exception that is thrown when the data to be parsed or to be made seems invalid and therefore can...
Definition: exceptions.h:25
The MediaFileInfo class allows to read and write tag information providing a container/tag format ind...
Definition: mediafileinfo.h:74
void setSaveFilePath(std::string_view saveFilePath)
Sets the "save file path".
GeneralMediaFormat general
Definition: mediaformat.h:259
void internalParseTags(Diagnostics &diag, AbortableProgressFeedback &progress) override
Internally called to parse the tags.
OggVorbisComment * tag(std::size_t index) override
Returns the tag with the specified index.
std::size_t tagCount() const override
Returns the number of tags attached to the container.
void internalParseTracks(Diagnostics &diag, AbortableProgressFeedback &progress) override
Internally called to parse the tracks.
void internalParseHeader(Diagnostics &diag, AbortableProgressFeedback &progress) override
Internally called to parse the header.
void removeAllTags() override
Actually just flags all tags as removed and clears all assigned fields.
OggVorbisComment * createTag(const TagTarget &target) override
Creates a new tag.
void reset() override
Discards all parsing results.
void internalMakeFile(Diagnostics &diag, AbortableProgressFeedback &progress) override
Internally called to make the file.
bool removeTag(Tag *tag) override
Actually just flags the specified tag as removed and clears all assigned fields.
void nextPage()
Increases the current position by one page.
Definition: oggiterator.cpp:63
void setSegmentIndex(std::vector< std::uint32_t >::size_type index)
Sets the current segment index.
Definition: oggiterator.h:184
void removeFilter()
Removes a previously set filter.
Definition: oggiterator.h:252
void setStream(std::istream &stream)
Sets the stream.
Definition: oggiterator.h:98
std::vector< std::uint32_t >::size_type currentSegmentIndex() const
Returns the index of the current segment (in the current page) if the iterator is valid; otherwise an...
Definition: oggiterator.h:193
void clear(std::istream &stream, std::uint64_t startOffset, std::uint64_t streamSize)
Sets the stream and related parameters and clears all available pages.
Definition: oggiterator.cpp:32
std::uint64_t currentSegmentOffset() const
Returns the start offset of the current segment in the input stream if the iterator is valid; otherwi...
Definition: oggiterator.h:202
const OggPage & currentPage() const
Returns the current OGG page.
Definition: oggiterator.h:131
const std::vector< OggPage > & pages() const
Returns a vector of containing the OGG pages that have been fetched yet.
Definition: oggiterator.h:122
bool resyncAt(std::uint64_t offset)
Fetches the next page at the specified offset.
void setPageIndex(std::vector< OggPage >::size_type index)
Sets the current page index.
Definition: oggiterator.h:172
std::vector< OggPage >::size_type currentPageIndex() const
Returns the index of the current page if the iterator is valid; otherwise an undefined index is retur...
Definition: oggiterator.h:163
void reset()
Resets the iterator to point at the first segment of the first page (matching the filter if set).
Definition: oggiterator.cpp:46
void ignore(std::size_t count=1)
Advances the position of the next character to be read from the OGG stream by count bytes.
The OggPage class is used to parse OGG pages.
Definition: oggpage.h:13
std::uint32_t sequenceNumber() const
Returns the page sequence number.
Definition: oggpage.h:174
std::uint32_t dataSize() const
Returns the data size in byte.
Definition: oggpage.h:227
std::uint32_t totalSize() const
Returns the total size of the page in byte.
Definition: oggpage.h:235
std::uint32_t streamSerialNumber() const
Returns the stream serial number.
Definition: oggpage.h:155
const std::vector< std::uint32_t > & segmentSizes() const
Returns the sizes of the segments of the page in byte.
Definition: oggpage.h:209
std::uint8_t headerTypeFlag() const
Returns the header type flag.
Definition: oggpage.h:102
std::uint64_t startOffset() const
Returns the start offset of the page.
Definition: oggpage.h:83
std::uint32_t checksum() const
Returns the page checksum.
Definition: oggpage.h:189
static std::uint32_t computeChecksum(std::istream &stream, std::uint64_t startOffset)
Computes the actual checksum of the page read from the specified stream at the specified startOffset.
Definition: oggpage.cpp:74
static void updateChecksum(std::iostream &stream, std::uint64_t startOffset)
Updates the checksum of the page read from the specified stream at the specified startOffset.
Definition: oggpage.cpp:111
Implementation of TagParser::AbstractTrack for OGG streams.
Definition: oggstream.h:13
std::size_t startPage() const
Definition: oggstream.h:34
Specialization of TagParser::VorbisComment for Vorbis comments inside an OGG stream.
Definition: oggcontainer.h:67
OggParameter & oggParams()
Returns the OGG parameter for the comment.
Definition: oggcontainer.h:113
The exception that is thrown when an operation has been stopped and thus not successfully completed b...
Definition: exceptions.h:46
The TagTarget class specifies the target of a tag.
Definition: tagtarget.h:20
const IdContainerType & tracks() const
Returns the tracks.
Definition: tagtarget.h:104
The Tag class is used to store, read and write tag information.
Definition: tag.h:108
const TagTarget & target() const
Returns the target of tag.
Definition: tag.h:184
The exception that is thrown when the data to be parsed is truncated and therefore can not be parsed ...
Definition: exceptions.h:39
Implementation of TagParser::Tag for Vorbis comments.
Definition: vorbiscomment.h:25
TAG_PARSER_EXPORT void createBackupFile(const std::string &backupDir, const std::string &originalPath, std::string &backupPath, CppUtilities::NativeFileStream &originalStream, CppUtilities::NativeFileStream &backupStream)
TAG_PARSER_EXPORT void handleFailureAfterFileModified(MediaFileInfo &mediaFileInfo, const std::string &backupPath, CppUtilities::NativeFileStream &outputStream, CppUtilities::NativeFileStream &backupStream, Diagnostics &diag, const std::string &context="making file")
constexpr TAG_PARSER_EXPORT std::string_view comment()
Contains all classes and functions of the TagInfo library.
Definition: aaccodebook.h:10
GeneralMediaFormat
The GeneralMediaFormat enum specifies the general format of media data (PCM, MPEG-4,...
Definition: mediaformat.h:30
The OggParameter struct holds the OGG parameter for a VorbisComment.
Definition: oggcontainer.h:27
GeneralMediaFormat streamFormat
Definition: oggcontainer.h:35
std::size_t lastPageIndex
Definition: oggcontainer.h:33
std::size_t lastSegmentIndex
Definition: oggcontainer.h:34
std::size_t firstSegmentIndex
Definition: oggcontainer.h:32
std::size_t firstPageIndex
Definition: oggcontainer.h:31