Hello!

The task is as follows:

  • 1 - there is a video file on the server;
  • 2 - the administrator starts it to play (the video broadcast starts);
  • 3 - users connected to the server - should be given a stream of video that is currently playing. Live broadcast in real time.

To accomplish this task, I tried the option: http://www.strathweb.com/2013/01/asynchronously-streaming-video-with-asp-net-web-api/ It worked. Gave the video file stream independently and in parallel. I searched further.

Next, it was necessary to solve the problem of broadcasting to several clients (point 3 in the assignment). I took this article: http://gigi.nullneuron.net/gigilabs/streaming-data-with-asp-net-web-api-and-pushcontentstream/

Since I need to give the video data in byte - I replaced the class StreamWriter with Stream. This works for one first client.

I made the site Asp.Net WebForms + WebApi + HTML5. Web site - to run the video administrator and view users. WebApi gives to the player (HTML5) a video stream.

HTML5:

<video> <source src="http://localhost:8080/SiteVideoStreaming/api/live/?filename=nameFile" /> </video> 

WebApi:

 public class LiveController : ApiController { private static ConcurrentBag<Stream> clients; // Список клиентов, которым надо одновременно отдавать видео данные static string fileName = ""; static LiveController() { clients = new ConcurrentBag<Stream>(); WriteToStream(); // Первое обращение - запуск видео файла на воспроизведение } [HttpGet] public HttpResponseMessage Subscribe(string filename) { fileName = HostingEnvironment.MapPath("~/Videos/") + filename; var response = Request.CreateResponse(); response.Content = new PushStreamContent((a, b, c) => { OnStreamAvailable(a, b, c); }, "video/mp4"); return response; } private void OnStreamAvailable(Stream stream, HttpContent content, TransportContext context) { clients.Add(stream); // Add new client } //Класс записи видео файла в поток public async static void WriteToStream() { var buffer = new byte[65536]; using (var video = File.Open(fileName, FileMode.Open, FileAccess.Read, FileShare.Read)) { var length = (int)video.Length; var bytesRead = 1; while (length > 0 && bytesRead > 0) { bytesRead = video.Read(buffer, 0, Math.Min(length, buffer.Length)); foreach (var client in clients)// Кажому клиенту отдаём видео данные { try { await client.WriteAsync(buffer, 0, bytesRead);// ERROR - здесь ошибка!!! await client.FlushAsync(); } catch (Exception ex) { Stream ignore; clients.TryTake(out ignore); } } length -= bytesRead; } } } } 

If the request first came from a single client - the video is given away, it works. If a second client connects - when you try to start sending a stream, an error occurs. At that, the connection of the first client drops.

The error is this:

[System.Web.HttpException] = {"The remote host terminated the connection. Error code: 0x800704CD."}

As I understand it after searching on the Internet is:

0x800704CD "An operation was attempted on a nonexistent network connection." An attempt was made to perform an operation for a non-existent network connection.

Assuming the browser or player breaks the connection, or does the PushStreamContent class itself support more than one connection?

Tell me what am I doing wrong? Thank.

    1 answer 1

    Did so. Used such a controller:

     public class VideoController : ApiController { // GET api/<controller> public HttpResponseMessage Get(string filename) { if (filename == null) return new HttpResponseMessage(HttpStatusCode.BadRequest); string filePath = HostingEnvironment.MapPath("~/Videos/") + filename; if (Request.Headers.Range != null) { //Range Specifc request: Stream video on wanted range. try { //NOTE: ETag calculation only with file name is one approach (Not the best one though - GUIDs or DateTime is may required in live applications.). Encoder stringEncoder = Encoding.UTF8.GetEncoder(); byte[] stringBytes = new byte[stringEncoder.GetByteCount(filePath.ToCharArray(), 0, filePath.Length, true)]; stringEncoder.GetBytes(filePath.ToCharArray(), 0, filePath.Length, stringBytes, 0, true); MD5CryptoServiceProvider MD5Enc = new MD5CryptoServiceProvider(); string hash = BitConverter.ToString(MD5Enc.ComputeHash(stringBytes)).Replace("-", string.Empty); HttpResponseMessage partialResponse = Request.CreateResponse(HttpStatusCode.PartialContent); partialResponse.Headers.AcceptRanges.Add("bytes"); partialResponse.Headers.ETag = new EntityTagHeaderValue("\"" + hash + "\""); var stream = new FileStream(filePath, FileMode.Open, FileAccess.Read); partialResponse.Content = new ByteRangeStreamContent(stream, Request.Headers.Range, new MediaTypeHeaderValue("video/mp4")); return partialResponse; } catch (Exception ex) { return new HttpResponseMessage(HttpStatusCode.InternalServerError); } } else { return new HttpResponseMessage(HttpStatusCode.RequestedRangeNotSatisfiable); } } } 

    On the client side, I control the video player through the SignalR technology.