目录

知识储备:

视频采集方式:

处理采集数据:

相关结构体:

对于设备的操作步骤:


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捕获的数据,最初是存放在内核空间的,这意味着用户不能直接访问该段内存,必须通过某些手段来转换地址。

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

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

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

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

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

处理采集数据:

V4L2有一个数据缓存,存放req.count数量的缓存数据。数据缓存采用FIFO的方式,当应用程序调用缓存数据时,缓存队列将最先采集到的视频数据缓存送出,并重新采集一张视频数据。这个过程需要用到两个ioctl命令,VIDIOC_DQBUF和VIDIOC_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函数转换成应用程序中的绝对地址,最后把这段缓存放入缓存队列(这是用到内存映射、优势是不用频繁转换到用户或者内核层,更快速))

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

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

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

#include <stdio.h>
#include <linux/videodev2.h>    //摄像头相关的头文件
#include <sys/ioctl.h>
#include <unistd.h>
#include <sys/types.h>
#include <string.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <stdlib.h>
//定义用户缓冲区,定义一个结构体、用来存储 内核空间映射之后的首地址和空间大小
typedef struct VideoBuffer{void *start;size_t length;
}VideoBuffer;
//定义结构体数组,用来代表一个数组元素映射的缓冲区
struct VideoBuffer buffers[8];
//calloc:在堆区开辟8个sizeof(*buffers)这么大的连续空间
//VideoBuffer *buffers = calloc(fmt3.count,sizeof(*buffers));
//定义一个全局缓冲区
struct v4l2_buffer buf;
int camera_init(const char *dev,int *ismjpeg)
{//1、以阻塞模式打开摄像头int fd = open(dev,O_RDWR,0);if(fd < 0){perror("open_error");return -1;}//2、获取摄像头支持的视频格式 char fmtBuf[32] = {0};struct v4l2_fmtdesc fmt1;memset(&fmt1,0,sizeof(fmt1));fmt1.index = 0;//V4L2_BUF_TYPE_VBI_CAPTURE:数据流类型,固定一直都是fmt1.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;//VIDIOC_ENUM_FMT:获取当前驱动的频捕获格式while( ioctl(fd,VIDIOC_ENUM_FMT,&fmt1) != -1){printf("fmt1.index:%d\n",fmt1.index++);printf("format:%s\n",fmt1.description);strcat(fmtBuf,fmt1.description);}//3、根据获得的视频格式,设置当前摄像头的采集格式和大小struct v4l2_format fmt2;fmt2.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;fmt2.fmt.pix.width = 640;fmt2.fmt.pix.height = 480;//设置视频捕获格式为JPGif(strstr(fmtBuf,"JPEG")!=NULL){fmt2.fmt.pix.pixelformat = V4L2_PIX_FMT_MJPEG;*ismjpeg = 1;}else{//如果是YUYV的视频格式,后续还需要转码, YUYV->RGB24->JPEGfmt2.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;*ismjpeg = 0;}if( ioctl(fd,VIDIOC_S_FMT,&fmt2)==-1 ){perror("set fmt");return -1;}//4、在内核分配内存空间struct v4l2_requestbuffers fmt3;fmt3.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;//设置缓存队列里保持8张照片fmt3.count = 8;fmt3.memory = V4L2_MEMORY_MMAP;//VIDIOC_REQBUFS:分配内存if(ioctl(fd,VIDIOC_REQBUFS,&fmt3)==-1){perror("req fmt");return -1;}//5、获取并记录缓存的物理空间
//    struct v4l2_buffer buf;for(int i=0;i<fmt3.count;++i){buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;buf.memory = V4L2_MEMORY_MMAP;buf.index = i;//读取内核中的某个index编号的缓冲区if( ioctl(fd,VIDIOC_QUERYBUF,&buf)==-1 ){perror("VIDIOC_QUERYBUF");return -1;}//将内核中读取的缓存映射到 用户空间 buffers[i].length = buf.length; //先保存编号为i的内核空间的大小到用户空间//内存映射buffers[i].start = mmap(NULL, buf.length, PROT_READ | PROT_WRITE,MAP_SHARED,fd, buf.m.offset);if(buffers[i].start == MAP_FAILED){ //判断是否映射失败perror("mmap");return -1;}//将读取出来的缓存重新放入缓存队列if (ioctl(fd, VIDIOC_QBUF, &buf) == -1) {perror("mmap qbuf");return -1;}}return fd;
}
int camera_start(int fd)
{enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;//开始视频采集,必须指定摄像头拍摄的图片存储的缓存区的数据类型if(ioctl(fd,VIDIOC_STREAMON,&type)==-1 ){perror("start no");return -1;}return 0;
}
int camera_eqbuf(int fd,void **jpeg,int *size,int *index)
{
//  struct v4l2_buffer buf;//局部放不回去,设置为全局的memset(&buf,0,sizeof(buf));buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;buf.memory = V4L2_MEMORY_MMAP;buf.index = 0;  //指定要出队的 内核缓存编号//将编号为 0 的内核缓存出队,缓存数据映射到了 用户缓存区buffers    //如果内核缓存没有图片数据,那么出队将会永久阻塞,为了防止永久阻塞,建议使用IO多路复用来出队if (ioctl(fd, VIDIOC_DQBUF, &buf) == -1){perror("DQBUF");return -1;}//由于缓存中的数据都已经映射到了 用户空间,因此可以将 用户缓存中的数据 读取出来//*size = buf[0].length;*size = buf.bytesused;      //保存一张图片的实际大小*jpeg = buffers[0].start; //保存一张图片数据的首地址*index = buf.index;          //保存当前图片缓存的索引return 0;
}
int camera_ebuf(int fd,int index)
{buf.index = index;if (ioctl(fd, VIDIOC_QBUF, &buf) == -1) {perror("qbuf");return -1;}return 0;
}
int camera_stop(int fd)
{enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;//开始视频采集,必须指定摄像头拍摄的图片存储的缓存区的数据类型if(ioctl(fd,VIDIOC_STREAMON,&type)==-1 ){perror("start no");return -1;}return 0;
}
int camera_quit(int fd)
{close(fd);return 0;
}
int main(int argc, char *argv[])
{ int ismjpeg = 0;int index = -1,size;char *yuv,*jpeg;//野指针,取一个地址,让野指针去存储图片首地址,会出问题int cameraFd = camera_init("/dev/video0",&ismjpeg);if(cameraFd<0){return -1;}if( camera_start(cameraFd)==-1 ){return -1;}if(ismjpeg == 1){printf("capture format:MJPEG\n");jpeg = (char *)malloc(640*480);}else{printf("capture format:YUYV\n");}for(int i=0;i<8;i++){//出队,得到一张图片的 首地址和大小,以及该图片的 索引编号if(camera_eqbuf(cameraFd,(void **)&yuv,&size,&index)==-1){break;}//入队 if(camera_ebuf(cameraFd,index)==-1 ){break;}}while(1){  //出队,得到一张图片的 首地址和大小,以及该图片的 索引编号if(camera_eqbuf(cameraFd,(void **)&yuv,&size,&index)==-1){break;}printf("size:%d\n",size);memset(jpeg,0,sizeof(jpeg));memcpy(jpeg,yuv,size);int fd1=open("1.jpg",O_WRONLY|O_CREAT,0644);int count = 0;while(count<size){int ret = write(fd1,jpeg+count,size-count);if(ret<size){printf("----数据太少----\n");}count += ret;}close(fd1);usleep(5000);//入队 if(camera_ebuf(cameraFd,index)==-1 ){break;}}camera_stop(cameraFd);camera_quit(cameraFd);return 0;
} 

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

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

