You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

自定义API转发Axis摄像头MJPG流在浏览器与Blazor中无法显示的问题

问题描述

我正在开发一个Blazor WASM应用,需要展示Axis摄像头的视频流。为避免暴露凭证,我希望通过自定义API来转发该流。

测试用摄像头流地址:
http://192.168.0.195/axis-cgi/mjpg/video.cgi

  • 摄像头已配置为匿名查看(用于测试)
  • 本地Chrome直接打开该地址:可正常显示流
  • VLC中打开该地址:可正常显示流

API控制器代码:

public async Task<IActionResult> GetStream()
{
    var cameraUrl = "http://192.168.0.195/axis-cgi/mjpg/video.cgi";

    var response = await _httpClient.GetAsync(cameraUrl, HttpCompletionOption.ResponseHeadersRead);

    if (!response.IsSuccessStatusCode)
    {
        return StatusCode((int)response.StatusCode, "Failed to retrieve stream from camera.");
    }

    var stream = await response.Content.ReadAsStreamAsync();

    return new FileStreamResult(stream, "multipart/x-mixed-replace");
}

测试情况:

  • Chrome打开API端点(https://localhost:7283/v1/camera/stream):显示黑屏
  • VLC打开该API端点:可正常播放
  • Blazor应用中使用<img src="https://localhost:7283/v1/camera/stream" alt="Live Camera Feed" />:显示损坏的图片图标

疑问:
为什么原始摄像头流在浏览器能正常显示,但API转发后的流不行?遗漏了什么配置?


问题排查与修复

核心原因

浏览器渲染MJPG流时,需要严格遵循multipart/x-mixed-replace格式的要求,尤其是响应头的完整性和流的实时性转发。你的API代码只转发了流内容,但丢失了摄像头返回的关键响应头,同时没有保持流的实时传输。

具体修复步骤

1. 转发摄像头的原始响应头

Axis摄像头返回的MJPG流包含Boundary参数(用于分隔多帧图片),浏览器需要这个参数才能正确解析流。修改API代码,将摄像头的响应头原样转发:

public async Task<IActionResult> GetStream()
{
    var cameraUrl = "http://192.168.0.195/axis-cgi/mjpg/video.cgi";

    var response = await _httpClient.GetAsync(cameraUrl, HttpCompletionOption.ResponseHeadersRead);

    if (!response.IsSuccessStatusCode)
    {
        return StatusCode((int)response.StatusCode, "Failed to retrieve stream from camera.");
    }

    // 复制摄像头返回的Content-Type头(包含boundary参数)
    if (response.Content.Headers.ContentType != null)
    {
        Response.ContentType = response.Content.Headers.ContentType.ToString();
    }

    // 复制其他必要的响应头
    foreach (var header in response.Content.Headers)
    {
        if (!Response.Headers.ContainsKey(header.Key))
        {
            Response.Headers.TryAdd(header.Key, header.Value.ToArray());
        }
    }

    // 直接流式转发,避免读取到内存导致延迟
    await using var stream = await response.Content.ReadAsStreamAsync();
    await stream.CopyToAsync(Response.Body);
    await Response.Body.FlushAsync();

    return new EmptyResult();
}

2. 禁用响应缓存

浏览器可能会缓存转发后的流,导致无法实时更新帧。在API端点添加缓存禁用特性:

[ResponseCache(NoStore = true, Location = ResponseCacheLocation.None)]
public async Task<IActionResult> GetStream()
{
    // 修复后的代码...
}

3. 检查CORS配置(跨域场景)

如果Blazor应用和API不在同一域名下,确保API的CORS策略允许Blazor域名访问,并且开放必要的方法和头:

// 在Program.cs中配置CORS
builder.Services.AddCors(options =>
{
    options.AddPolicy("CameraStreamPolicy", policy =>
    {
        policy.WithOrigins("https://your-blazor-app-domain.com")
              .AllowAnyMethod()
              .AllowAnyHeader()
              .AllowCredentials();
    });
});

app.UseCors("CameraStreamPolicy");

4. 验证流式传输逻辑

之前的代码将流读取到内存再返回,会导致流延迟,浏览器无法实时解析帧。改为直接将摄像头的流复制到响应体,保持流式传输的实时性。

验证修复

修改后重新测试:

  1. Chrome打开API端点,应能正常显示MJPG流
  2. Blazor应用中的<img>标签也能正常渲染实时视频流

内容的提问来源于stack exchange,提问作者Jasper

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.16 15:27:20