Linux下的视频传输

利用网线在电脑与开发板之间组成一个局域网,能够让电脑与开发板之间的网络互通,(记着要IP地址固定哟!!)
在电脑上挂载摄像头并作为服务端,开发板在显示屏上显示摄像头所拍摄的画面。

服务端

利用V4L2来采集视频
V4L2 是 Video for linux two 的简称,是 Linux 内核中视频类设备的一套驱动框架,为视频类设备驱动开发和应用层提供了一套统一的接口规范。

  1. 首先是打开摄像头设备;
/* 初始化摄像头 */
static int v4l2_dev_init(char *path)
{
    v4l2_fd = open(path, O_RDWR);
    if(v4l2_fd < 0) {
        perror("open_video error");
        exit(1);
    }
    return 1;
}
  1. 枚举出摄像头所支持的所有视频像素格式\采集分辨率\帧率
/* 枚举出摄像头所支持的所有视频像素格式\采集分辨率\帧率 */
static void v4l2_print_formats(void)
{
    struct v4l2_fmtdesc fmtdesc;
    struct v4l2_frmsizeenum frmsize;
    struct v4l2_frmivalenum frmival;

    fmtdesc.index = 0;
    fmtdesc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;//视频采集
    /*查看支持的像素格式*/
    while(ioctl(v4l2_fd, VIDIOC_ENUM_FMT, &fmtdesc)==0)
    {
        printf("fmt: \" %s \"  <0x%d>\n", fmtdesc.description, fmtdesc.pixelformat);
        fmtdesc.index++;

        frmsize.index = 0;
        frmsize.type =  V4L2_BUF_TYPE_VIDEO_CAPTURE;
        frmsize.pixel_format = fmtdesc.pixelformat;
        /*查看支持的分辨率*/
        while(ioctl(v4l2_fd, VIDIOC_ENUM_FRAMESIZES, &frmsize) == 0)
        {
            printf("frm_size<%d*%d>   ", frmsize.discrete.width, frmsize.discrete.height);
            frmsize.index++;

            frmival.index = 0;
            frmival.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
            frmival.pixel_format = fmtdesc.pixelformat;
            frmival.width = frmsize.discrete.width;
            frmival.height = frmsize.discrete.height;
            /*查看支持的帧率*/
            while(ioctl(v4l2_fd, VIDIOC_ENUM_FRAMEINTERVALS, &frmival) == 0)
            {
                printf("<%dfps>\n", (frmival.discrete.denominator / frmival.discrete.numerator));
                frmival.index++;
            }
        }
        printf("\r\n");
    }
}
  1. 设置设备的参数,譬如像素格式、 帧大小
/* 设置格式 */
static int v4l2_set_format(unsigned int format, int width, int height)
{
    struct v4l2_format fmt;

    fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;//视频采集
    fmt.fmt.pix.width =  width;//设置分辨率的宽
    fmt.fmt.pix.height = height;//设置分辨率的高
    fmt.fmt.pix.pixelformat = format;//设置视频输出格式
    if(ioctl(v4l2_fd, VIDIOC_S_FMT, &fmt) < 0){
        printf("Error: v4l2_set_format\r\n");
        return 0;
    }
    /*查询当前摄像头的工作模式*/
    ioctl(v4l2_fd, VIDIOC_G_FMT, &fmt);
    printf("width:%d, height:%d \n", fmt.fmt.pix.width, fmt.fmt.pix.height);
    return 1;
}
  1. 申请帧缓冲、内存映射
