data ConInt4 {
int4 value
}
data ExResult {
int value
int bits
}
data Offset {
int bytes
int bits
}
const int MBS_SIZE = 16
component provides H264Parser requires io.Output out, data.IntUtil iu, data.ByteUtil bu {
byte getFrameType(byte content[], int offset, int smlen)
{
if ((content[offset + smlen] & 0x1F) == 0x7)
{
return NALFrame.T_HEADER_SPS
}
else if ((content[offset + smlen] & 0x1F) == 0x6)
{
return NALFrame.T_HEADER_SEI
}
else if ((content[offset + smlen] & 0x1F) == 0x8)
{
return NALFrame.T_HEADER_PPS
}
else if ((content[offset + smlen] & 0x1F) == 0x5)
{
return NALFrame.T_FRAME_I
}
else if ((content[offset + smlen] & 0x1F) == 0x1)
{
return NALFrame.T_FRAME_P
}
else
{
}
return NALFrame.T_UNKNOWN
}
int isStartMarker(byte content[], int offset)
{
if ((offset + 4) < content.arrayLength)
{
if (content[offset] == 0 && content[offset+1] == 0 && content[offset+2] == 0 && content[offset+3] == 1)
{
return 4
}
}
else if ((offset + 3) < content.arrayLength)
{
if (content[offset] == 0 && content[offset+1] == 0 && content[offset+2] == 1)
{
return 3
}
}
return 0
}
NALFrame H264Parser:getNextFrame(byte content[], int offset)
{
if (offset >= content.arrayLength) return null
int smlen = isStartMarker(content, offset)
if (smlen == 0) throw new Exception("h264 frame start sequence not found")
int frameStart = offset
for (int i = offset + smlen; i < content.arrayLength; i++)
{
if ((smlen = isStartMarker(content, i)) != 0)
{
return new NALFrame(getFrameType(content, frameStart, smlen), frameStart, i - 1)
}
}
return new NALFrame(getFrameType(content, frameStart, smlen), frameStart, content.arrayLength - smlen - 1)
}
ExResult decodeExGo(byte content[], int offset, int fromBit)
{
//we're assuming 32-bit values as a maximum, though it's not clear that there is any maximum spec
// - we grab up to 5 bytes, into a 4-byte sequence, where we've shifted all the bits by the required amount
// - we then operate on that shifted volume
byte range[] = new byte[4]
if (fromBit == 0)
{
range =[] dana.sub(content, offset, offset + 4)
}
else
{
for (int i = 0; (i < 4) && (i + offset < content.arrayLength); i++)
{
range[i] = content[offset+i]
range[i] = range[i] << fromBit
int lshift = 8 - fromBit
byte nd = content[offset+i+1]
nd = nd >> lshift
range[i] = range[i] | nd
}
//TODO: if there weren't enough bytes in content to fill our int32, we need to right-shift everything by the missing bytes, and right-shift mask by the same amount
}
ConInt4 con = new ConInt4()
dana.serial(con) =[] range
int4 mask = 0x80000000
int count = 0
while (count < 31 && (con.value & mask) == 0)
{
int4 maskAdd = 0x80000000
count ++
maskAdd = maskAdd >> count
mask = mask | maskAdd
}
//'count' is how many bits are 0's on the front
int4 value = 0
int4 rshift = (31 - (count * 2))
mask = mask >> count
ConInt4 mcon = new ConInt4(mask)
con.value = con.value & mask
value = con.value >> rshift
value = value - 1
return new ExResult(value, (count * 2) + 1)
}
bool readBitFlag(byte content[], int offset, int fromBit)
{
byte c = content[offset]
byte mask = 0x80
mask = mask >> fromBit
byte val = c & mask
return (c & mask) != 0
}
void increaseOffset(Offset bb, int bits)
{
bb.bits += bits
while (bb.bits >= 8)
{
bb.bytes ++
bb.bits -= 8
}
}
SPSHeader H264Parser:parseSPS(byte content[], int offset)
{
SPSHeader result = new SPSHeader()
Offset bb = new Offset(offset, 0)
int smlen = isStartMarker(content, offset)
if (smlen == 0) throw new Exception("frame is not SPS (start marker)")
byte ft = getFrameType(content, offset, smlen)
if (ft != NALFrame.T_HEADER_SPS) throw new Exception("frame is not SPS (type)")
int ndx = offset + smlen + 1
result.profile = content[ndx]
ndx ++
byte constraints = content[ndx]
ndx ++
result.level = content[ndx]
ndx ++
bb.bytes = ndx
//now we're into exgo coded values...
ExResult exr
//param ID
exr = decodeExGo(content, bb.bytes, bb.bits)
increaseOffset(bb, exr.bits)
//frame no
exr = decodeExGo(content, bb.bytes, bb.bits)
increaseOffset(bb, exr.bits)
//cnt type
exr = decodeExGo(content, bb.bytes, bb.bits)
increaseOffset(bb, exr.bits)
//ref frames
exr = decodeExGo(content, bb.bytes, bb.bits)
increaseOffset(bb, exr.bits)
bool gapsInFrameNum = readBitFlag(content, bb.bytes, bb.bits)
increaseOffset(bb, 1)
exr = decodeExGo(content, bb.bytes, bb.bits)
result.width = (exr.value + 1) * MBS_SIZE
increaseOffset(bb, exr.bits)
exr = decodeExGo(content, bb.bytes, bb.bits)
result.height = (exr.value + 1) * MBS_SIZE
increaseOffset(bb, exr.bits)
bool mbsOnly = readBitFlag(content, bb.bytes, bb.bits)
increaseOffset(bb, 1)
bool direct8x8Inference = readBitFlag(content, bb.bytes, bb.bits)
increaseOffset(bb, 1)
result.crop = readBitFlag(content, bb.bytes, bb.bits)
increaseOffset(bb, 1)
if (result.crop)
{
exr = decodeExGo(content, bb.bytes, bb.bits)
result.cropL = exr.value
increaseOffset(bb, exr.bits)
exr = decodeExGo(content, bb.bytes, bb.bits)
result.cropR = exr.value
increaseOffset(bb, exr.bits)
exr = decodeExGo(content, bb.bytes, bb.bits)
result.cropT = exr.value
increaseOffset(bb, exr.bits)
exr = decodeExGo(content, bb.bytes, bb.bits)
result.cropB = exr.value
increaseOffset(bb, exr.bits)
}
bool vui = readBitFlag(content, bb.bytes, bb.bits)
increaseOffset(bb, 1)
if (vui)
{
bool aspectRatioPresent = readBitFlag(content, bb.bytes, bb.bits)
increaseOffset(bb, 1)
if (aspectRatioPresent)
{
//TODO
}
//TODO: parse the remainder of this info
}
return result
}
}