feat: 使用DDR读取Hdmi视频流
This commit is contained in:
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user