Bug 1409664 - P20. Add BitWriter class. r=bryce
authorJean-Yves Avenard <jyavenard@mozilla.com>
Sat, 16 Jun 2018 00:53:13 -0700
changeset 424826 b77050ae2d433d81588b82dc1080093072372650
parent 424825 6fe4e0609af6a7181ed62199a3d66d131bf93fc2
child 424827 128bb8d855d4884ab87fa00f68bbc918180b7cf4
push id104922
push userjyavenard@mozilla.com
push dateTue, 03 Jul 2018 18:51:29 +0000
treeherdermozilla-inbound@bf624413028d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbryce
bugs1409664
milestone63.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1409664 - P20. Add BitWriter class. r=bryce Summary: Depends on D1633 Tags: #secure-revision Bug #: 1409664 Differential Revision: https://phabricator.services.mozilla.com/D1678
dom/media/BitReader.cpp
dom/media/BitReader.h
dom/media/BitWriter.cpp
dom/media/BitWriter.h
dom/media/gtest/TestBitWriter.cpp
dom/media/gtest/moz.build
dom/media/moz.build
dom/media/platforms/agnostic/bytestreams/H264.cpp
--- a/dom/media/BitReader.cpp
+++ b/dom/media/BitReader.cpp
@@ -152,9 +152,55 @@ BitReader::FillReservoir()
     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
