输出YUYV格式的视频流

This commit is contained in:
zhangpeng 2025-06-30 17:01:41 +08:00
parent 947e52854d
commit 15599a6ee4
4 changed files with 109 additions and 13 deletions

View File

@ -60,6 +60,7 @@
"stop_token": "cpp",
"streambuf": "cpp",
"thread": "cpp",
"typeinfo": "cpp"
"typeinfo": "cpp",
"chrono": "cpp"
}
}

View File

@ -34,7 +34,8 @@ DEFINES =
CFLAGS = $(COMMON_CFLAGS) \
$(VIMBACPP_CFLAGS) \
$(VIMBAIMAGETRANSFORM_CFLAGS)
$(VIMBAIMAGETRANSFORM_CFLAGS) \
OBJ_FILES = $(OBJ_DIR)/ApiController.o \
$(OBJ_DIR)/FrameObserver.o \

View File

@ -4,6 +4,8 @@
#include <sys/ioctl.h>
#include <unistd.h>
#include <chrono>
#include <vector>
#include <iostream>
namespace AVT {
namespace VmbAPI {
@ -36,8 +38,9 @@ bool FrameObserver::SetupVideoDevice()
m_vfmt.fmt.pix.width = 640;
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.sizeimage = 640 * 480 * 3; // RGB24是3字节/像素
if (ioctl(m_videoFd, VIDIOC_S_FMT, &m_vfmt) < 0) {
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;
break;
case VmbPixelFormatBayerRG8:
m_vfmt.fmt.pix.pixelformat = V4L2_PIX_FMT_SRGGB8;
m_vfmt.fmt.pix.sizeimage = width * height;
m_vfmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV; // We convert Bayer to YUYV
m_vfmt.fmt.pix.sizeimage = width * height * 2;
break;
default:
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;
}
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();
// 1. 获取帧数据
VmbUchar_t* pBuffer = nullptr;
VmbUint32_t nSize = 0;
VmbUint32_t width = 0, height = 0;
@ -106,26 +163,57 @@ void FrameObserver::FrameReceived(const FramePtr pFrame)
pFrame->GetWidth(width) != VmbErrorSuccess ||
pFrame->GetHeight(height) != VmbErrorSuccess ||
pFrame->GetPixelFormat(pixelFormat) != VmbErrorSuccess) {
std::cerr << "Failed to get frame data!" << std::endl;
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)) {
std::cerr << "Failed to update video format!" << std::endl;
return;
}
ssize_t written = write(m_videoFd, pBuffer, nSize);
if (written != (ssize_t)nSize) {
perror("Write to video device");
// 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);
if (written != (ssize_t)nSize) {
perror("Write raw data to video device failed");
}
}
// 6. 统计性能
auto endTime = std::chrono::high_resolution_clock::now();
double latency = std::chrono::duration<double, std::milli>(endTime - startTime).count();
m_totalLatency.store(m_totalLatency.load() + latency);
m_frameCount++;
// 7. 重新排队帧
m_pCamera->QueueFrame(pFrame);
}
double FrameObserver::GetAverageLatency() const {
return m_frameCount > 0 ? m_totalLatency.load() / m_frameCount : 0.0;
}

View File

@ -8,6 +8,7 @@
#include <mutex>
#include <chrono>
#include <atomic>
#include <vector> // Add for buffer storage
namespace AVT {
namespace VmbAPI {
@ -29,6 +30,11 @@ private:
void CloseVideoDevice();
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;
struct v4l2_format m_vfmt;
std::mutex m_deviceMutex;