vb.net 通过 .NET HTML5 流式传输 MP4 视频

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/16421255/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-17 13:31:12  来源:igfitidea点击:

Streaming MP4 video through .NET HTML5

.netvb.nethtmlvideo-streaminghtml5-video

提问by JLo

I am trying to create a test page which will contain a HTML5 VIDEO tag which will allow converted videos to be played. I am successfully able to convert the videos and store these locally on the server but I'd like to be able to stream all videos through another .aspx page.

我正在尝试创建一个测试页面,其中将包含一个 HTML5 VIDEO 标签,该标签将允许播放转换后的视频。我能够成功转换视频并将它们本地存储在服务器上,但我希望能够通过另一个 .aspx 页面流式传输所有视频。

Assuming I have a player.aspx page which will contain the HTML code and getvideo.aspx page which will do nothing except provide the video binary, I thought that the following code would work fine in my player.aspx page:

假设我有一个包含 HTML 代码的 player.aspx 页面和除了提供视频二进制文件什么都不做的 getvideo.aspx 页面,我认为以下代码在我的 player.aspx 页面中可以正常工作:

<div style="text-align:center">   
<video controls autoplay id="video1" width="920">  
    <source src="http://www.mywebsite.com/getvideo.aspx?getvideo=1" type="video/mp4">  
    Your browser does not support HTML5 video.  
</video>  

The getvideo.aspx page contains the following vb.net code:

getvideo.aspx 页面包含以下 vb.net 代码:

Response.clearheaders
Response.AddHeader("Content-Type", "video/mp4")
Response.AddHeader("Content-Disposition", "inline;filename=""newvideo.mp4""")

dim Err as string = ""
Dim iStream As System.IO.Stream

' Buffer to read 10K bytes in chunk:
Dim buffer(buffersize) As Byte
' Length of the file:
Dim length As Integer

' Total bytes to read:
Dim dataToRead As Long

' Identify the file to download including its path.
Dim filepath As String = "./outout/videos/newvideo.mp4"

' Identify the file name.
Dim filename As String = System.IO.Path.GetFileName(filepath)

' Open the file.
try
    iStream = New System.IO.FileStream(filepath, System.IO.FileMode.Open, IO.FileAccess.Read, IO.FileShare.Read)
catch ex as exception
    throw new exception("Could not create FileStream for [" & filepath & "], error follows." & vbcrlf & ex.toString)
end try

Try
    ' Total bytes to read:
    dataToRead = iStream.Length

    ' Read the bytes.
    While dataToRead > 0
        ' Verify that the client is connected.
        If system.web.httpcontext.current.Response.IsClientConnected Then
            ' Read the data in buffer
            length = iStream.Read(buffer, 0, buffersize)
            ' Write the data to the current output stream.
            system.web.httpcontext.current.Response.OutputStream.Write(buffer, 0, length)
           ' Flush the data to the HTML output.
           system.web.httpcontext.current.Response.Flush()

           ReDim buffer(buffersize) ' Clear the buffer
           dataToRead = dataToRead - length
       Else
           'prevent infinite loop if user disconnects
           dataToRead = -1
       End If
    End While

Catch ex As Exception
    ' Trap the error, if any.
    err =  "Error accessing " & filepath & " : " & ex.tostring
Finally
    If IsNothing(iStream) = False Then
        ' Close the file.
        iStream.Close()
    End If
End Try

if err<>"" then throw new exception( err )

