输出YUYV格式的视频流
This commit is contained in:
parent
947e52854d
commit
15599a6ee4
3
.vscode/settings.json
vendored
3
.vscode/settings.json
vendored
@ -60,6 +60,7 @@
|
|||||||
"stop_token": "cpp",
|
"stop_token": "cpp",
|
||||||
"streambuf": "cpp",
|
"streambuf": "cpp",
|
||||||
"thread": "cpp",
|
"thread": "cpp",
|
||||||
"typeinfo": "cpp"
|
"typeinfo": "cpp",
|
||||||
|
"chrono": "cpp"
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -34,7 +34,8 @@ DEFINES =
|
|||||||
|
|
||||||
CFLAGS = $(COMMON_CFLAGS) \
|
CFLAGS = $(COMMON_CFLAGS) \
|
||||||
$(VIMBACPP_CFLAGS) \
|
$(VIMBACPP_CFLAGS) \
|
||||||
$(VIMBAIMAGETRANSFORM_CFLAGS)
|
$(VIMBAIMAGETRANSFORM_CFLAGS) \
|
||||||
|
|
||||||
|
|
||||||
OBJ_FILES = $(OBJ_DIR)/ApiController.o \
|
OBJ_FILES = $(OBJ_DIR)/ApiController.o \
|
||||||
$(OBJ_DIR)/FrameObserver.o \
|
$(OBJ_DIR)/FrameObserver.o \
|
||||||
|
@ -4,6 +4,8 @@
|
|||||||
#include <sys/ioctl.h>
|
#include <sys/ioctl.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
|
#include <vector>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
namespace AVT {
|
namespace AVT {
|
||||||
namespace VmbAPI {
|
namespace VmbAPI {
|
||||||
@ -36,8 +38,9 @@ bool FrameObserver::SetupVideoDevice()
|
|||||||
|
|
||||||
m_vfmt.fmt.pix.width = 640;
|
m_vfmt.fmt.pix.width = 640;
|
||||||
m_vfmt.fmt.pix.height = 480;
|
m_vfmt.fmt.pix.height = 480;
|
||||||
m_vfmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
|
m_vfmt.fmt.pix.pixelformat = V4L2_PIX_FMT_RGB24; // 改为RGB24格式
|
||||||
m_vfmt.fmt.pix.field = V4L2_FIELD_NONE;
|
m_vfmt.fmt.pix.field = V4L2_FIELD_NONE;
|
||||||
|
m_vfmt.fmt.pix.sizeimage = 640 * 480 * 3; // RGB24是3字节/像素
|
||||||
|
|
||||||
if (ioctl(m_videoFd, VIDIOC_S_FMT, &m_vfmt) < 0) {
|
if (ioctl(m_videoFd, VIDIOC_S_FMT, &m_vfmt) < 0) {
|
||||||
perror("Set video format");
|
perror("Set video format");
|
||||||
@ -76,8 +79,8 @@ bool FrameObserver::UpdateVideoFormat(VmbUint32_t width, VmbUint32_t height, Vmb
|
|||||||
m_vfmt.fmt.pix.sizeimage = width * height;
|
m_vfmt.fmt.pix.sizeimage = width * height;
|
||||||
break;
|
break;
|
||||||
case VmbPixelFormatBayerRG8:
|
case VmbPixelFormatBayerRG8:
|
||||||
m_vfmt.fmt.pix.pixelformat = V4L2_PIX_FMT_SRGGB8;
|
m_vfmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV; // We convert Bayer to YUYV
|
||||||
m_vfmt.fmt.pix.sizeimage = width * height;
|
m_vfmt.fmt.pix.sizeimage = width * height * 2;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
m_vfmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
|
m_vfmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
|
||||||
@ -92,10 +95,64 @@ bool FrameObserver::UpdateVideoFormat(VmbUint32_t width, VmbUint32_t height, Vmb
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void FrameObserver::FrameReceived(const FramePtr pFrame)
|
// Convert Bayer RGGB to RGB (simplified demosaicing)
|
||||||
{
|
void FrameObserver::ConvertBayerRGGBToRGB(const VmbUchar_t* bayerData, VmbUchar_t* rgbData, VmbUint32_t width, VmbUint32_t height) {
|
||||||
|
// 只处理中心像素(避免边界越界)
|
||||||
|
for (VmbUint32_t y = 1; y < height - 1; y++) {
|
||||||
|
for (VmbUint32_t x = 1; x < width - 1; x++) {
|
||||||
|
VmbUint32_t idx = y * width + x;
|
||||||
|
VmbUchar_t r, g, b;
|
||||||
|
|
||||||
|
if (y % 2 == 0) { // Even row (RGRG...)
|
||||||
|
if (x % 2 == 0) { // R
|
||||||
|
r = bayerData[idx];
|
||||||
|
g = (bayerData[idx - 1] + bayerData[idx + 1] + bayerData[idx - width] + bayerData[idx + width]) / 4;
|
||||||
|
b = (bayerData[idx - width - 1] + bayerData[idx - width + 1] + bayerData[idx + width - 1] + bayerData[idx + width + 1]) / 4;
|
||||||
|
} else { // G
|
||||||
|
r = (bayerData[idx - 1] + bayerData[idx + 1]) / 2;
|
||||||
|
g = bayerData[idx];
|
||||||
|
b = (bayerData[idx - width] + bayerData[idx + width]) / 2;
|
||||||
|
}
|
||||||
|
} else { // Odd row (GBGB...)
|
||||||
|
if (x % 2 == 0) { // G
|
||||||
|
r = (bayerData[idx - width] + bayerData[idx + width]) / 2;
|
||||||
|
g = bayerData[idx];
|
||||||
|
b = (bayerData[idx - 1] + bayerData[idx + 1]) / 2;
|
||||||
|
} else { // B
|
||||||
|
r = (bayerData[idx - width - 1] + bayerData[idx - width + 1] + bayerData[idx + width - 1] + bayerData[idx + width + 1]) / 4;
|
||||||
|
g = (bayerData[idx - 1] + bayerData[idx + 1] + bayerData[idx - width] + bayerData[idx + width]) / 4;
|
||||||
|
b = bayerData[idx];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rgbData[idx * 3] = r;
|
||||||
|
rgbData[idx * 3 + 1] = g;
|
||||||
|
rgbData[idx * 3 + 2] = b;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Convert RGB to YUYV (4:2:2)
|
||||||
|
void FrameObserver::ConvertRGBToYUYV(const VmbUchar_t* rgbData, VmbUchar_t* yuyvData, VmbUint32_t width, VmbUint32_t height) {
|
||||||
|
for (VmbUint32_t i = 0; i < width * height; i += 2) {
|
||||||
|
VmbUchar_t r1 = rgbData[i * 3], g1 = rgbData[i * 3 + 1], b1 = rgbData[i * 3 + 2];
|
||||||
|
VmbUchar_t r2 = rgbData[(i + 1) * 3], g2 = rgbData[(i + 1) * 3 + 1], b2 = rgbData[(i + 1) * 3 + 2];
|
||||||
|
|
||||||
|
// YUV转换(简化版BT.601)
|
||||||
|
VmbUchar_t y1 = 0.299f * r1 + 0.587f * g1 + 0.114f * b1;
|
||||||
|
VmbUchar_t u1 = -0.147f * r1 - 0.289f * g1 + 0.436f * b1 + 128;
|
||||||
|
VmbUchar_t v1 = 0.615f * r1 - 0.515f * g1 - 0.100f * b1 + 128;
|
||||||
|
VmbUchar_t y2 = 0.299f * r2 + 0.587f * g2 + 0.114f * b2;
|
||||||
|
|
||||||
|
yuyvData[i * 2] = y1;
|
||||||
|
yuyvData[i * 2 + 1] = u1;
|
||||||
|
yuyvData[i * 2 + 2] = y2;
|
||||||
|
yuyvData[i * 2 + 3] = v1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void FrameObserver::FrameReceived(const FramePtr pFrame) {
|
||||||
auto startTime = std::chrono::high_resolution_clock::now();
|
auto startTime = std::chrono::high_resolution_clock::now();
|
||||||
|
|
||||||
|
// 1. 获取帧数据
|
||||||
VmbUchar_t* pBuffer = nullptr;
|
VmbUchar_t* pBuffer = nullptr;
|
||||||
VmbUint32_t nSize = 0;
|
VmbUint32_t nSize = 0;
|
||||||
VmbUint32_t width = 0, height = 0;
|
VmbUint32_t width = 0, height = 0;
|
||||||
@ -106,26 +163,57 @@ void FrameObserver::FrameReceived(const FramePtr pFrame)
|
|||||||
pFrame->GetWidth(width) != VmbErrorSuccess ||
|
pFrame->GetWidth(width) != VmbErrorSuccess ||
|
||||||
pFrame->GetHeight(height) != VmbErrorSuccess ||
|
pFrame->GetHeight(height) != VmbErrorSuccess ||
|
||||||
pFrame->GetPixelFormat(pixelFormat) != VmbErrorSuccess) {
|
pFrame->GetPixelFormat(pixelFormat) != VmbErrorSuccess) {
|
||||||
|
std::cerr << "Failed to get frame data!" << std::endl;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 2. 检查数据有效性
|
||||||
|
if (!pBuffer || width == 0 || height == 0) {
|
||||||
|
std::cerr << "Invalid frame data: width=" << width << ", height=" << height << std::endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. 更新V4L2设备格式(确保支持当前分辨率/PixelFormat)
|
||||||
if (!UpdateVideoFormat(width, height, pixelFormat)) {
|
if (!UpdateVideoFormat(width, height, pixelFormat)) {
|
||||||
|
std::cerr << "Failed to update video format!" << std::endl;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 4. 处理Bayer RGGB格式(转换为YUYV)
|
||||||
|
if (pixelFormat == VmbPixelFormatBayerRG8) {
|
||||||
|
// 4.1 分配缓冲区
|
||||||
|
std::vector<VmbUchar_t> rgbData(width * height * 3); // RGB24: width * height * 3
|
||||||
|
std::vector<VmbUchar_t> yuyvData(width * height * 2); // YUYV: width * height * 2
|
||||||
|
|
||||||
|
// 4.2 Bayer RGGB -> RGB
|
||||||
|
ConvertBayerRGGBToRGB(pBuffer, rgbData.data(), width, height);
|
||||||
|
|
||||||
|
// 4.3 RGB -> YUYV
|
||||||
|
ConvertRGBToYUYV(rgbData.data(), yuyvData.data(), width, height);
|
||||||
|
|
||||||
|
// 4.4 写入V4L2设备
|
||||||
|
ssize_t written = write(m_videoFd, yuyvData.data(), yuyvData.size());
|
||||||
|
if (written != (ssize_t)yuyvData.size()) {
|
||||||
|
perror("Write YUYV to video device failed");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 5. 处理非Bayer格式(直接写入)
|
||||||
|
else {
|
||||||
ssize_t written = write(m_videoFd, pBuffer, nSize);
|
ssize_t written = write(m_videoFd, pBuffer, nSize);
|
||||||
if (written != (ssize_t)nSize) {
|
if (written != (ssize_t)nSize) {
|
||||||
perror("Write to video device");
|
perror("Write raw data to video device failed");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 6. 统计性能
|
||||||
auto endTime = std::chrono::high_resolution_clock::now();
|
auto endTime = std::chrono::high_resolution_clock::now();
|
||||||
double latency = std::chrono::duration<double, std::milli>(endTime - startTime).count();
|
double latency = std::chrono::duration<double, std::milli>(endTime - startTime).count();
|
||||||
m_totalLatency.store(m_totalLatency.load() + latency);
|
m_totalLatency.store(m_totalLatency.load() + latency);
|
||||||
m_frameCount++;
|
m_frameCount++;
|
||||||
|
|
||||||
|
// 7. 重新排队帧
|
||||||
m_pCamera->QueueFrame(pFrame);
|
m_pCamera->QueueFrame(pFrame);
|
||||||
}
|
}
|
||||||
|
|
||||||
double FrameObserver::GetAverageLatency() const {
|
double FrameObserver::GetAverageLatency() const {
|
||||||
return m_frameCount > 0 ? m_totalLatency.load() / m_frameCount : 0.0;
|
return m_frameCount > 0 ? m_totalLatency.load() / m_frameCount : 0.0;
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,7 @@
|
|||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
|
#include <vector> // Add for buffer storage
|
||||||
|
|
||||||
namespace AVT {
|
namespace AVT {
|
||||||
namespace VmbAPI {
|
namespace VmbAPI {
|
||||||
@ -29,6 +30,11 @@ private:
|
|||||||
void CloseVideoDevice();
|
void CloseVideoDevice();
|
||||||
bool UpdateVideoFormat(VmbUint32_t width, VmbUint32_t height, VmbPixelFormatType pixelFormat);
|
bool UpdateVideoFormat(VmbUint32_t width, VmbUint32_t height, VmbPixelFormatType pixelFormat);
|
||||||
|
|
||||||
|
// Bayer to RGB conversion
|
||||||
|
void ConvertBayerRGGBToRGB(const VmbUchar_t* bayerData, VmbUchar_t* rgbData, VmbUint32_t width, VmbUint32_t height);
|
||||||
|
// RGB to YUYV conversion
|
||||||
|
void ConvertRGBToYUYV(const VmbUchar_t* rgbData, VmbUchar_t* yuyvData, VmbUint32_t width, VmbUint32_t height);
|
||||||
|
|
||||||
int m_videoFd;
|
int m_videoFd;
|
||||||
struct v4l2_format m_vfmt;
|
struct v4l2_format m_vfmt;
|
||||||
std::mutex m_deviceMutex;
|
std::mutex m_deviceMutex;
|
||||||
|
Loading…
Reference in New Issue
Block a user