/*申请帧缓冲、内存映射*/
static int v4l2_init_buffer(void)
{
    struct v4l2_requestbuffers reqbuf;
    struct v4l2_buffer buf;

    reqbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    reqbuf.count = 4;  //帧缓冲的数量
    reqbuf.memory =  V4L2_MEMORY_MMAP;
    /*申请帧缓冲*/
    if(ioctl(v4l2_fd, VIDIOC_REQBUFS, &reqbuf) < 0){
        printf("error: v4l2_init_buffer\r\n");
        return 0;
    }

    buf.index = 0;
    buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    buf.memory = V4L2_MEMORY_MMAP;
    /*内存映射*/
    for(buf.index = 0;buf.index < 4;buf.index++)
    {
        ioctl(v4l2_fd, VIDIOC_QUERYBUF, &buf);
        frm_base[buf.index] = mmap(NULL, buf.length, \
                                PROT_READ | PROT_WRITE, MAP_SHARED, \
                                v4l2_fd, buf.m.offset);
        if (MAP_FAILED == frm_base[buf.index]) {
            perror("mmap error");
            return -1;
        }
        printf("查询内存成功  buf[%d]==%d长度\r\n",buf.index,buf.length);
        if(ioctl(v4l2_fd,VIDIOC_QBUF,&buf) < 0){//加入缓冲队列
            printf("error: VIDIOC_QBUF\r\n");
            return 0;
        }
    }
    return 1;
}
  1. 打开摄像头
static int v4l2_stream_on(void)
{
    enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    /* 打开摄像头、摄像头开始采集数据 */
    if(ioctl(v4l2_fd,  VIDIOC_STREAMON, &type) < 0){
        printf("error: v4l2_stream_on\r\n");
        return 0;
    }
    printf("Camera_open : success\r\n");
    return 1;
}
  1. 读取数据
static void v4l2_read_data(void)
{
    struct v4l2_buffer buf;

    buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    buf.memory = V4L2_MEMORY_MMAP;
    buf.index = 1;

    while(1)
    {
		for(buf.index = 1; buf.index < 4; buf.index++)
		{
		    if(ioctl(v4l2_fd, VIDIOC_DQBUF, &buf)!=0)//出队
		    {
		        printf("提取数据失败\r\n");
		        break;
		    }
            Send_Video_Data(connfd,frm_base[buf.index],buf.length);

		    if(ioctl(v4l2_fd, VIDIOC_QBUF, &buf)!=0)//入队
		    {
		        printf("放回队列失败\r\n");
		        exit(0);
		    }
		    usleep(33000);
		}
    }
}

服务端整体代码

#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>     
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <linux/videodev2.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <linux/fb.h>

#define SERVER_PORT 55555

static int sockfd, connfd;
static int v4l2_fd = -1;               //摄像头设备文件描述符
static unsigned char *frm_base[4] = {NULL};             //帧缓冲起始地址

