I'm working on creating an Library in C# to use the Airplay protocol to send Photos and Video to my Apple TV (Specifically working with Generation 3 but hopefully that should not matter for this).
https://airlib.codeplex.com/
All of the commands for Airplay are HTTP on port 70 as per this spec: http://nto.github.com/AirPlay.html
I have been successful at getting both photos and video to play on the Apple TV, but no matter what I do the AppleTV will only play 30 seconds worth of video. It appears as though my C# client that issues the play command is disconnecting right at 30 seconds, which causes the AppleTV to end the play session.
Reasons why I think this:
- Terminating the client app completely produces the same behavior as waiting 30 seconds (essentially forcing the connection to close).
- Manually closing the HttpWebRequest or TcpClient connection produces the same behavior (Midway through a play session).
- Regardless of how long I hold the breakpoint to prevent the GetResponse() call the video always times out 30 seconds after the WebRequest begins sending the message.
- Using a different source (IIS, external webserver) for the video does not change the behavior.
- Even after the video has cached on the AppleTV and does not re-stream the timeout still occurs.
I'm pretty sure that the client request needs to stay connected throughout the "play" of the video, and to the best of my knowledge I have coded it to do that. I really am at my wits end. I have tried everything that I can think of including doing the request both as a HttpWebRequest and as a raw TcpClient (which both work but both time out), setting the Recieve/Send timeouts to crazy numbers, and looping the read of the Tcp stream to ensure that there is "activity".
Its as though the AppleTV is expecting me to send a "hey, keep playing" message, but I have yet to see anything like that from any source on the web. I'm hoping that this is simply something stupid that I'm not doing based on my lack of Http/Tcp knowledge.
Here is my code:
Uri url = "http://somevideo.com/video.mov";
float startPosition = 0;
TcpClient tcpClient = new TcpClient("192.168.1.20",7000);
tcpClient.ReceiveTimeout = 100000;
tcpClient.SendTimeout = 100000;
//get the client stream to read data from.
NetworkStream clientStream = tcpClient.GetStream();
string body =
"Content-Location: " + url + "\n" +
"Start-Position: " + startPosition + "\n";
string request = "POST /play HTTP/1.1\n" +
"User-Agent: MediaControl/1.0\n" +
"Content-Type: text/parameters\n" +
"Content-Length: " + Encoding.ASCII.GetBytes(body).Length + "\n" +
"X-Apple-Session-ID:" + _sessionGuid.ToString() + "\n\n";
sendMessage(clientStream, request);
sendMessage(clientStream, body);
byte[] myReadBuffer = new byte[1024];
StringBuilder myCompleteMessage = new StringBuilder();
int numberOfBytesRead = 0;
//incoming message might be bigger than the buffer
do
{
try
{
numberOfBytesRead = clientStream.Read(myReadBuffer, 0, myReadBuffer.Length);
myCompleteMessage.Append(Encoding.ASCII.GetString(myReadBuffer, 0, numberOfBytesRead));
Thread.Sleep(10);//let the iOS device catch up sending data
}
catch (System.IO.IOException) { }
} while (tcpClient.Connected); //check if it's connected before checking for data available, as maybe the program might get quit and the sockets closed halfway through a read
Note: using telnet I am able to connect to the AppleTV on port 7000 and paste in this command which plays the entire video:
POST /play HTTP/1.1
User-Agent: MediaControl/1.0
Content-Type: text/parameters
Content-Length: 89
X-Apple-Session-ID:fb6d816a-a5ad-4e8f-8830-9642b6e6eb35
Content-Location: http://192.168.1.11:82/2012/2012_03_11/IMG_1328.MOV
Start-Position: 0
I'm running the Cassini Webserver on port 82, but this also works with IIS. This offers further evidence that the .Net stack is doing something under the hood at 30 seconds that causes a disconnect.