【问题标题】:How to stream web cam per ASP.Net Core WebApi如何通过 ASP.Net Core Web Api 流式传输网络摄像头
【发布时间】:2021-05-10 20:08:34
【问题描述】:

我尝试创建一个端点来提供我的网络摄像头的实时流。我正在使用 AForge 访问相机,但我无法创建并返回实际呈现在 html 客户端的视频标签中的流。

另外:我使用的 MemoryStream 每秒都在增长。这绝对不是我想要的。

到目前为止我尝试了什么:

Cam.cs:

public class Cam
{
    MemoryStream stream = new MemoryStream();

    public Cam()
    {
        FilterInfoCollection videoCaptureDevices = new FilterInfoCollection(FilterCategory.VideoInputDevice);
        VideoCaptureDevice finalVideo = new VideoCaptureDevice(videoCaptureDevices[0].MonikerString);
        finalVideo.NewFrame += this._streamNewFrame;
        finalVideo.Start();
    }

    private void _streamNewFrame(object sender, AForge.Video.NewFrameEventArgs eventArgs)
    {
        System.Drawing.Image imgforms = (Bitmap)eventArgs.Frame.Clone();


        imgforms.Save(this.stream, ImageFormat.Bmp);

        this.stream.Seek(0, SeekOrigin.Begin);
    }

    public Stream GetStream()
    {
        MemoryStream stream = new MemoryStream();
        this.stream.CopyTo(stream);
        return stream;
    }
}

CamController.cs:

[ApiController]
[Route("[controller]")]
public class CamController : ControllerBase
{
    private readonly Cam cam;

    public CamController(Cam cam)
    {
        this.cam = cam;
    }

    [HttpGet]
    public IActionResult Get()
    {
        var contentType = "multipart/x-mixed-replace;boundary=myboundary";
        Stream stream = this.cam.GetStream();
        var result = new FileStreamResult(stream, contentType)
        {
            EnableRangeProcessing = true,
            
        };
        return result;
    }
}

更新 1:

我取得了进步。我设法创建了一个有效的 MJPEP 流(如果知道它的外观很容易)。看那个:

CamController.cs:

using Microsoft.AspNetCore.Mvc;
using System;
using System.IO;
using System.Net;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;

namespace demo.Controllers
{
    [ApiController]
    [Route("[controller]")]
    public class CamController : ControllerBase
    {
        private readonly Cam cam;

        public CamController(Cam cam)
        {
            this.cam = cam;
        }

        [HttpGet]
        [Route("cam/video")]
        public async Task Get()
        {
            Response.ContentType = "video/webm";
            // How to go on here?            

        }

        [HttpGet]
        [Route("cam/mjepg")]
        public async Task Get2()
        {
            Response.StatusCode = 206;
            Response.ContentType = "multipart/x-mixed-replace; boundary=frame";
            Response.Headers.Add("Connection", "Keep-Alive");
            

            StreamingSession session = this.cam.StreamOn(data =>
                {
                    if (Request.HttpContext.RequestAborted.IsCancellationRequested)
                    {
                        throw new Exception();
                    }

                    Response.Body.Write(this.CreateHeader(data.Length));
                    Response.Body.Write(data);
                    Response.Body.Write(this.CreateFooter());
                    Response.Body.Flush();
                });

            await Response.StartAsync();

            await session.WaitAsync();
        }

        /// <summary>
        /// Create an appropriate header.
        /// </summary>
        /// <param name="length"></param>
        /// <returns></returns>
        private byte[] CreateHeader(int length)
        {
            string header =
                "--frame" + "\r\n" +
                "Content-Type:image/jpeg\r\n" +
                "Content-Length:" + length + "\r\n\r\n";

            return Encoding.ASCII.GetBytes(header);
        }

        private byte[] CreateFooter()
        {
            return Encoding.ASCII.GetBytes("\r\n");
        }
    }
}

Cam.cs:

using AForge.Video.DirectShow;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Linq;
using System.Threading.Tasks;

namespace demo
{
    public class Cam
    {
        object locker = new object();
        bool signaledToStop = false;
        List<StreamingSession> sessions = new List<StreamingSession>();
        VideoCaptureDevice finalVideo;

        public Cam()
        {
            FilterInfoCollection videoCaptureDevices = new FilterInfoCollection(FilterCategory.VideoInputDevice);
            finalVideo = new VideoCaptureDevice(videoCaptureDevices[0].MonikerString);
            
            finalVideo.VideoResolution = finalVideo.VideoCapabilities
                .OrderByDescending(x => x.MaximumFrameRate)
                .ThenByDescending(x=>x.FrameSize.Width)
                .FirstOrDefault();

            finalVideo.NewFrame += this._streamNewFrame;
        }