/* 初始化摄像头 */
static int v4l2_dev_init(char *path)
{
    v4l2_fd = open(path, O_RDWR);
    if(v4l2_fd < 0) {
        perror("open_video error");
        exit(1);
    }
    return 1;
}
/* 枚举出摄像头所支持的所有视频像素格式\采集分辨率\帧率 */
static void v4l2_print_formats(void)
{
    struct v4l2_fmtdesc fmtdesc;
    struct v4l2_frmsizeenum frmsize;
    struct v4l2_frmivalenum frmival;

    fmtdesc.index = 0;
    fmtdesc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;//视频采集
    /*查看支持的像素格式*/
    while(ioctl(v4l2_fd, VIDIOC_ENUM_FMT, &fmtdesc)==0)
    {
        printf("fmt: \" %s \"  <0x%d>\n", fmtdesc.description, fmtdesc.pixelformat);
        fmtdesc.index++;

        frmsize.index = 0;
        frmsize.type =  V4L2_BUF_TYPE_VIDEO_CAPTURE;
        frmsize.pixel_format = fmtdesc.pixelformat;
        /*查看支持的分辨率*/
        while(ioctl(v4l2_fd, VIDIOC_ENUM_FRAMESIZES, &frmsize) == 0)
        {
            printf("frm_size<%d*%d>   ", frmsize.discrete.width, frmsize.discrete.height);
            frmsize.index++;

            frmival.index = 0;
            frmival.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
            frmival.pixel_format = fmtdesc.pixelformat;
            frmival.width = frmsize.discrete.width;
            frmival.height = frmsize.discrete.height;
            /*查看支持的帧率*/
            while(ioctl(v4l2_fd, VIDIOC_ENUM_FRAMEINTERVALS, &frmival) == 0)
            {
                printf("<%dfps>\n", (frmival.discrete.denominator / frmival.discrete.numerator));
                frmival.index++;
            }
        }
        printf("\r\n");
    }
}
/* 设置格式 */
static int v4l2_set_format(unsigned int format, int width, int height)
{
    struct v4l2_format fmt;

    fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;//视频采集
    fmt.fmt.pix.width =  width;//设置分辨率的宽
    fmt.fmt.pix.height = height;//设置分辨率的高
    fmt.fmt.pix.pixelformat = format;//设置视频输出格式
    if(ioctl(v4l2_fd, VIDIOC_S_FMT, &fmt) < 0){
        printf("Error: v4l2_set_format\r\n");
        return 0;
    }
    /*查询当前摄像头的工作模式*/
    ioctl(v4l2_fd, VIDIOC_G_FMT, &fmt);
    printf("width:%d, height:%d \n", fmt.fmt.pix.width, fmt.fmt.pix.height);
    return 1;
}
/*申请帧缓冲、内存映射*/
static int v4l2_init_buffer(void)
{
    struct v4l2_requestbuffers reqbuf;
    struct v4l2_buffer buf;

    reqbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    reqbuf.count = 4;  //帧缓冲的数量
    reqbuf.memory =  V4L2_MEMORY_MMAP;
    /*申请帧缓冲*/
    if(ioctl(v4l2_fd, VIDIOC_REQBUFS, &reqbuf) < 0){
        printf("error: v4l2_init_buffer\r\n");
        return 0;
    }

    buf.index = 0;
    buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    buf.memory = V4L2_MEMORY_MMAP;
    /*内存映射*/
    for(buf.index = 0;buf.index < 4;buf.index++)
    {
        ioctl(v4l2_fd, VIDIOC_QUERYBUF, &buf);
        frm_base[buf.index] = mmap(NULL, buf.length, \
                                PROT_READ | PROT_WRITE, MAP_SHARED, \
                                v4l2_fd, buf.m.offset);
        if (MAP_FAILED == frm_base[buf.index]) {
            perror("mmap error");
            return -1;
        }
        printf("查询内存成功  buf[%d]==%d长度\r\n",buf.index,buf.length);
        if(ioctl(v4l2_fd,VIDIOC_QBUF,&buf) < 0){//加入缓冲队列
            printf("error: VIDIOC_QBUF\r\n");
            return 0;
        }
    }
    return 1;
}

static int v4l2_stream_on(void)
{
    enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    /* 打开摄像头、摄像头开始采集数据 */
    if(ioctl(v4l2_fd,  VIDIOC_STREAMON, &type) < 0){
        printf("error: v4l2_stream_on\r\n");
        return 0;
    }
    printf("Camera_open : success\r\n");
    return 1;
}

void Send_Video_Data(int fd,uint8_t * data,int data_len)
{
    int tmp = 0;
    int current = 0;
    if(fd > 0)
    {
        while(1)
        {
            tmp = write(fd,data,data_len);
            if(tmp < 0){
                perror("write buf");
                close("fd");
                exit(1);
            }
            current = data_len - tmp;//还有多少字节没有写入
            data+=tmp;//指针位移
            data_len = current;//写入剩下的字节
            if(data_len == 0)
            {
                break;
            }
        } 
    }  
}