--- a/dom/media/BitReader.h
+++ b/dom/media/BitReader.h
@@ -8,38 +8,41 @@
 #include "MediaData.h"
 
 namespace mozilla
 {
 
 class BitReader
 {
 public:
-  explicit BitReader(const mozilla::MediaByteBuffer* aBuffer);
-  BitReader(const mozilla::MediaByteBuffer* aBuffer, size_t aBits);
+  explicit BitReader(const MediaByteBuffer* aBuffer);
+  BitReader(const 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); }
+  bool ReadBit() { return ReadBits(1) != 0; }
   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.
   uint64_t ReadUTF8();
   // Read unsigned integer Exp-Golomb-coded.
   uint32_t ReadUE();
   // Read signed integer Exp-Golomb-coded.
   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;
 
+  // Return RBSP bit length.
+  static uint32_t GetBitLength(const MediaByteBuffer* aNAL);
+
 private:
   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.
new file mode 100644
--- /dev/null
+++ b/dom/media/BitWriter.cpp
@@ -0,0 +1,96 @@
+/* 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/. */
+
+#include "BitWriter.h"
+#include "MediaData.h"
+#include "mozilla/MathAlgorithms.h"
+
+namespace mozilla
+{
+
+constexpr uint8_t golombLen[256] = {
+  1,  3,  3,  5,  5,  5,  5,  7,  7,  7,  7,  7,  7,  7,  7,  9,  9,  9,  9,
+  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  11, 11, 11, 11, 11, 11, 11,
+  11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
+  11, 11, 11, 11, 11, 11, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
+  13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
+  13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
+  13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 15, 15, 15, 15, 15, 15,
+  15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
+  15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
+  15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
+  15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
+  15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
+  15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
+  15, 15, 15, 15, 15, 15, 15, 15, 17,
+};
+
+BitWriter::BitWriter(MediaByteBuffer* aBuffer)
+  : mBuffer(aBuffer)
+{
+}
+
+BitWriter::~BitWriter() {}
+
+void
+BitWriter::WriteBits(uint64_t aValue, size_t aBits)
+{
+  MOZ_ASSERT(aBits <= sizeof(uint64_t) * 8);
+
+  while (aBits) {
+    if (mBitIndex == 0) {
+      mBuffer->AppendElement(0);
+    }
+
+    const uint8_t clearMask = ~(~0 << (8 - mBitIndex));
+    uint8_t mask = 0;
+
+    if (mBitIndex + aBits > 8) {
+      // Not enough bits in the current byte to write all the bits
+      // required, we'll process what we can and continue with the left over.
+      const uint8_t leftOverBits = mBitIndex + aBits - 8;
+      const uint64_t leftOver = aValue & (~uint64_t(0) >> (8 - mBitIndex));
+      mask = aValue >> leftOverBits;
+
+      mBitIndex = 8;
+      aValue = leftOver;
+      aBits = leftOverBits;
+    } else {
+      const uint8_t offset = 8 - mBitIndex - aBits;
+      mask = aValue << offset;
+
+      mBitIndex += aBits;
+      aBits = 0;
+    }
+
+    mBuffer->ElementAt(mPosition) |= mask & clearMask;
+
+    if (mBitIndex == 8) {
+      mPosition++;
+      mBitIndex = 0;
+    }
+  }
+}
+
+void
+BitWriter::WriteUE(uint32_t aValue)
+{
+  MOZ_ASSERT(aValue <= (UINT32_MAX - 1));
+
+  if (aValue < 256) {
+    WriteBits(aValue + 1, golombLen[aValue]);
+  } else {
+    const uint32_t e = FloorLog2(aValue + 1);
+    WriteBits(aValue + 1, e * 2 + 1);
+  }
+}
+
+void
+BitWriter::CloseWithRbspTrailing()
+{
+  WriteBit(true);
+  WriteBits(0, (8 - mBitIndex) & 7);
+}
+
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/media/BitWriter.h
@@ -0,0 +1,43 @@
+/* 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/. */
+
+#ifndef BIT_WRITER_H_
+#define BIT_WRITER_H_
+
+#include "mozilla/RefPtr.h"
+
+namespace mozilla
+{
+
+class MediaByteBuffer;
+
+class BitWriter
+{
+public:
+  explicit BitWriter(MediaByteBuffer* aBuffer);
+  virtual ~BitWriter();
+  void WriteBits(uint64_t aValue, size_t aBits);
+  void WriteBit(bool aValue) { WriteBits(aValue, 1); }
+  void WriteU8(uint8_t aValue) { WriteBits(aValue, 8); }
+  void WriteU32(uint32_t aValue) { WriteBits(aValue, 32); }
+  void WriteU64(uint64_t aValue) { WriteBits(aValue, 64); }
+
+  // Write unsigned integer into Exp-Golomb-coded. 2^32-2 at most
+  void WriteUE(uint32_t aValue);
+
+  // Write RBSP trailing bits.
+  void CloseWithRbspTrailing();
+
+  // Return the number of bits written so far;
+  size_t BitCount() const { return mPosition * 8 + mBitIndex; }
+
+private:
+  RefPtr<MediaByteBuffer> mBuffer;
+  size_t mPosition = 0;
+  uint8_t mBitIndex = 0;
+};
+
+} // namespace mozilla
+
+#endif // BIT_WRITER_H_
new file mode 100644
--- /dev/null
+++ b/dom/media/gtest/TestBitWriter.cpp
@@ -0,0 +1,50 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/. */
+
+#include "gtest/gtest.h"
+#include "BitReader.h"
+#include "BitWriter.h"
+
+using namespace mozilla;
+
+TEST(BitWriter, BitWriter)
+{
+  RefPtr<MediaByteBuffer> test = new MediaByteBuffer();
+  BitWriter b(test);
+  b.WriteBit(false);
+  b.WriteBits(~1ULL, 1); // ensure that extra bits don't modify byte buffer.
+  b.WriteBits(3, 1);
+  b.WriteUE(1280 / 16 - 1);
+  b.WriteUE(720 / 16 - 1);
+  b.WriteUE(1280);
+  b.WriteUE(720);
+  b.WriteBit(true);
+  b.WriteBit(false);
+  b.WriteBit(true);
+  b.WriteU8(7);
+  b.WriteU32(16356);
+  b.WriteU64(116356);
+  b.WriteBits(~(0ULL) & ~1ULL, 16);
+  const uint32_t length = b.BitCount();
+  b.CloseWithRbspTrailing();
+
+  BitReader c(test);
+
+  EXPECT_EQ(c.ReadBit(), false);
+  EXPECT_EQ(c.ReadBit(), false);
+  EXPECT_EQ(c.ReadBit(), true);
+  EXPECT_EQ(c.ReadUE(), 1280u / 16 - 1);
+  EXPECT_EQ(c.ReadUE(), 720u / 16 - 1);
+  EXPECT_EQ(c.ReadUE(), 1280u);
+  EXPECT_EQ(c.ReadUE(), 720u);
+  EXPECT_EQ(c.ReadBit(), true);
+  EXPECT_EQ(c.ReadBit(), false);
+  EXPECT_EQ(c.ReadBit(), true);
+  EXPECT_EQ(c.ReadBits(8), 7u);
+  EXPECT_EQ(c.ReadU32(), 16356u);
+  EXPECT_EQ(c.ReadU64(), 116356u);
+  EXPECT_EQ(c.ReadBits(16), 0xfffeu);
+  EXPECT_EQ(length, BitReader::GetBitLength(test));
+}
--- a/dom/media/gtest/moz.build
+++ b/dom/media/gtest/moz.build
@@ -7,16 +7,17 @@
 UNIFIED_SOURCES += [
     'MockMediaResource.cpp',
     'TestAudioBuffers.cpp',
     'TestAudioCompactor.cpp',
     'TestAudioMixer.cpp',
     'TestAudioPacketizer.cpp',
     'TestAudioSegment.cpp',
     'TestAudioTrackEncoder.cpp',
+    'TestBitWriter.cpp',
     'TestBlankVideoDataCreator.cpp',
     'TestCDMStorage.cpp',
     'TestDataMutex.cpp',
     'TestGMPCrossOrigin.cpp',
     'TestGMPRemoveAndDelete.cpp',
     'TestGMPUtils.cpp',
     'TestIntervalSet.cpp',
     'TestMediaDataDecoder.cpp',
--- a/dom/media/moz.build
+++ b/dom/media/moz.build
@@ -100,16 +100,17 @@ EXPORTS += [
     'AudioPacketizer.h',
     'AudioSampleFormat.h',
     'AudioSegment.h',
     'AudioStream.h',
     'AutoplayPolicy.h',
     'BackgroundVideoDecodingPermissionObserver.h',
     'Benchmark.h',
     'BitReader.h',
+    'BitWriter.h',
     'BufferMediaResource.h',
     'BufferReader.h',
     'ByteWriter.h',
     'ChannelMediaDecoder.h',
     'CubebUtils.h',
     'DecoderTraits.h',
     'DOMMediaStream.h',
     'FileBlockCache.h',
@@ -219,16 +220,17 @@ UNIFIED_SOURCES += [
     'AudioStreamTrack.cpp',
     'AudioTrack.cpp',
     'AudioTrackList.cpp',
     'AutoplayPolicy.cpp',
     'BackgroundVideoDecodingPermissionObserver.cpp',
     'BaseMediaResource.cpp',
     'Benchmark.cpp',
     'BitReader.cpp',
+    'BitWriter.cpp',
     'CanvasCaptureMediaStream.cpp',
     'ChannelMediaDecoder.cpp',
     'ChannelMediaResource.cpp',
     'CloneableWithRangeMediaResource.cpp',
     'DOMMediaStream.cpp',
     'FileBlockCache.cpp',
     'FileMediaResource.cpp',
     'GetUserMediaRequest.cpp',
--- a/dom/media/platforms/agnostic/bytestreams/H264.cpp
+++ b/dom/media/platforms/agnostic/bytestreams/H264.cpp
@@ -115,62 +115,16 @@ scaling_list(BitReader& aBr, uint8_t (&a
 
 template <size_t N>
 static void
 scaling_list(BitReader& aBr, uint8_t (&aScalingList)[N], const uint8_t (&aDefaultList)[N])
 {
   detail::scaling_list(aBr, aScalingList, N, aDefaultList, nullptr);
 }
 
-static uint32_t
-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;
-}
-
 SPSData::SPSData()
 {
   PodZero(this);
   // Default values when they aren't defined as per ITU-T H.264 (2014/02).
   chroma_format_idc = 1;
   video_format = 5;
   colour_primaries = 2;
   transfer_characteristics = 2;
@@ -200,17 +154,17 @@ public:
   {
     MOZ_ASSERT(aPtr);
 
     if (aLength == 0 || (*aPtr & 0x1f) != H264_NAL_SPS) {
       return;
     }
     mDecodedNAL = H264::DecodeNALUnit(aPtr, aLength);
     if (mDecodedNAL) {
-      mLength = GetBitLength(mDecodedNAL);
+      mLength = BitReader::GetBitLength(mDecodedNAL);
     }
   }
 
   SPSNAL() { }
 
   bool IsValid() const { return mDecodedNAL; }
 
   bool operator==(const SPSNAL& aOther) const
@@ -400,17 +354,17 @@ ConditionDimension(float aValue)
 }
 
 /* static */ bool
 H264::DecodeSPS(const mozilla::MediaByteBuffer* aSPS, SPSData& aDest)
 {
   if (!aSPS) {
     return false;
   }
-  BitReader br(aSPS, GetBitLength(aSPS));
+  BitReader br(aSPS, BitReader::GetBitLength(aSPS));
 
   aDest.profile_idc = br.ReadBits(8);
   aDest.constraint_set0_flag = br.ReadBit();
   aDest.constraint_set1_flag = br.ReadBit();
   aDest.constraint_set2_flag = br.ReadBit();
   aDest.constraint_set3_flag = br.ReadBit();
   aDest.constraint_set4_flag = br.ReadBit();
   aDest.constraint_set5_flag = br.ReadBit();