rk3399_arm_lvds/capimage/gvideo.cpp

349 lines
7.5 KiB
C++
Raw Permalink Normal View History

2024-03-05 03:46:18 +00:00
#include "gvideo.h"
#include <iostream>
#include <fcntl.h> /* low-level i/o */
#include <unistd.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <sys/ioctl.h>
#include <dlfcn.h>
#include <linux/videodev2.h>
#include "linux/v4l2-subdev.h"
#include <cstring>
#include <poll.h>
#include <malloc.h>
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<std::mutex> lck(m_lock);
if (bStart)
return;
start_capturing();
bStart = true;
}
void gVideo::stop()
{
std::lock_guard<std::mutex> 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());
}