Asynchronous Video Live Streaming with ASP.NET Web APIs 2.0

Introduction

No matter what kind of project you are working on, at some point you will find the need to stream videos in your website or mobile apps for many purposes. So, we will learn how we can live stream our video content over HTTP, using ASP.NET Web APIs.

This is my first ever writing on ASP.NET Web APIs, so I’ll try my best to deliver what I learned about HTTP, REST, Web Services, Web API, and as well as asynchronous programming. As the article is about how to live stream videos with ASP.NET Web APIs, I still have to introduce other terminologies listed above for the folks who don’t already know about them. At the end, we will learn how we can set up a web service using ASP.NET Web APIs to live stream the videos asynchronously over HTTP protocol.

Background

I assume that you already have experience working with C# in ASP.NET (MVC, Web APIs) and generally know about how the Web works, about HTTP, about Servers and clients. You don’t need to be a master of them but you must have fair knowledge to follow along.

I’m also assuming that you have basic familiarity with working with Visual Studio. I’ll be using Visual Studio 2015 with Update 2 for this demo. If you are using Visual Studio 2013, then it’s perfectly fine.

ASP.NET Web APIs and REST

ASP.NET Web API is a new framework that Microsoft included in the ASP.NET family of technologies. Using ASP.NET Web APIs, we can create online Web Services or Web APIs, which client apps can consume to retrieve, update, and delete data over HTTP. Web services normally serve data in the form of JSON or XML.

The term API stands for Application Programming Interface, so whether someone says Web API or Web Service, it’s the same thing. Online Web Services have different names and formats like SOAP-based Services and most popular of them is REST or RESTful Services.

REST stands for Representational State Transfer. REST is an architectural pattern, that is used to create online Web Services to serve data in form of JSON and XML over HTTP protocol and that’s what exactly ASP.NET Web APIs are there for. Finally, we know we can create RESTful Services with ASP.NET Web APIs.

Asynchronous Programming

In asynchronous programming, we perform multiple tasks simultaneously parallel to each other at same time and the parallel running threads notify back to the calling thread after the completion of the task. In C# the best practice is to use TPL (Task Parallel Library) for parallel programming instead of using System.Threading.Thread. The task provides you with more control over threads.

If a task is not CPU bound but instead it's I/O bound or network bound where calling threads have to wait for database to respond or have to wait for a network call to respond, then we should use asynchronous pieces of code with async and await keywords. If you are not already familiar with asynchronous programming with Asyn and await, then click here to learn because Microsoft has made it very easy to write asynchronous code blocks with async and await for IO and Network tasks. Remember in IO or network-bound tasks thread remains idle to wait for a response, instead, why don’t we use them to perform some other tasks because we have limited resources?

Now, there is one question left in our minds: “What is Asynchronous Live streaming?”

What is Asynchronous video streaming?

In asynchronous video streaming, we send packets of data to the receiving client instead of sending the complete file and the client will be the browser in our case. For asynchronous live streaming with ASP.NET Web APIs, we will make use of the PushStreamContent class. The PushStreamContent class makes it possible to gradually send packets of data to the receiving client. With asynchronous streaming, we are reducing the load on the server side so that the Server doesn’t have to serve the whole file at a time, instead it can serve it with a specific size of packets.

On the client side, we will use the HTML 5 video element to playback the received video content. As we are asynchronously delivering the video content to the client, we don’t have to wait for the whole file to be downloaded. The playback will be immediately started. As long as client will be requesting for data, the server will be serving the client and if the client disconnects, the streaming will be finished.

Demo

A lot more chit-chat... So now, let’s create a handy ASP.Net MVC app that can stream videos from the Server.

Remember, ASP.Net Web APIs are built from ASP.Net MVC, so the same conventions apply here too and you can also add a Web API controller inside an MVC project which is perfectly fine in ASP.Net 4.5. In fact, you can start from any ASP.NET project and later, you can add any component it from the ASP.NET family of technologies whether it's a Web API controller, MVC controller, Web form, or SignalR hub.

Create a new empty ASP.NET project with a folder structure selected as MVC and paste any sample video into the root directory of the project for demo purposes.

 ASP.NET

Right-click on the Controllers folder and add a new Web API 2 empty controller with any name. In my case, I’ll name it SampleController.

 Web API

Now, first, we have to read the file from the server with FileStream so that we can write to the output stream. Now, we’ll create a new Helper method that can read the file and write its output stream. Paste the following code in SampleController which is not a good practice but we can do this for demo purposes.

