目錄
知識儲備:
視頻采集方式:
處理采集數(shù)據(jù):
相關結構體:
對于設備的操作步驟:
????????V4L2較V4L有較大的改動,并已成為2.6的標準接口,函蓋video\dvb\FM...,多數(shù)驅動都在向V4l2遷移。更好地了解V4L2先從應用入手,然后再深入到內核中結合物理設備/接口的規(guī)范實現(xiàn)相應的驅動。本文先就V4L2在視頻捕捉或camera方面的應用框架。
????????V4L2采用流水線的方式,操作更簡單直觀,基本遵循打開視頻設備、設置格式、處理數(shù)據(jù)、關閉設備,更多的具體操作通過ioctl函數(shù)來實現(xiàn)。
知識儲備:
?V4L2視頻編程本質:IO操作
攝像頭相關頭文件:/usr/include/linux/videodev2.h
設備文件不能被用戶創(chuàng)建,用戶也不能直接訪問內核的代碼和數(shù)據(jù)
=====================================================================
V4L2使用V4L2_MEMORY_USERPTR和V4L2_MEMORY_MMAP的區(qū)別:
視頻應用可以通過兩種方式從V4L2驅動申請buffer
1. USERPTR, 顧名思義是用戶空間指針的意思,應用層負責分配需要的內存空間,然后以指針的形式傳遞給V4L2驅動層,V4L2驅動會把capture的內容保存到指針所指的空間(層級切換,會慢不少)
一般來說,應用層需要確保這個內存空間物理上是連續(xù)的(IPU處理單元的需求),在android系統(tǒng)可以通過PMEM驅動來分配大塊的連續(xù)物理內存。應用層在不需要的時候要負責釋放申請的PMEM內存。
2. MMAP方式,內存映射模式,應用調用VIDIOC_REQBUFS ioctl分配設備buffers,參數(shù)標識需要的數(shù)目和類型。這個ioctl也可以用來改變buffers的數(shù)據(jù)以及釋放分配的內存,當然這個內存空間一般也是連續(xù)的。在應用空間能夠訪問這些物理地址之前,必須調用mmap函數(shù)把這些物理空間映射為用戶虛擬地址空間。
虛擬地址空間是通過munmap函數(shù)釋放的; 而物理內存的釋放是通過VIDIOC_REQBUFS來實現(xiàn)的(設置參數(shù)buf count為(0)),物理內存的釋放是實現(xiàn)特定的,mx51 v4l2是在關閉設備時進行釋放的。
所以二者都是申請連續(xù)的物理內存,只是申請和釋放的方式不同V4L2使用V4L2_MEMORY_USERPTR和V4L2_MEMORY_MMAP的區(qū)別 - Lxk- - 博客園
====================================================================
視頻采集方式:
?操作系統(tǒng)一般把系統(tǒng)使用的內存劃分成用戶空間和內核空間,分別由應用程序管理和操作系統(tǒng)管理。應用程序可以直接訪問內存的地址,而內核空間存放的是供內核訪問的代碼和數(shù)據(jù),用戶不能直接訪問。v4l2捕獲的數(shù)據(jù),最初是存放在內核空間的,這意味著用戶不能直接訪問該段內存,必須通過某些手段來轉換地址。
?一共有三種視頻采集方式:使用read、write方式;內存映射方式和用戶指針模式。
read、write方式,在用戶空間和內核空間不斷拷貝數(shù)據(jù),占用了大量用戶內存空間,效率不高。
內存映射方式:把設備里的內存映射到應用程序中的內存控件,直接處理設備內存,這是一種有效的方式。上面的mmap函數(shù)就是使用這種方式。
用戶指針模式:內存片段由應用程序自己分配。這點需要在v4l2_requestbuffers里將memory字段設置成V4L2_MEMORY_USERPTR。??
====================================================================
處理采集數(shù)據(jù):
V4L2有一個數(shù)據(jù)緩存,存放req.count數(shù)量的緩存數(shù)據(jù)。數(shù)據(jù)緩存采用FIFO的方式,當應用程序調用緩存數(shù)據(jù)時,緩存隊列將最先采集到的視頻數(shù)據(jù)緩存送出,并重新采集一張視頻數(shù)據(jù)。這個過程需要用到兩個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;?// 數(shù)據(jù)流類型,固定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的倍數(shù)
??? __u32?????????????????? height;?????? // 高,必須是16的倍數(shù)
??? __u32?????????????????? pixelformat;? // 視頻數(shù)據(jù)存儲類型,例如是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;?? //私有數(shù)據(jù),依賴于pixelformat???
};struct v4l2_requestbuffers結構體:用于在內核分配空間
struct v4l2_requestbuffers{
????????__u32?????????????? count;? // 緩存數(shù)量,也就是說在緩存隊列里保持多少張照片 ???
????????enum v4l2_buf_type? type;?// 數(shù)據(jù)流類型,固定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中分配的數(shù)據(jù)緩存轉換成物理地址
- VIDIOC_QUERYCAP:查詢驅動功能
- VIDIOC_ENUM_FMT:獲取當前驅動支持的視頻格式
- VIDIOC_S_FMT:設置當前驅動的頻捕獲格式
- VIDIOC_G_FMT:讀取當前驅動的頻捕獲格式
- VIDIOC_TRY_FMT:驗證當前驅動的顯示格式
- VIDIOC_CROPCAP:查詢驅動的修剪能力
- VIDIOC_S_CROP:設置視頻信號的邊框
- VIDIOC_G_CROP:讀取視頻信號的邊框
- VIDIOC_QBUF:把數(shù)據(jù)從緩存中讀取出來
- VIDIOC_DQBUF:把數(shù)據(jù)放回緩存隊列
- VIDIOC_STREAMON:開始視頻顯示函數(shù)
- VIDIOC_STREAMOFF:結束視頻顯示函數(shù)
- VIDIOC_QUERYSTD:檢查當前視頻設備支持的標準,例如PAL或NTSC。
====================================================================
對于設備的操作步驟:
①打開設備(阻塞或者非阻塞,驅動會將緩存(DQBUFF)里的東西返回給應用程序)②獲取設備支持的視頻格式(亞洲一般使用PAL(720*576)制式攝像頭、歐洲NTSC(720*480)制式攝像頭,使用VIDIOC_QUERYSTD來檢測)
③根據(jù)獲得的視頻格式,設置當前攝像頭的采集格式和大小(liunx編程中ioctl函數(shù))
④設置視頻捕獲格式(設置fmt.fmt.pix.pixelformat為YUYV還是V4L2_PIX_FMT_MJPEG)
⑤在內核分配內存空間(設置緩存數(shù)量(count)和存儲模式(內存映射、用戶空間指針))
⑥獲取并記錄緩存的物理空間(使用VIDIOC_REQBUFS,獲取count個緩存數(shù)量,其次調用VIDIOC_QUERYBUF獲取這些緩存的地址,然后使用mmap函數(shù)轉換成應用程序中的絕對地址,最后把這段緩存放入緩存隊列(這是用到內存映射、優(yōu)勢是不用頻繁轉換到用戶或者內核層,更快速))
⑦視頻采集(選擇read、write方式,內存映射方式,用戶指針模式? 這3種模式之一)
⑧處理采集數(shù)據(jù)(V4L2的數(shù)據(jù)緩存采用FIFO方式,將采集到的視頻數(shù)據(jù)緩存送出后再重新采集一張視頻數(shù)據(jù),需要用到VIDIOC_DQBUF和VIDIOC_QBUF)
⑨關閉視頻設備(close、fclose或者mmap(使用mmap后還需使用munmap方法))文章來源:http://www.zghlxwxcb.cn/news/detail-407664.html
#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>
//定義用戶緩沖區(qū),定義一個結構體、用來存儲 內核空間映射之后的首地址和空間大小
typedef struct VideoBuffer{
void *start;
size_t length;
}VideoBuffer;
//定義結構體數(shù)組,用來代表一個數(shù)組元素映射的緩沖區(qū)
struct VideoBuffer buffers[8];
//calloc:在堆區(qū)開辟8個sizeof(*buffers)這么大的連續(xù)空間
//VideoBuffer *buffers = calloc(fmt3.count,sizeof(*buffers));
//定義一個全局緩沖區(qū)
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:數(shù)據(jù)流類型,固定一直都是
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、根據(jù)獲得的視頻格式,設置當前攝像頭的采集格式和大小
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的視頻格式,后續(xù)還需要轉碼, 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編號的緩沖區(qū)
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;
//開始視頻采集,必須指定攝像頭拍攝的圖片存儲的緩存區(qū)的數(shù)據(jù)類型
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 的內核緩存出隊,緩存數(shù)據(jù)映射到了 用戶緩存區(qū)buffers
//如果內核緩存沒有圖片數(shù)據(jù),那么出隊將會永久阻塞,為了防止永久阻塞,建議使用IO多路復用來出隊
if (ioctl(fd, VIDIOC_DQBUF, &buf) == -1){
perror("DQBUF");
return -1;
}
//由于緩存中的數(shù)據(jù)都已經映射到了 用戶空間,因此可以將 用戶緩存中的數(shù)據(jù) 讀取出來
//*size = buf[0].length;
*size = buf.bytesused; //保存一張圖片的實際大小
*jpeg = buffers[0].start; //保存一張圖片數(shù)據(jù)的首地址
*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;
//開始視頻采集,必須指定攝像頭拍攝的圖片存儲的緩存區(qū)的數(shù)據(jù)類型
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("----數(shù)據(jù)太少----\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文章來源地址http://www.zghlxwxcb.cn/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模板網以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網!