zoukankan      html  css  js  c++  java
  • Linux下V4L2捕捉画面+H264压缩视频+帧缓冲显示视频————V4L2捕捉画面

    V4L2打开摄像头主要步骤是

    1. 打开设备文件, 比如/dev/video0
    2. 查询摄像头信息
    3. 设置摄像头参数, 如曝光、分辨率、帧率
    4. 映射内存

    需要注意的是

    1. 分辨率、帧率不一定能达到自己想要的值
    2. 设置的参数最好再读出来确认一次
    /* camera.h */
    #ifndef CAMERA_H
    #define CAMERA_H
    
    #include <stdint.h>
    
    typedef void (*vCameraFrameProcess)(uint8_t* pData, uint32_t Width, uint32_t Height, uint32_t Length) ;
    
    /**
     *  打开摄像头
     * @pDevName 设备节点
     *  返回值: 0成功 -1失败
     */
    int CameraOpen(const char* pDevName);
    
    /**
     * 关闭摄像头
     * 返回值: 0成功 -1失败
     */
    int CameraClose(void);
    
    /**
     * 设置捕获帧回调
     * 返回值: 0成功 -1失败
    */
    int CameraCaptureCallbackSet(vCameraFrameProcess pCallBack);
    
    /**
     * 开始捕获
     * 返回值: 0成功 -1失败
    */
    int CameraCaptureStart(void);
    
    /**
     * 停止捕获
     * 返回值: 0成功 -1失败
    */
    int CameraCaptureStop(void);
    
    #endif
    
    /* config.h */
    #ifndef CONFIG_H
    #define CONFIG_H
    
    #define CONFIG_CAPTURE_WIDTH		640
    #define CONFIG_CAPTURE_HEIGHT		480
    #define CONFIG_CAPTURE_FPS			30
    #define CONFIG_CAPTURE_BUF_CNT		10
    #define CONFIG_ENCODE_BITRATE		2500
    
    #define CONFIG_DEVNAME_LEN			32
    #define CONFIG_IOCTL_RETRY			5
    #define CONFIG_CAPTURE_DEVICE		"/dev/video0"
    #define CONFIG_DISPLAY_DEV			"/dev/fb0"
    
    #define CONFIG_QUEUE_SIZE			50
    #define CONFIG_QUEUE_LIMIT			2
    
    #endif
    
    /* camera.c */
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <errno.h>
    #include <fcntl.h>
    #include <unistd.h>
    #include <string.h>
    #include <sys/ioctl.h>
    #include <sys/mman.h>
    #include <assert.h>
    #include <linux/videodev2.h>
    #include <pthread.h>
    
    #include "config.h"
    #include "camera.h"
    
    static int Sign3 = 1;
    
    #define IOCTL_VIDEO(fd, req, value) ioctl(fd, req, value)
    
    typedef struct{  
    	void* pStart;
    	int Length;
    } sBufferType;
    
    typedef enum {
    	CAMERA_STATE_CLOSE,
    	CAMERA_SATTE_OPEN,
    	CAMERA_SATTE_CAP,
    	CAMERA_STATE_ERR
    } eCAMERA_STATE;
    
    static struct {
    	char 		DevName[CONFIG_DEVNAME_LEN];
    	int 		DevFd;
    	int 		BufferCnt;
    	uint32_t 	Width;
    	uint32_t 	Height;
    	sBufferType* pCameraBuffer;
    	vCameraFrameProcess pCallback;
    	eCAMERA_STATE State;							/* 0 未打开, 1打开摄像头, 2开始捕捉, 3错误 */
    	pthread_t	CaptureThreadId;
    } sCameraPrivateData;
    
    
    // static int xioctl(int Fd, int IOCTL_X, void *pArg)
    // {
    // 	int Ret = 0;
    // 	int Tries = CONFIG_IOCTL_RETRY;
    // 	do {
    // 		Ret = IOCTL_VIDEO(Fd, IOCTL_X, pArg);
    // 	} while(Ret && Tries-- && ((errno == EINTR) || (errno == EAGAIN) || (errno == ETIMEDOUT)));
    
    // 	if(Ret && (Tries <= 0)) fprintf(stderr, "ioctl (%i) retried %i times - giving up: %s)
    ", IOCTL_X, CONFIG_IOCTL_RETRY, strerror(errno));
    
    // 	return (Ret);
    // }
    
    
    /**
     * 存储设备名称
    */
    static int DevNameSet(const char* pDevName)
    {
    	int CopyLen = 0;
    	CopyLen = strlen(pDevName);
    	if(CopyLen > CONFIG_DEVNAME_LEN - 1) {
    		printf("Error: device name length over limit: %d", CopyLen);
    		return -1;
    	} 
    	memset(sCameraPrivateData.DevName, 0, CONFIG_DEVNAME_LEN);
    	memcpy(sCameraPrivateData.DevName, pDevName, CopyLen);
    	return 0;
    }
    
    /**
     * 重置摄像头信息
    */
    static void DevInfoReset(void)
    {
    	memset(sCameraPrivateData.DevName, 0, CONFIG_DEVNAME_LEN);
    	sCameraPrivateData.State = CAMERA_STATE_CLOSE;
    	sCameraPrivateData.DevFd = -1;
    	sCameraPrivateData.pCallback = NULL;
    	sCameraPrivateData.Width = 0;
    	sCameraPrivateData.Height = 0;
    }
    
    
    static inline int Yuv2Rgb(int Y, int U, int V)
    {
    	uint32_t Pixel32 = 0;
    	int R, G, B;
    	static long int Ruv, Guv, Buv;
    
    	if(Sign3) {
    		Sign3 = 0;
    		Ruv = 1159 * (V - 128);
    		Guv = -380 * (U - 128) + 813 * (V - 128);
    		Buv = 2018 * (U - 128);
    	}
    
    	int Delta = 1164 * (Y - 16);
    
    	R = (Delta + Ruv) / 1000;
    	G = (Delta - Guv) / 1000;
    	B = (Delta + Buv) / 1000;
    
    	R = R > 255 ? 255 : R < 0 ? 0 : R;
    	G = G > 255 ? 255 : G < 0 ? 0 : G;
    	B = B > 255 ? 255 : B < 0 ? 0 : B;
    
    	Pixel32 = (B & 0xFF) | (G & 0xFF) << 8 | (R & 0xFF) << 16;
    
    	return Pixel32;
    }
    
    int Yuyv2Rgb32(uint8_t* pYuv, uint8_t* pRgb, uint32_t Width, uint32_t Height)
    {
    	int y0, U, y1, V;
    	uint32_t Pixel32;
    	uint8_t* pPixel = (uint8_t *)&Pixel32;
    	// 分辨率描述像素点个数,而yuv2个像素点占有4个字符,所以这里计算总的字符个数,需要乘2
    	uint32_t Size = Width * Height * 2; 
    
    	for(uint32_t In = 0, Out = 0; In < Size; In += 4, Out += 8) {
    		y0 = pYuv[In + 0];
    		U  = pYuv[In + 1];
    		y1 = pYuv[In + 2];
    		V  = pYuv[In + 3];
    
    		Sign3 = 1;
    		Pixel32 = Yuv2Rgb(y0, U, V);
    		pRgb[Out + 0] = pPixel[0];   
    		pRgb[Out + 1] = pPixel[1];
    		pRgb[Out + 2] = pPixel[2];
    		pRgb[Out + 3] = 0;  //32位rgb多了一个保留位
    
    		Pixel32 = Yuv2Rgb(y1, U, V);
    		pRgb[Out + 4] = pPixel[0];
    		pRgb[Out + 5] = pPixel[1];
    		pRgb[Out + 6] = pPixel[2];
    		pRgb[Out + 7] = 0;
    
    	}
    	return 0;
    }
    
    int Yuyv2Rgb24(uint8_t* pYuv, uint8_t* pRgb, uint32_t Width, uint32_t Height)
    {
    	int y0, U, y1, V;
    	uint32_t Pixel32;
    	uint8_t* pPixel = (uint8_t *)&Pixel32;
    	// 分辨率描述像素点个数,而yuv2个像素点占有4个字符,所以这里计算总的字符个数,需要乘2
    	uint32_t Size = Width * Height * 2; 
    
    	for(uint32_t In = 0, Out = 0; In < Size; In += 4, Out += 6) {
    		y0 = pYuv[In + 0];
    		U  = pYuv[In + 1];
    		y1 = pYuv[In + 2];
    		V  = pYuv[In + 3];
    
    		Sign3 = 1;
    		Pixel32 = Yuv2Rgb(y0, U, V);
    		pRgb[Out + 0] = pPixel[0];   
    		pRgb[Out + 1] = pPixel[1];
    		pRgb[Out + 2] = pPixel[2];
    
    		Pixel32 = Yuv2Rgb(y1, U, V);
    		pRgb[Out + 3] = pPixel[0];
    		pRgb[Out + 4] = pPixel[1];
    		pRgb[Out + 5] = pPixel[2];
    
    	}
    	return 0;
    }
    
    
    int CameraOpen(const char* pDevName)
    {
    	int Ret = -1;
    
    	/* 检查摄像头是否已经打开或者在错误状态 */
    	printf("Open camera %s...
    ", pDevName);
    	if(sCameraPrivateData.State != CAMERA_STATE_CLOSE) {
    		printf("Error: camera was open or meet error, now state is: %d", sCameraPrivateData.State);
    		goto ErrorHandle;
    	}
    
    	/* 打开摄像头设备 */
    	if((sCameraPrivateData.DevFd = open(pDevName, O_RDWR, 0)) == -1) {
    	// if((sCameraPrivateData.DevFd = open(pDevName, O_RDWR | O_NONBLOCK, 0)) == -1) {
    		printf("Open camera %s faild.
    ", pDevName);
    		goto ErrorHandle;
    	}
    
    	printf("Open device successful.
    ");
    
    	/* 设置采集来源 */
    	struct v4l2_input Input;
    	Input.index = 0;
    
    	if (ioctl(sCameraPrivateData.DevFd, VIDIOC_S_INPUT, &Input)) {
    		perror("VIDIOC_S_INPUT");
    	}
    
    	/* 获取摄像头信息 */
    	struct v4l2_capability Cap;
    	
    	Ret = ioctl(sCameraPrivateData.DevFd, VIDIOC_QUERYCAP, &Cap);
    
    	if(Ret){
    		perror("Get device info error");
    		goto ErrorHandle;
    	}
    
    	printf("Get device info successful.
    ");
    
    	printf("Driver name: %s
    ", Cap.driver);
    	printf("Card name: %s
    ", Cap.card);
    	printf("Bus info: %s
    ", Cap.bus_info);
    
    	DevNameSet(pDevName);
    
    	struct v4l2_format TvFmt;
    	struct v4l2_fmtdesc FmtDesc;
    	struct v4l2_streamparm StreamParam;
    
    	memset(&FmtDesc, 0, sizeof(FmtDesc));
    	FmtDesc.index = 0;  
    	FmtDesc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    
    	/* 枚举支持的桢格式 */
    	printf("Support format:
    ");
    	while(ioctl(sCameraPrivateData.DevFd, VIDIOC_ENUM_FMT, &FmtDesc) == 0){  
    		FmtDesc.index++;
    		printf("Pixel format = %c%c%c%c
    ", 
    		(FmtDesc.pixelformat & 0xFF),
    		((FmtDesc.pixelformat >> 8) & 0xFF),
    		((FmtDesc.pixelformat >> 16) & 0xFF),
    		(FmtDesc.pixelformat >> 24));
    		printf("Description = %s 
    ", FmtDesc.description); 
    	}
    
    	/* 判断设备是否是可以捕获视频的设备 */
    	if(!(Cap.capabilities & V4L2_BUF_TYPE_VIDEO_CAPTURE)) {  
    		printf("The Current device is not a video capture device
    ");  
    		goto ErrorHandle;
    	}
    
    	/* 判断是否支持视频流 */
    	if(!(Cap.capabilities & V4L2_CAP_STREAMING)) {  
    		printf("The Current device does not support streaming i/o
    ");  
    		goto ErrorHandle;
    	}
    
    	/* 获取视频流信息 */
    	memset(&StreamParam, 0, sizeof(struct v4l2_streamparm));
    	StreamParam.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 
    	Ret = ioctl(sCameraPrivateData.DevFd, VIDIOC_G_PARM, &StreamParam);
    	if(Ret) {
    		perror("Get stream info failed");
    		goto ErrorHandle;
    	}
    
    	printf("Before setting stream params:
    ");
    	printf("Capability: %u
    ", StreamParam.parm.capture.capability);
    	printf("Capturemode: %u
    ", StreamParam.parm.capture.capturemode);
    	printf("Extendedmode: %u
    ", StreamParam.parm.capture.extendedmode);
    	printf("Timeperframe denominator: %u
    ", StreamParam.parm.capture.timeperframe.denominator);
    	printf("Timeperframe numerator: %u
    ", StreamParam.parm.capture.timeperframe.numerator);
    
    	/* 帧率分母 分子设置 */
    	StreamParam.parm.capture.timeperframe.denominator = CONFIG_CAPTURE_FPS;
    	StreamParam.parm.capture.timeperframe.numerator = 1;
    
    	if(ioctl(sCameraPrivateData.DevFd, VIDIOC_S_PARM, &StreamParam) == -1) {
    		perror("Unable to set fps");
    		goto ErrorHandle;
    	}
    
    	/* 获取视频流信息 */
    	Ret = ioctl(sCameraPrivateData.DevFd, VIDIOC_G_PARM, &StreamParam);
    	if(Ret) {
    		perror("Get stream info failed");
    		goto ErrorHandle;
    	}
    
    	printf("After setting stream params:
    ");
    	printf("Capability: %u
    ", StreamParam.parm.capture.capability);
    	printf("Capturemode: %u
    ", StreamParam.parm.capture.capturemode);
    	printf("Extendedmode: %u
    ", StreamParam.parm.capture.extendedmode);
    	printf("Timeperframe denominator: %u
    ", StreamParam.parm.capture.timeperframe.denominator);
    	printf("Timeperframe numerator: %u
    ", StreamParam.parm.capture.timeperframe.numerator);
    
    	// struct v4l2_control Ctrl;
    	// Ctrl.id = V4L2_CID_EXPOSURE_AUTO;
    	// Ret = ioctl(sCameraPrivateData.DevFd, VIDIOC_G_CTRL, &Ctrl);
    
    	// if(Ret) {
    	// 	perror("Get exposure info failed");
    	// 	goto ErrorHandle;
    	// }
    
    	// printf("Before setting control params:
    ");
    	// printf("Controll value: %#X", Ctrl.value);
    
    	/* 设置自动曝光 */
    	struct v4l2_control Ctrl;
    	Ctrl.id = V4L2_CID_EXPOSURE_ABSOLUTE;
    	Ret = ioctl(sCameraPrivateData.DevFd, VIDIOC_G_CTRL, &Ctrl);
    
    	if(Ret) {
    		perror("Set exposure failed");
    		// goto ErrorHandle;
    	}
    
    	/* 设置捕获格式 */
    	TvFmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    	TvFmt.fmt.pix.width = CONFIG_CAPTURE_WIDTH;
    	TvFmt.fmt.pix.height = CONFIG_CAPTURE_HEIGHT;
    	TvFmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
    	TvFmt.fmt.pix.field = V4L2_FIELD_INTERLACED;
    
    	if (ioctl(sCameraPrivateData.DevFd, VIDIOC_S_FMT, &TvFmt) < 0) {  
    		perror("Unable set VIDIOC_S_FMT");
    		goto ErrorHandle;
    	}
    
    	if (ioctl(sCameraPrivateData.DevFd, VIDIOC_G_FMT, &TvFmt) < 0) {  
    		perror("Get VIDIOC_S_FMT error");
    		goto ErrorHandle;
    	}
    
    	printf("After setting frame params:
    ");
    	printf("Width: %d, height: %d
    ", TvFmt.fmt.pix.width, TvFmt.fmt.pix.height);
    		printf("Pixel format = %c%c%c%c
    ", 
    		(TvFmt.fmt.pix.pixelformat & 0xFF),
    		((TvFmt.fmt.pix.pixelformat >> 8) & 0xFF),
    		((TvFmt.fmt.pix.pixelformat >> 16) & 0xFF),
    		(TvFmt.fmt.pix.pixelformat >> 24));
    
    	sCameraPrivateData.Width = TvFmt.fmt.pix.width;
    	sCameraPrivateData.Height = TvFmt.fmt.pix.height;
    
    	/* 请求V4L2驱动申请内存 */
    	struct v4l2_requestbuffers ReqBuffer;
    	memset(&ReqBuffer, 0, sizeof(ReqBuffer));
    	ReqBuffer.count = CONFIG_CAPTURE_BUF_CNT;
    	ReqBuffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    	ReqBuffer.memory = V4L2_MEMORY_MMAP;  
    
    	if(-1 == ioctl(sCameraPrivateData.DevFd, VIDIOC_REQBUFS, &ReqBuffer)){  
    		perror("Fail to ioctl 'VIDIOC_REQBUFS'");
    		goto ErrorHandle;
    	}
    
    	sCameraPrivateData.BufferCnt = ReqBuffer.count;  
    	printf("Buffer count: %d
    ", sCameraPrivateData.BufferCnt);
    
    	sCameraPrivateData.pCameraBuffer = (sBufferType *)calloc(sCameraPrivateData.BufferCnt, sizeof(sBufferType));
    	if(sCameraPrivateData.pCameraBuffer == NULL) {
    		printf("Malloc buffer failed
    ");
    		goto ErrorHandle;
    	}
    
    	/* 映射内核缓存到用户空间 */
    	struct v4l2_buffer Buf;  
    	for(int i = 0; i < sCameraPrivateData.BufferCnt; i++) {
    		memset(&Buf, 0, sizeof(Buf));  
    		Buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    		Buf.memory = V4L2_MEMORY_MMAP;
    		Buf.index = i;
    
    		if(-1 == ioctl(sCameraPrivateData.DevFd, VIDIOC_QUERYBUF, &Buf)) {
    			perror("Fail to ioctl : VIDIOC_QUERYBUF");
    			goto ErrorHandle;
    		}
    
    		sCameraPrivateData.pCameraBuffer[i].Length = Buf.length;
    		sCameraPrivateData.pCameraBuffer[i].pStart = 
    			mmap(NULL, Buf.length, PROT_READ | PROT_WRITE, MAP_SHARED, sCameraPrivateData.DevFd, Buf.m.offset);
    
    		if(MAP_FAILED == sCameraPrivateData.pCameraBuffer[i].pStart) {
    			perror("Fail to mmap");
    			goto ErrorHandle;
    		}
    
    		if(ioctl(sCameraPrivateData.DevFd, VIDIOC_QBUF, &Buf)) {
    			perror("Unable to queue buffer");
    			goto ErrorHandle;;
    		}
    	}
    
    	/* 开始捕获 */
    	enum v4l2_buf_type BufType;
    	BufType = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    	if(ioctl(sCameraPrivateData.DevFd, VIDIOC_STREAMON, &BufType) < 0) {
    		perror("VIDIOC_STREAMON error");
    		goto ErrorHandle;
    	}
    	sCameraPrivateData.State = CAMERA_SATTE_OPEN;
    
    	return 0;
    
    ErrorHandle:
    	printf("Camera open meet error, now handle it
    ");
    	close(sCameraPrivateData.DevFd);
    	sCameraPrivateData.State = CAMERA_STATE_CLOSE;
    	free(sCameraPrivateData.pCameraBuffer);
    	sCameraPrivateData.pCameraBuffer = NULL;
    	return -1;
    }
    
    int CameraClose(void)
    {
    	if(sCameraPrivateData.State == CAMERA_SATTE_OPEN || sCameraPrivateData.State == CAMERA_SATTE_CAP) {
    		printf("Close camera %s
    ", sCameraPrivateData.DevName);
    	} else {
    		printf("Skip close camera progress
    ");
    		return 0;
    	}
    	
    	for(int i = 0; i < CONFIG_CAPTURE_BUF_CNT; i++) {
    		
    		munmap(sCameraPrivateData.pCameraBuffer[i].pStart, 
    				sCameraPrivateData.pCameraBuffer[i].Length);
    		sCameraPrivateData.pCameraBuffer[i].Length = 0;
    	}
    
    	free(sCameraPrivateData.pCameraBuffer);
    	sCameraPrivateData.pCameraBuffer = NULL;
    
    	int Off = 1;
    
    	if(ioctl(sCameraPrivateData.DevFd , VIDIOC_STREAMOFF, &Off))  {
    		perror("Stop camera fail");
    		return -1 ;
    	}
    
    	close(sCameraPrivateData.DevFd);
    
    	DevInfoReset();
    
    	printf("Camera closed
    ");
    
    	return 0;
    }
    
    int CameraCaptureCallbackSet(vCameraFrameProcess pCallback)
    {
    	if(sCameraPrivateData.State == CAMERA_SATTE_CAP) {
    		printf("Set capture callback failed");
    		return -1;
    	}
    	sCameraPrivateData.pCallback = pCallback;
    	return 0;
    }
    
    static void* CameraCaptureThread(void* pParam)
    {
    	int Ret = -1;
    	
    	struct v4l2_buffer EnQueueBuf;
    	struct v4l2_buffer DeQueueBuf;
    
    	printf("Start capture
    ");
    
    	while(sCameraPrivateData.State == CAMERA_SATTE_CAP) {
    
    		memset(&DeQueueBuf, 0, sizeof(struct v4l2_buffer));
    		DeQueueBuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    		DeQueueBuf.memory = V4L2_MEMORY_MMAP;
    
    		memset(&EnQueueBuf, 0, sizeof(struct v4l2_buffer));
    		EnQueueBuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    		EnQueueBuf.memory = V4L2_MEMORY_MMAP;
    
    		// 取出队列中的数据
    		Ret = ioctl(sCameraPrivateData.DevFd, VIDIOC_DQBUF, &DeQueueBuf);
    		if(Ret) {
    			perror("Dequeue fail");
    			break;
    		}
    		sCameraPrivateData.pCallback(sCameraPrivateData.pCameraBuffer[DeQueueBuf.index].pStart, 
    										sCameraPrivateData.Width,
    										sCameraPrivateData.Height,
    										sCameraPrivateData.pCameraBuffer[DeQueueBuf.index].Length);
    
    		// 重新入队列
    		EnQueueBuf.index = DeQueueBuf.index;
    
    		if(ioctl(sCameraPrivateData.DevFd , VIDIOC_QBUF , &EnQueueBuf)) {
    			perror("Enqueue fail");
    			break;
    		}
    	}
    
    	printf("Stop capture
    ");
    
    	return NULL;
    }
    
    int CameraCaptureStart(void)
    {
    	printf("Start capture thread
    ");
    	if(!sCameraPrivateData.pCallback) {
    		printf("Capture callback not set, start faild
    ");
    		return -1;
    	}
    	if(sCameraPrivateData.State != CAMERA_SATTE_OPEN) {
    		printf("Camera not open, start faild
    ");
    		return -1;
    	}
    	sCameraPrivateData.State = CAMERA_SATTE_CAP;
    
    	pthread_create(&sCameraPrivateData.CaptureThreadId, NULL, CameraCaptureThread, (void *)NULL);
    
    	return 0;
    }
    
    int CameraCaptureStop(void)
    {
    	printf("Stop capture thread
    ");
    	sCameraPrivateData.State = CAMERA_SATTE_OPEN;
    	pthread_join(sCameraPrivateData.CaptureThreadId, NULL);
    	printf("Capture thread stop
    ");
    	return 0;
    }
    
  • 相关阅读:
    http协议的状态码——400,401,403,404,500,502,503,301,302等常见网页错误代码
    JS中的动态合集与静态合集
    对联
    诗词
    文言文
    Youth Is Not a Time of Life
    JS探秘——那些你理解存在偏差的问题
    JS中的加号+运算符详解
    支持HTTP2协议
    银行卡信息查询接口
  • 原文地址:https://www.cnblogs.com/rootming/p/10853775.html
Copyright © 2011-2022 走看看