首页 > 其他分享 >视频监控—在LCD上显示摄像头图像

视频监控—在LCD上显示摄像头图像

时间:2023-08-09 09:14:00浏览次数:40  
标签:视频 return int pixeldatas LCD video pixel 摄像头 out

一、框架 1、程序框架 仿照之前数码相册的框架来进行改造,完成在开发板上动态显示摄像头的数据信息:   对于上述完成主要功能的5个部分:display显示部分、debug调试信息输出部分、render渲染部分、video视频设备部分、convert格式转换部分   video视频设备部分:负责获得摄像头的原始数据; convert格式转换部分:负责对于摄像头的原始数据,进行格式转换,对于不同的摄像头有不同的数据格式,需要包含多个文件来支持多种转换方式; render渲染部分:负责对转换得到的数据格式,进行压缩、合并成可以在LDC上显示的数据; display显示部分:合并后的数据显示在LCD上; debug调试部分:设置打印等级和打印通道,通过打印等级来控制程序打印的调试信息、错误信息、警告信息等,通过打印通道设置来控制程序打印的输出的流向是标准输出还是网络打印输出。 2、Makefile框架   分为如下3部分:   顶层目录的Makefile:定义obj-y来指定根目录下要编进程序去的文件、子目录外,主要是定义工具链、编译参数、链接参数。 顶层目录的Makefile.build:把某个目录及它的所有子目录中、需要编进程序去的文件都编译出来,打包为built-in.o。 各级子目录的Makefile:把当前目录下的.c文件编进程序里。 二、数据的流向   如上图所示:   在 video视频设备部分,会从硬件中读取出原始的摄像头数据,存在videobuf中,这些数据可能是YUYV、MJPEG、RGB565格式等,需要convert格式转换部分转换成rgb格式 在convert格式转换部分,会根据video视频设备部分传来的数据格式videobuf,调用适合的转换文件来把原始的摄像头数据转换成rgb格式,存储在convertbuf中。 在render渲染部分中,对已经转为rgb格式的数据convertbuf进行缩放,随后渲染成可以直接可以在LCD上显示的数据,存储在zoombuf中,把整理好的数据数据流入到Framebuffer中 在display显示部分中,对Framebuffe的数据进行最后的排版与处理,最终显示在LCD上。 三、video视频设备部分代码编写 对于这个部分,采用面向对象的编程思想,抽取了如下的结构体:   typedef struct VideoDevice T_VIDEODEVICE, *PT_VIDEODEVICE; typedef struct VideoBuf T_VIDEOBUF, *PT_VIDEOBUF; typedef struct VdieoOpr T_VIDEOOPR, *PT_VIDEOOPR;   /*!  * 摄像头的描述信息  */ struct VideoDevice {     int fd;                 /**< 文件句柄 */     int pixel_format;       /**< 像素格式 */     int width;              /**< 图像宽度 */     int height;             /**< 图像高度 */     int videobuf_num;       /**< 分配缓冲区个数 */     int videeobuf_maxlen;   /**< 每个缓存区的大小 */     int cur_bufindex;       /**< 存储从队列取出缓冲区的index */          /**! 所分配的缓冲区 */     unsigned char *videobuf[REQ_BUFFER_NUM];       PT_VIDEOOPR video_opr;  /**< 数据处理 */ };   /*!  * 存储视频数据  */ struct VideoBuf {     T_PIXELDATAS pixel_data;    /**< 借用T_PixelDatas */     int pixel_format;           /**< 像素格式 */ };   /*!  * 视频数据处理结构体  */ struct VdieoOpr {     char *name; /**< 设备名 */          /* 初始化设备 */     int (*InitDevice)(char *dev_name, PT_VIDEODEVICE video_device);       /* 退出设备 */     int (*ExitDevice)(PT_VIDEODEVICE video_device);       /* 启动设备 */     int (*StartDevice)(PT_VIDEODEVICE video_device);       /* 停止设备 */     int (*StopDevice)(PT_VIDEODEVICE video_device);       /* 获得视频数据 */     int (*GetFrame)(PT_VIDEODEVICE video_device, PT_VIDEOBUF video_buf);       /* 获得视频格式 */     int (*GetFormat)(PT_VIDEODEVICE video_device);       /* 把视频数据放回队列 */     int (*PutFrame)(PT_VIDEODEVICE video_device, PT_VIDEOBUF video_buf);       struct VdieoOpr *ptNext; };     1、video_manager.c 对于其的管理模式,也是采用链表的方式进行管理。 在这个文件中,向下提供注册函数,供每个支持的设备注册进链表中,进行统一的管理。 向上提供设备的初始化函数,供上层应用来调用初始化整个video部分。   /*******************************************************************************  * Copyleft (c) 2021 Kcode  *  * @file    video_manager.c  * @brief   视频数据管理者文件,函数的实现  * @author  K  * @version 0.0.1  * @date    2021-07-26  * @license MulanPSL-1.0  *  * 文件修改历史:  * <时间>       | <版本>    | <作者>  | <描述>  * 2021-07-26   | v0.0.1    | Kcode   | 视频数据管理者文件  * -----------------------------------------------------------------------------  ******************************************************************************/   #include <string.h>   #include "config.h" #include "video_manager.h" #include "debug_manager.h"   static PT_VIDEOOPR s_ptVideoOprHead = NULL; /**< 支持设备链表头 */   /*!  * @brief  注册函数  * @param  ptVideoOpr[in] 要注册的设备结构体结点  * @return 0:成功 -1:失败  */ int RegisterVideoOpr(PT_VIDEOOPR ptVideoOpr) { PT_VIDEOOPR ptTmp;   if (!s_ptVideoOprHead) { s_ptVideoOprHead   = ptVideoOpr; ptVideoOpr->ptNext = NULL; } else { ptTmp = s_ptVideoOprHead; while (ptTmp->ptNext) { ptTmp = ptTmp->ptNext; } ptTmp->ptNext     = ptVideoOpr; ptVideoOpr->ptNext = NULL; }   return 0; }   /*!  * @brief  显示所支持的设备  * @param  [in] 无  * @return 无   */ void ShowVideoOpr(void) { int i = 0; PT_VIDEOOPR ptTmp = s_ptVideoOprHead;   while (ptTmp) { printf("%02d %s\n", i++, ptTmp->name); ptTmp = ptTmp->ptNext; } }   /*!  * @brief  根据name得到指定的设备文件  * @param  pName[in] 名字  * @return 成功:返回设备的结构体指针 失败:返回空指针  */ PT_VIDEOOPR GetVideoOpr(char *pName) { PT_VIDEOOPR ptTmp = s_ptVideoOprHead;   while (ptTmp) { if (strcmp(ptTmp->name, pName) == 0) { return ptTmp; } ptTmp = ptTmp->ptNext; } return NULL; }   /*!  * @brief  初始化设备函数,把对支持的各个设备进行初始化工作  * @param  devname[in] 名字  * @param  video_dev[in] 视频设备  * @return  0:初始化成功 -1:失败  */ int VideoDeviceInit(char * devname, PT_VIDEODEVICE video_dev) {     int error;     PT_VIDEOOPR ptTmp = s_ptVideoOprHead;   while (ptTmp) { error = ptTmp->InitDevice(devname, video_dev);         if (!error)             return 0;                  ptTmp = ptTmp->ptNext; }       return -1; }   /*!  * @brief  初始化函数,把对支持的各个设备注册进链表进行统一管理  * @param  [in] 无  * @return  无  */ int VideoInit(void) { int error;   error = V4l2Init(); if (error) { DBG_PRINTF("V4l2Init error!\n"); return -1; }   return 0; }   /*!  * @brief  退出函数,对设备进行具体的清理工作  * @param  [in] 无  * @return  无  */ void VideoExit(char *name, PT_VIDEOBUF video_buf) { PT_VIDEOOPR ptTmp = s_ptVideoOprHead;   while (ptTmp) { if (strcmp(ptTmp->name, name) == 0)             ptTmp->ExitDevice(video_buf);           ptTmp = ptTmp->ptNext; } }     2、v4l2.c 对于这个文件,是具体的设备文件。 在这个文件中,实现对摄像头数据的捕获、处理与存储。 整个执行流程与uvc驱动程序差不多,仿照luvcview来进行编写,涉及的函数开发大致如下:   打开驱动open 获取该设备的类型 VIDIOC_QUERYCAP 列举摄像头的数据格式 VIDIOC_ENUM_FMT 获得摄像头的数据格式 VIDIOC_G_FMT 测试是否支持该摄像头的数据格式 VIDIOC_TRY_FMT 设置参数VIDIOC_S_FMT 请求系统分配缓冲区VIDIOC_REQBUFS 查询所分配的缓冲区的信息 VIDIOC_QUERYBUF 映射缓冲区mmap 把缓冲区放入队列 VIDIOC_QBUF /*******************************************************************************  * Copyleft (c) 2021 Kcode  *  * @file    v4l2.c  * @brief   v4l2设备的文件,参考luvcview  * @author  K  * @version 0.0.1  * @date    2021-07-26  * @license MulanPSL-1.0  *  * 文件修改历史:  * <时间>       | <版本>    | <作者>  | <描述>  * 2021-07-26   | v0.0.1    | Kcode   | v4l2设备的文件  * -----------------------------------------------------------------------------  ******************************************************************************/   #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <sys/mman.h> #include <poll.h> #include <sys/ioctl.h> #include <string.h> #include <stdlib.h> #include <unistd.h> #include <linux/videodev2.h> #include <sys/mman.h>   #include "config.h" #include "debug_manager.h" #include "video_manager.h" #include "disp_manager.h"   static T_VIDEOOPR s_V4l2VideoOpr;   static int V4l2GetFrameForReadWrite(PT_VIDEODEVICE video_device,                                             PT_VIDEOBUF video_buf);   static int V4l2PutFrameForReadWrite(PT_VIDEODEVICE video_device,                                             PT_VIDEOBUF video_buf);     static int V4l2PutFrameForStreaming(PT_VIDEODEVICE video_device,                                             PT_VIDEOBUF video_buf);   static int V4l2GetFrameForStreaming(PT_VIDEODEVICE video_device,                                             PT_VIDEOBUF video_buf);     /* 所支持的格式 */ static int s_isSupportedFormat[] = {     V4L2_PIX_FMT_YUYV,     V4L2_PIX_FMT_MJPEG,     V4L2_PIX_FMT_RGB565, };   /*!  * @brief  判断是否支持此格式  * @return  0:不支持 1:支持  */ static int isSupportThisFormat(int pixel_format) {     int i;     int size;       size = sizeof(s_isSupportedFormat)/sizeof(s_isSupportedFormat[0]);     for(i = 0; i < size; i++)     {         if (s_isSupportedFormat[i] == pixel_format)             return 1;     }       return 0; }      /*!  * @brief 初始化设备  *        1、确定是否为视频捕获设备、支持何种接口(streaming/read、write)  *        2、查询支持何种属性  *        3、设置摄像头格式  *        4、向驱动程序申请buffer  *        5、确定每个buffer的信息且mmap  *        6、把buffer放入队列  *        7、启动设备  *        8、poll机制等待数据  *        9、有数据则从队列中取出并处理  *       10、再次放入队列,重复步骤  *       11、不操作时停止设备  */ static int V4l2InitDevice(char *dev_name, PT_VIDEODEVICE video_device) {     int i;     int fd;     int ret;     int lcd_width;     int lcd_height;     int lcd_bpp;     struct v4l2_capability v4l2cap;     struct v4l2_fmtdesc v4l2fmt_desc;     struct v4l2_format v4l2fmt;     struct v4l2_requestbuffers v4l2req_buf;     struct v4l2_buffer v4l2buf;              /*!      * 打开设备      */     fd = open(dev_name, O_RDWR);     if (fd < 0)     {         DebugPrint(APP_ERR"open error! Function:%s Line:%d\n",                                      __FUNCTION__, __LINE__);         return -1;     }       video_device->fd = fd;          /*!      * 调用VIDIOC_QUERYCAP的ioctl      */     memset(&v4l2cap, 0, sizeof(struct v4l2_capability));     ret = ioctl(fd, VIDIOC_QUERYCAP, &v4l2cap);     if (ret < 0)     {         DebugPrint(APP_ERR"Error opening device %s: unable to query device.\n",            dev_name);         goto err_eixt;     }       /*!      * 判断是否为视频捕获设备      */     if (!(v4l2cap.capabilities & V4L2_CAP_VIDEO_CAPTURE))     {         DebugPrint(APP_ERR"%s is not a video capture device\n", dev_name);         goto err_eixt;     }       if (v4l2cap.capabilities & V4L2_CAP_STREAMING)     DebugPrint(APP_NOTICE"%s supports streaming i/o\n", dev_name);    if (v4l2cap.capabilities & V4L2_CAP_READWRITE)     DebugPrint(APP_NOTICE"%s supports read i/o\n", dev_name);          /*!      * 枚举所支持的格式      */     memset(&v4l2fmt_desc, 0, sizeof(v4l2fmt_desc));     v4l2fmt_desc.index = 0;     v4l2fmt_desc.type  = V4L2_BUF_TYPE_VIDEO_CAPTURE;     while ((ret = ioctl(fd, VIDIOC_ENUM_FMT, &v4l2fmt_desc)) == 0)     {         if(isSupportThisFormat(v4l2fmt_desc.pixelformat))         {             video_device->pixel_format = v4l2fmt_desc.pixelformat;             break;         }         v4l2fmt_desc.index++;     }       /*!      * 无支持的设备格式      */     if (!video_device->pixel_format)     {         DebugPrint(APP_ERR"Can not support the format of this device\n");         goto err_eixt;     }       /*!      * 设置格式      */     GetDispResolution(&lcd_width, &lcd_height, &lcd_bpp);     memset(&v4l2fmt, 0, sizeof(struct v4l2_format));     v4l2fmt.type            = V4L2_BUF_TYPE_VIDEO_CAPTURE;     v4l2fmt.fmt.pix.width       = lcd_width;     v4l2fmt.fmt.pix.height      = lcd_height;     v4l2fmt.fmt.pix.pixelformat = video_device->pixel_format;     v4l2fmt.fmt.pix.field       = V4L2_FIELD_ANY;       /* 如果驱动参数无法支持某些参数(分辨率),会调整这些参数,并返回给应用程序 */     ret = ioctl(fd, VIDIOC_S_FMT, &v4l2fmt);     if (ret < 0)     {     DebugPrint(APP_ERR"Unable to set format: %d Function:%s Line:%d\n",                                      __FUNCTION__, __LINE__);     goto err_eixt;     }       /* 重新记录分辨率 */     video_device->width  = v4l2fmt.fmt.pix.width;     video_device->height = v4l2fmt.fmt.pix.height;       /*!      * 申请buffer      */     memset(&v4l2req_buf, 0, sizeof(struct v4l2_requestbuffers));     v4l2req_buf.count  = REQ_BUFFER_NUM;     v4l2req_buf.type   = V4L2_BUF_TYPE_VIDEO_CAPTURE;     v4l2req_buf.memory = V4L2_MEMORY_MMAP;       ret = ioctl(fd, VIDIOC_REQBUFS, &v4l2req_buf);     if (ret < 0)     {     DebugPrint(APP_ERR"Unable to allocate buffers Function:%s Line:%d\n",                                      __FUNCTION__, __LINE__);     goto err_eixt;     }       /* 重新记录缓冲区个数 */     video_device->videobuf_num = v4l2req_buf.count;       /*!      * 对于streaming接口的设备,需要查询缓冲区信息且mmap缓冲区      * 对于read/write接口的设置,只需分配内存,调用read      */     if (v4l2cap.capabilities & V4L2_CAP_STREAMING)     {                  /* 查询并映射每一个缓冲区 */         for (i = 0; i < video_device->videobuf_num; i++)         {             /* 设置 */         memset(&v4l2buf, 0, sizeof(struct v4l2_buffer));         v4l2buf.index  = i;         v4l2buf.type   = V4L2_BUF_TYPE_VIDEO_CAPTURE;         v4l2buf.memory = V4L2_MEMORY_MMAP;               /* 查询 */         ret = ioctl(fd, VIDIOC_QUERYBUF, &v4l2buf);         if (ret < 0)             {             DebugPrint(APP_ERR"Unable to query buffer\n");             goto err_eixt;         }               /* mmap */             video_device->videeobuf_maxlen = v4l2buf.length;             video_device->videobuf[i] = mmap(0 /* start anywhere */ ,                 v4l2buf.length, PROT_READ, MAP_SHARED, fd, v4l2buf.m.offset);             if (video_device->videobuf[i] == MAP_FAILED)             {                 DebugPrint(APP_ERR"Unable to map buffer\n");                 goto err_eixt;             }         }           /*!          * 缓冲区逐个放入队列          */         for (i = 0; i < video_device->videobuf_num; ++i)         {             /* 设置 */             memset(&v4l2buf, 0, sizeof(struct v4l2_buffer));             v4l2buf.index  = i;             v4l2buf.type   = V4L2_BUF_TYPE_VIDEO_CAPTURE;             v4l2buf.memory = V4L2_MEMORY_MMAP;               /* 放入队列 */             ret = ioctl(fd, VIDIOC_QBUF, &v4l2buf);             if (ret < 0)             {                 DebugPrint(APP_ERR"Unable to queue buffer\n");                 goto err_eixt;             }         }     }     else if (v4l2cap.capabilities & V4L2_CAP_READWRITE)     {         s_V4l2VideoOpr.GetFrame = V4l2GetFrameForReadWrite;         s_V4l2VideoOpr.PutFrame = V4l2PutFrameForReadWrite;                  video_device->videobuf_num    = 1;           /* 在程序所能支持的格式中,一个像素最多占4字节 */         video_device->videeobuf_maxlen = video_device->width * \             video_device->height * 4;                  video_device->videobuf[0] = (unsigned char *)malloc(                         video_device->videeobuf_maxlen);     }       video_device->video_opr = &s_V4l2VideoOpr;       return 0;      err_eixt:     close(fd);     return -1; }   /*!  * @brief  退出设备  * @return  0  */ static int V4l2ExitDevice(PT_VIDEODEVICE video_device) {     int i;       if (s_V4l2VideoOpr.GetFrame == V4l2GetFrameForStreaming)     {         for(i = 0; i < video_device->videobuf_num; i++)         {             if (video_device->videobuf[i])             {                 munmap(video_device->videobuf[i], video_device->videeobuf_maxlen);                 video_device->videobuf[i] = NULL;             }         }     }     else if (s_V4l2VideoOpr.GetFrame == V4l2GetFrameForReadWrite)         free(video_device->videobuf[0]);       close(video_device->fd);          return 0; }   /*!  * @brief  启动设备  * @return  0:成功 -1:失败  */ static int V4l2StartDevice(PT_VIDEODEVICE video_device) {     int type = V4L2_BUF_TYPE_VIDEO_CAPTURE;     int ret;       ret = ioctl(video_device->fd, VIDIOC_STREAMON, &type);     if (ret < 0)     {         DebugPrint(APP_ERR"Unable to start capture\n");         return -1;     }       return 0; }   /*!  * @brief  停止设备  * @return  0:成功 -1:失败  */ static int V4l2StopDevice(PT_VIDEODEVICE video_device) {     int type = V4L2_BUF_TYPE_VIDEO_CAPTURE;     int ret;       ret = ioctl(video_device->fd, VIDIOC_STREAMOFF, &type);     if (ret < 0)     {         DebugPrint(APP_ERR"Unable to stop capture\n");         return -1;     }          return 0; }   /*!  * @brief  获得视频数据,Streaming接口  *         poll,VIDIOC_DQBUF  * @return  0:成功 -1:失败  */ static int V4l2GetFrameForStreaming(PT_VIDEODEVICE video_device,                                             PT_VIDEOBUF video_buf) {     int ret;     struct pollfd poll_fd[1];     struct v4l2_buffer v4l2buf;       /*!      * 调用poll机制      */     poll_fd[0].fd     = video_device->fd;     poll_fd[0].events = POLLIN;       /* 永远等待 */     ret = poll(poll_fd, 1, -1);     if (ret <= 0)     {         DebugPrint(APP_ERR"Poll error!\n");         return -1;     }     else     {            /*!          * VIDIOC_DQBUF,从队列中取出缓冲区          */         memset(&v4l2buf, 0, sizeof(struct v4l2_buffer));         v4l2buf.type   = V4L2_BUF_TYPE_VIDEO_CAPTURE;         v4l2buf.memory = V4L2_MEMORY_MMAP;                  ret = ioctl(video_device->fd, VIDIOC_DQBUF, &v4l2buf);         if (ret < 0)         {             DebugPrint(APP_ERR"Unable to dequeue buffer\n");             return -1;         }           /* 记录取出缓冲区的index */         video_device->cur_bufindex = v4l2buf.index;           /* 设置传入的buf */         video_buf->pixel_format       = video_device->pixel_format;         video_buf->pixel_data.width   = video_device->width;         video_buf->pixel_data.height  = video_device->height;         video_buf->pixel_data.bpp  = (video_device->pixel_format == V4L2_PIX_FMT_YUYV)? 16 :\                                      (video_device->pixel_format == V4L2_PIX_FMT_MJPEG)? 0 :\                                      (video_device->pixel_format == V4L2_PIX_FMT_RGB565)? 16 : 0;         video_buf->pixel_data.linebytes  = video_device->width  * video_buf->pixel_data.bpp / 8;         video_buf->pixel_data.TotalBytes = v4l2buf.bytesused;         video_buf->pixel_data.PixelDatas = video_device->videobuf[v4l2buf.index];         }          return 0; }   /*!  * @brief  获得视频数据,read/write接口  *         VIDIOC_REQBUF  * @return  0:成功 -1:失败  */ static int V4l2GetFrameForReadWrite(PT_VIDEODEVICE video_device,                                             PT_VIDEOBUF video_buf) {     int ret;          ret = read(video_device->fd, video_device->videobuf[0],                                video_device->videeobuf_maxlen);     if (ret <= 0)        return -1;       /* 设置传入的buf */     video_buf->pixel_format       = video_device->pixel_format;     video_buf->pixel_data.width   = video_device->width;     video_buf->pixel_data.height  = video_device->height;     video_buf->pixel_data.bpp  = (video_device->pixel_format == V4L2_PIX_FMT_YUYV)? 16 :\                                 (video_device->pixel_format == V4L2_PIX_FMT_MJPEG)? 0 :\                                 (video_device->pixel_format == V4L2_PIX_FMT_RGB565)? 16 : 0;     video_buf->pixel_data.linebytes  = video_device->width  * video_buf->pixel_data.bpp / 8;     video_buf->pixel_data.TotalBytes = ret;     video_buf->pixel_data.PixelDatas = video_device->videobuf[0];                 return 0; }                                              /*!  * @brief  把视频数据重新放入队列,Streaming接口,VIDIOC_QBUF  * @return  0:成功 -1:失败  */ static int V4l2PutFrameForStreaming(PT_VIDEODEVICE video_device,                                             PT_VIDEOBUF video_buf) {     int ret;     struct v4l2_buffer v4l2buf;          memset(&v4l2buf, 0, sizeof(struct v4l2_buffer));     v4l2buf.index  = video_device->cur_bufindex;     v4l2buf.type   = V4L2_BUF_TYPE_VIDEO_CAPTURE;     v4l2buf.memory = V4L2_MEMORY_MMAP;          ret = ioctl(video_device->fd, VIDIOC_QBUF, &v4l2buf);     if (ret < 0)     {         DebugPrint(APP_ERR"Unable to queue buffer\n");         return -1;     }       return 0; }   /*!  * @brief  获得视频数据的原始格式  * @return  0:成功 -1:失败  */ static int V4l2GetFormat(PT_VIDEODEVICE video_device) {     return video_device->pixel_format; }   /*!  * @brief  把视频数据重新放入,read/write接口,无操作  * @return  0:成功 -1:失败  */ static int V4l2PutFrameForReadWrite(PT_VIDEODEVICE video_device,                                             PT_VIDEOBUF video_buf) {     return 0; }   /*!  * 构造T_VIDEOOPR结构体  */ static T_VIDEOOPR s_V4l2VideoOpr = {     .name         = "v4l2",     .InitDevice   = V4l2InitDevice,     .ExitDevice   = V4l2ExitDevice,     .StartDevice  = V4l2StartDevice,     .StopDevice   = V4l2StopDevice,     .GetFrame     = V4l2GetFrameForStreaming,     .GetFormat    = V4l2GetFormat,     .PutFrame     = V4l2PutFrameForStreaming,         };   /*!  * @brief  初始化函数,把s_V412VideoOpr结构体注册进链表中  * @param  [in] 无  * @return 0:成功 -1:失败   */ int V4l2Init(void) {     return RegisterVideoOpr(&s_V4l2VideoOpr); }     四、convert转换部分代码编写 对于这个部分,采用面向对象的编程思想,抽取了如下的结构体:   /*!  * 数据转换操作  */ typedef struct VideoConvertOpr {     char *name;       /* 支持的格式 */     int (*isSupport)(int pixel_format_in, int pixel_format_out);       /* 格式转换 */     int (*Convert)(PT_VIDEOBUF videobuf_in, PT_VIDEOBUF videobuf_out);       /* 退出 */     int (*ConvertExit)(PT_VIDEOBUF videobuf_out);       struct VideoConvertOpr *ptNext; }T_VIDEOCONVERTOPR, *PT_VIDEOCONVERTOPR;     1、convert_manager.c 对于其的管理模式,也是采用链表的方式进行管理。 在这个文件中,向下提供注册函数,供每个支持的设备注册进链表中,进行统一的管理。 向上提供设备的初始化函数,供上层应用来调用初始化整个convert部分。   /*******************************************************************************  * Copyleft (c) 2021 Kcode  *  * @file    yuv2rgb.c  * @brief   转换视频数据格式,把摄像头原始的yuv格式转换成rgb565或32格式  * @author  K  * @version 0.0.1  * @date    2021-07-26  * @license MulanPSL-1.0  *  * 文件修改历史:  * <时间>       | <版本>    | <作者>  | <描述>  * 2021-07-27   | v0.0.1    | Kcode   | 转换视频数据格式  * -----------------------------------------------------------------------------  ******************************************************************************/   #include <linux/videodev2.h> #include <stdlib.h>   #include "color.h" #include "debug_manager.h" #include "convert_manager.h"   /*!  * @brief  判断输入输出格式是否为yuv和rgb  * @return  0:不支持 1:支持  */ static int isSupportYuv2Rgb(int pixel_format_in, int pixel_format_out) {     if (pixel_format_in != V4L2_PIX_FMT_YUYV)         return 0;     if ((pixel_format_out != V4L2_PIX_FMT_RGB565) && \         (pixel_format_out != V4L2_PIX_FMT_RGB32))         return 0;       return 1; }   /*!  * @brief  支持的格式转换yuv为输出格式rgb565  *         参考luvcview  * @return  0:成功  */ static unsigned int Pyuv422torgb565(unsigned char * input_ptr, unsigned char * output_ptr,                  unsigned int image_width, unsigned int image_height) { unsigned int i, size; unsigned char Y, Y1, U, V; unsigned char *buff = input_ptr; unsigned char *output_pt = output_ptr;       unsigned int r, g, b;     unsigned int color;      size = image_width * image_height /2; for (i = size; i > 0; i--) { /* bgr instead rgb ?? */ Y = buff[0] ; U = buff[1] ; Y1 = buff[2]; V = buff[3]; buff += 4; r = R_FROMYV(Y,V); g = G_FROMYUV(Y,U,V); //b b = B_FROMYU(Y,U); //v           /*!          * 第一个颜色,把r,g,b三色构造为rgb565的16位值          */         r = r >> 3;         g = g >> 2;         b = b >> 3;         color = (r << 11) | (g << 5) | b;         *output_pt++ = color & 0xff;         *output_pt++ = (color >> 8) & 0xff;   r = R_FROMYV(Y1,V); g = G_FROMYUV(Y1,U,V); //b b = B_FROMYU(Y1,U); //v           /*!          * 第二个颜色,把r,g,b三色构造为rgb565的16位值          */         r = r >> 3;         g = g >> 2;         b = b >> 3;         color = (r << 11) | (g << 5) | b;         *output_pt++ = color & 0xff;         *output_pt++ = (color >> 8) & 0xff; }   return 0; }    /*!  * @brief  支持的格式转换yuv为输出格式rgb32  *         参考luvcview  * @return  0:成功  */ static unsigned int Pyuv422torgb32(unsigned char * input_ptr, unsigned char * output_ptr,                      unsigned int image_width, unsigned int image_height) { unsigned int i, size; unsigned char Y, Y1, U, V; unsigned char *buff = input_ptr; unsigned int *output_pt = (unsigned int *)output_ptr;       unsigned int r, g, b;     unsigned int color;   size = image_width * image_height /2; for (i = size; i > 0; i--) { /* bgr instead rgb ?? */ Y = buff[0] ; U = buff[1] ; Y1 = buff[2]; V = buff[3]; buff += 4;           /*!          * 第一个颜色,把r,g,b三色构造为rgb888的32位值          */         r = R_FROMYV(Y,V); g = G_FROMYUV(Y,U,V); //b b = B_FROMYU(Y,U); //v color = (r << 16) | (g << 8) | b;         *output_pt++ = color;           /*!          * 第二个颜色,把r,g,b三色构造为rgb888的32位值          */ r = R_FROMYV(Y1,V); g = G_FROMYUV(Y1,U,V); //b b = B_FROMYU(Y1,U); //v color = (r << 16) | (g << 8) | b;         *output_pt++ = color; }   return 0; }    /*!  * @brief  支持的格式转换yuv为输出格式rgb  *         参考luvcview  * @return  0:转换成功 -1:分配内存失败  */ static int Yuv2RgbConvert(PT_VIDEOBUF videobuf_in,                                     PT_VIDEOBUF videobuf_out) {     PT_PIXELDATAS pixeldatas_in   = &videobuf_in->pixel_data;     PT_PIXELDATAS pixeldatas_out = &videobuf_out->pixel_data;       pixeldatas_out->width  = pixeldatas_in->width;     pixeldatas_out->height = pixeldatas_in->height;       /*!      * 判断支持哪种输出格式,调用对应的转换函数      */     if (videobuf_out->pixel_format == V4L2_PIX_FMT_RGB565)     {         pixeldatas_out->bpp = 16;         pixeldatas_out->linebytes = pixeldatas_out->width * pixeldatas_out->bpp / 8;         pixeldatas_out->TotalBytes = pixeldatas_out->linebytes * pixeldatas_out->height;           if (!pixeldatas_out->PixelDatas)         {             pixeldatas_out->PixelDatas = (unsigned char *)malloc(pixeldatas_out->TotalBytes);             if (!pixeldatas_out->PixelDatas)             {                 DebugPrint(APP_ERR"pixeldatas_out->PixelDatas malloc err\n");                 return -1;             }         }                      Pyuv422torgb565(pixeldatas_in->PixelDatas, pixeldatas_out->PixelDatas,             pixeldatas_out->width, pixeldatas_out->height);           return 0;     }     else if (videobuf_out->pixel_format == V4L2_PIX_FMT_RGB32)     {          pixeldatas_out->bpp = 32;         pixeldatas_out->linebytes = pixeldatas_out->width * pixeldatas_out->bpp / 8;         pixeldatas_out->TotalBytes = pixeldatas_out->linebytes * pixeldatas_out->height;           if (!pixeldatas_out->PixelDatas)         {             pixeldatas_out->PixelDatas = (unsigned char *)malloc(pixeldatas_out->TotalBytes);             if (!pixeldatas_out->PixelDatas)             {                 DebugPrint(APP_ERR"pixeldatas_out->PixelDatas malloc err\n");                 return -1;             }         }                    Pyuv422torgb32(pixeldatas_in->PixelDatas, pixeldatas_out->PixelDatas,             pixeldatas_out->width, pixeldatas_out->height);             return 0;     }          return -1; }   /*!  * @brief  退出函数,释放内存  * @return  0  */ static int Yuv2RgbConvertExit(PT_VIDEOBUF videobuf_out) {     if (videobuf_out->pixel_data.PixelDatas)     {         free(videobuf_out->pixel_data.PixelDatas);         videobuf_out->pixel_data.PixelDatas = NULL;     }         return 0; }   static T_VIDEOCONVERTOPR s_Yuv2RgbConvert = {     .name        = "yuv2rgb",     .isSupport   = isSupportYuv2Rgb,     .Convert     = Yuv2RgbConvert,     .ConvertExit = Yuv2RgbConvertExit, };   extern void initLut(void);   /*!  * @brief  初始化函数,把对支持的各个转换格式进行初始化工作  *         对应的结构体放入链表统一管理  * @return  0:初始化成功 1:失败  */ int Yuv2RgbInit(void) {     initLut();     return RegisterVideoConvertOpr(&s_Yuv2RgbConvert); }     2、yuv2rgb.c 这个文件参考了部分luvcview的源码,对于视频的原始格式到转换的格式为yuv---->rgb565/rgb32   通过判断视频源数据格式是否为V4L2_PIX_FMT_YUYV,以及指定的转换格式是否为V4L2_PIX_FMT_RGB565 / V4L2_PIX_FMT_RGB32查看是否支持此转换文件**。   在进行具体的转换时,判断用户所要求的转换格式为V4L2_PIX_FMT_RGB565 / V4L2_PIX_FMT_RGB32,根据情况提前设置好转换格式的bpp、行宽、总的数据大小、像素存储的空间,最后调用具体的转换函数。   /*******************************************************************************  * Copyleft (c) 2021 Kcode  *  * @file    yuv2rgb.c  * @brief   转换视频数据格式,把摄像头原始的yuv格式转换成rgb565或32格式  * @author  K  * @version 0.0.1  * @date    2021-07-26  * @license MulanPSL-1.0  *  * 文件修改历史:  * <时间>       | <版本>    | <作者>  | <描述>  * 2021-07-27   | v0.0.1    | Kcode   | 转换视频数据格式  * -----------------------------------------------------------------------------  ******************************************************************************/   #include <linux/videodev2.h> #include <stdlib.h>   #include "color.h" #include "debug_manager.h" #include "convert_manager.h"   /*!  * @brief  判断输入输出格式是否为yuv和rgb  * @return  0:不支持 1:支持  */ static int isSupportYuv2Rgb(int pixel_format_in, int pixel_format_out) {     if (pixel_format_in != V4L2_PIX_FMT_YUYV)         return 0;     if ((pixel_format_out != V4L2_PIX_FMT_RGB565) && \         (pixel_format_out != V4L2_PIX_FMT_RGB32))         return 0;       return 1; }   /*!  * @brief  支持的格式转换yuv为输出格式rgb565  *         参考luvcview  * @return  0:成功  */ static unsigned int Pyuv422torgb565(unsigned char * input_ptr, unsigned char * output_ptr,                  unsigned int image_width, unsigned int image_height) { unsigned int i, size; unsigned char Y, Y1, U, V; unsigned char *buff = input_ptr; unsigned char *output_pt = output_ptr;       unsigned int r, g, b;     unsigned int color;      size = image_width * image_height /2; for (i = size; i > 0; i--) { /* bgr instead rgb ?? */ Y = buff[0] ; U = buff[1] ; Y1 = buff[2]; V = buff[3]; buff += 4; r = R_FROMYV(Y,V); g = G_FROMYUV(Y,U,V); //b b = B_FROMYU(Y,U); //v           /*!          * 第一个颜色,把r,g,b三色构造为rgb565的16位值          */         r = r >> 3;         g = g >> 2;         b = b >> 3;         color = (r << 11) | (g << 5) | b;         *output_pt++ = color & 0xff;         *output_pt++ = (color >> 8) & 0xff;   r = R_FROMYV(Y1,V); g = G_FROMYUV(Y1,U,V); //b b = B_FROMYU(Y1,U); //v           /*!          * 第二个颜色,把r,g,b三色构造为rgb565的16位值          */         r = r >> 3;         g = g >> 2;         b = b >> 3;         color = (r << 11) | (g << 5) | b;         *output_pt++ = color & 0xff;         *output_pt++ = (color >> 8) & 0xff; }   return 0; }    /*!  * @brief  支持的格式转换yuv为输出格式rgb32  *         参考luvcview  * @return  0:成功  */ static unsigned int Pyuv422torgb32(unsigned char * input_ptr, unsigned char * output_ptr,                      unsigned int image_width, unsigned int image_height) { unsigned int i, size; unsigned char Y, Y1, U, V; unsigned char *buff = input_ptr; unsigned int *output_pt = (unsigned int *)output_ptr;       unsigned int r, g, b;     unsigned int color;   size = image_width * image_height /2; for (i = size; i > 0; i--) { /* bgr instead rgb ?? */ Y = buff[0] ; U = buff[1] ; Y1 = buff[2]; V = buff[3]; buff += 4;           /*!          * 第一个颜色,把r,g,b三色构造为rgb888的32位值          */         r = R_FROMYV(Y,V); g = G_FROMYUV(Y,U,V); //b b = B_FROMYU(Y,U); //v color = (r << 16) | (g << 8) | b;         *output_pt++ = color;           /*!          * 第二个颜色,把r,g,b三色构造为rgb888的32位值          */ r = R_FROMYV(Y1,V); g = G_FROMYUV(Y1,U,V); //b b = B_FROMYU(Y1,U); //v color = (r << 16) | (g << 8) | b;         *output_pt++ = color; }   return 0; }    /*!  * @brief  支持的格式转换yuv为输出格式rgb  *         参考luvcview  * @return  0:转换成功 -1:分配内存失败  */ static int Yuv2RgbConvert(PT_VIDEOBUF videobuf_in,                                     PT_VIDEOBUF videobuf_out) {     PT_PIXELDATAS pixeldatas_in   = &videobuf_in->pixel_data;     PT_PIXELDATAS pixeldatas_out = &videobuf_out->pixel_data;       pixeldatas_out->width  = pixeldatas_in->width;     pixeldatas_out->height = pixeldatas_in->height;       /*!      * 判断支持哪种输出格式,调用对应的转换函数      */     if (videobuf_out->pixel_format == V4L2_PIX_FMT_RGB565)     {         pixeldatas_out->bpp = 16;         pixeldatas_out->linebytes = pixeldatas_out->width * pixeldatas_out->bpp / 8;         pixeldatas_out->TotalBytes = pixeldatas_out->linebytes * pixeldatas_out->height;           if (!pixeldatas_out->PixelDatas)         {             pixeldatas_out->PixelDatas = (unsigned char *)malloc(pixeldatas_out->TotalBytes);             if (!pixeldatas_out->PixelDatas)             {                 DebugPrint(APP_ERR"pixeldatas_out->PixelDatas malloc err\n");                 return -1;             }         }                      Pyuv422torgb565(pixeldatas_in->PixelDatas, pixeldatas_out->PixelDatas,             pixeldatas_out->width, pixeldatas_out->height);     }     else if (videobuf_out->pixel_format == V4L2_PIX_FMT_RGB32)     {          pixeldatas_out->bpp = 32;         pixeldatas_out->linebytes = pixeldatas_out->width * pixeldatas_out->bpp / 8;         pixeldatas_out->TotalBytes = pixeldatas_out->linebytes * pixeldatas_out->height;           if (!pixeldatas_out->PixelDatas)         {             pixeldatas_out->PixelDatas = (unsigned char *)malloc(pixeldatas_out->TotalBytes);             if (!pixeldatas_out->PixelDatas)             {                 DebugPrint(APP_ERR"pixeldatas_out->PixelDatas malloc err\n");                 return -1;             }         }                    Pyuv422torgb32(pixeldatas_in->PixelDatas, pixeldatas_out->PixelDatas,             pixeldatas_out->width, pixeldatas_out->height);             }          return 0; }   /*!  * @brief  退出函数,释放内存  * @return  0  */ static int Yuv2RgbConvertExit(PT_VIDEOBUF videobuf_out) {     if (videobuf_out->pixel_data.PixelDatas)     {         free(videobuf_out->pixel_data.PixelDatas);         videobuf_out->pixel_data.PixelDatas = NULL;     }         return 0; }   static T_VIDEOCONVERTOPR s_Yuv2RgbConvert = {     .name        = "yuv2rgb",     .isSupport   = isSupportYuv2Rgb,     .Convert     = Yuv2RgbConvert,     .ConvertExit = Yuv2RgbConvertExit, };   extern void initLut(void);   /*!  * @brief  初始化函数,把对支持的各个转换格式进行初始化工作  *         对应的结构体放入链表统一管理  * @return  0:初始化成功 1:失败  */ int Yuv2RgbInit(void) {     initLut();     return RegisterVideoConvertOpr(&s_Yuv2RgbConvert); }   3、rgb2rgb.c 对于此文件,主要针对的是判断视频源数据格式是否为V4L2_PIX_FMT_RGB565,以及指定的转换格式是否为V4L2_PIX_FMT_RGB565 / V4L2_PIX_FMT_RGB32。   在转换格式时,根据情况提前设置好转换格式的bpp、行宽、总的数据大小、像素存储的空间,若源数据格式与转换格式都为V4L2_PIX_FMT_RGB565,则直接进行memcpy;若不一致,则逐行处理像素的颜色信息,实现RGB565->RGB32的转换。   /*******************************************************************************  * Copyleft (c) 2021 Kcode  *  * @file    rgb2rgb.c  * @brief   转换视频数据格式,把摄像头原始的rgb565格式转换成rgb565或32格式  * @author  K  * @version 0.0.1  * @date    2021-07-26  * @license MulanPSL-1.0  *  * 文件修改历史:  * <时间>       | <版本>    | <作者>  | <描述>  * 2021-07-27   | v0.0.1    | Kcode   | 转换视频数据格式  * -----------------------------------------------------------------------------  ******************************************************************************/   #include <linux/videodev2.h> #include <stdlib.h> #include <string.h>   #include "convert_manager.h"   /*!  * @brief  判断输入输出格式是否为rgb和rgb  * @return  0:不支持 1:支持  */ static int isSupportRgb2Rgb(int pixel_format_in, int pixel_format_out) {     if (pixel_format_in != V4L2_PIX_FMT_RGB565)         return 0;          if ((pixel_format_out != V4L2_PIX_FMT_RGB565) && \         (pixel_format_out != V4L2_PIX_FMT_RGB32))         return 0;       return 1; }   /*!  * @brief  支持的格式转换rgb为输出格式rgb565  *         参考luvcview  * @return  0:成功 -1:失败  */ static int Rgb2RgbConvert(PT_VIDEOBUF videobuf_in, PT_VIDEOBUF videobuf_out) {     PT_PIXELDATAS pixeldatas_in   = &videobuf_in->pixel_data;     PT_PIXELDATAS pixeldatas_out = &videobuf_out->pixel_data;       int x, y;     int r, g, b;     int color;     unsigned short *pSrc = (unsigned short *)pixeldatas_in->PixelDatas;     unsigned int *pDest  = (unsigned int *)pixeldatas_in->PixelDatas;       if (videobuf_in->pixel_format != V4L2_PIX_FMT_RGB565)     {         return -1;     }          /*!      * 根据输入格式,调用不同的转换      */     if (videobuf_in->pixel_format == V4L2_PIX_FMT_RGB565)     {         /*!          * 设置          */         pixeldatas_out->width      = pixeldatas_in->width;         pixeldatas_out->height     = pixeldatas_in->height;         pixeldatas_out->bpp        = pixeldatas_in->bpp;         pixeldatas_out->linebytes  = pixeldatas_out->width * pixeldatas_out->bpp / 8;         pixeldatas_out->TotalBytes = pixeldatas_out->linebytes * pixeldatas_out->height;           if (NULL == pixeldatas_out->PixelDatas)         {             pixeldatas_out->PixelDatas = (unsigned char *)malloc(pixeldatas_out->TotalBytes);             if (pixeldatas_out->PixelDatas == NULL)                 return -1;         }           /*!          * 处理数据          */         memcpy(pixeldatas_out->PixelDatas, pixeldatas_in->PixelDatas,                                             pixeldatas_out->TotalBytes);     }     else if (videobuf_in->pixel_format == V4L2_PIX_FMT_RGB32)     {         /*!          * 设置          */         pixeldatas_out->width      = pixeldatas_in->width;         pixeldatas_out->height     = pixeldatas_in->height;         pixeldatas_out->bpp        = 16;         pixeldatas_out->linebytes  = pixeldatas_out->width * pixeldatas_out->bpp / 8;         pixeldatas_out->TotalBytes = pixeldatas_out->linebytes * pixeldatas_out->height;           if (NULL == pixeldatas_out->PixelDatas)         {             pixeldatas_out->PixelDatas = (unsigned char *)malloc(pixeldatas_out->TotalBytes);             if (pixeldatas_out->PixelDatas == NULL)                 return -1;         }           /*!          * 逐行处理数据          */         pDest = (unsigned int *)pixeldatas_out->PixelDatas;         for (y = 0; y < pixeldatas_out->height; y++)         {             for (x = 0; y < pixeldatas_out->width; x++)             {                 color = *pSrc++;                   /* 从RGB565格式的数据中提取出R,G,B */                 r = color >> 11;                 g = (color >> 5) & 0x3F;                 b = color & 0x1F;                   /* 把提取出的R,G,B转为0x00RRGGBB的32位数据 */                 color = ((r << 3) << 16) | ((g << 2) << 8) | (b << 3);                   /* 存入目的地址 */                 *pDest = color;                 pDest++;             }         }     }          return 0; }   /*!  * @brief  退出函数,释放内存  * @return  0  */ static int Rgb2RgbConvertExit(PT_VIDEOBUF videobuf_out) {     if (videobuf_out->pixel_data.PixelDatas == NULL)     {         free(videobuf_out->pixel_data.PixelDatas);         videobuf_out->pixel_data.PixelDatas = NULL;     }       return 0; }    static T_VIDEOCONVERTOPR s_Rgb2RgbConvert = {      .name  = "rgb2rgb",      .isSupport   = isSupportRgb2Rgb,      .Convert     = Rgb2RgbConvert,      .ConvertExit = Rgb2RgbConvertExit,  };   /*!  * @brief  初始化函数,把对支持的各个转换格式进行初始化工作  *         对应的结构体放入链表统一管理  * @return  0:初始化成功 1:失败  */  int Rgb2RgbInit(void)  {      return RegisterVideoConvertOpr(&s_Rgb2RgbConvert);  }     4、mjpeg2rgb.c 此文件是对于视频源数据格式为V4L2_PIX_FMT_MJPEG,转换格式为V4L2_PIX_FMT_RGB565 / V4L2_PIX_FMT_RGB32的需求。   对于mjpeg的视频格式,其每一帧的图像的编码都为jpeg,所以可以使用libjpeg-turbo库来对视频数据进行提取,逐帧逐行提取图像信息,根据情况提前设置好转换格式的bpp、行宽、总的数据大小、像素存储的空间,调用之前数码相框项目编写的CovertOneLine(),处理每一行的像素信息。   /*******************************************************************************  * Copyleft (c) 2021 Kcode  *  * @file    mjpeg2rgb.c  * @brief   转换视频数据格式,把摄像头原始的mjpeg格式转换成rgb565格式  * @author  K  * @version 0.0.1  * @date    2021-07-26  * @license MulanPSL-1.0  *  * 文件修改历史:  * <时间>       | <版本>    | <作者>  | <描述>  * 2021-07-27   | v0.0.1    | Kcode   | 转换视频数据格式  * -----------------------------------------------------------------------------  ******************************************************************************/   #include <linux/videodev2.h> #include <convert_manager.h> #include <stdlib.h> #include <string.h> #include <setjmp.h> #include <jpeglib.h>   #include "debug_manager.h"   typedef struct MyErrorMgr { struct jpeg_error_mgr pub; jmp_buf setjmp_buffer; }T_MyErrorMgr, *PT_MyErrorMgr;   extern void jpeg_mem_src_tj(j_decompress_ptr cinfo,       unsigned char * inbuffer, unsigned long insize);   /*!  * @brief  判断输入输出格式是否为mhpeg和rgb  * @return  0:不支持 1:支持  */ static int isSupportMjpeg2Rgb(int pixel_format_in, int pixel_format_out) {     if (pixel_format_in != V4L2_PIX_FMT_MJPEG)         return 0;     if ((pixel_format_out != V4L2_PIX_FMT_RGB565) && \     (pixel_format_out != V4L2_PIX_FMT_RGB32))         return 0;       return 1; }   /*!  * @brief  判断输入输出格式是否为mhpeg和rgb  * @return  0:不支持 1:支持  */ static void MyErrorExit(j_common_ptr ptCInfo) {     static char errStr[JMSG_LENGTH_MAX];      PT_MyErrorMgr ptMyErr = (PT_MyErrorMgr)ptCInfo->err;       /* Create the message */     (*ptCInfo->err->format_message) (ptCInfo, errStr);     DBG_PRINTF("%s\n", errStr);   longjmp(ptMyErr->setjmp_buffer, 1); }   /*!  * @brief  判断输入输出格式是否为mhpeg和rgb  * @return  0:不支持 1:支持  */ static int CovertOneLine(int width, int SrcBpp, int DstBpp,              unsigned char *pSrcDatas, unsigned char *pDstDatas) { unsigned int red; unsigned int green; unsigned int blue; unsigned int color;   unsigned short *pDstDatas16bpp = (unsigned short *)pDstDatas; unsigned int   *pDstDatas32bpp = (unsigned int *)pDstDatas;   int i; int pos = 0;   if (SrcBpp != 24) { return -1; }   if (DstBpp == 24) { memcpy(pDstDatas, pSrcDatas, width*3); } else { for (i = 0; i < width; i++) { red   = pSrcDatas[pos++]; green = pSrcDatas[pos++]; blue  = pSrcDatas[pos++]; if (DstBpp == 32) { color = (red << 16) | (green << 8) | blue; *pDstDatas32bpp = color; pDstDatas32bpp++; } else if (DstBpp == 16) { /* 565 */ red   = red >> 3; green = green >> 2; blue  = blue >> 3; color = (red << 11) | (green << 5) | (blue); *pDstDatas16bpp = color; pDstDatas16bpp++; } } } return 0; }   /*!  * @brief  支持的格式转换mjpeg为输出格式rgb  *         mjpeg实际是上多个jpeg  * @return  0:转换成功 -1:分配内存失败  */ static int      Mjpeg2RgbConvert(PT_VIDEOBUF videobuf_in, PT_VIDEOBUF videobuf_out)   {     struct jpeg_decompress_struct tDInfo;     //struct jpeg_error_mgr tJErr;     int ret;     int RowStride;     unsigned char *LineBuffer = NULL;     unsigned char *pDest;     T_MyErrorMgr tJerr;     PT_PIXELDATAS pixeldatas_out = &videobuf_out->pixel_data;       // 分配和初始化一个decompression结构体     //tDInfo.err = jpeg_std_error(&tJErr);       tDInfo.err               = jpeg_std_error(&tJerr.pub);     tJerr.pub.error_exit     = MyErrorExit;       if(setjmp(tJerr.setjmp_buffer))     {         /* 如果程序能运行到这里, 表示JPEG解码出错 */         jpeg_destroy_decompress(&tDInfo);         if (LineBuffer)         {             free(LineBuffer);         }         if (pixeldatas_out->PixelDatas)         {             free(pixeldatas_out->PixelDatas);         }         return -1;     }       jpeg_create_decompress(&tDInfo);       // 把数据源设为内存中的数据     jpeg_mem_src_tj(&tDInfo, videobuf_in->pixel_data.PixelDatas,                                  videobuf_in->pixel_data.TotalBytes);       ret = jpeg_read_header(&tDInfo, TRUE);       // 设置解压参数,比如放大、缩小     tDInfo.scale_num = tDInfo.scale_denom = 1;          // 启动解压:jpeg_start_decompress      jpeg_start_decompress(&tDInfo);          // 一行的数据长度     RowStride = tDInfo.output_width * tDInfo.output_components;     LineBuffer = malloc(RowStride);       if (NULL == LineBuffer)     {         return -1;     }       pixeldatas_out->width  = tDInfo.output_width;     pixeldatas_out->height = tDInfo.output_height;     //pixeldatas_out->bpp    = bpp;     pixeldatas_out->linebytes    = pixeldatas_out->width * pixeldatas_out->bpp / 8;     pixeldatas_out->TotalBytes   = pixeldatas_out->height * pixeldatas_out->linebytes;          if (NULL == pixeldatas_out->PixelDatas)     {         pixeldatas_out->PixelDatas = (unsigned char *)malloc(pixeldatas_out->TotalBytes);         if (pixeldatas_out->PixelDatas == NULL)             return -1;     }       pDest = pixeldatas_out->PixelDatas;       // 循环调用jpeg_read_scanlines来一行一行地获得解压的数据     while (tDInfo.output_scanline < tDInfo.output_height)      {         /* 得到一行数据,里面的颜色格式为0xRR, 0xGG, 0xBB */         (void) jpeg_read_scanlines(&tDInfo, &LineBuffer, 1);           // 转到ptPixelDatas去         CovertOneLine(pixeldatas_out->width, 24, pixeldatas_out->bpp, LineBuffer, pDest);         pDest += pixeldatas_out->linebytes;     }          free(LineBuffer);     jpeg_finish_decompress(&tDInfo);     jpeg_destroy_decompress(&tDInfo);       return 0; }     /*!  * @brief  退出函数,释放内存  * @return  0  */ static int Mjpeg2RgbConvertExit(PT_VIDEOBUF videobuf_out) {     if (videobuf_out->pixel_data.PixelDatas == NULL)     {         free(videobuf_out->pixel_data.PixelDatas);         videobuf_out->pixel_data.PixelDatas = NULL;     }       return 0; }   static T_VIDEOCONVERTOPR s_Mjpeg2RgbConvert = {     .name = "mjpeg2rgb",     .isSupport   = isSupportMjpeg2Rgb,     .Convert     = Mjpeg2RgbConvert,     .ConvertExit = Mjpeg2RgbConvertExit, };   /*!  * @brief  初始化函数,把对支持的各个转换格式进行初始化工作  *         对应的结构体放入链表统一管理  * @return  0:初始化成功 1:失败  */ int Mjpeg2RgbInit(void) {     return RegisterVideoConvertOpr(&s_Mjpeg2RgbConvert); }     五、main.c 对于显示部分和渲染部分,采用的代码为之前数码相框的代码,所以就不过多介绍了。   对于整个程序的工作流程如下:   初始化调试系统并设置好打印通道 注册显示模块,把支持的具体显示设备放入链表进行统一管理 指定使用的显示设备,并获取此设备的分辨率、bpp、显存、格式 注册摄像头模块,把支持的具体摄像头设备放入链表进行统一管理 初始化摄像头模块,对于支持的摄像头设备进行初始化: 5.1. 打开驱动open 5.2. 获取该设备的类型 VIDIOC_QUERYCAP 5.3. 列举摄像头的数据格式 VIDIOC_ENUM_FMT 5.4. 获得摄像头的数据格式 VIDIOC_G_FMT 5.5. 测试是否支持该摄像头的数据格式 VIDIOC_TRY_FMT 5.6. 设置参数VIDIOC_S_FMT 5.7. 请求系统分配缓冲区VIDIOC_REQBUFS 5.8. 查询所分配的缓冲区的信息 VIDIOC_QUERYBUF 5.9. 映射缓冲区mmap 5.10. 把缓冲区放入队列 VIDIOC_QBUF 以上是对V4L2_CAP_STREAMING接口的设置,对于V4L2_CAP_READWRITE接口: 5.1. 打开驱动open 5.2. 获取该设备的类型 VIDIOC_QUERYCAP 5.3. 列举摄像头的数据格式 VIDIOC_ENUM_FMT 5.4. 获得摄像头的数据格式 VIDIOC_G_FMT 5.5. 测试是否支持该摄像头的数据格式 VIDIOC_TRY_FMT 5.6. 设置参数VIDIOC_S_FMT 5.7. 请求系统分配缓冲区VIDIOC_REQBUFS 5.8. 分配一个video_buffer 获取摄像头的数据格式 注册转换模块,把支持的具体转换方式放入链表进行统一管理 根据6中获得的摄像头的数据格式与3中获得显示设备格式,获取支持格式的转换处理结构体 启动摄像头设备 处理摄像头数据: 10.1. 读出摄像头数据 ,存放在一个video_buf中(有数据则唤醒,从队列中把buf取出,无数据进程继续休眠) 10.2. 根据6中获得的摄像头的数据格式与3中获得显示设备格式,进行转换操作,得到的转换数据存放在convert_buf中 10.3. 比较得到的转换数据的分辨率与显示设备的分辨率,进行缩放设置,得到的缩放后数据存放在zoom_buf中 10.4 对数据进行合并处理 10.5 最后把数据刷新到LCD屏幕 重新把数据放入到队列中,继续进行poll等待数据 最后进行一些清理工作   /*******************************************************************************  * Copyleft (c) 2021 Kcode  *  * @file    main.c  * @brief   主程序,配合多个模块,实现在LCD显示屏上显示USB摄像头图像  * @author  K  * @version 0.0.1  * @date    2021-07-26  * @license MulanPSL-1.0  *  * 文件修改历史:  * <时间>       | <版本>    | <作者>  | <描述>  * 2021-07-28   | v0.0.1    | Kcode   | 主程序  * -----------------------------------------------------------------------------  ******************************************************************************/   #include <unistd.h> #include <stdlib.h> #include <stdio.h> #include <string.h> #include <sys/types.h> #include <sys/stat.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <sys/mman.h>   #include "config.h" #include "disp_manager.h" #include "debug_manager.h" #include "pic_operation.h" #include "render.h" #include "convert_manager.h" #include "video_manager.h"   int main(int argc, char **argv) { int error;     int video_pixel_format;     int display_pixel_format;     int lcd_width;     int lcd_height;     int lcd_bpp;     int topleft_x;     int topleft_y;     float k;     T_VIDEODEVICE video_dev;     T_VIDEOBUF video_buf;        T_VIDEOBUF convert_buf;     T_VIDEOBUF zoom_buf;     T_VIDEOBUF framebuf;     PT_VIDEOBUF cur_video_buf;     PT_VIDEOCONVERTOPR video_convert_opr;        /*!      * 初始化调试系统      */ error = DebugInit(); if (error) { printf(APP_ERR"DebugInit error! File:%s Line:%d\n", __FILE__, __LINE__); return -1; }   error = InitDebugChanel(); if (error) { printf(APP_ERR"InitDebugChanel error! File:%s Line:%d\n", __FILE__, __LINE__); return -1; }       /*!      * 操作信息提示:video2lcd </dev/video0>      */     if (argc != 2)     {         DebugPrint(APP_NOTICE"Usage:\n");         DebugPrint(APP_NOTICE"%s </dev/video0,1.....>\n", argv[0]);         return -1;     }         /*!      * 注册显示模块      */     error = DisplayInit();     if (error)     {         DebugPrint(APP_ERR"DisplayInit err\n");         return -1;     }          /* 选择显示设备 */     SelectAndInitDefaultDispDev("fb");       /* 获取显示屏参数 */     GetDispResolution(&lcd_width, &lcd_height, &lcd_bpp);       /* 获取显示屏显存 */     GetVideoBufForDisplay(&framebuf);       /* 设置显示器格式 */     display_pixel_format = framebuf.pixel_format;          /*!      * 注册摄像头模块       */     error = VideoInit();     if (error)     {         DebugPrint(APP_ERR"VideoInit err\n");         return -1;     }              /*!      * 初始化指定摄像头视频设备      */     error = VideoDeviceInit(argv[1], &video_dev);     if (error < 0)     {         DebugPrint(APP_ERR"VideoDeviceInit for %s err\n", argv[1]);         return -1;     }          /* 获取摄像头数据格式 */     video_pixel_format = video_dev.video_opr->GetFormat(&video_dev);       /*!      * 注册转换模块      */     error = VideoConvertInit();     if (error)     {         DebugPrint(APP_ERR"VideoConvertInit err\n");         return -1;     }          /* 获取支持格式的转换处理结构体 */     video_convert_opr = GetVIdeoConvertForFormats(video_pixel_format,                                             display_pixel_format);     if (video_convert_opr == NULL)     {         DebugPrint(APP_ERR"Can not support this format convert\n");         return -1;     }          /*!      * 启动摄像头设备       */     error = video_dev.video_opr->StartDevice(&video_dev);     if (error)     {         DebugPrint(APP_ERR"StartDevice for %s err\n", argv[1]);         return -1;     }       memset(&zoom_buf, 0, sizeof(T_VIDEOBUF));     memset(&video_buf, 0, sizeof(T_VIDEOBUF));     memset(&convert_buf, 0, sizeof(T_VIDEOBUF));     convert_buf.pixel_data.bpp = lcd_bpp;     convert_buf.pixel_format = display_pixel_format;       /*!      * 处理摄像头数据      */     while(1)     {         /*!          * 读出摄像头数据           */         error = video_dev.video_opr->GetFrame(&video_dev, &video_buf);         if (error)         {             DebugPrint(APP_ERR"GetFrame for %s err\n", argv[1]);             return -1;         }           cur_video_buf = &video_buf;              /*!          * 转换为RGB           */         if (video_pixel_format != display_pixel_format)         {             error = video_convert_opr->Convert(&video_buf, &convert_buf);             if (error)             {                 DebugPrint(APP_ERR"Convert for %s err\n", argv[1]);                 return -1;             }               cur_video_buf = &convert_buf;         }                  /*!          * 若图像分辨率大于LCD,缩放           */         if (cur_video_buf->pixel_data.height > lcd_height || \                     cur_video_buf->pixel_data.width > lcd_width)         {             /* 设置缩放参数 */             k = (float)cur_video_buf->pixel_data.height / (float)cur_video_buf->pixel_data.width;             zoom_buf.pixel_data.width  = lcd_width;             zoom_buf.pixel_data.height = lcd_width * k;             if (zoom_buf.pixel_data.height > lcd_height)             {                 zoom_buf.pixel_data.width  = lcd_height * k;                 zoom_buf.pixel_data.height = lcd_height;              }                          zoom_buf.pixel_data.bpp        = lcd_bpp;             zoom_buf.pixel_data.linebytes  = zoom_buf.pixel_data.width * zoom_buf.pixel_data.bpp / 8;             zoom_buf.pixel_data.TotalBytes = zoom_buf.pixel_data.linebytes * zoom_buf.pixel_data.height;               if (zoom_buf.pixel_data.PixelDatas == NULL)             {                 zoom_buf.pixel_data.PixelDatas = (unsigned char *)malloc(zoom_buf.pixel_data.TotalBytes);             }               /* 进行缩放 */             PicZoom(&cur_video_buf->pixel_data, &zoom_buf.pixel_data);               cur_video_buf = &zoom_buf;         }           /*!          * 合并到framebuffer           */         /* 居中显示,计算此时的左上角坐标 */         topleft_x = (lcd_width - cur_video_buf->pixel_data.width) / 2;         topleft_y = (lcd_height - cur_video_buf->pixel_data.height) / 2;                  PicMerge(topleft_x, topleft_y, &cur_video_buf->pixel_data, &framebuf.pixel_data);              /*!          * 把framebuffer的数据刷到LCD,显示           */         FlushPixelDatasToDev(&framebuf.pixel_data);           /*!          * 把数据放回队列           */         error = video_dev.video_opr->PutFrame(&video_dev, &video_buf);         if (error)         {             DebugPrint(APP_ERR"PutFrame for %s err\n", argv[1]);             return -1;         }             }       /*!      * 进行清理工作      */     VideoConvertExit(video_convert_opr->name, &convert_buf);     VideoExit("v4l2", &video_buf); }  

