2017-01-20 15 views
0

Я искал пару дней, чтобы найти примерный проект, который позволяет создать «веб-сервер», который просто передает видео в веб-камеру любому, кто подключается к нему. Звучит достаточно просто. Для этого есть приложения, но я хочу встроить эту функциональность в приложение формы Windows, над которым я работаю.Веб-камера веб-сервера в Dot Net

Так что, я просто хочу создать проект, который действует как хост HTTP (веб-сервер), и когда вы подключитесь к нему, он будет обслуживать видео в веб-камерах. Конечно, вы хотели бы иметь возможность одновременно обслуживать более одного человека.

Может ли кто-нибудь указать мне на такой проект/пример?

+0

Выездные запросы ресурсов и учебные пособия по теме здесь – BradleyDotNET

ответ

2

Если вы хотите установить пакет NuGet NequeoFFmpeg, это оболочка C++/CLI некоторых инструментов FFmpeg. Одна вещь, которую вы можете сделать, это использовать эту оболочку для получения данных WebCam через двоичные файлы FFmpeg. Вы можете получить мои предварительно созданные двоичные файлы FFmpeg от FFmpeg, пожалуйста, используйте версию 2016_01_15.

Пример кода:

private void Capture() 
    { 
     Nequeo.Media.FFmpeg.MediaDemux demux = new Nequeo.Media.FFmpeg.MediaDemux(); 
     demux.OpenDevice("video=Integrated Webcam", true, false); 

     // create instance of video writer 
     Nequeo.Media.FFmpeg.VideoFileWriter writer = new Nequeo.Media.FFmpeg.VideoFileWriter(); 
     writer.Open(@"C:\Temp\Misc\ffmpeg_screen_capture_video.avi", demux.Width, demux.Height, demux.FrameRate, Nequeo.Media.FFmpeg.VideoCodec.MPEG4); 

     byte[] sound = null; 
     Bitmap[] image = null; 

     List<Bitmap> video = new List<Bitmap>(); 
     long audioPos = 0; 
     long videoPos = 0; 

     int captureCount = 0; 
     int captureNumber = 500; 

     while ((demux.ReadFrame(out sound, out image, out audioPos, out videoPos) > 0) && captureCount < captureNumber) 
     { 
      if (image != null && image.Length > 0) 
      { 
       captureCount++; 

       for (int i = 0; i < image.Length; i++) 
       { 
        writer.WriteVideoFrame(image[i]); 
        image[i].Dispose(); 
       } 
      } 
     } 

     writer.Close(); 
     demux.Close(); 
    } 

Установить имя устройства захвата видео, В образце выше я пишу в файл, но вы можете просто написать Bitmap в поток. Вы можете сжать растровое изображение, а затем записать в поток. Вы можете изменить Bitmap на Jpeg, а затем отправить в поток.

FFmpeg может передавать живое WebCam видео смотрите: StreamingGuide

+0

Это выглядит многообещающим. Что относительно части сервера HTTP? если я пишу в поток, поток должен быть отправлен нескольким зрителям HTML. Есть ли образец этого? Прошло много времени с тех пор, как я написал сервер протокола. –

+1

Я добавил образец UDP-сервера с клиентом рабочего стола UDP. HTTP-сервер может быть адаптирован с UDP-сервера, а также клиентский клиент HTTP. Что касается HTML в веб-браузере, это намного больше. –

+0

Ну, я некоторое время нащупывал это в Visual Studio 15 и не мог запустить его. Я установил все пакеты и ffmpeg и получил приложение для запуска, и загорелся свет на веб-камере. Но я не мог подключиться к порту и в итоге получил ошибку. Выглядит хорошо, и я был взволнован, но был несколько разочарован. Я уверен, что делаю что-то неправильно. –

2

Я создал простой UDP-сервер для трансляции захваченного изображения с веб-камеры, пожалуйста, обратите внимание, что нет протокола участвует в отправке захваченных изображений, таких как RTSP. После захвата изображения с веб-камеры он отправляет это изображение как есть. На клиенте, когда изображение получено, вы должны отобразить это изображение по своему усмотрению.

