Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

[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 Memphiz authored
Showing with 97 additions and 55 deletions.
  1. +84 −49 xbmc/network/AirPlayServer.cpp
  2. +13 −6 xbmc/network/AirPlayServer.h
View
133 xbmc/network/AirPlayServer.cpp
@@ -38,6 +38,9 @@
#include "guilib/GUIWindowManager.h"
#include "URL.h"
#include "cores/IPlayer.h"
+#include "interfaces/AnnouncementManager.h"
+
+using namespace ANNOUNCEMENT;
#ifdef TARGET_WINDOWS
#define close closesocket
@@ -134,6 +137,8 @@ const char *eventStrings[] = {"playing", "paused", "loading", "stopped"};
"<dict>\r\n"\
"<key>category</key>\r\n"\
"<string>video</string>\r\n"\
+"<key>sessionID</key>\r\n"\
+"<integer>%d</integer>\r\n"\
"<key>state</key>\r\n"\
"<string>%s</string>\r\n"\
"</dict>\r\n"\
@@ -142,6 +147,25 @@ const char *eventStrings[] = {"playing", "paused", "loading", "stopped"};
#define AUTH_REALM "AirPlay"
#define AUTH_REQUIRED "WWW-Authenticate: Digest realm=\"" AUTH_REALM "\", nonce=\"%s\"\r\n"
+void CAirPlayServer::Announce(AnnouncementFlag flag, const char *sender, const char *message, const CVariant &data)
+{
+ if ( (flag & Player) && strcmp(sender, "xbmc") == 0 && ServerInstance)
+ {
+ if (strcmp(message, "OnStop") == 0)
+ {
+ ServerInstance->AnnounceToClients(EVENT_STOPPED);
+ }
+ else if (strcmp(message, "OnPlay") == 0)
+ {
+ ServerInstance->AnnounceToClients(EVENT_PLAYING);
+ }
+ else if (strcmp(message, "OnPause") == 0)
+ {
+ ServerInstance->AnnounceToClients(EVENT_PAUSED);
+ }
+ }
+}
+
bool CAirPlayServer::StartServer(int port, bool nonlocal)
{
StopServer(true);
@@ -187,17 +211,62 @@ void CAirPlayServer::StopServer(bool bWait)
}
}
+void CAirPlayServer::AnnounceToClients(int state)
+{
+ CSingleLock lock (m_connectionLock);
+
+ std::vector<CTCPClient>::iterator it;
+ for (it = m_connections.begin(); it != m_connections.end(); it++)
+ {
+ CStdString reverseHeader;
+ CStdString reverseBody;
+ CStdString response;
+ int reverseSocket = INVALID_SOCKET;
+ it->ComposeReverseEvent(reverseHeader, reverseBody, state);
+
+ // Send event status per reverse http socket (play, loading, paused)
+ // if we have a reverse header and a reverse socket
+ if (reverseHeader.size() > 0 && m_reverseSockets.find(it->m_sessionId) != m_reverseSockets.end())
+ {
+ //search the reverse socket to this sessionid
+ response.Format("POST /event HTTP/1.1\r\n");
+ reverseSocket = m_reverseSockets[it->m_sessionId]; //that is our reverse socket
+ response += reverseHeader;
+ }
+ response += "\r\n";
+
+ if (reverseBody.size() > 0)
+ {
+ response += reverseBody;
+ }
+
+ // don't send it to the connection object
+ // the reverse socket itself belongs to
+ if (reverseSocket != INVALID_SOCKET && reverseSocket != it->m_socket)
+ {
+ send(reverseSocket, response.c_str(), response.size(), 0);//send the event status on the eventSocket
+ }
+ }
+}
+
CAirPlayServer::CAirPlayServer(int port, bool nonlocal) : CThread("AirPlayServer")
{
m_port = port;
m_nonlocal = nonlocal;
m_ServerSocket = INVALID_SOCKET;
m_usePassword = false;
+ CAnnouncementManager::AddAnnouncer(this);
+}
+
+CAirPlayServer::~CAirPlayServer()
+{
+ CAnnouncementManager::RemoveAnnouncer(this);
}
void CAirPlayServer::Process()
{
m_bStop = false;
+ static int sessionCounter = 0;
while (!m_bStop)
{
@@ -240,6 +309,7 @@ void CAirPlayServer::Process()
}
if (nread <= 0)
{
+ CSingleLock lock (m_connectionLock);
CLog::Log(LOGINFO, "AIRPLAY Server: Disconnection detected");
m_connections[i].Disconnect();
m_connections.erase(m_connections.begin() + i);
@@ -252,6 +322,8 @@ void CAirPlayServer::Process()
CLog::Log(LOGDEBUG, "AIRPLAY Server: New connection detected");
CTCPClient newconnection;
newconnection.m_socket = accept(m_ServerSocket, &newconnection.m_cliaddr, &newconnection.m_addrlen);
+ sessionCounter++;
+ newconnection.m_sessionCounter = sessionCounter;
if (newconnection.m_socket == INVALID_SOCKET)
{
@@ -265,6 +337,7 @@ void CAirPlayServer::Process()
}
else
{
+ CSingleLock lock (m_connectionLock);
CLog::Log(LOGINFO, "AIRPLAY Server: New connection added");
m_connections.push_back(newconnection);
}
@@ -319,6 +392,7 @@ bool CAirPlayServer::Initialize()
void CAirPlayServer::Deinitialize()
{
+ CSingleLock lock (m_connectionLock);
for (unsigned int i = 0; i < m_connections.size(); i++)
m_connections[i].Disconnect();
@@ -381,11 +455,9 @@ void CAirPlayServer::CTCPClient::PushBuffer(CAirPlayServer *host, const char *bu
// Parse the request
CStdString responseHeader;
CStdString responseBody;
- CStdString reverseHeader;
- CStdString reverseBody;
- int status = ProcessRequest(responseHeader, responseBody, reverseHeader, reverseBody, sessionId);
+ int status = ProcessRequest(responseHeader, responseBody);
+ sessionId = m_sessionId;
CStdString statusMsg = "OK";
- int reverseSocket = INVALID_SOCKET;
switch(status)
{
@@ -435,28 +507,6 @@ void CAirPlayServer::CTCPClient::PushBuffer(CAirPlayServer *host, const char *bu
{
send(m_socket, response.c_str(), response.size(), 0);
}
-
- // Send event status per reverse http socket (play, loading, paused)
- // if we have a reverse header and a reverse socket
- if (reverseHeader.size() > 0 && reverseSockets.find(sessionId) != reverseSockets.end())
- {
- //search the reverse socket to this sessionid
- response.Format("POST /event HTTP/1.1\r\n");
- reverseSocket = reverseSockets[sessionId]; //that is our reverse socket
- response += reverseHeader;
- }
- response += "\r\n";
-
- if (reverseBody.size() > 0)
- {
- response += reverseBody;
- }
-
- if (reverseSocket != INVALID_SOCKET)
- {
- send(reverseSocket, response.c_str(), response.size(), 0);//send the event status on the eventSocket
- }
-
// We need a new parser...
delete m_httpParser;
m_httpParser = new HttpParser;
@@ -484,12 +534,12 @@ void CAirPlayServer::CTCPClient::Copy(const CTCPClient& client)
m_httpParser = client.m_httpParser;
m_authNonce = client.m_authNonce;
m_bAuthenticated = client.m_bAuthenticated;
+ m_sessionCounter = client.m_sessionCounter;
}
void CAirPlayServer::CTCPClient::ComposeReverseEvent( CStdString& reverseHeader,
CStdString& reverseBody,
- CStdString sessionId,
int state)
{
@@ -501,13 +551,13 @@ void CAirPlayServer::CTCPClient::ComposeReverseEvent( CStdString& reverseHeader,
case EVENT_LOADING:
case EVENT_PAUSED:
case EVENT_STOPPED:
- reverseBody.Format(EVENT_INFO, eventStrings[state]);
+ reverseBody.Format(EVENT_INFO, m_sessionCounter, eventStrings[state]);
CLog::Log(LOGDEBUG, "AIRPLAY: sending event: %s", eventStrings[state]);
break;
}
reverseHeader = "Content-Type: text/x-apple-plist+xml\r\n";
- reverseHeader.Format("%sContent-Length: %d",reverseHeader.c_str(),reverseBody.size());
- reverseHeader.Format("%sx-apple-session-id: %s\r\n",reverseHeader.c_str(),sessionId.c_str());
+ reverseHeader.Format("%sContent-Length: %d\r\n",reverseHeader.c_str(),reverseBody.size());
+ reverseHeader.Format("%sx-apple-session-id: %s\r\n",reverseHeader.c_str(),m_sessionId.c_str());
m_lastEvent = state;
}
}
@@ -631,17 +681,14 @@ bool CAirPlayServer::CTCPClient::checkAuthorization(const CStdString& authStr,
}
int CAirPlayServer::CTCPClient::ProcessRequest( CStdString& responseHeader,
- CStdString& responseBody,
- CStdString& reverseHeader,
- CStdString& reverseBody,
- CStdString& sessionId)
+ CStdString& responseBody)
{
CStdString method = m_httpParser->getMethod();
CStdString uri = m_httpParser->getUri();
CStdString queryString = m_httpParser->getQueryString();
CStdString body = m_httpParser->getBody();
CStdString contentType = m_httpParser->getValue("content-type");
- sessionId = m_httpParser->getValue("x-apple-session-id");
+ m_sessionId = m_httpParser->getValue("x-apple-session-id");
CStdString authorization = m_httpParser->getValue("authorization");
int status = AIRPLAY_STATUS_OK;
bool needAuth = false;
@@ -688,7 +735,6 @@ int CAirPlayServer::CTCPClient::ProcessRequest( CStdString& responseHeader,
if (g_application.m_pPlayer && g_application.m_pPlayer->IsPlaying() && !g_application.m_pPlayer->IsPaused())
{
CApplicationMessenger::Get().MediaPause();
- ComposeReverseEvent(reverseHeader, reverseBody, sessionId, EVENT_PAUSED);
}
}
else
@@ -696,7 +742,6 @@ int CAirPlayServer::CTCPClient::ProcessRequest( CStdString& responseHeader,
if (g_application.m_pPlayer && g_application.m_pPlayer->IsPlaying() && g_application.m_pPlayer->IsPaused())
{
CApplicationMessenger::Get().MediaPause();
- ComposeReverseEvent(reverseHeader, reverseBody, sessionId, EVENT_PLAYING);
}
}
}
@@ -820,8 +865,8 @@ int CAirPlayServer::CTCPClient::ProcessRequest( CStdString& responseHeader,
CFileItem fileToPlay(location, false);
fileToPlay.SetProperty("StartPercent", position*100.0f);
+ ServerInstance->AnnounceToClients(EVENT_LOADING);
CApplicationMessenger::Get().MediaPlay(fileToPlay);
- ComposeReverseEvent(reverseHeader, reverseBody, sessionId, EVENT_PLAYING);
}
}
@@ -879,7 +924,6 @@ int CAirPlayServer::CTCPClient::ProcessRequest( CStdString& responseHeader,
{
g_windowManager.PreviousWindow();
}
- ComposeReverseEvent(reverseHeader, reverseBody, sessionId, EVENT_STOPPED);
}
}
@@ -950,22 +994,13 @@ int CAirPlayServer::CTCPClient::ProcessRequest( CStdString& responseHeader,
if (g_application.m_pPlayer->IsCaching())
{
- ComposeReverseEvent(reverseHeader, reverseBody, sessionId, EVENT_LOADING);
- }
- else if (playing)
- {
- ComposeReverseEvent(reverseHeader, reverseBody, sessionId, EVENT_PLAYING);
- }
- else
- {
- ComposeReverseEvent(reverseHeader, reverseBody, sessionId, EVENT_PAUSED);
+ CAirPlayServer::ServerInstance->AnnounceToClients(EVENT_LOADING);
}
}
else
{
responseBody.Format(PLAYBACK_INFO_NOT_READY, duration, cachePosition, position, (playing ? 1 : 0), duration);
responseHeader = "Content-Type: text/x-apple-plist+xml\r\n";
- ComposeReverseEvent(reverseHeader, reverseBody, sessionId, EVENT_STOPPED);
}
}
View
19 xbmc/network/AirPlayServer.h
@@ -31,14 +31,19 @@
#include "threads/CriticalSection.h"
#include "utils/HttpParser.h"
#include "utils/StdString.h"
+#include "interfaces/IAnnouncer.h"
class DllLibPlist;
#define AIRPLAY_SERVER_VERSION_STR "101.28"
-class CAirPlayServer : public CThread
+class CAirPlayServer : public CThread, public ANNOUNCEMENT::IAnnouncer
{
public:
+ // IAnnouncer IF
+ virtual void Announce(ANNOUNCEMENT::AnnouncementFlag flag, const char *sender, const char *message, const CVariant &data);
+
+ //AirPlayServer impl.
static bool StartServer(int port, bool nonlocal);
static void StopServer(bool bWait);
static bool SetCredentials(bool usePassword, const CStdString& password);
@@ -50,9 +55,11 @@ class CAirPlayServer : public CThread
private:
CAirPlayServer(int port, bool nonlocal);
+ ~CAirPlayServer();
bool SetInternalCredentials(bool usePassword, const CStdString& password);
bool Initialize();
void Deinitialize();
+ void AnnounceToClients(int state);
class CTCPClient
{
@@ -66,6 +73,7 @@ class CAirPlayServer : public CThread
void PushBuffer(CAirPlayServer *host, const char *buffer,
int length, CStdString &sessionId,
std::map<CStdString, int> &reverseSockets);
+ void ComposeReverseEvent(CStdString& reverseHeader, CStdString& reverseBody, int state);
void Disconnect();
@@ -73,15 +81,13 @@ class CAirPlayServer : public CThread
struct sockaddr m_cliaddr;
socklen_t m_addrlen;
CCriticalSection m_critSection;
+ int m_sessionCounter;
+ CStdString m_sessionId;
private:
int ProcessRequest( CStdString& responseHeader,
- CStdString& response,
- CStdString& reverseHeader,
- CStdString& reverseBody,
- CStdString& sessionId);
+ CStdString& response);
- void ComposeReverseEvent(CStdString& reverseHeader, CStdString& reverseBody, CStdString sessionId, int state);
void ComposeAuthRequestAnswer(CStdString& responseHeader, CStdString& responseBody);
bool checkAuthorization(const CStdString& authStr, const CStdString& method, const CStdString& uri);
void Copy(const CTCPClient& client);
@@ -93,6 +99,7 @@ class CAirPlayServer : public CThread
CStdString m_authNonce;
};
+ CCriticalSection m_connectionLock;
std::vector<CTCPClient> m_connections;
std::map<CStdString, int> m_reverseSockets;
int m_ServerSocket;
Please sign in to comment.
Something went wrong with that request. Please try again.