Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C#

DLNA Media Server to feed Smart TV's

5.00/5 (2 votes)
29 Feb 2016CPOL3 min read 24.9K   1.2K  
Just a very simple web-server that uses byte-ranges realy

Introduction

This lightweight little DLNA server is able to stream files to your network devices ("Smart TV's) and will use byte-ranges if the device offers this option so that the current playing position of the movie or sound can be forwarded during playback.

One off the easiest ways to host movies for playing across the network is to use a virtual directory in window IIS-Server and then send the Url of the movie/sound file to the DLNA device but this method becomes a little bloated and is overkill.

This little service replaces the host service offered by windows IIS-Server and simply services the request from devices such as smart TV's and is not a DLNA-Player as such but has been tested with VLC-Player and works fine with Samsung, LG, Sony Smart TV's

What's in the files

The projects was written using Visual Studio 2010 with C# and I have included a noddy user-interface form in the download to get you started that will allow you to test the service which includes the core component "MediaService.cs" and a small helper class "Helper.cs" that can be used to create your own full blown media player or you may decide to use this code as a simple web-server in other projects.

Starting the service

The service needs the ip-address and port number to listing on and the root location of the file system that contains the media files which could be on the local machine or a network drive that is connected to the local network.

C#
//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();

When the service is started a new thread is used to listen for incoming requests in a loop and if the request is just for "HEAD" data then the file is opened to retrieve the size of the file and the data then gets returned to the calling client (Smart TV) using the main server thread.

C#
//
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;
    }
//

If on the other hand the request are for the actual data then a new thread is used to service the request so that the main thread can listen for other requests that might soon arrive.

Code below shows how chunks of data are sent back to the calling client that use a Byte-Range in the request for streaming the data a bit at a time.

C#
//
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--;
    }

//

Testing it works

See http://www.codeproject.com/Articles/893791/DLNA-made-easy-with-Play-To-from-any-device if you are buillding a media player that will work with this media-server or test out code by using VLC-Player and then clicking media/open netowork stream and then enter the Url that should look a something like hxxp://192.168.1.10:9090/MyMovie.avi

Whats Next !

Over the years I have managed to collect quite a wide range of music but often I removed or lost the album-artwork that came with the sound tracks but with disk space being cheap these days and the invent of DLNA device I now find it is nice to look at something on the TV whilst the music is playing.

The next part to this project is a bit of code that reads the "Artist" and "Album" name from .Mp3 files and then use a well known search engine to find the album-art images that will then download these images to my network storage device so they can later be sent to the TV to be displayed as music is playing.

Edit to add

Just added the option of streaming Youtube video's to the TV's screen but had to build a Youtube downloader that you can view here http://www.codeproject.com/Articles/1081335/Youtube-downloader-in-one-easy-class

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)