Этот сервер не должен использоваться более чем для 100 клиентов, если вам нужно что-то более надежное, вам нужно будет найти другую альтернативу.

Для серверной части вам нужно будет установить NuGet пакет NequeoNetServer, а также NequeoFFmpeg:

int _clientCount = 0; 
    bool _stopCapture = false; 
    Nequeo.Net.UdpSingleServer _udpsingle = null; 
    Nequeo.Media.FFmpeg.MediaDemux _demux = null; 
    ConcurrentDictionary<IPEndPoint, Nequeo.Net.Sockets.IUdpSingleServer> _clients = null; 

    private void StartServer() 
    { 
     // Create the server endpoint. 
     Nequeo.Net.Sockets.MultiEndpointModel[] model = new Nequeo.Net.Sockets.MultiEndpointModel[] 
      { 
       // None secure. 
       new Nequeo.Net.Sockets.MultiEndpointModel() 
       { 
        Port = 514, 
        Addresses = new System.Net.IPAddress[] 
        { 
         System.Net.IPAddress.IPv6Any, 
         System.Net.IPAddress.Any 
        } 
       }, 
      }; 

     if (_udpsingle == null) 
     { 
      // Create the UDP server. 
      _udpsingle = new Nequeo.Net.UdpSingleServer(model); 
      _udpsingle.OnContext += UDP_Single; 
     } 

     // Create the client collection. 
     _clients = new ConcurrentDictionary<IPEndPoint, Nequeo.Net.Sockets.IUdpSingleServer>(); 
     _demux = new Nequeo.Media.FFmpeg.MediaDemux(); 

     // Start the server. 
     _udpsingle.Start(); 

     _clientCount = 0; 
     _stopCapture = false; 

     // Start the capture process. 
     CaptureAndSend(); 
    } 

Остановите сервер:

private void StopServer() 
    { 
     _clientCount = 0; 
     _stopCapture = true; 

     if (_udpsingle != null) 
     { 
      _udpsingle.Stop(); 
      _udpsingle.Dispose(); 
     } 
     _udpsingle = null; 

     if (_demux != null) 
      _demux.Close(); 

     _demux = null; 
    } 

Клиент отправленное сообщение:

private void UDP_Single(object sender, Nequeo.Net.Sockets.IUdpSingleServer server, byte[] data, IPEndPoint endpoint) 
    { 
     string request = System.Text.Encoding.Default.GetString(data); 

     if (request.ToLower().Contains("connect")) 
      // Add the new client. 
      _clients.GetOrAdd(endpoint, server); 

     if (request.ToLower().Contains("disconnect")) 
     { 
      Nequeo.Net.Sockets.IUdpSingleServer removedServer = null; 

      // Remove the existing client. 
      _clients.TryRemove(endpoint, out removedServer); 
     } 
    } 

Захват: из демукса вы можете получить _demux.Width, _demux.Height и _demux.FrameRate устройства захвата.

