具体源代码参见链接:
http://download.csdn.net/download/qq269131024/10142165
下面是视频采集的主程序:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <errno.h>
#include <netinet/in.h>
#include <sys/wait.h>
#include "camera.h"
#define MAXLINE 1024
#define DEBUG 1
void usage(char *command)
{
printf("usage :%s portnum filename\n", command);
exit(0);
}
/*********************************************************************
* filename: tcpserver.c
*purpose: 循环tcp服务端程序
*tidied by: zhoulifa(zhoulifa@163.com) 周立发(http://zhoulifa.bokee.com)
*Linux爱好者 Linux知识传播者 SOHO族 开发者 最擅长C语言
*date time:2006-07-04 22:00:00
*Note: 任何人可以任意复制代码并运用这些文档,当然包括你的商业用途
*但请遵循GPL
*Thanks to: Google.com
*********************************************************************/
int main(int argc,char **argv)
{
struct sockaddr_in server_addr;
struct sockaddr_in client_addr;
char buf[MAXLINE];
int server_sock_id;
int client_sock_id;
int recv_len;
int write_leng;
int client_addr_len;
FILE *fp;
int send_len;
//文件名
char * fileName="hello.txt";
//端口号
unsigned int serverport = 8050;
//视频大小
int width = 240;
int height = 320;
char *separateur;
unsigned char* rgb;
/*
参数采集区
*/
/************************************************************
命令行输入参数设定命令行参数应该这个样子的:
./servfox -g -d /dev/video0 -s 640x480 -w 7070 -f fileName
***********************************************************/
int i;
for (i = 1; i < argc; i++)
{
/* skip bad arguments */
if (argv[i] == NULL || *argv[i] == 0 || *argv[i] != '-')
{
continue;
}
/************************************************************** /
-d 参数用于设置输入视频采集设备。
****************************************************************/
if (strcmp (argv[i], "-d") == 0)
{
if (i + 1 >= argc)
{
if(DEBUG) printf ("No parameter specified with -d, aborting./n");
exit (1);
}
//设置Parameter
//videodevice = strdup (argv[i + 1]);
}
/**********************************************************************
-g 参数用于读取采集到的视频的方式,用read方式而非mmap方式。
*********************************************************************/
if (strcmp (argv[i], "-g") == 0)
{
/* Ask for read instead default mmap */
//grabmethod = 0;
}
/************************************************************
-s 参数用于设置视频图像帧的大小,如 640x480
*********************************************************/
if (strcmp (argv[i], "-s") == 0)
{
if (i + 1 >= argc)
{
if(DEBUG) printf ("No parameter specified with -s, aborting./n");
exit (1);
}
//sizestring = strdup (argv[i + 1]);
//width = strtoul (sizestring, &separateur, 10);
if (*separateur != 'x')
{
if(DEBUG) printf ("Error in size use -s widthxheight /n");
exit (1);
}else {
++separateur;
height =strtoul (separateur, &separateur, 10);
if (*separateur != 0)
if(DEBUG) printf ("hmm.. dont like that!! trying this height /n");
if(DEBUG) printf (" size width: %d height: %d /n",
width, height);
}
}
/******************************************************
-w参数用于设置端口。
*********************************************************/
if (strcmp (argv[i], "-w") == 0)
{
if (i + 1 >= argc)
{
if(DEBUG) printf ("No parameter specified with -w, aborting./n");
exit (1);
}
serverport = (unsigned short) atoi (argv[i + 1]);
if (serverport < 1024 ){
if(DEBUG) printf ("Port should be between 1024 to 65536 set default 7070 !./n");
serverport = 8050;
}
}
/************************************************************
-f 用于设置文件名
*************************************************************/
if (strcmp (argv[i], "-f") == 0)
{
if (i + 1 >= argc)
{
if(DEBUG) printf ("No parameter specified with -f, aborting./n");
exit (1);
}
int ii;
char *str;
for(ii=0,str=argv[i + 1];str!='\0';ii++)
{
fileName[ii] = *str;
str++;
}
}
/************************************************************
-h 帮助信息
****************************************************************/
if (strcmp (argv[i], "-h") == 0)
{
printf ("usage: cdse [-h -d -g ] /n");
printf ("-h print this message /n");
printf ("-d /dev/videoX use videoX device/n");
printf ("-g use read method for grab instead mmap /n");
printf ("-s widthxheight use specified input size /n");
printf ("-w port server port /n");
exit (0);
}
}
/*
主代码区
*/
// printf("i am alive !");
// if ((fp = fopen(fileName, "w")) == NULL) {
// perror("Open file failed\n");
// exit(0);
// }
printf("i am alive 1!");
if ((server_sock_id = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) {
perror("Create socket failed\n");
exit(0);
}
printf("i am alive 2!");
/*fill the serverer sockaddr_in struct commented by guoqingbo*/
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(serverport);
server_addr.sin_addr.s_addr = INADDR_ANY;
if (bind(server_sock_id, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0 ) {
perror("Bind socket failed\n");
exit(0);
}
printf("i am alive 3!");
if (-1 == listen(server_sock_id, 20)) {
perror("Listen socket failed\n");
exit(0);
}
/* serverer part commented by guoqingbo*/
while (1) {
client_addr_len = sizeof(client_addr);
/**
#include <sys/types.h>
#include <sys/socket.h>
int accept(int sockfd,struct sockaddr *addr,socklen_t *addrlen);
accept()系统调用:
主要用在基于连接的套接字类型,比如SOCK_STREAM和SOCK_SEQPACKET。
它提取出所监听套接字的等待连接队列中第一个连接请求,创建一个新的套接字,
并返回指向该套接字的文件描述符。新建立的套接字不在监听状态,
原来所监听的套接字也不受该系统调用的影响。
参数:
sockfd,利用系统调用socket()建立的套接字描述符,通过bind()绑定到一个本地地址
(一般为服务器的套接字),并且通过listen()一直在监听连接;
addr, 指向struct sockaddr的指针,该结构用通讯层服务器对等套接字的地址
(一般为客户端地址)填写,返回地址addr的确切格式由套接字的地址类别
(比如TCP或UDP)决定;若addr为NULL,没有有效地址填写,这种情况下,addrlen也不使用,
应该置为NULL;
备注:addr是个指向局部数据结构sockaddr_in的指针,这就是要求接入的信息本地的套接字(地址和指针)。
addrlen, 一个值结果参数,调用函数必须初始化为包含addr所指向结构大小的数值,
函数返回时包含对等地址(一般为服务器地址)的实际数值;
备注:addrlen是个局部整形变量,设置为sizeof(struct sockaddr_in)。
///////////////////////////////////////////////////////////////////////
如果队列中没有等待的连接,套接字也没有被标记为Non-blocking,
accept()会阻塞调用函数直到连接出现;如果套接字被标记为Non-blocking,
队列中也没有等待的连接,accept()返回错误EAGAIN或EWOULDBLOCK。
成功时,返回非负整数,该整数是接收到套接字的描述符;出错时,返回-1,相应地设定全局变量errno。错误处理
**/
client_sock_id = accept(server_sock_id, (struct sockaddr *)&client_addr, &client_addr_len);
if (-1 == client_sock_id) {
perror("Accept socket failed\n");
exit(0);
}
camera_t* camera = camera_open("/dev/video0", height, width);
printf("open the camera success!\n");
camera_init(camera);
printf("camera init success!\n");
camera_start(camera);
printf("camera is starting!\n");
struct timeval timeout;
timeout.tv_sec = 1;
timeout.tv_usec = 0;
/* skip 5 frames for booting a cam */
for (i = 0; i < 5; i++) {
camera_frame(camera, timeout);
}
rgb = calloc(width * height * 3, sizeof (uint8_t));
while(1){
// printf("good morning!...");
camera_frame(camera, timeout);
// printf("we will capture a frame! yo\n");
yuyv2rgb888(camera->head.start,rgb,camera->width, camera->height);
// uint ii;
// uint8_t jj;
// for(ii=0;ii<height;ii++)
// for(jj=0;jj<width;jj++)
// {
// rgb[(ii*width+jj)*3+0]=jj;
// rgb[(ii*width+jj)*3+1]=jj;
// rgb[(ii*width+jj)*3+2]=jj;
// }
// printf("Sending...\n");
send_len = send(client_sock_id, rgb, width * height * 3*sizeof(uint8_t), 0);
if ( send_len < 0 ) {
perror("Send file failed\n");
exit(0);
}
// printf("I am going to sleepping good night!...\n");
// sleep(0.1);
}
free(rgb);
camera_stop(camera);
printf("now I'm so sorry to tell you that we'l close the camera!haha\n");
camera_finish(camera);
camera_close(camera);
printf("Remeber to close the door,When you leave\n");
// printf("i am alive 4!");
// bzero(buf, MAXLINE);
// while (recv_len = recv(client_sock_id, buf, MAXLINE, 0)) {
// /* receiver data part commented by guoqingbo*/
// if(recv_len < 0) {
// printf("Recieve Data From Server Failed!\n");
// break;
// }
// printf("#");
// write_leng = fwrite(buf, sizeof(char), recv_len, fp);
// if (write_leng < recv_len) {
// printf("Write file failed\n");
// break;
// }
// bzero(buf,MAXLINE);
// }
printf("\nFinish Recieve\n");
fclose(fp);
close(client_sock_id);
}
printf("i am alive always yo!");
close(server_sock_id);
return 0;
}
下面是所需的库函数
/*
* capturing from UVC cam
* requires: libjpeg-dev
* build: gcc -std=c99 capture.c -ljpeg -o capture
*/
#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <asm/types.h>
#include <linux/videodev2.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#define FALSE -1
#define TRUE 1
void quit(const char * msg)
{
fprintf(stderr, "[%s] %d: %s\n", msg, errno, strerror(errno));
exit(EXIT_FAILURE);
}
int xioctl(int fd, int request, void* arg)
{
int i;
for (i = 0; i < 100; i++) {
int r = ioctl(fd, request, arg);
if (r != -1 || errno != EINTR) return r;
}
return -1;
}
typedef struct {
uint8_t* start;
size_t length;
} buffer_t;
typedef struct {
int fd;
uint32_t width;
uint32_t height;
size_t buffer_count;
buffer_t* buffers;
buffer_t head;
} camera_t;
camera_t* camera_open(const char * device, uint32_t width, uint32_t height)
{
int fd = open(device, O_RDWR | O_NONBLOCK, 0);
if (fd == -1)
{
perror("open error:");
quit("open");
}
camera_t* camera = malloc(sizeof (camera_t));
camera->fd = fd;
camera->width = width;
camera->height = height;
camera->buffer_count = 0;
camera->buffers = NULL;
camera->head.length = 0;
camera->head.start = NULL;
return camera;
}
void camera_init(camera_t* camera) {
/**
struct v4l2_capability
{
__u8 driver[16]; //驱动名。
__u8 card[32]; // Device名
__u8 bus_info[32]; //在Bus系统中存放位置
__u32 version; //driver 版本
__u32 capabilities; //能力集
__u32 reserved[4];
};
**/
//第一个ioctl就是获取设备信息
struct v4l2_capability cap;
if (xioctl(camera->fd, VIDIOC_QUERYCAP, &cap) == -1) quit("VIDIOC_QUERYCAP");
if (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE)) quit("no capture");
if (!(cap.capabilities & V4L2_CAP_STREAMING)) quit("no streaming");
//打印信息
// printf(“DriverName:%s/nCard Name:%s/nBus info:%s/nDriverVersion:%u.%u.%u/n”,cap.driver,cap.card,cap.bus_info,(cap.version>>16)&0XFF,(cap.version>>8)&0XFF,cap.version&OXFF);
/**
5. 帧格式:
VIDIOC_ENUM_FMT// 显示所有支持的格式
int ioctl(int fd, int request, struct v4l2_fmtdesc *argp);
struct v4l2_fmtdesc
{
__u32 index; // 要查询的格式序号,应用程序设置 666
enumv4l2_buf_type type; // 帧类型,应用程序设置 666
__u32 flags; // 是否为压缩格式
__u8 description[32]; // 格式名称
__u32pixelformat; // 格式
__u32reserved[4]; // 保留
};
**/
struct v4l2_fmtdesc fmtdesc;
fmtdesc.index=0;
fmtdesc.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;
printf("Supportformat:/n");
while(ioctl(camera->fd,VIDIOC_ENUM_FMT,&fmtdesc)!=-1)
{
printf("/t%d.%s/n",fmtdesc.index+1,fmtdesc.description);
fmtdesc.index++;
}
/**
6 图像的缩放 VIDIOC_CROPCAP
v4l2_cropcap 结构体用来设置摄像头的捕捉能力,
在捕捉上视频时应先先设置v4l2_cropcap 的 type 域,
再通过 VIDIO_CROPCAP 操作命令获取设备捕捉能力的参数,
保存于 v4l2_cropcap 结构体中,包括 bounds
(最大捕捉方框的左上角坐标和宽高),defrect
(默认捕捉方框的左上角坐标和宽高)等。
VIDIOC_CROPCAP
int ioctl(int fd,int request, struct v4l2_cropcap *argp);
structv4l2_cropcap
{
enum v4l2_buf_type type;// 应用程序设置
struct v4l2_rectbounds;// 最大边界
struct v4l2_rectdefrect;// 默认值
structv4l2_fract pixelaspect;
};
V4L2_BUF_TYPE_VIDEO_CAPTURE 指定buf的类型为capture,用于视频捕获设备
V4L2_BUF_TYPE_VIDEO_OUTPUT 指定buf的类型output,用于视频输出设备
V4L2_BUF_TYPE_VIDEO_OVERLAY 指定buf的类型为overlay,用于overlay设备
V4L2_BUF_TYPE_VBI_CAPTURE 用于vbi捕获设备
V4L2_BUF_TYPE_VBI_OUTPUT 用于vbi输出设备
V4L2_BUF_TYPE_SLICED_VBI_CAPTURE 用于切片vbi捕获设备
V4L2_BUF_TYPE_SLICED_VBI_OUTPUT 用于切片vbi输出设备
V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY 用于视频输出overlay设备
V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE 用于多平面存储格式的视频捕获设备
V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE用于多平面存储格式的视频输出设备
**/
struct v4l2_cropcap cropcap;
memset(&cropcap, 0, sizeof (cropcap));
cropcap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
if (xioctl(camera->fd, VIDIOC_CROPCAP, &cropcap) == 0) {
struct v4l2_crop crop;
crop.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
crop.c = cropcap.defrect;
if (xioctl(camera->fd, VIDIOC_S_CROP, &crop) == -1) {
// cropping not supported
}
}
/**
// 查看或设置当前格式
VIDIOC_G_FMT,VIDIOC_S_FMT
// 检查是否支持某种格式
v4l2_format 结构体用来设置摄像头的视频制式、帧格式等,
在设置这个参数时应先填好 v4l2_format 的各个域,
如 type(传输流类型),fmt.pix.width(宽),
fmt.pix.heigth(高),fmt.pix.field(采样区域,如隔行采样),
fmt.pix.pixelformat(采样类型,如 yuyv4:2:2),
然后通过 VIDIO_S_FMT 操作命令设置视频捕捉格式。
如下图所示:
**/
struct v4l2_format format;
memset(&format, 0, sizeof (format));
format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
format.fmt.pix.width = camera->width;
format.fmt.pix.height = camera->height;
format.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
format.fmt.pix.field = V4L2_FIELD_NONE;
if (xioctl(camera->fd, VIDIOC_S_FMT, &format) == -1) quit("VIDIOC_S_FMT");
/**
9. 申请和管理缓冲区
应用程序和设备有三种交换数据的方法,直接 read/write、内存映射(memory mapping)
和用户指针。这里只讨论内存映射(memory mapping)。
9.1 向设备申请缓冲区 VIDIOC_REQBUFS
相关函数:
int ioctl(int fd, int request, struct v4l2_requestbuffers *argp);
struct v4l2_requestbuffers
{
u32 count; // 缓冲区内缓冲帧的数目
enum v4l2_buf_type type; // 缓冲帧数据格式
enum v4l2_memory memory; // 区别是内存映射还是用户指针方式
u32 reserved[2];
};
**/
struct v4l2_requestbuffers req;
memset(&req, 0, sizeof (req));
req.count = 4;
req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
req.memory = V4L2_MEMORY_MMAP;
if (xioctl(camera->fd, VIDIOC_REQBUFS, &req) == -1) quit("VIDIOC_REQBUFS");
camera->buffer_count = req.count;
camera->buffers = calloc(req.count, sizeof (buffer_t));
size_t buf_max = 0;
size_t i;
for (i = 0; i < camera->buffer_count; i++) {
struct v4l2_buffer buf;
memset(&buf, 0, sizeof (buf));
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory = V4L2_MEMORY_MMAP;
buf.index = i;
if (xioctl(camera->fd, VIDIOC_QUERYBUF, &buf) == -1)
quit("VIDIOC_QUERYBUF");
/**
#include<sys/mman.h>
void *mmap(void*addr, size_t length, int prot, int flags, int fd, off_t offset);
//addr 映射起始地址,一般为NULL ,让内核自动选择
//length 被映射内存块的长度
//prot 标志映射后能否被读写,其值为PROT_EXEC,PROT_READ,PROT_WRITE,PROT_NONE
//flags 确定此内存映射能否被其他进程共享,MAP_SHARED,MAP_PRIVATE
//fd,offset, 确定被映射的内存地址
返回成功映射后的地址,不成功返回MAP_FAILED ((void*)-1);
int munmap(void*addr, size_t length);// 断开映射
//addr 为映射后的地址,length 为映射后的内存长度
**/
if (buf.length > buf_max) buf_max = buf.length;
camera->buffers[i].length = buf.length;
camera->buffers[i].start =
mmap(NULL, buf.length, PROT_READ | PROT_WRITE, MAP_SHARED,
camera->fd, buf.m.offset);
printf("the image length is%d",buf.length);
if (camera->buffers[i].start == MAP_FAILED) quit("mmap");
}
camera->head.start = malloc(buf_max);
}
/**
10. 缓冲区处理好之后,就可以开始获取数据了
10.1 启动 或 停止数据流 VIDIOC_STREAMON,VIDIOC_STREAMOFF
**/
void camera_start(camera_t* camera)
{
size_t i;
/**
//argp 为流类型指针,如V4L2_BUF_TYPE_VIDEO_CAPTURE.
10.2 在开始之前,还应当把缓冲帧放入缓冲队列:
VIDIOC_QBUF// 把帧放入队列
VIDIOC_DQBUF// 从队列中取出帧
**/
for (i = 0; i < camera->buffer_count; i++) {
struct v4l2_buffer buf;
memset(&buf, 0, sizeof buf);
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory = V4L2_MEMORY_MMAP;
buf.index = i;//代表放入第几个buff
if (xioctl(camera->fd, VIDIOC_QBUF, &buf) == -1) quit("VIDIOC_QBUF");
}
//启动 或 停止数据流 VIDIOC_STREAMON, VIDIOC_STREAMOFF
enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
if (xioctl(camera->fd, VIDIOC_STREAMON, &type) == -1)
quit("VIDIOC_STREAMON");
}
void camera_stop(camera_t* camera)
{
//启动 或 停止数据流 VIDIOC_STREAMON, VIDIOC_STREAMOFF
enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
if (xioctl(camera->fd, VIDIOC_STREAMOFF, &type) == -1)
quit("VIDIOC_STREAMOFF");
}
void camera_finish(camera_t* camera)
{
size_t i;
for (i = 0; i < camera->buffer_count; i++) {
munmap(camera->buffers[i].start, camera->buffers[i].length);
}
free(camera->buffers);
camera->buffer_count = 0;
camera->buffers = NULL;
free(camera->head.start);
camera->head.length = 0;
camera->head.start = NULL;
}
void camera_close(camera_t* camera)
{
if (close(camera->fd) == -1) quit("close");
free(camera);
}
//获取一帧并处理
int camera_capture(camera_t* camera)
{
struct v4l2_buffer buf;
memset(&buf, 0, sizeof buf);
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory = V4L2_MEMORY_MMAP;
if (xioctl(camera->fd, VIDIOC_DQBUF, &buf) == -1) return FALSE;
memcpy(camera->head.start, camera->buffers[buf.index].start, buf.bytesused);
camera->head.length = buf.bytesused;
if (xioctl(camera->fd, VIDIOC_QBUF, &buf) == -1) return FALSE;
return TRUE;
}
int camera_frame(camera_t* camera, struct timeval timeout) {
/**
Select函数在Socket编程中还是比较重要的,可是对于初学Socket的人来说都不太爱用Select写程序,他们只是习惯写诸如connect、 accept、recv或recvfrom这样的阻塞程序(所谓阻塞方式block,顾名思义,就是进程或是线程执行到这些函数时必须等待某个事件的发生,如果事件没有发生,进程或线程就被阻塞,函数不能立即返回)。可是使用Select就可以完成非阻塞(所谓非阻塞方式non-block,就是进程或线程执行此函数时不必非要等待事件的发生,一旦执行肯定返回,以返回值的不同来反映函数的执行情况,如果事件发生则与阻塞方式相同,若事件没有发生则返回一个代码来告知事件未发生,而进程或线程继续执行,所以效率较高)方式工作的程序,它能够监视我们需要监视的文件描述符的变化情况——读写或是异常。下面详细介绍一下!
**/
fd_set fds;
FD_ZERO(&fds);
FD_SET(camera->fd, &fds);
int r = select(camera->fd + 1, &fds, 0, 0, &timeout);
if (r == -1) quit("select");
if (r == 0) return FALSE;
return camera_capture(camera);
}
int minmax(int min, int v, int max)
{
return (v < min) ? min : (max < v) ? max : v;
}
void yuyv2rgb(uint8_t* yuyv,uint8_t* rgb, uint32_t width, uint32_t height)
{
size_t i,j;
for (i = 0; i < height; i++) {
for (j = 0; j < width; j += 2) {
size_t index = i * width + j;
int y0 = yuyv[index * 2 + 0] << 8;
int u = yuyv[index * 2 + 1] - 128;
int y1 = yuyv[index * 2 + 2] << 8;
int v = yuyv[index * 2 + 3] - 128;
rgb[index * 3 + 0] = minmax(0, (y0 + 359 * v) >> 8, 255);
rgb[index * 3 + 1] = minmax(0, (y0 + 88 * v - 183 * u) >> 8, 255);
rgb[index * 3 + 2] = minmax(0, (y0 + 454 * u) >> 8, 255);
rgb[index * 3 + 3] = minmax(0, (y1 + 359 * v) >> 8, 255);
rgb[index * 3 + 4] = minmax(0, (y1 + 88 * v - 183 * u) >> 8, 255);
rgb[index * 3 + 5] = minmax(0, (y1 + 454 * u) >> 8, 255);
}
}
// return rgb;
}
void yuyv2rgb888(uint8_t* yuyv,uint8_t* rgb, uint32_t width, uint32_t height)
{
uint in, out = 0;
uint pixel_16;
uint8_t pixel_24[3];
uint pixel32;
int y0, u, y1, v;
for(in = 0; in < width * height * 2; in += 4) {
pixel_16 =
yuyv[in + 3] << 24 |
yuyv[in + 2] << 16 |
yuyv[in + 1] << 8 |
yuyv[in + 0];//yuyv422每个像素2字节,每两个像素共用一个Cr,Cb值,即u和v,RGB24每个像素3个字节
y0 = (pixel_16 & 0x000000ff);
u = (pixel_16 & 0x0000ff00) >> 8;
y1 = (pixel_16 & 0x00ff0000) >> 16;
v = (pixel_16 & 0xff000000) >> 24;
pixel32 = convert_yuyv_to_rgb_pixel(y0, u, v);
pixel_24[0] = (pixel32 & 0x000000ff);
pixel_24[1] = (pixel32 & 0x0000ff00) >> 8;
pixel_24[2] = (pixel32 & 0x00ff0000) >> 16;
rgb[out++] = pixel_24[0];
rgb[out++] = pixel_24[1];
rgb[out++] = pixel_24[2];//rgb的一个像素
pixel32 = convert_yuyv_to_rgb_pixel(y1, u, v);
pixel_24[0] = (pixel32 & 0x000000ff);
pixel_24[1] = (pixel32 & 0x0000ff00) >> 8;
pixel_24[2] = (pixel32 & 0x00ff0000) >> 16;
rgb[out++] = pixel_24[0];
rgb[out++] = pixel_24[1];
rgb[out++] = pixel_24[2];
}
// printf("\nthe rgb out is %d",out);
}
int convert_yuyv_to_rgb_pixel(int y, int u, int v)
{
uint pixel32 = 0;
uint8_t *pixel = (uint8_t *)&pixel32;
int r, g, b;
r = y + (1.370705 * (v-128));
g = y - (0.698001 * (v-128)) - (0.337633 * (u-128));
b = y + (1.732446 * (u-128));
if(r > 255) r = 255;
if(g > 255) g = 255;
if(b > 255) b = 255;
if(r < 0) r = 0;
if(g < 0) g = 0;
if(b < 0) b = 0;
pixel[0] = r * 220 / 256;
pixel[1] = g * 220 / 256;
pixel[2] = b * 220 / 256;
return pixel32;
}
// int main()
// {
// int i;
// camera_t* camera = camera_open("/dev/video0", 352, 288);
// printf("open the camera success!\n");
// camera_init(camera);
// printf("camera init success!\n");
// camera_start(camera);
// printf("camera is starting!\n");
// struct timeval timeout;
// timeout.tv_sec = 1;
// timeout.tv_usec = 0;
// /* skip 5 frames for booting a cam */
// for (i = 0; i < 5; i++) {
// camera_frame(camera, timeout);
// }
// camera_frame(camera, timeout);
// printf("we will capture a frame! yo\n");
// unsigned char* rgb =
// yuyv2rgb(camera->head.start, camera->width, camera->height);
// FILE* out = fopen("result.jpg", "w");
// fclose(out);
// free(rgb);
// camera_stop(camera);
// printf("now I'm so sorry to tell you that we'l close the camera!haha\n");
// camera_finish(camera);
// camera_close(camera);
// printf("Remeber to close the door,When you leave\n");
// return 0;
// }