gecko-dev/dom/media/BitReader.cpp
Chun-Min Chang b1734bc358 Bug 1831451 - Downgrade BitReader::ReadBits's assertion to warning r=padenot
`BitReader::ReadBits` will call a `NS_ASSERTION` when the request is
unreasonable. However, in WebCodecs' VedioDecoder wpt, one test will
intentionally decode a corrupted sample and it makes the av1 decoder to
reach that assertion and cause a wpt failure. Since that assertion
doesn't really do any meaningful work, downgrade it to `NS_WARNING`
to make wpt work seems fine.

Depends on D177212

Differential Revision: https://phabricator.services.mozilla.com/D179583
2023-06-29 22:33:13 +00:00

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_WARNING("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