private async void CaptureAndSend() 
    { 
     await System.Threading.Tasks.Task.Run(() => 
     { 
      if (_demux != null) 
      { 
       // Open the web cam device. 
       _demux.OpenDevice("video=Integrated Webcam", true, false); 

       byte[] sound = null; 
       Bitmap[] image = null; 

       long audioPos = 0; 
       long videoPos = 0; 

       int count = 0; 
       KeyValuePair<IPEndPoint, Nequeo.Net.Sockets.IUdpSingleServer>[] clientCol = null; 

       // Most of the time one image at a time. 
       MemoryStream[] imageStream = new MemoryStream[10]; 
       int imageStreamCount = 0; 

       // Within this loop you can place a check if there are any clients 
       // connected, and if none then stop capturing until some are connected. 
       while ((_demux.ReadFrame(out sound, out image, out audioPos, out videoPos) > 0) && !_stopCapture) 
       { 
        imageStreamCount = 0; 
        count = _clients.Count; 

        // If count has changed. 
        if (_clientCount != count) 
        { 
         // Get the collection of all clients. 
         _clientCount = count; 
         clientCol = _clients.ToArray(); 
        } 

        // Has an image been captured. 
        if (image != null && image.Length > 0) 
        { 
         // Get all clients and send. 
         if (clientCol != null) 
         { 
          for (int i = 0; i < image.Length; i++) 
          { 
           // Create a memory stream for each image. 
           imageStream[i] = new MemoryStream(); 
           imageStreamCount++; 

           // Save the image to the stream. 
           image[i].Save(imageStream[i], System.Drawing.Imaging.ImageFormat.Jpeg); 

           // Cleanup. 
           image[i].Dispose(); 
          } 

          // For each client. 
          foreach (KeyValuePair<IPEndPoint, Nequeo.Net.Sockets.IUdpSingleServer> client in clientCol) 
          { 
           // For each image captured. 
           for (int i = 0; i < imageStreamCount; i++) 
           { 
            // Send the image to this client. 
            client.Value.SendTo(imageStream[i].ToArray(), client.Key); 
            imageStream[i].Seek(0, SeekOrigin.Begin); 
           } 
          } 

          for (int i = 0; i < imageStreamCount; i++) 
           // Cleanup. 
           imageStream[i].Dispose(); 
         } 
        } 
       } 
      } 
     }); 
    } 

На стороне клиента, как указано выше, полученные данные являются захваченными изображениями, и вам необходимо отобразить изображение и все последующие изображения. Вы можете отправить клиенту с сервера Width, Height и FrameRate снятых изображений, которые могут быть использованы для визуализации каждого полученного изображения.

Код клиента: UDP клиент состояние контейнера

public class UdpState 
{ 
    public UdpClient u { get; set; } 
    public IPEndPoint e { get; set; } 
} 

Клиент код: клиентский код должен включать в себя какой-то буфер данных, так что вы можете получить и визуализации изображений без каких-либо пропущенных кадров.

private void Connect() 
    { 
     pictureBox1.ClientSize = new Size(320, 240); 

     // Create the client. 
     IPEndPoint ee = new IPEndPoint(IPAddress.Any, 541); 
     UdpClient u = new UdpClient(ee); 

     // Create the state. 
     UdpState s = new UdpState(); 
     s.e = ee; 
     s.u = u; 

     // Connect to the server. 
     IPEndPoint server = new IPEndPoint(IPAddress.Any, 514); 
     u.Connect("localhost", 514); 

     // Start the begin receive callback. 
     u.BeginReceive(new AsyncCallback(ReceiveCallback), s); 

     // Send a connect request. 
     byte[] connect = System.Text.Encoding.Default.GetBytes("connect"); 
     u.Send(connect, connect.Length); 
    } 

В обратном вызове приема вы можете отобразить изображение WebCam в окне изображения.

public void ReceiveCallback(IAsyncResult ar) 
    { 
     // Get the client. 
     UdpClient u = (UdpClient)((UdpState)(ar.AsyncState)).u; 
     IPEndPoint e = (IPEndPoint)((UdpState)(ar.AsyncState)).e; 

     // Get the image. 
     Byte[] receiveBytes = u.EndReceive(ar, ref e); 

     // Load the image into a stream. 
     MemoryStream stream = new MemoryStream(receiveBytes); 
     Image image = Image.FromStream(stream); 

     // Add the image to the picture box. 
     pictureBox1.SizeMode = PictureBoxSizeMode.StretchImage; 
     pictureBox1.Image = image; 

     stream.Dispose(); 

     // Start a new receive request. 
     u.BeginReceive(new AsyncCallback(ReceiveCallback), (UdpState)(ar.AsyncState)); 
    } 

