【问题标题】:ASP.NET attempting to load streamed video source into HTML5ASP.NET 尝试将流式视频源加载到 HTML5
【发布时间】:2021-08-26 19:00:24
【问题描述】:

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

我们需要流式传输的文件是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 对象动态获取视频链接

终于在 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 我为源输入了什么? p>

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

更新 1 (6/10)

我做了一个次要的方法,把WriteVidBytes变成Task——除了变成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 获取文件没有问题,页面仍然挂起。

【问题讨论】:

  • async void中删除async,并将await替换为同步的output.Write
  • @user1344783 我会试试看,但这是个好主意吗?我从未听说过有人同步流式传输视频。编辑:无论如何这会产生相同的结果
  • PushStreamContent 也有一个构造函数,它接受一个真正的异步 lambda:docs.microsoft.com/en-us/previous-versions/aspnet/…,因此您可以将您的 async void 转换为 async Task 并删除演员 (Action&lt;Stream, HttpContent, TransportContext&gt;)
  • @user1344783 我刚刚更新了问题
  • 我仍然认为你应该使用 async Task 而不是 async void ????

标签: c# asp.net asynchronous asp.net-web-api html5-video


【解决方案1】:

an example from Robert Huang on CodeProject 之后,我能够毫无问题地让视频通过 API 流式传输。

我改变的第一件事是&lt;video&gt; 读取源代码的方式。视频加载的是 API 链接,而不是 JSON 字符串。

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

按照 CodeProject 链接,我创建了一个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 表达式。

【讨论】:

    猜你喜欢
    • 2015-12-23
    • 1970-01-01
    • 2013-12-24
    • 2012-08-23
    • 1970-01-01
    • 2012-12-01
    • 1970-01-01
    • 1970-01-01
    • 2017-11-02
    相关资源
    最近更新 更多