博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Linux下V4L2捕捉画面+H264压缩视频+帧缓冲显示视频————V4L2捕捉画面
阅读量:4693 次
发布时间:2019-06-09

本文共 14065 字,大约阅读时间需要 46 分钟。

V4L2打开摄像头主要步骤是

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

需要注意的是

  1. 分辨率、帧率不一定能达到自己想要的值
  2. 设置的参数最好再读出来确认一次
/* camera.h */#ifndef CAMERA_H#define CAMERA_H#include 
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 
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#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)\n", 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...\n", 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.\n", pDevName); goto ErrorHandle; } printf("Open device successful.\n"); /* 设置采集来源 */ 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.\n"); printf("Driver name: %s\n", Cap.driver); printf("Card name: %s\n", Cap.card); printf("Bus info: %s\n", 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:\n"); while(ioctl(sCameraPrivateData.DevFd, VIDIOC_ENUM_FMT, &FmtDesc) == 0){ FmtDesc.index++; printf("Pixel format = %c%c%c%c\n", (FmtDesc.pixelformat & 0xFF), ((FmtDesc.pixelformat >> 8) & 0xFF), ((FmtDesc.pixelformat >> 16) & 0xFF), (FmtDesc.pixelformat >> 24)); printf("Description = %s \n", FmtDesc.description); } /* 判断设备是否是可以捕获视频的设备 */ if(!(Cap.capabilities & V4L2_BUF_TYPE_VIDEO_CAPTURE)) { printf("The Current device is not a video capture device\n"); goto ErrorHandle; } /* 判断是否支持视频流 */ if(!(Cap.capabilities & V4L2_CAP_STREAMING)) { printf("The Current device does not support streaming i/o\n"); 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:\n"); printf("Capability: %u\n", StreamParam.parm.capture.capability); printf("Capturemode: %u\n", StreamParam.parm.capture.capturemode); printf("Extendedmode: %u\n", StreamParam.parm.capture.extendedmode); printf("Timeperframe denominator: %u\n", StreamParam.parm.capture.timeperframe.denominator); printf("Timeperframe numerator: %u\n", 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:\n"); printf("Capability: %u\n", StreamParam.parm.capture.capability); printf("Capturemode: %u\n", StreamParam.parm.capture.capturemode); printf("Extendedmode: %u\n", StreamParam.parm.capture.extendedmode); printf("Timeperframe denominator: %u\n", StreamParam.parm.capture.timeperframe.denominator); printf("Timeperframe numerator: %u\n", 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:\n"); // 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:\n"); printf("Width: %d, height: %d\n", TvFmt.fmt.pix.width, TvFmt.fmt.pix.height); printf("Pixel format = %c%c%c%c\n", (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\n", sCameraPrivateData.BufferCnt); sCameraPrivateData.pCameraBuffer = (sBufferType *)calloc(sCameraPrivateData.BufferCnt, sizeof(sBufferType)); if(sCameraPrivateData.pCameraBuffer == NULL) { printf("Malloc buffer failed\n"); 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\n"); 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\n", sCameraPrivateData.DevName); } else { printf("Skip close camera progress\n"); 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\n"); 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\n"); 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\n"); return NULL;}int CameraCaptureStart(void){ printf("Start capture thread\n"); if(!sCameraPrivateData.pCallback) { printf("Capture callback not set, start faild\n"); return -1; } if(sCameraPrivateData.State != CAMERA_SATTE_OPEN) { printf("Camera not open, start faild\n"); return -1; } sCameraPrivateData.State = CAMERA_SATTE_CAP; pthread_create(&sCameraPrivateData.CaptureThreadId, NULL, CameraCaptureThread, (void *)NULL); return 0;}int CameraCaptureStop(void){ printf("Stop capture thread\n"); sCameraPrivateData.State = CAMERA_SATTE_OPEN; pthread_join(sCameraPrivateData.CaptureThreadId, NULL); printf("Capture thread stop\n"); return 0;}

转载于:https://www.cnblogs.com/rootming/p/10853775.html

你可能感兴趣的文章
IO模型之阻塞IO
查看>>
go语言学习笔记2----变量、常量
查看>>
Datetime 使用详解
查看>>
Two Sum III - Data Structure Design
查看>>
c++ inline函数
查看>>
类对象作为类成员
查看>>
面向对象和面向过程的区别及优劣对比详解
查看>>
const与指针
查看>>
thsi指针的一些用法及作用
查看>>
c++友元
查看>>
c++运算符重载
查看>>
一元运算符重载
查看>>
c中的malloc函数
查看>>
c++继承中的对象模型
查看>>
c++中的new和delete
查看>>
c++继承中同名成员处理
查看>>
值传递,引用传递,指针传递
查看>>
浅拷贝和深拷贝
查看>>
结构体嵌套一个例子
查看>>
虚析构和纯虚析构
查看>>