一、环境介绍操作系统:  ubuntu18.04  64位。X264版本:  x264-snapshot-20181217-2245博客的下载地址: https://download.csdn.net/download/xiaolong1126626497/12339693二、X264库编译安装参考这里: https://blog.csdn.net/xiaolong1126626497/article/details/104919095三、核心代码#include#include#include#include#include#include#include#include#include#include#include#include#include#include#include#include#include#include "include/x264.h"/*摄像头相关的全局变量声明区域*/#define UVC_VIDEO_DEVICE "/dev/video0" /*UVC摄像头设备节点*/int uvc_video_fd; /*存放摄像头设备节点的文件描述符*/unsigned char *video_memaddr_buffer[4]; /*存放的是摄像头映射出来的缓冲区首地址*/int Image_Width; /*图像的宽度*/int Image_Height; /*图像的高度*//*X264编码器相关的全局变量声明区域*/unsigned char *h264_buf=NULL;typedef struct{x264_param_t *param;x264_t *handle;x264_picture_t *picture; //说明一个视频序列中每帧特点x264_nal_t *nal;}Encoder;Encoder en;FILE *h264_fp; /*存放视频的文件*//*函数声明区域*/void X264_close_encoder(void); //关闭解码器/*函数功能: 处理退出的信号*/void exit_sighandler(int sig){/*关闭视频文件*/fclose(h264_fp);//关闭摄像头close(uvc_video_fd);//释放缓冲区free(h264_buf);//退出进程exit(1);}/*设置视频录制相关参数*/static int x264_param_apply_preset(x264_param_t *param, const char *preset){char *end;int i = strtol( preset, &end, 10 );if( *end == 0 && i >= 0 && i sizeof(x264_preset_names)/sizeof(*x264_preset_names)-1 )preset = x264_preset_names[i];/*快4*/if( !strcasecmp( preset, "ultrafast" ) ){param->i_frame_reference = 1;param->i_scenecut_threshold = 0;param->b_deblocking_filter = 0;param->b_cabac = 0;param->i_bframe = 0;param->analyse.intra = 0;param->analyse.inter = 0;param->analyse.b_transform_8x8 = 0;param->analyse.i_me_method = X264_ME_DIA;param->analyse.i_subpel_refine = 0;param->rc.i_aq_mode = 0;param->analyse.b_mixed_references = 0;param->analyse.i_trellis = 0;param->i_bframe_adaptive = X264_B_ADAPT_NONE;param->rc.b_mb_tree = 0;param->analyse.i_weighted_pred = X264_WEIGHTP_NONE;param->analyse.b_weighted_bipred = 0;param->rc.i_lookahead = 0;}/*快3*/else if( !strcasecmp( preset, "superfast" ) ){param->analyse.inter = X264_ANALYSE_I8x8|X264_ANALYSE_I4x4;param->analyse.i_me_method = X264_ME_DIA;param->analyse.i_subpel_refine = 1;param->i_frame_reference = 1;param->analyse.b_mixed_references = 0;param->analyse.i_trellis = 0;param->rc.b_mb_tree = 0;param->analyse.i_weighted_pred = X264_WEIGHTP_SIMPLE;param->rc.i_lookahead = 0;}/*快2*/else if( !strcasecmp( preset, "veryfast" ) ){param->analyse.i_me_method = X264_ME_HEX;param->analyse.i_subpel_refine = 2;param->i_frame_reference = 1;param->analyse.b_mixed_references = 0;param->analyse.i_trellis = 0;param->analyse.i_weighted_pred = X264_WEIGHTP_SIMPLE;param->rc.i_lookahead = 10;}/*快1*/else if( !strcasecmp( preset, "faster" ) ){param->analyse.b_mixed_references = 0;param->i_frame_reference = 2;param->analyse.i_subpel_refine = 4;param->analyse.i_weighted_pred = X264_WEIGHTP_SIMPLE;param->rc.i_lookahead = 20;}/*快速*/else if( !strcasecmp( preset, "fast" ) ){param->i_frame_reference = 2;param->analyse.i_subpel_refine = 6;param->analyse.i_weighted_pred = X264_WEIGHTP_SIMPLE;param->rc.i_lookahead = 30;}/*中等*/else if( !strcasecmp( preset, "medium" ) ){/* Default is medium */}/*慢速*/else if( !strcasecmp( preset, "slow" ) ){param->analyse.i_me_method = X264_ME_UMH;param->analyse.i_subpel_refine = 8;param->i_frame_reference = 5;param->i_bframe_adaptive = X264_B_ADAPT_TRELLIS;param->analyse.i_direct_mv_pred = X264_DIRECT_PRED_AUTO;param->rc.i_lookahead = 50;}else if( !strcasecmp( preset, "slower" ) ){param->analyse.i_me_method = X264_ME_UMH;param->analyse.i_subpel_refine = 9;param->i_frame_reference = 8;param->i_bframe_adaptive = X264_B_ADAPT_TRELLIS;param->analyse.i_direct_mv_pred = X264_DIRECT_PRED_AUTO;param->analyse.inter |= X264_ANALYSE_PSUB8x8;param->analyse.i_trellis = 2;param->rc.i_lookahead = 60;}else if( !strcasecmp( preset, "veryslow" ) ){param->analyse.i_me_method = X264_ME_UMH;param->analyse.i_subpel_refine = 10;}else{return -1;}return 0;}/*开始视频压缩*/void compress_begin(Encoder *en, int width, int height){en->param = (x264_param_t *) malloc(sizeof(x264_param_t));en->picture = (x264_picture_t *) malloc(sizeof(x264_picture_t));x264_param_default(en->param); //编码器默认设置/*订制编码器压缩的性能*/x264_param_apply_preset(en->param,"medium");en->param->i_width = width; //设置图像宽度en->param->i_height = height; //设置图像高度if((en->handle = x264_encoder_open(en->param)) == 0){return;}x264_picture_alloc(en->picture, X264_CSP_I420, en->param->i_width,en->param->i_height);en->picture->img.i_csp = X264_CSP_I420;en->picture->img.i_plane = 3;}/*结束压缩*/void compress_end(Encoder *en){if (en->picture){x264_picture_clean(en->picture);free(en->picture);en->picture = 0;}if(en->param){free(en->param);en->param = 0;}if(en->handle){x264_encoder_close(en->handle);}free(en);}/*初始化编码器*/void X264_init_encoder(int width,int height){compress_begin(&en,width,height);h264_buf=(uint8_t *)malloc(sizeof(uint8_t)*width*height*3);if(h264_buf==NULL)printf("X264缓冲区申请失败!\n");}/*压缩一帧数据*/int compress_frame(Encoder *en, int type, uint8_t *in, uint8_t *out) {x264_picture_t pic_out;int nNal = 0;int result = 0;int i = 0 , j = 0 ;uint8_t *p_out = out;en->nal=NULL;uint8_t *p422;char *y = en->picture->img.plane[0];char *u = en->picture->img.plane[1];char *v = en->picture->img.plane[2];//////////////////////////////////////////////////////////////////////////////////////int widthStep422 = en->param->i_width * 2;for(i = 0; i param->i_height; i += 2){p422 = in + i * widthStep422;for(j = 0; j 4){*(y++) = p422[j];*(u++) = p422[j+1];*(y++) = p422[j+2];}p422 += widthStep422;for(j = 0; j 4){*(y++) = p422[j];*(v++) = p422[j+3];*(y++) = p422[j+2];}}switch (type) {case 0:en->picture->i_type = X264_TYPE_P;break;case 1:en->picture->i_type = X264_TYPE_IDR;break;case 2:en->picture->i_type = X264_TYPE_I;break;default:en->picture->i_type = X264_TYPE_AUTO;break;}/*开始264编码*/if (x264_encoder_encode(en->handle, &(en->nal), &nNal, en->picture,&pic_out) 0) {return -1;}en->picture->i_pts++;for (i = 0; imemcpy(p_out, en->nal[i].p_payload, en->nal[i].i_payload);p_out += en->nal[i].i_payload;result += en->nal[i].i_payload;}return result;//return nNal;}//编码并写入一帧数据void encode_frame(uint8_t *yuv_frame){int h264_length = 0;//压缩一帧数据h264_length = compress_frame(&en, -1, yuv_frame, h264_buf);if(h264_length > 0){printf("h264_length=%d\n",h264_length);//写入视频文件fwrite(h264_buf, h264_length,1,h264_fp);}}/*函数功能: UVC摄像头初始化返回值: 0表示成功*/int UVCvideoInit(void){/*1. 打开摄像头设备*/uvc_video_fd=open(UVC_VIDEO_DEVICE,O_RDWR);if(uvc_video_fd0){printf("%s 摄像头设备打开失败!\n",UVC_VIDEO_DEVICE);return -1;}/*2. 设置摄像头的属性*/struct v4l2_format format;memset(&format,0,sizeof(struct v4l2_format));format.type=V4L2_BUF_TYPE_VIDEO_CAPTURE; /*表示视频捕获设备*/format.fmt.pix.width=320; /*预设的宽度*/format.fmt.pix.height=240; /*预设的高度*/format.fmt.pix.pixelformat=V4L2_PIX_FMT_YUYV; /*预设的格式*/format.fmt.pix.field=V4L2_FIELD_ANY; /*系统自动设置: 帧属性*/if(ioctl(uvc_video_fd,VIDIOC_S_FMT,&format)) /*设置摄像头的属性*/{printf("摄像头格式设置失败!\n");return -2;}Image_Width=format.fmt.pix.width;Image_Height=format.fmt.pix.height;printf("摄像头实际输出的图像尺寸:x=%d,y=%d\n",format.fmt.pix.width,format.fmt.pix.height);if(format.fmt.pix.pixelformat==V4L2_PIX_FMT_YUYV){printf("当前摄像头支持YUV格式图像输出!\n");}else{printf("当前摄像头不支持YUV格式图像输出!\n");return -3;}/*3. 请求缓冲区: 申请摄像头数据采集的缓冲区*/struct v4l2_requestbuffers req_buff;memset(&req_buff,0,sizeof(struct v4l2_requestbuffers));req_buff.count=4; /*预设要申请4个缓冲区*/req_buff.type=V4L2_BUF_TYPE_VIDEO_CAPTURE; /*视频捕获设备*/req_buff.memory=V4L2_MEMORY_MMAP; /*支持mmap内存映射*/if(ioctl(uvc_video_fd,VIDIOC_REQBUFS,&req_buff)) /*申请缓冲区*/{printf("申请摄像头数据采集的缓冲区失败!\n");return -4;}printf("摄像头缓冲区申请的数量: %d\n",req_buff.count);/*4. 获取缓冲区的详细信息: 地址,编号*/struct v4l2_buffer buff_info;memset(&buff_info,0,sizeof(struct v4l2_buffer));int i;for(i=0;i{buff_info.type=V4L2_BUF_TYPE_VIDEO_CAPTURE; /*视频捕获设备*/buff_info.memory=V4L2_MEMORY_MMAP; /*支持mmap内存映射*/if(ioctl(uvc_video_fd,VIDIOC_QUERYBUF,&buff_info)) /*获取缓冲区的详细信息*/{printf("获取缓冲区的详细信息失败!\n");return -5;}/*根据摄像头申请缓冲区信息: 使用mmap函数将内核的地址映射到进程空间*/video_memaddr_buffer[i]=mmap(NULL,buff_info.length,PROT_READ|PROT_WRITE,MAP_SHARED,uvc_video_fd,buff_info.m.offset);if(video_memaddr_buffer[i]==NULL){printf("缓冲区映射失败!\n");return -6;}}/*5. 将缓冲区放入采集队列*/memset(&buff_info,0,sizeof(struct v4l2_buffer));for(i=0;i{buff_info.type=V4L2_BUF_TYPE_VIDEO_CAPTURE; /*视频捕获设备*/buff_info.index=i; /*缓冲区的节点编号*/buff_info.memory=V4L2_MEMORY_MMAP; /*支持mmap内存映射*/if(ioctl(uvc_video_fd,VIDIOC_QBUF,&buff_info)) /*根据节点编号将缓冲区放入队列*/{printf("根据节点编号将缓冲区放入队列失败!\n");return -7;}}/*6. 启动摄像头数据采集*/int Type=V4L2_BUF_TYPE_VIDEO_CAPTURE;if(ioctl(uvc_video_fd,VIDIOC_STREAMON,&Type)){printf("启动摄像头数据采集失败!\n");return -8;}return 0;}/*函数功能: 采集摄像头的数据,并进行处理*/void *pthread_video_Data_Handler(void *dev){/*循环采集摄像头的数据*/struct pollfd fds;fds.fd=uvc_video_fd;fds.events=POLLIN;struct v4l2_buffer buff_info;memset(&buff_info,0,sizeof(struct v4l2_buffer));int index=0; /*表示当前缓冲区的编号*/printf("摄像头开始传输数据.......\n");while(1){/*1. 等待摄像头采集数据*/poll(&fds,1,-1);/*2. 取出一帧数据: 从采集队列里面取出一个缓冲区*/buff_info.type=V4L2_BUF_TYPE_VIDEO_CAPTURE; /*视频捕获设备*/ioctl(uvc_video_fd,VIDIOC_DQBUF,&buff_info); /*从采集队列取出缓冲区*/index=buff_info.index;//printf("采集数据的缓冲区的编号:%d\n",index);/*3. 处理数据: 进行H264编码*///video_memaddr_buffer[index]; /*当前存放数据的缓冲区地址*//*编码一帧数据*/encode_frame(video_memaddr_buffer[index]);/*4. 将缓冲区再次放入采集队列*/buff_info.memory=V4L2_MEMORY_MMAP; /*支持mmap内存映射*/buff_info.type=V4L2_BUF_TYPE_VIDEO_CAPTURE; /*视频捕获设备*/buff_info.index=index; /*缓冲区的节点编号*/ioctl(uvc_video_fd,VIDIOC_QBUF,&buff_info); /*根据节点编号将缓冲区放入队列*/}}int main(int argc,char **argv){if(argc!=2){printf("./app \n");return 0;}pthread_t thread;/*绑定将要捕获的信号*/signal(SIGINT,exit_sighandler);signal(SIGSEGV,exit_sighandler);signal(SIGPIPE,SIG_IGN);/*1. 初始化摄像头*/if(UVCvideoInit()!=0){printf("摄像头数据采集客户端:初始化摄像头失败!\n");exit(1);}/*2. 初始化编码器*/X264_init_encoder(Image_Width,Image_Height);/*3.创建存放视频的文件*/h264_fp=fopen(argv[1],"wa+");if(h264_fp==NULL){printf("文件创建失败!\n");exit(1);}/*4. 创建线程采集摄像头数据并编码*/pthread_create(&thread,NULL,pthread_video_Data_Handler,NULL);//设置线程的分离属性pthread_detach(thread);while(1){}}四、编译方法CC=gccall:$(CC) x264_VideoEncode.c -o x264_video_encode -lx264 -L./lib -lpthread -lm -ldl使用的是静态库链接编译。五、运行示例$ ./x264_video_encode 123.x264在本地生成一个123.x264文件,可以使用mplayer或者vlc播放器播放。
12-24 07:21