h264测试文件--从h.264文件读取单独帧模拟视频流

本文介绍了一种使用C语言从H264文件中读取并解析NAL单元的方法,实现了视频流的高效处理。通过定义各种NAL类型,如IDR、SEI、SPS和PPS等,代码能够准确识别并统计不同类型的NAL单元,适用于视频服务器和流媒体应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

测试服务器需要一个h264流,从h264文件读取流的函数实现: c语言版本

注意需要h264文件开头即为00 00 00 01

/***

***20190828 canok

*** output: complete frames

**/

#include<stdio.h>
#include<stdlib.h>	
#include <unistd.h>
#include <string.h>
#define MIN(a,b) ((a)<(b)?(a):(b))

typedef unsigned char   uint8_t;     //无符号8位数

#define NALU_TYPE_SLICE 1
#define NALU_TYPE_DPA 2
#define NALU_TYPE_DPB 3
#define NALU_TYPE_DPC 4
#define NALU_TYPE_IDR 5
#define NALU_TYPE_SEI 6
#define NALU_TYPE_SPS 7
#define NALU_TYPE_PPS 8
#define NALU_TYPE_AUD 9
#define NALU_TYPE_EOSEQ 10
#define NALU_TYPE_EOSTREAM 11
#define NALU_TYPE_FILL 12

#define CACH_LEN (1024*1024)//缓冲区不能设置太小,如果出现比某一帧比缓冲区大,会被覆盖掉一部分
static uint8_t *g_cach[2] = {NULL,NULL};
static FILE* fp_inH264 = NULL;
static int icach = 0;
static int ioffset = 0;
static int bLoop = 0;
int init()
{	
	if(g_cach[0] == NULL)
	{
		g_cach[0] = (uint8_t*)malloc(CACH_LEN);
	}
	if(g_cach[1] == NULL)
	{
		g_cach[1] = (uint8_t*)malloc(CACH_LEN);
	}
	
	if(fp_inH264 == NULL)
	{
		fp_inH264 = fopen("./test.h264","r");
		if(fp_inH264 == NULL)
		{
			printf("fope erro [%d%s]\n",__LINE__,__FUNCTION__);
			return -1;
		}
	}
	
	if(fread(g_cach[icach], 1,CACH_LEN,fp_inH264 )<CACH_LEN)
	{
		printf("intpufile too short . to decrease CACH_LEN [%d%s]\n",__LINE__,__FUNCTION__);
		return -1;
	}
	return 0;
}
int deinit()
{
	if(g_cach[0])
	{
		free(g_cach[0]);
		g_cach[0] = NULL;
	}
	if(g_cach[1])
	{
		free(g_cach[1]);
		g_cach[1] = NULL;
	}
	
	if(fp_inH264)
	{
		fclose(fp_inH264);
		fp_inH264 = NULL;
	}
	
	
}
static int I_count =0;
static int PB_count = 0;
static int All_count = 0;
static int SPS_count =0;
static int PPS_count =0;
static int AUD_count =0;
int checkNal(uint8_t nalHeader)
{
	char type = nalHeader & ((1<<5)-1);
    switch(type)
	{
		case NALU_TYPE_SPS:
			PPS_count ++;
			printf("sps\n");
			break;
		case NALU_TYPE_PPS:
			SPS_count ++;
			printf("pps\n");
			break;
		case NALU_TYPE_IDR:
			I_count ++;
			printf("I slice !!!!!!!!!!!!!!\n");
			break;
		case NALU_TYPE_SLICE:
			PB_count ++;
			printf("B/P slice\n");
			break;
		case NALU_TYPE_AUD:
			AUD_count ++;
			printf("Delimiter==========\n");
			break;
		default:
			printf("type :%d\n",type);
	}
	return type;
}
int checkFlag(uint8_t *buffer, int offset) 
{
	static uint8_t mMark[4] = {0x00,0x00,0x00,0x01};
	return !memcmp(buffer+offset,mMark,4);
}
//获取一个Nal到 buf, bufLen表示缓冲区最大可以容纳的数据
//返回实际的帧数据长度
int getOneNal(uint8_t *buf, int bufLen)
{
	int i =0;
	int startpoint = ioffset;
	int endpoint = ioffset;
	for (i = ioffset+4; i <= CACH_LEN - 4; i++) {
		if (checkFlag(g_cach[icach], i)){
			startpoint = ioffset;
			endpoint = i;
			break;
		}
	}
	if(endpoint - startpoint > 0)
	{
		int dataLen = endpoint -startpoint;
		if(bufLen < dataLen)
		{
			printf("recive buffer too short , need %d byte!\n",dataLen);
		}
		memcpy(buf,g_cach[icach]+startpoint, MIN(dataLen,bufLen));
		ioffset = endpoint;
		
		return MIN(dataLen,bufLen);
	}
	else
	{
		int oldLen =CACH_LEN -startpoint;
		memcpy(g_cach[(icach+1)%2],g_cach[icach]+startpoint, oldLen );
		
		int newLen = 0;
		newLen = fread(g_cach[(icach+1)%2]+oldLen, 1,CACH_LEN -(oldLen),fp_inH264);
		if(newLen <CACH_LEN -(oldLen) )
		{
			if(bLoop)
			{
				fseek(fp_inH264,0,SEEK_SET);
				ioffset =0;
				icach =0;
				fread(g_cach[icach], 1,CACH_LEN,fp_inH264 );
				return getOneNal(buf,bufLen); 
			}
			else
			{
				if(newLen <=0 )
				{
					return -1;
				}
                                memset(g_cach[(icach+1)%2]+oldLen+newLen,0,CACH_LEN -(oldLen)-newLen);
				
			}
			
		}
		
		ioffset = 0;
		icach = (icach+1)%2;
		
		return getOneNal(buf,bufLen);
	}
	
}

int main()
{
	if(init())
	{
		return -1;
	}
	uint8_t *buffer = (uint8_t*)malloc(CACH_LEN);
	int len =0;
	FILE *fp_out = fopen("out.h264","w+");
	while((len = getOneNal(buffer,CACH_LEN) )> 0)
	{
		printf("get a Nal len:%8d-----",len);
		checkNal(buffer[4]);
		fwrite(buffer,1,len,fp_out);
	}
	fclose(fp_out);
	free(buffer);
	deinit();

	printf("All_count %d\n",All_count);
	printf("I_count %d\n",I_count);
	printf("PB_count %d\n",PB_count);
	printf("AUD_count %d\n",AUD_count);
	printf("SPS_count %d\n",SPS_count);
	printf("PPS_count %d\n",PPS_count);
}

注:上述代码中,I_count  PB_cout 并不能完全表示 有I_cout 个关键帧/非关键帧。 h264的NAL单元,用 00 00 00 01 或者 00 00 01 来分割,一个NAL单元中如果携带视频数据的话,就是携带一个slice 片, 这里统计的就是这个slice的个数。 一般情况下一帧视频数据 会存储在一个slice中,这个时候slice数量就和帧数量相等。 但是有时候视频压缩率低,比如高质量的宣传片等,一帧数据被分割成几个slice存储,这种情况下slice数量就比 帧数要大了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值