Примечание: Я создал образец UDP, но вы можете создать образец HTTP TCP, используя тот же подход, пакет NuGet NequeoNetServer содержит сервер пользовательского HTTP можно адаптировать для обслуживания любого запроса HTTP.

private void StartCustomHttpServer() 
{ 
    Nequeo.Net.CustomServer c = new Nequeo.Net.CustomServer(typeof(http), 30); 
    c.Start(); 
} 

Пользовательский класс HTTP-сервер:

internal class http : Nequeo.Net.Http.CustomContext 
{ 
    /// <summary> 
    /// On new client Http context. 
    /// </summary> 
    /// <param name="context">The client Http context.</param> 
    protected override void OnHttpContext(Nequeo.Net.Http.HttpContext context) 
    { 
     // Get the headers from the stream and assign the request data. 
     bool headersExist = Nequeo.Net.Http.Utility.SetRequestHeaders(context, 30000, 10000); 

     context.HttpResponse.ContentLength = 5; 
     context.HttpResponse.ContentType = "text/plain"; 
     context.HttpResponse.StatusCode = 200; 
     context.HttpResponse.StatusDescription = "OK"; 
     context.HttpResponse.WriteHttpHeaders(); 
     context.HttpResponse.Write("Hello"); 
    } 
} 
+0

Ничего себе, спасибо Д., собираюсь поиграть с ним в эти выходные. 100 клиентов должны быть в порядке. Я не ожидаю больше десятка в один раз. –

+1

Я добавил образец веб-браузера WebSocket. –

3

Для всех, кто заинтересован в потоковых данных веб-камеры на веб-браузеры ниже код использует WebSockets, чтобы использовать образец получить пакет NuGet NequeoWebSockets, а также NequeoFFmpeg

Запуск сервера WebSocket:

TestServer.WebcamWebSocketServer wsServer = new WebcamWebSocketServer(); 
wsServer.UriList = new string[] { "http://localhost:2012/" }; 
wsServer.Start(); 

Код сервера WebCam WebSocket: адаптируйте свой код к образцу.

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.Threading.Tasks; 
using System.Net.WebSockets; 
using System.Threading; 
using System.IO; 
using System.Drawing; 
using System.Collections.Concurrent; 

namespace TestServer 
{ 
public class WebcamWebSocketServer : Nequeo.Net.WebSockets.WebSocketServer 
{ 
    /// <summary> 
    /// 
    /// </summary> 
    public WebcamWebSocketServer() 
    { 
     OnServerInitialise(); 
    } 

    /// <summary> 
    /// 
    /// </summary> 
    /// <param name="uriList"></param> 
    public WebcamWebSocketServer(string[] uriList) 
     : base(uriList) 
    { 
     OnServerInitialise(); 
    } 

    int _clientCount = 0; 
    private int READ_BUFFER_SIZE = 8192; 
    private bool _stopCapture = false; 
    private Nequeo.Media.FFmpeg.MediaDemux _demuxHttp = null; 
    ConcurrentDictionary<System.Net.WebSockets.HttpListenerWebSocketContext, WebSocket> _clients = null; 

    /// <summary> 
    /// 
    /// </summary> 
    private void OnServerInitialise() 
    { 
     base.Timeout = 60; 
     base.HeaderTimeout = 30000; 
     base.RequestTimeout = 30000; 
     base.ResponseTimeout = 30000; 
     base.Name = "Nequeo Web Socket Server"; 
     base.ServiceName = "WebSocketServer"; 
     base.OnWebSocketContext += WebSocketServer_OnWebSocketContext; 

     _demuxHttp = new Nequeo.Media.FFmpeg.MediaDemux(); 

     // Open the web cam device. 
     _demuxHttp.OpenDevice("video=Integrated Webcam", true, false); 
     _clients = new ConcurrentDictionary<HttpListenerWebSocketContext, WebSocket>(); 

     // Start capture. 
     CaptureAndSend(); 
    } 

