2012-08-08 22 views
5

Tôi đang làm việc để tạo Thư viện trong C# để sử dụng giao thức Airplay để gửi Ảnh và Video tới Apple TV của tôi (Cụ thể là làm việc với Thế hệ 3 nhưng hy vọng rằng điều này không quan trọng đối với điều này).TcpClient hoặc HttpWebRequest với Apple TV kết thúc sau 30 giây?

https://airlib.codeplex.com/

Tất cả các lệnh cho Airplay là HTTP trên cổng 70 theo spec này: http://nto.github.com/AirPlay.html

tôi đã thành công trong việc nhận được cả hình ảnh và video để chơi trên TV Apple, nhưng không có vấn đề những gì tôi làm AppleTV sẽ chỉ chơi 30 giây giá trị của video. Có vẻ như máy khách C# của tôi phát ra lệnh play đang ngắt kết nối ngay lúc 30 giây, điều này làm cho AppleTV kết thúc phiên chơi.

lý do tại sao tôi nghĩ rằng đây:

  • Chấm dứt ứng dụng client hoàn toàn tạo ra hành vi tương tự như chờ đợi 30 giây (thực chất là ép buộc kết nối để đóng).
  • Đóng thủ công kết nối HttpWebRequest hoặc TcpClient tạo ra cùng một hành vi (Midway qua một phiên chơi).
  • Bất kể khoảng thời gian tôi giữ điểm dừng để ngăn chặn GetResponse() gọi video luôn luôn hết 30 giây sau khi WebRequest bắt đầu gửi tin nhắn.
  • Sử dụng nguồn khác (IIS, máy chủ web bên ngoài) cho video không thay đổi hành vi.
  • Ngay cả sau khi video đã được lưu vào bộ nhớ cache trên AppleTV và không phát lại, thời gian chờ vẫn xảy ra.

Tôi khá chắc chắn rằng yêu cầu của khách hàng cần kết nối trong suốt quá trình "phát" video và theo hiểu biết tốt nhất của tôi, tôi đã mã hóa nó để thực hiện điều đó. Tôi thực sự đang ở trí thông minh của tôi kết thúc. Tôi đã thử tất cả mọi thứ mà tôi có thể nghĩ đến bao gồm cả yêu cầu cả HttpWebRequest và TcpClient (cả hai đều hoạt động nhưng cả hai đều hết thời gian), thiết lập thời gian nhận/gửi thành số điên và lặp lại luồng đọc của luồng Tcp để đảm bảo rằng có "hoạt động".

Có vẻ như AppleTV đang chờ tôi gửi thông báo "này, tiếp tục chơi", nhưng tôi vẫn chưa thấy bất cứ điều gì giống như vậy từ bất kỳ nguồn nào trên web. Tôi hy vọng rằng điều này chỉ đơn giản là một cái gì đó ngu ngốc mà tôi không làm dựa trên sự thiếu kiến ​​thức Http/Tcp của tôi.

Đây là mã của tôi:

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 

Lưu ý: sử dụng telnet Tôi có thể kết nối với AppleTV trên cổng 7000 và dán vào lệnh này mà đóng toàn bộ 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 

tôi đang chạy Cassini Webserver trên cổng 82, nhưng điều này cũng làm việc với IIS. Điều này cung cấp thêm bằng chứng cho thấy ngăn xếp Net đang làm điều gì đó dưới mui xe tại 30 giây gây ra một ngắt kết nối.

Trả lời

5

Tôi đã tìm ra nó cuối cùng. Nó không phải là mã .Net giết chết kết nối, đó chính là Apple TV. Với wireshark tôi đã có thể thấy các tin nhắn Ack và Fin thích hợp từ AppleTV sau 30 giây không nhận được bất kỳ tin nhắn mới nào trên kết nối đó. Để giải quyết vấn đề tôi đã tìm ra bằng cách chơi với Telnet rằng AppleTV dường như không quan tâm đến những gì bạn gửi nó miễn là bạn gửi nó SOMETHING trên cơ sở định kỳ, mà dường như giữ kết nối còn sống.

Với HttpWebRequest phần gửi/nhận là khá đóng hộp. Nó được thiết kế cho một yêu cầu và đáp ứng tiêu chuẩn của Http, và nếu bạn cần làm bất cứ điều gì khác, bạn chỉ cần bắt đầu một HttpWebRequest mới thay vì sử dụng một cái hiện có. Đang cố gắng gửi một thông báo thứ 2 trên cùng một lỗi HttpWebRequest.

Vì vậy, tôi đã phải sử dụng TcpClient và phải làm lại phần cuối.

/// <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); 
     } 
    } 

Rõ ràng đây không phải là câu trả lời cuối cùng, nhưng điều này là tối thiểu để làm cho nó hoạt động. Nếu bất kỳ ai tìm ra phần cứng Apple thực tế đang gửi thay cho "ok", vui lòng thêm ghi chú.

+0

Việc sử dụng tiêu đề Keep-Alive có giải quyết được điều này có thể không? –

+0

Đó thực sự là điều đầu tiên tôi thử, không may là không giúp được gì. Ý tưởng tuyệt vời mặc dù! – ExcaliburVT

+0

Có thể quá muộn cho bạn nhưng hữu ích cho người khác: triển khai "reverse HTTP" cho phiên của bạn hoặc chỉ thăm dò ý kiến ​​"/ scrub" để giữ cho quá trình phát lại hoạt động - cũng rất hữu ích để có được thời lượng và vị trí của bộ phim. – coyer

Các vấn đề liên quan