ASP.NET Web Api – Video (mp4/mjpeg/html) streaming inside self-host app with Owin/Katana

In previous posts for ASP.NET Web API, we’ve discussed how to get and post data to our REST web service. The service will be hosted then directly in IIS. Today I would like to make a small demo of ASP.NET Web API for streaming video in a self-host application. The self-host will be implemented by using Owin/Katana. Having this feature you can enable a web service embedded in your application and streaming data to any app connecting to your PC app.

1. Prerequisites

I won’t go in details how ASP.NET Web API works. If you have no experience with ASP.NET Web API, I suggest you reading this How to consume ASP.NET Web API RC with HttpClient?.

I’m using Visual Studio 2015 as my IDE. Some of new syntax are only supported in this IDE version. Maybe the code isn’t able to be compiled in older Visual Studio version.

2. Streaming

2.1 Project solution

As mentioned above, we’re using a self-host for our web service. Thanks to Owin/Katana we can do it easily just by making reference to a Nuget Package. Create a Console Application and add a Nuget reference to Microsoft.AspNet.WebApi.OwinSelfHost.

Microsoft.AspNet.WebApi.OwinSelfHost

Create a Startup class with minimal configuration like following

public class Startup
{
	// This code configures Web API. The Startup class is specified as a type
	// parameter in the WebApp.Start method.
	public void Configuration(IAppBuilder appBuilder)
	{
		// Configure Web API for self-host.
		HttpConfiguration config = new HttpConfiguration();
		config.Routes.MapHttpRoute(
			name: "DefaultApi",
			routeTemplate: "api/{controller}/{id}",
			defaults: new { id = RouteParameter.Optional }
		);

		appBuilder.UseWebApi(config);
	}
}

Then start our web service with following code at start up of program.

private static void Main(string[] args)
{
	string baseAddress = "http://localhost:9000/";

	// Start OWIN host
	WebApp.Start<Startup>(url: baseAddress);
	Console.WriteLine("Streaming start...");
	Console.ReadLine();
}

When the console application get started, an embedded web service will also run inside the console application.

2.2 Video streaming

Now we already have a self-host web service running inside our application and would like to use it for video streaming. Like any other REST service, we have to define a controller for providing this streaming resource

public class CameraController : ApiController
{
	[HttpGet]
	public HttpResponseMessage FromVideo(string videoName)
	{
		var video = new VideoStream(videoName);
		Func<Stream, HttpContent, TransportContext, Task> func = video.WriteToStream;
		var response = Request.CreateResponse();
		response.Content = new PushStreamContent(func, new MediaTypeHeaderValue("video/mp4"));
		return response;
	}
}

The PushStreamContent enables scenarios where a data producer wants to write directly (either synchronously or asynchronously) using a stream. The WriteToStream function will push data to output stream and stream to clients.

internal class VideoStream
{
	private readonly string videoName;

	public VideoStream(string videoName)
	{
		this.videoName = videoName;
	}

	public async Task WriteToStream(Stream outputStream, HttpContent content, TransportContext context)
	{
		string videoFileName = "TestData\\Videos\\" + videoName + ".mp4";
		try
		{
			var buffer = new byte[65536];

			using (var video = File.Open(videoFileName, FileMode.Open, FileAccess.Read))
			{
				var length = (int)video.Length;
				var bytesRead = 1;

				while (length > 0 && bytesRead > 0)
				{
					bytesRead = video.Read(buffer, 0, Math.Min(length, buffer.Length));
					await outputStream.WriteAsync(buffer, 0, bytesRead);
					length -= bytesRead;
				}
			}
		}
		catch (Exception ex)
		{
			return;
		}
		finally
		{
			outputStream.Close();
		}
	}
}

In code listing above, data will be splitted into chunk of data with size of 64 kBytes. Chunks will be then written to output stream asynchronously and pushed to clients. To test if streaming works, we can make a small HTML file with video tag to display content of that stream to browser.

<html>
<body>
    <video width="480" height="320" controls="controls" autoplay="autoplay">
        <source src="http://localhost:9000/api/camera/fromvideo/?videoName=Christmas" type="video/mp4">
    </video>
</body>
</html>

2.2 MJPEG streaming

With ASP.NET Web API, we can stream not only video but also images by using MJPEG encoding. The core component for streaming is also PushStreamContent. However the code has slightly changes so that we can enable MJPEG encoding for streaming images. In the controller, we call WriteToStream as following.

