整理代码

This commit is contained in:
蒲爽 2026-09-28 21:33:04 +08:00
commit c7b4e178ec
364 changed files with 59691 additions and 0 deletions

16
video/v4l2/Access.cpp Executable file
View file

@ -0,0 +1,16 @@
#include "Access.h"
namespace Video
{
namespace V4L2
{
Access::Access(Device* device): m_device(device)
{
}
Access::~Access()
{
delete m_device;
}
}
}

73
video/v4l2/Access.h Executable file
View file

@ -0,0 +1,73 @@
#pragma once
#include "Device.h"
namespace Video
{
namespace V4L2
{
class Access
{
public:
explicit Access(Device* device);
virtual ~Access();
int getFd()
{
return m_device->getFd();
}
unsigned int getBufferSize()
{
return m_device->getBufferSize();
}
unsigned int getFormat()
{
return m_device->getFormat();
}
unsigned int getWidth()
{
return m_device->getWidth();
}
unsigned int getHeight()
{
return m_device->getHeight();
}
const std::string getName()
{
return m_device->getName();
}
void queryFormat()
{
m_device->queryFormat();
}
int setFormat(unsigned int format, unsigned int width, unsigned int height)
{
return m_device->setFormat(format, width, height);
}
int setFps(int fps)
{
return m_device->setFps(fps);
}
int isReady()
{
return m_device->isReady();
}
int start()
{
return m_device->start();
}
int stop()
{
return m_device->stop();
}
private:
Access(const Access&);
Access& operator=(const Access&);
protected:
Device* m_device;
};
}
}

63
video/v4l2/Capture.cpp Executable file
View file

@ -0,0 +1,63 @@
#include <linux/videodev2.h>
#include "Capture.h"
#include "MmapDevice.h"
#include "ReadWriteDevice.h"
namespace Video
{
namespace V4L2
{
Capture* Capture::create(const DeviceParameters& param)
{
Capture* videoCapture = NULL;
Device* videoDevice = NULL;
int caps = V4L2_CAP_VIDEO_CAPTURE;
switch (param.m_iotype)
{
case IOTYPE_MMAP:
videoDevice = new MmapDevice(param, V4L2_BUF_TYPE_VIDEO_CAPTURE);
caps |= V4L2_CAP_STREAMING;
break;
case IOTYPE_READWRITE:
videoDevice = new ReadWriteDevice(param, V4L2_BUF_TYPE_VIDEO_CAPTURE);
caps |= V4L2_CAP_READWRITE;
break;
}
if (videoDevice && !videoDevice->init(caps))
{
delete videoDevice;
videoDevice = NULL;
}
if (videoDevice)
{
videoCapture = new Capture(videoDevice);
}
return videoCapture;
}
Capture::Capture(Device* device): Access(device)
{
}
Capture::~Capture()
{
}
bool Capture::isReadable(timeval* tv)
{
int fd = m_device->getFd();
fd_set fdset;
FD_ZERO(&fdset);
FD_SET(fd, &fdset);
return (select(fd + 1, &fdset, NULL, NULL, tv) == 1);
}
size_t Capture::read(char* buffer, size_t bufferSize)
{
return m_device->readInternal(buffer, bufferSize);
}
}
}

22
video/v4l2/Capture.h Executable file
View file

@ -0,0 +1,22 @@
#pragma once
#include "Access.h"
namespace Video
{
namespace V4L2
{
class Capture: public Access
{
protected:
explicit Capture(Device* device);
public:
static Capture* create(const DeviceParameters& param);
virtual ~Capture();
size_t read(char* buffer, size_t bufferSize);
bool isReadable(timeval* tv);
};
}
}

254
video/v4l2/Device.cpp Executable file
View file

