4

我一直在为提供视频而编写的一段代码遇到了一些问题。代码如下:

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 视频的直接链接,然后 iPad 立即开始播放。

为了使我的方法与iDevices兼容,我是否需要做一些我错过的特别事情?对此的任何帮助将不胜感激。

4

3 回答 3

4

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

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

您可以找到几种 ASP.NET 处理程序的实现,它们可以处理 Internet 中的部分请求。我建议为此使用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>

If 还可以通过创建 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 关于部分请求详细信息的文章并使用其作者编写的处理程序:它对我有用,但它没有缓存功能。

于 2012-08-25T23:56:35.720 回答
3

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

于 2012-08-29T21:14:54.083 回答
3

@whyleee 是正确的。我不能说 StaticFileHandler 有多好,但我自己也遇到过同样的问题,这让我发疯。标题必须Range包含在RequestResponse标题中才能正常工作。例如,使用我自己的处理程序中的一些代码对您的代码进行轻微修改,如下所示(请记住,这是使用 .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 处理程序的代码,所以我不确定它在多大程度上适用于您的情况,但我希望它对您有所帮助!

于 2012-08-26T07:26:57.743 回答