unsigned char rgbdatabuf[640*480*4]={0};
static void v4l2_read_data(void)
{
    struct v4l2_buffer buf;

    buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    buf.memory = V4L2_MEMORY_MMAP;
    buf.index = 1;

    while(1)
    {
		for(buf.index = 1; buf.index < 4; buf.index++)
		{
		    if(ioctl(v4l2_fd, VIDIOC_DQBUF, &buf)!=0)//出队
		    {
		        printf("提取数据失败\r\n");
		        break;
		    }
            Send_Video_Data(connfd,frm_base[buf.index],buf.length);

		    if(ioctl(v4l2_fd, VIDIOC_QBUF, &buf)!=0)//入队
		    {
		        printf("放回队列失败\r\n");
		        exit(0);
		    }
		    usleep(33000);
		}
    }
}

void tcp_server(void)
{
    struct sockaddr_in server_addr = {0};
    struct sockaddr_in client_addr = {0};
    char ip_str[20] = {'\0'};//客户端IP
    int addrlen = sizeof(struct sockaddr_in);//结构体 struct sockaddr_in 所占字节
    int ret;

    /* 打开套接字,得到套接字描述符 */
    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (0 > sockfd) {
        perror("socket error");
        exit(1);
    }
    /*配置监听描述符地址复用属性*/ 
    int opt = 1;//opt 只有(关闭(0)/打开(1))
    ret = setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
    if(ret < 0) {
        perror("setsockopt error");
        exit(1);
    }
    /* 将套接字与指定端口号进行绑定 */
    server_addr.sin_family = AF_INET;//协议族为IPV4
    server_addr.sin_addr.s_addr = htonl(INADDR_ANY);//绑定IP
    server_addr.sin_port = htons(SERVER_PORT);//绑定端口
    ret = bind(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr));
    if (0 > ret) {
        perror("bind error");
        close(sockfd);
        exit(1);
    }
    /* 使服务器进入监听状态 */
    ret = listen(sockfd, 50);
    if (0 > ret) {
        perror("listen error");
        close(sockfd);
        exit(1);
    }
    /* 阻塞等待客户端连接 */
    connfd = accept(sockfd, (struct sockaddr *)&client_addr, &addrlen);
    if (0 > connfd) {
        printf("\n有客户端连接失败\n");
        exit(1);
    }
    printf("有客户端接入...\n");
    //将二进制 Ipv4 地址转换成 点分十进制
    inet_ntop(AF_INET, &client_addr.sin_addr.s_addr, ip_str, sizeof(ip_str));
    printf("客户端主机的 IP 地址: %s\n", ip_str);
    printf("客户端进程的端口号: %d\n", client_addr.sin_port);
}

int main(int argc, char *argv[])
{
    v4l2_dev_init(argv[1]);
    v4l2_print_formats();
    v4l2_set_format(V4L2_PIX_FMT_YUYV, 640, 480);
    v4l2_init_buffer();
    v4l2_stream_on();
    tcp_server();

    v4l2_read_data();
}

客户端

初始化 LCD

/* 初始化 LCD */
static int fb_dev_init(void)
{
    struct fb_var_screeninfo fb_var = {0};
    struct fb_fix_screeninfo fb_fix = {0};
    unsigned long screen_size;
    /* 打开framebuffer设备 */
    fb_fd = open(FB_DEV, O_RDWR);
    if (0 > fb_fd) {
        perror("open_fb error");
        return -1;
    }
    /* 获取framebuffer设备信息 */
    ioctl(fb_fd, FBIOGET_VSCREENINFO, &fb_var);
    ioctl(fb_fd, FBIOGET_FSCREENINFO, &fb_fix);

    screen_size = fb_fix.line_length * fb_var.yres;
    width = fb_var.xres_virtual;
    height = fb_var.yres_virtual;
    printf("%d*%d\n", width, height);
    /* 内存映射 */
    screen_base = mmap(NULL, screen_size, PROT_READ | PROT_WRITE, MAP_SHARED, fb_fd, 0);
    if (MAP_FAILED == (void *)screen_base) {
        perror("mmap error");
        close(fb_fd);
        return -1;
    }
    /* LCD背景刷白 */
    memset(screen_base, 0xFF, screen_size);
    return 0;
}