        private void _streamNewFrame(object sender, AForge.Video.NewFrameEventArgs eventArgs)
        {
            System.Drawing.Image imgforms = (Bitmap)eventArgs.Frame.Clone();
            byte[] data = new byte[0];

            using (MemoryStream stream = new MemoryStream())
            {
                imgforms.Save(stream, ImageFormat.Jpeg);
                data = stream.ToArray();
            }

            lock (this.locker)
            {
                foreach (var session in sessions.ToList())
                {
                    session.ProvideData(data);
                }
            }
        }

        public StreamingSession StreamOn(Action<byte[]> callback)
        {
            StreamingSession session = new StreamingSession(callback);
            lock (this.locker)
            {
                this.sessions.Add(session);

                if (this.signaledToStop)
                {
                    this.finalVideo.WaitForStop();
                }

                if (!this.finalVideo.IsRunning)
                {
                    this.finalVideo.Start();
                    this.signaledToStop = false;
                }
            }

            session.OnSessionEnded += Session_OnSessionEnded;

            return session;
        }

        private void Session_OnSessionEnded(object sender, EventArgs e)
        {
            lock(this.locker)
            {
                this.sessions.Remove(sender as StreamingSession);

                if (!this.sessions.Any())
                {
                    this.finalVideo.SignalToStop();
                    this.signaledToStop = true;
                }
            }            
        }
    }

    public class StreamingSession
    {
        public StreamingSession(Action<byte[]> Callback)
        {
            this.Callback = Callback;
        }

        private Action<byte[]> Callback;
        private TaskCompletionSource Completion = new TaskCompletionSource();

        public event EventHandler OnSessionEnded;

        public Task WaitAsync(int? timeout = null)
        {
            if (timeout.HasValue)
            {
                return Task.WhenAny(Task.Delay(timeout.Value), this.Completion.Task);
            }

            return this.Completion.Task;
        }

        public void ProvideData(byte[] data)
        {
            try
            {
                this.Callback(data);
            }
            catch(Exception)
            {
                this.EndSession();
            }
        }

        public void EndSession()
        {
            this.Completion.SetResult();
            if (this.OnSessionEnded != null)
            {
                this.OnSessionEnded(this, null);
            }
        }
    }
}

但是还有一个问题:“视频”(更像是运动图像)仅在 img 标签中呈现。但它应该在视频标签中呈现。我读到视频标签不支持 mjpeg - 但是如何以视频标签理解的格式对流进行编码?

我在这里上传了完整的代码:https://github.com/ChristophWieske/asp-net-core-live-stream-source

【问题讨论】:

  • “在 html 客户端的视频标签中呈现”你是什么意思?您想在将视频发送给客户端之前渲染一些文本信息吗?
  • @eocron 不,我只是想显示视频。
  • @christoph 也显示客户端。解释为什么使用"multipart/x-mixed-replace;boundary=myboundary" 内容类型
  • 在您编写完整的示例之前,我们很难诊断问题。你应该做的第一件事——写一个测试。模拟与您的 cam 驱动程序相关的所有内容(因此所有内容都在接口后面,并且在那里看不到驱动程序名称空间)并提供您自己的实现,例如每 2 秒重复一次猫/狗图像。然后在上面调试您的应用程序。然后切换回驱动程序实现。这就是我在单元测试中所做的和固定的。
  • @Nkosi 我不太确定,但它似乎是 mjpeg 流的正确选择(它由多个 jpeg 图像组成 -> 所以是的 bmp 因为图像格式是错误的,我会更新)

标签: c# asp.net-core video-streaming webapi


【解决方案1】:

您可能需要考虑使用 WebRTC。

WebRTC 是一种相对较新的基于浏览器的技术,它允许服务器向 JavaScript 客户端发送流。

有关客户端代码,请参阅 WebRTC 示例:https://github.com/webrtc/samples

服务器端代码有点复杂。这是一个使用“混合现实”的过于复杂的示例。这有点过火了,但应该为您指明正确的方向。

https://microsoft.github.io/MixedReality-WebRTC/manual/cs/helloworld-cs-connection-core3.html

【讨论】:

  • 嗨 RobV8R,我实际上想将该服务用作实验室视频源。要使用视频流,只需一个具有正确来源的视频标签就足够了。使用 javascript 客户端不适合我。 (但我真的会研究一下 WebRTC,尤其是当我要从客户端流式传输到服务器时)
猜你喜欢
  • 1970-01-01
  • 2013-05-14
  • 2014-06-18
  • 2015-09-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多