65.9K
CodeProject 正在变化。 阅读更多。
Home

DLNA 媒体服务器,用于支持智能电视

starIconstarIconstarIconstarIconstarIcon

5.00/5 (2投票s)

2016年2月29日

CPOL

3分钟阅读

viewsIcon

25346

downloadIcon

1265

只是一个非常简单的 Web 服务器,实际上使用字节范围

引言

这个轻量级的 DLNA 服务器能够将文件流式传输到您的网络设备(“智能电视”),并且如果设备提供此选项,将使用字节范围,以便在播放过程中可以快进电影或声音的当前播放位置。

在网络上托管电影最简单的方法之一是在 Windows IIS 服务器中使用虚拟目录,然后将电影/声音文件的 URL 发送到 DLNA 设备,但这种方法变得有点臃肿并且过于复杂。

这个小型服务取代了 Windows IIS 服务器提供的托管服务,并且只是为智能电视等设备提供服务请求,它本身不是一个 DLNA 播放器,但已经用 VLC 播放器进行了测试,并且与三星、LG、索尼智能电视配合良好。

文件包含什么

该项目使用 Visual Studio 2010 和 C# 编写,并且我在下载中包含了一个简单的用户界面表单,以帮助您入门,它将允许您测试服务,其中包括核心组件“MediaService.cs”和一个小的帮助类“Helper.cs”,您可以使用它来创建自己的全功能媒体播放器,或者您也可以决定将此代码用作其他项目中的简单 Web 服务器。

启动服务

该服务需要用于监听的 IP 地址和端口号,以及包含媒体文件的文件系统的根位置,该位置可以在本地计算机上或连接到本地网络的网络驱动器上。

//Code to start the service
private MediaServer MServer = null;
MServer = new MediaServer(this.TxtIP.Text, int.Parse(this.TxtPort.Text), this.TxtMediaPath.Text);
MServer.Start();
//Code to stop the service
if (MServer != null) MServer.Stop();

启动服务时,使用新线程在循环中侦听传入的请求,如果请求只是“HEAD”数据,则打开文件以检索文件大小,然后将数据返回到调用客户端(智能电视),使用主服务器线程。

//
private void SendHeadData(Socket Soc, string FileName)
    {//This runs in the same thread as the service since it should be nice and fast
      FileInfo FInfo = new FileInfo(FileName);
      if (!FInfo.Exists)
      {//We cannot find the file so just dump the connection
        Soc.Close();
        this.TempClient = null;//Flag also so the next request can be serviced
        return;
      }
      string ContentType = GetContentType(FileName);
      string Reply = "HTTP/1.1 200 OK" + Environment.NewLine + "Server: VLC" + Environment.NewLine + "Content-Type: " + ContentType + Environment.NewLine;
      Reply += "Last-Modified: " + GMTTime(DateTime.Now.AddYears(-1).AddDays(-7)) + Environment.NewLine;//Just dream up a date
      Reply += "Date: " + GMTTime(DateTime.Now) + Environment.NewLine;
      if (!IsMusicOrImage(FileName)) Reply += "Accept-Ranges: bytes" + Environment.NewLine;//We only do ranges for movies
      Reply += "Content-Length: " + FInfo.Length + Environment.NewLine;
      Reply += "Connection: close" + Environment.NewLine + Environment.NewLine;
      Soc.Send(UTF8Encoding.UTF8.GetBytes(Reply), SocketFlags.None);
      Soc.Close();
      this.TempClient = null;
    }
//

另一方面,如果请求是实际数据,则使用新线程来服务请求,以便主线程可以侦听可能很快到达的其他请求。

下面的代码显示了如何将数据块发送回使用字节范围的调用客户端,该字节范围用于请求,以便一次流式传输数据。