All I get on my page output is a HTML video player (chrome's basic player) which seems to time out and the PLAY button goes grey. The Network tool in the Chrome Developer tools shows that it's downloading 45mb and gets a 200 response code. This suggests to me that it is working fine. Although I get a second GET request with the status of "Cancelled"?

我在页面输出上得到的只是一个 HTML 视频播放器(chrome 的基本播放器),它似乎超时并且 PLAY 按钮变灰。Chrome 开发人员工具中的网络工具显示它正在下载 45mb 并获得 200 响应代码。这向我表明它运行良好。虽然我收到了状态为“已取消”的第二个 GET 请求?

If I visit www.mywebsite.com/output/videos/myvideo.mp4 then this plays in the browser fine so I know IIS is configured to correctly stream video.

如果我访问 www.mywebsite.com/output/videos/myvideo.mp4,那么它可以在浏览器中正常播放,因此我知道 IIS 已配置为正确流式传输视频。

Also if I change the response content disposition to "attachment" then the browser correctly forces a download of the video when going to my ASPX page, but this also doesn't correctly play on the HTML player. Is there something 'clever' going on with the HTML5 VIDEO tag which is stopping a .aspx file from serving up video via .net? Or am I missing a response header?

此外,如果我将响应内容配置更改为“附件”,则浏览器会在转到我的 ASPX 页面时正确强制下载视频,但这也无法在 HTML 播放器上正确播放。HTML5 VIDEO 标签是否有一些“聪明”的东西会阻止 .aspx 文件通过 .net 提供视频?还是我缺少响应标头?

Thanks!

谢谢!

采纳答案by JLo

FIXED!

固定的!

The problem here is presumably something to do with the HTML5 video implementation of range-requests. Without supporting range-requests in the page being used to SERVE the video, they just won't work.

这里的问题大概与范围请求的 HTML5 视频实现有关。如果不支持用于 SERVE 视频的页面中的范围请求,它们将无法工作。

It was this very helpful post from Scott Mitchell which set me on the case: http://dotnetslackers.com/articles/aspnet/Range-Specific-Requests-in-ASP-NET.aspx

正是 Scott Mitchell 的这篇非常有帮助的帖子让我了解了这个案例:http: //dotnetslackers.com/articles/aspnet/Range-Specific-Requests-in-ASP-NET.aspx

And another helpful post from Chris Coulson which provided the implementation in C# for the solution! http://blogs.visigo.com/chriscoulson/easy-handling-of-http-range-requests-in-asp-net/

Chris Coulson 的另一篇有用的帖子提供了 C# 中的解决方案实现! http://blogs.visigo.com/chriscoulson/easy-handling-of-http-range-requests-in-asp-net/

My vb.net port of the code Chris found is below, to implement it simply put this in your page:

我的 Chris 找到的代码的 vb.net 端口在下面,要实现它,只需将它放在您的页面中:

system.web.httpcontext.current.Response.ContentType = "video/" & convertFormat.toString
RangeDownload(convertedVideo, system.web.httpcontext.current)

VB.NET CODE:

VB.NET 代码:

private sub RangeDownload( fullpath as string, context as HttpContext)

    dim size as long
    dim start as long
    dim theend as long
    dim length as long
    dim fp as long =0
    using reader as new StreamReader(fullpath)
        size = reader.BaseStream.Length
        start = 0
        theend = size - 1
        length = size
        '/ Now that we've gotten so far without errors we send the accept range header
        '* At the moment we only support single ranges.
        '* Multiple ranges requires some more work to ensure it works correctly
        '* and comply with the spesifications: http://www.w3.org/Protocols/rfc2616/rfc2616-sec19.html#sec19.2
        '*
        '* Multirange support annouces itself with:
        '* header('Accept-Ranges: bytes');
        '*
        '* Multirange content must be sent with multipart/byteranges mediatype,
        '* (mediatype = mimetype)
        '* as well as a boundry header to indicate the various chunks of data.
        '*/
        context.Response.AddHeader("Accept-Ranges", "0-" + size)
        '// header('Accept-Ranges: bytes')
        '// multipart/byteranges
        '// http://www.w3.org/Protocols/rfc2616/rfc2616-sec19.html#sec19.2

        if (not String.IsNullOrEmpty(context.Request.ServerVariables("HTTP_RANGE"))) then
            dim anotherStart as long = start
            dim anotherEnd as long = theend
            dim arr_split as string() = context.Request.ServerVariables("HTTP_RANGE").Split("=") 'new char[] { Convert.ToChar("=") })
            dim range as string = arr_split(1)

            '// Make sure the client hasn't sent us a multibyte range
            if (range.IndexOf(",") > -1) then
                '// (?) Shoud this be issued here, or should the first
                '// range be used? Or should the header be ignored and
                '// we output the whole content?
                context.Response.AddHeader("Content-Range", "bytes " & start & "-" & theend & "/" & size)
                throw new HttpException(416, "Requested Range Not Satisfiable")
            end if

            '// If the range starts with an '-' we start from the beginning
            '// If not, we forward the file pointer
            '// And make sure to get the end byte if spesified
            if (range.StartsWith("-")) then
                '// The n-number of the last bytes is requested
                anotherStart = size - Convert.ToInt64(range.Substring(1))
            else
                arr_split = range.Split("-")
                anotherStart = Convert.ToInt64(arr_split(0))
                dim temp as long = 0
                if (arr_split.Length > 1 andalso Int64.TryParse(arr_split(1).ToString(), temp)) then
                    anotherEnd =  Convert.ToInt64(arr_split(1))
                else
                    anotherEnd = size
                end if
            end if
            '/* Check the range and make sure it's treated according to the specs.
            ' * http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html
            ' */
            '// End bytes can not be larger than $end.
            if (anotherEnd > theend) then
                anotherEnd = theend
            else
                anotherEnd = anotherEnd
            end if
            '// Validate the requested range and return an error if it's not correct.
            if (anotherStart > anotherEnd or anotherStart > size - 1 or anotherEnd >= size) then
                context.Response.AddHeader("Content-Range", "bytes " + start + "-" + theend + "/" + size)
                throw new HttpException(416, "Requested Range Not Satisfiable")
            end if

            start = anotherStart
            theend = anotherEnd

            length = theend - start + 1 '// Calculate new content length
            fp = reader.BaseStream.Seek(start, SeekOrigin.Begin)
            context.Response.StatusCode = 206
        end if
    end using

    '// Notify the client the byte range we'll be outputting
    context.Response.AddHeader("Content-Range", "bytes " & start & "-" & theend & "/" & size)
    context.Response.AddHeader("Content-Length", length.ToString())
    '// Start buffered download
    context.Response.WriteFile(fullpath, fp, length)
    context.Response.End()
end sub