Двоичные данные
MKV имеют формат EBML , а целое число без знака может иметь переменный размер. Размер переменной int может состоять из переменного числа октетов (может иметь разный размер в байтах).
Каждое целое число переменного размера начинается с VINT_WIDTH, за которым следует VINT_MARKER. VINT_WIDTH является последовательностью из нуля или более битов значения 0
и заканчивается VINT_MARKER, который является единственным битом значения 1
. Общая длина в битах как VINT_WIDTH, так и VINT_MARKER является общей длиной в октетах целого числа переменного размера.
Одиночный бит 1
запускает целое число переменного размера с длиной в один октет. Последовательность битов 01
запускает целое число переменного размера длиной два октета. 001
запускает целочисленное значение переменного размера длиной в три октета и т. Д., При этом каждый дополнительный 0-разрядный бит добавляет один октет к длине целого числа переменного размера.
Положение первого ' 1 'бит в первом байте переменного размера целое число обозначает размер в байтах. Если он находится на первой позиции
1XXXXXXX (здесь я использую 'X' для других битов числа, кроме части длины)
, тогда переменная составляет один байт long и остальные биты после первого бита '1' (в данном случае 7 младших битов) являются двоичным представлением числа. Переменный размер int , начинающийся с
0000001X XXXXXXXX XXXXXXXX XXXXXXXX XXXXXXXX XXXXXXXX XXXXXXXX
- это длина в семь байт для первого '1' бита здесь седьмая позиция.
Итак, сначала вам нужно прочитать первый байт числа и найти позицию N первого бита '1', а затем прочитать все число N байт, игнорируя этот первый бит '1' (как будто это нулевой бит).
constexpr uint8_t VarSizeIntLenMark(int length)
{
return 1 << (8 - length); // set single bit at length's position
}
int VarSizeIntLen(const uint8_t* data)
{
for (int i = 1; i <= 8; i++)
if (VarSizeIntLenMark(i) & data[0]) return i;
return 0;
}
uint64_t ReadVariableSizeInt(const uint8_t* data)
{
int length = VarSizeIntLen(data[0]);
uint64_t parsedValue = data[0] & (~VarSizeIntLenMark(length)); // invert VINT_MARKER bit
for (int i = 1; i < length; i++) // read other bytes
parsedValue = (parsedValue << 8) + data[i];
return parsedValue;
}