• 项目之利用 V4L2应用程序框架 进行视频录制


    目录

    知识储备:

    视频采集方式:

    处理采集数据:

    相关结构体:

    对于设备的操作步骤:


            V4L2较V4L有较大的改动,并已成为2.6的标准接口,函盖video\dvb\FM...,多数驱动都在向V4l2迁移。更好地了解V4L2先从应用入手,然后再深入到内核中结合物理设备/接口的规范实现相应的驱动。本文先就V4L2在视频捕捉或camera方面的应用框架。
            V4L2采用流水线的方式,操作更简单直观,基本遵循打开视频设备、设置格式、处理数据、关闭设备,更多的具体操作通过ioctl函数来实现。

    知识储备:

     V4L2视频编程本质:IO操作

    摄像头相关头文件:/usr/include/linux/videodev2.h

    设备文件不能被用户创建,用户也不能直接访问内核的代码和数据

    =====================================================================

    V4L2使用V4L2_MEMORY_USERPTR和V4L2_MEMORY_MMAP的区别:
    视频应用可以通过两种方式从V4L2驱动申请buffer
    1. USERPTR, 顾名思义是用户空间指针的意思,应用层负责分配需要的内存空间,然后以指针的形式传递给V4L2驱动层,V4L2驱动会把capture的内容保存到指针所指的空间(层级切换,会慢不少)
    一般来说,应用层需要确保这个内存空间物理上是连续的(IPU处理单元的需求),在android系统可以通过PMEM驱动来分配大块的连续物理内存。应用层在不需要的时候要负责释放申请的PMEM内存。
    2. MMAP方式,内存映射模式,应用调用VIDIOC_REQBUFS ioctl分配设备buffers,参数标识需要的数目和类型。这个ioctl也可以用来改变buffers的数据以及释放分配的内存,当然这个内存空间一般也是连续的。在应用空间能够访问这些物理地址之前,必须调用mmap函数把这些物理空间映射为用户虚拟地址空间。
    虚拟地址空间是通过munmap函数释放的; 而物理内存的释放是通过VIDIOC_REQBUFS来实现的(设置参数buf count为(0)),物理内存的释放是实现特定的,mx51 v4l2是在关闭设备时进行释放的。
    所以二者都是申请连续的物理内存,只是申请和释放的方式不同

    V4L2使用V4L2_MEMORY_USERPTR和V4L2_MEMORY_MMAP的区别 - Lxk- - 博客园

    ====================================================================

    视频采集方式:

     操作系统一般把系统使用的内存划分成用户空间和内核空间,分别由应用程序管理和操作系统管理。应用程序可以直接访问内存的地址,而内核空间存放的是供内核访问的代码和数据,用户不能直接访问。v4l2捕获的数据,最初是存放在内核空间的,这意味着用户不能直接访问该段内存,必须通过某些手段来转换地址。

     一共有三种视频采集方式:使用readwrite方式;内存映射方式和用户指针模式。

    read、write方式,在用户空间和内核空间不断拷贝数据,占用了大量用户内存空间,效率不高。

    内存映射方式:把设备里的内存映射到应用程序中的内存控件,直接处理设备内存,这是一种有效的方式。上面的mmap函数就是使用这种方式。

    用户指针模式:内存片段由应用程序自己分配。这点需要在v4l2_requestbuffers里将memory字段设置成V4L2_MEMORY_USERPTR。  

    ====================================================================

    处理采集数据:

    V4L2有一个数据缓存,存放req.count数量的缓存数据。数据缓存采用FIFO的方式,当应用程序调用缓存数据时,缓存队列将最先采集到的视频数据缓存送出,并重新采集一张视频数据。这个过程需要用到两个ioctl命令,VIDIOC_DQBUFVIDIOC_QBUF.

    ====================================================================

    相关结构体:

    struct v4l2_fmtdesc结构体:用于获取摄像头支持的视频格式

    • struct v4l2_fmtdesc {
    •      __u32           index;             /* Format number编号*/
    •      __u32           type;              /* enum v4l2_buf_type */
    •      __u32               flags;
    •      __u8            description[32];   /* Description string */
    •      __u32           pixelformat;       /* Format fourcc      */
    •      __u32           reserved[4];

     };

    struct v4l2_format结构体:用于设置当前摄像头的采集格式和大小
    struct v4l2_format{
        enum v4l2_buf_type type; // 数据流类型,固定V4L2_BUF_TYPE_VIDEO_CAPTURE
        union
        {
            struct v4l2_pix_format    pix; 
            struct v4l2_window        win; 
            struct v4l2_vbi_format    vbi; 
            __u8    raw_data[200];         
        } fmt;
    };

    struct v4l2_pix_format结构体:是struct v4l2_format结构体的子结构体

    struct v4l2_pix_format{
       
    __u32                   width;        // 宽,必须是16的倍数
        __u32                   height;       // 高,必须是16的倍数
        __u32                   pixelformat// 视频数据存储类型,例如是YUV4:2:2还是RGB
        enum v4l2_field         field;        /* enum v4l2_field */
        __u32                   bytesperline//对于填充,如果未使用,则为0
        __u32                   sizeimage;
       
    enum v4l2_colorspace    colorspace; /* enum v4l2_colorspace */
        __u32                   priv;   //私有数据,依赖于pixelformat   
    };

    struct v4l2_requestbuffers结构体:用于在内核分配空间

    struct v4l2_requestbuffers{

            __u32               count// 缓存数量,也就是说在缓存队列里保持多少张照片    

            enum v4l2_buf_type  type// 数据流类型,固定V4L2_BUF_TYPE_VIDEO_CAPTURE 

            enum v4l2_memory  memory; // V4L2_MEMORY_MMAP(内存映射方式)或 V4L2_MEMORY_USERPTR(用户空间指针方式)  

            __u32               reserved[2];

    };

    =====================================================================

    int ioctl(int fd,unsigned long int request,...);

    常用的命令标志符:

    • VIDIOC_REQBUFS:分配内存
    • VIDIOC_QUERYBUF:把VIDIOC_REQBUFS中分配的数据缓存转换成物理地址
    • VIDIOC_QUERYCAP:查询驱动功能
    • VIDIOC_ENUM_FMT:获取当前驱动支持的视频格式
    • VIDIOC_S_FMT:设置当前驱动的频捕获格式
    • VIDIOC_G_FMT:读取当前驱动的频捕获格式
    • VIDIOC_TRY_FMT:验证当前驱动的显示格式
    • VIDIOC_CROPCAP:查询驱动的修剪能力
    • VIDIOC_S_CROP:设置视频信号的边框
    • VIDIOC_G_CROP:读取视频信号的边框
    • VIDIOC_QBUF:把数据从缓存中读取出来
    • VIDIOC_DQBUF:把数据放回缓存队列
    • VIDIOC_STREAMON:开始视频显示函数
    • VIDIOC_STREAMOFF:结束视频显示函数
    • VIDIOC_QUERYSTD:检查当前视频设备支持的标准,例如PAL或NTSC。

    ====================================================================

    对于设备的操作步骤:


    打开设备(阻塞或者非阻塞,驱动会将缓存(DQBUFF)里的东西返回给应用程序)

    获取设备支持的视频格式(亚洲一般使用PAL(720*576)制式摄像头、欧洲NTSC(720*480)制式摄像头,使用VIDIOC_QUERYSTD来检测)

    根据获得的视频格式,设置当前摄像头的采集格式和大小(liunx编程中ioctl函数)

    设置视频捕获格式(设置fmt.fmt.pix.pixelformat为YUYV还是V4L2_PIX_FMT_MJPEG)

    在内核分配内存空间(设置缓存数量(count)和存储模式(内存映射、用户空间指针))

    获取并记录缓存的物理空间(使用VIDIOC_REQBUFS,获取count个缓存数量,其次调用VIDIOC_QUERYBUF获取这些缓存的地址,然后使用mmap函数转换成应用程序中的绝对地址,最后把这段缓存放入缓存队列(这是用到内存映射、优势是不用频繁转换到用户或者内核层,更快速))

    视频采集(选择readwrite方式,内存映射方式,用户指针模式  这3种模式之一)

    ⑧处理采集数据(V4L2的数据缓存采用FIFO方式,将采集到的视频数据缓存送出后再重新采集一张视频数据,需要用到VIDIOC_DQBUFVIDIOC_QBUF)

    关闭视频设备(close、fclose或者mmap(使用mmap后还需使用munmap方法))

    1. #include <stdio.h>
    2. #include <linux/videodev2.h> //摄像头相关的头文件
    3. #include <sys/ioctl.h>
    4. #include <unistd.h>
    5. #include <sys/types.h>
    6. #include <string.h>
    7. #include <fcntl.h>
    8. #include <sys/mman.h>
    9. #include <stdlib.h>
    10. //定义用户缓冲区,定义一个结构体、用来存储 内核空间映射之后的首地址和空间大小
    11. typedef struct VideoBuffer{
    12. void *start;
    13. size_t length;
    14. }VideoBuffer;
    15. //定义结构体数组,用来代表一个数组元素映射的缓冲区
    16. struct VideoBuffer buffers[8];
    17. //calloc:在堆区开辟8个sizeof(*buffers)这么大的连续空间
    18. //VideoBuffer *buffers = calloc(fmt3.count,sizeof(*buffers));
    19. //定义一个全局缓冲区
    20. struct v4l2_buffer buf;
    21. int camera_init(const char *dev,int *ismjpeg)
    22. {
    23. //1、以阻塞模式打开摄像头
    24. int fd = open(dev,O_RDWR,0);
    25. if(fd < 0){
    26. perror("open_error");
    27. return -1;
    28. }
    29. //2、获取摄像头支持的视频格式
    30. char fmtBuf[32] = {0};
    31. struct v4l2_fmtdesc fmt1;
    32. memset(&fmt1,0,sizeof(fmt1));
    33. fmt1.index = 0;
    34. //V4L2_BUF_TYPE_VBI_CAPTURE:数据流类型,固定一直都是
    35. fmt1.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    36. //VIDIOC_ENUM_FMT:获取当前驱动的频捕获格式
    37. while( ioctl(fd,VIDIOC_ENUM_FMT,&fmt1) != -1){
    38. printf("fmt1.index:%d\n",fmt1.index++);
    39. printf("format:%s\n",fmt1.description);
    40. strcat(fmtBuf,fmt1.description);
    41. }
    42. //3、根据获得的视频格式,设置当前摄像头的采集格式和大小
    43. struct v4l2_format fmt2;
    44. fmt2.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    45. fmt2.fmt.pix.width = 640;
    46. fmt2.fmt.pix.height = 480;
    47. //设置视频捕获格式为JPG
    48. if(strstr(fmtBuf,"JPEG")!=NULL){
    49. fmt2.fmt.pix.pixelformat = V4L2_PIX_FMT_MJPEG;
    50. *ismjpeg = 1;
    51. }
    52. else{
    53. //如果是YUYV的视频格式,后续还需要转码, YUYV->RGB24->JPEG
    54. fmt2.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
    55. *ismjpeg = 0;
    56. }
    57. if( ioctl(fd,VIDIOC_S_FMT,&fmt2)==-1 ){
    58. perror("set fmt");
    59. return -1;
    60. }
    61. //4、在内核分配内存空间
    62. struct v4l2_requestbuffers fmt3;
    63. fmt3.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    64. //设置缓存队列里保持8张照片
    65. fmt3.count = 8;
    66. fmt3.memory = V4L2_MEMORY_MMAP;
    67. //VIDIOC_REQBUFS:分配内存
    68. if(ioctl(fd,VIDIOC_REQBUFS,&fmt3)==-1){
    69. perror("req fmt");
    70. return -1;
    71. }
    72. //5、获取并记录缓存的物理空间
    73. // struct v4l2_buffer buf;
    74. for(int i=0;i<fmt3.count;++i){
    75. buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    76. buf.memory = V4L2_MEMORY_MMAP;
    77. buf.index = i;
    78. //读取内核中的某个index编号的缓冲区
    79. if( ioctl(fd,VIDIOC_QUERYBUF,&buf)==-1 ){
    80. perror("VIDIOC_QUERYBUF");
    81. return -1;
    82. }
    83. //将内核中读取的缓存映射到 用户空间
    84. buffers[i].length = buf.length; //先保存编号为i的内核空间的大小到用户空间
    85. //内存映射
    86. buffers[i].start = mmap(NULL, buf.length, PROT_READ | PROT_WRITE,MAP_SHARED,fd, buf.m.offset);
    87. if(buffers[i].start == MAP_FAILED){ //判断是否映射失败
    88. perror("mmap");
    89. return -1;
    90. }
    91. //将读取出来的缓存重新放入缓存队列
    92. if (ioctl(fd, VIDIOC_QBUF, &buf) == -1) {
    93. perror("mmap qbuf");
    94. return -1;
    95. }
    96. }
    97. return fd;
    98. }
    99. int camera_start(int fd)
    100. {
    101. enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    102. //开始视频采集,必须指定摄像头拍摄的图片存储的缓存区的数据类型
    103. if(ioctl(fd,VIDIOC_STREAMON,&type)==-1 ){
    104. perror("start no");
    105. return -1;
    106. }
    107. return 0;
    108. }
    109. int camera_eqbuf(int fd,void **jpeg,int *size,int *index)
    110. {
    111. // struct v4l2_buffer buf;//局部放不回去,设置为全局的
    112. memset(&buf,0,sizeof(buf));
    113. buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    114. buf.memory = V4L2_MEMORY_MMAP;
    115. buf.index = 0; //指定要出队的 内核缓存编号
    116. //将编号为 0 的内核缓存出队,缓存数据映射到了 用户缓存区buffers
    117. //如果内核缓存没有图片数据,那么出队将会永久阻塞,为了防止永久阻塞,建议使用IO多路复用来出队
    118. if (ioctl(fd, VIDIOC_DQBUF, &buf) == -1){
    119. perror("DQBUF");
    120. return -1;
    121. }
    122. //由于缓存中的数据都已经映射到了 用户空间,因此可以将 用户缓存中的数据 读取出来
    123. //*size = buf[0].length;
    124. *size = buf.bytesused; //保存一张图片的实际大小
    125. *jpeg = buffers[0].start; //保存一张图片数据的首地址
    126. *index = buf.index; //保存当前图片缓存的索引
    127. return 0;
    128. }
    129. int camera_ebuf(int fd,int index)
    130. {
    131. buf.index = index;
    132. if (ioctl(fd, VIDIOC_QBUF, &buf) == -1) {
    133. perror("qbuf");
    134. return -1;
    135. }
    136. return 0;
    137. }
    138. int camera_stop(int fd)
    139. {
    140. enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    141. //开始视频采集,必须指定摄像头拍摄的图片存储的缓存区的数据类型
    142. if(ioctl(fd,VIDIOC_STREAMON,&type)==-1 ){
    143. perror("start no");
    144. return -1;
    145. }
    146. return 0;
    147. }
    148. int camera_quit(int fd)
    149. {
    150. close(fd);
    151. return 0;
    152. }
    153. int main(int argc, char *argv[])
    154. {
    155. int ismjpeg = 0;
    156. int index = -1,size;
    157. char *yuv,*jpeg;//野指针,取一个地址,让野指针去存储图片首地址,会出问题
    158. int cameraFd = camera_init("/dev/video0",&ismjpeg);
    159. if(cameraFd<0){
    160. return -1;
    161. }
    162. if( camera_start(cameraFd)==-1 ){
    163. return -1;
    164. }
    165. if(ismjpeg == 1){
    166. printf("capture format:MJPEG\n");
    167. jpeg = (char *)malloc(640*480);
    168. }else{
    169. printf("capture format:YUYV\n");
    170. }
    171. for(int i=0;i<8;i++){
    172. //出队,得到一张图片的 首地址和大小,以及该图片的 索引编号
    173. if(camera_eqbuf(cameraFd,(void **)&yuv,&size,&index)==-1){
    174. break;
    175. }
    176. //入队
    177. if(camera_ebuf(cameraFd,index)==-1 ){
    178. break;
    179. }
    180. }
    181. while(1){
    182. //出队,得到一张图片的 首地址和大小,以及该图片的 索引编号
    183. if(camera_eqbuf(cameraFd,(void **)&yuv,&size,&index)==-1){
    184. break;
    185. }
    186. printf("size:%d\n",size);
    187. memset(jpeg,0,sizeof(jpeg));
    188. memcpy(jpeg,yuv,size);
    189. int fd1=open("1.jpg",O_WRONLY|O_CREAT,0644);
    190. int count = 0;
    191. while(count<size){
    192. int ret = write(fd1,jpeg+count,size-count);
    193. if(ret<size){
    194. printf("----数据太少----\n");
    195. }
    196. count += ret;
    197. }
    198. close(fd1);
    199. usleep(5000);
    200. //入队
    201. if(camera_ebuf(cameraFd,index)==-1 ){
    202. break;
    203. }
    204. }
    205. camera_stop(cameraFd);
    206. camera_quit(cameraFd);
    207. return 0;
    208. }

    视频格式如果是YUYV,则需要转码:YUYV=>RGB24=>JPEG

    1. #include "convert.h"
    2. #define ROUND_0_255(v) ((v) < 0 ? 0 : ((v) > 255 ? 255 : (v)))
    3. typedef struct {
    4. struct jpeg_destination_mgr pub;
    5. JOCTET *buffer;
    6. unsigned char *outbuffer;
    7. int outbuffer_size;
    8. unsigned char *outbuffer_cursor;
    9. int *written;
    10. } jpeg_dest_mgr, *jpeg_dest_mgr_ptr;
    11. struct jpeg_mgr_info {
    12. unsigned long written;
    13. JSAMPROW row_pointer[1];
    14. struct jpeg_error_mgr jerr;
    15. struct jpeg_compress_struct cinfo;
    16. };
    17. static struct jpeg_mgr_info jinfo;
    18. static short radj[] = {
    19. -175, -174, -172, -171, -169, -168, -167, -165,
    20. -164, -163, -161, -160, -159, -157, -156, -154,
    21. -153, -152, -150, -149, -148, -146, -145, -143,
    22. -142, -141, -139, -138, -137, -135, -134, -132,
    23. -131, -130, -128, -127, -126, -124, -123, -121,
    24. -120, -119, -117, -116, -115, -113, -112, -111,
    25. -109, -108, -106, -105, -104, -102, -101, -100,
    26. -98, -97, -95, -94, -93, -91, -90, -89,
    27. -87, -86, -84, -83, -82, -80, -79, -78,
    28. -76, -75, -74, -72, -71, -69, -68, -67,
    29. -65, -64, -63, -61, -60, -58, -57, -56,
    30. -54, -53, -52, -50, -49, -47, -46, -45,
    31. -43, -42, -41, -39, -38, -37, -35, -34,
    32. -32, -31, -30, -28, -27, -26, -24, -23,
    33. -21, -20, -19, -17, -16, -15, -13, -12,
    34. -10, -9, -8, -6, -5, -4, -2, -1,
    35. 0, 1, 2, 4, 5, 6, 8, 9,
    36. 10, 12, 13, 15, 16, 17, 19, 20,
    37. 21, 23, 24, 26, 27, 28, 30, 31,
    38. 32, 34, 35, 37, 38, 39, 41, 42,
    39. 43, 45, 46, 47, 49, 50, 52, 53,
    40. 54, 56, 57, 58, 60, 61, 63, 64,
    41. 65, 67, 68, 69, 71, 72, 74, 75,
    42. 76, 78, 79, 80, 82, 83, 84, 86,
    43. 87, 89, 90, 91, 93, 94, 95, 97,
    44. 98, 100, 101, 102, 104, 105, 106, 108,
    45. 109, 111, 112, 113, 115, 116, 117, 119,
    46. 120, 121, 123, 124, 126, 127, 128, 130,
    47. 131, 132, 134, 135, 137, 138, 139, 141,
    48. 142, 143, 145, 146, 148, 149, 150, 152,
    49. 153, 154, 156, 157, 159, 160, 161, 163,
    50. 164, 165, 167, 168, 169, 171, 172, 174,
    51. };
    52. static short gadj1[] = {
    53. -89, -88, -87, -87, -86, -85, -85, -84,
    54. -83, -83, -82, -81, -80, -80, -79, -78,
    55. -78, -77, -76, -76, -75, -74, -73, -73,
    56. -72, -71, -71, -70, -69, -69, -68, -67,
    57. -67, -66, -65, -64, -64, -63, -62, -62,
    58. -61, -60, -60, -59, -58, -57, -57, -56,
    59. -55, -55, -54, -53, -53, -52, -51, -50,
    60. -50, -49, -48, -48, -47, -46, -46, -45,
    61. -44, -43, -43, -42, -41, -41, -40, -39,
    62. -39, -38, -37, -36, -36, -35, -34, -34,
    63. -33, -32, -32, -31, -30, -30, -29, -28,
    64. -27, -27, -26, -25, -25, -24, -23, -23,
    65. -22, -21, -20, -20, -19, -18, -18, -17,
    66. -16, -16, -15, -14, -13, -13, -12, -11,
    67. -11, -10, -9, -9, -8, -7, -6, -6,
    68. -5, -4, -4, -3, -2, -2, -1, 0,
    69. 0, 0, 1, 2, 2, 3, 4, 4,
    70. 5, 6, 6, 7, 8, 9, 9, 10,
    71. 11, 11, 12, 13, 13, 14, 15, 16,
    72. 16, 17, 18, 18, 19, 20, 20, 21,
    73. 22, 23, 23, 24, 25, 25, 26, 27,
    74. 27, 28, 29, 30, 30, 31, 32, 32,
    75. 33, 34, 34, 35, 36, 36, 37, 38,
    76. 39, 39, 40, 41, 41, 42, 43, 43,
    77. 44, 45, 46, 46, 47, 48, 48, 49,
    78. 50, 50, 51, 52, 53, 53, 54, 55,
    79. 55, 56, 57, 57, 58, 59, 60, 60,
    80. 61, 62, 62, 63, 64, 64, 65, 66,
    81. 67, 67, 68, 69, 69, 70, 71, 71,
    82. 72, 73, 73, 74, 75, 76, 76, 77,
    83. 78, 78, 79, 80, 80, 81, 82, 83,
    84. 83, 84, 85, 85, 86, 87, 87, 88,
    85. };
    86. static short gadj2[] = {
    87. -43, -42, -42, -42, -41, -41, -41, -40,
    88. -40, -40, -39, -39, -39, -38, -38, -38,
    89. -37, -37, -37, -36, -36, -36, -35, -35,
    90. -35, -34, -34, -34, -33, -33, -33, -32,
    91. -32, -32, -31, -31, -31, -30, -30, -30,
    92. -29, -29, -29, -28, -28, -28, -27, -27,
    93. -27, -26, -26, -25, -25, -25, -24, -24,
    94. -24, -23, -23, -23, -22, -22, -22, -21,
    95. -21, -21, -20, -20, -20, -19, -19, -19,
    96. -18, -18, -18, -17, -17, -17, -16, -16,
    97. -16, -15, -15, -15, -14, -14, -14, -13,
    98. -13, -13, -12, -12, -12, -11, -11, -11,
    99. -10, -10, -10, -9, -9, -9, -8, -8,
    100. -8, -7, -7, -7, -6, -6, -6, -5,
    101. -5, -5, -4, -4, -4, -3, -3, -3,
    102. -2, -2, -2, -1, -1, -1, 0, 0,
    103. 0, 0, 0, 1, 1, 1, 2, 2,
    104. 2, 3, 3, 3, 4, 4, 4, 5,
    105. 5, 5, 6, 6, 6, 7, 7, 7,
    106. 8, 8, 8, 9, 9, 9, 10, 10,
    107. 10, 11, 11, 11, 12, 12, 12, 13,
    108. 13, 13, 14, 14, 14, 15, 15, 15,
    109. 16, 16, 16, 17, 17, 17, 18, 18,
    110. 18, 19, 19, 19, 20, 20, 20, 21,
    111. 21, 21, 22, 22, 22, 23, 23, 23,
    112. 24, 24, 24, 25, 25, 25, 26, 26,
    113. 27, 27, 27, 28, 28, 28, 29, 29,
    114. 29, 30, 30, 30, 31, 31, 31, 32,
    115. 32, 32, 33, 33, 33, 34, 34, 34,
    116. 35, 35, 35, 36, 36, 36, 37, 37,
    117. 37, 38, 38, 38, 39, 39, 39, 40,
    118. 40, 40, 41, 41, 41, 42, 42, 42,
    119. };
    120. static short badj[] = {
    121. -221, -220, -218, -216, -214, -213, -211, -209,
    122. -207, -206, -204, -202, -200, -199, -197, -195,
    123. -194, -192, -190, -188, -187, -185, -183, -181,
    124. -180, -178, -176, -174, -173, -171, -169, -168,
    125. -166, -164, -162, -161, -159, -157, -155, -154,
    126. -152, -150, -148, -147, -145, -143, -142, -140,
    127. -138, -136, -135, -133, -131, -129, -128, -126,
    128. -124, -123, -121, -119, -117, -116, -114, -112,
    129. -110, -109, -107, -105, -103, -102, -100, -98,
    130. -97, -95, -93, -91, -90, -88, -86, -84,
    131. -83, -81, -79, -77, -76, -74, -72, -71,
    132. -69, -67, -65, -64, -62, -60, -58, -57,
    133. -55, -53, -51, -50, -48, -46, -45, -43,
    134. -41, -39, -38, -36, -34, -32, -31, -29,
    135. -27, -25, -24, -22, -20, -19, -17, -15,
    136. -13, -12, -10, -8, -6, -5, -3, -1,
    137. 0, 1, 3, 5, 6, 8, 10, 12,
    138. 13, 15, 17, 19, 20, 22, 24, 25,
    139. 27, 29, 31, 32, 34, 36, 38, 39,
    140. 41, 43, 45, 46, 48, 50, 51, 53,
    141. 55, 57, 58, 60, 62, 64, 65, 67,
    142. 69, 71, 72, 74, 76, 77, 79, 81,
    143. 83, 84, 86, 88, 90, 91, 93, 95,
    144. 97, 98, 100, 102, 103, 105, 107, 109,
    145. 110, 112, 114, 116, 117, 119, 121, 123,
    146. 124, 126, 128, 129, 131, 133, 135, 136,
    147. 138, 140, 142, 143, 145, 147, 148, 150,
    148. 152, 154, 155, 157, 159, 161, 162, 164,
    149. 166, 168, 169, 171, 173, 174, 176, 178,
    150. 180, 181, 183, 185, 187, 188, 190, 192,
    151. 194, 195, 197, 199, 200, 202, 204, 206,
    152. 207, 209, 211, 213, 214, 216, 218, 220,
    153. };
    154. void convert_yuv_to_rgb(void *yuv, void *rgb, unsigned int width, unsigned int height, unsigned int bps)
    155. {
    156. unsigned int i;
    157. int y1, y2, u, v;
    158. unsigned char *src = yuv;
    159. unsigned char *dst = rgb;
    160. unsigned int count = width * height / 2;
    161. switch (bps) {
    162. case 24:
    163. for (i = 0; i < count; i++) {
    164. y1 = *src++;
    165. u = *src++;
    166. y2 = *src++;
    167. v = *src++;
    168. *dst++ = ROUND_0_255(y1 + radj[v]);
    169. *dst++ = ROUND_0_255(y1 - gadj1[u] - gadj2[v]);
    170. *dst++ = ROUND_0_255(y1 + badj[u]);
    171. *dst++ = ROUND_0_255(y2 + radj[v]);
    172. *dst++ = ROUND_0_255(y2 - gadj1[u] - gadj2[v]);
    173. *dst++ = ROUND_0_255(y2 + badj[u]);
    174. }
    175. break;
    176. }
    177. }
    178. void convert_rgb_to_jpg_init(void)
    179. {
    180. memset(&jinfo, 0, sizeof(struct jpeg_mgr_info));
    181. jinfo.cinfo.err = jpeg_std_error(&jinfo.jerr);
    182. jpeg_create_compress(&jinfo.cinfo);
    183. }
    184. int convert_rgb_to_jpg_work(void *rgb, void *jpeg, unsigned int width, unsigned int height, unsigned int bpp, int quality)
    185. {
    186. jinfo.written = width * height * bpp / 3;
    187. jpeg_mem_dest(&jinfo.cinfo, (unsigned char **)&jpeg, &jinfo.written);
    188. jinfo.cinfo.image_width = width;
    189. jinfo.cinfo.image_height = height;
    190. jinfo.cinfo.input_components = bpp / 8;
    191. jinfo.cinfo.in_color_space = JCS_RGB;
    192. jpeg_set_defaults(&jinfo.cinfo);
    193. jpeg_set_quality(&jinfo.cinfo, quality, TRUE);
    194. jpeg_start_compress(&jinfo.cinfo, TRUE);
    195. while(jinfo.cinfo.next_scanline < height) {
    196. jinfo.row_pointer[0] = rgb + jinfo.cinfo.next_scanline * width * bpp / 8;
    197. jpeg_write_scanlines(&jinfo.cinfo, jinfo.row_pointer, 1);
    198. }
    199. jpeg_finish_compress(&jinfo.cinfo);
    200. return (jinfo.written);
    201. }
    202. void convert_rgb_to_jpg_exit(void)
    203. {
    204. jpeg_destroy_compress(&jinfo.cinfo);
    205. }

  • 相关阅读:
    软件开发项目文档系列之九如何撰写测试方案
    1055 The World‘s Richest
    HaGRID手势识别数据集使用说明和下载
    数据结构——树的实现
    CKEditor5定制及文件上传
    linux内存管理——内存大小、起始地址的解析与修改
    单片机-LED介绍
    Linux Shell 自动交互功能实现
    mapreduce-maven--30.串联所有单词的字串
    腾讯、百度花式布局CRM
  • 原文地址:https://blog.csdn.net/hold_the_key/article/details/126862957