Streaming large video files .net

Jake picture Jake · May 31, 2013 · Viewed 20.2k times · Source

I am trying to stream a large file in webforms from an HttpHandler. It doesn't seem to work because its not streaming the file. Instead its reading the file into memory then sends it back to the client. I look all over for a solution and the solution are telling me that they stream the file when they are doing the same thing. My solution that stream is this:

using (Stream fileStream = File.OpenRead(path))
{
    context.Response.Cache.SetExpires(DateTime.UtcNow.AddMinutes(360.0));
    context.Response.Cache.SetCacheability(HttpCacheability.Public);
    context.Response.AppendHeader("Content-Type", "video/mp4");
    context.Response.AppendHeader("content-length", file.Length);
    byte[] buffer = new byte[1024];
    while (true)
    {
      if (context.Response.IsClientConnected)
     {
       int bytesRead = fileStream.Read(buffer, 0, buffer.Length);
       if (bytesRead == 0) break;
       context.Response.OutputStream.Write(buffer, 0, bytesRead);
       context.Response.Flush();
     }
     else
     {
       break;
     }

   }
   context.Response.End();
}

What is happening is for small files if I debug the code, it will play the video but not until it reaches the context.Respond.End() line. But for large files this will not work because it is storing the whole file in memory which will bring issues.

Answer

Maxad picture Maxad · May 31, 2013

I had a similar issue, where the video had to be downloaded completely before playing.

I can see you want to stream videos, to be more specific. You have to be careful about the encoding (make sure it is streamable), don't rely on the extension only, because the person who created the file could have build the video in a wierd way, but 99% of the time you should be good. I use mediainfo. In your case should be H.264.

It also depends on browser and what you use to stream (other than backend code). For my case, I used Chrome/Html5 and .webm (VP8/Ogg Vorbis). It is working for files over 1G. Didn't test for bigger than 4G...

The code I use for download of the video:

    public void Video(string folder, string name) {
        string filepath = Server.MapPath(String.Format("{0}{1}", HttpUtility.UrlDecode(folder), name));
        string filename = name;

        System.IO.Stream iStream = null;
        byte[] buffer = new Byte[4096];
        int length;
        long dataToRead;

        try {
            // Open the file.
            iStream = new System.IO.FileStream(filepath, System.IO.FileMode.Open,
                        System.IO.FileAccess.Read, System.IO.FileShare.Read);


            // Total bytes to read:
            dataToRead = iStream.Length;

            Response.AddHeader("Accept-Ranges", "bytes");
            Response.ContentType = MimeType.GetMIMEType(name);

            int startbyte = 0;

            if (!String.IsNullOrEmpty(Request.Headers["Range"])) {
                string[] range = Request.Headers["Range"].Split(new char[] { '=', '-' });
                startbyte = Int32.Parse(range[1]);
                iStream.Seek(startbyte, SeekOrigin.Begin);

                Response.StatusCode = 206;
                Response.AddHeader("Content-Range", String.Format(" bytes {0}-{1}/{2}", startbyte, dataToRead - 1, dataToRead));
            }

            while (dataToRead > 0) {
                // Verify that the client is connected.
                if (Response.IsClientConnected) {
                    // Read the data in buffer.
                    length = iStream.Read(buffer, 0, buffer.Length);

                    // Write the data to the current output stream.
                    Response.OutputStream.Write(buffer, 0, buffer.Length);
                    // Flush the data to the HTML output.
                    Response.Flush();

                    buffer = new Byte[buffer.Length];
                    dataToRead = dataToRead - buffer.Length;
                } else {
                    //prevent infinite loop if user disconnects
                    dataToRead = -1;
                }
            }
        } catch (Exception ex) {
            // Trap the error, if any.
            Response.Write("Error : " + ex.Message);
        } finally {
            if (iStream != null) {
                //Close the file.
                iStream.Close();
            }
            Response.Close();
        }
    }

Make sure your response header contains everything you need.