@ -0,0 +1,254 @@
#include "Device.h"
#include "Logger.h"
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <linux/videodev2.h>
namespace Video
{
namespace V4L2
{
std::string Device::fourcc(unsigned int format)
{
char formatArray[] = {(char)(format & 0xff), (char)((format >> 8) & 0xff), (char)((format >> 16) & 0xff),
(char)((format >> 24) & 0xff), 0};
return std::string(formatArray, strlen(formatArray));
}
unsigned int Device::fourcc(const char* format)
{
char fourcc[4];
memset(&fourcc, 0, sizeof(fourcc));
if (format != NULL)
{
strncpy(fourcc, format, 4);
}
return v4l2_fourcc(fourcc[0], fourcc[1], fourcc[2], fourcc[3]);
}
Device::Device(const DeviceParameters& params, v4l2_buf_type deviceType)
: m_params(params), m_fd(-1), m_deviceType(deviceType), m_bufferSize(0), m_format(0)
{
}
Device::~Device()
{
this->close();
}
void Device::close()
{
if (m_fd != -1)
::close(m_fd);
m_fd = -1;
}
void Device::queryFormat()
{
struct v4l2_format fmt;
memset(&fmt, 0, sizeof(fmt));
fmt.type = m_deviceType;
if (0 == ioctl(m_fd, VIDIOC_G_FMT, &fmt))
{
m_format = fmt.fmt.pix.pixelformat;
m_width = fmt.fmt.pix.width;
m_height = fmt.fmt.pix.height;
m_bufferSize = fmt.fmt.pix.sizeimage;
logger().debug("%s: %s size: %ux%u bufferSize: %u", m_params.m_devName, fourcc(m_format), (uint32_t)m_width,
(uint32_t)m_height, (uint32_t)m_bufferSize);
}
}
bool Device::init(unsigned int mandatoryCapabilities)
{
struct stat sb;
if ((stat(m_params.m_devName.c_str(), &sb) == 0) && ((sb.st_mode & S_IFMT) == S_IFCHR))
{
if (initdevice(m_params.m_devName.c_str(), mandatoryCapabilities) == -1)
{
logger().error("%s: Cannot init device", m_params.m_devName);
}
}
else
{
m_fd = open(m_params.m_devName.c_str(), O_WRONLY | O_CREAT | O_TRUNC, S_IRWXU);
}
return (m_fd != -1);
}
int Device::initdevice(const char* dev_name, unsigned int mandatoryCapabilities)
{
m_fd = open(dev_name, m_params.m_openFlags);
if (m_fd < 0)
{
logger().error("%s: Cannot open device", m_params.m_devName);
this->close();
return -1;
}
if (checkCapabilities(m_fd, mandatoryCapabilities) != 0)
{
this->close();
return -1;
}
if (configureFormat(m_fd) != 0)
{
this->close();
return -1;
}
if (configureParam(m_fd, m_params.m_fps) != 0)
{
this->close();
return -1;
}
return m_fd;
}
int Device::checkCapabilities(int fd, unsigned int mandatoryCapabilities)
{
struct v4l2_capability cap;
memset(&(cap), 0, sizeof(cap));
if (-1 == ioctl(fd, VIDIOC_QUERYCAP, &cap))
{
logger().error("%s: Cannot get capabilities: %d(%s)", m_params.m_devName, errno, std::string(strerror(errno)));
return -1;
}
std::string str = "";
if ((cap.capabilities & V4L2_CAP_VIDEO_OUTPUT))
str += "output,";
if ((cap.capabilities & V4L2_CAP_VIDEO_CAPTURE))
str += "capture,";
if ((cap.capabilities & V4L2_CAP_READWRITE))
str += "read/write,";
if ((cap.capabilities & V4L2_CAP_STREAMING))
str += "streaming,";
if ((cap.capabilities & V4L2_CAP_TIMEPERFRAME))
str += "timeperframe";
m_name = (char*)cap.card;
logger().debug("%s(%s): support: %s", m_params.m_devName, m_name, str);
if ((cap.capabilities & mandatoryCapabilities) != mandatoryCapabilities)
{
logger().error("%s: Mandatory capability not available for device", m_params.m_devName);
return -1;
}
return 0;
}
int Device::configureFormat(int fd)
{
this->queryFormat();
unsigned int width = m_width;
unsigned int height = m_height;
if (m_params.m_width != 0)
{
width = m_params.m_width;
}
if (m_params.m_height != 0)
{
height = m_params.m_height;
}
if ((m_params.m_formatList.size() == 0) && (m_format != 0))
{
m_params.m_formatList.push_back(m_format);
}
std::list<unsigned int>::iterator it;
for (it = m_params.m_formatList.begin(); it != m_params.m_formatList.end(); ++it)
{
unsigned int format = *it;
if (this->configureFormat(fd, format, width, height) == 0)
{
this->queryFormat();
return 0;
}
}
return -1;
}
int Device::configureFormat(int fd, unsigned int format, unsigned int width, unsigned int height)
{
struct v4l2_format fmt;
memset(&(fmt), 0, sizeof(fmt));
fmt.type = m_deviceType;
if (ioctl(m_fd, VIDIOC_G_FMT, &fmt) == -1)
{
logger().error("%s: Cannot get format: %d(%s)", m_params.m_devName, errno, std::string(strerror(errno)));
return -1;
}
if (width != 0)
{
fmt.fmt.pix.width = width;
}
if (height != 0)
{
fmt.fmt.pix.height = height;
}
if (format != 0)
{
fmt.fmt.pix.pixelformat = format;
}
if (ioctl(fd, VIDIOC_S_FMT, &fmt) == -1)
{
logger().error("%s: Cannot set format: %d(%s)", m_params.m_devName, errno, std::string(strerror(errno)));
return -1;
}
if (fmt.fmt.pix.pixelformat != format)
{
logger().error("%s: Cannot set pixelformat to: %s format is ", m_params.m_devName, fourcc(format),
fourcc(fmt.fmt.pix.pixelformat));
return -1;
}
if ((fmt.fmt.pix.width != width) || (fmt.fmt.pix.height != height))
{
logger().warning("%s: Cannot set size to: %ux%u size is: %ux%u", m_params.m_devName, (uint32_t)width,
(uint32_t)height, (uint32_t)fmt.fmt.pix.width, (uint32_t)fmt.fmt.pix.height);
}
m_format = fmt.fmt.pix.pixelformat;
m_width = fmt.fmt.pix.width;
m_height = fmt.fmt.pix.height;
m_bufferSize = fmt.fmt.pix.sizeimage;
return 0;
}
int Device::configureParam(int fd, int fps)
{
if (fps != 0)
{
struct v4l2_streamparm param;
memset(&(param), 0, sizeof(param));
param.type = m_deviceType;
param.parm.capture.timeperframe.numerator = 1;
param.parm.capture.timeperframe.denominator = fps;
if (ioctl(fd, VIDIOC_S_PARM, &param) == -1)
{
logger().warning("%s: Cannot set param for device: %d(%s)", m_params.m_devName, errno,
std::string(strerror(errno)));
}
}
return 0;
}
}
}