项目之利用 V4L2应用程序框架 进行视频录制相关推荐

  1. 【Linux开发】V4L2应用程序框架

    V4L2应用程序框架 V4L2较V4L有较大的改动,并已成为2.6的标准接口,函盖video\dvb\FM...,多数驱动都在向V4l2迁移.更好地了解V4L2先从应用入手,然后再深入到内核中结合物理 ...

  2. 【愚公系列】2022年04月 微信小程序-实时音视频录制

    文章目录 前言 一.实时音视频录制 1.js代码 2.wxml代码 3.效果 前言 小程序的实时音视频播放需要先去微信开发者平台开通权限,「开发」-「接口设置」中自助开通该组件权限. 类目属性如下: ...

  3. V4L2应用程序框架

    V4L2是V4L的升级版本,linux下视频设备程序提供了一套接口规范. 常用的结构体在内核目录include/linux/videodev2.h中定义 struct v4l2_requestbuff ...

  4. V4L2应用程序框架--一【转】

    本文转载自:http://blog.csdn.net/tommy_wxie/article/details/11369667 V4L2是V4L的升级版本,linux下视频设备程序提供了一套接口规范. ...

  5. IOS下利用OpenCV框架去除视频水印

    想做个去水印的APP,第一个想到的就是CV里的inpaint图像修复技术.就想着把CV框架放在IOS中用,由于第一次接触IOS的开发,就看了两本实习时候导师大神推荐的书,很多东西都不太了解,虽然CV官 ...

  6. 基于微信小程序的短视频管理系统

    末尾获取源码 开发语言:Java Java开发工具:JDK1.8 后端框架:SSM 前端框架:VUE 数据库:MySQL5.7 服务器:Tomcat8.5 开发软件:IDEA / Eclipse 是否 ...

  7. Flash+fms视频录制在项目中的实际应用

    Flash+fms视频录制在项目中的实际应用 前言:以下只是记录本人在项目中的应用,而flash+fms视频录制有多种实现方式,具体可根据实际情况而定! 1:古人云:工欲善其事,必先利其器,首先安装f ...

  8. 小程序项目:基于微信小程序的在线考试系统springboot框架——计算机毕业设计

    项目介绍 随着我国经济迅速发展,人们对手机的需求越来越大,各种手机软件也都在被广泛应用,但是对于手机进行数据信息管理,对于手机的各种软件也是备受学生的喜爱,微信在线考试系统被学生普遍使用,为方便学生能 ...

  9. java聊天室小程序论文_在Java项目中利用continue与break制作一个聊天室小程序

    在Java项目中利用continue与break制作一个聊天室小程序 发布时间:2020-12-08 16:03:27 来源:亿速云 阅读:98 作者:Leah 在Java项目中利用continue与 ...