    /// <summary> 
    /// 
    /// </summary> 
    /// <param name="sender"></param> 
    /// <param name="context"></param> 
    private void WebSocketServer_OnWebSocketContext(object sender, System.Net.WebSockets.HttpListenerWebSocketContext context) 
    { 
     OnWebcamWebSocketContext(context); 
    } 

    /// <summary> 
    /// 
    /// </summary> 
    /// <param name="context"></param> 
    private async void OnWebcamWebSocketContext(System.Net.WebSockets.HttpListenerWebSocketContext context) 
    { 
     WebSocket webSocket = null; 

     try 
     { 
      // Get the current web socket. 
      webSocket = context.WebSocket; 

      CancellationTokenSource receiveCancelToken = new CancellationTokenSource(); 
      byte[] receiveBuffer = new byte[READ_BUFFER_SIZE]; 

      // While the WebSocket connection remains open run a 
      // simple loop that receives data and sends it back. 
      while (webSocket.State == WebSocketState.Open) 
      { 
       // Receive the next set of data. 
       ArraySegment<byte> arrayBuffer = new ArraySegment<byte>(receiveBuffer); 
       WebSocketReceiveResult receiveResult = await webSocket.ReceiveAsync(arrayBuffer, receiveCancelToken.Token); 

       // If the connection has been closed. 
       if (receiveResult.MessageType == WebSocketMessageType.Close) 
       { 
        break; 
       } 
       else 
       { 
        // Add the client 
        _clients.GetOrAdd(context, webSocket); 
       } 
      } 

      // Cancel the receive request. 
      if (webSocket.State != WebSocketState.Open) 
       receiveCancelToken.Cancel(); 
     } 
     catch { } 
     finally 
     { 
      // Clean up by disposing the WebSocket. 
      if (webSocket != null) 
       webSocket.Dispose(); 
     } 
    } 

    /// <summary> 
    /// 
    /// </summary> 
    private async void CaptureAndSend() 
    { 
     await System.Threading.Tasks.Task.Run(async() => 
     { 
      byte[] sound = null; 
      Bitmap[] image = null; 

      long audioPos = 0; 
      long videoPos = 0; 

      int count = 0; 
      KeyValuePair<HttpListenerWebSocketContext, WebSocket>[] clientCol = null; 

      // Most of the time one image at a time. 
      MemoryStream[] imageStream = new MemoryStream[10]; 
      int imageStreamCount = 0; 

      // Within this loop you can place a check if there are any clients 
      // connected, and if none then stop capturing until some are connected. 
      while ((_demuxHttp.ReadFrame(out sound, out image, out audioPos, out videoPos) > 0) && !_stopCapture) 
      { 
       imageStreamCount = 0; 
       count = _clients.Count; 

       // If count has changed. 
       if (_clientCount != count) 
       { 
        // Get the collection of all clients. 
        _clientCount = count; 
        clientCol = _clients.ToArray(); 
       } 

       // Has an image been captured. 
       if (image != null && image.Length > 0) 
       { 
        // Get all clients and send. 
        if (clientCol != null) 
        { 
         for (int i = 0; i < image.Length; i++) 
         { 
          // Create a memory stream for each image. 
          imageStream[i] = new MemoryStream(); 
          imageStreamCount++; 

          // Save the image to the stream. 
          image[i].Save(imageStream[i], System.Drawing.Imaging.ImageFormat.Jpeg); 

          // Cleanup. 
          image[i].Dispose(); 
         } 

         // For each client. 
         foreach (KeyValuePair<HttpListenerWebSocketContext, WebSocket> client in clientCol) 
         { 
          // For each image captured. 
          for (int i = 0; i < imageStreamCount; i++) 
          { 
           // data to send. 
           byte[] result = imageStream[0].GetBuffer(); 

           string base64 = Convert.ToBase64String(result); 
           byte[] base64Bytes = Encoding.Default.GetBytes(base64); 

           try 
           { 
            // Send a message back to the client indicating that 
            // the message was recivied and was sent. 
            await client.Value.SendAsync(new ArraySegment<byte>(base64Bytes), 
             WebSocketMessageType.Text, true, CancellationToken.None); 
           } 
           catch { } 

           imageStream[i].Seek(0, SeekOrigin.Begin); 
          } 
         } 

         for (int i = 0; i < imageStreamCount; i++) 
          // Cleanup. 
          imageStream[i].Dispose(); 
        } 
       } 
      } 
     }); 
    } 
} 
} 

