summaryrefslogtreecommitdiff
path: root/chromium/media/filters/h264_bitstream_buffer.cc
blob: 48c20e67e5a2ef0812a85a099d9c07b9e5a896f8 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "media/filters/h264_bitstream_buffer.h"

#include "base/bits.h"
#include "base/sys_byteorder.h"

namespace media {

H264BitstreamBuffer::H264BitstreamBuffer() : data_(nullptr) {
  Reset();
}

H264BitstreamBuffer::~H264BitstreamBuffer() {
  free(data_);
  data_ = nullptr;
}

void H264BitstreamBuffer::Reset() {
  free(data_);
  data_ = nullptr;

  capacity_ = 0;
  pos_ = 0;
  bits_in_buffer_ = 0;
  reg_ = 0;

  Grow();

  bits_left_in_reg_ = kRegBitSize;
}

void H264BitstreamBuffer::Grow() {
  data_ = static_cast<uint8_t*>(realloc(data_, capacity_ + kGrowBytes));
  CHECK(data_) << "Failed growing the buffer";
  capacity_ += kGrowBytes;
}

void H264BitstreamBuffer::FlushReg() {
  // Flush all bytes that have at least one bit cached, but not more
  // (on Flush(), reg_ may not be full).
  size_t bits_in_reg = kRegBitSize - bits_left_in_reg_;
  if (bits_in_reg == 0)
    return;

  size_t bytes_in_reg = base::bits::AlignUp(bits_in_reg, size_t{8}) / 8;
  reg_ <<= (kRegBitSize - bits_in_reg);

  // Convert to MSB and append as such to the stream.
  reg_ = base::HostToNet64(reg_);

  // Make sure we have enough space. Grow() will CHECK() on allocation failure.
  if (pos_ + bytes_in_reg > capacity_)
    Grow();

  memcpy(data_ + pos_, &reg_, bytes_in_reg);
  bits_in_buffer_ = pos_ * 8 + bits_in_reg;
  pos_ += bytes_in_reg;

  reg_ = 0;
  bits_left_in_reg_ = kRegBitSize;
}

void H264BitstreamBuffer::AppendU64(size_t num_bits, uint64_t val) {
  CHECK_LE(num_bits, kRegBitSize);

  while (num_bits > 0) {
    if (bits_left_in_reg_ == 0)
      FlushReg();

    uint64_t bits_to_write =
        num_bits > bits_left_in_reg_ ? bits_left_in_reg_ : num_bits;
    uint64_t val_to_write = (val >> (num_bits - bits_to_write));
    if (bits_to_write < 64)
      val_to_write &= ((1ull << bits_to_write) - 1);
    reg_ <<= bits_to_write;
    reg_ |= val_to_write;
    num_bits -= bits_to_write;
    bits_left_in_reg_ -= bits_to_write;
  }
}

void H264BitstreamBuffer::AppendBool(bool val) {
  if (bits_left_in_reg_ == 0)
    FlushReg();

  reg_ <<= 1;
  reg_ |= (static_cast<uint64_t>(val) & 1);
  --bits_left_in_reg_;
}

void H264BitstreamBuffer::AppendSE(int val) {
  if (val > 0)
    AppendUE(val * 2 - 1);
  else
    AppendUE(-val * 2);
}

void H264BitstreamBuffer::AppendUE(unsigned int val) {
  size_t num_zeros = 0;
  unsigned int v = val + 1;

  while (v > 1) {
    v >>= 1;
    ++num_zeros;
  }

  AppendBits(num_zeros, 0);
  AppendBits(num_zeros + 1, val + 1);
}

#define DCHECK_FINISHED()                                                      \
  DCHECK_EQ(bits_left_in_reg_, kRegBitSize) << "Pending bits not yet written " \
                                               "to the buffer, call "          \
                                               "FinishNALU() first."

void H264BitstreamBuffer::BeginNALU(H264NALU::Type nalu_type, int nal_ref_idc) {
  DCHECK_FINISHED();

  DCHECK_LE(nalu_type, H264NALU::kEOStream);
  DCHECK_GE(nal_ref_idc, 0);
  DCHECK_LE(nal_ref_idc, 3);

  AppendBits(32, 0x00000001);
  AppendBits(1, 0);  // forbidden_zero_bit
  AppendBits(2, nal_ref_idc);
  AppendBits(5, nalu_type);
}

void H264BitstreamBuffer::FinishNALU() {
  // RBSP stop one bit.
  AppendBits(1, 1);

  // Byte-alignment zero bits.
  AppendBits(bits_left_in_reg_ % 8, 0);

  Flush();
}

void H264BitstreamBuffer::Flush() {
  if (bits_left_in_reg_ != kRegBitSize)
    FlushReg();
}

size_t H264BitstreamBuffer::BitsInBuffer() const {
  return bits_in_buffer_;
}

size_t H264BitstreamBuffer::BytesInBuffer() const {
  DCHECK_FINISHED();
  return pos_;
}

const uint8_t* H264BitstreamBuffer::data() const {
  DCHECK(data_);
  DCHECK_FINISHED();

  return data_;
}

}  // namespace media