当从avc编码器拿到一帧数据时,有时会考虑按NALU头进行拆分,示例代码如下:
P帧和B帧只有一个NALU头,所以不再需要拆分,主要是I帧,一般包含7,8,6,5四部分。
/* find the size of a frame.
* @data the memory of avc bit stream, this should be start with [00 00 00 01].
* @data_size the length of the bit stream
* @start the next start index of @data
* @return return the length of the current frame which start with @data[start].
**/
static int find_nal(unsigned char *data, int data_size, int start)
{
int index = start + 4;
// I frame is the last frame
if((data[start + 4] & 0x1f) == 5) {
return (data_size - start);
}
while(index < (data_size - start)) {
if(data[index] == 0 &&
data[index + 1] == 0 &&
data[index + 2] == 0 &&
data[index + 3] == 1) {
return index;
}
index++;
}
return -1;
}
/* parse a I frame and send them partly.
* @chID the index of encoders
* @data an avc I frame which maybe include 7, 8, 6, 5
* @data_size the length of the I frame
**/
static void parseIFrameAndSend(int chID, unsigned char *data, int data_size)
{
int sps_size = 0;
int pps_size = 0;
int idr_size = 0;
int invaild_size = 0;
int start = 0;
while(1) {
int next_start = find_nal(data, data_size, start);
if(next_start == -1)
break;
if((data[start+4] & 0x1f) == 7) {
sps_size = next_start - start;
}
if((data[start+4] & 0x1f) == 8) {
pps_size = next_start - start;
}
if((data[start+4] & 0x1f) == 6) { // the frame will be dropped
invaild_size = next_start - start;
}
if((data[start+4] & 0x1f) == 5) {
idr_size = next_start;
break;
}
start = next_start;
}
int i = 0;
int size = 0;
int frame_size[3] = {sps_size, pps_size, idr_size};
start = 0;
for(i = 0; i < 3; i++) {
size = frame_size[i];
unsigned char *head = (unsigned char *)malloc(sizeof(chID) + size);
if(NULL == head){
printf("alloc memory for encoder stream failed\n");
return ;
}
memcpy(head, &chID, sizeof(chID));
memcpy(head + sizeof(chID), data + start, size); // copy the current frame to the allocated buffer
// send head by socket
if(gServerHandle != NULL)
Socket_SendData(gServerHandle, head, (sizeof(chID) + size));
start += size;
#if 1
if(i == 1) // skip 6 frame
start += invaild_size;
#endif
}
}
假设当前encoder只生成I帧和P帧,使用代码如下:
void send_enc_stream(int chID, void *data, int data_size)
{
unsigned char *p = (unsigned char *)data;
if((p[4] & 0x1f) == 1) // get a P frame
{
unsigned char *head = (unsigned char *)malloc(sizeof(chID) + data_size);
if(NULL == head){
printf("alloc memory for encoder stream failed\n");
return ;
}
memcpy(head, &chID, sizeof(chID));
memcpy(head + sizeof(chID), data, data_size); // copy the current frame to the allocated buffer
// send head by socket
if(gServerHandle != NULL)
Socket_SendData(gServerHandle, head, (sizeof(chID) + data_size));
}
else if((p[4] & 0x1f) == 7) // get an I frame
{
parseIFrameAndSend(chID, data, data_size);
}
}