C語言調用攝像頭生成avi視頻程序
更新時間:2023年11月14日 14:11:19 作者:樂山勁松
這篇文章主要為大家詳細介紹了C語言如何調用攝像頭生成avi視頻程序,文中的示例代碼講解詳細,具有一定的借鑒價值,有需要的小伙伴可以參考一下
時間控制是指:生成了n張圖片幀用了多少時間m。幀率等于n/m。對應于頭文件,m等于scale, n等于rate.為了精確,采用微秒計時。
此程序生成的視頻遠好于ffmpeg,可能是此程序沒有壓縮數(shù)據(jù)原因吧。
現(xiàn)在的幀率不高,是因為只用了一個攝像頭緩存區(qū)。
avi 頭文件
#ifndef AVI_H
#define AVI_H
#include <stdio.h>
//FILE * avi_ks(void);
//int avi_add(FILE*fp,char *data,int size);
//int avi_end(FILE *f_file);
struct avi{
struct riff{
unsigned char id[4];
unsigned int size;
unsigned char type[4];
}ri1;
struct hdrl{
unsigned char id[4]; //塊ID,固定為LIST
unsigned int size; //塊大小,等于struct avi_hdrl_list去掉id和size的大小
unsigned char type[4]; //塊類型,固定為hdrl
struct avih{
unsigned char id[4]; //塊ID,固定為avih
unsigned int size; //塊大小,等于struct avi_avih_chunk去掉id和size的大小
unsigned int us_per_frame; //視頻幀間隔時間(以微秒為單位)
unsigned int max_bytes_per_sec; //AVI文件的最大數(shù)據(jù)率
unsigned int padding; //設為0即可
unsigned int flags; //AVI文件全局屬性,如是否含有索引塊、音視頻數(shù)據(jù)是否交叉存儲等
unsigned int total_frames; //總幀數(shù)
unsigned int init_frames; //為交互格式指定初始幀數(shù)(非交互格式應該指定為0)
unsigned int streams; //文件包含的流的個數(shù),僅有視頻流時為1
unsigned int suggest_buff_size; //指定讀取本文件建議使用的緩沖區(qū)大小,通常為存儲一楨圖像 //以及同步聲音所需的數(shù)據(jù)之和,不指定時設為0
unsigned int width; //視頻主窗口寬度(單位:像素)
unsigned int height; //視頻主窗口高度(單位:像素)
unsigned int reserved[4]; //保留段,設為0即可
}ah1;
struct strl{
unsigned char id[4]; //塊ID,固定為LIST
unsigned int size; //塊大小,等于struct avi_strl_list去掉id和size的大小
unsigned char type[4]; //塊類型,固定為strl
struct strh{
unsigned char id[4]; //塊ID,固定為strh
unsigned int size; //塊大小,等于struct avi_strh_chunk去掉id和size的大小
unsigned char stream_type[4]; //流的類型,vids表示視頻流,auds表示音頻流
unsigned char codec[4]; //指定處理這個流需要的解碼器,如JPEG
unsigned int flags; //標記,如是否允許這個流輸出、調色板是否變化等,一般設為0即可
unsigned short priority; //流的優(yōu)先級,視頻流設為0即可
unsigned short language; //音頻語言代號,視頻流設為0即可
unsigned int init_frames; //為交互格式指定初始幀數(shù)(非交互格式應該指定為0)
unsigned int scale; //
unsigned int rate; //對于視頻流,rate / scale = 幀率fps
unsigned int start; //對于視頻流,設為0即可
unsigned int length; //對于視頻流,length即總幀數(shù)
unsigned int suggest_buff_size; //讀取這個流數(shù)據(jù)建議使用的緩沖區(qū)大小
unsigned int quality; //流數(shù)據(jù)的質量指標
unsigned int sample_size; //音頻采樣大小,視頻流設為0即可
struct rcFrame{ //這個流在視頻主窗口中的顯示位置,設為{0,0,width,height}即可
short left;
short top;
short right;
short bottom;
} AVI_RECT_FRAME;
}sh1;
struct strf{
unsigned char id[4]; //塊ID,固定為strf
unsigned int size; //塊大小,等于struct avi_strf_chunk去掉id和size的大小
unsigned int size1; //size1含義和值同size一樣
unsigned int width; //視頻主窗口寬度(單位:像素)
unsigned int height; //視頻主窗口高度(單位:像素)
unsigned short planes; //始終為1
unsigned short bitcount; //每個像素占的位數(shù),只能是1、4、8、16、24和32中的一個
unsigned char compression[4]; //視頻流編碼格式,如JPEG、MJPG等
unsigned int image_size; //視頻圖像大小,等于width * height * bitcount / 8
unsigned int x_pixels_per_meter; //顯示設備的水平分辨率,設為0即可
unsigned int y_pixels_per_meter; //顯示設備的垂直分辨率,設為0即可
unsigned int num_colors; //含義不清楚,設為0即可
unsigned int imp_colors; //含義不清楚,設為0即可
}sf1;
}sl1;
}hd1;
struct movi{
unsigned char id[4];
unsigned int size;
unsigned char type[4];
}movi1;
}HEAD;
#endif主程序
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <linux/videodev2.h>
#include <string.h>
#include <sys/mman.h>
#include "Avi.h"
#include <sys/time.h>
#define wid 1280 //攝像頭圖像寬度
#define hei 720 //圖像高度
#define bitlen 24 //圖像采樣寬度
#define perframe 30 //先預估一幀率,可以為攝像頭最大幀率,實際使用時的幀率小于次值
#define jhframe 30 //準備要錄像的圖片幀數(shù),控制錄像的時間長度
static int nframes=0; //總幀數(shù)
static int totalsize=0; //總字節(jié)數(shù)
FILE * avi_ks(void) {
FILE *fp = fopen("sample.avi", "w+b");
fseek(fp, sizeof(HEAD), SEEK_SET);
return fp;
}
int avi_add(FILE*fp, char *data, int size) {
unsigned char tmp[4] = {'0', '0', 'd', 'c'}; //00dc = 壓縮的視頻數(shù)據(jù)
fwrite(tmp, 4, 1, fp); //寫入是否是壓縮的視頻數(shù)據(jù)信息
fwrite(&size, 4, 1, fp); //寫入4字節(jié)對齊后的JPEG圖像大小
fwrite(data, size, 1, fp); //寫入真正的JPEG數(shù)據(jù)
return 0;
}
//----------------------------------------------------------------------------------
int avi_end(FILE *f_file) {
int width = wid;
int height = hei;
typedef struct hdrl AVI_HDRL_LIST;
typedef struct movi AVI_LIST_HEAD;
typedef struct avih AVI_AVIH_CHUNK;
typedef struct strl AVI_STRL_LIST;
typedef struct strh AVI_STRH_CHUNK;
typedef struct strf AVI_STRF_CHUNK;
typedef struct avi AVI_HEAD;
AVI_HEAD avi_head = {
{
{'R', 'I', 'F', 'F'},
4 + sizeof(AVI_HDRL_LIST) + sizeof(AVI_LIST_HEAD) + nframes * 8 + totalsize,
{'A', 'V', 'I', ' '}
},
{
{'L', 'I', 'S', 'T'},
sizeof(AVI_HDRL_LIST) - 8,
{'h', 'd', 'r', 'l'},
{
{'a', 'v', 'i', 'h'},
sizeof(AVI_AVIH_CHUNK) - 8,
1000000/perframe,width*height*bitlen*perframe/8, 0, 0, nframes,
0, 1,width*height*bitlen/8, width, height,
{0, 0, 0, 0}
},
{
{'L', 'I', 'S', 'T'},
sizeof(AVI_STRL_LIST) - 8,
{'s', 't', 'r', 'l'},
{
{'s', 't', 'r', 'h'},
sizeof(AVI_STRH_CHUNK) - 8,
{'v', 'i', 'd', 's'},
{'J', 'P', 'E', 'G'},
0, 0, 0, 0,
1, //4750
20000, //20000
0, nframes,width*height*bitlen*perframe/8,10000, 0,
{0, 0, width, height}
},
{
{'s', 't', 'r', 'f'},
sizeof(AVI_STRF_CHUNK) - 8,
sizeof(AVI_STRF_CHUNK) - 8,
width, height, 1,
bitlen,
{'J', 'P', 'E', 'G'},
width * height *bitlen/8, 0, 0, 0, 0
}
}
},
{
{'L', 'I', 'S', 'T'},
4 + nframes * 8 + totalsize,
{'m', 'o', 'v', 'i'}
}
};
fseek(f_file, 0, SEEK_SET);
fwrite(&avi_head, sizeof(HEAD), 1, f_file);
return 0;
}
int main(void){
int fd = open("/dev/video0", O_RDWR);
if(fd < 0)
{
perror("打開設備失敗");
return -1;
}
struct v4l2_format vfmt;
vfmt.type=1;
vfmt.fmt.pix.width=wid;
vfmt.fmt.pix.height=hei;
vfmt.fmt.pix.pixelformat=V4L2_PIX_FMT_MJPEG;
int ret = ioctl(fd, VIDIOC_S_FMT, &vfmt);
if(ret < 0)
{
perror("設置格式失敗");
}
struct v4l2_requestbuffers reqbuffer;
reqbuffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
reqbuffer.count = 1;
reqbuffer.memory = V4L2_MEMORY_MMAP ;
ret = ioctl(fd, VIDIOC_REQBUFS, &reqbuffer);
if(ret < 0)
{
perror("申請隊列空間失敗");
}
struct v4l2_buffer mapbuffer;
unsigned char *mptr;
unsigned int size;
mapbuffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
mapbuffer.index = 0;
ret = ioctl(fd, VIDIOC_QUERYBUF, &mapbuffer);//查詢緩沖區(qū)狀態(tài)
if(ret < 0)
{
perror("查詢內核空間隊列失敗");
}
int type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
ret = ioctl(fd, VIDIOC_STREAMON, &type); //啟動流
if(ret < 0)
{
perror("開啟失敗");
}
mptr= (unsigned char *)mmap(NULL, mapbuffer.length, PROT_READ|PROT_WRITE,
MAP_SHARED, fd,0); //設備映射到緩沖區(qū)內存
size=mapbuffer.length;
ret = ioctl(fd, VIDIOC_QBUF, &mapbuffer); //把緩沖區(qū)數(shù)據(jù)放入讀隊列中
if(ret < 0)
{
perror("放回失敗");
}
//---------------------------------------------------------------------------
FILE *file=avi_ks();
struct timeval start,end;
gettimeofday(&start, NULL ); //微秒記時開始
while(nframes<jhframe){
ret = ioctl(fd, VIDIOC_DQBUF, &mapbuffer); //讀當前隊列緩沖區(qū)的數(shù)據(jù)
if(ret < 0)
{
perror("提取數(shù)據(jù)失敗");
}
int size=wid*hei*bitlen/8;
unsigned char tmp[4] = {'0', '0', 'd', 'c'}; //00dc = 壓縮的視頻數(shù)據(jù)
fwrite(tmp, 4, 1, file); //寫入是否是壓縮的視頻數(shù)據(jù)信息
fwrite(&size, 4, 1, file); //寫入4字節(jié)對齊后的JPEG圖像大小
fwrite(mptr,size, 1, file); //寫入真正的JPEG數(shù)據(jù)
ret = ioctl(fd, VIDIOC_QBUF, &mapbuffer); //把緩沖區(qū)數(shù)據(jù)放入讀隊列中
if(ret < 0)
{
perror("放回失敗");
}
nframes++;
totalsize++;
}
static float timeuse1=0;
gettimeofday(&end, NULL ); //記時結束
timeuse1 =1000*(1000000 * ( end.tv_sec - start.tv_sec ) + end.tv_usec - start.tv_usec);
int timeuse=(int)(timeuse1/1000000); //轉換為豪秒
//---------------------------------------------------------
ret = ioctl(fd, VIDIOC_STREAMOFF, &type);
avi_end(file);
int scale=timeuse;
int rate=nframes*1000; //重寫實際幀率=rate/scale ,乘1000是timeuse 為毫秒,轉換為秒
fseek(file,128,SEEK_SET); //128 為scale 在文件中的字節(jié)位數(shù)(偏移)
fwrite(&scale,4,1,file);
fwrite(&rate,4,1,file); //rate 為128+4 位
fclose(file);
munmap(mptr, size);
/* FILE *ss=fopen("sample.avi","rb"); //檢驗scale ,rate 新值
fseek(ss,0,SEEK_END);
int len=ftell(ss);
int ff=fileno(ss);
char *tf=mmap(NULL,len,PROT_READ,MAP_SHARED,ff,0);
memcpy(&HEAD,&tf[0],sizeof(HEAD));
printf("scale:%d\n",HEAD.hd1.sl1.sh1.scale);
printf("rate:%d\n",HEAD.hd1.sl1.sh1.rate);
printf("end\n");
*/
close(fd);
return 0;
}到此這篇關于C語言調用攝像頭生成avi視頻程序的文章就介紹到這了,更多相關C語言生成avi視頻內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
C/C++數(shù)字與字符串互相轉換的實現(xiàn)示例
本文主要介紹了C/C++數(shù)字與字符串互相轉換的實現(xiàn)示例,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2025-02-02

