Friday, 3 February 2012

Playing Videos like YouTube and Thumbnail Creation in ASP.Net

If you want to play video like youtube in your ASP.Net application what will you do? Can you able to generate thumbnail for the video programmatically? How did you play converted video?? All these are for Free of cost. No need to buy any software then how??. 

This article will provide solutions for the above questions. Generally we can see some video sites like youtube. Here we can watch streaming media. But when you upload a wmv file into server and try to play it. What happen? If the video is long it will take time to play. For this reason we have to convert the video to a streaming media(like swf) so that when the page is opened automatically video playing started. 

For this we have to convert the video which is uploaded by the user to swf. Then only we can able to play the video quickly. In order to convert the video we will use a software called FFMPEG. It’s open source software you can download this software from here.

Using FFMPEG we can able to generate thumbnails for the video what ever we are uploading. By using FFMPEG’s API we can perform various operations for the videos and audios.
In order to start this Application first we have to import following name spaces…

using System.IO;
using System.Diagnostics;
When User Clicks on Upload button then we have to perform following tasks.
  • Check the Format of Video
  • Convert the Video to flv format with FFMPEG
  • Generate a Thumbnail for the uploaded video
  • Delete the original video.

  • Check the Format of Video

    Here I am using Regular Expression to check the video format. I want to upload only wmv, mpeg, or avi format videos. Here is the regular expression


    <asp:RegularExpressionValidator id="req1" runat="server"

    ErrorMessage="Only wmv, avi or mpeg files are allowed!"

    ValidationExpression="^(([a-zA-Z]:)(\\{2}\w+)\$?)(\\(\w[\w].*))+(.wmv.avi.mpeg.MPEG)$"

    ControlToValidate="FileUpload1" Display="dynamic"></asp:RegularExpressionValidator>

  • Convert the Video to flv format with FFMPEG
    To Convert our video to SWF format FFMPEG need File Name, File Arguments. File Name is the name of the FFMPEG folder with full path. Arguments can be write as..
  • The general syntax is ..
    ffmpeg [[infile options][@option{-i} infile]]... {[outfile options] outfile}...

    filargs = "-i " + inputfile + " -ar 22050 " + outputfile;
    To run the FFMPEG exe we have to start a Process. This can be write as..
    Process proc;
    proc = new Process();
    proc.StartInfo.FileName = spath + "\\ffmpeg\\ffmpeg.exe";
    proc.StartInfo.Arguments = filargs;
    proc.StartInfo.UseShellExecute = false;
    proc.StartInfo.CreateNoWindow = false;
    proc.StartInfo.RedirectStandardOutput = false;
    try
    {
    proc.Start();
    }
    catch (Exception ex)
    {
    Response.Write(ex.Message);
    }
    proc.WaitForExit();
    proc.Close();

    • Generate a Thumbnail for the uploaded video

      For the thumbnail we have to provide path to where we have to save our thumbnail. Here is code for thumbnails..

  • //Create Thumbs
    string thumbpath, thumbname;
    string thumbargs;
    string thumbre;
    thumbpath = AppDomain.CurrentDomain.BaseDirectory + "Video\\Thumb\\";
    thumbname = thumbpath + withoutext + "%d" + ".jpg";
    thumbargs = "-i " + inputfile + " -vframes 1 -ss 00:00:07 -s 150x150 " + thumbname;
    Process thumbproc = new Process();
    thumbproc = new Process();
    thumbproc.StartInfo.FileName = spath + "\\ffmpeg\\ffmpeg.exe";
    thumbproc.StartInfo.Arguments = thumbargs;
    thumbproc.StartInfo.UseShellExecute = false;
    thumbproc.StartInfo.CreateNoWindow = false;
    thumbproc.StartInfo.RedirectStandardOutput = false;
    try
    {
    thumbproc.Start();
    }
    catch (Exception ex)
    {
    Response.Write(ex.Message);
    }
    thumbproc.WaitForExit();
    thumbproc.Close();
  • Delete the original video.
    Once the conversion process is completed we delete wmv file. Here is the code.
    File.Delete(inputfile);
  • Here the most important thing is “Permissions”. In order to convert the video and generate the thumbnails we have to provide all permissions to our application. Especially for the folder where your going to save the videos and thumbnails.
    If you’re using FFMPEG in Remote server then you have to give all permissions to FFMPEG folder also. Because here we are running an exe to convert the videos.
    When ever you upload videos more then 4096 KB, ASP.Net does not allow you to upload the video because It’s the Max HTTP Request size for any application. If you want change the HTTP request size you have to change the “maxRequestLength” in web.config section. Here is the complete code..

    <system.web>
    <httpRuntime
    executionTimeout="110"
    maxRequestLength="30000"
    requestLengthDiskThreshold="80"
    useFullyQualifiedRedirectUrl="false"
    minFreeThreads="8"
    minLocalRequestFreeThreads="4"
    appRequestQueueLimit="5000"
    enableKernelOutputCache="true"
    enableVersionHeader="true"
    requireRootedSaveAsPath="true"
    enable="true"
    shutdownTimeout="90"
    delayNotificationTimeout="5"
    waitChangeNotification="0"
    maxWaitChangeNotification="0"
    enableHeaderChecking="true"
    sendCacheControlHeader="true"
    apartmentThreading="false" />
    </system.web>
    Now you can able to upload videos upto 30MB.
    How to Play Video:
    Using swf player we can play the video. In the sample application you can find this player. For this player we have to give the source file name with correct path. You can find play.aspx file in source code to know more about how to play the videos.


    In Part-I, I am explained how to convert the video from one format to another format and generating thumbnails. In Part-II I am going to explain how to bind this thumbnail to Gridview or DataList dynamically from Database and how to play the videos.
    Note: In the sample I did not place the ffmpeg software due to the size.(Its around 10MB)
    You can download for here:ffmpeg.rar
    Downloadm Source Code





    Playing Videos - Part II

    Friday, September 26, 2008

    In Playing Videos-I I have been explained to upload a videos, how convert video from one format to another format and how to generate thumbnail automatically. In this post I am going to expalin how to retrive the videos and thumbnail dynamically from database. 

    First I am starting with DataBase part, Here I am creating a table called videos. And it contains 3 fields the are
    Video_ID,Video_File,Video_Image. You can see the table design pic.



    Here "Video_File" field contains Name of the video with extension. Here I am going to store the file name only. But the video file is located in server. "Video_Image" is the thumbnail file name with extension. Same as video thumbnail also located in server. 

    When I am uploading video, I am going to save the videofilename, Image name in database. Here I used very simple code to insert this details. Here is the code..... 



    private void Savedetails()
    {
    string filename, imgname;
    filename = Session["outputfile"].ToString();
    imgname = Session["thumbname"].ToString();
    SqlConnection con = new SqlConnection(ConfigurationManager.ConnectionStrings["TestConnectionString"].ConnectionString);
    con.Open();
    SqlCommand cmd = new SqlCommand("insert into video(Video_File,Video_Image) values('" + filename + "','" + imgname + "')", con);
    cmd.ExecuteNonQuery();
    cmd.Dispose();
    con.Close();
    }

    You can use stored procedures in back end for the better performance. Once the video uploading is finished then I am going to show these details in DataList. Here is the code to bind the video thumbnail in Datalist


    <asp:DataList ID="dtlstVideo" runat="server" GridLines="Vertical"
    RepeatColumns="2" RepeatDirection="Horizontal" BackColor="White"
    BorderColor="#999999" BorderStyle="None" BorderWidth="1px" CellPadding="5">
    <ItemTemplate>
    <td><a href="Play.aspx?vid=<%# DataBinder.Eval(Container.DataItem, "Video_ID") %>">
    <img src="Video/Thumb/<%# DataBinder.Eval(Container.DataItem, "Video_Image") %>" border="0" />
    </a>
    </td>
    </ItemTemplate>
    <FooterStyle BackColor="#CCCCCC" ForeColor="Black" />
    <AlternatingItemStyle BackColor="#DCDCDC" />
    <ItemStyle BackColor="#EEEEEE" ForeColor="Black" />
    <SelectedItemStyle BackColor="#008A8C" Font-Bold="True" ForeColor="White" />
    <HeaderStyle BackColor="#000084" Font-Bold="True" ForeColor="White" />
    </asp:DataList>



    when the user click on the datalist it will be redirected to play.aspx with video ID as the query string. In the
    play.aspx page I am goign to bind the SWF player with video file name. Here is the code...


    string vid,vfname;
    protected void Page_Load(object sender, EventArgs e)
    {
    vid = Request.QueryString["vid"].ToString();
    DataSet dst = (DataSet)Session["videods"];
    DataRow[] dw = dst.Tables[0].Select("Video_ID= '"+ vid +"'");
    if (dw.Length > 0)
    {
    vfname = dw[0]["Video_File"].ToString();
    }
    string plyr = "<embed src='Players/flvplayer.swf' width='425' height='355'
    bgcolor='#FFFFFF' type='application/x-shockwave-flash'
    pluginspage='http://www.macromedia.com/go/getflashplayer'
    flashvars='file=Video/SWF/" + vfname + "&autostart=false&frontcolor=0xCCCCCC&
    backcolor=0x000000&lightcolor=0x996600&showdownload=false&showeq=false&repeat=false&
    volume=100&useaudio=false&usecaptions=false&usefullscreen=true&usekeys=true'>
    </embed>";
    plh.Controls.Add(new LiteralControl(plyr));
    }

    You can see the screen shot of playing a video dynamically..

    Source Code

    Thursday, 2 February 2012

    Implementing Caching in ASP.NET

    In this article, you will learn about concepts, advantages, types of caching and about implementation of caching in ASP.NET applications. The code also serves as an example of using inline coding, creating user controls, trace mechanism, etc.
    What is Caching?
    Caching is a technique of persisting the data in memory for immediate access to requesting program calls. Many in the developer community consider caching as one of the features available to improve performance of Web applications.
    Why Caching?
    Consider a page that has list of Employee name, contact numbers and mail-Ids of existing employees of a company on an intranet accessible by all employees. This is very useful information that is available throughout the company and could also be one of the most accessed pages. The functionality of adding, updating or deleting is usually less intensive compared to more transaction-based systems like Purchase ordering, Voucher creation etc. Now in a normal scenario the process of querying database for each request is not cost-effective in terms of server resources, hence is lot better to cache or persist the data to avoid this costly loss of resources.
    The .NET Advantage
    ASP.NET provides the flexibility in terms of caching at different levels.

    1. Page Level Output Caching
    This is at the page level and one of the easiest means for caching pages. This requires one to specify Duration of cache and Attribute of caching.
    Syntax: <%@ OutputCache Duration="60" VaryByParam="none" %>
    The above syntax specifies that the page be cached for duration of 60 seconds and the value "none" for VaryByParam* attribute makes sure that there is a single cached page available for this duration specified.
    * VaryByParam can take various "key" parameter names in query string. Also there are other attributes like VaryByHeader, VaryByCustom etc. Please refer to MSDN for more on this.
    2. Fragment Caching
    Even though this definition refers to caching portion/s of page, it is actually caching a user control that can be used in a base web form page. In theory, if you have used include files in the traditional ASP model then this caching model is like caching these include files separately. In ASP.NET more often this is done through User Controls. Initially even though one feels a bit misleading, this is a significant technique that can be used especially when implementing "n" instances of the controls in various *.aspx pages. We can use the same syntax that we declared for the page level caching as shown above, but the power of fragment caching comes from the attribute "VaryByControl". Using this attribute one can cache a user control based on the properties exposed.
    Syntax: <%@ OutputCache Duration="60" VaryByControl="DepartmentId" %>
    The above syntax when declared within an *.ascx file ensures that the control is cached for 60 seconds and the number of representations of cached control is dependant on the property "DepartmentId" declared in the control.
    Add the following into an *.ascx file. Please note the use of tag "Control" and the cache declaration.
    <%@ Control Language="C#"%>
    <%@ outputcache duration="60" varybycontrol="DepartMentId" %>
    <script runat="server">
    private int _Departmentid=0;public int DepartMentId
    {
    get{return _Departmentid;}set{_Departmentid =value;}
    }
    //Load event of controlvoid Page_Load(Object sender, EventArgs e)
    {
    lblText.Text = "Time is " + DateTime.Now.ToString() + " for Department id = "
    + _Departmentid + "\n";
    }
    </script>
    <
    asp:Label id="lblText" runat="server"></asp:Label>
    Add the following to an *.aspx file. Please note the way "Register" tag is used; the declaration of control using syntax <[TagPrefix]:[TagName]>; Usage of property " DepartMentId". Open the page in two browsers and closely watch the Base form timing and the User control timing. Also note that the following page results in two copies or representation of user control in the cache.
    <%@ Page Language="C#" Trace="true" %>
    <%@ Register TagPrefix="CacheSample" TagName="Text" Src="CachingControl.ascx" %>
    <script runat=server>void Page_Load(Object sender, EventArgs e)
    {
    this.lbltime.Text ="Base form time is " + DateTime.Now.ToString() + "\n";
    }
    </script>
    <
    html>
    <
    head>
    </
    head>
    <
    body>
    <
    form runat="server" ID="Form2">
    <
    table>
    <
    tbody>
    <
    tr>
    <
    td>
    <
    asp:Label id="lbltime" runat="server"></asp:Label>
    </
    td>
    </
    tr>
    <
    tr>
    <
    td>
    <
    CACHESAMPLE:TEXT id="instance1" runat="Server" DepartMentId="0"></CACHESAMPLE:TEXT>
    </
    td>
    </
    tr>
    <
    tr>
    <
    td>
    <
    CACHESAMPLE:TEXT id="instance2" runat="Server" DepartMentId="1">
    </
    CACHESAMPLE:TEXT>
    </
    td>
    </
    tr>
    </
    tbody>
    </
    table> </form>
    </
    body>
    </
    html>
    3. Application Level Caching
    With Page level Output caching one cannot cache objects between pages within an application. Fragment caching is great in that sense but has limitations by using user controls as means to do. We can use the Cache object programmatically to take advantage of caching objects and share the same between pages. Further the availability of different overloaded methods gives a greater flexibility for our Cache policy like Timespan, Absolute expiration etc. But one of the biggest takes is the CacheDependancy. This means that one can create a cache and associate with it a dependency that is either another cache key or a file.
    In almost all Web applications there could be numerous master tables that act as lookups to application specific tables. For e.g. if you take up adding a Employee, usually one has master tables like "tblQualification" to get list of qualifications, "tblLocations" to get list of locations etc. These tables* are usually set during the initial application configuration phase and could be modified once a month or even less than that. Hence it makes sense for us to use them in our Cache rather than making calls to database on each request. But then what Cache Policy do we adopt?

    We cannot hold these objects in Cache for entire application instance, because if anybody changes data in these tables one has to also refresh the cache. It is here that CacheDependancy can be used.
    * Even though these tables are less frequently used for updates, they are extensively used in our select statements through out the applications.
    Find below the snippet that uses CacheDependancy. Here what I have done is to provide a list view of existing employees. You need to create a Database in Sql Server, setup some data before you can continue. The schema scripts are enclosed in the article.
    Add database connection value in Web.Config and change the value as per your setup.

    <appSettings>
    <
    add key="conn" value="Data Source=vishnu;trusted_connection=yes;Initial Catalog=Users"/>
    </
    appSettings>
    First I get the dataset into which I fill the user list. But before this I check for the cache initially if it exists I directly cast it to a dataset, if not create a cache again.
    daUsers.Fill(dsUsers,"tblUsers");
    I create the cache with "Users" as key using Cache.Insert* and link this with a file "Master.xml". This "Master.xml" is a XML file that contains Master data of "tblQualifications" and "tbllocations". I have used "Server.MapPath" to get the physical path of the file on the server. The CacheDependancy instance will make sure that any change in this dependency file means that you need to recreate your cache key definition. This is a great feature to use since I can recreate my cache only when required instead of caching the data at the page level.

    Cache.Insert("Users",dsUsers,new System.Web.Caching.CacheDependency(Server.MapPath("Master.xml")) , DateTime.Now.AddSeconds(45),TimeSpan.Zero);
    * For other overloaded parameters refer MSDN.
    Also note how we could use trace within to add my own statements.

    HttpContext.Current.Trace.Write("from Database..");
    <%@ Page Language="c#" Trace="true" %>
    <%@ import Namespace="System" %>
    <%@ import Namespace="System.Data" %>
    <%@ import Namespace="System.Data.SqlClient" %>
    <%@ import Namespace="System.Configuration" %>
    <%@ import Namespace="System.Web" %>
    <%@ import Namespace="System.Collections" %>
    <%@ import Namespace="System.IO" %>
    <script runat="server">void Page_Load(Object sender, EventArgs e)
    {
    DataSet dsUsers;
    try{if(Cache["Users"]==null)
    {
    SqlConnection cn;
    dsUsers = 
    new DataSet("new");
    cn = 
    new SqlConnection(ConfigurationSettings.AppSettings.Get("conn"));
    SqlDataAdapter daUsers;
    daUsers = 
    new SqlDataAdapter("Select * from tblUsers",cn);
    cn.Open();
    daUsers.Fill(dsUsers,"tblUsers");
    //Update the cache objectCache.Insert("Users",dsUsers, new System.Web.Caching.CacheDependency(
    Server.MapPath("Master.xml")), DateTime.Now.AddSeconds(45),TimeSpan.Zero);
    HttpContext.Current.Trace.Write(DateTime.Now.AddSeconds(45).ToString() + " 
    is expiry time..");
    cn.Close();
    cn.Dispose();
    HttpContext.Current.Trace.Write("from Database..");
    lblChange.Text ="From the database....";
    }
    else{
    HttpContext.Current.Trace.Write("From cache..");
    lblChange.Text ="From the cache....";
    dsUsers= (DataSet) Cache["Users"];
    }
    dlUsers.DataSource =dsUsers;
    dlUsers.DataMember = dsUsers.Tables[0].TableName ;
    //lblChange.Text += Server.MapPath("Master.xml");this.DataBind();
    }
    catch(Exception ex)
    {
    lblChange.Text = ex.Message;
    }
    }
    </script>
    <!
    DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" >
    <
    html>
    <
    head>
    <
    title>Cache Dependency Tester</title> <meta content="Microsoft Visual Studio 7.0" name="GENERATOR" />
    <
    meta content="C#" name="CODE_LANGUAGE" />
    <
    meta content="JavaScript" name="vs_defaultClientScript" />
    <
    meta content="http://schemas.microsoft.com/intellisense/ie5" name="vs_targetSchema" />
    </
    head>
    <
    body ms_positioning="GridLayout">
    <
    form id="Form1" method="post" runat="server">
    <
    asp:DataList id="dlUsers" style="Z-INDEX: 101; LEFT: 44px; POSITION: absolute; TOP: 104px" runat="server" Height="148px" Width="343px" BorderWidth="1px"GridLines="Horizontal" CellPadding="4" BackColor="White" ForeColor="Black" BorderStyle="None" BorderColor="#CCCCCC">
    <
    SelectedItemStyle font-bold="True" forecolor="White" backcolor="#CC3333"></SelectedItemStyle>
    <
    FooterStyle forecolor="Black" backcolor="#CCCC99"></FooterStyle>
    <
    HeaderStyle font-bold="True" forecolor="White" backcolor="#333333"></HeaderStyle>
    <
    ItemTemplate>
    <
    table>
    <
    tr>
    <
    td><%#DataBinder.Eval(Container.DataItem,"UserId")%></td>
    <
    td><%#DataBinder.Eval(Container.DataItem,"FirstName")%></td>
    <
    td><%#DataBinder.Eval(Container.DataItem,"LastName")%></td>
    </
    tr>
    </
    table>
    </
    ItemTemplate>
    </
    asp:DataList>
    <
    asp:Label id="lblChange" style="Z-INDEX: 102; LEFT: 46px; POSITION: absolute; TOP: 63px" runat="server" Height="28px" Width="295px"></asp:Label>
    <
    asp:Button id="btnMaster" style="Z-INDEX: 103; LEFT: 50px; POSITION: absolute; TOP: 293px" onclick="btnMaster_Click" runat="server" Text="Refresh Master"></asp:Button>
    </
    form>
    </
    body>
    </
    html>
    We created the page that initiates and uses the Cache. For testing purpose we need another page that will overwrite this "Master.xml" on click of a button for which the code snippet is as follows. This ideally should be our master maintenance page that adds/updates Master records in database and overwrites the XML. But to make it easy I have just written an overwriting sample.
    <%@ Page Language="C#" Trace="true"%>
    <%@ import Namespace="System" %>
    <%@ import Namespace="System.Data" %>
    <%@ import Namespace="System.Data.SqlClient" %>
    <script runat="server">void btnMaster_Click(Object sender, EventArgs e)
    {
    //Call save functionthis.Save();
    }
    void Save()
    {
    try{
    SqlConnection cn;
    DataSet dsUsers = 
    new DataSet("Users");//I have used this to get the Connectionstring from the
    //Configuration file.
    cn = new SqlConnection(ConfigurationSettings.AppSettings.Get("conn"));
    SqlDataAdapter daQualification;
    SqlDataAdapter daLocations;
    daQualification = 
    new SqlDataAdapter("Select * from tblqualifications",cn);
    daLocations = 
    new SqlDataAdapter("Select * from tblLocations",cn);
    cn.Open();
    daQualification.Fill(dsUsers,"tblqualifications");
    daLocations.Fill(dsUsers,"tblLocations");
    HttpContext.Current.Trace.Write("Masters data up..");
    //Overwrite the XML file. Also please read MSDN on the overloaded parameters for WriteXmldsUsers.WriteXml(HttpContext.Current.Server.MapPath
    "Master.xml"),XmlWriteMode.WriteSchema);
    cn.Close();
    cn.Dispose();
    }
    catch(Exception ex)
    {
    throw new Exception(ex.Message);
    }
    }

    </script>
    <
    html>
    <
    head>
    </
    head>
    <
    body>
    <
    form runat="server" ID="Form1">
    <
    span> <table>
    <
    tbody>
    <
    tr>
    <
    td>
    <
    label id="lblRefresh" runat="server">Rewrite the XML File by clicking the buttonbelow.</label> </td>
    </
    tr>
    <
    tr align="middle">
    <
    td>
    <
    asp:Button id="btnMaster" onclick="btnMaster_Click" runat="server"Text="Write XML"></asp:Button>
    </
    td>
    </
    tr>
    </
    tbody>
    </
    table>
    </
    span>
    </
    form>
    </
    body>
    </
    html>
    Now once you have created the above pages i.e. one that implements caching and other that overwrites the dependency file, create two instance of browser and open the cache implementation page and note for trace, label text; open the other instance of browser with the page which overwrites the XML. Note the former, the first time it fetches data from the database and the subsequent request will be from cache till your expiration time of 45 seconds is reached or anyone overwrites or changes the "Master.xml" file. Also give a look on Timespan parameter since you have a concept of Sliding expiration that can also be implemented. Keep refreshing the first page and you will see that trace indicates the cached page retrieval. Click the overwrite XML button on the latter page that would overwrite the XML and again refresh the former page to note that the data is retrieved from database. Though in this example I have not shown any direct relation between the cached data and the dependency file (like get values from dependency file and merge with cached object etc) in terms of integrated usage, this could very easily be designed and implemented. Dependency caching is a powerful technique that .NET supports and should be utilized wherever applicable.
    Conclusion
    Caching is a technique that definitely improves the performance of web applications if one is careful to have a balance in terms of which data needs to be cached and parameter values for expiration policy.