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

Posted 承诺$枷锁

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了项目之利用 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

#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);

Android之利用EventBus进行消息传递

什么是EventBus

EventBus是一个 发布/订阅 模式的消息总线库,它简化了应用程序内各组件间、组件与后台线程间的通信,解耦了事件的发送者和接收者,避免了复杂的、易于出错的依赖及生命周期问题,可以使我们的代码更加简洁、健壮。EventBus 用于各组件通信,那么用于 fragment 之间的通信就非常合适了。


 

1、基本框架搭建

想必大家从一个Activity跳转到第二个Activity的程序应该都会写,这里先稍稍把两个Activity跳转的代码建起来。后面再添加EventBus相关的玩意。

MainActivity布局(activity_main.xml)

 

  1. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    
  2.     xmlns:tools="http://schemas.android.com/tools"    
  3.     android:layout_width="match_parent"    
  4.     android:layout_height="match_parent"    
  5.     android:orientation="vertical">    
  6.         
  7.     <Button     
  8.         android:id="@+id/btn_try"    
  9.         android:layout_width="match_parent"    
  10.         android:layout_height="wrap_content"    
  11.         android:text="btn_bty"/>    
  12.     <TextView     
  13.         android:id="@+id/tv"    
  14.         android:layout_width="wrap_content"    
  15.         android:layout_height="match_parent"/>    
  16.     
  17. </LinearLayout>   


新建一个Activity,SecondActivity布局(activity_second.xml)

 

  1. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    
  2.     xmlns:tools="http://schemas.android.com/tools"    
  3.     android:layout_width="match_parent"    
  4.     android:layout_height="match_parent"    
  5.     android:orientation="vertical"    
  6.     tools:context="com.harvic.try_eventbus_1.SecondActivity" >    
  7.     
  8.     <Button     
  9.         android:id="@+id/btn_first_event"    
  10.         android:layout_width="match_parent"    
  11.         android:layout_height="wrap_content"    
  12.         android:text="First Event"/>    
  13.     
  14. </LinearLayout>    

MainActivity.java (点击btn跳转到第二个Activity)

  1. public class MainActivity extends Activity {    
  2.     
  3.     Button btn;    
  4.     
  5.     @Override    
  6.     protected void onCreate(Bundle savedInstanceState) {    
  7.         super.onCreate(savedInstanceState);    
  8.         setContentView(R.layout.activity_main);    
  9.     
  10.         btn = (Button) findViewById(R.id.btn_try);    
  11.     
  12.         btn.setOnClickListener(new View.OnClickListener() {    
  13.     
  14.             @Override    
  15.             public void onClick(View v) {    
  16.                 // TODO Auto-generated method stub    
  17.                 Intent intent = new Intent(getApplicationContext(),    
  18.                         SecondActivity.class);    
  19.                 startActivity(intent);    
  20.             }    
  21.         });    
  22.     }    
  23.     
  24. }    


到这,基本框架就搭完了,下面开始按步骤使用EventBus了。

2、新建一个类FirstEvent

  1. package com.harvic.other;    
  2.     
  3. public class FirstEvent {    
  4.     
  5.     private String mMsg;    
  6.     public FirstEvent(String msg) {    
  7.         // TODO Auto-generated constructor stub    
  8.         mMsg = msg;    
  9.     }    
  10.     public String getMsg(){    
  11.         return mMsg;    
  12.     }    
  13. }   


3、在要接收消息的页面注册EventBus:

在上面的GIF图片的演示中,大家也可以看到,我们是要在MainActivity中接收发过来的消息的,所以我们在MainActivity中注册消息。

