Skip to content
This repository
Browse code

[airplay] - refactor the playstate announcement by using the IAnnounc…

…er interface

- also adapt to some new findings on revers engineering itunes airplay traffic (add sessioncounter to reverse event and fix http request formatting)
- fixes #14191
  • Loading branch information...
commit 7f0b5c61deb28bf7825a3504709be21fa37d95c1 1 parent 820b08c
Memphiz authored March 20, 2013
133  xbmc/network/AirPlayServer.cpp
@@ -38,6 +38,9 @@
38 38
 #include "guilib/GUIWindowManager.h"
39 39
 #include "URL.h"
40 40
 #include "cores/IPlayer.h"
  41
+#include "interfaces/AnnouncementManager.h"
  42
+
  43
+using namespace ANNOUNCEMENT;
41 44
 
42 45
 #ifdef TARGET_WINDOWS
43 46
 #define close closesocket
@@ -134,6 +137,8 @@ const char *eventStrings[] = {"playing", "paused", "loading", "stopped"};
134 137
 "<dict>\r\n"\
135 138
 "<key>category</key>\r\n"\
136 139
 "<string>video</string>\r\n"\
  140
+"<key>sessionID</key>\r\n"\
  141
+"<integer>%d</integer>\r\n"\
137 142
 "<key>state</key>\r\n"\
138 143
 "<string>%s</string>\r\n"\
139 144
 "</dict>\r\n"\
@@ -142,6 +147,25 @@ const char *eventStrings[] = {"playing", "paused", "loading", "stopped"};
142 147
 #define AUTH_REALM "AirPlay"
143 148
 #define AUTH_REQUIRED "WWW-Authenticate: Digest realm=\""  AUTH_REALM  "\", nonce=\"%s\"\r\n"
144 149
 
  150
+void CAirPlayServer::Announce(AnnouncementFlag flag, const char *sender, const char *message, const CVariant &data)
  151
+{
  152
+  if ( (flag & Player) && strcmp(sender, "xbmc") == 0 && ServerInstance)
  153
+  {
  154
+    if (strcmp(message, "OnStop") == 0)
  155
+    {
  156
+      ServerInstance->AnnounceToClients(EVENT_STOPPED);
  157
+    }
  158
+    else if (strcmp(message, "OnPlay") == 0)
  159
+    {
  160
+      ServerInstance->AnnounceToClients(EVENT_PLAYING);
  161
+    }
  162
+    else if (strcmp(message, "OnPause") == 0)
  163
+    {
  164
+      ServerInstance->AnnounceToClients(EVENT_PAUSED);
  165
+    }
  166
+  }
  167
+}
  168
+
145 169
 bool CAirPlayServer::StartServer(int port, bool nonlocal)
146 170
 {
147 171
   StopServer(true);
@@ -187,17 +211,62 @@ void CAirPlayServer::StopServer(bool bWait)
187 211
   }
188 212
 }
189 213
 
  214
+void CAirPlayServer::AnnounceToClients(int state)
  215
+{
  216
+  CSingleLock lock (m_connectionLock);
  217
+  
  218
+  std::vector<CTCPClient>::iterator it;
  219
+  for (it = m_connections.begin(); it != m_connections.end(); it++)
  220
+  {
  221
+    CStdString reverseHeader;
  222
+    CStdString reverseBody;
  223
+    CStdString response;
  224
+    int reverseSocket = INVALID_SOCKET;
  225
+    it->ComposeReverseEvent(reverseHeader, reverseBody, state);
  226
+  
  227
+    // Send event status per reverse http socket (play, loading, paused)
  228
+    // if we have a reverse header and a reverse socket
  229
+    if (reverseHeader.size() > 0 && m_reverseSockets.find(it->m_sessionId) != m_reverseSockets.end())
  230
+    {
  231
+      //search the reverse socket to this sessionid
  232
+      response.Format("POST /event HTTP/1.1\r\n");
  233
+      reverseSocket = m_reverseSockets[it->m_sessionId]; //that is our reverse socket
  234
+      response += reverseHeader;
  235
+    }
  236
+    response += "\r\n";
  237
+  
  238
+    if (reverseBody.size() > 0)
  239
+    {
  240
+      response += reverseBody;
  241
+    }
  242
+  
  243
+    // don't send it to the connection object
  244
+    // the reverse socket itself belongs to
  245
+    if (reverseSocket != INVALID_SOCKET && reverseSocket != it->m_socket)
  246
+    {
  247
+      send(reverseSocket, response.c_str(), response.size(), 0);//send the event status on the eventSocket
  248
+    }
  249
+  }
  250
+}
  251
+
190 252
 CAirPlayServer::CAirPlayServer(int port, bool nonlocal) : CThread("AirPlayServer")
