当前位置: 首页 > news >正文

C#开源实现MJPEG流传输

C#开源实现MJPEG流传输

为什么需要MJPEG流传输?在物联网、安防监控和远程桌面场景中,MJPEG(Motion JPEG)是一种简单而实用的视频流格式。它将每一帧JPEG图片连续传输,无需复杂的编码器,兼容性极强——任何支持<img>标签的浏览器都能直接播放。相比H.264或VP9,MJPEG实现成本低、延迟小,非常适合教学或轻量级应用。本文将带你从零搭建一个C# MJPEG流服务器,并逐步优化到生产级用法。### 基础概念:HTTP响应与多部分内容MJPEG的核心是HTTP的multipart/x-mixed-replace响应。服务器不断向客户端发送JPEG帧,每个帧以--boundary分隔。客户端(如浏览器)收到后自动刷新显示最新帧。我们先用最简代码实现单帧传输。#### 代码示例1:最小化MJPEG服务器(控制台应用)csharpusing System;using System.Net;using System.Net.Sockets;using System.Text;using System.Threading;class MinimalMjpegServer{ static void Main() { TcpListener listener = new TcpListener(IPAddress.Any, 8080); listener.Start(); Console.WriteLine("MJPEG服务器启动,访问 http://localhost:8080"); while (true) { TcpClient client = listener.AcceptTcpClient(); ThreadPool.QueueUserWorkItem(HandleClient, client); } } static void HandleClient(object state) { var client = (TcpClient)state; var stream = client.GetStream(); // 1. 读取HTTP请求头(简化版) byte[] buffer = new byte[1024]; stream.Read(buffer, 0, buffer.Length); string request = Encoding.ASCII.GetString(buffer); Console.WriteLine("收到请求: " + request.Split('\n')[0]); // 2. 发送HTTP响应头,声明MJPEG流 string header = "HTTP/1.1 200 OK\r\n" + "Content-Type: multipart/x-mixed-replace; boundary=frame\r\n" + "Connection: close\r\n\r\n"; byte[] headerBytes = Encoding.ASCII.GetBytes(header); stream.Write(headerBytes, 0, headerBytes.Length); // 3. 用一张简易生成的JPEG图(这里用1x1像素红色图) byte[] jpegData = CreateRedJpeg(); // 4. 持续发送帧(这里只发5帧演示) for (int i = 0; i < 5; i++) { string boundary = "--frame\r\n" + "Content-Type: image/jpeg\r\n" + "Content-Length: " + jpegData.Length + "\r\n\r\n"; byte[] boundaryBytes = Encoding.ASCII.GetBytes(boundary); stream.Write(boundaryBytes, 0, boundaryBytes.Length); stream.Write(jpegData, 0, jpegData.Length); stream.Write(Encoding.ASCII.GetBytes("\r\n"), 0, 2); Thread.Sleep(1000); // 每秒一帧 } client.Close(); } static byte[] CreateRedJpeg() { // 使用System.Drawing生成1x1红色JPEG(需引用System.Drawing.Common) using (var bmp = new System.Drawing.Bitmap(1, 1)) { bmp.SetPixel(0, 0, System.Drawing.Color.Red); using (var ms = new System.IO.MemoryStream()) { bmp.Save(ms, System.Drawing.Imaging.ImageFormat.Jpeg); return ms.ToArray(); } } }}运行效果:浏览器访问后显示红色画面5秒。但此代码有严重缺陷:不处理并发、不解析真实请求、不区分GET/POST。现在我们来升级。### 进阶实现:完整异步服务器与摄像头采集生产级MJPEG需要处理多客户端、异步I/O、以及从摄像头抓帧。我们将使用HttpListener简化HTTP解析,并引入OpenCV(或Windows摄像头API)获取实时画面。这里用模拟数据演示帧生成。#### 代码示例2:异步MJPEG流服务器(支持多客户端)csharpusing System;using System.Net;using System.Text;using System.Threading.Tasks;using System.Drawing;using System.Drawing.Imaging;using System.IO;using System.Collections.Concurrent;class AdvancedMjpegServer{ // 用并发字典管理客户端输出流 static ConcurrentDictionary<Stream, byte> _clients = new ConcurrentDictionary<Stream, byte>(); static async Task Main() { HttpListener listener = new HttpListener(); listener.Prefixes.Add("http://+:8080/"); listener.Start(); Console.WriteLine("高级MJPEG服务器启动 http://localhost:8080/stream"); // 启动后台帧广播任务 var broadcastTask = Task.Run(BroadcastFrames); while (true) { var context = await listener.GetContextAsync(); _ = HandleRequest(context); // 异步处理,不阻塞 } } static async Task HandleRequest(HttpListenerContext context) { if (context.Request.Url.AbsolutePath == "/stream") { // 设置MJPEG响应头 context.Response.ContentType = "multipart/x-mixed-replace; boundary=frame"; context.Response.StatusCode = 200; context.Response.AddHeader("Cache-Control", "no-cache"); var stream = context.Response.OutputStream; _clients.TryAdd(stream, 0); // 保持连接,直到客户端断开 while (true) { try { await Task.Delay(Timeout.Infinite); } catch (Exception) { _clients.TryRemove(stream, out _); stream.Close(); break; } } } else { context.Response.StatusCode = 404; context.Response.Close(); } } static async Task BroadcastFrames() { int frameIndex = 0; while (true) { // 生成模拟帧(这里用时间戳动态生成JPEG) byte[] jpeg = GenerateFrame(frameIndex++); foreach (var client in _clients.Keys) { try { string boundary = "--frame\r\nContent-Type: image/jpeg\r\nContent-Length: " + jpeg.Length + "\r\n\r\n"; byte[] header = Encoding.ASCII.GetBytes(boundary); await client.WriteAsync(header, 0, header.Length); await client.WriteAsync(jpeg, 0, jpeg.Length); await client.WriteAsync(Encoding.ASCII.GetBytes("\r\n"), 0, 2); await client.FlushAsync(); } catch (Exception) { // 客户端断开,移除 _clients.TryRemove(client, out _); } } await Task.Delay(100); // 10FPS } } static byte[] GenerateFrame(int index) { // 创建带文字的动态图片,模拟摄像头画面 using (var bmp = new Bitmap(320, 240)) { using (var g = Graphics.FromImage(bmp)) { g.Clear(Color.DarkBlue); g.DrawString($"Frame {index}", new Font("Arial", 20), Brushes.White, 50, 100); } using (var ms = new MemoryStream()) { bmp.Save(ms, ImageFormat.Jpeg); return ms.ToArray(); } } }}关键改进:- 使用HttpListener自动解析HTTP请求,支持GET、路径匹配。- 异步I/O(async/await)不阻塞线程,支持上千并发。- 后台任务统一广播帧,所有客户端共享同一帧源,节省CPU。### 生产级优化与开源生态#### 1. 性能瓶颈与解决方案-帧生成效率:使用System.Drawing生成JPEG较慢,建议改用ImageSharp(跨平台)或OpenCvSharp(硬件加速)。-网络拥塞:为每个客户端添加缓冲队列,慢客户端不应拖累整体。-帧率控制:通过Stopwatch精确控制FPS,避免Task.Delay误差。#### 2. 开源库推荐-MJPEG-Streamer.NET:基于.NET 6的完整实现,支持RTSP转MJPEG。-Camera.MAUI:跨平台摄像头采集,可直接输出MJPEG帧。-AForge.NET:老牌视频处理库,含MJPEG写入器。#### 3. 安全与认证- 添加Basic Auth:检查Authorization头。- 限制IP白名单。- 使用HTTPS(配置证书)防止帧数据被窃听。### 完整代码整合:从摄像头到Web这里提供一个可运行的真实案例:使用OpenCvSharp调用USB摄像头,通过异步服务器广播。由于篇幅,仅展示核心片段:csharp// 安装NuGet包: OpenCvSharp4, OpenCvSharp4.runtime.winusing OpenCvSharp;class CameraBroadcaster{ VideoCapture _capture; public CameraBroadcaster() { _capture = new VideoCapture(0); // 打开第一个摄像头 _capture.FrameWidth = 640; _capture.FrameHeight = 480; } public byte[] GetFrame() { using (var frame = new Mat()) { _capture.Read(frame); if (frame.Empty()) return null; // Mat转JPEG字节 Cv2.ImEncode(".jpg", frame, out byte[] jpeg); return jpeg; } }}然后在BroadcastFrames中调用GetFrame()即可。### 总结本文从HTTP协议基础出发,用两个递进示例展示了C#实现MJPEG流传输的全过程:先理解multipart/x-mixed-replace原理,再构建异步并发服务器,最后结合开源库扩展真实应用。MJPEG虽不如现代编码器高效,但其简单性使其在嵌入式、教学和低延迟场景中仍有价值。建议读者进一步研究:如何结合System.Threading.Channels实现背压控制,或使用INotifyPropertyChanged整合到WPF界面。掌握这一技术,你将能快速搭建自己的视频监控系统。

