tiny4412采集YUYV422转码RGB32 液晶显示屏实时显示

目的是通过tiny4412开发板采集视频通过压缩最后经RTP传输到电脑端的VLC播放器。所以第一步为采集摄像头视频并通过液晶屏显示出来。首先是初始化:先看看V4L2采集视频的初始化

bool Camera::init_device(void) {

    v4l2_input input;
    memset(&input, 0, sizeof(struct v4l2_input));
    input.index = 0;
    if (ioctl(fd, VIDIOC_ENUMINPUT, &input) != 0) {

        fprintf(stderr, "No matching index found\n");
        return false;
    }
    if (!input.name) {

        fprintf(stderr, "No matching index found\n");
        return false;
    }
    if (ioctl(fd, VIDIOC_S_INPUT, &input) < 0) {

        fprintf(stderr, "VIDIOC_S_INPUT failed\n");
        return false;
    }


    struct v4l2_fmtdesc fmt1; //v4l2_fmtdesc : 帧格式结构体
    int ret;
    memset(&fmt1, 0, sizeof(fmt1));//将fmt1结构体填充为0
    fmt1.index = 0;            //初始化为0,要查询的格式序号
    fmt1.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; // 数据流类型,必须永远是V4L2_BUF_TYPE_VIDEO_CAPTURE
    while ((ret = ioctl(fd, VIDIOC_ENUM_FMT, &fmt1)) == 0) //显示所有支持的格式
    {
        fmt1.index++;
        printf("{ pixelformat = '%c%c%c%c', description = '%s' }\n",fmt1.pixelformat & 0xFF, 
        (fmt1.pixelformat >> 8) & 0xFF,(fmt1.pixelformat >> 16) & 0xFF, 
        (fmt1.pixelformat >> 24) & 0xFF,fmt1.description); //  pixelformat;格式32位   description[32];// 格式名称8位
    }

     /**************************设置当前帧格式************************/
    struct v4l2_format fmt; //设置当前格式结构体(v4l2_format包含v4l2_fmtdesc  fmt为共用体)
    CLEAR (fmt);
    fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;//数据流类型
    fmt.fmt.pix.width = width;
    fmt.fmt.pix.height = height;
    fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;//V4L2_PIX_FMT_NV12;//视频数据存储类型
    fmt.fmt.pix.field = V4L2_FIELD_NONE;

    if (-1 == xioctl(fd, VIDIOC_S_FMT, &fmt))//设置图像格式
        return false;
     /**************************读取当前帧格式*************************/
    if (-1 == xioctl(fd, VIDIOC_G_FMT, &fmt))
    {   

        return false;

    }
    else
    printf("\nCurrent data format information:\n twidth:%d \n theight:%d \n",
                                     fmt.fmt.pix.width,fmt.fmt.pix.height);
     printf(" pixelformat = '%c%c%c%c'\n",fmt.fmt.pix.pixelformat & 0xFF, 
    (fmt.fmt.pix.pixelformat >> 8) & 0xFF,(fmt.fmt.pix.pixelformat >> 16) & 0xFF, 
    (fmt.fmt.pix.pixelformat >> 24) & 0xFF); //  pixelformat;格式32位      

    //原始摄像头数据每帧的大小
    cap_image_size = fmt.fmt.pix.sizeimage;
    init_mmap();

    return true;
}

在这个函数中首先显示摄像头所有支持的输出格式,其次设置当前采集格采集格式为V4L2_PIX_FMT_YUYV即YUYV422。

int main(void) {

    int width=640;
    int height=480;
    unsigned char* image;

    camera=new Camera(CAM_DEV, width, height);
    if(!camera->OpenDevice()){
        printf("Cam Open error\n");
        return -1;
    }
    fb = new Fb(FB_DEV,  80, 0, width, height);
    if(!fb->OpenDevice()){
        printf("Fb Open error\n");
        return -1;
    }

    fb->Trans(&image);
    printf("Waiting for signal SIGINT..\n");
    signal(SIGINT, sign_func);

    while(1){

        if(!camera->GetBuffer(image)){
            break;
        }
        fb->Draw();
    }

    return 0;
}

程序中有两个类,分别是Camera,Fb类。与V4L2相关操作的都在Camera中。Fb类主要是液晶屏显示。在这里unsigned char* image;这个指针比较重要,在程序中通过fb->Trans(&image); image=show_buffer。show_buffer为显示缓冲区。注意这里函数传递的是二级指针。

在while循环中

