Tag Parser  6.5.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 
2 #include "./oggcontainer.h"
3 
4 #include "../flac/flacmetadata.h"
5 
6 #include "../mediafileinfo.h"
7 #include "../backuphelper.h"
8 
9 #include <c++utilities/conversion/stringbuilder.h>
10 #include <c++utilities/io/copy.h>
11 #include <c++utilities/io/catchiofailure.h>
12 
13 #include <memory>
14 
15 using namespace std;
16 using namespace IoUtilities;
17 using namespace ConversionUtilities;
18 
19 namespace Media {
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 
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(), static_cast<size_t>(-1), false, track->format().general);
108  m_tags.back()->setTarget(target);
109  return m_tags.back().get();
110  } else {
111  // TODO: error handling?
112  }
113  default:
114  ;
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  static const string context("parsing OGG bitstream header");
189  bool pagesSkipped = false;
190 
191  // iterate through pages using OggIterator helper class
192  try {
193  // ensure iterator is setup properly
194  for(m_iterator.removeFilter(), m_iterator.reset(); m_iterator; m_iterator.nextPage()) {
195  const OggPage &page = m_iterator.currentPage();
196  if(m_validateChecksums && page.checksum() != OggPage::computeChecksum(stream(), page.startOffset())) {
197  addNotification(NotificationType::Warning, argsToString("The denoted checksum of the OGG page at ", m_iterator.currentSegmentOffset(), " does not match the computed checksum."), context);
198  }
199  OggStream *stream;
200  uint64 lastNewStreamOffset = 0;
201  try {
202  stream = m_tracks[m_streamsBySerialNo.at(page.streamSerialNumber())].get();
203  stream->m_size += page.dataSize();
204  } catch(const out_of_range &) {
205  // new stream serial number recognized -> add new stream
206  m_streamsBySerialNo[page.streamSerialNumber()] = m_tracks.size();
207  m_tracks.emplace_back(make_unique<OggStream>(*this, m_iterator.currentPageIndex()));
208  stream = m_tracks.back().get();
209  lastNewStreamOffset = page.startOffset();
210  }
211  if(stream->m_currentSequenceNumber != page.sequenceNumber()) {
212  if(stream->m_currentSequenceNumber) {
213  addNotification(NotificationType::Warning, "Page is missing (page sequence number omitted).", context);
214  }
215  stream->m_currentSequenceNumber = page.sequenceNumber() + 1;
216  } else {
217  ++stream->m_currentSequenceNumber;
218  }
219 
220  // 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
221  if(!fileInfo().isForcingFullParse()
222  && (fileInfo().size() - page.startOffset()) > (100 * 0x100000)
223  && (page.startOffset() - lastNewStreamOffset) > (20 * 0x100000)) {
224  if(m_iterator.resyncAt(fileInfo().size() - (20 * 0x100000))) {
225  const OggPage &resyncedPage = m_iterator.currentPage();
226  // prevent warning about missing pages
227  stream->m_currentSequenceNumber = resyncedPage.sequenceNumber() + 1;
228  pagesSkipped = true;
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."),
231  context);
232  } else {
233  // abort if skipping pages didn't work
234  addNotification(NotificationType::Critical, "Unable to re-sync after skipping OGG pages in the middle of the file. Try forcing a full parse.", context);
235  return;
236  }
237  }
238  }
239  } catch(const TruncatedDataException &) {
240  // thrown when page exceeds max size
241  addNotification(NotificationType::Critical, "The OGG file is truncated.", context);
242  } catch(const InvalidDataException &) {
243  // thrown when first 4 byte do not match capture pattern
244  addNotification(NotificationType::Critical, argsToString("Capture pattern \"OggS\" at ", m_iterator.currentSegmentOffset(), " expected."), context);
245  }
246 
247  // invalidate stream sizes in case pages have been skipped
248  if(pagesSkipped) {
249  for(auto &stream : m_tracks) {
250  stream->m_size = 0;
251  }
252  }
253 }
254 
256 {
257  // tracks needs to be parsed before because tags are stored at stream level
258  parseTracks();
259  for(auto &comment : m_tags) {
260  OggParameter &params = comment->oggParams();
261  m_iterator.setPageIndex(params.firstPageIndex);
262  m_iterator.setSegmentIndex(params.firstSegmentIndex);
263  switch(params.streamFormat) {
265  comment->parse(m_iterator);
266  break;
268  // skip header (has already been detected by OggStream)
269  m_iterator.ignore(8);
271  break;
273  m_iterator.ignore(4);
275  break;
276  default:
277  addNotification(NotificationType::Critical, "Stream format not supported.", "parsing tags from OGG streams");
278  }
279  params.lastPageIndex = m_iterator.currentPageIndex();
280  params.lastSegmentIndex = m_iterator.currentSegmentIndex();
281  }
282 }
283 
295 void OggContainer::announceComment(std::size_t pageIndex, std::size_t segmentIndex, bool lastMetaDataBlock, GeneralMediaFormat mediaFormat)
296 {
297  m_tags.emplace_back(make_unique<OggVorbisComment>());
298  m_tags.back()->oggParams().set(pageIndex, segmentIndex, lastMetaDataBlock, mediaFormat);
299 }
300 
302 {
303  static const string context("parsing OGG stream");
304  for(auto &stream : m_tracks) {
305  try { // try to parse header
306  stream->parseHeader();
307  if(stream->duration() > m_duration) {
308  m_duration = stream->duration();
309  }
310  } catch(const Failure &) {
311  addNotification(NotificationType::Critical, argsToString("Unable to parse stream at ", stream->startOffset(), '.'), context);
312  }
313  }
314 }
315 
320 void OggContainer::makeVorbisCommentSegment(stringstream &buffer, CopyHelper<65307> &copyHelper, vector<uint32> &newSegmentSizes, VorbisComment *comment, OggParameter *params)
321 {
322  const auto offset = buffer.tellp();
323  switch(params->streamFormat) {
325  comment->make(buffer);
326  break;
328  ConversionUtilities::BE::getBytes(static_cast<uint64>(0x4F70757354616773u), copyHelper.buffer());
329  buffer.write(copyHelper.buffer(), 8);
331  break;
333  // Vorbis comment must be wrapped in "METADATA_BLOCK_HEADER"
335  header.setLast(params->lastMetaDataBlock);
337 
338  // write the header later, when the size is known
339  buffer.write(copyHelper.buffer(), 4);
340 
342 
343  // finally make the header
344  header.setDataSize(buffer.tellp() - offset - 4);
345  if(header.dataSize() > 0xFFFFFF) {
346  addNotification(NotificationType::Critical, "Size of Vorbis comment exceeds size limit for FLAC \"METADATA_BLOCK_HEADER\".", "making Vorbis Comment");
347  }
348  buffer.seekp(offset);
349  header.makeHeader(buffer);
350  buffer.seekp(header.dataSize(), ios_base::cur);
351  break;
352  } default:
353  ;
354  }
355  newSegmentSizes.push_back(buffer.tellp() - offset);
356 }
357 
359 {
360  const string context("making OGG file");
361  updateStatus("Prepare for rewriting OGG file ...");
362  parseTags(); // tags need to be parsed before the file can be rewritten
363  string backupPath;
364  NativeFileStream backupStream;
365 
366  if(fileInfo().saveFilePath().empty()) {
367  // move current file to temp dir and reopen it as backupStream, recreate original file
368  try {
369  BackupHelper::createBackupFile(fileInfo().path(), backupPath, fileInfo().stream(), backupStream);
370  // recreate original file, define buffer variables
371  fileInfo().stream().open(fileInfo().path(), ios_base::out | ios_base::binary | ios_base::trunc);
372  } catch(...) {
373  const char *what = catchIoFailure();
374  addNotification(NotificationType::Critical, "Creation of temporary file (to rewrite the original file) failed.", context);
375  throwIoFailure(what);
376  }
377  } else {
378  // open the current file as backupStream and create a new outputStream at the specified "save file path"
379  try {
380  backupStream.exceptions(ios_base::badbit | ios_base::failbit);
381  backupStream.open(fileInfo().path(), ios_base::in | ios_base::binary);
382  fileInfo().close();
383  fileInfo().stream().open(fileInfo().saveFilePath(), ios_base::out | ios_base::binary | ios_base::trunc);
384  } catch(...) {
385  const char *what = catchIoFailure();
386  addNotification(NotificationType::Critical, "Opening streams to write output file failed.", context);
387  throwIoFailure(what);
388  }
389  }
390 
391  try {
392  // prepare iterating comments
393  OggVorbisComment *currentComment;
394  OggParameter *currentParams;
395  auto tagIterator = m_tags.cbegin(), tagEnd = m_tags.cend();
396  if(tagIterator != tagEnd) {
397  currentParams = &(currentComment = tagIterator->get())->oggParams();
398  } else {
399  currentComment = nullptr;
400  currentParams = nullptr;
401  }
402 
403  // define misc variables
404  CopyHelper<65307> copyHelper;
405  vector<uint64> updatedPageOffsets;
406  unordered_map<uint32, uint32> pageSequenceNumberBySerialNo;
407 
408  // iterate through all pages of the original file
409  for(m_iterator.setStream(backupStream), m_iterator.removeFilter(), m_iterator.reset(); m_iterator; m_iterator.nextPage()) {
410  const OggPage &currentPage = m_iterator.currentPage();
411  const auto pageSize = currentPage.totalSize();
412  uint32 &pageSequenceNumber = pageSequenceNumberBySerialNo[currentPage.streamSerialNumber()];
413  // check whether the Vorbis Comment is present in this Ogg page
414  if(currentComment
415  && m_iterator.currentPageIndex() >= currentParams->firstPageIndex
416  && m_iterator.currentPageIndex() <= currentParams->lastPageIndex
417  && !currentPage.segmentSizes().empty()) {
418  // page needs to be rewritten (not just copied)
419  // -> write segments to a buffer first
420  stringstream buffer(ios_base::in | ios_base::out | ios_base::binary);
421  vector<uint32> newSegmentSizes;
422  newSegmentSizes.reserve(currentPage.segmentSizes().size());
423  uint64 segmentOffset = m_iterator.currentSegmentOffset();
424  vector<uint32>::size_type segmentIndex = 0;
425  for(const auto segmentSize : currentPage.segmentSizes()) {
426  if(segmentSize) {
427  // check whether this segment contains the Vorbis Comment
428  if((m_iterator.currentPageIndex() >= currentParams->firstPageIndex && segmentIndex >= currentParams->firstSegmentIndex)
429  && (m_iterator.currentPageIndex() <= currentParams->lastPageIndex && segmentIndex <= currentParams->lastSegmentIndex)) {
430  // prevent making the comment twice if it spreads over multiple pages/segments
431  if(!currentParams->removed
432  && ((m_iterator.currentPageIndex() == currentParams->firstPageIndex
433  && m_iterator.currentSegmentIndex() == currentParams->firstSegmentIndex))) {
434  makeVorbisCommentSegment(buffer, copyHelper, newSegmentSizes, currentComment, currentParams);
435  }
436 
437  // proceed with next comment?
438  if(m_iterator.currentPageIndex() > currentParams->lastPageIndex
439  || (m_iterator.currentPageIndex() == currentParams->lastPageIndex && segmentIndex > currentParams->lastSegmentIndex)) {
440  if(++tagIterator != tagEnd) {
441  currentParams = &(currentComment = tagIterator->get())->oggParams();
442  } else {
443  currentComment = nullptr;
444  currentParams = nullptr;
445  }
446  }
447  } else {
448  // copy other segments unchanged
449  backupStream.seekg(segmentOffset);
450  copyHelper.copy(backupStream, buffer, segmentSize);
451  newSegmentSizes.push_back(segmentSize);
452 
453  // check whether there is a new comment to be inserted into the current page
454  if(m_iterator.currentPageIndex() == currentParams->lastPageIndex && currentParams->firstSegmentIndex == static_cast<size_t>(-1)) {
455  if(!currentParams->removed) {
456  makeVorbisCommentSegment(buffer, copyHelper, newSegmentSizes, currentComment, currentParams);
457  }
458  // proceed with next comment
459  if(++tagIterator != tagEnd) {
460  currentParams = &(currentComment = tagIterator->get())->oggParams();
461  } else {
462  currentComment = nullptr;
463  currentParams = nullptr;
464  }
465  }
466  }
467  segmentOffset += segmentSize;
468  }
469  ++segmentIndex;
470  }
471 
472  // write buffered data to actual stream
473  auto newSegmentSizesIterator = newSegmentSizes.cbegin(), newSegmentSizesEnd = newSegmentSizes.cend();
474  bool continuePreviousSegment = false;
475  if(newSegmentSizesIterator != newSegmentSizesEnd) {
476  uint32 bytesLeft = *newSegmentSizesIterator;
477  // write pages until all data in the buffer is written
478  while(newSegmentSizesIterator != newSegmentSizesEnd) {
479  // write header
480  backupStream.seekg(currentPage.startOffset());
481  updatedPageOffsets.push_back(stream().tellp()); // memorize offset to update checksum later
482  copyHelper.copy(backupStream, stream(), 27); // just copy header from original file
483  // set continue flag
484  stream().seekp(-22, ios_base::cur);
485  stream().put(currentPage.headerTypeFlag() & (continuePreviousSegment ? 0xFF : 0xFE));
486  continuePreviousSegment = true;
487  // adjust page sequence number
488  stream().seekp(12, ios_base::cur);
489  writer().writeUInt32LE(pageSequenceNumber);
490  stream().seekp(5, ios_base::cur);
491  int16 segmentSizesWritten = 0; // in the current page header only
492  // write segment sizes as long as there are segment sizes to be written and
493  // the max number of segment sizes (255) is not exceeded
494  uint32 currentSize = 0;
495  while(bytesLeft && segmentSizesWritten < 0xFF) {
496  while(bytesLeft >= 0xFF && segmentSizesWritten < 0xFF) {
497  stream().put(0xFF);
498  currentSize += 0xFF;
499  bytesLeft -= 0xFF;
500  ++segmentSizesWritten;
501  }
502  if(bytesLeft && segmentSizesWritten < 0xFF) {
503  // bytes left is here < 0xFF
504  stream().put(bytesLeft);
505  currentSize += bytesLeft;
506  bytesLeft = 0;
507  ++segmentSizesWritten;
508  }
509  if(!bytesLeft) {
510  // sizes for the segment have been written
511  // -> continue with next segment
512  if(++newSegmentSizesIterator != newSegmentSizesEnd) {
513  bytesLeft = *newSegmentSizesIterator;
514  continuePreviousSegment = false;
515  }
516  }
517  }
518 
519  // there are no bytes left in the current segment; remove continue flag
520  if(!bytesLeft) {
521  continuePreviousSegment = false;
522  }
523 
524  // page is full or all segment data has been covered
525  // -> write segment table size (segmentSizesWritten) and segment data
526  // -> seek back and write updated page segment number
527  stream().seekp(-1 - segmentSizesWritten, ios_base::cur);
528  stream().put(segmentSizesWritten);
529  stream().seekp(segmentSizesWritten, ios_base::cur);
530  // -> write actual page data
531  copyHelper.copy(buffer, stream(), currentSize);
532 
533  ++pageSequenceNumber;
534  }
535  }
536 
537  } else {
538  if(pageSequenceNumber != m_iterator.currentPageIndex()) {
539  // just update page sequence number
540  backupStream.seekg(currentPage.startOffset());
541  updatedPageOffsets.push_back(stream().tellp()); // memorize offset to update checksum later
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);
547  } else {
548  // copy page unchanged
549  backupStream.seekg(currentPage.startOffset());
550  copyHelper.copy(backupStream, stream(), pageSize);
551  }
552  ++pageSequenceNumber;
553  }
554  }
555 
556  // report new size
557  fileInfo().reportSizeChanged(stream().tellp());
558 
559  // "save as path" is now the regular path
560  if(!fileInfo().saveFilePath().empty()) {
561  fileInfo().reportPathChanged(fileInfo().saveFilePath());
562  fileInfo().setSaveFilePath(string());
563  }
564 
565  // close backups stream; reopen new file as readable stream
566  backupStream.close();
567  fileInfo().close();
568  fileInfo().stream().open(fileInfo().path(), ios_base::in | ios_base::out | ios_base::binary);
569 
570  // update checksums of modified pages
571  for(auto offset : updatedPageOffsets) {
573  }
574 
575  // clear iterator
576  m_iterator.clear(fileInfo().stream(), startOffset(), fileInfo().size());
577 
578  } catch(...) {
579  m_iterator.setStream(fileInfo().stream());
580  BackupHelper::handleFailureAfterFileModified(fileInfo(), backupPath, fileInfo().stream(), backupStream, context);
581  }
582 }
583 
584 }
IoUtilities::BinaryWriter & writer()
Returns the related BinaryWriter.
OggVorbisComment * createTag(const TagTarget &target)
Creates a new tag.
GeneralMediaFormat
The GeneralMediaFormat enum specifies the general format of media data (PCM, MPEG-4, PNG, ...).
Definition: mediaformat.h:29
const TagTarget & target() const
Returns the target of tag.
Definition: tag.h:245
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
Implementation of Media::Tag for Vorbis comments.
Definition: vorbiscomment.h:15
ChronoUtilities::TimeSpan m_duration
MediaFileInfo & fileInfo() const
Returns the related file info.
void internalParseTags()
Internally called to parse the tags.
uint32 checksum() const
Returns the page checksum.
Definition: oggpage.h:190
void removeFilter()
Removes a previously set filter.
Definition: oggiterator.h:252
void internalParseTracks()
Internally called to parse the tracks.
OggParameter & oggParams()
Returns the OGG parameter for the comment.
Definition: oggcontainer.h:111
The FlacMetaDataBlockHeader class is a FLAC "METADATA_BLOCK_HEADER" parser and maker.
Definition: flacmetadata.h:38
The GenericContainer class helps parsing header, track, tag and chapter information of a file...
uint32 sequenceNumber() const
Returns the page sequence number.
Definition: oggpage.h:175
TAG_PARSER_EXPORT void createBackupFile(const std::string &originalPath, std::string &backupPath, IoUtilities::NativeFileStream &originalStream, IoUtilities::NativeFileStream &backupStream)
GeneralMediaFormat general
Definition: mediaformat.h:274
const std::vector< OggPage > & pages() const
Returns a vector of containing the OGG pages that have been fetched yet.
Definition: oggiterator.h:122
void setPageIndex(std::vector< OggPage >::size_type index)
Sets the current page index.
Definition: oggiterator.h:172
Specialization of Media::VorbisComment for Vorbis comments inside an OGG stream.
Definition: oggcontainer.h:68
TAG_PARSER_EXPORT void handleFailureAfterFileModified(MediaFileInfo &mediaFileInfo, const std::string &backupPath, IoUtilities::NativeFileStream &outputStream, IoUtilities::NativeFileStream &backupStream, const std::string &context="making file")
STL namespace.
void ignore(std::size_t count=1)
Advances the position of the next character to be read from the OGG stream by count bytes...
void addNotification(const Notification &notification)
This method is meant to be called by the derived class to add a notification.
void reportPathChanged(const std::string &newPath)
Call this function to report that the path changed.
uint32 streamSerialNumber() const
Returns the stream serial number.
Definition: oggpage.h:156
uint32 totalSize() const
Returns the total size of the page in byte.
Definition: oggpage.h:236
void internalMakeFile()
Internally called to make the file.
std::size_t startPage() const
Definition: oggstream.h:35
MediaFormat format() const
Returns the format of the track if known; otherwise returns MediaFormat::Unknown. ...
The OggPage class is used to parse OGG pages.
Definition: oggpage.h:14
uint64 id() const
Returns the track ID if known; otherwise returns 0.
const IdContainerType & tracks() const
Returns the tracks.
Definition: tagtarget.h:113
std::size_t lastSegmentIndex
Definition: oggcontainer.h:36
void setSegmentIndex(std::vector< uint32 >::size_type index)
Sets the current segment index.
Definition: oggiterator.h:184
void close()
A possibly opened std::fstream will be closed.
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:109
IoUtilities::NativeFileStream & stream()
Returns the std::fstream for the current instance.
Definition: basicfileinfo.h:80
void setSaveFilePath(const std::string &saveFilePath)
Sets the "save file path".
GeneralMediaFormat streamFormat
Definition: oggcontainer.h:38
Contains utility classes helping to read and write streams.
uint64 startOffset() const
Returns the start offset in the related stream.
bool removeTag(Tag *tag)
Actually just flags the specified tag as removed and clears all assigned fields.
void makeHeader(std::ostream &outputStream)
Writes the header to the specified outputStream.
void parseTags()
Parses the tag information if not parsed yet.
The exception that is thrown when the data to be parsed or to be made seems invalid and therefore can...
Definition: exceptions.h:27
std::size_t firstPageIndex
Definition: oggcontainer.h:33
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
Implementation of Media::AbstractTrack for OGG streams.
Definition: oggstream.h:13
void setStream(std::istream &stream)
Sets the stream.
Definition: oggiterator.h:98
void reset()
Discards all parsing results.
The Tag class is used to store, read and write tag information.
Definition: tag.h:98
byte headerTypeFlag() const
Returns the header type flag.
Definition: oggpage.h:103
The class inherits from std::exception and serves as base class for exceptions thrown by the elements...
Definition: exceptions.h:11
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
void reportSizeChanged(uint64 newSize)
Call this function to report that the size changed.
void make(std::ostream &stream, VorbisCommentFlags flags=VorbisCommentFlags::None)
Writes tag information to the specified stream.
void reset()
Resets the iterator to point at the first segment of the first page (matching the filter if set)...
Definition: oggiterator.cpp:46
uint64 startOffset() const
Returns the start offset of the page.
Definition: oggpage.h:84
The exception that is thrown when the data to be parsed is truncated and therefore can not be parsed ...
Definition: exceptions.h:35
The MediaFileInfo class allows to read and write tag information providing a container/tag format ind...
Definition: mediafileinfo.h:53
void internalParseHeader()
Internally called to parse the header.
OggVorbisComment * tag(std::size_t index)
Returns the tag with the specified index.
void setLast(byte last)
Sets whether this is the last metadata block before the audio blocks.
Definition: flacmetadata.h:80
const OggPage & currentPage() const
Returns the current OGG page.
Definition: oggiterator.h:131
void nextPage()
Increases the current position by one page.
Definition: oggiterator.cpp:63
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
void removeAllTags()
Actually just flags all tags as removed and clears all assigned fields.
void updateStatus(const std::string &status)
This method is meant to be called by the derived class to report updated status information.
std::size_t firstSegmentIndex
Definition: oggcontainer.h:34
The TagTarget class specifies the target of a tag.
Definition: tagtarget.h:31
Contains all classes and functions of the TagInfo library.
Definition: exceptions.h:9
The OggParameter struct holds the OGG parameter for a VorbisComment.
Definition: oggcontainer.h:28
void clear(std::istream &stream, uint64 startOffset, uint64 streamSize)
Sets the stream and related parameters and clears all available pages.
Definition: oggiterator.cpp:32
uint32 dataSize() const
Returns the length in bytes of the meta data (excluding the size of the header itself).
Definition: flacmetadata.h:105
std::iostream & stream()
Returns the related stream.
bool resyncAt(uint64 offset)
Fetches the next page at the specified offset.
uint32 dataSize() const
Returns the data size in byte.
Definition: oggpage.h:228
std::size_t lastPageIndex
Definition: oggcontainer.h:35
std::size_t tagCount() const
Returns the number of tags attached to the container.
void parseTracks()
Parses the tracks of the file if not parsed yet.
TAG_PARSER_EXPORT const char * comment()
const std::vector< uint32 > & segmentSizes() const
Returns the sizes of the segments of the page in byte.
Definition: oggpage.h:210
void setType(FlacMetaDataBlockType type)
Sets the block type.
Definition: flacmetadata.h:97
void setDataSize(uint32 dataSize)
Sets the length in bytes of the meta data (excluding the size of the header itself).
Definition: flacmetadata.h:114