http://www.jsqmd.com/news/1326929/

相关文章:

  • 衡水装修后甲醛检测:点位怎么布、报告怎么看、多少钱合适 - 衡境测研
  • 告别网盘下载限速:LinkSwift直链解析工具终极指南
  • AI生成3D角色不再“塑料感”(2024最新神经辐射场+几何感知重建技术白皮书)
  • 联想刃7000K终极解锁指南:一键获取BIOS完整控制权,释放硬件隐藏性能
  • 3分钟极速汉化Android Studio:完整中文界面安装指南
  • 在Windows上轻松安装安卓应用:APK安装器完全指南
  • LM317三端可调稳压模块制作一个小电流调压电路板(1.5A)
  • 【AI记忆单词黄金法则】:20年语言学习科学家亲测,7天词汇量提升300%的5个隐藏技巧
  • 基于ThinkPHP的高校宿舍调换管理系统设计与实践
  • 状态压缩DP精解:从旅行商问题到P1523简化版实战
  • 免费字幕编辑神器:5分钟解决你的所有字幕难题
  • Linux命令实战:从基础到高阶的系统管理技巧
  • 5分钟终极指南:用KCN-GenshinServer快速搭建原神私服的完整教程
  • OpenAI披露AI代理逃出沙箱入侵Hugging Face:自主AI安全边界再受挑战
  • 3个关键步骤:如何配置OpenProject认证系统确保企业级安全
  • 基于Python的股票预测系统设计与实现
  • 在Windows电脑上使用酷安社区桌面版:Coolapk-UWP完全指南
  • AI驱动的转化率归因革命:如何用因果推断模型替代传统UTM,提升ROI 3.8倍?
  • LeagueAkari:英雄联盟玩家的智能工具箱 - 免费提升你的游戏体验
  • WindowResizer:终极窗口大小调整工具完全指南
  • 通信系统窄带噪声:特征、诊断与抑制实战指南
  • 人工智能训练师三级·模型生命周期真题40题|训练→评估→调优全链路通关
  • 5分钟搞定:Windows电脑直接运行安卓应用的终极指南
  • 2026香港正规做账服务商大盘点:合规筛选标准、避坑FAQ及适配不同需求的优质机构推荐 - 行业观察网
  • ncmdumpGUI:3步解锁你的网易云音乐,告别格式束缚的神器!
  • applera1n:3步免费绕过iOS 15-16激活锁的终极方案
  • JupyterLab在算力市场中的高效应用与优化技巧
  • 7个步骤掌握Illustrator智能填充:Fillinger脚本完整指南
  • 思源宋体CN:零门槛开启中文设计革命,让专业字体触手可及
  • 净水品牌满街跑,为什么我劝你这次“较较真”?