blob: 9c0c71b6e8bda423784941aab322c9c339096231 [file] [log] [blame]
// Copyright 2012 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "cobalt/media/filters/shell_rbsp_stream.h"
#include "base/logging.h"
namespace cobalt {
namespace media {
ShellRBSPStream::ShellRBSPStream(const uint8* nalu_buffer,
size_t nalu_buffer_size)
: nalu_buffer_(nalu_buffer),
nalu_buffer_size_(nalu_buffer_size),
nalu_buffer_byte_offset_(0),
current_nalu_byte_(0),
number_consecutive_zeros_(0),
rbsp_bit_offset_(0) {}
// read unsigned Exp-Golomb coded integer, ISO 14496-10 Section 9.1
bool ShellRBSPStream::ReadUEV(uint32* uev_out) {
DCHECK(uev_out);
int leading_zero_bits = -1;
for (uint8 b = 0; b == 0; leading_zero_bits++) {
if (!ReadRBSPBit(&b)) {
return false;
}
}
// we can only fit 31 bits of Exp-Golomb coded data into a 32-bit number
if (leading_zero_bits >= 32) {
return false;
}
uint32 result = (1 << leading_zero_bits) - 1;
uint32 remainder = 0;
if (!ReadBits(leading_zero_bits, &remainder)) {
return false;
}
result += remainder;
*uev_out = result;
return true;
}
// read signed Exp-Golomb coded integer, ISO 14496-10 Section 9.1
bool ShellRBSPStream::ReadSEV(int32* sev_out) {
DCHECK(sev_out);
// we start off by reading an unsigned Exp-Golomb coded number
uint32 uev = 0;
if (!ReadUEV(&uev)) {
return false;
}
// the LSb in this number is treated as the inverted sign bit
bool is_negative = !(uev & 1);
int32 result = static_cast<int32>((uev + 1) >> 1);
if (is_negative) {
result *= -1;
}
*sev_out = result;
return true;
}
// read and return up to 32 bits, filling from the right, meaning that
// ReadBits(17) on a stream of all 1s would return 0x01ffff
bool ShellRBSPStream::ReadBits(size_t bits, uint32* bits_out) {
DCHECK(bits_out);
if (bits > 32) {
return false;
}
if (bits == 0) {
return true;
}
uint32 result = 0;
size_t bytes = bits >> 3;
// read bytes first
for (int i = 0; i < bytes; i++) {
uint8 new_byte = 0;
if (!ReadRBSPByte(&new_byte)) {
return false;
}
result = result << 8;
result = result | static_cast<uint32>(new_byte);
}
// scoot any leftover bits in
bits = bits % 8;
for (int i = 0; i < bits; i++) {
uint8 new_bit = 0;
if (!ReadRBSPBit(&new_bit)) {
return false;
}
result = result << 1;
result = result | static_cast<uint32>(new_bit);
}
*bits_out = result;
return true;
}
// jump over bytes in the RBSP stream
bool ShellRBSPStream::SkipBytes(size_t bytes) {
for (int i = 0; i < bytes; ++i) {
if (!ConsumeNALUByte()) {
return false;
}
}
return true;
}
// jump over bits in the RBSP stream
bool ShellRBSPStream::SkipBits(size_t bits) {
// skip bytes first
size_t bytes = bits >> 3;
if (bytes > 0) {
if (!SkipBytes(bytes)) {
return false;
}
}
// mask off byte skips
bits = bits & 7;
// if no bits left to skip just return
if (bits == 0) {
return true;
}
// obey the convention that if our bit offset is 0 we haven't loaded the
// current byte, extract it from NALU stream as we are going to advance
// the bit cursor in to it (or potentially past it)
if (rbsp_bit_offset_ == 0) {
if (!ConsumeNALUByte()) {
return false;
}
}
// add to our bit offset
rbsp_bit_offset_ += bits;
// if we jumped in to the next byte advance the NALU stream, respecting the
// convention that if we're at 8 bits stay on the current byte
if (rbsp_bit_offset_ >= 9) {
if (!ConsumeNALUByte()) {
return false;
}
}
rbsp_bit_offset_ = rbsp_bit_offset_ % 8;
return true;
}
// advance by one byte through the NALU buffer, respecting the encoding of
// 00 00 03 => 00 00. Updates the state of current_nalu_byte_ to the new value.
bool ShellRBSPStream::ConsumeNALUByte() {
if (nalu_buffer_byte_offset_ >= nalu_buffer_size_) {
return false;
}
current_nalu_byte_ = nalu_buffer_[nalu_buffer_byte_offset_];
if (current_nalu_byte_ == 0x03 && number_consecutive_zeros_ >= 2) {
++nalu_buffer_byte_offset_;
current_nalu_byte_ = nalu_buffer_[nalu_buffer_byte_offset_];
number_consecutive_zeros_ = 0;
}
if (current_nalu_byte_ == 0) {
++number_consecutive_zeros_;
} else {
number_consecutive_zeros_ = 0;
}
++nalu_buffer_byte_offset_;
return true;
}
// return single bit in the LSb from the RBSP stream. Bits are read from MSb
// to LSb in the stream.
bool ShellRBSPStream::ReadRBSPBit(uint8* bit_out) {
DCHECK(bit_out);
// check to see if we need to consume a fresh byte
if (rbsp_bit_offset_ == 0) {
if (!ConsumeNALUByte()) {
return false;
}
}
// since we read from MSb to LSb in stream we shift right
uint8 bit = (current_nalu_byte_ >> (7 - rbsp_bit_offset_)) & 1;
// increment bit offset
rbsp_bit_offset_ = (rbsp_bit_offset_ + 1) % 8;
*bit_out = bit;
return true;
}
bool ShellRBSPStream::ReadRBSPByte(uint8* byte_out) {
DCHECK(byte_out);
// fast path for byte-aligned access
if (rbsp_bit_offset_ == 0) {
if (!ConsumeNALUByte()) {
return false;
}
*byte_out = current_nalu_byte_;
return true;
}
// at least some of the bits in the current byte will be included in this
// next byte, absorb them
uint8 upper_part = current_nalu_byte_;
// read next byte from stream
if (!ConsumeNALUByte()) {
return false;
}
// form the byte from the two bytes
*byte_out = (upper_part << rbsp_bit_offset_) |
(current_nalu_byte_ >> (8 - rbsp_bit_offset_));
return true;
}
} // namespace media
} // namespace cobalt