Introduction
In this Article, I am performing on How to store a video into Database? and How to stream it in MVC5 application?
Background
To perform this, no advance knowledge of ASP.NET MVC is needed. One should have knowledge of converting stream to bytes.
Using the code
Let's start.
First of all we need to create a database and table. We need to identify, which are the fields to include in the table. Create database VideoStreaming and then create table with following fields.
SrNo, FileName, FileType and Video
Datatypes of the above filed are mentioned below.
USE [VideoStreaming]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
SET ANSI_PADDING ON
GO
CREATE TABLE [dbo].[VideoSteam](
[SrNo] [int] IDENTITY(1,1) NOT NULL,
[FileName] [nvarchar](50) NOT NULL,
[FileType] [nvarchar](20) NOT NULL,
[Video] [varbinary](max) NOT NULL,
CONSTRAINT [PK_Video] PRIMARY KEY CLUSTERED
(
[SrNo] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GO
SET ANSI_PADDING OFF
GO
After creating database and table, goto visual studio and Create new MVC5 project with no authentication. Goto solution exporer. right click on models and then click New Item. and add ADO.Net Entity Data model. After adding Entity model, Add a Class file in same directory and name it VideoVM. This is the View Model we are going to use. Paste below lines in your View Model class file.
public class VideoVM
{
public string Name { get; set; }
public string ext { get; set; }
public byte[] video { get; set; }
public long id { get; set; }
}
now add Home controller by right clicking on controller folder in solution exporer. Add Index view by right clicking on Index action and add view.
Open Index.cshtml page. add following lines in the page.
<div class="col-md-12">
@using (Html.BeginForm("Index", "Home", FormMethod.Post, new { @id = "frmform", @enctype = "multipart/form-data" }))
{
<br />
<table>
<tr>
<td width="50px">Video</td>
<td class="text-center">
<input type="file" name="file" id="file" />
</td>
<td>
<button type="submit">Submit</button>
</td>
</tr>
</table>
}
</div>
Here, we have created a form with post method. This will post the video file to Index action on Home controller. Here, we need to store the video to database.
Now goto controller and create Index action which is of HttpPost type.
to save a file in database we need to convert it into byte array. Create byte array. Read the file using MemoryStream and write it to byte array. After converting file to byte array we can store it in the database.
If user has uploaded any video rathar than MP4, than we need to convert it to MP4. For that I've used NReco Video convertor.
To add Nreco Video convertor to your project, open Package Manger console and run following command.
Install-Package NReco.VideoConverter
[http://www.nrecosite.com/video_converter_net.aspx]
After adding NReco reference to site, Refer the below code.
[HttpPost]
public ActionResult Index(HttpPostedFileBase file)
{
VideoSteam video = new VideoSteam();
if (file != null)
{
string path = Server.MapPath("~/Upload/TempUpload/" + file.FileName);
file.SaveAs(path);
var ffMpeg = new FFMpegConverter();
var tname = Guid.NewGuid();
string output = Server.MapPath("~/Upload/TempUpload/" + tname + ".mp4");
ffMpeg.ConvertMedia(path, output, Format.mp4);
byte[] buffer = new byte[file.ContentLength];
using (MemoryStream ms = new MemoryStream())
{
using (FileStream tempfile = new FileStream(output, FileMode.Open, FileAccess.Read))
{
buffer = new byte[tempfile.Length];
tempfile.Read(buffer, 0, (int)tempfile.Length);
ms.Write(buffer, 0, (int)tempfile.Length);
}
}
string fname = Guid.NewGuid().ToString();
string ftype = "mp4";
using (db = new DBEntities())
{
video.FileName = fname;
video.FileType = ftype;
video.Video = buffer;
db.VideoSteams.Add(video);
db.SaveChanges();
}
}
return RedirectToAction("Index","Home");
}
Yea, we have saved the video in database. Now We need to show this video on our page where user can watch it.
Add following line to Index Action of type HttpGet
[httpGet]
public ActionResult Index()
{
List<VideoVM> list = new List<VideoVM>();
using (db = new DbEntities())
{
list = db.VideoSteams.Select(m=>new VideoVM {
ext = m.FileType,
Name = m.FileName,
id = m.SrNo
}).ToList();
}
return View(list);
}
We have sent a list of type VideoVM to our view, which consist of list of videos saved in database. To show video on view add following line on the top of the cshtml page
@model List<VideoStreamingDemo.Models.VideoVM>
now add following lines to cshtml page. To render video on page, Video tag of HTML5 is used. We need to create an action which returns particular video. that action name is VideoStream which we need to give in src prop of Video tag. We need to also specify the video id to fetch particular video.
<div class="col-md-12">
<ul class="list-inline">
@foreach (var item in Model)
{
<li>
<video width="100%" height="240" controls poster="@Url.Action("GetThumbnail", "Home", new { id = @item.id })">
<source src="@Url.Action("VideoStream","Home", new { id=@item.id})" type="video/@item.ext">
</video>
<label>@item.Name</label>
<a href="@Url.Action("DeleteVideo","Home", new { id=item.id})">Delete</a>
</li>
}
</ul>
</div>
Add following lines to controller. Here we need to create a void method. If we do so, we will not be able to call it from view. So EmptyResult method is used. When we returns EmptyResult then it is automatically converted to the type of Void().
VideoStream method will return video, where as GetThumnail method will return thumnail image. Thumnail creation reference http://www.joshholmes.com/blog/2009/01/27/returningthumbnailswiththemvcframework/
[HttpGet]
public EmptyResult VideoStream(long id = 0)
{
using (db = new DbEntities())
{
VideoVM vm = new VideoVM();
vm = db.VideoSteams.Where(m => m.SrNo == id).Select(m => new VideoVM
{
Name=m.FileName,
ext = m.FileType,
id = m.SrNo,
video = m.Video
}).FirstOrDefault();
HttpContext.Response.AddHeader("Content-Disposition", "attachment; filename="+vm.Name+"."+vm.ext);
HttpContext.Response.BinaryWrite(vm.video);
return new EmptyResult(); ;
}
}
public ActionResult DeleteVideo(long id = 0)
{
if (id > 0)
{
using (db = new DbEntities())
{
VideoSteam vs = new VideoSteam();
vs = db.VideoSteams.Where(m => m.SrNo == id).FirstOrDefault();
db.VideoSteams.Remove(vs);
db.SaveChanges();
}
}
return RedirectToAction("Index", "Home");
}
public ActionResult GetThumbnail(long id)
{
using (db = new DBEntities())
{
VideoSteam vs = new VideoSteam();
vs = db.VideoSteams.Where(m => m.SrNo == id).FirstOrDefault();
Image thumbnail = null;
var ffMpeg = new NReco.VideoConverter.FFMpegConverter();
float? frameTime = 07;
var path = Server.MapPath("~/Upload/TempUpload/" + vs.FileName + "." + vs.FileType);
string imageFilePath = Server.MapPath("~/Upload/TempUpload/" + vs.FileName + ".jpg");
ffMpeg.GetVideoThumbnail(path, imageFilePath, frameTime);
using (FileStream imageStream = new FileStream(imageFilePath, FileMode.Open, FileAccess.Read))
{
thumbnail = Image.FromFile(imageFilePath);
}
using (MemoryStream ms = new MemoryStream())
{
thumbnail.Save(ms, System.Drawing.Imaging.ImageFormat.Jpeg);
HttpContext.Response.ContentType = "image/bmp";
HttpContext.Response.BinaryWrite(ms.ToArray());
HttpContext.Response.End();
}
}
return new EmptyResult();
}
reffering http://www.dotnetcurry.com/aspnet-mvc/998/play-videos-aspnet-mvc-custom-action-filter, it is found that to stream video we need to attach the bytes to the response of the page. As seen in above code, we have added header and wrote bytes to the response.
We are done. Run the project and Enjoy streaming.
Note : as stated in http://www.w3schools.com/tags/tag_video.asp only video of only three formats , MP4, webm and ogg , will be worked with HTML5 video tag. For this reason we have converted uploaded videos to MP4.