1,NAL单元结构体
typedef struct nalu_t
{
int startcodeprefix_len; //!< 起始码长度,对于参数集和图像中的第一个片,其长度为4,其它的长度是3
unsigned len; //!< NAL单元的长度,不包括起始码长度
unsigned max_size; //!< NAL Unit Buffer size NAL单元缓冲区大小
int forbidden_bit; //!< should be always FALSE 禁止位,一般情况下为0.
NaluType nal_unit_type; //!< NALU_TYPE_xxxx NAL单元的类型,5个比特位
NalRefIdc nal_reference_idc; //!< 当前NAL单元的优先级,2个比特位,值范围为0~3,值越大,表示NAL单元越重要,越需要优先受到保护。
byte *buf; //!< contains the first byte followed by the EBSP 指向字节型的指针,可以指向起始码的第一个字节。
uint16 lost_packets; //!< true, if packet loss is detected
#if (MVC_EXTENSION_ENABLE)
int svc_extension_flag; //!< should be always 0, for MVC
int non_idr_flag; //!< 0 = current is IDR
int priority_id; //!< a lower value of priority_id specifies a higher priority
int view_id; //!< view identifier for the NAL unit
int temporal_id; //!< temporal identifier for the NAL unit
int anchor_pic_flag; //!< anchor access unit
int inter_view_flag; //!< inter-view prediction enable
int reserved_one_bit; //!< shall be equal to 1
#endif
} NALU_t;
2,NAL单元内存分配函数代码
/*!
*************************************************************************************
* \brief
* Allocates memory for a NALU 为一个NAL单元分配内存
*
* \param buffersize
* size of NALU buffer NAL单元缓冲区的大小
*
* \return
* pointer to a NALU 返回的是一个指向NAL单元的指针
*************************************************************************************
*/
//这是一个返回指针值的函数。
NALU_t *AllocNALU(int buffersize)
{
NALU_t *n; //NALU类型的指针n
if ((n = (NALU_t*)calloc (1, sizeof (NALU_t))) == NULL)//用来存放NALU类型的数据,即为其中的一些变量开辟内存空间。
no_mem_exit ("AllocNALU: n");//分配内存失败 //calloc函数原型为calloc(size_t n,size_t size),用来动态分配内存空间,并初始化内存为0,malloc不初始化
n->max_size=buffersize;
if ((n->buf = (byte*)calloc (buffersize, sizeof (byte))) == NULL)//开辟一段内存,用于存放NAL单元,类型是字节型的,将内存的首地址赋给n->buf。
{
free (n);//如果存放NAL单元的内存分配不成功,释放结构体NALU_t中的变量占据的内存空间。
no_mem_exit ("AllocNALU: n->buf");
}
return n;
}
3,释放一个NAL单元的函数
/*!
*************************************************************************************
* \brief
* Frees a NALU 释放一个NALU单元
*
* \param n
* NALU to be freed 需要被释放的NAL单元
* 先释放一个NALU占据的内存空间,再释放NALU结构体中的变量占据的内存空间
*************************************************************************************
*/
void FreeNALU(NALU_t *n)
{
if (n != NULL)
{
if (n->buf != NULL)
{
free(n->buf);
n->buf=NULL;
}
free (n);
}
}
4,编码端比特流结构体
struct bit_stream_enc
{
int buffer_size; //!< Buffer size 缓冲区的大小,是整形
int byte_pos; //!< current position in bitstream;
int bits_to_go; //!< current bitcounter
int stored_byte_pos; //!< storage for position in bitstream;
int stored_bits_to_go; //!< storage for bitcounter
int byte_pos_skip; //!< storage for position in bitstream;
int bits_to_go_skip; //!< storage for bitcounter
int write_flag; //!< Bitstream contains data and needs to be written
byte byte_buf; //!< current buffer for last written byte 最后一个已经写入字节的所在的缓冲区,类型为字节型
byte stored_byte_buf; //!< storage for buffer of last written byte
byte byte_buf_skip; //!< current buffer for last written byte
byte *streamBuffer; //!< actual buffer for written bytes
#if TRACE
Boolean trace_enabled;
#endif
};
以下是一段编码后的原始数据(SODB)在比特流缓冲区中的示意图
图1
以下是比特流结构体中变量解释以及和图1的对应关系
int buffer_size 缓冲区大小 可能是10个byte,也可能是20个byte,也可能是10KB…………这由内存分配决定。
int byte_pos 当前比特流指针指向的是第几个字节 第6个
int bits_to_go 保证字节对齐的比特数 4个,还需要4个比特,才能保证最后一个是字节对齐的。
以下两个变量都是储存上面最后两个变量的值的,相当于一个中间临时变量
int stored_byte_pos; //!< storage for position in bitstream; 用来保存比特流中字节的位置
int stored_bits_to_go; //!< storage for bitcounter 保存保证字节对齐的比特数
byte byte_buf; //!< current buffer for last written byte 最后一个字节的十进制值,范围在0~255 图1中的二进制值为1011,十进制值为11
byte stored_byte_buf; //!< storage for buffer of last written byte 保存最后一个字节的十进制值
byte byte_buf_skip; //!< current buffer for last written byte
byte *streamBuffer; //!< actual buffer for written bytes 指向缓冲区最后一个字节的指针 图1中的 streamBuffer,它是一个指针,值是一个地址。
下面来分析SODB封装成RBSP的函数
void SODBtoRBSP(Bitstream *currStream)
{
//byte *dddf=NULL;
currStream->byte_buf <<= 1;//比特流缓冲区中最后一个未对齐的字节左移1位,目的是补1
currStream->byte_buf |= 1;//空出的比特位补1
currStream->bits_to_go--;//离字节对齐还差bits_to_go-1个
currStream->byte_buf <<= currStream->bits_to_go;//再左移bits_to_go个比特位,相当于补了bits_to_go个0
currStream->streamBuffer[currStream->byte_pos++] = currStream->byte_buf;//将字节对齐的字节的值放入缓冲区中,streamBuffer右移一位
//dddf=&currStream->streamBuffer[--currStream->byte_pos];
currStream->bits_to_go = 8;//指针右移一位后,指向下一个字节,其中的内容为空,还未输入比特,相当于差8个比特才能字节对齐
currStream->byte_buf = 0;//当前指针指向的字节,十进制值为0.
}
代码分析如下:
/*!
************************************************************************
* \brief
* This function add emulation_prevention_three_byte for all occurrences
* of the following byte sequences in the stream
* 0x000000 -> 0x00000300
* 0x000001 -> 0x00000301
* 0x000002 -> 0x00000302
* 0x000003 -> 0x00000303
*
* \param NaluBuffer
* pointer to target buffer 指向封装后的EBSP缓冲区的首地址
* \param rbsp
* pointer to source buffer 指向原RBSP缓冲区的首地址
* \param rbsp_size
* Size of source RBSP中有多少个字节,即大小
* \return
* Size target buffer after emulation prevention.
*
************************************************************************
*/
//本质上:就是将一组无符号字符型数据(16进制的值),首地址为rbsp,复制到一个新的缓冲区内,
//首地址为NaluBuffer,依次复制的时候,如果遇到连续的两个字节0+一个字节0,或一个字节1,或一个字节2,或一个字节3.
//即在这三个字节的第一个字节前面插入0x03。相应的新的缓冲区大小可能会增加。
//这个函数中好的技巧是用了count来表征某个特殊值的个数。先判断个数
int RBSPtoEBSP(byte *NaluBuffer, unsigned char *rbsp, int rbsp_size)
{
int j = 0;//用来记录在引入防竞争机制后,缓冲区的长度
int count = 0;//用来统计在RBSP中,字节连续为0的个数,比如 0x00 00,此时就是两个连续的0字节
int i;
for(i = 0; i < rbsp_size; i++)
{
if(count == ZEROBYTES_SHORTSTARTCODE && !(rbsp[i] & 0xFC))// 0xFC的二进制是11111100,只有当rbsp[i]=0x00,0x01,0x02,0x03,
{ //才能使(rbsp[i] & 0xFC)=0,!(rbsp[i] & 0xFC)=1,如果cout==2,
NaluBuffer[j] = 0x03; //就执行条件内的语句
j++; //缓冲区长度加1
count = 0; //插入之后,将0字节个数归0
}
NaluBuffer[j] = rbsp[i]; //将当前的字节复制到新的缓冲区中。
if(rbsp[i] == 0x00) //统计字节连续为0的个数。
count++;
else
count = 0; //如果当前字节不为0,0字节个数归0
j++;
}
return j;
}