165
video/v4l2/Device.h Executable file
View file

@ -0,0 +1,165 @@
#pragma once
#include <string>
#include <list>
#include <linux/videodev2.h>
#include <fcntl.h>
#ifndef V4L2_PIX_FMT_VP8
#define V4L2_PIX_FMT_VP8 v4l2_fourcc('V', 'P', '8', '0')
#endif
#ifndef V4L2_PIX_FMT_VP9
#define V4L2_PIX_FMT_VP9 v4l2_fourcc('V', 'P', '9', '0')
#endif
#ifndef V4L2_PIX_FMT_HEVC
#define V4L2_PIX_FMT_HEVC v4l2_fourcc('H', 'E', 'V', 'C')
#endif
namespace Video
{
namespace V4L2
{
enum V4l2IoType
{
IOTYPE_READWRITE,
IOTYPE_MMAP
};
struct DeviceParameters
{
DeviceParameters(const char* devname, const std::list<unsigned int>& formatList, unsigned int width,
unsigned int height, int fps, V4l2IoType ioType = IOTYPE_MMAP, int verbose = 0,
int openFlags = O_RDWR | O_NONBLOCK)
: m_devName(devname), m_formatList(formatList), m_width(width), m_height(height), m_fps(fps), m_iotype(ioType),
m_verbose(verbose), m_openFlags(openFlags)
{
}
DeviceParameters(const char* devname, unsigned int format, unsigned int width, unsigned int height, int fps,
V4l2IoType ioType = IOTYPE_MMAP, int verbose = 0, int openFlags = O_RDWR | O_NONBLOCK)
: m_devName(devname), m_width(width), m_height(height), m_fps(fps), m_iotype(ioType), m_verbose(verbose),
m_openFlags(openFlags)
{
if (format)
{
m_formatList.push_back(format);
}
}
std::string m_devName;
std::list<unsigned int> m_formatList;
unsigned int m_width;
unsigned int m_height;
int m_fps;
V4l2IoType m_iotype;
int m_verbose;
int m_openFlags;
};
class Device
{
friend class Capture;
friend class Output;
protected:
void close();
int initdevice(const char* dev_name, unsigned int mandatoryCapabilities);
int checkCapabilities(int fd, unsigned int mandatoryCapabilities);
int configureFormat(int fd);
int configureFormat(int fd, unsigned int format, unsigned int width, unsigned int height);
int configureParam(int fd, int fps);
virtual bool init(unsigned int mandatoryCapabilities);
virtual size_t writeInternal(char*, size_t)
{
return -1;
}
virtual bool startPartialWrite()
{
return false;
}
virtual size_t writePartialInternal(char*, size_t)
{
return -1;
}
virtual bool endPartialWrite()
{
return false;
}
virtual size_t readInternal(char*, size_t)
{
return -1;
}
public:
Device(const DeviceParameters& params, v4l2_buf_type deviceType);
virtual ~Device();
virtual bool isReady()
{
return (m_fd != -1);
}
virtual bool start()
{
return true;
}
virtual bool stop()
{
return true;
}
std::string getName()
{
return m_name;
}
unsigned int getBufferSize()
{
return m_bufferSize;
}
unsigned int getFormat()
{
return m_format;
}
unsigned int getWidth()
{
return m_width;
}
unsigned int getHeight()
{
return m_height;
}
int getFd()
{
return m_fd;
}
void queryFormat();
int setFormat(unsigned int format, unsigned int width, unsigned int height)
{
return this->configureFormat(m_fd, format, width, height);
}
int setFps(int fps)
{
return this->configureParam(m_fd, fps);
}
static std::string fourcc(unsigned int format);
static unsigned int fourcc(const char* format);
protected:
DeviceParameters m_params;
int m_fd;
v4l2_buf_type m_deviceType;
unsigned int m_bufferSize;
unsigned int m_format;
unsigned int m_width;
unsigned int m_height;
struct v4l2_buffer m_partialWriteBuf;
bool m_partialWriteInProgress;
std::string m_name;
};
}
}