//
private string ContentString(long Range, string ContentType, long FileLength)
    {//Builds up our HTTP reply string for byte-range requests
      string Reply = "";
      Reply = "HTTP/1.1 206 Partial Content" + Environment.NewLine + "Server: VLC" + Environment.NewLine + "Content-Type: " + ContentType + Environment.NewLine;
      Reply += "Accept-Ranges: bytes" + Environment.NewLine;
      Reply += "Date: " + GMTTime(DateTime.Now) + Environment.NewLine;
      if (Range == 0)
      {
        Reply += "Content-Length: " + FileLength + Environment.NewLine;
        Reply += "Content-Range: bytes 0-" + (FileLength - 1) + "/" + FileLength + Environment.NewLine;
      }
      else
      {
        Reply += "Content-Length: " + (FileLength - Range) + Environment.NewLine;
        Reply += "Content-Range: bytes " + Range + "-" + (FileLength - 1) + "/" + FileLength + Environment.NewLine;
      }
      return Reply + Environment.NewLine;
    }


private void StreamMovie()
    {//Streams a movie using ranges and runs on it's own thread
      ClientCount++;
      long ChunkSize = 500000;
      long Range = TempRange;
      long BytesSent = 0;
      long ByteToSend = 1;
      string FileName = TempFileName.ToLower();
      string ContentType = GetContentType(FileName);
      Socket Client = this.TempClient;
      this.TempClient = null;//Server is ready to recive more requests now
      if (LastFileName != FileName) //Should use a lock here but i will risk it
      {
        if (!File.Exists(FileName)) { ClientCount--; Client.Close(); return; }
        if (FS != null) FS.Close();
        FileInfo FInfo = new FileInfo(FileName);
        LastFileLength = FInfo.Length;
        FS = new FileStream(FileName, FileMode.Open);
        LastFileName = FileName;
      }
      string Reply = ContentString(Range, ContentType, LastFileLength);
      Client.Send(UTF8Encoding.UTF8.GetBytes(Reply), SocketFlags.None);
      byte[] Buf = new byte[ChunkSize];
      if (FS.CanSeek)
        FS.Seek(Range, SeekOrigin.Begin);
      BytesSent = Range;
      while (this.Running && Client.Connected && ByteToSend > 0)
      {//Keep looping untill all the data is sent or the connection is dropped by the client
        LoopCount++;
        ByteToSend = LastFileLength - BytesSent;
        if (ByteToSend > ChunkSize) ByteToSend = ChunkSize;
        long BytesLeftToSend = LastFileLength - BytesSent;
        if (BytesSent + ChunkSize > LastFileLength)
          ChunkSize = LastFileLength - BytesSent;
        Buf = new byte[ByteToSend];
        FS.Read(Buf, 0, Buf.Length);
        BytesSent += Buf.Length;
        if (Client.Connected)
        {
          try { Client.Send(Buf); Thread.Sleep(100); }
          catch { ByteToSend = 0; }//Force an exit}
        }
      }
      if (!this.Running) { try { FS.Close(); FS = null; } catch { ;} }
      Client.Close();
      ClientCount--;
    }

//

测试它是否有效

如果您正在构建一个将与此媒体服务器一起使用的媒体播放器,请参阅 https://codeproject.org.cn/Articles/893791/DLNA-made-easy-with-Play-To-from-any-device,或者使用 VLC 播放器测试代码,然后单击媒体/打开网络流,然后输入 URL,该 URL 应该类似于 hxxp://192.168.1.10:9090/MyMovie.avi

下一步是什么!

多年来,我收集了相当多的音乐,但我经常删除或丢失了随音轨附带的专辑封面,但是随着当今磁盘空间变得便宜以及 DLNA 设备的出现,我现在发现能够在播放音乐的同时在电视上查看内容是一件很棒的事情。

该项目的下一部分代码会从 .Mp3 文件中读取“艺术家”和“专辑”名称,然后使用一个众所周知的搜索引擎来查找专辑封面图像,然后将这些图像下载到我的网络存储设备,以便以后可以在播放音乐时将其发送到电视上显示。

编辑以添加

刚刚添加了将 YouTube 视频流式传输到电视屏幕的选项,但不得不构建一个 YouTube 下载器,您可以在此处查看 https://codeproject.org.cn/Articles/1081335/Youtube-downloader-in-one-easy-class

© . All rights reserved.