191 253
 {
192 254
   m_port = port;
193 255
   m_nonlocal = nonlocal;
194 256
   m_ServerSocket = INVALID_SOCKET;
195 257
   m_usePassword = false;
  258
+  CAnnouncementManager::AddAnnouncer(this);
  259
+}
  260
+
  261
+CAirPlayServer::~CAirPlayServer()
  262
+{
  263
+  CAnnouncementManager::RemoveAnnouncer(this);
196 264
 }
197 265
 
198 266
 void CAirPlayServer::Process()
199 267
 {
200 268
   m_bStop = false;
  269
+  static int sessionCounter = 0;
201 270
 
202 271
   while (!m_bStop)
203 272
   {
@@ -240,6 +309,7 @@ void CAirPlayServer::Process()
240 309
           }
241 310
           if (nread <= 0)
242 311
           {
  312
+            CSingleLock lock (m_connectionLock);
243 313
             CLog::Log(LOGINFO, "AIRPLAY Server: Disconnection detected");
244 314
             m_connections[i].Disconnect();
245 315
             m_connections.erase(m_connections.begin() + i);
@@ -252,6 +322,8 @@ void CAirPlayServer::Process()
252 322
         CLog::Log(LOGDEBUG, "AIRPLAY Server: New connection detected");
253 323
         CTCPClient newconnection;
254 324
         newconnection.m_socket = accept(m_ServerSocket, &newconnection.m_cliaddr, &newconnection.m_addrlen);
  325
+        sessionCounter++;
  326
+        newconnection.m_sessionCounter = sessionCounter;
255 327
 
256 328
         if (newconnection.m_socket == INVALID_SOCKET)
257 329
         {
@@ -265,6 +337,7 @@ void CAirPlayServer::Process()
265 337
         }
266 338
         else
267 339
         {
  340
+          CSingleLock lock (m_connectionLock);
268 341
           CLog::Log(LOGINFO, "AIRPLAY Server: New connection added");
269 342
           m_connections.push_back(newconnection);
270 343
         }
@@ -319,6 +392,7 @@ bool CAirPlayServer::Initialize()
319 392
 
320 393
 void CAirPlayServer::Deinitialize()
321 394
 {
  395
+  CSingleLock lock (m_connectionLock);
322 396
   for (unsigned int i = 0; i < m_connections.size(); i++)
323 397
     m_connections[i].Disconnect();
324 398
 
@@ -381,11 +455,9 @@ void CAirPlayServer::CTCPClient::PushBuffer(CAirPlayServer *host, const char *bu
381 455
     // Parse the request
382 456
     CStdString responseHeader;
383 457
     CStdString responseBody;
384  
-    CStdString reverseHeader;
385  
-    CStdString reverseBody;
386  
-    int status = ProcessRequest(responseHeader, responseBody, reverseHeader, reverseBody, sessionId);
  458
+    int status = ProcessRequest(responseHeader, responseBody);
  459
+    sessionId = m_sessionId;
387 460
     CStdString statusMsg = "OK";
388  
-    int reverseSocket = INVALID_SOCKET;
389 461
 
390 462
     switch(status)
391 463
     {
@@ -435,28 +507,6 @@ void CAirPlayServer::CTCPClient::PushBuffer(CAirPlayServer *host, const char *bu
435 507
     {
436 508
       send(m_socket, response.c_str(), response.size(), 0);
437 509
     }
438  
-
439  
-    // Send event status per reverse http socket (play, loading, paused)
440  
-    // if we have a reverse header and a reverse socket
441  
-    if (reverseHeader.size() > 0 && reverseSockets.find(sessionId) != reverseSockets.end())
442  
-    {
443  
-      //search the reverse socket to this sessionid
444  
-      response.Format("POST /event HTTP/1.1\r\n");
445  
-      reverseSocket = reverseSockets[sessionId]; //that is our reverse socket
446  
-      response += reverseHeader;
447  
-    }
448  
-    response += "\r\n";
449  
-
450  
-    if (reverseBody.size() > 0)
451  
-    {
452  
-      response += reverseBody;
453  
-    }
454  
-
455  
-    if (reverseSocket != INVALID_SOCKET)
456  
-    {
457  
-      send(reverseSocket, response.c_str(), response.size(), 0);//send the event status on the eventSocket
458  
-    }
459  
-
460 510
     // We need a new parser...
461 511
     delete m_httpParser;
462 512
     m_httpParser = new HttpParser;
@@ -484,12 +534,12 @@ void CAirPlayServer::CTCPClient::Copy(const CTCPClient& client)
484 534
   m_httpParser        = client.m_httpParser;
485 535
   m_authNonce         = client.m_authNonce;
486 536
   m_bAuthenticated    = client.m_bAuthenticated;
  537
+  m_sessionCounter    = client.m_sessionCounter;
487 538
 }
488 539
 
489 540
 
490 541
 void CAirPlayServer::CTCPClient::ComposeReverseEvent( CStdString& reverseHeader,
491 542
                                                       CStdString& reverseBody,
492  
-                                                      CStdString sessionId,
493 543
                                                       int state)
494 544
 {
495 545
 
@@ -501,13 +551,13 @@ void CAirPlayServer::CTCPClient::ComposeReverseEvent( CStdString& reverseHeader,
501 551
       case EVENT_LOADING:
502 552
       case EVENT_PAUSED:
503 553
       case EVENT_STOPPED:      
504  
-        reverseBody.Format(EVENT_INFO, eventStrings[state]);
  554
+        reverseBody.Format(EVENT_INFO, m_sessionCounter, eventStrings[state]);
505 555
         CLog::Log(LOGDEBUG, "AIRPLAY: sending event: %s", eventStrings[state]);
506 556
         break;
507 557
     }
508 558
     reverseHeader = "Content-Type: text/x-apple-plist+xml\r\n";
509  
-    reverseHeader.Format("%sContent-Length: %d",reverseHeader.c_str(),reverseBody.size());
510  
-    reverseHeader.Format("%sx-apple-session-id: %s\r\n",reverseHeader.c_str(),sessionId.c_str());
  559
+    reverseHeader.Format("%sContent-Length: %d\r\n",reverseHeader.c_str(),reverseBody.size());
  560
+    reverseHeader.Format("%sx-apple-session-id: %s\r\n",reverseHeader.c_str(),m_sessionId.c_str());
511 561
     m_lastEvent = state;
512 562
   }
513 563
 }
@@ -631,17 +681,14 @@ bool CAirPlayServer::CTCPClient::checkAuthorization(const CStdString& authStr,
631 681
 }
632 682
 
633 683
 int CAirPlayServer::CTCPClient::ProcessRequest( CStdString& responseHeader,
634  
-                                                CStdString& responseBody,
635  
-                                                CStdString& reverseHeader,
636  
-                                                CStdString& reverseBody,
637  
-                                                CStdString& sessionId)
  684
+                                                CStdString& responseBody)
638 685
 {
639 686
   CStdString method = m_httpParser->getMethod();
640 687
   CStdString uri = m_httpParser->getUri();
641 688
   CStdString queryString = m_httpParser->getQueryString();
642 689
   CStdString body = m_httpParser->getBody();
643 690
   CStdString contentType = m_httpParser->getValue("content-type");
644  
-  sessionId = m_httpParser->getValue("x-apple-session-id");
  691
+  m_sessionId = m_httpParser->getValue("x-apple-session-id");
645 692
   CStdString authorization = m_httpParser->getValue("authorization");
646 693
   int status = AIRPLAY_STATUS_OK;
647 694
   bool needAuth = false;
@@ -688,7 +735,6 @@ int CAirPlayServer::CTCPClient::ProcessRequest( CStdString& responseHeader,
688 735
         if (g_application.m_pPlayer && g_application.m_pPlayer->IsPlaying() && !g_application.m_pPlayer->IsPaused())
689 736
         {
690 737
           CApplicationMessenger::Get().MediaPause();
691  
-          ComposeReverseEvent(reverseHeader, reverseBody, sessionId, EVENT_PAUSED);
692 738
         }
693 739
       }
694 740
       else
@@ -696,7 +742,6 @@ int CAirPlayServer::CTCPClient::ProcessRequest( CStdString& responseHeader,
696 742
         if (g_application.m_pPlayer && g_application.m_pPlayer->IsPlaying() && g_application.m_pPlayer->IsPaused())
697 743
         {
698 744
           CApplicationMessenger::Get().MediaPause();
699  
-          ComposeReverseEvent(reverseHeader, reverseBody, sessionId, EVENT_PLAYING);
700 745
         }
701 746
       }
702 747
   }
@@ -820,8 +865,8 @@ int CAirPlayServer::CTCPClient::ProcessRequest( CStdString& responseHeader,
820 865
 
821 866
       CFileItem fileToPlay(location, false);
822 867
       fileToPlay.SetProperty("StartPercent", position*100.0f);
  868
+      ServerInstance->AnnounceToClients(EVENT_LOADING);
823 869
       CApplicationMessenger::Get().MediaPlay(fileToPlay);
824  
-      ComposeReverseEvent(reverseHeader, reverseBody, sessionId, EVENT_PLAYING);
825 870
     }
826 871
   }
827 872
 
@@ -879,7 +924,6 @@ int CAirPlayServer::CTCPClient::ProcessRequest( CStdString& responseHeader,
879 924
       {
880 925
         g_windowManager.PreviousWindow();
881 926
       }
882  
-      ComposeReverseEvent(reverseHeader, reverseBody, sessionId, EVENT_STOPPED);
883 927
     }
884 928
   }
885 929
 
@@ -950,22 +994,13 @@ int CAirPlayServer::CTCPClient::ProcessRequest( CStdString& responseHeader,
950 994
 
951 995
       if (g_application.m_pPlayer->IsCaching())
952 996
       {
953  
-        ComposeReverseEvent(reverseHeader, reverseBody, sessionId, EVENT_LOADING);
954  
-      }
955  
-      else if (playing)
956  
-      {
957  
-        ComposeReverseEvent(reverseHeader, reverseBody, sessionId, EVENT_PLAYING);
958  
-      }
959  
-      else
960  
-      {
961  
-        ComposeReverseEvent(reverseHeader, reverseBody, sessionId, EVENT_PAUSED);
  997
+        CAirPlayServer::ServerInstance->AnnounceToClients(EVENT_LOADING);
962 998
       }
963 999
     }
964 1000
     else
965 1001
     {
966 1002
       responseBody.Format(PLAYBACK_INFO_NOT_READY, duration, cachePosition, position, (playing ? 1 : 0), duration);
967 1003
       responseHeader = "Content-Type: text/x-apple-plist+xml\r\n";     
968  
-      ComposeReverseEvent(reverseHeader, reverseBody, sessionId, EVENT_STOPPED);
969 1004
     }
970 1005
   }
