FPGA_WebLab/server/src/Controllers/VideoStreamController.cs

557 lines
18 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System.ComponentModel.DataAnnotations;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Cors;
using Microsoft.AspNetCore.Mvc;
/// <summary>
/// 视频流控制器,支持动态配置摄像头连接
/// </summary>
[ApiController]
[Route("api/[controller]")]
public class VideoStreamController : ControllerBase
{
private static NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();
private readonly server.Services.HttpVideoStreamService _videoStreamService;
/// <summary>
/// 视频流信息结构体
/// </summary>
public class StreamInfoResult
{
/// <summary>
/// TODO:
/// </summary>
public int FrameRate { get; set; }
/// <summary>
/// TODO:
/// </summary>
public int FrameWidth { get; set; }
/// <summary>
/// TODO:
/// </summary>
public int FrameHeight { get; set; }
/// <summary>
/// TODO:
/// </summary>
public string Format { get; set; } = "MJPEG";
/// <summary>
/// TODO:
/// </summary>
public string HtmlUrl { get; set; } = "";
/// <summary>
/// TODO:
/// </summary>
public string MjpegUrl { get; set; } = "";
/// <summary>
/// TODO:
/// </summary>
public string SnapshotUrl { get; set; } = "";
/// <summary>
/// TODO:
/// </summary>
public string UsbCameraUrl { get; set; } = "";
}
/// <summary>
/// 摄像头配置请求模型
/// </summary>
public class CameraConfigRequest
{
/// <summary>
/// 摄像头地址
/// </summary>
[Required]
[RegularExpression(@"^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$", ErrorMessage = "IP地址")]
public string Address { get; set; } = "";
/// <summary>
/// 摄像头端口
/// </summary>
[Required]
[Range(1, 65535, ErrorMessage = "端口必须在1-65535范围内")]
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>
/// <param name="videoStreamService">HTTP视频流服务</param>
public VideoStreamController(server.Services.HttpVideoStreamService videoStreamService)
{
logger.Info("创建VideoStreamController命名空间{Namespace}", this.GetType().Namespace);
_videoStreamService = videoStreamService;
}
/// <summary>
/// 获取 HTTP 视频流服务状态
/// </summary>
/// <returns>服务状态信息</returns>
[HttpGet("Status")]
[EnableCors("Users")]
[ProducesResponseType(typeof(object), StatusCodes.Status200OK)]
[ProducesResponseType(typeof(Exception), StatusCodes.Status500InternalServerError)]
public IResult GetStatus()
{
try
{
logger.Info("GetStatus方法被调用控制器{Controller}路径api/VideoStream/Status", this.GetType().Name);
// 使用HttpVideoStreamService提供的状态信息
var status = _videoStreamService.GetServiceStatus();
// 转换为小写首字母的JSON属性符合前端惯例
return TypedResults.Ok(status);
}
catch (Exception ex)
{
logger.Error(ex, "获取 HTTP 视频流服务状态失败");
return TypedResults.InternalServerError(ex.Message);
}
}
/// <summary>
/// 获取 HTTP 视频流信息
/// </summary>
/// <returns>流信息</returns>
[HttpGet("StreamInfo")]
[EnableCors("Users")]
[ProducesResponseType(typeof(StreamInfoResult), StatusCodes.Status200OK)]
[ProducesResponseType(typeof(Exception), StatusCodes.Status500InternalServerError)]
public IResult GetStreamInfo()
{
try
{
logger.Info("获取 HTTP 视频流信息");
var result = new StreamInfoResult
{
FrameRate = _videoStreamService.FrameRate,
FrameWidth = _videoStreamService.FrameWidth,
FrameHeight = _videoStreamService.FrameHeight,
Format = "MJPEG",
HtmlUrl = $"http://{Global.localhost}:{_videoStreamService.ServerPort}/video-feed.html",
MjpegUrl = $"http://{Global.localhost}:{_videoStreamService.ServerPort}/video-stream",
SnapshotUrl = $"http://{Global.localhost}:{_videoStreamService.ServerPort}/snapshot",
UsbCameraUrl = $"http://{Global.localhost}:{_videoStreamService.ServerPort}/usb-camera"
};
return TypedResults.Ok(result);
}
catch (Exception ex)
{
logger.Error(ex, "获取 HTTP 视频流信息失败");
return TypedResults.InternalServerError(ex.Message);
}
}
/// <summary>
/// 配置摄像头连接参数
/// </summary>
/// <param name="config">摄像头配置</param>
/// <returns>配置结果</returns>
[HttpPost("ConfigureCamera")]
[EnableCors("Users")]
[ProducesResponseType(typeof(object), StatusCodes.Status200OK)]
[ProducesResponseType(typeof(object), StatusCodes.Status400BadRequest)]
[ProducesResponseType(typeof(Exception), StatusCodes.Status500InternalServerError)]
public async Task<IResult> ConfigureCamera([FromBody] CameraConfigRequest config)
{
try
{
logger.Info("配置摄像头连接: {Address}:{Port}", config.Address, config.Port);
var success = await _videoStreamService.ConfigureCameraAsync(config.Address, config.Port);
if (success)
{
return TypedResults.Ok(new
{
success = true,
message = "摄像头配置成功",
cameraAddress = config.Address,
cameraPort = config.Port
});
}
else
{
return TypedResults.BadRequest(new
{
success = false,
message = "摄像头配置失败",
cameraAddress = config.Address,
cameraPort = config.Port
});
}
}
catch (Exception ex)
{
logger.Error(ex, "配置摄像头连接失败");
return TypedResults.InternalServerError(ex.Message);
}
}
/// <summary>
/// 获取当前摄像头配置
/// </summary>
/// <returns>摄像头配置信息</returns>
[HttpGet("CameraConfig")]
[EnableCors("Users")]
[ProducesResponseType(typeof(object), StatusCodes.Status200OK)]
[ProducesResponseType(typeof(Exception), StatusCodes.Status500InternalServerError)]
public IResult GetCameraConfig()
{
try
{
logger.Info("获取摄像头配置");
var cameraStatus = _videoStreamService.GetCameraStatus();
return TypedResults.Ok(cameraStatus);
}
catch (Exception ex)
{
logger.Error(ex, "获取摄像头配置失败");
return TypedResults.InternalServerError(ex.Message);
}
}
/// <summary>
/// 控制 HTTP 视频流服务开关
/// </summary>
/// <param name="enabled">是否启用服务</param>
/// <returns>操作结果</returns>
[HttpPost("SetEnabled")]
[EnableCors("Users")]
[ProducesResponseType(typeof(object), StatusCodes.Status200OK)]
[ProducesResponseType(typeof(Exception), StatusCodes.Status500InternalServerError)]
public async Task<IResult> SetEnabled([FromQuery] bool enabled)
{
logger.Info("设置视频流服务开关: {Enabled}", enabled);
await _videoStreamService.SetEnable(enabled);
return TypedResults.Ok();
}
/// <summary>
/// 测试 HTTP 视频流连接
/// </summary>
/// <returns>连接测试结果</returns>
[HttpPost("TestConnection")]
[EnableCors("Users")]
[ProducesResponseType(typeof(bool), StatusCodes.Status200OK)]
[ProducesResponseType(typeof(Exception), StatusCodes.Status500InternalServerError)]
public async Task<IResult> TestConnection()
{
try
{
logger.Info("测试 HTTP 视频流连接");
// 尝试通过HTTP请求检查视频流服务是否可访问
bool isConnected = false;
using (var httpClient = new HttpClient())
{
httpClient.Timeout = TimeSpan.FromSeconds(2); // 设置较短的超时时间
var response = await httpClient.GetAsync($"http://{Global.localhost}:{_videoStreamService.ServerPort}/");
// 只要能连接上就认为成功,不管返回状态
isConnected = response.IsSuccessStatusCode;
}
logger.Info("测试摄像头连接");
var (isSuccess, message) = await _videoStreamService.TestCameraConnectionAsync();
return TypedResults.Ok(new
{
isConnected = isConnected,
success = isSuccess,
message = message,
cameraAddress = _videoStreamService.CameraAddress,
cameraPort = _videoStreamService.CameraPort,
timestamp = DateTime.Now
});
}
catch (Exception ex)
{
logger.Error(ex, "HTTP 视频流连接测试失败");
// 连接失败但不抛出异常,而是返回连接失败的结果
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}");
}
}
/// <summary>
/// 初始化摄像头自动对焦功能
/// </summary>
/// <returns>初始化结果</returns>
[HttpPost("InitAutoFocus")]
[EnableCors("Users")]
[ProducesResponseType(typeof(object), StatusCodes.Status200OK)]
[ProducesResponseType(typeof(object), StatusCodes.Status400BadRequest)]
[ProducesResponseType(typeof(string), StatusCodes.Status500InternalServerError)]
public async Task<IResult> InitAutoFocus()
{
try
{
logger.Info("收到初始化自动对焦请求");
var result = await _videoStreamService.InitAutoFocusAsync();
if (result)
{
logger.Info("自动对焦初始化成功");
return TypedResults.Ok(new
{
success = true,
message = "自动对焦初始化成功",
timestamp = DateTime.Now
});
}
else
{
logger.Warn("自动对焦初始化失败");
return TypedResults.BadRequest(new
{
success = false,
message = "自动对焦初始化失败",
timestamp = DateTime.Now
});
}
}
catch (Exception ex)
{
logger.Error(ex, "初始化自动对焦时发生异常");
return TypedResults.InternalServerError($"初始化自动对焦失败: {ex.Message}");
}
}
/// <summary>
/// 执行自动对焦
/// </summary>
/// <returns>对焦结果</returns>
[HttpPost("AutoFocus")]
[EnableCors("Users")]
[ProducesResponseType(typeof(object), StatusCodes.Status200OK)]
[ProducesResponseType(typeof(object), StatusCodes.Status400BadRequest)]
[ProducesResponseType(typeof(string), StatusCodes.Status500InternalServerError)]
public async Task<IResult> AutoFocus()
{
try
{
logger.Info("收到执行自动对焦请求");
var result = await _videoStreamService.PerformAutoFocusAsync();
if (result)
{
logger.Info("自动对焦执行成功");
return TypedResults.Ok(new
{
success = true,
message = "自动对焦执行成功",
timestamp = DateTime.Now
});
}
else
{
logger.Warn("自动对焦执行失败");
return TypedResults.BadRequest(new
{
success = false,
message = "自动对焦执行失败",
timestamp = DateTime.Now
});
}
}
catch (Exception ex)
{
logger.Error(ex, "执行自动对焦时发生异常");
return TypedResults.InternalServerError($"执行自动对焦失败: {ex.Message}");
}
}
/// <summary>
/// 执行一次自动对焦 (GET方式)
/// </summary>
/// <returns>对焦结果</returns>
[HttpGet("Focus")]
[EnableCors("Users")]
[ProducesResponseType(typeof(object), StatusCodes.Status200OK)]
[ProducesResponseType(typeof(object), StatusCodes.Status400BadRequest)]
[ProducesResponseType(typeof(string), StatusCodes.Status500InternalServerError)]
public async Task<IResult> Focus()
{
try
{
logger.Info("收到执行一次对焦请求 (GET)");
// 检查摄像头是否已配置
if (!_videoStreamService.IsCameraConfigured())
{
logger.Warn("摄像头未配置,无法执行对焦");
return TypedResults.BadRequest(new
{
success = false,
message = "摄像头未配置,请先配置摄像头连接",
timestamp = DateTime.Now
});
}
var result = await _videoStreamService.PerformAutoFocusAsync();
if (result)
{
logger.Info("对焦执行成功");
return TypedResults.Ok(new
{
success = true,
message = "对焦执行成功",
timestamp = DateTime.Now
});
}
else
{
logger.Warn("对焦执行失败");
return TypedResults.BadRequest(new
{
success = false,
message = "对焦执行失败",
timestamp = DateTime.Now
});
}
}
catch (Exception ex)
{
logger.Error(ex, "执行对焦时发生异常");
return TypedResults.InternalServerError($"执行对焦失败: {ex.Message}");
}
}
}