#include "gvideo.h" #include #include /* low-level i/o */ #include #include #include #include #include #include #include "linux/v4l2-subdev.h" #include #include #include static const std::string loggername = "gVideo"; #define LOG_TRACE(...) #define LOG_ERROR printf #define LOG_INFO(...) using namespace std; #define CLEAR(x) memset(&(x), 0, sizeof(x)) #define LOG printf int file_size(const char *filename) { struct stat statbuf; int ret = stat(filename, &statbuf); int size = -1; if (ret == 0) size = statbuf.st_size; return size; } gVideo::gVideo() : fd(-1), buffers(0), bStart(false), dev_name("/dev/video3") { } gVideo::~gVideo() { close(); } void gVideo::set_buf_count(int count) { v4l_buffer_count = count; } bool gVideo::is_open() { return fd >= 0; } void gVideo::close() { if (is_open()) { stop(); uninit_device(); close_device(); } } void gVideo::open(int width, int height) { if (is_open()) return; open_device(); if (is_open()) { // init_device(); set_size(width, height); start(); } } void gVideo::start() { std::lock_guard lck(m_lock); if (bStart) return; start_capturing(); bStart = true; } void gVideo::stop() { std::lock_guard lck(m_lock); if (bStart) { stop_capturing(); bStart = false; } } void gVideo::grab(int color, bool bcrrect, int timeout, int flatpara) { } bool gVideo::hasframe() { return wait(0); } bool gVideo::wait(int msTimeout) { pollfd pfd; pfd.fd = this->fd; pfd.events = POLLIN | POLLRDNORM; if (poll(&pfd, 1, msTimeout) > 0) return pfd.revents; return false; } void *gVideo::read_frame(int timeout) { if (!wait(timeout)) { LOG_TRACE("read frame time out"); return 0; } v4l2_buffer buf; buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; buf.memory = V4L2_MEMORY_MMAP; if (-1 == ioctl(fd, VIDIOC_DQBUF, &buf)) { LOG_ERROR("VIDIOC_DQBUF error!!! index :%d\n", buf.index); return 0; } if (-1 == ioctl(fd, VIDIOC_QBUF, &buf)) { LOG_ERROR("VIDIOC_QBUF error!!! index :%d\n", buf.index); return 0; } return buffers[buf.index].start; } void gVideo::set_size(int width, int height) { if (!buffers.empty()) uninit_device(); v4l_width = width; v4l_height = height; // v4l_buffer_count = (10*1024*1024) / (v4l_width*v4l_height*3); // v4l_buffer_count = std::max(3, std::min(v4l_buffer_count, 40)); /* to-do */ printf("v4l buffer count %d\n", v4l_buffer_count); init_device(); } void gVideo::stop_capturing(void) { enum v4l2_buf_type type; type = V4L2_BUF_TYPE_VIDEO_CAPTURE; if (-1 == ioctl(fd, VIDIOC_STREAMOFF, &type)) LOG_ERROR("streamo off"); } unsigned int querybuf_length; void gVideo::start_capturing(void) { enum v4l2_buf_type type; LOG_INFO("start_capturing::querybuf_length = %d.\n", querybuf_length); for (int i = 0; i < n_buffers; ++i) { struct v4l2_buffer buf; CLEAR(buf); buf.index = i; buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; buf.memory = V4L2_MEMORY_MMAP; if (-1 == ioctl(fd, VIDIOC_QBUF, &buf)) LOG_ERROR("VIDIOC_QBUF Error %d", i); else LOG_INFO("VIDIOC_QBUF %d\n", i); } type = V4L2_BUF_TYPE_VIDEO_CAPTURE; if (-1 == ioctl(fd, VIDIOC_STREAMON, &type)) LOG_ERROR("VIDIOC_STREAMON"); } void gVideo::uninit_device(void) { if (!buffers.empty()) { for (int i = 0; i < buffers.size(); ++i) if (-1 == munmap(buffers[i].start, buffers[i].length)) LOG_ERROR("munmap %d error\n", i); struct v4l2_requestbuffers req; CLEAR(req); req.count = 0; req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; req.memory = V4L2_MEMORY_MMAP; if (-1 == ioctl(fd, VIDIOC_REQBUFS, &req)) LOG_ERROR("uinit_device VIDIOC_REQBUFS fail\n"); buffers.clear(); } } void gVideo::init_mmap(void) { struct v4l2_requestbuffers req; int ret; unsigned int length; unsigned int offset; CLEAR(req); req.count = v4l_buffer_count; req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; req.memory = V4L2_MEMORY_MMAP; LOG_INFO("set v4l_buffer_count = %d\n", req.count); LOG_INFO("set v4l_buffer_type = %d\n", req.type); LOG_INFO("set v4l_buffer_memory = %d\n", req.memory); ret = ioctl(fd, VIDIOC_REQBUFS, &req); if (ret < 0) { LOG_ERROR("Unable to request buffers: %s (%d).\n", strerror(errno), errno); } else { LOG_INFO("%s VIDIOC_REQBUFS sucess\n", dev_name.c_str()); } LOG_INFO("%u buffers requested.\n", req.count); buffers.resize(req.count); if (buffers.empty()) LOG_ERROR("Out of memory\n"); // 7.VIDIOC_QUERYBUF for (n_buffers = 0; n_buffers < req.count; ++n_buffers) { const char *ts_type, *ts_source; struct v4l2_buffer buf; CLEAR(buf); buf.index = n_buffers; buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; buf.memory = V4L2_MEMORY_MMAP; ret = ioctl(fd, VIDIOC_QUERYBUF, &buf); if (ret < 0) LOG_ERROR("Unable to query buffer %u: %s (%d).\n", n_buffers, strerror(errno), errno); else LOG_INFO("index %d VIDIOC_QUERYBUF sucess.\n", n_buffers); // mmap unsigned int length; unsigned int offset; length = buf.length; offset = buf.m.offset; LOG_INFO("Buffer %u length = %d.\n", n_buffers, length); LOG_INFO("Buffer %u offset = %d.\n", n_buffers, offset); buffers[n_buffers].length = length; buffers[n_buffers].start = mmap(NULL /* start anywhere */, length, PROT_READ | PROT_WRITE /* required */, MAP_SHARED /* recommended */, fd, offset); if (buffers[n_buffers].start == MAP_FAILED) LOG_ERROR("Unable to map buffer %u: %s (%d)\n", n_buffers, strerror(errno), errno); else LOG_INFO("Buffer %u mapped at address %p.\n", n_buffers, buffers[n_buffers].start); } } void printCamInfo(int fd) { // 获取摄像头所支持的分辨率 enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE; struct v4l2_fmtdesc fmt_1; struct v4l2_frmsizeenum frmsize; struct v4l2_frmivalenum frmival; fmt_1.index = 0; fmt_1.type = type; while (ioctl(fd, VIDIOC_ENUM_FMT, &fmt_1) >= 0) { frmsize.pixel_format = fmt_1.pixelformat; frmsize.index = 0; while (ioctl(fd, VIDIOC_ENUM_FRAMESIZES, &frmsize) >= 0) { // if(frmsize.type == V4L2_FRMSIZE_TYPE_DISCRETE) // LOG_INFO("line:%d %dx%d", __LINE__, frmsize.discrete.width, frmsize.discrete.height); // else if (frmsize.type == V4L2_FRMSIZE_TYPE_STEPWISE) // LOG_INFO("line:%d %dx%d", __LINE__, frmsize.discrete.width, frmsize.discrete.height); frmsize.index++; } fmt_1.index++; } } void gVideo::init_device(void) { struct v4l2_capability cap; struct v4l2_format fmt; printCamInfo(fd); if (-1 == ioctl(fd, VIDIOC_QUERYCAP, &cap)) { if (EINVAL == errno) LOG_ERROR("%s is no V4L2 device", dev_name.c_str()); else LOG_ERROR("VIDIOC_QUERYCAP"); } if (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE)) LOG_ERROR("%s is no video capture device", dev_name.c_str()); if (!(cap.capabilities & V4L2_CAP_STREAMING)) LOG_ERROR("%s does not support streaming i/o", dev_name.c_str()); CLEAR(fmt); fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; fmt.fmt.pix.width = v4l_width; fmt.fmt.pix.height = v4l_height; // LOG_TRACE("v4l2 init_device width:%d, height:%d\n", fmt.fmt.pix.width, fmt.fmt.pix.height); fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_RGB24; fmt.fmt.pix.field = V4L2_FIELD_INTERLACED; if (-1 == ioctl(fd, VIDIOC_S_FMT, &fmt)) LOG_ERROR("VIDIOC_S_FMT error"); init_mmap(); } void gVideo::close_device(void) { if (-1 == ::close(fd)) LOG_ERROR("close"); fd = -1; } void gVideo::open_device(void) { if ((fd = ::open(dev_name.c_str(), O_RDWR /* required */ /*| O_NONBLOCK*/, 0)) == -1) LOG_ERROR("Cannot open %s", dev_name.c_str()); }