.net MVC将MP4流式传输到iDevice问题

时间:2012-08-25 20:08:24

标签: asp.net .net ios asp.net-mvc html5

我在处理视频时遇到的一段代码遇到了一些问题。代码如下:

public ResumingFileStreamResult GetMP4Video(string videoID)
    {
        if (User.Identity.IsAuthenticated)
        {
            string clipLocation = string.Format("{0}\\Completed\\{1}.mp4", ConfigurationManager.AppSettings["VideoLocation"].ToString(), videoID);

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

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

            return fsr;
        }
        else
        {
            return null;
        }
    }

这是我的HTML代码:

<video controls preload poster="@Url.Content(string.Format("~/Videos/{0}_2.jpg", Model.VideoID))">
    <source src="@Url.Action("GetMP4Video", "Video", new { videoID = Model.VideoID })" type="video/mp4" />
    <source src="@Url.Action("GetWebMVideo", "Video", new { videoID = Model.VideoID })" type="video/webm" />

        <object id="flowplayer" data="@Url.Content("~/Scripts/FlowPlayer/flowplayer-3.2.14.swf")" type="application/x-shockwave-flash" width="640" height="360">
            <param name="movie" value="@Url.Content("~/Scripts/FlowPlayer/flowplayer-3.2.14.swf")" />
            <param name="allowfullscreen" value="true" />
            <param name="flashvars" value="config={'playlist':['@Url.Content(string.Format("~/Videos/{0}_2.jpg", Model.VideoID))',{'url':'@Url.Action("GetMP4Video", "Video", new { videoID = Model.VideoID })','autoPlay':false}]}" />
        </object>
</video>

我的问题是这个设置似乎在我的桌面上的所有浏览器中都能正常工作但是当我尝试使用我的iPad或iPhone加载页面时,它只显示播放图标,其中有一条线表示它无法播放。我尝试将mp4视频的源代码更改为mp4视频的直接链接,然后立即开始播放它。

我是否需要做一些我需要做的特别的事情,因为命令我的方法与iDevices兼容?任何有关这方面的帮助将不胜感激。

3 个答案:

答案 0 :(得分:4)

要在iOS设备上播放视频,您需要实现对字节范围(或部分)请求的支持。这些类型的请求不允许下载整个内容,而是部分地按块(典型的流式传输)下载。这是iOS设备在页面上获取和播放视频的唯一方式。

部分请求使用Range标头告诉服务器下一个块位置和大小。另一方的服务器响应206 Partial Content并请求块内容。

您可以找到几个可以处理Internet中部分请求的ASP.NET处理程序的实现。我建议使用 StaticFileHandler :易于安装,并且还具有开箱即用的缓存功能。它也可以通过Nuget传递,但包名为Talifun.Web

要配置StaticFileHandler,请在web.config中为 mp4 文件注册处理程序,并在单独的配置部分中对其进行配置:

<configuration>
  <configSections>
    <section name="StaticFileHandler" type="Talifun.Web.StaticFile.Config.StaticFileHandlerSection, Talifun.Web" requirePermission="false" allowDefinition="MachineToApplication"/>
  </configSections>

  <StaticFileHandler webServerType="NotSet">
    <!-- The defaults to use when an extension is found that does not have a specific rule  -->
    <fileExtensionDefault name="Default" serveFromMemory="true" maxMemorySize="100000" compress="true"/>
    <!-- Specific rules for extension types -->
    <fileExtensions>
        <fileExtension name="VideoStaticContent" extension="3gp, 3g2, asf, avi, dv, flv, mov, mp4, mpg, mpeg, wmv" serveFromMemory="true" maxMemorySize="100000" compress="false"/>
    </fileExtensions>
  </StaticFileHandler>

  <system.webServer>
    <handlers>
      <add name="StaticContentHandler" verb="GET,HEAD" path="*.mp4" type="Talifun.Web.StaticFile.StaticFileHandler, Talifun.Web"/>
    </handlers>
  </system.webServer>
</configuration>

如果还可以轻松应用自定义逻辑,例如授权或自定义视频文件源,则可以创建ASP.NET处理程序并直接调用StaticFileManager

public class MyOwnVideoHandler : IHttpHandler
{
    public void ProcessRequest(HttpContext context)
    {
        // Authorization or any other stuff.
        ...

        // Get file from your storage.
        FileInfo file = ...;

        // Serve the file with StaticFileHandler.
        StaticFileManager.Instance.ProcessRequest(new HttpContextWrapper(context), file);
    }
}

此外,您可以查看Scott Mitchell's article about partial requests以获取详细信息,并使用其作者编写的处理程序:它对我有用,但它没有缓存功能。

答案 1 :(得分:3)

@whyleee是正确的。我不能谈论StaticFileHandler有多好,但我自己也面临同样的问题,这让我发疯。 RangeRequest标头中必须包含Response标头,才能正常使用。例如,使用我自己的Handler中的一些代码对您的代码稍作修改,看起来像这样(请记住,这是使用.ashx处理程序):

//First, accept Range headers.
context.Response.AddHeader("Accept-Ranges", "bytes")

//Then, read all of the bytes from the file you are requesting.
Dim file_info As New System.IO.FileInfo(clipLocation)
Dim bytearr As Byte() = File.ReadAllBytes(file_info.FullName)

//Then, you will need to check for a range header, and then serve up a 206 Partial Content status code in your response.
Dim startbyte As Integer = 0
If Not context.Request.Headers("Range") Is Nothing Then
    //Get the actual byte range from the range header string, and set the starting byte.
    Dim range As String() = context.Request.Headers("Range").Split(New Char() {"="c, "-"c})
    startbyte = Convert.ToInt64(range(1))

    //Set the status code of the response to 206 (Partial Content) and add a content range header.
    context.Response.StatusCode = 206
    context.Response.AddHeader("Content-Range", String.Format(" bytes {0}-{1}/{2}", startbyte, bytearr.Length - 1, bytearr.Length))
End If

//Finally, write the video file to the output stream, starting from the specified byte position.
context.Response.OutputStream.Write(bytearr, startbyte, bytearr.Length - startbyte)

正如我所说,这是.ashx处理程序的代码,所以我不确定它适用于你的情况,但我希望它可以帮助你!

答案 2 :(得分:3)

感谢您的回复,所提供的信息非常有用。最后,我使用以下solution来实现字节范围请求。