tx-gxx-linux/device/gxx-linux/capimage/gvideo.cpp

452 lines
11 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#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>
#include "applog.h"
#include "stringex.hpp"
static const std::string loggername = "gVideo";
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")
{
LOG_INIT();
}
gVideo::~gVideo()
{
stop();
close();
}
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_USERPTR;
if (-1 == ioctl(fd, VIDIOC_DQBUF, &buf)) {
LOG_ERROR(string_format("VIDIOC_DQBUF error!!! index :%d\n", buf.index));
return 0;
}
if (-1 == ioctl(fd, VIDIOC_QBUF, &buf)) {
LOG_ERROR(string_format("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;
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;
printf("start_capturing::querybuf_length = %d.\n",querybuf_length);
for (int i = 0; i < n_buffers; ++i)
{
struct v4l2_buffer buf;
struct v4l2_plane planes[VIDEO_MAX_PLANES];
CLEAR(buf);
CLEAR(planes);
for(int j = 0; j < VIDEO_MAX_PLANES; j++)
{
//planes[j].length = buffers[i].length;
planes[j].length = querybuf_length;
planes[j].m.userptr =(unsigned long)buffers[i].start;
}
buf.index = i;
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
buf.memory = V4L2_MEMORY_USERPTR;//V4L2_MEMORY_MMAP;
buf.m.planes = planes;
buf.length = 1;
if (-1 == ioctl(fd, VIDIOC_QBUF, &buf))
LOG_ERROR(string_format("VIDIOC_QBUF Error %d", i));
else
LOG_ERROR(string_format("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())
{
struct v4l2_requestbuffers req;
CLEAR(req);
req.count = 0;
LOG_TRACE(string_format("frame count: %d", req.count));
req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
req.memory = V4L2_MEMORY_USERPTR;
if (-1 == ioctl(fd, VIDIOC_REQBUFS, &req))
{
if (EINVAL == errno)
LOG_ERROR("%s does not support user pointer i/o");
else
LOG_ERROR("VIDIOC_REQBUFS");
}
for (int i = 0; i < n_buffers; ++i)
free(buffers[i].start);
buffers.clear();
}
}
void gVideo::init_mmap(void)
{
struct v4l2_requestbuffers req;
int ret;
unsigned int length;
unsigned int offset;
unsigned int page_size;
int buffer_size = v4l_width * v4l_height * 3;
printf("init_mmap::buduiqi:changdu: buffer_size= %d\n", buffer_size);
page_size = getpagesize();
buffer_size = (buffer_size + page_size - 1) & ~(page_size - 1);
printf("init_mmap::shijichangdu:v4l_width = %d,v4l_height = %d\n", v4l_width,v4l_height);
printf("init_mmap::duiqi:page_size = %d,buffer_size = %d\n", page_size,buffer_size);
CLEAR(req);
//6.VIDIOC_REQBUFS
req.count = v4l_buffer_count;
//req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
//req.memory = V4L2_MEMORY_MMAP;
req.memory = V4L2_MEMORY_USERPTR;
printf("set v4l_buffer_count = %d\n", req.count);
printf("set v4l_buffer_type = %d\n", req.type);
printf("set v4l_buffer_memory = %d\n", req.memory);
ret = ioctl(fd, VIDIOC_REQBUFS, &req);
if (ret < 0) {
printf("Unable to request buffers: %s (%d).\n", strerror(errno),errno);
}
else {
printf("%s VIDIOC_REQBUFS sucess\n",dev_name.c_str());
}
printf("%u buffers requested.\n", req.count);
buffers.resize(req.count);
if (buffers.empty()) {
printf("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;
struct v4l2_plane planes[VIDEO_MAX_PLANES];
CLEAR(buf);
CLEAR(planes);
buf.index = n_buffers;
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
//buf.memory = V4L2_MEMORY_MMAP;
buf.memory = V4L2_MEMORY_USERPTR;
buf.length = VIDEO_MAX_PLANES;
buf.m.planes = planes;
ret = ioctl(fd, VIDIOC_QUERYBUF, &buf);
if (ret < 0) {
printf("Unable to query buffer %u: %s (%d).\n", n_buffers,strerror(errno), errno);
}
else {
printf("index %d VIDIOC_QUERYBUF sucess.\n",n_buffers);
}
//get_ts_flags(buf.flags, &ts_type, &ts_source);
//printf("length: %u offset: %u timestamp type/source: %s/%s\n",
// buf.length, buf.m.offset, ts_type, ts_source);
//mmap
unsigned int length;
unsigned int offset;
length = buf.m.planes[0].length;
offset = buf.m.planes[0].m.mem_offset;
querybuf_length = buf.m.planes[0].length;
printf("VIDIOC_QUERYBUFBuffer %u length = %d.\n",n_buffers,length);
printf("VIDIOC_QUERYBUFBuffer %u offset = %d.\n",n_buffers,offset);
printf("VIDIOC_QUERYBUFBuffer %u querybuf_length = %d.\n",n_buffers,querybuf_length);
}
for (n_buffers = 0; n_buffers < buffers.size(); ++n_buffers)
{
buffers[n_buffers].length = querybuf_length;
buffers[n_buffers].start = memalign(/* boundary */ 1024, querybuf_length);
printf("memalign:buffers[%d].length = %d\n", n_buffers,buffers[n_buffers].length);
printf("memalign:buffers[%d].start = %p\n", n_buffers,buffers[n_buffers].start);
if (!buffers[n_buffers].start)
{
fprintf(stderr, "Out of memory\n");
exit(EXIT_FAILURE);
}
}
// unsigned int page_size;
// int buffer_size = v4l_width * v4l_height * 3;
// page_size = getpagesize();
// buffer_size = (buffer_size + page_size - 1) & ~(page_size - 1);
// struct v4l2_requestbuffers req;
// CLEAR(req);
// req.count = v4l_buffer_count;
// LOG_TRACE(string_format("frame count: %d", req.count));
// req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
// req.memory = V4L2_MEMORY_USERPTR;
// if (-1 == ioctl(fd, VIDIOC_REQBUFS, &req))
// {
// if (EINVAL == errno)
// LOG_ERROR("%s does not support user pointer i/o");
// else
// LOG_ERROR("VIDIOC_REQBUFS");
// }
// buffers.resize(req.count);
// for (n_buffers = 0; n_buffers < buffers.size(); ++n_buffers)
// {
// buffers[n_buffers].length = buffer_size;
// buffers[n_buffers].start = memalign(/* boundary */ page_size, buffer_size);
// if (!buffers[n_buffers].start)
// LOG_ERROR("Out of memory");
// }
// struct v4l2_requestbuffers req;
// CLEAR(req);
// req.count = v4l_buffer_count;
// LOG("frame count: %d \n",req.count);
// req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
// req.memory = V4L2_MEMORY_MMAP;
// if (-1 == ioctl(fd, VIDIOC_REQBUFS, &req)) {
// if (EINVAL == errno) {
// LOG("%s does not support " "memory mapping\n", dev_name.c_str());
// }
// else {
// LOG("VIDIOC_REQBUFS \n");
// }
// }
// LOG("frame count:%d \n" ,req.count);
// if (req.count < 2) {
// LOG("Insufficient buffer memory on %s\n", dev_name.c_str());
// }
// buffers.resize(req.count);
// LOG("frame count: %d \n", req.count);
// if (buffers.empty()) {
// LOG("Out of memory\n");
// }
// for (n_buffers = 0; n_buffers < req.count; ++n_buffers) {
// struct v4l2_buffer buf;
// CLEAR(buf);
// buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
// buf.memory = V4L2_MEMORY_MMAP;
// buf.index = n_buffers;
// if (-1 == ioctl(fd, VIDIOC_QUERYBUF, &buf))
// LOG("VIDIOC_QUERYBUF \n");
// LOG("mmap buffer len:%d \n",buf.length);
// buffers[n_buffers].length = buf.length;
// buffers[n_buffers].start =
// mmap(NULL /* start anywhere */,
// buf.length,
// PROT_READ | PROT_WRITE /* required */,
// MAP_SHARED /* recommended */,
// fd, buf.m.offset);
// if (MAP_FAILED == buffers[n_buffers].start){
// LOG("MAP_FAILED!!! %d \n",buf.length);
// }
// }
}
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(string_format("line:%d %dx%d", __LINE__, frmsize.discrete.width, frmsize.discrete.height));
else if (frmsize.type == V4L2_FRMSIZE_TYPE_STEPWISE)
LOG_INFO(string_format("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(string_format("%s is no V4L2 device", dev_name.c_str()));
else
LOG_ERROR("VIDIOC_QUERYCAP");
}
if (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE))
LOG_ERROR(string_format("%s is no video capture device", dev_name.c_str()));
if (!(cap.capabilities & V4L2_CAP_STREAMING))
LOG_ERROR(string_format("%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_INFO(string_format("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(string_format("Cannot open %s", dev_name.c_str()));
}