158
video/v4l2/Input.cpp Normal file
View file

@ -0,0 +1,158 @@
/*
*******************************************************************************
* INCLUDE
*******************************************************************************
*/
#include "Input.h"
#include "Capture.h"
#include "InputFormat.h"
#include "../core/System.h"
#include "Logger.h"
namespace Video
{
namespace V4L2
{
/*
*******************************************************************************
* CLASS DEFINE
*******************************************************************************
*/
Input::Input(const std::string& name): VideoComponent(name), _task(name)
{
}
Input::~Input()
{
}
void Input::start()
{
_task.run(
[this](std::atomic<bool>& isCancelled) -> void
{
const std::string path = getPath();
setCodecID(Core::CodecID::MJPEG);
setSignalLost(true);
while (!isCancelled)
{
Poco::SharedPtr<Capture> pCapture;
InputFormat::Collection formats;
try
{
InputFormat::enumerate(path, formats);
if (formats.empty())
{
Poco::Thread::sleep(2000);
continue;
}
for (auto& c : formats)
{
if (V4L2_PIX_FMT_MJPEG == c.pixelFormat && !c.resolutions.empty())
{
auto p = c.resolutions.begin();
uint64_t maxProduct = 0;
for (auto it = c.resolutions.begin(); it != c.resolutions.end(); ++it)
{
uint32_t fps = it->width * it->height > 3686400 ? 30 : 60;
if (it->width <= 2560 && it->height <= 1440 && it->fps <= fps)
{
uint64_t product = static_cast<uint64_t>(it->width) * it->height * it->fps;
if (product > maxProduct)
{
logger().debug("%s: ", name());
maxProduct = product;
p = it;
}
}
}
DeviceParameters param(path.c_str(), c.pixelFormat, p->width, p->height, p->fps);
pCapture = Capture::create(param);
if (pCapture.isNull())
continue;
logger().information("%s: Camera [%s] found, %s, %ux%u@%u", name(), pCapture->getName(),
c.description, p->width, p->height, p->fps);
setWidth(p->width);
setHeight(p->height);
setFrameRate(p->fps);
setSignalLost(false);
serialCodeManager().increment();
break;
}
}
}
catch (...)
{
Poco::Thread::sleep(2000);
continue;
}
uint8_t* pBuffer = nullptr;
;
while (!isCancelled)
{
try
{
struct timeval tv = {1, 0};
if (!pCapture->isReadable(&tv))
break;
int len = pCapture->getBufferSize();
pBuffer = new uint8_t[len];
int rsize = pCapture->read((char*)pBuffer, len);
if (rsize < 0)
{
logger().error("%s: Failed to read frame!", pCapture->getName());
delete pBuffer;
pBuffer = nullptr;
break;
}
int64_t ts = Core::System::timestamp();
Core::Packet::Ptr pPacket = new Core::Packet(
serialCodeManager().code(), Core::Packet::FLAG_IS_VIDEO, pBuffer, rsize, ts, ts);
pBuffer = nullptr;
postToNextComponents(sharedFromThis(), pPacket, Core::MediaType::VIDEO);
}
catch (...)
{
if (pBuffer)
{
delete pBuffer;
pBuffer = nullptr;
}
}
}
setSignalLost(true);
logger().warning("%s: Camera [%s] lost!", name(), pCapture->getName());
}
});
}
void Input::stop()
{
_task.cancel();
}
void Input::onConfigChanged(ParameterNames& names)
{
if (!started())
return;
if (names.exists(_Path))
{
stop();
start();
}
}
}
}

