Tag Parser  8.0.0
C++ library for reading and writing MP4 (iTunes), ID3, Vorbis, Opus, FLAC and Matroska tags
matroskaseekinfo.cpp
Go to the documentation of this file.
1 #include "./matroskaseekinfo.h"
2 #include "./matroskaid.h"
3 
4 #include "../diagnostics.h"
5 #include "../exceptions.h"
6 
7 #include <c++utilities/conversion/binaryconversion.h>
8 #include <c++utilities/conversion/stringbuilder.h>
9 
10 #include <string>
11 
12 using namespace std;
13 using namespace ConversionUtilities;
14 
15 namespace TagParser {
16 
25 void MatroskaSeekInfo::shift(uint64 start, int64 amount)
26 {
27  for (auto &info : m_info) {
28  if (get<1>(info) >= start) {
29  if (amount > 0) {
30  get<1>(info) += static_cast<uint64>(amount);
31  } else {
32  get<1>(info) -= static_cast<uint64>(-amount);
33  }
34  }
35  }
36 }
37 
44 void MatroskaSeekInfo::parse(EbmlElement *seekHeadElement, Diagnostics &diag)
45 {
46  static const string context("parsing \"SeekHead\"-element");
47  m_seekHeadElement = seekHeadElement;
48  m_info.clear();
49  EbmlElement *seekElement = seekHeadElement->firstChild();
50  EbmlElement *seekElementChild, *seekIdElement, *seekPositionElement;
51  while (seekElement) {
52  seekElement->parse(diag);
53  switch (seekElement->id()) {
54  case MatroskaIds::Seek:
55  seekElementChild = seekElement->firstChild();
56  seekIdElement = seekPositionElement = nullptr;
57  while (seekElementChild) {
58  seekElementChild->parse(diag);
59  switch (seekElementChild->id()) {
61  if (seekIdElement) {
62  diag.emplace_back(DiagLevel::Warning,
63  "The \"Seek\"-element contains multiple \"SeekID\"-elements. Surplus elements will be ignored.", context);
64  }
65  seekIdElement = seekElementChild;
66  break;
68  if (seekPositionElement) {
69  diag.emplace_back(DiagLevel::Warning,
70  "The \"Seek\"-element contains multiple \"SeekPosition\"-elements. Surplus elements will be ignored.", context);
71  }
72  seekPositionElement = seekElementChild;
73  break;
74  case EbmlIds::Crc32:
75  case EbmlIds::Void:
76  break;
77  default:
78  diag.emplace_back(DiagLevel::Warning,
79  "The element \"" % seekElementChild->idToString()
80  + "\" within the \"Seek\" element is not a \"SeekID\"-element nor a \"SeekPosition\"-element and will be ignored.",
81  context);
82  }
83  seekElementChild = seekElementChild->nextSibling();
84  }
85  if (seekIdElement && seekPositionElement) {
86  m_info.emplace_back(seekIdElement->readUInteger(), seekPositionElement->readUInteger());
87  } else {
88  diag.emplace_back(DiagLevel::Warning, "The \"Seek\"-element does not contain a \"SeekID\"- and a \"SeekPosition\"-element.", context);
89  }
90  break;
91  case EbmlIds::Crc32:
92  case EbmlIds::Void:
93  break;
94  default:
95  diag.emplace_back(
96  DiagLevel::Warning, "The element " % seekElement->idToString() + " is not a seek element and will be ignored.", context);
97  }
98  seekElement = seekElement->nextSibling();
99  }
100  if (m_info.empty()) {
101  diag.emplace_back(DiagLevel::Warning, "No seek information found.", context);
102  }
103 }
104 
111 void MatroskaSeekInfo::make(ostream &stream, Diagnostics &diag)
112 {
113  VAR_UNUSED(diag)
114 
115  uint64 totalSize = 0;
116  char buff0[8];
117  char buff1[8];
118  char buff2[2];
119  byte sizeLength0, sizeLength1;
120  // calculate size
121  for (const auto &info : m_info) {
122  // "Seek" element + "SeekID" element + "SeekPosition" element
123  totalSize += 2 + 1 + (2 + 1 + EbmlElement::calculateIdLength(get<0>(info))) + (2 + 1 + EbmlElement::calculateUIntegerLength(get<1>(info)));
124  }
125  // write ID and size
126  BE::getBytes(static_cast<uint32>(MatroskaIds::SeekHead), buff0);
127  stream.write(buff0, 4);
128  sizeLength0 = EbmlElement::makeSizeDenotation(totalSize, buff0);
129  stream.write(buff0, sizeLength0);
130  // write entries
131  for (const auto &info : m_info) {
132  // make values
133  sizeLength0 = EbmlElement::makeId(get<0>(info), buff0);
134  sizeLength1 = EbmlElement::makeUInteger(get<1>(info), buff1);
135  // "Seek" header
136  BE::getBytes(static_cast<uint16>(MatroskaIds::Seek), buff2);
137  stream.write(buff2, 2);
138  stream.put(static_cast<char>(0x80 | (2 + 1 + sizeLength0 + 2 + 1 + sizeLength1)));
139  // "SeekID" element
140  BE::getBytes(static_cast<uint16>(MatroskaIds::SeekID), buff2);
141  stream.write(buff2, 2);
142  stream.put(static_cast<char>(0x80 | sizeLength0));
143  stream.write(buff0, sizeLength0);
144  // "SeekPosition" element
145  BE::getBytes(static_cast<uint16>(MatroskaIds::SeekPosition), buff2);
146  stream.write(buff2, 2);
147  stream.put(static_cast<char>(0x80 | sizeLength1));
148  stream.write(buff1, sizeLength1);
149  }
150 }
151 
156 uint64 MatroskaSeekInfo::minSize() const
157 {
158  uint64 maxTotalSize = m_info.size() * (2 + 1 + 2 + 1 + 1 + 2 + 1 + 1);
159  return 4 + EbmlElement::calculateSizeDenotationLength(maxTotalSize) + maxTotalSize;
160 }
161 
166 uint64 MatroskaSeekInfo::maxSize() const
167 {
168  uint64 maxTotalSize = m_info.size() * (2 + 1 + 2 + 1 + 4 + 2 + 1 + 8);
169  return 4 + EbmlElement::calculateSizeDenotationLength(maxTotalSize) + maxTotalSize;
170 }
171 
176 uint64 MatroskaSeekInfo::actualSize() const
177 {
178  uint64 totalSize = 0;
179  for (const auto &info : m_info) {
180  // "Seek" element + "SeekID" element + "SeekPosition" element
181  totalSize += 2 + 1 + (2 + 1 + EbmlElement::calculateIdLength(get<0>(info))) + (2 + 1 + EbmlElement::calculateUIntegerLength(get<1>(info)));
182  }
183  return totalSize += 4 + EbmlElement::calculateSizeDenotationLength(totalSize);
184 }
185 
194 bool MatroskaSeekInfo::push(unsigned int index, EbmlElement::IdentifierType id, uint64 offset)
195 {
196  unsigned int currentIndex = 0;
197  for (auto &entry : info()) {
198  if (get<0>(entry) == id) {
199  if (index == currentIndex) {
200  bool sizeUpdated = EbmlElement::calculateUIntegerLength(get<1>(entry)) != EbmlElement::calculateUIntegerLength(offset);
201  get<1>(entry) = offset;
202  return sizeUpdated;
203  }
204  ++currentIndex;
205  }
206  }
207  info().emplace_back(id, offset);
208  return true;
209 }
210 
214 void MatroskaSeekInfo::clear()
215 {
216  m_seekHeadElement = nullptr;
217  m_info.clear();
218 }
219 
223 std::pair<EbmlElement::IdentifierType, uint64> *MatroskaSeekInfo::findSeekInfo(std::vector<MatroskaSeekInfo> &seekInfos, uint64 offset)
224 {
225  for (auto &seekInfo : seekInfos) {
226  for (auto &entry : seekInfo.info()) {
227  if (get<1>(entry) == offset) {
228  return &entry;
229  }
230  }
231  }
232  return nullptr;
233 }
234 
239 bool MatroskaSeekInfo::updateSeekInfo(
240  const std::vector<MatroskaSeekInfo> &oldSeekInfos, std::vector<MatroskaSeekInfo> &newSeekInfos, uint64 oldOffset, uint64 newOffset)
241 {
242  bool updated = false;
243  auto oldIterator0 = oldSeekInfos.cbegin(), oldEnd0 = oldSeekInfos.cend();
244  auto newIterator0 = newSeekInfos.begin(), newEnd0 = newSeekInfos.end();
245  for (; oldIterator0 != oldEnd0 && newIterator0 != newEnd0; ++oldIterator0, ++newIterator0) {
246  auto oldIterator1 = oldIterator0->info().cbegin(), oldEnd1 = oldIterator0->info().cend();
247  auto newIterator1 = newIterator0->info().begin(), newEnd1 = newIterator0->info().end();
248  for (; oldIterator1 != oldEnd1 && newIterator1 != newEnd1; ++oldIterator1, ++newIterator1) {
249  if (get<1>(*oldIterator1) == oldOffset) {
250  if (get<1>(*newIterator1) != newOffset) {
251  updated
252  = updated || (EbmlElement::calculateUIntegerLength(newOffset) != EbmlElement::calculateUIntegerLength(get<1>(*newIterator1)));
253  get<1>(*newIterator1) = newOffset;
254  }
255  }
256  }
257  }
258  return updated;
259 }
260 
265 bool MatroskaSeekInfo::updateSeekInfo(std::vector<MatroskaSeekInfo> &newSeekInfos, uint64 oldOffset, uint64 newOffset)
266 {
267  if (oldOffset == newOffset) {
268  return false;
269  }
270  bool updated = false;
271  for (auto &seekInfo : newSeekInfos) {
272  for (auto &info : seekInfo.info()) {
273  if (get<1>(info) == oldOffset) {
274  get<1>(info) = newOffset;
275  updated = true;
276  }
277  }
278  }
279  return updated;
280 }
281 
282 } // namespace TagParser
ImplementationType * nextSibling()
Returns the next sibling of the element.
ImplementationType * firstChild()
Returns the first child of the element.
STL namespace.
The EbmlElement class helps to parse EBML files such as Matroska files.
Definition: ebmlelement.h:31
std::string idToString() const
Converts the specified EBML ID to a printable string.
Definition: ebmlelement.h:71
void parse(Diagnostics &diag)
Parses the header information of the element which is read from the related stream at the start offse...
uint64 readUInteger()
Reads the content of the element as unsigned integer.
void clear()
Clears the status of the element.
typename FileElementTraits< ImplementationType >::IdentifierType IdentifierType
Specifies the type used to store identifiers.
const IdentifierType & id() const
Returns the element ID.
Contains all classes and functions of the TagInfo library.
Definition: aaccodebook.h:9
The Diagnostics class is a container for DiagMessage.
Definition: diagnostics.h:156