Files
UnrealEngine/Engine/Source/Runtime/AVEncoder/Private/Decoders/vdecmpeg4/M4Bitstream.h
2025-05-18 13:04:45 +08:00

492 lines
11 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "vdecmpeg4.h"
#include "vdecmpeg4_Stream.h"
#include "M4Memory.h"
namespace vdecmpeg4
{
#if 1 // Little endian platform
inline uint32 XSWAP(uint32 value)
{
return (value << 24) | ((value & 0xff00) << 8) | ((value >> 8) & 0xff00) | (value >> 24);
}
#else
#define XSWAP(a) (a)
#endif
class M4Bitstream
{
public:
//! Construction of bitstream object
M4Bitstream();
//! Destructor
~M4Bitstream();
//! Set memory allocator
void setMemoryHook(M4MemHandler& memSys);
//! Link bitstream to provided byte sequence
void init(const uint8* bitstream, uint32 length);
//! Link bitstream to stream interface
void init(VIDStreamIO* pStream, uint32 streamBufferBytes);
//! Show indicated bit from stream without moving file position
uint32 show(const uint32 bits);
//! Skip indicated bits in stream
void skip(const uint32 bits);
//! Align bitstream to next byte boundary
void align();
//! implements next_start_code()
void nextStartCode();
void nextResyncMarker();
void skipResyncMarker();
//! implements nextbits_bytealigned()
uint32 showBitsByteAligned(const uint32 bits);
bool validStuffingBits();
//! Return indicated # of bits from stream
uint32 getBits(const uint32 bits);
//! Return 1 bit from stream
uint32 getBit();
//! Check eof
bool isEof();
//! Clear the byte counter
void totalBitsClear();
//! Read the byte counter
uint32 totalBitsGet() const;
//! Return memory base addres of stream
const uint32* getBaseAddr() const;
private:
//! Read from stream in buffered way
VIDError ReadStreamBuffered(uint32& value);
//! Copy-constructor is private to prevent usage
M4Bitstream(const M4Bitstream& pObj);
//! Assignment operator is private to prevent usage
const M4Bitstream& operator=(const M4Bitstream& pObj);
uint32 mAWord;
uint32 mBWord;
uint32 mPos;
uint32* mTail;
uint32 mLength; //!< length of bitstream in bytes
uint32* mStart; //!< pointer to start of data
VIDStreamIO* mpStreamIO; //!< pointer to stream interface for 'pulling' data
uint8* mpInternalBuffer;
uint32 mInternalBufferBytes;
uint32 mInternalBufferCurrentBytes;
uint32 mInternalBufferIndex;
M4MemHandler* mpMemSys; //!< memory system used for allocations
uint32 mTotalBits; //!< total consumed bits (reset via M4Bitstream::totalBitsClear())
};
/******************************************************************************
* INLINE FUNCTIONS
******************************************************************************/
// ----------------------------------------------------------------------------
/**
* Clear byte counter for statistics
*
* @return none
**/
inline void M4Bitstream::totalBitsClear()
{
mTotalBits = 0;
}
// ----------------------------------------------------------------------------
/**
* Ctor
**/
inline M4Bitstream::M4Bitstream()
: mTail(nullptr)
, mLength(0)
, mStart(nullptr)
, mpStreamIO(nullptr)
, mpInternalBuffer(nullptr)
, mInternalBufferBytes(0)
, mInternalBufferCurrentBytes(0)
, mInternalBufferIndex(0)
, mpMemSys(nullptr)
, mTotalBits(0)
{
}
// ----------------------------------------------------------------------------
/**
* Destructor
**/
inline M4Bitstream::~M4Bitstream()
{
if (mpInternalBuffer && mpMemSys)
{
mpMemSys->free(mpInternalBuffer);
}
}
// ----------------------------------------------------------------------------
/**
* Set memory hook for bitstream
*
* @param memSys
*
* @return none
**/
inline void M4Bitstream::setMemoryHook(M4MemHandler& memSys)
{
mpMemSys = &memSys;
}
/*!
******************************************************************************
* Link bitstream to indicated byte sequence
*
* @param bitstream ptr to input bytes
* @param length number of bytes in input stream
******************************************************************************
*/
inline void M4Bitstream::init(const uint8* bitstream, uint32 length)
{
mStart = mTail = (uint32*)bitstream;
// initialize 'working' words with 1st 64 bits from stream
mAWord = XSWAP(mStart[0]);
mBWord = XSWAP(mStart[1]);
mPos = 0;
mLength = length;
mpStreamIO = nullptr;
if (mpInternalBuffer)
{
mpMemSys->free(mpInternalBuffer);
}
mpInternalBuffer = nullptr;
totalBitsClear();
}
// ----------------------------------------------------------------------------
/**
* Read uint32 via stream iterator
*
* @return VIDError
**/
inline VIDError M4Bitstream::ReadStreamBuffered(uint32& value)
{
if (mInternalBufferIndex >= mInternalBufferCurrentBytes)
{
mInternalBufferIndex = 0;
VIDStreamResult result = mpStreamIO->Read(mpInternalBuffer, mInternalBufferBytes, mInternalBufferCurrentBytes);
if (result != VID_STREAM_OK)
{
value = 0;
mInternalBufferCurrentBytes = 0;
return result == VID_STREAM_EOF ? VID_ERROR_STREAM_EOF : VID_ERROR_STREAM_ERROR;
}
else
{
// Check returned data is multiple of 4bytes
M4CHECK((mInternalBufferCurrentBytes & 0x3) == 0 );
// Check if returned ANY result
M4CHECK( mInternalBufferCurrentBytes > 0 );
}
}
value = *((uint32*)(mpInternalBuffer + mInternalBufferIndex));
mInternalBufferIndex += 4;
return VID_OK;
}
// ----------------------------------------------------------------------------
/**
* Handle stream via stream iterator
*
* @param pStream stream to use
* @param streamBufferBytes size of intermediate buffer
**/
inline void M4Bitstream::init(VIDStreamIO* pStreamIO, uint32 streamBufferBytes)
{
M4CHECK(streamBufferBytes > 0);
mpStreamIO = pStreamIO;
if (mpInternalBuffer)
{
mpMemSys->free(mpInternalBuffer);
}
mInternalBufferBytes = streamBufferBytes;
mpInternalBuffer = (uint8*)mpMemSys->malloc(mInternalBufferBytes);
mInternalBufferCurrentBytes = 0;
mInternalBufferIndex = 0;
// Read initial chunk of data
uint32 value;
ReadStreamBuffered(value);
mAWord = XSWAP(value);
ReadStreamBuffered(value);
mBWord = XSWAP(value);
// initialize 'working' words with 1st 64 bits from stream
mPos = 0;
mLength = 0;
mStart = mTail = nullptr;
totalBitsClear();
}
/*!
******************************************************************************
* Read # bits from stream, but DON'T move stream pointer
*
* @param bits number of bits to read
*
* @return Bits to use 'together'
******************************************************************************
*/
inline uint32 M4Bitstream::show(const uint32 bits)
{
M4CHECK(bits>0 && bits<=32);
uint32 res2;
int32 nbit = (int32)bits + int32(mPos - 32);
if (nbit > 0)
{
res2 = ((mAWord & (0xffffffff >> mPos)) << nbit) | (mBWord >> (32 - nbit));
}
else
{
res2 = (mAWord & (0xffffffff >> mPos)) >> (32 - mPos - bits);
}
return res2;
}
/*!
******************************************************************************
* Skip indicated number of bits in stream
*
* @param bits number of bits to skip
******************************************************************************
*/
inline void M4Bitstream::skip(uint32 bits)
{
M4CHECK(bits>0 && bits<=32);
mPos += bits; // advance bit position pointer
mTotalBits += bits;
if (mPos >= 32)
{
mAWord = mBWord;
if (mpStreamIO)
{
uint32 value;
ReadStreamBuffered(value);
mBWord = XSWAP(value);
}
else
{
mBWord = XSWAP(*(mTail+2));
mTail++;
}
mPos -= 32;
}
}
// ----------------------------------------------------------------------------
/**
* Check end of file operation
*
* @return none
**/
inline bool M4Bitstream::isEof()
{
return mpStreamIO ? mpStreamIO->IsEof() : ((8 * 4 * (mTail - mStart) + mPos)>>3) >= mLength;
}
/*!
******************************************************************************
* Align bitstream pointer to next byte in stream
******************************************************************************
*/
inline void M4Bitstream::align()
{
uint32 remainder = mPos & 7;
if (remainder)
{
skip(8 - remainder);
}
}
// ----------------------------------------------------------------------------
/**
* implements next_start_code()
*/
inline void M4Bitstream::nextStartCode()
{
uint32 zeroBit = getBit();
(void)zeroBit;
M4CHECK(zeroBit == 0);
while((mPos & 7) != 0)
{
uint32 oneBit = getBit();
(void)oneBit;
M4CHECK(oneBit == 1);
}
}
inline void M4Bitstream::nextResyncMarker()
{
// This is the same as nextStartCode(), actually...
nextStartCode();
}
inline void M4Bitstream::skipResyncMarker()
{
// Resync marker is at least 16 zero bits followed by a one.
// The number of zero bits varies depending on the VOP type, but syntactically it is always the same
// so we can simply skip over zero bits and one 1 bit.
while(getBit() == 0)
{
}
}
// ----------------------------------------------------------------------------
/**
* implements nextbits_bytealigned()
*
* @param bits
*
* @return
*/
inline uint32 M4Bitstream::showBitsByteAligned(const uint32 bits)
{
M4CHECK(bits>0 && bits<=23);
uint32 rem = mPos & 7;
if (rem == 0)
{
// Are the next 8 bits an end marker?
if (show(8) == 0x7f)
{
// Yes. This must be skipped and the next n bits returned.
uint32 v = show(8 + bits);
return v & (0xffffffff >> (32 - bits));
}
else
{
return show(bits);
}
}
else
{
uint32 v = show(8 - (mPos & 7) + bits);
return v & (0xffffffff >> (32 - bits));
}
}
// ----------------------------------------------------------------------------
/**
* implements valid_stuffing_bits()
*
* @return
*/
inline bool M4Bitstream::validStuffingBits()
{
static const uint8 stuffing[8] = { 0x7f, 0x3f, 0x1f, 0x0f, 0x07, 0x03, 0x01, 0x00 };
uint32 rb = mPos & 7;
if (rb == 0)
{
return show(8) == 0x7f;
}
else
{
uint32 rem = show(8 - rb);
return rem == stuffing[rb];
}
}
/*!
******************************************************************************
* Return indicated # of bits from stream and move foreward in stream
*
* @param bits Number of bits to get
*
* @return requested 'bitword'
******************************************************************************
*/
inline uint32 M4Bitstream::getBits(const uint32 bits)
{
uint32 ret = show(bits);
skip(bits);
return ret;
}
/*!
******************************************************************************
* Return NEXT bit from stream
******************************************************************************
*/
inline uint32 M4Bitstream::getBit()
{
return getBits(1);
}
// ----------------------------------------------------------------------------
/**
* Return bits read
*
* @return bit count since last totalBitsClear
**/
inline uint32 M4Bitstream::totalBitsGet() const
{
return mTotalBits;
}
// ----------------------------------------------------------------------------
/**
* Return pointer to stream data in memory
**/
inline const uint32* M4Bitstream::getBaseAddr() const
{
return (const uint32*)mStart;
}
}