H264网络摄像头(一)

    网上知识点杂,比较乱,这里整理一下,也开始学习linux下获取摄像头数据,然后(s5pv210)h264硬编码,再封装成rtp包,使用rtsp传输。

 

一、v4l2视频采集流程

1. 打开设备文件。

    int fd=open(”/dev/video0″,O_RDWR);

2. 取得设备的capability,看看设备具有什么功能,比如是否具有视频输入,或者音频输入输出等。

    VIDIOC_QUERYCAP,struct v4l2_capability

3. 选择视频输入,一个视频设备可以有多个视频输入。

    VIDIOC_S_INPUT,struct v4l2_input

4. 设置视频的制式和帧格式,制式包括PAL,NTSC,帧的格式个包括宽度和高度等。

    VIDIOC_S_STD,VIDIOC_S_FMT,struct v4l2_std_id,struct v4l2_format

5. 向驱动申请帧缓冲,一般不超过5个。

    struct v4l2_requestbuffers

6. 将申请到的帧缓冲映射到用户空间,这样就可以直接操作采集到的帧了,而不必去复制。

    mmap

7. 将申请到的帧缓冲全部入队列,以便存放采集到的数据.

    VIDIOC_QBUF,struct v4l2_buffer

8. 开始视频的采集。

    VIDIOC_STREAMON

9. 出队列以取得已采集数据的帧缓冲,取得原始采集数据。

    VIDIOC_DQBUF

10. 将缓冲重新入队列尾,这样可以循环采集。

    VIDIOC_QBUF

11. 停止视频的采集。

    VIDIOC_STREAMOFF

12. 关闭视频设备。

    close(fd);


下面的代码是从video0下获取一帧数据,(前提是摄像头支持mjpeg输出,否则若输出为yuv,要先jpeg压缩,这里暂不做)并保存为jpeg图片。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h> 
#include <errno.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <asm/types.h>        
#include <linux/videodev2.h>

static int do_save(const unsigned char *buf, const int len)
{
	if(!buf)
		return -EINVAL;

	int fd = -1;

	fd = open("cap.jpeg", O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
	if(fd < 0) {
		perror("open cap.jpeg");
		return -1;
	}

	if(write(fd, buf, len) < 0) {
		perror("write to cap.jpeg");
	}
	
	close(fd);
	return 0;
}

#define NB_BUFFER 4

static const char* dev = "/dev/video0";
int devfd;

int main(void)
{
	int ret = 0;
	struct v4l2_capability cap;
	struct v4l2_format fmt;
	int width = 640;
	int height = 480;
	struct v4l2_requestbuffers rb;
	int i = 0;
	struct v4l2_buffer buf;
	void *mem[NB_BUFFER];
	int memlength[NB_BUFFER];
	int type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
	unsigned char *tmpbuffer;
	int tmpbytesused = 0;

	/* 1. open */
	devfd = open(dev, O_RDWR);
	if(devfd == -1) {
        perror("ERROR opening V4L interface");
        return -1;
    }

	/* 2. get capability */
	memset(&cap, 0, sizeof(struct v4l2_capability));
    ret = ioctl(devfd, VIDIOC_QUERYCAP, &cap);
    if(ret < 0) {
        fprintf(stderr, "Error opening %s: unable to query device.\n", dev);
		goto end;
    }

	if((cap.capabilities & V4L2_CAP_VIDEO_CAPTURE) == 0) {
        fprintf(stderr, "Error opening %s: video capture not supported.\n", dev);
        goto end;
    }

	if(!(cap.capabilities & V4L2_CAP_STREAMING)) {
        fprintf(stderr, "%s does not support streaming i/o\n", dev);
		goto end;
    }

	/* 3. set input */
	
	/* 4. set format */
    memset(&fmt, 0, sizeof(struct v4l2_format));
    fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    fmt.fmt.pix.width = width;
    fmt.fmt.pix.height = height;
    fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_MJPEG;/* V4L2_PIX_FMT_YUYV */
    fmt.fmt.pix.field = V4L2_FIELD_ANY;
    ret = ioctl(devfd, VIDIOC_S_FMT, &fmt);
    if(ret < 0) {
        fprintf(stderr, "Unable to set format: MJPEG res: %dx%d\n", width, height); //FIXME check the format availablility here!
        goto end;
    }

	/* 5. request buffers */
    memset(&rb, 0, sizeof(struct v4l2_requestbuffers));
    rb.count = NB_BUFFER;
    rb.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    rb.memory = V4L2_MEMORY_MMAP;
    ret = ioctl(devfd, VIDIOC_REQBUFS, &rb);
    if(ret < 0) {
        perror("Unable to allocate buffers");
        goto end;
    }

    /* 6. map buffers */
    for(i = 0; i < NB_BUFFER; i++) {
		memset(&buf, 0, sizeof(struct v4l2_buffer));
		buf.index = i;
		buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
		buf.memory = V4L2_MEMORY_MMAP;
		ret = ioctl(devfd, VIDIOC_QUERYBUF, &buf);
		if(ret < 0) {
			perror("Unable to query buffer");
			goto end;
		}

		fprintf(stderr, "length: %u offset: %u\n", buf.length, buf.m.offset);

		mem[i] = mmap(0 /* start anywhere */ ,
						buf.length, PROT_READ | PROT_WRITE, MAP_SHARED,
						devfd,
						buf.m.offset);
		if(mem[i] == MAP_FAILED) {
			perror("Unable to map buffer");
			mem[i] = NULL;
			goto end;
		}

		memlength[i] = buf.length;
		fprintf(stderr, "Buffer mapped at address %p.\n", mem[i]);
    }

    /* 7. queue buffers */
    for(i = 0; i < NB_BUFFER; ++i) {
        memset(&buf, 0, sizeof(struct v4l2_buffer));
        buf.index = i;
        buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
        buf.memory = V4L2_MEMORY_MMAP;
        ret = ioctl(devfd, VIDIOC_QBUF, &buf);
        if(ret < 0) {
            perror("Unable to queue buffer");
            goto end;
        }
    }

	/* 8. stream on */
    
    ret = ioctl(devfd, VIDIOC_STREAMON, &type);
    if(ret < 0) {
        perror("Unable to start capture");
        goto end_unmap;
    }

	/* 9. dqbuf */
	type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
	memset(&buf, 0, sizeof(struct v4l2_buffer));
	buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
	buf.memory = V4L2_MEMORY_MMAP;

	ret = ioctl(devfd, VIDIOC_DQBUF, &buf);
	if(ret < 0) {
		perror("Unable to dequeue buffer");
		goto streamoff;
	}
	
	tmpbuffer = (unsigned char *) malloc((size_t)(width * height << 1));	
	memcpy(tmpbuffer, mem[buf.index], buf.bytesused);
	tmpbytesused = buf.bytesused;

	/* 10. qbuf */	
	ret = ioctl(devfd, VIDIOC_QBUF, &buf);
    if(ret < 0) {
        perror("Unable to requeue buffer");
        goto free;
    }

	do_save(tmpbuffer, tmpbytesused);
	
free:
	free(tmpbuffer);
streamoff:
	/* 11. stream off */
	type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    ret = ioctl(devfd, VIDIOC_STREAMOFF, &type);
    if(ret != 0)
        perror("Unable to stop capture");
end_unmap:
	for(i = 0; i < NB_BUFFER; i++)
		if(mem[i] != NULL)
			munmap(mem[i], memlength[i]);
end:
	/* 12. close */
	close(devfd);
	return ret;
}

    linux下编译运行,得到cap.jpeg,可直接预览。

你可能感兴趣的:(H264网络摄像头(一))