492 lines
11 KiB
C++
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;
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|