44
video/v4l2/Input.h Normal file
View file

@ -0,0 +1,44 @@
#ifndef A523395E_7DEF_449B_B4DD_5FEE9147BE39
#define A523395E_7DEF_449B_B4DD_5FEE9147BE39
/*
*******************************************************************************
* INCLUDE
*******************************************************************************
*/
#include "../core/VideoComponent.h"
#include "util/SimpleFactory.h"
#include "util/Task.h"
namespace Video
{
namespace V4L2
{
/*
*******************************************************************************
* CLASS DECLARE
*******************************************************************************
*/
class Input: public Core::VideoComponent, public ::Util::SimpleFactory<Input, const std::string&>
{
public:
using Ptr = Poco::AutoPtr<Input>;
public:
Input(const std::string& name);
~Input();
public:
CONFIGURABLE_DECLARE_PARAMETER(CodecID, Core::CodecID);
CONFIGURABLE_DECLARE_PARAMETER(Path, std::string);
protected:
void start() override;
void stop() override;
void onConfigChanged(ParameterNames& names) override;
private:
Util::Task _task;
};
}
}
#endif

View file

@ -0,0 +1,83 @@
/*
*******************************************************************************
* INCLUDE
*******************************************************************************
*/
#include "InputFormat.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <linux/videodev2.h>
#include <Poco/Exception.h>
#include <Poco/Format.h>
namespace Video
{
namespace V4L2
{
/*
*******************************************************************************
* CLASS DEFINE
*******************************************************************************
*/
void InputFormat::enumerate(const std::string& deviceName, InputFormat::Collection& c)
{
int fd = open(deviceName.c_str(), O_RDWR);
if (fd < 0)
throw Poco::InvalidAccessException(Poco::format("Video::V4l2::InputFormat: Open %s FAIL: %d", deviceName, fd));
struct v4l2_fmtdesc fmt;
fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
fmt.index = 0;
while (ioctl(fd, VIDIOC_ENUM_FMT, &fmt) == 0)
{
InputFormat format;
format.pixelFormat = fmt.pixelformat;
format.description = (char*)fmt.description;
struct v4l2_frmsizeenum frmsize;
frmsize.index = 0;
frmsize.pixel_format = fmt.pixelformat;
while (ioctl(fd, VIDIOC_ENUM_FRAMESIZES, &frmsize) == 0)
{
switch (frmsize.type)
{
case V4L2_FRMSIZE_TYPE_DISCRETE:
{
struct v4l2_frmivalenum frameinterval;
frameinterval.width = frmsize.discrete.width;
frameinterval.height = frmsize.discrete.height;
frameinterval.pixel_format = fmt.pixelformat;
frameinterval.type = V4L2_FRMIVAL_TYPE_DISCRETE;
frameinterval.index = 0;
while (ioctl(fd, VIDIOC_ENUM_FRAMEINTERVALS, &frameinterval) == 0)
{
InputFormat::Resolution resolution;
resolution.width = frameinterval.width;
resolution.height = frameinterval.height;
resolution.fps = frameinterval.discrete.denominator / frameinterval.discrete.numerator;
format.resolutions.push_back(resolution);
frameinterval.index++;
}
}
break;
}
frmsize.index++;
}
format.resolutions.sort([](auto& a, auto& b) { return a.width * a.height > b.width * b.height; });
c.push_back(format);
fmt.index++;
}
close(fd);
}
}
}

39
video/v4l2/InputFormat.h Normal file
View file