显示图片

void show_pic(unsigned char * pic_data, int w, int h)
{
    int i,j;
    unsigned int * p = screen_base;//LCD起始地址
    unsigned int tmpdata;

    for(i=0;i<h;i++)//对某一行
    {
        for(j=0;j<w;j++)//一行中的一个像素点
        {
            //三个十六进制表示一个像素点
			//j*3表示当前行的第j+1个十六进制的数据
			//i*w*3表示第i+1行
            tmpdata = (pic_data[j*3+i*w*3]<<16 | pic_data[j*3+i*w*3+1]<<8 | pic_data[(j*3)+(i*w*3)+2]<<0);
            * (p+j) = tmpdata;
        }
        p+=width;
    }
}

YUYV422 转RGB 数据

//YUYV422 转RGB 数据
void yuyv_to_rgb(unsigned char *yuyvdata, unsigned char *rgbdata, int w, int h)
{
    //码流Y0 U0 Y1 V1 Y2 U2 Y3 V3 --》YUYV像素[Y0 U0 V1] [Y1 U0 V1] [Y2 U2 V3] [Y3 U2 V3]--》RGB像素
    int r1, g1, b1; 
    int r2, g2, b2;
    int i =0 ;
    for(i=0; i<w*h/2; i++)
    {
        char data[4];
        memcpy(data, yuyvdata+i*4, 4);
        unsigned char Y0=data[0];
        unsigned char U0=data[1];
        unsigned char Y1=data[2];
                unsigned char V1=data[3]; 
        //Y0U0Y1V1  -->[Y0 U0 V1] [Y1 U0 V1]
        r1 = Y0+1.4075*(V1-128); if(r1>255)r1=255; if(r1<0)r1=0;
        g1 =Y0- 0.3455 * (U0-128) - 0.7169*(V1-128); if(g1>255)g1=255; if(g1<0)g1=0;
        b1 = Y0 + 1.779 * (U0-128);  if(b1>255)b1=255; if(b1<0)b1=0;
            
        r2 = Y1+1.4075*(V1-128);if(r2>255)r2=255; if(r2<0)r2=0;
        g2 = Y1- 0.3455 * (U0-128) - 0.7169*(V1-128); if(g2>255)g2=255; if(g2<0)g2=0;
        b2 = Y1 + 1.779 * (U0-128);  if(b2>255)b2=255; if(b2<0)b2=0;
                
        rgbdata[i*6+0]=r1;
        rgbdata[i*6+1]=g1;
        rgbdata[i*6+2]=b1;
        rgbdata[i*6+3]=r2;
        rgbdata[i*6+4]=g2;
        rgbdata[i*6+5]=b2;
    }
}

彩色图像水平镜像

/**
*  @desc       彩色图像水平镜像
*  @param pImg 图像缓存,3个像素一个字节,RGB888格式
*  @param w    图像宽度
*  @param h    图像高度 
**/
void rotateColorImgMirrorH(unsigned char *pImg,unsigned int w,unsigned int h)
{
	unsigned char tmp;
	unsigned int i=0,j=0,m=0;	
	unsigned char *t = NULL,*tt = NULL;	
	unsigned int w3 = w*3;	
	tt = pImg;
	for(i=0;i<h;i++)
	{
		t = tt + ((w-1)*3);
		for(j=0;j<(w>>1);j++)
		{
			for(m=0;m<3;m++)
			{
				tmp = *tt;
				*tt = *t;
				*t = tmp;
				tt++;
				t++;
			}
			t -= 6;
		}
 		tt += ((w>>1)*3);
	}			
}

客户端整体程序

#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>     
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <linux/videodev2.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <linux/fb.h>

#define SERVER_PORT 55555
#define FB_DEV "/dev/fb0"

