HomeForumSourceResearchGuide
Sign in to contribute to source. how it works
Component media.video.H264Parser by barry
expand copy to clipboardexpand
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)
		{
		byte b = content[offset + 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 (content[offset] == 0 && content[offset+1] == 0 && content[offset+2] == 1)
				{
				return 3
				}
			}
			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 smlenInit = smlen
		int frameStart = offset

		for (int i = offset + smlen; i < content.arrayLength; i++)
			{
			if ((smlen = isStartMarker(content, i)) != 0)
				{
				return new NALFrame(getFrameType(content, frameStart, smlenInit), frameStart, i - 1)
				}
			}
		
		return new NALFrame(getFrameType(content, frameStart, smlenInit), 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
		}

}
Revision history
To propose a new revision to this entity, use dana source put -uc your/new/version.dn -n media.video.H264Parser -m "reason for update" -u yourUsername
Version 2 (this version) by barry
Notes for this version: Fixes bugs in detecting frame boundaries and types
Version 1 by barry