121 lines
3.9 KiB
C#
121 lines
3.9 KiB
C#
using System.Net;
|
||
using DotNext;
|
||
using Peripherals.PowerClient;
|
||
using WebProtocol;
|
||
|
||
namespace Peripherals.HdmiInClient;
|
||
|
||
static class HdmiInAddr
|
||
{
|
||
public const UInt32 BASE = 0xA000_0000;
|
||
public const UInt32 HdmiIn_CTRL = BASE + 0x0; //[0]: rstn, 0 is reset.
|
||
public const UInt32 HdmiIn_READFIFO = BASE + 0x1;
|
||
}
|
||
|
||
class HdmiIn
|
||
{
|
||
private static readonly NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();
|
||
|
||
readonly int timeout = 500;
|
||
readonly int taskID;
|
||
readonly int port;
|
||
readonly string address;
|
||
private IPEndPoint ep;
|
||
|
||
// 动态分辨率参数
|
||
private UInt16 _currentWidth = 960;
|
||
private UInt16 _currentHeight = 540;
|
||
private UInt32 _currentFrameLength = 960 * 540 * 2 / 4; // RGB565格式,2字节/像素,按4字节对齐
|
||
|
||
/// <summary>
|
||
/// 初始化HDMI输入客户端
|
||
/// </summary>
|
||
/// <param name="address">HDMI输入设备IP地址</param>
|
||
/// <param name="port">HDMI输入设备端口</param>
|
||
/// <param name="taskID">任务ID</param>
|
||
/// <param name="timeout">超时时间(毫秒)</param>
|
||
public HdmiIn(string address, int port, int taskID, int timeout = 500)
|
||
{
|
||
if (timeout < 0)
|
||
throw new ArgumentException("Timeout couldn't be negative", nameof(timeout));
|
||
this.address = address;
|
||
this.port = port;
|
||
this.ep = new IPEndPoint(IPAddress.Parse(address), port);
|
||
this.taskID = taskID;
|
||
this.timeout = timeout;
|
||
}
|
||
|
||
public async ValueTask<Result<bool>> EnableTrans(bool isEnable)
|
||
{
|
||
var ret = await UDPClientPool.WriteAddr(this.ep, this.taskID, HdmiInAddr.HdmiIn_CTRL, (isEnable ? 0x00000001u : 0x00000000u));
|
||
if (!ret.IsSuccessful)
|
||
{
|
||
logger.Error($"Failed to write HdmiIn_CTRL to HdmiIn at {this.address}:{this.port}, error: {ret.Error}");
|
||
return new(ret.Error);
|
||
}
|
||
if (!ret.Value)
|
||
{
|
||
logger.Error($"HdmiIn_CTRL write returned false for HdmiIn at {this.address}:{this.port}");
|
||
return false;
|
||
}
|
||
return true;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 读取一帧图像数据
|
||
/// </summary>
|
||
/// <returns>包含图像数据的字节数组</returns>
|
||
public async ValueTask<Result<byte[]>> ReadFrame()
|
||
{
|
||
// 只在第一次或出错时清除UDP缓冲区,避免每帧都清除造成延迟
|
||
// MsgBus.UDPServer.ClearUDPData(this.address, this.taskID);
|
||
|
||
logger.Trace($"Reading frame from HdmiIn {this.address}");
|
||
|
||
// 使用UDPClientPool读取图像帧数据
|
||
var result = await UDPClientPool.ReadAddr4BytesAsync(
|
||
this.ep,
|
||
this.taskID, // taskID
|
||
HdmiInAddr.HdmiIn_READFIFO,
|
||
(int)_currentFrameLength, // 使用当前分辨率的动态大小
|
||
BurstType.FixedBurst,
|
||
this.timeout);
|
||
|
||
if (!result.IsSuccessful)
|
||
{
|
||
logger.Error($"Failed to read frame from HdmiIn {this.address}:{this.port}, error: {result.Error}");
|
||
// 读取失败时清除缓冲区,为下次读取做准备
|
||
try
|
||
{
|
||
MsgBus.UDPServer.ClearUDPData(this.address, this.taskID);
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
logger.Warn($"Failed to clear UDP data after read error: {ex.Message}");
|
||
}
|
||
return new(result.Error);
|
||
}
|
||
|
||
logger.Trace($"Successfully read frame from HdmiIn {this.address}:{this.port}, data length: {result.Value.Length} bytes");
|
||
return result.Value;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取当前分辨率
|
||
/// </summary>
|
||
/// <returns>当前分辨率(宽度, 高度)</returns>
|
||
public (int Width, int Height) GetCurrentResolution()
|
||
{
|
||
return (_currentWidth, _currentHeight);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取当前帧长度
|
||
/// </summary>
|
||
/// <returns>当前帧长度</returns>
|
||
public UInt32 GetCurrentFrameLength()
|
||
{
|
||
return _currentFrameLength;
|
||
}
|
||
}
|