一、V4L2采集YUYV视频数据
a) 打开V4L2设备并创建接收yuyv数据的文件
open_v4l2_device(const char *const devname)
video_obj.v4l2_fd=open(devname,O_RDWR)//打卡v4l2设备
fopen(name,"wb+")//创建yuyv数据接收文件
b) 设置视频格式,分辨率
set_v4l2_fmt(unsigned int format,unsigned int width,unsigned int height)
video_obj.fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
video_obj.fmt.fmt.pix.pixelformat = format;
video_obj.fmt.fmt.pix.width = width;
video_obj.fmt.fmt.pix.height = height;
ioctl(video_obj.v4l2_fd, VIDIOC_S_FMT, &video_obj.fmt)
c) 获取当前的格式和分辨率,查看设置是否生效
struct v4l2_format fmt;
fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
ioctl(video_obj.v4l2_fd, VIDIOC_G_FMT, &fmt)
d) 设置帧率
set_v4l2_param(unsigned int num,unsigned int deno)
struct v4l2_streamparm param;
memset(¶m, 0, sizeof(struct v4l2_streamparm));
param.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
param.parm.capture.timeperframe.numerator = num;
param.parm.capture.timeperframe.denominator =deno;
ioctl(video_obj.v4l2_fd, VIDIOC_S_PARM,¶m)
e) 获取帧率,查看设置是否生效
get_v4l2_param(void)
struct v4l2_streamparm param;
memset(¶m, 0, sizeof(struct v4l2_streamparm));
param.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
ioctl(video_obj.v4l2_fd, VIDIOC_G_PARM,¶m)
f) 申请V4L2帧缓存
request_v4l2_buffer(unsigned int count)
video_obj.buffers = calloc(count, sizeof(VideoBuffer));
memset(&video_obj.req, 0, sizeof(video_obj.req));
video_obj.req.count = count;
video_obj.req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
video_obj.req.memory = V4L2_MEMORY_MMAP;
ioctl(video_obj.v4l2_fd, VIDIOC_REQBUFS, &video_obj.req)
g) 内存映射摄像头的缓存
for (numBufs = 0; numBufs < video_obj.req.count; numBufs++)
{
memset(&video_obj.buf, 0, sizeof(video_obj.buf));
//数据流类型,永远都是V4L2_BUF_TYPE_VIDEO_CAPTURE
video_obj.buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
//存储类型:V4L2_MEMORY_MMAP(内存映射)或V4L2_MEMORY_USERPTR(用户指针)
video_obj.buf.memory = V4L2_MEMORY_MMAP;
video_obj.buf.index = numBufs;
//使配置生效
ioctl(video_obj.v4l2_fd, VIDIOC_QUERYBUF, &video_obj.buf)
video_obj.buffers[numBufs].length = video_obj.buf.length;
video_obj.buffers[numBufs].offset = (size_t)video_obj.buf.m.offset;
//使用mmap函数将申请的缓存地址转换应用程序的绝对地址
video_obj.buffers[numBufs].start =
mmap(NULL,video_obj.buf.length,PROT_READ|PROT_WRITE,
MAP_SHARED,video_obj.v4l2_fd,video_obj.buf.m.offset);
//放入缓存队列
ioctl(video_obj.v4l2_fd,VIDIOC_QBUF,&video_obj.buf)
}
h) 开始采集数据
i. 获取一帧缓存数据
start_v4l2_capture(void)
type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
ioctl(video_obj.v4l2_fd, VIDIOC_STREAMON, &type)
pull_v4l2_frame_buffer(unsigned int index,unsigned char **start,unsigned int *len)
video_obj.buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
video_obj.buf.memory = V4L2_MEMORY_MMAP;
video_obj.buf.index = index;
ioctl(video_obj.v4l2_fd,VIDIOC_DQBUF,&video_obj.buf)
*start = video_obj.buffers[index].start;
*len = video_obj.buffers[index].length;
ii. 将yuyv数据写入到文件(同时通过SDL2显示当前帧的数据)
fwrite(photo,1,len,fd);
fflush(fd);
iii. 将该帧缓存放入到缓存池中
push_v4l2_frame_buffer(unsigned int index)
video_obj.buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
video_obj.buf.memory = V4L2_MEMORY_MMAP;
video_obj.buf.index = index;
ioctl(video_obj.v4l2_fd, VIDIOC_QBUF, &video_obj.buf)
i) 清理动态申请的数据
release_v4l2_resource(void)
for (numBufs = 0; numBufs < video_obj.req.count; numBufs++)
munmap(video_obj.buffers[numBufs].start,video_obj.buf.length);
free(video_obj.buffers);
close(video_obj.v4l2_fd);
二、通过SDL2显示采集到的yuyv原始数据
a) 初始化sdl2需要用到的功能
sdl2_init(int w,int h)
SDL_Init(SDL_INIT_VIDEO|SDL_INIT_TIMER)
b) 创建新的窗口
sdl2_init(int w,int h)
win = SDL_CreateWindow("Sam",0,0,w,h,SDL_WINDOW_SHOWN|SDL_WINDOW_RESIZABLE);
c) 创建渲染器
sdl2_init(int w,int h)
renderer = SDL_CreateRenderer(win,-1,SDL_RENDERER_SOFTWARE);
d) 设置渲染器的纹理
sdl2_init(int w,int h)
Texture=SDL_CreateTexture(renderer,SDL_PIXELFORMAT_YUY2,
SDL_TEXTUREACCESS_STREAMING,w,h);
e) 创建SDL2的事件处理线程
pthread_create(&pid, NULL,event_loop,NULL)
SDL_PollEvent(&event)
f) 循环显示步骤1:更新渲染器纹理
sdl2_refresh(void *pixels,int pitch)
SDL_UpdateTexture(texture,NULL,pixels,pitch)
g) 循环显示步骤2:清空渲染器
sdl2_refresh(void *pixels,int pitch)
SDL_UpdateTexture(texture,NULL,pixels,pitch)
h) 循环显示步骤3:将纹理数据拷贝到渲染器
sdl2_refresh(void *pixels,int pitch)
SDL_RenderCopy(renderer,texture,NULL,NULL)
i) 循环显示步骤4:显示视频
sdl2_refresh(void *pixels,int pitch)
SDL_RenderPresent(renderer);
三、通过libX264压缩视频数据到H264
a) 创建相关结构体,打开yuyv文件和将要保存h264数据的文件
x264_nal_t* pNals = NULL;
x264_t* pHandle = NULL;
x264_picture_t* pPic_in = (x264_picture_t*)malloc(sizeof(x264_picture_t));
x264_picture_t* pPic_out = (x264_picture_t*)malloc(sizeof(x264_picture_t));
x264_param_t* pParam = (x264_param_t*)malloc(sizeof(x264_param_t));
FILE* fp_src = fopen("x.yuv", "rb");
FILE* fp_dst = fopen("x.h264", "wb");
b) 给结构体x264_param_t赋予一些默认的参数,修改参数中的宽高和数据格式(因为录制的时候是采用V4L2_PIX_FMT_YUYV格式,宽高为640x480)
x264_param_default(pParam);
pParam->i_width = width;
pParam->i_height = height;
pParam->i_csp = csp;
c) 设置profile
x264_param_apply_profile(pParam, x264_profile_names[4]);
d) 打开编码器
pHandle = x264_encoder_open(pParam);
e) 初始化帧数据的输入输出结构体
x264_picture_init(pPic_out);
x264_picture_alloc(pPic_in, csp, pParam->i_width, pParam->i_height);
f) 根据视频数据计算出视频帧数
fseek(fp_src,0,SEEK_END);
switch(csp){
case X264_CSP_I444:
frame_num=ftell(fp_src)/(y_size*3);
break;
case X264_CSP_I420:
frame_num=ftell(fp_src)/(y_size*3/2);
break;
case X264_CSP_I422:
frame_num=ftell(fp_src)/(y_size*2);
break;
}
fseek(fp_src,0,SEEK_SET);
g) 循环编码步骤1:分离yuv分量
h) 循环编码步骤2:编码一帧
i) 循环编码步骤3:将编码后的数据写入到h264文件中
上面三步骤包含的内容:
for( i=0;i
switch(csp){
case X264_CSP_I444:{
fread(pPic_in->img.plane[0],y_size,1,fp_src);//Y
fread(pPic_in->img.plane[1],y_size,1,fp_src);//U
fread(pPic_in->img.plane[2],y_size,1,fp_src);//V
break;}
case X264_CSP_I420:{
fread(pPic_in->img.plane[0],y_size,1,fp_src);//Y
fread(pPic_in->img.plane[1],y_size/4,1,fp_src);//U
fread(pPic_in->img.plane[2],y_size/4,1,fp_src);//V
break;}
case X264_CSP_I422:{
/*
Yuyv格式数据的存放方式为:(4X4像素)
Y U Y V Y U Y V
Y U Y V Y U Y V
Y U Y V Y U Y V
Y U Y V Y U Y V
Y的个数为像素点的个数,
实际上像素点的个数为y个数的两倍
*/
int index = 0;
int y_i = 0 , u_i = 0 , v_i = 0;
for(index = 0 ; index < y_size*2 ;){
fread(&pPic_in->img.plane[0][y_i++],1,1,fp_src);//Y
index++;
fread(&pPic_in->img.plane[1][u_i++],1,1,fp_src);//U
index++;
fread(&pPic_in->img.plane[0][y_i++],1,1,fp_src);//Y
index++;
fread(&pPic_in->img.plane[2][v_i++],1,1,fp_src);//V
index++;
}break;
}
}
pPic_in->i_pts = i;
x264_encoder_encode(pHandle, &pNals, &iNal, pPic_in, pPic_out);
for ( j = 0; j < iNal; ++j)
fwrite(pNals[j].p_payload, 1, pNals[j].i_payload, fp_dst);
}
j) 编码步骤4:将还残留在编码器中的数据flush out,并写入到文件
while(1){
ret = x264_encoder_encode(pHandle, &pNals, &iNal, NULL, pPic_out);
if(ret == 0)
Break;
for(j = 0;j < iNal; ++j)
fwrite(pNals[j].p_payload, 1, pNals[j].i_payload, fp_dst);
}
k) 清理动态申请的资源
x264_picture_clean(pPic_in);
x264_encoder_close(pHandle);
pHandle = NULL;
free(pPic_in);
free(pPic_out);
free(pParam);
fclose(fp_src);
fclose(fp_dst);
下面是两个项目的源码:
说明:
1、V4L2采集数据和SDL2显示是在同一个项目中
2、YUV数据文件通过libx264压缩为H264格式的文件为一个项目
3、实验环境是vmware的ubuntu系统,默认安装了SDL1.2,卸载了原来安装的1.2版本,重新编译安装SDL2.0
4、需要安装Libx264库
5、项目中的源码大部分是参考网上各论坛的博客:
http://blog.csdn.net/leixiaohua1020/article/details/42078645
http://blog.csdn.net/yuanhubilie/article/details/37930429
文件v4l2lib.c(数据采集和显示项目)
//编译命令gcc v4l2lib.c -L/usr/local/SDL/lib/ -I/usr/local/SDL/include/SDL2 -lSDL2 -lpthread -o v4l2
//SDL的库安装路径为/usr/local/SDL,根据自己安装路径修改
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "SDL.h"
#include
#include
//--------macro definition------
#define MAX_DEV_NAME 32
#define MAX_BUF 5
#define VIDEO_REC
#define WIDTH_PIX 640
#define HEIGHT_PIX 480
//--------structions defined here-------
typedef struct VideoBuffer
{
unsigned char *start;
size_t offset;
size_t length;
}VideoBuffer;
typedef struct v4l2_param{
char v4l2_devname[MAX_DEV_NAME];//设备名
int v4l2_fd;//描述符号
VideoBuffer *buffers;
struct v4l2_requestbuffers req;
struct v4l2_capability cap;
struct v4l2_input input;
struct v4l2_format fmt;
struct v4l2_buffer buf;
}VIDEO_T;
//--------variable defined here-------
static VIDEO_T video_obj;
static pthread_t pid;
static unsigned char state = 0;
//--------SDL2----------
static unsigned char inited = 0;
static SDL_Window * win = NULL;
static SDL_Renderer * renderer = NULL;
static SDL_Texture * texture = NULL;
static SDL_CommonEvent comm;
static SDL_Event event;
static int open_v4l2_device(const char *const devname)
{
//打开设备
if(strlen(devname) >= MAX_DEV_NAME)
{
printf("device name fail:%s\n",devname);
return -1;
}
else
memset(&video_obj,0,sizeof(video_obj));
video_obj.v4l2_fd = open(devname,O_RDWR);
if(video_obj.v4l2_fd <= 0)
{
perror("open fail");
return -1;
}
else
printf("%s success\n",__func__);
memcpy(video_obj.v4l2_devname,devname,strlen(devname));
return 0;
}
static int set_v4l2_param(unsigned int num,unsigned int deno)
{
struct v4l2_streamparm param;
memset(¶m, 0, sizeof(struct v4l2_streamparm));
param.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
param.parm.capture.timeperframe.numerator = num;
param.parm.capture.timeperframe.denominator =deno;
if(ioctl(video_obj.v4l2_fd, VIDIOC_S_PARM,¶m) < 0)
{
printf("%s fail\n",__func__);
return -1;
}
else
{
printf("%s ok\n",__func__);
return 0;
}
}
static int get_v4l2_param(void)
{
struct v4l2_streamparm param;
memset(¶m, 0, sizeof(struct v4l2_streamparm));
param.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
if(ioctl(video_obj.v4l2_fd, VIDIOC_G_PARM, ¶m) < 0)
{
perror("get param failed");
return -1;
}
else
{
printf("%s:%d/%d\n",__func__,
param.parm.capture.timeperframe.numerator,param.parm.capture.timeperframe.denominator);
return 0;
}
}
static int set_v4l2_fmt(unsigned int format,
unsigned int width,unsigned int height)
{
//设置视频格式
memset(&video_obj.fmt,0,sizeof(video_obj.fmt));
//视频数据流类型,永远都是V4L2_BUF_TYPE_VIDEO_CAPTURE
video_obj.fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
//视频源的格式为JPEG或YUN4:2:2或RGB V4L2_PIX_FMT_RGB565 V4L2_PIX_FMT_YUV565
video_obj.fmt.fmt.pix.pixelformat = format;
//video_obj.fmt.fmt.pix.field = V4L2_FIELD_INTERLACED;
//设置视频宽度
video_obj.fmt.fmt.pix.width = width;
//设置视频高度
video_obj.fmt.fmt.pix.height = height;
if (ioctl(video_obj.v4l2_fd, VIDIOC_S_FMT, &video_obj.fmt) < 0)//使配置生效
{
perror("set format failed");
return -1;
}
else
printf("%s success[format:%X w:%d h:%d]\n",__func__,format,width,height);
struct v4l2_format fmt;
fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
if (ioctl(video_obj.v4l2_fd, VIDIOC_G_FMT, &fmt) < 0)
{
perror("set format failed");
return -1;
}
else
printf("%s get success[format:%X w:%d h:%d]\n",
__func__,fmt.fmt.pix.pixelformat,fmt.fmt.pix.width,fmt.fmt.pix.height);
return 0;
}
static int request_v4l2_buffer(unsigned int count)
{
//申请帧缓冲
video_obj.buffers = calloc(count, sizeof(VideoBuffer));
memset(&video_obj.req, 0, sizeof(video_obj.req));
//缓存数量,即可保存的图片数量
video_obj.req.count = count;
//数据流类型,永远都是V4L2_BUF_TYPE_VIDEO_CAPTURE
video_obj.req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
//存储类型:V4L2_MEMORY_MMAP或V4L2_MEMORY_USERPTR
video_obj.req.memory = V4L2_MEMORY_MMAP;
//使配置生效
if (ioctl(video_obj.v4l2_fd, VIDIOC_REQBUFS, &video_obj.req) == -1)
{
perror("request buffer error \n");
return -1;
}
else
printf("%s success[request %d buffers]\n",__func__,count);
return 0;
}
static int mmap_v4l2_buffer(void)
{
//将VIDIOC_REQBUFS获取内存转为物理空间
int numBufs;
for (numBufs = 0; numBufs < video_obj.req.count; numBufs++)
{
memset(&video_obj.buf, 0, sizeof(video_obj.buf));
//数据流类型,永远都是V4L2_BUF_TYPE_VIDEO_CAPTURE
video_obj.buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
//存储类型:V4L2_MEMORY_MMAP(内存映射)或V4L2_MEMORY_USERPTR(用户指针)
video_obj.buf.memory = V4L2_MEMORY_MMAP;
video_obj.buf.index = numBufs;
//使配置生效
if (ioctl(video_obj.v4l2_fd, VIDIOC_QUERYBUF, &video_obj.buf) < 0)
{
perror("VIDIOC_QUERYBUF");
return -1;
}
//printf("request buf %d success\n",numBufs);
video_obj.buffers[numBufs].length = video_obj.buf.length;
video_obj.buffers[numBufs].offset = (size_t)video_obj.buf.m.offset;
//使用mmap函数将申请的缓存地址转换应用程序的绝对地址
video_obj.buffers[numBufs].start = mmap(NULL,video_obj.buf.length,
PROT_READ|PROT_WRITE,MAP_SHARED,video_obj.v4l2_fd,video_obj.buf.m.offset);
if (video_obj.buffers[numBufs].start == MAP_FAILED)
{
perror("buffers error");
return -1;
}
//printf("mmap buf 0x%p lenght:%d success\n",video_obj.buffers[numBufs].start,video_obj.buf.length);
//放入缓存队列
if (ioctl(video_obj.v4l2_fd,VIDIOC_QBUF,&video_obj.buf) < 0)
{
printf("VIDIOC_QBUF");
return -1;
}
}
printf("%s success\n",__func__);
return 0;
}
static int start_v4l2_capture(void)
{
//开始视频显示
enum v4l2_buf_type type;
//数据流类型,永远都是V4L2_BUF_TYPE_VIDEO_CAPTURE
type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
if (ioctl(video_obj.v4l2_fd, VIDIOC_STREAMON, &type) < 0)
{
perror("VIDIOC_STREAMON");
return -1;
}
printf("%s stream on success\n",__func__);
return 0;
}
static int pull_v4l2_frame_buffer(unsigned int index , unsigned char **start , unsigned int *len)
{
video_obj.buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; //取得原始采集数据
video_obj.buf.memory = V4L2_MEMORY_MMAP; //存储类型:V4L2_MEMORY_MMAP(内存映射)或V4L2_MEMORY_USERPTR(用户指针)
if(video_obj.req.count <= index)
return -1;
video_obj.buf.index = index; //读取缓存中的第几帧
if (ioctl(video_obj.v4l2_fd,VIDIOC_DQBUF,&video_obj.buf) < 0)
{
perror("VIDIOC_DQBUF");
return -1;
}
*start = video_obj.buffers[index].start;
*len = video_obj.buffers[index].length;
return 0;
}
static int push_v4l2_frame_buffer(unsigned int index)
{
video_obj.buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; //取得原始采集数据
video_obj.buf.memory = V4L2_MEMORY_MMAP; //存储类型:V4L2_MEMORY_MMAP(内存映射)或V4L2_MEMORY_USERPTR(用户指针)
if(video_obj.req.count <= index)
return -1;
video_obj.buf.index = index; //第几帧放入缓存
//获取下一帧视频数据
if (ioctl(video_obj.v4l2_fd, VIDIOC_QBUF, &video_obj.buf) < 0)
{
perror("VIDIOC_QBUF");
return -1;
}
return 0;
}
static void release_v4l2_resource(void)
{
int numBufs;
for (numBufs = 0; numBufs < video_obj.req.count; numBufs++)
munmap(video_obj.buffers[numBufs].start,video_obj.buf.length);
free(video_obj.buffers);
close(video_obj.v4l2_fd);
printf("%s\n",__func__);
}
static int sdl2_init(int w,int h)
{
if(SDL_Init(SDL_INIT_VIDEO|SDL_INIT_TIMER) == -1)
{
printf("SDL_Init fail!");
return -1;
}
else
printf("SDL_Init success\n");
/*
title :窗口标题
x :窗口位置x坐标。也可以设置为SDL_WINDOWPOS_CENTERED或SDL_WINDOWPOS_UNDEFINED。
y :窗口位置y坐标。同上。
w :窗口的宽
h :窗口的高
flags :支持下列标识。包括了窗口的是否最大化、最小化,能否调整边界等等属性。
::SDL_WINDOW_FULLSCREEN, ::SDL_WINDOW_OPENGL,
::SDL_WINDOW_HIDDEN, ::SDL_WINDOW_BORDERLESS,
::SDL_WINDOW_RESIZABLE, ::SDL_WINDOW_MAXIMIZED,
::SDL_WINDOW_MINIMIZED, ::SDL_WINDOW_INPUT_GRABBED,
::SDL_WINDOW_ALLOW_HIGHDPI.
*/
win = SDL_CreateWindow("Sam",0,0,w,h,SDL_WINDOW_SHOWN|SDL_WINDOW_RESIZABLE);
if(win == NULL)
{
printf("SDL_CreateWindow fail\n");
return -1;
}
else
{
printf("SDL_CreateWindow success\n");
}
//创建渲染器
/*
window : 渲染的目标窗口。
index :打算初始化的渲染设备的索引。设置“-1”则初始化默认的渲染设备。
flags :支持以下值(位于SDL_RendererFlags定义中)
SDL_RENDERER_SOFTWARE :使用软件渲染
SDL_RENDERER_ACCELERATED :使用硬件加速
SDL_RENDERER_PRESENTVSYNC:和显示器的刷新率同步
SDL_RENDERER_TARGETTEXTURE :不太懂
*/
renderer = SDL_CreateRenderer(win,-1,SDL_RENDERER_SOFTWARE);
if(renderer == NULL)
{
printf("SDL_CreateRenderer fail\n");
return -1;
}
else
{
printf("SDL_CreateRenderer success\n");
}
/*
参数的含义如下。
renderer:目标渲染器。
format :纹理的格式。后面会详述。
access :可以取以下值(定义位于SDL_TextureAccess中)
SDL_TEXTUREACCESS_STATIC :变化极少
SDL_TEXTUREACCESS_STREAMING :变化频繁
SDL_TEXTUREACCESS_TARGET :暂时没有理解
w :纹理的宽
h :纹理的高
*/
texture = SDL_CreateTexture(renderer,SDL_PIXELFORMAT_YUY2,SDL_TEXTUREACCESS_STREAMING,w,h);
if(texture == NULL)
{
printf("SDL_CreateTexture fail\n");
return -1;
}
else
{
printf("SDL_CreateTexture success\n");
}
return 0;
}
static void *event_loop(void *param)
{
printf("%s begin time:%d\n",__func__,SDL_GetTicks());
while(1)
{
if(SDL_PollEvent(&event) > 0 &&
(comm.type != event.common.type || comm.timestamp != event.common.timestamp))
{
comm.type = event.common.type;
comm.timestamp = event.common.timestamp;
switch(event.type)
{
case SDL_QUIT:
printf("SDL_WINDOWEVENT\n");state = 1;return NULL;
case SDL_WINDOWEVENT:
printf("SDL_WINDOWEVENT\n");break;
case SDL_SYSWMEVENT:
printf("SDL_SYSWMEVENT\n");break;
case SDL_KEYDOWN:
printf("SDL_KEYDOWN\n");break;
case SDL_KEYUP:
printf("SDL_KEYUP\n");break;
case SDL_TEXTEDITING:
printf("SDL_TEXTEDITING\n");break;
case SDL_TEXTINPUT:
printf("SDL_TEXTINPUT\n");break;
case SDL_KEYMAPCHANGED:
printf("SDL_KEYMAPCHANGED\n");break;
case SDL_MOUSEMOTION:
printf("SDL_MOUSEMOTION\n");break;
case SDL_MOUSEBUTTONDOWN:
printf("SDL_MOUSEBUTTONDOWN\n");break;
case SDL_MOUSEBUTTONUP:
printf("SDL_MOUSEBUTTONUP\n");break;
case SDL_MOUSEWHEEL:
printf("SDL_MOUSEWHEEL\n");break;
default:
printf("%X\n",event.type);
break;
}
}
}
printf("%s end time:%d\n",__func__,SDL_GetTicks());
return NULL;
}
static int sdl2_refresh(void *pixels,int pitch)
{
if(inited == 0)
{
if(sdl2_init(WIDTH_PIX,HEIGHT_PIX))
return -1;
inited = 1;
if(pthread_create(&pid, NULL,event_loop,NULL) != 0)
{
printf("pthread_create fail\n");
return;
}
}
/*
参数的含义如下。
texture:目标纹理。
rect:更新像素的矩形区域。设置为NULL的时候更新整个区域。
pixels:像素数据。
pitch:一行像素数据的字节数。
*/
if(SDL_UpdateTexture(texture,NULL,pixels,pitch) != 0)
{
printf("SDL_UpdateTexture fail\n");
return -1;
}
//清空渲染
SDL_RenderClear(renderer);
/*
参数的含义如下。
renderer:渲染目标。
texture:输入纹理。
srcrect:选择输入纹理的一块矩形区域作为输入。设置为NULL的时候整个纹理作为输入。
dstrect:选择渲染目标的一块矩形区域作为输出。设置为NULL的时候整个渲染目标作为输出。
*/
if(SDL_RenderCopy(renderer,texture,NULL,NULL) != 0)
{
printf("SDL_RenderCopy fail\n");
return -1;
}
SDL_RenderPresent(renderer);
return 0;
}
static void sdl2_uninit(void)
{
SDL_DestroyTexture(texture);
SDL_DestroyRenderer(renderer);
SDL_DestroyWindow(win);
SDL_Quit();
printf("SDL uninit\n");
}
static int generate_yuv_name(char *name,int maxlen)
{
//实例化time_t结构
time_t now;
time(&now);
//localtime函数把从time取得的时间now换算成你电脑中的时间(就是你设置的地区)
snprintf(name,maxlen,"%d.yuv",2);
printf("%s:%s\n",__func__,name);
return 0;
}
void main(int argc ,char *argv[])
{
int index = 0;
int cnt = 0;
unsigned char *photo = NULL;
unsigned int len = 0;
char name[64] = {0};
if(open_v4l2_device(argv[1])!=0)
return;
#ifdef VIDEO_REC
generate_yuv_name(name,sizeof(name));
FILE * fd = fopen(name,"wb+");
if(fd == NULL)
{
printf("open fail:%s\n",name);
return;
}
#endif
if(set_v4l2_fmt(V4L2_PIX_FMT_YUYV,WIDTH_PIX,HEIGHT_PIX))
return ;
if(set_v4l2_param(1,30))
return ;
if(get_v4l2_param())
return ;
if(request_v4l2_buffer(MAX_BUF))
return ;
if(mmap_v4l2_buffer())
return ;
if(start_v4l2_capture())
return ;
while(1)
{
//printf("cnt:%d\n",cnt++);
if(pull_v4l2_frame_buffer(index,&photo,&len))
break;
#ifdef VIDEO_REC
{
int l = fwrite(photo,1,len,fd);
if(l != (HEIGHT_PIX*WIDTH_PIX*2))
{
printf("write fail:%s [%d]\n",name,l);
fclose(fd);
break;
}
fflush(fd);
}
#endif
if(sdl2_refresh(photo,WIDTH_PIX*2))
break;
push_v4l2_frame_buffer(index);
index++;
if(index == MAX_BUF)
index = 0;
if(state == 1)
break;
usleep(1000*30);
}
sdl2_uninit();
release_v4l2_resource();
}
文件x264.c(视频数据压缩项目)
//编译命令:gcc x264.c -L/usr/local/x264/lib/ -I/usr/local/x264/include/ -lx264 -o x264
//libx264库安装在/usr/local/x264/,需要更具自己的安装目录修改命令
#include
#include
#include "stdint.h"
#if defined ( __cplusplus)
extern "C"
{
#include "x264.h"
};
#else
#include "x264.h"
#endif
int main(int argc, char** argv)
{
int ret;
int y_size;
int i,j;
//Encode 50 frame
//if set 0, encode all frame
int frame_num = 0;
const int csp = X264_CSP_I422;
int width = 640;
int height = 480;
int iNal = 0;
x264_nal_t* pNals = NULL;
x264_t* pHandle = NULL;
x264_picture_t* pPic_in = (x264_picture_t*)malloc(sizeof(x264_picture_t));
x264_picture_t* pPic_out = (x264_picture_t*)malloc(sizeof(x264_picture_t));
x264_param_t* pParam = (x264_param_t*)malloc(sizeof(x264_param_t));
FILE* fp_src = fopen("2.yuv", "rb");
FILE* fp_dst = fopen("x.h264", "wb");
//Check
if(fp_src==NULL||fp_dst==NULL)
{
printf("Error open files.\n");
return -1;
}
x264_param_default(pParam);
pParam->i_width = width;
pParam->i_height = height;
//Param
/*
pParam->i_log_level = X264_LOG_DEBUG;
pParam->i_threads = X264_SYNC_LOOKAHEAD_AUTO;
pParam->i_frame_total = 0;
pParam->i_keyint_max = 10;
pParam->i_bframe = 0;
pParam->b_open_gop = 0;
pParam->i_bframe_pyramid = 0;
pParam->rc.i_qp_constant=0;
pParam->rc.i_qp_max=0;
pParam->rc.i_qp_min=0;
pParam->i_bframe_adaptive = X264_B_ADAPT_TRELLIS;
pParam->i_fps_den = 1;
pParam->i_fps_num = 25;
pParam->i_timebase_den = pParam->i_fps_num;
pParam->i_timebase_num = pParam->i_fps_den;
*/
pParam->i_csp = csp;
x264_param_apply_profile(pParam, x264_profile_names[4]);
pHandle = x264_encoder_open(pParam);
x264_picture_init(pPic_out);
x264_picture_alloc(pPic_in, csp, pParam->i_width, pParam->i_height);
y_size = pParam->i_width * pParam->i_height;
printf("w:%d h:%d\r\n",pParam->i_width,pParam->i_height);
//detect frame number
if(frame_num==0)
{
fseek(fp_src,0,SEEK_END);
switch(csp)
{
case X264_CSP_I444:
frame_num=ftell(fp_src)/(y_size*3);
break;
case X264_CSP_I420:
frame_num=ftell(fp_src)/(y_size*3/2);
break;
case X264_CSP_I422:
frame_num=ftell(fp_src)/(y_size*2);
break;
default:
printf("Colorspace Not Support.\n");
return -1;
}
fseek(fp_src,0,SEEK_SET);
}
printf("frame_num:%d y_size:%d\r\n",frame_num,y_size);
//Loop to Encode
for( i=0;i
{
switch(csp)
{
case X264_CSP_I444:
{
fread(pPic_in->img.plane[0],y_size,1,fp_src); //Y
fread(pPic_in->img.plane[1],y_size,1,fp_src); //U
fread(pPic_in->img.plane[2],y_size,1,fp_src); //V
break;
}
case X264_CSP_I420:
{
fread(pPic_in->img.plane[0],y_size,1,fp_src); //Y
fread(pPic_in->img.plane[1],y_size/4,1,fp_src); //U
fread(pPic_in->img.plane[2],y_size/4,1,fp_src); //V
break;
}
case X264_CSP_I422:
{
int index = 0;
int y_i = 0 , u_i = 0 , v_i = 0;
for(index = 0 ; index < y_size*2 ;)
{
fread(&pPic_in->img.plane[0][y_i++],1,1,fp_src);//Y
index++;
fread(&pPic_in->img.plane[1][u_i++],1,1,fp_src);//U
index++;
fread(&pPic_in->img.plane[0][y_i++],1,1,fp_src);//Y
index++;
fread(&pPic_in->img.plane[2][v_i++],1,1,fp_src);//V
index++;
}
break;
}
default:
{
printf("Colorspace Not Support.\n");
return -1;
}
}
pPic_in->i_pts = i;
ret = x264_encoder_encode(pHandle, &pNals, &iNal, pPic_in, pPic_out);
if (ret< 0)
{
printf("Error.\n");
return -1;
}
for ( j = 0; j < iNal; ++j)
fwrite(pNals[j].p_payload, 1, pNals[j].i_payload, fp_dst);
}
//flush encoder
while(1)
{
ret = x264_encoder_encode(pHandle, &pNals, &iNal, NULL, pPic_out);
if(ret==0)
break;
//printf("Flush 1 frame.\n");
for(j = 0;j < iNal; ++j)
fwrite(pNals[j].p_payload, 1, pNals[j].i_payload, fp_dst);
}
x264_picture_clean(pPic_in);
x264_encoder_close(pHandle);
pHandle = NULL;
free(pPic_in);
free(pPic_out);
free(pParam);
fclose(fp_src);
fclose(fp_dst);
return 0;
}
这里有相关资源的下载:
http://download.csdn.net/download/xushan239/10199075
http://download.csdn.net/download/xushan239/10190449