我正在尝试构建一个支持HLS的自定义C#网络服务器,但具体来说,HLS部分不起作用。以下是我使用的代码。我想尽可能简单地保持它。问题是除了HLS协议中使用的文件类型之外,所有文件类型都有效。我尝试过的文件类型包括mp4
,xml
,mp3
,jpg
,png
和没有扩展名的纯二进制数据文件。
public class LocalServer
{
public bool runListener = true;
private string prefix;
private string baseDirectory = "M:/ftp backup/";
public LocalServer(int port)
{
prefix = string.Format("http://+:{0}/", port);
Thread listenerThread = new Thread(LocalServerThread);
listenerThread.Start();
}
public void LocalServerThread()
{
HttpListener listener = new HttpListener();
listener.Prefixes.Add(prefix);
listener.Start();
Debug.Log(DateTime.Now.ToString(SimpleHTTPServer.dateFormat) + "<color=green>Started local web server</color>");
while (runListener)
{
IAsyncResult result = listener.BeginGetContext(new AsyncCallback(ListenerCallback), listener);
result.AsyncWaitHandle.WaitOne();
}
listener.Close();
Debug.Log(DateTime.Now.ToString(SimpleHTTPServer.dateFormat) + "<color=green>Stopped local web server</color>");
}
public void ListenerCallback(IAsyncResult result)
{
HttpListener listener = (HttpListener)result.AsyncState;
HttpListenerContext context = listener.EndGetContext(result);
string fileNameComplete = context.Request.Url.AbsolutePath.Substring(1).Replace("%20", " "); //removing the first slash and replacing the %20 web prefix by space
string pathComplete = baseDirectory + fileNameComplete;
Debug.Log(DateTime.Now.ToString(SimpleHTTPServer.dateFormat) + pathComplete + " requested to LocalServer");
using (Stream fileStream = new FileStream(pathComplete, FileMode.Open))
{
byte[] buffer = new byte[fileStream.Length];
fileStream.Read(buffer, 0, (int)fileStream.Length);
context.Response.ContentLength64 = fileStream.Length;
context.Response.ContentType = SimpleHTTPServer.GetMimeType(fileNameComplete);
//personal headers
context.Response.StatusCode = (int)HttpStatusCode.OK;
////apache headers
//context.Response.StatusDescription = "OK";
//context.Response.ProtocolVersion = new Version("1.1");
//context.Response.SendChunked = false;
//context.Response.KeepAlive = true;
//context.Response.AddHeader("Accept-Ranges", "bytes");
//context.Response.AddHeader("ETag", BinarySocket.RandomString(20));
//context.Response.AddHeader("Connection", "Keep-Alive");
//context.Response.AddHeader("Keep-Alive", "timeout=5, max=100");
////CORS
//context.Response.AddHeader("Access-Control-Allow-Origin", "*");
//context.Response.AddHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
//context.Response.AddHeader("Access-Control-Allow-Headers", "X-Requested-With");
//context.Response.AddHeader("Access-Control-Max-Age", "86400");
context.Response.OutputStream.Write(buffer, 0, buffer.Length);
context.Response.Close();
}
}
}
SimpleHTTPServer是我之前尝试过的类。此类使用SimpleHTTPServer类的mime类型字典。它还使用SimpleHTTPServer中的dateFormat:"dd-MM-yyyy HH:mm:ss.fff:"
。用于HLS的mime类型是:".m3u8", "application/vnd.apple.mpegurl"
和".ts", "video/MP2T"
,它们与apache使用的相同。我评论了Apache和CORS响应属性,因为它对下载没有影响。
某些媒体播放器(媒体基金会,直接展示)不会打开来自我的HTTP服务器的HLS流。在VLC中打开它会导致VLC非常缓慢地下载每个m3u8播放列表(主播放列表)和子m3u8播放列表(不同的分辨率/带宽),并且只有在完成下载所有播放列表之后,它才开始播放4k视频片段。下一个片段的分辨率为1440p,它一直向下,直到达到360p。但是,通过来自apache或Nginx的流播放完全相同的文件可以很好地工作。
有人知道我做错了什么或者以错误的方式实施以使HLS工作吗?我已经阅读了HTTP Live Streaming RFC(RFC-8216),我认为我正在做一切使HLS工作所需的权利,但事实并非如此,所以我一定做错了。 Apple文档描述了任何HTTP服务器只需添加mime类型即可支持HLS,但这样做并不能解决问题。
编辑:从visual studio控制台应用程序运行此代码时,它可以工作,但在Unity中运行此代码时,Unity将一直阻止,直到下载完成。看起来像一个线程问题,但它都运行在一个单独的线程上吗?或者是在主线程上执行了监听器回调?编辑2:监听器在一个单独的线程上执行,因此在下载文件时会阻塞主线程。显然无论下载什么,线程阻塞,其他一切都只是被下载太快而无法注意阻塞。 HLS实际上是下载主播放列表,然后是6个不同的播放列表,然后是彼此之后的所有15个视频片段。可能是因为我在线程中使用result.AsyncWaitHandle.WaitOne();
,这使得无法进行多个并行下载。我将其替换为Thread.Sleep(10);
,因此我可以进行并行下载。它无法解决阻塞问题。
编辑3:阻止似乎发生,因为程序同时上传和下载。代码的两面(下载/上传)在单独的程序中运行良好,但是在一个程序中发生的事实使得单位线程阻塞。
编辑4:仅使用DirectShow进行阻止,而不是使用MediaFoundation进行阻止