971 1006
 
19  xbmc/network/AirPlayServer.h
@@ -31,14 +31,19 @@
31 31
 #include "threads/CriticalSection.h"
32 32
 #include "utils/HttpParser.h"
33 33
 #include "utils/StdString.h"
  34
+#include "interfaces/IAnnouncer.h"
34 35
 
35 36
 class DllLibPlist;
36 37
 
37 38
 #define AIRPLAY_SERVER_VERSION_STR "101.28"
38 39
 
39  
-class CAirPlayServer : public CThread
  40
+class CAirPlayServer : public CThread, public ANNOUNCEMENT::IAnnouncer
40 41
 {
41 42
 public:
  43
+  // IAnnouncer IF
  44
+  virtual void Announce(ANNOUNCEMENT::AnnouncementFlag flag, const char *sender, const char *message, const CVariant &data);
  45
+
  46
+  //AirPlayServer impl.
42 47
   static bool StartServer(int port, bool nonlocal);
43 48
   static void StopServer(bool bWait);
44 49
   static bool SetCredentials(bool usePassword, const CStdString& password);
@@ -50,9 +55,11 @@ class CAirPlayServer : public CThread
50 55
 
51 56
 private:
52 57
   CAirPlayServer(int port, bool nonlocal);
  58
+  ~CAirPlayServer();
53 59
   bool SetInternalCredentials(bool usePassword, const CStdString& password);
54 60
   bool Initialize();
55 61
   void Deinitialize();
  62
+  void AnnounceToClients(int state);
56 63
 
57 64
   class CTCPClient
58 65
   {
@@ -66,6 +73,7 @@ class CAirPlayServer : public CThread
66 73
     void PushBuffer(CAirPlayServer *host, const char *buffer,
67 74
                     int length, CStdString &sessionId,
68 75
                     std::map<CStdString, int> &reverseSockets);
  76
+    void ComposeReverseEvent(CStdString& reverseHeader, CStdString& reverseBody, int state);
69 77
 
70 78
     void Disconnect();
71 79
 
@@ -73,15 +81,13 @@ class CAirPlayServer : public CThread
73 81
     struct sockaddr m_cliaddr;
74 82
     socklen_t m_addrlen;
75 83
     CCriticalSection m_critSection;
  84
+    int  m_sessionCounter;
  85
+    CStdString m_sessionId;
76 86
 
77 87
   private:
78 88
     int ProcessRequest( CStdString& responseHeader,
79  
-                        CStdString& response,
80  
-                        CStdString& reverseHeader,
81  
-                        CStdString& reverseBody,
82  
-                        CStdString& sessionId);
  89
+                        CStdString& response);
83 90
 
84  
-    void ComposeReverseEvent(CStdString& reverseHeader, CStdString& reverseBody, CStdString sessionId, int state);
85 91
     void ComposeAuthRequestAnswer(CStdString& responseHeader, CStdString& responseBody);
86 92
     bool checkAuthorization(const CStdString& authStr, const CStdString& method, const CStdString& uri);
87 93
     void Copy(const CTCPClient& client);
@@ -93,6 +99,7 @@ class CAirPlayServer : public CThread
93 99
     CStdString m_authNonce;
94 100
   };
95 101
 
  102
+  CCriticalSection m_connectionLock;
96 103
   std::vector<CTCPClient> m_connections;
97 104
   std::map<CStdString, int> m_reverseSockets;
98 105
   int m_ServerSocket;

0 notes on commit 7f0b5c6

Please sign in to comment.
Something went wrong with that request. Please try again.