Some sample of usage

Aug 11, 2011 at 5:49 PM
Edited Aug 11, 2011 at 6:34 PM

Hello,

Just found your project. I am trying to integrate html 5 video to asp.net mvc application.

Video and pictures are returned by handler like action functionality (FileContentResult  with data from server file - video or picture).

When I show images that point to handler action there are no problems. For video mostly no problems except chrome. Long debugging shown that this is due of chrome range requests (IE uses full request). So I am thinking about usage of your library. (not sure for pictures because they works correctly). I have some questions:

1) Could you please provide some sample code  how to add it to action?

2) Have you compared your solution with  something like http://dotnetslackers.com/articles/aspnet/Range-Specific-Requests-in-ASP-NET.aspx? Because I think general ideas could be the same but it is for web forms. I have also found similar asp.net mvc solution

http://code.google.com/p/talifun-web/. What do you think?

3) On which stage project is now? Could I use it in production or there are too many bugs?

Apr 20, 2012 at 5:50 PM

Hi yauhen,

First I want to apologize for the slow response. For some reason no alerts activated to let me know of available content! I had become bored checking the project each day to see nothing new and figured I would receive an alert if/when someone needed attention. I was clearly mistaken on this!

I will try to answer you questions now just in case you or someone else is still interested.

1) There is currently no documentation. The project should work just like the other File action results in the ASP.NET MVC framework. (At the time this was written, version 2 was what I used as a model.) For the most part you should be able to use them exactly the same but under the covers have the byte range requests handled for you.

2) This link looks like one of the many I consulted when I made the project. The biggest difference between the two is the use of an HTTP Module versus a routed URL to a resource. The method shown in the link would require a resource (video, music, etc.) to be directly requestable. This means its location needs to be static and there can be no real custom logic in between the request and response. The HTTP Module then takes care of parsing the range request and responding with the bytes. The approach I took is essentially the same but is in a higher level of logic. Making actions for MVC allows for more dynamic routing to resources. For instance, the file doesn't have to be available by name directly. It could be an ID referring to a database row. It could be a replication ID. It could be a virtual path. Same result, different experience for the developer.

3) I've left the project marked Beta for a very long time because there wasn't enough usage by people to figure out if there were bugs or performance issues that made it unfit for production use. I still feel this is true today (take note anonymous users: it's very helpful to drop a quick note saying "I use it - works great, no issues" or even better "I'm using feature xyz and it's working fine for some time!"). I can say I have not seen any issue with it but I also have no need for this myself. I created this as a successor to a project created as an answer to a Stack Overflow question. I have no direct observation of it being used and as such I can't really say "this is bullet-proof code!" I do think it's perfectly usable on production though!

Apr 20, 2012 at 6:25 PM

I want to also mention that most browsers (except the last round of IE I tested) will issue range requests if your method advertises this capability. Since you're not already using the methods for streaming results, this capability isn't advertised so most browsers will request the full resource. Why Chrome behaves differently I'm not entirely sure. One notable exception to this is Safari on iOS devices (iPhone, iPad, etc.) which will not attempt to play media over a certain (very small) size if it does not respond properly to range requests. This project was specifically created to support that scenario and as a happy side-effect also got (with some tweaking) support for standard browsers, Windows Media Player, etc.

The overall experience is better for end uses since most major products support this standard but each might do it in a slightly different way which can also change with later versions. I tested what was available at the time but browsers have gone up a few versions and in the case of Chrome I think a dozen versions have come and gone!

Jul 3, 2012 at 12:08 AM

First let me say that I am new to ASP.NET  C#.

Second, let me say that I realize you posted this a year ago. But maybe this will help the next guy.

1) If you don't have NuGet installed in your IDE, install it.

2) Install the package from the Package Manage Console

PM> Install-Package MVC.ResumingActionResults

3) In your controller, add this using clause

using VikingErik.Mvc.ResumingActionResults;

4) Then define your action method.

Declare it as one of the types provided by ResumingActionResults. I don't know how to use all of the types provided, but in my case In my case, I wanted to stream video, so I chose "ResumingFileStreamResult"  

Here is my test action, located in my own "StreamController.cs" file. I named it "Viking" in honor of Erik

public ResumingFileStreamResult Viking (string file)

        {

           moviename = Server.MapPath("~/Content/samples/" + file);

           FileStream fs = new FileStream(moviename, FileMode.Open, FileAccess.Read);

           ResumingFileStreamResult fsr = new ResumingFileStreamResult(fs,"video/mp4");

           return fsr;

         }

 

