349 lines
7.5 KiB
C++
349 lines
7.5 KiB
C++
|
#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());
|
||
|
}
|