@ -0,0 +1,39 @@
#ifndef D1EA41B1_FDDE_44D4_BDD5_2BA3EEBAC3B5
#define D1EA41B1_FDDE_44D4_BDD5_2BA3EEBAC3B5
/*
*******************************************************************************
* INCLUDE
*******************************************************************************
*/
#include "Capture.h"
namespace Video
{
namespace V4L2
{
/*
*******************************************************************************
* CLASS DECLARE
*******************************************************************************
*/
struct InputFormat
{
struct Resolution
{
uint32_t width;
uint32_t height;
uint32_t fps;
};
using Collection = std::list<InputFormat>;
uint32_t pixelFormat;
std::string description;
std::list<Resolution> resolutions;
public:
static void enumerate(const std::string& deviceName, Collection& c);
};
}
}
#endif

299
video/v4l2/MmapDevice.cpp Executable file
View file

@ -0,0 +1,299 @@
#include <string.h>
#include <fcntl.h>
#include <stdio.h>
#include <errno.h>
#include <sys/mman.h>
#include <sys/ioctl.h>
#include <linux/videodev2.h>
#include "MmapDevice.h"
#include "Logger.h"
namespace Video
{
namespace V4L2
{
MmapDevice::MmapDevice(const DeviceParameters& params, v4l2_buf_type deviceType)
: Device(params, deviceType), n_buffers(0)
{
memset(&m_buffer, 0, sizeof(m_buffer));
}
bool MmapDevice::init(unsigned int mandatoryCapabilities)
{
bool ret = Device::init(mandatoryCapabilities);
if (ret)
{
ret = this->start();
}
return ret;
}
MmapDevice::~MmapDevice()
{
this->stop();
}
bool MmapDevice::start()
{
bool success = true;
struct v4l2_requestbuffers req;
memset(&req, 0, sizeof(req));
req.count = V4L2MMAP_NBBUFFER;
req.type = m_deviceType;
req.memory = V4L2_MEMORY_MMAP;
if (-1 == ioctl(m_fd, VIDIOC_REQBUFS, &req))
{
if (EINVAL == errno)
{
logger().error("%s: does not support memory mapping", m_params.m_devName);
success = false;
}
else
{
logger().error("VIDIOC_REQBUFS");
success = false;
}
}
else
{
memset(&m_buffer, 0, sizeof(m_buffer));
for (n_buffers = 0; n_buffers < req.count; ++n_buffers)
{
struct v4l2_buffer buf;
memset(&buf, 0, sizeof(buf));
buf.type = m_deviceType;
buf.memory = V4L2_MEMORY_MMAP;
buf.index = n_buffers;
if (-1 == ioctl(m_fd, VIDIOC_QUERYBUF, &buf))
{
logger().error("VIDIOC_QUERYBUF");
success = false;
}
else
{
logger().debug("%s: [%u]: size: %u offset: %u", m_params.m_devName, (uint32_t)n_buffers,
(uint32_t)buf.length, (uint32_t)buf.m.offset);
m_buffer[n_buffers].length = buf.length;
if (!m_buffer[n_buffers].length)
{
m_buffer[n_buffers].length = buf.bytesused;
}
m_buffer[n_buffers].start =
mmap(NULL, m_buffer[n_buffers].length, PROT_READ | PROT_WRITE, MAP_SHARED, m_fd, buf.m.offset);
if (MAP_FAILED == m_buffer[n_buffers].start)
{
logger().error("mmap");
success = false;
}
}
}
for (unsigned int i = 0; i < n_buffers; ++i)
{
struct v4l2_buffer buf;
memset(&buf, 0, sizeof(buf));
buf.type = m_deviceType;
buf.memory = V4L2_MEMORY_MMAP;
buf.index = i;
if (-1 == ioctl(m_fd, VIDIOC_QBUF, &buf))
{
logger().error("VIDIOC_QBUF");
success = false;
}
}
int type = m_deviceType;
if (-1 == ioctl(m_fd, VIDIOC_STREAMON, &type))
{
logger().error("VIDIOC_STREAMON");
success = false;
}
}
return success;
}
bool MmapDevice::stop()
{
bool success = true;
int type = m_deviceType;
if (-1 == ioctl(m_fd, VIDIOC_STREAMOFF, &type))
{
logger().error("VIDIOC_STREAMOFF");
success = false;
}
for (unsigned int i = 0; i < n_buffers; ++i)
{
if (-1 == munmap(m_buffer[i].start, m_buffer[i].length))
{
logger().error("munmap");
success = false;
}
}
struct v4l2_requestbuffers req;
memset(&req, 0, sizeof(req));
req.count = 0;
req.type = m_deviceType;
req.memory = V4L2_MEMORY_MMAP;
if (-1 == ioctl(m_fd, VIDIOC_REQBUFS, &req))
{
logger().error("VIDIOC_REQBUFS");
success = false;
}
n_buffers = 0;
return success;
}
size_t MmapDevice::readInternal(char* buffer, size_t bufferSize)
{
size_t size = 0;
if (n_buffers > 0)
{
struct v4l2_buffer buf;
memset(&buf, 0, sizeof(buf));
buf.type = m_deviceType;
buf.memory = V4L2_MEMORY_MMAP;
if (-1 == ioctl(m_fd, VIDIOC_DQBUF, &buf))
{
if (errno == EAGAIN)
{
size = 0;
}
else
{
logger().error("VIDIOC_DQBUF");
size = -1;
}
}
else if (buf.index < n_buffers)
{
size = buf.bytesused;
if (size > bufferSize)
{
size = bufferSize;
logger().warning("%s: buffer truncated available: %u needed: %u", m_params.m_devName,
(uint32_t)bufferSize, (uint32_t)buf.bytesused);
}
memcpy(buffer, m_buffer[buf.index].start, size);
if (-1 == ioctl(m_fd, VIDIOC_QBUF, &buf))
{
logger().error("VIDIOC_QBUF");
size = -1;
}
}
}
return size;
}
size_t MmapDevice::writeInternal(char* buffer, size_t bufferSize)
{
size_t size = 0;
if (n_buffers > 0)
{
struct v4l2_buffer buf;
memset(&buf, 0, sizeof(buf));
buf.type = m_deviceType;
buf.memory = V4L2_MEMORY_MMAP;
if (-1 == ioctl(m_fd, VIDIOC_DQBUF, &buf))
{
logger().error("VIDIOC_DQBUF");
size = -1;
}
else if (buf.index < n_buffers)
{
size = bufferSize;
if (size > buf.length)
{
logger().warning("%s: buffer truncated available: %u needed: %u", m_params.m_devName,
(uint32_t)buf.length, (uint32_t)size);
size = buf.length;
}
memcpy(m_buffer[buf.index].start, buffer, size);
buf.bytesused = size;
if (-1 == ioctl(m_fd, VIDIOC_QBUF, &buf))
{
logger().error("VIDIOC_QBUF");
size = -1;
}
}
}
return size;
}
bool MmapDevice::startPartialWrite()
{
if (n_buffers <= 0)
return false;
if (m_partialWriteInProgress)
return false;
memset(&m_partialWriteBuf, 0, sizeof(m_partialWriteBuf));
m_partialWriteBuf.type = m_deviceType;
m_partialWriteBuf.memory = V4L2_MEMORY_MMAP;
if (-1 == ioctl(m_fd, VIDIOC_DQBUF, &m_partialWriteBuf))
{
logger().error("VIDIOC_DQBUF");
return false;
}
m_partialWriteBuf.bytesused = 0;
m_partialWriteInProgress = true;
return true;
}
size_t MmapDevice::writePartialInternal(char* buffer, size_t bufferSize)
{
size_t new_size = 0;
size_t size = 0;
if ((n_buffers > 0) && m_partialWriteInProgress)
{
if (m_partialWriteBuf.index < n_buffers)
{
new_size = m_partialWriteBuf.bytesused + bufferSize;
if (new_size > m_partialWriteBuf.length)
{
logger().warning("%s: buffer truncated available: %u needed: %u", m_params.m_devName,
(uint32_t)m_partialWriteBuf.length, (uint32_t)new_size);
new_size = m_partialWriteBuf.length;
}
size = new_size - m_partialWriteBuf.bytesused;
memcpy(&((char*)m_buffer[m_partialWriteBuf.index].start)[m_partialWriteBuf.bytesused], buffer, size);
m_partialWriteBuf.bytesused += size;
}
}
return size;
}
bool MmapDevice::endPartialWrite()
{
if (!m_partialWriteInProgress)
return false;
if (n_buffers <= 0)
{
m_partialWriteInProgress = false;
return true;
}
if (-1 == ioctl(m_fd, VIDIOC_QBUF, &m_partialWriteBuf))
{
logger().error("VIDIOC_QBUF");
m_partialWriteInProgress = false;
return true;
}
m_partialWriteInProgress = false;
return true;
}
}
}

