Chinaunix首页 | 论坛 | 博客
  • 博客访问: 2292898
  • 博文数量: 393
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 4178
  • 用 户 组: 普通用户
  • 注册时间: 2016-07-04 13:04
文章分类

全部博文(393)

文章存档

2020年(8)

2019年(24)

2018年(135)

2017年(158)

2016年(68)

我的朋友

分类: Android平台

2020-08-10 15:25:12

一、环境介绍

操作系统:  ubuntu18.04  64位。

X264版本:  x264-snapshot-20181217-2245

博客的下载地址: 

二、X264库编译安装

参考这里: https://blog.csdn.net/xiaolong1126626497/article/details/104919095

三、核心代码

  1. #include
  2. #include 
  3. #include
  4. #include
  5. #include
  6. #include
  7. #include
  8. #include
  9. #include
  10. #include
  11. #include
  12. #include
  13. #include
  14. #include
  15. #include
  16. #include
  17. #include
  18. #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 < en->param->i_height; i += 2)
  • {
  • p422 = in + i * widthStep422;
  • for(j = 0; j < widthStep422; j+=4)
  • {
  • *(y++) = p422[j];
  • *(u++) = p422[j+1];
  • *(y++) = p422[j+2];
  • }
  • p422 += widthStep422;
  • for(j = 0; j < widthStep422; 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; i < nNal; i++) {
  • memcpy(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_fd<0)
  • {
  • 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)
  • {

  • }
  • }
  • 四、编译方法

    1. CC=gcc
    2. all:
    3. $(CC) x264_VideoEncode.c -o x264_video_encode -lx264 -L./lib -lpthread -lm -ldl

    使用的是静态库链接编译。

    五、运行示例

    $ ./x264_video_encode 123.x264

    在本地生成一个123.x264文件,可以使用mplayer或者vlc播放器播放。

    阅读(436) | 评论(0) | 转发(0) |
    给主人留下些什么吧!~~