1 源代码如下:
int h264_enc_test()
{
void *handle;
FILE *fp_in, *fp_out;
int ret, i;
unsigned int width, height;
unsigned int frame_rate, bitrate, gop_num;
unsigned int intraqp, qpmax;
float gamma;
unsigned int num_slices;
unsigned int change_param[2], cur_pic_opt[2];
long size;
unsigned char *p_inbuf;
unsigned char *p_outbuf;
fp_in = fopen("//Storage Card//newsvga.yuv","rb");
if (fp_in == NULL) {
RETAILMSG(1,(L"File not found/n"));
return 0;
}
fp_out = fopen("//Storage Card//news_vga.264", "wb");
if (fp_out == NULL) {
RETAILMSG(1,(L"Cannot open the output file./n"));
return 0;
}
width = 640;
height = 480;
frame_rate = 25;
bitrate = 1000;
gop_num = 1;
intraqp = 10;
qpmax = 10;
gamma = 0.75;
//////////////////////////////////////
/// 1. Create new instance ///
/// (SsbSipH264EncodeInit) ///
//////////////////////////////////////
handle = SsbSipH264EncodeInit(width, height, frame_rate, bitrate, gop_num, intraqp, qpmax, gamma);
RETAILMSG(1,(L"/n HANDLE = 0x%X./n", (DWORD) handle));
if (handle == NULL) {
return 0;
}
////////////////////////////////////////////////////////////////////
/// 2. Set some configuration parameter for initialization ///
/// (SsbSipH264EncodeExe) ///
////////////////////////////////////////////////////////////////////
num_slices = 4;
// SsbSipH264EncodeSetConfig(handle, H264_ENC_SETCONF_NUM_SLICES, &num_slices);
////////////////////////////////////////////////
/// 3. Initialize the Encoder Instance ///
/// (SsbSipH264EncodeExe) ///
////////////////////////////////////////////////
if (SsbSipH264EncodeExe(handle) != SSBSIP_H264_ENC_RET_OK) {
RETAILMSG(1,(L"H.264 Encoder Instance Initialization Failed./n"));
return 0;
}
p_inbuf = SsbSipH264EncodeGetInBuf(handle, 0);
if (p_inbuf == NULL) {
RETAILMSG(1,(L"/n (ERROR)Input buffer is NULL/n"));
return 0;
}
for (i=0; i<650; i++) {
// Reading YUV frame
ret = fread(p_inbuf, 1, (width * height * 3) >> 1, fp_in);
if (ret != ((width * height * 3) >> 1)) {
break;
}
/*
if (i == 27) {
change_param[0] = H264_ENC_PARAM_GOP_NUM;
change_param[1] = 20;
SsbSipH264EncodeSetConfig(handle, H264_ENC_SETCONF_PARAM_CHANGE, &change_param);
change_param[0] = H264_ENC_PARAM_F_RATE;
change_param[1] = (2000 << 16) | 40000;
SsbSipH264EncodeSetConfig(handle, H264_ENC_SETCONF_PARAM_CHANGE, &change_param);
}
else if (i >= 49 && i <= 41) {
cur_pic_opt[0] = H264_ENC_PIC_OPT_IDR;
cur_pic_opt[1] = 1;
SsbSipH264EncodeSetConfig(handle, H264_ENC_SETCONF_CUR_PIC_OPT, &cur_pic_opt);
}
else if (i == 57) {
cur_pic_opt[0] = H264_ENC_PIC_OPT_SKIP;
cur_pic_opt[1] = 1;
SsbSipH264EncodeSetConfig(handle, H264_ENC_SETCONF_CUR_PIC_OPT, &cur_pic_opt);
}
else if (i == 46) {
cur_pic_opt[0] = H264_ENC_PIC_OPT_RECOVERY;
cur_pic_opt[1] = 1;
SsbSipH264EncodeSetConfig(handle, H264_ENC_SETCONF_CUR_PIC_OPT, &cur_pic_opt);
}
*/
ret = SsbSipH264EncodeExe(handle);
RETAILMSG(1,(L"/n Encode, ret=%d/n", ret));
p_outbuf = SsbSipH264EncodeGetOutBuf(handle, &size);
RETAILMSG(1,(L"/n Output Buf = 0x%X, size = %d/n", (DWORD) p_outbuf, size));
fwrite(p_outbuf, 1, size, fp_out);
}
fclose(fp_in);
fclose(fp_out);
RETAILMSG(1,(L"/n $$ Program ends./n"));
return 0;
}
2 代码分析:
H264的解码流程:
@函数Test_Display_H264:
1 打开源文件,返回文件描述符in_fd,函数open()
2 将文件的相关信息存放的结构体stat中,函数fstat(),比如文件的大小
3 将文件映射到内存中,函数mmap()返回首地址,in_addr
4 打开post processor,返回文件描述符,pp_fd
5 打开frame buffer,返回文件描述符fb_fd;
6 函数FrameExtractorInit,初始化帧提取器
FrameExtractorInit(FRAMEX_IN_TYPE_MEM, delimiter_h264, sizeof(delimiter_h264), 1);
参数:
FRAMEX_IN_TYPE_MEM:类型是枚举类型FRAMEX_IN_TYPE,指明输入文件的类型,在此说明是从内存读取文件,还可以是FRAMEX_IN_TYPE_FILE
delimiter_h264:类型是字符数组,有四个成员分别是0x00,0x00,0x00,0x01,与h264编码方式有关;
函数功能:
构建类型FRAMEX_CTX,并利用参数填充,最后返回其指针pFrameExCtx;
7 将文件在内存的起始地址,当前地址以及结束地址保存到类型为FRAMEX_STRM_PTR的量file_strm中;
8 函数FrameExtractorFirst,提取第一个帧?
FrameExtractorFirst(pFrameExCtx, &file_strm)
参数:
pFrameExCtx:初始化函数返回的指针
file_strm:包含文件地址信息
函数功能:
根据pFrameExCtx中保存的数据输入类型,选择进入函数:next_delimiter_mem(pCTX, (FRAMEX_STRM_PTR *) in, NULL, 0, NULL);
#函数next_delimiter_mem
#(FRAMEX_CTX *pCTX, FRAMEX_STRM_PTR *strm_ptr, unsigned char *outbuf, const int outbuf_size, int *n_fill)
#参数:
#pCTX:就是在帧提取器初始化函数返回的量:保存了文件输入类型以及一个4个字符的字符数组
#strm_ptr:保存了文件地址的指针
#函数功能:
#初始化一个字符数组queue[12],容量为12,依次取出源数据的前4个字符与pCTX中的比较,相等的话就将相应字符填充到queue[12]中,也就是说本次函数执行实现的就是将源数据的前四个个字符内容复制到queue[12]中,同时存储文件地址的量中的当前指针递增。(strm_ptr->p_cur++)
9 函数SsbSipH264DecodeInit()创建一个实例
返回值:
_MFCLIB_H264_DEC类型的指针
函数功能:
打开mfc设备,并将其映射到内存中,构建类型_MFCLIB_H264_DEC,并执行初始化填充,保存设备打开的描述符,内存地址,还有一个_MFCLIB_H264_DEC_MAGIC_NUMBER=0x92241002,
返回指向_MFCLIB_H264_DEC的指针handle
10 函数SsbSipH264DecodeGetInBuf,获取输入buffer
SsbSipH264DecodeGetInBuf(handle, nFrameLeng)
参数:
handle,创建实例是返回的类型_MFCLIB_H264_DEC,保存mfc设备的内存映射地址
nFrameLeng,初值为0;
函数功能:
构建类型统一体MFC_ARGS,应该是要传递给mfc的一些参数配置,并将mfc内存的首地址保存到此类型中,调用函数ioctl
ioctl(pCTX->hOpen, IOCTL_MFC_GET_LINE_BUF_ADDR, &mfc_args)
类型MFC_ARGS中的结构体MFC_GET_BUF_ADDR_ARG中会保存返回的数据:输出buffer的地址,输出buffer的大小,
返回输出buffer的地址
11 函数ExtractConfigStreamH264//H264 CONFIG stream extraction
ExtractConfigStreamH264(pFrameExCtx, &file_strm, pStrmBuf, INPUT_BUFFER_SIZE, NULL);
参数:
pFrameExCtx:初始化函数FrameExtractorInit返回的指向结构体FRAMEX_CTX的指针
file_strm:类型FRAMEX_STRM_PTR,保存文件映射到内存的首地址和结束地址
pStrmBuf:函数SsbSipH264DecodeGetInBuf返回的输出buffer的地址
INPUT_BUFFER_SIZE:宏定义:204800
结构体:H264_CONFIG_DATA,成员是长和宽,在此为NULL
函数功能://分析源文件获取总共的帧数,并返回nFrameLeng
(1)for循环(100)
(2)调用函数FrameExtractorPeek(pFrameExCtx, fp, frame_type, sizeof(frame_type), (int *)&nFrameSize);
frame_type:容量为10的字符数组,作为输出参数,函数实现就是读取源文件的前十个字符到frame_type中,并相应移动当前指针
(3)取返回数组中的元素frame_type[4]的弟八位,如果不是6,7或8退出函数
(4)函数FrameExtractorNext
#函数next_delimiter_mem
12 函数SsbSipH264DecodeExe(handle, nFrameLeng)
参数:
handle:decode实例化函数SsbSipH264DecodeInit()返回的指向类型_MFCLIB_H264_DEC的指针
根据参数对mfc初始化
调用函数ioctl(),会在mfc_args中返回信息,将这些信息传递给handle
13 函数SsbSipH264DecodeGetConfig(handle, H264_DEC_GETCONF_STREAMINFO, &stream_info);
获取stream的配置信息
参数:
H264_DEC_GETCONF_STREAMINFO:cmd类型
stream_info:类型是SSBSIP_H264_STREAM_INFO,将handle在函数SsbSipH264DecodeExe中获取的信息传递给此量和一个全局量g_stream_info
13 设置pp_param
pp_param的类型是结构体s3c_pp_params_t,是对pp的配置
调用ioctl将参数传递给pp
14 计算framebuffer的大小
fb_size = pp_param.dst_full_width * pp_param.dst_full_height * 2; // RGB565
fb_size = pp_param.dst_full_width * pp_param.dst_full_height * 4; // RGB888
并将fb映射到内存
15 设置OSD相关信息,此时在屏幕上就会画出一块巨型,准备显示了
16完成上面的以后就会进入解码环节:
#进入while(1),
#调用函数SsbSipH264DecodeExe,通知mfc开始解码
#函数SsbSipH264DecodeGetConfig(handle, H264_DEC_GETCONF_PHYADDR_FRAM_BUF, pYUVBuf),获取解码后的数据,输出buffer
与上面调用的区别是cmdType不同,调用ioctl,会在mfc_args中返回输出buffer的地址和输出buffer的大小
//////////////////////////
((unsigned int *) value)[0] = mfc_args.get_buf_addr.out_buf_addr;
((unsigned int *) value)[1] = mfc_args.get_buf_addr.out_buf_size;
//////////////////////////
#函数NextFrameH264,更新frame
#将输出buffer的地址传递给pp的配置结构体pp_param,调用ioctl设置pp,后处理并显示
#。。。。。下一帧解码
17 解码结束
deinit decode;
munmap;
close pp fb.
本文来自:http://www.crazycoder.cn/Bo-abstracts-selected/Article134077.html