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