GrabBag/Device/HikDevice/Src/HikDevice.cpp

723 lines
23 KiB
C++
Raw Normal View History

#include "HikDevice.h"
#include "VrLog.h"
#include "VrError.h"
#include <cstring>
#include <cstdint>
#include <algorithm>
#include <thread>
#include <chrono>
// 海康相机默认账户密码
#define HIK_DEFAULT_USERNAME "admin"
#define HIK_DEFAULT_PASSWORD "wood0908"
// 静态成员变量定义
std::map<long, CHikDevice*> CHikDevice::s_portDeviceMap;
std::mutex CHikDevice::s_mapMutex;
// ============ 静态工厂方法 ============
int IHikDevice::CreateObject(IHikDevice** ppDevice)
{
if (!ppDevice) {
return ERR_CODE(DEV_ARG_INVAILD);
}
*ppDevice = new CHikDevice();
return (*ppDevice != nullptr) ? SUCCESS : ERR_CODE(DATA_ERR_MEM);
}
// ============ 构造函数和析构函数 ============
CHikDevice::CHikDevice()
{
LOG_INFO("CHikDevice created\n");
}
CHikDevice::~CHikDevice()
{
// 停止预览
StopPreview();
// 登出
Logout();
// 清理SDK
CleanupSDK();
LOG_INFO("CHikDevice destroyed\n");
}
// ============ SDK 初始化 ============
int CHikDevice::InitSDK()
{
if (m_bSDKInitialized) {
LOG_INFO("CHikDevice::InitSDK - SDK already initialized\n");
return SUCCESS;
}
LOG_INFO("CHikDevice::InitSDK - Initializing HCNetSDK\n");
// 初始化海康SDK
if (!NET_DVR_Init()) {
m_lastError = NET_DVR_GetLastError();
LOG_ERR("NET_DVR_Init failed, error code: %d\n", m_lastError);
return ERR_CODE(DEV_OPEN_ERR);
}
// 设置连接超时和重连
NET_DVR_SetConnectTime(2000, 1); // 连接超时2秒
NET_DVR_SetReconnect(10000, TRUE); // 重连间隔10秒
// 设置异常回调
NET_DVR_SetExceptionCallBack_V30(0, NULL, HikSDKExceptionCallback, this);
m_bSDKInitialized = true;
LOG_INFO("CHikDevice::InitSDK - HCNetSDK initialized successfully\n");
return SUCCESS;
}
void CHikDevice::CleanupSDK()
{
if (!m_bSDKInitialized) {
return;
}
NET_DVR_Cleanup();
m_bSDKInitialized = false;
LOG_INFO("CHikDevice::CleanupSDK - HCNetSDK cleaned up\n");
}
// ============ 登录/登出 ============
int CHikDevice::Login(const HikDeviceConfig& config)
{
LOG_INFO("CHikDevice::Login - IP: %s, Port: %d\n", config.ip.c_str(), config.port);
if (!m_bSDKInitialized) {
LOG_ERR("CHikDevice::Login - SDK not initialized\n");
return ERR_CODE(DEV_NO_OPEN);
}
if (m_lUserID >= 0) {
LOG_WARN("CHikDevice::Login - Already logged in, logout first\n");
Logout();
}
// 保存配置
m_config = config;
// 如果用户名或密码为空,使用默认值
std::string username = config.username.empty() ? HIK_DEFAULT_USERNAME : config.username;
std::string password = config.password.empty() ? HIK_DEFAULT_PASSWORD : config.password;
if (config.username.empty() || config.password.empty()) {
LOG_INFO("CHikDevice::Login - Using default credentials (username: %s)\n", username.c_str());
}
// 登录参数
NET_DVR_USER_LOGIN_INFO loginInfo = {0};
NET_DVR_DEVICEINFO_V40 deviceInfo = {0};
loginInfo.bUseAsynLogin = FALSE; // 同步登录
strncpy(loginInfo.sDeviceAddress, config.ip.c_str(), NET_DVR_DEV_ADDRESS_MAX_LEN - 1);
loginInfo.wPort = static_cast<WORD>(config.port);
strncpy(loginInfo.sUserName, username.c_str(), NAME_LEN - 1);
strncpy(loginInfo.sPassword, password.c_str(), NAME_LEN - 1);
// 执行登录
m_lUserID = NET_DVR_Login_V40(&loginInfo, &deviceInfo);
if (m_lUserID < 0) {
m_lastError = NET_DVR_GetLastError();
LOG_ERR("NET_DVR_Login_V40 failed, error code: %d\n", m_lastError);
UpdateStatus(EHikDeviceStatus::Disconnected);
if (m_lastError == NET_DVR_ERROR_DEVICE_NOT_ACTIVATED) {
LOG_INFO("Device already activated\n");
return ERR_CODE(DEV_UNACTIVATE);
}
return ERR_CODE(NET_ERR_CONNECT);
}
UpdateStatus(EHikDeviceStatus::Connected);
LOG_INFO("CHikDevice::Login - Login successful, UserID: %ld, Channels: %d\n", m_lUserID, deviceInfo.struDeviceV30.byChanNum);
return SUCCESS;
}
void CHikDevice::Logout()
{
if (m_lUserID >= 0) {
// 先停止预览
StopPreview();
NET_DVR_Logout(m_lUserID);
LOG_INFO("CHikDevice::Logout - Logged out, UserID: %ld\n", m_lUserID);
m_lUserID = -1;
}
UpdateStatus(EHikDeviceStatus::Disconnected);
}
bool CHikDevice::IsLoggedIn() const
{
return m_lUserID >= 0;
}
// ============ 预览控制 ============
int CHikDevice::StartPreview()
{
LOG_INFO("CHikDevice::StartPreview\n");
if (m_lUserID < 0) {
LOG_ERR("CHikDevice::StartPreview - Not logged in\n");
return ERR_CODE(DEV_NO_OPEN);
}
if (m_lRealPlayHandle >= 0) {
LOG_WARN("CHikDevice::StartPreview - Preview already started\n");
return SUCCESS;
}
// 设置预览参数
NET_DVR_PREVIEWINFO previewInfo = {0};
previewInfo.hPlayWnd = NULL; // 不使用窗口显示,通过回调获取数据
previewInfo.lChannel = m_config.channelNo;
previewInfo.dwStreamType = m_config.streamType;
previewInfo.dwLinkMode = 0; // TCP方式
previewInfo.bBlocked = TRUE;
// 开始预览
m_lRealPlayHandle = NET_DVR_RealPlay_V40(m_lUserID, &previewInfo, HikRealDataCallback, this);
if (m_lRealPlayHandle < 0) {
m_lastError = NET_DVR_GetLastError();
LOG_ERR("NET_DVR_RealPlay_V40 failed, error code: %d\n", m_lastError);
return ERR_CODE(DEV_CTRL_ERR);
}
m_bPreviewing = true;
UpdateStatus(EHikDeviceStatus::Previewing);
LOG_INFO("CHikDevice::StartPreview - Preview started, Handle: %ld\n", m_lRealPlayHandle);
return SUCCESS;
}
int CHikDevice::StartPreviewEx(void* hWnd)
{
LOG_INFO("CHikDevice::StartPreviewEx - hWnd: %p\n", hWnd);
if (m_lUserID < 0) {
LOG_ERR("CHikDevice::StartPreviewEx - Not logged in\n");
return ERR_CODE(DEV_NO_OPEN);
}
if (m_lRealPlayHandle >= 0) {
LOG_WARN("CHikDevice::StartPreviewEx - Preview already started\n");
return SUCCESS;
}
// 设置预览参数
NET_DVR_PREVIEWINFO previewInfo = {0};
#ifdef _WIN32
previewInfo.hPlayWnd = (HWND)hWnd; // Windows: HWND 是指针类型
#else
// Linux: HWND 是 unsigned int (X11 窗口 ID),需要通过 uintptr_t 转换
previewInfo.hPlayWnd = static_cast<HWND>(reinterpret_cast<uintptr_t>(hWnd));
#endif
previewInfo.lChannel = m_config.channelNo;
previewInfo.dwStreamType = m_config.streamType;
previewInfo.dwLinkMode = 0; // TCP方式
previewInfo.bBlocked = TRUE;
previewInfo.dwDisplayBufNum = 1; // 最小显示缓冲,降低延迟
// 开始预览不需要回调SDK直接渲染
m_lRealPlayHandle = NET_DVR_RealPlay_V40(m_lUserID, &previewInfo, NULL, NULL);
if (m_lRealPlayHandle < 0) {
m_lastError = NET_DVR_GetLastError();
LOG_ERR("NET_DVR_RealPlay_V40 failed, error code: %d\n", m_lastError);
return ERR_CODE(DEV_CTRL_ERR);
}
// 设置预览延迟模式为实时(最低延迟)
NET_DVR_SetPlayerBufNumber(m_lRealPlayHandle, 1);
m_bPreviewing = true;
UpdateStatus(EHikDeviceStatus::Previewing);
LOG_INFO("CHikDevice::StartPreviewEx - Preview started with hardware rendering, Handle: %ld\n", m_lRealPlayHandle);
return SUCCESS;
}
void CHikDevice::StopPreview()
{
// 先停止预览
if (m_lRealPlayHandle >= 0) {
NET_DVR_StopRealPlay(m_lRealPlayHandle);
LOG_INFO("CHikDevice::StopPreview - Preview stopped, Handle: %ld\n", m_lRealPlayHandle);
m_lRealPlayHandle = -1;
}
// 停止 PlayM4 解码
if (m_lPlayM4Port >= 0) {
// 先移除端口映射
{
std::lock_guard<std::mutex> lock(s_mapMutex);
s_portDeviceMap.erase(m_lPlayM4Port);
}
if (m_bPlayM4Playing) {
PlayM4_Stop(m_lPlayM4Port);
m_bPlayM4Playing = false;
}
PlayM4_CloseStream(m_lPlayM4Port);
PlayM4_FreePort(m_lPlayM4Port);
m_lPlayM4Port = -1;
LOG_INFO("CHikDevice::StopPreview - PlayM4 decoder stopped and port freed\n");
}
m_bPreviewing = false;
if (m_lUserID >= 0) {
UpdateStatus(EHikDeviceStatus::Connected);
} else {
UpdateStatus(EHikDeviceStatus::Disconnected);
}
}
bool CHikDevice::IsPreviewing() const
{
return m_bPreviewing;
}
// ============ 状态管理 ============
EHikDeviceStatus CHikDevice::GetStatus() const
{
return m_status;
}
void CHikDevice::UpdateStatus(EHikDeviceStatus status)
{
if (m_status != status) {
m_status = status;
LOG_INFO("CHikDevice status changed to: %d\n", static_cast<int>(status));
if (m_statusCallback) {
m_statusCallback(status, m_pStatusCallbackUser);
}
}
}
// ============ 回调设置 ============
void CHikDevice::SetDecodedFrameCallback(HikDecodedFrameCallback callback, void* pUser)
{
m_frameCallback = callback;
m_pFrameCallbackUser = pUser;
}
void CHikDevice::SetStatusCallback(HikDeviceStatusCallback callback, void* pUser)
{
m_statusCallback = callback;
m_pStatusCallbackUser = pUser;
}
void CHikDevice::SetExceptionCallback(HikExceptionCallback callback, void* pUser)
{
m_exceptionCallback = callback;
m_pExceptionCallbackUser = pUser;
}
// ============ 帧数据获取 ============
int CHikDevice::GetCurrentFrame(unsigned char* pBuffer, int bufferSize, HikFrameInfo& frameInfo)
{
std::lock_guard<std::mutex> lock(m_frameMutex);
if (m_currentFrameData.empty()) {
return ERR_CODE(DEV_RESULT_EMPTY);
}
int dataSize = static_cast<int>(m_currentFrameData.size());
if (bufferSize < dataSize) {
return ERR_CODE(DATA_ERR_LEN);
}
memcpy(pBuffer, m_currentFrameData.data(), dataSize);
frameInfo = m_currentFrameInfo;
return dataSize;
}
const HikDeviceConfig& CHikDevice::GetConfig() const
{
return m_config;
}
// ============ 抓图 ============
int CHikDevice::CaptureToFile(const std::string& filePath, bool isJpeg)
{
if (m_lUserID < 0) {
LOG_ERR("CHikDevice::CaptureToFile - Not logged in\n");
return ERR_CODE(DEV_NO_OPEN);
}
if (isJpeg) {
// JPEG抓图
NET_DVR_JPEGPARA jpegPara = {0};
jpegPara.wPicSize = 0xFF; // 使用当前分辨率
jpegPara.wPicQuality = 0; // 最高质量
if (!NET_DVR_CaptureJPEGPicture(m_lUserID, m_config.channelNo, &jpegPara, (char*)filePath.c_str())) {
m_lastError = NET_DVR_GetLastError();
LOG_ERR("NET_DVR_CaptureJPEGPicture failed, error code: %d\n", m_lastError);
return ERR_CODE(DEV_CTRL_ERR);
}
} else {
// BMP抓图 (需要预览句柄)
if (m_lRealPlayHandle < 0) {
LOG_ERR("CHikDevice::CaptureToFile - Preview not started for BMP capture\n");
return ERR_CODE(DEV_NO_OPEN);
}
if (!NET_DVR_CapturePicture(m_lRealPlayHandle, (char*)filePath.c_str())) {
m_lastError = NET_DVR_GetLastError();
LOG_ERR("NET_DVR_CapturePicture failed, error code: %d\n", m_lastError);
return ERR_CODE(DEV_CTRL_ERR);
}
}
LOG_INFO("CHikDevice::CaptureToFile - Captured to: %s\n", filePath.c_str());
return SUCCESS;
}
// ============ 云台控制 ============
int CHikDevice::PTZControl(int command, bool stop, int speed)
{
if (m_lRealPlayHandle < 0) {
LOG_ERR("CHikDevice::PTZControl - Preview not started\n");
return ERR_CODE(DEV_NO_OPEN);
}
DWORD dwStop = stop ? 1 : 0;
if (!NET_DVR_PTZControlWithSpeed(m_lRealPlayHandle, command, dwStop, speed)) {
m_lastError = NET_DVR_GetLastError();
LOG_ERR("NET_DVR_PTZControlWithSpeed failed, error code: %d\n", m_lastError);
return ERR_CODE(DEV_CTRL_ERR);
}
return SUCCESS;
}
// ============ OSD配置 ============
int CHikDevice::ConfigureOSD(bool showChanName, bool showOsd)
{
LOG_INFO("CHikDevice::ConfigureOSD - showChanName: %d, showOsd: %d\n", showChanName, showOsd);
if (m_lUserID < 0) {
LOG_ERR("CHikDevice::ConfigureOSD - Not logged in\n");
return ERR_CODE(DEV_NO_OPEN);
}
// 获取当前图像参数
DWORD uiReturnLen = 0;
NET_DVR_PICCFG_V30 struPicCfg = {0};
struPicCfg.dwSize = sizeof(NET_DVR_PICCFG_V30);
if (!NET_DVR_GetDVRConfig(m_lUserID, NET_DVR_GET_PICCFG_V30, m_config.channelNo,
&struPicCfg, sizeof(NET_DVR_PICCFG_V30), &uiReturnLen)) {
m_lastError = NET_DVR_GetLastError();
LOG_ERR("NET_DVR_GetDVRConfig(PICCFG_V30) failed, error: %d\n", m_lastError);
return ERR_CODE(DEV_CTRL_ERR);
}
// 修改OSD设置
struPicCfg.dwShowChanName = showChanName ? 1 : 0; // 通道名称
struPicCfg.dwShowOsd = showOsd ? 1 : 0; // OSD时间
// 设置图像参数
if (!NET_DVR_SetDVRConfig(m_lUserID, NET_DVR_SET_PICCFG_V30, m_config.channelNo,
&struPicCfg, sizeof(NET_DVR_PICCFG_V30))) {
m_lastError = NET_DVR_GetLastError();
LOG_ERR("NET_DVR_SetDVRConfig(PICCFG_V30) failed, error: %d\n", m_lastError);
return ERR_CODE(DEV_CTRL_ERR);
}
LOG_INFO("CHikDevice::ConfigureOSD - OSD configured successfully\n");
return SUCCESS;
}
// ============ 静态回调函数实现 ============
void CALLBACK CHikDevice::HikRealDataCallback(LONG lRealHandle, DWORD dwDataType,
BYTE* pBuffer, DWORD dwBufSize, void* pUser)
{
CHikDevice* pDevice = static_cast<CHikDevice*>(pUser);
if (!pDevice) return;
switch (dwDataType) {
case NET_DVR_SYSHEAD:
{
// 系统头数据 - 初始化 PlayM4 解码器
LOG_DEBUG("CHikDevice received system header, size: %lu\n", dwBufSize);
// 获取 PlayM4 端口
if (pDevice->m_lPlayM4Port < 0) {
if (!PlayM4_GetPort(&pDevice->m_lPlayM4Port)) {
LOG_ERR("PlayM4_GetPort failed, error: %lu\n", PlayM4_GetLastError(pDevice->m_lPlayM4Port));
return;
}
}
// 设置流播放模式
if (!PlayM4_SetStreamOpenMode(pDevice->m_lPlayM4Port, STREAME_REALTIME)) {
LOG_ERR("PlayM4_SetStreamOpenMode failed, error: %lu\n", PlayM4_GetLastError(pDevice->m_lPlayM4Port));
return;
}
// 打开流减小缓冲区到350KB以降低延迟
if (!PlayM4_OpenStream(pDevice->m_lPlayM4Port, pBuffer, dwBufSize, 350 * 1024)) {
LOG_ERR("PlayM4_OpenStream failed, error: %lu\n", PlayM4_GetLastError(pDevice->m_lPlayM4Port));
return;
}
// 设置解码输出为 RGB32 格式避免手动YUV转换
if (!PlayM4_SetDecCBStream(pDevice->m_lPlayM4Port, T_RGB32)) {
LOG_WARN("PlayM4_SetDecCBStream(T_RGB32) failed, will use YV12 conversion\n");
}
// 注册端口到实例的映射解决64位系统指针截断问题
{
std::lock_guard<std::mutex> lock(s_mapMutex);
s_portDeviceMap[pDevice->m_lPlayM4Port] = pDevice;
}
// 设置解码回调通过端口号映射查找实例nUser 参数不使用)
#ifdef _WIN32
if (!PlayM4_SetDecCallBackMend(pDevice->m_lPlayM4Port, PlayM4DecodeCallback,
pDevice->m_lPlayM4Port)) {
#else
if (!PlayM4_SetDecCallBackMend(pDevice->m_lPlayM4Port, PlayM4DecodeCallback,
nullptr)) {
#endif
LOG_ERR("PlayM4_SetDecCallBackMend failed, error: %lu\n", PlayM4_GetLastError(pDevice->m_lPlayM4Port));
// 移除映射
{
std::lock_guard<std::mutex> lock(s_mapMutex);
s_portDeviceMap.erase(pDevice->m_lPlayM4Port);
}
return;
}
// 开始播放(不需要窗口句柄,仅解码)
if (!PlayM4_Play(pDevice->m_lPlayM4Port, NULL)) {
LOG_ERR("PlayM4_Play failed, error: %lu\n", PlayM4_GetLastError(pDevice->m_lPlayM4Port));
// 移除映射
{
std::lock_guard<std::mutex> lock(s_mapMutex);
s_portDeviceMap.erase(pDevice->m_lPlayM4Port);
}
return;
}
pDevice->m_bPlayM4Playing = true;
LOG_INFO("CHikDevice PlayM4 decoder initialized and started, port: %ld\n", pDevice->m_lPlayM4Port);
break;
}
case NET_DVR_STREAMDATA:
{
// 流数据 - 输入到 PlayM4 解码器
if (pDevice->m_lPlayM4Port >= 0 && pDevice->m_bPlayM4Playing) {
if (!PlayM4_InputData(pDevice->m_lPlayM4Port, pBuffer, dwBufSize)) {
// 缓冲区满时可能会失败,这是正常的
DWORD dwError = PlayM4_GetLastError(pDevice->m_lPlayM4Port);
if (dwError != PLAYM4_BUF_OVER) {
LOG_WARN("PlayM4_InputData failed, error: %lu\n", dwError);
}
}
}
break;
}
default:
break;
}
}
#ifdef _WIN32
void CALLBACK CHikDevice::PlayM4DecodeCallback(long nPort, char* pBuf, long nSize,
FRAME_INFO* pFrameInfo, long nUser, long nReserved2)
#else
void CALLBACK CHikDevice::PlayM4DecodeCallback(int nPort, char* pBuf, int nSize,
FRAME_INFO* pFrameInfo, void* nUser, int nReserved2)
#endif
{
(void)nUser; // 未使用,通过端口号查找实例
(void)nReserved2; // 未使用
if (!pBuf || !pFrameInfo) {
return;
}
// 通过端口号查找实例
CHikDevice* pDevice = nullptr;
{
std::lock_guard<std::mutex> lock(s_mapMutex);
auto it = s_portDeviceMap.find(nPort);
if (it != s_portDeviceMap.end()) {
pDevice = it->second;
}
}
if (!pDevice) {
return;
}
// 只处理视频帧YV12 或 RGB32 格式)
if (pFrameInfo->nType == T_YV12 || pFrameInfo->nType == T_RGB32) {
pDevice->ProcessDecodedFrame(pBuf, nSize, pFrameInfo);
}
}
void CALLBACK CHikDevice::HikSDKExceptionCallback(DWORD dwType, LONG lUserID, LONG lHandle, void* pUser)
{
CHikDevice* pDevice = static_cast<CHikDevice*>(pUser);
if (!pDevice) {
return;
}
pDevice->HandleException(dwType);
}
// ============ 内部方法实现 ============
void CHikDevice::ProcessDecodedFrame(char* pBuf, long nSize, FRAME_INFO* pFrameInfo)
{
if (!pBuf || !pFrameInfo) {
return;
}
int width = pFrameInfo->nWidth;
int height = pFrameInfo->nHeight;
int rgbSize = width * height * 4; // RGB32
// 更新当前帧数据
{
std::lock_guard<std::mutex> lock(m_frameMutex);
// 确保缓冲区大小足够
if (m_currentFrameData.size() != static_cast<size_t>(rgbSize)) {
m_currentFrameData.resize(rgbSize);
}
// 根据帧类型处理
if (pFrameInfo->nType == T_RGB32) {
// 直接使用 RGB32 数据PlayM4 已转换)
memcpy(m_currentFrameData.data(), pBuf, rgbSize);
} else if (pFrameInfo->nType == T_YV12) {
// YV12 格式需要手动转换
unsigned char* yPlane = reinterpret_cast<unsigned char*>(pBuf);
unsigned char* vPlane = yPlane + width * height;
unsigned char* uPlane = vPlane + (width / 2) * (height / 2);
ConvertYV12ToRGB32(yPlane, uPlane, vPlane, m_currentFrameData.data(), width, height);
} else {
LOG_WARN("Unsupported frame type: %d\n", pFrameInfo->nType);
return;
}
// 更新帧信息
m_currentFrameInfo.width = width;
m_currentFrameInfo.height = height;
m_currentFrameInfo.frameType = pFrameInfo->nType;
m_currentFrameInfo.timestamp = pFrameInfo->nStamp;
}
// 调用帧回调
if (m_frameCallback) {
m_frameCallback(m_currentFrameData.data(), rgbSize, m_currentFrameInfo, m_pFrameCallbackUser);
}
}
void CHikDevice::HandleException(DWORD dwType)
{
EHikExceptionType exType = EHikExceptionType::None;
switch (dwType) {
case EXCEPTION_RECONNECT:
LOG_INFO("CHikDevice reconnecting (preview)...\n");
exType = EHikExceptionType::Reconnect;
UpdateStatus(EHikDeviceStatus::Reconnecting);
break;
case EXCEPTION_PREVIEW:
LOG_WARN("CHikDevice preview exception\n");
exType = EHikExceptionType::PreviewException;
m_bPreviewing = false;
UpdateStatus(EHikDeviceStatus::Error);
break;
case EXCEPTION_SERIAL:
LOG_WARN("CHikDevice serial exception\n");
exType = EHikExceptionType::SerialException;
break;
case EXCEPTION_ALARMRECONNECT:
LOG_INFO("CHikDevice alarm reconnecting...\n");
exType = EHikExceptionType::AlarmReconnect;
break;
case EXCEPTION_EXCHANGE:
LOG_WARN("CHikDevice exchange exception\n");
exType = EHikExceptionType::ExchangeException;
break;
default:
LOG_DEBUG("CHikDevice unknown exception: %lu\n", dwType);
break;
}
// 调用异常回调
if (m_exceptionCallback && exType != EHikExceptionType::None) {
m_exceptionCallback(exType, m_pExceptionCallbackUser);
}
}
void CHikDevice::ConvertYV12ToRGB32(const unsigned char* yPlane, const unsigned char* uPlane,
const unsigned char* vPlane, unsigned char* rgbData,
int width, int height)
{
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
int yIndex = y * width + x;
int uvIndex = (y / 2) * (width / 2) + (x / 2);
int Y = yPlane[yIndex];
int U = uPlane[uvIndex] - 128;
int V = vPlane[uvIndex] - 128;
// YUV to RGB 转换公式
int R = static_cast<int>(Y + 1.402 * V);
int G = static_cast<int>(Y - 0.344 * U - 0.714 * V);
int B = static_cast<int>(Y + 1.772 * U);
// 限制范围
R = (std::max)(0, (std::min)(255, R));
G = (std::max)(0, (std::min)(255, G));
B = (std::max)(0, (std::min)(255, B));
// RGB32 格式: B G R A
int rgbIndex = yIndex * 4;
rgbData[rgbIndex + 0] = static_cast<unsigned char>(B);
rgbData[rgbIndex + 1] = static_cast<unsigned char>(G);
rgbData[rgbIndex + 2] = static_cast<unsigned char>(R);
rgbData[rgbIndex + 3] = 255; // Alpha
}
}
}