zoukankan      html  css  js  c++  java
  • h264解码之自定义信息(SEI)

         针对h264的解析网上优秀得博文、帖子一抓一大把,我就不在这班门弄斧了,仅仅提取一些自己在用的过程中比较有用的信息,对于sei自定义信息字段,虽然网上信息很多,但不容易精确搜到,就像我之前曾遇到一篇对我个人非常有用的文章,但后面一直找不到,现在再次看到,就把部分提取了出来,记录下,方便自己后面再次用到方便,也看能不能帮助到部分码友。

        参考博客:https://www.jianshu.com/p/4d9120dfcd69

    NAL header

    起始码(暗红底色)"0x00000001"分割出来的比特流即是NAL unit,起始码紧跟的第一个字节(墨绿底色)是NAL header。上图“NAL header”一共出现了四个数值:

    • "0x06",此时NRI为"00B",NAL unit type为SEI类型。
    • “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,此时整个payload是47个字节。

    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了

    可参考:https://blog.csdn.net/ab7936573/article/details/74135909

    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 sizepayload typeuuid
    	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 sizepayload typeuuid
    	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;
    }
    
     
  • 相关阅读:
    asp程序部署在IIS上时报http 500 错误的解决方法
    .NET中的三种接口实现方式
    深入理解JavaScript系列(19):求值策略(Evaluation strategy)
    大叔手记(19):你真懂JavaScript吗?
    深入理解JavaScript系列(12):变量对象(Variable Object)
    深入理解JavaScript系列(18):面向对象编程之ECMAScript实现(推荐)
    大叔手记(18):利用Elmah和Google体验一把入侵的快感
    深入理解JavaScript系列(14):作用域链(Scope Chain)
    深入理解JavaScript系列(17):面向对象编程之概论
    深入理解JavaScript系列(15):函数(Functions)
  • 原文地址:https://www.cnblogs.com/SunkingYang/p/11049156.html
Copyright © 2011-2022 走看看