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

这篇具有很好参考价值的文章主要介绍了项目之利用 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方法))

#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;
    //设置视频捕获格式为JPG
    if(strstr(fmtBuf,"JPEG")!=NULL){
        fmt2.fmt.pix.pixelformat = V4L2_PIX_FMT_MJPEG;
        *ismjpeg = 1;
    }
    else{
        //如果是YUYV的视频格式,后续还需要转码, YUYV->RGB24->JPEG
        fmt2.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文章来源地址https://www.toymoban.com/news/detail-407664.html

#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应用程序框架 进行视频录制的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包