Tag Parser  7.0.3
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 TagParser {
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,
263  bytesAvailable > numeric_limits<int32>::max() ? numeric_limits<int32>::max() : static_cast<int32>(bytesAvailable));
264  setPageIndex(m_pages.size() - 1);
265  return true;
266  } catch (const Failure &) {
267  stream().seekg(currentOffset);
268  }
269  }
270  FALLTHROUGH;
271  default:
272  lettersFound = 0;
273  }
274  }
275  return false;
276 }
277 
288 bool OggIterator::fetchNextPage()
289 {
290  if (m_page == m_pages.size()) { // can only fetch the next page if the current page is the last page
291  m_offset = m_pages.empty() ? m_startOffset : m_pages.back().startOffset() + m_pages.back().totalSize();
292  if (m_offset < m_streamSize) {
293  const uint64 bytesAvailable = m_streamSize - m_offset;
294  m_pages.emplace_back(*m_stream, m_offset,
295  bytesAvailable > numeric_limits<int32>::max() ? numeric_limits<int32>::max() : static_cast<int32>(bytesAvailable));
296  return true;
297  }
298  }
299  return false;
300 }
301 
302 } // namespace TagParser
uint64 dataOffset(byte segmentIndex=0) const
Returns the data offset of the segment with the specified segmentIndex.
Definition: oggpage.h:249
STL namespace.
const std::vector< uint32 > & segmentSizes() const
Returns the sizes of the segments of the page in byte.
Definition: oggpage.h:210
uint64 startOffset() const
Returns the start offset of the page.
Definition: oggpage.h:84
Contains utility classes helping to read and write streams.
uint32 headerSize() const
Returns the header size in byte.
Definition: oggpage.h:220