h264解碼之自定義資訊(SEI)
針對h264的解析網上優秀得博文、帖子一抓一大把,我就不在這班門弄斧了,僅僅提取一些自己在用的過程中比較有用的資訊,對於sei自定義資訊欄位,雖然網上資訊很多,但不容易精確搜到,就像我之前曾遇到一篇對我個人非常有用的文章,但後面一直找不到,現在再次看到,就把部分提取了出來,記錄下,方便自己後面再次用到方便,也看能不能幫助到部分碼友。
NAL header
起始碼(暗紅底色)"0x00000001"分割出來的位元流即是NAL unit,起始碼緊跟的第一個位元組(墨綠底色)是NAL header。上圖“NAL header”一共出現了四個數值:
- "0x06",此時NRI為"00B",NAL unit type
- “0x67”,此時NRI為“11B”,NAL unit type為SPS型別。
- “0x68”,此時NRI為“11B”,NAL unit type為PPS型別。
- “0x65”,此時NRI為“11B”,NAL unit type為IDR影象。
SEI payload type
"0x06"後一個位元組為“0x05”(淡黃底色)是SEI payload type,即表徵SEI payload分析遵循user_data_unregistered()語法。
在國標中。sei payload type為5,為自定義訊息:如圖
SEI payload size
“0x05”後一個位元組為“0x2F”(淡藍底色)是SEI payload size
SEI payload uuid
"0x2F"隨後的16個位元組即為uuid,此時uuid為:
dc45e9bde6d948b7962cd820d923eeef
SEI payload content
由於payload size是47個位元組,除去16位元組的uuid,剩下31個位元組的content。由於content是字串,所以有結束符"0x00",有效的30個字元內容是
Zencoder Video Encoding System
rbsp trailing bits
47個payload位元組後的"0x80"(灰底色)即是rbsp trailing bits,在user_data_unregistered()裡面都是按位元組寫入的,所以此時的NAL unit結尾寫入的位元組一定是0x80。
下面開始解析一段含SEI資訊的H264資料,資料中包含,座標資訊,人員資訊,先把程式碼貼出來,再一一說明程式碼中的各個部分:
注:已經去除起始碼00 00 00 01了
unsigned char* data_buf = NULL;
if ((data[0] & 0x1F) == 6)//判斷是否是SEI
{
int nCount = 0;
unsigned char buf[1602] = { 0 };
int nType = 0;
unsigned char * sei = (unsigned char *)(data + 1);
nCount = size;
//獲取SEI資訊:payload size、payload type、uuid
int nsize = get_sei_buffer(sei, ((unsigned char*)data + size - sei), buf, &nCount, &nType);
if (5 == nType)//判斷為5,自定義訊息user_data_unregistered()
{
int nSd_Type = buf[0];
if (1 == nSd_Type)
{
unsigned int nSd_Num = buf[1];
m_nNumSize = nSd_Num;
//max 100
if (m_nNumSize > 100)
{
m_nNumSize = 100;
}
if (nSd_Num > 0)
{
for (int i = 0; i < m_nNumSize; i++)
{
BYTE buffer[8];
//解析人臉框座標
buffer[0] = buf[i * 16 + 2];
buffer[1] = buf[i * 16 + 3];
unsigned short number = (buffer[0] << 8) + buffer[1];
m_pFaceInfo[i].face_x = number;
buffer[2] = buf[i * 16 + 4];
buffer[3] = buf[i * 16 + 5];
number = (buffer[2] << 8) + buffer[3];
m_pFaceInfo[i].face_y = number;
buffer[4] = buf[i * 16 + 6];
buffer[5] = buf[i * 16 + 7];
number = (buffer[4] << 8) + buffer[5];
m_pFaceInfo[i].face_w = number;
buffer[6] = buf[i * 16 + 8];
buffer[7] = buf[i * 16 + 9];
number = (buffer[6] << 8) + buffer[7];
m_pFaceInfo[i].face_h = number;
//解析人員資訊
//buffer[8] = *buf++;
m_pFaceInfo[i].usrSex = buf[i * 16 + 10];
m_pFaceInfo[i].userAge = buf[i * 16 + 11];
m_pFaceInfo[i].userHairstyle = buf[i * 16 + 12];
m_pFaceInfo[i].userHat = buf[i * 16 + 13];
m_pFaceInfo[i].userBrow = buf[i * 16 + 16];
m_pFaceInfo[i].userGlasses = buf[i * 16 + 14];
m_pFaceInfo[i].userMask = buf[i * 16 + 17];
m_pFaceInfo[i].userRace = buf[i * 16 + 18];
}
}
}
}
return;
}
//獲取SEI資訊:payload size、payload type、uuid
int nsize = get_sei_buffer(sei, ((unsigned char*)data + size - sei), buf, &nCount, &nType);
if (5 == nType)//判斷為5,自定義訊息user_data_unregistered()
{
int nSd_Type = buf[0];
if (1 == nSd_Type)
{
unsigned int nSd_Num = buf[1];
m_nNumSize = nSd_Num;
//max 100
if (m_nNumSize > 100)
{
m_nNumSize = 100;
}
if (nSd_Num > 0)
{
for (int i = 0; i < m_nNumSize; i++)
{
BYTE buffer[8];
//解析人臉框座標
buffer[0] = buf[i * 16 + 2];
buffer[1] = buf[i * 16 + 3];
unsigned short number = (buffer[0] << 8) + buffer[1];
m_pFaceInfo[i].face_x = number;
buffer[2] = buf[i * 16 + 4];
buffer[3] = buf[i * 16 + 5];
number = (buffer[2] << 8) + buffer[3];
m_pFaceInfo[i].face_y = number;
buffer[4] = buf[i * 16 + 6];
buffer[5] = buf[i * 16 + 7];
number = (buffer[4] << 8) + buffer[5];
m_pFaceInfo[i].face_w = number;
buffer[6] = buf[i * 16 + 8];
buffer[7] = buf[i * 16 + 9];
number = (buffer[6] << 8) + buffer[7];
m_pFaceInfo[i].face_h = number;
//解析人員資訊
//buffer[8] = *buf++;
m_pFaceInfo[i].usrSex = buf[i * 16 + 10];
m_pFaceInfo[i].userAge = buf[i * 16 + 11];
m_pFaceInfo[i].userHairstyle = buf[i * 16 + 12];
m_pFaceInfo[i].userHat = buf[i * 16 + 13];
m_pFaceInfo[i].userBrow = buf[i * 16 + 16];
m_pFaceInfo[i].userGlasses = buf[i * 16 + 14];
m_pFaceInfo[i].userMask = buf[i * 16 + 17];
m_pFaceInfo[i].userRace = buf[i * 16 + 18];
}
}
}
}
return;
}
get_sei_buffer如下:
int get_sei_buffer(unsigned char * data, uint32_t size, unsigned char * buffer, int *count, int *nType)
{
unsigned char * sei = data;
int sei_type = 0;
unsigned sei_size = 0;
//payload type
do {
sei_type += *sei;
*nType = sei_type;
} while (*sei++ == 255);
//資料長度
do {
sei_size += *sei;
} while (*sei++ == 255);
//檢查UUID
static unsigned char uuid[] = { 0x73, 0x74, 0x64, 0x74, 0x73, 0x74, 0x64, 0x74, 0x73, 0x74, 0x64, 0x74, 0x73, 0x74, 0x64, 0x74 };
if (sei_size >= UUID_SIZE && sei_size <= (data + size - sei) &&
sei_type == 5 /*&& memcmp(sei, uuid, UUID_SIZE) == 0*/)
{
sei += UUID_SIZE;
sei_size -= UUID_SIZE;
if (buffer != NULL && count != NULL)
{
if (*count > (int)sei_size)
{
memcpy(buffer, sei, sei_size);
}
}
*count = sei_size;
return sei_size;
}
return -1;
}