I'm attempting to serve video files from ASP.NET MVC to iPhone clients. The video is formatted properly, and if I have it in a publicly accessible web directory it works fine.
The core issue from what I've read is that the iPhone requires you to have a resume-ready download environment that lets you filter your byte ranges through HTTP headers. I assume this is so that users can skip forward through videos.
When serving files with MVC, these headers do not exist. I've tried to emulate it, but with no luck. We have IIS6 here and I'm unable to do many header manipulations at all. ASP.NET will complain at me saying "This operation requires IIS integrated pipeline mode."
Upgrading isn't an option, and I'm not allowed to move the files to a public web share. I feel limited by our environment but I'm looking for solutions nonetheless.
Here is some sample code of what I'm trying to do in short...
public ActionResult Mobile(string guid = "x")
{
guid = Path.GetFileNameWithoutExtension(guid);
apMedia media = DB.apMedia_GetMediaByFilename(guid);
string mediaPath = Path.Combine(Transcode.Swap_MobileDirectory, guid + ".m4v");
if (!Directory.Exists(Transcode.Swap_MobileDirectory)) //Make sure it's there...
Directory.CreateDirectory(Transcode.Swap_MobileDirectory);
if(System.IO.File.Exists(mediaPath))
return base.File(mediaPath, "video/x-m4v");
return Redirect("~/Error/404");
}
I know that I need to do something like this, however I'm unable to do it in .NET MVC. http://dotnetslackers.com/articles/aspnet/Range-Specific-Requests-in-ASP-NET.aspx
Here is an example of an HTTP response header that works:
Date Mon, 08 Nov 2010 17:02:38 GMT
Server Apache
Last-Modified Mon, 08 Nov 2010 17:02:13 GMT
Etag "14e78b2-295eff-4cd82d15"
Accept-Ranges bytes
Content-Length 2711295
Content-Range bytes 0-2711294/2711295
Keep-Alive timeout=15, max=100
Connection Keep-Alive
Content-Type text/plain
And here is an example of one that doesn't (this is from .NET)
Server ASP.NET Development Server/10.0.0.0
Date Mon, 08 Nov 2010 18:26:17 GMT
X-AspNet-Version 4.0.30319
X-AspNetMvc-Version 2.0
Content-Range bytes 0-2711294/2711295
Cache-Control private
Content-Type video/x-m4v
Content-Length 2711295
Connection Close
Any ideas? Thank you.
I tried looking for an existing extension but I didn't immediately find one (maybe my search-fu is weak.)
My immediate thought is that you'll need to make two new classes.
First, create a class inheriting from
ActionMethodSelectorAttribute
. This is the same base class forHttpGet
,HttpPost
, etc. In this class you'll overrideIsValidForRequest
. In that method, examine the headers to see if a range was requested. You can now use this attribute to decorate a method in your controller which will get called when someone is requested part of a stream (iOS, Silverlight, etc.)Second, create a class inheriting from either
ActionResult
or maybeFileResult
and override theExecuteResult
method to add the headers you identified for the byte range that you'll be returning. Return it like you would a JSON object with parameters for the byte range start, end, total size so it can generate the response headers correctly.Take a look at the way
FileContentResult
is implemented to see how you access the context'sHttpResponse
object to alter the headers.Take a look at
HttpGet
to see how it implements the check forIsValidForRequest
. The source is available on CodePlex or you can use Reflector like I just did.You might use this info to do a little more searching and see if anyone has already created this custom
ActionResult
already.For reference, here is what the AcceptVerbs attribute looks like:
And here is what FileResult looks like. Notice the use of AddHeader:
I just pieced this together. I don't know if it will suit your needs (or works).
Use it like this:
UPDATE: This is now a project on CodePlex.
Okay, I got it working on my local testing station and I can stream videos to my iPad. It's a bit dirty because it was a little more difficult than I expected and now that it's working I don't have the time to clean it up at the moment. Key parts:
Action Filter:
Custom Result based on FileStreamResult:
My MVC action:
I really hope this helps. I spent a LOT of time on this! One thing you might want to try is removing pieces until it breaks again. It would be nice to see if the ETag stuff, modified date, etc. could be removed. I just don't have the time at the moment.
Happy coding!
Can you move outside of MVC? This is a case where the system abstractions are shooting you in the foot, but a plain jane IHttpHandler should have alot more options.
All that said, before you implement your own streaming server, you are probably better off buying or renting one . . .
The header that work have the Content-type set to text/plain, is that correct or is a typo?. Anyone, you can try to set this headers on the Action with: