Live broadcast of the video site with Asp.Net WebForms + WebApi + HTML5

0

The problem is this:

  1. on the server have a video file;
  2. The administrator runs it on the play (video broadcast begins);
  3. user is connected to the server - must be given to the video stream that is currently playing. A live webcast in real time.

To implement this task, I took as a basis for the article: http://www.strathweb.com/2013/01/asynchronously-streaming-video-with-asp-net-web-api/
It worked. To give the stream a video file independently and in parallel. I was looking on.

Next it was necessary to solve the problem of broadcasting to multiple customers (paragraph 3 in the job). I took this article: http://gigi.nullneuron.net/gigilabs/streaming-data-with-asp-net-web-api-and-pushcontentstream/

Since I have to give evidence in the video byte - I replaced the StreamWriter class to Stream.
It works for one of the first client.

I made a website Asp.Net WebForms + WebApi + HTML5. Web page - to run a video manager and viewed by users. WebApi gives the player for <video> (HTML5) video stream.

HTML5:

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

WebApi controllers:

//Controllers
public class LiveController : ApiController
{
    private static ConcurrentBag<Stream> clients; // List of clients who need to simultaneously deliver video data
    static string fileName = "";

    static LiveController()
    {
        clients = new ConcurrentBag<Stream>();
        WriteToStream(); // The first call - start to play a video file
    }

    [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
    }

    //Class record a video file into a streams
    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)// Each client in turn we return video data
                {
                    try
                    {
                        await client.WriteAsync(buffer, 0, bytesRead); // ERROR - here !!! when you connect a second client
                        await client.FlushAsync();
                    }
                    catch (Exception ex)
                    {
                        Stream ignore;
                        clients.TryTake(out ignore);
                    }
                }

                length -= bytesRead;
            }
        }
    }
}

If the request first came from one client - is given to video. Working. If the request from the second client - when you try to start to give him a stream error occurs. In this connection drops and the first client.

The error is as follows:

[System.Web.HttpException] = {"The remote host closed the connection. The error code is 0x800704CD."}

As I understood after a search on the Internet is:

0x800704CD "An operation was attempted on a nonexistent network connection."

Tell me that I'm not doing right? Thank you.

asp.net
html
video
asp.net-web-api
broadcast
asked on Stack Overflow Dec 5, 2015 by egorychmaster • edited Dec 8, 2015 by Mr Lister

1 Answer

0

I do so. I use this 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 run it <video> video player through technology SignalR.

answered on Stack Overflow Dec 7, 2015 by egorychmaster • edited Dec 13, 2015 by Mr Lister

User contributions licensed under CC BY-SA 3.0