43
video/v4l2/MmapDevice.h Executable file
View file

@ -0,0 +1,43 @@
#pragma once
#include "Device.h"
namespace Video
{
namespace V4L2
{
#define V4L2MMAP_NBBUFFER 10
class MmapDevice: public Device
{
protected:
size_t writeInternal(char* buffer, size_t bufferSize);
bool startPartialWrite();
size_t writePartialInternal(char*, size_t);
bool endPartialWrite();
size_t readInternal(char* buffer, size_t bufferSize);
public:
MmapDevice(const DeviceParameters& params, v4l2_buf_type deviceType);
virtual ~MmapDevice();
virtual bool init(unsigned int mandatoryCapabilities);
virtual bool isReady()
{
return ((m_fd != -1) && (n_buffers != 0));
}
virtual bool start();
virtual bool stop();
protected:
unsigned int n_buffers;
struct buffer
{
void* start;
size_t length;
};
buffer m_buffer[V4L2MMAP_NBBUFFER];
};
}
}

81
video/v4l2/Output.cpp Executable file
View file

@ -0,0 +1,81 @@
#include "Output.h"
#include "MmapDevice.h"
#include "ReadWriteDevice.h"
#include <string.h>
#include <linux/videodev2.h>
namespace Video
{
namespace V4L2
{
Output* Output::create(const DeviceParameters& param)
{
Output* videoOutput = NULL;
Device* videoDevice = NULL;
int caps = V4L2_CAP_VIDEO_OUTPUT;
switch (param.m_iotype)
{
case IOTYPE_MMAP:
videoDevice = new MmapDevice(param, V4L2_BUF_TYPE_VIDEO_OUTPUT);
caps |= V4L2_CAP_STREAMING;
break;
case IOTYPE_READWRITE:
videoDevice = new ReadWriteDevice(param, V4L2_BUF_TYPE_VIDEO_OUTPUT);
caps |= V4L2_CAP_READWRITE;
break;
}
if (videoDevice && !videoDevice->init(caps))
{
delete videoDevice;
videoDevice = NULL;
}
if (videoDevice)
{
videoOutput = new Output(videoDevice);
}
return videoOutput;
}
Output::Output(Device* device): Access(device)
{
}
Output::~Output()
{
}
bool Output::isWritable(timeval* tv)
{
int fd = m_device->getFd();
fd_set fdset;
FD_ZERO(&fdset);
FD_SET(fd, &fdset);
return (select(fd + 1, NULL, &fdset, NULL, tv) == 1);
}
size_t Output::write(char* buffer, size_t bufferSize)
{
return m_device->writeInternal(buffer, bufferSize);
}
bool Output::startPartialWrite()
{
return m_device->startPartialWrite();
}
size_t Output::writePartial(char* buffer, size_t bufferSize)
{
return m_device->writePartialInternal(buffer, bufferSize);
}
bool Output::endPartialWrite()
{
return m_device->endPartialWrite();
}
}
}

25
video/v4l2/Output.h Executable file
View file

@ -0,0 +1,25 @@
#pragma once
#include "Access.h"
namespace Video
{
namespace V4L2
{
class Output: public Access
{
protected:
explicit Output(Device* device);
public:
static Output* create(const DeviceParameters& param);
virtual ~Output();
size_t write(char* buffer, size_t bufferSize);
bool isWritable(timeval* tv);
bool startPartialWrite();
size_t writePartial(char* buffer, size_t bufferSize);
bool endPartialWrite();
};
}
}

23
video/v4l2/ReadWriteDevice.cpp Executable file
View file

@ -0,0 +1,23 @@
#include <unistd.h>
#include "ReadWriteDevice.h"
namespace Video
{
namespace V4L2
{
ReadWriteDevice::ReadWriteDevice(const DeviceParameters& params, v4l2_buf_type deviceType): Device(params, deviceType)
{
}
size_t ReadWriteDevice::writeInternal(char* buffer, size_t bufferSize)
{
return ::write(m_fd, buffer, bufferSize);
}
size_t ReadWriteDevice::readInternal(char* buffer, size_t bufferSize)
{
return ::read(m_fd, buffer, bufferSize);
}
}
}

19
video/v4l2/ReadWriteDevice.h Executable file
View file

@ -0,0 +1,19 @@
#pragma once
#include "Device.h"
namespace Video
{
namespace V4L2
{
class ReadWriteDevice: public Device
{
protected:
virtual size_t writeInternal(char* buffer, size_t bufferSize);
virtual size_t readInternal(char* buffer, size_t bufferSize);
public:
ReadWriteDevice(const DeviceParameters& params, v4l2_buf_type deviceType);
};
}
}