xbmc: add some more upstream patches

Signed-off-by: Stephan Raue <stephan@openelec.tv>
This commit is contained in:
Stephan Raue 2012-06-09 10:55:42 +02:00
parent efdcefdcf9
commit 23e7cebd1e
4 changed files with 1149 additions and 0 deletions

View File

@ -0,0 +1,527 @@
From b73018af2ae69c7cfad0a4461d169a49c7d0dfbf Mon Sep 17 00:00:00 2001
From: Memphiz <memphis@machzwo.de>
Date: Tue, 15 May 2012 19:12:07 +0200
Subject: [PATCH] [airtunes] - implementation for windows using libshairplay
---
xbmc/network/AirTunesServer.cpp | 330 +++++++++++++++++++++++++++++++++++++--
xbmc/network/AirTunesServer.h | 23 +++
2 files changed, 343 insertions(+), 10 deletions(-)
diff --git a/xbmc/network/AirTunesServer.cpp b/xbmc/network/AirTunesServer.cpp
index a60ad09..e14da90 100644
--- a/xbmc/network/AirTunesServer.cpp
+++ b/xbmc/network/AirTunesServer.cpp
@@ -17,7 +17,9 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
+#if !defined(TARGET_WINDOWS)
#pragma GCC diagnostic ignored "-Wwrite-strings"
+#endif
#include "AirTunesServer.h"
@@ -37,15 +39,243 @@
#include "music/tags/MusicInfoTag.h"
#include "FileItem.h"
#include "GUIInfoManager.h"
+#include "guilib/GUIWindowManager.h"
#include "utils/Variant.h"
#include "settings/AdvancedSettings.h"
+#include "utils/EndianSwap.h"
+
+#include <map>
+#include <string>
+
using namespace XFILE;
+#if defined(TARGET_WINDOWS)
+DllLibShairplay *CAirTunesServer::m_pLibShairplay = NULL;
+#else
DllLibShairport *CAirTunesServer::m_pLibShairport = NULL;
+#endif
CAirTunesServer *CAirTunesServer::ServerInstance = NULL;
CStdString CAirTunesServer::m_macAddress;
+//parse daap metadata - thx to project MythTV
+std::map<std::string, std::string> decodeDMAP(const char *buffer, unsigned int size)
+{
+ std::map<std::string, std::string> result;
+ unsigned int offset = 8;
+ while (offset < size)
+ {
+ std::string tag;
+ tag.append(buffer + offset, 4);
+ offset += 4;
+ uint32_t length = Endian_SwapBE32(*(uint32_t *)(buffer + offset));
+ offset += sizeof(uint32_t);
+ std::string content;
+ content.append(buffer + offset, length);//possible fixme - utf8?
+ offset += length;
+ result[tag] = content;
+ }
+ return result;
+}
+
+void CAirTunesServer::SetMetadataFromBuffer(const char *buffer, unsigned int size)
+{
+ MUSIC_INFO::CMusicInfoTag tag;
+ std::map<std::string, std::string> metadata = decodeDMAP(buffer, size);
+ if(metadata["asal"].length())
+ tag.SetAlbum(metadata["asal"]);//album
+ if(metadata["minm"].length())
+ tag.SetTitle(metadata["minm"]);//title
+ if(metadata["asar"].length())
+ tag.SetArtist(metadata["asar"]);//artist
+ g_infoManager.SetCurrentSongTag(tag);
+}
+
+void CAirTunesServer::SetCoverArtFromBuffer(const char *buffer, unsigned int size)
+{
+ XFILE::CFile tmpFile;
+ const char *tmpFileName = "special://temp/airtunes_album_thumb.jpg";
+
+ if(!size)
+ return;
+
+ if (tmpFile.OpenForWrite(tmpFileName, true))
+ {
+ int writtenBytes=0;
+ writtenBytes = tmpFile.Write(buffer, size);
+ tmpFile.Close();
+
+ if(writtenBytes)
+ {
+ //reset to empty before setting the new one
+ //else it won't get refreshed because the name didn't change
+ g_infoManager.SetCurrentAlbumThumb("");
+ g_infoManager.SetCurrentAlbumThumb(tmpFileName);
+ //update the ui
+ CGUIMessage msg(GUI_MSG_NOTIFY_ALL,0,0,GUI_MSG_REFRESH_THUMBS);
+ g_windowManager.SendThreadMessage(msg);
+ }
+ }
+}
+
+#if defined(TARGET_WINDOWS)
+#define RSA_KEY " \
+-----BEGIN RSA PRIVATE KEY-----\
+MIIEpQIBAAKCAQEA59dE8qLieItsH1WgjrcFRKj6eUWqi+bGLOX1HL3U3GhC/j0Qg90u3sG/1CUt\
+wC5vOYvfDmFI6oSFXi5ELabWJmT2dKHzBJKa3k9ok+8t9ucRqMd6DZHJ2YCCLlDRKSKv6kDqnw4U\
+wPdpOMXziC/AMj3Z/lUVX1G7WSHCAWKf1zNS1eLvqr+boEjXuBOitnZ/bDzPHrTOZz0Dew0uowxf\
+/+sG+NCK3eQJVxqcaJ/vEHKIVd2M+5qL71yJQ+87X6oV3eaYvt3zWZYD6z5vYTcrtij2VZ9Zmni/\
+UAaHqn9JdsBWLUEpVviYnhimNVvYFZeCXg/IdTQ+x4IRdiXNv5hEewIDAQABAoIBAQDl8Axy9XfW\
+BLmkzkEiqoSwF0PsmVrPzH9KsnwLGH+QZlvjWd8SWYGN7u1507HvhF5N3drJoVU3O14nDY4TFQAa\
+LlJ9VM35AApXaLyY1ERrN7u9ALKd2LUwYhM7Km539O4yUFYikE2nIPscEsA5ltpxOgUGCY7b7ez5\
+NtD6nL1ZKauw7aNXmVAvmJTcuPxWmoktF3gDJKK2wxZuNGcJE0uFQEG4Z3BrWP7yoNuSK3dii2jm\
+lpPHr0O/KnPQtzI3eguhe0TwUem/eYSdyzMyVx/YpwkzwtYL3sR5k0o9rKQLtvLzfAqdBxBurciz\
+aaA/L0HIgAmOit1GJA2saMxTVPNhAoGBAPfgv1oeZxgxmotiCcMXFEQEWflzhWYTsXrhUIuz5jFu\
+a39GLS99ZEErhLdrwj8rDDViRVJ5skOp9zFvlYAHs0xh92ji1E7V/ysnKBfsMrPkk5KSKPrnjndM\
+oPdevWnVkgJ5jxFuNgxkOLMuG9i53B4yMvDTCRiIPMQ++N2iLDaRAoGBAO9v//mU8eVkQaoANf0Z\
+oMjW8CN4xwWA2cSEIHkd9AfFkftuv8oyLDCG3ZAf0vrhrrtkrfa7ef+AUb69DNggq4mHQAYBp7L+\
+k5DKzJrKuO0r+R0YbY9pZD1+/g9dVt91d6LQNepUE/yY2PP5CNoFmjedpLHMOPFdVgqDzDFxU8hL\
+AoGBANDrr7xAJbqBjHVwIzQ4To9pb4BNeqDndk5Qe7fT3+/H1njGaC0/rXE0Qb7q5ySgnsCb3DvA\
+cJyRM9SJ7OKlGt0FMSdJD5KG0XPIpAVNwgpXXH5MDJg09KHeh0kXo+QA6viFBi21y340NonnEfdf\
+54PX4ZGS/Xac1UK+pLkBB+zRAoGAf0AY3H3qKS2lMEI4bzEFoHeK3G895pDaK3TFBVmD7fV0Zhov\
+17fegFPMwOII8MisYm9ZfT2Z0s5Ro3s5rkt+nvLAdfC/PYPKzTLalpGSwomSNYJcB9HNMlmhkGzc\
+1JnLYT4iyUyx6pcZBmCd8bD0iwY/FzcgNDaUmbX9+XDvRA0CgYEAkE7pIPlE71qvfJQgoA9em0gI\
+LAuE4Pu13aKiJnfft7hIjbK+5kyb3TysZvoyDnb3HOKvInK7vXbKuU4ISgxB2bB3HcYzQMGsz1qJ\
+2gG0N5hvJpzwwhbhXqFKA4zaaSrw622wDniAK5MlIE0tIAKKP4yxNGjoD2QYjhBGuhvkWKY=\
+-----END RSA PRIVATE KEY-----"
+
+void CAirTunesServer::AudioOutputFunctions::audio_set_metadata(void *cls, void *session, const void *buffer, int buflen)
+{
+ CAirTunesServer::SetMetadataFromBuffer((char *)buffer, buflen);
+}
+
+void CAirTunesServer::AudioOutputFunctions::audio_set_coverart(void *cls, void *session, const void *buffer, int buflen)
+{
+ CAirTunesServer::SetCoverArtFromBuffer((char *)buffer, buflen);
+}
+
+void* CAirTunesServer::AudioOutputFunctions::audio_init(void *cls, int bits, int channels, int samplerate)
+{
+ XFILE::CPipeFile *pipe=(XFILE::CPipeFile *)cls;
+ pipe->OpenForWrite(XFILE::PipesManager::GetInstance().GetUniquePipeName());
+ pipe->SetOpenThreashold(300);
+
+ BXA_FmtHeader header;
+ strncpy(header.fourcc, "BXA ", 4);
+ header.type = BXA_PACKET_TYPE_FMT;
+ header.bitsPerSample = bits;
+ header.channels = channels;
+ header.sampleRate = samplerate;
+ header.durationMs = 0;
+
+ if (pipe->Write(&header, sizeof(header)) == 0)
+ return 0;
+
+ ThreadMessage tMsg = { TMSG_MEDIA_STOP };
+ g_application.getApplicationMessenger().SendMessage(tMsg, true);
+
+ CFileItem item;
+ item.SetPath(pipe->GetName());
+ item.SetMimeType("audio/x-xbmc-pcm");
+
+ ThreadMessage tMsg2 = { TMSG_GUI_ACTIVATE_WINDOW, WINDOW_VISUALISATION, 0 };
+ g_application.getApplicationMessenger().SendMessage(tMsg2, true);
+
+ g_application.getApplicationMessenger().PlayFile(item);
+
+ return "XBMC-AirTunes";//session
+}
+
+void CAirTunesServer::AudioOutputFunctions::audio_set_volume(void *cls, void *session, float volume)
+{
+ //volume from -144 - 0
+ float volPercent = 1 - volume/-144;
+ g_application.SetVolume(volPercent, false);//non-percent volume 0.0-1.0
+}
+
+void CAirTunesServer::AudioOutputFunctions::audio_process(void *cls, void *session, const void *buffer, int buflen)
+{
+ #define NUM_OF_BYTES 64
+ XFILE::CPipeFile *pipe=(XFILE::CPipeFile *)cls;
+ int sentBytes = 0;
+ unsigned char buf[NUM_OF_BYTES];
+
+ while (sentBytes < buflen)
+ {
+ int n = (buflen - sentBytes < NUM_OF_BYTES ? buflen - sentBytes : NUM_OF_BYTES);
+ memcpy(buf, (char*) buffer + sentBytes, n);
+
+ if (pipe->Write(buf, n) == 0)
+ return;
+
+ sentBytes += n;
+ }
+}
+
+void CAirTunesServer::AudioOutputFunctions::audio_flush(void *cls, void *session)
+{
+ XFILE::CPipeFile *pipe=(XFILE::CPipeFile *)cls;
+ pipe->Flush();
+}
+
+void CAirTunesServer::AudioOutputFunctions::audio_destroy(void *cls, void *session)
+{
+ XFILE::CPipeFile *pipe=(XFILE::CPipeFile *)cls;
+ pipe->SetEof();
+ pipe->Close();
+
+ //fix airplay video for ios5 devices
+ //on ios5 when airplaying video
+ //the client first opens an airtunes stream
+ //while the movie is loading
+ //in that case we don't want to stop the player here
+ //because this would stop the airplaying video
+#ifdef HAS_AIRPLAY
+ if (!CAirPlayServer::IsPlaying())
+#endif
+ {
+ ThreadMessage tMsg = { TMSG_MEDIA_STOP };
+ g_application.getApplicationMessenger().SendMessage(tMsg, true);
+ CLog::Log(LOGDEBUG, "AIRTUNES: AirPlay not running - stopping player");
+ }
+}
+
+void shairplay_log(int level, const char *msg)
+{
+ int xbmcLevel = LOGINFO;
+
+ switch(level)
+ {
+ case RAOP_LOG_EMERG: // system is unusable
+ xbmcLevel = LOGFATAL;
+ break;
+ case RAOP_LOG_ALERT: // action must be taken immediately
+ case RAOP_LOG_CRIT: // critical conditions
+ xbmcLevel = LOGSEVERE;
+ break;
+ case RAOP_LOG_ERR: // error conditions
+ xbmcLevel = LOGERROR;
+ break;
+ case RAOP_LOG_WARNING: // warning conditions
+ xbmcLevel = LOGWARNING;
+ break;
+ case RAOP_LOG_NOTICE: // normal but significant condition
+ xbmcLevel = LOGNOTICE;
+ break;
+ case RAOP_LOG_INFO: // informational
+ xbmcLevel = LOGINFO;
+ break;
+ case RAOP_LOG_DEBUG: // debug-level messages
+ xbmcLevel = LOGDEBUG;
+ break;
+ default:
+ break;
+ }
+ CLog::Log(xbmcLevel, "AIRTUNES: %s", msg);
+}
+
+#else
+
struct ao_device_xbmc
{
XFILE::CPipeFile *pipe;
@@ -214,6 +444,17 @@ char* CAirTunesServer::AudioOutputFunctions::ao_get_option(ao_option *options, c
return NULL;
}
+int shairport_log(const char* msg, size_t msgSize)
+{
+ if( g_advancedSettings.m_logEnableAirtunes)
+ {
+ CLog::Log(LOGDEBUG, "AIRTUNES: %s", msg);
+ }
+ return 1;
+}
+
+#endif
+
bool CAirTunesServer::StartServer(int port, bool nonlocal, bool usePassword, const CStdString &password/*=""*/)
{
bool success = false;
@@ -243,7 +484,9 @@ bool CAirTunesServer::StartServer(int port, bool nonlocal, bool usePassword, con
ServerInstance = new CAirTunesServer(port, nonlocal);
if (ServerInstance->Initialize(password))
{
+#ifndef TARGET_WINDOWS
ServerInstance->Create();
+#endif
success = true;
}
@@ -264,6 +507,9 @@ bool CAirTunesServer::StartServer(int port, bool nonlocal, bool usePassword, con
txt["sr"] = "44100";
txt["pw"] = "false";
txt["vn"] = "3";
+ txt["da"] = "true";
+ txt["vs"] = "130.14";
+ txt["md"] = "0,1,2";
txt["txtvers"] = "1";
CZeroconf::GetInstance()->PublishService("servers.airtunes", "_raop._tcp", appName, port, txt);
@@ -276,10 +522,12 @@ void CAirTunesServer::StopServer(bool bWait)
{
if (ServerInstance)
{
+#if !defined(TARGET_WINDOWS)
if (m_pLibShairport->IsLoaded())
{
m_pLibShairport->shairport_exit();
}
+#endif
ServerInstance->StopThread(bWait);
ServerInstance->Deinitialize();
if (bWait)
@@ -295,47 +543,98 @@ void CAirTunesServer::StopServer(bool bWait)
CAirTunesServer::CAirTunesServer(int port, bool nonlocal) : CThread("AirTunesServer")
{
m_port = port;
+#if defined(TARGET_WINDOWS)
+ m_pLibShairplay = new DllLibShairplay();
+ m_pPipe = new XFILE::CPipeFile;
+#else
m_pLibShairport = new DllLibShairport();
+#endif
}
CAirTunesServer::~CAirTunesServer()
{
+#if defined(TARGET_WINDOWS)
+ if (m_pLibShairplay->IsLoaded())
+ {
+ m_pLibShairplay->Unload();
+ }
+ delete m_pLibShairplay;
+ delete m_pPipe;
+#else
if (m_pLibShairport->IsLoaded())
{
m_pLibShairport->Unload();
}
delete m_pLibShairport;
+#endif
}
void CAirTunesServer::Process()
{
m_bStop = false;
+#if !defined(TARGET_WINDOWS)
while (!m_bStop && m_pLibShairport->shairport_is_running())
{
m_pLibShairport->shairport_loop();
}
+#endif
}
-int shairport_log(const char* msg, size_t msgSize)
+bool CAirTunesServer::Initialize(const CStdString &password)
{
- if( g_advancedSettings.m_logEnableAirtunes)
+ bool ret = false;
+
+ Deinitialize();
+
+#if defined(TARGET_WINDOWS)
+ if (m_pLibShairplay->Load())
{
- CLog::Log(LOGDEBUG, "AIRTUNES: %s", msg);
+
+ raop_callbacks_t ao;
+ ao.cls = m_pPipe;
+ ao.audio_init = AudioOutputFunctions::audio_init;
+ ao.audio_set_volume = AudioOutputFunctions::audio_set_volume;
+ ao.audio_set_metadata = AudioOutputFunctions::audio_set_metadata;
+ ao.audio_set_coverart = AudioOutputFunctions::audio_set_coverart;
+ ao.audio_process = AudioOutputFunctions::audio_process;
+ ao.audio_flush = AudioOutputFunctions::audio_flush;
+ ao.audio_destroy = AudioOutputFunctions::audio_destroy;
+ m_pLibShairplay->EnableDelayedUnload(false);
+ m_pRaop = m_pLibShairplay->raop_init(1, &ao, RSA_KEY);//1 - we handle one client at a time max
+ ret = m_pRaop != NULL;
+
+ if(ret)
+ {
+ char macAdr[6];
+ unsigned short port = (unsigned short)m_port;
+
+ m_pLibShairplay->raop_set_log_level(m_pRaop, RAOP_LOG_WARNING);
+ if(g_advancedSettings.m_logEnableAirtunes)
+ {
+ m_pLibShairplay->raop_set_log_level(m_pRaop, RAOP_LOG_DEBUG);
+ }
+
+ m_pLibShairplay->raop_set_log_callback(m_pRaop, shairplay_log);
+
+ CNetworkInterface *net = g_application.getNetwork().GetFirstConnectedInterface();
+
+ if (net)
+ {
+ net->GetMacAddressRaw(macAdr);
+ }
+
+ ret = m_pLibShairplay->raop_start(m_pRaop, &port, macAdr, 6, password.c_str()) >= 0;
+ }
}
- return 1;
-}
-bool CAirTunesServer::Initialize(const CStdString &password)
-{
- bool ret = false;
+#else
+
int numArgs = 3;
CStdString hwStr;
CStdString pwStr;
CStdString portStr;
- Deinitialize();
-
hwStr.Format("--mac=%s", m_macAddress.c_str());
pwStr.Format("--password=%s",password.c_str());
portStr.Format("--server_port=%d",m_port);
@@ -368,16 +667,27 @@ bool CAirTunesServer::Initialize(const CStdString &password)
m_pLibShairport->shairport_main(numArgs, argv);
ret = true;
}
+#endif
return ret;
}
void CAirTunesServer::Deinitialize()
{
+#if defined(TARGET_WINDOWS)
+ if (m_pLibShairplay && m_pLibShairplay->IsLoaded())
+ {
+ m_pLibShairplay->raop_stop(m_pRaop);
+ m_pLibShairplay->raop_destroy(m_pRaop);
+ m_pLibShairplay->Unload();
+ }
+#else
if (m_pLibShairport && m_pLibShairport->IsLoaded())
{
m_pLibShairport->shairport_exit();
m_pLibShairport->Unload();
}
+#endif
}
#endif
+
diff --git a/xbmc/network/AirTunesServer.h b/xbmc/network/AirTunesServer.h
index da893df..4a30e0a 100644
--- a/xbmc/network/AirTunesServer.h
+++ b/xbmc/network/AirTunesServer.h
@@ -26,7 +26,11 @@
#ifdef HAS_AIRTUNES
+#if defined(TARGET_WINDOWS)
+#include "DllLibShairplay.h"
+#else
#include "DllLibShairport.h"
+#endif
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
@@ -41,11 +45,14 @@
class DllLibShairport;
+
class CAirTunesServer : public CThread
{
public:
static bool StartServer(int port, bool nonlocal, bool usePassword, const CStdString &password="");
static void StopServer(bool bWait);
+ static void SetMetadataFromBuffer(const char *buffer, unsigned int size);
+ static void SetCoverArtFromBuffer(const char *buffer, unsigned int size);
protected:
void Process();
@@ -57,13 +64,28 @@ class CAirTunesServer : public CThread
void Deinitialize();
int m_port;
+#if defined(TARGET_WINDOWS)
+ static DllLibShairplay *m_pLibShairplay;//the lib
+ raop_t *m_pRaop;
+ XFILE::CPipeFile *m_pPipe;
+#else
static DllLibShairport *m_pLibShairport;//the lib
+#endif
static CAirTunesServer *ServerInstance;
static CStdString m_macAddress;
class AudioOutputFunctions
{
public:
+#if defined(TARGET_WINDOWS)
+ static void* audio_init(void *cls, int bits, int channels, int samplerate);
+ static void audio_set_volume(void *cls, void *session, float volume);
+ static void audio_set_metadata(void *cls, void *session, const void *buffer, int buflen);
+ static void audio_set_coverart(void *cls, void *session, const void *buffer, int buflen);
+ static void audio_process(void *cls, void *session, const void *buffer, int buflen);
+ static void audio_flush(void *cls, void *session);
+ static void audio_destroy(void *cls, void *session);
+#else
static void ao_initialize(void);
static int ao_play(ao_device *device, char *output_samples, uint32_t num_bytes);
static int ao_default_driver_id(void);
@@ -74,6 +96,7 @@ class CAirTunesServer : public CThread
static int ao_append_option(ao_option **options, const char *key, const char *value);
static void ao_free_options(ao_option *options);
static char* ao_get_option(ao_option *options, const char* key);
+#endif
};
};
--
1.7.10

View File

@ -0,0 +1,64 @@
From b7fb4615609c684a98dc1cc27906aaa0f117837f Mon Sep 17 00:00:00 2001
From: Memphiz <memphis@machzwo.de>
Date: Wed, 9 May 2012 18:53:45 +0200
Subject: [PATCH] [airtunes] - add dmap metadata parsing
---
xbmc/network/AirTunesServer.cpp | 13 ++++++++++++-
xbmc/network/AirTunesServer.h | 2 ++
2 files changed, 14 insertions(+), 1 deletion(-)
diff --git a/xbmc/network/AirTunesServer.cpp b/xbmc/network/AirTunesServer.cpp
index e14da90..2ad097e 100644
--- a/xbmc/network/AirTunesServer.cpp
+++ b/xbmc/network/AirTunesServer.cpp
@@ -47,7 +47,6 @@
#include <map>
#include <string>
-
using namespace XFILE;
#if defined(TARGET_WINDOWS)
@@ -390,6 +389,16 @@ int CAirTunesServer::AudioOutputFunctions::ao_close(ao_device *device)
return 0;
}
+void CAirTunesServer::AudioOutputFunctions::ao_set_metadata(const char *buffer, unsigned int size)
+{
+ CAirTunesServer::SetMetadataFromBuffer(buffer, size);
+}
+
+void CAirTunesServer::AudioOutputFunctions::ao_set_metadata_coverart(const char *buffer, unsigned int size)
+{
+ CAirTunesServer::SetCoverArtFromBuffer(buffer, size);
+}
+
/* -- Device Setup/Playback/Teardown -- */
int CAirTunesServer::AudioOutputFunctions::ao_append_option(ao_option **options, const char *key, const char *value)
{
@@ -658,6 +667,8 @@ bool CAirTunesServer::Initialize(const CStdString &password)
ao.ao_append_option = AudioOutputFunctions::ao_append_option;
ao.ao_free_options = AudioOutputFunctions::ao_free_options;
ao.ao_get_option = AudioOutputFunctions::ao_get_option;
+ ao.ao_set_metadata = AudioOutputFunctions::ao_set_metadata;
+ ao.ao_set_metadata_coverart = AudioOutputFunctions::ao_set_metadata_coverart;
struct printfPtr funcPtr;
funcPtr.extprintf = shairport_log;
diff --git a/xbmc/network/AirTunesServer.h b/xbmc/network/AirTunesServer.h
index 4a30e0a..0a4ce5a 100644
--- a/xbmc/network/AirTunesServer.h
+++ b/xbmc/network/AirTunesServer.h
@@ -96,6 +96,8 @@ class CAirTunesServer : public CThread
static int ao_append_option(ao_option **options, const char *key, const char *value);
static void ao_free_options(ao_option *options);
static char* ao_get_option(ao_option *options, const char* key);
+ static void ao_set_metadata(const char *buffer, unsigned int size);
+ static void ao_set_metadata_coverart(const char *buffer, unsigned int size);
#endif
};
};
--
1.7.10

View File

@ -0,0 +1,31 @@
From ab175ba28508445f6aff57386a8ce04b58a86f60 Mon Sep 17 00:00:00 2001
From: Memphiz <memphis@machzwo.de>
Date: Fri, 11 May 2012 19:56:37 +0200
Subject: [PATCH] [fix] - reapply fix for airtunes with AE which was lost
during merge
---
xbmc/network/AirTunesServer.cpp | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/xbmc/network/AirTunesServer.cpp b/xbmc/network/AirTunesServer.cpp
index 285a0a6..a60ad09 100644
--- a/xbmc/network/AirTunesServer.cpp
+++ b/xbmc/network/AirTunesServer.cpp
@@ -125,11 +125,11 @@ ao_device* CAirTunesServer::AudioOutputFunctions::ao_open_live(int driver_id, ao
if (ao_get_option(option, "name"))
item.GetMusicInfoTag()->SetTitle(ao_get_option(option, "name"));
- g_application.getApplicationMessenger().PlayFile(item);
-
ThreadMessage tMsg2 = { TMSG_GUI_ACTIVATE_WINDOW, WINDOW_VISUALISATION, 0 };
g_application.getApplicationMessenger().SendMessage(tMsg2, true);
+ g_application.getApplicationMessenger().PlayFile(item);
+
return (ao_device*) device;
}
--
1.7.10

View File

@ -0,0 +1,527 @@
From b73018af2ae69c7cfad0a4461d169a49c7d0dfbf Mon Sep 17 00:00:00 2001
From: Memphiz <memphis@machzwo.de>
Date: Tue, 15 May 2012 19:12:07 +0200
Subject: [PATCH] [airtunes] - implementation for windows using libshairplay
---
xbmc/network/AirTunesServer.cpp | 330 +++++++++++++++++++++++++++++++++++++--
xbmc/network/AirTunesServer.h | 23 +++
2 files changed, 343 insertions(+), 10 deletions(-)
diff --git a/xbmc/network/AirTunesServer.cpp b/xbmc/network/AirTunesServer.cpp
index a60ad09..e14da90 100644
--- a/xbmc/network/AirTunesServer.cpp
+++ b/xbmc/network/AirTunesServer.cpp
@@ -17,7 +17,9 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
+#if !defined(TARGET_WINDOWS)
#pragma GCC diagnostic ignored "-Wwrite-strings"
+#endif
#include "AirTunesServer.h"
@@ -37,15 +39,243 @@
#include "music/tags/MusicInfoTag.h"
#include "FileItem.h"
#include "GUIInfoManager.h"
+#include "guilib/GUIWindowManager.h"
#include "utils/Variant.h"
#include "settings/AdvancedSettings.h"
+#include "utils/EndianSwap.h"
+
+#include <map>
+#include <string>
+
using namespace XFILE;
+#if defined(TARGET_WINDOWS)
+DllLibShairplay *CAirTunesServer::m_pLibShairplay = NULL;
+#else
DllLibShairport *CAirTunesServer::m_pLibShairport = NULL;
+#endif
CAirTunesServer *CAirTunesServer::ServerInstance = NULL;
CStdString CAirTunesServer::m_macAddress;
+//parse daap metadata - thx to project MythTV
+std::map<std::string, std::string> decodeDMAP(const char *buffer, unsigned int size)
+{
+ std::map<std::string, std::string> result;
+ unsigned int offset = 8;
+ while (offset < size)
+ {
+ std::string tag;
+ tag.append(buffer + offset, 4);
+ offset += 4;
+ uint32_t length = Endian_SwapBE32(*(uint32_t *)(buffer + offset));
+ offset += sizeof(uint32_t);
+ std::string content;
+ content.append(buffer + offset, length);//possible fixme - utf8?
+ offset += length;
+ result[tag] = content;
+ }
+ return result;
+}
+
+void CAirTunesServer::SetMetadataFromBuffer(const char *buffer, unsigned int size)
+{
+ MUSIC_INFO::CMusicInfoTag tag;
+ std::map<std::string, std::string> metadata = decodeDMAP(buffer, size);
+ if(metadata["asal"].length())
+ tag.SetAlbum(metadata["asal"]);//album
+ if(metadata["minm"].length())
+ tag.SetTitle(metadata["minm"]);//title
+ if(metadata["asar"].length())
+ tag.SetArtist(metadata["asar"]);//artist
+ g_infoManager.SetCurrentSongTag(tag);
+}
+
+void CAirTunesServer::SetCoverArtFromBuffer(const char *buffer, unsigned int size)
+{
+ XFILE::CFile tmpFile;
+ const char *tmpFileName = "special://temp/airtunes_album_thumb.jpg";
+
+ if(!size)
+ return;
+
+ if (tmpFile.OpenForWrite(tmpFileName, true))
+ {
+ int writtenBytes=0;
+ writtenBytes = tmpFile.Write(buffer, size);
+ tmpFile.Close();
+
+ if(writtenBytes)
+ {
+ //reset to empty before setting the new one
+ //else it won't get refreshed because the name didn't change
+ g_infoManager.SetCurrentAlbumThumb("");
+ g_infoManager.SetCurrentAlbumThumb(tmpFileName);
+ //update the ui
+ CGUIMessage msg(GUI_MSG_NOTIFY_ALL,0,0,GUI_MSG_REFRESH_THUMBS);
+ g_windowManager.SendThreadMessage(msg);
+ }
+ }
+}
+
+#if defined(TARGET_WINDOWS)
+#define RSA_KEY " \
+-----BEGIN RSA PRIVATE KEY-----\
+MIIEpQIBAAKCAQEA59dE8qLieItsH1WgjrcFRKj6eUWqi+bGLOX1HL3U3GhC/j0Qg90u3sG/1CUt\
+wC5vOYvfDmFI6oSFXi5ELabWJmT2dKHzBJKa3k9ok+8t9ucRqMd6DZHJ2YCCLlDRKSKv6kDqnw4U\
+wPdpOMXziC/AMj3Z/lUVX1G7WSHCAWKf1zNS1eLvqr+boEjXuBOitnZ/bDzPHrTOZz0Dew0uowxf\
+/+sG+NCK3eQJVxqcaJ/vEHKIVd2M+5qL71yJQ+87X6oV3eaYvt3zWZYD6z5vYTcrtij2VZ9Zmni/\
+UAaHqn9JdsBWLUEpVviYnhimNVvYFZeCXg/IdTQ+x4IRdiXNv5hEewIDAQABAoIBAQDl8Axy9XfW\
+BLmkzkEiqoSwF0PsmVrPzH9KsnwLGH+QZlvjWd8SWYGN7u1507HvhF5N3drJoVU3O14nDY4TFQAa\
+LlJ9VM35AApXaLyY1ERrN7u9ALKd2LUwYhM7Km539O4yUFYikE2nIPscEsA5ltpxOgUGCY7b7ez5\
+NtD6nL1ZKauw7aNXmVAvmJTcuPxWmoktF3gDJKK2wxZuNGcJE0uFQEG4Z3BrWP7yoNuSK3dii2jm\
+lpPHr0O/KnPQtzI3eguhe0TwUem/eYSdyzMyVx/YpwkzwtYL3sR5k0o9rKQLtvLzfAqdBxBurciz\
+aaA/L0HIgAmOit1GJA2saMxTVPNhAoGBAPfgv1oeZxgxmotiCcMXFEQEWflzhWYTsXrhUIuz5jFu\
+a39GLS99ZEErhLdrwj8rDDViRVJ5skOp9zFvlYAHs0xh92ji1E7V/ysnKBfsMrPkk5KSKPrnjndM\
+oPdevWnVkgJ5jxFuNgxkOLMuG9i53B4yMvDTCRiIPMQ++N2iLDaRAoGBAO9v//mU8eVkQaoANf0Z\
+oMjW8CN4xwWA2cSEIHkd9AfFkftuv8oyLDCG3ZAf0vrhrrtkrfa7ef+AUb69DNggq4mHQAYBp7L+\
+k5DKzJrKuO0r+R0YbY9pZD1+/g9dVt91d6LQNepUE/yY2PP5CNoFmjedpLHMOPFdVgqDzDFxU8hL\
+AoGBANDrr7xAJbqBjHVwIzQ4To9pb4BNeqDndk5Qe7fT3+/H1njGaC0/rXE0Qb7q5ySgnsCb3DvA\
+cJyRM9SJ7OKlGt0FMSdJD5KG0XPIpAVNwgpXXH5MDJg09KHeh0kXo+QA6viFBi21y340NonnEfdf\
+54PX4ZGS/Xac1UK+pLkBB+zRAoGAf0AY3H3qKS2lMEI4bzEFoHeK3G895pDaK3TFBVmD7fV0Zhov\
+17fegFPMwOII8MisYm9ZfT2Z0s5Ro3s5rkt+nvLAdfC/PYPKzTLalpGSwomSNYJcB9HNMlmhkGzc\
+1JnLYT4iyUyx6pcZBmCd8bD0iwY/FzcgNDaUmbX9+XDvRA0CgYEAkE7pIPlE71qvfJQgoA9em0gI\
+LAuE4Pu13aKiJnfft7hIjbK+5kyb3TysZvoyDnb3HOKvInK7vXbKuU4ISgxB2bB3HcYzQMGsz1qJ\
+2gG0N5hvJpzwwhbhXqFKA4zaaSrw622wDniAK5MlIE0tIAKKP4yxNGjoD2QYjhBGuhvkWKY=\
+-----END RSA PRIVATE KEY-----"
+
+void CAirTunesServer::AudioOutputFunctions::audio_set_metadata(void *cls, void *session, const void *buffer, int buflen)
+{
+ CAirTunesServer::SetMetadataFromBuffer((char *)buffer, buflen);
+}
+
+void CAirTunesServer::AudioOutputFunctions::audio_set_coverart(void *cls, void *session, const void *buffer, int buflen)
+{
+ CAirTunesServer::SetCoverArtFromBuffer((char *)buffer, buflen);
+}
+
+void* CAirTunesServer::AudioOutputFunctions::audio_init(void *cls, int bits, int channels, int samplerate)
+{
+ XFILE::CPipeFile *pipe=(XFILE::CPipeFile *)cls;
+ pipe->OpenForWrite(XFILE::PipesManager::GetInstance().GetUniquePipeName());
+ pipe->SetOpenThreashold(300);
+
+ BXA_FmtHeader header;
+ strncpy(header.fourcc, "BXA ", 4);
+ header.type = BXA_PACKET_TYPE_FMT;
+ header.bitsPerSample = bits;
+ header.channels = channels;
+ header.sampleRate = samplerate;
+ header.durationMs = 0;
+
+ if (pipe->Write(&header, sizeof(header)) == 0)
+ return 0;
+
+ ThreadMessage tMsg = { TMSG_MEDIA_STOP };
+ g_application.getApplicationMessenger().SendMessage(tMsg, true);
+
+ CFileItem item;
+ item.SetPath(pipe->GetName());
+ item.SetMimeType("audio/x-xbmc-pcm");
+
+ ThreadMessage tMsg2 = { TMSG_GUI_ACTIVATE_WINDOW, WINDOW_VISUALISATION, 0 };
+ g_application.getApplicationMessenger().SendMessage(tMsg2, true);
+
+ g_application.getApplicationMessenger().PlayFile(item);
+
+ return "XBMC-AirTunes";//session
+}
+
+void CAirTunesServer::AudioOutputFunctions::audio_set_volume(void *cls, void *session, float volume)
+{
+ //volume from -144 - 0
+ float volPercent = 1 - volume/-144;
+ g_application.SetVolume(volPercent, false);//non-percent volume 0.0-1.0
+}
+
+void CAirTunesServer::AudioOutputFunctions::audio_process(void *cls, void *session, const void *buffer, int buflen)
+{
+ #define NUM_OF_BYTES 64
+ XFILE::CPipeFile *pipe=(XFILE::CPipeFile *)cls;
+ int sentBytes = 0;
+ unsigned char buf[NUM_OF_BYTES];
+
+ while (sentBytes < buflen)
+ {
+ int n = (buflen - sentBytes < NUM_OF_BYTES ? buflen - sentBytes : NUM_OF_BYTES);
+ memcpy(buf, (char*) buffer + sentBytes, n);
+
+ if (pipe->Write(buf, n) == 0)
+ return;
+
+ sentBytes += n;
+ }
+}
+
+void CAirTunesServer::AudioOutputFunctions::audio_flush(void *cls, void *session)
+{
+ XFILE::CPipeFile *pipe=(XFILE::CPipeFile *)cls;
+ pipe->Flush();
+}
+
+void CAirTunesServer::AudioOutputFunctions::audio_destroy(void *cls, void *session)
+{
+ XFILE::CPipeFile *pipe=(XFILE::CPipeFile *)cls;
+ pipe->SetEof();
+ pipe->Close();
+
+ //fix airplay video for ios5 devices
+ //on ios5 when airplaying video
+ //the client first opens an airtunes stream
+ //while the movie is loading
+ //in that case we don't want to stop the player here
+ //because this would stop the airplaying video
+#ifdef HAS_AIRPLAY
+ if (!CAirPlayServer::IsPlaying())
+#endif
+ {
+ ThreadMessage tMsg = { TMSG_MEDIA_STOP };
+ g_application.getApplicationMessenger().SendMessage(tMsg, true);
+ CLog::Log(LOGDEBUG, "AIRTUNES: AirPlay not running - stopping player");
+ }
+}
+
+void shairplay_log(int level, const char *msg)
+{
+ int xbmcLevel = LOGINFO;
+
+ switch(level)
+ {
+ case RAOP_LOG_EMERG: // system is unusable
+ xbmcLevel = LOGFATAL;
+ break;
+ case RAOP_LOG_ALERT: // action must be taken immediately
+ case RAOP_LOG_CRIT: // critical conditions
+ xbmcLevel = LOGSEVERE;
+ break;
+ case RAOP_LOG_ERR: // error conditions
+ xbmcLevel = LOGERROR;
+ break;
+ case RAOP_LOG_WARNING: // warning conditions
+ xbmcLevel = LOGWARNING;
+ break;
+ case RAOP_LOG_NOTICE: // normal but significant condition
+ xbmcLevel = LOGNOTICE;
+ break;
+ case RAOP_LOG_INFO: // informational
+ xbmcLevel = LOGINFO;
+ break;
+ case RAOP_LOG_DEBUG: // debug-level messages
+ xbmcLevel = LOGDEBUG;
+ break;
+ default:
+ break;
+ }
+ CLog::Log(xbmcLevel, "AIRTUNES: %s", msg);
+}
+
+#else
+
struct ao_device_xbmc
{
XFILE::CPipeFile *pipe;
@@ -214,6 +444,17 @@ char* CAirTunesServer::AudioOutputFunctions::ao_get_option(ao_option *options, c
return NULL;
}
+int shairport_log(const char* msg, size_t msgSize)
+{
+ if( g_advancedSettings.m_logEnableAirtunes)
+ {
+ CLog::Log(LOGDEBUG, "AIRTUNES: %s", msg);
+ }
+ return 1;
+}
+
+#endif
+
bool CAirTunesServer::StartServer(int port, bool nonlocal, bool usePassword, const CStdString &password/*=""*/)
{
bool success = false;
@@ -243,7 +484,9 @@ bool CAirTunesServer::StartServer(int port, bool nonlocal, bool usePassword, con
ServerInstance = new CAirTunesServer(port, nonlocal);
if (ServerInstance->Initialize(password))
{
+#ifndef TARGET_WINDOWS
ServerInstance->Create();
+#endif
success = true;
}
@@ -264,6 +507,9 @@ bool CAirTunesServer::StartServer(int port, bool nonlocal, bool usePassword, con
txt["sr"] = "44100";
txt["pw"] = "false";
txt["vn"] = "3";
+ txt["da"] = "true";
+ txt["vs"] = "130.14";
+ txt["md"] = "0,1,2";
txt["txtvers"] = "1";
CZeroconf::GetInstance()->PublishService("servers.airtunes", "_raop._tcp", appName, port, txt);
@@ -276,10 +522,12 @@ void CAirTunesServer::StopServer(bool bWait)
{
if (ServerInstance)
{
+#if !defined(TARGET_WINDOWS)
if (m_pLibShairport->IsLoaded())
{
m_pLibShairport->shairport_exit();
}
+#endif
ServerInstance->StopThread(bWait);
ServerInstance->Deinitialize();
if (bWait)
@@ -295,47 +543,98 @@ void CAirTunesServer::StopServer(bool bWait)
CAirTunesServer::CAirTunesServer(int port, bool nonlocal) : CThread("AirTunesServer")
{
m_port = port;
+#if defined(TARGET_WINDOWS)
+ m_pLibShairplay = new DllLibShairplay();
+ m_pPipe = new XFILE::CPipeFile;
+#else
m_pLibShairport = new DllLibShairport();
+#endif
}
CAirTunesServer::~CAirTunesServer()
{
+#if defined(TARGET_WINDOWS)
+ if (m_pLibShairplay->IsLoaded())
+ {
+ m_pLibShairplay->Unload();
+ }
+ delete m_pLibShairplay;
+ delete m_pPipe;
+#else
if (m_pLibShairport->IsLoaded())
{
m_pLibShairport->Unload();
}
delete m_pLibShairport;
+#endif
}
void CAirTunesServer::Process()
{
m_bStop = false;
+#if !defined(TARGET_WINDOWS)
while (!m_bStop && m_pLibShairport->shairport_is_running())
{
m_pLibShairport->shairport_loop();
}
+#endif
}
-int shairport_log(const char* msg, size_t msgSize)
+bool CAirTunesServer::Initialize(const CStdString &password)
{
- if( g_advancedSettings.m_logEnableAirtunes)
+ bool ret = false;
+
+ Deinitialize();
+
+#if defined(TARGET_WINDOWS)
+ if (m_pLibShairplay->Load())
{
- CLog::Log(LOGDEBUG, "AIRTUNES: %s", msg);
+
+ raop_callbacks_t ao;
+ ao.cls = m_pPipe;
+ ao.audio_init = AudioOutputFunctions::audio_init;
+ ao.audio_set_volume = AudioOutputFunctions::audio_set_volume;
+ ao.audio_set_metadata = AudioOutputFunctions::audio_set_metadata;
+ ao.audio_set_coverart = AudioOutputFunctions::audio_set_coverart;
+ ao.audio_process = AudioOutputFunctions::audio_process;
+ ao.audio_flush = AudioOutputFunctions::audio_flush;
+ ao.audio_destroy = AudioOutputFunctions::audio_destroy;
+ m_pLibShairplay->EnableDelayedUnload(false);
+ m_pRaop = m_pLibShairplay->raop_init(1, &ao, RSA_KEY);//1 - we handle one client at a time max
+ ret = m_pRaop != NULL;
+
+ if(ret)
+ {
+ char macAdr[6];
+ unsigned short port = (unsigned short)m_port;
+
+ m_pLibShairplay->raop_set_log_level(m_pRaop, RAOP_LOG_WARNING);
+ if(g_advancedSettings.m_logEnableAirtunes)
+ {
+ m_pLibShairplay->raop_set_log_level(m_pRaop, RAOP_LOG_DEBUG);
+ }
+
+ m_pLibShairplay->raop_set_log_callback(m_pRaop, shairplay_log);
+
+ CNetworkInterface *net = g_application.getNetwork().GetFirstConnectedInterface();
+
+ if (net)
+ {
+ net->GetMacAddressRaw(macAdr);
+ }
+
+ ret = m_pLibShairplay->raop_start(m_pRaop, &port, macAdr, 6, password.c_str()) >= 0;
+ }
}
- return 1;
-}
-bool CAirTunesServer::Initialize(const CStdString &password)
-{
- bool ret = false;
+#else
+
int numArgs = 3;
CStdString hwStr;
CStdString pwStr;
CStdString portStr;
- Deinitialize();
-
hwStr.Format("--mac=%s", m_macAddress.c_str());
pwStr.Format("--password=%s",password.c_str());
portStr.Format("--server_port=%d",m_port);
@@ -368,16 +667,27 @@ bool CAirTunesServer::Initialize(const CStdString &password)
m_pLibShairport->shairport_main(numArgs, argv);
ret = true;
}
+#endif
return ret;
}
void CAirTunesServer::Deinitialize()
{
+#if defined(TARGET_WINDOWS)
+ if (m_pLibShairplay && m_pLibShairplay->IsLoaded())
+ {
+ m_pLibShairplay->raop_stop(m_pRaop);
+ m_pLibShairplay->raop_destroy(m_pRaop);
+ m_pLibShairplay->Unload();
+ }
+#else
if (m_pLibShairport && m_pLibShairport->IsLoaded())
{
m_pLibShairport->shairport_exit();
m_pLibShairport->Unload();
}
+#endif
}
#endif
+
diff --git a/xbmc/network/AirTunesServer.h b/xbmc/network/AirTunesServer.h
index da893df..4a30e0a 100644
--- a/xbmc/network/AirTunesServer.h
+++ b/xbmc/network/AirTunesServer.h
@@ -26,7 +26,11 @@
#ifdef HAS_AIRTUNES
+#if defined(TARGET_WINDOWS)
+#include "DllLibShairplay.h"
+#else
#include "DllLibShairport.h"
+#endif
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
@@ -41,11 +45,14 @@
class DllLibShairport;
+
class CAirTunesServer : public CThread
{
public:
static bool StartServer(int port, bool nonlocal, bool usePassword, const CStdString &password="");
static void StopServer(bool bWait);
+ static void SetMetadataFromBuffer(const char *buffer, unsigned int size);
+ static void SetCoverArtFromBuffer(const char *buffer, unsigned int size);
protected:
void Process();
@@ -57,13 +64,28 @@ class CAirTunesServer : public CThread
void Deinitialize();
int m_port;
+#if defined(TARGET_WINDOWS)
+ static DllLibShairplay *m_pLibShairplay;//the lib
+ raop_t *m_pRaop;
+ XFILE::CPipeFile *m_pPipe;
+#else
static DllLibShairport *m_pLibShairport;//the lib
+#endif
static CAirTunesServer *ServerInstance;
static CStdString m_macAddress;
class AudioOutputFunctions
{
public:
+#if defined(TARGET_WINDOWS)
+ static void* audio_init(void *cls, int bits, int channels, int samplerate);
+ static void audio_set_volume(void *cls, void *session, float volume);
+ static void audio_set_metadata(void *cls, void *session, const void *buffer, int buflen);
+ static void audio_set_coverart(void *cls, void *session, const void *buffer, int buflen);
+ static void audio_process(void *cls, void *session, const void *buffer, int buflen);
+ static void audio_flush(void *cls, void *session);
+ static void audio_destroy(void *cls, void *session);
+#else
static void ao_initialize(void);
static int ao_play(ao_device *device, char *output_samples, uint32_t num_bytes);
static int ao_default_driver_id(void);
@@ -74,6 +96,7 @@ class CAirTunesServer : public CThread
static int ao_append_option(ao_option **options, const char *key, const char *value);
static void ao_free_options(ao_option *options);
static char* ao_get_option(ao_option *options, const char* key);
+#endif
};
};
--
1.7.10