Tag Parser 12.5.0
C++ library for reading and writing MP4 (iTunes), ID3, Vorbis, Opus, FLAC and Matroska tags
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
matroskaseekinfo.cpp
Go to the documentation of this file.
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
12using namespace std;
13using namespace CppUtilities;
14
15namespace TagParser {
16
21
25void MatroskaSeekInfo::shift(std::uint64_t start, std::int64_t amount)
26{
27 for (auto &info : m_info) {
28 if (get<1>(info) >= start) {
29 if (amount > 0) {
30 get<1>(info) += static_cast<std::uint64_t>(amount);
31 } else {
32 get<1>(info) -= static_cast<std::uint64_t>(-amount);
33 }
34 }
35 }
36}
37
51void MatroskaSeekInfo::parse(EbmlElement *seekHeadElement, Diagnostics &diag, size_t maxIndirection)
52{
53 static const string context("parsing \"SeekHead\"-element");
54
55 m_seekHeadElements.emplace_back(seekHeadElement);
56
57 for (EbmlElement *seekElement = seekHeadElement->firstChild(), *seekIdElement, *seekPositionElement; seekElement;
58 seekElement = seekElement->nextSibling()) {
59 seekElement->parse(diag);
60 switch (seekElement->id()) {
62 seekIdElement = seekPositionElement = nullptr;
63 for (auto *seekElementChild = seekElement->firstChild(); seekElementChild; seekElementChild = seekElementChild->nextSibling()) {
64 seekElementChild->parse(diag);
65 switch (seekElementChild->id()) {
67 if (seekIdElement) {
68 diag.emplace_back(DiagLevel::Warning,
69 "The \"Seek\"-element contains multiple \"SeekID\"-elements. Surplus elements will be ignored.", context);
70 }
71 seekIdElement = seekElementChild;
72 break;
74 if (seekPositionElement) {
75 diag.emplace_back(DiagLevel::Warning,
76 "The \"Seek\"-element contains multiple \"SeekPosition\"-elements. Surplus elements will be ignored.", context);
77 }
78 seekPositionElement = seekElementChild;
79 break;
80 case EbmlIds::Crc32:
81 case EbmlIds::Void:
82 break;
83 default:
84 diag.emplace_back(DiagLevel::Warning,
85 "The element \"" % seekElementChild->idToString()
86 + "\" within the \"Seek\" element is not a \"SeekID\"-element nor a \"SeekPosition\"-element and will be ignored.",
87 context);
88 }
89 }
90
91 if (!seekIdElement || !seekPositionElement) {
92 diag.emplace_back(DiagLevel::Warning, "The \"Seek\"-element does not contain a \"SeekID\"- and a \"SeekPosition\"-element.", context);
93 break;
94 }
95
96 m_info.emplace_back(seekIdElement->readUInteger(), seekPositionElement->readUInteger());
97
98 // follow possibly referenced seek head element
99 if (m_info.back().first == MatroskaIds::SeekHead) {
100 const auto startOffset = m_info.back().second;
101 if (!maxIndirection) {
102 diag.emplace_back(DiagLevel::Warning,
103 argsToString("Not following reference by \"Seek\"-element at ", seekElement->startOffset(),
104 " which points to another \"SeekHead\"-element at ", startOffset, '.'),
105 context);
106 break;
107 }
108
109 auto visited = false;
110 for (const auto *const visitedSeekHeadElement : m_seekHeadElements) {
111 if (visitedSeekHeadElement->startOffset() == startOffset) {
112 diag.emplace_back(DiagLevel::Warning,
113 argsToString("The \"Seek\"-element at ", seekElement->startOffset(), " contains a loop to the \"SeekHead\"-element at ",
114 visitedSeekHeadElement->startOffset(), '.'),
115 context);
116 visited = true;
117 break;
118 }
119 }
120 if (visited) {
121 break;
122 }
123 m_additionalSeekHeadElements.emplace_back(make_unique<EbmlElement>(seekHeadElement->container(), startOffset));
124 parse(m_additionalSeekHeadElements.back().get(), diag, maxIndirection - 1);
125 }
126
127 break;
128 case EbmlIds::Crc32:
129 case EbmlIds::Void:
130 break;
131 default:
132 diag.emplace_back(
133 DiagLevel::Warning, "The element " % seekElement->idToString() + " is not a seek element and will be ignored.", context);
134 }
135 }
136 if (m_info.empty()) {
137 diag.emplace_back(DiagLevel::Warning, "No seek information found.", context);
138 }
139}
140
147void MatroskaSeekInfo::make(ostream &stream, Diagnostics &diag)
148{
149 CPP_UTILITIES_UNUSED(diag)
150
151 std::uint64_t totalSize = 0;
152 char buff0[8];
153 char buff1[8];
154 char buff2[2];
155 std::uint8_t sizeLength0, sizeLength1;
156 // calculate size
157 for (const auto &info : m_info) {
158 // "Seek" element + "SeekID" element + "SeekPosition" element
159 totalSize += 2u + 1u + (2u + 1u + EbmlElement::calculateIdLength(info.first)) + (2u + 1u + EbmlElement::calculateUIntegerLength(info.second));
160 }
161 // write ID and size
162 BE::getBytes(static_cast<std::uint32_t>(MatroskaIds::SeekHead), buff0);
163 stream.write(buff0, 4);
164 sizeLength0 = EbmlElement::makeSizeDenotation(totalSize, buff0);
165 stream.write(buff0, sizeLength0);
166 // write entries
167 for (const auto &info : m_info) {
168 // make values
169 sizeLength0 = EbmlElement::makeId(info.first, buff0);
170 sizeLength1 = EbmlElement::makeUInteger(info.second, buff1);
171 // "Seek" header
172 BE::getBytes(static_cast<std::uint16_t>(MatroskaIds::Seek), buff2);
173 stream.write(buff2, 2);
174 stream.put(static_cast<char>(0x80 | (2 + 1 + sizeLength0 + 2 + 1 + sizeLength1)));
175 // "SeekID" element
176 BE::getBytes(static_cast<std::uint16_t>(MatroskaIds::SeekID), buff2);
177 stream.write(buff2, 2);
178 stream.put(static_cast<char>(0x80 | sizeLength0));
179 stream.write(buff0, sizeLength0);
180 // "SeekPosition" element
181 BE::getBytes(static_cast<std::uint16_t>(MatroskaIds::SeekPosition), buff2);
182 stream.write(buff2, 2);
183 stream.put(static_cast<char>(0x80 | sizeLength1));
184 stream.write(buff1, sizeLength1);
185 }
186}
187
192std::uint64_t MatroskaSeekInfo::minSize() const
193{
194 std::uint64_t maxTotalSize = m_info.size() * (2 + 1 + 2 + 1 + 1 + 2 + 1 + 1);
195 return 4 + EbmlElement::calculateSizeDenotationLength(maxTotalSize) + maxTotalSize;
196}
197
202std::uint64_t MatroskaSeekInfo::maxSize() const
203{
204 std::uint64_t maxTotalSize = m_info.size() * (2 + 1 + 2 + 1 + 4 + 2 + 1 + 8);
205 return 4 + EbmlElement::calculateSizeDenotationLength(maxTotalSize) + maxTotalSize;
206}
207
212std::uint64_t MatroskaSeekInfo::actualSize() const
213{
214 std::uint64_t totalSize = 0;
215 for (const auto &info : m_info) {
216 // "Seek" element + "SeekID" element + "SeekPosition" element
217 totalSize
218 += 2u + 1u + (2 + 1 + EbmlElement::calculateIdLength(get<0>(info))) + (2u + 1u + EbmlElement::calculateUIntegerLength(get<1>(info)));
219 }
220 return totalSize += 4u + EbmlElement::calculateSizeDenotationLength(totalSize);
221}
222
231bool MatroskaSeekInfo::push(unsigned int index, EbmlElement::IdentifierType id, std::uint64_t offset)
232{
233 unsigned int currentIndex = 0;
234 for (auto &entry : info()) {
235 if (get<0>(entry) == id) {
236 if (index == currentIndex) {
237 bool sizeUpdated = EbmlElement::calculateUIntegerLength(get<1>(entry)) != EbmlElement::calculateUIntegerLength(offset);
238 get<1>(entry) = offset;
239 return sizeUpdated;
240 }
241 ++currentIndex;
242 }
243 }
244 info().emplace_back(id, offset);
245 return true;
246}
247
252{
253 m_seekHeadElements.clear();
254 m_additionalSeekHeadElements.clear();
255 m_info.clear();
256}
257
258} // namespace TagParser
The Diagnostics class is a container for DiagMessage.
The EbmlElement class helps to parse EBML files such as Matroska files.
Definition ebmlelement.h:32
static std::uint8_t makeUInteger(std::uint64_t value, char *buff)
Writes value to buff.
static std::uint8_t calculateIdLength(IdentifierType id)
Returns the length of the specified id in byte.
static std::uint8_t calculateSizeDenotationLength(std::uint64_t size)
Returns the length of the size denotation for the specified size in byte.
static std::uint8_t makeId(IdentifierType id, char *buff)
Stores the specified id in the specified buffer which must be at least 8 bytes long.
static std::uint8_t makeSizeDenotation(std::uint64_t size, char *buff)
Makes the size denotation for the specified size and stores it to buff.
static std::uint8_t calculateUIntegerLength(std::uint64_t integer)
Returns the length of the specified unsigned integer in byte.
ImplementationType * nextSibling()
Returns the next sibling of the element.
ImplementationType * firstChild()
Returns the first child of the element.
typename FileElementTraits< EbmlElement >::IdentifierType IdentifierType
ContainerType & container()
Returns the related container.
void parse(EbmlElement *seekHeadElements, Diagnostics &diag, std::size_t maxIndirection=1)
Parses the specified seekHeadElement and populates info() with the gathered information.
void shift(std::uint64_t start, std::int64_t amount)
Shifts all offsets greater or equal than start by amount bytes.
void clear()
Resets the object to its initial state.
bool push(unsigned int index, EbmlElement::IdentifierType id, std::uint64_t offset)
Pushes the specified offset of an element with the specified id to the info.
void make(std::ostream &stream, Diagnostics &diag)
Writes a "SeekHead" element for the current instance to the specified stream.
std::vector< std::pair< EbmlElement::IdentifierType, std::uint64_t > > & info()
Returns a mutable version of the seek information gathered when the parse() method was called.
std::uint64_t maxSize() const
Returns the maximal number of bytes written when calling the make() method.
std::uint64_t minSize() const
Returns the minimal number of bytes written when calling the make() method.
const std::vector< std::pair< EbmlElement::IdentifierType, std::uint64_t > > & info() const
Returns the seek information gathered when the parse() method was called.
std::uint64_t actualSize() const
Returns the number of bytes which will be written when calling the make() method.
Contains all classes and functions of the TagInfo library.
Definition aaccodebook.h:10