ASP.NET 正在尝试将流式视频源加载到 HTML5

ASP.NET attempting to load streamed video source into HTML5

在过去的几周里,我一直在研究 ASP.NET WebAPI,它旨在从公司服务器之一流式传输视频并在 HTML5 <video>元素。在 a guide on C# Corner 之后,我们发布了 API,现在当我们的一个视频的 link 被粘贴到浏览器时,它开始下载(顺便说一句,我当我们尝试做的只是流式传输时,不确定它是否应该这样做。

我们需要流式传输的文件是 mp4 并且将主要通过 Safari 在 iOS 设备上使用。在任何人问之前:srcVid 被编程并确认能够成功编码 .mp4 文件,因为我们已经成功地将视频硬编码到这个元素中没有问题。话虽如此,这就是页面处理其 HTML5 元素的方式:

<video autoplay muted id="trainVid" style="width: 75%; height: auto;" controls>
   <source id="srcVid" runat="server"
     type='video/mp4; codecs*="avc1.424085, mp4a.40.2"' />
</video>

在 API 方面,这里是视频的处理方式,主要遵循 C# 文章设置的示例,以及来自 Stephen Cleary article:

的一些帮助
public class VidService
{
    public async void WriteVidBytes(Stream outputStream,
      HttpContent content, TransportContext tc)
    {
        try
        {
            var filePath = "\\server\link\to\file.mp4";
            int bufferSize = 1000;
            byte[] buffer = new byte[bufferSize];

            using (var fileStream = new FileStream(
              filePath, FileMode.Open, FileAccess.Read, FileShare.Read))
            {
                int fileSize = (int)fileStream.Length;
                while (fileSize > 0)
                {
                    int cnt = fileSize > bufferSize ? bufferSize : fileSize,
                        readBufferSize = fileStream.Read(buffer, 0, cnt);
                    await outputStream.WriteAsync(buffer, 0, readBufferSize);

                    fileSize -= readBufferSize;
                }
            }
        }
        catch (HttpException ex) { return; }
        finally { outputStream.Close(); }
    }

    public HttpResponseMessage GetVidContent()
    {
        // NOTE: please see the Edit on 6/10
        var httpResponse = new HttpResponseMessage(HttpStatusCode.OK)
        {
            Content = new PushStreamContent(
              (Action<Stream, HttpContent, TransportContext>)WriteVidBytes
            )
        };
        return httpResponse;
    }
}

public class VidController : ApiController
{
    private static readonly VidService vs = new VidService();

    [HttpGet]
    public HttpResponseMessage GetVid(int id)
    {
        return vs.GetVidContent(id);
    }
}

*注意实际程序通过一个Video.cs对象

动态获取视频links

最后 在 C# 端:

protected void LoadVideo(int vidId)
{
    HttpWebRequest req = (HttpWebRequest)WebRequest.Create(
        string.Format("http://mobileAPI.website.com/Vid/GetVid/" + vidId.ToString()));
    req.Method = "GET";
    HttpWebResponse resp = (HttpWebResponse)req.GetResponse();
    string jsonString;
    using (Stream stream = resp.GetResponseStream())
    {
        StreamReader r = new StreamReader(stream, System.Text.Encoding.UTF8);
        jsonString = r.ReadToEnd();
    }
    srcVid.Src = jsonString;
}

打开页面时,LoadVideo() 似乎没有错误执行 -- 但在此之后,页面变为空白并永远挂起。我想这可能是因为我在 srcVid.Src 中输入了错误的值,但是如果我不输入 jsonString,那么我输入的 do来源?

一如既往,我们将不胜感激!如果我遗漏了任何明显的内容,请告诉我,因为这是我第一次使用 WebAPI.

更新 1 (6/10)

我制作了一个辅助方法,将 WriteVidBytes 转换为 Task -- 除了将其转换为任务外,内部代码完全相同。另一个区别是 GetVidContent 获取数据的方式:

public HttpResponseMessage GetVidContent(int vId)
{
    var httpResponse = new HttpResponseMessage(HttpStatusCode.OK)
    {
        Content = new PushStreamContent(async
          (outputStream, httpContext, transportContext) =>
        {
            await WriteVidTask(outputStream, httpContext, transportContext);
        }),
    };
    return httpResponse;
}

但是,即使通过 Postman 或 Fiddler 获取文件没有问题,页面仍然挂起。

an example from Robert Huang on CodeProject 之后,我能够毫无问题地通过 API 播放视频。

我改变的第一件事是 <video> 读取源代码的方式。视频加载的不是 JSON 字符串,而是 API link.

srcVid.Src = "http://api.website.com/Vid/GetVid?id=" + vidId.ToString();

根据 CodeProject link,我创建了一个 HttpResponseMessage,与提供的非常相似——只有这个支持异步加载:

public HttpResponseMessage GetVidContent(RangeHeaderValue rh, FileInfo fi)
{
    HttpResponseMessage response = new HttpResponseMessage();
    response.Headers.AcceptRanges.Add("bytes");
    long totalLength = fi.Length;


    if (rh == null || !rh.Ranges.Any())
    {   // treat request normally if there is no range header
        response.StatusCode = HttpStatusCode.OK;
        response.Content = new PushStreamContent(async (outputStream, 
            httpContent, transpContext) =>
        {
            using (outputStream) // copy file to output stream straightforward
            using (Stream inputStream = fi.OpenRead())
            {
                try
                {
                    await inputStream.CopyToAsync(outputStream, ReadStreamBufferSize);
                }
                catch (Exception ex)
                {
                    Debug.WriteLine(ex);
                }
            }
        }, GetMimeNameFromExt(fi.Extension));
        return response;
    }

    long start = 0, end = 0;

    if (rh.Unit != "bytes" || rh.Ranges.Count > 1 || TryReadRangeItem(rh.Ranges.First(),
        totalLength, out start, out end))
    {
        response.StatusCode = HttpStatusCode.RequestedRangeNotSatisfiable;
        response.Content = new StreamContent(Stream.Null);
        response.Content.Headers.ContentRange = new ContentRangeHeaderValue(totalLength);
        response.Content.Headers.ContentType = GetMimeNameFromExt(fi.Extension);

        return response;
    }

    var contentRange = new ContentRangeHeaderValue(start, end, totalLength);

    response.StatusCode = HttpStatusCode.PartialContent;
    response.Content = new PushStreamContent(async (outputStream, httpContent, transpContext) =>
    {
        using (outputStream)
        using (Stream inputStream = fi.OpenRead())
            await CreatePartialContent(inputStream, outputStream, start, end);
    }, GetMimeNameFromExt(fi.Extension));
    response.Content.Headers.ContentLength = end - start + 1;
    response.Content.Headers.ContentRange = contentRange;

    return response;
}

await 区域中使用的 CodeProject 文章中的任何方法都标有 async 表达式。