最新文章

  1. dos下 和 批处理中的 for 语句的基本用法
  2. php获取服务器相关信息
  3. 时光机穿梭---工作区和暂存区
  4. EMF的一些总结(2)——关于EMF的序列化
  5. 案例:演示pageContext对象的使用及源码分析获取属性方法
  6. 常用头文件和一些简单的函数
  7. 一位北美 IT 技术人教你如何破局
  8. android_root后的玩机:magisk模块root隐藏/lsposedxposed框架的使用/MIUI小窗多开
  9. 【OpenCV】58 二值图像分析—寻找最大内接圆
  10. 分区助手扩大c盘后自动修复_分区助手怎么扩大C盘?分区助手扩大C盘的方法
  11. SA299学习笔记 第一章 描述接口配置(3)
  12. Unity VR专栏(一)手柄控制
  13. 为何一个简单的测试类H5却要花费我2天时间才完成?
  14. python-画3D图
  15. node与npm版本对应关系以及使用nvm管理node版本
  16. 微信王者抢先服是什么服务器,王者荣耀抢先服是什么?王者荣耀抢先服介绍
  17. 【soft6星评论】站在5G时代的入口,我们看到了“智慧交通”的含苞待放
  18. 四川工程职业技术学院计算机专业,四川工程职业技术学院学生在四川省大学生计算机作品大赛获佳绩...
  19. 缓存微信accesstoken实现
  20. 门户网站建设有哪些类型?

热门文章

  1. 彻底理解Python中浅拷贝和深拷贝的区别
  2. Python3 pd.merge()使用实例
  3. Spring Boot从0开始学的个人笔记 2 -- 配置文件
  4. spla计算机语言,东亚地区的语言及其文化价值-暨南大学学报.PDF
  5. AI 易谈、落地不易,触景无限Mini盒子带来的产业革命...
  6. 关于研究生复试,给大家的真诚建议和祝福
  7. Java Script 循环
  8. Mybatis学习笔记之---动态sql中标签的使用
  9. 通讯行业的标准和规范
  10. ray.tune文档总结