while(1){

        if(!camera->GetBuffer(image)){
            break;
        }
        fb->Draw();
    }

camera->GetBuffer(image)将采集到的视频存放的起始地址为image。接下来看看如何让进行视频采集

bool Camera::GetBuffer(unsigned char *image){

    fd_set fds;
    struct timeval tv;
    int r;

    FD_ZERO(&fds);
    FD_SET(fd, &fds);
    /* Timeout. */
    tv.tv_sec = 2;
    tv.tv_usec = 0;

    r = select(fd + 1, &fds, NULL, NULL, &tv);
    if (-1 == r) {
        errno_exit("select");
    }
    if (0 == r) {
        fprintf(stderr, "select timeout\n");
        exit(EXIT_FAILURE);
    }
    read_frame(image);

    return true;
}

int Camera::read_frame(unsigned char *image) {
    struct v4l2_buffer buf;

    //CLEAR (buf);
    buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    buf.memory = V4L2_MEMORY_MMAP;
    if (-1 == xioctl(fd, VIDIOC_DQBUF, &buf)) //把数据从缓存中读取出来
    {
        switch (errno) {
        case EAGAIN:
            return 0;
        case EIO:
            /* Could ignore EIO, see spec. */
            /* fall through */
        default:
            errno_exit("VIDIOC_DQBUF");
        }
    }
    assert(buf.index < CAPTURE_BUFFER_NUMBER);
     video_yuyv_to_rgb32((unsigned char*)buffers[0].start,(unsigned char*)image,width,height);
    if (-1 == xioctl(fd, VIDIOC_QBUF, &buf))//再将其入列,把数据放回缓存队列
        errno_exit("VIDIOC_QBUF");

    return 1;
}

通过video_yuyv_to_rgb24((unsigned char*)buffers[0].start,(unsigned char*)image,width,height);函数,将采集到视频进行转码,转码后存放于起始地址为image处,正好是触摸屏的显示缓冲区。
接下来看看转码函数。关于转码,这里首先需要了解RGB32和YUYV(yuv422)的编码格式。首先是RGB32,RGB32使用四个字节表示一个像素,排列顺序分别是B,G,R,A(用作Alpha通道或忽略)。YUYV422:排列顺序为Y0U0Y1V0 Y2U1Y3V2(共四个像素点,一个像素点一个Y)。两个Y共用一组UV,单像素占2bytes内存。
所以在下面的转码程序中size=width*height*2;一次for循环转码两个像素点,所以out每次都是加8,其次经过yuvtorgb()函数后,只填充B,G,R;A选择忽略。

//YUYV(yuv422)转RGB32
void Camera:: video_yuyv_to_rgb32(unsigned char *yuv, unsigned char *rgb, unsigned int width, unsigned int height)
{  
    unsigned int in, out;
     int y0, u, y1, v;
     unsigned int pixel24;
      unsigned char *pixel = (unsigned char *)&pixel24;
      unsigned int size = width*height*2;

      for (in = 0, out = 0; in < size; in += 4, out += 8)
     {
         y0 = yuv[in+0];
         u  = yuv[in+1];
         y1 = yuv[in+2];
         v  = yuv[in+3];

         sign3 = true;
         pixel24 = yuvtorgb(y0, u, v);
         rgb[out+0] = pixel[0];    //for QT
         rgb[out+1] = pixel[1];
         rgb[out+2] = pixel[2];

         //sign3 = true;
         pixel24 = yuvtorgb(y1, u, v);
         rgb[out+4] = pixel[0];
         rgb[out+5] = pixel[1];
         rgb[out+6] = pixel[2];

    }
     //return 0;

}

下面是yuv转码rgb的函数,这里要注意最后的排列顺序,是b,g,r。转化算法可以百度查到。

int Camera:: yuvtorgb(int y, int u, int v)
{
    unsigned int pixel24 = 0;
     unsigned char *pixel = (unsigned char *)&pixel24;
     int r, g, b;
     static long int ruv, guv, buv;

     if (sign3)
     {
         sign3 = false;
         ruv = 1159*(v-128);
         guv = 380*(u-128) + 813*(v-128);
         buv = 2018*(u-128);
    }

     r = (1164*(y-16) + ruv) / 1000;
     g = (1164*(y-16) - guv) / 1000;
     b = (1164*(y-16) + buv) / 1000;

     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] = b;
     pixel[1] = g;
     pixel[2] = r;

     return pixel24;
}

程序的下载地址为https://download.csdn.net/download/qq_26742291/10406386

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值