Streaming Video Content to a Browser using Web API Reply

misc1Recently, a client reached out to me that their video streaming wouldn’t allow for seeking (fast-forward & rewind).  Not knowing a lot about streaming, I did some google searching and found out that if the API is not setup to support seeking, then it will not work.

I found a great starting point for this:

https://www.codeproject.com/Articles/820146/HTTP-Partial-Content-In-ASP-NET-Web-API-Video

Basically, the browser will request various seek-points in the video and the endpoint must be able to handle this and return the requested chunks of media.  The requests will send a range header value that the API must use to return the proper spot in the video.  Also, the endpoint must set the status code to “Partial Content”.

I refactored the code a bit and created this Streamer class:

    class FileStreamer
    {
        public FileInfo FileInfo { get; set; }
        public long Start { get; set; }
        public long End { get; set; }

        public async Task WriteToStream(Stream outputStream, HttpContent content, TransportContext context)
        {
            try
            {
                var buffer = new byte[65536];
                using (var video = FileInfo.OpenRead())
                {
                    if (End == -1)
                    {
                        End = video.Length;
                    }
                    var position = Start;
                    var bytesLeft = End - Start + 1;
                    video.Position = Start;
                    while (position <= End)
                    {
                        var bytesRead = video.Read(buffer, 0, (int) Math.Min(bytesLeft, buffer.Length));
                        await outputStream.WriteAsync(buffer, 0, bytesRead);
                        position += bytesRead;
                        bytesLeft = End - position + 1;
                    }
                }
            }
            catch (Exception ex)
            {
                // fail silently
            }
            finally
            {
                outputStream.Close();
            }
        }
    }

It takes the file info and the start and end positions, in bytes, to return.

Now, in the controller, we must do a few things:

  1. Set the FileInfo Object on the Streamer object
  2. if the range is set in the header, then we must do the following:
    1. Retrieve the start & end indices from the header
    2. Set the corresponding properties in the Streamer object.
    3.  Set Content Length and the Content Range to the correct values in the response header.
    4. Set the status code to “Partial Content”
  3. if the range is not set, then we must:
    1. Return the media file as normal, which just entails returning the media file
    2. Set the status code to “Ok”

Sample controller code:

        public HttpResponseMessage Get(string filename)
        {

            var filePath = GetFilePath(filename);
            if (!File.Exists(filePath))
                return new HttpResponseMessage(HttpStatusCode.NotFound);

            var response = Request.CreateResponse();
            response.Headers.AcceptRanges.Add("bytes");

            var streamer = new FileStreamer();
            streamer.FileInfo = new FileInfo(filePath);
            response.Content = new PushStreamContent(streamer.WriteToStream, GetMimeType(filePath));

            RangeHeaderValue rangeHeader = Request.Headers.Range;
            if (rangeHeader != null)
            {
                long totalLength = streamer.FileInfo.Length;
                var range = rangeHeader.Ranges.First();
                streamer.Start = range.From ?? 0;
                streamer.End = range.To ?? totalLength - 1;

                response.Content.Headers.ContentLength = streamer.End - streamer.Start + 1;
                response.Content.Headers.ContentRange = new ContentRangeHeaderValue(streamer.Start, streamer.End,
                    totalLength);
                response.StatusCode = HttpStatusCode.PartialContent;
            }
            else
            {
                response.StatusCode = HttpStatusCode.OK;
            }

            return response;
        }

the standard way to reference the videos in html is to just use a video tag:

<video style="width:100%;height:100%;"                 
       controls                 
       autoplay="true"                 
       src="http://localhost:9000/api/getFile/testfile1.mp3">
</video>

 

And not your videos support streaming!

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s