支持HLS的自定义C#服务器

时间:2018-02-21 10:57:10

标签: c# http video-streaming http-live-streaming

我正在尝试构建一个支持HLS的自定义C#网络服务器,但具体来说,HLS部分不起作用。以下是我使用的代码。我想尽可能简单地保持它。问题是除了HLS协议中使用的文件类型之外,所有文件类型都有效。我尝试过的文件类型包括mp4xmlmp3jpgpng和没有扩展名的纯二进制数据文件。

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进行阻止

0 个答案:

没有答案