Код для одного HTML страницы:

<!DOCTYPE html> 
<html> 
<head> 
<title>Test</title> 
<script type="text/javascript" src="js/jquery.js"></script> 
<script type="text/javascript"> 
     var noSupportMessage = "Your browser cannot support WebSocket!"; 
     var ws; 

     function appendMessage(message) { 
      $('body').append(message); 
     } 

     function connectSocketServer() { 
      var support = "MozWebSocket" in window ? 'MozWebSocket' : ("WebSocket" in window ? 'WebSocket' : null); 

      if (support == null) { 
       appendMessage("* " + noSupportMessage + "<br/>"); 
       return; 
      } 

      appendMessage("* Connecting to server ..<br/>"); 
      // create a new websocket and connect 
      ws = new window[support]('ws://localhost:2012/'); 
      ws.binaryType = "arraybuffer"; 

      // when data is comming from the server, this metod is called 
      ws.onmessage = function (evt) { 
       if (evt.data) { 
        drawImage(evt.data); 
       } 
      }; 

      // when the connection is established, this method is called 
      ws.onopen = function() { 
       appendMessage('* Connection open<br/>'); 
       $('#messageInput').attr("disabled", ""); 
       $('#sendButton').attr("disabled", ""); 
       $('#connectButton').attr("disabled", "disabled"); 
       $('#disconnectButton').attr("disabled", ""); 
      }; 

      // when the connection is closed, this method is called 
      ws.onclose = function() { 
       appendMessage('* Connection closed<br/>'); 
       $('#messageInput').attr("disabled", "disabled"); 
       $('#sendButton').attr("disabled", "disabled"); 
       $('#connectButton').attr("disabled", ""); 
       $('#disconnectButton').attr("disabled", "disabled"); 
      } 
     } 

     function sendMessage() { 
      if (ws) { 
       var messageBox = document.getElementById('messageInput'); 
       ws.send(messageBox.value); 
       messageBox.value = ""; 
      } 
     } 

     function disconnectWebSocket() { 
      if (ws) { 
       ws.close(); 
      } 
     } 

     function connectWebSocket() { 
      connectSocketServer(); 
     } 

     window.onload = function() { 
      $('#messageInput').attr("disabled", "disabled"); 
      $('#sendButton').attr("disabled", "disabled"); 
      $('#disconnectButton').attr("disabled", "disabled"); 
     } 

     function drawImage(data) 
     { 
      $("#image").attr('src', 'data:image/jpg;base64,' + data); 
     } 

</script> 
</head> 
<body> 
    <input type="button" id="connectButton" value="Connect" onclick="connectWebSocket()" /> 
    <input type="button" id="disconnectButton" value="Disconnect" onclick="disconnectWebSocket()" /> 
    <input type="text" id="messageInput" /> 
    <input type="button" id="sendButton" value="Load Remote Image" onclick="sendMessage()" /> <br /> 
    <img id="image" src="" width="320" height="240" /> 
</body> 
</html> 
+0

D., Вы опубликовали полное решение? Я думал, что ты это сделал, но теперь я не могу найти его. Мне нравится стиль вашего кода, и мне бы очень хотелось попробовать его, но я не могу заставить его пойти в VS2015. По крайней мере, не легко. Спасибо –

+1

Отправил все три проекта в одном [решение] (https://github.com/nequeo/misc/blob/master/csharp/UDPWebCamServerClient.zip) с инструкцией на этой странице 27 января –

+0

Спасибо D. Я схватил его и будет играть с ним в выходные дни. –