feat: 修改视频流后端服务,使其适配jpeg格式
This commit is contained in:
		@@ -100,6 +100,51 @@ class HdmiIn
 | 
			
		||||
        return result.Value;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public async ValueTask<(byte[] header, byte[] data, byte[] footer)?> GetMJpegFrame()
 | 
			
		||||
    {
 | 
			
		||||
        // 从HDMI读取RGB24数据
 | 
			
		||||
        var readStartTime = DateTime.UtcNow;
 | 
			
		||||
        var frameResult = await ReadFrame();
 | 
			
		||||
        var readEndTime = DateTime.UtcNow;
 | 
			
		||||
        var readTime = (readEndTime - readStartTime).TotalMilliseconds;
 | 
			
		||||
 | 
			
		||||
        if (!frameResult.IsSuccessful || frameResult.Value == null)
 | 
			
		||||
        {
 | 
			
		||||
            logger.Warn("HDMI帧读取失败或为空");
 | 
			
		||||
            return null;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        var rgb24Data = frameResult.Value;
 | 
			
		||||
 | 
			
		||||
        // 验证数据长度是否正确 (RGB24为每像素2字节)
 | 
			
		||||
        var expectedLength = _currentWidth * _currentHeight * 2;
 | 
			
		||||
        if (rgb24Data.Length != expectedLength)
 | 
			
		||||
        {
 | 
			
		||||
            logger.Warn("HDMI数据长度不匹配,期望: {Expected}, 实际: {Actual}",
 | 
			
		||||
                expectedLength, rgb24Data.Length);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // 将RGB24转换为JPEG(参考Camera版本的处理)
 | 
			
		||||
        var jpegStartTime = DateTime.UtcNow;
 | 
			
		||||
        var jpegResult = Common.Image.ConvertRGB24ToJpeg(rgb24Data, _currentWidth, _currentHeight, 80);
 | 
			
		||||
        var jpegEndTime = DateTime.UtcNow;
 | 
			
		||||
        var jpegTime = (jpegEndTime - jpegStartTime).TotalMilliseconds;
 | 
			
		||||
 | 
			
		||||
        if (!jpegResult.IsSuccessful)
 | 
			
		||||
        {
 | 
			
		||||
            logger.Error("HDMI RGB24转JPEG失败: {Error}", jpegResult.Error);
 | 
			
		||||
            return null;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        var jpegData = jpegResult.Value;
 | 
			
		||||
 | 
			
		||||
        // 发送MJPEG帧(使用Camera版本的格式)
 | 
			
		||||
        var mjpegFrameHeader = Common.Image.CreateMjpegFrameHeader(jpegData.Length);
 | 
			
		||||
        var mjpegFrameFooter = Common.Image.CreateMjpegFrameFooter();
 | 
			
		||||
 | 
			
		||||
        return (mjpegFrameHeader, jpegData, mjpegFrameFooter);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 获取当前分辨率
 | 
			
		||||
    /// </summary>
 | 
			
		||||
 
 | 
			
		||||
@@ -32,46 +32,38 @@ public class HttpHdmiVideoStreamService : BackgroundService
 | 
			
		||||
 | 
			
		||||
    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
 | 
			
		||||
    {
 | 
			
		||||
        try
 | 
			
		||||
        while (!stoppingToken.IsCancellationRequested)
 | 
			
		||||
        {
 | 
			
		||||
            while (!stoppingToken.IsCancellationRequested)
 | 
			
		||||
            if (_httpListener == null) continue;
 | 
			
		||||
            try
 | 
			
		||||
            {
 | 
			
		||||
                HttpListenerContext? context = null;
 | 
			
		||||
                try
 | 
			
		||||
                logger.Debug("Waiting for HTTP request...");
 | 
			
		||||
                var contextTask = _httpListener.GetContextAsync();
 | 
			
		||||
                var completedTask = await Task.WhenAny(contextTask, Task.Delay(-1, stoppingToken));
 | 
			
		||||
                if (completedTask == contextTask)
 | 
			
		||||
                {
 | 
			
		||||
                    logger.Debug("Waiting for HTTP request...");
 | 
			
		||||
                    context = await _httpListener.GetContextAsync();
 | 
			
		||||
                    var context = contextTask.Result;
 | 
			
		||||
                    logger.Debug($"Received request: {context.Request.Url?.AbsolutePath}");
 | 
			
		||||
                    if (context != null)
 | 
			
		||||
                        _ = HandleRequestAsync(context, stoppingToken);
 | 
			
		||||
                }
 | 
			
		||||
                catch (ObjectDisposedException)
 | 
			
		||||
                else
 | 
			
		||||
                {
 | 
			
		||||
                    // Listener closed, exit loop
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
                catch (HttpListenerException)
 | 
			
		||||
                {
 | 
			
		||||
                    // Listener closed, exit loop
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
                catch (Exception ex)
 | 
			
		||||
                {
 | 
			
		||||
                    logger.Error(ex, "Error in GetContextAsync");
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
                if (context != null)
 | 
			
		||||
                    _ = HandleRequestAsync(context, stoppingToken);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        finally
 | 
			
		||||
        {
 | 
			
		||||
            _httpListener?.Close();
 | 
			
		||||
            logger.Info("HDMI Video Stream Service stopped.");
 | 
			
		||||
            catch (Exception ex)
 | 
			
		||||
            {
 | 
			
		||||
                logger.Error(ex, "Error in GetContextAsync");
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public override async Task StopAsync(CancellationToken cancellationToken)
 | 
			
		||||
    {
 | 
			
		||||
        logger.Info("Stopping HDMI Video Stream Service...");
 | 
			
		||||
        _httpListener?.Close();
 | 
			
		||||
 | 
			
		||||
        // 禁用所有活跃的HDMI传输
 | 
			
		||||
        var disableTasks = new List<Task>();
 | 
			
		||||
@@ -229,9 +221,6 @@ public class HttpHdmiVideoStreamService : BackgroundService
 | 
			
		||||
        {
 | 
			
		||||
            logger.Debug("处理HDMI快照请求");
 | 
			
		||||
 | 
			
		||||
            const int frameWidth = 960;  // HDMI输入分辨率
 | 
			
		||||
            const int frameHeight = 540;
 | 
			
		||||
 | 
			
		||||
            // 从HDMI读取RGB565数据
 | 
			
		||||
            var frameResult = await hdmiIn.ReadFrame();
 | 
			
		||||
            if (!frameResult.IsSuccessful || frameResult.Value == null)
 | 
			
		||||
@@ -244,41 +233,7 @@ public class HttpHdmiVideoStreamService : BackgroundService
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            var rgb565Data = frameResult.Value;
 | 
			
		||||
 | 
			
		||||
            // 验证数据长度
 | 
			
		||||
            var expectedLength = frameWidth * frameHeight * 2;
 | 
			
		||||
            if (rgb565Data.Length != expectedLength)
 | 
			
		||||
            {
 | 
			
		||||
                logger.Warn("HDMI快照数据长度不匹配,期望: {Expected}, 实际: {Actual}",
 | 
			
		||||
                    expectedLength, rgb565Data.Length);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // 将RGB565转换为RGB24
 | 
			
		||||
            var rgb24Result = Common.Image.ConvertRGB565ToRGB24(rgb565Data, frameWidth, frameHeight, isLittleEndian: false);
 | 
			
		||||
            if (!rgb24Result.IsSuccessful)
 | 
			
		||||
            {
 | 
			
		||||
                logger.Error("HDMI快照RGB565转RGB24失败: {Error}", rgb24Result.Error);
 | 
			
		||||
                response.StatusCode = 500;
 | 
			
		||||
                var errorBytes = System.Text.Encoding.UTF8.GetBytes("Failed to process HDMI snapshot");
 | 
			
		||||
                await response.OutputStream.WriteAsync(errorBytes, 0, errorBytes.Length, cancellationToken);
 | 
			
		||||
                response.Close();
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // 将RGB24转换为JPEG
 | 
			
		||||
            var jpegResult = Common.Image.ConvertRGB24ToJpeg(rgb24Result.Value, frameWidth, frameHeight, 80);
 | 
			
		||||
            if (!jpegResult.IsSuccessful)
 | 
			
		||||
            {
 | 
			
		||||
                logger.Error("HDMI快照RGB24转JPEG失败: {Error}", jpegResult.Error);
 | 
			
		||||
                response.StatusCode = 500;
 | 
			
		||||
                var errorBytes = System.Text.Encoding.UTF8.GetBytes("Failed to encode HDMI snapshot");
 | 
			
		||||
                await response.OutputStream.WriteAsync(errorBytes, 0, errorBytes.Length, cancellationToken);
 | 
			
		||||
                response.Close();
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            var jpegData = jpegResult.Value;
 | 
			
		||||
            var jpegData = frameResult.Value;
 | 
			
		||||
 | 
			
		||||
            // 设置响应头(参考Camera版本)
 | 
			
		||||
            response.ContentType = "image/jpeg";
 | 
			
		||||
@@ -314,8 +269,6 @@ public class HttpHdmiVideoStreamService : BackgroundService
 | 
			
		||||
            logger.Debug("开始HDMI MJPEG流传输");
 | 
			
		||||
 | 
			
		||||
            int frameCounter = 0;
 | 
			
		||||
            const int frameWidth = 960;  // HDMI输入分辨率
 | 
			
		||||
            const int frameHeight = 540;
 | 
			
		||||
 | 
			
		||||
            while (!cancellationToken.IsCancellationRequested)
 | 
			
		||||
            {
 | 
			
		||||
@@ -323,61 +276,13 @@ public class HttpHdmiVideoStreamService : BackgroundService
 | 
			
		||||
                {
 | 
			
		||||
                    var frameStartTime = DateTime.UtcNow;
 | 
			
		||||
 | 
			
		||||
                    // 从HDMI读取RGB565数据
 | 
			
		||||
                    var readStartTime = DateTime.UtcNow;
 | 
			
		||||
                    var frameResult = await hdmiIn.ReadFrame();
 | 
			
		||||
                    var readEndTime = DateTime.UtcNow;
 | 
			
		||||
                    var readTime = (readEndTime - readStartTime).TotalMilliseconds;
 | 
			
		||||
                    var ret = await hdmiIn.GetMJpegFrame();
 | 
			
		||||
                    if (ret == null) continue;
 | 
			
		||||
                    var frame = ret.Value;
 | 
			
		||||
 | 
			
		||||
                    if (!frameResult.IsSuccessful || frameResult.Value == null)
 | 
			
		||||
                    {
 | 
			
		||||
                        logger.Warn("HDMI帧读取失败或为空");
 | 
			
		||||
                        continue;
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    var rgb565Data = frameResult.Value;
 | 
			
		||||
 | 
			
		||||
                    // 验证数据长度是否正确 (RGB565为每像素2字节)
 | 
			
		||||
                    var expectedLength = frameWidth * frameHeight * 2;
 | 
			
		||||
                    if (rgb565Data.Length != expectedLength)
 | 
			
		||||
                    {
 | 
			
		||||
                        logger.Warn("HDMI数据长度不匹配,期望: {Expected}, 实际: {Actual}",
 | 
			
		||||
                            expectedLength, rgb565Data.Length);
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    // 将RGB565转换为RGB24(参考Camera版本的处理)
 | 
			
		||||
                    var convertStartTime = DateTime.UtcNow;
 | 
			
		||||
                    var rgb24Result = Common.Image.ConvertRGB565ToRGB24(rgb565Data, frameWidth, frameHeight, isLittleEndian: false);
 | 
			
		||||
                    var convertEndTime = DateTime.UtcNow;
 | 
			
		||||
                    var convertTime = (convertEndTime - convertStartTime).TotalMilliseconds;
 | 
			
		||||
 | 
			
		||||
                    if (!rgb24Result.IsSuccessful)
 | 
			
		||||
                    {
 | 
			
		||||
                        logger.Error("HDMI RGB565转RGB24失败: {Error}", rgb24Result.Error);
 | 
			
		||||
                        continue;
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    // 将RGB24转换为JPEG(参考Camera版本的处理)
 | 
			
		||||
                    var jpegStartTime = DateTime.UtcNow;
 | 
			
		||||
                    var jpegResult = Common.Image.ConvertRGB24ToJpeg(rgb24Result.Value, frameWidth, frameHeight, 80);
 | 
			
		||||
                    var jpegEndTime = DateTime.UtcNow;
 | 
			
		||||
                    var jpegTime = (jpegEndTime - jpegStartTime).TotalMilliseconds;
 | 
			
		||||
 | 
			
		||||
                    if (!jpegResult.IsSuccessful)
 | 
			
		||||
                    {
 | 
			
		||||
                        logger.Error("HDMI RGB24转JPEG失败: {Error}", jpegResult.Error);
 | 
			
		||||
                        continue;
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    var jpegData = jpegResult.Value;
 | 
			
		||||
 | 
			
		||||
                    // 发送MJPEG帧(使用Camera版本的格式)
 | 
			
		||||
                    var mjpegFrameHeader = Common.Image.CreateMjpegFrameHeader(jpegData.Length);
 | 
			
		||||
                    var mjpegFrameFooter = Common.Image.CreateMjpegFrameFooter();
 | 
			
		||||
 | 
			
		||||
                    await response.OutputStream.WriteAsync(mjpegFrameHeader, 0, mjpegFrameHeader.Length, cancellationToken);
 | 
			
		||||
                    await response.OutputStream.WriteAsync(jpegData, 0, jpegData.Length, cancellationToken);
 | 
			
		||||
                    await response.OutputStream.WriteAsync(mjpegFrameFooter, 0, mjpegFrameFooter.Length, cancellationToken);
 | 
			
		||||
                    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++;
 | 
			
		||||
@@ -387,8 +292,8 @@ public class HttpHdmiVideoStreamService : BackgroundService
 | 
			
		||||
                    // 性能统计日志(每30帧记录一次)
 | 
			
		||||
                    if (frameCounter % 30 == 0)
 | 
			
		||||
                    {
 | 
			
		||||
                        logger.Debug("HDMI帧 {FrameNumber} 性能统计 - 读取: {ReadTime:F1}ms, RGB转换: {ConvertTime:F1}ms, JPEG转换: {JpegTime:F1}ms, 总计: {TotalTime:F1}ms, JPEG大小: {JpegSize} 字节",
 | 
			
		||||
                            frameCounter, readTime, convertTime, jpegTime, totalTime, jpegData.Length);
 | 
			
		||||
                        logger.Debug("HDMI帧 {FrameNumber} 性能统计 - 总计: {TotalTime:F1}ms, JPEG大小: {JpegSize} 字节",
 | 
			
		||||
                            frameCounter, totalTime, frame.data.Length);
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                catch (Exception ex)
 | 
			
		||||
 
 | 
			
		||||
@@ -1,8 +1,5 @@
 | 
			
		||||
#define USB_CAMERA
 | 
			
		||||
 | 
			
		||||
using System.Net;
 | 
			
		||||
using System.Text;
 | 
			
		||||
using System.Threading.Tasks;
 | 
			
		||||
using Peripherals.CameraClient; // 添加摄像头客户端引用
 | 
			
		||||
 | 
			
		||||
#if USB_CAMERA
 | 
			
		||||
@@ -414,7 +411,7 @@ public class HttpVideoStreamService : BackgroundService
 | 
			
		||||
                {
 | 
			
		||||
                    _usbCamera = new VideoCapture(1);
 | 
			
		||||
                    _usbCamera.Fps = _frameRate;
 | 
			
		||||
                    _usbCamera.FrameWidth = _frameWidth;    
 | 
			
		||||
                    _usbCamera.FrameWidth = _frameWidth;
 | 
			
		||||
                    _usbCamera.FrameHeight = _frameHeight;
 | 
			
		||||
                    _usbCameraEnable = _usbCamera.IsOpened();
 | 
			
		||||
                }
 | 
			
		||||
@@ -1003,38 +1000,6 @@ public class HttpVideoStreamService : BackgroundService
 | 
			
		||||
        logger.Info("HTTP 视频流服务已停止");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 释放资源
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public override void Dispose()
 | 
			
		||||
    {
 | 
			
		||||
        if (_httpListener != null)
 | 
			
		||||
        {
 | 
			
		||||
            if (_httpListener.IsListening)
 | 
			
		||||
            {
 | 
			
		||||
                _httpListener.Stop();
 | 
			
		||||
            }
 | 
			
		||||
            _httpListener.Close();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        lock (_clientsLock)
 | 
			
		||||
        {
 | 
			
		||||
            foreach (var client in _activeClients)
 | 
			
		||||
            {
 | 
			
		||||
                try { client.Close(); }
 | 
			
		||||
                catch { /* 忽略关闭错误 */ }
 | 
			
		||||
            }
 | 
			
		||||
            _activeClients.Clear();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        lock (_cameraLock)
 | 
			
		||||
        {
 | 
			
		||||
            _camera = null;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        base.Dispose();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 设置视频流分辨率
 | 
			
		||||
    /// </summary>
 | 
			
		||||
 
 | 
			
		||||
@@ -190,7 +190,7 @@ public class ProgressTrackerService : BackgroundService
 | 
			
		||||
            {
 | 
			
		||||
                logger.Error(ex, "Error during ProgressTracker cleanup");
 | 
			
		||||
            }
 | 
			
		||||
            await Task.Delay(TimeSpan.FromSeconds(30));
 | 
			
		||||
            await Task.Delay(TimeSpan.FromSeconds(30), stoppingToken);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user