public async void WriteContentToStream(Stream outputStream, HttpContent content, TransportContext transportContext)
{
    // Path of the file we need to read
    var filePath = HttpContext.Current.Server.MapPath("~/MicrosoftBizSparkWorksWithStartups.mp4");  
    // Set the size of the buffer (you can adjust this value)
    int bufferSize = 1000;
    byte[] buffer = new byte[bufferSize];
    // Read the file from the server using FileStream
    using (var fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read))
    {
        int totalSize = (int)fileStream.Length;
        
        // Read bytes from the file as long as the total size is greater than 0
        while (totalSize > 0)
        {
            int count = totalSize > bufferSize ? bufferSize : totalSize;    
            // Read the buffer from the original file
            int sizeOfReadBuffer = fileStream.Read(buffer, 0, count);  
            // Write the read buffer to the output stream
            await outputStream.WriteAsync(buffer, 0, sizeOfReadBuffer);   
            // Decrement the total size of the file
            totalSize -= sizeOfReadBuffer;
        }
    }
}

Create a new action method with the name GetVideoContent. Again, I am assuming that you have basic familiarity with ASP.NET web APIs and know how routing works. In the GetVideoContent action, we will write the code that can gradually write our video's content returned from WriteContentToStream to HTTP response.

public HttpResponseMessage GetVideoContent()
{
    var httpResponse = Request.CreateResponse();
    httpResponse.Content = new PushStreamContent((Action<Stream, HttpContent, TransportContext>)WriteContentToStream);
    return httpResponse;
}

Now, on the server side, the final thing left is to register a route so that we can call into our GetVideoContent action with the URL “API/Sample”.

public static void Register(HttpConfiguration config)
{
    config.MapHttpAttributeRoutes();
    config.Routes.MapHttpRoute(
        name: "DefaultApi",
        routeTemplate: "api/{controller}/{id}",
        defaults: new
        {
            id = RouteParameter.Optional
        }
    );
}

Now, our Server is ready to serve the content on HTTP Get request at “API/Sample”. On the client side, all we need is an HTML 5 video element with src=” API/Sample”. Create a new HTML file and put the HTML 5 video element in it.

HTTP

Now, right-click on the file and click view in the browser.

 Browser

And finally, now you can see the playback.

Playback

I’m using my own media player but you can use the default controls for demo purposes and I’m assuming you know how to use HTML 5 controls.

SampleController.cs

using System;
using System.IO;
using System.Net;
using System.Net.Http;
using System.Web;
using System.Web.Http;

namespace MediaPlayer.Controllers {
    public class SampleController: ApiController {
        public HttpResponseMessage GetVideoContent() {
            var httpResponce = Request.CreateResponse();
            httpResponce.Content = new PushStreamContent((Action<Stream, HttpContent, TransportContext>) WriteContentToStream);
            return httpResponce;
        }

        public async void WriteContentToStream(Stream outputStream, HttpContent content, TransportContext transportContext) {
            //path of file which we have to read// 
            var filePath = HttpContext.Current.Server.MapPath("~/MicrosoftBizSparkWorksWithStartups.mp4");
            //here set the size of buffer, you can set any size 
            int bufferSize = 1000;
            byte[] buffer = new byte[bufferSize];
            //here we re using FileStream to read file from server// 
            using(var fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read)) {
                int totalSize = (int) fileStream.Length;
                /*here we are saying read bytes from file as long as total size of file 
                  is greater then 0*/
                while (totalSize > 0) {
                    int count = totalSize > bufferSize ? bufferSize : totalSize;
                    //here we are reading the buffer from orginal file 
                    int sizeOfReadedBuffer = fileStream.Read(buffer, 0, count);
                    //here we are writing the readed buffer to output// 
                    await outputStream.WriteAsync(buffer, 0, sizeOfReadedBuffer);
                    //and finally after writing to output stream decrementing it to total size of file. 
                    totalSize -= sizeOfReadedBuffer;
                }
            }
        }
    }
}

WebApiConfig.cs

using System.Web.Http;

namespace MediaPlayer
{
    public static class WebApiConfig
    {
        public static void Register(HttpConfiguration config)
        {
            config.MapHttpAttributeRoutes();
            config.Routes.MapHttpRoute(name: "DefaultApi", routeTemplate: "api/{controller}/{id}", defaults: new
            {
                id = RouteParameter.Optional
            });
        }
    }
}


Similar Articles