Tag Parser  6.4.0
C++ library for reading and writing MP4 (iTunes), ID3, Vorbis, Opus, FLAC and Matroska tags
oggiterator.cpp
Go to the documentation of this file.
1 #include "./oggiterator.h"
2 
3 #include "../exceptions.h"
4 
5 #include <c++utilities/io/binaryreader.h>
6 
7 #include <iostream>
8 #include <limits>
9 
10 using namespace std;
11 using namespace IoUtilities;
12 
13 namespace Media {
14 
32 void OggIterator::clear(istream &stream, uint64 startOffset, uint64 streamSize)
33 {
34  m_stream = &stream;
35  m_startOffset = startOffset;
36  m_streamSize = streamSize;
37  m_pages.clear();
38 }
39 
46 void OggIterator::reset()
47 {
48  for(m_page = m_segment = m_offset = 0; m_page < m_pages.size() || fetchNextPage(); ++m_page) {
49  const OggPage &page = m_pages[m_page];
50  if(!page.segmentSizes().empty() && matchesFilter(page)) {
51  // page is not empty and matches ID filter if set
52  m_offset = page.startOffset() + page.headerSize();
53  break;
54  }
55  }
56  // no matching page found -> iterator is invalid
57 }
58 
63 void OggIterator::nextPage()
64 {
65  while(++m_page < m_pages.size() || fetchNextPage()) {
66  const OggPage &page = m_pages[m_page];
67  if(!page.segmentSizes().empty() && matchesFilter(page)) {
68  // page is not empty and matches ID filter if set
69  m_segment = m_bytesRead = 0;
70  m_offset = page.startOffset() + page.headerSize();
71  return;
72  }
73  }
74  // no next page available -> iterator is in invalid state
75 }
76 
81 void OggIterator::nextSegment()
82 {
83  const OggPage &page = m_pages[m_page];
84  if(matchesFilter(page) && ++m_segment < page.segmentSizes().size()) {
85  // current page has next segment
86  m_bytesRead = 0;
87  m_offset += page.segmentSizes()[m_segment - 1];
88  } else {
89  // next (matching) page has next segment
90  nextPage();
91  }
92 }
93 
98 void OggIterator::previousPage()
99 {
100  while(m_page) {
101  const OggPage &page = m_pages[--m_page];
102  if(matchesFilter(page)) {
103  m_offset = page.dataOffset(m_segment = page.segmentSizes().size() - 1);
104  return;
105  }
106  }
107 }
108 
113 void OggIterator::previousSegment()
114 {
115  const OggPage &page = m_pages[m_page];
116  if(m_segment && matchesFilter(page)) {
117  m_offset -= page.segmentSizes()[m_segment--];
118  } else {
119  previousPage();
120  }
121 }
122 
134 void OggIterator::read(char *buffer, size_t count)
135 {
136  size_t bytesRead = 0;
137  while(*this && count) {
138  const uint32 available = currentSegmentSize() - m_bytesRead;
139  stream().seekg(currentCharacterOffset());
140  if(count <= available) {
141  stream().read(buffer + bytesRead, count);
142  m_bytesRead += count;
143  return;
144  } else {
145  stream().read(buffer + bytesRead, available);
146  nextSegment();
147  bytesRead += available;
148  count -= available;
149  }
150  }
151  if(count) {
152  // still bytes to read but no more available
153  throw TruncatedDataException();
154  }
155 }
156 
169 size_t OggIterator::readAll(char *buffer, size_t max)
170 {
171  size_t bytesRead = 0;
172  while(*this && max) {
173  const uint32 available = currentSegmentSize() - m_bytesRead;
174  stream().seekg(currentCharacterOffset());
175  if(max <= available) {
176  stream().read(buffer + bytesRead, max);
177  m_bytesRead += max;
178  return bytesRead + max;
179  } else {
180  stream().read(buffer + bytesRead, available);
181  nextSegment();
182  bytesRead += available;
183  max -= available;
184  }
185  }
186  return bytesRead;
187 }
188 
199 void OggIterator::ignore(size_t count)
200 {
201  uint32 available = currentSegmentSize() - m_bytesRead;
202  while(*this) {
203  available = currentSegmentSize() - m_bytesRead;
204  if(count <= available) {
205  m_bytesRead += count;
206  return;
207  } else {
208  nextSegment();
209  count -= available;
210  }
211  }
212  throw TruncatedDataException();
213 }
214 
238 bool OggIterator::resyncAt(uint64 offset)
239 {
240  // check whether offset is valid
241  if(offset >= streamSize() || offset < (m_pages.empty() ? m_startOffset : m_pages.back().startOffset() + m_pages.back().totalSize())) {
242  return false;
243  }
244 
245  // find capture pattern 'OggS'
246  stream().seekg(offset);
247  byte lettersFound = 0;
248  for(uint64 bytesAvailable = max<uint64>(streamSize() - offset, 65307ul); bytesAvailable >= 27; --bytesAvailable) {
249  switch(static_cast<char>(stream().get())) {
250  case 'O':
251  lettersFound = 1;
252  break;
253  case 'g':
254  lettersFound = lettersFound == 1 || lettersFound == 2 ? lettersFound + 1 : 0;
255  break;
256  case 'S':
257  if(lettersFound == 3) {
258  // capture pattern found
259  const auto currentOffset = stream().tellg();
260  // -> try to parse an OGG page at this position
261  try {
262  m_pages.emplace_back(stream(), static_cast<uint64>(stream().tellg()) - 4, bytesAvailable > numeric_limits<int32>::max() ? numeric_limits<int32>::max() : static_cast<int32>(bytesAvailable));
263  setPageIndex(m_pages.size() - 1);
264  return true;
265  } catch (const Failure &) {
266  stream().seekg(currentOffset);
267  }
268  }
269  FALLTHROUGH;
270  default:
271  lettersFound = 0;
272  }
273  }
274  return false;
275 }
276 
287 bool OggIterator::fetchNextPage()
288 {
289  if(m_page == m_pages.size()) { // can only fetch the next page if the current page is the last page
290  m_offset = m_pages.empty() ? m_startOffset : m_pages.back().startOffset() + m_pages.back().totalSize();
291  if(m_offset < m_streamSize) {
292  const uint64 bytesAvailable = m_streamSize - m_offset;
293  m_pages.emplace_back(*m_stream, m_offset, bytesAvailable > numeric_limits<int32>::max() ? numeric_limits<int32>::max() : static_cast<int32>(bytesAvailable));
294  return true;
295  }
296  }
297  return false;
298 }
299 
300 }
uint64 dataOffset(byte segmentIndex=0) const
Returns the data offset of the segment with the specified segmentIndex.
Definition: oggpage.h:249
uint32 headerSize() const
Returns the header size in byte.
Definition: oggpage.h:220
STL namespace.
The OggPage class is used to parse OGG pages.
Definition: oggpage.h:14
Contains utility classes helping to read and write streams.
The class inherits from std::exception and serves as base class for exceptions thrown by the elements...
Definition: exceptions.h:11
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
Contains all classes and functions of the TagInfo library.
Definition: exceptions.h:9
const std::vector< uint32 > & segmentSizes() const
Returns the sizes of the segments of the page in byte.
Definition: oggpage.h:210