1323081: P1. Add native BitReader class. r?gerald draft
authorJean-Yves Avenard <jyavenard@mozilla.com>
Wed, 14 Dec 2016 15:27:48 +1100
changeset 449460 720518da8319a18fcc8e3b46b52c5983dabb9189
parent 449439 1ea0c60db5d25a7d522e2f252c1978ff4fc7538e
child 449461 95d3e18dd47701fd468ce27716ba250f30d3609b
push id38568
push userbmo:jyavenard@mozilla.com
push dateWed, 14 Dec 2016 06:36:13 +0000
reviewersgerald
bugs1323081
milestone53.0a1
1323081: P1. Add native BitReader class. r?gerald This is a rewrite from Stagefright's ABitReader. The major difference is that you give the original size in bits rather than in bytes. ABitReader always read all bits available. While under some circumstances we want to bound the buffer to a set number of bits. MozReview-Commit-ID: hdJ7CAwOea
dom/media/flac/FlacDemuxer.cpp
media/libstagefright/binding/BitReader.cpp
media/libstagefright/binding/include/mp4_demuxer/BitReader.h
--- a/dom/media/flac/FlacDemuxer.cpp
+++ b/dom/media/flac/FlacDemuxer.cpp
@@ -55,17 +55,17 @@ public:
   // Parse the current packet and check that it made a valid flac frame header.
   // From https://xiph.org/flac/format.html#frame_header
   // A valid header is one that can be decoded without error and that has a
   // valid CRC.
   // aPacket must points to a buffer that is at least FLAC_MAX_FRAME_HEADER_SIZE
   // bytes.
   bool Parse(const uint8_t* aPacket)
   {
-    mp4_demuxer::BitReader br(aPacket, FLAC_MAX_FRAME_HEADER_SIZE);
+    mp4_demuxer::BitReader br(aPacket, FLAC_MAX_FRAME_HEADER_SIZE * 8);
 
     // Frame sync code.
     if ((br.ReadBits(15) & 0x7fff) != 0x7ffc) {
       return false;
     }
 
     // Variable block size stream code.
     mVariableBlockSize = br.ReadBit();
--- a/media/libstagefright/binding/BitReader.cpp
+++ b/media/libstagefright/binding/BitReader.cpp
@@ -1,39 +1,73 @@
 /* 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 "mp4_demuxer/BitReader.h"
 #include <media/stagefright/foundation/ABitReader.h>
 
 using namespace mozilla;
 using namespace stagefright;
 
 namespace mp4_demuxer
 {
 
 BitReader::BitReader(const mozilla::MediaByteBuffer* aBuffer)
-  : mBitReader(new ABitReader(aBuffer->Elements(), aBuffer->Length()))
-  , mSize(aBuffer->Length()) {}
+  : 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 aLength)
-  : mBitReader(new ABitReader(aBuffer, aLength))
-  , mSize(aLength) {}
+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() {}
+BitReader::~BitReader() { }
 
 uint32_t
 BitReader::ReadBits(size_t aNum)
 {
   MOZ_ASSERT(aNum <= 32);
-  if (mBitReader->numBitsLeft() < aNum) {
+  if (mTotalBitsLeft < aNum) {
+    NS_ASSERTION(false, "Reading past end of buffer");
     return 0;
   }
-  return mBitReader->getBits(aNum);
+  uint32_t result = 0;
+  while (aNum > 0) {
+    if (mNumBitsLeft == 0) {
+      FillReservoir();
+    }
+
+    size_t m = aNum;
+    if (m > mNumBitsLeft) {
+      m = mNumBitsLeft;
+    }
+
+    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;
 
@@ -93,18 +127,38 @@ BitReader::ReadUTF8()
   }
   val &= (top << 1) - 1;
   return val;
 }
 
 size_t
 BitReader::BitCount() const
 {
-  return mSize * 8 - mBitReader->numBitsLeft();
+  return mOriginalBitSize - mTotalBitsLeft;
 }
 
 size_t
 BitReader::BitsLeft() const
 {
-  return mBitReader->numBitsLeft();
+  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;
 }
 
 } // namespace mp4_demuxer
--- a/media/libstagefright/binding/include/mp4_demuxer/BitReader.h
+++ b/media/libstagefright/binding/include/mp4_demuxer/BitReader.h
@@ -12,17 +12,18 @@ namespace stagefright { class ABitReader
 
 namespace mp4_demuxer
 {
 
 class BitReader
 {
 public:
   explicit BitReader(const mozilla::MediaByteBuffer* aBuffer);
-  BitReader(const uint8_t* aBuffer, size_t aLength);
+  BitReader(const mozilla::MediaByteBuffer* aBuffer, size_t aBits);
+  BitReader(const uint8_t* aBuffer, size_t aBits);
   ~BitReader();
   uint32_t ReadBits(size_t aNum);
   uint32_t ReadBit() { return ReadBits(1); }
   uint32_t ReadU32() { return ReadBits(32); }
   uint64_t ReadU64();
 
   // Read the UTF-8 sequence and convert it to its 64-bit UCS-4 encoded form.
   // Return 0xfffffffffffffff if sequence was invalid.
@@ -33,15 +34,20 @@ public:
   int32_t ReadSE();
 
   // Return the number of bits parsed so far;
   size_t BitCount() const;
   // Return the number of bits left.
   size_t BitsLeft() const;
 
 private:
-  nsAutoPtr<stagefright::ABitReader> mBitReader;
-  const size_t mSize;
+  void FillReservoir();
+  const uint8_t* mData;
+  const size_t mOriginalBitSize;
+  size_t mTotalBitsLeft;
+  size_t mSize;           // Size left in bytes
+  uint32_t mReservoir;    // Left-aligned bits
+  size_t mNumBitsLeft;    // Number of bits left in reservoir.
 };
 
 } // namespace mp4_demuxer
 
 #endif // BIT_READER_H_
\ No newline at end of file