int sockfd;
static int width;                       //LCD宽度
static int height;                      //LCD高度
static int fb_fd = -1;                  //LCD设备文件描述符
static int v4l2_fd = -1;               //摄像头设备文件描述符
static unsigned int *screen_base = NULL;//LCD显存基地址
static unsigned char *frm_base[4] = {NULL};             //帧缓冲起始地址
unsigned char rgbdatabuf[640*480*4]={0};
char yuyvbuf[614400]={0};

/* 初始化 LCD */
static int fb_dev_init(void)
{
    struct fb_var_screeninfo fb_var = {0};
    struct fb_fix_screeninfo fb_fix = {0};
    unsigned long screen_size;
    /* 打开framebuffer设备 */
    fb_fd = open(FB_DEV, O_RDWR);
    if (0 > fb_fd) {
        perror("open_fb error");
        return -1;
    }
    /* 获取framebuffer设备信息 */
    ioctl(fb_fd, FBIOGET_VSCREENINFO, &fb_var);
    ioctl(fb_fd, FBIOGET_FSCREENINFO, &fb_fix);

    screen_size = fb_fix.line_length * fb_var.yres;
    width = fb_var.xres_virtual;
    height = fb_var.yres_virtual;
    printf("%d*%d\n", width, height);
    /* 内存映射 */
    screen_base = mmap(NULL, screen_size, PROT_READ | PROT_WRITE, MAP_SHARED, fb_fd, 0);
    if (MAP_FAILED == (void *)screen_base) {
        perror("mmap error");
        close(fb_fd);
        return -1;
    }
    /* LCD背景刷白 */
    memset(screen_base, 0xFF, screen_size);
    return 0;
}
//YUYV422 转RGB 数据
void yuyv_to_rgb(unsigned char *yuyvdata, unsigned char *rgbdata, int w, int h)
{
    //码流Y0 U0 Y1 V1 Y2 U2 Y3 V3 --》YUYV像素[Y0 U0 V1] [Y1 U0 V1] [Y2 U2 V3] [Y3 U2 V3]--》RGB像素
    int r1, g1, b1; 
    int r2, g2, b2;
    int i =0 ;
    for(i=0; i<w*h/2; i++)
    {
        char data[4];
        memcpy(data, yuyvdata+i*4, 4);
        unsigned char Y0=data[0];
        unsigned char U0=data[1];
        unsigned char Y1=data[2];
                unsigned char V1=data[3]; 
        //Y0U0Y1V1  -->[Y0 U0 V1] [Y1 U0 V1]
        r1 = Y0+1.4075*(V1-128); if(r1>255)r1=255; if(r1<0)r1=0;
        g1 =Y0- 0.3455 * (U0-128) - 0.7169*(V1-128); if(g1>255)g1=255; if(g1<0)g1=0;
        b1 = Y0 + 1.779 * (U0-128);  if(b1>255)b1=255; if(b1<0)b1=0;
            
        r2 = Y1+1.4075*(V1-128);if(r2>255)r2=255; if(r2<0)r2=0;
        g2 = Y1- 0.3455 * (U0-128) - 0.7169*(V1-128); if(g2>255)g2=255; if(g2<0)g2=0;
        b2 = Y1 + 1.779 * (U0-128);  if(b2>255)b2=255; if(b2<0)b2=0;
                
        rgbdata[i*6+0]=r1;
        rgbdata[i*6+1]=g1;
        rgbdata[i*6+2]=b1;
        rgbdata[i*6+3]=r2;
        rgbdata[i*6+4]=g2;
        rgbdata[i*6+5]=b2;
    }
}
/**
*  @desc       彩色图像水平镜像
*  @param pImg 图像缓存,3个像素一个字节,RGB888格式
*  @param w    图像宽度
*  @param h    图像高度 
**/
void rotateColorImgMirrorH(unsigned char *pImg,unsigned int w,unsigned int h)
{
	unsigned char tmp;
	unsigned int i=0,j=0,m=0;	
	unsigned char *t = NULL,*tt = NULL;	
	unsigned int w3 = w*3;	
	tt = pImg;
	for(i=0;i<h;i++)
	{
		t = tt + ((w-1)*3);
		for(j=0;j<(w>>1);j++)
		{
			for(m=0;m<3;m++)
			{
				tmp = *tt;
				*tt = *t;
				*t = tmp;
				tt++;
				t++;
			}
			t -= 6;
		}
 		tt += ((w>>1)*3);
	}			
}
void show_pic(unsigned char * pic_data, int w, int h)
{
    int i,j;
    unsigned int * p = screen_base;//LCD起始地址
    unsigned int tmpdata;

    for(i=0;i<h;i++)//对某一行
    {
        for(j=0;j<w;j++)//一行中的一个像素点
        {
            //三个十六进制表示一个像素点
			//j*3表示当前行的第j+1个十六进制的数据
			//i*w*3表示第i+1行
            tmpdata = (pic_data[j*3+i*w*3]<<16 | pic_data[j*3+i*w*3+1]<<8 | pic_data[(j*3)+(i*w*3)+2]<<0);
            * (p+j) = tmpdata;
        }
        p+=width;
    }
}
void Recv_Video_Data(int fd,uint8_t * data,int data_len)
{
    int tmp = 0;
    int current = 0;
    if(fd > 0)
    {
        while(1)
        {
            tmp = read(fd,data,data_len);
            if(tmp < 0){
                perror("read buf");
                close("fd");
                exit(1);
            }
            current = data_len - tmp;
            data+=tmp;
            data_len = current;
            if(data_len == 0)
            {   
                break;
            }
        }
    }  
}
void tcp_client(char * SERVER_IP)
{
    struct sockaddr_in server_addr = {0};
    int ret;

    /* 打开套接字,得到套接字描述符 */
    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (0 > sockfd) {
        perror("socket error");
        exit(EXIT_FAILURE);
    }
    /* 调用 connect 连接远端服务器 */
    server_addr.sin_family = AF_INET;//IPV4
    server_addr.sin_port = htons(SERVER_PORT); //端口号
    server_addr.sin_addr.s_addr = inet_addr(SERVER_IP);
    // inet_pton(AF_INET, SERVER_IP, &server_addr.sin_addr);//IP 地址
    
    /* 请求与服务器建立连接 */
    ret = connect(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr));
    if (0 > ret) {
        perror("connect error");
        close(sockfd);
        exit(EXIT_FAILURE);
    }
    printf("服务器连接成功...\n\n");

    /* 向服务器发送数据 */
    while(1) 
    {
        Recv_Video_Data(sockfd,yuyvbuf,sizeof(yuyvbuf));
        yuyv_to_rgb(yuyvbuf, rgbdatabuf ,640, 480);
        rotateColorImgMirrorH(rgbdatabuf,640,480);
        show_pic(rgbdatabuf,640,480);
        usleep(66000);
    }
    close(sockfd);
    exit(EXIT_SUCCESS);
}

