更新:这现在是CodePlex 上的项目 http://mediastreamingmvc.codeplex.com/.
好的,我已经在本地测试站上运行了,并且可以将视频传输到我的 iPad 上。它有点脏,因为它比我预期的要困难一些,而且现在它正在工作,我现在没有时间清理它。关键部分:
动作过滤器:
public class ByteRangeRequest : FilterAttribute, IActionFilter
{
protected string RangeStart { get; set; }
protected string RangeEnd { get; set; }
public ByteRangeRequest(string RangeStartParameter, string RangeEndParameter)
{
RangeStart = RangeStartParameter;
RangeEnd = RangeEndParameter;
}
public void OnActionExecuting(ActionExecutingContext filterContext)
{
if (filterContext == null)
throw new ArgumentNullException("filterContext");
if (!filterContext.ActionParameters.ContainsKey(RangeStart))
filterContext.ActionParameters.Add(RangeStart, null);
if (!filterContext.ActionParameters.ContainsKey(RangeEnd))
filterContext.ActionParameters.Add(RangeEnd, null);
var headerKeys = filterContext.RequestContext.HttpContext.Request.Headers.AllKeys.Where(key => key.Equals("Range", StringComparison.InvariantCultureIgnoreCase));
Regex rangeParser = new Regex(@"(\d+)-(\d+)", RegexOptions.Compiled);
foreach(string headerKey in headerKeys)
{
string value = filterContext.RequestContext.HttpContext.Request.Headers[headerKey];
if (!string.IsNullOrEmpty(value))
{
if (rangeParser.IsMatch(value))
{
Match match = rangeParser.Match(value);
filterContext.ActionParameters[RangeStart] = int.Parse(match.Groups[1].ToString());
filterContext.ActionParameters[RangeEnd] = int.Parse(match.Groups[2].ToString());
break;
}
}
}
}
public void OnActionExecuted(ActionExecutedContext filterContext)
{
}
}
基于FileStreamResult的自定义结果:
public class ContentRangeResult : FileStreamResult
{
public int StartIndex { get; set; }
public int EndIndex { get; set; }
public long TotalSize { get; set; }
public DateTime LastModified { get; set; }
public FileStreamResult(int startIndex, int endIndex, long totalSize, DateTime lastModified, string contentType, Stream fileStream)
: base(fileStream, contentType)
{
StartIndex = startIndex;
EndIndex = endIndex;
TotalSize = totalSize;
LastModified = lastModified;
}
public override void ExecuteResult(ControllerContext context)
{
if (context == null)
throw new ArgumentNullException("context");
HttpResponseBase response = context.HttpContext.Response;
response.ContentType = this.ContentType;
response.AddHeader(HttpWorkerRequest.GetKnownResponseHeaderName(HttpWorkerRequest.HeaderContentRange), string.Format("bytes {0}-{1}/{2}", StartIndex, EndIndex, TotalSize));
response.StatusCode = 206;
WriteFile(response);
}
protected override void WriteFile(HttpResponseBase response)
{
Stream outputStream = response.OutputStream;
using (this.FileStream)
{
byte[] buffer = new byte[0x1000];
int totalToSend = EndIndex - StartIndex;
int bytesRemaining = totalToSend;
int count = 0;
FileStream.Seek(StartIndex, SeekOrigin.Begin);
while (bytesRemaining > 0)
{
if (bytesRemaining <= buffer.Length)
count = FileStream.Read(buffer, 0, bytesRemaining);
else
count = FileStream.Read(buffer, 0, buffer.Length);
outputStream.Write(buffer, 0, count);
bytesRemaining -= count;
}
}
}
}
我的 MVC 操作:
[ByteRangeRequest("StartByte", "EndByte")]
public FileStreamResult NextSegment(int? StartByte, int? EndByte)
{
FileStream contentFileStream = System.IO.File.OpenRead(@"C:\temp\Gets.mp4");
var time = System.IO.File.GetLastWriteTime(@"C:\temp\Gets.mp4");
if (StartByte.HasValue && EndByte.HasValue)
return new ContentRangeResult(StartByte.Value, EndByte.Value, contentFileStream.Length, time, "video/x-m4v", contentFileStream);
return new ContentRangeResult(0, (int)contentFileStream.Length, contentFileStream.Length, time, "video/x-m4v", contentFileStream);
}
我真的希望这有帮助。我在这上面花了很多时间!您可能想要尝试的一件事是移除碎片,直到它再次破裂。很高兴看到 ETag 内容、修改日期等是否可以被删除。我只是现在没有时间。
快乐编码!