add: 添加分辨率设置逻辑
This commit is contained in:
		@@ -33,6 +33,26 @@ public class VideoStreamController : ControllerBase
 | 
			
		||||
        public int Port { get; set; }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 分辨率配置请求模型
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public class ResolutionConfigRequest
 | 
			
		||||
    {
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// 宽度
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        [Required]
 | 
			
		||||
        [Range(640, 1920, ErrorMessage = "宽度必须在640-1920范围内")]
 | 
			
		||||
        public int Width { get; set; }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// 高度
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        [Required]
 | 
			
		||||
        [Range(480, 1080, ErrorMessage = "高度必须在480-1080范围内")]
 | 
			
		||||
        public int Height { get; set; }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 初始化HTTP视频流控制器
 | 
			
		||||
    /// </summary>
 | 
			
		||||
@@ -233,4 +253,116 @@ public class VideoStreamController : ControllerBase
 | 
			
		||||
            return TypedResults.Ok(false);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 设置视频流分辨率
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="request">分辨率配置请求</param>
 | 
			
		||||
    /// <returns>设置结果</returns>
 | 
			
		||||
    [HttpPost("Resolution")]
 | 
			
		||||
    [EnableCors("Users")]
 | 
			
		||||
    [ProducesResponseType(typeof(object), StatusCodes.Status200OK)]
 | 
			
		||||
    [ProducesResponseType(typeof(string), StatusCodes.Status400BadRequest)]
 | 
			
		||||
    [ProducesResponseType(typeof(string), StatusCodes.Status500InternalServerError)]
 | 
			
		||||
    public async Task<IResult> SetResolution([FromBody] ResolutionConfigRequest request)
 | 
			
		||||
    {
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            logger.Info($"设置视频流分辨率为 {request.Width}x{request.Height}");
 | 
			
		||||
 | 
			
		||||
            var (isSuccess, message) = await _videoStreamService.SetResolutionAsync(request.Width, request.Height);
 | 
			
		||||
 | 
			
		||||
            if (isSuccess)
 | 
			
		||||
            {
 | 
			
		||||
                return TypedResults.Ok(new
 | 
			
		||||
                {
 | 
			
		||||
                    success = true,
 | 
			
		||||
                    message = message,
 | 
			
		||||
                    width = request.Width,
 | 
			
		||||
                    height = request.Height,
 | 
			
		||||
                    timestamp = DateTime.Now
 | 
			
		||||
                });
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                return TypedResults.BadRequest(new
 | 
			
		||||
                {
 | 
			
		||||
                    success = false,
 | 
			
		||||
                    message = message,
 | 
			
		||||
                    timestamp = DateTime.Now
 | 
			
		||||
                });
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        catch (Exception ex)
 | 
			
		||||
        {
 | 
			
		||||
            logger.Error(ex, $"设置分辨率为 {request.Width}x{request.Height} 失败");
 | 
			
		||||
            return TypedResults.InternalServerError($"设置分辨率失败: {ex.Message}");
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 获取当前分辨率
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <returns>当前分辨率信息</returns>
 | 
			
		||||
    [HttpGet("Resolution")]
 | 
			
		||||
    [EnableCors("Users")]
 | 
			
		||||
    [ProducesResponseType(typeof(object), StatusCodes.Status200OK)]
 | 
			
		||||
    [ProducesResponseType(typeof(string), StatusCodes.Status500InternalServerError)]
 | 
			
		||||
    public IResult GetCurrentResolution()
 | 
			
		||||
    {
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            logger.Info("获取当前视频流分辨率");
 | 
			
		||||
 | 
			
		||||
            var (width, height) = _videoStreamService.GetCurrentResolution();
 | 
			
		||||
 | 
			
		||||
            return TypedResults.Ok(new
 | 
			
		||||
            {
 | 
			
		||||
                width = width,
 | 
			
		||||
                height = height,
 | 
			
		||||
                resolution = $"{width}x{height}",
 | 
			
		||||
                timestamp = DateTime.Now
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
        catch (Exception ex)
 | 
			
		||||
        {
 | 
			
		||||
            logger.Error(ex, "获取当前分辨率失败");
 | 
			
		||||
            return TypedResults.InternalServerError($"获取当前分辨率失败: {ex.Message}");
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 获取支持的分辨率列表
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <returns>支持的分辨率列表</returns>
 | 
			
		||||
    [HttpGet("SupportedResolutions")]
 | 
			
		||||
    [EnableCors("Users")]
 | 
			
		||||
    [ProducesResponseType(typeof(object), StatusCodes.Status200OK)]
 | 
			
		||||
    [ProducesResponseType(typeof(string), StatusCodes.Status500InternalServerError)]
 | 
			
		||||
    public IResult GetSupportedResolutions()
 | 
			
		||||
    {
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            logger.Info("获取支持的分辨率列表");
 | 
			
		||||
 | 
			
		||||
            var resolutions = _videoStreamService.GetSupportedResolutions();
 | 
			
		||||
 | 
			
		||||
            return TypedResults.Ok(new
 | 
			
		||||
            {
 | 
			
		||||
                resolutions = resolutions.Select(r => new
 | 
			
		||||
                {
 | 
			
		||||
                    width = r.Width,
 | 
			
		||||
                    height = r.Height,
 | 
			
		||||
                    name = r.Name,
 | 
			
		||||
                    value = $"{r.Width}x{r.Height}"
 | 
			
		||||
                }),
 | 
			
		||||
                timestamp = DateTime.Now
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
        catch (Exception ex)
 | 
			
		||||
        {
 | 
			
		||||
            logger.Error(ex, "获取支持的分辨率列表失败");
 | 
			
		||||
            return TypedResults.InternalServerError($"获取支持的分辨率列表失败: {ex.Message}");
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -26,19 +26,13 @@ class Camera
 | 
			
		||||
 | 
			
		||||
    const uint CAM_I2C_ADDR = 0x3C;
 | 
			
		||||
    const Peripherals.I2cClient.I2cProtocol CAM_PROTO = Peripherals.I2cClient.I2cProtocol.SCCB;
 | 
			
		||||
    const UInt16 H_START = 0;      //default: 0
 | 
			
		||||
    const UInt16 V_START = 0;      //default: 0
 | 
			
		||||
    const UInt16 DVPHO = 640;      //default: 2592 (0xA20)
 | 
			
		||||
    const UInt16 DVPVO = 480;      //default: 1944 (0x798)
 | 
			
		||||
    const UInt16 H_END = H_START + 1500 - 1; //default: 2624-1 (0xA3F)
 | 
			
		||||
    const UInt16 V_END = V_START + 1300 - 1; //default: 1951-1 (0x79F)
 | 
			
		||||
    const UInt16 HTS = 1700;       //default: 2844 (0xB1C)
 | 
			
		||||
    const UInt16 VTS = 1500;       //default: 1968 (0x7B0)
 | 
			
		||||
    const UInt16 H_OFFSET = 16;    //default: 16 (0x10)
 | 
			
		||||
    const UInt16 V_OFFSET = 4;     //default: 4 (0x04)
 | 
			
		||||
    const byte PLL_MUX = 10;
 | 
			
		||||
    const UInt32 FrameAddr = 0x00;
 | 
			
		||||
    const UInt32 FrameLength = DVPHO * DVPVO * 16 / 32;
 | 
			
		||||
 | 
			
		||||
    // 动态分辨率参数
 | 
			
		||||
    private UInt16 _currentWidth = 640;
 | 
			
		||||
    private UInt16 _currentHeight = 480;
 | 
			
		||||
    private UInt32 _currentFrameLength = 640 * 480 * 2 / 4; // RGB565格式,2字节/像素,按4字节对齐
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
@@ -183,8 +177,7 @@ class Camera
 | 
			
		||||
            this.ep,
 | 
			
		||||
            this.taskID, // taskID
 | 
			
		||||
            FrameAddr,
 | 
			
		||||
            // ((int)FrameLength),
 | 
			
		||||
            1280*720/2,
 | 
			
		||||
            (int)(_currentWidth * _currentHeight * 2), // 使用当前分辨率的动态大小
 | 
			
		||||
            this.timeout);
 | 
			
		||||
 | 
			
		||||
        if (!result.IsSuccessful)
 | 
			
		||||
@@ -423,6 +416,67 @@ class Camera
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 切换摄像头分辨率
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="width">宽度</param>
 | 
			
		||||
    /// <param name="height">高度</param>
 | 
			
		||||
    /// <returns>配置结果</returns>
 | 
			
		||||
    public async ValueTask<Result<bool>> ChangeResolution(int width, int height)
 | 
			
		||||
    {
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            logger.Info($"正在切换摄像头分辨率到 {width}x{height}");
 | 
			
		||||
 | 
			
		||||
            Result<bool> result;
 | 
			
		||||
            switch ($"{width}x{height}")
 | 
			
		||||
            {
 | 
			
		||||
                case "640x480":
 | 
			
		||||
                    result = await ConfigureResolution640x480();
 | 
			
		||||
                    break;
 | 
			
		||||
                case "1280x720":
 | 
			
		||||
                    result = await ConfigureResolution1280x720();
 | 
			
		||||
                    break;
 | 
			
		||||
                default:
 | 
			
		||||
                    logger.Error($"不支持的分辨率: {width}x{height}");
 | 
			
		||||
                    return new(new ArgumentException($"不支持的分辨率: {width}x{height}"));
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (result.IsSuccessful)
 | 
			
		||||
            {
 | 
			
		||||
                _currentWidth = (UInt16)width;
 | 
			
		||||
                _currentHeight = (UInt16)height;
 | 
			
		||||
                _currentFrameLength = (UInt32)(width * height * 2 / 4); // RGB565格式,按4字节对齐
 | 
			
		||||
                logger.Info($"摄像头分辨率已切换到 {width}x{height}");
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return result;
 | 
			
		||||
        }
 | 
			
		||||
        catch (Exception ex)
 | 
			
		||||
        {
 | 
			
		||||
            logger.Error(ex, $"切换分辨率到 {width}x{height} 时发生错误");
 | 
			
		||||
            return new(ex);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 获取当前分辨率
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <returns>当前分辨率(宽度, 高度)</returns>
 | 
			
		||||
    public (int Width, int Height) GetCurrentResolution()
 | 
			
		||||
    {
 | 
			
		||||
        return (_currentWidth, _currentHeight);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 获取当前帧长度
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <returns>当前帧长度</returns>
 | 
			
		||||
    public UInt32 GetCurrentFrameLength()
 | 
			
		||||
    {
 | 
			
		||||
        return _currentFrameLength;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 复位摄像头
 | 
			
		||||
    /// </summary>
 | 
			
		||||
 
 | 
			
		||||
@@ -82,8 +82,11 @@ public class HttpVideoStreamService : BackgroundService
 | 
			
		||||
    private HttpListener? _httpListener;
 | 
			
		||||
    private readonly int _serverPort = 8080;
 | 
			
		||||
    private readonly int _frameRate = 30; // 30 FPS
 | 
			
		||||
    private readonly int _frameWidth = 1280;
 | 
			
		||||
    private readonly int _frameHeight = 720;
 | 
			
		||||
    
 | 
			
		||||
    // 动态分辨率配置
 | 
			
		||||
    private int _frameWidth = 640;  // 默认640x480
 | 
			
		||||
    private int _frameHeight = 480;
 | 
			
		||||
    private readonly object _resolutionLock = new object();
 | 
			
		||||
 | 
			
		||||
    // 摄像头客户端
 | 
			
		||||
    private Camera? _camera;
 | 
			
		||||
@@ -439,8 +442,16 @@ public class HttpVideoStreamService : BackgroundService
 | 
			
		||||
            // 获取当前帧
 | 
			
		||||
            var imageData = await GetFPGAImageData();
 | 
			
		||||
 | 
			
		||||
            // 获取当前分辨率
 | 
			
		||||
            int currentWidth, currentHeight;
 | 
			
		||||
            lock (_resolutionLock)
 | 
			
		||||
            {
 | 
			
		||||
                currentWidth = _frameWidth;
 | 
			
		||||
                currentHeight = _frameHeight;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // 直接使用Common.Image.ConvertRGB24ToJpeg进行转换
 | 
			
		||||
            var jpegResult = Common.Image.ConvertRGB24ToJpeg(imageData, _frameWidth, _frameHeight, 80);
 | 
			
		||||
            var jpegResult = Common.Image.ConvertRGB24ToJpeg(imageData, currentWidth, currentHeight, 80);
 | 
			
		||||
            if (!jpegResult.IsSuccessful)
 | 
			
		||||
            {
 | 
			
		||||
                logger.Error("RGB24转JPEG失败: {Error}", jpegResult.Error);
 | 
			
		||||
@@ -647,6 +658,14 @@ public class HttpVideoStreamService : BackgroundService
 | 
			
		||||
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            // 获取当前分辨率
 | 
			
		||||
            int currentWidth, currentHeight;
 | 
			
		||||
            lock (_resolutionLock)
 | 
			
		||||
            {
 | 
			
		||||
                currentWidth = _frameWidth;
 | 
			
		||||
                currentHeight = _frameHeight;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // 从摄像头读取帧数据
 | 
			
		||||
            var readStartTime = DateTime.UtcNow;
 | 
			
		||||
            var result = await currentCamera.ReadFrame();
 | 
			
		||||
@@ -662,15 +681,15 @@ public class HttpVideoStreamService : BackgroundService
 | 
			
		||||
            var rgb565Data = result.Value;
 | 
			
		||||
 | 
			
		||||
            // 验证数据长度是否正确
 | 
			
		||||
            if (!Common.Image.ValidateImageDataLength(rgb565Data, _frameWidth, _frameHeight, 2))
 | 
			
		||||
            if (!Common.Image.ValidateImageDataLength(rgb565Data, currentWidth, currentHeight, 2))
 | 
			
		||||
            {
 | 
			
		||||
                logger.Warn("摄像头数据长度不匹配,期望: {Expected}, 实际: {Actual}",
 | 
			
		||||
                    _frameWidth * _frameHeight * 2, rgb565Data.Length);
 | 
			
		||||
                    currentWidth * currentHeight * 2, rgb565Data.Length);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // 将 RGB565 转换为 RGB24
 | 
			
		||||
            var convertStartTime = DateTime.UtcNow;
 | 
			
		||||
            var rgb24Result = Common.Image.ConvertRGB565ToRGB24(rgb565Data, _frameWidth, _frameHeight, isLittleEndian: false);
 | 
			
		||||
            var rgb24Result = Common.Image.ConvertRGB565ToRGB24(rgb565Data, currentWidth, currentHeight, isLittleEndian: false);
 | 
			
		||||
            var convertEndTime = DateTime.UtcNow;
 | 
			
		||||
            var convertTime = (convertEndTime - convertStartTime).TotalMilliseconds;
 | 
			
		||||
 | 
			
		||||
@@ -708,8 +727,16 @@ public class HttpVideoStreamService : BackgroundService
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // 获取当前分辨率
 | 
			
		||||
        int currentWidth, currentHeight;
 | 
			
		||||
        lock (_resolutionLock)
 | 
			
		||||
        {
 | 
			
		||||
            currentWidth = _frameWidth;
 | 
			
		||||
            currentHeight = _frameHeight;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // 直接使用Common.Image.ConvertRGB24ToJpeg进行转换
 | 
			
		||||
        var jpegResult = Common.Image.ConvertRGB24ToJpeg(frameData, _frameWidth, _frameHeight, 80);
 | 
			
		||||
        var jpegResult = Common.Image.ConvertRGB24ToJpeg(frameData, currentWidth, currentHeight, 80);
 | 
			
		||||
        if (!jpegResult.IsSuccessful)
 | 
			
		||||
        {
 | 
			
		||||
            logger.Error("RGB24转JPEG失败: {Error}", jpegResult.Error);
 | 
			
		||||
@@ -904,4 +931,102 @@ public class HttpVideoStreamService : BackgroundService
 | 
			
		||||
 | 
			
		||||
        base.Dispose();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 设置视频流分辨率
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="width">宽度</param>
 | 
			
		||||
    /// <param name="height">高度</param>
 | 
			
		||||
    /// <returns>设置结果</returns>
 | 
			
		||||
    public async Task<(bool IsSuccess, string Message)> SetResolutionAsync(int width, int height)
 | 
			
		||||
    {
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            logger.Info($"正在设置视频流分辨率为 {width}x{height}");
 | 
			
		||||
 | 
			
		||||
            // 验证分辨率
 | 
			
		||||
            if (!IsSupportedResolution(width, height))
 | 
			
		||||
            {
 | 
			
		||||
                var message = $"不支持的分辨率: {width}x{height},支持的分辨率: 640x480, 1280x720";
 | 
			
		||||
                logger.Error(message);
 | 
			
		||||
                return (false, message);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            Camera? currentCamera = null;
 | 
			
		||||
            lock (_cameraLock)
 | 
			
		||||
            {
 | 
			
		||||
                currentCamera = _camera;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (currentCamera == null)
 | 
			
		||||
            {
 | 
			
		||||
                var message = "摄像头未配置,无法设置分辨率";
 | 
			
		||||
                logger.Error(message);
 | 
			
		||||
                return (false, message);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // 设置摄像头分辨率
 | 
			
		||||
            var cameraResult = await currentCamera.ChangeResolution(width, height);
 | 
			
		||||
            if (!cameraResult.IsSuccessful)
 | 
			
		||||
            {
 | 
			
		||||
                var message = $"设置摄像头分辨率失败: {cameraResult.Error}";
 | 
			
		||||
                logger.Error(message);
 | 
			
		||||
                return (false, message);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // 更新HTTP服务的分辨率配置
 | 
			
		||||
            lock (_resolutionLock)
 | 
			
		||||
            {
 | 
			
		||||
                _frameWidth = width;
 | 
			
		||||
                _frameHeight = height;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            var successMessage = $"视频流分辨率已成功设置为 {width}x{height}";
 | 
			
		||||
            logger.Info(successMessage);
 | 
			
		||||
            return (true, successMessage);
 | 
			
		||||
        }
 | 
			
		||||
        catch (Exception ex)
 | 
			
		||||
        {
 | 
			
		||||
            var message = $"设置分辨率时发生错误: {ex.Message}";
 | 
			
		||||
            logger.Error(ex, message);
 | 
			
		||||
            return (false, message);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 获取当前分辨率
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <returns>当前分辨率(宽度, 高度)</returns>
 | 
			
		||||
    public (int Width, int Height) GetCurrentResolution()
 | 
			
		||||
    {
 | 
			
		||||
        lock (_resolutionLock)
 | 
			
		||||
        {
 | 
			
		||||
            return (_frameWidth, _frameHeight);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 检查是否支持该分辨率
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="width">宽度</param>
 | 
			
		||||
    /// <param name="height">高度</param>
 | 
			
		||||
    /// <returns>是否支持</returns>
 | 
			
		||||
    private bool IsSupportedResolution(int width, int height)
 | 
			
		||||
    {
 | 
			
		||||
        var resolution = $"{width}x{height}";
 | 
			
		||||
        return resolution == "640x480" || resolution == "1280x720";
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 获取支持的分辨率列表
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <returns>支持的分辨率列表</returns>
 | 
			
		||||
    public List<(int Width, int Height, string Name)> GetSupportedResolutions()
 | 
			
		||||
    {
 | 
			
		||||
        return new List<(int, int, string)>
 | 
			
		||||
        {
 | 
			
		||||
            (640, 480, "640x480 (VGA)"),
 | 
			
		||||
            (1280, 720, "1280x720 (HD)")
 | 
			
		||||
        };
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										144
									
								
								src/APIClient.ts
									
									
									
									
									
								
							
							
						
						
									
										144
									
								
								src/APIClient.ts
									
									
									
									
									
								
							@@ -311,6 +311,150 @@ export class VideoStreamClient {
 | 
			
		||||
        }
 | 
			
		||||
        return Promise.resolve<boolean>(null as any);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 设置视频流分辨率
 | 
			
		||||
     * @param width 宽度
 | 
			
		||||
     * @param height 高度
 | 
			
		||||
     * @return 操作结果
 | 
			
		||||
     */
 | 
			
		||||
    setResolution(width: number, height: number): Promise<boolean> {
 | 
			
		||||
        let url_ = this.baseUrl + "/api/VideoStream/SetResolution";
 | 
			
		||||
        url_ = url_.replace(/[?&]$/, "");
 | 
			
		||||
 | 
			
		||||
        const content_ = JSON.stringify({ width: width, height: height });
 | 
			
		||||
 | 
			
		||||
        let options_: RequestInit = {
 | 
			
		||||
            method: "POST",
 | 
			
		||||
            body: content_,
 | 
			
		||||
            headers: {
 | 
			
		||||
                "Content-Type": "application/json",
 | 
			
		||||
                "Accept": "application/json"
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        return this.http.fetch(url_, options_).then((_response: Response) => {
 | 
			
		||||
            return this.processSetResolution(_response);
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected processSetResolution(response: Response): Promise<boolean> {
 | 
			
		||||
        const status = response.status;
 | 
			
		||||
        let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };
 | 
			
		||||
        if (status === 200) {
 | 
			
		||||
            return response.text().then((_responseText) => {
 | 
			
		||||
            let result200: any = null;
 | 
			
		||||
            let resultData200 = _responseText === "" ? null : JSON.parse(_responseText, this.jsonParseReviver);
 | 
			
		||||
                result200 = resultData200 !== undefined ? resultData200 : <any>null;
 | 
			
		||||
    
 | 
			
		||||
            return result200;
 | 
			
		||||
            });
 | 
			
		||||
        } else if (status === 500) {
 | 
			
		||||
            return response.text().then((_responseText) => {
 | 
			
		||||
            let result500: any = null;
 | 
			
		||||
            let resultData500 = _responseText === "" ? null : JSON.parse(_responseText, this.jsonParseReviver);
 | 
			
		||||
            result500 = Exception.fromJS(resultData500);
 | 
			
		||||
            return throwException("A server side error occurred.", status, _responseText, _headers, result500);
 | 
			
		||||
            });
 | 
			
		||||
        } else if (status !== 200 && status !== 204) {
 | 
			
		||||
            return response.text().then((_responseText) => {
 | 
			
		||||
            return throwException("An unexpected server error occurred.", status, _responseText, _headers);
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
        return Promise.resolve<boolean>(null as any);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 获取当前视频流分辨率
 | 
			
		||||
     * @return 当前分辨率信息
 | 
			
		||||
     */
 | 
			
		||||
    getCurrentResolution(): Promise<any> {
 | 
			
		||||
        let url_ = this.baseUrl + "/api/VideoStream/GetCurrentResolution";
 | 
			
		||||
        url_ = url_.replace(/[?&]$/, "");
 | 
			
		||||
 | 
			
		||||
        let options_: RequestInit = {
 | 
			
		||||
            method: "GET",
 | 
			
		||||
            headers: {
 | 
			
		||||
                "Accept": "application/json"
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        return this.http.fetch(url_, options_).then((_response: Response) => {
 | 
			
		||||
            return this.processGetCurrentResolution(_response);
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected processGetCurrentResolution(response: Response): Promise<any> {
 | 
			
		||||
        const status = response.status;
 | 
			
		||||
        let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };
 | 
			
		||||
        if (status === 200) {
 | 
			
		||||
            return response.text().then((_responseText) => {
 | 
			
		||||
            let result200: any = null;
 | 
			
		||||
            let resultData200 = _responseText === "" ? null : JSON.parse(_responseText, this.jsonParseReviver);
 | 
			
		||||
                result200 = resultData200 !== undefined ? resultData200 : <any>null;
 | 
			
		||||
    
 | 
			
		||||
            return result200;
 | 
			
		||||
            });
 | 
			
		||||
        } else if (status === 500) {
 | 
			
		||||
            return response.text().then((_responseText) => {
 | 
			
		||||
            let result500: any = null;
 | 
			
		||||
            let resultData500 = _responseText === "" ? null : JSON.parse(_responseText, this.jsonParseReviver);
 | 
			
		||||
            result500 = Exception.fromJS(resultData500);
 | 
			
		||||
            return throwException("A server side error occurred.", status, _responseText, _headers, result500);
 | 
			
		||||
            });
 | 
			
		||||
        } else if (status !== 200 && status !== 204) {
 | 
			
		||||
            return response.text().then((_responseText) => {
 | 
			
		||||
            return throwException("An unexpected server error occurred.", status, _responseText, _headers);
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
        return Promise.resolve<any>(null as any);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 获取支持的视频流分辨率列表
 | 
			
		||||
     * @return 支持的分辨率列表
 | 
			
		||||
     */
 | 
			
		||||
    getSupportedResolutions(): Promise<any[]> {
 | 
			
		||||
        let url_ = this.baseUrl + "/api/VideoStream/GetSupportedResolutions";
 | 
			
		||||
        url_ = url_.replace(/[?&]$/, "");
 | 
			
		||||
 | 
			
		||||
        let options_: RequestInit = {
 | 
			
		||||
            method: "GET",
 | 
			
		||||
            headers: {
 | 
			
		||||
                "Accept": "application/json"
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        return this.http.fetch(url_, options_).then((_response: Response) => {
 | 
			
		||||
            return this.processGetSupportedResolutions(_response);
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected processGetSupportedResolutions(response: Response): Promise<any[]> {
 | 
			
		||||
        const status = response.status;
 | 
			
		||||
        let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };
 | 
			
		||||
        if (status === 200) {
 | 
			
		||||
            return response.text().then((_responseText) => {
 | 
			
		||||
            let result200: any = null;
 | 
			
		||||
            let resultData200 = _responseText === "" ? null : JSON.parse(_responseText, this.jsonParseReviver);
 | 
			
		||||
                result200 = resultData200 !== undefined ? resultData200 : <any>null;
 | 
			
		||||
    
 | 
			
		||||
            return result200;
 | 
			
		||||
            });
 | 
			
		||||
        } else if (status === 500) {
 | 
			
		||||
            return response.text().then((_responseText) => {
 | 
			
		||||
            let result500: any = null;
 | 
			
		||||
            let resultData500 = _responseText === "" ? null : JSON.parse(_responseText, this.jsonParseReviver);
 | 
			
		||||
            result500 = Exception.fromJS(resultData500);
 | 
			
		||||
            return throwException("A server side error occurred.", status, _responseText, _headers, result500);
 | 
			
		||||
            });
 | 
			
		||||
        } else if (status !== 200 && status !== 204) {
 | 
			
		||||
            return response.text().then((_responseText) => {
 | 
			
		||||
            return throwException("An unexpected server error occurred.", status, _responseText, _headers);
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
        return Promise.resolve<any[]>(null as any);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export class BsdlParserClient {
 | 
			
		||||
 
 | 
			
		||||
@@ -8,7 +8,7 @@
 | 
			
		||||
          控制面板
 | 
			
		||||
        </h2>
 | 
			
		||||
 | 
			
		||||
        <div class="grid grid-cols-1 md:grid-cols-3 gap-4">
 | 
			
		||||
        <div class="grid grid-cols-1 md:grid-cols-4 gap-4">
 | 
			
		||||
          <!-- 服务状态 -->
 | 
			
		||||
          <div class="stats shadow">
 | 
			
		||||
            <div class="stat bg-base-100">
 | 
			
		||||
@@ -42,6 +42,38 @@
 | 
			
		||||
            </div>
 | 
			
		||||
          </div>
 | 
			
		||||
 | 
			
		||||
          <!-- 分辨率控制 -->
 | 
			
		||||
          <div class="stats shadow">
 | 
			
		||||
            <div class="stat bg-base-100">
 | 
			
		||||
              <div class="stat-figure text-info">
 | 
			
		||||
                <Settings class="w-8 h-8" />
 | 
			
		||||
              </div>
 | 
			
		||||
              <div class="stat-title">分辨率设置</div>
 | 
			
		||||
              <div class="stat-value text-sm">
 | 
			
		||||
                <select 
 | 
			
		||||
                  class="select select-sm select-bordered max-w-xs" 
 | 
			
		||||
                  v-model="selectedResolution"
 | 
			
		||||
                  @change="changeResolution"
 | 
			
		||||
                  :disabled="changingResolution"
 | 
			
		||||
                >
 | 
			
		||||
                  <option v-for="res in supportedResolutions" :key="`${res.width}x${res.height}`" :value="res">
 | 
			
		||||
                    {{ res.width }}×{{ res.height }}
 | 
			
		||||
                  </option>
 | 
			
		||||
                </select>
 | 
			
		||||
              </div>
 | 
			
		||||
              <div class="stat-desc">
 | 
			
		||||
                <button 
 | 
			
		||||
                  class="btn btn-xs btn-outline btn-info mt-1"
 | 
			
		||||
                  @click="refreshResolutions"
 | 
			
		||||
                  :disabled="loadingResolutions"
 | 
			
		||||
                >
 | 
			
		||||
                  <RefreshCw v-if="loadingResolutions" class="animate-spin h-3 w-3" />
 | 
			
		||||
                  {{ loadingResolutions ? "刷新中..." : "刷新" }}
 | 
			
		||||
                </button>
 | 
			
		||||
              </div>
 | 
			
		||||
            </div>
 | 
			
		||||
          </div>
 | 
			
		||||
 | 
			
		||||
          <!-- 连接数 -->
 | 
			
		||||
          <div class="stats shadow">
 | 
			
		||||
            <div class="stat bg-base-100 relative">
 | 
			
		||||
@@ -321,6 +353,15 @@ const isPlaying = ref(false);
 | 
			
		||||
const hasVideoError = ref(false);
 | 
			
		||||
const videoStatus = ref('点击"播放视频流"按钮开始查看实时视频');
 | 
			
		||||
 | 
			
		||||
// 分辨率相关状态
 | 
			
		||||
const changingResolution = ref(false);
 | 
			
		||||
const loadingResolutions = ref(false);
 | 
			
		||||
const selectedResolution = ref({ width: 640, height: 480 });
 | 
			
		||||
const supportedResolutions = ref([
 | 
			
		||||
  { width: 640, height: 480 },
 | 
			
		||||
  { width: 1280, height: 720 }
 | 
			
		||||
]);
 | 
			
		||||
 | 
			
		||||
// 数据
 | 
			
		||||
const statusInfo = ref({
 | 
			
		||||
  isRunning: false,
 | 
			
		||||
@@ -549,6 +590,69 @@ const startStream = async () => {
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// 分辨率相关方法
 | 
			
		||||
// 获取支持的分辨率列表
 | 
			
		||||
const refreshResolutions = async () => {
 | 
			
		||||
  loadingResolutions.value = true;
 | 
			
		||||
  try {
 | 
			
		||||
    addLog("info", "正在获取支持的分辨率列表...");
 | 
			
		||||
    const resolutions = await videoClient.getSupportedResolutions();
 | 
			
		||||
    supportedResolutions.value = resolutions;
 | 
			
		||||
    
 | 
			
		||||
    // 获取当前分辨率
 | 
			
		||||
    const currentRes = await videoClient.getCurrentResolution();
 | 
			
		||||
    selectedResolution.value = currentRes;
 | 
			
		||||
    
 | 
			
		||||
    addLog("success", "分辨率列表获取成功");
 | 
			
		||||
  } catch (error) {
 | 
			
		||||
    addLog("error", `获取分辨率列表失败: ${error}`);
 | 
			
		||||
    console.error("获取分辨率列表失败:", error);
 | 
			
		||||
  } finally {
 | 
			
		||||
    loadingResolutions.value = false;
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// 切换分辨率
 | 
			
		||||
const changeResolution = async () => {
 | 
			
		||||
  if (!selectedResolution.value) return;
 | 
			
		||||
  
 | 
			
		||||
  changingResolution.value = true;
 | 
			
		||||
  const wasPlaying = isPlaying.value;
 | 
			
		||||
  
 | 
			
		||||
  try {
 | 
			
		||||
    addLog("info", `正在切换分辨率到 ${selectedResolution.value.width}×${selectedResolution.value.height}...`);
 | 
			
		||||
    
 | 
			
		||||
    // 如果正在播放,先停止视频流
 | 
			
		||||
    if (wasPlaying) {
 | 
			
		||||
      stopStream();
 | 
			
		||||
      await new Promise(resolve => setTimeout(resolve, 1000)); // 等待1秒
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    // 设置新分辨率
 | 
			
		||||
    const success = await videoClient.setResolution(selectedResolution.value.width, selectedResolution.value.height);
 | 
			
		||||
    
 | 
			
		||||
    if (success) {
 | 
			
		||||
      // 刷新流信息
 | 
			
		||||
      await refreshStatus();
 | 
			
		||||
      
 | 
			
		||||
      // 如果之前在播放,重新启动视频流
 | 
			
		||||
      if (wasPlaying) {
 | 
			
		||||
        await new Promise(resolve => setTimeout(resolve, 500)); // 短暂延迟
 | 
			
		||||
        await startStream();
 | 
			
		||||
      }
 | 
			
		||||
      
 | 
			
		||||
      addLog("success", `分辨率已切换到 ${selectedResolution.value.width}×${selectedResolution.value.height}`);
 | 
			
		||||
    } else {
 | 
			
		||||
      addLog("error", "分辨率切换失败");
 | 
			
		||||
    }
 | 
			
		||||
  } catch (error) {
 | 
			
		||||
    addLog("error", `分辨率切换失败: ${error}`);
 | 
			
		||||
    console.error("分辨率切换失败:", error);
 | 
			
		||||
  } finally {
 | 
			
		||||
    changingResolution.value = false;
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// 停止视频流
 | 
			
		||||
const stopStream = () => {
 | 
			
		||||
  try {
 | 
			
		||||
@@ -574,6 +678,7 @@ const stopStream = () => {
 | 
			
		||||
onMounted(async () => {
 | 
			
		||||
  addLog("info", "HTTP 视频流页面已加载");
 | 
			
		||||
  await refreshStatus();
 | 
			
		||||
  await refreshResolutions(); // 初始化分辨率信息
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
onUnmounted(() => {
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user