int main(int argc, char **argv)
{
    fb_dev_init();
    tcp_client("192.168.88.88");
}
  • 2
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
Linux网络编程(总共41集) 讲解Linux网络编程知识,分以下四个篇章。 Linux网络编程之TCP/IP基础篇 Linux网络编程之socket编程篇 Linux网络编程之进程间通信篇 Linux网络编程之线程篇 Linux网络编程之TCP/IP基础篇 01TCPIP基础(一) ISO/OSI参考模型 TCP/IP四层模型 基本概念(对等通信、封装、分用、端口) 02TCPIP基础(二) 最大传输单元(MTU)/路径MTU 以太网帧格式 ICMP ARP RARP 03TCPIP基础(三) IP数据报格式 网际校验和 路由 04TCPIP基础(四) TCP特点 TCP报文格式 连接建立三次握手 连接终止四次握手 TCP如何保证可靠性 05TCPIP基础(五) 滑动窗口协议 UDP特点 UDP报文格式 Linux网络编程之socket编程篇 06socket编程(一) 什么是socket IPv4套接口地址结构 网络字节序 字节序转换函数 地址转换函数 套接字类型 07socket编程(二) TCP客户/服务器模型 回射客户 /服务器 socket、bind、listen、accept、connect 08socket编程(三) SO_REUSEADDR 处理多客户连接(process-per-conection) 点对点聊天程序实现 09socket编程(四) 流协议与粘包 粘包产生的原因 粘包处理方案 readn writen 回射客户/服务器 10socket编程(五) read、write与recv、send readline实现 用readline实现回射客户/服务器 getsockname、getpeername gethostname、gethostbyname、gethostbyaddr 11socket编程(六) TCP回射客户/服务器 TCP是个流协议 僵进程与SIGCHLD信号 12socket编程(七) TCP 11种状态 连接建立三次握手、连接终止四次握手 TIME_WAIT与SO_REUSEADDR SIGPIPE 13socket编程(八) 五种I/O模型 select 用select改进回射客户端程序 14socket编程(九) select 读、写、异常事件发生条件 用select改进回射服务器程序。 15socket编程(十) 用select改进第八章点对点聊天程序 16socket编程(十一) 套接字I/O超时设置方法 用select实现超时 read_timeout函数封装 write_timeout函数封装 accept_timeout函数封装 connect_timeout函数封装 17socket编程(十二) select限制 poll 18socket编程(十三) epoll使用 epoll与select、poll区别 epoll LT/ET模式 19socket编程(十四) UDP特点 UDP客户/服务基本模型 UDP回射客户/服务器 UDP注意点 20socket编程(十五) udp聊天室实现 21socket编程(十六) UNIX域协议特点 UNIX域地址结构 UNIX域字节流回射客户/服务 UNIX域套接字编程注意点 22socket编程(十七) socketpair sendmsg/recvmsg UNIX域套接字传递描述符字 Linux网络编程之进程间通信篇 23进程间通信介绍(一) 进程同步与进程互斥 进程间通信目的 进程间通信发展 进程间通信分类 进程间共享信息的三种方式 IPC对象的持续性 24进程间通信介绍(二) 死锁 信号量 PV原语 用PV原语解决司机与售票员问题 用PV原语解决民航售票问题 用PV原语解决汽车租赁问题 25System V消息队列(一) 消息队列 IPC对象数据结构 消息队列结构 消息队列在内核中的表示 消息队列函数 26System V消息队列(二) msgsnd函数 msgrcv函数 27System V消息队列(三) 消息队列实现回射客户/服务器 28共享内存介绍 共享内存 共享内存示意图 管道、消息队列与共享内存传递数据对比 mmap函数 munmap函数 msync函数 29System V共享内存 共享内存数据结构 共享内存函数 共享内存示例 30System V信号量(一) 信号量 信号量集结构 信号量集函数 信号量示例 31System V信号量(二) 用信号量实现进程互斥示例 32System V信号量(三) 用信号集解决哲学家就餐问题 33System V共享内存与信号量综合 用信号量解决生产者消费者问题 实现shmfifo 34POSIX消息队列 POSIX消息队列相关函数 POSIX消息队列示例 35POSIX共享内存 POSIX共享内存相关函数 POSIX共享内存示例 Linux网络编程之线程篇 36线程介绍 什么是线程 进程与线程 线程优缺点 线程模型 N:1用户线程模型 1:1核心线程模型 N:M混合线程模型 37POSIX线程(一) POSIX线程库相关函数 用线程实现回射客户/服务器 38POSIX线程(二) 线程属性 线程特定数据 39POSIX信号量与互斥锁 POSIX信号量相关函数 POSIX互斥锁相关函数 生产者消费者问题 自旋锁与读写锁介绍 40POSIX条件变量 条件变量 条件变量函数 条件变量使用规范 使用条件变量解决生产者消费者问题 41一个简单的线程池实现 线程池性能分析 线程池实现 网络编程, Linux

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值