[HttpGet]
public HttpResponseMessage FromImages()
{
	var imageStream = new ImageStream();
	Func<Stream, HttpContent, TransportContext, Task> func = imageStream.WriteToStream;
	var response = Request.CreateResponse();
	response.Content = new PushStreamContent(func);
	response.Content.Headers.Remove("Content-Type");
	response.Content.Headers.TryAddWithoutValidation("Content-Type", "multipart/x-mixed-replace;boundary=" + imageStream.Boundary);
	return response;
}

The Content-Type need to be explicitly declared for our response. In WriteToStream, the MJPEG encoding need to be applied

internal class ImageStream
{
	public object Boundary { get; private set; } = "HintDesk";

	public async Task WriteToStream(Stream outputStream, HttpContent content, TransportContext context)
	{
		byte[] newLine = Encoding.UTF8.GetBytes("\r\n");

		foreach (var file in Directory.GetFiles(@"TestData\Images", "*.jpg"))
		{
			var fileInfo = new FileInfo(file);
			var header = $"--{Boundary}\r\nContent-Type: image/jpeg\r\nContent-Length: {fileInfo.Length}\r\n\r\n";
			var headerData = Encoding.UTF8.GetBytes(header);
			await outputStream.WriteAsync(headerData, 0, headerData.Length);
			await fileInfo.OpenRead().CopyToAsync(outputStream);
			await outputStream.WriteAsync(newLine, 0, newLine.Length);
			await Task.Delay(1000 / 30);
		}
	}
}

To test if streaming works, just use the web browser browsing to http://localhost:9000/api/camera/fromimages. The images will be sequentially streamed to server.

2.3 Html streaming

In this section, we’ll stream HTML content to clients. Our demo HTML content will contain HTML code for layout and Javascript for handling some basic actions. The HTML content will also stream MJPEG and Video from controllers from two sections above when its content is sent to clients.

<html>
<head>
    <script>#REPLACE_WITH_JS#</script>
</head>
<body ng-app="CameraApp">
    <div ng-controller="MainController">
        <div>
            <img src="http://localhost:9000/api/camera/fromimages" width="480" height="320" alt="">
        </div>
        <div>
            <video id="videoPlayer" width="480" height="320" controls="controls">
                <source data-ng-src="{{selectedVideoUrl}}" type="video/mp4">
                <!--  <source src="http://localhost:9000/api/camera/fromvideo/?videoName=Christmas" type="video/mp4">-->
            </video>
        </div>
        <div>
            <select ng-model="selectedVideo">
                <option ng-repeat="video in videos" value="{{video}}">{{video}}</option>
            </select>
        </div>
        <div>
            <button ng-click="start()">Start</button>
            <button ng-click="stop()">Stop</button>
        </div>
    </div>
</body>
</html>

AngularJS framework was used for enhancing data binding. The framework and custom javascript will be directly embedded inside HTML (not as file separately).

public class HtmlController : ApiController
{
	public HttpResponseMessage Get()
	{
		string htmlFileName = "TestData\\Html\\Data.html";
		string angularJS = "TestData\\Html\\angular.min.js";
		string mainJS = "TestData\\Html\\main.js";
		string js = File.ReadAllText(angularJS);
		js += Environment.NewLine + File.ReadAllText(mainJS);
		string html = File.ReadAllText(htmlFileName);
		html = html.Replace("#REPLACE_WITH_JS#", js);
		var response = new HttpResponseMessage { Content = new StringContent(html) };
		response.Content.Headers.ContentType = new MediaTypeHeaderValue("text/html");
		return response;
	}
}

In demo you can see HTML streaming by browsing to URL http://localhost:9000/api/html in your browser.

HTML Streaming

3. Updates

3.1 Update 23.12.2015

The following commands will help you to open the port and registered program itself with firewall so that the self-host server can be accessed from other computer in same network

netsh http add urlacl "http://*:2612/" user=everyone
netsh advfirewall firewall add rule name="Open Port 80" dir=in action=allow protocol=TCP localport=8080

4. Source code

Source code: https://bitbucket.org/hintdesk/dotnet-aspnet-web-api-video-mp4-mjpeg-html-streaming-inside

6 thoughts on “ASP.NET Web Api – Video (mp4/mjpeg/html) streaming inside self-host app with Owin/Katana”

  1. Hi very good job ! Thanks for sharing.
    I downloaded the project to test it and I faced an issue. I can not stream at the sameTime more than 8 videos. Is this a limitation from owin katana ?

  2. Great work. How about if you have a Bitmap or Image object instead of file for the 2.2 (mjpeg) streaming?

Leave a Reply

Your email address will not be published. Required fields are marked *