feat: 使用DDR读取Hdmi视频流

This commit is contained in:
2025-08-19 15:20:17 +08:00
parent 7e53b805ae
commit 3c73aa344a
5 changed files with 326 additions and 157 deletions

View File

@@ -17,7 +17,7 @@ public class HdmiVideoStreamClient
{
public required HdmiIn HdmiInClient { get; set; }
public required Jpeg JpegClient { get; set; }
// public required Jpeg JpegClient { get; set; }
public required CancellationTokenSource CTS { get; set; }
@@ -102,7 +102,8 @@ public class HttpHdmiVideoStreamService : BackgroundService
var client = _clientDict[key];
client.CTS.Cancel();
var disableResult = await client.JpegClient.SetEnable(false);
// var disableResult = await client.JpegClient.SetEnable(false);
var disableResult = await client.HdmiInClient.SetTransEnable(false);
if (disableResult)
{
logger.Info("Successfully disabled HDMI transmission");
@@ -111,6 +112,8 @@ public class HttpHdmiVideoStreamService : BackgroundService
{
logger.Error($"Failed to disable HDMI transmission");
}
client.CTS = new CancellationTokenSource();
}
catch (Exception ex)
{
@@ -120,53 +123,51 @@ public class HttpHdmiVideoStreamService : BackgroundService
private async Task<HdmiVideoStreamClient?> GetOrCreateClientAsync(string boardId)
{
if (_clientDict.TryGetValue(boardId, out var client))
if (!_clientDict.TryGetValue(boardId, out var client))
{
client.Width = client.JpegClient.Width;
client.Height = client.JpegClient.Height;
return client;
var userManager = new Database.UserManager();
var boardRet = userManager.GetBoardByID(Guid.Parse(boardId));
if (!boardRet.IsSuccessful || !boardRet.Value.HasValue)
{
logger.Error($"Failed to get board with ID {boardId}");
return null;
}
var board = boardRet.Value.Value;
client = new HdmiVideoStreamClient()
{
HdmiInClient = new HdmiIn(board.IpAddr, board.Port, 1),
// JpegClient = new Jpeg(board.IpAddr, board.Port, 1),
CTS = new CancellationTokenSource(),
Offset = 0
};
}
var userManager = new Database.UserManager();
var boardRet = userManager.GetBoardByID(Guid.Parse(boardId));
if (!boardRet.IsSuccessful || !boardRet.Value.HasValue)
{
logger.Error($"Failed to get board with ID {boardId}");
return null;
}
var board = boardRet.Value.Value;
client = new HdmiVideoStreamClient()
{
HdmiInClient = new HdmiIn(board.IpAddr, board.Port, 1),
JpegClient = new Jpeg(board.IpAddr, board.Port, 1),
CTS = new CancellationTokenSource(),
Offset = 0
};
// 启用HDMI传输
try
{
// var hdmiEnableRet = await client.JpegClient.EnableTrans(true);
// if (!hdmiEnableRet.IsSuccessful)
// {
// logger.Error($"Failed to enable HDMI transmission for board {boardId}: {hdmiEnableRet.Error}");
// return null;
// }
// logger.Info($"Successfully enabled HDMI transmission for board {boardId}");
var jpegEnableRet = await client.JpegClient.Init(true);
if (!jpegEnableRet.IsSuccessful)
var hdmiEnableRet = await client.HdmiInClient.Init(true);
if (!hdmiEnableRet.IsSuccessful)
{
logger.Error($"Failed to enable JPEG transmission for board {boardId}: {jpegEnableRet.Error}");
logger.Error($"Failed to enable HDMI transmission for board {boardId}: {hdmiEnableRet.Error}");
return null;
}
logger.Info($"Successfully enabled JPEG transmission for board {boardId}");
logger.Info($"Successfully enabled HDMI transmission for board {boardId}");
client.Width = client.JpegClient.Width;
client.Height = client.JpegClient.Height;
// var jpegEnableRet = await client.JpegClient.Init(true);
// if (!jpegEnableRet.IsSuccessful)
// {
// logger.Error($"Failed to enable JPEG transmission for board {boardId}: {jpegEnableRet.Error}");
// return null;
// }
// logger.Info($"Successfully enabled JPEG transmission for board {boardId}");
client.Width = client.HdmiInClient.Width;
client.Height = client.HdmiInClient.Height;
// client.Width = client.JpegClient.Width;
// client.Height = client.JpegClient.Height;
}
catch (Exception ex)
{
@@ -195,15 +196,16 @@ public class HttpHdmiVideoStreamService : BackgroundService
return;
}
var hdmiInToken = _clientDict[boardId].CTS.Token;
var token = CancellationTokenSource.CreateLinkedTokenSource(
cancellationToken, client.CTS.Token).Token;
if (path == "/snapshot")
{
await HandleSnapshotRequestAsync(context.Response, client, hdmiInToken);
await HandleSnapshotRequestAsync(context.Response, client, token);
}
else if (path == "/mjpeg")
{
await HandleMjpegStreamAsync(context.Response, client, hdmiInToken);
await HandleMjpegStreamAsync(context.Response, client, token);
}
else if (path == "/video")
{
@@ -223,36 +225,47 @@ public class HttpHdmiVideoStreamService : BackgroundService
logger.Debug("处理HDMI快照请求");
// 从HDMI读取RGB565数据
var frameResult = await client.JpegClient.GetMultiFrames((uint)client.Offset);
if (!frameResult.IsSuccessful || frameResult.Value == null || frameResult.Value.Count == 0)
{
logger.Error("HDMI快照获取失败");
response.StatusCode = 500;
var errorBytes = System.Text.Encoding.UTF8.GetBytes("Failed to get HDMI snapshot");
await response.OutputStream.WriteAsync(errorBytes, 0, errorBytes.Length, cancellationToken);
response.Close();
return;
}
// var frameResult = await client.JpegClient.GetMultiFrames((uint)client.Offset);
// if (!frameResult.IsSuccessful || frameResult.Value == null || frameResult.Value.Count == 0)
// {
// logger.Error("HDMI快照获取失败");
// response.StatusCode = 500;
// var errorBytes = System.Text.Encoding.UTF8.GetBytes("Failed to get HDMI snapshot");
// await response.OutputStream.WriteAsync(errorBytes, 0, errorBytes.Length, cancellationToken);
// response.Close();
// return;
// }
var jpegData = frameResult.Value[0];
var quantTableResult = await client.JpegClient.GetQuantizationTable();
if (!quantTableResult.IsSuccessful || quantTableResult.Value == null)
// var jpegData = frameResult.Value[0];
// var quantTableResult = await client.JpegClient.GetQuantizationTable();
// if (!quantTableResult.IsSuccessful || quantTableResult.Value == null)
// {
// logger.Error("获取JPEG量化表失败: {Error}", quantTableResult.Error);
// response.StatusCode = 500;
// var errorBytes = System.Text.Encoding.UTF8.GetBytes("Failed to get quantization table");
// await response.OutputStream.WriteAsync(errorBytes, 0, errorBytes.Length, cancellationToken);
// response.Close();
// return;
// }
// var jpegImage = Common.Image.CompleteJpegData(jpegData, client.Width, client.Height, quantTableResult.Value);
// if (!jpegImage.IsSuccessful)
// {
// logger.Error("JPEG数据补全失败");
// response.StatusCode = 500;
// var errorBytes = System.Text.Encoding.UTF8.GetBytes("Failed to complete JPEG data");
// await response.OutputStream.WriteAsync(errorBytes, 0, errorBytes.Length, cancellationToken);
// response.Close();
// return;
// }
var jpegImage = await client.HdmiInClient.GetMJpegFrame();
if (!jpegImage.HasValue)
{
logger.Error("获取JPEG量化表失败: {Error}", quantTableResult.Error);
logger.Error("获取HDMI MJPEG帧失败");
response.StatusCode = 500;
var errorBytes = System.Text.Encoding.UTF8.GetBytes("Failed to get quantization table");
await response.OutputStream.WriteAsync(errorBytes, 0, errorBytes.Length, cancellationToken);
response.Close();
return;
}
var jpegImage = Common.Image.CompleteJpegData(jpegData, client.Width, client.Height, quantTableResult.Value);
if (!jpegImage.IsSuccessful)
{
logger.Error("JPEG数据补全失败");
response.StatusCode = 500;
var errorBytes = System.Text.Encoding.UTF8.GetBytes("Failed to complete JPEG data");
var errorBytes = System.Text.Encoding.UTF8.GetBytes("Failed to get HDMI MJPEG frame");
await response.OutputStream.WriteAsync(errorBytes, 0, errorBytes.Length, cancellationToken);
response.Close();
return;
@@ -260,13 +273,13 @@ public class HttpHdmiVideoStreamService : BackgroundService
// 设置响应头参考Camera版本
response.ContentType = "image/jpeg";
response.ContentLength64 = jpegImage.Value.Length;
response.ContentLength64 = jpegImage.Value.data.Length;
response.Headers.Add("Cache-Control", "no-cache, no-store, must-revalidate");
await response.OutputStream.WriteAsync(jpegImage.Value, 0, jpegImage.Value.Length, cancellationToken);
await response.OutputStream.WriteAsync(jpegImage.Value.data, 0, jpegImage.Value.data.Length, cancellationToken);
await response.OutputStream.FlushAsync(cancellationToken);
logger.Debug("已发送HDMI快照图像大小{Size} 字节", jpegImage.Value.Length);
logger.Debug("已发送HDMI快照图像大小{Size} 字节", jpegImage.Value.data.Length);
}
catch (Exception ex)
{
@@ -275,6 +288,7 @@ public class HttpHdmiVideoStreamService : BackgroundService
}
finally
{
response.StatusCode = 200;
response.Close();
}
}
@@ -292,17 +306,17 @@ public class HttpHdmiVideoStreamService : BackgroundService
logger.Debug("开始HDMI MJPEG流传输");
var quantTableResult = await client.JpegClient.GetQuantizationTable();
if (!quantTableResult.IsSuccessful || quantTableResult.Value == null)
{
logger.Error("获取JPEG量化表失败: {Error}", quantTableResult.Error);
response.StatusCode = 500;
await response.OutputStream.WriteAsync(
System.Text.Encoding.UTF8.GetBytes("Failed to get quantization table"), 0, 0, cancellationToken);
response.Close();
return;
}
var quantTable = quantTableResult.Value;
// var quantTableResult = await client.JpegClient.GetQuantizationTable();
// if (!quantTableResult.IsSuccessful || quantTableResult.Value == null)
// {
// logger.Error("获取JPEG量化表失败: {Error}", quantTableResult.Error);
// response.StatusCode = 500;
// await response.OutputStream.WriteAsync(
// System.Text.Encoding.UTF8.GetBytes("Failed to get quantization table"), 0, 0, cancellationToken);
// response.Close();
// return;
// }
// var quantTable = quantTableResult.Value;
int frameCounter = 0;
@@ -310,51 +324,74 @@ public class HttpHdmiVideoStreamService : BackgroundService
{
var frameStartTime = DateTime.UtcNow;
var frameResult =
await client.JpegClient.GetMultiFrames((uint)client.Offset);
if (!frameResult.IsSuccessful || frameResult.Value == null || frameResult.Value.Count == 0)
var frameRet = await client.HdmiInClient.GetMJpegFrame();
if (!frameRet.HasValue)
{
logger.Error("获取HDMI帧失败");
await Task.Delay(100, cancellationToken);
continue;
}
var frame = frameRet.Value;
foreach (var framebytes in frameResult.Value)
await response.OutputStream.WriteAsync(frame.header, 0, frame.header.Length, cancellationToken);
await response.OutputStream.WriteAsync(frame.data, 0, frame.data.Length, cancellationToken);
await response.OutputStream.WriteAsync(frame.footer, 0, frame.footer.Length, cancellationToken);
await response.OutputStream.FlushAsync(cancellationToken);
frameCounter++;
var totalTime = (DateTime.UtcNow - frameStartTime).TotalMilliseconds;
// 性能统计日志每30帧记录一次
if (frameCounter % 30 == 0)
{
var jpegImage = Common.Image.CompleteJpegData(framebytes, client.Width, client.Height, quantTable);
if (!jpegImage.IsSuccessful)
{
logger.Error("JPEG数据不完整");
await Task.Delay(100, cancellationToken);
continue;
}
var frameRet = Common.Image.CreateMjpegFrameFromJpeg(jpegImage.Value);
if (!frameRet.IsSuccessful)
{
logger.Error("创建MJPEG帧失败");
await Task.Delay(100, cancellationToken);
continue;
}
var frame = frameRet.Value;
await response.OutputStream.WriteAsync(frame.header, 0, frame.header.Length, cancellationToken);
await response.OutputStream.WriteAsync(frame.data, 0, frame.data.Length, cancellationToken);
await response.OutputStream.WriteAsync(frame.footer, 0, frame.footer.Length, cancellationToken);
await response.OutputStream.FlushAsync(cancellationToken);
frameCounter++;
var totalTime = (DateTime.UtcNow - frameStartTime).TotalMilliseconds;
// 性能统计日志每30帧记录一次
if (frameCounter % 30 == 0)
{
logger.Debug("HDMI帧 {FrameNumber} 性能统计 - 总计: {TotalTime:F1}ms, JPEG大小: {JpegSize} 字节",
frameCounter, totalTime, frame.data.Length);
}
logger.Debug("HDMI帧 {FrameNumber} 性能统计 - 总计: {TotalTime:F1}ms, JPEG大小: {JpegSize} 字节",
frameCounter, totalTime, frame.data.Length);
}
// var frameResult =
// await client.JpegClient.GetMultiFrames((uint)client.Offset);
// if (!frameResult.IsSuccessful || frameResult.Value == null || frameResult.Value.Count == 0)
// {
// logger.Error("获取HDMI帧失败");
// await Task.Delay(100, cancellationToken);
// continue;
// }
// foreach (var framebytes in frameResult.Value)
// {
// var jpegImage = Common.Image.CompleteJpegData(framebytes, client.Width, client.Height, quantTable);
// if (!jpegImage.IsSuccessful)
// {
// logger.Error("JPEG数据不完整");
// await Task.Delay(100, cancellationToken);
// continue;
// }
// var frameRet = Common.Image.CreateMjpegFrameFromJpeg(jpegImage.Value);
// if (!frameRet.IsSuccessful)
// {
// logger.Error("创建MJPEG帧失败");
// await Task.Delay(100, cancellationToken);
// continue;
// }
// var frame = frameRet.Value;
// await response.OutputStream.WriteAsync(frame.header, 0, frame.header.Length, cancellationToken); // await response.OutputStream.WriteAsync(frame.data, 0, frame.data.Length, cancellationToken);
// await response.OutputStream.WriteAsync(frame.footer, 0, frame.footer.Length, cancellationToken);
// await response.OutputStream.FlushAsync(cancellationToken);
// frameCounter++;
// var totalTime = (DateTime.UtcNow - frameStartTime).TotalMilliseconds;
// // 性能统计日志每30帧记录一次
// if (frameCounter % 30 == 0)
// {
// logger.Debug("HDMI帧 {FrameNumber} 性能统计 - 总计: {TotalTime:F1}ms, JPEG大小: {JpegSize} 字节",
// frameCounter, totalTime, frame.data.Length);
// }
// }
}
}
catch (Exception ex)
@@ -366,7 +403,7 @@ public class HttpHdmiVideoStreamService : BackgroundService
try
{
// 停止传输时禁用HDMI传输
await client.HdmiInClient.EnableTrans(false);
await client.HdmiInClient.SetTransEnable(false);
logger.Info("已禁用HDMI传输");
}
catch (Exception ex)