Following is what I put in my view file:

<script src="http://api.html5media.info/1.1.5/html5media.min.js"></script>

 <video controls preload>

        <source src="/Stream/Viking?file=Chrome_ImF.mp4" />

 </video>

 

Voila, it worked like a charm.

Dec 13, 2012 at 5:42 PM

I'm trying to get this working against an IPad, I would like to be able to stream a wav file.  However, i've not having much success.  In IE the code below will open media player and play the file just fine, however in Chrome and Safari, i get a black screen with a speaker that is crossed out.  Any help would be appreciated.

My Action is quite simply 

        public ResumingFileStreamResult ApplePlayback(string filename) {

return new ResumingFileStreamResult(fileHandler.GetMemoryStreamOfFile(1),"audio/x-wav");       

}

and in my view I just have an anchor.  

Download

Is there anything else i need to do?

Mar 8, 2014 at 10:19 AM
Edited Mar 8, 2014 at 10:56 AM
@tolerance72: great example works like a charm.... if your files are small enough.

@VikingErik: Using this library would solve my problems if the most common c# file read problem was solved:
System.OutOfMemoryException when serving large files.

the following funtion does that just fine, except for the lack of resume support.
        private void TransmitFile(string fullPath, string outFileName, string contentType)
        {
            System.IO.Stream iStream = null;
            byte[] buffer = new Byte[10000]; // Buffer to read 10K bytes in chunk:
            int length; // Length of the file:
            long dataToRead; // Total bytes to read:
            string filepath = fullPath; // Identify the file to download including its path.
            string filename = System.IO.Path.GetFileName(filepath); // Identify the file name.
            try
            {
                    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.Clear();
                    Response.ContentType = contentType;// "application/octet-stream";
                    Response.AddHeader("Content-Disposition", "attachment; filename=" + outFileName);
                    Response.AddHeader("Content-Length", iStream.Length.ToString());
                    // Read the bytes.
                    while (dataToRead > 0)
                    {
                        // Verify that the client is connected.
                        if (Response.IsClientConnected)
                        {
                            // Read the data in buffer.
                            length = iStream.Read(buffer, 0, 10000);

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

                            // Flush the data to the output.
                            Response.Flush();

                            buffer = new Byte[10000];
                            dataToRead = dataToRead - length;
                        }
                        else
                        {
                            //prevent infinite loop if user disconnects
                            dataToRead = -1;
                        }
                    }
                
            }
            catch (Exception ex)
            {
                throw new ApplicationException(ex.Message); 
            }
            finally
            {
                if (iStream != null)
                    iStream.Close();
                Response.Close();
            }
        }
Is there a way to modify your project to only read chucks of data instead of the enite file at once?
Jun 2, 2014 at 9:40 AM
The ResumingFileStreamResult works like a charm for me too. I tried it on a 6MB mp4 file but I have noticed that it calls Viking method 4 times (and this is only ever called once in the code). If I try to force it to come out after the 1st call, it doesn't stream properly. Only that the method is called 4 times, it is streamed properly. Is this correct?
Jun 2, 2014 at 4:14 PM
It depends on what client you're using. If you capture the network traffic you'll see why this is happening.

For iOS devices (at least iOS 5 I believe back when this was first written), the media application starts with a Byte-Range request for Bytes 0-1 or something like that. It then checks the response to see that the response headers are correct (partial response) and the length of data returned matches the request. This is acting like a handshake to see if the resource supports ranged requests. If it doesn't, iOS will show the play icon with a line through it signifying your device can't stream.

I want to say it would do a second call to for Bytes 1-2 to make sure the response will not force a "start from beginning" of the stream. Meaning that the resource can jump anywhere into the stream. I'm not 100% certain if I'm remembering this correctly or if I'm mixing up behavior from Chrome or a fever dream.

If iOS determines streaming is possible, another request is made for a larger chunk. This is where the playback actually begins. Every time the device requests another chunk, the method will get called again. For browsers this is unlikely to happen unless you specifically skip around in the stream because computers have enough memory to just store the whole response as it comes in.

If you see the method getting called more times than the traffic capture suggests it should be called, then maybe there's been a change to MVC that is causing this duplication to happen. This library was written against MVC 2 and has purposefully been left to be as low compatible as possible. That might have introduced a problem with later versions. Or it could be a bug somewhere and I call the method twice somehow.

If you can show it's doubling the calls, I can try to take a look at the call chain and see where this might be happening.