一、准备工作
sudo apt-get install libv4l-dev
二、编码实现
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <linux/videodev2.h>
#include <time.h>
#define CAMERA_DEVICE "/dev/video0"
#define INITIAL_IMAGE_PATH "./"
#define VIDEO_WIDTH 1366
#define VIDEO_HEIGHT 768
#define VIDEO_FORMAT V4L2_PIX_FMT_JPEG
#define BUFFER_COUNT 2 //image count in device buffer queue
#define DESIRED_IMAGE_COUNT 5
#define M2S(x) #x
typedef struct video_buffer
{
void *start;
size_t length;
}VideoBuffer;
//get each image's path name via different catching time
void get_image_name(char _str[],int _num)
{
char time_str[30];
time_t timep;
struct tm *p;
time(&timep);
p=localtime(&timep);
sprintf(time_str,"%d%d%d_%d_%d_%d_%d",(1900+p->tm_year), (1+p->tm_mon),p->tm_mday, p->tm_hour, p->tm_min, p->tm_sec,_num);
strcat(time_str,".jpg");
strcat(_str,time_str);
}
int main(int argc,char **argv)
{
int fd = 0; //camera handel
int err = 0; //returned value of function: inctl
char image_path[80] = INITIAL_IMAGE_PATH;
struct v4l2_capability cap;
struct v4l2_fmtdesc fmtdesc;
struct v4l2_format format;
struct v4l2_requestbuffers req; //buffer in device
struct v4l2_buffer buf;
struct video_buffer *video_buf = NULL;
//1. open file of camera
printf("#open camera:\n");
fd=open(CAMERA_DEVICE,O_RDWR,0);
if(fd == -1)
{
printf("failed to open camera!\n");
exit(0);
}
printf("fd=%d\n",fd);
//2. query driver's capability
printf("\n#query driver's capability:\n");
if(ioctl(fd, VIDIOC_QUERYCAP, &cap) == -1)
{
printf("failed to query driver's driver capability!\n");
exit(0);
}
printf("cap.capabilities=0x%x\n",cap.capabilities);
//3. enumerate image format(s) driver supported
printf("\n#enumerate image format(s) driver supported:\n");
fmtdesc.index=0;
fmtdesc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
printf("image format(s) driver supported:\n");
do{
err=ioctl(fd,VIDIOC_ENUM_FMT,&fmtdesc);
printf("index:%d,flag:%d, format name:%s,pixelformat=0x%x \n",fmtdesc.index,fmtdesc.flags,fmtdesc.description,fmtdesc.pixelformat);
fmtdesc.index++;
}while(0==err);
//4. set frame format
printf("\n#set frame format:\n");
memset(&format,0,sizeof(struct v4l2_format));
format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
format.fmt.pix.width = VIDEO_WIDTH; //width of frame photo
format.fmt.pix.height = VIDEO_HEIGHT;//hight of frame photo
format.fmt.pix.pixelformat = VIDEO_FORMAT;//frame format
err = ioctl(fd, VIDIOC_TRY_FMT, &format);
if(err == -1){
printf("Unsupported video format value of %s: .\n",M2S(VIDEO_FORMAT));
return 0;
}
err = ioctl(fd, VIDIOC_S_FMT, &format);
printf("set value of VIDIOC_S_FMT=%d\n",err);
//5. apply memory for images from catching video
printf("\n#apply memory for images from catching video:\n");
req.count = BUFFER_COUNT;
req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
req.memory = V4L2_MEMORY_MMAP;
if(ioctl(fd,VIDIOC_REQBUFS,&req) == -1) //VIDIOC_REQBUFS means allocate memory from device
{
printf("failed to apply memory for images from catching video!\n");
exit(0);
}
//6. apply phycisal(program) memory and map it to memory of images
printf("\n#apply phycisal memory and map it to memory of images:\n");
video_buf = calloc(req.count, sizeof(*video_buf));
int numBufs=0;
for (numBufs = 0; numBufs < req.count; numBufs++)
{
memset( &buf, 0, sizeof(buf) );
buf.index = numBufs;
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory = V4L2_MEMORY_MMAP;
//apply buffer memory from device
if (ioctl(fd, VIDIOC_QUERYBUF, &buf) == -1)
{
printf("faild to get buffer memory%d\n",numBufs);
exit(0);
}
video_buf[numBufs].length = buf.length;
// map device buffer into program memory so that we can get information of image(s) and display in monitor
video_buf[numBufs].start = mmap(NULL, buf.length, PROT_READ | PROT_WRITE,MAP_SHARED,fd, buf.m.offset);
if (video_buf[numBufs].start == MAP_FAILED)
{
printf("video_buf[%d] ",numBufs);
perror("map error:");
exit(0);
}
//queue buffer to save images from device
if (ioctl(fd, VIDIOC_QBUF, &buf) == -1)
{
printf("fiald to push into buffer queue\n");
exit(0);
}
printf("Frame buffer %d: address=0x%x, length=%d\n", numBufs, (unsigned int)video_buf[numBufs].start, video_buf[numBufs].length);
}
//7. start to record video
printf("\n#start to record video:\n");
enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
err = ioctl(fd, VIDIOC_STREAMON, &type);//VIDIOC_STREAMON: start command:video captuer,then the device program will start
//colloct video data and save it into device's buffer
if (err < 0)
{
printf("VIDIOC_STREAMON failed (%d)\n", err);
exit(0);
}
int loop_i;
for(loop_i=0;loop_i<DESIRED_IMAGE_COUNT;loop_i++)
{
// Get frame
err = ioctl(fd, VIDIOC_DQBUF, &buf);//get a photo from device and save into device buffer,because of mapping before,
//it also saved into program memory
if (err < 0)
{
printf("VIDIOC_DQBUF failed (%d)\n", err);
exit(0);
}
// save frame
strcpy(image_path,INITIAL_IMAGE_PATH);//Re-initialise path name of image
get_image_name(image_path,loop_i+1);//get new path name
FILE *fp = fopen(image_path, "wb");
if (fp < 0)
{
printf("open frame data file failed\n");
exit(0);
}
fwrite(video_buf[buf.index].start, 1, buf.length, fp);
fclose(fp);
printf("Capture frame%d saved in %s\n",loop_i+1,image_path);
// Re-queen buffer
err = ioctl(fd, VIDIOC_QBUF, &buf);
if (err < 0)
{
printf("VIDIOC_QBUF failed (%d)\n", err);
exit(0);
}
}
// Release the resource
for (numBufs=0; numBufs< req.count; numBufs++)
{
munmap(video_buf[numBufs].start, video_buf[numBufs].length);
}
//8. close file of camera
printf("\n#close camera:\n");
if(fd != -1)
{
if(close(fd) != -1)
printf("succeed to close camera!\n");
}
else
{
printf("failed to close camera!\n");
exit(0);
}
//9. free memory
free(video_buf);
return 0;
}
Makefile
DIRS =
CC = gcc
CXX = g++
AR = ar
ARFLAGS = -scurv
RANLIB = ranlib
CFLAGS = -Wall -g -DDEBUG
CXXFLAGS = -Wall -g
LDFLAGS =
SRCS = $(wildcard *.c)
OBJS = $(patsubst %.c,%.o,$(SRCS))
TARGETS = main
all : $(TARGETS)
$(TARGETS): $(OBJS)
$(CC) -o$@ $^ $(LDFLAGS)
clean :
@for dir in $(DIRS); \
do \
make -C $$dir clean; \
echo; \
done
rm -rf $(OBJS) $(TARGETS)
.PHONY: all clean
三、v4l2相关内容
https://linuxtv.org/downloads/v4l-dvb-apis/uapi/v4l/v4l2.htm
3.1 Querying Capabilities
对于大多数摄像头,需要关注V4L2_CAP_VIDEO_CAPTURE、V4L2_CAP_STREAMING两个Capabilities
3.1.1 Device Capabilities Flags
V4L2_CAP_VIDEO_CAPTURE |
0x00000001 | The device supports the single-planar API through the Video Capture interface. |
---|---|---|
V4L2_CAP_VIDEO_CAPTURE_MPLANE |
0x00001000 | The device supports the multi-planar API through the Video Capture interface. |
V4L2_CAP_VIDEO_OUTPUT |
0x00000002 | The device supports the single-planar API through the Video Output interface. |
V4L2_CAP_VIDEO_OUTPUT_MPLANE |
0x00002000 | The device supports the multi-planar API through the Video Output interface. |
V4L2_CAP_VIDEO_M2M |
0x00004000 | The device supports the single-planar API through the Video Memory-To-Memory interface. |
V4L2_CAP_VIDEO_M2M_MPLANE |
0x00008000 | The device supports the multi-planar API through the Video Memory-To-Memory interface. |
V4L2_CAP_VIDEO_OVERLAY |
0x00000004 | The device supports the Video Overlay interface. A video overlay device typically stores captured images directly in the video memory of a graphics card, with hardware clipping and scaling. |
V4L2_CAP_VBI_CAPTURE |
0x00000010 | The device supports the Raw VBI Capture interface, providing Teletext and Closed Caption data. |
V4L2_CAP_VBI_OUTPUT |
0x00000020 | The device supports the Raw VBI Output interface. |
V4L2_CAP_SLICED_VBI_CAPTURE |
0x00000040 | The device supports the Sliced VBI Capture interface. |
V4L2_CAP_SLICED_VBI_OUTPUT |
0x00000080 | The device supports the Sliced VBI Output interface. |
V4L2_CAP_RDS_CAPTURE |
0x00000100 | The device supports the RDS capture interface. |
V4L2_CAP_VIDEO_OUTPUT_OVERLAY |
0x00000200 | The device supports the Video Output Overlay (OSD) interface. Unlike the Video Overlay interface, this is a secondary function of video output devices and overlays an image onto an outgoing video signal. When the driver sets this flag, it must clear the V4L2_CAP_VIDEO_OVERLAY flag and vice versa. [1] |
V4L2_CAP_HW_FREQ_SEEK |
0x00000400 | The device supports the ioctl VIDIOC_S_HW_FREQ_SEEK ioctl for hardware frequency seeking. |
V4L2_CAP_RDS_OUTPUT |
0x00000800 | The device supports the RDS output interface. |
V4L2_CAP_TUNER |
0x00010000 | The device has some sort of tuner to receive RF-modulated video signals. For more information about tuner programming see Tuners and Modulators. |
V4L2_CAP_AUDIO |
0x00020000 | The device has audio inputs or outputs. It may or may not support audio recording or playback, in PCM or compressed formats. PCM audio support must be implemented as ALSA or OSS interface. For more information on audio inputs and outputs see Audio Inputs and Outputs. |
V4L2_CAP_RADIO |
0x00040000 | This is a radio receiver. |
V4L2_CAP_MODULATOR |
0x00080000 | The device has some sort of modulator to emit RF-modulated video/audio signals. For more information about modulator programming see Tuners and Modulators. |
V4L2_CAP_SDR_CAPTURE |
0x00100000 | The device supports the SDR Capture interface. |
V4L2_CAP_EXT_PIX_FORMAT |
0x00200000 | The device supports the struct v4l2_pix_format extended fields. |
V4L2_CAP_SDR_OUTPUT |
0x00400000 | The device supports the SDR Output interface. |
V4L2_CAP_META_CAPTURE |
0x00800000 | The device supports the Metadata Interface capture interface. |
V4L2_CAP_READWRITE |
0x01000000 | The device supports the read() and/or write() I/O methods. |
V4L2_CAP_ASYNCIO |
0x02000000 | The device supports the asynchronous I/O methods. |
V4L2_CAP_STREAMING |
0x04000000 | The device supports the streaming I/O method. |
V4L2_CAP_META_OUTPUT |
0x08000000 | The device supports the Metadata Interface output interface. |
V4L2_CAP_TOUCH |
0x10000000 | This is a touch device. |
V4L2_CAP_DEVICE_CAPS |
0x80000000 | The driver fills the device_caps field. This capability can only appear in the capabilities field and never in the device_caps field. |
3.1.2 The System Call API
/**
* struct v4l2_capability - Describes V4L2 device caps returned by VIDIOC_QUERYCAP
*
* @driver: name of the driver module (e.g. "bttv")
* @card: name of the card (e.g. "Hauppauge WinTV")
* @bus_info: name of the bus (e.g. "PCI:" + pci_name(pci_dev) )
* @version: KERNEL_VERSION
* @capabilities: capabilities of the physical device as a whole
* @device_caps: capabilities accessed via this particular device (node)
* @reserved: reserved fields for future extensions
*/
struct v4l2_capability {
__u8 driver[16];
__u8 card[32];
__u8 bus_info[32];
__u32 version;
__u32 capabilities;
__u32 device_caps;
__u32 reserved[3];
};
int ioctl(int fd, VIDIOC_QUERYCAP, struct v4l2_capability *argp;
On success 0 is returned,
on error -1 and the errno variable is set appropriately.
The generic error codes are described at the Generic Error Codes chapter