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

使用WebAPI流式传输MJPEG时始终出现缓冲问题

解决MJPEG流式传输的核心思路

MJPEG的本质是multipart/x-mixed-replace类型的响应,它完全不需要提前知道总帧数或总长度——每个视频帧作为独立的部分,用自定义分隔符(boundary)分开,浏览器会自动持续接收并刷新画面。关键是要正确配置响应头,然后持续向输出流写入帧数据。

1. 配置WebAPI路由

首先确保MJPEG流路由和其他路由(配置、Web界面)互不干扰,推荐用属性路由明确区分:

public class CameraController : ApiController
{
    // MJPEG流专属路由
    [Route("api/camera/stream")]
    [HttpGet]
    public HttpResponseMessage GetCameraStream()
    {
        // 流逻辑实现放在这里
    }

    // 示例:配置查询路由
    [Route("api/camera/config")]
    [HttpGet]
    public IHttpActionResult GetCameraConfig()
    {
        return Ok(new { Resolution = "1920x1080", FrameRate = 25 });
    }

    // 示例:Web界面路由(如果是返回静态页面,可结合Hosted静态文件处理)
    [Route("camera/dashboard")]
    [HttpGet]
    public HttpResponseMessage GetDashboard()
    {
        var content = new StringContent(File.ReadAllText("dashboard.html"));
        content.Headers.ContentType = new MediaTypeHeaderValue("text/html");
        return new HttpResponseMessage(HttpStatusCode.OK) { Content = content };
    }
}

2. 实现MJPEG流的核心逻辑

核心是手动构建HttpResponseMessage,启用分块传输,然后循环写入每个帧:

[Route("api/camera/stream")]
[HttpGet]
public HttpResponseMessage GetCameraStream()
{
    // 定义分块分隔符(选一个不会在帧数据中出现的唯一字符串)
    var boundary = "camera-stream-boundary-7890";

    // 创建响应,使用PushStreamContent主动控制输出流
    var response = new HttpResponseMessage(HttpStatusCode.OK);
    response.Content = new PushStreamContent((stream, content, context) =>
    {
        using var writer = new StreamWriter(stream);
        using var binaryWriter = new BinaryWriter(stream);
        try
        {
            // 持续获取监控帧(替换成你的安防摄像头帧获取逻辑)
            while (true)
            {
                // 1. 获取当前帧的字节数组(示例:调用摄像头SDK/本地帧源)
                byte[] frameBytes = FetchCurrentCameraFrame();
                if (frameBytes == null || frameBytes.Length == 0)
                {
                    Thread.Sleep(50); // 无帧时短暂休眠,避免空循环占用资源
                    continue;
                }

                // 2. 写入分块边界与帧头
                writer.WriteLine($"--{boundary}");
                writer.WriteLine("Content-Type: image/jpeg");
                writer.WriteLine($"Content-Length: {frameBytes.Length}");
                writer.WriteLine(); // 空行分隔头信息与帧内容
                writer.Flush(); // 确保头信息立即写入流

                // 3. 写入帧数据
                binaryWriter.Write(frameBytes);
                binaryWriter.Flush();

                // 控制帧率(比如25帧/秒,根据实际需求调整)
                Thread.Sleep(40);
            }
        }
        catch (IOException)
        {
            // 客户端断开连接时会抛出此异常,清理并结束流
            writer.WriteLine($"--{boundary}--");
            writer.Flush();
        }
        finally
        {
            stream.Close();
        }
    });

    // 设置响应的多部分类型与分隔符
    response.Content.Headers.ContentType = new MediaTypeHeaderValue("multipart/x-mixed-replace");
    response.Content.Headers.ContentType.Parameters.Add(new NameValueHeaderValue("boundary", boundary));

    // 禁用缓存,避免浏览器缓存旧帧不刷新
    response.Headers.CacheControl = new CacheControlHeaderValue
    {
        NoCache = true,
        NoStore = true,
        MustRevalidate = true
    };

    return response;
}

// 示例:替换为你的实际帧获取逻辑(比如调用安防摄像头SDK)
private byte[] FetchCurrentCameraFrame()
{
    // 测试用:读取本地图片模拟帧
    // return File.ReadAllBytes("test-frame.jpg");
    // 实际场景:调用摄像头SDK的帧抓取方法
    throw new NotImplementedException("请替换为你的安防摄像头帧获取逻辑");
}

3. 关键注意事项

  • 分块传输自动启用:使用PushStreamContent且不设置总Content-Length时,WebAPI会自动启用Transfer-Encoding: chunked,这正是MJPEG流式传输需要的。
  • 边界格式严格遵守:每个帧的开头必须是--{boundary},客户端断开时可以用--{boundary}--结束流。
  • 处理客户端断开:一定要捕获IOException,当用户关闭页面/刷新时,流会断开,此时需清理资源并退出循环。
  • 禁用缓存是必须的:如果不设置Cache-Control头,浏览器可能会缓存第一帧,导致后续画面不刷新。
  • 帧率控制要合理:根据摄像头实际帧率和客户端性能调整Thread.Sleep时长,避免服务器和客户端过载。

4. 验证其他路由可用性

你的配置路由、Web界面路由完全不受影响——WebAPI会根据路由匹配规则分发请求,MJPEG流的Action只会处理匹配/api/camera/stream的请求,其他请求正常流向对应路由。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.25 07:56:49