2012-08-08 7 views
5

Estoy trabajando en la creación de una Biblioteca en C# para usar el protocolo Airplay para enviar Fotos y Video a mi Apple TV (Específicamente trabajando con Generation 3 pero espero que eso no debería importar).TcpClient o HttpWebRequest al Apple TV que finaliza después de 30 segundos?

https://airlib.codeplex.com/

Todos los comandos para Airplay son HTTP en el puerto 70 de acuerdo con esta especificación: http://nto.github.com/AirPlay.html

que han tenido éxito en conseguir ambas fotos y vídeo para reproducir en el Apple TV, pero sin importa lo que haga, AppleTV solo reproducirá 30 segundos de video. Parece como si mi cliente C# que emite el comando de reproducción se desconectara a la derecha en 30 segundos, lo que hace que el AppleTV termine la sesión de juego.

razones por las que creo esto:

  • por concluida la aplicación cliente produce por completo el mismo comportamiento que esperar 30 segundos (forzando esencialmente la conexión para cerrar).
  • El cierre manual de la conexión HttpWebRequest o TcpClient produce el mismo comportamiento (a mitad de camino a través de una sesión de reproducción).
  • Independientemente de cuánto tiempo mantenga el punto de interrupción para evitar la llamada a GetResponse(), el video siempre expira 30 segundos después de que WebRequest comience a enviar el mensaje.
  • El uso de una fuente diferente (IIS, servidor web externo) para el video no cambia el comportamiento.
  • Incluso después de que el video se haya guardado en la memoria del AppleTV y no se vuelva a transmitir, el tiempo de espera aún se produce.

Estoy bastante seguro de que la solicitud del cliente debe permanecer conectada durante toda la "reproducción" del video, y a mi leal saber y entender, lo he codificado para hacerlo. Realmente estoy en mi ingenio final. He intentado todo lo que puedo pensar incluyendo hacer la solicitud tanto como HttpWebRequest como como TcpClient sin procesar (que ambos funcionan pero ambos tiempos de espera), establecer los tiempos de espera de recepción/envío en números locos y poner en bucle la lectura de la secuencia de Tcp para asegurar que haya "actividad".

Es como si el AppleTV esperara que enviara un mensaje de "oye, sigue jugando", pero aún no he visto nada de eso en ninguna fuente en la web. Espero que esto sea simplemente algo estúpido que no estoy haciendo debido a mi falta de conocimiento de Http/Tcp.

Aquí está mi código:

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 

Nota: el uso de telnet soy capaz de conectar con el AppleTV en el puerto 7000 y pegar en este comando, que juega todo el vídeo:

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 Estoy ejecutando el servidor web Cassini en el puerto 82, pero esto también funciona con IIS. Esto ofrece más evidencia de que la pila .Net está haciendo algo bajo el capó a los 30 segundos que causa una desconexión.

Respuesta

5

Lo descubrí finalmente. No era el código .Net lo que mataba la conexión, era el propio Apple TV. Con wireshark pude ver los mensajes apropiados de Ack y Fin que eran de AppleTV después de 30 segundos de no recibir ningún mensaje nuevo en esa conexión. Para resolver el problema, me di cuenta al jugar con Telnet que al AppleTV no parece importarle lo que envíes, siempre y cuando lo envíes ALGO de forma periódica, lo que parece mantener viva la conexión.

Con HttpWebRequest, la porción de envío/recepción está bastante enlatada. Está diseñado para una solicitud y respuesta Http estándar, y si necesita hacer otra cosa, simplemente comienza una nueva HttpWebRequest en lugar de usar la existente. Intentando enviar un segundo mensaje en los mismos errores de HttpWebRequest.

Así que tuve que usar un TcpClient y tuve que volver a trabajar el final.

/// <summary> 
    /// Starts a video. 
    /// </summary> 
    /// <param name="url">The URL of the video to play.</param> 
    /// <param name="startPosition">The start position of the video. This value must be between 0 and 1</param> 
    public void StartVideo(Uri url, float startPosition = 0) 
    { 
     if (startPosition > 1) 
     { 
      throw new ArgumentException("Start Position must be between 0 and 1"); 
     } 

     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"; 

     //Send the headers 
     sendMessage(clientStream, request); 
     //Send the body 
     sendMessage(clientStream, body); 

     //Get the response 
     byte[] myReadBuffer = new byte[1024]; 
     StringBuilder myCompleteMessage = new StringBuilder(); 
     int numberOfBytesRead = 0; 
     numberOfBytesRead = clientStream.Read(myReadBuffer, 0, myReadBuffer.Length); 
     myCompleteMessage.Append(Encoding.ASCII.GetString(myReadBuffer, 0, numberOfBytesRead)); 

     //Now start doing a "keepalive" 
     while (true) 
     { 
      //Simply send the characters "ok" every two seconds 
      sendMessage(clientStream, "ok"); 
      Thread.Sleep(2000); 
     }      
    } 

    /// <summary> 
    /// Sends a message across the NetworkStream 
    /// </summary> 
    /// <param name="clientStream">The stream to send the message down</param> 
    /// <param name="message">The message to send</param> 
    public void sendMessage(NetworkStream clientStream, string message) 
    { 
     byte[] buffer = new ASCIIEncoding().GetBytes(message); 
     try 
     { 
      clientStream.Write(buffer, 0, buffer.Length); 
      clientStream.Flush(); 
     } 
     catch (System.IO.IOException e) 
     { 
      Debug.WriteLine("IOException: " + e.Message); 
     } 
    } 

Obviamente esta no es la respuesta final, pero este fue el mínimo indispensable para que funcione. Si alguien se da cuenta de lo que está enviando el hardware real de Apple en lugar del "ok", por favor agregue una nota.

+0

¿Usaría un encabezado Keep-Alive para resolver esto posiblemente? –

+0

Eso fue en realidad lo primero que probé, desafortunadamente no ayudó. ¡Gran idea! – ExcaliburVT

+0

Tal vez sea demasiado tarde para usted pero útil para otras personas: implemente el "HTTP inverso" para su sesión o simplemente mire "/ scrub" para mantener la reproducción viva; también la eliminación es útil para obtener la duración y posición de la película. – coyer

Cuestiones relacionadas