项目之利用 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捕获的数据,最初是存放在内核空间的,这意味着用户不能直接访问该段内存,必须通过某些手段来转换地址。
一共有三种视频采集方式:使用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应用程序框架 进行视频录制相关推荐
- 【Linux开发】V4L2应用程序框架
V4L2应用程序框架 V4L2较V4L有较大的改动,并已成为2.6的标准接口,函盖video\dvb\FM...,多数驱动都在向V4l2迁移.更好地了解V4L2先从应用入手,然后再深入到内核中结合物理 ...
- 【愚公系列】2022年04月 微信小程序-实时音视频录制
文章目录 前言 一.实时音视频录制 1.js代码 2.wxml代码 3.效果 前言 小程序的实时音视频播放需要先去微信开发者平台开通权限,「开发」-「接口设置」中自助开通该组件权限. 类目属性如下: ...
- V4L2应用程序框架
V4L2是V4L的升级版本,linux下视频设备程序提供了一套接口规范. 常用的结构体在内核目录include/linux/videodev2.h中定义 struct v4l2_requestbuff ...
- V4L2应用程序框架--一【转】
本文转载自:http://blog.csdn.net/tommy_wxie/article/details/11369667 V4L2是V4L的升级版本,linux下视频设备程序提供了一套接口规范. ...
- IOS下利用OpenCV框架去除视频水印
想做个去水印的APP,第一个想到的就是CV里的inpaint图像修复技术.就想着把CV框架放在IOS中用,由于第一次接触IOS的开发,就看了两本实习时候导师大神推荐的书,很多东西都不太了解,虽然CV官 ...
- 基于微信小程序的短视频管理系统
末尾获取源码 开发语言:Java Java开发工具:JDK1.8 后端框架:SSM 前端框架:VUE 数据库:MySQL5.7 服务器:Tomcat8.5 开发软件:IDEA / Eclipse 是否 ...
- Flash+fms视频录制在项目中的实际应用
Flash+fms视频录制在项目中的实际应用 前言:以下只是记录本人在项目中的应用,而flash+fms视频录制有多种实现方式,具体可根据实际情况而定! 1:古人云:工欲善其事,必先利其器,首先安装f ...
- 小程序项目:基于微信小程序的在线考试系统springboot框架——计算机毕业设计
项目介绍 随着我国经济迅速发展,人们对手机的需求越来越大,各种手机软件也都在被广泛应用,但是对于手机进行数据信息管理,对于手机的各种软件也是备受学生的喜爱,微信在线考试系统被学生普遍使用,为方便学生能 ...
- java聊天室小程序论文_在Java项目中利用continue与break制作一个聊天室小程序
在Java项目中利用continue与break制作一个聊天室小程序 发布时间:2020-12-08 16:03:27 来源:亿速云 阅读:98 作者:Leah 在Java项目中利用continue与 ...
最新文章
- dos下 和 批处理中的 for 语句的基本用法
- php获取服务器相关信息
- 时光机穿梭---工作区和暂存区
- EMF的一些总结(2)——关于EMF的序列化
- 案例:演示pageContext对象的使用及源码分析获取属性方法
- 常用头文件和一些简单的函数
- 一位北美 IT 技术人教你如何破局
- android_root后的玩机:magisk模块root隐藏/lsposedxposed框架的使用/MIUI小窗多开
- 【OpenCV】58 二值图像分析—寻找最大内接圆
- 分区助手扩大c盘后自动修复_分区助手怎么扩大C盘?分区助手扩大C盘的方法
- SA299学习笔记 第一章 描述接口配置(3)
- Unity VR专栏(一)手柄控制
- 为何一个简单的测试类H5却要花费我2天时间才完成?
- python-画3D图
- node与npm版本对应关系以及使用nvm管理node版本
- 微信王者抢先服是什么服务器,王者荣耀抢先服是什么?王者荣耀抢先服介绍
- 【soft6星评论】站在5G时代的入口,我们看到了“智慧交通”的含苞待放
- 四川工程职业技术学院计算机专业,四川工程职业技术学院学生在四川省大学生计算机作品大赛获佳绩...
- 缓存微信accesstoken实现
- 门户网站建设有哪些类型?