mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-24 13:21:05 +00:00
eedf4d7699
Additionally, BitReader and BitWriter now support ULEB128 values which are used in AV1 sequence headers. Differential Revision: https://phabricator.services.mozilla.com/D141072
198 lines
4.3 KiB
C++
198 lines
4.3 KiB
C++
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
// Derived from Stagefright's ABitReader.
|
|
|
|
#include "BitReader.h"
|
|
|
|
namespace mozilla {
|
|
|
|
BitReader::BitReader(const mozilla::MediaByteBuffer* aBuffer)
|
|
: BitReader(aBuffer->Elements(), aBuffer->Length() * 8) {}
|
|
|
|
BitReader::BitReader(const mozilla::MediaByteBuffer* aBuffer, size_t aBits)
|
|
: BitReader(aBuffer->Elements(), aBits) {}
|
|
|
|
BitReader::BitReader(const uint8_t* aBuffer, size_t aBits)
|
|
: mData(aBuffer),
|
|
mOriginalBitSize(aBits),
|
|
mTotalBitsLeft(aBits),
|
|
mSize((aBits + 7) / 8),
|
|
mReservoir(0),
|
|
mNumBitsLeft(0) {}
|
|
|
|
BitReader::~BitReader() = default;
|
|
|
|
uint32_t BitReader::ReadBits(size_t aNum) {
|
|
MOZ_ASSERT(aNum <= 32);
|
|
if (mTotalBitsLeft < aNum) {
|
|
NS_ASSERTION(false, "Reading past end of buffer");
|
|
return 0;
|
|
}
|
|
uint32_t result = 0;
|
|
while (aNum > 0) {
|
|
if (mNumBitsLeft == 0) {
|
|
FillReservoir();
|
|
}
|
|
|
|
size_t m = aNum;
|
|
if (m > mNumBitsLeft) {
|
|
m = mNumBitsLeft;
|
|
}
|
|
|
|
if (m == 32) {
|
|
result = mReservoir;
|
|
mReservoir = 0;
|
|
} else {
|
|
result = (result << m) | (mReservoir >> (32 - m));
|
|
mReservoir <<= m;
|
|
}
|
|
mNumBitsLeft -= m;
|
|
mTotalBitsLeft -= m;
|
|
|
|
aNum -= m;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
// Read unsigned integer Exp-Golomb-coded.
|
|
uint32_t BitReader::ReadUE() {
|
|
uint32_t i = 0;
|
|
|
|
while (ReadBit() == 0 && i < 32) {
|
|
i++;
|
|
}
|
|
if (i == 32) {
|
|
// This can happen if the data is invalid, or if it's
|
|
// short, since ReadBit() will return 0 when it runs
|
|
// off the end of the buffer.
|
|
NS_WARNING("Invalid H.264 data");
|
|
return 0;
|
|
}
|
|
uint32_t r = ReadBits(i);
|
|
r += (uint32_t(1) << i) - 1;
|
|
|
|
return r;
|
|
}
|
|
|
|
// Read signed integer Exp-Golomb-coded.
|
|
int32_t BitReader::ReadSE() {
|
|
int32_t r = ReadUE();
|
|
if (r & 1) {
|
|
return (r + 1) / 2;
|
|
} else {
|
|
return -r / 2;
|
|
}
|
|
}
|
|
|
|
uint64_t BitReader::ReadU64() {
|
|
uint64_t hi = ReadU32();
|
|
uint32_t lo = ReadU32();
|
|
return (hi << 32) | lo;
|
|
}
|
|
|
|
CheckedUint64 BitReader::ReadULEB128() {
|
|
// See https://en.wikipedia.org/wiki/LEB128#Decode_unsigned_integer
|
|
CheckedUint64 value = 0;
|
|
for (size_t i = 0; i < sizeof(uint64_t) * 8 / 7; i++) {
|
|
bool more = ReadBit();
|
|
value += static_cast<uint64_t>(ReadBits(7)) << (i * 7);
|
|
if (!more) {
|
|
break;
|
|
}
|
|
}
|
|
return value;
|
|
}
|
|
|
|
uint64_t BitReader::ReadUTF8() {
|
|
int64_t val = ReadBits(8);
|
|
uint32_t top = (val & 0x80) >> 1;
|
|
|
|
if ((val & 0xc0) == 0x80 || val >= 0xFE) {
|
|
// error.
|
|
return -1;
|
|
}
|
|
while (val & top) {
|
|
int tmp = ReadBits(8) - 128;
|
|
if (tmp >> 6) {
|
|
// error.
|
|
return -1;
|
|
}
|
|
val = (val << 6) + tmp;
|
|
top <<= 5;
|
|
}
|
|
val &= (top << 1) - 1;
|
|
return val;
|
|
}
|
|
|
|
size_t BitReader::BitCount() const { return mOriginalBitSize - mTotalBitsLeft; }
|
|
|
|
size_t BitReader::BitsLeft() const { return mTotalBitsLeft; }
|
|
|
|
void BitReader::FillReservoir() {
|
|
if (mSize == 0) {
|
|
NS_ASSERTION(false, "Attempting to fill reservoir from past end of data");
|
|
return;
|
|
}
|
|
|
|
mReservoir = 0;
|
|
size_t i;
|
|
for (i = 0; mSize > 0 && i < 4; i++) {
|
|
mReservoir = (mReservoir << 8) | *mData;
|
|
mData++;
|
|
mSize--;
|
|
}
|
|
|
|
mNumBitsLeft = 8 * i;
|
|
mReservoir <<= 32 - mNumBitsLeft;
|
|
}
|
|
|
|
/* static */
|
|
uint32_t BitReader::GetBitLength(const mozilla::MediaByteBuffer* aNAL) {
|
|
size_t size = aNAL->Length();
|
|
|
|
while (size > 0 && aNAL->ElementAt(size - 1) == 0) {
|
|
size--;
|
|
}
|
|
|
|
if (!size) {
|
|
return 0;
|
|
}
|
|
|
|
if (size > UINT32_MAX / 8) {
|
|
// We can't represent it, we'll use as much as we can.
|
|
return UINT32_MAX;
|
|
}
|
|
|
|
uint8_t v = aNAL->ElementAt(size - 1);
|
|
size *= 8;
|
|
|
|
// Remove the stop bit and following trailing zeros.
|
|
if (v) {
|
|
// Count the consecutive zero bits (trailing) on the right by binary search.
|
|
// Adapted from Matt Whitlock algorithm to only bother with 8 bits integers.
|
|
uint32_t c;
|
|
if (v & 1) {
|
|
// Special case for odd v (assumed to happen half of the time).
|
|
c = 0;
|
|
} else {
|
|
c = 1;
|
|
if ((v & 0xf) == 0) {
|
|
v >>= 4;
|
|
c += 4;
|
|
}
|
|
if ((v & 0x3) == 0) {
|
|
v >>= 2;
|
|
c += 2;
|
|
}
|
|
c -= v & 0x1;
|
|
}
|
|
size -= c + 1;
|
|
}
|
|
return size;
|
|
}
|
|
|
|
} // namespace mozilla
|