通过我们会在OnCreate()函数中注册EventBus,在OnDestroy()函数中反注册。所以整体的注册与反注册的代码如下:

  1. package com.example.tryeventbus_simple;    
  2.     
  3. import com.harvic.other.FirstEvent;    
  4.     
  5. import de.greenrobot.event.EventBus;    
  6. import android.app.Activity;    
  7. import android.content.Intent;    
  8. import android.os.Bundle;    
  9. import android.util.Log;    
  10. import android.view.View;    
  11. import android.widget.Button;    
  12. import android.widget.TextView;    
  13. import android.widget.Toast;    
  14.     
  15. public class MainActivity extends Activity {    
  16.     
  17.     Button btn;    
  18.     TextView tv;    
  19.     
  20.     @Override    
  21.     protected void onCreate(Bundle savedInstanceState) {    
  22.         super.onCreate(savedInstanceState);    
  23.         setContentView(R.layout.activity_main);    
  24.                 //注册EventBus    
  25.         EventBus.getDefault().register(this);    
  26.     
  27.         btn = (Button) findViewById(R.id.btn_try);    
  28.         tv = (TextView)findViewById(R.id.tv);    
  29.     
  30.         btn.setOnClickListener(new View.OnClickListener() {    
  31.     
  32.             @Override    
  33.             public void onClick(View v) {    
  34.                 // TODO Auto-generated method stub    
  35.                 Intent intent = new Intent(getApplicationContext(),    
  36.                         SecondActivity.class);    
  37.                 startActivity(intent);    
  38.             }    
  39.         });    
  40.     }    
  41.     @Override    
  42.     protected void onDestroy(){    
  43.         super.onDestroy();    
  44.         EventBus.getDefault().unregister(this);//反注册EventBus    
  45.     }    
  46. }    


4、发送消息

发送消息是使用EventBus中的Post方法来实现发送的,发送过去的是我们新建的类的实例!

 

  1. EventBus.getDefault().post(new FirstEvent("FirstEvent btn clicked"));    

完整的SecondActivity.Java的代码如下:

 

  1. package com.example.tryeventbus_simple;    
  2.     
  3. import com.harvic.other.FirstEvent;    
  4.     
  5. import de.greenrobot.event.EventBus;    
  6. import android.app.Activity;    
  7. import android.os.Bundle;    
  8. import android.view.View;    
  9. import android.widget.Button;    
  10.     
  11. public class SecondActivity extends Activity {    
  12.     private Button btn_FirstEvent;    
  13.     
  14.     @Override    
  15.     protected void onCreate(Bundle savedInstanceState) {    
  16.         super.onCreate(savedInstanceState);    
  17.         setContentView(R.layout.activity_second);    
  18.         btn_FirstEvent = (Button) findViewById(R.id.btn_first_event);    
  19.     
  20.         btn_FirstEvent.setOnClickListener(new View.OnClickListener() {    
  21.     
  22.             @Override    
  23.             public void onClick(View v) {    
  24.                 // TODO Auto-generated method stub    
  25.                 EventBus.getDefault().post(    
  26.                         new FirstEvent("FirstEvent btn clicked"));    
  27.             }    
  28.         });    
  29.     }    
  30. }    


5、接收消息

接收消息时,我们使用EventBus中最常用的onEventMainThread()函数来接收消息,具体为什么用这个,我们下篇再讲,这里先给大家一个初步认识,要先能把EventBus用起来先。

在MainActivity中重写onEventMainThread(FirstEvent event),参数就是我们自己定义的类:

在收到Event实例后,我们将其中携带的消息取出,一方面Toast出去,一方面传到TextView中;

    1. public void onEventMainThread(FirstEvent event) {    
    2.     
    3.     String msg = "onEventMainThread收到了消息:" + event.getMsg();    
    4.     Log.d("harvic", msg);    
    5.     tv.setText(msg);    
    6.     Toast.makeText(this, msg, Toast.LENGTH_LONG).show();    
    7. }   











以上是关于项目之利用 V4L2应用程序框架 进行视频录制的主要内容,如果未能解决你的问题,请参考以下文章

Xilinx Linux V4L2视频管道(Video Pipeline)驱动程序分析

1. 摄像头V4L2驱动框架分析

二十四V4L2框架分析和虚拟摄像头驱动编写

V4L2驱动程序框架架构

Android之利用EventBus进行消息传递

为v4l2实现循环缓冲区