summaryrefslogtreecommitdiffstats
path: root/dom/media/BitReader.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'dom/media/BitReader.cpp')
-rw-r--r--dom/media/BitReader.cpp197
1 files changed, 197 insertions, 0 deletions
diff --git a/dom/media/BitReader.cpp b/dom/media/BitReader.cpp
new file mode 100644
index 0000000000..f66ab76f19
--- /dev/null
+++ b/dom/media/BitReader.cpp
@@ -0,0 +1,197 @@
+/* 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