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
表达式。
在过去的几周里,我一直在研究 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
表达式。