标签:视频,return,int,pixeldatas,LCD,video,pixel,摄像头,out
From: https://www.cnblogs.com/kn-zheng/p/17615946.html

相关文章

  • Android平台GB28181设备接入端如何实现多视频通道接入?
    技术背景我们在设计Android平台GB28181设备接入模块的时候,有这样的场景诉求,一个设备可能需要多个通道,常见的场景,比如车载终端,一台设备,可能需要接入多个摄像头,那么这台车载终端设备可以作为主设备,然后,主设备下,配置多个通道,听起来是不是有点儿类似于DVR或NVR?技术实现这里,我们说下,我们......
  • Hugging News #0807: ChatUI 官方 Docker 模板发布、 Hub 和开源生态介绍视频来啦!
    每一周,我们的同事都会向社区的成员们发布一些关于HuggingFace相关的更新,包括我们的产品和平台更新、社区活动、学习资源和内容更新、开源库和模型更新等,我们将其称之为「HuggingNews」。本期HuggingNews有哪些有趣的消息,快来看看吧!......
  • RTMP流媒体服务器LntonMedia(免费版)视频流媒体平台成功清理录像计划的具体操作步骤
    LntonMedia是支持接入RTMP推流摄像头的视频流媒体平台,新版LntonMedia互联网直播点播平台支持创建录像计划,用户可以设定周一至周日中,某天某个时间段内开启录像,其他时间不录像。LntonMedia包含一个根据录像计划清理录像的功能,是我们在添加录像计划后同步添加的功能,功能实现代码大家可......
  • 安防监控视频汇聚平台EasyCVR分发的FLV视频流在VLC中无法播放是什么原因?
    众所周知,TSINGSEE青犀视频汇聚平台EasyCVR可支持多协议方式接入,包括主流标准协议国标GB28181、RTSP/Onvif、RTMP等,以及厂家私有协议与SDK接入,包括海康Ehome、海大宇等设备的SDK等。在视频流的处理与分发上,视频监控汇聚平台EasyCVR的性能也同样表现得很优秀,平台可对外分发多格式的视......
  • 如何通过navicat连接数据库修改EasyCVR视频监控管理平台的登录密码?
    TSINGSEE青犀视频监控管理平台EasyCVR可以根据不同的应用场景需求,让平台在内网、专网、VPN、广域网、互联网等各种环境下进行音视频的采集、接入与多端分发。在视频能力上,平台可实现视频实时直播、云端录像、云存储、回放与检索、告警上报、视频快照、视频转码与分发、平台级联等。......
  • 4412 使用usb摄像头拍照YUYV格式
    一、内核设置Linux内核中已经带有很完善的USB摄像头驱动,支持几乎所有的USB摄像头,我们只需要配置内核,选择上相应的Sensor型号即可。配置内核,支持USB摄像头:DeviceDrivers---><*>Multimediasupport---><*>VideoForLinux[*]Ena......
  • USB摄像头——v4l2打开设备、获取设备支持的格式
    一、v4l2介绍  v4l2意思为VideoforLinux2,是linux中视频设备的内核驱动。它有以下几个接口: 视频采集接口:这种设备可以是高频头或摄像头;视频输出接口:可以驱动计算机的外围视频图像设备直接传输视频接口:主要工作是把从视频采集设备采集过来的信号直接输出到输出设备之上,......
  • RTSP/Onvif视频服务器LntonNVR(源码版)视频平台获取录像接口返回精确的录像文件的具体操
    LntonNVR视频平台可实现RTSP/Onvif协议接入设备、实时直播、录像、检索与回放、存储、视频分发等视频能力。在录像功能上,LntonNVR支持7*24小时不间断录像,也可以设定录像计划,对指定时间进行录像,并且支持录像的检索与回放、支持本地录像回放。有用户需要获取LntonNVR平台录像视频指定......
  • RTSP流媒体服务器LntonNVR(源码版)视频平台通过级联到上级云服务器但视频无法播放的问题
    在经过多次的测试后,官方发布的版本可以正常级联。在实际使用过程中,有用户反馈LntonNVR通过国标GB28181协议级联到上级云服务器平台后,出现了上级平台无法播放的问题,需要我们技术人员协助进行排查。从上图我们可以看出,用户的云服务器平台显示是正常的,但是实际点击播放却存在一些问题......
  • 视频汇聚平台视频融合平台LiteCVR如何连接级联教程
    LiteCVR视频融合平台是一种基于云边端一体化技术的平台,可以帮助用户实现海量视频资源的轻松接入、汇聚和管理。该平台提供了丰富的视频能力服务,包括视频监控直播、云端录像、云存储、录像检索与回看、智能告警、平台级联以及智能分析等功能。其中,平台级联是利用国标GB28181协议实现......